diff options
Diffstat (limited to 'src/scene')
| -rw-r--r-- | src/scene/camera.c | 18 | ||||
| -rw-r--r-- | src/scene/light.c | 39 | ||||
| -rw-r--r-- | src/scene/light_impl.h | 20 | ||||
| -rw-r--r-- | src/scene/material.c | 24 | ||||
| -rw-r--r-- | src/scene/material_impl.h | 13 | ||||
| -rw-r--r-- | src/scene/mesh.c | 26 | ||||
| -rw-r--r-- | src/scene/mesh_impl.h | 11 | ||||
| -rw-r--r-- | src/scene/model.c | 45 | ||||
| -rw-r--r-- | src/scene/model_impl.h | 16 | ||||
| -rw-r--r-- | src/scene/node.c | 352 | ||||
| -rw-r--r-- | src/scene/node_impl.h | 40 | ||||
| -rw-r--r-- | src/scene/object.c | 80 | ||||
| -rw-r--r-- | src/scene/object_impl.h | 23 | ||||
| -rw-r--r-- | src/scene/scene.c | 31 | ||||
| -rw-r--r-- | src/scene/scene_graph.h | 144 | ||||
| -rw-r--r-- | src/scene/scene_impl.h | 9 |
16 files changed, 891 insertions, 0 deletions
diff --git a/src/scene/camera.c b/src/scene/camera.c new file mode 100644 index 0000000..fcfc496 --- /dev/null +++ b/src/scene/camera.c | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #include <gfx/scene.h> | ||
| 2 | |||
| 3 | #include "memory.h" | ||
| 4 | |||
| 5 | #include <assert.h> | ||
| 6 | #include <math/camera.h> | ||
| 7 | |||
| 8 | Camera* gfx_make_camera(void) { | ||
| 9 | Camera* camera = mem_alloc_camera(); | ||
| 10 | return camera; | ||
| 11 | } | ||
| 12 | |||
| 13 | void gfx_destroy_camera(Camera** camera) { | ||
| 14 | assert(camera); | ||
| 15 | if (*camera) { | ||
| 16 | mem_free_camera(camera); | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/src/scene/light.c b/src/scene/light.c new file mode 100644 index 0000000..4233330 --- /dev/null +++ b/src/scene/light.c | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #include "light_impl.h" | ||
| 2 | |||
| 3 | #include "memory.h" | ||
| 4 | |||
| 5 | #include <cassert.h> | ||
| 6 | #include <error.h> | ||
| 7 | |||
| 8 | static void make_environment_light( | ||
| 9 | Light* light, const EnvironmentLightDesc* desc) { | ||
| 10 | assert(light); | ||
| 11 | assert(desc); | ||
| 12 | light->type = EnvironmentLightType; | ||
| 13 | light->environment.environment_map = desc->environment_map; | ||
| 14 | } | ||
| 15 | |||
| 16 | Light* gfx_make_light(const LightDesc* desc) { | ||
| 17 | assert(desc); | ||
| 18 | |||
| 19 | Light* light = mem_alloc_light(); | ||
| 20 | |||
| 21 | switch (desc->type) { | ||
| 22 | case EnvironmentLightType: | ||
| 23 | make_environment_light(light, &desc->light.environment); | ||
| 24 | break; | ||
| 25 | default: | ||
| 26 | log_error("Unhandled light type"); | ||
| 27 | gfx_destroy_light(&light); | ||
| 28 | return 0; | ||
| 29 | } | ||
| 30 | |||
| 31 | return light; | ||
| 32 | } | ||
| 33 | |||
| 34 | void gfx_destroy_light(Light** light) { | ||
| 35 | assert(light); | ||
| 36 | if (*light) { | ||
| 37 | mem_free_light(light); | ||
| 38 | } | ||
| 39 | } | ||
diff --git a/src/scene/light_impl.h b/src/scene/light_impl.h new file mode 100644 index 0000000..3191a50 --- /dev/null +++ b/src/scene/light_impl.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | |||
| 5 | /// An environment light. | ||
| 6 | typedef struct EnvironmentLight { | ||
| 7 | const Texture* environment_map; | ||
| 8 | const Texture* irradiance_map; // Renderer implementation. | ||
| 9 | const Texture* prefiltered_environment_map; // Renderer implementation. | ||
| 10 | int max_reflection_lod; // Mandatory when prefiltered_environment_map is | ||
| 11 | // given. | ||
| 12 | } EnvironmentLight; | ||
| 13 | |||
| 14 | /// A scene light. | ||
| 15 | typedef struct Light { | ||
| 16 | LightType type; | ||
| 17 | union { | ||
| 18 | EnvironmentLight environment; | ||
| 19 | }; | ||
| 20 | } Light; | ||
diff --git a/src/scene/material.c b/src/scene/material.c new file mode 100644 index 0000000..9fe6c1b --- /dev/null +++ b/src/scene/material.c | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #include "material_impl.h" | ||
| 2 | |||
| 3 | #include "memory.h" | ||
| 4 | |||
| 5 | static void material_make(Material* material, const MaterialDesc* desc) { | ||
| 6 | assert(material); | ||
| 7 | assert(desc); | ||
| 8 | assert(desc->num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | ||
| 9 | material->alpha_mode = desc->alpha_mode; | ||
| 10 | material->alpha_cutoff = desc->alpha_cutoff; | ||
| 11 | material->num_uniforms = (int8_t)desc->num_uniforms; | ||
| 12 | for (int i = 0; i < desc->num_uniforms; ++i) { | ||
| 13 | material->uniforms[i] = desc->uniforms[i]; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | Material* gfx_make_material(const MaterialDesc* desc) { | ||
| 18 | assert(desc); | ||
| 19 | Material* material = mem_alloc_material(); | ||
| 20 | material_make(material, desc); | ||
| 21 | return material; | ||
| 22 | } | ||
| 23 | |||
| 24 | void gfx_destroy_material(Material** material) { mem_free_material(material); } | ||
diff --git a/src/scene/material_impl.h b/src/scene/material_impl.h new file mode 100644 index 0000000..488ffc7 --- /dev/null +++ b/src/scene/material_impl.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | #include <gfx/sizes.h> | ||
| 5 | |||
| 6 | typedef struct ShaderProgram ShaderProgram; | ||
| 7 | |||
| 8 | typedef struct Material { | ||
| 9 | AlphaMode alpha_mode; | ||
| 10 | float alpha_cutoff; | ||
| 11 | int8_t num_uniforms; | ||
| 12 | ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; | ||
| 13 | } Material; | ||
diff --git a/src/scene/mesh.c b/src/scene/mesh.c new file mode 100644 index 0000000..770c3fb --- /dev/null +++ b/src/scene/mesh.c | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | #include "mesh_impl.h" | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | |||
| 5 | #include "memory.h" | ||
| 6 | |||
| 7 | #include <cassert.h> | ||
| 8 | |||
| 9 | static void mesh_make(Mesh* mesh, const MeshDesc* desc) { | ||
| 10 | assert(mesh); | ||
| 11 | assert(desc); | ||
| 12 | assert(desc->geometry); | ||
| 13 | assert(desc->material); | ||
| 14 | assert(desc->shader); | ||
| 15 | mesh->geometry = desc->geometry; | ||
| 16 | mesh->material = desc->material; | ||
| 17 | mesh->shader = desc->shader; | ||
| 18 | } | ||
| 19 | |||
| 20 | Mesh* gfx_make_mesh(const MeshDesc* desc) { | ||
| 21 | Mesh* mesh = mem_alloc_mesh(); | ||
| 22 | mesh_make(mesh, desc); | ||
| 23 | return mesh; | ||
| 24 | } | ||
| 25 | |||
| 26 | void gfx_destroy_mesh(Mesh** mesh) { mem_free_mesh(mesh); } | ||
diff --git a/src/scene/mesh_impl.h b/src/scene/mesh_impl.h new file mode 100644 index 0000000..c7e2211 --- /dev/null +++ b/src/scene/mesh_impl.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | typedef struct Geometry Geometry; | ||
| 4 | typedef struct Material Material; | ||
| 5 | typedef struct ShaderProgram ShaderProgram; | ||
| 6 | |||
| 7 | typedef struct Mesh { | ||
| 8 | const Geometry* geometry; | ||
| 9 | const Material* material; | ||
| 10 | ShaderProgram* shader; // TODO: Move this back to Material? | ||
| 11 | } Mesh; | ||
diff --git a/src/scene/model.c b/src/scene/model.c new file mode 100644 index 0000000..1bd0112 --- /dev/null +++ b/src/scene/model.c | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | #include "model_impl.h" | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | |||
| 5 | #include "memory.h" | ||
| 6 | |||
| 7 | #include <assert.h> | ||
| 8 | |||
| 9 | Model* gfx_make_model(const SceneNode* root) { | ||
| 10 | assert(root); | ||
| 11 | |||
| 12 | Model* model = mem_alloc_model(); | ||
| 13 | model->root = mem_get_node_index(root); | ||
| 14 | return model; | ||
| 15 | } | ||
| 16 | |||
| 17 | void gfx_del_model(Model** model) { | ||
| 18 | assert(model); | ||
| 19 | |||
| 20 | if (*model) { | ||
| 21 | SceneNode* root = mem_get_node((*model)->root); | ||
| 22 | gfx_destroy_node(&root); | ||
| 23 | *model = 0; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | Anima* gfx_get_model_anima(Model* model) { | ||
| 28 | assert(model); | ||
| 29 | |||
| 30 | SceneNode* root = mem_get_node(model->root); | ||
| 31 | if (gfx_get_node_type(root) == AnimaNode) { | ||
| 32 | return gfx_get_node_anima_mut(root); | ||
| 33 | } else { | ||
| 34 | return 0; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | const SceneNode* gfx_get_model_root(const Model* model) { | ||
| 39 | assert(model); | ||
| 40 | return mem_get_node(model->root); | ||
| 41 | } | ||
| 42 | |||
| 43 | SceneNode* gfx_get_model_root_mut(Model* model) { | ||
| 44 | return (SceneNode*)gfx_get_model_root(model); | ||
| 45 | } | ||
diff --git a/src/scene/model_impl.h b/src/scene/model_impl.h new file mode 100644 index 0000000..72cd0ab --- /dev/null +++ b/src/scene/model_impl.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | |||
| 5 | #include "memory.h" | ||
| 6 | |||
| 7 | /// Model. | ||
| 8 | typedef struct Model { | ||
| 9 | node_idx root; | ||
| 10 | } Model; | ||
| 11 | |||
| 12 | /// Create a new model. | ||
| 13 | Model* gfx_make_model(const SceneNode* root); | ||
| 14 | |||
| 15 | /// Destroy the model. | ||
| 16 | void gfx_del_model(Model**); | ||
diff --git a/src/scene/node.c b/src/scene/node.c new file mode 100644 index 0000000..0004d27 --- /dev/null +++ b/src/scene/node.c | |||
| @@ -0,0 +1,352 @@ | |||
| 1 | #include "node_impl.h" | ||
| 2 | |||
| 3 | #include "animation_impl.h" | ||
| 4 | #include "memory.h" | ||
| 5 | #include "object_impl.h" | ||
| 6 | #include "render/llr_impl.h" | ||
| 7 | #include "scene_graph.h" | ||
| 8 | |||
| 9 | #include "gfx_assert.h" | ||
| 10 | |||
| 11 | #include <gfx/scene.h> | ||
| 12 | |||
| 13 | #include <cstring.h> | ||
| 14 | #include <log/log.h> | ||
| 15 | |||
| 16 | static void scene_node_make(SceneNode* node) { | ||
| 17 | assert(node); | ||
| 18 | node->type = LogicalNode; | ||
| 19 | node->transform = mat4_id(); | ||
| 20 | } | ||
| 21 | |||
| 22 | SceneNode* gfx_make_node(void) { | ||
| 23 | SceneNode* node = mem_alloc_node(); | ||
| 24 | scene_node_make(node); | ||
| 25 | return node; | ||
| 26 | } | ||
| 27 | |||
| 28 | SceneNode* gfx_make_anima_node(Anima* anima) { | ||
| 29 | assert(anima); | ||
| 30 | SceneNode* node = gfx_make_node(); | ||
| 31 | node->type = AnimaNode; | ||
| 32 | node->anima = mem_get_anima_index(anima); | ||
| 33 | return node; | ||
| 34 | } | ||
| 35 | |||
| 36 | SceneNode* gfx_make_camera_node(Camera* camera) { | ||
| 37 | assert(camera); | ||
| 38 | SceneNode* node = gfx_make_node(); | ||
| 39 | node->type = CameraNode; | ||
| 40 | node->camera = mem_get_camera_index(camera); | ||
| 41 | return node; | ||
| 42 | } | ||
| 43 | |||
| 44 | SceneNode* gfx_make_light_node(Light* light) { | ||
| 45 | assert(light); | ||
| 46 | SceneNode* node = gfx_make_node(); | ||
| 47 | node->type = LightNode; | ||
| 48 | node->light = mem_get_light_index(light); | ||
| 49 | return node; | ||
| 50 | } | ||
| 51 | |||
| 52 | SceneNode* gfx_make_model_node(Model* model) { | ||
| 53 | assert(model); | ||
| 54 | SceneNode* node = gfx_make_node(); | ||
| 55 | node->type = ModelNode; | ||
| 56 | node->model = mem_get_model_index(model); | ||
| 57 | return node; | ||
| 58 | } | ||
| 59 | |||
| 60 | SceneNode* gfx_make_object_node(SceneObject* object) { | ||
| 61 | assert(object); | ||
| 62 | SceneNode* node = gfx_make_node(); | ||
| 63 | node->type = ObjectNode; | ||
| 64 | node->object = mem_get_object_index(object); | ||
| 65 | return node; | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Frees the node's resource. | ||
| 69 | static void free_node_resource(SceneNode* node) { | ||
| 70 | assert(node); | ||
| 71 | |||
| 72 | switch (node->type) { | ||
| 73 | case AnimaNode: { | ||
| 74 | Anima* anima = mem_get_anima(node->anima); | ||
| 75 | gfx_destroy_anima(&anima); | ||
| 76 | return; | ||
| 77 | } | ||
| 78 | case CameraNode: { | ||
| 79 | Camera* camera = mem_get_camera(node->camera); | ||
| 80 | gfx_destroy_camera(&camera); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | case LightNode: { | ||
| 84 | Light* light = mem_get_light(node->light); | ||
| 85 | gfx_destroy_light(&light); | ||
| 86 | return; | ||
| 87 | } | ||
| 88 | case ModelNode: { | ||
| 89 | return; // Model data is owned by the asset cache. | ||
| 90 | } | ||
| 91 | case ObjectNode: { | ||
| 92 | SceneObject* object = mem_get_object(node->object); | ||
| 93 | gfx_destroy_object(&object); | ||
| 94 | return; | ||
| 95 | } | ||
| 96 | case LogicalNode: { | ||
| 97 | return; // Logical nodes have no resource. | ||
| 98 | } | ||
| 99 | } | ||
| 100 | FAIL("unhandled node type"); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void destroy_node_rec(SceneNode* node) { | ||
| 104 | assert(node); | ||
| 105 | |||
| 106 | // First child. | ||
| 107 | if (node->child.val) { | ||
| 108 | destroy_node_rec(mem_get_node(node->child)); | ||
| 109 | } | ||
| 110 | |||
| 111 | // Right sibling. | ||
| 112 | if (node->next.val) { | ||
| 113 | destroy_node_rec(mem_get_node(node->next)); | ||
| 114 | } | ||
| 115 | |||
| 116 | free_node_resource(node); | ||
| 117 | mem_free_node(&node); | ||
| 118 | } | ||
| 119 | |||
| 120 | void gfx_destroy_node(SceneNode** node) { | ||
| 121 | assert(node); | ||
| 122 | if (*node) { | ||
| 123 | // Since the node and the whole hierarchy under it gets destroyed, there is | ||
| 124 | // no need to individually detach every node from its hierarchy. We can | ||
| 125 | // simply detach the given node and then destroy it and its sub-hierarchy. | ||
| 126 | TREE_REMOVE(*node); | ||
| 127 | destroy_node_rec(*node); | ||
| 128 | *node = 0; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | // TODO: Think more about ownership of nodes and resources. Should this function | ||
| 133 | // even exist? | ||
| 134 | void gfx_del_node(node_idx index) { | ||
| 135 | assert(index.val); | ||
| 136 | SceneNode* node = mem_get_node(index); | ||
| 137 | assert(node); | ||
| 138 | // TODO: Should destroy children recursively? | ||
| 139 | TREE_REMOVE(node); | ||
| 140 | mem_free_node(&node); | ||
| 141 | } | ||
| 142 | |||
| 143 | NodeType gfx_get_node_type(const SceneNode* node) { | ||
| 144 | assert(node); | ||
| 145 | return node->type; | ||
| 146 | } | ||
| 147 | |||
| 148 | #define NODE_GET(node, field, expected_type) \ | ||
| 149 | { \ | ||
| 150 | assert(node); \ | ||
| 151 | assert(node->type == expected_type); \ | ||
| 152 | return mem_get_##field(node->field); \ | ||
| 153 | } | ||
| 154 | |||
| 155 | const Anima* gfx_get_node_anima(const SceneNode* node) { | ||
| 156 | NODE_GET(node, anima, AnimaNode); | ||
| 157 | } | ||
| 158 | |||
| 159 | Anima* gfx_get_node_anima_mut(SceneNode* node) { | ||
| 160 | NODE_GET(node, anima, AnimaNode); | ||
| 161 | } | ||
| 162 | |||
| 163 | const Camera* gfx_get_node_camera(const SceneNode* node) { | ||
| 164 | NODE_GET(node, camera, CameraNode); | ||
| 165 | } | ||
| 166 | |||
| 167 | Camera* gfx_get_node_camera_mut(SceneNode* node) { | ||
| 168 | NODE_GET(node, camera, CameraNode); | ||
| 169 | } | ||
| 170 | |||
| 171 | const Light* gfx_get_node_light(const SceneNode* node) { | ||
| 172 | NODE_GET(node, light, LightNode); | ||
| 173 | } | ||
| 174 | |||
| 175 | Light* gfx_get_node_light_mut(SceneNode* node) { | ||
| 176 | NODE_GET(node, light, LightNode); | ||
| 177 | } | ||
| 178 | |||
| 179 | const Model* gfx_get_node_model(const SceneNode* node) { | ||
| 180 | NODE_GET(node, model, ModelNode); | ||
| 181 | } | ||
| 182 | |||
| 183 | Model* gfx_get_node_model_mut(SceneNode* node) { | ||
| 184 | NODE_GET(node, model, ModelNode); | ||
| 185 | } | ||
| 186 | |||
| 187 | const SceneObject* gfx_get_node_object(const SceneNode* node) { | ||
| 188 | NODE_GET(node, object, ObjectNode); | ||
| 189 | } | ||
| 190 | |||
| 191 | SceneObject* gfx_get_node_object_mut(SceneNode* node) { | ||
| 192 | NODE_GET(node, object, ObjectNode); | ||
| 193 | } | ||
| 194 | |||
| 195 | const SceneNode* gfx_get_node_parent(const SceneNode* node) { | ||
| 196 | assert(node); | ||
| 197 | return mem_get_node(node->parent); | ||
| 198 | } | ||
| 199 | |||
| 200 | SceneNode* gfx_get_node_parent_mut(SceneNode* node) { | ||
| 201 | assert(node); | ||
| 202 | return mem_get_node(node->parent); | ||
| 203 | } | ||
| 204 | |||
| 205 | const SceneNode* gfx_get_node_child(const SceneNode* node) { | ||
| 206 | assert(node); | ||
| 207 | if (node->child.val) { | ||
| 208 | return mem_get_node(node->child); | ||
| 209 | } else { | ||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | SceneNode* gfx_get_node_child_mut(SceneNode* node) { | ||
| 215 | return (SceneNode*)gfx_get_node_child(node); | ||
| 216 | } | ||
| 217 | |||
| 218 | const SceneNode* gfx_get_node_sibling(const SceneNode* node) { | ||
| 219 | assert(node); | ||
| 220 | if (node->next.val) { | ||
| 221 | return mem_get_node(node->next); | ||
| 222 | } else { | ||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | SceneNode* gfx_get_node_sibling_mut(SceneNode* node) { | ||
| 228 | return (SceneNode*)gfx_get_node_sibling(node); | ||
| 229 | } | ||
| 230 | |||
| 231 | mat4 gfx_get_node_transform(const SceneNode* node) { | ||
| 232 | assert(node); | ||
| 233 | return node->transform; | ||
| 234 | } | ||
| 235 | |||
| 236 | mat4 gfx_get_node_global_transform(const SceneNode* node) { | ||
| 237 | assert(node); | ||
| 238 | mat4 transform = node->transform; | ||
| 239 | node_idx parent_index = node->parent; | ||
| 240 | while (parent_index.val != 0) { | ||
| 241 | const SceneNode* parent = mem_get_node(parent_index); | ||
| 242 | transform = mat4_mul(parent->transform, transform); | ||
| 243 | parent_index = parent->parent; | ||
| 244 | } | ||
| 245 | return transform; | ||
| 246 | } | ||
| 247 | |||
| 248 | void gfx_set_node_parent(SceneNode* child, SceneNode* parent_node) { | ||
| 249 | assert(child); | ||
| 250 | // Parent can be null. | ||
| 251 | SET_PARENT(child, parent_node); | ||
| 252 | } | ||
| 253 | |||
| 254 | void gfx_set_node_transform(SceneNode* node, const mat4* transform) { | ||
| 255 | assert(node); | ||
| 256 | assert(transform); | ||
| 257 | node->transform = *transform; | ||
| 258 | } | ||
| 259 | |||
| 260 | void gfx_set_node_position(SceneNode* node, const vec3* position) { | ||
| 261 | assert(node); | ||
| 262 | assert(position); | ||
| 263 | mat4_set_v3(&node->transform, *position); | ||
| 264 | } | ||
| 265 | |||
| 266 | void gfx_set_node_rotation(SceneNode* node, const quat* rotation) { | ||
| 267 | assert(node); | ||
| 268 | assert(rotation); | ||
| 269 | mat4_set_3x3(&node->transform, mat4_from_quat(*rotation)); | ||
| 270 | } | ||
| 271 | |||
| 272 | void gfx_set_node_rotation_mat(SceneNode* node, const mat4* rotation) { | ||
| 273 | assert(node); | ||
| 274 | assert(rotation); | ||
| 275 | mat4_set_3x3(&node->transform, *rotation); | ||
| 276 | } | ||
| 277 | |||
| 278 | static const char* get_node_type_str(NodeType type) { | ||
| 279 | switch (type) { | ||
| 280 | case LogicalNode: | ||
| 281 | return "LogicalNode"; | ||
| 282 | case AnimaNode: | ||
| 283 | return "AnimaNode"; | ||
| 284 | case CameraNode: | ||
| 285 | return "CameraNode"; | ||
| 286 | case LightNode: | ||
| 287 | return "LightNode"; | ||
| 288 | case ModelNode: | ||
| 289 | return "ModelNode"; | ||
| 290 | case ObjectNode: | ||
| 291 | return "ObjectNode"; | ||
| 292 | } | ||
| 293 | FAIL("Unhandled node type"); | ||
| 294 | return ""; | ||
| 295 | } | ||
| 296 | |||
| 297 | static void log_node_hierarchy_rec(const SceneNode* node, const sstring* pad) { | ||
| 298 | assert(node); | ||
| 299 | assert(pad); | ||
| 300 | |||
| 301 | LOGI( | ||
| 302 | "%s%s (%u)", sstring_cstr(pad), get_node_type_str(node->type), | ||
| 303 | mem_get_node_index(node).val); | ||
| 304 | |||
| 305 | // Log the children. | ||
| 306 | if (node->child.val) { | ||
| 307 | const sstring new_pad = sstring_concat_cstr(*pad, " "); | ||
| 308 | log_node_hierarchy_rec(mem_get_node(node->child), &new_pad); | ||
| 309 | } | ||
| 310 | |||
| 311 | // Then log the siblings. | ||
| 312 | if (node->next.val) { | ||
| 313 | log_node_hierarchy_rec(mem_get_node(node->next), pad); | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | void gfx_log_node_hierarchy(const SceneNode* node) { | ||
| 318 | const sstring pad = sstring_make(""); | ||
| 319 | log_node_hierarchy_rec(node, &pad); | ||
| 320 | } | ||
| 321 | |||
| 322 | static SceneNode* clone_scene_rec(const SceneNode* node) { | ||
| 323 | assert(node); | ||
| 324 | |||
| 325 | SceneNode* copy = mem_alloc_node(); | ||
| 326 | *copy = *node; // Shallow clone of the node's resource. | ||
| 327 | |||
| 328 | if (node->child.val) { | ||
| 329 | SceneNode* child = mem_get_node(node->child); | ||
| 330 | SceneNode* child_copy = clone_scene_rec(child); | ||
| 331 | copy->child = mem_get_node_index(child_copy); | ||
| 332 | child_copy->parent = mem_get_node_index(copy); | ||
| 333 | } | ||
| 334 | |||
| 335 | if (node->next.val) { | ||
| 336 | SceneNode* next = mem_get_node(node->next); | ||
| 337 | SceneNode* next_copy = clone_scene_rec(next); | ||
| 338 | copy->next = mem_get_node_index(next_copy); | ||
| 339 | next_copy->prev = mem_get_node_index(copy); | ||
| 340 | } | ||
| 341 | |||
| 342 | return copy; | ||
| 343 | } | ||
| 344 | |||
| 345 | SceneNode* gfx_clone_scene_shallow(const SceneNode* node) { | ||
| 346 | assert(node); | ||
| 347 | // Must be a root node; not allowed to have siblings. | ||
| 348 | assert(!node->prev.val); | ||
| 349 | assert(!node->next.val); | ||
| 350 | |||
| 351 | return clone_scene_rec(node); | ||
| 352 | } | ||
diff --git a/src/scene/node_impl.h b/src/scene/node_impl.h new file mode 100644 index 0000000..9e65588 --- /dev/null +++ b/src/scene/node_impl.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | |||
| 5 | #include "../types.h" | ||
| 6 | |||
| 7 | #include <math/camera.h> | ||
| 8 | #include <math/mat4.h> | ||
| 9 | |||
| 10 | /// Scene node. | ||
| 11 | /// | ||
| 12 | /// The SceneNode owns its cameras, objects, lights and child nodes. These | ||
| 13 | /// together form a strict tree hierarchy and not a more general DAG. | ||
| 14 | typedef struct SceneNode { | ||
| 15 | NodeType type; | ||
| 16 | union { | ||
| 17 | anima_idx anima; | ||
| 18 | camera_idx camera; | ||
| 19 | light_idx light; | ||
| 20 | model_idx model; | ||
| 21 | object_idx object; | ||
| 22 | }; | ||
| 23 | mat4 transform; // Transformation for this node and its children. | ||
| 24 | node_idx parent; // Parent SceneNode. | ||
| 25 | node_idx child; // First child SceneNode. | ||
| 26 | node_idx next; // Next sibling SceneNode. | ||
| 27 | node_idx prev; // Previous sibling SceneNode. | ||
| 28 | } SceneNode; | ||
| 29 | |||
| 30 | /// Recursively destroy a node given its index but without destroying the node | ||
| 31 | /// resources. | ||
| 32 | /// | ||
| 33 | /// The node and its children are removed from the scene graph. | ||
| 34 | /// | ||
| 35 | /// This function is for the library's internal use only. | ||
| 36 | void gfx_del_node(node_idx); | ||
| 37 | |||
| 38 | /// Return a shallow clone of the scene rooted at the given node. | ||
| 39 | /// The given node must have no siblings (must be a root node). | ||
| 40 | SceneNode* gfx_clone_scene_shallow(const SceneNode*); | ||
diff --git a/src/scene/object.c b/src/scene/object.c new file mode 100644 index 0000000..ac86b39 --- /dev/null +++ b/src/scene/object.c | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | #include "object_impl.h" | ||
| 2 | |||
| 3 | #include <gfx/core.h> | ||
| 4 | |||
| 5 | #include "memory.h" | ||
| 6 | #include "render/llr_impl.h" | ||
| 7 | #include "scene/mesh_impl.h" | ||
| 8 | #include "scene/node_impl.h" | ||
| 9 | |||
| 10 | #include <assert.h> | ||
| 11 | |||
| 12 | static aabb3 calc_object_aabb(const SceneObject* object) { | ||
| 13 | assert(object); | ||
| 14 | |||
| 15 | bool first = true; | ||
| 16 | aabb3 box; | ||
| 17 | |||
| 18 | mesh_link_idx ml = object->mesh_link; | ||
| 19 | while (ml.val) { | ||
| 20 | const MeshLink* mesh_link = mem_get_mesh_link(ml); | ||
| 21 | const mesh_idx mi = mesh_link->mesh; | ||
| 22 | if (mi.val) { | ||
| 23 | const Mesh* mesh = mem_get_mesh(mi); | ||
| 24 | const aabb3 mesh_box = gfx_get_geometry_aabb(mesh->geometry); | ||
| 25 | if (first) { | ||
| 26 | box = mesh_box; | ||
| 27 | first = false; | ||
| 28 | } else { | ||
| 29 | box = aabb3_sum(box, mesh_box); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | ml = mesh_link->next; | ||
| 33 | } | ||
| 34 | |||
| 35 | return box; | ||
| 36 | } | ||
| 37 | |||
| 38 | static void add_object_mesh(SceneObject* object, Mesh* mesh) { | ||
| 39 | assert(object); | ||
| 40 | assert(mesh); | ||
| 41 | |||
| 42 | MeshLink* link = mem_alloc_mesh_link(); | ||
| 43 | link->mesh = mem_get_mesh_index(mesh); | ||
| 44 | link->next = object->mesh_link; | ||
| 45 | object->mesh_link = mem_get_mesh_link_index(link); | ||
| 46 | } | ||
| 47 | |||
| 48 | SceneObject* gfx_make_object(const ObjectDesc* desc) { | ||
| 49 | assert(desc); | ||
| 50 | |||
| 51 | SceneObject* object = mem_alloc_object(); | ||
| 52 | for (size_t i = 0; i < desc->num_meshes; ++i) { | ||
| 53 | add_object_mesh(object, desc->meshes[i]); | ||
| 54 | } | ||
| 55 | object->box = calc_object_aabb(object); | ||
| 56 | return object; | ||
| 57 | } | ||
| 58 | |||
| 59 | void gfx_destroy_object(SceneObject** object) { | ||
| 60 | assert(object); | ||
| 61 | if (*object) { | ||
| 62 | mem_free_object(object); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | void gfx_set_object_skeleton(SceneObject* object, const Skeleton* skeleton) { | ||
| 67 | assert(object); | ||
| 68 | assert(skeleton); | ||
| 69 | object->skeleton = mem_get_skeleton_index(skeleton); | ||
| 70 | } | ||
| 71 | |||
| 72 | const Skeleton* gfx_get_object_skeleton(const SceneObject* object) { | ||
| 73 | assert(object); | ||
| 74 | return (object->skeleton.val == 0) ? 0 : mem_get_skeleton(object->skeleton); | ||
| 75 | } | ||
| 76 | |||
| 77 | aabb3 gfx_get_object_aabb(const SceneObject* object) { | ||
| 78 | assert(object); | ||
| 79 | return object->box; | ||
| 80 | } | ||
diff --git a/src/scene/object_impl.h b/src/scene/object_impl.h new file mode 100644 index 0000000..345d615 --- /dev/null +++ b/src/scene/object_impl.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | |||
| 5 | #include "../types.h" | ||
| 6 | |||
| 7 | typedef struct MeshLink { | ||
| 8 | mesh_idx mesh; | ||
| 9 | mesh_link_idx next; // Next MeshLink in the list. | ||
| 10 | } MeshLink; | ||
| 11 | |||
| 12 | /// Scene object. | ||
| 13 | /// | ||
| 14 | /// A SceneObject does not own its Meshes, and they are instead shared for | ||
| 15 | /// re-use. The SceneObject consequently embeds a list of MeshLinks as opposed | ||
| 16 | /// to a list of Meshes. The MeshLinks define a list of Meshes, which can be | ||
| 17 | /// different for each SceneObject. Each SceneObject may then have a unique list | ||
| 18 | /// of Meshes, and the Meshes are re-used. | ||
| 19 | typedef struct SceneObject { | ||
| 20 | mesh_link_idx mesh_link; /// First MeshLink in the list. | ||
| 21 | skeleton_idx skeleton; /// 0 for static objects. | ||
| 22 | aabb3 box; | ||
| 23 | } SceneObject; | ||
diff --git a/src/scene/scene.c b/src/scene/scene.c new file mode 100644 index 0000000..52ddb58 --- /dev/null +++ b/src/scene/scene.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #include "scene_impl.h" | ||
| 2 | |||
| 3 | #include "memory.h" | ||
| 4 | #include "node_impl.h" | ||
| 5 | |||
| 6 | #include <assert.h> | ||
| 7 | |||
| 8 | Scene* gfx_make_scene(void) { | ||
| 9 | Scene* scene = mem_alloc_scene(); | ||
| 10 | scene->root = mem_get_node_index(gfx_make_node()); | ||
| 11 | return scene; | ||
| 12 | } | ||
| 13 | |||
| 14 | void gfx_destroy_scene(Scene** scene) { | ||
| 15 | assert(scene); | ||
| 16 | if (*scene) { | ||
| 17 | SceneNode* node = mem_get_node((*scene)->root); | ||
| 18 | gfx_destroy_node(&node); | ||
| 19 | mem_free_scene(scene); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | const SceneNode* gfx_get_scene_root(const Scene* scene) { | ||
| 24 | assert(scene); | ||
| 25 | return mem_get_node(scene->root); | ||
| 26 | } | ||
| 27 | |||
| 28 | SceneNode* gfx_get_scene_root_mut(Scene* scene) { | ||
| 29 | assert(scene); | ||
| 30 | return (SceneNode*)gfx_get_scene_root(scene); | ||
| 31 | } | ||
diff --git a/src/scene/scene_graph.h b/src/scene/scene_graph.h new file mode 100644 index 0000000..36c3a98 --- /dev/null +++ b/src/scene/scene_graph.h | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | /// Functions for list manipulation. | ||
| 2 | #pragma once | ||
| 3 | |||
| 4 | #include "memory.h" | ||
| 5 | |||
| 6 | // NOTE: SceneMemory guarantees that index 0 can be regarded as an invalid | ||
| 7 | // index. | ||
| 8 | |||
| 9 | #define MEM_GET(INDEX) \ | ||
| 10 | _Generic( \ | ||
| 11 | (INDEX), \ | ||
| 12 | camera_idx: mem_get_camera, \ | ||
| 13 | material_idx: mem_get_material, \ | ||
| 14 | mesh_idx: mem_get_mesh, \ | ||
| 15 | mesh_link_idx: mem_get_mesh_link, \ | ||
| 16 | node_idx: mem_get_node, \ | ||
| 17 | object_idx: mem_get_object, \ | ||
| 18 | scene_idx: mem_get_scene)(INDEX) | ||
| 19 | |||
| 20 | #define MEM_GET_INDEX(ITEM) \ | ||
| 21 | _Generic( \ | ||
| 22 | (ITEM), \ | ||
| 23 | Camera *: mem_get_camera_index, \ | ||
| 24 | Material *: mem_get_material_index, \ | ||
| 25 | Mesh *: mem_get_mesh_index, \ | ||
| 26 | MeshLink *: mem_get_mesh_link_index, \ | ||
| 27 | SceneNode *: mem_get_node_index, \ | ||
| 28 | SceneObject *: mem_get_object_index, \ | ||
| 29 | Scene *: mem_get_scene_index)(ITEM) | ||
| 30 | |||
| 31 | /// Assert the list node invariant. | ||
| 32 | /// | ||
| 33 | /// - A node does not point to itself. | ||
| 34 | #if NDEBUG | ||
| 35 | #define ASSERT_LIST_NODE_INVARIANT(ITEM) | ||
| 36 | #else | ||
| 37 | #define ASSERT_LIST_NODE_INVARIANT(ITEM) \ | ||
| 38 | { \ | ||
| 39 | const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \ | ||
| 40 | assert((ITEM)->prev.val != item_idx); \ | ||
| 41 | assert((ITEM)->next.val != item_idx); \ | ||
| 42 | } | ||
| 43 | #endif | ||
| 44 | |||
| 45 | /// Assert the tree node invariant. | ||
| 46 | /// | ||
| 47 | /// - A node does not point to itself. | ||
| 48 | /// - The node's left and right siblings cannot be equal, unless both are 0. | ||
| 49 | /// - The node's left/right sibling cannot be its child, unless both are 0. | ||
| 50 | /// - The node's parent cannot be the node's child or sibling, unless it's 0. | ||
| 51 | /// - If the node has a parent and the node is the leftmost sibling, then the | ||
| 52 | /// parent's child is the node. | ||
| 53 | #define ASSERT_TREE_NODE_INVARIANT(ITEM) \ | ||
| 54 | { \ | ||
| 55 | const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \ | ||
| 56 | assert((ITEM)->prev.val != item_idx); \ | ||
| 57 | assert((ITEM)->next.val != item_idx); \ | ||
| 58 | if ((ITEM)->prev.val) { \ | ||
| 59 | assert((ITEM)->prev.val != (ITEM)->next.val); \ | ||
| 60 | } \ | ||
| 61 | if ((ITEM)->child.val) { \ | ||
| 62 | assert((ITEM)->child.val != (ITEM)->prev.val); \ | ||
| 63 | assert((ITEM)->child.val != (ITEM)->next.val); \ | ||
| 64 | } \ | ||
| 65 | assert((ITEM)->parent.val != item_idx); \ | ||
| 66 | if ((ITEM)->parent.val && !(ITEM)->prev.val) { \ | ||
| 67 | assert((ITEM)->parent.val != (ITEM)->prev.val); \ | ||
| 68 | assert((ITEM)->parent.val != (ITEM)->next.val); \ | ||
| 69 | const __typeof__(ITEM) item_parent = MEM_GET((ITEM)->parent); \ | ||
| 70 | assert(item_parent->child.val == item_idx); \ | ||
| 71 | } \ | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Prepend an item to a list. | ||
| 75 | /// Modify HEAD_INDEX to equal the index of the new head. | ||
| 76 | #define LIST_PREPEND(HEAD_INDEX, ITEM) \ | ||
| 77 | (ITEM)->next = HEAD_INDEX; \ | ||
| 78 | if (HEAD_INDEX.val) { \ | ||
| 79 | __typeof__(ITEM) old_head = MEM_GET(HEAD_INDEX); \ | ||
| 80 | old_head->prev = MEM_GET_INDEX(ITEM); \ | ||
| 81 | } \ | ||
| 82 | HEAD_INDEX = MEM_GET_INDEX(ITEM); \ | ||
| 83 | ASSERT_LIST_NODE_INVARIANT(ITEM); | ||
| 84 | |||
| 85 | /// Disconnect an item from its siblings. | ||
| 86 | #define LIST_REMOVE(ITEM) \ | ||
| 87 | if ((ITEM)->prev.val) { \ | ||
| 88 | __typeof__(ITEM) prev_sibling = MEM_GET((ITEM)->prev); \ | ||
| 89 | prev_sibling->next = (ITEM)->next; \ | ||
| 90 | } \ | ||
| 91 | if ((ITEM)->next.val) { \ | ||
| 92 | __typeof__(ITEM) next_sibling = MEM_GET((ITEM)->next); \ | ||
| 93 | next_sibling->prev = (ITEM)->prev; \ | ||
| 94 | } \ | ||
| 95 | (ITEM)->prev.val = 0; \ | ||
| 96 | (ITEM)->next.val = 0; \ | ||
| 97 | ASSERT_LIST_NODE_INVARIANT(ITEM); | ||
| 98 | |||
| 99 | /// Set the child's parent. | ||
| 100 | /// | ||
| 101 | /// The hierarchy is a strict tree hierarchy and a parent node points to its | ||
| 102 | /// first/leftmost child only. To add a new child, the new child becomes the | ||
| 103 | /// leftmost node in the list of siblings, the one that the parent then points | ||
| 104 | /// to. | ||
| 105 | /// | ||
| 106 | /// The child is also completely disconnected from its previous hierarchy. This | ||
| 107 | /// is because siblings in a hierarchy must all point to the same parent. | ||
| 108 | #define SET_PARENT(CHILD, PARENT) \ | ||
| 109 | assert(CHILD); \ | ||
| 110 | assert(CHILD != PARENT); \ | ||
| 111 | ASSERT_TREE_NODE_INVARIANT(CHILD); \ | ||
| 112 | ASSERT_TREE_NODE_INVARIANT(PARENT); \ | ||
| 113 | TREE_REMOVE(CHILD); /* Disconnect CHILD from its previous hierarchy. */ \ | ||
| 114 | if (PARENT) { \ | ||
| 115 | LIST_PREPEND((PARENT)->child, CHILD); \ | ||
| 116 | (CHILD)->parent = MEM_GET_INDEX(PARENT); \ | ||
| 117 | } else { \ | ||
| 118 | (CHILD)->parent.val = 0; \ | ||
| 119 | } \ | ||
| 120 | ASSERT_TREE_NODE_INVARIANT(CHILD); \ | ||
| 121 | if (PARENT) { \ | ||
| 122 | ASSERT_TREE_NODE_INVARIANT(PARENT); \ | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Remove an item from its hierarchy. | ||
| 126 | /// | ||
| 127 | /// The item is disconnected from its parents and siblings. The hierarchy rooted | ||
| 128 | /// under the item remains intact. | ||
| 129 | #define TREE_REMOVE(ITEM) \ | ||
| 130 | assert(ITEM); \ | ||
| 131 | if ((ITEM)->parent.val) { \ | ||
| 132 | /* The parent points only to its first/leftmost child. If this item is */ \ | ||
| 133 | /* the leftmost sibling, then we need to rewire the parent to point to */ \ | ||
| 134 | /* the next sibling to keep the parent connected to its children. */ \ | ||
| 135 | __typeof__(ITEM) parent = MEM_GET((ITEM)->parent); \ | ||
| 136 | const __typeof__(ITEM) parent_child = MEM_GET(parent->child); \ | ||
| 137 | if (parent_child == ITEM) { \ | ||
| 138 | assert((ITEM)->prev.val == 0); \ | ||
| 139 | parent->child = (ITEM)->next; \ | ||
| 140 | } \ | ||
| 141 | } \ | ||
| 142 | (ITEM)->parent.val = 0; \ | ||
| 143 | LIST_REMOVE(ITEM); /* Disconnect ITEM from its siblings. */ \ | ||
| 144 | ASSERT_TREE_NODE_INVARIANT(ITEM); | ||
diff --git a/src/scene/scene_impl.h b/src/scene/scene_impl.h new file mode 100644 index 0000000..ad2e892 --- /dev/null +++ b/src/scene/scene_impl.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | |||
| 5 | #include "../types.h" | ||
| 6 | |||
| 7 | typedef struct Scene { | ||
| 8 | node_idx root; | ||
| 9 | } Scene; | ||
