summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2023-05-26 19:12:59 -0700
committer3gg <3gg@shellblade.net>2023-05-26 19:12:59 -0700
commit29af7cf14cd55ece0a8c8b1305f3b71ab57bebd6 (patch)
treee175572b0afd1bcd32bea86696dbe320bbb1ba81
parent49d327315f5be8b72b7c100ec95ecaa5ace8126d (diff)
Add bounding boxes and an immediate mode renderer. Pending proper bounding boxes for animated meshes.
-rw-r--r--gfx/CMakeLists.txt3
-rw-r--r--gfx/include/gfx/gfx.h10
-rw-r--r--gfx/include/gfx/render_backend.h84
-rw-r--r--gfx/include/gfx/renderer.h53
-rw-r--r--gfx/include/gfx/scene/object.h7
-rw-r--r--gfx/include/gfx/sizes.h8
-rw-r--r--gfx/include/gfx/util/shader.h3
-rw-r--r--gfx/shaders/immediate_mode.frag10
-rw-r--r--gfx/shaders/immediate_mode.vert11
-rw-r--r--gfx/src/gfx.c17
-rw-r--r--gfx/src/render/buffer.c32
-rw-r--r--gfx/src/render/buffer.h10
-rw-r--r--gfx/src/render/geometry.c284
-rw-r--r--gfx/src/render/geometry.h8
-rw-r--r--gfx/src/render/render_backend.c35
-rw-r--r--gfx/src/renderer/imm_renderer.c181
-rw-r--r--gfx/src/renderer/renderer.c30
-rw-r--r--gfx/src/renderer/renderer_impl.h42
-rw-r--r--gfx/src/scene/object.c32
-rw-r--r--gfx/src/scene/scene_memory.c3
-rw-r--r--gfx/src/util/scene.c132
-rw-r--r--gfx/src/util/shader.c7
-rw-r--r--gltfview/src/game.c41
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
3typedef struct ImmRenderer ImmRenderer;
3typedef struct RenderBackend RenderBackend; 4typedef struct RenderBackend RenderBackend;
4typedef struct Renderer Renderer; 5typedef struct Renderer Renderer;
5typedef struct Scene Scene; 6typedef struct Scene Scene;
6typedef struct SceneCamera SceneCamera; 7typedef struct SceneCamera SceneCamera;
7 8
8typedef struct Gfx Gfx; 9typedef struct Gfx Gfx;
9 10
@@ -19,6 +20,9 @@ RenderBackend* gfx_get_render_backend(Gfx*);
19/// Get the renderer. 20/// Get the renderer.
20Renderer* gfx_get_renderer(Gfx*); 21Renderer* gfx_get_renderer(Gfx*);
21 22
23/// Get the immediate mode renderer.
24ImmRenderer* gfx_get_imm_renderer(Gfx*);
25
22/// Create a new scene. 26/// Create a new scene.
23Scene* gfx_make_scene(Gfx*); 27Scene* 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.
57typedef 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.
66typedef struct BufferDesc { 81typedef 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.
121typedef struct GeometryDesc { 137typedef 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.
299void gfx_get_viewport(RenderBackend*, int* width, int* height); 317void gfx_get_viewport(RenderBackend*, int* width, int* height);
300 318
319/// Set blending state.
320void gfx_set_blending(RenderBackend*, bool enable);
321
322/// Set depth mask.
323void gfx_set_depth_mask(RenderBackend*, bool enable);
324
325/// Set cull mode.
326void gfx_set_culling(RenderBackend*, bool enable);
327
328/// Set polygon offset.
329void 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.
309void gfx_destroy_buffer(RenderBackend*, Buffer**); 339void gfx_destroy_buffer(RenderBackend*, Buffer**);
310 340
341/// Update the buffer's data.
342void 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.
333void gfx_update_geometry(Geometry*, const GeometryDesc*); 370void gfx_update_geometry(Geometry*, const GeometryDesc*);
334 371
335/// Render the geometry. 372/// Render the geometry.
336void gfx_render_geometry(const Geometry*); 373void gfx_render_geometry(const Geometry*);
337 374
375/// Return the geometry's bounding box.
376aabb3 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
3typedef struct RenderBackend RenderBackend; 9typedef struct RenderBackend RenderBackend;
4typedef struct Scene Scene; 10typedef struct Scene Scene;
5typedef struct SceneCamera SceneCamera; 11typedef struct SceneCamera SceneCamera;
12
13typedef struct ImmRenderer ImmRenderer;
14typedef struct Renderer Renderer;
6 15
7typedef struct Renderer Renderer; 16// -----------------------------------------------------------------------------
17// Main Renderer.
18// -----------------------------------------------------------------------------
8 19
9/// Render the scene. 20/// Render the scene.
10void gfx_render_scene(Renderer*, RenderBackend*, const Scene*, 21void 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.
30void 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.
36void gfx_imm_end(ImmRenderer*);
37
38/// Draw a set of triangles.
39void gfx_imm_draw_triangles(ImmRenderer*, const vec3[], size_t num_triangles);
40
41/// Draw a triangle.
42void gfx_imm_draw_triangle(ImmRenderer*, const vec3[3]);
43
44/// Draw a bounding box.
45void gfx_imm_draw_aabb(ImmRenderer*, aabb3);
46
47/// Set the camera.
48void gfx_imm_set_camera(ImmRenderer*, const Camera*);
49
50/// Set the model matrix.
51void gfx_imm_set_model_matrix(ImmRenderer*, const mat4*);
52
53/// Set the render colour.
54void 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
5typedef struct Mesh Mesh; 7typedef struct Mesh Mesh;
6typedef struct SceneNode SceneNode; 8typedef struct SceneNode SceneNode;
7typedef struct Skeleton Skeleton; 9typedef 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.
30void gfx_set_object_skeleton(SceneObject*, const Skeleton*); 32void 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.
37aabb3 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.
22ShaderProgram* gfx_make_debug3d_shader(RenderBackend*); 22ShaderProgram* gfx_make_debug3d_shader(RenderBackend*);
23 23
24/// Create a shader for drawing in immediate mode.
25ShaderProgram* 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.
25ShaderProgram* gfx_make_irradiance_map_shader(RenderBackend*); 28ShaderProgram* 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 @@
1precision highp float;
2
3uniform vec4 Colour;
4
5out vec4 FragColour;
6
7void 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 @@
1precision highp float;
2
3uniform mat4 Model;
4uniform mat4 ViewProjection;
5
6layout (location = 0) in vec3 vPosition;
7
8void 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
15typedef struct Gfx { 15typedef 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
21Gfx* gfx_init() { 22Gfx* 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
70ImmRenderer* gfx_get_imm_renderer(Gfx* gfx) {
71 assert(gfx);
72 return &gfx->imm_renderer;
73}
74
62Scene* gfx_make_scene(Gfx* gfx) { 75Scene* 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
9static size_t get_buffer_size_bytes(const BufferDesc* desc) { 9static 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
26static GLenum get_buffer_usage(BufferUsage usage) { 31static GLenum get_buffer_usage(BufferUsage usage) {
@@ -36,11 +41,13 @@ static GLenum get_buffer_usage(BufferUsage usage) {
36 41
37bool gfx_init_buffer(Buffer* buffer, const BufferDesc* desc) { 42bool 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
64void 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
10typedef struct BufferDesc BufferDesc;
11
12typedef struct Buffer { 12typedef 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
11static GLenum primitive_type_to_gl(PrimitiveType type) { 15static 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) { \
29static 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.
44static bool configure_buffer( 43static 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
187static 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
153bool gfx_init_geometry( 216bool 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
249cleanup:
250 gfx_del_geometry(geometry);
251 return 0;
207} 252}
208 253
209void gfx_del_geometry(Geometry* geometry) { 254void gfx_del_geometry(Geometry* geometry) {
@@ -217,6 +262,7 @@ void gfx_del_geometry(Geometry* geometry) {
217void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { 262void 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
244void gfx_render_geometry(const Geometry* geometry) { 298void 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
322aabb3 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.
15typedef struct Geometry { 15typedef 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
99void 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
109void gfx_set_depth_mask(RenderBackend* render_backend, bool enable) {
110 assert(render_backend);
111 glDepthMask(enable ? GL_TRUE : GL_FALSE);
112}
113
114void 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
123void 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
13bool 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
42cleanup:
43 imm_renderer_destroy(renderer);
44 return false;
45}
46
47void 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
61void 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
81void 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
97void 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
109void 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
124void gfx_imm_draw_triangle(ImmRenderer* renderer, const vec3 verts[3]) {
125 gfx_imm_draw_triangles(renderer, verts, 1);
126}
127
128void 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
160void 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
169void 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
176void 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".
21static const int IRRADIANCE_MAP_WIDTH = 1024; 22static const int IRRADIANCE_MAP_WIDTH = 1024;
22static const int IRRADIANCE_MAP_HEIGHT = 1024; 23static const int IRRADIANCE_MAP_HEIGHT = 1024;
23static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128; 24static 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
48void renderer_destroy(Renderer* renderer, RenderBackend* render_backend) { 51void 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
242void gfx_render_scene( 252void 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
17typedef struct Buffer Buffer;
18typedef struct Geometry Geometry;
19typedef struct ShaderProgram ShaderProgram;
8 20
9typedef struct Renderer { 21typedef 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
27typedef 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.
15bool renderer_make(Renderer*, RenderBackend*); 40bool renderer_make(Renderer*, RenderBackend*);
16 41
17/// Destroy the renderer. 42/// Destroy the renderer.
18void renderer_destroy(Renderer*, RenderBackend*); 43void renderer_destroy(Renderer*);
44
45/// Create a new immediate mode renderer.
46bool imm_renderer_make(ImmRenderer*, RenderBackend*);
47
48/// Destroy the immediate mode renderer.
49void imm_renderer_destroy(ImmRenderer*);
50
51/// Flush draw commands.
52void 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.
85aabb3 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.
185Anima* mem_get_anima(anima_idx index) { 188Anima* 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).
861aabb3 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.
847static bool load_meshes( 880static 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
99ShaderProgram* 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
97ShaderProgram* gfx_make_irradiance_map_shader(RenderBackend* render_backend) { 104ShaderProgram* 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";
33static const char* DAMAGED_HELMET = 34static 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";
36static 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
38static const char* CLOUDS1_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp"; 41static 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.
196static 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
192bool game_new(Game* game, int argc, const char** argv) { 215bool 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); }
239void game_update(Game* game, double t, double dt) { 262void 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
265void game_render(const Game* game) { 290void 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
270void game_set_viewport(Game* game, int width, int height) { 305void game_set_viewport(Game* game, int width, int height) {