FreeWRL/FreeX3D  3.0.0
Component_ParticleSystems.c
1 /*
2 
3 
4 X3D Particle Systems 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 "RenderFuncs.h"
51 #include "LinearAlgebra.h"
52 //#include "Component_ParticleSystems.h"
53 #include "Children.h"
54 #include "Component_Shape.h"
55 #include "../opengl/Textures.h"
56 
58  int something;
60 void *Component_ParticleSystems_constructor(){
61  void *v = MALLOCV(sizeof(struct pComponent_ParticleSystems));
62  memset(v,0,sizeof(struct pComponent_ParticleSystems));
63  return v;
64 }
65 void Component_ParticleSystems_init(struct tComponent_ParticleSystems *t){
66  //public
67  //private
68  t->prv = Component_ParticleSystems_constructor();
69  {
71  p->something = 0;
72  }
73 }
74 void Component_ParticleSystems_clear(struct tComponent_ParticleSystems *t){
75  //public
76 }
77 
78 //ppComponent_ParticleSystems p = (ppComponent_ParticleSystems)gglobal()->Component_ParticleSystems.prv;
79 
80 /* Particle Systems
81  http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html
82  examples:
83  Non- x3d:
84  https://stemkoski.github.io/Three.js/#particlesystem-shader
85  x3d scenes:
86  links:
87  http://mmaklin.com/uppfra_preprint.pdf
88  Nice particle physics
89  http://www.nvidia.com/object/doc_characters.html
90  Nvidia link page for game programmming with shaders
91 
92  Fuzzy Design:
93  1. Update position of particles from a particleSystem node
94  Eval particles after events (after the do_tick) and befre RBP physics
95  see http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/concepts.html#ExecutionModel
96  positions are update wrt the local system of the particleSystme node
97  each particle has a struct { lifetime remaining, position, velocity vector, ??}
98  up to 10,000 particles (per particle node)
99  randomizing: use C srand(time) once, and rand() for each randomizing. Scale by Variation field
100  CPU design: iterate over particles, updating each one
101  GPU design ie openCL: do same thing in massive parallel
102  2. Render
103  during geom pass of render_hier, in render_particleSystem()
104  CPU design: iterate over particles like children:
105  updating transform stack with particle position
106  updating appearance f(time)
107  calling render_node(node) on each particle
108  GPU design: send arrray of particle positions/states to GPU
109  in shader iterate over positions, re-rendering for each
110 
111  PROBLEM with trying to do physics in shader:
112  x how do you update the state of each particle in a way the next frame can access?
113  vs on cpu, if you have 80 particles, you can whip through them, updating their state,
114  - and resending the state via attribute array on each frame
115  - also more flexible when doing geometryType="GOEMETRY" / goemetry node, that code is CPU
116 
117  PROBLEM with sending just the particle position, and generating the sprite geometry
118  in the shader: GLES2 doesn't have gometry shaders.
119 
120  GLES2 has no gl_VertexID per vertex in vertex shader, so:
121  - send glAttributeArray of xyz positions to match vertices?
122  - send repetitive triangles - same 3 xyz repetitively?
123  - in shader add position to triangle verts?
124  - or send glAttributeArray of sprite ID of length nvert
125  -- and uniform3fv of xyz of length nsprite
126  -- then lookup xyz[spriteID] in vertex shader?
127 
128  EASIEST CPU/GPU SPLIT:
129  1. just send geometry for 1 particle to shader
130  2. cpu loop over particles:
131  foreach liveparticle
132  send position to shader
133  send texcoord to shader
134  send cpv to shader
135  gl_DrawArrays
136 
137  POSITION
138  because position is just xyz (not orientation or scale) the shader could
139  take a vec3 for that, and add it on before transforming from local to view
140  for non-GEOMETRY, the transform needs to keep the face normal parallel to the view Z
141  H: you could do that by transforming 0,0,0 to view, and adding on gl_vertex
142  x but that wouldn't do scale, or orientation if you need it
143  - for scale also transform gl_vertex, get the |diff| from 0 for scale
144 
145  PHYSICS - I don't see any rotational momentum needs, which involve cross products
146  - so forces F, positions p, velocities v, accelerations a are vec3
147  - mass, time are scalars
148  - physics:
149  F = m * a
150  a = F/m
151  v2 = v1 + a*dt
152  p2 = p1 + .5(v1 + v2)*dt
153  p2 = p1 + v1*dt + .5*a*dt**2
154  p - position
155  v - velocity
156  a - acceleration
157  dt - delta time = (time2 - time1)
158  m - mass
159  F - force
160 
161  RANDOM DIRECTIONS
162  http://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d
163 
164  RANDOM TRIANGLE COORDS
165  picking a random triangle won't evenly distribute by area, so we can be approx with point inside tri too
166  can pick 2 0-1 range numbers, and use as barycentric coords b1,b2:
167  https://en.wikipedia.org/wiki/Barycentric_coordinate_system
168  p = b1*p1 + b2*p2 + (1 - b1 - b2)*p3
169 
170  COMPARISONS - H3D, Octaga, Xj3D claim particle physics
171  - Octaga responds to particle size, has good force and wind effects
172 
173 Its like a Shape node, or is a shape node,
174 set geometry
175 set basic appearance
176 foreach liveparticle
177  update texcoord
178  update color (color per vertex)
179  update position
180  gl_DrawArrays or gl_DrawElements
181 
182 Options in freewrl:
183 1. per-particle child_Shape
184  foreach liveparticle
185  push particle position onto transform stack
186  fiddle with appearance node on child
187  call child_Shape(particle)
188 2. per-particle-system child_Shape
189  call child_Shape
190  if(geometryType 'GEOMETRY')
191  .... sendArraysToGPU (hack)
192  foreach liveparticle
193  update position
194  glDrawArrays
195  .... sendElementsToGPU (hack)
196  foreach liveparticle
197  update position
198  glDrawElements
199  if(geometryType !GEOMETRY)
200  send vbo with one line or quad or 2 triangles or point
201  foreach liveparticle
202  update position
203  update texcoord
204  update color (color per vertex)
205  gl_DrawArrays or gl_DrawElements
206 3. refactor child shape to flatten the call hierarchy
207  child shape:
208  a) determine shader flags
209  b) compile/set/bind shader program
210  c) set appearance - pull setupShader out of sendArraysToGPU
211  set material
212  set texture
213  set shader
214  ...
215  d) set geometry vertices and type element/array, line/triangle
216  if GEOMETRY: render(geom node) - except don't call gl_DrawArrays or gl_DrawElements
217  PROBLEM: some geometries -cylinder, cone- are made from multiple calls to gl_DrawElements
218  OPTION: refactor so uses polyrep, or single permuatation call
219  (cylinder: 4 permutations: full, bottom, top, no-ends)
220  else !GEOMETRY: send one quad/line/point/triangle
221  e) foreach liveparticle
222  update position
223  update texcoord
224  update color (CPV)
225  gl_Draw xxx: what was set in d) above
226 4. half-flatten child_shape
227  as in #3, except just take setupShader out of sendArraysToGPU/sendElementsToGPU, and put in
228  child_shape body
229  then child_particlesystem can be a copy of child_shape, with loop over particles
230  calling render_node(geometry) for GEOMETRY type (resending vbos)
231 5. make sendArrays etc a callback function, different for particles
232 CHOICE: #3
233  setup shader //sends materials, matrices to shader
234  render_node(geometry) //sends vertex data to shader, saves call parameters to gl_DrawArrays/Elements
235  foreach liveparticle
236  update particle-specific position, color, texcoords
237  reallyDrawOnce() //calls glDrawArrays or Elements
238  clearDraw()
239 
240 */
241 
242 
243 float uniformRand(){
244  // 0 to 1 inclusive
245  static int once = 0;
246  unsigned int ix;
247  float rx;
248 
249  if(!once)
250  srand((unsigned int) TickTime());
251  once = 1;
252  ix = rand();
253  rx = (float)ix/(float)RAND_MAX; //you would do (RAND_MAX - 1) here for excluding 1
254  return rx;
255 }
256 float uniformRandCentered(){
257  return uniformRand() - .5f; //center on 0
258 }
259 void circleRand2D(float *xy){
260  //random xy on a circle area radius 1
261  float radius2;
262  for(;;){
263  xy[0] = 2.0f*(uniformRand() - .5f);
264  xy[1] = 2.0f*(uniformRand() - .5f);
265  radius2 = xy[0]*xy[0] + xy[1]*xy[1];
266  if(radius2 <= 1.0f) break;
267  }
268 }
269 float normalRand(){
270  // in -.5 to .5 range
271  float rxy[2];
272  // by just taking points in a circle radius, this emulates the falloff of a normal curve in one dimension
273  // .
274  // . x .
275  // : :
276  // . .
277  // . o
278  //
279  circleRand2D(rxy);
280  return (float)(rxy[0]*.5); //scale from -1 to 1 into -.5 to .5 range
281 }
282 
283 void randomTriangleCoord_dug9_uneducated_guess(float *p, float* p1, float *p2, float *p3){
284  // get 2 random barycentric coords 0-1, and use those
285  // https://en.wikipedia.org/wiki/Barycentric_coordinate_system
286  // x I think b1 + b2 can be > 1.0 and thats wrong
287  int i;
288  float b1, b2;
289  b1 = uniformRand();
290  b2 = uniformRand();
291  for(i=0;i<3;i++){
292  p[i] = b1*p1[i] + b2*p2[i] + (1.0f - b1 - b2)*p3[i];
293  }
294 }
295 void randomTriangleCoord(float *p, float* p1, float *p2, float *p3){
296  // http://math.stackexchange.com/questions/18686/uniform-random-point-in-triangle
297  int i;
298  float r1, r2, sqr1,sqr2;
299  r1 = uniformRand();
300  r2 = uniformRand();
301  sqr1 = sqrtf(r1);
302  sqr2 = sqrtf(r2);
303  for(i=0;i<3;i++){
304  p[i] = (1.0f - sqr1)*p1[i] + (sqr1*(1.0f - sqr2))*p2[i] + (r2*sqr1)*p3[i];
305  }
306 
307 }
308 void randomPoint3D(float *xyz){
309  //- .5 to .5 range
310  xyz[0] = (uniformRand() - .5f);
311  xyz[1] = (uniformRand() - .5f);
312  xyz[2] = (uniformRand() - .5f);
313 }
314 void randomDirection(float *xyz){
315  //random xyz direction from a point
316  //http://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d
317  float radius3;
318  for(;;){
319  //get random point in a unit cube
320  //xyz[0] = (uniformRand() - .5f);
321  //xyz[1] = (uniformRand() - .5f);
322  //xyz[2] = (uniformRand() - .5f);
323  randomPoint3D(xyz);
324  //discard point if outside unit sphere
325  radius3 = xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2];
326  if(radius3 <= 1.0f && radius3 > 0.0000001f){
327  //vecnormalize3f(xyz,xyz);
328  //normalize direction to point
329  vecscale3f(xyz,xyz,1.0f/sqrtf(radius3));
330  break;
331  }
332  }
333 }
334 
335 typedef struct {
336  //store at end of current iteration, for use on next iteration
337  float age;
338  float lifespan; //assigned at birth
339  float size[2]; //assigned at birth
340  float position[3];
341  float velocity[3];
342  float origin[3]; //zero normally. For boundedphysics, updated on each reflection to be last reflection point.
343  //float direction[3];
344  //float speed;
345  float mass;
346  float surfaceArea;
347 } particle;
348 enum {
349  GEOM_QUAD = 1,
350  GEOM_LINE = 2,
351  GEOM_POINT = 3,
352  GEOM_SPRITE = 4,
353  GEOM_TRIANGLE = 5,
354  GEOM_GEOMETRY = 6,
355 };
356 struct {
357 const char *name;
358 int type;
359 } geomtype_table [] = {
360 {"QUAD",GEOM_QUAD},
361 {"LINE",GEOM_LINE},
362 {"POINT",GEOM_POINT},
363 {"SPRITE",GEOM_SPRITE},
364 {"TRIANGLE",GEOM_TRIANGLE},
365 {"GEOMETRY",GEOM_GEOMETRY},
366 {NULL,0},
367 };
368 int lookup_geomtype(const char *name){
369  int iret,i;
370  iret=i=0;
371  for(;;){
372  if(geomtype_table[i].name == NULL) break;
373  if(!strcmp(geomtype_table[i].name,name)){
374  iret = geomtype_table[i].type;
375  break;
376  }
377  i++;
378  }
379  return iret;
380 }
381 //GLfloat quadtris [18] = {1.0f,1.0f,0.0f, -1.0f,1.0f,0.0f, -1.0f,-1.0f,0.0f, 1.0f,1.0f,0.0f, -1.0f,-1.0f,0.0f, 1.0f,-1.0f,0.0f};
382 GLfloat quadtris [18] = {-.5f,-.5f,0.0f, .5f,-.5f,0.0f, .5f,.5f,0.0f, .5f,.5f,0.0f, -.5f,.5f,0.0f, -.5f,-.5f,0.0f,};
383 GLfloat twotrisnorms [18] = {0.f,0.f,1.f, 0.f,0.f,1.f, 0.f,0.f,1.f, 0.f,0.f,1.f, 0.f,0.f,1.f, 0.f,0.f,1.f,};
384 GLfloat twotristex [12] = {0.f,0.f, 1.f,0.f, 1.f,1.f, 1.f,1.f, 0.f,1.f, 0.f,0.f};
385 
386 void compile_Shape (struct X3D_Shape *node);
387 // COMPILE PARTICLE SYSTEM
388 void compile_ParticleSystem(struct X3D_ParticleSystem *node){
389  int i,j, maxparticles;
390  float *vertices; //*boxtris,
391  Stack *_particles;
392 
393  ConsoleMessage("compile_particlesystem\n");
394  //delegate to compile_shape - same order to appearance, geometry fields
395  compile_Shape((struct X3D_Shape*)node);
396 
397  node->_geometryType = lookup_geomtype(node->geometryType->strptr);
398  if(node->_tris == NULL){
399  node->_tris = MALLOC(void *,18 * sizeof(float));
400  //memcpy(node->_tris,quadtris,18*sizeof(float));
401  }
402  vertices = (float*)(node->_tris);
403  //rescale vertices, in case scale changed
404  for(i=0;i<6;i++){
405  float *vert, *vert0;
406  vert0 = &quadtris[i*3];
407  vert = &vertices[i*3];
408  vert[0] = vert0[0]*node->particleSize.c[0];
409  vert[1] = vert0[1]*node->particleSize.c[1];
410  vert[2] = vert0[2];
411  }
412  if(node->texCoordRamp){
413  int ml,mq,mt,n;
414  struct X3D_TextureCoordinate *tc = (struct X3D_TextureCoordinate *)node->texCoordRamp;
415  n = node->texCoordKey.n;
416  mq = n*4; //quad
417  ml = n*2; //2 pt line
418  mt = n*6; //2 triangles
419 
420  //malloc for both lines and tex, in case changed on the fly
421  if(!node->_ttex)
422  node->_ttex = MALLOC(void *,mt*2*sizeof(float));
423  if(!node->_ltex)
424  node->_ltex = MALLOC(void *,ml*2*sizeof(float));
425  if(tc->point.n == mq){
426  //enough tex coords for quads, expand to suit triangles
427  // 4 - 3
428  // 5 / 2 2 triangle config
429  // 0 _ 1
430  float *ttex, *ltex;
431  ttex = (float*)node->_ttex;
432  for(i=0;i<n;i++){
433  int k;
434  for(j=0,k=0;j<4;j++,k++){
435  float *p = (float*)(float *)&tc->point.p[i*4 + j];
436  veccopy2f(&ttex[(i*6 + k)*2],p);
437  if(k==0){
438  veccopy2f(&ttex[(i*6 + 5)*2],p); //copy to 5 (last of 0-6 2-triangle)
439  }
440  if(k==2){
441  k++;
442  veccopy2f(&ttex[(i*6 + k)*2],p); //copy 2 to 3 (start of 2nd triangle
443  }
444  }
445  }
446  if(0) for(i=0;i<n;i++){
447  for(j=0;j<6;j++)
448  printf("%f %f,",ttex[(i*6 + j)*2 +0],ttex[(i*6 + j)*2 +1]);
449  printf("\n");
450  }
451  //for(i=0;i<(n*6*2);i++){
452  // printf("%f \n",ttex[i]);
453  //}
454 
455  ltex = (float*)node->_ltex;
456  for(i=0;i<n;i++){
457  // make something up for lines
458  for(j=0;j<2;j++){
459  float p[2];
460  struct SFVec2f *sf = (struct SFVec2f *)&tc->point.p[i*4 + j];
461  p[0] = sf->c[0];
462  p[1] = min(sf->c[1],.9999f); //clamp texture here otherwise tends to wrap around
463  veccopy2f(&ltex[(i*2 + j)*2],p);
464 
465  }
466  }
467  }
468  if(tc->point.n == ml){
469  //enough points for lines
470  float *ttex, *ltex;
471 
472  ltex = (float*)node->_ltex;
473  for(i=0;i<n;i++){
474  // copy lines straightforwardly
475  for(j=0;j<2;j++){
476  float p[2];
477  struct SFVec2f *sf = (struct SFVec2f *)&tc->point.p[i*2 + j];
478  p[0] = sf->c[0];
479  p[1] = min(sf->c[1],.9999f); //clamp texture here otherwise tends to wrap around
480  veccopy2f(&ltex[(i*2 + j)*2],p);
481  }
482  }
483  if(0) for(i=0;i<n;i++){
484  printf("%f %f, %f %f\n",ltex[i*2*2 + 0],ltex[i*2*2 + 1],ltex[i*2*2 + 2],ltex[i*2*2 + 3]);
485  }
486  //make something up for triangles
487  ttex = (float*)node->_ttex;
488  for(i=0;i<n;i++){
489  float *p;
490  j = i;
491  p = (float*)(float *)&tc->point.p[j*2 + 0];
492  veccopy2f(&ttex[(i*6 + 0)*2],p); //copy to 0
493  veccopy2f(&ttex[(i*6 + 5)*2],p); //copy to 5
494  p = (float*)(float *)&tc->point.p[j*2 + 1];
495  veccopy2f(&ttex[(i*6 + 1)*2],p); //copy to 1
496  j++;
497  j = j == n ? j - 1 : j; //clamp to last
498  p = (float*)(float *)&tc->point.p[j*2 + 1];
499  veccopy2f(&ttex[(i*6 + 2)*2],p); //copy to 2
500  veccopy2f(&ttex[(i*6 + 3)*2],p); //copy to 3
501  p = (float*)(float *)&tc->point.p[j*2 + 0];
502  veccopy2f(&ttex[(i*6 + 4)*2],p); //copy to 4
503  }
504  }
505  }
506  maxparticles = min(node->maxParticles,10000);
507  if(node->_particles == NULL)
508  node->_particles = newVector(particle,maxparticles);
509  _particles = node->_particles;
510  if(_particles->allocn < maxparticles) {
511  //resize /realloc vector, set nalloc, in case someone changed maxparticles on the fly
512  _particles->data = realloc(_particles->data,maxparticles);
513  _particles->allocn = maxparticles;
514  }
515  node->_lasttime = TickTime();
516  if(node->enabled){
517  node->isActive = TRUE;
518  MARK_EVENT (X3D_NODE(node),offsetof (struct X3D_ParticleSystem, isActive));
519  }
520  MARK_NODE_COMPILED
521 }
522 
523 //PHYSICS
524 void prep_windphysics(struct X3D_Node *physics){
525  //per-frame gustiness update
526  struct X3D_WindPhysicsModel *px = (struct X3D_WindPhysicsModel *)physics;
527  float speed;
528  speed = px->speed * (1.0f + uniformRandCentered()*px->gustiness);
529  px->_frameSpeed = speed;
530 }
531 void apply_windphysics(particle *pp, struct X3D_Node *physics, float dtime){
532  // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#WindPhysicsModel
533  struct X3D_WindPhysicsModel *px = (struct X3D_WindPhysicsModel *)physics;
534  if(px->enabled && pp->mass != 0.0f){
535  float pressure;
536  float force, speed;
537  float turbdir[3], pdir[3],acceleration[3], v2[3];
538  speed = px->_frameSpeed;
539  pressure = powf(10.0f,2.0f*log10f(speed)) * .64615f;
540  force = pressure * pp->surfaceArea;
541  randomDirection(turbdir);
542  vecscale3f(turbdir,turbdir,px->turbulence);
543  vecadd3f(pdir,px->direction.c,turbdir);
544  vecnormalize3f(pdir,pdir);
545  vecscale3f(pdir,pdir,force);
546 
547  vecscale3f(acceleration,pdir,1.0f/pp->mass);
548  vecscale3f(v2,acceleration,dtime);
549  vecadd3f(pp->velocity,pp->velocity,v2);
550 
551  }
552 }
553 int intersect_polyrep(struct X3D_Node *node, float *p1, float *p2, float *nearest, float *normal);
554 
555 void compile_geometry(struct X3D_Node *gnode){
556  //this needs generalizing, for all triangle geometry, and I don't know how
557  if(gnode)
558  switch(gnode->_nodeType){
559  case NODE_IndexedFaceSet:
560  {
561  struct X3D_IndexedFaceSet *node = (struct X3D_IndexedFaceSet *)gnode;
562  COMPILE_POLY_IF_REQUIRED (node->coord, node->fogCoord, node->color, node->normal, node->texCoord)
563  }
564  break;
565  default:
566  break;
567  }
568 
569 }
570 int intersect_geometry(struct X3D_Node *gnode, float *p1, float *p2, float *nearest, float *normal){
571  //this needs generalizing for all triangle geometry
572  int iret = 0;
573  if(gnode)
574  switch(gnode->_nodeType){
575  case NODE_IndexedFaceSet:
576  iret = intersect_polyrep(gnode,p1,p2,nearest,normal);
577  break;
578  default:
579  break;
580  }
581  return iret;
582 }
583 void apply_boundedphysics(particle *pp, struct X3D_Node *physics, float *positionChange){
584  struct X3D_BoundedPhysicsModel *px = (struct X3D_BoundedPhysicsModel *)physics;
585  if(px->enabled && px->geometry ) { //&& pp->mass != 0.0f){
586  //shall we assume its a 100% elastic bounce?
587  int nintersections;
588  // int ntries;
589  struct X3D_Node *node = (struct X3D_Node *) px->geometry;
590  float pos1[3], pos2[3], pnearest[3],normal[3], delta[3];
591  static int count;
592 
593  //make_IndexedFaceSet((struct X3D_IndexedFaceSet*)px->geometry);
594  //COMPILE_POLY_IF_REQUIRED (node->coord, node->fogCoord, node->color, node->normal, node->texCoord)
595  if(NODE_NEEDS_COMPILING)
596  compile_geometry(node);
597 
598  //if polyrep
599  //veccopy3f(pos1,pp->position);
600 
601  veccopy3f(pos1,pp->origin);
602  //vecadd3f(pos2,pp->position,positionChange);
603  vecadd3f(pos2,pp->position,positionChange);
604  //ntries = 0;
605  count = 0;
606  //for(;;) //in theory we may travel far enough for 2 bounces
607  {
608  //if(pos2[0] >= .5f)
609  // printf("should hit\n");
610  nintersections = intersect_geometry(px->geometry,pos1,pos2,pnearest,normal);
611  if(nintersections > 0){
612  float d[3], r[3], rn[3], rd[3], n[3], ddotnn[3], orthogn[3], orthogn2[3];
613  float ddotn, speed, dlengthi,dlength;
614  vecdif3f(delta,pos2,pos1);
615  dlength = veclength3f(delta);
616  count++;
617 
618  // get reflection
619  // pos1
620  // o|\d
621  // n<--x
622  // o|/r
623  // ddotn = dot(d,n)*n //projection of d onto n, as vector
624  // o = d - ddotn //vector orthogonal to n, such that d = ddotn + o
625  // r = ddotn - o
626  // or
627  // r = ddotn - (d - ddotn)
628  // or
629  // r = 2*ddotn - d
630  // or
631  // r = -(d - 2*dot(d,n)*n)
632  // http://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector
633 
634  vecdif3f(d,pnearest,pos1);
635  dlengthi = veclength3f(d);
636  vecnormalize3f(n,normal);
637  ddotn = vecdot3f(d,n);
638  //ddotn = -ddotn; //assuming the surface normal is pointing out
639  vecscale3f(ddotnn,n,ddotn);
640  vecdif3f(orthogn,d,ddotnn);
641  vecscale3f(orthogn2,orthogn,2.0f);
642  vecdif3f(r,d,orthogn2);
643  vecscale3f(r,r,-1.0f); //reverse direction
644  vecnormalize3f(rn,r);
645  // update the velocity vector direction (keep speed constant, assuming elastic bounce)
646  // specs: could use an elasticity factor
647  speed = veclength3f(pp->velocity);
648  vecscale3f(pp->velocity,rn,speed);
649  //do positionChange here, and zero positionchange for calling code
650  vecscale3f(rd,rn,dlength - dlengthi);
651  vecadd3f(pp->position,pnearest,rd);
652  vecscale3f(positionChange,positionChange,0.0f);
653  veccopy3f(pos1,pnearest);
654  veccopy3f(pp->origin,pos1);
655  veccopy3f(pos2,pp->position);
656  if(0) pp->age = 1000.0f; //simply expire if / when goes outside
657  // specs could add death-on-hitting-wall
658  }
659  //break;
660  //if(nintersections == 0)break;
661  //ntries++;
662  //if(ntries > 3)break;
663  }
664  }
665 }
666 void apply_forcephysics(particle *pp, struct X3D_Node *physics, float dtime){
667  // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#ForcePhysicsModel
668  // I think Octaga mis-interprets the [0 -9.81 0] force as acceleartion of gravity.
669  // it will be if mass==1. Otherwise you need to scale your force by mass:
670  // ie if your mass is 10, then force needs to be [0 98.1 0]
671  struct X3D_ForcePhysicsModel *px = (struct X3D_ForcePhysicsModel *)physics;
672  //a = F/m;
673  //v += a*dt
674  if(px->enabled && pp->mass != 0.0f){
675  float acceleration[3], v2[3];
676  vecscale3f(acceleration,px->force.c,1.0f/pp->mass);
677  vecscale3f(v2,acceleration,dtime);
678  vecadd3f(pp->velocity,pp->velocity,v2);
679  }
680 }
681 
682 //EMITTERS
683 void apply_ConeEmitter(particle *pp, struct X3D_Node *emitter){
684  // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#ConeEmitter
685  // like point emitter, except we need work in the direction:
686  // 2 randoms, one for angle-from-direction < e.angle, and one for angle-around-direction 0-2PI
687  struct X3D_ConeEmitter *e = (struct X3D_ConeEmitter *)emitter;
688  float direction[3], tilt, azimuth, speed;
689  {
690  //prep - can be done once per direction
691  //need 2 orthogonal axes
692  //a) find a minor axis
693  float amin;
694  float orthog1[3],orthog2[3];
695  int i,imin,method;
696  vecnormalize3f(direction,e->direction.c);
697  amin = min(min(fabsf(direction[0]),fabsf(direction[1])),fabsf(direction[2]));
698  imin = 0;
699  for(i=0;i<3;i++){
700  if(fabsf(direction[i]) == amin){
701  imin = i; break;
702  }
703  }
704  //make a vector with the minor axis dominant
705  for(i=0;i<3;i++) orthog1[i] = 0.0f;
706  orthog1[imin] = 1.0f;
707  //orthog1 will only be approximately orthogonal to direction
708  //do a cross product to get ortho2
709  veccross3f(orthog2,direction,orthog1);
710  //orthog2 will be truely orthogonal
711  //cross orthog2 with direction to get truely orthog1
712  veccross3f(orthog1,direction,orthog2);
713 
714  //for this particle
715  method = 2;
716  if(method == 1){
717  //METHOD 1: TILT + AZIMUTH
718  //tends to crowd/cluster around central direction, and fade with tilt
719  //(due to equal chance of tilt angle, but larger area to cover as tilt goes up)
720  //direction = cos(tilt)*direction
721  //az = cos(azimuth)*orthog1 + sin(azimuth)*orthog2
722  //az = sin(tilt)*az
723  //direction += az;
724  //where
725  // tilt - from e.direction axis
726  // azimuth - angle around e.direction vector (in plane orthogonal to direction vector)
727  // az[3] - vector in the orthogonal plane, in direction of azimuth
728  float az[3],az1[3],az2[3],ctilt,stilt,caz,saz;
729  tilt = uniformRand()*e->angle;
730  ctilt = cosf(tilt);
731  stilt = sinf(tilt);
732  azimuth = uniformRand()*2.0f*(float)PI;
733  caz = cosf(azimuth);
734  saz = sinf(azimuth);
735  vecscale3f(az1,orthog1,caz);
736  vecscale3f(az2,orthog2,saz);
737  vecadd3f(az,az1,az2);
738  //now az is a unit vector in orthogonal plane, in direction of azimuth
739  vecscale3f(az,az,stilt);
740  //now az is scaled for adding to direction
741  vecscale3f(direction,direction,ctilt);
742  //direction is shrunk (or reversed) to account for tilt
743  vecadd3f(direction,direction,az);
744  //now direction is unit vector in tilt,az direction from e.direction
745  }
746  if(method == 2){
747  //METHOD 2: POINT IN CIRCLE
748  //tends to give even distribution over circle face of cone
749  //xy = randomCircle
750  //orthog = x*orthog1 + y*orthog2
751  //direction += orthog
752  float xy[2], orx[3],ory[3], orthog[3];
753  circleRand2D(xy);
754  //orthog = x*orthog1 + y*orthog2
755  vecscale3f(orx,orthog1,xy[0]);
756  vecscale3f(ory,orthog2,xy[1]);
757  vecadd3f(orthog,orx,ory);
758  vecscale3f(orthog,orthog,sinf(e->angle));
759  vecscale3f(direction,direction,cosf(e->angle));
760  //direction += orthog
761  vecadd3f(direction,orthog,direction);
762  //normalize(direction)
763  vecnormalize3f(direction,direction);
764  }
765 
766  }
767  memcpy(pp->position,e->position.c,3*sizeof(float));
768  speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
769  vecscale3f(pp->velocity,direction,speed);
770  pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
771  pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
772 
773 }
774 void apply_ExplosionEmitter(particle *pp, struct X3D_Node *emitter){
775  // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#ExplosionEmitter
776  // like point emitter, except always random direction
777  // the create-all-at-time-zero is handled up one level
778  struct X3D_ExplosionEmitter *e = (struct X3D_ExplosionEmitter *)emitter;
779  float direction[3], speed;
780  memcpy(pp->position,e->position.c,3*sizeof(float));
781  randomDirection(direction);
782  speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
783  vecscale3f(pp->velocity,direction,speed);
784  pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
785  pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
786 }
787 void apply_PointEmitter(particle *pp, struct X3D_Node *emitter){
788  // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#PointEmitter
789  // like explosion, except may have a non-random direction
790  struct X3D_PointEmitter *e = (struct X3D_PointEmitter *)emitter;
791  float direction[3], speed;
792  memcpy(pp->position,e->position.c,3*sizeof(float));
793  if(veclength3f(e->direction.c) < .00001){
794  randomDirection(direction);
795  }else{
796  memcpy(direction,e->direction.c,3*sizeof(float));
797  vecnormalize3f(direction,direction);
798  }
799  speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
800  vecscale3f(pp->velocity,direction,speed);
801  pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
802  pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
803 
804 }
805 enum {
806  POLYLINEEMITTER_METHODA = 1,
807  POLYLINEEMITTER_METHODB = 2,
808 };
809 void compile_PolylineEmitter(struct X3D_Node *node){
810  struct X3D_PolylineEmitter *e = (struct X3D_PolylineEmitter *)node;
811  float *segs = NULL;
812  //e->_method = POLYLINEEMITTER_METHODA;
813  e->_method = POLYLINEEMITTER_METHODB;
814  if(e->coord && e->coordIndex.n > 1){
815  //convert IndexedLineSet to pairs of coordinates
816  int i,k,ind, n,nseg = 0;
817  float *pts[2];
818  struct X3D_Coordinate *coord = (struct X3D_Coordinate *)e->coord;
819  n = e->coordIndex.n;
820  segs = MALLOC(void*,2*3*sizeof(float) *n*2 ); //2 vertices per lineseg, and 1 lineseg per coordindex should be more than enough
821  k = 0;
822  nseg = 0;
823  for(i=0;i<e->coordIndex.n;i++){
824  ind = e->coordIndex.p[i];
825  if( ind == -1) {
826  k = 0;
827  continue;
828  }
829  pts[k] = (float*)&coord->point.p[ind];
830  k++;
831  if(k==2){
832  veccopy3f(&segs[(nseg*2 +0)*3],pts[0]);
833  veccopy3f(&segs[(nseg*2 +1)*3],pts[1]);
834  pts[0] = pts[1];
835  nseg++;
836  k = 1;
837  }
838  }
839  e->_segs = segs;
840  e->_nseg = nseg;
841  }
842  if(e->_method == POLYLINEEMITTER_METHODB){
843  int i;
844  float *portions, totaldist, dist, delta[3];
845  portions = MALLOC(float *,e->_nseg * sizeof(float));
846  e->_portions = portions;
847  totaldist = 0.0f;
848  for(i=0;i<e->_nseg;i++){
849  vecdif3f(delta,&segs[(i*2 + 1)*3],&segs[(i*2 + 0)*3]);
850  dist = veclength3f(delta);
851  //printf("dist %d %f\n",i,dist);
852  portions[i] = dist;
853  totaldist += dist;
854  }
855  for(i=0;i<e->_nseg;i++){
856  portions[i] = portions[i]/totaldist;
857  //printf("portion %d %f\n",i,portions[i]);
858  }
859  }
860  MARK_NODE_COMPILED
861 }
862 void apply_PolylineEmitter(particle *pp, struct X3D_Node *node){
863  // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#PolylineEmitter
864  float direction[3], speed;
865 
866  struct X3D_PolylineEmitter *e = (struct X3D_PolylineEmitter *)node;
867  //like point emitter, except posiion is drawn randomly along polyline
868  //option A: pick segment index at random, then pick distance along segment at random (Octaga?)
869  //option B: pick random 0-1, then map that to cumulative distance along polyline
870  if(NODE_NEEDS_COMPILING)
871  compile_PolylineEmitter(node);
872  memset(pp->position,0,3*sizeof(float)); //in case no coords/polyline/segs
873  if(e->_method == POLYLINEEMITTER_METHODA && e->_nseg){
874  float *segs, delta[3], pos[3];
875  // pick a segment at random:
876  int iseg = (int) floorf(uniformRand() * (float)e->_nseg);
877  //pick a point on the segment
878  float fraction = uniformRand();
879  segs = (float *)e->_segs;
880  vecdif3f(delta,&segs[(iseg*2 + 1)*3],&segs[(iseg*2 + 0)*3]);
881  vecscale3f(delta,delta,fraction);
882  vecadd3f(pos,&segs[(iseg*2 + 0)*3],delta);
883  veccopy3f(pp->position,pos);
884  }
885  if(e->_method == POLYLINEEMITTER_METHODB && e->_nseg){
886  //pick rand 0-1
887  int i;
888  float cumulative, fraction, *portions, *segs, delta[3], pos[3], segfraction;
889  fraction = uniformRand();
890  portions = (float*)e->_portions;
891  cumulative = 0.0f;
892  for(i=0;i<e->_nseg;i++){
893  cumulative +=portions[i];
894  if(cumulative > fraction){
895  segfraction = (cumulative - fraction) / portions[i];
896  segs = (float *)e->_segs;
897  vecdif3f(delta,&segs[(i*2 + 1)*3],&segs[(i*2 + 0)*3]);
898  vecscale3f(delta,delta,segfraction);
899  vecadd3f(pos,&segs[(i*2 + 0)*3],delta);
900  veccopy3f(pp->position,pos);
901  break;
902  }
903  }
904  }
905 
906  //the rest is like point emitter:
907 //not for polyline see above memcpy(pp->position,e->position.c,3*sizeof(float));
908  if(veclength3f(e->direction.c) < .00001){
909  randomDirection(direction);
910  }else{
911  memcpy(direction,e->direction.c,3*sizeof(float));
912  vecnormalize3f(direction,direction);
913  }
914  speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
915  vecscale3f(pp->velocity,direction,speed);
916  pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
917  pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
918 
919 }
920 int getPolyrepTriangleCount(struct X3D_Node *node);
921 int getPolyrepTriangleByIndex(struct X3D_Node *node, int index, float *v1, float *v2, float *v3);
922 
923 void apply_SurfaceEmitter(particle *pp, struct X3D_Node *emitter){
924  struct X3D_SurfaceEmitter *e = (struct X3D_SurfaceEmitter *)emitter;
925  struct X3D_Node *node;
926 
927  node = e->surface ? e->surface : e->geometry;
928  if(NODE_NEEDS_COMPILING){
929  compile_geometry(X3D_NODE(node));
930  }
931  if(node){
932  int index, ntri;
933  float fraction;
934  float speed;
935  float xyz[3], v1[3],v2[3],v3[3],e1[3],e2[3], normal[3], direction[3];
936 
937  fraction = uniformRand();
938  ntri = getPolyrepTriangleCount(node);
939  if(ntri){
940  index = (int)floorf(fraction * (float)(ntri-1));
941  getPolyrepTriangleByIndex(node,index,v1,v2,v3);
942  randomTriangleCoord(xyz,v1,v2,v3);
943  vecdif3f(e1,v2,v1);
944  vecdif3f(e2,v3,v1);
945  veccross3f(normal,e1,e2);
946  vecnormalize3f(direction,normal);
947 
948  }
949 
950  //the rest is like point emitter
951  memcpy(pp->position,xyz,3*sizeof(float));
952  speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
953  vecscale3f(pp->velocity,direction,speed);
954  pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
955  pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
956  }
957 
958 }
959 void apply_VolumeEmitter(particle *pp, struct X3D_Node *emitter){
960  struct X3D_VolumeEmitter *e = (struct X3D_VolumeEmitter *)emitter;
961  if(!e->_ifs && e->coord){
962  struct X3D_IndexedFaceSet *ifs;
963  ifs = createNewX3DNode0(NODE_IndexedFaceSet);
964  ifs->coord = e->coord;
965  ifs->coordIndex = e->coordIndex;
966  compile_geometry(X3D_NODE(ifs));
967  e->_ifs = ifs;
968  }
969  if(e->_ifs){
970  int nint, i, isInside;
971  float xyz[3], plumb[3], nearest[3], normal[3];
972  float direction[3], speed;
973  struct X3D_IndexedFaceSet *ifs = (struct X3D_IndexedFaceSet *)e->_ifs;
974 
975  isInside = FALSE;
976  for(i=0;i<10;i++){
977  randomPoint3D(xyz);
978  //spread random points over box
979  xyz[0] *= ifs->EXTENT_MAX_X - ifs->EXTENT_MIN_X;
980  xyz[1] *= ifs->EXTENT_MAX_Y - ifs->EXTENT_MIN_Y;
981  xyz[2] *= ifs->EXTENT_MAX_Z - ifs->EXTENT_MIN_Z;
982  veccopy3f(plumb,xyz);
983  plumb[2] = ifs->EXTENT_MIN_Z - 1.0f; //ray end point below box
984  nint = intersect_geometry(e->_ifs,xyz,plumb,nearest,normal);
985  nint = abs(nint) % 2;
986  if(nint == 1){
987  isInside = TRUE;
988  break; //if there's an odd number of intersections, its inside, else even outside
989  }
990  }
991  if(!isInside)
992  vecscale3f(xyz,xyz,0.0f); //emit from 0
993  //the rest is like point emitter
994  memcpy(pp->position,xyz,3*sizeof(float));
995  if(veclength3f(e->direction.c) < .00001){
996  randomDirection(direction);
997  }else{
998  memcpy(direction,e->direction.c,3*sizeof(float));
999  vecnormalize3f(direction,direction);
1000  }
1001  speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
1002  vecscale3f(pp->velocity,direction,speed);
1003  pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
1004  pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
1005  }
1006 }
1007 void updateColorRamp(struct X3D_ParticleSystem *node, particle *pp, GLint cramp){
1008  int j,k,ifloor, iceil, found;
1009  float rgbaf[4], rgbac[4], rgba[4], fraclife;
1010  found = FALSE;
1011  fraclife = pp->age / pp->lifespan;
1012  for(j=0;j<node->colorKey.n;j++){
1013  if(node->colorKey.p[j] <= fraclife && node->colorKey.p[j+1] > fraclife){
1014  ifloor = j;
1015  iceil = j+1;
1016  found = TRUE;
1017  break;
1018  }
1019  }
1020  if(found){
1021  float spread, fraction;
1022  struct SFColorRGBA * crgba = NULL;
1023  struct SFColor *crgb = NULL;
1024  switch(node->colorRamp->_nodeType){
1025  case NODE_ColorRGBA: crgba = ((struct X3D_ColorRGBA *)node->colorRamp)->color.p; break;
1026  case NODE_Color: crgb = ((struct X3D_Color *)node->colorRamp)->color.p; break;
1027  default:
1028  break;
1029  }
1030  spread = node->colorKey.p[iceil] - node->colorKey.p[ifloor];
1031  fraction = (fraclife - node->colorKey.p[ifloor]) / spread;
1032  if(crgba){
1033  memcpy(rgbaf,&crgba[ifloor],sizeof(struct SFColorRGBA));
1034  memcpy(rgbac,&crgba[iceil],sizeof(struct SFColorRGBA));
1035  }else if(crgb){
1036  memcpy(rgbaf,&crgb[ifloor],sizeof(struct SFColor));
1037  rgbaf[3] = 1.0f;
1038  memcpy(rgbac,&crgb[iceil],sizeof(struct SFColor));
1039  rgbac[3] = 1.0f;
1040  }
1041  for(k=0;k<4;k++){
1042  rgba[k] = (1.0f - fraction)*rgbaf[k] + fraction*rgbac[k];
1043  }
1044  glUniform4fv(cramp,1,rgba);
1045  }else{
1046  //re-use last color
1047  }
1048 }
1049 void updateTexCoordRamp(struct X3D_ParticleSystem *node, particle *pp, float *texcoord){
1050  int found, ifloor,j;
1051  float fraclife, fracKey;
1052 
1053  ifloor = 0;
1054  fraclife = pp->age / pp->lifespan;
1055  fracKey = 1.0f / (float)(node->texCoordKey.n);
1056  //if(node->_geometryType != GEOM_LINE)
1057  fraclife -= fracKey; //for 3 keys, fracKey will be .333
1058  // v 0000 v 1111111 v 222 change points
1059  // 0 .5 1 key
1060  for(j=0;j<node->texCoordKey.n;j++){
1061  if( node->texCoordKey.p[j] > fraclife){
1062  ifloor = j;
1063  found = TRUE;
1064  break;
1065  }
1066  }
1067  if(found){
1068  switch(node->_geometryType){
1069  case GEOM_LINE:
1070  FW_GL_TEXCOORD_POINTER (2,GL_FLOAT,0,(float *)&texcoord[ifloor*2*2],0);
1071  break;
1072  case GEOM_QUAD:
1073  case GEOM_TRIANGLE:
1074  //we use triangles for both quad and triangle, 6 vertices per age
1075  FW_GL_TEXCOORD_POINTER (2,GL_FLOAT,0,(float *)&texcoord[ifloor*2*6],0);
1076  break;
1077  default:
1078  break;
1079  }
1080  }
1081 }
1082 
1083 void reallyDrawOnce();
1084 void clearDraw();
1085 GLfloat linepts [6] = {-.5f,0.f,0.f, .5f,0.f,0.f};
1086 ushort lineindices[2] = {0,1};
1087 int getImageChannelCountFromTTI(struct X3D_Node *appearanceNode );
1088 void update_effect_uniforms();
1089 
1090 void child_ParticleSystem(struct X3D_ParticleSystem *node){
1091  //
1092  // ParticleSystem
1093  // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#ParticleSystem
1094  // In here we are doing basically what child_Shape does to draw a single geom,
1095  // except once we send the geometry vertex buffer to the shader,
1096  // we go into a loop to draw all the particles, sending an xyz position update to the shader
1097  // for each particle (and color update if colorRamp, or texCoordUpdate if texCoordRamp, and
1098  // velocity direction if LINE)
1099  //
1100  //s_shader_capabilities_t *caps;
1101  // static int once = 0;
1102  COMPILE_IF_REQUIRED
1103  if (renderstate()->render_blend == (node->_renderFlags & VF_Blend)) {
1104  if(node->enabled){
1105  if(node->isActive){
1106  int i,j,k,maxparticles;
1107  double ttime;
1108  float dtime;
1109  //int colorSource, alphaSource, isLit,
1110  int isUserShader;
1112  shaderflagsstruct shader_requirements;
1113  Stack *_particles;
1114  int allowsTexcoordRamp = FALSE;
1115  float *texcoord = NULL;
1116  GLint ppos, cr, gtype;
1117  int haveColorRamp,haveTexcoordRamp;
1118 
1119  struct X3D_Node *tmpNG;
1120  ttglobal tg = gglobal();
1121 
1122  ttime = TickTime();
1123  dtime = (float)(ttime - node->_lasttime); //increment to particle age
1124 
1125  //if(!once)
1126  // printf("child particlesystem \n");
1127 
1128 
1129  //RETIRE remove deceased/retired particles (by packing vector)
1130  _particles = node->_particles;
1131  maxparticles = min(node->maxParticles,10000);
1132  for(i=0,j=0;i<vectorSize(_particles);i++){
1133  particle pp = vector_get(particle,_particles,i);
1134  pp.age += dtime;
1135  if(pp.age < pp.lifespan){
1136  vector_set(particle,_particles,j,pp); //pack vector to live position j
1137  j++;
1138  }
1139  }
1140 
1141  //PREP PHYSICS - wind geta a per-frame gustiness update
1142  for(k=0;k<node->physics.n;k++){
1143  switch(node->physics.p[k]->_nodeType){
1144  case NODE_WindPhysicsModel:
1145  prep_windphysics(node->physics.p[k]); break;
1146  default:
1147  break;
1148  }
1149  }
1150  //APPLY PHYSICS
1151  for(i=0;i<vectorSize(_particles);i++){
1152  particle pp = vector_get(particle,_particles,i);
1153  float positionChange[3];
1154 
1155 
1156  //update velocity vector based on physics accelerations
1157  // A = F/M
1158  // V2 = V1 + A*dT
1159  for(k=0;k<node->physics.n;k++){
1160  switch(node->physics.p[k]->_nodeType){
1161  case NODE_WindPhysicsModel:
1162  apply_windphysics(&pp,node->physics.p[k],dtime); break;
1163  case NODE_ForcePhysicsModel:
1164  apply_forcephysics(&pp,node->physics.p[k],dtime); break;
1165  default:
1166  break;
1167  }
1168  }
1169 
1170  //update position: P1 = P0 + .5(V0 + V1) x dT
1171  vecscale3f(positionChange,pp.velocity,.5f * dtime);
1172 
1173  for(k=0;k<node->physics.n;k++){
1174  switch(node->physics.p[k]->_nodeType){
1175  case NODE_BoundedPhysicsModel:
1176  apply_boundedphysics(&pp,node->physics.p[k],positionChange); break;
1177  default:
1178  break;
1179  }
1180  }
1181  vecadd3f(pp.position,pp.position,positionChange);
1182 
1183  vector_set(particle,_particles,i,pp); //pack vector to live position j
1184  }
1185 
1186  //CREATE via emitters (implied dtime = 0, so no physics on first frame)
1187  _particles->n = j;
1188  if(node->createParticles && _particles->n < maxparticles){
1189  //create new particles to reach maxparticles limit
1190  int n_per_frame, n_needed, n_this_frame;
1191  float particles_per_second, particles_per_frame;
1192  n_needed = maxparticles - _particles->n;
1193  //for explosion emitter, we want them all created on the first pass
1194  //for all the rest we want maxparticles spread over a lifetime, so by the
1195  //time some start dying, well be at maxparticles
1196  //particles_per_second [p/s] = maxparticles[p] / particleLifetime[s]
1197  particles_per_second = (float)node->maxParticles / (float) node->particleLifetime;
1198  //particles_per_frame [p/f] = particles_per_second [p/s] / frames_per_second [f/s]
1199  particles_per_frame = particles_per_second * dtime;
1200  particles_per_frame += node->_remainder;
1201  n_per_frame = (int)particles_per_frame;
1202  node->_remainder = particles_per_frame - (float)n_per_frame;
1203  n_this_frame = min(n_per_frame,n_needed);
1204  if(node->emitter->_nodeType == NODE_ExplosionEmitter)
1205  n_this_frame = n_needed;
1206  j = _particles->n;
1207  for(i=0;i<n_this_frame;i++,j++){
1208  particle pp;
1209  pp.age = 0.0f;
1210  memset(pp.origin,0,sizeof(float)*3); //for bounded physics
1211  pp.lifespan = node->particleLifetime * (1.0f + uniformRandCentered()*node->lifetimeVariation);
1212  memcpy(pp.size,node->particleSize.c,2*sizeof(float));
1213  //emit particles
1214  switch(node->emitter->_nodeType){
1215  case NODE_ConeEmitter: apply_ConeEmitter(&pp,node->emitter); break;
1216  case NODE_ExplosionEmitter: apply_ExplosionEmitter(&pp,node->emitter);
1217  node->createParticles = FALSE;
1218  break;
1219  case NODE_PointEmitter: apply_PointEmitter(&pp,node->emitter); break;
1220  case NODE_PolylineEmitter: apply_PolylineEmitter(&pp,node->emitter); break;
1221  case NODE_SurfaceEmitter: apply_SurfaceEmitter(&pp,node->emitter); break;
1222  case NODE_VolumeEmitter: apply_VolumeEmitter(&pp,node->emitter); break;
1223  default:
1224  break;
1225  }
1226  //save particle
1227  vector_set(particle,_particles,j,pp);
1228  }
1229  _particles->n = j;
1230  }
1231 
1232  //prepare to draw, like child_shape
1233  //render appearance
1234  //BORROWED FROM CHILD SHAPE >>>>>>>>>
1235 
1236  //unsigned int shader_requirements;
1237  memset(&shader_requirements,0,sizeof(shaderflagsstruct));
1238 
1239  //prep_Appearance
1240  RENDER_MATERIAL_SUBNODES(node->appearance); //child_Appearance
1241 
1242 
1243 #ifdef HAVE_P
1244  if (p->material_oneSided != NULL) {
1245  memcpy (&p->appearanceProperties.fw_FrontMaterial, p->material_oneSided->_verifiedColor.p, sizeof (struct fw_MaterialParameters));
1246  memcpy (&p->appearanceProperties.fw_BackMaterial, p->material_oneSided->_verifiedColor.p, sizeof (struct fw_MaterialParameters));
1247  /* copy the emissive colour over for lines and points */
1248  memcpy(p->appearanceProperties.emissionColour,p->material_oneSided->_verifiedColor.p, 3*sizeof(float));
1249 
1250  } else if (p->material_twoSided != NULL) {
1251  memcpy (&p->appearanceProperties.fw_FrontMaterial, p->material_twoSided->_verifiedFrontColor.p, sizeof (struct fw_MaterialParameters));
1252  memcpy (&p->appearanceProperties.fw_BackMaterial, p->material_twoSided->_verifiedBackColor.p, sizeof (struct fw_MaterialParameters));
1253  /* copy the emissive colour over for lines and points */
1254  memcpy(p->appearanceProperties.emissionColour,p->material_twoSided->_verifiedFrontColor.p, 3*sizeof(float));
1255  } else {
1256  /* no materials selected.... */
1257  }
1258 #endif
1259 
1260  /* enable the shader for this shape */
1261  //ConsoleMessage("turning shader on %x",node->_shaderTableEntry);
1262 
1263  POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->geometry,tmpNG);
1264 
1265  shader_requirements.base = node->_shaderflags_base; //_shaderTableEntry;
1266  shader_requirements.effects = node->_shaderflags_effects;
1267  shader_requirements.usershaders = node->_shaderflags_usershaders;
1268  isUserShader = shader_requirements.usershaders ? TRUE : FALSE; // >= USER_DEFINED_SHADER_START ? TRUE : FALSE;
1269  //if(!p->userShaderNode || !(shader_requirements >= USER_DEFINED_SHADER_START)){
1270  if(!isUserShader){
1271  //for Luminance and Luminance-Alpha images, we have to tinker a bit in the Vertex shader
1272  // New concept of operations Aug 26, 2016
1273  // in the specs there are some things that can replace other things (but not the reverse)
1274  // Texture can repace CPV, diffuse and 111
1275  // CPV can replace diffuse and 111
1276  // diffuse can replace 111
1277  // Texture > CPV > Diffuse > (1,1,1)
1278  // so there's a kind of order / sequence to it.
1279  // There can be a flag at each step saying if you want to replace the prior value (otherwise modulate)
1280  // Diffuse replacing or modulating (111) is the same thing, no flag needed
1281  // Therefore we need at most 2 flags for color:
1282  // TEXTURE_REPLACE_PRIOR and CPV_REPLACE_PRIOR.
1283  // and other flag for alpha: ALPHA_REPLACE_PRIOR (same as ! WANT_TEXALPHA)
1284  // if all those are false, then its full modulation.
1285  // our WANT_LUMINANCE is really == ! TEXTURE_REPLACE_PRIOR
1286  // we are missing a CPV_REPLACE_PRIOR, or more precisely this is a default burned into the shader
1287 
1288  int channels;
1289  //modulation:
1290  //- for Castle-style full-modulation of texture x CPV x mat.diffuse
1291  // and texalpha x (1-mat.trans), set 2
1292  //- for specs table 17-2 RGB Tex replaces CPV with modulation
1293  // of table 17-2 entries with mat.diffuse and (1-mat.trans) set 1
1294  //- for specs table 17-3 as written and ignoring modulation sentences
1295  // so CPV replaces diffuse, texture replaces CPV and diffuse- set 0
1296  // testing: KelpForest SharkLefty.x3d has CPV, ImageTexture RGB, and mat.diffuse
1297  // 29C.wrl has mat.transparency=1 and LumAlpha image, modulate=0 shows sphere, 1,2 inivisble
1298  // test all combinations of: modulation {0,1,2} x shadingStyle {gouraud,phong}: 0 looks bright texture only, 1 texture and diffuse, 2 T X C X D
1299  int modulation = 1; //freewrl default 1 (dug9 Aug 27, 2016 interpretation of Lighting specs)
1300  channels = getImageChannelCountFromTTI(node->appearance);
1301 
1302  if(modulation == 0)
1303  shader_requirements.base |= MAT_FIRST; //strict use of table 17-3, CPV can replace mat.diffuse, so texture > cpv > diffuse > 111
1304 
1305  if(shader_requirements.base & COLOUR_MATERIAL_SHADER){
1306  //printf("has a color node\n");
1307  //lets turn it off, and see if we get texture
1308  //shader_requirements &= ~(COLOUR_MATERIAL_SHADER);
1309  if(modulation == 0)
1310  shader_requirements.base |= CPV_REPLACE_PRIOR;
1311  }
1312 
1313  if(channels && (channels == 3 || channels == 4) && modulation < 2)
1314  shader_requirements.base |= TEXTURE_REPLACE_PRIOR;
1315  //if the image has a real alpha, we may want to turn off alpha modulation,
1316  // see comment about modulate in Compositing_Shaders.c
1317  if(channels && (channels == 2 || channels == 4) && modulation == 0)
1318  shader_requirements.base |= TEXALPHA_REPLACE_PRIOR;
1319 
1320  //getShaderFlags() are from non-leaf-node shader influencers:
1321  // fog, local_lights, clipplane, Effect/EffectPart (for CastlePlugs) ...
1322  // - as such they may be different for the same shape node DEF/USEd in different branches of the scenegraph
1323  // - so they are ORd here before selecting a shader permutation
1324  shader_requirements.base |= getShaderFlags().base;
1325  shader_requirements.effects |= getShaderFlags().effects;
1326  //if(shader_requirements & FOG_APPEARANCE_SHADER)
1327  // printf("fog in child_shape\n");
1328 
1329  //ParticleSystem flag
1330  shader_requirements.base |= PARTICLE_SHADER;
1331  if(node->colorRamp)
1332  shader_requirements.base |= HAVE_UNLIT_COLOR;
1333  }
1334  //printf("child_shape shader_requirements base %d effects %d user %d\n",shader_requirements.base,shader_requirements.effects,shader_requirements.usershaders);
1335  scap = getMyShaders(shader_requirements);
1336  enableGlobalShader(scap);
1337  //enableGlobalShader (getMyShader(shader_requirements)); //node->_shaderTableEntry));
1338 
1339  //see if we have to set up a TextureCoordinateGenerator type here
1340  if (tmpNG && tmpNG->_intern) {
1341  if (tmpNG->_intern->tcoordtype == NODE_TextureCoordinateGenerator) {
1342  getAppearanceProperties()->texCoordGeneratorType = tmpNG->_intern->texgentype;
1343  //ConsoleMessage("shape, matprop val %d, geom val %d",getAppearanceProperties()->texCoordGeneratorType, node->geometry->_intern->texgentype);
1344  }
1345  }
1346  //userDefined = (whichOne >= USER_DEFINED_SHADER_START) ? TRUE : FALSE;
1347  //if (p->userShaderNode != NULL && shader_requirements >= USER_DEFINED_SHADER_START) {
1348  #ifdef ALLOW_USERSHADERS
1349  if(isUserShader && p->userShaderNode){
1350  //we come in here right after a COMPILE pass in APPEARANCE which renders the shader, which sets p->userShaderNode
1351  //if nothing changed with appearance -no compile pass- we don't come in here again
1352  //ConsoleMessage ("have a shader of type %s",stringNodeType(p->userShaderNode->_nodeType));
1353  switch (p->userShaderNode->_nodeType) {
1354  case NODE_ComposedShader:
1355  if (X3D_COMPOSEDSHADER(p->userShaderNode)->isValid) {
1356  if (!X3D_COMPOSEDSHADER(p->userShaderNode)->_initialized) {
1357  sendInitialFieldsToShader(p->userShaderNode);
1358  }
1359  }
1360  break;
1361  case NODE_ProgramShader:
1362  if (X3D_PROGRAMSHADER(p->userShaderNode)->isValid) {
1363  if (!X3D_PROGRAMSHADER(p->userShaderNode)->_initialized) {
1364  sendInitialFieldsToShader(p->userShaderNode);
1365  }
1366  }
1367 
1368  break;
1369  case NODE_PackagedShader:
1370  if (X3D_PACKAGEDSHADER(p->userShaderNode)->isValid) {
1371  if (!X3D_PACKAGEDSHADER(p->userShaderNode)->_initialized) {
1372  sendInitialFieldsToShader(p->userShaderNode);
1373  }
1374  }
1375 
1376  break;
1377  }
1378  }
1379  #endif //ALLOW_USERSHADERS
1380  //update effect field uniforms
1381  if(shader_requirements.effects){
1382  update_effect_uniforms();
1383  }
1384 
1385  //<<<<< BORROWED FROM CHILD SHAPE
1386 
1387 
1388  //send materials, textures, matrices to shader
1389  textureTransform_start();
1390  setupShaderB();
1391  //send vertex buffer to shader
1392  allowsTexcoordRamp = FALSE;
1393  texcoord = NULL;
1394  switch(node->_geometryType){
1395  case GEOM_LINE:
1396  {
1397  FW_GL_VERTEX_POINTER (3,GL_FLOAT,0,(float *)linepts);
1398  sendElementsToGPU(GL_LINES,2,(ushort *)lineindices);
1399  texcoord = (float*)node->_ltex;
1400  allowsTexcoordRamp = TRUE;
1401  }
1402  break;
1403  case GEOM_POINT:
1404  {
1405  float point[3];
1406  memset(point,0,3*sizeof(float));
1407  FW_GL_VERTEX_POINTER (3,GL_FLOAT,0,(GLfloat *)point);
1408  sendArraysToGPU (GL_POINTS, 0, 1);
1409  }
1410  break;
1411  case GEOM_QUAD:
1412  {
1413  //textureCoord_send(&mtf);
1414  FW_GL_VERTEX_POINTER (3,GL_FLOAT,0,(GLfloat *)node->_tris);
1415  FW_GL_NORMAL_POINTER (GL_FLOAT,0,twotrisnorms);
1416  sendArraysToGPU (GL_TRIANGLES, 0, 6);
1417  texcoord = (float*)node->_ttex;
1418  allowsTexcoordRamp = TRUE;
1419  }
1420  break;
1421  case GEOM_SPRITE:
1422  {
1423  //textureCoord_send(&mtf);
1424  FW_GL_VERTEX_POINTER (3,GL_FLOAT,0,(GLfloat *)node->_tris);
1425  FW_GL_NORMAL_POINTER (GL_FLOAT,0,twotrisnorms);
1426  sendArraysToGPU (GL_TRIANGLES, 0, 6);
1427  }
1428  break;
1429  case GEOM_TRIANGLE:
1430  {
1431  //textureCoord_send(&mtf);
1432  FW_GL_VERTEX_POINTER (3,GL_FLOAT,0,(GLfloat *)node->_tris);
1433  FW_GL_NORMAL_POINTER (GL_FLOAT,0,twotrisnorms);
1434  sendArraysToGPU (GL_TRIANGLES, 0, 6);
1435  texcoord = (float*)node->_ttex;
1436  allowsTexcoordRamp = TRUE;
1437  }
1438  break;
1439  case GEOM_GEOMETRY:
1440  render_node(node->geometry);
1441  break;
1442  default:
1443  break;
1444  }
1445 
1446  ppos = GET_UNIFORM(scap->myShaderProgram,"particlePosition");
1447  cr = GET_UNIFORM(scap->myShaderProgram,"fw_UnlitColor");
1448  gtype = GET_UNIFORM(scap->myShaderProgram,"fw_ParticleGeomType");
1449  glUniform1i(gtype,node->_geometryType); //for SPRITE = 4, screen alignment
1450  //loop over live particles, drawing each one
1451  haveColorRamp = node->colorRamp ? TRUE : FALSE;
1452  haveColorRamp = haveColorRamp && cr > -1;
1453  haveTexcoordRamp = node->texCoordRamp ? TRUE : FALSE;
1454  haveTexcoordRamp = haveTexcoordRamp && allowsTexcoordRamp && texcoord;
1455 
1456  for(i=0;i<vectorSize(_particles);i++){
1457  particle pp = vector_get(particle,_particles,i);
1458  //update particle-specific uniforms
1459  glUniform3fv(ppos,1,pp.position);
1460  if(haveColorRamp)
1461  updateColorRamp(node,&pp,cr);
1462  if(haveTexcoordRamp)
1463  updateTexCoordRamp(node,&pp,texcoord);
1464  if(node->_geometryType == GEOM_LINE){
1465  float lpts[6], vel[3];
1466  vecnormalize3f(vel,pp.velocity);
1467  vecscale3f(&lpts[3],vel,.5f*node->particleSize.c[1]);
1468  vecscale3f(&lpts[0],vel,-.5f*node->particleSize.c[1]);
1469  FW_GL_VERTEX_POINTER (3,GL_FLOAT,0,(float *)lpts);
1470  }
1471  //draw
1472  reallyDrawOnce();
1473  }
1474  clearDraw();
1475  //cleanup after draw, like child_shape
1476  FW_GL_BINDBUFFER(GL_ARRAY_BUFFER, 0);
1477  FW_GL_BINDBUFFER(GL_ELEMENT_ARRAY_BUFFER, 0);
1478  textureTransform_end();
1479 
1480  //BORROWED FROM CHILD_SHAPE >>>>>>
1481  //fin_Appearance
1482  if(node->appearance){
1483  struct X3D_Appearance *tmpA;
1484  POSSIBLE_PROTO_EXPANSION(struct X3D_Appearance *,node->appearance,tmpA);
1485  if(tmpA->effects.n)
1486  fin_sibAffectors(X3D_NODE(tmpA),&tmpA->effects);
1487  }
1488  /* any shader turned on? if so, turn it off */
1489 
1490  //ConsoleMessage("turning shader off");
1491  finishedWithGlobalShader();
1492 #ifdef HAVE_P
1493  p->material_twoSided = NULL;
1494  p->material_oneSided = NULL;
1495  p->userShaderNode = NULL;
1496 #endif
1497  tg->RenderFuncs.shapenode = NULL;
1498 
1499  /* load the identity matrix for textures. This is necessary, as some nodes have TextureTransforms
1500  and some don't. So, if we have a TextureTransform, loadIdentity */
1501 
1502 #ifdef HAVE_P
1503  if (p->this_textureTransform) {
1504  p->this_textureTransform = NULL;
1505 #endif //HAVE_P
1506  FW_GL_MATRIX_MODE(GL_TEXTURE);
1507  FW_GL_LOAD_IDENTITY();
1508  FW_GL_MATRIX_MODE(GL_MODELVIEW);
1509 #ifdef HAVE_P
1510  }
1511 #endif
1512  /* LineSet, PointSets, set the width back to the original. */
1513  {
1514  float gl_linewidth = tg->Mainloop.gl_linewidth;
1515  glLineWidth(gl_linewidth);
1516 #ifdef HAVE_P
1517  p->appearanceProperties.pointSize = gl_linewidth;
1518 #endif
1519  }
1520 
1521  /* did the lack of an Appearance or Material node turn lighting off? */
1522  LIGHTING_ON;
1523 
1524  /* turn off face culling */
1525  DISABLE_CULL_FACE;
1526 
1527  //<<<<< BORROWED FROM CHILD_SHAPE
1528 
1529  node->_lasttime = ttime;
1530  } //isActive
1531  } //enabled
1532  } //VF_Blend
1533  // once = 1;
1534 }
Definition: Vector.h:36