FreeWRL / FreeX3D 4.3.0
gltf_loader.c
1
2
3#include <config.h>
4#include <system.h>
5#include <display.h>
6#include <internal.h>
7
8#include "../vrml_parser/Structs.h"
9#include "../vrml_parser/CRoutes.h"
10#include "../main/headers.h"
11
12#include "../input/EAIHeaders.h"
13#include "../input/EAIHelpers.h"
14#include "../opengl/Frustum.h"
15#include "../opengl/OpenGL_Utils.h"
16#include "../opengl/Textures.h"
17
18#include "Component_Networking.h"
19#include "Children.h"
20#include "../scenegraph/RenderFuncs.h"
21#include "Polyrep.h"
22#include <libFreeWRL.h>
23#include <list.h>
24#include <io_http.h>
25#include "quaternion.h"
26
27/* GLTF
28https://github.com/jkuhlmann/cgltf
29- include 100 line recursive json parser (how does data come out?) etc.
30- first 600 lines of header is API. next 4000 lines is CGLTF_IMPLEMENTATION
31 see also:
32 https://github.com/michaliskambi/x3d-tests/wiki/Converting-glTF-to-X3D
33 https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
34 https://github.com/KhronosGroup/3DC-Certification/tree/main/models
35 https://github.com/KhronosGroup/3DC-Certification
36 -see Commerce Viewer https ://github.khronos.org/3DC-Sample-Viewer/
37-source code for it https ://github.com/KhronosGroup/glTF-Sample-Viewer can drag & drop glb
38State as of May 4, 2022: working not too bad:
39a) can load .glb and .gltf directly or via Inline
40b) the khronos samples render, except GreenChair wood texture not aligned like their
41 sample renderer, I tried flipping the textureTransform.translation.c[1] (y) and even scaling it by the
42 texture transform scale[1], but still not quite aligned.
43c) HAnim not done - I think Michalis has some comments on that, I have no samples
44*/
45#define CGLTF_IMPLEMENTATION 1
46#include "cgltf.h"
47
48
49// a list of loaded gltf file units, with one unit representing one .glb or one (.gltf,.bin)
50// June 22, 2020: in theory this list should be per-execution_context (Scene, Inline, ProtoBody)
51// as should texture unit array. For now, will be per freewrl main-scene-gglobal.
52typedef struct gltf_unit {
53 cgltf_data *data; //parsed to cgltf nodes
54 int bin_loaded; // spawned x3d nodes that need buffer.data check this to know when they can 'compile'
55 unsigned char *bin; //where to place data for .bin resource loader, not used for .glb
56 int bin_len;
57 unsigned char *blob;//.glb blob includes .bin and textures, .gltf blob only json text and possible inlined textures
58 int blob_len;
59 Stack *bin_file_list;
60} gltf_unit;
61
62typedef struct pgltf_loader{
63 Stack *gltf_units;
64}* ppgltf_loader;
65void *gltf_loader_constructor(){
66 void *v = MALLOCV(sizeof(struct pgltf_loader));
67 memset(v,0,sizeof(struct pgltf_loader));
68 return v;
69}
70void gltf_loader_init(struct tgltf_loader *t){
71 //public
72 //private
73 t->prv = gltf_loader_constructor();
74 {
75 ppgltf_loader p = (ppgltf_loader)t->prv;
76 p->gltf_units = newStack(struct gltf_unit*);
77 }
78}
79void gltf_loader_clear(struct tgltf_loader *t){
80 //public
81 //private
82 {
83 ppgltf_loader p = (ppgltf_loader)t->prv;
84 //june 22, 2020 not done: detailed cleanup
85 //cgltf_free(data);
86 deleteStack(struct gltf_unit*,p->gltf_units);
87 }
88}
89//ppgltf_loader p = (ppgltf_loader)gglobal()->gltf_loader.prv;
90
91
92struct name_node {
93char *name;
94int nodeclass;
95struct X3D_Node * node;
96};
97static Stack *defs = NULL;
98struct X3D_Node *USE_node(char *name, int nodeclass){
99 if(name){
100 if(!defs) defs = newStack(struct name_node);
101 struct name_node nn;
102 for(int i=0;i<defs->n;i++){
103 nn = vector_get(struct name_node,defs,i);
104 if(!strcmp(name,nn.name) && nodeclass == nn.nodeclass){
105 return nn.node;
106 }
107 }
108 }
109 return NULL;
110}
111struct X3D_Node *DEF_node(struct X3D_Node *ectx, char *name, int nodetype){
112 if(!defs) defs = newStack(struct name_node);
113 struct name_node nn;
114 struct X3D_Node *node = createNewX3DNode(nodetype);
115 add_node_to_broto_context(X3D_PROTO(ectx),X3D_NODE(node));
116 if(name){
117 nn.name = name;
118 nn.nodeclass = getSAI_X3DNodeType(nodetype);
119 nn.node = node;
120 stack_push(struct name_node,defs,nn);
121 }
122
123return node;
124}
125
126
127void* set_MeshRep(void* _meshrep) {
128 struct X3D_MeshRep* meshrep = NULL;
129 //to be called from compile_BufferGeometry
130 if (!_meshrep) {
131 _meshrep = MALLOC(struct X3D_MeshRep*, sizeof(struct X3D_MeshRep));
132 memset(_meshrep, 0, sizeof(struct X3D_MeshRep));
133 }
134 meshrep = (struct X3D_MeshRep*)_meshrep;
135 meshrep->itype = 3; //0 meshrep 1 linerep 2 polyrep 3 meshrep
136 meshrep->mode = 4; //0 Meshs 1-3 lines 4-6 mesh: 4 TRIANGLES 5 TRIANGLE_STRIP 6 TRIANGLE_FAN
137 //buffer(buffersize) - unlike PointRep for x3d/x3dv nodes, PointSet 1:1 PointRep 1:1 geomBuffer
138 // for gltf we assume BufferGeometry 1:1 MeshRep m:1 geomBuffer
139 //npoints,attrib[]
140 //nindex,index
141 //set_geomBuffer(Meshrep->buffer);
142 return meshrep;
143}
144#include "Component_Shape.h"
145void saveArraysForGPU(int mode, int first, int count);
146void reallyDrawOnce();
147void render_MeshRep(void* _meshrep) {
148 //like render_PointRep
149 s_shader_capabilities_t* me = NULL;
150
151 struct X3D_MeshRep* meshrep = (struct X3D_MeshRep*)_meshrep;
152 struct geomBuffer* gb = meshrep->buffer;
153 if (!gb || gb->VBO < 1) return;
154 //old-stile GL_POINTS rendering - opengl generates point triangles in geometry shader automatically
155
156 glBindBuffer(GL_ARRAY_BUFFER, gb->VBO);
157 struct bufAccess* ba = &meshrep->attrib[0];
158 FW_GL_VERTEX_POINTER(ba->dataSize, ba->dataType, ba->byteStride, (GLfloat*)BUFFER_OFFSET(ba->byteOffset)); //dataSize, dataType, stride, pointer
159 //sendAttribToGPU(FW_VERTEX_POINTER_TYPE, dataSize, dataType, GL_FALSE, stride, pointer, 0, __FILE__, __LINE__);
160
161 // do we have colours?
162 ba = &meshrep->attrib[1];
163 if (ba->in_use) {
164 FW_GL_COLOR_POINTER(ba->dataSize, ba->dataType, ba->byteStride, (GLfloat*)BUFFER_OFFSET(ba->byteOffset)); //dataSize, dataType, stride, pointer
165 }
166
167 // do we have fogcoord?
168 ba = &meshrep->attrib[2];
169 if (ba->in_use) {
170 FW_GL_FOG_POINTER(ba->dataType, ba->byteStride, (GLfloat*)BUFFER_OFFSET(ba->byteOffset)); //dataType, stride, pointer
171 }
172 // do we have normals?
173 ba = &meshrep->attrib[3];
174 if (ba->in_use) {
175 FW_GL_NORMAL_POINTER(ba->dataType, ba->byteStride, (GLfloat*)BUFFER_OFFSET(ba->byteOffset));
176 }
177
178 // do we have UV?
179 {
180 //see also textureCoord_send
181 int kuv = 0;
182 for (int j = 0; j < 4; j++) {
183 ba = &meshrep->attrib[4 + j];
184
185 if (ba->in_use) {
186 kuv++;
187 FW_GL_TEXCOORD_POINTER(ba->dataSize, ba->dataType, ba->byteStride, (GLfloat*)BUFFER_OFFSET(ba->byteOffset), j); //dataSize, dataType, stride, pointer, texID
188 }
189 }
190 me = getAppearanceProperties()->currentShaderProperties;
191 glUniform1i(me->nTexCoordChannels, kuv); //PBR: send all you got, and say how many (channels)
192 glUniform1i(me->flipuv, meshrep->flipuv);
193 }
194
195 // do we have indexes?
196 ba = &meshrep->index;
197 // don't send indexes as arrays - wait till draw command and pass as parameter
198 //where do normals-per-face live?
199 if (ba->in_use) {
200 char* indices = meshrep->buffer->address + ba->byteOffset;
201 //sendElementsToGPU(GL_TRIANGLES, ntri * 3, uindexs); //WORKS
202 //glDrawElements( GL_TRIANGLES, ntri*3, GL_UNSIGNED_INT, indexs); //WORKS
203 saveElementsForGPU0(GL_TRIANGLES, meshrep->nindex, ba->dataType, indices);
204 }
205 else {
206 saveArraysForGPU(GL_TRIANGLES, 0, meshrep->ncoord);
207 //sendArraysToGPU(GL_TRIANGLES, 0, meshrep->ncoord);
208 }
209 reallyDrawOnce();
210
211
212 //printf for debugging accessors.
213 if (0) {
214 char* paddress;
215 for (int i = 0; i < meshrep->ncoord; i++) {
216 printf("%d [", i);
217 for (int k = 0; k < 8; k++) {
218 ba = &meshrep->attrib[k]; //point
219 if (ba->in_use) {
220 paddress = get_Attribi(ba, gb, i);;
221 float* ai = (float*)paddress;
222 printf(" [");
223 for (int j = 0; j < ba->dataSize; j++)
224 printf("%f ", ai[j]);
225 printf("]");
226 }
227 }
228 printf("\n");
229 }
230 printf("");
231 }
232}
233void rendray_MeshRep(void* _meshrep) {
234 struct X3D_MeshRep* mr = (struct X3D_MeshRep*)_meshrep;
235 if (!mr) return;
236 if (!mr->ncoord) return;
237
238 struct geomBuffer* gb = mr->buffer;
239 if (!gb || gb->loaded < 2) return;
240
241 //this doesn't work with large pick rays for geo size scenes
242 //struct X3D_Virt *virt;
243 struct X3D_Node* genericNodePtr;
244 int pt;
245 float point[3][3];
246 int cindex[3];
247 struct point_XYZ v1, v2, v3;
248 float pt1, pt2, pt3;
249 struct point_XYZ hitpoint;
250 float tmp1, tmp2;
251 float v1len, v2len, v3len;
252 float v12pt;
253 struct point_XYZ t_r1, t_r2;
254 struct bufAccess* bai, * ba;
255 char* paddress;
256
257 get_current_ray(&t_r1, &t_r2);
258 bai = &mr->index;
259 ba = &mr->attrib[0];
260
261 int ntri = mr->ncoord / 3;
262 if (mr->index.in_use) {
263 bai = &mr->index;
264 ntri = mr->nindex / 3;
265 for (int i = 0, j=0; i < ntri; i++) {
266 if (mr->index.in_use) {
267 for (int k = 0; k < 3; k++) {
268 paddress = get_Attribi(bai, gb, i * 3 + k);
269 int ic;
270 if (bai->dataType == GL_UNSIGNED_BYTE) {
271 unsigned char* ip = (unsigned char*)paddress;
272 ic = (int)(*ip);
273 }
274 else if (bai->dataType == GL_UNSIGNED_SHORT) {
275 unsigned short* ip = (unsigned short*)paddress;
276 ic = (int)(*ip);
277
278 }else if (bai->dataType == GL_SHORT) {
279 short* ip = (short*)paddress;
280 ic = (int)(*ip);
281 }
282 else { //LONG
283 int* ip = (int*)paddress;
284 ic = (int)(*ip);
285 }
286 cindex[k] = ic;
287 }
288 }
289 else {
290 for (int k = 0; k < 3; k++)
291 cindex[k] = i * 3 + k;
292
293 }
294 for (int k = 0; k < 3; k++) {
295 paddress = get_Attribi(ba, gb, cindex[k]);
296 veccopy3f(point[k], (float*)paddress);
297 }
298 //intersect ray with triangle
299
300 /*
301 printf ("have points (%f %f %f) (%f %f %f) (%f %f %f)\n",
302 point[0][0],point[0][1],point[0][2],
303 point[1][0],point[1][1],point[1][2],
304 point[2][0],point[2][1],point[2][2]);
305 */
306
307 /* First we need to project our point to the surface */
308 /* Poss. 1: */
309 /* Solve s1xs2 dot ((1-r)r1 + r r2 - pt0) == 0 */
310 /* I.e. calculate s1xs2 and ... */
311 v1.x = point[1][0] - point[0][0];
312 v1.y = point[1][1] - point[0][1];
313 v1.z = point[1][2] - point[0][2];
314 v2.x = point[2][0] - point[0][0];
315 v2.y = point[2][1] - point[0][1];
316 v2.z = point[2][2] - point[0][2];
317 v1len = (float)sqrt(VECSQ(v1)); VECSCALE(v1, 1 / v1len);
318 v2len = (float)sqrt(VECSQ(v2)); VECSCALE(v2, 1 / v2len);
319 v12pt = (float)VECPT(v1, v2);
320
321 /* this will get around a divide by zero further on JAS */
322 if (fabs(v12pt - 1.0) < 0.00001) continue;
323
324 /* if we have a degenerate triangle, we can't compute a normal, so skip */
325 if ((fabs(v1len) > 0.00001) && (fabs(v2len) > 0.00001)) {
326
327 /* v3 is our normal to the surface */
328 VECCP(v1, v2, v3);
329 v3len = (float)sqrt(VECSQ(v3)); VECSCALE(v3, 1 / v3len);
330 pt1 = (float)VECPT(t_r1, v3);
331 pt2 = (float)VECPT(t_r2, v3);
332 pt3 = (float)(v3.x * point[0][0] + v3.y * point[0][1] + v3.z * point[0][2]);
333 /* Now we have (1-r)pt1 + r pt2 - pt3 = 0
334 * r * (pt1 - pt2) = pt1 - pt3
335 */
336 tmp1 = pt1 - pt2;
337 if (!APPROX(tmp1, 0)) {
338 float ra, rb;
339 float k, l;
340 struct point_XYZ p0h;
341
342 tmp2 = (float)((pt1 - pt3) / (pt1 - pt2));
343 hitpoint.x = MRATX(tmp2);
344 hitpoint.y = MRATY(tmp2);
345 hitpoint.z = MRATZ(tmp2);
346 /* Now we want to see if we are in the triangle */
347 /* Projections to the two triangle sides */
348 p0h.x = hitpoint.x - point[0][0];
349 p0h.y = hitpoint.y - point[0][1];
350 p0h.z = hitpoint.z - point[0][2];
351 ra = (float)VECPT(v1, p0h);
352 if (ra < 0.0f) { continue; }
353 rb = (float)VECPT(v2, p0h);
354 if (rb < 0.0f) { continue; }
355 /* Now, the condition for the point to
356 * be inside
357 * (ka + lb = p)
358 * (k + l b.a = p.a)
359 * (k b.a + l = p.b)
360 * (k - (b.a)**2 k = p.a - (b.a)*p.b)
361 * k = (p.a - (b.a)*(p.b)) / (1-(b.a)**2)
362 */
363 k = (ra - v12pt * rb) / (1 - v12pt * v12pt);
364 l = (rb - v12pt * ra) / (1 - v12pt * v12pt);
365 k /= v1len; l /= v2len;
366 if (k + l > 1 || k < 0 || l < 0) {
367 continue;
368 }
369 rayhit(((float)(tmp2)),
370 ((float)(hitpoint.x)),
371 ((float)(hitpoint.y)),
372 ((float)(hitpoint.z)),
373 ((float)(v3.x)),
374 ((float)(v3.y)),
375 ((float)(v3.z)),
376 ((float)-1), ((float)-1), "polyrep");
377 }
378 /*
379 } else {
380 printf ("render_ray_polyrep, skipping degenerate triangle\n");
381 */
382 }
383 }
384 }
385}
386void delete_MeshRep(void* meshrep) {
387 //like delete_PointRep
388 struct X3D_MeshRep* mr;
389 mr = (struct X3D_MeshRep*)meshrep;
390 subtract_geomBufferUser(mr->buffer);
391 FREE_IF_NZ(mr);
392}
393struct geomBuffer* find_or_create_buffer_add_user(void* ectx, const cgltf_buffer_view* bv) {
394 struct geomBuffer* buffer = find_buffer_in_broto_context_from_cgltf_buffer(ectx, bv->buffer);
395 if (!buffer) {
396 //first use of buffer, allocate
397 buffer = add_geomBuffer0(ectx, bv->buffer->size, 1);
398 buffer->cgltf_buffer = (char*)bv->buffer;
399 if (bv->buffer->data) {
400 memcpy(buffer->address, bv->buffer->data, bv->buffer->size); //deep copy, do we need deep?
401 buffer->loaded = 1;
402 buffer->cgltf_buffer = (char*)bv->buffer; //simpler address copy
403 }
404 }
405 else {
406 add_geomBufferUser(buffer);
407 }
408 return buffer;
409}
410
411struct X3D_Node *x3dtexture_from_cgltf_texture(struct X3D_Node *ectx, cgltf_texture* texture ){
412 char* iname = texture->image->name;
413 struct X3D_Node* image = USE_node(iname, X3DTextureNode);
414 if (!image) {
415 if (texture->image->buffer_view) { //->buffer->data){
416 image = DEF_node(ectx, iname, NODE_BufferTexture);
417 struct X3D_TextureRep* tr = set_TextureRep(NULL);
418 tr->buffer = find_or_create_buffer_add_user(ectx, texture->image->buffer_view);
419 tr->byteOffset = texture->image->buffer_view->offset;
420 tr->byteSize = texture->image->buffer_view->size;
421 image->_intern = (void*)tr;
422 }
423 else {
424 char* iuri = texture->image->uri;
425 image = DEF_node(ectx, iname, NODE_ImageTexture);
426 struct X3D_ImageTexture* it = (struct X3D_ImageTexture*)image;
427 it->url.p = malloc(sizeof(void*));
428 it->url.p[0] = newASCIIString(iuri);
429 it->url.n = 1;
430 }
431 }
432 return image;
433}
434
435struct X3D_TextureTransform* add_texture_transform(cgltf_texture_view ctexture, int ntextrans) {
436 char scratch[20];
437 struct X3D_TextureTransform* ttrans = createNewX3DNode(NODE_TextureTransform);
438 veccopy2f(ttrans->translation.c, ctexture.transform.offset);
439 ttrans->translation.c[1] = (1.0f - ttrans->translation.c[1])* ctexture.transform.scale[1]; //flip yoffset ? closer but not perfect
440 ttrans->rotation = ctexture.transform.rotation;
441 veccopy2f(ttrans->scale.c, ctexture.transform.scale);
442 sprintf(scratch, "%d", ntextrans);
443 ttrans->mapping = newASCIIString(scratch);
444 return ttrans;
445}
446void save_texture_transforms(struct X3D_Node* appearance, int ntextrans, struct X3D_TextureTransform** textrans) {
447 struct X3D_Appearance* appear = X3D_APPEARANCE(appearance);
448 if (ntextrans > 1) {
449 struct X3D_MultiTextureTransform* mtrans = createNewX3DNode(NODE_MultiTextureTransform);
450 mtrans->textureTransform.p = malloc(ntextrans * sizeof(struct X3D_MultiTextureTransform*));
451 mtrans->textureTransform.n = ntextrans;
452 for (int k = 0; k < ntextrans; k++)
453 mtrans->textureTransform.p[k] = X3D_NODE(textrans[k]);
454 appear->textureTransform = X3D_NODE(mtrans);
455 }
456 else {
457 appear->textureTransform = X3D_NODE(textrans[0]);
458 }
459}
460struct Uni_String* set_mat_mapping(cgltf_texture_view ctexture, int itextrans) {
461 char scratch[20], ctemp[10];
462 //sprintf(scratch, "%d%d", itextrans, ctexture.texcoord);
463 scratch[0] = '\0';
464 if (ctexture.texcoord > -1) {
465 sprintf(ctemp, "C%1d", ctexture.texcoord);
466 strcat(scratch, ctemp);
467 }
468 if (ctexture.has_transform) {
469 sprintf(ctemp, "T%1d", itextrans); //just refer to the textrans by name, and take the default texcoord
470 strcat(scratch, ctemp);
471 }
472 return newASCIIString(scratch);
473}
474
475int parse_gltf_node(struct X3D_Node *ectx, struct X3D_Node **spot, cgltf_data * data, cgltf_node *node, gltf_unit *unit){
476// june 22, 2020 not done: skinned / rigged animated charactors, points, lines and various things noted below.
477// generally we got glb and gltf+bin to load and render a bit - a proof of concept.
478// biggest thing left: inline and in-bin textures - do we need a BufferTexture node (to bypass freewrl spaghetti code)?
479 //content part
480 int show = FALSE;
481 struct Vector vector;
482 struct Vector* pp = &vector;
483 memset(pp, 0, sizeof(struct Vector));
484
485 if(node->camera){
486 //june 22, 2020 not done: add viewpoint here
487 struct X3D_Node* viewpoint = NULL;
488 cgltf_camera* camera = node->camera;
489 viewpoint = (struct X3D_Node*)USE_node(camera->name, X3DBindableNode);
490 if (!viewpoint) {
491 if (camera->type == cgltf_camera_type_perspective) {
492 struct X3D_Viewpoint* vp = (struct X3D_Viewpoint*)DEF_node(ectx, camera->name, NODE_Viewpoint);
493 cgltf_camera_perspective perspective = camera->data.perspective;
494 vp->fieldOfView = perspective.yfov;
495 vp->aspectRatio = perspective.aspect_ratio > 0.0f ? perspective.aspect_ratio : .75;
496 vp->farClippingPlane = perspective.zfar;
497 vp->nearClippingPlane = perspective.znear;
498 vp->description = newASCIIString(camera->name);
499 vecset3f(vp->position.c, 0.0f, 0.0f, 0.0f); //let the transform position (default is 0 0 10)
500 //vp->navigationInfo = createNewX3DNode(NODE_NavigationInfo);
501 viewpoint = X3D_NODE(vp);
502 //printf("vp desc %s", vp->description->strptr);
503 }
504 else if (camera->type == cgltf_camera_type_orthographic) {
505 struct X3D_OrthoViewpoint* vp = (struct X3D_OrthoViewpoint*)DEF_node(ectx, camera->name, NODE_OrthoViewpoint);
506 cgltf_camera_orthographic ortho = camera->data.orthographic;
507 vp->fieldOfView.p[0] *= ortho.xmag;
508 vp->fieldOfView.p[1] *= ortho.ymag;
509 vp->fieldOfView.p[2] *= ortho.xmag;
510 vp->fieldOfView.p[3] *= ortho.ymag;
511 vp->farClippingPlane = ortho.zfar;
512 vp->nearClippingPlane = ortho.znear;
513 vp->description = newASCIIString(camera->name);
514 vecset3f(vp->position.c, 0.0f, 0.0f, 0.0f); //let the transform position (default is 0 0 10)
515 //vp->navigationInfo = createNewX3DNode(NODE_NavigationInfo);
516 viewpoint = X3D_NODE(vp);
517 //printf("vp desc %s", vp->description->strptr);
518 //printf("orth");
519 }
520 }
521 if (viewpoint) {
522 vector_pushBack(struct X3D_Node*, pp, viewpoint);
523 //printf("adding viewpoint\n");
524 }
525
526 }
527 if (node->light) {
528 //m++;
529 //p = realloc(p,m*sizeof(struct X3D_Node*));
530 // june 22, 2020 not done: add punctual (directional, point, spot) light here
531 // - not done and not supported yet EnvironmentLight
532 //cgltf_light_type_invalid, 0
533 //cgltf_light_type_directional, 1
534 //cgltf_light_type_point, 2
535 //cgltf_light_type_spot, 3
536
537 struct X3D_Node* light = NULL;
538 cgltf_light* plight = node->light;
539 light = (struct X3D_Node*)USE_node(plight->name, X3DLightNode);
540 if (!light) {
541 if (node->light->type > cgltf_light_type_invalid) {
542 //cgltf_light
543 //char* name;
544 //cgltf_float color[3];
545 //cgltf_float intensity;
546 //cgltf_light_type type;
547 //cgltf_float range;
548 //cgltf_float spot_inner_cone_angle;
549 //cgltf_float spot_outer_cone_angle;
550
551 switch (node->light->type) {
552 case cgltf_light_type_directional:
553 {
554 struct X3D_DirectionalLight* dl = (struct X3D_DirectionalLight*)DEF_node(ectx, plight->name, NODE_DirectionalLight);
555 veccopy3f(dl->color.c, plight->color);
556 dl->intensity = plight->intensity;
557 light = X3D_NODE(dl);
558 }
559 break;
560 case cgltf_light_type_point:
561 {
562 struct X3D_PointLight* pl = (struct X3D_PointLight*)DEF_node(ectx, plight->name, NODE_PointLight);
563 veccopy3f(pl->color.c, plight->color);
564 pl->intensity = plight->intensity;
565 pl->radius = plight->range;
566 light = X3D_NODE(pl);
567 }
568 break;
569 case cgltf_light_type_spot:
570 {
571 struct X3D_SpotLight* sl = (struct X3D_SpotLight*)DEF_node(ectx, plight->name, NODE_SpotLight);
572 veccopy3f(sl->color.c, plight->color);
573 sl->intensity = plight->intensity;
574 sl->radius = plight->range;
575 sl->beamWidth = plight->spot_inner_cone_angle;
576 sl->cutOffAngle = plight->spot_outer_cone_angle;
577 light = X3D_NODE(sl);
578 }
579
580 default:
581 break;
582 }
583 }
584 if (light) {
585 vector_pushBack(struct X3D_Node*, pp, light);
586 //printf("adding light\n");
587 }
588 }
589 }
590 if(node->mesh){
591 //gltf mesh is like our shape: it refers to material and to geometry/accessor
592 //we unconditionally add a Shape node, even if appearance and geometry are null
593 //struct X3D_Shape *sn = (struct X3D_Shape*) USE_node(node->mesh->name,X3DBoundedObject);
594 struct X3D_Group* gr = (struct X3D_Group*)USE_node(node->mesh->name, X3DGroupingNode);
595 if(!gr){
596 gr = (struct X3D_Group*) DEF_node(ectx,node->mesh->name,NODE_Group);
597 int do_mapping = FALSE;
598
599 for(int j=0;j<node->mesh->primitives_count;j++){
600 cgltf_primitive *prim = &node->mesh->primitives[j];
601 struct X3D_Shape* sn = (struct X3D_Shape*)DEF_node(ectx, NULL, NODE_Shape);
602 struct X3D_TextureTransform* textrans[4];
603 int ntextrans;
604 char scratch[20];
605
606 if(prim->material){
607
608 //typedef struct cgltf_material
609 //{
610 // char* name;
611 // cgltf_bool has_pbr_metallic_roughness;
612 // cgltf_bool has_pbr_specular_glossiness;
613 // cgltf_bool has_clearcoat;
614 // cgltf_pbr_metallic_roughness pbr_metallic_roughness;
615 // cgltf_pbr_specular_glossiness pbr_specular_glossiness;
616 // cgltf_clearcoat clearcoat;
617 // cgltf_texture_view normal_texture;
618 // cgltf_texture_view occlusion_texture;
619 // cgltf_texture_view emissive_texture;
620 // cgltf_float emissive_factor[3];
621 // cgltf_alpha_mode alpha_mode;
622 // cgltf_float alpha_cutoff;
623 // cgltf_bool double_sided;
624 // cgltf_bool unlit;
625 // cgltf_extras extras;
626 //} cgltf_material;
627 // june 22, 2020: not done: normal texture (supported by freewrl), occlusion texture (not supported)
628 // - and see below for material-type=specific not-dones.
629 if(prim->material->unlit){
630 int mtype = NODE_UnlitMaterial;
631 struct X3D_UnlitMaterial* mat = (struct X3D_UnlitMaterial*) USE_node(prim->material->name,X3DMaterialNode);
632 ntextrans = 0;
633 if(!mat){
634 mat = (struct X3D_UnlitMaterial*) DEF_node(ectx,prim->material->name,mtype);
635 veccopy3f(mat->emissiveColor.c,prim->material->emissive_factor);
636 //mat->emissiveTextureChannel
637 if (prim->material->emissive_texture.texture) {
638 if (prim->material->emissive_texture.texture) {
639 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, prim->material->emissive_texture.texture);
640
641 //char* iname = prim->material->emissive_texture.texture->image->name;
642 //struct X3D_Node* image = USE_node(iname, X3DTextureNode);
643 //if (!image) {
644 // if (prim->material->emissive_texture.texture->image->buffer_view) { //->buffer->data){
645 // if (show) printf("image loaded for us - in theory a PixelTexture\n");
646 // image = DEF_node(ectx, iname, NODE_BufferTexture);
647 // struct X3D_TextureRep* tr = set_TextureRep(NULL);
648 // tr->buffer = find_or_create_buffer_add_user(ectx, prim->material->emissive_texture.texture->image->buffer_view);
649 // image->_intern = (void*)tr;
650 // }
651 // else {
652 // char* iuri = prim->material->emissive_texture.texture->image->uri;
653 // image = DEF_node(ectx, iname, NODE_ImageTexture);
654 // struct X3D_ImageTexture* it = (struct X3D_ImageTexture*)image;
655 // it->url.p = malloc(sizeof(void*));
656 // it->url.p[0] = newASCIIString(iuri);
657 // it->url.n = 1;
658 // }
659 //}
660 mat->emissiveTexture = image;
661 mat->emissiveTextureMapping = set_mat_mapping(prim->material->emissive_texture, ntextrans);
662 if (prim->material->emissive_texture.has_transform) {
663 textrans[ntextrans++] = add_texture_transform(prim->material->emissive_texture, ntextrans);
664 }
665 }
666 }
667 }
668 sn->appearance = createNewX3DNode(NODE_Appearance);
669 if (ntextrans > 0) {
670 save_texture_transforms(sn->appearance, ntextrans, textrans);
671 }
672 X3D_APPEARANCE(sn->appearance)->material = X3D_NODE(mat);
673 }else if(prim->material->has_pbr_metallic_roughness){
674 int mtype = NODE_PhysicalMaterial;
675 struct X3D_PhysicalMaterial* mat = (struct X3D_PhysicalMaterial*) USE_node(prim->material->name,X3DMaterialNode);
676 ntextrans = 0;
677 if(!mat){
678 //typedef struct cgltf_pbr_metallic_roughness
679 //{
680 // cgltf_texture_view base_color_texture;
681 // cgltf_texture_view metallic_roughness_texture;
682 //
683 // cgltf_float base_color_factor[4];
684 // cgltf_float metallic_factor;
685 // cgltf_float roughness_factor;
686 //
687 // cgltf_extras extras;
688 //} cgltf_pbr_metallic_roughness;
689 // june 22, 2020 done: url loaded texture
690 // - not done: inline (base64 for gltf) / in-bin textures (for .glb)
691 cgltf_pbr_metallic_roughness *pbr = &prim->material->pbr_metallic_roughness;
692 mat = (struct X3D_PhysicalMaterial*) DEF_node(ectx,prim->material->name,mtype);
693 veccopy3f(mat->emissiveColor.c,prim->material->emissive_factor);
694 veccopy3f(mat->baseColor.c,pbr->base_color_factor);
695 mat->transparency = 1.0f - pbr->base_color_factor[3];
696 mat->metallic = pbr->metallic_factor;
697 mat->roughness = pbr->roughness_factor;
698 if(pbr->base_color_texture.texture ){
699 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, pbr->base_color_texture.texture);
700 mat->baseTexture = image;
701 //if (do_mapping) mat->baseTextureMapping = newASCIIString("one");
702 mat->baseTextureMapping = set_mat_mapping(pbr->base_color_texture, ntextrans);
703 if (pbr->base_color_texture.has_transform) {
704 textrans[ntextrans++] = add_texture_transform(pbr->base_color_texture, ntextrans);
705 }
706 }
707 if (pbr->metallic_roughness_texture.texture ) {
708 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, pbr->metallic_roughness_texture.texture);
709 mat->metallicRoughnessTexture = image;
710 //if (do_mapping) mat->metallicRoughnessTextureMapping = newASCIIString("one");
711 mat->metallicRoughnessTextureMapping = set_mat_mapping(pbr->metallic_roughness_texture, ntextrans);
712 if (pbr->metallic_roughness_texture.has_transform) {
713 textrans[ntextrans++] = add_texture_transform(pbr->metallic_roughness_texture, ntextrans);
714 }
715 }
716 if (prim->material->emissive_texture.texture ) {
717 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, prim->material->emissive_texture.texture);
718 mat->emissiveTexture = image;
719 //if (do_mapping) mat->emissiveTextureMapping = newASCIIString("one");
720 mat->emissiveTextureMapping = set_mat_mapping(prim->material->emissive_texture, ntextrans);
721 if (prim->material->emissive_texture.has_transform) {
722 textrans[ntextrans++] = add_texture_transform(prim->material->emissive_texture, ntextrans);
723 }
724 }
725 if (prim->material->normal_texture.texture ) {
726 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, prim->material->normal_texture.texture);
727 mat->normalTexture = image;
728 //if (do_mapping) mat->normalTextureMapping = newASCIIString("one");
729 mat->normalTextureMapping = set_mat_mapping(prim->material->normal_texture, ntextrans);
730 if (prim->material->normal_texture.has_transform) {
731 textrans[ntextrans++] = add_texture_transform(prim->material->normal_texture, ntextrans);
732 }
733 }
734 if (prim->material->occlusion_texture.texture) {
735 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, prim->material->occlusion_texture.texture);
736 mat->occlusionTexture = image;
737 //if (do_mapping) mat->occlusionTextureMapping = newASCIIString("one");
738 mat->occlusionTextureMapping = set_mat_mapping(prim->material->occlusion_texture, ntextrans);
739 if (prim->material->occlusion_texture.has_transform) {
740 textrans[ntextrans++] = add_texture_transform(prim->material->occlusion_texture, ntextrans);
741 }
742 }
743 }
744 sn->appearance = createNewX3DNode(NODE_Appearance);
745 if (ntextrans > 0) {
746 save_texture_transforms(sn->appearance, ntextrans, textrans);
747 }
748 X3D_APPEARANCE(sn->appearance)->material = X3D_NODE(mat);
749 }else if(prim->material->has_pbr_specular_glossiness){
750 int mtype = NODE_Material;
751 struct X3D_Material* mat = (struct X3D_Material*) USE_node(prim->material->name,X3DMaterialNode);
752 ntextrans = 0;
753 if(!mat){
754 //typedef struct cgltf_pbr_specular_glossiness
755 //{
756 // cgltf_texture_view diffuse_texture;
757 // cgltf_texture_view specular_glossiness_texture;
758 //
759 // cgltf_float diffuse_factor[4];
760 // cgltf_float specular_factor[3];
761 // cgltf_float glossiness_factor;
762 //} cgltf_pbr_specular_glossiness;
763 // june 22, 2020 not done: textures for diffuse and specular_glossiness
764 // neither url nor inline / in-bin handled
765 mat = (struct X3D_Material*) DEF_node(ectx,prim->material->name,mtype);
766 cgltf_pbr_specular_glossiness *pbr = &prim->material->pbr_specular_glossiness;
767 veccopy3f(mat->emissiveColor.c,prim->material->emissive_factor);
768 veccopy3f(mat->diffuseColor.c,pbr->diffuse_factor);
769 veccopy3f(mat->specularColor.c,pbr->specular_factor);
770 mat->shininess = pbr->glossiness_factor;
771 mat->transparency = 1.0f - pbr->diffuse_factor[3];
772
773 if (pbr->specular_glossiness_texture.texture) {
774 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, pbr->specular_glossiness_texture.texture);
775 mat->specularTexture = image;
776 mat->shininessTexture = image;
777 //if (do_mapping) mat->specularTextureMapping = newASCIIString("one");
778 //if (do_mapping) mat->shininessTextureMapping = newASCIIString("one");
779 mat->specularTextureMapping = set_mat_mapping(pbr->specular_glossiness_texture, ntextrans);
780 mat->shininessTextureMapping = set_mat_mapping(pbr->specular_glossiness_texture, ntextrans);
781 if (pbr->specular_glossiness_texture.has_transform) {
782 textrans[ntextrans++] = add_texture_transform(pbr->specular_glossiness_texture, ntextrans);
783 }
784
785 }
786
787 if (pbr->diffuse_texture.texture) {
788 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, pbr->diffuse_texture.texture);
789 mat->diffuseTexture = image;
790 //if (do_mapping) mat->diffuseTextureMapping = newASCIIString("one");
791 mat->diffuseTextureMapping = set_mat_mapping(pbr->diffuse_texture, ntextrans);
792 if (pbr->diffuse_texture.has_transform) {
793 textrans[ntextrans++] = add_texture_transform(pbr->diffuse_texture, ntextrans);
794 }
795 }
796
797 if (prim->material->emissive_texture.texture) {
798 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, prim->material->emissive_texture.texture);
799 mat->emissiveTexture = image;
800 //if (do_mapping) mat->emissiveTextureMapping = newASCIIString("one");
801 mat->emissiveTextureMapping = set_mat_mapping(prim->material->emissive_texture, ntextrans);
802 if (prim->material->emissive_texture.has_transform) {
803 textrans[ntextrans++] = add_texture_transform(prim->material->emissive_texture, ntextrans);
804 }
805 }
806
807 if (prim->material->normal_texture.texture) {
808 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, prim->material->normal_texture.texture);
809 mat->normalTexture = image;
810 //if (do_mapping) mat->normalTextureMapping = newASCIIString("one");
811 mat->normalTextureMapping = set_mat_mapping(prim->material->normal_texture, ntextrans);
812 if (prim->material->normal_texture.has_transform) {
813 textrans[ntextrans++] = add_texture_transform(prim->material->normal_texture, ntextrans);
814 }
815 }
816
817 if (prim->material->occlusion_texture.texture) {
818 struct X3D_Node* image = x3dtexture_from_cgltf_texture(ectx, prim->material->occlusion_texture.texture);
819 mat->occlusionTexture = image;
820 //if (do_mapping) mat->occlusionTextureMapping = newASCIIString("one");
821 mat->occlusionTextureMapping = set_mat_mapping(prim->material->occlusion_texture, ntextrans);
822 if (prim->material->occlusion_texture.has_transform) {
823 textrans[ntextrans++] = add_texture_transform(prim->material->occlusion_texture, ntextrans);
824 }
825 }
826 }
827 sn->appearance = createNewX3DNode(NODE_Appearance);
828 if (ntextrans > 0) {
829 save_texture_transforms(sn->appearance, ntextrans, textrans);
830 }
831 X3D_APPEARANCE(sn->appearance)->material = X3D_NODE(mat);
832 }
833
834 }
835 // https://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/rendering.html
836 // https://www.khronos.org/files/gltf20-reference-guide.pdf
837 struct X3D_Node *gn = NULL;
838 switch(prim->type){
839 case cgltf_primitive_type_points:
840 case cgltf_primitive_type_lines:
841 case cgltf_primitive_type_line_loop:
842 case cgltf_primitive_type_line_strip:
843 break;
844 case cgltf_primitive_type_triangles:
845 {
846 cgltf_float element_float[16];
847 int acount = prim->attributes_count;
848 if(show){
849 //printout to help get the idea of whats in the structs
850 printf("triangles\n");
851 for(int ii=0;ii<acount;ii++){
852 printf("attr %s indx %d ",prim->attributes[ii].name,prim->attributes[ii].index);
853 switch(prim->attributes[ii].type){
854 case cgltf_attribute_type_invalid: printf("invalid");break;
855 case cgltf_attribute_type_position: printf("position");break;
856 case cgltf_attribute_type_normal: printf("normal");break;
857 case cgltf_attribute_type_tangent: printf("tangent");break;
858 case cgltf_attribute_type_texcoord: printf("texcoord");break;
859 case cgltf_attribute_type_color: printf("color");break;
860 case cgltf_attribute_type_joints: printf("joints");break;
861 case cgltf_attribute_type_weights: printf("weights");break;
862 default: break;
863 }
864
865 const cgltf_accessor* blob = prim->attributes[ii].data;
866 cgltf_size nfloats = cgltf_num_components(blob->type) * blob->count;
867 printf(" nfloats = %d accessor type %d count %d ",(int)nfloats,blob->type, (int)blob->count);
868 switch(blob->type){
869 case cgltf_type_scalar: printf("SCALAR");break;
870 case cgltf_type_vec2: printf("VEC2");break;
871 case cgltf_type_vec3: printf("VEC3");break;
872 default: break;
873 }
874 printf("\n");
875 cgltf_float element_float[16];
876 for (cgltf_size index = 0; index < blob->count; index++)
877 {
878 cgltf_accessor_read_float(blob, index, element_float, 16);
879 printf("%d %f %f %f\n",(int)index,element_float[0],element_float[1],element_float[2]);
880 }
881 }
882 {
883 // indexes for indexedtriangleset
884 const cgltf_accessor* blob = prim->indices;
885 cgltf_uint element_int;
886 printf("triangle indices\n");
887 int ntri = blob->count / 3;
888 for (int i = 0; i < ntri; i++)
889 {
890 printf("%d [",i);
891 for(int j=0;j<3;j++){
892 int index = (i*3)+j;
893 cgltf_accessor_read_uint(blob, index, &element_int, 1);
894 printf("%d ",element_int);
895 }
896 printf("]\n");
897 }
898 }
899 }
900 gn = createNewX3DNode(NODE_BufferGeometry); //NODE_TriangleSet);
901 add_node_to_broto_context(X3D_PROTO(ectx),X3D_NODE(gn));
902 sn->geometry = gn;
903 struct X3D_BufferGeometry *ts = (struct X3D_BufferGeometry*)gn;
904 int lookup_attrib_index[] = {
905 //typedef enum cgltf_attribute_type
906 //{
907 -1, // cgltf_attribute_type_invalid, //0
908 0, // cgltf_attribute_type_position, //1
909 3, // cgltf_attribute_type_normal, //2
910 -1, // cgltf_attribute_type_tangent, //3
911 4, //4-7 cgltf_attribute_type_texcoord, //4
912 1, // cgltf_attribute_type_color, //5
913 -1, // cgltf_attribute_type_joints, //6
914 -1, // cgltf_attribute_type_weights, //7
915 2, //fog not mentioned in cgltf
916 };
917 int lookup_GL_type[] = {
918 -1, //0 cgltf_component_type_invalid,
919 GL_BYTE, //1 cgltf_component_type_r_8, /* BYTE */
920 GL_UNSIGNED_BYTE, //2 cgltf_component_type_r_8u, /* UNSIGNED_BYTE */
921 GL_SHORT, //3 cgltf_component_type_r_16, /* SHORT */
922 GL_UNSIGNED_SHORT, //4 cgltf_component_type_r_16u, /* UNSIGNED_SHORT */
923 GL_UNSIGNED_INT, //5 cgltf_component_type_r_32u, /* UNSIGNED_INT */
924 GL_FLOAT, //6 cgltf_component_type_r_32f, /* FLOAT */
925 GL_DOUBLE, //7 not convered by cgltf
926 };
927 struct X3D_MeshRep* mr;
928 mr = set_MeshRep(NULL);
929 ts->_intern = (struct X3D_GeomRep*)mr;
930 //set per-vertex attributes (coord, color, fog, normal, UV[0-4])
931 mr->flipuv = TRUE; //somewhere else flip UV y, see: http://web3d.org/pipermail/x3d-public_web3d.org/2022-April/017117.html
932 acount = prim->attributes_count;
933 for (int ii = 0; ii < acount; ii++) {
934 const cgltf_accessor* blob = prim->attributes[ii].data;
935 int iat = lookup_attrib_index[prim->attributes[ii].type];
936 if (iat < 0) continue;
937 if (iat == 0) {
938 //vertex accessor, take the vertex count
939 mr->ncoord = blob->count;
940 }
941 struct bufAccess* ba = &mr->attrib[iat];
942 //Q. which buffer? It might already be allocated.
943 // in freewrl we allocate once
944
945 //mr->buffer = find_buffer_in_broto_context_from_cgltf_buffer(ectx, blob->buffer_view->buffer);
946 //if (!mr->buffer) {
947 // //first use of buffer, allocate
948 // mr->buffer = add_geomBuffer0(ectx,blob->buffer_view->buffer->size, 1);
949 // mr->buffer->cgltf_buffer = (char*)blob->buffer_view->buffer;
950 // if (blob->buffer_view->buffer->data) {
951 // memcpy(mr->buffer->address, blob->buffer_view->buffer->data, blob->buffer_view->buffer->size);
952 // mr->buffer->loaded = 1;
953 // mr->buffer->cgltf_buffer = (char*) blob->buffer_view->buffer;
954 // // can't do in this thread, wait for compile_BufferGeometry set_geomBuffer(mr->buffer);
955 // } else {
956 // //how / where do we connect this to uri loading via resource fetch?
957 // }
958 //} else {
959 // add_geomBufferUser(mr->buffer);
960 //}
961 mr->buffer = find_or_create_buffer_add_user(ectx, blob->buffer_view);
962
963 ba->byteOffset = blob->buffer_view->offset;
964 ba->dataSize = cgltf_num_components(blob->type);
965 ba->dataType = lookup_GL_type[blob->component_type];
966 ba->byteStride = blob->stride; //the bufferView also has a stride
967 ba->byteSize = ba->dataSize * lookup_dataType_size(ba->dataType);
968 ba->in_use = 1;
969 }
970 {
971 // indexes for indexedtriangleset
972 const cgltf_accessor* blob = prim->indices;
973 struct bufAccess* ba = &mr->index;
974 ba->byteOffset = blob->buffer_view->offset;
975 ba->dataSize = cgltf_num_components(blob->type);
976 ba->dataType = lookup_GL_type[blob->component_type];
977 ba->byteStride = blob->stride; //the bufferView also has a stride
978 ba->byteSize = ba->dataSize * lookup_dataType_size(ba->dataType);
979 ba->in_use = 1;
980 mr->nindex = blob->count;
981 }
982 if (prim->attributes->data->has_max && prim->attributes->data->has_min) {
983 float *emin = prim->attributes->data->min;
984 float* emax = prim->attributes->data->max;
985 extent6f_constructor(ts->_extent, emin[0], emin[1], emin[2], emax[0], emax[1], emax[2]);
986 }
987 }
988 break;
989 case cgltf_primitive_type_triangle_strip:
990 //June 22, 2020 possible in gltf format, but not implemented here
991 // hypothesis: one BufferGeometry node (above) can handle all geom types
992 // including points and lines
993 printf("triangle strip\n");
994 break;
995 case cgltf_primitive_type_triangle_fan:
996 printf("triangle fan\n");
997 break;
998 }
999 //printf("adding shape to mesh group\n");
1000 //vector_pushBack(void *, &gr->children, sn);
1001 AddRemoveChildren(X3D_NODE(gr), offsetPointer_deref(void*, gr, offsetof(struct X3D_Group, children)), (struct X3D_Node * *)(X3D_NODE(sn)), 1, 1, __FILE__, __LINE__);
1002 }
1003 }
1004 //printf("adding mesh\n");
1005 vector_pushBack(void *, pp, gr);
1006 }
1007 if(node->skin){
1008 //vector_pushBack(void *, pp, skin);
1009 }
1010 if(node->weights_count){
1011 }
1012 size_t estart = node->extras.start_offset;
1013 size_t eend = node->extras.end_offset;
1014 //children part
1015 int mc = node->children_count;
1016 if(mc){
1017
1018 struct X3D_Node* pn;
1019 for(int i=0;i<mc;i++){
1020 if (parse_gltf_node(ectx, &pn, data, node->children[i], unit)) {
1021 vector_pushBack(struct X3D_Node*, pp, pn);
1022 }
1023 }
1024 //printf("adding children\n");
1025 }
1026 int got_something = FALSE;
1027 if (pp->n) {
1028 //transform part: gltf has a flat scenegraph, with each node having a transform and a thing, with thing being mesh, camera. Like Blender.
1029 struct X3D_Transform* t = createNewX3DNode(NODE_Transform);
1030 if (node->has_matrix) {
1031 //parse matrix into TRS
1032 //
1033 printf("gltf_loader not parsing matrix yet\n");
1034 }
1035 else {
1036 vecset3f(t->translation.c, 0.0f, 0.0f, 0.0f);
1037 vecset3f(t->scale.c, 1.0f, 1.0f, 1.0f);
1038 vecset4f(t->rotation.c, 0.0f, 1.0f, 0.0f, 0.0f);
1039 if (node->has_rotation ) {
1040 float* r = t->rotation.c;
1041 double rd[4];
1042 Quaternion q;
1043 q.x = node->rotation[0];
1044 q.y = node->rotation[1];
1045 q.z = node->rotation[2];
1046 q.w = node->rotation[3];
1047 quaternion_normalize(&q);
1048 quaternion_to_vrmlrot(&q, &rd[0], &rd[1], &rd[2], &rd[3]);
1049 double2float(r, rd, 4);
1050 //r[3] = -r[3];
1051 }
1052 if (node->has_scale ) {
1053 veccopy3f(t->scale.c, node->scale);
1054 //printf("scale %f %f %f\n", t->scale.c[0], t->scale.c[1], t->scale.c[2]);
1055 }
1056 if (node->has_translation ) {
1057 veccopy3f(t->translation.c, node->translation);
1058 }
1059 }
1060 t->children.n = pp->n;
1061 t->children.p = pp->data;
1062 if (0) {
1063 printf("translation %f %f %f\n", t->translation.c[0], t->translation.c[1], t->translation.c[2]);
1064 printf("scale %f %f %f\n", t->scale.c[0], t->scale.c[1], t->scale.c[2]);
1065 printf("rotation %f %f %f %f\n", t->rotation.c[0], t->rotation.c[1], t->rotation.c[2], t->rotation.c[3]);
1066 printf("number of children %d\n", t->children.n);
1067 for (int k = 0; k < t->children.n; k++)
1068 printf(" %d %s\n", k, stringNodeType(t->children.p[k]->_nodeType));
1069 }
1070 for (int i = 0; i < t->children.n; ++i) {
1071 ADD_PARENT(t->children.p[i], X3D_NODE(t));
1072 }
1073 add_node_to_broto_context(X3D_PROTO(ectx), X3D_NODE(t));
1074
1075 *spot = X3D_NODE(t);
1076 got_something = TRUE;
1077 }
1078 return got_something;
1079}
1080
1081int parse_gltf(struct X3D_Node *ectx, struct Multi_Node *spot, cgltf_data * data, gltf_unit *unit){
1082 int n = data->scene[0].nodes_count;
1083 spot->p = realloc(spot->p, n * sizeof(struct X3D_Node *));
1084 n = 0; //we may not know how to parse them all, so don't count ones we don't parse.
1085 for(int i=0;i<data->scene[0].nodes_count;i++){
1086 if( parse_gltf_node(ectx,&spot->p[n],data,data->scene[0].nodes[i], unit) ) n++;
1087 }
1088 spot->n = n;
1089 //printf("got %d children\n", n);
1090 int ret = TRUE;
1091 return ret;
1092}
1093
1094struct uri_data {
1095 char *uri;
1096 void **data;
1097 int data_size;
1098 void* cdata;
1099};
1100cgltf_result cgltf_load_buffers_except_files(const cgltf_options* options, cgltf_data* data, Stack *file_list)
1101{
1102 if (options == NULL)
1103 {
1104 return cgltf_result_invalid_options;
1105 }
1106
1107 if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin)
1108 {
1109 if (data->bin_size < data->buffers[0].size)
1110 {
1111 return cgltf_result_data_too_short;
1112 }
1113
1114 data->buffers[0].data = (void*)data->bin;
1115 }
1116
1117 for (cgltf_size i = 0; i < data->buffers_count; ++i)
1118 {
1119 if (data->buffers[i].data)
1120 {
1121 continue;
1122 }
1123
1124 const char* uri = data->buffers[i].uri;
1125
1126 if (uri == NULL)
1127 {
1128 continue;
1129 }
1130
1131 if (strncmp(uri, "data:", 5) == 0)
1132 {
1133 const char* comma = strchr(uri, ',');
1134
1135 if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0)
1136 {
1137 cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data);
1138
1139 if (res != cgltf_result_success)
1140 {
1141 return res;
1142 }
1143 }
1144 else
1145 {
1146 return cgltf_result_unknown_format;
1147 }
1148 }
1149 else if (strstr(uri, "://") == NULL )
1150 {
1151 struct uri_data ud;
1152 ud.uri = uri;
1153 ud.cdata = &data->buffers[i]; //after phase 2 parse, we will look up geombuffer from this buffer address
1154 ud.data_size = data->buffers[i].size;
1155 stack_push(struct uri_data,file_list,ud);
1156 }
1157 else
1158 {
1159 return cgltf_result_unknown_format;
1160 }
1161 }
1162
1163 return cgltf_result_success;
1164}
1165
1166
1167//ret = X3DParse(ectx, X3D_NODE(nRn), (const char*)input);
1168int parser_do_parse_gltf(const char *input, const int len, struct X3D_Node *ectx, struct X3D_Node *myParent)
1169{
1170 // ectx - the context node - either Inline or Scene
1171 // rNr temporary group container node where we'll put the new nodes as children (should have been struct MFNode * field of container)
1172 int ret = FALSE;
1173 {
1174 cgltf_options options;
1175 memset(&options, 0, sizeof(cgltf_options));
1176 cgltf_data* data = NULL;
1177 ppgltf_loader p = (ppgltf_loader)gglobal()->gltf_loader.prv;
1178 gltf_unit *unit = malloc(sizeof(gltf_unit));
1179 memset(unit,0,sizeof(gltf_unit));
1180 stack_push(gltf_unit*,p->gltf_units,unit);
1181
1182 unit->blob = malloc(len);
1183 unit->blob_len = len;
1184 memcpy(unit->blob,input,len); //resource process garbage collects input. For .glb we need to keep blob
1185 cgltf_result result = cgltf_parse( &options, (void*) unit->blob, unit->blob_len, &data);
1186 unit->data = data;
1187 if (result == cgltf_result_success)
1188 {
1189 printf("gltf parsed into cgltf scene struct\n");
1190 /* TODO make awesome stuff */
1191 //char *local_path = getContext ectx->_
1192 //result = cgltf_load_buffers(&options, data, "./");
1193 Stack *file_list = newStack(struct uri_data);
1194 result = cgltf_load_buffers_except_files(&options, data,file_list);
1195
1196 if(result == cgltf_result_success && file_list->n == 0){
1197 unit->bin_loaded = TRUE;
1198 }else if(result == cgltf_result_file_not_found){
1199 printf("gltf .bin file not found ... yet\n");
1200 //generate a resource to fetch .bin
1201 }else if(file_list->n){
1202 resource_item_t *res;
1203 struct X3D_Proto *context = X3D_PROTO(ectx);
1204
1205 //compact file list?
1206 //spawn resource(s) to fetch>
1207 unit->bin_file_list = file_list;
1208 int nn = 1;
1209 nn = file_list->n;
1210 for (int k = 0; k < nn; k++) {
1211 struct uri_data* ud = vector_get_ptr(struct uri_data, file_list, k);
1212 char* uri = ud->uri;
1213 res = resource_create_single(uri);
1214 res->ectx = ectx;
1215 res->media_type = resm_unknown; // resm_bin;
1216 res->resm_specific = ud->cdata;
1217 resource_identify(context->_parentResource, res);
1218 res->actions = resa_download | resa_load | resa_process;
1219 resitem_enqueue(ml_new(res));
1220 }
1221 }
1222 // 1. go over struct, creating x3d nodes and nesting them
1223 struct Multi_Node *spot;
1224 if(myParent->_nodeType == NODE_Proto || myParent->_nodeType == NODE_Inline )
1225 spot = &((struct X3D_Proto*)(myParent))->__children;
1226 else
1227 spot = &((struct X3D_Group*)(myParent))->children;
1228 spot->p = NULL; spot->n = 0;
1229 parse_gltf(ectx,spot,data,unit);
1230 for(int j=0;j<spot->n;j++)
1231 ADD_PARENT(X3D_NODE(spot->p[j]), X3D_NODE(ectx));
1232 // documentation: """Note that cgltf does not load the contents of extra files such as buffers or images into memory by default.
1233 // You'll need to read these files yourself using URIs from data.buffers[] or data.images[] respectively. """
1234 ret = TRUE;
1235 }
1236 }
1237
1238 return ret;
1239}
1240
1241// .glb has the .bin binary buffers inside and we can load and parse in one shot
1242// .glTF refers to a separate .bin file
1243// 1 we parse either to cgltf nodes
1244// 2 then check if bin loaded as part of glb, and if so apply
1245// 3 else we spawn a resource loader to fetch .bin (should work also over http)
1246// 4 we parse into x3d nodes either way, putting gltf_unit* in nodes that need the bin data
1247// 5 nodes check gltf_unit, and delay compile_ until .bin loaded flag set
1248// 6 when .bin resource loads here, we apply bin to cgltf nodes buffer.data and set the gltf_unit-loaded flag
1249
1250int gltf_load_bin(resource_item_t *res){
1251 // late arriving .bin (for .gltf separated unit)
1252 cgltf_buffer* buffer = (void*)res->resm_specific;
1253 if (buffer) {
1254 struct geomBuffer *gb = find_buffer_in_broto_context_from_cgltf_buffer(res->ectx, buffer);
1255 if (gb) {
1256 openned_file_t* of = res->openned_files;
1257 int len = of->fileDataSize;
1258 char* input = of->fileData;
1259 memcpy(gb->address, input, len);
1260 gb->loaded = 1;
1261 }
1262 }
1263
1264 return TRUE;
1265}
1266
1267bool parser_process_res_VRML_X3D(resource_item_t *res);
1268
1269int parser_process_res_gltf(resource_item_t *res){
1270 //these media types (require us to) generate x3d scene nodes and can be a scene unto themselves,
1271 // or inline body
1272 //some embed needed resources, others request more resources which are placed in their node fields.
1273
1274 int parsedOk = FALSE;
1275 switch(res->media_type){
1276 case resm_glb:
1277 case resm_gltf:
1278 // .bin gl buffers and images are packed into one .glb file
1279 //text/json gltf file can inline some .bin and img buffers as text
1280 // but more normally separate .bin binary buffer file and image urls
1281 parsedOk = parser_process_res_VRML_X3D(res);
1282 break;
1283 case resm_bin:
1284 //gltf can be exported with separate binary buffer file
1285 // like loading image textures, the bin needs to catch-up to the
1286 // parsed x3d node, so after applying binary to parsed cgltf nodes,
1287 // sets a flag that previously spawned x3d nodes can check to see
1288 // when binary gl buffer data has been loaded and applied to primitives
1289 parsedOk = gltf_load_bin(res);
1290 break;
1291 //cesium related - for future geo/cesium work
1292 case resm_json:
1293 break;
1294 case resm_b3dm:
1295 break;
1296 case resm_i3dm:
1297 break;
1298 case resm_pnts:
1299 break;
1300 case resm_cmpt:
1301 break;
1302
1303 }
1304 return parsedOk;
1305}
1306float* extent6f_fromBufferAccess(float* e6, struct geomBuffer* gb, struct bufAccess* ba, int ncoord) {
1307 extent6f_clear(e6);
1308 float point[3];
1309 double* dp;
1310 float* fp;
1311 char* paddress;
1312 memset(point, 0, 3 * sizeof(float));
1313 for (int i = 0; i < ncoord; i++) {
1314 paddress = get_Attribi(ba, gb, i);
1315 if (ba->dataType == GL_FLOAT) {
1316 fp = (float*)paddress;
1317 for (int j = 0; j < ba->dataSize; j++) {
1318 point[j] = fp[j];
1319 }
1320 }
1321 else if (ba->dataType == GL_DOUBLE) {
1322 dp = (double*)paddress;
1323 double2float(point, dp, ba->dataSize);
1324 }
1325 extent6f_union_vec3f(e6, point);
1326 }
1327 return e6;
1328}
1329void compile_BufferGeometry(struct X3D_BufferGeometry *node){
1330 struct X3D_MeshRep* mr = (struct X3D_MeshRep*) node->_intern;
1331 if (mr) {
1332 //if (0) {
1333 // printf("compile_BufferGeometry context = %p\n", get_executionContext());
1334 // struct geomBuffer* gb = find_buffer_in_broto_context_from_cgltf_buffer(get_executionContext(), mr->buffer->cgltf_buffer);
1335 // if (gb) printf("found geomBuffer in this thread executionContext\n");
1336 // else printf("didn't find geombuffer in this thread executionContext\n");
1337 //}
1338 if (mr->buffer->loaded == 1)
1339 set_geomBuffer(mr->buffer);
1340 if (mr->buffer->loaded == 2) {
1341 float e6[6];
1342 extent6f_clear(e6);
1343
1344 MARK_NODE_COMPILED
1345 if (mr->attrib[0].in_use) {
1346 //update extent if not set
1347 if (!extent6f_isSet(node->_extent)) {
1348 extent6f_fromBufferAccess(e6, mr->buffer, &mr->attrib[0], mr->ncoord);
1349 }
1350 }
1351 int nuv = 0;
1352 for (int j = 0; j < 4; j++)
1353 if (mr->attrib[4 + j].in_use) nuv++;
1354 mr->nuv = nuv;
1355 if (0) {
1356 extent6f_printf(node->_extent);
1357 printf("cgltf min,max\n");
1358 extent6f_printf(e6);
1359 printf("compile_BufferGeometry extent\n");
1360 }
1361 if(extent6f_isSet(e6))
1362 extent6f_copy(node->_extent, e6);
1363 }
1364 }
1365}
1366void render_BufferGeometry(struct X3D_BufferGeometry *node){
1367
1368 //we lazy-load .bin binary buffer part for .gltf, so have to wait
1369 // till its loaded. .glb loads in one shot
1370 COMPILE_IF_REQUIRED;
1371 setExtent(node->EXTENT_MAX_X, node->EXTENT_MIN_X, node->EXTENT_MAX_Y,
1372 node->EXTENT_MIN_Y, node->EXTENT_MAX_Z, node->EXTENT_MIN_Z,
1373 X3D_NODE(node));
1374 render_MeshRep(node->_intern);
1375}
1376void rendray_BufferGeometry(struct X3D_BufferGeometry* node) {
1377 /* is this structure still loading? */
1378 if (!node) return;
1379 /* is this structure still loading? */
1380 if (!(node->_intern)) {
1381 return;
1382 }
1383 if (node->_ichange == 0) return; //not compiled yet
1384
1385 rendray_MeshRep(node->_intern);
1386}
1387
1388void collide_BufferGeometry(struct X3D_BufferGeometry *node){
1389}