1 /****************************************************************************
4 * This module contains all radiosity calculation functions.
6 * This file was written by Jim McElhiney.
8 * from Persistence of Vision(tm) Ray Tracer
9 * Copyright 1996,1999 Persistence of Vision Team
10 *---------------------------------------------------------------------------
11 * NOTICE: This source code file is provided so that users may experiment
12 * with enhancements to POV-Ray and to port the software to platforms other
13 * than those supported by the POV-Ray Team. There are strict rules under
14 * which you are permitted to use this file. The rules are in the file
15 * named POVLEGAL.DOC which should be distributed with this file.
16 * If POVLEGAL.DOC is not available or for more info please contact the POV-Ray
17 * Team Coordinator by email to team-coord@povray.org or visit us on the web at
18 * http://www.povray.org. The latest version of POV-Ray may be found at this site.
20 * This program is based on the popular DKB raytracer version 2.12.
21 * DKBTrace was originally written by David K. Buck.
22 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
24 *****************************************************************************/
26 /************************************************************************
27 * Radiosity calculation routies.
29 * (This does not work the way that most radiosity programs do, but it accomplishes
30 * the diffuse interreflection integral the hard way and produces similar results. It
31 * is called radiosity here to avoid confusion with ambient and diffuse, which
32 * already have well established meanings within POV).
33 * Inspired by the paper "A Ray Tracing Solution for Diffuse Interreflection"
34 * by Ward, Rubinstein, and Clear, in Siggraph '88 proceedings.
36 * Basic Idea: Never use a constant ambient term. Instead,
37 * - For first pixel, cast a whole bunch of rays in different directions
38 * from the object intersection point to see what the diffuse illumination
39 * really is. Save this value, after estimating its
40 * degree of reusability. (Method 1)
41 * - For second and subsequent pixels,
42 * - If there are one or more nearby values already computed,
43 * average them and use the result (Method 2), else
46 * Implemented by and (c) 1994-6 Jim McElhiney, mcelhiney@acm.org or 71201,1326
47 * All standard POV distribution rights granted. All other rights reserved.
48 *************************************************************************/
65 /*****************************************************************************
66 * Local preprocessor defines
67 ******************************************************************************/
69 #define RAD_GRADIENT 1
71 /* #define SIGMOID_METHOD 1 */
72 /* #define SHOW_SAMPLE_SPOTS 1 */ /* try this! bright spots at sample pts */
74 /*****************************************************************************
76 ******************************************************************************/
80 /*****************************************************************************
82 ******************************************************************************/
85 long ra_reuse_count
= 0;
86 long ra_gather_count
= 0;
88 int Radiosity_Trace_Level
= 1;
90 COLOUR Radiosity_Gather_Total
;
91 long Radiosity_Gather_Total_Count
;
92 COLOUR Radiosity_Setting_Total
;
93 long Radiosity_Setting_Total_Count
;
96 extern long ot_blockcount
;
97 long ot_seenodecount
= 0;
98 long ot_seeblockcount
= 0;
99 long ot_doblockcount
= 0;
100 long ot_dotokcount
= 0;
101 long ot_lastcount
= 0;
102 long ot_lowerrorcount
= 0;
106 OT_NODE
*ot_root
= NULL
;
108 /* This (and all other changing globals) should really be in an execution
109 * context structure passed down the execution call tree as a parameter to
110 * each function. This would allow for a multiprocessor/multithreaded version.
115 /*****************************************************************************
117 ******************************************************************************/
119 static long ra_reuse (VECTOR IPoint
, VECTOR Normal
, COLOUR Illuminance
);
120 static long ra_average_near (OT_BLOCK
*block
, void *void_info
);
121 static void ra_gather (VECTOR IPoint
, VECTOR Normal
, COLOUR Illuminance
, DBL Weight
);
122 static void VUnpack (VECTOR dest_vec
, BYTE_XYZ
*pack
);
126 /*****************************************************************************
144 * Main entry point for calculated diffuse illumination
148 * --- 1994 : Creation.
150 ******************************************************************************/
151 /* the colour to be calculated */
152 /* maximum possible contribution to pixel colour */
153 int Compute_Ambient(VECTOR IPoint
, VECTOR S_Normal
, COLOUR Ambient_Colour
, DBL Weight
)
156 DBL grey
, save_bound
;
158 save_bound
= opts
.Radiosity_Error_Bound
;
161 opts
.Radiosity_Error_Bound
+= (.25 - Weight
);
163 reuse
= ra_reuse(IPoint
, S_Normal
, Ambient_Colour
);
164 opts
.Radiosity_Error_Bound
= save_bound
;
174 ra_gather(IPoint
, S_Normal
, Ambient_Colour
, Weight
);
176 ra_gather_count
++; /* keep a running count */
182 if ( Radiosity_Trace_Level
== 1 )
185 grey
= (Ambient_Colour
[RED
] + Ambient_Colour
[GREEN
] + Ambient_Colour
[BLUE
]) / 3.;
187 /* note grey spelling: american options structure with worldbeat calculations! */
188 Ambient_Colour
[RED
] = opts
.Radiosity_Gray
* grey
+ Ambient_Colour
[RED
] * (1.-opts
.Radiosity_Gray
);
189 Ambient_Colour
[GREEN
] = opts
.Radiosity_Gray
* grey
+ Ambient_Colour
[GREEN
] * (1.-opts
.Radiosity_Gray
);
190 Ambient_Colour
[BLUE
] = opts
.Radiosity_Gray
* grey
+ Ambient_Colour
[BLUE
] * (1.-opts
.Radiosity_Gray
);
192 /* Scale up by current brightness factor prior to return */
193 VScale(Ambient_Colour
, Ambient_Colour
, opts
.Radiosity_Brightness
);
201 /*****************************************************************************
219 * Returns whether or not there were some prestored values close enough to
224 * --- 1994 : Creation.
226 ******************************************************************************/
228 static long ra_reuse
/* return value */(VECTOR IPoint
, VECTOR S_Normal
, COLOUR Illuminance
)
235 Make_Colour(gather
.Weights_Times_Illuminances
, 0.0, 0.0, 0.0);
237 gather
.Weights
= 0.0;
239 Assign_Vector(gather
.P
, IPoint
);
240 Assign_Vector(gather
.N
, S_Normal
);
242 gather
.Weights_Count
= 0;
243 gather
.Good_Count
= 0;
244 gather
.Close_Count
= 0;
245 gather
.Current_Error_Bound
= opts
.Radiosity_Error_Bound
;
247 for (i
= 1; i
< Radiosity_Trace_Level
; i
++)
249 gather
.Current_Error_Bound
*= 1.4;
253 * Go through the tree calculating a weighted average of all of the
254 * usable points near this one
257 ot_dist_traverse(ot_root
, IPoint
, Radiosity_Trace_Level
,
258 ra_average_near
, (void *)&gather
);
260 /* Did we get any nearby points we could reuse? */
262 if (gather
.Good_Count
> 0)
264 Make_Colour(gather
.Weights_Times_Illuminances
, 0.0, 0.0, 0.0);
266 gather
.Weights
= 0.0;
268 for (i
= 0; i
< gather
.Close_Count
; i
++)
270 VAddEq(gather
.Weights_Times_Illuminances
, gather
.Weight_Times_Illuminance
[i
]);
272 gather
.Weights
+= gather
.Weight
[i
];
275 VInverseScale(Illuminance
, gather
.Weights_Times_Illuminances
, gather
.Weights
);
280 gather
.Good_Count
= 0; /* No tree, so no reused values */
283 return(gather
.Good_Count
);
288 /*****************************************************************************
306 * Tree traversal function used by ra_reuse()
307 * Calculate the weight of this cached value, taking into account how far
308 * it is from our test point, and the difference in surface normal angles.
310 * Given a node with an old cached value, check to see if it is reusable, and
311 * aggregate its info into the weighted average being built during the tree
312 * traversal. block contains Point, Normal, Illuminance,
313 * Harmonic_Mean_Distance
317 * --- 1994 : Creation.
319 ******************************************************************************/
321 static long ra_average_near(OT_BLOCK
*block
, void *void_info
)
324 WT_AVG
*info
= (WT_AVG
*) void_info
;
325 VECTOR half
, delta
, delta_unit
= {};
326 COLOUR tc
, prediction
;
327 DBL ri
, error_reuse
, dir_diff
, in_front
, dist
, weight
, square_dist
, dr
, dg
, db
;
328 DBL error_reuse_rotate
, error_reuse_translate
, inverse_dist
, cos_diff_from_nearest
;
336 VSub(delta
, info
->P
, block
->Point
); /* a = b - c, which is test p minus old pt */
338 square_dist
= VSumSqr(delta
);
340 quickcheck_rad
= (DBL
)block
->Harmonic_Mean_Distance
* info
->Current_Error_Bound
;
342 /* first we do a tuning test--this func gets called a LOT */
343 if (square_dist
< quickcheck_rad
* quickcheck_rad
)
346 dist
= sqrt(square_dist
);
347 ri
= (DBL
)block
->Harmonic_Mean_Distance
;
350 if ( dist
> .000001 )
352 inverse_dist
= 1./dist
;
353 VScale(delta_unit
, delta
, inverse_dist
); /* this is a normalization */
355 /* This block reduces the radius of influence when it points near the nearest
356 surface found during sampling. */
357 VDot( cos_diff_from_nearest
, block
->To_Nearest_Surface
, delta_unit
);
358 if ( cos_diff_from_nearest
> 0. )
360 ri
= cos_diff_from_nearest
* (DBL
)block
->Nearest_Distance
+
361 (1.-cos_diff_from_nearest
) * ri
;
365 if (dist
< ri
* info
->Current_Error_Bound
)
367 VDot(dir_diff
, info
->N
, block
->S_Normal
);
369 /* NB error_reuse varies from 0 to 3.82 (1+ 2 root 2) */
371 error_reuse_translate
= dist
/ ri
;
373 error_reuse_rotate
= 2.0 * sqrt(fabs(1.0 - dir_diff
));
375 error_reuse
= error_reuse_translate
+ error_reuse_rotate
;
377 /* is this old point within a reasonable error distance? */
379 if (error_reuse
< info
->Current_Error_Bound
)
388 * Make sure that the old point is not in front of this point, the
389 * old surface might shadow this point and make the result
392 VHalf(half
, info
->N
, block
->S_Normal
);
393 VNormalizeEq(half
); /* needed so check can be to constant */
394 VDot(in_front
, delta_unit
, half
);
402 * Theory: eliminate the use of old points well in front of our
403 * new point we are calculating, but not ones which are just a little
404 * tiny bit in front. This (usually) avoids eliminating points on the
405 * same surface by accident.
408 if (in_front
> (-0.05))
414 #ifdef SIGMOID_METHOD
415 weight
= error_reuse
/ info
->Current_Error_Bound
; /* 0 < t < 1 */
416 weight
= (cos(weight
* M_PI
) + 1.0) * 0.5; /* 0 < w < 1 */
419 weight
= 1.0 - (error_reuse
/ info
->Current_Error_Bound
); /* 0 < t < 1 */
422 if ( weight
> 0.001 )
423 { /* avoid floating point oddities near zero */
425 /* This is the block where we use the gradient to improve the prediction */
426 dr
= delta
[X
] * block
->drdx
+ delta
[Y
] * block
->drdy
+ delta
[Z
] * block
->drdz
;
427 dg
= delta
[X
] * block
->dgdx
+ delta
[Y
] * block
->dgdy
+ delta
[Z
] * block
->dgdz
;
428 db
= delta
[X
] * block
->dbdx
+ delta
[Y
] * block
->dbdy
+ delta
[Z
] * block
->dbdz
;
433 /* Ensure that the total change in colour is a reasonable magnitude */
434 if ( dr
> .1 ) dr
= .1; else if ( dr
< -.1 ) dr
= -.1;
435 if ( dg
> .1 ) dg
= .1; else if ( dg
< -.1 ) dg
= -.1;
436 if ( db
> .1 ) db
= .1; else if ( db
< -.1 ) db
= -.1;
438 prediction
[RED
] = block
->Illuminance
[RED
] + dr
;
439 prediction
[RED
] = min(max(prediction
[RED
], 0.), 1.);
440 prediction
[GREEN
] = block
->Illuminance
[GREEN
] + dg
;
441 prediction
[GREEN
] = min(max(prediction
[GREEN
],0.), 1.);
442 prediction
[BLUE
] = block
->Illuminance
[BLUE
] + db
;
443 prediction
[BLUE
] = min(max(prediction
[BLUE
], 0.), 1.);
445 #ifdef SHOW_SAMPLE_SPOTS
446 if ( dist
< opts
.Radiosity_Dist_Max
* .015 ) {
447 prediction
[RED
] = prediction
[GREEN
] = prediction
[BLUE
] = 3.;
451 /* The predicted colour is an extrapolation based on the old value */
452 VScale(tc
, prediction
, weight
);
454 VAddEq(info
->Weights_Times_Illuminances
, tc
);
456 info
->Weights
+= weight
;
458 info
->Weights_Count
++;
464 if (info
->Close_Count
< opts
.Radiosity_Nearest_Count
)
466 ind
= info
->Close_Count
++;
470 for (i
= 0; i
< info
->Close_Count
; i
++)
472 if (dist
< info
->Distance
[i
])
476 i
= opts
.Radiosity_Nearest_Count
;
483 info
->Distance
[ind
] = dist
;
485 info
->Weight
[ind
] = weight
;
487 VScale(info
->Weight_Times_Illuminance
[ind
], prediction
, weight
);
501 /*****************************************************************************
508 * IPoint - a point at which the illumination is needed
509 * S_Normal - the surface normal (not perturbed by the current layer) at that point
510 * Illuminance - a place to put the return result
511 * Weight - the weight of this point in final output, to drive ADC_Bailout
514 * The average colour of light of objects visible from the specified point.
515 * The colour is returned in the Illuminance parameter.
525 * Gather up the incident light and average it.
526 * Return the results in Illuminance, and also cache them for later.
527 * Note that last parameter is similar to weight parameter used
528 * to control ADC_Bailout as a parameter to Trace(), but it also
529 * takes into account that this subsystem calculates only ambient
530 * values. Therefore, coming in at the top level, the value might
531 * be 0.3 if the first object hit had an ambient of 0.3, whereas
532 * Trace() would have been passed a parameter of 1.0 (since it
533 * calculates the whole pixel value).
537 * --- 1994 : Creation.
539 ******************************************************************************/
541 static void ra_gather(VECTOR IPoint
, VECTOR S_Normal
, COLOUR Illuminance
, DBL Weight
)
544 long i
, hit
, Current_Radiosity_Count
;
545 unsigned long Save_Quality_Flags
, Save_Options
;
546 VECTOR random_vec
, direction
, n2
, n3
, up
, min_dist_vec
;
547 int save_Max_Trace_Level
;
548 DBL Inverse_Distance_Sum
, depth
, mean_dist
, weight
, save_min_reuse
,
549 drdxs
, dgdxs
, dbdxs
, drdys
, dgdys
, dbdys
, drdzs
, dgdzs
, dbdzs
,
550 depth_weight_for_this_gradient
, dxsquared
, dysquared
, dzsquared
,
551 constant_term
, deemed_depth
, min_dist
, reuse_dist_min
, to_eye
,
552 sum_of_inverse_dist
, sum_of_dist
, average_dist
, gradient_count
;
553 COLOUR Colour_Sums
, Temp_Colour
;
559 * A bit of theory: The goal is to create a set of "random" direction rays
560 * so that the probability of close-to-normal versus close-to-tangent rolls
561 * off in a cos-theta curve, where theta is the deviation from normal.
562 * That is, lots of rays close to normal, and very few close to tangent.
563 * You also want to have all of the rays be evenly spread, no matter how
564 * many you want to use. The lookup array has an array of points carefully
565 * chosen to meet all of these criteria.
569 /* The number of rays to trace varies with our recursion depth */
571 Current_Radiosity_Count
= opts
.Radiosity_Count
;
572 save_min_reuse
= opts
.Radiosity_Min_Reuse
;
573 for ( i
=1; i
<Radiosity_Trace_Level
; i
++ )
575 Current_Radiosity_Count
/= 3;
576 opts
.Radiosity_Min_Reuse
*= 2.;
579 /* Save some global stuff which we have to change for now */
581 save_Max_Trace_Level
= Max_Trace_Level
;
584 /* Since we'll be calculating averages, zero the accumulators */
586 Make_Colour(Colour_Sums
, 0., 0., 0.);
587 Inverse_Distance_Sum
= 0.;
590 min_dist
= BOUND_HUGE
;
592 if ( fabs(fabs(S_Normal
[Z
])- 1.) < .1 ) {
593 /* too close to vertical for comfort, so use cross product with horizon */
594 up
[X
] = 0.; up
[Y
] = 1.; up
[Z
] = 0.;
598 up
[X
] = 0.; up
[Y
] = 0.; up
[Z
] = 1.;
601 VCross(n2
, S_Normal
, up
); VNormalizeEq(n2
);
602 VCross(n3
, S_Normal
, n2
); VNormalizeEq(n3
);
605 /* Note that this max() forces at least one ray to be shot.
606 Otherwise, the loop does nothing, since every call to
607 Trace() just bails out immediately! */
608 weight
= max(ADC_Bailout
, Weight
/(DBL
)Current_Radiosity_Count
);
610 /* Initialized the accumulators for the integrals which will be come the rad gradient */
611 drdxs
= dgdxs
= dbdxs
= drdys
= dgdys
= dbdys
= drdzs
= dgdzs
= dbdzs
= 0.;
612 sum_of_inverse_dist
= sum_of_dist
= gradient_count
= 0.;
615 for (i
= hit
= 0; i
< Current_Radiosity_Count
; i
++)
617 /* pick a random direction with the right statistical skew */
619 VUnpack(random_vec
, &rad_samples
[i
]);
621 if ( fabs(S_Normal
[Z
] - 1.) < .001 ) /* pretty well straight Z, folks */
623 /* we are within 1/20 degree of pointing in the Z axis. */
624 /* use all vectors as is--they're precomputed this way */
625 Assign_Vector(direction
, random_vec
);
629 direction
[X
] = n2
[X
]*random_vec
[X
] + n3
[X
]*random_vec
[Y
] + S_Normal
[X
]*random_vec
[Z
];
630 direction
[Y
] = n2
[Y
]*random_vec
[X
] + n3
[Y
]*random_vec
[Y
] + S_Normal
[Y
]*random_vec
[Z
];
631 direction
[Z
] = n2
[Z
]*random_vec
[X
] + n3
[Z
]*random_vec
[Y
] + S_Normal
[Z
]*random_vec
[Z
];
635 /* Build a ray pointing in the chosen direction */
636 Make_Colour(Temp_Colour
, 0.0, 0.0, 0.0);
637 Initialize_Ray_Containers(&New_Ray
);
638 Assign_Vector(New_Ray
.Initial
, IPoint
);
639 Assign_Vector(New_Ray
.Direction
, direction
);
641 /* save some flags that must be set to a different value during the trace() */
642 Save_Quality_Flags
= opts
.Quality_Flags
;
643 Save_Options
= opts
.Options
;
644 opts
.Radiosity_Quality
= 6;
647 opts
.Quality_Flags
= Quality_Values
[opts
.Radiosity_Quality
];
649 /* Set up a custom quality level, like Q=5, without area lights, with radiosity */
650 opts
.Quality_Flags
= Q_SHADOW
;
651 opts
.Options
&= ~USE_LIGHT_BUFFER
;
654 /* Go down in recursion, trace the result, and come back up */
657 Radiosity_Trace_Level
++;
658 depth
= Trace(&New_Ray
, Temp_Colour
, weight
);
659 Radiosity_Trace_Level
--;
663 /* Add into illumination gradient integrals */
665 deemed_depth
= depth
;
666 if (deemed_depth
< opts
.Radiosity_Dist_Max
* 10.)
668 depth_weight_for_this_gradient
= 1. / deemed_depth
;
669 sum_of_inverse_dist
+= 1. / deemed_depth
;
670 sum_of_dist
+= deemed_depth
;
673 dxsquared
= direction
[X
] * direction
[X
]; if (direction
[X
] < 0.) dxsquared
= -dxsquared
;
674 dysquared
= direction
[Y
] * direction
[Y
]; if (direction
[Y
] < 0.) dysquared
= -dysquared
;
675 dzsquared
= direction
[Z
] * direction
[Z
]; if (direction
[Z
] < 0.) dzsquared
= -dzsquared
;
676 drdxs
+= dxsquared
* Temp_Colour
[RED
] * depth_weight_for_this_gradient
;
677 dgdxs
+= dxsquared
* Temp_Colour
[GREEN
] * depth_weight_for_this_gradient
;
678 dbdxs
+= dxsquared
* Temp_Colour
[BLUE
] * depth_weight_for_this_gradient
;
679 drdys
+= dysquared
* Temp_Colour
[RED
] * depth_weight_for_this_gradient
;
680 dgdys
+= dysquared
* Temp_Colour
[GREEN
] * depth_weight_for_this_gradient
;
681 dbdys
+= dysquared
* Temp_Colour
[BLUE
] * depth_weight_for_this_gradient
;
682 drdzs
+= dzsquared
* Temp_Colour
[RED
] * depth_weight_for_this_gradient
;
683 dgdzs
+= dzsquared
* Temp_Colour
[GREEN
] * depth_weight_for_this_gradient
;
684 dbdzs
+= dzsquared
* Temp_Colour
[BLUE
] * depth_weight_for_this_gradient
;
688 if (depth
> opts
.Radiosity_Dist_Max
)
690 depth
= opts
.Radiosity_Dist_Max
;
699 if (depth
< min_dist
)
702 Assign_Vector(min_dist_vec
, direction
);
705 opts
.Quality_Flags
= Save_Quality_Flags
;
706 opts
.Options
= Save_Options
;
708 /* Add into total illumination integral */
710 VAddEq(Colour_Sums
, Temp_Colour
);
712 Inverse_Distance_Sum
+= 1.0 / depth
;
713 } /* end ray sampling loop */
717 * Use the accumulated values to calculate the averages needed. The sphere
718 * of influence of this primary-method sample point is based on the
719 * harmonic mean distance to the points encountered. (An harmonic mean is
720 * the inverse of the mean of the inverses).
723 mean_dist
= 1.0 / (Inverse_Distance_Sum
/ (DBL
) Current_Radiosity_Count
);
725 VInverseScale(Illuminance
, Colour_Sums
, (DBL
) Current_Radiosity_Count
);
727 /* Keep a running total of the final Illuminances we calculated */
728 if ( Radiosity_Trace_Level
== 1.) {
729 VAddEq(Radiosity_Gather_Total
, Illuminance
);
730 Radiosity_Gather_Total_Count
++;
734 /* We want to cached this block for later reuse. But,
735 * if ground units not big enough, meaning that the value has very
736 * limited reuse potential, forget it.
739 if (mean_dist
> (opts
.Radiosity_Dist_Max
* 0.0001))
743 * Theory: we don't want to calculate a primary method ray loop at every
744 * point along the inside edges, so a minimum effectivity is practical.
745 * It is expressed as a fraction of the distance to the eyepoint. 1/2%
746 * is a good number. This enhancement was Greg Ward's idea, but the use
747 * of % units is my idea. [JDM]
750 VDist(to_eye
, Frame
.Camera
->Location
, IPoint
);
751 reuse_dist_min
= to_eye
* opts
.Radiosity_Min_Reuse
;
752 if (mean_dist
< reuse_dist_min
)
754 mean_dist
= reuse_dist_min
;
758 /* figure out the block id */
759 ot_index_sphere(IPoint
, mean_dist
* opts
.Radiosity_Error_Bound
, &id
);
766 /* After end of ray loop, we've decided that this point is worth storing */
767 /* Allocate a block, and fill it with values for reuse in cacheing later */
768 block
= (OT_BLOCK
*)POV_MALLOC(sizeof(OT_BLOCK
), "octree block");
769 memset(block
, 0, sizeof(OT_BLOCK
));
772 if ( gradient_count
> 10.)
774 average_dist
= sum_of_dist
/ gradient_count
;
775 constant_term
= 1.00 / (sum_of_inverse_dist
* average_dist
);
777 block
->drdx
= (float)(drdxs
* constant_term
);
778 block
->dgdx
= (float)(dgdxs
* constant_term
);
779 block
->dbdx
= (float)(dbdxs
* constant_term
);
780 block
->drdy
= (float)(drdys
* constant_term
);
781 block
->dgdy
= (float)(dgdys
* constant_term
);
782 block
->dbdy
= (float)(dbdys
* constant_term
);
783 block
->drdz
= (float)(drdzs
* constant_term
);
784 block
->dgdz
= (float)(dgdzs
* constant_term
);
785 block
->dbdz
= (float)(dbdzs
* constant_term
);
789 /* Fill up the values in the octree (ot_) cache block */
791 Assign_Vector(block
->Illuminance
, Illuminance
);
792 Assign_Vector(block
->To_Nearest_Surface
, min_dist_vec
);
793 block
->Harmonic_Mean_Distance
= (float)mean_dist
;
794 block
->Nearest_Distance
= (float)min_dist
;
795 block
->Bounce_Depth
= (short)Radiosity_Trace_Level
;
796 Assign_Vector(block
->Point
, IPoint
);
797 Assign_Vector(block
->S_Normal
, S_Normal
);
800 /* store the info block in the oct tree */
801 ot_ins(&ot_root
, block
, &id
);
803 /* In case the rendering is suspended, save the cache tree values to a file */
804 if ( opts
.Radiosity_File_SaveWhileRendering
&& (ot_fd
!= NULL
) ) {
805 ot_write_block(block
, ot_fd
);
809 /* Put things back where they were in recursion depth */
810 Max_Trace_Level
= save_Max_Trace_Level
;
811 opts
.Radiosity_Min_Reuse
= save_min_reuse
;
816 /*****************************************************************************
818 * FUNCTION VUnpack() - Unpacks "pack_vec" into "dest_vec" and normalizes it.
826 * AUTHOUR Jim McElhiney
830 * The precomputed radiosity rays are packed into a lookup array with one byte
831 * for each of dx, dy, and dz. dx and dy are scaled from the range (-1. to 1.),
832 * and dz is scaled from the range (0. to 1.), and both are stored in the range
835 * The reason for this function is that it saves a bit of memory. There are 2000
836 * entries in the table, and packing them saves 21 bytes each, or 42KB.
840 * --- Jan 1996 : Creation.
842 ******************************************************************************/
843 static void VUnpack(VECTOR dest_vec
, BYTE_XYZ
* pack_vec
)
845 dest_vec
[X
] = ((double)pack_vec
->x
* (1./ 255.))*2.-1.;
846 dest_vec
[Y
] = ((double)pack_vec
->y
* (1./ 255.))*2.-1.;
847 dest_vec
[Z
] = ((double)pack_vec
->z
* (1./ 255.));
849 VNormalizeEq(dest_vec
); /* already good to about 1%, but we can do better */
853 /*****************************************************************************
855 * FUNCTION Initialize_Radiosity_Code
859 * OUTPUT Sets various global states used by radiosity. Notably,
860 * ot_fd - the file identifier of the file used to save radiosity values
862 * RETURNS 1 for Success, 0 for failure (e.g., could not open cache file)
864 * AUTHOUR Jim McElhiney
870 * --- Jan 1996 : Creation.
872 ******************************************************************************/
874 Initialize_Radiosity_Code()
876 long retval
, used_existing_file
;
878 char *modes
, rad_cache_filename
[256];
880 retval
= 1; /* assume the best */
882 if ( opts
.Options
& RADIOSITY
)
884 opts
.Radiosity_Preview_Done
= 0;
890 if ( opts
.Radiosity_Dist_Max
== 0. )
892 /* User hasn't picked a radiosity dist max, so pick one automatically. */
893 VDist(opts
.Radiosity_Dist_Max
, Frame
.Camera
->Location
,
894 Frame
.Camera
->Look_At
);
895 opts
.Radiosity_Dist_Max
*= 0.2;
900 ot_seeblockcount
= 0;
903 ot_lowerrorcount
= 0;
907 if ( ot_fd
!= NULL
) /* if already open for some unknown reason, close it */
913 /* build the file name for the radiosity cache file */
914 strcpy(rad_cache_filename
, opts
.Scene_Name
);
915 strcat(rad_cache_filename
, RADIOSITY_CACHE_EXTENSION
);
917 used_existing_file
= 0;
918 if ( ((opts
.Options
& CONTINUE_TRACE
) && opts
.Radiosity_File_ReadOnContinue
) ||
919 opts
.Radiosity_File_AlwaysReadAtStart
)
921 fd
= fopen(rad_cache_filename
, READ_BINFILE_STRING
); /* "myname.rca" */
923 used_existing_file
= ot_read_file(fd
);
924 retval
&= used_existing_file
;
930 DELETE_FILE(rad_cache_filename
); /* default case, force a clean start */
934 if ( opts
.Radiosity_File_SaveWhileRendering
)
936 /* If we are writing a file, but not using what's there, we truncate,
937 since we conclude that what is there is bad.
938 But, if we are also using what's there, then it must be good, so
939 we just append to it.
941 modes
= used_existing_file
? APPEND_BINFILE_STRING
: WRITE_BINFILE_STRING
;
942 ot_fd
= fopen(rad_cache_filename
, modes
);
943 retval
&= (ot_fd
!= NULL
);
951 /*****************************************************************************
953 * FUNCTION Deinitialize_Radiosity_Code()
957 * OUTPUT Sets various global states used by radiosity. Notably,
958 * ot_fd - the file identifier of the file used to save radiosity values
960 * RETURNS 1 for total success, 0 otherwise (e.g., could not save cache tree)
962 * AUTHOUR Jim McElhiney
965 * Wrap up and free any radiosity-specific features.
966 * Note that this function is safe to call even if radiosity was not on.
970 * --- Jan 1996 : Creation.
972 ******************************************************************************/
974 Deinitialize_Radiosity_Code()
977 char rad_cache_filename
[256];
980 retval
= 1; /* assume the best */
982 if ( opts
.Options
& RADIOSITY
)
984 /* if the global file identifier is set, close it */
985 if ( ot_fd
!= NULL
) {
991 /* build the file name for the radiosity cache file */
992 strcpy(rad_cache_filename
, opts
.Scene_Name
);
993 strcat(rad_cache_filename
, RADIOSITY_CACHE_EXTENSION
);
996 /* If user has not asked us to save the radiosity cache file, delete it */
997 if ( opts
.Radiosity_File_SaveWhileRendering
&&
998 !(opts
.Radiosity_File_KeepAlways
|| (Stop_Flag
&& opts
.Radiosity_File_KeepOnAbort
) ) )
1000 DELETE_FILE(rad_cache_filename
);
1003 /* after-the-fact version. This is an alternative to putting a call to
1004 ot_write_node after the call to ot_ins in ra_gather().
1005 The on-the-fly version (all of the code which uses ot_fd) is superior
1006 in that you will get partial results if you restart your rendering
1007 with a different resolution or camera angle. This version is superior
1008 in that your rendering goes a lot quicker.
1011 if (!(opts
.Radiosity_File_KeepAlways
|| (Stop_Flag
&& opts
.Radiosity_File_KeepOnAbort
)) &&
1012 !opts
.Radiosity_File_SaveWhileRendering
&& ot_root
!= NULL
)
1014 fd
= fopen(rad_cache_filename
, WRITE_BINFILE_STRING
);
1017 retval
&= ot_save_tree(ot_root
, fd
);
1028 /* Note that multiframe animations should call this free function if they have
1029 moving objects and want correct results.
1030 They should NOT call this function if they have no moving objects (like
1031 fly-throughs) and want speed
1033 if ( ot_root
!= NULL
) {
1034 retval
&= ot_free_tree(&ot_root
); /* this zeroes the root pointer */