FreeWRL / FreeX3D 4.3.0
Component_ParticleSystems.c
1/*
2
3
4X3D 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;
60void *Component_ParticleSystems_constructor(){
61 void *v = MALLOCV(sizeof(struct pComponent_ParticleSystems));
62 memset(v,0,sizeof(struct pComponent_ParticleSystems));
63 return v;
64}
65void Component_ParticleSystems_init(struct tComponent_ParticleSystems *t){
66 //public
67 //private
68 t->prv = Component_ParticleSystems_constructor();
69 {
71 p->something = 0;
72 }
73}
74void 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
173Its like a Shape node, or is a shape node,
174set geometry
175set basic appearance
176foreach liveparticle
177 update texcoord
178 update color (color per vertex)
179 update position
180 gl_DrawArrays or gl_DrawElements
181
182Options in freewrl:
1831. 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)
1882. 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
2063. 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
2264. 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)
2315. make sendArrays etc a callback function, different for particles
232CHOICE: #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
243float 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}
256float uniformRandCentered(){
257 return uniformRand() - .5f; //center on 0
258}
259void 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}
269float 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
283void 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}
295void 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}
308void randomPoint3D(float *xyz){
309 //- .5 to .5 range
310 xyz[0] = (uniformRand() - .5f);
311 xyz[1] = (uniformRand() - .5f);
312 xyz[2] = (uniformRand() - .5f);
313}
314void 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
335typedef 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;
348enum {
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};
356struct {
357const char *name;
358int 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};
368int 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};
382GLfloat 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,};
383GLfloat 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,};
384GLfloat 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
386void compile_Shape (struct X3D_Shape *node);
387// COMPILE PARTICLE SYSTEM
388void 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
524void 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}
531void 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}
553int intersect_polyrep(struct X3D_Node *node, float *p1, float *p2, float *nearest, float *normal);
554
555void 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}
570int 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}
583void 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}
666void 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
683void 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}
774void 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}
787void 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}
805enum {
806 POLYLINEEMITTER_METHODA = 1,
807 POLYLINEEMITTER_METHODB = 2,
808};
809void 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}
862void 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}
920int getPolyrepTriangleCount(struct X3D_Node *node);
921int getPolyrepTriangleByIndex(struct X3D_Node *node, int index, float *v1, float *v2, float *v3);
922
923void 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}
959void 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}
1007void 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}
1049void 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
1083void reallyDrawOnce();
1084void clearDraw();
1085GLfloat linepts [6] = {-.5f,0.f,0.f, .5f,0.f,0.f};
1086ushort lineindices[2] = {0,1};
1087int getImageChannelCountFromTTI(struct X3D_Node *appearanceNode );
1088void update_effect_uniforms();
1089
1090void 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}