naev 0.12.5
gltf.c
1#include "gltf.h"
2
3#include "SDL_image.h"
4#include "glad.h"
5#include <libgen.h>
6#include <math.h>
7
8#include "physfsrwops.h"
9
10#define CGLTF_IMPLEMENTATION
11#include "cgltf.h"
12
13#ifdef HAVE_NAEV
14#include "conf.h"
15#include "naev.h"
16#include "nfile.h"
17#include "opengl_shader.h"
18#else /* HAVE_NAEV */
19#include "common.h"
20#include "shader_min.h"
21#define gl_contextSet()
22#define gl_contextUnset()
23#endif /* HAVE_NAEV */
24#include "array.h"
25#include "mat3.h"
26#include "vec3.h"
27
28#define SHADOWMAP_SIZE_LOW 128
29#define SHADOWMAP_SIZE_HIGH 512
30
31/* Horrible hack that turns a variable name into a string. */
32#define STR_HELPER( x ) #x
33#define STR( x ) STR_HELPER( x )
34
38typedef struct ObjectCache {
39 char *name;
43static ObjectCache *obj_cache = NULL;
44static SDL_mutex *cache_lock = NULL;
45
46static Material material_default;
47
51typedef struct ShaderLight_ {
52 GLuint Hshadow; /* mat4 */
53 GLuint sun; /* bool */
54 GLuint position; /* vec3 */
55 GLuint colour; /* vec3 */
56 GLuint intensity; /* float */
57 GLuint shadowmap_tex; /* sampler2D */
59
60/*
61 * EV:Nova
62 * -> key light is top left, slight elevation, pointed at ship. directional
63 * light
64 * -> 1 to 3 fill lights. Depends on ship. Point lights.
65 */
66const Lighting L_default_const = {
67 .ambient_r = 0.,
68 .ambient_g = 0.,
69 .ambient_b = 0.,
70 .intensity = 1.,
71 .nlights = 2,
72 .lights =
73 {
74 {
75 /* Key Light. */
76 .colour = { .v = { 1., 1., 1. } },
77 // Endless Sky (point) power: 150, pos: -12.339, 10.559, -11.787
78 /*
79 */
80 .sun = 0,
81 .pos = { .v = { -3., 2.75, -3. } },
82 .intensity = 80.,
83 // Sharky (directional) power: 5, direction: 10.75, -12.272, 7.463
84 /*
85 .sun = 1,
86 .pos = { .v = { 12., 10.5, -12. } },
87 .intensity = 2.5,
88 */
89 },
90 {
91 /* Fill light. */
92 .colour = { .v = { 1., 1., 1. } },
93 // Endless Sky (directional) power: 1.5,
94 // direction: 9.772, 11.602, 6.988
95 /*
96 */
97 .sun = 1,
98 .pos = { .v = { 10., 11.5, 7. } },
99 .intensity = 1.,
100 // Sharky (point) power: 2000., position: -12.339, 10.559, 11.787
101 /*
102 .sun = 0,
103 .pos = { .v = { -12.5, 10.5, 12. } },
104 .intensity = 2000.,
105 */
106 },
107 { 0 },
108 },
109};
110const Lighting L_store_const = {
111 .ambient_r = 0.1,
112 .ambient_g = 0.1,
113 .ambient_b = 0.1,
114 .nlights = 2,
115 .intensity = 1.5,
116 .lights =
117 {
118 {
119 /* Key Light. */
120 .colour = { .v = { 1., 1., 1. } },
121 .sun = 0,
122 .pos = { .v = { -3., 2.75, -3. } },
123 .intensity = 100.,
124 },
125 {
126 /* Fill light. */
127 .colour = { .v = { 1., 1., 1. } },
128 .sun = 1,
129 .pos = { .v = { 10., 11.5, 7. } },
130 .intensity = 1.,
131 },
132 { 0 },
133 },
134};
135Lighting L_default;
136
137static GLuint light_fbo_low[MAX_LIGHTS];
138static GLuint
139 light_tex_low[MAX_LIGHTS];
140static GLuint light_fbo_high[MAX_LIGHTS];
142static GLuint light_tex_high[MAX_LIGHTS];
144static GLuint shadowmap_size = SHADOWMAP_SIZE_LOW;
145static GLuint *light_fbo = light_fbo_low;
146static GLuint *light_tex = light_tex_low;
147static mat4 light_mat_def[MAX_LIGHTS];
148static mat4 light_mat_alt[MAX_LIGHTS];
149static mat4 *light_mat = light_mat_def;
150
154typedef struct Shader_ {
155 GLuint program;
156 /* Attriutes. */
157 GLuint vertex;
158 GLuint vertex_normal;
159 GLuint vertex_tex0;
160 GLuint vertex_tex1;
161 /* Vertex Uniforms. */
162 GLuint Hmodel;
163 GLuint Hnormal;
164 GLuint Hshadow;
165 /* Fragment uniforms. */
166 GLuint baseColour_tex;
167 GLuint baseColour_texcoord;
168 GLuint metallic_tex;
169 GLuint metallic_texcoord;
170 GLuint u_has_normal;
171 GLuint normal_tex;
172 GLuint normal_texcoord;
173 GLuint normal_scale;
174 GLuint metallicFactor;
175 GLuint roughnessFactor;
176 GLuint baseColour;
177#if 0
178 GLuint sheenTint;
179 GLuint sheen;
180 GLuint clearcoat;
181 GLuint clearcoat_roughness;
182#endif
183 GLuint emissive;
184 GLuint emissive_tex;
185 GLuint emissive_texcoord;
186 GLuint occlusion_tex;
187 GLuint occlusion_texcoord;
188 ShaderLight lights[MAX_LIGHTS];
189 GLuint nlights;
190 GLuint blend;
191 GLuint u_ambient;
192 /* Custom Naev. */
193 // GLuint waxiness;
194} Shader;
195static Shader gltf_shader;
196static Shader shadow_shader;
197static Texture tex_zero = {
198 .tex = 0,
199 .texcoord = 0,
200 .strength = 1. }; /* Used to detect initialization for now. */
201static Texture tex_ones = { .tex = 0, .texcoord = 0, .strength = 1. };
202
203/* Below here are for blurring purposes. */
204static GLuint shadow_vbo;
205static GLuint shadow_fbo_low;
206static GLuint shadow_fbo_high;
207static GLuint shadow_tex_low;
208static GLuint shadow_tex_high;
209static GLuint *shadow_fbo = &shadow_fbo_low;
210static GLuint *shadow_tex = &shadow_tex_low;
211static Shader shadow_shader_blurX;
212static Shader shadow_shader_blurY;
213
214/* Options. */
215static int use_normal_mapping = 1;
216static int use_ambient_occlusion = 1;
217static int max_tex_size = 0;
218
219/* Prototypes. */
220static int cache_cmp( const void *p1, const void *p2 );
221static GltfObject *cache_get( const char *filename, int *new );
222static int cache_dec( GltfObject *obj );
223static void gltf_applyAnim( GltfObject *obj, GLfloat time );
224
234static int gltf_loadTexture( const GltfObject *obj, Texture *otex,
235 const cgltf_texture_view *ctex, const Texture *def,
236 int notsrgb )
237{
238 const SDL_PixelFormatEnum fmt = SDL_PIXELFORMAT_ABGR8888;
239 GLuint tex;
240 SDL_Surface *surface = NULL;
241 int has_alpha;
242 const char *path;
243 GLint internalformat;
244 SDL_RWops *rw;
245#ifdef HAVE_NAEV
246 has_alpha = 0;
247#endif /* HAVE_NAEV */
248
249 /* Must haev texture to load it. */
250 if ( ( ctex == NULL ) || ( ctex->texture == NULL ) ) {
251 *otex = *def;
252 return 0;
253 }
254
255 /* Set up some data. */
256 otex->texcoord = ctex->texcoord;
257 otex->strength = ctex->transform.scale[0];
258
259 /* Load from path. */
260 path = ( ctex->texture->has_webp ) ? ctex->texture->webp_image->uri
261 : ctex->texture->image->uri;
262 if ( path == NULL ) {
263 DEBUG( "Buffer textures not supported yet!" );
264 return -1;
265 }
266
267 char filepath[PATH_MAX];
268#ifdef HAVE_NAEV
269 snprintf( filepath, sizeof( filepath ), "%s/%s", obj->path, path );
270#else /* HAVE_NAEV */
271 snprintf( filepath, sizeof( filepath ), "%s", path );
272#endif /* HAVE_NAEV */
273 nfile_simplifyPath( filepath );
274
275 /* Check to see if it already exists. */
276#ifdef HAVE_NAEV
277 int created;
278 int flags = OPENGL_TEX_MIPMAPS;
279 if ( notsrgb )
280 flags |= OPENGL_TEX_NOTSRGB;
281 otex->gtex = gl_texExistsOrCreate( filepath, flags, 1, 1, &created );
282 if ( !created ) {
283 /* Already exists, so texture should be valid. */
284 otex->tex = otex->gtex->texture;
285 return 0;
286 }
287#endif /* HAVE_NAEV */
288
289 /* Load the image data as a surface. */
290 rw = PHYSFSRWOPS_openRead( filepath );
291 if ( rw == NULL ) {
292 WARN( _( "Unable to open '%s': %s" ), filepath, SDL_GetError() );
293 *otex = *def;
294 return 0;
295 }
296 surface = IMG_Load_RW( rw, 1 );
297 if ( surface == NULL ) {
298 WARN( _( "Unable to load surface '%s': %s" ), filepath, SDL_GetError() );
299 *otex = *def;
300 return 0;
301 }
302
303 gl_contextSet();
304
305 glGenTextures( 1, &tex );
306 glBindTexture( GL_TEXTURE_2D, tex );
307
308 /* Set stuff. */
309 if ( ctex->texture->sampler != NULL ) {
310 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
311 ctex->texture->sampler->mag_filter );
312 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
313 ctex->texture->sampler->min_filter );
314 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
315 ctex->texture->sampler->wrap_s );
316 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
317 ctex->texture->sampler->wrap_t );
318 } else {
319 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
320 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
321 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
322 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
323 }
324
325 if ( surface != NULL ) {
326 has_alpha = surface->format->Amask;
327 if ( notsrgb )
328 internalformat = has_alpha ? GL_RGBA : GL_RGB;
329 else
330 internalformat = has_alpha ? GL_SRGB_ALPHA : GL_SRGB;
331 if ( surface->format->format != fmt ) {
332 SDL_Surface *temp = surface;
333 surface = SDL_ConvertSurfaceFormat( temp, fmt, 0 );
334 SDL_FreeSurface( temp );
335 }
336
337 SDL_LockSurface( surface );
338 glPixelStorei( GL_UNPACK_ALIGNMENT,
339 MIN( surface->pitch & -surface->pitch, 8 ) );
340 if ( notsrgb )
341 glTexImage2D( GL_TEXTURE_2D, 0, internalformat, surface->w, surface->h,
342 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels );
343 else
344 glTexImage2D( GL_TEXTURE_2D, 0, internalformat, surface->w, surface->h,
345 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels );
346 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
347 SDL_UnlockSurface( surface );
348 } else {
349 /*
350 glTexImage2D( GL_TEXTURE_2D, 0, GL_SRGB_ALPHA,
351 surface->w, surface->h, 0,
352 surface->format->Amask ? GL_RGBA : GL_RGB,
353 GL_UNSIGNED_BYTE, surface->pixels );
354 */
355 }
356
357#ifdef HAVE_NAEV
358 /* Downsample as necessary. */
359 if ( ( max_tex_size > 0 ) && ( surface != NULL ) &&
360 ( MAX( surface->w, surface->h ) > max_tex_size ) ) {
361 GLuint fbo, downfbo, downtex;
362 GLint status;
363
364 /* Create the downsampling framebuffers. */
365 gl_fboCreate( &downfbo, &downtex, max_tex_size, max_tex_size );
366
367 /* Create the render buffer, keeping RGB status. */
368 glGenTextures( 1, &downtex );
369 glBindTexture( GL_TEXTURE_2D, downtex );
370 glTexImage2D( GL_TEXTURE_2D, 0, internalformat, max_tex_size,
371 max_tex_size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
372
373 /* Create the frame buffer. */
374 glGenFramebuffers( 1, &downfbo );
375 glBindFramebuffer( GL_FRAMEBUFFER, downfbo );
376
377 /* Attach the colour buffer. */
378 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
379 GL_TEXTURE_2D, downtex, 0 );
380
381 /* Check status. */
382 status = glCheckFramebufferStatus( GL_FRAMEBUFFER );
383 if ( status != GL_FRAMEBUFFER_COMPLETE )
384 WARN( _( "Error setting up framebuffer!" ) );
385
386 /* Attach a framebuffer to the current texture. */
387 glGenFramebuffers( 1, &fbo );
388 glBindFramebuffer( GL_FRAMEBUFFER, fbo );
389 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
390 GL_TEXTURE_2D, tex, 0 );
391
392 /* Blit to framebuffer. */
393 glBindFramebuffer( GL_READ_FRAMEBUFFER, fbo );
394 glBindFramebuffer( GL_DRAW_FRAMEBUFFER, downfbo );
395 glBlitFramebuffer( 0, 0, surface->w, surface->h, 0, 0, max_tex_size,
396 max_tex_size, GL_COLOR_BUFFER_BIT, GL_LINEAR );
397 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
398
399 /* Clean up. */
400 glDeleteTextures( 1, &tex );
401 glDeleteFramebuffers( 1, &downfbo );
402 glDeleteFramebuffers( 1, &fbo );
403 tex = downtex;
404 glBindTexture( GL_TEXTURE_2D, tex );
405
406 /* Reapply sampling. */
407 if ( ctex->texture->sampler != NULL ) {
408 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
409 ctex->texture->sampler->mag_filter );
410 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
411 ctex->texture->sampler->min_filter );
412 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
413 ctex->texture->sampler->wrap_s );
414 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
415 ctex->texture->sampler->wrap_t );
416 } else {
417 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
418 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
419 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
420 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
421 }
422 }
423#endif /* HAVE_NAEV */
424
425 /* Set up mipmaps. */
426 /* TODO only generate if necessary. */
427 glGenerateMipmap( GL_TEXTURE_2D );
428
429 /* Free the surface. */
430 SDL_FreeSurface( surface );
431
432 glBindTexture( GL_TEXTURE_2D, 0 );
433
434 gl_checkErr();
435 gl_contextUnset();
436
437 otex->tex = tex;
438#ifdef HAVE_NAEV
439 otex->gtex->texture = tex; /* Update the texture. */
440#endif /* HAVE_NAEV */
441 return 0;
442}
443
447static int gltf_loadMaterial( const GltfObject *obj, Material *mat,
448 const cgltf_material *cmat,
449 const cgltf_data *data )
450{
451 const GLfloat white[4] = { 1., 1., 1., 1. };
452 /* TODO complete this. */
453 if ( cmat && cmat->has_pbr_metallic_roughness ) {
454 mat->metallicFactor = cmat->pbr_metallic_roughness.metallic_factor;
455 mat->roughnessFactor = cmat->pbr_metallic_roughness.roughness_factor;
456 gltf_loadTexture( obj, &mat->baseColour_tex,
457 &cmat->pbr_metallic_roughness.base_color_texture,
458 &tex_ones, 0 );
459 if ( mat->baseColour_tex.tex == tex_ones.tex )
460 memcpy( mat->baseColour,
461 cmat->pbr_metallic_roughness.base_color_factor,
462 sizeof( mat->baseColour ) );
463 else
464 memcpy( mat->baseColour, white, sizeof( mat->baseColour ) );
465 gltf_loadTexture(
466 obj, &mat->metallic_tex,
467 &cmat->pbr_metallic_roughness.metallic_roughness_texture, &tex_ones,
468 1 );
469 } else {
470 memcpy( mat->baseColour, white, sizeof( mat->baseColour ) );
471 mat->metallicFactor = 0.;
472 mat->roughnessFactor = 1.;
473 mat->baseColour_tex = tex_ones;
474 mat->metallic_tex = tex_ones;
475 mat->normal_tex = tex_zero;
476 }
477
478#if 0
479 /* Sheen. */
480 if ( cmat && cmat->has_sheen ) {
481 memcpy( mat->sheen, cmat->sheen.sheen_color_factor,
482 sizeof( mat->sheen ) );
483 mat->sheen_roughness = cmat->sheen.sheen_roughness_factor;
484 } else {
485 memset( mat->sheen, 0, sizeof( mat->sheen ) );
486 mat->sheen_roughness = 0.;
487 }
488
489 /* Handle clearcoat. */
490 if ( cmat && cmat->has_clearcoat ) {
491 mat->clearcoat = cmat->clearcoat.clearcoat_factor;
492 mat->clearcoat_roughness = cmat->clearcoat.clearcoat_roughness_factor;
493 } else {
494 mat->clearcoat = 0.;
495 mat->clearcoat_roughness = 0.;
496 }
497#endif
498
499 /* Handle emissiveness and such. */
500 if ( cmat ) {
501 memcpy( mat->emissiveFactor, cmat->emissive_factor,
502 sizeof( GLfloat ) * 3 );
503 gltf_loadTexture( obj, &mat->emissive_tex, &cmat->emissive_texture,
504 &tex_ones, 0 );
505 if ( use_ambient_occlusion )
506 gltf_loadTexture( obj, &mat->occlusion_tex, &cmat->occlusion_texture,
507 &tex_ones, 1 );
508 else
509 mat->occlusion_tex = tex_ones;
510 if ( use_normal_mapping )
511 gltf_loadTexture( obj, &mat->normal_tex, &cmat->normal_texture,
512 &tex_zero, 1 );
513 else
514 mat->normal_tex = tex_ones;
515 mat->blend = ( cmat->alpha_mode == cgltf_alpha_mode_blend );
516 mat->double_sided = cmat->double_sided;
517 mat->unlit = cmat->unlit;
518 } else {
519 memset( mat->emissiveFactor, 0, sizeof( GLfloat ) * 3 );
520 mat->emissive_tex = tex_ones;
521 mat->occlusion_tex = tex_ones;
522 mat->normal_tex = tex_ones;
523 mat->blend = 0;
524 }
525 /* Emissive strength extension just multiplies the emissiveness. */
526 if ( cmat && cmat->has_emissive_strength ) {
527 for ( int i = 0; i < 3; i++ )
528 mat->emissiveFactor[i] *= cmat->emissive_strength.emissive_strength;
529 }
530
531 mat->noshadows = mat->blend; /* Transparent things don't cast shadows. */
532 if ( cmat && data ) {
533 char buf[STRMAX_SHORT];
534 cgltf_size len = sizeof( buf );
535 cgltf_copy_extras_json( data, &cmat->extras, buf, &len );
536 jsmn_parser p;
537 jsmntok_t t[16]; /* Max number of expected tokens. */
538 jsmn_init( &p );
539 int r = jsmn_parse( &p, buf, len, t, sizeof( t ) / sizeof( jsmntok_t ) );
540 for ( int j = 0; j < r; j++ ) {
541 const jsmntok_t *tj = &t[j];
542 const char *str = "NAEV_noShadows";
543 if ( strncmp( str, &buf[tj->start],
544 MIN( strlen( str ),
545 (size_t)( tj->end - tj->start ) ) ) == 0 ) {
546 if ( j + 1 >= r )
547 break;
548 /* Disables shadows. */
549 mat->noshadows = 1;
550 break;
551 }
552 }
553 }
554
555#if 0
556 mat->waxiness = 0.;
557 if (cmat) {
558 for (size_t i=0; i<cmat->extensions_count; i++) {
559 cgltf_extension *ext = &cmat->extensions[i];
560 if (strcmp(ext->name,"NAEV_ext")!=0)
561 continue;
562 jsmn_parser p;
563 jsmntok_t t[16]; /* Max number of expected tokens. */
564 jsmn_init(&p);
565 int r = jsmn_parse( &p, ext->data, strlen(ext->data), t, sizeof(t)/sizeof(jsmntok_t) );
566 for (int j=0; j<r; j++) {
567 jsmntok_t *tj = &t[j];
568 const char *str = "waxFactor";
569 if (strncmp( str, &ext->data[tj->start], MIN(strlen(str),(size_t)(tj->end-tj->start)) )==0) {
570 if (j+1 >= r)
571 break;
572 mat->waxiness = atof(&ext->data[t[j+1].start]);
573 break;
574 }
575 }
576 }
577 }
578#endif
579
580 return 0;
581}
582
589static GLuint gltf_loadVBO( const cgltf_accessor *acc, cgltf_float **data,
590 cgltf_size *datasize )
591{
592 GLuint vid;
593 cgltf_size num = cgltf_accessor_unpack_floats( acc, NULL, 0 );
594 cgltf_float *dat = calloc( num, sizeof( cgltf_float ) );
595 cgltf_accessor_unpack_floats( acc, dat, num );
596 if ( data != NULL ) {
597 *data = dat;
598 *datasize = num;
599 }
600
601 /* OpenGL magic. */
602 gl_contextSet();
603 glGenBuffers( 1, &vid );
604 glBindBuffer( GL_ARRAY_BUFFER, vid );
605 glBufferData( GL_ARRAY_BUFFER, sizeof( cgltf_float ) * num, dat,
606 GL_STATIC_DRAW );
607 glBindBuffer( GL_ARRAY_BUFFER, 0 );
608 gl_checkErr();
609 gl_contextUnset();
610
611 if ( data == NULL )
612 free( dat );
613 return vid;
614}
615
619static int gltf_loadMesh( GltfObject *obj, const cgltf_data *data, Mesh *mesh,
620 const cgltf_mesh *cmesh )
621{
622 mesh->primitives =
623 calloc( cmesh->primitives_count, sizeof( MeshPrimitive ) );
624 mesh->nprimitives = cmesh->primitives_count;
625 for ( size_t i = 0; i < cmesh->primitives_count; i++ ) {
626 MeshPrimitive *prim = &mesh->primitives[i];
627 const cgltf_primitive *cprim = &cmesh->primitives[i];
628 const cgltf_accessor *acc = cprim->indices;
629 cgltf_float *rawdata = NULL;
630 cgltf_size datasize;
631 if ( acc == NULL ) {
632 prim->material = -1;
633 continue;
634 }
635
636 cgltf_size num = cgltf_num_components( acc->type ) * acc->count;
637 GLuint *idx = calloc( num, sizeof( cgltf_uint ) );
638 for ( size_t j = 0; j < num; j++ )
639 cgltf_accessor_read_uint( acc, j, &idx[j], 1 );
640
641 /* Check material. */
642 if ( cprim->material != NULL )
643 prim->material = cgltf_material_index( data, cprim->material );
644 else
645 prim->material = -1;
646
647 /* Store indices. */
648 gl_contextSet();
649 glGenBuffers( 1, &prim->vbo_idx );
650 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, prim->vbo_idx );
651 glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( cgltf_uint ) * num, idx,
652 GL_STATIC_DRAW );
653 prim->nidx = acc->count;
654 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
655 gl_checkErr();
656 gl_contextUnset();
657 free( idx );
658
659 for ( size_t j = 0; j < cprim->attributes_count; j++ ) {
660 const cgltf_attribute *attr = &cprim->attributes[j];
661 switch ( attr->type ) {
662 case cgltf_attribute_type_position:
663 prim->vbo_pos = gltf_loadVBO( attr->data, &rawdata, &datasize );
664 break;
665
666 case cgltf_attribute_type_normal:
667 prim->vbo_nor = gltf_loadVBO( attr->data, NULL, NULL );
668 break;
669
670 case cgltf_attribute_type_texcoord:
671 if ( attr->index == 0 )
672 prim->vbo_tex0 = gltf_loadVBO( attr->data, NULL, NULL );
673 else
674 prim->vbo_tex1 = gltf_loadVBO( attr->data, NULL, NULL );
675 /* TODO handle other cases? */
676 break;
677
678 case cgltf_attribute_type_color:
679 case cgltf_attribute_type_tangent:
680 default:
681 break;
682 }
683 }
684
685 /* Try to figure out dimensions. */
686 if ( rawdata != NULL ) {
687 /* Try to find associated node. */
688 for ( size_t n = 0; n < obj->nnodes; n++ ) {
689 Node *node = &obj->nodes[n];
690 if ( cmesh != data->nodes[n].mesh )
691 continue;
692 mat4 H;
693 cgltf_node_transform_world( &data->nodes[n], H.ptr );
694 for ( unsigned int di = 0; di < datasize; di += 3 ) {
695 vec3 v, d;
696 for ( unsigned int dj = 0; dj < 3; dj++ )
697 d.v[dj] = rawdata[di + dj];
698 // mat4_mul_vec( &v, &node->H, &d );
699 mat4_mul_vec( &v, &H, &d );
700 vec3_min( &node->aabb_min, &v, &node->aabb_min );
701 vec3_max( &node->aabb_max, &v, &node->aabb_max );
702 node->radius = MAX( node->radius, vec3_length( &v ) );
703 }
704 }
705 free( rawdata );
706 }
707 }
708 return 0;
709}
710
714static int gltf_loadNode( GltfObject *obj, const cgltf_data *data, Node *node,
715 const cgltf_node *cnode )
716{
717 /* Get transform for node. */
718 cgltf_node_transform_local( cnode, node->Horig.ptr );
719 node->H = node->Horig; /* Copy over. */
720 // node->parent = cgltf_node_index( data, cnode->parent );
721
722 if ( cnode->has_rotation )
723 memcpy( node->nt.r.q, cnode->rotation, sizeof( cnode->rotation ) );
724 else {
725 node->nt.r.q[0] = 0.;
726 node->nt.r.q[1] = 0.;
727 node->nt.r.q[3] = 0.;
728 node->nt.r.q[3] = 1.;
729 }
730 if ( cnode->has_translation )
731 memcpy( node->nt.t.v, cnode->translation, sizeof( cnode->translation ) );
732 else {
733 node->nt.t.v[0] = 0.;
734 node->nt.t.v[1] = 0.;
735 node->nt.t.v[2] = 0.;
736 }
737 if ( cnode->has_scale )
738 memcpy( node->nt.s.v, cnode->scale, sizeof( cnode->scale ) );
739 else {
740 node->nt.s.v[0] = 1.;
741 node->nt.s.v[1] = 1.;
742 node->nt.s.v[2] = 1.;
743 }
744 node->ntorig = node->nt;
745
746 /* Get the mesh. */
747 if ( cnode->mesh != NULL )
748 node->mesh = cgltf_mesh_index( data, cnode->mesh );
749 else
750 node->mesh = -1;
751
752 /* Handle extras. */
753 if ( cnode->extras.data != NULL ) {
754 char buf[STRMAX_SHORT];
755 cgltf_size len = sizeof( buf );
756 cgltf_copy_extras_json( data, &cnode->extras, buf, &len );
757 jsmn_parser p;
758 jsmntok_t t[32]; /* Max number of expected tokens. */
759 jsmn_init( &p );
760 int r = jsmn_parse( &p, buf, len, t, sizeof( t ) / sizeof( jsmntok_t ) );
761 for ( int j = 0; j < r; j++ ) {
762 const jsmntok_t *tj = &t[j];
763 /* Handle trail generators, which are stored as properties on nodes. */
764 const char *strtrail = "NAEV_trail_generator";
765 if ( strncmp( strtrail, &buf[tj->start],
766 MIN( strlen( strtrail ),
767 (size_t)( tj->end - tj->start ) ) ) == 0 ) {
768 GltfTrail trail;
769 mat4 H;
770 vec3 v = { .v = {
771 0.,
772 0.,
773 0.,
774 } };
775 if ( j + 1 >= r )
776 break;
777 trail.generator = calloc( 1, t[j + 1].end - t[j + 1].start + 1 );
778 strncpy( trail.generator, &cnode->extras.data[t[j + 1].start],
779 t[j + 1].end - t[j + 1].start );
780 trail.generator[t[j + 1].end - t[j + 1].start] = '\0';
781 cgltf_node_transform_world( cnode, H.ptr );
782 mat4_mul_vec( &trail.pos, &H, &v );
783 if ( obj->trails == NULL )
784 obj->trails = array_create( GltfTrail );
785 array_push_back( &obj->trails, trail );
786 break;
787 }
788 /* Handle mount points, also stored as extras. */
789 const char *strmount = "NAEV_weapon_mount";
790 if ( strncmp( strmount, &buf[tj->start],
791 MIN( strlen( strmount ),
792 (size_t)( tj->end - tj->start ) ) ) == 0 ) {
793 GltfMount mount;
794 mat4 H;
795 vec3 v = { .v = {
796 0.,
797 0.,
798 0.,
799 } };
800 if ( j + 1 >= r )
801 break;
802 mount.id = atoi( &cnode->extras.data[t[j + 1].start] );
803 cgltf_node_transform_world( cnode, H.ptr );
804 mat4_mul_vec( &mount.pos, &H, &v );
805 if ( obj->mounts == NULL )
806 obj->mounts = array_create( GltfMount );
807 array_push_back( &obj->mounts, mount );
808 break;
809 }
810 }
811 }
812
813 /* Iterate over children. */
814 node->nchildren = cnode->children_count;
815 if ( node->nchildren > 0 ) {
816 node->children = calloc( cnode->children_count, sizeof( size_t ) );
817 for ( cgltf_size i = 0; i < cnode->children_count; i++ )
818 node->children[i] = cgltf_node_index( data, cnode->children[i] );
819 }
820 return 0;
821}
822
823static int gltf_loadAnimation( GltfObject *obj, const cgltf_data *data,
824 Animation *anim, const cgltf_animation *canim )
825{
826 anim->nsamplers = canim->samplers_count;
827 anim->samplers = calloc( anim->nsamplers, sizeof( AnimationSampler ) );
828 for ( cgltf_size i = 0; i < canim->samplers_count; i++ ) {
829 const cgltf_animation_sampler *csamp = &canim->samplers[i];
830 AnimationSampler *samp = &anim->samplers[i];
831 cgltf_size n;
832
833 switch ( csamp->interpolation ) {
834 case cgltf_interpolation_type_linear:
835 samp->interp = ANIM_INTER_LINEAR;
836 break;
837 case cgltf_interpolation_type_step:
838 samp->interp = ANIM_INTER_STEP;
839 break;
840 default:
841 WARN( _( "Unsupported interpolation type %d!" ),
842 csamp->interpolation );
843 break;
844 }
845
846 samp->n = cgltf_accessor_unpack_floats( csamp->input, NULL, 0 );
847 samp->time = calloc( samp->n, sizeof( cgltf_float ) );
848 cgltf_accessor_unpack_floats( csamp->input, samp->time, samp->n );
849 samp->max = samp->time[samp->n - 1];
850
851 samp->l = cgltf_num_components( csamp->output->type );
852 n = cgltf_accessor_unpack_floats( csamp->output, NULL, 0 );
853 if ( cgltf_num_components( csamp->output->type ) * samp->n != n )
854 WARN( _( "Wrong number of elements. Got %lu, but expected %lu!" ),
855 samp->n * cgltf_num_components( csamp->output->type ), n );
856 samp->data = calloc( n, sizeof( cgltf_float ) );
857 cgltf_accessor_unpack_floats( csamp->output, samp->data, n );
858 }
859
860 anim->nchannels = canim->channels_count;
861 anim->channels = calloc( anim->nchannels, sizeof( AnimationChannel ) );
862 for ( cgltf_size i = 0; i < canim->channels_count; i++ ) {
863 const cgltf_animation_channel *cchan = &canim->channels[i];
864 AnimationChannel *chan = &anim->channels[i];
865 chan->sampler =
866 &anim
867 ->samplers[cgltf_animation_sampler_index( canim, cchan->sampler )];
868 chan->target = &obj->nodes[cgltf_node_index( data, cchan->target_node )];
869 switch ( cchan->target_path ) {
870 case cgltf_animation_path_type_translation:
871 chan->type = ANIM_TYPE_TRANSLATION;
872 break;
873 case cgltf_animation_path_type_rotation:
874 chan->type = ANIM_TYPE_ROTATION;
875 break;
876 case cgltf_animation_path_type_scale:
877 chan->type = ANIM_TYPE_SCALE;
878 break;
879 default:
880 WARN( _( "Uknown animation type %d!" ), cchan->target_path );
881 break;
882 }
883 }
884 return 0;
885}
886
887static void shadow_matrix( mat4 *m, const Light *light )
888{
889 const vec3 up = { .v = { 0., 1., 0. } };
890 const vec3 center = { .v = { 0., 0., 0. } };
891 const float r = 1.0;
892 if ( light->sun ) {
893 vec3 light_pos = light->pos;
894 vec3_normalize( &light_pos );
895 const mat4 L = mat4_lookat( &light_pos, &center, &up );
896 const mat4 O = mat4_ortho( -r, r, -r, r, 0.0, 2.0 );
897 mat4_mul( m, &L, &O );
898 } else {
899 /* TODO fix this. Point lights should use perspective matrix... */
900 const vec3 light_pos = light->pos;
901 const mat4 L = mat4_lookat( &light_pos, &center, &up );
902 const float norm = vec3_length( &light_pos );
903 const mat4 O = mat4_ortho( -r, r, -r, r, norm - 1.0, norm + 1.0 );
904 mat4_mul( m, &L, &O );
905 }
906}
907
911static void renderMeshPrimitiveShadow( const GltfObject *obj,
912 const MeshPrimitive *mesh,
913 const mat4 *H )
914{
915 (void)obj;
916 const Shader *shd = &shadow_shader;
917
918 /* Skip with no shadows. */
919 if ( ( mesh->material >= 0 ) &&
920 ( obj->materials[mesh->material].noshadows ) )
921 return;
922
923 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->vbo_idx );
924
925 /* TODO put everything in a single VBO */
926 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo_pos );
927 glVertexAttribPointer( shd->vertex, 3, GL_FLOAT, GL_FALSE, 0, NULL );
928 glEnableVertexAttribArray( shd->vertex );
929
930 glUniformMatrix4fv( shd->Hmodel, 1, GL_FALSE, H->ptr );
931 glDrawElements( GL_TRIANGLES, mesh->nidx, GL_UNSIGNED_INT, 0 );
932}
933static void renderMeshShadow( const GltfObject *obj, const Mesh *mesh,
934 const mat4 *H )
935{
936 for ( int i = 0; i < mesh->nprimitives; i++ )
937 renderMeshPrimitiveShadow( obj, &mesh->primitives[i], H );
938}
939
943static void renderMeshPrimitive( const GltfObject *obj,
944 const MeshPrimitive *mesh, const mat4 *H )
945{
946 const Material *mat;
947 const Shader *shd = &gltf_shader;
948
949 if ( mesh->material < 0 )
950 mat = &material_default;
951 else
952 mat = &obj->materials[mesh->material];
953
954 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->vbo_idx );
955
956 /* TODO put everything in a single VBO */
957 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo_pos );
958 glVertexAttribPointer( shd->vertex, 3, GL_FLOAT, GL_FALSE, 0, NULL );
959 glEnableVertexAttribArray( shd->vertex );
960 if ( mesh->vbo_nor ) {
961 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo_nor );
962 glVertexAttribPointer( shd->vertex_normal, 3, GL_FLOAT, GL_FALSE, 0,
963 NULL );
964 glEnableVertexAttribArray( shd->vertex_normal );
965 }
966 gl_checkErr();
967 if ( mesh->vbo_tex0 ) {
968 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo_tex0 );
969 glVertexAttribPointer( shd->vertex_tex0, 2, GL_FLOAT, GL_FALSE, 0, NULL );
970 glEnableVertexAttribArray( shd->vertex_tex0 );
971 }
972 gl_checkErr();
973 if ( mesh->vbo_tex1 ) {
974 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo_tex1 );
975 glVertexAttribPointer( shd->vertex_tex1, 2, GL_FLOAT, GL_FALSE, 0, NULL );
976 glEnableVertexAttribArray( shd->vertex_tex1 );
977 }
978 gl_checkErr();
979
980 /* Set up shader. */
981 glUseProgram( shd->program );
982
983 /* Compute normal matrix. */
984 mat3 Hnormal;
985 mat3_from_mat4( &Hnormal, H );
986 mat3_invert( &Hnormal );
987 mat3_transpose( &Hnormal );
988
989 /* Pass the uniforms. */
990 glUniformMatrix4fv( shd->Hmodel, 1, GL_FALSE, H->ptr );
991 glUniformMatrix3fv( shd->Hnormal, 1, GL_FALSE, Hnormal.ptr );
992 glUniform1f( shd->metallicFactor, mat->metallicFactor );
993 glUniform1f( shd->roughnessFactor, mat->roughnessFactor );
994 glUniform4f( shd->baseColour, mat->baseColour[0], mat->baseColour[1],
995 mat->baseColour[2], mat->baseColour[3] );
996#if 0
997 glUniform3f( shd->sheenTint, mat->sheen[0], mat->sheen[1], mat->sheen[2] );
998 glUniform1f( shd->sheen, mat->sheen_roughness );
999 glUniform1f( shd->clearcoat, mat->clearcoat );
1000 glUniform1f( shd->clearcoat_roughness, mat->clearcoat_roughness );
1001#endif
1002 glUniform3f( shd->emissive, mat->emissiveFactor[0], mat->emissiveFactor[1],
1003 mat->emissiveFactor[2] );
1004 glUniform1i( shd->blend, mat->blend );
1005 if ( use_normal_mapping ) {
1006 glUniform1i( shd->u_has_normal, ( mat->normal_tex.tex != tex_zero.tex ) );
1007 glUniform1f( shd->normal_scale, mat->normal_tex.strength );
1008 glUniform1i( shd->normal_texcoord, mat->normal_tex.texcoord );
1009 }
1010 // glUniform1f( shd->waxiness, mat->waxiness );
1011 /* Texture coordinates. */
1012 glUniform1i( shd->baseColour_texcoord, mat->baseColour_tex.texcoord );
1013 glUniform1i( shd->metallic_texcoord, mat->metallic_tex.texcoord );
1014 glUniform1i( shd->emissive_texcoord, mat->emissive_tex.texcoord );
1015 if ( use_ambient_occlusion )
1016 glUniform1i( shd->occlusion_texcoord, mat->occlusion_tex.texcoord );
1017 gl_checkErr();
1018
1019 /* Texture. */
1020 glActiveTexture( GL_TEXTURE1 );
1021 glBindTexture( GL_TEXTURE_2D, mat->metallic_tex.tex );
1022 glUniform1i( shd->metallic_tex, 1 );
1023 glActiveTexture( GL_TEXTURE2 );
1024 glBindTexture( GL_TEXTURE_2D, mat->emissive_tex.tex );
1025 glUniform1i( shd->emissive_tex, 2 );
1026 if ( use_normal_mapping ) {
1027 glActiveTexture( GL_TEXTURE3 );
1028 glBindTexture( GL_TEXTURE_2D, mat->normal_tex.tex );
1029 glUniform1i( shd->normal_tex, 3 );
1030 }
1031 if ( use_ambient_occlusion ) {
1032 glActiveTexture( GL_TEXTURE4 );
1033 glBindTexture( GL_TEXTURE_2D, mat->occlusion_tex.tex );
1034 glUniform1i( shd->occlusion_tex, 4 );
1035 }
1036 /* Have to have GL_TEXTURE0 be last. */
1037 glActiveTexture( GL_TEXTURE0 );
1038 glBindTexture( GL_TEXTURE_2D, mat->baseColour_tex.tex );
1039 glUniform1i( shd->baseColour_tex, 0 );
1040 gl_checkErr();
1041
1042 if ( mat->double_sided )
1043 glDisable( GL_CULL_FACE );
1044 if ( mat->blend ) /* Don't write depth for transparent objects. */
1045 glDepthMask( GL_FALSE );
1046 glDrawElements( GL_TRIANGLES, mesh->nidx, GL_UNSIGNED_INT, 0 );
1047 if ( mat->double_sided )
1048 glEnable( GL_CULL_FACE );
1049 if ( mat->blend )
1050 glDepthMask( GL_TRUE );
1051}
1052static void renderMesh( const GltfObject *obj, const Mesh *mesh, const mat4 *H )
1053{
1054 for ( int i = 0; i < mesh->nprimitives; i++ )
1055 renderMeshPrimitive( obj, &mesh->primitives[i], H );
1056}
1057
1061static void gltf_renderNodeShadow( const GltfObject *obj, const Node *node,
1062 const mat4 *H )
1063{
1064 /* Multiply matrices, can be animated so not caching. */
1065 /* TODO cache when not animated. */
1066 mat4 HH = node->H;
1067 mat4_apply( &HH, H );
1068
1069 /* Draw mesh. */
1070 if ( node->mesh >= 0 )
1071 renderMeshShadow( obj, &obj->meshes[node->mesh], &HH );
1072
1073 /* Draw children. */
1074 for ( size_t i = 0; i < node->nchildren; i++ )
1075 gltf_renderNodeShadow( obj, &obj->nodes[node->children[i]], &HH );
1076
1077 gl_checkErr();
1078}
1079
1083static void gltf_renderNodeMesh( const GltfObject *obj, const Node *node,
1084 const mat4 *H )
1085{
1086 /* Multiply matrices, can be animated so not caching. */
1087 /* TODO cache when not animated. */
1088 mat4 HH = node->H;
1089 mat4_apply( &HH, H );
1090
1091 /* If determinant is negative, we have to invert winding. */
1092 mat3 m;
1093 mat3_from_mat4( &m, &HH );
1094 glFrontFace( ( mat3_det( &m ) < 0. ) ? GL_CW : GL_CCW );
1095
1096 /* Draw mesh. */
1097 if ( node->mesh >= 0 )
1098 renderMesh( obj, &obj->meshes[node->mesh], &HH );
1099
1100 /* Draw children. */
1101 for ( size_t i = 0; i < node->nchildren; i++ )
1102 gltf_renderNodeMesh( obj, &obj->nodes[node->children[i]], &HH );
1103
1104 gl_checkErr();
1105}
1106
1107static void gltf_renderShadow( const GltfObject *obj, int scene, const mat4 *H,
1108 const Light *light, int i )
1109{
1110 (void)light;
1111 const Shader *shd = &shadow_shader;
1112
1113 /* Set up the shadow map and render. */
1114 glBindFramebuffer( GL_FRAMEBUFFER, light_fbo[i] );
1115 glClear( GL_DEPTH_BUFFER_BIT );
1116 glViewport( 0, 0, shadowmap_size, shadowmap_size );
1117
1118 /* Set up shader. */
1119 glUseProgram( shd->program );
1120 glUniformMatrix4fv( shd->Hshadow, 1, GL_FALSE, light_mat[i].ptr );
1121
1122 for ( size_t j = 0; j < obj->scenes[scene].nnodes; j++ )
1123 gltf_renderNodeShadow( obj, &obj->nodes[obj->scenes[scene].nodes[j]], H );
1124
1125 glDisable( GL_CULL_FACE );
1126 gl_checkErr();
1127
1128 /* Now we have to blur. We'll do a separable filter approach and do two
1129 * passes. */
1130 /* First pass for X. */
1131 shd = &shadow_shader_blurX;
1132 glBindFramebuffer( GL_FRAMEBUFFER, *shadow_fbo );
1133 glClear( GL_DEPTH_BUFFER_BIT );
1134 glUseProgram( shd->program );
1135
1136 glBindBuffer( GL_ARRAY_BUFFER, shadow_vbo );
1137 glVertexAttribPointer( shd->vertex, 2, GL_FLOAT, GL_FALSE, 0, NULL );
1138 glEnableVertexAttribArray( shd->vertex );
1139
1140 glActiveTexture( GL_TEXTURE0 );
1141 glBindTexture( GL_TEXTURE_2D, light_tex[i] );
1142
1143 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1144 gl_checkErr();
1145 /* Second pass for Y and back into the proper framebuffer. */
1146 shd = &shadow_shader_blurY;
1147 glBindFramebuffer( GL_FRAMEBUFFER, light_fbo[i] );
1148 glClear( GL_DEPTH_BUFFER_BIT );
1149
1150 glUseProgram( shd->program );
1151
1152 glBindBuffer( GL_ARRAY_BUFFER, shadow_vbo );
1153 glVertexAttribPointer( shd->vertex, 2, GL_FLOAT, GL_FALSE, 0, NULL );
1154 glEnableVertexAttribArray( shd->vertex );
1155
1156 glActiveTexture( GL_TEXTURE0 );
1157 glBindTexture( GL_TEXTURE_2D, *shadow_tex );
1158
1159 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1160 /* Clean up. */
1161 glBindBuffer( GL_ARRAY_BUFFER, 0 );
1162 glDisableVertexAttribArray( shd->vertex );
1163 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1164 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
1165
1166 gl_checkErr();
1167}
1168
1169static void gltf_renderMesh( const GltfObject *obj, int scene, const mat4 *H,
1170 const Lighting *L )
1171{
1172 /* Load constant stuff. */
1173 const Shader *shd = &gltf_shader;
1174 glUseProgram( shd->program );
1175 glUniform3f( shd->u_ambient, L->ambient_r, L->ambient_g, L->ambient_b );
1176 glUniform1i( shd->nlights, L->nlights );
1177 for ( int i = 0; i < L->nlights; i++ ) {
1178 const Light *l = &L->lights[i];
1179 const ShaderLight *sl = &shd->lights[i];
1180
1181 /* Other parameters. */
1182 glUniform1i( sl->sun, l->sun );
1183 if ( l->sun ) {
1184 /* Normalize here istead of frag shader. */
1185 vec3 light_pos = l->pos;
1186 vec3_normalize( &light_pos );
1187 glUniform3f( sl->position, light_pos.v[0], light_pos.v[1],
1188 light_pos.v[2] );
1189 } else
1190 glUniform3f( sl->position, l->pos.v[0], l->pos.v[1], l->pos.v[2] );
1191 glUniform3f( sl->colour, l->colour.v[0], l->colour.v[1], l->colour.v[2] );
1192 glUniform1f( sl->intensity, l->intensity * L->intensity );
1193
1194 /* Set up matrix. */
1195 glUniformMatrix4fv( sl->Hshadow, 1, GL_FALSE, light_mat[i].ptr );
1196
1197 /* Set up textures. */
1198 glActiveTexture( GL_TEXTURE5 + i );
1199 glBindTexture( GL_TEXTURE_2D, light_tex[i] );
1200 glUniform1i( shd->lights[i].shadowmap_tex, 5 + i );
1201 }
1202 gl_checkErr();
1203
1204 /* Cull faces. */
1205 glEnable( GL_CULL_FACE );
1206 for ( size_t i = 0; i < obj->scenes[scene].nnodes; i++ )
1207 gltf_renderNodeMesh( obj, &obj->nodes[obj->scenes[scene].nodes[i]], H );
1208
1209 glBindTexture( GL_TEXTURE_2D, 0 );
1210 glBindBuffer( GL_ARRAY_BUFFER, 0 );
1211 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
1212
1213 gl_checkErr();
1214}
1215
1216static void gltf_applyAnimNode( GltfObject *obj, Animation *anim, GLfloat time )
1217{
1218 (void)obj;
1219 for ( size_t j = 0; j < anim->nchannels; j++ ) {
1220 const AnimationChannel *chan = &anim->channels[j];
1221 const AnimationSampler *samp = chan->sampler;
1222 int pi, ni;
1223 GLfloat p, n, mix;
1224 GLfloat t = fmod( time, samp->max );
1225 Node *node = chan->target;
1226
1227 /* See if target has animation, and initialize. */
1228 if ( !node->has_anim ) {
1229 node->nt = node->ntorig;
1230 node->has_anim = 1;
1231 }
1232
1233 /* TODO something better than linear search. */
1234 pi = 0;
1235 for ( size_t i = 0; i < samp->n; i++ ) {
1236 if ( samp->time[i] < t ) {
1237 pi = i;
1238 break;
1239 }
1240 }
1241 ni = ( pi + 1 ) % samp->n;
1242 p = samp->time[pi];
1243 n = samp->time[ni];
1244
1245 /* Interpolate. */
1246 switch ( samp->interp ) {
1247 case ANIM_INTER_LINEAR:
1248 mix = ( t - p ) / ( n - p );
1249 break;
1250 case ANIM_INTER_STEP:
1251 default:
1252 mix = 0.;
1253 break;
1254 }
1255 /* Apply. */
1256 switch ( chan->type ) {
1257 case ANIM_TYPE_ROTATION:
1258 quat_slerp( node->nt.r.q, &samp->data[pi * samp->l],
1259 &samp->data[ni * samp->l], mix );
1260 break;
1261 case ANIM_TYPE_TRANSLATION:
1262 for ( size_t i = 0; i < samp->l; i++ )
1263 node->nt.t.v[i] = samp->data[pi * samp->l + i] * ( 1. - mix ) +
1264 samp->data[ni * samp->l + i] * mix;
1265 break;
1266 case ANIM_TYPE_SCALE:
1267 for ( size_t i = 0; i < samp->l; i++ )
1268 node->nt.s.v[i] = samp->data[pi * samp->l + i] * ( 1. - mix ) +
1269 samp->data[ni * samp->l + i] * mix;
1270 break;
1271 }
1272 }
1273}
1274
1275static void gltf_applyAnim( GltfObject *obj, GLfloat time )
1276{
1277 if ( obj->nanimations <= 0 )
1278 return;
1279
1280 for ( size_t i = 0; i < obj->nnodes; i++ )
1281 obj->nodes[i].has_anim = 0;
1282 for ( size_t i = 0; i < obj->nanimations; i++ )
1283 gltf_applyAnimNode( obj, &obj->animations[i], time );
1284 for ( size_t i = 0; i < obj->nnodes; i++ ) {
1285 Node *node = &obj->nodes[i];
1286 if ( !node->has_anim )
1287 continue;
1288 mat4_trs( &node->H, &node->nt.t, &node->nt.r, &node->nt.s );
1289 }
1290}
1291
1298void gltf_render( GLuint fb, GltfObject *obj, const mat4 *H, GLfloat time,
1299 double size )
1300{
1301 return gltf_renderScene( fb, obj, obj->scene_body, H, time, size, 0 );
1302}
1303
1304void gltf_renderScene( GLuint fb, GltfObject *obj, int scene, const mat4 *H,
1305 GLfloat time, double size, const Lighting *L )
1306{
1307 if ( scene < 0 )
1308 return;
1309 const GLfloat sca = 1.0 / obj->radius;
1310 const mat4 Hscale = { .m = { { sca, 0.0, 0.0, 0.0 },
1311 { 0.0, sca, 0.0, 0.0 },
1312 { 0.0, 0.0, -sca, 0.0 },
1313 { 0.0, 0.0, 0.0, 1.0 } } };
1314 mat4 Hptr;
1315
1316 /* Choose lighting stuff based on size. */
1317 if ( size > 255. ) {
1318 shadowmap_size = SHADOWMAP_SIZE_HIGH;
1319 light_fbo = light_fbo_high;
1320 light_tex = light_tex_high;
1321 shadow_fbo = &shadow_fbo_high;
1322 shadow_tex = &shadow_tex_high;
1323 } else {
1324 shadowmap_size = SHADOWMAP_SIZE_LOW;
1325 light_fbo = light_fbo_low;
1326 light_tex = light_tex_low;
1327 shadow_fbo = &shadow_fbo_low;
1328 shadow_tex = &shadow_tex_low;
1329 }
1330
1331 /* Apply scaling. */
1332 if ( H == NULL )
1333 Hptr = Hscale;
1334 else {
1335 Hptr = *H;
1336 mat4_apply( &Hptr, &Hscale );
1337 }
1338
1339 /* Do animations if applicable. */
1340 gltf_applyAnim( obj, time );
1341
1342 if ( L == NULL ) {
1343 /* Use default light and shadow matrices. */
1344 L = &L_default;
1345 light_mat = light_mat_def;
1346 } else {
1347 /* Compute shadow matrices. */
1348 for ( int i = 0; i < L->nlights; i++ )
1349 shadow_matrix( &light_mat_alt[i], &L->lights[i] );
1350 light_mat = light_mat_alt;
1351 }
1352
1353 /* Set up blend mode. */
1354 glEnable( GL_BLEND );
1355 glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
1356 GL_ONE_MINUS_SRC_ALPHA );
1357
1358 /* Depth testing. */
1359 glEnable( GL_DEPTH_TEST );
1360 glDepthFunc( GL_LESS );
1361
1362 /* Render shadows for each light. */
1363 glCullFace( GL_FRONT );
1364 for ( int i = 0; i < L->nlights; i++ )
1365 gltf_renderShadow( obj, scene, &Hptr, &L->lights[i], i );
1366
1367 /* Finally render the scene. */
1368 glViewport( 0, 0, size, size );
1369 glBindFramebuffer( GL_FRAMEBUFFER, fb );
1370 gltf_renderMesh( obj, scene, &Hptr, L );
1371
1372 /* Some clean up. */
1373 glDisable( GL_CULL_FACE );
1374 glDisable( GL_DEPTH_TEST );
1375 glUseProgram( 0 );
1376#ifdef HAVE_NAEV
1377 glViewport( 0, 0, gl_screen.rw, gl_screen.rh );
1378#endif /* HAVE_NAEV */
1379}
1380
1381static cgltf_result
1382gltf_read( const struct cgltf_memory_options *memory_options,
1383 const struct cgltf_file_options *file_options, const char *path,
1384 cgltf_size *size, void **data )
1385{
1386 (void)file_options;
1387 PHYSFS_Stat path_stat;
1388
1389 void *( *memory_alloc )( void *, cgltf_size ) =
1390 memory_options->alloc_func ? memory_options->alloc_func
1391 : &cgltf_default_alloc;
1392 void ( *memory_free )( void *, void * ) = memory_options->free_func
1393 ? memory_options->free_func
1394 : &cgltf_default_free;
1395
1396 if ( !PHYSFS_stat( path, &path_stat ) ) {
1397 WARN( _( "File '%s' not found!" ), path );
1398 return cgltf_result_file_not_found;
1399 }
1400
1401 cgltf_size file_size = size ? *size : 0;
1402 if ( file_size == 0 )
1403 file_size = path_stat.filesize;
1404
1405 char *file_data =
1406 (char *)memory_alloc( memory_options->user_data, file_size );
1407 if ( !file_data )
1408 return cgltf_result_out_of_memory;
1409
1410 PHYSFS_file *pfile = PHYSFS_openRead( path );
1411 if ( pfile == NULL ) {
1412 WARN( _( "Unable to open '%s' for reading: %s" ), path,
1413 PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) );
1414 return cgltf_result_file_not_found;
1415 }
1416 cgltf_size read_size = PHYSFS_readBytes( pfile, file_data, file_size );
1417 PHYSFS_close( pfile );
1418
1419 if ( read_size != file_size ) {
1420 memory_free( memory_options->user_data, file_data );
1421 return cgltf_result_io_error;
1422 }
1423
1424 if ( size )
1425 *size = file_size;
1426 if ( data )
1427 *data = file_data;
1428
1429 return cgltf_result_success;
1430}
1431
1432/* TODO replace _Thread_local and friends with SDL_qsort_r when we switch to
1433 * SDL3. */
1434static _Thread_local const GltfObject *cmp_obj;
1435static int cmp_node( const void *p1, const void *p2 )
1436{
1437 const Node *n1 = &cmp_obj->nodes[*(size_t *)p1];
1438 const Node *n2 = &cmp_obj->nodes[*(size_t *)p2];
1439 int b1 = 0;
1440 int b2 = 0;
1441 int b;
1442 if ( n1->mesh >= 0 ) {
1443 const Mesh *m = &cmp_obj->meshes[n1->mesh];
1444 for ( int i = 0; i < m->nprimitives; i++ ) {
1445 int mat = m->primitives[i].material;
1446 if ( mat >= 0 )
1447 b1 |= cmp_obj->materials[mat].blend;
1448 }
1449 }
1450 if ( n2->mesh >= 0 ) {
1451 const Mesh *m = &cmp_obj->meshes[n2->mesh];
1452 for ( int i = 0; i < m->nprimitives; i++ ) {
1453 int mat = m->primitives[i].material;
1454 if ( mat >= 0 )
1455 b2 |= cmp_obj->materials[mat].blend;
1456 }
1457 }
1458 b = b1 - b2;
1459 if ( b )
1460 return b;
1461 return 0;
1462}
1463static int cmp_mesh( const void *p1, const void *p2 )
1464{
1465 const MeshPrimitive *m1 = p1;
1466 const MeshPrimitive *m2 = p2;
1467 int b1 = ( m1->material >= 0 ) ? cmp_obj->materials[m1->material].blend : -1;
1468 int b2 = ( m2->material >= 0 ) ? cmp_obj->materials[m2->material].blend : -1;
1469 int b = b1 - b2;
1470 if ( b )
1471 return b;
1472 return 0;
1473}
1474static int cmp_mount( const void *p1, const void *p2 )
1475{
1476 const GltfMount *m1 = p1;
1477 const GltfMount *m2 = p2;
1478 return m1->id - m2->id;
1479}
1480static int cmp_trail( const void *p1, const void *p2 )
1481{
1482 const GltfTrail *t1 = p1;
1483 const GltfTrail *t2 = p2;
1484 double d = t1->pos.v[1] - t2->pos.v[1];
1485 if ( d < 0. )
1486 return -1;
1487 else if ( d > 0. )
1488 return +1;
1489 return 0;
1490}
1491
1492static const char *gltf_error_str( cgltf_result result )
1493{
1494 switch ( result ) {
1495 case cgltf_result_success:
1496 return p_( "cgltf", "success" );
1497 case cgltf_result_data_too_short:
1498 return p_( "cgltf", "data too short" );
1499 case cgltf_result_unknown_format:
1500 return p_( "cgltf", "unknown format" );
1501 case cgltf_result_invalid_json:
1502 return p_( "cgltf", "invalid json" );
1503 case cgltf_result_invalid_gltf:
1504 return p_( "cgltf", "invalid gltf" );
1505 case cgltf_result_invalid_options:
1506 return p_( "cgltf", "invalid options" );
1507 case cgltf_result_file_not_found:
1508 return p_( "cgltf", "file not found" );
1509 case cgltf_result_io_error:
1510 return p_( "cgltf", "io error" );
1511 case cgltf_result_out_of_memory:
1512 return p_( "cgltf", "out of memory" );
1513 case cgltf_result_legacy_gltf:
1514 return p_( "cgltf", "legacy gltf" );
1515 default:
1516 return NULL;
1517 }
1518}
1519
1526GltfObject *gltf_loadFromFile( const char *filename )
1527{
1528 GltfObject *obj;
1529 cgltf_result res;
1530 cgltf_data *data;
1531 cgltf_options opts;
1532 char *dirpath;
1533 int new;
1534 memset( &opts, 0, sizeof( opts ) );
1535
1536 /* Initialize object. */
1537 obj = cache_get( filename, &new );
1538 if ( !new ) {
1539 /* Horribly spinlock. TODO fix, but it'll go away with rust... */
1540 while ( !obj->loaded )
1541 SDL_Delay( 1 );
1542 return obj;
1543 }
1544
1545 /* Set up the gltf path. */
1546 dirpath = strdup( filename );
1547#ifdef HAVE_NAEV
1548 SDL_asprintf( &obj->path, "%s", dirname( dirpath ) );
1549#else /* HAVE_NAEV */
1550 char mountpath[PATH_MAX];
1551 snprintf( mountpath, sizeof( mountpath ), "%s/%s",
1552 PHYSFS_getRealDir( filename ), dirname( dirpath ) );
1553 PHYSFS_mount( mountpath, "/", 0 ); /* Prefix so more priority. */
1554 obj->path = strdup( mountpath );
1555#endif /* HAVE_NAEV */
1556 free( dirpath );
1557
1558 /* Start loading the file. */
1559 opts.file.read = gltf_read;
1560 res = cgltf_parse_file( &opts, filename, &data );
1561 if ( res != cgltf_result_success ) {
1562 WARN( _( "Error loading GLTF file '%s': %s" ), filename,
1563 gltf_error_str( res ) );
1564 return NULL;
1565 }
1566
1567#if DEBUGGING
1568 /* Validate just in case. */
1569 res = cgltf_validate( data );
1570 if ( res != cgltf_result_success ) {
1571 WARN( _( "Error loading GLTF file '%s': %s" ), filename,
1572 gltf_error_str( res ) );
1573 return NULL;
1574 }
1575#endif /* DEBUGGING */
1576
1577 /* Will load from PHYSFS. */
1578 res = cgltf_load_buffers( &opts, data, filename );
1579 if ( res != cgltf_result_success ) {
1580 WARN( _( "Error loading GLTF file '%s': %s" ), filename,
1581 gltf_error_str( res ) );
1582 return NULL;
1583 }
1584
1585 /* Load materials. */
1586 obj->materials = calloc( data->materials_count, sizeof( Material ) );
1587 obj->nmaterials = data->materials_count;
1588 for ( size_t i = 0; i < data->materials_count; i++ )
1589 gltf_loadMaterial( obj, &obj->materials[i], &data->materials[i], data );
1590
1591 /* Load nodes. */
1592 obj->nodes = calloc( data->nodes_count, sizeof( Node ) );
1593 obj->nnodes = data->nodes_count;
1594 for ( size_t n = 0; n < obj->nnodes; n++ ) {
1595 const cgltf_node *cnode = &data->nodes[n];
1596 Node *node = &obj->nodes[n];
1597 gltf_loadNode( obj, data, node, cnode );
1598 }
1599
1600 /* Load animations, has to be before meshes so we can update rotation
1601 * information. */
1602 obj->animations = calloc( data->animations_count, sizeof( Animation ) );
1603 obj->nanimations = data->animations_count;
1604 for ( size_t i = 0; i < obj->nanimations; i++ )
1605 gltf_loadAnimation( obj, data, &obj->animations[i],
1606 &data->animations[i] );
1607 gltf_applyAnim(
1608 obj,
1609 0. ); /* Initial propagation of transformations for determining size. */
1610
1611 /* Load meshes, has to be after nodes so we can fill information backwards.
1612 */
1613 obj->meshes = calloc( data->meshes_count, sizeof( Mesh ) );
1614 obj->nmeshes = data->meshes_count;
1615 for ( size_t i = 0; i < data->meshes_count; i++ )
1616 gltf_loadMesh( obj, data, &obj->meshes[i], &data->meshes[i] );
1617
1618 /* Load scenes. */
1619 obj->scenes = calloc( data->scenes_count, sizeof( Scene ) );
1620 obj->nscenes = data->scenes_count;
1621 obj->scene_body = 0;
1622 obj->scene_engine = -1;
1623 for ( size_t s = 0; s < obj->nscenes; s++ ) {
1624 /* Load nodes. */
1625 const cgltf_scene *cscene =
1626 &data->scenes[s]; /* data->scene may be NULL */
1627 Scene *scene = &obj->scenes[s];
1628 if ( cscene->name != NULL ) {
1629 scene->name = strdup( cscene->name );
1630 if ( strcmp( scene->name, "body" ) == 0 )
1631 obj->scene_body = s;
1632 else if ( strcmp( scene->name, "engine" ) == 0 )
1633 obj->scene_engine = s;
1634 }
1635
1636 /* Set up and allocate scene. */
1637 scene->nodes = calloc( cscene->nodes_count, sizeof( size_t ) );
1638 scene->nnodes = cscene->nodes_count;
1639 for ( size_t i = 0; i < scene->nnodes; i++ )
1640 scene->nodes[i] = cgltf_node_index( data, cscene->nodes[i] );
1641 }
1642
1643 /* Get true radius. */
1644 for ( size_t i = 0; i < obj->nnodes; i++ )
1645 obj->radius = MAX( obj->radius, obj->nodes[i].radius );
1646
1647 /* Sort stuff afterwards so we can lock it. */
1648 cmp_obj = obj; /* For comparisons. */
1649 for ( size_t i = 0; i < obj->nmeshes; i++ ) {
1650 Mesh *m = &obj->meshes[i];
1651 qsort( m->primitives, m->nprimitives, sizeof( MeshPrimitive ), cmp_mesh );
1652 }
1653 for ( size_t s = 0; s < obj->nscenes; s++ ) {
1654 Scene *scene = &obj->scenes[s];
1655 qsort( scene->nodes, scene->nnodes, sizeof( size_t ), cmp_node );
1656 }
1657 cmp_obj = NULL; /* No more comparisons. */
1658
1659 /* Some post-processing. */
1660 /* Sort trails from lowest to highest so they get properly ordered in-game.
1661 */
1662 qsort( obj->trails, array_size( obj->trails ), sizeof( GltfTrail ),
1663 cmp_trail );
1664 for ( int i = 0; i < array_size( obj->trails ); i++ ) {
1665 GltfTrail *t = &obj->trails[i];
1666 vec3_scale( &t->pos, 1. / obj->radius );
1667 }
1668 /* Sort mounts to match ids. */
1669 qsort( obj->mounts, array_size( obj->mounts ), sizeof( GltfMount ),
1670 cmp_mount );
1671 for ( int i = 0; i < array_size( obj->mounts ); i++ ) {
1672 GltfMount *m = &obj->mounts[i];
1673 vec3_scale( &m->pos, 1. / obj->radius );
1674 if ( m->id != i )
1675 WARN( _( "gltf warning '%s': expected mount with id=%d, but got %d!" ),
1676 filename, i, m->id );
1677 }
1678
1679#ifndef HAVE_NAEV
1680 LOG( "Loaded %s", filename );
1681 LOG( " has %d trail generators", array_size( obj->trails ) );
1682 LOG( " has %d weapon mounts", array_size( obj->mounts ) );
1683
1684 PHYSFS_unmount( obj->path );
1685#endif /* HAVE_NAEV */
1686
1687 cgltf_free( data );
1688
1689 obj->loaded = 1;
1690 return obj;
1691}
1692
1693static void gltf_freeMesh( Mesh *mesh )
1694{
1695 for ( int i = 0; i < mesh->nprimitives; i++ ) {
1696 MeshPrimitive *mp = &mesh->primitives[i];
1697 if ( mp->vbo_idx )
1698 glDeleteBuffers( 1, &mp->vbo_idx );
1699 if ( mp->vbo_pos )
1700 glDeleteBuffers( 1, &mp->vbo_pos );
1701 if ( mp->vbo_nor )
1702 glDeleteBuffers( 1, &mp->vbo_nor );
1703 if ( mp->vbo_tex0 )
1704 glDeleteBuffers( 1, &mp->vbo_tex0 );
1705 if ( mp->vbo_tex1 )
1706 glDeleteBuffers( 1, &mp->vbo_tex1 );
1707 }
1708 free( mesh->primitives );
1709 gl_checkErr();
1710}
1711
1712static void gltf_freeNode( Node *node )
1713{
1714 free( node->name );
1715 free( node->children );
1716}
1717
1718static void gltf_freeAnimation( Animation *anim )
1719{
1720 free( anim->name );
1721 for ( size_t i = 0; i < anim->nsamplers; i++ ) {
1722 AnimationSampler *samp = &anim->samplers[i];
1723 free( samp->time );
1724 free( samp->data );
1725 }
1726 free( anim->samplers );
1727 free( anim->channels );
1728}
1729
1730static void gltf_freeTex( Texture *tex )
1731{
1732 /* Don't have to free default textures. */
1733 if ( ( tex == NULL ) || ( tex->tex == tex_zero.tex ) ||
1734 ( tex->tex == tex_ones.tex ) )
1735 return;
1736
1737#ifdef HAVE_NAEV
1738 gl_freeTexture( tex->gtex ); /* Frees texture too. */
1739#else /* HAVE_NAEV */
1740 glDeleteTextures( 1, &tex->tex );
1741#endif /* HAVE_NAEV */
1742 gl_checkErr();
1743}
1744
1745void gltf_free( GltfObject *obj )
1746{
1747 int c;
1748 if ( obj == NULL )
1749 return;
1750
1751 /* Try to get from cache, if refcount > 0 then it'll be 0. */
1752 c = cache_dec( obj );
1753 if ( c == 0 )
1754 return;
1755
1756 free( obj->path );
1757
1758 for ( size_t i = 0; i < obj->nmeshes; i++ ) {
1759 Mesh *mesh = &obj->meshes[i];
1760 gltf_freeMesh( mesh );
1761 }
1762 free( obj->meshes );
1763
1764 for ( size_t i = 0; i < obj->nnodes; i++ ) {
1765 Node *node = &obj->nodes[i];
1766 gltf_freeNode( node );
1767 }
1768 free( obj->nodes );
1769
1770 for ( size_t i = 0; i < obj->nanimations; i++ ) {
1771 Animation *anim = &obj->animations[i];
1772 gltf_freeAnimation( anim );
1773 }
1774 free( obj->animations );
1775
1776 for ( size_t s = 0; s < obj->nscenes; s++ ) {
1777 Scene *scene = &obj->scenes[s];
1778 free( scene->name );
1779 free( scene->nodes );
1780 }
1781 free( obj->scenes );
1782
1783 for ( size_t i = 0; i < obj->nmaterials; i++ ) {
1784 Material *m = &obj->materials[i];
1785 gltf_freeTex( &m->baseColour_tex );
1786 gltf_freeTex( &m->metallic_tex );
1787 gltf_freeTex( &m->normal_tex );
1788 gltf_freeTex( &m->occlusion_tex );
1789 gltf_freeTex( &m->emissive_tex );
1790 }
1791 free( obj->materials );
1792
1793 /* Clean up extras. */
1794 for ( int i = 0; i < array_size( obj->trails ); i++ ) {
1795 GltfTrail *t = &obj->trails[i];
1796 free( t->generator );
1797 }
1798 array_free( obj->trails );
1799 array_free( obj->mounts );
1800
1801 free( obj );
1802}
1803
1804int gltf_init( void )
1805{
1806 const GLubyte data_zero[4] = { 0, 0, 0, 0 };
1807 const GLubyte data_ones[4] = { 255, 255, 255, 255 };
1808 const GLfloat b[4] = { 1., 1., 1., 1. };
1809 GLenum status;
1810 Shader *shd;
1811 const char *prepend_fix = "#define MAX_LIGHTS " STR( MAX_LIGHTS ) "\n";
1812 char prepend[STRMAX];
1813
1814 cache_lock = SDL_CreateMutex();
1815
1816 /* Set up default lighting. */
1817 L_default = L_default_const;
1818 for ( int i = 0; i < L_default.nlights; i++ )
1819 shadow_matrix( &light_mat_def[i], &L_default.lights[i] );
1820
1821 /* Set global options. */
1822 use_normal_mapping = !conf.low_memory;
1823 use_ambient_occlusion = !conf.low_memory;
1824 max_tex_size = ( conf.low_memory ? conf.max_3d_tex_size : 0 );
1825
1826 /* Set prefix stuff. */
1827 snprintf( prepend, sizeof( prepend ),
1828 "%s\n#define HAS_NORMAL %d\n#define HAS_AO %d\n", prepend_fix,
1829 use_normal_mapping, use_ambient_occlusion );
1830
1831 /* Load textures. */
1832 glGenTextures( 1, &tex_zero.tex );
1833 glBindTexture( GL_TEXTURE_2D, tex_zero.tex );
1834 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1835 data_zero );
1836 glGenTextures( 1, &tex_ones.tex );
1837 glBindTexture( GL_TEXTURE_2D, tex_ones.tex );
1838 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1839 data_ones );
1840 glBindTexture( GL_TEXTURE_2D, 0 );
1841 gl_checkErr();
1842
1843 /* Set up default material. */
1844 gltf_loadMaterial( NULL, &material_default, NULL, NULL );
1845
1846 /* We'll have to set up some rendering stuff for blurring purposes. */
1847 const GLfloat vbo_data[8] = { 0., 0., 1., 0., 0., 1., 1., 1. };
1848 glGenBuffers( 1, &shadow_vbo );
1849 glBindBuffer( GL_ARRAY_BUFFER, shadow_vbo );
1850 glBufferData( GL_ARRAY_BUFFER, sizeof( GLfloat ) * 8, vbo_data,
1851 GL_STATIC_DRAW );
1852 glBindBuffer( GL_ARRAY_BUFFER, 0 );
1853
1854 for ( int l = 0; l < 2; l++ ) {
1855 if ( l == 0 ) {
1856 shadowmap_size = SHADOWMAP_SIZE_LOW;
1857 light_fbo = light_fbo_low;
1858 light_tex = light_tex_low;
1859 shadow_fbo = &shadow_fbo_low;
1860 shadow_tex = &shadow_tex_low;
1861 } else {
1862 shadowmap_size = SHADOWMAP_SIZE_HIGH;
1863 light_fbo = light_fbo_high;
1864 light_tex = light_tex_high;
1865 shadow_fbo = &shadow_fbo_high;
1866 shadow_tex = &shadow_tex_high;
1867 }
1868
1869 /* Gen the texture. */
1870 glGenTextures( 1, shadow_tex );
1871 glBindTexture( GL_TEXTURE_2D, *shadow_tex );
1872 glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, shadowmap_size,
1873 shadowmap_size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL );
1874 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1875 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1876 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
1877 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
1878 glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, b );
1879 glBindTexture( GL_TEXTURE_2D, 0 );
1880 /* Set up shadow buffer FBO. */
1881 glGenFramebuffers( 1, shadow_fbo );
1882 glBindFramebuffer( GL_FRAMEBUFFER, *shadow_fbo );
1883 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
1884 GL_TEXTURE_2D, *shadow_tex, 0 );
1885 glDrawBuffer( GL_NONE );
1886 glReadBuffer( GL_NONE );
1887 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1888 status = glCheckFramebufferStatus( GL_FRAMEBUFFER );
1889 if ( status != GL_FRAMEBUFFER_COMPLETE )
1890 WARN( _( "Error setting up shadowmap framebuffer!" ) );
1891
1892 /* Now generate the light maps .*/
1893 glGenTextures( MAX_LIGHTS, light_tex );
1894 glGenFramebuffers( MAX_LIGHTS, light_fbo );
1895 for ( int i = 0; i < MAX_LIGHTS; i++ ) {
1896 /* Set up shadow buffer depth tex. */
1897 glBindTexture( GL_TEXTURE_2D, light_tex[i] );
1898 glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, shadowmap_size,
1899 shadowmap_size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL );
1900 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1901 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1902 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
1903 GL_CLAMP_TO_BORDER );
1904 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
1905 GL_CLAMP_TO_BORDER );
1906 glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, b );
1907 glBindTexture( GL_TEXTURE_2D, 0 );
1908 /* Set up shadow buffer FBO. */
1909 glBindFramebuffer( GL_FRAMEBUFFER, light_fbo[i] );
1910 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
1911 GL_TEXTURE_2D, light_tex[i], 0 );
1912 glDrawBuffer( GL_NONE );
1913 glReadBuffer( GL_NONE );
1914 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1915 status = glCheckFramebufferStatus( GL_FRAMEBUFFER );
1916 if ( status != GL_FRAMEBUFFER_COMPLETE )
1917 WARN( _( "Error setting up shadowmap framebuffer!" ) );
1918 }
1919 }
1920
1921 /* Compile the shadow shader. */
1922 shd = &shadow_shader;
1923 memset( shd, 0, sizeof( Shader ) );
1924 shd->program =
1925 gl_program_backend( "shadow.vert", "shadow.frag", NULL, prepend );
1926 if ( shd->program == 0 )
1927 return -1;
1928 glUseProgram( shd->program );
1929 /* Attributes. */
1930 shd->vertex = glGetAttribLocation( shd->program, "vertex" );
1931 /* Vertex uniforms. */
1932 shd->Hshadow = glGetUniformLocation( shd->program, "u_shadow" );
1933 shd->Hmodel = glGetUniformLocation( shd->program, "u_model" );
1934
1935 /* Compile the X blur shader. */
1936 shd = &shadow_shader_blurX;
1937 memset( shd, 0, sizeof( Shader ) );
1938 shd->program =
1939 gl_program_backend( "blur.vert", "blurX.frag", NULL, prepend );
1940 if ( shd->program == 0 )
1941 return -1;
1942 glUseProgram( shd->program );
1943 /* Attributes. */
1944 shd->vertex = glGetAttribLocation( shd->program, "vertex" );
1945 /* Uniforms. */
1946 shd->baseColour_tex = glGetUniformLocation( shd->program, "sampler" );
1947 glUniform1i( shd->baseColour_tex, 0 );
1948
1949 /* Compile the Y blur shader. */
1950 shd = &shadow_shader_blurY;
1951 memset( shd, 0, sizeof( Shader ) );
1952 shd->program =
1953 gl_program_backend( "blur.vert", "blurY.frag", NULL, prepend );
1954 if ( shd->program == 0 )
1955 return -1;
1956 glUseProgram( shd->program );
1957 /* Attributes. */
1958 shd->vertex = glGetAttribLocation( shd->program, "vertex" );
1959 /* Uniforms. */
1960 shd->baseColour_tex = glGetUniformLocation( shd->program, "sampler" );
1961
1962 /* Compile the shader. */
1963 shd = &gltf_shader;
1964 memset( shd, 0, sizeof( Shader ) );
1965 shd->program =
1966 gl_program_backend( "gltf.vert", "gltf_pbr.frag", NULL, prepend );
1967 if ( shd->program == 0 )
1968 return -1;
1969 glUseProgram( shd->program );
1970 /* Attributes. */
1971 shd->vertex = glGetAttribLocation( shd->program, "vertex" );
1972 shd->vertex_normal = glGetAttribLocation( shd->program, "vertex_normal" );
1973 shd->vertex_tex0 = glGetAttribLocation( shd->program, "vertex_tex0" );
1974 shd->vertex_tex1 = glGetAttribLocation( shd->program, "vertex_tex1" );
1975 /* Vertex uniforms. */
1976 shd->Hmodel = glGetUniformLocation( shd->program, "u_model" );
1977 shd->Hnormal = glGetUniformLocation( shd->program, "u_normal" );
1978 /* Fragment uniforms. */
1979 shd->blend = glGetUniformLocation( shd->program, "u_blend" );
1980 shd->u_ambient = glGetUniformLocation( shd->program, "u_ambient" );
1981 shd->baseColour_tex = glGetUniformLocation( shd->program, "baseColour_tex" );
1982 shd->baseColour_texcoord =
1983 glGetUniformLocation( shd->program, "baseColour_texcoord" );
1984 shd->metallic_tex = glGetUniformLocation( shd->program, "metallic_tex" );
1985 shd->metallic_texcoord =
1986 glGetUniformLocation( shd->program, "metallic_texcoord" );
1987 if ( use_normal_mapping ) {
1988 shd->u_has_normal = glGetUniformLocation( shd->program, "u_has_normal" );
1989 shd->normal_tex = glGetUniformLocation( shd->program, "normal_tex" );
1990 shd->normal_texcoord =
1991 glGetUniformLocation( shd->program, "normal_texcoord" );
1992 shd->normal_scale = glGetUniformLocation( shd->program, "normal_scale" );
1993 }
1994 shd->metallicFactor = glGetUniformLocation( shd->program, "metallicFactor" );
1995 shd->roughnessFactor =
1996 glGetUniformLocation( shd->program, "roughnessFactor" );
1997 shd->baseColour = glGetUniformLocation( shd->program, "baseColour" );
1998#if 0
1999 shd->sheenTint = glGetUniformLocation( shd->program, "sheenTint" );
2000 shd->sheen = glGetUniformLocation( shd->program, "sheen" );
2001 shd->clearcoat = glGetUniformLocation( shd->program, "clearcoat" );
2002 shd->clearcoat_roughness =
2003 glGetUniformLocation( shd->program, "clearcoat_roughness" );
2004#endif
2005 shd->emissive = glGetUniformLocation( shd->program, "emissive" );
2006 if ( use_ambient_occlusion ) {
2007 shd->occlusion_tex =
2008 glGetUniformLocation( shd->program, "occlusion_tex" );
2009 shd->occlusion_texcoord =
2010 glGetUniformLocation( shd->program, "occlusion_texcoord" );
2011 }
2012 shd->emissive_tex = glGetUniformLocation( shd->program, "emissive_tex" );
2013 shd->emissive_texcoord =
2014 glGetUniformLocation( shd->program, "emissive_texcoord" );
2015 /* Special. */
2016 // shd->waxiness = glGetUniformLocation( shd->program, "u_waxiness" );
2017 /* Lights. */
2018 for ( int i = 0; i < MAX_LIGHTS; i++ ) {
2019 ShaderLight *sl = &shd->lights[i];
2020 char buf[128];
2021 snprintf( buf, sizeof( buf ), "u_lights[%d].position", i );
2022 sl->position = glGetUniformLocation( shd->program, buf );
2023 snprintf( buf, sizeof( buf ), "u_lights[%d].sun", i );
2024 sl->sun = glGetUniformLocation( shd->program, buf );
2025 snprintf( buf, sizeof( buf ), "u_lights[%d].colour", i );
2026 sl->colour = glGetUniformLocation( shd->program, buf );
2027 snprintf( buf, sizeof( buf ), "u_lights[%d].intensity", i );
2028 sl->intensity = glGetUniformLocation( shd->program, buf );
2029 snprintf( buf, sizeof( buf ), "shadowmap_tex[%d]", i );
2030 sl->shadowmap_tex = glGetUniformLocation( shd->program, buf );
2031 snprintf( buf, sizeof( buf ), "u_shadow[%d]", i );
2032 sl->Hshadow = glGetUniformLocation( shd->program, buf );
2033 }
2034 shd->nlights = glGetUniformLocation( shd->program, "u_nlights" );
2035
2036 glUseProgram( 0 );
2037 /* Light default values set on init. */
2038 gl_checkErr();
2039
2040 return 0;
2041}
2042
2043void gltf_exit( void )
2044{
2045 /* Not initialized. */
2046 if ( tex_zero.tex == 0 )
2047 return;
2048
2049 SDL_DestroyMutex( cache_lock );
2050 for ( int i = 0; i < array_size( obj_cache ); i++ ) {
2051 WARN( _( "Object Cache '%s' not properly freed (refcount=%d)!" ),
2052 obj_cache[i].name, obj_cache[i].refcount );
2053 free( obj_cache->name );
2054 }
2055 array_free( obj_cache );
2056
2057 glDeleteBuffers( 1, &shadow_vbo );
2058 glDeleteTextures( 1, &shadow_tex_high );
2059 glDeleteTextures( 1, &shadow_tex_low );
2060 glDeleteFramebuffers( 1, &shadow_fbo_high );
2061 glDeleteFramebuffers( 1, &shadow_fbo_low );
2062 glDeleteTextures( MAX_LIGHTS, light_tex_low );
2063 glDeleteFramebuffers( MAX_LIGHTS, light_fbo_low );
2064 glDeleteTextures( MAX_LIGHTS, light_tex_high );
2065 glDeleteFramebuffers( MAX_LIGHTS, light_fbo_high );
2066 glDeleteTextures( 1, &tex_zero.tex );
2067 glDeleteTextures( 1, &tex_ones.tex );
2068 glDeleteProgram( gltf_shader.program );
2069 glDeleteProgram( shadow_shader.program );
2070}
2071
2075void gltf_lightReset( void )
2076{
2077 L_default = L_default_const;
2078}
2079
2086int gltf_lightSet( int idx, const Light *L )
2087{
2088 int n = L_default_const.nlights +
2089 idx; /* We start counting after the default lights. */
2090 if ( n >= MAX_LIGHTS ) {
2091 WARN( _( "Trying to set more lights than MAX_LIGHTS allows!" ) );
2092 return -1;
2093 }
2094 L_default.nlights = MAX( L_default.nlights, n + 1 );
2095 L_default.lights[n] = *L;
2096 shadow_matrix( &light_mat_def[n], &L_default.lights[n] );
2097 return 0;
2098}
2099
2103void gltf_lightAmbient( double r, double g, double b )
2104{
2105 const double factor = 1.0 / M_PI;
2106 L_default.ambient_r = r * factor;
2107 L_default.ambient_g = g * factor;
2108 L_default.ambient_b = b * factor;
2109}
2110
2114void gltf_lightAmbientGet( double *r, double *g, double *b )
2115{
2116 *r = L_default.ambient_r * M_PI;
2117 *g = L_default.ambient_g * M_PI;
2118 *b = L_default.ambient_b * M_PI;
2119}
2120
2124void gltf_lightIntensity( double strength )
2125{
2126 L_default.intensity = strength;
2127}
2128
2132double gltf_lightIntensityGet( void )
2133{
2134 return L_default.intensity;
2135}
2136
2140void gltf_lightTransform( Lighting *L, const mat4 *H )
2141{
2142 for ( int i = 0; i < L->nlights; i++ ) {
2143 Light *l = &L->lights[i];
2144 if ( l->sun ) {
2145 mat3 h;
2146 vec3 v = l->pos;
2147 mat3_from_mat4( &h, H );
2148 mat3_mul_vec( &l->pos, &h, &v );
2149 } else {
2150 vec3 v = l->pos;
2151 mat4_mul_vec( &l->pos, H, &v );
2152 }
2153 }
2154}
2155
2159GLuint gltf_shadowmap( int light )
2160{
2161 return light_tex[light];
2162}
2163
2167static int cache_cmp( const void *p1, const void *p2 )
2168{
2169 const ObjectCache *o1 = p1;
2170 const ObjectCache *o2 = p2;
2171 return strcmp( o1->name, o2->name );
2172}
2173
2181static GltfObject *cache_get( const char *filename, int *new )
2182{
2183 SDL_mutexP( cache_lock );
2184 const ObjectCache cache = {
2185 .name = (char *)filename,
2186 };
2187 GltfObject *obj;
2188 ObjectCache *hit = bsearch( &cache, obj_cache, array_size( obj_cache ),
2189 sizeof( ObjectCache ), cache_cmp );
2190 if ( hit == NULL ) {
2191 ObjectCache newcache = {
2192 .name = strdup( filename ),
2193 .obj = calloc( 1, sizeof( GltfObject ) ),
2194 .refcount = 1,
2195 };
2196 if ( obj_cache == NULL )
2197 obj_cache = array_create( ObjectCache );
2198 array_push_back( &obj_cache, newcache );
2199 qsort( obj_cache, array_size( obj_cache ), sizeof( ObjectCache ),
2200 cache_cmp );
2201 *new = 1;
2202 obj = newcache.obj;
2203 SDL_mutexV( cache_lock );
2204 return obj;
2205 }
2206 hit->refcount++;
2207 *new = 0;
2208 obj = hit->obj;
2209 SDL_mutexV( cache_lock );
2210 return obj;
2211}
2212
2220static int cache_dec( GltfObject *obj )
2221{
2222 SDL_mutexP( cache_lock );
2223 for ( int i = 0; i < array_size( obj_cache ); i++ ) {
2224 ObjectCache *c = &obj_cache[i];
2225 if ( c->obj != obj )
2226 continue;
2227
2228 c->refcount--;
2229 if ( c->refcount <= 0 ) {
2230 free( c->name );
2231 array_erase( &obj_cache, &c[0], &c[1] );
2232 SDL_mutexV( cache_lock );
2233 return 1;
2234 }
2235 SDL_mutexV( cache_lock );
2236 return 0;
2237 }
2238 WARN( _( "GltfObject '%s' not found in cache!" ), obj->path );
2239 SDL_mutexV( cache_lock );
2240 return 0;
2241}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
void mat4_apply(mat4 *lhs, const mat4 *rhs)
Applies a transformation to another, storing the result in the left hand side.
Definition mat4.c:84
mat4 mat4_lookat(const vec3 *eye, const vec3 *center, const vec3 *up)
Creates a matrix with a transformation to look at a center point from an eye with an up vector.
Definition mat4.c:382
void mat4_mul_vec(vec3 *out, const mat4 *m, const vec3 *v)
Multiplies a matrix with a vector (out = m * v);.
Definition mat4.c:67
void mat4_mul(mat4 *out, const mat4 *m1, const mat4 *m2)
Multiplies two matrices (out = m1 * m2).
Definition mat4.c:46
void mat4_trs(mat4 *m, const vec3 *t, const quat *r, const vec3 *s)
Creates a homogeneous transform matrix from a translation, rotation, and scaling. Uses T*R*S order.
Definition mat4.c:290
mat4 mat4_ortho(double left, double right, double bottom, double top, double nearVal, double farVal)
Creates an orthographic projection matrix.
Definition mat4.c:347
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define MAX(x, y)
Definition naev.h:37
#define PATH_MAX
Definition naev.h:57
int nfile_simplifyPath(char path[static 1])
Simplifies the path removing things like ".." or consecutive "/".
Definition nfile.c:676
glInfo gl_screen
Definition opengl.c:47
USE_RESULT glTexture * gl_texExistsOrCreate(const char *path, unsigned int flags, int sx, int sy, int *created)
Check to see if a texture matching a path already exists.
Definition opengl_tex.c:508
int gl_fboCreate(GLuint *fbo, GLuint *tex, GLsizei width, GLsizei height)
Creates a framebuffer and its associated texture.
Definition opengl_tex.c:268
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:835
static const double c[]
Definition rng.c:256
static const double d[]
Definition rng.c:263
Node * target
Definition gltf.h:147
AnimationSampler * sampler
Definition gltf.h:148
AnimationType type
Definition gltf.h:146
GLfloat max
Definition gltf.h:142
float * time
Definition gltf.h:136
AnimationInterpolation interp
Definition gltf.h:138
GLfloat * data
Definition gltf.h:137
char * name
Definition gltf.h:152
AnimationChannel * channels
Definition gltf.h:155
size_t nsamplers
Definition gltf.h:154
AnimationSampler * samplers
Definition gltf.h:153
size_t nchannels
Definition gltf.h:156
int id
Definition gltf.h:174
vec3 pos
Definition gltf.h:176
Defines a complete object.
Definition gltf.h:182
int scene_body
Definition gltf.h:196
GltfMount * mounts
Definition gltf.h:200
size_t nscenes
Definition gltf.h:189
Material * materials
Definition gltf.h:190
size_t nmaterials
Definition gltf.h:191
Animation * animations
Definition gltf.h:192
int scene_engine
Definition gltf.h:197
Node * nodes
Definition gltf.h:186
char * path
Definition gltf.h:183
GLfloat radius
Definition gltf.h:194
Scene * scenes
Definition gltf.h:188
size_t nmeshes
Definition gltf.h:185
Mesh * meshes
Definition gltf.h:184
GltfTrail * trails
Definition gltf.h:199
int loaded
Definition gltf.h:201
size_t nanimations
Definition gltf.h:193
size_t nnodes
Definition gltf.h:187
char * generator
Definition gltf.h:169
vec3 pos
Definition gltf.h:170
Simple point/sun light model.
Definition gltf.h:207
vec3 pos
Definition gltf.h:211
double intensity
Definition gltf.h:213
int sun
Definition gltf.h:208
vec3 colour
Definition gltf.h:214
int nlights
Definition gltf.h:220
double ambient_b
Definition gltf.h:218
Light lights[MAX_LIGHTS]
Definition gltf.h:219
double intensity
Definition gltf.h:222
PBR Material of an object.
Definition gltf.h:38
int blend
Definition gltf.h:40
GLfloat roughnessFactor
Definition gltf.h:51
int noshadows
Definition gltf.h:41
Texture metallic_tex
Definition gltf.h:46
int unlit
Definition gltf.h:43
int double_sided
Definition gltf.h:42
GLfloat baseColour[4]
Definition gltf.h:53
Texture baseColour_tex
Definition gltf.h:45
GLfloat metallicFactor
Definition gltf.h:49
Represents the underlyig 3D data and associated material.
Definition gltf.h:77
GLuint vbo_tex1
Definition gltf.h:83
int material
Definition gltf.h:84
GLuint vbo_nor
Definition gltf.h:81
GLuint vbo_tex0
Definition gltf.h:82
size_t nidx
Definition gltf.h:78
GLuint vbo_idx
Definition gltf.h:79
GLuint vbo_pos
Definition gltf.h:80
Represents a mesh that can be made of multiple primitives.
Definition gltf.h:90
MeshPrimitive * primitives
Definition gltf.h:91
int nprimitives
Definition gltf.h:92
quat r
Definition gltf.h:97
vec3 t
Definition gltf.h:96
vec3 s
Definition gltf.h:98
Represents a node of an object. Each node can have multiple meshes and children nodes with an associa...
Definition gltf.h:105
char * name
Definition gltf.h:106
NodeTransform nt
Definition gltf.h:120
int has_anim
Definition gltf.h:119
GLfloat radius
Definition gltf.h:114
NodeTransform ntorig
Definition gltf.h:121
mat4 Horig
Definition gltf.h:108
size_t * children
Definition gltf.h:111
mat4 H
Definition gltf.h:107
int mesh
Definition gltf.h:109
vec3 aabb_min
Definition gltf.h:115
size_t nchildren
Definition gltf.h:112
vec3 aabb_max
Definition gltf.h:116
Simple caching for GLTF objects to avoid double loads.
Definition gltf.c:38
GltfObject * obj
Definition gltf.c:40
char * name
Definition gltf.c:39
int refcount
Definition gltf.c:41
Represents a scene that can have multiple nodes.
Definition gltf.h:162
char * name
Definition gltf.h:163
size_t * nodes
Definition gltf.h:164
size_t nnodes
Definition gltf.h:165
Simple point light model for shaders.
Definition gltf.c:51
Shader to use witha material.
Definition gltf.c:154
Definition gltf.h:25
GLfloat strength
Definition gltf.h:28
GLuint tex
Definition gltf.h:26
GLuint texcoord
Definition gltf.h:27
Definition mat3.h:11
Definition mat4.h:12
Definition vec3.h:6