diff options
-rw-r--r-- | gfx/CMakeLists.txt | 3 | ||||
-rw-r--r-- | gfx/include/gfx/gfx.h | 10 | ||||
-rw-r--r-- | gfx/include/gfx/render_backend.h | 84 | ||||
-rw-r--r-- | gfx/include/gfx/renderer.h | 53 | ||||
-rw-r--r-- | gfx/include/gfx/scene/object.h | 7 | ||||
-rw-r--r-- | gfx/include/gfx/sizes.h | 8 | ||||
-rw-r--r-- | gfx/include/gfx/util/shader.h | 3 | ||||
-rw-r--r-- | gfx/shaders/immediate_mode.frag | 10 | ||||
-rw-r--r-- | gfx/shaders/immediate_mode.vert | 11 | ||||
-rw-r--r-- | gfx/src/gfx.c | 17 | ||||
-rw-r--r-- | gfx/src/render/buffer.c | 32 | ||||
-rw-r--r-- | gfx/src/render/buffer.h | 10 | ||||
-rw-r--r-- | gfx/src/render/geometry.c | 284 | ||||
-rw-r--r-- | gfx/src/render/geometry.h | 8 | ||||
-rw-r--r-- | gfx/src/render/render_backend.c | 35 | ||||
-rw-r--r-- | gfx/src/renderer/imm_renderer.c | 181 | ||||
-rw-r--r-- | gfx/src/renderer/renderer.c | 30 | ||||
-rw-r--r-- | gfx/src/renderer/renderer_impl.h | 42 | ||||
-rw-r--r-- | gfx/src/scene/object.c | 32 | ||||
-rw-r--r-- | gfx/src/scene/scene_memory.c | 3 | ||||
-rw-r--r-- | gfx/src/util/scene.c | 132 | ||||
-rw-r--r-- | gfx/src/util/shader.c | 7 | ||||
-rw-r--r-- | gltfview/src/game.c | 41 |
23 files changed, 823 insertions, 220 deletions
diff --git a/gfx/CMakeLists.txt b/gfx/CMakeLists.txt index d60a59f..8a9bc8f 100644 --- a/gfx/CMakeLists.txt +++ b/gfx/CMakeLists.txt | |||
@@ -16,6 +16,8 @@ add_shader_library(shaders | |||
16 | shaders/cubemap_filtering.vert | 16 | shaders/cubemap_filtering.vert |
17 | shaders/debug3d.frag | 17 | shaders/debug3d.frag |
18 | shaders/debug3d.vert | 18 | shaders/debug3d.vert |
19 | shaders/immediate_mode.frag | ||
20 | shaders/immediate_mode.vert | ||
19 | shaders/irradiance_map.frag | 21 | shaders/irradiance_map.frag |
20 | shaders/prefiltered_environment_map.frag | 22 | shaders/prefiltered_environment_map.frag |
21 | shaders/quad.vert | 23 | shaders/quad.vert |
@@ -39,6 +41,7 @@ add_library(gfx | |||
39 | src/render/shader_program.c | 41 | src/render/shader_program.c |
40 | src/render/shader.c | 42 | src/render/shader.c |
41 | src/render/texture.c | 43 | src/render/texture.c |
44 | src/renderer/imm_renderer.c | ||
42 | src/renderer/renderer.c | 45 | src/renderer/renderer.c |
43 | src/scene/animation.c | 46 | src/scene/animation.c |
44 | src/scene/camera.c | 47 | src/scene/camera.c |
diff --git a/gfx/include/gfx/gfx.h b/gfx/include/gfx/gfx.h index b9b7183..303ddcd 100644 --- a/gfx/include/gfx/gfx.h +++ b/gfx/include/gfx/gfx.h | |||
@@ -1,9 +1,10 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | typedef struct ImmRenderer ImmRenderer; | ||
3 | typedef struct RenderBackend RenderBackend; | 4 | typedef struct RenderBackend RenderBackend; |
4 | typedef struct Renderer Renderer; | 5 | typedef struct Renderer Renderer; |
5 | typedef struct Scene Scene; | 6 | typedef struct Scene Scene; |
6 | typedef struct SceneCamera SceneCamera; | 7 | typedef struct SceneCamera SceneCamera; |
7 | 8 | ||
8 | typedef struct Gfx Gfx; | 9 | typedef struct Gfx Gfx; |
9 | 10 | ||
@@ -19,6 +20,9 @@ RenderBackend* gfx_get_render_backend(Gfx*); | |||
19 | /// Get the renderer. | 20 | /// Get the renderer. |
20 | Renderer* gfx_get_renderer(Gfx*); | 21 | Renderer* gfx_get_renderer(Gfx*); |
21 | 22 | ||
23 | /// Get the immediate mode renderer. | ||
24 | ImmRenderer* gfx_get_imm_renderer(Gfx*); | ||
25 | |||
22 | /// Create a new scene. | 26 | /// Create a new scene. |
23 | Scene* gfx_make_scene(Gfx*); | 27 | Scene* gfx_make_scene(Gfx*); |
24 | 28 | ||
diff --git a/gfx/include/gfx/render_backend.h b/gfx/include/gfx/render_backend.h index ef6e4e9..b07cf16 100644 --- a/gfx/include/gfx/render_backend.h +++ b/gfx/include/gfx/render_backend.h | |||
@@ -6,6 +6,7 @@ | |||
6 | 6 | ||
7 | #include "sizes.h" | 7 | #include "sizes.h" |
8 | 8 | ||
9 | #include <math/aabb3.h> | ||
9 | #include <math/fwd.h> | 10 | #include <math/fwd.h> |
10 | #include <math/mat4.h> | 11 | #include <math/mat4.h> |
11 | #include <math/vec4.h> | 12 | #include <math/vec4.h> |
@@ -47,10 +48,24 @@ typedef enum BufferType { | |||
47 | Buffer2d, | 48 | Buffer2d, |
48 | Buffer3d, | 49 | Buffer3d, |
49 | Buffer4d, | 50 | Buffer4d, |
51 | BufferFloat, | ||
50 | BufferU8, | 52 | BufferU8, |
51 | BufferU16 | 53 | BufferU16 |
52 | } BufferType; | 54 | } BufferType; |
53 | 55 | ||
56 | /// Buffer data descriptor. | ||
57 | typedef struct BufferDataDesc { | ||
58 | union { | ||
59 | const void* data; | ||
60 | const vec2* vec2s; | ||
61 | const vec3* vec3s; | ||
62 | const float* floats; | ||
63 | const uint8_t* u8s; | ||
64 | const uint16_t* u16s; | ||
65 | }; | ||
66 | size_t count; | ||
67 | } BufferDataDesc; | ||
68 | |||
54 | /// Buffer descriptor. | 69 | /// Buffer descriptor. |
55 | /// | 70 | /// |
56 | /// 'count' is the number of elements in the array. For untyped buffers, this is | 71 | /// 'count' is the number of elements in the array. For untyped buffers, this is |
@@ -64,27 +79,20 @@ typedef enum BufferType { | |||
64 | /// Typed buffers don't work well with interleaved vertex attributes. Not sure | 79 | /// Typed buffers don't work well with interleaved vertex attributes. Not sure |
65 | /// this is really worth it. | 80 | /// this is really worth it. |
66 | typedef struct BufferDesc { | 81 | typedef struct BufferDesc { |
67 | BufferUsage usage; | 82 | BufferUsage usage; |
68 | BufferType type; | 83 | BufferType type; |
69 | union { | 84 | BufferDataDesc data; |
70 | const void* data; | ||
71 | const vec2* vec2s; | ||
72 | const vec3* vec3s; | ||
73 | const uint8_t* u8s; | ||
74 | const uint16_t* u16s; | ||
75 | }; | ||
76 | size_t count; | ||
77 | } BufferDesc; | 85 | } BufferDesc; |
78 | 86 | ||
79 | /// A buffer view for vertex data (attributes or indices). | 87 | /// A buffer view for vertex data (attributes or indices). |
80 | /// Either 'data' or 'buffer' must be set. | 88 | /// Either 'data' or 'buffer' must be set. |
81 | #define MAKE_BUFFER_VIEW(NAME, TYPE) \ | 89 | #define MAKE_BUFFER_VIEW(NAME, TYPE) \ |
82 | typedef struct NAME { \ | 90 | typedef struct NAME { \ |
83 | const TYPE* data; \ | 91 | const TYPE* data; \ |
84 | const Buffer* buffer; \ | 92 | Buffer* buffer; \ |
85 | size_t offset_bytes; \ | 93 | size_t offset_bytes; \ |
86 | size_t size_bytes; \ | 94 | size_t size_bytes; \ |
87 | size_t stride_bytes; \ | 95 | size_t stride_bytes; \ |
88 | } NAME; | 96 | } NAME; |
89 | 97 | ||
90 | /// A buffer view for untyped data. | 98 | /// A buffer view for untyped data. |
@@ -116,6 +124,14 @@ MAKE_BUFFER_VIEW(BufferViewIdx16, uint16_t) | |||
116 | 124 | ||
117 | /// Describes a piece of geometry. | 125 | /// Describes a piece of geometry. |
118 | /// | 126 | /// |
127 | /// Buffer views may point to either already-existing GPU buffers or to data in | ||
128 | /// host memory. | ||
129 | /// | ||
130 | /// If the buffer views do not already point to GPU buffers, GPU buffers are | ||
131 | /// created for the geometry. The 'buffer_usage' field specifies the usage for | ||
132 | /// the created buffers. Use BufferStatic for static geometry and BufferDynamic | ||
133 | /// for dynamic geometry. | ||
134 | /// | ||
119 | /// Currently we support only up to 16-bit vertex indices. Might have to change | 135 | /// Currently we support only up to 16-bit vertex indices. Might have to change |
120 | /// this to support a larger variety of 3D models. | 136 | /// this to support a larger variety of 3D models. |
121 | typedef struct GeometryDesc { | 137 | typedef struct GeometryDesc { |
@@ -138,6 +154,8 @@ typedef struct GeometryDesc { | |||
138 | VertexCount num_verts; | 154 | VertexCount num_verts; |
139 | size_t num_indices; | 155 | size_t num_indices; |
140 | PrimitiveType type; | 156 | PrimitiveType type; |
157 | BufferUsage buffer_usage; | ||
158 | aabb3 aabb; | ||
141 | } GeometryDesc; | 159 | } GeometryDesc; |
142 | 160 | ||
143 | /// Shader compiler define. | 161 | /// Shader compiler define. |
@@ -298,6 +316,18 @@ void gfx_set_viewport(RenderBackend*, int width, int height); | |||
298 | /// Get the render backend's viewport dimensions. | 316 | /// Get the render backend's viewport dimensions. |
299 | void gfx_get_viewport(RenderBackend*, int* width, int* height); | 317 | void gfx_get_viewport(RenderBackend*, int* width, int* height); |
300 | 318 | ||
319 | /// Set blending state. | ||
320 | void gfx_set_blending(RenderBackend*, bool enable); | ||
321 | |||
322 | /// Set depth mask. | ||
323 | void gfx_set_depth_mask(RenderBackend*, bool enable); | ||
324 | |||
325 | /// Set cull mode. | ||
326 | void gfx_set_culling(RenderBackend*, bool enable); | ||
327 | |||
328 | /// Set polygon offset. | ||
329 | void gfx_set_polygon_offset(RenderBackend*, float scale, float bias); | ||
330 | |||
301 | // ----------------------------------------------------------------------------- | 331 | // ----------------------------------------------------------------------------- |
302 | // Buffers. | 332 | // Buffers. |
303 | // ----------------------------------------------------------------------------- | 333 | // ----------------------------------------------------------------------------- |
@@ -308,6 +338,9 @@ Buffer* gfx_make_buffer(RenderBackend*, const BufferDesc*); | |||
308 | /// Destroy the buffer. | 338 | /// Destroy the buffer. |
309 | void gfx_destroy_buffer(RenderBackend*, Buffer**); | 339 | void gfx_destroy_buffer(RenderBackend*, Buffer**); |
310 | 340 | ||
341 | /// Update the buffer's data. | ||
342 | void gfx_update_buffer(RenderBackend*, Buffer*, const BufferDataDesc*); | ||
343 | |||
311 | // ----------------------------------------------------------------------------- | 344 | // ----------------------------------------------------------------------------- |
312 | // Geometry. | 345 | // Geometry. |
313 | // ----------------------------------------------------------------------------- | 346 | // ----------------------------------------------------------------------------- |
@@ -321,20 +354,27 @@ void gfx_destroy_geometry(RenderBackend*, Geometry**); | |||
321 | /// Upload new vertex data for the geometry. | 354 | /// Upload new vertex data for the geometry. |
322 | /// | 355 | /// |
323 | /// This is similar to gfx_make_geometry(), but the geometry need not be | 356 | /// This is similar to gfx_make_geometry(), but the geometry need not be |
324 | /// entirely specified. Only the vertex attributes set in the descriptor are | 357 | /// entirely specified. |
325 | /// updated. | 358 | /// |
359 | /// Only the vertex attributes, vertex count, and index count set in the | ||
360 | /// descriptor are updated. Index data, primitive type, and other properties of | ||
361 | /// the geometry are not updated. | ||
326 | /// | 362 | /// |
327 | /// This function only updates vertex attributes, not indices or primitive type. | 363 | /// New data must be given as arrays in host memory. That is, the buffer views |
364 | /// in the descriptor must point to CPU arrays, not GPU buffers. | ||
328 | /// | 365 | /// |
329 | /// Note that the descriptor cannot specify more vertex attributes than the | 366 | /// Note that the descriptor cannot specify a larger vertex or index count than |
330 | /// geometry was created with. If the size or any other attribute not handled | 367 | /// what the geometry was created with. If the geometry size or any other |
331 | /// by this update function needs to be changed, then a new geometry must be | 368 | /// attribute not handled by this update function needs to be changed, then a |
332 | /// created. | 369 | /// new geometry must be created. |
333 | void gfx_update_geometry(Geometry*, const GeometryDesc*); | 370 | void gfx_update_geometry(Geometry*, const GeometryDesc*); |
334 | 371 | ||
335 | /// Render the geometry. | 372 | /// Render the geometry. |
336 | void gfx_render_geometry(const Geometry*); | 373 | void gfx_render_geometry(const Geometry*); |
337 | 374 | ||
375 | /// Return the geometry's bounding box. | ||
376 | aabb3 gfx_get_geometry_aabb(const Geometry*); | ||
377 | |||
338 | // ----------------------------------------------------------------------------- | 378 | // ----------------------------------------------------------------------------- |
339 | // Textures. | 379 | // Textures. |
340 | // ----------------------------------------------------------------------------- | 380 | // ----------------------------------------------------------------------------- |
diff --git a/gfx/include/gfx/renderer.h b/gfx/include/gfx/renderer.h index d82fab6..1517af6 100644 --- a/gfx/include/gfx/renderer.h +++ b/gfx/include/gfx/renderer.h | |||
@@ -1,11 +1,54 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include <math/aabb3.h> | ||
4 | #include <math/camera.h> | ||
5 | #include <math/mat4.h> | ||
6 | #include <math/vec3.h> | ||
7 | #include <math/vec4.h> | ||
8 | |||
3 | typedef struct RenderBackend RenderBackend; | 9 | typedef struct RenderBackend RenderBackend; |
4 | typedef struct Scene Scene; | 10 | typedef struct Scene Scene; |
5 | typedef struct SceneCamera SceneCamera; | 11 | typedef struct SceneCamera SceneCamera; |
12 | |||
13 | typedef struct ImmRenderer ImmRenderer; | ||
14 | typedef struct Renderer Renderer; | ||
6 | 15 | ||
7 | typedef struct Renderer Renderer; | 16 | // ----------------------------------------------------------------------------- |
17 | // Main Renderer. | ||
18 | // ----------------------------------------------------------------------------- | ||
8 | 19 | ||
9 | /// Render the scene. | 20 | /// Render the scene. |
10 | void gfx_render_scene(Renderer*, RenderBackend*, const Scene*, | 21 | void gfx_render_scene(Renderer*, const Scene*, const SceneCamera*); |
11 | const SceneCamera*); | 22 | |
23 | // ----------------------------------------------------------------------------- | ||
24 | // Immediate Mode Renderer. | ||
25 | // ----------------------------------------------------------------------------- | ||
26 | |||
27 | /// Prepare the graphics systems for immediate-mode rendering. | ||
28 | /// | ||
29 | /// Call this before issuing any immediate-mode rendering draws. | ||
30 | void gfx_imm_start(ImmRenderer*); | ||
31 | |||
32 | /// End immediate mode rendering. | ||
33 | /// | ||
34 | /// Call this after issuing immediate-mode rendering draws and before swapping | ||
35 | /// buffers. | ||
36 | void gfx_imm_end(ImmRenderer*); | ||
37 | |||
38 | /// Draw a set of triangles. | ||
39 | void gfx_imm_draw_triangles(ImmRenderer*, const vec3[], size_t num_triangles); | ||
40 | |||
41 | /// Draw a triangle. | ||
42 | void gfx_imm_draw_triangle(ImmRenderer*, const vec3[3]); | ||
43 | |||
44 | /// Draw a bounding box. | ||
45 | void gfx_imm_draw_aabb(ImmRenderer*, aabb3); | ||
46 | |||
47 | /// Set the camera. | ||
48 | void gfx_imm_set_camera(ImmRenderer*, const Camera*); | ||
49 | |||
50 | /// Set the model matrix. | ||
51 | void gfx_imm_set_model_matrix(ImmRenderer*, const mat4*); | ||
52 | |||
53 | /// Set the render colour. | ||
54 | void gfx_imm_set_colour(ImmRenderer*, vec4 colour); | ||
diff --git a/gfx/include/gfx/scene/object.h b/gfx/include/gfx/scene/object.h index 59372c5..ccc9999 100644 --- a/gfx/include/gfx/scene/object.h +++ b/gfx/include/gfx/scene/object.h | |||
@@ -2,6 +2,8 @@ | |||
2 | 2 | ||
3 | #include <math/fwd.h> | 3 | #include <math/fwd.h> |
4 | 4 | ||
5 | #include <math/aabb3.h> | ||
6 | |||
5 | typedef struct Mesh Mesh; | 7 | typedef struct Mesh Mesh; |
6 | typedef struct SceneNode SceneNode; | 8 | typedef struct SceneNode SceneNode; |
7 | typedef struct Skeleton Skeleton; | 9 | typedef struct Skeleton Skeleton; |
@@ -28,3 +30,8 @@ void gfx_remove_object_mesh(SceneObject*, Mesh*); | |||
28 | 30 | ||
29 | /// Set the object's skeleton. | 31 | /// Set the object's skeleton. |
30 | void gfx_set_object_skeleton(SceneObject*, const Skeleton*); | 32 | void gfx_set_object_skeleton(SceneObject*, const Skeleton*); |
33 | |||
34 | /// Computes the object's bounding box. | ||
35 | /// | ||
36 | /// The object's bounding box is the bounding box of its mesh geometries. | ||
37 | aabb3 gfx_calc_object_aabb(const SceneObject*); | ||
diff --git a/gfx/include/gfx/sizes.h b/gfx/include/gfx/sizes.h index 17e7c7d..9f13e30 100644 --- a/gfx/include/gfx/sizes.h +++ b/gfx/include/gfx/sizes.h | |||
@@ -74,6 +74,12 @@ | |||
74 | /// Maximum number of compiler defines in a Shader. | 74 | /// Maximum number of compiler defines in a Shader. |
75 | #define GFX_MAX_SHADER_COMPILER_DEFINES 16 | 75 | #define GFX_MAX_SHADER_COMPILER_DEFINES 16 |
76 | 76 | ||
77 | // Gfx | 77 | // Renderer. |
78 | |||
79 | /// Maximum number of triangles that the immediate-mode renderer can draw in a | ||
80 | /// frame. | ||
81 | #define IMM_MAX_NUM_TRIANGLES 1024 | ||
82 | |||
83 | // Gfx. | ||
78 | 84 | ||
79 | #define GFX_MAX_NUM_SCENES 4 | 85 | #define GFX_MAX_NUM_SCENES 4 |
diff --git a/gfx/include/gfx/util/shader.h b/gfx/include/gfx/util/shader.h index cadca55..9bde8cf 100644 --- a/gfx/include/gfx/util/shader.h +++ b/gfx/include/gfx/util/shader.h | |||
@@ -21,6 +21,9 @@ ShaderProgram* gfx_make_cook_torrance_shader_perm( | |||
21 | /// Create a 3D debugging shader. | 21 | /// Create a 3D debugging shader. |
22 | ShaderProgram* gfx_make_debug3d_shader(RenderBackend*); | 22 | ShaderProgram* gfx_make_debug3d_shader(RenderBackend*); |
23 | 23 | ||
24 | /// Create a shader for drawing in immediate mode. | ||
25 | ShaderProgram* gfx_make_immediate_mode_shader(RenderBackend*); | ||
26 | |||
24 | /// Create a shader for computing irradiance maps from cube maps. | 27 | /// Create a shader for computing irradiance maps from cube maps. |
25 | ShaderProgram* gfx_make_irradiance_map_shader(RenderBackend*); | 28 | ShaderProgram* gfx_make_irradiance_map_shader(RenderBackend*); |
26 | 29 | ||
diff --git a/gfx/shaders/immediate_mode.frag b/gfx/shaders/immediate_mode.frag new file mode 100644 index 0000000..ac23b5c --- /dev/null +++ b/gfx/shaders/immediate_mode.frag | |||
@@ -0,0 +1,10 @@ | |||
1 | precision highp float; | ||
2 | |||
3 | uniform vec4 Colour; | ||
4 | |||
5 | out vec4 FragColour; | ||
6 | |||
7 | void main() | ||
8 | { | ||
9 | FragColour = vec4(pow(Colour.rgb, vec3(1.0/2.2)), Colour.a); | ||
10 | } | ||
diff --git a/gfx/shaders/immediate_mode.vert b/gfx/shaders/immediate_mode.vert new file mode 100644 index 0000000..65070bb --- /dev/null +++ b/gfx/shaders/immediate_mode.vert | |||
@@ -0,0 +1,11 @@ | |||
1 | precision highp float; | ||
2 | |||
3 | uniform mat4 Model; | ||
4 | uniform mat4 ViewProjection; | ||
5 | |||
6 | layout (location = 0) in vec3 vPosition; | ||
7 | |||
8 | void main() | ||
9 | { | ||
10 | gl_Position = ViewProjection * Model * vec4(vPosition, 1.0); | ||
11 | } | ||
diff --git a/gfx/src/gfx.c b/gfx/src/gfx.c index 4640a52..27312b2 100644 --- a/gfx/src/gfx.c +++ b/gfx/src/gfx.c | |||
@@ -13,9 +13,10 @@ | |||
13 | #include <stdlib.h> | 13 | #include <stdlib.h> |
14 | 14 | ||
15 | typedef struct Gfx { | 15 | typedef struct Gfx { |
16 | scene_idx scene; // First child scene. | ||
16 | RenderBackend render_backend; | 17 | RenderBackend render_backend; |
17 | Renderer renderer; | 18 | Renderer renderer; |
18 | scene_idx scene; // First child scene. | 19 | ImmRenderer imm_renderer; |
19 | } Gfx; | 20 | } Gfx; |
20 | 21 | ||
21 | Gfx* gfx_init() { | 22 | Gfx* gfx_init() { |
@@ -33,6 +34,12 @@ Gfx* gfx_init() { | |||
33 | gfx_destroy(&gfx); | 34 | gfx_destroy(&gfx); |
34 | return 0; | 35 | return 0; |
35 | } | 36 | } |
37 | if (!imm_renderer_make(&gfx->imm_renderer, &gfx->render_backend)) { | ||
38 | // TODO: Add error logs to the initialization failure cases here and inside | ||
39 | // the renderers. | ||
40 | gfx_destroy(&gfx); | ||
41 | return 0; | ||
42 | } | ||
36 | scene_mem_init(); | 43 | scene_mem_init(); |
37 | return gfx; | 44 | return gfx; |
38 | } | 45 | } |
@@ -43,7 +50,8 @@ void gfx_destroy(Gfx** gfx) { | |||
43 | return; | 50 | return; |
44 | } | 51 | } |
45 | scene_mem_destroy(); | 52 | scene_mem_destroy(); |
46 | renderer_destroy(&(*gfx)->renderer, &(*gfx)->render_backend); | 53 | renderer_destroy(&(*gfx)->renderer); |
54 | imm_renderer_destroy(&(*gfx)->imm_renderer); | ||
47 | gfx_del_render_backend(&(*gfx)->render_backend); | 55 | gfx_del_render_backend(&(*gfx)->render_backend); |
48 | free(*gfx); | 56 | free(*gfx); |
49 | *gfx = 0; | 57 | *gfx = 0; |
@@ -59,6 +67,11 @@ Renderer* gfx_get_renderer(Gfx* gfx) { | |||
59 | return &gfx->renderer; | 67 | return &gfx->renderer; |
60 | } | 68 | } |
61 | 69 | ||
70 | ImmRenderer* gfx_get_imm_renderer(Gfx* gfx) { | ||
71 | assert(gfx); | ||
72 | return &gfx->imm_renderer; | ||
73 | } | ||
74 | |||
62 | Scene* gfx_make_scene(Gfx* gfx) { | 75 | Scene* gfx_make_scene(Gfx* gfx) { |
63 | Scene* scene = mem_alloc_scene(); | 76 | Scene* scene = mem_alloc_scene(); |
64 | if (!scene) { | 77 | if (!scene) { |
diff --git a/gfx/src/render/buffer.c b/gfx/src/render/buffer.c index 392777c..3c0c794 100644 --- a/gfx/src/render/buffer.c +++ b/gfx/src/render/buffer.c | |||
@@ -6,8 +6,9 @@ | |||
6 | #include <math/vec3.h> | 6 | #include <math/vec3.h> |
7 | #include <math/vec4.h> | 7 | #include <math/vec4.h> |
8 | 8 | ||
9 | static size_t get_buffer_size_bytes(const BufferDesc* desc) { | 9 | static size_t get_buffer_size_bytes( |
10 | switch (desc->type) { | 10 | BufferType type, const BufferDataDesc* desc) { |
11 | switch (type) { | ||
11 | case BufferUntyped: | 12 | case BufferUntyped: |
12 | return desc->count; | 13 | return desc->count; |
13 | case Buffer2d: | 14 | case Buffer2d: |
@@ -16,11 +17,15 @@ static size_t get_buffer_size_bytes(const BufferDesc* desc) { | |||
16 | return desc->count * sizeof(vec3); | 17 | return desc->count * sizeof(vec3); |
17 | case Buffer4d: | 18 | case Buffer4d: |
18 | return desc->count * sizeof(vec4); | 19 | return desc->count * sizeof(vec4); |
20 | case BufferFloat: | ||
21 | return desc->count * sizeof(float); | ||
19 | case BufferU8: | 22 | case BufferU8: |
20 | return desc->count * sizeof(uint8_t); | 23 | return desc->count * sizeof(uint8_t); |
21 | case BufferU16: | 24 | case BufferU16: |
22 | return desc->count * sizeof(uint16_t); | 25 | return desc->count * sizeof(uint16_t); |
23 | } | 26 | } |
27 | assert(false); | ||
28 | return 0; | ||
24 | } | 29 | } |
25 | 30 | ||
26 | static GLenum get_buffer_usage(BufferUsage usage) { | 31 | static GLenum get_buffer_usage(BufferUsage usage) { |
@@ -36,11 +41,13 @@ static GLenum get_buffer_usage(BufferUsage usage) { | |||
36 | 41 | ||
37 | bool gfx_init_buffer(Buffer* buffer, const BufferDesc* desc) { | 42 | bool gfx_init_buffer(Buffer* buffer, const BufferDesc* desc) { |
38 | assert(buffer); | 43 | assert(buffer); |
39 | buffer->size_bytes = get_buffer_size_bytes(desc); | 44 | buffer->type = desc->type; |
45 | buffer->usage = desc->usage; | ||
46 | buffer->size_bytes = get_buffer_size_bytes(desc->type, &desc->data); | ||
40 | const GLenum usage = get_buffer_usage(desc->usage); | 47 | const GLenum usage = get_buffer_usage(desc->usage); |
41 | glGenBuffers(1, &buffer->vbo); | 48 | glGenBuffers(1, &buffer->vbo); |
42 | glBindBuffer(GL_ARRAY_BUFFER, buffer->vbo); | 49 | glBindBuffer(GL_ARRAY_BUFFER, buffer->vbo); |
43 | glBufferData(GL_ARRAY_BUFFER, buffer->size_bytes, desc->data, usage); | 50 | glBufferData(GL_ARRAY_BUFFER, buffer->size_bytes, desc->data.data, usage); |
44 | glBindBuffer(GL_ARRAY_BUFFER, 0); | 51 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
45 | ASSERT_GL; | 52 | ASSERT_GL; |
46 | return true; | 53 | return true; |
@@ -53,3 +60,20 @@ void gfx_del_buffer(Buffer* buffer) { | |||
53 | buffer->vbo = 0; | 60 | buffer->vbo = 0; |
54 | } | 61 | } |
55 | } | 62 | } |
63 | |||
64 | void gfx_update_buffer( | ||
65 | RenderBackend* render_backend, Buffer* buffer, const BufferDataDesc* desc) { | ||
66 | assert(render_backend); | ||
67 | assert(buffer); | ||
68 | assert(desc); | ||
69 | // OpenGL allows updating static buffers, but it is not optimal for | ||
70 | // performance, so we enforce data in static buffers remain static. | ||
71 | assert(buffer->usage == BufferDynamic); | ||
72 | |||
73 | const size_t update_size_bytes = get_buffer_size_bytes(buffer->type, desc); | ||
74 | assert(update_size_bytes <= buffer->size_bytes); | ||
75 | |||
76 | glBindBuffer(GL_ARRAY_BUFFER, buffer->vbo); | ||
77 | glBufferSubData(GL_ARRAY_BUFFER, 0, update_size_bytes, desc->data); | ||
78 | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||
79 | } | ||
diff --git a/gfx/src/render/buffer.h b/gfx/src/render/buffer.h index 575fbb9..0c81e7b 100644 --- a/gfx/src/render/buffer.h +++ b/gfx/src/render/buffer.h | |||
@@ -1,5 +1,7 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include <gfx/render_backend.h> | ||
4 | |||
3 | #include "gl_util.h" | 5 | #include "gl_util.h" |
4 | 6 | ||
5 | #include <math/fwd.h> | 7 | #include <math/fwd.h> |
@@ -7,11 +9,11 @@ | |||
7 | #include <stdbool.h> | 9 | #include <stdbool.h> |
8 | #include <stddef.h> | 10 | #include <stddef.h> |
9 | 11 | ||
10 | typedef struct BufferDesc BufferDesc; | ||
11 | |||
12 | typedef struct Buffer { | 12 | typedef struct Buffer { |
13 | GLuint vbo; | 13 | GLuint vbo; |
14 | size_t size_bytes; | 14 | BufferType type; |
15 | BufferUsage usage; | ||
16 | size_t size_bytes; | ||
15 | } Buffer; | 17 | } Buffer; |
16 | 18 | ||
17 | /// Create a buffer from raw data. | 19 | /// Create a buffer from raw data. |
diff --git a/gfx/src/render/geometry.c b/gfx/src/render/geometry.c index 076a956..e9d3ae5 100644 --- a/gfx/src/render/geometry.c +++ b/gfx/src/render/geometry.c | |||
@@ -6,7 +6,11 @@ | |||
6 | #include <math/vec2.h> | 6 | #include <math/vec2.h> |
7 | #include <math/vec3.h> | 7 | #include <math/vec3.h> |
8 | 8 | ||
9 | #define view_is_populated(BUFFER_VIEW) (BUFFER_VIEW.data || BUFFER_VIEW.buffer) | 9 | /// Determines whether a view is populated. |
10 | /// | ||
11 | /// Note that views are allowed to have no data, in which case a buffer of the | ||
12 | /// specified size is created. | ||
13 | #define view_is_populated(BUFFER_VIEW) (BUFFER_VIEW.size_bytes > 0) | ||
10 | 14 | ||
11 | static GLenum primitive_type_to_gl(PrimitiveType type) { | 15 | static GLenum primitive_type_to_gl(PrimitiveType type) { |
12 | switch (type) { | 16 | switch (type) { |
@@ -22,23 +26,18 @@ static GLenum primitive_type_to_gl(PrimitiveType type) { | |||
22 | } | 26 | } |
23 | } | 27 | } |
24 | 28 | ||
25 | /// Create a buffer for the view. | 29 | /// Create a buffer for the buffer view if the view does not already point to |
26 | /// | 30 | /// a buffer. |
27 | /// If the view already has a buffer, return the buffer. Otherwise create a | 31 | #define INIT_VIEW_BUFFER(render_backend, view, buffer_type, buffer_usage) \ |
28 | /// buffer from the view's data and assign it to the view. | 32 | if (!view.buffer) { \ |
29 | static const Buffer* get_or_make_view_buffer( | 33 | view.buffer = (__typeof__(view.buffer))gfx_make_buffer( \ |
30 | RenderBackend* render_backend, BufferView* view) { | 34 | render_backend, &(BufferDesc){ \ |
31 | if (view->buffer) { | 35 | .usage = buffer_usage, \ |
32 | return view->buffer; | 36 | .type = buffer_type, \ |
33 | } else { | 37 | .data.data = view.data, \ |
34 | return view->buffer = gfx_make_buffer( | 38 | .data.count = view.size_bytes}); \ |
35 | render_backend, &(BufferDesc){ | 39 | } \ |
36 | .usage = BufferStatic, | 40 | assert(view.size_bytes <= view.buffer->size_bytes); |
37 | .type = BufferUntyped, | ||
38 | .data = view->data, | ||
39 | .count = view->size_bytes}); | ||
40 | } | ||
41 | } | ||
42 | 41 | ||
43 | /// Create a buffer for the view, then configure it in the VAO. | 42 | /// Create a buffer for the view, then configure it in the VAO. |
44 | static bool configure_buffer( | 43 | static bool configure_buffer( |
@@ -48,15 +47,13 @@ static bool configure_buffer( | |||
48 | assert(render_backend); | 47 | assert(render_backend); |
49 | assert(desc); | 48 | assert(desc); |
50 | assert(view); | 49 | assert(view); |
50 | assert(view->buffer); | ||
51 | assert( | 51 | assert( |
52 | desc->num_verts <= | 52 | desc->num_verts <= |
53 | view->size_bytes / (num_components * component_size_bytes)); | 53 | view->size_bytes / (num_components * component_size_bytes)); |
54 | const Buffer* buffer = get_or_make_view_buffer(render_backend, view); | 54 | assert(view->size_bytes <= view->buffer->size_bytes); |
55 | if (!buffer) { | 55 | |
56 | return false; | 56 | glBindBuffer(GL_ARRAY_BUFFER, view->buffer->vbo); |
57 | } | ||
58 | assert(view->size_bytes <= buffer->size_bytes); | ||
59 | glBindBuffer(GL_ARRAY_BUFFER, buffer->vbo); | ||
60 | glEnableVertexAttribArray(channel); | 57 | glEnableVertexAttribArray(channel); |
61 | if ((component_type == GL_FLOAT) || normalized) { | 58 | if ((component_type == GL_FLOAT) || normalized) { |
62 | glVertexAttribPointer( | 59 | glVertexAttribPointer( |
@@ -72,6 +69,8 @@ static bool configure_buffer( | |||
72 | channel, num_components, component_type, view->stride_bytes, | 69 | channel, num_components, component_type, view->stride_bytes, |
73 | (const void*)view->offset_bytes); | 70 | (const void*)view->offset_bytes); |
74 | } | 71 | } |
72 | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||
73 | |||
75 | return true; | 74 | return true; |
76 | } | 75 | } |
77 | 76 | ||
@@ -80,74 +79,138 @@ static bool configure_vertex_attributes( | |||
80 | assert(render_backend); | 79 | assert(render_backend); |
81 | assert(desc); | 80 | assert(desc); |
82 | 81 | ||
83 | bool result = true; | ||
84 | |||
85 | if (view_is_populated(desc->positions3d)) { | 82 | if (view_is_populated(desc->positions3d)) { |
86 | result = | 83 | INIT_VIEW_BUFFER( |
87 | result && configure_buffer( | 84 | render_backend, desc->positions3d, Buffer3d, desc->buffer_usage); |
88 | render_backend, desc, (BufferView*)&desc->positions3d, 3, | 85 | if (!desc->positions3d.buffer || |
89 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); | 86 | !configure_buffer( |
87 | render_backend, desc, (BufferView*)&desc->positions3d, 3, | ||
88 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL)) { | ||
89 | return false; | ||
90 | } | ||
90 | } else if (view_is_populated(desc->positions2d)) { | 91 | } else if (view_is_populated(desc->positions2d)) { |
91 | result = | 92 | INIT_VIEW_BUFFER( |
92 | result && configure_buffer( | 93 | render_backend, desc->positions2d, Buffer2d, desc->buffer_usage); |
93 | render_backend, desc, (BufferView*)&desc->positions2d, 2, | 94 | if (!desc->positions2d.buffer || |
94 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); | 95 | !configure_buffer( |
96 | render_backend, desc, (BufferView*)&desc->positions2d, 2, | ||
97 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL)) { | ||
98 | return false; | ||
99 | } | ||
95 | } | 100 | } |
96 | |||
97 | if (view_is_populated(desc->normals)) { | 101 | if (view_is_populated(desc->normals)) { |
98 | result = | 102 | INIT_VIEW_BUFFER( |
99 | result && configure_buffer( | 103 | render_backend, desc->normals, Buffer3d, desc->buffer_usage); |
100 | render_backend, desc, (BufferView*)&desc->normals, 3, | 104 | if (!desc->normals.buffer || |
101 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_NORMAL_CHANNEL); | 105 | !configure_buffer( |
106 | render_backend, desc, (BufferView*)&desc->normals, 3, sizeof(float), | ||
107 | GL_FLOAT, GL_FALSE, GFX_NORMAL_CHANNEL)) { | ||
108 | return false; | ||
109 | } | ||
102 | } | 110 | } |
103 | |||
104 | if (view_is_populated(desc->tangents)) { | 111 | if (view_is_populated(desc->tangents)) { |
105 | result = | 112 | INIT_VIEW_BUFFER( |
106 | result && configure_buffer( | 113 | render_backend, desc->tangents, Buffer4d, desc->buffer_usage); |
107 | render_backend, desc, (BufferView*)&desc->tangents, 4, | 114 | if (!desc->tangents.buffer || |
108 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_TANGENT_CHANNEL); | 115 | !configure_buffer( |
116 | render_backend, desc, (BufferView*)&desc->tangents, 4, | ||
117 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_TANGENT_CHANNEL)) { | ||
118 | return false; | ||
119 | } | ||
109 | } | 120 | } |
110 | |||
111 | if (view_is_populated(desc->texcoords)) { | 121 | if (view_is_populated(desc->texcoords)) { |
112 | result = | 122 | INIT_VIEW_BUFFER( |
113 | result && configure_buffer( | 123 | render_backend, desc->texcoords, Buffer2d, desc->buffer_usage); |
114 | render_backend, desc, (BufferView*)&desc->texcoords, 2, | 124 | if (!desc->texcoords.buffer || |
115 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_TEXCOORDS_CHANNEL); | 125 | !configure_buffer( |
126 | render_backend, desc, (BufferView*)&desc->texcoords, 2, | ||
127 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_TEXCOORDS_CHANNEL)) { | ||
128 | return false; | ||
129 | } | ||
116 | } | 130 | } |
117 | |||
118 | if (view_is_populated(desc->joints.u8)) { | 131 | if (view_is_populated(desc->joints.u8)) { |
119 | result = result && configure_buffer( | 132 | INIT_VIEW_BUFFER( |
120 | render_backend, desc, (BufferView*)&desc->joints.u8, | 133 | render_backend, desc->joints.u8, BufferU8, desc->buffer_usage); |
121 | 4, sizeof(uint8_t), GL_UNSIGNED_BYTE, GL_FALSE, | 134 | if (!desc->joints.u8.buffer || |
122 | GFX_JOINTS_CHANNEL); | 135 | !configure_buffer( |
136 | render_backend, desc, (BufferView*)&desc->joints.u8, 4, | ||
137 | sizeof(uint8_t), GL_UNSIGNED_BYTE, GL_FALSE, GFX_JOINTS_CHANNEL)) { | ||
138 | return false; | ||
139 | } | ||
123 | } else if (view_is_populated(desc->joints.u16)) { | 140 | } else if (view_is_populated(desc->joints.u16)) { |
124 | result = result && configure_buffer( | 141 | INIT_VIEW_BUFFER( |
125 | render_backend, desc, (BufferView*)&desc->joints.u16, | 142 | render_backend, desc->joints.u16, BufferU16, desc->buffer_usage); |
126 | 4, sizeof(uint16_t), GL_UNSIGNED_SHORT, GL_FALSE, | 143 | if (!desc->joints.u16.buffer || |
127 | GFX_JOINTS_CHANNEL); | 144 | !configure_buffer( |
145 | render_backend, desc, (BufferView*)&desc->joints.u16, 4, | ||
146 | sizeof(uint16_t), GL_UNSIGNED_SHORT, GL_FALSE, | ||
147 | GFX_JOINTS_CHANNEL)) { | ||
148 | return false; | ||
149 | } | ||
128 | } | 150 | } |
129 | 151 | ||
130 | // If weights are given as unsigned integers, then they are normalized when | 152 | // If weights are given as unsigned integers, then they are normalized when |
131 | // read by the shader. | 153 | // read by the shader. |
132 | if (view_is_populated(desc->weights.u8)) { | 154 | if (view_is_populated(desc->weights.u8)) { |
133 | result = result && configure_buffer( | 155 | INIT_VIEW_BUFFER( |
134 | render_backend, desc, (BufferView*)&desc->weights.u8, | 156 | render_backend, desc->weights.u8, BufferU8, desc->buffer_usage); |
135 | 4, sizeof(uint8_t), GL_UNSIGNED_BYTE, GL_TRUE, | 157 | if (!desc->weights.u8.buffer || |
136 | GFX_WEIGHTS_CHANNEL); | 158 | !configure_buffer( |
159 | render_backend, desc, (BufferView*)&desc->weights.u8, 4, | ||
160 | sizeof(uint8_t), GL_UNSIGNED_BYTE, GL_TRUE, GFX_WEIGHTS_CHANNEL)) { | ||
161 | return false; | ||
162 | } | ||
137 | } else if (view_is_populated(desc->weights.u16)) { | 163 | } else if (view_is_populated(desc->weights.u16)) { |
138 | result = result && configure_buffer( | 164 | INIT_VIEW_BUFFER( |
139 | render_backend, desc, | 165 | render_backend, desc->weights.u16, BufferU16, desc->buffer_usage); |
140 | (BufferView*)&desc->weights.u16, 4, sizeof(uint16_t), | 166 | if (!desc->weights.u16.buffer || |
141 | GL_UNSIGNED_SHORT, GL_TRUE, GFX_WEIGHTS_CHANNEL); | 167 | !configure_buffer( |
168 | render_backend, desc, (BufferView*)&desc->weights.u16, 4, | ||
169 | sizeof(uint16_t), GL_UNSIGNED_SHORT, GL_TRUE, | ||
170 | GFX_WEIGHTS_CHANNEL)) { | ||
171 | return false; | ||
172 | } | ||
142 | } else if (view_is_populated(desc->weights.floats)) { | 173 | } else if (view_is_populated(desc->weights.floats)) { |
143 | result = result && | 174 | INIT_VIEW_BUFFER( |
144 | configure_buffer( | 175 | render_backend, desc->weights.floats, BufferFloat, desc->buffer_usage); |
145 | render_backend, desc, (BufferView*)&desc->weights.floats, 4, | 176 | if (!desc->weights.floats.buffer || |
146 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_WEIGHTS_CHANNEL); | 177 | !configure_buffer( |
178 | render_backend, desc, (BufferView*)&desc->weights.floats, 4, | ||
179 | sizeof(float), GL_FLOAT, GL_FALSE, GFX_WEIGHTS_CHANNEL)) { | ||
180 | return false; | ||
181 | } | ||
147 | } | 182 | } |
148 | 183 | ||
149 | glBindBuffer(GL_ARRAY_BUFFER, 0); | 184 | return true; |
150 | return result; | 185 | } |
186 | |||
187 | static bool configure_indices( | ||
188 | RenderBackend* render_backend, GeometryDesc* desc) { | ||
189 | assert(render_backend); | ||
190 | assert(desc); | ||
191 | |||
192 | if (view_is_populated(desc->indices8)) { | ||
193 | assert(desc->num_indices > 0); | ||
194 | assert( | ||
195 | desc->num_indices <= desc->indices8.size_bytes / sizeof(VertexIndex8)); | ||
196 | INIT_VIEW_BUFFER( | ||
197 | render_backend, desc->indices8, BufferU8, desc->buffer_usage); | ||
198 | if (!desc->indices8.buffer) { | ||
199 | return false; | ||
200 | } | ||
201 | } else if (view_is_populated(desc->indices16)) { | ||
202 | assert(desc->num_indices > 0); | ||
203 | assert( | ||
204 | desc->num_indices <= | ||
205 | desc->indices16.size_bytes / sizeof(VertexIndex16)); | ||
206 | INIT_VIEW_BUFFER( | ||
207 | render_backend, desc->indices16, BufferU16, desc->buffer_usage); | ||
208 | if (!desc->indices16.buffer) { | ||
209 | return false; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | return true; | ||
151 | } | 214 | } |
152 | 215 | ||
153 | bool gfx_init_geometry( | 216 | bool gfx_init_geometry( |
@@ -163,47 +226,29 @@ bool gfx_init_geometry( | |||
163 | 226 | ||
164 | geometry->mode = primitive_type_to_gl(input_desc->type); | 227 | geometry->mode = primitive_type_to_gl(input_desc->type); |
165 | geometry->desc = *input_desc; | 228 | geometry->desc = *input_desc; |
229 | geometry->num_verts = input_desc->num_verts; | ||
166 | geometry->render_backend = render_backend; | 230 | geometry->render_backend = render_backend; |
167 | 231 | ||
168 | // We manipulate the descriptor copy below. Create a shorter name for it. | 232 | // The geometry's copy of the descriptor is manipulated below. Create a |
233 | // shorter name for it. | ||
169 | GeometryDesc* desc = &geometry->desc; | 234 | GeometryDesc* desc = &geometry->desc; |
170 | 235 | ||
171 | glGenVertexArrays(1, &geometry->vao); | 236 | glGenVertexArrays(1, &geometry->vao); |
172 | glBindVertexArray(geometry->vao); | 237 | glBindVertexArray(geometry->vao); |
173 | |||
174 | if (!configure_vertex_attributes(render_backend, desc)) { | 238 | if (!configure_vertex_attributes(render_backend, desc)) { |
175 | gfx_del_geometry(geometry); | 239 | goto cleanup; |
176 | return false; | ||
177 | } | 240 | } |
178 | 241 | if (!configure_indices(render_backend, desc)) { | |
179 | if (view_is_populated(desc->indices8)) { | 242 | goto cleanup; |
180 | assert(desc->num_indices > 0); | ||
181 | assert( | ||
182 | desc->num_indices <= desc->indices8.size_bytes / sizeof(VertexIndex8)); | ||
183 | const Buffer* buffer = | ||
184 | get_or_make_view_buffer(render_backend, (BufferView*)&desc->indices8); | ||
185 | if (!buffer) { | ||
186 | gfx_del_geometry(geometry); | ||
187 | return false; | ||
188 | } | ||
189 | assert(desc->indices8.size_bytes <= buffer->size_bytes); | ||
190 | } else if (view_is_populated(desc->indices16)) { | ||
191 | assert(desc->num_indices > 0); | ||
192 | assert( | ||
193 | desc->num_indices <= | ||
194 | desc->indices16.size_bytes / sizeof(VertexIndex16)); | ||
195 | const Buffer* buffer = | ||
196 | get_or_make_view_buffer(render_backend, (BufferView*)&desc->indices16); | ||
197 | if (!buffer) { | ||
198 | gfx_del_geometry(geometry); | ||
199 | return false; | ||
200 | } | ||
201 | assert(desc->indices16.size_bytes <= buffer->size_bytes); | ||
202 | } | 243 | } |
203 | |||
204 | glBindVertexArray(0); | 244 | glBindVertexArray(0); |
205 | ASSERT_GL; | 245 | ASSERT_GL; |
206 | return geometry; | 246 | |
247 | return true; | ||
248 | |||
249 | cleanup: | ||
250 | gfx_del_geometry(geometry); | ||
251 | return 0; | ||
207 | } | 252 | } |
208 | 253 | ||
209 | void gfx_del_geometry(Geometry* geometry) { | 254 | void gfx_del_geometry(Geometry* geometry) { |
@@ -217,6 +262,7 @@ void gfx_del_geometry(Geometry* geometry) { | |||
217 | void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { | 262 | void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { |
218 | assert(geometry); | 263 | assert(geometry); |
219 | assert(desc); | 264 | assert(desc); |
265 | // New geometry size cannot exceed original size. | ||
220 | assert(desc->positions3d.size_bytes <= geometry->desc.positions3d.size_bytes); | 266 | assert(desc->positions3d.size_bytes <= geometry->desc.positions3d.size_bytes); |
221 | assert(desc->positions2d.size_bytes <= geometry->desc.positions2d.size_bytes); | 267 | assert(desc->positions2d.size_bytes <= geometry->desc.positions2d.size_bytes); |
222 | assert(desc->normals.size_bytes <= geometry->desc.normals.size_bytes); | 268 | assert(desc->normals.size_bytes <= geometry->desc.normals.size_bytes); |
@@ -230,22 +276,32 @@ void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { | |||
230 | desc->weights.floats.size_bytes <= | 276 | desc->weights.floats.size_bytes <= |
231 | geometry->desc.weights.floats.size_bytes); | 277 | geometry->desc.weights.floats.size_bytes); |
232 | 278 | ||
233 | geometry->desc = *desc; | 279 | if (desc->positions3d.data) { |
280 | // The geometry must already have an underlying GPU buffer. | ||
281 | assert(geometry->desc.positions3d.buffer); | ||
282 | gfx_update_buffer( | ||
283 | geometry->render_backend, geometry->desc.positions3d.buffer, | ||
284 | &(BufferDataDesc){ | ||
285 | .vec3s = desc->positions3d.data, | ||
286 | .count = desc->positions3d.size_bytes / sizeof(vec3)}); | ||
287 | } | ||
288 | // TODO: more | ||
289 | else { | ||
290 | assert(false && "TODO: gfx_update_geometry() - handle other buffer types"); | ||
291 | } | ||
234 | 292 | ||
235 | glBindVertexArray(geometry->vao); | 293 | if (desc->num_verts != 0) { |
236 | bool result = | 294 | geometry->num_verts = desc->num_verts; |
237 | configure_vertex_attributes(geometry->render_backend, &geometry->desc); | 295 | } |
238 | // Shouldn't fail since we're just uploading buffer data, not creating new | ||
239 | // buffers. | ||
240 | assert(result); | ||
241 | glBindVertexArray(0); | ||
242 | } | 296 | } |
243 | 297 | ||
244 | void gfx_render_geometry(const Geometry* geometry) { | 298 | void gfx_render_geometry(const Geometry* geometry) { |
245 | assert(geometry); | 299 | assert(geometry); |
246 | assert(geometry->vao); | 300 | assert(geometry->vao); |
301 | |||
247 | const GeometryDesc* desc = &geometry->desc; | 302 | const GeometryDesc* desc = &geometry->desc; |
248 | glBindVertexArray(geometry->vao); | 303 | glBindVertexArray(geometry->vao); |
304 | |||
249 | if (desc->indices8.buffer) { | 305 | if (desc->indices8.buffer) { |
250 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices8.buffer->vbo); | 306 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices8.buffer->vbo); |
251 | glDrawElements( | 307 | glDrawElements( |
@@ -257,7 +313,13 @@ void gfx_render_geometry(const Geometry* geometry) { | |||
257 | geometry->mode, desc->num_indices, GL_UNSIGNED_SHORT, | 313 | geometry->mode, desc->num_indices, GL_UNSIGNED_SHORT, |
258 | (const void*)desc->indices16.offset_bytes); | 314 | (const void*)desc->indices16.offset_bytes); |
259 | } else { | 315 | } else { |
260 | glDrawArrays(geometry->mode, 0, desc->num_verts); | 316 | glDrawArrays(geometry->mode, 0, geometry->num_verts); |
261 | } | 317 | } |
318 | |||
262 | glBindVertexArray(0); | 319 | glBindVertexArray(0); |
263 | } | 320 | } |
321 | |||
322 | aabb3 gfx_get_geometry_aabb(const Geometry* geometry) { | ||
323 | assert(geometry); | ||
324 | return geometry->desc.aabb; | ||
325 | } | ||
diff --git a/gfx/src/render/geometry.h b/gfx/src/render/geometry.h index 8fb36da..484934d 100644 --- a/gfx/src/render/geometry.h +++ b/gfx/src/render/geometry.h | |||
@@ -13,9 +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 | GeometryDesc desc; | 18 | GeometryDesc desc; |
19 | size_t num_verts; // May differ from the initial value in the descriptor if | ||
20 | // the geometry is updated. | ||
19 | RenderBackend* render_backend; | 21 | RenderBackend* render_backend; |
20 | } Geometry; | 22 | } Geometry; |
21 | 23 | ||
diff --git a/gfx/src/render/render_backend.c b/gfx/src/render/render_backend.c index 8feefab..defc164 100644 --- a/gfx/src/render/render_backend.c +++ b/gfx/src/render/render_backend.c | |||
@@ -96,6 +96,41 @@ void gfx_get_viewport(RenderBackend* render_backend, int* width, int* height) { | |||
96 | *height = render_backend->viewport.height; | 96 | *height = render_backend->viewport.height; |
97 | } | 97 | } |
98 | 98 | ||
99 | void gfx_set_blending(RenderBackend* render_backend, bool enable) { | ||
100 | assert(render_backend); | ||
101 | if (enable) { | ||
102 | glEnable(GL_BLEND); | ||
103 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
104 | } else { | ||
105 | glDisable(GL_BLEND); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | void gfx_set_depth_mask(RenderBackend* render_backend, bool enable) { | ||
110 | assert(render_backend); | ||
111 | glDepthMask(enable ? GL_TRUE : GL_FALSE); | ||
112 | } | ||
113 | |||
114 | void gfx_set_culling(RenderBackend* render_backend, bool enable) { | ||
115 | assert(render_backend); | ||
116 | if (enable) { | ||
117 | glEnable(GL_CULL_FACE); | ||
118 | } else { | ||
119 | glDisable(GL_CULL_FACE); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | void gfx_set_polygon_offset( | ||
124 | RenderBackend* render_backend, float scale, float bias) { | ||
125 | assert(render_backend); | ||
126 | if ((scale != 0.0f) || (bias != 0.0f)) { | ||
127 | glEnable(GL_POLYGON_OFFSET_FILL); | ||
128 | } else { | ||
129 | glDisable(GL_POLYGON_OFFSET_FILL); | ||
130 | } | ||
131 | glPolygonOffset(scale, bias); | ||
132 | } | ||
133 | |||
99 | // ----------------------------------------------------------------------------- | 134 | // ----------------------------------------------------------------------------- |
100 | // Buffers. | 135 | // Buffers. |
101 | // ----------------------------------------------------------------------------- | 136 | // ----------------------------------------------------------------------------- |
diff --git a/gfx/src/renderer/imm_renderer.c b/gfx/src/renderer/imm_renderer.c new file mode 100644 index 0000000..e9f98f8 --- /dev/null +++ b/gfx/src/renderer/imm_renderer.c | |||
@@ -0,0 +1,181 @@ | |||
1 | #include "renderer_impl.h" | ||
2 | |||
3 | #include <gfx/render_backend.h> | ||
4 | #include <gfx/util/shader.h> | ||
5 | |||
6 | #include <math/aabb3.h> | ||
7 | |||
8 | #include <assert.h> | ||
9 | #include <string.h> // memcpy | ||
10 | |||
11 | #include <log/log.h> // TODO: remove | ||
12 | |||
13 | bool imm_renderer_make(ImmRenderer* renderer, RenderBackend* render_backend) { | ||
14 | assert(renderer); | ||
15 | assert(render_backend); | ||
16 | |||
17 | const size_t num_triangle_verts = IMM_MAX_NUM_TRIANGLES * 3; | ||
18 | |||
19 | renderer->render_backend = render_backend; | ||
20 | |||
21 | renderer->triangles = gfx_make_geometry( | ||
22 | render_backend, | ||
23 | &(GeometryDesc){ | ||
24 | .type = Triangles, | ||
25 | .buffer_usage = BufferDynamic, | ||
26 | .num_verts = num_triangle_verts, | ||
27 | .positions3d = | ||
28 | (BufferView3d){.size_bytes = num_triangle_verts * sizeof(vec3)}}); | ||
29 | if (!renderer->triangles) { | ||
30 | goto cleanup; | ||
31 | } | ||
32 | |||
33 | renderer->shader = gfx_make_immediate_mode_shader(render_backend); | ||
34 | if (!renderer->shader) { | ||
35 | goto cleanup; | ||
36 | } | ||
37 | |||
38 | gfx_imm_set_colour(renderer, vec4_make(0.0, 0.0, 0.0, 1.0)); | ||
39 | |||
40 | return true; | ||
41 | |||
42 | cleanup: | ||
43 | imm_renderer_destroy(renderer); | ||
44 | return false; | ||
45 | } | ||
46 | |||
47 | void imm_renderer_destroy(ImmRenderer* renderer) { | ||
48 | assert(renderer); | ||
49 | assert(renderer->render_backend); | ||
50 | |||
51 | if (renderer->triangles) { | ||
52 | gfx_destroy_geometry(renderer->render_backend, &renderer->triangles); | ||
53 | // TODO: Could also destroy the geometry's buffers here. | ||
54 | } | ||
55 | |||
56 | if (renderer->shader) { | ||
57 | gfx_destroy_shader_program(renderer->render_backend, &renderer->shader); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | void imm_renderer_flush(ImmRenderer* renderer) { | ||
62 | assert(renderer); | ||
63 | |||
64 | if (renderer->num_triangle_verts > 0) { | ||
65 | gfx_update_geometry( | ||
66 | renderer->triangles, | ||
67 | &(GeometryDesc){ | ||
68 | .num_verts = renderer->num_triangle_verts, | ||
69 | .positions3d = (BufferView3d){ | ||
70 | .data = renderer->triangle_verts, | ||
71 | .size_bytes = renderer->num_triangle_verts * sizeof(vec3)} | ||
72 | }); | ||
73 | |||
74 | gfx_apply_uniforms(renderer->shader); | ||
75 | gfx_render_geometry(renderer->triangles); | ||
76 | |||
77 | renderer->num_triangle_verts = 0; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | void gfx_imm_start(ImmRenderer* renderer) { | ||
82 | assert(renderer); | ||
83 | |||
84 | ShaderProgram* shader = renderer->shader; | ||
85 | gfx_activate_shader_program(shader); | ||
86 | // TODO: Having to apply manually is annoying. Maybe just apply implicitly | ||
87 | // when the program is activated? | ||
88 | // Note that then we'll need to be able to just gfx_apply_uniforms() in | ||
89 | // flush(). | ||
90 | // gfx_set_culling(renderer->render_backend, false); | ||
91 | gfx_apply_uniforms(shader); | ||
92 | gfx_set_blending(renderer->render_backend, true); | ||
93 | gfx_set_depth_mask(renderer->render_backend, false); | ||
94 | gfx_set_polygon_offset(renderer->render_backend, 0.5f, 0.5f); | ||
95 | } | ||
96 | |||
97 | void gfx_imm_end(ImmRenderer* renderer) { | ||
98 | assert(renderer); | ||
99 | |||
100 | imm_renderer_flush(renderer); | ||
101 | |||
102 | gfx_set_polygon_offset(renderer->render_backend, 0.0f, 0.0f); | ||
103 | gfx_set_depth_mask(renderer->render_backend, true); | ||
104 | gfx_set_blending(renderer->render_backend, false); | ||
105 | // gfx_set_culling(renderer->render_backend, true); | ||
106 | gfx_deactivate_shader_program(renderer->shader); | ||
107 | } | ||
108 | |||
109 | void gfx_imm_draw_triangles( | ||
110 | ImmRenderer* renderer, const vec3 verts[], size_t num_triangles) { | ||
111 | assert(renderer); | ||
112 | assert(verts); | ||
113 | const size_t new_verts = num_triangles * 3; | ||
114 | assert( | ||
115 | renderer->num_triangle_verts + new_verts < (IMM_MAX_NUM_TRIANGLES * 3)); | ||
116 | |||
117 | memcpy( | ||
118 | renderer->triangle_verts + renderer->num_triangle_verts, verts, | ||
119 | new_verts * sizeof(vec3)); | ||
120 | |||
121 | renderer->num_triangle_verts += new_verts; | ||
122 | } | ||
123 | |||
124 | void gfx_imm_draw_triangle(ImmRenderer* renderer, const vec3 verts[3]) { | ||
125 | gfx_imm_draw_triangles(renderer, verts, 1); | ||
126 | } | ||
127 | |||
128 | void gfx_imm_draw_aabb(ImmRenderer* renderer, aabb3 box) { | ||
129 | assert(renderer); | ||
130 | |||
131 | const vec3 verts[8] = { | ||
132 | box.min, // 2 ----- 6 | ||
133 | vec3_make(box.min.x, box.min.y, box.max.z), // / /| | ||
134 | vec3_make(box.min.x, box.max.y, box.min.z), // 3 ----- 7 | | ||
135 | vec3_make(box.min.x, box.max.y, box.max.z), // | | | | ||
136 | vec3_make(box.max.x, box.min.y, box.min.z), // | 0 ----- 4 | ||
137 | vec3_make(box.max.x, box.min.y, box.max.z), // |/ |/ | ||
138 | vec3_make(box.max.x, box.max.y, box.min.z), // 1 ----- 5 | ||
139 | box.max}; | ||
140 | |||
141 | #define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2] | ||
142 | |||
143 | // TODO: Use vertex indices in Geometry. | ||
144 | const vec3 tris[36] = {// Front. | ||
145 | tri(1, 5, 7), tri(1, 7, 3), | ||
146 | // Right. | ||
147 | tri(5, 4, 6), tri(5, 6, 7), | ||
148 | // Back. | ||
149 | tri(4, 0, 2), tri(4, 2, 6), | ||
150 | // Left. | ||
151 | tri(0, 1, 3), tri(0, 3, 2), | ||
152 | // Top. | ||
153 | tri(3, 7, 6), tri(3, 6, 2), | ||
154 | // Bottom. | ||
155 | tri(0, 4, 5), tri(0, 5, 1)}; | ||
156 | |||
157 | gfx_imm_draw_triangles(renderer, tris, 12); | ||
158 | } | ||
159 | |||
160 | void gfx_imm_set_camera(ImmRenderer* renderer, const Camera* camera) { | ||
161 | assert(renderer); | ||
162 | assert(renderer->shader); | ||
163 | imm_renderer_flush(renderer); | ||
164 | const mat4 modelview = spatial3_inverse_transform(&camera->spatial); | ||
165 | const mat4 mvp = mat4_mul(camera->projection, modelview); | ||
166 | gfx_set_mat4_uniform(renderer->shader, "ViewProjection", &mvp); | ||
167 | } | ||
168 | |||
169 | void gfx_imm_set_model_matrix(ImmRenderer* renderer, const mat4* model) { | ||
170 | assert(renderer); | ||
171 | assert(model); | ||
172 | imm_renderer_flush(renderer); | ||
173 | gfx_set_mat4_uniform(renderer->shader, "Model", model); | ||
174 | } | ||
175 | |||
176 | void gfx_imm_set_colour(ImmRenderer* renderer, vec4 colour) { | ||
177 | assert(renderer); | ||
178 | assert(renderer->shader); | ||
179 | imm_renderer_flush(renderer); | ||
180 | gfx_set_vec4_uniform(renderer->shader, "Colour", colour); | ||
181 | } | ||
diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c index b0bef33..5d88ae6 100644 --- a/gfx/src/renderer/renderer.c +++ b/gfx/src/renderer/renderer.c | |||
@@ -18,6 +18,7 @@ | |||
18 | 18 | ||
19 | #include <assert.h> | 19 | #include <assert.h> |
20 | 20 | ||
21 | // TODO: Move to a header like "constants.h". | ||
21 | static const int IRRADIANCE_MAP_WIDTH = 1024; | 22 | static const int IRRADIANCE_MAP_WIDTH = 1024; |
22 | static const int IRRADIANCE_MAP_HEIGHT = 1024; | 23 | static const int IRRADIANCE_MAP_HEIGHT = 1024; |
23 | static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128; | 24 | static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128; |
@@ -29,29 +30,31 @@ bool renderer_make(Renderer* renderer, RenderBackend* render_backend) { | |||
29 | assert(renderer); | 30 | assert(renderer); |
30 | assert(render_backend); | 31 | assert(render_backend); |
31 | 32 | ||
33 | renderer->render_backend = render_backend; | ||
34 | |||
32 | // TODO: Load the IBL stuff lazily. | 35 | // TODO: Load the IBL stuff lazily. |
33 | if (!(renderer->ibl = gfx_make_ibl(render_backend))) { | 36 | if (!(renderer->ibl = gfx_make_ibl(render_backend))) { |
34 | renderer_destroy(renderer, render_backend); | 37 | renderer_destroy(renderer); |
35 | return false; | 38 | return false; |
36 | } | 39 | } |
37 | 40 | ||
38 | if (!(renderer->brdf_integration_map = gfx_make_brdf_integration_map( | 41 | if (!(renderer->brdf_integration_map = gfx_make_brdf_integration_map( |
39 | renderer->ibl, render_backend, BRDF_INTEGRATION_MAP_WIDTH, | 42 | renderer->ibl, render_backend, BRDF_INTEGRATION_MAP_WIDTH, |
40 | BRDF_INTEGRATION_MAP_HEIGHT))) { | 43 | BRDF_INTEGRATION_MAP_HEIGHT))) { |
41 | renderer_destroy(renderer, render_backend); | 44 | renderer_destroy(renderer); |
42 | return false; | 45 | return false; |
43 | } | 46 | } |
44 | 47 | ||
45 | return true; | 48 | return true; |
46 | } | 49 | } |
47 | 50 | ||
48 | void renderer_destroy(Renderer* renderer, RenderBackend* render_backend) { | 51 | void renderer_destroy(Renderer* renderer) { |
49 | if (!renderer) { | 52 | if (!renderer) { |
50 | return; | 53 | return; |
51 | } | 54 | } |
52 | assert(render_backend); | 55 | assert(renderer->render_backend); |
53 | if (renderer->ibl) { | 56 | if (renderer->ibl) { |
54 | gfx_destroy_ibl(render_backend, &renderer->ibl); | 57 | gfx_destroy_ibl(renderer->render_backend, &renderer->ibl); |
55 | } | 58 | } |
56 | } | 59 | } |
57 | 60 | ||
@@ -168,6 +171,8 @@ static void draw_recursively( | |||
168 | const SceneObject* object = mem_get_object(node->object); | 171 | const SceneObject* object = mem_get_object(node->object); |
169 | assert(object); | 172 | assert(object); |
170 | 173 | ||
174 | // TODO: Here we would frustum-cull the object. | ||
175 | |||
171 | // TODO: Avoid computing matrices like Modelview or MVP if the shader does | 176 | // TODO: Avoid computing matrices like Modelview or MVP if the shader does |
172 | // not use them. | 177 | // not use them. |
173 | const mat4 model_matrix = mat4_mul(node_transform, object->transform); | 178 | const mat4 model_matrix = mat4_mul(node_transform, object->transform); |
@@ -189,6 +194,11 @@ static void draw_recursively( | |||
189 | } | 194 | } |
190 | assert(mesh->geometry); | 195 | assert(mesh->geometry); |
191 | assert(mesh->material); | 196 | assert(mesh->material); |
197 | |||
198 | // TODO: Here we would frustum-cull the mesh. The AABB would have to be | ||
199 | // transformed by the model matrix. Rotation would make the AABB | ||
200 | // relatively large, but still, the culling would be conservative. | ||
201 | |||
192 | // Apply common shader uniforms not captured by materials. | 202 | // Apply common shader uniforms not captured by materials. |
193 | ShaderProgram* shader = mesh->shader; | 203 | ShaderProgram* shader = mesh->shader; |
194 | gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix); | 204 | gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix); |
@@ -240,14 +250,12 @@ static void draw_recursively( | |||
240 | } | 250 | } |
241 | 251 | ||
242 | void gfx_render_scene( | 252 | void gfx_render_scene( |
243 | Renderer* renderer, RenderBackend* render_backend, const Scene* scene, | 253 | Renderer* renderer, const Scene* scene, const SceneCamera* camera) { |
244 | const SceneCamera* camera) { | ||
245 | assert(renderer); | 254 | assert(renderer); |
246 | assert(render_backend); | 255 | assert(scene); |
256 | assert(camera); | ||
247 | 257 | ||
248 | if (!scene) { | 258 | RenderBackend* render_backend = renderer->render_backend; |
249 | return; | ||
250 | } | ||
251 | 259 | ||
252 | const mat4 projection = camera ? camera->camera.projection : mat4_id(); | 260 | const mat4 projection = camera ? camera->camera.projection : mat4_id(); |
253 | const mat4 camera_rotation = | 261 | const mat4 camera_rotation = |
diff --git a/gfx/src/renderer/renderer_impl.h b/gfx/src/renderer/renderer_impl.h index ce8ebe7..833025a 100644 --- a/gfx/src/renderer/renderer_impl.h +++ b/gfx/src/renderer/renderer_impl.h | |||
@@ -1,18 +1,52 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include <gfx/renderer.h> | 3 | #include <gfx/renderer.h> |
4 | 4 | #include <gfx/sizes.h> | |
5 | #include <gfx/util/ibl.h> | 5 | #include <gfx/util/ibl.h> |
6 | 6 | ||
7 | #include <math/vec3.h> | ||
8 | #include <math/vec4.h> | ||
9 | |||
7 | #include <stdbool.h> | 10 | #include <stdbool.h> |
11 | #include <stddef.h> | ||
12 | |||
13 | // Currently, the immediate mode renderer can only draw up to a maximum number | ||
14 | // of primitives per frame. It does not adjust this number dynamically. Keeps | ||
15 | // things simple while the extra complexity is not needed. | ||
16 | |||
17 | typedef struct Buffer Buffer; | ||
18 | typedef struct Geometry Geometry; | ||
19 | typedef struct ShaderProgram ShaderProgram; | ||
8 | 20 | ||
9 | typedef struct Renderer { | 21 | typedef struct Renderer { |
10 | IBL* ibl; | 22 | RenderBackend* render_backend; |
11 | Texture* brdf_integration_map; | 23 | IBL* ibl; |
24 | Texture* brdf_integration_map; | ||
12 | } Renderer; | 25 | } Renderer; |
13 | 26 | ||
27 | typedef struct ImmRenderer { | ||
28 | RenderBackend* render_backend; | ||
29 | ShaderProgram* shader; | ||
30 | Geometry* triangles; | ||
31 | size_t num_triangle_verts; // Number of triangle verts this frame. | ||
32 | // TODO: wireframe rendering. | ||
33 | struct { | ||
34 | bool wireframe : 1; | ||
35 | } flags; | ||
36 | vec3 triangle_verts[IMM_MAX_NUM_TRIANGLES * 3]; | ||
37 | } ImmRenderer; | ||
38 | |||
14 | /// Create a new renderer. | 39 | /// Create a new renderer. |
15 | bool renderer_make(Renderer*, RenderBackend*); | 40 | bool renderer_make(Renderer*, RenderBackend*); |
16 | 41 | ||
17 | /// Destroy the renderer. | 42 | /// Destroy the renderer. |
18 | void renderer_destroy(Renderer*, RenderBackend*); | 43 | void renderer_destroy(Renderer*); |
44 | |||
45 | /// Create a new immediate mode renderer. | ||
46 | bool imm_renderer_make(ImmRenderer*, RenderBackend*); | ||
47 | |||
48 | /// Destroy the immediate mode renderer. | ||
49 | void imm_renderer_destroy(ImmRenderer*); | ||
50 | |||
51 | /// Flush draw commands. | ||
52 | void imm_renderer_flush(ImmRenderer*); | ||
diff --git a/gfx/src/scene/object.c b/gfx/src/scene/object.c index 47d2f25..64bb5a6 100644 --- a/gfx/src/scene/object.c +++ b/gfx/src/scene/object.c | |||
@@ -1,5 +1,8 @@ | |||
1 | #include "object_impl.h" | 1 | #include "object_impl.h" |
2 | 2 | ||
3 | #include <gfx/render_backend.h> | ||
4 | |||
5 | #include "mesh_impl.h" | ||
3 | #include "node_impl.h" | 6 | #include "node_impl.h" |
4 | #include "scene_memory.h" | 7 | #include "scene_memory.h" |
5 | 8 | ||
@@ -75,3 +78,32 @@ void gfx_set_object_skeleton(SceneObject* object, const Skeleton* skeleton) { | |||
75 | assert(skeleton); | 78 | assert(skeleton); |
76 | object->skeleton = mem_get_skeleton_index(skeleton); | 79 | object->skeleton = mem_get_skeleton_index(skeleton); |
77 | } | 80 | } |
81 | |||
82 | // TODO: Could compute just once if we changed the Object API to require an | ||
83 | // object descriptor up front instead of allowing the client to add meshes | ||
84 | // and skeletons dynamically. | ||
85 | aabb3 gfx_calc_object_aabb(const SceneObject* object) { | ||
86 | assert(object); | ||
87 | |||
88 | bool first = true; | ||
89 | aabb3 box; | ||
90 | |||
91 | mesh_link_idx ml = object->mesh_link; | ||
92 | while (ml.val) { | ||
93 | const MeshLink* mesh_link = mem_get_mesh_link(ml); | ||
94 | const mesh_idx mi = mesh_link->mesh; | ||
95 | if (mi.val) { | ||
96 | const Mesh* mesh = mem_get_mesh(mi); | ||
97 | const aabb3 mesh_box = gfx_get_geometry_aabb(mesh->geometry); | ||
98 | if (first) { | ||
99 | box = mesh_box; | ||
100 | first = false; | ||
101 | } else { | ||
102 | box = aabb3_sum(box, mesh_box); | ||
103 | } | ||
104 | } | ||
105 | ml = mesh_link->next; | ||
106 | } | ||
107 | |||
108 | return box; | ||
109 | } | ||
diff --git a/gfx/src/scene/scene_memory.c b/gfx/src/scene/scene_memory.c index 62e9401..217c719 100644 --- a/gfx/src/scene/scene_memory.c +++ b/gfx/src/scene/scene_memory.c | |||
@@ -182,6 +182,9 @@ void mem_free_skeleton(Skeleton** skeleton) { | |||
182 | } | 182 | } |
183 | 183 | ||
184 | // Query by index. | 184 | // Query by index. |
185 | // | ||
186 | // TODO: Check for 0 index and return nullptr? Otherwise this can accidentally | ||
187 | // return a pointer to the dummy objects. | ||
185 | Anima* mem_get_anima(anima_idx index) { | 188 | Anima* mem_get_anima(anima_idx index) { |
186 | return mempool_get_block(&mem.animas, index.val); | 189 | return mempool_get_block(&mem.animas, index.val); |
187 | } | 190 | } |
diff --git a/gfx/src/util/scene.c b/gfx/src/util/scene.c index e8dd6b1..dc55d5a 100644 --- a/gfx/src/util/scene.c +++ b/gfx/src/util/scene.c | |||
@@ -373,23 +373,31 @@ static float read_float(const void* data, const cgltf_accessor* accessor) { | |||
373 | #define ACCESSOR_FOREACH_VEC(dimensions, accessor, body) \ | 373 | #define ACCESSOR_FOREACH_VEC(dimensions, accessor, body) \ |
374 | { \ | 374 | { \ |
375 | assert((1 <= dimensions) && (dimensions <= 4)); \ | 375 | assert((1 <= dimensions) && (dimensions <= 4)); \ |
376 | assert(!(dimensions == 1) || (accessor->type == cgltf_type_scalar)); \ | 376 | assert( \ |
377 | assert(!(dimensions == 2) || (accessor->type == cgltf_type_vec2)); \ | 377 | ((dimensions == 1) && (accessor->type == cgltf_type_scalar)) || \ |
378 | assert(!(dimensions == 3) || (accessor->type == cgltf_type_vec3)); \ | 378 | ((dimensions == 2) && (accessor->type == cgltf_type_vec2)) || \ |
379 | assert(!(dimensions == 4) || (accessor->type == cgltf_type_vec4)); \ | 379 | ((dimensions == 3) && (accessor->type == cgltf_type_vec3)) || \ |
380 | ((dimensions == 4) && (accessor->type == cgltf_type_vec4))); \ | ||
380 | const cgltf_buffer_view* view = accessor->buffer_view; \ | 381 | const cgltf_buffer_view* view = accessor->buffer_view; \ |
381 | const cgltf_buffer* buffer = view->buffer; \ | 382 | const cgltf_buffer* buffer = view->buffer; \ |
382 | const cgltf_size offset = accessor->offset + view->offset; \ | 383 | const cgltf_size offset = accessor->offset + view->offset; \ |
384 | const uint8_t* bytes = (const uint8_t*)buffer->data + offset; \ | ||
385 | /* Component size in bytes. */ \ | ||
383 | const cgltf_size comp_size = get_component_size(accessor->component_type); \ | 386 | const cgltf_size comp_size = get_component_size(accessor->component_type); \ |
384 | const uint8_t* bytes = (const uint8_t*)buffer->data + offset; \ | 387 | /* Element size in bytes. */ \ |
388 | const cgltf_size elem_size = dimensions * comp_size; \ | ||
389 | /* Stride in bytes. If the view stride is 0, then the elements are tightly \ | ||
390 | * packed. */ \ | ||
391 | const cgltf_size stride = view->stride != 0 ? view->stride : elem_size; \ | ||
392 | /* There isn't an accessor stride in the spec, but cgltf still specifies \ | ||
393 | * one. */ \ | ||
394 | assert(accessor->stride == elem_size); \ | ||
395 | /* Accessor data must fit inside the buffer. */ \ | ||
385 | assert( \ | 396 | assert( \ |
386 | (offset + accessor->count * dimensions * comp_size) < buffer->size); \ | 397 | (offset + (accessor->count * elem_size) + \ |
387 | /* From the spec: */ \ | 398 | ((accessor->count - 1) * view->stride)) <= buffer->size); \ |
388 | /* "Buffer views with other types of data MUST NOT not define */ \ | 399 | /* Accessor data must fit inside the view. */ \ |
389 | /* byteStride (unless such layout is explicitly enabled by an */ \ | 400 | assert(accessor->count * accessor->stride <= view->size); \ |
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; \ | 401 | cgltf_float x = 0, y = 0, z = 0, w = 0; \ |
394 | /* Silence unused variable warnings. */ \ | 402 | /* Silence unused variable warnings. */ \ |
395 | (void)y; \ | 403 | (void)y; \ |
@@ -401,58 +409,63 @@ static float read_float(const void* data, const cgltf_accessor* accessor) { | |||
401 | types, we take the performance hit and perform checks and conversions \ | 409 | types, we take the performance hit and perform checks and conversions \ |
402 | inside the loop for simplicity. */ \ | 410 | inside the loop for simplicity. */ \ |
403 | if (accessor->component_type == cgltf_component_type_r_32f) { \ | 411 | if (accessor->component_type == cgltf_component_type_r_32f) { \ |
404 | const cgltf_float* floats = (const cgltf_float*)bytes; \ | ||
405 | switch (dimensions) { \ | 412 | switch (dimensions) { \ |
406 | case 1: \ | 413 | case 1: \ |
407 | assert(accessor->type == cgltf_type_scalar); \ | 414 | assert(accessor->type == cgltf_type_scalar); \ |
408 | for (cgltf_size i = 0; i < accessor->count; ++i) { \ | 415 | for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \ |
409 | x = *floats++; \ | 416 | const cgltf_float* floats = (const cgltf_float*)bytes; \ |
417 | x = *floats; \ | ||
410 | body; \ | 418 | body; \ |
411 | } \ | 419 | } \ |
412 | break; \ | 420 | break; \ |
413 | case 2: \ | 421 | case 2: \ |
414 | assert(accessor->type == cgltf_type_vec2); \ | 422 | assert(accessor->type == cgltf_type_vec2); \ |
415 | for (cgltf_size i = 0; i < accessor->count; ++i) { \ | 423 | for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \ |
416 | x = *floats++; \ | 424 | const cgltf_float* floats = (const cgltf_float*)bytes; \ |
417 | y = *floats++; \ | 425 | x = *floats++; \ |
426 | y = *floats; \ | ||
418 | body; \ | 427 | body; \ |
419 | } \ | 428 | } \ |
420 | break; \ | 429 | break; \ |
421 | case 3: \ | 430 | case 3: \ |
422 | assert(accessor->type == cgltf_type_vec3); \ | 431 | assert(accessor->type == cgltf_type_vec3); \ |
423 | for (cgltf_size i = 0; i < accessor->count; ++i) { \ | 432 | for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \ |
424 | x = *floats++; \ | 433 | const cgltf_float* floats = (const cgltf_float*)bytes; \ |
425 | y = *floats++; \ | 434 | x = *floats++; \ |
426 | z = *floats++; \ | 435 | y = *floats++; \ |
436 | z = *floats; \ | ||
427 | body; \ | 437 | body; \ |
428 | } \ | 438 | } \ |
429 | break; \ | 439 | break; \ |
430 | case 4: \ | 440 | case 4: \ |
431 | assert(accessor->type == cgltf_type_vec4); \ | 441 | assert(accessor->type == cgltf_type_vec4); \ |
432 | for (cgltf_size i = 0; i < accessor->count; ++i) { \ | 442 | for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \ |
433 | x = *floats++; \ | 443 | const cgltf_float* floats = (const cgltf_float*)bytes; \ |
434 | y = *floats++; \ | 444 | x = *floats++; \ |
435 | z = *floats++; \ | 445 | y = *floats++; \ |
436 | w = *floats++; \ | 446 | z = *floats++; \ |
447 | w = *floats; \ | ||
437 | body; \ | 448 | body; \ |
438 | } \ | 449 | } \ |
439 | break; \ | 450 | break; \ |
440 | } \ | 451 | } \ |
441 | } else { \ | 452 | } else { \ |
442 | for (cgltf_size i = 0; i < accessor->count; ++i) { \ | 453 | for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \ |
443 | x = read_float(bytes, accessor); \ | 454 | const uint8_t* component = bytes; \ |
444 | bytes += accessor->stride; \ | 455 | \ |
456 | x = read_float(component, accessor); \ | ||
457 | component += comp_size; \ | ||
445 | if (dimensions > 1) { \ | 458 | if (dimensions > 1) { \ |
446 | y = read_float(bytes, accessor); \ | 459 | y = read_float(component, accessor); \ |
447 | bytes += accessor->stride; \ | 460 | component += comp_size; \ |
448 | } \ | 461 | } \ |
449 | if (dimensions > 2) { \ | 462 | if (dimensions > 2) { \ |
450 | z = read_float(bytes, accessor); \ | 463 | z = read_float(component, accessor); \ |
451 | bytes += accessor->stride; \ | 464 | component += comp_size; \ |
452 | } \ | 465 | } \ |
453 | if (dimensions > 3) { \ | 466 | if (dimensions > 3) { \ |
454 | w = read_float(bytes, accessor); \ | 467 | w = read_float(component, accessor); \ |
455 | bytes += accessor->stride; \ | 468 | component += comp_size; \ |
456 | } \ | 469 | } \ |
457 | body; \ | 470 | body; \ |
458 | } \ | 471 | } \ |
@@ -538,10 +551,10 @@ static bool load_buffers( | |||
538 | assert(buffer->data); | 551 | assert(buffer->data); |
539 | buffers[i] = gfx_make_buffer( | 552 | buffers[i] = gfx_make_buffer( |
540 | render_backend, &(BufferDesc){ | 553 | render_backend, &(BufferDesc){ |
541 | .usage = BufferStatic, | 554 | .usage = BufferStatic, |
542 | .type = BufferUntyped, | 555 | .type = BufferUntyped, |
543 | .data = buffer->data, | 556 | .data.data = buffer->data, |
544 | .count = buffer->size}); | 557 | .data.count = buffer->size}); |
545 | if (!buffers[i]) { | 558 | if (!buffers[i]) { |
546 | return false; | 559 | return false; |
547 | } | 560 | } |
@@ -564,10 +577,10 @@ static bool load_tangent_buffers( | |||
564 | assert(buffer->data); | 577 | assert(buffer->data); |
565 | tangent_buffers[i] = gfx_make_buffer( | 578 | tangent_buffers[i] = gfx_make_buffer( |
566 | render_backend, &(BufferDesc){ | 579 | render_backend, &(BufferDesc){ |
567 | .usage = BufferStatic, | 580 | .usage = BufferStatic, |
568 | .type = BufferUntyped, | 581 | .type = BufferUntyped, |
569 | .data = buffer->data, | 582 | .data.data = buffer->data, |
570 | .count = buffer->size_bytes}); | 583 | .data.count = buffer->size_bytes}); |
571 | if (!tangent_buffers[i]) { | 584 | if (!tangent_buffers[i]) { |
572 | return false; | 585 | return false; |
573 | } | 586 | } |
@@ -843,6 +856,26 @@ static Material* make_default_material() { | |||
843 | return gfx_make_material(&desc); | 856 | return gfx_make_material(&desc); |
844 | } | 857 | } |
845 | 858 | ||
859 | /// Compute the bounding box of the vertices pointed to by the accessor. | ||
860 | /// 'dim' is the dimension of the vertices (2D or 3D). | ||
861 | aabb3 compute_aabb(const cgltf_accessor* accessor, int dim) { | ||
862 | aabb3 box = {0}; | ||
863 | if (accessor->has_min && accessor->has_max) { | ||
864 | box = aabb3_make( | ||
865 | vec3_from_array(accessor->min), vec3_from_array(accessor->max)); | ||
866 | } else { | ||
867 | ACCESSOR_FOREACH_VEC(dim, accessor, { | ||
868 | const vec3 p = vec3_make(x, y, z); | ||
869 | if (i == 0) { | ||
870 | box = aabb3_make(p, p); | ||
871 | } else { | ||
872 | box = aabb3_add(box, p); | ||
873 | } | ||
874 | }); | ||
875 | } | ||
876 | return box; | ||
877 | } | ||
878 | |||
846 | /// Load all meshes from the glTF scene. | 879 | /// Load all meshes from the glTF scene. |
847 | static bool load_meshes( | 880 | static bool load_meshes( |
848 | const cgltf_data* data, Gfx* gfx, Buffer** buffers, | 881 | const cgltf_data* data, Gfx* gfx, Buffer** buffers, |
@@ -910,7 +943,8 @@ static bool load_meshes( | |||
910 | } | 943 | } |
911 | 944 | ||
912 | GeometryDesc geometry_desc = { | 945 | GeometryDesc geometry_desc = { |
913 | .type = from_gltf_primitive_type(prim->type)}; | 946 | .type = from_gltf_primitive_type(prim->type), |
947 | .buffer_usage = BufferStatic}; | ||
914 | 948 | ||
915 | // Vertex indices. | 949 | // Vertex indices. |
916 | if (prim->indices) { | 950 | if (prim->indices) { |
@@ -969,11 +1003,13 @@ static bool load_meshes( | |||
969 | switch (accessor->type) { | 1003 | switch (accessor->type) { |
970 | case cgltf_type_vec2: | 1004 | case cgltf_type_vec2: |
971 | assert(geometry_desc.positions3d.buffer == 0); | 1005 | assert(geometry_desc.positions3d.buffer == 0); |
972 | buffer_view_2d = &geometry_desc.positions2d; | 1006 | buffer_view_2d = &geometry_desc.positions2d; |
1007 | geometry_desc.aabb = compute_aabb(accessor, 2); | ||
973 | break; | 1008 | break; |
974 | case cgltf_type_vec3: | 1009 | case cgltf_type_vec3: |
975 | assert(geometry_desc.positions2d.buffer == 0); | 1010 | assert(geometry_desc.positions2d.buffer == 0); |
976 | buffer_view_3d = &geometry_desc.positions3d; | 1011 | buffer_view_3d = &geometry_desc.positions3d; |
1012 | geometry_desc.aabb = compute_aabb(accessor, 3); | ||
977 | break; | 1013 | break; |
978 | default: | 1014 | default: |
979 | LOGE( | 1015 | LOGE( |
@@ -1478,6 +1514,8 @@ static SceneNode* load_scene( | |||
1478 | scene_nodes[i] = gfx_make_node(); | 1514 | scene_nodes[i] = gfx_make_node(); |
1479 | } | 1515 | } |
1480 | 1516 | ||
1517 | // TODO: If the scene does not have animations, then a top-level LogicalNode | ||
1518 | // would make more sense than an AnimaNode. | ||
1481 | anima_node = gfx_make_node(); | 1519 | anima_node = gfx_make_node(); |
1482 | load_skins(data, buffers, scene_nodes, anima_desc->skeletons); | 1520 | load_skins(data, buffers, scene_nodes, anima_desc->skeletons); |
1483 | load_animations(data, scene_nodes, anima_desc->animations); | 1521 | load_animations(data, scene_nodes, anima_desc->animations); |
diff --git a/gfx/src/util/shader.c b/gfx/src/util/shader.c index fdfb1d3..ed81f79 100644 --- a/gfx/src/util/shader.c +++ b/gfx/src/util/shader.c | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <shaders/cubemap_filtering.vert.h> | 7 | #include <shaders/cubemap_filtering.vert.h> |
8 | #include <shaders/debug3d.frag.h> | 8 | #include <shaders/debug3d.frag.h> |
9 | #include <shaders/debug3d.vert.h> | 9 | #include <shaders/debug3d.vert.h> |
10 | #include <shaders/immediate_mode.frag.h> | ||
11 | #include <shaders/immediate_mode.vert.h> | ||
10 | #include <shaders/irradiance_map.frag.h> | 12 | #include <shaders/irradiance_map.frag.h> |
11 | #include <shaders/prefiltered_environment_map.frag.h> | 13 | #include <shaders/prefiltered_environment_map.frag.h> |
12 | #include <shaders/quad.vert.h> | 14 | #include <shaders/quad.vert.h> |
@@ -94,6 +96,11 @@ ShaderProgram* gfx_make_cook_torrance_shader_perm( | |||
94 | num_defines); | 96 | num_defines); |
95 | } | 97 | } |
96 | 98 | ||
99 | ShaderProgram* gfx_make_immediate_mode_shader(RenderBackend* render_backend) { | ||
100 | return make_shader_program( | ||
101 | render_backend, immediate_mode_vert, immediate_mode_frag, 0, 0); | ||
102 | } | ||
103 | |||
97 | ShaderProgram* gfx_make_irradiance_map_shader(RenderBackend* render_backend) { | 104 | ShaderProgram* gfx_make_irradiance_map_shader(RenderBackend* render_backend) { |
98 | return make_shader_program( | 105 | return make_shader_program( |
99 | render_backend, cubemap_filtering_vert, irradiance_map_frag, 0, 0); | 106 | render_backend, cubemap_filtering_vert, irradiance_map_frag, 0, 0); |
diff --git a/gltfview/src/game.c b/gltfview/src/game.c index f822b08..662272c 100644 --- a/gltfview/src/game.c +++ b/gltfview/src/game.c | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | #include <gfx/error.h> | 3 | #include <gfx/error.h> |
4 | #include <gfx/render_backend.h> | 4 | #include <gfx/render_backend.h> |
5 | #include <gfx/scene/camera.h> | ||
5 | #include <gfx/scene/light.h> | 6 | #include <gfx/scene/light.h> |
6 | #include <gfx/scene/material.h> | 7 | #include <gfx/scene/material.h> |
7 | #include <gfx/scene/mesh.h> | 8 | #include <gfx/scene/mesh.h> |
@@ -32,8 +33,10 @@ static const char* FLIGHT_HELMET = | |||
32 | "/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/FlightHelmet.gltf"; | 33 | "/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/FlightHelmet.gltf"; |
33 | static const char* DAMAGED_HELMET = | 34 | static const char* DAMAGED_HELMET = |
34 | "/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf"; | 35 | "/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf"; |
36 | static const char* GIRL = | ||
37 | "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf"; | ||
35 | 38 | ||
36 | #define DEFAULT_SCENE_FILE DAMAGED_HELMET | 39 | #define DEFAULT_SCENE_FILE GIRL |
37 | 40 | ||
38 | static const char* CLOUDS1_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp"; | 41 | static const char* CLOUDS1_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp"; |
39 | 42 | ||
@@ -189,6 +192,26 @@ static bool load_texture_debugger_scene(Game* game) { | |||
189 | return true; | 192 | return true; |
190 | } | 193 | } |
191 | 194 | ||
195 | /// Render the bounding boxes of all scene objects. | ||
196 | static void render_bounding_boxes(ImmRenderer* imm, const SceneNode* node) { | ||
197 | if (gfx_get_node_type(node) == ObjectNode) { | ||
198 | // TODO: Look at the scene log. The JointNodes are detached from the | ||
199 | // ObjectNodes. This is why the boxes are not being transformed as expected | ||
200 | // here. Anima needs to animate boxes? Use OOBB in addition to AABB? | ||
201 | const mat4 model = gfx_get_node_global_transform(node); | ||
202 | const SceneObject* obj = gfx_get_node_object(node); | ||
203 | const aabb3 box = gfx_calc_object_aabb(obj); | ||
204 | gfx_imm_set_model_matrix(imm, &model); | ||
205 | gfx_imm_draw_aabb(imm, box); | ||
206 | } | ||
207 | |||
208 | // Render children's boxes. | ||
209 | for (NodeIter it = gfx_get_node_child(node); it; | ||
210 | it = gfx_get_next_child(it)) { | ||
211 | render_bounding_boxes(imm, gfx_get_iter_node(it)); | ||
212 | } | ||
213 | } | ||
214 | |||
192 | bool game_new(Game* game, int argc, const char** argv) { | 215 | bool game_new(Game* game, int argc, const char** argv) { |
193 | // TODO: getopt() to implement proper argument parsing. | 216 | // TODO: getopt() to implement proper argument parsing. |
194 | const char* scene_filepath = argc > 1 ? argv[1] : DEFAULT_SCENE_FILE; | 217 | const char* scene_filepath = argc > 1 ? argv[1] : DEFAULT_SCENE_FILE; |
@@ -239,6 +262,8 @@ void game_end(Game* game) { gfx_destroy(&game->gfx); } | |||
239 | void game_update(Game* game, double t, double dt) { | 262 | void game_update(Game* game, double t, double dt) { |
240 | // LOGD("Tick"); | 263 | // LOGD("Tick"); |
241 | 264 | ||
265 | // TODO: Animation should be handled by Gfx instead. Descend through the scene | ||
266 | // looking for animas and animate them. gfx_animate(t). | ||
242 | Anima* anima = gfx_get_node_anima(game->root_node); | 267 | Anima* anima = gfx_get_node_anima(game->root_node); |
243 | gfx_update_animation(anima, t); | 268 | gfx_update_animation(anima, t); |
244 | 269 | ||
@@ -263,8 +288,18 @@ void game_update(Game* game, double t, double dt) { | |||
263 | } | 288 | } |
264 | 289 | ||
265 | void game_render(const Game* game) { | 290 | void game_render(const Game* game) { |
266 | gfx_render_scene( | 291 | gfx_render_scene(game->renderer, game->scene, game->camera); |
267 | game->renderer, game->render_backend, game->scene, game->camera); | 292 | |
293 | ImmRenderer* imm = gfx_get_imm_renderer(game->gfx); | ||
294 | assert(imm); | ||
295 | gfx_imm_start(imm); | ||
296 | gfx_imm_set_camera(imm, gfx_get_camera_camera(game->camera)); | ||
297 | gfx_imm_set_colour(imm, vec4_make(0.2, 0.2, 1.0, 0.3)); | ||
298 | // DEBUG | ||
299 | // const aabb3 box = aabb3_make(vec3_make(0, 0, 0), vec3_make(1, 1, 1)); | ||
300 | // gfx_imm_draw_aabb(imm, box); | ||
301 | render_bounding_boxes(imm, gfx_get_scene_root(game->scene)); | ||
302 | gfx_imm_end(imm); | ||
268 | } | 303 | } |
269 | 304 | ||
270 | void game_set_viewport(Game* game, int width, int height) { | 305 | void game_set_viewport(Game* game, int width, int height) { |