mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-08-13 08:49:35 +00:00
web/ compiles the v3 C runtime with emcc (Emscripten SDK from ~/work/emsdk by default) and ships a simple browser demo that loads glTF/GLB files via drag & drop and renders them with three.js: - loader.c: C bridge exporting flattened primitives, materials, textures (bufferView + data-URI paths) and the scene graph to JS - main.js/index.html/style.css: three.js viewer (OrbitControls, PBR materials, textures, node hierarchy, primitive modes) - gen_sample.py: generates a small self-contained textured Cube.glb sample from tracked assets (no large binaries committed) - Makefile: emcc build (MODULARIZE + ES6, no filesystem), sample generation and local http server targets Verified end-to-end: native bridge tests, Node harness exercising the WASM exports (parse, geometry, images, clear) and HTTP serving.
549 lines
21 KiB
C
549 lines
21 KiB
C
/*
|
|
* loader.c — tinygltf v3 C WASM bridge for the three.js demo.
|
|
*
|
|
* Parses glTF/GLB bytes passed in from JavaScript (Emscripten HEAPU8),
|
|
* then exposes flattened per-primitive vertex/index data plus materials,
|
|
* textures and the node hierarchy through EMSCRIPTEN_KEEPALIVE functions.
|
|
*
|
|
* Build with emcc (see Makefile). No filesystem or image decoding is
|
|
* used: all assets must be embedded in the file (GLB chunk or data URI).
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tiny_gltf_v3.h"
|
|
|
|
#if defined(__EMSCRIPTEN__)
|
|
#include <emscripten.h>
|
|
#define TG3W_EXPORT EMSCRIPTEN_KEEPALIVE
|
|
#else
|
|
#define TG3W_EXPORT
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Exported model info */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
typedef struct {
|
|
int32_t material; /* material index or -1 */
|
|
int32_t mode; /* TG3_MODE_* */
|
|
uint32_t vertex_count;
|
|
uint32_t index_count;
|
|
uint32_t pos_offset; /* offset in tg3w_positions (floats, 3 * vertex_count) */
|
|
int32_t nrm_offset; /* -1 = absent; floats, 3 * vertex_count */
|
|
int32_t uv_offset; /* -1 = absent; floats, 2 * vertex_count */
|
|
uint32_t idx_offset; /* offset in tg3w_indices (uint32) */
|
|
} tg3w_prim;
|
|
|
|
typedef struct {
|
|
float base_color[4];
|
|
float metallic;
|
|
float roughness;
|
|
int32_t base_color_texture; /* texture index or -1 */
|
|
int32_t alpha_mode; /* 0 = OPAQUE, 1 = MASK, 2 = BLEND */
|
|
float alpha_cutoff;
|
|
int32_t double_sided;
|
|
} tg3w_mat;
|
|
|
|
typedef struct {
|
|
int32_t mesh; /* mesh index or -1 */
|
|
float translation[3];
|
|
float rotation[4];
|
|
float scale[3];
|
|
} tg3w_node;
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Module state (single parse at a time, like a simple loading screen) */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static tg3_model g_model;
|
|
static tg3_error_stack g_errors;
|
|
static tg3_parse_options g_opts_;
|
|
static tg3w_prim *g_prims = NULL;
|
|
static uint32_t g_prim_count = 0;
|
|
static uint32_t *g_mesh_prim_start = NULL;
|
|
static uint32_t *g_mesh_prim_count = NULL;
|
|
static float *g_positions = NULL;
|
|
static float *g_normals = NULL;
|
|
static float *g_uvs = NULL;
|
|
static uint32_t *g_indices = NULL;
|
|
static char g_last_error[512];
|
|
|
|
static int g_inited = 0;
|
|
|
|
/* Free the current model and all flattened buffers. */
|
|
TG3W_EXPORT void tg3w_clear(void) {
|
|
tg3_model_free(&g_model);
|
|
free(g_prims); g_prims = NULL;
|
|
free(g_positions); g_positions = NULL;
|
|
free(g_normals); g_normals = NULL;
|
|
free(g_uvs); g_uvs = NULL;
|
|
free(g_indices); g_indices = NULL;
|
|
free(g_mesh_prim_start); g_mesh_prim_start = NULL;
|
|
free(g_mesh_prim_count); g_mesh_prim_count = NULL;
|
|
g_prim_count = 0;
|
|
}
|
|
|
|
static void tg3w_init_once(void) {
|
|
if (!g_inited) {
|
|
tg3_parse_options_init(&g_opts_);
|
|
tg3_error_stack_init(&g_errors);
|
|
g_inited = 1;
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Accessor helpers */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static uint32_t tg3w_type_components(int32_t type) {
|
|
switch (type) {
|
|
case TG3_TYPE_SCALAR: return 1;
|
|
case TG3_TYPE_VEC2: return 2;
|
|
case TG3_TYPE_VEC3: return 3;
|
|
case TG3_TYPE_VEC4: return 4;
|
|
case TG3_TYPE_MAT4: return 16;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static const uint8_t *tg3w_accessor_ptr(const tg3_model *m,
|
|
const tg3_accessor *a,
|
|
uint64_t *stride_out) {
|
|
if (a->buffer_view < 0 || a->sparse.is_sparse) {
|
|
return NULL;
|
|
}
|
|
const tg3_buffer_view *bv = &m->buffer_views[a->buffer_view];
|
|
if (bv->buffer < 0) {
|
|
return NULL;
|
|
}
|
|
const tg3_buffer *b = &m->buffers[bv->buffer];
|
|
if (!b->data.data || b->data.count < bv->byte_offset + bv->byte_length) {
|
|
return NULL;
|
|
}
|
|
uint32_t comps = tg3w_type_components(a->type);
|
|
uint64_t elem = 0;
|
|
switch (a->component_type) {
|
|
case TG3_COMPONENT_TYPE_FLOAT: elem = 4; break;
|
|
case TG3_COMPONENT_TYPE_DOUBLE: elem = 8; break;
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_BYTE: elem = 1; break;
|
|
case TG3_COMPONENT_TYPE_BYTE: elem = 1; break;
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_SHORT: elem = 2; break;
|
|
case TG3_COMPONENT_TYPE_SHORT: elem = 2; break;
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_INT: elem = 4; break;
|
|
case TG3_COMPONENT_TYPE_INT: elem = 4; break;
|
|
default: return NULL;
|
|
}
|
|
uint64_t stride = bv->byte_stride ? bv->byte_stride : elem * comps;
|
|
*stride_out = stride;
|
|
return b->data.data + bv->byte_offset + a->byte_offset;
|
|
}
|
|
|
|
static int tg3w_attr_index(const tg3_primitive *p, const char *name) {
|
|
for (uint32_t i = 0; i < p->attributes_count; i++) {
|
|
const tg3_str_int_pair *a = &p->attributes[i];
|
|
if (a->key.len == (uint32_t)strlen(name) &&
|
|
strncmp(a->key.data, name, a->key.len) == 0) {
|
|
return a->value;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Parse + flatten */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
TG3W_EXPORT int tg3w_parse(const uint8_t *data, uint32_t size) {
|
|
tg3w_init_once();
|
|
|
|
tg3_model_free(&g_model);
|
|
tg3_error_stack_free(&g_errors);
|
|
tg3_error_stack_init(&g_errors);
|
|
tg3_parse_options_init(&g_opts_);
|
|
|
|
tg3_error_code err = tg3_parse_auto(&g_model, &g_errors, data, size,
|
|
NULL, 0, &g_opts_);
|
|
if (err != TG3_OK) {
|
|
snprintf(g_last_error, sizeof(g_last_error), "parse failed: %d", (int)err);
|
|
return (int)err;
|
|
}
|
|
|
|
/* Pass 1: count vertices/indices per primitive. */
|
|
uint32_t prims = 0;
|
|
uint64_t total_pos = 0, total_nrm = 0, total_uv = 0, total_idx = 0;
|
|
for (uint32_t mi = 0; mi < g_model.meshes_count; mi++) {
|
|
const tg3_mesh *mesh = &g_model.meshes[mi];
|
|
for (uint32_t pi = 0; pi < mesh->primitives_count; pi++) {
|
|
const tg3_primitive *p = &mesh->primitives[pi];
|
|
int pos_i = tg3w_attr_index(p, "POSITION");
|
|
int nrm_i = tg3w_attr_index(p, "NORMAL");
|
|
int uv_i = tg3w_attr_index(p, "TEXCOORD_0");
|
|
uint64_t vcount = 0;
|
|
if (pos_i >= 0) {
|
|
const tg3_accessor *a = &g_model.accessors[pos_i];
|
|
if (a->component_type == TG3_COMPONENT_TYPE_FLOAT &&
|
|
a->type == TG3_TYPE_VEC3 && !a->sparse.is_sparse) {
|
|
vcount = a->count;
|
|
}
|
|
}
|
|
uint64_t icount = 0;
|
|
if (p->indices >= 0) {
|
|
const tg3_accessor *a = &g_model.accessors[p->indices];
|
|
if (!a->sparse.is_sparse) {
|
|
switch (a->component_type) {
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_BYTE:
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_SHORT:
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_INT:
|
|
icount = a->count;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
if (vcount == 0) {
|
|
continue; /* unsupported primitive (e.g. sparse/double) */
|
|
}
|
|
total_pos += vcount * 3;
|
|
if (nrm_i >= 0) {
|
|
const tg3_accessor *a = &g_model.accessors[nrm_i];
|
|
if (a->component_type == TG3_COMPONENT_TYPE_FLOAT &&
|
|
a->type == TG3_TYPE_VEC3 && !a->sparse.is_sparse) {
|
|
total_nrm += vcount * 3;
|
|
}
|
|
}
|
|
if (uv_i >= 0) {
|
|
const tg3_accessor *a = &g_model.accessors[uv_i];
|
|
if (a->component_type == TG3_COMPONENT_TYPE_FLOAT &&
|
|
a->type == TG3_TYPE_VEC2 && !a->sparse.is_sparse) {
|
|
total_uv += vcount * 2;
|
|
}
|
|
}
|
|
total_idx += icount;
|
|
prims++;
|
|
}
|
|
}
|
|
|
|
if (prims == 0) {
|
|
snprintf(g_last_error, sizeof(g_last_error),
|
|
"no renderable primitives (float VEC3 POSITION required)");
|
|
return -1;
|
|
}
|
|
|
|
/* Allocate flattened arrays. */
|
|
free(g_prims); free(g_positions); free(g_normals); free(g_uvs); free(g_indices);
|
|
free(g_mesh_prim_start); free(g_mesh_prim_count);
|
|
g_prims = (tg3w_prim *)malloc(prims * sizeof(tg3w_prim));
|
|
g_positions = (float *)malloc(total_pos * sizeof(float));
|
|
g_normals = total_nrm ? (float *)malloc(total_nrm * sizeof(float)) : NULL;
|
|
g_uvs = total_uv ? (float *)malloc(total_uv * sizeof(float)) : NULL;
|
|
g_indices = (uint32_t *)malloc(total_idx * sizeof(uint32_t));
|
|
g_mesh_prim_start = (uint32_t *)malloc(g_model.meshes_count * sizeof(uint32_t));
|
|
g_mesh_prim_count = (uint32_t *)malloc(g_model.meshes_count * sizeof(uint32_t));
|
|
g_prim_count = 0;
|
|
|
|
if (!g_prims || (total_pos && !g_positions) || (total_nrm && !g_normals) ||
|
|
(total_uv && !g_uvs) || (total_idx && !g_indices) ||
|
|
!g_mesh_prim_start || !g_mesh_prim_count) {
|
|
snprintf(g_last_error, sizeof(g_last_error), "out of memory");
|
|
tg3w_clear();
|
|
return -1;
|
|
}
|
|
memset(g_mesh_prim_start, 0, g_model.meshes_count * sizeof(uint32_t));
|
|
memset(g_mesh_prim_count, 0, g_model.meshes_count * sizeof(uint32_t));
|
|
|
|
/* Pass 2: fill. */
|
|
uint32_t p_off = 0, n_off = 0, u_off = 0, i_off = 0;
|
|
for (uint32_t mi = 0; mi < g_model.meshes_count; mi++) {
|
|
const tg3_mesh *mesh = &g_model.meshes[mi];
|
|
g_mesh_prim_start[mi] = g_prim_count;
|
|
for (uint32_t pi = 0; pi < mesh->primitives_count; pi++) {
|
|
const tg3_primitive *p = &mesh->primitives[pi];
|
|
int pos_i = tg3w_attr_index(p, "POSITION");
|
|
uint64_t vcount = 0;
|
|
uint64_t pstride = 0;
|
|
const uint8_t *pp = NULL;
|
|
if (pos_i >= 0) {
|
|
const tg3_accessor *a = &g_model.accessors[pos_i];
|
|
if (a->component_type == TG3_COMPONENT_TYPE_FLOAT &&
|
|
a->type == TG3_TYPE_VEC3 && !a->sparse.is_sparse) {
|
|
vcount = a->count;
|
|
pp = tg3w_accessor_ptr(&g_model, a, &pstride);
|
|
}
|
|
}
|
|
if (!pp || vcount == 0) {
|
|
continue;
|
|
}
|
|
|
|
tg3w_prim *out = &g_prims[g_prim_count];
|
|
memset(out, 0, sizeof(*out));
|
|
out->nrm_offset = -1;
|
|
out->uv_offset = -1;
|
|
out->material = p->material;
|
|
out->mode = (p->mode == -1) ? TG3_MODE_TRIANGLES : p->mode;
|
|
out->vertex_count = (uint32_t)vcount;
|
|
out->pos_offset = p_off;
|
|
|
|
for (uint64_t v = 0; v < vcount; v++) {
|
|
const float *f = (const float *)(pp + v * pstride);
|
|
g_positions[p_off + (uint32_t)v * 3 + 0] = f[0];
|
|
g_positions[p_off + (uint32_t)v * 3 + 1] = f[1];
|
|
g_positions[p_off + (uint32_t)v * 3 + 2] = f[2];
|
|
}
|
|
p_off += (uint32_t)vcount * 3;
|
|
|
|
int nrm_i = tg3w_attr_index(p, "NORMAL");
|
|
if (nrm_i >= 0) {
|
|
const tg3_accessor *a = &g_model.accessors[nrm_i];
|
|
uint64_t stride = 0;
|
|
const uint8_t *np = tg3w_accessor_ptr(&g_model, a, &stride);
|
|
if (np && a->component_type == TG3_COMPONENT_TYPE_FLOAT &&
|
|
a->type == TG3_TYPE_VEC3 && a->count == vcount) {
|
|
out->nrm_offset = (int32_t)n_off;
|
|
for (uint64_t v = 0; v < vcount; v++) {
|
|
const float *f = (const float *)(np + v * stride);
|
|
g_normals[n_off + (uint32_t)v * 3 + 0] = f[0];
|
|
g_normals[n_off + (uint32_t)v * 3 + 1] = f[1];
|
|
g_normals[n_off + (uint32_t)v * 3 + 2] = f[2];
|
|
}
|
|
n_off += (uint32_t)vcount * 3;
|
|
}
|
|
}
|
|
|
|
int uv_i = tg3w_attr_index(p, "TEXCOORD_0");
|
|
if (uv_i >= 0) {
|
|
const tg3_accessor *a = &g_model.accessors[uv_i];
|
|
uint64_t stride = 0;
|
|
const uint8_t *up = tg3w_accessor_ptr(&g_model, a, &stride);
|
|
if (up && a->component_type == TG3_COMPONENT_TYPE_FLOAT &&
|
|
a->type == TG3_TYPE_VEC2 && a->count == vcount) {
|
|
out->uv_offset = (int32_t)u_off;
|
|
for (uint64_t v = 0; v < vcount; v++) {
|
|
const float *f = (const float *)(up + v * stride);
|
|
g_uvs[u_off + (uint32_t)v * 2 + 0] = f[0];
|
|
g_uvs[u_off + (uint32_t)v * 2 + 1] = f[1];
|
|
}
|
|
u_off += (uint32_t)vcount * 2;
|
|
}
|
|
}
|
|
|
|
if (p->indices >= 0) {
|
|
const tg3_accessor *a = &g_model.accessors[p->indices];
|
|
uint64_t stride = 0;
|
|
const uint8_t *ip = tg3w_accessor_ptr(&g_model, a, &stride);
|
|
if (ip && !a->sparse.is_sparse) {
|
|
out->index_count = (uint32_t)a->count;
|
|
out->idx_offset = i_off;
|
|
for (uint64_t v = 0; v < a->count; v++) {
|
|
uint32_t idx = 0;
|
|
switch (a->component_type) {
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_BYTE:
|
|
idx = ((const uint8_t *)ip)[v * stride];
|
|
break;
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_SHORT:
|
|
idx = ((const uint16_t *)ip)[v * stride / 2];
|
|
break;
|
|
case TG3_COMPONENT_TYPE_UNSIGNED_INT:
|
|
idx = ((const uint32_t *)ip)[v * stride / 4];
|
|
break;
|
|
default: break;
|
|
}
|
|
g_indices[i_off + (uint32_t)v] = idx;
|
|
}
|
|
i_off += (uint32_t)a->count;
|
|
}
|
|
}
|
|
g_prim_count++;
|
|
}
|
|
g_mesh_prim_count[mi] = g_prim_count - g_mesh_prim_start[mi];
|
|
}
|
|
|
|
g_last_error[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Getters */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
TG3W_EXPORT const char *tg3w_last_error(void) { return g_last_error; }
|
|
|
|
TG3W_EXPORT uint32_t tg3w_error_count(void) { return g_errors.count; }
|
|
|
|
TG3W_EXPORT const char *tg3w_error_message(uint32_t i) {
|
|
if (i >= g_errors.count || !g_errors.entries[i].message) return "";
|
|
return g_errors.entries[i].message;
|
|
}
|
|
|
|
TG3W_EXPORT int tg3w_error_severity(uint32_t i) {
|
|
if (i >= g_errors.count) return -1;
|
|
return (int)g_errors.entries[i].severity;
|
|
}
|
|
|
|
TG3W_EXPORT uint32_t tg3w_prim_count(void) { return g_prim_count; }
|
|
|
|
TG3W_EXPORT const tg3w_prim *tg3w_prim_at(uint32_t i) {
|
|
if (i >= g_prim_count) return NULL;
|
|
return &g_prims[i];
|
|
}
|
|
|
|
TG3W_EXPORT const float *tg3w_positions(void) { return g_positions; }
|
|
TG3W_EXPORT const float *tg3w_normals(void) { return g_normals; }
|
|
TG3W_EXPORT const float *tg3w_uvs(void) { return g_uvs; }
|
|
TG3W_EXPORT const uint32_t *tg3w_indices(void) { return g_indices; }
|
|
|
|
TG3W_EXPORT uint32_t tg3w_mesh_count(void) { return g_model.meshes_count; }
|
|
|
|
TG3W_EXPORT uint32_t tg3w_mesh_prim_start(uint32_t m) {
|
|
if (m >= g_model.meshes_count) return 0;
|
|
return g_mesh_prim_start[m];
|
|
}
|
|
|
|
TG3W_EXPORT uint32_t tg3w_mesh_prim_count(uint32_t m) {
|
|
if (m >= g_model.meshes_count) return 0;
|
|
return g_mesh_prim_count[m];
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Materials */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
TG3W_EXPORT uint32_t tg3w_material_count(void) { return g_model.materials_count; }
|
|
|
|
TG3W_EXPORT const float *tg3w_material_base_color(uint32_t i) {
|
|
static float c[4];
|
|
if (i >= g_model.materials_count) { c[0]=1;c[1]=1;c[2]=1;c[3]=1; return c; }
|
|
const tg3_material *m = &g_model.materials[i];
|
|
for (int k = 0; k < 4; k++) c[k] = (float)m->pbr_metallic_roughness.base_color_factor[k];
|
|
return c;
|
|
}
|
|
|
|
TG3W_EXPORT float tg3w_material_metallic(uint32_t i) {
|
|
if (i >= g_model.materials_count) return 1.0f;
|
|
return (float)g_model.materials[i].pbr_metallic_roughness.metallic_factor;
|
|
}
|
|
|
|
TG3W_EXPORT float tg3w_material_roughness(uint32_t i) {
|
|
if (i >= g_model.materials_count) return 1.0f;
|
|
return (float)g_model.materials[i].pbr_metallic_roughness.roughness_factor;
|
|
}
|
|
|
|
TG3W_EXPORT int tg3w_material_base_color_texture(uint32_t i) {
|
|
if (i >= g_model.materials_count) return -1;
|
|
return g_model.materials[i].pbr_metallic_roughness.base_color_texture.index;
|
|
}
|
|
|
|
TG3W_EXPORT int tg3w_material_alpha_mode(uint32_t i) {
|
|
if (i >= g_model.materials_count) return 0;
|
|
const tg3_str *a = &g_model.materials[i].alpha_mode;
|
|
if (a->len == 5 && strncmp(a->data, "BLEND", 5) == 0) return 2;
|
|
if (a->len == 4 && strncmp(a->data, "MASK", 4) == 0) return 1;
|
|
return 0;
|
|
}
|
|
|
|
TG3W_EXPORT float tg3w_material_alpha_cutoff(uint32_t i) {
|
|
if (i >= g_model.materials_count) return 0.5f;
|
|
return (float)g_model.materials[i].alpha_cutoff;
|
|
}
|
|
|
|
TG3W_EXPORT int tg3w_material_double_sided(uint32_t i) {
|
|
if (i >= g_model.materials_count) return 0;
|
|
return g_model.materials[i].double_sided;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Textures (raw image bytes, decoded client-side) */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
TG3W_EXPORT uint32_t tg3w_texture_count(void) { return g_model.textures_count; }
|
|
TG3W_EXPORT uint32_t tg3w_image_count(void) { return g_model.images_count; }
|
|
|
|
TG3W_EXPORT int tg3w_texture_source(uint32_t i) {
|
|
if (i >= g_model.textures_count) return -1;
|
|
return g_model.textures[i].source;
|
|
}
|
|
|
|
/* Image payload: either a bufferView (GLB / .bin) or a data URI in the
|
|
* glTF JSON. The v3 parser does not decode image bytes itself, so expose
|
|
* both paths to JS. */
|
|
TG3W_EXPORT int tg3w_image_buffer_view(uint32_t i) {
|
|
if (i >= g_model.images_count) return -1;
|
|
return g_model.images[i].buffer_view;
|
|
}
|
|
|
|
TG3W_EXPORT const char *tg3w_image_uri(uint32_t i) {
|
|
if (i >= g_model.images_count) return NULL;
|
|
return g_model.images[i].uri.data ? g_model.images[i].uri.data : NULL;
|
|
}
|
|
|
|
TG3W_EXPORT const uint8_t *tg3w_image_bytes(uint32_t i) {
|
|
if (i >= g_model.images_count) return NULL;
|
|
int32_t bv = g_model.images[i].buffer_view;
|
|
if (bv < 0 || bv >= (int32_t)g_model.buffer_views_count) return NULL;
|
|
const tg3_buffer_view *v = &g_model.buffer_views[bv];
|
|
if (v->buffer < 0 || v->buffer >= (int32_t)g_model.buffers_count) return NULL;
|
|
const tg3_buffer *b = &g_model.buffers[v->buffer];
|
|
if (!b->data.data || b->data.count < v->byte_offset + v->byte_length) return NULL;
|
|
return b->data.data + v->byte_offset;
|
|
}
|
|
|
|
TG3W_EXPORT uint64_t tg3w_image_size(uint32_t i) {
|
|
if (i >= g_model.images_count) return 0;
|
|
int32_t bv = g_model.images[i].buffer_view;
|
|
if (bv < 0 || bv >= (int32_t)g_model.buffer_views_count) return 0;
|
|
return g_model.buffer_views[bv].byte_length;
|
|
}
|
|
|
|
TG3W_EXPORT const char *tg3w_image_mime(uint32_t i) {
|
|
if (i >= g_model.images_count) return "";
|
|
return g_model.images[i].mime_type.data ? g_model.images[i].mime_type.data : "";
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Scene graph (default scene only) */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
TG3W_EXPORT uint32_t tg3w_node_count(void) { return g_model.nodes_count; }
|
|
|
|
TG3W_EXPORT int32_t tg3w_node_mesh(uint32_t i) {
|
|
if (i >= g_model.nodes_count) return -1;
|
|
return g_model.nodes[i].mesh;
|
|
}
|
|
|
|
TG3W_EXPORT uint32_t tg3w_node_child_count(uint32_t i) {
|
|
if (i >= g_model.nodes_count) return 0;
|
|
return g_model.nodes[i].children_count;
|
|
}
|
|
|
|
TG3W_EXPORT const int32_t *tg3w_node_children(uint32_t i) {
|
|
if (i >= g_model.nodes_count) return NULL;
|
|
return g_model.nodes[i].children;
|
|
}
|
|
|
|
TG3W_EXPORT const float *tg3w_node_trs(uint32_t i) {
|
|
static float trs[10];
|
|
if (i >= g_model.nodes_count) { memset(trs, 0, sizeof(trs)); trs[6] = 1.0f; return trs; }
|
|
const tg3_node *n = &g_model.nodes[i];
|
|
for (int k = 0; k < 3; k++) trs[k] = (float)n->translation[k];
|
|
for (int k = 0; k < 4; k++) trs[3 + k] = (float)n->rotation[k];
|
|
for (int k = 0; k < 3; k++) trs[7 + k] = (float)n->scale[k];
|
|
return trs;
|
|
}
|
|
|
|
TG3W_EXPORT int tg3w_default_scene(void) { return g_model.default_scene; }
|
|
|
|
TG3W_EXPORT uint32_t tg3w_scene_count(void) { return g_model.scenes_count; }
|
|
|
|
TG3W_EXPORT uint32_t tg3w_scene_node_count(uint32_t i) {
|
|
if (i >= g_model.scenes_count) return 0;
|
|
return g_model.scenes[i].nodes_count;
|
|
}
|
|
|
|
TG3W_EXPORT const int32_t *tg3w_scene_nodes(uint32_t i) {
|
|
if (i >= g_model.scenes_count) return NULL;
|
|
return g_model.scenes[i].nodes;
|
|
}
|