#include #include #define CGLTF_IMPLEMENTATION #include "cgltf.h" typedef struct { unsigned short p, n, t; } Vertex; struct { int vc, pc, nc, tc; int* p, * n, * t; Vertex* verts; } result = { 0 }; struct { unsigned short* ind; float* p, * n, * t; int ic, pc, nc, tc; } intern = { 0 }; void parse_attrib( float** d, int* c, int ec, const cgltf_accessor* a ) { int count, es = ec * 4; const cgltf_buffer_view* v = a->buffer_view; if (a->component_type != cgltf_component_type_r_32f) { fprintf(stderr, "Only float attributes are supported.\n"); exit(33); } if (ec * 4 != a->stride) { fprintf(stderr, "Attribute stride doesn't match component count.\n"); exit(34); } count = *c + a->count; *d = realloc(*d, count * es); memcpy( &(*d)[*c], &((char*)v->buffer->data)[v->offset], a->count * es ); *c = count; } void parse_prim_attribs(const cgltf_primitive* p) { int i; unsigned m = 0; for (i = 0; i < p->attributes_count; i++) { const cgltf_attribute* a = &p->attributes[i]; switch (a->type) { case cgltf_attribute_type_position: parse_attrib(&intern.p, &intern.pc, 3, a->data); m |= 0x1; break; case cgltf_attribute_type_normal: parse_attrib(&intern.n, &intern.nc, 3, a->data); m |= 0x2; break; case cgltf_attribute_type_texcoord: parse_attrib(&intern.t, &intern.tc, 2, a->data); m |= 0x4; break; default: break; } } if (m != 0x7) { fprintf(stderr, "Not all attributes were provided.\n"); exit(32); } } void parse_prim(const cgltf_primitive* p) { int i, c, s; const cgltf_accessor* a; const cgltf_buffer_view* v; if (p->type != cgltf_primitive_type_triangles) { fprintf(stderr, "Only triangle meshes are supported.\n"); exit(30); } a = p->indices; if (!a) { fprintf(stderr, "Meshes must be indexed.\n"); exit(35); } if (a->stride != 2) { fprintf(stderr, "Only two byte indices are supported.\n"); exit(31); } v = a->buffer_view; c = intern.ic + a->count; intern.ind = realloc(intern.ind, c * sizeof *intern.ind); memcpy( &intern.ind[intern.ic], &((char*)v->buffer->data)[v->offset], a->count * sizeof *intern.ind ); intern.ic = c; parse_prim_attribs(p); } void parse_mesh(const cgltf_mesh* m) { int i; for (i = 0; i < m->primitives_count; i++) parse_prim(&m->primitives[i]); } int parse(const char* name) { int i; cgltf_options opt = { 0 }; cgltf_data* d; cgltf_result r; r = cgltf_parse_file(&opt, name, &d); if (r) return r; r = cgltf_load_buffers(&opt, d, name); if (r) return r; for (i = 0; i < d->meshes_count; i++) parse_mesh(&d->meshes[i]); cgltf_free(d); return 0; } int to_fixed(float v) { return (int)(v * 512.0f); } int attrib_eq(float* a, int* b, int ec) { int i; for (i = 0; i < ec; i++) { int fpv = to_fixed(a[i]); if (fpv != b[i]) return 0; } return 1; } void convert_attrib( float* s, int ec, unsigned short* di, int* dc, int* d ) { int i, idx, c = *dc; for (i = 0; i < c; i++) { int ei = i * ec; if (attrib_eq(s, &d[ei], ec)) { *di = i; return; } } idx = c++; *dc = c; *di = (unsigned short)idx; for (i = 0; i < ec; i++) { int iidx = i + idx * ec; d[iidx] = to_fixed(s[i]); } } void convert_vert(int i) { Vertex* d = &result.verts[result.vc++]; int idx = intern.ind[i]; convert_attrib( &intern.p[idx * 3], 3, &d->p, &result.pc, result.p ); convert_attrib( &intern.n[idx * 3], 3, &d->n, &result.nc, result.n ); convert_attrib( &intern.t[idx * 2], 2, &d->t, &result.tc, result.t ); } void convert(void) { int i; result.p = malloc(intern.pc * sizeof *result.p * 3); result.n = malloc(intern.nc * sizeof *result.n * 3); result.t = malloc(intern.tc * sizeof *result.t * 2); result.verts = malloc(intern.ic * sizeof *result.verts); for (i = 0; i < intern.ic; i++) { convert_vert(i); } } void dump(const char* name) { FILE* outfile = fopen(name, "wb"); if (!outfile) { fprintf(stderr, "Failed to open %s\n", name); exit(35); } fwrite(&result, 1, sizeof result, outfile); fwrite(result.p, result.pc, sizeof* result.p * 3, outfile); fwrite(result.n, result.nc, sizeof* result.n * 3, outfile); fwrite(result.t, result.tc, sizeof* result.t * 2, outfile); fwrite(result.verts, result.vc, sizeof* result.verts, outfile); } int main(int argc, const char** argv) { int r; if (argc < 3) { fprintf( stderr, "Usage: %s infile outfile\n", argv[0] ); return 1; } r = parse(argv[1]); if (r) return r; convert(); dump(argv[2]); return 0; }