From 56c9da8c1a0c7361ecd1546e10931e3a566438ea Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 17 Feb 2026 16:04:38 -0600 Subject: [PATCH] fix syntax --- fbx.c | 398 +++++++++++++++++++++++++++++--------------------------- gltf.c | 343 ++++++++++++++++++++++++------------------------ obj.c | 404 +++++++++++++++++++++++++-------------------------------- 3 files changed, 558 insertions(+), 587 deletions(-) diff --git a/fbx.c b/fbx.c index 00ab21d..65f9576 100644 --- a/fbx.c +++ b/fbx.c @@ -6,18 +6,20 @@ static JSValue make_float_array(JSContext *js, const double *arr, int count) { - JSValue a = JS_NewArray(js); + JS_FRAME(js); + JS_ROOT(a, JS_NewArray(js)); for (int i = 0; i < count; i++) - JS_SetPropertyNumber(js, a, i, JS_NewFloat64(js, arr[i])); - return a; + JS_SetPropertyNumber(js, a.val, i, JS_NewFloat64(js, arr[i])); + JS_RETURN(a.val); } static JSValue make_float_array_f(JSContext *js, const float *arr, int count) { - JSValue a = JS_NewArray(js); + JS_FRAME(js); + JS_ROOT(a, JS_NewArray(js)); for (int i = 0; i < count; i++) - JS_SetPropertyNumber(js, a, i, JS_NewFloat64(js, arr[i])); - return a; + JS_SetPropertyNumber(js, a.val, i, JS_NewFloat64(js, arr[i])); + JS_RETURN(a.val); } JSValue js_fbx_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) @@ -36,7 +38,8 @@ JSValue js_fbx_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a if (!scene) return JS_ThrowReferenceError(js, "failed to parse FBX: %s", error.description.data); - JSValue obj = JS_NewObject(js); + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); // Count total vertices and indices across all meshes size_t total_vertices = 0; @@ -141,66 +144,73 @@ JSValue js_fbx_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a } // Create buffer - JSValue buffers_arr = JS_NewArray(js); - JSValue buf = JS_NewObject(js); - JS_SetPropertyStr(js, buf, "blob", js_new_blob_stoned_copy(js, buffer_data, total_buffer_size)); - JS_SetPropertyStr(js, buf, "byte_length", JS_NewInt64(js, total_buffer_size)); - JS_SetPropertyNumber(js, buffers_arr, 0, buf); - JS_SetPropertyStr(js, obj, "buffers", buffers_arr); + JS_ROOT(buffers_arr, JS_NewArray(js)); + { + JS_ROOT(buf, JS_NewObject(js)); + JS_SetPropertyStr(js, buf.val, "blob", js_new_blob_stoned_copy(js, buffer_data, total_buffer_size)); + JS_SetPropertyStr(js, buf.val, "byte_length", JS_NewInt64(js, total_buffer_size)); + JS_SetPropertyNumber(js, buffers_arr.val, 0, buf.val); + } + JS_SetPropertyStr(js, obj.val, "buffers", buffers_arr.val); // Create views - JSValue views_arr = JS_NewArray(js); + JS_ROOT(views_arr, JS_NewArray(js)); int view_idx = 0; - JSValue pos_view = JS_NewObject(js); - JS_SetPropertyStr(js, pos_view, "buffer", JS_NewInt32(js, 0)); - JS_SetPropertyStr(js, pos_view, "byte_offset", JS_NewInt64(js, 0)); - JS_SetPropertyStr(js, pos_view, "byte_length", JS_NewInt64(js, pos_size)); - JS_SetPropertyStr(js, pos_view, "byte_stride", JS_NULL); - JS_SetPropertyStr(js, pos_view, "usage", JS_NewString(js, "vertex")); - int pos_view_idx = view_idx; - JS_SetPropertyNumber(js, views_arr, view_idx++, pos_view); + { + JS_ROOT(pos_view, JS_NewObject(js)); + JS_SetPropertyStr(js, pos_view.val, "buffer", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, pos_view.val, "byte_offset", JS_NewInt64(js, 0)); + JS_SetPropertyStr(js, pos_view.val, "byte_length", JS_NewInt64(js, pos_size)); + JS_SetPropertyStr(js, pos_view.val, "byte_stride", JS_NULL); + JS_SetPropertyStr(js, pos_view.val, "usage", JS_NewString(js, "vertex")); + JS_SetPropertyNumber(js, views_arr.val, view_idx++, pos_view.val); + } + int pos_view_idx = 0; int norm_view_idx = -1; if (global_has_normals) { - JSValue norm_view = JS_NewObject(js); - JS_SetPropertyStr(js, norm_view, "buffer", JS_NewInt32(js, 0)); - JS_SetPropertyStr(js, norm_view, "byte_offset", JS_NewInt64(js, pos_size)); - JS_SetPropertyStr(js, norm_view, "byte_length", JS_NewInt64(js, norm_size)); - JS_SetPropertyStr(js, norm_view, "byte_stride", JS_NULL); - JS_SetPropertyStr(js, norm_view, "usage", JS_NewString(js, "vertex")); + JS_ROOT(norm_view, JS_NewObject(js)); + JS_SetPropertyStr(js, norm_view.val, "buffer", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, norm_view.val, "byte_offset", JS_NewInt64(js, pos_size)); + JS_SetPropertyStr(js, norm_view.val, "byte_length", JS_NewInt64(js, norm_size)); + JS_SetPropertyStr(js, norm_view.val, "byte_stride", JS_NULL); + JS_SetPropertyStr(js, norm_view.val, "usage", JS_NewString(js, "vertex")); norm_view_idx = view_idx; - JS_SetPropertyNumber(js, views_arr, view_idx++, norm_view); + JS_SetPropertyNumber(js, views_arr.val, view_idx++, norm_view.val); } int uv_view_idx = -1; if (global_has_uvs) { - JSValue uv_view = JS_NewObject(js); - JS_SetPropertyStr(js, uv_view, "buffer", JS_NewInt32(js, 0)); - JS_SetPropertyStr(js, uv_view, "byte_offset", JS_NewInt64(js, pos_size + norm_size)); - JS_SetPropertyStr(js, uv_view, "byte_length", JS_NewInt64(js, uv_size)); - JS_SetPropertyStr(js, uv_view, "byte_stride", JS_NULL); - JS_SetPropertyStr(js, uv_view, "usage", JS_NewString(js, "vertex")); + JS_ROOT(uv_view, JS_NewObject(js)); + JS_SetPropertyStr(js, uv_view.val, "buffer", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, uv_view.val, "byte_offset", JS_NewInt64(js, pos_size + norm_size)); + JS_SetPropertyStr(js, uv_view.val, "byte_length", JS_NewInt64(js, uv_size)); + JS_SetPropertyStr(js, uv_view.val, "byte_stride", JS_NULL); + JS_SetPropertyStr(js, uv_view.val, "usage", JS_NewString(js, "vertex")); uv_view_idx = view_idx; - JS_SetPropertyNumber(js, views_arr, view_idx++, uv_view); + JS_SetPropertyNumber(js, views_arr.val, view_idx++, uv_view.val); } - JSValue idx_view = JS_NewObject(js); - JS_SetPropertyStr(js, idx_view, "buffer", JS_NewInt32(js, 0)); - JS_SetPropertyStr(js, idx_view, "byte_offset", JS_NewInt64(js, pos_size + norm_size + uv_size)); - JS_SetPropertyStr(js, idx_view, "byte_length", JS_NewInt64(js, idx_size)); - JS_SetPropertyStr(js, idx_view, "byte_stride", JS_NULL); - JS_SetPropertyStr(js, idx_view, "usage", JS_NewString(js, "index")); - int idx_view_idx = view_idx; - JS_SetPropertyNumber(js, views_arr, view_idx++, idx_view); + { + JS_ROOT(idx_view, JS_NewObject(js)); + JS_SetPropertyStr(js, idx_view.val, "buffer", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, idx_view.val, "byte_offset", JS_NewInt64(js, pos_size + norm_size + uv_size)); + JS_SetPropertyStr(js, idx_view.val, "byte_length", JS_NewInt64(js, idx_size)); + JS_SetPropertyStr(js, idx_view.val, "byte_stride", JS_NULL); + JS_SetPropertyStr(js, idx_view.val, "usage", JS_NewString(js, "index")); + int idx_view_idx_tmp = view_idx; + JS_SetPropertyNumber(js, views_arr.val, view_idx++, idx_view.val); + } + int idx_view_idx = view_idx - 1; - JS_SetPropertyStr(js, obj, "views", views_arr); + JS_SetPropertyStr(js, obj.val, "views", views_arr.val); // Create accessors per mesh - JSValue accessors_arr = JS_NewArray(js); + JS_ROOT(accessors_arr, JS_NewArray(js)); int acc_idx = 0; - JSValue meshes_arr = JS_NewArray(js); + JS_ROOT(meshes_arr, JS_NewArray(js)); for (size_t mi = 0; mi < scene->meshes.count; mi++) { ufbx_mesh *mesh = scene->meshes.data[mi]; @@ -208,155 +218,161 @@ JSValue js_fbx_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a // Position accessor int mesh_pos_acc = acc_idx; - JSValue spa = JS_NewObject(js); - JS_SetPropertyStr(js, spa, "view", JS_NewInt32(js, pos_view_idx)); - JS_SetPropertyStr(js, spa, "byte_offset", JS_NewInt64(js, info->vertex_start * 3 * sizeof(float))); - JS_SetPropertyStr(js, spa, "count", JS_NewInt64(js, info->vertex_count)); - JS_SetPropertyStr(js, spa, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, spa, "type", JS_NewString(js, "vec3")); - JS_SetPropertyStr(js, spa, "normalized", JS_FALSE); - JS_SetPropertyStr(js, spa, "min", JS_NULL); - JS_SetPropertyStr(js, spa, "max", JS_NULL); - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, spa); + { + JS_ROOT(spa, JS_NewObject(js)); + JS_SetPropertyStr(js, spa.val, "view", JS_NewInt32(js, pos_view_idx)); + JS_SetPropertyStr(js, spa.val, "byte_offset", JS_NewInt64(js, info->vertex_start * 3 * sizeof(float))); + JS_SetPropertyStr(js, spa.val, "count", JS_NewInt64(js, info->vertex_count)); + JS_SetPropertyStr(js, spa.val, "component_type", JS_NewString(js, "f32")); + JS_SetPropertyStr(js, spa.val, "type", JS_NewString(js, "vec3")); + JS_SetPropertyStr(js, spa.val, "normalized", JS_FALSE); + JS_SetPropertyStr(js, spa.val, "min", JS_NULL); + JS_SetPropertyStr(js, spa.val, "max", JS_NULL); + JS_SetPropertyNumber(js, accessors_arr.val, acc_idx++, spa.val); + } int mesh_norm_acc = -1; if (global_has_normals) { mesh_norm_acc = acc_idx; - JSValue sna = JS_NewObject(js); - JS_SetPropertyStr(js, sna, "view", JS_NewInt32(js, norm_view_idx)); - JS_SetPropertyStr(js, sna, "byte_offset", JS_NewInt64(js, info->vertex_start * 3 * sizeof(float))); - JS_SetPropertyStr(js, sna, "count", JS_NewInt64(js, info->vertex_count)); - JS_SetPropertyStr(js, sna, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, sna, "type", JS_NewString(js, "vec3")); - JS_SetPropertyStr(js, sna, "normalized", JS_FALSE); - JS_SetPropertyStr(js, sna, "min", JS_NULL); - JS_SetPropertyStr(js, sna, "max", JS_NULL); - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, sna); + JS_ROOT(sna, JS_NewObject(js)); + JS_SetPropertyStr(js, sna.val, "view", JS_NewInt32(js, norm_view_idx)); + JS_SetPropertyStr(js, sna.val, "byte_offset", JS_NewInt64(js, info->vertex_start * 3 * sizeof(float))); + JS_SetPropertyStr(js, sna.val, "count", JS_NewInt64(js, info->vertex_count)); + JS_SetPropertyStr(js, sna.val, "component_type", JS_NewString(js, "f32")); + JS_SetPropertyStr(js, sna.val, "type", JS_NewString(js, "vec3")); + JS_SetPropertyStr(js, sna.val, "normalized", JS_FALSE); + JS_SetPropertyStr(js, sna.val, "min", JS_NULL); + JS_SetPropertyStr(js, sna.val, "max", JS_NULL); + JS_SetPropertyNumber(js, accessors_arr.val, acc_idx++, sna.val); } int mesh_uv_acc = -1; if (global_has_uvs) { mesh_uv_acc = acc_idx; - JSValue sua = JS_NewObject(js); - JS_SetPropertyStr(js, sua, "view", JS_NewInt32(js, uv_view_idx)); - JS_SetPropertyStr(js, sua, "byte_offset", JS_NewInt64(js, info->vertex_start * 2 * sizeof(float))); - JS_SetPropertyStr(js, sua, "count", JS_NewInt64(js, info->vertex_count)); - JS_SetPropertyStr(js, sua, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, sua, "type", JS_NewString(js, "vec2")); - JS_SetPropertyStr(js, sua, "normalized", JS_FALSE); - JS_SetPropertyStr(js, sua, "min", JS_NULL); - JS_SetPropertyStr(js, sua, "max", JS_NULL); - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, sua); + JS_ROOT(sua, JS_NewObject(js)); + JS_SetPropertyStr(js, sua.val, "view", JS_NewInt32(js, uv_view_idx)); + JS_SetPropertyStr(js, sua.val, "byte_offset", JS_NewInt64(js, info->vertex_start * 2 * sizeof(float))); + JS_SetPropertyStr(js, sua.val, "count", JS_NewInt64(js, info->vertex_count)); + JS_SetPropertyStr(js, sua.val, "component_type", JS_NewString(js, "f32")); + JS_SetPropertyStr(js, sua.val, "type", JS_NewString(js, "vec2")); + JS_SetPropertyStr(js, sua.val, "normalized", JS_FALSE); + JS_SetPropertyStr(js, sua.val, "min", JS_NULL); + JS_SetPropertyStr(js, sua.val, "max", JS_NULL); + JS_SetPropertyNumber(js, accessors_arr.val, acc_idx++, sua.val); } int mesh_idx_acc = acc_idx; - JSValue sia = JS_NewObject(js); - JS_SetPropertyStr(js, sia, "view", JS_NewInt32(js, idx_view_idx)); - JS_SetPropertyStr(js, sia, "byte_offset", JS_NewInt64(js, info->index_start * (use_32bit ? sizeof(uint32_t) : sizeof(uint16_t)))); - JS_SetPropertyStr(js, sia, "count", JS_NewInt64(js, info->index_count)); - JS_SetPropertyStr(js, sia, "component_type", JS_NewString(js, use_32bit ? "u32" : "u16")); - JS_SetPropertyStr(js, sia, "type", JS_NewString(js, "scalar")); - JS_SetPropertyStr(js, sia, "normalized", JS_FALSE); - JS_SetPropertyStr(js, sia, "min", JS_NULL); - JS_SetPropertyStr(js, sia, "max", JS_NULL); - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, sia); + { + JS_ROOT(sia, JS_NewObject(js)); + JS_SetPropertyStr(js, sia.val, "view", JS_NewInt32(js, idx_view_idx)); + JS_SetPropertyStr(js, sia.val, "byte_offset", JS_NewInt64(js, info->index_start * (use_32bit ? sizeof(uint32_t) : sizeof(uint16_t)))); + JS_SetPropertyStr(js, sia.val, "count", JS_NewInt64(js, info->index_count)); + JS_SetPropertyStr(js, sia.val, "component_type", JS_NewString(js, use_32bit ? "u32" : "u16")); + JS_SetPropertyStr(js, sia.val, "type", JS_NewString(js, "scalar")); + JS_SetPropertyStr(js, sia.val, "normalized", JS_FALSE); + JS_SetPropertyStr(js, sia.val, "min", JS_NULL); + JS_SetPropertyStr(js, sia.val, "max", JS_NULL); + JS_SetPropertyNumber(js, accessors_arr.val, acc_idx++, sia.val); + } // Create mesh - JSValue m = JS_NewObject(js); - JS_SetPropertyStr(js, m, "name", mesh->element.name.length > 0 ? JS_NewString(js, mesh->element.name.data) : JS_NULL); + JS_ROOT(m, JS_NewObject(js)); + JS_SetPropertyStr(js, m.val, "name", mesh->element.name.length > 0 ? JS_NewString(js, mesh->element.name.data) : JS_NULL); - JSValue prims_arr = JS_NewArray(js); - JSValue prim = JS_NewObject(js); - JS_SetPropertyStr(js, prim, "topology", JS_NewString(js, "triangles")); + JS_ROOT(prims_arr, JS_NewArray(js)); + { + JS_ROOT(prim, JS_NewObject(js)); + JS_SetPropertyStr(js, prim.val, "topology", JS_NewString(js, "triangles")); - JSValue attrs = JS_NewObject(js); - JS_SetPropertyStr(js, attrs, "POSITION", JS_NewInt32(js, mesh_pos_acc)); - if (mesh_norm_acc >= 0) - JS_SetPropertyStr(js, attrs, "NORMAL", JS_NewInt32(js, mesh_norm_acc)); - if (mesh_uv_acc >= 0) - JS_SetPropertyStr(js, attrs, "TEXCOORD_0", JS_NewInt32(js, mesh_uv_acc)); - JS_SetPropertyStr(js, prim, "attributes", attrs); + JS_ROOT(attrs, JS_NewObject(js)); + JS_SetPropertyStr(js, attrs.val, "POSITION", JS_NewInt32(js, mesh_pos_acc)); + if (mesh_norm_acc >= 0) + JS_SetPropertyStr(js, attrs.val, "NORMAL", JS_NewInt32(js, mesh_norm_acc)); + if (mesh_uv_acc >= 0) + JS_SetPropertyStr(js, attrs.val, "TEXCOORD_0", JS_NewInt32(js, mesh_uv_acc)); + JS_SetPropertyStr(js, prim.val, "attributes", attrs.val); - JS_SetPropertyStr(js, prim, "indices", JS_NewInt32(js, mesh_idx_acc)); - JS_SetPropertyStr(js, prim, "material", mesh->materials.count > 0 ? JS_NewInt32(js, mesh->materials.data[0]->typed_id) : JS_NULL); + JS_SetPropertyStr(js, prim.val, "indices", JS_NewInt32(js, mesh_idx_acc)); + JS_SetPropertyStr(js, prim.val, "material", mesh->materials.count > 0 ? JS_NewInt32(js, mesh->materials.data[0]->typed_id) : JS_NULL); - JS_SetPropertyNumber(js, prims_arr, 0, prim); - JS_SetPropertyStr(js, m, "primitives", prims_arr); + JS_SetPropertyNumber(js, prims_arr.val, 0, prim.val); + } + JS_SetPropertyStr(js, m.val, "primitives", prims_arr.val); - JS_SetPropertyNumber(js, meshes_arr, mi, m); + JS_SetPropertyNumber(js, meshes_arr.val, mi, m.val); } - JS_SetPropertyStr(js, obj, "accessors", accessors_arr); - JS_SetPropertyStr(js, obj, "meshes", meshes_arr); + JS_SetPropertyStr(js, obj.val, "accessors", accessors_arr.val); + JS_SetPropertyStr(js, obj.val, "meshes", meshes_arr.val); // Materials - JSValue materials_arr = JS_NewArray(js); + JS_ROOT(materials_arr, JS_NewArray(js)); for (size_t i = 0; i < scene->materials.count; i++) { ufbx_material *mat = scene->materials.data[i]; - JSValue m = JS_NewObject(js); - JS_SetPropertyStr(js, m, "name", mat->element.name.length > 0 ? JS_NewString(js, mat->element.name.data) : JS_NULL); + JS_ROOT(m, JS_NewObject(js)); + JS_SetPropertyStr(js, m.val, "name", mat->element.name.length > 0 ? JS_NewString(js, mat->element.name.data) : JS_NULL); - JSValue pbr = JS_NewObject(js); + JS_ROOT(pbr, JS_NewObject(js)); float bc[4] = { (float)mat->pbr.base_color.value_vec4.x, (float)mat->pbr.base_color.value_vec4.y, (float)mat->pbr.base_color.value_vec4.z, (float)mat->pbr.base_color.value_vec4.w }; - JS_SetPropertyStr(js, pbr, "base_color_factor", make_float_array_f(js, bc, 4)); - JS_SetPropertyStr(js, pbr, "base_color_texture", JS_NULL); - JS_SetPropertyStr(js, pbr, "metallic_factor", JS_NewFloat64(js, mat->pbr.metalness.value_real)); - JS_SetPropertyStr(js, pbr, "roughness_factor", JS_NewFloat64(js, mat->pbr.roughness.value_real)); - JS_SetPropertyStr(js, pbr, "metallic_roughness_texture", JS_NULL); - JS_SetPropertyStr(js, pbr, "normal_texture", JS_NULL); - JS_SetPropertyStr(js, pbr, "occlusion_texture", JS_NULL); + JS_SetPropertyStr(js, pbr.val, "base_color_factor", make_float_array_f(js, bc, 4)); + JS_SetPropertyStr(js, pbr.val, "base_color_texture", JS_NULL); + JS_SetPropertyStr(js, pbr.val, "metallic_factor", JS_NewFloat64(js, mat->pbr.metalness.value_real)); + JS_SetPropertyStr(js, pbr.val, "roughness_factor", JS_NewFloat64(js, mat->pbr.roughness.value_real)); + JS_SetPropertyStr(js, pbr.val, "metallic_roughness_texture", JS_NULL); + JS_SetPropertyStr(js, pbr.val, "normal_texture", JS_NULL); + JS_SetPropertyStr(js, pbr.val, "occlusion_texture", JS_NULL); float ef[3] = { (float)mat->pbr.emission_color.value_vec3.x, (float)mat->pbr.emission_color.value_vec3.y, (float)mat->pbr.emission_color.value_vec3.z }; - JS_SetPropertyStr(js, pbr, "emissive_factor", make_float_array_f(js, ef, 3)); - JS_SetPropertyStr(js, pbr, "emissive_texture", JS_NULL); - JS_SetPropertyStr(js, m, "pbr", pbr); + JS_SetPropertyStr(js, pbr.val, "emissive_factor", make_float_array_f(js, ef, 3)); + JS_SetPropertyStr(js, pbr.val, "emissive_texture", JS_NULL); + JS_SetPropertyStr(js, m.val, "pbr", pbr.val); - JS_SetPropertyStr(js, m, "alpha_mode", JS_NewString(js, "OPAQUE")); - JS_SetPropertyStr(js, m, "alpha_cutoff", JS_NewFloat64(js, 0.5)); - JS_SetPropertyStr(js, m, "double_sided", JS_FALSE); + JS_SetPropertyStr(js, m.val, "alpha_mode", JS_NewString(js, "OPAQUE")); + JS_SetPropertyStr(js, m.val, "alpha_cutoff", JS_NewFloat64(js, 0.5)); + JS_SetPropertyStr(js, m.val, "double_sided", JS_FALSE); - JS_SetPropertyNumber(js, materials_arr, i, m); + JS_SetPropertyNumber(js, materials_arr.val, i, m.val); } - JS_SetPropertyStr(js, obj, "materials", materials_arr); + JS_SetPropertyStr(js, obj.val, "materials", materials_arr.val); // Images/textures (simplified - just list texture files) - JSValue images_arr = JS_NewArray(js); + JS_ROOT(images_arr, JS_NewArray(js)); for (size_t i = 0; i < scene->texture_files.count; i++) { ufbx_texture_file *tf = &scene->texture_files.data[i]; - JSValue im = JS_NewObject(js); - JS_SetPropertyStr(js, im, "kind", JS_NewString(js, "uri")); - JS_SetPropertyStr(js, im, "uri", tf->filename.length > 0 ? JS_NewString(js, tf->filename.data) : JS_NULL); - JS_SetPropertyStr(js, im, "mime", JS_NULL); - JS_SetPropertyNumber(js, images_arr, i, im); + JS_ROOT(im, JS_NewObject(js)); + JS_SetPropertyStr(js, im.val, "kind", JS_NewString(js, "uri")); + JS_SetPropertyStr(js, im.val, "uri", tf->filename.length > 0 ? JS_NewString(js, tf->filename.data) : JS_NULL); + JS_SetPropertyStr(js, im.val, "mime", JS_NULL); + JS_SetPropertyNumber(js, images_arr.val, i, im.val); } - JS_SetPropertyStr(js, obj, "images", images_arr); + JS_SetPropertyStr(js, obj.val, "images", images_arr.val); - JSValue textures_arr = JS_NewArray(js); + JS_ROOT(textures_arr, JS_NewArray(js)); for (size_t i = 0; i < scene->textures.count; i++) { ufbx_texture *tex = scene->textures.data[i]; - JSValue t = JS_NewObject(js); - JS_SetPropertyStr(js, t, "image", tex->file_index != UFBX_NO_INDEX ? JS_NewInt32(js, tex->file_index) : JS_NULL); - JS_SetPropertyStr(js, t, "sampler", JS_NULL); - JS_SetPropertyNumber(js, textures_arr, i, t); + JS_ROOT(t, JS_NewObject(js)); + JS_SetPropertyStr(js, t.val, "image", tex->file_index != UFBX_NO_INDEX ? JS_NewInt32(js, tex->file_index) : JS_NULL); + JS_SetPropertyStr(js, t.val, "sampler", JS_NULL); + JS_SetPropertyNumber(js, textures_arr.val, i, t.val); } - JS_SetPropertyStr(js, obj, "textures", textures_arr); + JS_SetPropertyStr(js, obj.val, "textures", textures_arr.val); - JS_SetPropertyStr(js, obj, "samplers", JS_NewArray(js)); + JS_SetPropertyStr(js, obj.val, "samplers", JS_NewArray(js)); // Nodes - JSValue nodes_arr = JS_NewArray(js); + JS_ROOT(nodes_arr, JS_NewArray(js)); for (size_t i = 0; i < scene->nodes.count; i++) { ufbx_node *node = scene->nodes.data[i]; - JSValue n = JS_NewObject(js); - JS_SetPropertyStr(js, n, "name", node->element.name.length > 0 ? JS_NewString(js, node->element.name.data) : JS_NULL); + JS_ROOT(n, JS_NewObject(js)); + JS_SetPropertyStr(js, n.val, "name", node->element.name.length > 0 ? JS_NewString(js, node->element.name.data) : JS_NULL); // Find mesh index if this node has a mesh int mesh_idx = -1; @@ -368,99 +384,101 @@ JSValue js_fbx_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a } } } - JS_SetPropertyStr(js, n, "mesh", mesh_idx >= 0 ? JS_NewInt32(js, mesh_idx) : JS_NULL); + JS_SetPropertyStr(js, n.val, "mesh", mesh_idx >= 0 ? JS_NewInt32(js, mesh_idx) : JS_NULL); - JSValue children = JS_NewArray(js); + JS_ROOT(children, JS_NewArray(js)); for (size_t ci = 0; ci < node->children.count; ci++) { // Find child node index for (size_t ni = 0; ni < scene->nodes.count; ni++) { if (scene->nodes.data[ni] == node->children.data[ci]) { - JS_SetPropertyNumber(js, children, ci, JS_NewInt32(js, ni)); + JS_SetPropertyNumber(js, children.val, ci, JS_NewInt32(js, ni)); break; } } } - JS_SetPropertyStr(js, n, "children", children); + JS_SetPropertyStr(js, n.val, "children", children.val); - JS_SetPropertyStr(js, n, "matrix", JS_NULL); - double t[3] = {node->local_transform.translation.x, node->local_transform.translation.y, node->local_transform.translation.z}; - double r[4] = {node->local_transform.rotation.x, node->local_transform.rotation.y, node->local_transform.rotation.z, node->local_transform.rotation.w}; - double s[3] = {node->local_transform.scale.x, node->local_transform.scale.y, node->local_transform.scale.z}; - JS_SetPropertyStr(js, n, "translation", make_float_array(js, t, 3)); - JS_SetPropertyStr(js, n, "rotation", make_float_array(js, r, 4)); - JS_SetPropertyStr(js, n, "scale", make_float_array(js, s, 3)); - JS_SetPropertyStr(js, n, "skin", JS_NULL); + JS_SetPropertyStr(js, n.val, "matrix", JS_NULL); + double tr[3] = {node->local_transform.translation.x, node->local_transform.translation.y, node->local_transform.translation.z}; + double ro[4] = {node->local_transform.rotation.x, node->local_transform.rotation.y, node->local_transform.rotation.z, node->local_transform.rotation.w}; + double sc[3] = {node->local_transform.scale.x, node->local_transform.scale.y, node->local_transform.scale.z}; + JS_SetPropertyStr(js, n.val, "translation", make_float_array(js, tr, 3)); + JS_SetPropertyStr(js, n.val, "rotation", make_float_array(js, ro, 4)); + JS_SetPropertyStr(js, n.val, "scale", make_float_array(js, sc, 3)); + JS_SetPropertyStr(js, n.val, "skin", JS_NULL); - JS_SetPropertyNumber(js, nodes_arr, i, n); + JS_SetPropertyNumber(js, nodes_arr.val, i, n.val); } - JS_SetPropertyStr(js, obj, "nodes", nodes_arr); + JS_SetPropertyStr(js, obj.val, "nodes", nodes_arr.val); // Scenes - FBX has one implicit scene with root node - JSValue scenes_arr = JS_NewArray(js); - JSValue scene_obj = JS_NewObject(js); - JSValue scene_nodes = JS_NewArray(js); - // Find root node index - for (size_t i = 0; i < scene->nodes.count; i++) { - if (scene->nodes.data[i] == scene->root_node) { - JS_SetPropertyNumber(js, scene_nodes, 0, JS_NewInt32(js, i)); - break; + JS_ROOT(scenes_arr, JS_NewArray(js)); + { + JS_ROOT(scene_obj, JS_NewObject(js)); + JS_ROOT(scene_nodes, JS_NewArray(js)); + // Find root node index + for (size_t i = 0; i < scene->nodes.count; i++) { + if (scene->nodes.data[i] == scene->root_node) { + JS_SetPropertyNumber(js, scene_nodes.val, 0, JS_NewInt32(js, i)); + break; + } } + JS_SetPropertyStr(js, scene_obj.val, "nodes", scene_nodes.val); + JS_SetPropertyNumber(js, scenes_arr.val, 0, scene_obj.val); } - JS_SetPropertyStr(js, scene_obj, "nodes", scene_nodes); - JS_SetPropertyNumber(js, scenes_arr, 0, scene_obj); - JS_SetPropertyStr(js, obj, "scenes", scenes_arr); - JS_SetPropertyStr(js, obj, "scene", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, obj.val, "scenes", scenes_arr.val); + JS_SetPropertyStr(js, obj.val, "scene", JS_NewInt32(js, 0)); // Animations - JSValue anims_arr = JS_NewArray(js); + JS_ROOT(anims_arr, JS_NewArray(js)); for (size_t ai = 0; ai < scene->anim_stacks.count; ai++) { ufbx_anim_stack *stack = scene->anim_stacks.data[ai]; - JSValue a = JS_NewObject(js); - JS_SetPropertyStr(js, a, "name", stack->element.name.length > 0 ? JS_NewString(js, stack->element.name.data) : JS_NULL); - JS_SetPropertyStr(js, a, "samplers", JS_NewArray(js)); - JS_SetPropertyStr(js, a, "channels", JS_NewArray(js)); - JS_SetPropertyNumber(js, anims_arr, ai, a); + JS_ROOT(a, JS_NewObject(js)); + JS_SetPropertyStr(js, a.val, "name", stack->element.name.length > 0 ? JS_NewString(js, stack->element.name.data) : JS_NULL); + JS_SetPropertyStr(js, a.val, "samplers", JS_NewArray(js)); + JS_SetPropertyStr(js, a.val, "channels", JS_NewArray(js)); + JS_SetPropertyNumber(js, anims_arr.val, ai, a.val); } - JS_SetPropertyStr(js, obj, "animations", anims_arr); + JS_SetPropertyStr(js, obj.val, "animations", anims_arr.val); // Skins - JSValue skins_arr = JS_NewArray(js); + JS_ROOT(skins_arr, JS_NewArray(js)); for (size_t i = 0; i < scene->skin_deformers.count; i++) { ufbx_skin_deformer *skin = scene->skin_deformers.data[i]; - JSValue s = JS_NewObject(js); - JS_SetPropertyStr(js, s, "name", skin->element.name.length > 0 ? JS_NewString(js, skin->element.name.data) : JS_NULL); + JS_ROOT(s, JS_NewObject(js)); + JS_SetPropertyStr(js, s.val, "name", skin->element.name.length > 0 ? JS_NewString(js, skin->element.name.data) : JS_NULL); - JSValue joints = JS_NewArray(js); + JS_ROOT(joints, JS_NewArray(js)); for (size_t ci = 0; ci < skin->clusters.count; ci++) { ufbx_skin_cluster *cluster = skin->clusters.data[ci]; if (cluster->bone_node) { for (size_t ni = 0; ni < scene->nodes.count; ni++) { if (scene->nodes.data[ni] == cluster->bone_node) { - JS_SetPropertyNumber(js, joints, ci, JS_NewInt32(js, ni)); + JS_SetPropertyNumber(js, joints.val, ci, JS_NewInt32(js, ni)); break; } } } } - JS_SetPropertyStr(js, s, "joints", joints); - JS_SetPropertyStr(js, s, "inverse_bind_matrices", JS_NULL); - JS_SetPropertyStr(js, s, "skeleton", JS_NULL); + JS_SetPropertyStr(js, s.val, "joints", joints.val); + JS_SetPropertyStr(js, s.val, "inverse_bind_matrices", JS_NULL); + JS_SetPropertyStr(js, s.val, "skeleton", JS_NULL); - JS_SetPropertyNumber(js, skins_arr, i, s); + JS_SetPropertyNumber(js, skins_arr.val, i, s.val); } - JS_SetPropertyStr(js, obj, "skins", skins_arr); + JS_SetPropertyStr(js, obj.val, "skins", skins_arr.val); // Extensions - JSValue exts = JS_NewObject(js); - JS_SetPropertyStr(js, exts, "used", JS_NewArray(js)); - JS_SetPropertyStr(js, exts, "required", JS_NewArray(js)); - JS_SetPropertyStr(js, obj, "extensions", exts); + JS_ROOT(exts, JS_NewObject(js)); + JS_SetPropertyStr(js, exts.val, "used", JS_NewArray(js)); + JS_SetPropertyStr(js, exts.val, "required", JS_NewArray(js)); + JS_SetPropertyStr(js, obj.val, "extensions", exts.val); free(mesh_infos); free(buffer_data); ufbx_free_scene(scene); - return obj; + JS_RETURN(obj.val); } static const JSCFunctionListEntry js_fbx_funcs[] = { diff --git a/gltf.c b/gltf.c index 0aece12..26d511f 100644 --- a/gltf.c +++ b/gltf.c @@ -71,26 +71,28 @@ static const char *attribute_name(cgltf_attribute_type t, int index) static JSValue make_float_array(JSContext *js, const float *arr, int count) { - JSValue a = JS_NewArray(js); + JS_FRAME(js); + JS_ROOT(a, JS_NewArray(js)); for (int i = 0; i < count; i++) - JS_SetPropertyNumber(js, a, i, JS_NewFloat64(js, arr[i])); - return a; + JS_SetPropertyNumber(js, a.val, i, JS_NewFloat64(js, arr[i])); + JS_RETURN(a.val); } static JSValue make_texture_info(JSContext *js, cgltf_texture_view *tv, cgltf_data *data) { if (!tv->texture) return JS_NULL; - JSValue o = JS_NewObject(js); - JS_SetPropertyStr(js, o, "texture", JS_NewInt32(js, (int)(tv->texture - data->textures))); - JS_SetPropertyStr(js, o, "texcoord", JS_NewInt32(js, tv->texcoord)); + JS_FRAME(js); + JS_ROOT(o, JS_NewObject(js)); + JS_SetPropertyStr(js, o.val, "texture", JS_NewInt32(js, (int)(tv->texture - data->textures))); + JS_SetPropertyStr(js, o.val, "texcoord", JS_NewInt32(js, tv->texcoord)); if (tv->has_transform) { - JSValue tr = JS_NewObject(js); - JS_SetPropertyStr(js, tr, "offset", make_float_array(js, tv->transform.offset, 2)); - JS_SetPropertyStr(js, tr, "scale", make_float_array(js, tv->transform.scale, 2)); - JS_SetPropertyStr(js, tr, "rotation", JS_NewFloat64(js, tv->transform.rotation)); - JS_SetPropertyStr(js, o, "transform", tr); + JS_ROOT(tr, JS_NewObject(js)); + JS_SetPropertyStr(js, tr.val, "offset", make_float_array(js, tv->transform.offset, 2)); + JS_SetPropertyStr(js, tr.val, "scale", make_float_array(js, tv->transform.scale, 2)); + JS_SetPropertyStr(js, tr.val, "rotation", JS_NewFloat64(js, tv->transform.rotation)); + JS_SetPropertyStr(js, o.val, "transform", tr.val); } - return o; + JS_RETURN(o.val); } JSValue js_gltf_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) @@ -112,259 +114,260 @@ JSValue js_gltf_decode(JSContext *js, JSValue this_val, int argc, JSValueConst * return JS_ThrowReferenceError(js, "failed to load glTF buffers: %d", result); } - JSValue obj = JS_NewObject(js); + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); // Buffers - JSValue buffers_arr = JS_NewArray(js); + JS_ROOT(buffers_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->buffers_count; i++) { cgltf_buffer *buf = &data->buffers[i]; - JSValue b = JS_NewObject(js); + JS_ROOT(b, JS_NewObject(js)); if (buf->data && buf->size > 0) - JS_SetPropertyStr(js, b, "blob", js_new_blob_stoned_copy(js, buf->data, buf->size)); + JS_SetPropertyStr(js, b.val, "blob", js_new_blob_stoned_copy(js, buf->data, buf->size)); else - JS_SetPropertyStr(js, b, "blob", JS_NULL); - JS_SetPropertyStr(js, b, "byte_length", JS_NewInt64(js, buf->size)); - JS_SetPropertyNumber(js, buffers_arr, i, b); + JS_SetPropertyStr(js, b.val, "blob", JS_NULL); + JS_SetPropertyStr(js, b.val, "byte_length", JS_NewInt64(js, buf->size)); + JS_SetPropertyNumber(js, buffers_arr.val, i, b.val); } - JS_SetPropertyStr(js, obj, "buffers", buffers_arr); + JS_SetPropertyStr(js, obj.val, "buffers", buffers_arr.val); // Buffer views - JSValue views_arr = JS_NewArray(js); + JS_ROOT(views_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->buffer_views_count; i++) { cgltf_buffer_view *bv = &data->buffer_views[i]; - JSValue v = JS_NewObject(js); - JS_SetPropertyStr(js, v, "buffer", JS_NewInt32(js, (int)(bv->buffer - data->buffers))); - JS_SetPropertyStr(js, v, "byte_offset", JS_NewInt64(js, bv->offset)); - JS_SetPropertyStr(js, v, "byte_length", JS_NewInt64(js, bv->size)); - JS_SetPropertyStr(js, v, "byte_stride", bv->stride ? JS_NewInt32(js, bv->stride) : JS_NULL); + JS_ROOT(v, JS_NewObject(js)); + JS_SetPropertyStr(js, v.val, "buffer", JS_NewInt32(js, (int)(bv->buffer - data->buffers))); + JS_SetPropertyStr(js, v.val, "byte_offset", JS_NewInt64(js, bv->offset)); + JS_SetPropertyStr(js, v.val, "byte_length", JS_NewInt64(js, bv->size)); + JS_SetPropertyStr(js, v.val, "byte_stride", bv->stride ? JS_NewInt32(js, bv->stride) : JS_NULL); const char *usage = "unknown"; if (bv->type == cgltf_buffer_view_type_vertices) usage = "vertex"; else if (bv->type == cgltf_buffer_view_type_indices) usage = "index"; - JS_SetPropertyStr(js, v, "usage", JS_NewString(js, usage)); - JS_SetPropertyNumber(js, views_arr, i, v); + JS_SetPropertyStr(js, v.val, "usage", JS_NewString(js, usage)); + JS_SetPropertyNumber(js, views_arr.val, i, v.val); } - JS_SetPropertyStr(js, obj, "views", views_arr); + JS_SetPropertyStr(js, obj.val, "views", views_arr.val); // Accessors - JSValue accessors_arr = JS_NewArray(js); + JS_ROOT(accessors_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->accessors_count; i++) { cgltf_accessor *acc = &data->accessors[i]; - JSValue a = JS_NewObject(js); + JS_ROOT(a, JS_NewObject(js)); if (acc->buffer_view) - JS_SetPropertyStr(js, a, "view", JS_NewInt32(js, (int)(acc->buffer_view - data->buffer_views))); + JS_SetPropertyStr(js, a.val, "view", JS_NewInt32(js, (int)(acc->buffer_view - data->buffer_views))); else - JS_SetPropertyStr(js, a, "view", JS_NULL); - JS_SetPropertyStr(js, a, "byte_offset", JS_NewInt64(js, acc->offset)); - JS_SetPropertyStr(js, a, "count", JS_NewInt64(js, acc->count)); - JS_SetPropertyStr(js, a, "component_type", JS_NewString(js, component_type_str(acc->component_type))); - JS_SetPropertyStr(js, a, "type", JS_NewString(js, type_str(acc->type))); - JS_SetPropertyStr(js, a, "normalized", JS_NewBool(js, acc->normalized)); + JS_SetPropertyStr(js, a.val, "view", JS_NULL); + JS_SetPropertyStr(js, a.val, "byte_offset", JS_NewInt64(js, acc->offset)); + JS_SetPropertyStr(js, a.val, "count", JS_NewInt64(js, acc->count)); + JS_SetPropertyStr(js, a.val, "component_type", JS_NewString(js, component_type_str(acc->component_type))); + JS_SetPropertyStr(js, a.val, "type", JS_NewString(js, type_str(acc->type))); + JS_SetPropertyStr(js, a.val, "normalized", JS_NewBool(js, acc->normalized)); if (acc->has_min) { int n = cgltf_num_components(acc->type); - JS_SetPropertyStr(js, a, "min", make_float_array(js, acc->min, n)); + JS_SetPropertyStr(js, a.val, "min", make_float_array(js, acc->min, n)); } else { - JS_SetPropertyStr(js, a, "min", JS_NULL); + JS_SetPropertyStr(js, a.val, "min", JS_NULL); } if (acc->has_max) { int n = cgltf_num_components(acc->type); - JS_SetPropertyStr(js, a, "max", make_float_array(js, acc->max, n)); + JS_SetPropertyStr(js, a.val, "max", make_float_array(js, acc->max, n)); } else { - JS_SetPropertyStr(js, a, "max", JS_NULL); + JS_SetPropertyStr(js, a.val, "max", JS_NULL); } - JS_SetPropertyNumber(js, accessors_arr, i, a); + JS_SetPropertyNumber(js, accessors_arr.val, i, a.val); } - JS_SetPropertyStr(js, obj, "accessors", accessors_arr); + JS_SetPropertyStr(js, obj.val, "accessors", accessors_arr.val); // Meshes - JSValue meshes_arr = JS_NewArray(js); + JS_ROOT(meshes_arr, JS_NewArray(js)); for (cgltf_size mi = 0; mi < data->meshes_count; mi++) { cgltf_mesh *mesh = &data->meshes[mi]; - JSValue m = JS_NewObject(js); - JS_SetPropertyStr(js, m, "name", mesh->name ? JS_NewString(js, mesh->name) : JS_NULL); + JS_ROOT(m, JS_NewObject(js)); + JS_SetPropertyStr(js, m.val, "name", mesh->name ? JS_NewString(js, mesh->name) : JS_NULL); - JSValue prims_arr = JS_NewArray(js); + JS_ROOT(prims_arr, JS_NewArray(js)); for (cgltf_size pi = 0; pi < mesh->primitives_count; pi++) { cgltf_primitive *prim = &mesh->primitives[pi]; - JSValue p = JS_NewObject(js); - JS_SetPropertyStr(js, p, "topology", JS_NewString(js, topology_str(prim->type))); + JS_ROOT(p, JS_NewObject(js)); + JS_SetPropertyStr(js, p.val, "topology", JS_NewString(js, topology_str(prim->type))); - JSValue attrs = JS_NewObject(js); + JS_ROOT(attrs, JS_NewObject(js)); for (cgltf_size ai = 0; ai < prim->attributes_count; ai++) { cgltf_attribute *attr = &prim->attributes[ai]; const char *name = attribute_name(attr->type, attr->index); - JS_SetPropertyStr(js, attrs, name, JS_NewInt32(js, (int)(attr->data - data->accessors))); + JS_SetPropertyStr(js, attrs.val, name, JS_NewInt32(js, (int)(attr->data - data->accessors))); } - JS_SetPropertyStr(js, p, "attributes", attrs); + JS_SetPropertyStr(js, p.val, "attributes", attrs.val); if (prim->indices) - JS_SetPropertyStr(js, p, "indices", JS_NewInt32(js, (int)(prim->indices - data->accessors))); + JS_SetPropertyStr(js, p.val, "indices", JS_NewInt32(js, (int)(prim->indices - data->accessors))); else - JS_SetPropertyStr(js, p, "indices", JS_NULL); + JS_SetPropertyStr(js, p.val, "indices", JS_NULL); if (prim->material) - JS_SetPropertyStr(js, p, "material", JS_NewInt32(js, (int)(prim->material - data->materials))); + JS_SetPropertyStr(js, p.val, "material", JS_NewInt32(js, (int)(prim->material - data->materials))); else - JS_SetPropertyStr(js, p, "material", JS_NULL); + JS_SetPropertyStr(js, p.val, "material", JS_NULL); - JS_SetPropertyNumber(js, prims_arr, pi, p); + JS_SetPropertyNumber(js, prims_arr.val, pi, p.val); } - JS_SetPropertyStr(js, m, "primitives", prims_arr); - JS_SetPropertyNumber(js, meshes_arr, mi, m); + JS_SetPropertyStr(js, m.val, "primitives", prims_arr.val); + JS_SetPropertyNumber(js, meshes_arr.val, mi, m.val); } - JS_SetPropertyStr(js, obj, "meshes", meshes_arr); + JS_SetPropertyStr(js, obj.val, "meshes", meshes_arr.val); // Images - JSValue images_arr = JS_NewArray(js); + JS_ROOT(images_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->images_count; i++) { cgltf_image *img = &data->images[i]; - JSValue im = JS_NewObject(js); + JS_ROOT(im, JS_NewObject(js)); if (img->buffer_view) { - JS_SetPropertyStr(js, im, "kind", JS_NewString(js, "buffer_view")); - JS_SetPropertyStr(js, im, "view", JS_NewInt32(js, (int)(img->buffer_view - data->buffer_views))); + JS_SetPropertyStr(js, im.val, "kind", JS_NewString(js, "buffer_view")); + JS_SetPropertyStr(js, im.val, "view", JS_NewInt32(js, (int)(img->buffer_view - data->buffer_views))); } else if (img->uri) { - JS_SetPropertyStr(js, im, "kind", JS_NewString(js, "uri")); - JS_SetPropertyStr(js, im, "uri", JS_NewString(js, img->uri)); + JS_SetPropertyStr(js, im.val, "kind", JS_NewString(js, "uri")); + JS_SetPropertyStr(js, im.val, "uri", JS_NewString(js, img->uri)); } - JS_SetPropertyStr(js, im, "mime", img->mime_type ? JS_NewString(js, img->mime_type) : JS_NULL); - JS_SetPropertyNumber(js, images_arr, i, im); + JS_SetPropertyStr(js, im.val, "mime", img->mime_type ? JS_NewString(js, img->mime_type) : JS_NULL); + JS_SetPropertyNumber(js, images_arr.val, i, im.val); } - JS_SetPropertyStr(js, obj, "images", images_arr); + JS_SetPropertyStr(js, obj.val, "images", images_arr.val); // Textures - JSValue textures_arr = JS_NewArray(js); + JS_ROOT(textures_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->textures_count; i++) { cgltf_texture *tex = &data->textures[i]; - JSValue t = JS_NewObject(js); - JS_SetPropertyStr(js, t, "image", tex->image ? JS_NewInt32(js, (int)(tex->image - data->images)) : JS_NULL); - JS_SetPropertyStr(js, t, "sampler", tex->sampler ? JS_NewInt32(js, (int)(tex->sampler - data->samplers)) : JS_NULL); - JS_SetPropertyNumber(js, textures_arr, i, t); + JS_ROOT(t, JS_NewObject(js)); + JS_SetPropertyStr(js, t.val, "image", tex->image ? JS_NewInt32(js, (int)(tex->image - data->images)) : JS_NULL); + JS_SetPropertyStr(js, t.val, "sampler", tex->sampler ? JS_NewInt32(js, (int)(tex->sampler - data->samplers)) : JS_NULL); + JS_SetPropertyNumber(js, textures_arr.val, i, t.val); } - JS_SetPropertyStr(js, obj, "textures", textures_arr); + JS_SetPropertyStr(js, obj.val, "textures", textures_arr.val); // Samplers - JSValue samplers_arr = JS_NewArray(js); + JS_ROOT(samplers_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->samplers_count; i++) { cgltf_sampler *samp = &data->samplers[i]; - JSValue s = JS_NewObject(js); - JS_SetPropertyStr(js, s, "min_filter", JS_NewInt32(js, samp->min_filter)); - JS_SetPropertyStr(js, s, "mag_filter", JS_NewInt32(js, samp->mag_filter)); - JS_SetPropertyStr(js, s, "wrap_s", JS_NewInt32(js, samp->wrap_s)); - JS_SetPropertyStr(js, s, "wrap_t", JS_NewInt32(js, samp->wrap_t)); - JS_SetPropertyNumber(js, samplers_arr, i, s); + JS_ROOT(s, JS_NewObject(js)); + JS_SetPropertyStr(js, s.val, "min_filter", JS_NewInt32(js, samp->min_filter)); + JS_SetPropertyStr(js, s.val, "mag_filter", JS_NewInt32(js, samp->mag_filter)); + JS_SetPropertyStr(js, s.val, "wrap_s", JS_NewInt32(js, samp->wrap_s)); + JS_SetPropertyStr(js, s.val, "wrap_t", JS_NewInt32(js, samp->wrap_t)); + JS_SetPropertyNumber(js, samplers_arr.val, i, s.val); } - JS_SetPropertyStr(js, obj, "samplers", samplers_arr); + JS_SetPropertyStr(js, obj.val, "samplers", samplers_arr.val); // Materials - JSValue materials_arr = JS_NewArray(js); + JS_ROOT(materials_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->materials_count; i++) { cgltf_material *mat = &data->materials[i]; - JSValue m = JS_NewObject(js); - JS_SetPropertyStr(js, m, "name", mat->name ? JS_NewString(js, mat->name) : JS_NULL); + JS_ROOT(m, JS_NewObject(js)); + JS_SetPropertyStr(js, m.val, "name", mat->name ? JS_NewString(js, mat->name) : JS_NULL); - JSValue pbr = JS_NewObject(js); + JS_ROOT(pbr, JS_NewObject(js)); if (mat->has_pbr_metallic_roughness) { cgltf_pbr_metallic_roughness *pmr = &mat->pbr_metallic_roughness; - JS_SetPropertyStr(js, pbr, "base_color_factor", make_float_array(js, pmr->base_color_factor, 4)); - JS_SetPropertyStr(js, pbr, "base_color_texture", make_texture_info(js, &pmr->base_color_texture, data)); - JS_SetPropertyStr(js, pbr, "metallic_factor", JS_NewFloat64(js, pmr->metallic_factor)); - JS_SetPropertyStr(js, pbr, "roughness_factor", JS_NewFloat64(js, pmr->roughness_factor)); - JS_SetPropertyStr(js, pbr, "metallic_roughness_texture", make_texture_info(js, &pmr->metallic_roughness_texture, data)); + JS_SetPropertyStr(js, pbr.val, "base_color_factor", make_float_array(js, pmr->base_color_factor, 4)); + JS_SetPropertyStr(js, pbr.val, "base_color_texture", make_texture_info(js, &pmr->base_color_texture, data)); + JS_SetPropertyStr(js, pbr.val, "metallic_factor", JS_NewFloat64(js, pmr->metallic_factor)); + JS_SetPropertyStr(js, pbr.val, "roughness_factor", JS_NewFloat64(js, pmr->roughness_factor)); + JS_SetPropertyStr(js, pbr.val, "metallic_roughness_texture", make_texture_info(js, &pmr->metallic_roughness_texture, data)); } - JS_SetPropertyStr(js, pbr, "normal_texture", make_texture_info(js, &mat->normal_texture, data)); - JS_SetPropertyStr(js, pbr, "occlusion_texture", make_texture_info(js, &mat->occlusion_texture, data)); - JS_SetPropertyStr(js, pbr, "emissive_factor", make_float_array(js, mat->emissive_factor, 3)); - JS_SetPropertyStr(js, pbr, "emissive_texture", make_texture_info(js, &mat->emissive_texture, data)); - JS_SetPropertyStr(js, m, "pbr", pbr); + JS_SetPropertyStr(js, pbr.val, "normal_texture", make_texture_info(js, &mat->normal_texture, data)); + JS_SetPropertyStr(js, pbr.val, "occlusion_texture", make_texture_info(js, &mat->occlusion_texture, data)); + JS_SetPropertyStr(js, pbr.val, "emissive_factor", make_float_array(js, mat->emissive_factor, 3)); + JS_SetPropertyStr(js, pbr.val, "emissive_texture", make_texture_info(js, &mat->emissive_texture, data)); + JS_SetPropertyStr(js, m.val, "pbr", pbr.val); const char *alpha_mode = "OPAQUE"; if (mat->alpha_mode == cgltf_alpha_mode_mask) alpha_mode = "MASK"; else if (mat->alpha_mode == cgltf_alpha_mode_blend) alpha_mode = "BLEND"; - JS_SetPropertyStr(js, m, "alpha_mode", JS_NewString(js, alpha_mode)); - JS_SetPropertyStr(js, m, "alpha_cutoff", JS_NewFloat64(js, mat->alpha_cutoff)); - JS_SetPropertyStr(js, m, "double_sided", JS_NewBool(js, mat->double_sided)); - JS_SetPropertyStr(js, m, "unlit", JS_NewBool(js, mat->unlit)); + JS_SetPropertyStr(js, m.val, "alpha_mode", JS_NewString(js, alpha_mode)); + JS_SetPropertyStr(js, m.val, "alpha_cutoff", JS_NewFloat64(js, mat->alpha_cutoff)); + JS_SetPropertyStr(js, m.val, "double_sided", JS_NewBool(js, mat->double_sided)); + JS_SetPropertyStr(js, m.val, "unlit", JS_NewBool(js, mat->unlit)); - JS_SetPropertyNumber(js, materials_arr, i, m); + JS_SetPropertyNumber(js, materials_arr.val, i, m.val); } - JS_SetPropertyStr(js, obj, "materials", materials_arr); + JS_SetPropertyStr(js, obj.val, "materials", materials_arr.val); // Nodes - JSValue nodes_arr = JS_NewArray(js); + JS_ROOT(nodes_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->nodes_count; i++) { cgltf_node *node = &data->nodes[i]; - JSValue n = JS_NewObject(js); - JS_SetPropertyStr(js, n, "name", node->name ? JS_NewString(js, node->name) : JS_NULL); - JS_SetPropertyStr(js, n, "mesh", node->mesh ? JS_NewInt32(js, (int)(node->mesh - data->meshes)) : JS_NULL); + JS_ROOT(n, JS_NewObject(js)); + JS_SetPropertyStr(js, n.val, "name", node->name ? JS_NewString(js, node->name) : JS_NULL); + JS_SetPropertyStr(js, n.val, "mesh", node->mesh ? JS_NewInt32(js, (int)(node->mesh - data->meshes)) : JS_NULL); - JSValue children = JS_NewArray(js); + JS_ROOT(children, JS_NewArray(js)); for (cgltf_size ci = 0; ci < node->children_count; ci++) - JS_SetPropertyNumber(js, children, ci, JS_NewInt32(js, (int)(node->children[ci] - data->nodes))); - JS_SetPropertyStr(js, n, "children", children); + JS_SetPropertyNumber(js, children.val, ci, JS_NewInt32(js, (int)(node->children[ci] - data->nodes))); + JS_SetPropertyStr(js, n.val, "children", children.val); if (node->has_matrix) { - JS_SetPropertyStr(js, n, "matrix", make_float_array(js, node->matrix, 16)); - JS_SetPropertyStr(js, n, "translation", JS_NULL); - JS_SetPropertyStr(js, n, "rotation", JS_NULL); - JS_SetPropertyStr(js, n, "scale", JS_NULL); + JS_SetPropertyStr(js, n.val, "matrix", make_float_array(js, node->matrix, 16)); + JS_SetPropertyStr(js, n.val, "translation", JS_NULL); + JS_SetPropertyStr(js, n.val, "rotation", JS_NULL); + JS_SetPropertyStr(js, n.val, "scale", JS_NULL); } else { - JS_SetPropertyStr(js, n, "matrix", JS_NULL); - JS_SetPropertyStr(js, n, "translation", make_float_array(js, node->translation, 3)); - JS_SetPropertyStr(js, n, "rotation", make_float_array(js, node->rotation, 4)); - JS_SetPropertyStr(js, n, "scale", make_float_array(js, node->scale, 3)); + JS_SetPropertyStr(js, n.val, "matrix", JS_NULL); + JS_SetPropertyStr(js, n.val, "translation", make_float_array(js, node->translation, 3)); + JS_SetPropertyStr(js, n.val, "rotation", make_float_array(js, node->rotation, 4)); + JS_SetPropertyStr(js, n.val, "scale", make_float_array(js, node->scale, 3)); } - JS_SetPropertyStr(js, n, "skin", node->skin ? JS_NewInt32(js, (int)(node->skin - data->skins)) : JS_NULL); - JS_SetPropertyNumber(js, nodes_arr, i, n); + JS_SetPropertyStr(js, n.val, "skin", node->skin ? JS_NewInt32(js, (int)(node->skin - data->skins)) : JS_NULL); + JS_SetPropertyNumber(js, nodes_arr.val, i, n.val); } - JS_SetPropertyStr(js, obj, "nodes", nodes_arr); + JS_SetPropertyStr(js, obj.val, "nodes", nodes_arr.val); // Scenes - JSValue scenes_arr = JS_NewArray(js); + JS_ROOT(scenes_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->scenes_count; i++) { cgltf_scene *scene = &data->scenes[i]; - JSValue s = JS_NewObject(js); - JSValue snodes = JS_NewArray(js); + JS_ROOT(s, JS_NewObject(js)); + JS_ROOT(snodes, JS_NewArray(js)); for (cgltf_size ni = 0; ni < scene->nodes_count; ni++) - JS_SetPropertyNumber(js, snodes, ni, JS_NewInt32(js, (int)(scene->nodes[ni] - data->nodes))); - JS_SetPropertyStr(js, s, "nodes", snodes); - JS_SetPropertyNumber(js, scenes_arr, i, s); + JS_SetPropertyNumber(js, snodes.val, ni, JS_NewInt32(js, (int)(scene->nodes[ni] - data->nodes))); + JS_SetPropertyStr(js, s.val, "nodes", snodes.val); + JS_SetPropertyNumber(js, scenes_arr.val, i, s.val); } - JS_SetPropertyStr(js, obj, "scenes", scenes_arr); - JS_SetPropertyStr(js, obj, "scene", data->scene ? JS_NewInt32(js, (int)(data->scene - data->scenes)) : JS_NULL); + JS_SetPropertyStr(js, obj.val, "scenes", scenes_arr.val); + JS_SetPropertyStr(js, obj.val, "scene", data->scene ? JS_NewInt32(js, (int)(data->scene - data->scenes)) : JS_NULL); // Animations - JSValue anims_arr = JS_NewArray(js); + JS_ROOT(anims_arr, JS_NewArray(js)); for (cgltf_size ai = 0; ai < data->animations_count; ai++) { cgltf_animation *anim = &data->animations[ai]; - JSValue a = JS_NewObject(js); - JS_SetPropertyStr(js, a, "name", anim->name ? JS_NewString(js, anim->name) : JS_NULL); + JS_ROOT(a, JS_NewObject(js)); + JS_SetPropertyStr(js, a.val, "name", anim->name ? JS_NewString(js, anim->name) : JS_NULL); - JSValue samplers = JS_NewArray(js); + JS_ROOT(samps, JS_NewArray(js)); for (cgltf_size si = 0; si < anim->samplers_count; si++) { cgltf_animation_sampler *samp = &anim->samplers[si]; - JSValue s = JS_NewObject(js); - JS_SetPropertyStr(js, s, "input", samp->input ? JS_NewInt32(js, (int)(samp->input - data->accessors)) : JS_NULL); - JS_SetPropertyStr(js, s, "output", samp->output ? JS_NewInt32(js, (int)(samp->output - data->accessors)) : JS_NULL); + JS_ROOT(s, JS_NewObject(js)); + JS_SetPropertyStr(js, s.val, "input", samp->input ? JS_NewInt32(js, (int)(samp->input - data->accessors)) : JS_NULL); + JS_SetPropertyStr(js, s.val, "output", samp->output ? JS_NewInt32(js, (int)(samp->output - data->accessors)) : JS_NULL); const char *interp = "LINEAR"; if (samp->interpolation == cgltf_interpolation_type_step) interp = "STEP"; else if (samp->interpolation == cgltf_interpolation_type_cubic_spline) interp = "CUBICSPLINE"; - JS_SetPropertyStr(js, s, "interpolation", JS_NewString(js, interp)); - JS_SetPropertyNumber(js, samplers, si, s); + JS_SetPropertyStr(js, s.val, "interpolation", JS_NewString(js, interp)); + JS_SetPropertyNumber(js, samps.val, si, s.val); } - JS_SetPropertyStr(js, a, "samplers", samplers); + JS_SetPropertyStr(js, a.val, "samplers", samps.val); - JSValue channels = JS_NewArray(js); + JS_ROOT(channels, JS_NewArray(js)); for (cgltf_size ci = 0; ci < anim->channels_count; ci++) { cgltf_animation_channel *chan = &anim->channels[ci]; - JSValue c = JS_NewObject(js); + JS_ROOT(c, JS_NewObject(js)); // Find sampler index within this animation int samp_idx = -1; for (cgltf_size si = 0; si < anim->samplers_count; si++) { if (&anim->samplers[si] == chan->sampler) { samp_idx = (int)si; break; } } - JS_SetPropertyStr(js, c, "sampler", JS_NewInt32(js, samp_idx)); - JSValue target = JS_NewObject(js); - JS_SetPropertyStr(js, target, "node", chan->target_node ? JS_NewInt32(js, (int)(chan->target_node - data->nodes)) : JS_NULL); + JS_SetPropertyStr(js, c.val, "sampler", JS_NewInt32(js, samp_idx)); + JS_ROOT(target, JS_NewObject(js)); + JS_SetPropertyStr(js, target.val, "node", chan->target_node ? JS_NewInt32(js, (int)(chan->target_node - data->nodes)) : JS_NULL); const char *path = "unknown"; switch (chan->target_path) { case cgltf_animation_path_type_translation: path = "translation"; break; @@ -373,46 +376,46 @@ JSValue js_gltf_decode(JSContext *js, JSValue this_val, int argc, JSValueConst * case cgltf_animation_path_type_weights: path = "weights"; break; default: break; } - JS_SetPropertyStr(js, target, "path", JS_NewString(js, path)); - JS_SetPropertyStr(js, c, "target", target); - JS_SetPropertyNumber(js, channels, ci, c); + JS_SetPropertyStr(js, target.val, "path", JS_NewString(js, path)); + JS_SetPropertyStr(js, c.val, "target", target.val); + JS_SetPropertyNumber(js, channels.val, ci, c.val); } - JS_SetPropertyStr(js, a, "channels", channels); + JS_SetPropertyStr(js, a.val, "channels", channels.val); - JS_SetPropertyNumber(js, anims_arr, ai, a); + JS_SetPropertyNumber(js, anims_arr.val, ai, a.val); } - JS_SetPropertyStr(js, obj, "animations", anims_arr); + JS_SetPropertyStr(js, obj.val, "animations", anims_arr.val); // Skins - JSValue skins_arr = JS_NewArray(js); + JS_ROOT(skins_arr, JS_NewArray(js)); for (cgltf_size i = 0; i < data->skins_count; i++) { cgltf_skin *skin = &data->skins[i]; - JSValue s = JS_NewObject(js); - JS_SetPropertyStr(js, s, "name", skin->name ? JS_NewString(js, skin->name) : JS_NULL); - JSValue joints = JS_NewArray(js); + JS_ROOT(s, JS_NewObject(js)); + JS_SetPropertyStr(js, s.val, "name", skin->name ? JS_NewString(js, skin->name) : JS_NULL); + JS_ROOT(joints, JS_NewArray(js)); for (cgltf_size ji = 0; ji < skin->joints_count; ji++) - JS_SetPropertyNumber(js, joints, ji, JS_NewInt32(js, (int)(skin->joints[ji] - data->nodes))); - JS_SetPropertyStr(js, s, "joints", joints); - JS_SetPropertyStr(js, s, "inverse_bind_matrices", skin->inverse_bind_matrices ? JS_NewInt32(js, (int)(skin->inverse_bind_matrices - data->accessors)) : JS_NULL); - JS_SetPropertyStr(js, s, "skeleton", skin->skeleton ? JS_NewInt32(js, (int)(skin->skeleton - data->nodes)) : JS_NULL); - JS_SetPropertyNumber(js, skins_arr, i, s); + JS_SetPropertyNumber(js, joints.val, ji, JS_NewInt32(js, (int)(skin->joints[ji] - data->nodes))); + JS_SetPropertyStr(js, s.val, "joints", joints.val); + JS_SetPropertyStr(js, s.val, "inverse_bind_matrices", skin->inverse_bind_matrices ? JS_NewInt32(js, (int)(skin->inverse_bind_matrices - data->accessors)) : JS_NULL); + JS_SetPropertyStr(js, s.val, "skeleton", skin->skeleton ? JS_NewInt32(js, (int)(skin->skeleton - data->nodes)) : JS_NULL); + JS_SetPropertyNumber(js, skins_arr.val, i, s.val); } - JS_SetPropertyStr(js, obj, "skins", skins_arr); + JS_SetPropertyStr(js, obj.val, "skins", skins_arr.val); // Extensions - JSValue exts = JS_NewObject(js); - JSValue used = JS_NewArray(js); + JS_ROOT(exts, JS_NewObject(js)); + JS_ROOT(used, JS_NewArray(js)); for (cgltf_size i = 0; i < data->extensions_used_count; i++) - JS_SetPropertyNumber(js, used, i, JS_NewString(js, data->extensions_used[i])); - JS_SetPropertyStr(js, exts, "used", used); - JSValue required = JS_NewArray(js); + JS_SetPropertyNumber(js, used.val, i, JS_NewString(js, data->extensions_used[i])); + JS_SetPropertyStr(js, exts.val, "used", used.val); + JS_ROOT(required, JS_NewArray(js)); for (cgltf_size i = 0; i < data->extensions_required_count; i++) - JS_SetPropertyNumber(js, required, i, JS_NewString(js, data->extensions_required[i])); - JS_SetPropertyStr(js, exts, "required", required); - JS_SetPropertyStr(js, obj, "extensions", exts); + JS_SetPropertyNumber(js, required.val, i, JS_NewString(js, data->extensions_required[i])); + JS_SetPropertyStr(js, exts.val, "required", required.val); + JS_SetPropertyStr(js, obj.val, "extensions", exts.val); cgltf_free(data); - return obj; + JS_RETURN(obj.val); } static const JSCFunctionListEntry js_gltf_funcs[] = { diff --git a/obj.c b/obj.c index fbd201b..3d0fd6b 100644 --- a/obj.c +++ b/obj.c @@ -28,10 +28,11 @@ static void obj_file_reader(void *ctx, const char *filename, int is_mtl, const c static JSValue make_float_array(JSContext *js, const float *arr, int count) { - JSValue a = JS_NewArray(js); + JS_FRAME(js); + JS_ROOT(a, JS_NewArray(js)); for (int i = 0; i < count; i++) - JS_SetPropertyNumber(js, a, i, JS_NewFloat64(js, arr[i])); - return a; + JS_SetPropertyNumber(js, a.val, i, JS_NewFloat64(js, arr[i])); + JS_RETURN(a.val); } JSValue js_obj_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) @@ -55,7 +56,8 @@ JSValue js_obj_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a if (result != TINYOBJ_SUCCESS) return JS_ThrowReferenceError(js, "failed to parse OBJ file"); - JSValue obj = JS_NewObject(js); + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); // Build unified buffer with all vertex data for all shapes // First pass: count total vertices across all shapes @@ -90,7 +92,7 @@ JSValue js_obj_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a // Allocate and fill buffer uint8_t *buffer_data = malloc(total_buffer_size); float *positions = (float *)buffer_data; - float *normals = global_has_normals ? (float *)(buffer_data + pos_size) : NULL; + float *normals_buf = global_has_normals ? (float *)(buffer_data + pos_size) : NULL; float *uvs = global_has_uvs ? (float *)(buffer_data + pos_size + norm_size) : NULL; void *indices = buffer_data + pos_size + norm_size + uv_size; @@ -119,16 +121,16 @@ JSValue js_obj_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a } // Normal - if (normals) { + if (normals_buf) { int vn_idx = idx.vn_idx; if (vn_idx >= 0 && vn_idx < (int)(attrib.num_normals)) { - normals[v * 3 + 0] = attrib.normals[vn_idx * 3 + 0]; - normals[v * 3 + 1] = attrib.normals[vn_idx * 3 + 1]; - normals[v * 3 + 2] = attrib.normals[vn_idx * 3 + 2]; + normals_buf[v * 3 + 0] = attrib.normals[vn_idx * 3 + 0]; + normals_buf[v * 3 + 1] = attrib.normals[vn_idx * 3 + 1]; + normals_buf[v * 3 + 2] = attrib.normals[vn_idx * 3 + 2]; } else { - normals[v * 3 + 0] = 0; - normals[v * 3 + 1] = 1; - normals[v * 3 + 2] = 0; + normals_buf[v * 3 + 0] = 0; + normals_buf[v * 3 + 1] = 1; + normals_buf[v * 3 + 2] = 0; } } @@ -156,135 +158,75 @@ JSValue js_obj_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a } // Create buffer - JSValue buffers_arr = JS_NewArray(js); - JSValue buf = JS_NewObject(js); - JS_SetPropertyStr(js, buf, "blob", js_new_blob_stoned_copy(js, buffer_data, total_buffer_size)); - JS_SetPropertyStr(js, buf, "byte_length", JS_NewInt64(js, total_buffer_size)); - JS_SetPropertyNumber(js, buffers_arr, 0, buf); - JS_SetPropertyStr(js, obj, "buffers", buffers_arr); + JS_ROOT(buffers_arr, JS_NewArray(js)); + { + JS_ROOT(buf, JS_NewObject(js)); + JS_SetPropertyStr(js, buf.val, "blob", js_new_blob_stoned_copy(js, buffer_data, total_buffer_size)); + JS_SetPropertyStr(js, buf.val, "byte_length", JS_NewInt64(js, total_buffer_size)); + JS_SetPropertyNumber(js, buffers_arr.val, 0, buf.val); + } + JS_SetPropertyStr(js, obj.val, "buffers", buffers_arr.val); // Create views - JSValue views_arr = JS_NewArray(js); + JS_ROOT(views_arr, JS_NewArray(js)); int view_idx = 0; // Position view - JSValue pos_view = JS_NewObject(js); - JS_SetPropertyStr(js, pos_view, "buffer", JS_NewInt32(js, 0)); - JS_SetPropertyStr(js, pos_view, "byte_offset", JS_NewInt64(js, 0)); - JS_SetPropertyStr(js, pos_view, "byte_length", JS_NewInt64(js, pos_size)); - JS_SetPropertyStr(js, pos_view, "byte_stride", JS_NULL); - JS_SetPropertyStr(js, pos_view, "usage", JS_NewString(js, "vertex")); - JS_SetPropertyNumber(js, views_arr, view_idx++, pos_view); + { + JS_ROOT(pos_view, JS_NewObject(js)); + JS_SetPropertyStr(js, pos_view.val, "buffer", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, pos_view.val, "byte_offset", JS_NewInt64(js, 0)); + JS_SetPropertyStr(js, pos_view.val, "byte_length", JS_NewInt64(js, pos_size)); + JS_SetPropertyStr(js, pos_view.val, "byte_stride", JS_NULL); + JS_SetPropertyStr(js, pos_view.val, "usage", JS_NewString(js, "vertex")); + JS_SetPropertyNumber(js, views_arr.val, view_idx++, pos_view.val); + } int pos_view_idx = 0; int norm_view_idx = -1; if (global_has_normals) { - JSValue norm_view = JS_NewObject(js); - JS_SetPropertyStr(js, norm_view, "buffer", JS_NewInt32(js, 0)); - JS_SetPropertyStr(js, norm_view, "byte_offset", JS_NewInt64(js, pos_size)); - JS_SetPropertyStr(js, norm_view, "byte_length", JS_NewInt64(js, norm_size)); - JS_SetPropertyStr(js, norm_view, "byte_stride", JS_NULL); - JS_SetPropertyStr(js, norm_view, "usage", JS_NewString(js, "vertex")); + JS_ROOT(norm_view, JS_NewObject(js)); + JS_SetPropertyStr(js, norm_view.val, "buffer", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, norm_view.val, "byte_offset", JS_NewInt64(js, pos_size)); + JS_SetPropertyStr(js, norm_view.val, "byte_length", JS_NewInt64(js, norm_size)); + JS_SetPropertyStr(js, norm_view.val, "byte_stride", JS_NULL); + JS_SetPropertyStr(js, norm_view.val, "usage", JS_NewString(js, "vertex")); norm_view_idx = view_idx; - JS_SetPropertyNumber(js, views_arr, view_idx++, norm_view); + JS_SetPropertyNumber(js, views_arr.val, view_idx++, norm_view.val); } int uv_view_idx = -1; if (global_has_uvs) { - JSValue uv_view = JS_NewObject(js); - JS_SetPropertyStr(js, uv_view, "buffer", JS_NewInt32(js, 0)); - JS_SetPropertyStr(js, uv_view, "byte_offset", JS_NewInt64(js, pos_size + norm_size)); - JS_SetPropertyStr(js, uv_view, "byte_length", JS_NewInt64(js, uv_size)); - JS_SetPropertyStr(js, uv_view, "byte_stride", JS_NULL); - JS_SetPropertyStr(js, uv_view, "usage", JS_NewString(js, "vertex")); + JS_ROOT(uv_view, JS_NewObject(js)); + JS_SetPropertyStr(js, uv_view.val, "buffer", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, uv_view.val, "byte_offset", JS_NewInt64(js, pos_size + norm_size)); + JS_SetPropertyStr(js, uv_view.val, "byte_length", JS_NewInt64(js, uv_size)); + JS_SetPropertyStr(js, uv_view.val, "byte_stride", JS_NULL); + JS_SetPropertyStr(js, uv_view.val, "usage", JS_NewString(js, "vertex")); uv_view_idx = view_idx; - JS_SetPropertyNumber(js, views_arr, view_idx++, uv_view); + JS_SetPropertyNumber(js, views_arr.val, view_idx++, uv_view.val); } // Index view - JSValue idx_view = JS_NewObject(js); - JS_SetPropertyStr(js, idx_view, "buffer", JS_NewInt32(js, 0)); - JS_SetPropertyStr(js, idx_view, "byte_offset", JS_NewInt64(js, pos_size + norm_size + uv_size)); - JS_SetPropertyStr(js, idx_view, "byte_length", JS_NewInt64(js, idx_size)); - JS_SetPropertyStr(js, idx_view, "byte_stride", JS_NULL); - JS_SetPropertyStr(js, idx_view, "usage", JS_NewString(js, "index")); - int idx_view_idx = view_idx; - JS_SetPropertyNumber(js, views_arr, view_idx++, idx_view); + int idx_view_idx; + { + JS_ROOT(idx_view, JS_NewObject(js)); + JS_SetPropertyStr(js, idx_view.val, "buffer", JS_NewInt32(js, 0)); + JS_SetPropertyStr(js, idx_view.val, "byte_offset", JS_NewInt64(js, pos_size + norm_size + uv_size)); + JS_SetPropertyStr(js, idx_view.val, "byte_length", JS_NewInt64(js, idx_size)); + JS_SetPropertyStr(js, idx_view.val, "byte_stride", JS_NULL); + JS_SetPropertyStr(js, idx_view.val, "usage", JS_NewString(js, "index")); + idx_view_idx = view_idx; + JS_SetPropertyNumber(js, views_arr.val, view_idx++, idx_view.val); + } - JS_SetPropertyStr(js, obj, "views", views_arr); + JS_SetPropertyStr(js, obj.val, "views", views_arr.val); - // Create accessors - JSValue accessors_arr = JS_NewArray(js); + // Build per-shape accessors and meshes + JS_ROOT(accessors_arr, JS_NewArray(js)); int acc_idx = 0; - // Position accessor - JSValue pos_acc = JS_NewObject(js); - JS_SetPropertyStr(js, pos_acc, "view", JS_NewInt32(js, pos_view_idx)); - JS_SetPropertyStr(js, pos_acc, "byte_offset", JS_NewInt64(js, 0)); - JS_SetPropertyStr(js, pos_acc, "count", JS_NewInt64(js, total_vertices)); - JS_SetPropertyStr(js, pos_acc, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, pos_acc, "type", JS_NewString(js, "vec3")); - JS_SetPropertyStr(js, pos_acc, "normalized", JS_FALSE); - JS_SetPropertyStr(js, pos_acc, "min", JS_NULL); - JS_SetPropertyStr(js, pos_acc, "max", JS_NULL); - int pos_acc_idx = acc_idx; - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, pos_acc); - - int norm_acc_idx = -1; - if (global_has_normals) { - JSValue norm_acc = JS_NewObject(js); - JS_SetPropertyStr(js, norm_acc, "view", JS_NewInt32(js, norm_view_idx)); - JS_SetPropertyStr(js, norm_acc, "byte_offset", JS_NewInt64(js, 0)); - JS_SetPropertyStr(js, norm_acc, "count", JS_NewInt64(js, total_vertices)); - JS_SetPropertyStr(js, norm_acc, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, norm_acc, "type", JS_NewString(js, "vec3")); - JS_SetPropertyStr(js, norm_acc, "normalized", JS_FALSE); - JS_SetPropertyStr(js, norm_acc, "min", JS_NULL); - JS_SetPropertyStr(js, norm_acc, "max", JS_NULL); - norm_acc_idx = acc_idx; - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, norm_acc); - } - - int uv_acc_idx = -1; - if (global_has_uvs) { - JSValue uv_acc = JS_NewObject(js); - JS_SetPropertyStr(js, uv_acc, "view", JS_NewInt32(js, uv_view_idx)); - JS_SetPropertyStr(js, uv_acc, "byte_offset", JS_NewInt64(js, 0)); - JS_SetPropertyStr(js, uv_acc, "count", JS_NewInt64(js, total_vertices)); - JS_SetPropertyStr(js, uv_acc, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, uv_acc, "type", JS_NewString(js, "vec2")); - JS_SetPropertyStr(js, uv_acc, "normalized", JS_FALSE); - JS_SetPropertyStr(js, uv_acc, "min", JS_NULL); - JS_SetPropertyStr(js, uv_acc, "max", JS_NULL); - uv_acc_idx = acc_idx; - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, uv_acc); - } - - // Index accessor - JSValue idx_acc = JS_NewObject(js); - JS_SetPropertyStr(js, idx_acc, "view", JS_NewInt32(js, idx_view_idx)); - JS_SetPropertyStr(js, idx_acc, "byte_offset", JS_NewInt64(js, 0)); - JS_SetPropertyStr(js, idx_acc, "count", JS_NewInt64(js, total_vertices)); - JS_SetPropertyStr(js, idx_acc, "component_type", JS_NewString(js, use_32bit ? "u32" : "u16")); - JS_SetPropertyStr(js, idx_acc, "type", JS_NewString(js, "scalar")); - JS_SetPropertyStr(js, idx_acc, "normalized", JS_FALSE); - JS_SetPropertyStr(js, idx_acc, "min", JS_NULL); - JS_SetPropertyStr(js, idx_acc, "max", JS_NULL); - int idx_acc_idx = acc_idx; - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, idx_acc); - - JS_SetPropertyStr(js, obj, "accessors", accessors_arr); - - // Create meshes - each shape becomes a mesh with one primitive - // But we need per-shape accessors since they reference different ranges - // Actually, for OBJ we'll create per-shape accessors that point to subranges - - // Rebuild accessors per shape - JS_FreeValue(js, accessors_arr); - accessors_arr = JS_NewArray(js); - acc_idx = 0; - - JSValue meshes_arr = JS_NewArray(js); + JS_ROOT(meshes_arr, JS_NewArray(js)); vertex_offset = 0; for (size_t si = 0; si < num_shapes; si++) { @@ -293,167 +235,175 @@ JSValue js_obj_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a // Create accessors for this shape int shape_pos_acc = acc_idx; - JSValue spa = JS_NewObject(js); - JS_SetPropertyStr(js, spa, "view", JS_NewInt32(js, pos_view_idx)); - JS_SetPropertyStr(js, spa, "byte_offset", JS_NewInt64(js, vertex_offset * 3 * sizeof(float))); - JS_SetPropertyStr(js, spa, "count", JS_NewInt64(js, shape_vertices)); - JS_SetPropertyStr(js, spa, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, spa, "type", JS_NewString(js, "vec3")); - JS_SetPropertyStr(js, spa, "normalized", JS_FALSE); - JS_SetPropertyStr(js, spa, "min", JS_NULL); - JS_SetPropertyStr(js, spa, "max", JS_NULL); - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, spa); + { + JS_ROOT(spa, JS_NewObject(js)); + JS_SetPropertyStr(js, spa.val, "view", JS_NewInt32(js, pos_view_idx)); + JS_SetPropertyStr(js, spa.val, "byte_offset", JS_NewInt64(js, vertex_offset * 3 * sizeof(float))); + JS_SetPropertyStr(js, spa.val, "count", JS_NewInt64(js, shape_vertices)); + JS_SetPropertyStr(js, spa.val, "component_type", JS_NewString(js, "f32")); + JS_SetPropertyStr(js, spa.val, "type", JS_NewString(js, "vec3")); + JS_SetPropertyStr(js, spa.val, "normalized", JS_FALSE); + JS_SetPropertyStr(js, spa.val, "min", JS_NULL); + JS_SetPropertyStr(js, spa.val, "max", JS_NULL); + JS_SetPropertyNumber(js, accessors_arr.val, acc_idx++, spa.val); + } int shape_norm_acc = -1; if (global_has_normals) { shape_norm_acc = acc_idx; - JSValue sna = JS_NewObject(js); - JS_SetPropertyStr(js, sna, "view", JS_NewInt32(js, norm_view_idx)); - JS_SetPropertyStr(js, sna, "byte_offset", JS_NewInt64(js, vertex_offset * 3 * sizeof(float))); - JS_SetPropertyStr(js, sna, "count", JS_NewInt64(js, shape_vertices)); - JS_SetPropertyStr(js, sna, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, sna, "type", JS_NewString(js, "vec3")); - JS_SetPropertyStr(js, sna, "normalized", JS_FALSE); - JS_SetPropertyStr(js, sna, "min", JS_NULL); - JS_SetPropertyStr(js, sna, "max", JS_NULL); - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, sna); + JS_ROOT(sna, JS_NewObject(js)); + JS_SetPropertyStr(js, sna.val, "view", JS_NewInt32(js, norm_view_idx)); + JS_SetPropertyStr(js, sna.val, "byte_offset", JS_NewInt64(js, vertex_offset * 3 * sizeof(float))); + JS_SetPropertyStr(js, sna.val, "count", JS_NewInt64(js, shape_vertices)); + JS_SetPropertyStr(js, sna.val, "component_type", JS_NewString(js, "f32")); + JS_SetPropertyStr(js, sna.val, "type", JS_NewString(js, "vec3")); + JS_SetPropertyStr(js, sna.val, "normalized", JS_FALSE); + JS_SetPropertyStr(js, sna.val, "min", JS_NULL); + JS_SetPropertyStr(js, sna.val, "max", JS_NULL); + JS_SetPropertyNumber(js, accessors_arr.val, acc_idx++, sna.val); } int shape_uv_acc = -1; if (global_has_uvs) { shape_uv_acc = acc_idx; - JSValue sua = JS_NewObject(js); - JS_SetPropertyStr(js, sua, "view", JS_NewInt32(js, uv_view_idx)); - JS_SetPropertyStr(js, sua, "byte_offset", JS_NewInt64(js, vertex_offset * 2 * sizeof(float))); - JS_SetPropertyStr(js, sua, "count", JS_NewInt64(js, shape_vertices)); - JS_SetPropertyStr(js, sua, "component_type", JS_NewString(js, "f32")); - JS_SetPropertyStr(js, sua, "type", JS_NewString(js, "vec2")); - JS_SetPropertyStr(js, sua, "normalized", JS_FALSE); - JS_SetPropertyStr(js, sua, "min", JS_NULL); - JS_SetPropertyStr(js, sua, "max", JS_NULL); - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, sua); + JS_ROOT(sua, JS_NewObject(js)); + JS_SetPropertyStr(js, sua.val, "view", JS_NewInt32(js, uv_view_idx)); + JS_SetPropertyStr(js, sua.val, "byte_offset", JS_NewInt64(js, vertex_offset * 2 * sizeof(float))); + JS_SetPropertyStr(js, sua.val, "count", JS_NewInt64(js, shape_vertices)); + JS_SetPropertyStr(js, sua.val, "component_type", JS_NewString(js, "f32")); + JS_SetPropertyStr(js, sua.val, "type", JS_NewString(js, "vec2")); + JS_SetPropertyStr(js, sua.val, "normalized", JS_FALSE); + JS_SetPropertyStr(js, sua.val, "min", JS_NULL); + JS_SetPropertyStr(js, sua.val, "max", JS_NULL); + JS_SetPropertyNumber(js, accessors_arr.val, acc_idx++, sua.val); } int shape_idx_acc = acc_idx; - JSValue sia = JS_NewObject(js); - JS_SetPropertyStr(js, sia, "view", JS_NewInt32(js, idx_view_idx)); - JS_SetPropertyStr(js, sia, "byte_offset", JS_NewInt64(js, vertex_offset * (use_32bit ? sizeof(uint32_t) : sizeof(uint16_t)))); - JS_SetPropertyStr(js, sia, "count", JS_NewInt64(js, shape_vertices)); - JS_SetPropertyStr(js, sia, "component_type", JS_NewString(js, use_32bit ? "u32" : "u16")); - JS_SetPropertyStr(js, sia, "type", JS_NewString(js, "scalar")); - JS_SetPropertyStr(js, sia, "normalized", JS_FALSE); - JS_SetPropertyStr(js, sia, "min", JS_NULL); - JS_SetPropertyStr(js, sia, "max", JS_NULL); - JS_SetPropertyNumber(js, accessors_arr, acc_idx++, sia); + { + JS_ROOT(sia, JS_NewObject(js)); + JS_SetPropertyStr(js, sia.val, "view", JS_NewInt32(js, idx_view_idx)); + JS_SetPropertyStr(js, sia.val, "byte_offset", JS_NewInt64(js, vertex_offset * (use_32bit ? sizeof(uint32_t) : sizeof(uint16_t)))); + JS_SetPropertyStr(js, sia.val, "count", JS_NewInt64(js, shape_vertices)); + JS_SetPropertyStr(js, sia.val, "component_type", JS_NewString(js, use_32bit ? "u32" : "u16")); + JS_SetPropertyStr(js, sia.val, "type", JS_NewString(js, "scalar")); + JS_SetPropertyStr(js, sia.val, "normalized", JS_FALSE); + JS_SetPropertyStr(js, sia.val, "min", JS_NULL); + JS_SetPropertyStr(js, sia.val, "max", JS_NULL); + JS_SetPropertyNumber(js, accessors_arr.val, acc_idx++, sia.val); + } // Create mesh - JSValue mesh = JS_NewObject(js); - JS_SetPropertyStr(js, mesh, "name", shape->name ? JS_NewString(js, shape->name) : JS_NULL); + JS_ROOT(mesh, JS_NewObject(js)); + JS_SetPropertyStr(js, mesh.val, "name", shape->name ? JS_NewString(js, shape->name) : JS_NULL); - JSValue prims_arr = JS_NewArray(js); - JSValue prim = JS_NewObject(js); - JS_SetPropertyStr(js, prim, "topology", JS_NewString(js, "triangles")); + JS_ROOT(prims_arr, JS_NewArray(js)); + { + JS_ROOT(prim, JS_NewObject(js)); + JS_SetPropertyStr(js, prim.val, "topology", JS_NewString(js, "triangles")); - JSValue attrs = JS_NewObject(js); - JS_SetPropertyStr(js, attrs, "POSITION", JS_NewInt32(js, shape_pos_acc)); - if (shape_norm_acc >= 0) - JS_SetPropertyStr(js, attrs, "NORMAL", JS_NewInt32(js, shape_norm_acc)); - if (shape_uv_acc >= 0) - JS_SetPropertyStr(js, attrs, "TEXCOORD_0", JS_NewInt32(js, shape_uv_acc)); - JS_SetPropertyStr(js, prim, "attributes", attrs); + JS_ROOT(attrs, JS_NewObject(js)); + JS_SetPropertyStr(js, attrs.val, "POSITION", JS_NewInt32(js, shape_pos_acc)); + if (shape_norm_acc >= 0) + JS_SetPropertyStr(js, attrs.val, "NORMAL", JS_NewInt32(js, shape_norm_acc)); + if (shape_uv_acc >= 0) + JS_SetPropertyStr(js, attrs.val, "TEXCOORD_0", JS_NewInt32(js, shape_uv_acc)); + JS_SetPropertyStr(js, prim.val, "attributes", attrs.val); - JS_SetPropertyStr(js, prim, "indices", JS_NewInt32(js, shape_idx_acc)); - JS_SetPropertyStr(js, prim, "material", JS_NULL); + JS_SetPropertyStr(js, prim.val, "indices", JS_NewInt32(js, shape_idx_acc)); + JS_SetPropertyStr(js, prim.val, "material", JS_NULL); - JS_SetPropertyNumber(js, prims_arr, 0, prim); - JS_SetPropertyStr(js, mesh, "primitives", prims_arr); + JS_SetPropertyNumber(js, prims_arr.val, 0, prim.val); + } + JS_SetPropertyStr(js, mesh.val, "primitives", prims_arr.val); - JS_SetPropertyNumber(js, meshes_arr, si, mesh); + JS_SetPropertyNumber(js, meshes_arr.val, si, mesh.val); vertex_offset += shape_vertices; } - JS_SetPropertyStr(js, obj, "accessors", accessors_arr); - JS_SetPropertyStr(js, obj, "meshes", meshes_arr); + JS_SetPropertyStr(js, obj.val, "accessors", accessors_arr.val); + JS_SetPropertyStr(js, obj.val, "meshes", meshes_arr.val); // Materials from OBJ - JSValue materials_arr = JS_NewArray(js); + JS_ROOT(materials_arr, JS_NewArray(js)); for (size_t i = 0; i < num_materials; i++) { tinyobj_material_t *mat = &materials[i]; - JSValue m = JS_NewObject(js); - JS_SetPropertyStr(js, m, "name", mat->name ? JS_NewString(js, mat->name) : JS_NULL); + JS_ROOT(m, JS_NewObject(js)); + JS_SetPropertyStr(js, m.val, "name", mat->name ? JS_NewString(js, mat->name) : JS_NULL); - JSValue pbr = JS_NewObject(js); + JS_ROOT(pbr, JS_NewObject(js)); float bc[4] = {mat->diffuse[0], mat->diffuse[1], mat->diffuse[2], 1.0f}; - JS_SetPropertyStr(js, pbr, "base_color_factor", make_float_array(js, bc, 4)); - JS_SetPropertyStr(js, pbr, "base_color_texture", JS_NULL); - JS_SetPropertyStr(js, pbr, "metallic_factor", JS_NewFloat64(js, 0.0)); - JS_SetPropertyStr(js, pbr, "roughness_factor", JS_NewFloat64(js, 1.0)); - JS_SetPropertyStr(js, pbr, "metallic_roughness_texture", JS_NULL); - JS_SetPropertyStr(js, pbr, "normal_texture", JS_NULL); - JS_SetPropertyStr(js, pbr, "occlusion_texture", JS_NULL); + JS_SetPropertyStr(js, pbr.val, "base_color_factor", make_float_array(js, bc, 4)); + JS_SetPropertyStr(js, pbr.val, "base_color_texture", JS_NULL); + JS_SetPropertyStr(js, pbr.val, "metallic_factor", JS_NewFloat64(js, 0.0)); + JS_SetPropertyStr(js, pbr.val, "roughness_factor", JS_NewFloat64(js, 1.0)); + JS_SetPropertyStr(js, pbr.val, "metallic_roughness_texture", JS_NULL); + JS_SetPropertyStr(js, pbr.val, "normal_texture", JS_NULL); + JS_SetPropertyStr(js, pbr.val, "occlusion_texture", JS_NULL); float ef[3] = {mat->emission[0], mat->emission[1], mat->emission[2]}; - JS_SetPropertyStr(js, pbr, "emissive_factor", make_float_array(js, ef, 3)); - JS_SetPropertyStr(js, pbr, "emissive_texture", JS_NULL); - JS_SetPropertyStr(js, m, "pbr", pbr); + JS_SetPropertyStr(js, pbr.val, "emissive_factor", make_float_array(js, ef, 3)); + JS_SetPropertyStr(js, pbr.val, "emissive_texture", JS_NULL); + JS_SetPropertyStr(js, m.val, "pbr", pbr.val); - JS_SetPropertyStr(js, m, "alpha_mode", JS_NewString(js, "OPAQUE")); - JS_SetPropertyStr(js, m, "alpha_cutoff", JS_NewFloat64(js, 0.5)); - JS_SetPropertyStr(js, m, "double_sided", JS_FALSE); + JS_SetPropertyStr(js, m.val, "alpha_mode", JS_NewString(js, "OPAQUE")); + JS_SetPropertyStr(js, m.val, "alpha_cutoff", JS_NewFloat64(js, 0.5)); + JS_SetPropertyStr(js, m.val, "double_sided", JS_FALSE); - JS_SetPropertyNumber(js, materials_arr, i, m); + JS_SetPropertyNumber(js, materials_arr.val, i, m.val); } - JS_SetPropertyStr(js, obj, "materials", materials_arr); + JS_SetPropertyStr(js, obj.val, "materials", materials_arr.val); // Empty arrays for unsupported features - JS_SetPropertyStr(js, obj, "images", JS_NewArray(js)); - JS_SetPropertyStr(js, obj, "textures", JS_NewArray(js)); - JS_SetPropertyStr(js, obj, "samplers", JS_NewArray(js)); + JS_SetPropertyStr(js, obj.val, "images", JS_NewArray(js)); + JS_SetPropertyStr(js, obj.val, "textures", JS_NewArray(js)); + JS_SetPropertyStr(js, obj.val, "samplers", JS_NewArray(js)); // Nodes - one node per mesh - JSValue nodes_arr = JS_NewArray(js); - JSValue scene_nodes = JS_NewArray(js); + JS_ROOT(nodes_arr, JS_NewArray(js)); + JS_ROOT(scene_nodes, JS_NewArray(js)); for (size_t i = 0; i < num_shapes; i++) { - JSValue n = JS_NewObject(js); - JS_SetPropertyStr(js, n, "name", shapes[i].name ? JS_NewString(js, shapes[i].name) : JS_NULL); - JS_SetPropertyStr(js, n, "mesh", JS_NewInt32(js, i)); - JS_SetPropertyStr(js, n, "children", JS_NewArray(js)); - JS_SetPropertyStr(js, n, "matrix", JS_NULL); + JS_ROOT(n, JS_NewObject(js)); + JS_SetPropertyStr(js, n.val, "name", shapes[i].name ? JS_NewString(js, shapes[i].name) : JS_NULL); + JS_SetPropertyStr(js, n.val, "mesh", JS_NewInt32(js, i)); + JS_SetPropertyStr(js, n.val, "children", JS_NewArray(js)); + JS_SetPropertyStr(js, n.val, "matrix", JS_NULL); float t[3] = {0, 0, 0}; float r[4] = {0, 0, 0, 1}; float s[3] = {1, 1, 1}; - JS_SetPropertyStr(js, n, "translation", make_float_array(js, t, 3)); - JS_SetPropertyStr(js, n, "rotation", make_float_array(js, r, 4)); - JS_SetPropertyStr(js, n, "scale", make_float_array(js, s, 3)); - JS_SetPropertyStr(js, n, "skin", JS_NULL); - JS_SetPropertyNumber(js, nodes_arr, i, n); - JS_SetPropertyNumber(js, scene_nodes, i, JS_NewInt32(js, i)); + JS_SetPropertyStr(js, n.val, "translation", make_float_array(js, t, 3)); + JS_SetPropertyStr(js, n.val, "rotation", make_float_array(js, r, 4)); + JS_SetPropertyStr(js, n.val, "scale", make_float_array(js, s, 3)); + JS_SetPropertyStr(js, n.val, "skin", JS_NULL); + JS_SetPropertyNumber(js, nodes_arr.val, i, n.val); + JS_SetPropertyNumber(js, scene_nodes.val, i, JS_NewInt32(js, i)); } - JS_SetPropertyStr(js, obj, "nodes", nodes_arr); + JS_SetPropertyStr(js, obj.val, "nodes", nodes_arr.val); // Scenes - JSValue scenes_arr = JS_NewArray(js); - JSValue scene = JS_NewObject(js); - JS_SetPropertyStr(js, scene, "nodes", scene_nodes); - JS_SetPropertyNumber(js, scenes_arr, 0, scene); - JS_SetPropertyStr(js, obj, "scenes", scenes_arr); - JS_SetPropertyStr(js, obj, "scene", JS_NewInt32(js, 0)); + JS_ROOT(scenes_arr, JS_NewArray(js)); + { + JS_ROOT(sc, JS_NewObject(js)); + JS_SetPropertyStr(js, sc.val, "nodes", scene_nodes.val); + JS_SetPropertyNumber(js, scenes_arr.val, 0, sc.val); + } + JS_SetPropertyStr(js, obj.val, "scenes", scenes_arr.val); + JS_SetPropertyStr(js, obj.val, "scene", JS_NewInt32(js, 0)); // Empty arrays for animations/skins - JS_SetPropertyStr(js, obj, "animations", JS_NewArray(js)); - JS_SetPropertyStr(js, obj, "skins", JS_NewArray(js)); + JS_SetPropertyStr(js, obj.val, "animations", JS_NewArray(js)); + JS_SetPropertyStr(js, obj.val, "skins", JS_NewArray(js)); // Extensions - JSValue exts = JS_NewObject(js); - JS_SetPropertyStr(js, exts, "used", JS_NewArray(js)); - JS_SetPropertyStr(js, exts, "required", JS_NewArray(js)); - JS_SetPropertyStr(js, obj, "extensions", exts); + JS_ROOT(exts, JS_NewObject(js)); + JS_SetPropertyStr(js, exts.val, "used", JS_NewArray(js)); + JS_SetPropertyStr(js, exts.val, "required", JS_NewArray(js)); + JS_SetPropertyStr(js, obj.val, "extensions", exts.val); free(buffer_data); tinyobj_attrib_free(&attrib); tinyobj_shapes_free(shapes, num_shapes); tinyobj_materials_free(materials, num_materials); - return obj; + JS_RETURN(obj.val); } static const JSCFunctionListEntry js_obj_funcs[] = {