diff options
-rw-r--r-- | CMakeLists.txt | 98 | ||||
-rw-r--r-- | README.md (renamed from gfx/README.md) | 44 | ||||
-rw-r--r-- | app/src/app.c | 3 | ||||
-rwxr-xr-x | bin/txt2c.py (renamed from gfx/bin/txt2c.py) | 0 | ||||
-rw-r--r-- | cmake/shader.txt (renamed from gfx/cmake/shader.txt) | 0 | ||||
-rw-r--r-- | cmake/txt2c.txt (renamed from gfx/cmake/txt2c.txt) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/CMakeLists.txt (renamed from gfx/contrib/cgltf-tangents/CMakeLists.txt) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/LICENSE (renamed from gfx/contrib/cgltf-tangents/LICENSE) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/MikkTSpace/README.md (renamed from gfx/contrib/cgltf-tangents/MikkTSpace/README.md) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/MikkTSpace/mikktspace.c (renamed from gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.c) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/MikkTSpace/mikktspace.h (renamed from gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.h) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/README.md (renamed from gfx/contrib/cgltf-tangents/README.md) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/cgltf_tangents.c (renamed from gfx/contrib/cgltf-tangents/cgltf_tangents.c) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/cgltf_tangents.h (renamed from gfx/contrib/cgltf-tangents/cgltf_tangents.h) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/test/CMakeLists.txt (renamed from gfx/contrib/cgltf-tangents/test/CMakeLists.txt) | 0 | ||||
-rw-r--r-- | contrib/cgltf-tangents/test/main.c (renamed from gfx/contrib/cgltf-tangents/test/main.c) | 0 | ||||
-rw-r--r-- | contrib/cgltf/CMakeLists.txt (renamed from gfx/contrib/cgltf/CMakeLists.txt) | 0 | ||||
-rw-r--r-- | contrib/cgltf/LICENSE (renamed from gfx/contrib/cgltf/LICENSE) | 0 | ||||
-rw-r--r-- | contrib/cgltf/README.md (renamed from gfx/contrib/cgltf/README.md) | 0 | ||||
-rw-r--r-- | contrib/cgltf/cgltf.h (renamed from gfx/contrib/cgltf/cgltf.h) | 0 | ||||
-rw-r--r-- | contrib/cgltf/cgltf_write.h (renamed from gfx/contrib/cgltf/cgltf_write.h) | 0 | ||||
-rw-r--r-- | contrib/stb/CMakeLists.txt (renamed from gfx/contrib/stb/CMakeLists.txt) | 0 | ||||
-rw-r--r-- | contrib/stb/stb_image.h (renamed from gfx/contrib/stb/stb_image.h) | 0 | ||||
-rw-r--r-- | doc/extern/2013SiggraphPresentationsNotes-26915738.pdf (renamed from gfx/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf) | bin | 2947929 -> 2947929 bytes | |||
-rw-r--r-- | doc/extern/Scene Graph - CSE 167.pdf (renamed from gfx/doc/extern/Scene Graph - CSE 167.pdf) | bin | 890801 -> 890801 bytes | |||
-rw-r--r-- | doc/gfx.png (renamed from gfx/doc/gfx.png) | bin | 6272 -> 6272 bytes | |||
-rw-r--r-- | doc/gfx.txt (renamed from gfx/doc/gfx.txt) | 0 | ||||
-rw-r--r-- | doc/gltfOverview-2.0.0b.png (renamed from gfx/doc/gltfOverview-2.0.0b.png) | bin | 4757973 -> 4757973 bytes | |||
-rw-r--r-- | doc/pipeline.png (renamed from gfx/doc/pipeline.png) | bin | 10318 -> 10318 bytes | |||
-rw-r--r-- | doc/pipeline.txt (renamed from gfx/doc/pipeline.txt) | 0 | ||||
-rw-r--r-- | doc/renderer.png (renamed from gfx/doc/renderer.png) | bin | 13144 -> 13144 bytes | |||
-rw-r--r-- | doc/renderer.txt (renamed from gfx/doc/renderer.txt) | 0 | ||||
-rw-r--r-- | doc/scene.png (renamed from gfx/doc/scene.png) | bin | 59119 -> 59119 bytes | |||
-rw-r--r-- | doc/scene.txt (renamed from gfx/doc/scene.txt) | 0 | ||||
-rw-r--r-- | game/CMakeLists.txt | 22 | ||||
-rw-r--r-- | game/src/game.c | 223 | ||||
-rw-r--r-- | game/src/game.h | 21 | ||||
-rw-r--r-- | game/src/plugins/CMakeLists.txt | 29 | ||||
-rw-r--r-- | game/src/plugins/plugin.h | 52 | ||||
-rw-r--r-- | game/src/plugins/pong.c | 237 | ||||
-rw-r--r-- | game/src/plugins/texture_view.c | 144 | ||||
-rw-r--r-- | game/src/plugins/viewer.c | 366 | ||||
-rw-r--r-- | gfx-iso/CMakeLists.txt | 42 | ||||
-rw-r--r-- | gfx-iso/demos/CMakeLists.txt | 2 | ||||
-rw-r--r-- | gfx-iso/demos/checkerboard/CMakeLists.txt | 16 | ||||
-rw-r--r-- | gfx-iso/demos/checkerboard/checkerboard.c | 166 | ||||
-rw-r--r-- | gfx-iso/demos/isomap/CMakeLists.txt | 16 | ||||
-rw-r--r-- | gfx-iso/demos/isomap/isomap.c | 105 | ||||
-rw-r--r-- | gfx-iso/include/isogfx/backend.h | 28 | ||||
-rw-r--r-- | gfx-iso/include/isogfx/isogfx.h | 136 | ||||
-rw-r--r-- | gfx-iso/src/backend.c | 199 | ||||
-rw-r--r-- | gfx-iso/src/isogfx.c | 952 | ||||
-rw-r--r-- | gfx-iso/tools/mkasset.py | 324 | ||||
-rw-r--r-- | gfx/CMakeLists.txt | 89 | ||||
-rw-r--r-- | gfx/include/gfx/renderer.h | 104 | ||||
-rw-r--r-- | gfx/src/renderer/imm_renderer.c | 260 | ||||
-rw-r--r-- | gfx/src/renderer/renderer.c | 396 | ||||
-rw-r--r-- | gfx/src/scene/material_impl.h | 16 | ||||
-rw-r--r-- | include/gfx/asset.h (renamed from gfx/include/gfx/asset.h) | 0 | ||||
-rw-r--r-- | include/gfx/core.h (renamed from gfx/include/gfx/core.h) | 4 | ||||
-rw-r--r-- | include/gfx/gfx.h (renamed from gfx/include/gfx/gfx.h) | 6 | ||||
-rw-r--r-- | include/gfx/llr/light.h (renamed from gfx/include/gfx/scene/light.h) | 0 | ||||
-rw-r--r-- | include/gfx/llr/llr.h | 67 | ||||
-rw-r--r-- | include/gfx/llr/material.h (renamed from gfx/include/gfx/scene/material.h) | 0 | ||||
-rw-r--r-- | include/gfx/llr/mesh.h (renamed from gfx/include/gfx/scene/mesh.h) | 0 | ||||
-rw-r--r-- | include/gfx/renderer.h | 29 | ||||
-rw-r--r-- | include/gfx/renderer/imm_renderer.h | 55 | ||||
-rw-r--r-- | include/gfx/scene.h (renamed from gfx/include/gfx/scene.h) | 7 | ||||
-rw-r--r-- | include/gfx/scene/animation.h (renamed from gfx/include/gfx/scene/animation.h) | 4 | ||||
-rw-r--r-- | include/gfx/scene/camera.h (renamed from gfx/include/gfx/scene/camera.h) | 0 | ||||
-rw-r--r-- | include/gfx/scene/model.h (renamed from gfx/include/gfx/scene/model.h) | 0 | ||||
-rw-r--r-- | include/gfx/scene/node.h (renamed from gfx/include/gfx/scene/node.h) | 4 | ||||
-rw-r--r-- | include/gfx/scene/object.h (renamed from gfx/include/gfx/scene/object.h) | 0 | ||||
-rw-r--r-- | include/gfx/scene/scene.h (renamed from gfx/include/gfx/scene/scene.h) | 0 | ||||
-rw-r--r-- | include/gfx/sizes.h (renamed from gfx/include/gfx/sizes.h) | 24 | ||||
-rw-r--r-- | include/gfx/util/geometry.h (renamed from gfx/include/gfx/util/geometry.h) | 0 | ||||
-rw-r--r-- | include/gfx/util/ibl.h (renamed from gfx/include/gfx/util/ibl.h) | 0 | ||||
-rw-r--r-- | include/gfx/util/shader.h (renamed from gfx/include/gfx/util/shader.h) | 0 | ||||
-rw-r--r-- | include/gfx/util/skyquad.h (renamed from gfx/include/gfx/util/skyquad.h) | 0 | ||||
-rw-r--r-- | shaders/brdf_integration_map.frag (renamed from gfx/shaders/brdf_integration_map.frag) | 0 | ||||
-rw-r--r-- | shaders/cook_torrance.frag (renamed from gfx/shaders/cook_torrance.frag) | 0 | ||||
-rw-r--r-- | shaders/cook_torrance.vert (renamed from gfx/shaders/cook_torrance.vert) | 0 | ||||
-rw-r--r-- | shaders/cubemap_filtering.vert (renamed from gfx/shaders/cubemap_filtering.vert) | 0 | ||||
-rw-r--r-- | shaders/debug3d.frag (renamed from gfx/shaders/debug3d.frag) | 0 | ||||
-rw-r--r-- | shaders/debug3d.vert (renamed from gfx/shaders/debug3d.vert) | 0 | ||||
-rw-r--r-- | shaders/immediate_mode.frag (renamed from gfx/shaders/immediate_mode.frag) | 0 | ||||
-rw-r--r-- | shaders/immediate_mode.vert (renamed from gfx/shaders/immediate_mode.vert) | 0 | ||||
-rw-r--r-- | shaders/irradiance_map.frag (renamed from gfx/shaders/irradiance_map.frag) | 0 | ||||
-rw-r--r-- | shaders/prefiltered_environment_map.frag (renamed from gfx/shaders/prefiltered_environment_map.frag) | 0 | ||||
-rw-r--r-- | shaders/quad.vert (renamed from gfx/shaders/quad.vert) | 0 | ||||
-rw-r--r-- | shaders/skyquad.frag (renamed from gfx/shaders/skyquad.frag) | 0 | ||||
-rw-r--r-- | shaders/skyquad.vert (renamed from gfx/shaders/skyquad.vert) | 0 | ||||
-rw-r--r-- | shaders/view_normal_mapped_normals.frag (renamed from gfx/shaders/view_normal_mapped_normals.frag) | 0 | ||||
-rw-r--r-- | shaders/view_normal_mapped_normals.vert (renamed from gfx/shaders/view_normal_mapped_normals.vert) | 0 | ||||
-rw-r--r-- | shaders/view_normals.frag (renamed from gfx/shaders/view_normals.frag) | 0 | ||||
-rw-r--r-- | shaders/view_normals.vert (renamed from gfx/shaders/view_normals.vert) | 0 | ||||
-rw-r--r-- | shaders/view_tangents.frag (renamed from gfx/shaders/view_tangents.frag) | 0 | ||||
-rw-r--r-- | shaders/view_tangents.vert (renamed from gfx/shaders/view_tangents.vert) | 0 | ||||
-rw-r--r-- | shaders/view_texture.frag (renamed from gfx/shaders/view_texture.frag) | 0 | ||||
-rw-r--r-- | shaders/view_texture.vert (renamed from gfx/shaders/view_texture.vert) | 0 | ||||
-rw-r--r-- | src/asset/asset_cache.c (renamed from gfx/src/asset/asset_cache.c) | 2 | ||||
-rw-r--r-- | src/asset/asset_cache.h (renamed from gfx/src/asset/asset_cache.h) | 0 | ||||
-rw-r--r-- | src/asset/model.c (renamed from gfx/src/asset/model.c) | 116 | ||||
-rw-r--r-- | src/asset/model.h (renamed from gfx/src/asset/model.h) | 0 | ||||
-rw-r--r-- | src/asset/texture.c (renamed from gfx/src/asset/texture.c) | 32 | ||||
-rw-r--r-- | src/asset/texture.h (renamed from gfx/src/asset/texture.h) | 0 | ||||
-rw-r--r-- | src/core/buffer.c (renamed from gfx/src/core/buffer.c) | 0 | ||||
-rw-r--r-- | src/core/buffer.h (renamed from gfx/src/core/buffer.h) | 0 | ||||
-rw-r--r-- | src/core/constants.h (renamed from gfx/src/core/constants.h) | 0 | ||||
-rw-r--r-- | src/core/core.c (renamed from gfx/src/core/core.c) | 4 | ||||
-rw-r--r-- | src/core/core_impl.h (renamed from gfx/src/core/core_impl.h) | 0 | ||||
-rw-r--r-- | src/core/framebuffer.c (renamed from gfx/src/core/framebuffer.c) | 0 | ||||
-rw-r--r-- | src/core/framebuffer.h (renamed from gfx/src/core/framebuffer.h) | 0 | ||||
-rw-r--r-- | src/core/geometry.c (renamed from gfx/src/core/geometry.c) | 0 | ||||
-rw-r--r-- | src/core/geometry.h (renamed from gfx/src/core/geometry.h) | 0 | ||||
-rw-r--r-- | src/core/gl_util.h (renamed from gfx/src/core/gl_util.h) | 0 | ||||
-rw-r--r-- | src/core/renderbuffer.c (renamed from gfx/src/core/renderbuffer.c) | 0 | ||||
-rw-r--r-- | src/core/renderbuffer.h (renamed from gfx/src/core/renderbuffer.h) | 0 | ||||
-rw-r--r-- | src/core/shader.c (renamed from gfx/src/core/shader.c) | 0 | ||||
-rw-r--r-- | src/core/shader.h (renamed from gfx/src/core/shader.h) | 0 | ||||
-rw-r--r-- | src/core/shader_program.c (renamed from gfx/src/core/shader_program.c) | 0 | ||||
-rw-r--r-- | src/core/shader_program.h (renamed from gfx/src/core/shader_program.h) | 0 | ||||
-rw-r--r-- | src/core/texture.c (renamed from gfx/src/core/texture.c) | 10 | ||||
-rw-r--r-- | src/core/texture.h (renamed from gfx/src/core/texture.h) | 0 | ||||
-rw-r--r-- | src/gfx.c (renamed from gfx/src/gfx.c) | 24 | ||||
-rw-r--r-- | src/gfx_assert.h (renamed from gfx/src/gfx_assert.h) | 0 | ||||
-rw-r--r-- | src/llr/light.c (renamed from gfx/src/scene/light.c) | 4 | ||||
-rw-r--r-- | src/llr/light_impl.h (renamed from gfx/src/scene/light_impl.h) | 4 | ||||
-rw-r--r-- | src/llr/llr.c | 381 | ||||
-rw-r--r-- | src/llr/llr_impl.h | 83 | ||||
-rw-r--r-- | src/llr/material.c (renamed from gfx/src/scene/material.c) | 4 | ||||
-rw-r--r-- | src/llr/material_impl.h | 15 | ||||
-rw-r--r-- | src/llr/mesh.c (renamed from gfx/src/scene/mesh.c) | 2 | ||||
-rw-r--r-- | src/llr/mesh_impl.h (renamed from gfx/src/scene/mesh_impl.h) | 2 | ||||
-rw-r--r-- | src/renderer/imm_renderer.c | 192 | ||||
-rw-r--r-- | src/renderer/imm_renderer_impl.h (renamed from gfx/src/renderer/imm_renderer_impl.h) | 29 | ||||
-rw-r--r-- | src/renderer/renderer.c | 247 | ||||
-rw-r--r-- | src/renderer/renderer_impl.h (renamed from gfx/src/renderer/renderer_impl.h) | 10 | ||||
-rw-r--r-- | src/scene/animation.c (renamed from gfx/src/scene/animation.c) | 0 | ||||
-rw-r--r-- | src/scene/animation_impl.h (renamed from gfx/src/scene/animation_impl.h) | 0 | ||||
-rw-r--r-- | src/scene/camera.c (renamed from gfx/src/scene/camera.c) | 0 | ||||
-rw-r--r-- | src/scene/camera_impl.h (renamed from gfx/src/scene/camera_impl.h) | 0 | ||||
-rw-r--r-- | src/scene/model.c (renamed from gfx/src/scene/model.c) | 0 | ||||
-rw-r--r-- | src/scene/model_impl.h (renamed from gfx/src/scene/model_impl.h) | 0 | ||||
-rw-r--r-- | src/scene/node.c (renamed from gfx/src/scene/node.c) | 2 | ||||
-rw-r--r-- | src/scene/node_impl.h (renamed from gfx/src/scene/node_impl.h) | 0 | ||||
-rw-r--r-- | src/scene/object.c (renamed from gfx/src/scene/object.c) | 2 | ||||
-rw-r--r-- | src/scene/object_impl.h (renamed from gfx/src/scene/object_impl.h) | 7 | ||||
-rw-r--r-- | src/scene/scene.c (renamed from gfx/src/scene/scene.c) | 0 | ||||
-rw-r--r-- | src/scene/scene_graph.h (renamed from gfx/src/scene/scene_graph.h) | 42 | ||||
-rw-r--r-- | src/scene/scene_impl.h (renamed from gfx/src/scene/scene_impl.h) | 0 | ||||
-rw-r--r-- | src/scene/scene_memory.c (renamed from gfx/src/scene/scene_memory.c) | 67 | ||||
-rw-r--r-- | src/scene/scene_memory.h (renamed from gfx/src/scene/scene_memory.h) | 0 | ||||
-rw-r--r-- | src/scene/types.h (renamed from gfx/src/scene/types.h) | 0 | ||||
-rw-r--r-- | src/util/geometry.c (renamed from gfx/src/util/geometry.c) | 0 | ||||
-rw-r--r-- | src/util/ibl.c (renamed from gfx/src/util/ibl.c) | 0 | ||||
-rw-r--r-- | src/util/shader.c (renamed from gfx/src/util/shader.c) | 0 | ||||
-rw-r--r-- | src/util/skyquad.c (renamed from gfx/src/util/skyquad.c) | 17 |
158 files changed, 1447 insertions, 4141 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 25c7560..c480daf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -1,8 +1,96 @@ | |||
1 | cmake_minimum_required(VERSION 3.0) | 1 | cmake_minimum_required(VERSION 3.5) |
2 | 2 | ||
3 | project(gfx-all) | 3 | add_subdirectory(contrib/cgltf) |
4 | add_subdirectory(contrib/cgltf-tangents) | ||
5 | add_subdirectory(contrib/stb) | ||
4 | 6 | ||
5 | add_subdirectory(app) | 7 | add_subdirectory(app) |
6 | add_subdirectory(gfx) | 8 | |
7 | add_subdirectory(gfx-iso) | 9 | project(gfx) |
8 | add_subdirectory(game) | 10 | |
11 | include(cmake/shader.txt) | ||
12 | |||
13 | set(CMAKE_C_STANDARD 23) | ||
14 | set(CMAKE_C_STANDARD_REQUIRED On) | ||
15 | set(CMAKE_C_EXTENSIONS Off) | ||
16 | |||
17 | add_shader_library(shaders | ||
18 | shaders/brdf_integration_map.frag | ||
19 | shaders/cook_torrance.frag | ||
20 | shaders/cook_torrance.vert | ||
21 | shaders/cubemap_filtering.vert | ||
22 | shaders/debug3d.frag | ||
23 | shaders/debug3d.vert | ||
24 | shaders/immediate_mode.frag | ||
25 | shaders/immediate_mode.vert | ||
26 | shaders/irradiance_map.frag | ||
27 | shaders/prefiltered_environment_map.frag | ||
28 | shaders/quad.vert | ||
29 | shaders/skyquad.frag | ||
30 | shaders/skyquad.vert | ||
31 | shaders/view_normal_mapped_normals.frag | ||
32 | shaders/view_normal_mapped_normals.vert | ||
33 | shaders/view_normals.frag | ||
34 | shaders/view_normals.vert | ||
35 | shaders/view_tangents.frag | ||
36 | shaders/view_tangents.vert | ||
37 | shaders/view_texture.frag | ||
38 | shaders/view_texture.vert) | ||
39 | |||
40 | add_library(gfx SHARED | ||
41 | src/asset/asset_cache.c | ||
42 | src/asset/model.c | ||
43 | src/asset/texture.c | ||
44 | src/core/buffer.c | ||
45 | src/core/core.c | ||
46 | src/core/framebuffer.c | ||
47 | src/core/geometry.c | ||
48 | src/core/renderbuffer.c | ||
49 | src/core/shader_program.c | ||
50 | src/core/shader.c | ||
51 | src/core/texture.c | ||
52 | src/llr/llr.c | ||
53 | src/llr/light.c | ||
54 | src/llr/material.c | ||
55 | src/llr/mesh.c | ||
56 | src/renderer/imm_renderer.c | ||
57 | src/renderer/renderer.c | ||
58 | src/scene/animation.c | ||
59 | src/scene/camera.c | ||
60 | src/scene/model.c | ||
61 | src/scene/node.c | ||
62 | src/scene/object.c | ||
63 | src/scene/scene.c | ||
64 | src/scene/scene_memory.c | ||
65 | src/gfx.c | ||
66 | src/util/geometry.c | ||
67 | src/util/ibl.c | ||
68 | src/util/shader.c | ||
69 | src/util/skyquad.c) | ||
70 | |||
71 | target_include_directories(gfx PUBLIC | ||
72 | include) | ||
73 | |||
74 | target_include_directories(gfx PRIVATE | ||
75 | src) | ||
76 | |||
77 | target_compile_options(gfx PRIVATE -std=gnu11 -Wall -Wextra -Wpedantic) | ||
78 | |||
79 | target_link_libraries(gfx PUBLIC | ||
80 | cstring | ||
81 | math) | ||
82 | |||
83 | target_link_libraries(gfx PRIVATE | ||
84 | cassert | ||
85 | cgltf | ||
86 | cgltf-tangents | ||
87 | error | ||
88 | gfx-app | ||
89 | log | ||
90 | mempool | ||
91 | shaders | ||
92 | stb | ||
93 | # System libraries. | ||
94 | GL | ||
95 | # Required to initialize GLAD. | ||
96 | -ldl) | ||
diff --git a/gfx/README.md b/README.md index f0b103d..491761d 100644 --- a/gfx/README.md +++ b/README.md | |||
@@ -20,32 +20,35 @@ educational purposes. | |||
20 | ### Gfx | 20 | ### Gfx |
21 | 21 | ||
22 | The `Gfx` object represents the graphics subsystem and is at the center of the | 22 | The `Gfx` object represents the graphics subsystem and is at the center of the |
23 | library's high-level API. The `Gfx` object exposes a `Render` backend and a | 23 | library's high-level API. The `Gfx` object exposes a render backend (`GfxCore`) |
24 | `Renderer` and allows the caller to create `Scene`s. | 24 | and a `Renderer`, and allows the caller to create `Scene`s. |
25 | 25 | ||
26 | ### Render Backend | 26 | ### Render Backend |
27 | 27 | ||
28 | The `Render` backend is a thin abstraction layer over low-level graphics APIs | 28 | The render backend (`GfxCore`) is a thin abstraction layer over low-level |
29 | like OpenGL or Vulkan. It holds GPU resources such as geometry, textures, | 29 | graphics APIs like OpenGL or Vulkan. It holds GPU resources such as geometry, |
30 | shaders, etc, and exposes functions to manipulate them. | 30 | textures, shaders, etc, and exposes functions to manipulate them. |
31 | 31 | ||
32 | Currently there is only one implementation of the `Render` backend based on | 32 | Currently, there is only one implementation of the render backend based on |
33 | OpenGL. | 33 | OpenGL. |
34 | 34 | ||
35 | #### Ownership | 35 | #### Ownership |
36 | 36 | ||
37 | The `Render` backend owns all rendering resources: buffers, geometries, | 37 | The render backend owns all rendering resources: buffers, geometries, textures, |
38 | textures, shaders, etc. Even resources that point to other resources do not own | 38 | shaders, etc. Even resources that point to other resources do not own those |
39 | those other resources (geometries pointing to buffers). | 39 | other resources (geometries pointing to buffers). |
40 | 40 | ||
41 | There is no ownership tracking in the render backend. It is up to the client to | 41 | There is no lifetime tracking in the render backend. It is up to the client to |
42 | manage resource lifetime. | 42 | manage resource lifetime. |
43 | 43 | ||
44 | ### Low-level Renderer | ||
45 | |||
46 | `ImmRenderer` is a low-level, immediate mode renderer. | ||
47 | |||
44 | ### Scene | 48 | ### Scene |
45 | 49 | ||
46 | A `Scene` encapsulates a scene graph. A scene graph contains the elements that | 50 | A `Scene` graph contains the elements that make up a scene. The current scene |
47 | make up a scene: nodes, cameras, lights, objects, etc. The current scene graph | 51 | graph implementation includes: |
48 | implementation includes: | ||
49 | 52 | ||
50 | - Camera | 53 | - Camera |
51 | - Light | 54 | - Light |
@@ -64,7 +67,8 @@ functions to set the parent node of an object/camera/light. If we exposed the | |||
64 | former, the API could create the illusion that the hierarchy can be a DAG. | 67 | former, the API could create the illusion that the hierarchy can be a DAG. |
65 | 68 | ||
66 | The strict tree hierarchy should not be that restrictive in practice. Even the | 69 | The strict tree hierarchy should not be that restrictive in practice. Even the |
67 | glTF 2.0 spec [enforces this](https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#nodes-and-hierarchy): | 70 | glTF 2.0 |
71 | spec [enforces this](https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#nodes-and-hierarchy): | ||
68 | 72 | ||
69 | > *For Version 2.0 conformance, the glTF node hierarchy is not a directed | 73 | > *For Version 2.0 conformance, the glTF node hierarchy is not a directed |
70 | > acyclic graph (DAG) or scene graph, but a disjoint union of strict trees. That | 74 | > acyclic graph (DAG) or scene graph, but a disjoint union of strict trees. That |
@@ -76,9 +80,9 @@ glTF 2.0 spec [enforces this](https://github.com/KhronosGroup/glTF/blob/master/s | |||
76 | Two use cases for instancing seem to be: | 80 | Two use cases for instancing seem to be: |
77 | 81 | ||
78 | 1. Creating N identical clones, but each with a unique transform. (Ex: N | 82 | 1. Creating N identical clones, but each with a unique transform. (Ex: N |
79 | animated characters animated in unison but located in different locations.) | 83 | animated characters animated in unison but located in different locations.) |
80 | 2. Creating N copies of a sub-tree, each now being their own unique tree. (Ex: | 84 | 2. Creating N copies of a sub-tree, each now being their own unique tree. (Ex: |
81 | The same N animated characters, but each of them now being animated separately.) | 85 | The same N animated characters, but each of them now being animated separately.) |
82 | 86 | ||
83 | Some scene graphs | 87 | Some scene graphs |
84 | ([Panda3D](https://docs.panda3d.org/1.10/python/programming/scene-graph/instancing)) | 88 | ([Panda3D](https://docs.panda3d.org/1.10/python/programming/scene-graph/instancing)) |
@@ -87,10 +91,10 @@ to have multiple parents. This turns the scene graph into a DAG and adds a | |||
87 | number of complications for us: | 91 | number of complications for us: |
88 | 92 | ||
89 | 1. Shared ownership of children. We would now need some sort of ref counting or | 93 | 1. Shared ownership of children. We would now need some sort of ref counting or |
90 | deferred GC to delete nodes and their subtrees. | 94 | deferred GC to delete nodes and their subtrees. |
91 | 2. Nodes no longer have a unique parent. | 95 | 2. Nodes no longer have a unique parent. |
92 | 3. Given a node, we can no longer determine its location (which parent link do | 96 | 3. Given a node, we can no longer determine its location (which parent link do |
93 | you follow?), or any attribute that is derived from its parent(s). | 97 | you follow?), or any attribute that is derived from its parent(s). |
94 | 98 | ||
95 | In our case, we stick to strict tree hierarchies. | 99 | In our case, we stick to strict tree hierarchies. |
96 | 100 | ||
@@ -131,6 +135,10 @@ Code under `util/` provides functionality that need not be in the core part | |||
131 | of the library (Gfx, render backend, scene or renderer). This includes functions | 135 | of the library (Gfx, render backend, scene or renderer). This includes functions |
132 | to compute irradiance maps, create procedural geometry, etc. | 136 | to compute irradiance maps, create procedural geometry, etc. |
133 | 137 | ||
138 | ### Memory management | ||
139 | |||
140 | TODO | ||
141 | |||
134 | ## Ideas for Future Work | 142 | ## Ideas for Future Work |
135 | 143 | ||
136 | - Render graphs to allow for custom multi-pass rendering algorithms. | 144 | - Render graphs to allow for custom multi-pass rendering algorithms. |
diff --git a/app/src/app.c b/app/src/app.c index 9b816ee..58d7b90 100644 --- a/app/src/app.c +++ b/app/src/app.c | |||
@@ -112,6 +112,9 @@ bool gfx_app_run(const GfxAppDesc* desc, const GfxAppCallbacks* callbacks) { | |||
112 | } | 112 | } |
113 | glfwMakeContextCurrent(g_gfx_app.window); | 113 | glfwMakeContextCurrent(g_gfx_app.window); |
114 | 114 | ||
115 | // Request adaptive sync if supported. | ||
116 | glfwSwapInterval(-1); | ||
117 | |||
115 | // Load GL before calling the application init clalback. | 118 | // Load GL before calling the application init clalback. |
116 | if (!gladLoadGL()) { | 119 | if (!gladLoadGL()) { |
117 | LOGE("Failed loading glad!"); | 120 | LOGE("Failed loading glad!"); |
diff --git a/gfx/bin/txt2c.py b/bin/txt2c.py index 1d8ff70..1d8ff70 100755 --- a/gfx/bin/txt2c.py +++ b/bin/txt2c.py | |||
diff --git a/gfx/cmake/shader.txt b/cmake/shader.txt index 8273f4d..8273f4d 100644 --- a/gfx/cmake/shader.txt +++ b/cmake/shader.txt | |||
diff --git a/gfx/cmake/txt2c.txt b/cmake/txt2c.txt index 0cb11a9..0cb11a9 100644 --- a/gfx/cmake/txt2c.txt +++ b/cmake/txt2c.txt | |||
diff --git a/gfx/contrib/cgltf-tangents/CMakeLists.txt b/contrib/cgltf-tangents/CMakeLists.txt index 2c0771e..2c0771e 100644 --- a/gfx/contrib/cgltf-tangents/CMakeLists.txt +++ b/contrib/cgltf-tangents/CMakeLists.txt | |||
diff --git a/gfx/contrib/cgltf-tangents/LICENSE b/contrib/cgltf-tangents/LICENSE index 7796e37..7796e37 100644 --- a/gfx/contrib/cgltf-tangents/LICENSE +++ b/contrib/cgltf-tangents/LICENSE | |||
diff --git a/gfx/contrib/cgltf-tangents/MikkTSpace/README.md b/contrib/cgltf-tangents/MikkTSpace/README.md index 9fda155..9fda155 100644 --- a/gfx/contrib/cgltf-tangents/MikkTSpace/README.md +++ b/contrib/cgltf-tangents/MikkTSpace/README.md | |||
diff --git a/gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.c b/contrib/cgltf-tangents/MikkTSpace/mikktspace.c index 0342ae0..0342ae0 100644 --- a/gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.c +++ b/contrib/cgltf-tangents/MikkTSpace/mikktspace.c | |||
diff --git a/gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.h b/contrib/cgltf-tangents/MikkTSpace/mikktspace.h index 52c44a7..52c44a7 100644 --- a/gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.h +++ b/contrib/cgltf-tangents/MikkTSpace/mikktspace.h | |||
diff --git a/gfx/contrib/cgltf-tangents/README.md b/contrib/cgltf-tangents/README.md index 2a68b27..2a68b27 100644 --- a/gfx/contrib/cgltf-tangents/README.md +++ b/contrib/cgltf-tangents/README.md | |||
diff --git a/gfx/contrib/cgltf-tangents/cgltf_tangents.c b/contrib/cgltf-tangents/cgltf_tangents.c index 80b1e56..80b1e56 100644 --- a/gfx/contrib/cgltf-tangents/cgltf_tangents.c +++ b/contrib/cgltf-tangents/cgltf_tangents.c | |||
diff --git a/gfx/contrib/cgltf-tangents/cgltf_tangents.h b/contrib/cgltf-tangents/cgltf_tangents.h index 79e3502..79e3502 100644 --- a/gfx/contrib/cgltf-tangents/cgltf_tangents.h +++ b/contrib/cgltf-tangents/cgltf_tangents.h | |||
diff --git a/gfx/contrib/cgltf-tangents/test/CMakeLists.txt b/contrib/cgltf-tangents/test/CMakeLists.txt index 422c950..422c950 100644 --- a/gfx/contrib/cgltf-tangents/test/CMakeLists.txt +++ b/contrib/cgltf-tangents/test/CMakeLists.txt | |||
diff --git a/gfx/contrib/cgltf-tangents/test/main.c b/contrib/cgltf-tangents/test/main.c index 0d70008..0d70008 100644 --- a/gfx/contrib/cgltf-tangents/test/main.c +++ b/contrib/cgltf-tangents/test/main.c | |||
diff --git a/gfx/contrib/cgltf/CMakeLists.txt b/contrib/cgltf/CMakeLists.txt index 0ac840a..0ac840a 100644 --- a/gfx/contrib/cgltf/CMakeLists.txt +++ b/contrib/cgltf/CMakeLists.txt | |||
diff --git a/gfx/contrib/cgltf/LICENSE b/contrib/cgltf/LICENSE index 0afe8c7..0afe8c7 100644 --- a/gfx/contrib/cgltf/LICENSE +++ b/contrib/cgltf/LICENSE | |||
diff --git a/gfx/contrib/cgltf/README.md b/contrib/cgltf/README.md index 3b49d52..3b49d52 100644 --- a/gfx/contrib/cgltf/README.md +++ b/contrib/cgltf/README.md | |||
diff --git a/gfx/contrib/cgltf/cgltf.h b/contrib/cgltf/cgltf.h index 077cf36..077cf36 100644 --- a/gfx/contrib/cgltf/cgltf.h +++ b/contrib/cgltf/cgltf.h | |||
diff --git a/gfx/contrib/cgltf/cgltf_write.h b/contrib/cgltf/cgltf_write.h index 2096a5b..2096a5b 100644 --- a/gfx/contrib/cgltf/cgltf_write.h +++ b/contrib/cgltf/cgltf_write.h | |||
diff --git a/gfx/contrib/stb/CMakeLists.txt b/contrib/stb/CMakeLists.txt index 8cee003..8cee003 100644 --- a/gfx/contrib/stb/CMakeLists.txt +++ b/contrib/stb/CMakeLists.txt | |||
diff --git a/gfx/contrib/stb/stb_image.h b/contrib/stb/stb_image.h index 97038e6..97038e6 100644 --- a/gfx/contrib/stb/stb_image.h +++ b/contrib/stb/stb_image.h | |||
diff --git a/gfx/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf b/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf index 989658e..989658e 100644 --- a/gfx/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf +++ b/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf | |||
Binary files differ | |||
diff --git a/gfx/doc/extern/Scene Graph - CSE 167.pdf b/doc/extern/Scene Graph - CSE 167.pdf index 5fbbb10..5fbbb10 100644 --- a/gfx/doc/extern/Scene Graph - CSE 167.pdf +++ b/doc/extern/Scene Graph - CSE 167.pdf | |||
Binary files differ | |||
diff --git a/gfx/doc/gfx.png b/doc/gfx.png index e64f6e1..e64f6e1 100644 --- a/gfx/doc/gfx.png +++ b/doc/gfx.png | |||
Binary files differ | |||
diff --git a/gfx/doc/gfx.txt b/doc/gfx.txt index d3ce01b..d3ce01b 100644 --- a/gfx/doc/gfx.txt +++ b/doc/gfx.txt | |||
diff --git a/gfx/doc/gltfOverview-2.0.0b.png b/doc/gltfOverview-2.0.0b.png index 6a5bb61..6a5bb61 100644 --- a/gfx/doc/gltfOverview-2.0.0b.png +++ b/doc/gltfOverview-2.0.0b.png | |||
Binary files differ | |||
diff --git a/gfx/doc/pipeline.png b/doc/pipeline.png index 426f39e..426f39e 100644 --- a/gfx/doc/pipeline.png +++ b/doc/pipeline.png | |||
Binary files differ | |||
diff --git a/gfx/doc/pipeline.txt b/doc/pipeline.txt index 51523d6..51523d6 100644 --- a/gfx/doc/pipeline.txt +++ b/doc/pipeline.txt | |||
diff --git a/gfx/doc/renderer.png b/doc/renderer.png index d0516b0..d0516b0 100644 --- a/gfx/doc/renderer.png +++ b/doc/renderer.png | |||
Binary files differ | |||
diff --git a/gfx/doc/renderer.txt b/doc/renderer.txt index 90b18f8..90b18f8 100644 --- a/gfx/doc/renderer.txt +++ b/doc/renderer.txt | |||
diff --git a/gfx/doc/scene.png b/doc/scene.png index 85d2447..85d2447 100644 --- a/gfx/doc/scene.png +++ b/doc/scene.png | |||
Binary files differ | |||
diff --git a/gfx/doc/scene.txt b/doc/scene.txt index a771488..a771488 100644 --- a/gfx/doc/scene.txt +++ b/doc/scene.txt | |||
diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt deleted file mode 100644 index 3a88bb7..0000000 --- a/game/CMakeLists.txt +++ /dev/null | |||
@@ -1,22 +0,0 @@ | |||
1 | cmake_minimum_required(VERSION 3.0) | ||
2 | |||
3 | add_subdirectory(src/plugins) | ||
4 | |||
5 | project(game) | ||
6 | |||
7 | add_executable(game | ||
8 | src/game.c) | ||
9 | |||
10 | target_include_directories(game PRIVATE | ||
11 | src/) | ||
12 | |||
13 | target_link_libraries(game PRIVATE | ||
14 | cstring | ||
15 | error | ||
16 | gfx | ||
17 | gfx-app | ||
18 | list | ||
19 | log | ||
20 | math | ||
21 | mempool | ||
22 | plugin) | ||
diff --git a/game/src/game.c b/game/src/game.c deleted file mode 100644 index dc2248b..0000000 --- a/game/src/game.c +++ /dev/null | |||
@@ -1,223 +0,0 @@ | |||
1 | /* | ||
2 | * Main game module with entry point and game loop. | ||
3 | * | ||
4 | * The game module sets up the window and GL context and defers the core game | ||
5 | * logic to a plugin. | ||
6 | */ | ||
7 | #define _GNU_SOURCE 200112L // For readlink() | ||
8 | |||
9 | #include "game.h" | ||
10 | |||
11 | #include "plugins/plugin.h" | ||
12 | |||
13 | #include <gfx/app.h> | ||
14 | #include <gfx/core.h> | ||
15 | #include <gfx/gfx.h> | ||
16 | #include <gfx/renderer.h> | ||
17 | #include <gfx/scene/camera.h> | ||
18 | #include <gfx/scene/node.h> | ||
19 | #include <gfx/scene/object.h> | ||
20 | #include <gfx/scene/scene.h> | ||
21 | |||
22 | #include <error.h> | ||
23 | #include <log/log.h> | ||
24 | #include <math/camera.h> | ||
25 | #include <plugin.h> | ||
26 | |||
27 | #include <assert.h> | ||
28 | #include <stdbool.h> | ||
29 | #include <stdio.h> | ||
30 | #include <stdlib.h> | ||
31 | |||
32 | #include <linux/limits.h> | ||
33 | |||
34 | #include <unistd.h> | ||
35 | |||
36 | #undef _GNU_SOURCE | ||
37 | |||
38 | static const int WIDTH = 1350; | ||
39 | static const int HEIGHT = 900; | ||
40 | static const int MAX_FPS = 60; | ||
41 | |||
42 | typedef struct GfxAppState { | ||
43 | Game game; | ||
44 | } GfxAppState; | ||
45 | |||
46 | /// Initialize the game's plugin. | ||
47 | static bool init_plugin(Game* game) { | ||
48 | assert(game); | ||
49 | assert(game->plugin); | ||
50 | // Plugin state is allowed to be null, either when the plugin does not | ||
51 | // expose an init() or when init() does not initialize a state. | ||
52 | if (plugin_resolve(game->plugin, plugin_init, "init")) { | ||
53 | State* plugin_state = 0; | ||
54 | if (!plugin_call(game->plugin, plugin_init, "init", game, &plugin_state)) { | ||
55 | return false; | ||
56 | } | ||
57 | set_plugin_state(game->plugin, plugin_state); | ||
58 | } | ||
59 | return true; // Plugin does not need to expose an init(). | ||
60 | } | ||
61 | |||
62 | /// Shutdown the game's plugin. | ||
63 | /// The game's plugin is allowed to be null in the call to this function. | ||
64 | static void shutdown_plugin(Game* game) { | ||
65 | assert(game); | ||
66 | if (game->plugin && | ||
67 | (plugin_resolve(game->plugin, plugin_shutdown, "shutdown"))) { | ||
68 | void* plugin_state = get_plugin_state(game->plugin); | ||
69 | plugin_call(game->plugin, plugin_shutdown, "shutdown", game, plugin_state); | ||
70 | set_plugin_state(game->plugin, 0); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /// Boot the game's plugin. | ||
75 | static bool boot_plugin(Game* game) { | ||
76 | assert(game); | ||
77 | assert(game->plugin); | ||
78 | if (plugin_resolve(game->plugin, plugin_boot, "boot")) { | ||
79 | void* plugin_state = get_plugin_state(game->plugin); | ||
80 | return plugin_call(game->plugin, plugin_boot, "boot", game, plugin_state); | ||
81 | } | ||
82 | return true; // Plugin does not need to expose a boot(). | ||
83 | } | ||
84 | |||
85 | /// Update the plugin's state. | ||
86 | static void update_plugin(Game* game, double t, double dt) { | ||
87 | assert(game); | ||
88 | assert(game->plugin); | ||
89 | if (plugin_resolve(game->plugin, plugin_update, "update")) { | ||
90 | void* plugin_state = get_plugin_state(game->plugin); | ||
91 | plugin_call( | ||
92 | game->plugin, plugin_update, "update", game, plugin_state, t, dt); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | /// Plugin render. | ||
97 | static void render_plugin(const Game* game) { | ||
98 | assert(game); | ||
99 | assert(game->plugin); | ||
100 | if (plugin_resolve(game->plugin, plugin_render, "render")) { | ||
101 | void* plugin_state = get_plugin_state(game->plugin); | ||
102 | plugin_call(game->plugin, plugin_render, "render", game, plugin_state); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | /// Plugin resize. | ||
107 | static void resize_plugin(Game* game, int width, int height) { | ||
108 | assert(game); | ||
109 | assert(game->plugin); | ||
110 | if (plugin_resolve(game->plugin, plugin_resize, "resize")) { | ||
111 | void* plugin_state = get_plugin_state(game->plugin); | ||
112 | plugin_call( | ||
113 | game->plugin, plugin_resize, "resize", game, plugin_state, width, | ||
114 | height); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static void Shutdown(Game* game); | ||
119 | |||
120 | static bool Init(Game* game, int argc, const char** argv) { | ||
121 | assert(game); | ||
122 | |||
123 | if (argc <= 1) { | ||
124 | LOGE("Usage: %s <plugin> [plugin args]", argv[0]); | ||
125 | return false; | ||
126 | } | ||
127 | |||
128 | // Syntax: game <plugin> [plugin args] | ||
129 | // | ||
130 | // Here we consume the <plugin> arg so that plugins receive the remainder | ||
131 | // args starting from 0. | ||
132 | game->argc = argc - 1; | ||
133 | game->argv = argv + 1; | ||
134 | |||
135 | char exe_path_buf[NAME_MAX] = {0}; | ||
136 | if (readlink("/proc/self/exe", exe_path_buf, sizeof(exe_path_buf)) == -1) { | ||
137 | LOGE("readlink(/proc/self/exe) failed"); | ||
138 | goto cleanup; | ||
139 | } | ||
140 | |||
141 | // Replace the last / with a null terminator to remove the exe file from the | ||
142 | // path. This gets the file's parent directory. | ||
143 | *strrchr(exe_path_buf, '/') = 0; | ||
144 | |||
145 | const mstring exe_dir = mstring_make(exe_path_buf); | ||
146 | const mstring plugins_path = mstring_concat_cstr(exe_dir, "/src/plugins"); | ||
147 | |||
148 | if (!(game->plugin_engine = new_plugin_engine( | ||
149 | &(PluginEngineDesc){.plugins_dir = mstring_cstr(&plugins_path)}))) { | ||
150 | goto cleanup; | ||
151 | } | ||
152 | |||
153 | const char* plugin = argv[1]; | ||
154 | if (!(game->plugin = load_plugin(game->plugin_engine, plugin))) { | ||
155 | goto cleanup; | ||
156 | } | ||
157 | |||
158 | if (!(game->gfx = gfx_init())) { | ||
159 | goto cleanup; | ||
160 | } | ||
161 | |||
162 | if (!init_plugin(game)) { | ||
163 | goto cleanup; | ||
164 | } | ||
165 | if (!boot_plugin(game)) { | ||
166 | goto cleanup; | ||
167 | } | ||
168 | |||
169 | return true; | ||
170 | |||
171 | cleanup: | ||
172 | LOGE("Gfx error: %s", get_error()); | ||
173 | Shutdown(game); | ||
174 | return false; | ||
175 | } | ||
176 | |||
177 | static void Shutdown(Game* game) { | ||
178 | assert(game); | ||
179 | shutdown_plugin(game); | ||
180 | if (game->gfx) { | ||
181 | gfx_destroy(&game->gfx); | ||
182 | } | ||
183 | if (game->plugin) { | ||
184 | delete_plugin(&game->plugin); | ||
185 | } | ||
186 | if (game->plugin_engine) { | ||
187 | delete_plugin_engine(&game->plugin_engine); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | static void Update(Game* game, double t, double dt) { | ||
192 | plugin_engine_update(game->plugin_engine); | ||
193 | if (plugin_reloaded(game->plugin)) { | ||
194 | shutdown_plugin(game); | ||
195 | const bool result = init_plugin(game); | ||
196 | assert(result); // TODO: handle error better. | ||
197 | |||
198 | // Trigger a resize just like the initial resize that occurs when the gfx | ||
199 | // application starts. | ||
200 | resize_plugin(game, game->width, game->height); | ||
201 | } | ||
202 | |||
203 | update_plugin(game, t, dt); | ||
204 | } | ||
205 | |||
206 | static void Render(const Game* game) { | ||
207 | GfxCore* gfxcore = gfx_get_core(game->gfx); | ||
208 | gfx_start_frame(gfxcore); | ||
209 | render_plugin(game); | ||
210 | gfx_end_frame(gfxcore); | ||
211 | } | ||
212 | |||
213 | static void Resize(Game* game, int width, int height) { | ||
214 | game->width = width; | ||
215 | game->height = height; | ||
216 | |||
217 | GfxCore* gfxcore = gfx_get_core(game->gfx); | ||
218 | gfx_set_viewport(gfxcore, 0, 0, width, height); | ||
219 | |||
220 | resize_plugin(game, width, height); | ||
221 | } | ||
222 | |||
223 | GFX_APP_MAIN(WIDTH, HEIGHT, MAX_FPS, "Game"); | ||
diff --git a/game/src/game.h b/game/src/game.h deleted file mode 100644 index 579ba3c..0000000 --- a/game/src/game.h +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | /* | ||
2 | * Header file defining the game state, included by plugins. | ||
3 | */ | ||
4 | #pragma once | ||
5 | |||
6 | typedef struct PluginEngine PluginEngine; | ||
7 | typedef struct Plugin Plugin; | ||
8 | typedef struct Gfx Gfx; | ||
9 | typedef struct Scene Scene; | ||
10 | typedef struct SceneCamera SceneCamera; | ||
11 | |||
12 | /// Game state. | ||
13 | typedef struct { | ||
14 | int argc; | ||
15 | const char** argv; | ||
16 | PluginEngine* plugin_engine; | ||
17 | Plugin* plugin; | ||
18 | Gfx* gfx; | ||
19 | int width; | ||
20 | int height; | ||
21 | } Game; | ||
diff --git a/game/src/plugins/CMakeLists.txt b/game/src/plugins/CMakeLists.txt deleted file mode 100644 index 8661598..0000000 --- a/game/src/plugins/CMakeLists.txt +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
1 | cmake_minimum_required(VERSION 3.0) | ||
2 | |||
3 | project(plugins) | ||
4 | |||
5 | set(LINK_LIBRARIES cstring math gfx gfx-app) | ||
6 | |||
7 | # Viewer | ||
8 | |||
9 | add_library(viewer SHARED | ||
10 | viewer.c) | ||
11 | |||
12 | target_link_libraries(viewer PUBLIC | ||
13 | ${LINK_LIBRARIES}) | ||
14 | |||
15 | # Texture viewer | ||
16 | |||
17 | add_library(texture_view SHARED | ||
18 | texture_view.c) | ||
19 | |||
20 | target_link_libraries(texture_view PUBLIC | ||
21 | ${LINK_LIBRARIES}) | ||
22 | |||
23 | # Pong | ||
24 | |||
25 | add_library(pong SHARED | ||
26 | pong.c) | ||
27 | |||
28 | target_link_libraries(pong PUBLIC | ||
29 | ${LINK_LIBRARIES}) | ||
diff --git a/game/src/plugins/plugin.h b/game/src/plugins/plugin.h deleted file mode 100644 index f7219c6..0000000 --- a/game/src/plugins/plugin.h +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | /* | ||
2 | * Game plugin. | ||
3 | */ | ||
4 | #pragma once | ||
5 | |||
6 | #include "../game.h" | ||
7 | |||
8 | #include <gfx/gfx.h> | ||
9 | #include <gfx/scene.h> | ||
10 | |||
11 | #include <stdbool.h> | ||
12 | |||
13 | typedef struct State State; | ||
14 | |||
15 | /// Initialize the plugin, which may optionally return a state object. | ||
16 | /// | ||
17 | /// This function is called every time the plugin is (re)loaded. | ||
18 | /// | ||
19 | /// It is assumed that the plugin's state is fully encapsulated in the returned | ||
20 | /// state object. The plugin should not store any (mutable) state outside of the | ||
21 | /// returned state object (e.g., no mutable global variables.) | ||
22 | bool init(Game*, State**); | ||
23 | |||
24 | /// Shut down the plugin. | ||
25 | /// | ||
26 | /// This function is called before the plugin is unloaded. | ||
27 | /// | ||
28 | /// The plugin should perform any destruction needed, but not free the state | ||
29 | /// object; freeing the state object's memory is handled by the caller. | ||
30 | void shutdown(Game*, State*); | ||
31 | |||
32 | /// Function called the first time the plugin is loaded throughout the | ||
33 | /// application's lifetime. This allows the plugin to do one-time initialization | ||
34 | /// of the game state. | ||
35 | bool boot(Game*, State*); | ||
36 | |||
37 | /// Update the plugin's and the game's state. | ||
38 | void update(Game*, State*, double t, double dt); | ||
39 | |||
40 | /// Render hook. | ||
41 | void render(const Game*, const State*); | ||
42 | |||
43 | /// Called when the game's window is resized. | ||
44 | void resize(Game*, State*, int width, int height); | ||
45 | |||
46 | // Signatures for the plugin's exposed functions. | ||
47 | typedef bool (*plugin_init)(Game*, State**); | ||
48 | typedef bool (*plugin_shutdown)(Game*, State*); | ||
49 | typedef bool (*plugin_boot)(Game*, State*); | ||
50 | typedef void (*plugin_update)(Game*, State*, double t, double dt); | ||
51 | typedef void (*plugin_render)(const Game*, const State*); | ||
52 | typedef void (*plugin_resize)(Game* game, State* state, int width, int height); | ||
diff --git a/game/src/plugins/pong.c b/game/src/plugins/pong.c deleted file mode 100644 index c1c55be..0000000 --- a/game/src/plugins/pong.c +++ /dev/null | |||
@@ -1,237 +0,0 @@ | |||
1 | #include "plugin.h" | ||
2 | |||
3 | #include <gfx/app.h> | ||
4 | #include <gfx/gfx.h> | ||
5 | #include <gfx/renderer.h> | ||
6 | |||
7 | #include <math/mat4.h> | ||
8 | #include <math/vec2.h> | ||
9 | #include <math/vec4.h> | ||
10 | |||
11 | #include <stdlib.h> | ||
12 | |||
13 | static const vec2 PAD_SIZE = (vec2){120, 20}; | ||
14 | static const R PLAYER_Y_OFFSET = 50; | ||
15 | static const R PLAYER_SPEED = 800; | ||
16 | |||
17 | static const R ENEMY_SPEED = 2; | ||
18 | |||
19 | static const R BALL_SIZE = 18; | ||
20 | static const R BALL_SPEED = 360; // In each dimension. | ||
21 | |||
22 | static const R EPS = (R)1e-3; | ||
23 | |||
24 | typedef struct Player { | ||
25 | vec2 position; | ||
26 | } Player; | ||
27 | |||
28 | typedef struct Ball { | ||
29 | vec2 position; | ||
30 | vec2 velocity; | ||
31 | } Ball; | ||
32 | |||
33 | typedef struct State { | ||
34 | bool game_started; | ||
35 | Player human; | ||
36 | Player enemy; | ||
37 | Ball ball; | ||
38 | mat4 viewProjection; | ||
39 | } State; | ||
40 | |||
41 | bool init(Game* game, State** pp_state) { | ||
42 | assert(game); | ||
43 | |||
44 | State* state = calloc(1, sizeof(State)); | ||
45 | if (!state) { | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | *pp_state = state; | ||
50 | return true; | ||
51 | |||
52 | cleanup: | ||
53 | free(state); | ||
54 | return false; | ||
55 | } | ||
56 | |||
57 | void shutdown(Game* game, State* state) { | ||
58 | assert(game); | ||
59 | assert(state); | ||
60 | } | ||
61 | |||
62 | static void move_ball(Ball* ball, R dt, int width, int height) { | ||
63 | assert(ball); | ||
64 | |||
65 | const R offset = BALL_SIZE / 2; | ||
66 | |||
67 | ball->position = vec2_add(ball->position, vec2_scale(ball->velocity, dt)); | ||
68 | |||
69 | // Right wall. | ||
70 | if (ball->position.x + offset > (R)width) { | ||
71 | ball->position.x = (R)width - offset - EPS; | ||
72 | ball->velocity.x = -ball->velocity.x; | ||
73 | } | ||
74 | // Left wall. | ||
75 | else if (ball->position.x - offset < 0) { | ||
76 | ball->position.x = offset + EPS; | ||
77 | ball->velocity.x = -ball->velocity.x; | ||
78 | } | ||
79 | // Top wall. | ||
80 | if (ball->position.y + offset > (R)height) { | ||
81 | ball->position.y = (R)height - offset - EPS; | ||
82 | ball->velocity.y = -ball->velocity.y; | ||
83 | } | ||
84 | // Bottom wall. | ||
85 | else if (ball->position.y - offset < 0) { | ||
86 | ball->position.y = offset + EPS; | ||
87 | ball->velocity.y = -ball->velocity.y; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | void move_enemy_player(int width, Player* player, R t) { | ||
92 | const R half_width = (R)width / 2; | ||
93 | const R amplitude = half_width - (PAD_SIZE.x / 2); | ||
94 | player->position.x = half_width + amplitude * sinf(t * ENEMY_SPEED); | ||
95 | } | ||
96 | |||
97 | void move_human_player(Player* player, R dt) { | ||
98 | assert(player); | ||
99 | |||
100 | R speed = 0; | ||
101 | if (gfx_app_is_key_pressed('a')) { | ||
102 | speed -= PLAYER_SPEED; | ||
103 | } | ||
104 | if (gfx_app_is_key_pressed('d')) { | ||
105 | speed += PLAYER_SPEED; | ||
106 | } | ||
107 | |||
108 | player->position.x += speed * dt; | ||
109 | } | ||
110 | |||
111 | void clamp_player(Player* player, int width) { | ||
112 | assert(player); | ||
113 | |||
114 | const R offset = PAD_SIZE.x / 2; | ||
115 | |||
116 | // Left wall. | ||
117 | if (player->position.x + offset > (R)width) { | ||
118 | player->position.x = (R)width - offset; | ||
119 | } | ||
120 | // Right wall. | ||
121 | else if (player->position.x - offset < 0) { | ||
122 | player->position.x = offset; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | void collide_ball(vec2 old_ball_position, const Player* player, Ball* ball) { | ||
127 | assert(player); | ||
128 | assert(ball); | ||
129 | |||
130 | // Discrete but simple collision. Checks for intersection and moves the ball | ||
131 | // back by a small epsilon. | ||
132 | |||
133 | // Player bounding box. | ||
134 | const vec2 player_pmin = vec2_make( | ||
135 | player->position.x - PAD_SIZE.x / 2, player->position.y - PAD_SIZE.y / 2); | ||
136 | const vec2 player_pmax = vec2_make( | ||
137 | player->position.x + PAD_SIZE.x / 2, player->position.y + PAD_SIZE.y / 2); | ||
138 | |||
139 | // Ball bounding box. | ||
140 | const vec2 ball_pmin = vec2_make( | ||
141 | ball->position.x - BALL_SIZE / 2, ball->position.y - BALL_SIZE / 2); | ||
142 | const vec2 ball_pmax = vec2_make( | ||
143 | ball->position.x + BALL_SIZE / 2, ball->position.y + BALL_SIZE / 2); | ||
144 | |||
145 | // Check for intersection and update ball. | ||
146 | if (!((ball_pmax.x < player_pmin.x) || (ball_pmin.x > player_pmax.x) || | ||
147 | (ball_pmax.y < player_pmin.y) || (ball_pmin.y > player_pmax.y))) { | ||
148 | ball->position = | ||
149 | vec2_add(old_ball_position, vec2_scale(ball->velocity, -EPS)); | ||
150 | ball->velocity.y = -ball->velocity.y; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | void update(Game* game, State* state, double t, double dt) { | ||
155 | assert(game); | ||
156 | assert(state); | ||
157 | |||
158 | // TODO: Move game width/height to GfxApp query functions? | ||
159 | const vec2 old_ball_position = state->ball.position; | ||
160 | move_ball(&state->ball, (R)dt, game->width, game->height); | ||
161 | move_human_player(&state->human, (R)dt); | ||
162 | move_enemy_player(game->width, &state->enemy, (R)t); | ||
163 | clamp_player(&state->human, game->width); | ||
164 | collide_ball(old_ball_position, &state->human, &state->ball); | ||
165 | collide_ball(old_ball_position, &state->enemy, &state->ball); | ||
166 | } | ||
167 | |||
168 | static void draw_player(ImmRenderer* imm, const Player* player) { | ||
169 | assert(imm); | ||
170 | assert(player); | ||
171 | |||
172 | const vec2 half_box = vec2_div(PAD_SIZE, vec2_make(2, 2)); | ||
173 | |||
174 | const vec2 pmin = vec2_sub(player->position, half_box); | ||
175 | const vec2 pmax = vec2_add(player->position, half_box); | ||
176 | const aabb2 box = aabb2_make(pmin, pmax); | ||
177 | |||
178 | gfx_imm_draw_aabb2(imm, box); | ||
179 | } | ||
180 | |||
181 | static void draw_ball(ImmRenderer* imm, const Ball* ball) { | ||
182 | assert(imm); | ||
183 | assert(ball); | ||
184 | |||
185 | const vec2 half_box = vec2_make(BALL_SIZE / 2, BALL_SIZE / 2); | ||
186 | const vec2 pmin = vec2_sub(ball->position, half_box); | ||
187 | const vec2 pmax = vec2_add(ball->position, half_box); | ||
188 | const aabb2 box = aabb2_make(pmin, pmax); | ||
189 | |||
190 | gfx_imm_draw_aabb2(imm, box); | ||
191 | } | ||
192 | |||
193 | void render(const Game* game, const State* state) { | ||
194 | assert(game); | ||
195 | assert(state); | ||
196 | |||
197 | ImmRenderer* imm = gfx_get_imm_renderer(game->gfx); | ||
198 | gfx_imm_start(imm); | ||
199 | gfx_imm_set_view_projection_matrix(imm, &state->viewProjection); | ||
200 | gfx_imm_load_identity(imm); | ||
201 | gfx_imm_set_colour(imm, vec4_make(1, 1, 1, 1)); | ||
202 | draw_player(imm, &state->human); | ||
203 | draw_player(imm, &state->enemy); | ||
204 | draw_ball(imm, &state->ball); | ||
205 | gfx_imm_end(imm); | ||
206 | } | ||
207 | |||
208 | static R clamp_to_width(int width, R x, R extent) { | ||
209 | return min(x, (R)width - extent); | ||
210 | } | ||
211 | |||
212 | void resize(Game* game, State* state, int width, int height) { | ||
213 | assert(game); | ||
214 | assert(state); | ||
215 | |||
216 | state->viewProjection = mat4_ortho(0, (R)width, 0, (R)height, -1, 1); | ||
217 | |||
218 | state->human.position.y = PLAYER_Y_OFFSET; | ||
219 | state->enemy.position.y = (R)height - PLAYER_Y_OFFSET; | ||
220 | |||
221 | if (!state->game_started) { | ||
222 | state->human.position.x = (R)width / 2; | ||
223 | state->enemy.position.x = (R)width / 2; | ||
224 | |||
225 | state->ball.position = | ||
226 | vec2_div(vec2_make((R)width, (R)height), vec2_make(2, 2)); | ||
227 | |||
228 | state->ball.velocity = vec2_make(BALL_SPEED, BALL_SPEED); | ||
229 | |||
230 | state->game_started = true; | ||
231 | } else { | ||
232 | state->human.position.x = | ||
233 | clamp_to_width(width, state->human.position.x, PAD_SIZE.x / 2); | ||
234 | state->enemy.position.x = | ||
235 | clamp_to_width(width, state->enemy.position.x, PAD_SIZE.x / 2); | ||
236 | } | ||
237 | } | ||
diff --git a/game/src/plugins/texture_view.c b/game/src/plugins/texture_view.c deleted file mode 100644 index a8b2a94..0000000 --- a/game/src/plugins/texture_view.c +++ /dev/null | |||
@@ -1,144 +0,0 @@ | |||
1 | #include "plugin.h" | ||
2 | |||
3 | #include <gfx/asset.h> | ||
4 | #include <gfx/core.h> | ||
5 | #include <gfx/renderer.h> | ||
6 | #include <gfx/scene.h> | ||
7 | #include <gfx/util/geometry.h> | ||
8 | #include <gfx/util/shader.h> | ||
9 | |||
10 | #include <math/camera.h> | ||
11 | |||
12 | #include <assert.h> | ||
13 | #include <stdlib.h> | ||
14 | |||
15 | // Default texture to load if no texture is provided. | ||
16 | static const char* DEFAULT_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp"; | ||
17 | // static const char* DEFAULT_TEXTURE = "/assets/checkerboard.jpg"; | ||
18 | |||
19 | struct State { | ||
20 | Scene* scene; | ||
21 | SceneCamera* camera; | ||
22 | }; | ||
23 | |||
24 | bool init(Game* game, State** pp_state) { | ||
25 | assert(game); | ||
26 | assert(pp_state); | ||
27 | |||
28 | State* state = calloc(1, sizeof(State)); | ||
29 | if (!state) { | ||
30 | goto cleanup; | ||
31 | } | ||
32 | |||
33 | // Usage: [texture file] | ||
34 | const char* texture_file = game->argc > 1 ? game->argv[1] : DEFAULT_TEXTURE; | ||
35 | |||
36 | GfxCore* gfxcore = gfx_get_core(game->gfx); | ||
37 | |||
38 | const Texture* texture = gfx_load_texture( | ||
39 | game->gfx, &(LoadTextureCmd){ | ||
40 | .origin = AssetFromFile, | ||
41 | .type = LoadTexture, | ||
42 | .filtering = LinearFiltering, | ||
43 | .mipmaps = false, | ||
44 | .data.texture.filepath = mstring_make(texture_file)}); | ||
45 | if (!texture) { | ||
46 | goto cleanup; | ||
47 | } | ||
48 | |||
49 | ShaderProgram* shader = gfx_make_view_texture_shader(gfxcore); | ||
50 | if (!shader) { | ||
51 | goto cleanup; | ||
52 | } | ||
53 | |||
54 | Geometry* geometry = gfx_make_quad_11(gfxcore); | ||
55 | if (!geometry) { | ||
56 | goto cleanup; | ||
57 | } | ||
58 | |||
59 | MaterialDesc material_desc = (MaterialDesc){.num_uniforms = 1}; | ||
60 | material_desc.uniforms[0] = (ShaderUniform){ | ||
61 | .type = UniformTexture, | ||
62 | .value.texture = texture, | ||
63 | .name = sstring_make("Texture")}; | ||
64 | Material* material = gfx_make_material(&material_desc); | ||
65 | if (!material) { | ||
66 | goto cleanup; | ||
67 | } | ||
68 | |||
69 | const MeshDesc mesh_desc = | ||
70 | (MeshDesc){.geometry = geometry, .material = material, .shader = shader}; | ||
71 | Mesh* mesh = gfx_make_mesh(&mesh_desc); | ||
72 | if (!mesh) { | ||
73 | goto cleanup; | ||
74 | } | ||
75 | |||
76 | SceneObject* object = | ||
77 | gfx_make_object(&(ObjectDesc){.num_meshes = 1, .meshes = {mesh}}); | ||
78 | if (!object) { | ||
79 | goto cleanup; | ||
80 | } | ||
81 | |||
82 | if (!(state->scene = gfx_make_scene())) { | ||
83 | goto cleanup; | ||
84 | } | ||
85 | |||
86 | SceneNode* node = gfx_make_object_node(object); | ||
87 | if (!node) { | ||
88 | goto cleanup; | ||
89 | } | ||
90 | SceneNode* root = gfx_get_scene_root(state->scene); | ||
91 | if (!root) { | ||
92 | goto cleanup; | ||
93 | } | ||
94 | gfx_set_node_parent(node, root); | ||
95 | |||
96 | if (!(state->camera = gfx_make_camera())) { | ||
97 | goto cleanup; | ||
98 | } | ||
99 | |||
100 | *pp_state = state; | ||
101 | return true; | ||
102 | |||
103 | cleanup: | ||
104 | shutdown(game, state); | ||
105 | if (state) { | ||
106 | free(state); | ||
107 | } | ||
108 | return false; | ||
109 | } | ||
110 | |||
111 | void shutdown(Game* game, State* state) { | ||
112 | assert(game); | ||
113 | if (state) { | ||
114 | gfx_destroy_camera(&state->camera); | ||
115 | gfx_destroy_scene(&state->scene); | ||
116 | // State freed by plugin engine. | ||
117 | } | ||
118 | } | ||
119 | |||
120 | void render(const Game* game, const State* state) { | ||
121 | assert(game); | ||
122 | assert(state); | ||
123 | |||
124 | Renderer* renderer = gfx_get_renderer(game->gfx); | ||
125 | gfx_render_scene( | ||
126 | renderer, &(RenderSceneParams){ | ||
127 | .mode = RenderDefault, | ||
128 | .scene = state->scene, | ||
129 | .camera = state->camera}); | ||
130 | } | ||
131 | |||
132 | void resize(Game* game, State* state, int width, int height) { | ||
133 | assert(game); | ||
134 | assert(state); | ||
135 | |||
136 | const R fovy = 90 * TO_RAD; | ||
137 | const R aspect = (R)width / (R)height; | ||
138 | const R near = 0.1; | ||
139 | const R far = 1000; | ||
140 | const mat4 projection = mat4_perspective(fovy, aspect, near, far); | ||
141 | |||
142 | Camera* camera = gfx_get_camera_camera(state->camera); | ||
143 | camera->projection = projection; | ||
144 | } | ||
diff --git a/game/src/plugins/viewer.c b/game/src/plugins/viewer.c deleted file mode 100644 index 5fc4be7..0000000 --- a/game/src/plugins/viewer.c +++ /dev/null | |||
@@ -1,366 +0,0 @@ | |||
1 | #include "plugin.h" | ||
2 | |||
3 | #include <gfx/app.h> | ||
4 | #include <gfx/asset.h> | ||
5 | #include <gfx/renderer.h> | ||
6 | #include <gfx/scene.h> | ||
7 | #include <gfx/util/skyquad.h> | ||
8 | #include <math/camera.h> | ||
9 | #include <math/spatial3.h> | ||
10 | |||
11 | #include <log/log.h> | ||
12 | |||
13 | #include <stdlib.h> | ||
14 | |||
15 | // Paths to various scene files. | ||
16 | static const char* BOX = "/assets/models/box.gltf"; | ||
17 | static const char* SUZANNE = "/assets/models/suzanne.gltf"; | ||
18 | static const char* SPONZA = | ||
19 | "/assets/glTF-Sample-Models/2.0/Sponza/glTF/Sponza.gltf"; | ||
20 | static const char* FLIGHT_HELMET = | ||
21 | "/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/FlightHelmet.gltf"; | ||
22 | static const char* DAMAGED_HELMET = | ||
23 | "/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf"; | ||
24 | static const char* GIRL = | ||
25 | "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf"; | ||
26 | static const char* BOXES = | ||
27 | "/home/jeanne/Nextcloud/assets/models/boxes/boxes.gltf"; | ||
28 | |||
29 | #define DEFAULT_SCENE_FILE GIRL | ||
30 | |||
31 | static const bool RenderBoundingBoxes = false; | ||
32 | static const R DefaultCameraSpeed = (R)6.0; | ||
33 | static const R DefaultMouseSensitivity = (R)(10 * TO_RAD); | ||
34 | static const vec3 DefaultCameraPosition = (vec3){0, 2, 5}; | ||
35 | |||
36 | typedef struct CameraCommand { | ||
37 | bool CameraMoveLeft : 1; | ||
38 | bool CameraMoveRight : 1; | ||
39 | bool CameraMoveForward : 1; | ||
40 | bool CameraMoveBackward : 1; | ||
41 | } CameraCommand; | ||
42 | |||
43 | typedef struct CameraController { | ||
44 | R camera_speed; // Camera movement speed. | ||
45 | R mouse_sensitivity; // Controls the degree with which mouse movements | ||
46 | // rotate the camera. | ||
47 | vec2 prev_mouse_position; // Mouse position in the previous frame. | ||
48 | bool rotating; // When true, subsequent mouse movements cause the | ||
49 | // camera to rotate. | ||
50 | } CameraController; | ||
51 | |||
52 | typedef struct State { | ||
53 | Scene* scene; | ||
54 | Model* model; | ||
55 | SceneCamera* camera; | ||
56 | CameraController camera_controller; | ||
57 | } State; | ||
58 | |||
59 | /// Load the skyquad texture. | ||
60 | static const Texture* load_environment_map(Gfx* gfx) { | ||
61 | assert(gfx); | ||
62 | return gfx_load_texture( | ||
63 | gfx, &(LoadTextureCmd){ | ||
64 | .origin = AssetFromFile, | ||
65 | .type = LoadCubemap, | ||
66 | .colour_space = sRGB, | ||
67 | .filtering = NearestFiltering, | ||
68 | .mipmaps = false, | ||
69 | .data.cubemap.filepaths = { | ||
70 | mstring_make("/assets/skybox/clouds1/clouds1_east.bmp"), | ||
71 | mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"), | ||
72 | mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"), | ||
73 | mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"), | ||
74 | mstring_make("/assets/skybox/clouds1/clouds1_south.bmp"), | ||
75 | mstring_make("/assets/skybox/clouds1/clouds1_north.bmp")} | ||
76 | }); | ||
77 | } | ||
78 | |||
79 | /// Load the skyquad and return the environment light node. | ||
80 | static SceneNode* load_skyquad(Gfx* gfx, SceneNode* root) { | ||
81 | assert(gfx); | ||
82 | assert(root); | ||
83 | |||
84 | GfxCore* gfxcore = gfx_get_core(gfx); | ||
85 | |||
86 | const Texture* environment_map = load_environment_map(gfx); | ||
87 | if (!environment_map) { | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | return gfx_setup_skyquad(gfxcore, root, environment_map); | ||
92 | } | ||
93 | |||
94 | /// Load the 3D scene. | ||
95 | /// Return the loaded model. | ||
96 | static Model* load_scene(Game* game, State* state, const char* scene_filepath) { | ||
97 | assert(game); | ||
98 | assert(game->gfx); | ||
99 | assert(state); | ||
100 | assert(state->scene); | ||
101 | |||
102 | Camera* camera = gfx_get_camera_camera(state->camera); | ||
103 | spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2)); | ||
104 | |||
105 | SceneNode* root = gfx_get_scene_root(state->scene); | ||
106 | SceneNode* sky_light_node = load_skyquad(game->gfx, root); | ||
107 | if (!sky_light_node) { | ||
108 | return 0; // test | ||
109 | } | ||
110 | |||
111 | Model* model = gfx_load_model( | ||
112 | game->gfx, | ||
113 | &(LoadModelCmd){ | ||
114 | .origin = AssetFromFile, .filepath = mstring_make(scene_filepath)}); | ||
115 | if (!model) { | ||
116 | return 0; | ||
117 | } | ||
118 | SceneNode* model_node = gfx_make_model_node(model); | ||
119 | if (!model_node) { | ||
120 | return 0; | ||
121 | } | ||
122 | gfx_set_node_parent(model_node, sky_light_node); | ||
123 | |||
124 | gfx_log_node_hierarchy(root); | ||
125 | |||
126 | return model; | ||
127 | } | ||
128 | |||
129 | bool init(Game* game, State** pp_state) { | ||
130 | assert(game); | ||
131 | |||
132 | // Usage: <scene file> | ||
133 | const char* scene_filepath = | ||
134 | game->argc > 1 ? game->argv[1] : DEFAULT_SCENE_FILE; | ||
135 | |||
136 | State* state = calloc(1, sizeof(State)); | ||
137 | if (!state) { | ||
138 | goto cleanup; | ||
139 | } | ||
140 | |||
141 | if (!(state->scene = gfx_make_scene())) { | ||
142 | goto cleanup; | ||
143 | } | ||
144 | if (!(state->camera = gfx_make_camera())) { | ||
145 | goto cleanup; | ||
146 | } | ||
147 | |||
148 | state->model = load_scene(game, state, scene_filepath); | ||
149 | if (!state->model) { | ||
150 | goto cleanup; | ||
151 | } | ||
152 | |||
153 | Anima* anima = gfx_get_model_anima(state->model); | ||
154 | if (anima) { | ||
155 | gfx_play_animation( | ||
156 | anima, &(AnimationPlaySettings){.name = "Walk", .loop = true}); | ||
157 | // TODO: Interpolate animations. | ||
158 | /*gfx_play_animation( | ||
159 | anima, | ||
160 | &(AnimationPlaySettings){.name = "Jumping-jack-lower", .loop = true}); | ||
161 | gfx_play_animation( | ||
162 | anima, &(AnimationPlaySettings){ | ||
163 | .name = "Jumping-jack-arms-mid", .loop = true});*/ | ||
164 | } | ||
165 | |||
166 | spatial3_set_position( | ||
167 | &gfx_get_camera_camera(state->camera)->spatial, DefaultCameraPosition); | ||
168 | |||
169 | state->camera_controller.camera_speed = DefaultCameraSpeed; | ||
170 | state->camera_controller.mouse_sensitivity = DefaultMouseSensitivity; | ||
171 | |||
172 | *pp_state = state; | ||
173 | return true; | ||
174 | |||
175 | cleanup: | ||
176 | shutdown(game, state); | ||
177 | if (state) { | ||
178 | free(state); | ||
179 | } | ||
180 | return false; | ||
181 | } | ||
182 | |||
183 | void shutdown(Game* game, State* state) { | ||
184 | assert(game); | ||
185 | if (state) { | ||
186 | gfx_destroy_camera(&state->camera); | ||
187 | gfx_destroy_scene(&state->scene); | ||
188 | // State freed by plugin engine. | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static void update_camera( | ||
193 | CameraController* controller, R dt, vec2 mouse_position, | ||
194 | CameraCommand command, Spatial3* camera) { | ||
195 | assert(controller); | ||
196 | assert(camera); | ||
197 | |||
198 | // Translation. | ||
199 | const R move_x = (R)(command.CameraMoveLeft ? -1 : 0) + | ||
200 | (R)(command.CameraMoveRight ? 1 : 0); | ||
201 | const R move_y = (R)(command.CameraMoveForward ? 1 : 0) + | ||
202 | (R)(command.CameraMoveBackward ? -1 : 0); | ||
203 | const vec2 translation = | ||
204 | vec2_scale(vec2_make(move_x, move_y), controller->camera_speed * dt); | ||
205 | spatial3_move_right(camera, translation.x); | ||
206 | spatial3_move_forwards(camera, translation.y); | ||
207 | |||
208 | // Rotation. | ||
209 | if (controller->rotating) { | ||
210 | const vec2 mouse_delta = | ||
211 | vec2_sub(mouse_position, controller->prev_mouse_position); | ||
212 | |||
213 | const vec2 rotation = | ||
214 | vec2_scale(mouse_delta, controller->mouse_sensitivity * dt); | ||
215 | |||
216 | spatial3_global_yaw(camera, -rotation.x); | ||
217 | spatial3_pitch(camera, -rotation.y); | ||
218 | } | ||
219 | |||
220 | // Update controller state. | ||
221 | controller->prev_mouse_position = mouse_position; | ||
222 | } | ||
223 | |||
224 | void update(Game* game, State* state, double t, double dt) { | ||
225 | assert(game); | ||
226 | assert(state); | ||
227 | assert(state->scene); | ||
228 | assert(state->camera); | ||
229 | |||
230 | double mouse_x, mouse_y; | ||
231 | gfx_app_get_mouse_position(&mouse_x, &mouse_y); | ||
232 | const vec2 mouse_position = {(R)mouse_x, (R)mouse_y}; | ||
233 | |||
234 | const CameraCommand camera_command = (CameraCommand){ | ||
235 | .CameraMoveLeft = gfx_app_is_key_pressed(KeyA), | ||
236 | .CameraMoveRight = gfx_app_is_key_pressed(KeyD), | ||
237 | .CameraMoveForward = gfx_app_is_key_pressed(KeyW), | ||
238 | .CameraMoveBackward = gfx_app_is_key_pressed(KeyS), | ||
239 | }; | ||
240 | |||
241 | state->camera_controller.rotating = gfx_app_is_mouse_button_pressed(LMB); | ||
242 | |||
243 | update_camera( | ||
244 | &state->camera_controller, (R)dt, mouse_position, camera_command, | ||
245 | &gfx_get_camera_camera(state->camera)->spatial); | ||
246 | |||
247 | // const vec3 orbit_point = vec3_make(0, 2, 0); | ||
248 | // Camera* camera = gfx_get_camera_camera(state->camera); | ||
249 | // spatial3_orbit( | ||
250 | // &camera->spatial, orbit_point, | ||
251 | // /*radius=*/5, | ||
252 | // /*azimuth=*/(R)(t * 0.5), /*zenith=*/0); | ||
253 | // spatial3_lookat(&camera->spatial, orbit_point); | ||
254 | |||
255 | gfx_update(state->scene, state->camera, (R)t); | ||
256 | } | ||
257 | |||
258 | /// Render the bounding boxes of all scene objects. | ||
259 | static void render_bounding_boxes_rec( | ||
260 | ImmRenderer* imm, const Anima* anima, const mat4* parent_model_matrix, | ||
261 | const SceneNode* node) { | ||
262 | assert(imm); | ||
263 | assert(node); | ||
264 | |||
265 | const mat4 model_matrix = | ||
266 | mat4_mul(*parent_model_matrix, gfx_get_node_transform(node)); | ||
267 | |||
268 | const NodeType node_type = gfx_get_node_type(node); | ||
269 | |||
270 | if (node_type == ModelNode) { | ||
271 | const Model* model = gfx_get_node_model(node); | ||
272 | const SceneNode* root = gfx_get_model_root(model); | ||
273 | render_bounding_boxes_rec(imm, anima, &model_matrix, root); | ||
274 | } else if (node_type == AnimaNode) { | ||
275 | anima = gfx_get_node_anima(node); | ||
276 | } else if (node_type == ObjectNode) { | ||
277 | gfx_imm_set_model_matrix(imm, &model_matrix); | ||
278 | |||
279 | const SceneObject* obj = gfx_get_node_object(node); | ||
280 | const Skeleton* skeleton = gfx_get_object_skeleton(obj); | ||
281 | |||
282 | if (skeleton) { // Animated model. | ||
283 | assert(anima); | ||
284 | const size_t num_joints = gfx_get_skeleton_num_joints(skeleton); | ||
285 | for (size_t i = 0; i < num_joints; ++i) { | ||
286 | if (gfx_joint_has_box(anima, skeleton, i)) { | ||
287 | const Box box = gfx_get_joint_box(anima, skeleton, i); | ||
288 | gfx_imm_draw_box3(imm, box.vertices); | ||
289 | } | ||
290 | } | ||
291 | } else { // Static model. | ||
292 | const aabb3 box = gfx_get_object_aabb(obj); | ||
293 | gfx_imm_draw_aabb3(imm, box); | ||
294 | } | ||
295 | } | ||
296 | |||
297 | // Render children's boxes. | ||
298 | const SceneNode* child = gfx_get_node_child(node); | ||
299 | while (child) { | ||
300 | render_bounding_boxes_rec(imm, anima, &model_matrix, child); | ||
301 | child = gfx_get_node_sibling(child); | ||
302 | } | ||
303 | } | ||
304 | |||
305 | /// Render the bounding boxes of all scene objects. | ||
306 | static void render_bounding_boxes(const Game* game, const State* state) { | ||
307 | assert(game); | ||
308 | assert(state); | ||
309 | |||
310 | GfxCore* gfxcore = gfx_get_core(game->gfx); | ||
311 | ImmRenderer* imm = gfx_get_imm_renderer(game->gfx); | ||
312 | assert(gfxcore); | ||
313 | assert(imm); | ||
314 | |||
315 | const mat4 id = mat4_id(); | ||
316 | Anima* anima = 0; | ||
317 | |||
318 | gfx_set_blending(gfxcore, true); | ||
319 | gfx_set_depth_mask(gfxcore, false); | ||
320 | gfx_set_polygon_offset(gfxcore, -1.5f, -1.0f); | ||
321 | |||
322 | gfx_imm_start(imm); | ||
323 | gfx_imm_set_camera(imm, gfx_get_camera_camera(state->camera)); | ||
324 | gfx_imm_set_colour(imm, vec4_make(0.3, 0.3, 0.9, 0.1)); | ||
325 | render_bounding_boxes_rec(imm, anima, &id, gfx_get_scene_root(state->scene)); | ||
326 | gfx_imm_end(imm); | ||
327 | |||
328 | gfx_reset_polygon_offset(gfxcore); | ||
329 | gfx_set_depth_mask(gfxcore, true); | ||
330 | gfx_set_blending(gfxcore, false); | ||
331 | } | ||
332 | |||
333 | void render(const Game* game, const State* state) { | ||
334 | assert(state); | ||
335 | assert(game); | ||
336 | assert(game->gfx); | ||
337 | assert(state->scene); | ||
338 | assert(state->camera); | ||
339 | |||
340 | Renderer* renderer = gfx_get_renderer(game->gfx); | ||
341 | assert(renderer); | ||
342 | |||
343 | gfx_render_scene( | ||
344 | renderer, &(RenderSceneParams){ | ||
345 | .mode = RenderDefault, | ||
346 | .scene = state->scene, | ||
347 | .camera = state->camera}); | ||
348 | |||
349 | if (RenderBoundingBoxes) { | ||
350 | render_bounding_boxes(game, state); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | void resize(Game* game, State* state, int width, int height) { | ||
355 | assert(game); | ||
356 | assert(state); | ||
357 | |||
358 | const R fovy = 60 * TO_RAD; | ||
359 | const R aspect = (R)width / (R)height; | ||
360 | const R near = 0.1; | ||
361 | const R far = 1000; | ||
362 | const mat4 projection = mat4_perspective(fovy, aspect, near, far); | ||
363 | |||
364 | Camera* camera = gfx_get_camera_camera(state->camera); | ||
365 | camera->projection = projection; | ||
366 | } | ||
diff --git a/gfx-iso/CMakeLists.txt b/gfx-iso/CMakeLists.txt deleted file mode 100644 index e4a677d..0000000 --- a/gfx-iso/CMakeLists.txt +++ /dev/null | |||
@@ -1,42 +0,0 @@ | |||
1 | cmake_minimum_required(VERSION 3.0) | ||
2 | |||
3 | project(isogfx) | ||
4 | |||
5 | set(CMAKE_C_STANDARD 17) | ||
6 | set(CMAKE_C_STANDARD_REQUIRED On) | ||
7 | set(CMAKE_C_EXTENSIONS Off) | ||
8 | |||
9 | # isogfx | ||
10 | |||
11 | add_library(isogfx | ||
12 | src/isogfx.c) | ||
13 | |||
14 | target_include_directories(isogfx PUBLIC | ||
15 | include) | ||
16 | |||
17 | target_link_libraries(isogfx PUBLIC | ||
18 | filesystem | ||
19 | mem | ||
20 | mempool) | ||
21 | |||
22 | target_compile_options(isogfx PRIVATE -Wall -Wextra -Wpedantic) | ||
23 | |||
24 | # Backend | ||
25 | |||
26 | add_library(isogfx-backend | ||
27 | src/backend.c) | ||
28 | |||
29 | target_include_directories(isogfx-backend PUBLIC | ||
30 | include) | ||
31 | |||
32 | target_link_libraries(isogfx-backend PUBLIC | ||
33 | isogfx) | ||
34 | |||
35 | target_link_libraries(isogfx-backend PRIVATE | ||
36 | gfx) | ||
37 | |||
38 | target_compile_options(isogfx-backend PRIVATE -Wall -Wextra -Wpedantic) | ||
39 | |||
40 | # Demos | ||
41 | |||
42 | add_subdirectory(demos) | ||
diff --git a/gfx-iso/demos/CMakeLists.txt b/gfx-iso/demos/CMakeLists.txt deleted file mode 100644 index c0a4101..0000000 --- a/gfx-iso/demos/CMakeLists.txt +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | add_subdirectory(checkerboard) | ||
2 | add_subdirectory(isomap) | ||
diff --git a/gfx-iso/demos/checkerboard/CMakeLists.txt b/gfx-iso/demos/checkerboard/CMakeLists.txt deleted file mode 100644 index d1691c6..0000000 --- a/gfx-iso/demos/checkerboard/CMakeLists.txt +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | cmake_minimum_required(VERSION 3.0) | ||
2 | |||
3 | project(checkerboard) | ||
4 | |||
5 | set(CMAKE_C_STANDARD 17) | ||
6 | set(CMAKE_C_STANDARD_REQUIRED On) | ||
7 | set(CMAKE_C_EXTENSIONS Off) | ||
8 | |||
9 | add_executable(checkerboard | ||
10 | checkerboard.c) | ||
11 | |||
12 | target_link_libraries(checkerboard PRIVATE | ||
13 | gfx-app | ||
14 | isogfx-backend) | ||
15 | |||
16 | target_compile_options(checkerboard PRIVATE -Wall -Wextra -Wpedantic) | ||
diff --git a/gfx-iso/demos/checkerboard/checkerboard.c b/gfx-iso/demos/checkerboard/checkerboard.c deleted file mode 100644 index dbc817c..0000000 --- a/gfx-iso/demos/checkerboard/checkerboard.c +++ /dev/null | |||
@@ -1,166 +0,0 @@ | |||
1 | #include <isogfx/backend.h> | ||
2 | #include <isogfx/isogfx.h> | ||
3 | |||
4 | #include <gfx/app.h> | ||
5 | |||
6 | #include <assert.h> | ||
7 | #include <stdbool.h> | ||
8 | #include <stdio.h> | ||
9 | |||
10 | static const int WINDOW_WIDTH = 1408; | ||
11 | static const int WINDOW_HEIGHT = 960; | ||
12 | static const int MAX_FPS = 60; | ||
13 | |||
14 | // Virtual screen dimensions. | ||
15 | static const int SCREEN_WIDTH = 704; | ||
16 | static const int SCREEN_HEIGHT = 480; | ||
17 | |||
18 | static const int TILE_WIDTH = 32; | ||
19 | static const int TILE_HEIGHT = TILE_WIDTH / 2; | ||
20 | static const int WORLD_WIDTH = 20; | ||
21 | static const int WORLD_HEIGHT = 20; | ||
22 | |||
23 | static const TileDesc tile_set[] = { | ||
24 | {.type = TileFromColour, | ||
25 | .width = TILE_WIDTH, | ||
26 | .height = TILE_HEIGHT, | ||
27 | .colour = (Pixel){.r = 0x38, .g = 0x3b, .b = 0x46, .a = 0xff}}, | ||
28 | {.type = TileFromColour, | ||
29 | .width = TILE_WIDTH, | ||
30 | .height = TILE_HEIGHT, | ||
31 | .colour = (Pixel){.r = 0xA5, .g = 0xb3, .b = 0xc0, .a = 0xff}}, | ||
32 | {.type = TileFromColour, | ||
33 | .width = TILE_WIDTH, | ||
34 | .height = TILE_HEIGHT, | ||
35 | .colour = (Pixel){.r = 0xdc, .g = 0x76, .b = 0x84, .a = 0xff}}, | ||
36 | }; | ||
37 | |||
38 | typedef enum Colour { | ||
39 | Black, | ||
40 | White, | ||
41 | Red, | ||
42 | } Colour; | ||
43 | |||
44 | typedef struct GfxAppState { | ||
45 | IsoBackend* backend; | ||
46 | IsoGfx* iso; | ||
47 | Tile red; | ||
48 | int xpick; | ||
49 | int ypick; | ||
50 | } GfxAppState; | ||
51 | |||
52 | static void make_checkerboard(IsoGfx* iso, Tile black, Tile white) { | ||
53 | assert(iso); | ||
54 | for (int y = 0; y < isogfx_world_height(iso); ++y) { | ||
55 | for (int x = 0; x < isogfx_world_width(iso); ++x) { | ||
56 | const int odd_col = x & 1; | ||
57 | const int odd_row = y & 1; | ||
58 | const Tile value = (odd_row ^ odd_col) == 0 ? black : white; | ||
59 | isogfx_set_tile(iso, x, y, value); | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | static bool init(GfxAppState* state, int argc, const char** argv) { | ||
65 | assert(state); | ||
66 | |||
67 | (void)argc; | ||
68 | (void)argv; | ||
69 | |||
70 | if (!(state->iso = isogfx_new(&(IsoGfxDesc){ | ||
71 | .screen_width = SCREEN_WIDTH, .screen_height = SCREEN_HEIGHT}))) { | ||
72 | return false; | ||
73 | } | ||
74 | IsoGfx* iso = state->iso; | ||
75 | |||
76 | isogfx_resize(iso, SCREEN_WIDTH, SCREEN_HEIGHT); | ||
77 | |||
78 | if (!isogfx_make_world( | ||
79 | iso, &(WorldDesc){ | ||
80 | .tile_width = TILE_WIDTH, | ||
81 | .tile_height = TILE_HEIGHT, | ||
82 | .world_width = WORLD_WIDTH, | ||
83 | .world_height = WORLD_HEIGHT})) { | ||
84 | return false; | ||
85 | } | ||
86 | |||
87 | const Tile black = isogfx_make_tile(iso, &tile_set[Black]); | ||
88 | const Tile white = isogfx_make_tile(iso, &tile_set[White]); | ||
89 | state->red = isogfx_make_tile(iso, &tile_set[Red]); | ||
90 | make_checkerboard(iso, black, white); | ||
91 | |||
92 | if (!(state->backend = IsoBackendInit(iso))) { | ||
93 | return false; | ||
94 | } | ||
95 | |||
96 | return true; | ||
97 | } | ||
98 | |||
99 | static void shutdown(GfxAppState* state) { | ||
100 | assert(state); | ||
101 | |||
102 | IsoBackendShutdown(&state->backend); | ||
103 | isogfx_del(&state->iso); | ||
104 | } | ||
105 | |||
106 | static void update(GfxAppState* state, double t, double dt) { | ||
107 | assert(state); | ||
108 | (void)dt; | ||
109 | |||
110 | IsoGfx* iso = state->iso; | ||
111 | |||
112 | isogfx_update(iso, t); | ||
113 | |||
114 | // Get mouse position in window coordinates. | ||
115 | double mouse_x, mouse_y; | ||
116 | gfx_app_get_mouse_position(&mouse_x, &mouse_y); | ||
117 | |||
118 | // Map from window coordinates to virtual screen coordinates. | ||
119 | IsoBackendGetMousePosition( | ||
120 | state->backend, mouse_x, mouse_y, &mouse_x, &mouse_y); | ||
121 | |||
122 | isogfx_pick_tile(iso, mouse_x, mouse_y, &state->xpick, &state->ypick); | ||
123 | |||
124 | printf("Picked tile: (%d, %d)\n", state->xpick, state->ypick); | ||
125 | } | ||
126 | |||
127 | static void render(GfxAppState* state) { | ||
128 | assert(state); | ||
129 | |||
130 | IsoGfx* iso = state->iso; | ||
131 | |||
132 | isogfx_render(iso); | ||
133 | |||
134 | if ((state->xpick != -1) && (state->ypick != -1)) { | ||
135 | isogfx_draw_tile(iso, state->xpick, state->ypick, state->red); | ||
136 | } | ||
137 | |||
138 | IsoBackendRender(state->backend, iso); | ||
139 | } | ||
140 | |||
141 | static void resize(GfxAppState* state, int width, int height) { | ||
142 | assert(state); | ||
143 | |||
144 | IsoBackendResizeWindow(state->backend, state->iso, width, height); | ||
145 | } | ||
146 | |||
147 | int main(int argc, const char** argv) { | ||
148 | GfxAppState state = {0}; | ||
149 | gfx_app_run( | ||
150 | &(GfxAppDesc){ | ||
151 | .argc = argc, | ||
152 | .argv = argv, | ||
153 | .width = WINDOW_WIDTH, | ||
154 | .height = WINDOW_HEIGHT, | ||
155 | .max_fps = MAX_FPS, | ||
156 | .update_delta_time = MAX_FPS > 0 ? 1.0 / (double)MAX_FPS : 0.0, | ||
157 | .title = "Isometric Renderer", | ||
158 | .app_state = &state}, | ||
159 | &(GfxAppCallbacks){ | ||
160 | .init = init, | ||
161 | .update = update, | ||
162 | .render = render, | ||
163 | .resize = resize, | ||
164 | .shutdown = shutdown}); | ||
165 | return 0; | ||
166 | } | ||
diff --git a/gfx-iso/demos/isomap/CMakeLists.txt b/gfx-iso/demos/isomap/CMakeLists.txt deleted file mode 100644 index 2dbfd32..0000000 --- a/gfx-iso/demos/isomap/CMakeLists.txt +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | cmake_minimum_required(VERSION 3.0) | ||
2 | |||
3 | project(isomap) | ||
4 | |||
5 | set(CMAKE_C_STANDARD 17) | ||
6 | set(CMAKE_C_STANDARD_REQUIRED On) | ||
7 | set(CMAKE_C_EXTENSIONS Off) | ||
8 | |||
9 | add_executable(isomap | ||
10 | isomap.c) | ||
11 | |||
12 | target_link_libraries(isomap PRIVATE | ||
13 | gfx-app | ||
14 | isogfx-backend) | ||
15 | |||
16 | target_compile_options(isomap PRIVATE -Wall -Wextra -Wpedantic) | ||
diff --git a/gfx-iso/demos/isomap/isomap.c b/gfx-iso/demos/isomap/isomap.c deleted file mode 100644 index a233659..0000000 --- a/gfx-iso/demos/isomap/isomap.c +++ /dev/null | |||
@@ -1,105 +0,0 @@ | |||
1 | #include <isogfx/backend.h> | ||
2 | #include <isogfx/isogfx.h> | ||
3 | |||
4 | #include <gfx/app.h> | ||
5 | |||
6 | #include <assert.h> | ||
7 | #include <stdbool.h> | ||
8 | |||
9 | static const int WINDOW_WIDTH = 1408; | ||
10 | static const int WINDOW_HEIGHT = 960; | ||
11 | static const int MAX_FPS = 60; | ||
12 | |||
13 | // Virtual screen dimensions. | ||
14 | static const int SCREEN_WIDTH = 704; | ||
15 | static const int SCREEN_HEIGHT = 480; | ||
16 | |||
17 | typedef struct GfxAppState { | ||
18 | IsoBackend* backend; | ||
19 | IsoGfx* iso; | ||
20 | int xpick; | ||
21 | int ypick; | ||
22 | SpriteSheet stag_sheet; | ||
23 | Sprite stag; | ||
24 | } GfxAppState; | ||
25 | |||
26 | static bool init(GfxAppState* state, int argc, const char** argv) { | ||
27 | assert(state); | ||
28 | (void)argc; | ||
29 | (void)argv; | ||
30 | |||
31 | if (!(state->iso = isogfx_new(&(IsoGfxDesc){ | ||
32 | .screen_width = SCREEN_WIDTH, .screen_height = SCREEN_HEIGHT}))) { | ||
33 | return false; | ||
34 | } | ||
35 | IsoGfx* iso = state->iso; | ||
36 | |||
37 | isogfx_resize(iso, SCREEN_WIDTH, SCREEN_HEIGHT); | ||
38 | |||
39 | if (!isogfx_load_world(iso, "/home/jeanne/assets/tilemaps/demo1.tm")) { | ||
40 | return false; | ||
41 | } | ||
42 | |||
43 | if (!isogfx_load_sprite_sheet( | ||
44 | iso, "/home/jeanne/assets/tilesets/scrabling/critters/stag/stag.ss", | ||
45 | &state->stag_sheet)) { | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | state->stag = isogfx_make_sprite(iso, state->stag_sheet); | ||
50 | isogfx_set_sprite_position(iso, state->stag, 5, 4); | ||
51 | |||
52 | if (!(state->backend = IsoBackendInit(iso))) { | ||
53 | return false; | ||
54 | } | ||
55 | |||
56 | return true; | ||
57 | } | ||
58 | |||
59 | static void shutdown(GfxAppState* state) { | ||
60 | assert(state); | ||
61 | // | ||
62 | } | ||
63 | |||
64 | static void update(GfxAppState* state, double t, double dt) { | ||
65 | assert(state); | ||
66 | (void)dt; | ||
67 | |||
68 | IsoGfx* iso = state->iso; | ||
69 | isogfx_update(iso, t); | ||
70 | } | ||
71 | |||
72 | static void render(GfxAppState* state) { | ||
73 | assert(state); | ||
74 | |||
75 | IsoGfx* iso = state->iso; | ||
76 | isogfx_render(iso); | ||
77 | IsoBackendRender(state->backend, iso); | ||
78 | } | ||
79 | |||
80 | static void resize(GfxAppState* state, int width, int height) { | ||
81 | assert(state); | ||
82 | |||
83 | IsoBackendResizeWindow(state->backend, state->iso, width, height); | ||
84 | } | ||
85 | |||
86 | int main(int argc, const char** argv) { | ||
87 | GfxAppState state = {0}; | ||
88 | gfx_app_run( | ||
89 | &(GfxAppDesc){ | ||
90 | .argc = argc, | ||
91 | .argv = argv, | ||
92 | .width = WINDOW_WIDTH, | ||
93 | .height = WINDOW_HEIGHT, | ||
94 | .max_fps = MAX_FPS, | ||
95 | .update_delta_time = MAX_FPS > 0 ? 1.0 / (double)MAX_FPS : 0.0, | ||
96 | .title = "Isometric Renderer", | ||
97 | .app_state = &state}, | ||
98 | &(GfxAppCallbacks){ | ||
99 | .init = init, | ||
100 | .update = update, | ||
101 | .render = render, | ||
102 | .resize = resize, | ||
103 | .shutdown = shutdown}); | ||
104 | return 0; | ||
105 | } | ||
diff --git a/gfx-iso/include/isogfx/backend.h b/gfx-iso/include/isogfx/backend.h deleted file mode 100644 index 172991d..0000000 --- a/gfx-iso/include/isogfx/backend.h +++ /dev/null | |||
@@ -1,28 +0,0 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <stdbool.h> | ||
4 | |||
5 | typedef struct Gfx Gfx; | ||
6 | typedef struct IsoGfx IsoGfx; | ||
7 | |||
8 | typedef struct IsoBackend IsoBackend; | ||
9 | |||
10 | /// Initialize the backend. | ||
11 | IsoBackend* IsoBackendInit(const IsoGfx*); | ||
12 | |||
13 | /// Shut down the backend. | ||
14 | void IsoBackendShutdown(IsoBackend**); | ||
15 | |||
16 | /// Notify the backend of a window resize event. | ||
17 | /// This allows the backend to determine how to position and scale the iso | ||
18 | /// screen buffer on the graphics window. | ||
19 | void IsoBackendResizeWindow(IsoBackend*, const IsoGfx*, int width, int height); | ||
20 | |||
21 | /// Render the iso screen to the graphics window. | ||
22 | void IsoBackendRender(const IsoBackend*, const IsoGfx*); | ||
23 | |||
24 | /// Map window coordinates to iso space coordinates. | ||
25 | /// This takes into account any possible resizing done by the backend in | ||
26 | /// response to calls to IsoBackendResizeWindow(). | ||
27 | bool IsoBackendGetMousePosition( | ||
28 | const IsoBackend*, double window_x, double window_y, double* x, double* y); | ||
diff --git a/gfx-iso/include/isogfx/isogfx.h b/gfx-iso/include/isogfx/isogfx.h deleted file mode 100644 index 3421a7b..0000000 --- a/gfx-iso/include/isogfx/isogfx.h +++ /dev/null | |||
@@ -1,136 +0,0 @@ | |||
1 | /* | ||
2 | * Isometric rendering engine. | ||
3 | */ | ||
4 | #pragma once | ||
5 | |||
6 | #include <stdbool.h> | ||
7 | #include <stdint.h> | ||
8 | |||
9 | typedef struct IsoGfx IsoGfx; | ||
10 | |||
11 | /// Sprite sheet handle. | ||
12 | typedef uint16_t SpriteSheet; | ||
13 | |||
14 | /// Sprite handle. | ||
15 | typedef uint16_t Sprite; | ||
16 | |||
17 | /// Tile handle. | ||
18 | typedef uint16_t Tile; | ||
19 | |||
20 | /// Colour channel. | ||
21 | typedef uint8_t Channel; | ||
22 | |||
23 | typedef struct Pixel { | ||
24 | Channel r, g, b, a; | ||
25 | } Pixel; | ||
26 | |||
27 | typedef enum TileDescType { | ||
28 | TileFromColour, | ||
29 | TileFromFile, | ||
30 | TileFromMemory, | ||
31 | } TileDescType; | ||
32 | |||
33 | typedef struct TileDesc { | ||
34 | TileDescType type; | ||
35 | int width; /// Tile width in pixels. | ||
36 | int height; /// Tile height in pixels. | ||
37 | union { | ||
38 | Pixel colour; /// Constant colour tile. | ||
39 | struct { | ||
40 | const char* path; | ||
41 | } file; | ||
42 | struct { | ||
43 | const uint8_t* data; /// sizeof(Pixel) * width * height | ||
44 | } mem; | ||
45 | }; | ||
46 | } TileDesc; | ||
47 | |||
48 | typedef struct WorldDesc { | ||
49 | int tile_width; /// Base tile width in pixels. | ||
50 | int tile_height; /// Base tile height in pixels. | ||
51 | int world_width; /// World width in tiles. | ||
52 | int world_height; /// World height in tiles. | ||
53 | int max_num_tiles; /// 0 for an implementation-defined default. | ||
54 | } WorldDesc; | ||
55 | |||
56 | typedef struct IsoGfxDesc { | ||
57 | int screen_width; /// Screen width in pixels. | ||
58 | int screen_height; /// Screen height in pixels. | ||
59 | int max_num_sprites; /// 0 for an implementation-defined default. | ||
60 | int sprite_sheet_pool_size_bytes; /// 0 for an implementation-defined default. | ||
61 | } IsoGfxDesc; | ||
62 | |||
63 | /// Create a new isometric graphics engine. | ||
64 | IsoGfx* isogfx_new(const IsoGfxDesc*); | ||
65 | |||
66 | /// Destroy the isometric graphics engine. | ||
67 | void isogfx_del(IsoGfx**); | ||
68 | |||
69 | /// Create an empty world. | ||
70 | bool isogfx_make_world(IsoGfx*, const WorldDesc*); | ||
71 | |||
72 | /// Load a world from a tile map (.TM) file. | ||
73 | bool isogfx_load_world(IsoGfx*, const char* filepath); | ||
74 | |||
75 | /// Return the world's width. | ||
76 | int isogfx_world_width(const IsoGfx*); | ||
77 | |||
78 | /// Return the world's height. | ||
79 | int isogfx_world_height(const IsoGfx*); | ||
80 | |||
81 | /// Create a new tile. | ||
82 | Tile isogfx_make_tile(IsoGfx*, const TileDesc*); | ||
83 | |||
84 | /// Set the tile at position (x,y). | ||
85 | void isogfx_set_tile(IsoGfx*, int x, int y, Tile); | ||
86 | |||
87 | /// Set the tiles in positions in the range (x0,y0) - (x1,y1). | ||
88 | void isogfx_set_tiles(IsoGfx*, int x0, int y0, int x1, int y1, Tile); | ||
89 | |||
90 | /// Load a sprite sheet (.SS) file. | ||
91 | bool isogfx_load_sprite_sheet(IsoGfx*, const char* filepath, SpriteSheet*); | ||
92 | |||
93 | /// Create an animated sprite. | ||
94 | Sprite isogfx_make_sprite(IsoGfx*, SpriteSheet); | ||
95 | |||
96 | /// Destroy the sprite. | ||
97 | void isogfx_del_sprite(IsoGfx*, Sprite); | ||
98 | |||
99 | /// Destroy all the sprites. | ||
100 | void isogfx_del_sprites(IsoGfx*); | ||
101 | |||
102 | /// Set the sprite's position. | ||
103 | void isogfx_set_sprite_position(IsoGfx*, Sprite, int x, int y); | ||
104 | |||
105 | /// Set the sprite's current animation. | ||
106 | void isogfx_set_sprite_animation(IsoGfx*, Sprite, int animation); | ||
107 | |||
108 | /// Update the renderer. | ||
109 | /// | ||
110 | /// Currently this updates the sprite animations. | ||
111 | void isogfx_update(IsoGfx*, double t); | ||
112 | |||
113 | /// Render the world. | ||
114 | void isogfx_render(IsoGfx*); | ||
115 | |||
116 | /// Draw/overlay a tile at position (x,y). | ||
117 | /// | ||
118 | /// This function just renders a tile at position (x,y) and should be called | ||
119 | /// after isogfx_render() to obtain the correct result. To set the tile at | ||
120 | /// position (x,y) instead, use isogfx_set_tile(). | ||
121 | void isogfx_draw_tile(IsoGfx*, int x, int y, Tile); | ||
122 | |||
123 | /// Resize the virtual screen's dimensions. | ||
124 | bool isogfx_resize(IsoGfx*, int screen_width, int screen_height); | ||
125 | |||
126 | /// Get the virtual screen's dimensions. | ||
127 | void isogfx_get_screen_size(const IsoGfx*, int* width, int* height); | ||
128 | |||
129 | /// Return a pointer to the virtual screen's colour buffer. | ||
130 | /// | ||
131 | /// Call after each call to isogfx_render() to retrieve the render output. | ||
132 | const Pixel* isogfx_get_screen_buffer(const IsoGfx*); | ||
133 | |||
134 | /// Translate Cartesian to isometric coordinates. | ||
135 | void isogfx_pick_tile( | ||
136 | const IsoGfx*, double xcart, double ycart, int* xiso, int* yiso); | ||
diff --git a/gfx-iso/src/backend.c b/gfx-iso/src/backend.c deleted file mode 100644 index db91647..0000000 --- a/gfx-iso/src/backend.c +++ /dev/null | |||
@@ -1,199 +0,0 @@ | |||
1 | #include <isogfx/backend.h> | ||
2 | #include <isogfx/isogfx.h> | ||
3 | |||
4 | #include <gfx/core.h> | ||
5 | #include <gfx/gfx.h> | ||
6 | #include <gfx/renderer.h> | ||
7 | #include <gfx/scene.h> | ||
8 | #include <gfx/util/geometry.h> | ||
9 | #include <gfx/util/shader.h> | ||
10 | |||
11 | #include <assert.h> | ||
12 | #include <stdlib.h> | ||
13 | |||
14 | typedef struct IsoBackend { | ||
15 | Gfx* gfx; | ||
16 | Scene* scene; | ||
17 | /// The screen or "iso screen" refers to the colour buffer of the iso graphics | ||
18 | /// library. This texture is used to draw the iso screen onto the graphics | ||
19 | /// window. | ||
20 | Texture* screen_texture; | ||
21 | /// Window size. | ||
22 | int window_width; | ||
23 | int window_height; | ||
24 | /// The viewport refers to the area inside the window to which screen_texture | ||
25 | /// is drawn. It is a scaled version of the iso screen, scaled while | ||
26 | /// respecting the iso screen's aspect ratio to prevent distortion. | ||
27 | int viewport_x, viewport_y, viewport_width, viewport_height; | ||
28 | double stretch; // Stretch factor from iso screen dimensions to viewport | ||
29 | // dimensions. | ||
30 | } IsoBackend; | ||
31 | |||
32 | IsoBackend* IsoBackendInit(const IsoGfx* iso) { | ||
33 | assert(iso); | ||
34 | |||
35 | IsoBackend* backend = calloc(1, sizeof(IsoBackend)); | ||
36 | if (!backend) { | ||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | if (!(backend->gfx = gfx_init())) { | ||
41 | goto cleanup; | ||
42 | } | ||
43 | GfxCore* gfxcore = gfx_get_core(backend->gfx); | ||
44 | |||
45 | int screen_width, screen_height; | ||
46 | isogfx_get_screen_size(iso, &screen_width, &screen_height); | ||
47 | |||
48 | if (!(backend->screen_texture = gfx_make_texture( | ||
49 | gfxcore, &(TextureDesc){ | ||
50 | .width = screen_width, | ||
51 | .height = screen_height, | ||
52 | .dimension = Texture2D, | ||
53 | .format = TextureSRGBA8, | ||
54 | .filtering = NearestFiltering, | ||
55 | .wrap = ClampToEdge, | ||
56 | .mipmaps = false}))) { | ||
57 | goto cleanup; | ||
58 | } | ||
59 | |||
60 | ShaderProgram* shader = gfx_make_view_texture_shader(gfxcore); | ||
61 | if (!shader) { | ||
62 | goto cleanup; | ||
63 | } | ||
64 | |||
65 | Geometry* geometry = gfx_make_quad_11(gfxcore); | ||
66 | if (!geometry) { | ||
67 | goto cleanup; | ||
68 | } | ||
69 | |||
70 | MaterialDesc material_desc = (MaterialDesc){.num_uniforms = 1}; | ||
71 | material_desc.uniforms[0] = (ShaderUniform){ | ||
72 | .type = UniformTexture, | ||
73 | .value.texture = backend->screen_texture, | ||
74 | .name = sstring_make("Texture")}; | ||
75 | Material* material = gfx_make_material(&material_desc); | ||
76 | if (!material) { | ||
77 | return false; | ||
78 | } | ||
79 | |||
80 | const MeshDesc mesh_desc = | ||
81 | (MeshDesc){.geometry = geometry, .material = material, .shader = shader}; | ||
82 | Mesh* mesh = gfx_make_mesh(&mesh_desc); | ||
83 | if (!mesh) { | ||
84 | goto cleanup; | ||
85 | } | ||
86 | |||
87 | SceneObject* object = | ||
88 | gfx_make_object(&(ObjectDesc){.num_meshes = 1, .meshes = {mesh}}); | ||
89 | if (!object) { | ||
90 | goto cleanup; | ||
91 | } | ||
92 | |||
93 | backend->scene = gfx_make_scene(); | ||
94 | SceneNode* node = gfx_make_object_node(object); | ||
95 | SceneNode* root = gfx_get_scene_root(backend->scene); | ||
96 | gfx_set_node_parent(node, root); | ||
97 | |||
98 | return backend; | ||
99 | |||
100 | cleanup: | ||
101 | if (backend->gfx) { | ||
102 | gfx_destroy(&backend->gfx); | ||
103 | } | ||
104 | free(backend); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | void IsoBackendShutdown(IsoBackend** ppApp) { | ||
109 | assert(ppApp); | ||
110 | |||
111 | IsoBackend* app = *ppApp; | ||
112 | if (!app) { | ||
113 | return; | ||
114 | } | ||
115 | |||
116 | gfx_destroy(&app->gfx); | ||
117 | } | ||
118 | |||
119 | void IsoBackendResizeWindow( | ||
120 | IsoBackend* app, const IsoGfx* iso, int width, int height) { | ||
121 | assert(app); | ||
122 | assert(iso); | ||
123 | |||
124 | app->window_width = width; | ||
125 | app->window_height = height; | ||
126 | |||
127 | // Virtual screen dimensions. | ||
128 | int screen_width, screen_height; | ||
129 | isogfx_get_screen_size(iso, &screen_width, &screen_height); | ||
130 | |||
131 | // Stretch the virtual screen onto the viewport while respecting the screen's | ||
132 | // aspect ratio to prevent distortion. | ||
133 | if (width > height) { // Wide screen. | ||
134 | app->stretch = (double)height / (double)screen_height; | ||
135 | app->viewport_width = (int)((double)screen_width * app->stretch); | ||
136 | app->viewport_height = height; | ||
137 | app->viewport_x = (width - app->viewport_width) / 2; | ||
138 | app->viewport_y = 0; | ||
139 | } else { // Tall screen. | ||
140 | app->stretch = (double)width / (double)screen_width; | ||
141 | app->viewport_width = width; | ||
142 | app->viewport_height = (int)((float)screen_height * app->stretch); | ||
143 | app->viewport_x = 0; | ||
144 | app->viewport_y = (height - app->viewport_height) / 2; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | void IsoBackendRender(const IsoBackend* app, const IsoGfx* iso) { | ||
149 | assert(app); | ||
150 | assert(iso); | ||
151 | |||
152 | const Pixel* screen = isogfx_get_screen_buffer(iso); | ||
153 | assert(screen); | ||
154 | gfx_update_texture(app->screen_texture, &(TextureDataDesc){.pixels = screen}); | ||
155 | |||
156 | GfxCore* gfxcore = gfx_get_core(app->gfx); | ||
157 | Renderer* renderer = gfx_get_renderer(app->gfx); | ||
158 | |||
159 | // Clear the whole window. | ||
160 | gfx_set_viewport(gfxcore, 0, 0, app->window_width, app->window_height); | ||
161 | gfx_clear(gfxcore, vec4_make(0, 0, 0, 0)); | ||
162 | |||
163 | // Draw to the subregion where the virtual screen can stretch without | ||
164 | // distortion. | ||
165 | gfx_set_viewport( | ||
166 | gfxcore, app->viewport_x, app->viewport_y, app->viewport_width, | ||
167 | app->viewport_height); | ||
168 | |||
169 | // Render the iso screen. | ||
170 | gfx_start_frame(gfxcore); | ||
171 | gfx_render_scene( | ||
172 | renderer, &(RenderSceneParams){ | ||
173 | .mode = RenderDefault, .scene = app->scene, .camera = 0}); | ||
174 | gfx_end_frame(gfxcore); | ||
175 | } | ||
176 | |||
177 | bool IsoBackendGetMousePosition( | ||
178 | const IsoBackend* app, double window_x, double window_y, double* x, | ||
179 | double* y) { | ||
180 | assert(app); | ||
181 | |||
182 | // Translate from window coordinates to the subregion where the stretched | ||
183 | // iso screen is rendered. | ||
184 | const double screen_x = window_x - app->viewport_x; | ||
185 | const double screen_y = window_y - app->viewport_y; | ||
186 | |||
187 | // Position may be out of bounds. | ||
188 | if ((0 <= screen_x) && (screen_x < app->viewport_width) && (0 <= screen_y) && | ||
189 | (screen_y < app->viewport_height)) { | ||
190 | // Scale back from the stretched subregion to the iso screen dimensions. | ||
191 | *x = screen_x / app->stretch; | ||
192 | *y = screen_y / app->stretch; | ||
193 | return true; | ||
194 | } else { | ||
195 | *x = -1; | ||
196 | *y = -1; | ||
197 | return false; | ||
198 | } | ||
199 | } | ||
diff --git a/gfx-iso/src/isogfx.c b/gfx-iso/src/isogfx.c deleted file mode 100644 index 52c4ae2..0000000 --- a/gfx-iso/src/isogfx.c +++ /dev/null | |||
@@ -1,952 +0,0 @@ | |||
1 | #include <isogfx/isogfx.h> | ||
2 | |||
3 | #include <filesystem.h> | ||
4 | #include <mem.h> | ||
5 | #include <mempool.h> | ||
6 | #include <path.h> | ||
7 | |||
8 | #include <linux/limits.h> | ||
9 | |||
10 | #include <assert.h> | ||
11 | #include <stdbool.h> | ||
12 | #include <stdint.h> | ||
13 | #include <stdio.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <string.h> | ||
16 | |||
17 | /// Maximum number of tiles unless the user specifies a value. | ||
18 | #define DEFAULT_MAX_NUM_TILES 1024 | ||
19 | |||
20 | /// Maximum number of sprites unless the user specifies a value. | ||
21 | #define DEFAULT_MAX_NUM_SPRITES 128 | ||
22 | |||
23 | /// Size of sprite sheet pool in bytes unless the user specifies a value. | ||
24 | #define DEFAULT_SPRITE_SHEET_POOL_SIZE_BYTES (8 * 1024 * 1024) | ||
25 | |||
26 | /// Default animation speed. | ||
27 | #define ANIMATION_FPS 10 | ||
28 | |||
29 | /// Time between animation updates. | ||
30 | #define ANIMATION_UPDATE_DELTA (1.0 / ANIMATION_FPS) | ||
31 | |||
32 | typedef struct ivec2 { | ||
33 | int x, y; | ||
34 | } ivec2; | ||
35 | |||
36 | typedef struct vec2 { | ||
37 | double x, y; | ||
38 | } vec2; | ||
39 | |||
40 | // ----------------------------------------------------------------------------- | ||
41 | // Tile set (TS) and tile map (TM) file formats. | ||
42 | // ----------------------------------------------------------------------------- | ||
43 | |||
44 | /// Maximum length of path strings in .TS and .TM files. | ||
45 | #define MAX_PATH_LENGTH 128 | ||
46 | |||
47 | typedef struct Ts_Tile { | ||
48 | uint16_t width; /// Tile width in pixels. | ||
49 | uint16_t height; /// Tile height in pixels. | ||
50 | Pixel pixels[1]; /// Count: width * height. | ||
51 | } Ts_Tile; | ||
52 | |||
53 | typedef struct Ts_TileSet { | ||
54 | uint16_t num_tiles; | ||
55 | uint16_t max_tile_width; /// Maximum tile width in pixels. | ||
56 | uint16_t max_tile_height; /// Maximum tile height in pixels. | ||
57 | Ts_Tile tiles[1]; /// Count: num_tiles. | ||
58 | } Ts_TileSet; | ||
59 | |||
60 | typedef struct Tm_Layer { | ||
61 | union { | ||
62 | char tileset_path[MAX_PATH_LENGTH]; // Relative to the Tm_Map file. | ||
63 | }; | ||
64 | Tile tiles[1]; /// Count: world_width * world_height. | ||
65 | } Tm_Layer; | ||
66 | |||
67 | typedef struct Tm_Map { | ||
68 | uint16_t world_width; /// World width in number of tiles. | ||
69 | uint16_t world_height; /// World height in number of tiles. | ||
70 | uint16_t base_tile_width; | ||
71 | uint16_t base_tile_height; | ||
72 | uint16_t num_layers; | ||
73 | Tm_Layer layers[1]; // Count: num_layers. | ||
74 | } Tm_Map; | ||
75 | |||
76 | static inline const Tm_Layer* tm_map_get_next_layer( | ||
77 | const Tm_Map* map, const Tm_Layer* layer) { | ||
78 | assert(map); | ||
79 | assert(layer); | ||
80 | return (const Tm_Layer*)((const uint8_t*)layer + sizeof(Tm_Layer) + | ||
81 | ((map->world_width * map->world_height - 1) * | ||
82 | sizeof(Tile))); | ||
83 | } | ||
84 | |||
85 | static inline const Ts_Tile* ts_tileset_get_next_tile( | ||
86 | const Ts_TileSet* tileset, const Ts_Tile* tile) { | ||
87 | assert(tileset); | ||
88 | assert(tile); | ||
89 | return (const Ts_Tile*)((const uint8_t*)tile + sizeof(Ts_Tile) + | ||
90 | ((tile->width * tile->height - 1) * sizeof(Pixel))); | ||
91 | } | ||
92 | |||
93 | // ----------------------------------------------------------------------------- | ||
94 | // Sprite sheet file format. | ||
95 | // ----------------------------------------------------------------------------- | ||
96 | |||
97 | /// A row of sprites in a sprite sheet. | ||
98 | /// | ||
99 | /// Each row in a sprite sheet can have a different number of columns. | ||
100 | /// | ||
101 | /// The pixels of the row follow a "sprite-major" order. It contains the | ||
102 | /// 'sprite_width * sprite_height' pixels for the first column/sprite, then the | ||
103 | /// second column/sprite, etc. | ||
104 | /// | ||
105 | /// Pixels are 8-bit indices into the sprite sheet's colour palette. | ||
106 | typedef struct Ss_Row { | ||
107 | uint16_t num_cols; /// Number of columns in this row. | ||
108 | uint8_t pixels[1]; /// Count: num_cols * sprite_width * sprite_height. | ||
109 | } Ss_Row; | ||
110 | |||
111 | typedef struct Ss_Palette { | ||
112 | uint16_t num_colours; | ||
113 | Pixel colours[1]; /// Count: num_colors. | ||
114 | } Ss_Palette; | ||
115 | |||
116 | /// Sprite sheet top-level data definition. | ||
117 | /// | ||
118 | /// Sprite width and height are assumed constant throughout the sprite sheet. | ||
119 | typedef struct Ss_SpriteSheet { | ||
120 | uint16_t sprite_width; /// Sprite width in pixels. | ||
121 | uint16_t sprite_height; /// Sprite height in pixels. | ||
122 | uint16_t num_rows; | ||
123 | Ss_Palette palette; /// Variable size. | ||
124 | Ss_Row rows[1]; /// Count: num_rows. Variable offset. | ||
125 | } Ss_SpriteSheet; | ||
126 | |||
127 | static inline const Ss_Row* get_sprite_sheet_row( | ||
128 | const Ss_SpriteSheet* sheet, int row) { | ||
129 | assert(sheet); | ||
130 | assert(row >= 0); | ||
131 | assert(row < sheet->num_rows); | ||
132 | // Skip over the palette. | ||
133 | const Ss_Row* rows = | ||
134 | (const Ss_Row*)(&sheet->palette.colours[0] + sheet->palette.num_colours); | ||
135 | return &rows[row]; | ||
136 | } | ||
137 | |||
138 | static inline const uint8_t* get_sprite_sheet_sprite( | ||
139 | const Ss_SpriteSheet* sheet, const Ss_Row* row, int col) { | ||
140 | assert(sheet); | ||
141 | assert(row); | ||
142 | assert(col >= 0); | ||
143 | assert(col < row->num_cols); | ||
144 | const int sprite_offset = col * sheet->sprite_width * sheet->sprite_height; | ||
145 | const uint8_t* sprite = &row->pixels[sprite_offset]; | ||
146 | return sprite; | ||
147 | } | ||
148 | |||
149 | // ----------------------------------------------------------------------------- | ||
150 | // Renderer state. | ||
151 | // ----------------------------------------------------------------------------- | ||
152 | |||
153 | typedef struct TileData { | ||
154 | uint16_t width; | ||
155 | uint16_t height; | ||
156 | uint16_t pixels_handle; // Handle to the tile's pixels in the pixel pool. | ||
157 | } TileData; | ||
158 | |||
159 | // File format is already convenient for working in memory. | ||
160 | typedef Ss_Row SpriteSheetRow; | ||
161 | typedef Ss_SpriteSheet SpriteSheetData; | ||
162 | |||
163 | typedef struct SpriteData { | ||
164 | SpriteSheet sheet; // Handle to the sprite's sheet. | ||
165 | ivec2 position; | ||
166 | int animation; // Current animation. | ||
167 | int frame; // Current frame of animation. | ||
168 | } SpriteData; | ||
169 | |||
170 | DEF_MEMPOOL_DYN(TilePool, TileData) | ||
171 | DEF_MEM_DYN(PixelPool, Pixel) | ||
172 | |||
173 | DEF_MEMPOOL_DYN(SpritePool, SpriteData) | ||
174 | DEF_MEM_DYN(SpriteSheetPool, SpriteSheetData) | ||
175 | |||
176 | typedef struct IsoGfx { | ||
177 | int screen_width; | ||
178 | int screen_height; | ||
179 | int tile_width; | ||
180 | int tile_height; | ||
181 | int world_width; | ||
182 | int world_height; | ||
183 | int max_num_sprites; | ||
184 | int sprite_sheet_pool_size_bytes; | ||
185 | double last_animation_time; | ||
186 | Tile* world; | ||
187 | Pixel* screen; | ||
188 | TilePool tiles; | ||
189 | PixelPool pixels; | ||
190 | SpritePool sprites; | ||
191 | SpriteSheetPool sheets; | ||
192 | } IsoGfx; | ||
193 | |||
194 | // ----------------------------------------------------------------------------- | ||
195 | // Math and world / tile / screen access. | ||
196 | // ----------------------------------------------------------------------------- | ||
197 | |||
198 | static inline ivec2 ivec2_add(ivec2 a, ivec2 b) { | ||
199 | return (ivec2){.x = a.x + b.x, .y = a.y + b.y}; | ||
200 | } | ||
201 | |||
202 | static inline ivec2 ivec2_scale(ivec2 a, int s) { | ||
203 | return (ivec2){.x = a.x * s, .y = a.y * s}; | ||
204 | } | ||
205 | |||
206 | static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) { | ||
207 | return (ivec2){ | ||
208 | .x = (iso.x - iso.y) * (s / 2) + (w / 2), .y = (iso.x + iso.y) * (t / 2)}; | ||
209 | } | ||
210 | |||
211 | // Method 1. | ||
212 | // static inline vec2 cart2iso(vec2 cart, int s, int t, int w) { | ||
213 | // const double x = cart.x - (double)(w / 2); | ||
214 | // const double xiso = (x * t + cart.y * s) / (double)(s * t); | ||
215 | // return (vec2){ | ||
216 | // .x = (int)(xiso), .y = (int)((2.0 / (double)t) * cart.y - xiso)}; | ||
217 | //} | ||
218 | |||
219 | // Method 2. | ||
220 | static inline vec2 cart2iso(vec2 cart, int s, int t, int w) { | ||
221 | const double one_over_s = 1. / (double)s; | ||
222 | const double one_over_t = 1. / (double)t; | ||
223 | const double x = cart.x - (double)(w / 2); | ||
224 | return (vec2){ | ||
225 | .x = (one_over_s * x + one_over_t * cart.y), | ||
226 | .y = (-one_over_s * x + one_over_t * cart.y)}; | ||
227 | } | ||
228 | |||
229 | static const Pixel* tile_xy_const_ref( | ||
230 | const IsoGfx* iso, const TileData* tile, int x, int y) { | ||
231 | assert(iso); | ||
232 | assert(tile); | ||
233 | assert(x >= 0); | ||
234 | assert(y >= 0); | ||
235 | assert(x < tile->width); | ||
236 | assert(y < tile->height); | ||
237 | return &mem_get_chunk(&iso->pixels, tile->pixels_handle)[y * tile->width + x]; | ||
238 | } | ||
239 | |||
240 | // static Pixel tile_xy(const IsoGfx* iso, const TileData* tile, int x, int y) { | ||
241 | // return *tile_xy_const_ref(iso, tile, x, y); | ||
242 | // } | ||
243 | |||
244 | static Pixel* tile_xy_mut(const IsoGfx* iso, TileData* tile, int x, int y) { | ||
245 | return (Pixel*)tile_xy_const_ref(iso, tile, x, y); | ||
246 | } | ||
247 | |||
248 | static inline const Tile* world_xy_const_ref(const IsoGfx* iso, int x, int y) { | ||
249 | assert(iso); | ||
250 | assert(x >= 0); | ||
251 | assert(y >= 0); | ||
252 | assert(x < iso->world_width); | ||
253 | assert(y < iso->world_height); | ||
254 | return &iso->world[y * iso->world_width + x]; | ||
255 | } | ||
256 | |||
257 | static inline Tile world_xy(const IsoGfx* iso, int x, int y) { | ||
258 | return *world_xy_const_ref(iso, x, y); | ||
259 | } | ||
260 | |||
261 | static inline Tile* world_xy_mut(IsoGfx* iso, int x, int y) { | ||
262 | return (Tile*)world_xy_const_ref(iso, x, y); | ||
263 | } | ||
264 | |||
265 | static inline const Pixel* screen_xy_const_ref( | ||
266 | const IsoGfx* iso, int x, int y) { | ||
267 | assert(iso); | ||
268 | assert(x >= 0); | ||
269 | assert(y >= 0); | ||
270 | assert(x < iso->screen_width); | ||
271 | assert(y < iso->screen_height); | ||
272 | return &iso->screen[y * iso->screen_width + x]; | ||
273 | } | ||
274 | |||
275 | static inline Pixel screen_xy(IsoGfx* iso, int x, int y) { | ||
276 | return *screen_xy_const_ref(iso, x, y); | ||
277 | } | ||
278 | |||
279 | static inline Pixel* screen_xy_mut(IsoGfx* iso, int x, int y) { | ||
280 | return (Pixel*)screen_xy_const_ref(iso, x, y); | ||
281 | } | ||
282 | |||
283 | static int calc_num_tile_blocks( | ||
284 | int base_tile_width, int base_tile_height, int tile_width, | ||
285 | int tile_height) { | ||
286 | const int base_tile_size = base_tile_width * base_tile_height; | ||
287 | const int tile_size = tile_width * tile_height; | ||
288 | const int num_blocks = tile_size / base_tile_size; | ||
289 | return num_blocks; | ||
290 | } | ||
291 | |||
292 | // ----------------------------------------------------------------------------- | ||
293 | // Renderer, world and tile management. | ||
294 | // ----------------------------------------------------------------------------- | ||
295 | |||
296 | IsoGfx* isogfx_new(const IsoGfxDesc* desc) { | ||
297 | assert(desc->screen_width > 0); | ||
298 | assert(desc->screen_height > 0); | ||
299 | // Part of our implementation assumes even widths and heights for precision. | ||
300 | assert((desc->screen_width & 1) == 0); | ||
301 | assert((desc->screen_height & 1) == 0); | ||
302 | |||
303 | IsoGfx* iso = calloc(1, sizeof(IsoGfx)); | ||
304 | if (!iso) { | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | iso->screen_width = desc->screen_width; | ||
309 | iso->screen_height = desc->screen_height; | ||
310 | |||
311 | iso->last_animation_time = 0.0; | ||
312 | |||
313 | iso->max_num_sprites = desc->max_num_sprites == 0 ? DEFAULT_MAX_NUM_SPRITES | ||
314 | : desc->max_num_sprites; | ||
315 | iso->sprite_sheet_pool_size_bytes = desc->sprite_sheet_pool_size_bytes == 0 | ||
316 | ? DEFAULT_SPRITE_SHEET_POOL_SIZE_BYTES | ||
317 | : desc->sprite_sheet_pool_size_bytes; | ||
318 | |||
319 | const int screen_size = desc->screen_width * desc->screen_height; | ||
320 | if (!(iso->screen = calloc(screen_size, sizeof(Pixel)))) { | ||
321 | goto cleanup; | ||
322 | } | ||
323 | |||
324 | return iso; | ||
325 | |||
326 | cleanup: | ||
327 | isogfx_del(&iso); | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | /// Destroy the world, its tile set, and the underlying pools. | ||
332 | static void destroy_world(IsoGfx* iso) { | ||
333 | assert(iso); | ||
334 | if (iso->world) { | ||
335 | free(iso->world); | ||
336 | iso->world = 0; | ||
337 | } | ||
338 | mempool_del(&iso->tiles); | ||
339 | mem_del(&iso->pixels); | ||
340 | } | ||
341 | |||
342 | /// Destroy all loaded sprites and the underlying pools. | ||
343 | static void destroy_sprites(IsoGfx* iso) { | ||
344 | assert(iso); | ||
345 | mempool_del(&iso->sprites); | ||
346 | mem_del(&iso->sheets); | ||
347 | } | ||
348 | |||
349 | void isogfx_del(IsoGfx** pIso) { | ||
350 | assert(pIso); | ||
351 | IsoGfx* iso = *pIso; | ||
352 | if (iso) { | ||
353 | destroy_world(iso); | ||
354 | destroy_sprites(iso); | ||
355 | if (iso->screen) { | ||
356 | free(iso->screen); | ||
357 | iso->screen = 0; | ||
358 | } | ||
359 | free(iso); | ||
360 | *pIso = 0; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | bool isogfx_make_world(IsoGfx* iso, const WorldDesc* desc) { | ||
365 | assert(iso); | ||
366 | assert(desc); | ||
367 | assert(desc->tile_width > 0); | ||
368 | assert(desc->tile_height > 0); | ||
369 | // Part of our implementation assumes even widths and heights for greater | ||
370 | // precision. | ||
371 | assert((desc->tile_width & 1) == 0); | ||
372 | assert((desc->tile_height & 1) == 0); | ||
373 | |||
374 | // Handle recreation by destroying the previous world. | ||
375 | destroy_world(iso); | ||
376 | |||
377 | iso->tile_width = desc->tile_width; | ||
378 | iso->tile_height = desc->tile_height; | ||
379 | iso->world_width = desc->world_width; | ||
380 | iso->world_height = desc->world_height; | ||
381 | |||
382 | const int world_size = desc->world_width * desc->world_height; | ||
383 | const int tile_size = desc->tile_width * desc->tile_height; | ||
384 | const int tile_size_bytes = tile_size * (int)sizeof(Pixel); | ||
385 | const int tile_pool_size = | ||
386 | desc->max_num_tiles > 0 ? desc->max_num_tiles : DEFAULT_MAX_NUM_TILES; | ||
387 | |||
388 | if (!(iso->world = calloc(world_size, sizeof(Tile)))) { | ||
389 | goto cleanup; | ||
390 | } | ||
391 | if (!mempool_make_dyn(&iso->tiles, world_size, sizeof(TileData))) { | ||
392 | goto cleanup; | ||
393 | } | ||
394 | if (!mem_make_dyn(&iso->pixels, tile_pool_size, tile_size_bytes)) { | ||
395 | goto cleanup; | ||
396 | } | ||
397 | |||
398 | return true; | ||
399 | |||
400 | cleanup: | ||
401 | destroy_world(iso); | ||
402 | return false; | ||
403 | } | ||
404 | |||
405 | bool isogfx_load_world(IsoGfx* iso, const char* filepath) { | ||
406 | assert(iso); | ||
407 | assert(filepath); | ||
408 | |||
409 | bool success = false; | ||
410 | |||
411 | // Handle recreation by destroying the previous world. | ||
412 | destroy_world(iso); | ||
413 | |||
414 | // Load the map. | ||
415 | printf("Load tile map: %s\n", filepath); | ||
416 | Tm_Map* map = read_file(filepath); | ||
417 | if (!map) { | ||
418 | goto cleanup; | ||
419 | } | ||
420 | |||
421 | // Allocate memory for the map and tile sets. | ||
422 | const int world_size = map->world_width * map->world_height; | ||
423 | const int base_tile_size = map->base_tile_width * map->base_tile_height; | ||
424 | const int base_tile_size_bytes = base_tile_size * (int)sizeof(Pixel); | ||
425 | // TODO: Need to get the total number of tiles from the map. | ||
426 | const int tile_pool_size = DEFAULT_MAX_NUM_TILES; | ||
427 | |||
428 | if (!(iso->world = calloc(world_size, sizeof(Tile)))) { | ||
429 | goto cleanup; | ||
430 | } | ||
431 | if (!mempool_make_dyn(&iso->tiles, tile_pool_size, sizeof(TileData))) { | ||
432 | goto cleanup; | ||
433 | } | ||
434 | if (!mem_make_dyn(&iso->pixels, tile_pool_size, base_tile_size_bytes)) { | ||
435 | goto cleanup; | ||
436 | } | ||
437 | |||
438 | // Load the tile sets. | ||
439 | const Tm_Layer* layer = &map->layers[0]; | ||
440 | // TODO: Handle num_layers layers. | ||
441 | for (int i = 0; i < 1; ++i) { | ||
442 | const char* ts_path = layer->tileset_path; | ||
443 | |||
444 | // Tile set path is relative to the tile map file. Make it relative to the | ||
445 | // current working directory before loading. | ||
446 | char ts_path_cwd[PATH_MAX] = {0}; | ||
447 | if (!path_make_relative(filepath, ts_path, ts_path_cwd, PATH_MAX)) { | ||
448 | goto cleanup; | ||
449 | } | ||
450 | |||
451 | Ts_TileSet* tileset = read_file(ts_path_cwd); | ||
452 | if (!tileset) { | ||
453 | goto cleanup; | ||
454 | }; | ||
455 | |||
456 | // Load tile data. | ||
457 | const Ts_Tile* tile = &tileset->tiles[0]; | ||
458 | for (uint16_t j = 0; j < tileset->num_tiles; ++j) { | ||
459 | // Tile dimensions should be a multiple of the base tile size. | ||
460 | assert((tile->width % map->base_tile_width) == 0); | ||
461 | assert((tile->height % map->base_tile_height) == 0); | ||
462 | |||
463 | // Allocate N base tile size blocks for the tile. | ||
464 | const uint16_t tile_size = tile->width * tile->height; | ||
465 | const int num_blocks = tile_size / base_tile_size; | ||
466 | Pixel* pixels = mem_alloc(&iso->pixels, num_blocks); | ||
467 | assert(pixels); | ||
468 | memcpy(pixels, tile->pixels, tile_size * sizeof(Pixel)); | ||
469 | |||
470 | // Allocate the tile data. | ||
471 | TileData* tile_data = mempool_alloc(&iso->tiles); | ||
472 | assert(tile_data); | ||
473 | tile_data->width = tile->width; | ||
474 | tile_data->height = tile->height; | ||
475 | tile_data->pixels_handle = | ||
476 | (uint16_t)mem_get_chunk_handle(&iso->pixels, pixels); | ||
477 | |||
478 | tile = ts_tileset_get_next_tile(tileset, tile); | ||
479 | } | ||
480 | |||
481 | printf("Loaded tile set (%u tiles): %s\n", tileset->num_tiles, ts_path_cwd); | ||
482 | |||
483 | free(tileset); | ||
484 | layer = tm_map_get_next_layer(map, layer); | ||
485 | } | ||
486 | |||
487 | // Load the map into the world. | ||
488 | layer = &map->layers[0]; | ||
489 | // TODO: Handle num_layers layers. | ||
490 | for (int i = 0; i < 1; ++i) { | ||
491 | memcpy(iso->world, layer->tiles, world_size * sizeof(Tile)); | ||
492 | |||
493 | // TODO: We need to handle 'firsgid' in TMX files. | ||
494 | for (int j = 0; j < world_size; ++j) { | ||
495 | iso->world[j] -= 1; | ||
496 | } | ||
497 | |||
498 | layer = tm_map_get_next_layer(map, layer); | ||
499 | } | ||
500 | |||
501 | iso->world_width = map->world_width; | ||
502 | iso->world_height = map->world_height; | ||
503 | iso->tile_width = map->base_tile_width; | ||
504 | iso->tile_height = map->base_tile_height; | ||
505 | |||
506 | success = true; | ||
507 | |||
508 | cleanup: | ||
509 | if (map) { | ||
510 | free(map); | ||
511 | } | ||
512 | if (!success) { | ||
513 | destroy_world(iso); | ||
514 | } | ||
515 | return success; | ||
516 | } | ||
517 | |||
518 | int isogfx_world_width(const IsoGfx* iso) { | ||
519 | assert(iso); | ||
520 | return iso->world_width; | ||
521 | } | ||
522 | |||
523 | int isogfx_world_height(const IsoGfx* iso) { | ||
524 | assert(iso); | ||
525 | return iso->world_height; | ||
526 | } | ||
527 | |||
528 | /// Create a tile mask procedurally. | ||
529 | static void make_tile_from_colour( | ||
530 | const IsoGfx* iso, Pixel colour, TileData* tile) { | ||
531 | assert(iso); | ||
532 | assert(tile); | ||
533 | |||
534 | const int width = tile->width; | ||
535 | const int height = tile->height; | ||
536 | const int r = width / height; | ||
537 | |||
538 | for (int y = 0; y < height / 2; ++y) { | ||
539 | const int mask_start = width / 2 - r * y - 1; | ||
540 | const int mask_end = width / 2 + r * y + 1; | ||
541 | for (int x = 0; x < width; ++x) { | ||
542 | const bool mask = (mask_start <= x) && (x <= mask_end); | ||
543 | const Pixel val = mask ? colour : (Pixel){.r = 0, .g = 0, .b = 0, .a = 0}; | ||
544 | |||
545 | // Top half. | ||
546 | *tile_xy_mut(iso, tile, x, y) = val; | ||
547 | |||
548 | // Bottom half reflects the top half. | ||
549 | const int y_reflected = height - y - 1; | ||
550 | *tile_xy_mut(iso, tile, x, y_reflected) = val; | ||
551 | } | ||
552 | } | ||
553 | } | ||
554 | |||
555 | Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) { | ||
556 | assert(iso); | ||
557 | assert(desc); | ||
558 | // Client must create world before creating tiles. | ||
559 | assert(iso->tile_width > 0); | ||
560 | assert(iso->tile_height > 0); | ||
561 | |||
562 | TileData* tile = mempool_alloc(&iso->tiles); | ||
563 | assert(tile); // TODO: Make this a hard assert. | ||
564 | |||
565 | const int num_blocks = calc_num_tile_blocks( | ||
566 | iso->tile_width, iso->tile_height, desc->width, desc->height); | ||
567 | |||
568 | Pixel* pixels = mem_alloc(&iso->pixels, num_blocks); | ||
569 | assert(pixels); // TODO: Make this a hard assert. | ||
570 | |||
571 | tile->width = desc->width; | ||
572 | tile->height = desc->height; | ||
573 | tile->pixels_handle = mem_get_chunk_handle(&iso->pixels, pixels); | ||
574 | |||
575 | switch (desc->type) { | ||
576 | case TileFromColour: | ||
577 | make_tile_from_colour(iso, desc->colour, tile); | ||
578 | break; | ||
579 | case TileFromFile: | ||
580 | assert(false); // TODO | ||
581 | break; | ||
582 | case TileFromMemory: | ||
583 | assert(false); // TODO | ||
584 | break; | ||
585 | } | ||
586 | |||
587 | return (Tile)mempool_get_block_index(&iso->tiles, tile); | ||
588 | } | ||
589 | |||
590 | void isogfx_set_tile(IsoGfx* iso, int x, int y, Tile tile) { | ||
591 | assert(iso); | ||
592 | *world_xy_mut(iso, x, y) = tile; | ||
593 | } | ||
594 | |||
595 | void isogfx_set_tiles(IsoGfx* iso, int x0, int y0, int x1, int y1, Tile tile) { | ||
596 | assert(iso); | ||
597 | for (int y = y0; y < y1; ++y) { | ||
598 | for (int x = x0; x < x1; ++x) { | ||
599 | isogfx_set_tile(iso, x, y, tile); | ||
600 | } | ||
601 | } | ||
602 | } | ||
603 | |||
604 | bool isogfx_load_sprite_sheet( | ||
605 | IsoGfx* iso, const char* filepath, SpriteSheet* p_sheet) { | ||
606 | assert(iso); | ||
607 | assert(filepath); | ||
608 | assert(p_sheet); | ||
609 | |||
610 | bool success = false; | ||
611 | |||
612 | // Lazy initialization of sprite pools. | ||
613 | if (mempool_capacity(&iso->sprites) == 0) { | ||
614 | if (!mempool_make_dyn( | ||
615 | &iso->sprites, iso->max_num_sprites, sizeof(SpriteData))) { | ||
616 | return false; | ||
617 | } | ||
618 | } | ||
619 | if (mem_capacity(&iso->sheets) == 0) { | ||
620 | // Using a block size of 1 byte for sprite sheet data. | ||
621 | if (!mem_make_dyn(&iso->sheets, iso->sprite_sheet_pool_size_bytes, 1)) { | ||
622 | return false; | ||
623 | } | ||
624 | } | ||
625 | |||
626 | // Load sprite sheet file. | ||
627 | printf("Load sprite sheet: %s\n", filepath); | ||
628 | FILE* file = fopen(filepath, "rb"); | ||
629 | if (file == NULL) { | ||
630 | goto cleanup; | ||
631 | } | ||
632 | const size_t sheet_size = get_file_size(file); | ||
633 | SpriteSheetData* ss_sheet = mem_alloc(&iso->sheets, sheet_size); | ||
634 | if (!ss_sheet) { | ||
635 | goto cleanup; | ||
636 | } | ||
637 | if (fread(ss_sheet, sheet_size, 1, file) != 1) { | ||
638 | goto cleanup; | ||
639 | } | ||
640 | |||
641 | *p_sheet = mem_get_chunk_handle(&iso->sheets, ss_sheet); | ||
642 | success = true; | ||
643 | |||
644 | cleanup: | ||
645 | // Pools remain initialized since client may attempt to load other sprites. | ||
646 | if (file != NULL) { | ||
647 | fclose(file); | ||
648 | } | ||
649 | if (!success) { | ||
650 | if (ss_sheet) { | ||
651 | mem_free(&iso->sheets, &ss_sheet); | ||
652 | } | ||
653 | } | ||
654 | return success; | ||
655 | } | ||
656 | |||
657 | Sprite isogfx_make_sprite(IsoGfx* iso, SpriteSheet sheet) { | ||
658 | assert(iso); | ||
659 | |||
660 | SpriteData* sprite = mempool_alloc(&iso->sprites); | ||
661 | assert(sprite); | ||
662 | |||
663 | sprite->sheet = sheet; | ||
664 | |||
665 | return mempool_get_block_index(&iso->sprites, sprite); | ||
666 | } | ||
667 | |||
668 | #define with_sprite(SPRITE, BODY) \ | ||
669 | { \ | ||
670 | SpriteData* data = mempool_get_block(&iso->sprites, sprite); \ | ||
671 | assert(data); \ | ||
672 | BODY; \ | ||
673 | } | ||
674 | |||
675 | void isogfx_set_sprite_position(IsoGfx* iso, Sprite sprite, int x, int y) { | ||
676 | assert(iso); | ||
677 | with_sprite(sprite, { | ||
678 | data->position.x = x; | ||
679 | data->position.y = y; | ||
680 | }); | ||
681 | } | ||
682 | |||
683 | void isogfx_set_sprite_animation(IsoGfx* iso, Sprite sprite, int animation) { | ||
684 | assert(iso); | ||
685 | with_sprite(sprite, { data->animation = animation; }); | ||
686 | } | ||
687 | |||
688 | void isogfx_update(IsoGfx* iso, double t) { | ||
689 | assert(iso); | ||
690 | |||
691 | // If this is the first time update() is called after initialization, just | ||
692 | // record the starting animation time. | ||
693 | if (iso->last_animation_time == 0.0) { | ||
694 | iso->last_animation_time = t; | ||
695 | return; | ||
696 | } | ||
697 | |||
698 | if ((t - iso->last_animation_time) >= ANIMATION_UPDATE_DELTA) { | ||
699 | // TODO: Consider linking animated sprites in a list so that we only walk | ||
700 | // over those here and not also the static sprites. | ||
701 | mempool_foreach(&iso->sprites, sprite, { | ||
702 | const SpriteSheetData* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); | ||
703 | assert(sheet); // TODO: Make this a hard assert inside the mem/pool. | ||
704 | const SpriteSheetRow* row = | ||
705 | get_sprite_sheet_row(sheet, sprite->animation); | ||
706 | sprite->frame = (sprite->frame + 1) % row->num_cols; | ||
707 | }); | ||
708 | |||
709 | iso->last_animation_time = t; | ||
710 | } | ||
711 | } | ||
712 | |||
713 | // ----------------------------------------------------------------------------- | ||
714 | // Rendering and picking. | ||
715 | // ----------------------------------------------------------------------------- | ||
716 | |||
717 | typedef struct CoordSystem { | ||
718 | ivec2 o; /// Origin. | ||
719 | ivec2 x; | ||
720 | ivec2 y; | ||
721 | } CoordSystem; | ||
722 | |||
723 | /// Create the basis for the isometric coordinate system with origin and vectors | ||
724 | /// expressed in the Cartesian system. | ||
725 | static CoordSystem make_iso_coord_system(const IsoGfx* iso) { | ||
726 | assert(iso); | ||
727 | const ivec2 o = {iso->screen_width / 2, 0}; | ||
728 | const ivec2 x = {.x = iso->tile_width / 2, .y = iso->tile_height / 2}; | ||
729 | const ivec2 y = {.x = -iso->tile_width / 2, .y = iso->tile_height / 2}; | ||
730 | return (CoordSystem){o, x, y}; | ||
731 | } | ||
732 | |||
733 | /// Get the screen position of the top diamond-corner of the tile at world | ||
734 | /// (x,y). | ||
735 | static ivec2 GetTileScreenOrigin( | ||
736 | const CoordSystem iso_space, int world_x, int world_y) { | ||
737 | const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x); | ||
738 | const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y); | ||
739 | const ivec2 screen_origin = | ||
740 | ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset)); | ||
741 | |||
742 | return screen_origin; | ||
743 | } | ||
744 | |||
745 | static Pixel alpha_blend(Pixel src, Pixel dst) { | ||
746 | if ((src.a == 255) || (dst.a == 0)) { | ||
747 | return src; | ||
748 | } | ||
749 | const uint16_t one_minus_alpha = 255 - src.a; | ||
750 | #define blend(s, d) \ | ||
751 | (Channel)( \ | ||
752 | (double)((uint16_t)s * (uint16_t)src.a + \ | ||
753 | (uint16_t)d * one_minus_alpha) / \ | ||
754 | 255.0) | ||
755 | return (Pixel){ | ||
756 | .r = blend(src.r, dst.r), | ||
757 | .g = blend(src.g, dst.g), | ||
758 | .b = blend(src.b, dst.b), | ||
759 | .a = src.a}; | ||
760 | } | ||
761 | |||
762 | /// Draw a rectangle (tile or sprite). | ||
763 | /// | ||
764 | /// The rectangle's top-left corner is mapped to the screen space position given | ||
765 | /// by 'top_left'. | ||
766 | /// | ||
767 | /// The rectangle's pixels are assumed to be arranged in a linear, row-major | ||
768 | /// fashion. | ||
769 | /// | ||
770 | /// If indices are given, then the image is assumed to be colour-paletted, where | ||
771 | /// 'pixels' is the palette and 'indices' the pixel indices. Otherwise, the | ||
772 | /// image is assumed to be in plain RGBA format. | ||
773 | static void draw_rect( | ||
774 | IsoGfx* iso, ivec2 top_left, int rect_width, int rect_height, | ||
775 | const Pixel* pixels, const uint8_t* indices) { | ||
776 | assert(iso); | ||
777 | |||
778 | #define rect_pixel(X, Y) \ | ||
779 | (indices ? pixels[indices[Y * rect_width + X]] : pixels[Y * rect_width + X]) | ||
780 | |||
781 | // Rect origin can be outside screen bounds, so we must offset accordingly to | ||
782 | // draw only the visible portion. | ||
783 | #define max(a, b) (a > b ? a : b) | ||
784 | const int px_offset = max(0, -top_left.x); | ||
785 | const int py_offset = max(0, -top_left.y); | ||
786 | |||
787 | // Rect can exceed screen bounds, so clip along Y and X as we draw. | ||
788 | for (int py = py_offset; | ||
789 | (py < rect_height) && (top_left.y + py < iso->screen_height); ++py) { | ||
790 | const int sy = top_left.y + py; | ||
791 | for (int px = px_offset; | ||
792 | (px < rect_width) && (top_left.x + px < iso->screen_width); ++px) { | ||
793 | const Pixel colour = rect_pixel(px, py); | ||
794 | if (colour.a > 0) { | ||
795 | const int sx = top_left.x + px; | ||
796 | const Pixel dst = screen_xy(iso, sx, sy); | ||
797 | const Pixel final = alpha_blend(colour, dst); | ||
798 | *screen_xy_mut(iso, sx, sy) = final; | ||
799 | } | ||
800 | } | ||
801 | } | ||
802 | } | ||
803 | |||
804 | /// Draw a tile. | ||
805 | /// | ||
806 | /// 'screen_origin' is the screen coordinates of the top diamond-corner of the | ||
807 | /// tile (the base tile for super tiles). | ||
808 | /// World (0, 0) -> (screen_width / 2, 0). | ||
809 | static void draw_tile(IsoGfx* iso, ivec2 screen_origin, Tile tile) { | ||
810 | assert(iso); | ||
811 | |||
812 | const TileData* tile_data = mempool_get_block(&iso->tiles, tile); | ||
813 | assert(tile_data); | ||
814 | const Pixel* pixels = tile_xy_const_ref(iso, tile_data, 0, 0); | ||
815 | |||
816 | // Move from the top diamond-corner to the top-left corner of the tile image. | ||
817 | // For regular tiles, tile height == base tile height, so the y offset is 0. | ||
818 | // For super tiles, move as high up as the height of the tile. | ||
819 | const ivec2 offset = { | ||
820 | -(iso->tile_width / 2), tile_data->height - iso->tile_height}; | ||
821 | const ivec2 top_left = ivec2_add(screen_origin, offset); | ||
822 | |||
823 | draw_rect(iso, top_left, tile_data->width, tile_data->height, pixels, 0); | ||
824 | } | ||
825 | |||
826 | static void draw_world(IsoGfx* iso) { | ||
827 | assert(iso); | ||
828 | |||
829 | const int W = iso->screen_width; | ||
830 | const int H = iso->screen_height; | ||
831 | |||
832 | memset(iso->screen, 0, W * H * sizeof(Pixel)); | ||
833 | |||
834 | const CoordSystem iso_space = make_iso_coord_system(iso); | ||
835 | |||
836 | // TODO: Culling. | ||
837 | // Ex: map the screen corners to tile space to cull. | ||
838 | // Ex: walk in screen space and fetch the tile. | ||
839 | // The tile-centric approach might be more cache-friendly since the | ||
840 | // screen-centric approach would juggle multiple tiles throughout the scan. | ||
841 | for (int wy = 0; wy < iso->world_height; ++wy) { | ||
842 | for (int wx = 0; wx < iso->world_width; ++wx) { | ||
843 | const Tile tile = world_xy(iso, wx, wy); | ||
844 | const ivec2 screen_origin = GetTileScreenOrigin(iso_space, wx, wy); | ||
845 | draw_tile(iso, screen_origin, tile); | ||
846 | } | ||
847 | } | ||
848 | } | ||
849 | |||
850 | static void draw_sprite( | ||
851 | IsoGfx* iso, ivec2 origin, const SpriteData* sprite, | ||
852 | const SpriteSheetData* sheet) { | ||
853 | assert(iso); | ||
854 | assert(sprite); | ||
855 | assert(sheet); | ||
856 | assert(sprite->animation >= 0); | ||
857 | assert(sprite->animation < sheet->num_rows); | ||
858 | assert(sprite->frame >= 0); | ||
859 | |||
860 | const SpriteSheetRow* row = get_sprite_sheet_row(sheet, sprite->animation); | ||
861 | const uint8_t* frame = get_sprite_sheet_sprite(sheet, row, sprite->frame); | ||
862 | draw_rect( | ||
863 | iso, origin, sheet->sprite_width, sheet->sprite_height, | ||
864 | sheet->palette.colours, frame); | ||
865 | } | ||
866 | |||
867 | static void draw_sprites(IsoGfx* iso) { | ||
868 | assert(iso); | ||
869 | |||
870 | const CoordSystem iso_space = make_iso_coord_system(iso); | ||
871 | |||
872 | mempool_foreach(&iso->sprites, sprite, { | ||
873 | const SpriteSheetData* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); | ||
874 | assert(sheet); | ||
875 | |||
876 | const ivec2 screen_origin = | ||
877 | GetTileScreenOrigin(iso_space, sprite->position.x, sprite->position.y); | ||
878 | draw_sprite(iso, screen_origin, sprite, sheet); | ||
879 | }); | ||
880 | } | ||
881 | |||
882 | void isogfx_render(IsoGfx* iso) { | ||
883 | assert(iso); | ||
884 | draw_world(iso); | ||
885 | draw_sprites(iso); | ||
886 | } | ||
887 | |||
888 | void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { | ||
889 | assert(iso); | ||
890 | assert(x >= 0); | ||
891 | assert(y >= 0); | ||
892 | assert(x < iso->world_width); | ||
893 | assert(y < iso->world_height); | ||
894 | |||
895 | const CoordSystem iso_space = make_iso_coord_system(iso); | ||
896 | const ivec2 screen_origin = GetTileScreenOrigin(iso_space, x, y); | ||
897 | draw_tile(iso, screen_origin, tile); | ||
898 | } | ||
899 | |||
900 | bool isogfx_resize(IsoGfx* iso, int screen_width, int screen_height) { | ||
901 | assert(iso); | ||
902 | assert(iso->screen); | ||
903 | |||
904 | const int current_size = iso->screen_width * iso->screen_height; | ||
905 | const int new_size = screen_width * screen_height; | ||
906 | |||
907 | if (new_size > current_size) { | ||
908 | Pixel* new_screen = calloc(new_size, sizeof(Pixel)); | ||
909 | if (new_screen) { | ||
910 | free(iso->screen); | ||
911 | iso->screen = new_screen; | ||
912 | } else { | ||
913 | return false; | ||
914 | } | ||
915 | } | ||
916 | iso->screen_width = screen_width; | ||
917 | iso->screen_height = screen_height; | ||
918 | return true; | ||
919 | } | ||
920 | |||
921 | void isogfx_get_screen_size(const IsoGfx* iso, int* width, int* height) { | ||
922 | assert(iso); | ||
923 | assert(width); | ||
924 | assert(height); | ||
925 | *width = iso->screen_width; | ||
926 | *height = iso->screen_height; | ||
927 | } | ||
928 | |||
929 | const Pixel* isogfx_get_screen_buffer(const IsoGfx* iso) { | ||
930 | assert(iso); | ||
931 | return iso->screen; | ||
932 | } | ||
933 | |||
934 | void isogfx_pick_tile( | ||
935 | const IsoGfx* iso, double xcart, double ycart, int* xiso, int* yiso) { | ||
936 | assert(iso); | ||
937 | assert(xiso); | ||
938 | assert(yiso); | ||
939 | |||
940 | const vec2 xy_iso = cart2iso( | ||
941 | (vec2){.x = xcart, .y = ycart}, iso->tile_width, iso->tile_height, | ||
942 | iso->screen_width); | ||
943 | |||
944 | if ((0 <= xy_iso.x) && (xy_iso.x < iso->world_width) && (0 <= xy_iso.y) && | ||
945 | (xy_iso.y < iso->world_height)) { | ||
946 | *xiso = (int)xy_iso.x; | ||
947 | *yiso = (int)xy_iso.y; | ||
948 | } else { | ||
949 | *xiso = -1; | ||
950 | *yiso = -1; | ||
951 | } | ||
952 | } | ||
diff --git a/gfx-iso/tools/mkasset.py b/gfx-iso/tools/mkasset.py deleted file mode 100644 index 3ca8a1d..0000000 --- a/gfx-iso/tools/mkasset.py +++ /dev/null | |||
@@ -1,324 +0,0 @@ | |||
1 | # Converts assets to binary formats (.ts, .tm, .ss) for the engine. | ||
2 | # | ||
3 | # Input file formats: | ||
4 | # - Tiled tile set (.tsx) | ||
5 | # - Tiled tile map (.tmx) | ||
6 | # - Sprite sheets (.jpg, .png, etc), 1 row per animation. | ||
7 | # | ||
8 | # Output file formats: | ||
9 | # - Binary tile set file (.ts) | ||
10 | # - Binary tile map file (.tm) | ||
11 | # - Binary sprite sheet file (.ss) | ||
12 | # | ||
13 | import argparse | ||
14 | import ctypes | ||
15 | import os | ||
16 | from PIL import Image | ||
17 | import sys | ||
18 | from xml.etree import ElementTree | ||
19 | |||
20 | # Maximum length of path strings in .TS and .TM files. | ||
21 | # Must match the engine's value. | ||
22 | MAX_PATH_LENGTH = 128 | ||
23 | |||
24 | |||
25 | def drop_extension(filepath): | ||
26 | return filepath[:filepath.rfind('.')] | ||
27 | |||
28 | |||
29 | def to_char_array(string, length): | ||
30 | """Convert a string to a fixed-length ASCII char array. | ||
31 | |||
32 | The length of str must be at most length-1 so that the resulting string can | ||
33 | be null-terminated. | ||
34 | """ | ||
35 | assert (len(string) < length) | ||
36 | chars = string.encode("ascii") | ||
37 | nulls = ("\0" * (length - len(string))).encode("ascii") | ||
38 | return chars + nulls | ||
39 | |||
40 | |||
41 | def convert_tsx(input_filepath, output_filepath): | ||
42 | """Converts a Tiled .tsx tileset file to a .TS tile set file.""" | ||
43 | xml = ElementTree.parse(input_filepath) | ||
44 | root = xml.getroot() | ||
45 | |||
46 | tile_count = int(root.attrib["tilecount"]) | ||
47 | max_tile_width = int(root.attrib["tilewidth"]) | ||
48 | max_tile_height = int(root.attrib["tileheight"]) | ||
49 | |||
50 | print(f"Tile count: {tile_count}") | ||
51 | print(f"Max width: {max_tile_width}") | ||
52 | print(f"Max height: {max_tile_height}") | ||
53 | |||
54 | with open(output_filepath, 'bw') as output: | ||
55 | output.write(ctypes.c_uint16(tile_count)) | ||
56 | output.write(ctypes.c_uint16(max_tile_width)) | ||
57 | output.write(ctypes.c_uint16(max_tile_height)) | ||
58 | |||
59 | num_tile = 0 | ||
60 | for tile in root: | ||
61 | # Skip the "grid" and other non-tile elements. | ||
62 | if not tile.tag == "tile": | ||
63 | continue | ||
64 | |||
65 | # Assuming tiles are numbered 0..N. | ||
66 | tile_id = int(tile.attrib["id"]) | ||
67 | assert (tile_id == num_tile) | ||
68 | num_tile += 1 | ||
69 | |||
70 | image = tile[0] | ||
71 | tile_width = int(image.attrib["width"]) | ||
72 | tile_height = int(image.attrib["height"]) | ||
73 | tile_path = image.attrib["source"] | ||
74 | |||
75 | output.write(ctypes.c_uint16(tile_width)) | ||
76 | output.write(ctypes.c_uint16(tile_height)) | ||
77 | |||
78 | with Image.open(tile_path) as im: | ||
79 | bytes = im.convert('RGBA').tobytes() | ||
80 | output.write(bytes) | ||
81 | |||
82 | |||
83 | def convert_tmx(input_filepath, output_filepath): | ||
84 | """Converts a Tiled .tmx file to a .TM tile map file.""" | ||
85 | xml = ElementTree.parse(input_filepath) | ||
86 | root = xml.getroot() | ||
87 | |||
88 | map_width = int(root.attrib["width"]) | ||
89 | map_height = int(root.attrib["height"]) | ||
90 | base_tile_width = int(root.attrib["tilewidth"]) | ||
91 | base_tile_height = int(root.attrib["tileheight"]) | ||
92 | num_layers = 1 | ||
93 | |||
94 | print(f"Map width: {map_width}") | ||
95 | print(f"Map height: {map_height}") | ||
96 | print(f"Tile width: {base_tile_width}") | ||
97 | print(f"Tile height: {base_tile_height}") | ||
98 | |||
99 | with open(output_filepath, 'bw') as output: | ||
100 | output.write(ctypes.c_uint16(map_width)) | ||
101 | output.write(ctypes.c_uint16(map_height)) | ||
102 | output.write(ctypes.c_uint16(base_tile_width)) | ||
103 | output.write(ctypes.c_uint16(base_tile_height)) | ||
104 | output.write(ctypes.c_uint16(num_layers)) | ||
105 | |||
106 | tileset_path = None | ||
107 | |||
108 | for child in root: | ||
109 | if child.tag == "tileset": | ||
110 | tileset = child | ||
111 | tileset_path = tileset.attrib["source"] | ||
112 | |||
113 | print(f"Tile set: {tileset_path}") | ||
114 | |||
115 | tileset_path = tileset_path.replace("tsx", "ts") | ||
116 | elif child.tag == "layer": | ||
117 | layer = child | ||
118 | layer_id = int(layer.attrib["id"]) | ||
119 | layer_width = int(layer.attrib["width"]) | ||
120 | layer_height = int(layer.attrib["height"]) | ||
121 | |||
122 | print(f"Layer: {layer_id}") | ||
123 | print(f"Width: {layer_width}") | ||
124 | print(f"Height: {layer_height}") | ||
125 | |||
126 | assert (tileset_path) | ||
127 | output.write(to_char_array(tileset_path, MAX_PATH_LENGTH)) | ||
128 | |||
129 | # Assume the layer's dimensions matches the map's. | ||
130 | assert (layer_width == map_width) | ||
131 | assert (layer_height == map_height) | ||
132 | |||
133 | data = layer[0] | ||
134 | # Handle other encodings later. | ||
135 | assert (data.attrib["encoding"] == "csv") | ||
136 | |||
137 | csv = data.text.strip() | ||
138 | rows = csv.split('\n') | ||
139 | for row in rows: | ||
140 | tile_ids = [x.strip() for x in row.split(',') if x] | ||
141 | for tile_id in tile_ids: | ||
142 | output.write(ctypes.c_uint16(int(tile_id))) | ||
143 | |||
144 | |||
145 | def get_num_cols(image, sprite_width): | ||
146 | """Return the number of non-empty columns in the image. | ||
147 | |||
148 | Assumes no gaps in the columns. | ||
149 | """ | ||
150 | assert (image.width % sprite_width == 0) | ||
151 | num_cols = image.width // sprite_width | ||
152 | |||
153 | # Start the search from right to left. | ||
154 | for col in reversed(range(1, num_cols)): | ||
155 | left = (col - 1) * sprite_width | ||
156 | right = col * sprite_width | ||
157 | rect = image.crop((left, 0, right, image.height)) | ||
158 | min_max = rect.getextrema() | ||
159 | for (channel_min, channel_max) in min_max: | ||
160 | if channel_min != 0 or channel_max != 0: | ||
161 | # 'col' is the rightmost non-empty column. | ||
162 | # Assuming no gaps, col+1 is the number of non-empty columns. | ||
163 | return col + 1 | ||
164 | |||
165 | return 0 | ||
166 | |||
167 | |||
168 | def get_sprite_sheet_rows(im, sprite_width, sprite_height): | ||
169 | """Gets the individual rows of a sprite sheet. | ||
170 | |||
171 | The input sprite sheet can have any number of rows. | ||
172 | |||
173 | Returns a list of lists [[sprite]], one inner list for the columns in each | ||
174 | row. | ||
175 | """ | ||
176 | # Sprite sheet's width and height must be integer multiples of the | ||
177 | # sprite's width and height. | ||
178 | assert (im.width % sprite_width == 0) | ||
179 | assert (im.height % sprite_height == 0) | ||
180 | |||
181 | num_rows = im.height // sprite_height | ||
182 | |||
183 | rows = [] | ||
184 | for row in range(num_rows): | ||
185 | # Get the number of columns. | ||
186 | upper = row * sprite_height | ||
187 | lower = (row + 1) * sprite_height | ||
188 | whole_row = im.crop((0, upper, im.width, lower)) | ||
189 | num_cols = get_num_cols(whole_row, sprite_width) | ||
190 | assert (num_cols > 0) | ||
191 | |||
192 | # Crop the row into N columns. | ||
193 | cols = [] | ||
194 | for i in range(num_cols): | ||
195 | left = i * sprite_width | ||
196 | right = (i + 1) * sprite_width | ||
197 | sprite = im.crop((left, upper, right, lower)) | ||
198 | cols.append(sprite) | ||
199 | |||
200 | assert (len(cols) == num_cols) | ||
201 | rows.append(cols) | ||
202 | |||
203 | return rows | ||
204 | |||
205 | |||
206 | def make_image_from_rows(rows, sprite_width, sprite_height): | ||
207 | """Concatenate the rows into a single RGBA image.""" | ||
208 | im_width = sprite_width * max(len(row) for row in rows) | ||
209 | im_height = len(rows) * sprite_height | ||
210 | im = Image.new('RGBA', (im_width, im_height)) | ||
211 | y = 0 | ||
212 | for row in rows: | ||
213 | x = 0 | ||
214 | for sprite in row: | ||
215 | im.paste(sprite.convert('RGBA'), (x, y)) | ||
216 | x += sprite_width | ||
217 | y += sprite_height | ||
218 | return im | ||
219 | |||
220 | |||
221 | def convert_sprite_sheet(input_file_paths, sprite_width, sprite_height, | ||
222 | output_filepath): | ||
223 | """Converts a set of sprite sheet images into a binary sprite sheet file | ||
224 | (.ss). | ||
225 | |||
226 | The input sprite sheets can have any number of rows, one row per animation. | ||
227 | All rows from all sprite sheets are concatenated in the output file. | ||
228 | |||
229 | The sprite's width and height is assumed constant throughout the input | ||
230 | sprite sheets. | ||
231 | """ | ||
232 | rows = [] | ||
233 | for input_filepath in input_file_paths: | ||
234 | with Image.open(input_filepath) as sprite_sheet: | ||
235 | rows.extend( | ||
236 | get_sprite_sheet_rows(sprite_sheet, sprite_width, | ||
237 | sprite_height)) | ||
238 | |||
239 | im = make_image_from_rows(rows, sprite_width, sprite_height) | ||
240 | im = im.convert(mode="P", palette=Image.ADAPTIVE, colors=256) | ||
241 | |||
242 | # The sprite data in 'rows' is no longer needed. | ||
243 | # Keep just the number of columns per row. | ||
244 | rows = [len(row) for row in rows] | ||
245 | |||
246 | with open(output_filepath, 'bw') as output: | ||
247 | output.write(ctypes.c_uint16(sprite_width)) | ||
248 | output.write(ctypes.c_uint16(sprite_height)) | ||
249 | output.write(ctypes.c_uint16(len(rows))) | ||
250 | |||
251 | # Write palette. | ||
252 | # getpalette() returns 256 colors, but the palette might use less than | ||
253 | # that. getcolors() returns the number of unique colors. | ||
254 | # getpalette() also returns a flattened list, which is why we must *4. | ||
255 | num_colours = len(im.getcolors()) | ||
256 | colours = im.getpalette(rawmode="RGBA")[:4 * num_colours] | ||
257 | palette = [] | ||
258 | for i in range(0, 4 * num_colours, 4): | ||
259 | palette.append((colours[i], colours[i + 1], colours[i + 2], | ||
260 | colours[i + 3])) | ||
261 | |||
262 | output.write(ctypes.c_uint16(len(palette))) | ||
263 | output.write(bytearray(colours)) | ||
264 | |||
265 | print(f"Sprite width: {sprite_width}") | ||
266 | print(f"Sprite height: {sprite_height}") | ||
267 | print(f"Rows: {len(rows)}") | ||
268 | print(f"Colours: {len(palette)}") | ||
269 | |||
270 | # print("Palette") | ||
271 | # for i, colour in enumerate(palette): | ||
272 | # print(f"{i}: {colour}") | ||
273 | |||
274 | for row, num_columns in enumerate(rows): | ||
275 | output.write(ctypes.c_uint16(num_columns)) | ||
276 | upper = row * sprite_height | ||
277 | lower = (row + 1) * sprite_height | ||
278 | for col in range(num_columns): | ||
279 | left = col * sprite_width | ||
280 | right = (col + 1) * sprite_width | ||
281 | sprite = im.crop((left, upper, right, lower)) | ||
282 | sprite_bytes = sprite.tobytes() | ||
283 | |||
284 | assert (len(sprite_bytes) == sprite_width * sprite_height) | ||
285 | output.write(sprite_bytes) | ||
286 | |||
287 | # if (row == 0) and (col == 0): | ||
288 | # print(f"Sprite: ({len(sprite_bytes)})") | ||
289 | # print(list(sprite_bytes)) | ||
290 | # sprite.save("out.png") | ||
291 | |||
292 | |||
293 | def main(): | ||
294 | parser = argparse.ArgumentParser() | ||
295 | parser.add_argument("input", | ||
296 | nargs="+", | ||
297 | help="Input file (.tsx, .tmx) or path regex (sprite sheets)") | ||
298 | parser.add_argument("--width", type=int, help="Sprite width in pixels") | ||
299 | parser.add_argument("--height", type=int, help="Sprite height in pixels") | ||
300 | parser.add_argument("--out", help="Output file (sprite sheets)") | ||
301 | args = parser.parse_args() | ||
302 | |||
303 | if ".tsx" in args.input: | ||
304 | output_filepath_no_ext = drop_extension(args.input) | ||
305 | output_filepath = output_filepath_no_ext + ".ts" | ||
306 | convert_tsx(args.input, output_filepath) | ||
307 | elif ".tmx" in args.input: | ||
308 | output_filepath_no_ext = drop_extension(args.input) | ||
309 | output_filepath = output_filepath_no_ext + ".tm" | ||
310 | convert_tmx(args.input, output_filepath) | ||
311 | else: | ||
312 | # Sprite sheets. | ||
313 | if not args.width or not args.height: | ||
314 | print("Sprite width and height must be given") | ||
315 | return 1 | ||
316 | output_filepath = args.out if args.out else "out.ss" | ||
317 | convert_sprite_sheet(args.input, args.width, args.height, | ||
318 | output_filepath) | ||
319 | |||
320 | return 0 | ||
321 | |||
322 | |||
323 | if __name__ == '__main__': | ||
324 | sys.exit(main()) | ||
diff --git a/gfx/CMakeLists.txt b/gfx/CMakeLists.txt deleted file mode 100644 index 7d629dc..0000000 --- a/gfx/CMakeLists.txt +++ /dev/null | |||
@@ -1,89 +0,0 @@ | |||
1 | cmake_minimum_required(VERSION 3.0) | ||
2 | |||
3 | include(cmake/shader.txt) | ||
4 | |||
5 | add_subdirectory(contrib/cgltf) | ||
6 | add_subdirectory(contrib/cgltf-tangents) | ||
7 | add_subdirectory(contrib/stb) | ||
8 | |||
9 | project(gfx) | ||
10 | |||
11 | add_shader_library(shaders | ||
12 | shaders/brdf_integration_map.frag | ||
13 | shaders/cook_torrance.frag | ||
14 | shaders/cook_torrance.vert | ||
15 | shaders/cubemap_filtering.vert | ||
16 | shaders/debug3d.frag | ||
17 | shaders/debug3d.vert | ||
18 | shaders/immediate_mode.frag | ||
19 | shaders/immediate_mode.vert | ||
20 | shaders/irradiance_map.frag | ||
21 | shaders/prefiltered_environment_map.frag | ||
22 | shaders/quad.vert | ||
23 | shaders/skyquad.frag | ||
24 | shaders/skyquad.vert | ||
25 | shaders/view_normal_mapped_normals.frag | ||
26 | shaders/view_normal_mapped_normals.vert | ||
27 | shaders/view_normals.frag | ||
28 | shaders/view_normals.vert | ||
29 | shaders/view_tangents.frag | ||
30 | shaders/view_tangents.vert | ||
31 | shaders/view_texture.frag | ||
32 | shaders/view_texture.vert) | ||
33 | |||
34 | add_library(gfx SHARED | ||
35 | src/asset/asset_cache.c | ||
36 | src/asset/model.c | ||
37 | src/asset/texture.c | ||
38 | src/core/buffer.c | ||
39 | src/core/core.c | ||
40 | src/core/framebuffer.c | ||
41 | src/core/geometry.c | ||
42 | src/core/renderbuffer.c | ||
43 | src/core/shader_program.c | ||
44 | src/core/shader.c | ||
45 | src/core/texture.c | ||
46 | src/renderer/imm_renderer.c | ||
47 | src/renderer/renderer.c | ||
48 | src/scene/animation.c | ||
49 | src/scene/camera.c | ||
50 | src/scene/light.c | ||
51 | src/scene/material.c | ||
52 | src/scene/mesh.c | ||
53 | src/scene/model.c | ||
54 | src/scene/node.c | ||
55 | src/scene/object.c | ||
56 | src/scene/scene.c | ||
57 | src/scene/scene_memory.c | ||
58 | src/gfx.c | ||
59 | src/util/geometry.c | ||
60 | src/util/ibl.c | ||
61 | src/util/shader.c | ||
62 | src/util/skyquad.c) | ||
63 | |||
64 | target_include_directories(gfx PUBLIC | ||
65 | include) | ||
66 | |||
67 | target_include_directories(gfx PRIVATE | ||
68 | src) | ||
69 | |||
70 | target_compile_options(gfx PRIVATE -std=gnu11 -Wall -Wextra -Wpedantic) | ||
71 | |||
72 | target_link_libraries(gfx PUBLIC | ||
73 | cstring | ||
74 | math) | ||
75 | |||
76 | target_link_libraries(gfx PRIVATE | ||
77 | cassert | ||
78 | cgltf | ||
79 | cgltf-tangents | ||
80 | error | ||
81 | gfx-app | ||
82 | log | ||
83 | mempool | ||
84 | shaders | ||
85 | stb | ||
86 | # System libraries. | ||
87 | GL | ||
88 | # Required to initialize GLAD. | ||
89 | -ldl) | ||
diff --git a/gfx/include/gfx/renderer.h b/gfx/include/gfx/renderer.h deleted file mode 100644 index 2a4ada1..0000000 --- a/gfx/include/gfx/renderer.h +++ /dev/null | |||
@@ -1,104 +0,0 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <math/aabb2.h> | ||
4 | #include <math/aabb3.h> | ||
5 | #include <math/camera.h> | ||
6 | #include <math/defs.h> | ||
7 | #include <math/mat4.h> | ||
8 | #include <math/vec3.h> | ||
9 | #include <math/vec4.h> | ||
10 | |||
11 | typedef struct GfxCore GfxCore; | ||
12 | typedef struct Scene Scene; | ||
13 | typedef struct SceneCamera SceneCamera; | ||
14 | |||
15 | typedef struct ImmRenderer ImmRenderer; | ||
16 | typedef struct Renderer Renderer; | ||
17 | |||
18 | // ----------------------------------------------------------------------------- | ||
19 | // Main Renderer. | ||
20 | // ----------------------------------------------------------------------------- | ||
21 | |||
22 | typedef enum RenderSceneMode { | ||
23 | RenderDefault, | ||
24 | RenderDebug, | ||
25 | RenderNormals, | ||
26 | RenderNormalMappedNormals, | ||
27 | RenderTangents | ||
28 | } RenderSceneMode; | ||
29 | |||
30 | typedef struct RenderSceneParams { | ||
31 | RenderSceneMode mode; | ||
32 | const Scene* scene; | ||
33 | const SceneCamera* camera; | ||
34 | } RenderSceneParams; | ||
35 | |||
36 | /// Render the scene. | ||
37 | void gfx_render_scene(Renderer*, const RenderSceneParams*); | ||
38 | |||
39 | /// Update the scene. | ||
40 | void gfx_update(Scene*, const SceneCamera*, R t); | ||
41 | |||
42 | // ----------------------------------------------------------------------------- | ||
43 | // Immediate Mode Renderer. | ||
44 | // ----------------------------------------------------------------------------- | ||
45 | |||
46 | /// Prepare the graphics systems for immediate-mode rendering. | ||
47 | /// | ||
48 | /// Call this before issuing any immediate-mode rendering draws. | ||
49 | void gfx_imm_start(ImmRenderer*); | ||
50 | |||
51 | /// End immediate mode rendering. | ||
52 | /// | ||
53 | /// Call this after issuing immediate-mode rendering draws and before swapping | ||
54 | /// buffers. | ||
55 | void gfx_imm_end(ImmRenderer*); | ||
56 | |||
57 | /// Draw a set of triangles. | ||
58 | void gfx_imm_draw_triangles(ImmRenderer*, const vec3[], size_t num_triangles); | ||
59 | |||
60 | /// Draw a triangle. | ||
61 | void gfx_imm_draw_triangle(ImmRenderer*, const vec3[3]); | ||
62 | |||
63 | /// Draw a bounding box. | ||
64 | void gfx_imm_draw_aabb2(ImmRenderer*, aabb2); | ||
65 | |||
66 | /// Draw a bounding box. | ||
67 | void gfx_imm_draw_aabb3(ImmRenderer*, aabb3); | ||
68 | |||
69 | /// Draw a box. | ||
70 | /// | ||
71 | /// The vertices must be given in the following order: | ||
72 | /// | ||
73 | /// 7 ----- 6 | ||
74 | /// / /| | ||
75 | /// 3 ----- 2 | | ||
76 | /// | | | | ||
77 | /// | 4 ----- 5 | ||
78 | /// |/ |/ | ||
79 | /// 0 ----- 1 | ||
80 | void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]); | ||
81 | |||
82 | /// Set the camera. | ||
83 | void gfx_imm_set_camera(ImmRenderer*, const Camera*); | ||
84 | |||
85 | /// Load an identity model matrix. Clears the matrix stack. | ||
86 | void gfx_imm_load_identity(ImmRenderer* renderer); | ||
87 | |||
88 | /// Push the given matrix to the matrix stack. | ||
89 | void gfx_imm_push_matrix(ImmRenderer* renderer, const mat4* matrix); | ||
90 | |||
91 | /// Pop the top of the matrix stack. | ||
92 | void gfx_imm_pop_matrix(ImmRenderer* renderer); | ||
93 | |||
94 | /// Push a translation matrix to the matrix stack. | ||
95 | void gfx_imm_translate(ImmRenderer* renderer, vec3 offset); | ||
96 | |||
97 | /// Set the model matrix. Clears the matrix stack. | ||
98 | void gfx_imm_set_model_matrix(ImmRenderer*, const mat4*); | ||
99 | |||
100 | /// Set the view-projection matrix. | ||
101 | void gfx_imm_set_view_projection_matrix(ImmRenderer*, const mat4*); | ||
102 | |||
103 | /// Set the render colour. | ||
104 | void gfx_imm_set_colour(ImmRenderer*, vec4 colour); | ||
diff --git a/gfx/src/renderer/imm_renderer.c b/gfx/src/renderer/imm_renderer.c deleted file mode 100644 index 8cf3a10..0000000 --- a/gfx/src/renderer/imm_renderer.c +++ /dev/null | |||
@@ -1,260 +0,0 @@ | |||
1 | #include "imm_renderer_impl.h" | ||
2 | |||
3 | #include <gfx/core.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 | bool imm_renderer_make(ImmRenderer* renderer, GfxCore* gfxcore) { | ||
12 | assert(renderer); | ||
13 | assert(gfxcore); | ||
14 | |||
15 | const size_t num_triangle_verts = IMM_MAX_NUM_TRIANGLES * 3; | ||
16 | |||
17 | renderer->gfxcore = gfxcore; | ||
18 | |||
19 | renderer->triangles = gfx_make_geometry( | ||
20 | gfxcore, &(GeometryDesc){ | ||
21 | .type = Triangles, | ||
22 | .buffer_usage = BufferDynamic, | ||
23 | .num_verts = num_triangle_verts, | ||
24 | .positions3d = (BufferView3d){ | ||
25 | .size_bytes = num_triangle_verts * sizeof(vec3)}}); | ||
26 | if (!renderer->triangles) { | ||
27 | goto cleanup; | ||
28 | } | ||
29 | |||
30 | renderer->shader = gfx_make_immediate_mode_shader(gfxcore); | ||
31 | if (!renderer->shader) { | ||
32 | goto cleanup; | ||
33 | } | ||
34 | |||
35 | renderer->matrix_stack[0] = mat4_id(); | ||
36 | renderer->stack_pointer = 0; | ||
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->gfxcore); | ||
50 | |||
51 | if (renderer->triangles) { | ||
52 | gfx_destroy_geometry(renderer->gfxcore, &renderer->triangles); | ||
53 | // TODO: Could also destroy the geometry's buffers here. | ||
54 | } | ||
55 | |||
56 | if (renderer->shader) { | ||
57 | gfx_destroy_shader_program(renderer->gfxcore, &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 | // Shader uniforms are applied lazily. | ||
84 | // TODO: In the event that gfx_activate_shader_program() activates uniforms | ||
85 | // automatically for convenience, call an overload here that doesn't do so. | ||
86 | ShaderProgram* shader = renderer->shader; | ||
87 | gfx_activate_shader_program(shader); | ||
88 | } | ||
89 | |||
90 | void gfx_imm_end(ImmRenderer* renderer) { | ||
91 | assert(renderer); | ||
92 | imm_renderer_flush(renderer); | ||
93 | gfx_deactivate_shader_program(renderer->shader); | ||
94 | } | ||
95 | |||
96 | void gfx_imm_draw_triangles( | ||
97 | ImmRenderer* renderer, const vec3 verts[], size_t num_triangles) { | ||
98 | assert(renderer); | ||
99 | assert(verts); | ||
100 | const size_t new_verts = num_triangles * 3; | ||
101 | assert( | ||
102 | renderer->num_triangle_verts + new_verts < (IMM_MAX_NUM_TRIANGLES * 3)); | ||
103 | |||
104 | memcpy( | ||
105 | renderer->triangle_verts + renderer->num_triangle_verts, verts, | ||
106 | new_verts * sizeof(vec3)); | ||
107 | |||
108 | renderer->num_triangle_verts += new_verts; | ||
109 | } | ||
110 | |||
111 | void gfx_imm_draw_triangle(ImmRenderer* renderer, const vec3 verts[3]) { | ||
112 | gfx_imm_draw_triangles(renderer, verts, 1); | ||
113 | } | ||
114 | |||
115 | void gfx_imm_draw_aabb2(ImmRenderer* renderer, aabb2 box) { | ||
116 | assert(renderer); | ||
117 | |||
118 | // clang-format off | ||
119 | const vec3 verts[4] = { | ||
120 | vec3_make(box.min.x, box.min.y, 0), // 3 ---- 2 | ||
121 | vec3_make(box.max.x, box.min.y, 0), // | | | ||
122 | vec3_make(box.max.x, box.max.y, 0), // | | | ||
123 | vec3_make(box.min.x, box.max.y, 0)}; // 0 ---- 1 | ||
124 | // clang-format on | ||
125 | |||
126 | #define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2] | ||
127 | const vec3 tris[6] = {tri(0, 1, 2), tri(0, 2, 3)}; | ||
128 | #undef tri | ||
129 | |||
130 | gfx_imm_draw_triangles(renderer, tris, 2); | ||
131 | } | ||
132 | |||
133 | void gfx_imm_draw_aabb3(ImmRenderer* renderer, aabb3 box) { | ||
134 | assert(renderer); | ||
135 | |||
136 | // clang-format off | ||
137 | const vec3 vertices[8] = { | ||
138 | vec3_make(box.min.x, box.min.y, box.max.z), // 7 ----- 6 | ||
139 | vec3_make(box.max.x, box.min.y, box.max.z), // / /| | ||
140 | vec3_make(box.max.x, box.max.y, box.max.z), // 3 ----- 2 | | ||
141 | vec3_make(box.min.x, box.max.y, box.max.z), // | | | | ||
142 | vec3_make(box.min.x, box.min.y, box.min.z), // | 4 ----- 5 | ||
143 | vec3_make(box.max.x, box.min.y, box.min.z), // |/ |/ | ||
144 | vec3_make(box.max.x, box.max.y, box.min.z), // 0 ----- 1 | ||
145 | vec3_make(box.min.x, box.max.y, box.min.z)}; | ||
146 | // clang-format on | ||
147 | |||
148 | gfx_imm_draw_box3(renderer, vertices); | ||
149 | } | ||
150 | |||
151 | void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]) { | ||
152 | assert(renderer); | ||
153 | assert(vertices); | ||
154 | |||
155 | // 7 ----- 6 | ||
156 | // / /| | ||
157 | // 3 ----- 2 | | ||
158 | // | | | | ||
159 | // | 4 ----- 5 | ||
160 | // |/ |/ | ||
161 | // 0 ----- 1 | ||
162 | |||
163 | #define tri(i0, i1, i2) vertices[i0], vertices[i1], vertices[i2] | ||
164 | const vec3 tris[36] = {// Front. | ||
165 | tri(0, 1, 2), tri(0, 2, 3), | ||
166 | // Right. | ||
167 | tri(1, 5, 6), tri(1, 6, 2), | ||
168 | // Back. | ||
169 | tri(5, 4, 7), tri(5, 7, 6), | ||
170 | // Left. | ||
171 | tri(4, 0, 03), tri(4, 3, 7), | ||
172 | // Top. | ||
173 | tri(3, 2, 6), tri(3, 6, 7), | ||
174 | // Bottom. | ||
175 | tri(0, 4, 5), tri(0, 5, 1)}; | ||
176 | |||
177 | gfx_imm_draw_triangles(renderer, tris, 12); | ||
178 | } | ||
179 | |||
180 | // Load the top of the matrix stack into the shader. | ||
181 | static void update_shader_model_matrix(ImmRenderer* renderer) { | ||
182 | assert(renderer); | ||
183 | imm_renderer_flush(renderer); | ||
184 | gfx_set_mat4_uniform( | ||
185 | renderer->shader, "Model", | ||
186 | &renderer->matrix_stack[renderer->stack_pointer]); | ||
187 | } | ||
188 | |||
189 | void gfx_imm_load_identity(ImmRenderer* renderer) { | ||
190 | assert(renderer); | ||
191 | renderer->matrix_stack[0] = mat4_id(); | ||
192 | renderer->stack_pointer = 0; | ||
193 | update_shader_model_matrix(renderer); | ||
194 | } | ||
195 | |||
196 | void gfx_imm_push_matrix(ImmRenderer* renderer, const mat4* matrix) { | ||
197 | assert(renderer); | ||
198 | assert(matrix); | ||
199 | assert(renderer->stack_pointer >= 0); | ||
200 | assert(renderer->stack_pointer < IMM_MAX_NUM_MATRICES); // TODO: hard assert. | ||
201 | |||
202 | renderer->matrix_stack[renderer->stack_pointer + 1] = | ||
203 | mat4_mul(*matrix, renderer->matrix_stack[renderer->stack_pointer]); | ||
204 | renderer->stack_pointer += 1; | ||
205 | |||
206 | update_shader_model_matrix(renderer); | ||
207 | } | ||
208 | |||
209 | void gfx_imm_pop_matrix(ImmRenderer* renderer) { | ||
210 | assert(renderer); | ||
211 | assert(renderer->stack_pointer > 0); // TODO: hard assert. | ||
212 | |||
213 | // For debugging, zero out the matrix stack as matrices are popped out. | ||
214 | memset( | ||
215 | &renderer->matrix_stack[renderer->stack_pointer], 0, | ||
216 | sizeof(renderer->matrix_stack[0])); | ||
217 | |||
218 | renderer->stack_pointer -= 1; | ||
219 | |||
220 | update_shader_model_matrix(renderer); | ||
221 | } | ||
222 | |||
223 | void gfx_imm_translate(ImmRenderer* renderer, vec3 offset) { | ||
224 | assert(renderer); | ||
225 | const mat4 mat = mat4_translate(offset); | ||
226 | gfx_imm_push_matrix(renderer, &mat); | ||
227 | } | ||
228 | |||
229 | void gfx_imm_set_camera(ImmRenderer* renderer, const Camera* camera) { | ||
230 | assert(renderer); | ||
231 | assert(renderer->shader); | ||
232 | imm_renderer_flush(renderer); | ||
233 | const mat4 view = spatial3_inverse_transform(&camera->spatial); | ||
234 | const mat4 view_proj = mat4_mul(camera->projection, view); | ||
235 | gfx_imm_set_view_projection_matrix(renderer, &view_proj); | ||
236 | } | ||
237 | |||
238 | void gfx_imm_set_model_matrix(ImmRenderer* renderer, const mat4* model) { | ||
239 | assert(renderer); | ||
240 | assert(model); | ||
241 | imm_renderer_flush(renderer); | ||
242 | renderer->matrix_stack[0] = *model; | ||
243 | renderer->stack_pointer = 0; | ||
244 | update_shader_model_matrix(renderer); | ||
245 | } | ||
246 | |||
247 | void gfx_imm_set_view_projection_matrix( | ||
248 | ImmRenderer* renderer, const mat4* view_proj) { | ||
249 | assert(renderer); | ||
250 | assert(renderer->shader); | ||
251 | imm_renderer_flush(renderer); | ||
252 | gfx_set_mat4_uniform(renderer->shader, "ViewProjection", view_proj); | ||
253 | } | ||
254 | |||
255 | void gfx_imm_set_colour(ImmRenderer* renderer, vec4 colour) { | ||
256 | assert(renderer); | ||
257 | assert(renderer->shader); | ||
258 | imm_renderer_flush(renderer); | ||
259 | gfx_set_vec4_uniform(renderer->shader, "Colour", colour); | ||
260 | } | ||
diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c deleted file mode 100644 index c2a7dda..0000000 --- a/gfx/src/renderer/renderer.c +++ /dev/null | |||
@@ -1,396 +0,0 @@ | |||
1 | #include "renderer_impl.h" | ||
2 | |||
3 | #include "scene/animation_impl.h" | ||
4 | #include "scene/camera_impl.h" | ||
5 | #include "scene/light_impl.h" | ||
6 | #include "scene/material_impl.h" | ||
7 | #include "scene/mesh_impl.h" | ||
8 | #include "scene/model_impl.h" | ||
9 | #include "scene/node_impl.h" | ||
10 | #include "scene/object_impl.h" | ||
11 | #include "scene/scene_impl.h" | ||
12 | #include "scene/scene_memory.h" | ||
13 | |||
14 | #include <gfx/core.h> | ||
15 | #include <gfx/util/ibl.h> | ||
16 | #include <gfx/util/shader.h> | ||
17 | |||
18 | #include <log/log.h> | ||
19 | #include <math/mat4.h> | ||
20 | #include <math/spatial3.h> | ||
21 | |||
22 | #include <assert.h> | ||
23 | |||
24 | // TODO: Move to a header like "constants.h". | ||
25 | static const int IRRADIANCE_MAP_WIDTH = 1024; | ||
26 | static const int IRRADIANCE_MAP_HEIGHT = 1024; | ||
27 | static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128; | ||
28 | static const int PREFILTERED_ENVIRONMENT_MAP_HEIGHT = 128; | ||
29 | static const int BRDF_INTEGRATION_MAP_WIDTH = 512; | ||
30 | static const int BRDF_INTEGRATION_MAP_HEIGHT = 512; | ||
31 | |||
32 | bool renderer_make(Renderer* renderer, GfxCore* gfxcore) { | ||
33 | assert(renderer); | ||
34 | assert(gfxcore); | ||
35 | |||
36 | renderer->gfxcore = gfxcore; | ||
37 | |||
38 | return true; | ||
39 | } | ||
40 | |||
41 | void renderer_destroy(Renderer* renderer) { | ||
42 | if (!renderer) { | ||
43 | return; | ||
44 | } | ||
45 | assert(renderer->gfxcore); | ||
46 | GfxCore* gfxcore = renderer->gfxcore; | ||
47 | if (renderer->ibl) { | ||
48 | gfx_destroy_ibl(gfxcore, &renderer->ibl); | ||
49 | } | ||
50 | if (renderer->shaders.debug) { | ||
51 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.debug); | ||
52 | } | ||
53 | if (renderer->shaders.normals) { | ||
54 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.normals); | ||
55 | } | ||
56 | if (renderer->shaders.normal_mapped_normals) { | ||
57 | gfx_destroy_shader_program( | ||
58 | gfxcore, &renderer->shaders.normal_mapped_normals); | ||
59 | } | ||
60 | if (renderer->shaders.tangents) { | ||
61 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.tangents); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | /// Initialize renderer state for IBL if not already initialized. | ||
66 | static bool init_ibl(Renderer* renderer) { | ||
67 | assert(renderer); | ||
68 | |||
69 | if (!renderer->ibl && !(renderer->ibl = gfx_make_ibl(renderer->gfxcore))) { | ||
70 | return false; | ||
71 | } | ||
72 | |||
73 | if (!renderer->brdf_integration_map && | ||
74 | !(renderer->brdf_integration_map = gfx_make_brdf_integration_map( | ||
75 | renderer->ibl, renderer->gfxcore, BRDF_INTEGRATION_MAP_WIDTH, | ||
76 | BRDF_INTEGRATION_MAP_HEIGHT))) { | ||
77 | return false; | ||
78 | } | ||
79 | |||
80 | return true; | ||
81 | } | ||
82 | |||
83 | static ShaderProgram* load_shader(Renderer* renderer, RenderSceneMode mode) { | ||
84 | assert(renderer); | ||
85 | |||
86 | #define LOAD_AND_RETURN(pShader, constructor) \ | ||
87 | { \ | ||
88 | if (!pShader) { \ | ||
89 | pShader = constructor(renderer->gfxcore); \ | ||
90 | } \ | ||
91 | assert(pShader); \ | ||
92 | return pShader; \ | ||
93 | } | ||
94 | |||
95 | switch (mode) { | ||
96 | case RenderDefault: | ||
97 | return 0; | ||
98 | case RenderDebug: | ||
99 | LOAD_AND_RETURN(renderer->shaders.debug, gfx_make_debug3d_shader); | ||
100 | case RenderNormals: | ||
101 | LOAD_AND_RETURN(renderer->shaders.normals, gfx_make_view_normals_shader); | ||
102 | case RenderNormalMappedNormals: | ||
103 | LOAD_AND_RETURN( | ||
104 | renderer->shaders.normal_mapped_normals, | ||
105 | gfx_make_view_normal_mapped_normals_shader); | ||
106 | case RenderTangents: | ||
107 | LOAD_AND_RETURN(renderer->shaders.tangents, gfx_make_view_tangents_shader); | ||
108 | } | ||
109 | assert(false); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | // static void log_matrix(const mat4* m) { | ||
114 | // for (int row = 0; row < 4; ++row) { | ||
115 | // LOGI("[ %5.2f, %5.2f, %5.2f, %5.2f ]", m->val[0][row], m->val[1][row], | ||
116 | // m->val[2][row], m->val[3][row]); | ||
117 | // } | ||
118 | // } | ||
119 | |||
120 | /// Computes irradiance and prefiltered environment maps for the light if they | ||
121 | /// have not been already computed. | ||
122 | static bool setup_environment_light( | ||
123 | Renderer* renderer, GfxCore* gfxcore, EnvironmentLight* light) { | ||
124 | assert(renderer); | ||
125 | assert(light); | ||
126 | |||
127 | if (!init_ibl(renderer)) { | ||
128 | return false; | ||
129 | } | ||
130 | |||
131 | if (light->irradiance_map) { | ||
132 | assert(light->prefiltered_environment_map); | ||
133 | return true; | ||
134 | } | ||
135 | |||
136 | Texture* irradiance_map = 0; | ||
137 | Texture* prefiltered_environment_map = 0; | ||
138 | |||
139 | if (!(irradiance_map = gfx_make_irradiance_map( | ||
140 | renderer->ibl, gfxcore, light->environment_map, | ||
141 | IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT))) { | ||
142 | goto cleanup; | ||
143 | } | ||
144 | |||
145 | int max_mip_level = 0; | ||
146 | if (!(prefiltered_environment_map = gfx_make_prefiltered_environment_map( | ||
147 | renderer->ibl, gfxcore, light->environment_map, | ||
148 | PREFILTERED_ENVIRONMENT_MAP_WIDTH, | ||
149 | PREFILTERED_ENVIRONMENT_MAP_HEIGHT, &max_mip_level))) { | ||
150 | goto cleanup; | ||
151 | } | ||
152 | |||
153 | light->irradiance_map = irradiance_map; | ||
154 | light->prefiltered_environment_map = prefiltered_environment_map; | ||
155 | light->max_reflection_lod = max_mip_level; | ||
156 | |||
157 | return true; | ||
158 | |||
159 | cleanup: | ||
160 | if (irradiance_map) { | ||
161 | gfx_destroy_texture(gfxcore, &irradiance_map); | ||
162 | } | ||
163 | if (prefiltered_environment_map) { | ||
164 | gfx_destroy_texture(gfxcore, &prefiltered_environment_map); | ||
165 | } | ||
166 | return false; | ||
167 | } | ||
168 | |||
169 | typedef struct RenderState { | ||
170 | GfxCore* gfxcore; | ||
171 | Renderer* renderer; | ||
172 | ShaderProgram* shader; // Null to use scene shaders. | ||
173 | const Scene* scene; | ||
174 | const Camera* camera; | ||
175 | const mat4* camera_rotation; // From camera to world space, rotation only. | ||
176 | const mat4* view_matrix; | ||
177 | const mat4* projection; | ||
178 | const float fovy; | ||
179 | const float aspect; | ||
180 | Light* environment_light; | ||
181 | const Anima* anima; | ||
182 | size_t num_joints; | ||
183 | mat4 joint_matrices[GFX_MAX_NUM_JOINTS]; | ||
184 | } RenderState; | ||
185 | |||
186 | /// Load joint matrices into the render state. | ||
187 | static void load_skeleton(RenderState* state, skeleton_idx skeleton_index) { | ||
188 | assert(state); | ||
189 | assert(skeleton_index.val != 0); | ||
190 | |||
191 | const Skeleton* skeleton = mem_get_skeleton(skeleton_index); | ||
192 | assert(skeleton); | ||
193 | assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS); | ||
194 | |||
195 | state->num_joints = skeleton->num_joints; | ||
196 | |||
197 | for (size_t i = 0; i < skeleton->num_joints; ++i) { | ||
198 | const joint_idx joint_index = skeleton->joints[i]; | ||
199 | const Joint* joint = &state->anima->joints[joint_index]; | ||
200 | state->joint_matrices[i] = joint->joint_matrix; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | /// Draw the scene recursively. | ||
205 | static void draw_recursively( | ||
206 | RenderState* state, mat4 parent_transform, const SceneNode* node) { | ||
207 | assert(state); | ||
208 | const mat4 node_transform = mat4_mul(parent_transform, node->transform); | ||
209 | |||
210 | // Anima. | ||
211 | if (node->type == AnimaNode) { | ||
212 | state->anima = gfx_get_node_anima(node); | ||
213 | } | ||
214 | // Activate light. | ||
215 | else if (node->type == LightNode) { | ||
216 | Light* light = mem_get_light(node->light); | ||
217 | assert(light); | ||
218 | |||
219 | if (light->type == EnvironmentLightType) { | ||
220 | bool result = setup_environment_light( | ||
221 | state->renderer, state->gfxcore, &light->environment); | ||
222 | // TODO: Handle the result in a better way. | ||
223 | assert(result); | ||
224 | state->environment_light = light; | ||
225 | } | ||
226 | } | ||
227 | // Model. | ||
228 | else if (node->type == ModelNode) { | ||
229 | const Model* model = gfx_get_node_model(node); | ||
230 | const SceneNode* root = mem_get_node(model->root); | ||
231 | draw_recursively(state, parent_transform, root); | ||
232 | } | ||
233 | // Render object. | ||
234 | else if (node->type == ObjectNode) { | ||
235 | const SceneObject* object = mem_get_object(node->object); | ||
236 | assert(object); | ||
237 | |||
238 | // TODO: Here we would frustum-cull the object. | ||
239 | |||
240 | // TODO: Avoid computing matrices like Modelview or MVP if the shader does | ||
241 | // not use them. | ||
242 | const mat4 model_matrix = node_transform; | ||
243 | const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix); | ||
244 | const mat4 mvp = mat4_mul(*state->projection, modelview); | ||
245 | |||
246 | if (object->skeleton.val) { | ||
247 | load_skeleton(state, object->skeleton); | ||
248 | } | ||
249 | |||
250 | for (mesh_link_idx mesh_link_index = object->mesh_link; | ||
251 | mesh_link_index.val;) { | ||
252 | const MeshLink* mesh_link = mem_get_mesh_link(mesh_link_index); | ||
253 | mesh_link_index = mesh_link->next; | ||
254 | |||
255 | const Mesh* mesh = mem_get_mesh(mesh_link->mesh); | ||
256 | if (!mesh) { | ||
257 | continue; | ||
258 | } | ||
259 | assert(mesh->geometry); | ||
260 | assert(mesh->material); | ||
261 | |||
262 | // TODO: Here we would frustum-cull the mesh. The AABB would have to be | ||
263 | // transformed by the model matrix. Rotation would make the AABB | ||
264 | // relatively large, but still, the culling would be conservative. | ||
265 | |||
266 | // Apply common shader uniforms not captured by materials. | ||
267 | ShaderProgram* shader = state->shader ? state->shader : mesh->shader; | ||
268 | gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix); | ||
269 | gfx_set_mat4_uniform(shader, "Modelview", &modelview); | ||
270 | gfx_set_mat4_uniform(shader, "View", state->view_matrix); | ||
271 | gfx_set_mat4_uniform(shader, "Projection", state->projection); | ||
272 | gfx_set_mat4_uniform(shader, "MVP", &mvp); | ||
273 | gfx_set_mat4_uniform(shader, "CameraRotation", state->camera_rotation); | ||
274 | gfx_set_float_uniform(shader, "Fovy", state->fovy); | ||
275 | gfx_set_float_uniform(shader, "Aspect", state->aspect); | ||
276 | if (state->camera) { | ||
277 | gfx_set_vec3_uniform( | ||
278 | shader, "CameraPosition", state->camera->spatial.p); | ||
279 | } | ||
280 | if (state->num_joints > 0) { | ||
281 | gfx_set_mat4_array_uniform( | ||
282 | shader, "JointMatrices", state->joint_matrices, state->num_joints); | ||
283 | } | ||
284 | // Apply lights. | ||
285 | if (state->environment_light) { | ||
286 | const EnvironmentLight* light = &state->environment_light->environment; | ||
287 | assert(light->environment_map); | ||
288 | assert(light->irradiance_map); | ||
289 | assert(light->prefiltered_environment_map); | ||
290 | assert(state->renderer->brdf_integration_map); | ||
291 | gfx_set_texture_uniform( | ||
292 | shader, "BRDFIntegrationMap", | ||
293 | state->renderer->brdf_integration_map); | ||
294 | gfx_set_texture_uniform(shader, "Sky", light->environment_map); | ||
295 | gfx_set_texture_uniform(shader, "IrradianceMap", light->irradiance_map); | ||
296 | gfx_set_texture_uniform( | ||
297 | shader, "PrefilteredEnvironmentMap", | ||
298 | light->prefiltered_environment_map); | ||
299 | gfx_set_float_uniform( | ||
300 | shader, "MaxReflectionLOD", light->max_reflection_lod); | ||
301 | } | ||
302 | material_activate(shader, mesh->material); | ||
303 | gfx_activate_shader_program(shader); | ||
304 | gfx_apply_uniforms(shader); | ||
305 | gfx_render_geometry(mesh->geometry); | ||
306 | } | ||
307 | |||
308 | // Reset state for next object. | ||
309 | state->num_joints = 0; | ||
310 | } | ||
311 | |||
312 | // Render children recursively. | ||
313 | for (node_idx child_index = node->child; child_index.val;) { | ||
314 | const SceneNode* child = mem_get_node(child_index); | ||
315 | draw_recursively(state, node_transform, child); | ||
316 | child_index = child->next; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) { | ||
321 | assert(renderer); | ||
322 | assert(params); | ||
323 | assert(params->scene); | ||
324 | |||
325 | ShaderProgram* const shader = load_shader(renderer, params->mode); | ||
326 | |||
327 | const Scene* scene = params->scene; | ||
328 | const SceneCamera* camera = params->camera; | ||
329 | |||
330 | GfxCore* gfxcore = renderer->gfxcore; | ||
331 | |||
332 | mat4 projection, camera_rotation, view_matrix; | ||
333 | if (camera) { | ||
334 | projection = camera->camera.projection; | ||
335 | camera_rotation = | ||
336 | mat4_rotation(spatial3_transform(&camera->camera.spatial)); | ||
337 | view_matrix = spatial3_inverse_transform(&camera->camera.spatial); | ||
338 | } else { | ||
339 | projection = mat4_id(); | ||
340 | camera_rotation = mat4_id(); | ||
341 | view_matrix = mat4_id(); | ||
342 | } | ||
343 | |||
344 | int x, y, width, height; | ||
345 | gfx_get_viewport(gfxcore, &x, &y, &width, &height); | ||
346 | const float aspect = (float)width / (float)height; | ||
347 | |||
348 | RenderState state = { | ||
349 | .gfxcore = gfxcore, | ||
350 | .renderer = renderer, | ||
351 | .shader = shader, | ||
352 | .scene = scene, | ||
353 | .camera = &camera->camera, | ||
354 | .camera_rotation = &camera_rotation, | ||
355 | .view_matrix = &view_matrix, | ||
356 | .projection = &projection, | ||
357 | .environment_light = 0, | ||
358 | // Assuming a perspective matrix. | ||
359 | .fovy = atan(1.0 / (mat4_at(projection, 1, 1))) * 2, | ||
360 | .aspect = aspect}; | ||
361 | |||
362 | draw_recursively(&state, mat4_id(), scene->root); | ||
363 | } | ||
364 | |||
365 | static void update_rec(SceneNode* node, const SceneCamera* camera, R t) { | ||
366 | assert(node); | ||
367 | assert(camera); | ||
368 | |||
369 | const NodeType node_type = gfx_get_node_type(node); | ||
370 | |||
371 | // TODO: Models do not need to be animated if they are not visible to the | ||
372 | // camera. | ||
373 | if (node_type == AnimaNode) { | ||
374 | Anima* anima = gfx_get_node_anima_mut(node); | ||
375 | gfx_update_animation(anima, (R)t); | ||
376 | } else if (node_type == ModelNode) { | ||
377 | Model* model = gfx_get_node_model_mut(node); | ||
378 | SceneNode* root = gfx_get_model_root_mut(model); | ||
379 | update_rec(root, camera, t); | ||
380 | } | ||
381 | |||
382 | // Children. | ||
383 | SceneNode* child = gfx_get_node_child_mut(node); | ||
384 | while (child) { | ||
385 | update_rec(child, camera, t); | ||
386 | child = gfx_get_node_sibling_mut(child); | ||
387 | } | ||
388 | } | ||
389 | |||
390 | void gfx_update(Scene* scene, const SceneCamera* camera, R t) { | ||
391 | assert(scene); | ||
392 | assert(camera); | ||
393 | |||
394 | SceneNode* node = gfx_get_scene_root(scene); | ||
395 | update_rec(node, camera, t); | ||
396 | } | ||
diff --git a/gfx/src/scene/material_impl.h b/gfx/src/scene/material_impl.h deleted file mode 100644 index a6aa95b..0000000 --- a/gfx/src/scene/material_impl.h +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <gfx/scene/material.h> | ||
4 | |||
5 | typedef struct ShaderProgram ShaderProgram; | ||
6 | |||
7 | typedef struct Material { | ||
8 | ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; | ||
9 | int num_uniforms; | ||
10 | } Material; | ||
11 | |||
12 | /// Activate the material. | ||
13 | /// | ||
14 | /// This activates the material's shader and configures the shader uniforms that | ||
15 | /// are specific to the material. | ||
16 | void material_activate(ShaderProgram* shader, const Material* material); | ||
diff --git a/gfx/include/gfx/asset.h b/include/gfx/asset.h index caf40c1..caf40c1 100644 --- a/gfx/include/gfx/asset.h +++ b/include/gfx/asset.h | |||
diff --git a/gfx/include/gfx/core.h b/include/gfx/core.h index 44509c9..0cf4465 100644 --- a/gfx/include/gfx/core.h +++ b/include/gfx/core.h | |||
@@ -219,6 +219,7 @@ typedef enum { Texture2D, TextureCubeMap } TextureDimension; | |||
219 | /// Texture data format. | 219 | /// Texture data format. |
220 | typedef enum { | 220 | typedef enum { |
221 | TextureDepth, | 221 | TextureDepth, |
222 | TextureR8, | ||
222 | TextureRG16, | 223 | TextureRG16, |
223 | TextureRG16F, | 224 | TextureRG16F, |
224 | TextureRGB8, | 225 | TextureRGB8, |
@@ -473,6 +474,9 @@ void gfx_deactivate_shader_program(const ShaderProgram*); | |||
473 | /// | 474 | /// |
474 | /// This function should be called after setting all of the uniform variables | 475 | /// This function should be called after setting all of the uniform variables |
475 | /// and prior to issuing a draw call. | 476 | /// and prior to issuing a draw call. |
477 | /// | ||
478 | /// The given program must have been activated prior to this call with | ||
479 | /// gfx_activate_shader_program(). | ||
476 | void gfx_apply_uniforms(const ShaderProgram*); | 480 | void gfx_apply_uniforms(const ShaderProgram*); |
477 | 481 | ||
478 | /// Set the texture uniform. | 482 | /// Set the texture uniform. |
diff --git a/gfx/include/gfx/gfx.h b/include/gfx/gfx.h index 7c670a5..d5c25b6 100644 --- a/gfx/include/gfx/gfx.h +++ b/include/gfx/gfx.h | |||
@@ -3,6 +3,7 @@ | |||
3 | typedef struct AssetCache AssetCache; | 3 | typedef struct AssetCache AssetCache; |
4 | typedef struct GfxCore GfxCore; | 4 | typedef struct GfxCore GfxCore; |
5 | typedef struct ImmRenderer ImmRenderer; | 5 | typedef struct ImmRenderer ImmRenderer; |
6 | typedef struct LLR LLR; | ||
6 | typedef struct Renderer Renderer; | 7 | typedef struct Renderer Renderer; |
7 | 8 | ||
8 | typedef struct Gfx Gfx; | 9 | typedef struct Gfx Gfx; |
@@ -16,12 +17,15 @@ void gfx_destroy(Gfx**); | |||
16 | /// Get the render backend. | 17 | /// Get the render backend. |
17 | GfxCore* gfx_get_core(Gfx*); | 18 | GfxCore* gfx_get_core(Gfx*); |
18 | 19 | ||
19 | /// Get the renderer. | 20 | /// Get the scene renderer. |
20 | Renderer* gfx_get_renderer(Gfx*); | 21 | Renderer* gfx_get_renderer(Gfx*); |
21 | 22 | ||
22 | /// Get the immediate mode renderer. | 23 | /// Get the immediate mode renderer. |
23 | ImmRenderer* gfx_get_imm_renderer(Gfx*); | 24 | ImmRenderer* gfx_get_imm_renderer(Gfx*); |
24 | 25 | ||
26 | /// Get the low-level renderer. | ||
27 | LLR* gfx_get_llr(Gfx*); | ||
28 | |||
25 | /// Get the asset cache. | 29 | /// Get the asset cache. |
26 | AssetCache* gfx_get_asset_cache(Gfx*); | 30 | AssetCache* gfx_get_asset_cache(Gfx*); |
27 | 31 | ||
diff --git a/gfx/include/gfx/scene/light.h b/include/gfx/llr/light.h index 132e344..132e344 100644 --- a/gfx/include/gfx/scene/light.h +++ b/include/gfx/llr/light.h | |||
diff --git a/include/gfx/llr/llr.h b/include/gfx/llr/llr.h new file mode 100644 index 0000000..77df33f --- /dev/null +++ b/include/gfx/llr/llr.h | |||
@@ -0,0 +1,67 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <math/camera.h> | ||
4 | #include <math/mat4.h> | ||
5 | #include <math/vec3.h> | ||
6 | |||
7 | typedef struct Anima Anima; | ||
8 | typedef struct Geometry Geometry; | ||
9 | typedef struct Light Light; | ||
10 | typedef struct Mesh Mesh; | ||
11 | typedef struct ShaderProgram ShaderProgram; | ||
12 | typedef struct Skeleton Skeleton; | ||
13 | |||
14 | typedef struct LLR LLR; | ||
15 | |||
16 | /// Set the shader to be used for subsequent draw calls. | ||
17 | /// The shader is not yet activated at this point. | ||
18 | void gfx_llr_set_shader(LLR*, ShaderProgram*); | ||
19 | |||
20 | /// Push a light into the lights stack. | ||
21 | void gfx_llr_push_light(LLR*, Light*); | ||
22 | |||
23 | /// Pop the last light from the lights stack. | ||
24 | void gfx_llr_pop_light(LLR*); | ||
25 | |||
26 | /// Load a skeleton. | ||
27 | /// | ||
28 | /// If a skeleton is loaded, subsequent meshes are rendered with joint data | ||
29 | /// passed to the shader. This has a cost, so if subsequent meshes are not | ||
30 | /// animated, unload the skeleton prior to rendering them. | ||
31 | void gfx_llr_set_skeleton(LLR*, const Anima*, const Skeleton*); | ||
32 | |||
33 | /// Clear the loaded skeleton. | ||
34 | void gfx_llr_clear_skeleton(LLR*); | ||
35 | |||
36 | /// Set the camera. | ||
37 | void gfx_llr_set_camera(LLR*, const Camera*); | ||
38 | |||
39 | /// Set the view-projection matrix. | ||
40 | // void gfx_llr_set_view_projection_matrix(LLR*, const mat4*); | ||
41 | |||
42 | /// Set the aspect ratio. | ||
43 | void gfx_llr_set_aspect(LLR*, float aspect); | ||
44 | |||
45 | /// Render the geometry. | ||
46 | void gfx_llr_render_geometry(LLR*, const Geometry*); | ||
47 | |||
48 | /// Render the mesh. | ||
49 | void gfx_llr_render_mesh(LLR*, const Mesh*); | ||
50 | |||
51 | // ----------------------------------------------------------------------------- | ||
52 | // Matrix stack manipulation. | ||
53 | |||
54 | /// Load an identity model matrix. Clears the matrix stack. | ||
55 | void gfx_llr_load_identity(LLR* renderer); | ||
56 | |||
57 | /// Push the given matrix to the matrix stack. | ||
58 | void gfx_llr_push_matrix(LLR* renderer, const mat4* matrix); | ||
59 | |||
60 | /// Pop the top of the matrix stack. | ||
61 | void gfx_llr_pop_matrix(LLR* renderer); | ||
62 | |||
63 | /// Push a translation matrix to the matrix stack. | ||
64 | void gfx_llr_translate(LLR* renderer, vec3 offset); | ||
65 | |||
66 | /// Set the model matrix. Clears the matrix stack. | ||
67 | void gfx_llr_set_model_matrix(LLR*, const mat4*); | ||
diff --git a/gfx/include/gfx/scene/material.h b/include/gfx/llr/material.h index bca664e..bca664e 100644 --- a/gfx/include/gfx/scene/material.h +++ b/include/gfx/llr/material.h | |||
diff --git a/gfx/include/gfx/scene/mesh.h b/include/gfx/llr/mesh.h index 0d3b4d4..0d3b4d4 100644 --- a/gfx/include/gfx/scene/mesh.h +++ b/include/gfx/llr/mesh.h | |||
diff --git a/include/gfx/renderer.h b/include/gfx/renderer.h new file mode 100644 index 0000000..1da74eb --- /dev/null +++ b/include/gfx/renderer.h | |||
@@ -0,0 +1,29 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <math/defs.h> | ||
4 | |||
5 | typedef struct GfxCore GfxCore; | ||
6 | typedef struct Scene Scene; | ||
7 | typedef struct SceneCamera SceneCamera; | ||
8 | |||
9 | typedef struct Renderer Renderer; | ||
10 | |||
11 | typedef enum RenderSceneMode { | ||
12 | RenderDefault, | ||
13 | RenderDebug, | ||
14 | RenderNormals, | ||
15 | RenderNormalMappedNormals, | ||
16 | RenderTangents | ||
17 | } RenderSceneMode; | ||
18 | |||
19 | typedef struct RenderSceneParams { | ||
20 | RenderSceneMode mode; | ||
21 | const Scene* scene; | ||
22 | const SceneCamera* camera; | ||
23 | } RenderSceneParams; | ||
24 | |||
25 | /// Render the scene. | ||
26 | void gfx_render_scene(Renderer*, const RenderSceneParams*); | ||
27 | |||
28 | /// Update the scene. | ||
29 | void gfx_update(Scene*, const SceneCamera*, R t); | ||
diff --git a/include/gfx/renderer/imm_renderer.h b/include/gfx/renderer/imm_renderer.h new file mode 100644 index 0000000..db4d290 --- /dev/null +++ b/include/gfx/renderer/imm_renderer.h | |||
@@ -0,0 +1,55 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <math/aabb2.h> | ||
4 | #include <math/aabb3.h> | ||
5 | #include <math/camera.h> | ||
6 | #include <math/mat4.h> | ||
7 | #include <math/vec3.h> | ||
8 | #include <math/vec4.h> | ||
9 | |||
10 | typedef struct ImmRenderer ImmRenderer; | ||
11 | |||
12 | /// Prepare the graphics systems for immediate-mode rendering. | ||
13 | /// | ||
14 | /// Call this before issuing any immediate-mode rendering draws. | ||
15 | void gfx_imm_start(ImmRenderer*); | ||
16 | |||
17 | /// End immediate mode rendering. | ||
18 | /// | ||
19 | /// Call this after issuing immediate-mode rendering draws and before swapping | ||
20 | /// buffers. | ||
21 | void gfx_imm_end(ImmRenderer*); | ||
22 | |||
23 | /// Flush draw commands. | ||
24 | /// | ||
25 | /// This should be done when changing any state that may affect the rendering of | ||
26 | /// primitives; for example, LLR matrix stack changes. | ||
27 | void gfx_imm_flush(ImmRenderer*); | ||
28 | |||
29 | /// Draw a set of triangles. | ||
30 | void gfx_imm_draw_triangles(ImmRenderer*, const vec3[], size_t num_triangles); | ||
31 | |||
32 | /// Draw a triangle. | ||
33 | void gfx_imm_draw_triangle(ImmRenderer*, const vec3[3]); | ||
34 | |||
35 | /// Draw a bounding box. | ||
36 | void gfx_imm_draw_aabb2(ImmRenderer*, aabb2); | ||
37 | |||
38 | /// Draw a bounding box. | ||
39 | void gfx_imm_draw_aabb3(ImmRenderer*, aabb3); | ||
40 | |||
41 | /// Draw a box. | ||
42 | /// | ||
43 | /// The vertices must be given in the following order: | ||
44 | /// | ||
45 | /// 7 ----- 6 | ||
46 | /// / /| | ||
47 | /// 3 ----- 2 | | ||
48 | /// | | | | ||
49 | /// | 4 ----- 5 | ||
50 | /// |/ |/ | ||
51 | /// 0 ----- 1 | ||
52 | void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]); | ||
53 | |||
54 | /// Set the render colour. | ||
55 | void gfx_imm_set_colour(ImmRenderer*, vec4 colour); | ||
diff --git a/gfx/include/gfx/scene.h b/include/gfx/scene.h index abcaa70..37a7e0b 100644 --- a/gfx/include/gfx/scene.h +++ b/include/gfx/scene.h | |||
@@ -1,10 +1,11 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | // TODO: Remove references to gfx/llr once the transition is complete. | ||
4 | #include <gfx/llr/light.h> | ||
5 | #include <gfx/llr/material.h> | ||
6 | #include <gfx/llr/mesh.h> | ||
3 | #include <gfx/scene/animation.h> | 7 | #include <gfx/scene/animation.h> |
4 | #include <gfx/scene/camera.h> | 8 | #include <gfx/scene/camera.h> |
5 | #include <gfx/scene/light.h> | ||
6 | #include <gfx/scene/material.h> | ||
7 | #include <gfx/scene/mesh.h> | ||
8 | #include <gfx/scene/model.h> | 9 | #include <gfx/scene/model.h> |
9 | #include <gfx/scene/node.h> | 10 | #include <gfx/scene/node.h> |
10 | #include <gfx/scene/object.h> | 11 | #include <gfx/scene/object.h> |
diff --git a/gfx/include/gfx/scene/animation.h b/include/gfx/scene/animation.h index d95b895..3ef0471 100644 --- a/gfx/include/gfx/scene/animation.h +++ b/include/gfx/scene/animation.h | |||
@@ -1,6 +1,5 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include "node.h" | ||
4 | #include "object.h" | 3 | #include "object.h" |
5 | #include <gfx/sizes.h> | 4 | #include <gfx/sizes.h> |
6 | 5 | ||
@@ -15,8 +14,7 @@ | |||
15 | #include <stddef.h> | 14 | #include <stddef.h> |
16 | #include <stdint.h> | 15 | #include <stdint.h> |
17 | 16 | ||
18 | typedef struct Buffer Buffer; | 17 | typedef struct Buffer Buffer; |
19 | typedef struct SceneNode SceneNode; | ||
20 | 18 | ||
21 | typedef struct Anima Anima; | 19 | typedef struct Anima Anima; |
22 | typedef struct Joint Joint; | 20 | typedef struct Joint Joint; |
diff --git a/gfx/include/gfx/scene/camera.h b/include/gfx/scene/camera.h index 99d83fe..99d83fe 100644 --- a/gfx/include/gfx/scene/camera.h +++ b/include/gfx/scene/camera.h | |||
diff --git a/gfx/include/gfx/scene/model.h b/include/gfx/scene/model.h index 42f85d4..42f85d4 100644 --- a/gfx/include/gfx/scene/model.h +++ b/include/gfx/scene/model.h | |||
diff --git a/gfx/include/gfx/scene/node.h b/include/gfx/scene/node.h index a2c2836..193eb25 100644 --- a/gfx/include/gfx/scene/node.h +++ b/include/gfx/scene/node.h | |||
@@ -1,12 +1,8 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include "animation.h" | ||
4 | |||
5 | #include <math/fwd.h> | 3 | #include <math/fwd.h> |
6 | #include <math/mat4.h> | 4 | #include <math/mat4.h> |
7 | 5 | ||
8 | #include <stdint.h> | ||
9 | |||
10 | typedef struct Anima Anima; | 6 | typedef struct Anima Anima; |
11 | typedef struct Light Light; | 7 | typedef struct Light Light; |
12 | typedef struct Model Model; | 8 | typedef struct Model Model; |
diff --git a/gfx/include/gfx/scene/object.h b/include/gfx/scene/object.h index 7579d29..7579d29 100644 --- a/gfx/include/gfx/scene/object.h +++ b/include/gfx/scene/object.h | |||
diff --git a/gfx/include/gfx/scene/scene.h b/include/gfx/scene/scene.h index 0d96210..0d96210 100644 --- a/gfx/include/gfx/scene/scene.h +++ b/include/gfx/scene/scene.h | |||
diff --git a/gfx/include/gfx/sizes.h b/include/gfx/sizes.h index 076113c..3eb7481 100644 --- a/gfx/include/gfx/sizes.h +++ b/include/gfx/sizes.h | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | // Scene. | 4 | // Scene. |
5 | 5 | ||
6 | /// Maximum number of cameras per scene. | 6 | /// Maximum number of cameras. |
7 | #define GFX_MAX_NUM_CAMERAS 16 | 7 | #define GFX_MAX_NUM_CAMERAS 16 |
8 | 8 | ||
9 | /// Maximum number of lights. | 9 | /// Maximum number of lights. |
@@ -31,10 +31,10 @@ | |||
31 | #define GFX_MAX_NUM_CHANNELS 128 | 31 | #define GFX_MAX_NUM_CHANNELS 128 |
32 | 32 | ||
33 | /// Maximum number of skeletons. | 33 | /// Maximum number of skeletons. |
34 | #define GFX_MAX_NUM_SKELETONS 128 | 34 | #define GFX_MAX_NUM_SKELETONS 8 |
35 | 35 | ||
36 | /// Maximum number of animations. | 36 | /// Maximum number of animations. |
37 | #define GFX_MAX_NUM_ANIMATIONS 128 | 37 | #define GFX_MAX_NUM_ANIMATIONS 32 |
38 | 38 | ||
39 | /// Maximum number of animas. | 39 | /// Maximum number of animas. |
40 | #define GFX_MAX_NUM_ANIMAS 128 | 40 | #define GFX_MAX_NUM_ANIMAS 128 |
@@ -77,16 +77,22 @@ | |||
77 | /// Maximum number of compiler defines in a Shader. | 77 | /// Maximum number of compiler defines in a Shader. |
78 | #define GFX_MAX_SHADER_COMPILER_DEFINES 16 | 78 | #define GFX_MAX_SHADER_COMPILER_DEFINES 16 |
79 | 79 | ||
80 | // Renderer. | 80 | // Low-level renderer. |
81 | |||
82 | /// Maximum number of lights that the low-level renderer can enable per rendered | ||
83 | /// mesh. | ||
84 | #define GFX_LLR_MAX_NUM_LIGHTS 8 | ||
85 | |||
86 | /// Maximum number of matrices in the low-level renderer's matrix stack. | ||
87 | #define GFX_LLR_MAX_NUM_MATRICES 32 | ||
88 | |||
89 | // Immediate-mode Renderer. | ||
81 | 90 | ||
82 | /// Maximum number of triangles that the immediate-mode renderer can draw in a | 91 | /// Maximum number of triangles that the immediate-mode renderer can draw in a |
83 | /// frame. | 92 | /// frame. |
84 | #define IMM_MAX_NUM_TRIANGLES 1024 | 93 | #define GFX_IMM_MAX_NUM_TRIANGLES 1024 |
85 | |||
86 | /// Maximum number of matrices in the immediate-mode renderer's matrix stack. | ||
87 | #define IMM_MAX_NUM_MATRICES 32 | ||
88 | 94 | ||
89 | // Asset Manager. | 95 | // Asset Cache. |
90 | 96 | ||
91 | #define GFX_MAX_NUM_ASSETS 1024 | 97 | #define GFX_MAX_NUM_ASSETS 1024 |
92 | 98 | ||
diff --git a/gfx/include/gfx/util/geometry.h b/include/gfx/util/geometry.h index a962291..a962291 100644 --- a/gfx/include/gfx/util/geometry.h +++ b/include/gfx/util/geometry.h | |||
diff --git a/gfx/include/gfx/util/ibl.h b/include/gfx/util/ibl.h index 6e39180..6e39180 100644 --- a/gfx/include/gfx/util/ibl.h +++ b/include/gfx/util/ibl.h | |||
diff --git a/gfx/include/gfx/util/shader.h b/include/gfx/util/shader.h index bd058f4..bd058f4 100644 --- a/gfx/include/gfx/util/shader.h +++ b/include/gfx/util/shader.h | |||
diff --git a/gfx/include/gfx/util/skyquad.h b/include/gfx/util/skyquad.h index 2b3fe17..2b3fe17 100644 --- a/gfx/include/gfx/util/skyquad.h +++ b/include/gfx/util/skyquad.h | |||
diff --git a/gfx/shaders/brdf_integration_map.frag b/shaders/brdf_integration_map.frag index bb2cebd..bb2cebd 100644 --- a/gfx/shaders/brdf_integration_map.frag +++ b/shaders/brdf_integration_map.frag | |||
diff --git a/gfx/shaders/cook_torrance.frag b/shaders/cook_torrance.frag index 1975491..1975491 100644 --- a/gfx/shaders/cook_torrance.frag +++ b/shaders/cook_torrance.frag | |||
diff --git a/gfx/shaders/cook_torrance.vert b/shaders/cook_torrance.vert index 5f126c0..5f126c0 100644 --- a/gfx/shaders/cook_torrance.vert +++ b/shaders/cook_torrance.vert | |||
diff --git a/gfx/shaders/cubemap_filtering.vert b/shaders/cubemap_filtering.vert index d0cf73f..d0cf73f 100644 --- a/gfx/shaders/cubemap_filtering.vert +++ b/shaders/cubemap_filtering.vert | |||
diff --git a/gfx/shaders/debug3d.frag b/shaders/debug3d.frag index 54568d4..54568d4 100644 --- a/gfx/shaders/debug3d.frag +++ b/shaders/debug3d.frag | |||
diff --git a/gfx/shaders/debug3d.vert b/shaders/debug3d.vert index d51684f..d51684f 100644 --- a/gfx/shaders/debug3d.vert +++ b/shaders/debug3d.vert | |||
diff --git a/gfx/shaders/immediate_mode.frag b/shaders/immediate_mode.frag index ac23b5c..ac23b5c 100644 --- a/gfx/shaders/immediate_mode.frag +++ b/shaders/immediate_mode.frag | |||
diff --git a/gfx/shaders/immediate_mode.vert b/shaders/immediate_mode.vert index 65070bb..65070bb 100644 --- a/gfx/shaders/immediate_mode.vert +++ b/shaders/immediate_mode.vert | |||
diff --git a/gfx/shaders/irradiance_map.frag b/shaders/irradiance_map.frag index 8200e73..8200e73 100644 --- a/gfx/shaders/irradiance_map.frag +++ b/shaders/irradiance_map.frag | |||
diff --git a/gfx/shaders/prefiltered_environment_map.frag b/shaders/prefiltered_environment_map.frag index 8327950..8327950 100644 --- a/gfx/shaders/prefiltered_environment_map.frag +++ b/shaders/prefiltered_environment_map.frag | |||
diff --git a/gfx/shaders/quad.vert b/shaders/quad.vert index ef1834c..ef1834c 100644 --- a/gfx/shaders/quad.vert +++ b/shaders/quad.vert | |||
diff --git a/gfx/shaders/skyquad.frag b/shaders/skyquad.frag index 9b44bfd..9b44bfd 100644 --- a/gfx/shaders/skyquad.frag +++ b/shaders/skyquad.frag | |||
diff --git a/gfx/shaders/skyquad.vert b/shaders/skyquad.vert index c0c46e6..c0c46e6 100644 --- a/gfx/shaders/skyquad.vert +++ b/shaders/skyquad.vert | |||
diff --git a/gfx/shaders/view_normal_mapped_normals.frag b/shaders/view_normal_mapped_normals.frag index a372c02..a372c02 100644 --- a/gfx/shaders/view_normal_mapped_normals.frag +++ b/shaders/view_normal_mapped_normals.frag | |||
diff --git a/gfx/shaders/view_normal_mapped_normals.vert b/shaders/view_normal_mapped_normals.vert index 004ed9a..004ed9a 100644 --- a/gfx/shaders/view_normal_mapped_normals.vert +++ b/shaders/view_normal_mapped_normals.vert | |||
diff --git a/gfx/shaders/view_normals.frag b/shaders/view_normals.frag index e90189c..e90189c 100644 --- a/gfx/shaders/view_normals.frag +++ b/shaders/view_normals.frag | |||
diff --git a/gfx/shaders/view_normals.vert b/shaders/view_normals.vert index d51684f..d51684f 100644 --- a/gfx/shaders/view_normals.vert +++ b/shaders/view_normals.vert | |||
diff --git a/gfx/shaders/view_tangents.frag b/shaders/view_tangents.frag index 11d1455..11d1455 100644 --- a/gfx/shaders/view_tangents.frag +++ b/shaders/view_tangents.frag | |||
diff --git a/gfx/shaders/view_tangents.vert b/shaders/view_tangents.vert index 561ad22..561ad22 100644 --- a/gfx/shaders/view_tangents.vert +++ b/shaders/view_tangents.vert | |||
diff --git a/gfx/shaders/view_texture.frag b/shaders/view_texture.frag index 12fa367..12fa367 100644 --- a/gfx/shaders/view_texture.frag +++ b/shaders/view_texture.frag | |||
diff --git a/gfx/shaders/view_texture.vert b/shaders/view_texture.vert index 4e3c7d7..4e3c7d7 100644 --- a/gfx/shaders/view_texture.vert +++ b/shaders/view_texture.vert | |||
diff --git a/gfx/src/asset/asset_cache.c b/src/asset/asset_cache.c index 16c4d5c..727b63f 100644 --- a/gfx/src/asset/asset_cache.c +++ b/src/asset/asset_cache.c | |||
@@ -179,6 +179,7 @@ static Model* clone_model(const Model* model) { | |||
179 | 179 | ||
180 | void gfx_init_asset_cache(AssetCache* cache) { | 180 | void gfx_init_asset_cache(AssetCache* cache) { |
181 | assert(cache); | 181 | assert(cache); |
182 | |||
182 | mempool_make(&cache->assets); | 183 | mempool_make(&cache->assets); |
183 | 184 | ||
184 | // Allocate a dummy asset at index 0 to guarantee that no assets allocated by | 185 | // Allocate a dummy asset at index 0 to guarantee that no assets allocated by |
@@ -189,6 +190,7 @@ void gfx_init_asset_cache(AssetCache* cache) { | |||
189 | 190 | ||
190 | void gfx_destroy_asset_cache(AssetCache* cache) { | 191 | void gfx_destroy_asset_cache(AssetCache* cache) { |
191 | assert(cache); | 192 | assert(cache); |
193 | // TODO: Destroy assets here. | ||
192 | mempool_del(&cache->assets); | 194 | mempool_del(&cache->assets); |
193 | } | 195 | } |
194 | 196 | ||
diff --git a/gfx/src/asset/asset_cache.h b/src/asset/asset_cache.h index b2a35ed..b2a35ed 100644 --- a/gfx/src/asset/asset_cache.h +++ b/src/asset/asset_cache.h | |||
diff --git a/gfx/src/asset/model.c b/src/asset/model.c index 25f2780..0c57470 100644 --- a/gfx/src/asset/model.c +++ b/src/asset/model.c | |||
@@ -84,13 +84,12 @@ | |||
84 | #include "asset/texture.h" | 84 | #include "asset/texture.h" |
85 | #include "gfx/core.h" | 85 | #include "gfx/core.h" |
86 | #include "gfx/gfx.h" | 86 | #include "gfx/gfx.h" |
87 | #include "gfx/llr/material.h" | ||
88 | #include "gfx/llr/mesh.h" | ||
87 | #include "gfx/scene/animation.h" | 89 | #include "gfx/scene/animation.h" |
88 | #include "gfx/scene/camera.h" | 90 | #include "gfx/scene/camera.h" |
89 | #include "gfx/scene/material.h" | ||
90 | #include "gfx/scene/mesh.h" | ||
91 | #include "gfx/scene/node.h" | 91 | #include "gfx/scene/node.h" |
92 | #include "gfx/scene/object.h" | 92 | #include "gfx/scene/object.h" |
93 | #include "gfx/scene/scene.h" | ||
94 | #include "gfx/sizes.h" | 93 | #include "gfx/sizes.h" |
95 | #include "gfx/util/shader.h" | 94 | #include "gfx/util/shader.h" |
96 | 95 | ||
@@ -578,11 +577,10 @@ static bool load_buffers( | |||
578 | const cgltf_buffer* buffer = &data->buffers[i]; | 577 | const cgltf_buffer* buffer = &data->buffers[i]; |
579 | assert(buffer->data); | 578 | assert(buffer->data); |
580 | buffers[i] = gfx_make_buffer( | 579 | buffers[i] = gfx_make_buffer( |
581 | gfxcore, &(BufferDesc){ | 580 | gfxcore, &(BufferDesc){.usage = BufferStatic, |
582 | .usage = BufferStatic, | 581 | .type = BufferUntyped, |
583 | .type = BufferUntyped, | 582 | .data.data = buffer->data, |
584 | .data.data = buffer->data, | 583 | .data.count = buffer->size}); |
585 | .data.count = buffer->size}); | ||
586 | if (!buffers[i]) { | 584 | if (!buffers[i]) { |
587 | return false; | 585 | return false; |
588 | } | 586 | } |
@@ -604,11 +602,10 @@ static bool load_tangent_buffers( | |||
604 | const cgltfTangentBuffer* buffer = &cgltf_tangent_buffers[i]; | 602 | const cgltfTangentBuffer* buffer = &cgltf_tangent_buffers[i]; |
605 | assert(buffer->data); | 603 | assert(buffer->data); |
606 | tangent_buffers[i] = gfx_make_buffer( | 604 | tangent_buffers[i] = gfx_make_buffer( |
607 | gfxcore, &(BufferDesc){ | 605 | gfxcore, &(BufferDesc){.usage = BufferStatic, |
608 | .usage = BufferStatic, | 606 | .type = BufferUntyped, |
609 | .type = BufferUntyped, | 607 | .data.data = buffer->data, |
610 | .data.data = buffer->data, | 608 | .data.count = buffer->size_bytes}); |
611 | .data.count = buffer->size_bytes}); | ||
612 | if (!tangent_buffers[i]) { | 609 | if (!tangent_buffers[i]) { |
613 | return false; | 610 | return false; |
614 | } | 611 | } |
@@ -682,14 +679,13 @@ static void load_textures_lazy( | |||
682 | mstring fullpath = | 679 | mstring fullpath = |
683 | mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); | 680 | mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); |
684 | 681 | ||
685 | load_texture_cmds[i] = (LoadTextureCmd){ | 682 | load_texture_cmds[i] = (LoadTextureCmd){.origin = AssetFromFile, |
686 | .origin = AssetFromFile, | 683 | .type = LoadTexture, |
687 | .type = LoadTexture, | 684 | .colour_space = LinearColourSpace, |
688 | .colour_space = sRGB, | 685 | .filtering = filtering, |
689 | .filtering = filtering, | 686 | .wrap = wrap, |
690 | .wrap = wrap, | 687 | .mipmaps = mipmaps, |
691 | .mipmaps = mipmaps, | 688 | .data.texture.filepath = fullpath}; |
692 | .data.texture.filepath = fullpath}; | ||
693 | } | 689 | } |
694 | } | 690 | } |
695 | 691 | ||
@@ -717,8 +713,12 @@ static bool load_texture_and_uniform( | |||
717 | // not be used as albedo and vice versa. | 713 | // not be used as albedo and vice versa. |
718 | if (!textures[texture_index]) { | 714 | if (!textures[texture_index]) { |
719 | LoadTextureCmd* cmd = &load_texture_cmds[texture_index]; | 715 | LoadTextureCmd* cmd = &load_texture_cmds[texture_index]; |
720 | // TODO: Check for colour textures and default to LinearColourSpace instead. | 716 | // Albedo and emissive use sRGB. Other textures use a linear colour space. |
721 | if (texture_type == NormalMap) { | 717 | // See the notes on the spec. |
718 | if ((texture_type == BaseColorTexture) || | ||
719 | (texture_type == EmissiveTexture)) { | ||
720 | cmd->colour_space = sRGB; | ||
721 | } else { | ||
722 | cmd->colour_space = LinearColourSpace; | 722 | cmd->colour_space = LinearColourSpace; |
723 | } | 723 | } |
724 | 724 | ||
@@ -778,22 +778,22 @@ static bool load_materials( | |||
778 | .value.vec4 = vec4_from_array(pbr->base_color_factor)}; | 778 | .value.vec4 = vec4_from_array(pbr->base_color_factor)}; |
779 | 779 | ||
780 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 780 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
781 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 781 | desc.uniforms[next_uniform++] = |
782 | .name = sstring_make(UNIFORM_METALLIC_FACTOR), | 782 | (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), |
783 | .type = UniformFloat, | 783 | .type = UniformFloat, |
784 | .value.scalar = pbr->metallic_factor}; | 784 | .value.scalar = pbr->metallic_factor}; |
785 | 785 | ||
786 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 786 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
787 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 787 | desc.uniforms[next_uniform++] = |
788 | .name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), | 788 | (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), |
789 | .type = UniformFloat, | 789 | .type = UniformFloat, |
790 | .value.scalar = pbr->roughness_factor}; | 790 | .value.scalar = pbr->roughness_factor}; |
791 | 791 | ||
792 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 792 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
793 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 793 | desc.uniforms[next_uniform++] = |
794 | .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), | 794 | (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), |
795 | .type = UniformVec3, | 795 | .type = UniformVec3, |
796 | .value.vec3 = vec3_from_array(mat->emissive_factor)}; | 796 | .value.vec3 = vec3_from_array(mat->emissive_factor)}; |
797 | 797 | ||
798 | if (pbr->base_color_texture.texture) { | 798 | if (pbr->base_color_texture.texture) { |
799 | if (!load_texture_and_uniform( | 799 | if (!load_texture_and_uniform( |
@@ -854,28 +854,28 @@ static Material* make_default_material() { | |||
854 | MaterialDesc desc = (MaterialDesc){0}; | 854 | MaterialDesc desc = (MaterialDesc){0}; |
855 | 855 | ||
856 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 856 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
857 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 857 | desc.uniforms[desc.num_uniforms++] = |
858 | .name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), | 858 | (ShaderUniform){.name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), |
859 | .type = UniformVec4, | 859 | .type = UniformVec4, |
860 | .value.vec4 = vec4_make(1, 1, 1, 1)}; | 860 | .value.vec4 = vec4_make(1, 1, 1, 1)}; |
861 | 861 | ||
862 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 862 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
863 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 863 | desc.uniforms[desc.num_uniforms++] = |
864 | .name = sstring_make(UNIFORM_METALLIC_FACTOR), | 864 | (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), |
865 | .type = UniformFloat, | 865 | .type = UniformFloat, |
866 | .value.scalar = 0}; | 866 | .value.scalar = 0}; |
867 | 867 | ||
868 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 868 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
869 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 869 | desc.uniforms[desc.num_uniforms++] = |
870 | .name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), | 870 | (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), |
871 | .type = UniformFloat, | 871 | .type = UniformFloat, |
872 | .value.scalar = 1}; | 872 | .value.scalar = 1}; |
873 | 873 | ||
874 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 874 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
875 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 875 | desc.uniforms[desc.num_uniforms++] = |
876 | .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), | 876 | (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), |
877 | .type = UniformVec3, | 877 | .type = UniformVec3, |
878 | .value.vec3 = vec3_make(0, 0, 0)}; | 878 | .value.vec3 = vec3_make(0, 0, 0)}; |
879 | 879 | ||
880 | return gfx_make_material(&desc); | 880 | return gfx_make_material(&desc); |
881 | } | 881 | } |
@@ -1192,10 +1192,10 @@ static bool load_meshes( | |||
1192 | shader ? shader : make_shader_permutation(gfxcore, perm); | 1192 | shader ? shader : make_shader_permutation(gfxcore, perm); |
1193 | assert(mesh_shader); | 1193 | assert(mesh_shader); |
1194 | 1194 | ||
1195 | meshes[next_mesh] = gfx_make_mesh(&(MeshDesc){ | 1195 | meshes[next_mesh] = |
1196 | .geometry = geometries[next_mesh], | 1196 | gfx_make_mesh(&(MeshDesc){.geometry = geometries[next_mesh], |
1197 | .material = material, | 1197 | .material = material, |
1198 | .shader = mesh_shader}); | 1198 | .shader = mesh_shader}); |
1199 | 1199 | ||
1200 | if (!meshes[next_mesh]) { | 1200 | if (!meshes[next_mesh]) { |
1201 | return false; | 1201 | return false; |
@@ -1432,9 +1432,9 @@ static void load_animations( | |||
1432 | const cgltf_animation* animation = &data->animations[a]; | 1432 | const cgltf_animation* animation = &data->animations[a]; |
1433 | AnimationDesc* animation_desc = &anima_desc->animations[a]; | 1433 | AnimationDesc* animation_desc = &anima_desc->animations[a]; |
1434 | 1434 | ||
1435 | *animation_desc = (AnimationDesc){ | 1435 | *animation_desc = |
1436 | .name = sstring_make(animation->name), | 1436 | (AnimationDesc){.name = sstring_make(animation->name), |
1437 | .num_channels = animation->channels_count}; | 1437 | .num_channels = animation->channels_count}; |
1438 | 1438 | ||
1439 | assert(animation->channels_count <= GFX_MAX_NUM_CHANNELS); | 1439 | assert(animation->channels_count <= GFX_MAX_NUM_CHANNELS); |
1440 | for (cgltf_size c = 0; c < animation->channels_count; ++c) { | 1440 | for (cgltf_size c = 0; c < animation->channels_count; ++c) { |
diff --git a/gfx/src/asset/model.h b/src/asset/model.h index d6399b1..d6399b1 100644 --- a/gfx/src/asset/model.h +++ b/src/asset/model.h | |||
diff --git a/gfx/src/asset/texture.c b/src/asset/texture.c index c790394..fb423cc 100644 --- a/gfx/src/asset/texture.c +++ b/src/asset/texture.c | |||
@@ -49,7 +49,7 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
49 | assert(cmd->origin == AssetFromFile || cmd->origin == AssetFromMemory); | 49 | assert(cmd->origin == AssetFromFile || cmd->origin == AssetFromMemory); |
50 | assert(cmd->type == LoadTexture || cmd->type == LoadCubemap); | 50 | assert(cmd->type == LoadTexture || cmd->type == LoadCubemap); |
51 | 51 | ||
52 | int width, height, components, old_components; | 52 | int width, height, components; |
53 | unsigned char* pixels[6] = {0}; | 53 | unsigned char* pixels[6] = {0}; |
54 | 54 | ||
55 | switch (cmd->origin) { | 55 | switch (cmd->origin) { |
@@ -64,7 +64,8 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
64 | } | 64 | } |
65 | break; | 65 | break; |
66 | } | 66 | } |
67 | case LoadCubemap: | 67 | case LoadCubemap: { |
68 | int old_components = 0; | ||
68 | for (int i = 0; i < 6; ++i) { | 69 | for (int i = 0; i < 6; ++i) { |
69 | // Flip +Y and -Y textures vertically. | 70 | // Flip +Y and -Y textures vertically. |
70 | stbi_set_flip_vertically_on_load(((i == 2) || (i == 3)) ? 1 : 0); | 71 | stbi_set_flip_vertically_on_load(((i == 2) || (i == 3)) ? 1 : 0); |
@@ -76,9 +77,10 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
76 | log_error("Failed to load texture file: %s", filepath); | 77 | log_error("Failed to load texture file: %s", filepath); |
77 | break; | 78 | break; |
78 | } | 79 | } |
79 | if (i > 0 && components != old_components) { | 80 | if ((i > 0) && (components != old_components)) { |
80 | log_error("All textures in a cubemap must have the same number of " | 81 | log_error( |
81 | "components"); | 82 | "All textures in a cubemap must have the same number of " |
83 | "components"); | ||
82 | break; | 84 | break; |
83 | } | 85 | } |
84 | if ((i != 2) && (i != 3)) { | 86 | if ((i != 2) && (i != 3)) { |
@@ -89,6 +91,7 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
89 | } | 91 | } |
90 | break; | 92 | break; |
91 | } | 93 | } |
94 | } | ||
92 | break; | 95 | break; |
93 | case AssetFromMemory: | 96 | case AssetFromMemory: |
94 | // TODO: Load textures from memory. | 97 | // TODO: Load textures from memory. |
@@ -122,6 +125,25 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
122 | } | 125 | } |
123 | 126 | ||
124 | switch (components) { | 127 | switch (components) { |
128 | case 1: | ||
129 | switch (cmd->colour_space) { | ||
130 | case LinearColourSpace: | ||
131 | desc.format = TextureR8; | ||
132 | break; | ||
133 | case sRGB: | ||
134 | // TODO: Gamma single-channel textures are not implemented yet. | ||
135 | // The caller should convert the single-channel to RGB and pass it down | ||
136 | // as sRGB. This is why the ChronographWatch currently appears red on the | ||
137 | // back. | ||
138 | log_error("Gamma colour space is not supported for 1-channel textures"); | ||
139 | // return 0; | ||
140 | desc.format = TextureR8; | ||
141 | break; | ||
142 | default: | ||
143 | log_error("Unsupported texture colour space: %d", cmd->colour_space); | ||
144 | return 0; | ||
145 | } | ||
146 | break; | ||
125 | case 3: | 147 | case 3: |
126 | switch (cmd->colour_space) { | 148 | switch (cmd->colour_space) { |
127 | case LinearColourSpace: | 149 | case LinearColourSpace: |
diff --git a/gfx/src/asset/texture.h b/src/asset/texture.h index 0d38bd9..0d38bd9 100644 --- a/gfx/src/asset/texture.h +++ b/src/asset/texture.h | |||
diff --git a/gfx/src/core/buffer.c b/src/core/buffer.c index 3b7e4bc..3b7e4bc 100644 --- a/gfx/src/core/buffer.c +++ b/src/core/buffer.c | |||
diff --git a/gfx/src/core/buffer.h b/src/core/buffer.h index b9080f0..b9080f0 100644 --- a/gfx/src/core/buffer.h +++ b/src/core/buffer.h | |||
diff --git a/gfx/src/core/constants.h b/src/core/constants.h index a6a3b94..a6a3b94 100644 --- a/gfx/src/core/constants.h +++ b/src/core/constants.h | |||
diff --git a/gfx/src/core/core.c b/src/core/core.c index 90038c6..e1671ea 100644 --- a/gfx/src/core/core.c +++ b/src/core/core.c | |||
@@ -420,6 +420,10 @@ void gfx_destroy_shader_program(GfxCore* gfxcore, ShaderProgram** prog) { | |||
420 | // Remove the shader program from the cache. | 420 | // Remove the shader program from the cache. |
421 | ProgramCache* cache = &gfxcore->program_cache; | 421 | ProgramCache* cache = &gfxcore->program_cache; |
422 | ShaderProgramCacheEntry* entry = find_program_cache_entry(cache, *prog); | 422 | ShaderProgramCacheEntry* entry = find_program_cache_entry(cache, *prog); |
423 | // TODO: The following assertion is too restrictive. Clients can end up | ||
424 | // re-using the same shader by virtue of the cache. The assertion assumes | ||
425 | // that no two "different" clients use the same set of shaders. This can | ||
426 | // be relaxed by reference-counting the shaders in the cache. | ||
423 | assert(entry); // Must be there, shaders can't go untracked. | 427 | assert(entry); // Must be there, shaders can't go untracked. |
424 | mempool_free(cache, &entry); | 428 | mempool_free(cache, &entry); |
425 | 429 | ||
diff --git a/gfx/src/core/core_impl.h b/src/core/core_impl.h index eefdfbe..eefdfbe 100644 --- a/gfx/src/core/core_impl.h +++ b/src/core/core_impl.h | |||
diff --git a/gfx/src/core/framebuffer.c b/src/core/framebuffer.c index 76d9002..76d9002 100644 --- a/gfx/src/core/framebuffer.c +++ b/src/core/framebuffer.c | |||
diff --git a/gfx/src/core/framebuffer.h b/src/core/framebuffer.h index 1a3439c..1a3439c 100644 --- a/gfx/src/core/framebuffer.h +++ b/src/core/framebuffer.h | |||
diff --git a/gfx/src/core/geometry.c b/src/core/geometry.c index cfc749f..cfc749f 100644 --- a/gfx/src/core/geometry.c +++ b/src/core/geometry.c | |||
diff --git a/gfx/src/core/geometry.h b/src/core/geometry.h index c37a76f..c37a76f 100644 --- a/gfx/src/core/geometry.h +++ b/src/core/geometry.h | |||
diff --git a/gfx/src/core/gl_util.h b/src/core/gl_util.h index d2d6e22..d2d6e22 100644 --- a/gfx/src/core/gl_util.h +++ b/src/core/gl_util.h | |||
diff --git a/gfx/src/core/renderbuffer.c b/src/core/renderbuffer.c index 2753f3b..2753f3b 100644 --- a/gfx/src/core/renderbuffer.c +++ b/src/core/renderbuffer.c | |||
diff --git a/gfx/src/core/renderbuffer.h b/src/core/renderbuffer.h index ea11610..ea11610 100644 --- a/gfx/src/core/renderbuffer.h +++ b/src/core/renderbuffer.h | |||
diff --git a/gfx/src/core/shader.c b/src/core/shader.c index dded084..dded084 100644 --- a/gfx/src/core/shader.c +++ b/src/core/shader.c | |||
diff --git a/gfx/src/core/shader.h b/src/core/shader.h index b9f5679..b9f5679 100644 --- a/gfx/src/core/shader.h +++ b/src/core/shader.h | |||
diff --git a/gfx/src/core/shader_program.c b/src/core/shader_program.c index 3cbe48d..3cbe48d 100644 --- a/gfx/src/core/shader_program.c +++ b/src/core/shader_program.c | |||
diff --git a/gfx/src/core/shader_program.h b/src/core/shader_program.h index 1443663..1443663 100644 --- a/gfx/src/core/shader_program.h +++ b/src/core/shader_program.h | |||
diff --git a/gfx/src/core/texture.c b/src/core/texture.c index 89f7ec0..372f9e6 100644 --- a/gfx/src/core/texture.c +++ b/src/core/texture.c | |||
@@ -37,6 +37,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | |||
37 | gfx_del_texture(texture); | 37 | gfx_del_texture(texture); |
38 | return false; | 38 | return false; |
39 | } | 39 | } |
40 | ASSERT_GL; | ||
40 | 41 | ||
41 | texture->format = to_GL_format(desc->format); | 42 | texture->format = to_GL_format(desc->format); |
42 | texture->type = to_GL_type(desc->format); | 43 | texture->type = to_GL_type(desc->format); |
@@ -50,6 +51,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | |||
50 | // Mipmaps. | 51 | // Mipmaps. |
51 | if (desc->mipmaps) { | 52 | if (desc->mipmaps) { |
52 | glGenerateMipmap(texture->target); | 53 | glGenerateMipmap(texture->target); |
54 | ASSERT_GL; | ||
53 | } | 55 | } |
54 | 56 | ||
55 | // Texture filtering. | 57 | // Texture filtering. |
@@ -60,6 +62,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | |||
60 | GLenum mag = linear ? GL_LINEAR : GL_NEAREST; | 62 | GLenum mag = linear ? GL_LINEAR : GL_NEAREST; |
61 | glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); | 63 | glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); |
62 | glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); | 64 | glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); |
65 | ASSERT_GL; | ||
63 | 66 | ||
64 | // Texture wrapping. | 67 | // Texture wrapping. |
65 | GLenum wrap = GL_INVALID_ENUM; | 68 | GLenum wrap = GL_INVALID_ENUM; |
@@ -74,6 +77,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | |||
74 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); | 77 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); |
75 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); | 78 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); |
76 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); | 79 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); |
80 | ASSERT_GL; | ||
77 | 81 | ||
78 | glBindTexture(texture->target, 0); | 82 | glBindTexture(texture->target, 0); |
79 | return true; | 83 | return true; |
@@ -119,6 +123,7 @@ void gfx_update_texture(Texture* texture, const TextureDataDesc* desc) { | |||
119 | FAIL("Unhandled texture dimension"); | 123 | FAIL("Unhandled texture dimension"); |
120 | break; | 124 | break; |
121 | } | 125 | } |
126 | ASSERT_GL; | ||
122 | 127 | ||
123 | glBindTexture(texture->target, 0); | 128 | glBindTexture(texture->target, 0); |
124 | } | 129 | } |
@@ -139,6 +144,8 @@ GLenum to_GL_internal_format(TextureFormat format) { | |||
139 | switch (format) { | 144 | switch (format) { |
140 | case TextureDepth: | 145 | case TextureDepth: |
141 | return GL_DEPTH_COMPONENT; | 146 | return GL_DEPTH_COMPONENT; |
147 | case TextureR8: | ||
148 | return GL_R8; | ||
142 | case TextureRG16: | 149 | case TextureRG16: |
143 | return GL_RG16; | 150 | return GL_RG16; |
144 | case TextureRG16F: | 151 | case TextureRG16F: |
@@ -163,6 +170,8 @@ GLenum to_GL_format(TextureFormat format) { | |||
163 | switch (format) { | 170 | switch (format) { |
164 | case TextureDepth: | 171 | case TextureDepth: |
165 | return GL_DEPTH_COMPONENT; | 172 | return GL_DEPTH_COMPONENT; |
173 | case TextureR8: | ||
174 | return GL_RED; | ||
166 | case TextureRG16: | 175 | case TextureRG16: |
167 | case TextureRG16F: | 176 | case TextureRG16F: |
168 | return GL_RG; | 177 | return GL_RG; |
@@ -185,6 +194,7 @@ GLenum to_GL_type(TextureFormat format) { | |||
185 | case TextureRG16F: | 194 | case TextureRG16F: |
186 | case TextureR11G11B10F: | 195 | case TextureR11G11B10F: |
187 | return GL_FLOAT; | 196 | return GL_FLOAT; |
197 | case TextureR8: | ||
188 | case TextureRG16: | 198 | case TextureRG16: |
189 | case TextureRGB8: | 199 | case TextureRGB8: |
190 | case TextureRGBA8: | 200 | case TextureRGBA8: |
diff --git a/gfx/src/core/texture.h b/src/core/texture.h index 4af41e9..4af41e9 100644 --- a/gfx/src/core/texture.h +++ b/src/core/texture.h | |||
diff --git a/gfx/src/gfx.c b/src/gfx.c index cd2ac90..4291ae7 100644 --- a/gfx/src/gfx.c +++ b/src/gfx.c | |||
@@ -2,20 +2,20 @@ | |||
2 | 2 | ||
3 | #include "asset/asset_cache.h" | 3 | #include "asset/asset_cache.h" |
4 | #include "core/core_impl.h" | 4 | #include "core/core_impl.h" |
5 | #include "llr/llr_impl.h" | ||
5 | #include "renderer/imm_renderer_impl.h" | 6 | #include "renderer/imm_renderer_impl.h" |
6 | #include "renderer/renderer_impl.h" | 7 | #include "renderer/renderer_impl.h" |
7 | #include "scene/scene_memory.h" | 8 | #include "scene/scene_memory.h" |
8 | 9 | ||
9 | #include <log/log.h> | ||
10 | |||
11 | #include <assert.h> | 10 | #include <assert.h> |
12 | #include <stdlib.h> | 11 | #include <stdlib.h> |
13 | 12 | ||
14 | typedef struct Gfx { | 13 | typedef struct Gfx { |
15 | AssetCache asset_cache; | 14 | AssetCache asset_cache; |
16 | GfxCore gfxcore; | 15 | GfxCore gfxcore; |
17 | Renderer renderer; | 16 | LLR llr; |
18 | ImmRenderer imm_renderer; | 17 | ImmRenderer imm_renderer; |
18 | Renderer renderer; | ||
19 | } Gfx; | 19 | } Gfx; |
20 | 20 | ||
21 | Gfx* gfx_init(void) { | 21 | Gfx* gfx_init(void) { |
@@ -24,16 +24,20 @@ Gfx* gfx_init(void) { | |||
24 | return 0; | 24 | return 0; |
25 | } | 25 | } |
26 | gfx_init_gfxcore(&gfx->gfxcore); | 26 | gfx_init_gfxcore(&gfx->gfxcore); |
27 | if (!renderer_make(&gfx->renderer, &gfx->gfxcore)) { | 27 | if (!gfx_llr_make(&gfx->llr, &gfx->gfxcore)) { |
28 | gfx_destroy(&gfx); | 28 | gfx_destroy(&gfx); |
29 | return 0; | 29 | return 0; |
30 | } | 30 | } |
31 | if (!imm_renderer_make(&gfx->imm_renderer, &gfx->gfxcore)) { | 31 | if (!gfx_imm_make(&gfx->imm_renderer, &gfx->gfxcore, &gfx->llr)) { |
32 | // TODO: Add error logs to the initialization failure cases here and inside | 32 | // TODO: Add error logs to the initialization failure cases here and inside |
33 | // the renderers. | 33 | // the renderers. |
34 | gfx_destroy(&gfx); | 34 | gfx_destroy(&gfx); |
35 | return 0; | 35 | return 0; |
36 | } | 36 | } |
37 | if (!gfx_renderer_make(&gfx->renderer, &gfx->llr, &gfx->gfxcore)) { | ||
38 | gfx_destroy(&gfx); | ||
39 | return 0; | ||
40 | } | ||
37 | gfx_init_asset_cache(&gfx->asset_cache); | 41 | gfx_init_asset_cache(&gfx->asset_cache); |
38 | scene_mem_init(); | 42 | scene_mem_init(); |
39 | return gfx; | 43 | return gfx; |
@@ -45,8 +49,9 @@ void gfx_destroy(Gfx** gfx) { | |||
45 | } | 49 | } |
46 | scene_mem_destroy(); | 50 | scene_mem_destroy(); |
47 | gfx_destroy_asset_cache(&(*gfx)->asset_cache); | 51 | gfx_destroy_asset_cache(&(*gfx)->asset_cache); |
48 | renderer_destroy(&(*gfx)->renderer); | 52 | gfx_renderer_destroy(&(*gfx)->renderer); |
49 | imm_renderer_destroy(&(*gfx)->imm_renderer); | 53 | gfx_imm_destroy(&(*gfx)->imm_renderer); |
54 | gfx_llr_destroy(&(*gfx)->llr); | ||
50 | gfx_del_gfxcore(&(*gfx)->gfxcore); | 55 | gfx_del_gfxcore(&(*gfx)->gfxcore); |
51 | free(*gfx); | 56 | free(*gfx); |
52 | *gfx = 0; | 57 | *gfx = 0; |
@@ -67,6 +72,11 @@ ImmRenderer* gfx_get_imm_renderer(Gfx* gfx) { | |||
67 | return &gfx->imm_renderer; | 72 | return &gfx->imm_renderer; |
68 | } | 73 | } |
69 | 74 | ||
75 | LLR* gfx_get_llr(Gfx* gfx) { | ||
76 | assert(gfx); | ||
77 | return &gfx->llr; | ||
78 | } | ||
79 | |||
70 | AssetCache* gfx_get_asset_cache(Gfx* gfx) { | 80 | AssetCache* gfx_get_asset_cache(Gfx* gfx) { |
71 | assert(gfx); | 81 | assert(gfx); |
72 | return &gfx->asset_cache; | 82 | return &gfx->asset_cache; |
diff --git a/gfx/src/gfx_assert.h b/src/gfx_assert.h index f4b3aa5..f4b3aa5 100644 --- a/gfx/src/gfx_assert.h +++ b/src/gfx_assert.h | |||
diff --git a/gfx/src/scene/light.c b/src/llr/light.c index adbec8d..1d1c40d 100644 --- a/gfx/src/scene/light.c +++ b/src/llr/light.c | |||
@@ -1,7 +1,7 @@ | |||
1 | #include "light_impl.h" | 1 | #include "light_impl.h" |
2 | 2 | ||
3 | #include "node_impl.h" | 3 | #include "../scene/node_impl.h" |
4 | #include "scene_memory.h" | 4 | #include "../scene/scene_memory.h" |
5 | 5 | ||
6 | #include <error.h> | 6 | #include <error.h> |
7 | 7 | ||
diff --git a/gfx/src/scene/light_impl.h b/src/llr/light_impl.h index 1aa0bb4..32203c4 100644 --- a/gfx/src/scene/light_impl.h +++ b/src/llr/light_impl.h | |||
@@ -1,8 +1,8 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include <gfx/scene/light.h> | 3 | #include <../../include/gfx/llr/light.h> |
4 | 4 | ||
5 | #include "types.h" | 5 | #include "../scene/types.h" |
6 | 6 | ||
7 | typedef struct Texture Texture; | 7 | typedef struct Texture Texture; |
8 | 8 | ||
diff --git a/src/llr/llr.c b/src/llr/llr.c new file mode 100644 index 0000000..74cfaed --- /dev/null +++ b/src/llr/llr.c | |||
@@ -0,0 +1,381 @@ | |||
1 | #include "light_impl.h" | ||
2 | #include "llr_impl.h" | ||
3 | #include "mesh_impl.h" | ||
4 | |||
5 | #include "llr/material_impl.h" | ||
6 | #include "scene/animation_impl.h" | ||
7 | |||
8 | #include <gfx/core.h> | ||
9 | #include <gfx/util/ibl.h> | ||
10 | |||
11 | #include <cassert.h> | ||
12 | |||
13 | static const int IRRADIANCE_MAP_WIDTH = 1024; | ||
14 | static const int IRRADIANCE_MAP_HEIGHT = 1024; | ||
15 | static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128; | ||
16 | static const int PREFILTERED_ENVIRONMENT_MAP_HEIGHT = 128; | ||
17 | static const int BRDF_INTEGRATION_MAP_WIDTH = 512; | ||
18 | static const int BRDF_INTEGRATION_MAP_HEIGHT = 512; | ||
19 | |||
20 | /// Initialize renderer state for IBL. | ||
21 | static bool init_ibl(LLR* renderer) { | ||
22 | assert(renderer); | ||
23 | assert(!renderer->ibl); | ||
24 | assert(!renderer->brdf_integration_map); | ||
25 | |||
26 | if (!((renderer->ibl = gfx_make_ibl(renderer->gfxcore)))) { | ||
27 | return false; | ||
28 | } | ||
29 | |||
30 | if (!((renderer->brdf_integration_map = gfx_make_brdf_integration_map( | ||
31 | renderer->ibl, renderer->gfxcore, BRDF_INTEGRATION_MAP_WIDTH, | ||
32 | BRDF_INTEGRATION_MAP_HEIGHT)))) { | ||
33 | return false; | ||
34 | } | ||
35 | |||
36 | return true; | ||
37 | } | ||
38 | |||
39 | // TODO: Why is this done lazily here? Do it when the environment light is | ||
40 | // created. | ||
41 | // | ||
42 | /// Compute irradiance and prefiltered environment maps for the light if they | ||
43 | /// have not been already computed. | ||
44 | static bool set_up_environment_light(LLR* renderer, EnvironmentLight* light) { | ||
45 | assert(renderer); | ||
46 | assert(light); | ||
47 | assert(renderer->ibl); | ||
48 | assert(renderer->brdf_integration_map); | ||
49 | |||
50 | if (light->irradiance_map) { | ||
51 | assert(light->prefiltered_environment_map); | ||
52 | return true; | ||
53 | } | ||
54 | |||
55 | // For convenience. | ||
56 | GfxCore* gfxcore = renderer->gfxcore; | ||
57 | |||
58 | Texture* irradiance_map = 0; | ||
59 | Texture* prefiltered_environment_map = 0; | ||
60 | |||
61 | if (!((irradiance_map = gfx_make_irradiance_map( | ||
62 | renderer->ibl, gfxcore, light->environment_map, | ||
63 | IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT)))) { | ||
64 | goto cleanup; | ||
65 | } | ||
66 | |||
67 | int max_mip_level = 0; | ||
68 | if (!((prefiltered_environment_map = gfx_make_prefiltered_environment_map( | ||
69 | renderer->ibl, gfxcore, light->environment_map, | ||
70 | PREFILTERED_ENVIRONMENT_MAP_WIDTH, | ||
71 | PREFILTERED_ENVIRONMENT_MAP_HEIGHT, &max_mip_level)))) { | ||
72 | goto cleanup; | ||
73 | } | ||
74 | |||
75 | light->irradiance_map = irradiance_map; | ||
76 | light->prefiltered_environment_map = prefiltered_environment_map; | ||
77 | light->max_reflection_lod = max_mip_level; | ||
78 | |||
79 | return true; | ||
80 | |||
81 | cleanup: | ||
82 | if (irradiance_map) { | ||
83 | gfx_destroy_texture(gfxcore, &irradiance_map); | ||
84 | } | ||
85 | if (prefiltered_environment_map) { | ||
86 | gfx_destroy_texture(gfxcore, &prefiltered_environment_map); | ||
87 | } | ||
88 | return false; | ||
89 | } | ||
90 | |||
91 | static void configure_light(LLR* renderer, Light* light) { | ||
92 | assert(renderer); | ||
93 | assert(light); | ||
94 | |||
95 | // For convenience. | ||
96 | ShaderProgram* const shader = renderer->shader; | ||
97 | |||
98 | switch (light->type) { | ||
99 | case EnvironmentLightType: { | ||
100 | EnvironmentLight* env = &light->environment; | ||
101 | |||
102 | const bool initialized = set_up_environment_light(renderer, env); | ||
103 | ASSERT(initialized); | ||
104 | assert(env->environment_map); | ||
105 | assert(env->irradiance_map); | ||
106 | assert(env->prefiltered_environment_map); | ||
107 | assert(renderer->brdf_integration_map); | ||
108 | |||
109 | gfx_set_texture_uniform( | ||
110 | shader, "BRDFIntegrationMap", renderer->brdf_integration_map); | ||
111 | gfx_set_texture_uniform(shader, "Sky", env->environment_map); | ||
112 | gfx_set_texture_uniform(shader, "IrradianceMap", env->irradiance_map); | ||
113 | gfx_set_texture_uniform( | ||
114 | shader, "PrefilteredEnvironmentMap", env->prefiltered_environment_map); | ||
115 | gfx_set_float_uniform( | ||
116 | shader, "MaxReflectionLOD", (float)env->max_reflection_lod); | ||
117 | |||
118 | break; | ||
119 | } | ||
120 | default: | ||
121 | assert(false); // TODO: Implement other light types. | ||
122 | break; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static void configure_state(LLR* renderer) { | ||
127 | assert(renderer); | ||
128 | |||
129 | // Check if anything changed first so that we don't call gfx_apply_uniforms() | ||
130 | // unnecessarily. | ||
131 | const bool nothing_changed = (renderer->changed_flags == 0); | ||
132 | if (nothing_changed) { | ||
133 | return; | ||
134 | } | ||
135 | // Setting a null shader is also allowed, in which case there is nothing to | ||
136 | // configure. | ||
137 | if (renderer->shader == 0) { | ||
138 | renderer->shader_changed = false; | ||
139 | return; | ||
140 | } | ||
141 | |||
142 | // For convenience. | ||
143 | ShaderProgram* const shader = renderer->shader; | ||
144 | const mat4* const model = &renderer->matrix_stack[renderer->stack_pointer]; | ||
145 | |||
146 | // TODO: Check to see which ones the shader actually uses and avoid | ||
147 | // computing the unnecessary matrices. | ||
148 | |||
149 | if (renderer->matrix_changed || renderer->shader_changed) { | ||
150 | renderer->matrix_changed = false; | ||
151 | |||
152 | gfx_set_mat4_uniform(shader, "Model", model); | ||
153 | gfx_set_mat4_uniform(shader, "ModelMatrix", model); | ||
154 | } | ||
155 | |||
156 | // TODO: camera_changed is not set anywhere. Need to think how imm primitive | ||
157 | // rendering and imm mesh rendering work together. We could treat imm | ||
158 | // primitive calls like setting a new shader. | ||
159 | if (renderer->camera_changed || renderer->shader_changed) { | ||
160 | renderer->camera_changed = false; | ||
161 | |||
162 | // Set all supported camera-related uniforms. Shaders can choose which ones | ||
163 | // to use. | ||
164 | const mat4 modelview = mat4_mul(renderer->view, *model); | ||
165 | const mat4 view_proj = mat4_mul(renderer->projection, renderer->view); | ||
166 | const mat4 mvp = mat4_mul(renderer->projection, modelview); | ||
167 | |||
168 | gfx_set_mat4_uniform(shader, "Modelview", &modelview); | ||
169 | gfx_set_mat4_uniform(shader, "View", &renderer->view); | ||
170 | gfx_set_mat4_uniform(shader, "Projection", &renderer->projection); | ||
171 | gfx_set_mat4_uniform(shader, "ViewProjection", &view_proj); | ||
172 | gfx_set_mat4_uniform(shader, "MVP", &mvp); | ||
173 | gfx_set_vec3_uniform(shader, "CameraPosition", renderer->camera_position); | ||
174 | gfx_set_mat4_uniform(shader, "CameraRotation", &renderer->camera_rotation); | ||
175 | gfx_set_float_uniform(shader, "Fovy", renderer->fovy); | ||
176 | gfx_set_float_uniform(shader, "Aspect", renderer->aspect); | ||
177 | } | ||
178 | |||
179 | if (renderer->lights_changed || renderer->shader_changed) { | ||
180 | renderer->lights_changed = false; | ||
181 | |||
182 | // TODO: Could do better by only setting the lights that have actually | ||
183 | // changed. | ||
184 | // TODO: Will also need to pass the number of lights to the shader once the | ||
185 | // other light types are implemented. | ||
186 | for (int i = 0; i < renderer->num_lights; ++i) { | ||
187 | configure_light(renderer, renderer->lights[i]); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | if (renderer->skeleton_changed || renderer->shader_changed) { | ||
192 | renderer->skeleton_changed = false; | ||
193 | |||
194 | if (renderer->num_joints > 0) { | ||
195 | gfx_set_mat4_array_uniform( | ||
196 | shader, "JointMatrices", renderer->joint_matrices, | ||
197 | renderer->num_joints); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | if (renderer->shader_changed) { | ||
202 | renderer->shader_changed = false; | ||
203 | gfx_activate_shader_program(renderer->shader); | ||
204 | } | ||
205 | |||
206 | // Must be called after activating the program. | ||
207 | gfx_apply_uniforms(renderer->shader); | ||
208 | } | ||
209 | |||
210 | bool gfx_llr_make(LLR* renderer, GfxCore* gfxcore) { | ||
211 | assert(renderer); | ||
212 | assert(gfxcore); | ||
213 | |||
214 | renderer->gfxcore = gfxcore; | ||
215 | if (!init_ibl(renderer)) { | ||
216 | goto cleanup; | ||
217 | } | ||
218 | gfx_llr_load_identity(renderer); | ||
219 | renderer->view = mat4_id(); | ||
220 | renderer->projection = mat4_id(); | ||
221 | renderer->camera_rotation = mat4_id(); | ||
222 | return true; | ||
223 | |||
224 | cleanup: | ||
225 | gfx_llr_destroy(renderer); | ||
226 | return false; | ||
227 | } | ||
228 | |||
229 | void gfx_llr_destroy(LLR* renderer) { | ||
230 | assert(renderer); | ||
231 | assert(renderer->gfxcore); | ||
232 | |||
233 | if (renderer->brdf_integration_map) { | ||
234 | gfx_destroy_texture(renderer->gfxcore, &renderer->brdf_integration_map); | ||
235 | } | ||
236 | |||
237 | // TODO: Do this once the IBL from the scene renderer is gone. | ||
238 | if (renderer->ibl) { | ||
239 | // gfx_destroy_ibl(renderer->gfxcore, &renderer->ibl); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | void gfx_llr_set_shader(LLR* renderer, ShaderProgram* shader) { | ||
244 | assert(renderer); | ||
245 | // null shader is allowed, so do not assert it. | ||
246 | |||
247 | // It's important to not set shader_changed unnecessarily, since that would | ||
248 | // re-trigger the setting of uniforms. | ||
249 | if (renderer->shader != shader) { | ||
250 | renderer->shader = shader; | ||
251 | renderer->shader_changed = true; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | void gfx_llr_push_light(LLR* renderer, Light* light) { | ||
256 | assert(renderer); | ||
257 | assert(light); | ||
258 | assert(renderer->num_lights >= 0); | ||
259 | ASSERT(renderer->num_lights < GFX_LLR_MAX_NUM_LIGHTS); | ||
260 | |||
261 | renderer->lights[renderer->num_lights++] = light; | ||
262 | renderer->lights_changed = true; | ||
263 | } | ||
264 | |||
265 | void gfx_llr_pop_light(LLR* renderer) { | ||
266 | assert(renderer); | ||
267 | ASSERT(renderer->num_lights > 0); | ||
268 | |||
269 | renderer->lights[--renderer->num_lights] = 0; | ||
270 | renderer->lights_changed = true; | ||
271 | } | ||
272 | |||
273 | void gfx_llr_set_skeleton( | ||
274 | LLR* renderer, const Anima* anima, const Skeleton* skeleton) { | ||
275 | assert(renderer); | ||
276 | assert(anima); | ||
277 | assert(skeleton); | ||
278 | assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS); | ||
279 | |||
280 | for (size_t i = 0; i < skeleton->num_joints; ++i) { | ||
281 | const joint_idx joint_index = skeleton->joints[i]; | ||
282 | const Joint* joint = &anima->joints[joint_index]; | ||
283 | renderer->joint_matrices[i] = joint->joint_matrix; | ||
284 | } | ||
285 | renderer->num_joints = skeleton->num_joints; | ||
286 | renderer->skeleton_changed = true; | ||
287 | } | ||
288 | |||
289 | void gfx_llr_clear_skeleton(LLR* renderer) { | ||
290 | assert(renderer); | ||
291 | |||
292 | renderer->num_joints = 0; | ||
293 | renderer->skeleton_changed = true; | ||
294 | } | ||
295 | |||
296 | void gfx_llr_set_camera(LLR* renderer, const Camera* camera) { | ||
297 | assert(renderer); | ||
298 | |||
299 | renderer->camera_position = camera->spatial.p; | ||
300 | renderer->camera_rotation = | ||
301 | mat4_rotation(spatial3_transform(&camera->spatial)); | ||
302 | renderer->view = spatial3_inverse_transform(&camera->spatial); | ||
303 | renderer->projection = camera->projection; | ||
304 | // Assuming a perspective matrix. | ||
305 | renderer->fovy = (R)atan(1.0 / (mat4_at(camera->projection, 1, 1))) * 2; | ||
306 | renderer->camera_changed = true; | ||
307 | } | ||
308 | |||
309 | void gfx_llr_set_aspect(LLR* renderer, float aspect) { | ||
310 | assert(renderer); | ||
311 | |||
312 | renderer->aspect = aspect; | ||
313 | renderer->camera_changed = true; | ||
314 | } | ||
315 | |||
316 | void gfx_llr_render_geometry(LLR* renderer, const Geometry* geometry) { | ||
317 | assert(renderer); | ||
318 | assert(geometry); | ||
319 | |||
320 | configure_state(renderer); | ||
321 | gfx_render_geometry(geometry); | ||
322 | } | ||
323 | |||
324 | void gfx_llr_render_mesh(LLR* renderer, const Mesh* mesh) { | ||
325 | assert(renderer); | ||
326 | assert(renderer->shader); | ||
327 | assert(mesh); | ||
328 | assert(mesh->geometry); | ||
329 | assert(mesh->material); | ||
330 | |||
331 | gfx_material_activate(renderer->shader, mesh->material); | ||
332 | gfx_llr_render_geometry(renderer, mesh->geometry); | ||
333 | } | ||
334 | |||
335 | void gfx_llr_load_identity(LLR* renderer) { | ||
336 | assert(renderer); | ||
337 | |||
338 | renderer->matrix_stack[0] = mat4_id(); | ||
339 | renderer->stack_pointer = 0; | ||
340 | renderer->matrix_changed = true; | ||
341 | } | ||
342 | |||
343 | void gfx_llr_push_matrix(LLR* renderer, const mat4* matrix) { | ||
344 | assert(renderer); | ||
345 | assert(matrix); | ||
346 | assert(renderer->stack_pointer >= 0); | ||
347 | ASSERT(renderer->stack_pointer < GFX_LLR_MAX_NUM_MATRICES); | ||
348 | |||
349 | renderer->stack_pointer += 1; | ||
350 | renderer->matrix_stack[renderer->stack_pointer] = | ||
351 | mat4_mul(*matrix, renderer->matrix_stack[renderer->stack_pointer - 1]); | ||
352 | renderer->matrix_changed = true; | ||
353 | } | ||
354 | |||
355 | void gfx_llr_pop_matrix(LLR* renderer) { | ||
356 | assert(renderer); | ||
357 | ASSERT(renderer->stack_pointer > 0); | ||
358 | |||
359 | // For debugging, zero out the matrix stack as matrices are popped out. | ||
360 | memset( | ||
361 | &renderer->matrix_stack[renderer->stack_pointer], 0, | ||
362 | sizeof(renderer->matrix_stack[0])); | ||
363 | renderer->stack_pointer -= 1; | ||
364 | renderer->matrix_changed = true; | ||
365 | } | ||
366 | |||
367 | void gfx_llr_translate(LLR* renderer, vec3 offset) { | ||
368 | assert(renderer); | ||
369 | |||
370 | const mat4 mat = mat4_translate(offset); | ||
371 | gfx_llr_push_matrix(renderer, &mat); | ||
372 | } | ||
373 | |||
374 | void gfx_llr_set_model_matrix(LLR* renderer, const mat4* model) { | ||
375 | assert(renderer); | ||
376 | assert(model); | ||
377 | |||
378 | renderer->matrix_stack[0] = *model; | ||
379 | renderer->stack_pointer = 0; | ||
380 | renderer->matrix_changed = true; | ||
381 | } | ||
diff --git a/src/llr/llr_impl.h b/src/llr/llr_impl.h new file mode 100644 index 0000000..ada2d79 --- /dev/null +++ b/src/llr/llr_impl.h | |||
@@ -0,0 +1,83 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <gfx/llr/llr.h> | ||
4 | #include <gfx/sizes.h> | ||
5 | |||
6 | #include <math/mat4.h> | ||
7 | #include <math/vec3.h> | ||
8 | |||
9 | #include <stdbool.h> | ||
10 | #include <stddef.h> | ||
11 | #include <stdint.h> | ||
12 | |||
13 | typedef struct Geometry Geometry; | ||
14 | typedef struct GfxCore GfxCore; | ||
15 | typedef struct IBL IBL; | ||
16 | typedef struct Material Material; | ||
17 | typedef struct ShaderProgram ShaderProgram; | ||
18 | typedef struct Texture Texture; | ||
19 | |||
20 | /// Immediate mode renderer. | ||
21 | /// | ||
22 | /// The renderer caches state changes in memory and only programs the underlying | ||
23 | /// shader program when a draw call is issued and if anything has changed. This | ||
24 | /// keeps the number of graphics API calls to a minimum, but requires tracking | ||
25 | /// state changes. The 'changed' booleans below fulfill this purpose, and | ||
26 | /// indicate whether a given state has changed since the last draw call. | ||
27 | /// | ||
28 | /// The renderer must combine state changes accordingly. For example, if only | ||
29 | /// the lights have changed, then it is sufficient to update light uniforms in | ||
30 | /// the current shader program. On the other hand, if the shader program has | ||
31 | /// changed, then the renderer must reconfigure it from scratch and set light | ||
32 | /// uniforms, camera uniforms, etc. | ||
33 | /// | ||
34 | /// Note that the shader program API has its own level of caching as well, so | ||
35 | /// reconfiguration at the level of the renderer does not result in the | ||
36 | /// worst-case set of graphics API calls. | ||
37 | typedef struct LLR { | ||
38 | GfxCore* gfxcore; | ||
39 | |||
40 | union { | ||
41 | struct { | ||
42 | bool shader_changed : 1; // Whether the shader has changed. | ||
43 | bool camera_changed : 1; // Whether the camera parameters have changed. | ||
44 | bool lights_changed : 1; // Whether the lights have changed. | ||
45 | bool skeleton_changed : 1; // Whether the skeleton has changed. | ||
46 | bool matrix_changed : 1; // Whether the matrix stack has changed. | ||
47 | }; | ||
48 | uint8_t changed_flags; | ||
49 | }; | ||
50 | |||
51 | IBL* ibl; | ||
52 | Texture* brdf_integration_map; | ||
53 | |||
54 | ShaderProgram* shader; // Active shader. Not owned. | ||
55 | |||
56 | vec3 camera_position; | ||
57 | mat4 camera_rotation; | ||
58 | mat4 view; // Camera view matrix. | ||
59 | mat4 projection; // Camera projection matrix. | ||
60 | R fovy; // Camera vertical field of view. | ||
61 | R aspect; // Aspect ratio. | ||
62 | |||
63 | // Lights are not const because environment lights store lazily-computed | ||
64 | // irradiance maps. | ||
65 | Light* lights[GFX_LLR_MAX_NUM_LIGHTS]; // Lights stack. | ||
66 | int num_lights; // Number of lights enabled at a given point in time. It | ||
67 | // points to one past the top of the stack. | ||
68 | |||
69 | size_t num_joints; | ||
70 | mat4 joint_matrices[GFX_MAX_NUM_JOINTS]; | ||
71 | |||
72 | // The matrix stack contains pre-multiplied matrices. | ||
73 | // It is also never empty. The top of the stack is an identity matrix when the | ||
74 | // stack is "empty" from the user's perspective. | ||
75 | mat4 matrix_stack[GFX_LLR_MAX_NUM_MATRICES]; | ||
76 | int stack_pointer; // Points to the top of the stack. | ||
77 | } LLR; | ||
78 | |||
79 | /// Create a new immediate mode renderer. | ||
80 | bool gfx_llr_make(LLR*, GfxCore*); | ||
81 | |||
82 | /// Destroy the immediate mode renderer. | ||
83 | void gfx_llr_destroy(LLR*); | ||
diff --git a/gfx/src/scene/material.c b/src/llr/material.c index 3248243..4014482 100644 --- a/gfx/src/scene/material.c +++ b/src/llr/material.c | |||
@@ -1,6 +1,6 @@ | |||
1 | #include "material_impl.h" | 1 | #include "material_impl.h" |
2 | 2 | ||
3 | #include "scene_memory.h" | 3 | #include "../scene/scene_memory.h" |
4 | 4 | ||
5 | #include <gfx/core.h> | 5 | #include <gfx/core.h> |
6 | 6 | ||
@@ -48,7 +48,7 @@ static void set_uniform(ShaderProgram* prog, const ShaderUniform* uniform) { | |||
48 | } | 48 | } |
49 | } | 49 | } |
50 | 50 | ||
51 | void material_activate(ShaderProgram* shader, const Material* material) { | 51 | void gfx_material_activate(ShaderProgram* shader, const Material* material) { |
52 | assert(material); | 52 | assert(material); |
53 | for (int i = 0; i < material->num_uniforms; ++i) { | 53 | for (int i = 0; i < material->num_uniforms; ++i) { |
54 | const ShaderUniform* uniform = &material->uniforms[i]; | 54 | const ShaderUniform* uniform = &material->uniforms[i]; |
diff --git a/src/llr/material_impl.h b/src/llr/material_impl.h new file mode 100644 index 0000000..138497f --- /dev/null +++ b/src/llr/material_impl.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <../../include/gfx/llr/material.h> | ||
4 | |||
5 | typedef struct ShaderProgram ShaderProgram; | ||
6 | |||
7 | typedef struct Material { | ||
8 | ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; | ||
9 | int num_uniforms; | ||
10 | } Material; | ||
11 | |||
12 | /// Activate the material. | ||
13 | /// | ||
14 | /// This configures the shader uniforms that are specific to the material. | ||
15 | void gfx_material_activate(ShaderProgram* shader, const Material* material); | ||
diff --git a/gfx/src/scene/mesh.c b/src/llr/mesh.c index 1a93bed..3aebb04 100644 --- a/gfx/src/scene/mesh.c +++ b/src/llr/mesh.c | |||
@@ -1,6 +1,6 @@ | |||
1 | #include "mesh_impl.h" | 1 | #include "mesh_impl.h" |
2 | 2 | ||
3 | #include "scene_memory.h" | 3 | #include "../scene/scene_memory.h" |
4 | 4 | ||
5 | #include <assert.h> | 5 | #include <assert.h> |
6 | 6 | ||
diff --git a/gfx/src/scene/mesh_impl.h b/src/llr/mesh_impl.h index 560b77e..47ff525 100644 --- a/gfx/src/scene/mesh_impl.h +++ b/src/llr/mesh_impl.h | |||
@@ -1,6 +1,6 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include <gfx/scene/mesh.h> | 3 | #include <../../include/gfx/llr/mesh.h> |
4 | 4 | ||
5 | typedef struct Mesh { | 5 | typedef struct Mesh { |
6 | const Geometry* geometry; | 6 | const Geometry* geometry; |
diff --git a/src/renderer/imm_renderer.c b/src/renderer/imm_renderer.c new file mode 100644 index 0000000..01cc5bb --- /dev/null +++ b/src/renderer/imm_renderer.c | |||
@@ -0,0 +1,192 @@ | |||
1 | #include "imm_renderer_impl.h" | ||
2 | |||
3 | #include <gfx/core.h> | ||
4 | #include <gfx/llr/llr.h> | ||
5 | #include <gfx/renderer/imm_renderer.h> | ||
6 | #include <gfx/util/shader.h> | ||
7 | |||
8 | #include <math/aabb3.h> | ||
9 | |||
10 | #include <assert.h> | ||
11 | #include <string.h> // memcpy | ||
12 | |||
13 | bool gfx_imm_make(ImmRenderer* renderer, GfxCore* gfxcore, LLR* llr) { | ||
14 | assert(renderer); | ||
15 | assert(gfxcore); | ||
16 | assert(llr); | ||
17 | |||
18 | const size_t num_triangle_verts = GFX_IMM_MAX_NUM_TRIANGLES * 3; | ||
19 | |||
20 | renderer->gfxcore = gfxcore; | ||
21 | renderer->llr = llr; | ||
22 | |||
23 | renderer->triangles = gfx_make_geometry( | ||
24 | gfxcore, | ||
25 | &(GeometryDesc){.type = Triangles, | ||
26 | .buffer_usage = BufferDynamic, | ||
27 | .num_verts = num_triangle_verts, | ||
28 | .positions3d = (BufferView3d){ | ||
29 | .size_bytes = num_triangle_verts * sizeof(vec3)}}); | ||
30 | if (!renderer->triangles) { | ||
31 | goto cleanup; | ||
32 | } | ||
33 | |||
34 | renderer->shader = gfx_make_immediate_mode_shader(gfxcore); | ||
35 | if (!renderer->shader) { | ||
36 | goto cleanup; | ||
37 | } | ||
38 | |||
39 | gfx_imm_set_colour(renderer, vec4_make(0.0f, 0.0f, 0.0f, 1.0f)); | ||
40 | |||
41 | return true; | ||
42 | |||
43 | cleanup: | ||
44 | gfx_imm_destroy(renderer); | ||
45 | return false; | ||
46 | } | ||
47 | |||
48 | void gfx_imm_destroy(ImmRenderer* renderer) { | ||
49 | assert(renderer); | ||
50 | assert(renderer->gfxcore); | ||
51 | |||
52 | if (renderer->triangles) { | ||
53 | gfx_destroy_geometry(renderer->gfxcore, &renderer->triangles); | ||
54 | // TODO: Could also destroy the geometry's buffers here. | ||
55 | } | ||
56 | |||
57 | if (renderer->shader) { | ||
58 | gfx_destroy_shader_program(renderer->gfxcore, &renderer->shader); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | void gfx_imm_flush(ImmRenderer* renderer) { | ||
63 | assert(renderer); | ||
64 | |||
65 | if (renderer->num_triangle_verts > 0) { | ||
66 | gfx_update_geometry( | ||
67 | renderer->triangles, | ||
68 | &(GeometryDesc){ | ||
69 | .num_verts = renderer->num_triangle_verts, | ||
70 | .positions3d = (BufferView3d){ | ||
71 | .data = renderer->triangle_verts, | ||
72 | .size_bytes = renderer->num_triangle_verts * sizeof(vec3)} | ||
73 | }); | ||
74 | |||
75 | gfx_llr_render_geometry(renderer->llr, renderer->triangles); | ||
76 | |||
77 | renderer->num_triangle_verts = 0; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | void gfx_imm_start(ImmRenderer* renderer) { | ||
82 | assert(renderer); | ||
83 | |||
84 | // Shader uniforms are applied lazily. | ||
85 | // TODO: In the event that gfx_activate_shader_program() activates uniforms | ||
86 | // automatically for convenience, call an overload here that doesn't do so. | ||
87 | // gfx_activate_shader_program(renderer->shader); | ||
88 | gfx_llr_set_shader(renderer->llr, renderer->shader); | ||
89 | } | ||
90 | |||
91 | void gfx_imm_end(ImmRenderer* renderer) { | ||
92 | assert(renderer); | ||
93 | |||
94 | gfx_imm_flush(renderer); | ||
95 | // gfx_deactivate_shader_program(renderer->shader); | ||
96 | gfx_llr_set_shader(renderer->llr, 0); | ||
97 | } | ||
98 | |||
99 | void gfx_imm_draw_triangles( | ||
100 | ImmRenderer* renderer, const vec3 verts[], size_t num_triangles) { | ||
101 | assert(renderer); | ||
102 | assert(verts); | ||
103 | const size_t new_verts = num_triangles * 3; | ||
104 | assert( | ||
105 | renderer->num_triangle_verts + new_verts < | ||
106 | (GFX_IMM_MAX_NUM_TRIANGLES * 3)); | ||
107 | |||
108 | memcpy( | ||
109 | renderer->triangle_verts + renderer->num_triangle_verts, verts, | ||
110 | new_verts * sizeof(vec3)); | ||
111 | |||
112 | renderer->num_triangle_verts += new_verts; | ||
113 | } | ||
114 | |||
115 | void gfx_imm_draw_triangle(ImmRenderer* renderer, const vec3 verts[3]) { | ||
116 | gfx_imm_draw_triangles(renderer, verts, 1); | ||
117 | } | ||
118 | |||
119 | void gfx_imm_draw_aabb2(ImmRenderer* renderer, aabb2 box) { | ||
120 | assert(renderer); | ||
121 | |||
122 | // clang-format off | ||
123 | const vec3 verts[4] = { | ||
124 | vec3_make(box.min.x, box.min.y, 0), // 3 ---- 2 | ||
125 | vec3_make(box.max.x, box.min.y, 0), // | | | ||
126 | vec3_make(box.max.x, box.max.y, 0), // | | | ||
127 | vec3_make(box.min.x, box.max.y, 0)}; // 0 ---- 1 | ||
128 | // clang-format on | ||
129 | |||
130 | #define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2] | ||
131 | const vec3 tris[6] = {tri(0, 1, 2), tri(0, 2, 3)}; | ||
132 | #undef tri | ||
133 | |||
134 | gfx_imm_draw_triangles(renderer, tris, 2); | ||
135 | } | ||
136 | |||
137 | void gfx_imm_draw_aabb3(ImmRenderer* renderer, aabb3 box) { | ||
138 | assert(renderer); | ||
139 | |||
140 | // clang-format off | ||
141 | const vec3 vertices[8] = { | ||
142 | vec3_make(box.min.x, box.min.y, box.max.z), // 7 ----- 6 | ||
143 | vec3_make(box.max.x, box.min.y, box.max.z), // / /| | ||
144 | vec3_make(box.max.x, box.max.y, box.max.z), // 3 ----- 2 | | ||
145 | vec3_make(box.min.x, box.max.y, box.max.z), // | | | | ||
146 | vec3_make(box.min.x, box.min.y, box.min.z), // | 4 ----- 5 | ||
147 | vec3_make(box.max.x, box.min.y, box.min.z), // |/ |/ | ||
148 | vec3_make(box.max.x, box.max.y, box.min.z), // 0 ----- 1 | ||
149 | vec3_make(box.min.x, box.max.y, box.min.z)}; | ||
150 | // clang-format on | ||
151 | |||
152 | gfx_imm_draw_box3(renderer, vertices); | ||
153 | } | ||
154 | |||
155 | void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]) { | ||
156 | assert(renderer); | ||
157 | assert(vertices); | ||
158 | |||
159 | // 7 ----- 6 | ||
160 | // / /| | ||
161 | // 3 ----- 2 | | ||
162 | // | | | | ||
163 | // | 4 ----- 5 | ||
164 | // |/ |/ | ||
165 | // 0 ----- 1 | ||
166 | |||
167 | #define tri(i0, i1, i2) vertices[i0], vertices[i1], vertices[i2] | ||
168 | const vec3 tris[36] = { | ||
169 | // Front. | ||
170 | tri(0, 1, 2), tri(0, 2, 3), | ||
171 | // Right. | ||
172 | tri(1, 5, 6), tri(1, 6, 2), | ||
173 | // Back. | ||
174 | tri(5, 4, 7), tri(5, 7, 6), | ||
175 | // Left. | ||
176 | tri(4, 0, 03), tri(4, 3, 7), | ||
177 | // Top. | ||
178 | tri(3, 2, 6), tri(3, 6, 7), | ||
179 | // Bottom. | ||
180 | tri(0, 4, 5), tri(0, 5, 1)}; | ||
181 | |||
182 | gfx_imm_draw_triangles(renderer, tris, 12); | ||
183 | } | ||
184 | |||
185 | void gfx_imm_set_colour(ImmRenderer* renderer, vec4 colour) { | ||
186 | assert(renderer); | ||
187 | assert(renderer->shader); | ||
188 | |||
189 | gfx_imm_flush(renderer); | ||
190 | |||
191 | gfx_set_vec4_uniform(renderer->shader, "Colour", colour); | ||
192 | } | ||
diff --git a/gfx/src/renderer/imm_renderer_impl.h b/src/renderer/imm_renderer_impl.h index 5ece354..61b49a7 100644 --- a/gfx/src/renderer/imm_renderer_impl.h +++ b/src/renderer/imm_renderer_impl.h | |||
@@ -1,44 +1,43 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include <gfx/renderer.h> | ||
4 | #include <gfx/sizes.h> | 3 | #include <gfx/sizes.h> |
5 | 4 | ||
6 | #include <math/mat4.h> | ||
7 | #include <math/vec3.h> | 5 | #include <math/vec3.h> |
8 | 6 | ||
9 | #include <stdbool.h> | 7 | #include <stdbool.h> |
10 | #include <stddef.h> | 8 | #include <stddef.h> |
11 | 9 | ||
12 | typedef struct Geometry Geometry; | 10 | typedef struct Geometry Geometry; |
11 | typedef struct GfxCore GfxCore; | ||
12 | typedef struct IBL IBL; | ||
13 | typedef struct LLR LLR; | ||
14 | typedef struct Material Material; | ||
13 | typedef struct ShaderProgram ShaderProgram; | 15 | typedef struct ShaderProgram ShaderProgram; |
16 | typedef struct Texture Texture; | ||
14 | 17 | ||
15 | /// Immediate mode renderer. | 18 | /// Immediate mode renderer. |
16 | /// | 19 | /// |
17 | /// Currently, the immediate mode renderer can only draw up to a maximum number | 20 | /// Currently, the immediate mode renderer can only draw up to a maximum number |
18 | /// of primitives per frame. It does not adjust this number dynamically. Keeps | 21 | /// of primitives per frame. It does not adjust this number dynamically. Keeps |
19 | /// things simple while the extra complexity is not needed. | 22 | /// things simple while the extra complexity is not needed. |
23 | /// TODO: Flush the buffer when it reaches its maximum size to remove this | ||
24 | /// constraint. | ||
20 | typedef struct ImmRenderer { | 25 | typedef struct ImmRenderer { |
21 | GfxCore* gfxcore; | 26 | GfxCore* gfxcore; |
22 | ShaderProgram* shader; | 27 | LLR* llr; |
28 | |||
29 | ShaderProgram* shader; // Immediate-mode shader program for primitives. | ||
23 | Geometry* triangles; | 30 | Geometry* triangles; |
24 | size_t num_triangle_verts; // Number of triangle verts this frame. | 31 | size_t num_triangle_verts; // Number of triangle verts this frame. |
25 | // TODO: wireframe rendering. | 32 | // TODO: wireframe rendering. |
26 | struct { | 33 | struct { |
27 | bool wireframe : 1; | 34 | bool wireframe : 1; |
28 | } flags; | 35 | } flags; |
29 | vec3 triangle_verts[IMM_MAX_NUM_TRIANGLES * 3]; | 36 | vec3 triangle_verts[GFX_IMM_MAX_NUM_TRIANGLES * 3]; |
30 | // Matrix stack contains pre-multiplied matrices. | ||
31 | // It is also never empty. The top of the stack is an identity matrix when the | ||
32 | // stack is "empty" from the user's perspective. | ||
33 | mat4 matrix_stack[IMM_MAX_NUM_MATRICES]; | ||
34 | int stack_pointer; | ||
35 | } ImmRenderer; | 37 | } ImmRenderer; |
36 | 38 | ||
37 | /// Create a new immediate mode renderer. | 39 | /// Create a new immediate mode renderer. |
38 | bool imm_renderer_make(ImmRenderer*, GfxCore*); | 40 | bool gfx_imm_make(ImmRenderer*, GfxCore*, LLR*); |
39 | 41 | ||
40 | /// Destroy the immediate mode renderer. | 42 | /// Destroy the immediate mode renderer. |
41 | void imm_renderer_destroy(ImmRenderer*); | 43 | void gfx_imm_destroy(ImmRenderer*); |
42 | |||
43 | /// Flush draw commands. | ||
44 | void imm_renderer_flush(ImmRenderer*); | ||
diff --git a/src/renderer/renderer.c b/src/renderer/renderer.c new file mode 100644 index 0000000..29a1813 --- /dev/null +++ b/src/renderer/renderer.c | |||
@@ -0,0 +1,247 @@ | |||
1 | #include "renderer_impl.h" | ||
2 | |||
3 | #include "llr/light_impl.h" | ||
4 | #include "llr/mesh_impl.h" | ||
5 | #include "scene/animation_impl.h" | ||
6 | #include "scene/camera_impl.h" | ||
7 | #include "scene/model_impl.h" | ||
8 | #include "scene/node_impl.h" | ||
9 | #include "scene/object_impl.h" | ||
10 | #include "scene/scene_impl.h" | ||
11 | #include "scene/scene_memory.h" | ||
12 | |||
13 | #include <gfx/core.h> | ||
14 | #include <gfx/llr/llr.h> | ||
15 | #include <gfx/util/shader.h> | ||
16 | |||
17 | #include <math/mat4.h> | ||
18 | |||
19 | #include <assert.h> | ||
20 | |||
21 | bool gfx_renderer_make(Renderer* renderer, LLR* llr, GfxCore* gfxcore) { | ||
22 | assert(renderer); | ||
23 | assert(llr); | ||
24 | assert(gfxcore); | ||
25 | |||
26 | renderer->gfxcore = gfxcore; | ||
27 | renderer->llr = llr; | ||
28 | |||
29 | return true; | ||
30 | } | ||
31 | |||
32 | void gfx_renderer_destroy(Renderer* renderer) { | ||
33 | if (!renderer) { | ||
34 | return; | ||
35 | } | ||
36 | assert(renderer->gfxcore); | ||
37 | GfxCore* gfxcore = renderer->gfxcore; | ||
38 | if (renderer->shaders.debug) { | ||
39 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.debug); | ||
40 | } | ||
41 | if (renderer->shaders.normals) { | ||
42 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.normals); | ||
43 | } | ||
44 | if (renderer->shaders.normal_mapped_normals) { | ||
45 | gfx_destroy_shader_program( | ||
46 | gfxcore, &renderer->shaders.normal_mapped_normals); | ||
47 | } | ||
48 | if (renderer->shaders.tangents) { | ||
49 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.tangents); | ||
50 | } | ||
51 | } | ||
52 | |||
53 | static ShaderProgram* load_shader(Renderer* renderer, RenderSceneMode mode) { | ||
54 | assert(renderer); | ||
55 | |||
56 | #define LOAD_AND_RETURN(pShader, constructor) \ | ||
57 | { \ | ||
58 | if (!pShader) { \ | ||
59 | pShader = constructor(renderer->gfxcore); \ | ||
60 | } \ | ||
61 | assert(pShader); \ | ||
62 | return pShader; \ | ||
63 | } | ||
64 | |||
65 | switch (mode) { | ||
66 | case RenderDefault: | ||
67 | return 0; | ||
68 | case RenderDebug: | ||
69 | LOAD_AND_RETURN(renderer->shaders.debug, gfx_make_debug3d_shader); | ||
70 | case RenderNormals: | ||
71 | LOAD_AND_RETURN(renderer->shaders.normals, gfx_make_view_normals_shader); | ||
72 | case RenderNormalMappedNormals: | ||
73 | LOAD_AND_RETURN( | ||
74 | renderer->shaders.normal_mapped_normals, | ||
75 | gfx_make_view_normal_mapped_normals_shader); | ||
76 | case RenderTangents: | ||
77 | LOAD_AND_RETURN(renderer->shaders.tangents, gfx_make_view_tangents_shader); | ||
78 | } | ||
79 | assert(false); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | // static void log_matrix(const mat4* m) { | ||
84 | // for (int row = 0; row < 4; ++row) { | ||
85 | // LOGI("[ %5.2f, %5.2f, %5.2f, %5.2f ]", m->val[0][row], m->val[1][row], | ||
86 | // m->val[2][row], m->val[3][row]); | ||
87 | // } | ||
88 | // } | ||
89 | |||
90 | typedef struct RenderState { | ||
91 | GfxCore* gfxcore; | ||
92 | LLR* llr; | ||
93 | Renderer* renderer; | ||
94 | ShaderProgram* shader; // Null to use scene shaders. | ||
95 | const Scene* scene; | ||
96 | const Anima* anima; | ||
97 | } RenderState; | ||
98 | |||
99 | static void draw_children( | ||
100 | RenderState* state, const mat4* node_transform, const SceneNode* node); | ||
101 | |||
102 | /// Draw the scene recursively. | ||
103 | static void draw_recursively( | ||
104 | RenderState* state, mat4 parent_transform, const SceneNode* node) { | ||
105 | assert(state); | ||
106 | const mat4 node_transform = mat4_mul(parent_transform, node->transform); | ||
107 | |||
108 | // Anima. | ||
109 | if (node->type == AnimaNode) { | ||
110 | // Save the anima so that we can animate objects. | ||
111 | state->anima = gfx_get_node_anima(node); | ||
112 | |||
113 | draw_children(state, &node_transform, node); | ||
114 | } | ||
115 | // Activate light. | ||
116 | else if (node->type == LightNode) { | ||
117 | Light* light = mem_get_light(node->light); | ||
118 | assert(light); | ||
119 | gfx_llr_push_light(state->llr, light); | ||
120 | { | ||
121 | draw_children(state, &node_transform, node); | ||
122 | } | ||
123 | gfx_llr_pop_light(state->llr); | ||
124 | } | ||
125 | // Model. | ||
126 | else if (node->type == ModelNode) { | ||
127 | const Model* model = gfx_get_node_model(node); | ||
128 | const SceneNode* root = mem_get_node(model->root); | ||
129 | draw_recursively(state, parent_transform, root); | ||
130 | draw_children(state, &node_transform, node); | ||
131 | } | ||
132 | // Render object. | ||
133 | else if (node->type == ObjectNode) { | ||
134 | const SceneObject* object = mem_get_object(node->object); | ||
135 | assert(object); | ||
136 | |||
137 | // TODO: Here we would frustum-cull the object. | ||
138 | |||
139 | // A model/anima can have many skeletons. We need to animate the given | ||
140 | // object using its skeleton, not just any skeleton of the anima. | ||
141 | if (object->skeleton.val) { | ||
142 | const Skeleton* skeleton = mem_get_skeleton(object->skeleton); | ||
143 | gfx_llr_set_skeleton(state->llr, state->anima, skeleton); | ||
144 | } | ||
145 | |||
146 | const mat4 model_matrix = node_transform; | ||
147 | |||
148 | for (mesh_link_idx mesh_link_index = object->mesh_link; | ||
149 | mesh_link_index.val;) { | ||
150 | const MeshLink* mesh_link = mem_get_mesh_link(mesh_link_index); | ||
151 | mesh_link_index = mesh_link->next; | ||
152 | |||
153 | const Mesh* mesh = mem_get_mesh(mesh_link->mesh); | ||
154 | if (!mesh) { | ||
155 | continue; | ||
156 | } | ||
157 | |||
158 | // TODO: Here we would frustum-cull the mesh. The AABB would have to be | ||
159 | // transformed by the model matrix. Rotation would make the AABB | ||
160 | // relatively large, but still, the culling would be conservative. | ||
161 | |||
162 | ShaderProgram* shader = state->shader ? state->shader : mesh->shader; | ||
163 | gfx_llr_set_shader(state->llr, shader); | ||
164 | gfx_llr_set_model_matrix(state->llr, &model_matrix); | ||
165 | gfx_llr_render_mesh(state->llr, mesh); | ||
166 | } | ||
167 | |||
168 | if (object->skeleton.val) { | ||
169 | gfx_llr_clear_skeleton(state->llr); | ||
170 | } | ||
171 | |||
172 | draw_children(state, &node_transform, node); | ||
173 | } else { | ||
174 | draw_children(state, &node_transform, node); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /// Draw the node's children. | ||
179 | static void draw_children( | ||
180 | RenderState* state, const mat4* node_transform, const SceneNode* node) { | ||
181 | // Render children recursively. | ||
182 | for (node_idx child_index = node->child; child_index.val;) { | ||
183 | const SceneNode* child = mem_get_node(child_index); | ||
184 | draw_recursively(state, *node_transform, child); | ||
185 | child_index = child->next; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) { | ||
190 | assert(renderer); | ||
191 | assert(params); | ||
192 | assert(params->scene); | ||
193 | |||
194 | ShaderProgram* const shader = load_shader(renderer, params->mode); | ||
195 | |||
196 | const Scene* scene = params->scene; | ||
197 | const SceneCamera* camera = params->camera; | ||
198 | GfxCore* const gfxcore = renderer->gfxcore; | ||
199 | |||
200 | int x, y, width, height; | ||
201 | gfx_get_viewport(gfxcore, &x, &y, &width, &height); | ||
202 | const R aspect = (R)width / (R)height; | ||
203 | |||
204 | RenderState state = { | ||
205 | .gfxcore = gfxcore, | ||
206 | .llr = renderer->llr, | ||
207 | .renderer = renderer, | ||
208 | .shader = shader, | ||
209 | .scene = scene}; | ||
210 | |||
211 | gfx_llr_set_camera(renderer->llr, &camera->camera); | ||
212 | gfx_llr_set_aspect(renderer->llr, aspect); | ||
213 | draw_recursively(&state, mat4_id(), scene->root); | ||
214 | } | ||
215 | |||
216 | static void update_rec(SceneNode* node, const SceneCamera* camera, R t) { | ||
217 | assert(node); | ||
218 | assert(camera); | ||
219 | |||
220 | const NodeType node_type = gfx_get_node_type(node); | ||
221 | |||
222 | // TODO: Models do not need to be animated if they are not visible to the | ||
223 | // camera. | ||
224 | if (node_type == AnimaNode) { | ||
225 | Anima* anima = gfx_get_node_anima_mut(node); | ||
226 | gfx_update_animation(anima, (R)t); | ||
227 | } else if (node_type == ModelNode) { | ||
228 | Model* model = gfx_get_node_model_mut(node); | ||
229 | SceneNode* root = gfx_get_model_root_mut(model); | ||
230 | update_rec(root, camera, t); | ||
231 | } | ||
232 | |||
233 | // Children. | ||
234 | SceneNode* child = gfx_get_node_child_mut(node); | ||
235 | while (child) { | ||
236 | update_rec(child, camera, t); | ||
237 | child = gfx_get_node_sibling_mut(child); | ||
238 | } | ||
239 | } | ||
240 | |||
241 | void gfx_update(Scene* scene, const SceneCamera* camera, R t) { | ||
242 | assert(scene); | ||
243 | assert(camera); | ||
244 | |||
245 | SceneNode* node = gfx_get_scene_root(scene); | ||
246 | update_rec(node, camera, t); | ||
247 | } | ||
diff --git a/gfx/src/renderer/renderer_impl.h b/src/renderer/renderer_impl.h index fc14dcb..7395915 100644 --- a/gfx/src/renderer/renderer_impl.h +++ b/src/renderer/renderer_impl.h | |||
@@ -4,14 +4,12 @@ | |||
4 | 4 | ||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | 6 | ||
7 | typedef struct IBL IBL; | 7 | typedef struct LLR LLR; |
8 | typedef struct ShaderProgram ShaderProgram; | 8 | typedef struct ShaderProgram ShaderProgram; |
9 | typedef struct Texture Texture; | ||
10 | 9 | ||
11 | typedef struct Renderer { | 10 | typedef struct Renderer { |
12 | GfxCore* gfxcore; | 11 | GfxCore* gfxcore; |
13 | IBL* ibl; | 12 | LLR* llr; |
14 | Texture* brdf_integration_map; | ||
15 | struct { | 13 | struct { |
16 | ShaderProgram* debug; | 14 | ShaderProgram* debug; |
17 | ShaderProgram* normals; | 15 | ShaderProgram* normals; |
@@ -21,7 +19,7 @@ typedef struct Renderer { | |||
21 | } Renderer; | 19 | } Renderer; |
22 | 20 | ||
23 | /// Create a new renderer. | 21 | /// Create a new renderer. |
24 | bool renderer_make(Renderer*, GfxCore*); | 22 | bool gfx_renderer_make(Renderer*, LLR*, GfxCore*); |
25 | 23 | ||
26 | /// Destroy the renderer. | 24 | /// Destroy the renderer. |
27 | void renderer_destroy(Renderer*); | 25 | void gfx_renderer_destroy(Renderer*); |
diff --git a/gfx/src/scene/animation.c b/src/scene/animation.c index 08d02ce..08d02ce 100644 --- a/gfx/src/scene/animation.c +++ b/src/scene/animation.c | |||
diff --git a/gfx/src/scene/animation_impl.h b/src/scene/animation_impl.h index 4408158..4408158 100644 --- a/gfx/src/scene/animation_impl.h +++ b/src/scene/animation_impl.h | |||
diff --git a/gfx/src/scene/camera.c b/src/scene/camera.c index be7d806..be7d806 100644 --- a/gfx/src/scene/camera.c +++ b/src/scene/camera.c | |||
diff --git a/gfx/src/scene/camera_impl.h b/src/scene/camera_impl.h index 20c3890..20c3890 100644 --- a/gfx/src/scene/camera_impl.h +++ b/src/scene/camera_impl.h | |||
diff --git a/gfx/src/scene/model.c b/src/scene/model.c index cc41a9a..cc41a9a 100644 --- a/gfx/src/scene/model.c +++ b/src/scene/model.c | |||
diff --git a/gfx/src/scene/model_impl.h b/src/scene/model_impl.h index a99d32c..a99d32c 100644 --- a/gfx/src/scene/model_impl.h +++ b/src/scene/model_impl.h | |||
diff --git a/gfx/src/scene/node.c b/src/scene/node.c index 67ce93c..e359f73 100644 --- a/gfx/src/scene/node.c +++ b/src/scene/node.c | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | #include "animation_impl.h" | 3 | #include "animation_impl.h" |
4 | #include "camera_impl.h" | 4 | #include "camera_impl.h" |
5 | #include "light_impl.h" | 5 | #include "llr/light_impl.h" |
6 | #include "model_impl.h" | 6 | #include "model_impl.h" |
7 | #include "object_impl.h" | 7 | #include "object_impl.h" |
8 | #include "scene_graph.h" | 8 | #include "scene_graph.h" |
diff --git a/gfx/src/scene/node_impl.h b/src/scene/node_impl.h index c79f252..c79f252 100644 --- a/gfx/src/scene/node_impl.h +++ b/src/scene/node_impl.h | |||
diff --git a/gfx/src/scene/object.c b/src/scene/object.c index e8e3ee6..27ff5db 100644 --- a/gfx/src/scene/object.c +++ b/src/scene/object.c | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | #include <gfx/core.h> | 3 | #include <gfx/core.h> |
4 | 4 | ||
5 | #include "mesh_impl.h" | 5 | #include "llr/mesh_impl.h" |
6 | #include "node_impl.h" | 6 | #include "node_impl.h" |
7 | #include "scene_memory.h" | 7 | #include "scene_memory.h" |
8 | 8 | ||
diff --git a/gfx/src/scene/object_impl.h b/src/scene/object_impl.h index 88f8e31..e864e53 100644 --- a/gfx/src/scene/object_impl.h +++ b/src/scene/object_impl.h | |||
@@ -4,8 +4,6 @@ | |||
4 | 4 | ||
5 | #include "types.h" | 5 | #include "types.h" |
6 | 6 | ||
7 | #include <math/mat4.h> | ||
8 | |||
9 | typedef struct MeshLink { | 7 | typedef struct MeshLink { |
10 | mesh_idx mesh; | 8 | mesh_idx mesh; |
11 | mesh_link_idx next; // Next MeshLink in the list. | 9 | mesh_link_idx next; // Next MeshLink in the list. |
@@ -21,6 +19,7 @@ typedef struct MeshLink { | |||
21 | typedef struct SceneObject { | 19 | typedef struct SceneObject { |
22 | mesh_link_idx mesh_link; /// First MeshLink in the list. | 20 | mesh_link_idx mesh_link; /// First MeshLink in the list. |
23 | skeleton_idx skeleton; /// 0 for static objects. | 21 | skeleton_idx skeleton; /// 0 for static objects. |
24 | node_idx parent; /// Parent SceneNode. | 22 | // TODO: Can we just store node indices in nodes, not the contained object? |
25 | aabb3 box; | 23 | node_idx parent; /// Parent SceneNode. |
24 | aabb3 box; | ||
26 | } SceneObject; | 25 | } SceneObject; |
diff --git a/gfx/src/scene/scene.c b/src/scene/scene.c index 54452dd..54452dd 100644 --- a/gfx/src/scene/scene.c +++ b/src/scene/scene.c | |||
diff --git a/gfx/src/scene/scene_graph.h b/src/scene/scene_graph.h index a26f828..0b1f7d0 100644 --- a/gfx/src/scene/scene_graph.h +++ b/src/scene/scene_graph.h | |||
@@ -6,35 +6,41 @@ | |||
6 | // NOTE: SceneMemory guarantees that index 0 can be regarded as an invalid | 6 | // NOTE: SceneMemory guarantees that index 0 can be regarded as an invalid |
7 | // index. | 7 | // index. |
8 | 8 | ||
9 | #define MEM_GET(INDEX) \ | 9 | #define MEM_GET(INDEX) \ |
10 | _Generic((INDEX), camera_idx \ | 10 | _Generic( \ |
11 | : mem_get_camera, material_idx \ | 11 | (INDEX), \ |
12 | : mem_get_material, mesh_idx \ | 12 | camera_idx: mem_get_camera, \ |
13 | : mem_get_mesh, mesh_link_idx \ | 13 | material_idx: mem_get_material, \ |
14 | : mem_get_mesh_link, node_idx \ | 14 | mesh_idx: mem_get_mesh, \ |
15 | : mem_get_node, object_idx \ | 15 | mesh_link_idx: mem_get_mesh_link, \ |
16 | : mem_get_object, scene_idx \ | 16 | node_idx: mem_get_node, \ |
17 | : mem_get_scene)(INDEX) | 17 | object_idx: mem_get_object, \ |
18 | scene_idx: mem_get_scene)(INDEX) | ||
18 | 19 | ||
19 | #define MEM_GET_INDEX(ITEM) \ | 20 | #define MEM_GET_INDEX(ITEM) \ |
20 | _Generic((ITEM), SceneCamera * \ | 21 | _Generic( \ |
21 | : mem_get_camera_index, Material * \ | 22 | (ITEM), \ |
22 | : mem_get_material_index, Mesh * \ | 23 | SceneCamera *: mem_get_camera_index, \ |
23 | : mem_get_mesh_index, MeshLink * \ | 24 | Material *: mem_get_material_index, \ |
24 | : mem_get_mesh_link_index, SceneNode * \ | 25 | Mesh *: mem_get_mesh_index, \ |
25 | : mem_get_node_index, SceneObject * \ | 26 | MeshLink *: mem_get_mesh_link_index, \ |
26 | : mem_get_object_index, Scene * \ | 27 | SceneNode *: mem_get_node_index, \ |
27 | : mem_get_scene_index)(ITEM) | 28 | SceneObject *: mem_get_object_index, \ |
29 | Scene *: mem_get_scene_index)(ITEM) | ||
28 | 30 | ||
29 | /// Assert the list node invariant. | 31 | /// Assert the list node invariant. |
30 | /// | 32 | /// |
31 | /// - A node does not point to itself. | 33 | /// - A node does not point to itself. |
34 | #if NDEBUG | ||
35 | #define ASSERT_LIST_NODE_INVARIANT(ITEM) | ||
36 | #else | ||
32 | #define ASSERT_LIST_NODE_INVARIANT(ITEM) \ | 37 | #define ASSERT_LIST_NODE_INVARIANT(ITEM) \ |
33 | { \ | 38 | { \ |
34 | const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \ | 39 | const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \ |
35 | assert((ITEM)->prev.val != item_idx); \ | 40 | assert((ITEM)->prev.val != item_idx); \ |
36 | assert((ITEM)->next.val != item_idx); \ | 41 | assert((ITEM)->next.val != item_idx); \ |
37 | } | 42 | } |
43 | #endif | ||
38 | 44 | ||
39 | /// Assert the tree node invariant. | 45 | /// Assert the tree node invariant. |
40 | /// | 46 | /// |
diff --git a/gfx/src/scene/scene_impl.h b/src/scene/scene_impl.h index 992f620..992f620 100644 --- a/gfx/src/scene/scene_impl.h +++ b/src/scene/scene_impl.h | |||
diff --git a/gfx/src/scene/scene_memory.c b/src/scene/scene_memory.c index 85c27e7..3a01325 100644 --- a/gfx/src/scene/scene_memory.c +++ b/src/scene/scene_memory.c | |||
@@ -4,14 +4,15 @@ | |||
4 | 4 | ||
5 | #include "animation_impl.h" | 5 | #include "animation_impl.h" |
6 | #include "camera_impl.h" | 6 | #include "camera_impl.h" |
7 | #include "light_impl.h" | 7 | #include "llr/light_impl.h" |
8 | #include "material_impl.h" | 8 | #include "llr/material_impl.h" |
9 | #include "mesh_impl.h" | 9 | #include "llr/mesh_impl.h" |
10 | #include "model_impl.h" | 10 | #include "model_impl.h" |
11 | #include "node_impl.h" | 11 | #include "node_impl.h" |
12 | #include "object_impl.h" | 12 | #include "object_impl.h" |
13 | #include "scene_impl.h" | 13 | #include "scene_impl.h" |
14 | 14 | ||
15 | #include <log/log.h> | ||
15 | #include <mempool.h> | 16 | #include <mempool.h> |
16 | 17 | ||
17 | DEF_MEMPOOL(anima_pool, Anima, GFX_MAX_NUM_ANIMAS) | 18 | DEF_MEMPOOL(anima_pool, Anima, GFX_MAX_NUM_ANIMAS) |
@@ -47,10 +48,11 @@ typedef struct SceneMemory { | |||
47 | 48 | ||
48 | static SceneMemory mem; | 49 | static SceneMemory mem; |
49 | 50 | ||
50 | #define ALLOC_DUMMY(POOL) \ | 51 | #define ALLOC_DUMMY(POOL) \ |
51 | { \ | 52 | { \ |
52 | const void* object = mempool_alloc(POOL); \ | 53 | const void* object = mempool_alloc(POOL); \ |
53 | assert(mempool_get_block_index(POOL, object) == 0); \ | 54 | (void)object; /* Silence warning in release builds. */ \ |
55 | assert(mempool_get_block_index(POOL, object) == 0); \ | ||
54 | } | 56 | } |
55 | 57 | ||
56 | #define PLURAL(name) name##s | 58 | #define PLURAL(name) name##s |
@@ -71,7 +73,7 @@ void scene_mem_init() { | |||
71 | mempool_make(&mem.skeletons); | 73 | mempool_make(&mem.skeletons); |
72 | 74 | ||
73 | // Allocate dummy objects at index 0 to guarantee that no objects allocated by | 75 | // Allocate dummy objects at index 0 to guarantee that no objects allocated by |
74 | // the caller map to index 0. | 76 | // the caller map to index 0. This allows 0 to be used as a sentinel. |
75 | ALLOC_DUMMY(&mem.animas); | 77 | ALLOC_DUMMY(&mem.animas); |
76 | ALLOC_DUMMY(&mem.animations); | 78 | ALLOC_DUMMY(&mem.animations); |
77 | ALLOC_DUMMY(&mem.cameras); | 79 | ALLOC_DUMMY(&mem.cameras); |
@@ -90,13 +92,40 @@ void scene_mem_destroy() { | |||
90 | // NOTE: the dummy objects are not constructed, so the destruction code below | 92 | // NOTE: the dummy objects are not constructed, so the destruction code below |
91 | // always skips index 0. (I don't really like the conditional inside the loop, | 93 | // always skips index 0. (I don't really like the conditional inside the loop, |
92 | // but this gets the job done without having to specialize the loop macro.) | 94 | // but this gets the job done without having to specialize the loop macro.) |
93 | #define DESTROY(name) \ | 95 | #define DESTROY(NAME) \ |
94 | mempool_foreach(&MEM_FIELD(name), obj, { \ | 96 | mempool_foreach(&MEM_FIELD(NAME), obj, { \ |
95 | if (i > 0) { \ | 97 | if (i > 0) { \ |
96 | gfx_destroy_##name(&obj); \ | 98 | gfx_destroy_##NAME(&obj); \ |
97 | } \ | 99 | } \ |
98 | }) | 100 | }) |
99 | 101 | ||
102 | // Print memory diagnostics. | ||
103 | #define PRINT_POOL(POOL_NAME, POOL) \ | ||
104 | { \ | ||
105 | const size_t capacity = mempool_capacity(POOL); \ | ||
106 | const size_t size = mempool_size(POOL); \ | ||
107 | const size_t block_size_bytes = mempool_block_size_bytes(POOL); \ | ||
108 | const size_t size_bytes = size * block_size_bytes; \ | ||
109 | const size_t capacity_bytes = capacity * block_size_bytes; \ | ||
110 | LOGI( \ | ||
111 | "%s pool: %lu/%lu (%lu/%lu bytes)", POOL_NAME, size, capacity, \ | ||
112 | size_bytes, capacity_bytes); \ | ||
113 | } | ||
114 | |||
115 | LOGI("Pool diagnostics:"); | ||
116 | PRINT_POOL("Animas", &mem.animas); | ||
117 | PRINT_POOL("Animations", &mem.animations); | ||
118 | PRINT_POOL("Cameras", &mem.cameras); | ||
119 | PRINT_POOL("Lights", &mem.lights); | ||
120 | PRINT_POOL("Materials", &mem.materials); | ||
121 | PRINT_POOL("Meshes", &mem.meshs); | ||
122 | PRINT_POOL("Mesh links", &mem.mesh_links); | ||
123 | PRINT_POOL("Models", &mem.models); | ||
124 | PRINT_POOL("Nodes", &mem.nodes); | ||
125 | PRINT_POOL("Objects", &mem.objects); | ||
126 | PRINT_POOL("Scenes", &mem.scenes); | ||
127 | PRINT_POOL("Skeletons", &mem.skeletons); | ||
128 | |||
100 | // Models contain scene elements. Destruction is handled by the remainder of | 129 | // Models contain scene elements. Destruction is handled by the remainder of |
101 | // scene destructionb elow. | 130 | // scene destructionb elow. |
102 | // | 131 | // |
@@ -119,20 +148,20 @@ void scene_mem_destroy() { | |||
119 | // Skeletons are owned by animas and do not have a destructor. | 148 | // Skeletons are owned by animas and do not have a destructor. |
120 | } | 149 | } |
121 | 150 | ||
122 | #define DEF_MEMORY(name, type) \ | 151 | #define DEF_MEMORY(NAME, TYPE) \ |
123 | /* xyz* mem_alloc_xyz(); */ \ | 152 | /* xyz* mem_alloc_xyz(); */ \ |
124 | type* mem_alloc_##name() { return mempool_alloc(&MEM_FIELD(name)); } \ | 153 | TYPE* mem_alloc_##NAME() { return mempool_alloc(&MEM_FIELD(NAME)); } \ |
125 | /* void mem_free_xyz(xyz**); */ \ | 154 | /* void mem_free_xyz(xyz**); */ \ |
126 | void mem_free_##name(type** obj) { mempool_free(&MEM_FIELD(name), obj); } \ | 155 | void mem_free_##NAME(TYPE** obj) { mempool_free(&MEM_FIELD(NAME), obj); } \ |
127 | /* xyz* mem_get_xyz(xyz_idx); */ \ | 156 | /* xyz* mem_get_xyz(xyz_idx); */ \ |
128 | type* mem_get_##name(NAMED_INDEX(name) index) { \ | 157 | TYPE* mem_get_##NAME(NAMED_INDEX(NAME) index) { \ |
129 | assert(index.val != 0); /* 0 is the dummy allocation. */ \ | 158 | assert(index.val != 0); /* 0 is the dummy allocation. */ \ |
130 | return mempool_get_block(&MEM_FIELD(name), index.val); \ | 159 | return mempool_get_block(&MEM_FIELD(NAME), index.val); \ |
131 | } \ | 160 | } \ |
132 | /* xyz_idx mem_get_xyz_index(const xyz*); */ \ | 161 | /* xyz_idx mem_get_xyz_index(const xyz*); */ \ |
133 | NAMED_INDEX(name) mem_get_##name##_index(const type* obj) { \ | 162 | NAMED_INDEX(NAME) mem_get_##NAME##_index(const TYPE* obj) { \ |
134 | return (NAMED_INDEX(name)){ \ | 163 | return (NAMED_INDEX(NAME)){ \ |
135 | .val = mempool_get_block_index(&MEM_FIELD(name), obj)}; \ | 164 | .val = mempool_get_block_index(&MEM_FIELD(NAME), obj)}; \ |
136 | } | 165 | } |
137 | 166 | ||
138 | DEF_MEMORY(anima, Anima) | 167 | DEF_MEMORY(anima, Anima) |
diff --git a/gfx/src/scene/scene_memory.h b/src/scene/scene_memory.h index d175cba..d175cba 100644 --- a/gfx/src/scene/scene_memory.h +++ b/src/scene/scene_memory.h | |||
diff --git a/gfx/src/scene/types.h b/src/scene/types.h index d0ffc41..d0ffc41 100644 --- a/gfx/src/scene/types.h +++ b/src/scene/types.h | |||
diff --git a/gfx/src/util/geometry.c b/src/util/geometry.c index afe0109..afe0109 100644 --- a/gfx/src/util/geometry.c +++ b/src/util/geometry.c | |||
diff --git a/gfx/src/util/ibl.c b/src/util/ibl.c index 5a79990..5a79990 100644 --- a/gfx/src/util/ibl.c +++ b/src/util/ibl.c | |||
diff --git a/gfx/src/util/shader.c b/src/util/shader.c index f5c22cc..f5c22cc 100644 --- a/gfx/src/util/shader.c +++ b/src/util/shader.c | |||
diff --git a/gfx/src/util/skyquad.c b/src/util/skyquad.c index 08fa044..094de67 100644 --- a/gfx/src/util/skyquad.c +++ b/src/util/skyquad.c | |||
@@ -1,18 +1,14 @@ | |||
1 | #include <gfx/util/skyquad.h> | 1 | #include <gfx/util/skyquad.h> |
2 | 2 | ||
3 | #include <gfx/core.h> | 3 | #include <gfx/core.h> |
4 | #include <gfx/gfx.h> | 4 | #include <gfx/llr/light.h> |
5 | #include <gfx/scene/light.h> | 5 | #include <gfx/llr/material.h> |
6 | #include <gfx/scene/material.h> | 6 | #include <gfx/llr/mesh.h> |
7 | #include <gfx/scene/mesh.h> | ||
8 | #include <gfx/scene/node.h> | 7 | #include <gfx/scene/node.h> |
9 | #include <gfx/scene/object.h> | 8 | #include <gfx/scene/object.h> |
10 | #include <gfx/scene/scene.h> | ||
11 | #include <gfx/util/geometry.h> | 9 | #include <gfx/util/geometry.h> |
12 | #include <gfx/util/shader.h> | 10 | #include <gfx/util/shader.h> |
13 | 11 | ||
14 | #include <math/vec4.h> | ||
15 | |||
16 | #include <assert.h> | 12 | #include <assert.h> |
17 | 13 | ||
18 | SceneObject* gfx_make_skyquad(GfxCore* gfxcore, const Texture* texture) { | 14 | SceneObject* gfx_make_skyquad(GfxCore* gfxcore, const Texture* texture) { |
@@ -36,10 +32,9 @@ SceneObject* gfx_make_skyquad(GfxCore* gfxcore, const Texture* texture) { | |||
36 | } | 32 | } |
37 | 33 | ||
38 | MaterialDesc material_desc = (MaterialDesc){0}; | 34 | MaterialDesc material_desc = (MaterialDesc){0}; |
39 | material_desc.uniforms[0] = (ShaderUniform){ | 35 | material_desc.uniforms[0] = (ShaderUniform){.type = UniformTexture, |
40 | .type = UniformTexture, | 36 | .value.texture = texture, |
41 | .value.texture = texture, | 37 | .name = sstring_make("Skyquad")}; |
42 | .name = sstring_make("Skyquad")}; | ||
43 | material_desc.num_uniforms = 1; | 38 | material_desc.num_uniforms = 1; |
44 | material = gfx_make_material(&material_desc); | 39 | material = gfx_make_material(&material_desc); |
45 | if (!material) { | 40 | if (!material) { |