1 /*  NAME:
2         E3GeometryNURBPatch.c
3 
4     DESCRIPTION:
5         Implementation of Quesa NURB Patch geometry class.
6 
7     COPYRIGHT:
8         Copyright (c) 1999-2005, Quesa Developers. All rights reserved.
9 
10         For the current release of Quesa, please see:
11 
12             <http://www.quesa.org/>
13 
14         Redistribution and use in source and binary forms, with or without
15         modification, are permitted provided that the following conditions
16         are met:
17 
18             o Redistributions of source code must retain the above copyright
19               notice, this list of conditions and the following disclaimer.
20 
21             o Redistributions in binary form must reproduce the above
22               copyright notice, this list of conditions and the following
23               disclaimer in the documentation and/or other materials provided
24               with the distribution.
25 
26             o Neither the name of Quesa nor the names of its contributors
27               may be used to endorse or promote products derived from this
28               software without specific prior written permission.
29 
30         THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31         "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32         LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33         A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34         OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35         SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
36         TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
37         PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
38         LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
39         NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40         SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41     ___________________________________________________________________________
42 */
43 //=============================================================================
44 //      Include files
45 //-----------------------------------------------------------------------------
46 #include "E3Prefix.h"
47 #include "E3View.h"
48 #include "E3Geometry.h"
49 #include "E3GeometryTriMesh.h"
50 #include "E3GeometryNURBPatch.h"
51 
52 
53 
54 
55 
56 //=============================================================================
57 //      Internal constants
58 //-----------------------------------------------------------------------------
59 #define		kFiniteSubdivision		32
60 
61 
62 
63 
64 
65 //=============================================================================
66 //      Internal types
67 //-----------------------------------------------------------------------------
68 
69 class E3NURBPatch : public E3Geometry // This is a leaf class so no other classes use this,
70 								// so it can be here in the .c file rather than in
71 								// the .h file, hence all the fields can be public
72 								// as nobody should be including this file
73 	{
74 Q3_CLASS_ENUMS ( kQ3GeometryTypeNURBPatch, E3NURBPatch, E3Geometry )
75 public :
76 
77 	TQ3NURBPatchData			instanceData ;
78 
79 	} ;
80 
81 
82 
83 //=============================================================================
84 //      Internal functions
85 //-----------------------------------------------------------------------------
86 //      e3geom_patch_copydata : Copy TQ3NURBPatchData from one to another.
87 //-----------------------------------------------------------------------------
88 //		Note :	If isDuplicate is true, we duplicate shared objects rather than
89 //				obtaining new references to them.
90 //-----------------------------------------------------------------------------
91 static TQ3Status
92 e3geom_patch_copydata(const TQ3NURBPatchData *src, TQ3NURBPatchData *dst, TQ3Boolean isDuplicate)
93 {
94 	TQ3Status		qd3dStatus = kQ3Success;
95 	TQ3Uns32		theSize, i, j, numKnots;
96 
97 	// copy uOrders, vOrder, numColumns, numRows, numTrimLoops
98 	dst->uOrder = src->uOrder;
99 	dst->vOrder = src->vOrder;
100 	dst->numColumns = src->numColumns;
101 	dst->numRows = src->numRows;
102 	dst->numTrimLoops = src->numTrimLoops;
103 
104 	// copy controlPoints, uKnots, vKnots
105 	theSize = sizeof(TQ3RationalPoint4D) * src->numColumns * src->numRows;
106 	dst->controlPoints = (TQ3RationalPoint4D *) Q3Memory_Allocate( theSize );
107 	Q3Memory_Copy( src->controlPoints, dst->controlPoints, theSize );
108 
109 	theSize = sizeof(float) * (src->uOrder+src->numColumns);
110 	dst->uKnots = (float *) Q3Memory_Allocate( theSize );
111 	Q3Memory_Copy( src->uKnots, dst->uKnots, theSize );
112 
113 	theSize = sizeof(float) * (src->vOrder+src->numRows);
114 	dst->vKnots = (float *) Q3Memory_Allocate( theSize );
115 	Q3Memory_Copy( src->vKnots, dst->vKnots, theSize );
116 
117 	// Copy all trim loops.
118 	// This is complicated because we have several layers of nested arrays.
119 	dst->numTrimLoops = src->numTrimLoops;
120 	if (src->numTrimLoops)
121 	{
122 		// Copy TrimLoops, basic data.
123 		theSize = sizeof(TQ3NURBPatchTrimLoopData) * src->numTrimLoops;
124 		dst->trimLoops = (TQ3NURBPatchTrimLoopData *) Q3Memory_Allocate( theSize );
125 		Q3Memory_Copy( src->trimLoops, dst->trimLoops, theSize );
126 
127 		// Now iterate over trim loop curves, copy them.
128 		for (i=0; i < src->numTrimLoops; i++) {
129 
130 			// For a particular trimLoop i, copy its array of curve data.
131 			theSize = sizeof(TQ3NURBPatchTrimCurveData) * src->trimLoops[i].numTrimCurves;
132 			if (theSize) {
133 				dst->trimLoops[i].trimCurves = (TQ3NURBPatchTrimCurveData *) Q3Memory_Allocate( theSize );
134 				Q3Memory_Copy( src->trimLoops[i].trimCurves, dst->trimLoops[i].trimCurves, theSize );
135 
136 				// Now, for a particular curve, copy its control points and knots.
137 				for (j=0; j < src->trimLoops[i].numTrimCurves; j++) {
138 					theSize = sizeof(TQ3RationalPoint3D) * src->trimLoops[i].trimCurves[j].numPoints;
139 					if (theSize) {
140 						dst->trimLoops[i].trimCurves[j].controlPoints = (TQ3RationalPoint3D *) Q3Memory_Allocate(theSize);
141 						Q3Memory_Copy(	src->trimLoops[i].trimCurves[j].controlPoints,
142 										dst->trimLoops[i].trimCurves[j].controlPoints,
143 										theSize );
144 					}
145 					numKnots = src->trimLoops[i].trimCurves[j].numPoints
146 								  + src->trimLoops[i].trimCurves[j].order;
147 					theSize = sizeof(float) * numKnots;
148 					if (theSize) {
149 						dst->trimLoops[i].trimCurves[j].knots = (float *) Q3Memory_Allocate(theSize);
150 						Q3Memory_Copy(	src->trimLoops[i].trimCurves[j].knots,
151 										dst->trimLoops[i].trimCurves[j].knots,
152 										theSize );
153 					}
154 				}
155 			}
156 		}
157 	} else dst->trimLoops = NULL;
158 
159 
160 	// copy or shared-replace the attributes
161 	if (isDuplicate)
162 	{
163 		if (src->patchAttributeSet != NULL)
164 		{
165 			dst->patchAttributeSet = Q3Object_Duplicate(src->patchAttributeSet);
166 			if (dst->patchAttributeSet == NULL) qd3dStatus = kQ3Failure;
167 		} else dst->patchAttributeSet = NULL;
168 
169 	}
170 	else {
171 		E3Shared_Replace(&dst->patchAttributeSet, src->patchAttributeSet);
172 	}
173 
174 	return qd3dStatus;
175 }
176 
177 
178 
179 
180 
181 //=============================================================================
182 //      e3geom_patch_disposedata : Dispose of a TQ3NURBPatch's data.
183 //-----------------------------------------------------------------------------
184 static void
185 e3geom_patch_disposedata(TQ3NURBPatchData *theNURBPatch)
186 {
187 	TQ3Uns32 i, j;
188 
189 	Q3Memory_Free( &theNURBPatch->controlPoints );
190 	Q3Memory_Free( &theNURBPatch->uKnots );
191 	Q3Memory_Free( &theNURBPatch->vKnots );
192 	Q3Object_CleanDispose(&theNURBPatch->patchAttributeSet );
193 
194 	for (i=0; i < theNURBPatch->numTrimLoops; i++) {
195 		for (j=0; j < theNURBPatch->trimLoops[i].numTrimCurves; j++) {
196 			Q3Memory_Free( &theNURBPatch->trimLoops[i].trimCurves[j].controlPoints );
197 			Q3Memory_Free( &theNURBPatch->trimLoops[i].trimCurves[j].knots );
198 		}
199 		Q3Memory_Free( &theNURBPatch->trimLoops[i].trimCurves );
200 	}
201 	Q3Memory_Free( &theNURBPatch->trimLoops );
202 }
203 
204 
205 
206 
207 
208 //=============================================================================
209 //      e3geom_nurbpatch_new : NURBPatch new method.
210 //-----------------------------------------------------------------------------
211 static TQ3Status
212 e3geom_nurbpatch_new(TQ3Object theObject, void *privateData, const void *paramData)
213 {	TQ3NURBPatchData		*instanceData  = (TQ3NURBPatchData *)       privateData;
214 	const TQ3NURBPatchData	*nurbpatchData = (const TQ3NURBPatchData *) paramData;
215 	TQ3Status			qd3dStatus;
216 #pragma unused(theObject)
217 
218 
219 	// Initialise our instance data
220 	qd3dStatus = e3geom_patch_copydata(nurbpatchData, instanceData, kQ3False);
221 
222 	return(qd3dStatus);
223 }
224 
225 
226 
227 
228 
229 //=============================================================================
230 //      e3geom_nurbpatch_delete : NURBPatch delete method.
231 //-----------------------------------------------------------------------------
232 static void
233 e3geom_nurbpatch_delete(TQ3Object theObject, void *privateData)
234 {	TQ3NURBPatchData		*instanceData = (TQ3NURBPatchData *) privateData;
235 #pragma unused(theObject)
236 
237 
238 	// Dispose of our instance data
239 	e3geom_patch_disposedata(instanceData);
240 }
241 
242 
243 
244 
245 
246 //=============================================================================
247 //      e3geom_nurbpatch_duplicate : NURBPatch duplicate method.
248 //-----------------------------------------------------------------------------
249 static TQ3Status
250 e3geom_nurbpatch_duplicate(TQ3Object fromObject, const void *fromPrivateData,
251 						  TQ3Object toObject,   void       *toPrivateData)
252 {	const TQ3NURBPatchData	*fromInstanceData = (const TQ3NURBPatchData *) fromPrivateData;
253 	TQ3NURBPatchData			*toInstanceData   = (TQ3NURBPatchData *)       toPrivateData;
254 	TQ3Status				qd3dStatus;
255 #pragma unused(fromObject)
256 #pragma unused(toObject)
257 
258 
259 
260 	// Validate our parameters
261 	Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(fromObject),      kQ3Failure);
262 	Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(fromPrivateData), kQ3Failure);
263 	Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(toObject),        kQ3Failure);
264 	Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(toPrivateData),   kQ3Failure);
265 
266 
267 
268 	// Initialise the instance data of the new object
269 	qd3dStatus = e3geom_patch_copydata( fromInstanceData, toInstanceData, kQ3True );
270 
271 
272 
273 	// Handle failure
274 	if (qd3dStatus != kQ3Success)
275 		e3geom_patch_disposedata(toInstanceData);
276 
277 	return(qd3dStatus);
278 }
279 
280 
281 
282 
283 
284 //=============================================================================
285 //      e3geom_nurbpatch_evaluate_basis : Evaluate the basis.
286 //-----------------------------------------------------------------------------
287 //		Note :	This is the recursive definition, as in the Schneider article.
288 //				fracR = 1 - fracL.  That's why non-recursive versions are so
289 //				fast, they take one-minus over and over again.
290 //-----------------------------------------------------------------------------
291 static float
292 e3geom_nurbpatch_evaluate_basis( float u, TQ3Uns32 i, TQ3Uns32 k, float *knots )
293 {	float bottomL, bottomR, fracL, fracR, theResult;
294 
295 
296 	if ( k == 1 ) {
297 		if ( u >= knots[i] && u <= knots[i+1] )
298 			theResult = 1.0f ;
299 		else
300 			theResult = 0.0f ;
301 	}
302 
303 	else {
304 		bottomL = ( knots[i+k-1] - knots[i] ) ;
305 		fracL =
306 			( bottomL <= kQ3RealZero ) ? 0.0f : // floating point inaccuracies
307 			( u - knots[i] ) / bottomL ;
308 		bottomR = ( knots[i+k] - knots[i+1] ) ;
309 		fracR =
310 			( bottomR <= kQ3RealZero ) ? 0.0f : // fp inaccuracies
311 			( knots[i+k] - u ) / bottomR ;
312 
313 		// stops recursing if we already know the result will be 0
314 		if ( fracL > kQ3RealZero ) // fp inaccuracies
315 			fracL *= e3geom_nurbpatch_evaluate_basis(u,i,k-1,knots) ;
316 		else fracL = 0.0f ;
317 
318 		if ( fracR > kQ3RealZero ) // fp inaccuracies
319 			fracR *= e3geom_nurbpatch_evaluate_basis(u,i+1,k-1,knots) ;
320 		else fracR = 0.0f ;
321 
322 		theResult = fracL + fracR ;
323 	}
324 
325 	return theResult;
326 }
327 
328 
329 
330 
331 
332 //=============================================================================
333 //      e3geom_nurbpatch_evaluate_basis_deriv : Evaluate the basis derivative.
334 //-----------------------------------------------------------------------------
335 //		Note : Also lifted from the B,B,&B book.
336 //-----------------------------------------------------------------------------
337 static float
338 e3geom_nurbpatch_evaluate_basis_deriv( float u, TQ3Uns32 i, TQ3Uns32 k, float *knots )
339 {
340 
341 	//float theResult ;
342 	float bottomL, bottomR, fracL, fracR ;
343 
344 	bottomL = ( knots[i+k-1] - knots[i] ) ;
345 	fracL =
346 		( bottomL <= kQ3RealZero ) ? 0.0f : // floating point inaccuracies
347 		1.0f / bottomL ;
348 	bottomR = ( knots[i+k] - knots[i+1] ) ;
349 	fracR =
350 		( bottomR <= kQ3RealZero ) ? 0.0f : // fp inaccuracies
351 		1.0f / bottomR ;
352 
353 	// stops recursing if we already know the result will be 0
354 	if ( fracL > kQ3RealZero )	// fp inaccuracies
355 		fracL *= e3geom_nurbpatch_evaluate_basis(u,i,k-1,knots) ;
356 	else fracL = 0.0f ;
357 
358 	if ( fracR > kQ3RealZero ) // fp inaccuracies
359 		fracR *= e3geom_nurbpatch_evaluate_basis(u,i+1,k-1,knots) ;
360 	else fracR = 0.0f ;
361 
362 	return (k-1)*(fracL - fracR) ;
363 }
364 
365 
366 
367 
368 
369 //=============================================================================
370 //      e3geom_nurbpatch_evaluate_uv : Evaluate the NURB patch data, computing
371 //									   the normal.
372 //-----------------------------------------------------------------------------
373 //		Note :	Returns the coordinates into outPoint and the normal into
374 //				outNormal.
375 //-----------------------------------------------------------------------------
376 static void
377 e3geom_nurbpatch_evaluate_uv( float u, float v, const TQ3NURBPatchData * patchData, TQ3Vector3D * outNormal, TQ3Point3D * outPoint,
378 	float* uBasisValues, float* vBasisValues, float* uBasisDerivValues, float* vBasisDerivValues )
379 {
380 
381 	TQ3Uns32		iU, jV ;
382 	float			xTop, yTop, zTop, xTopDu, yTopDu, zTopDu, xTopDv, yTopDv, zTopDv ;
383 	float			OneOverBottom, bottom, bottom_squared, bottomDu, bottomDv ;
384 	TQ3Vector3D		dU, dV ;
385 
386 	// Let's...
387 	xTop = yTop = zTop = bottom = xTopDu = xTopDv = yTopDu = yTopDv = zTopDu = zTopDv = bottomDu = bottomDv = 0.0f ;
388 	// Go
389 	for ( iU = 0; iU < patchData->numColumns; iU++ ) {
390 		uBasisValues[iU] = e3geom_nurbpatch_evaluate_basis( u, iU, patchData->uOrder, patchData->uKnots) ;
391 		uBasisDerivValues[iU] = e3geom_nurbpatch_evaluate_basis_deriv( u, iU, patchData->uOrder, patchData->uKnots) ;
392 	}
393 
394 	// Again
395 	for ( jV = 0; jV < patchData->numRows; jV++ ) {
396 		vBasisValues[jV] = e3geom_nurbpatch_evaluate_basis( v, jV, patchData->vOrder, patchData->vKnots) ;
397 		vBasisDerivValues[jV] = e3geom_nurbpatch_evaluate_basis_deriv( v, jV, patchData->vOrder, patchData->vKnots) ;
398 	}
399 
400 	// Now some summation rotation recreation, like p. 46-47 in Bartels, Beatty, & Barsky
401 	for ( jV = 0; jV < patchData->numRows; jV++ ) {
402 		for ( iU = 0; iU < patchData->numColumns; iU++ ) {
403 			// First the point
404 			xTop += patchData->controlPoints[patchData->numColumns*jV + iU].x * uBasisValues[iU] * vBasisValues[jV] ;
405 			yTop += patchData->controlPoints[patchData->numColumns*jV + iU].y * uBasisValues[iU] * vBasisValues[jV] ;
406 			zTop += patchData->controlPoints[patchData->numColumns*jV + iU].z * uBasisValues[iU] * vBasisValues[jV] ;
407 			bottom += patchData->controlPoints[patchData->numColumns*jV + iU].w * uBasisValues[iU] * vBasisValues[jV] ;
408 			/* Now let's take care of the derivatives */
409 			/*
410 			 * To do the derivative, we must use the chain rule:
411 			 * ((low * Dhigh) - (high * Dlow)) / low^2.
412 			 *
413 			 * So for this upcoming loop, we are building the Du of the individual Top's and the bottom, as well as the Dv of the Top's
414 			 * and bottom.
415 			 * Then later I will put it together with that chain rule to get my answer.
416 			 */
417 			// So first Du
418 			xTopDu += patchData->controlPoints[patchData->numColumns*jV + iU].x * uBasisDerivValues[iU] * vBasisValues[jV] ;
419 			yTopDu += patchData->controlPoints[patchData->numColumns*jV + iU].y * uBasisDerivValues[iU] * vBasisValues[jV] ;
420 			zTopDu += patchData->controlPoints[patchData->numColumns*jV + iU].z * uBasisDerivValues[iU] * vBasisValues[jV] ;
421 			bottomDu += patchData->controlPoints[patchData->numColumns*jV + iU].w * uBasisDerivValues[iU] * vBasisValues[jV] ;
422 			// And next Dv
423 			xTopDv += patchData->controlPoints[patchData->numColumns*jV + iU].x * uBasisValues[iU] * vBasisDerivValues[jV] ;
424 			yTopDv += patchData->controlPoints[patchData->numColumns*jV + iU].y * uBasisValues[iU] * vBasisDerivValues[jV] ;
425 			zTopDv += patchData->controlPoints[patchData->numColumns*jV + iU].z * uBasisValues[iU] * vBasisDerivValues[jV] ;
426 			bottomDv += patchData->controlPoints[patchData->numColumns*jV + iU].w * uBasisValues[iU] * vBasisDerivValues[jV] ;
427 	}	}
428 
429 
430 	// Calculate bottom squared
431 	Q3_ASSERT(bottom != 0.0f);
432 	bottom_squared = bottom * bottom ;
433 
434 
435 	// The point
436 	OneOverBottom = 1.0f / bottom ;
437 	outPoint->x = xTop * OneOverBottom ;
438 	outPoint->y = yTop * OneOverBottom ;
439 	outPoint->z = zTop * OneOverBottom ;
440 
441 	/* The Du vector */
442 	// low^2 = bottom^2
443 	OneOverBottom = 1.0f / bottom_squared ;
444 	// ((low * Dhigh) - (high * Dlow)) / bottom^2
445 	dU.x = ((bottom * xTopDu) - (xTop * bottomDu))*OneOverBottom ;
446 	dU.y = ((bottom * yTopDu) - (yTop * bottomDu))*OneOverBottom ;
447 	dU.z = ((bottom * zTopDu) - (zTop * bottomDu))*OneOverBottom ;
448 
449 	/* The Dv vector */
450 	// low^2 = bottom^2
451 	// OneOverBottom = same as above
452 	// ((low * Dhigh) - (high * Dlow)) / bottom^2
453 	dV.x = ((bottom * xTopDv) - (xTop * bottomDv))*OneOverBottom ;
454 	dV.y = ((bottom * yTopDv) - (yTop * bottomDv))*OneOverBottom ;
455 	dV.z = ((bottom * zTopDv) - (zTop * bottomDv))*OneOverBottom ;
456 
457 	Q3Vector3D_Cross(&dU, &dV, outNormal);
458 
459 	// Normalize the normal vector
460 	if (Q3FastVector3D_LengthSquared(outNormal) < kQ3RealZero)
461 	{
462 		outNormal->x = 1.0f;	// arbitrary unit vector
463 		outNormal->y = 0.0f;
464 		outNormal->z = 0.0f;
465 	}
466 	else
467 	{
468 		Q3FastVector3D_Normalize( outNormal, outNormal );
469 	}
470 }
471 
472 
473 
474 
475 
476 //=============================================================================
477 //      e3geom_nurbpatch_evaluate_uv_no_deriv : Evaluate the NURB patch data
478 //												without computing the normal.
479 //-----------------------------------------------------------------------------
480 //		Note :	Returns the coordinates into outPoint
481 //-----------------------------------------------------------------------------
482 static void
483 e3geom_nurbpatch_evaluate_uv_no_deriv( float u, float v, const TQ3NURBPatchData * patchData, TQ3Point3D * outPoint,
484 	float* uBasisValues, float* vBasisValues )
485 {
486 
487 	TQ3Uns32		iU, jV ;
488 	float			xTop, yTop, zTop ;
489 	float			OneOverBottom, bottom ;
490 
491 	// Let's...
492 	xTop = yTop = zTop = bottom = 0.0f ;
493 	// Go
494 	for ( iU = 0; iU < patchData->numColumns; iU++ ) {
495 		uBasisValues[iU] = e3geom_nurbpatch_evaluate_basis( u, iU, patchData->uOrder, patchData->uKnots) ;
496 	}
497 
498 	// Again
499 	for ( jV = 0; jV < patchData->numRows; jV++ ) {
500 		vBasisValues[jV] = e3geom_nurbpatch_evaluate_basis( v, jV, patchData->vOrder, patchData->vKnots) ;
501 	}
502 
503 	// Now some summation rotation recreation, like p. 46-47 in Bartels, Beatty, & Barsky
504 	for ( jV = 0; jV < patchData->numRows; jV++ ) {
505 		for ( iU = 0; iU < patchData->numColumns; iU++ ) {
506 			// First the point
507 			xTop += patchData->controlPoints[patchData->numColumns*jV + iU].x * uBasisValues[iU] * vBasisValues[jV] ;
508 			yTop += patchData->controlPoints[patchData->numColumns*jV + iU].y * uBasisValues[iU] * vBasisValues[jV] ;
509 			zTop += patchData->controlPoints[patchData->numColumns*jV + iU].z * uBasisValues[iU] * vBasisValues[jV] ;
510 			bottom += patchData->controlPoints[patchData->numColumns*jV + iU].w * uBasisValues[iU] * vBasisValues[jV] ;
511 	}	}
512 
513 
514 	// The point
515 	OneOverBottom = 1.0f / bottom ;
516 	outPoint->x = xTop * OneOverBottom ;
517 	outPoint->y = yTop * OneOverBottom ;
518 	outPoint->z = zTop * OneOverBottom ;
519 }
520 
521 
522 
523 
524 
525 //=============================================================================
526 //      e3geom_nurbpatch_interesting_knots : Find the interesting knots.
527 //-----------------------------------------------------------------------------
528 //		Note : Interesting == non-repetitive.
529 //-----------------------------------------------------------------------------
530 static TQ3Uns32
531 e3geom_nurbpatch_interesting_knots( float * inKnots, TQ3Uns32 numPoints, TQ3Uns32 order, float * interestingK )
532 {
533 	TQ3Uns32 count, n ;
534 	interestingK[0] = inKnots[order - 1] ;
535 
536 	count = 1 ;
537 	for( n = order ; n <= numPoints ; n++ ) {
538 
539 		// if current knot differs from the previous, add this knot
540 		if( inKnots[n] != inKnots[n-1] ) {
541 			interestingK[ count ] = inKnots[n] ;
542 			count++ ;
543 		}
544 
545 	} // ~for( n in knot vector )
546 
547 #if Q3_DEBUG
548 	Q3_ASSERT( count <= numPoints - order + 2 ) ;
549 #endif
550 	return (TQ3Uns32) count ;
551 }
552 
553 
554 
555 
556 
557 //=============================================================================
558 //      e3geom_nurbpatch_recursive_quad_world_subdivide : Recursively subdivide
559 //														  a patch into 4
560 //														  parametrically square
561 //														  sections.
562 //-----------------------------------------------------------------------------
563 //		Note : Returns the maximum depth of recursion.
564 //-----------------------------------------------------------------------------
565 static TQ3Uns32
566 e3geom_nurbpatch_recursive_quad_world_subdivide( TQ3Uns32 depth, float subdiv, float fu, float lu, float fv, float lv,
567 												  const TQ3Point3D* Pfufv, const TQ3Point3D* Plufv, const TQ3Point3D* Pfulv, const TQ3Point3D* Plulv,
568 								  				  const TQ3NURBPatchData *geomData, const TQ3Matrix4x4* localToWorld,
569 								  				  float* uBasisValues, float* vBasisValues )
570 {
571 	float hu, hv ;
572 	TQ3Point3D Phufv, Pfuhv, Phuhv, Pluhv, Phulv ;
573 
574 	TQ3Uns32 recurseDepth, maxRecurseDepth ;
575 
576 	depth++ ;
577 	maxRecurseDepth = 0 ;
578 
579 	#define a Q3Point3D_DistanceSquared( Pfufv, Plufv )
580 	#define b Q3Point3D_DistanceSquared( Plufv, Plulv )
581 	#define c Q3Point3D_DistanceSquared( Pfulv, Plulv )
582 	#define d Q3Point3D_DistanceSquared( Pfufv, Pfulv )
583 
584 	if( a > subdiv || b > subdiv || c > subdiv || d > subdiv ) {
585 
586 		hu = (fu + lu)*0.5f ;
587 		hv = (fv + lv)*0.5f ;
588 
589 		e3geom_nurbpatch_evaluate_uv_no_deriv( hu,
590 											   fv,
591 											   geomData,
592 											   &Phufv,
593 											   uBasisValues, vBasisValues );
594 		Q3Point3D_Transform( &Phufv, localToWorld, &Phufv ) ;
595 
596 		e3geom_nurbpatch_evaluate_uv_no_deriv( fu,
597 											   hv,
598 											   geomData,
599 											   &Pfuhv,
600 											   uBasisValues, vBasisValues );
601 		Q3Point3D_Transform( &Pfuhv, localToWorld, &Pfuhv ) ;
602 
603 		e3geom_nurbpatch_evaluate_uv_no_deriv( hu,
604 											   hv,
605 											   geomData,
606 											   &Phuhv,
607 											   uBasisValues, vBasisValues );
608 		Q3Point3D_Transform( &Phuhv, localToWorld, &Phuhv ) ;
609 
610 		e3geom_nurbpatch_evaluate_uv_no_deriv( lu,
611 											   hv,
612 											   geomData,
613 											   &Pluhv,
614 											   uBasisValues, vBasisValues );
615 		Q3Point3D_Transform( &Pluhv, localToWorld, &Pluhv ) ;
616 
617 		e3geom_nurbpatch_evaluate_uv_no_deriv( hu,
618 											   lv,
619 											   geomData,
620 											   &Phulv,
621 											   uBasisValues, vBasisValues );
622 		Q3Point3D_Transform( &Phulv, localToWorld, &Phulv ) ;
623 
624 		// Top-left square
625 		recurseDepth = e3geom_nurbpatch_recursive_quad_world_subdivide( depth,
626 														subdiv, fu, hu, fv, hv,
627 														Pfufv, &Phufv, &Pfuhv, &Phuhv,
628 														geomData, localToWorld,
629 														uBasisValues, vBasisValues ) ;
630 		maxRecurseDepth = maxRecurseDepth > recurseDepth ? maxRecurseDepth : recurseDepth ;
631 
632 		// Top-right square
633 		recurseDepth = e3geom_nurbpatch_recursive_quad_world_subdivide( depth,
634 														 subdiv, hu, lu, fv, hv,
635 														 &Phufv, Plufv, &Phuhv, &Pluhv,
636 														 geomData, localToWorld,
637 														 uBasisValues, vBasisValues ) ;
638 		maxRecurseDepth = maxRecurseDepth > recurseDepth ? maxRecurseDepth : recurseDepth ;
639 
640 		// Bottom-left square
641 		recurseDepth = e3geom_nurbpatch_recursive_quad_world_subdivide( depth,
642 														 subdiv, fu, hu, hv, lv,
643 														 &Pfuhv, &Phuhv, Pfulv, &Phulv,
644 														 geomData, localToWorld,
645 														 uBasisValues, vBasisValues ) ;
646 		maxRecurseDepth = maxRecurseDepth > recurseDepth ? maxRecurseDepth : recurseDepth ;
647 
648 		// Bottom-right square
649 		recurseDepth = e3geom_nurbpatch_recursive_quad_world_subdivide( depth,
650 														 subdiv, hu, lu, hv, lv,
651 														 &Phuhv, &Pluhv, &Phulv, Plulv,
652 														 geomData, localToWorld,
653 														 uBasisValues, vBasisValues ) ;
654 		maxRecurseDepth = maxRecurseDepth > recurseDepth ? maxRecurseDepth : recurseDepth ;
655 	}
656 
657 	depth = depth > maxRecurseDepth ? depth : maxRecurseDepth ;
658 
659 	return depth ;
660 
661 	#undef a
662 	#undef b
663 	#undef c
664 	#undef d
665 }
666 
667 
668 
669 
670 
671 //=============================================================================
672 //      e3geom_nurbpatch_recursive_quad_screen_subdivide : Recursively subdivide
673 //														   a patch into 4
674 //														   parametrically square
675 //														   sections.
676 //-----------------------------------------------------------------------------
677 //		Note : If *points == NULL, an error has occurred (memory).
678 //-----------------------------------------------------------------------------
679 static TQ3Uns32
680 e3geom_nurbpatch_recursive_quad_screen_subdivide( TQ3Uns32 depth, float subdiv, float fu, float lu, float fv, float lv,
681 												  const TQ3Point2D* Pfufv2, const TQ3Point2D* Plufv2, const TQ3Point2D* Pfulv2, const TQ3Point2D* Plulv2,
682 								  				  const TQ3NURBPatchData *geomData, const TQ3Matrix4x4* localToWindow,
683 								  				  float* uBasisValues, float* vBasisValues )
684 {
685 	float hu, hv ;
686 	TQ3Point3D Phufv, Pfuhv, Phuhv, Pluhv, Phulv, transformPoint ;
687 	TQ3Point2D Phufv2, Pfuhv2, Phuhv2, Pluhv2, Phulv2 ;
688 
689 	TQ3Uns32 recurseDepth, maxRecurseDepth ;
690 
691 	depth++ ;
692 	maxRecurseDepth = 0 ;
693 
694 	#define a Q3Point2D_DistanceSquared( Pfufv2, Plufv2 )
695 	#define b Q3Point2D_DistanceSquared( Plufv2, Plulv2 )
696 	#define c Q3Point2D_DistanceSquared( Pfulv2, Plulv2 )
697 	#define d Q3Point2D_DistanceSquared( Pfufv2, Pfulv2 )
698 
699 	if( a > subdiv || b > subdiv || c > subdiv || d > subdiv ) {
700 
701 		hu = (fu + lu)*0.5f ;
702 		hv = (fv + lv)*0.5f ;
703 
704 		e3geom_nurbpatch_evaluate_uv_no_deriv( hu,
705 											   fv,
706 											   geomData,
707 											   &Phufv,
708 											   uBasisValues, vBasisValues );
709 		Q3Point3D_Transform( &Phufv, localToWindow, &transformPoint ) ;
710 		Phufv2.x = transformPoint.x ;
711 		Phufv2.y = transformPoint.y ;
712 
713 		e3geom_nurbpatch_evaluate_uv_no_deriv( fu,
714 											   hv,
715 											   geomData,
716 											   &Pfuhv,
717 											   uBasisValues, vBasisValues );
718 		Q3Point3D_Transform( &Pfuhv, localToWindow, &transformPoint ) ;
719 		Pfuhv2.x = transformPoint.x ;
720 		Pfuhv2.y = transformPoint.y ;
721 
722 		e3geom_nurbpatch_evaluate_uv_no_deriv( hu,
723 											   hv,
724 											   geomData,
725 											   &Phuhv,
726 											   uBasisValues, vBasisValues );
727 		Q3Point3D_Transform( &Phuhv, localToWindow, &transformPoint ) ;
728 		Phuhv2.x = transformPoint.x ;
729 		Phuhv2.y = transformPoint.y ;
730 
731 		e3geom_nurbpatch_evaluate_uv_no_deriv( lu,
732 											   hv,
733 											   geomData,
734 											   &Pluhv,
735 											   uBasisValues, vBasisValues );
736 		Q3Point3D_Transform( &Pluhv, localToWindow, &transformPoint ) ;
737 		Pluhv2.x = transformPoint.x ;
738 		Pluhv2.y = transformPoint.y ;
739 
740 		e3geom_nurbpatch_evaluate_uv_no_deriv( hu,
741 											   lv,
742 											   geomData,
743 											   &Phulv,
744 											   uBasisValues, vBasisValues );
745 		Q3Point3D_Transform( &Phulv, localToWindow, &transformPoint ) ;
746 		Phulv2.x = transformPoint.x ;
747 		Phulv2.y = transformPoint.y ;
748 
749 		// Top-left square
750 		recurseDepth = e3geom_nurbpatch_recursive_quad_screen_subdivide( depth,
751 														subdiv, fu, hu, fv, hv,
752 														Pfufv2, &Phufv2, &Pfuhv2, &Phuhv2,
753 														geomData, localToWindow,
754 														uBasisValues, vBasisValues ) ;
755 		maxRecurseDepth = maxRecurseDepth > recurseDepth ? maxRecurseDepth : recurseDepth ;
756 
757 		// Top-right square
758 		recurseDepth = e3geom_nurbpatch_recursive_quad_screen_subdivide( depth,
759 														 subdiv, hu, lu, fv, hv,
760 														 &Phufv2, Plufv2, &Phuhv2, &Pluhv2,
761 														 geomData, localToWindow,
762 														 uBasisValues, vBasisValues ) ;
763 		maxRecurseDepth = maxRecurseDepth > recurseDepth ? maxRecurseDepth : recurseDepth ;
764 
765 		// Bottom-left square
766 		recurseDepth = e3geom_nurbpatch_recursive_quad_screen_subdivide( depth,
767 														 subdiv, fu, hu, hv, lv,
768 														 &Pfuhv2, &Phuhv2, Pfulv2, &Phulv2,
769 														 geomData, localToWindow,
770 														 uBasisValues, vBasisValues ) ;
771 		maxRecurseDepth = maxRecurseDepth > recurseDepth ? maxRecurseDepth : recurseDepth ;
772 
773 		// Bottom-right square
774 		recurseDepth = e3geom_nurbpatch_recursive_quad_screen_subdivide( depth,
775 														 subdiv, hu, lu, hv, lv,
776 														 &Phuhv2, &Pluhv2, &Phulv2, Plulv2,
777 														 geomData, localToWindow,
778 														 uBasisValues, vBasisValues ) ;
779 		maxRecurseDepth = maxRecurseDepth > recurseDepth ? maxRecurseDepth : recurseDepth ;
780 	}
781 
782 	depth = depth > maxRecurseDepth ? depth : maxRecurseDepth ;
783 
784 	return depth ;
785 
786 	#undef a
787 	#undef b
788 	#undef c
789 	#undef d
790 }
791 
792 
793 
794 
795 
796 //=============================================================================
797 //      e3geom_nurbcurve_worldscreen_subdiv : Subdivide the given NURB curve
798 //											  into triangles whose edges have
799 //											  at most the given world or
800 //											  screen-space length. Vertices are
801 //											  guaranteed at knots.
802 //-----------------------------------------------------------------------------
803 //		Note :	Calls through to e3geom_nurbcurve_constant_subdiv.
804 //				If the points array is non-NULL on return, be sure to free it
805 //				with Q3Memory_Free(). If it is NULL, then an error has occurred.
806 //-----------------------------------------------------------------------------
807 // I need the function declaration for constant subdivision since I call through to it
808 static void
809 e3geom_nurbpatch_constant_subdiv( TQ3Point3D** thePoints, TQ3Uns32* numPoints,
810 								  TQ3Param2D** theUVs, TQ3Vector3D** theNormals,
811 								  TQ3TriMeshTriangleData** theTriangles, TQ3Uns32* numTriangles,
812 								  float subdivU, float subdivV,
813 								  const TQ3NURBPatchData *geomData,
814 								  float* uBasisValues, float* vBasisValues, float* uBasisDerivValues, float* vBasisDerivValues ) ;
815 
816 static void
817 e3geom_nurbpatch_worldscreen_subdiv( TQ3Point3D** thePoints, TQ3Uns32* numPoints,
818 									 TQ3Param2D** theUVs, TQ3Vector3D** theNormals,
819 									 TQ3TriMeshTriangleData** theTriangles, TQ3Uns32* numTriangles,
820 									 float subdiv,
821 									 const TQ3NURBPatchData *geomData, TQ3ViewObject theView, TQ3Boolean isScreenSpaceSubdivision,
822 									 float* uBasisValues, float* vBasisValues, float* uBasisDerivValues, float* vBasisDerivValues )
823 {	float			*interestingU, *interestingV ;
824 	TQ3Uns32		nu, nv,
825 					maxdepth, somedepth,
826 					numIntU, numIntV ;
827 	TQ3Point3D		u0v0, u1v0, u0v1, u1v1 ;
828 	TQ3Point2D		u0v02, u1v02, u0v12, u1v12 ;
829 	// To cache the local -> world and local -> screen matrices
830 	TQ3Matrix4x4	localToWorld, worldToFrustum, frustumToWindow, localToWindow ;
831 
832 #if Q3_DEBUG
833 	Q3_ASSERT( thePoints != NULL && numPoints != NULL && theUVs != NULL && theNormals != NULL
834 			   && theTriangles != NULL && numTriangles != NULL ) ;
835 #endif
836 
837 	// For the error handler
838 	interestingU = interestingV = NULL ;
839 
840 	// First some sanity checking on subdivisionData
841 	subdiv = E3Num_Max(subdiv, 0.001f) ;
842 
843 	// Find the interesting knots (ie skip the repeated knots)
844 	interestingU = (float *) Q3Memory_Allocate((geomData->numColumns - geomData->uOrder + 2) * sizeof(float));
845 	if (interestingU == NULL) {
846 		goto nurbpatch_world_subdiv_error_handler ;
847 	}
848 	numIntU = e3geom_nurbpatch_interesting_knots( geomData->uKnots, geomData->numColumns, geomData->uOrder, interestingU );
849 
850 	interestingV = (float *) Q3Memory_Allocate((geomData->numRows - geomData->vOrder + 2) * sizeof(float));
851 	if (interestingV == NULL) {
852 		goto nurbpatch_world_subdiv_error_handler ;
853 	}
854 	numIntV = e3geom_nurbpatch_interesting_knots( geomData->vKnots, geomData->numRows, geomData->vOrder, interestingV );
855 
856 	// we need the localToWorld matrix for both world and screen subdivisions
857 	Q3View_GetLocalToWorldMatrixState(theView, &localToWorld);
858 	// if we are doing a screen subdivision,
859 	// truncate subdiv into an integer and cache the worldToWindow matrix
860 	if( isScreenSpaceSubdivision ) {
861 		// truncate subdiv
862 		subdiv = (float) floor( subdiv ) ;
863 		// cache the localToWindow matrix for theView
864 		Q3View_GetWorldToFrustumMatrixState(theView,  &worldToFrustum);
865 		Q3View_GetFrustumToWindowMatrixState(theView, &frustumToWindow);
866 
867 		Q3Matrix4x4_Multiply(&localToWorld, &worldToFrustum, &localToWindow);
868 		Q3Matrix4x4_Multiply(&localToWindow, &frustumToWindow, &localToWindow);
869 	}
870 
871 
872 	// square subdiv now to save a whole lot of sqrt's later (in the distance comparison)
873 	subdiv *= subdiv ;
874 
875 	maxdepth = 0 ;
876 
877 	// Iterate over every 'square' of the grid resulting from (u-knots) x (v-knots)
878 	for( nv = 0 ; nv < numIntV -1 ; nv++ ) {
879 		for( nu = 0 ; nu < numIntU -1 ; nu++ ) {
880 
881 			e3geom_nurbpatch_evaluate_uv_no_deriv( interestingU[ nu ],
882 												   interestingV[ nv ],
883 												   geomData,
884 												   &u0v0,
885 												   uBasisValues, vBasisValues );
886 			e3geom_nurbpatch_evaluate_uv_no_deriv( interestingU[ nu +1 ],
887 												   interestingV[ nv ],
888 												   geomData,
889 												   &u1v0,
890 												   uBasisValues, vBasisValues );
891 			e3geom_nurbpatch_evaluate_uv_no_deriv( interestingU[ nu ],
892 												   interestingV[ nv +1 ],
893 												   geomData,
894 												   &u0v1,
895 												   uBasisValues, vBasisValues );
896 			e3geom_nurbpatch_evaluate_uv_no_deriv( interestingU[ nu +1 ],
897 												   interestingV[ nv +1 ],
898 												   geomData,
899 												   &u1v1,
900 												   uBasisValues, vBasisValues );
901 
902 			if( kQ3False == isScreenSpaceSubdivision ) {
903 				Q3Point3D_Transform(&u0v0, &localToWorld, &u0v0) ;
904 				Q3Point3D_Transform(&u1v0, &localToWorld, &u1v0) ;
905 				Q3Point3D_Transform(&u0v1, &localToWorld, &u0v1) ;
906 				Q3Point3D_Transform(&u1v1, &localToWorld, &u1v1) ;
907 				somedepth = e3geom_nurbpatch_recursive_quad_world_subdivide( 0, subdiv,
908 																 interestingU[ nu ], interestingU[ nu +1 ],
909 																 interestingV[ nv ], interestingV[ nv +1 ],
910 																 &u0v0, &u1v0, &u0v1, &u1v1,
911 																 geomData, &localToWorld,
912 																 uBasisValues, vBasisValues ) ;
913 			} else {
914 				Q3Point3D_Transform(&u0v0, &localToWorld, &u0v0) ;
915 				u0v02.x = u0v0.x ;
916 				u0v02.y = u0v0.y ;
917 				Q3Point3D_Transform(&u1v0, &localToWorld, &u1v0) ;
918 				u1v02.x = u1v0.x ;
919 				u1v02.y = u1v0.y ;
920 				Q3Point3D_Transform(&u0v1, &localToWorld, &u0v1) ;
921 				u0v12.x = u0v1.x ;
922 				u0v12.y = u0v1.y ;
923 				Q3Point3D_Transform(&u1v1, &localToWorld, &u1v1) ;
924 				u1v12.x = u1v1.x ;
925 				u1v12.y = u1v1.y ;
926 				somedepth = e3geom_nurbpatch_recursive_quad_screen_subdivide( 0, subdiv,
927 																  interestingU[ nu ], interestingU[ nu +1 ],
928 																  interestingV[ nv ], interestingV[ nv +1 ],
929 																  &u0v02, &u1v02, &u0v12, &u1v12,
930 																  geomData, &localToWindow,
931 																  uBasisValues, vBasisValues ) ;
932 			}
933 
934 			maxdepth = maxdepth > somedepth ? maxdepth : somedepth ;
935 
936 		} // ~for( nu )
937 	} // ~for( nv )
938 	Q3Memory_Free( &interestingU ) ;
939 	Q3Memory_Free( &interestingV ) ;
940 
941 	subdiv = (float)pow( 2.0, (int)maxdepth -1 ) ;
942 
943 
944 	// I am not sure what causes subdiv to become infinite or what the value should really be,
945 	// but I am sure that we do not want infinity here.
946 	if (!isfinite( subdiv ))
947 		subdiv = kFiniteSubdivision;
948 
949 
950 	e3geom_nurbpatch_constant_subdiv( thePoints, numPoints, theUVs, theNormals,
951 									  theTriangles, numTriangles,
952 									  subdiv, subdiv,
953 									  geomData,
954 									  uBasisValues, vBasisValues, uBasisDerivValues, vBasisDerivValues ) ;
955 
956 	return ;
957 
958    nurbpatch_world_subdiv_error_handler:
959 	Q3Memory_Free( &interestingU ) ;
960 	Q3Memory_Free( &interestingV ) ;
961 
962 	*thePoints = NULL ;
963 	return ;
964 }
965 
966 
967 
968 
969 
970 //=============================================================================
971 //      e3geom_nurbpatch_constant_subdiv : Subdivide the given NURB curve into
972 //										   the some number of segments.
973 //-----------------------------------------------------------------------------
974 //		Note :	If the points array is non-NULL on return, be sure to free it
975 //				with Q3Memory_Free(). If it is NULL, then an error has occured.
976 //-----------------------------------------------------------------------------
977 static void
978 e3geom_nurbpatch_constant_subdiv( TQ3Point3D** thePoints, TQ3Uns32* numPoints,
979 								  TQ3Param2D** theUVs, TQ3Vector3D** theNormals,
980 								  TQ3TriMeshTriangleData** theTriangles, TQ3Uns32* numTriangles,
981 								  float subdivU, float subdivV,
982 								  const TQ3NURBPatchData *geomData,
983 								  float* uBasisValues, float* vBasisValues, float* uBasisDerivValues, float* vBasisDerivValues )
984 {	float		incrementU, incrementV, curIncrU, curIncrV, curU, curV ;
985 	float		*interestingU, *interestingV;
986 	TQ3Uns32	curKnotU, curKnotV, u, v, ptInd, trInd,
987 				numIntU, numIntV, numrows, numcolumns, numpts, numtris ;
988 
989 #if Q3_DEBUG
990 	Q3_ASSERT( thePoints != NULL && numPoints != NULL && theUVs != NULL && theNormals != NULL
991 			   && theTriangles != NULL && numTriangles != NULL ) ;
992 #endif
993 
994 	// First some sanity checking on subdivisionData
995 	subdivU = (float) ((TQ3Uns32) E3Num_Clamp(subdivU, 1.0f, 256.0f));
996 	subdivV = (float) ((TQ3Uns32) E3Num_Clamp(subdivV, 1.0f, 256.0f));
997 
998 	// Find the interesting knots (ie skip the repeated knots)
999 	interestingU = (float *) Q3Memory_Allocate((geomData->numColumns - geomData->uOrder + 2) * sizeof(float));
1000 	if (interestingU == NULL) {
1001 		*thePoints = NULL ;
1002 		return ;
1003 	}
1004 	numIntU = e3geom_nurbpatch_interesting_knots( geomData->uKnots, geomData->numColumns, geomData->uOrder, interestingU );
1005 	numcolumns = (numIntU-1)*((TQ3Uns32)subdivU) + 1;
1006 
1007 	interestingV = (float *) Q3Memory_Allocate((geomData->numRows - geomData->vOrder + 2) * sizeof(float));
1008 	if (interestingV == NULL) {
1009 		Q3Memory_Free( &interestingU ) ;
1010 
1011 		*thePoints = NULL ;
1012 		return ;
1013 	}
1014 	numIntV = e3geom_nurbpatch_interesting_knots( geomData->vKnots, geomData->numRows, geomData->vOrder, interestingV );
1015 	numrows = (numIntV-1)*((TQ3Uns32)subdivV) + 1;
1016 
1017 	// Number of points, number of triangles
1018 	numpts = numrows * numcolumns;
1019 	numtris = (numrows - 1)*(numcolumns - 1)*2;
1020 
1021 	// Allocate some memory for the TriMesh
1022 	*thePoints    = (TQ3Point3D *)             Q3Memory_Allocate(numpts    * sizeof(TQ3Point3D));
1023 	*theNormals   = (TQ3Vector3D *)            Q3Memory_Allocate(numpts    * sizeof(TQ3Vector3D));
1024 	*theUVs       = (TQ3Param2D  *)            Q3Memory_Allocate(numpts    * sizeof(TQ3Param2D));
1025 	*theTriangles = (TQ3TriMeshTriangleData *) Q3Memory_Allocate(numtris * sizeof(TQ3TriMeshTriangleData));
1026 
1027 	if (*thePoints == NULL || *theNormals == NULL || *theUVs == NULL || *theTriangles == NULL) {
1028 		Q3Memory_Free( &interestingU ) ;
1029 		Q3Memory_Free( &interestingV ) ;
1030 
1031 		*thePoints = NULL ;
1032 		return ;
1033 	}
1034 	// Outer V loop
1035 	for (curKnotV = 0; curKnotV < numIntV - 1; curKnotV++ ) {
1036 		incrementV = (interestingV[curKnotV+1] - interestingV[curKnotV]) / subdivV;
1037 
1038 		for (curIncrV = 0.0f; curIncrV < subdivV; curIncrV+=1.0f ) {
1039 			curV = interestingV[curKnotV] + curIncrV*incrementV;
1040 
1041 			// Inner U loop
1042 			for (curKnotU = 0; curKnotU < numIntU - 1; curKnotU++ ) {
1043 				incrementU = (interestingU[curKnotU+1] - interestingU[curKnotU]) / subdivU;
1044 
1045 				for (curIncrU = 0.0f; curIncrU < subdivU; curIncrU+=1.0f ) {
1046 						curU = interestingU[curKnotU] + curIncrU*incrementU;
1047 
1048 						ptInd =  ( curKnotV*(TQ3Uns32)subdivV+(TQ3Uns32)curIncrV )*numcolumns +
1049 								curKnotU*(TQ3Uns32)subdivU+(TQ3Uns32)curIncrU ;
1050 						// Let's try this for our uv's
1051 						(*theUVs)[ptInd].u = curU ;
1052 						(*theUVs)[ptInd].v = curV ;
1053 
1054 						e3geom_nurbpatch_evaluate_uv(
1055 												curU,
1056 												curV,
1057 												geomData,
1058 												&(*theNormals)[ptInd],
1059 												&(*thePoints)[ptInd],
1060 												uBasisValues, vBasisValues, uBasisDerivValues, vBasisDerivValues );
1061 				}
1062 			}
1063 			// Cap evaluation for u
1064 			ptInd = ( curKnotV*(TQ3Uns32)subdivV+(TQ3Uns32)curIncrV )*numcolumns +
1065 					numcolumns - 1;
1066 			// Let's try this for our uv's
1067 			(*theUVs)[ptInd].u = interestingU[numIntU - 1] ;
1068 			(*theUVs)[ptInd].v = curV ;
1069 			e3geom_nurbpatch_evaluate_uv(
1070 										interestingU[numIntU - 1],
1071 										curV,
1072 										geomData,
1073 										&(*theNormals)[ptInd],
1074 										&(*thePoints)[ptInd],
1075 										uBasisValues, vBasisValues, uBasisDerivValues, vBasisDerivValues );
1076 
1077 		}
1078 	}
1079 	// Final evaluation loop (on the v-cap)
1080 	for (curKnotU = 0; curKnotU < numIntU - 1; curKnotU++ ) {
1081 		incrementU = (interestingU[curKnotU+1] - interestingU[curKnotU]) / subdivU;
1082 
1083 		for (curIncrU = 0.0f; curIncrU < subdivU; curIncrU+=1.0f ) {
1084 				curU = interestingU[curKnotU] + curIncrU*incrementU;
1085 
1086 				ptInd =  (numrows - 1)*numcolumns +
1087 						curKnotU*(TQ3Uns32)subdivU+(TQ3Uns32)curIncrU ;
1088 				// Let's try this for our uv's
1089 				(*theUVs)[ptInd].u = curU ;
1090 				(*theUVs)[ptInd].v = interestingV[numIntV - 1] ;
1091 
1092 				e3geom_nurbpatch_evaluate_uv(
1093 										curU,
1094 										interestingV[numIntV - 1],
1095 										geomData,
1096 										&(*theNormals)[ptInd],
1097 										&(*thePoints)[ptInd],
1098 										uBasisValues, vBasisValues, uBasisDerivValues, vBasisDerivValues );
1099 		}
1100 	}
1101 	// The grande finale cap evaluation
1102 	ptInd = numpts - 1;
1103 	// Let's try this for our uv's
1104 	(*theUVs)[ptInd].u = interestingU[numIntU - 1] ;
1105 	(*theUVs)[ptInd].v = interestingV[numIntV - 1] ;
1106 
1107 	e3geom_nurbpatch_evaluate_uv(
1108 								interestingU[numIntU - 1],
1109 								interestingV[numIntV - 1],
1110 								geomData,
1111 								&(*theNormals)[ptInd],
1112 								&(*thePoints)[numpts - 1],
1113 								uBasisValues, vBasisValues, uBasisDerivValues, vBasisDerivValues );
1114 
1115 	// Make triangles from the points
1116 	for ( v = 0; v < numrows - 1; v++ )
1117 		for ( u = 0; u < (numcolumns - 1)*2; u+=2 ) {
1118 			// The first triangle
1119 			trInd = v*(numcolumns-1)*2 + u;
1120 															ptInd = v*numcolumns + u/2;
1121 			(*theTriangles)[trInd].pointIndices[0] = ptInd;
1122 															ptInd = v*numcolumns + u/2 +1;
1123 			(*theTriangles)[trInd].pointIndices[1] = ptInd;
1124 															ptInd = (v+1)*numcolumns + u/2;
1125 			(*theTriangles)[trInd].pointIndices[2] = ptInd;
1126 			// The second triangle
1127 			trInd = v*(numcolumns-1)*2 + u+1;
1128 															ptInd = v*numcolumns + u/2 +1;
1129 			(*theTriangles)[trInd].pointIndices[0] = ptInd;
1130 															ptInd = (v+1)*numcolumns + u/2 +1;
1131 			(*theTriangles)[trInd].pointIndices[1] = ptInd;
1132 															ptInd = (v+1)*numcolumns + u/2;
1133 			(*theTriangles)[trInd].pointIndices[2] = ptInd;
1134 	}
1135 
1136 	//  Uncomment this and get a big flat square approximating the surface.
1137 	//	Not very interesting if what is supposed to work does ;)
1138 	//	(*theTriangles)[0].pointIndices[0] = 0;
1139 	//	(*theTriangles)[0].pointIndices[1] = numpts-1;//numcolumns - 1;
1140 	//	(*theTriangles)[0].pointIndices[2] = (numrows-1)*(numcolumns);
1141 	//	(*theTriangles)[1].pointIndices[0] = numrows - 1;
1142 	//	(*theTriangles)[1].pointIndices[1] = numpts - 1;
1143 	//	(*theTriangles)[1].pointIndices[2] = (numrows-1)*(numcolumns);
1144 
1145 	*numPoints = numpts ;
1146 	*numTriangles = numtris ;
1147 }
1148 
1149 
1150 
1151 
1152 
1153 //=============================================================================
1154 //      e3geom_nurbpatch_cache_new : NURBPatch cache new method.
1155 //-----------------------------------------------------------------------------
1156 static TQ3Object
1157 e3geom_nurbpatch_cache_new(TQ3ViewObject theView, TQ3GeometryObject theGeom, const TQ3NURBPatchData *geomData)
1158 {	TQ3TriMeshData			triMeshData;
1159 	TQ3GeometryObject		theTriMesh;
1160 	TQ3GroupObject			theGroup;
1161 	TQ3Point3D 				*points;
1162 	TQ3Vector3D 			*normals;
1163 	TQ3Param2D				*uvs;
1164 	TQ3TriMeshTriangleData	*triangles;
1165 	TQ3SubdivisionStyleData	subdivisionData;
1166 	TQ3TriMeshAttributeData	vertexAttributes[2];
1167 	float					subdivU = 10.0f, subdivV = 10.0f;
1168 	TQ3Uns32				numpoints, numtriangles;
1169 	float					*uBasisValues, *vBasisValues, *uBasisDerivValues, *vBasisDerivValues ;
1170 
1171 	theGroup = NULL;
1172 	points = NULL ;
1173 	normals = NULL ;
1174 	uvs = NULL ;
1175 	triangles = NULL ;
1176 	uBasisValues = vBasisValues = uBasisDerivValues = vBasisDerivValues = NULL ;
1177 
1178 	// Set NULL initially so that return value is NULL if we goto the error label
1179 	Q3Memory_Clear(&triMeshData, sizeof(triMeshData));
1180 	theTriMesh = NULL;
1181 
1182 	uBasisValues = (float*) Q3Memory_Allocate( geomData->numColumns * sizeof(float) ) ;
1183 	if( uBasisValues == NULL )
1184 		goto surface_cache_new_error_cleanup ;
1185 	vBasisValues = (float*) Q3Memory_Allocate( geomData->numRows * sizeof(float) ) ;
1186 	if( vBasisValues == NULL )
1187 		goto surface_cache_new_error_cleanup ;
1188 
1189 	uBasisDerivValues = (float*) Q3Memory_Allocate( geomData->numColumns * sizeof(float) ) ;
1190 	if( uBasisDerivValues == NULL )
1191 		goto surface_cache_new_error_cleanup ;
1192 	vBasisDerivValues = (float*) Q3Memory_Allocate( geomData->numRows * sizeof(float) ) ;
1193 	if( vBasisDerivValues == NULL )
1194 		goto surface_cache_new_error_cleanup ;
1195 
1196 	// Get the subdivision style, figure out how to tessellate.
1197 	if (Q3View_GetSubdivisionStyleState( theView, &subdivisionData ) == kQ3Success) {
1198 		subdivU = subdivisionData.c1;
1199 		subdivV = subdivisionData.c2;
1200 
1201 		switch (subdivisionData.method) {
1202 			case kQ3SubdivisionMethodScreenSpace:
1203 				e3geom_nurbpatch_worldscreen_subdiv( &points, &numpoints, &uvs, &normals,
1204 												  	 &triangles, &numtriangles,
1205 												  	 subdivU,
1206 												  	 geomData, theView, kQ3True,
1207 												  	 uBasisValues, vBasisValues, uBasisDerivValues, vBasisDerivValues ) ;
1208 
1209 				if( points == NULL )
1210 					goto surface_cache_new_error_cleanup ;
1211 
1212 				break;
1213 
1214 			case kQ3SubdivisionMethodWorldSpace:
1215 				e3geom_nurbpatch_worldscreen_subdiv( &points, &numpoints, &uvs, &normals,
1216 												  	 &triangles, &numtriangles,
1217 												  	 subdivU,
1218 												  	 geomData, theView, kQ3False,
1219 												  	 uBasisValues, vBasisValues, uBasisDerivValues, vBasisDerivValues ) ;
1220 
1221 				if( points == NULL )
1222 					goto surface_cache_new_error_cleanup ;
1223 
1224 				break;
1225 
1226 			case kQ3SubdivisionMethodConstant:
1227 				e3geom_nurbpatch_constant_subdiv( &points, &numpoints, &uvs, &normals,
1228 												  &triangles, &numtriangles,
1229 												  subdivU, subdivV,
1230 												  geomData,
1231 												  uBasisValues, vBasisValues, uBasisDerivValues, vBasisDerivValues ) ;
1232 
1233 				if( points == NULL )
1234 					goto surface_cache_new_error_cleanup ;
1235 
1236 				break;
1237 
1238 			case kQ3SubdivisionMethodSize32:
1239 			default:
1240 				Q3_ASSERT(!"Unknown subdivision method");
1241 				break;
1242 		}
1243 	}
1244 
1245 
1246 
1247 	// set up the attributes
1248 	E3AttributeSet_Combine(geomData->patchAttributeSet, NULL, &triMeshData.triMeshAttributeSet);
1249 
1250 
1251 
1252 	// set up remaining trimesh data
1253 	vertexAttributes[0].attributeType     = kQ3AttributeTypeNormal;
1254 	vertexAttributes[0].data              = normals;
1255 	vertexAttributes[0].attributeUseArray = NULL;
1256 
1257 	vertexAttributes[1].attributeType     = kQ3AttributeTypeSurfaceUV;
1258 	vertexAttributes[1].data              = uvs;
1259 	vertexAttributes[1].attributeUseArray = NULL;
1260 
1261 	triMeshData.numPoints                 = numpoints;
1262 	triMeshData.points                    = points;
1263 	triMeshData.numTriangles              = numtriangles;
1264 	triMeshData.triangles                 = triangles;
1265 	triMeshData.numTriangleAttributeTypes = 0;
1266 	triMeshData.triangleAttributeTypes    = NULL;
1267 	triMeshData.numEdges                  = 0;
1268 	triMeshData.edges                     = NULL;
1269 	triMeshData.numEdgeAttributeTypes     = 0;
1270 	triMeshData.edgeAttributeTypes        = NULL;
1271 	triMeshData.numVertexAttributeTypes   = 2;
1272 	triMeshData.vertexAttributeTypes      = vertexAttributes;
1273 
1274 	Q3BoundingBox_SetFromPoints3D(&triMeshData.bBox,
1275 									triMeshData.points,
1276 									numpoints,
1277 									sizeof(TQ3Point3D));
1278 
1279 
1280 
1281 	// finally, create the TriMesh
1282 	theTriMesh = Q3TriMesh_New(&triMeshData);
1283 	theGroup   = E3TriMesh_BuildOrientationGroup(theTriMesh, kQ3OrientationStyleCounterClockwise);
1284 
1285 
1286 
1287 	// Clean up
1288 surface_cache_new_error_cleanup:
1289 
1290 	Q3Object_CleanDispose(&triMeshData.triMeshAttributeSet);
1291 	Q3Memory_Free(&points);
1292 	Q3Memory_Free(&normals);
1293 	Q3Memory_Free(&uvs);
1294 	Q3Memory_Free(&triangles);
1295 
1296 	Q3Memory_Free(&uBasisValues);
1297 	Q3Memory_Free(&vBasisValues);
1298 	Q3Memory_Free(&uBasisDerivValues);
1299 	Q3Memory_Free(&vBasisDerivValues);
1300 
1301 	return(theGroup);
1302 }
1303 
1304 
1305 
1306 
1307 
1308 //=============================================================================
1309 //      e3geom_nurbpatch_bounds : NURBPatch bounds method.
1310 //-----------------------------------------------------------------------------
1311 //		Note : Untested.  A more efficient algorithm would only pass 'suspect'
1312 //			   control points that have a chance of being used.  But this might
1313 //			   just mean stepping on some boundingbox function toes and make
1314 //			   things slower.
1315 //-----------------------------------------------------------------------------
1316 static TQ3Status
1317 e3geom_nurbpatch_bounds(TQ3ViewObject theView, TQ3ObjectType objectType, TQ3Object theObject, const void *objectData)
1318 {	const TQ3NURBPatchData			*instanceData = (const TQ3NURBPatchData *) objectData;
1319 #pragma unused(objectType)
1320 #pragma unused(theObject)
1321 
1322 
1323 
1324 	// Update the bounds
1325 	E3View_UpdateBounds(theView, instanceData->numRows * instanceData->numColumns, sizeof(TQ3RationalPoint4D), (TQ3Point3D *)instanceData->controlPoints);
1326 
1327 	return(kQ3Success);
1328 }
1329 
1330 
1331 
1332 
1333 
1334 //=============================================================================
1335 //      e3geom_nurbpatch_get_attribute : NURBPatch get attribute set pointer.
1336 //-----------------------------------------------------------------------------
1337 static TQ3AttributeSet *
1338 e3geom_nurbpatch_get_attribute ( E3NURBPatch* nurbPatch )
1339 	{
1340 	// Return the address of the geometry attribute set
1341 	return & nurbPatch->instanceData.patchAttributeSet ;
1342 	}
1343 
1344 
1345 
1346 
1347 
1348 //=============================================================================
1349 //      e3geom_nurbpatch_metahandler : NURBPatch metahandler.
1350 //-----------------------------------------------------------------------------
1351 static TQ3XFunctionPointer
1352 e3geom_nurbpatch_metahandler(TQ3XMethodType methodType)
1353 {	TQ3XFunctionPointer		theMethod = NULL;
1354 
1355 
1356 
1357 	// Return our methods
1358 	switch (methodType) {
1359 		case kQ3XMethodTypeObjectNew:
1360 			theMethod = (TQ3XFunctionPointer) e3geom_nurbpatch_new;
1361 			break;
1362 
1363 		case kQ3XMethodTypeObjectDelete:
1364 			theMethod = (TQ3XFunctionPointer) e3geom_nurbpatch_delete;
1365 			break;
1366 
1367 		case kQ3XMethodTypeObjectDuplicate:
1368 			theMethod = (TQ3XFunctionPointer) e3geom_nurbpatch_duplicate;
1369 			break;
1370 
1371 		case kQ3XMethodTypeGeomCacheNew:
1372 			theMethod = (TQ3XFunctionPointer) e3geom_nurbpatch_cache_new;
1373 			break;
1374 
1375 		case kQ3XMethodTypeObjectSubmitBounds:
1376 			theMethod = (TQ3XFunctionPointer) e3geom_nurbpatch_bounds;
1377 			break;
1378 
1379 		case kQ3XMethodTypeGeomGetAttribute:
1380 			theMethod = (TQ3XFunctionPointer) e3geom_nurbpatch_get_attribute;
1381 			break;
1382 
1383 		case kQ3XMethodTypeGeomUsesSubdivision:
1384 			theMethod = (TQ3XFunctionPointer) kQ3True;
1385 			break;
1386 		}
1387 
1388 	return(theMethod);
1389 }
1390 
1391 
1392 
1393 
1394 
1395 //=============================================================================
1396 //      Public functions
1397 //-----------------------------------------------------------------------------
1398 //      E3GeometryNURBPatch_RegisterClass : Register the class.
1399 //-----------------------------------------------------------------------------
1400 #pragma mark -
1401 TQ3Status
1402 E3GeometryNURBPatch_RegisterClass(void)
1403 	{
1404 	// Register the class
1405 	return Q3_REGISTER_CLASS (	kQ3ClassNameGeometryNURBPatch,
1406 								e3geom_nurbpatch_metahandler,
1407 								E3NURBPatch ) ;
1408 	}
1409 
1410 
1411 
1412 
1413 
1414 //=============================================================================
1415 //      E3GeometryNURBPatch_UnregisterClass : Unregister the class.
1416 //-----------------------------------------------------------------------------
1417 TQ3Status
1418 E3GeometryNURBPatch_UnregisterClass(void)
1419 {	TQ3Status		qd3dStatus;
1420 
1421 
1422 
1423 	// Unregister the class
1424 	qd3dStatus = E3ClassTree::UnregisterClass(kQ3GeometryTypeNURBPatch, kQ3True);
1425 
1426 	return(qd3dStatus);
1427 }
1428 
1429 
1430 
1431 
1432 
1433 //=============================================================================
1434 //      E3NURBPatch_New : Create a NURB patch object.
1435 //-----------------------------------------------------------------------------
1436 #pragma mark -
1437 TQ3GeometryObject
1438 E3NURBPatch_New(const TQ3NURBPatchData *nurbPatchData)
1439 {	TQ3Object		theObject;
1440 
1441 
1442 
1443 	// Create the object
1444 	theObject = E3ClassTree::CreateInstance ( kQ3GeometryTypeNURBPatch, kQ3False, nurbPatchData);
1445 	return(theObject);
1446 }
1447 
1448 
1449 
1450 
1451 
1452 //=============================================================================
1453 //      E3NURBPatch_Submit : Submit a NURB patch.
1454 //-----------------------------------------------------------------------------
1455 TQ3Status
1456 E3NURBPatch_Submit(const TQ3NURBPatchData *nurbPatchData, TQ3ViewObject theView)
1457 {	TQ3Status		qd3dStatus;
1458 
1459 
1460 
1461 	// Submit the geometry
1462 	qd3dStatus = E3View_SubmitImmediate(theView, kQ3GeometryTypeNURBPatch, nurbPatchData);
1463 	return(qd3dStatus);
1464 }
1465 
1466 
1467 
1468 
1469 
1470 //=============================================================================
1471 //      E3NURBPatch_SetData : Set a NURBPatch's internal data from public data.
1472 //-----------------------------------------------------------------------------
1473 //		Note : Quite untested.
1474 //-----------------------------------------------------------------------------
1475 TQ3Status
1476 E3NURBPatch_SetData(TQ3GeometryObject theNurbPatch, const TQ3NURBPatchData *nurbPatchData)
1477 	{
1478 	E3NURBPatch* nurbPatch = (E3NURBPatch*) theNurbPatch ;
1479 
1480 	// first, free the old data
1481 	e3geom_patch_disposedata ( & nurbPatch->instanceData ) ;
1482 
1483 	// then copy in the new data
1484 	TQ3Status qd3dStatus = e3geom_patch_copydata ( nurbPatchData, & nurbPatch->instanceData, kQ3False ) ;
1485 	Q3Shared_Edited ( nurbPatch ) ;
1486 
1487 	return qd3dStatus ;
1488 	}
1489 
1490 
1491 
1492 
1493 
1494 //=============================================================================
1495 //      E3NURBPatch_GetData : Get a NURBPatch's data.
1496 //-----------------------------------------------------------------------------
1497 //		Note : Quite untested.
1498 //-----------------------------------------------------------------------------
1499 TQ3Status
1500 E3NURBPatch_GetData(TQ3GeometryObject theNurbPatch, TQ3NURBPatchData *nurbPatchData)
1501 	{
1502 	E3NURBPatch* nurbPatch = (E3NURBPatch*) theNurbPatch ;
1503 
1504 	// Copy the data out of the NURBPatch
1505 	nurbPatchData->patchAttributeSet = NULL;
1506 
1507 	return e3geom_patch_copydata ( & nurbPatch->instanceData, nurbPatchData, kQ3False ) ;
1508 	}
1509 
1510 
1511 
1512 
1513 
1514 //=============================================================================
1515 //      E3NURBPatch_EmptyData : Dispose of a NURBPatch's data.
1516 //-----------------------------------------------------------------------------
1517 //		Note : Quite untested.
1518 //-----------------------------------------------------------------------------
1519 TQ3Status
1520 E3NURBPatch_EmptyData(TQ3NURBPatchData *nurbPatchData)
1521 {
1522 
1523 	// Dispose of the data
1524 	e3geom_patch_disposedata(nurbPatchData);
1525 
1526 	return(kQ3Success);
1527 }
1528 
1529 
1530 
1531 
1532 
1533 //=============================================================================
1534 //      E3NURBPatch_SetControlPoint : Set a NURB patch control point.
1535 //-----------------------------------------------------------------------------
1536 //		Note : Untested.
1537 //-----------------------------------------------------------------------------
1538 TQ3Status
1539 E3NURBPatch_SetControlPoint(TQ3GeometryObject theNurbPatch, TQ3Uns32 rowIndex, TQ3Uns32 columnIndex, const TQ3RationalPoint4D *point4D)
1540 	{
1541 	E3NURBPatch* nurbPatch = (E3NURBPatch*) theNurbPatch ;
1542 
1543 	// Copy the point from point4D to controlPoints
1544 	Q3Memory_Copy ( point4D, & nurbPatch->instanceData.controlPoints [ nurbPatch->instanceData.numColumns * rowIndex + columnIndex ], sizeof(TQ3RationalPoint4D) ) ;
1545 
1546 	Q3Shared_Edited ( nurbPatch ) ;
1547 
1548 	return kQ3Success ;
1549 	}
1550 
1551 
1552 
1553 
1554 
1555 //=============================================================================
1556 //      E3NURBPatch_GetControlPoint : Get a NURB patch control point.
1557 //-----------------------------------------------------------------------------
1558 //		Note : Untested.
1559 //-----------------------------------------------------------------------------
1560 TQ3Status
1561 E3NURBPatch_GetControlPoint(TQ3GeometryObject theNurbPatch, TQ3Uns32 rowIndex, TQ3Uns32 columnIndex, TQ3RationalPoint4D *point4D)
1562 	{
1563 	E3NURBPatch* nurbPatch = (E3NURBPatch*) theNurbPatch ;
1564 
1565 	// Copy the point from controlPoints to point4D
1566 	Q3Memory_Copy ( & nurbPatch->instanceData.controlPoints [ nurbPatch->instanceData.numColumns * rowIndex + columnIndex ], point4D, sizeof(TQ3RationalPoint4D) ) ;
1567 
1568 	return kQ3Success ;
1569 	}
1570 
1571 
1572 
1573 
1574 
1575 //=============================================================================
1576 //      E3NURBPatch_SetUKnot : Set a NURB patch knot on the U axis.
1577 //-----------------------------------------------------------------------------
1578 //		Note : Untested.
1579 //-----------------------------------------------------------------------------
1580 TQ3Status
1581 E3NURBPatch_SetUKnot(TQ3GeometryObject theNurbPatch, TQ3Uns32 knotIndex, float knotValue)
1582 	{
1583 	E3NURBPatch* nurbPatch = (E3NURBPatch*) theNurbPatch ;
1584 
1585 	// Copy the knot from knotValue to uKnots
1586 	Q3Memory_Copy ( &knotValue, & nurbPatch->instanceData.uKnots [ knotIndex ], sizeof(float) ) ;
1587 
1588 	Q3Shared_Edited ( nurbPatch ) ;
1589 
1590 	return kQ3Success ;
1591 	}
1592 
1593 
1594 
1595 
1596 
1597 //=============================================================================
1598 //      E3NURBPatch_SetVKnot : Set a NURB patch knot on the V axis.
1599 //-----------------------------------------------------------------------------
1600 //		Note : Untested.
1601 //-----------------------------------------------------------------------------
1602 TQ3Status
1603 E3NURBPatch_SetVKnot(TQ3GeometryObject theNurbPatch, TQ3Uns32 knotIndex, float knotValue)
1604 	{
1605 	E3NURBPatch* nurbPatch = (E3NURBPatch*) theNurbPatch ;
1606 
1607 	// Copy the knot from knotValue to vKnots
1608 	Q3Memory_Copy ( &knotValue, & nurbPatch->instanceData.vKnots [ knotIndex ], sizeof(float) ) ;
1609 
1610 	Q3Shared_Edited ( nurbPatch ) ;
1611 
1612 	return kQ3Success ;
1613 	}
1614 
1615 
1616 
1617 
1618 
1619 //=============================================================================
1620 //      E3NURBPatch_GetUKnot : Get a NURB patch knot on the U axis.
1621 //-----------------------------------------------------------------------------
1622 //		Note : Untested.
1623 //-----------------------------------------------------------------------------
1624 TQ3Status
1625 E3NURBPatch_GetUKnot(TQ3GeometryObject theNurbPatch, TQ3Uns32 knotIndex, float *knotValue)
1626 	{
1627 	E3NURBPatch* nurbPatch = (E3NURBPatch*) theNurbPatch ;
1628 
1629 	// Copy the knot from uKnots to knotValue
1630 	Q3Memory_Copy ( & nurbPatch->instanceData.uKnots [ knotIndex ], knotValue, sizeof(float) ) ;
1631 
1632 	return kQ3Success ;
1633 	}
1634 
1635 
1636 
1637 
1638 
1639 //=============================================================================
1640 //      E3NURBPatch_GetVKnot : Get a NURB patch knot on the V axis.
1641 //-----------------------------------------------------------------------------
1642 //		Note : Untested.
1643 //-----------------------------------------------------------------------------
1644 TQ3Status
1645 E3NURBPatch_GetVKnot(TQ3GeometryObject theNurbPatch, TQ3Uns32 knotIndex, float *knotValue)
1646 	{
1647 	E3NURBPatch* nurbPatch = (E3NURBPatch*) theNurbPatch ;
1648 
1649 	// Copy the knot from uKnots to knotValue
1650 	Q3Memory_Copy ( & nurbPatch->instanceData.vKnots [ knotIndex ], knotValue, sizeof(float) ) ;
1651 
1652 	return kQ3Success ;
1653 	}
1654 
1655 
1656 
1657 
1658 
1659 
1660 
1661