fix remapping behavior. Remapping is only necessary if we are rendering on the workbe...
[AROS-Contrib.git] / gfx / povray / radiosit.c
blob44349310d044c3c9d76775400620aa325e1cd012
1 /****************************************************************************
2 * radiosit.c
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
44 * - Use method 1.
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 *************************************************************************/
50 #include "string.h"
51 #include "frame.h"
52 #include "lighting.h"
53 #include "vector.h"
54 #include "povray.h"
55 #include "optin.h"
56 #include "povproto.h"
57 #include "render.h"
58 #include "texture.h"
59 #include "octree.h"
60 #include "radiosit.h"
61 #include "ray.h"
65 /*****************************************************************************
66 * Local preprocessor defines
67 ******************************************************************************/
69 #define RAD_GRADIENT 1
70 #define SAW_METHOD 1
71 /* #define SIGMOID_METHOD 1 */
72 /* #define SHOW_SAMPLE_SPOTS 1 */ /* try this! bright spots at sample pts */
74 /*****************************************************************************
75 * Local typedefs
76 ******************************************************************************/
80 /*****************************************************************************
81 * Local variables
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;
95 #ifdef RADSTATS
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;
103 #endif
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.
112 FILE *ot_fd = NULL;
115 /*****************************************************************************
116 * Static functions
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 /*****************************************************************************
128 * FUNCTION
130 * Compute_Ambient
132 * INPUT
134 * OUTPUT
136 * RETURNS
138 * AUTHOUR
140 * Jim McElhiney
142 * DESCRIPTION
144 * Main entry point for calculated diffuse illumination
146 * CHANGES
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)
155 int retval, reuse;
156 DBL grey, save_bound;
158 save_bound = opts.Radiosity_Error_Bound;
159 if ( Weight < .25 )
161 opts.Radiosity_Error_Bound += (.25 - Weight);
163 reuse = ra_reuse(IPoint, S_Normal, Ambient_Colour);
164 opts.Radiosity_Error_Bound = save_bound;
167 if ( reuse )
169 ra_reuse_count++;
170 retval = 0;
172 else
174 ra_gather(IPoint, S_Normal, Ambient_Colour, Weight);
176 ra_gather_count++; /* keep a running count */
178 retval = 1;
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);
196 return(retval);
201 /*****************************************************************************
203 * FUNCTION
205 * ra_reuse
207 * INPUT
209 * OUTPUT
211 * RETURNS
213 * AUTHOUR
215 * Jim McElhiney
217 * DESCRIPTION
219 * Returns whether or not there were some prestored values close enough to
220 * reuse.
222 * CHANGES
224 * --- 1994 : Creation.
226 ******************************************************************************/
228 static long ra_reuse/* return value */(VECTOR IPoint, VECTOR S_Normal, COLOUR Illuminance)
230 long i;
231 WT_AVG gather;
233 if (ot_root != NULL)
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);
278 else
280 gather.Good_Count = 0; /* No tree, so no reused values */
283 return(gather.Good_Count);
288 /*****************************************************************************
290 * FUNCTION
292 * ra_average_near
294 * INPUT
296 * OUTPUT
298 * RETURNS
300 * AUTHOUR
302 * Jim McElhiney
304 * DESCRIPTION
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
315 * CHANGES
317 * --- 1994 : Creation.
319 ******************************************************************************/
321 static long ra_average_near(OT_BLOCK *block, void *void_info)
323 long ind, i;
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;
329 DBL quickcheck_rad;
332 #ifdef RADSTATS
333 ot_doblockcount++;
334 #endif
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)
381 #ifdef RADSTATS
382 ot_lowerrorcount++;
383 #endif
385 if (dist > 0.000001)
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
390 * meaningless
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);
396 else
398 in_front = 1.0;
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))
410 #ifdef RADSTATS
411 ot_dotokcount++;
412 #endif
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 */
417 #endif
418 #ifdef SAW_METHOD
419 weight = 1.0 - (error_reuse / info->Current_Error_Bound); /* 0 < t < 1 */
420 #endif
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;
429 #ifndef RAD_GRADIENT
430 dr = dg = db = 0.;
431 #endif
432 #if 0
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;
437 #endif
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.;
449 #endif
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++;
460 info->Good_Count++;
462 ind = -1;
464 if (info->Close_Count < opts.Radiosity_Nearest_Count)
466 ind = info->Close_Count++;
468 else
470 for (i = 0; i < info->Close_Count; i++)
472 if (dist < info->Distance[i])
474 ind = i;
476 i = opts.Radiosity_Nearest_Count;
481 if (ind != -1)
483 info->Distance[ind] = dist;
485 info->Weight[ind] = weight;
487 VScale(info->Weight_Times_Illuminance[ind], prediction, weight);
496 return(1);
501 /*****************************************************************************
503 * FUNCTION
505 * ra_gather
507 * INPUT
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
513 * OUTPUT
514 * The average colour of light of objects visible from the specified point.
515 * The colour is returned in the Illuminance parameter.
518 * RETURNS
520 * AUTHOUR
522 * Jim McElhiney
524 * DESCRIPTION
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).
535 * CHANGES
537 * --- 1994 : Creation.
539 ******************************************************************************/
541 static void ra_gather(VECTOR IPoint, VECTOR S_Normal, COLOUR Illuminance, DBL Weight)
543 extern FRAME Frame;
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;
554 RAY New_Ray;
555 OT_BLOCK *block;
556 OT_ID id;
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.;
596 else
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);
627 else
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;
646 #ifdef SAFE_BUT_SLOW
647 opts.Quality_Flags = Quality_Values[opts.Radiosity_Quality];
648 #else
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;
652 #endif
654 /* Go down in recursion, trace the result, and come back up */
656 Trace_Level++;
657 Radiosity_Trace_Level++;
658 depth = Trace(&New_Ray, Temp_Colour, weight);
659 Radiosity_Trace_Level--;
660 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;
671 gradient_count++;
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;
692 else
694 #ifdef RADSTATS
695 hit++;
696 #endif
699 if (depth < min_dist)
701 min_dist = depth;
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);
762 #ifdef RADSTATS
763 ot_blockcount++;
764 #endif
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));
771 /* beta */
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);
798 block->next = NULL;
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.
820 * INPUT
822 * OUTPUT
824 * RETURNS Nothing
826 * AUTHOUR Jim McElhiney
828 * DESCRIPTION
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
833 * 0 to 255.
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.
838 * CHANGES
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
857 * INPUT Nothing.
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
866 * DESCRIPTION
868 * CHANGES
870 * --- Jan 1996 : Creation.
872 ******************************************************************************/
873 long
874 Initialize_Radiosity_Code()
876 long retval, used_existing_file;
877 FILE *fd;
878 char *modes, rad_cache_filename[256];
880 retval = 1; /* assume the best */
882 if ( opts.Options & RADIOSITY )
884 opts.Radiosity_Preview_Done = 0;
886 ra_gather_count = 0;
887 ra_reuse_count = 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;
898 #ifdef RADSTATS
899 ot_seenodecount = 0;
900 ot_seeblockcount = 0;
901 ot_doblockcount = 0;
902 ot_dotokcount = 0;
903 ot_lowerrorcount = 0;
904 ot_lastcount = 0;
905 #endif
907 if ( ot_fd != NULL ) /* if already open for some unknown reason, close it */
909 fclose(ot_fd);
910 ot_fd = 0;
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" */
922 if ( fd != NULL) {
923 used_existing_file = ot_read_file(fd);
924 retval &= used_existing_file;
925 fclose(fd);
928 else
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);
947 return retval;
951 /*****************************************************************************
953 * FUNCTION Deinitialize_Radiosity_Code()
955 * INPUT Nothing.
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
964 * DESCRIPTION
965 * Wrap up and free any radiosity-specific features.
966 * Note that this function is safe to call even if radiosity was not on.
968 * CHANGES
970 * --- Jan 1996 : Creation.
972 ******************************************************************************/
973 long
974 Deinitialize_Radiosity_Code()
976 long retval;
977 char rad_cache_filename[256];
978 FILE *fd;
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 ) {
986 fclose(ot_fd);
987 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);
1016 if ( fd != NULL ) {
1017 retval &= ot_save_tree(ot_root, fd);
1019 fclose(fd);
1021 else
1023 retval = 0;
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 */
1038 return retval;