1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
/* SPDX-License-Identifier: MIT */
/*
* konmodel.h
*
* 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.
*/
#ifndef KONMODEL_H
#define KONMODEL_H
#include <stdint.h>
#include <stddef.h>
#include "konmath.h"
#include "konsofren.h"
#include "konassets.h"
#ifndef KONSOFREN_HAS_3D
#error "this needs the 3D part of konsofren, define KONSOFREN_3D before the first include of konsofren.h (build.sh does it for -m 3d, model and sector)"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*** definition ***/
#define KON_MODEL_MAX_BONES 128
#define KON_MODEL_NAME_SIZE 32
#define KON_MODEL_TEXTURE_SIZE 64
typedef struct kon_modelMaterial {
char texture[KON_MODEL_TEXTURE_SIZE]; /* asset name, empty for none */
uint32_t color;
kon_filter_t filter;
const kon_image *image; /* set by kon_modelBindTextures */
} kon_modelMaterial_t;
/* joints and weights hold 4 entries per vertex, both are NULL if the mesh isn't skinned */
typedef struct kon_modelMesh {
int material;
int vertexCount;
int triangleCount;
kon_vertex_t *vertices;
uint16_t *indices;
uint8_t *joints;
float *weights;
} kon_modelMesh_t;
/* bones are sorted so a parent always comes before its children */
typedef struct kon_modelBone {
char name[KON_MODEL_NAME_SIZE];
int parent; /* -1 for a root */
kon_mat4_t inverseBind;
kon_vec3_t position;
kon_vec4_t rotation; /* quaternion x y z w */
kon_vec3_t scale;
} kon_modelBone_t;
typedef enum kon_modelPath {
KON_MODEL_POSITION = 0,
KON_MODEL_ROTATION,
KON_MODEL_SCALE
} kon_modelPath_t;
/* values holds 3 floats per key, 4 for rotation. step holds each key until the next one instead of blending */
typedef struct kon_modelChannel {
int bone;
int path;
int step;
int keyCount;
float *times;
float *values;
} kon_modelChannel_t;
typedef struct kon_modelClip {
char name[KON_MODEL_NAME_SIZE];
float duration;
int channelCount;
kon_modelChannel_t *channels;
} kon_modelClip_t;
typedef struct kon_model {
int materialCount, meshCount, boneCount, clipCount;
kon_modelMaterial_t *materials;
kon_modelMesh_t *meshes;
kon_modelBone_t *bones;
kon_modelClip_t *clips;
kon_vertex_t *scratch; /* skinned vertices are built here while drawing */
int scratchCount;
size_t bytes;
} kon_model_t;
/*** declarations ***/
/* NULL if the data is not a valid model, nothing is read outside of it */
kon_model_t *kon_modelLoad(const uint8_t *data, size_t size);
void kon_modelFree(kon_model_t *model);
/* loads .kmdl files through the asset manager, returns the type id for kon_assetGetAs or -1.
a model gets its textures when it is loaded and gives them back when it is collected, you don't need to bind them yourself */
int kon_modelRegister(kon_assets_t *assets);
/* gets every texture the materials name and keeps it in use until unbind. missing ones are left untextured.
only needed for models you loaded without the asset manager */
void kon_modelBindTextures(kon_model_t *model, kon_assets_t *assets);
void kon_modelUnbindTextures(kon_model_t *model, kon_assets_t *assets);
int kon_modelFindBone(const kon_model_t *model, const char *name);
int kon_modelFindClip(const kon_model_t *model, const char *name);
/* fills skin with one matrix per bone to pass to kon_modelDraw. clip -1 gives the rest pose.
a looping clip wraps around, otherwise time stops at the end */
void kon_modelSample(const kon_model_t *model, int clip, float time, int loop, kon_mat4_t *skin);
/* a pose is where every bone is relative to its parent, for mixing animations before skinning */
typedef struct kon_pose {
kon_vec3_t position[KON_MODEL_MAX_BONES];
kon_vec4_t rotation[KON_MODEL_MAX_BONES];
kon_vec3_t scale[KON_MODEL_MAX_BONES];
} kon_pose_t;
void kon_poseRest(const kon_model_t *model, kon_pose_t *pose);
/* writes the bones the clip moves into pose and leaves the others as they were.
clipModel is the model the clip is stored in, it can be another model or NULL for the same one. bones are matched by name,
so a clip from a different model works on any model whose skeleton uses the same bone names */
void kon_poseClip(const kon_model_t *model, const kon_model_t *clipModel, int clip, float time, int loop, kon_pose_t *pose);
/* out = a at t 0, b at t 1. mask has one weight per bone to blend some bones more than others, or NULL for all.
out can be the same as a or b */
void kon_poseBlend(const kon_model_t *model, const kon_pose_t *a, const kon_pose_t *b, float t, const float *mask, kon_pose_t *out);
/* sets mask to 1 for the named bone and everything below it, 0 for the rest. returns 0 if there is no such bone */
int kon_modelMaskBranch(const kon_model_t *model, const char *bone, float *mask);
void kon_poseSkin(const kon_model_t *model, const kon_pose_t *pose, kon_mat4_t *skin);
/* skin can be NULL to draw without animation. light and flags work like in kon_drawMesh.
not safe to call for the same model from two threads */
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);
#ifdef KONMODEL_WRITER
/* makes the .kmdl bytes for a model, free them yourself. NULL if it fails. only the converter needs this */
uint8_t *kon_modelSerialize(const kon_model_t *model, size_t *size);
#endif
#ifdef __cplusplus
}
#endif
#endif
|