1 //////////////////////////////////////////////////////////////////////
2 //
3 //                             Pixie
4 //
5 // Copyright � 1999 - 2003, Okan Arikan
6 //
7 // Contact: okan@cs.utexas.edu
8 //
9 //	This library is free software; you can redistribute it and/or
10 //	modify it under the terms of the GNU Lesser General Public
11 //	License as published by the Free Software Foundation; either
12 //	version 2.1 of the License, or (at your option) any later version.
13 //
14 //	This library is distributed in the hope that it will be useful,
15 //	but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 //	Lesser General Public License for more details.
18 //
19 //	You should have received a copy of the GNU Lesser General Public
20 //	License along with this library; if not, write to the Free Software
21 //	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 //
23 ///////////////////////////////////////////////////////////////////////
24 ///////////////////////////////////////////////////////////////////////
25 //
26 //  File				:	options.cpp
27 //  Classes				:	COptions
28 //  Description			:	Implementation
29 //
30 ////////////////////////////////////////////////////////////////////////
31 #include <math.h>
32 #include <stdio.h>
33 #include <string.h>
34 
35 #ifdef __APPLE__
36 #include <sys/stat.h>
37 #include <CoreServices/CoreServices.h>
38 #ifdef check   // Apple's library defines "check"
39 #undef check
40 #endif
41 #endif
42 
43 #include "options.h"
44 #include "texture.h"
45 #include "stats.h"
46 #include "ri_config.h"
47 
48 ///////////////////////////////////////////////////////////////////////
49 // Class				:	COptions
50 // Method				:	optionsDeleteSearchPath
51 // Description			:	Delete the searchpath
52 // Return Value			:	-
53 // Comments				:
optionsDeleteSearchPath(TSearchpath * cPath)54 static	void		optionsDeleteSearchPath(TSearchpath *cPath) {
55 	TSearchpath	*nPath;
56 
57 	for(;cPath!=NULL;) {
58 		nPath	=	cPath->next;
59 		free(cPath->directory);
60 		delete cPath;
61 		cPath	=	nPath;
62 	}
63 }
64 
65 ///////////////////////////////////////////////////////////////////////
66 // Class				:	COptions
67 // Method				:	optionsCloneSearchPath
68 // Description			:	Clone a search path
69 // Return Value			:	-
70 // Comments				:
optionsCloneSearchPath(TSearchpath * cPath)71 static	TSearchpath	*optionsCloneSearchPath(TSearchpath *cPath) {
72 	TSearchpath	*nPath	=	NULL;
73 	TSearchpath	*lPath	=	NULL;
74 
75 	for(;cPath!=NULL;cPath=cPath->next) {
76 		TSearchpath	*ncPath	=	new TSearchpath;
77 
78 		ncPath->directory	=	strdup(cPath->directory);
79 		ncPath->next		=	NULL;
80 
81 		if (lPath == NULL) {
82 			lPath			=	ncPath;
83 			nPath			=	ncPath;
84 		} else {
85 			lPath->next		=	ncPath;
86 			lPath			=	ncPath;
87 		}
88 	}
89 
90 	return nPath;
91 }
92 
93 
94 ///////////////////////////////////////////////////////////////////////
95 // Class				:	COptions::CDisplay
96 // Method				:	CDisplay
97 // Description			:	Ctor
98 // Return Value			:	-
99 // Comments				:
CDisplay()100 COptions::CDisplay::CDisplay() {
101 	outDevice		=	NULL;
102 	outName			=	NULL;
103 	outSamples		=	NULL;
104 	next			=	NULL;
105 
106 	startFunction	=	NULL;
107 	dataFunction	=	NULL;
108 	finishFunction	=	NULL;
109 
110 	quantizer[0]	=	-1;	// Copy from the default
111 
112 	numParameters	=	0;
113 	parameters		=	NULL;
114 }
115 
116 ///////////////////////////////////////////////////////////////////////
117 // Class				:	COptions::CDisplay
118 // Method				:	CDisplay
119 // Description			:	Ctor
120 // Return Value			:	-
121 // Comments				:
CDisplay(const CDisplay * other)122 COptions::CDisplay::CDisplay(const CDisplay *other) {
123 
124 	outDevice		=	strdup(other->outDevice);
125 	outName			=	strdup(other->outName);
126 	outSamples		=	strdup(other->outSamples);
127 	quantizer[0]	=	other->quantizer[0];
128 	quantizer[1]	=	other->quantizer[1];
129 	quantizer[2]	=	other->quantizer[2];
130 	quantizer[3]	=	other->quantizer[3];
131 	quantizer[4]	=	other->quantizer[4];
132 
133 	startFunction	=	other->startFunction;
134 	dataFunction	=	other->dataFunction;
135 	finishFunction	=	other->finishFunction;
136 
137 	if (other->numParameters > 0) {
138 		int	i;
139 
140 		numParameters	=	other->numParameters;
141 		parameters		=	new TDisplayParameter[numParameters];
142 
143 		// Duplicate the parameters
144 		for (i=0;i<numParameters;i++) {
145 			parameters[i]		=	other->parameters[i];
146 			parameters[i].name	=	strdup(other->parameters[i].name);
147 			switch(parameters[i].type) {
148 			case FLOAT_PARAMETER:
149 				parameters[i].data		=	new	float[parameters[i].numItems];
150 				memcpy(parameters[i].data,other->parameters[i].data,parameters[i].numItems*sizeof(float));
151 				break;
152 			case VECTOR_PARAMETER:
153 				parameters[i].data		=	new	float[parameters[i].numItems*3];
154 				memcpy(parameters[i].data,other->parameters[i].data,parameters[i].numItems*sizeof(float)*3);
155 				break;
156 			case MATRIX_PARAMETER:
157 				parameters[i].data		=	new	float[parameters[i].numItems*16];
158 				memcpy(parameters[i].data,other->parameters[i].data,parameters[i].numItems*sizeof(float)*16);
159 				break;
160 			case STRING_PARAMETER:
161 				char	*str;
162 
163 				str						=	strdup((char *) other->parameters[i].data);
164 				parameters[i].data		=	str;
165 				break;
166 			case INTEGER_PARAMETER:
167 				parameters[i].data		=	new	int[parameters[i].numItems];
168 				memcpy(parameters[i].data,other->parameters[i].data,parameters[i].numItems*sizeof(int));
169 				break;
170 			default:
171 				break;
172 			}
173 		}
174 	} else {
175 		numParameters	=	0;
176 		parameters		=	NULL;
177 	}
178 }
179 
180 ///////////////////////////////////////////////////////////////////////
181 // Class				:	COptions::CDisplay
182 // Method				:	~CDisplay
183 // Description			:	Dtor
184 // Return Value			:	-
185 // Comments				:
~CDisplay()186 COptions::CDisplay::~CDisplay() {
187 	if (outDevice	!= NULL)	free(outDevice);
188 	if (outName		!= NULL)	free(outName);
189 	if (outSamples  != NULL)	free(outSamples);
190 
191 	if (parameters != NULL) {
192 		int	i;
193 
194 		for (i=0;i<numParameters;i++) {
195 			switch(parameters[i].type) {
196 			case FLOAT_PARAMETER:
197 			case VECTOR_PARAMETER:
198 			case MATRIX_PARAMETER:
199 				delete [] (float *) parameters[i].data;
200 				break;
201 			case STRING_PARAMETER:
202 				free((char *) parameters[i].data);
203 				break;
204 			}
205 
206 			free(parameters[i].name);
207 		}
208 
209 		delete [] parameters;
210 	}
211 }
212 
213 ///////////////////////////////////////////////////////////////////////
214 // Class				:	COptions::CClipPlane
215 // Method				:	CCplitPlane
216 // Description			:	Ctor
217 // Return Value			:	-
218 // Comments				:
CClipPlane()219 COptions::CClipPlane::CClipPlane() {
220 }
221 
222 
223 ///////////////////////////////////////////////////////////////////////
224 // Class				:	COptions::CClipPlane
225 // Method				:	CCplitPlane
226 // Description			:	Ctor
227 // Return Value			:	-
228 // Comments				:
CClipPlane(const CClipPlane * other)229 COptions::CClipPlane::CClipPlane(const CClipPlane *other) {
230 	movvv(normal,other->normal);
231 	d		=	other->d;
232 }
233 
234 
235 ///////////////////////////////////////////////////////////////////////
236 // Class				:	COptions
237 // Method				:	COptions
238 // Description			:	All the default frame specific settings are defined here
239 // Return Value			:	-
240 // Comments				:
COptions()241 COptions::COptions() {
242 	atomicIncrement(&stats.numOptions);
243 
244 	xres					=	640;
245 	yres					=	480;
246 
247 	frame					=	-1;
248 
249 	pixelAR					=	1;
250 	frameAR					=	4.0f/3.0f;
251 
252 	cropLeft				=	0;
253 	cropRight				=	1;
254 	cropTop					=	0;
255 	cropBottom				=	1;
256 
257 	screenLeft				=	-4.0f/3.0f;
258 	screenRight				=	4.0f/3.0f;
259 	screenTop				=	1;
260 	screenBottom			=	-1;
261 
262 	clipMin					=	C_EPSILON;
263 	clipMax					=	C_INFINITY;
264 
265 	pixelVariance			=	0.05f;
266 
267 	jitter					=	0.99f;
268 
269 	hider					=	strdup("stochastic");
270 
271 #ifdef __APPLE__
272 	// Support for finding resources in Mac OS X bundles and standard Mac OS X file system locations
273 
274 	// Find the application bundle's plug-in and Resources directory
275 	char path[OS_MAX_PATH_LENGTH];
276 	char pathtmp[OS_MAX_PATH_LENGTH];
277 	CFBundleRef bundle = CFBundleGetMainBundle();
278 	if (bundle) {
279 		CFURLRef url = CFBundleCopyBuiltInPlugInsURL(bundle);
280 		if (url) {
281 			Boolean validpath = CFURLGetFileSystemRepresentation(url,true,(UInt8*)path,OS_MAX_PATH_LENGTH);
282 			if (validpath)
283 				setenv("PIXIEAPPPLUGINS", (const char*)path, 1);
284 			CFRelease(url);
285 		}
286 		url = CFBundleCopyResourcesDirectoryURL(bundle);
287 		if (url) {
288 			Boolean validpath = CFURLGetFileSystemRepresentation(url,true,(UInt8*)path,OS_MAX_PATH_LENGTH);
289 			if (validpath)
290 				setenv("PIXIEAPPRESOURCES", (const char*)path, 1);
291 			CFRelease(url);
292 		}
293 		CFRelease(bundle);
294 	}
295 
296 	// Find the application support directory (~/Library/Application Support/Pixie/PlugIns), and set the
297 	// PIXIEUSERDIR environment variable to that directory
298 	FSRef appsupport;
299     if (FSFindFolder(kUserDomain, kApplicationSupportFolderType, kCreateFolder, &appsupport) == noErr) {
300 		FSRefMakePath(&appsupport, (UInt8*)path, OS_MAX_PATH_LENGTH);
301 		sprintf(pathtmp, "%s/" PACKAGE, path);
302 		mkdir(pathtmp, 0755);
303 		setenv("PIXIEUSERDIR", (const char*)pathtmp, 1);
304 	}
305 
306 	// Find the application support directory (/Library/Application Support/Pixie/PlugIns), and set the
307 	// PIXIELOCALDIR environment variable to that directory
308     if (FSFindFolder(kLocalDomain, kApplicationSupportFolderType, kCreateFolder, &appsupport) == noErr) {
309 		FSRefMakePath(&appsupport, (UInt8*)path, OS_MAX_PATH_LENGTH);
310 		snprintf(pathtmp, OS_MAX_PATH_LENGTH, "%s/" PACKAGE, path);
311 		mkdir(pathtmp, 0755);
312 		setenv("PIXIELOCALDIR", (const char*)pathtmp, 1);
313 	}
314 
315 	// Default home, unless overridden with environment
316 	setenv("PIXIEHOME","/Library/Pixie",0);
317 
318 	archivePath             =   optionsGetSearchPath(".:%RIBS%:%PIXIEHOME%/ribs" ,NULL);
319 	proceduralPath          =   optionsGetSearchPath(".:%PROCEDURALS%:%PIXIEUSERDIR%/procedurals:%PIXIELOCALDIR%/procedurals:%PIXIEAPPPLUGINS%:%PIXIEHOME%/procedurals",NULL);
320 	texturePath             =   optionsGetSearchPath(".:%TEXTURES%:%PIXIEUSERDIR%/textures:%PIXIELOCALDIR%/textures:%PIXIEAPPRESOURCES%/textures:%PIXIEHOME%/textures",NULL);
321 	shaderPath              =   optionsGetSearchPath(".:%SHADERS%:%PIXIEUSERDIR%/shaders:%PIXIELOCALDIR%/shaders:%PIXIEAPPRESOURCES%/shaders:%PIXIEHOME%/shaders",NULL);
322 	displayPath             =   optionsGetSearchPath("%DISPLAYS%:%PIXIEUSERDIR%/displays:%PIXIELOCALDIR%/displays:%PIXIEAPPPLUGINS%:%PIXIEHOME%/displays",NULL);
323 	modulePath              =   optionsGetSearchPath("%MODULES%:%PIXIEUSERDIR%/modules:%PIXIELOCALDIR%/modules:%PIXIEAPPPLUGINS%:%PIXIEHOME%/modules",NULL);
324 
325 #else
326 	archivePath				=	optionsGetSearchPath(".:%RIBS%:" PIXIE_RIBS,NULL);
327 	proceduralPath			=	optionsGetSearchPath(".:%PROCEDURALS%:" PIXIE_PROCEDURALS,NULL);
328 	texturePath				=	optionsGetSearchPath(".:%TEXTURES%:" PIXIE_TEXTURES,NULL);
329 	shaderPath				=	optionsGetSearchPath(".:%SHADERS%:" PIXIE_SHADERS,NULL);
330 	displayPath				=	optionsGetSearchPath(".:%DISPLAYS%:" PIXIE_DISPLAYS,NULL);
331 	modulePath				=	optionsGetSearchPath(".:%MODULES%:" PIXIE_MODULES,NULL);
332 #endif
333 
334 	pixelXsamples			=	2;
335 	pixelYsamples			=	2;
336 
337 	gamma					=	1;
338 	gain					=	1;
339 
340 	pixelFilterWidth		=	2;
341 	pixelFilterHeight		=	2;
342 	pixelFilter				=	RiCatmullRomFilter;
343 
344 	colorQuantizer[0]		=	0;				// Zero
345 	colorQuantizer[1]		=	255;			// One
346 	colorQuantizer[2]		=	0;				// Min
347 	colorQuantizer[3]		=	255;			// Max
348 	colorQuantizer[4]		=	0.5;
349 	depthQuantizer[0]		=	0;				// Zero
350 	depthQuantizer[1]		=	0;				// One
351 	depthQuantizer[2]		=	0;				// Min
352 	depthQuantizer[3]		=	0;				// Max
353 	depthQuantizer[4]		=	0;
354 
355 	initv(opacityThreshold,0.996f);
356 	initv(zvisibilityThreshold,0.996f);
357 
358 	// We default to sampling motion, but this can be turned off.
359 	// Additionally, if there's no motionblur in the scene, it will be turned off
360 	flags					=	OPTIONS_FLAGS_SAMPLEMOTION;
361 
362 	displays				=	NULL;
363 
364 	clipPlanes				=	NULL;
365 
366 	relativeDetail			=	1;
367 
368 	projection				=	OPTIONS_PROJECTION_ORTHOGRAPHIC;
369 	fov						=	90;
370 
371 	nColorComps				=	3;
372 	fromRGB					=	NULL;
373 	toRGB					=	NULL;
374 
375 	fstop					=	C_INFINITY;
376 	focallength				=	1;
377 	focaldistance			=	1;
378 
379 	shutterOpen				=	0;
380 	shutterClose			=	0;
381 	shutterOffset			=	0;
382 
383 	endofframe				=	0;
384 	filelog					=	NULL;
385 
386 	numThreads              =   osAvailableCPUs();
387 	if (numThreads < 1)
388 		numThreads          =   DEFAULT_NUM_THREADS;
389 
390 	maxTextureSize			=	DEFAULT_MAX_TEXTURESIZE;
391 	maxBrickSize			=	DEFAULT_MAX_BRICKSIZE;
392 
393 	maxGridSize				=	DEFAULT_MAX_GRIDSIZE;
394 
395 	maxRayDepth				=	5;
396 	maxPhotonDepth			=	10;
397 
398 	bucketWidth				=	DEFAULT_TILE_WIDTH;
399 	bucketHeight			=	DEFAULT_TILE_HEIGHT;
400 
401 	netXBuckets				=	DEFAULT_NET_XBUCKETS;
402 	netYBuckets				=	DEFAULT_NET_YBUCKETS;
403 
404 	threadStride			=	DEFAULT_THREAD_STRIDE;
405 
406 	geoCacheMemory			=	DEFAULT_GEO_CACHE_SIZE;
407 
408 	maxEyeSplits			=	10;
409 
410 	tsmThreshold			=	DEFAULT_TSM_THRESHOLD;
411 
412 	causticIn				=	NULL;
413 	causticOut				=	NULL;
414 
415 	globalIn				=	NULL;
416 	globalOut				=	NULL;
417 
418 	numEmitPhotons			=	10000;
419 
420 	shootStep				=	1000;
421 
422 	depthFilter				=	DEPTH_MIN;
423 }
424 
425 
426 ///////////////////////////////////////////////////////////////////////
427 // Class				:	COptions
428 // Method				:	COptions
429 // Description			:	Create an exact copy of another options block
430 // Return Value			:	-
431 // Comments				:
COptions(const COptions * o)432 COptions::COptions(const COptions *o) {
433 	atomicIncrement(&stats.numOptions);
434 
435 	this[0]					=	o[0];
436 
437 	// Note: The assignment here also invokes the assignment operator of userOptions
438 	//      so there's no need for a separate copy for that
439 
440 	hider					=	strdup(o->hider);
441 
442 	archivePath				=	optionsCloneSearchPath(o->archivePath);
443 	proceduralPath			=	optionsCloneSearchPath(o->proceduralPath);
444 	texturePath				=	optionsCloneSearchPath(o->texturePath);
445 	shaderPath				=	optionsCloneSearchPath(o->shaderPath);
446 	displayPath				=	optionsCloneSearchPath(o->displayPath);
447 	modulePath				=	optionsCloneSearchPath(o->modulePath);
448 
449 	if (o->displays != NULL) {
450 		CDisplay	*cDisplay,*nDisplay;
451 
452 		displays		=	NULL;
453 		for (cDisplay=o->displays;cDisplay!=NULL;cDisplay=cDisplay->next) {
454 			nDisplay		=	new CDisplay(cDisplay);
455 			nDisplay->next	=	displays;
456 			displays		=	nDisplay;
457 		}
458 	} else {
459 		displays		=	NULL;
460 	}
461 
462 
463 	if (o->clipPlanes != NULL) {
464 		CClipPlane		*cPlane,*nPlane;
465 
466 		clipPlanes		=	NULL;
467 		for (cPlane=o->clipPlanes;cPlane!=NULL;cPlane=cPlane->next) {
468 			nPlane			=	new CClipPlane;
469 			*nPlane			=	*cPlane;
470 			nPlane->next	=	clipPlanes;
471 			clipPlanes		=	nPlane;
472 		}
473 	} else {
474 		clipPlanes		=	NULL;
475 	}
476 
477 
478 	if (o->fromRGB	!= NULL) {
479 		fromRGB		=	new float[3*nColorComps];
480 		memcpy(fromRGB,o->fromRGB,3*nColorComps*sizeof(float));
481 	} else {
482 		fromRGB		=	NULL;
483 	}
484 
485 	if (o->toRGB	!= NULL) {
486 		toRGB		=	new float[3*nColorComps];
487 		memcpy(toRGB,o->toRGB,3*nColorComps*sizeof(float));
488 	} else {
489 		toRGB		=	NULL;
490 	}
491 
492 	causticIn				=	(o->causticIn != NULL ? strdup(o->causticIn) : NULL);
493 	causticOut				=	(o->causticOut != NULL ? strdup(o->causticOut) : NULL);
494 	globalIn				=	(o->globalIn != NULL ? strdup(o->globalIn) : NULL);
495 	globalOut				=	(o->globalOut != NULL ? strdup(o->globalOut) : NULL);
496 	filelog					=	(o->filelog != NULL ? strdup(o->filelog) : NULL);
497 }
498 
499 
500 ///////////////////////////////////////////////////////////////////////
501 // Class				:	COptions
502 // Method				:	~COptions
503 // Description			:	Destructor
504 // Return Value			:	-
505 // Comments				:
~COptions()506 COptions::~COptions(){
507 	atomicDecrement(&stats.numOptions);
508 
509 	if (fromRGB != NULL)
510 		delete [] fromRGB;
511 
512 	if (toRGB != NULL)
513 		delete [] toRGB;
514 
515 	if (displays != NULL) {
516 		CDisplay	*cDisplay,*nDisplay;
517 
518 		for (cDisplay=displays;cDisplay!=NULL;) {
519 			nDisplay	=	cDisplay->next;
520 			delete cDisplay;
521 			cDisplay	=	nDisplay;
522 		}
523 	}
524 
525 	if (clipPlanes != NULL) {
526 		CClipPlane	*cPlane,*nPlane;
527 
528 		for (cPlane=clipPlanes;cPlane!=NULL;) {
529 			nPlane	=	cPlane->next;
530 			delete cPlane;
531 			cPlane	=	nPlane;
532 		}
533 	}
534 
535 	if (hider != NULL)
536 		free(hider);
537 
538 
539 	optionsDeleteSearchPath(archivePath);
540 	optionsDeleteSearchPath(proceduralPath);
541 	optionsDeleteSearchPath(texturePath);
542 	optionsDeleteSearchPath(shaderPath);
543 	optionsDeleteSearchPath(displayPath);
544 	optionsDeleteSearchPath(modulePath);
545 
546 	if (causticIn				!= NULL)	free(causticIn);
547 	if (causticOut				!= NULL)	free(causticOut);
548 	if (globalIn				!= NULL)	free(globalIn);
549 	if (globalOut				!= NULL)	free(globalOut);
550 	if (filelog					!= NULL)	free(filelog);
551 }
552 
553 ///////////////////////////////////////////////////////////////////////
554 // Class				:	COptions
555 // Method				:	convertColor
556 // Description			:	Convert color to RGB space from whatever space entered
557 // Return Value			:	-
558 // Comments				:
convertColor(vector & c,const float * f) const559 void	COptions::convertColor(vector &c,const float *f)	const	{
560 	int	i,j;
561 	if (toRGB == NULL) {
562 		c[COMP_R] = f[0];
563 		c[COMP_G] = f[1];
564 		c[COMP_B] = f[2];
565 	} else {
566 		for (i=0;i<3;i++) {
567 			c[i] = 0;
568 			for (j=0;j<(int) nColorComps;j++)
569 				c[i] += f[j]*toRGB[i*nColorComps+j];
570 		}
571 	}
572 }
573 
574 
575 ///////////////////////////////////////////////////////////////////////
576 // Class				:	COptions
577 // Method				:	pickSearchpath
578 // Description			:	Pick a searchpath from name
579 // Return Value			:	-
580 // Comments				:
pickSearchpath(const char * name)581 TSearchpath		*COptions::pickSearchpath(const char *name) {
582 
583 	if (strstr(name,"rib") != NULL) {
584 		return archivePath;
585 
586 	} else if (strstr(name,"tif") != NULL) {
587 		return texturePath;
588 
589 	} else if (strstr(name,"tiff") != NULL) {
590 		return texturePath;
591 
592 	} else if (strstr(name,"tex") != NULL) {
593 		return texturePath;
594 
595 	} else if (strstr(name,"tx") != NULL) {
596 		return texturePath;
597 
598 	} else if (strstr(name,"ptc") != NULL) {
599 		return texturePath;
600 
601 	} else if (strstr(name,"bm") != NULL) {
602 		return texturePath;
603 
604 	} else if (strstr(name,"sdr") != NULL) {
605 		return shaderPath;
606 
607 	} else if (strstr(name,osModuleExtension) != NULL) {
608 		return proceduralPath;
609 	}
610 
611 	return NULL;
612 }
613 
614 
615 ///////////////////////////////////////////////////////////////////////
616 // Function				:	optionsGetSearchPath
617 // Description			:	Get the searchpath
618 // Return Value			:	-
619 // Comments				:
optionsGetSearchPath(const char * path,TSearchpath * oldPath)620 TSearchpath					*optionsGetSearchPath(const char *path,TSearchpath *oldPath) {
621 	TSearchpath		*newPath	=	NULL;
622 	TSearchpath		*lastPath	=	NULL;
623 	TSearchpath		*cPath;
624 	const	char	*currentPath;
625 	char			tmp[OS_MAX_PATH_LENGTH];
626 	char			*dest;
627 
628 	for (dest=tmp,currentPath=path;;) {
629 		if ((*currentPath == '\0') || (*currentPath == ':')) {		// End of the current path
630 
631 #ifdef _WINDOWS
632 			if ((dest - tmp) == 1) {
633 				if ((currentPath[1] == '\\') || (currentPath[1] == '/')) {
634 					*dest++	=	*currentPath++;
635 					continue;
636 				}
637 			}
638 #endif
639 
640 			if ((dest - tmp) > 0) {		// Do we have anything to record ?
641 				dest--;
642 
643 				if ((*dest == '/') || (*dest == '\\')) {	// The last character has to be a slash
644 					dest++;
645 				} else {
646 					dest++;
647 					*dest++	=	'/';
648 				}
649 
650 				*dest++		=	'\0';
651 				osFixSlashes(tmp);
652 
653 				cPath				=	new TSearchpath;
654 				if (strncmp(tmp,"\\\\",2) == 0) {
655 					tmp[1]	=	tmp[2];
656 					tmp[2]	=	':';
657 					tmp[3]	=	'\\';
658 					cPath->directory	=	strdup(tmp+1);
659 				} else {
660 					cPath->directory	=	strdup(tmp);
661 				}
662 				cPath->next			=	NULL;
663 
664 				if (lastPath == NULL) {
665 					lastPath		=	cPath;
666 					newPath			=	cPath;
667 				} else {
668 					lastPath->next	=	cPath;
669 					lastPath		=	cPath;
670 				}
671 			}
672 
673 			dest			=	tmp;
674 
675 			if (*currentPath == '\0')	break;
676 
677 			currentPath++;
678 		} else if (*currentPath == '%') {
679 			const	char	*endOfCurrentPath	=	strchr(currentPath+1,'%');
680 			char			environmentVariable[OS_MAX_PATH_LENGTH];
681 
682 			if (endOfCurrentPath!=NULL) {
683 				const	int		environmentLength	=	(int) (endOfCurrentPath - currentPath) - 1;
684 				const	char	*value;
685 
686 				strncpy(environmentVariable,currentPath+1,environmentLength);
687 				environmentVariable[environmentLength]	=	'\0';
688 
689 				value		=	osEnvironment(environmentVariable);
690 				if (value != NULL) {
691 					strcpy(dest,value);
692 					dest	+=	strlen(value);
693 					currentPath =   endOfCurrentPath+1;
694 				} else {
695 					// If this environment variable was not defined, scrap the entire
696 					// path in progress b/c it will not be correct without this env variable
697 					dest = tmp;
698 					*dest = '\0';   // Truncate dest path
699 					currentPath = strchr(endOfCurrentPath,':');         // Skip to next path
700 					if (!currentPath)
701 						currentPath = strchr(endOfCurrentPath,'\0');    // ...or end if last path
702 				}
703 
704 			} else {
705 				currentPath++;
706 			}
707 		} else if ((*currentPath == '@') || (*currentPath == '&')) {
708 			for (cPath=oldPath;cPath!=NULL;cPath=cPath->next) {
709 				TSearchpath	*nPath	=	new TSearchpath;
710 
711 				nPath->directory	=	strdup(cPath->directory);
712 				nPath->next			=	NULL;
713 
714 				if (lastPath == NULL) {
715 					lastPath		=	nPath;
716 					newPath			=	nPath;
717 				} else {
718 					lastPath->next	=	nPath;
719 					lastPath		=	nPath;
720 				}
721 			}
722 			currentPath++;
723 		} else {
724 			*dest++	=	*currentPath++;
725 		}
726 	}
727 
728 	optionsDeleteSearchPath(oldPath);
729 
730 	return newPath;
731 }
732 
733 ///////////////////////////////////////////////////////////////////////
734 // Class				:	COptions
735 // Method				:	find
736 // Description			:	Find the value of a particular option
737 // Return Value			:	-
738 // Comments				:
find(const char * name,const char * category,EVariableType & type,const void * & value,int & intValue,float & floatValue) const739 int			COptions::find(const char *name,const char *category,EVariableType &type,const void *&value,int &intValue,float &floatValue) const {
740 
741 	// Check the common case first
742 	if ((category == NULL) || (strcmp(category,RI_USER) == 0)) {
743 		CVariable *var;
744         if (userOptions.lookup(name,var) == TRUE) {
745 			type = var->type;
746 			value = var->defaultValue;
747 			if (value != NULL)	return TRUE;
748 		}
749 	}
750 
751 	if ((category == NULL) || (strcmp(category,RI_LIMITS) == 0)) {
752 		if (strcmp(name,RI_BUCKETSIZE) == 0)				{	type	=	TYPE_INTEGER;	value	=	&bucketWidth;			return TRUE;}
753 		else if (strcmp(name,RI_METABUCKETS) == 0)			{	type	=	TYPE_INTEGER;	value	=	&netXBuckets;			return TRUE;}
754 		else if (strcmp(name,RI_GRIDSIZE) == 0)				{	type	=	TYPE_INTEGER;	value	=	&maxGridSize;			return TRUE;}
755 		else if (strcmp(name,RI_EYESPLITS) == 0)			{	type	=	TYPE_INTEGER;	value	=	&maxEyeSplits;			return TRUE;}
756 		else if (strcmp(name,RI_TEXTUREMEMORY) == 0)		{	type	=	TYPE_INTEGER;	value	=	NULL;	intValue = maxTextureSize / 1000;	return TRUE;}
757 		else if (strcmp(name,RI_BRICKMEMORY) == 0)			{	type	=	TYPE_INTEGER;	value	=	NULL;	intValue = maxBrickSize / 1000;		return TRUE;}
758 		else if (strcmp(name,RI_NUMTHREADS) == 0)			{	type	=	TYPE_INTEGER;	value	=	&numThreads;			return TRUE;}
759 		else if (strcmp(name,RI_THREADSTRIDE) == 0)			{	type	=	TYPE_INTEGER;	value	=	&threadStride;			return TRUE;}
760 		else if (strcmp(name,RI_GEOCACHEMEMORY) == 0)		{	type	=	TYPE_INTEGER;	value	=	NULL;	intValue = geoCacheMemory / 1000;	return TRUE;}
761 		else if (strcmp(name,RI_INHERITATTRIBUTES) == 0)	{	type	=	TYPE_INTEGER;	value	=	NULL;	intValue = (flags & OPTIONS_FLAGS_INHERIT_ATTRIBUTES) != 0;				return TRUE;}
762 		else if (strcmp(name,"frame") == 0)					{	type	=	TYPE_INTEGER;	value	=	&frame;					return TRUE;}
763 	}
764 
765 	if ((category == NULL) || (strcmp(category,RI_HIDER) == 0)) {
766 		if (strcmp(name,RI_JITTER) == 0)					{	type	=	TYPE_FLOAT;		value	=	&jitter;				return TRUE;}
767 		else if (strcmp(name,RI_EMIT) == 0)					{	type	=	TYPE_INTEGER;	value	=	&numEmitPhotons;		return TRUE;}
768 		else if (strcmp(name,RI_SAMPLESPECTRUM) == 0)		{	type	=	TYPE_INTEGER;	value	=	NULL;	intValue = (flags & OPTIONS_FLAGS_SAMPLESPECTRUM) != 0;				return TRUE;}
769 		else if (strcmp(name,RI_SAMPLEMOTION) == 0)			{	type	=	TYPE_INTEGER;	value	=	NULL;	intValue = (flags & OPTIONS_FLAGS_SAMPLEMOTION) != 0;				return TRUE;}
770 	}
771 
772 	if ((category == NULL) || (strcmp(category,RI_TRACE) == 0)) {
773 		if (strcmp(name,RI_MAXDEPTH) == 0)					{	type	=	TYPE_INTEGER;	value	=	&maxRayDepth;			return TRUE;}
774 	}
775 
776 	if ((category == NULL) || (strcmp(category,RI_STATISTICS) == 0)) {
777 		if (strcmp(name,RI_ENDOFFRAME) == 0)				{	type	=	TYPE_INTEGER;	value	=	&endofframe;			return TRUE;}
778 		else if (strcmp(name,RI_FILELOG) == 0)				{	type	=	TYPE_STRING;	value	=	filelog;				return TRUE;}
779 		else if (strcmp(name,RI_PROGRESS) == 0)				{	type	=	TYPE_INTEGER;	value	=	NULL;	intValue = (flags & OPTIONS_FLAGS_PROGRESS) != 0;				return TRUE;}
780 
781 	}
782 
783 	if ((category == NULL) || (strcmp(category,RI_SHUTTER) == 0)) {
784 		if (strcmp(name,RI_OFFSET) == 0)					{	type	=	TYPE_FLOAT;		value	=	&shutterOffset;			return TRUE;}
785 	}
786 
787 
788 	return FALSE;
789 }
790