FreeWRL / FreeX3D 4.3.0
Component_Picking.c
1/*
2
3
4X3D Picking Component
5
6*/
7
8
9/****************************************************************************
10 This file is part of the FreeWRL/FreeX3D Distribution.
11
12 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
13
14 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
15 it under the terms of the GNU Lesser Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
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 "../vrml_parser/CRoutes.h"
37#include "../main/headers.h"
38
39#include "../world_script/fieldSet.h"
40#include "../x3d_parser/Bindable.h"
41#include "Collision.h"
42#include "quaternion.h"
43#include "Viewer.h"
44#include "../opengl/Frustum.h"
45#include "../opengl/Material.h"
46#include "../opengl/OpenGL_Utils.h"
47#include "../input/EAIHelpers.h" /* for newASCIIString() */
48
49#include "Polyrep.h"
50#include "LinearAlgebra.h"
51#include "Component_Picking.h"
52#include "Children.h"
53#include "RenderFuncs.h"
54
56 struct X3D_Node* node;
57 float dist;
58};
59
60typedef struct pComponent_Picking{
61 //Stack *stack_nodesintersected;
62 Stack *stack_nodesdistance;
63 Stack *stack_intersections;
64 Stack *stack_pointsinside;
66void *Component_Picking_constructor(){
67 void *v = MALLOCV(sizeof(struct pComponent_Picking));
68 memset(v,0,sizeof(struct pComponent_Picking));
69 return v;
70}
71void Component_Picking_init(struct tComponent_Picking *t){
72 //public
73 //private
74 t->prv = Component_Picking_constructor();
75 {
77 p->stack_intersections = newStack(struct intersection_info);
78 //p->stack_nodesintersected = newStack();
79 p->stack_nodesdistance = newStack(struct nodedistance);
80 p->stack_pointsinside = newStack(struct intersection_info);
81 }
82}
83//ppComponent_Picking p = (ppComponent_Picking)gglobal()->Component_Picking.prv;
84
85
86/* PICKSENSOR stubs */
87void other_PointPickSensor (struct X3D_PointPickSensor *node) {}
88void other_PickableGroup (struct X3D_Group *node) {}
89void other_Sphere (struct X3D_Sphere *node) {}
90//void child_PickableGroup (struct X3D_Group *node) {}
91void prep_PickableGroup (struct X3D_Group *node) {}
92void add_picksensor(struct X3D_Node * node) {}
93void remove_picksensor(struct X3D_Node * node) {}
94
95void push_pickablegroupdata(void *userdata);
96void pop_pickablegroupdata();
97void child_PickableGroup (struct X3D_Group *node) {
98 CHILDREN_COUNT
99 RETURN_FROM_CHILD_IF_NOT_FOR_ME
100 /* printf("%s:%d child_PickableGroup\n",__FILE__,__LINE__); */
101
102 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
103
104
105 //PUSH OBJECTTYPE
106 //PUSH PICKABLE == TRUE/FALSE
107 push_pickablegroupdata(node);
108
109 normalChildren(node->children);
110
111 //POP PICKABLE == TRUE/FALSE
112 //POP OBJECTTTYPE
113 pop_pickablegroupdata();
114 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
115}
116
117
118
119int overlapMBBs(GLDOUBLE *MBBmin1, GLDOUBLE *MBBmax1, GLDOUBLE *MBBmin2, GLDOUBLE* MBBmax2);
120void transformMBB(GLDOUBLE *rMBBmin, GLDOUBLE *rMBBmax, GLDOUBLE *matTransform, GLDOUBLE* inMBBmin, GLDOUBLE* inMBBmax);
121/*
122A. Q. Should we even bother to implement Picking component? Few others are:
123 http://www.web3d.org/wiki/index.php/Player_support_for_X3D_components
124 - Xj3D and Flux/Vivaty listed as supporting picking component
125 x problems running xj3d on win10 - doesn't run for dug9
126 x neither vivaty studio nor vivatyplayer have it, just something called PickGroup with enable field
127 x cobweb not listed, but on their site they don't mention Picking component
128 * dune has the nodes,
129 x but doesn't seem to generate proper parent-child at design-time:
130 - picksensor.pickingGeometry should be a geometry node
131 * but does load a scene designed elsewhere ie by hand in text editor
132
133B. New theory of operation for PickSensors (July 2016, dug9):
1341. like the new do_TransformSensor - all you need is do_PickSensorTick and VF_USE and usehit_ functions.
135 - picksensor and target nodes are flagged here with VF_USE
136 - then on next frame/tick the picksensor and targets are recovered here with usehit_ functions,
137 including their modelview transform
138 - and all the work is done here in do_PickSensorTick
139 - (no need for compile_ other_ etc, not yet)
1402. we could simplify by doing BOUNDS only:
141 - if target is geom, then can do BOUNDS or GEOMETRY intersection
142 - if target is 'chunk of scenegraph' ie Group, Transform or PickableGroup, then
143 for now july 2016: we do only BOUNDS,
144 - taking only the _extent of the (topmost) target node,
145 - not delving/probing into children/descendants
146 future: do a special setup of render_node(node) and children recursion for picking that examines each descendant
147 - and/or over-ride rayhit (mouse picking) functions
148 - push and pop X3DPickable state according to X3DPickableGroup in the transform hierarchy
149 - ^^the pickingGeometry SFNode is not a USE reference, it has only this node as parent (or we'll ignore any other USE modelview matrix)
1503. what 'space' to work in, given 3 parts: PickSensor, pickingGeometry, pickTarget:
151 - transformsensor did its work in transformsensor node space (where the transformsensor was in scenegraph)
152 - so any evenouts were in transformsensor space
153 - in theory for picksensors we could do our work in
154 a) world space (by taking view off modelview matrix, and doing one-way transforms local2world for all 3 parts), or
155 b) pickTarget space (as we do with mouse picking, transforming the pickray on each transform stack push), or
156 c) pickingGeometry space (we'll assume^^ this is the same space as d) picksensor)
157 d) pickSensor space (like transformSensor)
158 -each option would/could affect function design
159 - for now we'll do work in d) picksensor space, so 'closest' is easy to sort relative to 0,0,0
160 and that means transforming pickTarget to picksensor space
161 and assuming^^ pickingGeometry is already in picksensor space
162HARD PARTS:
163 1. picking against a partial transform hierarchy of objects
164 - can use the USEUSE approach to get the starting transform stack for target node
165 - need to modify render_node() and/or normalChildren and/or render(node0 for picking partial pass
166 2. geom-geom intersections
167 - bounding box easy
168 - we do ray-geom for touchsensors
169 - we do 'avatar brush' (bunch of line segments) vs geom in collision
170 - RBP will do geom-geom collisions, but the geoms are simplifications: box, sphere
171 - if transform pickee into picker space, then simple math formulas
172 ie cone if bottomRadius is R, and height is H, and and a point is between bottom and top
173 at hieght h from bottom, then cone r = f(h,R,H) = R*(1-h/H)
174 - convex hull - particlephysics we did that for polyrep
175
176*/
177int objecttypes_overlap(struct Multi_String * list1, struct Multi_String * list2, struct Uni_String * criterion){
178 int ntries, nmatches, iallsensor, ialltarget,inonetarget;
179 int i,j,iret = FALSE;
180 //hope I got this logic right
181 ntries = nmatches = iallsensor = ialltarget = inonetarget = 0;
182 for(j=0;j<list2->n;j++){
183 if(!strcmp(list2->p[j]->strptr,"ALL")) ialltarget = TRUE;
184 if(!strcmp(list2->p[j]->strptr,"NONE")) inonetarget = TRUE;
185 }
186
187 for(i=0;i<list1->n;i++){
188 if(!strcmp(list1->p[i]->strptr,"ALL")) iallsensor = TRUE;
189 for(j=0;j<list2->n;j++){
190 ntries++;
191 if(!strcmp(list1->p[i]->strptr,list2->p[j]->strptr)) {
192 nmatches++;
193 }
194 }
195 }
196 if(!strcmp(criterion->strptr,"MATCH_ANY")){
197 if(nmatches) iret = TRUE;
198 }else if(!strcmp(criterion->strptr,"MATCH_ALL")){
199 if(nmatches == ntries) iret = TRUE;
200 }else if(!strcmp(criterion->strptr,"MATCH_ONE")){
201 if(nmatches == 1) iret = TRUE;
202 }
203 if(iallsensor || ialltarget) iret = TRUE;
204 if(inonetarget) iret = FALSE;
205 return iret;
206}
207int isGeometryNode(struct X3D_Node* node){
208 //is there a bitflag or other function somewhere to say if a node is a geometry type node?
209 int iret = FALSE;
210 struct X3D_Virt *virt = virtTable[node->_nodeType];
211 if(virt->rend || virt->rendray) iret = TRUE;
212 return iret;
213}
214//struct nodedistance {
215// struct X3D_Node* node;
216// float dist;
217//};
218//Compare function
219//return value Description
220//< 0 elem1 less than elem2
221// 0 elem1 equivalent to elem2
222//> 0 elem1 greater than elem2
223int compare_nodedistance(const void *elem1,const void * elem2 )
224{
225 struct nodedistance *nd1 = (struct nodedistance *)elem1;
226 struct nodedistance *nd2 = (struct nodedistance *)elem2;
227 return nd1->dist < nd2->dist ? -1 : nd1->dist > nd2->dist ? 1 : 0;
228}
229int compare_intersectiondistance(const void *elem1, const void * elem2){
230 struct intersection_info *nd1 = (struct intersection_info *)elem1;
231 struct intersection_info *nd2 = (struct intersection_info *)elem2;
232 return nd1->dist < nd2->dist ? -1 : nd1->dist > nd2->dist ? 1 : 0;
233}
234void do_PickSensorTick(void *ptr){
235 //heavy borrowing from do_TransformSensor
236 int ishit,i,j;
237 usehit *mehit, *uhit;
238 struct X3D_Node *unode,*menode, *pnode;
239 struct Multi_Node *unodes;
240 //we'll use PrimitivePickSensor for the generic picksensor type
241 // -the order of field definitions for all picksensors must be the same in Perl code generator VRMLNodes.pm
242 // up to the point of per-type differences
243 ppComponent_Picking p = (ppComponent_Picking)gglobal()->Component_Picking.prv;
244 struct X3D_PrimitivePickSensor *node = (struct X3D_PrimitivePickSensor *) ptr;
245 switch(node->_nodeType){
246 case NODE_LinePickSensor:
247 case NODE_PointPickSensor:
248 case NODE_PrimitivePickSensor:
249 case NODE_VolumePickSensor:
250 break;
251 default:
252 return; //not for me
253 }
254
255 // if not enabled, do nothing
256 if (!node) return;
257 if (node->__oldEnabled != node->enabled) {
258 node->__oldEnabled = node->enabled;
259 MARK_EVENT(X3D_NODE(node),offsetof (struct X3D_PrimitivePickSensor, enabled));
260 }
261 // are we enabled?
262 if (!node->enabled) return;
263
264 #ifdef SEVERBOSE
265 printf ("do_TransformSensorTick enabled\n");
266 #endif
267
268 //temp clear hit flag
269 ishit = 0;
270 mehit = NULL;
271 unodes = &node->pickTarget;
272 menode = (struct X3D_Node*)node; //upcaste
273 pnode = node->pickingGeometry;
274 //naming:
275 //'me' is the pickingsensor node
276 //'u' is a pick target node
277 if(unodes->n && pnode){
278 //check all USE-USE combinations of this node and pickTargets
279 //find ME: the picksensor, in the usehit list
280 while((mehit = usehit_next(menode,mehit))){
281 //hopefully there's only one instance of me/picksensor node in the scenegraph
282 //int iret;
283 double meinv[16],memin[3],memax[3];
284 float emin[3], emax[3]; //, halfsize[3];
285
286 matinverseAFFINE(meinv,mehit->mvm);
287 //iret = __gluInvertMatrixd( mehit->mvm, meinv);
288
289 if(0){
290 //check inverse
291 double ident[16];
292 int j;
293 matmultiplyAFFINE(ident,meinv,mehit->mvm);
294
295 printf("inverse check do_TransformSensor\n");
296 for(i=0;i<4;i++){
297 for(j=0;j<4;j++) printf("%lf ",ident[i*3+j]);
298 printf("\n");
299 }
300 printf("\n");
301 }
302 //update extent on me, in case center or size has changed
303 for(i=0;i<3;i++)
304 {
305 emin[i] = pnode->_extent[i*2 + 1];
306 emax[i] = pnode->_extent[i*2];
307 }
308 for(i=0;i<3;i++)
309 {
310 node->_extent[i*2 + 1] = emin[i];
311 node->_extent[i*2] = emax[i];
312 }
313 for(i=0;i<3;i++)
314 {
315 memin[i] = node->_extent[i*2 + 1];
316 memax[i] = node->_extent[i*2];
317 }
318
319 //find U: a target/pickable in the usehit list
320 p->stack_intersections->n = 0; //stack_clear
321 p->stack_nodesdistance->n = 0; //stack_clear
322 p->stack_pointsinside->n = 0; //stack_clear
323 uhit = NULL;
324 for(j=0;j<unodes->n;j++){
325 unode = unodes->p[j];
326 while((uhit = usehit_next(unode,uhit))){
327 //see if they intersect, if so do something about it
328 //-prepare matrixTarget2this
329 int intypes,pickable;
330 double u2me[16], me2u[16], umin[3],umax[3],uumin[3],uumax[3];
331
332 struct X3D_PickableGroup *pgroup;
333 pgroup = (struct X3D_PickableGroup *) uhit->userdata;
334 intypes = TRUE;
335 pickable = TRUE;
336 if(pgroup){
337 pickable = pgroup->pickable;
338 intypes = objecttypes_overlap(&node->objectType,&pgroup->objectType,node->matchCriterion);
339 }
340 if(intypes && pickable){
341 matmultiplyAFFINE(u2me,uhit->mvm,meinv);
342 matinverseAFFINE(me2u,u2me);
343 //-transform target AABB/MBB from target space to this space
344 //the specs say it should be done in world space, and perhaps there,
345 //.. normally the MBB/AABB will be aligned to world, as the scene author is thinking
346 //.. but we'll do our MBB/AABB test in picksensor space for now,
347 //.. to save a step
348 for(i=0;i<3;i++)
349 {
350 umin[i] = unode->_extent[i*2 + 1];
351 umax[i] = unode->_extent[i*2];
352 }
353 transformMBB(uumin,uumax,u2me,umin,umax);
354 //-see if AABB intersect
355 if( overlapMBBs(memin, memax, uumin, uumax) ){
356 //-if so take further action:
357 //(not implemented july 17, 2016 - end of day, no time left,
358 // ..but it does get in here, showing the above plumbing is working)
359 //picknode-specific intersections with various targetnode types
360 //if further testing shows they intersect, then ishit++:
361 if(!strcmp(node->intersectionType->strptr,"BOUNDS") || unode->_nodeType == NODE_Inline){
362 struct nodedistance ndist;
363 double c1[3],c2[3],dd[3];
364 //stack_push(struct X3D_Node*,p->stack_nodesintersected,unode);
365 vecaddd(c1,memin,memax);
366 vecscaled(c1,c1,.5);
367 vecaddd(c2,umin,umax);
368 vecscaled(c2,c2,.5);
369 vecdifd(dd,c2,c1);
370 ndist.dist = (float)veclengthd(dd);
371 ndist.node = unode;
372 stack_push(struct nodedistance,p->stack_nodesdistance,ndist);
373 ishit++;
374 }else if(!strcmp(node->intersectionType->strptr,"GEOMETRY")){
375 //we need to traverse the scenegraph subsection to get to the geometry
376 //clear some global variables
377 //set some render flags
378 //call render(unode) and bottom out on a callback above, which will
379 // do the work and set global variables
380 //retrieve global variables
381 //options:
382 //a) the callback snapshots the geometry node and matrix in a uhit,
383 // and we get back (yet another) list of uhits
384 //b) the callback does the intersections, and gives us back
385 // the intersection lists
386 //decision: lets do a) here
387 Stack *usehitB;
388 double viewMatrix[16];
389 int m;
390
391 usehitB_clear();
392
393 if(isGeometryNode(unode)){
394 double matidentity[16];
395 loadIdentityMatrix(matidentity);
396 usehitB_add2(unode,matidentity,pgroup);
397 loadIdentityMatrix(viewMatrix);
398 }else{
399 //snapshot matrix stack before renderhier
400 //- we're at the world level, so viewpoint will be in the matrix stack
401 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, viewMatrix);
402 render_hier(unode, VF_Geom | VF_Picking);
403 }
404 usehitB = getUseHitBStack();
405 for(m=0;m<vectorSize(usehitB);m++){
406 //geometry node
407 double u2meg[16], me2ug[16]; // nodedistance;
408 usehit *ghit = vector_get_ptr(usehit,usehitB,m);
409
410 // concatonate matrices
411 // u2meg = gmat * umat * meinv
412 // = gmat * u2me
413 {
414 double viewinv[16], world2geom[16];
415 //take off the viewmatrix from the geometry matrix
416 matinverseAFFINE(viewinv,viewMatrix);
417 //now get the matrix to go from ME to UGeom
418 matmultiply(world2geom,viewinv,ghit->mvm);
419 matmultiplyAFFINE(me2ug,world2geom,me2u);
420 matinverseAFFINE(u2meg,me2ug);
421 }
422
423
424 switch(node->_nodeType){
425 case NODE_LinePickSensor:
426 {
427 //foreach line segment:
428 // transform ends into U
429 // intersect with U geometry
430 // sort by distance
431 // transform intersections and normals back
432 // accumulate intersections
433 //compile_LinePick_sensor
434 float *segments = NULL; //a segment has 2 points / 6 floats
435 int nseg = 0;
436 float cumdist = 0.0f;
437 int ik, cumcount = 0;
438
439
440 switch(pnode->_nodeType){
441 case NODE_IndexedLineSet:
442 {
443 float *points;
444 int ik;
445 struct X3D_IndexedLineSet *lnode = (struct X3D_IndexedLineSet *)pnode;
446 segments = MALLOC(float*,lnode->coordIndex.n * 2 * 3 * sizeof(float));
447 points = (float*)((struct X3D_Coordinate*)lnode->coord)->point.p;
448 for(ik=0;ik<lnode->coordIndex.n;ik++){
449 if(lnode->coordIndex.p[ik] == -1) continue;
450 if(lnode->coordIndex.p[ik+1] == -1) continue;
451 veccopy3f(&segments[6*nseg +0],&points[3*lnode->coordIndex.p[ik]]);
452 veccopy3f(&segments[6*nseg +1],&points[3*lnode->coordIndex.p[ik+1]]);
453 nseg++;
454 }
455 }
456 break;
457 case NODE_LineSet:
458 {
459 float *points;
460 struct X3D_LineSet *lnode = (struct X3D_LineSet *)pnode;
461 int kk,ik,jk, nn = 0;
462 for(ik=0;ik<lnode->vertexCount.n;ik++)
463 nn += lnode->vertexCount.p[ik];
464 segments = MALLOC(float*,nn * 2 * 3 * sizeof(float));
465 points = (float*)((struct X3D_Coordinate*)lnode->coord)->point.p;
466 kk=0;
467 for(ik=0;ik<lnode->vertexCount.n;ik++){
468 for(jk=0;jk<lnode->vertexCount.p[ik]-1;jk++){
469 veccopy3f(&segments[6*nseg +0],&points[3*(kk+jk)]);
470 veccopy3f(&segments[6*nseg +3],&points[3*(kk+jk+1)]);
471 nseg++;
472 }
473 kk+= lnode->vertexCount.p[ik];
474 }
475
476 }
477 break;
478 default:
479 break;
480 }
481 cumdist = 0.0f;
482 cumcount = 0;
483 for(ik=0;ik<nseg;ik++){
484 float p1[3], p2[3];
485 double dd[3];
486 veccopy3f(p1, &segments[6*ik]);
487 veccopy3f(p2,&segments[6*ik+3]);
488 float2double(dd,p1,3);
489 transformAFFINEd(dd,dd,me2ug);
490 double2float(p1,dd,3);
491 float2double(dd,p2,3);
492 transformAFFINEd(dd,dd,me2ug);
493 double2float(p2,dd,3);
494 //printf("p1,p2 in cylinder space: [%f %f %f][%f %f %f]\n",
495 // p1[0],p1[1],p1[2],p2[0],p2[1],p2[2]);
496 if(intersect_polyrep2(ghit->node, p1, p2, p->stack_intersections )){
497 float delta[3];
498 int jk;
499 for(jk=cumcount;jk<p->stack_intersections->n;jk++){
500 struct intersection_info *iinfo = vector_get_ptr(struct intersection_info,p->stack_intersections,jk);
501 iinfo->dist += cumdist;
502 float2double(dd,iinfo->p,3);
503 transformAFFINEd(dd,dd,u2meg);
504 //in theory normals should be inverse transpose,
505 //we'll cheat here for now, and _you_ the reader, should fix
506 double2float(iinfo->p,dd,3);
507 float2double(dd,iinfo->normal,3);
508 transformUPPER3X3d(dd,dd,u2meg);
509 double2float(iinfo->normal,dd,3);
510 }
511 cumdist += veclength3f(vecdif3f(delta,p2, p1));
512 cumcount = p->stack_intersections->n;
513 }
514
515 }
516 if(cumcount) {
517 struct nodedistance ndist;
518 struct intersection_info *iinfo;
519
520 ndist.node = unode;
521 //for node distance for geometry > lines we'll take the
522 //closest distance along the line that the node intersects
523 qsort(p->stack_intersections->data,p->stack_intersections->n, sizeof(struct intersection_info), compare_intersectiondistance );
524 iinfo = vector_get_ptr(struct intersection_info,p->stack_intersections,0);
525 ndist.dist = iinfo->dist;
526 //stack_push(struct X3D_Node*,p->stack_nodesintersected,unode);
527 stack_push(struct nodedistance,p->stack_nodesdistance,ndist);
528 ishit++;
529 }
530 FREE_IF_NZ(segments);
531 }
532 break;
533 case NODE_PointPickSensor:
534 {
535 //for each picksensor point:
536 // transform into U
537 // create plumbline
538 // intersect plumbline with target geom and see if intersection count is odd
539 // if odd (point inside) accumulate geometry list
540 float *points; //, cumdist;
541 int npoints,cumcount,ik;
542 struct X3D_PointSet *ps = (struct X3D_PointSet*)pnode;
543 struct X3D_Coordinate *cc = (struct X3D_Coordinate *)ps->coord;
544 points = (float*)cc->point.p;
545 npoints = cc->point.n;
546
547 // cumdist = 0.0f;
548 cumcount = 0;
549 for(ik=0;ik<npoints;ik++){
550 float p1[3], p2[4];
551 double dd[3];
552 int ixcount;
553
554 veccopy3f(p1, &points[3*ik]);
555 float2double(dd,p1,3);
556 transformAFFINEd(dd,dd,me2ug);
557 double2float(p1,dd,3);
558 veccopy3f(p2,p1);
559 p2[3] = unode->_extent[4] - 1.0f; //plumbline point must be guaranteed outside target geom
560
561 //printf("p1,p2 in cylinder space: [%f %f %f][%f %f %f]\n",
562 // p1[0],p1[1],p1[2],p2[0],p2[1],p2[2]);
563 if((ixcount = intersect_polyrep2(ghit->node, p1, p2, p->stack_intersections ))){
564 if(ixcount % 2){
565 //if odd number of intersections, then the point is inside
566 struct intersection_info iinfo;
567 float delta[3];
568 double c1[3], pointdist;
569 cumcount++;
570 //stack_push(struct X3D_Node*,p->stack_nodesintersected,unode);
571 vecaddd(c1,memin,memax);
572 vecscaled(c1,c1,.5);
573 double2float(delta,c1,3);
574 pointdist = veclength3f(vecdif3f(delta,delta,p1));
575 iinfo.dist = (float)pointdist;
576 veccopy3f(iinfo.p,&points[3*ik]); //the point inside
577 stack_push(struct intersection_info,p->stack_pointsinside,iinfo);
578 }
579 }
580
581 }
582 if(cumcount) {
583 struct nodedistance ndist;
584 ndist.node = unode;
585 ndist.dist = 0.0; //no distance to node required in specs for pointpicksensor
586 stack_push(struct nodedistance,p->stack_nodesdistance,ndist);
587 ishit++;
588 }
589 }
590 break;
591 case NODE_PrimitivePickSensor:
592 {
593 //for each target geometry vertex:
594 // transform into picksensor space
595 // test if inside geometry
596 // accumulate list
597 struct X3D_PolyRep* pr = (struct X3D_PolyRep*)ghit->node->_intern;
598 if(pr){
599 float *points = pr->actualCoord;
600 int ik, npts = pr->ntri;
601 int cumcount = 0;
602
603 for(ik=0;ik<npts;ik++){
604 double dd[3];
605 float pp[3];
606 float2double(dd,&points[ik*3],3);
607 transformAFFINEd(dd,dd,u2meg);
608 double2float(pp,dd,3);
609 switch(pnode->_nodeType){
610 case NODE_Cone:
611 {
612 float R,H,h,rc,rp;
613 struct X3D_Cone * cone = (struct X3D_Cone*)pnode;
614 H = cone->height;
615 R = cone->bottomRadius;
616 if(pp[1] >= -H/2.0f && pp[1] < H/2.0f){
617 float xz[2];
618 h = pp[1] - (-H/2.0f);
619 rc = R*(1.0f - h/H);
620 xz[0] = pp[0];
621 xz[1] = pp[2];
622 rp = veclength2f(xz);
623 if(rp <= rc){
624 //inside the cone
625 struct intersection_info iinfo;
626 float apex[3], delta[3], conedist;
627 apex[1] = H/2.0f;
628 apex[0] = apex[2] = 0.0f;
629 conedist = veclength3f(vecdif3f(delta,pp,apex));
630 iinfo.dist = conedist;
631 veccopy3f(iinfo.p,pp); //the point inside
632 stack_push(struct intersection_info,p->stack_pointsinside,iinfo);
633 }
634
635 }
636 }
637 break;
638 case NODE_Cylinder:
639 {
640 float R,H,rp;
641 //float h;
642 struct X3D_Cylinder * cyl = (struct X3D_Cylinder*)pnode;
643 H = cyl->height;
644 R = cyl->radius;
645 if(pp[1] >= -H/2.0f && pp[1] < H/2.0f){
646 float xz[2];
647 //h = pp[1] - (-H/2.0f);
648 xz[0] = pp[0];
649 xz[1] = pp[2];
650 rp = veclength2f(xz);
651 if(rp <= R){
652 //inside the cylinder
653 struct intersection_info iinfo;
654 iinfo.dist = veclength3f(pp);
655 veccopy3f(iinfo.p,pp); //the point inside
656 stack_push(struct intersection_info,p->stack_pointsinside,iinfo);
657 }
658 }
659 }
660 break;
661 case NODE_Sphere:
662 {
663 float R,rp;
664 struct X3D_Sphere * sphere = (struct X3D_Sphere*)pnode;
665 R = sphere->radius;
666 rp = veclength3f(pp);
667 if(rp <= R){
668 //inside the sphere
669 struct intersection_info iinfo;
670 iinfo.dist = rp;
671 veccopy3f(iinfo.p,pp); //the point inside
672 stack_push(struct intersection_info,p->stack_pointsinside,iinfo);
673 }
674
675 }
676 break;
677 case NODE_Box:
678 {
679 int inside,im;
680 struct X3D_Box * box = (struct X3D_Box*)pnode;
681 inside = TRUE;
682 for(im=0;im<3;im++)
683 inside = inside && pp[im] >= -box->size.c[im] && pp[im] <= box->size.c[im];
684 if(inside){
685 struct intersection_info iinfo;
686 iinfo.dist = veclength3f(pp);
687 veccopy3f(iinfo.p,pp); //the point inside
688 stack_push(struct intersection_info,p->stack_pointsinside,iinfo);
689 }
690 }
691 break;
692 default:
693 break;
694 }
695 }
696 cumcount = p->stack_pointsinside->n;
697 if(cumcount) {
698 struct nodedistance ndist;
699 struct intersection_info *iinfo;
700 ndist.node = unode;
701 qsort(p->stack_intersections->data,p->stack_intersections->n, sizeof(struct intersection_info), compare_intersectiondistance );
702 iinfo = vector_get_ptr(struct intersection_info,p->stack_intersections,0);
703
704 ndist.dist = iinfo->dist; //no distance to node required in specs for pointpicksensor
705
706 stack_push(struct nodedistance,p->stack_nodesdistance,ndist);
707 ishit++;
708 }
709
710 }
711 }
712 break;
713 case NODE_VolumePickSensor:
714 {
715 //for each target geometry vertex
716 // transform into picksensor space
717 // make plubline
718 // test if intersections of plubline with pickgeometry are odd
719 // if odd (inside) accumulate list
720 struct X3D_PolyRep* pr = (struct X3D_PolyRep*)ghit->node->_intern;
721
722 if(pr){
723 float *points = pr->actualCoord;
724 int npts = pr->ntri;
725 int ik,cumcount = 0;
726
727 for(ik=0;ik<npts;ik++){
728 double dd[3];
729 float pp[3];
730 float2double(dd,&points[ik*3],3);
731 transformAFFINEd(dd,dd,u2meg);
732 double2float(pp,dd,3);
733 {
734 float p1[3], p2[4];
735 //double dd[3];
736 int ixcount;
737 veccopy3f(p1, pp);
738 veccopy3f(p2,p1);
739 p2[3] = menode->_extent[4] - 1.0f; //plumbline point must be guaranteed outside target geom
740
741 //printf("p1,p2 in cylinder space: [%f %f %f][%f %f %f]\n",
742 // p1[0],p1[1],p1[2],p2[0],p2[1],p2[2]);
743 if((ixcount = intersect_polyrep2(pnode, p1, p2, p->stack_intersections ))){
744 if(ixcount % 2){
745 //if odd number of intersections, then the point is inside
746 struct intersection_info iinfo;
747 float delta[3];
748 double c1[3], pointdist;
749 cumcount++;
750 //stack_push(struct X3D_Node*,p->stack_nodesintersected,unode);
751 vecaddd(c1,memin,memax);
752 vecscaled(c1,c1,.5);
753 double2float(delta,c1,3);
754 pointdist = veclength3f(vecdif3f(delta,delta,p1));
755 iinfo.dist = (float) pointdist;
756 veccopy3f(iinfo.p,&points[3*ik]); //the point inside
757 stack_push(struct intersection_info,p->stack_pointsinside,iinfo);
758 }
759 }
760 }
761
762 }
763 cumcount = p->stack_pointsinside->n;
764 if(cumcount) {
765 struct nodedistance ndist;
766 struct intersection_info *iinfo;
767 ndist.node = unode;
768 qsort(p->stack_intersections->data,p->stack_intersections->n, sizeof(struct intersection_info), compare_intersectiondistance );
769 iinfo = vector_get_ptr(struct intersection_info,p->stack_intersections,0);
770
771 ndist.dist = iinfo->dist; //no distance to node required in specs for pointpicksensor
772
773 stack_push(struct nodedistance,p->stack_nodesdistance,ndist);
774 ishit++;
775 }
776
777 }
778
779
780 }
781 break;
782 default:
783 break; //not for me
784 }
785 } //for each subscenegraph node
786 } //end else GEOMETRY
787 } //if overlap
788 } //if intypes and pickable
789 } //while uhit
790 } //for unodes
791 } //while mehit
792 if(ishit){
793 if (!node->isActive) {
794 #ifdef SEVERBOSE
795 printf ("transformensor - now active\n");
796 #endif
797
798 node->isActive = 1;
799 MARK_EVENT (ptr, offsetof(struct X3D_PrimitivePickSensor, isActive));
800 }
801 //sort by sortOrder
802 if(!strcmp(node->sortOrder->strptr,"ANY")){
803 struct nodedistance *ndist;
804 node->pickedGeometry.p = realloc(node->pickedGeometry.p,1 * sizeof(struct X3D_Node*));
805 ndist = vector_get_ptr(struct nodedistance,p->stack_nodesdistance,0);
806 node->pickedGeometry.p[0] = ndist->node;
807 }else if(!strcmp(node->sortOrder->strptr,"ALL")){
808 int ii;
809 struct nodedistance *ndist;
810 node->pickedGeometry.p = realloc(node->pickedGeometry.p,p->stack_nodesdistance->n * sizeof(struct X3D_Node*));
811 for(ii=0;ii<p->stack_nodesdistance->n;ii++){
812 ndist = vector_get_ptr(struct nodedistance,p->stack_nodesdistance,0);
813 node->pickedGeometry.p[ii] = ndist->node;
814 }
815 //memcpy(node->pickedGeometry.p,p->stack_nodesintersected->data,p->stack_nodesintersected->n*sizeof(struct X3D_Node*));
816 }else if(!strcmp(node->sortOrder->strptr,"ALL_SORTED")){
817
818 //int compare( (void *) & elem1, (void *) & elem2 );
819 //stdlib.h
820 //void qsort(
821 // void *base,
822 // size_t num,
823 // size_t width,
824 // int (__cdecl *compare )(const void *, const void *)
825 //);
826 struct nodedistance *ndist;
827 int ii;
828
829 qsort(p->stack_nodesdistance->data,p->stack_nodesdistance->n, sizeof(struct nodedistance), compare_nodedistance );
830 node->pickedGeometry.p = realloc(node->pickedGeometry.p,p->stack_nodesdistance->n * sizeof(struct X3D_Node*));
831 for(ii=0;ii<p->stack_nodesdistance->n;ii++){
832 ndist = vector_get_ptr(struct nodedistance,p->stack_nodesdistance,0);
833 node->pickedGeometry.p[ii] = ndist->node;
834 }
835 }else if(!strcmp(node->sortOrder->strptr,"CLOSEST")){
836 struct nodedistance *ndist;
837
838 qsort(p->stack_nodesdistance->data,p->stack_nodesdistance->n, sizeof(struct nodedistance), compare_nodedistance );
839 node->pickedGeometry.p = realloc(node->pickedGeometry.p,1 * sizeof(struct X3D_Node*));
840 ndist = vector_get_ptr(struct nodedistance,p->stack_nodesdistance,0);
841 node->pickedGeometry.p[0] = ndist->node;
842 }
843 MARK_EVENT(ptr,offsetof(struct X3D_PrimitivePickSensor, pickedGeometry));
844 //MARK_EVENT - pickedGeometry (all)
845 //MARK_EVENT - pickedPoint (line and point)
846 //MARK_EVENT - pickedNormal (line)
847 //MARK_EVENT - pickedTextureCoordinate (line)
848 switch(node->_nodeType){
849 case NODE_LinePickSensor:
850 {
851 int ik;
852 struct X3D_LinePickSensor *lnode = (struct X3D_LinePickSensor *)node;
853 lnode->pickedPoint.n = p->stack_intersections->n;
854 lnode->pickedNormal.n = p->stack_intersections->n;
855 lnode->pickedTextureCoordinate.n = p->stack_intersections->n;
856 lnode->pickedPoint.p = realloc(lnode->pickedPoint.p,lnode->pickedPoint.n * 3 * sizeof(float));
857 lnode->pickedNormal.p = realloc(lnode->pickedNormal.p,lnode->pickedPoint.n * 3 * sizeof(float));
858 lnode->pickedTextureCoordinate.p = realloc(lnode->pickedTextureCoordinate.p,lnode->pickedPoint.n * 3 * sizeof(float));
859 for(ik=0;ik<p->stack_intersections->n;ik++){
860 struct intersection_info *iinfo = vector_get_ptr(struct intersection_info,p->stack_intersections,ik);
861 veccopy3f(lnode->pickedPoint.p[ik].c,iinfo->p);
862 veccopy3f(lnode->pickedNormal.p[ik].c,iinfo->normal);
863 veccopy3f(lnode->pickedTextureCoordinate.p[ik].c,iinfo->texcoord);
864 }
865 MARK_EVENT(ptr,offsetof(struct X3D_LinePickSensor, pickedPoint));
866 MARK_EVENT(ptr,offsetof(struct X3D_LinePickSensor, pickedNormal));
867 MARK_EVENT(ptr,offsetof(struct X3D_LinePickSensor, pickedTextureCoordinate));
868 }
869 break;
870 case NODE_PointPickSensor:
871 {
872 //send points that were inside
873 int ik;
874 struct X3D_PointPickSensor *lnode = (struct X3D_PointPickSensor *)node;
875 lnode->pickedPoint.n = p->stack_pointsinside->n;
876 lnode->pickedPoint.p = realloc(lnode->pickedPoint.p,lnode->pickedPoint.n * 3 * sizeof(float));
877 for(ik=0;ik<p->stack_pointsinside->n;ik++){
878 struct intersection_info *iinfo = vector_get_ptr(struct intersection_info,p->stack_pointsinside,ik);
879 veccopy3f(lnode->pickedPoint.p[ik].c,iinfo->p);
880 }
881 MARK_EVENT(ptr,offsetof(struct X3D_LinePickSensor, pickedPoint));
882 }
883 break;
884 case NODE_PrimitivePickSensor:
885 //just the geometry list
886 case NODE_VolumePickSensor:
887 //just the geometry list
888 default:
889 break;
890 }
891
892 }
893 if(!ishit){
894 if (node->isActive) {
895 #ifdef SEVERBOSE
896 printf ("transformsensor - going inactive\n");
897 #endif
898
899 node->isActive = 0;
900 MARK_EVENT (ptr, offsetof(struct X3D_PrimitivePickSensor, isActive));
901 }
902 }
903
904 //ask this node, and target nodes to save their modelviewmatrix for each USE,
905 //..when visited, on the upcoming frame
906 for(i=0;i<unodes->n;i++)
907 unodes->p[i]->_renderFlags |= VF_USE;
908 } //if targets
909 node->_renderFlags |= VF_USE;
910}