summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2023-02-04 14:36:02 -0800
committer3gg <3gg@shellblade.net>2023-02-04 14:36:02 -0800
commit8068d0a816b3efd17ebb0dcf468c6d333e3577d3 (patch)
treef88f0845c34efbb1c43f88ea71fcbf32ea5adae9
parentf319f0bb59f5e05bd06dcd160699f2b0f2f14e8a (diff)
Add support for skeletal animation.
-rw-r--r--gfx/CMakeLists.txt1
-rw-r--r--gfx/doc/extern/Scene Graph - CSE 167.pdfbin0 -> 890801 bytes
-rw-r--r--gfx/doc/scene.pngbin45239 -> 59119 bytes
-rw-r--r--gfx/doc/scene.txt37
-rw-r--r--gfx/include/gfx/render_backend.h57
-rw-r--r--gfx/include/gfx/scene/README.md50
-rw-r--r--gfx/include/gfx/scene/animation.h127
-rw-r--r--gfx/include/gfx/scene/node.h59
-rw-r--r--gfx/include/gfx/scene/object.h6
-rw-r--r--gfx/include/gfx/sizes.h18
-rw-r--r--gfx/include/gfx/util/scene.h5
-rw-r--r--gfx/shaders/cook_torrance.vert38
-rw-r--r--gfx/src/render/constants.h8
-rw-r--r--gfx/src/render/geometry.c222
-rw-r--r--gfx/src/render/geometry.h10
-rw-r--r--gfx/src/render/render_backend.c48
-rw-r--r--gfx/src/render/render_backend_impl.h12
-rw-r--r--gfx/src/render/shader_program.c98
-rw-r--r--gfx/src/renderer/renderer.c34
-rw-r--r--gfx/src/scene/animation.c403
-rw-r--r--gfx/src/scene/animation_impl.h95
-rw-r--r--gfx/src/scene/material.c5
-rw-r--r--gfx/src/scene/node.c193
-rw-r--r--gfx/src/scene/node_impl.h11
-rw-r--r--gfx/src/scene/object.c14
-rw-r--r--gfx/src/scene/object_impl.h7
-rw-r--r--gfx/src/scene/scene_memory.c79
-rw-r--r--gfx/src/scene/scene_memory.h53
-rw-r--r--gfx/src/scene/types.h29
-rw-r--r--gfx/src/util/scene.c692
-rw-r--r--gltfview/src/game.c41
-rw-r--r--gltfview/src/game.h11
32 files changed, 2070 insertions, 393 deletions
diff --git a/gfx/CMakeLists.txt b/gfx/CMakeLists.txt
index e91450b..d60a59f 100644
--- a/gfx/CMakeLists.txt
+++ b/gfx/CMakeLists.txt
@@ -40,6 +40,7 @@ add_library(gfx
40 src/render/shader.c 40 src/render/shader.c
41 src/render/texture.c 41 src/render/texture.c
42 src/renderer/renderer.c 42 src/renderer/renderer.c
43 src/scene/animation.c
43 src/scene/camera.c 44 src/scene/camera.c
44 src/scene/light.c 45 src/scene/light.c
45 src/scene/material.c 46 src/scene/material.c
diff --git a/gfx/doc/extern/Scene Graph - CSE 167.pdf b/gfx/doc/extern/Scene Graph - CSE 167.pdf
new file mode 100644
index 0000000..5fbbb10
--- /dev/null
+++ b/gfx/doc/extern/Scene Graph - CSE 167.pdf
Binary files differ
diff --git a/gfx/doc/scene.png b/gfx/doc/scene.png
index 017e91b..85d2447 100644
--- a/gfx/doc/scene.png
+++ b/gfx/doc/scene.png
Binary files differ
diff --git a/gfx/doc/scene.txt b/gfx/doc/scene.txt
index dc22927..a771488 100644
--- a/gfx/doc/scene.txt
+++ b/gfx/doc/scene.txt
@@ -4,7 +4,6 @@ class Scene {
4 4
5} 5}
6 6
7Scene *-- Camera
8Scene *-- Node 7Scene *-- Node
9 8
10class Camera { 9class Camera {
@@ -15,11 +14,41 @@ class Node {
15 + transform 14 + transform
16} 15}
17 16
17Node *-- AnimationObject
18Node *-- Object 18Node *-- Object
19Node o-- Light : "affected by" 19Node *-- Light
20Node o-- Camera 20Node *-- Camera
21Node *-- Node 21Node *-- Node
22 22
23class AnimationObject {
24
25}
26
27AnimationObject *-- Animation
28AnimationObject --> AnimationState
29AnimationObject --> Skeleton
30
31class AnimationState {
32 + time
33 + current_animation
34 + pose
35}
36
37class Animation {
38 + name
39}
40
41Animation *-- Keyframe
42
43class Keyframe {
44 + time
45 + transforms
46}
47
48class Skeleton {
49 + joints
50}
51
23class Object { 52class Object {
24 + transform 53 + transform
25} 54}
@@ -33,6 +62,7 @@ class Mesh {
33Mesh --> BoundingVolume 62Mesh --> BoundingVolume
34Mesh --> Geometry 63Mesh --> Geometry
35Mesh --> Material 64Mesh --> Material
65Mesh --> Shader
36 66
37class Geometry { 67class Geometry {
38 + positions 68 + positions
@@ -49,7 +79,6 @@ class Material {
49 + shader params 79 + shader params
50} 80}
51 81
52Material --> Shader
53Material o-- Texture 82Material o-- Texture
54 83
55class Shader { 84class Shader {
diff --git a/gfx/include/gfx/render_backend.h b/gfx/include/gfx/render_backend.h
index 3f2dbd0..167cf8a 100644
--- a/gfx/include/gfx/render_backend.h
+++ b/gfx/include/gfx/render_backend.h
@@ -48,6 +48,9 @@ typedef enum PrimitiveType {
48 size_t stride_bytes; \ 48 size_t stride_bytes; \
49 } NAME; 49 } NAME;
50 50
51/// A buffer view for untyped data.
52MAKE_BUFFER_VIEW(BufferView, void)
53
51/// A buffer view for 2D vectors. 54/// A buffer view for 2D vectors.
52MAKE_BUFFER_VIEW(BufferView2d, vec2) 55MAKE_BUFFER_VIEW(BufferView2d, vec2)
53 56
@@ -57,16 +60,37 @@ MAKE_BUFFER_VIEW(BufferView3d, vec3)
57/// A buffer view for 4D vectors. 60/// A buffer view for 4D vectors.
58MAKE_BUFFER_VIEW(BufferView4d, vec4) 61MAKE_BUFFER_VIEW(BufferView4d, vec4)
59 62
63/// A buffer view for floats.
64MAKE_BUFFER_VIEW(BufferViewFloat, float)
65
66/// A buffer view for 8-bit unsigned integers.
67MAKE_BUFFER_VIEW(BufferViewU8, uint8_t)
68
69/// A buffer view for 16-bit unsigned integers.
70MAKE_BUFFER_VIEW(BufferViewU16, uint16_t)
71
60/// A buffer view for vertex indices. 72/// A buffer view for vertex indices.
61MAKE_BUFFER_VIEW(BufferViewIdx, uint16_t) 73MAKE_BUFFER_VIEW(BufferViewIdx, uint16_t)
62 74
63/// Describes a piece of geometry. 75/// Describes a piece of geometry.
76///
77/// Currently we support only 16-bit vertex indices. Might have to change this
78/// to support a larger variety of 3D models.
64typedef struct GeometryDesc { 79typedef struct GeometryDesc {
65 BufferView2d positions2d; 80 BufferView2d positions2d;
66 BufferView3d positions3d; 81 BufferView3d positions3d;
67 BufferView3d normals; 82 BufferView3d normals;
68 BufferView4d tangents; 83 BufferView4d tangents;
69 BufferView2d texcoords; 84 BufferView2d texcoords;
85 struct {
86 BufferViewU8 u8;
87 BufferViewU16 u16;
88 } joints; // uvec4.
89 struct {
90 BufferViewFloat floats;
91 BufferViewU8 u8;
92 BufferViewU16 u16;
93 } weights; // vec4 or uvec4.
70 BufferViewIdx indices; 94 BufferViewIdx indices;
71 VertexCount num_verts; 95 VertexCount num_verts;
72 size_t num_indices; 96 size_t num_indices;
@@ -102,10 +126,14 @@ typedef enum {
102 UniformMat4, 126 UniformMat4,
103 UniformTexture, 127 UniformTexture,
104 UniformVec3, 128 UniformVec3,
105 UniformVec4 129 UniformVec4,
130 UniformMat4Array
106} UniformType; 131} UniformType;
107 132
108/// Shader uniform. 133/// Shader uniform.
134///
135/// For uniform arrays, the client must ensure that the array is still valid by
136/// the time the uniform data is passed to the GPU.
109typedef struct ShaderUniform { 137typedef struct ShaderUniform {
110 sstring name; 138 sstring name;
111 UniformType type; 139 UniformType type;
@@ -115,6 +143,12 @@ typedef struct ShaderUniform {
115 vec3 vec3; 143 vec3 vec3;
116 vec4 vec4; 144 vec4 vec4;
117 float scalar; 145 float scalar;
146 struct {
147 size_t count;
148 union {
149 const mat4* values;
150 };
151 } array;
118 } value; 152 } value;
119} ShaderUniform; 153} ShaderUniform;
120 154
@@ -236,6 +270,12 @@ Buffer* gfx_make_buffer3d(RenderBackend*, const vec3* verts, size_t count);
236/// Create a buffer from 4D vertices. 270/// Create a buffer from 4D vertices.
237Buffer* gfx_make_buffer4d(RenderBackend*, const vec4* verts, size_t count); 271Buffer* gfx_make_buffer4d(RenderBackend*, const vec4* verts, size_t count);
238 272
273/// Create a buffer from 8-bit unsigned integers.
274Buffer* gfx_make_bufferu8(RenderBackend*, const uint8_t* vals, size_t count);
275
276/// Create a buffer from 16-bit unsigned integers.
277Buffer* gfx_make_bufferu16(RenderBackend*, const uint16_t* vals, size_t count);
278
239/// Destroy the buffer. 279/// Destroy the buffer.
240void gfx_destroy_buffer(RenderBackend*, Buffer**); 280void gfx_destroy_buffer(RenderBackend*, Buffer**);
241 281
@@ -353,3 +393,8 @@ void gfx_set_vec4_uniform(ShaderProgram*, const char* name, vec4);
353/// Set the float uniform. 393/// Set the float uniform.
354/// Has no effect if the shader does not contain the given uniform. 394/// Has no effect if the shader does not contain the given uniform.
355void gfx_set_float_uniform(ShaderProgram*, const char* name, float value); 395void gfx_set_float_uniform(ShaderProgram*, const char* name, float value);
396
397/// Set the matrix array uniform.
398/// Has no effect if the shader does not contain the given uniform.
399void gfx_set_mat4_array_uniform(
400 ShaderProgram*, const char* name, const mat4*, size_t count);
diff --git a/gfx/include/gfx/scene/README.md b/gfx/include/gfx/scene/README.md
index 1910abe..c8cdadb 100644
--- a/gfx/include/gfx/scene/README.md
+++ b/gfx/include/gfx/scene/README.md
@@ -10,7 +10,7 @@ A scene graph implementation that includes:
10- Object 10- Object
11- Scene 11- Scene
12 12
13## Hierarchy and parenting 13## Hierarchy and Parenting
14 14
15Scene graphs typically expose functions on nodes to add/remove objects, cameras, 15Scene graphs typically expose functions on nodes to add/remove objects, cameras,
16lights, etc. This implementation forces the hierarchy to be a strict tree and 16lights, etc. This implementation forces the hierarchy to be a strict tree and
@@ -25,3 +25,51 @@ glTF 2.0 spec [enforces this](https://github.com/KhronosGroup/glTF/blob/master/s
25> acyclic graph (DAG) or scene graph, but a disjoint union of strict trees. That 25> acyclic graph (DAG) or scene graph, but a disjoint union of strict trees. That
26> is, no node may be a direct descendant of more than one node. This restriction 26> is, no node may be a direct descendant of more than one node. This restriction
27> is meant to simplify implementation and facilitate conformance.* 27> is meant to simplify implementation and facilitate conformance.*
28
29## Instancing
30
31Two use cases for instancing seem to be:
32
331. Creating N identical clones, but each with a unique transform. (Ex: N
34animated characters animated in unison but located in different locations.)
352. Creating N copies of a sub-tree, each now being their own unique tree. (Ex:
36The same N animated characters, but each of them now being animated separately.)
37
38Some scene graphs
39([Panda3D](https://docs.panda3d.org/1.10/python/programming/scene-graph/instancing))
40allow two or more nodes to point to the same child, or, in other words, a node
41to have multiple parents. This turns the scene graph into a DAG and adds a
42number of complications for us:
43
441. Shared ownership of children. We would now need some sort of ref counting or
45deferred GC to delete nodes and their subtrees.
462. Nodes no longer have a unique parent.
473. Given a node, we can no longer determine its location (which parent link do
48you follow?), or any attribute that is derived from its parent(s).
49
50In our case, we stick to strict tree hierarchies.
51
52Use case (1), N identical clones with unique transforms, is not a problem for
53us. This is because the bulk of the data -- geometry buffers, etc. -- is stored
54in the render backend anyway. So creating a full copy of the node does not
55present a significant overhead since we need a unique transform for each of the
56clones anyway.
57
58Use case (2) does present a bit more overhead and we currently do not handle it.
59This could be handled in the future by special-casing a node such as
60`InstanceNode` that has one child subtree and N transforms (or other
61attributes), one for each unique instance of that child subtree.
62
63Therefore, to visit the use cases again:
64
651. N character clones animated in unison in different locations -> future
66 `InstanceNode`.
672. N unique character copies animated on their own -> copy the character subtree
68 (N unique skeletons; shared mesh data and textures stored in the render
69 backend.)
70
71## Reading
72
73[Panda3D Scene Graph](https://docs.panda3d.org/1.10/python/programming/scene-graph/index)
74
75[Pixar's USD](https://graphics.pixar.com/usd/release/intro.html)
diff --git a/gfx/include/gfx/scene/animation.h b/gfx/include/gfx/scene/animation.h
new file mode 100644
index 0000000..ce5d73d
--- /dev/null
+++ b/gfx/include/gfx/scene/animation.h
@@ -0,0 +1,127 @@
1#pragma once
2
3#include "node.h"
4#include "object.h"
5#include <gfx/sizes.h>
6
7#include <cstring.h>
8#include <math/defs.h>
9#include <math/mat4.h>
10#include <math/quat.h>
11#include <math/vec3.h>
12
13#include <stdbool.h>
14#include <stddef.h>
15#include <stdint.h>
16
17typedef struct Buffer Buffer;
18typedef struct SceneNode SceneNode;
19
20typedef struct Anima Anima;
21typedef struct Joint Joint;
22// TODO: Remove this when removing gfx_get_anima_skeleton().
23typedef struct Skeleton Skeleton;
24
25/// Joint descriptor.
26typedef struct JointDesc {
27 mat4 inv_bind_matrix;
28} JointDesc;
29
30/// Skeleton descriptor.
31///
32/// The last element of the joints array is the root of the hierarchy. For
33/// flexibility (glTF), the root need not be the immediate parent of the
34/// top-level joints of the skeleton, but rather, intermediate non-joint nodes
35/// are allowed between the root and the skeleton.
36typedef struct SkeletonDesc {
37 size_t num_joints; // Number of joints and matrices.
38 const SceneNode* joints[GFX_MAX_NUM_JOINTS];
39} SkeletonDesc;
40
41/// Animation interpolation mode.
42typedef enum AnimationInterpolation {
43 StepInterpolation,
44 LinearInterpolation,
45 CubicSplineInterpolation
46} AnimationInterpolation;
47
48/// The kind of transformation applied by a Channel.
49typedef enum ChannelType {
50 RotationChannel,
51 ScaleChannel,
52 TranslationChannel,
53 WeightsChannel
54} ChannelType;
55
56/// Animation keyframe descriptor.
57///
58/// The arrays should have as many entries as 'num_joints' in the SkeletonDesc.
59typedef struct KeyframeDesc {
60 R time; // Start time in [0, end animation time]
61 union {
62 vec3 translation;
63 quat rotation;
64 };
65} KeyframeDesc;
66
67/// Animation channel descriptor.
68typedef struct ChannelDesc {
69 SceneNode* target;
70 ChannelType type;
71 AnimationInterpolation interpolation;
72 size_t num_keyframes;
73 KeyframeDesc keyframes[GFX_MAX_NUM_KEYFRAMES];
74} ChannelDesc;
75
76/// Animation descriptor.
77typedef struct AnimationDesc {
78 // TODO: Replace the name with a hash for a smaller footprint and faster
79 // comparisons.
80 sstring name; // Animation name. Required for playback.
81 size_t num_channels; // Number of channels.
82 ChannelDesc channels[GFX_MAX_NUM_CHANNELS];
83} AnimationDesc;
84
85/// Anima object descriptor.
86typedef struct AnimaDesc {
87 size_t num_skeletons;
88 size_t num_animations;
89 SkeletonDesc skeletons[GFX_MAX_NUM_SKELETONS];
90 AnimationDesc animations[GFX_MAX_NUM_ANIMATIONS];
91} AnimaDesc;
92
93/// Animation play settings.
94typedef struct AnimationPlaySettings {
95 const char* name; // Animation name.
96 bool loop; // Whether to loop the animation or just play once.
97 // TODO: Add animation speed.
98} AnimationPlaySettings;
99
100/// Create a joint.
101Joint* gfx_make_joint(const JointDesc*);
102
103/// Destroy the joint.
104void gfx_destroy_joint(Joint**);
105
106/// Create an anima object.
107///
108/// The anima owns its skeletons and animations.
109Anima* gfx_make_anima(const AnimaDesc*);
110
111/// Destroy the anima.
112void gfx_destroy_anima(Anima**);
113
114/// Play an animation (sets the current animation).
115bool gfx_play_animation(Anima*, const AnimationPlaySettings*);
116
117/// Update the current animation.
118void gfx_update_animation(Anima*, R t);
119
120/// Stop the current animation.
121void gfx_stop_animation(Anima*);
122
123// TODO: Remove this, it's ugly. Things would be much simpler if scene nodes
124// are allocated in arrays. Then our descs can take indices instead of pointers,
125// and locating a node is simply a matter of indexing the array. The same is
126// true for skeletons here and other objects.
127const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i);
diff --git a/gfx/include/gfx/scene/node.h b/gfx/include/gfx/scene/node.h
index 292a91f..ab6ca12 100644
--- a/gfx/include/gfx/scene/node.h
+++ b/gfx/include/gfx/scene/node.h
@@ -1,11 +1,28 @@
1#pragma once 1#pragma once
2 2
3#include "animation.h"
3#include <math/fwd.h> 4#include <math/fwd.h>
4 5
6typedef struct Anima Anima;
7typedef struct Joint Joint;
5typedef struct Light Light; 8typedef struct Light Light;
6typedef struct SceneCamera SceneCamera; 9typedef struct SceneCamera SceneCamera;
7typedef struct SceneObject SceneObject; 10typedef struct SceneObject SceneObject;
8 11
12/// Scene node type.
13typedef enum NodeType {
14 LogicalNode,
15 AnimaNode,
16 CameraNode,
17 JointNode,
18 LightNode,
19 ObjectNode
20} NodeType;
21
22/// A node in the scene graph.
23///
24/// Scene nodes take ownership of the object they are associated with (Camera,
25/// Light, SceneObject, etc), as well as of child nodes.
9typedef struct SceneNode SceneNode; 26typedef struct SceneNode SceneNode;
10 27
11/// Create a new scene node. 28/// Create a new scene node.
@@ -14,15 +31,36 @@ typedef struct SceneNode SceneNode;
14/// as a logical and spatial construct. 31/// as a logical and spatial construct.
15SceneNode* gfx_make_node(); 32SceneNode* gfx_make_node();
16 33
34/// Create an anima node.
35SceneNode* gfx_make_anima_node(Anima*);
36
17/// Create a new camera node. 37/// Create a new camera node.
18SceneNode* gfx_make_camera_node(SceneCamera*); 38SceneNode* gfx_make_camera_node(SceneCamera*);
19 39
40/// Create a joint node.
41SceneNode* gfx_make_joint_node(Joint*);
42
20/// Create a new light node. 43/// Create a new light node.
21SceneNode* gfx_make_light_node(Light*); 44SceneNode* gfx_make_light_node(Light*);
22 45
23/// Create a new object node. 46/// Create a new object node.
24SceneNode* gfx_make_object_node(SceneObject*); 47SceneNode* gfx_make_object_node(SceneObject*);
25 48
49/// Make the node an anima node.
50void gfx_construct_anima_node(SceneNode*, Anima*);
51
52/// Make the node a camera node.
53void gfx_construct_camera_node(SceneNode*, SceneCamera*);
54
55/// Make the node a joint node.
56void gfx_construct_joint_node(SceneNode*, Joint*);
57
58/// Make the node a light node.
59void gfx_construct_light_node(SceneNode*, Light*);
60
61/// Make the node an object node.
62void gfx_construct_object_node(SceneNode*, SceneObject*);
63
26/// Recursively destroy the scene node and its children. 64/// Recursively destroy the scene node and its children.
27/// 65///
28/// The scene node and its children are removed from the scene graph. 66/// The scene node and its children are removed from the scene graph.
@@ -30,19 +68,36 @@ SceneNode* gfx_make_object_node(SceneObject*);
30/// Node resources -- cameras, lights, objects, etc. -- are also destroyed. 68/// Node resources -- cameras, lights, objects, etc. -- are also destroyed.
31void gfx_destroy_node(SceneNode**); 69void gfx_destroy_node(SceneNode**);
32 70
71/// Return the node's type.
72NodeType gfx_get_node_type(const SceneNode*);
73
74/// Get the node's anima.
75///
76/// The node must be of type AnimaNode.
77Anima* gfx_get_node_anima(const SceneNode*);
78
33/// Set the node's parent. 79/// Set the node's parent.
34/// 80///
35/// Pass in null to unwire from the existing parent, if one exists. 81/// Pass in null to unwire from the existing parent, if one exists.
36void gfx_set_node_parent(SceneNode*, SceneNode* parent); 82void gfx_set_node_parent(SceneNode*, SceneNode* parent);
37 83
38/// Set the node's transform. 84/// Set the node's (local) transform.
39void gfx_set_node_transform(SceneNode*, const mat4* transform); 85void gfx_set_node_transform(SceneNode*, const mat4* transform);
40 86
41/// Set the node's position. 87/// Set the node's position.
42void gfx_set_node_position(SceneNode*, const vec3* position); 88void gfx_set_node_position(SceneNode*, const vec3* position);
43 89
44/// Set the node's rotation. 90/// Set the node's rotation.
45void gfx_set_node_rotation(SceneNode*, const mat4* rotation); 91void gfx_set_node_rotation(SceneNode*, const quat* rotation);
92
93/// Set the node's rotation.
94void gfx_set_node_rotation_mat(SceneNode*, const mat4* rotation);
95
96/// Get the node's (local) transform.
97mat4 gfx_get_node_transform(const SceneNode*);
98
99/// Get the node's global transform.
100mat4 gfx_get_node_global_transform(const SceneNode*);
46 101
47/// Log the node's hierarchy. 102/// Log the node's hierarchy.
48void gfx_log_node_hierarchy(const SceneNode*); 103void gfx_log_node_hierarchy(const SceneNode*);
diff --git a/gfx/include/gfx/scene/object.h b/gfx/include/gfx/scene/object.h
index 69a2231..59372c5 100644
--- a/gfx/include/gfx/scene/object.h
+++ b/gfx/include/gfx/scene/object.h
@@ -2,8 +2,9 @@
2 2
3#include <math/fwd.h> 3#include <math/fwd.h>
4 4
5typedef struct Mesh Mesh; 5typedef struct Mesh Mesh;
6typedef struct SceneNode SceneNode; 6typedef struct SceneNode SceneNode;
7typedef struct Skeleton Skeleton;
7 8
8typedef struct SceneObject SceneObject; 9typedef struct SceneObject SceneObject;
9 10
@@ -24,3 +25,6 @@ void gfx_add_object_mesh(SceneObject*, Mesh*);
24 25
25/// Remove a mesh from the object. 26/// Remove a mesh from the object.
26void gfx_remove_object_mesh(SceneObject*, Mesh*); 27void gfx_remove_object_mesh(SceneObject*, Mesh*);
28
29/// Set the object's skeleton.
30void gfx_set_object_skeleton(SceneObject*, const Skeleton*);
diff --git a/gfx/include/gfx/sizes.h b/gfx/include/gfx/sizes.h
index 2ed25b4..17e7c7d 100644
--- a/gfx/include/gfx/sizes.h
+++ b/gfx/include/gfx/sizes.h
@@ -18,6 +18,24 @@
18/// Maximum number of mesh links. 18/// Maximum number of mesh links.
19#define GFX_MAX_NUM_MESH_LINKS 1024 19#define GFX_MAX_NUM_MESH_LINKS 1024
20 20
21/// Maximum number of joints per skeleton.
22#define GFX_MAX_NUM_JOINTS 96
23
24/// Maximum number of keyframes per channel.
25#define GFX_MAX_NUM_KEYFRAMES 32
26
27/// Maximum number of channels per animation.
28#define GFX_MAX_NUM_CHANNELS 128
29
30/// Maximum number of skeletons.
31#define GFX_MAX_NUM_SKELETONS 128
32
33/// Maximum number of animations.
34#define GFX_MAX_NUM_ANIMATIONS 128
35
36/// Maximum number of animas.
37#define GFX_MAX_NUM_ANIMAS 128
38
21/// Maximum number of nodes per scene. 39/// Maximum number of nodes per scene.
22#define GFX_MAX_NUM_NODES 1024 40#define GFX_MAX_NUM_NODES 1024
23 41
diff --git a/gfx/include/gfx/util/scene.h b/gfx/include/gfx/util/scene.h
index 0aad3d3..fa9304b 100644
--- a/gfx/include/gfx/util/scene.h
+++ b/gfx/include/gfx/util/scene.h
@@ -24,11 +24,12 @@ typedef struct LoadSceneCmd {
24 24
25/// Load a scene. 25/// Load a scene.
26/// 26///
27/// |root_node| is the node under which scene elements are loaded. 27/// Return a top-level node under which scene elements are rooted. |root_node|
28/// is made the parent of this top-level node.
28/// 29///
29/// |shader| is an optional shader program assigned to the loaded scene objects. 30/// |shader| is an optional shader program assigned to the loaded scene objects.
30/// If no shader is given, a Cook-Torrance shader based on the object's 31/// If no shader is given, a Cook-Torrance shader based on the object's
31/// characteristics (presence of normals, tangents, etc) is assigned. 32/// characteristics (presence of normals, tangents, etc) is assigned.
32/// 33///
33/// Currently only supports the GLTF format. 34/// Currently only supports the GLTF format.
34bool gfx_load_scene(Gfx*, SceneNode* root_node, const LoadSceneCmd*); 35SceneNode* gfx_load_scene(Gfx*, SceneNode* root_node, const LoadSceneCmd*);
diff --git a/gfx/shaders/cook_torrance.vert b/gfx/shaders/cook_torrance.vert
index 6425565..697bb0c 100644
--- a/gfx/shaders/cook_torrance.vert
+++ b/gfx/shaders/cook_torrance.vert
@@ -1,7 +1,23 @@
1precision highp float; 1precision highp float;
2 2
3uniform mat4 ModelMatrix; 3uniform mat4 ModelMatrix;
4uniform mat4 MVP; 4uniform mat4 Modelview;
5uniform mat4 Projection;
6//uniform mat4 MVP;
7#ifdef HAS_JOINTS
8// The client should pass in an appropriate value for MAX_JOINTS.
9// #define MAX_JOINTS 96
10//
11// matnxm -- n columns and m rows, different convention from math.
12// We don't need the last row of [0, 0, 0, 1], so drop it to pack the matrices
13// as tightly as possible.
14// 256 joints * 4x4 matrix * 4 bytes/float = 16.0KB
15// 256 joints * 4x3 matrix * 4 bytes/float = 12.0KB
16// 96 joints * 4x4 matrix * 4 bytes/float = 6.0KB
17// 96 joints * 4x3 matrix * 4 bytes/float = 4.5KB
18//uniform mat4x3 Joints[MAX_JOINTS];
19uniform mat4 JointMatrices[MAX_JOINTS]; // Use 4x4 for now to keep it simple.
20#endif
5 21
6layout (location = 0) in vec3 vPosition; 22layout (location = 0) in vec3 vPosition;
7#ifdef HAS_NORMALS 23#ifdef HAS_NORMALS
@@ -9,10 +25,14 @@ layout (location = 1) in vec3 vNormal;
9#endif 25#endif
10#ifdef HAS_TANGENTS 26#ifdef HAS_TANGENTS
11layout (location = 2) in vec4 vTangent; 27layout (location = 2) in vec4 vTangent;
12#endif // HAS_TANGENTS 28#endif
13#ifdef HAS_TEXCOORDS 29#ifdef HAS_TEXCOORDS
14layout (location = 3) in vec2 vTexcoord; 30layout (location = 3) in vec2 vTexcoord;
15#endif 31#endif
32#ifdef HAS_JOINTS
33layout (location = 4) in uvec4 vJoint;
34layout (location = 5) in vec4 vWeight;
35#endif
16 36
17// World-space position and normal. 37// World-space position and normal.
18out vec3 Position; 38out vec3 Position;
@@ -28,7 +48,16 @@ out vec2 Texcoord;
28 48
29void main() 49void main()
30{ 50{
31 Position = vec3(ModelMatrix * vec4(vPosition, 1.0)); 51#ifdef HAS_JOINTS
52 mat4 skinMatrix =
53 vWeight.x * JointMatrices[vJoint.x] +
54 vWeight.y * JointMatrices[vJoint.y] +
55 vWeight.z * JointMatrices[vJoint.z] +
56 vWeight.w * JointMatrices[vJoint.w];
57 Position = vec3(Modelview * skinMatrix * vec4(vPosition, 1.0));
58#else
59 Position = vec3(Modelview * vec4(vPosition, 1.0));
60#endif
32#ifdef HAS_NORMALS 61#ifdef HAS_NORMALS
33 Normal = mat3(ModelMatrix) * vNormal; 62 Normal = mat3(ModelMatrix) * vNormal;
34 //Normal = normalize(ModelMatrix * vec4(vNormal, 0.0)).xyz; 63 //Normal = normalize(ModelMatrix * vec4(vNormal, 0.0)).xyz;
@@ -39,5 +68,6 @@ void main()
39#ifdef HAS_TEXCOORDS 68#ifdef HAS_TEXCOORDS
40 Texcoord = vTexcoord; 69 Texcoord = vTexcoord;
41#endif 70#endif
42 gl_Position = MVP * vec4(vPosition, 1.0); 71 gl_Position = Projection * vec4(Position, 1.0);
72 //gl_Position = MVP * vec4(vPosition, 1.0);
43} 73}
diff --git a/gfx/src/render/constants.h b/gfx/src/render/constants.h
index b98b0ac..a6a3b94 100644
--- a/gfx/src/render/constants.h
+++ b/gfx/src/render/constants.h
@@ -1,7 +1,9 @@
1#pragma once 1#pragma once
2 2
3// Shaders vertex attribute locations must match the channels here. 3// Shaders vertex attribute locations must match the channels here.
4#define GFX_POSITION_CHANNEL 0 4#define GFX_POSITION_CHANNEL 0
5#define GFX_NORMAL_CHANNEL 1 5#define GFX_NORMAL_CHANNEL 1
6#define GFX_TANGENT_CHANNEL 2 6#define GFX_TANGENT_CHANNEL 2
7#define GFX_TEXCOORDS_CHANNEL 3 7#define GFX_TEXCOORDS_CHANNEL 3
8#define GFX_JOINTS_CHANNEL 4
9#define GFX_WEIGHTS_CHANNEL 5
diff --git a/gfx/src/render/geometry.c b/gfx/src/render/geometry.c
index d5b5a95..0df6efb 100644
--- a/gfx/src/render/geometry.c
+++ b/gfx/src/render/geometry.c
@@ -22,136 +22,154 @@ static GLenum primitive_type_to_gl(PrimitiveType type) {
22 } 22 }
23} 23}
24 24
25static const Buffer* get_or_make_buffer2d(RenderBackend* render_backend, 25/// Create a buffer for the view.
26 const BufferView2d* view) { 26///
27/// If the view already has a buffer, return the buffer. Otherwise create a
28/// buffer from the view's data.
29static const Buffer* get_or_make_buffer(
30 RenderBackend* render_backend, const BufferView* view) {
27 if (view->buffer) { 31 if (view->buffer) {
28 return view->buffer; 32 return view->buffer;
29 } else { 33 } else {
30 return gfx_make_buffer2d(render_backend, view->data, 34 return gfx_make_buffer(render_backend, view->data, view->size_bytes);
31 view->size_bytes / sizeof(vec2));
32 }
33}
34
35static const Buffer* get_or_make_buffer3d(RenderBackend* render_backend,
36 const BufferView3d* view) {
37 if (view->buffer) {
38 return view->buffer;
39 } else {
40 return gfx_make_buffer3d(render_backend, view->data,
41 view->size_bytes / sizeof(vec3));
42 } 35 }
43} 36}
44 37
45static const Buffer* get_or_make_buffer4d(RenderBackend* render_backend, 38/// Create a buffer for the view, then configure it in the VAO.
46 const BufferView4d* view) { 39static bool configure_buffer(
47 if (view->buffer) { 40 RenderBackend* render_backend, const GeometryDesc* desc,
48 return view->buffer; 41 const BufferView* view, size_t num_components, size_t component_size_bytes,
49 } else { 42 GLenum component_type, GLboolean normalized, GLuint channel,
50 return gfx_make_buffer4d(render_backend, view->data, 43 const Buffer** buffer) {
51 view->size_bytes / sizeof(vec4)); 44 assert(render_backend);
45 assert(desc);
46 assert(view);
47 assert(buffer);
48 assert(
49 desc->num_verts <=
50 view->size_bytes / (num_components * component_size_bytes));
51 *buffer = get_or_make_buffer(render_backend, view);
52 if (!(*buffer)) {
53 return false;
52 } 54 }
53} 55 assert(view->size_bytes <= (*buffer)->size_bytes);
54 56 glBindBuffer(GL_ARRAY_BUFFER, (*buffer)->vbo);
55static const Buffer* get_or_make_buffer_idx(RenderBackend* render_backend, 57 glEnableVertexAttribArray(channel);
56 const BufferViewIdx* view) { 58 if ((component_type == GL_FLOAT) || normalized) {
57 if (view->buffer) { 59 glVertexAttribPointer(
58 return view->buffer; 60 channel, num_components, component_type, normalized, view->stride_bytes,
61 (const void*)view->offset_bytes);
59 } else { 62 } else {
60 return gfx_make_buffer(render_backend, view->data, view->size_bytes); 63 assert(!normalized);
64 assert(
65 (component_type == GL_BYTE) || (component_type == GL_UNSIGNED_BYTE) ||
66 (component_type == GL_SHORT) || (component_type == GL_UNSIGNED_SHORT) ||
67 (component_type == GL_INT) || component_type == GL_UNSIGNED_INT);
68 glVertexAttribIPointer(
69 channel, num_components, component_type, view->stride_bytes,
70 (const void*)view->offset_bytes);
61 } 71 }
72 return true;
62} 73}
63 74
64bool gfx_init_geometry(Geometry* geometry, RenderBackend* render_backend, 75bool gfx_init_geometry(
65 const GeometryDesc* desc) { 76 Geometry* geometry, RenderBackend* render_backend,
77 const GeometryDesc* desc) {
66 assert(geometry); 78 assert(geometry);
67 assert(render_backend); 79 assert(render_backend);
68 assert(desc); 80 assert(desc);
69 assert(view_is_populated(desc->positions3d) || 81 assert(
70 view_is_populated(desc->positions2d)); 82 view_is_populated(desc->positions3d) ||
83 view_is_populated(desc->positions2d));
71 assert(desc->num_verts > 0); 84 assert(desc->num_verts > 0);
72 85
73 geometry->mode = primitive_type_to_gl(desc->type); 86 geometry->mode = primitive_type_to_gl(desc->type);
74 geometry->num_verts = desc->num_verts; 87 geometry->num_verts = desc->num_verts;
75 geometry->num_indices = desc->num_indices; 88 geometry->num_indices = desc->num_indices;
76 geometry->indices_offset_bytes = desc->indices.offset_bytes; 89 geometry->indices_offset_bytes = desc->indices.offset_bytes;
77 90
78 glGenVertexArrays(1, &geometry->vao); 91 glGenVertexArrays(1, &geometry->vao);
79 glBindVertexArray(geometry->vao); 92 glBindVertexArray(geometry->vao);
80 93
94 bool result = true;
95
81 if (view_is_populated(desc->positions3d)) { 96 if (view_is_populated(desc->positions3d)) {
82 assert(desc->num_verts <= desc->positions3d.size_bytes / sizeof(vec3)); 97 result = result &&
83 geometry->positions = 98 configure_buffer(
84 get_or_make_buffer3d(render_backend, &desc->positions3d); 99 render_backend, desc, (const BufferView*)&desc->positions3d, 3,
85 if (!geometry->positions) { 100 sizeof(float), GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL,
86 gfx_del_geometry(geometry); 101 &geometry->positions);
87 return false;
88 }
89 assert(desc->positions3d.size_bytes <= geometry->positions->size_bytes);
90 glBindBuffer(GL_ARRAY_BUFFER, geometry->positions->vbo);
91 glEnableVertexAttribArray(GFX_POSITION_CHANNEL);
92 glVertexAttribPointer(GFX_POSITION_CHANNEL, 3, GL_FLOAT, GL_FALSE,
93 desc->positions3d.stride_bytes,
94 (const void*)desc->positions3d.offset_bytes);
95 } else if (view_is_populated(desc->positions2d)) { 102 } else if (view_is_populated(desc->positions2d)) {
96 assert(desc->num_verts <= desc->positions2d.size_bytes / sizeof(vec2)); 103 result = result &&
97 geometry->positions = 104 configure_buffer(
98 get_or_make_buffer2d(render_backend, &desc->positions2d); 105 render_backend, desc, (const BufferView*)&desc->positions2d, 2,
99 if (!geometry->positions) { 106 sizeof(float), GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL,
100 gfx_del_geometry(geometry); 107 &geometry->positions);
101 return false;
102 }
103 assert(desc->positions2d.size_bytes <= geometry->positions->size_bytes);
104 glBindBuffer(GL_ARRAY_BUFFER, geometry->positions->vbo);
105 glEnableVertexAttribArray(GFX_POSITION_CHANNEL);
106 glVertexAttribPointer(GFX_POSITION_CHANNEL, 2, GL_FLOAT, GL_FALSE,
107 desc->positions2d.stride_bytes,
108 (const void*)desc->positions2d.offset_bytes);
109 } 108 }
110 109
111 if (view_is_populated(desc->normals)) { 110 if (view_is_populated(desc->normals)) {
112 assert(desc->num_verts <= desc->normals.size_bytes / sizeof(vec3)); 111 result =
113 geometry->normals = get_or_make_buffer3d(render_backend, &desc->normals); 112 result && configure_buffer(
114 if (!geometry->normals) { 113 render_backend, desc, (const BufferView*)&desc->normals,
115 gfx_del_geometry(geometry); 114 3, sizeof(float), GL_FLOAT, GL_FALSE, GFX_NORMAL_CHANNEL,
116 return false; 115 &geometry->normals);
117 }
118 assert(desc->normals.size_bytes <= geometry->normals->size_bytes);
119 glBindBuffer(GL_ARRAY_BUFFER, geometry->normals->vbo);
120 glEnableVertexAttribArray(GFX_NORMAL_CHANNEL);
121 glVertexAttribPointer(GFX_NORMAL_CHANNEL, 3, GL_FLOAT, GL_FALSE,
122 desc->normals.stride_bytes,
123 (const void*)desc->normals.offset_bytes);
124 } 116 }
125 117
126 if (view_is_populated(desc->tangents)) { 118 if (view_is_populated(desc->tangents)) {
127 assert(desc->num_verts <= desc->tangents.size_bytes / sizeof(vec3)); 119 result =
128 geometry->tangents = get_or_make_buffer4d(render_backend, &desc->tangents); 120 result && configure_buffer(
129 if (!geometry->tangents) { 121 render_backend, desc, (const BufferView*)&desc->tangents,
130 gfx_del_geometry(geometry); 122 4, sizeof(float), GL_FLOAT, GL_FALSE, GFX_TANGENT_CHANNEL,
131 return false; 123 &geometry->tangents);
132 }
133 assert(desc->tangents.size_bytes <= geometry->tangents->size_bytes);
134 glBindBuffer(GL_ARRAY_BUFFER, geometry->tangents->vbo);
135 glEnableVertexAttribArray(GFX_TANGENT_CHANNEL);
136 glVertexAttribPointer(GFX_TANGENT_CHANNEL, 4, GL_FLOAT, GL_FALSE,
137 desc->tangents.stride_bytes,
138 (const void*)desc->tangents.offset_bytes);
139 } 124 }
140 125
141 if (view_is_populated(desc->texcoords)) { 126 if (view_is_populated(desc->texcoords)) {
142 assert(desc->num_verts <= desc->texcoords.size_bytes / sizeof(vec2)); 127 result =
143 geometry->texcoords = 128 result && configure_buffer(
144 get_or_make_buffer2d(render_backend, &desc->texcoords); 129 render_backend, desc, (const BufferView*)&desc->texcoords,
145 if (!geometry->texcoords) { 130 2, sizeof(float), GL_FLOAT, GL_FALSE,
146 gfx_del_geometry(geometry); 131 GFX_TEXCOORDS_CHANNEL, &geometry->texcoords);
147 return false; 132 }
148 } 133
149 assert(desc->texcoords.size_bytes <= geometry->texcoords->size_bytes); 134 if (view_is_populated(desc->joints.u8)) {
150 glBindBuffer(GL_ARRAY_BUFFER, geometry->texcoords->vbo); 135 result =
151 glEnableVertexAttribArray(GFX_TEXCOORDS_CHANNEL); 136 result && configure_buffer(
152 glVertexAttribPointer(GFX_TEXCOORDS_CHANNEL, 2, GL_FLOAT, GL_FALSE, 137 render_backend, desc, (const BufferView*)&desc->joints.u8,
153 desc->texcoords.stride_bytes, 138 4, sizeof(uint8_t), GL_UNSIGNED_BYTE, GL_FALSE,
154 (const void*)desc->texcoords.offset_bytes); 139 GFX_JOINTS_CHANNEL, &geometry->joints);
140 } else if (view_is_populated(desc->joints.u16)) {
141 result = result &&
142 configure_buffer(
143 render_backend, desc, (const BufferView*)&desc->joints.u16, 4,
144 sizeof(uint16_t), GL_UNSIGNED_SHORT, GL_FALSE,
145 GFX_JOINTS_CHANNEL, &geometry->joints);
146 }
147
148 // If weights are given as unsigned integers, then they are normalized when
149 // read by the shader.
150 if (view_is_populated(desc->weights.u8)) {
151 result = result &&
152 configure_buffer(
153 render_backend, desc, (const BufferView*)&desc->weights.u8, 4,
154 sizeof(uint8_t), GL_UNSIGNED_BYTE, GL_TRUE,
155 GFX_WEIGHTS_CHANNEL, &geometry->weights);
156 } else if (view_is_populated(desc->weights.u16)) {
157 result = result &&
158 configure_buffer(
159 render_backend, desc, (const BufferView*)&desc->weights.u16, 4,
160 sizeof(uint16_t), GL_UNSIGNED_SHORT, GL_TRUE,
161 GFX_WEIGHTS_CHANNEL, &geometry->weights);
162 } else if (view_is_populated(desc->weights.floats)) {
163 result = result &&
164 configure_buffer(
165 render_backend, desc, (const BufferView*)&desc->weights.floats,
166 4, sizeof(float), GL_FLOAT, GL_FALSE, GFX_WEIGHTS_CHANNEL,
167 &geometry->weights);
168 }
169
170 if (!result) {
171 gfx_del_geometry(geometry);
172 return false;
155 } 173 }
156 174
157 glBindBuffer(GL_ARRAY_BUFFER, 0); 175 glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -159,7 +177,8 @@ bool gfx_init_geometry(Geometry* geometry, RenderBackend* render_backend,
159 if (view_is_populated(desc->indices)) { 177 if (view_is_populated(desc->indices)) {
160 assert(desc->num_indices > 0); 178 assert(desc->num_indices > 0);
161 assert(desc->num_indices <= desc->indices.size_bytes / sizeof(VertexIndex)); 179 assert(desc->num_indices <= desc->indices.size_bytes / sizeof(VertexIndex));
162 geometry->indices = get_or_make_buffer_idx(render_backend, &desc->indices); 180 geometry->indices =
181 get_or_make_buffer(render_backend, (const BufferView*)&desc->indices);
163 if (!geometry->indices) { 182 if (!geometry->indices) {
164 gfx_del_geometry(geometry); 183 gfx_del_geometry(geometry);
165 return false; 184 return false;
@@ -186,8 +205,9 @@ void gfx_render_geometry(const Geometry* geometry) {
186 glBindVertexArray(geometry->vao); 205 glBindVertexArray(geometry->vao);
187 if (geometry->indices) { 206 if (geometry->indices) {
188 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->indices->vbo); 207 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->indices->vbo);
189 glDrawElements(geometry->mode, geometry->num_indices, GL_UNSIGNED_SHORT, 208 glDrawElements(
190 (const void*)geometry->indices_offset_bytes); 209 geometry->mode, geometry->num_indices, GL_UNSIGNED_SHORT,
210 (const void*)geometry->indices_offset_bytes);
191 } else { 211 } else {
192 glDrawArrays(geometry->mode, 0, geometry->num_verts); 212 glDrawArrays(geometry->mode, 0, geometry->num_verts);
193 } 213 }
diff --git a/gfx/src/render/geometry.h b/gfx/src/render/geometry.h
index a6f7e05..38e716b 100644
--- a/gfx/src/render/geometry.h
+++ b/gfx/src/render/geometry.h
@@ -13,11 +13,11 @@
13/// the renderer assumes ownership of all rendering resources, which simplifies 13/// the renderer assumes ownership of all rendering resources, which simplifies
14/// their management. 14/// their management.
15typedef struct Geometry { 15typedef struct Geometry {
16 GLuint vao; 16 GLuint vao;
17 GLenum mode; 17 GLenum mode;
18 VertexCount num_verts; 18 VertexCount num_verts;
19 size_t num_indices; 19 size_t num_indices;
20 size_t indices_offset_bytes; 20 size_t indices_offset_bytes;
21 // Buffer pointers are no longer needed once the VAO is created, but we store 21 // Buffer pointers are no longer needed once the VAO is created, but we store
22 // them here so that we can keep track of what Geometry objects use what 22 // them here so that we can keep track of what Geometry objects use what
23 // Buffer objects. 23 // Buffer objects.
@@ -25,6 +25,8 @@ typedef struct Geometry {
25 const Buffer* normals; 25 const Buffer* normals;
26 const Buffer* tangents; 26 const Buffer* tangents;
27 const Buffer* texcoords; 27 const Buffer* texcoords;
28 const Buffer* joints;
29 const Buffer* weights;
28 const Buffer* indices; 30 const Buffer* indices;
29} Geometry; 31} Geometry;
30 32
diff --git a/gfx/src/render/render_backend.c b/gfx/src/render/render_backend.c
index b3dc805..fed906b 100644
--- a/gfx/src/render/render_backend.c
+++ b/gfx/src/render/render_backend.c
@@ -112,44 +112,32 @@ Buffer* gfx_make_buffer(
112 112
113Buffer* gfx_make_buffer2d( 113Buffer* gfx_make_buffer2d(
114 RenderBackend* render_backend, const vec2* verts, size_t count) { 114 RenderBackend* render_backend, const vec2* verts, size_t count) {
115 assert(render_backend); 115 return gfx_make_buffer(
116 Buffer* buffer = mempool_alloc(&render_backend->buffers); 116 render_backend, (const void*)verts, count * sizeof(verts));
117 if (!buffer) {
118 return 0;
119 }
120 if (!gfx_init_buffer_2d(buffer, verts, count)) {
121 mempool_free(&render_backend->buffers, &buffer);
122 return 0;
123 }
124 return buffer;
125} 117}
126 118
127Buffer* gfx_make_buffer3d( 119Buffer* gfx_make_buffer3d(
128 RenderBackend* render_backend, const vec3* verts, size_t count) { 120 RenderBackend* render_backend, const vec3* verts, size_t count) {
129 assert(render_backend); 121 return gfx_make_buffer(
130 Buffer* buffer = mempool_alloc(&render_backend->buffers); 122 render_backend, (const void*)verts, count * sizeof(verts));
131 if (!buffer) {
132 return 0;
133 }
134 if (!gfx_init_buffer_3d(buffer, verts, count)) {
135 mempool_free(&render_backend->buffers, &buffer);
136 return 0;
137 }
138 return buffer;
139} 123}
140 124
141Buffer* gfx_make_buffer4d( 125Buffer* gfx_make_buffer4d(
142 RenderBackend* render_backend, const vec4* verts, size_t count) { 126 RenderBackend* render_backend, const vec4* verts, size_t count) {
143 assert(render_backend); 127 return gfx_make_buffer(
144 Buffer* buffer = mempool_alloc(&render_backend->buffers); 128 render_backend, (const void*)verts, count * sizeof(verts));
145 if (!buffer) { 129}
146 return 0; 130
147 } 131Buffer* gfx_make_bufferu8(
148 if (!gfx_init_buffer_4d(buffer, verts, count)) { 132 RenderBackend* render_backend, const uint8_t* vals, size_t count) {
149 mempool_free(&render_backend->buffers, &buffer); 133 return gfx_make_buffer(
150 return 0; 134 render_backend, (const void*)vals, count * sizeof(uint8_t));
151 } 135}
152 return buffer; 136
137Buffer* gfx_make_bufferu16(
138 RenderBackend* render_backend, const uint16_t* vals, size_t count) {
139 return gfx_make_buffer(
140 render_backend, (const void*)vals, count * sizeof(uint16_t));
153} 141}
154 142
155void gfx_destroy_buffer(RenderBackend* render_backend, Buffer** buffer) { 143void gfx_destroy_buffer(RenderBackend* render_backend, Buffer** buffer) {
diff --git a/gfx/src/render/render_backend_impl.h b/gfx/src/render/render_backend_impl.h
index 6d13367..9e552c2 100644
--- a/gfx/src/render/render_backend_impl.h
+++ b/gfx/src/render/render_backend_impl.h
@@ -29,13 +29,13 @@ typedef struct {
29typedef struct RenderBackend { 29typedef struct RenderBackend {
30 Viewport viewport; 30 Viewport viewport;
31 // mempools for render-specific objects: textures, geometry, etc. 31 // mempools for render-specific objects: textures, geometry, etc.
32 buffer_pool buffers; 32 buffer_pool buffers;
33 framebuffer_pool framebuffers; 33 framebuffer_pool framebuffers;
34 geometry_pool geometries; 34 geometry_pool geometries;
35 renderbuffer_pool renderbuffers; 35 renderbuffer_pool renderbuffers;
36 shader_pool shaders; 36 shader_pool shaders;
37 shader_program_pool shader_programs; 37 shader_program_pool shader_programs;
38 texture_pool textures; 38 texture_pool textures;
39} RenderBackend; 39} RenderBackend;
40 40
41/// Create a new render backend. 41/// Create a new render backend.
diff --git a/gfx/src/render/shader_program.c b/gfx/src/render/shader_program.c
index 05cfd5a..d80ee4f 100644
--- a/gfx/src/render/shader_program.c
+++ b/gfx/src/render/shader_program.c
@@ -41,8 +41,8 @@ static GLuint create_program(GLuint vertex_shader, GLuint fragment_shader) {
41 return prog; 41 return prog;
42} 42}
43 43
44bool gfx_build_shader_program(ShaderProgram* prog, 44bool gfx_build_shader_program(
45 const ShaderProgramDesc* desc) { 45 ShaderProgram* prog, const ShaderProgramDesc* desc) {
46 assert(prog); 46 assert(prog);
47 assert(desc); 47 assert(desc);
48 prog->id = create_program(desc->vertex_shader->id, desc->fragment_shader->id); 48 prog->id = create_program(desc->vertex_shader->id, desc->fragment_shader->id);
@@ -70,8 +70,8 @@ void gfx_deactivate_shader_program(const ShaderProgram* prog) {
70 ASSERT_GL; 70 ASSERT_GL;
71} 71}
72 72
73static void set_texture_uniform(GLuint prog, const char* name, int texture_unit, 73static void set_texture_uniform(
74 const Texture* texture) { 74 GLuint prog, const char* name, int texture_unit, const Texture* texture) {
75 assert(prog != 0); 75 assert(prog != 0);
76 assert(name); 76 assert(name);
77 assert(texture); 77 assert(texture);
@@ -83,13 +83,14 @@ static void set_texture_uniform(GLuint prog, const char* name, int texture_unit,
83 } 83 }
84} 84}
85 85
86static void set_mat4_uniform(GLuint prog, const char* name, const mat4* mat) { 86static void set_mat4_uniform(
87 GLuint prog, const char* name, const mat4* mats, size_t count) {
87 assert(prog != 0); 88 assert(prog != 0);
88 assert(name); 89 assert(name);
89 assert(mat); 90 assert(mats);
90 const GLint location = glGetUniformLocation(prog, name); 91 const GLint location = glGetUniformLocation(prog, name);
91 if (location >= 0) { 92 if (location >= 0) {
92 glUniformMatrix4fv(location, 1, GL_FALSE, (const float*)mat); 93 glUniformMatrix4fv(location, count, GL_FALSE, (const float*)mats);
93 } 94 }
94} 95}
95 96
@@ -127,12 +128,13 @@ void gfx_apply_uniforms(const ShaderProgram* prog) {
127 const ShaderUniform* uniform = &prog->uniforms[i]; 128 const ShaderUniform* uniform = &prog->uniforms[i];
128 switch (uniform->type) { 129 switch (uniform->type) {
129 case UniformTexture: 130 case UniformTexture:
130 set_texture_uniform(prog->id, uniform->name.str, next_texture_unit, 131 set_texture_uniform(
131 uniform->value.texture); 132 prog->id, uniform->name.str, next_texture_unit,
133 uniform->value.texture);
132 next_texture_unit++; 134 next_texture_unit++;
133 break; 135 break;
134 case UniformMat4: 136 case UniformMat4:
135 set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4); 137 set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4, 1);
136 break; 138 break;
137 case UniformVec3: 139 case UniformVec3:
138 set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3); 140 set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3);
@@ -143,17 +145,19 @@ void gfx_apply_uniforms(const ShaderProgram* prog) {
143 case UniformFloat: 145 case UniformFloat:
144 set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar); 146 set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar);
145 break; 147 break;
148 case UniformMat4Array:
149 set_mat4_uniform(
150 prog->id, uniform->name.str, uniform->value.array.values,
151 uniform->value.array.count);
152 break;
146 } 153 }
147 } 154 }
148} 155}
149 156
150// Get the ShaderUniform object by name from the shader program if it already 157// Get the ShaderUniform object by name from the shader program if it already
151// exists, or allocate a new one otherwise. 158// exists, or allocate a new one otherwise.
152// 159static ShaderUniform* get_or_allocate_uniform(
153// If there is no more space for a new uniform, this function logs an error and 160 ShaderProgram* prog, const char* name) {
154// returns 0.
155static ShaderUniform* get_or_allocate_uniform(ShaderProgram* prog,
156 const char* name) {
157 assert(prog); 161 assert(prog);
158 assert(name); 162 assert(name);
159 // First search for the uniform in the list. 163 // First search for the uniform in the list.
@@ -167,6 +171,7 @@ static ShaderUniform* get_or_allocate_uniform(ShaderProgram* prog,
167 if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) { 171 if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) {
168 LOGE("Exceeded the maximum number of uniforms per shader. Please increase " 172 LOGE("Exceeded the maximum number of uniforms per shader. Please increase "
169 "this value."); 173 "this value.");
174 assert(false);
170 return 0; 175 return 0;
171 } 176 }
172 ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms]; 177 ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms];
@@ -177,8 +182,8 @@ static ShaderUniform* get_or_allocate_uniform(ShaderProgram* prog,
177// The functions below save the value of a uniform in the shader program. If the 182// The functions below save the value of a uniform in the shader program. If the
178// uniform does not even exist, then there is no need to store the value. 183// uniform does not even exist, then there is no need to store the value.
179 184
180void gfx_set_texture_uniform(ShaderProgram* prog, const char* name, 185void gfx_set_texture_uniform(
181 const Texture* texture) { 186 ShaderProgram* prog, const char* name, const Texture* texture) {
182 assert(prog); 187 assert(prog);
183 assert(name); 188 assert(name);
184 assert(texture); 189 assert(texture);
@@ -187,16 +192,14 @@ void gfx_set_texture_uniform(ShaderProgram* prog, const char* name,
187 return; 192 return;
188 } 193 }
189 ShaderUniform* uniform = get_or_allocate_uniform(prog, name); 194 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
190 if (!uniform) { 195 assert(uniform);
191 return; 196 uniform->name = sstring_make(name);
192 } 197 uniform->type = UniformTexture;
193 uniform->name = sstring_make(name);
194 uniform->type = UniformTexture;
195 uniform->value.texture = texture; 198 uniform->value.texture = texture;
196} 199}
197 200
198void gfx_set_mat4_uniform(ShaderProgram* prog, const char* name, 201void gfx_set_mat4_uniform(
199 const mat4* mat) { 202 ShaderProgram* prog, const char* name, const mat4* mat) {
200 assert(prog); 203 assert(prog);
201 assert(name); 204 assert(name);
202 assert(mat); 205 assert(mat);
@@ -205,11 +208,9 @@ void gfx_set_mat4_uniform(ShaderProgram* prog, const char* name,
205 return; 208 return;
206 } 209 }
207 ShaderUniform* uniform = get_or_allocate_uniform(prog, name); 210 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
208 if (!uniform) { 211 assert(uniform);
209 return; 212 uniform->name = sstring_make(name);
210 } 213 uniform->type = UniformMat4;
211 uniform->name = sstring_make(name);
212 uniform->type = UniformMat4;
213 uniform->value.mat4 = *mat; 214 uniform->value.mat4 = *mat;
214} 215}
215 216
@@ -221,11 +222,9 @@ void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) {
221 return; 222 return;
222 } 223 }
223 ShaderUniform* uniform = get_or_allocate_uniform(prog, name); 224 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
224 if (!uniform) { 225 assert(uniform);
225 return; 226 uniform->name = sstring_make(name);
226 } 227 uniform->type = UniformVec3;
227 uniform->name = sstring_make(name);
228 uniform->type = UniformVec3;
229 uniform->value.vec3 = value; 228 uniform->value.vec3 = value;
230} 229}
231 230
@@ -237,11 +236,9 @@ void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) {
237 return; 236 return;
238 } 237 }
239 ShaderUniform* uniform = get_or_allocate_uniform(prog, name); 238 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
240 if (!uniform) { 239 assert(uniform);
241 return; 240 uniform->name = sstring_make(name);
242 } 241 uniform->type = UniformVec4;
243 uniform->name = sstring_make(name);
244 uniform->type = UniformVec4;
245 uniform->value.vec4 = value; 242 uniform->value.vec4 = value;
246} 243}
247 244
@@ -255,10 +252,25 @@ void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) {
255 return; 252 return;
256 } 253 }
257 ShaderUniform* uniform = get_or_allocate_uniform(prog, name); 254 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
258 if (!uniform) { 255 assert(uniform);
256 uniform->name = sstring_make(name);
257 uniform->type = UniformFloat;
258 uniform->value.scalar = value;
259}
260
261void gfx_set_mat4_array_uniform(
262 ShaderProgram* prog, const char* name, const mat4* mats, size_t count) {
263 assert(prog);
264 assert(name);
265 assert(mats);
266 const GLint location = glGetUniformLocation(prog->id, name);
267 if (location < 0) {
259 return; 268 return;
260 } 269 }
261 uniform->name = sstring_make(name); 270 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
262 uniform->type = UniformFloat; 271 assert(uniform);
263 uniform->value.scalar = value; 272 uniform->name = sstring_make(name);
273 uniform->type = UniformMat4Array;
274 uniform->value.array.count = count;
275 uniform->value.array.values = mats;
264} 276}
diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c
index 8b101db..47f1344 100644
--- a/gfx/src/renderer/renderer.c
+++ b/gfx/src/renderer/renderer.c
@@ -119,8 +119,31 @@ typedef struct RenderState {
119 Light* environment_light; 119 Light* environment_light;
120 const float fovy; 120 const float fovy;
121 const float aspect; 121 const float aspect;
122 size_t num_joints;
123 mat4 joint_matrices[GFX_MAX_NUM_JOINTS];
122} RenderState; 124} RenderState;
123 125
126/// Load joint matrices into the render state.
127static void load_skeleton(RenderState* state, skeleton_idx skeleton_index) {
128 assert(state);
129 assert(skeleton_index.val != 0);
130
131 const Skeleton* skeleton = mem_get_skeleton(skeleton_index);
132 assert(skeleton);
133 assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS);
134
135 for (size_t i = 0; i < skeleton->num_joints; ++i) {
136 const SceneNode* node = mem_get_node(skeleton->joints[i]);
137 assert(node);
138 assert(node->type == JointNode);
139 const Joint* joint = mem_get_joint(node->joint);
140 assert(joint);
141 state->joint_matrices[i] = joint->joint_matrix;
142 }
143 state->num_joints = skeleton->num_joints;
144}
145
146/// Draw the scene recursively.
124static void draw_recursively( 147static void draw_recursively(
125 RenderState* state, mat4 parent_transform, const SceneNode* node) { 148 RenderState* state, mat4 parent_transform, const SceneNode* node) {
126 assert(state); 149 assert(state);
@@ -148,6 +171,10 @@ static void draw_recursively(
148 const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix); 171 const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix);
149 const mat4 mvp = mat4_mul(*state->projection, modelview); 172 const mat4 mvp = mat4_mul(*state->projection, modelview);
150 173
174 if (object->skeleton.val) {
175 load_skeleton(state, object->skeleton);
176 }
177
151 for (mesh_link_idx mesh_link_index = object->mesh_link; 178 for (mesh_link_idx mesh_link_index = object->mesh_link;
152 mesh_link_index.val;) { 179 mesh_link_index.val;) {
153 const MeshLink* mesh_link = mem_get_mesh_link(mesh_link_index); 180 const MeshLink* mesh_link = mem_get_mesh_link(mesh_link_index);
@@ -170,6 +197,10 @@ static void draw_recursively(
170 gfx_set_float_uniform(shader, "Fovy", state->fovy); 197 gfx_set_float_uniform(shader, "Fovy", state->fovy);
171 gfx_set_float_uniform(shader, "Aspect", state->aspect); 198 gfx_set_float_uniform(shader, "Aspect", state->aspect);
172 gfx_set_vec3_uniform(shader, "CameraPosition", state->camera->spatial.p); 199 gfx_set_vec3_uniform(shader, "CameraPosition", state->camera->spatial.p);
200 if (state->num_joints > 0) {
201 gfx_set_mat4_array_uniform(
202 shader, "JointMatrices", state->joint_matrices, state->num_joints);
203 }
173 // Apply lights. 204 // Apply lights.
174 if (state->environment_light) { 205 if (state->environment_light) {
175 const EnvironmentLight* light = &state->environment_light->environment; 206 const EnvironmentLight* light = &state->environment_light->environment;
@@ -192,6 +223,9 @@ static void draw_recursively(
192 gfx_apply_uniforms(shader); 223 gfx_apply_uniforms(shader);
193 gfx_render_geometry(mesh->geometry); 224 gfx_render_geometry(mesh->geometry);
194 } 225 }
226
227 // Reset state for next object.
228 state->num_joints = 0;
195 } 229 }
196 230
197 // Render children recursively. 231 // Render children recursively.
diff --git a/gfx/src/scene/animation.c b/gfx/src/scene/animation.c
new file mode 100644
index 0000000..91d3e08
--- /dev/null
+++ b/gfx/src/scene/animation.c
@@ -0,0 +1,403 @@
1#include "animation_impl.h"
2
3#include "node_impl.h"
4#include "scene_memory.h"
5
6// #include <log/log.h> // Debugging.
7
8static const R PLAYBACK_UNINITIALIZED = -1;
9
10Joint* gfx_make_joint(const JointDesc* desc) {
11 assert(desc);
12
13 // The joint matrix needs to be initialized so that meshes look right even if
14 // no animation is played. Initializing joint matrices to the identity makes
15 // meshes appear in their bind pose.
16 Joint* joint = mem_alloc_joint();
17 assert(joint);
18 joint->inv_bind_matrix = desc->inv_bind_matrix;
19 joint->joint_matrix = mat4_id();
20 return joint;
21}
22
23void gfx_destroy_joint(Joint** joint) {
24 assert(joint);
25 assert(*joint);
26
27 if ((*joint)->parent.val) {
28 gfx_del_node((*joint)->parent);
29 }
30 mem_free_joint(joint);
31}
32
33static Skeleton* make_skeleton(const SkeletonDesc* desc) {
34 assert(desc);
35 assert(desc->num_joints < GFX_MAX_NUM_JOINTS);
36
37 Skeleton* skeleton = mem_alloc_skeleton();
38 assert(skeleton);
39 skeleton->num_joints = desc->num_joints;
40 for (size_t i = 0; i < desc->num_joints; ++i) {
41 skeleton->joints[i] = mem_get_node_index(desc->joints[i]);
42 }
43 return skeleton;
44}
45
46static Animation* make_animation(const AnimationDesc* desc) {
47 assert(desc);
48 assert(desc->num_channels < GFX_MAX_NUM_CHANNELS);
49
50 Animation* animation = mem_alloc_animation();
51 assert(animation);
52 animation->name = desc->name;
53 animation->duration = 0;
54 animation->num_channels = desc->num_channels;
55 R start_time = 0;
56 R end_time = 0;
57 for (size_t c = 0; c < desc->num_channels; ++c) {
58 const ChannelDesc* channel_desc = &desc->channels[c];
59 Channel* channel = &animation->channels[c];
60
61 channel->target = mem_get_node_index(channel_desc->target);
62 channel->type = channel_desc->type;
63 channel->interpolation = channel_desc->interpolation;
64 channel->num_keyframes = channel_desc->num_keyframes;
65 assert(channel_desc->num_keyframes < GFX_MAX_NUM_KEYFRAMES);
66
67 for (size_t k = 0; k < channel_desc->num_keyframes; ++k) {
68 const KeyframeDesc* keyframe_desc = &channel_desc->keyframes[k];
69 Keyframe* keyframe = &channel->keyframes[k];
70
71 keyframe->time = keyframe_desc->time;
72 keyframe->translation = keyframe_desc->translation;
73 keyframe->rotation = keyframe_desc->rotation;
74
75 start_time = keyframe->time < start_time ? keyframe->time : start_time;
76 end_time = keyframe->time > end_time ? keyframe->time : end_time;
77 }
78 }
79 // LOGD("Animation start/end: %f / %f", start_time, end_time);
80 animation->duration = end_time - start_time;
81 assert(animation->duration >= 0);
82 return animation;
83}
84
85Anima* gfx_make_anima(const AnimaDesc* desc) {
86 assert(desc);
87
88 Anima* anima = mem_alloc_anima();
89 if (!anima) {
90 return 0;
91 }
92
93 // Wire the skeletons in the same order they are given in the descriptor.
94 Skeleton* last_skeleton = 0;
95 for (size_t i = 0; i < desc->num_skeletons; ++i) {
96 Skeleton* skeleton = make_skeleton(&desc->skeletons[i]);
97 // TODO: Here and everywhere else, I think it would simplify the code
98 // greatly to make mem_alloc_xyz() fail if the allocation fails. At that
99 // point the user should just bump their memory limits.
100 assert(skeleton);
101 const skeleton_idx skeleton_index = mem_get_skeleton_index(skeleton);
102 if (last_skeleton == 0) {
103 anima->skeleton = skeleton_index;
104 } else {
105 last_skeleton->next = skeleton_index;
106 }
107 last_skeleton = skeleton;
108 }
109
110 // Wire the animations in the same order they are given in the descriptor.
111 Animation* last_animation = 0;
112 for (size_t i = 0; i < desc->num_animations; ++i) {
113 Animation* animation = make_animation(&desc->animations[i]);
114 assert(animation);
115 const animation_idx animation_index = mem_get_animation_index(animation);
116 if (last_animation == 0) {
117 anima->animation = animation_index;
118 } else {
119 last_animation->next = animation_index;
120 }
121 last_animation = animation;
122 }
123
124 return anima;
125}
126
127void gfx_destroy_anima(Anima** anima) {
128 assert(anima);
129 assert(*anima);
130
131 for (skeleton_idx i = (*anima)->skeleton; i.val != 0;) {
132 Skeleton* skeleton = mem_get_skeleton(i);
133 i = skeleton->next;
134 mem_free_skeleton(&skeleton);
135 }
136
137 for (animation_idx i = (*anima)->animation; i.val != 0;) {
138 Animation* animation = mem_get_animation(i);
139 i = animation->next;
140 mem_free_animation(&animation);
141 }
142
143 if ((*anima)->parent.val) {
144 gfx_del_node((*anima)->parent);
145 }
146
147 mem_free_anima(anima);
148}
149
150static Animation* find_animation(animation_idx index, const char* name) {
151 assert(name);
152
153 while (index.val != 0) {
154 Animation* animation = mem_get_animation(index);
155 assert(animation);
156 if (sstring_eq_cstr(animation->name, name)) {
157 // LOGD(
158 // "Found animation at index %u, %s - %s", index.val,
159 // sstring_cstr(&animation->name), name);
160 // LOGD("Animation has duration %f", animation->duration);
161 return animation;
162 }
163 index = animation->next;
164 }
165
166 return 0;
167}
168
169bool gfx_play_animation(Anima* anima, const AnimationPlaySettings* settings) {
170 assert(anima);
171 assert(settings);
172
173 // TODO: Should we animate at t=0 here to kickstart the animation? Otherwise
174 // the client is forced to call gfx_update_animation() to do this.
175 Animation* animation = find_animation(anima->animation, settings->name);
176 if (!animation) {
177 return false;
178 }
179 // Playback initialized on first call to update().
180 AnimationState* state = &anima->state;
181 state->start_time = PLAYBACK_UNINITIALIZED;
182 state->animation = mem_get_animation_index(animation);
183 state->loop = settings->loop;
184 return true;
185}
186
187static void find_keyframes(const Channel* channel, R t, int* prev, int* next) {
188 assert(channel);
189 assert(prev);
190 assert(next);
191 *prev = -1;
192 *next = 0;
193 while (((*next + 1) < (int)channel->num_keyframes) &&
194 (t >= channel->keyframes[*next + 1].time)) {
195 (*prev)++;
196 (*next)++;
197 }
198}
199
200static R normalize_time(R a, R b, R t) {
201 assert(a <= t);
202 assert(t <= b);
203 return (t - a) / (b - a);
204}
205
206static quat interpolate_rotation(
207 const Channel* channel, int prev, int next, R t) {
208 assert(channel);
209
210 if (next == 0) {
211 // Animation has not started at this point in time yet.
212 return channel->keyframes[next].rotation;
213 } else {
214 switch (channel->interpolation) {
215 case StepInterpolation:
216 return channel->keyframes[prev].rotation;
217 case LinearInterpolation: {
218 const R normalized_t = normalize_time(
219 channel->keyframes[prev].time, channel->keyframes[next].time, t);
220 return qnormalize(qslerp(
221 channel->keyframes[prev].rotation, channel->keyframes[next].rotation,
222 normalized_t));
223 break;
224 }
225 case CubicSplineInterpolation:
226 assert(false); // TODO
227 return qmake(0, 0, 0, 0);
228 default:
229 assert(false);
230 return qmake(0, 0, 0, 0);
231 }
232 }
233}
234
235static vec3 interpolate_translation(
236 const Channel* channel, int prev, int next, R t) {
237 assert(channel);
238
239 if (next == 0) {
240 // Animation has not started at this point in time yet.
241 return channel->keyframes[next].translation;
242 } else {
243 switch (channel->interpolation) {
244 case StepInterpolation:
245 return channel->keyframes[prev].translation;
246 case LinearInterpolation: {
247 const R normalized_t = normalize_time(
248 channel->keyframes[prev].time, channel->keyframes[next].time, t);
249 return vec3_lerp(
250 channel->keyframes[prev].translation,
251 channel->keyframes[next].translation, normalized_t);
252 break;
253 }
254 case CubicSplineInterpolation:
255 assert(false); // TODO
256 return vec3_make(0, 0, 0);
257 default:
258 assert(false);
259 return vec3_make(0, 0, 0);
260 }
261 }
262}
263
264static void animate_channel(const Channel* channel, R t) {
265 assert(channel);
266 assert(channel->target.val != 0);
267
268 int prev, next;
269 find_keyframes(channel, t, &prev, &next);
270
271 // Note that not all channels extend to the duration of an animation; some
272 // channels may stop animating their targets earlier. Clamp the animation time
273 // to the channel's end keyframe to make the rest of the math (normalize_time)
274 // work.
275 t = t > channel->keyframes[next].time ? channel->keyframes[next].time : t;
276
277 SceneNode* target = mem_get_node(channel->target);
278 assert(target);
279
280 switch (channel->type) {
281 case RotationChannel: {
282 const quat rotation = interpolate_rotation(channel, prev, next, t);
283 gfx_set_node_rotation(target, &rotation);
284 break;
285 }
286 case TranslationChannel: {
287 const vec3 translation = interpolate_translation(channel, prev, next, t);
288 gfx_set_node_position(target, &translation);
289 break;
290 }
291 // Not yet supported.
292 case ScaleChannel:
293 case WeightsChannel:
294 default:
295 // TODO: Add back the assertion or add support for scaling.
296 // assert(false);
297 break;
298 }
299}
300
301static void compute_joint_matrices_rec(
302 node_idx node_index, mat4 parent_global_joint_transform,
303 const mat4* root_inv_global_transform) {
304 if (node_index.val == 0) {
305 return;
306 }
307
308 const SceneNode* node = mem_get_node(node_index);
309 assert(node);
310 const mat4 global_joint_transform =
311 mat4_mul(parent_global_joint_transform, node->transform);
312
313 // For flexibility (glTF), we allow non-joint nodes between the root of the
314 // skeleton and its joint nodes. We check the node type as opposed to assert
315 // it.
316 if (node->type == JointNode) {
317 // Compute this node's joint matrix.
318 Joint* joint = mem_get_joint(node->joint);
319 assert(joint);
320
321 joint->joint_matrix = mat4_mul(
322 *root_inv_global_transform,
323 mat4_mul(global_joint_transform, joint->inv_bind_matrix));
324 }
325
326 // Recursively compute the joint matrices for this node's children.
327 node_idx child = node->child;
328 while (child.val != 0) {
329 compute_joint_matrices_rec(
330 child, global_joint_transform, root_inv_global_transform);
331 node = mem_get_node(child);
332 child = node->next; // Next sibling.
333 }
334}
335
336void gfx_update_animation(Anima* anima, R t) {
337 assert(anima);
338
339 AnimationState* state = &anima->state;
340 if (state->animation.val == 0) {
341 return; // No active animation.
342 }
343 const Animation* animation = mem_get_animation(state->animation);
344 assert(animation);
345
346 // On a call to play(), the start time is set to -1 to signal that the
347 // animation playback has not yet been initialized.
348 if (state->start_time == PLAYBACK_UNINITIALIZED) {
349 state->start_time = t;
350 }
351 // Locate the current time point inside the animation's timeline.
352 assert(t >= state->start_time);
353 assert(animation->duration >= 0.0);
354 const R local_time = t - state->start_time;
355 const R animation_time = state->loop
356 ? rmod(local_time, animation->duration)
357 : clamp(local_time, 0.0, animation->duration);
358
359 // LOGD(
360 // "animation_time = %f, animation duration: %f", animation_time,
361 // animation->duration);
362
363 // Play through the animation to transform skeleton nodes.
364 for (size_t i = 0; i < animation->num_channels; ++i) {
365 const Channel* channel = &animation->channels[i];
366 animate_channel(channel, animation_time);
367 }
368
369 // Compute joint matrices after having transformed the skeletons.
370 //
371 // Skeletons are not guaranteed to have a common parent, but are generally a
372 // set of disjoint trees (glTF). The anima's parent node, however, is
373 // guaranteed to be the common ancestor of all skeletons.
374 //
375 // Joint matrix calculation therefore begins by descending from the anima's
376 // node. This node's immediate children may not be joints, however, so we need
377 // to gracefully handle them and proceed recursively.
378 //
379 // Lack of a common parent aside, the procedure touches every joint exactly
380 // once (and potentially other non-joint intermediate nodes).
381 node_idx root_index = anima->parent;
382 SceneNode* root = mem_get_node(root_index);
383 assert(root);
384 // LOGD("Root: %u, child: %u", root_index.val, root->child.val);
385 const mat4 root_global_transform = gfx_get_node_global_transform(root);
386 const mat4 root_inv_global_transform = mat4_inverse(root_global_transform);
387 compute_joint_matrices_rec(
388 root->child, root_global_transform, &root_inv_global_transform);
389}
390
391const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i) {
392 assert(anima);
393
394 skeleton_idx skeleton_index = anima->skeleton;
395 const Skeleton* skeleton = mem_get_skeleton(skeleton_index);
396
397 for (size_t j = 1; j < i; ++j) {
398 skeleton_index = skeleton->next;
399 mem_get_skeleton(skeleton_index);
400 }
401
402 return skeleton;
403}
diff --git a/gfx/src/scene/animation_impl.h b/gfx/src/scene/animation_impl.h
new file mode 100644
index 0000000..ceebbbf
--- /dev/null
+++ b/gfx/src/scene/animation_impl.h
@@ -0,0 +1,95 @@
1#pragma once
2
3#include <gfx/scene/animation.h>
4#include <gfx/sizes.h>
5
6#include "types.h"
7
8#include <cstring.h>
9#include <math/defs.h>
10#include <math/mat4.h>
11#include <math/quat.h>
12#include <math/vec3.h>
13
14#include <stddef.h>
15#include <stdint.h>
16
17typedef struct Buffer Buffer;
18
19// Currently ignoring scale in skinning and animation.
20//
21// TODO: Simultaneous animation of disjoint animations.
22
23typedef struct Joint {
24 node_idx parent; // Parent SceneNode.
25 mat4 inv_bind_matrix;
26 mat4 joint_matrix;
27} Joint;
28
29/// Animation skeleton.
30///
31/// The joints array maps joint indices to scene nodes.
32///
33/// The last element of the joints array is the root of the hierarchy. Storing
34/// the root of the hierarchy allows us to compute joint matrices only for scene
35/// nodes that are actually part of a skeleton, since the root identifies the
36/// skeleton's node hierarchy.
37///
38/// Some model formats (glTF) may not actually specify a root. In that case, the
39/// client must create one to form a strict tree hierarchy.
40typedef struct Skeleton {
41 skeleton_idx next;
42 size_t num_joints;
43 node_idx joints[GFX_MAX_NUM_JOINTS]; // Last = root.
44} Skeleton;
45
46/// A keyframe of animation.
47typedef struct Keyframe {
48 R time; // Start time in [0, end animation time]
49 union {
50 vec3 translation;
51 quat rotation;
52 };
53} Keyframe;
54
55/// Animation channel.
56typedef struct Channel {
57 node_idx target;
58 ChannelType type;
59 AnimationInterpolation interpolation;
60 size_t num_keyframes;
61 Keyframe keyframes[GFX_MAX_NUM_KEYFRAMES];
62} Channel;
63
64/// A skeletal animation.
65typedef struct Animation {
66 animation_idx next;
67 sstring name;
68 R duration;
69 size_t num_channels;
70 Channel channels[GFX_MAX_NUM_CHANNELS];
71} Animation;
72
73/// Animation state.
74///
75/// This represents the current state of an animation.
76typedef struct AnimationState {
77 R start_time; // Time when the current animation started playing. -1 means the
78 // animation playback has not yet been initialized.
79 animation_idx animation; // Current animation. 0 = no animation.
80 bool loop;
81} AnimationState;
82
83/// Animation object.
84///
85/// This is the top-level animation object that encapsulates everything
86/// necessary for animation.
87///
88/// For lack of a better name, this is called an Anima. It is short and the
89/// Latin root of animation.
90typedef struct Anima {
91 AnimationState state;
92 skeleton_idx skeleton;
93 animation_idx animation; // Index of first animation.
94 node_idx parent; // Parent SceneNode.
95} Anima;
diff --git a/gfx/src/scene/material.c b/gfx/src/scene/material.c
index e5856d0..6d6decb 100644
--- a/gfx/src/scene/material.c
+++ b/gfx/src/scene/material.c
@@ -43,6 +43,11 @@ static void set_uniform(ShaderProgram* prog, const ShaderUniform* uniform) {
43 case UniformFloat: 43 case UniformFloat:
44 gfx_set_float_uniform(prog, uniform->name.str, uniform->value.scalar); 44 gfx_set_float_uniform(prog, uniform->name.str, uniform->value.scalar);
45 break; 45 break;
46 case UniformMat4Array:
47 gfx_set_mat4_array_uniform(
48 prog, uniform->name.str, uniform->value.array.values,
49 uniform->value.array.count);
50 break;
46 } 51 }
47} 52}
48 53
diff --git a/gfx/src/scene/node.c b/gfx/src/scene/node.c
index 71e0e0b..760951b 100644
--- a/gfx/src/scene/node.c
+++ b/gfx/src/scene/node.c
@@ -26,6 +26,18 @@ SceneNode* gfx_make_node() {
26 return node; 26 return node;
27} 27}
28 28
29SceneNode* gfx_make_anima_node(Anima* anima) {
30 assert(anima);
31 SceneNode* node = gfx_make_node();
32 if (!node) {
33 return 0;
34 }
35 node->type = AnimaNode;
36 node->anima = mem_get_anima_index(anima);
37 anima->parent = mem_get_node_index(node);
38 return node;
39}
40
29SceneNode* gfx_make_camera_node(SceneCamera* camera) { 41SceneNode* gfx_make_camera_node(SceneCamera* camera) {
30 assert(camera); 42 assert(camera);
31 SceneNode* node = gfx_make_node(); 43 SceneNode* node = gfx_make_node();
@@ -38,6 +50,18 @@ SceneNode* gfx_make_camera_node(SceneCamera* camera) {
38 return node; 50 return node;
39} 51}
40 52
53SceneNode* gfx_make_joint_node(Joint* joint) {
54 assert(joint);
55 SceneNode* node = gfx_make_node();
56 if (!node) {
57 return 0;
58 }
59 node->type = JointNode;
60 node->joint = mem_get_joint_index(joint);
61 joint->parent = mem_get_node_index(node);
62 return node;
63}
64
41SceneNode* gfx_make_light_node(Light* light) { 65SceneNode* gfx_make_light_node(Light* light) {
42 assert(light); 66 assert(light);
43 SceneNode* node = gfx_make_node(); 67 SceneNode* node = gfx_make_node();
@@ -62,6 +86,100 @@ SceneNode* gfx_make_object_node(SceneObject* object) {
62 return node; 86 return node;
63} 87}
64 88
89/// Frees the node's resource.
90static void free_node_resource(SceneNode* node) {
91 assert(node);
92
93 // Set the resource's parent node back to 0 to avoid a recursive call into
94 // gfx_del_node().
95 switch (node->type) {
96 case AnimaNode: {
97 Anima* anima = mem_get_anima(node->anima);
98 anima->parent.val = 0;
99 gfx_destroy_anima(&anima);
100 break;
101 }
102 case CameraNode: {
103 SceneCamera* camera = mem_get_camera(node->camera);
104 camera->parent.val = 0;
105 gfx_destroy_camera(&camera);
106 break;
107 }
108 case JointNode: {
109 Joint* joint = mem_get_joint(node->joint);
110 joint->parent.val = 0;
111 gfx_destroy_joint(&joint);
112 break;
113 }
114 case LightNode: {
115 Light* light = mem_get_light(node->light);
116 light->parent.val = 0;
117 gfx_destroy_light(&light);
118 break;
119 }
120 case ObjectNode: {
121 SceneObject* object = mem_get_object(node->object);
122 object->parent.val = 0;
123 gfx_destroy_object(&object);
124 break;
125 }
126 case LogicalNode:
127 break; // Logical nodes have no resource.
128 }
129}
130
131void gfx_construct_anima_node(SceneNode* node, Anima* anima) {
132 assert(node);
133 assert(anima);
134
135 free_node_resource(node);
136 node->type = AnimaNode;
137 node->anima = mem_get_anima_index(anima);
138 anima->parent = mem_get_node_index(node);
139}
140
141void gfx_construct_camera_node(SceneNode* node, SceneCamera* camera) {
142 assert(node);
143 assert(camera);
144
145 free_node_resource(node);
146 node->type = CameraNode;
147 node->camera = mem_get_camera_index(camera);
148 camera->parent = mem_get_node_index(node);
149}
150
151// TODO: Add a common helper function between each gfx_make_xyz_node() and
152// gfx_construct_xyz_node() pair.
153void gfx_construct_joint_node(SceneNode* node, Joint* joint) {
154 assert(node);
155 assert(joint);
156
157 free_node_resource(node);
158 node->type = JointNode;
159 node->joint = mem_get_joint_index(joint);
160 joint->parent = mem_get_node_index(node);
161}
162
163void gfx_construct_light_node(SceneNode* node, Light* light) {
164 assert(node);
165 assert(light);
166
167 free_node_resource(node);
168 node->type = LightNode;
169 node->light = mem_get_light_index(light);
170 light->parent = mem_get_node_index(node);
171}
172
173void gfx_construct_object_node(SceneNode* node, SceneObject* object) {
174 assert(node);
175 assert(object);
176
177 free_node_resource(node);
178 node->type = ObjectNode;
179 node->object = mem_get_object_index(object);
180 object->parent = mem_get_node_index(node);
181}
182
65static void destroy_node_rec(SceneNode* node) { 183static void destroy_node_rec(SceneNode* node) {
66 assert(node); 184 assert(node);
67 185
@@ -75,21 +193,7 @@ static void destroy_node_rec(SceneNode* node) {
75 destroy_node_rec(mem_get_node(node->next)); 193 destroy_node_rec(mem_get_node(node->next));
76 } 194 }
77 195
78 // Destroy the node's resource. 196 free_node_resource(node);
79 if (node->type == CameraNode) {
80 SceneCamera* camera = mem_get_camera(node->camera);
81 camera->parent.val = 0; // Avoid call into gfx_del_node().
82 gfx_destroy_camera(&camera);
83 } else if (node->type == LightNode) {
84 Light* light = mem_get_light(node->light);
85 light->parent.val = 0; // Avoid call into gfx_del_node().
86 gfx_destroy_light(&light);
87 } else if (node->type == ObjectNode) {
88 SceneObject* object = mem_get_object(node->object);
89 object->parent.val = 0; // Avoid call into gfx_del_node().
90 gfx_destroy_object(&object);
91 }
92
93 mem_free_node(&node); 197 mem_free_node(&node);
94} 198}
95 199
@@ -118,6 +222,17 @@ void gfx_del_node(node_idx index) {
118 mem_free_node(&node); 222 mem_free_node(&node);
119} 223}
120 224
225NodeType gfx_get_node_type(const SceneNode* node) {
226 assert(node);
227 return node->type;
228}
229
230Anima* gfx_get_node_anima(const SceneNode* node) {
231 assert(node);
232 assert(node->type == AnimaNode);
233 return mem_get_anima(node->anima);
234}
235
121void gfx_set_node_parent(SceneNode* child, SceneNode* parent) { 236void gfx_set_node_parent(SceneNode* child, SceneNode* parent) {
122 assert(child); 237 assert(child);
123 // Parent can be null. 238 // Parent can be null.
@@ -136,17 +251,61 @@ void gfx_set_node_position(SceneNode* node, const vec3* position) {
136 mat4_set_v3(&node->transform, *position); 251 mat4_set_v3(&node->transform, *position);
137} 252}
138 253
139void gfx_set_node_rotation(SceneNode* node, const mat4* rotation) { 254void gfx_set_node_rotation(SceneNode* node, const quat* rotation) {
255 assert(node);
256 assert(rotation);
257 mat4_set_3x3(&node->transform, mat4_from_quat(*rotation));
258}
259
260void gfx_set_node_rotation_mat(SceneNode* node, const mat4* rotation) {
140 assert(node); 261 assert(node);
141 assert(rotation); 262 assert(rotation);
142 mat4_set_3x3(&node->transform, *rotation); 263 mat4_set_3x3(&node->transform, *rotation);
143} 264}
144 265
266mat4 gfx_get_node_transform(const SceneNode* node) {
267 assert(node);
268 return node->transform;
269}
270
271mat4 gfx_get_node_global_transform(const SceneNode* node) {
272 assert(node);
273 mat4 transform = node->transform;
274 node_idx parent_index = node->parent;
275 while (parent_index.val != 0) {
276 const SceneNode* parent = mem_get_node(parent_index);
277 transform = mat4_mul(parent->transform, transform);
278 parent_index = parent->parent;
279 }
280 return transform;
281}
282
283static const char* get_node_type_str(NodeType type) {
284 switch (type) {
285 case LogicalNode:
286 return "LogicalNode";
287 case AnimaNode:
288 return "AnimaNode";
289 case CameraNode:
290 return "CameraNode";
291 case JointNode:
292 return "JointNode";
293 case LightNode:
294 return "LightNode";
295 case ObjectNode:
296 return "ObjectNode";
297 }
298 assert(false);
299 return "";
300}
301
145static void log_node_hierarchy_rec(const SceneNode* node, const sstring* pad) { 302static void log_node_hierarchy_rec(const SceneNode* node, const sstring* pad) {
146 assert(node); 303 assert(node);
147 assert(pad); 304 assert(pad);
148 305
149 LOGD("%sNode (%u)", sstring_cstring(pad), mem_get_node_index(node).val); 306 LOGI(
307 "%s%s (%u)", sstring_cstr(pad), get_node_type_str(node->type),
308 mem_get_node_index(node).val);
150 309
151 // Log the children. 310 // Log the children.
152 if (node->child.val) { 311 if (node->child.val) {
diff --git a/gfx/src/scene/node_impl.h b/gfx/src/scene/node_impl.h
index b5c4af8..e8d546f 100644
--- a/gfx/src/scene/node_impl.h
+++ b/gfx/src/scene/node_impl.h
@@ -4,16 +4,9 @@
4 4
5#include "types.h" 5#include "types.h"
6 6
7#include <cstring.h>
7#include <math/mat4.h> 8#include <math/mat4.h>
8 9
9/// Scene node type.
10typedef enum NodeType {
11 LogicalNode,
12 CameraNode,
13 LightNode,
14 ObjectNode
15} NodeType;
16
17/// Scene node. 10/// Scene node.
18/// 11///
19/// The SceneNode owns its cameras, objects, lights and child nodes. These 12/// The SceneNode owns its cameras, objects, lights and child nodes. These
@@ -21,7 +14,9 @@ typedef enum NodeType {
21typedef struct SceneNode { 14typedef struct SceneNode {
22 NodeType type; 15 NodeType type;
23 union { 16 union {
17 anima_idx anima;
24 camera_idx camera; 18 camera_idx camera;
19 joint_idx joint;
25 light_idx light; 20 light_idx light;
26 object_idx object; 21 object_idx object;
27 }; 22 };
diff --git a/gfx/src/scene/object.c b/gfx/src/scene/object.c
index 7f23e92..47d2f25 100644
--- a/gfx/src/scene/object.c
+++ b/gfx/src/scene/object.c
@@ -40,8 +40,8 @@ void gfx_add_object_mesh(SceneObject* object, Mesh* mesh) {
40 40
41 MeshLink* link = mem_alloc_mesh_link(); 41 MeshLink* link = mem_alloc_mesh_link();
42 assert(link); 42 assert(link);
43 link->mesh = mem_get_mesh_index(mesh); 43 link->mesh = mem_get_mesh_index(mesh);
44 link->next = object->mesh_link; 44 link->next = object->mesh_link;
45 object->mesh_link = mem_get_mesh_link_index(link); 45 object->mesh_link = mem_get_mesh_link_index(link);
46} 46}
47 47
@@ -49,7 +49,7 @@ void gfx_remove_object_mesh(SceneObject* object, Mesh* mesh) {
49 assert(object); 49 assert(object);
50 assert(mesh); 50 assert(mesh);
51 51
52 MeshLink* prev = 0; 52 MeshLink* prev = 0;
53 const mesh_idx mesh_index = mem_get_mesh_index(mesh); 53 const mesh_idx mesh_index = mem_get_mesh_index(mesh);
54 54
55 // Find the MeshLink in the object that contains the given mesh. 55 // Find the MeshLink in the object that contains the given mesh.
@@ -65,7 +65,13 @@ void gfx_remove_object_mesh(SceneObject* object, Mesh* mesh) {
65 mem_free_mesh_link(&mesh_link); 65 mem_free_mesh_link(&mesh_link);
66 break; 66 break;
67 } 67 }
68 prev = mesh_link; 68 prev = mesh_link;
69 mesh_link_index = mesh_link->next; 69 mesh_link_index = mesh_link->next;
70 } 70 }
71} 71}
72
73void gfx_set_object_skeleton(SceneObject* object, const Skeleton* skeleton) {
74 assert(object);
75 assert(skeleton);
76 object->skeleton = mem_get_skeleton_index(skeleton);
77}
diff --git a/gfx/src/scene/object_impl.h b/gfx/src/scene/object_impl.h
index bd3fdbc..45119db 100644
--- a/gfx/src/scene/object_impl.h
+++ b/gfx/src/scene/object_impl.h
@@ -7,7 +7,7 @@
7#include <math/mat4.h> 7#include <math/mat4.h>
8 8
9typedef struct MeshLink { 9typedef struct MeshLink {
10 mesh_idx mesh; 10 mesh_idx mesh;
11 mesh_link_idx next; // Next MeshLink in the list. 11 mesh_link_idx next; // Next MeshLink in the list.
12} MeshLink; 12} MeshLink;
13 13
@@ -19,7 +19,8 @@ typedef struct MeshLink {
19/// different for each SceneObject. Each SceneObject may then have a unique list 19/// different for each SceneObject. Each SceneObject may then have a unique list
20/// of Meshes, and the Meshes are re-used. 20/// of Meshes, and the Meshes are re-used.
21typedef struct SceneObject { 21typedef struct SceneObject {
22 mat4 transform; 22 mat4 transform;
23 mesh_link_idx mesh_link; // First MeshLink in the list. 23 mesh_link_idx mesh_link; // First MeshLink in the list.
24 node_idx parent; // Parent SceneNode. 24 skeleton_idx skeleton;
25 node_idx parent; // Parent SceneNode.
25} SceneObject; 26} SceneObject;
diff --git a/gfx/src/scene/scene_memory.c b/gfx/src/scene/scene_memory.c
index 83ecd57..62e9401 100644
--- a/gfx/src/scene/scene_memory.c
+++ b/gfx/src/scene/scene_memory.c
@@ -2,6 +2,7 @@
2 2
3#include <gfx/sizes.h> 3#include <gfx/sizes.h>
4 4
5#include "animation_impl.h"
5#include "camera_impl.h" 6#include "camera_impl.h"
6#include "light_impl.h" 7#include "light_impl.h"
7#include "material_impl.h" 8#include "material_impl.h"
@@ -13,7 +14,10 @@
13 14
14#include <mempool.h> 15#include <mempool.h>
15 16
17DEF_MEMPOOL(anima_pool, Anima, GFX_MAX_NUM_ANIMAS)
18DEF_MEMPOOL(animation_pool, Animation, GFX_MAX_NUM_ANIMATIONS)
16DEF_MEMPOOL(camera_pool, SceneCamera, GFX_MAX_NUM_CAMERAS) 19DEF_MEMPOOL(camera_pool, SceneCamera, GFX_MAX_NUM_CAMERAS)
20DEF_MEMPOOL(joint_pool, Joint, GFX_MAX_NUM_JOINTS)
17DEF_MEMPOOL(light_pool, Light, GFX_MAX_NUM_LIGHTS) 21DEF_MEMPOOL(light_pool, Light, GFX_MAX_NUM_LIGHTS)
18DEF_MEMPOOL(material_pool, Material, GFX_MAX_NUM_MATERIALS) 22DEF_MEMPOOL(material_pool, Material, GFX_MAX_NUM_MATERIALS)
19DEF_MEMPOOL(mesh_pool, Mesh, GFX_MAX_NUM_MESHES) 23DEF_MEMPOOL(mesh_pool, Mesh, GFX_MAX_NUM_MESHES)
@@ -21,12 +25,16 @@ DEF_MEMPOOL(mesh_link_pool, MeshLink, GFX_MAX_NUM_MESH_LINKS)
21DEF_MEMPOOL(node_pool, SceneNode, GFX_MAX_NUM_NODES) 25DEF_MEMPOOL(node_pool, SceneNode, GFX_MAX_NUM_NODES)
22DEF_MEMPOOL(object_pool, SceneObject, GFX_MAX_NUM_OBJECTS) 26DEF_MEMPOOL(object_pool, SceneObject, GFX_MAX_NUM_OBJECTS)
23DEF_MEMPOOL(scene_pool, Scene, GFX_MAX_NUM_SCENES) 27DEF_MEMPOOL(scene_pool, Scene, GFX_MAX_NUM_SCENES)
28DEF_MEMPOOL(skeleton_pool, Skeleton, GFX_MAX_NUM_SKELETONS)
24 29
25/// Scene memory. 30/// Scene memory.
26/// 31///
27/// Holds memory pools for every type of scene object. 32/// Holds memory pools for every type of scene object.
28typedef struct SceneMemory { 33typedef struct SceneMemory {
34 anima_pool animas;
35 animation_pool animations;
29 camera_pool cameras; 36 camera_pool cameras;
37 joint_pool joints;
30 light_pool lights; 38 light_pool lights;
31 material_pool materials; 39 material_pool materials;
32 mesh_pool meshes; 40 mesh_pool meshes;
@@ -34,6 +42,7 @@ typedef struct SceneMemory {
34 node_pool nodes; 42 node_pool nodes;
35 object_pool objects; 43 object_pool objects;
36 scene_pool scenes; 44 scene_pool scenes;
45 skeleton_pool skeletons;
37} SceneMemory; 46} SceneMemory;
38 47
39static SceneMemory mem; 48static SceneMemory mem;
@@ -42,7 +51,10 @@ static SceneMemory mem;
42 assert(mempool_get_block_index(POOL, mempool_alloc(POOL)) == 0) 51 assert(mempool_get_block_index(POOL, mempool_alloc(POOL)) == 0)
43 52
44void scene_mem_init() { 53void scene_mem_init() {
54 mempool_make(&mem.animas);
55 mempool_make(&mem.animations);
45 mempool_make(&mem.cameras); 56 mempool_make(&mem.cameras);
57 mempool_make(&mem.joints);
46 mempool_make(&mem.lights); 58 mempool_make(&mem.lights);
47 mempool_make(&mem.materials); 59 mempool_make(&mem.materials);
48 mempool_make(&mem.meshes); 60 mempool_make(&mem.meshes);
@@ -50,10 +62,14 @@ void scene_mem_init() {
50 mempool_make(&mem.nodes); 62 mempool_make(&mem.nodes);
51 mempool_make(&mem.objects); 63 mempool_make(&mem.objects);
52 mempool_make(&mem.scenes); 64 mempool_make(&mem.scenes);
65 mempool_make(&mem.skeletons);
53 66
54 // Allocate dummy objects at index 0 to guarantee that no objects allocated by 67 // Allocate dummy objects at index 0 to guarantee that no objects allocated by
55 // the caller map to index 0. 68 // the caller map to index 0.
69 ALLOC_DUMMY(&mem.animas);
70 ALLOC_DUMMY(&mem.animations);
56 ALLOC_DUMMY(&mem.cameras); 71 ALLOC_DUMMY(&mem.cameras);
72 ALLOC_DUMMY(&mem.joints);
57 ALLOC_DUMMY(&mem.lights); 73 ALLOC_DUMMY(&mem.lights);
58 ALLOC_DUMMY(&mem.materials); 74 ALLOC_DUMMY(&mem.materials);
59 ALLOC_DUMMY(&mem.meshes); 75 ALLOC_DUMMY(&mem.meshes);
@@ -61,6 +77,7 @@ void scene_mem_init() {
61 ALLOC_DUMMY(&mem.nodes); 77 ALLOC_DUMMY(&mem.nodes);
62 ALLOC_DUMMY(&mem.objects); 78 ALLOC_DUMMY(&mem.objects);
63 ALLOC_DUMMY(&mem.scenes); 79 ALLOC_DUMMY(&mem.scenes);
80 ALLOC_DUMMY(&mem.skeletons);
64} 81}
65 82
66void scene_mem_destroy() { 83void scene_mem_destroy() {
@@ -76,12 +93,30 @@ void scene_mem_destroy() {
76 scene_destroy(scene); 93 scene_destroy(scene);
77 } 94 }
78 }); 95 });
96 // Then delete stray nodes. This will delete their children nodes and
97 // resource.
98 mempool_foreach(&mem.nodes, node, {
99 if (i > 0) {
100 gfx_destroy_node(&node);
101 }
102 });
79 // Destroy remaining scene elements. 103 // Destroy remaining scene elements.
104 mempool_foreach(&mem.animas, anima, {
105 if (i > 0) {
106 gfx_destroy_anima(&anima);
107 }
108 });
109 // Animations are owned by animas and do not have a destructor.
80 mempool_foreach(&mem.cameras, camera, { 110 mempool_foreach(&mem.cameras, camera, {
81 if (i > 0) { 111 if (i > 0) {
82 gfx_destroy_camera(&camera); 112 gfx_destroy_camera(&camera);
83 } 113 }
84 }); 114 });
115 mempool_foreach(&mem.joints, joint, {
116 if (i > 0) {
117 gfx_destroy_joint(&joint);
118 }
119 });
85 mempool_foreach(&mem.lights, light, { 120 mempool_foreach(&mem.lights, light, {
86 if (i > 0) { 121 if (i > 0) {
87 gfx_destroy_light(&light); 122 gfx_destroy_light(&light);
@@ -98,20 +133,19 @@ void scene_mem_destroy() {
98 } 133 }
99 }); 134 });
100 // Mesh links don't have a destructor. 135 // Mesh links don't have a destructor.
101 mempool_foreach(&mem.nodes, node, {
102 if (i > 0) {
103 gfx_destroy_node(&node);
104 }
105 });
106 mempool_foreach(&mem.objects, object, { 136 mempool_foreach(&mem.objects, object, {
107 if (i > 0) { 137 if (i > 0) {
108 gfx_destroy_object(&object); 138 gfx_destroy_object(&object);
109 } 139 }
110 }); 140 });
141 // Skeletons are owned by animas and do not have a destructor.
111} 142}
112 143
113// Memory allocation. 144// Memory allocation.
145Anima* mem_alloc_anima() { return mempool_alloc(&mem.animas); }
146Animation* mem_alloc_animation() { return mempool_alloc(&mem.animations); }
114SceneCamera* mem_alloc_camera() { return mempool_alloc(&mem.cameras); } 147SceneCamera* mem_alloc_camera() { return mempool_alloc(&mem.cameras); }
148Joint* mem_alloc_joint() { return mempool_alloc(&mem.joints); }
115Light* mem_alloc_light() { return mempool_alloc(&mem.lights); } 149Light* mem_alloc_light() { return mempool_alloc(&mem.lights); }
116Material* mem_alloc_material() { return mempool_alloc(&mem.materials); } 150Material* mem_alloc_material() { return mempool_alloc(&mem.materials); }
117Mesh* mem_alloc_mesh() { return mempool_alloc(&mem.meshes); } 151Mesh* mem_alloc_mesh() { return mempool_alloc(&mem.meshes); }
@@ -119,11 +153,17 @@ MeshLink* mem_alloc_mesh_link() { return mempool_alloc(&mem.mesh_links); }
119SceneNode* mem_alloc_node() { return mempool_alloc(&mem.nodes); } 153SceneNode* mem_alloc_node() { return mempool_alloc(&mem.nodes); }
120SceneObject* mem_alloc_object() { return mempool_alloc(&mem.objects); } 154SceneObject* mem_alloc_object() { return mempool_alloc(&mem.objects); }
121Scene* mem_alloc_scene() { return mempool_alloc(&mem.scenes); } 155Scene* mem_alloc_scene() { return mempool_alloc(&mem.scenes); }
156Skeleton* mem_alloc_skeleton() { return mempool_alloc(&mem.skeletons); }
122 157
123// Memory free. 158// Memory free.
159void mem_free_anima(Anima** anima) { mempool_free(&mem.animas, anima); }
160void mem_free_animation(Animation** animation) {
161 mempool_free(&mem.animations, animation);
162}
124void mem_free_camera(SceneCamera** camera) { 163void mem_free_camera(SceneCamera** camera) {
125 mempool_free(&mem.cameras, camera); 164 mempool_free(&mem.cameras, camera);
126} 165}
166void mem_free_joint(Joint** joint) { mempool_free(&mem.joints, joint); }
127void mem_free_light(Light** light) { mempool_free(&mem.lights, light); } 167void mem_free_light(Light** light) { mempool_free(&mem.lights, light); }
128void mem_free_material(Material** material) { 168void mem_free_material(Material** material) {
129 mempool_free(&mem.materials, material); 169 mempool_free(&mem.materials, material);
@@ -137,11 +177,23 @@ void mem_free_object(SceneObject** object) {
137 mempool_free(&mem.objects, object); 177 mempool_free(&mem.objects, object);
138} 178}
139void mem_free_scene(Scene** scene) { mempool_free(&mem.scenes, scene); } 179void mem_free_scene(Scene** scene) { mempool_free(&mem.scenes, scene); }
180void mem_free_skeleton(Skeleton** skeleton) {
181 mempool_free(&mem.skeletons, skeleton);
182}
140 183
141// Query by index. 184// Query by index.
185Anima* mem_get_anima(anima_idx index) {
186 return mempool_get_block(&mem.animas, index.val);
187}
188Animation* mem_get_animation(animation_idx index) {
189 return mempool_get_block(&mem.animations, index.val);
190}
142SceneCamera* mem_get_camera(camera_idx index) { 191SceneCamera* mem_get_camera(camera_idx index) {
143 return mempool_get_block(&mem.cameras, index.val); 192 return mempool_get_block(&mem.cameras, index.val);
144} 193}
194Joint* mem_get_joint(joint_idx index) {
195 return mempool_get_block(&mem.joints, index.val);
196}
145Light* mem_get_light(light_idx index) { 197Light* mem_get_light(light_idx index) {
146 return mempool_get_block(&mem.lights, index.val); 198 return mempool_get_block(&mem.lights, index.val);
147} 199}
@@ -163,11 +215,24 @@ SceneObject* mem_get_object(object_idx index) {
163Scene* mem_get_scene(scene_idx index) { 215Scene* mem_get_scene(scene_idx index) {
164 return mempool_get_block(&mem.scenes, index.val); 216 return mempool_get_block(&mem.scenes, index.val);
165} 217}
218Skeleton* mem_get_skeleton(skeleton_idx index) {
219 return mempool_get_block(&mem.skeletons, index.val);
220}
166 221
167// Map object to index. 222// Map object to index.
223anima_idx mem_get_anima_index(const Anima* anima) {
224 return (anima_idx){.val = mempool_get_block_index(&mem.animas, anima)};
225}
226animation_idx mem_get_animation_index(const Animation* animation) {
227 return (animation_idx){
228 .val = mempool_get_block_index(&mem.animations, animation)};
229}
168camera_idx mem_get_camera_index(const SceneCamera* camera) { 230camera_idx mem_get_camera_index(const SceneCamera* camera) {
169 return (camera_idx){.val = mempool_get_block_index(&mem.cameras, camera)}; 231 return (camera_idx){.val = mempool_get_block_index(&mem.cameras, camera)};
170} 232}
233joint_idx mem_get_joint_index(const Joint* joint) {
234 return (joint_idx){.val = mempool_get_block_index(&mem.joints, joint)};
235}
171light_idx mem_get_light_index(const Light* light) { 236light_idx mem_get_light_index(const Light* light) {
172 return (light_idx){.val = mempool_get_block_index(&mem.lights, light)}; 237 return (light_idx){.val = mempool_get_block_index(&mem.lights, light)};
173} 238}
@@ -191,3 +256,7 @@ object_idx mem_get_object_index(const SceneObject* object) {
191scene_idx mem_get_scene_index(const Scene* scene) { 256scene_idx mem_get_scene_index(const Scene* scene) {
192 return (scene_idx){.val = mempool_get_block_index(&mem.scenes, scene)}; 257 return (scene_idx){.val = mempool_get_block_index(&mem.scenes, scene)};
193} 258}
259skeleton_idx mem_get_skeleton_index(const Skeleton* skeleton) {
260 return (skeleton_idx){
261 .val = mempool_get_block_index(&mem.skeletons, skeleton)};
262}
diff --git a/gfx/src/scene/scene_memory.h b/gfx/src/scene/scene_memory.h
index bd2c691..197e4ca 100644
--- a/gfx/src/scene/scene_memory.h
+++ b/gfx/src/scene/scene_memory.h
@@ -1,8 +1,12 @@
1/// Memory management of scene objects. 1/// Memory management of scene objects.
2#pragma once 2#pragma once
3 3
4#include "animation_impl.h"
4#include "types.h" 5#include "types.h"
5 6
7typedef struct Anima Anima;
8typedef struct Animation Animation;
9typedef struct Joint Joint;
6typedef struct Light Light; 10typedef struct Light Light;
7typedef struct Material Material; 11typedef struct Material Material;
8typedef struct Mesh Mesh; 12typedef struct Mesh Mesh;
@@ -11,6 +15,7 @@ typedef struct SceneCamera SceneCamera;
11typedef struct SceneNode SceneNode; 15typedef struct SceneNode SceneNode;
12typedef struct SceneObject SceneObject; 16typedef struct SceneObject SceneObject;
13typedef struct Scene Scene; 17typedef struct Scene Scene;
18typedef struct Skeleton Skeleton;
14 19
15/// Initialize scene memory. 20/// Initialize scene memory.
16/// 21///
@@ -25,9 +30,18 @@ void scene_mem_destroy();
25/// Memory allocation. 30/// Memory allocation.
26/// ---------------------------------------------------------------------------- 31/// ----------------------------------------------------------------------------
27 32
33/// Allocate space for an anima.
34Anima* mem_alloc_anima();
35
36/// Allocate space for an animation.
37Animation* mem_alloc_animation();
38
28/// Allocate space for a camera. 39/// Allocate space for a camera.
29SceneCamera* mem_alloc_camera(); 40SceneCamera* mem_alloc_camera();
30 41
42/// Allocate space for a joint.
43Joint* mem_alloc_joint();
44
31/// Allocate space for a light. 45/// Allocate space for a light.
32Light* mem_alloc_light(); 46Light* mem_alloc_light();
33 47
@@ -49,9 +63,21 @@ SceneObject* mem_alloc_object();
49/// Allocate space for a scene. 63/// Allocate space for a scene.
50Scene* mem_alloc_scene(); 64Scene* mem_alloc_scene();
51 65
66/// Allocate space for a skeleton.
67Skeleton* mem_alloc_skeleton();
68
69/// Free the anima.
70void mem_free_anima(Anima**);
71
72/// Free the animation.
73void mem_free_animation(Animation**);
74
52/// Free the camera. 75/// Free the camera.
53void mem_free_camera(SceneCamera**); 76void mem_free_camera(SceneCamera**);
54 77
78/// Free the joint.
79void mem_free_joint(Joint**);
80
55/// Free the light. 81/// Free the light.
56void mem_free_light(Light**); 82void mem_free_light(Light**);
57 83
@@ -73,13 +99,25 @@ void mem_free_object(SceneObject**);
73/// Free the scene. 99/// Free the scene.
74void mem_free_scene(Scene**); 100void mem_free_scene(Scene**);
75 101
102/// Free the skeleton.
103void mem_free_skeleton(Skeleton**);
104
76/// ---------------------------------------------------------------------------- 105/// ----------------------------------------------------------------------------
77/// Query objects by index. 106/// Query objects by index.
78/// ---------------------------------------------------------------------------- 107/// ----------------------------------------------------------------------------
79 108
109/// Get an anima by index.
110Anima* mem_get_anima(anima_idx);
111
112/// Get an animation by index.
113Animation* mem_get_animation(animation_idx);
114
80/// Get a camera by index. 115/// Get a camera by index.
81SceneCamera* mem_get_camera(camera_idx); 116SceneCamera* mem_get_camera(camera_idx);
82 117
118/// Get a joint by index.
119Joint* mem_get_joint(joint_idx);
120
83/// Get a light by index. 121/// Get a light by index.
84Light* mem_get_light(light_idx); 122Light* mem_get_light(light_idx);
85 123
@@ -101,13 +139,25 @@ SceneObject* mem_get_object(object_idx);
101/// Get a scene by index. 139/// Get a scene by index.
102Scene* mem_get_scene(scene_idx); 140Scene* mem_get_scene(scene_idx);
103 141
142/// Get a skeleton by index.
143Skeleton* mem_get_skeleton(skeleton_idx);
144
104/// ---------------------------------------------------------------------------- 145/// ----------------------------------------------------------------------------
105/// Map objects to indices. 146/// Map objects to indices.
106/// ---------------------------------------------------------------------------- 147/// ----------------------------------------------------------------------------
107 148
149/// Get the anima's index.
150anima_idx mem_get_anima_index(const Anima*);
151
152/// Get the animation's index.
153animation_idx mem_get_animation_index(const Animation*);
154
108/// Get the camera's index. 155/// Get the camera's index.
109camera_idx mem_get_camera_index(const SceneCamera*); 156camera_idx mem_get_camera_index(const SceneCamera*);
110 157
158/// Get the joint's index.
159joint_idx mem_get_joint_index(const Joint*);
160
111/// Get the light's index. 161/// Get the light's index.
112light_idx mem_get_light_index(const Light*); 162light_idx mem_get_light_index(const Light*);
113 163
@@ -128,3 +178,6 @@ object_idx mem_get_object_index(const SceneObject*);
128 178
129/// Get the scene's index. 179/// Get the scene's index.
130scene_idx mem_get_scene_index(const Scene*); 180scene_idx mem_get_scene_index(const Scene*);
181
182/// Get the skeleton's index.
183skeleton_idx mem_get_skeleton_index(const Skeleton*);
diff --git a/gfx/src/scene/types.h b/gfx/src/scene/types.h
index 1c52991..cd87430 100644
--- a/gfx/src/scene/types.h
+++ b/gfx/src/scene/types.h
@@ -1,21 +1,24 @@
1/// Strongly-typed indices for every kind of scene node resource.
1#pragma once 2#pragma once
2 3
3#include <stdint.h> 4#include <stdint.h>
4 5
5typedef uint16_t gfx_idx; 6typedef uint16_t gfx_idx;
6 7
7// Strongly-typed indices. 8#define DEF_STRONG_INDEX(TYPE_NAME, IDX_TYPE) \
8 9 typedef struct TYPE_NAME##_idx { \
9#define DEF_STRONG_INDEX(TYPE_NAME) \ 10 IDX_TYPE val; \
10 typedef struct TYPE_NAME##_idx { \
11 gfx_idx val; \
12 } TYPE_NAME##_idx; 11 } TYPE_NAME##_idx;
13 12
14DEF_STRONG_INDEX(camera) 13DEF_STRONG_INDEX(anima, gfx_idx)
15DEF_STRONG_INDEX(light) 14DEF_STRONG_INDEX(animation, gfx_idx)
16DEF_STRONG_INDEX(material) 15DEF_STRONG_INDEX(camera, gfx_idx)
17DEF_STRONG_INDEX(mesh) 16DEF_STRONG_INDEX(joint, gfx_idx)
18DEF_STRONG_INDEX(mesh_link) 17DEF_STRONG_INDEX(light, gfx_idx)
19DEF_STRONG_INDEX(node) 18DEF_STRONG_INDEX(material, gfx_idx)
20DEF_STRONG_INDEX(object) 19DEF_STRONG_INDEX(mesh, gfx_idx)
21DEF_STRONG_INDEX(scene) 20DEF_STRONG_INDEX(mesh_link, gfx_idx)
21DEF_STRONG_INDEX(node, gfx_idx)
22DEF_STRONG_INDEX(object, gfx_idx)
23DEF_STRONG_INDEX(scene, gfx_idx)
24DEF_STRONG_INDEX(skeleton, gfx_idx)
diff --git a/gfx/src/util/scene.c b/gfx/src/util/scene.c
index 87d775c..49e661d 100644
--- a/gfx/src/util/scene.c
+++ b/gfx/src/util/scene.c
@@ -84,6 +84,7 @@
84#include <gfx/error.h> 84#include <gfx/error.h>
85#include <gfx/gfx.h> 85#include <gfx/gfx.h>
86#include <gfx/render_backend.h> 86#include <gfx/render_backend.h>
87#include <gfx/scene/animation.h>
87#include <gfx/scene/camera.h> 88#include <gfx/scene/camera.h>
88#include <gfx/scene/material.h> 89#include <gfx/scene/material.h>
89#include <gfx/scene/mesh.h> 90#include <gfx/scene/mesh.h>
@@ -97,6 +98,7 @@
97#include <cstring.h> 98#include <cstring.h>
98#include <log/log.h> 99#include <log/log.h>
99#include <math/camera.h> 100#include <math/camera.h>
101#include <math/defs.h>
100#include <math/mat4.h> 102#include <math/mat4.h>
101#include <math/quat.h> 103#include <math/quat.h>
102#include <math/vec2.h> 104#include <math/vec2.h>
@@ -138,6 +140,8 @@
138#define DEFINE_HAS_NORMAL_MAP "HAS_NORMAL_MAP" 140#define DEFINE_HAS_NORMAL_MAP "HAS_NORMAL_MAP"
139#define DEFINE_HAS_OCCLUSION_MAP "HAS_OCCLUSION_MAP" 141#define DEFINE_HAS_OCCLUSION_MAP "HAS_OCCLUSION_MAP"
140#define DEFINE_HAS_EMISSIVE_MAP "HAS_EMISSIVE_MAP" 142#define DEFINE_HAS_EMISSIVE_MAP "HAS_EMISSIVE_MAP"
143#define DEFINE_HAS_JOINTS "HAS_JOINTS"
144#define DEFINE_MAX_JOINTS "MAX_JOINTS"
141 145
142typedef enum TextureType { 146typedef enum TextureType {
143 BaseColorTexture, 147 BaseColorTexture,
@@ -156,6 +160,8 @@ typedef struct MeshPermutation {
156 bool has_texcoords : 1; 160 bool has_texcoords : 1;
157 bool has_normals : 1; 161 bool has_normals : 1;
158 bool has_tangents : 1; 162 bool has_tangents : 1;
163 bool has_joints : 1;
164 bool has_weights : 1;
159 // Textures. 165 // Textures.
160 bool has_albedo_map : 1; 166 bool has_albedo_map : 1;
161 bool has_metallic_roughness_map : 1; 167 bool has_metallic_roughness_map : 1;
@@ -179,16 +185,22 @@ static size_t make_defines(
179 defines[next].value = sstring_make(str_true); \ 185 defines[next].value = sstring_make(str_true); \
180 next++; \ 186 next++; \
181 } 187 }
182
183 check(has_texcoords, DEFINE_HAS_TEXCOORDS); 188 check(has_texcoords, DEFINE_HAS_TEXCOORDS);
184 check(has_normals, DEFINE_HAS_NORMALS); 189 check(has_normals, DEFINE_HAS_NORMALS);
185 check(has_tangents, DEFINE_HAS_TANGENTS); 190 check(has_tangents, DEFINE_HAS_TANGENTS);
191 check(has_joints, DEFINE_HAS_JOINTS);
186 check(has_albedo_map, DEFINE_HAS_ALBEDO_MAP); 192 check(has_albedo_map, DEFINE_HAS_ALBEDO_MAP);
187 check(has_metallic_roughness_map, DEFINE_HAS_METALLIC_ROUGHNESS_MAP); 193 check(has_metallic_roughness_map, DEFINE_HAS_METALLIC_ROUGHNESS_MAP);
188 check(has_normal_map, DEFINE_HAS_NORMAL_MAP); 194 check(has_normal_map, DEFINE_HAS_NORMAL_MAP);
189 check(has_occlusion_map, DEFINE_HAS_OCCLUSION_MAP); 195 check(has_occlusion_map, DEFINE_HAS_OCCLUSION_MAP);
190 check(has_emissive_map, DEFINE_HAS_EMISSIVE_MAP); 196 check(has_emissive_map, DEFINE_HAS_EMISSIVE_MAP);
191 197
198 if (perm.has_joints) {
199 defines[next].name = sstring_make(DEFINE_MAX_JOINTS);
200 defines[next].value = sstring_itoa(GFX_MAX_NUM_JOINTS);
201 next++;
202 }
203
192 return next; 204 return next;
193} 205}
194 206
@@ -197,11 +209,13 @@ static ShaderProgram* make_shader_permutation(
197 RenderBackend* render_backend, MeshPermutation perm) { 209 RenderBackend* render_backend, MeshPermutation perm) {
198 LOGD( 210 LOGD(
199 "Compiling Cook-Torrance shader permutation: texcoords: %d, normals: " 211 "Compiling Cook-Torrance shader permutation: texcoords: %d, normals: "
200 "%d, tangents: %d, albedo map: %d, metallic-roughness map: %d, normal " 212 "%d, tangents: %d, joints: %d, weights: %d, albedo map: %d, "
213 "metallic-roughness map: "
214 "%d, normal "
201 "map: %d, AO map: %d, emissive map: %d", 215 "map: %d, AO map: %d, emissive map: %d",
202 perm.has_texcoords, perm.has_normals, perm.has_tangents, 216 perm.has_texcoords, perm.has_normals, perm.has_tangents, perm.has_joints,
203 perm.has_albedo_map, perm.has_metallic_roughness_map, perm.has_normal_map, 217 perm.has_weights, perm.has_albedo_map, perm.has_metallic_roughness_map,
204 perm.has_occlusion_map, perm.has_emissive_map); 218 perm.has_normal_map, perm.has_occlusion_map, perm.has_emissive_map);
205 219
206 ShaderCompilerDefine defines[GFX_MAX_SHADER_COMPILER_DEFINES]; 220 ShaderCompilerDefine defines[GFX_MAX_SHADER_COMPILER_DEFINES];
207 const size_t num_defines = make_defines(perm, defines); 221 const size_t num_defines = make_defines(perm, defines);
@@ -211,7 +225,7 @@ static ShaderProgram* make_shader_permutation(
211 225
212/// Map a texture type to the name of the shader uniform used to access the 226/// Map a texture type to the name of the shader uniform used to access the
213/// texture. 227/// texture.
214static const char* TextureUniformName(TextureType type) { 228static const char* get_texture_uniform_name(TextureType type) {
215 switch (type) { 229 switch (type) {
216 case BaseColorTexture: 230 case BaseColorTexture:
217 return UNIFORM_BASE_COLOR_TEXTURE; 231 return UNIFORM_BASE_COLOR_TEXTURE;
@@ -223,10 +237,9 @@ static const char* TextureUniformName(TextureType type) {
223 return UNIFORM_AMBIENT_OCCLUSION_TEXTURE; 237 return UNIFORM_AMBIENT_OCCLUSION_TEXTURE;
224 case NormalMap: 238 case NormalMap:
225 return UNIFORM_NORMAL_MAP; 239 return UNIFORM_NORMAL_MAP;
226 default:
227 assert(false);
228 return 0;
229 } 240 }
241 assert(false);
242 return 0;
230} 243}
231 244
232/// Map a glTF primitive type to a gfx primitive type. 245/// Map a glTF primitive type to a gfx primitive type.
@@ -238,13 +251,261 @@ static PrimitiveType from_gltf_primitive_type(cgltf_primitive_type type) {
238 return TriangleFan; 251 return TriangleFan;
239 case cgltf_primitive_type_triangle_strip: 252 case cgltf_primitive_type_triangle_strip:
240 return TriangleStrip; 253 return TriangleStrip;
241 default: 254 // Not yet implemented.
242 LOGE("Unsupported primitive type: %d", type); 255 case cgltf_primitive_type_lines:
256 case cgltf_primitive_type_line_loop:
257 case cgltf_primitive_type_line_strip:
258 case cgltf_primitive_type_points:
259 break;
260 }
261 LOGE("Unsupported primitive type: %d", type);
262 assert(false);
263 return 0;
264}
265
266/// Map a glTF animation path type to its Gfx equivalent.
267static ChannelType from_gltf_animation_path_type(
268 cgltf_animation_path_type type) {
269 switch (type) {
270 case cgltf_animation_path_type_translation:
271 return TranslationChannel;
272 case cgltf_animation_path_type_rotation:
273 return RotationChannel;
274 case cgltf_animation_path_type_scale:
275 return ScaleChannel;
276 case cgltf_animation_path_type_weights:
277 return WeightsChannel;
278 case cgltf_animation_path_type_invalid:
243 assert(false); 279 assert(false);
244 return 0; 280 break;
281 }
282 assert(false);
283 return 0;
284}
285
286/// Map a glTF interpolation to its Gfx equivalent.
287static AnimationInterpolation from_gltf_interpolation_type(
288 cgltf_interpolation_type type) {
289 switch (type) {
290 case cgltf_interpolation_type_linear:
291 return LinearInterpolation;
292 case cgltf_interpolation_type_step:
293 return StepInterpolation;
294 case cgltf_interpolation_type_cubic_spline:
295 return CubicSplineInterpolation;
245 } 296 }
297 assert(false);
298 return 0;
246} 299}
247 300
301/// Return the component's size in bytes.
302static cgltf_size get_component_size(cgltf_component_type type) {
303 switch (type) {
304 case cgltf_component_type_r_8:
305 return 1;
306 case cgltf_component_type_r_8u:
307 return 1;
308 case cgltf_component_type_r_16:
309 return 2;
310 case cgltf_component_type_r_16u:
311 return 2;
312 case cgltf_component_type_r_32u:
313 return 4;
314 case cgltf_component_type_r_32f:
315 return 4;
316 case cgltf_component_type_invalid:
317 assert(false);
318 break;
319 }
320 assert(false);
321 return 0;
322}
323
324/// Read a float from the given data pointer and accessor.
325///
326/// This function uses the normalization equations from the spec. See the
327/// animation section:
328///
329/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#animations
330static float read_float(const void* data, const cgltf_accessor* accessor) {
331 assert(data);
332 assert(accessor);
333
334 switch (accessor->component_type) {
335 case cgltf_component_type_r_8: {
336 assert(accessor->normalized);
337 const int8_t c = *((int8_t*)data);
338 return max((float)c / 127.0, -1.0);
339 }
340 case cgltf_component_type_r_8u: {
341 assert(accessor->normalized);
342 const uint8_t c = *((uint8_t*)data);
343 return (float)c / 255.0;
344 }
345 case cgltf_component_type_r_16: {
346 assert(accessor->normalized);
347 const int16_t c = *((int16_t*)data);
348 return max((float)c / 32767.0, -1.0);
349 }
350 case cgltf_component_type_r_16u: {
351 assert(accessor->normalized);
352 const uint16_t c = *((uint16_t*)data);
353 return (float)c / 65535.0;
354 }
355 case cgltf_component_type_r_32u: {
356 assert(accessor->normalized);
357 const uint32_t c = *((uint32_t*)data);
358 return (float)c / 4294967295.0;
359 }
360 case cgltf_component_type_r_32f: {
361 const float c = *((float*)data);
362 return c;
363 }
364 case cgltf_component_type_invalid:
365 assert(false);
366 break;
367 }
368 assert(false);
369 return 0;
370}
371
372/// Iterate over the vectors in an accessor.
373#define ACCESSOR_FOREACH_VEC(dimensions, accessor, body) \
374 { \
375 assert((1 <= dimensions) && (dimensions <= 4)); \
376 assert(!(dimensions == 1) || (accessor->type == cgltf_type_scalar)); \
377 assert(!(dimensions == 2) || (accessor->type == cgltf_type_vec2)); \
378 assert(!(dimensions == 3) || (accessor->type == cgltf_type_vec3)); \
379 assert(!(dimensions == 4) || (accessor->type == cgltf_type_vec4)); \
380 const cgltf_buffer_view* view = accessor->buffer_view; \
381 const cgltf_buffer* buffer = view->buffer; \
382 const cgltf_size offset = accessor->offset + view->offset; \
383 const cgltf_size comp_size = get_component_size(accessor->component_type); \
384 const uint8_t* bytes = (const uint8_t*)buffer->data + offset; \
385 assert( \
386 (offset + accessor->count * dimensions * comp_size) < buffer->size); \
387 /* From the spec: */ \
388 /* "Buffer views with other types of data MUST NOT not define */ \
389 /* byteStride (unless such layout is explicitly enabled by an */ \
390 /* extension)."*/ \
391 assert(view->stride == 0); \
392 assert(accessor->stride == dimensions * comp_size); \
393 cgltf_float x = 0, y = 0, z = 0, w = 0; \
394 /* Silence unused variable warnings. */ \
395 (void)y; \
396 (void)z; \
397 (void)w; \
398 /* The {component type} X {dimensions} combinations are a pain to handle. \
399 For floats, we switch on type first and then lay out a loop for each \
400 dimension to get a tight loop with a possibly inlined body. For other \
401 types, we take the performance hit and perform checks and conversions \
402 inside the loop for simplicity. */ \
403 if (accessor->component_type == cgltf_component_type_r_32f) { \
404 const cgltf_float* floats = (const cgltf_float*)bytes; \
405 switch (dimensions) { \
406 case 1: \
407 assert(accessor->type == cgltf_type_scalar); \
408 for (cgltf_size i = 0; i < accessor->count; ++i) { \
409 x = *floats++; \
410 body; \
411 } \
412 break; \
413 case 2: \
414 assert(accessor->type == cgltf_type_vec2); \
415 for (cgltf_size i = 0; i < accessor->count; ++i) { \
416 x = *floats++; \
417 y = *floats++; \
418 body; \
419 } \
420 break; \
421 case 3: \
422 assert(accessor->type == cgltf_type_vec3); \
423 for (cgltf_size i = 0; i < accessor->count; ++i) { \
424 x = *floats++; \
425 y = *floats++; \
426 z = *floats++; \
427 body; \
428 } \
429 break; \
430 case 4: \
431 assert(accessor->type == cgltf_type_vec4); \
432 for (cgltf_size i = 0; i < accessor->count; ++i) { \
433 x = *floats++; \
434 y = *floats++; \
435 z = *floats++; \
436 w = *floats++; \
437 body; \
438 } \
439 break; \
440 } \
441 } else { \
442 for (cgltf_size i = 0; i < accessor->count; ++i) { \
443 x = read_float(bytes, accessor); \
444 bytes += accessor->stride; \
445 if (dimensions > 1) { \
446 y = read_float(bytes, accessor); \
447 bytes += accessor->stride; \
448 } \
449 if (dimensions > 2) { \
450 z = read_float(bytes, accessor); \
451 bytes += accessor->stride; \
452 } \
453 if (dimensions > 3) { \
454 w = read_float(bytes, accessor); \
455 bytes += accessor->stride; \
456 } \
457 body; \
458 } \
459 } \
460 }
461
462/// Iterate over the matrices in an accessor.
463#define ACCESSOR_FOREACH_MAT(dimensions, accessor, body) \
464 { \
465 assert((2 <= dimensions) && (dimensions <= 4)); \
466 assert(!(dimensions == 2) || (accessor->type == cgltf_type_mat2)); \
467 assert(!(dimensions == 3) || (accessor->type == cgltf_type_mat3)); \
468 assert(!(dimensions == 4) || (accessor->type == cgltf_type_mat4)); \
469 const cgltf_buffer_view* view = accessor->buffer_view; \
470 const cgltf_buffer* buffer = view->buffer; \
471 const cgltf_size offset = accessor->offset + view->offset; \
472 const cgltf_size comp_size = get_component_size(accessor->component_type); \
473 const uint8_t* bytes = (const uint8_t*)buffer->data + offset; \
474 assert( \
475 (offset + accessor->count * dimensions * comp_size) < buffer->size); \
476 /* From the spec: */ \
477 /* "Buffer views with other types of data MUST NOT not define */ \
478 /* byteStride (unless such layout is explicitly enabled by an */ \
479 /* extension)."*/ \
480 assert(view->stride == 0); \
481 assert(accessor->stride == dimensions * dimensions * comp_size); \
482 assert(accessor->component_type == cgltf_component_type_r_32f); \
483 const cgltf_float* floats = (const cgltf_float*)bytes; \
484 switch (dimensions) { \
485 case 2: \
486 assert(accessor->type == cgltf_type_mat2); \
487 for (cgltf_size i = 0; i < accessor->count; ++i) { \
488 body; \
489 floats += 4; \
490 } \
491 break; \
492 case 3: \
493 assert(accessor->type == cgltf_type_mat3); \
494 for (cgltf_size i = 0; i < accessor->count; ++i) { \
495 body; \
496 floats += 9; \
497 } \
498 break; \
499 case 4: \
500 assert(accessor->type == cgltf_type_mat4); \
501 for (cgltf_size i = 0; i < accessor->count; ++i) { \
502 body; \
503 floats += 16; \
504 } \
505 break; \
506 } \
507 }
508
248/// Return the total number of primitives in the scene. Each mesh may contain 509/// Return the total number of primitives in the scene. Each mesh may contain
249/// multiple primitives. 510/// multiple primitives.
250/// 511///
@@ -261,8 +522,11 @@ static size_t get_total_primitives(const cgltf_data* data) {
261/// 522///
262/// If buffer data is loaded from memory, set filepath = null. 523/// If buffer data is loaded from memory, set filepath = null.
263/// 524///
264/// Return an array of Buffers such that the index of each glTF buffer in 525/// Return an array of Buffers such that the index of each glTF buffer in the
265/// the original array matches the same Buffer in the resulting array. 526/// original array matches the same Buffer in the resulting array.
527///
528/// TODO: There is no need to load the inverse bind matrices buffer into the
529/// GPU. Might need to lazily load buffers.
266static bool load_buffers( 530static bool load_buffers(
267 const cgltf_data* data, RenderBackend* render_backend, Buffer** buffers) { 531 const cgltf_data* data, RenderBackend* render_backend, Buffer** buffers) {
268 assert(data); 532 assert(data);
@@ -411,21 +675,21 @@ static bool load_texture_and_uniform(
411 675
412 LOGD( 676 LOGD(
413 "Load texture: %s (mipmaps: %d, filtering: %d)", 677 "Load texture: %s (mipmaps: %d, filtering: %d)",
414 mstring_cstring(&cmd->data.texture.filepath), cmd->mipmaps, 678 mstring_cstr(&cmd->data.texture.filepath), cmd->mipmaps,
415 cmd->filtering); 679 cmd->filtering);
416 680
417 textures[texture_index] = gfx_load_texture(render_backend, cmd); 681 textures[texture_index] = gfx_load_texture(render_backend, cmd);
418 if (!textures[texture_index]) { 682 if (!textures[texture_index]) {
419 gfx_prepend_error( 683 gfx_prepend_error(
420 "Failed to load texture: %s", 684 "Failed to load texture: %s",
421 mstring_cstring(&cmd->data.texture.filepath)); 685 mstring_cstr(&cmd->data.texture.filepath));
422 return false; 686 return false;
423 } 687 }
424 } 688 }
425 689
426 assert(*next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); 690 assert(*next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL);
427 desc->uniforms[(*next_uniform)++] = (ShaderUniform){ 691 desc->uniforms[(*next_uniform)++] = (ShaderUniform){
428 .name = sstring_make(TextureUniformName(texture_type)), 692 .name = sstring_make(get_texture_uniform_name(texture_type)),
429 .type = UniformTexture, 693 .type = UniformTexture,
430 .value.texture = textures[texture_index]}; 694 .value.texture = textures[texture_index]};
431 695
@@ -627,9 +891,12 @@ static bool load_meshes(
627 assert(buffer_index < data->buffers_count); 891 assert(buffer_index < data->buffers_count);
628 const Buffer* buffer = buffers[buffer_index]; 892 const Buffer* buffer = buffers[buffer_index];
629 893
630 BufferView2d* buffer_view_2d = 0; 894 BufferView2d* buffer_view_2d = 0;
631 BufferView3d* buffer_view_3d = 0; 895 BufferView3d* buffer_view_3d = 0;
632 BufferView4d* buffer_view_4d = 0; 896 BufferView4d* buffer_view_4d = 0;
897 BufferViewFloat* buffer_view_float = 0;
898 BufferViewU8* buffer_view_u8 = 0;
899 BufferViewU16* buffer_view_u16 = 0;
633 900
634 switch (attrib->type) { 901 switch (attrib->type) {
635 case cgltf_attribute_type_position: { 902 case cgltf_attribute_type_position: {
@@ -665,29 +932,66 @@ static bool load_meshes(
665 buffer_view_2d = &geometry_desc.texcoords; 932 buffer_view_2d = &geometry_desc.texcoords;
666 perm.has_texcoords = true; 933 perm.has_texcoords = true;
667 break; 934 break;
668 default: 935 case cgltf_attribute_type_color:
669 // Attribute ignored. 936 // TODO: Add support for color.
937 break;
938 case cgltf_attribute_type_joints:
939 // Joints can be either u8 or u16.
940 switch (accessor->component_type) {
941 case cgltf_component_type_r_8u:
942 buffer_view_u8 = &geometry_desc.joints.u8;
943 break;
944 case cgltf_component_type_r_16u:
945 buffer_view_u16 = &geometry_desc.joints.u16;
946 break;
947 default:
948 assert(false);
949 return false;
950 }
951 perm.has_joints = true;
952 break;
953 case cgltf_attribute_type_weights:
954 // Weights can be either u8, u16, or float.
955 switch (accessor->component_type) {
956 case cgltf_component_type_r_8u:
957 buffer_view_u8 = &geometry_desc.weights.u8;
958 break;
959 case cgltf_component_type_r_16u:
960 buffer_view_u16 = &geometry_desc.weights.u16;
961 break;
962 case cgltf_component_type_r_32f:
963 buffer_view_float = &geometry_desc.weights.floats;
964 break;
965 default:
966 assert(false);
967 return false;
968 }
969 perm.has_weights = true;
970 break;
971 case cgltf_attribute_type_invalid:
972 assert(false);
670 break; 973 break;
671 } 974 }
672 975
673 if (buffer_view_2d) { 976#define CONFIGURE_BUFFER(buf) \
674 buffer_view_2d->buffer = buffer; 977 if (buf) { \
675 buffer_view_2d->offset_bytes = offset; 978 buf->buffer = buffer; \
676 buffer_view_2d->size_bytes = view->size; 979 buf->offset_bytes = offset; \
677 buffer_view_2d->stride_bytes = view->stride; 980 buf->size_bytes = view->size; \
678 } else if (buffer_view_3d) { 981 buf->stride_bytes = view->stride; \
679 buffer_view_3d->buffer = buffer; 982 }
680 buffer_view_3d->offset_bytes = offset; 983 CONFIGURE_BUFFER(buffer_view_2d);
681 buffer_view_3d->size_bytes = view->size; 984 CONFIGURE_BUFFER(buffer_view_3d);
682 buffer_view_3d->stride_bytes = view->stride; 985 CONFIGURE_BUFFER(buffer_view_4d);
683 } else if (buffer_view_4d) { 986 CONFIGURE_BUFFER(buffer_view_u8);
684 buffer_view_4d->buffer = buffer; 987 CONFIGURE_BUFFER(buffer_view_u16);
685 buffer_view_4d->offset_bytes = offset; 988 CONFIGURE_BUFFER(buffer_view_float);
686 buffer_view_4d->size_bytes = view->size;
687 buffer_view_4d->stride_bytes = view->stride;
688 }
689 } // Vertex attributes. 989 } // Vertex attributes.
690 990
991 assert(
992 (perm.has_joints && perm.has_weights) ||
993 (!perm.has_joints && !perm.has_weights));
994
691 // If the mesh primitive has no tangents, see if they were computed 995 // If the mesh primitive has no tangents, see if they were computed
692 // separately. 996 // separately.
693 if (!geometry_desc.tangents.buffer) { 997 if (!geometry_desc.tangents.buffer) {
@@ -712,23 +1016,23 @@ static bool load_meshes(
712 (geometry_desc.positions2d.size_bytes / sizeof(vec2)) + 1016 (geometry_desc.positions2d.size_bytes / sizeof(vec2)) +
713 (geometry_desc.positions3d.size_bytes / sizeof(vec3)); 1017 (geometry_desc.positions3d.size_bytes / sizeof(vec3));
714 1018
1019#define CHECK_COUNT(buffer_view, type, num_components) \
1020 if (geometry_desc.buffer_view.buffer) { \
1021 assert( \
1022 (geometry_desc.buffer_view.size_bytes / \
1023 (num_components * sizeof(type))) == geometry_desc.num_verts); \
1024 }
1025
715 // Check that the number of vertices is consistent across all vertex 1026 // Check that the number of vertices is consistent across all vertex
716 // attributes. 1027 // attributes.
717 if (geometry_desc.normals.buffer) { 1028 CHECK_COUNT(normals, vec3, 1);
718 assert( 1029 CHECK_COUNT(tangents, vec4, 1);
719 (geometry_desc.normals.size_bytes / sizeof(vec3)) == 1030 CHECK_COUNT(texcoords, vec2, 1);
720 geometry_desc.num_verts); 1031 CHECK_COUNT(joints.u8, uint8_t, 4);
721 } 1032 CHECK_COUNT(joints.u16, uint16_t, 4);
722 if (geometry_desc.tangents.buffer) { 1033 CHECK_COUNT(weights.u8, uint8_t, 4);
723 assert( 1034 CHECK_COUNT(weights.u16, uint16_t, 4);
724 (geometry_desc.tangents.size_bytes / sizeof(vec4)) == 1035 CHECK_COUNT(weights.floats, float, 4);
725 geometry_desc.num_verts);
726 }
727 if (geometry_desc.texcoords.buffer) {
728 assert(
729 (geometry_desc.texcoords.size_bytes / sizeof(vec2)) ==
730 geometry_desc.num_verts);
731 }
732 1036
733 const cgltf_size material_index = prim->material - data->materials; 1037 const cgltf_size material_index = prim->material - data->materials;
734 assert(material_index < data->materials_count); 1038 assert(material_index < data->materials_count);
@@ -774,13 +1078,117 @@ static bool load_meshes(
774 return true; 1078 return true;
775} 1079}
776 1080
1081/// Load all skins (Gfx skeletons) from the glTF scene.
1082static void load_skins(
1083 const cgltf_data* data, Buffer* const* buffers, SceneNode** nodes,
1084 SkeletonDesc* descs) {
1085 assert(data);
1086 assert(buffers);
1087 assert(nodes);
1088 assert(descs);
1089
1090 for (cgltf_size s = 0; s < data->skins_count; ++s) {
1091 const cgltf_skin* skin = &data->skins[s];
1092 const cgltf_accessor* matrices_accessor = skin->inverse_bind_matrices;
1093 assert(matrices_accessor->count == skin->joints_count);
1094 SkeletonDesc* desc = &descs[s];
1095
1096 *desc = (SkeletonDesc){.num_joints = skin->joints_count};
1097
1098 assert(skin->joints_count < GFX_MAX_NUM_JOINTS);
1099 // for (cgltf_size j = 0; j < skin->joints_count; ++j) {
1100 ACCESSOR_FOREACH_MAT(4, matrices_accessor, {
1101 const mat4 inv_bind_matrix = mat4_from_array(floats);
1102
1103 const cgltf_size node_index = skin->joints[i] - data->nodes;
1104 assert(node_index < data->nodes_count);
1105 SceneNode* node = nodes[node_index];
1106
1107 // Transform the node into a joint node.
1108 const JointDesc joint_desc =
1109 (JointDesc){.inv_bind_matrix = inv_bind_matrix};
1110 Joint* joint = gfx_make_joint(&joint_desc);
1111 gfx_construct_joint_node(node, joint);
1112
1113 desc->joints[i] = node;
1114 });
1115
1116 // glTF may specify a "skeleton", which is the root of the skeleton's node
1117 // hierarchy. TODO: We could use this root node to optimize the descend in
1118 // joint matrix computation. The root node should be passed with the
1119 // AnimaDesc.
1120 // if (skin->skeleton) {
1121 // cgltf_size root_index = skin->skeleton - data->nodes;
1122 // assert(root_index <= data->nodes_count);
1123 // root_node = nodes[root_index];
1124 // }
1125 }
1126}
1127
1128/// Load all animations from the glTF scene.
1129static void load_animations(
1130 const cgltf_data* data, SceneNode** nodes, AnimationDesc* descs) {
1131 assert(data);
1132 assert(nodes);
1133 assert(descs);
1134
1135 for (cgltf_size a = 0; a < data->animations_count; ++a) {
1136 const cgltf_animation* animation = &data->animations[a];
1137 AnimationDesc* animation_desc = &descs[a];
1138
1139 *animation_desc = (AnimationDesc){
1140 .name = sstring_make(animation->name),
1141 .num_channels = animation->channels_count};
1142
1143 assert(animation->channels_count <= GFX_MAX_NUM_CHANNELS);
1144 for (cgltf_size c = 0; c < animation->channels_count; ++c) {
1145 const cgltf_animation_channel* channel = &animation->channels[c];
1146 ChannelDesc* channel_desc = &animation_desc->channels[c];
1147 const cgltf_animation_sampler* sampler = channel->sampler;
1148 const size_t node_index = channel->target_node - data->nodes;
1149 assert(node_index < data->nodes_count);
1150 SceneNode* target = nodes[node_index];
1151 assert(target);
1152
1153 *channel_desc = (ChannelDesc){
1154 .target = target,
1155 .type = from_gltf_animation_path_type(channel->target_path),
1156 .interpolation = from_gltf_interpolation_type(sampler->interpolation),
1157 .num_keyframes = 0};
1158
1159 // Read time inputs.
1160 ACCESSOR_FOREACH_VEC(1, sampler->input, {
1161 channel_desc->keyframes[i].time = x;
1162 channel_desc->num_keyframes++;
1163 });
1164
1165 // Read transform outputs.
1166 switch (channel->target_path) {
1167 case cgltf_animation_path_type_translation:
1168 ACCESSOR_FOREACH_VEC(3, sampler->output, {
1169 channel_desc->keyframes[i].translation = vec3_make(x, y, z);
1170 });
1171 break;
1172 case cgltf_animation_path_type_rotation:
1173 ACCESSOR_FOREACH_VEC(4, sampler->output, {
1174 channel_desc->keyframes[i].rotation = qmake(x, y, z, w);
1175 });
1176 break;
1177 default:
1178 // TODO: Handle other channel transformations.
1179 break;
1180 }
1181 }
1182 }
1183}
1184
777/// Load all nodes from the glTF scene. 1185/// Load all nodes from the glTF scene.
778/// 1186///
779/// This function ignores the many scenes and default scene of the glTF spec 1187/// This function ignores the many scenes and default scene of the glTF spec
780/// and instead just loads all nodes into a single gfx Scene. 1188/// and instead just loads all nodes into a single gfx Scene.
781static void load_nodes( 1189static void load_nodes(
782 const cgltf_data* data, SceneNode* root_node, SceneObject** objects, 1190 const cgltf_data* data, SceneNode* root_node, SceneObject** objects,
783 SceneCamera** cameras, SceneNode** nodes) { 1191 SceneCamera** cameras, const Anima* anima, SceneNode** nodes) {
784 // Note that with glTF 2.0, nodes do not form a DAG / scene graph but a 1192 // Note that with glTF 2.0, nodes do not form a DAG / scene graph but a
785 // disjount union of strict trees: 1193 // disjount union of strict trees:
786 // 1194 //
@@ -796,12 +1204,11 @@ static void load_nodes(
796 assert(root_node); 1204 assert(root_node);
797 assert(objects); 1205 assert(objects);
798 assert(cameras); 1206 assert(cameras);
1207 assert(anima);
799 assert(nodes); 1208 assert(nodes);
800 1209
801 cgltf_size next_camera = 0; 1210 cgltf_size next_camera = 0;
802 1211
803 // First pass: create all nodes with no hierarchy just yet. We cannot build
804 // the hierarchy if we do not have a node pointer to connect.
805 for (cgltf_size n = 0; n < data->nodes_count; ++n) { 1212 for (cgltf_size n = 0; n < data->nodes_count; ++n) {
806 const cgltf_node* node = &data->nodes[n]; 1213 const cgltf_node* node = &data->nodes[n];
807 1214
@@ -810,7 +1217,15 @@ static void load_nodes(
810 if (node->mesh) { 1217 if (node->mesh) {
811 const cgltf_size mesh_index = node->mesh - data->meshes; 1218 const cgltf_size mesh_index = node->mesh - data->meshes;
812 assert(mesh_index < data->meshes_count); 1219 assert(mesh_index < data->meshes_count);
813 nodes[n] = gfx_make_object_node(objects[mesh_index]); 1220 SceneObject* object = objects[mesh_index];
1221
1222 gfx_construct_object_node(nodes[n], object);
1223 if (node->skin) {
1224 const cgltf_size skin_index = node->skin - data->skins;
1225 assert(skin_index < data->skins_count);
1226 const Skeleton* skeleton = gfx_get_anima_skeleton(anima, skin_index);
1227 gfx_set_object_skeleton(object, skeleton);
1228 }
814 } else if (node->camera) { 1229 } else if (node->camera) {
815 assert(next_camera < data->cameras_count); 1230 assert(next_camera < data->cameras_count);
816 1231
@@ -836,16 +1251,11 @@ static void load_nodes(
836 } 1251 }
837 1252
838 gfx_set_camera_camera(cameras[next_camera], &camera); 1253 gfx_set_camera_camera(cameras[next_camera], &camera);
839 nodes[n] = gfx_make_camera_node(cameras[next_camera]); 1254 gfx_construct_camera_node(nodes[n], cameras[next_camera]);
840 ++next_camera; 1255 ++next_camera;
841 } else { 1256 } else {
842 // TODO: implementation for missing node types. 1257 // TODO: implementation for missing node types.
843 // Create a vanilla SceneNode for unhandled node types for the sake of 1258 // These nodes currently default to logical nodes.
844 // being able to complete the hierarchy. Otherwise, wiring nodes in pass 2
845 // fails.
846 LOGW("Unhandled node type, creating vanilla SceneNode for hierarchy "
847 "completeness");
848 nodes[n] = gfx_make_node();
849 } 1259 }
850 assert(nodes[n]); 1260 assert(nodes[n]);
851 1261
@@ -876,22 +1286,14 @@ static void load_nodes(
876 // given root node. 1286 // given root node.
877 if (!node->parent) { 1287 if (!node->parent) {
878 gfx_set_node_parent(nodes[n], root_node); 1288 gfx_set_node_parent(nodes[n], root_node);
1289 } else {
1290 const cgltf_size parent_index = node->parent - data->nodes;
1291 assert(parent_index < data->nodes_count);
1292 SceneNode* parent = nodes[parent_index];
1293 assert(parent);
1294 gfx_set_node_parent(nodes[n], parent);
879 } 1295 }
880 } // SceneNode. 1296 } // SceneNode.
881
882 // Second pass: wire nodes together to build the hierarchy.
883 for (cgltf_size n = 0; n < data->nodes_count; ++n) {
884 const cgltf_node* parent = &data->nodes[n];
885 SceneNode* parent_scene_node = nodes[n];
886
887 for (cgltf_size c = 0; c < parent->children_count; ++c) {
888 const cgltf_size child_index = parent->children[c] - data->nodes;
889 assert(child_index < data->nodes_count);
890 SceneNode* child_scene_node = nodes[child_index];
891 assert(child_scene_node);
892 gfx_set_node_parent(child_scene_node, parent_scene_node);
893 }
894 }
895} 1297}
896 1298
897/// Load all scenes from the glTF file into the given gfx Scene. 1299/// Load all scenes from the glTF file into the given gfx Scene.
@@ -900,7 +1302,7 @@ static void load_nodes(
900/// 1302///
901/// This function ignores the many scenes and default scene of the glTF spec 1303/// This function ignores the many scenes and default scene of the glTF spec
902/// and instead just loads all scenes into a single gfx Scene. 1304/// and instead just loads all scenes into a single gfx Scene.
903static bool load_scene( 1305static SceneNode* load_scene(
904 cgltf_data* data, Gfx* gfx, SceneNode* root_node, const char* filepath, 1306 cgltf_data* data, Gfx* gfx, SceneNode* root_node, const char* filepath,
905 ShaderProgram* shader, const cgltfTangentBuffer* cgltf_tangent_buffers, 1307 ShaderProgram* shader, const cgltfTangentBuffer* cgltf_tangent_buffers,
906 cgltf_size num_tangent_buffers) { 1308 cgltf_size num_tangent_buffers) {
@@ -915,16 +1317,23 @@ static bool load_scene(
915 // an error is encountered, the helper functions can simply return and this 1317 // an error is encountered, the helper functions can simply return and this
916 // function cleans up any intermediate objects that had been created up until 1318 // function cleans up any intermediate objects that had been created up until
917 // the point of failure. 1319 // the point of failure.
1320 //
1321 // Loading animation data:
1322 // - Buffers with animation sampler data need to stay on the CPU, not
1323 // uploaded to the GPU. We could try to implement GPU animation at a later
1324 // stage.
918 assert(data); 1325 assert(data);
919 assert(gfx); 1326 assert(gfx);
920 assert(root_node); 1327 assert(root_node);
921 1328
1329 bool success = false;
1330
922 RenderBackend* render_backend = gfx_get_render_backend(gfx); 1331 RenderBackend* render_backend = gfx_get_render_backend(gfx);
923 const size_t primitive_count = get_total_primitives(data); 1332 const size_t primitive_count = get_total_primitives(data);
924 1333
925 const mstring directory = mstring_dirname(mstring_make(filepath)); 1334 const mstring directory = mstring_dirname(mstring_make(filepath));
926 LOGD("Filepath: %s", filepath); 1335 LOGD("Filepath: %s", filepath);
927 LOGD("Directory: %s", mstring_cstring(&directory)); 1336 LOGD("Directory: %s", mstring_cstr(&directory));
928 1337
929 Buffer** tangent_buffers = 0; 1338 Buffer** tangent_buffers = 0;
930 Buffer** buffers = 0; 1339 Buffer** buffers = 0;
@@ -933,9 +1342,12 @@ static bool load_scene(
933 Material** materials = 0; 1342 Material** materials = 0;
934 Geometry** geometries = 0; 1343 Geometry** geometries = 0;
935 Mesh** meshes = 0; 1344 Mesh** meshes = 0;
1345 AnimaDesc* anima_desc = 0;
936 SceneObject** scene_objects = 0; 1346 SceneObject** scene_objects = 0;
937 SceneCamera** scene_cameras = 0; 1347 SceneCamera** scene_cameras = 0;
938 SceneNode** scene_nodes = 0; 1348 SceneNode** scene_nodes = 0;
1349 Anima* anima = 0;
1350 SceneNode* anima_node = 0;
939 1351
940 tangent_buffers = calloc(num_tangent_buffers, sizeof(Buffer*)); 1352 tangent_buffers = calloc(num_tangent_buffers, sizeof(Buffer*));
941 buffers = calloc(data->buffers_count, sizeof(Buffer*)); 1353 buffers = calloc(data->buffers_count, sizeof(Buffer*));
@@ -943,6 +1355,7 @@ static bool load_scene(
943 materials = calloc(data->materials_count, sizeof(Material*)); 1355 materials = calloc(data->materials_count, sizeof(Material*));
944 geometries = calloc(primitive_count, sizeof(Geometry*)); 1356 geometries = calloc(primitive_count, sizeof(Geometry*));
945 meshes = calloc(primitive_count, sizeof(Mesh*)); 1357 meshes = calloc(primitive_count, sizeof(Mesh*));
1358 anima_desc = calloc(1, sizeof(AnimaDesc));
946 scene_objects = calloc(data->meshes_count, sizeof(SceneObject*)); 1359 scene_objects = calloc(data->meshes_count, sizeof(SceneObject*));
947 scene_cameras = calloc(data->cameras_count, sizeof(SceneCamera**)); 1360 scene_cameras = calloc(data->cameras_count, sizeof(SceneCamera**));
948 scene_nodes = calloc(data->nodes_count, sizeof(SceneNode**)); 1361 scene_nodes = calloc(data->nodes_count, sizeof(SceneNode**));
@@ -972,7 +1385,7 @@ static bool load_scene(
972 1385
973 if (data->textures_count > 0) { 1386 if (data->textures_count > 0) {
974 load_textures_lazy( 1387 load_textures_lazy(
975 data, render_backend, mstring_cstring(&directory), load_texture_cmds); 1388 data, render_backend, mstring_cstr(&directory), load_texture_cmds);
976 } 1389 }
977 1390
978 if (!load_materials( 1391 if (!load_materials(
@@ -987,23 +1400,49 @@ static bool load_scene(
987 goto cleanup; 1400 goto cleanup;
988 } 1401 }
989 1402
990 load_nodes(data, root_node, scene_objects, scene_cameras, scene_nodes); 1403 // Skins refer to nodes, and nodes may refer to skins. To break this circular
1404 // dependency, glTF defines skins in terms of node indices. We could do the
1405 // same if Gfx allowed allocating nodes contiguously in memory. For now,
1406 // create the nodes up front and use the indices of the array to map to the
1407 // node_idx.
1408 for (cgltf_size i = 0; i < data->nodes_count; ++i) {
1409 scene_nodes[i] = gfx_make_node();
1410 }
991 1411
992 return true; 1412 anima_node = gfx_make_node();
1413 load_skins(data, buffers, scene_nodes, anima_desc->skeletons);
1414 load_animations(data, scene_nodes, anima_desc->animations);
1415 anima_desc->num_skeletons = data->skins_count;
1416 anima_desc->num_animations = data->animations_count;
1417 anima = gfx_make_anima(anima_desc);
1418 gfx_construct_anima_node(anima_node, anima);
1419 gfx_set_node_parent(anima_node, root_node);
1420
1421 // The anima node becomes the root of all scene nodes.
1422 load_nodes(
1423 data, anima_node, scene_objects, scene_cameras, anima, scene_nodes);
1424
1425 success = anima_node != 0;
993 1426
994cleanup: 1427cleanup:
1428 // The arrays of resources are no longer needed. The resources themselves are
1429 // destroyed only if this function fails.
995 if (tangent_buffers) { 1430 if (tangent_buffers) {
996 for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { 1431 if (!success) {
997 if (tangent_buffers[i]) { 1432 for (cgltf_size i = 0; i < num_tangent_buffers; ++i) {
998 gfx_destroy_buffer(render_backend, &tangent_buffers[i]); 1433 if (tangent_buffers[i]) {
1434 gfx_destroy_buffer(render_backend, &tangent_buffers[i]);
1435 }
999 } 1436 }
1000 } 1437 }
1001 free(tangent_buffers); 1438 free(tangent_buffers);
1002 } 1439 }
1003 if (buffers) { 1440 if (buffers) {
1004 for (cgltf_size i = 0; i < data->buffers_count; ++i) { 1441 if (!success) {
1005 if (buffers[i]) { 1442 for (cgltf_size i = 0; i < data->buffers_count; ++i) {
1006 gfx_destroy_buffer(render_backend, &buffers[i]); 1443 if (buffers[i]) {
1444 gfx_destroy_buffer(render_backend, &buffers[i]);
1445 }
1007 } 1446 }
1008 } 1447 }
1009 free(buffers); 1448 free(buffers);
@@ -1012,70 +1451,93 @@ cleanup:
1012 free(load_texture_cmds); 1451 free(load_texture_cmds);
1013 } 1452 }
1014 if (textures) { 1453 if (textures) {
1015 for (cgltf_size i = 0; i < data->textures_count; ++i) { 1454 if (!success) {
1016 if (textures[i]) { 1455 for (cgltf_size i = 0; i < data->textures_count; ++i) {
1017 gfx_destroy_texture(render_backend, &textures[i]); 1456 if (textures[i]) {
1457 gfx_destroy_texture(render_backend, &textures[i]);
1458 }
1018 } 1459 }
1019 } 1460 }
1020 free(textures); 1461 free(textures);
1021 } 1462 }
1022 if (materials) { 1463 if (materials) {
1023 for (cgltf_size i = 0; i < data->materials_count; ++i) { 1464 if (!success) {
1024 if (materials[i]) { 1465 for (cgltf_size i = 0; i < data->materials_count; ++i) {
1025 gfx_destroy_material(&materials[i]); 1466 if (materials[i]) {
1467 gfx_destroy_material(&materials[i]);
1468 }
1026 } 1469 }
1027 } 1470 }
1028 free(materials); 1471 free(materials);
1029 } 1472 }
1030 if (geometries) { 1473 if (geometries) {
1031 for (size_t i = 0; i < primitive_count; ++i) { 1474 if (!success) {
1032 if (geometries[i]) { 1475 for (size_t i = 0; i < primitive_count; ++i) {
1033 gfx_destroy_geometry(render_backend, &geometries[i]); 1476 if (geometries[i]) {
1477 gfx_destroy_geometry(render_backend, &geometries[i]);
1478 }
1034 } 1479 }
1035 } 1480 }
1036 free(geometries); 1481 free(geometries);
1037 } 1482 }
1038 if (meshes) { 1483 if (meshes) {
1039 for (size_t i = 0; i < primitive_count; ++i) { 1484 if (!success) {
1040 if (meshes[i]) { 1485 for (size_t i = 0; i < primitive_count; ++i) {
1041 gfx_destroy_mesh(&meshes[i]); 1486 if (meshes[i]) {
1487 gfx_destroy_mesh(&meshes[i]);
1488 }
1042 } 1489 }
1043 } 1490 }
1044 free(meshes); 1491 free(meshes);
1045 } 1492 }
1493 if (anima_desc) {
1494 free(anima_desc);
1495 }
1046 if (scene_objects) { 1496 if (scene_objects) {
1047 for (cgltf_size i = 0; i < data->meshes_count; ++i) { 1497 if (!success) {
1048 if (scene_objects[i]) { 1498 for (cgltf_size i = 0; i < data->meshes_count; ++i) {
1049 gfx_destroy_object(&scene_objects[i]); 1499 if (scene_objects[i]) {
1500 gfx_destroy_object(&scene_objects[i]);
1501 }
1050 } 1502 }
1051 } 1503 }
1052 free(scene_objects); 1504 free(scene_objects);
1053 } 1505 }
1054 if (scene_cameras) { 1506 if (scene_cameras) {
1055 for (cgltf_size i = 0; i < data->cameras_count; ++i) { 1507 if (!success) {
1056 if (scene_cameras[i]) { 1508 for (cgltf_size i = 0; i < data->cameras_count; ++i) {
1057 gfx_destroy_camera(&scene_cameras[i]); 1509 if (scene_cameras[i]) {
1510 gfx_destroy_camera(&scene_cameras[i]);
1511 }
1058 } 1512 }
1059 } 1513 }
1060 free(scene_cameras); 1514 free(scene_cameras);
1061 } 1515 }
1062 if (scene_nodes) { 1516 if (scene_nodes) {
1063 for (cgltf_size i = 0; i < data->nodes_count; ++i) { 1517 if (!success) {
1064 if (scene_nodes[i]) { 1518 for (cgltf_size i = 0; i < data->nodes_count; ++i) {
1065 gfx_destroy_node(&scene_nodes[i]); 1519 if (scene_nodes[i]) {
1520 gfx_destroy_node(&scene_nodes[i]);
1521 }
1066 } 1522 }
1067 } 1523 }
1068 free(scene_nodes); 1524 free(scene_nodes);
1069 } 1525 }
1070 return false; 1526 if (!success && anima_node) {
1527 gfx_destroy_node(&anima_node); // Node owns the anima.
1528 } else if (!success && anima) {
1529 gfx_destroy_anima(&anima);
1530 }
1531 return anima_node;
1071} 1532}
1072 1533
1073bool gfx_load_scene(Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) { 1534SceneNode* gfx_load_scene(
1535 Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) {
1074 assert(gfx); 1536 assert(gfx);
1075 assert(root_node); 1537 assert(root_node);
1076 assert(cmd); 1538 assert(cmd);
1077 1539
1078 bool success = false; 1540 SceneNode* scene_node = 0;
1079 1541
1080 cgltf_options options = {0}; 1542 cgltf_options options = {0};
1081 cgltf_data* data = NULL; 1543 cgltf_data* data = NULL;
@@ -1107,7 +1569,7 @@ bool gfx_load_scene(Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) {
1107 cgltf_compute_tangents( 1569 cgltf_compute_tangents(
1108 &options, data, &tangent_buffers, &num_tangent_buffers); 1570 &options, data, &tangent_buffers, &num_tangent_buffers);
1109 1571
1110 success = load_scene( 1572 scene_node = load_scene(
1111 data, gfx, root_node, cmd->filepath, cmd->shader, tangent_buffers, 1573 data, gfx, root_node, cmd->filepath, cmd->shader, tangent_buffers,
1112 num_tangent_buffers); 1574 num_tangent_buffers);
1113 1575
@@ -1118,5 +1580,5 @@ cleanup:
1118 if (tangent_buffers) { 1580 if (tangent_buffers) {
1119 free(tangent_buffers); 1581 free(tangent_buffers);
1120 } 1582 }
1121 return success; 1583 return scene_node;
1122} 1584}
diff --git a/gltfview/src/game.c b/gltfview/src/game.c
index 612ec67..1db7cba 100644
--- a/gltfview/src/game.c
+++ b/gltfview/src/game.c
@@ -89,13 +89,13 @@ static SceneNode* load_skyquad(Game* game) {
89} 89}
90 90
91/// Load the 3D scene. 91/// Load the 3D scene.
92static bool load_scene( 92static SceneNode* load_scene(
93 Game* game, const char* scene_filepath, const char* view_mode) { 93 Game* game, const char* scene_filepath, const char* view_mode) {
94 assert(game); 94 assert(game);
95 95
96 game->camera = gfx_make_camera(); 96 game->camera = gfx_make_camera();
97 if (!game->camera) { 97 if (!game->camera) {
98 return false; 98 return 0;
99 } 99 }
100 Camera* camera = gfx_get_camera_camera(game->camera); 100 Camera* camera = gfx_get_camera_camera(game->camera);
101 // Sponza. 101 // Sponza.
@@ -105,7 +105,7 @@ static bool load_scene(
105 105
106 SceneNode* sky_light_node = load_skyquad(game); 106 SceneNode* sky_light_node = load_skyquad(game);
107 if (!sky_light_node) { 107 if (!sky_light_node) {
108 return false; 108 return 0;
109 } 109 }
110 110
111 // TODO: Move the debug rendering to the renderer. 111 // TODO: Move the debug rendering to the renderer.
@@ -114,16 +114,16 @@ static bool load_scene(
114 // return false; 114 // return false;
115 // } 115 // }
116 116
117 if (!gfx_load_scene( 117 SceneNode* scene_node = gfx_load_scene(
118 game->gfx, sky_light_node, 118 game->gfx, sky_light_node,
119 &(LoadSceneCmd){ 119 &(LoadSceneCmd){.origin = SceneFromFile, .filepath = scene_filepath});
120 .origin = SceneFromFile, .filepath = scene_filepath})) { 120 if (!scene_node) {
121 return false; 121 return 0;
122 } 122 }
123 123
124 gfx_log_node_hierarchy(gfx_get_scene_root(game->scene)); 124 gfx_log_node_hierarchy(gfx_get_scene_root(game->scene));
125 125
126 return true; 126 return scene_node;
127} 127}
128 128
129/// Load a scene for debugging textures. 129/// Load a scene for debugging textures.
@@ -207,13 +207,23 @@ bool game_new(Game* game, int argc, const char** argv) {
207 goto cleanup; 207 goto cleanup;
208 } 208 }
209 209
210 if (!load_scene(game, scene_filepath, view_mode)) { 210 game->root_node = load_scene(game, scene_filepath, view_mode);
211 if (!game->root_node) {
211 goto cleanup; 212 goto cleanup;
212 } 213 }
213 /*if (!load_texture_debugger_scene(game)) { 214 /*if (!load_texture_debugger_scene(game)) {
214 goto cleanup; 215 goto cleanup;
215 }*/ 216 }*/
216 217
218 Anima* anima = gfx_get_node_anima(game->root_node);
219
220 // const bool play_result = gfx_play_animation(
221 // anima, &(AnimationPlaySettings){.name = "Defiant stance", .loop =
222 // false});
223 const bool play_result = gfx_play_animation(
224 anima, &(AnimationPlaySettings){.name = "Walk", .loop = true});
225 assert(play_result);
226
217 return true; 227 return true;
218 228
219cleanup: 229cleanup:
@@ -227,12 +237,11 @@ cleanup:
227void game_end(Game* game) { gfx_destroy(&game->gfx); } 237void game_end(Game* game) { gfx_destroy(&game->gfx); }
228 238
229void game_update(Game* game, double t, double dt) { 239void game_update(Game* game, double t, double dt) {
230 game->elapsed += dt; 240 // LOGD("Tick");
231 while (game->elapsed >= 1.0) { 241
232 // LOGD("Tick"); 242 Anima* anima = gfx_get_node_anima(game->root_node);
233 usleep(1000); 243 gfx_update_animation(anima, t);
234 game->elapsed -= 1.0; 244
235 }
236 // TODO: Compute bounding boxes to then find a good orbit point and radius 245 // TODO: Compute bounding boxes to then find a good orbit point and radius
237 // for each scene. 246 // for each scene.
238 const vec3 orbit_point = vec3_make(0, 2, 0); 247 const vec3 orbit_point = vec3_make(0, 2, 0);
diff --git a/gltfview/src/game.h b/gltfview/src/game.h
index 92c0885..4aeb5ea 100644
--- a/gltfview/src/game.h
+++ b/gltfview/src/game.h
@@ -4,6 +4,7 @@
4#include <gfx/render_backend.h> 4#include <gfx/render_backend.h>
5#include <gfx/renderer.h> 5#include <gfx/renderer.h>
6#include <gfx/scene/camera.h> 6#include <gfx/scene/camera.h>
7#include <gfx/scene/node.h>
7#include <gfx/scene/scene.h> 8#include <gfx/scene/scene.h>
8 9
9#include <stdbool.h> 10#include <stdbool.h>
@@ -13,12 +14,12 @@ static const double game_dt = 1.0 / 60.0;
13 14
14/// Game state. 15/// Game state.
15typedef struct { 16typedef struct {
16 Gfx* gfx; 17 Gfx* gfx;
17 RenderBackend* render_backend; 18 RenderBackend* render_backend;
18 Renderer* renderer; 19 Renderer* renderer;
19 Scene* scene; 20 Scene* scene;
20 SceneCamera* camera; 21 SceneCamera* camera;
21 double elapsed; 22 SceneNode* root_node;
22} Game; 23} Game;
23 24
24bool game_new(Game*, int argc, const char** argv); 25bool game_new(Game*, int argc, const char** argv);