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