summaryrefslogtreecommitdiff
path: root/convmodel.c
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2024-12-28 18:27:30 +1100
committerquou <quou@disroot.org>2024-12-28 18:27:30 +1100
commite31535abfff4c3335dd465e3745a69d3102253a6 (patch)
tree48ff456b34ea295e3ea45c54cb0c97820f0290ec /convmodel.c
parent86810460eed75c19beb9f524e75c193edfa9ddfd (diff)
converting, loading and rendering GLTF models
Diffstat (limited to 'convmodel.c')
-rw-r--r--convmodel.c418
1 files changed, 418 insertions, 0 deletions
diff --git a/convmodel.c b/convmodel.c
new file mode 100644
index 0000000..5e57fc9
--- /dev/null
+++ b/convmodel.c
@@ -0,0 +1,418 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "memory.h"
+#include "plat.h"
+#include "sc/sh_enums.h"
+#include "sc/sh_helpers.h"
+#include "str.h"
+
+#define CGLTF_IMPLEMENTATION
+#include "cgltf.h"
+
+typedef struct {
+ const char* shader;
+} Node_Config;
+
+typedef struct Shader_Attrib {
+ cgltf_attribute_type target;
+ SVariable_Type type;
+ int offset;
+ int size;
+ struct Shader_Attrib* next;
+} Shader_Attrib;
+
+Arena arena;
+const char* shader_dir;
+const char* shader_dir_sep;
+
+char* vertex_buffer;
+int vertex_buffer_size;
+uint16_t* index_buffer;
+int index_buffer_size;
+int vertex_count, index_count;
+int vertex_size;
+int full_vert_size = 0;
+int full_ind_size = 0;
+float* current_vertex;
+const cgltf_accessor* accessors[cgltf_attribute_type_max_enum];
+Shader_Attrib* target_attribs[cgltf_attribute_type_max_enum];
+
+int tcmp(const char* src, const char* s, const jsmntok_t* t) {
+ int l = t->end - t->start;
+ if (t->type != JSMN_STRING) return 0;
+ return !strncmp(src + t->start, s, l);
+}
+
+const char* read_str(const char* src, const jsmntok_t* t) {
+ int l = t->end - t->start, i;
+ char* buf = arena_alloc(&arena, l + 1);
+ for (i = 0; i < l; i++)
+ buf[i] = src[i + t->start];
+ buf[i] = 0;
+ return buf;
+}
+
+void parse_node_cfg(const cgltf_node* n, Node_Config* cfg) {
+ int i, c;
+ const char* json = n->extras.data;
+ jsmn_parser p;
+ jsmntok_t toks[32];
+ jsmn_init(&p);
+ c = jsmn_parse(
+ &p,
+ json,
+ strlen(json),
+ toks,
+ sizeof toks / sizeof *toks
+ );
+ if (c == 0) return;
+ if (c < 0) {
+ print_err("Invalid extras json (or too big).\n");
+ pbreak(100);
+ }
+ for (i = 0; i < c; i++) {
+ const jsmntok_t* t = &toks[i];
+ if (tcmp(json, "shader", t)) {
+ cfg->shader = read_str(json, &t[1]);
+ i++;
+ }
+ }
+}
+
+void build_attrib_accessors(
+ const cgltf_primitive* p,
+ Shader_Attrib* desired
+) {
+ int i, c = cgltf_attribute_type_max_enum;
+ Shader_Attrib* attrib = desired;
+ for (i = 0; i < c; i++) {
+ target_attribs[i] = 0;
+ accessors[i] = 0;
+ }
+ c = p->attributes_count;
+ for (; attrib; attrib = attrib->next) {
+ for (i = 0; i < c; i++) {
+ const cgltf_attribute* a = &p->attributes[i];
+ if (attrib->target == a->type) {
+ accessors[attrib->target] = a->data;
+ break;
+ }
+ }
+ target_attribs[attrib->target] = attrib;
+ }
+}
+
+void read_vertex(
+ float* dst,
+ uint16_t idx
+) {
+ int i, c = cgltf_attribute_type_max_enum;
+ for (i = 0; i < c; i++) {
+ const cgltf_accessor* a = accessors[i];
+ Shader_Attrib* t = target_attribs[i];
+ if (!t) continue;
+ int ec = t->size / 4;
+ if (a->component_type != cgltf_component_type_r_32f) {
+ print_err("Only float attributes are supported.\n");
+ pbreak(33);
+ }
+ if (a) {
+ const cgltf_buffer_view* v = a->buffer_view;
+ int j, off = t->offset / 4;
+ int sec = (a->stride / 4);
+ if (ec >= sec) {
+ for (j = 0; j < sec; j++)
+ dst[j + off] = ((float*)&((char*)v->buffer->data)[v->offset])
+ [idx * sec + j];
+ for (; j < ec; j++)
+ dst[j + off] = 0.0f;
+ } else {
+ for (j = 0; j < ec; j++)
+ dst[j + off] = ((float*)&((char*)v->buffer->data)[v->offset])
+ [idx * sec + j];
+ }
+ } else
+ memset(&dst[t->offset / 4], 0, t->size);
+ }
+}
+
+void add_index(uint16_t i) {
+ int s = sizeof i;
+ if (index_count * s >= index_buffer_size) {
+ index_buffer_size *= 2;
+ index_buffer = realloc(index_buffer, index_buffer_size);
+ }
+ index_buffer[index_count++] = i;
+ full_ind_size += sizeof *index_buffer;
+}
+
+int about_eq(float* a, float* b) {
+ int i, ec = vertex_size / 4;
+ for (i = 0; i < ec; i++) {
+ float v = a[i] - b[i];
+ if (v > 0.0001f || v < -0.0001f)
+ return 0;
+ }
+ return 1;
+}
+
+void add_vertex(float* vertex) {
+ int i = 0;
+ for (i = 0; i < vertex_count; i++) {
+ float* check = (float*)&vertex_buffer[vertex_size * i];
+ if (about_eq(vertex, check)) {
+ add_index((uint16_t)i);
+ return;
+ }
+ }
+ if (
+ vertex_count * vertex_size + vertex_size >= vertex_buffer_size
+ ) {
+ vertex_buffer_size *= 2;
+ vertex_buffer = realloc(vertex_buffer, vertex_buffer_size);
+ }
+ memcpy(
+ &vertex_buffer[vertex_size * vertex_count],
+ vertex,
+ vertex_size
+ );
+ add_index(vertex_count++);
+ full_vert_size += vertex_size;
+}
+
+void parse_prim(
+ const cgltf_primitive* p,
+ Shader_Attrib* desired
+) {
+ int i, c;
+ const cgltf_accessor* a;
+ const cgltf_buffer_view* v;
+ uint16_t* indices;
+ if (p->type != cgltf_primitive_type_triangles) {
+ print_err("Only triangle meshes are supported.\n");
+ pbreak(30);
+ }
+ a = p->indices;
+ if (!a) {
+ print_err("Meshes must be indexed.\n");
+ pbreak(31);
+ }
+ if (a->stride != sizeof *index_buffer) {
+ print_err(
+ "Only %d byte indices are supported.\n",
+ sizeof *index_buffer
+ );
+ pbreak(32);
+ }
+ v = a->buffer_view;
+ c = a->count;
+ indices = (uint16_t*)&((char*)v->buffer->data)[v->offset];
+ build_attrib_accessors(p, desired);
+ for (i = 0; i < c; i++) {
+ read_vertex(current_vertex, indices[i]);
+ add_vertex(current_vertex);
+ }
+}
+
+void parse_node_mesh(
+ const cgltf_node* n,
+ Shader_Attrib* desired,
+ FILE* outfile
+) {
+ const cgltf_mesh* m = n->mesh;
+ int i, c = m->primitives_count;
+ vertex_count = 0;
+ index_count = 0;
+ for (i = 0; i < c; i++)
+ parse_prim(&m->primitives[i], desired);
+ fwrite("MESH", 4, 1, outfile);
+ fwrite(&vertex_size, 4, 1, outfile);
+ fwrite(&index_count, 4, 1, outfile);
+ fwrite(&vertex_count, 4, 1, outfile);
+ fwrite(vertex_buffer, 1, vertex_count * vertex_size, outfile);
+ fwrite(index_buffer, 1, index_count * sizeof *index_buffer, outfile);
+}
+
+cgltf_attribute_type get_attribute_type(const char* name) {
+ if (string_equal(name, "position"))
+ return cgltf_attribute_type_position;
+ if (string_equal(name, "normal"))
+ return cgltf_attribute_type_normal;
+ if (string_equal(name, "tangent"))
+ return cgltf_attribute_type_tangent;
+ if (string_equal(name, "uv"))
+ return cgltf_attribute_type_texcoord;
+ if (string_equal(name, "colour"))
+ return cgltf_attribute_type_color;
+ if (string_equal(name, "joints"))
+ return cgltf_attribute_type_joints;
+ if (string_equal(name, "weights"))
+ return cgltf_attribute_type_weights;
+ return cgltf_attribute_type_invalid;
+}
+
+Shader_Attrib* parse_shader_attribs(const char* fname) {
+ FILE* f;
+ char magic[4];
+ int type, i;
+ int binding_count, target_count, desc_count;
+ char* fpath = arena_alloc(
+ &arena,
+ string_len(fname) +
+ string_len(shader_dir) +
+ string_len(shader_dir_sep) + 1
+ );
+ strcpy(fpath, shader_dir);
+ strcat(fpath, shader_dir_sep);
+ strcat(fpath, fname);
+ f = fopen(fpath, "rb");
+ if (!f) {
+ print_err("Failed to open shader %s.\n", fpath);
+ pbreak(303);
+ }
+ fread(magic, 4, 1, f);
+ if (
+ magic[0] != 'C' ||
+ magic[1] != 'S' ||
+ magic[2] != 'H' ||
+ magic[3] != '2'
+ ) {
+ print_err("%s - invalid shader.\n", fname);
+ pbreak(304);
+ }
+ fread(&type, 4, 1, f);
+ assert(type == sprogram_type_graphics);
+ fread(&binding_count, 4, 1, f);
+ fread(&target_count, 4, 1, f);
+ fread(&desc_count, 4, 1, f);
+ assert(binding_count);
+ for (i = 0; i < binding_count; i++) {
+ char name[24];
+ int rate, count;
+ fread(name, 1, sizeof name, f);
+ fread(&rate, 4, 1, f);
+ fread(&count, 4, 1, f);
+ if (string_equal(name, "mesh")) {
+ int j, coff = 0;
+ Shader_Attrib* r = 0;
+ for (j = 0; j < count; j++) {
+ char aname[28];
+ SVariable_Type type;
+ cgltf_attribute_type target;
+ Shader_Attrib* attrib;
+ fread(aname, 1, sizeof aname, f);
+ fread(&type, 4, 1, f);
+ target = get_attribute_type(aname);
+ if (target == cgltf_attribute_type_invalid) {
+ print_err("%s is not a valid mesh attribute.\n", aname);
+ pbreak(305);
+ }
+ attrib = arena_alloc(&arena, sizeof *attrib);
+ attrib->target = target;
+ attrib->type = type;
+ attrib->offset = coff;
+ attrib->size = svariable_type_size(type);
+ attrib->next = r;
+ r = attrib;
+ coff += attrib->size;
+ }
+ fclose(f);
+ return r;
+ } else {
+ fseek(f, 32 * count, SEEK_CUR);
+ }
+ }
+ print_err("Shader %s has no mesh vertex binding.\n", fname);
+ pbreak(306);
+ fclose(f);
+ return 0;
+}
+
+int calc_vertex_size(Shader_Attrib* attribs) {
+ int s = 0;
+ for (; attribs; attribs = attribs->next)
+ s += attribs->size;
+ return s;
+}
+
+void parse_node(const cgltf_node* n, FILE* outfile) {
+ Node_Config cfg = { 0 };
+ if (n->extras.data)
+ parse_node_cfg(n, &cfg);
+ if (n->mesh) {
+ Shader_Attrib* desired;
+ if (!cfg.shader) {
+ print_err(
+ "Node %s has a mesh, but doesn't specify a shader.\n",
+ n->name
+ );
+ pbreak(48);
+ }
+ desired = parse_shader_attribs(cfg.shader);
+ vertex_size = calc_vertex_size(desired);
+ current_vertex = arena_alloc(&arena, vertex_size);
+ assert(desired != 0);
+ parse_node_mesh(n, desired, outfile);
+ }
+}
+
+void write_header(FILE* outfile) {
+ int z = 0;
+ fwrite("MODL", 4, 1, outfile);
+ fwrite(&z, 4, 1, outfile);
+ fwrite(&z, 4, 1, outfile);
+}
+
+int parse(const char* fname, FILE* outfile) {
+ int i, c;
+ cgltf_options opt = { 0 };
+ cgltf_data* d;
+ cgltf_result r;
+ r = cgltf_parse_file(&opt, fname, &d);
+ if (r) return r;
+ r = cgltf_load_buffers(&opt, d, fname);
+ if (r) return r;
+ c = (int)d->nodes_count;
+ fwrite(&c, 4, 1, outfile);
+ for (i = 0; i < c; i++) {
+ clear_arena(&arena);
+ parse_node(&d->nodes[i], outfile);
+ }
+ cgltf_free(d);
+ return 0;
+}
+
+int main(int argc, const char** argv) {
+ int r;
+ int mem_size = 1024 * 1024;
+ void* mem;
+ FILE* outfile;
+ if (argc < 4) {
+ print_err("Usage: %s shader_dir infile outfile\n", argv[0]);
+ return 0;
+ }
+ vertex_buffer_size = 1024;
+ index_buffer_size = 1024;
+ vertex_buffer = malloc(vertex_buffer_size);
+ index_buffer = malloc(index_buffer_size);
+ outfile = fopen(argv[3], "wb");
+ shader_dir = argv[1];
+ shader_dir_sep = "";
+ if (shader_dir[string_len(shader_dir) - 1] != '/')
+ shader_dir_sep = "/";
+ mem = malloc(mem_size);
+ init_arena(&arena, mem, mem_size);
+ write_header(outfile);
+ r = parse(argv[2], outfile);
+ if (r) {
+ print_err("Parse or file error.\n");
+ fclose(outfile);
+ return r;
+ }
+ fseek(outfile, 4, SEEK_SET);
+ fwrite(&full_vert_size, 4, 1, outfile);
+ fwrite(&full_ind_size, 4, 1, outfile);
+ fclose(outfile);
+ return 0;
+}