diff options
author | 3gg <3gg@shellblade.net> | 2023-02-04 14:36:02 -0800 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2023-02-04 14:36:02 -0800 |
commit | 8068d0a816b3efd17ebb0dcf468c6d333e3577d3 (patch) | |
tree | f88f0845c34efbb1c43f88ea71fcbf32ea5adae9 | |
parent | f319f0bb59f5e05bd06dcd160699f2b0f2f14e8a (diff) |
Add support for skeletal animation.
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 | ||
7 | Scene *-- Camera | ||
8 | Scene *-- Node | 7 | Scene *-- Node |
9 | 8 | ||
10 | class Camera { | 9 | class Camera { |
@@ -15,11 +14,41 @@ class Node { | |||
15 | + transform | 14 | + transform |
16 | } | 15 | } |
17 | 16 | ||
17 | Node *-- AnimationObject | ||
18 | Node *-- Object | 18 | Node *-- Object |
19 | Node o-- Light : "affected by" | 19 | Node *-- Light |
20 | Node o-- Camera | 20 | Node *-- Camera |
21 | Node *-- Node | 21 | Node *-- Node |
22 | 22 | ||
23 | class AnimationObject { | ||
24 | |||
25 | } | ||
26 | |||
27 | AnimationObject *-- Animation | ||
28 | AnimationObject --> AnimationState | ||
29 | AnimationObject --> Skeleton | ||
30 | |||
31 | class AnimationState { | ||
32 | + time | ||
33 | + current_animation | ||
34 | + pose | ||
35 | } | ||
36 | |||
37 | class Animation { | ||
38 | + name | ||
39 | } | ||
40 | |||
41 | Animation *-- Keyframe | ||
42 | |||
43 | class Keyframe { | ||
44 | + time | ||
45 | + transforms | ||
46 | } | ||
47 | |||
48 | class Skeleton { | ||
49 | + joints | ||
50 | } | ||
51 | |||
23 | class Object { | 52 | class Object { |
24 | + transform | 53 | + transform |
25 | } | 54 | } |
@@ -33,6 +62,7 @@ class Mesh { | |||
33 | Mesh --> BoundingVolume | 62 | Mesh --> BoundingVolume |
34 | Mesh --> Geometry | 63 | Mesh --> Geometry |
35 | Mesh --> Material | 64 | Mesh --> Material |
65 | Mesh --> Shader | ||
36 | 66 | ||
37 | class Geometry { | 67 | class Geometry { |
38 | + positions | 68 | + positions |
@@ -49,7 +79,6 @@ class Material { | |||
49 | + shader params | 79 | + shader params |
50 | } | 80 | } |
51 | 81 | ||
52 | Material --> Shader | ||
53 | Material o-- Texture | 82 | Material o-- Texture |
54 | 83 | ||
55 | class Shader { | 84 | class 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. | ||
52 | MAKE_BUFFER_VIEW(BufferView, void) | ||
53 | |||
51 | /// A buffer view for 2D vectors. | 54 | /// A buffer view for 2D vectors. |
52 | MAKE_BUFFER_VIEW(BufferView2d, vec2) | 55 | MAKE_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. |
58 | MAKE_BUFFER_VIEW(BufferView4d, vec4) | 61 | MAKE_BUFFER_VIEW(BufferView4d, vec4) |
59 | 62 | ||
63 | /// A buffer view for floats. | ||
64 | MAKE_BUFFER_VIEW(BufferViewFloat, float) | ||
65 | |||
66 | /// A buffer view for 8-bit unsigned integers. | ||
67 | MAKE_BUFFER_VIEW(BufferViewU8, uint8_t) | ||
68 | |||
69 | /// A buffer view for 16-bit unsigned integers. | ||
70 | MAKE_BUFFER_VIEW(BufferViewU16, uint16_t) | ||
71 | |||
60 | /// A buffer view for vertex indices. | 72 | /// A buffer view for vertex indices. |
61 | MAKE_BUFFER_VIEW(BufferViewIdx, uint16_t) | 73 | MAKE_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. | ||
64 | typedef struct GeometryDesc { | 79 | typedef 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. | ||
109 | typedef struct ShaderUniform { | 137 | typedef 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. |
237 | Buffer* gfx_make_buffer4d(RenderBackend*, const vec4* verts, size_t count); | 271 | Buffer* gfx_make_buffer4d(RenderBackend*, const vec4* verts, size_t count); |
238 | 272 | ||
273 | /// Create a buffer from 8-bit unsigned integers. | ||
274 | Buffer* gfx_make_bufferu8(RenderBackend*, const uint8_t* vals, size_t count); | ||
275 | |||
276 | /// Create a buffer from 16-bit unsigned integers. | ||
277 | Buffer* gfx_make_bufferu16(RenderBackend*, const uint16_t* vals, size_t count); | ||
278 | |||
239 | /// Destroy the buffer. | 279 | /// Destroy the buffer. |
240 | void gfx_destroy_buffer(RenderBackend*, Buffer**); | 280 | void 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. |
355 | void gfx_set_float_uniform(ShaderProgram*, const char* name, float value); | 395 | void 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. | ||
399 | void 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 | ||
15 | Scene graphs typically expose functions on nodes to add/remove objects, cameras, | 15 | Scene graphs typically expose functions on nodes to add/remove objects, cameras, |
16 | lights, etc. This implementation forces the hierarchy to be a strict tree and | 16 | lights, 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 | |||
31 | Two use cases for instancing seem to be: | ||
32 | |||
33 | 1. Creating N identical clones, but each with a unique transform. (Ex: N | ||
34 | animated characters animated in unison but located in different locations.) | ||
35 | 2. Creating N copies of a sub-tree, each now being their own unique tree. (Ex: | ||
36 | The same N animated characters, but each of them now being animated separately.) | ||
37 | |||
38 | Some scene graphs | ||
39 | ([Panda3D](https://docs.panda3d.org/1.10/python/programming/scene-graph/instancing)) | ||
40 | allow two or more nodes to point to the same child, or, in other words, a node | ||
41 | to have multiple parents. This turns the scene graph into a DAG and adds a | ||
42 | number of complications for us: | ||
43 | |||
44 | 1. Shared ownership of children. We would now need some sort of ref counting or | ||
45 | deferred GC to delete nodes and their subtrees. | ||
46 | 2. Nodes no longer have a unique parent. | ||
47 | 3. Given a node, we can no longer determine its location (which parent link do | ||
48 | you follow?), or any attribute that is derived from its parent(s). | ||
49 | |||
50 | In our case, we stick to strict tree hierarchies. | ||
51 | |||
52 | Use case (1), N identical clones with unique transforms, is not a problem for | ||
53 | us. This is because the bulk of the data -- geometry buffers, etc. -- is stored | ||
54 | in the render backend anyway. So creating a full copy of the node does not | ||
55 | present a significant overhead since we need a unique transform for each of the | ||
56 | clones anyway. | ||
57 | |||
58 | Use case (2) does present a bit more overhead and we currently do not handle it. | ||
59 | This 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 | ||
61 | attributes), one for each unique instance of that child subtree. | ||
62 | |||
63 | Therefore, to visit the use cases again: | ||
64 | |||
65 | 1. N character clones animated in unison in different locations -> future | ||
66 | `InstanceNode`. | ||
67 | 2. 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 | |||
17 | typedef struct Buffer Buffer; | ||
18 | typedef struct SceneNode SceneNode; | ||
19 | |||
20 | typedef struct Anima Anima; | ||
21 | typedef struct Joint Joint; | ||
22 | // TODO: Remove this when removing gfx_get_anima_skeleton(). | ||
23 | typedef struct Skeleton Skeleton; | ||
24 | |||
25 | /// Joint descriptor. | ||
26 | typedef 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. | ||
36 | typedef 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. | ||
42 | typedef enum AnimationInterpolation { | ||
43 | StepInterpolation, | ||
44 | LinearInterpolation, | ||
45 | CubicSplineInterpolation | ||
46 | } AnimationInterpolation; | ||
47 | |||
48 | /// The kind of transformation applied by a Channel. | ||
49 | typedef 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. | ||
59 | typedef 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. | ||
68 | typedef 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. | ||
77 | typedef 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. | ||
86 | typedef 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. | ||
94 | typedef 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. | ||
101 | Joint* gfx_make_joint(const JointDesc*); | ||
102 | |||
103 | /// Destroy the joint. | ||
104 | void gfx_destroy_joint(Joint**); | ||
105 | |||
106 | /// Create an anima object. | ||
107 | /// | ||
108 | /// The anima owns its skeletons and animations. | ||
109 | Anima* gfx_make_anima(const AnimaDesc*); | ||
110 | |||
111 | /// Destroy the anima. | ||
112 | void gfx_destroy_anima(Anima**); | ||
113 | |||
114 | /// Play an animation (sets the current animation). | ||
115 | bool gfx_play_animation(Anima*, const AnimationPlaySettings*); | ||
116 | |||
117 | /// Update the current animation. | ||
118 | void gfx_update_animation(Anima*, R t); | ||
119 | |||
120 | /// Stop the current animation. | ||
121 | void 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. | ||
127 | const 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 | ||
6 | typedef struct Anima Anima; | ||
7 | typedef struct Joint Joint; | ||
5 | typedef struct Light Light; | 8 | typedef struct Light Light; |
6 | typedef struct SceneCamera SceneCamera; | 9 | typedef struct SceneCamera SceneCamera; |
7 | typedef struct SceneObject SceneObject; | 10 | typedef struct SceneObject SceneObject; |
8 | 11 | ||
12 | /// Scene node type. | ||
13 | typedef 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. | ||
9 | typedef struct SceneNode SceneNode; | 26 | typedef 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. |
15 | SceneNode* gfx_make_node(); | 32 | SceneNode* gfx_make_node(); |
16 | 33 | ||
34 | /// Create an anima node. | ||
35 | SceneNode* gfx_make_anima_node(Anima*); | ||
36 | |||
17 | /// Create a new camera node. | 37 | /// Create a new camera node. |
18 | SceneNode* gfx_make_camera_node(SceneCamera*); | 38 | SceneNode* gfx_make_camera_node(SceneCamera*); |
19 | 39 | ||
40 | /// Create a joint node. | ||
41 | SceneNode* gfx_make_joint_node(Joint*); | ||
42 | |||
20 | /// Create a new light node. | 43 | /// Create a new light node. |
21 | SceneNode* gfx_make_light_node(Light*); | 44 | SceneNode* gfx_make_light_node(Light*); |
22 | 45 | ||
23 | /// Create a new object node. | 46 | /// Create a new object node. |
24 | SceneNode* gfx_make_object_node(SceneObject*); | 47 | SceneNode* gfx_make_object_node(SceneObject*); |
25 | 48 | ||
49 | /// Make the node an anima node. | ||
50 | void gfx_construct_anima_node(SceneNode*, Anima*); | ||
51 | |||
52 | /// Make the node a camera node. | ||
53 | void gfx_construct_camera_node(SceneNode*, SceneCamera*); | ||
54 | |||
55 | /// Make the node a joint node. | ||
56 | void gfx_construct_joint_node(SceneNode*, Joint*); | ||
57 | |||
58 | /// Make the node a light node. | ||
59 | void gfx_construct_light_node(SceneNode*, Light*); | ||
60 | |||
61 | /// Make the node an object node. | ||
62 | void 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. |
31 | void gfx_destroy_node(SceneNode**); | 69 | void gfx_destroy_node(SceneNode**); |
32 | 70 | ||
71 | /// Return the node's type. | ||
72 | NodeType gfx_get_node_type(const SceneNode*); | ||
73 | |||
74 | /// Get the node's anima. | ||
75 | /// | ||
76 | /// The node must be of type AnimaNode. | ||
77 | Anima* 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. |
36 | void gfx_set_node_parent(SceneNode*, SceneNode* parent); | 82 | void gfx_set_node_parent(SceneNode*, SceneNode* parent); |
37 | 83 | ||
38 | /// Set the node's transform. | 84 | /// Set the node's (local) transform. |
39 | void gfx_set_node_transform(SceneNode*, const mat4* transform); | 85 | void gfx_set_node_transform(SceneNode*, const mat4* transform); |
40 | 86 | ||
41 | /// Set the node's position. | 87 | /// Set the node's position. |
42 | void gfx_set_node_position(SceneNode*, const vec3* position); | 88 | void gfx_set_node_position(SceneNode*, const vec3* position); |
43 | 89 | ||
44 | /// Set the node's rotation. | 90 | /// Set the node's rotation. |
45 | void gfx_set_node_rotation(SceneNode*, const mat4* rotation); | 91 | void gfx_set_node_rotation(SceneNode*, const quat* rotation); |
92 | |||
93 | /// Set the node's rotation. | ||
94 | void gfx_set_node_rotation_mat(SceneNode*, const mat4* rotation); | ||
95 | |||
96 | /// Get the node's (local) transform. | ||
97 | mat4 gfx_get_node_transform(const SceneNode*); | ||
98 | |||
99 | /// Get the node's global transform. | ||
100 | mat4 gfx_get_node_global_transform(const SceneNode*); | ||
46 | 101 | ||
47 | /// Log the node's hierarchy. | 102 | /// Log the node's hierarchy. |
48 | void gfx_log_node_hierarchy(const SceneNode*); | 103 | void 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 | ||
5 | typedef struct Mesh Mesh; | 5 | typedef struct Mesh Mesh; |
6 | typedef struct SceneNode SceneNode; | 6 | typedef struct SceneNode SceneNode; |
7 | typedef struct Skeleton Skeleton; | ||
7 | 8 | ||
8 | typedef struct SceneObject SceneObject; | 9 | typedef 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. |
26 | void gfx_remove_object_mesh(SceneObject*, Mesh*); | 27 | void gfx_remove_object_mesh(SceneObject*, Mesh*); |
28 | |||
29 | /// Set the object's skeleton. | ||
30 | void 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. |
34 | bool gfx_load_scene(Gfx*, SceneNode* root_node, const LoadSceneCmd*); | 35 | SceneNode* 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 @@ | |||
1 | precision highp float; | 1 | precision highp float; |
2 | 2 | ||
3 | uniform mat4 ModelMatrix; | 3 | uniform mat4 ModelMatrix; |
4 | uniform mat4 MVP; | 4 | uniform mat4 Modelview; |
5 | uniform 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]; | ||
19 | uniform mat4 JointMatrices[MAX_JOINTS]; // Use 4x4 for now to keep it simple. | ||
20 | #endif | ||
5 | 21 | ||
6 | layout (location = 0) in vec3 vPosition; | 22 | layout (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 |
11 | layout (location = 2) in vec4 vTangent; | 27 | layout (location = 2) in vec4 vTangent; |
12 | #endif // HAS_TANGENTS | 28 | #endif |
13 | #ifdef HAS_TEXCOORDS | 29 | #ifdef HAS_TEXCOORDS |
14 | layout (location = 3) in vec2 vTexcoord; | 30 | layout (location = 3) in vec2 vTexcoord; |
15 | #endif | 31 | #endif |
32 | #ifdef HAS_JOINTS | ||
33 | layout (location = 4) in uvec4 vJoint; | ||
34 | layout (location = 5) in vec4 vWeight; | ||
35 | #endif | ||
16 | 36 | ||
17 | // World-space position and normal. | 37 | // World-space position and normal. |
18 | out vec3 Position; | 38 | out vec3 Position; |
@@ -28,7 +48,16 @@ out vec2 Texcoord; | |||
28 | 48 | ||
29 | void main() | 49 | void 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 | ||
25 | static 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. | ||
29 | static 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 | |||
35 | static 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 | ||
45 | static 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) { | 39 | static 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); | |
55 | static 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 | ||
64 | bool gfx_init_geometry(Geometry* geometry, RenderBackend* render_backend, | 75 | bool 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. |
15 | typedef struct Geometry { | 15 | typedef 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 | ||
113 | Buffer* gfx_make_buffer2d( | 113 | Buffer* 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 | ||
127 | Buffer* gfx_make_buffer3d( | 119 | Buffer* 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 | ||
141 | Buffer* gfx_make_buffer4d( | 125 | Buffer* 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 | } | 131 | Buffer* 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 | |
137 | Buffer* 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 | ||
155 | void gfx_destroy_buffer(RenderBackend* render_backend, Buffer** buffer) { | 143 | void 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 { | |||
29 | typedef struct RenderBackend { | 29 | typedef 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 | ||
44 | bool gfx_build_shader_program(ShaderProgram* prog, | 44 | bool 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 | ||
73 | static void set_texture_uniform(GLuint prog, const char* name, int texture_unit, | 73 | static 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 | ||
86 | static void set_mat4_uniform(GLuint prog, const char* name, const mat4* mat) { | 86 | static 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 | // | 159 | static 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. | ||
155 | static 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 | ||
180 | void gfx_set_texture_uniform(ShaderProgram* prog, const char* name, | 185 | void 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 | ||
198 | void gfx_set_mat4_uniform(ShaderProgram* prog, const char* name, | 201 | void 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 | |||
261 | void 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. | ||
127 | static 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. | ||
124 | static void draw_recursively( | 147 | static 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 | |||
8 | static const R PLAYBACK_UNINITIALIZED = -1; | ||
9 | |||
10 | Joint* 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 | |||
23 | void 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 | |||
33 | static 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 | |||
46 | static 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 | |||
85 | Anima* 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 | |||
127 | void 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 | |||
150 | static 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 | |||
169 | bool 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 | |||
187 | static 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 | |||
200 | static 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 | |||
206 | static 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 | |||
235 | static 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 | |||
264 | static 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 | |||
301 | static 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 | |||
336 | void 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 | |||
391 | const 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 | |||
17 | typedef struct Buffer Buffer; | ||
18 | |||
19 | // Currently ignoring scale in skinning and animation. | ||
20 | // | ||
21 | // TODO: Simultaneous animation of disjoint animations. | ||
22 | |||
23 | typedef 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. | ||
40 | typedef 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. | ||
47 | typedef 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. | ||
56 | typedef 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. | ||
65 | typedef 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. | ||
76 | typedef 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. | ||
90 | typedef 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 | ||
29 | SceneNode* 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 | |||
29 | SceneNode* gfx_make_camera_node(SceneCamera* camera) { | 41 | SceneNode* 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 | ||
53 | SceneNode* 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 | |||
41 | SceneNode* gfx_make_light_node(Light* light) { | 65 | SceneNode* 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. | ||
90 | static 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 | |||
131 | void 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 | |||
141 | void 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. | ||
153 | void 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 | |||
163 | void 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 | |||
173 | void 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 | |||
65 | static void destroy_node_rec(SceneNode* node) { | 183 | static 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 | ||
225 | NodeType gfx_get_node_type(const SceneNode* node) { | ||
226 | assert(node); | ||
227 | return node->type; | ||
228 | } | ||
229 | |||
230 | Anima* gfx_get_node_anima(const SceneNode* node) { | ||
231 | assert(node); | ||
232 | assert(node->type == AnimaNode); | ||
233 | return mem_get_anima(node->anima); | ||
234 | } | ||
235 | |||
121 | void gfx_set_node_parent(SceneNode* child, SceneNode* parent) { | 236 | void 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 | ||
139 | void gfx_set_node_rotation(SceneNode* node, const mat4* rotation) { | 254 | void 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 | |||
260 | void 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 | ||
266 | mat4 gfx_get_node_transform(const SceneNode* node) { | ||
267 | assert(node); | ||
268 | return node->transform; | ||
269 | } | ||
270 | |||
271 | mat4 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 | |||
283 | static 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 | |||
145 | static void log_node_hierarchy_rec(const SceneNode* node, const sstring* pad) { | 302 | static 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. | ||
10 | typedef 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 { | |||
21 | typedef struct SceneNode { | 14 | typedef 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 | |||
73 | void 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 | ||
9 | typedef struct MeshLink { | 9 | typedef 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. |
21 | typedef struct SceneObject { | 21 | typedef 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 | ||
17 | DEF_MEMPOOL(anima_pool, Anima, GFX_MAX_NUM_ANIMAS) | ||
18 | DEF_MEMPOOL(animation_pool, Animation, GFX_MAX_NUM_ANIMATIONS) | ||
16 | DEF_MEMPOOL(camera_pool, SceneCamera, GFX_MAX_NUM_CAMERAS) | 19 | DEF_MEMPOOL(camera_pool, SceneCamera, GFX_MAX_NUM_CAMERAS) |
20 | DEF_MEMPOOL(joint_pool, Joint, GFX_MAX_NUM_JOINTS) | ||
17 | DEF_MEMPOOL(light_pool, Light, GFX_MAX_NUM_LIGHTS) | 21 | DEF_MEMPOOL(light_pool, Light, GFX_MAX_NUM_LIGHTS) |
18 | DEF_MEMPOOL(material_pool, Material, GFX_MAX_NUM_MATERIALS) | 22 | DEF_MEMPOOL(material_pool, Material, GFX_MAX_NUM_MATERIALS) |
19 | DEF_MEMPOOL(mesh_pool, Mesh, GFX_MAX_NUM_MESHES) | 23 | DEF_MEMPOOL(mesh_pool, Mesh, GFX_MAX_NUM_MESHES) |
@@ -21,12 +25,16 @@ DEF_MEMPOOL(mesh_link_pool, MeshLink, GFX_MAX_NUM_MESH_LINKS) | |||
21 | DEF_MEMPOOL(node_pool, SceneNode, GFX_MAX_NUM_NODES) | 25 | DEF_MEMPOOL(node_pool, SceneNode, GFX_MAX_NUM_NODES) |
22 | DEF_MEMPOOL(object_pool, SceneObject, GFX_MAX_NUM_OBJECTS) | 26 | DEF_MEMPOOL(object_pool, SceneObject, GFX_MAX_NUM_OBJECTS) |
23 | DEF_MEMPOOL(scene_pool, Scene, GFX_MAX_NUM_SCENES) | 27 | DEF_MEMPOOL(scene_pool, Scene, GFX_MAX_NUM_SCENES) |
28 | DEF_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. |
28 | typedef struct SceneMemory { | 33 | typedef 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 | ||
39 | static SceneMemory mem; | 48 | static 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 | ||
44 | void scene_mem_init() { | 53 | void 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 | ||
66 | void scene_mem_destroy() { | 83 | void 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. |
145 | Anima* mem_alloc_anima() { return mempool_alloc(&mem.animas); } | ||
146 | Animation* mem_alloc_animation() { return mempool_alloc(&mem.animations); } | ||
114 | SceneCamera* mem_alloc_camera() { return mempool_alloc(&mem.cameras); } | 147 | SceneCamera* mem_alloc_camera() { return mempool_alloc(&mem.cameras); } |
148 | Joint* mem_alloc_joint() { return mempool_alloc(&mem.joints); } | ||
115 | Light* mem_alloc_light() { return mempool_alloc(&mem.lights); } | 149 | Light* mem_alloc_light() { return mempool_alloc(&mem.lights); } |
116 | Material* mem_alloc_material() { return mempool_alloc(&mem.materials); } | 150 | Material* mem_alloc_material() { return mempool_alloc(&mem.materials); } |
117 | Mesh* mem_alloc_mesh() { return mempool_alloc(&mem.meshes); } | 151 | Mesh* mem_alloc_mesh() { return mempool_alloc(&mem.meshes); } |
@@ -119,11 +153,17 @@ MeshLink* mem_alloc_mesh_link() { return mempool_alloc(&mem.mesh_links); } | |||
119 | SceneNode* mem_alloc_node() { return mempool_alloc(&mem.nodes); } | 153 | SceneNode* mem_alloc_node() { return mempool_alloc(&mem.nodes); } |
120 | SceneObject* mem_alloc_object() { return mempool_alloc(&mem.objects); } | 154 | SceneObject* mem_alloc_object() { return mempool_alloc(&mem.objects); } |
121 | Scene* mem_alloc_scene() { return mempool_alloc(&mem.scenes); } | 155 | Scene* mem_alloc_scene() { return mempool_alloc(&mem.scenes); } |
156 | Skeleton* mem_alloc_skeleton() { return mempool_alloc(&mem.skeletons); } | ||
122 | 157 | ||
123 | // Memory free. | 158 | // Memory free. |
159 | void mem_free_anima(Anima** anima) { mempool_free(&mem.animas, anima); } | ||
160 | void mem_free_animation(Animation** animation) { | ||
161 | mempool_free(&mem.animations, animation); | ||
162 | } | ||
124 | void mem_free_camera(SceneCamera** camera) { | 163 | void mem_free_camera(SceneCamera** camera) { |
125 | mempool_free(&mem.cameras, camera); | 164 | mempool_free(&mem.cameras, camera); |
126 | } | 165 | } |
166 | void mem_free_joint(Joint** joint) { mempool_free(&mem.joints, joint); } | ||
127 | void mem_free_light(Light** light) { mempool_free(&mem.lights, light); } | 167 | void mem_free_light(Light** light) { mempool_free(&mem.lights, light); } |
128 | void mem_free_material(Material** material) { | 168 | void 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 | } |
139 | void mem_free_scene(Scene** scene) { mempool_free(&mem.scenes, scene); } | 179 | void mem_free_scene(Scene** scene) { mempool_free(&mem.scenes, scene); } |
180 | void mem_free_skeleton(Skeleton** skeleton) { | ||
181 | mempool_free(&mem.skeletons, skeleton); | ||
182 | } | ||
140 | 183 | ||
141 | // Query by index. | 184 | // Query by index. |
185 | Anima* mem_get_anima(anima_idx index) { | ||
186 | return mempool_get_block(&mem.animas, index.val); | ||
187 | } | ||
188 | Animation* mem_get_animation(animation_idx index) { | ||
189 | return mempool_get_block(&mem.animations, index.val); | ||
190 | } | ||
142 | SceneCamera* mem_get_camera(camera_idx index) { | 191 | SceneCamera* 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 | } |
194 | Joint* mem_get_joint(joint_idx index) { | ||
195 | return mempool_get_block(&mem.joints, index.val); | ||
196 | } | ||
145 | Light* mem_get_light(light_idx index) { | 197 | Light* 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) { | |||
163 | Scene* mem_get_scene(scene_idx index) { | 215 | Scene* 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 | } |
218 | Skeleton* 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. |
223 | anima_idx mem_get_anima_index(const Anima* anima) { | ||
224 | return (anima_idx){.val = mempool_get_block_index(&mem.animas, anima)}; | ||
225 | } | ||
226 | animation_idx mem_get_animation_index(const Animation* animation) { | ||
227 | return (animation_idx){ | ||
228 | .val = mempool_get_block_index(&mem.animations, animation)}; | ||
229 | } | ||
168 | camera_idx mem_get_camera_index(const SceneCamera* camera) { | 230 | camera_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 | } |
233 | joint_idx mem_get_joint_index(const Joint* joint) { | ||
234 | return (joint_idx){.val = mempool_get_block_index(&mem.joints, joint)}; | ||
235 | } | ||
171 | light_idx mem_get_light_index(const Light* light) { | 236 | light_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) { | |||
191 | scene_idx mem_get_scene_index(const Scene* scene) { | 256 | scene_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 | } |
259 | skeleton_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 | ||
7 | typedef struct Anima Anima; | ||
8 | typedef struct Animation Animation; | ||
9 | typedef struct Joint Joint; | ||
6 | typedef struct Light Light; | 10 | typedef struct Light Light; |
7 | typedef struct Material Material; | 11 | typedef struct Material Material; |
8 | typedef struct Mesh Mesh; | 12 | typedef struct Mesh Mesh; |
@@ -11,6 +15,7 @@ typedef struct SceneCamera SceneCamera; | |||
11 | typedef struct SceneNode SceneNode; | 15 | typedef struct SceneNode SceneNode; |
12 | typedef struct SceneObject SceneObject; | 16 | typedef struct SceneObject SceneObject; |
13 | typedef struct Scene Scene; | 17 | typedef struct Scene Scene; |
18 | typedef 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. | ||
34 | Anima* mem_alloc_anima(); | ||
35 | |||
36 | /// Allocate space for an animation. | ||
37 | Animation* mem_alloc_animation(); | ||
38 | |||
28 | /// Allocate space for a camera. | 39 | /// Allocate space for a camera. |
29 | SceneCamera* mem_alloc_camera(); | 40 | SceneCamera* mem_alloc_camera(); |
30 | 41 | ||
42 | /// Allocate space for a joint. | ||
43 | Joint* mem_alloc_joint(); | ||
44 | |||
31 | /// Allocate space for a light. | 45 | /// Allocate space for a light. |
32 | Light* mem_alloc_light(); | 46 | Light* 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. |
50 | Scene* mem_alloc_scene(); | 64 | Scene* mem_alloc_scene(); |
51 | 65 | ||
66 | /// Allocate space for a skeleton. | ||
67 | Skeleton* mem_alloc_skeleton(); | ||
68 | |||
69 | /// Free the anima. | ||
70 | void mem_free_anima(Anima**); | ||
71 | |||
72 | /// Free the animation. | ||
73 | void mem_free_animation(Animation**); | ||
74 | |||
52 | /// Free the camera. | 75 | /// Free the camera. |
53 | void mem_free_camera(SceneCamera**); | 76 | void mem_free_camera(SceneCamera**); |
54 | 77 | ||
78 | /// Free the joint. | ||
79 | void mem_free_joint(Joint**); | ||
80 | |||
55 | /// Free the light. | 81 | /// Free the light. |
56 | void mem_free_light(Light**); | 82 | void mem_free_light(Light**); |
57 | 83 | ||
@@ -73,13 +99,25 @@ void mem_free_object(SceneObject**); | |||
73 | /// Free the scene. | 99 | /// Free the scene. |
74 | void mem_free_scene(Scene**); | 100 | void mem_free_scene(Scene**); |
75 | 101 | ||
102 | /// Free the skeleton. | ||
103 | void 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. | ||
110 | Anima* mem_get_anima(anima_idx); | ||
111 | |||
112 | /// Get an animation by index. | ||
113 | Animation* mem_get_animation(animation_idx); | ||
114 | |||
80 | /// Get a camera by index. | 115 | /// Get a camera by index. |
81 | SceneCamera* mem_get_camera(camera_idx); | 116 | SceneCamera* mem_get_camera(camera_idx); |
82 | 117 | ||
118 | /// Get a joint by index. | ||
119 | Joint* mem_get_joint(joint_idx); | ||
120 | |||
83 | /// Get a light by index. | 121 | /// Get a light by index. |
84 | Light* mem_get_light(light_idx); | 122 | Light* 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. |
102 | Scene* mem_get_scene(scene_idx); | 140 | Scene* mem_get_scene(scene_idx); |
103 | 141 | ||
142 | /// Get a skeleton by index. | ||
143 | Skeleton* 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. | ||
150 | anima_idx mem_get_anima_index(const Anima*); | ||
151 | |||
152 | /// Get the animation's index. | ||
153 | animation_idx mem_get_animation_index(const Animation*); | ||
154 | |||
108 | /// Get the camera's index. | 155 | /// Get the camera's index. |
109 | camera_idx mem_get_camera_index(const SceneCamera*); | 156 | camera_idx mem_get_camera_index(const SceneCamera*); |
110 | 157 | ||
158 | /// Get the joint's index. | ||
159 | joint_idx mem_get_joint_index(const Joint*); | ||
160 | |||
111 | /// Get the light's index. | 161 | /// Get the light's index. |
112 | light_idx mem_get_light_index(const Light*); | 162 | light_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. |
130 | scene_idx mem_get_scene_index(const Scene*); | 180 | scene_idx mem_get_scene_index(const Scene*); |
181 | |||
182 | /// Get the skeleton's index. | ||
183 | skeleton_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 | ||
5 | typedef uint16_t gfx_idx; | 6 | typedef 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 | ||
14 | DEF_STRONG_INDEX(camera) | 13 | DEF_STRONG_INDEX(anima, gfx_idx) |
15 | DEF_STRONG_INDEX(light) | 14 | DEF_STRONG_INDEX(animation, gfx_idx) |
16 | DEF_STRONG_INDEX(material) | 15 | DEF_STRONG_INDEX(camera, gfx_idx) |
17 | DEF_STRONG_INDEX(mesh) | 16 | DEF_STRONG_INDEX(joint, gfx_idx) |
18 | DEF_STRONG_INDEX(mesh_link) | 17 | DEF_STRONG_INDEX(light, gfx_idx) |
19 | DEF_STRONG_INDEX(node) | 18 | DEF_STRONG_INDEX(material, gfx_idx) |
20 | DEF_STRONG_INDEX(object) | 19 | DEF_STRONG_INDEX(mesh, gfx_idx) |
21 | DEF_STRONG_INDEX(scene) | 20 | DEF_STRONG_INDEX(mesh_link, gfx_idx) |
21 | DEF_STRONG_INDEX(node, gfx_idx) | ||
22 | DEF_STRONG_INDEX(object, gfx_idx) | ||
23 | DEF_STRONG_INDEX(scene, gfx_idx) | ||
24 | DEF_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 | ||
142 | typedef enum TextureType { | 146 | typedef 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. |
214 | static const char* TextureUniformName(TextureType type) { | 228 | static 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. | ||
267 | static 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. | ||
287 | static 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. | ||
302 | static 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 | ||
330 | static 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. | ||
266 | static bool load_buffers( | 530 | static 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. | ||
1082 | static 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. | ||
1129 | static 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. |
781 | static void load_nodes( | 1189 | static 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. |
903 | static bool load_scene( | 1305 | static 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 | ||
994 | cleanup: | 1427 | cleanup: |
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 | ||
1073 | bool gfx_load_scene(Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) { | 1534 | SceneNode* 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. |
92 | static bool load_scene( | 92 | static 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 | ||
219 | cleanup: | 229 | cleanup: |
@@ -227,12 +237,11 @@ cleanup: | |||
227 | void game_end(Game* game) { gfx_destroy(&game->gfx); } | 237 | void game_end(Game* game) { gfx_destroy(&game->gfx); } |
228 | 238 | ||
229 | void game_update(Game* game, double t, double dt) { | 239 | void 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. |
15 | typedef struct { | 16 | typedef 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 | ||
24 | bool game_new(Game*, int argc, const char** argv); | 25 | bool game_new(Game*, int argc, const char** argv); |