diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-09 13:14:00 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-09 13:14:00 +0800 |
| commit | 5b5f3e8826758942322849e0ba9f611d323e8fa1 (patch) | |
| tree | 7249890aa6051b1f6bfb8ce65d52e535fba7fe99 /kon/konmodel.c | |
| parent | ce29bee5026fc708d5aae23c9b95307bb7ad5bd1 (diff) | |
Add konmodel tool to convert glTF to kmdl
Diffstat (limited to 'kon/konmodel.c')
| -rw-r--r-- | kon/konmodel.c | 652 |
1 files changed, 652 insertions, 0 deletions
diff --git a/kon/konmodel.c b/kon/konmodel.c new file mode 100644 index 0000000..389d16c --- /dev/null +++ b/kon/konmodel.c @@ -0,0 +1,652 @@ +/* SPDX-License-Identifier: MIT */ +/* + * konmodel.c + * + * Copyright (c) 2026 KonAki + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "konmodel.h" + +/*** definition ***/ + +#define KON_MODEL_VERSION 1 +#define KON_MODEL_MAX_COUNT (1 << 24) + +/*** reading ***/ + +typedef struct kon_cursor { + const uint8_t *data; + size_t size, at; + int bad; +} kon_cursor_t; + +static const uint8_t *kon_take_(kon_cursor_t *c, size_t n) { + const uint8_t *p; + + if (c->bad || n > c->size - c->at) { + c->bad = 1; + return NULL; + } + p = c->data + c->at; + c->at += n; + return p; +} + +static uint32_t kon_readU32_(kon_cursor_t *c) { + const uint8_t *p = kon_take_(c, 4); + if (!p) return 0; + return (uint32_t)p[0] | (uint32_t)p[1] << 8 | (uint32_t)p[2] << 16 | (uint32_t)p[3] << 24; +} + +static float kon_readFloat_(kon_cursor_t *c) { + uint32_t u = kon_readU32_(c); + float f; + memcpy(&f, &u, sizeof f); + return f; +} + +static void kon_readFloats_(kon_cursor_t *c, float *out, size_t n) { + size_t i; + for (i = 0; i < n; i++) out[i] = kon_readFloat_(c); +} + +static void kon_readName_(kon_cursor_t *c, char *out, size_t n) { + const uint8_t *p = kon_take_(c, n); + if (!p) return; + memcpy(out, p, n); + out[n - 1] = 0; +} + +/* the count has to fit in what is left of the file, so a bad count can't make a huge allocation */ +static int kon_countFits_(const kon_cursor_t *c, uint32_t count, size_t each) { + if (count > KON_MODEL_MAX_COUNT) return 0; + return (size_t)count * each <= c->size - c->at; +} + +static void *kon_alloc_(kon_model_t *model, size_t count, size_t each) { + void *p; + + if (count == 0) return NULL; + p = calloc(count, each); + if (p) model->bytes += count * each; + return p; +} + +static int kon_validFloat_(float f) { + return f == f && f > -1e30f && f < 1e30f; +} + +static int kon_readMaterial_(kon_cursor_t *c, kon_modelMaterial_t *m) { + uint32_t filter; + + kon_readName_(c, m->texture, KON_MODEL_TEXTURE_SIZE); + m->color = kon_readU32_(c); + filter = kon_readU32_(c); + m->filter = filter ? KON_FILTER_LINEAR : KON_FILTER_NEAREST; + return !c->bad; +} + +static int kon_readMesh_(kon_cursor_t *c, kon_model_t *model, kon_modelMesh_t *mesh) { + uint32_t material = kon_readU32_(c), vertexCount = kon_readU32_(c), triangleCount = kon_readU32_(c), skinned = kon_readU32_(c); + int i; + + if (c->bad || material >= (uint32_t)model->materialCount || skinned > 1) return 0; + if (vertexCount == 0 || vertexCount > 65535 || triangleCount == 0) return 0; + if (!kon_countFits_(c, vertexCount, 32 + (skinned ? 20 : 0))) return 0; + if (!kon_countFits_(c, triangleCount, 6)) return 0; + if (skinned && model->boneCount == 0) return 0; + + mesh->material = (int)material; + mesh->vertexCount = (int)vertexCount; + mesh->triangleCount = (int)triangleCount; + mesh->vertices = kon_alloc_(model, vertexCount, sizeof *mesh->vertices); + mesh->indices = kon_alloc_(model, (size_t)triangleCount * 3, sizeof *mesh->indices); + if (skinned) { + mesh->joints = kon_alloc_(model, (size_t)vertexCount * 4, 1); + mesh->weights = kon_alloc_(model, (size_t)vertexCount * 4, sizeof *mesh->weights); + if (!mesh->joints || !mesh->weights) return 0; + } + if (!mesh->vertices || !mesh->indices) return 0; + + for (i = 0; i < mesh->vertexCount; i++) { + float f[8]; + kon_readFloats_(c, f, 8); + if (c->bad) return 0; + for (int k = 0; k < 8; k++) if (!kon_validFloat_(f[k])) return 0; + mesh->vertices[i].position = KON_VEC3(f[0], f[1], f[2]); + mesh->vertices[i].normal = KON_VEC3(f[3], f[4], f[5]); + mesh->vertices[i].uv = (kon_vec2_t){f[6], f[7]}; + } + if (skinned) { + for (i = 0; i < mesh->vertexCount; i++) { + const uint8_t *j = kon_take_(c, 4); + if (!j) return 0; + for (int k = 0; k < 4; k++) { + float w = kon_readFloat_(c); + if (!kon_validFloat_(w) || j[k] >= model->boneCount) return 0; + mesh->joints[i * 4 + k] = j[k]; + mesh->weights[i * 4 + k] = w; + } + } + } + for (i = 0; i < mesh->triangleCount * 3; i++) { + const uint8_t *p = kon_take_(c, 2); + if (!p) return 0; + mesh->indices[i] = (uint16_t)(p[0] | p[1] << 8); + if (mesh->indices[i] >= mesh->vertexCount) return 0; + } + return 1; +} + +static int kon_readBone_(kon_cursor_t *c, kon_modelBone_t *bone, int index) { + float f[26]; + uint32_t parent; + int i; + + kon_readName_(c, bone->name, KON_MODEL_NAME_SIZE); + parent = kon_readU32_(c); + kon_readFloats_(c, f, 26); + if (c->bad) return 0; + for (i = 0; i < 26; i++) if (!kon_validFloat_(f[i])) return 0; + bone->parent = (int)parent; + if (parent == 0xFFFFFFFFu) bone->parent = -1; + else if (parent >= (uint32_t)index) return 0; + memcpy(bone->inverseBind.m, f, 16 * sizeof(float)); + bone->position = KON_VEC3(f[16], f[17], f[18]); + bone->rotation = (kon_vec4_t){f[19], f[20], f[21], f[22]}; + bone->scale = KON_VEC3(f[23], f[24], f[25]); + return 1; +} + +static int kon_readClip_(kon_cursor_t *c, kon_model_t *model, kon_modelClip_t *clip) { + uint32_t channelCount; + int i; + + kon_readName_(c, clip->name, KON_MODEL_NAME_SIZE); + clip->duration = kon_readFloat_(c); + channelCount = kon_readU32_(c); + if (c->bad || !kon_validFloat_(clip->duration) || clip->duration < 0.0f) return 0; + if (!kon_countFits_(c, channelCount, 12)) return 0; + + clip->channels = kon_alloc_(model, channelCount, sizeof *clip->channels); + if (channelCount && !clip->channels) return 0; + clip->channelCount = (int)channelCount; + + for (i = 0; i < clip->channelCount; i++) { + kon_modelChannel_t *ch = &clip->channels[i]; + uint32_t bone = kon_readU32_(c), kind = kon_readU32_(c), keyCount = kon_readU32_(c); + int width, k; + + ch->path = (int)(kind & 0xFF); + ch->step = (int)((kind >> 8) & 1); + if (c->bad || bone >= (uint32_t)model->boneCount || ch->path > KON_MODEL_SCALE || (kind >> 9)) return 0; + width = ch->path == KON_MODEL_ROTATION ? 4 : 3; + if (keyCount == 0 || !kon_countFits_(c, keyCount, 4 + (size_t)width * 4)) return 0; + ch->bone = (int)bone; + ch->keyCount = (int)keyCount; + ch->times = kon_alloc_(model, keyCount, sizeof(float)); + ch->values = kon_alloc_(model, (size_t)keyCount * width, sizeof(float)); + if (!ch->times || !ch->values) return 0; + kon_readFloats_(c, ch->times, keyCount); + kon_readFloats_(c, ch->values, (size_t)keyCount * width); + if (c->bad) return 0; + for (k = 0; k < ch->keyCount; k++) { + if (!kon_validFloat_(ch->times[k]) || (k && ch->times[k] < ch->times[k - 1])) return 0; + } + for (k = 0; k < ch->keyCount * width; k++) if (!kon_validFloat_(ch->values[k])) return 0; + } + return 1; +} + +kon_model_t *kon_modelLoad(const uint8_t *data, size_t size) { + kon_cursor_t c = {data, size, 0, 0}; + kon_model_t *model; + const uint8_t *magic; + uint32_t version, materials, meshes, bones, clips; + int i, maxVertices = 0; + + if (!data) return NULL; + magic = kon_take_(&c, 4); + if (!magic || memcmp(magic, "KMDL", 4) != 0) return NULL; + version = kon_readU32_(&c); + materials = kon_readU32_(&c); + meshes = kon_readU32_(&c); + bones = kon_readU32_(&c); + clips = kon_readU32_(&c); + if (c.bad || version != KON_MODEL_VERSION || bones > KON_MODEL_MAX_BONES) return NULL; + if (!kon_countFits_(&c, materials, 72) || !kon_countFits_(&c, meshes, 16)) return NULL; + if (!kon_countFits_(&c, bones, 32 + 4 + 104) || !kon_countFits_(&c, clips, 40)) return NULL; + + model = calloc(1, sizeof *model); + if (!model) return NULL; + model->bytes = sizeof *model; + model->materialCount = (int)materials; + model->meshCount = (int)meshes; + model->boneCount = (int)bones; + model->clipCount = (int)clips; + model->materials = kon_alloc_(model, materials, sizeof *model->materials); + model->meshes = kon_alloc_(model, meshes, sizeof *model->meshes); + model->bones = kon_alloc_(model, bones, sizeof *model->bones); + model->clips = kon_alloc_(model, clips, sizeof *model->clips); + if ((materials && !model->materials) || (meshes && !model->meshes) || (bones && !model->bones) || (clips && !model->clips)) goto fail; + + for (i = 0; i < model->materialCount; i++) if (!kon_readMaterial_(&c, &model->materials[i])) goto fail; + for (i = 0; i < model->boneCount; i++) if (!kon_readBone_(&c, &model->bones[i], i)) goto fail; + for (i = 0; i < model->meshCount; i++) { + if (!kon_readMesh_(&c, model, &model->meshes[i])) goto fail; + if (model->meshes[i].vertexCount > maxVertices) maxVertices = model->meshes[i].vertexCount; + } + for (i = 0; i < model->clipCount; i++) if (!kon_readClip_(&c, model, &model->clips[i])) goto fail; + + model->scratch = kon_alloc_(model, (size_t)maxVertices, sizeof *model->scratch); + if (maxVertices && !model->scratch) goto fail; + model->scratchCount = maxVertices; + return model; + +fail: + kon_modelFree(model); + return NULL; +} + +void kon_modelFree(kon_model_t *model) { + int i, j; + + if (!model) return; + for (i = 0; i < model->meshCount; i++) { + free(model->meshes[i].vertices); + free(model->meshes[i].indices); + free(model->meshes[i].joints); + free(model->meshes[i].weights); + } + for (i = 0; i < model->clipCount; i++) { + for (j = 0; j < model->clips[i].channelCount; j++) { + free(model->clips[i].channels[j].times); + free(model->clips[i].channels[j].values); + } + free(model->clips[i].channels); + } + free(model->materials); + free(model->meshes); + free(model->bones); + free(model->clips); + free(model->scratch); + free(model); +} + +/*** assets ***/ + +static void *kon_modelAssetLoad_(const uint8_t *data, size_t size, size_t *bytes) { + kon_model_t *model = kon_modelLoad(data, size); + if (model) *bytes = model->bytes; + return model; +} + +static void kon_modelAssetUnload_(void *object) { + kon_modelFree((kon_model_t *)object); +} + +static const kon_assetLoader_t kon_modelLoader_ = {"model", ".kmdl", kon_modelAssetLoad_, kon_modelAssetUnload_}; + +int kon_modelRegister(kon_assets_t *assets) { + return kon_assetsRegister(assets, &kon_modelLoader_); +} + +void kon_modelBindTextures(kon_model_t *model, kon_assets_t *assets) { + int i; + + if (!model || !assets) return; + for (i = 0; i < model->materialCount; i++) { + kon_modelMaterial_t *m = &model->materials[i]; + if (!m->texture[0] || m->image) continue; + if (!kon_assetGet(assets, m->texture)) continue; + m->image = kon_assetImage(assets, m->texture); + if (!m->image) kon_assetRelease(assets, m->texture); + } +} + +void kon_modelUnbindTextures(kon_model_t *model, kon_assets_t *assets) { + int i; + + if (!model || !assets) return; + for (i = 0; i < model->materialCount; i++) { + kon_modelMaterial_t *m = &model->materials[i]; + if (m->image) kon_assetRelease(assets, m->texture); + m->image = NULL; + } +} + +/*** animation ***/ + +int kon_modelFindBone(const kon_model_t *model, const char *name) { + int i; + + if (!model || !name) return -1; + for (i = 0; i < model->boneCount; i++) if (strcmp(model->bones[i].name, name) == 0) return i; + return -1; +} + +int kon_modelFindClip(const kon_model_t *model, const char *name) { + int i; + + if (!model || !name) return -1; + for (i = 0; i < model->clipCount; i++) if (strcmp(model->clips[i].name, name) == 0) return i; + return -1; +} + +static kon_vec4_t kon_quatSlerp_(kon_vec4_t a, kon_vec4_t b, float t) { + float dot = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; + float wa, wb, len; + kon_vec4_t r; + + if (dot < 0.0f) { + dot = -dot; + b = (kon_vec4_t){-b.x, -b.y, -b.z, -b.w}; + } + if (dot > 0.9995f) { + wa = 1.0f - t; + wb = t; + } else { + float angle = acosf(dot), s = sinf(angle); + wa = sinf((1.0f - t) * angle) / s; + wb = sinf(t * angle) / s; + } + r = (kon_vec4_t){wa * a.x + wb * b.x, wa * a.y + wb * b.y, wa * a.z + wb * b.z, wa * a.w + wb * b.w}; + len = sqrtf(r.x * r.x + r.y * r.y + r.z * r.z + r.w * r.w); + if (len > 0.0f) r = (kon_vec4_t){r.x / len, r.y / len, r.z / len, r.w / len}; + return r; +} + +static kon_mat4_t kon_composeTRS_(kon_vec3_t p, kon_vec4_t q, kon_vec3_t s) { + float len = sqrtf(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w); + float x, y, z, w; + kon_mat4_t r = kon_mat4Identity(); + + if (len > 0.0f) { + x = q.x / len; y = q.y / len; z = q.z / len; w = q.w / len; + } else { + x = y = z = 0.0f; w = 1.0f; + } + r.m[0] = (1.0f - 2.0f * (y * y + z * z)) * s.x; + r.m[1] = 2.0f * (x * y + z * w) * s.x; + r.m[2] = 2.0f * (x * z - y * w) * s.x; + r.m[4] = 2.0f * (x * y - z * w) * s.y; + r.m[5] = (1.0f - 2.0f * (x * x + z * z)) * s.y; + r.m[6] = 2.0f * (y * z + x * w) * s.y; + r.m[8] = 2.0f * (x * z + y * w) * s.z; + r.m[9] = 2.0f * (y * z - x * w) * s.z; + r.m[10] = (1.0f - 2.0f * (x * x + y * y)) * s.z; + r.m[12] = p.x; r.m[13] = p.y; r.m[14] = p.z; + return r; +} + +/* finds the pair of keys around time and how far between them it is. before the first or after the last key it holds that key */ +static void kon_findKeys_(const kon_modelChannel_t *ch, float time, int *k0, int *k1, float *t) { + int lo = 0, hi = ch->keyCount - 1; + + if (time <= ch->times[0]) { *k0 = *k1 = 0; *t = 0.0f; return; } + if (time >= ch->times[hi]) { *k0 = *k1 = hi; *t = 0.0f; return; } + while (hi - lo > 1) { + int mid = (lo + hi) / 2; + if (ch->times[mid] <= time) lo = mid; else hi = mid; + } + *k0 = lo; + *k1 = hi; + *t = (time - ch->times[lo]) / (ch->times[hi] - ch->times[lo]); +} + +void kon_modelSample(const kon_model_t *model, int clip, float time, int loop, kon_mat4_t *skin) { + kon_vec3_t position[KON_MODEL_MAX_BONES], scale[KON_MODEL_MAX_BONES]; + kon_vec4_t rotation[KON_MODEL_MAX_BONES]; + kon_mat4_t global[KON_MODEL_MAX_BONES]; + int i; + + if (!model || !skin) return; + for (i = 0; i < model->boneCount; i++) { + position[i] = model->bones[i].position; + rotation[i] = model->bones[i].rotation; + scale[i] = model->bones[i].scale; + } + + if (clip >= 0 && clip < model->clipCount) { + const kon_modelClip_t *c = &model->clips[clip]; + + if (!(time == time)) time = 0.0f; + if (c->duration > 0.0f) { + if (loop) { + time = fmodf(time, c->duration); + if (time < 0.0f) time += c->duration; + } else if (time > c->duration) { + time = c->duration; + } + } + if (time < 0.0f) time = 0.0f; + + for (i = 0; i < c->channelCount; i++) { + const kon_modelChannel_t *ch = &c->channels[i]; + int k0, k1; + float t; + + kon_findKeys_(ch, time, &k0, &k1, &t); + if (ch->step) t = 0.0f; + if (ch->path == KON_MODEL_ROTATION) { + const float *a = &ch->values[k0 * 4], *b = &ch->values[k1 * 4]; + rotation[ch->bone] = kon_quatSlerp_((kon_vec4_t){a[0], a[1], a[2], a[3]}, (kon_vec4_t){b[0], b[1], b[2], b[3]}, t); + } else { + const float *a = &ch->values[k0 * 3], *b = &ch->values[k1 * 3]; + kon_vec3_t v = kon_vec3Lerp(KON_VEC3(a[0], a[1], a[2]), KON_VEC3(b[0], b[1], b[2]), t); + if (ch->path == KON_MODEL_POSITION) position[ch->bone] = v; else scale[ch->bone] = v; + } + } + } + + for (i = 0; i < model->boneCount; i++) { + kon_mat4_t local = kon_composeTRS_(position[i], rotation[i], scale[i]); + int parent = model->bones[i].parent; + global[i] = parent >= 0 ? kon_mat4Mul(global[parent], local) : local; + skin[i] = kon_mat4Mul(global[i], model->bones[i].inverseBind); + } +} + +/*** drawing ***/ + +static kon_vec3_t kon_mat4MulDirection_(const kon_mat4_t *m, kon_vec3_t v) { + return KON_VEC3(m->m[0] * v.x + m->m[4] * v.y + m->m[8] * v.z, + m->m[1] * v.x + m->m[5] * v.y + m->m[9] * v.z, + m->m[2] * v.x + m->m[6] * v.y + m->m[10] * v.z); +} + +static void kon_skinMesh_(const kon_modelMesh_t *mesh, const kon_mat4_t *skin, kon_vertex_t *out) { + int i, k; + + for (i = 0; i < mesh->vertexCount; i++) { + const kon_vertex_t *v = &mesh->vertices[i]; + kon_vec3_t p = KON_VEC3_ZERO, n = KON_VEC3_ZERO; + float total = 0.0f; + + for (k = 0; k < 4; k++) { + float w = mesh->weights[i * 4 + k]; + const kon_mat4_t *m = &skin[mesh->joints[i * 4 + k]]; + if (w == 0.0f) continue; + p = kon_vec3Add(p, kon_vec3Scale(kon_mat4MulPoint(*m, v->position), w)); + n = kon_vec3Add(n, kon_vec3Scale(kon_mat4MulDirection_(m, v->normal), w)); + total += w; + } + out[i].uv = v->uv; + if (total > 0.0f) { + out[i].position = p; + out[i].normal = kon_vec3Normalize(n); + } else { + out[i].position = v->position; + out[i].normal = v->normal; + } + } +} + +void kon_modelDraw(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_mat4_t world, kon_model_t *model, const kon_mat4_t *skin, const kon_light_t *light, kon_renderFlags_t flags) { + int i; + + if (!fb || !model) return; + for (i = 0; i < model->meshCount; i++) { + const kon_modelMesh_t *mesh = &model->meshes[i]; + const kon_modelMaterial_t *source = &model->materials[mesh->material]; + kon_material_t material; + kon_mesh_t draw; + + material.texture = source->image; + material.color = source->color; + material.filter = source->filter; + draw.indices = mesh->indices; + draw.triangleCount = mesh->triangleCount; + draw.vertices = mesh->vertices; + if (skin && mesh->weights) { + kon_skinMesh_(mesh, skin, model->scratch); + draw.vertices = model->scratch; + } + kon_drawMesh(fb, viewProjection, world, &draw, &material, light, flags); + } +} + +/*** writing ***/ + +#ifdef KONMODEL_WRITER + +typedef struct kon_sink { + uint8_t *data; + size_t size, capacity; + int bad; +} kon_sink_t; + +static void kon_put_(kon_sink_t *s, const void *src, size_t n) { + if (s->bad) return; + if (s->size + n > s->capacity) { + size_t capacity = s->capacity ? s->capacity : 1024; + uint8_t *grown; + while (capacity < s->size + n) capacity *= 2; + grown = realloc(s->data, capacity); + if (!grown) { s->bad = 1; return; } + s->data = grown; + s->capacity = capacity; + } + memcpy(s->data + s->size, src, n); + s->size += n; +} + +static void kon_putU32_(kon_sink_t *s, uint32_t v) { + uint8_t b[4] = {(uint8_t)v, (uint8_t)(v >> 8), (uint8_t)(v >> 16), (uint8_t)(v >> 24)}; + kon_put_(s, b, 4); +} + +static void kon_putFloat_(kon_sink_t *s, float f) { + uint32_t u; + memcpy(&u, &f, sizeof u); + kon_putU32_(s, u); +} + +static void kon_putName_(kon_sink_t *s, const char *name, size_t n) { + char buffer[KON_MODEL_TEXTURE_SIZE]; + size_t length; + + memset(buffer, 0, sizeof buffer); + length = strlen(name); + if (length > n - 1) length = n - 1; + memcpy(buffer, name, length); + kon_put_(s, buffer, n); +} + +uint8_t *kon_modelSerialize(const kon_model_t *model, size_t *size) { + kon_sink_t s = {NULL, 0, 0, 0}; + int i, j, k; + + if (!model) return NULL; + kon_put_(&s, "KMDL", 4); + kon_putU32_(&s, KON_MODEL_VERSION); + kon_putU32_(&s, (uint32_t)model->materialCount); + kon_putU32_(&s, (uint32_t)model->meshCount); + kon_putU32_(&s, (uint32_t)model->boneCount); + kon_putU32_(&s, (uint32_t)model->clipCount); + + for (i = 0; i < model->materialCount; i++) { + kon_putName_(&s, model->materials[i].texture, KON_MODEL_TEXTURE_SIZE); + kon_putU32_(&s, model->materials[i].color); + kon_putU32_(&s, model->materials[i].filter == KON_FILTER_LINEAR); + } + for (i = 0; i < model->boneCount; i++) { + const kon_modelBone_t *b = &model->bones[i]; + kon_putName_(&s, b->name, KON_MODEL_NAME_SIZE); + kon_putU32_(&s, b->parent < 0 ? 0xFFFFFFFFu : (uint32_t)b->parent); + for (k = 0; k < 16; k++) kon_putFloat_(&s, b->inverseBind.m[k]); + kon_putFloat_(&s, b->position.x); kon_putFloat_(&s, b->position.y); kon_putFloat_(&s, b->position.z); + kon_putFloat_(&s, b->rotation.x); kon_putFloat_(&s, b->rotation.y); kon_putFloat_(&s, b->rotation.z); kon_putFloat_(&s, b->rotation.w); + kon_putFloat_(&s, b->scale.x); kon_putFloat_(&s, b->scale.y); kon_putFloat_(&s, b->scale.z); + } + for (i = 0; i < model->meshCount; i++) { + const kon_modelMesh_t *m = &model->meshes[i]; + int skinned = m->joints && m->weights; + kon_putU32_(&s, (uint32_t)m->material); + kon_putU32_(&s, (uint32_t)m->vertexCount); + kon_putU32_(&s, (uint32_t)m->triangleCount); + kon_putU32_(&s, (uint32_t)skinned); + for (j = 0; j < m->vertexCount; j++) { + const kon_vertex_t *v = &m->vertices[j]; + kon_putFloat_(&s, v->position.x); kon_putFloat_(&s, v->position.y); kon_putFloat_(&s, v->position.z); + kon_putFloat_(&s, v->normal.x); kon_putFloat_(&s, v->normal.y); kon_putFloat_(&s, v->normal.z); + kon_putFloat_(&s, v->uv.x); kon_putFloat_(&s, v->uv.y); + } + if (skinned) { + for (j = 0; j < m->vertexCount; j++) { + kon_put_(&s, &m->joints[j * 4], 4); + for (k = 0; k < 4; k++) kon_putFloat_(&s, m->weights[j * 4 + k]); + } + } + for (j = 0; j < m->triangleCount * 3; j++) { + uint8_t b[2] = {(uint8_t)m->indices[j], (uint8_t)(m->indices[j] >> 8)}; + kon_put_(&s, b, 2); + } + } + for (i = 0; i < model->clipCount; i++) { + const kon_modelClip_t *c = &model->clips[i]; + kon_putName_(&s, c->name, KON_MODEL_NAME_SIZE); + kon_putFloat_(&s, c->duration); + kon_putU32_(&s, (uint32_t)c->channelCount); + for (j = 0; j < c->channelCount; j++) { + const kon_modelChannel_t *ch = &c->channels[j]; + int width = ch->path == KON_MODEL_ROTATION ? 4 : 3; + kon_putU32_(&s, (uint32_t)ch->bone); + kon_putU32_(&s, (uint32_t)ch->path | (uint32_t)(ch->step != 0) << 8); + kon_putU32_(&s, (uint32_t)ch->keyCount); + for (k = 0; k < ch->keyCount; k++) kon_putFloat_(&s, ch->times[k]); + for (k = 0; k < ch->keyCount * width; k++) kon_putFloat_(&s, ch->values[k]); + } + } + + if (s.bad) { + free(s.data); + return NULL; + } + *size = s.size; + return s.data; +} + +#endif |
