FreeWRL / FreeX3D 4.3.0
Component_HAnim.c
1/*
2
3
4X3D H-Anim 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
29
30#include <config.h>
31#include <system.h>
32#include <display.h>
33#include <internal.h>
34
35#include <libFreeWRL.h>
36
37#include "../vrml_parser/Structs.h"
38#include "../vrml_parser/CRoutes.h"
39#include "../main/headers.h"
40#include "../opengl/Material.h"
41#include "../opengl/OpenGL_Utils.h"
42#include "Children.h"
43#include "../scenegraph/RenderFuncs.h"
44#include "Polyrep.h"
45#include "Component_Shape.h"
46#include "../opengl/Frustum.h"
47#include "LinearAlgebra.h"
48#include "../ui/common.h"
49
50/* #include "OpenFW_GL_Utils.h" */
51
52/*
53HAnim examples:
54http://www.web3d.org/x3d/content/examples/Basic/HumanoidAnimation/
55 Octaga InstantReality
56- BoxMan.x3d good doesn't animate
57- AllenDutton.x3d skin stuck good
58- NancyStandShootRifleM24.x3d good anim good, skin bad
59- (KelpForest) NancyDiving.x3d good good
60HAnim Prototypes:
61http://www.web3d.org/x3d/content/examples/Basic/HumanoidAnimation/_pages/page11.html
62HAnim examples:
63http://www.web3d.org/x3d/content/examples/Basic/#HumanoidAnimation
64Specs:
65http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/hanim.html
66These specs don't articulate the field meanings, instead point to an ISO doc:
67http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=33912
68I19774 about $80
69Here's some free online docs:
70http://www.h-anim.org/
71http://h-anim.org/Specifications/H-Anim200x/ISO_IEC_FCD_19774/
72http://h-anim.org/Specifications/H-Anim200x/ISO_IEC_FCD_19774/ObjectInterfaces.html
73- Humanoid has transform, skeleton, skin, (flat lists of) skinCoord, skinNormal, segments,joints,sites,viewpoints
74- Joint has transform, children, displacers, skinCoord indx,wt
75- Segment has children, displacers, mass, coord
76- Site has transform, children
77- Displacer has displacements, coord, wt, coord index,
78All the fields are discussed.
79
80July 2016
81- where we left off years ago?
82- define HANIMHANIM below to compile - lots of errors
83Nov 2016
84- stack errors fixed by removing return; at top of some functions
85x but doesn't render/animate correctly: whole body frozen, various limbs moving independently/dismembered
86
87Related Links on HAnim
88http://www.web3d.org/working-groups/humanoid-animation-h-anim
89- The tutorial
90Links on character animation for real-time graphics:
91http://www.cescg.org/CESCG-2002/MPutzKHufnagl/paper.pdf
92- Character Animation for Real-time Applications
93http://apc.dcti.iscte.pt/praticas/Real-Time%20Character%20Animation%20for%20Computer%20Games.pdf
94- (Anderson) Real-Time Character Animation for Computer Games
95https://books.google.ca/books?id=O0sxtDeT5PEC&pg=PA125&lpg=PA125&dq=Animating+single+mesh+character+in+real+time&source=bl&ots=u8gETEoSM0&sig=AW7nQB25K4IxjZp_7xSlLJRxiCQ&hl=en&sa=X&ved=0ahUKEwiQ6NHdoarOAhVX2mMKHVrdDR8Q6AEIJjAB#v=onepage&q=Animating%20single%20mesh%20character%20in%20real%20time&f=false
96- book: Real-time Character Animation with Visual C++
97- shows additive approach to vertex blending:
98 vertex {
99 xyz original;
100 xyz world;
101 int number_of_limbs_influencing;
102 }
103 set number_of_limbs influencing vertex;
104 on each frame,
105 zero worlds
106 Iterate over limbs, transforming originals influenced and adding/summing onto worlds.
107 divide each world by number_of_limbs
108http://www.nvidia.com/object/doc_characters.html
109- Nvidia link page for various game programming algos with gpu acceleration
110http://ruh.li/AnimationVertexSkinning.html
111- Skinning shader
112http://http.developer.nvidia.com/GPUGems/gpugems_ch04.html
113- Bind pose, dawn
114http://www.3dgep.com/gpu-skinning-of-md5-models-in-opengl-and-cg/
115- Glsl impl of skinning
116http://tech-artists.org/wiki/Vertex_Skinning
117- ogre sample shader
118https://www.cs.tcd.ie/publications/tech-reports/reports.06/TCD-CS-2006-46.pdf
119- dual quat skinning
120
121
122Q if the skeleton isnt rendered, when would you traverse the skeleton
123And whats the output?
124Q isnt there some kind of ik solver that could/should be exposed to sai as a node?
125Otherwise you will need compiled/fast scrpt engine if its all in js, and
126scene authors will need to know all that stuff.
127https://en.wikipedia.org/wiki/Inverse_kinematics
128https://en.wikipedia.org/wiki/Moment_of_inertia
129dug9 july 2016: What's Weird about HAnim:
130a) looks like there's very little animation work we have to do in our browser
131 the scene author has to do it all in js / via SAI
132b) the skeleton isn't rendered (if no geom on sites/segments?)
133 so when do you traverse the joints and what's the output?
134
135dug9 aug 2016: would this work:
136Just render_HAnimHumanoid (as un-shared/opaque private scenegraph?)
137- traverse the joints and segments privately from HanimHumanoid to call their render_ functions
138- no HanimHumanoid? then don't render any joints or segments - don't list virtual functions for them
139
140Design Options:
1411. 2-step
142a) interpolate the pose of joints and segments relative to HanimHumanoid
143 i) start with Identity transform at HanimHumanoid
144 ii) traverse down segments and joints, and sites, pushing, multiplying and saving
145 the cumulative transform in each segment/joint/site
146b) for each segment/joint/site (done at the HanimHumanoid level)
147 i) push cumulative pose for segment/joint/site onto transform stack
148 ii) render any attached geometry via noralchildren
1492. Combined step
150a) traverse down segments pushing and multiplying pose
151b) when visiting a segment/joint/site render its children geometry
152 x this wont work with skin/'skinning', just attached solid geometry ie scuba tank
153 * so instead of the 'render' step for skin, there would be vertex-update step
1543. Best Guess
155a) traverse skeleton joints rendering attached solid geometry, and updating influenced skin vertices
156b) divide skin vertices by number of influencers
157c) render skin
158
159Requirements:
160- single deformable mesh should be 'easy' / possible / efficient to update / interpolate
161
162How to update single deformable mesh?
163Please research, as its a common thing in game programming.
164Guesses:
165- do we have/could we have a system for interpolating the compiled polyrep?
166- for example if during compiling the polyrep we kept an expanded index,
167 so a vertex index stored in a joint could find all the triangle vertices in the compiled / tesselated / triangulated mesh?
168 array[original index] [list of compiled polyrep vertex indexes]
169- or when compiling the HAnimHumanoid, would/could we break the polyrep into chunks associated with segments
170 based on 3D proximity
171- or would we transform the whole mesh for each segment, except weight the points differently,
172 so that the final mesh is a per-vertex-weighted sum of all segment meshes
173
174Nov 2016
175state Nov 3, 2016:
176NancyDiving.x3d (scene :HanimHumanoid(HH) with
177 HH->Skeleton children[ RootJoint, Joints, Segments] and
178 HH->joints, HH->segments
179 no HH->skin,->viewpoints
180x I don't see a single skin mesh/vertices being updated by weighted transforms
181x I don't see any mention of Displacer node type
182H: its a LOA 0 (or lowest level, with separate segments for each limb)
183
184freewrl
185x I don't see a single skin mesh vertices being updated by weighted transforms in code below
186x I don't see any mention of Displacer node type below, although its in perl/structs.h
187H:
188it was put together quickly using boilerplate scenegraph calls, for LOA 0,
189but without detailed custom code or testing to make it work for LOA 1+
190
191freewrl rendering of NancyDiving.x3d
192x skin frozen, while indvidual body segments are transformed separately / dismembered,
193x seem to be missing rotations on the segments
194FIXED for LOA-0 ie NancyDiving.x3d
195
196Nov 5, 2016
197Need to add .skin weighted vertex blending for LOA-1 ie BoxMan.x3d
1982 methods:
199CPU - transform coords on CPU side, try to do without re-compiling shape node as a result of change
200 a) stream_polyrep actualcoord update based on tesselation indexes, for glDrawArray
201 b) replace stream_polyrep with new stream_indexed so child_shape renders via glDrawElements
202 then less processing to update vertices which are kept 1:1 with original
203GPU - as with CPU a) or b), except:
204 jointTransforms JT[] are sent to gpu, with index and weight as vertex attributes
205 https://www.opengl.org/wiki/Skeletal_Animation
206
207How it would all work
208on child_humanoid rendering call:
209- clear a list of per-skinCoord-vertex indexs PSVI[] and weights PSVW[] size = skinCoord.n
210- clear a flat list of transforms, [] size ~= number of joints
211- clear a stack of humanoid transforms
212- render skeleton
213 for each Joint
214 aggregate humanoid transform like we normally do
215 GPU method:
216 parse xyz and quaternion from the matrix (is that possible/easy? do I have code?)
217 add {xyz,quaternion} joint transform to joint transform list JT[], get its new index JI
218 CPU method
219 add cumulative humanoid joint transform to joint transform list JT[], get its new index JI
220 iterate over joint's list of coordIndices, and for each index
221 add the jointTransformIndex JI to PSVI[index]
222 add the jointWeight to go with the index PSVW[index] = wt
223 for each sites or segment - render as normal scenegraph content
224- render skin
225 GPU method: set skeletal animation vertex blending shader flag SKELETAL on shaderflags stack
226 CPU method: copy and do weighted transform of coords here using PSVI, PSVW
227 for each shape in skin:
228 draw as normal shape, almost, except:
229 GPU method: send (untransformed) coordinates to shader as vertices as usual
230 CPU method: if SKELETAL && CPU substitute transformed coords, and send to shader
231 for drawElements, send indices
232 if SKELETAL && GPU, then, after compiling/fetching shader:
233 send list of jointtransforms to shader as array(s) of vec4
234 send indexes to JTs to shader as vertex attributes
235 send weights to shader as vertex attributes
236 in shader if SKELETAL && GPU
237 apply weighted transforms
238
239June 24, 2020 Note: glTF skinning shows vertex shader formula
240- you pass shader a skin mesh in standing pose
241- and (an array of) matrices and skin vertex weights
242- and vertex shader does the math
243https://www.khronos.org/gltf/
244https://www.khronos.org/files/gltf20-reference-guide.pdf
245- reference guide shows vertex shader code for weighted matrix blending for skinning
246- freewrl needs this
247x currenlty we are transforming mesh vertices in CPU on each frame - all CPU
248
249Dec 18, 2023
250- stream polyrep duplicates vertices to make simple streaming triangle set
251x that destroys indexability, so GPU skinning can't work on original indexes
252options:
2531) change from stream_polyrep to index preserving methods
2542) map stream polyrep vertex indexes back to original indexes
255Dec 25, 2023
256Decided to try #2 by making Cindex (already used for collision) into a VBO
257- always created for polyrep (indexed face set etc geom types)
258- and only bound/sent to GPU if the shader has a use for it
259- so vertex shader will get a vertex attribute int that points to original vertex index
260- then for skinning that index will [index] image buffers
261 or SSBO holding skin weights and joint matrix indices
262- optionally for accelerating displacers, it can index back to a sum-of-displacements buffer
263Dec 30, 2023
264- have SSBOs (joint weights,indexes, and joint matrices) working a bit for GPU skinning
265
266SUMMARY OF GPU SKINNING METHOD ADOPTION
267HANIM SPEEDUP VIA GPU SKINNING
268There are multiple ways to speed up skinning with GPU including GPU programs, and various ways to send data to Shader.including UBO uniform buffer object, SSBO shader storage buffer object, buffer-backed textures.
269For freeWRL --a bit tardy adding GPU skinning-- I chose to use SSBO shader storage buffer objects to send extra data to Vertex Shader, it happened to be the first thing I got working.
270Speedup for Gramps (a scene with humanoid skinCoord 224,000 vertices) rendering from 2 FPS with CPU, to 20-40 FPS with GPU skinning.
271
272
273more detail:
274My experience implementing GPU SKINNING in freeWRL
275CPU method (slow):
276a) once / early:
277-- saves original skinCoordinates
278-- records per-vertex joint indexes and weights
279b) on each frame
280-- copy from saved coordinates to skinCoord Coordinates
281-- traverse skeleton
282--- record Joint transform matrix and normal matrix, apply Joint Displacer to skinCoord
283-- apply joint matrix and normal transforms to skinCoord using joint indexes and weights
284-- recompile / re-stream mesh (duplicating normals and vertices) and resends vertices with attributes to shader
285GPU method (fast):
286a) once / early
287-- save original skinCoord index 'cindex' as vertex attribute in VBO vertex buffer object
288-- compile/stream mesh (duplicating normals and vertices) and sends vertices with attributes including cindex to shader
289-- traverse skeleton:
290--- record per-vertex skinCoord indexes ad weights and sends to GPU as SSBO shader storage buffer objects and send to GPU
291--- record per-vertex joint displacer packed displace array indexes 'dindex'
292---- a) in Displacer local dindex
293---- b) as per-humanoid dindex SSBO and send to GPU
294b) on each frame
295-- zero joint displace packed array
296-- traverses skeleton:
297--- recording Joint transform matrix and normal matrix, send to GPU as SSBO
298--- update joint displacer weights
299--- sum joint displacements onto packed displace array, send packed displace array to GPU
300c) in shader
301vertex = in_vertex;
302//apply displacer displacements or 0,0,0 if dindex is 0
303vertex.xyz += displace[dindex[cindex]].xyz;
304//apply joint matrix transforms to vertex or 0 if weight is 0
305newvertex += jointmatrix[jindex.x[cindex]]*weight.x[cindex]*vertex;
306newvertex += jointmatrix[jindex.y[cindex]]*weight.y[cindex]*vertex;
307newvertex += jointmatrix[jindex.z[cindex]]*weight.z[cindex]*vertex;
308newvertex += jointmatrix[jindex.w[cindex]]*weight.w[cindex]*vertex;
309vertex = newvertex;
310The vertex transforms should apply to mesh and lines, but web3d has no IndexedPointSet so they only way a skin can show points is with PointSet which shows all the skinCoord as points, a rare use-case In freeWRL I didn't implement Points in GPU skinning, and have a Launcher / commandline parameter for thunking / reverting to CPU skinning for those cases.
311
312PACKED JOINT DISPLACE ARRAY
313a) assumptions:
314-- segment displacers don't refer to humanoid skinCoord -- they refer to Segment-local Shape geometry, and are applied directly to those local Coordinates, and don't need to coordinate with GPU skinning, so no change for their method
315-- joint displacers are applied to humanoid skinCoord, so need to be applied in shader when using GPU skinning
316-- joint displacers are not good candidates for DEF/USE between multiple humanoids or LOD level of detail humanoids, because they list specific skinCoord indexes, and the weights when routed to would apply to all humanoids sharing, so would apply to marching army scene only - a rare use case that can be done other ways by DEF/USEing the whole humanoid. Therefore Displacers can hold humanoid-specific state variables.
317-- a small % of skin vertices are involved in joint displacers, so no need to send entire skinCoord coords on each frame
318-- the summing of weighted joint displacements isn't compute intensive and can be done on CPU side
319b) method
320- a lookup table is created and used to record joint displacer coordinate indexes, with each humanoid-unique joint displacer-index being entered once and given a row in a packed displace sum.xyzw array[], with the first 0th row reserved for 0,0,0,0., and length of array = number of unique vertex indexes referred to by all joint displacers in the humanoid
321- on an early pass / once, when traversing the skeleton, displacer indexes are checked against the lookup table, and entered in table if not already and given an int index called dindex into the packed displace sum array, and a int dindex[] array is created once for each joint displacer to twin the index[] field and hold the dindex into the packed array, and entered in a humanoid-dindex array to be sent once via SSBO to GPU
322- on each frame the packed displace array is zeroed, skeleton is traversed, and joint displacer weights are updated, and wieghted displacements summed onto packed displace array rows displace[dindex[cindex]].xyz += displacement[ci].xyz*weight;
323- once per frame the summed displace array is sent to GPU via SSBO
324- in vertex shader
325vertex.xyz += displace[dindex[cindex]].xyz
326
327https://freewrl.sourceforge.io/tests/26_Humanoid_Animation/BoxmanBVH_displacer_playlib.x3d
328- Boxman humanoid scene with Joint Displacer weight controlled with upper left slidebar (bvh motion controlled with lower right slidebar)
329https://freewrl.sourceforge.io/tests/26_Humanoid_Animation/BoxmanBVH_displacer_playlib.mp4
330- video showing GPU displacer applied during GPU skinning in freewrl version 6.5.0
331https://sourceforge.net/projects/freewrl/files/freewrl-win32/6.0/
332- 650.msi has the GPU skinning and GPU joint displacers
333https://sourceforge.net/p/freewrl/git/ci/develop/tree/freex3d/src/lib/scenegraph/Component_HAnim.c
334- CPU-side code for HAnim
335https://sourceforge.net/p/freewrl/git/ci/develop/tree/freex3d/src/lib/opengl/Compositing_Shaders.c
336- GPU-side shader code search SKINNING - about line 521 and line 926
337
338*/
339typedef struct {
340 float head[3];
341 float tail[3];
342} bone;
343
344/* last HAnimHumanoid skinCoord and skinNormals */
345typedef struct pComponent_HAnim{
346 double HHMatrix[16];
347 Stack *humanoid_stack;
348 Stack* humanoid_skinCoord_stack;
349 Stack* joint_center;
350 Stack* bones;
351}* ppComponent_HAnim;
352void *Component_HAnim_constructor(){
353 void *v = MALLOCV(sizeof(struct pComponent_HAnim));
354 memset(v,0,sizeof(struct pComponent_HAnim));
355 return v;
356}
357void Component_HAnim_init(struct tComponent_HAnim *t){
358 //public
359 //private
360 t->prv = Component_HAnim_constructor();
361 {
362 ppComponent_HAnim p = (ppComponent_HAnim)t->prv;
363 p->humanoid_stack = newStack(struct X3D_HAnimHumanoid*);
364 p->humanoid_skinCoord_stack = newStack(void*);
365 stack_push(void*, p->humanoid_skinCoord_stack, NULL);
366 p->joint_center = newStack(struct SFVec3f);
367 p->bones = newStack(bone);
368 }
369}
370void Component_HAnim_clear(struct tComponent_HAnim *t){
371 //public
372 //private
373 {
374 ppComponent_HAnim p = (ppComponent_HAnim)t->prv;
375 deleteStack(struct X3D_HAnimHumanoid*,p->humanoid_stack);
376 deleteStack(void*, p->humanoid_skinCoord_stack);
377 deleteStack(float*, p->joint_center);
378 deleteStack(bone, p->bones);
379 }
380}
381//ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
382
383void push_humanoid_skinCoord(void* coord) {
384 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
385 stack_push(void*, p->humanoid_skinCoord_stack, coord);
386}
387void* peek_humanoid_skinCoord() {
388 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
389 return stack_top(void*, p->humanoid_skinCoord_stack);
390}
391void pop_humanoid_skinCoord() {
392 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
393 stack_pop(void*, p->humanoid_skinCoord_stack);
394}
395
396// compile_HAnimHumanoid and render_ push and pop
397// so accessory nodes when rendered can refer to HH = peek_humanoid() without passing down call stack
398void push_humanoid(struct X3D_HAnimHumanoid *HH){
399 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
400 stack_push(struct X3D_HAnimHumanoid*,p->humanoid_stack,HH);
401}
402void pop_humanoid(){
403 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
404 stack_pop(struct X3D_HAnimHumanoid *,p->humanoid_stack);
405}
406struct X3D_HAnimHumanoid * peek_humanoid(){
407 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
408 return stack_top(struct X3D_HAnimHumanoid *, p->humanoid_stack);
409}
410void push_joint_center(float *center) {
411 //push already transformed to humanoid root coords
412 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
413 double modelview[16], rootmat[16], a[3], r[3];
414 struct SFVec3f rcenter;
415 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelview);
416 matmultiplyAFFINE(rootmat, modelview, p->HHMatrix);
417 float2double(a, center, 3);
418 transformAFFINEd(r, a, rootmat);
419 double2float(rcenter.c, r,3);
420 stack_push(struct SFVec3f, p->joint_center, rcenter);
421}
422void pop_joint_center() {
423 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
424 stack_pop(struct SFVec3f, p->joint_center);
425}
426float* peek_joint_center() {
427 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
428 struct SFVec3f* cc = stack_top_ptr(struct SFVec3f, p->joint_center);
429 return cc->c;
430}
431int joint_center_count() {
432 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
433 return p->joint_center->n;
434}
435void push_bone(float *head, float* tail) {
436 //assume head,tail already transformed into Humanoid root coordinates
437 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
438 bone b;
439 veccopy3f(b.head, head);
440 veccopy3f(b.tail, tail);
441 stack_push(bone, p->bones, b);
442}
443void clear_bones() {
444 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
445 clearStack(p->bones);
446}
447bone* peek_bone(int index) {
448 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
449 return vector_get_ptr(bone, p->bones,index);
450}
451int bone_count() {
452 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
453 return vectorSize(p->bones);
454}
455
456
457
458void update_jointMatrixFromMotion(struct X3D_Node* HM, char *jname, double *jmatrix);
459
460
461void compile_HAnimJoint (struct X3D_HAnimJoint *node){
462
463 INITIALIZE_EXTENT;
464
465 /* printf ("changed Transform for node %u\n",node); */
466 node->__do_center = verify_translate ((GLfloat *)node->center.c);
467 node->__do_trans = verify_translate ((GLfloat *)node->translation.c);
468 node->__do_scale = verify_scale ((GLfloat *)node->scale.c);
469 node->__do_rotation = verify_rotate ((GLfloat *)node->rotation.c);
470 node->__do_scaleO = verify_rotate ((GLfloat *)node->scaleOrientation.c);
471
472 node->__do_anything = (node->__do_center ||
473 node->__do_trans ||
474 node->__do_scale ||
475 node->__do_rotation ||
476 node->__do_scaleO);
477
478 //REINITIALIZE_SORTED_NODES_FIELD(node->children,node->_sortedChildren);
479 INITIALIZE_EXTENT
480 struct X3D_HAnimHumanoid* HH = peek_humanoid();
481 if (HH) {
482 struct X3D_HanimRep* hr;
483 hr = (struct X3D_HanimRep*)HH->_intern;
484 if (hr) {
485 hr->joint_changed = TRUE; // GPU method will re-write SSBO only if joint weights, indexes changed
486 }
487 }
488 MARK_NODE_COMPILED
489
490}
491void update_displacerWeightFromMotion(struct X3D_Node* HMnode, char* jname, float* weight);
492
493void prep_HAnimJoint (struct X3D_HAnimJoint *node) {
494
495
496 COMPILE_IF_REQUIRED
497
498 /* rendering the viewpoint means doing the inverse transformations in reverse order (while poping stack),
499 * so we do nothing here in that case -ncoder */
500
501 /* printf ("prep_Transform, render_hier vp %d geom %d light %d sens %d blend %d prox %d col %d\n",
502 render_vp,render_geom,render_light,render_sensitive,render_blend,render_proximity,render_collision); */
503
504 /* do we have any geometry visible, and are we doing anything with geometry? */
505 //OCCLUSIONTEST
506
507 if(!renderstate()->render_vp) {
508 push_transform_local_identity();
509
510 /* do we actually have any thing to rotate/translate/scale?? */
511 if (node->__do_anything) {
512
513 FW_GL_PUSH_MATRIX();
514 FW_GL_PUSH_MATRIX(); //this is to get us a separate 4x4 matrix just for the stuff here
515 FW_GL_LOAD_IDENTITY(); // .. wehich we will save for child_Transform to propagate its bbox up to its extent
516
517 /* TRANSLATION */
518 if (node->__do_trans)
519 FW_GL_TRANSLATE_F(node->translation.c[0],node->translation.c[1],node->translation.c[2]);
520
521 /* CENTER */
522 if (node->__do_center)
523 FW_GL_TRANSLATE_F(node->center.c[0],node->center.c[1],node->center.c[2]);
524 //any motion nodes enabled? if so apply current frame transform
525 if(1){
526 struct X3D_HAnimHumanoid *HH = peek_humanoid();
527 if(HH->motions.n){
528 double modelviewMatrix[16];
529 for (int i = 0; i < HH->motions.n; i++) {
530 //if(HH->motionsEnabled.p[i]){
531 struct X3D_HAnimMotion* HM = (struct X3D_HAnimMotion*)HH->motions.p[i];
532 if(HM->transitionWeight > 0.0){
533 //printmatrix(jointMatrix.mat);
534 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelviewMatrix);
535 //double dmat[16];
536 //memcpy(dmat, modelviewMatrix, 16 * sizeof(double));
537
538 update_jointMatrixFromMotion(X3D_NODE(HM),node->name->strptr,modelviewMatrix);
539
540 FW_GL_SETDOUBLEV(GL_MODELVIEW_MATRIX, modelviewMatrix);
541 //printmatrix(jointMatrix.mat);
542 }
543 for (int j = 0; j < node->displacers.n; j++) {
544 struct X3D_HAnimDisplacer* dp = (struct X3D_HAnimDisplacer*)node->displacers.p[j];
545 char* name = dp->name->strptr;
546 update_displacerWeightFromMotion(X3D_NODE(HM), name, &dp->weight);
547 }
548 }
549 }
550 }
551
552
553 /* ROTATION */
554 if (node->__do_rotation) {
555 FW_GL_ROTATE_RADIANS(node->rotation.c[3], node->rotation.c[0],node->rotation.c[1],node->rotation.c[2]);
556 }
557
558 /* SCALEORIENTATION */
559 if (node->__do_scaleO) {
560 FW_GL_ROTATE_RADIANS(node->scaleOrientation.c[3], node->scaleOrientation.c[0], node->scaleOrientation.c[1],node->scaleOrientation.c[2]);
561 }
562
563
564 /* SCALE */
565 if (node->__do_scale)
566 FW_GL_SCALE_F(node->scale.c[0],node->scale.c[1],node->scale.c[2]);
567
568 /* REVERSE SCALE ORIENTATION */
569 if (node->__do_scaleO)
570 FW_GL_ROTATE_RADIANS(-node->scaleOrientation.c[3], node->scaleOrientation.c[0], node->scaleOrientation.c[1],node->scaleOrientation.c[2]);
571
572 /* REVERSE CENTER */
573 if (node->__do_center)
574 FW_GL_TRANSLATE_F(-node->center.c[0],-node->center.c[1],-node->center.c[2]);
575
576 {
577 double mat[16];
578
579 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX,mat); //we got our local transform saved
580 FW_GL_POP_MATRIX();
581 FW_GL_TRANSFORM_D(mat); //now apply the above to prep for child_Tranform
582 reset_transform_local(mat);
583 }
584
585 }
586
587 //RECORD_DISTANCE
588
589 }
590
591}
592
593
594void fin_HAnimJoint (struct X3D_HAnimJoint *node) {
595
596 OCCLUSIONTEST
597
598 if(!renderstate()->render_vp) {
599 pop_transform_local();
600 if (node->__do_anything) {
601 FW_GL_POP_MATRIX();
602 }
603 } else {
604 /*Rendering the viewpoint only means finding it, and calculating the reverse WorldView matrix.*/
605 if((node->_renderFlags & VF_Viewpoint) == VF_Viewpoint) {
606 FW_GL_TRANSLATE_F(((node->center).c[0]),((node->center).c[1]),((node->center).c[2])
607 );
608 FW_GL_ROTATE_RADIANS(((node->scaleOrientation).c[3]),((node->scaleOrientation).c[0]),((node->scaleOrientation).c[1]),((node->scaleOrientation).c[2])
609 );
610 FW_GL_SCALE_F((float)1.0/(((node->scale).c[0])),(float)1.0/(((node->scale).c[1])),(float)1.0/(((node->scale).c[2]))
611 );
612 FW_GL_ROTATE_RADIANS(-(((node->scaleOrientation).c[3])),((node->scaleOrientation).c[0]),((node->scaleOrientation).c[1]),((node->scaleOrientation).c[2])
613 );
614 FW_GL_ROTATE_RADIANS(-(((node->rotation).c[3])),((node->rotation).c[0]),((node->rotation).c[1]),((node->rotation).c[2])
615 );
616 FW_GL_TRANSLATE_F(-(((node->center).c[0])),-(((node->center).c[1])),-(((node->center).c[2]))
617 );
618 FW_GL_TRANSLATE_F(-(((node->translation).c[0])),-(((node->translation).c[1])),-(((node->translation).c[2]))
619 );
620 }
621 }
622
623}
624
625void compile_HAnimSite (struct X3D_HAnimSite *node){
626
627 INITIALIZE_EXTENT;
628
629 /* printf ("changed Transform for node %u\n",node); */
630 node->__do_center = verify_translate ((GLfloat *)node->center.c);
631 node->__do_trans = verify_translate ((GLfloat *)node->translation.c);
632 node->__do_scale = verify_scale ((GLfloat *)node->scale.c);
633 node->__do_rotation = verify_rotate ((GLfloat *)node->rotation.c);
634 node->__do_scaleO = verify_rotate ((GLfloat *)node->scaleOrientation.c);
635
636 node->__do_anything = (node->__do_center ||
637 node->__do_trans ||
638 node->__do_scale ||
639 node->__do_rotation ||
640 node->__do_scaleO);
641
642 //REINITIALIZE_SORTED_NODES_FIELD(node->children,node->_sortedChildren);
643 INITIALIZE_EXTENT
644 MARK_NODE_COMPILED
645
646}
647void prep_HAnimSite (struct X3D_HAnimSite *node) {
648
649
650
651 COMPILE_IF_REQUIRED
652
653 /* rendering the viewpoint means doing the inverse transformations in reverse order (while poping stack),
654 * so we do nothing here in that case -ncoder */
655
656 /* printf ("prep_Transform, render_hier vp %d geom %d light %d sens %d blend %d prox %d col %d\n",
657 render_vp,render_geom,render_light,render_sensitive,render_blend,render_proximity,render_collision); */
658
659 /* do we have any geometry visible, and are we doing anything with geometry? */
660 //OCCLUSIONTEST
661
662 if(!renderstate()->render_vp) {
663 /* do we actually have any thing to rotate/translate/scale?? */
664 push_transform_local_identity();
665
666 if (node->__do_anything) {
667
668 FW_GL_PUSH_MATRIX();
669 FW_GL_PUSH_MATRIX(); //this is to get us a separate 4x4 matrix just for the stuff here
670 FW_GL_LOAD_IDENTITY(); // .. wehich we will save for child_Transform to propagate its bbox up to its extent
671
672 /* TRANSLATION */
673 if (node->__do_trans)
674 FW_GL_TRANSLATE_F(node->translation.c[0],node->translation.c[1],node->translation.c[2]);
675
676 /* CENTER */
677 if (node->__do_center)
678 FW_GL_TRANSLATE_F(node->center.c[0],node->center.c[1],node->center.c[2]);
679
680 /* ROTATION */
681 if (node->__do_rotation) {
682 FW_GL_ROTATE_RADIANS(node->rotation.c[3], node->rotation.c[0],node->rotation.c[1],node->rotation.c[2]);
683 }
684
685 /* SCALEORIENTATION */
686 if (node->__do_scaleO) {
687 FW_GL_ROTATE_RADIANS(node->scaleOrientation.c[3], node->scaleOrientation.c[0], node->scaleOrientation.c[1],node->scaleOrientation.c[2]);
688 }
689
690
691 /* SCALE */
692 if (node->__do_scale)
693 FW_GL_SCALE_F(node->scale.c[0],node->scale.c[1],node->scale.c[2]);
694
695 /* REVERSE SCALE ORIENTATION */
696 if (node->__do_scaleO)
697 FW_GL_ROTATE_RADIANS(-node->scaleOrientation.c[3], node->scaleOrientation.c[0], node->scaleOrientation.c[1],node->scaleOrientation.c[2]);
698
699 /* REVERSE CENTER */
700 if (node->__do_center)
701 FW_GL_TRANSLATE_F(-node->center.c[0],-node->center.c[1],-node->center.c[2]);
702 {
703 double mat[16];
704
705 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX,mat); //we got our local transform saved
706 FW_GL_POP_MATRIX();
707 FW_GL_TRANSFORM_D(mat); //now apply the above to prep for child_Tranform
708 reset_transform_local(mat);
709 }
710
711 }
712
713 //RECORD_DISTANCE
714
715 }
716
717}
718
719
720void fin_HAnimSite (struct X3D_HAnimSite *node) {
721
722 OCCLUSIONTEST
723
724 if(!renderstate()->render_vp) {
725 pop_transform_local();
726 if (node->__do_anything) {
727 FW_GL_POP_MATRIX();
728 }
729 } else {
730 /*Rendering the viewpoint only means finding it, and calculating the reverse WorldView matrix.*/
731 if((node->_renderFlags & VF_Viewpoint) == VF_Viewpoint) {
732 FW_GL_TRANSLATE_F(((node->center).c[0]),((node->center).c[1]),((node->center).c[2])
733 );
734 FW_GL_ROTATE_RADIANS(((node->scaleOrientation).c[3]),((node->scaleOrientation).c[0]),((node->scaleOrientation).c[1]),((node->scaleOrientation).c[2])
735 );
736 FW_GL_SCALE_F((float)1.0/(((node->scale).c[0])),(float)1.0/(((node->scale).c[1])),(float)1.0/(((node->scale).c[2]))
737 );
738 FW_GL_ROTATE_RADIANS(-(((node->scaleOrientation).c[3])),((node->scaleOrientation).c[0]),((node->scaleOrientation).c[1]),((node->scaleOrientation).c[2])
739 );
740 FW_GL_ROTATE_RADIANS(-(((node->rotation).c[3])),((node->rotation).c[0]),((node->rotation).c[1]),((node->rotation).c[2])
741 );
742 FW_GL_TRANSLATE_F(-(((node->center).c[0])),-(((node->center).c[1])),-(((node->center).c[2]))
743 );
744 FW_GL_TRANSLATE_F(-(((node->translation).c[0])),-(((node->translation).c[1])),-(((node->translation).c[2]))
745 );
746 }
747 }
748
749}
750
751typedef struct {
752 double mat [16];
753 float normat[9];
754} JMATRIX;
755enum {
756 VERTEXTRANSFORMMETHOD_CPU = 1,
757 VERTEXTRANSFORMMETHOD_GPU = 2,
758};
759static int vertex_transform_method = VERTEXTRANSFORMMETHOD_GPU;
760int vertexTransformMethod() {
761 return vertex_transform_method;
762}
763void fwl_set_skinning(char tf) {
764 if (tf == 'F' || tf == 'f')
765 vertex_transform_method = VERTEXTRANSFORMMETHOD_CPU;
766 else
767 vertex_transform_method = VERTEXTRANSFORMMETHOD_GPU;
768}
769char* lookup_brotoDefname(struct X3D_Proto* ec, struct X3D_Node* node);
770
771void render_HAnimHumanoid (struct X3D_HAnimHumanoid *node) {
772 /* save the skinCoords and skinNormals for use in following HAnimJoints */
773 //printf ("rendering HAnimHumanoid DEF %s type %s\n", lookup_brotoDefname(X3D_PROTO(node->_executionContext), X3D_NODE(node)), stringNodeType(node->_nodeType));
774
775}
776
777
778void line_draw(float* p, float* q, int depthtest, float linewidth);
779
780void render_rig_bones() {
781 //renders the whole skeletal rig as bones, after skin rendered
782 //coordinates are in hanim root local
783 //turn off depth testing
784 if (fwl_getDrawRig()) {
785 glDisable(GL_DEPTH_TEST);
786 //iterate over pre-transformed bone (head,tail) pairs drawing bone
787 for (int i = 0; i < bone_count(); i++) {
788 bone* b = peek_bone(i);
789 line_draw(b->head, b->tail, TRUE, 1.5);
790 }
791 //turn on depth testing
792 glEnable(GL_DEPTH_TEST);
793 clear_bones();
794 }
795}
796void save_rig_bone(struct X3D_HAnimJoint* joint, double* jointmat) {
797 if(fwl_getDrawRig())
798 if (joint_center_count()) {
799 double a[3], r[3];
800 float rcenter[3];
801 float2double(a, joint->center.c, 3);
802 transformAFFINEd(r, a, jointmat);
803 double2float(rcenter, r, 3);
804
805 push_bone(peek_joint_center(), rcenter);
806 }
807}
808void render_HAnimJoint (struct X3D_HAnimJoint * node) {
809 int i,j, jointTransformIndex;
810 double modelviewMatrix[16]; //, mvmInverse[16];
811 struct X3D_HAnimHumanoid *HH;
812 JMATRIX jointMatrix;
813 float* PVW;
814 int* PVI;
815 struct X3D_HanimRep* hr;
816
817 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
818 //printf ("rendering HAnimJoint DEF %s type %s\n", lookup_brotoDefname(X3D_PROTO(node->_executionContext), X3D_NODE(node)), stringNodeType(node->_nodeType));
819
820 HH = peek_humanoid();
821 if(HH){
822 hr = (struct X3D_HanimRep*)HH->_intern;
823 if (hr->make_joint_list) {
824 HH->joints.n++;
825 HH->joints.p = realloc(HH->joints.p, HH->joints.n * sizeof(void*));
826 HH->joints.p[HH->joints.n - 1] = X3D_NODE(node);
827 }
828 //step 1, generate transform
829 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelviewMatrix);
830 matmultiplyAFFINE(jointMatrix.mat,modelviewMatrix,p->HHMatrix);
831 if (1) save_rig_bone(node, jointMatrix.mat);
832
833 //any motion nodes enabled? if so apply current frame transform
834 // .. moved elsewhere to ensure its between center shifts
835 if(0) if(HH->motions.n){
836 for(int i=0;i<HH->motions.n;i++){
837 if(HH->motionsEnabled.p[i]){
838 //printmatrix(jointMatrix.mat);
839 update_jointMatrixFromMotion(HH->motions.p[i],node->name->strptr,jointMatrix.mat);
840 //printmatrix(jointMatrix.mat);
841 }
842 }
843 }
844 //if(HH->skinNormal){
845 if(1){
846 //want 'inverse-transpose' 3x3 float for transforming normals
847 //(its almost the same as jointMatrix.mat except when shear due to assymetric scales)
848 float fmat4[16], fmat3[9],fmat3i[9]; //,fmat3it[9];
849 matdouble2float4(fmat4,jointMatrix.mat);
850 mat423f(fmat3,fmat4);
851 matinverse3f(fmat3i,fmat3);
852 mattranspose3f(jointMatrix.normat,fmat3i);
853 //printf("jm.normat[1] %f\n",jointMatrix.normat[1]);
854 }
855
856 //if (0) render_rig_bone(JT, node, jointMatrix.mat);
857
858 //step 2, add transform to HH transform list, get its index in list
859 stack_push(JMATRIX,hr->JT,jointMatrix);
860
861 //I'll let this index start at 1, and subtract 1 when retrieving with vector_get,
862 //so I can use jointTransformIndex==0 as a sentinal value for 'no transform stored'
863 //to save me from having an extra .n transforms variable
864 jointTransformIndex = vectorSize(hr->JT);
865
866 //step 3, add transform index and weight to each skin vertex
867 // we re-do this on every frame but could we skip?
868 // no, we have to zero PVI, PVW and start over on every frame
869 PVW = hr->PVW; // (float*)HH->_PVW;
870 PVI = hr->PVI; // (int*)HH->_PVI;
871 if (PVW && PVI) {
872 for (i = 0; i < node->skinCoordIndex.n; i++) {
873 int idx = node->skinCoordIndex.p[i];
874 float wt = node->skinCoordWeight.n ? node->skinCoordWeight.p[min(i, node->skinCoordWeight.n - 1)] : 1.0f;
875 for (j = 0; j < 4; j++) {
876 if (PVI[idx * 4 + j] == 0) {
877 PVI[idx * 4 + j] = jointTransformIndex;
878 PVW[idx * 4 + j] = wt;
879 break;
880 }
881 }
882 }
883 hr->PVset = TRUE;
884 }
885
886 //step 4: add on any Displacer displacements
887 if (HH->skinCoord && node->displacers.n) {
888 if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_CPU) {
889 int ni, i;
890 float* psc, * pdp;
891 int* ci;
892 struct X3D_Coordinate* nc = (struct X3D_Coordinate*)HH->skinCoord;
893 psc = (float*)nc->point.p;
894 // nsc = nc->point.n;
895 for (i = 0; i < node->displacers.n; i++) {
896 int index, j;
897 float* point, weight, wdisp[3];
898 struct X3D_HAnimDisplacer* dp = (struct X3D_HAnimDisplacer*)node->displacers.p[i];
899 weight = dp->weight; //updated from MotionInterpolator in prep_HAnimJoint
900 //printf(" %f ",weight);
901 pdp = (float*)dp->displacements.p;
902 // ndp = dp->displacements.n;
903
904 ni = dp->coordIndex.n;
905 ci = dp->coordIndex.p;
906 for (j = 0; j < ni; j++) {
907 index = ci[j];
908 point = &psc[index * 3];
909 vecscale3f(wdisp, &pdp[j * 3], weight);
910 vecadd3f(point, point, wdisp);
911 }
912 }
913 if (0) { //this is done in child_HAnimHumanoid for the skinCoord parents
914 //force HAnimSegment.children[] shape nodes using segment->coord to recompile
915 int k;
916 Stack* parents;
917 HH->skinCoord->_change++;
918 parents = HH->skinCoord->_parentVector;
919 for (k = 0; k < vectorSize(parents); k++) {
920 struct X3D_Node* parent = vector_get(struct X3D_Node*, parents, k);
921 parent->_change++;
922 }
923 }
924
925 }
926 else if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_GPU) {
927 /* Displacements are summed on CPU side and put in a 'packed' array of size
928 vec3 x number of unique vertexes being displaced
929 This is because usually a small % of all humanoid.skinCoord coordinates
930 are displaced, for example small facial features or clothing
931 And the displacements are sent to GPU on every frame, so the smaller
932 the better. To index into this paced array, a displacer index dindex [] array
933 is prepared early in the scene and sent once to GPU. In the GPU:
934 vertex_object += displace[dindex[cindex]];
935 where cindex (fw_Cindex) is the index of the vertex in the original (not streamed)
936 humanoid.skinCoord, and is an attribute in the vertex shader.
937 This method avoids indefinite length loops in GPU shader which stall parallel threads,
938 and avoids sending the whole updated coordinate array to GPU.
939 So if it doesn't speed up displacers, why are we doing it?
940 Because when we do the skinning coordinate transforms on the GPU shader we
941 send the coordinates once early, so they would miss out on displacements
942 if we didn't do ths.
943 */
944 if (!hr->dindex_done) {
945 if (!hr->dindex_lookup) {
946 // lookup tuple (cindex,dindex)
947 // does 2 things 1) stores cindex (index of coordinate) so
948 // we know if it's already been used once by another displacer
949 // 2) records the dindex into the packed array for that cindex
950 hr->dindex_lookup = newStack(ivec2);
951 // reserve the first row of packed displace array for 0,0,0
952 // so when a coordinate has no dindex (dindex == 0) it gets
953 // a default displacement of 0,0,0 in the shader.
954 ivec2 cindin = { -1,0 };
955 hr->ND = 1;
956 stack_push(ivec2, hr->dindex_lookup, cindin);
957 }
958 for (i = 0; i < node->displacers.n; i++) {
959 int index, * dindex, j;
960 float* point, weight, wdisp[3];
961 struct X3D_HAnimDisplacer* dp = (struct X3D_HAnimDisplacer*)node->displacers.p[i];
962 //we add a local dindex once to displacer node
963 // so on subsequent summing passes we don't need to do a lookup
964
965 int ni = dp->coordIndex.n;
966 int* ci = dp->coordIndex.p;
967 if (!dp->_dindex) dp->_dindex = malloc(sizeof(int) * ni);
968 dindex = (int*)dp->_dindex;
969
970 //we want the unique ones, because we only add once in vertex shader
971 for (j = 0; j < ni; j++) {
972 int found = FALSE;
973 for (int k = 0; k < hr->ND; k++) {
974 ivec2 cindin = vector_get(ivec2, hr->dindex_lookup, k);
975 if (cindin.X == ci[j]) {
976 found = TRUE;
977 index = cindin.Y;
978 }
979 }
980 if (!found) {
981 // add to humanoid (cindex,dindex) lookup
982 ivec2 cindin;
983 index = cindin.Y = hr->ND; hr->ND++;
984 cindin.X = ci[j];
985 stack_push(ivec2, hr->dindex_lookup, cindin);
986 // add to humanoid dindex
987 if (!hr->dindex) {
988 //humanoid doesn't know if there are displacers
989 // so we allocate on-demand
990 int nc = X3D_COORDINATE(HH->skinCoord)->point.n;
991 hr->dindex = malloc(sizeof(int) * nc );
992 memset(hr->dindex, 0, sizeof(int)* nc);
993 }
994 hr->dindex[ci[j]] = index;
995 }
996 // add to displacer node dindex
997 dindex[j] = index;
998 }
999 }
1000 hr->joint_displacer_count += node->displacers.n;
1001 }
1002 else {
1003 //summing pass on every frame
1004 // - just add weighted displacements on packed displace array
1005 //whole hr->_displace[] vector is zeroed outside in child_HAnimHumanoid
1006 //weighted-sum diplacements
1007 if(hr->displace)
1008 for (i = 0; i < node->displacers.n; i++) {
1009 int index, *dindex, j;
1010 float* point, weight, wdisp[3];
1011 struct X3D_HAnimDisplacer* dp = (struct X3D_HAnimDisplacer*)node->displacers.p[i];
1012 weight = dp->weight;
1013 if (weight > 0.0f) {
1014 //printf(" %f ",weight);
1015 float* pdp = (float*)dp->displacements.p;
1016 // ndp = dp->displacements.n;
1017 int ni = dp->coordIndex.n;
1018 int* ci = dp->coordIndex.p;
1019 dindex = dp->_dindex;
1020 for (j = 0; j < ni; j++) {
1021 index = dindex[j];
1022 float* displace = &hr->displace[index * 4];
1023 vecscale3f(wdisp, &pdp[j * 3], weight);
1024 vecadd3f(displace, displace, wdisp);
1025 }
1026 }
1027 }
1028 }
1029 }
1030 }
1031 } //if HH
1032
1033}
1034int vecsametol3f(float *a, float *b, float tol){
1035 int i,isame = TRUE;
1036 for(i=0;i<3;i++)
1037 if(fabsf(a[i] - b[i]) > tol) isame = FALSE;
1038 return isame;
1039}
1040
1041
1042void compile_HAnimHumanoid(struct X3D_HAnimHumanoid* node) {
1043 //printf("compile_HAnimHumanoid\n");
1044 //check if the coordinate count is the same
1045 INITIALIZE_EXTENT;
1046 if (!node->_intern) {
1047 node->_intern = malloc(sizeof(struct X3D_HanimRep));
1048 memset(node->_intern, 0, sizeof(struct X3D_HanimRep));
1049 }
1050 struct X3D_HanimRep* hr = (struct X3D_HanimRep*)node->_intern;
1051 hr->itype = 9;
1052 static int oncegpu = 0;
1053 if (!oncegpu) {
1054 char* smeth = "GPU";
1055 if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_CPU)
1056 smeth = "CPU";
1057 printf("Skinning Method: %s\n", smeth);
1058 oncegpu++;
1059 }
1060 push_humanoid(node);
1061 if (node->motions.n) {
1062 if (node->motions.n > node->motionsEnabled.n) {
1063 // the default is to enable all motions
1064 int* moe = MALLOC(int*, node->motions.n * sizeof(int));
1065 memset(moe, 0, node->motions.n * sizeof(int));
1066 memcpy(moe, node->motionsEnabled.p, node->motionsEnabled.n * sizeof(int));
1067
1068 for (int i = node->motionsEnabled.n; i < node->motions.n; i++) {
1069 moe[i] = TRUE; //FALSE //not sure - specs don't say default, just empty [], I'll use TRUE while developing/debugging
1070
1071 }
1072 FREE_IF_NZ(node->motionsEnabled.p);
1073 node->motionsEnabled.p = moe;
1074 node->motionsEnabled.n = node->motions.n;
1075
1076 }
1077 if (node->motions.n > node->_lastMotionsEnabled.n) {
1078 node->_lastMotionsEnabled.p = MALLOC(int*, node->motions.n * sizeof(int));
1079 node->_lastMotionsEnabled.n = node->motions.n;
1080 for (int i = 0; i < node->_lastMotionsEnabled.n; i++)
1081 node->_lastMotionsEnabled.p[i] = 0;
1082 }
1083 for (int i = 0; i < node->motions.n; i++) {
1084 check_compile(node->motions.p[i]);
1085 }
1086 }
1087
1088 int nsc = 0, nsn = 0;
1089 float* psc = NULL, * psn = NULL;
1090 if (node->skinCoord && node->skinCoord->_nodeType == NODE_Coordinate) {
1091 float ee[6];
1092 struct X3D_Coordinate* nc = (struct X3D_Coordinate*)node->skinCoord;
1093 nsc = nc->point.n;
1094 static int ionce = 0;
1095 if (!ionce) {
1096 printf("number of skin coord points %d", nsc);
1097 ionce = 1;
1098 }
1099 else {
1100 //printf("^"); //a hint we are recompiling, for testing in Dec 2023
1101 }
1102 if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_CPU) {
1103 psc = (float*)nc->point.p;
1104 node->_origCoords = realloc(node->_origCoords, nsc * 3 * sizeof(float));
1105 memcpy(node->_origCoords, psc, nsc * 3 * sizeof(float));
1106 if (0) {
1107 //find a few coordinates in skinCoord I hacked, by xyz, and give me their index, for making a displacer
1108 float myfind[9] = { -0.030000f, -0.070000f, 1.777000f, -0.070000f, 1.777000f, 0.130000f, 1.777000f, 0.130000f, 0.070000f };
1109 int i, j;
1110 for (i = 0; i < nsc; i++) {
1111 for (j = 0; j < 3; j++)
1112 if (vecsametol3f(&psc[i * 3], &myfind[j * 3], .001f)) {
1113 printf("%d %f %f %f\n", i, myfind[j * 3 + 0], myfind[j * 3 + 1], myfind[j * 3 + 2]);
1114 }
1115 }
1116 }
1117 //extent6f_from_box3fn(ee,nc->point.p->c, nc->point.n);
1118 //setExtent(ee[0],ee[1],ee[2],ee[3],ee[4],ee[5],X3D_NODE(node));
1119 }
1120 }
1121 if (node->skinNormal && node->skinNormal->_nodeType == NODE_Normal) {
1122 struct X3D_Normal* nn = (struct X3D_Normal*)node->skinNormal;
1123 //Assuming 1 normal per coord, coord 1:1 normal
1124 nsn = nn->vector.n;
1125 if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_CPU) {
1126 psn = (float*)nn->vector.p;
1127 node->_origNorms = realloc(node->_origNorms, nsn * 3 * sizeof(float));
1128 memcpy(node->_origNorms, psn, nsn * 3 * sizeof(float));
1129 }
1130 }
1131 if (!node->skeleton.n && node->joints.n) {
1132 //find name='humanoid_root' or 'root' and put in skeleton
1133 for (int i = 0; i < node->joints.n; i++) {
1134 struct X3D_HAnimJoint* joint = (struct X3D_HAnimJoint*)node->joints.p[i];
1135 char* name = joint->name->strptr;
1136 if (name && !strcmp(name, "humanoid_root") || !strcmp(name, "root") || !strcmp(name, "humanoidroot")){
1137
1138 node->skeleton.p = malloc(sizeof(void*));
1139 node->skeleton.n = 1;
1140 node->skeleton.p[0] = X3D_NODE(joint);
1141 break;
1142 }
1143 }
1144 }
1145 //allocate the joint-transform_index and joint-weight arrays
1146 //Nov 2016: max 4: meaning each skinCoord can have up to 4 joints referencing/influencing it
1147 //4 chosen so it's easier to port to GPU method with vec4
1148 if (hr->NV == 0 || hr->NV != nsc) {
1149 hr->PVI = realloc(hr->PVI, nsc * 4 * sizeof(int)); //indexes, up to 4 joints per skinCoord can be ivec4
1150 hr->PVW = realloc(hr->PVW, nsc * 4 * sizeof(float)); //weights, up to 4 joints per skinCoord
1151 hr->NV = nsc;
1152 }
1153
1154 //allocate the transform array
1155 if (hr->JT == NULL) {
1156 hr->JT = newStack(JMATRIX); //we don't know how many joints there are - need to count as we go
1157 }
1158// node->_renderFlags |= VF_Geom; //a HAnimHumanoid is a child but also skin is geom
1159 MARK_NODE_COMPILED
1160 pop_humanoid();
1161
1162}
1163
1164void rwhat_printf(int rwhat);
1165
1166void child_HAnimHumanoid(struct X3D_HAnimHumanoid *node) {
1167 int nc;
1168 //float *originalCoords;
1169 struct X3D_HAnimHumanoid *HH;
1170 Stack *JT;
1171 ppComponent_HAnim p = (ppComponent_HAnim)gglobal()->Component_HAnim.prv;
1172 COMPILE_IF_REQUIRED;
1173 //LOCAL_LIGHT_SAVE
1174 struct X3D_HanimRep* hr = (struct X3D_HanimRep*)node->_intern;
1175
1176 /* any segments at all? */
1177/*
1178printf ("hanimHumanoid, segment counts joints %d segs %d sites %d skeleton %d skin %d vps %d\n",
1179 node->joints.n,
1180 node->segments.n,
1181 node->sites.n,
1182 node->skeleton.n,
1183 node->skin.n,
1184 node->viewpoints.n);
1185*/
1186
1187 nc = node->joints.n + node->segments.n + node->viewpoints.n + node->sites.n +
1188 node->skeleton.n + node->skin.n;
1189
1190 RETURN_FROM_CHILD_IF_NOT_FOR_ME;
1191 //rwhat_printf(renderstate()->rwhat);
1192
1193 push_humanoid(node);
1194
1195 if(renderstate()->render_vp){
1196 /* Lets do viewpoints */
1197 normalChildren(node->viewpoints);
1198 return;
1199 }
1200
1201 if(node->motions.n){
1202 int nkept = 0;
1203 for(int i=0;i<node->motions.n;i++){
1204 struct X3D_HAnimMotion* HM = (struct X3D_HAnimMotion*)node->motions.p[i];
1205 int keep = node->motionsEnabled.p[i];
1206 HM->transitionWeight = 1.0f;
1207 if (HM->transitionStart == 0.0) HM->transitionStart = TickTime() - node->transitionTime;
1208 if (node->transitionTime > 0.0) {
1209 if (node->motionsEnabled.p[i] != node->_lastMotionsEnabled.p[i]) {
1210 HM->transitionStart = TickTime();
1211 }
1212 double dtime = TickTime() - HM->transitionStart;
1213 float weight = dtime / node->transitionTime;
1214 //printf("%f ", weight);
1215 weight = min(1.0f, weight);
1216 if (node->motionsEnabled.p[i])
1217 HM->transitionWeight = weight;
1218 else HM->transitionWeight = 1.0f - weight;
1219 if (HM->transitionWeight > 0.0f) keep = TRUE;
1220 node->_lastMotionsEnabled.p[i] = node->motionsEnabled.p[i];
1221 }
1222 if (keep) {
1223 //if(HM->transitionWeight < 1.0f)
1224 // printf("%d %f ", i, HM->transitionWeight);
1225 render_node(X3D_NODE(node->motions.p[i]));
1226 nkept++;
1227 }
1228 }
1229 //printf("%d", nkept);
1230 }
1231
1232 // segments, joints, sites are flat-lists for convenience
1233 // skeleton is the scenegraph-like transform hierarchy of joints and segments and sites
1234 // skin relies on something updating its vertices based on skeleton transforms
1235 /* Lets do segments first */
1236 /* now, just render the non-directionalLight segments */
1237 if(0) normalChildren(node->segments);
1238
1239
1240 /* Lets do joints second */
1241 /* do we have to sort this node? */
1242 /* now, just render the non-directionalLight joints */
1243 if(0) normalChildren(node->joints);
1244
1245
1246 /* Lets do sites third */
1247 /* do we have to sort this node? */
1248 /* do we have a local light for a child? */
1249 //LOCAL_LIGHT_CHILDREN(node->sites);
1250 /* now, just render the non-directionalLight sites */
1251 if(0) normalChildren(node->sites);
1252
1253 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
1254
1255 prep_BBox((struct BBoxFields*)&node->bboxCenter);
1256
1257
1258 /* Lets do skeleton fourth */
1259 /* do we have to sort this node? */
1260 /* now, just render the non-directionalLight skeleton */
1261 //skeleton is the basic thing to render for LOA 0
1262 memset(hr->PVI,0,4*hr->NV*sizeof(int));
1263 memset(hr->PVW,0,4*hr->NV*sizeof(float));
1264 JT = hr->JT;
1265 JT->n = 0;
1266
1267 //in theory, HH, HHMatrix could be a stack, so you could have an hanimhumaoid within an hanimhunaniod
1268 HH = node;
1269 {
1270 double modelviewMatrix[16];
1271 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelviewMatrix);
1272 matinverseAFFINE(p->HHMatrix,modelviewMatrix);
1273 }
1274 if(node->skin.n){
1275 if(vertexTransformMethod() == VERTEXTRANSFORMMETHOD_CPU) {
1276 //save original coordinates before rendering skeleton
1277 // - HAnimJoint may have displacers that change the Coords
1278 //transform each vertex and its normal using weighted transform
1279 int nsc = 0, nsn = 0;
1280 float *psc = NULL, *psn = NULL;
1281 if(node->skinCoord && node->skinCoord->_nodeType == NODE_Coordinate){
1282 struct X3D_Coordinate * nc = (struct X3D_Coordinate * )node->skinCoord;
1283 struct X3D_Normal *nn = (struct X3D_Normal *)node->skinNormal; //might be NULL
1284 nsc = nc->point.n;
1285 psc = (float*)nc->point.p;
1286 memcpy(psc,node->_origCoords,3*nsc*sizeof(float));
1287 if(nn){
1288 nsn = nn->vector.n;
1289 psn = (float *)nn->vector.p;
1290 memcpy(psn,node->_origNorms,3*nsn*sizeof(float));
1291 }
1292 }
1293 }
1294 else if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_GPU) {
1295 //we clear the displacer sum table on every frame before
1296 // rendering skeleton, where joint displacements will be sum = weight x displacment
1297 if (hr->ND) {
1298 if(!hr->displace)
1299 hr->displace = malloc(hr->ND * 4 * sizeof(float));
1300 hr->dindex_done = TRUE; //only do the dindex once
1301 memset(hr->displace, 0, hr->ND * 4 * sizeof(float));
1302 }
1303 }
1304
1305 }
1306
1307 float zerocenter[3];
1308 //push_joint_center(vecset3f(zerocenter, 0.0f, 0.0f, 0.0f));
1309 if (node->skeleton.n && !node->joints.n) {
1310 //set a flag to make a joints list
1311 hr->make_joint_list = TRUE;
1312 }
1313
1314 if(1) normalChildren(node->skeleton); //render_HAnimJoint happens here
1315 if (hr->make_joint_list) {
1316 printf("joint names [");
1317 for (int j = 0; j < node->joints.n; j++) {
1318 struct X3D_HAnimJoint* jnode = (struct X3D_HAnimJoint*)node->joints.p[j];
1319 printf("%s ", jnode->name->strptr);
1320 }
1321 printf("]");
1322 hr->make_joint_list = FALSE;
1323 }
1324
1325 //pop_joint_center();
1326 int renderpass = (renderstate()->render_geom || renderstate()->render_other) && !renderstate()->render_sensitive;
1327 //rwhat_printf(renderstate()->rwhat);
1328 if(node->skin.n){
1329 if (renderpass) {
1330 if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_CPU) {
1331 //save original coordinates
1332 //transform each vertex and its normal using weighted transform
1333 int i, j, nsc = 0;
1334 // int nsn = 0;
1335 float* psc = NULL, * psn = NULL;
1336 if (node->skinCoord && node->skinCoord->_nodeType == NODE_Coordinate) {
1337 float ee[6];
1338 struct X3D_Coordinate* nc = (struct X3D_Coordinate*)node->skinCoord;
1339 struct X3D_Normal* nn = (struct X3D_Normal*)node->skinNormal; //might be NULL
1340 nsc = nc->point.n;
1341 psc = (float*)nc->point.p[0].c;
1342 //memcpy(psc,node->_origCoords,3*nsc*sizeof(float));
1343 if (nn) {
1344 // nsn = nn->vector.n;
1345 psn = (float*)nn->vector.p;
1346 //memcpy(psn,node->_origNorms,3*nsn*sizeof(float));
1347 }
1348
1349 for (i = 0; i < nsc; i++) {
1350 float totalWeight;
1351 float* point, * norm;
1352 float newpoint[3], newnorm[3];
1353 float* PVW;
1354 int* PVI;
1355
1356 point = &psc[i * 3];
1357 norm = NULL;
1358 if (nn) norm = &psn[i * 3];
1359 PVW = hr->PVW;
1360 PVI = hr->PVI;
1361
1362 memset(newpoint, 0, 3 * sizeof(float));
1363 memset(newnorm, 0, 3 * sizeof(float));
1364 totalWeight = 0.0f;
1365 for (j = 0; j < 4; j++) {
1366 int jointTransformIndex = PVI[i * 4 + j];
1367 float wt = PVW[i * 4 + j];
1368 if (jointTransformIndex > 0) {
1369 float tpoint[3], tnorm[3];
1370 JMATRIX jointMatrix;
1371 jointMatrix = vector_get(JMATRIX, hr->JT, jointTransformIndex - 1);
1372 transformf(tpoint, point, jointMatrix.mat);
1373 vecscale3f(tpoint, tpoint, wt);
1374 vecadd3f(newpoint, newpoint, tpoint);
1375 if (nn) {
1376 transform3x3f(tnorm, norm, jointMatrix.normat);
1377 vecnormalize3f(tnorm, tnorm);
1378 vecscale3f(tnorm, tnorm, wt);
1379 vecadd3f(newnorm, newnorm, tnorm);
1380 }
1381 totalWeight += wt;
1382 }
1383 }
1384 if (totalWeight > 0.0f) {
1385 vecscale3f(newpoint, newpoint, 1.0f / totalWeight);
1386 veccopy3f(point, newpoint);
1387 if (nn) {
1388 vecscale3f(newnorm, newnorm, 1.0f / totalWeight);
1389 vecnormalize3f(norm, newnorm);
1390 }
1391 }
1392 }
1393 if (0) {
1394 //print out before and after coords
1395 float* osc = node->_origCoords;
1396 for (i = 0; i < nsc; i++) {
1397 printf("%d ", i);
1398 for (j = 0; j < 3; j++) printf("%f ", psc[i * 3 + j]);
1399 printf("/ ");
1400 for (j = 0; j < 3; j++) printf("%f ", osc[i * 3 + j]);
1401 printf("\n");
1402 }
1403 printf("\n");
1404 }
1405
1406 //trigger recompile of skin->shapes when rendering skin
1407 //Nov 6, 2016: recompiling a shape / polyrep on each frame eats memory
1408 //NODE_NEEDS_COMPILING
1409 if (1) {
1410 int k;
1411 Stack* parents;
1412 node->skinCoord->_change++;
1413 parents = node->skinCoord->_parentVector;
1414 for (k = 0; k < vectorSize(parents); k++) {
1415 struct X3D_Node* parent = vector_get(struct X3D_Node*, parents, k);
1416 parent->_change++;
1417 }
1418 }
1419 //extent6f_from_box3fn(ee, psc, nsc);
1420 //setExtent(ee[0], ee[1], ee[2], ee[3], ee[4], ee[5], X3D_NODE(node));
1421
1422 }
1423 }
1424 else if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_GPU) {
1425 if (renderstate()->render_blend || renderstate()->render_geom) {// == (node->_renderFlags & VF_Blend)) {
1426 //push shader flaga with += SKINNING (later in child_Shape when we filter the skin shapes by Coordinate node == humanoid.coord)
1427 if (node->skinCoord && node->skinCoord->_nodeType == NODE_Coordinate) {
1428 push_humanoid_skinCoord(node->skinCoord);
1429 //#define USING_IMAGEBUFFER 1
1430
1431 //bind skin weights and joint indexes to SSBO once if not done yet
1432 // https://www.khronos.org/opengl/wiki/Shader_Storage_Buffer_Object
1433 if ((hr->joint_changed == TRUE) && hr->PVset) {
1434 hr->joint_changed = FALSE;
1435 //OGLPG 4.5 Chapter 11 Memory example 11.6 Creating a Buffer and Using It for Shader Storage
1436 if (1) {
1437 //skin weights PVW
1438 static int pvwonce = 1;// 0;
1439 if (pvwonce == 0) {
1440 for (int kk = 0; kk < hr->NV; kk++)
1441 {
1442 printf("[");
1443 for (int jj = 0; jj < 4; jj++) {
1444 printf("%f ", hr->PVW[kk * 4 + jj]);
1445 }
1446 printf("]");
1447 }
1448 pvwonce++;
1449 }
1450 if (hr->bo_PVW == 0) {
1451 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 0");
1452 glGenBuffers(1, &hr->bo_PVW);
1453 }
1454 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_PVW);
1455 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 1");
1456 glBufferData(GL_SHADER_STORAGE_BUFFER, hr->NV * sizeof(float) * 4, hr->PVW, GL_STATIC_DRAW); //sizeof(data) only works for statically sized C/C++ arrays.
1457 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 2");
1458
1459 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, hr->bo_PVW); //GL 3+
1460 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 3");
1461
1462 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // unbind
1463 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 4");
1464 }
1465 if (1) {
1466 //skin joint_matrix indexes
1467 static int pvionce = 1; //0;
1468 if (pvionce == 0) {
1469 for (int kk = 0; kk < hr->NV; kk++)
1470 {
1471 printf("[");
1472 for (int jj = 0; jj < 4; jj++) {
1473 printf("%d ", hr->PVI[kk * 4 + jj]);
1474 }
1475 printf("]");
1476 }
1477 pvionce++;
1478 }
1479 if (hr->bo_PVI == 0)
1480 glGenBuffers(1, &hr->bo_PVI);
1481 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_PVI);
1482 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 5");
1483 glBufferData(GL_SHADER_STORAGE_BUFFER, hr->NV * sizeof(int) * 4, hr->PVI, GL_STATIC_DRAW); //GL 2+ sizeof(data) only works for statically sized C/C++ arrays.
1484 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 6");
1485 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, hr->bo_PVI);
1486 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 7");
1487 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // unbind
1488 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 8");
1489 }
1490 if (hr->dindex) {
1491 //displacers, send dindex[nc] once
1492 static int dionce = 0; //0;
1493 if (dionce == 0) {
1494 for (int kk = 0; kk < hr->NV; kk++)
1495 {
1496 if (hr->dindex[kk]) {
1497 printf("[%d %d]\n", kk, hr->dindex[kk]);
1498 }
1499 }
1500 //dionce++;
1501 }
1502
1503 if (hr->bo_dindex == 0)
1504 glGenBuffers(1, &hr->bo_dindex);
1505 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_dindex);
1506 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 9");
1507 glBufferData(GL_SHADER_STORAGE_BUFFER, hr->NV * sizeof(int), hr->dindex, GL_STATIC_DRAW); //GL 2+ sizeof(data) only works for statically sized C/C++ arrays.
1508 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 10");
1509 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 14, hr->bo_dindex);
1510 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 11");
1511 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // unbind
1512 PRINT_GL_ERROR_IF_ANY("Hanim SSBO 12");
1513 }
1514
1515 }
1516 if (hr->ND && hr->displace) {
1517 //send every frame (unless a flag says non of the displacers are weighted,
1518 // and sent 0s once already)
1519 if (!hr->bo_displace) {
1520 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO 0");
1521 glGenBuffers(1, &hr->bo_displace);
1522 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_displace);
1523 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO 13");
1524 glBufferData(GL_SHADER_STORAGE_BUFFER, hr->ND * 4 * sizeof(float), NULL, GL_DYNAMIC_DRAW); //sizeof(data) only works for statically sized C/C++ arrays.
1525 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO 14");
1526 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 15, hr->bo_displace);
1527 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO 15");
1528 }
1529 else {
1530 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_displace);
1531 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO 16");
1532 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 15, hr->bo_displace);
1533 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO 17");
1534 }
1535 static int donce = 1; //0;
1536 if (donce == 0) {
1537 printf("\nND %d\n", hr->ND);
1538 int nz = 0;
1539 for (int kk = 0; kk < hr->ND; kk++)
1540 {
1541 float* v4 = &hr->displace[kk * 4];
1542 if (v4[0] == v4[1] == v4[2] == 0.0f) nz++;
1543 printf("[%d %f %f %f]\n", kk, v4[0], v4[1], v4[2]);
1544 }
1545 if (nz > 1) {
1546 printf("nz=%d ", nz - 1);
1547 }
1548 //printf("rs %d %d %o\n", renderstate()->render_blend, renderstate()->render_geom, renderstate()->rwhat);
1549 rwhat_printf(renderstate()->rwhat);
1550 donce++;
1551 }
1552 //donce++;
1553 //float* dd = hr->displace;
1554 //vecset3f(dd, .01, 0.0); //test
1555 void* bdata = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY); //GL 2+ access modes Table 3.4 in Opengl Programmers Guide 4.5 location 3708
1556 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO 18");
1557 memcpy(bdata, hr->displace, hr->ND * 4 * sizeof(float));
1558 glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
1559 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO 18");
1560 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1561
1562
1563 }
1564 if (vectorSize(hr->JT)) {
1565 // skin joint_matrix - send every frame
1566 //# joints LAO1 18 LOA2 71 LOA3 94 LOA4 144
1567 // uniform blocks limited to 64k bytes
1568 // SSBO no limit on size
1569 //convert joint transforms to float32
1570 int normatsize = 12; //mat3 is 12, mat4 is 16
1571 int nmat = vectorSize(hr->JT);
1572 if (hr->jt32 == NULL)
1573 hr->jt32 = malloc(nmat * 16 * sizeof(float));
1574 if (hr->jn32 == NULL) {
1575 hr->jn32 = malloc(nmat * normatsize * sizeof(float));
1576 memset(hr->jn32, 0, nmat * normatsize * sizeof(float));
1577 }
1578 for (int i = 0; i < nmat; i++) {
1579 JMATRIX* jm = vector_get_ptr(JMATRIX, hr->JT, i);
1580 double2float(&hr->jt32[i * 16], jm->mat, 16);
1581 //if (HH->skinNormal) {
1582 //GLSL needs N4 alignment
1583 // .. a mat3 needs a padding on every row, so 3 rows of 4 floats
1584 for (int k = 0; k < 3; k++)
1585 veccopy3f(&hr->jn32[i * normatsize + k * 4], &jm->normat[k * 3]);
1586 if (normatsize == 16) hr->jn32[i * normatsize + 15] = 1.0f;
1587 //}
1588 }
1589
1590 if (1) {
1591 static int mat_once = 0;
1592 if (0 && mat_once == 30) {
1593 int nrow = 4;
1594 for (int kk = 0; kk < nmat; kk++) {
1595 printf("%d\n", kk);
1596 for (int jj = 0; jj < 4; jj++) {
1597 for (int ii = 0; ii < 4; ii++)
1598 printf("%f ", hr->jt32[(kk * nrow + jj) * 4 + ii]);
1599 printf("\n");
1600 }
1601 }
1602 }
1603 mat_once++;
1604
1605 if (!hr->bo_JT) {
1606 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO 0");
1607 glGenBuffers(1, &hr->bo_JT);
1608 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_JT);
1609 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO 1");
1610 glBufferData(GL_SHADER_STORAGE_BUFFER, nmat * 16 * sizeof(float), NULL, GL_DYNAMIC_DRAW); //sizeof(data) only works for statically sized C/C++ arrays.
1611 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO 2");
1612 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, hr->bo_JT);
1613 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO 3");
1614 }
1615 else {
1616 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_JT);
1617 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO 4");
1618 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, hr->bo_JT);
1619 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO 5");
1620 }
1621 void* bdata = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY); //GL 2+ access modes Table 3.4 in Opengl Programmers Guide 4.5 location 3708
1622 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO 6");
1623 memcpy(bdata, hr->jt32, nmat * 16 * sizeof(float));
1624 glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
1625 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO 7");
1626 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1627
1628 }
1629 if (1) { //&& HH->skinNormal) {
1630 static int normat_once = 0;
1631 if (0 && normat_once == 30) {
1632 int nrow = normatsize == 12 ? 3 : 4;
1633 for (int kk = 0; kk < nmat; kk++) {
1634 printf("%d\n", kk);
1635 for (int jj = 0; jj < 4; jj++) {
1636 for (int ii = 0; ii < 4; ii++)
1637 printf("%f ", hr->jn32[(kk * nrow + jj) * 4 + ii]);
1638 printf("\n");
1639 }
1640 }
1641 }
1642 normat_once++;
1643 if (!hr->bo_JN) {
1644 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO 0");
1645 glGenBuffers(1, &hr->bo_JN);
1646 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_JN);
1647 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO 1");
1648 glBufferData(GL_SHADER_STORAGE_BUFFER, nmat * normatsize * sizeof(float), NULL, GL_DYNAMIC_DRAW); //sizeof(data) only works for statically sized C/C++ arrays.
1649 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO 2");
1650 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 13, hr->bo_JN);
1651 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO 3");
1652 }
1653 else {
1654 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_JN);
1655 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO 4");
1656 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 13, hr->bo_JN);
1657 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO 5");
1658 }
1659 void* bdata = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY); //GL 2+ access modes Table 3.4 in Opengl Programmers Guide 4.5 location 3708
1660 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO 6");
1661 memcpy(bdata, hr->jn32, nmat * normatsize * sizeof(float));
1662 glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
1663 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO 7");
1664 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1665
1666 }
1667 }
1668 }
1669 }
1670 }
1671 }
1672 hr->render_count++; //mysterious shader crash with GL_LINES on early render passes
1673 if(hr->render_count > 2) normalChildren(node->skin);
1674 if(0) for (int j = 0; j < node->skin.n; j++) {
1675 printf("skin[%d] extent: ", j);
1676 for (int i = 0; i < 6; i++) printf("%4.3f ", node->skin.p[j]->_extent[i]);
1677 printf("\n");
1678 }
1679 if (renderpass) {
1680 if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_GPU) {
1681 //pop shader flags
1682 if (renderstate()->render_blend || renderstate()->render_geom) { //} == (node->_renderFlags & VF_Blend)) {
1683 //push shader flaga with += SKINNING (later in Shape or render_polyrep)
1684 if (node->skinCoord && node->skinCoord->_nodeType == NODE_Coordinate) {
1685 //unbind joint matrices UBO
1686 //unbind skin weights SSBO
1687 pop_humanoid_skinCoord();
1688 }
1689 }
1690 }
1691 else if (vertexTransformMethod() == VERTEXTRANSFORMMETHOD_CPU) {
1692 //restore original coordinates
1693 int nsc, nsn;
1694 float* psc, * psn;
1695 struct X3D_Coordinate* nc = (struct X3D_Coordinate*)node->skinCoord;
1696 struct X3D_Normal* nn = (struct X3D_Normal*)node->skinNormal;
1697 nsc = nc->point.n;
1698 psc = (float*)nc->point.p;
1699 memcpy(psc, node->_origCoords, 3 * nsc * sizeof(float));
1700 if (nn) {
1701 nsn = nn->vector.n;
1702 psn = (float*)nn->vector.p;
1703 memcpy(psn, node->_origNorms, 3 * nsn * sizeof(float));
1704 }
1705 }
1706 }
1707 } //if skin
1708 //if (renderstate()->render_geom) printf("humanoid gets geom and other=%d\n",renderstate()->render_other);
1709 render_rig_bones(); //rendered last so depth testing can be disabled
1710
1711 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
1712 //printf("bboxCenter %f %f %f size %f %f %f\n", node->bboxCenter.c[0], node->bboxCenter.c[1], node->bboxCenter.c[2],
1713 // node->bboxSize.c[0], node->bboxSize.c[1], node->bboxSize.c[2]);
1714 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
1715
1716
1717 /* did we have that directionalLight? */
1718 //LOCAL_LIGHT_OFF
1719 pop_humanoid();
1720}
1721//called from child_Shape if skinning: sendSkinningInfo, clearSkinningInfo
1722// it should only come in here if GPU skinning is activated
1723void sendSkinningInfo() {
1724 if (1) {
1725 struct X3D_HAnimHumanoid* HH = peek_humanoid();
1726 struct X3D_HanimRep* hr = (struct X3D_HanimRep*)HH->_intern;
1727 PRINT_GL_ERROR_IF_ANY("Hanim SENd 0");
1728
1729 if(1)
1730 {
1731 //JT_SSBO
1732 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_JT);
1733 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO SENd 3");
1734 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, hr->bo_JT);
1735 PRINT_GL_ERROR_IF_ANY("Hanim JT_SSBO SENd 4");
1736 if (hr->bo_JN) {
1737 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_JN);
1738 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO SENd 5");
1739 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 13, hr->bo_JN);
1740 PRINT_GL_ERROR_IF_ANY("Hanim JN_SSBO SENd 6");
1741 }
1742 }
1743
1744
1745 if (hr->PVset) {
1746 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_PVW);
1747 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, hr->bo_PVW);
1748 }
1749 if (hr->PVset) {
1750 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_PVI);
1751 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, hr->bo_PVI);
1752 }
1753 //the SKINNING_SHADER flag is added conditionally in child_Shape()
1754 if (hr->bo_dindex) {
1755 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_dindex);
1756 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 14, hr->bo_dindex);
1757 if (hr->bo_displace) {
1758 glBindBuffer(GL_SHADER_STORAGE_BUFFER, hr->bo_displace);
1759 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO SENd 3");
1760 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 15, hr->bo_displace);
1761 PRINT_GL_ERROR_IF_ANY("Hanim ND_SSBO SENd 4");
1762 }
1763 //shaderflagsstruct shaderflags = getShaderFlags();
1764 //shaderflags.base |= DISPLACER_SHADER;
1765 //pushShaderFlags(shaderflags);
1766 }
1767 }
1768}
1769void clearSkinningInfo() {
1770 if (1) {
1771 //struct X3D_HAnimHumanoid* HH = peek_humanoid();
1772 //struct X3D_HanimRep* hr = (struct X3D_HanimRep*)HH->_intern;
1773
1774 PRINT_GL_ERROR_IF_ANY("Hanim CLEAR 0");
1775 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1776 PRINT_GL_ERROR_IF_ANY("Hanim SSBO CLEAR 1");
1777 //if (hr->bo_dindex) {
1778 // popShaderFlags(); //DISPLACER_SHADER
1779 //}
1780 }
1781}
1782void child_HAnimJoint(struct X3D_HAnimJoint *node) {
1783
1784 //CHILDREN_COUNT
1785 /* any children at all? */
1786 //if (nc==0) return;
1787
1788 /* should we go down here? */
1789 //RETURN_FROM_CHILD_IF_NOT_FOR_ME
1790
1791 /* do we have to sort this node? */
1792
1793 /* just render the non-directionalLight children */
1794 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
1795 prep_BBox((struct BBoxFields*)&node->bboxCenter);
1796
1797 push_joint_center(node->center.c); //joint center for drawing armature
1798 /* now, just render the non-directionalLight children */
1799 normalChildren(node->children);
1800 pop_joint_center();
1801
1802 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,TRUE);
1803 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
1804}
1805float *vecmix3f(float *out3, float* a3, float *b3, float fraction){
1806 int i;
1807 for(i=0;i<3;i++){
1808 out3[i] = (1.0f - fraction)*a3[i] + fraction*b3[i];
1809 }
1810 return out3;
1811}
1812void child_HAnimSegment(struct X3D_HAnimSegment *node) {
1813
1814 //CHILDREN_COUNT
1815
1816
1817//note to implementer: have to POSSIBLE_PROTO_EXPANSION(node->coord, tmpN)
1818
1819 /* any children at all? */
1820 //if (nc==0) return;
1821
1822 /* should we go down here? */
1823 //RETURN_FROM_CHILD_IF_NOT_FOR_ME
1824
1825 /* do we have to sort this node? Only if not a proto - only first node has visible children. */
1826
1827 /* now, just render the non-directionalLight children */
1828 if(node->coord && node->displacers.n){
1829
1830 int nsc, ni, i;
1831 float *psc, *pdp;
1832 int *ci;
1833 struct X3D_Coordinate *nc = (struct X3D_Coordinate*)node->coord;
1834 psc = (float*)nc->point.p;
1835 nsc = nc->point.n;
1836 if(!node->_origCoords)
1837 node->_origCoords = malloc(3*nsc*sizeof(float));
1838 memcpy(node->_origCoords,psc,3*nsc*sizeof(float));
1839 for(i=0;i<node->displacers.n;i++){
1840 int index, j;
1841 float *point, weight, wdisp[3];
1842 struct X3D_HAnimDisplacer *dp = (struct X3D_HAnimDisplacer *)node->displacers.p[i];
1843
1844 weight = dp->weight;
1845 //printf(" %f ",weight);
1846 pdp = (float*)dp->displacements.p;
1847 // ndp = dp->displacements.n;
1848
1849 ni = dp->coordIndex.n;
1850 ci = dp->coordIndex.p;
1851 for(j=0;j<ni;j++){
1852 index = ci[j];
1853 point = &psc[index*3];
1854 vecscale3f(wdisp,&pdp[j*3],weight);
1855 vecadd3f(point,point,wdisp);
1856 }
1857 }
1858 if(1){
1859 //force HAnimSegment.children[] shape nodes using segment->coord to recompile
1860 Stack *parents;
1861 int k;
1862 node->coord->_change++;
1863 parents = node->coord->_parentVector;
1864 for(k=0;k<vectorSize(parents);k++){
1865 struct X3D_Node *parent = vector_get(struct X3D_Node*,parents,k);
1866 parent->_change++;
1867 }
1868 }
1869
1870 if(0){
1871 //find a few coordinates in segment->coord I hacked, by xyz, and give me their index,
1872 // for making a displacer
1873 float myfind[9] = {-0.029100f, 1.603000f, 0.042740f, -0.045570f, 1.601000f, 0.036520f, -0.018560f, 1.600000f, 0.043490f };
1874 int j,found = FALSE;
1875 printf("\n");
1876 for(i=0;i<nsc;i++){
1877 for(j=0;j<3;j++)
1878 if(vecsametol3f(&psc[i*3],&myfind[j*3],.0001f)){
1879 printf("%d %f %f %f\n",i,myfind[j*3 + 0],myfind[j*3 +1],myfind[j*3 +2]);
1880 found = TRUE;
1881 }
1882 }
1883 if(found)
1884 printf("\n");
1885 }
1886 }
1887 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
1888 prep_BBox((struct BBoxFields*)&node->bboxCenter);
1889
1890 normalChildren(node->children);
1891
1892 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
1893 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
1894
1895 if(node->coord && node->displacers.n){
1896 int nsc;
1897 float *psc;
1898 struct X3D_Coordinate *nc = (struct X3D_Coordinate*)node->coord;
1899 psc = (float*)nc->point.p;
1900 nsc = nc->point.n;
1901 memcpy(psc,node->_origCoords,3*nsc*sizeof(float));
1902 }
1903}
1904
1905
1906void child_HAnimSite(struct X3D_HAnimSite *node) {
1907
1908 //CHILDREN_COUNT
1909 //RETURN_FROM_CHILD_IF_NOT_FOR_ME
1910
1911 /* do we have to sort this node? */
1912
1913 /* do we have a local light for a child? */
1914 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
1915 prep_BBox((struct BBoxFields*)&node->bboxCenter);
1916
1917 /* now, just render the non-directionalLight children */
1918 normalChildren(node->children);
1919
1920 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,TRUE);
1921 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
1922}
1923
1924
1925// ======== HAnimMotion >>>>>>>>>>>>>>
1926int char_is_separator(char c, char *separators){
1927 int is_sep = FALSE;
1928 char *s = separators;
1929 while(*s != 0){
1930 if(c == *s){
1931 is_sep = TRUE; break;
1932 }
1933 s++;
1934 }
1935 return is_sep;
1936}
1937//adapted from cson
1938static int next_token( char const ** inp, char *separators, char const ** end )
1939{
1940 char const * pos = NULL;
1941 if(!(inp && end && *inp))
1942 printf("ouch\n");
1943 assert( inp && end && *inp );
1944 if( *inp == *end ) return 0;
1945 pos = *inp;
1946 if( !*pos )
1947 {
1948 *end = pos;
1949 return 0;
1950 }
1951 for( ; *pos && ( char_is_separator(*pos,separators)); ++pos) { /* skip preceeding splitters */ }
1952 *inp = pos;
1953 for( ; *pos && ( !char_is_separator(*pos,separators)); ++pos) { /* find next splitter */ }
1954 *end = pos;
1955 return (pos > *inp) ? 1 : 0;
1956}
1957Stack* parse_joint_names(struct X3D_Node* node, char *joint_names){
1958 char *sep = " \n\r\t,";
1959 Stack* jnames = newStack(char*);
1960 //adapted from cson >>
1961 int len, rc;
1962 char *beg, *end;
1963 beg = joint_names;
1964 end = NULL;
1965 for(int i=0;; ++i, beg=end, end=NULL )
1966 {
1967 rc = next_token( &beg, sep, &end );
1968 if(!rc) break;
1969 assert( beg != end );
1970 assert( end > beg );
1971 //*end = '\0';
1972 len = (unsigned int)(end - beg);
1973 char *name = malloc(len+1);
1974 register_node_gc(node,name);
1975 //if( len > (BufSize-1) ) return cson_rc.RangeError;
1976 //memset( buf, 0, len + 1 );
1977 memcpy(name, beg, len );
1978 name[len] = 0;
1979 stack_push(char*,jnames,name);
1980 }
1981 //<< adapted from cson
1982 return jnames;
1983}
1984enum {
1985CHAN_RX = 1,
1986CHAN_RY = 2,
1987CHAN_RZ = 3,
1988CHAN_TX = 4,
1989CHAN_TY = 5,
1990CHAN_TZ = 6,
1991CHAN_NONE = 0,
1992};
1993static struct chan_name {
1994int iname;
1995char *cname;
1996} chan_names [] = {
1997{CHAN_RX, "Xrotation"},
1998{CHAN_RY, "Yrotation"},
1999{CHAN_RZ, "Zrotation"},
2000{CHAN_TX, "Xposition"},
2001{CHAN_TY, "Yposition"},
2002{CHAN_TZ, "Zposition"},
2003{CHAN_NONE,NULL},
2004};
2005static int chan_lookup(char *cname){
2006 int i, iname;
2007 struct chan_name *cn;
2008 i = 0;
2009 iname = 0;
2010 do{
2011 cn = &chan_names[i];
2012 if(!strcmp(cn->cname,cname)){
2013 iname = cn->iname;
2014 break;
2015 }
2016 i++;
2017 }while(cn->cname != NULL);
2018 return iname;
2019
2020}
2021struct joint_frame_motion {
2022 char *jname;
2023 char *mocap_name;
2024 int nchan;
2025 int ichan[6];
2026 int level;
2027 float *values;
2028};
2029char *channame_lookup(int ichan){
2030 int i;
2031 struct chan_name *cn;
2032 i = 0;
2033 char * cname = NULL;
2034 do{
2035 cn = &chan_names[i];
2036 if(cn->iname == ichan){
2037 cname = cn->cname;
2038 break;
2039 }
2040 i++;
2041 }while(cn->iname != CHAN_NONE);
2042 return cname;
2043}
2044char *next_buffer_token(char **beg, char* sep, char **end){
2045 static char buffer[128];
2046 int len, rc;
2047 buffer[0] = '\0';
2048 rc = next_token( beg, sep, end );
2049 if(rc){
2050 assert( *beg != *end );
2051 assert( *end > *beg );
2052 //*end = '\0';
2053 len = (unsigned int)(*end - *beg);
2054 len = min(len,127);
2055 memcpy(buffer, *beg, len );
2056 buffer[len] = 0;
2057 }
2058 return buffer;
2059}
2060int parse_channels(char *channelstring, int nentries, struct joint_frame_motion * chan){
2061 char *sep = " \n\r\t,";
2062 //adapted from cson >>
2063 int len, rc, count, totalcount;
2064 char *beg, *end, *token;
2065 totalcount = 0;
2066 beg = channelstring;
2067 end = NULL;
2068 for(int i=0;i<nentries; ++i, beg=end, end=NULL )
2069 {
2070 token = next_buffer_token( &beg, sep, &end );
2071 len = strlen(token);
2072 if(!len) break;
2073 sscanf(token,"%d",&count);
2074 totalcount += count;
2075 chan[i].nchan = count;
2076 for(int j=0;j<count;j++){
2077 beg=end; end=NULL;
2078 token = next_buffer_token( &beg, sep, &end );
2079 int ichan = chan_lookup(token);
2080 chan[i].ichan[j] = ichan;
2081 }
2082 }
2083 return totalcount;
2084}
2085//struct mojoint {
2086// float v[6];
2087//};
2088//struct moframe {
2089// struct mojoint * mj;
2090//};
2091float *parse_float_values(int n, char *str){
2092 char *beg, *end, *token;
2093 int len;
2094 char *sep = " \n\r\t,";
2095 float *fv = malloc(n*sizeof(float));
2096 beg = str;
2097 end = NULL;
2098 for(int i=0;i<n; ++i, beg=end, end=NULL )
2099 {
2100 token = next_buffer_token( &beg, sep, &end );
2101 len = (unsigned int)(*end - *beg);
2102 if(!len) break;
2103 sscanf(token,"%f",&fv[i]);
2104 }
2105 return fv;
2106}
2107//void parse_values(struct moframe *moframes,int framecount, int jointcount, int channelcount, struct joint_frame_motion* chan, char *values){
2108// float *fvalues = parse_float_values(framecount * channelcount, values);
2109// float *fv = fvalues;
2110// for(int i=0;i<framecount;i++){
2111// struct moframe *mof = &moframes[i];
2112// for(int j=0;j<jointcount;j++){
2113// struct mojoint *moj = &mof->mj[j];
2114// for(int k=0;k<chan[j].count;k++){
2115// moj->v[k] = *fv;
2116// if(chan[j].channel[k] < 4)
2117// moj->v[k] *= PI/180.0;
2118// fv++;
2119// }
2120// }
2121// }
2122//}
2123#define RADIANS_PER_DEGREE (double)0.0174532925199432957692
2124#define DEGREES_PER_RADIAN (double)57.2957795130823208768
2125void compile_HAnimMotion(struct X3D_HAnimMotion *node) {
2126 //motion data
2127 if (node->frameCount == 0) {
2128 //parse jouint names
2129 struct Vector* jnames = parse_joint_names(X3D_NODE(node), node->joints->strptr);
2130 printf("\n");
2131 for (int i = 0; i < jnames->n; i++)
2132 printf("%d %s\n", i, vector_get(char*, jnames, i));
2133 int njoints = jnames->n;
2134
2135 //parse channels
2136 struct joint_frame_motion* chan = malloc(njoints * sizeof(struct joint_frame_motion));
2137 int channelcount = parse_channels(node->channels->strptr, njoints, chan);
2138 //in theory channelcount is how many floats to advance in fvalues to get the next frame pointer.
2139
2140
2141 for (int i = 0; i < njoints; i++) {
2142 chan[i].jname = vector_get(char*, jnames, i);
2143 printf("joint %d nchan %d ", i, chan[i].nchan);
2144 for (int j = 0; j < chan[i].nchan; j++) {
2145 printf("%s ", channame_lookup(chan[i].ichan[j]));
2146 }
2147 printf("\n");
2148 }
2149 //parse float frame data
2150 //float *fvalues = parse_float_values(node->frameCount * channelcount, node->values->strptr);
2151 float* fvalues = node->values.p;
2152 if (channelcount)
2153 node->frameCount = node->values.n / channelcount;
2154 else
2155 node->frameCount = 0;
2156 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_HAnimMotion, frameCount));
2157
2158 //convert degrees to radians
2159 for (int iframe = 0; iframe < node->frameCount; iframe++) {
2160 float* fv = &fvalues[iframe * channelcount];
2161 int kchan = 0;
2162 for (int j = 0; j < njoints; j++) {
2163 //printf("%s %d \n",vector_get(char*,jnames,j),chan[j].nchan);
2164 for (int k = 0; k < chan[j].nchan; k++) {
2165 if (chan[j].ichan[k] < 4)
2166 fv[kchan] *= RADIANS_PER_DEGREE; //PI / 180.0; //
2167 //printf("%d %5.2f ",chan[j].ichan[k],chan[j].ichan[k] < 4 ? fv[kchan]*180.0/PI : fv[kchan]);
2168 kchan++;
2169 }
2170 //printf("\n");
2171 }
2172 }
2173
2174 //we won't 'map' to parent during compile - we'll find the motion joint -if any- on the fly in HAnimJoint function(s)
2175
2176 //frame state
2177 //?? anything to do?
2178 node->_njoints = njoints;
2179 node->_channelcount = channelcount;
2180 node->_fvalues = fvalues;
2181 node->_channels = chan;
2182 node->_framevalues = fvalues;
2183 //node->startFrame = 0;
2184 if (node->endFrame == 0) node->endFrame = node->frameCount - 1;
2185 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_HAnimMotion, frameCount));
2186 printf("frameCount %d startFrame %d endFrame %d channels %d\n",
2187 node->frameCount, node->startFrame, node->endFrame, node->_channelcount);
2188 }
2189 MARK_NODE_COMPILED
2190}
2191void render_HAnimMotion(struct X3D_HAnimMotion *node) {
2192 //main job: set the frame pointer for the current time, increment, enabled state
2193 COMPILE_IF_REQUIRED
2194 int index = 0;
2195 float *fvalues = (float*)node->_fvalues;
2196 int channelcount = (int)node->_channelcount;
2197 float *frame_values;
2198 int isActive = FALSE;
2199
2200 int increment = node->frameIncrement;
2201 if(increment == 0) return; //the official way to pause
2202 index = node->frameIndex;
2203 int fcount = node->frameCount;
2204 index = max(0,min(index,fcount-1)); //iclamp
2205
2206 int starting = 0;
2207 int stopping = 0;
2208 isActive = node->enabled && ((node->loop && increment != 0) || (increment > 0 && index < fcount -1) || (increment < 0 && index > 0) );
2209 if(node->enabled && !node->_lastenabled){
2210 starting = TRUE;
2211 node->_lastenabled = node->enabled;
2212 }else if(!node->enabled && node->_lastenabled){
2213 stopping = TRUE;
2214 node->_lastenabled = node->enabled;
2215 }
2216 if(starting){
2217 node->_startTime = TickTime();
2218 }
2219
2220
2221 if(node->next){
2222 index = index + increment;
2223 node->next = FALSE;
2224 } else if(node->previous){
2225 index = index - increment;
2226 node->previous = FALSE;
2227 } else if(node->enabled){
2228 double dtime = TickTime() - node->_startTime;
2229 index = node->frameIncrement * (int)( dtime / node->frameDuration);
2230 }
2231 int startingloop = 0;
2232 if(node->loop){
2233 int lindex = index % fcount;
2234 startingloop = lindex != index;
2235 index = lindex;
2236 }
2237 index = max(0,min(index,fcount-1)); //iclamp
2238 if(starting && index == fcount -1 && increment > 0) index = 0;
2239 if(starting && index == 0 && increment < 0) index = fcount -1;
2240 if(starting || startingloop ){
2241 node->cycleTime = TickTime();
2242 MARK_EVENT (X3D_NODE(node), offsetof(struct X3D_HAnimMotion, cycleTime));
2243 }
2244 if(isActive){
2245 node->elapsedTime = TickTime();
2246 MARK_EVENT (X3D_NODE(node), offsetof(struct X3D_HAnimMotion, elapsedTime));
2247 }
2248 int last_index = node->frameIndex;
2249 node->frameIndex = index;
2250 if (last_index != index)
2251 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_HAnimMotion, frameIndex));
2252 frame_values = &fvalues[node->frameIndex * channelcount];
2253 node->_framevalues = frame_values; //frame pointer into big array of floats, good for current frame only
2254}
2255enum{
2256 LOADER_INITIAL_STATE=0,
2257 LOADER_REQUEST_RESOURCE,
2258 LOADER_FETCHING_RESOURCE,
2259 LOADER_PROCESSING,
2260 LOADER_LOADED,
2261 LOADER_COMPILED,
2262 LOADER_STABLE,
2263};
2264
2265struct joint_frame_motion * jointFrameMotion(struct X3D_HAnimMotion *node, char *jname){
2266 struct joint_frame_motion * jm = NULL;
2267 if(node){
2268 if(node->_nodeType == NODE_HAnimMotion){
2269 struct X3D_HAnimMotion* HM = (struct X3D_HAnimMotion*) node;
2270 if(HM->enabled){
2271 //see if we have the joint
2272 int njoints = (int)HM->_njoints;
2273 struct joint_frame_motion * chan = HM->_channels;
2274 float *frame_values = (float*)HM->_framevalues; //render_HAnimMotion should have run this frame to set the frame pointer
2275 int kchan = 0;
2276 for(int i=0;i<njoints;i++){
2277 if(!strcmp(chan[i].jname,jname)){
2278 //if so return the channel mapping and fvalue pointer
2279 jm = &chan[i];
2280 jm->values = &frame_values[kchan];
2281 break;
2282 }
2283 kchan += chan[i].nchan;
2284 }
2285 }
2286 }else if(node->_nodeType == NODE_HAnimMotionPlay){
2287 struct X3D_HAnimMotionPlay* HM = (struct X3D_HAnimMotionPlay*) node;
2288 struct X3D_HAnimMotionData *HD = (struct X3D_HAnimMotionData*) HM->data;
2289 if(HD->__loadstatus != LOADER_LOADED) return jm; //return NULL
2290 if(HM->enabled && HD){
2291 //see if we have the joint
2292 int njoints = (int)HD->_njoints;
2293 struct joint_frame_motion * chan = HD->_channels;
2294 float *frame_values = (float*)HM->_framevalues; //render_HAnimMotion should have run this frame to set the frame pointer
2295 if (!frame_values) return NULL; //but with multiple motions, and changing motion on the fly, sometimes it needs another frame
2296 int kchan = 0;
2297 char* kname = jname;
2298 if (HM->mapping.n) {
2299 // MotionPlay.mapping maps names from foreign skeleton to HAnim LOA names
2300 // (when .bvh mocap loads, the MotionDataFile also has a .mapping to
2301 // .. map from foreign animation rig to HAnim LOA
2302 // 2-step mapping reduces the number of mappings needed from n x m to n + m
2303 for (int k = 0; k < HM->mapping.n; k += 2) {
2304 char* sname = HM->mapping.p[k]->strptr;
2305 char* dname = HM->mapping.p[k + 1]->strptr;
2306 if (!strcmp(jname, sname)) {
2307 kname = dname;
2308 break;
2309 }
2310 }
2311 }
2312 for(int i=0;i<njoints;i++){
2313 if(!strcmp(chan[i].jname,kname)){
2314 //if so return the channel mapping and fvalue pointer
2315 //printf("%s ",jname);
2316 jm = &chan[i];
2317 jm->values = &frame_values[kchan];
2318 //if(!strcmp(jname,"humanoid_root")){
2319 // printf("humanoid_root vals=");
2320 // for(int m=0;m<chan[i].nchan;m++) printf("%f ",jm->values[m]);
2321 // printf("\n");
2322 //}
2323 break;
2324 }
2325 kchan += chan[i].nchan;
2326 }
2327 }
2328 }
2329 }
2330 return jm;
2331}
2332void update_displacerWeightFromMotion(struct X3D_Node* HMnode, char* jname, float *weight) {
2333 if (HMnode && (HMnode->_nodeType == NODE_HAnimMotion || HMnode->_nodeType == NODE_HAnimMotionPlay)) {
2334 //in theory adding a new channel type "WT" would allow mixing
2335 // of displacer weights and POS, ROT channels in same Motion data
2336 }
2337 else if (HMnode && (HMnode->_nodeType == NODE_HAnimMotionInterpolator)) {
2338 struct X3D_HAnimMotionInterpolator* HMO = (struct X3D_HAnimMotionInterpolator*)HMnode;
2339 struct Vector* jointnames = HMO->_jointnames;
2340
2341 if (jointnames && vectorSize(jointnames)) {
2342 int n = vectorSize(jointnames);
2343 for (int i = 0; i < n; i++) {
2344 char* hmoname = vector_get(char*, jointnames, i);
2345 //printf("(%s,%s)", jname, hmoname);
2346 if (!strcmp(jname, hmoname) && strcmp(jname, "IGNORE")) {
2347 //printf(" %s",jname);
2348 if (i < HMO->children.n) {
2349 struct X3D_Node* inode = HMO->children.p[i];
2350 if (inode->_nodeType == NODE_ScalarInterpolator) {
2351 struct X3D_ScalarInterpolator* onode = (struct X3D_ScalarInterpolator*)inode;
2352 //printf(" %d(%f %f %f %f)", i,onode->value_changed.c[0], onode->value_changed.c[1], onode->value_changed.c[2], onode->value_changed.c[3]);
2353 float wt = onode->value_changed;
2354 //printf("[%f,%f]", wt, onode->set_fraction);
2355 *weight = wt; //overwrite ?
2356 }
2357 }
2358 }
2359 }
2360 }
2361 }
2362}
2363void update_jointMatrixFromMotion(struct X3D_Node* HMnode, char* jname, double* jmatrix0) {
2364 struct X3D_HAnimMotion* HM = (struct X3D_HAnimMotion*)HMnode;
2365 if (HM && (HM->_nodeType == NODE_HAnimMotion || HM->_nodeType == NODE_HAnimMotionPlay)) {
2366 float weight = HM->transitionWeight;
2367 struct joint_frame_motion* jm = jointFrameMotion(HM, jname);
2368 int debug, debug2;
2369 debug = debug2 = FALSE;
2370 //if(!strcmp(jname,"l_shoulder")) debug = TRUE;
2371 if(jm){ // && strcmp(jname,"HumanoidRoot")){
2372 double mat1[16],jmatrix[16],xyz[3];
2373 if(debug) printf("in update_jointMatrix\n");
2374 if(debug) printmatrix(jmatrix0);
2375 matidentity4d(jmatrix);
2376 int igl = FALSE;
2377 if (igl) glPushMatrix();
2378 if (igl) glLoadIdentity();
2379 if(debug || debug2)
2380 printf("%s ",jname);
2381
2382 for(int ii=0;ii<jm->nchan;ii++){
2383 int i = ii; // jm->nchan - 1 - ii;
2384 float value = jm->values[i] * weight;
2385 if(debug)
2386 printf("%d %4.2f ",jm->ichan[i],value);
2387 // Q. what kind of angles are those
2388 // https://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm
2389 int ir = 0;
2390 matidentity4d(mat1);
2391 switch(jm->ichan[i]){
2392 case 1:
2393 matrixFromAxisAngle4d(mat1, -(double)value, 1.0, 0.0,0.0);
2394 if (igl) glRotatef(value*DEGREES_PER_RADIAN, 1, 0, 0);
2395 if (debug2) printf("xr %f ", value*DEGREES_PER_RADIAN);
2396 if(ir) matmultiplyAFFINE(jmatrix,jmatrix, mat1);
2397 else matmultiplyAFFINE(jmatrix, mat1, jmatrix);
2398 if(debug){
2399 printf("case 1 mat1\n");
2400 printmatrix(mat1);
2401 printf("case 1 jmatrix\n");
2402 printmatrix(jmatrix);
2403 }
2404 break;
2405 case 2:
2406 matrixFromAxisAngle4d(mat1, -(double)value, 0.0, 1.0, 0.0);
2407 if (igl) glRotatef(value * DEGREES_PER_RADIAN, 0, 1, 0);
2408 if (debug2) printf("yr %f ", value * DEGREES_PER_RADIAN);
2409 if (ir) matmultiplyAFFINE(jmatrix, jmatrix, mat1);
2410 else matmultiplyAFFINE(jmatrix, mat1, jmatrix);
2411 if(debug){
2412 printf("case 2 mat1\n");
2413 printmatrix(mat1);
2414 printf("case 2 jmatrix\n");
2415 printmatrix(jmatrix);
2416 }
2417 break;
2418 case 3:
2419 matrixFromAxisAngle4d(mat1, -(double)value, 0.0, 0.0, 1.0);
2420 if (igl) glRotatef(value * DEGREES_PER_RADIAN, 0, 0, 1);
2421 if (debug2) printf("zr %f ", value * DEGREES_PER_RADIAN);
2422 if (ir) matmultiplyAFFINE(jmatrix, jmatrix, mat1);
2423 else matmultiplyAFFINE(jmatrix, mat1, jmatrix);
2424 if(debug){
2425 printf("case 3 mat1\n");
2426 printmatrix(mat1);
2427 printf("case 3 jmatrix\n");
2428 printmatrix(jmatrix);
2429 }
2430 break;
2431 case 4:
2432 mattranslate4d(mat1,vecsetd(xyz,(double)value,0.0,0.0));
2433 if (igl) glTranslatef(value,0,0);
2434 if (ir) matmultiplyAFFINE(jmatrix, jmatrix, mat1);
2435 else matmultiplyAFFINE(jmatrix, mat1, jmatrix);
2436 if(debug){
2437 printf("case 4 mat1\n");
2438 printmatrix(mat1);
2439 printf("case 4 jmatrix\n");
2440 printmatrix(jmatrix);
2441 }
2442 break;
2443 case 5:
2444 mattranslate4d(mat1,vecsetd(xyz,0.0,(double)value,0.0));
2445 if (igl) glTranslatef(0, value, 0);
2446 if (ir) matmultiplyAFFINE(jmatrix, jmatrix, mat1);
2447 else matmultiplyAFFINE(jmatrix, mat1, jmatrix);
2448 if(debug){
2449 printf("case 5 mat1\n");
2450 printmatrix(mat1);
2451 printf("case 5 jmatrix\n");
2452 printmatrix(jmatrix);
2453 }
2454 break;
2455 case 6:
2456 mattranslate4d(mat1,vecsetd(xyz,0.0,0.0,(double)value));
2457 if (igl) glTranslatef(0, 0, value);
2458 if (ir) matmultiplyAFFINE(jmatrix, jmatrix, mat1);
2459 else matmultiplyAFFINE(jmatrix, mat1, jmatrix);
2460 if(debug){
2461 printf("case 6 mat1\n");
2462 printmatrix(mat1);
2463 printf("case 6 jmatrix\n");
2464 printmatrix(jmatrix);
2465 }
2466 break;
2467 default:
2468 if(debug) printf("OUCH DEFAULT\n");
2469 break;
2470 }
2471 }
2472 if (debug2)printf("\n");
2473 if(0) if (jm->level == 1) {
2474 double toYup[] = {1,0,0,0, 0,0,-1,0, 0,1,0,0, 0,0,0,1};
2475 matmultiplyAFFINE(jmatrix, toYup, jmatrix);
2476 }
2477 if(debug) if (!strcmp(jm->jname, "l_shoulder")) {
2478 double tmatrix[16];
2479 glGetDoublev(GL_MODELVIEW_MATRIX, tmatrix);
2480
2481 printf("\n");
2482 if (igl) {
2483 printf("opengl matrix multiply\n");
2484 printf("%lf %lf %lf %lf\n %lf %lf %lf %lf\n %lf %lf %lf %lf\n %lf %lf %lf %lf\n",
2485 tmatrix[0], tmatrix[1], tmatrix[2], tmatrix[3],
2486 tmatrix[4], tmatrix[5], tmatrix[6], tmatrix[7],
2487 tmatrix[8], tmatrix[9], tmatrix[10], tmatrix[11],
2488 tmatrix[12], tmatrix[13], tmatrix[14], tmatrix[15]);
2489 printf("\n");
2490 }
2491 printf("fw matrix multiply\n");
2492 printf("%lf %lf %lf %lf\n %lf %lf %lf %lf\n %lf %lf %lf %lf\n %lf %lf %lf %lf\n",
2493 jmatrix[0], jmatrix[1], jmatrix[2], jmatrix[3],
2494 jmatrix[4], jmatrix[5], jmatrix[6], jmatrix[7],
2495 jmatrix[8], jmatrix[9], jmatrix[10], jmatrix[11],
2496 jmatrix[12], jmatrix[13], jmatrix[14], jmatrix[15]);
2497 printf("\n");
2498 if (igl) memcpy(jmatrix, tmatrix, 16 * sizeof(double));
2499 printf("fw matrix multiply\n");
2500 printf("%lf %lf %lf %lf\n %lf %lf %lf %lf\n %lf %lf %lf %lf\n %lf %lf %lf %lf\n",
2501 jmatrix[0], jmatrix[1], jmatrix[2], jmatrix[3],
2502 jmatrix[4], jmatrix[5], jmatrix[6], jmatrix[7],
2503 jmatrix[8], jmatrix[9], jmatrix[10], jmatrix[11],
2504 jmatrix[12], jmatrix[13], jmatrix[14], jmatrix[15]);
2505 printf("\n");
2506
2507 }
2508 //matinverseAFFINE(mat1,jmatrix);
2509 matmultiplyAFFINE(jmatrix0,jmatrix,jmatrix0);
2510 if (igl) glPopMatrix();
2511 //if(debug)
2512 //printf("\n");
2513 }
2514 }
2515 else if (HM && (HM->_nodeType == NODE_HAnimMotionInterpolator)) {
2516 //printf("update from orientation ");
2518 struct Vector* jointnames = HMO->_jointnames;
2519 float weight = HMO->transitionWeight;
2520
2521 if(jointnames && vectorSize(jointnames)){
2522 double mat1[16];
2523 int n = vectorSize(jointnames);
2524 //printf("jointnames size %d ", n);
2525 for (int i = 0; i < n; i++) {
2526 char* hmoname = vector_get(char*, jointnames, i);
2527 //printf("(%s,%s)", jname, hmoname);
2528 if (!strcmp(jname, hmoname) && strcmp(jname,"IGNORE")) {
2529 //printf(" %s",jname);
2530 if (i < HMO->children.n) {
2531 matidentity4d(mat1);
2532
2533 struct X3D_Node* inode = HMO->children.p[i];
2534 if (inode->_nodeType == NODE_OrientationInterpolator) {
2535 struct X3D_OrientationInterpolator* onode = (struct X3D_OrientationInterpolator*)inode;
2536 //printf(" %d(%f %f %f %f)", i,onode->value_changed.c[0], onode->value_changed.c[1], onode->value_changed.c[2], onode->value_changed.c[3]);
2537 struct SFRotation* sfr = &onode->value_changed;
2538 matrixFromAxisAngle4d(mat1, -sfr->c[3]*weight,sfr->c[0],sfr->c[1], sfr->c[2]);
2539 matmultiplyAFFINE(jmatrix0, mat1, jmatrix0);
2540 }
2541 else if (inode->_nodeType == NODE_PositionInterpolator) {
2542 struct X3D_PositionInterpolator* pnode = (struct X3D_PositionInterpolator*)inode;
2543 struct SFVec3f* sft = &pnode->value_changed;
2544 float wvec[3];
2545 double dvec[3];
2546 vecscale3f(wvec, sft->c, weight);
2547 float2double(dvec, wvec, 3);
2548 //vecprint3fb("trans ", wvec, " ");
2549 matidentity4d(mat1);
2550 mattranslate4d(mat1, dvec);
2551 matmultiplyAFFINE(jmatrix0, mat1, jmatrix0);
2552 }
2553 }
2554 }
2555 }
2556 }
2557 }
2558
2559}
2560// <<<<<<<<< HAnimMotion ======================
2561
2562
2563//exprimental nodes not in specs:
2564// Motion = MotionPlay + (MotionData or MotionDataFile)
2565// we still have v4 Motion, but also a MotionPlay:Motion which
2566// allows MotionData part to be DEF/USEd aka shared among charagers in a scene.
2567// MotionPlay will have a frame index and timing info, so can stay 1:1 with HAnimHumanoid character
2568// MotionData can be DEF/USED by multiple MotionPlay nodes
2569// MotionDataFile - allows reading popular mocap/MotionCapture file formats .bvh, .c3d ...
2570void map_mocap_to_hanim_loa( struct joint_frame_motion *chan, int mjoint, int loa);
2571void bvh_set_mapping(char** mapping, int n);
2572void read_bvh_blob(char *blob, int ignorePosition, int yUp, int teePose,
2573 int flipZ, float armAngle, float legAngle, float scale,
2574 struct joint_frame_motion **chan, int *njoint, int *channel_count, float **values,
2575 float *bvh_frame_time, int *bvh_frame_count);
2576void read_bvh_blob_to_node(struct X3D_HAnimMotionDataFile * node, char *blob, int len){
2577 //Stack *bvh_nodes = NULL;
2578 float bvh_frame_time;
2579 int bvh_frame_count;
2580 //float global_scale = 1.0f;
2581 struct joint_frame_motion * chan = NULL;
2582 float *fvalues = NULL;
2583 int channel_count;
2584 int njoint;
2585 if (node->mapping.n) {
2586 char** mapp = malloc(2 * sizeof(char*) * node->mapping.n);
2587 for (int i = 0; i < node->mapping.n; i++)
2588 mapp[i] = node->mapping.p[i]->strptr;
2589 bvh_set_mapping(mapp, node->mapping.n / 2);
2590 }
2591 else {
2592 bvh_set_mapping(NULL, 0); //will use internal mapping
2593 }
2594 read_bvh_blob(blob, node->ignorePosition, node->yUp, node->teePose,
2595 node->flipZ, node->armAngle, node->legAngle, node->scale,
2596 &chan, &njoint, &channel_count, &fvalues, &bvh_frame_time,&bvh_frame_count);
2597 map_mocap_to_hanim_loa(chan,njoint,node->loa);
2598 node->frameCount = bvh_frame_count;
2599 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_HAnimMotionDataFile, frameCount));
2600 node->frameDuration = bvh_frame_time;
2601 node->_njoints = njoint;
2602 node->_channels = chan;
2603 node->_channelcount = channel_count;
2604 node->_fvalues = fvalues;
2605}
2606void process_mocap(resource_item_t *res){
2607 //a chance to do a bit of out-of-render-thread processing.
2608 openned_file_t *of;
2609 of = res->openned_files;
2610 if (!of) {
2611 /* error */
2612 return;
2613 }
2614
2615 char *blob = of->fileData;
2616 int len = of->fileDataSize;
2617
2618 struct X3D_HAnimMotionDataFile * node = (struct X3D_HAnimMotionDataFile *) res->whereToPlaceData;
2619
2620 printf("process mocap\n");
2621 read_bvh_blob_to_node(node,blob,len);
2622 res->complete = TRUE;
2623 res->status = ress_parsed;
2624}
2625void compile_HAnimMotionData(struct X3D_HAnimMotionData *node){
2626 //motion data
2627
2628 //parse jouint names
2629 struct Vector *jnames = parse_joint_names(X3D_NODE(node),node->joints->strptr);
2630 printf("\n");
2631 for(int i=0;i<jnames->n;i++)
2632 printf("%d %s\n",i,vector_get(char*,jnames,i));
2633 int njoints = jnames->n;
2634
2635 //parse channels
2636 struct joint_frame_motion *chan = malloc(njoints * sizeof(struct joint_frame_motion));
2637 int channelcount = parse_channels(node->channels->strptr,njoints,chan);
2638 //in theory channelcount is how many floats to advance in fvalues to get the next frame pointer.
2639
2640
2641 for(int i=0;i<njoints;i++){
2642 chan[i].jname = vector_get(char*,jnames,i);
2643 printf("joint %d nchan %d ",i, chan[i].nchan);
2644 for(int j=0;j<chan[i].nchan;j++){
2645 printf("%s ",channame_lookup(chan[i].ichan[j]));
2646 }
2647 printf("\n");
2648 }
2649 //parse float frame data
2650 //float *fvalues = parse_float_values(node->frameCount * channelcount, node->values->strptr);
2651 float* fvalues = node->values.p;
2652 if (channelcount)
2653 node->frameCount = node->values.n / channelcount;
2654 else
2655 node->frameCount = 0;
2656 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_HAnimMotionData, frameCount));
2657
2658 //convert degrees to radians
2659 for(int iframe=0;iframe<node->frameCount;iframe++){
2660 float *fv = &fvalues[iframe * channelcount];
2661 int kchan = 0;
2662 for(int j=0;j<njoints;j++){
2663 //printf("%s %d \n",vector_get(char*,jnames,j),chan[j].nchan);
2664 for(int k=0;k<chan[j].nchan;k++){
2665 if(chan[j].ichan[k] < 4)
2666 fv[kchan] *= RADIANS_PER_DEGREE; //PI / 180.0; //
2667 //printf("%d %5.2f ",chan[j].ichan[k],chan[j].ichan[k] < 4 ? fv[kchan]*180.0/PI : fv[kchan]);
2668 kchan++;
2669 }
2670 //printf("\n");
2671 }
2672 }
2673
2674 //we won't 'map' to parent during compile - we'll find the motion joint -if any- on the fly in HAnimJoint function(s)
2675
2676 //frame state
2677 //?? anything to do?
2678 node->_njoints = njoints;
2679 node->_channelcount = channelcount;
2680 node->_fvalues = fvalues;
2681 node->_channels = chan;
2682 node->__loadstatus = LOADER_LOADED;
2683 MARK_NODE_COMPILED
2684}
2685void render_HAnimMotionData(struct X3D_HAnimMotionData *node){
2686 COMPILE_IF_REQUIRED
2687}
2688
2689
2690//enum{
2691// LOADER_INITIAL_STATE=0,
2692// LOADER_REQUEST_RESOURCE,
2693// LOADER_FETCHING_RESOURCE,
2694// LOADER_PROCESSING,
2695// LOADER_LOADED,
2696// LOADER_COMPILED,
2697// LOADER_STABLE,
2698//};
2699void compile_HAnimMotionDataFile(struct X3D_HAnimMotionDataFile *node){
2700 resource_item_t *res;
2701 int retval = FALSE;
2702 switch (node->__loadstatus) {
2703 case LOADER_INITIAL_STATE: /* nothing happened yet */
2704
2705 if (node->url.n == 0) {
2706 node->__loadstatus = LOADER_STABLE; /* a "do-nothing" approach */
2707 } else {
2708 res = resource_create_multi(&(node->url));
2709 res->media_type = resm_mocap; //resm_fshader;
2710 node->__loadstatus = LOADER_REQUEST_RESOURCE;
2711 node->__loadResource = res;
2712 }
2713 break;
2714
2715 case LOADER_REQUEST_RESOURCE:
2716 res = node->__loadResource;
2717 resource_identify(node->_parentResource, res);
2718 /* printf ("load_Inline, we have type %s status %s\n",
2719 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
2720 res->actions = resa_download | resa_load; //not resa_parse which we do below
2721 resitem_enqueue(ml_new(res));
2722 //frontenditem_enqueue(ml_new(res));
2723 node->__loadstatus = LOADER_FETCHING_RESOURCE;
2724 break;
2725
2726 case LOADER_FETCHING_RESOURCE:
2727 res = node->__loadResource;
2728 /* printf ("load_Inline, we have type %s status %s\n",
2729 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
2730 // do we try the next url in the multi-url?
2731 if(res->complete){
2732 if (res->status == ress_loaded) {
2733 //determined during load process by resource_identify_type(): res->media_type = resm_vrml; //resm_unknown;
2734 if(1){
2735 //send it for out-of-display-thread-processing
2736 res->whereToPlaceData = X3D_NODE(node);
2737 //res->offsetFromWhereToPlaceData = 0;
2738 res->actions = resa_process;
2739 node->__loadstatus = LOADER_PROCESSING; // a "do-nothing" approach
2740 res->complete = FALSE;
2741 //send_resource_to_parser(res);
2742 //send_resource_to_parser_if_available(res);
2743 resitem_enqueue(ml_new(res));
2744 }else{
2745 //in-display-thread procesing
2746 process_mocap(res);
2747 node->__loadstatus = LOADER_LOADED; // a "do-nothing" approach
2748 res->complete = TRUE;
2749
2750 }
2751 } else if ((res->status == ress_failed) || (res->status == ress_invalid)) {
2752 //no hope left
2753 printf ("resource failed to load\n");
2754 node->__loadstatus = LOADER_STABLE; // a "do-nothing" approach
2755 }
2756 }
2757 break;
2758
2759 case LOADER_PROCESSING:
2760 res = node->__loadResource;
2761
2762 //printf ("inline parsing.... %s\n",resourceStatusToString(res->status));
2763 //printf ("res complete %d\n",res->complete);
2764 if(res->complete){
2765 if (res->status == ress_parsed) {
2766 node->__loadstatus = LOADER_LOADED;
2767 }else{
2768 node->__loadstatus = LOADER_STABLE;
2769 }
2770 }
2771
2772 break;
2773 case LOADER_STABLE:
2774 break;
2775 case LOADER_LOADED:
2776 case LOADER_COMPILED:
2777 retval = TRUE;
2778 }
2779 if(node->__loadstatus == LOADER_STABLE || node->__loadstatus == LOADER_LOADED)
2780 MARK_NODE_COMPILED
2781}
2782void render_HAnimMotionDataFile(struct X3D_HAnimMotionDataFile *node){
2783 COMPILE_IF_REQUIRED
2784}
2785
2786void compile_HAnimMotionClip(struct X3D_HAnimMotionClip *node){
2787 int is_file = node->url.n;
2788 if(is_file){
2789
2790 resource_item_t *res;
2791 int retval = FALSE;
2792 switch (node->__loadstatus) {
2793 case LOADER_INITIAL_STATE: /* nothing happened yet */
2794
2795 if (node->url.n == 0) {
2796 node->__loadstatus = LOADER_STABLE; /* a "do-nothing" approach */
2797 } else {
2798 res = resource_create_multi(&(node->url));
2799 res->media_type = resm_mocap; //resm_fshader;
2800 node->__loadstatus = LOADER_REQUEST_RESOURCE;
2801 node->__loadResource = res;
2802 }
2803 break;
2804
2805 case LOADER_REQUEST_RESOURCE:
2806 res = node->__loadResource;
2807 resource_identify(node->_parentResource, res);
2808 /* printf ("load_Inline, we have type %s status %s\n",
2809 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
2810 res->actions = resa_download | resa_load; //not resa_parse which we do below
2811 resitem_enqueue(ml_new(res));
2812 //frontenditem_enqueue(ml_new(res));
2813 node->__loadstatus = LOADER_FETCHING_RESOURCE;
2814 break;
2815
2816 case LOADER_FETCHING_RESOURCE:
2817 res = node->__loadResource;
2818 /* printf ("load_Inline, we have type %s status %s\n",
2819 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
2820 // do we try the next url in the multi-url?
2821 if(res->complete){
2822 if (res->status == ress_loaded) {
2823 //determined during load process by resource_identify_type(): res->media_type = resm_vrml; //resm_unknown;
2824 if(1){
2825 //send it for out-of-display-thread-processing
2826 res->whereToPlaceData = X3D_NODE(node);
2827 //res->offsetFromWhereToPlaceData = 0;
2828 res->actions = resa_process;
2829 node->__loadstatus = LOADER_PROCESSING; // a "do-nothing" approach
2830 res->complete = FALSE;
2831 //send_resource_to_parser(res);
2832 //send_resource_to_parser_if_available(res);
2833 resitem_enqueue(ml_new(res));
2834 }else{
2835 //in-display-thread procesing
2836 process_mocap(res);
2837 node->__loadstatus = LOADER_LOADED; // a "do-nothing" approach
2838 res->complete = TRUE;
2839
2840 }
2841 } else if ((res->status == ress_failed) || (res->status == ress_invalid)) {
2842 //no hope left
2843 printf ("resource failed to load\n");
2844 node->__loadstatus = LOADER_STABLE; // a "do-nothing" approach
2845 }
2846 }
2847 break;
2848
2849 case LOADER_PROCESSING:
2850 res = node->__loadResource;
2851
2852 //printf ("inline parsing.... %s\n",resourceStatusToString(res->status));
2853 //printf ("res complete %d\n",res->complete);
2854 if(res->complete){
2855 if (res->status == ress_parsed) {
2856 node->__loadstatus = LOADER_LOADED;
2857 }else{
2858 node->__loadstatus = LOADER_STABLE;
2859 }
2860 }
2861
2862 break;
2863 case LOADER_STABLE:
2864 break;
2865 case LOADER_LOADED:
2866 case LOADER_COMPILED:
2867 retval = TRUE;
2868 }
2869 if(node->__loadstatus == LOADER_STABLE || node->__loadstatus == LOADER_LOADED)
2870 MARK_NODE_COMPILED
2871
2872 }else{
2873 //field data
2874 //parse jouint names
2875 struct Vector *jnames = parse_joint_names(X3D_NODE(node),node->joints->strptr);
2876 printf("\n");
2877 for(int i=0;i<jnames->n;i++)
2878 printf("%d %s\n",i,vector_get(char*,jnames,i));
2879 int njoints = jnames->n;
2880
2881 //parse channels
2882 struct joint_frame_motion *chan = malloc(njoints * sizeof(struct joint_frame_motion));
2883 int channelcount = parse_channels(node->channels->strptr,njoints,chan);
2884 //in theory channelcount is how many floats to advance in fvalues to get the next frame pointer.
2885
2886
2887 for(int i=0;i<njoints;i++){
2888 chan[i].jname = vector_get(char*,jnames,i);
2889 printf("joint %d nchan %d ",i, chan[i].nchan);
2890 for(int j=0;j<chan[i].nchan;j++){
2891 printf("%s ",channame_lookup(chan[i].ichan[j]));
2892 }
2893 printf("\n");
2894 }
2895 //parse float frame data
2896 //float *fvalues = parse_float_values(node->frameCount * channelcount, node->values->strptr);
2897 float* fvalues = node->values.p;
2898 if (channelcount)
2899 node->frameCount = node->values.n / channelcount;
2900 else
2901 node->frameCount = 0;
2902 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_HAnimMotionClip, frameCount));
2903
2904 //convert degrees to radians
2905 for(int iframe=0;iframe<node->frameCount;iframe++){
2906 float *fv = &fvalues[iframe * channelcount];
2907 int kchan = 0;
2908 for(int j=0;j<njoints;j++){
2909 //printf("%s %d \n",vector_get(char*,jnames,j),chan[j].nchan);
2910 for(int k=0;k<chan[j].nchan;k++){
2911 if(chan[j].ichan[k] < 4)
2912 fv[kchan] *= RADIANS_PER_DEGREE; //PI / 180.0; //
2913 //printf("%d %5.2f ",chan[j].ichan[k],chan[j].ichan[k] < 4 ? fv[kchan]*180.0/PI : fv[kchan]);
2914 kchan++;
2915 }
2916 //printf("\n");
2917 }
2918 }
2919
2920 //we won't 'map' to parent during compile - we'll find the motion joint -if any- on the fly in HAnimJoint function(s)
2921
2922 //frame state
2923 //?? anything to do?
2924 node->_njoints = njoints;
2925 node->_channelcount = channelcount;
2926 node->_fvalues = fvalues;
2927 node->_channels = chan;
2928 node->__loadstatus = LOADER_LOADED;
2929 MARK_NODE_COMPILED
2930 }
2931
2932}
2933void render_HAnimMotionClip(struct X3D_HAnimMotionClip *node){
2934 COMPILE_IF_REQUIRED
2935}
2936
2937void compile_HAnimMotionPlay(struct X3D_HAnimMotionPlay *node){
2938
2939 struct X3D_HAnimMotionData *motiondata = (struct X3D_HAnimMotionData *)node->data;
2940
2941 if(motiondata){
2942 if(node->data->_nodeType == NODE_HAnimMotionData || node->data->_nodeType == NODE_HAnimMotionDataFile || node->data->_nodeType == NODE_HAnimMotionClip){
2943 render_node(X3D_NODE(node->data));
2944 int fileclip = node->data->_nodeType == NODE_HAnimMotionClip && ((struct X3D_HAnimMotionClip*)(node->data))->url.n > 0;
2945 if(node->data->_nodeType == NODE_HAnimMotionDataFile || fileclip){
2946 struct X3D_HAnimMotionDataFile * motiondatafile = (struct X3D_HAnimMotionDataFile*)node->data;
2947 //node->startFrame = motiondatafile->ignoreFirstFrame ? 1 : 0;
2948 //if(node->endFrame == 0) node->endFrame = motiondatafile->frameCount -1;
2949 if (motiondatafile->__loadstatus != LOADER_LOADED) return;
2950 MARK_EVENT(X3D_NODE(motiondata), offsetof(struct X3D_HAnimMotionData, frameCount));
2951 MARK_NODE_COMPILED
2952 }else{
2953 //node->startFrame = 0;
2954 //if(node->endFrame == 0) node->endFrame = motiondata->frameCount -1;
2955 MARK_EVENT(X3D_NODE(motiondata), offsetof(struct X3D_HAnimMotionData, frameCount));
2956 MARK_NODE_COMPILED
2957 }
2958 }
2959 }
2960}
2961void updateMotionPlayFromData(struct X3D_HAnimMotionPlay* play, struct X3D_HAnimMotionData* data) {
2962 if (data && data->_nodeType == NODE_HAnimMotionData || data->_nodeType == NODE_HAnimMotionDataFile || data->_nodeType == NODE_HAnimMotionClip) {
2963 render_node(X3D_NODE(data));
2964 if (data->__loadstatus != LOADER_LOADED) return;
2965 int fileclip = data->_nodeType == NODE_HAnimMotionClip && ((struct X3D_HAnimMotionClip*)(data))->url.n > 0;
2966 if (data->_nodeType == NODE_HAnimMotionDataFile || fileclip) {
2967 struct X3D_HAnimMotionDataFile* motiondatafile = (struct X3D_HAnimMotionDataFile*)data;
2968 play->startFrame = motiondatafile->ignoreFirstFrame ? 1 : 0;
2969 play->endFrame = motiondatafile->frameCount - 1;
2970 }
2971 else {
2972 //node->startFrame = 0;
2973 //if (play->endFrame == 0)
2974 play->startFrame = 0;
2975 play->endFrame = data->frameCount - 1;
2976 }
2977 }
2978}
2979void render_HAnimMotionPlay(struct X3D_HAnimMotionPlay *node){
2980 //main job: set the frame pointer for the current time, increment, enabled state
2981 COMPILE_IF_REQUIRED
2982 int index = 0;
2983 struct X3D_HAnimMotionData *motiondata = (struct X3D_HAnimMotionData *)node->data;
2984 if(motiondata && motiondata->_nodeType == NODE_HAnimMotionData || motiondata->_nodeType == NODE_HAnimMotionDataFile || motiondata->_nodeType == NODE_HAnimMotionClip ){
2985 render_node(X3D_NODE(motiondata));
2986 if(motiondata->__loadstatus != LOADER_LOADED) return;
2987 }
2988 updateMotionPlayFromData(node, motiondata);
2989
2990 float *fvalues = (float*)motiondata->_fvalues;
2991 int channelcount = (int)motiondata->_channelcount;
2992 float *frame_values;
2993 int isActive = FALSE;
2994
2995 int increment = node->frameIncrement;
2996// if(increment == 0) return; //the official way to pause
2997 index = node->frameIndex;
2998 int fcount = node->endFrame - node->startFrame + 1; // motiondata->frameCount;
2999 index = max(0,min(index,node->endFrame)); //iclamp
3000
3001 int starting = 0;
3002 int stopping = 0;
3003 isActive = node->enabled && ((node->loop && increment != 0) || (increment > 0 && index < node->endFrame) || (increment < 0 && index > 0) );
3004 if(node->enabled && !node->_lastenabled){
3005 starting = TRUE;
3006 node->_lastenabled = node->enabled;
3007 }else if(!node->enabled && node->_lastenabled){
3008 stopping = TRUE;
3009 node->_lastenabled = node->enabled;
3010 }
3011 if(starting){
3012 node->_startTime = TickTime();
3013 }
3014
3015
3016 if(node->next){
3017 index = index + 1;// increment;
3018 node->next = FALSE;
3019 } else if(node->previous){
3020 index = index - 1; // increment;
3021 node->previous = FALSE;
3022 } else if(node->enabled && increment){
3023 double dtime = TickTime() - node->_startTime;
3024 index = node->frameIncrement * (int)( dtime / motiondata->frameDuration);
3025 }
3026 int startingloop = 0;
3027 if(node->loop){
3028 int lindex = ((index - node->startFrame) % fcount) + node->startFrame;
3029 startingloop = lindex != index;
3030 index = lindex;
3031 }
3032 index = max(0,min(index,node->endFrame)); //iclamp
3033 if (increment) index = max(index, node->startFrame); //if playing, skip initial teePose
3034 if(starting && index == node->endFrame && increment > 0) index = node->startFrame;
3035 if(starting && index <= node->startFrame && increment < 0) index = node->endFrame;
3036 if(starting || startingloop ){
3037 node->cycleTime = TickTime();
3038 MARK_EVENT (X3D_NODE(node), offsetof(struct X3D_HAnimMotion, cycleTime));
3039 }
3040 if(isActive){
3041 node->elapsedTime = TickTime();
3042 MARK_EVENT (X3D_NODE(node), offsetof(struct X3D_HAnimMotion, elapsedTime));
3043 }
3044 int last_index = node->frameIndex;
3045 node->frameIndex = index;
3046 if(last_index != index)
3047 MARK_EVENT(X3D_NODE(node), offsetof( struct X3D_HAnimMotionPlay, frameIndex));
3048 frame_values = &fvalues[node->frameIndex * channelcount];
3049 node->_framevalues = frame_values; //frame pointer into big array of floats, good for current frame only
3050 COMPILE_IF_REQUIRED
3051}
3052
3053void compile_HAnimPermuter(struct X3D_HAnimPermuter* node){
3054 if (node->compute) {
3055 //generate random permutations of
3056 // HH HAnimHumanoid
3057 // HM HAnimMotion
3058 // keep Stand motion the same for all
3059 unsigned int permutation;
3060 int HHindex, HMindex, np;
3061 int HHn, HMn;
3062 HHn = node->humanoids.n;
3063 HMn = node->motions.n; //first one is walk same for every humanoid
3064 np = 0;
3065 FREE_IF_NZ(node->permutations.p);
3066 node->permutations.p = malloc((HHn*HMn+2) * sizeof(int));
3067 for (int i = 0; i < HHn; i++) {
3068 for (int j = 1; j < HMn; j++) {
3069 node->permutations.p[np] = i * 1000 + j;
3070 np++;
3071 }
3072 }
3073 node->permutations.n = np;
3074 }
3075 MARK_NODE_COMPILED
3076}
3077void render_HAnimPermuter(struct X3D_HAnimPermuter* node){
3078 COMPILE_IF_REQUIRED
3079}
3080void child_HAnimPermuter(struct X3D_HAnimPermuter* node){
3081 //here we do the permutation you choose in the ParticleSystem
3082 int permutation = node->permutations.p[node->index];
3083 int HHindex = permutation / 1000;
3084 int HMindex = permutation - (HHindex*1000);
3085 struct X3D_HAnimHumanoid* HH = (struct X3D_HAnimHumanoid*)node->humanoids.p[HHindex];
3086 if (HH->motions.n == 0) {
3087 HH->motions.n = 2;
3088 HH->motions.p = malloc(2 * sizeof(void*));
3089 }
3090 struct X3D_HAnimMotion* HM = (struct X3D_HAnimMotion*)node->motions.p[HMindex];
3091 if (node->_play.n == 0) {
3092 struct X3D_HAnimMotionPlay* HMP0, * HMP1;
3093 node->_play.p = malloc(2 * sizeof(void*));
3094 HMP0 = createNewX3DNode(NODE_HAnimMotionPlay); //for standing motion
3095 HMP1 = createNewX3DNode(NODE_HAnimMotionPlay); //for walking motions
3096 node->_play.p[0] = X3D_NODE(HMP0);
3097 node->_play.p[1] = X3D_NODE(HMP1);
3098
3099 node->_play.n = 2;
3100 //enabled='true' loop='true' frameIncrement='1' frameIndex='1'
3101 HMP0->enabled = TRUE;
3102 HMP1->enabled = TRUE;
3103 HMP0->loop = TRUE;
3104 HMP1->loop = TRUE;
3105 HMP0->frameIncrement = 1;
3106 HMP1->frameIncrement = 1;
3107 HMP0->frameIndex = 1;
3108 HMP1->frameIndex = 1;
3109 }
3110 if (HM->_nodeType == NODE_HAnimMotionData || HM->_nodeType == NODE_HAnimMotionDataFile) {
3111 //parent MotionData to MotionPlay
3112 struct X3D_HAnimMotionPlay* HMP = (struct X3D_HAnimMotionPlay*)node->_play.p[1];
3113 HMP->data = X3D_NODE(HM);
3114 HM = (struct X3D_HAnimMotion*)HMP;
3115 }
3116 HH->motions.p[1] = X3D_NODE(HM);
3117 //set HM-stand
3118 struct X3D_HAnimMotion* HMS = (struct X3D_HAnimMotion*)node->motions.p[0]; //assume stand is the first motion
3119 if (HMS->_nodeType == NODE_HAnimMotionData || HMS->_nodeType == NODE_HAnimMotionDataFile) {
3120 struct X3D_HAnimMotionPlay* HMP = (struct X3D_HAnimMotionPlay*)node->_play.p[0];
3121 HMP->data = X3D_NODE(HMS);
3122 HMS = (struct X3D_HAnimMotion*)HMP;
3123 }
3124 HH->motions.p[0] = X3D_NODE(HMS);
3125 node->humanoid = X3D_NODE(HH);
3126 //now draw
3127 //child_HAnimHumanoid(HH); // node->humanoid);
3128
3129}
3130void compile_HAnimMotionInterpolator(struct X3D_HAnimMotionInterpolator* node) {
3131 struct Vector* jnames = parse_joint_names(X3D_NODE(node), node->joints->strptr);
3132 node->_jointnames = jnames;
3133 //printf("_jointnames size %d", vectorSize(jnames));
3134 MARK_NODE_COMPILED
3135}
3136void render_HAnimMotionInterpolator(struct X3D_HAnimMotionInterpolator* node) {
3137 //we can expose something here -- a string of eulers
3138 // or let the update_joint function do the work
3139 COMPILE_IF_REQUIRED
3140
3141}
3142
3143void delete_HanimRep(void* _hanimrep) {
3144 //call during node deletion > unRegisterX3DAnyNode > delete_geomrep
3145 if (_hanimrep) {
3146 struct X3D_HanimRep* hanimrep = _hanimrep;
3147 if (hanimrep->JT) {
3148 deleteStack(struct JMATRIX*, hanimrep->JT);
3149 }
3150 FREE_IF_NZ(hanimrep->PVI);
3151 FREE_IF_NZ(hanimrep->PVW);
3152 if (hanimrep->bo_JT) glInvalidateBufferData(hanimrep->bo_JT);
3153 FREE_IF_NZ(_hanimrep);
3154 }
3155}