FreeWRL / FreeX3D 4.3.0
Component_NURBS.c
1
2/****************************************************************************
3 This file is part of the FreeWRL/FreeX3D Distribution.
4
5 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
6
7 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
19****************************************************************************/
20
21
22/*******************************************************************
23
24 X3D NURBS Component
25
26*********************************************************************/
27
28#include <config.h>
29#include <system.h>
30#include <display.h>
31#include <internal.h>
32
33#include <libFreeWRL.h>
34
35#include "../vrml_parser/Structs.h"
36#include "../main/headers.h"
37
38#include "Collision.h"
39#include "LinearAlgebra.h"
40#include "../opengl/Frustum.h"
41#include "../opengl/Material.h"
42#include "Component_Geometry3D.h"
43#include "../opengl/OpenGL_Utils.h"
44#include "../opengl/Textures.h"
45
46#include "Component_Shape.h"
47#include "../scenegraph/RenderFuncs.h"
48#include "../vrml_parser/CRoutes.h"
49#include "Polyrep.h"
50#include <float.h>
51#if defined(_MSC_VER) && _MSC_VER < 1500
52#define cosf cos
53#define sinf sin
54#endif
55
56typedef struct pComponent_NURBS{
57 void *nada;// = 0;
58
59}* ppComponent_NURBS;
60void *Component_NURBS_constructor(){
61 void *v = MALLOCV(sizeof(struct pComponent_NURBS));
62 memset(v,0,sizeof(struct pComponent_NURBS));
63 return v;
64}
65void Component_NURBS_init(struct tComponent_NURBS *t){
66 //public
67 //private
68 t->prv = Component_NURBS_constructor();
69 {
70 ppComponent_NURBS p = (ppComponent_NURBS)t->prv;
71 p->nada = NULL;
72 }
73}
74//ppComponent_NURBS p = (ppComponent_NURBS)gglobal()->Component_NURBS.prv;
75
76/*
77Specs:
78http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/nurbs.html
79Examples:
80http://www.web3d.org/x3d/content/examples/Basic/NURBS/
81
82Book: The NURBS Book
83Piegl, Les and Tiller, Wayne; The NURBS Book, 2nd Edition, Springer-Verlag (Berlin), 1997, ISBN: 3-540-61545-8.
84- about $120 new, softcover or kindle
85- some university libraries have it, you don't absolutely need it, the swept and swung ideas are in it
86
87Nov 2014 - dug9 did some nurbs using libnurbs2, and following The Nurbs Book
88 - didn't finish the component
89 - personal notes say Nurbs Level 1 done
90July 2016
91Not sure what's done and what's not, nothing indicaded on Conformace page:
92http://freewrl.sourceforge.net/conformance.html
93 code
94Level 1 Conformance page review
95CoordinateDouble (not mentioned) DONE
96NurbsCurve Not Implemented DONE
97NurbsOrientationInterpolatorNot Implemented stub
98NurbsPatchSurface Not Implemented DONE
99NurbsPositionInterpolator Not Implemented stub
100NurbsSurfaceInterpolator Not Implemented stub
101NurbsTextureCoordinate Not Implemented -
102Level 2
103NurbsSet Not Implemented -
104Level 3
105NurbsCurve2D Not Implemented DONE
106ContourPolyline2D Not Implemented DONE
107NurbsSweptSurface Not Implemented -
108NurbsSwungSurface Not Implemented -
109Level 4
110Contour2D Not Implemented DONE
111NurbsTrimmedSurface Not Implemented DONE?
112
113Perl: all the above including CoordinateDouble are in perl
114
115Analysis
116dug9, 2016:
117Chapter 12 of Redbook has lots of glu nurbs functions.
118freewrl doesn't use opengl glu.
119There is some source for it:
120 http://mesa3d.org/
121 ftp://ftp.freedesktop.org/pub/mesa/glu/
122 http://oss.sgi.com/projects/ogl-sample/
123Here's a blog complaining glu won't render some example nurbs curves
124 https://www.codeproject.com/articles/996281/nurbs-curve-made-easy
125So we're left to re-implement the hard way.
126I find nurbs libs are always disappointing in documentation.
127I think that's because there's not a lot to nurbs, mostly plumbing and little meat:
128- Each tesselation point Ci is computed as a weighted blend of control points: Ci = f(Pn,wn)
129- There are a few blending functions: linear, quadratic, cubic - in theory could go higher
130- there's a trick for doing sharp corners: knots (repeating a control point)
131- hard part: adaptive triangulation, might need callback plumbing, libnurbs2 has some of that
132Did I make a mistake trying to use libnurbs in 2014? Is it too much lib plumbing for too little meat?
133Here's a smaller MIT .c
134https://github.com/retuxx/tinyspline
135Or you can just read a bit more and manually implement nurbs.
136
137
138HARD PARTS:
1391. nurbsSet:
140 Q1.how smooth between nurbs 'tiles'? C0, C1, C2 continuity?
141 Q1a. how to find matching/joining/adjacent edges from 2+ patches?
142 Q1b. how to use adjoinging patch info to modify/blend on/near/in-overalap of the join?
143
1442. trimmed surfaces - procedure?
145 Hint: however we triangulate font glyphs in Component_Text would be a good example,
146 - might be able to borrow code from it
147 Terminology:
148 Tesselate: Nurbs term for interpolate/blend a given target interpolation point
149 Triangulate: take cloud of points, and join them with edges to make surface triangles
150
151 Fuzzy guess algo:
152 a) tesselate 2D curve in uv space of surface => A2dtess
153 b) tesselate 3D surface tesselation grid by itself => Btess
154 [option skip points inside A2dtess here instead of c)]
155 c) use point-in-poly test to remove Btess surface tesselation points
156 inside A2dtess tesselated curve: Btess - inside(A2dtess) => Ctess
157 d) go over A2dtess 2D curve point by point, interpolating/blending/tesselating in 3D like they
158 were surface tesselation points A2dtess => Dtess
159 e) add Dtess points to Ctess points => Etess
160 f) triangulate Etess => Etris
161 g) follow A2dtess points in Etris, swapping triangles so triangle edges are along A2dtess
162 h) do point-in-poly of Etris 2D triangle centroids, using A2dtess polygon,
163 to remove triangles inside A2dtess => Ftris
164 Ftris should be a trimmed surface
165
1663. Adaptive triangulation
167 a regular grid in uv space does not transform into a uniform grid in xyz space
168 with nurbs. And so there's a lot of plumbing in glu and other libs for
169 refining the xyz grid a) in xyz space or b) in screenspace (pixels), using some
170 measure of error and/or chordlength.
171
172 http://people.scs.carleton.ca/~c_shu/Publications/stmalo99.pdf
173 - Triangulating Trimmed NURBS Surfaces
174 http://www.saccade.com/writing/graphics/RE-PARAM.PDF
175 - Arc Length Parameterization of Spline Curves
176 - uses yet-another nurbs curve to interpolate XY space regular length chords
177
178
179*/
180void free_polyrep(struct X3D_PolyRep *rep){
181 //see also delete_polyrep - did dug9 duplicate the function or is it different?
182 if(rep){
183 rep->ntri = 0;
184 //rep->transparency = 0;
185 //Q. are any of these added to GC tables? If not..
186 glDeleteBuffers(VBO_COUNT, rep->VBO_buffers);
187 FREE_IF_NZ(rep->actualCoord);
188 FREE_IF_NZ(rep->cindex);
189 FREE_IF_NZ(rep->colindex);
190 FREE_IF_NZ(rep->GeneratedTexCoords[0]);
191 FREE_IF_NZ(rep->norindex);
192 FREE_IF_NZ(rep->normal);
193 FREE_IF_NZ(rep->tcindex);
194 FREE_IF_NZ(rep);
195 }
196}
197struct X3D_PolyRep * create_polyrep0(){
198 int i;
199 struct X3D_PolyRep *polyrep;
200
201 polyrep = MALLOC(struct X3D_PolyRep *, sizeof(struct X3D_PolyRep));
202 memset(polyrep,0,sizeof(struct X3D_PolyRep));
203 polyrep->itype = 2; //0 points 1 lines 2 mesh
204 polyrep->mode = 4; //4 TRIANGLES 5 TRIANGLE_STRIP 6 TRIANGLE_FAN
205 polyrep->ntri = -1;
206 //polyrep->cindex = 0; polyrep->actualCoord = 0; polyrep->colindex = 0; polyrep->color = 0;
207 //polyrep->norindex = 0; polyrep->normal = 0; polyrep->flat_normal = 0; polyrep->GeneratedTexCoords = 0;
208 //polyrep->tri_indices = 0; polyrep->wire_indices = 0; polyrep->actualFog = 0;
209 //polyrep->tcindex = 0;
210 //polyrep->tcoordtype = 0;
211 //polyrep->last_index_type = 0; polyrep->last_normal_type = 0;
212 polyrep->streamed = FALSE;
213
214 /* for Collision, default texture generation */
215 polyrep->minVals[0] = 999999.9f;
216 polyrep->minVals[1] = 999999.9f;
217 polyrep->minVals[2] = 999999.9f;
218 polyrep->maxVals[0] = -999999.9f;
219 polyrep->maxVals[1] = -999999.9f;
220 polyrep->maxVals[2] = -999999.9f;
221
222 for (i=0; i<VBO_COUNT; i++)
223 polyrep->VBO_buffers[i] = 0;
224
225 return polyrep;
226}
227struct X3D_PolyRep * create_polyrep(){
228 struct X3D_PolyRep *polyrep = create_polyrep0();
229 /* printf ("generating buffers for node %p, type %s\n",p,stringNodeType(p->_nodeType)); */
230 glGenBuffers(1,&polyrep->VBO_buffers[VERTEX_VBO]);
231 glGenBuffers(1,&polyrep->VBO_buffers[INDEX_VBO]);
232 return polyrep;
233}
234
235#define NURBS_LIB 1
236//#undef NURBS_LIB
237#ifdef NURBS_LIB
238//START MIT LIC >>>>>>>>
239//some algorithms from "The Nurbs Book", Les Piegl et al
240int uniformKnot(int n, int p, float *U){
241 int j, k, m, mm;
242 float uniform;
243 m = n + p + 1;
244 k = 0;
245 uniform = 1.0f/(float)(n-p);
246 for(j=0;j<p;k++,j++){
247 U[k] = 0.0f;
248 }
249 mm = n - p + 1;
250 for(j=0;j<mm;j++,k++){
251 U[k] = j*uniform;
252 }
253 for(j=0;j<p;j++,k++){
254 U[k] = 1.0f;
255 }
256 U[8] = 1.0f;
257 printf("U= ");
258 for(j=0;j<m+1;j++){
259 printf(" U[%d]=%f",j,U[j]);
260 }
261 return 1;
262}
263
264//ALGORITHM A2.1 p.68 Piegl
265int FindSpan(int n, int p, float u, float *U)
266{
267 /* Determine the knot span index: where u is in U[i]
268 Input:
269 n - # of control points == m - p - 1
270 p - degree of curve = power + 1 ie linear 2, quadratic 3, cubic 4
271 U - knot vector [0 ... m-1]
272 u - scalar curve parameter in range u0 - um
273 Return:
274 knot span index ie if u is between U[i] and U[i+1] return i
275 Internal:
276 order = p + 1
277 m = number of knots = n + order
278 Algorithm:
279 limit the search range between p and m - p - 1 (2 and 4 for this example)
280 assume clamped/pinned ends
281 Example:
282 U = { 0,0,0,1,2,3,4,4,5,5,5 } m = 11
283 spnidx 0 1 2 3 4 5 6 7 8 9
284 u = 2.5 ^ span index == 4
285 u = .0001 ^ span index == 2
286 u = 0 ^ span index == 2
287 u = .4999 ^ span index = 4
288 u = 5 ^ span index = 4
289
290 */
291 if(1){
292 //dug9 algo, simpler linear search
293 int i, span, m, order;
294 order = p + 1;
295 m = n + order;
296 span = p;
297 for(i=p;i<n;i++){
298 span = i;
299 if(u >= U[i] && u < U[i+1])
300 break;
301 }
302 return span;
303 }else{
304 int low, high, mid;
305 //if(u == U[n+1]) return n;
306 if(u == U[n]) return n-1; //this prevents divide by zero when u = 1
307 low = p; high = n+1; mid = (low+high)/2;
308 while(u < U[mid] || u >= U[mid+1]){
309 if(u < U[mid]) high = mid;
310 else low = mid;
311 mid = (low + high)/2;
312 }
313 return mid;
314 }
315}
316//ALGORITHM A2.2 p.70 Piegl
317int BasisFuns(int span, float u, int p, float *U, float *N){
318 /* Compute the non-vanishing Basis functions
319 Input:
320 span = knot span: which knots is this u in between: if between U[i] and U[i+1], span == i
321 u - scalar curve parameter in range u0 - um
322 p - degree of curve = power + 1 ie linear 2, quadratic 3, cubic 4
323 U - knot vector [0 ... m-1]
324 Output:
325 N - precomputed rational bernstein basis functions for a given span
326 - these are blending weights that say how much of each surrounding
327 control point is used in a given span
328 */
329 int j, r;
330 float left[5], right[5], saved, temp;
331 //float testzero;
332 N[0] =1.0f;
333 for(j=1;j<=p;j++){
334 left[j] = u - U[span+1 - j];
335 right[j] = U[span+j] - u;
336 saved = 0.0f;
337 for(r=0;r<j;r++){
338 //testzero = right[r+1]+left[j-r];
339 //if(fabs(testzero) < .00001)
340 // printf("ouch divide by zero\n");
341 temp = N[r]/(right[r+1]+left[j-r]);
342 N[r] = saved + right[r+1]*temp;
343 saved = left[j-r]*temp;
344 }
345 N[j] = saved;
346 }
347 return 1;
348}
349
350//ALGORITHM A4.1 p.124 Piegl
351int CurvePoint(int n, int p, float* U, float *Pw, float u, float *C )
352{
353 /* Compute point on rational B-spline curve
354 Input:
355 n - # of control points == m - p - 1
356 p - degree of curve linear 1, quadratic 2, cubic 3
357 U[] - knot vector [0 ... m], m = n + p + 1
358 Pw[] - control point vector
359 where w means rational/homogenous: Pw[i] = {wi*xi,wi*yi,wi*zi,wi}
360 u - scalar curve parameter in range u0 - um
361 Output:
362 C - 3D point = Cw/w
363 Internal:
364 span = knot span: which knots is this u in between: if between U[i] and U[i+1], span == i
365 N[] - precomputed rational bernstein basis functions for a given span
366 - these are blending weights that say how much of each surrounding control point is used in a given span
367 w - weight, assuming it's uniform
368 */
369 int span,i,j;
370 float N[100], w;
371 float Cw[4];
372 span = FindSpan(n,p,u,U);
373 BasisFuns(span,u,p,U,N);
374 w = 1.0f;
375 for(i=0;i<4;i++) Cw[i] = 0.0f;
376 //Cw[3] = w;
377 for(j=0;j<=p;j++){
378 for(i=0;i<4;i++){
379 Cw[i] += N[j]*Pw[(span-p+j)*4 + i];
380 }
381 }
382 for(i=0;i<3;i++)
383 C[i] = Cw[i]/Cw[3];
384
385 return 1;
386}
387
388
389
390
391
392//ALGORITHM A4.3 p.134 Piegl
393/* example call:
394ok = SurfacePoint( node->uDimension,node->uOrder-1,node->uKnot.p,
395 node->vDimension,node->vOrder-1,node->vKnot.p,
396 node->controlPoint.p,uv[0],uv[1],xyz);
397*/
398int SurfacePoint(int n,int p,float *U,
399 int m, int q,float *V,
400 float *Pw,float u,float v,float *S)
401{
402 /* Compute point on rational B-Spline surface S(u,v)
403 Input:
404 u direction:
405 n - # of control points
406 p - degree of curve linear 1, quadratic 2, cubic 3
407 U[] - knot vector [0 ... n + p + 1]
408 u - scalar curve parameter
409 v direction:
410 m - # of control points
411 q - degree of curve linear 1, quadratic 2, cubic 3
412 V[] - knot vector [0 ... m + q + 1]
413 v - scalar curve parameter
414 Pw[] - control point vector
415 where w means rational/homogenous: Pw[i] = {wi*xi,wi*yi,wi*zi,wi}
416 Output:
417 S - output 3D point = Sw/w
418 */
419 int uspan, vspan, i, l, k;
420 float Nu[100], Nv[100], temp[6][4], Sw[4];
421
422 uspan = FindSpan(n,p,u,U);
423 BasisFuns(uspan,u,p,U,Nu);
424 vspan = FindSpan(m,q,v,V);
425 BasisFuns(vspan,v,q,V,Nv);
426 for(l=0;l<=q;l++){
427 for(i=0;i<4;i++)
428 temp[l][i] = 0.0f;
429 for(k=0;k<=p;k++){
430 //temp[l] += Nu[k]*Pw[uspan-p+k][vspan-q+l];
431 for(i=0;i<4;i++)
432 temp[l][i] += Nu[k]*Pw[((uspan-p+k)*n + (vspan-q+l))*4 + i];
433
434 }
435 }
436 for(i=0;i<4;i++) Sw[i] = 0.0f;
437 for(l=0;l<=q;l++){
438 for(i=0;i<4;i++)
439 Sw[i] += Nv[l]*temp[l][i];
440 }
441 for(i=0;i<3;i++)
442 S[i] = Sw[i]/Sw[3];
443 return 1;
444}
445// <<<<< END MIT LIC
446#ifdef AQUA
447#include <OpenGL/gl.h>
448#include <OpenGL/glu.h>
449#define CALLBACK
450#else
451#ifndef _MSC_VER
452#define CALLBACK
453#include <GL/glu.h>
454#endif //__MSC_VER
455#include <../libnurbs/libnurbs2.h>
456#include <../libtess/libtess2.h>
457#endif
458static int DEBG = 0; //glu nurbs surface and trim calls
459static int DEBGC = 0; //curve calls
460
461//defined in Component_RigidBodyPhysics
462int NNC0(struct X3D_Node* node);
463void MNC0(struct X3D_Node* node);
464void MNX0(struct X3D_Node* node);
465#define NNC(A) NNC0(X3D_NODE(A)) //node needs compiling
466#define MNC(A) MNC0(X3D_NODE(A)) //mark node compiled
467#define MNX(A) MNX0(X3D_NODE(A)) //mark node changed
468#define PPX(A) getTypeNode(X3D_NODE(A)) //possible proto expansion
469
470void CALLBACK nurbsError(GLenum errorCode)
471{
472 //const GLubyte *estring;
473
474 //estring = gluErrorString(errorCode);
475 //fprintf (stderr, "Nurbs Error: %s\n", estring);
476 printf("ouch from nurbsError\n");
477 // exit (0);
478}
479
480//curve
481void CALLBACK nurbscurveBegincb(GLenum type, void *ud)
482{
483 struct X3D_NurbsCurve *node = (struct X3D_NurbsCurve *)ud;
484 if(DEBGC) printf("nurbscurveBegin\n");
485}
486void CALLBACK nurbscurveVertexcb(GLfloat *vertex, void *ud)
487{
488 int i, np,ns;
489 struct SFVec3f *pp;
490 struct X3D_NurbsCurve *node = (struct X3D_NurbsCurve *)ud;
491 ns = node->__points.n;
492 np = node->__numPoints;
493 if(np+1 > ns) {
494 ns = np *2;
495 node->__points.p = REALLOC(node->__points.p,ns * sizeof(struct SFVec3f));
496 node->__points.n = ns;
497 }
498 pp = &node->__points.p[np];
499 for(i=0;i<3;i++)
500 pp->c[i] = vertex[i];
501 node->__numPoints ++;
502 //node->__points.n++;
503 if(DEBGC) printf("nurbscurveVertex\n");
504}
505void CALLBACK nurbscurveNormalcb(GLfloat *nml, void *ud)
506{
507 struct X3D_NurbsCurve *node = (struct X3D_NurbsCurve *)ud;
508 if(DEBGC) printf("nurbscurveNormal\n");
509}
510void CALLBACK nurbscurveEndcb(void *ud)
511{
512 struct X3D_NurbsCurve *node = (struct X3D_NurbsCurve *)ud;
513 //node->__numPoints = node->__points.n;
514 if(DEBGC) printf("nurbscurveEnd\n");
515}
516
517
518
519int generateUniformKnotVector(int order, int ncontrol, float *knots){
520 //produced pinned uniform knot vector
521 //caller: please malloc knots = malloc( (ncontrol + order ) * sizeof(float))
522 // http://www.saccade.com/writing/graphics/KnotVectors.pdf
523 //maximum nuber of equalvalue consecutive knots:
524 // a) in middle of knot vector: <= order-1
525 // b) at start and end of knot vector: <= order (for pinned uniform)
526 // exmple order = 4 + ncontrol = 6 => 10 knots
527 // 0 0 0 0 .33 .66 1 1 1 1
528 // example order = 3 + ncontrol = 3 => 6 knots
529 // 0 0 0 1 1 1
530 // example order = 2 + ncontrol = 2 => 4 knots
531 // 0 0 1 1
532 //number of knots == ncontrol + order
533 int j,k,m;
534 float uniform;
535 m = ncontrol - order;
536 k = 0;
537 uniform = 1.0f/(float)(m + 1);
538 for(j=0;j<order;k++,j++){
539 knots[k] = 0.0f;
540 }
541 for(j=0;j<m;j++,k++){
542 knots[k] =uniform*(float)(j+1);
543 }
544 for(j=0;j<order;j++,k++){
545 knots[k] = 1.0f;
546 }
547 return m;
548}
549int knotsOK(int order, int ncontrol, int nknots, double *knots){
550 int ok = TRUE;
551
552 if(nknots < 2 || nknots != ncontrol + order )
553 ok = FALSE;
554 if(ok){
555 int i,nconsec = 1;
556 double lastval = knots[0];
557 for(i=1;i<nknots;i++){
558 if(lastval == knots[i]) nconsec++;
559 else nconsec = 1;
560 if(nconsec > order)
561 ok = false;
562 if(knots[i] < lastval)
563 ok = false;
564 if(!ok) break;
565 lastval = knots[i];
566 }
567 }
568 return ok;
569}
570int knotsOKf(int order, int ncontrol, int nknots, float *knots){
571 int ok = TRUE;
572
573 if(nknots < 2 || nknots != ncontrol + order )
574 ok = FALSE;
575 if(ok){
576 int i, nconsec = 1;
577 double lastval = knots[0];
578 for(i=1;i<nknots;i++){
579 if(lastval == knots[i]) nconsec++;
580 else nconsec = 1;
581 if(nconsec > order)
582 ok = false;
583 if(knots[i] < lastval)
584 ok = false;
585 if(!ok) break;
586 lastval = knots[i];
587 }
588 }
589 return ok;
590}
591void compile_ContourPolyline2D(struct X3D_ContourPolyline2D *node){
592 MARK_NODE_COMPILED;
593 if(node->point.n && !node->controlPoint.n){
594 int i;
595 //version v3.0 had a mfvec2f point field, version 3.1+ changed to mfvec2d controlPoint field
596 node->controlPoint.p = MALLOC(struct SFVec2d*,node->point.n * sizeof(struct SFVec2d));
597 for(i=0;i<node->point.n;i++)
598 float2double(node->controlPoint.p[i].c,node->point.p[i].c,2);
599 }
600}
601
602void compile_NurbsCurve(struct X3D_NurbsCurve *node){
603 MARK_NODE_COMPILED
604 {
605 int i,j, n, nk;
606 GLfloat *xyzw, *knots;
607 nk = n = 0;
608 xyzw = knots = NULL;
609 if(node->controlPoint){
610 if(node->controlPoint->_nodeType == NODE_CoordinateDouble){
611 struct Multi_Vec3d *mfd;
612 mfd = &((struct X3D_CoordinateDouble *)(node->controlPoint))->point;
613 n = mfd->n;
614 xyzw = MALLOC(void *, n * 4 * sizeof(GLfloat));
615 for(i=0;i<mfd->n;i++){
616 for(j=0;j<3;j++){
617 xyzw[i*4 + j] = (float) mfd->p[i].c[j];
618 }
619 }
620 }else if(node->controlPoint->_nodeType == NODE_Coordinate){
621 struct Multi_Vec3f *mff;
622 mff = &((struct X3D_Coordinate *)(node->controlPoint))->point;
623 n = mff->n;
624 xyzw = MALLOC(void *, n * 4 * sizeof(GLfloat));
625 for(i=0;i<mff->n;i++){
626 for(j=0;j<3;j++){
627 xyzw[i*4 + j] = mff->p[i].c[j];
628 }
629 }
630 }
631 }else{
632 n = 0;
633 }
634 if(node->weight.n && node->weight.n == n){
635 double w;
636 int m,im;
637 m = min(node->weight.n, n);
638 for(i=0;i<n;i++){
639 im = i < m ? i : m-1;
640 w = node->weight.p[im];
641 xyzw[i*4 + 3] = (float)w;
642 }
643 }else{
644 for(i=0;i<n;i++) xyzw[i*4 + 3] = 1.0;
645 }
646 //if(node->knot.n && node->knot.n == n + node->order ){
647 if(knotsOK(node->order,n,node->knot.n,node->knot.p)){
648
649 nk = node->knot.n;
650 knots = MALLOC(void *, nk * sizeof(GLfloat));
651 for(i=0;i<nk;i++){
652 knots[i] = (GLfloat)node->knot.p[i];
653 }
654 //printf("good knot nk=%d\n",nk);
655 //for(int ii=0;ii<nk;ii++)
656 // printf("[%d]=%f \n",ii,knots[ii]);
657
658 }else{
659 static int once = 0;
660 //generate uniform knot vector
661 nk = n + node->order ;
662 //caller: please malloc knots = malloc( (ncontrol + order ) * sizeof(float))
663 knots = MALLOC(void *, nk *sizeof(GLfloat));
664 generateUniformKnotVector(node->order,n, knots);
665 if(!once){
666 int ii;
667 printf("bad knot vector, replacing with:\n");
668 for(ii=0;ii<nk;ii++)
669 printf("[%d]=%f \n",ii,knots[ii]);
670 once = 1;
671 }
672 //nk = 0;
673 }
674
675 if(n && nk && nk >= n){
676 GLUnurbsObj *theNurb;
677 int ntess, mtess;
678 mtess = node->order + 1;
679 ntess = node->tessellation;
680 theNurb = gluNewNurbsRenderer();
681 gluNurbsProperty(theNurb, GLU_NURBS_MODE, GLU_NURBS_TESSELLATOR);
682 gluNurbsCallbackData(theNurb,(GLvoid*)node);
683 if(0){
684 //chord length or automatic - not implemented properly nor tested thoroughly
685 //if you do chord length in pixels, you need to manually pass in sampling matrices
686 //and somehow you need to trigger a recompile: another call to this compile_
687 // as avatar/viewer moves closer (father) from the nurbs node
688 double model[16], proj[16];
689 float modelf[16], projf[16];
690 int viewPort[10];
691 if(ntess > 0)
692 mtess = ntess;
693 else if(ntess < 0)
694 mtess = -ntess;
695 node->__points.p = MALLOC(void *, sizeof(struct SFVec3f)*n*10); // just a guess to get started
696 node->__points.n = n*10; //.n will be used for realloc test in callbacks
697
698 gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, (float)(mtess)); //25.0);
699 if(ntess < 0)
700 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_PATH_LENGTH); //pixels, the default
701 else
702 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_PARAMETRIC_TOLERANCE);
703 gluNurbsProperty(theNurb, GLU_AUTO_LOAD_MATRIX,GL_FALSE);
704
705 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, model);
706 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
707 FW_GL_GETINTEGERV(GL_VIEWPORT, viewPort);
708 for(i=0;i<16;i++){
709 modelf[i] = (float)model[i];
710 projf[i] = (float)proj[i];
711 }
712 gluLoadSamplingMatrices(theNurb,modelf,projf,viewPort);
713 }
714 if(1){
715 //uniform spacing of sampling points in u (or uv) parameter space - works
716 //node must specify tesselation value. see specs for interpretation
717 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/nurbs.html#NurbsCurve
718 if(ntess > 0)
719 mtess = max(mtess,ntess+1);
720 else if(ntess < 0)
721 mtess = max(mtess,(-ntess * n) + 1);
722 else
723 mtess = max(mtess,2*n + 1);
724 mtess = (int)((float)mtess * node->_tscale);
725 node->__points.p = MALLOC(void *, sizeof(struct SFVec3f)*mtess+1);
726 node->__points.n = mtess; //.n will be used for realloc test in callbacks
727 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_DOMAIN_DISTANCE);
728 gluNurbsProperty(theNurb,GLU_U_STEP,(GLfloat)mtess);
729 }
730 gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);
731 gluNurbsCallback(theNurb, GLU_ERROR, nurbsError);
732 gluNurbsCallback(theNurb, GLU_NURBS_BEGIN_DATA, nurbscurveBegincb);
733 gluNurbsCallback(theNurb, GLU_NURBS_VERTEX_DATA, nurbscurveVertexcb);
734 gluNurbsCallback(theNurb, GLU_NURBS_NORMAL_DATA, nurbscurveNormalcb);
735 gluNurbsCallback(theNurb, GLU_NURBS_END_DATA, nurbscurveEndcb);
736 gluBeginCurve(theNurb);
737 node->__numPoints = 0;
738 gluNurbsCurve(theNurb,nk,knots,4,xyzw,node->order,GL_MAP1_VERTEX_4);
739 gluEndCurve(theNurb);
740 gluDeleteNurbsRenderer(theNurb);
741 node->__points.n = node->__numPoints;
742 }
743 }
744}
745
746void render_NurbsCurve(struct X3D_NurbsCurve *node){
747 ttglobal tg = gglobal();
748 COMPILE_IF_REQUIRED
749 // from Arc2D
750 if (node->__numPoints>0) {
751 // for BoundingBox calculations
752 setExtent( node->EXTENT_MAX_X, node->EXTENT_MIN_X,
753 node->EXTENT_MAX_Y, node->EXTENT_MIN_Y, 0.0f,0.0f,X3D_NODE(node));
754
755 //OLDCODE GET_COLOUR_POINTER
756 LIGHTING_OFF
757 DISABLE_CULL_FACE
758 //OLDCODE DO_COLOUR_POINTER
759
760 FW_GL_VERTEX_POINTER (3,GL_FLOAT,0,(GLfloat *)node->__points.p);
761 sendArraysToGPU (GL_LINE_STRIP, 0, node->__numPoints);
762 tg->Mainloop.trisThisLoop += node->__numPoints;
763 }
764}
765
766void compile_NurbsTextureCoordinate(struct X3D_NurbsTextureCoordinate *node){
767 //get knots from double to float and QC the knots
768 int nc, nu, nku, nkv, nv, i,j;
769 float *knotsu, *knotsv, *xyzw;
770
771 struct Multi_Vec2f *mff;
772 mff = &node->controlPoint;
773 nc = mff->n;
774 xyzw = MALLOC(void *, nc * 4 * sizeof(GLfloat));
775 for(i=0;i<mff->n;i++){
776 for(j=0;j<2;j++){
777 xyzw[i*4 + j] = mff->p[i].c[j];
778 }
779 xyzw[i*4 + 2] = 0.0f; //z == 0 for 2D
780 xyzw[i*4 + 3] = 1.0f; //homogenous 1
781 }
782 nu = node->uDimension;
783 nv = node->vDimension;
784 if(node->weight.n && node->weight.n == nc){
785 double w;
786 int m,im;
787 m = min(node->weight.n, nc);
788 for(i=0;i<nc;i++){
789 im = i < m ? i : m-1;
790 w = node->weight.p[im];
791 xyzw[i*4 + 3] = (float) w;
792 }
793 }else{
794 for(i=0;i<nc;i++) xyzw[i*4 + 3] = 1.0;
795 }
796 nu = node->uDimension;
797 nv = node->vDimension;
798 //int knotsOK(int order, int ncontrol, int nknots, double *knots)
799 //if(node->uKnot.n && node->uKnot.n == nu + node->uOrder ){
800 if(knotsOK(node->uOrder,nu,node->uKnot.n,node->uKnot.p)){
801 //could do another check: max number of consecutive equal value knots == order
802 //could do another check: knot values == or ascending
803 nku = node->uKnot.n;
804 knotsu = MALLOC(void *, nku * sizeof(GLfloat));
805 for(i=0;i<nku;i++){
806 knotsu[i] = (GLfloat)node->uKnot.p[i];
807 }
808 if(DEBG){
809 int ii;
810 printf("good u knot vector nk=%d\n",nku);
811 for(ii=0;ii<nku;ii++)
812 printf("[%d]=%f \n",ii,knotsu[ii]);
813 }
814
815 }else{
816 //generate uniform knot vector
817 static int once = 0;
818 nku = nu + node->uOrder ;
819 //caller: please malloc knots = MALLOC(void *, (ncontrol + order ) * sizeof(float))
820 knotsu = MALLOC(void *, nku *sizeof(GLfloat));
821 generateUniformKnotVector(node->uOrder,nu, knotsu);
822 if(!once){
823 int ii;
824 printf("bad u knot vector given, replacing with:\n");
825 for(ii=0;ii<nku;ii++)
826 printf("[%d]=%f \n",ii,knotsu[ii]);
827 once = 1;
828 }
829 //nk = 0;
830 }
831
832 if(knotsOK(node->vOrder,nv,node->vKnot.n,node->vKnot.p)){
833 //if(node->vKnot.n && node->vKnot.n == nv + node->vOrder ){
834 nkv = node->vKnot.n;
835 knotsv = MALLOC(void *, nkv * sizeof(GLfloat));
836 for(i=0;i<nkv;i++){
837 knotsv[i] = (GLfloat)node->vKnot.p[i];
838 }
839 if(DEBG){
840 int ii;
841 printf("good v knot vector nk=%d\n",nkv);
842 for(ii=0;ii<nkv;ii++)
843 printf("[%d]=%f \n",ii,knotsv[ii]);
844 }
845
846 }else{
847 static int once = 0;
848 //generate uniform knot vector
849 nkv = nv + node->vOrder ;
850 //caller: please malloc knots = MALLOC(void *, (ncontrol + order ) * sizeof(float))
851 knotsv = MALLOC(void *, nkv *sizeof(GLfloat));
852 generateUniformKnotVector(node->vOrder,nv, knotsv);
853 if(!once){
854 int ii;
855 printf("bad v knot vector given, replacing with:\n");
856 for(ii=0;ii<nkv;ii++)
857 printf("[%d]=%f \n",ii,knotsv[ii]);
858 once = 1;
859 }
860 if(!knotsOKf(node->vOrder,nv,nkv,knotsv))
861 printf("ouch still not right knot vector\n");
862 //nk = 0;
863 }
864 node->_uKnot.p = knotsu;
865 node->_uKnot.n = nku;
866 node->_vKnot.p = knotsv;
867 node->_vKnot.n = nkv;
868 node->_controlPoint.p = (struct SFVec4f*)xyzw;
869 node->_controlPoint.n = nc;
870 MNC(node);
871
872}
873int getNurbsSurfacePoint(struct X3D_Node *nurbsSurfaceNode, float *uv, float *xyz){
874 int ret = 0;
875 if(nurbsSurfaceNode){
876 switch(nurbsSurfaceNode->_nodeType){
877 case NODE_NurbsTextureCoordinate:
878 {
879 struct X3D_NurbsTextureCoordinate *node = (struct X3D_NurbsTextureCoordinate *)PPX(nurbsSurfaceNode);
880 if(NNC(node)) compile_NurbsTextureCoordinate(node);
881 ret = SurfacePoint( node->uDimension,node->uOrder-1,node->_uKnot.p,
882 node->vDimension,node->vOrder-1,node->_vKnot.p,
883 (float *)node->_controlPoint.p,uv[0],uv[1],xyz);
884 }
885 break;
886 case NODE_NurbsPatchSurface:
887 break;
888 case NODE_NurbsTrimmedSurface:
889 break;
890 default:
891 break;
892 }
893 }
894
895 return ret;
896}
897
898/* GenPolyrep functions assume a node inherits from X3DGeometryNode.
899 NurbsPatchSurface inherits from X3DParametricGeometryNode.
900 So we can't use all the genpolyrep stuff, until we have our node compiled into one.
901 Then we can delegate back to generic polyrep functions
902render - can delegate to polyrep
903rendray - can delegate to polyrep
904(x make - we will do a compile instead)
905collide - can delegate to polyrep
906compile - custom: we will convert our parametric surface to a polyrep in here
907
908 */
909
910 //stripstate - used for capturing the callback data when using gluNurbs in TESSELATOR mode
911 //we use TESSELATOR mode instead of RENDER mode because we use GLES2, not desktop GL.
912 //the libnurbs we have has the GL rendering stuff ifdefed out.
913 //so we capture the data in the callbacks, and then we can do what we normally do with
914 //mesh data in GLES2
915
917 int type;
918 struct Vector pv; //vector of vertex points
919 struct Vector nv; //vector of normals
920 struct Vector tv; //vector of texcoords
921};
922
923//surface
924// strategy: on begin, we'll store the gl_ type, and zero __points
925// then accumulate the __points
926// and on end, convert the __points to ->_intern->polyrep points and triangle indices
927// using the stored type as a guide
928void CALLBACK nurbssurfBegincb(GLenum type, void *ud)
929{
930 struct stripState ss;
931 struct Vector * strips = (struct Vector *)ud;
932 if(0) if(DEBG) printf("callback nurbsSurfaceBegin\n");
933 if(0){
934 printf("nurbssurfBegin type = ");
935 switch(type){
936 case GL_QUAD_STRIP: printf("QUAD_STRIP");break;
937 case GL_TRIANGLE_STRIP: printf("TRIANGLE_STRIP");break;
938 case GL_TRIANGLE_FAN: printf("TRIANGLE_FAN");break;
939 case GL_TRIANGLES: printf("TRIANGLES");break;
940 default:
941 printf("not sure %x %d",type,type);
942 }
943 printf("\n");
944 }
945 ss.nv.n = 0;
946 ss.nv.allocn = 0;
947 ss.nv.data = NULL;
948 ss.tv.n = 0;
949 ss.tv.allocn = 0;
950 ss.tv.data = NULL;
951 ss.pv.n = 0;
952 ss.pv.allocn = 0;
953 ss.pv.data = NULL;
954 ss.type = type;
955 vector_pushBack(struct stripState,strips,ss);
956}
957void CALLBACK nurbssurfVertexcb(GLfloat *vertex, void *ud)
958{
959 struct stripState *ss;
960 struct SFVec3f pp;
961 struct Vector * strips = (struct Vector *)ud;
962 ss = vector_get_ptr(struct stripState,strips,strips->n -1);
963 memcpy(&pp,vertex,sizeof(struct SFVec3f));
964 vector_pushBack(struct SFVec3f,&ss->pv,pp);
965 //vector_set(struct stripState,strips,strips->n-1,ss);
966
967 if(0) printf("callback nurbssurfVertex %f %f %f\n",vertex[0],vertex[1],vertex[2]);
968}
969void CALLBACK nurbssurfNormalcb(GLfloat *nml, void *ud)
970{
971 struct stripState *ss;
972 struct SFVec3f pp;
973 struct Vector * strips = (struct Vector *)ud;
974 ss = vector_get_ptr(struct stripState,strips,strips->n -1);
975 memcpy(&pp,nml,sizeof(struct SFVec3f));
976 vector_pushBack(struct SFVec3f,&ss->nv,pp);
977 //vector_set(struct stripState,strips,strips->n-1,ss);
978
979 if(0) printf("callback nurbssurfNormal\n");
980}
981void CALLBACK nurbssurfEndcb(void *ud)
982{
983 struct stripState *ss;
984 struct Vector * strips = (struct Vector *)ud;
985 ss = vector_get_ptr(struct stripState,strips,strips->n -1);
986 if(0){
987 int i;
988 printf("nurbssurfEnd #p %d #n %d\n",ss->pv.n, ss->nv.n);
989 for(i=0;i<ss->pv.n;i++){
990 struct SFVec3f pp = vector_get(struct SFVec3f,&ss->pv,i);
991 printf("%f %f %f\n",pp.c[0],pp.c[1],pp.c[2]);
992 }
993 }
994 if(0) if(DEBG) printf("callback nurbsSurfaceEnd\n");
995
996}
997void CALLBACK nurbssurfTexcoordcb(GLfloat *tCrd, void *ud){
998 static int count = 0;
999 struct stripState *ss;
1000 struct SFVec2f tp;
1001 struct Vector * strips = (struct Vector *)ud;
1002 ss = vector_get_ptr(struct stripState,strips,strips->n -1);
1003 memcpy(&tp,tCrd,sizeof(struct SFVec2f));
1004 vector_pushBack(struct SFVec2f,&ss->tv,tp);
1005 //vector_set(struct stripState,strips,strips->n-1,ss);
1006 //printf("%f %f %f\n",tCrd[0],tCrd[1],0.0f);
1007 //count++;
1008 //if(count % 50 == 0)
1009 // printf("\n");
1010 if(0) if(DEBG)
1011 printf("callback nurbssufTexcoordcb\n");
1012}
1013
1014
1015
1016#define GL_QUAD_STRIP 0x0008
1017
1018static int USETXCOORD = 1;
1019void convert_strips_to_polyrep(struct Vector * strips,struct X3D_NurbsTrimmedSurface *node){
1020 //this is a bit like compile_polyrep, except the virt_make is below
1021
1022 int i, j, npoints, np, ni, ntri, nindex, ntc;
1023 struct stripState *ss;
1024 struct X3D_PolyRep *rep_, *polyrep;
1025 struct X3D_TextureCoordinate * tcnode, tcnode0;
1026 GLuint *cindex, *norindex, *tcindex;
1027 float *tcoord = NULL;
1028
1029 //from compile_polyrep:
1030 //node = X3D_NODE(innode);
1031 //virt = virtTable[node->_nodeType];
1032
1033 /* first time through; make the intern structure for this polyrep node */
1034 if(node->_intern){
1035 polyrep = (struct X3D_PolyRep*)node->_intern;
1036 FREE_IF_NZ(polyrep->cindex);
1037 FREE_IF_NZ(polyrep->actualCoord);
1038 FREE_IF_NZ(polyrep->GeneratedTexCoords[0]);
1039 FREE_IF_NZ(polyrep->colindex);
1040 FREE_IF_NZ(polyrep->color);
1041 FREE_IF_NZ(polyrep->norindex);
1042 FREE_IF_NZ(polyrep->normal);
1043 FREE_IF_NZ(polyrep->flat_normal);
1044 FREE_IF_NZ(polyrep->tcindex);
1045 FREE_IF_NZ(polyrep->wire_indices);
1046 //glDeleteBuffers(VBO_COUNT,polyrep->VBO_buffers); //streampoly checks if 0 before doing a new one
1047 }
1048 if(!node->_intern)
1049 node->_intern = (struct X3D_GeomRep*) create_polyrep();
1050
1051 rep_ = polyrep = (struct X3D_PolyRep*) node->_intern;
1052
1053
1054 /* if multithreading, tell the rendering loop that we are regenning this one */
1055 /* if singlethreading, this'll be set to TRUE before it is tested */
1056
1057 //<< END FROM Compile_polyrep
1058
1059 // Start Virt_make_polyrep section >>>
1060 //texcoord
1061 if(USETXCOORD){
1062 rep_->ntexdim[0] = 2;
1063 rep_->tcoordtype = NODE_TextureCoordinate; //??
1064 rep_->ntcoord = 1;
1065 }
1066 //tcnode = createNewX3DNode(NODE_TextureCoordinate);
1067 //memcpy(&tcnode0, tcnode, sizeof(struct X3D_TextureCoordinate));
1068 //free(tcnode);
1069 //tcnode = &tcnode0; //createNewX3DNode(NODE_TextureCoordinate);
1070 tcnode = createNewX3DNode(NODE_TextureCoordinate);
1071 npoints = nindex = ntc = 0;
1072 for(i=0;i<strips->n;i++){
1073 ss = vector_get_ptr(struct stripState,strips,i);
1074 npoints += ss->pv.n;
1075 ntc += ss->tv.n;
1076 switch(ss->type){
1077 case GL_QUAD_STRIP: nindex += (ss->pv.n -2)/2 * 5;break;
1078 case GL_TRIANGLE_STRIP: nindex += (ss->pv.n -2);break;
1079 case GL_TRIANGLE_FAN: nindex += (ss->pv.n -2);break;
1080 case GL_TRIANGLES: nindex += (ss->pv.n -2);break;
1081 default:
1082 nindex += (ss->pv.n -2);
1083 }
1084 }
1085 if (npoints > 0)
1086 {
1087 //printf("npoints %d ntc %d\n",npoints,ntc);
1088 rep_->actualCoord = MALLOC(void *, npoints * 3 * sizeof(float));
1089 rep_->normal = MALLOC(void *, npoints * 3 * sizeof(float));
1090 //if(USETXCOORD) rep->GeneratedTexCoords[0] = MALLOC(void *, npoints * 2 * sizeof(float));
1091 //if(USETXCOORD){
1092 // tcnode->point.p = MALLOC(void*, npoints * 2 * sizeof(float));
1093 // tcnode->point.n = npoints;
1094 //}
1095 //rep->t
1096 }
1097 rep_->ntri = ntri = nindex; //we'll over-malloc
1098
1099 if (rep_->ntri > 0)
1100 {
1101 cindex = rep_->cindex = MALLOC(GLuint *, sizeof(GLuint)*3*(ntri));
1102 norindex = rep_->norindex = MALLOC(GLuint *,sizeof(GLuint)*3*ntri);
1103 //if(USETXCOORD) rep_->tcindex = MALLOC(void *, ntri * 4 * sizeof(GLuint));
1104 tcindex = rep_->tcindex = MALLOC(GLuint*, sizeof(GLuint)*3*(ntri));
1105 // colindex = rep_->colindex = MALLOC(GLuint *, sizeof(*(rep_->colindex))*3*(ntri));
1106
1107 //FREE_IF_NZ(rep_->GeneratedTexCoords[0]);
1108 // we'll pass a X3D_TexCoordinate node //rep_->GeneratedTexCoords[0]
1109 tcoord = MALLOC (float *, sizeof (float) * ntri * 2 * 3);
1110
1111 }
1112
1113 np = 0;
1114 ni = 0;
1115 ntri = 0;
1116 for(i=0;i<strips->n;i++){
1117 ss = vector_get_ptr(struct stripState,strips,i);
1118//printf("ss.pv.n=%d nv.n=%d tv.n=%d\n",ss.pv.n,ss.nv.n,ss.tv.n);
1119 memcpy(&rep_->actualCoord[np*3],ss->pv.data,ss->pv.n * 3 * sizeof(float));
1120 memcpy(&rep_->normal[np*3],ss->nv.data,ss->nv.n * 3 * sizeof(float));
1121 if(USETXCOORD && tcoord) memcpy(&tcoord[np*2],ss->tv.data,ss->tv.n * 2 * sizeof(float));
1122 switch(ss->type){
1123 case GL_QUAD_STRIP:
1124 for(j=0;j<ss->pv.n -2;j+=2){
1125 rep_->cindex[ni++] = np+j;
1126 rep_->cindex[ni++] = np+j+1;
1127 rep_->cindex[ni++] = np+j+3;
1128 //rep->cindex[ni++] = -1;
1129 rep_->cindex[ni++] = np+j+3;
1130 rep_->cindex[ni++] = np+j+2;
1131 rep_->cindex[ni++] = np+j;
1132 //rep->cindex[ni++] = -1;
1133 //memcpy(&rep->norindex[ntri*4],&rep->cindex[ntri*4],2*4*sizeof(int));
1134 memcpy(&rep_->norindex[ntri*3],&rep_->cindex[ntri*3],2*3*sizeof(int));
1135 if(USETXCOORD) memcpy(&rep_->tcindex[ntri*3],&rep_->cindex[ntri*3],2*3*sizeof(int));
1136 ntri += 2;
1137 }
1138 break;
1139 case GL_TRIANGLE_STRIP:
1140 nindex += (ss->pv.n -2);
1141 break;
1142 case GL_TRIANGLE_FAN:
1143 //nindex += (ss.pv.n -2);
1144 for(j=0;j<ss->pv.n -2;j+=1){
1145 rep_->cindex[ni++] = np;
1146 rep_->cindex[ni++] = np+j+1;
1147 rep_->cindex[ni++] = np+j+2;
1148 memcpy(&rep_->norindex[ntri*3],&rep_->cindex[ntri*3],3*sizeof(int));
1149 if(USETXCOORD) memcpy(&rep_->tcindex[ntri*3],&rep_->cindex[ntri*3],3*sizeof(int));
1150 ntri += 1;
1151 }
1152 break;
1153 case GL_TRIANGLES:
1154 nindex += (ss->pv.n -2);
1155 break;
1156 default:
1157 nindex += (ss->pv.n -2);
1158 }
1159 np += ss->pv.n;
1160 }
1161 rep_->ntri = ntri;
1162 if(node->texCoord && node->texCoord->_nodeType == NODE_NurbsTextureCoordinate){
1163 static FILE *fp = NULL;
1164 for(i=0;i<np;i++){
1165 float stru[4];
1166 float xyz[4];
1167 //treat above callback texturecoord as uv,
1168 //and do lookup on NurbsTextureCoordinate surface to get new texture coord st
1169 xyz[0] = tcoord[i*2 + 1];
1170 xyz[1] = tcoord[i*2 + 0]; //don't know why but I have to swap xy to match octaga
1171 xyz[2] = 0.0f;
1172 xyz[3] = tcoord[i*2 + 3];
1173 getNurbsSurfacePoint(node->texCoord, xyz, stru);
1174 //memcpy(&tcoord[i*2],stru,2*sizeof(float));
1175 tcoord[i*2 + 0] = stru[0];
1176 tcoord[i*2 + 1] = stru[1];
1177 //if(1){
1178 // static int once = 0;
1179 // if(!once) fp= fopen("nurbstexturecoord.txt","w+");
1180 // once = 1;
1181 // fprintf(fp,"%d uv %f %f st %f %f\n",i,tcoord[i*2 +0],tcoord[i*2 + 1],stru[0],stru[1]);
1182 //}
1183 }
1184 //if(fp) fclose(fp);
1185 }
1186 tcnode->point.p = (struct SFVec2f*)tcoord;
1187 tcnode->point.n = np;
1188 if(0) for(i=0;i<tcnode->point.n;i++){
1189 printf("%d %f %f\n",i,tcnode->point.p[i].c[0],tcnode->point.p[i].c[1]);
1190 if(i % 50 == 0)
1191 printf("\n");
1192 }
1193
1194 //END virt_make_polyrep section <<<<<<
1195
1196 //FROM Compile_polyrep
1197 if (polyrep->ntri != 0) {
1198 //float *fogCoord = NULL;
1199 stream_polyrep(node, NULL,NULL,NULL,NULL, tcnode);
1200 /* and, tell the rendering process that this shape is now compiled */
1201 }
1202 FREE_IF_NZ(tcnode->point.p);
1203 //else wait for set_coordIndex to be converted to coordIndex
1204 //MARK POLYREP COMPILED
1205 polyrep->irep_change = node->_change;
1206
1207 /*
1208 // dump then can copy and paste to x3d or wrl IndexedFaceSet.coordIndex and Coordinate.point fields
1209 FILE * fp = fopen("IFS_DUMP.txt","w+");
1210 fprintf(fp,"#vertices %d\n",np);
1211 for(i=0;i<np;i++){
1212 fprintf(fp,"%f %f %f\n",rep->actualCoord[i*3 +0],rep->actualCoord[i*3 +1],rep->actualCoord[i*3 +2]);
1213 }
1214 fprintf(fp,"#face indices %d\n",ni);
1215 for(i=0;i<ni;i++){
1216 fprintf(fp,"%d ",rep->cindex[i]);
1217 if((ni+1) % 3 == 0)
1218 fprintf(fp,"%d ",-1);
1219 }
1220 fprintf(fp,"\n");
1221 fclose(fp);
1222 */
1223
1224}
1225
1226void compile_NurbsSurface(struct X3D_NurbsPatchSurface *node, struct Multi_Node *trim){
1227 MARK_NODE_COMPILED
1228
1229 {
1230 int i,j, n, nu, nv, nku, nkv;
1231 GLfloat *xyzw, *knotsu, *knotsv;
1232 ppComponent_NURBS p = (ppComponent_NURBS)gglobal()->Component_NURBS.prv;
1233
1234 nku = nkv = nu = nv = n = 0;
1235 xyzw = knotsu = knotsv = NULL;
1236 // I should call something like:
1237 // struct Multi_Vec3f *getCoordinate (struct X3D_Node *innode, char *str);
1238 // to get the control points - it will do proto expansion, compile the coordinate node if needed
1239 // (should do something similar with texcoord when implemented,
1240 // as I think it needs conversion from controlpoint spacing to sampling/tesselation spacing for use in polyrep)
1241 // here's an amature shortcut that returns doubles
1242 if(node->controlPoint){
1243 if(node->controlPoint->_nodeType == NODE_CoordinateDouble){
1244 struct Multi_Vec3d *mfd;
1245 mfd = &((struct X3D_CoordinateDouble *)(node->controlPoint))->point;
1246 n = mfd->n;
1247 xyzw = MALLOC(void *, n * 4 * sizeof(GLfloat));
1248 for(i=0;i<mfd->n;i++){
1249 for(j=0;j<3;j++){
1250 xyzw[i*4 + j] = (float)mfd->p[i].c[j];
1251 }
1252 }
1253 }else if(node->controlPoint->_nodeType == NODE_Coordinate){
1254 struct Multi_Vec3f *mff;
1255 mff = &((struct X3D_Coordinate *)(node->controlPoint))->point;
1256 n = mff->n;
1257 xyzw = MALLOC(void *, n * 4 * sizeof(GLfloat));
1258 for(i=0;i<mff->n;i++){
1259 for(j=0;j<3;j++){
1260 xyzw[i*4 + j] = mff->p[i].c[j];
1261 }
1262 }
1263 }
1264 }else{
1265 n = 0;
1266 }
1267 if(node->weight.n && node->weight.n == n){
1268 double w;
1269 int m,im;
1270 m = min(node->weight.n, n);
1271 for(i=0;i<n;i++){
1272 im = i < m ? i : m-1;
1273 w = node->weight.p[im];
1274 xyzw[i*4 + 3] = (float)w;
1275 }
1276 }else{
1277 for(i=0;i<n;i++) xyzw[i*4 + 3] = 1.0;
1278 }
1279 nu = node->uDimension;
1280 nv = node->vDimension;
1281 //int knotsOK(int order, int ncontrol, int nknots, double *knots)
1282 //if(node->uKnot.n && node->uKnot.n == nu + node->uOrder ){
1283 if(knotsOK(node->uOrder,nu,node->uKnot.n,node->uKnot.p)){
1284 //could do another check: max number of consecutive equal value knots == order
1285 //could do another check: knot values == or ascending
1286 nku = node->uKnot.n;
1287 knotsu = MALLOC(void *, nku * sizeof(GLfloat));
1288 for(i=0;i<nku;i++){
1289 knotsu[i] = (GLfloat)node->uKnot.p[i];
1290 }
1291 if(DEBG){
1292 int ii;
1293 printf("good u knot vector nk=%d\n",nku);
1294 for(ii=0;ii<nku;ii++)
1295 printf("[%d]=%f \n",ii,knotsu[ii]);
1296 }
1297
1298 }else{
1299 //generate uniform knot vector
1300 static int once = 0;
1301 nku = nu + node->uOrder ;
1302 //caller: please malloc knots = MALLOC(void *, (ncontrol + order ) * sizeof(float))
1303 knotsu = MALLOC(void *, nku *sizeof(GLfloat));
1304 generateUniformKnotVector(node->uOrder,nu, knotsu);
1305 if(!once){
1306 int ii;
1307 printf("bad u knot vector given, replacing with:\n");
1308 for(ii=0;ii<nku;ii++)
1309 printf("[%d]=%f \n",ii,knotsu[ii]);
1310 once = 1;
1311 }
1312 //nk = 0;
1313 }
1314
1315 if(knotsOK(node->vOrder,nv,node->vKnot.n,node->vKnot.p)){
1316 //if(node->vKnot.n && node->vKnot.n == nv + node->vOrder ){
1317 nkv = node->vKnot.n;
1318 knotsv = MALLOC(void *, nkv * sizeof(GLfloat));
1319 for(i=0;i<nkv;i++){
1320 knotsv[i] = (GLfloat)node->vKnot.p[i];
1321 }
1322 if(DEBG){
1323 int ii;
1324 printf("good v knot vector nk=%d\n",nkv);
1325 for(ii=0;ii<nkv;ii++)
1326 printf("[%d]=%f \n",ii,knotsv[ii]);
1327 }
1328
1329 }else{
1330 static int once = 0;
1331 //generate uniform knot vector
1332 nkv = nv + node->vOrder ;
1333 //caller: please malloc knots = MALLOC(void *, (ncontrol + order ) * sizeof(float))
1334 knotsv = MALLOC(void *, nkv *sizeof(GLfloat));
1335 generateUniformKnotVector(node->vOrder,nv, knotsv);
1336 if(!once){
1337 int ii;
1338 printf("bad v knot vector given, replacing with:\n");
1339 for(ii=0;ii<nkv;ii++)
1340 printf("[%d]=%f \n",ii,knotsv[ii]);
1341 once = 1;
1342 }
1343 //nk = 0;
1344 }
1345
1346 if(n && nku && nkv){
1347 static GLUnurbsObj *theNurb = NULL;
1348 int ntessu, ntessv, mtessu, mtessv;
1349 struct Vector * strips;
1350 struct X3D_Node * texCoordNode;
1351 int texcoordnodeIsGenerated;
1352
1353 mtessu = node->uOrder + 1;
1354 ntessu = node->uTessellation;
1355 mtessv = node->vOrder + 1;
1356 ntessv = node->vTessellation;
1357
1358 if(DEBG) printf("gluNewNurbsRenderer\n");
1359 if(!theNurb)
1360 theNurb = gluNewNurbsRenderer();
1361 gluNurbsProperty(theNurb, GLU_NURBS_MODE, GLU_NURBS_TESSELLATOR);
1362 if(0){
1363 //chord length or automatic - not implemented properly nor tested thoroughly
1364 //if you do chord length in pixels, you need to manually pass in sampling matrices
1365 //and somehow you need to trigger a recompile: another call to this compile_
1366 // as avatar/viewer moves closer (father) from the nurbs node
1367 double model[16], proj[16];
1368 float modelf[16], projf[16];
1369 int viewPort[10];
1370 if(ntessu > 0)
1371 mtessu = ntessu;
1372 else if(ntessu < 0)
1373 mtessu = -ntessu;
1374
1375 gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, (float)(mtessu)); //25.0);
1376 if(ntessu < 0)
1377 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_PATH_LENGTH); //pixels, the default
1378 else
1379 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_PARAMETRIC_TOLERANCE);
1380 gluNurbsProperty(theNurb, GLU_AUTO_LOAD_MATRIX,GL_FALSE);
1381
1382 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, model);
1383 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
1384 FW_GL_GETINTEGERV(GL_VIEWPORT, viewPort);
1385 for(i=0;i<16;i++){
1386 modelf[i] = (float)model[i];
1387 projf[i] = (float)proj[i];
1388 }
1389 gluLoadSamplingMatrices(theNurb,modelf,projf,viewPort);
1390 }
1391 if(1){
1392 //uniform spacing of sampling points in u (or uv) parameter space - works
1393 //node must specify tesselation value. see specs for interpretation
1394 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/nurbs.html#NurbsCurve
1395 if(ntessu > 0)
1396 mtessu = max(mtessu,ntessu+1);
1397 else if(ntessu < 0)
1398 mtessu = max(mtessu,(-ntessu * nu) + 1);
1399 else
1400 mtessu = max(mtessu,2*nu + 1);
1401
1402 if(ntessv > 0)
1403 mtessv = max(mtessv,ntessv+1);
1404 else if(ntessv < 0)
1405 mtessv = max(mtessv,(-ntessv * nv) + 1);
1406 else
1407 mtessv = max(mtessv,2*nv + 1);
1408 mtessu = (int)((float)mtessu * node->_tscale);
1409 mtessv = (int)((float)mtessv * node->_tscale);
1410
1411 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_DOMAIN_DISTANCE);
1412 gluNurbsProperty(theNurb,GLU_U_STEP,(GLfloat)mtessu);
1413 gluNurbsProperty(theNurb,GLU_V_STEP,(GLfloat)mtessv);
1414 }
1415 gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);
1416
1417 gluNurbsCallback(theNurb, GLU_ERROR, nurbsError);
1418 gluNurbsCallback(theNurb, GLU_NURBS_BEGIN_DATA, nurbssurfBegincb);
1419 gluNurbsCallback(theNurb, GLU_NURBS_VERTEX_DATA, nurbssurfVertexcb);
1420 gluNurbsCallback(theNurb, GLU_NURBS_NORMAL_DATA, nurbssurfNormalcb);
1421 gluNurbsCallback(theNurb, GLU_NURBS_END_DATA, nurbssurfEndcb);
1422 gluNurbsCallback(theNurb, GLU_NURBS_TEXTURE_COORD_DATA, nurbssurfTexcoordcb);
1423
1424 strips = newVector(struct stripState,20);
1425 gluNurbsCallbackData(theNurb,(GLvoid*)strips);
1426
1427 if(DEBG) printf("gluBeginSurface \n");
1428 gluBeginSurface(theNurb);
1429 gluNurbsSurface(theNurb,nku,knotsu,nkv,knotsv,4,4*nu,xyzw,node->uOrder,node->vOrder,GL_MAP2_VERTEX_4);
1430 /*
1431 TextureCoordinate handling
1432 https://www.opengl.org/discussion_boards/showthread.php/127668-Texture-mapping-for-NURBS
1433 https://www.cs.drexel.edu/~david/Classes/ICG/Lectures/Lecture7.pdf p.56 of slides
1434
1435 texture coordinate hypotheses:
1436 1. if regular texture coordinate node supplied,
1437 - H1a: use same order and knot vector as control, or
1438 - H1b: use linear order=2 and knot vector
1439 - specify the texturecoordinate points as control
1440 2. if no texture coordinate node node supplied,
1441 H2a:
1442 - compute defaults using relative spatial distance between xyz control
1443 along u (row) and v (column) directions
1444 - apply #1
1445 H2b:
1446 - compute defaults using equal spacing
1447 along u (row) and v (column) directions
1448 - apply #1
1449 H2c:
1450 - leave texcoords blank and let stream_polyrep supply defaults
1451 3. if nurbstexturecoordinate node supplied,
1452 H3a:
1453 - set texture surface control to 0 0 1 1
1454 - use linear order 2
1455 H3b:
1456 - do H2a or H2b
1457 Both:
1458 - interpret the texturecallback points as uv
1459 - use uv to lookup st using piegl surface interpolator
1460 on separate surface in nurbstexturecoordinate node
1461 options: a) in texture callback b) when converting to polyrep
1462 */
1463 texcoordnodeIsGenerated = FALSE; //for possible garbage collection
1464 texCoordNode = node->texCoord;
1465 if(!texCoordNode){
1466 //2 no texcoord node supplied
1467 switch('b'){
1468 case 'a':
1469 //H2a compute uniform defaults using relative spatial distance of xyz in u and v
1470 break;
1471 case 'b':
1472 // H2b: - compute defaults using equal spacing
1473 {
1474 float du, dv, uu, vv;
1475 int jj,j,k;
1476 struct X3D_TextureCoordinate *texCoord = createNewX3DNode0(NODE_TextureCoordinate);
1477 texcoordnodeIsGenerated = TRUE;
1478 texCoord->point.p = MALLOC(struct SFVec2f*,nu * nv * sizeof(struct SFVec2f));
1479 du = 1.0f / (float)max(1,(nu -1));
1480 dv = 1.0f / (float)max(1,(nv -1));
1481 vv = 0.0f;
1482 jj = 0;
1483 for(k=0;k<nv;k++){
1484 if(k == nv-1) vv = 1.0f; //they like end exact on 1.0f
1485 uu = 0.0f;
1486 for(j=0;j<nu;j++){
1487 if(j == nu-1) uu = 1.0f; //they like end exact on 1.0f
1488 texCoord->point.p[jj].c[0] = uu;
1489 texCoord->point.p[jj].c[1] = vv;
1490 uu += du;
1491 jj++;
1492 }
1493 vv += dv;
1494 }
1495 texCoord->point.n = jj;
1496 texCoordNode = X3D_NODE(texCoord);
1497 }
1498 break;
1499 default:
1500 //H2c skip - nada
1501 break;
1502 }
1503 }
1504 if(texCoordNode){
1505 //USETEXCOORD = TRUE
1506 if(texCoordNode->_nodeType == NODE_TextureCoordinate){
1507 //TEXCOORDTYPE = 1 //interpret nurbsurftexcoordcb coords as texture coords
1508 struct X3D_TextureCoordinate *texCoord = (struct X3D_TextureCoordinate *)texCoordNode;
1509 float *control2D = (float*)texCoord->point.p;
1510 int nctrl = texCoord->point.n;
1511 if(1){
1512 //H1a: use same order and knot vector as controlPoints
1513 // except using texcoord.point as nurbscontrol
1514 //CONFIRMED when using H2b 'b' above to generate missing TexCoord node
1515 gluNurbsSurface(theNurb,nku,knotsu,nkv,knotsv,2,2*nu,control2D,node->uOrder,node->vOrder,GL_MAP2_TEXTURE_COORD_2);
1516 }else{
1517 //H1b: use linear order=2 and knot vectors, order from main surface
1518 //DISCONFIRMED: the texturecoord callback is never called
1519 float *tknotsu, *tknotsv;
1520 int k;
1521
1522 tknotsu = MALLOC(float *, (nu+2) *sizeof(GLfloat));
1523 tknotsv = MALLOC(float *, (nv+2) *sizeof(GLfloat));
1524 generateUniformKnotVector(2,nu,tknotsu);
1525 generateUniformKnotVector(2,nv,tknotsv);
1526 printf("tknotsu = [");
1527 for(k=0;k<nu+2;k++) printf("%f ",tknotsu[k]);
1528 printf("]\ntknotsv = [");
1529 for(k=0;k<nv+2;k++) printf("%f ",tknotsv[k]);
1530 printf("]\n");
1531 gluNurbsSurface(theNurb,nu+2,knotsu,nv+2,knotsv,4,4*nu,control2D,2,2,GL_MAP2_TEXTURE_COORD_2);
1532 }
1533 } else if(texCoordNode->_nodeType == NODE_NurbsTextureCoordinate){
1534 //TEXCOORDTYPE = 2 /interpret nurbsurftexcoordcb coords as uv coords,
1535 // use to lookup st via piegl interpolation of NurbsTextureCoordinate nurbs surface
1536 if(0){
1537 //H3a:
1538 //- set texture surface control to 0 0 1 1
1539 //- use linear order 2
1540 float tknots[4] = {0.0f, 0.0f, 1.0f, 1.0f};
1541 float unit_control2D [8] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
1542 struct X3D_NurbsTextureCoordinate *texCoord = (struct X3D_NurbsTextureCoordinate *)texCoordNode;
1543 gluNurbsSurface(theNurb,4,tknots,4,tknots,2,2*2,unit_control2D,2,2,GL_MAP2_TEXTURE_COORD_2);
1544 }else{
1545 //H3b same as H2a or H2b
1546 // H2b: - compute defaults using equal spacing
1547 {
1548 float du, dv, uu, vv;
1549 int jj,j,k;
1550 float *control2D;
1551 struct X3D_TextureCoordinate *texCoord = createNewX3DNode0(NODE_TextureCoordinate);
1552 texcoordnodeIsGenerated = TRUE;
1553 FREE_IF_NZ(texCoord->point.p);
1554 texCoord->point.p = MALLOC(struct SFVec2f*,nu * nv * sizeof(struct SFVec2f));
1555 du = 1.0f / (float)max(1,(nu -1));
1556 dv = 1.0f / (float)max(1,(nv -1));
1557 vv = 0.0f;
1558 jj = 0;
1559 for(k=0;k<nv;k++){
1560 if(k == nv-1) vv = 1.0f; //they like end exact on 1.0f
1561 uu = 0.0f;
1562 for(j=0;j<nu;j++){
1563 if(j == nu-1) uu = 1.0f; //they like end exact on 1.0f
1564 texCoord->point.p[jj].c[0] = uu;
1565 texCoord->point.p[jj].c[1] = vv;
1566 uu += du;
1567 jj++;
1568 }
1569 vv += dv;
1570 }
1571 texCoord->point.n = jj;
1572 texCoordNode = X3D_NODE(texCoord);
1573 //H1a: use same order and knot vector as controlPoints
1574 // except using texcoord.point as nurbscontrol
1575 //CONFIRMED when using H2b 'b' above to generate missing TexCoord node
1576 control2D = (float*)texCoord->point.p;
1577 gluNurbsSurface(theNurb,nku,knotsu,nkv,knotsv,2,2*nu,control2D,node->uOrder,node->vOrder,GL_MAP2_TEXTURE_COORD_2);
1578
1579 }
1580
1581 }
1582
1583 }
1584 }
1585 if(trim){
1586 int i;
1587
1588 if(0){
1589 if(DEBG) printf("gluBeginTrim \n");
1590 gluBeginTrim (theNurb);
1591 //outside border H: scene author is responsible
1592 if(1){
1593 // counter clockwise, simple 4 corner uv from redbook sample
1594 GLfloat edgePt[5][2] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}, {0.0, 0.0}};
1595 if(DEBG) printf("gluPwlCurve 0\n");
1596 gluPwlCurve (theNurb, 5, &edgePt[0][0], 2, GLU_MAP1_TRIM_2);
1597 }else{
1598 // 2 x (node.utessselation u + node.vtesselation v) + 1 edge values
1599 // except I get a nurbs error with the following
1600 GLfloat *edges = MALLOC(void *, (2 * ntessu + 2 * ntessv) *2*sizeof(GLfloat));
1601 GLfloat uspan, vspan;
1602 uspan = 1.0f/(float)(ntessu -1);
1603 vspan = 1.0f/(float)(ntessv -1);
1604 for(i=0;i<ntessu-1;i++){
1605 edges[i*2 +0] = (float)(i)*uspan;
1606 edges[i*2 +1] = 0.0;
1607 edges[(ntessu+ntessv+i)*2 + 0] = (float)(ntessu - 1 - i)*uspan;
1608 edges[(ntessu+ntessv+i)*2 + 1] = 1.0;
1609 }
1610 for(i=0;i<ntessv;i++){
1611 edges[(ntessu+i)*2 + 0] = 1.0;
1612 edges[(ntessu+i)*2 + 1] = (float)(i)*vspan;
1613 edges[(ntessu+ntessv+ntessu+i)*2 + 0] = 0.0;
1614 edges[(ntessu+ntessv+ntessu+i)*2 + 1] = (float)(ntessv - 1 - i)*vspan;
1615 }
1616 //close curve
1617 edges[((ntessu -1)*2 + (ntessv -1)*2)*2 + 0] = 0.0;
1618 edges[((ntessu -1)*2 + (ntessv -1)*2)*2 + 1] = 0.0;
1619 if(DEBG) printf("gluPwlCurve 1\n");
1620 gluPwlCurve (theNurb, 2*(ntessu -1 + ntessv -1) +1, edges, 2, GLU_MAP1_TRIM_2);
1621 }
1622 if(DEBG) printf("gluEndTrim\n");
1623 gluEndTrim (theNurb);
1624 }
1625
1626 //interior cutouts
1627 if(0){
1628 //redbook example trim curves - these work
1629 GLfloat curvePt[4][2] = /* clockwise */
1630 {{0.25, 0.5}, {0.25, 0.75}, {0.75, 0.75}, {0.75, 0.5}};
1631 GLfloat curveKnots[8] =
1632 {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0};
1633 GLfloat pwlPt[4][2] = /* clockwise */
1634 {{0.75, 0.5}, {0.5, 0.25}, {0.25, 0.5}};
1635
1636 if(DEBG) printf("gluBeginTrim A\n");
1637 gluBeginTrim (theNurb);
1638 if(DEBG) printf("gluNurbsCurve A\n");
1639 gluNurbsCurve (theNurb, 8, curveKnots, 2,
1640 &curvePt[0][0], 4, GLU_MAP1_TRIM_2);
1641 if(DEBG) printf("gluPwlCurve A\n");
1642 gluPwlCurve (theNurb, 3, &pwlPt[0][0], 2, GLU_MAP1_TRIM_2);
1643 if(DEBG) printf("gluEndTrim A\n");
1644 gluEndTrim (theNurb);
1645 }
1646 if(1)
1647 for(i=0;i<trim->n;i++){
1648 int m;
1649 struct X3D_Contour2D * tc = (struct X3D_Contour2D *)trim->p[i];
1650 if(DEBG) printf("gluBeginTrim B\n");
1651 gluBeginTrim (theNurb);
1652 for(m=0;m<tc->children.n;m++)
1653 {
1654 int j,k,nk,dim;
1655 struct X3D_ContourPolyline2D *cp2d;
1656 struct X3D_NurbsCurve2D *nc2d;
1657 struct X3D_Node *ctr = tc->children.p[m]; //trim->p[i];
1658 GLfloat *cknot, *ctrl, *cweight;
1659 cknot = ctrl = cweight = NULL;
1660 switch(ctr->_nodeType){
1661 case NODE_ContourPolyline2D:
1662 cp2d = (struct X3D_ContourPolyline2D *)ctr;
1663 ctrl = MALLOC(void *, cp2d->controlPoint.n * 2*sizeof(GLfloat));
1664 for(j=0;j<cp2d->controlPoint.n;j++) {
1665 for(k=0;k<2;k++)
1666 ctrl[j*2 + k] = (float)cp2d->controlPoint.p[j].c[k];
1667 }
1668 if(DEBG) printf("gluPwlCurve B\n");
1669 gluPwlCurve (theNurb, cp2d->controlPoint.n, ctrl, 2, GLU_MAP1_TRIM_2);
1670 FREE_IF_NZ(ctrl);
1671 break;
1672 case NODE_NurbsCurve2D:
1673 nc2d = (struct X3D_NurbsCurve2D *)ctr;
1674 dim = 2;
1675 nk = nc2d->controlPoint.n + nc2d->order;
1676 if(nk == nc2d->knot.n)
1677
1678 cknot = MALLOC(void *, nk * sizeof(GLfloat));
1679 if(nc2d->weight.n){ // == 3){
1680 dim = 3;
1681 cweight = MALLOC(void *, nc2d->controlPoint.n * sizeof(GLfloat));
1682 if(nc2d->weight.n == nc2d->controlPoint.n){
1683 for(j=0;j<nc2d->weight.n;j++) cweight[j] = (float)nc2d->weight.p[j];
1684 }else{
1685 for(j=0;j<nc2d->controlPoint.n;j++) cweight[j] = 1.0f;
1686 }
1687 }
1688 ctrl = MALLOC(void *, nc2d->controlPoint.n * dim*sizeof(GLfloat));
1689 for(j=0;j<nc2d->controlPoint.n;j++) {
1690 for(k=0;k<2;k++){
1691 ctrl[j*dim + k] = (float)nc2d->controlPoint.p[j].c[k];
1692 if(dim == 3) ctrl[j*dim + k] *= cweight[j];
1693 }
1694 if(dim == 3) ctrl[j*dim + dim-1] = (float)cweight[j];
1695 }
1696 if(knotsOK(nc2d->order,nc2d->controlPoint.n,nc2d->knot.n,nc2d->knot.p)){
1697 //if(nk == nc2d->knot.n){
1698 for(j=0;j<nc2d->knot.n;j++)
1699 cknot[j] = (float)nc2d->knot.p[j];
1700 }else{
1701 generateUniformKnotVector(nc2d->order,nc2d->controlPoint.n,cknot);
1702 printf("replacing nurbscurve2D knotvector with:\n");
1703 for(j=0;j<nk;j++){
1704 printf("%f ",cknot[j]);
1705 }
1706 printf("\n");
1707
1708 }
1709 if(DEBGC) {
1710 printf("knot %d ={",nc2d->knot.n);
1711 for(j=0;j<nc2d->knot.n;j++){
1712 printf("%f ",cknot[j]);
1713 }
1714 printf("}\n");
1715 printf("control %d = {\n",nc2d->controlPoint.n);
1716 for(j=0;j<nc2d->controlPoint.n;j++) {
1717 for(k=0;k<dim;k++) printf("%f \n",ctrl[j*dim +k]);
1718 printf("\n");
1719 }
1720 printf("}\n");
1721 }
1722 if(DEBG) printf("gluNurbsCurve B\n");
1723 if(1){
1724 int mtess, ntess;
1725 mtess = nc2d->order + 1;
1726 ntess = nc2d->tessellation;
1727 if(0){
1728 //chord length or automatic - not implemented properly nor tested thoroughly
1729 //if you do chord length in pixels, you need to manually pass in sampling matrices
1730 //and somehow you need to trigger a recompile: another call to this compile_
1731 // as avatar/viewer moves closer (father) from the nurbs node
1732 double model[16], proj[16];
1733 float modelf[16], projf[16];
1734 int viewPort[10];
1735 if(ntess > 0)
1736 mtess = ntess;
1737 else if(ntess < 0)
1738 mtess = -ntess;
1739
1740 gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, (float)(mtess)); //25.0);
1741 if(ntess < 0)
1742 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_PATH_LENGTH); //pixels, the default
1743 else
1744 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_PARAMETRIC_TOLERANCE);
1745 gluNurbsProperty(theNurb, GLU_AUTO_LOAD_MATRIX,GL_FALSE);
1746
1747 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, model);
1748 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
1749 FW_GL_GETINTEGERV(GL_VIEWPORT, viewPort);
1750 for(i=0;i<16;i++){
1751 modelf[i] = (float)model[i];
1752 projf[i] = (float)proj[i];
1753 }
1754 gluLoadSamplingMatrices(theNurb,modelf,projf,viewPort);
1755 }
1756 if(1){
1757 //uniform spacing of sampling points in u (or uv) parameter space - works
1758 //node must specify tesselation value. see specs for interpretation
1759 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/nurbs.html#NurbsCurve
1760 if(ntess > 0)
1761 mtess = max(mtess,ntess+1);
1762 else if(ntess < 0)
1763 mtess = max(mtess,(-ntess * n) + 1);
1764 else
1765 mtess = max(mtess,2*n + 1);
1766 gluNurbsProperty(theNurb,GLU_SAMPLING_METHOD,GLU_DOMAIN_DISTANCE);
1767 gluNurbsProperty(theNurb,GLU_U_STEP,(GLfloat)mtess);
1768 }
1769 }
1770 gluNurbsCurve (theNurb, nk, cknot, dim, ctrl, nc2d->order, GLU_MAP1_TRIM_2);
1771 break;
1772 default:
1773 ConsoleMessage("%s %d","unknown trimming contour node",ctr->_nodeType);
1774 }
1775 FREE_IF_NZ(ctrl);
1776 FREE_IF_NZ(cknot);
1777 FREE_IF_NZ(cweight);
1778 }
1779 if(DEBG) printf("gluEndTrim B\n");
1780 gluEndTrim (theNurb);
1781 }
1782 }
1783 if(DEBG) printf("gluEndSurface \n");
1784 gluEndSurface(theNurb);
1785 if(DEBG) printf("gluDeleteNurbsRenderer \n");
1786 if(0) gluDeleteNurbsRenderer(theNurb);
1787
1788 //convert points to polyrep
1789 convert_strips_to_polyrep(strips,(struct X3D_NurbsTrimmedSurface*)node);
1790
1791 if(texcoordnodeIsGenerated){
1792 struct X3D_TextureCoordinate *texCoord = (struct X3D_TextureCoordinate *)texCoordNode;
1793 FREE_IF_NZ(texCoord->point.p);
1794 FREE_IF_NZ(texCoordNode);
1795 }
1796 //vector_releaseData(,strips);
1797 if(strips){
1798 for(i=0;i<vectorSize(strips);i++){
1799 struct stripState *ss = vector_get_ptr(struct stripState,strips,i);
1800 FREE_IF_NZ(ss->pv.data); ss->pv.allocn = 0; ss->pv.n = 0;
1801 FREE_IF_NZ(ss->nv.data); ss->nv.allocn = 0; ss->nv.n = 0;
1802 FREE_IF_NZ(ss->tv.data); ss->tv.allocn = 0; ss->tv.n = 0;
1803 }
1804 FREE_IF_NZ(strips->data); strips->allocn = 0; strips->n = 0;
1805 FREE_IF_NZ(strips);
1806 }
1807 FREE_IF_NZ(xyzw);
1808 }
1809 FREE_IF_NZ(knotsv);
1810 FREE_IF_NZ(knotsu);
1811 }
1812
1813}
1814
1815void render_ray_polyrep(void *node);
1816void render_polyrep(void *node);
1817
1818void compile_NurbsPatchSurface(struct X3D_NurbsPatchSurface *node){
1819 MARK_NODE_COMPILED
1820 compile_NurbsSurface(node, NULL);
1821}
1822void rendray_NurbsPatchSurface (struct X3D_NurbsPatchSurface *node) {
1823 COMPILE_IF_REQUIRED
1824 if (!node->_intern) return;
1825 render_ray_polyrep(node);
1826}
1827
1828void collide_NurbsPatchSurface (struct X3D_NurbsPatchSurface *node) {
1829 COMPILE_IF_REQUIRED
1830 if (!node->_intern) return;
1831 collide_genericfaceset(X3D_INDEXEDFACESET(node));
1832}
1833
1834void render_NurbsPatchSurface (struct X3D_NurbsPatchSurface *node) {
1835 COMPILE_IF_REQUIRED
1836 if (!node->_intern) return;
1837 CULL_FACE(node->solid)
1838 render_polyrep(node);
1839}
1840
1841void compile_NurbsTrimmedSurface(struct X3D_NurbsTrimmedSurface *node){
1842 MARK_NODE_COMPILED
1843 compile_NurbsSurface((struct X3D_NurbsPatchSurface *)node, &node->trimmingContour);
1844}
1845
1846void rendray_NurbsTrimmedSurface (struct X3D_NurbsTrimmedSurface *node) {
1847 COMPILE_IF_REQUIRED
1848 if (!node->_intern) return;
1849 render_ray_polyrep(node);
1850}
1851
1852void collide_NurbsTrimmedSurface (struct X3D_NurbsTrimmedSurface *node) {
1853 COMPILE_IF_REQUIRED
1854 if (!node->_intern) return;
1855 collide_genericfaceset(X3D_INDEXEDFACESET(node));
1856}
1857
1858void render_NurbsTrimmedSurface (struct X3D_NurbsTrimmedSurface *node) {
1859 COMPILE_IF_REQUIRED
1860 if (!node->_intern) return;
1861 CULL_FACE(node->solid)
1862 render_polyrep(node);
1863}
1864
1865void do_NurbsPositionInterpolator (void *node) {
1867 struct X3D_Coordinate *control;
1868 float fraction; //knotrange[2],
1869 double *weight;
1870 float cw[4]; //*xyzw,
1871
1872 if (!node) return;
1873 px = (struct X3D_NurbsPositionInterpolator *) PPX(node);
1874 if(NNC(px)){
1875 float *knots;
1876 int i,nk, n;
1877
1878 MNC(px);
1879 if(!px->_OK == TRUE){
1880 px->_OK = FALSE;
1881 if(!(px->knot.n > 1))
1882 return;
1883 if(!px->controlPoint )
1884 return;
1885 if(px->controlPoint->_nodeType != NODE_Coordinate)
1886 return;
1887 control = (struct X3D_Coordinate *) px->controlPoint;
1888 if(control->point.n < 2)
1889 return;
1890 n = control->point.n;
1891 weight = NULL;
1892 if(px->weight.n == n)
1893 weight = px->weight.p;
1894 px->_xyzw.p = MALLOC(struct SFVec4f*,control->point.n * sizeof(struct SFVec4f));
1895 px->_xyzw.n = n;
1896 for(i=0;i<control->point.n;i++){
1897 float xyzw[4], wt;
1898 wt = weight ? (float)weight[i] : 1.0f;
1899 veccopy3f(xyzw,control->point.p[i].c);
1900 vecscale3f(xyzw,xyzw,wt);
1901 xyzw[3] = wt;
1902 veccopy4f(px->_xyzw.p[i].c,xyzw);
1903 }
1904 if(knotsOK(px->order,px->_xyzw.n,px->knot.n,px->knot.p)){
1905
1906 nk = px->knot.n;
1907 knots = MALLOC(void *, nk * sizeof(GLfloat));
1908 for(i=0;i<nk;i++){
1909 knots[i] = (GLfloat)px->knot.p[i];
1910 }
1911 //printf("good knot nk=%d\n",nk);
1912 //for(int ii=0;ii<nk;ii++)
1913 // printf("[%d]=%f \n",ii,knots[ii]);
1914
1915 }else{
1916 static int once = 0;
1917 //generate uniform knot vector
1918 nk = n + px->order ;
1919 //caller: please malloc knots = malloc( (ncontrol + order ) * sizeof(float))
1920 knots = MALLOC(void *, nk *sizeof(GLfloat));
1921 generateUniformKnotVector(px->order,n, knots);
1922 if(!once){
1923 int ii;
1924 printf("bad knot vector, replacing with:\n");
1925 for(ii=0;ii<nk;ii++)
1926 printf("[%d]=%f \n",ii,knots[ii]);
1927 once = 1;
1928 }
1929 //nk = 0;
1930 }
1931 px->_knot.p = knots;
1932 px->_knot.n = nk;
1933 px->_knotrange.c[0] = px->_knot.p[0];
1934 px->_knotrange.c[1] = px->_knot.p[px->_knot.n-1];
1935
1936 px->_OK = TRUE;
1937 }
1938 }
1939 if(!px->_OK)
1940 return;
1941
1942 fraction = max(px->_knotrange.c[0],px->set_fraction);
1943 fraction = min(px->_knotrange.c[1],px->set_fraction);
1944 CurvePoint(px->_xyzw.n, px->order-1, px->_knot.p, (float*)px->_xyzw.p, fraction, cw );
1945 veccopy3f(px->value_changed.c,cw);
1946 MARK_EVENT (node, offsetof (struct X3D_NurbsPositionInterpolator, value_changed));
1947
1948 #ifdef SEVERBOSE
1949 printf("do_PositionInt: Position/Vec3f interp, node %u kin %d kvin %d set_fraction %f\n",
1950 node, kin, kvin, px->set_fraction);
1951 #endif
1952
1953
1954
1955}
1956
1957/* NurbsOrientationInterpolator
1958 Called during the "events_processed" section of the event loop,
1959 so this is called ONLY when there is something required to do, thus
1960 there is no need to look at whether it is active or not
1961 */
1962void do_NurbsOrientationInterpolator (void *node) {
1964 struct X3D_Coordinate *control;
1965 float fraction; //knotrange[2],
1966 double *weight;
1967 //float *xyzw; //, cw[4];
1968
1969 if (!node) return;
1970 px = (struct X3D_NurbsOrientationInterpolator *) PPX(node);
1971 if(NNC(px)){
1972 float *knots;
1973 int i,nk, n;
1974
1975 MNC(px);
1976 if(!px->_OK == TRUE){
1977 px->_OK = FALSE;
1978 if(!(px->knot.n > 1))
1979 return;
1980 if(!px->controlPoint )
1981 return;
1982 if(px->controlPoint->_nodeType != NODE_Coordinate)
1983 return;
1984 control = (struct X3D_Coordinate *) px->controlPoint;
1985 if(control->point.n < 2)
1986 return;
1987 n = control->point.n;
1988 weight = NULL;
1989 if(px->weight.n == n)
1990 weight = px->weight.p;
1991 px->_xyzw.p = MALLOC(struct SFVec4f*,control->point.n * sizeof(struct SFVec4f));
1992 px->_xyzw.n = n;
1993 for(i=0;i<control->point.n;i++){
1994 float xyzw[4], wt;
1995 wt = weight ? (float)weight[i] : 1.0f;
1996 veccopy3f(xyzw,control->point.p[i].c);
1997 vecscale3f(xyzw,xyzw,wt);
1998 xyzw[3] = wt;
1999 veccopy4f(px->_xyzw.p[i].c,xyzw);
2000 }
2001 if(knotsOK(px->order,px->_xyzw.n,px->knot.n,px->knot.p)){
2002
2003 nk = px->knot.n;
2004 knots = MALLOC(void *, nk * sizeof(GLfloat));
2005 for(i=0;i<nk;i++){
2006 knots[i] = (GLfloat)px->knot.p[i];
2007 }
2008 //printf("good knot nk=%d\n",nk);
2009 //for(int ii=0;ii<nk;ii++)
2010 // printf("[%d]=%f \n",ii,knots[ii]);
2011
2012 }else{
2013 static int once = 0;
2014 //generate uniform knot vector
2015 nk = n + px->order ;
2016 //caller: please malloc knots = malloc( (ncontrol + order ) * sizeof(float))
2017 knots = MALLOC(void *, nk *sizeof(GLfloat));
2018 generateUniformKnotVector(px->order,n, knots);
2019 if(!once){
2020 int ii;
2021 printf("bad knot vector, replacing with:\n");
2022 for(ii=0;ii<nk;ii++)
2023 printf("[%d]=%f \n",ii,knots[ii]);
2024 once = 1;
2025 }
2026 //nk = 0;
2027 }
2028 px->_knot.p = knots;
2029 px->_knot.n = nk;
2030 px->_knotrange.c[0] = px->_knot.p[0];
2031 px->_knotrange.c[1] = px->_knot.p[px->_knot.n-1];
2032
2033 px->_OK = TRUE;
2034 }
2035 }
2036 if(!px->_OK)
2037 return;
2038
2039 fraction = max(px->_knotrange.c[0],px->set_fraction);
2040 fraction = min(px->_knotrange.c[1],px->set_fraction);
2041 if(1){
2042 //DELTA METHOD: instead of using a piegl formula for curve derivitive,
2043 //we sample 2 points near the u of interest, and take the difference
2044 //to get a slope vector
2045 float f1, f2, cw1[4], cw2[4], dir[3], rot[4];
2046
2047
2048 f1 = fraction;
2049 f2 = fraction + .01f;
2050 if(f2 > 1.0f){
2051 f1 = fraction - .01f;
2052 f2 = fraction;
2053 }
2054
2055 CurvePoint(px->_xyzw.n, px->order-1, px->_knot.p, (float*)px->_xyzw.p, f1, cw1 );
2056 CurvePoint(px->_xyzw.n, px->order-1, px->_knot.p, (float*)px->_xyzw.p, f2, cw2 );
2057 vecdif3f(dir,cw2,cw1);
2058 vecnormalize3f(dir,dir);
2059
2060 if(1){
2061 //1-direction vector relative to x-axis
2062 //now have 2 direction vectors - one at start, one at end
2063 //do a difference to get relative rotation from start
2064 float perp[3], dirx[3], sine;
2065 memset(dirx,0,3*sizeof(float));
2066 dirx[0] = 1.0f;
2067 veccross3f(perp,dirx,dir);
2068 sine = veclength3f(perp);
2069 if(sine == 0.0f){
2070 //no net rotation from start
2071 float default_direction [4] = {1.0f, 0.0f, 0.0f, 0.0f};
2072 veccopy4f(rot,default_direction);
2073 }else{
2074 float cosine, angle;
2075 vecnormalize3f(perp,perp);
2076 cosine = vecdot3f(dir,dirx);
2077 angle = atan2f(sine,cosine);
2078 veccopy3f(rot,perp);
2079 rot[3] = angle;
2080 }
2081 }else if(0){
2082 //2-direction vector to axis angle method
2083 //now have 2 direction vectors - one at start, one at end
2084 //do a difference to get relative rotation from start
2085 float perp[3],dir0[3];
2086
2087 CurvePoint(px->_xyzw.n, px->order-1, px->_knot.p, (float*)px->_xyzw.p, 0.0f, cw1 );
2088 CurvePoint(px->_xyzw.n, px->order-1, px->_knot.p, (float*)px->_xyzw.p, .001f, cw2 );
2089 vecdif3f(dir0,cw2,cw1);
2090 vecnormalize3f(dir0,dir0);
2091
2092 veccross3f(perp,dir,dir0);
2093 if(veclength3f(perp) == 0.0f){
2094 //no net rotation from start
2095 float default_direction [4] = {1.0f, 0.0f, 0.0f, 0.0f};
2096 veccopy4f(rot,default_direction);
2097 printf(".");
2098 }else{
2099 float cosine,angle;
2100 vecnormalize3f(perp,perp);
2101 cosine = vecdot3f(dir0,dir);
2102 angle = acosf(cosine);
2103 veccopy3f(rot,perp);
2104 rot[3] = -angle;
2105 }
2106 }
2107 veccopy4f(px->value_changed.c,rot);
2108 }else{
2109 float default_direction [4] = {1.0f, 0.0f, 0.0f, 0.0f};
2110 veccopy4f(px->value_changed.c,default_direction);
2111 }
2112 MARK_EVENT (node, offsetof (struct X3D_NurbsOrientationInterpolator, value_changed));
2113
2114 #ifdef SEVERBOSE
2115 printf("do_NurbsOrientInt: set_fraction %f value_changed %f %f %f %f\n",fraction,
2116 px->value_changed.c[0],px->value_changed.c[1],px->value_changed.c[2],px->value_changed.c[3] );
2117 #endif
2118
2119}
2120
2121void do_NurbsSurfaceInterpolator (void *_node) {
2122 float uv[2], xyzw[4];
2123 int ok;
2124 struct X3D_NurbsSurfaceInterpolator *node;
2125
2126 if (!_node) return;
2127 node = (struct X3D_NurbsSurfaceInterpolator *) _node;
2128 if(node->_nodeType != NODE_NurbsSurfaceInterpolator) return;
2129
2130 if(NNC(node)){
2131 MNC(node);
2132 if(node->_OK != TRUE){
2133 int i,j, n, nu, nv, nku, nkv;
2134 GLfloat *xyzw, *knotsu, *knotsv;
2135
2136 nku = nkv = nu = nv = n = 0;
2137 xyzw = knotsu = knotsv = NULL;
2138 if(node->controlPoint){
2139 if(node->controlPoint->_nodeType == NODE_CoordinateDouble){
2140 struct Multi_Vec3d *mfd;
2141 mfd = &((struct X3D_CoordinateDouble *)(node->controlPoint))->point;
2142 n = mfd->n;
2143 xyzw = MALLOC(void *, n * 4 * sizeof(GLfloat));
2144 for(i=0;i<mfd->n;i++){
2145 for(j=0;j<3;j++){
2146 xyzw[i*4 + j] = (float)mfd->p[i].c[j];
2147 }
2148 }
2149 }else if(node->controlPoint->_nodeType == NODE_Coordinate){
2150 struct Multi_Vec3f *mff;
2151 mff = &((struct X3D_Coordinate *)(node->controlPoint))->point;
2152 n = mff->n;
2153 xyzw = MALLOC(void *, n * 4 * sizeof(GLfloat));
2154 for(i=0;i<mff->n;i++){
2155 for(j=0;j<3;j++){
2156 xyzw[i*4 + j] = mff->p[i].c[j];
2157 }
2158 }
2159 }
2160 }else{
2161 n = 0;
2162 }
2163
2164 if(node->weight.n && node->weight.n == n){
2165 double w;
2166 int m,im;
2167 m = min(node->weight.n, n);
2168 for(i=0;i<n;i++){
2169 im = i < m ? i : m-1;
2170 w = node->weight.p[im];
2171 xyzw[i*4 + 3] = (float)w;
2172 }
2173 }else{
2174 for(i=0;i<n;i++) xyzw[i*4 + 3] = 1.0;
2175 }
2176 nu = node->uDimension;
2177 nv = node->vDimension;
2178 if(nu * nv != n) return;
2179 if(nu < node->uOrder) return;
2180 if(nv < node->vOrder) return;
2181 //int knotsOK(int order, int ncontrol, int nknots, double *knots)
2182 //if(node->uKnot.n && node->uKnot.n == nu + node->uOrder ){
2183 if(knotsOK(node->uOrder,nu,node->uKnot.n,node->uKnot.p)){
2184 //could do another check: max number of consecutive equal value knots == order
2185 //could do another check: knot values == or ascending
2186 nku = node->uKnot.n;
2187 knotsu = MALLOC(void *, nku * sizeof(GLfloat));
2188 for(i=0;i<nku;i++){
2189 knotsu[i] = (GLfloat)node->uKnot.p[i];
2190 }
2191 if(DEBG){
2192 int ii;
2193 printf("good u knot vector nk=%d\n",nku);
2194 for(ii=0;ii<nku;ii++)
2195 printf("[%d]=%f \n",ii,knotsu[ii]);
2196 }
2197
2198 }else{
2199 //generate uniform knot vector
2200 static int once = 0;
2201 nku = nu + node->uOrder ;
2202 //caller: please malloc knots = MALLOC(void *, (ncontrol + order ) * sizeof(float))
2203 knotsu = MALLOC(void *, nku *sizeof(GLfloat));
2204 generateUniformKnotVector(node->uOrder,nu, knotsu);
2205 if(!once){
2206 int ii;
2207 printf("bad u knot vector given, replacing with:\n");
2208 for(ii=0;ii<nku;ii++)
2209 printf("[%d]=%f \n",ii,knotsu[ii]);
2210 once = 1;
2211 }
2212 //nk = 0;
2213 }
2214
2215 if(knotsOK(node->vOrder,nv,node->vKnot.n,node->vKnot.p)){
2216 //if(node->vKnot.n && node->vKnot.n == nv + node->vOrder ){
2217 nkv = node->vKnot.n;
2218 knotsv = MALLOC(void *, nkv * sizeof(GLfloat));
2219 for(i=0;i<nkv;i++){
2220 knotsv[i] = (GLfloat)node->vKnot.p[i];
2221 }
2222 if(DEBG){
2223 int ii;
2224 printf("good v knot vector nk=%d\n",nkv);
2225 for(ii=0;ii<nkv;ii++)
2226 printf("[%d]=%f \n",ii,knotsv[ii]);
2227 }
2228
2229 }else{
2230 static int once = 0;
2231 //generate uniform knot vector
2232 nkv = nv + node->vOrder ;
2233 //caller: please malloc knots = MALLOC(void *, (ncontrol + order ) * sizeof(float))
2234 knotsv = MALLOC(void *, nkv *sizeof(GLfloat));
2235 generateUniformKnotVector(node->vOrder,nv, knotsv);
2236 if(!once){
2237 int ii;
2238 printf("bad v knot vector given, replacing with:\n");
2239 for(ii=0;ii<nkv;ii++)
2240 printf("[%d]=%f \n",ii,knotsv[ii]);
2241 once = 1;
2242 }
2243 //nk = 0;
2244 }
2245 node->_controlPoint.p = (struct SFVec4f*)xyzw;
2246 node->_controlPoint.n = n;
2247 node->_uKnot.p = knotsu;
2248 node->_uKnot.n = nku;
2249 node->_vKnot.p = knotsv;
2250 node->_vKnot.n = nkv;
2251 node->_OK = TRUE;
2252 }
2253
2254 }
2255
2256 if(!node->_OK) return;
2257
2258 veccopy2f(uv,node->set_fraction.c);
2259
2260 ok = SurfacePoint(node->uDimension,node->uOrder-1,node->_uKnot.p,
2261 node->vDimension,node->vOrder-1,node->_vKnot.p,
2262 (float *)node->_controlPoint.p,uv[1],uv[0],xyzw);
2263
2264 veccopy3f(node->position_changed.c,xyzw);
2265 if(1){
2266 //DELTA method to get normal
2267 //u direction
2268 float udir[3], vdir[3], normal[3], xyz1[4];
2269 ok = SurfacePoint(node->uDimension,node->uOrder-1,node->_uKnot.p,
2270 node->vDimension,node->vOrder-1,node->_vKnot.p,
2271 (float *)node->_controlPoint.p,uv[1],uv[0]+.01f,xyz1);
2272 vecdif3f(udir,xyz1,xyzw);
2273 ok = SurfacePoint(node->uDimension,node->uOrder-1,node->_uKnot.p,
2274 node->vDimension,node->vOrder-1,node->_vKnot.p,
2275 (float *)node->_controlPoint.p,uv[1]+.01f,uv[0],xyz1);
2276 vecdif3f(vdir,xyz1,xyzw);
2277 veccross3f(normal,udir,vdir);
2278 vecnormalize3f(normal,normal);
2279 veccopy3f(node->normal_changed.c,normal);
2280
2281
2282 }
2283
2284 MARK_EVENT (_node, offsetof (struct X3D_NurbsSurfaceInterpolator, position_changed));
2285 MARK_EVENT (_node, offsetof (struct X3D_NurbsSurfaceInterpolator, normal_changed));
2286
2287 #ifdef SEVERBOSE
2288 printf ("Pos/Col, new value (%f %f %f)\n",
2289 px->value_changed.c[0],px->value_changed.c[1],px->value_changed.c[2]);
2290 #endif
2291
2292}
2293
2294//SWUNG
2295
2296void compile_NurbsSwungSurface(struct X3D_NurbsSwungSurface *node){
2297 struct X3D_NurbsPatchSurface *patch;
2298 struct X3D_Coordinate *controlPoint;
2299 struct X3D_NurbsCurve2D *trajectoryxz;
2300 struct X3D_NurbsCurve2D *profileyz;
2301 int nt, np,ic,j,i,k;
2302 double *xyzp, *xyzt;
2303 float *xyz;
2304
2305 MARK_NODE_COMPILED
2306 //strategy: generate 3D control net from curves,
2307 // then delegate to NurbsPatchSurface
2308 //Swung:
2309 patch = (struct X3D_NurbsPatchSurface*) node->_patch;
2310 if(!patch){
2311 patch = (struct X3D_NurbsPatchSurface*)createNewX3DNode(NODE_NurbsPatchSurface);
2312 controlPoint = (struct X3D_Coordinate*)createNewX3DNode(NODE_Coordinate);
2313 node->_patch = X3D_NODE(patch);
2314 patch->controlPoint = X3D_NODE(controlPoint);
2315 }else{
2316 controlPoint = (struct X3D_Coordinate*)patch->controlPoint;
2317 }
2318
2319 trajectoryxz = (struct X3D_NurbsCurve2D *)node->trajectoryCurve;
2320 profileyz = (struct X3D_NurbsCurve2D *)node->profileCurve;
2321
2322 nt = trajectoryxz->controlPoint.n;
2323 np = profileyz->controlPoint.n;
2324 xyzp = (double*)profileyz->controlPoint.p;
2325 xyzt = (double*)trajectoryxz->controlPoint.p;
2326 xyz = MALLOC(float*,nt * np * 3 * sizeof(float));
2327 controlPoint->point.p = (struct SFVec3f*)xyz;
2328 controlPoint->point.n = nt * np;
2329 ic = 0;
2330 for(j=0;j<nt;j++){
2331 float pt[3];
2332 double2float(pt,&xyzt[j*2],2);
2333 for(i=0;i<np;i++){
2334 float pp[3];
2335 float cosine, sine, swingangle;
2336 double2float(pp,&xyzp[2*i],2);
2337 swingangle = atan2f(pt[1],pt[0]);
2338 cosine = cosf(swingangle);
2339 sine = sinf(swingangle);
2340 xyz[ic*3 + 0] = pt[0] + cosine * pp[0];
2341 xyz[ic*3 + 1] = pp[1];
2342 xyz[ic*3 + 2] = pt[1] + sine * pp[0];
2343 ic++;
2344 }
2345 }
2346 patch->solid = node->solid;
2347 //u will be profile,
2348 patch->uDimension = np;
2349 patch->uKnot.p = malloc(profileyz->knot.n * sizeof(double));
2350 memcpy(patch->uKnot.p,profileyz->knot.p,profileyz->knot.n * sizeof(double));
2351 patch->uKnot.n = profileyz->knot.n;
2352 patch->uOrder = profileyz->order;
2353 patch->uTessellation = (int)((float)profileyz->tessellation * profileyz->_tscale);
2354 //v will be trajectory
2355 patch->vDimension = nt;
2356 patch->vKnot.p = malloc(trajectoryxz->knot.n * sizeof(double));
2357 memcpy(patch->vKnot.p,trajectoryxz->knot.p,trajectoryxz->knot.n * sizeof(double));
2358 patch->vKnot.n = trajectoryxz->knot.n;
2359 patch->vOrder = trajectoryxz->order;
2360 patch->vTessellation = (int)((float)trajectoryxz->tessellation * profileyz->_tscale);
2361 if(0){
2362 int ic = 0;
2363 for(j=0;j<nt;j++){
2364 for(k=0;k<np;k++){
2365 printf("%f %f %f,",xyz[ic*3 + 0], xyz[ic*3 +1], xyz[ic*3 +2]);
2366 ic++;
2367 }
2368 printf("\n");
2369 }
2370 printf("uDimension=%d vDimension=%d nc=%d\n",np,nt,ic);
2371 }
2372 compile_NurbsPatchSurface((struct X3D_NurbsPatchSurface*)node->_patch);
2373}
2374void rendray_NurbsSwungSurface (struct X3D_NurbsSwungSurface *node) {
2375 COMPILE_IF_REQUIRED
2376 if (!node->_intern) return;
2377 render_ray_polyrep(node->_patch);
2378}
2379
2380void collide_NurbsSwungSurface (struct X3D_NurbsSwungSurface *node) {
2381 COMPILE_IF_REQUIRED
2382 if (!node->_intern) return;
2383 collide_genericfaceset(X3D_INDEXEDFACESET(node->_patch));
2384}
2385
2386void render_NurbsSwungSurface (struct X3D_NurbsSwungSurface *node) {
2387 struct X3D_NurbsPatchSurface *patch;
2388 COMPILE_IF_REQUIRED
2389 if (!node->_patch->_intern)
2390 return;
2391 patch = (struct X3D_NurbsPatchSurface*) node->_patch;
2392 CULL_FACE(patch->solid)
2393 render_polyrep(X3D_NODE(patch));
2394}
2395
2396
2397//SWEPT
2398int compute_tessellation(int tessellation, int order, int ncontrol ){
2399 /* for a given axis on a nurbs surface or nurbs curve,
2400 and given the node->tesselation value, order and nDimension or ncontrol for the axis
2401 return a computed tesselation value
2402 */
2403 int mtessu, ntessu; //mtessv, ntessv,
2404 mtessu = order + 1;
2405 ntessu = tessellation;
2406
2407 if(ntessu > 0)
2408 mtessu = max(mtessu,ntessu+1);
2409 else if(ntessu < 0)
2410 mtessu = max(mtessu,(-ntessu * ncontrol) + 1);
2411 else
2412 mtessu = max(mtessu,2*ncontrol + 1);
2413 return mtessu;
2414}
2415void compute_knotvector(int order, int ncontrol, int nknots, double *knots, int *newnknots, float **newknots, float *range){
2416 //for a given axis, QCs the knot vector, and replaces if necessary
2417 //will malloc the necessary newknot vector - caller responsible
2418 //range: pass in float[2] already allocated
2419 int nku,i,ii;
2420 float *knotsu;
2421 if(knotsOK(order,ncontrol,nknots,knots)){
2422 //could do another check: max number of consecutive equal value knots == order
2423 //could do another check: knot values == or ascending
2424 nku = nknots;
2425 knotsu = MALLOC(void *, nku * sizeof(GLfloat));
2426 for(i=0;i<nku;i++){
2427 knotsu[i] = (GLfloat)knots[i];
2428 }
2429 if(DEBG){
2430 printf("good u knot vector nk=%d\n",nku);
2431 for(ii=0;ii<nku;ii++)
2432 printf("[%d]=%f \n",ii,knotsu[ii]);
2433 }
2434 *newknots = knotsu;
2435 *newnknots = nku;
2436 }else{
2437 //generate uniform knot vector
2438 static int once = 0;
2439 nku = ncontrol + order ;
2440 //caller: please malloc knots = MALLOC(void *, (ncontrol + order ) * sizeof(float))
2441 knotsu = MALLOC(void *, nku *sizeof(GLfloat));
2442 generateUniformKnotVector(order,ncontrol, knotsu);
2443 if(!once){
2444 printf("bad u knot vector given, replacing with:\n");
2445 for(ii=0;ii<nku;ii++)
2446 printf("[%d]=%f \n",ii,knotsu[ii]);
2447 once = 1;
2448 }
2449 *newknots = knotsu;
2450 *newnknots= nku;
2451 //nk = 0;
2452 }
2453 range[0] = knotsu[0];
2454 range[1] = knotsu[nku-1];
2455}
2456void compute_weightedcontrol(double *xyz, int dim, int nc, int nweight, double *weights, float **cxyzw){
2457/* will malloc xyzw the right size, and return, caller responsible
2458 the opengl and piegl functions take floats
2459 dim = 3 for incoming xyz control
2460 dim = 2 for incoming xy (2D) control
2461 outgoing is [w*x,w*y,w*z,w]
2462*/
2463 int i,j;
2464 float *xyzw;
2465
2466 xyzw = MALLOC(float *, nc * 4 * sizeof(GLfloat));
2467 for(i=0;i<nc;i++)
2468 xyzw[i*4 +0] = xyzw[i*4 +1] =xyzw[i*4 +2] =xyzw[i*4 +3] = 0.0f;
2469 for(i=0;i<nc;i++){
2470 for(j=0;j<dim;j++){
2471 xyzw[i*4 + j] = (float)xyz[i*dim + j];
2472 }
2473 xyzw[i*4 + 3] = 1.0f;
2474 }
2475 if(nweight && nweight == nc ){
2476 for(i=0;i<nc;i++){
2477 float wt = (float)weights[i];
2478 xyzw[i*4 + 3] = wt;
2479 vecscale3f(&xyzw[i*4],&xyzw[i*4],wt);
2480 }
2481 }
2482 *cxyzw = xyzw;
2483}
2484void compute_doublecontrol(struct X3D_Node *controlPoint, int *nc, double** xyz ){
2485 /* rather than switch-casing elsewhere in the code, we'll get both types into double here
2486
2487 */
2488 int n,i;
2489 double *xyzd = NULL;
2490 n = 0;
2491 switch(controlPoint->_nodeType){
2492 case NODE_Coordinate:
2493 {
2494 struct X3D_Coordinate *tcoord = (struct X3D_Coordinate*) controlPoint;
2495 n = tcoord->point.n;
2496 xyzd = MALLOC(double *,n * 3 * sizeof(double));
2497 for(i=0;i<tcoord->point.n;i++)
2498 float2double(&xyzd[i*3],tcoord->point.p[i].c,3);
2499 }
2500 break;
2501 case NODE_CoordinateDouble:
2502 {
2503 struct X3D_CoordinateDouble *tcoord = (struct X3D_CoordinateDouble *)controlPoint;
2504 n = tcoord->point.n;
2505 xyzd = MALLOC(double *,n * 3 * sizeof(double));
2506 for(i=0;i<tcoord->point.n;i++)
2507 veccopyd(&xyzd[i*3],tcoord->point.p[i].c);
2508
2509 }
2510 break;
2511 default:
2512 break;
2513 }
2514 *nc = n;
2515 *xyz = xyzd;
2516
2517}
2518float *vecabs3f(float *res, float *p){
2519 int i;
2520 for(i=0;i<3;i++)
2521 res[i] = fabsf(p[i]);
2522 return res;
2523}
2524int ivecdominantdirection3f(int *irank, float *p){
2525 float rmax, rmin, vabs[3];
2526 int i,iret = 0;
2527 vecabs3f(vabs,p);
2528 rmax = max(vabs[0],max(vabs[1],vabs[2]));
2529 rmin = min(vabs[0],min(vabs[1],vabs[2]));
2530 for(i=0;i<3;i++){
2531 irank[i] = 1;
2532 if(vabs[i] == rmax) {
2533 irank[i] = 2;
2534 iret = i;
2535 }
2536 if(vabs[i] == rmin) irank[i] = 0;
2537 }
2538 return iret;
2539}
2540void convert_mesh_to_polyrep(float *xyz, int npts, float *nxyz, int* tindex, int ntri, struct X3D_Node *node){
2541 //this is a bit like compile_polyrep, except the virt_make is below
2542
2543 struct X3D_PolyRep *rep_, *polyrep;
2544 GLuint *norindex; //*cindex,
2545
2546 //from compile_polyrep:
2547 //node = X3D_NODE(innode);
2548 //virt = virtTable[node->_nodeType];
2549
2550 /* first time through; make the intern structure for this polyrep node */
2551 if(node->_intern){
2552 polyrep = (struct X3D_PolyRep*) node->_intern;
2553 FREE_IF_NZ(polyrep->cindex);
2554 FREE_IF_NZ(polyrep->actualCoord);
2555 FREE_IF_NZ(polyrep->GeneratedTexCoords[0]);
2556 FREE_IF_NZ(polyrep->colindex);
2557 FREE_IF_NZ(polyrep->color);
2558 FREE_IF_NZ(polyrep->norindex);
2559 FREE_IF_NZ(polyrep->normal);
2560 FREE_IF_NZ(polyrep->flat_normal);
2561 FREE_IF_NZ(polyrep->tcindex);
2562 }
2563 if(!node->_intern)
2564 node->_intern = (struct X3D_GeomRep*) create_polyrep();
2565
2566 rep_ = polyrep = (struct X3D_PolyRep*) node->_intern;
2567
2568
2569 /* if multithreading, tell the rendering loop that we are regenning this one */
2570 /* if singlethreading, this'll be set to TRUE before it is tested */
2571
2572 //<< END FROM Compile_polyrep
2573
2574 // Start Virt_make_polyrep section >>>
2575
2576 if (npts > 0)
2577 {
2578 //printf("npoints %d ntc %d\n",npoints,ntc);
2579 rep_->actualCoord = xyz;
2580 rep_->normal = nxyz;
2581 }
2582 rep_->ntri = ntri;
2583
2584 if (rep_->ntri > 0)
2585 {
2586 rep_->cindex = tindex;
2587 norindex = rep_->norindex = MALLOC(GLuint *,sizeof(GLuint)*3*ntri);
2588 memcpy(norindex,tindex,sizeof(GLuint)*3*ntri);
2589 }
2590
2591
2592 //END virt_make_polyrep section <<<<<<
2593
2594 //FROM Compile_polyrep
2595 if (polyrep->ntri != 0) {
2596 //float *fogCoord = NULL;
2597 stream_polyrep(node, NULL,NULL,NULL,NULL, NULL);
2598 /* and, tell the rendering process that this shape is now compiled */
2599 }
2600 //else wait for set_coordIndex to be converted to coordIndex
2601 polyrep->irep_change = node->_change;
2602
2603 /*
2604 // dump then can copy and paste to x3d or wrl IndexedFaceSet.coordIndex and Coordinate.point fields
2605 FILE * fp = fopen("IFS_DUMP.txt","w+");
2606 fprintf(fp,"#vertices %d\n",np);
2607 for(i=0;i<np;i++){
2608 fprintf(fp,"%f %f %f\n",rep->actualCoord[i*3 +0],rep->actualCoord[i*3 +1],rep->actualCoord[i*3 +2]);
2609 }
2610 fprintf(fp,"#face indices %d\n",ni);
2611 for(i=0;i<ni;i++){
2612 fprintf(fp,"%d ",rep->cindex[i]);
2613 if((ni+1) % 3 == 0)
2614 fprintf(fp,"%d ",-1);
2615 }
2616 fprintf(fp,"\n");
2617 fclose(fp);
2618 */
2619
2620}
2621void compile_NurbsSweptSurface(struct X3D_NurbsSweptSurface *node){
2622 int nt, np;
2623 double *xyzx;
2624 double *xyzt;
2625 struct X3D_NurbsCurve *trajectory;
2626 struct X3D_NurbsCurve2D *xsection;
2627 MARK_NODE_COMPILED
2628 //Swept:
2629
2630 trajectory = (struct X3D_NurbsCurve *)node->trajectoryCurve;
2631 xyzt = NULL;
2632 nt = 0;
2633 /*
2634 switch(trajectory->controlPoint->_nodeType){
2635 case NODE_Coordinate:
2636 {
2637 struct X3D_Coordinate *tcoord = (struct X3D_Coordinate*) trajectory->controlPoint;
2638 nt = tcoord->point.n;
2639 xyzt = MALLOC(double *,nt * sizeof(double));
2640 for(int i=0;i<tcoord->point.n;i++)
2641 float2double(&xyzt[i*3],tcoord->point.p[i].c,3);
2642 }
2643 break;
2644 case NODE_CoordinateDouble:
2645 {
2646 struct X3D_CoordinateDouble *tcoord = (struct X3D_CoordinateDouble *)trajectory->controlPoint;
2647 nt = tcoord->point.n;
2648 xyzt = MALLOC(double *,nt * sizeof(double));
2649 for(int i=0;i<tcoord->point.n;i++)
2650 veccopyd(&xyzt[i*3],tcoord->point.p[i].c);
2651
2652 }
2653 break;
2654 default:
2655 break;
2656 }
2657 */
2658 compute_doublecontrol(trajectory->controlPoint,&nt,&xyzt);
2659 xsection = (struct X3D_NurbsCurve2D *)node->crossSectionCurve;
2660
2661 np = xsection->controlPoint.n;
2662 xyzx = (double*)xsection->controlPoint.p;
2663
2664 // "The Nurbs Book", Les Piegl, Wayne Tiller, 2nd, 1997, Springer
2665 // piegl p.472 10.4 Swept Surfaces
2666 //ALGO Method 1.
2667 // method 1. S(u,v) = T(v) + C(u)
2668 // - has a precise NURBS definition
2669 // - (no need for spine, B up vector, planes, skinning)
2670 // - just set up the Suv control net and weights, and delegate to Patch
2671 // P(i,j) = Tj + Qi (control points)
2672 // w(i,j) = wT(j) x wC(i) (weights)
2673 // - but piegl Figure 10.11 p.474 shows the results of this sweep:
2674 // * its good for nearly linear trajectories
2675 // x but not good if you turn a 90 corner
2676 // - the profile isn't rotated with the trajectory
2677 // - so the 'tube' will flatten
2678 //ALGO 2 Method 2.
2679 // if you want the tube to stay open ie profile rotates with trajectory curve,
2680 // then you need to implement method 2. and for that
2681 // 1. compute tesselation points along trajectory curve (use piegl CurvePoint)
2682 // - get direction vector aka Tangent T of curve using Delta or Derivs, as xsection normal
2683 // - project up vector from last profile (1st profile up is arbitrary)
2684 // Piegl p.483 formula 10.27:
2685 // B0 - arbitrary unit vector perpendicular/orthogonal to trajectory tangent vector at v0
2686 // Ti = T'(vi)/|T'(vi)| //get the tangent vector along the trajectory curve, can use delta or Derivs
2687 // bi = Bi-1 - (Bi-1 * Ti)Ti
2688 // Bi = bi/|bi|
2689 // 2. comupte tesselated cross section aka xsection aka profile (use piegl CurvePoint)
2690 // 3. for each trajectory tesselation point:
2691 // a) insert up- and tangent- oriented xsection points
2692 // b) skin: join current xsection points with last with triangles
2693 //
2694 if(!strcmp(node->method->strptr,"FULL"))
2695 node->_method = 2;
2696 if(!strcmp(node->method->strptr,"TRANSLATE"))
2697 node->_method = 1;
2698 if(false && node->_method == 1){
2699 //xx broken, April 2022, don't use this _method == 1 section (go through _method == 2 below with _method == 1 modifications)
2700 //ALGO 1 Suv = T(v) + C(u)
2701 struct X3D_NurbsPatchSurface *patch;
2702 struct X3D_Coordinate *controlPoint;
2703 float *xyz;
2704 int ic,j,i;
2705 double *weight;
2706
2707 patch = (struct X3D_NurbsPatchSurface*)node->_patch;
2708 controlPoint = (struct X3D_Coordinate*)patch->controlPoint;
2709 if(!patch){
2710 patch = (struct X3D_NurbsPatchSurface*) createNewX3DNode(NODE_NurbsPatchSurface);
2711 controlPoint = (struct X3D_Coordinate*) createNewX3DNode(NODE_Coordinate);
2712 node->_patch = X3D_NODE(patch);
2713 patch->controlPoint = X3D_NODE(controlPoint);
2714 }
2715 xyz = MALLOC(float*,nt * np * 3 * sizeof(float));
2716 controlPoint->point.p = (struct SFVec3f*) xyz;
2717 controlPoint->point.n = nt * np;
2718
2719 ic = 0;
2720 for(j=0;j<nt;j++){
2721 float pt[3];
2722 double2float(pt,&xyzt[j*3],3);
2723 for(i=0;i<np;i++){
2724 float pp[3];
2725 double2float(pp,&xyzx[2*i],2);
2726 xyz[ic*3 + 0] = pt[0] + pp[0];
2727 xyz[ic*3 + 1] = pt[1] + pp[1];
2728 xyz[ic*3 + 2] = pt[2];
2729 ic++;
2730 }
2731 }
2732
2733 weight = NULL;
2734 if((trajectory->weight.n && trajectory->weight.n == nt) ||
2735 (xsection->weight.n && xsection->weight.n == np)){
2736 //we have some non-default weights, so apply the piegl formula p.473
2737 // w(i,j) = wT(j) x wC(i) (weights)
2738 weight = MALLOC(double*,nt * np * sizeof(double));
2739 for(j=0;j<nt;j++){
2740 double wtTj = trajectory->weight.p[j];
2741 for(i=0;i<np;i++)
2742 weight[j*np + i] = wtTj * xsection->weight.p[i];
2743 }
2744 }
2745
2746
2747 if(weight){
2748 patch->weight.p = weight;
2749 patch->weight.n = nt * np;
2750 }
2751 patch->solid = node->solid;
2752 //u will be profile,
2753 patch->uDimension = np;
2754 patch->uKnot.p = malloc(xsection->knot.n * sizeof(double));
2755 memcpy(patch->uKnot.p,xsection->knot.p,xsection->knot.n * sizeof(double));
2756 patch->uKnot.n = xsection->knot.n;
2757 patch->uOrder = xsection->order;
2758 patch->uTessellation = (int)((float)xsection->tessellation * xsection->_tscale);
2759 //v will be trajectory
2760 patch->vDimension = nt;
2761 patch->vKnot.p = malloc(xsection->knot.n * sizeof(double));
2762 memcpy(patch->vKnot.p,trajectory->knot.p,trajectory->knot.n * sizeof(double));
2763 patch->vKnot.n = trajectory->knot.n;
2764 patch->vOrder = trajectory->order;
2765 patch->vTessellation = (int)((float)trajectory->tessellation * trajectory->_tscale);
2766 if(0){
2767 int j,k,ic = 0;
2768 for(j=0;j<nt;j++){
2769 for(k=0;k<np;k++){
2770 printf("%f %f %f,",xyz[ic*3 + 0], xyz[ic*3 +1], xyz[ic*3 +2]);
2771 ic++;
2772 }
2773 printf("\n");
2774 }
2775 printf("uDimension=%d vDimension=%d nc=%d\n",np,nt,ic);
2776 }
2777 compile_NurbsPatchSurface((struct X3D_NurbsPatchSurface*)node->_patch);
2778 } //end method == 1
2779 if(true || node->_method == 2){
2780 //ALGO 2 skinning like extrusion
2781 int mtessv, mtessu, nku, nkv;
2782 int i,DBGSW;
2783 float *knotsu,*knotsv,*xyzwu,*xyzwv, urange[2],vrange[2];
2784 float *Tv, *Tangentv, *Bup, *pts;
2785 float *Qu, *Nu;
2786 float *normals;
2787 int *idx;
2788 int ic, it;
2789 float matB0[9];
2790 int ntri;
2791
2792
2793 int mtessu1, mtessv1;
2794
2795 mtessu = compute_tessellation(xsection->tessellation,xsection->order,np);
2796 mtessu = (int)((float)mtessu * xsection->_tscale);
2797 mtessv = compute_tessellation(trajectory->tessellation,trajectory->order,nt);
2798 mtessv = (int)((float)mtessv * trajectory->_tscale);
2799 compute_knotvector(xsection->order,np,xsection->knot.n,xsection->knot.p,&nku,&knotsu,urange);
2800 compute_knotvector(trajectory->order,nt,trajectory->knot.n,trajectory->knot.p,&nkv,&knotsv,vrange);
2801 compute_weightedcontrol(xyzt,3,nt, trajectory->weight.n, trajectory->weight.p, &xyzwv);
2802 compute_weightedcontrol(xyzx,2,np, xsection->weight.n, xsection->weight.p, &xyzwu);
2803 DBGSW = FALSE;
2804 if(DBGSW){
2805 int i;
2806 printf("np %d mtessu %d nku %d, nt %d mtessv %d, nkv %d",np,mtessu,nku,nt,mtessv,nkv);
2807 printf("trajectory nt %d points:\n",nt);
2808 for(i=0;i<nt;i++)
2809 printf("%d %f %f %f %f\n",i,xyzwv[i*4 + 0],xyzwv[i*4 + 1],xyzwv[i*4 + 2],xyzwv[i*4 + 3]);
2810 printf("xsection np %d points:\n",np);
2811 for(i=0;i<np;i++)
2812 printf("%d %f %f %f %f\n",i,xyzwu[i*4 + 0],xyzwu[i*4 + 1],xyzwu[i*4 + 2],xyzwu[i*4 + 3]);
2813 }
2814 // 1. compute tesselation points along trajectory curve (use piegl CurvePoint)
2815 // - get direction vector aka Tangent T of curve using Delta or Derivs, as xsection normal
2816 // - project up-vector from last profile (1st profile up is arbitrary)
2817 // Piegl p.483 formula 10.27:
2818 // B0 - arbitrary unit vector perpendicular/orthogonal to trajectory tangent vector at v0
2819 // Ti = T'(vi)/|T'(vi)| //get the tangent vector Tangentv along the trajectory curve, can use delta or Derivs
2820 // bi = Bi-1 - (Bi-1 dot Ti)Ti //where dot is the dot product
2821 // Bi = bi/|bi|
2822 mtessu1 = mtessu + 1;
2823 mtessv1 = mtessv + 1;
2824 Tv = MALLOC(float*,(mtessv1)*3*sizeof(float));
2825 Tangentv = MALLOC(float*,(mtessv1)*3*sizeof(float));
2826 Bup = MALLOC(float*,(mtessv1)*3*sizeof(float));
2827 for(i=0;i<mtessv1;i++){
2828 float cw[4], cw1[4], delta[3], v;
2829 v = (float)i*(vrange[1]-vrange[0])/(float)mtessv;
2830 CurvePoint(nt, trajectory->order-1, knotsv, xyzwv, v, cw );
2831 veccopy3f(&Tv[i*3],cw);
2832 //- get direction vector aka Tangent T of curve using Delta or Derivs, as xsection normal
2833 CurvePoint(nt, trajectory->order-1, knotsv, xyzwv, v+.01f, cw1 );
2834 vecdif3f(delta,cw1,cw);
2835 // Ti = T'(vi)/|T'(vi)| //get the tangent vector Tangentv along the trajectory curve, can use delta or Derivs
2836 vecnormalize3f(delta,delta);
2837 veccopy3f(&Tangentv[i*3],delta);
2838 //- project up-vector from last profile (1st profile up is arbitrary)
2839 if(i==0){
2840 // B0 - arbitrary unit vector perpendicular/orthogonal to trajectory tangent vector at v0
2841 int k,irank[3],idom, inondom;
2842 float perp[3], perp2[3];
2843 idom = ivecdominantdirection3f(irank,delta);
2844 inondom = idom + 1 > 2 ? 0 : idom + 1;
2845 for(k=0;k<3;k++) if(irank[k] == 0) inondom = k;
2846 memset(perp,0,3*sizeof(float));
2847 perp[inondom] = 1.0; //close to perpendicular, but not quite
2848 veccross3f(perp2,delta,perp); //perp2 perpendicular to perp and delta
2849 veccross3f(perp,perp2,delta); //another cross to get perp exactly perpendicular
2850 //perp should be perpendicular to delta[0]
2851 veccopy3f(&Bup[i*3],perp);
2852 }else{
2853 // bi = Bi-1 - (Bi-1 dot Ti)Ti
2854 // Bi = bi/|bi|
2855 float bi[3], bi1dotti, tiscaled[3];
2856 bi1dotti = vecdot3f(&Bup[(i-1)*3],&Tangentv[i*3]);
2857 vecdif3f(bi,&Bup[(i-1)*3],vecscale3f(tiscaled,&Tangentv[i*3],bi1dotti));
2858 vecnormalize3f(&Bup[i*3],bi);
2859 }
2860 }
2861 if(DBGSW){
2862 printf("trajectory T:\n");
2863 for(i=0;i<mtessv1;i++){
2864 printf("%d [%f %f %f] \n",i,
2865 Tv[i*3 +0],Tv[i*3 +1],Tv[i*3 +2]);
2866 }
2867 printf("trajectory T', B:\n");
2868 for(i=0;i<mtessv1;i++){
2869 printf("%d [%f %f %f] [%f %f %f] \n",i,
2870 Tangentv[i*3 +0],Tangentv[i*3 +1],Tangentv[i*3 +2],
2871 Bup[i*3 +0],Bup[i*3 +1],Bup[i*3 +2]);
2872 }
2873
2874 }
2875 //if(trajectory->closed){
2876 // //compute backward Bup, and average fore and aft Bup
2877 //}
2878 // 2. comupte tesselated cross section aka xsection aka profile (use piegl CurvePoint)
2879 Qu = MALLOC(float*,(mtessu+1)*3*sizeof(float)); //cross section points
2880 Nu = MALLOC(float*,(mtessu+1)*3*sizeof(float)); //cross section normals
2881 if(DBGSW) printf("Xsection tess pts:\n");
2882 for(i=0;i<mtessu1;i++){
2883 float u, cw[4], cw1[4], delta[3], normal[3];
2884 float zzz[3] = {0.0f,0.0f,1.0f};
2885 u = (float)i*(urange[1]-urange[0])/(float)mtessu;
2886 CurvePoint(np, xsection->order-1, knotsu, xyzwu, u, cw );
2887 veccopy3f(&Qu[i*3],cw);
2888 CurvePoint(np, xsection->order-1, knotsu, xyzwu, u+.01f, cw1 );
2889 vecdif3f(delta,cw1,cw);
2890 vecnormalize3f(delta,delta);
2891 veccross3f(normal,zzz,delta);
2892 veccopy3f(&Nu[i*3],normal);
2893 if(DBGSW) printf("%d %f %f %f\n",i,Qu[i*3 +0],Qu[i*3 +1],Qu[i*3 +2]);
2894
2895 }
2896 // 3. for each trajectory tesselation point:
2897 // a) insert up- and tangent- oriented xsection points
2898 // b) skin: join current xsection points with last with triangles
2899 pts = MALLOC(float*,mtessu1 * mtessv1 * 3 * sizeof(float));
2900 normals = MALLOC(float*,mtessu1 * mtessv1 * 3 * sizeof(float));
2901 idx = MALLOC(int *, mtessu * mtessv * 2 * 3 * sizeof(int));
2902 ic = 0;
2903 it = 0;
2904 for(i=0;i<mtessv1;i++){
2905 //insert oriented xsection at T(v)
2906 float mat[9], matt[9];
2907 int j;
2908 //set up 3x3 rotation by using 3 perpendicular local unit vectors as rot mat rows
2909 //http://renderdan.blogspot.ca/2006/05/rotation-matrix-from-axis-vectors.html
2910
2911 veccross3f(&mat[0],&Bup[i*3],&Tangentv[i*3]);
2912 veccopy3f(&mat[3],&Bup[i*3]);
2913 veccopy3f(&mat[6],&Tangentv[i*3]);
2914 mattranspose3f(matt,mat); //seems like I need columns, not rows, by experimentation
2915 if(i==0){
2916 //not sure but I think we should take off the
2917 //arbitrary rotation of the first crossection from all subsequent
2918 //so first xsection not rotated (you need to design with
2919 //your crosssection plane perpendicular to the start of your trajectory)
2920 //and subsequent are rotated with respect to first
2921 //Looks good
2922 if (node->_method == 1) {
2923 matidentity3f(mat);
2924 }
2925 memcpy(matB0,mat,9*sizeof(float));
2926 }
2927 matmultiply3f(mat,matt,matB0);
2928 if (node->_method == 1) {
2929 matidentity3f(mat);
2930 }
2931 for(j=0;j<mtessu1;j++){
2932 float pp[3], norm[3], qq[3];
2933 matmultvec3f(pp, mat, &Qu[j*3] ); //orient profile point
2934 //matmultvec3f(norm,mat,&Nu[j*3]); //didn't work
2935 //compute norm as difference of 2 transformed points
2936 vecadd3f(qq, &Nu[j * 3], &Qu[j * 3]);
2937 matmultvec3f(qq, mat, qq);
2938 vecdif3f(norm, pp, qq);
2939 vecnormalize3f(norm, norm);
2940 veccopy3f(&normals[ic * 3], norm);
2941
2942 //shift rotated point to trajectory point
2943 vecadd3f(pp,pp,&Tv[i*3]); //add on trajectory point
2944 veccopy3f(&pts[ic*3],pp);
2945 if (node->_method == 1) {
2946 float tt[3], ee[3], ff[3], ii[3]; //method 1 normal computation vectors
2947
2948 //compute normal as ii = ee x ff (edge normal cross face normal)
2949 //norm = ii x tt (tt is direction of travel vector)
2950 //x-section face normal, assume x-section in xy plane
2951 vecset3f(ff, 0.0f, 0.0f, 1.0f); //assumed normal to x-section face
2952 //edge normal - in plane of x-section, at vertex of x-section point
2953 veccopy3f(ee, &Nu[j * 3]);
2954 //travel vector along profile
2955 if (i == 0) vecdif3f(tt, &Tv[(i+1) * 3], &Tv[i * 3]); //vecset3f(tt, 0.0f, 0.0f, -1.0f);
2956 else vecdif3f(tt, &Tv[i * 3], &Tv[(i - 1) * 3]);
2957 //printf("tt[%d] %f %f %f\n", j, tt[0], tt[1], tt[2]);
2958 veccross3f(ii, ff, ee);
2959 veccross3f(norm, ii, tt);
2960 vecnormalize3f(norm, norm);
2961 veccopy3f(&normals[ic * 3], norm);
2962 }
2963 ic++;
2964 }
2965 //connect to last xsection with triangles
2966 if(i > 0){
2967 int j,kk,mm;
2968 kk = (i-1)*mtessu1;
2969 mm = i*mtessu1;
2970 for(j=0;j<mtessu;j++){
2971 // 1 1 3
2972 // 0 2 2
2973 //first triangle
2974 idx[it++] = kk+j;
2975 idx[it++] = mm+j;
2976 idx[it++] = kk+j+1;
2977 //second triangle
2978 idx[it++] = kk+j+1;
2979 idx[it++] = mm+j;
2980 idx[it++] = mm+j+1;
2981 }
2982 }
2983 }
2984 //assign to something
2985 ntri = it/3;
2986 if(DBGSW){
2987 printf("ntri %d triangle indexes:\n",ntri);
2988 for(i=0;i<ntri;i++){
2989 int j;
2990 printf("%d [",i);
2991 for(j=0;j<3;j++){
2992 printf("%d ",idx[i*3 +j]);
2993 }
2994 printf("\n");
2995 }
2996 printf("triangle vertices:\n");
2997 for(i=0;i<ntri;i++){
2998 int j;
2999 for(j=0;j<3;j++){
3000 float pt[3];
3001 int ix = idx[i*3 +j];
3002 veccopy3f(pt,&pts[ix*3]);
3003 printf("%d %d %f %f %f\n",i,ix,pt[0],pt[1],pt[2]);
3004 }
3005 }
3006 }
3007
3008 //send to polyrep
3009 convert_mesh_to_polyrep(pts,ic,normals,idx,ntri,X3D_NODE(node));
3010 }
3011
3012}
3013void rendray_NurbsSweptSurface (struct X3D_NurbsSweptSurface *node) {
3014 COMPILE_IF_REQUIRED
3015 if(node->_method == 1){
3016 if (!node->_patch) return;
3017 render_ray_polyrep(node->_patch);
3018 }
3019 if(node->_method == 2){
3020 if(!node->_intern) return;
3021 render_ray_polyrep(node);
3022 }
3023}
3024
3025void collide_NurbsSweptSurface (struct X3D_NurbsSweptSurface *node) {
3026 COMPILE_IF_REQUIRED
3027 if(node->_method == 1){
3028 if (!node->_patch) return;
3029 collide_genericfaceset(X3D_INDEXEDFACESET(node->_patch));
3030 }
3031 if(node->_method == 2){
3032 if (!node->_intern) return;
3033 collide_genericfaceset(X3D_INDEXEDFACESET(node));
3034 }
3035}
3036
3037void render_NurbsSweptSurface (struct X3D_NurbsSweptSurface *node) {
3038 COMPILE_IF_REQUIRED
3039 if(false && node->_method == 1){
3040 struct X3D_NurbsPatchSurface *patch;
3041 if (!node->_patch->_intern)
3042 return;
3043 patch = (struct X3D_NurbsPatchSurface *)node->_patch;
3044 CULL_FACE(patch->solid)
3045 render_polyrep(patch);
3046 }
3047 if(true || node->_method == 2){
3048 if (!node->_intern)
3049 return;
3050 //CULL_FACE(node->solid)
3051 render_polyrep(node);
3052
3053 }
3054}
3055void compile_NurbsSet(struct X3D_NurbsSet *node){
3056 int i;
3057 MARK_NODE_COMPILED
3058 //tessellationScale
3059 for(i=0;i<node->geometry.n;i++){
3060 struct X3D_Node *gn = (struct X3D_Node*)node->geometry.p[i];
3061 switch(gn->_nodeType){
3062 case NODE_NurbsCurve:
3063 {
3064 struct X3D_NurbsCurve* g = (struct X3D_NurbsCurve*)gn;
3065 g->_tscale = node->tessellationScale;
3066 MNX(g);
3067 }
3068 break;
3069 case NODE_NurbsCurve2D:
3070 {
3071 struct X3D_NurbsCurve2D* g = (struct X3D_NurbsCurve2D*)gn;
3072 g->_tscale = node->tessellationScale;
3073 MNX(g);
3074 }
3075 break;
3076 case NODE_NurbsPatchSurface:
3077 {
3078 struct X3D_NurbsPatchSurface* g = (struct X3D_NurbsPatchSurface*)gn;
3079 g->_tscale = node->tessellationScale;
3080 MNX(g);
3081 }
3082 break;
3083 case NODE_NurbsTrimmedSurface:
3084 {
3085 struct X3D_NurbsTrimmedSurface* g = (struct X3D_NurbsTrimmedSurface*)gn;
3086 g->_tscale = node->tessellationScale;
3087 MNX(g);
3088 }
3089 break;
3090 case NODE_NurbsSweptSurface:
3091 {
3092 struct X3D_NurbsSweptSurface* g = (struct X3D_NurbsSweptSurface*)gn;
3093 if(g->_method == 1){
3094 if(g->_patch){
3095 struct X3D_NurbsPatchSurface *patch = (struct X3D_NurbsPatchSurface *)g->_patch;
3096 patch->_tscale = node->tessellationScale;
3097 MNX(g);
3098 }
3099 }
3100 if(g->_method == 2){
3101 struct X3D_NurbsCurve2D *curve = (struct X3D_NurbsCurve2D *)g->crossSectionCurve;
3102 curve->_tscale = node->tessellationScale;
3103 MNX(g->crossSectionCurve);
3104 curve = (struct X3D_NurbsCurve2D *)g->trajectoryCurve;
3105 curve->_tscale = node->tessellationScale;
3106 MNX(g->trajectoryCurve);
3107 }
3108 }
3109 break;
3110 case NODE_NurbsSwungSurface:
3111 {
3112 struct X3D_NurbsSwungSurface* g = (struct X3D_NurbsSwungSurface*)gn;
3113 if(g->_patch){
3114 struct X3D_NurbsPatchSurface *patch = (struct X3D_NurbsPatchSurface *)g->_patch;
3115 patch->_tscale = node->tessellationScale;
3116 MNX(g);
3117 }
3118 }
3119 break;
3120 }
3121 }
3122}
3123void render_NurbsSet(struct X3D_NurbsSet *node){
3124 COMPILE_IF_REQUIRED
3125}
3126#else //LIB_NURBS
3127void compile_ContourPolyline2D(struct X3D_ContourPolyline2D *node){}
3128void compile_NurbsCurve(struct X3D_NurbsCurve *node){}
3129void render_NurbsCurve(struct X3D_NurbsCurve *node){}
3130void compile_NurbsPatchSurface(struct X3D_NurbsPatchSurface *node){}
3131void rendray_NurbsPatchSurface (struct X3D_NurbsPatchSurface *node) {}
3132void collide_NurbsPatchSurface (struct X3D_NurbsPatchSurface *node) {}
3133void render_NurbsPatchSurface (struct X3D_NurbsPatchSurface *node) {}
3134void compile_NurbsTrimmedSurface(struct X3D_NurbsTrimmedSurface *node){}
3135void rendray_NurbsTrimmedSurface (struct X3D_NurbsTrimmedSurface *node) {}
3136void collide_NurbsTrimmedSurface (struct X3D_NurbsTrimmedSurface *node) {}
3137void render_NurbsTrimmedSurface (struct X3D_NurbsTrimmedSurface *node) {}
3138void do_NurbsPositionInterpolator (void *node) {}
3139void do_NurbsOrientationInterpolator (void *node){}
3140void do_NurbsSurfaceInterpolator (void *_node){}
3141void compile_NurbsSwungSurface(struct X3D_NurbsSwungSurface *node){}
3142void rendray_NurbsSwungSurface (struct X3D_NurbsSwungSurface *node) {}
3143void collide_NurbsSwungSurface (struct X3D_NurbsSwungSurface *node) {}
3144void render_NurbsSwungSurface (struct X3D_NurbsSwungSurface *node) {}
3145void compile_NurbsSweptSurface(struct X3D_NurbsSweptSurface *node){}
3146void rendray_NurbsSweptSurface (struct X3D_NurbsSweptSurface *node) {}
3147void collide_NurbsSweptSurface (struct X3D_NurbsSweptSurface *node) {}
3148void render_NurbsSweptSurface (struct X3D_NurbsSweptSurface *node){}
3149void compile_NurbsSet(struct X3D_NurbsSet *node){}
3150void render_NurbsSet(struct X3D_NurbsSet *node){}
3151#endif //LIB_NURBS