diff options
| author | 3gg <3gg@shellblade.net> | 2023-05-26 19:12:59 -0700 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2023-05-26 19:12:59 -0700 |
| commit | 29af7cf14cd55ece0a8c8b1305f3b71ab57bebd6 (patch) | |
| tree | e175572b0afd1bcd32bea86696dbe320bbb1ba81 | |
| parent | 49d327315f5be8b72b7c100ec95ecaa5ace8126d (diff) | |
Add bounding boxes and an immediate mode renderer. Pending proper bounding boxes for animated meshes.
| -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) { |
