FreeWRL/FreeX3D  3.0.0
Component_Picking.c
1 /*
2 
3 
4 X3D 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 
55 struct nodedistance {
56  struct X3D_Node* node;
57  float dist;
58 };
59 
60 typedef struct pComponent_Picking{
61  //Stack *stack_nodesintersected;
62  Stack *stack_nodesdistance;
63  Stack *stack_intersections;
64  Stack *stack_pointsinside;
66 void *Component_Picking_constructor(){
67  void *v = MALLOCV(sizeof(struct pComponent_Picking));
68  memset(v,0,sizeof(struct pComponent_Picking));
69  return v;
70 }
71 void 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 */
87 void other_PointPickSensor (struct X3D_PointPickSensor *node) {}
88 void other_PickableGroup (struct X3D_Group *node) {}
89 void other_Sphere (struct X3D_Sphere *node) {}
90 //void child_PickableGroup (struct X3D_Group *node) {}
91 void prep_PickableGroup (struct X3D_Group *node) {}
92 void add_picksensor(struct X3D_Node * node) {}
93 void remove_picksensor(struct X3D_Node * node) {}
94 
95 void push_pickablegroupdata(void *userdata);
96 void pop_pickablegroupdata();
97 void 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 
119 int overlapMBBs(GLDOUBLE *MBBmin1, GLDOUBLE *MBBmax1, GLDOUBLE *MBBmin2, GLDOUBLE* MBBmax2);
120 void transformMBB(GLDOUBLE *rMBBmin, GLDOUBLE *rMBBmax, GLDOUBLE *matTransform, GLDOUBLE* inMBBmin, GLDOUBLE* inMBBmax);
121 /*
122 A. 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 
133 B. New theory of operation for PickSensors (July 2016, dug9):
134 1. 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)
140 2. 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)
150 3. 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
162 HARD 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 */
177 int 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 }
207 int 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
223 int 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 }
229 int 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 }
234 void 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 }
Definition: Vector.h:36