aboutsummaryrefslogtreecommitdiff
path: root/mons_gltf/src
diff options
context:
space:
mode:
authorLibravatar Silas Bartha <silas@exvacuum.dev>2025-02-07 11:27:18 -0500
committerLibravatar Silas Bartha <silas@exvacuum.dev>2025-02-07 11:27:18 -0500
commit4da7be39827ea5888ef9c97b1aadf61b0d76347c (patch)
tree15d0ff8f8bcb0e871efb1b2e65c2bc8d07b17565 /mons_gltf/src
initial commit (lol)
Diffstat (limited to 'mons_gltf/src')
-rw-r--r--mons_gltf/src/gltf.c549
1 files changed, 549 insertions, 0 deletions
diff --git a/mons_gltf/src/gltf.c b/mons_gltf/src/gltf.c
new file mode 100644
index 0000000..10f177c
--- /dev/null
+++ b/mons_gltf/src/gltf.c
@@ -0,0 +1,549 @@
+#include "gltf.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string.h>
+#include "json.h"
+#include "transform.h"
+#include "mons_math/vec3.h"
+#include "mons_math/quat.h"
+#include "vertex.h"
+#include "mesh.h"
+#include "texture.h"
+#include "image.h"
+#include "qoi.h"
+#include "model.h"
+#include "shader.h"
+
+// TODO: Morph Targets
+// TODO: Skins
+
+typedef enum mons_gltf_buffer_view_target {
+ MONS_GLTF_BUFFER_VIEW_ARRAY_BUFFER = 34962,
+ MONS_GLTF_BUFFER_VIEW_ELEMENT_ARRAY_BUFFER = 34963,
+} mons_gltf_buffer_view_target;
+
+typedef struct mons_gltf_buffer_view {
+ int buffer_index;
+ int byte_length;
+ int byte_offset;
+ int byte_stride;
+ mons_gltf_buffer_view_target target;
+} mons_gltf_buffer_view;
+
+typedef enum mons_gltf_accessor_component_type {
+ MONS_GLTF_ACCESSOR_BYTE = 5120,
+ MONS_GLTF_ACCESSOR_UNSIGNED_BYTE = 5121,
+ MONS_GLTF_ACCESSOR_SHORT = 5122,
+ MONS_GLTF_ACCESSOR_UNSIGNED_SHORT = 5123,
+ MONS_GLTF_ACCESSOR_UNSIGNED_INT = 5125,
+ MONS_GLTF_ACCESSOR_FLOAT = 5126,
+} mons_gltf_accessor_component_type;
+
+typedef enum mons_gltf_accessor_type {
+ MONS_GLTF_ACCESSOR_SCALAR = 1,
+ MONS_GLTF_ACCESSOR_VEC2 = 2,
+ MONS_GLTF_ACCESSOR_VEC3 = 3,
+ MONS_GLTF_ACCESSOR_VEC4 = 4,
+ MONS_GLTF_ACCESSOR_MAT2 = 4,
+ MONS_GLTF_ACCESSOR_MAT3 = 9,
+ MONS_GLTF_ACCESSOR_MAT4 = 16,
+} mons_gltf_accessor_type;
+
+typedef struct mons_gltf_accessor {
+ int buffer_view_index;
+ int byte_offset;
+ mons_gltf_accessor_component_type component_type;
+ int count;
+ float *min;
+ float *max;
+ mons_gltf_accessor_type type;
+} mons_gltf_accessor;
+
+typedef enum mons_gltf_primitive_mode {
+ MONS_GLTF_MODE_POINTS,
+ MONS_GLTF_MODE_LINES,
+ MONS_GLTF_MODE_LINE_LOOP,
+ MONS_GLTF_MODE_LINE_STRIP,
+ MONS_GLTF_MODE_TRIANGLES,
+ MONS_GLTF_MODE_TRIANGLE_STRIP,
+ MONS_GLTF_MODE_TRIANGLE_FAN,
+} mons_gltf_primitive_mode;
+
+typedef struct mons_gltf_primitive {
+ mons_hashmap attributes;
+ int indices_accessor;
+ int material;
+ mons_gltf_primitive_mode mode;
+} mons_gltf_primitive;
+
+typedef struct mons_gltf_mesh {
+ mons_gltf_primitive *primitives;
+} mons_gltf_mesh;
+
+typedef struct mons_gltf_texture {
+ int sampler_index;
+ int source_index;
+} mons_gltf_texture;
+
+typedef enum mons_gltf_sampler_filter {
+ MONS_SAMPLER_NEAREST = 9728,
+ MONS_SAMPLER_LINEAR = 9729,
+ MONS_SAMPLER_NEAREST_MIPMAP_NEAREST = 9984,
+ MONS_SAMPLER_LINEAR_MIPMAP_NEAREST = 9985,
+ MONS_SAMPLER_NEAREST_MIPMAP_LINEAR = 9986,
+ MONS_SAMPLER_LINEAR_MIPMAP_LINEAR = 9987,
+} mons_gltf_sampler_filter;
+
+typedef enum mons_gltf_sampler_wrapmode {
+ MONS_SAMPLER_CLAMP_TO_EDGE = 33071,
+ MONS_SAMPLER_MIRRORED_REPEAT = 33648,
+ MONS_SAMPLER_REPEAT = 10497,
+} mons_gltf_sampler_wrapmode;
+
+typedef struct mons_gltf_sampler {
+ mons_gltf_sampler_filter mag_filter;
+ mons_gltf_sampler_filter min_filter;
+ mons_gltf_sampler_wrapmode wrap_s;
+ mons_gltf_sampler_wrapmode wrap_t;
+} mons_gltf_sampler;
+
+typedef struct mons_gltf_material {
+ int base_color_texture_index;
+ int normal_texture_index;
+ int metallic_roughness_texture_index;
+} mons_gltf_material;
+
+int mons_load_gltf(char *path, mons_program shader, mons_model **out, int *count) {
+
+ FILE *stream = fopen(path, "r");
+ // Read JSON into memory
+ fseek(stream, 0L, SEEK_END);
+ unsigned long len = ftell(stream);
+ char *json_buffer = malloc(len);
+ rewind(stream);
+ fread(json_buffer, 1, len, stream);
+ fclose(stream);
+ mons_json_value json;
+ if (mons_json_parse(json_buffer, &json) == EXIT_FAILURE) {
+ printf("Failed to parse JSON\n");
+ return EXIT_FAILURE;
+ }
+
+ mons_json_value asset_info;
+ mons_json_get_value(json, "asset", &asset_info);
+ char *asset_info_str = mons_json_to_string(asset_info);
+ printf("Loading GLTF Asset: %s\n", asset_info_str);
+ free(asset_info_str);
+
+ mons_json_array gltf_nodes_array;
+ mons_json_get_array(json, "nodes", &gltf_nodes_array);
+ printf("GLTF Node Count: %d\n", gltf_nodes_array.len);
+ for (int i = 0; i < gltf_nodes_array.len; i++) {
+ mons_json_value node_info = gltf_nodes_array.values[i];
+ char *name;
+ mons_json_get_string(node_info, "name", &name);
+ printf("\t%d: %s\n", i, name);
+ mons_transform transform = MONS_TRANSFORM_IDENTITY;
+ mons_json_array node_transform_matrix_json;
+ if (mons_json_get_array(node_info, "matrix",
+ &node_transform_matrix_json) == EXIT_SUCCESS) {
+ for (int j = 0; j < node_transform_matrix_json.len; j++) {
+ *(((float *)&transform) + j) =
+ node_transform_matrix_json.values[j].data.number;
+ }
+ } else {
+
+ mons_vec3 translation = MONS_VEC3_ZERO;
+ mons_vec3 scale = MONS_VEC3_ONE;
+ mons_quat rotation = MONS_QUAT_IDENTITY;
+ mons_json_array node_translation_json;
+ mons_json_array node_rotation_json;
+ mons_json_array node_scale_json;
+ int has_translation =
+ (mons_json_get_array(node_info, "translation",
+ &node_translation_json) == EXIT_SUCCESS);
+ int has_rotation =
+ (mons_json_get_array(node_info, "rotation",
+ &node_rotation_json) == EXIT_SUCCESS);
+ int has_scale =
+ (mons_json_get_array(node_info, "scale",
+ &node_translation_json) == EXIT_SUCCESS);
+
+ if (has_translation) {
+ for (int j = 0; j < node_translation_json.len; j++) {
+ *(((float *)&translation) + j) =
+ node_translation_json.values[j].data.number;
+ }
+ printf("TRANSLATION: %f, %f, %f\n", translation.x,
+ translation.y, translation.z);
+ }
+ if (has_rotation) {
+ printf("ROTATION\n");
+ for (int j = 0; j < node_rotation_json.len; j++) {
+ *(((float *)&rotation) + j) =
+ node_rotation_json.values[j].data.number;
+ }
+ }
+ if (has_scale) {
+ printf("SCALE\n");
+ for (int j = 0; j < node_scale_json.len; j++) {
+ *(((float *)&scale) + j) =
+ node_scale_json.values[j].data.number;
+ }
+ }
+ if (has_scale || has_rotation || has_translation) {
+ transform = (mons_transform) {
+ translation,
+ scale,
+ rotation,
+ };
+ }
+ }
+ }
+
+ mons_json_array gltf_scenes_array;
+ mons_json_get_array(json, "scenes", &gltf_scenes_array);
+ printf("GLTF Scene Count: %d\n", gltf_scenes_array.len);
+ for (int i = 0; i < gltf_scenes_array.len; i++) {
+ mons_json_value scene_info = gltf_scenes_array.values[i];
+ char *name;
+ mons_json_get_string(scene_info, "name", &name);
+ printf("\t%d: %s\n", i, name);
+ mons_json_array scene_nodes_array;
+ mons_json_get_array(scene_info, "nodes", &scene_nodes_array);
+ printf("\t%d Nodes:\n", scene_nodes_array.len);
+ for (int j = 0; j < scene_nodes_array.len; j++) {
+ int node_index = scene_nodes_array.values[j].data.number;
+ printf("\t\t%d\n", node_index);
+ }
+ }
+
+ mons_json_array gltf_buffers_array;
+ mons_json_get_array(json, "buffers", &gltf_buffers_array);
+ printf("GLTF Buffer Count: %d\n", gltf_buffers_array.len);
+ FILE **buffers = calloc(gltf_buffers_array.len, sizeof(FILE *));
+ for (int i = 0; i < gltf_buffers_array.len; i++) {
+ mons_json_value buffer_info = gltf_buffers_array.values[i];
+ char *buffer_uri;
+ mons_json_get_string(buffer_info, "uri", &buffer_uri);
+ char *path_copy = strdup(path);
+ char *base_path = dirname(path_copy);
+ char *full_buffer_path =
+ calloc(strlen(base_path) + strlen(buffer_uri) + 2, 1);
+ snprintf(full_buffer_path, strlen(base_path) + strlen(buffer_uri) + 2,
+ "%s/%s", base_path, buffer_uri);
+ printf("\t%d: %s\n", i, full_buffer_path);
+ buffers[i] = fopen(full_buffer_path, "rb");
+ if (buffers[i] == NULL) {
+ printf("Failed to open buffer for reading\n");
+ }
+ free(path_copy);
+ }
+
+ mons_json_array gltf_buffer_views_array;
+ mons_json_get_array(json, "bufferViews", &gltf_buffer_views_array);
+ printf("GLTF Buffer View Count: %d\n", gltf_buffer_views_array.len);
+ mons_gltf_buffer_view *buffer_views =
+ calloc(gltf_buffer_views_array.len, sizeof(mons_gltf_buffer_view));
+ for (int i = 0; i < gltf_buffer_views_array.len; i++) {
+ mons_json_value buffer_view_info = gltf_buffer_views_array.values[i];
+ mons_json_get_int(buffer_view_info, "buffer",
+ &buffer_views[i].buffer_index);
+ mons_json_get_int(buffer_view_info, "byteLength",
+ &buffer_views[i].byte_length);
+ mons_json_get_int(buffer_view_info, "byteOffset",
+ &buffer_views[i].byte_offset);
+ mons_json_get_int(buffer_view_info, "byteStride",
+ &buffer_views[i].byte_stride);
+ mons_json_get_int(buffer_view_info, "target",
+ (int *)(&buffer_views[i].target));
+ printf("%d: %d bytes @ %d+%d, type %d, stride %d\n", i,
+ buffer_views[i].byte_length, buffer_views[i].buffer_index,
+ buffer_views[i].byte_offset, buffer_views[i].target,
+ buffer_views[i].byte_stride);
+ }
+
+ mons_json_array gltf_accessors_array;
+ mons_json_get_array(json, "accessors", &gltf_accessors_array);
+ printf("GLTF Accessor Count: %d\n", gltf_accessors_array.len);
+ mons_gltf_accessor *accessors =
+ calloc(gltf_accessors_array.len, sizeof(mons_gltf_accessor));
+ for (int i = 0; i < gltf_accessors_array.len; i++) {
+ mons_json_value accessor_info = gltf_accessors_array.values[i];
+ mons_json_get_int(accessor_info, "bufferView",
+ &accessors[i].buffer_view_index);
+ mons_json_get_int(accessor_info, "byteOffset",
+ &accessors[i].byte_offset);
+ mons_json_get_int(accessor_info, "componentType",
+ (int *)(&accessors[i].component_type));
+ mons_json_get_int(accessor_info, "count", &accessors[i].count);
+ char *accessor_type;
+ mons_json_get_string(accessor_info, "type", &accessor_type);
+ if (strcmp(accessor_type, "SCALAR") == 0) {
+ accessors[i].type = MONS_GLTF_ACCESSOR_SCALAR;
+ } else if (strcmp(accessor_type, "VEC2") == 0) {
+ accessors[i].type = MONS_GLTF_ACCESSOR_VEC2;
+ } else if (strcmp(accessor_type, "VEC3") == 0) {
+ accessors[i].type = MONS_GLTF_ACCESSOR_VEC3;
+ } else if (strcmp(accessor_type, "VEC4") == 0) {
+ accessors[i].type = MONS_GLTF_ACCESSOR_VEC4;
+ } else if (strcmp(accessor_type, "MAT2") == 0) {
+ accessors[i].type = MONS_GLTF_ACCESSOR_MAT2;
+ } else if (strcmp(accessor_type, "MAT3") == 0) {
+ accessors[i].type = MONS_GLTF_ACCESSOR_MAT3;
+ } else if (strcmp(accessor_type, "MAT4") == 0) {
+ accessors[i].type = MONS_GLTF_ACCESSOR_MAT4;
+ }
+ mons_json_array min_array;
+ if (mons_json_get_array(accessor_info, "min", &min_array) ==
+ EXIT_SUCCESS) {
+ accessors[i].min = calloc(accessors[i].type, sizeof(float));
+ for (int j = 0; j < accessors[i].type; j++) {
+ accessors[i].min[j] = min_array.values[j].data.number;
+ }
+ }
+ mons_json_array max_array;
+ if (mons_json_get_array(accessor_info, "max", &max_array) ==
+ EXIT_SUCCESS) {
+ accessors[i].max = calloc(accessors[i].type, sizeof(float));
+ for (int j = 0; j < accessors[i].type; j++) {
+ accessors[i].max[j] = max_array.values[j].data.number;
+ }
+ }
+ printf("%d: %d %d-component elements @ %d+%d\n", i, accessors[i].count,
+ accessors[i].type, accessors[i].buffer_view_index,
+ accessors[i].byte_offset);
+ if (accessors[i].min != NULL) {
+ printf("\tMin: [");
+ for (int j = 0; j < accessors[i].type; j++) {
+ printf("%f", accessors[i].min[j]);
+ if (j + 1 < accessors[i].type) {
+ printf(", ");
+ } else {
+ printf("]\n");
+ }
+ }
+ }
+ if (accessors[i].max != NULL) {
+ printf("\tMax: [");
+ for (int j = 0; j < accessors[i].type; j++) {
+ printf("%f", accessors[i].max[j]);
+ if (j + 1 < accessors[i].type) {
+ printf(", ");
+ } else {
+ printf("]\n");
+ }
+ }
+ }
+ }
+
+ mons_json_array gltf_images_array;
+ mons_json_get_array(json, "images", &gltf_images_array);
+ printf("GLTF Image Count: %d\n", gltf_images_array.len);
+ mons_image *images = calloc(gltf_images_array.len, sizeof(mons_image));
+ for (int i = 0; i < gltf_images_array.len; i++) {
+ mons_json_value image_info = gltf_images_array.values[i];
+ char *mimetype = NULL;
+ mons_json_get_string(image_info, "mimeType", &mimetype);
+ int buffer_view_index;
+ FILE *image_buffer = NULL;
+ if (mons_json_get_int(image_info, "bufferView", &buffer_view_index) ==
+ EXIT_SUCCESS) {
+ if (mimetype == NULL) {
+ printf(
+ "Cannot load image from buffer view, missing mimetype\n");
+ continue;
+ }
+ mons_gltf_buffer_view image_buffer_view =
+ buffer_views[buffer_view_index];
+ image_buffer = buffers[image_buffer_view.buffer_index];
+ }
+ char *uri = NULL;
+ if(mons_json_get_string(image_info, "uri", &uri) == EXIT_SUCCESS) {
+ char *path_copy = strdup(path);
+ char *base_path = dirname(path_copy);
+ char *full_buffer_path =
+ calloc(strlen(base_path) + strlen(uri) + 2, 1);
+ snprintf(full_buffer_path, strlen(base_path) + strlen(uri) + 2,
+ "%s/%s", base_path, uri);
+ printf("%d: %s\n", i, full_buffer_path);
+ image_buffer = fopen(full_buffer_path, "rb");
+ }
+
+ if(image_buffer == NULL) {
+ printf("Failed to open image buffer for reading\n");
+ continue;
+ }
+ if (strcmp(mimetype, "image/qoi") == 0 ||
+ (uri != NULL && strcmp(strrchr(uri, '.'), "qoi") == 0)) {
+ images[i] = mons_load_qoi(image_buffer);
+ }
+ }
+
+ mons_json_array gltf_samplers_array;
+ mons_json_get_array(json, "samplers", &gltf_samplers_array);
+ printf("GLTF Sampler Count: %d\n", gltf_samplers_array.len);
+ mons_gltf_sampler *samplers = calloc(gltf_samplers_array.len, sizeof(mons_gltf_sampler));
+ for(int i = 0; i < gltf_samplers_array.len; i++) {
+ mons_json_value sampler_info = gltf_samplers_array.values[i];
+ mons_json_get_int(sampler_info, "magFilter", (int*)&samplers[i].mag_filter);
+ mons_json_get_int(sampler_info, "minFilter", (int*)&samplers[i].min_filter);
+ mons_json_get_int(sampler_info, "wrapS", (int*)&samplers[i].wrap_s);
+ mons_json_get_int(sampler_info, "wrapT", (int*)&samplers[i].wrap_t);
+ printf("%d: min %d, mag %d, wrap %d, %d\n", i, samplers[i].min_filter, samplers[i].mag_filter, samplers[i].wrap_s, samplers[i].wrap_t);
+ }
+
+ mons_json_array gltf_textures_array;
+ mons_json_get_array(json, "textures", &gltf_textures_array);
+ printf("GLTF Texture Count: %d\n", gltf_textures_array.len);
+ mons_gltf_texture *textures =
+ calloc(gltf_textures_array.len, sizeof(mons_gltf_texture));
+ for (int i = 0; i < gltf_textures_array.len; i++) {
+ mons_json_value texture_info = gltf_textures_array.values[i];
+ textures[i].sampler_index = -1;
+ textures[i].source_index = -1;
+ mons_json_get_int(texture_info, "sampler", &textures[i].sampler_index);
+ mons_json_get_int(texture_info, "source", &textures[i].source_index);
+ printf("%d: sampler %d, source %d\n", i, textures[i].sampler_index,
+ textures[i].source_index);
+ }
+
+ mons_json_array gltf_materials_array;
+ mons_json_get_array(json, "materials", &gltf_materials_array);
+ mons_gltf_material *materials = calloc(gltf_materials_array.len, sizeof(mons_gltf_material));
+ printf("GLTF Material Count: %d\n", gltf_materials_array.len);
+ for (int i = 0; i < gltf_materials_array.len; i++) {
+ mons_json_value material_info = gltf_materials_array.values[i];
+ mons_json_get_int(material_info, "pbrMetallicRoughness.baseColorTexture.index", &materials[i].base_color_texture_index);
+ mons_json_get_int(material_info, "normalTexture.index", &materials[i].normal_texture_index);
+ mons_json_get_int(material_info, "pbrMetallicRoughness.metallicRoughnessTexture.index", &materials[i].metallic_roughness_texture_index);
+ }
+
+ mons_json_array gltf_mesh_array;
+ mons_json_get_array(json, "meshes", &gltf_mesh_array);
+ printf("GLTF Mesh Count: %d\n", gltf_mesh_array.len);
+ mons_model *models = calloc(gltf_mesh_array.len, sizeof(mons_model));
+ for (int i = 0; i < gltf_mesh_array.len; i++) {
+ mons_json_value mesh_info = gltf_mesh_array.values[i];
+ mons_json_array primitives_array;
+ mons_json_get_array(mesh_info, "primitives", &primitives_array);
+ printf("%d: %d Primitives\n", i, primitives_array.len);
+ for (int j = 0; j < primitives_array.len; j++) {
+ mons_json_value primitive_info = primitives_array.values[j];
+ mons_vertex *vertices;
+ int vertex_count;
+ int position_accessor_index;
+ if (mons_json_get_int(primitive_info, "attributes.POSITION",
+ &position_accessor_index) == EXIT_SUCCESS) {
+ mons_gltf_accessor position_accessor =
+ accessors[position_accessor_index];
+ vertices = calloc(position_accessor.count, sizeof(mons_vertex));
+ mons_gltf_buffer_view position_buffer_view =
+ buffer_views[position_accessor.buffer_view_index];
+ FILE *position_buffer =
+ buffers[position_buffer_view.buffer_index];
+ mons_vec3 *positions =
+ calloc(position_accessor.count, sizeof(mons_vec3));
+ fseek(position_buffer, position_buffer_view.byte_offset,
+ SEEK_SET);
+ fread(positions, sizeof(mons_vec3), position_accessor.count,
+ position_buffer);
+ for (int k = 0; k < position_accessor.count; k++) {
+ vertices[k].position = positions[k];
+ }
+ vertex_count = position_accessor.count;
+ }
+ int texcoord_accessor_index;
+ if (mons_json_get_int(primitive_info, "attributes.TEXCOORD_0",
+ &texcoord_accessor_index) == EXIT_SUCCESS) {
+ mons_gltf_accessor texcoord_accessor =
+ accessors[texcoord_accessor_index];
+ mons_gltf_buffer_view texcoord_buffer_view =
+ buffer_views[texcoord_accessor.buffer_view_index];
+ FILE *texcoord_buffer =
+ buffers[texcoord_buffer_view.buffer_index];
+ mons_vec2 *texcoords =
+ calloc(texcoord_accessor.count, sizeof(mons_vec2));
+ fseek(texcoord_buffer, texcoord_buffer_view.byte_offset,
+ SEEK_SET);
+ fread(texcoords, sizeof(mons_vec2), texcoord_accessor.count,
+ texcoord_buffer);
+ for (int k = 0; k < texcoord_accessor.count; k++) {
+ vertices[k].texture_coords = texcoords[k];
+ }
+ }
+ int index_accessor_index;
+ int *indices;
+ int index_count;
+ if (mons_json_get_int(primitive_info, "indices",
+ &index_accessor_index) == EXIT_SUCCESS) {
+ mons_gltf_accessor index_accessor =
+ accessors[index_accessor_index];
+ mons_gltf_buffer_view index_buffer_view =
+ buffer_views[index_accessor.buffer_view_index];
+ FILE *index_buffer = buffers[index_buffer_view.buffer_index];
+ indices = calloc(index_accessor.count, sizeof(int));
+ unsigned short *index_shorts =
+ calloc(index_accessor.count, sizeof(unsigned short));
+ fseek(index_buffer, index_buffer_view.byte_offset, SEEK_SET);
+ fread(index_shorts, sizeof(unsigned short),
+ index_accessor.count, index_buffer);
+ for (int k = 0; k < index_accessor.count; k++) {
+ indices[k] = index_shorts[k];
+ }
+ index_count = index_accessor.count;
+ }
+ int normal_accessor_index;
+ if (mons_json_get_int(primitive_info, "attributes.NORMAL",
+ &normal_accessor_index) == EXIT_SUCCESS) {
+ mons_gltf_accessor normal_accessor =
+ accessors[normal_accessor_index];
+ mons_gltf_buffer_view normal_buffer_view =
+ buffer_views[normal_accessor.buffer_view_index];
+ FILE *normal_buffer =
+ buffers[normal_buffer_view.buffer_index];
+ mons_vec3 *normals =
+ calloc(normal_accessor.count, sizeof(mons_vec3));
+ fseek(normal_buffer, normal_buffer_view.byte_offset,
+ SEEK_SET);
+ fread(normals, sizeof(mons_vec3), normal_accessor.count,
+ normal_buffer);
+ for (int k = 0; k < normal_accessor.count; k++) {
+ vertices[k].normal = normals[k];
+ }
+ }
+
+ int material_index;
+ mons_json_get_int(primitive_info, "material", &material_index);
+
+ mons_gltf_material material = materials[material_index];
+
+ mons_texture *textures = calloc(3, sizeof(mons_texture));
+ textures[0] = mons_texture_from_image(images[material.base_color_texture_index]);
+ textures[1] = mons_texture_from_image(images[material.normal_texture_index]);
+ textures[2] = mons_texture_from_image(images[material.metallic_roughness_texture_index]);
+
+ models[i] = (mons_model) {
+ .mesh = mons_create_mesh(vertices, vertex_count, indices, index_count),
+ .textures = textures,
+ .textures_len = 3,
+ .shader = shader,
+ .transform = MONS_TRANSFORM_IDENTITY,
+ };
+ }
+ }
+
+ *out = models;
+ *count = gltf_mesh_array.len;
+
+ for (int i = 0; i < gltf_accessors_array.len; i++) {
+ free(accessors[i].min);
+ free(accessors[i].max);
+ }
+ free(accessors);
+ free(buffer_views);
+ free(buffers);
+ mons_json_free(json);
+ return EXIT_SUCCESS;
+}