/**************************************************************************** * octree.c * * This module contains all oct-tree functions for radiosity. * * This file was written by Jim McElhiney. * * from Persistence of Vision(tm) Ray Tracer * Copyright 1996 Persistence of Vision Team *--------------------------------------------------------------------------- * NOTICE: This source code file is provided so that users may experiment * with enhancements to POV-Ray and to port the software to platforms other * than those supported by the POV-Ray Team. There are strict rules under * which you are permitted to use this file. The rules are in the file * named POVLEGAL.DOC which should be distributed with this file. If * POVLEGAL.DOC is not available or for more info please contact the POV-Ray * Team Coordinator by leaving a message in CompuServe's Graphics Developer's * Forum. The latest version of POV-Ray may be found there as well. * * This program is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * *****************************************************************************/ /************************************************************************ * Oct-tree routines. Used by Radiosity calculation routies. * * To understand the relationship between an ot_id (x,y,z,size) and * a place in model space, you have to scale the integer values: * The nominal space occupied is given as follows: * fsize = pow(2,size-127); * lox = (float)x *fsize; loy = (float)y * fsize; loz = (float)z * fsize; * hix = lox + fsize; hiy = loy + fsize; hiz = loz + fsize; * All elements within this node are guaranteed to stick outside of the * nominal box by a distance of less than fsize/2 in x, y, and/or z. * Therefore, the following box is guaranteed to contain all of the * elements: * minx = lox - fsize/2.; miny = loy - fsize/2.; minz = loz - fsize/2.; * maxx = lox + fsize/2.; maxy = loy + fsize/2.; maxz = loz + fsize/2.; * Implemented by and (c) 1994-6 Jim McElhiney, mcelhiney@acm.org or 71201,1326 * All standard POV distribution rights granted. All other rights reserved. *************************************************************************/ #include "frame.h" #include "vector.h" #include "povproto.h" #include "povray.h" #include "octree.h" #include "radiosit.h" /***************************************************************************** * Local preprocessor defines ******************************************************************************/ #define SAFE_METHOD 1 /* #define OT_DEBUG 1 */ /***************************************************************************** * Local typedefs ******************************************************************************/ /***************************************************************************** * Local variables ******************************************************************************/ #ifdef RADSTATS long ot_inscount = 0; long ot_nodecount = 0; long ot_blockcount = 0; long ot_minsize = 1000; long ot_maxsize = 0; #endif #ifdef RADSTATS long overflows = 0; long thisloops = 0; long totloops = 0; long minloops = 1000; long maxloops = 0; #endif /***************************************************************************** * Static functions ******************************************************************************/ long ot_save_node PARAMS((VECTOR point, OT_ID *node)); long ot_traverse PARAMS((OT_NODE *subtree, long (*function)(OT_BLOCK *block, void * handle1), void * handle2)); long ot_free_subtree PARAMS((OT_NODE *node)); /***************************************************************************** * * FUNCTION * * ot_ins * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Called with a pointer to the root pointer, because this routine can * create a new root block higher up. * * CHANGES * * --- 1994 : Creation. * ******************************************************************************/ void ot_ins(root_ptr, new_block, new_id) OT_NODE **root_ptr; OT_BLOCK *new_block; /* The data to store */ OT_ID *new_id; /* The oct-tree node id at which to store */ { long target_size, dx, dy, dz, index; OT_NODE *temp_node, *this_node; OT_ID temp_id; #ifdef RADSTATS ot_inscount++; #endif /* If there is no root yet, create one. This is a first-time-through */ if (*root_ptr == NULL) { *root_ptr = (OT_NODE *)POV_CALLOC(1, sizeof(OT_NODE), "octree node"); #ifdef RADSTATS ot_nodecount = 1; #endif /* Might as well make it the right size for our first data block */ (*root_ptr)->Id = *new_id; } /* * What if the thing we're inserting is bigger than the biggest node in the * existing tree? Add a new top to the tree till it's big enough. */ while ((*root_ptr)->Id.Size < new_id->Size) { /* root too small */ ot_newroot(root_ptr); } /* * What if the new block is the right size, but for an area of space which * does not overlap with the current tree? New bigger root, until the * areas overlap. */ /* Build a temp id, like a cursor to move around with */ temp_id = *new_id; /* First, find the parent of our new node which is as big as root */ while (temp_id.Size < (*root_ptr)->Id.Size) { ot_parent(&temp_id, &temp_id); } while((temp_id.x != (*root_ptr)->Id.x) || (temp_id.y != (*root_ptr)->Id.y) || (temp_id.z != (*root_ptr)->Id.z)) { /* while separate subtrees... */ ot_newroot(root_ptr); /* create bigger root */ ot_parent(&temp_id, &temp_id); /* and move cursor up one, too */ } /* * At this point, the new node is known to fit under the current tree * somewhere. Go back down the tree to the right level, making new nodes * as you go. */ this_node = *root_ptr; /* start at the root */ while (this_node->Id.Size > new_id->Size) { /* First, pick the node id of the child we are talking about */ target_size = this_node->Id.Size - 1; /* this is the size we want */ temp_id = *new_id; /* start with the new one */ while (temp_id.Size < target_size) { ot_parent(&temp_id, &temp_id); /* climb up till one below here */ } /* Now we have to pick which child number we are talking about */ dx = (temp_id.x & 1) * 4; dy = (temp_id.y & 1) * 2; dz = (temp_id.z & 1); index = dx + dy + dz; if (this_node->Kids[index] == NULL) { /* Next level down doesn't exist yet, so create it */ temp_node = (OT_NODE *)POV_CALLOC(1, sizeof(OT_NODE), "octree node"); #ifdef RADSTATS ot_nodecount++; #endif /* Fill in the data */ temp_node->Id = temp_id; /* Add it onto the tree */ this_node->Kids[index] = temp_node; } /* Now follow it down and repeat */ this_node = this_node->Kids[index]; } /* Finally, we're in the right place, so insert the new value */ ot_list_insert(&(this_node->Values), new_block); } /***************************************************************************** * * FUNCTION * * ot_list_insert * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * - * * CHANGES * * --- 1994 : Creation. * ******************************************************************************/ void ot_list_insert(list_head, new_block) OT_BLOCK **list_head; OT_BLOCK *new_block; { new_block->next = *list_head; /* copy addr of old first block */ *list_head = new_block; } /***************************************************************************** * * FUNCTION * * ot_newroot * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Modify a tree so that it has a bigger root, owning the old root passed in. * Note that this function is called with a POINTER TO the root pointer, * since the root pointer will be changed. * * CHANGES * * --- 1994 : Creation. * ******************************************************************************/ void ot_newroot(root_ptr) OT_NODE **root_ptr; { OT_NODE *newroot; long dx, dy, dz, index; newroot = (OT_NODE *)POV_CALLOC(1, sizeof(OT_NODE), "octree node"); #ifdef RADSTATS ot_nodecount++; #endif ot_parent(&newroot->Id, &((*root_ptr)->Id)); /* sets the x/y/z/size id */ /* * Function: decide which child of the new root the old root is. Theory: * x,y,z values are measured in block sizes, and are a factor of 2 smaller * at each level higher. The parent of both (3,4,5,k) and (2,5,4,k) is * (1,2,2,k+1), so the oddness of the child's ordinates determines which * child it is, and hence the value of the index into the parent's array of * children. First half of array (4 entries) is kids with low/even x; * First half of those is kids with low/even y (2 entries), and the very * first entry is the one with low/even everything. */ dx = ((*root_ptr)->Id.x & 1) * 4; dy = ((*root_ptr)->Id.y & 1) * 2; dz = ((*root_ptr)->Id.z & 1); index = dx + dy + dz; newroot->Kids[index] = *root_ptr; *root_ptr = newroot; } /***************************************************************************** * * FUNCTION * * ot_dist_traverse * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Call "function(&node, handle)" for every node which is less than a node * width from the test point. Post traverse = small stuff first = the kids * before this node. "function(*node, handle)" must return true/false on * whether or not to continue with further processing. Returns 0 if * execution was halted this way, 1 otherwise; * * CHANGES * * --- 1994 : Creation. * ******************************************************************************/ long ot_dist_traverse(subtree, point, bounce_depth, function, handle) OT_NODE *subtree; VECTOR point; int bounce_depth; /* only those nodes with this recur depth */ long (*function) PARAMS((OT_BLOCK * bl, void *handle1)); void *handle; { #ifdef RADSTATS extern long ot_seenodecount, ot_seeblockcount; #endif long i, oksofar; OT_NODE *this_node; OT_BLOCK *this_block; #ifdef RADSTATS ot_seenodecount++; #endif /* First, recurse to the child nodes */ oksofar = 1; for (i = 0; i < 8 && oksofar; i++) { /* for each potential kid */ this_node = subtree->Kids[i]; if (this_node != NULL) { /* ...which exists */ if (ot_point_in_node(point, &this_node->Id)) { /* ...and in range */ oksofar = ot_dist_traverse(this_node, point, bounce_depth, function, handle); } } } /* * Now, call the specified routine for each data block hung off this tree * node */ /* if ( ot_point_in_node(point, &subtree->Id) ) { */ { this_block = subtree->Values; while (oksofar && (this_block != NULL)) { #ifdef RADSTATS if (subtree->Id.Size < 100 || subtree->Id.Size > 140 ) { Debug_Info("bounds error, unreasonable size %d\n", subtree->Id.Size); } ot_seeblockcount++; #endif if ((int)this_block->Bounce_Depth == bounce_depth) { oksofar = (*function) (this_block, handle); } this_block = this_block->next; } } return oksofar; } /***************************************************************************** * * FUNCTION * * ot_traverse - call a function for every block in the tree. * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Call "function(&block, handle)" for every block hanging off every node. * Post traverse = small stuff first = the kids before this node. * "function(*node, handle)" must return true/false on whether or not to * Continue with further processing. Returns 0 if execution * was halted this way, 1 otherwise; * * CHANGES * * --- Jan 1996 : Creation. * ******************************************************************************/ long ot_traverse(subtree, function, handle) /* Call "function(&block, handle)" for every block hanging off every node. Post traverse = small stuff first = the kids before this node. "function(*node, handle)" must return true/false on whether or not to Continue with further processing. Returns 0 if execution was halted this way, 1 otherwise; */ OT_NODE *subtree; long (*function)(OT_BLOCK * bl, void * handle1); void *handle; { long i, oksofar; OT_NODE *this_node; OT_BLOCK *this_block; /* First, recurse to the child nodes */ oksofar = 1; if (subtree!=NULL) { for (i=0; i<8 && oksofar; i++ ) /* for each potential kid */ { this_node = subtree->Kids[i]; if ( this_node != NULL ) /* ...which exists */ { oksofar = ot_traverse(this_node, function, handle); } } /* Now, call the specified routine for each data block hung off this tree node */ this_block = subtree->Values; while ( oksofar && (this_block != NULL) ) { oksofar = (*function)(this_block, handle); this_block = this_block->next; } } return oksofar; } /***************************************************************************** * * FUNCTION * * ot_point_in_node * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Returns true if the specified point is inside the max extent of the node * with the specified ID. * * CHANGES * * --- 1994 : Creation. * ******************************************************************************/ long ot_point_in_node(point, id) VECTOR point; OT_ID *id; { DBL sized, minx, miny, minz, lox, loy, loz, hix, hiy, hiz; union { float f; long l; } size; /* MUST be float, NOT DBL */ size.l = id->Size << 23; sized = (DBL) size.f; minx = (DBL) id->x * sized - OT_BIAS; miny = (DBL) id->y * sized - OT_BIAS; minz = (DBL) id->z * sized - OT_BIAS; lox = minx - sized * .5; hix = minx + sized * 1.5; loy = miny - sized * .5; hiy = miny + sized * 1.5; loz = minz - sized * .5; hiz = minz + sized * 1.5; return(point[X] >= lox && point[X] < hix && point[Y] >= loy && point[Y] < hiy && point[Z] >= loz && point[Z] < hiz); } /***************************************************************************** * * FUNCTION * * ot_index_sphere * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Return the oct-tree index for an object with the specified bounding * sphere. This is the smallest box in the tree that this object fits in with * a maximum 50% hand-over in any (or all) directions. For example, an object * at (.49, .49, 49) of radius 1 fits in the box (0,0,0) size 127 (length 1). * * CHANGES * * --- 1994 : Creation. * ******************************************************************************/ void ot_index_sphere(point, radius, id) VECTOR point; DBL radius; OT_ID *id; { VECTOR min_point, max_point; min_point[X] = point[X] - radius; min_point[Y] = point[Y] - radius; min_point[Z] = point[Z] - radius; max_point[X] = point[X] + radius; max_point[Y] = point[Y] + radius; max_point[Z] = point[Z] + radius; ot_index_box(min_point, max_point, id); #ifdef RADSTATS if (id->Size < ot_minsize) { ot_minsize = id->Size; } if (id->Size > ot_maxsize) { ot_maxsize = id->Size; } #endif } /***************************************************************************** * * FUNCTION * * ot_index_box * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Return the oct-tree index for an object with the specified bounding box. * near_point is lox, loy, loz; far_point is hix, hiy, hiz. This is the * smallest box in the tree that this object fits in with a maximum 50% * hang-over in any (or all) directions. For example, an object with extent * (-.49, -.49, -49) to (1.49, 1.49, 1.49) is the largest that fits in the * box (0,0,0) with size 127 (length 1). * * PORTABILITY WARNING: this function REQUIRES IEEE single precision floating * point format to work. This is true of most common systems except VAXen, * Crays, and Alpha AXP in VAX compatibility mode. Local "float" variables * can NOT be made double precision "double" or "DBL". * * CHANGES * * --- 1994 : Creation. * ******************************************************************************/ void ot_index_box(min_point, max_point, id) VECTOR min_point; VECTOR max_point; OT_ID *id; { long done, idx, idy, idz; float dx, dy, dz, maxdel; /* MUST BE "float" NOT "DBL" */ DBL bsized, maxord; union { float f; long l; } convert; OT_ID base_id, test_id; dx = (float) (max_point[X] - min_point[X]); dy = (float) (max_point[Y] - min_point[Y]); dz = (float) (max_point[Z] - min_point[Z]); maxdel = MAX3(dx, dy, dz); /* * This hex operation does a floor to next lower power of 2, by clearing * all of the mantissa bits. Works only on IEEE single precision floats */ convert.f = maxdel; convert.l &= 0xff800000; bsized = (DBL) convert.f; #ifdef SAFE_METHOD /* * This block checks for the case where the node id would cause integer * overflow, since it is a small buffer far away */ maxord = MAX3(fabs(min_point[X]), fabs(min_point[Y]), fabs(min_point[Z])); maxord += OT_BIAS; while (maxord / bsized > 1000000000.) { #ifdef RADSTATS overflows++; #endif bsized *= 2.; } #endif /* calculate the smallest possible node that the item might fit into */ base_id.x = (long) floor((min_point[X] + OT_BIAS) / bsized); base_id.y = (long) floor((min_point[Y] + OT_BIAS) / bsized); base_id.z = (long) floor((min_point[Z] + OT_BIAS) / bsized); /* * This magic hex operation extracts the exponent, which gives us an * integer number suitable for labelling a range of a power of 2. In IEEE * format, value = pow(2,exponent-127). Therefore, if our index is, say, * 129, then the item has a maximum extent of (2 to the (129-127)), or * about 4 space units. */ convert.f = (float) bsized; base_id.Size = (convert.l & 0x7f800000) >> 23; /* Now increase the node size until it fits for sure */ #ifdef RADSTATS thisloops = 0; #endif done = 0; while (!done) { test_id.Size = base_id.Size; for (idx = 0; idx < 2 && !done; idx++) { for (idy = 0; idy < 2 && !done; idy++) { for (idz = 0; idz < 2 && !done; idz++) { test_id.x = base_id.x + idx; test_id.y = base_id.y + idy; test_id.z = base_id.z + idz; if (ot_point_in_node(min_point, &test_id) && ot_point_in_node(max_point, &test_id)) { done = 1; } } } } /* * Debug_Info("looping %d,%d,%d,%d min=%d, max=%d\n", test_id.x, test_id.y, * test_id.z, test_id.Size, ot_point_in_node(min_point, &test_id), * ot_point_in_node(max_point, &test_id)); */ ot_parent(&base_id, &base_id); #ifdef RADSTATS totloops++; thisloops++; #endif } #ifdef RADSTATS if (thisloops < minloops) minloops = thisloops; if (thisloops > maxloops) maxloops = thisloops; #endif *id = test_id; #ifdef OT_DEBUG if (id->Size > 139) { Debug_Info("unusually large id, maxdel=%.4f, bsized=%.4f, isize=%d\n", maxdel, bsized, id->Size); } #endif } /***************************************************************************** * * FUNCTION * * ot_parent * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Set the x/y/z/size block ID info of dad = the parent ID of kid * * CHANGES * * --- 1994 : Creation. * ******************************************************************************/ void ot_parent(dad_id, kid_id) OT_ID *dad_id, *kid_id; { dad_id->Size = kid_id->Size + 1; dad_id->x = (kid_id->x > 0) ? (kid_id->x >> 1) : (kid_id->x - 1) / 2; dad_id->y = (kid_id->y > 0) ? (kid_id->y >> 1) : (kid_id->y - 1) / 2; dad_id->z = (kid_id->z > 0) ? (kid_id->z >> 1) : (kid_id->z - 1) / 2; } /***************************************************************************** * * FUNCTION * * ot_save_tree * * INPUT * * OUTPUT * * RETURNS 1 for success, 0 for failure. * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Given the root pointer of the in-memory cache tree, and a file descriptor * of a file you want to write to, write the whole tree to that file. * * CHANGES * * Jan 1996 : Creation by JDM. * * TO DO * * Code must be written which turns Radiosity_File_* flags on and off. * These flags should be in the opts structure. * ******************************************************************************/ long ot_save_tree(root, fd) OT_NODE *root; FILE *fd; { long retval = 0; if ( fd != NULL ) { retval = ot_traverse(root, &ot_write_block, (void *)fd); } else { Warning(0.0, "bad radiosity cache file handle\n"); } return retval; } /***************************************************************************** * * FUNCTION * * ot_write_block * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Write one block (not a node) from the memory cache to the cache file. * * CHANGES * * --- Jan 1996 : Creation. * ******************************************************************************/ long ot_write_block(bl, fd) OT_BLOCK *bl; void *fd; /* must be passed as void * for compatibility */ { if ( bl->Bounce_Depth == 1 ) { fprintf((FILE *)fd, "C%ld\t%g\t%g\t%g\t%02x%02x%02x\t%.4f\t%.4f\t%.4f\t%g\t%g\t%02x%02x%02x\n", (long)bl->Bounce_Depth, bl->Point[X], bl->Point[Y], bl->Point[Z], (long)((bl->S_Normal[X]+1.)*.5*254.+.499999), (long)((bl->S_Normal[Y]+1.)*.5*254.+.499999), (long)((bl->S_Normal[Z]+1.)*.5*254.+.499999), bl->Illuminance[X], bl->Illuminance[Y], bl->Illuminance[Z], bl->Harmonic_Mean_Distance, bl->Nearest_Distance, (long)((bl->To_Nearest_Surface[X]+1.)*.5*254.+.499999), (long)((bl->To_Nearest_Surface[Y]+1.)*.5*254.+.499999), (long)((bl->To_Nearest_Surface[Z]+1.)*.5*254.+.499999) ); } return 1; } /***************************************************************************** * * FUNCTION * * ot_free_tree() - get rid of the entire in-memory radiosity cache tree, * and zero the pointer to its root. * * INPUT - pointer to the tree root pointer. * * RETURNS - success 1, failure 0 * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Free a complete radiosity cache tree, and all of its nodes and blocks. * NOTE parameter is a pointer to the tree pointer...tree pointer will get zeroed. * Example call: * ot_free_tree(&ot_root); * Returns 1 for success, 0 for failure. * * CHANGES * * --- Jan 1996 : Creation. * ******************************************************************************/ long ot_free_tree(ppRoot) /* Free a complete radiosity cache tree, and all of its nodes and blocks. Note parameter is a pointer to the tree pointer...tree pointer will get zeroed. Example call: ot_free_tree(&ot_root); Returns 1 for success, 0 for failure. */ OT_NODE **ppRoot; { long all_ok; all_ok = ot_free_subtree(*ppRoot); *ppRoot = NULL; return all_ok; } /***************************************************************************** * * FUNCTION * * ot_free_subtree - free every node from this node downwards, and all blocks * hanging off those nodes, and then free the node which was passed. * * INPUT * * OUTPUT * * RETURNS * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Set the x/y/z/size block ID info of dad = the parent ID of kid * * CHANGES * * --- Jan 1996 : Creation. * ******************************************************************************/ long ot_free_subtree(subtree) /* Free this subtree. That is, free all of its daughters, then free all of the blocks hanging off this node, then free this node itself. Returns 0 if problems were encountered anywhere in the tree. Currently, this code assumes success. If called with an invalid tree pointer, it would probably crash with a memory protection error. */ OT_NODE *subtree; { long i, oksofar; OT_NODE *this_node; OT_BLOCK *this_block, *next_block; /* First, recurse to the child nodes */ oksofar = 1; for (i=0; i<8 && oksofar; i++ ) /* for each potential kid */ { this_node = subtree->Kids[i]; if ( this_node != NULL ) { /* ...which exists */ oksofar &= ot_free_subtree(this_node); } } /* Now, free each block hanging off this node. */ this_block = subtree->Values; while ( this_block != NULL ) { next_block = this_block->next; POV_FREE(this_block); this_block = next_block; } /* Finally, free this block itself */ POV_FREE(subtree); return oksofar; } /***************************************************************************** * * FUNCTION * * ot_read_file * * INPUT * file descriptor handle of file (already opened) to read into memory. * * OUTPUT * * RETURNS - Success 1 / failure 0 * * AUTHOUR * * Jim McElhiney * * DESCRIPTION * * Read in a radiosity cache file, building a tree from its values. * If there is an existing tree, these values are added to it. * * CHANGES * * --- Jan 1996 : Creation. * ******************************************************************************/ long ot_read_file(fd) /* Read in a radiosity cache file, building a tree from its values. If there is an existing tree, these values are added to it. */ FILE *fd; { long retval, line_num, tempdepth, tx, ty, tz, goodreads; int count, goodparse, readret; DBL brightness; OT_BLOCK bl, *new_block; OT_ID id; char normal_string[30], to_nearest_string[30], line[101]; memset(&bl, 0, sizeof(OT_BLOCK)); if ( fd != NULL ) { line_num = 0; Make_Colour(Radiosity_Gather_Total, 0., 0., 0.); Radiosity_Gather_Total_Count = 0; goodparse = 1; goodreads = 0; while ( ((readret = fscanf(fd, "%99[^\n]\n", line)) == 1) && goodparse ) { switch ( line[0] ) { case 'B': /* the file contains the old radiosity_brightness value */ { if ( sscanf(line, "B%lf\n", &brightness) == 1 ) { opts.Radiosity_Brightness = brightness; } break; } case 'P': /* the file made it to the point that the Preview was done */ { opts.Radiosity_Preview_Done = 1; break; } case 'C': { count = sscanf(line, "C%d %lf %lf %lf %s %f %f %f %f %f %s\n", &tempdepth, /* since you can't scan a short */ &bl.Point[X], &bl.Point[Y], &bl.Point[Z], normal_string, &bl.Illuminance[X], &bl.Illuminance[Y], &bl.Illuminance[Z], &bl.Harmonic_Mean_Distance, &bl.Nearest_Distance, to_nearest_string ); if ( count == 11 ) { bl.Bounce_Depth = (short)tempdepth; /* normals aren't very critical for direction precision, so they are packed */ sscanf(normal_string, "%02lx%02lx%02lx", &tx, &ty, &tz); bl.S_Normal[X] = ((double)tx * (1./ 254.))*2.-1.; bl.S_Normal[Y] = ((double)ty * (1./ 254.))*2.-1.; bl.S_Normal[Z] = ((double)tz * (1./ 254.))*2.-1.; VNormalizeEq(bl.S_Normal); sscanf(to_nearest_string, "%02lx%02lx%02lx", &tx, &ty, &tz); bl.To_Nearest_Surface[X] = ((double)tx * (1./ 254.))*2.-1.; bl.To_Nearest_Surface[Y] = ((double)ty * (1./ 254.))*2.-1.; bl.To_Nearest_Surface[Z] = ((double)tz * (1./ 254.))*2.-1.; VNormalizeEq(bl.To_Nearest_Surface); line_num++; new_block = (OT_BLOCK *)POV_MALLOC(sizeof (OT_BLOCK), "octree node from file"); if ( new_block != NULL ) { memcpy(new_block, &bl, sizeof (OT_BLOCK)); ot_index_sphere(bl.Point, bl.Harmonic_Mean_Distance * opts.Radiosity_Error_Bound, &id); ot_ins(&ot_root, new_block, &id); goodreads++; } else { goodparse = 0; /* allocation error, better stop now */ } } break; } default: { /* wrong leading character on line, just try again on next line */ } } /* end switch */ } /* end while-reading loop */ if ( (readret != EOF) || !goodparse ) { Status_Info("\nError processing the radiosity cache file at line %ld", (long)line); retval = 0; } else { if ( goodreads > 0 ) { Status_Info("\nReloaded %ld values from radiosity cache file.", goodreads); } else { Status_Info("\nUnable to read any values from the radiosity cache file."); } retval = 1; } } else { retval = 0; } return retval; }