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/LoadTextures.h"
46#include "../opengl/Material.h"
47#include "../opengl/OpenGL_Utils.h"
48#include "../input/EAIHelpers.h" /* for newASCIIString() */
49
50#include "Polyrep.h"
51#include "RenderFuncs.h"
52#include "LinearAlgebra.h"
53//#include "Component_ParticleSystems.h"
54#include "Children.h"
55#include "Component_Shape.h"
56#include "../opengl/Textures.h"
57
59 int something;
60}* ppComponent_ParticleSystems;
61void *Component_ParticleSystems_constructor(){
62 void *v = MALLOCV(sizeof(struct pComponent_ParticleSystems));
63 memset(v,0,sizeof(struct pComponent_ParticleSystems));
64 return v;
65}
66void Component_ParticleSystems_init(struct tComponent_ParticleSystems *t){
67 //public
68 //private
69 t->prv = Component_ParticleSystems_constructor();
70 {
71 ppComponent_ParticleSystems p = (ppComponent_ParticleSystems)t->prv;
72 p->something = 0;
73 }
74}
75void Component_ParticleSystems_clear(struct tComponent_ParticleSystems *t){
76 //public
77}
78
79//ppComponent_ParticleSystems p = (ppComponent_ParticleSystems)gglobal()->Component_ParticleSystems.prv;
80
81/* Particle Systems
82 http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html
83 examples:
84 Non- x3d:
85 https://stemkoski.github.io/Three.js/#particlesystem-shader
86 x3d scenes:
87 links:
88 http://mmaklin.com/uppfra_preprint.pdf
89 Nice particle physics
90 http://www.nvidia.com/object/doc_characters.html
91 Nvidia link page for game programmming with shaders
92
93 Fuzzy Design:
94 1. Update position of particles from a particleSystem node
95 Eval particles after events (after the do_tick) and befre RBP physics
96 see http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/concepts.html#ExecutionModel
97 positions are update wrt the local system of the particleSystme node
98 each particle has a struct { lifetime remaining, position, velocity vector, ??}
99 up to 10,000 particles (per particle node)
100 randomizing: use C srand(time) once, and rand() for each randomizing. Scale by Variation field
101 CPU design: iterate over particles, updating each one
102 GPU design ie openCL: do same thing in massive parallel
103 2. Render
104 during geom pass of render_hier, in render_particleSystem()
105 CPU design: iterate over particles like children:
106 updating transform stack with particle position
107 updating appearance f(time)
108 calling render_node(node) on each particle
109 GPU design: send arrray of particle positions/states to GPU
110 in shader iterate over positions, re-rendering for each
111
112 PROBLEM with trying to do physics in shader:
113 x how do you update the state of each particle in a way the next frame can access?
114 vs on cpu, if you have 80 particles, you can whip through them, updating their state,
115 - and resending the state via attribute array on each frame
116 - also more flexible when doing geometryType="GOEMETRY" / goemetry node, that code is CPU
117
118 PROBLEM with sending just the particle position, and generating the sprite geometry
119 in the shader: GLES2 doesn't have gometry shaders.
120
121 GLES2 has no gl_VertexID per vertex in vertex shader, so:
122 - send glAttributeArray of xyz positions to match vertices?
123 - send repetitive triangles - same 3 xyz repetitively?
124 - in shader add position to triangle verts?
125 - or send glAttributeArray of sprite ID of length nvert
126 -- and uniform3fv of xyz of length nsprite
127 -- then lookup xyz[spriteID] in vertex shader?
128
129 EASIEST CPU/GPU SPLIT:
130 1. just send geometry for 1 particle to shader
131 2. cpu loop over particles:
132 foreach liveparticle
133 send position to shader
134 send texcoord to shader
135 send cpv to shader
136 gl_DrawArrays
137
138 POSITION
139 because position is just xyz (not orientation or scale) the shader could
140 take a vec3 for that, and add it on before transforming from local to view
141 for non-GEOMETRY, the transform needs to keep the face normal parallel to the view Z
142 H: you could do that by transforming 0,0,0 to view, and adding on gl_vertex
143 x but that wouldn't do scale, or orientation if you need it
144 - for scale also transform gl_vertex, get the |diff| from 0 for scale
145
146 PHYSICS - I don't see any rotational momentum needs, which involve cross products
147 - so forces F, positions p, velocities v, accelerations a are vec3
148 - mass, time are scalars
149 - physics:
150 F = m * a
151 a = F/m
152 v2 = v1 + a*dt
153 p2 = p1 + .5(v1 + v2)*dt
154 p2 = p1 + v1*dt + .5*a*dt**2
155 p - position
156 v - velocity
157 a - acceleration
158 dt - delta time = (time2 - time1)
159 m - mass
160 F - force
161
162 RANDOM DIRECTIONS
163 http://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d
164
165 RANDOM TRIANGLE COORDS
166 picking a random triangle won't evenly distribute by area, so we can be approx with point inside tri too
167 can pick 2 0-1 range numbers, and use as barycentric coords b1,b2:
168 https://en.wikipedia.org/wiki/Barycentric_coordinate_system
169 p = b1*p1 + b2*p2 + (1 - b1 - b2)*p3
170
171 COMPARISONS - H3D, Octaga, Xj3D claim particle physics
172 - Octaga responds to particle size, has good force and wind effects
173
174Its like a Shape node, or is a shape node,
175set geometry
176set basic appearance
177foreach liveparticle
178 update texcoord
179 update color (color per vertex)
180 update position
181 gl_DrawArrays or gl_DrawElements
182
183Options in freewrl:
1841. per-particle child_Shape
185 foreach liveparticle
186 push particle position onto transform stack
187 fiddle with appearance node on child
188 call child_Shape(particle)
1892. per-particle-system child_Shape
190 call child_Shape
191 if(geometryType 'GEOMETRY')
192 .... sendArraysToGPU (hack)
193 foreach liveparticle
194 update position
195 glDrawArrays
196 .... sendElementsToGPU (hack)
197 foreach liveparticle
198 update position
199 glDrawElements
200 if(geometryType !GEOMETRY)
201 send vbo with one line or quad or 2 triangles or point
202 foreach liveparticle
203 update position
204 update texcoord
205 update color (color per vertex)
206 gl_DrawArrays or gl_DrawElements
2073. refactor child shape to flatten the call hierarchy
208 child shape:
209 a) determine shader flags
210 b) compile/set/bind shader program
211 c) set appearance - pull setupShader out of sendArraysToGPU
212 set material
213 set texture
214 set shader
215 ...
216 d) set geometry vertices and type element/array, line/triangle
217 if GEOMETRY: render(geom node) - except don't call gl_DrawArrays or gl_DrawElements
218 PROBLEM: some geometries -cylinder, cone- are made from multiple calls to gl_DrawElements
219 OPTION: refactor so uses polyrep, or single permuatation call
220 (cylinder: 4 permutations: full, bottom, top, no-ends)
221 else !GEOMETRY: send one quad/line/point/triangle
222 e) foreach liveparticle
223 update position
224 update texcoord
225 update color (CPV)
226 gl_Draw xxx: what was set in d) above
2274. half-flatten child_shape
228 as in #3, except just take setupShader out of sendArraysToGPU/sendElementsToGPU, and put in
229 child_shape body
230 then child_particlesystem can be a copy of child_shape, with loop over particles
231 calling render_node(geometry) for GEOMETRY type (resending vbos)
2325. make sendArrays etc a callback function, different for particles
233CHOICE: #3
234 setup shader //sends materials, matrices to shader
235 render_node(geometry) //sends vertex data to shader, saves call parameters to gl_DrawArrays/Elements
236 foreach liveparticle
237 update particle-specific position, color, texcoords
238 reallyDrawOnce() //calls glDrawArrays or Elements
239 clearDraw()
240
241*/
242
243
244float uniformRand(){
245 // 0 to 1 inclusive
246 static int once = 0;
247 unsigned int ix;
248 float rx;
249
250 if(!once)
251 srand((unsigned int) TickTime());
252 once = 1;
253 ix = rand();
254 rx = (float)ix/(float)RAND_MAX; //you would do (RAND_MAX - 1) here for excluding 1
255 return rx;
256}
257float uniformRandCentered(){
258 return uniformRand() - .5f; //center on 0
259}
260void circleRand2D(float *xy){
261 //random xy on a circle area radius 1
262 float radius2;
263 for(;;){
264 xy[0] = 2.0f*(uniformRand() - .5f);
265 xy[1] = 2.0f*(uniformRand() - .5f);
266 radius2 = xy[0]*xy[0] + xy[1]*xy[1];
267 if(radius2 <= 1.0f) break;
268 }
269}
270float normalRand(){
271 // in -.5 to .5 range
272 float rxy[2];
273 // by just taking points in a circle radius, this emulates the falloff of a normal curve in one dimension
274 // .
275 // . x .
276 // : :
277 // . .
278 // . o
279 //
280 circleRand2D(rxy);
281 return (float)(rxy[0]*.5); //scale from -1 to 1 into -.5 to .5 range
282}
283
284void randomTriangleCoord_dug9_uneducated_guess(float *p, float* p1, float *p2, float *p3){
285 // get 2 random barycentric coords 0-1, and use those
286 // https://en.wikipedia.org/wiki/Barycentric_coordinate_system
287 // x I think b1 + b2 can be > 1.0 and thats wrong
288 int i;
289 float b1, b2;
290 b1 = uniformRand();
291 b2 = uniformRand();
292 for(i=0;i<3;i++){
293 p[i] = b1*p1[i] + b2*p2[i] + (1.0f - b1 - b2)*p3[i];
294 }
295}
296void randomTriangleCoord(float *p, float* p1, float *p2, float *p3){
297 // http://math.stackexchange.com/questions/18686/uniform-random-point-in-triangle
298 int i;
299 float r1, r2, sqr1,sqr2;
300 r1 = uniformRand();
301 r2 = uniformRand();
302 sqr1 = sqrtf(r1);
303 sqr2 = sqrtf(r2);
304 for(i=0;i<3;i++){
305 p[i] = (1.0f - sqr1)*p1[i] + (sqr1*(1.0f - sqr2))*p2[i] + (r2*sqr1)*p3[i];
306 }
307
308}
309void randomPoint3D(float *xyz){
310 //- .5 to .5 range
311 xyz[0] = (uniformRand() - .5f);
312 xyz[1] = (uniformRand() - .5f);
313 xyz[2] = (uniformRand() - .5f);
314}
315void randomDirection(float *xyz){
316 //random xyz direction from a point
317 //http://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d
318 float radius3;
319 for(;;){
320 //get random point in a unit cube
321 //xyz[0] = (uniformRand() - .5f);
322 //xyz[1] = (uniformRand() - .5f);
323 //xyz[2] = (uniformRand() - .5f);
324 randomPoint3D(xyz);
325 //discard point if outside unit sphere
326 radius3 = xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2];
327 if(radius3 <= 1.0f && radius3 > 0.0000001f){
328 //vecnormalize3f(xyz,xyz);
329 //normalize direction to point
330 vecscale3f(xyz,xyz,1.0f/sqrtf(radius3));
331 break;
332 }
333 }
334}
335
336typedef struct {
337 //store at end of current iteration, for use on next iteration
338 float age;
339 float lifespan; //assigned at birth
340 float size[2]; //assigned at birth
341 float position[3];
342 float velocity[3];
343 float origin[3]; //zero normally. For boundedphysics, updated on each reflection to be last reflection point.
344 float direction[3]; //normalized last non-zero velocity vector
345 float speed;
346 float mass;
347 float surfaceArea;
348 //mapemitter method
349 int sink; //assigned after birth in MapPhysics, for MapPhysics, MapEmitter
350 int maplocation[2]; //last popmap location in MapPhysics
351 int paused; //HANIM 0= use first motion 1= use second motion
352 double transitionStart[2];
353 double transitionTime[2];
354 double _startTime[2];
355 int lastMotionsEnabled[2];
356 //mapemitter + HAnimPermuter method (could be generalized to more emitters?)
357 int permutationIndex;
358} particle;
359enum {
360 GEOM_QUAD = 1,
361 GEOM_LINE = 2,
362 GEOM_POINT = 3,
363 GEOM_SPRITE = 4,
364 GEOM_TRIANGLE = 5,
365 GEOM_GEOMETRY = 6,
366 GEOM_HANIM = 7,
367 GEOM_CHILD = 8,
368};
369struct {
370const char *name;
371int type;
372} geomtype_table [] = {
373{"QUAD",GEOM_QUAD},
374{"LINE",GEOM_LINE},
375{"POINT",GEOM_POINT},
376{"SPRITE",GEOM_SPRITE},
377{"TRIANGLE",GEOM_TRIANGLE},
378{"GEOMETRY",GEOM_GEOMETRY},
379{"HANIM",GEOM_HANIM},
380{"CHILD",GEOM_CHILD},
381{NULL,0},
382};
383int lookup_geomtype(const char *name){
384 int iret,i;
385 iret=i=0;
386 for(;;){
387 if(geomtype_table[i].name == NULL) break;
388 if(!strcmp(geomtype_table[i].name,name)){
389 iret = geomtype_table[i].type;
390 break;
391 }
392 i++;
393 }
394 return iret;
395}
396//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};
397static 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,};
398static 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,};
399static 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};
400
401void compile_Shape (struct X3D_Shape *node);
402// COMPILE PARTICLE SYSTEM
403void compile_geom_particle(struct X3D_ParticleSystem* node) {
404 int i, j;
405 float* vertices; //*boxtris,
406
407 //compile shape specifics
408 //ConsoleMessage("compile_particlesystem\n");
409 //delegate to compile_shape - same order to appearance, geometry fields
410 compile_Shape((struct X3D_Shape*)node);
411
412 if (node->_tris == NULL) {
413 node->_tris = MALLOC(void*, 18 * sizeof(float));
414 //memcpy(node->_tris,quadtris,18*sizeof(float));
415 }
416 vertices = (float*)(node->_tris);
417 //rescale vertices, in case scale changed
418 for (i = 0; i < 6; i++) {
419 float* vert, * vert0;
420 vert0 = &quadtris[i * 3];
421 vert = &vertices[i * 3];
422 vert[0] = vert0[0] * node->particleSize.c[0];
423 vert[1] = vert0[1] * node->particleSize.c[1];
424 vert[2] = vert0[2];
425 }
426
427 if (node->texCoordRamp || node->texCoord) {
428 int ml, mq, mt, n;
429 struct X3D_TextureCoordinate* tc;
430 if (node->texCoordRamp)
431 tc = (struct X3D_TextureCoordinate*)node->texCoordRamp;
432 else
433 tc = (struct X3D_TextureCoordinate*)node->texCoord;
434 n = node->texCoordKey.n;
435 mq = n * 4; //quad
436 ml = n * 2; //2 pt line
437 mt = n * 6; //2 triangles
438
439 //malloc for both lines and tex, in case changed on the fly
440 if (!node->_ttex)
441 node->_ttex = MALLOC(void*, mt * 2 * sizeof(float));
442 if (!node->_ltex)
443 node->_ltex = MALLOC(void*, ml * 2 * sizeof(float));
444 if (tc->point.n == mq) {
445 //enough tex coords for quads, expand to suit triangles
446 // 4 - 3
447 // 5 / 2 2 triangle config
448 // 0 _ 1
449 float* ttex, * ltex;
450 ttex = (float*)node->_ttex;
451 for (i = 0; i < n; i++) {
452 int k;
453 for (j = 0, k = 0; j < 4; j++, k++) {
454 float* p = (float*)(float*)&tc->point.p[i * 4 + j];
455 veccopy2f(&ttex[(i * 6 + k) * 2], p);
456 if (k == 0) {
457 veccopy2f(&ttex[(i * 6 + 5) * 2], p); //copy to 5 (last of 0-6 2-triangle)
458 }
459 if (k == 2) {
460 k++;
461 veccopy2f(&ttex[(i * 6 + k) * 2], p); //copy 2 to 3 (start of 2nd triangle
462 }
463 }
464 }
465 if (0) for (i = 0; i < n; i++) {
466 for (j = 0; j < 6; j++)
467 printf("%f %f,", ttex[(i * 6 + j) * 2 + 0], ttex[(i * 6 + j) * 2 + 1]);
468 printf("\n");
469 }
470 //for(i=0;i<(n*6*2);i++){
471 // printf("%f \n",ttex[i]);
472 //}
473
474 ltex = (float*)node->_ltex;
475 for (i = 0; i < n; i++) {
476 // make something up for lines
477 for (j = 0; j < 2; j++) {
478 float p[2];
479 struct SFVec2f* sf = (struct SFVec2f*)&tc->point.p[i * 4 + j];
480 p[0] = sf->c[0];
481 p[1] = min(sf->c[1], .9999f); //clamp texture here otherwise tends to wrap around
482 veccopy2f(&ltex[(i * 2 + j) * 2], p);
483
484 }
485 }
486 }
487 if (tc->point.n == ml) {
488 //enough points for lines
489 float* ttex, * ltex;
490
491 ltex = (float*)node->_ltex;
492 for (i = 0; i < n; i++) {
493 // copy lines straightforwardly
494 for (j = 0; j < 2; j++) {
495 float p[2];
496 struct SFVec2f* sf = (struct SFVec2f*)&tc->point.p[i * 2 + j];
497 p[0] = sf->c[0];
498 p[1] = min(sf->c[1], .9999f); //clamp texture here otherwise tends to wrap around
499 veccopy2f(&ltex[(i * 2 + j) * 2], p);
500 }
501 }
502 if (0) for (i = 0; i < n; i++) {
503 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]);
504 }
505 //make something up for triangles
506 ttex = (float*)node->_ttex;
507 for (i = 0; i < n; i++) {
508 float* p;
509 j = i;
510 p = (float*)(float*)&tc->point.p[j * 2 + 0];
511 veccopy2f(&ttex[(i * 6 + 0) * 2], p); //copy to 0
512 veccopy2f(&ttex[(i * 6 + 5) * 2], p); //copy to 5
513 p = (float*)(float*)&tc->point.p[j * 2 + 1];
514 veccopy2f(&ttex[(i * 6 + 1) * 2], p); //copy to 1
515 j++;
516 j = j == n ? j - 1 : j; //clamp to last
517 p = (float*)(float*)&tc->point.p[j * 2 + 1];
518 veccopy2f(&ttex[(i * 6 + 2) * 2], p); //copy to 2
519 veccopy2f(&ttex[(i * 6 + 3) * 2], p); //copy to 3
520 p = (float*)(float*)&tc->point.p[j * 2 + 0];
521 veccopy2f(&ttex[(i * 6 + 4) * 2], p); //copy to 4
522 }
523 }
524 }
525}
526void compile_hanim_particle(struct X3D_ParticleSystem* pnode) {
527 struct X3D_HAnimHumanoid* node = (struct X3D_HAnimHumanoid*)pnode->geometry;
528 COMPILE_IF_REQUIRED
529}
530void compile_ParticleSystem(struct X3D_ParticleSystem *node){
531 int i,j, maxparticles;
532 Stack *_particles;
533
534 //compile GEOM type particles
535 node->_geometryType = lookup_geomtype(node->geometryType->strptr);
536 if (node->_geometryType < GEOM_HANIM) {
537 compile_geom_particle(node);
538 }
539 //compile HANIM type particles
540 if (node->_geometryType == GEOM_HANIM) {
541 compile_hanim_particle(node);
542 }
543
544 //compile generic particles
545 maxparticles = min(node->maxParticles,10000);
546 if(node->_particles == NULL)
547 node->_particles = newVector(particle,maxparticles);
548 _particles = node->_particles;
549 if(_particles->allocn < maxparticles) {
550 //resize /realloc vector, set nalloc, in case someone changed maxparticles on the fly
551 _particles->data = realloc(_particles->data,maxparticles);
552 _particles->allocn = maxparticles;
553 }
554
555 //compile time-dependent node
556 if(!node->_lasttime || node->enabled && !node->_lastEnabled)
557 node->_lasttime = TickTime();
558 if(node->enabled && !node->_lastEnabled){
559 node->isActive = TRUE;
560 MARK_EVENT (X3D_NODE(node),offsetof (struct X3D_ParticleSystem, isActive));
561 }else if(!node->enabled && node->_lastEnabled){
562 node->isActive = FALSE;
563 MARK_EVENT (X3D_NODE(node),offsetof (struct X3D_ParticleSystem, isActive));
564 }
565 node->_lastEnabled = node->enabled;
566 MARK_NODE_COMPILED
567}
568
569
570//PHYSICS
571void prep_windphysics(struct X3D_Node *physics){
572 //per-frame gustiness update
573 struct X3D_WindPhysicsModel *px = (struct X3D_WindPhysicsModel *)physics;
574 float speed;
575 speed = px->speed * (1.0f + uniformRandCentered()*px->gustiness);
576 px->_frameSpeed = speed;
577}
578void apply_windphysics(particle *pp, struct X3D_Node *physics, float dtime){
579 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#WindPhysicsModel
580 struct X3D_WindPhysicsModel *px = (struct X3D_WindPhysicsModel *)physics;
581 if(px->enabled && pp->mass != 0.0f){
582 float pressure;
583 float force, speed;
584 float turbdir[3], pdir[3],acceleration[3], v2[3];
585 speed = px->_frameSpeed;
586 pressure = powf(10.0f,2.0f*log10f(speed)) * .64615f;
587 force = pressure * pp->surfaceArea;
588 randomDirection(turbdir);
589 vecscale3f(turbdir,turbdir,px->turbulence);
590 vecadd3f(pdir,px->direction.c,turbdir);
591 vecnormalize3f(pdir,pdir);
592 vecscale3f(pdir,pdir,force);
593
594 vecscale3f(acceleration,pdir,1.0f/pp->mass);
595 vecscale3f(v2,acceleration,dtime);
596 vecadd3f(pp->velocity,pp->velocity,v2);
597 float flen = veclength3f(pp->velocity);
598 if (flen > 0.0f) {
599 vecscale3f(pp->direction, pp->velocity, 1.0f / flen);
600 }
601
602 }
603}
604int intersect_polyrep(struct X3D_Node *node, float *p1, float *p2, float *nearest, float *normal);
605
606void compile_geometry(struct X3D_Node *gnode){
607 //this needs generalizing, for all triangle geometry, and I don't know how
608 if(gnode)
609 switch(gnode->_nodeType){
610 case NODE_IndexedFaceSet:
611 {
612 struct X3D_IndexedFaceSet *node = (struct X3D_IndexedFaceSet *)gnode;
613 //COMPILE_POLY_IF_REQUIRED (node->coord, node->fogCoord, node->color, node->normal, node->texCoord)
614 if (!compile_poly_if_required(node, node->coord, node->fogCoord, node->color, node->normal, node->texCoord))return;
615 }
616 break;
617 default:
618 break;
619 }
620
621}
622int intersect_geometry(struct X3D_Node *gnode, float *p1, float *p2, float *nearest, float *normal){
623 //this needs generalizing for all triangle geometry
624 int iret = 0;
625 if(gnode)
626 switch(gnode->_nodeType){
627 case NODE_IndexedFaceSet:
628 iret = intersect_polyrep(gnode,p1,p2,nearest,normal);
629 break;
630 default:
631 break;
632 }
633 return iret;
634}
635void apply_boundedphysics(particle *pp, struct X3D_Node *physics, float *positionChange){
636 struct X3D_BoundedPhysicsModel *px = (struct X3D_BoundedPhysicsModel *)physics;
637 if(px->enabled && px->geometry ) { //&& pp->mass != 0.0f){
638 //shall we assume its a 100% elastic bounce?
639 int nintersections;
640 // int ntries;
641 struct X3D_Node *node = (struct X3D_Node *) px->geometry;
642 float pos1[3], pos2[3], pnearest[3],normal[3], delta[3];
643 static int count;
644
645 //make_IndexedFaceSet((struct X3D_IndexedFaceSet*)px->geometry);
646 //COMPILE_POLY_IF_REQUIRED (node->coord, node->fogCoord, node->color, node->normal, node->texCoord)
647 if(NODE_NEEDS_COMPILING)
648 compile_geometry(node);
649
650 //if polyrep
651 //veccopy3f(pos1,pp->position);
652
653 veccopy3f(pos1,pp->origin);
654 //vecadd3f(pos2,pp->position,positionChange);
655 vecadd3f(pos2,pp->position,positionChange);
656 //ntries = 0;
657 count = 0;
658 //for(;;) //in theory we may travel far enough for 2 bounces
659 {
660 //if(pos2[0] >= .5f)
661 // printf("should hit\n");
662 nintersections = intersect_geometry(px->geometry,pos1,pos2,pnearest,normal);
663 if(nintersections > 0){
664 float d[3], r[3], rn[3], rd[3], n[3], ddotnn[3], orthogn[3], orthogn2[3];
665 float ddotn, speed, dlengthi,dlength;
666 vecdif3f(delta,pos2,pos1);
667 dlength = veclength3f(delta);
668 count++;
669
670 // get reflection
671 // pos1
672 // o|\d
673 // n<--x
674 // o|/r
675 // ddotn = dot(d,n)*n //projection of d onto n, as vector
676 // o = d - ddotn //vector orthogonal to n, such that d = ddotn + o
677 // r = ddotn - o
678 // or
679 // r = ddotn - (d - ddotn)
680 // or
681 // r = 2*ddotn - d
682 // or
683 // r = -(d - 2*dot(d,n)*n)
684 // http://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector
685
686 vecdif3f(d,pnearest,pos1);
687 dlengthi = veclength3f(d);
688 vecnormalize3f(n,normal);
689 ddotn = vecdot3f(d,n);
690 //ddotn = -ddotn; //assuming the surface normal is pointing out
691 vecscale3f(ddotnn,n,ddotn);
692 vecdif3f(orthogn,d,ddotnn);
693 vecscale3f(orthogn2,orthogn,2.0f);
694 vecdif3f(r,d,orthogn2);
695 vecscale3f(r,r,-1.0f); //reverse direction
696 vecnormalize3f(rn,r);
697 // update the velocity vector direction (keep speed constant, assuming elastic bounce)
698 // specs: could use an elasticity factor
699 speed = veclength3f(pp->velocity);
700 vecscale3f(pp->velocity,rn,speed);
701 float flen = veclength3f(pp->velocity);
702 if (flen > 0.0f) {
703 vecscale3f(pp->direction, pp->velocity, 1.0f / flen);
704 }
705
706 //do positionChange here, and zero positionchange for calling code
707 vecscale3f(rd,rn,dlength - dlengthi);
708 vecadd3f(pp->position,pnearest,rd);
709 vecscale3f(positionChange,positionChange,0.0f);
710 veccopy3f(pos1,pnearest);
711 veccopy3f(pp->origin,pos1);
712 veccopy3f(pos2,pp->position);
713 if(0) pp->age = 1000.0f; //simply expire if / when goes outside
714 // specs could add death-on-hitting-wall
715 }
716 //break;
717 //if(nintersections == 0)break;
718 //ntries++;
719 //if(ntries > 3)break;
720 }
721 }
722}
723void apply_forcephysics(particle *pp, struct X3D_Node *physics, float dtime){
724 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#ForcePhysicsModel
725 // I think Octaga mis-interprets the [0 -9.81 0] force as acceleartion of gravity.
726 // it will be if mass==1. Otherwise you need to scale your force by mass:
727 // ie if your mass is 10, then force needs to be [0 98.1 0]
728 struct X3D_ForcePhysicsModel *px = (struct X3D_ForcePhysicsModel *)physics;
729 //a = F/m;
730 //v += a*dt
731 if(px->enabled && pp->mass != 0.0f){
732 float acceleration[3], v2[3];
733 vecscale3f(acceleration,px->force.c,1.0f/pp->mass);
734 vecscale3f(v2,acceleration,dtime);
735 vecadd3f(pp->velocity,pp->velocity,v2);
736 float flen = veclength3f(pp->velocity);
737 if (flen > 0.0f) {
738 vecscale3f(pp->direction, pp->velocity, 1.0f / flen);
739 }
740
741 }
742}
743
744void apply_resistancephysics(particle* pp, struct X3D_Node* physics, float dtime) {
745 struct X3D_ResistancePhysicsModel* px = (struct X3D_ResistancePhysicsModel*)physics;
746 //a = F/m;
747 //v += a*dt
748 if (px->enabled && pp->mass != 0.0f) {
749 float deceleration, v2;
750 deceleration = px->force / pp->mass;
751 v2 = 1.0f - deceleration * dtime;
752 vecscale3f(pp->velocity, pp->velocity, v2);
753 float flen = veclength3f(pp->velocity);
754 if (flen > 0.0f) {
755 vecscale3f(pp->direction, pp->velocity, 1.0f / flen);
756 }
757
758 }
759}
760
761//EMITTERS
762void apply_ConeEmitter(particle *pp, struct X3D_Node *emitter){
763 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#ConeEmitter
764 // like point emitter, except we need work in the direction:
765 // 2 randoms, one for angle-from-direction < e.angle, and one for angle-around-direction 0-2PI
766 struct X3D_ConeEmitter *e = (struct X3D_ConeEmitter *)emitter;
767 float direction[3], tilt, azimuth, speed;
768 {
769 //prep - can be done once per direction
770 //need 2 orthogonal axes
771 //a) find a minor axis
772 float amin;
773 float orthog1[3],orthog2[3];
774 int i,imin,method;
775 vecnormalize3f(direction,e->direction.c);
776 amin = min(min(fabsf(direction[0]),fabsf(direction[1])),fabsf(direction[2]));
777 imin = 0;
778 for(i=0;i<3;i++){
779 if(fabsf(direction[i]) == amin){
780 imin = i; break;
781 }
782 }
783 //make a vector with the minor axis dominant
784 for(i=0;i<3;i++) orthog1[i] = 0.0f;
785 orthog1[imin] = 1.0f;
786 //orthog1 will only be approximately orthogonal to direction
787 //do a cross product to get ortho2
788 veccross3f(orthog2,direction,orthog1);
789 //orthog2 will be truely orthogonal
790 //cross orthog2 with direction to get truely orthog1
791 veccross3f(orthog1,direction,orthog2);
792
793 //for this particle
794 method = 2;
795 if(method == 1){
796 //METHOD 1: TILT + AZIMUTH
797 //tends to crowd/cluster around central direction, and fade with tilt
798 //(due to equal chance of tilt angle, but larger area to cover as tilt goes up)
799 //direction = cos(tilt)*direction
800 //az = cos(azimuth)*orthog1 + sin(azimuth)*orthog2
801 //az = sin(tilt)*az
802 //direction += az;
803 //where
804 // tilt - from e.direction axis
805 // azimuth - angle around e.direction vector (in plane orthogonal to direction vector)
806 // az[3] - vector in the orthogonal plane, in direction of azimuth
807 float az[3],az1[3],az2[3],ctilt,stilt,caz,saz;
808 tilt = uniformRand()*e->angle;
809 ctilt = cosf(tilt);
810 stilt = sinf(tilt);
811 azimuth = uniformRand()*2.0f*(float)PI;
812 caz = cosf(azimuth);
813 saz = sinf(azimuth);
814 vecscale3f(az1,orthog1,caz);
815 vecscale3f(az2,orthog2,saz);
816 vecadd3f(az,az1,az2);
817 //now az is a unit vector in orthogonal plane, in direction of azimuth
818 vecscale3f(az,az,stilt);
819 //now az is scaled for adding to direction
820 vecscale3f(direction,direction,ctilt);
821 //direction is shrunk (or reversed) to account for tilt
822 vecadd3f(direction,direction,az);
823 //now direction is unit vector in tilt,az direction from e.direction
824 }
825 if(method == 2){
826 //METHOD 2: POINT IN CIRCLE
827 //tends to give even distribution over circle face of cone
828 //xy = randomCircle
829 //orthog = x*orthog1 + y*orthog2
830 //direction += orthog
831 float xy[2], orx[3],ory[3], orthog[3];
832 circleRand2D(xy);
833 //orthog = x*orthog1 + y*orthog2
834 vecscale3f(orx,orthog1,xy[0]);
835 vecscale3f(ory,orthog2,xy[1]);
836 vecadd3f(orthog,orx,ory);
837 vecscale3f(orthog,orthog,sinf(e->angle));
838 vecscale3f(direction,direction,cosf(e->angle));
839 //direction += orthog
840 vecadd3f(direction,orthog,direction);
841 //normalize(direction)
842 vecnormalize3f(direction,direction);
843 }
844
845 }
846 memcpy(pp->position,e->position.c,3*sizeof(float));
847 speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
848 vecscale3f(pp->velocity,direction,speed);
849 float flen = veclength3f(direction);
850 if (flen > 0.0f) {
851 vecscale3f(pp->direction, direction, 1.0f / flen);
852 }
853 pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
854 pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
855
856}
857void apply_ExplosionEmitter(particle *pp, struct X3D_Node *emitter){
858 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#ExplosionEmitter
859 // like point emitter, except always random direction
860 // the create-all-at-time-zero is handled up one level
861 struct X3D_ExplosionEmitter *e = (struct X3D_ExplosionEmitter *)emitter;
862 float direction[3], speed;
863 memcpy(pp->position,e->position.c,3*sizeof(float));
864 randomDirection(direction);
865 speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
866 vecscale3f(pp->velocity,direction,speed);
867 float flen = veclength3f(direction);
868 if (flen > 0.0f) {
869 vecscale3f(pp->direction, direction, 1.0f / flen);
870 }
871 pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
872 pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
873}
874void apply_PointEmitter(particle *pp, struct X3D_Node *emitter){
875 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#PointEmitter
876 // like explosion, except may have a non-random direction
877 struct X3D_PointEmitter *e = (struct X3D_PointEmitter *)emitter;
878 float direction[3], speed;
879 memcpy(pp->position,e->position.c,3*sizeof(float));
880 if(veclength3f(e->direction.c) < .00001){
881 randomDirection(direction);
882 }else{
883 memcpy(direction,e->direction.c,3*sizeof(float));
884 vecnormalize3f(direction,direction);
885 }
886 speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
887 vecscale3f(pp->velocity,direction,speed);
888 float flen = veclength3f(direction);
889 if (flen > 0.0f) {
890 vecscale3f(pp->direction, direction, 1.0f / flen);
891 }
892 pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
893 pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
894
895}
896enum {
897 POLYLINEEMITTER_METHODA = 1,
898 POLYLINEEMITTER_METHODB = 2,
899};
900void compile_PolylineEmitter(struct X3D_Node *node){
901 struct X3D_PolylineEmitter *e = (struct X3D_PolylineEmitter *)node;
902 float *segs = NULL;
903 //e->_method = POLYLINEEMITTER_METHODA;
904 e->_method = POLYLINEEMITTER_METHODB;
905 if(e->coord && e->coordIndex.n > 1){
906 //convert IndexedLineSet to pairs of coordinates
907 int i,k,ind, n,nseg = 0;
908 float *pts[2];
909 struct X3D_Coordinate *coord = (struct X3D_Coordinate *)e->coord;
910 n = e->coordIndex.n;
911 segs = MALLOC(void*,2*3*sizeof(float) *n*2 ); //2 vertices per lineseg, and 1 lineseg per coordindex should be more than enough
912 k = 0;
913 nseg = 0;
914 for(i=0;i<e->coordIndex.n;i++){
915 ind = e->coordIndex.p[i];
916 if( ind == -1) {
917 k = 0;
918 continue;
919 }
920 pts[k] = (float*)&coord->point.p[ind];
921 k++;
922 if(k==2){
923 veccopy3f(&segs[(nseg*2 +0)*3],pts[0]);
924 veccopy3f(&segs[(nseg*2 +1)*3],pts[1]);
925 pts[0] = pts[1];
926 nseg++;
927 k = 1;
928 }
929 }
930 e->_segs = segs;
931 e->_nseg = nseg;
932 }
933 if(e->_method == POLYLINEEMITTER_METHODB){
934 int i;
935 float *portions, totaldist, dist, delta[3];
936 portions = MALLOC(float *,e->_nseg * sizeof(float));
937 e->_portions = portions;
938 totaldist = 0.0f;
939 for(i=0;i<e->_nseg;i++){
940 vecdif3f(delta,&segs[(i*2 + 1)*3],&segs[(i*2 + 0)*3]);
941 dist = veclength3f(delta);
942 //printf("dist %d %f\n",i,dist);
943 portions[i] = dist;
944 totaldist += dist;
945 }
946 for(i=0;i<e->_nseg;i++){
947 portions[i] = portions[i]/totaldist;
948 //printf("portion %d %f\n",i,portions[i]);
949 }
950 }
951 MARK_NODE_COMPILED
952}
953void apply_PolylineEmitter(particle *pp, struct X3D_Node *node){
954 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#PolylineEmitter
955 float direction[3], speed;
956
957 struct X3D_PolylineEmitter *e = (struct X3D_PolylineEmitter *)node;
958 //like point emitter, except posiion is drawn randomly along polyline
959 //option A: pick segment index at random, then pick distance along segment at random (Octaga?)
960 //option B: pick random 0-1, then map that to cumulative distance along polyline
961 if(NODE_NEEDS_COMPILING)
962 compile_PolylineEmitter(node);
963 memset(pp->position,0,3*sizeof(float)); //in case no coords/polyline/segs
964 if(e->_method == POLYLINEEMITTER_METHODA && e->_nseg){
965 float *segs, delta[3], pos[3];
966 // pick a segment at random:
967 int iseg = (int) floorf(uniformRand() * (float)e->_nseg);
968 //pick a point on the segment
969 float fraction = uniformRand();
970 segs = (float *)e->_segs;
971 vecdif3f(delta,&segs[(iseg*2 + 1)*3],&segs[(iseg*2 + 0)*3]);
972 vecscale3f(delta,delta,fraction);
973 vecadd3f(pos,&segs[(iseg*2 + 0)*3],delta);
974 veccopy3f(pp->position,pos);
975 }
976 if(e->_method == POLYLINEEMITTER_METHODB && e->_nseg){
977 //pick rand 0-1
978 int i;
979 float cumulative, fraction, *portions, *segs, delta[3], pos[3], segfraction;
980 fraction = uniformRand();
981 portions = (float*)e->_portions;
982 cumulative = 0.0f;
983 for(i=0;i<e->_nseg;i++){
984 cumulative +=portions[i];
985 if(cumulative > fraction){
986 segfraction = (cumulative - fraction) / portions[i];
987 segs = (float *)e->_segs;
988 vecdif3f(delta,&segs[(i*2 + 1)*3],&segs[(i*2 + 0)*3]);
989 vecscale3f(delta,delta,segfraction);
990 vecadd3f(pos,&segs[(i*2 + 0)*3],delta);
991 veccopy3f(pp->position,pos);
992 break;
993 }
994 }
995 }
996
997 //the rest is like point emitter:
998//not for polyline see above memcpy(pp->position,e->position.c,3*sizeof(float));
999 if(veclength3f(e->direction.c) < .00001){
1000 randomDirection(direction);
1001 }else{
1002 memcpy(direction,e->direction.c,3*sizeof(float));
1003 vecnormalize3f(direction,direction);
1004 }
1005 speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
1006 vecscale3f(pp->velocity,direction,speed);
1007 float flen = veclength3f(direction);
1008 if (flen > 0.0f) {
1009 vecscale3f(pp->direction, direction, 1.0f / flen);
1010 }
1011 pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
1012 pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
1013
1014}
1015int getPolyrepTriangleCount(struct X3D_Node *node);
1016int getPolyrepTriangleByIndex(struct X3D_Node *node, int index, float *v1, float *v2, float *v3);
1017
1018void apply_SurfaceEmitter(particle *pp, struct X3D_Node *emitter){
1019 struct X3D_SurfaceEmitter *e = (struct X3D_SurfaceEmitter *)emitter;
1020 struct X3D_Node *node;
1021
1022 node = e->surface ? e->surface : e->geometry;
1023 if(node){
1024 int index, ntri;
1025 float fraction;
1026 float speed;
1027 float xyz[3], v1[3],v2[3],v3[3],e1[3],e2[3], normal[3], direction[3];
1028
1029 if(NODE_NEEDS_COMPILING){
1030 compile_geometry(X3D_NODE(node));
1031 }
1032
1033
1034 fraction = uniformRand();
1035 ntri = getPolyrepTriangleCount(node);
1036 if(ntri){
1037 index = (int)floorf(fraction * (float)(ntri-1));
1038 getPolyrepTriangleByIndex(node,index,v1,v2,v3);
1039 randomTriangleCoord(xyz,v1,v2,v3);
1040 vecdif3f(e1,v2,v1);
1041 vecdif3f(e2,v3,v1);
1042 veccross3f(normal,e1,e2);
1043 vecnormalize3f(direction,normal);
1044
1045 }
1046
1047 //the rest is like point emitter
1048 memcpy(pp->position,xyz,3*sizeof(float));
1049 speed = e->speed*(1.0f + uniformRandCentered()*e->variation);
1050 vecscale3f(pp->velocity,direction,speed);
1051 float flen = veclength3f(direction);
1052 if (flen > 0.0f) {
1053 vecscale3f(pp->direction, direction, 1.0f / flen);
1054 }
1055 pp->mass = e->mass*(1.0f + uniformRandCentered()*e->variation);
1056 pp->surfaceArea = e->surfaceArea*(1.0f + uniformRandCentered()*e->variation);
1057 }
1058
1059}
1060
1061
1062
1063void apply_VolumeEmitter(particle* pp, struct X3D_Node* emitter) {
1064 struct X3D_VolumeEmitter* e = (struct X3D_VolumeEmitter*)emitter;
1065 if (!e->_ifs && e->coord) {
1066 struct X3D_IndexedFaceSet* ifs;
1067 ifs = createNewX3DNode0(NODE_IndexedFaceSet);
1068 ifs->coord = e->coord;
1069 ifs->coordIndex = e->coordIndex;
1070 compile_geometry(X3D_NODE(ifs));
1071 e->_ifs = ifs;
1072 }
1073 if (e->_ifs) {
1074 int nint, i, isInside;
1075 float xyz[3], plumb[3], nearest[3], normal[3];
1076 float direction[3], speed;
1077 struct X3D_IndexedFaceSet* ifs = (struct X3D_IndexedFaceSet*)e->_ifs;
1078
1079 isInside = FALSE;
1080 for (i = 0; i < 10; i++) {
1081 randomPoint3D(xyz);
1082 //spread random points over box
1083 xyz[0] *= ifs->EXTENT_MAX_X - ifs->EXTENT_MIN_X;
1084 xyz[1] *= ifs->EXTENT_MAX_Y - ifs->EXTENT_MIN_Y;
1085 xyz[2] *= ifs->EXTENT_MAX_Z - ifs->EXTENT_MIN_Z;
1086 veccopy3f(plumb, xyz);
1087 plumb[2] = ifs->EXTENT_MIN_Z - 1.0f; //ray end point below box
1088 nint = intersect_geometry(e->_ifs, xyz, plumb, nearest, normal);
1089 nint = abs(nint) % 2;
1090 if (nint == 1) {
1091 isInside = TRUE;
1092 break; //if there's an odd number of intersections, its inside, else even outside
1093 }
1094 }
1095 if (!isInside)
1096 vecscale3f(xyz, xyz, 0.0f); //emit from 0
1097 //the rest is like point emitter
1098 memcpy(pp->position, xyz, 3 * sizeof(float));
1099 if (veclength3f(e->direction.c) < .00001) {
1100 randomDirection(direction);
1101 }
1102 else {
1103 memcpy(direction, e->direction.c, 3 * sizeof(float));
1104 vecnormalize3f(direction, direction);
1105 }
1106 speed = e->speed * (1.0f + uniformRandCentered() * e->variation);
1107 vecscale3f(pp->velocity, direction, speed);
1108 float flen = veclength3f(direction);
1109 if (flen > 0.0f) {
1110 vecscale3f(pp->direction, direction, 1.0f / flen);
1111 }
1112 pp->mass = e->mass * (1.0f + uniformRandCentered() * e->variation);
1113 pp->surfaceArea = e->surfaceArea * (1.0f + uniformRandCentered() * e->variation);
1114 }
1115}
1116
1117
1118// BEGIN HUMANOID PARTICLE SECTION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1119struct rgba { unsigned char r, g, b, a; };
1120typedef union {
1121 unsigned char bytes[4];
1122 struct rgba;
1123 short int16[2];
1124 int int32;
1125} pix;
1126
1127unsigned char* sample_image(textureTableIndexStruct_s* tt, float x, float y) {
1128 //x, y in range 0.0 to 1.0
1129 int px, py, ix, iy;
1130 px = tt->x;
1131 py = tt->y;
1132 ix = (int)( px * x );
1133 ix = ix < 0 ? 0 : ix >= tt->x ? tt->x - 1 : ix;
1134 iy = (int)(py * y);
1135 iy = iy < 0 ? 0 : iy >= tt->y ? tt->y - 1 : iy;
1136 unsigned char* pixel = &tt->texdata[(iy * px + ix) * 4]; // tt->channels];
1137 return pixel;
1138}
1139void set_image_pixel_color(unsigned char * image, int cols, int rows, unsigned char * pixel, int x, int y) {
1140 memcpy(&image[(y * cols + x) * 4], pixel, 3);
1141}
1142unsigned char* get_image_pixel_color(unsigned char* image, int cols, int rows, int x, int y) {
1143 return &image[(y * cols + x) * 4];
1144}
1145void set_image_pixel_transparency(unsigned char* image, int cols, int rows, unsigned char transparency, int x, int y) {
1146 image[(y * cols + x) * 4 + 3] = transparency;
1147}
1148unsigned char get_image_pixel_transparency(unsigned char* image, int cols, int rows, int x, int y) {
1149 return image[(y * cols + x) * 4 + 3];
1150}
1151unsigned char get_image_pixel_channel(unsigned char* image, int cols, int rows, int channel, int x, int y) {
1152 return image[(y * cols + x) * 4 + channel];
1153}
1154void set_image_pixel_channel(unsigned char* image, int cols, int rows, unsigned char c, int channel, int x, int y) {
1155 image[(y * cols + x) * 4 + channel] = c;
1156}
1157void print_image_channel(unsigned char* imageRGBA, int channel, int width, int height) {
1158 for (int k = 0; k < width; k += 10) printf("%d ", k / 10);
1159 printf("\n");
1160 for (int j = 0; j < height; j++) {
1161 for (int k = 0; k < width; k++) {
1162 unsigned char c = get_image_pixel_channel(imageRGBA, width, height, channel, k, j);
1163 if (c)
1164 printf("%c", c + 'A');
1165 else
1166 printf("%c", ' ');
1167 }
1168 printf(" %2d\n", j);
1169 }
1170}
1171
1172
1173float* extent4f_clear(float* e) {
1174 e[0] = 10000.0f;
1175 e[1] = 10000.0f;
1176 e[2] = -10000.0f;
1177 e[3] = -10000.0f;
1178 return e;
1179}
1180int extent4f_isSet(float* e4) {
1181 //extents are set with min > max, so a way to tell
1182 // if they are set is to check if min <= max or max >= min
1183 int iret;
1184 float* e = e4;
1185 iret = (e[2] >= e[0] && e[3] >= e[1]) ? TRUE : FALSE;
1186 return iret;
1187}
1188float * extent4f_union_extent4f(float *e4, float *ein4){
1189 int i, isa, isb;
1190 isa = extent4f_isSet(e4);
1191 isb = extent4f_isSet(ein4);
1192 if (isa && isb)
1193 for (i = 0; i < 2; i++) {
1194 e4[i] = min(e4[i], ein4[i]); //the miniumum of the minimums
1195 e4[i+2] = max(e4[i+2], ein4[i+2]); //the maximum of the maximums
1196 }
1197 else if (isb) veccopy4f(e4, ein4);
1198 return e4;
1199}
1200float* extent4f_union_vec2f(float* extent4, float* p2) {
1201 int i, isa, isb;
1202 isa = extent4f_isSet(extent4);
1203 if (!isa)
1204 for (i = 0; i < 2; i++) {
1205 extent4[i] = p2[i];
1206 extent4[i+2] = p2[i];
1207 }
1208 for (i = 0; i < 2; i++) {
1209 extent4[i] = min(extent4[i], p2[i]);
1210 extent4[i+2] = max(extent4[i+2], p2[i]);
1211 }
1212 return extent4;
1213}
1214void extent4f_printf(float* extent4) {
1215 printf("min %f %f max %f %f \n", extent4[0], extent4[1], extent4[2], extent4[3]);
1216}
1217float* pixel2color3(float * color, unsigned char* pixel) {
1218 for (int i = 0; i < 3; i++)
1219 color[i] = ((float)(int)pixel[i]) / 255.0f;
1220 return color;
1221}
1222void apply_MapEmitter(particle* pp, struct X3D_Node* emitter) {
1223 struct X3D_MapEmitter* e = (struct X3D_MapEmitter*)emitter;
1224 vecset3f(pp->position, 0.0f, 0.0f, 0.0f);
1225 //give it a random walking speed +- 1m/s from e->speed
1226 pp->speed = normalRand() * e->variation + e->speed;
1227 pp->speed = pp->speed <= 0.0 ? e->speed : pp->speed;
1228 pp->_startTime[0] = pp->_startTime[1] = TickTime(); //for HAnim, but could be used for anything
1229 //pp->speed = e->speed;
1230
1231 pp->sink = -1; //we won't assign a sink until physics, because that's when we count the sinks
1232 if (e->functionMap) {
1233 //printf("functionMap type %s\n", stringNodeType(e->functionMap->_nodeType));
1234 render_node(e->functionMap);
1235 textureTableIndexStruct_s* tt = getTableTableFromTextureNode(e->functionMap);
1236 if (tt && tt->status >= TEX_READ) {
1237 if (e->emitterColor.n)
1238 {
1239 if (!e->classified) {
1240 //make a 2D box around each emitter color area, so we don't have to
1241 // search the whole image pixel by pixel on each frame
1242 printf("start classifying emitter..\n");
1243 e->eboxes.p = malloc(e->emitterColor.n * sizeof(struct SFVec4f));
1244 e->eboxes.n = e->emitterColor.n;
1245 e->iboxes.p = malloc(e->emitterColor.n * sizeof(struct SFVec4f));
1246 e->iboxes.n = e->emitterColor.n;
1247 for (int i = 0; i < e->eboxes.n; i++) {
1248 extent4f_clear(e->eboxes.p[i].c);
1249 extent4f_clear(e->iboxes.p[i].c);
1250 }
1251 //for now assume one human-step-sized grid cell is 1m
1252 int isteps[2];
1253 isteps[0] = (int)(e->gridSize.c[0] + .5f);
1254 isteps[1] = (int)(e->gridSize.c[1] + .5f);
1255 for(int i=0; i< isteps[0];i++)
1256 for (int j = 0; j < isteps[1]; j++) {
1257 float x, y, s[3], exy[2], ixy[2];
1258 //image sampling coords
1259 x = (float)i / (float)e->gridSize.c[0];
1260 y = (float)j / (float)e->gridSize.c[1];
1261 ixy[0] = x;
1262 ixy[1] = y;
1263 //scene grid coords in meters
1264 exy[0] = (float)i - e->gridSize.c[0]/2.0f;
1265 exy[1] = (float)j - e->gridSize.c[1]/2.0f;
1266 unsigned char* pixel = sample_image(tt, x, y);
1267 pixel2color3(s, pixel);
1268
1269 for (int k = 0; k < e->emitterColor.n; k++) {
1270 float* c = e->emitterColor.p[k].c;
1271 int is_close = vecclose3f(s, c, e->colorMatchTolerance);
1272 if (is_close) {
1273 extent4f_union_vec2f(e->iboxes.p[k].c, ixy);
1274 extent4f_union_vec2f(e->eboxes.p[k].c, exy);
1275 }
1276 //printf("k %d c %f %f %f s %f %f %f close %d\n", k, c[0], c[1], c[2], s[0], s[1], s[2], is_close);
1277 }
1278
1279 }
1280 for (int i = 0; i < e->eboxes.n; i++) {
1281 extent4f_printf(e->eboxes.p[i].c);
1282 extent4f_printf(e->iboxes.p[i].c);
1283 }
1284 printf("..end classfying emitter\n");
1285 e->classified = TRUE;
1286 }
1287 // emit one from one randomly chosen emitter color area
1288 // but only from the emitter areas found in the function_map image
1289 int valid_regions = 0;
1290 for (int i = 0; i < e->iboxes.n; i++)
1291 if (extent4f_isSet(e->eboxes.p[i].c)) valid_regions++;
1292 int iregion = (int)(uniformRand() * (float)(valid_regions));
1293 int nvalid = -1;
1294 int ivalid = 0;
1295 for (int i = 0; i < e->iboxes.n; i++)
1296 {
1297 int is_set = extent4f_isSet(e->iboxes.p[i].c);
1298 if (is_set) nvalid++;
1299 if (is_set && nvalid == iregion) {
1300 int i = iregion;
1301 float x, y, xyz[3], s[3];
1302 float exy[2], ixy[2];
1303 int more = TRUE;
1304 do {
1305 x = uniformRand();
1306 y = uniformRand();
1307 //scale to image box
1308 ixy[0] = x * (e->iboxes.p[i].c[2] - e->iboxes.p[i].c[0]) + e->iboxes.p[i].c[0];
1309 ixy[1] = y * (e->iboxes.p[i].c[3] - e->iboxes.p[i].c[1]) + e->iboxes.p[i].c[1];
1310 //scale to scene box
1311 exy[0] = x * (e->eboxes.p[i].c[2] - e->eboxes.p[i].c[0]) + e->eboxes.p[i].c[0];
1312 exy[1] = y * (e->eboxes.p[i].c[3] - e->eboxes.p[i].c[1]) + e->eboxes.p[i].c[1];
1313
1314 unsigned char* pixel = sample_image(tt, ixy[0], ixy[1]);
1315 pixel2color3(s, pixel);
1316 if (vecclose3f(s, e->emitterColor.p[i].c, e->colorMatchTolerance))
1317 more = FALSE;
1318 } while (more);
1319 vecset3f(xyz, exy[0], exy[1], 0.0f);
1320 //the rest is like point emitter
1321 printf("iregion %d xy %f %f valid_regions %d", iregion, exy[0], exy[1], valid_regions);
1322 veccopy3f(pp->position, xyz);
1323 //HAnimPermuter method
1324 pp->permutationIndex = -1; //on first draw do uniformRand()*permutations.n
1325 break;
1326 }
1327 }
1328 }
1329 }
1330 }
1331
1332}
1333
1334int emitter_loaded(struct X3D_Node* emitter) {
1335 int loaded = FALSE;
1336 switch (emitter->_nodeType) {
1337 case NODE_MapEmitter:
1338 {
1339 struct X3D_MapEmitter* e = (struct X3D_MapEmitter*)emitter;
1340 if (e->functionMap) {
1341 textureTableIndexStruct_s* tt = getTableTableFromTextureNode(e->functionMap);
1342 tt->no_gl = TRUE; //don't load in GL, and preserve texdata for processing
1343 render_node(e->functionMap);
1344 if (tt && tt->status >= TEX_READ) loaded = TRUE;
1345 //printf("functionMap type %s loaded %d \n", stringNodeType(e->functionMap->_nodeType), loaded);
1346 }
1347 }
1348 break;
1349 default:
1350 loaded = TRUE;
1351 break;
1352 }
1353 return loaded;
1354}
1355void norm2image(int *ixy, int *isize, float *fxy) {
1356 //convert from 0-1 floats to image pixel coords
1357 ixy[0] = (int)(fxy[0] * (float)isize[0] + .5f);
1358 ixy[1] = (int)(fxy[1] * (float)isize[1] + .5f);
1359 ixy[0] = max(min(isize[0] - 1, ixy[0]),0);
1360 ixy[1] = max(min(isize[1] - 1, ixy[1]),0);
1361}
1362void image2norm(float* fxy, int* ixy, int* isize) {
1363 //convert from image pixel coords to 0-1 floats
1364 fxy[0] = (float)ixy[0] / (float)isize[0];
1365 fxy[1] = (float)ixy[1] / (float)isize[1];
1366}
1367void saveSnapshotBMP(char* pathname, char* buffer, int bytesPerPixel, int width, int height);
1368void set_debug_quad(int which_debug_shader, int textureID);
1369void render_debug_quad();
1370void display_imagedata4(unsigned char* texdata, int width, int height, int imageIndex) {
1371 //makes or updates a gl texture, and pops it up on the screen at end of frame render
1372 //assumes 4 bytes per pixel
1373 static textureTableIndexStruct_s tts;
1374 static int once = 0;
1375 tts.x = width;
1376 tts.y = height;
1377 tts.z = 1;
1378 tts.texdata = texdata;
1379 tts.channels = 4;
1380 if (!once) {
1381 FW_GL_GENTEXTURES(1, &tts.OpenGLTexture);
1382 once = 1;
1383 }
1384 char namebuf[200];
1385 if (0) {
1386 //works but not needed if saving .bmp
1387 sprintf(namebuf, "C:\\tmp\\sinkmap%d.web3dit", imageIndex);
1388 saveImage_web3dit(&tts, namebuf);
1389 }
1390 sprintf(namebuf, "C:\\tmp\\sinkmap%d.bmp", imageIndex);
1391 saveSnapshotBMP(namebuf, tts.texdata, tts.channels, tts.x, tts.y);
1392
1393 // popup image doesn't render here (but cubemap use does work)
1394 //glActiveTexture(GL_TEXTURE0);
1395 glBindTexture(GL_TEXTURE_2D,tts.OpenGLTexture);
1396 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tts.x, tts.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, tts.texdata);
1397 set_debug_quad(1, tts.OpenGLTexture);
1398 //render_debug_quad();
1399}
1400
1401
1402void apply_mapphysics(particle* pp, struct X3D_Node* physics, float dtime) {
1403 struct X3D_MapPhysicsModel* px = (struct X3D_MapPhysicsModel*)physics;
1404 //a = F/m;
1405 //v += a*dt
1406 if (px->enabled ) {
1407 if (!px->classified) {
1408 if (px->functionMap) {
1409 //printf("functionMap type %s\n", stringNodeType(e->functionMap->_nodeType));
1410 textureTableIndexStruct_s* tt = getTableTableFromTextureNode(px->functionMap);
1411 tt->no_gl = TRUE;
1412 render_node(px->functionMap);
1413
1414 if (tt && tt->status >= TEX_READ) {
1415
1416 if (px->sinkColor.n)
1417 {
1418 printf("start classifying physics..\n");
1419 //make a 2D box around each sink color area, so we don't have to
1420 // search the whole image pixel by pixel on each frame
1421 px->eboxes.p = malloc(px->sinkColor.n * sizeof(struct SFVec4f));
1422 px->eboxes.n = px->sinkColor.n;
1423 px->iboxes.p = malloc(px->sinkColor.n * sizeof(struct SFVec4f));
1424 px->iboxes.n = px->sinkColor.n;
1425 for (int i = 0; i < px->eboxes.n; i++) {
1426 extent4f_clear(px->eboxes.p[i].c);
1427 extent4f_clear(px->iboxes.p[i].c);
1428 }
1429 //for now assume one human-step-sized grid cell is .25m
1430 int isteps[2];
1431 isteps[0] = ((int)(px->gridSize.c[0] + .5f));
1432 isteps[1] = ((int)(px->gridSize.c[1] + .5f));
1433 for (int i = 0; i < isteps[0]; i++)
1434 for (int j = 0; j < isteps[1]; j++) {
1435 float x, y, s[3], exy[2], ixy[2];
1436 //image sampling coords
1437 x = (float)i / (float)px->gridSize.c[0];
1438 y = (float)j / (float)px->gridSize.c[1];
1439 ixy[0] = x;
1440 ixy[1] = y;
1441 //scene grid coords in meters
1442 exy[0] = (float)i - px->gridSize.c[0] / 2.0f;
1443 exy[1] = (float)j - px->gridSize.c[1] / 2.0f;
1444 unsigned char* pixel = sample_image(tt, x, y);
1445 pixel2color3(s, pixel);
1446
1447 for (int k = 0; k < px->sinkColor.n; k++) {
1448 float* c = px->sinkColor.p[k].c;
1449 int is_close = vecclose3f(s, c, px->colorMatchTolerance);
1450 if (is_close) {
1451 extent4f_union_vec2f(px->iboxes.p[k].c, ixy);
1452 extent4f_union_vec2f(px->eboxes.p[k].c, exy);
1453 }
1454 //printf("k %d c %f %f %f s %f %f %f close %d\n", k, c[0], c[1], c[2], s[0], s[1], s[2], is_close);
1455 }
1456
1457 }
1458 printf("sink boxes\n");
1459 for (int i = 0; i < px->eboxes.n; i++) {
1460 extent4f_printf(px->eboxes.p[i].c);
1461 extent4f_printf(px->iboxes.p[i].c);
1462 }
1463 // generate one sink map for each sink color area
1464 int jsteps[2];
1465 jsteps[0] = isteps[0] * 2;
1466 jsteps[1] = isteps[1] * 2;
1467 int sinkmapsize = jsteps[0] * jsteps[1] * 4;
1468 px->_sinkmaps = malloc(sinkmapsize * (px->eboxes.n + 1)); //one for each sink, plus a population map at index 0
1469 unsigned char* sinkmaps = (unsigned char*)px->_sinkmaps;
1470 unsigned char* popmap = &sinkmaps[0];
1471 memset(popmap, 0, sinkmapsize);
1472
1473 for (int i = 0; i < px->iboxes.n; i++)
1474 {
1475
1476 int is_set = extent4f_isSet(px->iboxes.p[i].c);
1477 if (is_set) {
1478
1479 //generate_sink_map()
1480 unsigned char* texdata = &sinkmaps[(i+1) * sinkmapsize];
1481 memset(texdata, 0, jsteps[0] * jsteps[1] * 4);
1482 //start sink map at center of ibox
1483 int icenter[2];
1484 float fcenter[2];
1485 float* bbox = &px->iboxes.p[i].c[0];
1486 fcenter[0] = ((bbox[0] + bbox[2]) / 2.0f);
1487 fcenter[1] = ((bbox[1] + bbox[3]) / 2.0f);
1488 norm2image(icenter, jsteps, fcenter);
1489 pix pixel, diag;
1490 pixel.int16[0] = 1; //1/255 is almost black, and we increase toward 255/255 white as the flooding progresses
1491 set_image_pixel_color(texdata, jsteps[0], jsteps[1], pixel.bytes, icenter[0], icenter[1]);
1492 //ideally a queue is used for breadth-first flood-filling
1493 //2023 freewrl doesn't have a queue data structure
1494 //will use 2 vectors, and alternate: current round, next round
1495 //and use transparency to mark pixel 0=not done 1/255=queued 2/255=processed
1496 struct ixy { int x, y, steps; };
1497 struct Vector* current = newVector(struct ixy, 100);
1498 struct Vector* next = newVector(struct ixy, 100);
1499 struct Vector* tmp;
1500 struct ixy p, q, nebor[8];
1501 unsigned char done, steps, * funcp;
1502 float color[3];
1503 //neighboring pixel relative coordinates, we'll do 8 surrounding pixels.
1504 // 5 6 7
1505 // 3 4
1506 // 0 1 2
1507 for (int i = 0; i < 8; i++) {
1508 nebor[i].x = nebor[i].y = 0;
1509 nebor[i].steps = 2; //staying with ints, we'll set 2 for this, and 3 for diagonal
1510 }
1511 nebor[0].y = nebor[1].y = nebor[2].y = -1;
1512 nebor[0].x = nebor[3].x = nebor[5].x = -1;
1513 nebor[2].x = nebor[4].x = nebor[7].x = 1;
1514 nebor[5].y = nebor[6].y = nebor[7].y = 1;
1515 nebor[0].steps = nebor[2].steps = nebor[5].steps = nebor[7].steps = 3; //close to 2 * root(2)
1516 p.x = icenter[0];
1517 p.y = icenter[1];
1518 p.steps = 1;
1519 set_image_pixel_transparency(texdata, jsteps[0], jsteps[1], 1, p.x, p.y);
1520 stack_push(struct ixy, current, p);
1521 int more = TRUE;
1522 steps = 0;
1523
1524 while (more) {
1526 //pixel[0] = pixel[1] = pixel[2] = steps + 2;
1527 //diag[0] = diag[1] = diag[2] = steps + 3;
1528 //steps += 2;
1529 for (int i = 0; i < vectorSize(current); i++) {
1530 p = vector_get(struct ixy, current, i);
1531 done = get_image_pixel_transparency(texdata, jsteps[0], jsteps[1], p.x, p.y);
1532 steps = p.steps;
1533 if (done < 2) {
1534 //mark as done and set the steps distance to sink
1535 pixel.int16[0] = p.steps;
1536 pixel.bytes[2] = 0;
1537 set_image_pixel_transparency(texdata, jsteps[0], jsteps[1], 2, p.x, p.y);
1538 set_image_pixel_color(texdata, jsteps[0], jsteps[1], pixel.bytes, p.x, p.y);
1539 //queue any un-done neighbors for next loop
1540 for (int j = 0; j < 8; j++) {
1541 q.x = p.x + nebor[j].x;
1542 q.y = p.y + nebor[j].y;
1543 q.steps = p.steps + nebor[j].steps;
1544 //skip if outside image
1545 if (q.x < 0 || q.x >= jsteps[0] || q.y < 0 || q.y >= jsteps[1]) continue;
1546
1547 //skip if obstacle in functionMap
1548 float xx, yy;
1549 xx = (float)q.x / (float)jsteps[0]; // px->gridSize.c[0];
1550 yy = (float)q.y / (float)jsteps[1]; // px->gridSize.c[1];
1551 funcp = sample_image(tt, xx,yy);
1552 pixel2color3(color, funcp);
1553 int is_close = vecclose3f(color, px->obstacleColor.c, px->colorMatchTolerance);
1554 if (is_close) continue;
1555
1556 //skip if its already queued in next (or should we replace if this one is fewer gross steps?)
1557 done = get_image_pixel_transparency(texdata, jsteps[0], jsteps[1], q.x, q.y);
1558 if (done == 2) continue;
1559 if (done == 1) {
1560 //its in the queue. if current q.steps is less than the one already in next, replace steps
1561 for (int k = 0; k < vectorSize(next); k++) {
1562 struct ixy* qq = vector_get_ptr(struct ixy, next, k);
1563 if (qq->x == q.x && qq->y == q.y) {
1564 if (qq->steps > q.steps) {
1565 qq->steps = q.steps;
1566 }
1567 break;
1568 }
1569 }
1570 continue;
1571 }
1572
1573 //queue it and flag it as queued 1
1574 stack_push(struct ixy, next, q);
1575 set_image_pixel_transparency(texdata, jsteps[0], jsteps[1], 1, q.x, q.y);
1576
1577 }
1578 }
1579 } //more in current queue to flood fill
1580 //recycle current vector, and swap current and next vectors
1581 vector_clear(current);
1582 tmp = current;
1583 current = next;
1584 next = tmp;
1585 more = vectorSize(current);
1586 } //more to flood fill
1587 if (0) {
1588 //no longer works - now short ints.
1589 //print flood map to screen as characters, with A==0
1590 printf("flood map %d x steps %d y steps %d\n", i, jsteps[0], jsteps[1]);
1591 print_image_channel(texdata, 0, jsteps[0], jsteps[1]);
1592 }
1593 if (0) {
1594 //show image on screen and in C:/tmp .bmp of flood map
1595 for (int jj = 0; jj < jsteps[1]; jj++)
1596 for (int ii = 0; ii < jsteps[0]; ii++) {
1597 //texdata[(jj * jsteps[0] + ii) * 4 + 0] = 127;
1598 //texdata[(jj * jsteps[0] + ii) * 4 + 1] = 127;
1599 //texdata[(jj * jsteps[0] + ii) * 4 + 2] = 0; //clear blue
1600 texdata[(jj * jsteps[0] + ii) * 4 + 3] = 0xff;
1601 }
1602 display_imagedata4(texdata, jsteps[0], jsteps[1],i); //sinkmap i
1603 //display_imagedata4(tt->texdata, tt->x, tt->y); //function map
1604 }
1605
1606 } //if box is_set
1607
1608 } //for each box
1609 px->classified = TRUE;
1610 printf("..end classifying physics\n");
1611 } //if sinkcolors
1612
1613 } //if tt
1614
1615 } //functionmap
1616 } //classified
1617 else {
1618 //classified, use sink maps
1619 // coordinate systems:
1620 // a) scene units, the classification bboxes
1621 // b) functionMap image pixels, computed from tt->x, tt->y given 0-1 image fraction coords
1622 // c) sinkmap grid pixels isteps, ibboxes
1623 // we've been assuming the gridSize is in m and we have one grid cell per meter
1624 // and scene grid centered on 0,0
1625 // and we've been assuming the functionMap image covers the same area as the gridSize (but different resolution)
1626 int isteps[2], jsteps[2];
1627 isteps[0] = ((int)(px->gridSize.c[0] + .5f));
1628 isteps[1] = ((int)(px->gridSize.c[1] + .5f));
1629 jsteps[0] = isteps[0] * 2;
1630 jsteps[1] = isteps[1] * 2;
1631 int sinkmapsize = jsteps[0] * jsteps[1] * 4;
1632 unsigned char* sinkmaps = (unsigned char*)px->_sinkmaps; //have all sink maps + pop map packed in one malloc
1633
1634 //first sink map is the population map showing where particles are, for particle collision avoidance
1635 unsigned char* popmap = &sinkmaps[0];
1636 //mapemitter assigns a destination (sink) at random to particle
1637 unsigned char* sinkmap;
1638 pix * sinkcolor, * funccolor, sinkuchar;
1639 float color[3];
1640 struct ixy { int x, y; };
1641 struct ixy p, q, nebor[8];
1642 int debug = FALSE;
1643 // we use the imageTexture functionmap below for checking for pauseZone
1644 textureTableIndexStruct_s* tt = getTableTableFromTextureNode(px->functionMap);
1645
1646 //sinkmap first assigned here, now that we have the sink count
1647 if (pp->sink == -1) {
1648 pp->sink = (int)(uniformRand() * (float)px->sinkColor.n);
1649 printf("pp.sink = %d\n", pp->sink);
1650 if (pp->sink < 0 || pp->sink >= px->sinkColor.n) {
1651 printf("bad sink number %d, should be 0-%d\n", pp->sink, px->sinkColor.n - 1);
1652 }
1653 }
1654 sinkmap = &sinkmaps[sinkmapsize * (pp->sink + 1)];
1655 //pp.position is in scene/ground coords centered on 0,0
1656 // we need grid coords of same size, but shifted wrt 0,0
1657 // and rescaled to map coords
1658 float xy[2];
1659 //get map normalized coords (0-1)
1660 xy[0] = (pp->position[0] + px->gridSize.c[0] * .5f) / px->gridSize.c[0];
1661 xy[1] = (pp->position[1] + px->gridSize.c[1] * .5f) / px->gridSize.c[1];
1662 //get sinkmap/popmap coords (0-imagasize)
1663 p.x = (int)(xy[0] * jsteps[0] + .5);
1664 p.y = (int)(xy[1] * jsteps[1] + .5); // pp->position[1] + px->gridSize.c[1] * .5f + .5f) * 2;
1665 if(debug) printf("pp.position %f %f p %d %d\n", pp->position[0], pp->position[1], p.x, p.y);
1666 if (p.x < 0 || p.x >= jsteps[0] || p.y < 0 || p.y >= jsteps[1]) {
1667 //vecset3f(pp->position, 0.0f, 0.0f, 0.0f);
1668 //vecset3f(pp->velocity, 0.0f, 0.0f, 0.0f);
1669 printf("off the sink map\n");
1670 return;
1671 }
1672
1673 for (int i = 0; i < 8; i++) nebor[i].x = nebor[i].y = 0;
1674 nebor[0].y = nebor[1].y = nebor[2].y = -1;
1675 nebor[0].x = nebor[3].x = nebor[5].x = -1;
1676 nebor[2].x = nebor[4].x = nebor[7].x = 1;
1677 nebor[5].y = nebor[6].y = nebor[7].y = 1;
1678 // ^ 5 6 7
1679 // y 3 4
1680 // x> 0 1 2
1681
1682 //which way to go?
1683 //check if we are on the sink/destination, if so recycle.
1684 sinkcolor = (pix*)get_image_pixel_color(sinkmap, jsteps[0], jsteps[1], p.x, p.y);
1685 if (sinkcolor->int16[0] < 3) { //}&& sinkcolor->int16[0] > 0) {
1686 //end of life, recycle - clear from population map
1687 //or emitter may have launched onto obstacle by mistake (sinkcolor == 0) so just retire early
1688 set_image_pixel_channel(popmap, jsteps[0], jsteps[1],0, 0, p.x, p.y);
1689 pp->age = pp->lifespan+1.0f;
1690 }
1691 else {
1692 //check if we are on a wait area, will affect neighbor decision
1693 float xx, yy;
1694 funccolor = (pix*)sample_image(tt, xy[0], xy[1]);
1695 pixel2color3(color, funccolor->bytes);
1696 int on_wait = vecclose3f(color, px->pauseColor.c, px->colorMatchTolerance);
1697 //if (on_wait) printf("on_wait ");
1698
1699 //check neighbors and rank by shortest distance
1700 int nlist, ilist[8], dlist[8], iscore[8];
1701 int ishortest = -1;
1702 int dshortest = 1000000;
1703 if (debug) print_image_channel(popmap, 0, jsteps[0], jsteps[1]);
1704 /*
1705 static int iframes;
1706 iframes++;
1707 if (iframes == 10600) {
1708 set_image_pixel_channel(popmap, jsteps[0], jsteps[1], 1, 0, 1,1);
1709 //show image on screen and in C:/tmp .bmp of flood map
1710 for (int jj = 0; jj < jsteps[1]; jj++)
1711 for (int ii = 0; ii < jsteps[0]; ii++) {
1712 //texdata[(jj * jsteps[0] + ii) * 4 + 0] = 127;
1713 //texdata[(jj * jsteps[0] + ii) * 4 + 1] = 127;
1714 //texdata[(jj * jsteps[0] + ii) * 4 + 2] = 0; //clear blue
1715 popmap[(jj * jsteps[0] + ii) * 4 + 3] = 0xff;
1716 }
1717 display_imagedata4(popmap, jsteps[0], jsteps[1], 0); //sinkmap i
1718 //display_imagedata4(tt->texdata, tt->x, tt->y); //function map
1719 print_image_channel(popmap, 0, jsteps[0], jsteps[1]);
1720 }
1721 */
1722
1723 //clear our last known location from popmap so we dont block ourself
1724 set_image_pixel_channel(popmap, jsteps[0], jsteps[1], 0,0, pp->maplocation[0],pp->maplocation[1]);
1725
1726 for (int i = 0; i < 8; i++) {
1727 dlist[i] = 2000000;
1728 iscore[i] = 0;
1729 q.x = p.x + nebor[i].x;
1730 q.y = p.y + nebor[i].y;
1731 //skip if outside image
1732 if (q.x < 0 || q.x >= jsteps[0] || q.y < 0 || q.y >= jsteps[1]) continue;
1733 iscore[i] = 1;
1734
1735 pix *sinkval = (pix*)get_image_pixel_color(sinkmap, jsteps[0], jsteps[1], q.x, q.y);
1736 //skip if obstacle
1737 if (sinkval->int16[0] == 0) continue;
1738 iscore[i] = 2;
1739 //skip if we are already on waitzone/crosswalk
1740 iscore[i] = 3;
1741 //skip if someone already populating grid cell (avoid particle collision)
1742 unsigned char populated = get_image_pixel_channel(popmap, jsteps[0], jsteps[1], 0, q.x, q.y);
1743 //printf("nebor %d populated %d\n", i, populated);
1744 if (populated) continue;
1745
1746 iscore[i] = 4;
1747 dlist[i] = sinkval->int16[0]; // uchar;
1748 if (dlist[i] < dshortest) {
1749 ishortest = i;
1750 dshortest = dlist[i];
1751 iscore[i] = 5;
1752 }
1753 }
1754 if(0) if (debug || ishortest < 0) {
1755 for (int m = 0; m < 8; m++) printf("iscore[%d]=%d,", m, iscore[m]);
1756 printf("\n");
1757 }
1758 if (ishortest > -1) {
1759 //move toward ishortest neighbor
1760
1761 q.x = p.x + nebor[ishortest].x;
1762 q.y = p.y + nebor[ishortest].y;
1763 int is_wait = 0;
1764 if (!on_wait) {
1765 //if not on crosswalk yet, and next step is on crosswalk, wait if function says to
1766 xx = (float)q.x / (float)jsteps[0]; // px->gridSize.c[0];
1767 yy = (float)q.y / (float)jsteps[1]; // px->gridSize.c[1];
1768 funccolor = (pix*)sample_image(tt, xx, yy);
1769 pixel2color3(color, funccolor->bytes);
1770 if (px->pauseState) {
1771 is_wait = vecclose3f(color, px->pauseColor.c, px->colorMatchTolerance);
1772 }
1773 }
1774 if (is_wait) {
1775 set_image_pixel_channel(popmap, jsteps[0], jsteps[1], 255, 0, p.x, p.y);
1776 vecset3f(pp->velocity, 0.0f, 0.0f, 0.0f);
1777 pp->paused = TRUE; //for HANIM motion change
1778 }
1779 else {
1780 float pxy[3], qxy[3], diff[3], dir[3];
1781 pxy[0] = (float)p.x * .5f;
1782 pxy[1] = (float)p.y * .5f;
1783 pxy[2] = 0.0f;
1784 qxy[0] = (float)q.x * .5f;
1785 qxy[1] = (float)q.y * .5f;
1786 qxy[2] = 0.0f;
1787 vecdif3f(diff, qxy, pxy);
1788 vecnormalize3f(dir, diff);
1789
1790 vecscale3f(pp->velocity, dir, pp->speed);
1791 float flen = veclength3f(dir);
1792 if (flen > 0.0f) {
1793 vecscale3f(pp->direction, dir, 1.0f / flen);
1794 }
1795
1796 //mark new location in popmap so others dont hit us
1797 set_image_pixel_channel(popmap, jsteps[0], jsteps[1], 255, 0, q.x, q.y);
1798 pp->maplocation[0] = q.x;
1799 pp->maplocation[1] = q.y;
1800 if (debug) printf("shortest %d velocity %f %f particle %p\n", ishortest, pp->velocity[0], pp->velocity[1], pp);
1801 pp->paused = FALSE; //for HANIM motion change
1802 }
1803 }
1804 else {
1805 //wait / stand
1806 if(debug) printf("waiting particle %p\n", pp);
1807 set_image_pixel_channel(popmap, jsteps[0], jsteps[1], 255, 0, p.x, p.y);
1808 //if (ishortest == -2) {
1809 vecset3f(pp->velocity, 0.0f, 0.0f, 0.0f);
1810 pp->paused = TRUE; //for HANIM motion change
1811 //}
1812 }
1813 if(debug) getchar();
1814 } //end of life
1815 } //classified
1816 } //enabled
1817}
1818// END HUMANOID PARTICLE SECTION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
1819
1820
1821void updateColorRamp(struct X3D_ParticleSystem *node, particle *pp, GLint cramp){
1822 int j,k,ifloor, iceil, found;
1823 float rgbaf[4], rgbac[4], rgba[4], fraclife;
1824 found = FALSE;
1825 fraclife = pp->age / pp->lifespan;
1826 for(j=0;j<node->colorKey.n;j++){
1827 if(node->colorKey.p[j] <= fraclife && node->colorKey.p[j+1] > fraclife){
1828 ifloor = j;
1829 iceil = j+1;
1830 found = TRUE;
1831 break;
1832 }
1833 }
1834 if(found){
1835 float spread, fraction;
1836 struct SFColorRGBA * crgba = NULL;
1837 struct SFColor *crgb = NULL;
1838 struct X3D_Node* color_ramp;
1839 if (node->colorRamp) color_ramp = node->colorRamp;
1840 else if (node->color) color_ramp = node->color;
1841 switch(color_ramp->_nodeType){
1842 case NODE_ColorRGBA: crgba = ((struct X3D_ColorRGBA *)color_ramp)->color.p; break;
1843 case NODE_Color: crgb = ((struct X3D_Color *)color_ramp)->color.p; break;
1844 default:
1845 break;
1846 }
1847 spread = node->colorKey.p[iceil] - node->colorKey.p[ifloor];
1848 fraction = (fraclife - node->colorKey.p[ifloor]) / spread;
1849 if(crgba){
1850 memcpy(rgbaf,&crgba[ifloor],sizeof(struct SFColorRGBA));
1851 memcpy(rgbac,&crgba[iceil],sizeof(struct SFColorRGBA));
1852 }else if(crgb){
1853 memcpy(rgbaf,&crgb[ifloor],sizeof(struct SFColor));
1854 rgbaf[3] = 1.0f;
1855 memcpy(rgbac,&crgb[iceil],sizeof(struct SFColor));
1856 rgbac[3] = 1.0f;
1857 }
1858 for(k=0;k<4;k++){
1859 rgba[k] = (1.0f - fraction)*rgbaf[k] + fraction*rgbac[k];
1860 }
1861 glUniform4fv(cramp,1,rgba);
1862 }else{
1863 //re-use last color
1864 }
1865}
1866void updateTexCoordRamp(struct X3D_ParticleSystem *node, particle *pp, float *texcoord){
1867 int found, ifloor,j;
1868 float fraclife, fracKey;
1869
1870 ifloor = 0;
1871 fraclife = pp->age / pp->lifespan;
1872 fracKey = 1.0f / (float)(node->texCoordKey.n);
1873 //if(node->_geometryType != GEOM_LINE)
1874 fraclife -= fracKey; //for 3 keys, fracKey will be .333
1875 // v 0000 v 1111111 v 222 change points
1876 // 0 .5 1 key
1877 for(j=0;j<node->texCoordKey.n;j++){
1878 if( node->texCoordKey.p[j] > fraclife){
1879 ifloor = j;
1880 found = TRUE;
1881 break;
1882 }
1883 }
1884 if(found){
1885 switch(node->_geometryType){
1886 case GEOM_LINE:
1887 FW_GL_TEXCOORD_POINTER (2,GL_FLOAT,0,(float *)&texcoord[ifloor*2*2],0);
1888 break;
1889 case GEOM_QUAD:
1890 case GEOM_TRIANGLE:
1891 //we use triangles for both quad and triangle, 6 vertices per age
1892 FW_GL_TEXCOORD_POINTER (2,GL_FLOAT,0,(float *)&texcoord[ifloor*2*6],0);
1893 break;
1894 default:
1895 break;
1896 }
1897 }
1898}
1899
1900void reallyDrawOnce();
1901void clearDraw();
1902GLfloat linepts [6] = {-.5f,0.f,0.f, .5f,0.f,0.f};
1903int lineindices[2] = {0,1};
1904int getImageChannelCountFromTTI(struct X3D_Node *appearanceNode );
1905void update_effect_uniforms();
1906void check_compile(struct X3D_Node* node){
1907 COMPILE_IF_REQUIRED
1908}
1909void child_geom_particle_shadow(struct X3D_ParticleSystem* node) {
1910 //child_geomParticle_shadow
1911 PRINT_GL_ERROR_IF_ANY("child_shape depth start");
1912 s_shader_capabilities_t* scap;
1913 shaderflagsstruct shader_requirements;
1914 memset(&shader_requirements, 0, sizeof(shaderflagsstruct));
1915 shader_requirements.base = node->_shaderflags_base; //_shaderTableEntry;
1916 shader_requirements.effects = node->_shaderflags_effects;
1917 shader_requirements.usershaders = node->_shaderflags_usershaders;
1918
1919 shader_requirements.depth = TRUE;
1920 shader_requirements.base |= PARTICLE_SHADER;
1921
1922 scap = getMyShaders(shader_requirements);
1923 enableGlobalShader(scap);
1924 sendMatriciesToShader(scap); //send matrices
1925 switch (node->_geometryType) {
1926 case GEOM_LINE:
1927 {
1928 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (float*)linepts);
1929 sendElementsToGPU(GL_LINES, 2, (int*)lineindices);
1930 }
1931 break;
1932 case GEOM_POINT:
1933 {
1934 float point[3];
1935 memset(point, 0, 3 * sizeof(float));
1936 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (GLfloat*)point);
1937 sendArraysToGPU(GL_POINTS, 0, 1);
1938 }
1939 break;
1940 case GEOM_QUAD:
1941 {
1942 //textureCoord_send(&mtf);
1943 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (GLfloat*)node->_tris);
1944 FW_GL_NORMAL_POINTER(GL_FLOAT, 0, twotrisnorms);
1945 sendArraysToGPU(GL_TRIANGLES, 0, 6);
1946 }
1947 break;
1948 case GEOM_SPRITE:
1949 {
1950 //textureCoord_send(&mtf);
1951 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (GLfloat*)node->_tris);
1952 FW_GL_NORMAL_POINTER(GL_FLOAT, 0, twotrisnorms);
1953 sendArraysToGPU(GL_TRIANGLES, 0, 6);
1954 }
1955 break;
1956 case GEOM_TRIANGLE:
1957 {
1958 //textureCoord_send(&mtf);
1959 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (GLfloat*)node->_tris);
1960 FW_GL_NORMAL_POINTER(GL_FLOAT, 0, twotrisnorms);
1961 sendArraysToGPU(GL_TRIANGLES, 0, 6);
1962 }
1963 break;
1964 case GEOM_GEOMETRY:
1965 render_node(node->geometry);
1966 break;
1967 default:
1968 break;
1969 }
1970 GLint ppos, pdir, cr, gtype, itrans;
1971 int i;
1972 ppos = GET_UNIFORM(scap->myShaderProgram, "particlePosition");
1973 pdir = GET_UNIFORM(scap->myShaderProgram, "particleDirection");
1974 itrans = GET_UNIFORM(scap->myShaderProgram, "particleTransform");
1975 cr = GET_UNIFORM(scap->myShaderProgram, "fw_UnlitColor");
1976 gtype = GET_UNIFORM(scap->myShaderProgram, "fw_ParticleGeomType");
1977 glUniform1i(gtype, node->_geometryType); //for SPRITE = 4, screen alignment
1978 //loop over live particles, drawing each one
1979 //float estart6[6], eout6[6];
1980 //extent6f_copy(estart6, peek_group_extent());
1981 Stack* _particles = node->_particles;
1982 //apply static orientation and size to all particles
1983 {
1984 double matrix[16], * mat[4];
1985 float fmat[16];
1986 for (i = 0; i < 4; i++)
1987 mat[i] = &matrix[i * 4];
1988 matidentity4d(matrix);
1989 if (1) {
1990 double* rot[4], * rot2[4], matyaw[16], matpitch[16], matrot[16];
1991 //apply particle size
1992 for (i = 0; i < 2; i++)
1993 mat[i][i] = node->particleSize.c[i];
1994 //apply particle orientation (relative to direction 1 0 0)
1995 for (i = 0; i < 3; i++)
1996 axisangle_rotate3d(mat[i], mat[i], node->particleOrientation.c);
1997 }
1998 double2float(fmat, matrix, 16);
1999 glUniformMatrix4fv(itrans, 1, TRUE, fmat);
2000 }
2001
2002 for (int i = 0; i < vectorSize(_particles); i++) {
2003 particle pp = vector_get(particle, _particles, i);
2004 //update particle-specific uniforms
2005 glUniform3fv(ppos, 1, pp.position);
2006 //printf("(%f %f %f)", pp.direction[0], pp.direction[1], pp.direction[2]);
2007 glUniform3fv(pdir, 1, pp.direction);
2008 //draw
2009 reallyDrawOnce();
2010 //extent6f_translate3f(eout6, estart6, pp.position);
2011 //union_group_extent(eout6);
2012 }
2013 clearDraw();
2014 //cleanup after draw, like child_shape
2015 FW_GL_BINDBUFFER(GL_ARRAY_BUFFER, 0);
2016 FW_GL_BINDBUFFER(GL_ELEMENT_ARRAY_BUFFER, 0);
2017 finishedWithGlobalShader();
2018}
2019void render_geom_particle(struct X3D_ParticleSystem* node, Stack* _particles) {
2020 ttglobal tg = gglobal();
2021
2022 int i;
2023 GLint ppos, pdir, cr, gtype;
2024
2025 //render_geom_particle(node);
2026
2027 //prepare to draw, like child_shape
2028 //render appearance
2029 //BORROWED FROM CHILD SHAPE >>>>>>>>>
2030 //declare geom particle variables
2031 int allowsTexcoordRamp = FALSE;
2032 float* texcoord = NULL;
2033 int haveColorRamp, haveTexcoordRamp;
2034 //int colorSource, alphaSource, isLit,
2035 int isUserShader;
2036 s_shader_capabilities_t* scap;
2037 shaderflagsstruct shader_requirements;
2038 struct X3D_Node* tmpNG;
2039
2040 //unsigned int shader_requirements;
2041 memset(&shader_requirements, 0, sizeof(shaderflagsstruct));
2042
2043 //prep_Appearance
2044 RENDER_MATERIAL_SUBNODES(node->appearance); //child_Appearance
2045
2046 // enable the shader for this shape
2047 //ConsoleMessage("turning shader on %x",node->_shaderTableEntry);
2048
2049 POSSIBLE_PROTO_EXPANSION(struct X3D_Node*, node->geometry, tmpNG);
2050
2051 shader_requirements.base = node->_shaderflags_base; //_shaderTableEntry;
2052 shader_requirements.effects = node->_shaderflags_effects;
2053 shader_requirements.usershaders = node->_shaderflags_usershaders;
2054 isUserShader = shader_requirements.usershaders ? TRUE : FALSE; // >= USER_DEFINED_SHADER_START ? TRUE : FALSE;
2055 //if(!p->userShaderNode || !(shader_requirements >= USER_DEFINED_SHADER_START)){
2056 if (!isUserShader) {
2057 //for Luminance and Luminance-Alpha images, we have to tinker a bit in the Vertex shader
2058 // New concept of operations Aug 26, 2016
2059 // in the specs there are some things that can replace other things (but not the reverse)
2060 // Texture can repace CPV, diffuse and 111
2061 // CPV can replace diffuse and 111
2062 // diffuse can replace 111
2063 // Texture > CPV > Diffuse > (1,1,1)
2064 // so there's a kind of order / sequence to it.
2065 // There can be a flag at each step saying if you want to replace the prior value (otherwise modulate)
2066 // Diffuse replacing or modulating (111) is the same thing, no flag needed
2067 // Therefore we need at most 2 flags for color:
2068 // TEXTURE_REPLACE_PRIOR and CPV_REPLACE_PRIOR.
2069 // and other flag for alpha: ALPHA_REPLACE_PRIOR (same as ! WANT_TEXALPHA)
2070 // if all those are false, then its full modulation.
2071 // our WANT_LUMINANCE is really == ! TEXTURE_REPLACE_PRIOR
2072 // we are missing a CPV_REPLACE_PRIOR, or more precisely this is a default burned into the shader
2073
2074 int channels, modulation, scenefile_specversion;
2075 //modulation:
2076 //- for Castle-style full-modulation of texture x CPV x mat.diffuse
2077 // and texalpha x (1-mat.trans), set 2
2078 //- for specs table 17-2 RGB Tex replaces CPV with modulation
2079 // of table 17-2 entries with mat.diffuse and (1-mat.trans) set 1
2080 //- for specs table 17-3 as written and ignoring modulation sentences
2081 // so CPV replaces diffuse, texture replaces CPV and diffuse- set 0
2082 // testing: KelpForest SharkLefty.x3d has CPV, ImageTexture RGB, and mat.diffuse
2083 // 29C.wrl has mat.transparency=1 and LumAlpha image, modulate=0 shows sphere, 1,2 inivisble
2084 // 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
2085 channels = getImageChannelCountFromTTI(node->appearance);
2086 // specversion <= 330 use v3.3 table 17-3
2087 // specversion >= 400 modulate everything
2088 scenefile_specversion = X3D_PROTO(node->_executionContext)->__specversion;
2089 // p->modulation; 0)scenefile specversion 1)v3.3- 2) v4.0+ (dug9 Mar 28, 2020)
2090 switch (fwl_get_modulation()) {
2091 case 0:
2092 //allows mixing modulations depending on which inline/proto/scenefile the shape was defined in
2093 modulation = scenefile_specversion >= 400 ? TRUE : FALSE;
2094 break;
2095 case 1:
2096 modulation = FALSE; break;
2097 case 2:
2098 modulation = TRUE; break;
2099 default:
2100 modulation = FALSE;
2101 }
2102 if (modulation == TRUE) {
2103 shader_requirements.base |= MODULATE_TEXTURE; //web3d most browsers default: texture replaces prior by default
2104 }
2105 if (!channels || (channels == 1 || channels == 3))
2106 shader_requirements.base |= MODULATE_ALPHA; //A = (1-TM)
2107 if (channels && (channels == 1 || channels == 2))
2108 shader_requirements.base |= MODULATE_COLOR; //ODrgb = IT x ICrgb
2109
2110
2111 //getShaderFlags() are from non-leaf-node shader influencers:
2112 // fog, local_lights, clipplane, Effect/EffectPart (for CastlePlugs) ...
2113 // - as such they may be different for the same shape node DEF/USEd in different branches of the scenegraph
2114 // - so they are ORd here before selecting a shader permutation
2115 shader_requirements.base |= getShaderFlags().base;
2116 shader_requirements.effects |= getShaderFlags().effects;
2117 //if(shader_requirements & FOG_APPEARANCE_SHADER)
2118 // printf("fog in child_shape\n");
2119
2120 //ParticleSystem flag
2121 shader_requirements.base |= PARTICLE_SHADER;
2122 if (node->colorRamp || node->color)
2123 shader_requirements.base |= HAVE_UNLIT_COLOR;
2124 }
2125 //printf("child_shape shader_requirements base %d effects %d user %d\n",shader_requirements.base,shader_requirements.effects,shader_requirements.usershaders);
2126 scap = getMyShaders(shader_requirements);
2127 enableGlobalShader(scap);
2128 //enableGlobalShader (getMyShader(shader_requirements)); //node->_shaderTableEntry));
2129
2130 //see if we have to set up a TextureCoordinateGenerator type here
2131 if (tmpNG && tmpNG->_intern && tmpNG->_intern->itype == 2) {
2132 struct X3D_PolyRep* tmppr = (struct X3D_PolyRep*)tmpNG->_intern;
2133 if (tmppr->tcoordtype == NODE_TextureCoordinateGenerator) {
2134 getAppearanceProperties()->texCoordGeneratorType = tmppr->texgentype;
2135 //ConsoleMessage("shape, matprop val %d, geom val %d",getAppearanceProperties()->texCoordGeneratorType, node->geometry->_intern->texgentype);
2136 }
2137 }
2138 //userDefined = (whichOne >= USER_DEFINED_SHADER_START) ? TRUE : FALSE;
2139 //if (p->userShaderNode != NULL && shader_requirements >= USER_DEFINED_SHADER_START) {
2140#ifdef ALLOW_USERSHADERS
2141 if (isUserShader && p->userShaderNode) {
2142 //we come in here right after a COMPILE pass in APPEARANCE which renders the shader, which sets p->userShaderNode
2143 //if nothing changed with appearance -no compile pass- we don't come in here again
2144 //ConsoleMessage ("have a shader of type %s",stringNodeType(p->userShaderNode->_nodeType));
2145 switch (p->userShaderNode->_nodeType) {
2146 case NODE_ComposedShader:
2147 if (X3D_COMPOSEDSHADER(p->userShaderNode)->isValid) {
2148 if (!X3D_COMPOSEDSHADER(p->userShaderNode)->_initialized) {
2149 sendInitialFieldsToShader(p->userShaderNode);
2150 }
2151 }
2152 break;
2153 case NODE_ProgramShader:
2154 if (X3D_PROGRAMSHADER(p->userShaderNode)->isValid) {
2155 if (!X3D_PROGRAMSHADER(p->userShaderNode)->_initialized) {
2156 sendInitialFieldsToShader(p->userShaderNode);
2157 }
2158 }
2159
2160 break;
2161 case NODE_PackagedShader:
2162 if (X3D_PACKAGEDSHADER(p->userShaderNode)->isValid) {
2163 if (!X3D_PACKAGEDSHADER(p->userShaderNode)->_initialized) {
2164 sendInitialFieldsToShader(p->userShaderNode);
2165 }
2166 }
2167
2168 break;
2169 }
2170 }
2171#endif //ALLOW_USERSHADERS
2172 //update effect field uniforms
2173 if (shader_requirements.effects) {
2174 update_effect_uniforms();
2175 }
2176
2177 //<<<<< BORROWED FROM CHILD SHAPE
2178
2179
2180 //send materials, textures, matrices to shader
2181 clear_textureUnit_used(); //appearance.texture material.textureXXX, PTMs.texture all need TEXTURE0+ XXX, where xxx starts from 0
2182 clear_material_samplers(); //PTM and material.textureXXX share frag shader sampler2D textureUnit[16] array
2183 clear_materialparameters_per_draw_counts(); //especially diffuse texture counts which both appearance and material share
2184
2185 textureTransform_start();
2186 // maybe too much? resend_textureprojector_matrix();
2187 setupShaderB();
2188 //send vertex buffer to shader
2189 allowsTexcoordRamp = FALSE;
2190 texcoord = NULL;
2191 switch (node->_geometryType) {
2192 case GEOM_LINE:
2193 {
2194 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (float*)linepts);
2195 sendElementsToGPU(GL_LINES, 2, (int*)lineindices);
2196 texcoord = (float*)node->_ltex;
2197 allowsTexcoordRamp = TRUE;
2198 }
2199 break;
2200 case GEOM_POINT:
2201 {
2202 float point[3];
2203 memset(point, 0, 3 * sizeof(float));
2204 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (GLfloat*)point);
2205 sendArraysToGPU(GL_POINTS, 0, 1);
2206 }
2207 break;
2208 case GEOM_QUAD:
2209 {
2210 //textureCoord_send(&mtf);
2211 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (GLfloat*)node->_tris);
2212 FW_GL_NORMAL_POINTER(GL_FLOAT, 0, twotrisnorms);
2213 sendArraysToGPU(GL_TRIANGLES, 0, 6);
2214 texcoord = (float*)node->_ttex;
2215 allowsTexcoordRamp = TRUE;
2216 }
2217 break;
2218 case GEOM_SPRITE:
2219 {
2220 //textureCoord_send(&mtf);
2221 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (GLfloat*)node->_tris);
2222 FW_GL_NORMAL_POINTER(GL_FLOAT, 0, twotrisnorms);
2223 sendArraysToGPU(GL_TRIANGLES, 0, 6);
2224 }
2225 break;
2226 case GEOM_TRIANGLE:
2227 {
2228 //textureCoord_send(&mtf);
2229 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (GLfloat*)node->_tris);
2230 FW_GL_NORMAL_POINTER(GL_FLOAT, 0, twotrisnorms);
2231 sendArraysToGPU(GL_TRIANGLES, 0, 6);
2232 texcoord = (float*)node->_ttex;
2233 allowsTexcoordRamp = TRUE;
2234 }
2235 break;
2236 case GEOM_GEOMETRY:
2237 render_node(node->geometry);
2238 break;
2239 default:
2240 break;
2241 }
2242
2243 ppos = GET_UNIFORM(scap->myShaderProgram, "particlePosition");
2244 pdir = GET_UNIFORM(scap->myShaderProgram, "particleDirection");
2245 cr = GET_UNIFORM(scap->myShaderProgram, "fw_UnlitColor");
2246 gtype = GET_UNIFORM(scap->myShaderProgram, "fw_ParticleGeomType");
2247 int itrans = GET_UNIFORM(scap->myShaderProgram, "particleTransform");
2248 glUniform1i(gtype, node->_geometryType); //for SPRITE = 4, screen alignment
2249 //loop over live particles, drawing each one
2250 haveColorRamp = node->colorRamp || node->color ? TRUE : FALSE;
2251 haveColorRamp = haveColorRamp && cr > -1;
2252 haveTexcoordRamp = node->texCoordRamp || node->texCoord ? TRUE : FALSE;
2253 haveTexcoordRamp = haveTexcoordRamp && allowsTexcoordRamp && texcoord;
2254 if (haveTexcoordRamp) {
2255 //glUniform1i(scap->nTexMatrix,0);
2256 glUniform1i(scap->nTexCoordChannels, 1);
2257 glUniform1i(scap->flipuv, 0);
2258 //glUniform1i(scap->textureCount,1);
2259 }
2260 float estart6[6], eout6[6];
2261 //extent6f_copy(estart6, peek_group_extent());
2262 extent6f_clear(estart6);
2263 //apply static orientation and size to all particles
2264 {
2265 double matrix[16], * mat[4];
2266 float fmat[16];
2267 for (i = 0; i < 4; i++)
2268 mat[i] = &matrix[i * 4];
2269 matidentity4d(matrix);
2270 if (1) {
2271 double* rot[4], * rot2[4], matyaw[16], matpitch[16], matrot[16];
2272 //apply particle size
2273 for (i = 0; i < 2; i++)
2274 mat[i][i] = node->particleSize.c[i];
2275 //apply particle orientation (relative to direction 1 0 0)
2276 for (i = 0; i < 3; i++)
2277 axisangle_rotate3d(mat[i], mat[i], node->particleOrientation.c);
2278 }
2279 double2float(fmat, matrix, 16);
2280 glUniformMatrix4fv(itrans, 1, TRUE, fmat);
2281 }
2282
2283 for (i = 0; i < vectorSize(_particles); i++) {
2284 particle pp = vector_get(particle, _particles, i);
2285 //update particle-specific uniforms
2286 glUniform3fv(ppos, 1, pp.position);
2287 glUniform3fv(pdir, 1, pp.direction);
2288
2289 //printf("(%f %f %f)", pp.direction[0], pp.direction[1], pp.direction[2]);
2290 if (haveColorRamp)
2291 updateColorRamp(node, &pp, cr);
2292 if (haveTexcoordRamp)
2293 updateTexCoordRamp(node, &pp, texcoord);
2294 if (node->_geometryType == GEOM_LINE) {
2295 float lpts[6], vel[3];
2296 vecnormalize3f(vel, pp.velocity);
2297 vecscale3f(&lpts[3], vel, .5f * node->particleSize.c[1]);
2298 vecscale3f(&lpts[0], vel, -.5f * node->particleSize.c[1]);
2299 FW_GL_VERTEX_POINTER(3, GL_FLOAT, 0, (float*)lpts);
2300 }
2301 //draw
2302 reallyDrawOnce();
2303 //extent6f_translate3f(eout6, estart6, pp.position);
2304 //union_group_extent(eout6);
2305 //printf("pp.pos %f %f %f\n", pp.position[0], pp.position[1], pp.position[2]);
2306 extent6f_union_vec3f(estart6, pp.position);
2307 }
2308 memcpy(node->_extent, estart6, 6 * sizeof(float));
2309 clearDraw();
2310 //cleanup after draw, like child_shape
2311 FW_GL_BINDBUFFER(GL_ARRAY_BUFFER, 0);
2312 FW_GL_BINDBUFFER(GL_ELEMENT_ARRAY_BUFFER, 0);
2313 textureTransform_end();
2314
2315 //BORROWED FROM CHILD_SHAPE >>>>>>
2316 //fin_Appearance
2317 if (node->appearance) {
2318 struct X3D_Appearance* tmpA;
2319 POSSIBLE_PROTO_EXPANSION(struct X3D_Appearance*, node->appearance, tmpA);
2320 if (tmpA->effects.n)
2321 fin_sibAffectors(X3D_NODE(tmpA), &tmpA->effects);
2322 }
2323 // any shader turned on? if so, turn it off
2324
2325 //ConsoleMessage("turning shader off");
2326 finishedWithGlobalShader();
2327#ifdef HAVE_P
2328 p->material_twoSided = NULL;
2329 p->material_oneSided = NULL;
2330 p->userShaderNode = NULL;
2331#endif
2332 tg->RenderFuncs.shapenode = NULL;
2333
2334 // load the identity matrix for textures. This is necessary, as some nodes have TextureTransforms
2335 // and some don't. So, if we have a TextureTransform, loadIdentity
2336
2337#ifdef HAVE_P
2338 if (p->this_textureTransform) {
2339 p->this_textureTransform = NULL;
2340#endif //HAVE_P
2341 FW_GL_MATRIX_MODE(GL_TEXTURE);
2342 FW_GL_LOAD_IDENTITY();
2343 FW_GL_MATRIX_MODE(GL_MODELVIEW);
2344#ifdef HAVE_P
2345 }
2346#endif
2347 // LineSet, PointSets, set the width back to the original.
2348 {
2349 float gl_linewidth = tg->Mainloop.gl_linewidth;
2350 glLineWidth(gl_linewidth);
2351#ifdef HAVE_P
2352 p->appearanceProperties.pointSize = gl_linewidth;
2353#endif
2354 }
2355
2356 // did the lack of an Appearance or Material node turn lighting off?
2357 LIGHTING_ON;
2358
2359 // turn off face culling
2360 DISABLE_CULL_FACE;
2361
2362 //<<<<< BORROWED FROM CHILD_SHAPE
2363
2364}
2365
2366void child_HAnimHumanoid(struct X3D_HAnimHumanoid *);
2367
2368void render_hanim_particle(struct X3D_ParticleSystem* node, Stack* _particles) {
2369 double mat[16], xyz[3];
2370 for (int i = 0; i < vectorSize(_particles); i++) {
2371 particle *pp = vector_get_ptr(particle, _particles, i);
2372 //update particle-specific uniforms
2373 //glUniform3fv(ppos, 1, pp.position);
2374 //glUniform3fv(pdir, 1, pp.direction);
2375 //convert ppos,pdir to matrix and push on transform stack
2376 //matrixIdentity4d(mat);
2377 //mattranslate4d(mat, float2double(xyz, pp.position, 3));
2378 if (1) {
2379 FW_GL_PUSH_MATRIX(); //POPPED in textureTransform_end
2380 //FW_GL_LOAD_IDENTITY();
2381 FW_GL_TRANSLATE_F(pp->position[0], pp->position[1], pp->position[2]);
2382 FW_GL_ROTATE_RADIANS(1.570796, 1, 0, 0);
2383 //euler2axixAngle
2384 float yaw = atan2(pp->direction[1], pp->direction[0]) + 1.570796;
2385 float xydist = sqrt(pp->direction[1] * pp->direction[1] + pp->direction[0] * pp->direction[0]);
2386 float tilt = atan(pp->direction[2] / xydist);
2387 FW_GL_ROTATE_RADIANS(tilt, 1, 0, 0);
2388 FW_GL_ROTATE_RADIANS(yaw, 0, 1, 0);
2389 }
2390 //assume first motion is walk, second is stand
2391 struct X3D_HAnimHumanoid* HH = (struct X3D_HAnimHumanoid*)node->geometry;
2392 if (HH->_nodeType == NODE_HAnimPermuter) {
2393 struct X3D_HAnimPermuter* HP = (struct X3D_HAnimPermuter*)HH;
2394 if (pp->permutationIndex == -1)
2395 pp->permutationIndex = uniformRand() * HP->permutations.n;
2396 HP->index = pp->permutationIndex;
2397 render_node(X3D_NODE(HP));
2398 HH = (struct X3D_HAnimHumanoid*)HP->humanoid;
2399 }
2400 struct X3D_HAnimMotion* HM[2];
2401 for (int j = 0; j < 2; j++) {
2402 HM[j] = (struct X3D_HAnimMotion*)HH->motions.p[j];
2403 HM[j]->transitionStart = pp->transitionStart[j];
2404 if(pp->_startTime[j] > 0.0)
2405 HM[j]->_startTime = pp->_startTime[j];
2406 if (HH->_lastMotionsEnabled.n == 0) {
2407 HH->_lastMotionsEnabled.p = malloc(2 * sizeof(int));
2408 HH->_lastMotionsEnabled.n = 2;
2409 }
2410 HH->_lastMotionsEnabled.p[j] = pp->lastMotionsEnabled[j];
2411 }
2412 if (pp->paused) {
2413 //ME->p[0] = FALSE;
2414 //ME->p[1] = TRUE;
2415 HH->motionsEnabled.p[0] = TRUE;
2416 HH->motionsEnabled.p[1] = FALSE;
2417 }
2418 else {
2419 //ME->p[0] = TRUE;
2420 //ME->p[1] = FALSE;
2421 HH->motionsEnabled.p[0] = FALSE;
2422 HH->motionsEnabled.p[1] = TRUE;
2423 }
2424 if (0) {
2425 struct X3D_HAnimMotion* HM0 = (struct X3D_HAnimMotion*)HH->motions.p[0];
2426 struct X3D_HAnimMotion* HM1 = (struct X3D_HAnimMotion*)HH->motions.p[1];
2427 printf("HH %p HM0 %p HM1 %p wt %f %f enabled %d %d\n", HH, HM0, HM1, HM0->transitionWeight, HM1->transitionWeight, HH->motionsEnabled.p[0], HH->motionsEnabled.p[1]);
2428 }
2429 child_HAnimHumanoid(HH);
2430 //save HM parameters for this particle
2431 for (int j = 0; j < 2; j++) {
2432 pp->transitionStart[j] = HM[j]->transitionStart;
2433 pp->lastMotionsEnabled[j] = HH->_lastMotionsEnabled.p[j] ;
2434 pp->_startTime[j] = HM[j]->_startTime;
2435 }
2436
2437 if(1)
2438 FW_GL_POP_MATRIX();
2439 //draw
2440 //reallyDrawOnce();
2441 //extent6f_union_vec3f(estart6, pp.position);
2442 }
2443
2444}
2445void child_ParticleSystem(struct X3D_ParticleSystem *node){
2446 //
2447 // ParticleSystem
2448 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/particle_systems.html#ParticleSystem
2449 // In here we are doing basically what child_Shape does to draw a single geom,
2450 // except once we send the geometry vertex buffer to the shader,
2451 // we go into a loop to draw all the particles, sending an xyz position update to the shader
2452 // for each particle (and color update if colorRamp, or texCoordUpdate if texCoordRamp, and
2453 // velocity direction if LINE)
2454 //
2455 //s_shader_capabilities_t *caps;
2456 // static int once = 0;
2457 ttglobal tg = gglobal();
2458
2459 COMPILE_IF_REQUIRED
2460 //check_compile(node);
2461
2462 /* initialization. This will get overwritten if there is a texture in an Appearance
2463 node in this shape (see child_Appearance) */
2464 tg->RenderFuncs.last_texture_type = NOTEXTURE;
2465 tg->RenderFuncs.shapenode = node;
2466
2467 if (renderstate()->render_depth) {
2468 if (node->castShadow) {
2469 child_geom_particle_shadow(node);
2470 PRINT_GL_ERROR_IF_ANY("child_shape depth end");
2471 }
2472 return;
2473 }
2474
2475
2476 prep_BBox((struct BBoxFields*)&node->bboxCenter);
2477
2478
2479 /* copy the material stuff in preparation for copying all to the shader */
2480 initialize_front_and_back_material_params();
2481
2482 if (renderstate()->render_blend == (node->_renderFlags & VF_Blend)) {
2483 if(node->enabled){
2484 if(TRUE){ //node->isActive){
2485 //declare particle variables
2486 int i,j,k,maxparticles;
2487 double ttime;
2488 float dtime;
2489 Stack* _particles;
2490
2491
2492 //initialize time-dependen node variables
2493 ttime = TickTime();
2494 dtime = (float)(ttime - node->_lasttime); //increment to particle age
2495
2496 //if(!once)
2497 // printf("child particlesystem \n");
2498
2499 //UPDATE PARTICLES
2500 //RETIRE remove deceased/retired particles (by packing vector)
2501 _particles = node->_particles;
2502 maxparticles = min(node->maxParticles,10000);
2503 for(i=0,j=0;i<vectorSize(_particles);i++){
2504 particle pp = vector_get(particle,_particles,i);
2505 pp.age += dtime;
2506 if(pp.age < pp.lifespan){
2507 vector_set(particle,_particles,j,pp); //pack vector to live position j
2508 j++;
2509 }
2510 }
2511
2512 //PREP PHYSICS - wind geta a per-frame gustiness update
2513 for(k=0;k<node->physics.n;k++){
2514 switch(node->physics.p[k]->_nodeType){
2515 case NODE_WindPhysicsModel:
2516 prep_windphysics(node->physics.p[k]); break;
2517 default:
2518 break;
2519 }
2520 }
2521 //APPLY PHYSICS
2522 for(i=0;i<vectorSize(_particles);i++){
2523 particle pp = vector_get(particle,_particles,i);
2524 float positionChange[3];
2525
2526
2527 //update velocity vector based on physics accelerations
2528 // A = F/M
2529 // V2 = V1 + A*dT
2530 for(k=0;k<node->physics.n;k++){
2531 switch(node->physics.p[k]->_nodeType){
2532 case NODE_WindPhysicsModel:
2533 apply_windphysics(&pp,node->physics.p[k],dtime); break;
2534 case NODE_ForcePhysicsModel:
2535 apply_forcephysics(&pp,node->physics.p[k],dtime); break;
2536 case NODE_MapPhysicsModel:
2537 apply_mapphysics(&pp, node->physics.p[k], dtime); break;
2538 case NODE_ResistancePhysicsModel:
2539 apply_resistancephysics(&pp, node->physics.p[k], dtime); break;
2540 default:
2541 break;
2542 }
2543 }
2544
2545 //update position: P1 = P0 + .5(V0 + V1) x dT
2546 vecscale3f(positionChange,pp.velocity,.5f * dtime);
2547
2548 for(k=0;k<node->physics.n;k++){
2549 switch(node->physics.p[k]->_nodeType){
2550 case NODE_BoundedPhysicsModel:
2551 apply_boundedphysics(&pp,node->physics.p[k],positionChange); break;
2552 default:
2553 break;
2554 }
2555 }
2556 vecadd3f(pp.position,pp.position,positionChange);
2557
2558 vector_set(particle,_particles,i,pp); //pack vector to live position j
2559 }
2560
2561 //CREATE via emitters (implied dtime = 0, so no physics on first frame)
2562 _particles->n = j;
2563 if(node->createParticles && _particles->n < maxparticles && node->emitter && emitter_loaded(node->emitter)){
2564 //create new particles to reach maxparticles limit
2565 int n_per_frame, n_needed, n_this_frame;
2566 float particles_per_second, particles_per_frame;
2567 n_needed = maxparticles - _particles->n;
2568 //for explosion emitter, we want them all created on the first pass
2569 //for all the rest we want maxparticles spread over a lifetime, so by the
2570 //time some start dying, well be at maxparticles
2571 //particles_per_second [p/s] = maxparticles[p] / particleLifetime[s]
2572 particles_per_second = (float)node->maxParticles / (float) node->particleLifetime;
2573 //particles_per_frame [p/f] = particles_per_second [p/s] / frames_per_second [f/s]
2574 particles_per_frame = particles_per_second * dtime;
2575 particles_per_frame += node->_remainder;
2576 n_per_frame = (int)particles_per_frame;
2577 node->_remainder = particles_per_frame - (float)n_per_frame;
2578 n_this_frame = min(n_per_frame,n_needed);
2579 if(node->emitter->_nodeType == NODE_ExplosionEmitter)
2580 n_this_frame = n_needed;
2581 j = _particles->n;
2582 for(i=0;i<n_this_frame;i++,j++){
2583 particle pp;
2584 memset(&pp,0,sizeof(particle));
2585 vecset3f(pp.origin, 0.0f, 0.0f, 0.0f);//for bounded physics
2586 pp.age = 0.0f;
2587 pp.lifespan = node->particleLifetime * (1.0f + uniformRandCentered()*node->lifetimeVariation);
2588 veccopy2f(pp.size,node->particleSize.c);
2589 //emit particles
2590 switch(node->emitter->_nodeType){
2591 case NODE_ConeEmitter: apply_ConeEmitter(&pp,node->emitter); break;
2592 case NODE_ExplosionEmitter: apply_ExplosionEmitter(&pp,node->emitter);
2593 node->createParticles = FALSE;
2594 break;
2595 case NODE_PointEmitter: apply_PointEmitter(&pp,node->emitter); break;
2596 case NODE_PolylineEmitter: apply_PolylineEmitter(&pp,node->emitter); break;
2597 case NODE_SurfaceEmitter: apply_SurfaceEmitter(&pp,node->emitter); break;
2598 case NODE_VolumeEmitter: apply_VolumeEmitter(&pp,node->emitter); break;
2599 case NODE_MapEmitter: apply_MapEmitter(&pp, node->emitter); break;
2600 default:
2601 break;
2602 }
2603 //save particle
2604 vector_set(particle,_particles,j,pp);
2605 }
2606 _particles->n = j;
2607 }
2608 if (node->_geometryType < GEOM_HANIM) {
2609 render_geom_particle(node, _particles);
2610 }
2611 if (node->_geometryType == GEOM_HANIM) {
2612 render_hanim_particle(node, _particles);
2613 }
2614
2615
2616 node->_lasttime = ttime;
2617 } //isActive
2618 } //enabled
2619 } //VF_Blend
2620 // once = 1;
2621 fin_BBox((struct X3D_Node*)node, (struct BBoxFields*)&node->bboxCenter, FALSE);
2622
2623}
2624