diff options
| -rw-r--r-- | CMakeLists.txt | 99 | ||||
| -rw-r--r-- | README.md (renamed from gfx/README.md) | 56 | ||||
| -rw-r--r-- | app/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | app/demo/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | app/include/gfx/app.h | 31 | ||||
| -rw-r--r-- | app/src/app.c | 46 | ||||
| -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) | 2 | ||||
| -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/include/gfx/scene.h | 11 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/camera.h | 22 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/light.h | 30 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/material.h | 25 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/mesh.h | 23 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/model.h | 12 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/node.h | 156 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/object.h | 39 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/scene.h | 21 | ||||
| -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/camera.c | 37 | ||||
| -rw-r--r-- | gfx/src/scene/camera_impl.h | 12 | ||||
| -rw-r--r-- | gfx/src/scene/material.c | 57 | ||||
| -rw-r--r-- | gfx/src/scene/material_impl.h | 16 | ||||
| -rw-r--r-- | gfx/src/scene/mesh_impl.h | 12 | ||||
| -rw-r--r-- | gfx/src/scene/scene.c | 25 | ||||
| -rw-r--r-- | gfx/src/scene/scene_impl.h | 13 | ||||
| -rw-r--r-- | include/gfx/animation.h (renamed from gfx/include/gfx/scene/animation.h) | 5 | ||||
| -rw-r--r-- | include/gfx/asset.h (renamed from gfx/include/gfx/asset.h) | 4 | ||||
| -rw-r--r-- | include/gfx/core.h (renamed from gfx/include/gfx/core.h) | 34 | ||||
| -rw-r--r-- | include/gfx/gfx.h (renamed from gfx/include/gfx/gfx.h) | 10 | ||||
| -rw-r--r-- | include/gfx/render/imm.h | 52 | ||||
| -rw-r--r-- | include/gfx/render/llr.h | 81 | ||||
| -rw-r--r-- | include/gfx/render/renderer.h | 36 | ||||
| -rw-r--r-- | include/gfx/scene.h | 296 | ||||
| -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) | 1 | ||||
| -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) | 123 | ||||
| -rw-r--r-- | shaders/cook_torrance.vert (renamed from gfx/shaders/cook_torrance.vert) | 24 | ||||
| -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/animation.c (renamed from gfx/src/scene/animation.c) | 29 | ||||
| -rw-r--r-- | src/animation_impl.h (renamed from gfx/src/scene/animation_impl.h) | 32 | ||||
| -rw-r--r-- | src/asset/asset_cache.c (renamed from gfx/src/asset/asset_cache.c) | 9 | ||||
| -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) | 552 | ||||
| -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) | 2 | ||||
| -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) | 40 | ||||
| -rw-r--r-- | src/core/core_impl.h (renamed from gfx/src/core/core_impl.h) | 8 | ||||
| -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) | 73 | ||||
| -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) | 148 | ||||
| -rw-r--r-- | src/core/shader_program.h (renamed from gfx/src/core/shader_program.h) | 2 | ||||
| -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) | 40 | ||||
| -rw-r--r-- | src/gfx_assert.h (renamed from gfx/src/gfx_assert.h) | 0 | ||||
| -rw-r--r-- | src/memory.c (renamed from gfx/src/scene/scene_memory.c) | 87 | ||||
| -rw-r--r-- | src/memory.h (renamed from gfx/src/scene/scene_memory.h) | 10 | ||||
| -rw-r--r-- | src/render/imm.c | 194 | ||||
| -rw-r--r-- | src/render/imm_impl.h (renamed from gfx/src/renderer/imm_renderer_impl.h) | 33 | ||||
| -rw-r--r-- | src/render/llr.c | 441 | ||||
| -rw-r--r-- | src/render/llr_impl.h | 85 | ||||
| -rw-r--r-- | src/render/renderer.c | 282 | ||||
| -rw-r--r-- | src/render/renderer_impl.h (renamed from gfx/src/renderer/renderer_impl.h) | 12 | ||||
| -rw-r--r-- | src/scene/camera.c | 18 | ||||
| -rw-r--r-- | src/scene/light.c (renamed from gfx/src/scene/light.c) | 7 | ||||
| -rw-r--r-- | src/scene/light_impl.h (renamed from gfx/src/scene/light_impl.h) | 9 | ||||
| -rw-r--r-- | src/scene/material.c | 24 | ||||
| -rw-r--r-- | src/scene/material_impl.h | 13 | ||||
| -rw-r--r-- | src/scene/mesh.c (renamed from gfx/src/scene/mesh.c) | 6 | ||||
| -rw-r--r-- | src/scene/mesh_impl.h | 11 | ||||
| -rw-r--r-- | src/scene/model.c (renamed from gfx/src/scene/model.c) | 4 | ||||
| -rw-r--r-- | src/scene/model_impl.h (renamed from gfx/src/scene/model_impl.h) | 5 | ||||
| -rw-r--r-- | src/scene/node.c (renamed from gfx/src/scene/node.c) | 83 | ||||
| -rw-r--r-- | src/scene/node_impl.h (renamed from gfx/src/scene/node_impl.h) | 6 | ||||
| -rw-r--r-- | src/scene/object.c (renamed from gfx/src/scene/object.c) | 11 | ||||
| -rw-r--r-- | src/scene/object_impl.h (renamed from gfx/src/scene/object_impl.h) | 7 | ||||
| -rw-r--r-- | src/scene/scene.c | 31 | ||||
| -rw-r--r-- | src/scene/scene_graph.h (renamed from gfx/src/scene/scene_graph.h) | 44 | ||||
| -rw-r--r-- | src/scene/scene_impl.h | 9 | ||||
| -rw-r--r-- | src/types.h (renamed from gfx/src/scene/types.h) | 0 | ||||
| -rw-r--r-- | src/util/geometry.c (renamed from gfx/src/util/geometry.c) | 13 | ||||
| -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) | 18 |
167 files changed, 2552 insertions, 5256 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 25c7560..7d3767d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -1,8 +1,97 @@ | |||
| 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/animation.c | ||
| 42 | src/asset/asset_cache.c | ||
| 43 | src/asset/model.c | ||
| 44 | src/asset/texture.c | ||
| 45 | src/core/buffer.c | ||
| 46 | src/core/core.c | ||
| 47 | src/core/framebuffer.c | ||
| 48 | src/core/geometry.c | ||
| 49 | src/core/renderbuffer.c | ||
| 50 | src/core/shader_program.c | ||
| 51 | src/core/shader.c | ||
| 52 | src/core/texture.c | ||
| 53 | src/memory.c | ||
| 54 | src/render/imm.c | ||
| 55 | src/render/llr.c | ||
| 56 | src/render/renderer.c | ||
| 57 | src/scene/camera.c | ||
| 58 | src/scene/light.c | ||
| 59 | src/scene/material.c | ||
| 60 | src/scene/mesh.c | ||
| 61 | src/scene/model.c | ||
| 62 | src/scene/node.c | ||
| 63 | src/scene/object.c | ||
| 64 | src/scene/scene.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 | hash | ||
| 82 | math) | ||
| 83 | |||
| 84 | target_link_libraries(gfx PRIVATE | ||
| 85 | cassert | ||
| 86 | cgltf | ||
| 87 | cgltf-tangents | ||
| 88 | error | ||
| 89 | gfx-app | ||
| 90 | log | ||
| 91 | mempool | ||
| 92 | shaders | ||
| 93 | stb | ||
| 94 | # System libraries. | ||
| 95 | GL | ||
| 96 | # Required to initialize GLAD. | ||
| 97 | -ldl) | ||
diff --git a/gfx/README.md b/README.md index f0b103d..3a30ee4 100644 --- a/gfx/README.md +++ b/README.md | |||
| @@ -20,32 +20,45 @@ 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 ("Core") |
| 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 | The low-level renderer (`LLR`) provides a low-level, immediate-mode interface | ||
| 47 | on top of the render backend. It understands higher-level concepts to make | ||
| 48 | rendering more convenient (object, mesh, material, light, camera, etc), but is | ||
| 49 | still relatively low-level and does not understand any particular scene data | ||
| 50 | structure. | ||
| 51 | |||
| 52 | ### Immediate-mode Renderer | ||
| 53 | |||
| 54 | The immediate-mode renderer (`Imm`) provides an API to define and render | ||
| 55 | geometry procedurally and on the spot. It should be reserved for debug or | ||
| 56 | not-performance-intensive rendering. | ||
| 57 | |||
| 44 | ### Scene | 58 | ### Scene |
| 45 | 59 | ||
| 46 | A `Scene` encapsulates a scene graph. A scene graph contains the elements that | 60 | 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 | 61 | graph implementation includes: |
| 48 | implementation includes: | ||
| 49 | 62 | ||
| 50 | - Camera | 63 | - Camera |
| 51 | - Light | 64 | - Light |
| @@ -64,7 +77,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. | 77 | former, the API could create the illusion that the hierarchy can be a DAG. |
| 65 | 78 | ||
| 66 | The strict tree hierarchy should not be that restrictive in practice. Even the | 79 | 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): | 80 | glTF 2.0 |
| 81 | spec [enforces this](https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#nodes-and-hierarchy): | ||
| 68 | 82 | ||
| 69 | > *For Version 2.0 conformance, the glTF node hierarchy is not a directed | 83 | > *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 | 84 | > acyclic graph (DAG) or scene graph, but a disjoint union of strict trees. That |
| @@ -76,9 +90,9 @@ glTF 2.0 spec [enforces this](https://github.com/KhronosGroup/glTF/blob/master/s | |||
| 76 | Two use cases for instancing seem to be: | 90 | Two use cases for instancing seem to be: |
| 77 | 91 | ||
| 78 | 1. Creating N identical clones, but each with a unique transform. (Ex: N | 92 | 1. Creating N identical clones, but each with a unique transform. (Ex: N |
| 79 | animated characters animated in unison but located in different locations.) | 93 | 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: | 94 | 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.) | 95 | The same N animated characters, but each of them now being animated separately.) |
| 82 | 96 | ||
| 83 | Some scene graphs | 97 | Some scene graphs |
| 84 | ([Panda3D](https://docs.panda3d.org/1.10/python/programming/scene-graph/instancing)) | 98 | ([Panda3D](https://docs.panda3d.org/1.10/python/programming/scene-graph/instancing)) |
| @@ -87,10 +101,10 @@ to have multiple parents. This turns the scene graph into a DAG and adds a | |||
| 87 | number of complications for us: | 101 | number of complications for us: |
| 88 | 102 | ||
| 89 | 1. Shared ownership of children. We would now need some sort of ref counting or | 103 | 1. Shared ownership of children. We would now need some sort of ref counting or |
| 90 | deferred GC to delete nodes and their subtrees. | 104 | deferred GC to delete nodes and their subtrees. |
| 91 | 2. Nodes no longer have a unique parent. | 105 | 2. Nodes no longer have a unique parent. |
| 92 | 3. Given a node, we can no longer determine its location (which parent link do | 106 | 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). | 107 | you follow?), or any attribute that is derived from its parent(s). |
| 94 | 108 | ||
| 95 | In our case, we stick to strict tree hierarchies. | 109 | In our case, we stick to strict tree hierarchies. |
| 96 | 110 | ||
| @@ -131,6 +145,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 | 145 | of the library (Gfx, render backend, scene or renderer). This includes functions |
| 132 | to compute irradiance maps, create procedural geometry, etc. | 146 | to compute irradiance maps, create procedural geometry, etc. |
| 133 | 147 | ||
| 148 | ### Memory management | ||
| 149 | |||
| 150 | TODO | ||
| 151 | |||
| 134 | ## Ideas for Future Work | 152 | ## Ideas for Future Work |
| 135 | 153 | ||
| 136 | - Render graphs to allow for custom multi-pass rendering algorithms. | 154 | - Render graphs to allow for custom multi-pass rendering algorithms. |
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 7703ccb..588ee87 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.0) | 1 | cmake_minimum_required(VERSION 3.5) |
| 2 | 2 | ||
| 3 | add_subdirectory(contrib/glad) | 3 | add_subdirectory(contrib/glad) |
| 4 | 4 | ||
diff --git a/app/demo/CMakeLists.txt b/app/demo/CMakeLists.txt index bbb3aae..58d77cc 100644 --- a/app/demo/CMakeLists.txt +++ b/app/demo/CMakeLists.txt | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.0) | 1 | cmake_minimum_required(VERSION 3.5) |
| 2 | 2 | ||
| 3 | project(gfxappdemo) | 3 | project(gfxappdemo) |
| 4 | 4 | ||
diff --git a/app/include/gfx/app.h b/app/include/gfx/app.h index 77b6ad2..3017707 100644 --- a/app/include/gfx/app.h +++ b/app/include/gfx/app.h | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <stdbool.h> | 3 | typedef struct GfxApp GfxApp; |
| 4 | |||
| 5 | typedef struct GfxAppState GfxAppState; | 4 | typedef struct GfxAppState GfxAppState; |
| 6 | 5 | ||
| 7 | /// Application settings. | 6 | /// Application settings. |
| @@ -16,11 +15,11 @@ typedef struct GfxAppDesc { | |||
| 16 | GfxAppState* app_state; | 15 | GfxAppState* app_state; |
| 17 | } GfxAppDesc; | 16 | } GfxAppDesc; |
| 18 | 17 | ||
| 19 | typedef bool (*GfxAppInit)(GfxAppState*, int argc, const char** argv); | 18 | typedef bool (*GfxAppInit)(GfxApp*, GfxAppState*, int argc, const char** argv); |
| 20 | typedef void (*GfxAppShutdown)(GfxAppState*); | 19 | typedef void (*GfxAppShutdown)(GfxApp*, GfxAppState*); |
| 21 | typedef void (*GfxAppUpdate)(GfxAppState*, double t, double dt); | 20 | typedef void (*GfxAppUpdate)(GfxApp*, GfxAppState*, double t, double dt); |
| 22 | typedef void (*GfxAppRender)(GfxAppState*); | 21 | typedef void (*GfxAppRender)(const GfxApp*, GfxAppState*); |
| 23 | typedef void (*GfxAppResize)(GfxAppState*, int width, int height); | 22 | typedef void (*GfxAppResize)(GfxApp*, GfxAppState*, int width, int height); |
| 24 | 23 | ||
| 25 | /// Application callback functions. | 24 | /// Application callback functions. |
| 26 | typedef struct GfxAppCallbacks { | 25 | typedef struct GfxAppCallbacks { |
| @@ -64,6 +63,7 @@ typedef enum Key { | |||
| 64 | KeyX, | 63 | KeyX, |
| 65 | KeyY, | 64 | KeyY, |
| 66 | KeyZ, | 65 | KeyZ, |
| 66 | KeyLShift, | ||
| 67 | } Key; | 67 | } Key; |
| 68 | 68 | ||
| 69 | #ifdef __cplusplus | 69 | #ifdef __cplusplus |
| @@ -74,13 +74,13 @@ extern "C" { | |||
| 74 | bool gfx_app_run(const GfxAppDesc*, const GfxAppCallbacks*); | 74 | bool gfx_app_run(const GfxAppDesc*, const GfxAppCallbacks*); |
| 75 | 75 | ||
| 76 | /// Get the mouse coordinates relative to the app's window. | 76 | /// Get the mouse coordinates relative to the app's window. |
| 77 | void gfx_app_get_mouse_position(double* x, double* y); | 77 | void gfx_app_get_mouse_position(GfxApp*, double* x, double* y); |
| 78 | 78 | ||
| 79 | /// Return if the given mouse button is pressed. | 79 | /// Return if the given mouse button is pressed. |
| 80 | bool gfx_app_is_mouse_button_pressed(MouseButton); | 80 | bool gfx_app_is_mouse_button_pressed(GfxApp*, MouseButton); |
| 81 | 81 | ||
| 82 | /// Return true if the given key is pressed. | 82 | /// Return true if the given key is pressed. |
| 83 | bool gfx_app_is_key_pressed(Key); | 83 | bool gfx_app_is_key_pressed(GfxApp*, Key); |
| 84 | 84 | ||
| 85 | #ifdef __cplusplus | 85 | #ifdef __cplusplus |
| 86 | } // extern "C" | 86 | } // extern "C" |
| @@ -102,11 +102,10 @@ bool gfx_app_is_key_pressed(Key); | |||
| 102 | .title = TITLE, \ | 102 | .title = TITLE, \ |
| 103 | .app_state = &app_state, \ | 103 | .app_state = &app_state, \ |
| 104 | }, \ | 104 | }, \ |
| 105 | &(GfxAppCallbacks){ \ | 105 | &(GfxAppCallbacks){.init = Init, \ |
| 106 | .init = (GfxAppInit)Init, \ | 106 | .shutdown = Shutdown, \ |
| 107 | .shutdown = (GfxAppShutdown)Shutdown, \ | 107 | .update = Update, \ |
| 108 | .update = (GfxAppUpdate)Update, \ | 108 | .render = Render, \ |
| 109 | .render = (GfxAppRender)Render, \ | 109 | .resize = Resize}); \ |
| 110 | .resize = (GfxAppResize)Resize}); \ | ||
| 111 | return 0; \ | 110 | return 0; \ |
| 112 | } | 111 | } |
diff --git a/app/src/app.c b/app/src/app.c index 9b816ee..6eaa3d6 100644 --- a/app/src/app.c +++ b/app/src/app.c | |||
| @@ -24,7 +24,7 @@ static GfxApp g_gfx_app; | |||
| 24 | 24 | ||
| 25 | /// Called by GLFW when the window is resized. | 25 | /// Called by GLFW when the window is resized. |
| 26 | static void on_resize(GLFWwindow* window, int width, int height) { | 26 | static void on_resize(GLFWwindow* window, int width, int height) { |
| 27 | (*g_gfx_app.callbacks.resize)(g_gfx_app.app_state, width, height); | 27 | (*g_gfx_app.callbacks.resize)(&g_gfx_app, g_gfx_app.app_state, width, height); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /// Run the application's main loop. | 30 | /// Run the application's main loop. |
| @@ -41,12 +41,13 @@ static void loop(GfxApp* app) { | |||
| 41 | 41 | ||
| 42 | // Warm up the update to initialize the application's state. | 42 | // Warm up the update to initialize the application's state. |
| 43 | (*app->callbacks.update)( | 43 | (*app->callbacks.update)( |
| 44 | app->app_state, time_delta_to_sec(time), time_delta_to_sec(update_dt)); | 44 | app, app->app_state, time_delta_to_sec(time), |
| 45 | time_delta_to_sec(update_dt)); | ||
| 45 | 46 | ||
| 46 | // Warm up the rendering before entering the main loop. A renderer can | 47 | // Warm up the rendering before entering the main loop. A renderer can |
| 47 | // compile shaders and do other initialization the first time it renders a | 48 | // compile shaders and do other initialization the first time it renders a |
| 48 | // scene. | 49 | // scene. |
| 49 | (*app->callbacks.render)(app->app_state); | 50 | (*app->callbacks.render)(app, app->app_state); |
| 50 | glfwSwapBuffers(app->window); | 51 | glfwSwapBuffers(app->window); |
| 51 | 52 | ||
| 52 | timer_start(&timer); | 53 | timer_start(&timer); |
| @@ -56,14 +57,14 @@ static void loop(GfxApp* app) { | |||
| 56 | 57 | ||
| 57 | while (time_budget >= update_dt) { | 58 | while (time_budget >= update_dt) { |
| 58 | (*app->callbacks.update)( | 59 | (*app->callbacks.update)( |
| 59 | app->app_state, time_delta_to_sec(time), | 60 | app, app->app_state, time_delta_to_sec(time), |
| 60 | time_delta_to_sec(update_dt)); | 61 | time_delta_to_sec(update_dt)); |
| 61 | 62 | ||
| 62 | time += update_dt; | 63 | time += update_dt; |
| 63 | time_budget -= update_dt; | 64 | time_budget -= update_dt; |
| 64 | } | 65 | } |
| 65 | 66 | ||
| 66 | (*app->callbacks.render)(app->app_state); | 67 | (*app->callbacks.render)(app, app->app_state); |
| 67 | glfwSwapBuffers(app->window); | 68 | glfwSwapBuffers(app->window); |
| 68 | glfwPollEvents(); | 69 | glfwPollEvents(); |
| 69 | 70 | ||
| @@ -85,7 +86,7 @@ bool gfx_app_run(const GfxAppDesc* desc, const GfxAppCallbacks* callbacks) { | |||
| 85 | g_gfx_app.callbacks = *callbacks; | 86 | g_gfx_app.callbacks = *callbacks; |
| 86 | g_gfx_app.max_fps = desc->max_fps; | 87 | g_gfx_app.max_fps = desc->max_fps; |
| 87 | g_gfx_app.update_delta_time = desc->update_delta_time; | 88 | g_gfx_app.update_delta_time = desc->update_delta_time; |
| 88 | g_gfx_app.window = 0; | 89 | g_gfx_app.window = nullptr; |
| 89 | 90 | ||
| 90 | if (!glfwInit()) { | 91 | if (!glfwInit()) { |
| 91 | LOGE("glfwInit() failed"); | 92 | LOGE("glfwInit() failed"); |
| @@ -112,28 +113,33 @@ bool gfx_app_run(const GfxAppDesc* desc, const GfxAppCallbacks* callbacks) { | |||
| 112 | } | 113 | } |
| 113 | glfwMakeContextCurrent(g_gfx_app.window); | 114 | glfwMakeContextCurrent(g_gfx_app.window); |
| 114 | 115 | ||
| 115 | // Load GL before calling the application init clalback. | 116 | // Request adaptive sync if supported. |
| 116 | if (!gladLoadGL()) { | 117 | glfwSwapInterval(-1); |
| 117 | LOGE("Failed loading glad!"); | 118 | |
| 118 | return 0; | 119 | // Load GL before calling the application init callback. |
| 120 | // Use the GLFW loader. See: https://github.com/apitrace/apitrace/issues/954 | ||
| 121 | if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { | ||
| 122 | LOGE("Failed loading glad"); | ||
| 123 | goto cleanup; | ||
| 119 | } | 124 | } |
| 120 | 125 | ||
| 121 | // Initialize the application's state before setting any callbacks. | 126 | // Initialize the application's state before setting any callbacks. |
| 122 | if (!(*g_gfx_app.callbacks.init)( | 127 | if (!(*g_gfx_app.callbacks.init)( |
| 123 | g_gfx_app.app_state, desc->argc, desc->argv)) { | 128 | &g_gfx_app, g_gfx_app.app_state, desc->argc, desc->argv)) { |
| 124 | LOGE("Failed to initialize application"); | 129 | LOGE("Failed to initialize application"); |
| 125 | goto cleanup; | 130 | goto cleanup; |
| 126 | } | 131 | } |
| 127 | 132 | ||
| 128 | // Trigger an initial resize for convenience. | 133 | // Trigger an initial resize for convenience. |
| 129 | (*g_gfx_app.callbacks.resize)(g_gfx_app.app_state, desc->width, desc->height); | 134 | (*g_gfx_app.callbacks.resize)( |
| 135 | &g_gfx_app, g_gfx_app.app_state, desc->width, desc->height); | ||
| 130 | 136 | ||
| 131 | // Set GLFW callbacks now that the application has been initialized. | 137 | // Set GLFW callbacks now that the application has been initialized. |
| 132 | glfwSetWindowSizeCallback(g_gfx_app.window, on_resize); | 138 | glfwSetWindowSizeCallback(g_gfx_app.window, on_resize); |
| 133 | 139 | ||
| 134 | loop(&g_gfx_app); | 140 | loop(&g_gfx_app); |
| 135 | 141 | ||
| 136 | (*g_gfx_app.callbacks.shutdown)(g_gfx_app.app_state); | 142 | (*g_gfx_app.callbacks.shutdown)(&g_gfx_app, g_gfx_app.app_state); |
| 137 | 143 | ||
| 138 | success = true; | 144 | success = true; |
| 139 | 145 | ||
| @@ -145,21 +151,21 @@ cleanup: | |||
| 145 | return success; | 151 | return success; |
| 146 | } | 152 | } |
| 147 | 153 | ||
| 148 | void gfx_app_get_mouse_position(double* x, double* y) { | 154 | void gfx_app_get_mouse_position(GfxApp* app, double* x, double* y) { |
| 149 | glfwGetCursorPos(g_gfx_app.window, x, y); | 155 | glfwGetCursorPos(app->window, x, y); |
| 150 | } | 156 | } |
| 151 | 157 | ||
| 152 | static int to_glfw_mouse_button(MouseButton button); | 158 | static int to_glfw_mouse_button(MouseButton button); |
| 153 | 159 | ||
| 154 | bool gfx_app_is_mouse_button_pressed(MouseButton button) { | 160 | bool gfx_app_is_mouse_button_pressed(GfxApp* app, MouseButton button) { |
| 155 | return glfwGetMouseButton(g_gfx_app.window, to_glfw_mouse_button(button)) == | 161 | return glfwGetMouseButton(app->window, to_glfw_mouse_button(button)) == |
| 156 | GLFW_PRESS; | 162 | GLFW_PRESS; |
| 157 | } | 163 | } |
| 158 | 164 | ||
| 159 | static int to_glfw_key(Key key); | 165 | static int to_glfw_key(Key key); |
| 160 | 166 | ||
| 161 | bool gfx_app_is_key_pressed(Key key) { | 167 | bool gfx_app_is_key_pressed(GfxApp* app, Key key) { |
| 162 | return glfwGetKey(g_gfx_app.window, to_glfw_key(key)) == GLFW_PRESS; | 168 | return glfwGetKey(app->window, to_glfw_key(key)) == GLFW_PRESS; |
| 163 | } | 169 | } |
| 164 | 170 | ||
| 165 | static int to_glfw_mouse_button(MouseButton button) { | 171 | static int to_glfw_mouse_button(MouseButton button) { |
| @@ -227,5 +233,7 @@ static int to_glfw_key(Key key) { | |||
| 227 | return GLFW_KEY_Y; | 233 | return GLFW_KEY_Y; |
| 228 | case KeyZ: | 234 | case KeyZ: |
| 229 | return GLFW_KEY_Z; | 235 | return GLFW_KEY_Z; |
| 236 | case KeyLShift: | ||
| 237 | return GLFW_KEY_LEFT_SHIFT; | ||
| 230 | } | 238 | } |
| 231 | } | 239 | } |
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..3f86fc2 100644 --- a/gfx/contrib/cgltf-tangents/CMakeLists.txt +++ b/contrib/cgltf-tangents/CMakeLists.txt | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.0) | 1 | cmake_minimum_required(VERSION 3.5) |
| 2 | 2 | ||
| 3 | project(cgltf-tangents) | 3 | project(cgltf-tangents) |
| 4 | 4 | ||
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/include/gfx/scene.h b/gfx/include/gfx/scene.h deleted file mode 100644 index abcaa70..0000000 --- a/gfx/include/gfx/scene.h +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene/animation.h> | ||
| 4 | #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/node.h> | ||
| 10 | #include <gfx/scene/object.h> | ||
| 11 | #include <gfx/scene/scene.h> | ||
diff --git a/gfx/include/gfx/scene/camera.h b/gfx/include/gfx/scene/camera.h deleted file mode 100644 index 99d83fe..0000000 --- a/gfx/include/gfx/scene/camera.h +++ /dev/null | |||
| @@ -1,22 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <math/fwd.h> | ||
| 4 | |||
| 5 | typedef struct SceneNode SceneNode; | ||
| 6 | |||
| 7 | typedef struct SceneCamera SceneCamera; | ||
| 8 | |||
| 9 | /// Create a new camera. | ||
| 10 | SceneCamera* gfx_make_camera(); | ||
| 11 | |||
| 12 | /// Destroy the camera. | ||
| 13 | /// | ||
| 14 | /// The camera is conveniently removed from the scene graph and its parent scene | ||
| 15 | /// node is destroyed. | ||
| 16 | void gfx_destroy_camera(SceneCamera**); | ||
| 17 | |||
| 18 | /// Set the scene camera's math camera. | ||
| 19 | void gfx_set_camera_camera(SceneCamera* scene_camera, Camera* camera); | ||
| 20 | |||
| 21 | /// Get the scene camera's math camera. | ||
| 22 | Camera* gfx_get_camera_camera(SceneCamera*); | ||
diff --git a/gfx/include/gfx/scene/light.h b/gfx/include/gfx/scene/light.h deleted file mode 100644 index 132e344..0000000 --- a/gfx/include/gfx/scene/light.h +++ /dev/null | |||
| @@ -1,30 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | typedef struct Texture Texture; | ||
| 4 | |||
| 5 | typedef struct Light Light; | ||
| 6 | |||
| 7 | /// Light type. | ||
| 8 | typedef enum LightType { EnvironmentLightType } LightType; | ||
| 9 | |||
| 10 | /// Describes an environment light. | ||
| 11 | typedef struct EnvironmentLightDesc { | ||
| 12 | const Texture* environment_map; | ||
| 13 | } EnvironmentLightDesc; | ||
| 14 | |||
| 15 | /// Describes a light. | ||
| 16 | typedef struct LightDesc { | ||
| 17 | LightType type; | ||
| 18 | union { | ||
| 19 | EnvironmentLightDesc environment; | ||
| 20 | } light; | ||
| 21 | } LightDesc; | ||
| 22 | |||
| 23 | /// Create a light. | ||
| 24 | Light* gfx_make_light(const LightDesc*); | ||
| 25 | |||
| 26 | /// Destroy the light. | ||
| 27 | /// | ||
| 28 | /// The light is conveniently removed from the scene graph and its parent scene | ||
| 29 | /// node is destroyed. | ||
| 30 | void gfx_destroy_light(Light**); | ||
diff --git a/gfx/include/gfx/scene/material.h b/gfx/include/gfx/scene/material.h deleted file mode 100644 index bca664e..0000000 --- a/gfx/include/gfx/scene/material.h +++ /dev/null | |||
| @@ -1,25 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/core.h> | ||
| 4 | #include <gfx/sizes.h> | ||
| 5 | |||
| 6 | typedef struct Material Material; | ||
| 7 | |||
| 8 | /// Describes a material. | ||
| 9 | /// | ||
| 10 | /// A material holds a shader program and a set of shader-specific uniform | ||
| 11 | /// variables. Two materials can share the same shader, but shader parameters | ||
| 12 | /// generally give two materials a different appearance. | ||
| 13 | typedef struct MaterialDesc { | ||
| 14 | ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; | ||
| 15 | int num_uniforms; | ||
| 16 | } MaterialDesc; | ||
| 17 | |||
| 18 | /// Create a material. | ||
| 19 | Material* gfx_make_material(const MaterialDesc*); | ||
| 20 | |||
| 21 | /// Destroy the material. | ||
| 22 | /// | ||
| 23 | /// The caller must make sure that no Mesh points to the given Material. | ||
| 24 | /// For a safe purge of unused resources, see scene_purge(). | ||
| 25 | void gfx_destroy_material(Material**); | ||
diff --git a/gfx/include/gfx/scene/mesh.h b/gfx/include/gfx/scene/mesh.h deleted file mode 100644 index 0d3b4d4..0000000 --- a/gfx/include/gfx/scene/mesh.h +++ /dev/null | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | typedef struct Geometry Geometry; | ||
| 4 | typedef struct Material Material; | ||
| 5 | typedef struct ShaderProgram ShaderProgram; | ||
| 6 | |||
| 7 | typedef struct Mesh Mesh; | ||
| 8 | |||
| 9 | /// Describes a mesh. | ||
| 10 | typedef struct MeshDesc { | ||
| 11 | const Geometry* geometry; | ||
| 12 | const Material* material; | ||
| 13 | ShaderProgram* shader; | ||
| 14 | } MeshDesc; | ||
| 15 | |||
| 16 | /// Create a mesh. | ||
| 17 | Mesh* gfx_make_mesh(const MeshDesc*); | ||
| 18 | |||
| 19 | /// Destroy the mesh. | ||
| 20 | /// | ||
| 21 | /// The caller must make sure that no SceneObject points to the given Mesh. | ||
| 22 | /// For a safe purge of unused resources, see scene_purge(). | ||
| 23 | void gfx_destroy_mesh(Mesh**); | ||
diff --git a/gfx/include/gfx/scene/model.h b/gfx/include/gfx/scene/model.h deleted file mode 100644 index 42f85d4..0000000 --- a/gfx/include/gfx/scene/model.h +++ /dev/null | |||
| @@ -1,12 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | typedef struct Anima Anima; | ||
| 4 | typedef struct Model Model; | ||
| 5 | typedef struct SceneNode SceneNode; | ||
| 6 | |||
| 7 | /// Return the model's anima, or null if the model is not animated. | ||
| 8 | Anima* gfx_get_model_anima(Model*); | ||
| 9 | |||
| 10 | /// Return the model's root node. | ||
| 11 | const SceneNode* gfx_get_model_root(const Model*); | ||
| 12 | SceneNode* gfx_get_model_root_mut(Model*); | ||
diff --git a/gfx/include/gfx/scene/node.h b/gfx/include/gfx/scene/node.h deleted file mode 100644 index a2c2836..0000000 --- a/gfx/include/gfx/scene/node.h +++ /dev/null | |||
| @@ -1,156 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "animation.h" | ||
| 4 | |||
| 5 | #include <math/fwd.h> | ||
| 6 | #include <math/mat4.h> | ||
| 7 | |||
| 8 | #include <stdint.h> | ||
| 9 | |||
| 10 | typedef struct Anima Anima; | ||
| 11 | typedef struct Light Light; | ||
| 12 | typedef struct Model Model; | ||
| 13 | typedef struct SceneCamera SceneCamera; | ||
| 14 | typedef struct SceneObject SceneObject; | ||
| 15 | |||
| 16 | /// Scene node type. | ||
| 17 | typedef enum NodeType { | ||
| 18 | LogicalNode, | ||
| 19 | AnimaNode, | ||
| 20 | CameraNode, | ||
| 21 | LightNode, | ||
| 22 | ModelNode, | ||
| 23 | ObjectNode, | ||
| 24 | } NodeType; | ||
| 25 | |||
| 26 | /// A node in the scene graph. | ||
| 27 | /// | ||
| 28 | /// Scene nodes take ownership of the object they are associated with (Camera, | ||
| 29 | /// Light, SceneObject, etc), as well as of child nodes. | ||
| 30 | typedef struct SceneNode SceneNode; | ||
| 31 | |||
| 32 | // ----------------------------------------------------------------------------- | ||
| 33 | // Constructors and destructor. | ||
| 34 | // ----------------------------------------------------------------------------- | ||
| 35 | |||
| 36 | /// Create a new scene node. | ||
| 37 | /// | ||
| 38 | /// This node does not contain any camera, light, object, etc. and exists simply | ||
| 39 | /// as a logical and spatial construct. | ||
| 40 | SceneNode* gfx_make_node(); | ||
| 41 | |||
| 42 | /// Create an anima node. | ||
| 43 | SceneNode* gfx_make_anima_node(Anima*); | ||
| 44 | |||
| 45 | /// Create a new camera node. | ||
| 46 | SceneNode* gfx_make_camera_node(SceneCamera*); | ||
| 47 | |||
| 48 | /// Create a new light node. | ||
| 49 | SceneNode* gfx_make_light_node(Light*); | ||
| 50 | |||
| 51 | /// Create a new model node. | ||
| 52 | SceneNode* gfx_make_model_node(Model*); | ||
| 53 | |||
| 54 | /// Create a new object node. | ||
| 55 | SceneNode* gfx_make_object_node(SceneObject*); | ||
| 56 | |||
| 57 | /// Make the node an anima node. | ||
| 58 | void gfx_construct_anima_node(SceneNode*, Anima*); | ||
| 59 | |||
| 60 | /// Make the node a camera node. | ||
| 61 | void gfx_construct_camera_node(SceneNode*, SceneCamera*); | ||
| 62 | |||
| 63 | /// Make the node a light node. | ||
| 64 | void gfx_construct_light_node(SceneNode*, Light*); | ||
| 65 | |||
| 66 | /// Make the node a model node. | ||
| 67 | void gfx_construct_model_node(SceneNode*, Model*); | ||
| 68 | |||
| 69 | /// Make the node an object node. | ||
| 70 | void gfx_construct_object_node(SceneNode*, SceneObject*); | ||
| 71 | |||
| 72 | /// Recursively destroy the scene node and its children. | ||
| 73 | /// | ||
| 74 | /// The scene node and its children are removed from the scene graph. | ||
| 75 | /// | ||
| 76 | /// Node resources -- cameras, lights, objects, etc. -- are also destroyed. | ||
| 77 | void gfx_destroy_node(SceneNode**); | ||
| 78 | |||
| 79 | // ----------------------------------------------------------------------------- | ||
| 80 | // Getters. | ||
| 81 | // ----------------------------------------------------------------------------- | ||
| 82 | |||
| 83 | /// Get the node's type. | ||
| 84 | NodeType gfx_get_node_type(const SceneNode*); | ||
| 85 | |||
| 86 | /// Get the node's anima. | ||
| 87 | /// | ||
| 88 | /// The node must be of type AnimaNode. | ||
| 89 | const Anima* gfx_get_node_anima(const SceneNode*); | ||
| 90 | Anima* gfx_get_node_anima_mut(SceneNode*); | ||
| 91 | |||
| 92 | /// Get the node's camera. | ||
| 93 | /// | ||
| 94 | /// The node must be of type CameraNode. | ||
| 95 | const SceneCamera* gfx_get_node_camera(const SceneNode* node); | ||
| 96 | SceneCamera* gfx_get_node_camera_mut(SceneNode* node); | ||
| 97 | |||
| 98 | /// Get the node's light. | ||
| 99 | /// | ||
| 100 | /// The node must be of type LightNode. | ||
| 101 | const Light* gfx_get_node_light(const SceneNode*); | ||
| 102 | Light* gfx_get_node_light_mut(SceneNode*); | ||
| 103 | |||
| 104 | /// Get the node's model. | ||
| 105 | /// | ||
| 106 | /// The node must be of type ModelNode. | ||
| 107 | const Model* gfx_get_node_model(const SceneNode*); | ||
| 108 | Model* gfx_get_node_model_mut(SceneNode*); | ||
| 109 | |||
| 110 | /// Get the node's scene object. | ||
| 111 | /// | ||
| 112 | /// The node must be of type ObjectNode. | ||
| 113 | const SceneObject* gfx_get_node_object(const SceneNode*); | ||
| 114 | SceneObject* gfx_get_node_object_mut(SceneNode*); | ||
| 115 | |||
| 116 | /// Get the node's parent. | ||
| 117 | const SceneNode* gfx_get_node_parent(const SceneNode*); | ||
| 118 | SceneNode* gfx_get_node_parent_mut(SceneNode*); | ||
| 119 | |||
| 120 | /// Get the node's first child. | ||
| 121 | const SceneNode* gfx_get_node_child(const SceneNode*); | ||
| 122 | SceneNode* gfx_get_node_child_mut(SceneNode*); | ||
| 123 | |||
| 124 | /// Get the node's immediate sibling. | ||
| 125 | const SceneNode* gfx_get_node_sibling(const SceneNode*); | ||
| 126 | SceneNode* gfx_get_node_sibling_mut(SceneNode*); | ||
| 127 | |||
| 128 | /// Get the node's (local) transform. | ||
| 129 | mat4 gfx_get_node_transform(const SceneNode*); | ||
| 130 | |||
| 131 | /// Get the node's global transform. | ||
| 132 | mat4 gfx_get_node_global_transform(const SceneNode*); | ||
| 133 | |||
| 134 | // ----------------------------------------------------------------------------- | ||
| 135 | // Setters. | ||
| 136 | // ----------------------------------------------------------------------------- | ||
| 137 | |||
| 138 | /// Set the node's parent. | ||
| 139 | /// | ||
| 140 | /// Pass in null to unwire from the existing parent, if one exists. | ||
| 141 | void gfx_set_node_parent(SceneNode*, SceneNode* parent_node); | ||
| 142 | |||
| 143 | /// Set the node's (local) transform. | ||
| 144 | void gfx_set_node_transform(SceneNode*, const mat4* transform); | ||
| 145 | |||
| 146 | /// Set the node's position. | ||
| 147 | void gfx_set_node_position(SceneNode*, const vec3* position); | ||
| 148 | |||
| 149 | /// Set the node's rotation. | ||
| 150 | void gfx_set_node_rotation(SceneNode*, const quat* rotation); | ||
| 151 | |||
| 152 | /// Set the node's rotation. | ||
| 153 | void gfx_set_node_rotation_mat(SceneNode*, const mat4* rotation); | ||
| 154 | |||
| 155 | /// Log the node's hierarchy. | ||
| 156 | void gfx_log_node_hierarchy(const SceneNode*); | ||
diff --git a/gfx/include/gfx/scene/object.h b/gfx/include/gfx/scene/object.h deleted file mode 100644 index 7579d29..0000000 --- a/gfx/include/gfx/scene/object.h +++ /dev/null | |||
| @@ -1,39 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/sizes.h> | ||
| 4 | |||
| 5 | #include <math/fwd.h> | ||
| 6 | |||
| 7 | #include <math/aabb3.h> | ||
| 8 | |||
| 9 | typedef struct Mesh Mesh; | ||
| 10 | typedef struct SceneNode SceneNode; | ||
| 11 | typedef struct Skeleton Skeleton; | ||
| 12 | |||
| 13 | typedef struct SceneObject SceneObject; | ||
| 14 | |||
| 15 | typedef struct ObjectDesc { | ||
| 16 | size_t num_meshes; | ||
| 17 | Mesh* meshes[GFX_MAX_NUM_MESHES]; | ||
| 18 | } ObjectDesc; | ||
| 19 | |||
| 20 | /// Create a new object. | ||
| 21 | SceneObject* gfx_make_object(const ObjectDesc*); | ||
| 22 | |||
| 23 | /// Destroy the object. | ||
| 24 | /// | ||
| 25 | /// The object is conveniently removed from the scene graph and its parent scene | ||
| 26 | /// node is destroyed. | ||
| 27 | void gfx_destroy_object(SceneObject**); | ||
| 28 | |||
| 29 | /// Set the object's skeleton. | ||
| 30 | void gfx_set_object_skeleton(SceneObject*, const Skeleton*); | ||
| 31 | |||
| 32 | /// Get the object's skeleton. | ||
| 33 | /// Return null if the object has no skeleton. | ||
| 34 | const Skeleton* gfx_get_object_skeleton(const SceneObject*); | ||
| 35 | |||
| 36 | /// Gets the object's bounding box. | ||
| 37 | /// | ||
| 38 | /// The object's bounding box is the bounding box of its mesh geometries. | ||
| 39 | aabb3 gfx_get_object_aabb(const SceneObject*); | ||
diff --git a/gfx/include/gfx/scene/scene.h b/gfx/include/gfx/scene/scene.h deleted file mode 100644 index 0d96210..0000000 --- a/gfx/include/gfx/scene/scene.h +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <math/defs.h> | ||
| 4 | #include <math/fwd.h> | ||
| 5 | |||
| 6 | typedef struct SceneNode SceneNode; | ||
| 7 | |||
| 8 | typedef struct Scene Scene; | ||
| 9 | |||
| 10 | /// Create a new scene. | ||
| 11 | Scene* gfx_make_scene(void); | ||
| 12 | |||
| 13 | /// Destroy the scene. | ||
| 14 | /// | ||
| 15 | /// This function destroys the scene and all objects that it owns (scene | ||
| 16 | /// objects, cameras, lights, etc), but not objects that could be shared with | ||
| 17 | /// other scenes (meshes, materials, etc). | ||
| 18 | void gfx_destroy_scene(Scene**); | ||
| 19 | |||
| 20 | /// Get the scene's root node. | ||
| 21 | SceneNode* gfx_get_scene_root(Scene*); | ||
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/camera.c b/gfx/src/scene/camera.c deleted file mode 100644 index be7d806..0000000 --- a/gfx/src/scene/camera.c +++ /dev/null | |||
| @@ -1,37 +0,0 @@ | |||
| 1 | #include "camera_impl.h" | ||
| 2 | |||
| 3 | #include "node_impl.h" | ||
| 4 | #include "scene_memory.h" | ||
| 5 | |||
| 6 | #include <assert.h> | ||
| 7 | |||
| 8 | SceneCamera* gfx_make_camera() { | ||
| 9 | SceneCamera* camera = mem_alloc_camera(); | ||
| 10 | |||
| 11 | camera->camera = camera_perspective( | ||
| 12 | /*fovy=*/90.0 * TO_RAD, /*aspect=*/16.0 / 9.0, | ||
| 13 | /*near=*/0.1, /*far=*/1000); | ||
| 14 | |||
| 15 | return camera; | ||
| 16 | } | ||
| 17 | |||
| 18 | void gfx_destroy_camera(SceneCamera** camera) { | ||
| 19 | assert(camera); | ||
| 20 | if (*camera) { | ||
| 21 | if ((*camera)->parent.val) { | ||
| 22 | gfx_del_node((*camera)->parent); | ||
| 23 | } | ||
| 24 | mem_free_camera(camera); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | void gfx_set_camera_camera(SceneCamera* scene_camera, Camera* camera) { | ||
| 29 | assert(scene_camera); | ||
| 30 | assert(camera); | ||
| 31 | scene_camera->camera = *camera; | ||
| 32 | } | ||
| 33 | |||
| 34 | Camera* gfx_get_camera_camera(SceneCamera* camera) { | ||
| 35 | assert(camera); | ||
| 36 | return &camera->camera; | ||
| 37 | } | ||
diff --git a/gfx/src/scene/camera_impl.h b/gfx/src/scene/camera_impl.h deleted file mode 100644 index 20c3890..0000000 --- a/gfx/src/scene/camera_impl.h +++ /dev/null | |||
| @@ -1,12 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene/camera.h> | ||
| 4 | |||
| 5 | #include "types.h" | ||
| 6 | |||
| 7 | #include <math/camera.h> | ||
| 8 | |||
| 9 | typedef struct SceneCamera { | ||
| 10 | Camera camera; | ||
| 11 | node_idx parent; // Parent SceneNode. | ||
| 12 | } SceneCamera; | ||
diff --git a/gfx/src/scene/material.c b/gfx/src/scene/material.c deleted file mode 100644 index 3248243..0000000 --- a/gfx/src/scene/material.c +++ /dev/null | |||
| @@ -1,57 +0,0 @@ | |||
| 1 | #include "material_impl.h" | ||
| 2 | |||
| 3 | #include "scene_memory.h" | ||
| 4 | |||
| 5 | #include <gfx/core.h> | ||
| 6 | |||
| 7 | static void material_make(Material* material, const MaterialDesc* desc) { | ||
| 8 | assert(material); | ||
| 9 | assert(desc); | ||
| 10 | assert(desc->num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | ||
| 11 | material->num_uniforms = desc->num_uniforms; | ||
| 12 | for (int i = 0; i < desc->num_uniforms; ++i) { | ||
| 13 | material->uniforms[i] = desc->uniforms[i]; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | Material* gfx_make_material(const MaterialDesc* desc) { | ||
| 18 | assert(desc); | ||
| 19 | Material* material = mem_alloc_material(); | ||
| 20 | material_make(material, desc); | ||
| 21 | return material; | ||
| 22 | } | ||
| 23 | |||
| 24 | void gfx_destroy_material(Material** material) { mem_free_material(material); } | ||
| 25 | |||
| 26 | static void set_uniform(ShaderProgram* prog, const ShaderUniform* uniform) { | ||
| 27 | switch (uniform->type) { | ||
| 28 | case UniformTexture: | ||
| 29 | gfx_set_texture_uniform(prog, uniform->name.str, uniform->value.texture); | ||
| 30 | break; | ||
| 31 | case UniformMat4: | ||
| 32 | gfx_set_mat4_uniform(prog, uniform->name.str, &uniform->value.mat4); | ||
| 33 | break; | ||
| 34 | case UniformVec3: | ||
| 35 | gfx_set_vec3_uniform(prog, uniform->name.str, uniform->value.vec3); | ||
| 36 | break; | ||
| 37 | case UniformVec4: | ||
| 38 | gfx_set_vec4_uniform(prog, uniform->name.str, uniform->value.vec4); | ||
| 39 | break; | ||
| 40 | case UniformFloat: | ||
| 41 | gfx_set_float_uniform(prog, uniform->name.str, uniform->value.scalar); | ||
| 42 | break; | ||
| 43 | case UniformMat4Array: | ||
| 44 | gfx_set_mat4_array_uniform( | ||
| 45 | prog, uniform->name.str, uniform->value.array.values, | ||
| 46 | uniform->value.array.count); | ||
| 47 | break; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | void material_activate(ShaderProgram* shader, const Material* material) { | ||
| 52 | assert(material); | ||
| 53 | for (int i = 0; i < material->num_uniforms; ++i) { | ||
| 54 | const ShaderUniform* uniform = &material->uniforms[i]; | ||
| 55 | set_uniform(shader, uniform); | ||
| 56 | } | ||
| 57 | } | ||
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/src/scene/mesh_impl.h b/gfx/src/scene/mesh_impl.h deleted file mode 100644 index 560b77e..0000000 --- a/gfx/src/scene/mesh_impl.h +++ /dev/null | |||
| @@ -1,12 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene/mesh.h> | ||
| 4 | |||
| 5 | typedef struct Mesh { | ||
| 6 | const Geometry* geometry; | ||
| 7 | const Material* material; | ||
| 8 | ShaderProgram* shader; | ||
| 9 | } Mesh; | ||
| 10 | |||
| 11 | // TODO: a mesh_render() that takes a transform, applies the material and the | ||
| 12 | // transform, and then renders the geometry. | ||
diff --git a/gfx/src/scene/scene.c b/gfx/src/scene/scene.c deleted file mode 100644 index 54452dd..0000000 --- a/gfx/src/scene/scene.c +++ /dev/null | |||
| @@ -1,25 +0,0 @@ | |||
| 1 | #include "scene_impl.h" | ||
| 2 | |||
| 3 | #include "node_impl.h" | ||
| 4 | #include "scene_memory.h" | ||
| 5 | |||
| 6 | #include <assert.h> | ||
| 7 | |||
| 8 | Scene* gfx_make_scene(void) { | ||
| 9 | Scene* scene = mem_alloc_scene(); | ||
| 10 | scene->root = gfx_make_node(); | ||
| 11 | return scene; | ||
| 12 | } | ||
| 13 | |||
| 14 | void gfx_destroy_scene(Scene** scene) { | ||
| 15 | assert(scene); | ||
| 16 | if (*scene) { | ||
| 17 | gfx_destroy_node(&(*scene)->root); | ||
| 18 | mem_free_scene(scene); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | SceneNode* gfx_get_scene_root(Scene* scene) { | ||
| 23 | assert(scene); | ||
| 24 | return scene->root; | ||
| 25 | } | ||
diff --git a/gfx/src/scene/scene_impl.h b/gfx/src/scene/scene_impl.h deleted file mode 100644 index 992f620..0000000 --- a/gfx/src/scene/scene_impl.h +++ /dev/null | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene/scene.h> | ||
| 4 | |||
| 5 | #include "types.h" | ||
| 6 | |||
| 7 | typedef struct SceneNode SceneNode; | ||
| 8 | |||
| 9 | typedef struct Scene { | ||
| 10 | SceneNode* root; | ||
| 11 | scene_idx next; | ||
| 12 | scene_idx prev; | ||
| 13 | } Scene; | ||
diff --git a/gfx/include/gfx/scene/animation.h b/include/gfx/animation.h index d95b895..b55e575 100644 --- a/gfx/include/gfx/scene/animation.h +++ b/include/gfx/animation.h | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include "node.h" | ||
| 4 | #include "object.h" | ||
| 5 | #include <gfx/sizes.h> | 3 | #include <gfx/sizes.h> |
| 6 | 4 | ||
| 7 | #include <cstring.h> | 5 | #include <cstring.h> |
| @@ -15,8 +13,7 @@ | |||
| 15 | #include <stddef.h> | 13 | #include <stddef.h> |
| 16 | #include <stdint.h> | 14 | #include <stdint.h> |
| 17 | 15 | ||
| 18 | typedef struct Buffer Buffer; | 16 | typedef struct Buffer Buffer; |
| 19 | typedef struct SceneNode SceneNode; | ||
| 20 | 17 | ||
| 21 | typedef struct Anima Anima; | 18 | typedef struct Anima Anima; |
| 22 | typedef struct Joint Joint; | 19 | typedef struct Joint Joint; |
diff --git a/gfx/include/gfx/asset.h b/include/gfx/asset.h index caf40c1..3018cfd 100644 --- a/gfx/include/gfx/asset.h +++ b/include/gfx/asset.h | |||
| @@ -18,8 +18,8 @@ typedef enum AssetOrigin { | |||
| 18 | 18 | ||
| 19 | /// Describes a texture's colour space. | 19 | /// Describes a texture's colour space. |
| 20 | typedef enum TextureColourSpace { | 20 | typedef enum TextureColourSpace { |
| 21 | sRGB, // The most likely default. | 21 | LinearColourSpace, // The most likely default. |
| 22 | LinearColourSpace, | 22 | sRGB, |
| 23 | } TextureColourSpace; | 23 | } TextureColourSpace; |
| 24 | 24 | ||
| 25 | /// Describes a command to load a texture. | 25 | /// Describes a command to load a texture. |
diff --git a/gfx/include/gfx/core.h b/include/gfx/core.h index 44509c9..8f01081 100644 --- a/gfx/include/gfx/core.h +++ b/include/gfx/core.h | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | /// rendering operations. | 4 | /// rendering operations. |
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "sizes.h" | 7 | #include <gfx/sizes.h> |
| 8 | 8 | ||
| 9 | #include <math/aabb3.h> | 9 | #include <math/aabb3.h> |
| 10 | #include <math/fwd.h> | 10 | #include <math/fwd.h> |
| @@ -93,6 +93,7 @@ typedef struct BufferDesc { | |||
| 93 | size_t offset_bytes; \ | 93 | size_t offset_bytes; \ |
| 94 | size_t size_bytes; \ | 94 | size_t size_bytes; \ |
| 95 | size_t stride_bytes; \ | 95 | size_t stride_bytes; \ |
| 96 | size_t count; \ | ||
| 96 | } NAME; | 97 | } NAME; |
| 97 | 98 | ||
| 98 | /// A buffer view for untyped data. | 99 | /// A buffer view for untyped data. |
| @@ -183,11 +184,12 @@ typedef struct ShaderProgramDesc { | |||
| 183 | 184 | ||
| 184 | /// Shader uniform type. | 185 | /// Shader uniform type. |
| 185 | typedef enum { | 186 | typedef enum { |
| 187 | UniformInt, | ||
| 186 | UniformFloat, | 188 | UniformFloat, |
| 187 | UniformMat4, | 189 | UniformMat4, |
| 188 | UniformTexture, | ||
| 189 | UniformVec3, | 190 | UniformVec3, |
| 190 | UniformVec4, | 191 | UniformVec4, |
| 192 | UniformTexture, | ||
| 191 | UniformMat4Array | 193 | UniformMat4Array |
| 192 | } UniformType; | 194 | } UniformType; |
| 193 | 195 | ||
| @@ -200,10 +202,11 @@ typedef struct ShaderUniform { | |||
| 200 | UniformType type; | 202 | UniformType type; |
| 201 | union { | 203 | union { |
| 202 | const Texture* texture; | 204 | const Texture* texture; |
| 203 | mat4 mat4; | 205 | int uniform_int; |
| 204 | vec3 vec3; | 206 | float uniform_float; |
| 205 | vec4 vec4; | 207 | mat4 uniform_mat4; |
| 206 | float scalar; | 208 | vec3 uniform_vec3; |
| 209 | vec4 uniform_vec4; | ||
| 207 | struct { | 210 | struct { |
| 208 | size_t count; | 211 | size_t count; |
| 209 | union { | 212 | union { |
| @@ -219,6 +222,7 @@ typedef enum { Texture2D, TextureCubeMap } TextureDimension; | |||
| 219 | /// Texture data format. | 222 | /// Texture data format. |
| 220 | typedef enum { | 223 | typedef enum { |
| 221 | TextureDepth, | 224 | TextureDepth, |
| 225 | TextureR8, | ||
| 222 | TextureRG16, | 226 | TextureRG16, |
| 223 | TextureRG16F, | 227 | TextureRG16F, |
| 224 | TextureRGB8, | 228 | TextureRGB8, |
| @@ -473,11 +477,21 @@ void gfx_deactivate_shader_program(const ShaderProgram*); | |||
| 473 | /// | 477 | /// |
| 474 | /// This function should be called after setting all of the uniform variables | 478 | /// This function should be called after setting all of the uniform variables |
| 475 | /// and prior to issuing a draw call. | 479 | /// and prior to issuing a draw call. |
| 480 | /// | ||
| 481 | /// The given program must have been activated prior to this call with | ||
| 482 | /// gfx_activate_shader_program(). | ||
| 476 | void gfx_apply_uniforms(const ShaderProgram*); | 483 | void gfx_apply_uniforms(const ShaderProgram*); |
| 477 | 484 | ||
| 478 | /// Set the texture uniform. | 485 | /// Set the uniform. |
| 486 | void gfx_set_uniform(ShaderProgram* prog, const ShaderUniform* uniform); | ||
| 487 | |||
| 488 | /// Set the int uniform. | ||
| 479 | /// Has no effect if the shader does not contain the given uniform. | 489 | /// Has no effect if the shader does not contain the given uniform. |
| 480 | void gfx_set_texture_uniform(ShaderProgram*, const char* name, const Texture*); | 490 | void gfx_set_int_uniform(ShaderProgram*, const char* name, int value); |
| 491 | |||
| 492 | /// Set the float uniform. | ||
| 493 | /// Has no effect if the shader does not contain the given uniform. | ||
| 494 | void gfx_set_float_uniform(ShaderProgram*, const char* name, float value); | ||
| 481 | 495 | ||
| 482 | /// Set the matrix uniform. | 496 | /// Set the matrix uniform. |
| 483 | /// Has no effect if the shader does not contain the given uniform. | 497 | /// Has no effect if the shader does not contain the given uniform. |
| @@ -491,9 +505,9 @@ void gfx_set_vec3_uniform(ShaderProgram*, const char* name, vec3); | |||
| 491 | /// Has no effect if the shader does not contain the given uniform. | 505 | /// Has no effect if the shader does not contain the given uniform. |
| 492 | void gfx_set_vec4_uniform(ShaderProgram*, const char* name, vec4); | 506 | void gfx_set_vec4_uniform(ShaderProgram*, const char* name, vec4); |
| 493 | 507 | ||
| 494 | /// Set the float uniform. | 508 | /// Set the texture uniform. |
| 495 | /// Has no effect if the shader does not contain the given uniform. | 509 | /// Has no effect if the shader does not contain the given uniform. |
| 496 | void gfx_set_float_uniform(ShaderProgram*, const char* name, float value); | 510 | void gfx_set_texture_uniform(ShaderProgram*, const char* name, const Texture*); |
| 497 | 511 | ||
| 498 | /// Set the matrix array uniform. | 512 | /// Set the matrix array uniform. |
| 499 | /// Has no effect if the shader does not contain the given uniform. | 513 | /// Has no effect if the shader does not contain the given uniform. |
diff --git a/gfx/include/gfx/gfx.h b/include/gfx/gfx.h index 7c670a5..eb7a3cd 100644 --- a/gfx/include/gfx/gfx.h +++ b/include/gfx/gfx.h | |||
| @@ -2,7 +2,8 @@ | |||
| 2 | 2 | ||
| 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 Imm Imm; |
| 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,11 +17,14 @@ 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 | Imm* gfx_get_imm(Gfx*); |
| 25 | |||
| 26 | /// Get the low-level renderer. | ||
| 27 | LLR* gfx_get_llr(Gfx*); | ||
| 24 | 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*); |
diff --git a/include/gfx/render/imm.h b/include/gfx/render/imm.h new file mode 100644 index 0000000..62c071e --- /dev/null +++ b/include/gfx/render/imm.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <math/aabb2.h> | ||
| 4 | #include <math/aabb3.h> | ||
| 5 | #include <math/vec3.h> | ||
| 6 | |||
| 7 | typedef struct Imm Imm; | ||
| 8 | |||
| 9 | /// Prepare the graphics systems for immediate-mode rendering. | ||
| 10 | /// | ||
| 11 | /// Call this before issuing any immediate-mode rendering draws. | ||
| 12 | void gfx_imm_start(Imm*); | ||
| 13 | |||
| 14 | /// End immediate mode rendering. | ||
| 15 | /// | ||
| 16 | /// Call this after issuing immediate-mode rendering draws and before swapping | ||
| 17 | /// buffers. | ||
| 18 | void gfx_imm_end(Imm*); | ||
| 19 | |||
| 20 | /// Flush draw commands. | ||
| 21 | /// | ||
| 22 | /// This should be done when changing any state that may affect the rendering of | ||
| 23 | /// primitives; for example, LLR matrix stack changes. | ||
| 24 | void gfx_imm_flush(Imm*); | ||
| 25 | |||
| 26 | /// Draw a set of triangles. | ||
| 27 | void gfx_imm_draw_triangles(Imm*, const vec3[], size_t num_triangles); | ||
| 28 | |||
| 29 | /// Draw a triangle. | ||
| 30 | void gfx_imm_draw_triangle(Imm*, const vec3[3]); | ||
| 31 | |||
| 32 | /// Draw a bounding box. | ||
| 33 | void gfx_imm_draw_aabb2(Imm*, aabb2); | ||
| 34 | |||
| 35 | /// Draw a bounding box. | ||
| 36 | void gfx_imm_draw_aabb3(Imm*, aabb3); | ||
| 37 | |||
| 38 | /// Draw a box. | ||
| 39 | /// | ||
| 40 | /// The vertices must be given in the following order: | ||
| 41 | /// | ||
| 42 | /// 7 ----- 6 | ||
| 43 | /// / /| | ||
| 44 | /// 3 ----- 2 | | ||
| 45 | /// | | | | ||
| 46 | /// | 4 ----- 5 | ||
| 47 | /// |/ |/ | ||
| 48 | /// 0 ----- 1 | ||
| 49 | void gfx_imm_draw_box3(Imm* renderer, const vec3 vertices[8]); | ||
| 50 | |||
| 51 | /// Set the render colour. | ||
| 52 | void gfx_imm_set_colour(Imm*, vec4 colour); | ||
diff --git a/include/gfx/render/llr.h b/include/gfx/render/llr.h new file mode 100644 index 0000000..6f635b6 --- /dev/null +++ b/include/gfx/render/llr.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <math/fwd.h> | ||
| 4 | #include <math/vec3.h> | ||
| 5 | |||
| 6 | typedef struct Anima Anima; | ||
| 7 | typedef struct Camera Camera; | ||
| 8 | typedef struct Geometry Geometry; | ||
| 9 | typedef struct Light Light; | ||
| 10 | typedef struct Material Material; | ||
| 11 | typedef struct Mesh Mesh; | ||
| 12 | typedef struct ShaderProgram ShaderProgram; | ||
| 13 | typedef struct Skeleton Skeleton; | ||
| 14 | typedef struct Texture Texture; | ||
| 15 | |||
| 16 | typedef struct LLR LLR; | ||
| 17 | |||
| 18 | // ----------------------------------------------------------------------------- | ||
| 19 | // Low-level rendering. | ||
| 20 | // ----------------------------------------------------------------------------- | ||
| 21 | |||
| 22 | /// Set the shader to be used for subsequent draw calls. | ||
| 23 | /// The shader is not yet activated at this point. | ||
| 24 | void gfx_llr_set_shader(LLR*, ShaderProgram*); | ||
| 25 | |||
| 26 | /// Push a light into the lights stack. | ||
| 27 | void gfx_llr_push_light(LLR*, Light*); | ||
| 28 | |||
| 29 | /// Pop the last light from the lights stack. | ||
| 30 | void gfx_llr_pop_light(LLR*); | ||
| 31 | |||
| 32 | /// Load a skeleton. | ||
| 33 | /// | ||
| 34 | /// If a skeleton is loaded, subsequent meshes are rendered with joint data | ||
| 35 | /// passed to the shader. This has a cost, so if subsequent meshes are not | ||
| 36 | /// animated, unload the skeleton prior to rendering them. | ||
| 37 | void gfx_llr_set_skeleton(LLR*, const Anima*, const Skeleton*); | ||
| 38 | |||
| 39 | /// Clear the loaded skeleton. | ||
| 40 | void gfx_llr_clear_skeleton(LLR*); | ||
| 41 | |||
| 42 | /// Set the material. | ||
| 43 | /// | ||
| 44 | /// The material need not be set explicitly when rendering a mesh | ||
| 45 | /// (gfx_llr_render_mesh). This is mostly useful when using the lower-level | ||
| 46 | /// function to render geometry (gfx_llr_render_geometry). | ||
| 47 | void gfx_llr_set_material(LLR*, const Material*); | ||
| 48 | |||
| 49 | /// Set the camera. | ||
| 50 | void gfx_llr_set_camera(LLR*, const Camera*); | ||
| 51 | |||
| 52 | /// Set the projection matrix. | ||
| 53 | void gfx_llr_set_projection_matrix(LLR* renderer, const mat4* projection); | ||
| 54 | |||
| 55 | /// Set the aspect ratio. | ||
| 56 | void gfx_llr_set_aspect(LLR*, float aspect); | ||
| 57 | |||
| 58 | /// Render the geometry. | ||
| 59 | void gfx_llr_render_geometry(LLR*, const Geometry*); | ||
| 60 | |||
| 61 | /// Render the mesh. | ||
| 62 | void gfx_llr_render_mesh(LLR*, const Mesh*); | ||
| 63 | |||
| 64 | // ----------------------------------------------------------------------------- | ||
| 65 | // Matrix stack manipulation. | ||
| 66 | // ----------------------------------------------------------------------------- | ||
| 67 | |||
| 68 | /// Load an identity model matrix. Clears the matrix stack. | ||
| 69 | void gfx_llr_load_identity(LLR* renderer); | ||
| 70 | |||
| 71 | /// Push the given matrix to the matrix stack. | ||
| 72 | void gfx_llr_push_matrix(LLR* renderer, const mat4* matrix); | ||
| 73 | |||
| 74 | /// Pop the top of the matrix stack. | ||
| 75 | void gfx_llr_pop_matrix(LLR* renderer); | ||
| 76 | |||
| 77 | /// Push a translation matrix to the matrix stack. | ||
| 78 | void gfx_llr_translate(LLR* renderer, vec3 offset); | ||
| 79 | |||
| 80 | /// Set the model matrix. Clears the matrix stack. | ||
| 81 | void gfx_llr_set_model_matrix(LLR*, const mat4*); | ||
diff --git a/include/gfx/render/renderer.h b/include/gfx/render/renderer.h new file mode 100644 index 0000000..9f3231b --- /dev/null +++ b/include/gfx/render/renderer.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <math/defs.h> | ||
| 4 | #include <math/fwd.h> | ||
| 5 | |||
| 6 | typedef struct GfxCore GfxCore; | ||
| 7 | typedef struct Scene Scene; | ||
| 8 | |||
| 9 | typedef struct Renderer Renderer; | ||
| 10 | |||
| 11 | // TODO: Add RenderDepth. | ||
| 12 | typedef enum RenderSceneMode { | ||
| 13 | RenderDefault, | ||
| 14 | RenderDebug, | ||
| 15 | RenderNormals, | ||
| 16 | RenderNormalMappedNormals, | ||
| 17 | RenderTangents | ||
| 18 | } RenderSceneMode; | ||
| 19 | |||
| 20 | typedef enum RenderSceneFilter { | ||
| 21 | RenderOpaqueAndAlphaMasked, | ||
| 22 | RenderTransparent | ||
| 23 | } RenderSceneFilter; | ||
| 24 | |||
| 25 | typedef struct RenderSceneParams { | ||
| 26 | RenderSceneMode mode; | ||
| 27 | RenderSceneFilter filter; | ||
| 28 | const Scene* scene; | ||
| 29 | const Camera* camera; | ||
| 30 | } RenderSceneParams; | ||
| 31 | |||
| 32 | /// Render the scene. | ||
| 33 | void gfx_render_scene(Renderer*, const RenderSceneParams*); | ||
| 34 | |||
| 35 | /// Update the scene. | ||
| 36 | void gfx_update(Scene*, const Camera*, R t); | ||
diff --git a/include/gfx/scene.h b/include/gfx/scene.h new file mode 100644 index 0000000..3aa6e0e --- /dev/null +++ b/include/gfx/scene.h | |||
| @@ -0,0 +1,296 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/core.h> | ||
| 4 | #include <gfx/sizes.h> | ||
| 5 | |||
| 6 | #include <math/aabb3.h> | ||
| 7 | #include <math/fwd.h> | ||
| 8 | #include <math/mat4.h> | ||
| 9 | |||
| 10 | typedef struct Anima Anima; | ||
| 11 | typedef struct Camera Camera; | ||
| 12 | typedef struct Light Light; | ||
| 13 | typedef struct Material Material; | ||
| 14 | typedef struct Mesh Mesh; | ||
| 15 | typedef struct Model Model; | ||
| 16 | typedef struct Scene Scene; | ||
| 17 | /// A node in the scene graph. | ||
| 18 | /// | ||
| 19 | /// Scene nodes take ownership of the object they are associated with (Camera, | ||
| 20 | /// Light, SceneObject, etc), as well as of child nodes. | ||
| 21 | typedef struct SceneNode SceneNode; | ||
| 22 | typedef struct SceneObject SceneObject; | ||
| 23 | typedef struct Skeleton Skeleton; | ||
| 24 | typedef struct Texture Texture; | ||
| 25 | |||
| 26 | /// Light type. | ||
| 27 | typedef enum LightType { EnvironmentLightType } LightType; | ||
| 28 | |||
| 29 | /// Describes an environment light. | ||
| 30 | typedef struct EnvironmentLightDesc { | ||
| 31 | const Texture* environment_map; | ||
| 32 | } EnvironmentLightDesc; | ||
| 33 | |||
| 34 | /// Describes a light. | ||
| 35 | typedef struct LightDesc { | ||
| 36 | LightType type; | ||
| 37 | union { | ||
| 38 | EnvironmentLightDesc environment; | ||
| 39 | } light; | ||
| 40 | } LightDesc; | ||
| 41 | |||
| 42 | /// Alpha mode. | ||
| 43 | typedef enum AlphaMode { Opaque = 0, Mask = 1, Blend = 2 } AlphaMode; | ||
| 44 | |||
| 45 | /// Describes a material. | ||
| 46 | /// | ||
| 47 | /// TODO: It doesn't hold the shader program anymore...It's in the Mesh. | ||
| 48 | /// A material holds a shader program and a set of shader-specific uniform | ||
| 49 | /// variables. Two materials can share the same shader, but shader parameters | ||
| 50 | /// generally give two materials a different appearance. | ||
| 51 | typedef struct MaterialDesc { | ||
| 52 | AlphaMode alpha_mode; | ||
| 53 | float alpha_cutoff; | ||
| 54 | int num_uniforms; | ||
| 55 | ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; | ||
| 56 | } MaterialDesc; | ||
| 57 | |||
| 58 | /// Describes a mesh. | ||
| 59 | typedef struct MeshDesc { | ||
| 60 | const Geometry* geometry; | ||
| 61 | const Material* material; | ||
| 62 | ShaderProgram* shader; | ||
| 63 | } MeshDesc; | ||
| 64 | |||
| 65 | typedef struct ObjectDesc { | ||
| 66 | size_t num_meshes; | ||
| 67 | Mesh* meshes[GFX_MAX_NUM_MESHES]; | ||
| 68 | } ObjectDesc; | ||
| 69 | |||
| 70 | /// Scene node type. | ||
| 71 | typedef enum NodeType { | ||
| 72 | LogicalNode, | ||
| 73 | AnimaNode, | ||
| 74 | CameraNode, | ||
| 75 | LightNode, | ||
| 76 | ModelNode, | ||
| 77 | ObjectNode, | ||
| 78 | } NodeType; | ||
| 79 | |||
| 80 | // ----------------------------------------------------------------------------- | ||
| 81 | // Camera. | ||
| 82 | // ----------------------------------------------------------------------------- | ||
| 83 | |||
| 84 | /// Create a new camera. | ||
| 85 | Camera* gfx_make_camera(void); | ||
| 86 | |||
| 87 | /// Destroy the camera. | ||
| 88 | /// | ||
| 89 | /// The caller must make sure that no Node owns the given Camera. | ||
| 90 | /// For a safe purge of unused resources, see scene_purge(). | ||
| 91 | void gfx_destroy_camera(Camera**); | ||
| 92 | |||
| 93 | // ----------------------------------------------------------------------------- | ||
| 94 | // Light. | ||
| 95 | // ----------------------------------------------------------------------------- | ||
| 96 | |||
| 97 | /// Create a light. | ||
| 98 | Light* gfx_make_light(const LightDesc*); | ||
| 99 | |||
| 100 | /// Destroy the light. | ||
| 101 | /// | ||
| 102 | /// The caller must make sure that no Node owns the given Light. | ||
| 103 | /// For a safe purge of unused resources, see scene_purge(). | ||
| 104 | void gfx_destroy_light(Light**); | ||
| 105 | |||
| 106 | // ----------------------------------------------------------------------------- | ||
| 107 | // Material. | ||
| 108 | // ----------------------------------------------------------------------------- | ||
| 109 | |||
| 110 | /// Create a material. | ||
| 111 | Material* gfx_make_material(const MaterialDesc*); | ||
| 112 | |||
| 113 | /// Destroy the material. | ||
| 114 | /// | ||
| 115 | /// The caller must make sure that no Mesh points to the given Material. | ||
| 116 | /// For a safe purge of unused resources, see scene_purge(). | ||
| 117 | void gfx_destroy_material(Material**); | ||
| 118 | |||
| 119 | // ----------------------------------------------------------------------------- | ||
| 120 | // Mesh. | ||
| 121 | // ----------------------------------------------------------------------------- | ||
| 122 | |||
| 123 | /// Create a mesh. | ||
| 124 | Mesh* gfx_make_mesh(const MeshDesc*); | ||
| 125 | |||
| 126 | /// Destroy the mesh. | ||
| 127 | /// | ||
| 128 | /// The caller must make sure that no SceneObject points to the given Mesh. | ||
| 129 | /// For a safe purge of unused resources, see scene_purge(). | ||
| 130 | void gfx_destroy_mesh(Mesh**); | ||
| 131 | |||
| 132 | // ----------------------------------------------------------------------------- | ||
| 133 | // Model. | ||
| 134 | // ----------------------------------------------------------------------------- | ||
| 135 | |||
| 136 | /// Return the model's anima, or null if the model is not animated. | ||
| 137 | Anima* gfx_get_model_anima(Model*); | ||
| 138 | |||
| 139 | /// Return the model's root node. | ||
| 140 | const SceneNode* gfx_get_model_root(const Model*); | ||
| 141 | SceneNode* gfx_get_model_root_mut(Model*); | ||
| 142 | |||
| 143 | // ----------------------------------------------------------------------------- | ||
| 144 | // Object. | ||
| 145 | // ----------------------------------------------------------------------------- | ||
| 146 | |||
| 147 | /// Create a new object. | ||
| 148 | SceneObject* gfx_make_object(const ObjectDesc*); | ||
| 149 | |||
| 150 | /// Destroy the object. | ||
| 151 | /// | ||
| 152 | /// The caller must make sure that no Node owns the given SceneObject. | ||
| 153 | /// For a safe purge of unused resources, see scene_purge(). | ||
| 154 | void gfx_destroy_object(SceneObject**); | ||
| 155 | |||
| 156 | /// Set the object's skeleton. | ||
| 157 | void gfx_set_object_skeleton(SceneObject*, const Skeleton*); | ||
| 158 | |||
| 159 | /// Get the object's skeleton. | ||
| 160 | /// Return null if the object has no skeleton. | ||
| 161 | const Skeleton* gfx_get_object_skeleton(const SceneObject*); | ||
| 162 | |||
| 163 | /// Gets the object's bounding box. | ||
| 164 | /// | ||
| 165 | /// The object's bounding box is the bounding box of its mesh geometries. | ||
| 166 | aabb3 gfx_get_object_aabb(const SceneObject*); | ||
| 167 | |||
| 168 | // TODO: Remove the scene object? It only contains the root node. | ||
| 169 | // ----------------------------------------------------------------------------- | ||
| 170 | // Scene. | ||
| 171 | // ----------------------------------------------------------------------------- | ||
| 172 | |||
| 173 | /// Create a new scene. | ||
| 174 | Scene* gfx_make_scene(void); | ||
| 175 | |||
| 176 | /// Destroy the scene. | ||
| 177 | /// | ||
| 178 | /// This function destroys the scene and all objects that it owns (scene | ||
| 179 | /// objects, cameras, lights, etc), but not objects that could be shared with | ||
| 180 | /// other scenes (meshes, materials, etc). | ||
| 181 | void gfx_destroy_scene(Scene**); | ||
| 182 | |||
| 183 | /// Get the scene's root node. | ||
| 184 | const SceneNode* gfx_get_scene_root(const Scene*); | ||
| 185 | SceneNode* gfx_get_scene_root_mut(Scene*); | ||
| 186 | |||
| 187 | // ----------------------------------------------------------------------------- | ||
| 188 | // Node constructors and destructor. | ||
| 189 | // ----------------------------------------------------------------------------- | ||
| 190 | |||
| 191 | /// Create a new scene node. | ||
| 192 | /// | ||
| 193 | /// This node does not contain any camera, light, object, etc. and exists simply | ||
| 194 | /// as a logical and spatial construct. | ||
| 195 | SceneNode* gfx_make_node(void); | ||
| 196 | |||
| 197 | /// Create an anima node. | ||
| 198 | SceneNode* gfx_make_anima_node(Anima*); | ||
| 199 | |||
| 200 | /// Create a new camera node. | ||
| 201 | SceneNode* gfx_make_camera_node(Camera*); | ||
| 202 | |||
| 203 | /// Create a new light node. | ||
| 204 | SceneNode* gfx_make_light_node(Light*); | ||
| 205 | |||
| 206 | /// Create a new model node. | ||
| 207 | SceneNode* gfx_make_model_node(Model*); | ||
| 208 | |||
| 209 | /// Create a new object node. | ||
| 210 | SceneNode* gfx_make_object_node(SceneObject*); | ||
| 211 | |||
| 212 | /// Recursively destroy the scene node and its children. | ||
| 213 | /// | ||
| 214 | /// The scene node and its children are removed from the scene graph. | ||
| 215 | /// | ||
| 216 | /// Node resources -- cameras, lights, objects, etc. -- are also destroyed. | ||
| 217 | void gfx_destroy_node(SceneNode**); | ||
| 218 | |||
| 219 | // ----------------------------------------------------------------------------- | ||
| 220 | // Node getters. | ||
| 221 | // ----------------------------------------------------------------------------- | ||
| 222 | |||
| 223 | /// Get the node's type. | ||
| 224 | NodeType gfx_get_node_type(const SceneNode*); | ||
| 225 | |||
| 226 | /// Get the node's anima. | ||
| 227 | /// | ||
| 228 | /// The node must be of type AnimaNode. | ||
| 229 | const Anima* gfx_get_node_anima(const SceneNode*); | ||
| 230 | Anima* gfx_get_node_anima_mut(SceneNode*); | ||
| 231 | |||
| 232 | /// Get the node's camera. | ||
| 233 | /// | ||
| 234 | /// The node must be of type CameraNode. | ||
| 235 | const Camera* gfx_get_node_camera(const SceneNode* node); | ||
| 236 | Camera* gfx_get_node_camera_mut(SceneNode* node); | ||
| 237 | |||
| 238 | /// Get the node's light. | ||
| 239 | /// | ||
| 240 | /// The node must be of type LightNode. | ||
| 241 | const Light* gfx_get_node_light(const SceneNode*); | ||
| 242 | Light* gfx_get_node_light_mut(SceneNode*); | ||
| 243 | |||
| 244 | /// Get the node's model. | ||
| 245 | /// | ||
| 246 | /// The node must be of type ModelNode. | ||
| 247 | const Model* gfx_get_node_model(const SceneNode*); | ||
| 248 | Model* gfx_get_node_model_mut(SceneNode*); | ||
| 249 | |||
| 250 | /// Get the node's scene object. | ||
| 251 | /// | ||
| 252 | /// The node must be of type ObjectNode. | ||
| 253 | const SceneObject* gfx_get_node_object(const SceneNode*); | ||
| 254 | SceneObject* gfx_get_node_object_mut(SceneNode*); | ||
| 255 | |||
| 256 | /// Get the node's parent. | ||
| 257 | const SceneNode* gfx_get_node_parent(const SceneNode*); | ||
| 258 | SceneNode* gfx_get_node_parent_mut(SceneNode*); | ||
| 259 | |||
| 260 | /// Get the node's first child. | ||
| 261 | const SceneNode* gfx_get_node_child(const SceneNode*); | ||
| 262 | SceneNode* gfx_get_node_child_mut(SceneNode*); | ||
| 263 | |||
| 264 | /// Get the node's immediate sibling. | ||
| 265 | const SceneNode* gfx_get_node_sibling(const SceneNode*); | ||
| 266 | SceneNode* gfx_get_node_sibling_mut(SceneNode*); | ||
| 267 | |||
| 268 | /// Get the node's (local) transform. | ||
| 269 | mat4 gfx_get_node_transform(const SceneNode*); | ||
| 270 | |||
| 271 | /// Get the node's global transform. | ||
| 272 | mat4 gfx_get_node_global_transform(const SceneNode*); | ||
| 273 | |||
| 274 | // ----------------------------------------------------------------------------- | ||
| 275 | // Node setters. | ||
| 276 | // ----------------------------------------------------------------------------- | ||
| 277 | |||
| 278 | /// Set the node's parent. | ||
| 279 | /// | ||
| 280 | /// Pass in null to unwire from the existing parent, if one exists. | ||
| 281 | void gfx_set_node_parent(SceneNode*, SceneNode* parent_node); | ||
| 282 | |||
| 283 | /// Set the node's (local) transform. | ||
| 284 | void gfx_set_node_transform(SceneNode*, const mat4* transform); | ||
| 285 | |||
| 286 | /// Set the node's position. | ||
| 287 | void gfx_set_node_position(SceneNode*, const vec3* position); | ||
| 288 | |||
| 289 | /// Set the node's rotation. | ||
| 290 | void gfx_set_node_rotation(SceneNode*, const quat* rotation); | ||
| 291 | |||
| 292 | /// Set the node's rotation. | ||
| 293 | void gfx_set_node_rotation_mat(SceneNode*, const mat4* rotation); | ||
| 294 | |||
| 295 | /// Log the node's hierarchy. | ||
| 296 | void gfx_log_node_hierarchy(const SceneNode*); | ||
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..55319e1 100644 --- a/gfx/include/gfx/util/skyquad.h +++ b/include/gfx/util/skyquad.h | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | #pragma once | 2 | #pragma once |
| 3 | 3 | ||
| 4 | typedef struct GfxCore GfxCore; | 4 | typedef struct GfxCore GfxCore; |
| 5 | typedef struct Scene Scene; | ||
| 6 | typedef struct SceneNode SceneNode; | 5 | typedef struct SceneNode SceneNode; |
| 7 | typedef struct SceneObject SceneObject; | 6 | typedef struct SceneObject SceneObject; |
| 8 | typedef struct Texture Texture; | 7 | typedef struct Texture Texture; |
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..f6c6048 100644 --- a/gfx/shaders/cook_torrance.frag +++ b/shaders/cook_torrance.frag | |||
| @@ -1,3 +1,8 @@ | |||
| 1 | /* | ||
| 2 | [1] BRDF reference: https://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html | ||
| 3 | [2] glTF reference implementation: https://github.com/KhronosGroup/glTF-Sample-Renderer/blob/main/source/Renderer/shaders/brdf.glsl | ||
| 4 | [3] Learn OpenGL reference: https://learnopengl.com/PBR/Theory (see other chapters also for IBL) | ||
| 5 | */ | ||
| 1 | precision highp float; | 6 | precision highp float; |
| 2 | 7 | ||
| 3 | uniform vec4 BaseColorFactor; | 8 | uniform vec4 BaseColorFactor; |
| @@ -5,19 +10,26 @@ uniform float MetallicFactor; | |||
| 5 | uniform float RoughnessFactor; | 10 | uniform float RoughnessFactor; |
| 6 | uniform vec3 EmissiveFactor; | 11 | uniform vec3 EmissiveFactor; |
| 7 | 12 | ||
| 8 | #ifdef HAS_ALBEDO_MAP | 13 | #if HAS_TRANSPARENCY |
| 14 | #define ALPHA_MODE_MASK 1 | ||
| 15 | #define ALPHA_MODE_BLEND 2 | ||
| 16 | uniform int AlphaMode; | ||
| 17 | uniform float AlphaCutoff; | ||
| 18 | #endif | ||
| 19 | |||
| 20 | #if HAS_ALBEDO_MAP | ||
| 9 | uniform sampler2D BaseColorTexture; | 21 | uniform sampler2D BaseColorTexture; |
| 10 | #endif | 22 | #endif |
| 11 | #ifdef HAS_METALLIC_ROUGHNESS_MAP | 23 | #if HAS_METALLIC_ROUGHNESS_MAP |
| 12 | uniform sampler2D MetallicRoughnessTexture; | 24 | uniform sampler2D MetallicRoughnessTexture; |
| 13 | #endif | 25 | #endif |
| 14 | #ifdef HAS_EMISSIVE_MAP | 26 | #if HAS_EMISSIVE_MAP |
| 15 | uniform sampler2D EmissiveTexture; | 27 | uniform sampler2D EmissiveTexture; |
| 16 | #endif | 28 | #endif |
| 17 | #ifdef HAS_OCCLUSION_MAP | 29 | #if HAS_OCCLUSION_MAP |
| 18 | uniform sampler2D AmbientOcclusionTexture; | 30 | uniform sampler2D AmbientOcclusionTexture; |
| 19 | #endif | 31 | #endif |
| 20 | #ifdef HAS_NORMAL_MAP | 32 | #if HAS_NORMAL_MAP |
| 21 | uniform sampler2D NormalMap; | 33 | uniform sampler2D NormalMap; |
| 22 | #endif | 34 | #endif |
| 23 | 35 | ||
| @@ -32,13 +44,13 @@ uniform vec3 CameraPosition; // World space. | |||
| 32 | 44 | ||
| 33 | // World-space position, normal and tangent. | 45 | // World-space position, normal and tangent. |
| 34 | in vec3 Position; | 46 | in vec3 Position; |
| 35 | #ifdef HAS_NORMALS | 47 | #if HAS_NORMALS |
| 36 | in vec3 Normal; | 48 | in vec3 Normal; |
| 37 | #endif | 49 | #endif |
| 38 | #ifdef HAS_TANGENTS | 50 | #if HAS_TANGENTS |
| 39 | in vec4 Tangent; | 51 | in vec4 Tangent; |
| 40 | #endif | 52 | #endif |
| 41 | #ifdef HAS_TEXCOORDS | 53 | #if HAS_TEXCOORDS |
| 42 | in vec2 Texcoord; | 54 | in vec2 Texcoord; |
| 43 | #endif | 55 | #endif |
| 44 | 56 | ||
| @@ -56,7 +68,7 @@ layout (location = 0) out vec4 Colour; | |||
| 56 | #if defined(HAS_NORMAL_MAP) && (defined(HAS_TANGENTS) || defined(HAS_TEXCOORDS)) | 68 | #if defined(HAS_NORMAL_MAP) && (defined(HAS_TANGENTS) || defined(HAS_TEXCOORDS)) |
| 57 | vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { | 69 | vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { |
| 58 | vec3 N = normalize(Normal); | 70 | vec3 N = normalize(Normal); |
| 59 | #ifdef HAS_TANGENTS | 71 | #if HAS_TANGENTS |
| 60 | //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N); | 72 | //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N); |
| 61 | vec3 T = Tangent.xyz; | 73 | vec3 T = Tangent.xyz; |
| 62 | vec3 B = Tangent.w * cross(N, T); | 74 | vec3 B = Tangent.w * cross(N, T); |
| @@ -86,8 +98,10 @@ vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { | |||
| 86 | } | 98 | } |
| 87 | #endif // HAS_TANGENTS || HAS_TEXCOORDS | 99 | #endif // HAS_TANGENTS || HAS_TEXCOORDS |
| 88 | 100 | ||
| 101 | // Normal distribution function (NDF). | ||
| 102 | // Eq (4) in reference [1]. | ||
| 89 | float trowbridge_reitz_GGX(float roughness, float NdotH) { | 103 | float trowbridge_reitz_GGX(float roughness, float NdotH) { |
| 90 | float a = roughness * roughness; | 104 | float a = roughness * roughness; // "alpha roughness" in the reference. |
| 91 | float a2 = a * a; | 105 | float a2 = a * a; |
| 92 | float d = NdotH * NdotH * (a2 - 1.0) + 1.0; | 106 | float d = NdotH * NdotH * (a2 - 1.0) + 1.0; |
| 93 | return a2 / (PI * d * d); | 107 | return a2 / (PI * d * d); |
| @@ -97,6 +111,8 @@ float geometry_schlick_GGX(float k, float NdotV) { | |||
| 97 | return NdotV / (NdotV * (1.0 - k) + k); | 111 | return NdotV / (NdotV * (1.0 - k) + k); |
| 98 | } | 112 | } |
| 99 | 113 | ||
| 114 | // Geometry function. | ||
| 115 | // See "Smith" under "Geometric Shadowing" in reference [1]. | ||
| 100 | float geometry_smith(float roughness, float NdotL, float NdotV) { | 116 | float geometry_smith(float roughness, float NdotL, float NdotV) { |
| 101 | float k = roughness * roughness / 2.0; // IBL | 117 | float k = roughness * roughness / 2.0; // IBL |
| 102 | return geometry_schlick_GGX(k, NdotV) * geometry_schlick_GGX(k, NdotL); | 118 | return geometry_schlick_GGX(k, NdotV) * geometry_schlick_GGX(k, NdotL); |
| @@ -121,7 +137,11 @@ vec3 cook_torrance( | |||
| 121 | vec3 F = fresnel_schlick(F0, HdotV); | 137 | vec3 F = fresnel_schlick(F0, HdotV); |
| 122 | float G = geometry_smith(roughness, NdotL, NdotV); | 138 | float G = geometry_smith(roughness, NdotL, NdotV); |
| 123 | vec3 Kd = mix(vec3(1.0) - F, vec3(0.0), metallic); | 139 | vec3 Kd = mix(vec3(1.0) - F, vec3(0.0), metallic); |
| 124 | vec3 diffuse = Kd*albedo*INV_PI; | 140 | // A non-HDR environment map essentially has the 1/pi baked in as it does not |
| 141 | // use physical units. See: | ||
| 142 | // https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ | ||
| 143 | // vec3 diffuse = Kd * albedo * INV_PI; | ||
| 144 | vec3 diffuse = Kd * albedo * INV_PI; | ||
| 125 | // Take a max to prevent division by 0 when either dot product is 0. | 145 | // Take a max to prevent division by 0 when either dot product is 0. |
| 126 | vec3 specular = (D*F*G) / max(4.0 * NdotV * NdotL, 0.0001); | 146 | vec3 specular = (D*F*G) / max(4.0 * NdotV * NdotL, 0.0001); |
| 127 | return diffuse + specular; | 147 | return diffuse + specular; |
| @@ -132,46 +152,33 @@ vec3 cook_torrance_IBL( | |||
| 132 | vec3 albedo, float metallic, float roughness, float occlusion, | 152 | vec3 albedo, float metallic, float roughness, float occlusion, |
| 133 | float NdotV, | 153 | float NdotV, |
| 134 | vec3 irradiance, vec3 prefiltered_env, vec2 BRDF_env) { | 154 | vec3 irradiance, vec3 prefiltered_env, vec2 BRDF_env) { |
| 135 | vec3 F0 = mix(vec3(0.04), albedo, metallic); | 155 | vec3 F0 = mix(vec3(0.04), albedo, metallic); // albedo = F0 for metals |
| 136 | vec3 F = fresnel_schlick_roughness(F0, NdotV, roughness); | 156 | vec3 F = fresnel_schlick_roughness(F0, NdotV, roughness); |
| 137 | vec3 Kd = mix(vec3(1.0) - F, vec3(0.0), metallic); | 157 | vec3 Kd = (vec3(1.0) - F) * (1.0 - metallic); |
| 138 | vec3 diffuse = Kd * albedo * INV_PI * irradiance; | 158 | // A non-HDR environment map essentially has the 1/pi baked in as it does not |
| 159 | // use physical units. See: | ||
| 160 | // https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ | ||
| 161 | // vec3 diffuse = Kd * albedo * INV_PI * irradiance; | ||
| 162 | vec3 diffuse = Kd * albedo * irradiance; | ||
| 139 | vec3 specular = prefiltered_env * (F * BRDF_env.x + BRDF_env.y); | 163 | vec3 specular = prefiltered_env * (F * BRDF_env.x + BRDF_env.y); |
| 140 | return occlusion * (diffuse + specular); | 164 | return occlusion * (diffuse + specular); |
| 141 | } | 165 | } |
| 142 | 166 | ||
| 143 | void main() | 167 | void main() |
| 144 | { | 168 | { |
| 145 | // TODO: Also use the specular F0 map from the model, and emissive. Make sure | ||
| 146 | // to use all maps. | ||
| 147 | // https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4 | ||
| 148 | #ifdef HAS_NORMAL_MAP | ||
| 149 | vec3 normalMapSample = texture(NormalMap, Texcoord).xyz * 2.0 - 1.0; | ||
| 150 | vec3 N = get_ws_normal(Normal, normalMapSample); | ||
| 151 | #elif HAS_NORMALS | ||
| 152 | vec3 N = normalize(Normal); | ||
| 153 | #endif | ||
| 154 | vec3 V = normalize(CameraPosition - Position); | ||
| 155 | vec3 R = reflect(-V, N); | ||
| 156 | // Not needed for IBL. | ||
| 157 | //vec3 L = N; | ||
| 158 | //vec3 H = normalize(L + V); | ||
| 159 | |||
| 160 | float NdotV = max(0.0, dot(N, V)); | ||
| 161 | // Not needed for IBL. | ||
| 162 | //float NdotL = max(0.0, dot(N,L)); | ||
| 163 | //float NdotH = max(0.0, dot(N,H)); | ||
| 164 | //float HdotV = clamp(dot(H,V), 0.0, 1.0); // Clamp to prevent black spots. | ||
| 165 | |||
| 166 | // TODO: BaseColorFactor and BaseColorTexture are vec4/rgba quantities | ||
| 167 | // respectively. Handle the alpha channel. | ||
| 168 | // TODO: Other factors. | 169 | // TODO: Other factors. |
| 169 | #ifdef HAS_ALBEDO_MAP | 170 | #if HAS_ALBEDO_MAP |
| 170 | vec3 albedo = vec3(BaseColorFactor) * texture(BaseColorTexture, Texcoord).rgb; | 171 | vec4 base_colour = vec4(BaseColorFactor) * texture(BaseColorTexture, Texcoord); |
| 171 | #else | 172 | #else |
| 172 | vec3 albedo = vec3(BaseColorFactor); | 173 | vec4 base_colour = vec4(BaseColorFactor); |
| 174 | #endif | ||
| 175 | vec3 albedo = base_colour.rgb; | ||
| 176 | #if HAS_TRANSPARENCY | ||
| 177 | if ((AlphaMode == ALPHA_MODE_MASK) && (base_colour.a < AlphaCutoff)) { | ||
| 178 | discard; | ||
| 179 | } | ||
| 173 | #endif | 180 | #endif |
| 174 | #ifdef HAS_METALLIC_ROUGHNESS_MAP | 181 | #if HAS_METALLIC_ROUGHNESS_MAP |
| 175 | // Spec: "Its green channel contains roughness values and its blue channel | 182 | // Spec: "Its green channel contains roughness values and its blue channel |
| 176 | // contains metalness values." | 183 | // contains metalness values." |
| 177 | vec2 metal_roughness | 184 | vec2 metal_roughness |
| @@ -180,12 +187,12 @@ void main() | |||
| 180 | #else | 187 | #else |
| 181 | vec2 metal_roughness = vec2(MetallicFactor, RoughnessFactor); | 188 | vec2 metal_roughness = vec2(MetallicFactor, RoughnessFactor); |
| 182 | #endif | 189 | #endif |
| 183 | #ifdef HAS_EMISSIVE_MAP | 190 | #if HAS_EMISSIVE_MAP |
| 184 | vec3 emissive = EmissiveFactor * texture(EmissiveTexture, Texcoord).rgb; | 191 | vec3 emissive = EmissiveFactor * texture(EmissiveTexture, Texcoord).rgb; |
| 185 | #else | 192 | #else |
| 186 | vec3 emissive = EmissiveFactor; | 193 | vec3 emissive = EmissiveFactor; |
| 187 | #endif | 194 | #endif |
| 188 | #ifdef HAS_OCCLUSION_MAP | 195 | #if HAS_OCCLUSION_MAP |
| 189 | float occlusion = texture(AmbientOcclusionTexture, Texcoord).r; | 196 | float occlusion = texture(AmbientOcclusionTexture, Texcoord).r; |
| 190 | #else | 197 | #else |
| 191 | float occlusion = 1.0; | 198 | float occlusion = 1.0; |
| @@ -193,6 +200,32 @@ void main() | |||
| 193 | float metallic = metal_roughness.x; | 200 | float metallic = metal_roughness.x; |
| 194 | float roughness = metal_roughness.y; | 201 | float roughness = metal_roughness.y; |
| 195 | 202 | ||
| 203 | // TODO: Also use the specular F0 map from the model, and emissive. Make sure | ||
| 204 | // to use all maps. | ||
| 205 | // https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4 | ||
| 206 | #if HAS_NORMAL_MAP | ||
| 207 | // Spec: "After dequantization, texel values MUST be mapped as follows: | ||
| 208 | // red [0.0 .. 1.0] to X [-1 .. 1], | ||
| 209 | // green [0.0 .. 1.0] to Y [-1 .. 1], | ||
| 210 | // blue (0.5 .. 1.0] to Z ( 0 .. 1]. | ||
| 211 | // Normal textures SHOULD NOT contain blue values less than or equal to 0.5." | ||
| 212 | vec3 normalMapSample = texture(NormalMap, Texcoord).xyz * 2.0 - 1.0; | ||
| 213 | vec3 N = get_ws_normal(Normal, normalMapSample); | ||
| 214 | #elif HAS_NORMALS | ||
| 215 | vec3 N = normalize(Normal); | ||
| 216 | #endif | ||
| 217 | vec3 V = normalize(CameraPosition - Position); | ||
| 218 | vec3 R = reflect(-V, N); | ||
| 219 | // Not needed for IBL. | ||
| 220 | //vec3 L = N; | ||
| 221 | //vec3 H = normalize(L + V); | ||
| 222 | |||
| 223 | float NdotV = max(0.0, dot(N, V)); | ||
| 224 | // Not needed for IBL. | ||
| 225 | //float NdotL = max(0.0, dot(N,L)); | ||
| 226 | //float NdotH = max(0.0, dot(N,H)); | ||
| 227 | //float HdotV = clamp(dot(H,V), 0.0, 1.0); // Clamp to prevent black spots. | ||
| 228 | |||
| 196 | // For a single light direction: | 229 | // For a single light direction: |
| 197 | // vec3 brdf = cook_torrance(albedo, metallic, roughness, NdotL, NdotV, NdotH, HdotV); | 230 | // vec3 brdf = cook_torrance(albedo, metallic, roughness, NdotL, NdotV, NdotH, HdotV); |
| 198 | // vec3 Li = texture(Sky, N).rgb; | 231 | // vec3 Li = texture(Sky, N).rgb; |
| @@ -251,5 +284,5 @@ void main() | |||
| 251 | // //colour = B * 0.5 + 0.5; | 284 | // //colour = B * 0.5 + 0.5; |
| 252 | // } | 285 | // } |
| 253 | 286 | ||
| 254 | Colour = vec4(colour, 1.0); | 287 | Colour = vec4(colour, base_colour.a); |
| 255 | } | 288 | } |
diff --git a/gfx/shaders/cook_torrance.vert b/shaders/cook_torrance.vert index 5f126c0..17fe1f7 100644 --- a/gfx/shaders/cook_torrance.vert +++ b/shaders/cook_torrance.vert | |||
| @@ -5,7 +5,7 @@ uniform mat4 ModelMatrix; | |||
| 5 | uniform mat4 View; | 5 | uniform mat4 View; |
| 6 | uniform mat4 Projection; | 6 | uniform mat4 Projection; |
| 7 | //uniform mat4 MVP; | 7 | //uniform mat4 MVP; |
| 8 | #ifdef HAS_JOINTS | 8 | #if HAS_JOINTS |
| 9 | // The client should pass in an appropriate value for MAX_JOINTS. | 9 | // The client should pass in an appropriate value for MAX_JOINTS. |
| 10 | // #define MAX_JOINTS 96 | 10 | // #define MAX_JOINTS 96 |
| 11 | // | 11 | // |
| @@ -21,35 +21,35 @@ uniform mat4 JointMatrices[MAX_JOINTS]; // Use 4x4 for now to keep it simple. | |||
| 21 | #endif | 21 | #endif |
| 22 | 22 | ||
| 23 | layout (location = 0) in vec3 vPosition; | 23 | layout (location = 0) in vec3 vPosition; |
| 24 | #ifdef HAS_NORMALS | 24 | #if HAS_NORMALS |
| 25 | layout (location = 1) in vec3 vNormal; | 25 | layout (location = 1) in vec3 vNormal; |
| 26 | #endif | 26 | #endif |
| 27 | #ifdef HAS_TANGENTS | 27 | #if HAS_TANGENTS |
| 28 | layout (location = 2) in vec4 vTangent; | 28 | layout (location = 2) in vec4 vTangent; |
| 29 | #endif | 29 | #endif |
| 30 | #ifdef HAS_TEXCOORDS | 30 | #if HAS_TEXCOORDS |
| 31 | layout (location = 3) in vec2 vTexcoord; | 31 | layout (location = 3) in vec2 vTexcoord; |
| 32 | #endif | 32 | #endif |
| 33 | #ifdef HAS_JOINTS | 33 | #if HAS_JOINTS |
| 34 | layout (location = 4) in uvec4 vJoint; | 34 | layout (location = 4) in uvec4 vJoint; |
| 35 | layout (location = 5) in vec4 vWeight; | 35 | layout (location = 5) in vec4 vWeight; |
| 36 | #endif | 36 | #endif |
| 37 | 37 | ||
| 38 | // World-space position, normal and tangent. | 38 | // World-space position, normal and tangent. |
| 39 | out vec3 Position; | 39 | out vec3 Position; |
| 40 | #ifdef HAS_NORMALS | 40 | #if HAS_NORMALS |
| 41 | out vec3 Normal; | 41 | out vec3 Normal; |
| 42 | #endif | 42 | #endif |
| 43 | #ifdef HAS_TANGENTS | 43 | #if HAS_TANGENTS |
| 44 | out vec4 Tangent; | 44 | out vec4 Tangent; |
| 45 | #endif | 45 | #endif |
| 46 | #ifdef HAS_TEXCOORDS | 46 | #if HAS_TEXCOORDS |
| 47 | out vec2 Texcoord; | 47 | out vec2 Texcoord; |
| 48 | #endif | 48 | #endif |
| 49 | 49 | ||
| 50 | void main() | 50 | void main() |
| 51 | { | 51 | { |
| 52 | #ifdef HAS_JOINTS | 52 | #if HAS_JOINTS |
| 53 | mat4 skinMatrix = | 53 | mat4 skinMatrix = |
| 54 | vWeight.x * JointMatrices[vJoint.x] + | 54 | vWeight.x * JointMatrices[vJoint.x] + |
| 55 | vWeight.y * JointMatrices[vJoint.y] + | 55 | vWeight.y * JointMatrices[vJoint.y] + |
| @@ -59,14 +59,14 @@ void main() | |||
| 59 | #else | 59 | #else |
| 60 | Position = vec3(ModelMatrix * vec4(vPosition, 1.0)); | 60 | Position = vec3(ModelMatrix * vec4(vPosition, 1.0)); |
| 61 | #endif | 61 | #endif |
| 62 | #ifdef HAS_NORMALS | 62 | #if HAS_NORMALS |
| 63 | Normal = mat3(ModelMatrix) * vNormal; | 63 | Normal = mat3(ModelMatrix) * vNormal; |
| 64 | //Normal = normalize(ModelMatrix * vec4(vNormal, 0.0)).xyz; | 64 | //Normal = normalize(ModelMatrix * vec4(vNormal, 0.0)).xyz; |
| 65 | #endif | 65 | #endif |
| 66 | #ifdef HAS_TANGENTS | 66 | #if HAS_TANGENTS |
| 67 | Tangent = vec4(mat3(ModelMatrix) * vTangent.xyz, vTangent.w); | 67 | Tangent = vec4(mat3(ModelMatrix) * vTangent.xyz, vTangent.w); |
| 68 | #endif | 68 | #endif |
| 69 | #ifdef HAS_TEXCOORDS | 69 | #if HAS_TEXCOORDS |
| 70 | Texcoord = vTexcoord; | 70 | Texcoord = vTexcoord; |
| 71 | #endif | 71 | #endif |
| 72 | gl_Position = Projection * View * vec4(Position, 1.0); | 72 | gl_Position = Projection * View * vec4(Position, 1.0); |
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/scene/animation.c b/src/animation.c index 08d02ce..c52df73 100644 --- a/gfx/src/scene/animation.c +++ b/src/animation.c | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | #include "animation_impl.h" | 1 | #include "animation_impl.h" |
| 2 | 2 | ||
| 3 | #include "node_impl.h" | 3 | #include "memory.h" |
| 4 | #include "scene_memory.h" | ||
| 5 | 4 | ||
| 6 | #include <string.h> | 5 | #include <string.h> |
| 7 | 6 | ||
| @@ -204,10 +203,6 @@ void gfx_destroy_anima(Anima** anima) { | |||
| 204 | mem_free_animation(&animation); | 203 | mem_free_animation(&animation); |
| 205 | } | 204 | } |
| 206 | 205 | ||
| 207 | if ((*anima)->parent.val) { | ||
| 208 | gfx_del_node((*anima)->parent); | ||
| 209 | } | ||
| 210 | |||
| 211 | mem_free_anima(anima); | 206 | mem_free_anima(anima); |
| 212 | } | 207 | } |
| 213 | } | 208 | } |
| @@ -450,10 +445,8 @@ void gfx_update_animation(Anima* anima, R t) { | |||
| 450 | // which we have constructed to be the common root of all skeletons. | 445 | // which we have constructed to be the common root of all skeletons. |
| 451 | // | 446 | // |
| 452 | // This procedure touches every joint exactly once. | 447 | // This procedure touches every joint exactly once. |
| 453 | SceneNode* root_node = mem_get_node(anima->parent); | 448 | const mat4 root_global_transform = mat4_id(); |
| 454 | // LOGD("Root: %u, child: %u", anima->parent.val, root->child.val); | 449 | const mat4 root_inv_global_transform = mat4_id(); |
| 455 | const mat4 root_global_transform = gfx_get_node_global_transform(root_node); | ||
| 456 | const mat4 root_inv_global_transform = mat4_inverse(root_global_transform); | ||
| 457 | 450 | ||
| 458 | Joint* root_joint = get_anima_root_joint(anima); | 451 | Joint* root_joint = get_anima_root_joint(anima); |
| 459 | compute_joint_matrices_rec( | 452 | compute_joint_matrices_rec( |
| @@ -504,21 +497,21 @@ Box gfx_get_joint_box( | |||
| 504 | return (Box){ | 497 | return (Box){ |
| 505 | .vertices = { | 498 | .vertices = { |
| 506 | mat4_mul_vec3( | 499 | mat4_mul_vec3( |
| 507 | joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmax.z), 1), | 500 | joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmax.z), 1), |
| 508 | mat4_mul_vec3( | 501 | mat4_mul_vec3( |
| 509 | joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmax.z), 1), | 502 | joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmax.z), 1), |
| 510 | mat4_mul_vec3( | 503 | mat4_mul_vec3( |
| 511 | joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmax.z), 1), | 504 | joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmax.z), 1), |
| 512 | mat4_mul_vec3( | 505 | mat4_mul_vec3( |
| 513 | joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmax.z), 1), | 506 | joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmax.z), 1), |
| 514 | mat4_mul_vec3( | 507 | mat4_mul_vec3( |
| 515 | joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmin.z), 1), | 508 | joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmin.z), 1), |
| 516 | mat4_mul_vec3( | 509 | mat4_mul_vec3( |
| 517 | joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmin.z), 1), | 510 | joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmin.z), 1), |
| 518 | mat4_mul_vec3( | 511 | mat4_mul_vec3( |
| 519 | joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmin.z), 1), | 512 | joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmin.z), 1), |
| 520 | mat4_mul_vec3( | 513 | mat4_mul_vec3( |
| 521 | joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmin.z), 1), | 514 | joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmin.z), 1), |
| 522 | } | 515 | } |
| 523 | }; | 516 | }; |
| 524 | } | 517 | } |
diff --git a/gfx/src/scene/animation_impl.h b/src/animation_impl.h index 4408158..4929b97 100644 --- a/gfx/src/scene/animation_impl.h +++ b/src/animation_impl.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <gfx/scene/animation.h> | 3 | #include <gfx/animation.h> |
| 4 | #include <gfx/sizes.h> | 4 | #include <gfx/sizes.h> |
| 5 | 5 | ||
| 6 | #include "types.h" | 6 | #include "types.h" |
| @@ -12,7 +12,6 @@ | |||
| 12 | #include <math/vec3.h> | 12 | #include <math/vec3.h> |
| 13 | 13 | ||
| 14 | #include <stddef.h> | 14 | #include <stddef.h> |
| 15 | #include <stdint.h> | ||
| 16 | 15 | ||
| 17 | typedef struct Buffer Buffer; | 16 | typedef struct Buffer Buffer; |
| 18 | 17 | ||
| @@ -24,24 +23,24 @@ typedef struct Buffer Buffer; | |||
| 24 | /// Joints are mutable and store the transform and joint matrices that result | 23 | /// Joints are mutable and store the transform and joint matrices that result |
| 25 | /// from animation, aside from the inverse bind matrix. | 24 | /// from animation, aside from the inverse bind matrix. |
| 26 | typedef struct Joint { | 25 | typedef struct Joint { |
| 27 | joint_idx child; /// First child Joint; index into Anima's joints. | 26 | joint_idx child; // First child Joint; index into Anima's joints. |
| 28 | joint_idx next; /// Next sibling Joint; index into Anima's joints. | 27 | joint_idx next; // Next sibling Joint; index into Anima's joints. |
| 29 | mat4 transform; /// Local transform relative to parent. | 28 | mat4 transform; // Local transform relative to parent. |
| 30 | mat4 inv_bind_matrix; /// Transforms the mesh into the joint's local space. | 29 | mat4 inv_bind_matrix; // Transforms the mesh into the joint's local space. |
| 31 | mat4 joint_matrix; /// inv(global) * global joint transform * inv(bind). | 30 | mat4 joint_matrix; // inv(global) * global joint transform * inv(bind). |
| 32 | aabb3 box; /// Bounding box of vertices affected by joint. | 31 | aabb3 box; // Bounding box of vertices affected by joint. |
| 33 | } Joint; | 32 | } Joint; |
| 34 | 33 | ||
| 35 | /// Animation skeleton. | 34 | /// Animation skeleton. |
| 36 | typedef struct Skeleton { | 35 | typedef struct Skeleton { |
| 37 | skeleton_idx next; | 36 | skeleton_idx next; |
| 38 | size_t num_joints; | 37 | size_t num_joints; |
| 39 | joint_idx joints[GFX_MAX_NUM_JOINTS]; /// Indices into Anima's joints array. | 38 | joint_idx joints[GFX_MAX_NUM_JOINTS]; // Indices into Anima's joints array. |
| 40 | } Skeleton; | 39 | } Skeleton; |
| 41 | 40 | ||
| 42 | /// A keyframe of animation. | 41 | /// A keyframe of animation. |
| 43 | typedef struct Keyframe { | 42 | typedef struct Keyframe { |
| 44 | R time; /// Start time in [0, end animation time] | 43 | R time; // Start time in [0, end animation time] |
| 45 | union { | 44 | union { |
| 46 | vec3 translation; | 45 | vec3 translation; |
| 47 | quat rotation; | 46 | quat rotation; |
| @@ -50,7 +49,7 @@ typedef struct Keyframe { | |||
| 50 | 49 | ||
| 51 | /// Animation channel. | 50 | /// Animation channel. |
| 52 | typedef struct Channel { | 51 | typedef struct Channel { |
| 53 | joint_idx target; /// Index into Anima's joints array. | 52 | joint_idx target; // Index into Anima's joints array. |
| 54 | ChannelType type; | 53 | ChannelType type; |
| 55 | AnimationInterpolation interpolation; | 54 | AnimationInterpolation interpolation; |
| 56 | size_t num_keyframes; | 55 | size_t num_keyframes; |
| @@ -89,10 +88,9 @@ typedef struct AnimationState { | |||
| 89 | /// have no parent (a skeleton need not have its own root and can be a set of | 88 | /// have no parent (a skeleton need not have its own root and can be a set of |
| 90 | /// disjoint node hierarchies). | 89 | /// disjoint node hierarchies). |
| 91 | typedef struct Anima { | 90 | typedef struct Anima { |
| 92 | node_idx parent; /// Parent SceneNode. | 91 | skeleton_idx skeleton; // Index of first skeleton. |
| 93 | skeleton_idx skeleton; /// Index of first skeleton. | 92 | animation_idx animation; // Index of first animation. |
| 94 | animation_idx animation; /// Index of first animation. | 93 | AnimationState state; // Current animation state. |
| 95 | AnimationState state; /// Current animation state. | 94 | size_t num_joints; // Number of actual joints in the array. |
| 96 | size_t num_joints; /// Number of actual joints in the array. | 95 | Joint joints[GFX_MAX_NUM_JOINTS]; // Shared by all skeletons. |
| 97 | Joint joints[GFX_MAX_NUM_JOINTS]; /// Shared by all skeletons. | ||
| 98 | } Anima; | 96 | } Anima; |
diff --git a/gfx/src/asset/asset_cache.c b/src/asset/asset_cache.c index 16c4d5c..dfaf7c6 100644 --- a/gfx/src/asset/asset_cache.c +++ b/src/asset/asset_cache.c | |||
| @@ -1,15 +1,15 @@ | |||
| 1 | #include "asset_cache.h" | 1 | #include "asset_cache.h" |
| 2 | 2 | ||
| 3 | #include "animation_impl.h" | ||
| 4 | #include "memory.h" | ||
| 3 | #include "model.h" | 5 | #include "model.h" |
| 4 | #include "scene/animation_impl.h" | ||
| 5 | #include "scene/model_impl.h" | 6 | #include "scene/model_impl.h" |
| 6 | #include "scene/node_impl.h" | 7 | #include "scene/node_impl.h" |
| 7 | #include "scene/scene_memory.h" | ||
| 8 | #include "texture.h" | 8 | #include "texture.h" |
| 9 | 9 | ||
| 10 | #include <gfx/asset.h> | 10 | #include <gfx/asset.h> |
| 11 | #include <gfx/gfx.h> | 11 | #include <gfx/gfx.h> |
| 12 | #include <gfx/scene/node.h> | 12 | #include <gfx/scene.h> |
| 13 | #include <gfx_assert.h> | 13 | #include <gfx_assert.h> |
| 14 | 14 | ||
| 15 | #include <cstring.h> | 15 | #include <cstring.h> |
| @@ -167,7 +167,6 @@ static Model* clone_model(const Model* model) { | |||
| 167 | 167 | ||
| 168 | SceneNode* root_copy = gfx_clone_scene_shallow(root); | 168 | SceneNode* root_copy = gfx_clone_scene_shallow(root); |
| 169 | root_copy->anima = mem_get_anima_index(anima_copy); | 169 | root_copy->anima = mem_get_anima_index(anima_copy); |
| 170 | anima_copy->parent = mem_get_node_index(root_copy); | ||
| 171 | 170 | ||
| 172 | Model* copy = mem_alloc_model(); | 171 | Model* copy = mem_alloc_model(); |
| 173 | copy->root = mem_get_node_index(root_copy); | 172 | copy->root = mem_get_node_index(root_copy); |
| @@ -179,6 +178,7 @@ static Model* clone_model(const Model* model) { | |||
| 179 | 178 | ||
| 180 | void gfx_init_asset_cache(AssetCache* cache) { | 179 | void gfx_init_asset_cache(AssetCache* cache) { |
| 181 | assert(cache); | 180 | assert(cache); |
| 181 | |||
| 182 | mempool_make(&cache->assets); | 182 | mempool_make(&cache->assets); |
| 183 | 183 | ||
| 184 | // Allocate a dummy asset at index 0 to guarantee that no assets allocated by | 184 | // Allocate a dummy asset at index 0 to guarantee that no assets allocated by |
| @@ -189,6 +189,7 @@ void gfx_init_asset_cache(AssetCache* cache) { | |||
| 189 | 189 | ||
| 190 | void gfx_destroy_asset_cache(AssetCache* cache) { | 190 | void gfx_destroy_asset_cache(AssetCache* cache) { |
| 191 | assert(cache); | 191 | assert(cache); |
| 192 | // TODO: Destroy assets here. | ||
| 192 | mempool_del(&cache->assets); | 193 | mempool_del(&cache->assets); |
| 193 | } | 194 | } |
| 194 | 195 | ||
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..b5c6b0d 100644 --- a/gfx/src/asset/model.c +++ b/src/asset/model.c | |||
| @@ -82,30 +82,26 @@ | |||
| 82 | #include "asset/model.h" | 82 | #include "asset/model.h" |
| 83 | 83 | ||
| 84 | #include "asset/texture.h" | 84 | #include "asset/texture.h" |
| 85 | #include "gfx/core.h" | ||
| 86 | #include "gfx/gfx.h" | ||
| 87 | #include "gfx/scene/animation.h" | ||
| 88 | #include "gfx/scene/camera.h" | ||
| 89 | #include "gfx/scene/material.h" | ||
| 90 | #include "gfx/scene/mesh.h" | ||
| 91 | #include "gfx/scene/node.h" | ||
| 92 | #include "gfx/scene/object.h" | ||
| 93 | #include "gfx/scene/scene.h" | ||
| 94 | #include "gfx/sizes.h" | ||
| 95 | #include "gfx/util/shader.h" | ||
| 96 | |||
| 97 | #include "gfx_assert.h" | 85 | #include "gfx_assert.h" |
| 98 | #include "scene/model_impl.h" | 86 | #include "scene/model_impl.h" |
| 99 | 87 | ||
| 100 | #include "cstring.h" | 88 | #include <gfx/animation.h> |
| 101 | #include "error.h" | 89 | #include <gfx/core.h> |
| 102 | #include "log/log.h" | 90 | #include <gfx/gfx.h> |
| 103 | #include "math/camera.h" | 91 | #include <gfx/render/llr.h> |
| 104 | #include "math/defs.h" | 92 | #include <gfx/scene.h> |
| 105 | #include "math/mat4.h" | 93 | #include <gfx/sizes.h> |
| 106 | #include "math/quat.h" | 94 | #include <gfx/util/shader.h> |
| 107 | #include "math/vec2.h" | 95 | |
| 108 | #include "math/vec3.h" | 96 | #include <cstring.h> |
| 97 | #include <error.h> | ||
| 98 | #include <log/log.h> | ||
| 99 | #include <math/camera.h> | ||
| 100 | #include <math/defs.h> | ||
| 101 | #include <math/mat4.h> | ||
| 102 | #include <math/quat.h> | ||
| 103 | #include <math/vec2.h> | ||
| 104 | #include <math/vec3.h> | ||
| 109 | 105 | ||
| 110 | #include "cgltf_tangents.h" | 106 | #include "cgltf_tangents.h" |
| 111 | #define CGLTF_IMPLEMENTATION | 107 | #define CGLTF_IMPLEMENTATION |
| @@ -142,6 +138,7 @@ | |||
| 142 | #define DEFINE_HAS_NORMAL_MAP "HAS_NORMAL_MAP" | 138 | #define DEFINE_HAS_NORMAL_MAP "HAS_NORMAL_MAP" |
| 143 | #define DEFINE_HAS_OCCLUSION_MAP "HAS_OCCLUSION_MAP" | 139 | #define DEFINE_HAS_OCCLUSION_MAP "HAS_OCCLUSION_MAP" |
| 144 | #define DEFINE_HAS_EMISSIVE_MAP "HAS_EMISSIVE_MAP" | 140 | #define DEFINE_HAS_EMISSIVE_MAP "HAS_EMISSIVE_MAP" |
| 141 | #define DEFINE_HAS_TRANSPARENCY "HAS_TRANSPARENCY" | ||
| 145 | #define DEFINE_HAS_JOINTS "HAS_JOINTS" | 142 | #define DEFINE_HAS_JOINTS "HAS_JOINTS" |
| 146 | #define DEFINE_MAX_JOINTS "MAX_JOINTS" | 143 | #define DEFINE_MAX_JOINTS "MAX_JOINTS" |
| 147 | 144 | ||
| @@ -170,6 +167,8 @@ typedef struct MeshPermutation { | |||
| 170 | bool has_normal_map : 1; | 167 | bool has_normal_map : 1; |
| 171 | bool has_occlusion_map : 1; | 168 | bool has_occlusion_map : 1; |
| 172 | bool has_emissive_map : 1; | 169 | bool has_emissive_map : 1; |
| 170 | // Material. | ||
| 171 | bool has_transparency : 1; | ||
| 173 | }; | 172 | }; |
| 174 | int32_t all; | 173 | int32_t all; |
| 175 | }; | 174 | }; |
| @@ -196,6 +195,7 @@ static size_t make_defines( | |||
| 196 | check(has_normal_map, DEFINE_HAS_NORMAL_MAP); | 195 | check(has_normal_map, DEFINE_HAS_NORMAL_MAP); |
| 197 | check(has_occlusion_map, DEFINE_HAS_OCCLUSION_MAP); | 196 | check(has_occlusion_map, DEFINE_HAS_OCCLUSION_MAP); |
| 198 | check(has_emissive_map, DEFINE_HAS_EMISSIVE_MAP); | 197 | check(has_emissive_map, DEFINE_HAS_EMISSIVE_MAP); |
| 198 | check(has_transparency, DEFINE_HAS_TRANSPARENCY); | ||
| 199 | 199 | ||
| 200 | if (perm.has_joints) { | 200 | if (perm.has_joints) { |
| 201 | defines[next].name = sstring_make(DEFINE_MAX_JOINTS); | 201 | defines[next].name = sstring_make(DEFINE_MAX_JOINTS); |
| @@ -212,12 +212,12 @@ static ShaderProgram* make_shader_permutation( | |||
| 212 | LOGD( | 212 | LOGD( |
| 213 | "Compiling Cook-Torrance shader permutation: texcoords: %d, normals: " | 213 | "Compiling Cook-Torrance shader permutation: texcoords: %d, normals: " |
| 214 | "%d, tangents: %d, joints: %d, weights: %d, albedo map: %d, " | 214 | "%d, tangents: %d, joints: %d, weights: %d, albedo map: %d, " |
| 215 | "metallic-roughness map: " | 215 | "metallic-roughness map: %d, normal map: %d, AO map: %d, emissive map: " |
| 216 | "%d, normal " | 216 | "%d, has transparency: %d", |
| 217 | "map: %d, AO map: %d, emissive map: %d", | ||
| 218 | perm.has_texcoords, perm.has_normals, perm.has_tangents, perm.has_joints, | 217 | perm.has_texcoords, perm.has_normals, perm.has_tangents, perm.has_joints, |
| 219 | perm.has_weights, perm.has_albedo_map, perm.has_metallic_roughness_map, | 218 | perm.has_weights, perm.has_albedo_map, perm.has_metallic_roughness_map, |
| 220 | perm.has_normal_map, perm.has_occlusion_map, perm.has_emissive_map); | 219 | perm.has_normal_map, perm.has_occlusion_map, perm.has_emissive_map, |
| 220 | perm.has_transparency); | ||
| 221 | 221 | ||
| 222 | ShaderCompilerDefine defines[GFX_MAX_SHADER_COMPILER_DEFINES]; | 222 | ShaderCompilerDefine defines[GFX_MAX_SHADER_COMPILER_DEFINES]; |
| 223 | const size_t num_defines = make_defines(perm, defines); | 223 | const size_t num_defines = make_defines(perm, defines); |
| @@ -578,11 +578,10 @@ static bool load_buffers( | |||
| 578 | const cgltf_buffer* buffer = &data->buffers[i]; | 578 | const cgltf_buffer* buffer = &data->buffers[i]; |
| 579 | assert(buffer->data); | 579 | assert(buffer->data); |
| 580 | buffers[i] = gfx_make_buffer( | 580 | buffers[i] = gfx_make_buffer( |
| 581 | gfxcore, &(BufferDesc){ | 581 | gfxcore, &(BufferDesc){.usage = BufferStatic, |
| 582 | .usage = BufferStatic, | 582 | .type = BufferUntyped, |
| 583 | .type = BufferUntyped, | 583 | .data.data = buffer->data, |
| 584 | .data.data = buffer->data, | 584 | .data.count = buffer->size}); |
| 585 | .data.count = buffer->size}); | ||
| 586 | if (!buffers[i]) { | 585 | if (!buffers[i]) { |
| 587 | return false; | 586 | return false; |
| 588 | } | 587 | } |
| @@ -604,11 +603,10 @@ static bool load_tangent_buffers( | |||
| 604 | const cgltfTangentBuffer* buffer = &cgltf_tangent_buffers[i]; | 603 | const cgltfTangentBuffer* buffer = &cgltf_tangent_buffers[i]; |
| 605 | assert(buffer->data); | 604 | assert(buffer->data); |
| 606 | tangent_buffers[i] = gfx_make_buffer( | 605 | tangent_buffers[i] = gfx_make_buffer( |
| 607 | gfxcore, &(BufferDesc){ | 606 | gfxcore, &(BufferDesc){.usage = BufferStatic, |
| 608 | .usage = BufferStatic, | 607 | .type = BufferUntyped, |
| 609 | .type = BufferUntyped, | 608 | .data.data = buffer->data, |
| 610 | .data.data = buffer->data, | 609 | .data.count = buffer->size_bytes}); |
| 611 | .data.count = buffer->size_bytes}); | ||
| 612 | if (!tangent_buffers[i]) { | 610 | if (!tangent_buffers[i]) { |
| 613 | return false; | 611 | return false; |
| 614 | } | 612 | } |
| @@ -682,14 +680,13 @@ static void load_textures_lazy( | |||
| 682 | mstring fullpath = | 680 | mstring fullpath = |
| 683 | mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); | 681 | mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); |
| 684 | 682 | ||
| 685 | load_texture_cmds[i] = (LoadTextureCmd){ | 683 | load_texture_cmds[i] = (LoadTextureCmd){.origin = AssetFromFile, |
| 686 | .origin = AssetFromFile, | 684 | .type = LoadTexture, |
| 687 | .type = LoadTexture, | 685 | .colour_space = LinearColourSpace, |
| 688 | .colour_space = sRGB, | 686 | .filtering = filtering, |
| 689 | .filtering = filtering, | 687 | .wrap = wrap, |
| 690 | .wrap = wrap, | 688 | .mipmaps = mipmaps, |
| 691 | .mipmaps = mipmaps, | 689 | .data.texture.filepath = fullpath}; |
| 692 | .data.texture.filepath = fullpath}; | ||
| 693 | } | 690 | } |
| 694 | } | 691 | } |
| 695 | 692 | ||
| @@ -717,8 +714,12 @@ static bool load_texture_and_uniform( | |||
| 717 | // not be used as albedo and vice versa. | 714 | // not be used as albedo and vice versa. |
| 718 | if (!textures[texture_index]) { | 715 | if (!textures[texture_index]) { |
| 719 | LoadTextureCmd* cmd = &load_texture_cmds[texture_index]; | 716 | LoadTextureCmd* cmd = &load_texture_cmds[texture_index]; |
| 720 | // TODO: Check for colour textures and default to LinearColourSpace instead. | 717 | // Albedo and emissive use sRGB. Other textures use a linear colour space. |
| 721 | if (texture_type == NormalMap) { | 718 | // See the notes on the spec. |
| 719 | if ((texture_type == BaseColorTexture) || | ||
| 720 | (texture_type == EmissiveTexture)) { | ||
| 721 | cmd->colour_space = sRGB; | ||
| 722 | } else { | ||
| 722 | cmd->colour_space = LinearColourSpace; | 723 | cmd->colour_space = LinearColourSpace; |
| 723 | } | 724 | } |
| 724 | 725 | ||
| @@ -745,6 +746,19 @@ static bool load_texture_and_uniform( | |||
| 745 | return true; | 746 | return true; |
| 746 | } | 747 | } |
| 747 | 748 | ||
| 749 | static AlphaMode to_gfx_alpha_mode(cgltf_alpha_mode mode) { | ||
| 750 | switch (mode) { | ||
| 751 | case cgltf_alpha_mode_opaque: | ||
| 752 | return Opaque; | ||
| 753 | case cgltf_alpha_mode_mask: | ||
| 754 | return Mask; | ||
| 755 | case cgltf_alpha_mode_blend: | ||
| 756 | return Blend; | ||
| 757 | } | ||
| 758 | FAIL("unhandled alpha mode"); | ||
| 759 | return Opaque; | ||
| 760 | } | ||
| 761 | |||
| 748 | /// Load all materials from the glTF scene. | 762 | /// Load all materials from the glTF scene. |
| 749 | /// | 763 | /// |
| 750 | /// Return an array of Materials such that the index of each descriptor matches | 764 | /// Return an array of Materials such that the index of each descriptor matches |
| @@ -773,27 +787,27 @@ static bool load_materials( | |||
| 773 | 787 | ||
| 774 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 788 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 775 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 789 | desc.uniforms[next_uniform++] = (ShaderUniform){ |
| 776 | .name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), | 790 | .name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), |
| 777 | .type = UniformVec4, | 791 | .type = UniformVec4, |
| 778 | .value.vec4 = vec4_from_array(pbr->base_color_factor)}; | 792 | .value.uniform_vec4 = vec4_from_array(pbr->base_color_factor)}; |
| 779 | 793 | ||
| 780 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 794 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 781 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 795 | desc.uniforms[next_uniform++] = |
| 782 | .name = sstring_make(UNIFORM_METALLIC_FACTOR), | 796 | (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), |
| 783 | .type = UniformFloat, | 797 | .type = UniformFloat, |
| 784 | .value.scalar = pbr->metallic_factor}; | 798 | .value.uniform_float = pbr->metallic_factor}; |
| 785 | 799 | ||
| 786 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 800 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 787 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 801 | desc.uniforms[next_uniform++] = |
| 788 | .name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), | 802 | (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), |
| 789 | .type = UniformFloat, | 803 | .type = UniformFloat, |
| 790 | .value.scalar = pbr->roughness_factor}; | 804 | .value.uniform_float = pbr->roughness_factor}; |
| 791 | 805 | ||
| 792 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 806 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 793 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 807 | desc.uniforms[next_uniform++] = (ShaderUniform){ |
| 794 | .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), | 808 | .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), |
| 795 | .type = UniformVec3, | 809 | .type = UniformVec3, |
| 796 | .value.vec3 = vec3_from_array(mat->emissive_factor)}; | 810 | .value.uniform_vec3 = vec3_from_array(mat->emissive_factor)}; |
| 797 | 811 | ||
| 798 | if (pbr->base_color_texture.texture) { | 812 | if (pbr->base_color_texture.texture) { |
| 799 | if (!load_texture_and_uniform( | 813 | if (!load_texture_and_uniform( |
| @@ -840,6 +854,9 @@ static bool load_materials( | |||
| 840 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 854 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 841 | desc.num_uniforms = next_uniform; | 855 | desc.num_uniforms = next_uniform; |
| 842 | 856 | ||
| 857 | desc.alpha_mode = to_gfx_alpha_mode(mat->alpha_mode); | ||
| 858 | desc.alpha_cutoff = mat->alpha_cutoff; | ||
| 859 | |||
| 843 | materials[i] = gfx_make_material(&desc); | 860 | materials[i] = gfx_make_material(&desc); |
| 844 | if (!materials[i]) { | 861 | if (!materials[i]) { |
| 845 | return false; | 862 | return false; |
| @@ -854,28 +871,28 @@ static Material* make_default_material() { | |||
| 854 | MaterialDesc desc = (MaterialDesc){0}; | 871 | MaterialDesc desc = (MaterialDesc){0}; |
| 855 | 872 | ||
| 856 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 873 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 857 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 874 | desc.uniforms[desc.num_uniforms++] = |
| 858 | .name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), | 875 | (ShaderUniform){.name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), |
| 859 | .type = UniformVec4, | 876 | .type = UniformVec4, |
| 860 | .value.vec4 = vec4_make(1, 1, 1, 1)}; | 877 | .value.uniform_vec4 = vec4_make(1, 1, 1, 1)}; |
| 861 | 878 | ||
| 862 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 879 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 863 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 880 | desc.uniforms[desc.num_uniforms++] = |
| 864 | .name = sstring_make(UNIFORM_METALLIC_FACTOR), | 881 | (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), |
| 865 | .type = UniformFloat, | 882 | .type = UniformFloat, |
| 866 | .value.scalar = 0}; | 883 | .value.uniform_float = 0}; |
| 867 | 884 | ||
| 868 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 885 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 869 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 886 | desc.uniforms[desc.num_uniforms++] = |
| 870 | .name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), | 887 | (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), |
| 871 | .type = UniformFloat, | 888 | .type = UniformFloat, |
| 872 | .value.scalar = 1}; | 889 | .value.uniform_float = 1}; |
| 873 | 890 | ||
| 874 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 891 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
| 875 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 892 | desc.uniforms[desc.num_uniforms++] = |
| 876 | .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), | 893 | (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), |
| 877 | .type = UniformVec3, | 894 | .type = UniformVec3, |
| 878 | .value.vec3 = vec3_make(0, 0, 0)}; | 895 | .value.uniform_vec3 = vec3_make(0, 0, 0)}; |
| 879 | 896 | ||
| 880 | return gfx_make_material(&desc); | 897 | return gfx_make_material(&desc); |
| 881 | } | 898 | } |
| @@ -953,6 +970,7 @@ static bool load_meshes( | |||
| 953 | perm.has_normal_map = mat->normal_texture.texture != 0; | 970 | perm.has_normal_map = mat->normal_texture.texture != 0; |
| 954 | perm.has_occlusion_map = mat->occlusion_texture.texture != 0; | 971 | perm.has_occlusion_map = mat->occlusion_texture.texture != 0; |
| 955 | perm.has_emissive_map = mat->emissive_texture.texture != 0; | 972 | perm.has_emissive_map = mat->emissive_texture.texture != 0; |
| 973 | perm.has_transparency = mat->alpha_mode != cgltf_alpha_mode_opaque; | ||
| 956 | 974 | ||
| 957 | if (mat->has_pbr_metallic_roughness) { | 975 | if (mat->has_pbr_metallic_roughness) { |
| 958 | const cgltf_pbr_metallic_roughness* pbr = | 976 | const cgltf_pbr_metallic_roughness* pbr = |
| @@ -1012,7 +1030,6 @@ static bool load_meshes( | |||
| 1012 | const cgltf_attribute* attrib = &prim->attributes[a]; | 1030 | const cgltf_attribute* attrib = &prim->attributes[a]; |
| 1013 | const cgltf_accessor* accessor = attrib->data; | 1031 | const cgltf_accessor* accessor = attrib->data; |
| 1014 | const cgltf_buffer_view* view = accessor->buffer_view; | 1032 | const cgltf_buffer_view* view = accessor->buffer_view; |
| 1015 | const cgltf_size offset = accessor->offset + view->offset; | ||
| 1016 | const cgltf_size buffer_index = view->buffer - data->buffers; | 1033 | const cgltf_size buffer_index = view->buffer - data->buffers; |
| 1017 | 1034 | ||
| 1018 | assert(buffer_index < data->buffers_count); | 1035 | assert(buffer_index < data->buffers_count); |
| @@ -1102,12 +1119,20 @@ static bool load_meshes( | |||
| 1102 | break; | 1119 | break; |
| 1103 | } | 1120 | } |
| 1104 | 1121 | ||
| 1105 | #define CONFIGURE_BUFFER(buf) \ | 1122 | // See comments here for accessor/view/buffer invariants: |
| 1106 | if (buf) { \ | 1123 | // https://github.com/KhronosGroup/glTF-Sample-Assets/issues/242 |
| 1107 | buf->buffer = buffer; \ | 1124 | // Gfx only has Buffer and BufferView, not accessors. We must combine |
| 1108 | buf->offset_bytes = offset; \ | 1125 | // the glTF's accessor and view offsets correctly. |
| 1109 | buf->size_bytes = view->size; \ | 1126 | const cgltf_size offset = accessor->offset + view->offset; |
| 1110 | buf->stride_bytes = view->stride; \ | 1127 | const cgltf_size size_bytes = view->size - accessor->offset; |
| 1128 | |||
| 1129 | #define CONFIGURE_BUFFER(buf) \ | ||
| 1130 | if (buf) { \ | ||
| 1131 | buf->buffer = buffer; \ | ||
| 1132 | buf->offset_bytes = offset; \ | ||
| 1133 | buf->size_bytes = size_bytes; \ | ||
| 1134 | buf->stride_bytes = view->stride; \ | ||
| 1135 | buf->count = accessor->count; \ | ||
| 1111 | } | 1136 | } |
| 1112 | CONFIGURE_BUFFER(buffer_view_2d); | 1137 | CONFIGURE_BUFFER(buffer_view_2d); |
| 1113 | CONFIGURE_BUFFER(buffer_view_3d); | 1138 | CONFIGURE_BUFFER(buffer_view_3d); |
| @@ -1142,14 +1167,11 @@ static bool load_meshes( | |||
| 1142 | // either 2d or 3d positions but not both, here we can perform addition | 1167 | // either 2d or 3d positions but not both, here we can perform addition |
| 1143 | // to compute the total number of vertices. | 1168 | // to compute the total number of vertices. |
| 1144 | geometry_desc.num_verts = | 1169 | geometry_desc.num_verts = |
| 1145 | (geometry_desc.positions2d.size_bytes / sizeof(vec2)) + | 1170 | geometry_desc.positions2d.count + geometry_desc.positions3d.count; |
| 1146 | (geometry_desc.positions3d.size_bytes / sizeof(vec3)); | 1171 | |
| 1147 | 1172 | #define CHECK_COUNT(buffer_view, type, num_components) \ | |
| 1148 | #define CHECK_COUNT(buffer_view, type, num_components) \ | 1173 | if (geometry_desc.buffer_view.buffer) { \ |
| 1149 | if (geometry_desc.buffer_view.buffer) { \ | 1174 | assert(geometry_desc.buffer_view.count == geometry_desc.num_verts); \ |
| 1150 | assert( \ | ||
| 1151 | (geometry_desc.buffer_view.size_bytes / \ | ||
| 1152 | (num_components * sizeof(type))) == geometry_desc.num_verts); \ | ||
| 1153 | } | 1175 | } |
| 1154 | 1176 | ||
| 1155 | // Check that the number of vertices is consistent across all vertex | 1177 | // Check that the number of vertices is consistent across all vertex |
| @@ -1192,10 +1214,10 @@ static bool load_meshes( | |||
| 1192 | shader ? shader : make_shader_permutation(gfxcore, perm); | 1214 | shader ? shader : make_shader_permutation(gfxcore, perm); |
| 1193 | assert(mesh_shader); | 1215 | assert(mesh_shader); |
| 1194 | 1216 | ||
| 1195 | meshes[next_mesh] = gfx_make_mesh(&(MeshDesc){ | 1217 | meshes[next_mesh] = |
| 1196 | .geometry = geometries[next_mesh], | 1218 | gfx_make_mesh(&(MeshDesc){.geometry = geometries[next_mesh], |
| 1197 | .material = material, | 1219 | .material = material, |
| 1198 | .shader = mesh_shader}); | 1220 | .shader = mesh_shader}); |
| 1199 | 1221 | ||
| 1200 | if (!meshes[next_mesh]) { | 1222 | if (!meshes[next_mesh]) { |
| 1201 | return false; | 1223 | return false; |
| @@ -1432,9 +1454,9 @@ static void load_animations( | |||
| 1432 | const cgltf_animation* animation = &data->animations[a]; | 1454 | const cgltf_animation* animation = &data->animations[a]; |
| 1433 | AnimationDesc* animation_desc = &anima_desc->animations[a]; | 1455 | AnimationDesc* animation_desc = &anima_desc->animations[a]; |
| 1434 | 1456 | ||
| 1435 | *animation_desc = (AnimationDesc){ | 1457 | *animation_desc = |
| 1436 | .name = sstring_make(animation->name), | 1458 | (AnimationDesc){.name = sstring_make(animation->name), |
| 1437 | .num_channels = animation->channels_count}; | 1459 | .num_channels = animation->channels_count}; |
| 1438 | 1460 | ||
| 1439 | assert(animation->channels_count <= GFX_MAX_NUM_CHANNELS); | 1461 | assert(animation->channels_count <= GFX_MAX_NUM_CHANNELS); |
| 1440 | for (cgltf_size c = 0; c < animation->channels_count; ++c) { | 1462 | for (cgltf_size c = 0; c < animation->channels_count; ++c) { |
| @@ -1490,15 +1512,75 @@ static void load_animations( | |||
| 1490 | } | 1512 | } |
| 1491 | } | 1513 | } |
| 1492 | 1514 | ||
| 1515 | /// Remove joint nodes from the Gfx Scene. | ||
| 1516 | /// | ||
| 1517 | /// Joint nodes are not needed because joints are packed into the Anima. | ||
| 1518 | static void remove_joint_nodes( | ||
| 1519 | const cgltf_data* data, SceneNode** scene_nodes) { | ||
| 1520 | assert(data); | ||
| 1521 | assert(scene_nodes); | ||
| 1522 | |||
| 1523 | // This works assuming the joint nodes are contiguous. Contiguity is checked | ||
| 1524 | // when loading skins. See load_skins(). | ||
| 1525 | size_t min_joint_index = (size_t)-1; | ||
| 1526 | size_t max_joint_index = 0; | ||
| 1527 | |||
| 1528 | // First get the minimum and maximum indices of all joint nodes. | ||
| 1529 | for (cgltf_size s = 0; s < data->skins_count; ++s) { | ||
| 1530 | const cgltf_skin* skin = &data->skins[s]; | ||
| 1531 | |||
| 1532 | for (cgltf_size j = 0; j < skin->joints_count; ++j) { | ||
| 1533 | // Joint is an index/pointer into the nodes array. | ||
| 1534 | const cgltf_size joint_index = skin->joints[j] - data->nodes; | ||
| 1535 | assert(joint_index < data->nodes_count); | ||
| 1536 | |||
| 1537 | if (joint_index < min_joint_index) { | ||
| 1538 | min_joint_index = joint_index; | ||
| 1539 | } | ||
| 1540 | if (joint_index > max_joint_index) { | ||
| 1541 | max_joint_index = joint_index; | ||
| 1542 | } | ||
| 1543 | } | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | assert(min_joint_index < data->nodes_count); | ||
| 1547 | assert(max_joint_index < data->nodes_count); | ||
| 1548 | |||
| 1549 | // Now walk over the joint nodes. If a joint's parent is itself not a joint | ||
| 1550 | // node, then that joint is a root of a joint hierarchy (skins in glTF may | ||
| 1551 | // have multiple roots). In such case, delete the root joint recursively. | ||
| 1552 | for (cgltf_size s = 0; s < data->skins_count; ++s) { | ||
| 1553 | const cgltf_skin* skin = &data->skins[s]; | ||
| 1554 | |||
| 1555 | for (cgltf_size j = 0; j < skin->joints_count; ++j) { | ||
| 1556 | // Joint is an index/pointer into the nodes array. | ||
| 1557 | const cgltf_size joint_index = skin->joints[j] - data->nodes; | ||
| 1558 | assert(joint_index < data->nodes_count); | ||
| 1559 | |||
| 1560 | const cgltf_node* joint = &data->nodes[joint_index]; | ||
| 1561 | |||
| 1562 | // Parent node index. | ||
| 1563 | const cgltf_size parent_index = joint->parent - data->nodes; | ||
| 1564 | assert(parent_index < data->nodes_count); | ||
| 1565 | |||
| 1566 | // If the parent is not a joint node, recursively delete this joint node. | ||
| 1567 | if ((parent_index < min_joint_index) || | ||
| 1568 | (parent_index > max_joint_index)) { | ||
| 1569 | gfx_destroy_node(&scene_nodes[joint_index]); | ||
| 1570 | } | ||
| 1571 | } | ||
| 1572 | } | ||
| 1573 | } | ||
| 1574 | |||
| 1493 | /// Load all nodes from the glTF scene. | 1575 | /// Load all nodes from the glTF scene. |
| 1494 | /// | 1576 | /// |
| 1495 | /// This function ignores the many scenes and default scene of the glTF spec | 1577 | /// This function ignores the many scenes and default scene of the glTF spec |
| 1496 | /// and instead just loads all nodes into a single gfx Scene. | 1578 | /// and instead just loads all nodes into a single gfx Scene. |
| 1497 | static void load_nodes( | 1579 | static void load_nodes( |
| 1498 | const cgltf_data* data, SceneNode* root_node, SceneObject** objects, | 1580 | const cgltf_data* data, SceneNode* root_node, SceneObject** objects, |
| 1499 | SceneCamera** cameras, const Anima* anima, SceneNode** nodes) { | 1581 | const Anima* anima, SceneNode** nodes) { |
| 1500 | // Note that with glTF 2.0, nodes do not form a DAG / scene graph but a | 1582 | // Note that with glTF 2.0, nodes do not form a DAG / scene graph but a |
| 1501 | // disjount union of strict trees: | 1583 | // disjoint union of strict trees: |
| 1502 | // | 1584 | // |
| 1503 | // "For Version 2.0 conformance, the glTF node hierarchy is not a directed | 1585 | // "For Version 2.0 conformance, the glTF node hierarchy is not a directed |
| 1504 | // acyclic graph (DAG) or scene graph, but a disjoint union of strict trees. | 1586 | // acyclic graph (DAG) or scene graph, but a disjoint union of strict trees. |
| @@ -1511,25 +1593,25 @@ static void load_nodes( | |||
| 1511 | assert(data); | 1593 | assert(data); |
| 1512 | assert(root_node); | 1594 | assert(root_node); |
| 1513 | assert(objects); | 1595 | assert(objects); |
| 1514 | assert(cameras); | ||
| 1515 | assert(nodes); | 1596 | assert(nodes); |
| 1516 | 1597 | ||
| 1517 | cgltf_size next_camera = 0; | 1598 | cgltf_size next_camera = 0; |
| 1518 | 1599 | ||
| 1600 | // First pass: create the nodes. | ||
| 1519 | for (cgltf_size n = 0; n < data->nodes_count; ++n) { | 1601 | for (cgltf_size n = 0; n < data->nodes_count; ++n) { |
| 1520 | const cgltf_node* node = &data->nodes[n]; | 1602 | const cgltf_node* node = &data->nodes[n]; |
| 1521 | 1603 | ||
| 1522 | // Add SceneObject, SceneCamera or Lights. | 1604 | // Add SceneObject, Camera or Lights. |
| 1523 | // TODO: Handle lights once they are implemented in the gfx library. | 1605 | // TODO: Handle lights once they are implemented in the gfx library. |
| 1524 | if (node->mesh) { | 1606 | if (node->mesh) { |
| 1525 | const cgltf_size mesh_index = node->mesh - data->meshes; | 1607 | const cgltf_size mesh_index = node->mesh - data->meshes; |
| 1526 | assert(mesh_index < data->meshes_count); | 1608 | assert(mesh_index < data->meshes_count); |
| 1527 | SceneObject* object = objects[mesh_index]; | 1609 | SceneObject* object = objects[mesh_index]; |
| 1528 | gfx_construct_object_node(nodes[n], object); | 1610 | |
| 1611 | nodes[n] = gfx_make_object_node(object); | ||
| 1529 | 1612 | ||
| 1530 | if (node->skin) { | 1613 | if (node->skin) { |
| 1531 | assert(anima); | 1614 | assert(anima); |
| 1532 | |||
| 1533 | const cgltf_size skin_index = node->skin - data->skins; | 1615 | const cgltf_size skin_index = node->skin - data->skins; |
| 1534 | assert(skin_index < data->skins_count); | 1616 | assert(skin_index < data->skins_count); |
| 1535 | const Skeleton* skeleton = gfx_get_anima_skeleton(anima, skin_index); | 1617 | const Skeleton* skeleton = gfx_get_anima_skeleton(anima, skin_index); |
| @@ -1538,33 +1620,32 @@ static void load_nodes( | |||
| 1538 | } else if (node->camera) { | 1620 | } else if (node->camera) { |
| 1539 | assert(next_camera < data->cameras_count); | 1621 | assert(next_camera < data->cameras_count); |
| 1540 | 1622 | ||
| 1541 | Camera camera; | ||
| 1542 | const cgltf_camera* cam = node->camera; | 1623 | const cgltf_camera* cam = node->camera; |
| 1543 | 1624 | ||
| 1544 | // TODO: We could define a function load_cameras() the same way we load | 1625 | // TODO: We could define a function load_cameras() the same way we load |
| 1545 | // every mesh and then remove this ad-hoc loading of cameras here, as well | 1626 | // every mesh and then remove this ad-hoc loading of cameras here, as well |
| 1546 | // as remove 'next_camera'. | 1627 | // as remove 'next_camera'. |
| 1628 | Camera* camera = gfx_make_camera(); | ||
| 1547 | switch (cam->type) { | 1629 | switch (cam->type) { |
| 1548 | case cgltf_camera_type_orthographic: | 1630 | case cgltf_camera_type_orthographic: |
| 1549 | camera = camera_orthographic( | 1631 | *camera = camera_orthographic( |
| 1550 | 0, cam->data.orthographic.xmag, 0, cam->data.orthographic.ymag, | 1632 | 0, cam->data.orthographic.xmag, 0, cam->data.orthographic.ymag, |
| 1551 | cam->data.orthographic.znear, cam->data.orthographic.zfar); | 1633 | cam->data.orthographic.znear, cam->data.orthographic.zfar); |
| 1552 | break; | 1634 | break; |
| 1553 | case cgltf_camera_type_perspective: | 1635 | case cgltf_camera_type_perspective: |
| 1554 | camera = camera_perspective( | 1636 | *camera = camera_perspective( |
| 1555 | cam->data.perspective.yfov, cam->data.perspective.aspect_ratio, | 1637 | cam->data.perspective.yfov, cam->data.perspective.aspect_ratio, |
| 1556 | cam->data.perspective.znear, cam->data.perspective.zfar); | 1638 | cam->data.perspective.znear, cam->data.perspective.zfar); |
| 1557 | break; | 1639 | break; |
| 1558 | case cgltf_camera_type_invalid: | 1640 | case cgltf_camera_type_invalid: |
| 1559 | break; | 1641 | break; |
| 1560 | } | 1642 | } |
| 1561 | 1643 | nodes[n] = gfx_make_camera_node(camera); | |
| 1562 | gfx_set_camera_camera(cameras[next_camera], &camera); | ||
| 1563 | gfx_construct_camera_node(nodes[n], cameras[next_camera]); | ||
| 1564 | ++next_camera; | 1644 | ++next_camera; |
| 1565 | } else { | 1645 | } else { |
| 1566 | // TODO: implementation for missing node types. | 1646 | // TODO: implementation for missing node types. |
| 1567 | // These nodes currently default to logical nodes. | 1647 | // These nodes currently default to logical nodes. |
| 1648 | nodes[n] = gfx_make_node(); | ||
| 1568 | } | 1649 | } |
| 1569 | assert(nodes[n]); | 1650 | assert(nodes[n]); |
| 1570 | 1651 | ||
| @@ -1590,6 +1671,11 @@ static void load_nodes( | |||
| 1590 | } | 1671 | } |
| 1591 | } | 1672 | } |
| 1592 | gfx_set_node_transform(nodes[n], &transform); | 1673 | gfx_set_node_transform(nodes[n], &transform); |
| 1674 | } | ||
| 1675 | |||
| 1676 | // Second pass: wire up the node hierarchy. | ||
| 1677 | for (cgltf_size n = 0; n < data->nodes_count; ++n) { | ||
| 1678 | const cgltf_node* node = &data->nodes[n]; | ||
| 1593 | 1679 | ||
| 1594 | // If this is a top-level node in the glTF scene, set its parent to the | 1680 | // If this is a top-level node in the glTF scene, set its parent to the |
| 1595 | // given root node. | 1681 | // given root node. |
| @@ -1602,66 +1688,12 @@ static void load_nodes( | |||
| 1602 | assert(parent); | 1688 | assert(parent); |
| 1603 | gfx_set_node_parent(nodes[n], parent); | 1689 | gfx_set_node_parent(nodes[n], parent); |
| 1604 | } | 1690 | } |
| 1605 | } // SceneNode. | ||
| 1606 | } | ||
| 1607 | |||
| 1608 | /// Remove joint nodes from the Gfx Scene. | ||
| 1609 | /// | ||
| 1610 | /// Joint nodes are not needed because joints are packed into the Anima. | ||
| 1611 | static void remove_joint_nodes( | ||
| 1612 | const cgltf_data* data, SceneNode** scene_nodes) { | ||
| 1613 | assert(data); | ||
| 1614 | assert(scene_nodes); | ||
| 1615 | |||
| 1616 | // This works assuming the joint nodes are contiguous. Contiguity is checked | ||
| 1617 | // when loading skins. See load_skins(). | ||
| 1618 | size_t min_joint_index = (size_t)-1; | ||
| 1619 | size_t max_joint_index = 0; | ||
| 1620 | |||
| 1621 | // First get the minimum and maximum indices of all joint nodes. | ||
| 1622 | for (cgltf_size s = 0; s < data->skins_count; ++s) { | ||
| 1623 | const cgltf_skin* skin = &data->skins[s]; | ||
| 1624 | |||
| 1625 | for (cgltf_size j = 0; j < skin->joints_count; ++j) { | ||
| 1626 | // Joint is an index/pointer into the nodes array. | ||
| 1627 | const cgltf_size joint_index = skin->joints[j] - data->nodes; | ||
| 1628 | assert(joint_index < data->nodes_count); | ||
| 1629 | |||
| 1630 | if (joint_index < min_joint_index) { | ||
| 1631 | min_joint_index = joint_index; | ||
| 1632 | } | ||
| 1633 | if (joint_index > max_joint_index) { | ||
| 1634 | max_joint_index = joint_index; | ||
| 1635 | } | ||
| 1636 | } | ||
| 1637 | } | 1691 | } |
| 1638 | 1692 | ||
| 1639 | assert(min_joint_index < data->nodes_count); | 1693 | // Clean up scene nodes that correspond to joints in the glTF. These are not |
| 1640 | assert(max_joint_index < data->nodes_count); | 1694 | // needed anymore. |
| 1641 | 1695 | if (data->skins_count > 0) { | |
| 1642 | // Now walk over the joint nodes. If a joint's parent is itself not a joint | 1696 | remove_joint_nodes(data, nodes); |
| 1643 | // node, then that joint is a root of a joint hierarchy (skins in glTF may | ||
| 1644 | // have multiple roots). In such case, delete the root joint recursively. | ||
| 1645 | for (cgltf_size s = 0; s < data->skins_count; ++s) { | ||
| 1646 | const cgltf_skin* skin = &data->skins[s]; | ||
| 1647 | |||
| 1648 | for (cgltf_size j = 0; j < skin->joints_count; ++j) { | ||
| 1649 | // Joint is an index/pointer into the nodes array. | ||
| 1650 | const cgltf_size joint_index = skin->joints[j] - data->nodes; | ||
| 1651 | assert(joint_index < data->nodes_count); | ||
| 1652 | |||
| 1653 | const cgltf_node* joint = &data->nodes[joint_index]; | ||
| 1654 | |||
| 1655 | // Parent node index. | ||
| 1656 | const cgltf_size parent_index = joint->parent - data->nodes; | ||
| 1657 | assert(parent_index < data->nodes_count); | ||
| 1658 | |||
| 1659 | // If the parent is not a joint node, recursively delete this joint node. | ||
| 1660 | if ((parent_index < min_joint_index) || | ||
| 1661 | (parent_index > max_joint_index)) { | ||
| 1662 | gfx_destroy_node(&scene_nodes[joint_index]); | ||
| 1663 | } | ||
| 1664 | } | ||
| 1665 | } | 1697 | } |
| 1666 | } | 1698 | } |
| 1667 | 1699 | ||
| @@ -1705,42 +1737,21 @@ static Model* load_scene( | |||
| 1705 | LOGD("Filepath: %s", mstring_cstr(filepath)); | 1737 | LOGD("Filepath: %s", mstring_cstr(filepath)); |
| 1706 | LOGD("Directory: %s", mstring_cstr(&directory)); | 1738 | LOGD("Directory: %s", mstring_cstr(&directory)); |
| 1707 | 1739 | ||
| 1708 | Buffer** tangent_buffers = 0; | ||
| 1709 | Buffer** buffers = 0; | ||
| 1710 | LoadTextureCmd* load_texture_cmds = 0; | ||
| 1711 | const Texture** textures = 0; // Textures are owned by asset cache. | ||
| 1712 | Material** materials = 0; | ||
| 1713 | Geometry** geometries = 0; | ||
| 1714 | Mesh** meshes = 0; | ||
| 1715 | AnimaDesc* anima_desc = 0; | ||
| 1716 | SceneObject** scene_objects = 0; | ||
| 1717 | SceneCamera** scene_cameras = 0; | ||
| 1718 | SceneNode** scene_nodes = 0; | ||
| 1719 | Anima* anima = 0; | ||
| 1720 | SceneNode* root_node = 0; | ||
| 1721 | Model* model = 0; | ||
| 1722 | |||
| 1723 | tangent_buffers = calloc(num_tangent_buffers, sizeof(Buffer*)); | ||
| 1724 | buffers = calloc(data->buffers_count, sizeof(Buffer*)); | ||
| 1725 | textures = calloc(data->textures_count, sizeof(Texture*)); | ||
| 1726 | materials = calloc(data->materials_count, sizeof(Material*)); | ||
| 1727 | geometries = calloc(primitive_count, sizeof(Geometry*)); | ||
| 1728 | meshes = calloc(primitive_count, sizeof(Mesh*)); | ||
| 1729 | scene_objects = calloc(data->meshes_count, sizeof(SceneObject*)); | ||
| 1730 | scene_cameras = calloc(data->cameras_count, sizeof(SceneCamera**)); | ||
| 1731 | scene_nodes = calloc(data->nodes_count, sizeof(SceneNode**)); | ||
| 1732 | // A glTF scene does not necessarily have textures. Materials can be given | 1740 | // A glTF scene does not necessarily have textures. Materials can be given |
| 1733 | // as constants, for example. | 1741 | // as constants, for example. |
| 1734 | if (data->textures_count > 0) { | 1742 | // gfx textures are owned by asset cache. |
| 1735 | load_texture_cmds = calloc(data->textures_count, sizeof(LoadTextureCmd)); | 1743 | Buffer* tangent_buffers[num_tangent_buffers]; |
| 1736 | } | 1744 | Buffer* buffers[data->buffers_count]; |
| 1737 | 1745 | LoadTextureCmd load_texture_cmds[data->textures_count]; | |
| 1738 | if (!buffers || !tangent_buffers || | 1746 | const Texture* textures[data->textures_count]; |
| 1739 | ((data->textures_count > 0) && !load_texture_cmds) || !textures || | 1747 | Material* materials[data->materials_count]; |
| 1740 | !materials || !geometries || !meshes || !scene_objects || | 1748 | Geometry* geometries[primitive_count]; |
| 1741 | !scene_cameras || !scene_nodes) { | 1749 | Mesh* meshes[primitive_count]; |
| 1742 | goto cleanup; | 1750 | SceneObject* scene_objects[data->meshes_count]; |
| 1743 | } | 1751 | SceneNode* scene_nodes[data->nodes_count]; |
| 1752 | Anima* anima = 0; | ||
| 1753 | SceneNode* root_node = 0; | ||
| 1754 | Model* model = 0; | ||
| 1744 | 1755 | ||
| 1745 | if ((num_tangent_buffers > 0) && | 1756 | if ((num_tangent_buffers > 0) && |
| 1746 | !load_tangent_buffers( | 1757 | !load_tangent_buffers( |
| @@ -1769,47 +1780,30 @@ static Model* load_scene( | |||
| 1769 | goto cleanup; | 1780 | goto cleanup; |
| 1770 | } | 1781 | } |
| 1771 | 1782 | ||
| 1772 | // Skins refer to nodes, and nodes may refer to skins. To break this circular | ||
| 1773 | // dependency, glTF defines skins in terms of node indices. We could do the | ||
| 1774 | // same if Gfx allowed allocating nodes contiguously in memory. For now, | ||
| 1775 | // create the nodes up front and use the indices of the array to map to the | ||
| 1776 | // node_idx. | ||
| 1777 | for (cgltf_size i = 0; i < data->nodes_count; ++i) { | ||
| 1778 | scene_nodes[i] = gfx_make_node(); | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | // Create the scene's root node. | 1783 | // Create the scene's root node. |
| 1782 | // This is an anima node if the scene has skins; otherwise it is a logical | 1784 | // This is an anima node if the scene has skins; otherwise it is a logical |
| 1783 | // node. | 1785 | // node. |
| 1784 | root_node = gfx_make_node(); | ||
| 1785 | if (data->skins_count > 0) { | 1786 | if (data->skins_count > 0) { |
| 1786 | anima_desc = calloc(1, sizeof(AnimaDesc)); | ||
| 1787 | if (!anima_desc) { | ||
| 1788 | goto cleanup; | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | const cgltf_size base = find_base_joint_index(data); | 1787 | const cgltf_size base = find_base_joint_index(data); |
| 1792 | 1788 | ||
| 1793 | anima_desc->num_skeletons = data->skins_count; | 1789 | AnimaDesc anima_desc = |
| 1794 | anima_desc->num_animations = data->animations_count; | 1790 | (AnimaDesc){.num_skeletons = data->skins_count, |
| 1795 | anima_desc->num_joints = load_skins(data, buffers, base, anima_desc); | 1791 | .num_animations = data->animations_count, |
| 1796 | load_animations(data, base, anima_desc); | 1792 | .num_joints = load_skins(data, buffers, base, &anima_desc)}; |
| 1793 | load_animations(data, base, &anima_desc); | ||
| 1797 | 1794 | ||
| 1798 | compute_joint_bounding_boxes( | 1795 | compute_joint_bounding_boxes( |
| 1799 | data, anima_desc->num_joints, anima_desc->joints); | 1796 | data, anima_desc.num_joints, anima_desc.joints); |
| 1800 | 1797 | ||
| 1801 | anima = gfx_make_anima(anima_desc); | 1798 | anima = gfx_make_anima(&anima_desc); |
| 1802 | gfx_construct_anima_node(root_node, anima); | 1799 | root_node = gfx_make_anima_node(anima); |
| 1800 | } else { | ||
| 1801 | root_node = gfx_make_node(); | ||
| 1803 | } | 1802 | } |
| 1803 | assert(root_node); | ||
| 1804 | 1804 | ||
| 1805 | // The root node becomes the root of all scene nodes. | 1805 | // The root node becomes the root of all scene nodes. |
| 1806 | load_nodes(data, root_node, scene_objects, scene_cameras, anima, scene_nodes); | 1806 | load_nodes(data, root_node, scene_objects, anima, scene_nodes); |
| 1807 | |||
| 1808 | // Clean up scene nodes that correspond to joints in the glTF. These are | ||
| 1809 | // not needed anymore. | ||
| 1810 | if (data->skins_count > 0) { | ||
| 1811 | remove_joint_nodes(data, scene_nodes); | ||
| 1812 | } | ||
| 1813 | 1807 | ||
| 1814 | model = gfx_make_model(root_node); | 1808 | model = gfx_make_model(root_node); |
| 1815 | 1809 | ||
| @@ -1818,96 +1812,42 @@ static Model* load_scene( | |||
| 1818 | cleanup: | 1812 | cleanup: |
| 1819 | // The arrays of resources are no longer needed. The resources themselves are | 1813 | // The arrays of resources are no longer needed. The resources themselves are |
| 1820 | // destroyed only if this function fails. | 1814 | // destroyed only if this function fails. |
| 1821 | if (tangent_buffers) { | 1815 | if (!success) { |
| 1822 | if (!success) { | 1816 | for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { |
| 1823 | for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { | 1817 | if (tangent_buffers[i]) { |
| 1824 | if (tangent_buffers[i]) { | 1818 | gfx_destroy_buffer(gfxcore, &tangent_buffers[i]); |
| 1825 | gfx_destroy_buffer(gfxcore, &tangent_buffers[i]); | ||
| 1826 | } | ||
| 1827 | } | ||
| 1828 | } | ||
| 1829 | free(tangent_buffers); | ||
| 1830 | } | ||
| 1831 | if (buffers) { | ||
| 1832 | if (!success) { | ||
| 1833 | for (cgltf_size i = 0; i < data->buffers_count; ++i) { | ||
| 1834 | if (buffers[i]) { | ||
| 1835 | gfx_destroy_buffer(gfxcore, &buffers[i]); | ||
| 1836 | } | ||
| 1837 | } | 1819 | } |
| 1838 | } | 1820 | } |
| 1839 | free(buffers); | 1821 | for (cgltf_size i = 0; i < data->buffers_count; ++i) { |
| 1840 | } | 1822 | if (buffers[i]) { |
| 1841 | if (load_texture_cmds) { | 1823 | gfx_destroy_buffer(gfxcore, &buffers[i]); |
| 1842 | free(load_texture_cmds); | ||
| 1843 | } | ||
| 1844 | if (textures) { | ||
| 1845 | free(textures); | ||
| 1846 | } | ||
| 1847 | if (materials) { | ||
| 1848 | if (!success) { | ||
| 1849 | for (cgltf_size i = 0; i < data->materials_count; ++i) { | ||
| 1850 | if (materials[i]) { | ||
| 1851 | gfx_destroy_material(&materials[i]); | ||
| 1852 | } | ||
| 1853 | } | 1824 | } |
| 1854 | } | 1825 | } |
| 1855 | free(materials); | 1826 | for (cgltf_size i = 0; i < data->materials_count; ++i) { |
| 1856 | } | 1827 | if (materials[i]) { |
| 1857 | if (geometries) { | 1828 | gfx_destroy_material(&materials[i]); |
| 1858 | if (!success) { | ||
| 1859 | for (size_t i = 0; i < primitive_count; ++i) { | ||
| 1860 | if (geometries[i]) { | ||
| 1861 | gfx_destroy_geometry(gfxcore, &geometries[i]); | ||
| 1862 | } | ||
| 1863 | } | 1829 | } |
| 1864 | } | 1830 | } |
| 1865 | free(geometries); | 1831 | for (size_t i = 0; i < primitive_count; ++i) { |
| 1866 | } | 1832 | if (geometries[i]) { |
| 1867 | if (meshes) { | 1833 | gfx_destroy_geometry(gfxcore, &geometries[i]); |
| 1868 | if (!success) { | ||
| 1869 | for (size_t i = 0; i < primitive_count; ++i) { | ||
| 1870 | if (meshes[i]) { | ||
| 1871 | gfx_destroy_mesh(&meshes[i]); | ||
| 1872 | } | ||
| 1873 | } | 1834 | } |
| 1874 | } | 1835 | } |
| 1875 | free(meshes); | 1836 | for (size_t i = 0; i < primitive_count; ++i) { |
| 1876 | } | 1837 | if (meshes[i]) { |
| 1877 | if (anima_desc) { | 1838 | gfx_destroy_mesh(&meshes[i]); |
| 1878 | free(anima_desc); | ||
| 1879 | } | ||
| 1880 | if (scene_objects) { | ||
| 1881 | if (!success) { | ||
| 1882 | for (cgltf_size i = 0; i < data->meshes_count; ++i) { | ||
| 1883 | if (scene_objects[i]) { | ||
| 1884 | gfx_destroy_object(&scene_objects[i]); | ||
| 1885 | } | ||
| 1886 | } | 1839 | } |
| 1887 | } | 1840 | } |
| 1888 | free(scene_objects); | 1841 | for (cgltf_size i = 0; i < data->meshes_count; ++i) { |
| 1889 | } | 1842 | if (scene_objects[i]) { |
| 1890 | if (scene_cameras) { | 1843 | gfx_destroy_object(&scene_objects[i]); |
| 1891 | if (!success) { | ||
| 1892 | for (cgltf_size i = 0; i < data->cameras_count; ++i) { | ||
| 1893 | if (scene_cameras[i]) { | ||
| 1894 | gfx_destroy_camera(&scene_cameras[i]); | ||
| 1895 | } | ||
| 1896 | } | 1844 | } |
| 1897 | } | 1845 | } |
| 1898 | free(scene_cameras); | 1846 | for (cgltf_size i = 0; i < data->nodes_count; ++i) { |
| 1899 | } | 1847 | if (scene_nodes[i]) { |
| 1900 | if (scene_nodes) { | 1848 | gfx_destroy_node(&scene_nodes[i]); |
| 1901 | if (!success) { | ||
| 1902 | for (cgltf_size i = 0; i < data->nodes_count; ++i) { | ||
| 1903 | if (scene_nodes[i]) { | ||
| 1904 | gfx_destroy_node(&scene_nodes[i]); | ||
| 1905 | } | ||
| 1906 | } | 1849 | } |
| 1907 | } | 1850 | } |
| 1908 | free(scene_nodes); | ||
| 1909 | } | ||
| 1910 | if (!success) { | ||
| 1911 | if (root_node) { | 1851 | if (root_node) { |
| 1912 | gfx_destroy_node(&root_node); // Node owns the anima. | 1852 | gfx_destroy_node(&root_node); // Node owns the anima. |
| 1913 | } else if (anima) { | 1853 | } else if (anima) { |
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..1df225a 100644 --- a/gfx/src/core/buffer.h +++ b/src/core/buffer.h | |||
| @@ -4,8 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include "gl_util.h" | 5 | #include "gl_util.h" |
| 6 | 6 | ||
| 7 | #include <math/fwd.h> | ||
| 8 | |||
| 9 | #include <stdbool.h> | 7 | #include <stdbool.h> |
| 10 | #include <stddef.h> | 8 | #include <stddef.h> |
| 11 | 9 | ||
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..9c04cc7 100644 --- a/gfx/src/core/core.c +++ b/src/core/core.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include "gl_util.h" | 3 | #include "gl_util.h" |
| 4 | 4 | ||
| 5 | // #include <log/log.h> | 5 | // #include <log/log.h> |
| 6 | #include <fnv1a.h> | ||
| 6 | 7 | ||
| 7 | #include <assert.h> | 8 | #include <assert.h> |
| 8 | 9 | ||
| @@ -282,26 +283,29 @@ void gfx_destroy_framebuffer(GfxCore* gfxcore, FrameBuffer** framebuffer) { | |||
| 282 | // Shaders. | 283 | // Shaders. |
| 283 | // ----------------------------------------------------------------------------- | 284 | // ----------------------------------------------------------------------------- |
| 284 | 285 | ||
| 285 | static uint64_t hash_shader_desc(const ShaderDesc* desc) { | 286 | static hash_t hash_shader_desc(const ShaderDesc* desc) { |
| 286 | assert(desc); | 287 | assert(desc); |
| 287 | // Note that defines may affect shader permutations, so we need to hash those | 288 | // Defines may affect shader permutations, so we need to hash those as well. |
| 288 | // as well. | 289 | hash_t hash = fnv1a32_begin(); |
| 289 | uint64_t hash = 0; | 290 | hash = fnv1a32_update(hash, desc->code, strlen(desc->code)); |
| 290 | for (size_t i = 0; i < desc->num_defines; ++i) { | 291 | for (size_t i = 0; i < desc->num_defines; ++i) { |
| 291 | const ShaderCompilerDefine* define = &desc->defines[i]; | 292 | const ShaderCompilerDefine* define = &desc->defines[i]; |
| 292 | hash = (((hash << 13) + sstring_hash(define->name)) << 7) + | 293 | |
| 293 | sstring_hash(define->value); | 294 | hash = fnv1a32_update( |
| 295 | hash, sstring_cstr(&define->name), sstring_length(&define->name)); | ||
| 296 | hash = fnv1a32_update( | ||
| 297 | hash, sstring_cstr(&define->value), sstring_length(&define->value)); | ||
| 294 | } | 298 | } |
| 295 | return (hash << 17) + cstring_hash(desc->code); | 299 | return hash; |
| 296 | } | 300 | } |
| 297 | 301 | ||
| 298 | static uint64_t hash_program_desc(const ShaderProgramDesc* desc) { | 302 | static hash_t hash_program_desc(const ShaderProgramDesc* desc) { |
| 299 | assert(desc); | 303 | assert(desc); |
| 300 | return ((uint64_t)desc->vertex_shader->id << 32) | | 304 | return ((hash_t)desc->vertex_shader->id << 16) | |
| 301 | (uint64_t)desc->fragment_shader->id; | 305 | (hash_t)desc->fragment_shader->id; |
| 302 | } | 306 | } |
| 303 | 307 | ||
| 304 | static Shader* find_cached_shader(ShaderCache* cache, uint64_t hash) { | 308 | static Shader* find_cached_shader(ShaderCache* cache, hash_t hash) { |
| 305 | assert(cache); | 309 | assert(cache); |
| 306 | mempool_foreach(cache, entry, { | 310 | mempool_foreach(cache, entry, { |
| 307 | if (entry->hash == hash) { | 311 | if (entry->hash == hash) { |
| @@ -311,7 +315,7 @@ static Shader* find_cached_shader(ShaderCache* cache, uint64_t hash) { | |||
| 311 | return 0; | 315 | return 0; |
| 312 | } | 316 | } |
| 313 | 317 | ||
| 314 | static ShaderProgram* find_cached_program(ProgramCache* cache, uint64_t hash) { | 318 | static ShaderProgram* find_cached_program(ProgramCache* cache, hash_t hash) { |
| 315 | assert(cache); | 319 | assert(cache); |
| 316 | mempool_foreach(cache, entry, { | 320 | mempool_foreach(cache, entry, { |
| 317 | if (entry->hash == hash) { | 321 | if (entry->hash == hash) { |
| @@ -350,9 +354,9 @@ Shader* gfx_make_shader(GfxCore* gfxcore, const ShaderDesc* desc) { | |||
| 350 | assert(desc); | 354 | assert(desc); |
| 351 | 355 | ||
| 352 | // Check the shader cache first. | 356 | // Check the shader cache first. |
| 353 | ShaderCache* cache = &gfxcore->shader_cache; | 357 | ShaderCache* cache = &gfxcore->shader_cache; |
| 354 | const uint64_t hash = hash_shader_desc(desc); | 358 | const hash_t hash = hash_shader_desc(desc); |
| 355 | Shader* shader = find_cached_shader(cache, hash); | 359 | Shader* shader = find_cached_shader(cache, hash); |
| 356 | if (shader) { | 360 | if (shader) { |
| 357 | // LOGD("Found cached shader with hash [%lx]", hash); | 361 | // LOGD("Found cached shader with hash [%lx]", hash); |
| 358 | return shader; | 362 | return shader; |
| @@ -395,7 +399,7 @@ ShaderProgram* gfx_make_shader_program( | |||
| 395 | 399 | ||
| 396 | // Check the shader program cache first. | 400 | // Check the shader program cache first. |
| 397 | ProgramCache* cache = &gfxcore->program_cache; | 401 | ProgramCache* cache = &gfxcore->program_cache; |
| 398 | const uint64_t hash = hash_program_desc(desc); | 402 | const hash_t hash = hash_program_desc(desc); |
| 399 | ShaderProgram* prog = find_cached_program(cache, hash); | 403 | ShaderProgram* prog = find_cached_program(cache, hash); |
| 400 | if (prog) { | 404 | if (prog) { |
| 401 | // LOGD("Found cached shader program with hash [%lx]", hash); | 405 | // LOGD("Found cached shader program with hash [%lx]", hash); |
| @@ -420,6 +424,10 @@ void gfx_destroy_shader_program(GfxCore* gfxcore, ShaderProgram** prog) { | |||
| 420 | // Remove the shader program from the cache. | 424 | // Remove the shader program from the cache. |
| 421 | ProgramCache* cache = &gfxcore->program_cache; | 425 | ProgramCache* cache = &gfxcore->program_cache; |
| 422 | ShaderProgramCacheEntry* entry = find_program_cache_entry(cache, *prog); | 426 | ShaderProgramCacheEntry* entry = find_program_cache_entry(cache, *prog); |
| 427 | // TODO: The following assertion is too restrictive. Clients can end up | ||
| 428 | // re-using the same shader by virtue of the cache. The assertion assumes | ||
| 429 | // that no two "different" clients use the same set of shaders. This can | ||
| 430 | // be relaxed by reference-counting the shaders in the cache. | ||
| 423 | assert(entry); // Must be there, shaders can't go untracked. | 431 | assert(entry); // Must be there, shaders can't go untracked. |
| 424 | mempool_free(cache, &entry); | 432 | mempool_free(cache, &entry); |
| 425 | 433 | ||
diff --git a/gfx/src/core/core_impl.h b/src/core/core_impl.h index eefdfbe..320532d 100644 --- a/gfx/src/core/core_impl.h +++ b/src/core/core_impl.h | |||
| @@ -15,16 +15,18 @@ | |||
| 15 | 15 | ||
| 16 | #include <stdint.h> | 16 | #include <stdint.h> |
| 17 | 17 | ||
| 18 | typedef uint32_t hash_t; | ||
| 19 | |||
| 18 | // TODO: Make a generic (hash, void*) structure and define functions over it. | 20 | // TODO: Make a generic (hash, void*) structure and define functions over it. |
| 19 | // Then define a macro that defines type-safe macros given the type of the | 21 | // Then define a macro that defines type-safe macros given the type of the |
| 20 | // entry. | 22 | // entry. |
| 21 | typedef struct ShaderCacheEntry { | 23 | typedef struct ShaderCacheEntry { |
| 22 | uint64_t hash; | 24 | hash_t hash; |
| 23 | Shader* shader; | 25 | Shader* shader; |
| 24 | } ShaderCacheEntry; | 26 | } ShaderCacheEntry; |
| 25 | 27 | ||
| 26 | typedef struct ShaderProgramCacheEntry { | 28 | typedef struct ShaderProgramCacheEntry { |
| 27 | uint64_t hash; | 29 | hash_t hash; |
| 28 | ShaderProgram* program; | 30 | ShaderProgram* program; |
| 29 | } ShaderProgramCacheEntry; | 31 | } ShaderProgramCacheEntry; |
| 30 | 32 | ||
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..488dc23 100644 --- a/gfx/src/core/geometry.c +++ b/src/core/geometry.c | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | /// | 12 | /// |
| 13 | /// Note that views are allowed to have no data, in which case a buffer of the | 13 | /// Note that views are allowed to have no data, in which case a buffer of the |
| 14 | /// specified size is created. | 14 | /// specified size is created. |
| 15 | #define view_is_populated(BUFFER_VIEW) (BUFFER_VIEW.size_bytes > 0) | 15 | #define view_is_populated(BUFFER_VIEW) (BUFFER_VIEW.count > 0) |
| 16 | 16 | ||
| 17 | static GLenum primitive_type_to_gl(PrimitiveType type) { | 17 | static GLenum primitive_type_to_gl(PrimitiveType type) { |
| 18 | switch (type) { | 18 | switch (type) { |
| @@ -34,30 +34,25 @@ void init_view_buffer( | |||
| 34 | BufferUsage buffer_usage) { | 34 | BufferUsage buffer_usage) { |
| 35 | if (!view->buffer) { | 35 | if (!view->buffer) { |
| 36 | view->buffer = gfx_make_buffer( | 36 | view->buffer = gfx_make_buffer( |
| 37 | gfxcore, | 37 | gfxcore, &(BufferDesc){.usage = buffer_usage, |
| 38 | &(BufferDesc){ | 38 | .type = buffer_type, |
| 39 | .usage = buffer_usage, | 39 | .data.data = view->data, |
| 40 | .type = buffer_type, | 40 | .data.count = view->count}); |
| 41 | .data.data = view->data, | 41 | assert(view->buffer); |
| 42 | .data.count = view->size_bytes / | ||
| 43 | gfx_get_buffer_type_size_bytes(buffer_type)}); | ||
| 44 | } | 42 | } |
| 45 | assert(view->size_bytes <= view->buffer->size_bytes); | ||
| 46 | } | 43 | } |
| 47 | 44 | ||
| 48 | /// Configure the buffer in teh VAO. | 45 | /// Configure the buffer in the VAO. |
| 49 | static void configure_buffer( | 46 | static void configure_buffer( |
| 50 | GfxCore* gfxcore, const GeometryDesc* desc, BufferView* view, | 47 | GfxCore* gfxcore, const GeometryDesc* desc, BufferView* view, |
| 51 | size_t num_components, size_t component_size_bytes, GLenum component_type, | 48 | size_t num_components, GLenum component_type, GLboolean normalized, |
| 52 | GLboolean normalized, GLuint channel) { | 49 | GLuint channel) { |
| 53 | assert(gfxcore); | 50 | assert(gfxcore); |
| 54 | assert(desc); | 51 | assert(desc); |
| 55 | assert(view); | 52 | assert(view); |
| 56 | assert(view->buffer); | 53 | assert(view->buffer); |
| 57 | assert( | 54 | assert(view->count == desc->num_verts); |
| 58 | desc->num_verts <= | 55 | assert((view->offset_bytes + view->size_bytes) <= view->buffer->size_bytes); |
| 59 | view->size_bytes / (num_components * component_size_bytes)); | ||
| 60 | assert(view->size_bytes <= view->buffer->size_bytes); | ||
| 61 | 56 | ||
| 62 | glBindBuffer(GL_ARRAY_BUFFER, view->buffer->vbo); | 57 | glBindBuffer(GL_ARRAY_BUFFER, view->buffer->vbo); |
| 63 | glEnableVertexAttribArray(channel); | 58 | glEnableVertexAttribArray(channel); |
| @@ -89,8 +84,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 89 | return false; | 84 | return false; |
| 90 | } | 85 | } |
| 91 | configure_buffer( | 86 | configure_buffer( |
| 92 | gfxcore, desc, (BufferView*)&desc->positions3d, 3, sizeof(float), | 87 | gfxcore, desc, (BufferView*)&desc->positions3d, 3, GL_FLOAT, GL_FALSE, |
| 93 | GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); | 88 | GFX_POSITION_CHANNEL); |
| 94 | } else if (view_is_populated(desc->positions2d)) { | 89 | } else if (view_is_populated(desc->positions2d)) { |
| 95 | init_view_buffer( | 90 | init_view_buffer( |
| 96 | gfxcore, (BufferView*)&desc->positions2d, Buffer2d, desc->buffer_usage); | 91 | gfxcore, (BufferView*)&desc->positions2d, Buffer2d, desc->buffer_usage); |
| @@ -98,8 +93,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 98 | return false; | 93 | return false; |
| 99 | } | 94 | } |
| 100 | configure_buffer( | 95 | configure_buffer( |
| 101 | gfxcore, desc, (BufferView*)&desc->positions2d, 2, sizeof(float), | 96 | gfxcore, desc, (BufferView*)&desc->positions2d, 2, GL_FLOAT, GL_FALSE, |
| 102 | GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); | 97 | GFX_POSITION_CHANNEL); |
| 103 | } | 98 | } |
| 104 | if (view_is_populated(desc->normals)) { | 99 | if (view_is_populated(desc->normals)) { |
| 105 | init_view_buffer( | 100 | init_view_buffer( |
| @@ -108,8 +103,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 108 | return false; | 103 | return false; |
| 109 | } | 104 | } |
| 110 | configure_buffer( | 105 | configure_buffer( |
| 111 | gfxcore, desc, (BufferView*)&desc->normals, 3, sizeof(float), GL_FLOAT, | 106 | gfxcore, desc, (BufferView*)&desc->normals, 3, GL_FLOAT, GL_FALSE, |
| 112 | GL_FALSE, GFX_NORMAL_CHANNEL); | 107 | GFX_NORMAL_CHANNEL); |
| 113 | } | 108 | } |
| 114 | if (view_is_populated(desc->tangents)) { | 109 | if (view_is_populated(desc->tangents)) { |
| 115 | init_view_buffer( | 110 | init_view_buffer( |
| @@ -118,8 +113,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 118 | return false; | 113 | return false; |
| 119 | } | 114 | } |
| 120 | configure_buffer( | 115 | configure_buffer( |
| 121 | gfxcore, desc, (BufferView*)&desc->tangents, 4, sizeof(float), GL_FLOAT, | 116 | gfxcore, desc, (BufferView*)&desc->tangents, 4, GL_FLOAT, GL_FALSE, |
| 122 | GL_FALSE, GFX_TANGENT_CHANNEL); | 117 | GFX_TANGENT_CHANNEL); |
| 123 | } | 118 | } |
| 124 | if (view_is_populated(desc->texcoords)) { | 119 | if (view_is_populated(desc->texcoords)) { |
| 125 | init_view_buffer( | 120 | init_view_buffer( |
| @@ -128,8 +123,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 128 | return false; | 123 | return false; |
| 129 | } | 124 | } |
| 130 | configure_buffer( | 125 | configure_buffer( |
| 131 | gfxcore, desc, (BufferView*)&desc->texcoords, 2, sizeof(float), | 126 | gfxcore, desc, (BufferView*)&desc->texcoords, 2, GL_FLOAT, GL_FALSE, |
| 132 | GL_FLOAT, GL_FALSE, GFX_TEXCOORDS_CHANNEL); | 127 | GFX_TEXCOORDS_CHANNEL); |
| 133 | } | 128 | } |
| 134 | if (view_is_populated(desc->joints.u8)) { | 129 | if (view_is_populated(desc->joints.u8)) { |
| 135 | init_view_buffer( | 130 | init_view_buffer( |
| @@ -138,8 +133,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 138 | return false; | 133 | return false; |
| 139 | } | 134 | } |
| 140 | configure_buffer( | 135 | configure_buffer( |
| 141 | gfxcore, desc, (BufferView*)&desc->joints.u8, 4, sizeof(uint8_t), | 136 | gfxcore, desc, (BufferView*)&desc->joints.u8, 4, GL_UNSIGNED_BYTE, |
| 142 | GL_UNSIGNED_BYTE, GL_FALSE, GFX_JOINTS_CHANNEL); | 137 | GL_FALSE, GFX_JOINTS_CHANNEL); |
| 143 | } else if (view_is_populated(desc->joints.u16)) { | 138 | } else if (view_is_populated(desc->joints.u16)) { |
| 144 | init_view_buffer( | 139 | init_view_buffer( |
| 145 | gfxcore, (BufferView*)&desc->joints.u16, BufferU16, desc->buffer_usage); | 140 | gfxcore, (BufferView*)&desc->joints.u16, BufferU16, desc->buffer_usage); |
| @@ -147,8 +142,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 147 | return false; | 142 | return false; |
| 148 | } | 143 | } |
| 149 | configure_buffer( | 144 | configure_buffer( |
| 150 | gfxcore, desc, (BufferView*)&desc->joints.u16, 4, sizeof(uint16_t), | 145 | gfxcore, desc, (BufferView*)&desc->joints.u16, 4, GL_UNSIGNED_SHORT, |
| 151 | GL_UNSIGNED_SHORT, GL_FALSE, GFX_JOINTS_CHANNEL); | 146 | GL_FALSE, GFX_JOINTS_CHANNEL); |
| 152 | } | 147 | } |
| 153 | 148 | ||
| 154 | // If weights are given as unsigned integers, then they are normalized | 149 | // If weights are given as unsigned integers, then they are normalized |
| @@ -160,8 +155,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 160 | return false; | 155 | return false; |
| 161 | } | 156 | } |
| 162 | configure_buffer( | 157 | configure_buffer( |
| 163 | gfxcore, desc, (BufferView*)&desc->weights.u8, 4, sizeof(uint8_t), | 158 | gfxcore, desc, (BufferView*)&desc->weights.u8, 4, GL_UNSIGNED_BYTE, |
| 164 | GL_UNSIGNED_BYTE, GL_TRUE, GFX_WEIGHTS_CHANNEL); | 159 | GL_TRUE, GFX_WEIGHTS_CHANNEL); |
| 165 | } else if (view_is_populated(desc->weights.u16)) { | 160 | } else if (view_is_populated(desc->weights.u16)) { |
| 166 | init_view_buffer( | 161 | init_view_buffer( |
| 167 | gfxcore, (BufferView*)&desc->weights.u16, BufferU16, | 162 | gfxcore, (BufferView*)&desc->weights.u16, BufferU16, |
| @@ -170,8 +165,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 170 | return false; | 165 | return false; |
| 171 | } | 166 | } |
| 172 | configure_buffer( | 167 | configure_buffer( |
| 173 | gfxcore, desc, (BufferView*)&desc->weights.u16, 4, sizeof(uint16_t), | 168 | gfxcore, desc, (BufferView*)&desc->weights.u16, 4, GL_UNSIGNED_SHORT, |
| 174 | GL_UNSIGNED_SHORT, GL_TRUE, GFX_WEIGHTS_CHANNEL); | 169 | GL_TRUE, GFX_WEIGHTS_CHANNEL); |
| 175 | } else if (view_is_populated(desc->weights.floats)) { | 170 | } else if (view_is_populated(desc->weights.floats)) { |
| 176 | init_view_buffer( | 171 | init_view_buffer( |
| 177 | gfxcore, (BufferView*)&desc->weights.floats, BufferFloat, | 172 | gfxcore, (BufferView*)&desc->weights.floats, BufferFloat, |
| @@ -180,8 +175,8 @@ static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | |||
| 180 | return false; | 175 | return false; |
| 181 | } | 176 | } |
| 182 | configure_buffer( | 177 | configure_buffer( |
| 183 | gfxcore, desc, (BufferView*)&desc->weights.floats, 4, sizeof(float), | 178 | gfxcore, desc, (BufferView*)&desc->weights.floats, 4, GL_FLOAT, |
| 184 | GL_FLOAT, GL_FALSE, GFX_WEIGHTS_CHANNEL); | 179 | GL_FALSE, GFX_WEIGHTS_CHANNEL); |
| 185 | } | 180 | } |
| 186 | 181 | ||
| 187 | return true; | 182 | return true; |
| @@ -282,9 +277,9 @@ void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { | |||
| 282 | assert(geometry->desc.positions3d.buffer); | 277 | assert(geometry->desc.positions3d.buffer); |
| 283 | gfx_update_buffer( | 278 | gfx_update_buffer( |
| 284 | geometry->desc.positions3d.buffer, | 279 | geometry->desc.positions3d.buffer, |
| 285 | &(BufferDataDesc){ | 280 | &(BufferDataDesc){.vec3s = desc->positions3d.data, |
| 286 | .vec3s = desc->positions3d.data, | 281 | .count = |
| 287 | .count = desc->positions3d.size_bytes / sizeof(vec3)}); | 282 | desc->positions3d.size_bytes / sizeof(vec3)}); |
| 288 | } | 283 | } |
| 289 | // TODO: more | 284 | // TODO: more |
| 290 | else { | 285 | else { |
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..3840019 100644 --- a/gfx/src/core/shader_program.c +++ b/src/core/shader_program.c | |||
| @@ -72,17 +72,23 @@ void gfx_deactivate_shader_program(const ShaderProgram* prog) { | |||
| 72 | ASSERT_GL; | 72 | ASSERT_GL; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | static void set_texture_uniform( | 75 | static void set_int_uniform(GLuint prog, const char* name, int value) { |
| 76 | GLuint prog, const char* name, int texture_unit, const Texture* texture) { | ||
| 77 | assert(prog != 0); | 76 | assert(prog != 0); |
| 78 | assert(name); | 77 | assert(name); |
| 79 | assert(texture); | ||
| 80 | 78 | ||
| 81 | const GLint location = glGetUniformLocation(prog, name); | 79 | const GLint location = glGetUniformLocation(prog, name); |
| 82 | if (location >= 0) { | 80 | if (location >= 0) { |
| 83 | glActiveTexture(GL_TEXTURE0 + texture_unit); | 81 | glUniform1i(location, value); |
| 84 | glBindTexture(texture->target, texture->id); | 82 | } |
| 85 | glUniform1i(location, texture_unit); | 83 | } |
| 84 | |||
| 85 | static void set_float_uniform(GLuint prog, const char* name, float value) { | ||
| 86 | assert(prog != 0); | ||
| 87 | assert(name); | ||
| 88 | |||
| 89 | const GLint location = glGetUniformLocation(prog, name); | ||
| 90 | if (location >= 0) { | ||
| 91 | glUniform1f(location, value); | ||
| 86 | } | 92 | } |
| 87 | } | 93 | } |
| 88 | 94 | ||
| @@ -118,13 +124,17 @@ static void set_vec4_uniform(GLuint prog, const char* name, vec4 value) { | |||
| 118 | } | 124 | } |
| 119 | } | 125 | } |
| 120 | 126 | ||
| 121 | static void set_float_uniform(GLuint prog, const char* name, float value) { | 127 | static void set_texture_uniform( |
| 128 | GLuint prog, const char* name, int texture_unit, const Texture* texture) { | ||
| 122 | assert(prog != 0); | 129 | assert(prog != 0); |
| 123 | assert(name); | 130 | assert(name); |
| 131 | assert(texture); | ||
| 124 | 132 | ||
| 125 | const GLint location = glGetUniformLocation(prog, name); | 133 | const GLint location = glGetUniformLocation(prog, name); |
| 126 | if (location >= 0) { | 134 | if (location >= 0) { |
| 127 | glUniform1f(location, value); | 135 | glActiveTexture(GL_TEXTURE0 + texture_unit); |
| 136 | glBindTexture(texture->target, texture->id); | ||
| 137 | glUniform1i(location, texture_unit); | ||
| 128 | } | 138 | } |
| 129 | } | 139 | } |
| 130 | 140 | ||
| @@ -135,23 +145,30 @@ void gfx_apply_uniforms(const ShaderProgram* prog) { | |||
| 135 | for (int i = 0; i < prog->num_uniforms; ++i) { | 145 | for (int i = 0; i < prog->num_uniforms; ++i) { |
| 136 | const ShaderUniform* uniform = &prog->uniforms[i]; | 146 | const ShaderUniform* uniform = &prog->uniforms[i]; |
| 137 | switch (uniform->type) { | 147 | switch (uniform->type) { |
| 138 | case UniformTexture: | 148 | case UniformInt: |
| 139 | set_texture_uniform( | 149 | set_int_uniform(prog->id, uniform->name.str, uniform->value.uniform_int); |
| 140 | prog->id, uniform->name.str, next_texture_unit, | 150 | break; |
| 141 | uniform->value.texture); | 151 | case UniformFloat: |
| 142 | next_texture_unit++; | 152 | set_float_uniform( |
| 153 | prog->id, uniform->name.str, uniform->value.uniform_float); | ||
| 143 | break; | 154 | break; |
| 144 | case UniformMat4: | 155 | case UniformMat4: |
| 145 | set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4, 1); | 156 | set_mat4_uniform( |
| 157 | prog->id, uniform->name.str, &uniform->value.uniform_mat4, 1); | ||
| 146 | break; | 158 | break; |
| 147 | case UniformVec3: | 159 | case UniformVec3: |
| 148 | set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3); | 160 | set_vec3_uniform( |
| 161 | prog->id, uniform->name.str, uniform->value.uniform_vec3); | ||
| 149 | break; | 162 | break; |
| 150 | case UniformVec4: | 163 | case UniformVec4: |
| 151 | set_vec4_uniform(prog->id, uniform->name.str, uniform->value.vec4); | 164 | set_vec4_uniform( |
| 165 | prog->id, uniform->name.str, uniform->value.uniform_vec4); | ||
| 152 | break; | 166 | break; |
| 153 | case UniformFloat: | 167 | case UniformTexture: |
| 154 | set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar); | 168 | set_texture_uniform( |
| 169 | prog->id, uniform->name.str, next_texture_unit, | ||
| 170 | uniform->value.texture); | ||
| 171 | next_texture_unit++; | ||
| 155 | break; | 172 | break; |
| 156 | case UniformMat4Array: | 173 | case UniformMat4Array: |
| 157 | set_mat4_uniform( | 174 | set_mat4_uniform( |
| @@ -179,8 +196,9 @@ static ShaderUniform* get_or_allocate_uniform( | |||
| 179 | 196 | ||
| 180 | // Create the uniform if it does not exist. | 197 | // Create the uniform if it does not exist. |
| 181 | if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) { | 198 | if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) { |
| 182 | FAIL("Exceeded the maximum number of uniforms per shader. Please increase " | 199 | FAIL( |
| 183 | "this value."); | 200 | "Exceeded the maximum number of uniforms per shader. Please increase " |
| 201 | "this value."); | ||
| 184 | return 0; | 202 | return 0; |
| 185 | } | 203 | } |
| 186 | ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms]; | 204 | ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms]; |
| @@ -191,21 +209,67 @@ static ShaderUniform* get_or_allocate_uniform( | |||
| 191 | // The functions below save the value of a uniform in the shader program. If the | 209 | // The functions below save the value of a uniform in the shader program. If the |
| 192 | // uniform does not even exist, then there is no need to store the value. | 210 | // uniform does not even exist, then there is no need to store the value. |
| 193 | 211 | ||
| 194 | void gfx_set_texture_uniform( | 212 | void gfx_set_uniform(ShaderProgram* prog, const ShaderUniform* uniform) { |
| 195 | ShaderProgram* prog, const char* name, const Texture* texture) { | 213 | switch (uniform->type) { |
| 214 | case UniformInt: | ||
| 215 | gfx_set_int_uniform(prog, uniform->name.str, uniform->value.uniform_int); | ||
| 216 | break; | ||
| 217 | case UniformFloat: | ||
| 218 | gfx_set_float_uniform( | ||
| 219 | prog, uniform->name.str, uniform->value.uniform_float); | ||
| 220 | break; | ||
| 221 | case UniformMat4: | ||
| 222 | gfx_set_mat4_uniform(prog, uniform->name.str, &uniform->value.uniform_mat4); | ||
| 223 | break; | ||
| 224 | case UniformVec3: | ||
| 225 | gfx_set_vec3_uniform(prog, uniform->name.str, uniform->value.uniform_vec3); | ||
| 226 | break; | ||
| 227 | case UniformVec4: | ||
| 228 | gfx_set_vec4_uniform(prog, uniform->name.str, uniform->value.uniform_vec4); | ||
| 229 | break; | ||
| 230 | case UniformTexture: | ||
| 231 | gfx_set_texture_uniform(prog, uniform->name.str, uniform->value.texture); | ||
| 232 | break; | ||
| 233 | case UniformMat4Array: | ||
| 234 | gfx_set_mat4_array_uniform( | ||
| 235 | prog, uniform->name.str, uniform->value.array.values, | ||
| 236 | uniform->value.array.count); | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | void gfx_set_int_uniform(ShaderProgram* prog, const char* name, int value) { | ||
| 196 | assert(prog); | 242 | assert(prog); |
| 197 | assert(name); | 243 | assert(name); |
| 198 | assert(texture); | ||
| 199 | 244 | ||
| 245 | // No need to store the uniform on our side if it does not exist in the | ||
| 246 | // program. | ||
| 200 | const GLint location = glGetUniformLocation(prog->id, name); | 247 | const GLint location = glGetUniformLocation(prog->id, name); |
| 201 | if (location < 0) { | 248 | if (location < 0) { |
| 202 | return; | 249 | return; |
| 203 | } | 250 | } |
| 204 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | 251 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); |
| 205 | assert(uniform); | 252 | assert(uniform); |
| 206 | uniform->name = sstring_make(name); | 253 | uniform->name = sstring_make(name); |
| 207 | uniform->type = UniformTexture; | 254 | uniform->type = UniformInt; |
| 208 | uniform->value.texture = texture; | 255 | uniform->value.uniform_int = value; |
| 256 | } | ||
| 257 | |||
| 258 | void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) { | ||
| 259 | assert(prog); | ||
| 260 | assert(name); | ||
| 261 | |||
| 262 | // No need to store the uniform on our side if it does not exist in the | ||
| 263 | // program. | ||
| 264 | const GLint location = glGetUniformLocation(prog->id, name); | ||
| 265 | if (location < 0) { | ||
| 266 | return; | ||
| 267 | } | ||
| 268 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | ||
| 269 | assert(uniform); | ||
| 270 | uniform->name = sstring_make(name); | ||
| 271 | uniform->type = UniformFloat; | ||
| 272 | uniform->value.uniform_float = value; | ||
| 209 | } | 273 | } |
| 210 | 274 | ||
| 211 | void gfx_set_mat4_uniform( | 275 | void gfx_set_mat4_uniform( |
| @@ -220,9 +284,9 @@ void gfx_set_mat4_uniform( | |||
| 220 | } | 284 | } |
| 221 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | 285 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); |
| 222 | assert(uniform); | 286 | assert(uniform); |
| 223 | uniform->name = sstring_make(name); | 287 | uniform->name = sstring_make(name); |
| 224 | uniform->type = UniformMat4; | 288 | uniform->type = UniformMat4; |
| 225 | uniform->value.mat4 = *mat; | 289 | uniform->value.uniform_mat4 = *mat; |
| 226 | } | 290 | } |
| 227 | 291 | ||
| 228 | void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { | 292 | void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { |
| @@ -235,9 +299,9 @@ void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { | |||
| 235 | } | 299 | } |
| 236 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | 300 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); |
| 237 | assert(uniform); | 301 | assert(uniform); |
| 238 | uniform->name = sstring_make(name); | 302 | uniform->name = sstring_make(name); |
| 239 | uniform->type = UniformVec3; | 303 | uniform->type = UniformVec3; |
| 240 | uniform->value.vec3 = value; | 304 | uniform->value.uniform_vec3 = value; |
| 241 | } | 305 | } |
| 242 | 306 | ||
| 243 | void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { | 307 | void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { |
| @@ -250,26 +314,26 @@ void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { | |||
| 250 | } | 314 | } |
| 251 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | 315 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); |
| 252 | assert(uniform); | 316 | assert(uniform); |
| 253 | uniform->name = sstring_make(name); | 317 | uniform->name = sstring_make(name); |
| 254 | uniform->type = UniformVec4; | 318 | uniform->type = UniformVec4; |
| 255 | uniform->value.vec4 = value; | 319 | uniform->value.uniform_vec4 = value; |
| 256 | } | 320 | } |
| 257 | 321 | ||
| 258 | void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) { | 322 | void gfx_set_texture_uniform( |
| 323 | ShaderProgram* prog, const char* name, const Texture* texture) { | ||
| 259 | assert(prog); | 324 | assert(prog); |
| 260 | assert(name); | 325 | assert(name); |
| 326 | assert(texture); | ||
| 261 | 327 | ||
| 262 | // No need to store the uniform on our side if it does not exist in the | ||
| 263 | // program. | ||
| 264 | const GLint location = glGetUniformLocation(prog->id, name); | 328 | const GLint location = glGetUniformLocation(prog->id, name); |
| 265 | if (location < 0) { | 329 | if (location < 0) { |
| 266 | return; | 330 | return; |
| 267 | } | 331 | } |
| 268 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | 332 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); |
| 269 | assert(uniform); | 333 | assert(uniform); |
| 270 | uniform->name = sstring_make(name); | 334 | uniform->name = sstring_make(name); |
| 271 | uniform->type = UniformFloat; | 335 | uniform->type = UniformTexture; |
| 272 | uniform->value.scalar = value; | 336 | uniform->value.texture = texture; |
| 273 | } | 337 | } |
| 274 | 338 | ||
| 275 | void gfx_set_mat4_array_uniform( | 339 | void gfx_set_mat4_array_uniform( |
| @@ -277,7 +341,7 @@ void gfx_set_mat4_array_uniform( | |||
| 277 | assert(prog); | 341 | assert(prog); |
| 278 | assert(name); | 342 | assert(name); |
| 279 | assert(mats); | 343 | assert(mats); |
| 280 | 344 | ||
| 281 | const GLint location = glGetUniformLocation(prog->id, name); | 345 | const GLint location = glGetUniformLocation(prog->id, name); |
| 282 | if (location < 0) { | 346 | if (location < 0) { |
| 283 | return; | 347 | return; |
diff --git a/gfx/src/core/shader_program.h b/src/core/shader_program.h index 1443663..521118d 100644 --- a/gfx/src/core/shader_program.h +++ b/src/core/shader_program.h | |||
| @@ -5,8 +5,6 @@ | |||
| 5 | 5 | ||
| 6 | #include "gl_util.h" | 6 | #include "gl_util.h" |
| 7 | 7 | ||
| 8 | #include <math/fwd.h> | ||
| 9 | |||
| 10 | #include <stdbool.h> | 8 | #include <stdbool.h> |
| 11 | 9 | ||
| 12 | typedef struct Texture Texture; | 10 | typedef struct Texture Texture; |
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..bf713ca 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 "renderer/imm_renderer_impl.h" | 5 | #include "memory.h" |
| 6 | #include "renderer/renderer_impl.h" | 6 | #include "render/imm_impl.h" |
| 7 | #include "scene/scene_memory.h" | 7 | #include "render/llr_impl.h" |
| 8 | 8 | #include "render/renderer_impl.h" | |
| 9 | #include <log/log.h> | ||
| 10 | 9 | ||
| 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 | Imm imm; |
| 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, &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); |
| 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; |
| @@ -62,9 +67,14 @@ Renderer* gfx_get_renderer(Gfx* gfx) { | |||
| 62 | return &gfx->renderer; | 67 | return &gfx->renderer; |
| 63 | } | 68 | } |
| 64 | 69 | ||
| 65 | ImmRenderer* gfx_get_imm_renderer(Gfx* gfx) { | 70 | Imm* gfx_get_imm(Gfx* gfx) { |
| 71 | assert(gfx); | ||
| 72 | return &gfx->imm; | ||
| 73 | } | ||
| 74 | |||
| 75 | LLR* gfx_get_llr(Gfx* gfx) { | ||
| 66 | assert(gfx); | 76 | assert(gfx); |
| 67 | return &gfx->imm_renderer; | 77 | return &gfx->llr; |
| 68 | } | 78 | } |
| 69 | 79 | ||
| 70 | AssetCache* gfx_get_asset_cache(Gfx* gfx) { | 80 | AssetCache* gfx_get_asset_cache(Gfx* gfx) { |
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/scene_memory.c b/src/memory.c index 85c27e7..754f04d 100644 --- a/gfx/src/scene/scene_memory.c +++ b/src/memory.c | |||
| @@ -1,22 +1,23 @@ | |||
| 1 | #include "scene_memory.h" | 1 | #include "memory.h" |
| 2 | 2 | ||
| 3 | #include <gfx/scene.h> | ||
| 3 | #include <gfx/sizes.h> | 4 | #include <gfx/sizes.h> |
| 4 | 5 | ||
| 5 | #include "animation_impl.h" | 6 | #include "animation_impl.h" |
| 6 | #include "camera_impl.h" | 7 | #include "scene/light_impl.h" |
| 7 | #include "light_impl.h" | 8 | #include "scene/material_impl.h" |
| 8 | #include "material_impl.h" | 9 | #include "scene/mesh_impl.h" |
| 9 | #include "mesh_impl.h" | 10 | #include "scene/model_impl.h" |
| 10 | #include "model_impl.h" | 11 | #include "scene/node_impl.h" |
| 11 | #include "node_impl.h" | 12 | #include "scene/object_impl.h" |
| 12 | #include "object_impl.h" | 13 | #include "scene/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) |
| 18 | DEF_MEMPOOL(animation_pool, Animation, GFX_MAX_NUM_ANIMATIONS) | 19 | DEF_MEMPOOL(animation_pool, Animation, GFX_MAX_NUM_ANIMATIONS) |
| 19 | DEF_MEMPOOL(camera_pool, SceneCamera, GFX_MAX_NUM_CAMERAS) | 20 | DEF_MEMPOOL(camera_pool, Camera, GFX_MAX_NUM_CAMERAS) |
| 20 | DEF_MEMPOOL(light_pool, Light, GFX_MAX_NUM_LIGHTS) | 21 | DEF_MEMPOOL(light_pool, Light, GFX_MAX_NUM_LIGHTS) |
| 21 | DEF_MEMPOOL(material_pool, Material, GFX_MAX_NUM_MATERIALS) | 22 | DEF_MEMPOOL(material_pool, Material, GFX_MAX_NUM_MATERIALS) |
| 22 | DEF_MEMPOOL(mesh_pool, Mesh, GFX_MAX_NUM_MESHES) | 23 | DEF_MEMPOOL(mesh_pool, Mesh, GFX_MAX_NUM_MESHES) |
| @@ -47,16 +48,17 @@ 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 |
| 57 | #define MEM_FIELD(name) mem.PLURAL(name) | 59 | #define MEM_FIELD(name) mem.PLURAL(name) |
| 58 | 60 | ||
| 59 | void scene_mem_init() { | 61 | void scene_mem_init(void) { |
| 60 | mempool_make(&mem.animas); | 62 | mempool_make(&mem.animas); |
| 61 | mempool_make(&mem.animations); | 63 | mempool_make(&mem.animations); |
| 62 | mempool_make(&mem.cameras); | 64 | mempool_make(&mem.cameras); |
| @@ -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); |
| @@ -86,17 +88,44 @@ void scene_mem_init() { | |||
| 86 | ALLOC_DUMMY(&mem.skeletons); | 88 | ALLOC_DUMMY(&mem.skeletons); |
| 87 | } | 89 | } |
| 88 | 90 | ||
| 89 | void scene_mem_destroy() { | 91 | void scene_mem_destroy(void) { |
| 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,25 +148,25 @@ 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(void) { 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) |
| 139 | DEF_MEMORY(animation, Animation) | 168 | DEF_MEMORY(animation, Animation) |
| 140 | DEF_MEMORY(camera, SceneCamera) | 169 | DEF_MEMORY(camera, Camera) |
| 141 | DEF_MEMORY(light, Light) | 170 | DEF_MEMORY(light, Light) |
| 142 | DEF_MEMORY(material, Material) | 171 | DEF_MEMORY(material, Material) |
| 143 | DEF_MEMORY(mesh, Mesh) | 172 | DEF_MEMORY(mesh, Mesh) |
diff --git a/gfx/src/scene/scene_memory.h b/src/memory.h index d175cba..bfbee66 100644 --- a/gfx/src/scene/scene_memory.h +++ b/src/memory.h | |||
| @@ -3,21 +3,23 @@ | |||
| 3 | 3 | ||
| 4 | #include "types.h" | 4 | #include "types.h" |
| 5 | 5 | ||
| 6 | typedef struct Camera Camera; | ||
| 7 | |||
| 6 | /// Initialize scene memory. | 8 | /// Initialize scene memory. |
| 7 | /// | 9 | /// |
| 8 | /// The scene memory guarantees that every object maps to an index different | 10 | /// The scene memory guarantees that every object maps to an index different |
| 9 | /// than 0. This way, 0 can be used as a special index to denote "no value". | 11 | /// than 0. This way, 0 can be used as a special index to denote "no value". |
| 10 | void scene_mem_init(); | 12 | void scene_mem_init(void); |
| 11 | 13 | ||
| 12 | /// Destroy the scene memory and all allocated objects. | 14 | /// Destroy the scene memory and all allocated objects. |
| 13 | void scene_mem_destroy(); | 15 | void scene_mem_destroy(void); |
| 14 | 16 | ||
| 15 | #define NAMED_INDEX(name) name##_idx | 17 | #define NAMED_INDEX(name) name##_idx |
| 16 | 18 | ||
| 17 | #define DECL_MEMORY(name, type) \ | 19 | #define DECL_MEMORY(name, type) \ |
| 18 | typedef struct type type; \ | 20 | typedef struct type type; \ |
| 19 | /* xyz* mem_alloc_xyz() */ \ | 21 | /* xyz* mem_alloc_xyz() */ \ |
| 20 | type* mem_alloc_##name(); \ | 22 | type* mem_alloc_##name(void); \ |
| 21 | /* mem_free_xyz(xyz**) */ \ | 23 | /* mem_free_xyz(xyz**) */ \ |
| 22 | void mem_free_##name(type**); \ | 24 | void mem_free_##name(type**); \ |
| 23 | /* xyz* mem_get_xyz(xyz_idx); */ \ | 25 | /* xyz* mem_get_xyz(xyz_idx); */ \ |
| @@ -27,7 +29,7 @@ void scene_mem_destroy(); | |||
| 27 | 29 | ||
| 28 | DECL_MEMORY(anima, Anima) | 30 | DECL_MEMORY(anima, Anima) |
| 29 | DECL_MEMORY(animation, Animation) | 31 | DECL_MEMORY(animation, Animation) |
| 30 | DECL_MEMORY(camera, SceneCamera) | 32 | DECL_MEMORY(camera, Camera) |
| 31 | DECL_MEMORY(light, Light) | 33 | DECL_MEMORY(light, Light) |
| 32 | DECL_MEMORY(material, Material) | 34 | DECL_MEMORY(material, Material) |
| 33 | DECL_MEMORY(mesh, Mesh) | 35 | DECL_MEMORY(mesh, Mesh) |
diff --git a/src/render/imm.c b/src/render/imm.c new file mode 100644 index 0000000..7ab8d62 --- /dev/null +++ b/src/render/imm.c | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | #include "imm_impl.h" | ||
| 2 | |||
| 3 | #include <gfx/core.h> | ||
| 4 | #include <gfx/render/imm.h> | ||
| 5 | #include <gfx/render/llr.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(Imm* 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, &(GeometryDesc){ | ||
| 25 | .type = Triangles, | ||
| 26 | .buffer_usage = BufferDynamic, | ||
| 27 | .num_verts = num_triangle_verts, | ||
| 28 | .positions3d = (BufferView3d){ | ||
| 29 | .size_bytes = num_triangle_verts * sizeof(vec3), | ||
| 30 | .count = num_triangle_verts} | ||
| 31 | }); | ||
| 32 | if (!renderer->triangles) { | ||
| 33 | goto cleanup; | ||
| 34 | } | ||
| 35 | |||
| 36 | renderer->shader = gfx_make_immediate_mode_shader(gfxcore); | ||
| 37 | if (!renderer->shader) { | ||
| 38 | goto cleanup; | ||
| 39 | } | ||
| 40 | |||
| 41 | gfx_imm_set_colour(renderer, vec4_make(0.0f, 0.0f, 0.0f, 1.0f)); | ||
| 42 | |||
| 43 | return true; | ||
| 44 | |||
| 45 | cleanup: | ||
| 46 | gfx_imm_destroy(renderer); | ||
| 47 | return false; | ||
| 48 | } | ||
| 49 | |||
| 50 | void gfx_imm_destroy(Imm* renderer) { | ||
| 51 | assert(renderer); | ||
| 52 | assert(renderer->gfxcore); | ||
| 53 | |||
| 54 | if (renderer->triangles) { | ||
| 55 | gfx_destroy_geometry(renderer->gfxcore, &renderer->triangles); | ||
| 56 | // TODO: Could also destroy the geometry's buffers here. | ||
| 57 | } | ||
| 58 | |||
| 59 | if (renderer->shader) { | ||
| 60 | gfx_destroy_shader_program(renderer->gfxcore, &renderer->shader); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | void gfx_imm_flush(Imm* renderer) { | ||
| 65 | assert(renderer); | ||
| 66 | |||
| 67 | if (renderer->num_triangle_verts > 0) { | ||
| 68 | gfx_update_geometry( | ||
| 69 | renderer->triangles, | ||
| 70 | &(GeometryDesc){ | ||
| 71 | .num_verts = renderer->num_triangle_verts, | ||
| 72 | .positions3d = (BufferView3d){ | ||
| 73 | .data = renderer->triangle_verts, | ||
| 74 | .size_bytes = renderer->num_triangle_verts * sizeof(vec3)} | ||
| 75 | }); | ||
| 76 | |||
| 77 | gfx_llr_render_geometry(renderer->llr, renderer->triangles); | ||
| 78 | |||
| 79 | renderer->num_triangle_verts = 0; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | void gfx_imm_start(Imm* renderer) { | ||
| 84 | assert(renderer); | ||
| 85 | |||
| 86 | // Shader uniforms are applied lazily. | ||
| 87 | // TODO: In the event that gfx_activate_shader_program() activates uniforms | ||
| 88 | // automatically for convenience, call an overload here that doesn't do so. | ||
| 89 | // gfx_activate_shader_program(renderer->shader); | ||
| 90 | gfx_llr_set_shader(renderer->llr, renderer->shader); | ||
| 91 | } | ||
| 92 | |||
| 93 | void gfx_imm_end(Imm* renderer) { | ||
| 94 | assert(renderer); | ||
| 95 | |||
| 96 | gfx_imm_flush(renderer); | ||
| 97 | // gfx_deactivate_shader_program(renderer->shader); | ||
| 98 | gfx_llr_set_shader(renderer->llr, 0); | ||
| 99 | } | ||
| 100 | |||
| 101 | void gfx_imm_draw_triangles( | ||
| 102 | Imm* renderer, const vec3 verts[], size_t num_triangles) { | ||
| 103 | assert(renderer); | ||
| 104 | assert(verts); | ||
| 105 | const size_t new_verts = num_triangles * 3; | ||
| 106 | assert( | ||
| 107 | renderer->num_triangle_verts + new_verts < | ||
| 108 | (GFX_IMM_MAX_NUM_TRIANGLES * 3)); | ||
| 109 | |||
| 110 | memcpy( | ||
| 111 | renderer->triangle_verts + renderer->num_triangle_verts, verts, | ||
| 112 | new_verts * sizeof(vec3)); | ||
| 113 | |||
| 114 | renderer->num_triangle_verts += new_verts; | ||
| 115 | } | ||
| 116 | |||
| 117 | void gfx_imm_draw_triangle(Imm* renderer, const vec3 verts[3]) { | ||
| 118 | gfx_imm_draw_triangles(renderer, verts, 1); | ||
| 119 | } | ||
| 120 | |||
| 121 | void gfx_imm_draw_aabb2(Imm* renderer, aabb2 box) { | ||
| 122 | assert(renderer); | ||
| 123 | |||
| 124 | // clang-format off | ||
| 125 | const vec3 verts[4] = { | ||
| 126 | vec3_make(box.min.x, box.min.y, 0), // 3 ---- 2 | ||
| 127 | vec3_make(box.max.x, box.min.y, 0), // | | | ||
| 128 | vec3_make(box.max.x, box.max.y, 0), // | | | ||
| 129 | vec3_make(box.min.x, box.max.y, 0)}; // 0 ---- 1 | ||
| 130 | // clang-format on | ||
| 131 | |||
| 132 | #define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2] | ||
| 133 | const vec3 tris[6] = {tri(0, 1, 2), tri(0, 2, 3)}; | ||
| 134 | #undef tri | ||
| 135 | |||
| 136 | gfx_imm_draw_triangles(renderer, tris, 2); | ||
| 137 | } | ||
| 138 | |||
| 139 | void gfx_imm_draw_aabb3(Imm* renderer, aabb3 box) { | ||
| 140 | assert(renderer); | ||
| 141 | |||
| 142 | // clang-format off | ||
| 143 | const vec3 vertices[8] = { | ||
| 144 | vec3_make(box.min.x, box.min.y, box.max.z), // 7 ----- 6 | ||
| 145 | vec3_make(box.max.x, box.min.y, box.max.z), // / /| | ||
| 146 | vec3_make(box.max.x, box.max.y, box.max.z), // 3 ----- 2 | | ||
| 147 | vec3_make(box.min.x, box.max.y, box.max.z), // | | | | ||
| 148 | vec3_make(box.min.x, box.min.y, box.min.z), // | 4 ----- 5 | ||
| 149 | vec3_make(box.max.x, box.min.y, box.min.z), // |/ |/ | ||
| 150 | vec3_make(box.max.x, box.max.y, box.min.z), // 0 ----- 1 | ||
| 151 | vec3_make(box.min.x, box.max.y, box.min.z)}; | ||
| 152 | // clang-format on | ||
| 153 | |||
| 154 | gfx_imm_draw_box3(renderer, vertices); | ||
| 155 | } | ||
| 156 | |||
| 157 | void gfx_imm_draw_box3(Imm* renderer, const vec3 vertices[8]) { | ||
| 158 | assert(renderer); | ||
| 159 | assert(vertices); | ||
| 160 | |||
| 161 | // 7 ----- 6 | ||
| 162 | // / /| | ||
| 163 | // 3 ----- 2 | | ||
| 164 | // | | | | ||
| 165 | // | 4 ----- 5 | ||
| 166 | // |/ |/ | ||
| 167 | // 0 ----- 1 | ||
| 168 | |||
| 169 | #define tri(i0, i1, i2) vertices[i0], vertices[i1], vertices[i2] | ||
| 170 | const vec3 tris[36] = { | ||
| 171 | // Front. | ||
| 172 | tri(0, 1, 2), tri(0, 2, 3), | ||
| 173 | // Right. | ||
| 174 | tri(1, 5, 6), tri(1, 6, 2), | ||
| 175 | // Back. | ||
| 176 | tri(5, 4, 7), tri(5, 7, 6), | ||
| 177 | // Left. | ||
| 178 | tri(4, 0, 03), tri(4, 3, 7), | ||
| 179 | // Top. | ||
| 180 | tri(3, 2, 6), tri(3, 6, 7), | ||
| 181 | // Bottom. | ||
| 182 | tri(0, 4, 5), tri(0, 5, 1)}; | ||
| 183 | |||
| 184 | gfx_imm_draw_triangles(renderer, tris, 12); | ||
| 185 | } | ||
| 186 | |||
| 187 | void gfx_imm_set_colour(Imm* renderer, vec4 colour) { | ||
| 188 | assert(renderer); | ||
| 189 | assert(renderer->shader); | ||
| 190 | |||
| 191 | gfx_imm_flush(renderer); | ||
| 192 | |||
| 193 | gfx_set_vec4_uniform(renderer->shader, "Colour", colour); | ||
| 194 | } | ||
diff --git a/gfx/src/renderer/imm_renderer_impl.h b/src/render/imm_impl.h index 5ece354..d87b910 100644 --- a/gfx/src/renderer/imm_renderer_impl.h +++ b/src/render/imm_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. |
| 20 | typedef struct ImmRenderer { | 23 | /// TODO: Flush the buffer when it reaches its maximum size to remove this |
| 21 | GfxCore* gfxcore; | 24 | /// constraint. |
| 22 | ShaderProgram* shader; | 25 | typedef struct Imm { |
| 26 | GfxCore* gfxcore; | ||
| 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. | 37 | } Imm; |
| 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; | ||
| 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(Imm*, 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(Imm*); |
| 42 | |||
| 43 | /// Flush draw commands. | ||
| 44 | void imm_renderer_flush(ImmRenderer*); | ||
diff --git a/src/render/llr.c b/src/render/llr.c new file mode 100644 index 0000000..752b65b --- /dev/null +++ b/src/render/llr.c | |||
| @@ -0,0 +1,441 @@ | |||
| 1 | #include "llr_impl.h" | ||
| 2 | |||
| 3 | #include "animation_impl.h" | ||
| 4 | #include "scene/light_impl.h" | ||
| 5 | #include "scene/material_impl.h" | ||
| 6 | #include "scene/mesh_impl.h" | ||
| 7 | #include "scene/node_impl.h" | ||
| 8 | |||
| 9 | #include <gfx/core.h> | ||
| 10 | #include <gfx/util/ibl.h> | ||
| 11 | |||
| 12 | #include <cassert.h> | ||
| 13 | |||
| 14 | static const int IRRADIANCE_MAP_WIDTH = 1024; | ||
| 15 | static const int IRRADIANCE_MAP_HEIGHT = 1024; | ||
| 16 | static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128; | ||
| 17 | static const int PREFILTERED_ENVIRONMENT_MAP_HEIGHT = 128; | ||
| 18 | static const int BRDF_INTEGRATION_MAP_WIDTH = 512; | ||
| 19 | static const int BRDF_INTEGRATION_MAP_HEIGHT = 512; | ||
| 20 | |||
| 21 | /// Activate the material. | ||
| 22 | /// | ||
| 23 | /// This configures the shader uniforms that are specific to the material. | ||
| 24 | static void material_activate(ShaderProgram* shader, const Material* material) { | ||
| 25 | assert(material); | ||
| 26 | for (int i = 0; i < material->num_uniforms; ++i) { | ||
| 27 | const ShaderUniform* uniform = &material->uniforms[i]; | ||
| 28 | gfx_set_uniform(shader, uniform); | ||
| 29 | } | ||
| 30 | if (material->alpha_mode != Opaque) { | ||
| 31 | gfx_set_uniform( | ||
| 32 | shader, &(ShaderUniform){.name = sstring_make("AlphaMode"), | ||
| 33 | .type = UniformInt, | ||
| 34 | .value.uniform_int = material->alpha_mode}); | ||
| 35 | } | ||
| 36 | if (material->alpha_mode == Mask) { | ||
| 37 | gfx_set_uniform( | ||
| 38 | shader, | ||
| 39 | &(ShaderUniform){.name = sstring_make("AlphaCutoff"), | ||
| 40 | .type = UniformFloat, | ||
| 41 | .value.uniform_float = material->alpha_cutoff}); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Initialize renderer state for IBL. | ||
| 46 | static bool init_ibl(LLR* renderer) { | ||
| 47 | assert(renderer); | ||
| 48 | assert(!renderer->ibl); | ||
| 49 | assert(!renderer->brdf_integration_map); | ||
| 50 | |||
| 51 | if (!((renderer->ibl = gfx_make_ibl(renderer->gfxcore)))) { | ||
| 52 | return false; | ||
| 53 | } | ||
| 54 | |||
| 55 | if (!((renderer->brdf_integration_map = gfx_make_brdf_integration_map( | ||
| 56 | renderer->ibl, renderer->gfxcore, BRDF_INTEGRATION_MAP_WIDTH, | ||
| 57 | BRDF_INTEGRATION_MAP_HEIGHT)))) { | ||
| 58 | return false; | ||
| 59 | } | ||
| 60 | |||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Compute irradiance and prefiltered environment maps for the light if they | ||
| 65 | /// have not been already computed. | ||
| 66 | /// | ||
| 67 | /// This is done lazily here, and not when the light is created, because we | ||
| 68 | /// need an IBL instance to do this and it is more convenient for the public | ||
| 69 | /// API to create lights without worrying about those details. It also makes the | ||
| 70 | /// public API cheaper, since the maps are only computed when they are actually | ||
| 71 | /// needed. | ||
| 72 | static bool set_up_environment_light(LLR* renderer, EnvironmentLight* light) { | ||
| 73 | assert(renderer); | ||
| 74 | assert(light); | ||
| 75 | assert(renderer->ibl); | ||
| 76 | assert(renderer->brdf_integration_map); | ||
| 77 | |||
| 78 | if (light->irradiance_map) { | ||
| 79 | assert(light->prefiltered_environment_map); | ||
| 80 | return true; | ||
| 81 | } | ||
| 82 | |||
| 83 | // For convenience. | ||
| 84 | GfxCore* gfxcore = renderer->gfxcore; | ||
| 85 | |||
| 86 | Texture* irradiance_map = 0; | ||
| 87 | Texture* prefiltered_environment_map = 0; | ||
| 88 | |||
| 89 | if (!((irradiance_map = gfx_make_irradiance_map( | ||
| 90 | renderer->ibl, gfxcore, light->environment_map, | ||
| 91 | IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT)))) { | ||
| 92 | goto cleanup; | ||
| 93 | } | ||
| 94 | |||
| 95 | int max_mip_level = 0; | ||
| 96 | if (!((prefiltered_environment_map = gfx_make_prefiltered_environment_map( | ||
| 97 | renderer->ibl, gfxcore, light->environment_map, | ||
| 98 | PREFILTERED_ENVIRONMENT_MAP_WIDTH, | ||
| 99 | PREFILTERED_ENVIRONMENT_MAP_HEIGHT, &max_mip_level)))) { | ||
| 100 | goto cleanup; | ||
| 101 | } | ||
| 102 | |||
| 103 | light->irradiance_map = irradiance_map; | ||
| 104 | light->prefiltered_environment_map = prefiltered_environment_map; | ||
| 105 | light->max_reflection_lod = max_mip_level; | ||
| 106 | |||
| 107 | return true; | ||
| 108 | |||
| 109 | cleanup: | ||
| 110 | if (irradiance_map) { | ||
| 111 | gfx_destroy_texture(gfxcore, &irradiance_map); | ||
| 112 | } | ||
| 113 | if (prefiltered_environment_map) { | ||
| 114 | gfx_destroy_texture(gfxcore, &prefiltered_environment_map); | ||
| 115 | } | ||
| 116 | return false; | ||
| 117 | } | ||
| 118 | |||
| 119 | static void configure_light(LLR* renderer, Light* light) { | ||
| 120 | assert(renderer); | ||
| 121 | assert(light); | ||
| 122 | |||
| 123 | // For convenience. | ||
| 124 | ShaderProgram* const shader = renderer->shader; | ||
| 125 | |||
| 126 | switch (light->type) { | ||
| 127 | case EnvironmentLightType: { | ||
| 128 | EnvironmentLight* env = &light->environment; | ||
| 129 | |||
| 130 | const bool initialized = set_up_environment_light(renderer, env); | ||
| 131 | ASSERT(initialized); | ||
| 132 | assert(env->environment_map); | ||
| 133 | assert(env->irradiance_map); | ||
| 134 | assert(env->prefiltered_environment_map); | ||
| 135 | assert(renderer->brdf_integration_map); | ||
| 136 | |||
| 137 | gfx_set_texture_uniform( | ||
| 138 | shader, "BRDFIntegrationMap", renderer->brdf_integration_map); | ||
| 139 | gfx_set_texture_uniform(shader, "Sky", env->environment_map); | ||
| 140 | gfx_set_texture_uniform(shader, "IrradianceMap", env->irradiance_map); | ||
| 141 | gfx_set_texture_uniform( | ||
| 142 | shader, "PrefilteredEnvironmentMap", env->prefiltered_environment_map); | ||
| 143 | gfx_set_float_uniform( | ||
| 144 | shader, "MaxReflectionLOD", (float)env->max_reflection_lod); | ||
| 145 | |||
| 146 | break; | ||
| 147 | } | ||
| 148 | default: | ||
| 149 | assert(false); // TODO: Implement other light types. | ||
| 150 | break; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | static void configure_state(LLR* renderer) { | ||
| 155 | assert(renderer); | ||
| 156 | |||
| 157 | // Check if anything changed first so that we don't call gfx_apply_uniforms() | ||
| 158 | // unnecessarily. | ||
| 159 | const bool nothing_changed = (renderer->changed_flags == 0); | ||
| 160 | if (nothing_changed) { | ||
| 161 | return; | ||
| 162 | } | ||
| 163 | // Setting a null shader is also allowed, in which case there is nothing to | ||
| 164 | // configure. | ||
| 165 | if (renderer->shader == 0) { | ||
| 166 | renderer->shader_changed = false; | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | |||
| 170 | // For convenience. | ||
| 171 | ShaderProgram* const shader = renderer->shader; | ||
| 172 | const mat4* const model = &renderer->matrix_stack[renderer->stack_pointer]; | ||
| 173 | |||
| 174 | // TODO: Check to see which ones the shader actually uses and avoid | ||
| 175 | // computing the unnecessary matrices. | ||
| 176 | |||
| 177 | if (renderer->matrix_changed || renderer->shader_changed) { | ||
| 178 | renderer->matrix_changed = false; | ||
| 179 | |||
| 180 | gfx_set_mat4_uniform(shader, "Model", model); | ||
| 181 | gfx_set_mat4_uniform(shader, "ModelMatrix", model); | ||
| 182 | } | ||
| 183 | |||
| 184 | // TODO: camera_changed is not set anywhere. Need to think how imm primitive | ||
| 185 | // rendering and imm mesh rendering work together. We could treat imm | ||
| 186 | // primitive calls like setting a new shader. | ||
| 187 | if (renderer->camera_changed || renderer->shader_changed) { | ||
| 188 | renderer->camera_changed = false; | ||
| 189 | |||
| 190 | // Set all supported camera-related uniforms. Shaders can choose which ones | ||
| 191 | // to use. | ||
| 192 | const mat4 modelview = mat4_mul(renderer->view, *model); | ||
| 193 | const mat4 view_proj = mat4_mul(renderer->projection, renderer->view); | ||
| 194 | const mat4 mvp = mat4_mul(renderer->projection, modelview); | ||
| 195 | |||
| 196 | gfx_set_mat4_uniform(shader, "Modelview", &modelview); | ||
| 197 | gfx_set_mat4_uniform(shader, "View", &renderer->view); | ||
| 198 | gfx_set_mat4_uniform(shader, "Projection", &renderer->projection); | ||
| 199 | gfx_set_mat4_uniform(shader, "ViewProjection", &view_proj); | ||
| 200 | gfx_set_mat4_uniform(shader, "MVP", &mvp); | ||
| 201 | gfx_set_vec3_uniform(shader, "CameraPosition", renderer->camera_position); | ||
| 202 | gfx_set_mat4_uniform(shader, "CameraRotation", &renderer->camera_rotation); | ||
| 203 | gfx_set_float_uniform(shader, "Fovy", renderer->fovy); | ||
| 204 | gfx_set_float_uniform(shader, "Aspect", renderer->aspect); | ||
| 205 | } | ||
| 206 | |||
| 207 | if (renderer->lights_changed || renderer->shader_changed) { | ||
| 208 | renderer->lights_changed = false; | ||
| 209 | |||
| 210 | // TODO: Could do better by only setting the lights that have actually | ||
| 211 | // changed. | ||
| 212 | // TODO: Will also need to pass the number of lights to the shader once the | ||
| 213 | // other light types are implemented. | ||
| 214 | for (int i = 0; i < renderer->num_lights; ++i) { | ||
| 215 | configure_light(renderer, renderer->lights[i]); | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | if (renderer->skeleton_changed || renderer->shader_changed) { | ||
| 220 | renderer->skeleton_changed = false; | ||
| 221 | |||
| 222 | if (renderer->num_joints > 0) { | ||
| 223 | gfx_set_mat4_array_uniform( | ||
| 224 | shader, "JointMatrices", renderer->joint_matrices, | ||
| 225 | renderer->num_joints); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | if (renderer->material_changed || renderer->shader_changed) { | ||
| 230 | renderer->material_changed = false; | ||
| 231 | |||
| 232 | // Geometry may be rendered without a material. | ||
| 233 | if (renderer->material) { | ||
| 234 | material_activate(renderer->shader, renderer->material); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | if (renderer->shader_changed) { | ||
| 239 | renderer->shader_changed = false; | ||
| 240 | gfx_activate_shader_program(renderer->shader); | ||
| 241 | } | ||
| 242 | |||
| 243 | // TODO: At present, this results in many redundant calls to | ||
| 244 | // glGetUniformLocation() and glUniformXyz(). Look at the trace. | ||
| 245 | // | ||
| 246 | // TODO: Could add to qapitrace functionality to detect redundant calls and | ||
| 247 | // other inefficiencies. Maybe ask in the Github first if there would be | ||
| 248 | // interest in this. | ||
| 249 | // | ||
| 250 | // Must be called after activating the program. | ||
| 251 | gfx_apply_uniforms(renderer->shader); | ||
| 252 | } | ||
| 253 | |||
| 254 | bool gfx_llr_make(LLR* renderer, GfxCore* gfxcore) { | ||
| 255 | assert(renderer); | ||
| 256 | assert(gfxcore); | ||
| 257 | |||
| 258 | renderer->gfxcore = gfxcore; | ||
| 259 | if (!init_ibl(renderer)) { | ||
| 260 | goto cleanup; | ||
| 261 | } | ||
| 262 | gfx_llr_load_identity(renderer); | ||
| 263 | renderer->view = mat4_id(); | ||
| 264 | renderer->projection = mat4_id(); | ||
| 265 | renderer->camera_rotation = mat4_id(); | ||
| 266 | return true; | ||
| 267 | |||
| 268 | cleanup: | ||
| 269 | gfx_llr_destroy(renderer); | ||
| 270 | return false; | ||
| 271 | } | ||
| 272 | |||
| 273 | void gfx_llr_destroy(LLR* renderer) { | ||
| 274 | assert(renderer); | ||
| 275 | assert(renderer->gfxcore); | ||
| 276 | |||
| 277 | if (renderer->brdf_integration_map) { | ||
| 278 | gfx_destroy_texture(renderer->gfxcore, &renderer->brdf_integration_map); | ||
| 279 | } | ||
| 280 | |||
| 281 | // TODO: Do this once the IBL from the scene renderer is gone. | ||
| 282 | if (renderer->ibl) { | ||
| 283 | // gfx_destroy_ibl(renderer->gfxcore, &renderer->ibl); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | void gfx_llr_set_shader(LLR* renderer, ShaderProgram* shader) { | ||
| 288 | assert(renderer); | ||
| 289 | // null shader is allowed, so do not assert it. | ||
| 290 | |||
| 291 | // It's important to not set shader_changed unnecessarily, since that would | ||
| 292 | // re-trigger the setting of uniforms. | ||
| 293 | if (renderer->shader != shader) { | ||
| 294 | renderer->shader = shader; | ||
| 295 | renderer->shader_changed = true; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | void gfx_llr_push_light(LLR* renderer, Light* light) { | ||
| 300 | assert(renderer); | ||
| 301 | assert(light); | ||
| 302 | assert(renderer->num_lights >= 0); | ||
| 303 | ASSERT(renderer->num_lights < GFX_LLR_MAX_NUM_LIGHTS); | ||
| 304 | |||
| 305 | renderer->lights[renderer->num_lights++] = light; | ||
| 306 | renderer->lights_changed = true; | ||
| 307 | } | ||
| 308 | |||
| 309 | void gfx_llr_pop_light(LLR* renderer) { | ||
| 310 | assert(renderer); | ||
| 311 | ASSERT(renderer->num_lights > 0); | ||
| 312 | |||
| 313 | renderer->lights[--renderer->num_lights] = 0; | ||
| 314 | renderer->lights_changed = true; | ||
| 315 | } | ||
| 316 | |||
| 317 | void gfx_llr_set_skeleton( | ||
| 318 | LLR* renderer, const Anima* anima, const Skeleton* skeleton) { | ||
| 319 | assert(renderer); | ||
| 320 | assert(anima); | ||
| 321 | assert(skeleton); | ||
| 322 | assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS); | ||
| 323 | |||
| 324 | for (size_t i = 0; i < skeleton->num_joints; ++i) { | ||
| 325 | const joint_idx joint_index = skeleton->joints[i]; | ||
| 326 | const Joint* joint = &anima->joints[joint_index]; | ||
| 327 | renderer->joint_matrices[i] = joint->joint_matrix; | ||
| 328 | } | ||
| 329 | renderer->num_joints = skeleton->num_joints; | ||
| 330 | renderer->skeleton_changed = true; | ||
| 331 | } | ||
| 332 | |||
| 333 | void gfx_llr_clear_skeleton(LLR* renderer) { | ||
| 334 | assert(renderer); | ||
| 335 | |||
| 336 | renderer->num_joints = 0; | ||
| 337 | renderer->skeleton_changed = true; | ||
| 338 | } | ||
| 339 | |||
| 340 | void gfx_llr_set_material(LLR* renderer, const Material* material) { | ||
| 341 | assert(renderer); | ||
| 342 | assert(material); | ||
| 343 | |||
| 344 | renderer->material = material; | ||
| 345 | renderer->material_changed = true; | ||
| 346 | } | ||
| 347 | |||
| 348 | void gfx_llr_set_camera(LLR* renderer, const Camera* camera) { | ||
| 349 | assert(renderer); | ||
| 350 | |||
| 351 | renderer->camera_position = camera->spatial.p; | ||
| 352 | renderer->camera_rotation = | ||
| 353 | mat4_rotation(spatial3_transform(&camera->spatial)); | ||
| 354 | renderer->view = spatial3_inverse_transform(&camera->spatial); | ||
| 355 | renderer->projection = camera->projection; | ||
| 356 | // Assuming a perspective matrix. | ||
| 357 | renderer->fovy = (R)atan(1.0 / (mat4_at(camera->projection, 1, 1))) * 2; | ||
| 358 | renderer->camera_changed = true; | ||
| 359 | } | ||
| 360 | |||
| 361 | void gfx_llr_set_projection_matrix(LLR* renderer, const mat4* projection) { | ||
| 362 | assert(renderer); | ||
| 363 | |||
| 364 | renderer->projection = *projection; | ||
| 365 | renderer->camera_changed = true; | ||
| 366 | } | ||
| 367 | |||
| 368 | void gfx_llr_set_aspect(LLR* renderer, float aspect) { | ||
| 369 | assert(renderer); | ||
| 370 | |||
| 371 | renderer->aspect = aspect; | ||
| 372 | renderer->camera_changed = true; | ||
| 373 | } | ||
| 374 | |||
| 375 | void gfx_llr_render_geometry(LLR* renderer, const Geometry* geometry) { | ||
| 376 | assert(renderer); | ||
| 377 | assert(geometry); | ||
| 378 | |||
| 379 | configure_state(renderer); | ||
| 380 | gfx_render_geometry(geometry); | ||
| 381 | } | ||
| 382 | |||
| 383 | void gfx_llr_render_mesh(LLR* renderer, const Mesh* mesh) { | ||
| 384 | assert(renderer); | ||
| 385 | assert(mesh); | ||
| 386 | assert(mesh->geometry); | ||
| 387 | assert(mesh->material); | ||
| 388 | assert(mesh->shader); | ||
| 389 | |||
| 390 | gfx_llr_set_material(renderer, mesh->material); | ||
| 391 | gfx_llr_set_shader(renderer, mesh->shader); | ||
| 392 | gfx_llr_render_geometry(renderer, mesh->geometry); | ||
| 393 | } | ||
| 394 | |||
| 395 | void gfx_llr_load_identity(LLR* renderer) { | ||
| 396 | assert(renderer); | ||
| 397 | |||
| 398 | renderer->matrix_stack[0] = mat4_id(); | ||
| 399 | renderer->stack_pointer = 0; | ||
| 400 | renderer->matrix_changed = true; | ||
| 401 | } | ||
| 402 | |||
| 403 | void gfx_llr_push_matrix(LLR* renderer, const mat4* matrix) { | ||
| 404 | assert(renderer); | ||
| 405 | assert(matrix); | ||
| 406 | assert(renderer->stack_pointer >= 0); | ||
| 407 | ASSERT(renderer->stack_pointer < GFX_LLR_MAX_NUM_MATRICES); | ||
| 408 | |||
| 409 | renderer->stack_pointer += 1; | ||
| 410 | renderer->matrix_stack[renderer->stack_pointer] = | ||
| 411 | mat4_mul(*matrix, renderer->matrix_stack[renderer->stack_pointer - 1]); | ||
| 412 | renderer->matrix_changed = true; | ||
| 413 | } | ||
| 414 | |||
| 415 | void gfx_llr_pop_matrix(LLR* renderer) { | ||
| 416 | assert(renderer); | ||
| 417 | ASSERT(renderer->stack_pointer > 0); | ||
| 418 | |||
| 419 | // For debugging, zero out the matrix stack as matrices are popped out. | ||
| 420 | memset( | ||
| 421 | &renderer->matrix_stack[renderer->stack_pointer], 0, | ||
| 422 | sizeof(renderer->matrix_stack[0])); | ||
| 423 | renderer->stack_pointer -= 1; | ||
| 424 | renderer->matrix_changed = true; | ||
| 425 | } | ||
| 426 | |||
| 427 | void gfx_llr_translate(LLR* renderer, vec3 offset) { | ||
| 428 | assert(renderer); | ||
| 429 | |||
| 430 | const mat4 mat = mat4_translate(offset); | ||
| 431 | gfx_llr_push_matrix(renderer, &mat); | ||
| 432 | } | ||
| 433 | |||
| 434 | void gfx_llr_set_model_matrix(LLR* renderer, const mat4* model) { | ||
| 435 | assert(renderer); | ||
| 436 | assert(model); | ||
| 437 | |||
| 438 | renderer->matrix_stack[0] = *model; | ||
| 439 | renderer->stack_pointer = 0; | ||
| 440 | renderer->matrix_changed = true; | ||
| 441 | } | ||
diff --git a/src/render/llr_impl.h b/src/render/llr_impl.h new file mode 100644 index 0000000..9d70843 --- /dev/null +++ b/src/render/llr_impl.h | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/render/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 GfxCore GfxCore; | ||
| 14 | typedef struct IBL IBL; | ||
| 15 | typedef struct Material Material; | ||
| 16 | typedef struct ShaderProgram ShaderProgram; | ||
| 17 | typedef struct Texture Texture; | ||
| 18 | |||
| 19 | /// Immediate mode renderer. | ||
| 20 | /// | ||
| 21 | /// The renderer caches state changes in memory and only programs the underlying | ||
| 22 | /// shader program when a draw call is issued and if anything has changed. This | ||
| 23 | /// keeps the number of graphics API calls to a minimum, but requires tracking | ||
| 24 | /// state changes. The 'changed' booleans below fulfill this purpose, and | ||
| 25 | /// indicate whether a given state has changed since the last draw call. | ||
| 26 | /// | ||
| 27 | /// The renderer must combine state changes accordingly. For example, if only | ||
| 28 | /// the lights have changed, then it is sufficient to update light uniforms in | ||
| 29 | /// the current shader program. On the other hand, if the shader program has | ||
| 30 | /// changed, then the renderer must reconfigure it from scratch and set light | ||
| 31 | /// uniforms, camera uniforms, etc. | ||
| 32 | /// | ||
| 33 | /// Note that the shader program API has its own level of caching as well, so | ||
| 34 | /// reconfiguration at the level of the renderer does not result in the | ||
| 35 | /// worst-case set of graphics API calls. | ||
| 36 | typedef struct LLR { | ||
| 37 | GfxCore* gfxcore; | ||
| 38 | |||
| 39 | union { | ||
| 40 | struct { | ||
| 41 | bool shader_changed : 1; // Whether the shader has changed. | ||
| 42 | bool camera_changed : 1; // Whether the camera parameters have changed. | ||
| 43 | bool lights_changed : 1; // Whether the lights have changed. | ||
| 44 | bool skeleton_changed : 1; // Whether the skeleton has changed. | ||
| 45 | bool material_changed : 1; // Whether the material 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 | const Material* material; // Active material. Not owned. | ||
| 57 | |||
| 58 | vec3 camera_position; | ||
| 59 | mat4 camera_rotation; | ||
| 60 | mat4 view; // Camera view matrix. | ||
| 61 | mat4 projection; // Camera projection matrix. | ||
| 62 | R fovy; // Camera vertical field of view. | ||
| 63 | R aspect; // Aspect ratio. | ||
| 64 | |||
| 65 | // Lights are not const because environment lights store lazily-computed | ||
| 66 | // irradiance maps. | ||
| 67 | Light* lights[GFX_LLR_MAX_NUM_LIGHTS]; // Lights stack. | ||
| 68 | int num_lights; // Number of lights enabled at a given point in time. It | ||
| 69 | // points to one past the top of the stack. | ||
| 70 | |||
| 71 | size_t num_joints; | ||
| 72 | mat4 joint_matrices[GFX_MAX_NUM_JOINTS]; | ||
| 73 | |||
| 74 | // The matrix stack contains pre-multiplied matrices. | ||
| 75 | // It is also never empty. The top of the stack is an identity matrix when the | ||
| 76 | // stack is "empty" from the user's perspective. | ||
| 77 | mat4 matrix_stack[GFX_LLR_MAX_NUM_MATRICES]; | ||
| 78 | int stack_pointer; // Points to the top of the stack. | ||
| 79 | } LLR; | ||
| 80 | |||
| 81 | /// Create a new immediate mode renderer. | ||
| 82 | bool gfx_llr_make(LLR*, GfxCore*); | ||
| 83 | |||
| 84 | /// Destroy the immediate mode renderer. | ||
| 85 | void gfx_llr_destroy(LLR*); | ||
diff --git a/src/render/renderer.c b/src/render/renderer.c new file mode 100644 index 0000000..a9d9bef --- /dev/null +++ b/src/render/renderer.c | |||
| @@ -0,0 +1,282 @@ | |||
| 1 | #include "renderer_impl.h" | ||
| 2 | |||
| 3 | #include "animation_impl.h" | ||
| 4 | #include "llr_impl.h" | ||
| 5 | #include "memory.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 | |||
| 12 | #include <gfx/core.h> | ||
| 13 | #include <gfx/render/llr.h> | ||
| 14 | #include <gfx/util/shader.h> | ||
| 15 | |||
| 16 | #include <math/mat4.h> | ||
| 17 | |||
| 18 | #include <assert.h> | ||
| 19 | |||
| 20 | bool gfx_renderer_make(Renderer* renderer, LLR* llr, GfxCore* gfxcore) { | ||
| 21 | assert(renderer); | ||
| 22 | assert(llr); | ||
| 23 | assert(gfxcore); | ||
| 24 | |||
| 25 | renderer->gfxcore = gfxcore; | ||
| 26 | renderer->llr = llr; | ||
| 27 | |||
| 28 | return true; | ||
| 29 | } | ||
| 30 | |||
| 31 | void gfx_renderer_destroy(Renderer* renderer) { | ||
| 32 | if (!renderer) { | ||
| 33 | return; | ||
| 34 | } | ||
| 35 | assert(renderer->gfxcore); | ||
| 36 | GfxCore* gfxcore = renderer->gfxcore; | ||
| 37 | if (renderer->shaders.debug) { | ||
| 38 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.debug); | ||
| 39 | } | ||
| 40 | if (renderer->shaders.normals) { | ||
| 41 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.normals); | ||
| 42 | } | ||
| 43 | if (renderer->shaders.normal_mapped_normals) { | ||
| 44 | gfx_destroy_shader_program( | ||
| 45 | gfxcore, &renderer->shaders.normal_mapped_normals); | ||
| 46 | } | ||
| 47 | if (renderer->shaders.tangents) { | ||
| 48 | gfx_destroy_shader_program(gfxcore, &renderer->shaders.tangents); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | static ShaderProgram* load_shader(Renderer* renderer, RenderSceneMode mode) { | ||
| 53 | assert(renderer); | ||
| 54 | |||
| 55 | #define LOAD_AND_RETURN(pShader, constructor) \ | ||
| 56 | { \ | ||
| 57 | if (!pShader) { \ | ||
| 58 | pShader = constructor(renderer->gfxcore); \ | ||
| 59 | } \ | ||
| 60 | assert(pShader); \ | ||
| 61 | return pShader; \ | ||
| 62 | } | ||
| 63 | |||
| 64 | switch (mode) { | ||
| 65 | case RenderDefault: | ||
| 66 | return 0; | ||
| 67 | case RenderDebug: | ||
| 68 | LOAD_AND_RETURN(renderer->shaders.debug, gfx_make_debug3d_shader); | ||
| 69 | case RenderNormals: | ||
| 70 | LOAD_AND_RETURN(renderer->shaders.normals, gfx_make_view_normals_shader); | ||
| 71 | case RenderNormalMappedNormals: | ||
| 72 | LOAD_AND_RETURN( | ||
| 73 | renderer->shaders.normal_mapped_normals, | ||
| 74 | gfx_make_view_normal_mapped_normals_shader); | ||
| 75 | case RenderTangents: | ||
| 76 | LOAD_AND_RETURN(renderer->shaders.tangents, gfx_make_view_tangents_shader); | ||
| 77 | } | ||
| 78 | assert(false); | ||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | // static void log_matrix(const mat4* m) { | ||
| 83 | // for (int row = 0; row < 4; ++row) { | ||
| 84 | // LOGI("[ %5.2f, %5.2f, %5.2f, %5.2f ]", m->val[0][row], m->val[1][row], | ||
| 85 | // m->val[2][row], m->val[3][row]); | ||
| 86 | // } | ||
| 87 | // } | ||
| 88 | |||
| 89 | typedef struct RenderState { | ||
| 90 | GfxCore* gfxcore; | ||
| 91 | LLR* llr; | ||
| 92 | Renderer* renderer; | ||
| 93 | ShaderProgram* shader; // Null to use scene shaders. | ||
| 94 | const Scene* scene; | ||
| 95 | const Anima* anima; | ||
| 96 | RenderSceneFilter filter; | ||
| 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 | // Filter out by material. | ||
| 159 | const Material* material = mesh->material; | ||
| 160 | if (material) { | ||
| 161 | const AlphaMode mode = material->alpha_mode; | ||
| 162 | switch (state->filter) { | ||
| 163 | case RenderOpaqueAndAlphaMasked: | ||
| 164 | if (mode == Blend) { | ||
| 165 | continue; | ||
| 166 | } | ||
| 167 | break; | ||
| 168 | case RenderTransparent: | ||
| 169 | if (mode != Blend) { | ||
| 170 | continue; | ||
| 171 | } | ||
| 172 | break; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | // TODO: Here we would frustum-cull the mesh. The AABB would have to be | ||
| 177 | // transformed by the model matrix. Rotation would make the AABB | ||
| 178 | // relatively large, but still, the culling would be conservative. | ||
| 179 | |||
| 180 | ShaderProgram* shader = state->shader ? state->shader : mesh->shader; | ||
| 181 | gfx_llr_set_shader(state->llr, shader); | ||
| 182 | gfx_llr_set_model_matrix(state->llr, &model_matrix); | ||
| 183 | gfx_llr_render_mesh(state->llr, mesh); | ||
| 184 | } | ||
| 185 | |||
| 186 | if (object->skeleton.val) { | ||
| 187 | gfx_llr_clear_skeleton(state->llr); | ||
| 188 | } | ||
| 189 | |||
| 190 | draw_children(state, &node_transform, node); | ||
| 191 | } else { | ||
| 192 | draw_children(state, &node_transform, node); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | /// Draw the node's children. | ||
| 197 | static void draw_children( | ||
| 198 | RenderState* state, const mat4* node_transform, const SceneNode* node) { | ||
| 199 | // Render children recursively. | ||
| 200 | for (node_idx child_index = node->child; child_index.val;) { | ||
| 201 | const SceneNode* child = mem_get_node(child_index); | ||
| 202 | draw_recursively(state, *node_transform, child); | ||
| 203 | child_index = child->next; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) { | ||
| 208 | assert(renderer); | ||
| 209 | assert(params); | ||
| 210 | assert(params->scene); | ||
| 211 | |||
| 212 | ShaderProgram* const shader = load_shader(renderer, params->mode); | ||
| 213 | |||
| 214 | const Scene* scene = params->scene; | ||
| 215 | const Camera* camera = params->camera; | ||
| 216 | GfxCore* const gfxcore = renderer->gfxcore; | ||
| 217 | |||
| 218 | int x, y, width, height; | ||
| 219 | gfx_get_viewport(gfxcore, &x, &y, &width, &height); | ||
| 220 | const R aspect = (R)width / (R)height; | ||
| 221 | |||
| 222 | RenderState state = { | ||
| 223 | .gfxcore = gfxcore, | ||
| 224 | .llr = renderer->llr, | ||
| 225 | .renderer = renderer, | ||
| 226 | .shader = shader, | ||
| 227 | .scene = scene}; | ||
| 228 | |||
| 229 | gfx_llr_set_camera(renderer->llr, camera); | ||
| 230 | gfx_llr_set_aspect(renderer->llr, aspect); | ||
| 231 | // TODO: Render Opaque and Mask alpha-mode materials first, then Blend ones. | ||
| 232 | // TODO: I'm not sure if this belongs to the scene renderer per se, or if it | ||
| 233 | // is something that should be driven from the outside. Specifically, the | ||
| 234 | // caller could pass in a filter that determines what objects to render. The | ||
| 235 | // filter could include alpha mode. | ||
| 236 | // This caller would be some component that understands render passes and | ||
| 237 | // potentially renders the scene multiple times as needed. For example, a | ||
| 238 | // depth-prepass, followed by G-buffer, followed by some post-processing, | ||
| 239 | // etc. Rename this renderer to scene_renderer? | ||
| 240 | // TODO: When rendering transparent geometry, we need to turn off depth | ||
| 241 | // writes. | ||
| 242 | // Opaque. | ||
| 243 | state.filter = RenderOpaqueAndAlphaMasked; | ||
| 244 | draw_recursively(&state, mat4_id(), gfx_get_scene_root(scene)); | ||
| 245 | // Transparent. | ||
| 246 | state.filter = RenderTransparent; | ||
| 247 | draw_recursively(&state, mat4_id(), gfx_get_scene_root(scene)); | ||
| 248 | } | ||
| 249 | |||
| 250 | static void update_rec(SceneNode* node, const Camera* camera, R t) { | ||
| 251 | assert(node); | ||
| 252 | assert(camera); | ||
| 253 | |||
| 254 | const NodeType node_type = gfx_get_node_type(node); | ||
| 255 | |||
| 256 | // TODO: Models do not need to be animated if they are not visible to the | ||
| 257 | // camera. | ||
| 258 | if (node_type == AnimaNode) { | ||
| 259 | Anima* anima = gfx_get_node_anima_mut(node); | ||
| 260 | gfx_update_animation(anima, (R)t); | ||
| 261 | } else if (node_type == ModelNode) { | ||
| 262 | Model* model = gfx_get_node_model_mut(node); | ||
| 263 | SceneNode* root = gfx_get_model_root_mut(model); | ||
| 264 | update_rec(root, camera, t); | ||
| 265 | } | ||
| 266 | |||
| 267 | // Children. | ||
| 268 | SceneNode* child = gfx_get_node_child_mut(node); | ||
| 269 | while (child) { | ||
| 270 | update_rec(child, camera, t); | ||
| 271 | child = gfx_get_node_sibling_mut(child); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | // TODO: Move this outside the renderer. | ||
| 276 | void gfx_update(Scene* scene, const Camera* camera, R t) { | ||
| 277 | assert(scene); | ||
| 278 | assert(camera); | ||
| 279 | |||
| 280 | SceneNode* node = gfx_get_scene_root_mut(scene); | ||
| 281 | update_rec(node, camera, t); | ||
| 282 | } | ||
diff --git a/gfx/src/renderer/renderer_impl.h b/src/render/renderer_impl.h index fc14dcb..160ff52 100644 --- a/gfx/src/renderer/renderer_impl.h +++ b/src/render/renderer_impl.h | |||
| @@ -1,17 +1,15 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <gfx/renderer.h> | 3 | #include <gfx/render/renderer.h> |
| 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/src/scene/camera.c b/src/scene/camera.c new file mode 100644 index 0000000..fcfc496 --- /dev/null +++ b/src/scene/camera.c | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #include <gfx/scene.h> | ||
| 2 | |||
| 3 | #include "memory.h" | ||
| 4 | |||
| 5 | #include <assert.h> | ||
| 6 | #include <math/camera.h> | ||
| 7 | |||
| 8 | Camera* gfx_make_camera(void) { | ||
| 9 | Camera* camera = mem_alloc_camera(); | ||
| 10 | return camera; | ||
| 11 | } | ||
| 12 | |||
| 13 | void gfx_destroy_camera(Camera** camera) { | ||
| 14 | assert(camera); | ||
| 15 | if (*camera) { | ||
| 16 | mem_free_camera(camera); | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/gfx/src/scene/light.c b/src/scene/light.c index adbec8d..4233330 100644 --- a/gfx/src/scene/light.c +++ b/src/scene/light.c | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | #include "light_impl.h" | 1 | #include "light_impl.h" |
| 2 | 2 | ||
| 3 | #include "node_impl.h" | 3 | #include "memory.h" |
| 4 | #include "scene_memory.h" | ||
| 5 | 4 | ||
| 5 | #include <cassert.h> | ||
| 6 | #include <error.h> | 6 | #include <error.h> |
| 7 | 7 | ||
| 8 | static void make_environment_light( | 8 | static void make_environment_light( |
| @@ -34,9 +34,6 @@ Light* gfx_make_light(const LightDesc* desc) { | |||
| 34 | void gfx_destroy_light(Light** light) { | 34 | void gfx_destroy_light(Light** light) { |
| 35 | assert(light); | 35 | assert(light); |
| 36 | if (*light) { | 36 | if (*light) { |
| 37 | if ((*light)->parent.val) { | ||
| 38 | gfx_del_node((*light)->parent); | ||
| 39 | } | ||
| 40 | mem_free_light(light); | 37 | mem_free_light(light); |
| 41 | } | 38 | } |
| 42 | } | 39 | } |
diff --git a/gfx/src/scene/light_impl.h b/src/scene/light_impl.h index 1aa0bb4..3191a50 100644 --- a/gfx/src/scene/light_impl.h +++ b/src/scene/light_impl.h | |||
| @@ -1,10 +1,6 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <gfx/scene/light.h> | 3 | #include <gfx/scene.h> |
| 4 | |||
| 5 | #include "types.h" | ||
| 6 | |||
| 7 | typedef struct Texture Texture; | ||
| 8 | 4 | ||
| 9 | /// An environment light. | 5 | /// An environment light. |
| 10 | typedef struct EnvironmentLight { | 6 | typedef struct EnvironmentLight { |
| @@ -12,7 +8,7 @@ typedef struct EnvironmentLight { | |||
| 12 | const Texture* irradiance_map; // Renderer implementation. | 8 | const Texture* irradiance_map; // Renderer implementation. |
| 13 | const Texture* prefiltered_environment_map; // Renderer implementation. | 9 | const Texture* prefiltered_environment_map; // Renderer implementation. |
| 14 | int max_reflection_lod; // Mandatory when prefiltered_environment_map is | 10 | int max_reflection_lod; // Mandatory when prefiltered_environment_map is |
| 15 | // given. | 11 | // given. |
| 16 | } EnvironmentLight; | 12 | } EnvironmentLight; |
| 17 | 13 | ||
| 18 | /// A scene light. | 14 | /// A scene light. |
| @@ -21,5 +17,4 @@ typedef struct Light { | |||
| 21 | union { | 17 | union { |
| 22 | EnvironmentLight environment; | 18 | EnvironmentLight environment; |
| 23 | }; | 19 | }; |
| 24 | node_idx parent; // Parent SceneNode. | ||
| 25 | } Light; | 20 | } Light; |
diff --git a/src/scene/material.c b/src/scene/material.c new file mode 100644 index 0000000..9fe6c1b --- /dev/null +++ b/src/scene/material.c | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #include "material_impl.h" | ||
| 2 | |||
| 3 | #include "memory.h" | ||
| 4 | |||
| 5 | static void material_make(Material* material, const MaterialDesc* desc) { | ||
| 6 | assert(material); | ||
| 7 | assert(desc); | ||
| 8 | assert(desc->num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | ||
| 9 | material->alpha_mode = desc->alpha_mode; | ||
| 10 | material->alpha_cutoff = desc->alpha_cutoff; | ||
| 11 | material->num_uniforms = (int8_t)desc->num_uniforms; | ||
| 12 | for (int i = 0; i < desc->num_uniforms; ++i) { | ||
| 13 | material->uniforms[i] = desc->uniforms[i]; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | Material* gfx_make_material(const MaterialDesc* desc) { | ||
| 18 | assert(desc); | ||
| 19 | Material* material = mem_alloc_material(); | ||
| 20 | material_make(material, desc); | ||
| 21 | return material; | ||
| 22 | } | ||
| 23 | |||
| 24 | void gfx_destroy_material(Material** material) { mem_free_material(material); } | ||
diff --git a/src/scene/material_impl.h b/src/scene/material_impl.h new file mode 100644 index 0000000..488ffc7 --- /dev/null +++ b/src/scene/material_impl.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | #include <gfx/sizes.h> | ||
| 5 | |||
| 6 | typedef struct ShaderProgram ShaderProgram; | ||
| 7 | |||
| 8 | typedef struct Material { | ||
| 9 | AlphaMode alpha_mode; | ||
| 10 | float alpha_cutoff; | ||
| 11 | int8_t num_uniforms; | ||
| 12 | ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; | ||
| 13 | } Material; | ||
diff --git a/gfx/src/scene/mesh.c b/src/scene/mesh.c index 1a93bed..770c3fb 100644 --- a/gfx/src/scene/mesh.c +++ b/src/scene/mesh.c | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | #include "mesh_impl.h" | 1 | #include "mesh_impl.h" |
| 2 | 2 | ||
| 3 | #include "scene_memory.h" | 3 | #include <gfx/scene.h> |
| 4 | 4 | ||
| 5 | #include <assert.h> | 5 | #include "memory.h" |
| 6 | |||
| 7 | #include <cassert.h> | ||
| 6 | 8 | ||
| 7 | static void mesh_make(Mesh* mesh, const MeshDesc* desc) { | 9 | static void mesh_make(Mesh* mesh, const MeshDesc* desc) { |
| 8 | assert(mesh); | 10 | assert(mesh); |
diff --git a/src/scene/mesh_impl.h b/src/scene/mesh_impl.h new file mode 100644 index 0000000..c7e2211 --- /dev/null +++ b/src/scene/mesh_impl.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | typedef struct Geometry Geometry; | ||
| 4 | typedef struct Material Material; | ||
| 5 | typedef struct ShaderProgram ShaderProgram; | ||
| 6 | |||
| 7 | typedef struct Mesh { | ||
| 8 | const Geometry* geometry; | ||
| 9 | const Material* material; | ||
| 10 | ShaderProgram* shader; // TODO: Move this back to Material? | ||
| 11 | } Mesh; | ||
diff --git a/gfx/src/scene/model.c b/src/scene/model.c index cc41a9a..1bd0112 100644 --- a/gfx/src/scene/model.c +++ b/src/scene/model.c | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | #include "model_impl.h" | 1 | #include "model_impl.h" |
| 2 | 2 | ||
| 3 | #include <gfx/scene/node.h> | 3 | #include <gfx/scene.h> |
| 4 | 4 | ||
| 5 | #include "scene_memory.h" | 5 | #include "memory.h" |
| 6 | 6 | ||
| 7 | #include <assert.h> | 7 | #include <assert.h> |
| 8 | 8 | ||
diff --git a/gfx/src/scene/model_impl.h b/src/scene/model_impl.h index a99d32c..72cd0ab 100644 --- a/gfx/src/scene/model_impl.h +++ b/src/scene/model_impl.h | |||
| @@ -1,13 +1,12 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <gfx/scene/model.h> | 3 | #include <gfx/scene.h> |
| 4 | 4 | ||
| 5 | #include "scene_memory.h" | 5 | #include "memory.h" |
| 6 | 6 | ||
| 7 | /// Model. | 7 | /// Model. |
| 8 | typedef struct Model { | 8 | typedef struct Model { |
| 9 | node_idx root; | 9 | node_idx root; |
| 10 | node_idx parent; // Parent SceneNode. | ||
| 11 | } Model; | 10 | } Model; |
| 12 | 11 | ||
| 13 | /// Create a new model. | 12 | /// Create a new model. |
diff --git a/gfx/src/scene/node.c b/src/scene/node.c index 67ce93c..0004d27 100644 --- a/gfx/src/scene/node.c +++ b/src/scene/node.c | |||
| @@ -1,15 +1,15 @@ | |||
| 1 | #include "node_impl.h" | 1 | #include "node_impl.h" |
| 2 | 2 | ||
| 3 | #include "animation_impl.h" | 3 | #include "animation_impl.h" |
| 4 | #include "camera_impl.h" | 4 | #include "memory.h" |
| 5 | #include "light_impl.h" | ||
| 6 | #include "model_impl.h" | ||
| 7 | #include "object_impl.h" | 5 | #include "object_impl.h" |
| 6 | #include "render/llr_impl.h" | ||
| 8 | #include "scene_graph.h" | 7 | #include "scene_graph.h" |
| 9 | #include "scene_memory.h" | ||
| 10 | 8 | ||
| 11 | #include "gfx_assert.h" | 9 | #include "gfx_assert.h" |
| 12 | 10 | ||
| 11 | #include <gfx/scene.h> | ||
| 12 | |||
| 13 | #include <cstring.h> | 13 | #include <cstring.h> |
| 14 | #include <log/log.h> | 14 | #include <log/log.h> |
| 15 | 15 | ||
| @@ -19,7 +19,7 @@ static void scene_node_make(SceneNode* node) { | |||
| 19 | node->transform = mat4_id(); | 19 | node->transform = mat4_id(); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | SceneNode* gfx_make_node() { | 22 | SceneNode* gfx_make_node(void) { |
| 23 | SceneNode* node = mem_alloc_node(); | 23 | SceneNode* node = mem_alloc_node(); |
| 24 | scene_node_make(node); | 24 | scene_node_make(node); |
| 25 | return node; | 25 | return node; |
| @@ -30,16 +30,14 @@ SceneNode* gfx_make_anima_node(Anima* anima) { | |||
| 30 | SceneNode* node = gfx_make_node(); | 30 | SceneNode* node = gfx_make_node(); |
| 31 | node->type = AnimaNode; | 31 | node->type = AnimaNode; |
| 32 | node->anima = mem_get_anima_index(anima); | 32 | node->anima = mem_get_anima_index(anima); |
| 33 | anima->parent = mem_get_node_index(node); | ||
| 34 | return node; | 33 | return node; |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | SceneNode* gfx_make_camera_node(SceneCamera* camera) { | 36 | SceneNode* gfx_make_camera_node(Camera* camera) { |
| 38 | assert(camera); | 37 | assert(camera); |
| 39 | SceneNode* node = gfx_make_node(); | 38 | SceneNode* node = gfx_make_node(); |
| 40 | node->type = CameraNode; | 39 | node->type = CameraNode; |
| 41 | node->camera = mem_get_camera_index(camera); | 40 | node->camera = mem_get_camera_index(camera); |
| 42 | camera->parent = mem_get_node_index(node); | ||
| 43 | return node; | 41 | return node; |
| 44 | } | 42 | } |
| 45 | 43 | ||
| @@ -48,7 +46,6 @@ SceneNode* gfx_make_light_node(Light* light) { | |||
| 48 | SceneNode* node = gfx_make_node(); | 46 | SceneNode* node = gfx_make_node(); |
| 49 | node->type = LightNode; | 47 | node->type = LightNode; |
| 50 | node->light = mem_get_light_index(light); | 48 | node->light = mem_get_light_index(light); |
| 51 | light->parent = mem_get_node_index(node); | ||
| 52 | return node; | 49 | return node; |
| 53 | } | 50 | } |
| 54 | 51 | ||
| @@ -57,7 +54,6 @@ SceneNode* gfx_make_model_node(Model* model) { | |||
| 57 | SceneNode* node = gfx_make_node(); | 54 | SceneNode* node = gfx_make_node(); |
| 58 | node->type = ModelNode; | 55 | node->type = ModelNode; |
| 59 | node->model = mem_get_model_index(model); | 56 | node->model = mem_get_model_index(model); |
| 60 | model->parent = mem_get_node_index(node); | ||
| 61 | return node; | 57 | return node; |
| 62 | } | 58 | } |
| 63 | 59 | ||
| @@ -66,7 +62,6 @@ SceneNode* gfx_make_object_node(SceneObject* object) { | |||
| 66 | SceneNode* node = gfx_make_node(); | 62 | SceneNode* node = gfx_make_node(); |
| 67 | node->type = ObjectNode; | 63 | node->type = ObjectNode; |
| 68 | node->object = mem_get_object_index(object); | 64 | node->object = mem_get_object_index(object); |
| 69 | object->parent = mem_get_node_index(node); | ||
| 70 | return node; | 65 | return node; |
| 71 | } | 66 | } |
| 72 | 67 | ||
| @@ -74,24 +69,19 @@ SceneNode* gfx_make_object_node(SceneObject* object) { | |||
| 74 | static void free_node_resource(SceneNode* node) { | 69 | static void free_node_resource(SceneNode* node) { |
| 75 | assert(node); | 70 | assert(node); |
| 76 | 71 | ||
| 77 | // Set the resource's parent node back to 0 to avoid a recursive call into | ||
| 78 | // gfx_del_node(). | ||
| 79 | switch (node->type) { | 72 | switch (node->type) { |
| 80 | case AnimaNode: { | 73 | case AnimaNode: { |
| 81 | Anima* anima = mem_get_anima(node->anima); | 74 | Anima* anima = mem_get_anima(node->anima); |
| 82 | anima->parent.val = 0; | ||
| 83 | gfx_destroy_anima(&anima); | 75 | gfx_destroy_anima(&anima); |
| 84 | return; | 76 | return; |
| 85 | } | 77 | } |
| 86 | case CameraNode: { | 78 | case CameraNode: { |
| 87 | SceneCamera* camera = mem_get_camera(node->camera); | 79 | Camera* camera = mem_get_camera(node->camera); |
| 88 | camera->parent.val = 0; | ||
| 89 | gfx_destroy_camera(&camera); | 80 | gfx_destroy_camera(&camera); |
| 90 | return; | 81 | return; |
| 91 | } | 82 | } |
| 92 | case LightNode: { | 83 | case LightNode: { |
| 93 | Light* light = mem_get_light(node->light); | 84 | Light* light = mem_get_light(node->light); |
| 94 | light->parent.val = 0; | ||
| 95 | gfx_destroy_light(&light); | 85 | gfx_destroy_light(&light); |
| 96 | return; | 86 | return; |
| 97 | } | 87 | } |
| @@ -100,63 +90,16 @@ static void free_node_resource(SceneNode* node) { | |||
| 100 | } | 90 | } |
| 101 | case ObjectNode: { | 91 | case ObjectNode: { |
| 102 | SceneObject* object = mem_get_object(node->object); | 92 | SceneObject* object = mem_get_object(node->object); |
| 103 | object->parent.val = 0; | ||
| 104 | gfx_destroy_object(&object); | 93 | gfx_destroy_object(&object); |
| 105 | return; | 94 | return; |
| 106 | } | 95 | } |
| 107 | case LogicalNode: | 96 | case LogicalNode: { |
| 108 | return; // Logical nodes have no resource. | 97 | return; // Logical nodes have no resource. |
| 109 | } | 98 | } |
| 99 | } | ||
| 110 | FAIL("unhandled node type"); | 100 | FAIL("unhandled node type"); |
| 111 | } | 101 | } |
| 112 | 102 | ||
| 113 | void gfx_construct_anima_node(SceneNode* node, Anima* anima) { | ||
| 114 | assert(node); | ||
| 115 | assert(anima); | ||
| 116 | free_node_resource(node); | ||
| 117 | node->type = AnimaNode; | ||
| 118 | node->anima = mem_get_anima_index(anima); | ||
| 119 | anima->parent = mem_get_node_index(node); | ||
| 120 | } | ||
| 121 | |||
| 122 | void gfx_construct_camera_node(SceneNode* node, SceneCamera* camera) { | ||
| 123 | assert(node); | ||
| 124 | assert(camera); | ||
| 125 | free_node_resource(node); | ||
| 126 | node->type = CameraNode; | ||
| 127 | node->camera = mem_get_camera_index(camera); | ||
| 128 | camera->parent = mem_get_node_index(node); | ||
| 129 | } | ||
| 130 | |||
| 131 | // TODO: Add a common helper function between each gfx_make_xyz_node() and | ||
| 132 | // gfx_construct_xyz_node() pair. | ||
| 133 | void gfx_construct_light_node(SceneNode* node, Light* light) { | ||
| 134 | assert(node); | ||
| 135 | assert(light); | ||
| 136 | free_node_resource(node); | ||
| 137 | node->type = LightNode; | ||
| 138 | node->light = mem_get_light_index(light); | ||
| 139 | light->parent = mem_get_node_index(node); | ||
| 140 | } | ||
| 141 | |||
| 142 | void gfx_construct_model_node(SceneNode* node, Model* model) { | ||
| 143 | assert(node); | ||
| 144 | assert(model); | ||
| 145 | free_node_resource(node); | ||
| 146 | node->type = ModelNode; | ||
| 147 | node->model = mem_get_model_index(model); | ||
| 148 | model->parent = mem_get_node_index(node); | ||
| 149 | } | ||
| 150 | |||
| 151 | void gfx_construct_object_node(SceneNode* node, SceneObject* object) { | ||
| 152 | assert(node); | ||
| 153 | assert(object); | ||
| 154 | free_node_resource(node); | ||
| 155 | node->type = ObjectNode; | ||
| 156 | node->object = mem_get_object_index(object); | ||
| 157 | object->parent = mem_get_node_index(node); | ||
| 158 | } | ||
| 159 | |||
| 160 | static void destroy_node_rec(SceneNode* node) { | 103 | static void destroy_node_rec(SceneNode* node) { |
| 161 | assert(node); | 104 | assert(node); |
| 162 | 105 | ||
| @@ -217,11 +160,11 @@ Anima* gfx_get_node_anima_mut(SceneNode* node) { | |||
| 217 | NODE_GET(node, anima, AnimaNode); | 160 | NODE_GET(node, anima, AnimaNode); |
| 218 | } | 161 | } |
| 219 | 162 | ||
| 220 | const SceneCamera* gfx_get_node_camera(const SceneNode* node) { | 163 | const Camera* gfx_get_node_camera(const SceneNode* node) { |
| 221 | NODE_GET(node, camera, CameraNode); | 164 | NODE_GET(node, camera, CameraNode); |
| 222 | } | 165 | } |
| 223 | 166 | ||
| 224 | SceneCamera* gfx_get_node_camera_mut(SceneNode* node) { | 167 | Camera* gfx_get_node_camera_mut(SceneNode* node) { |
| 225 | NODE_GET(node, camera, CameraNode); | 168 | NODE_GET(node, camera, CameraNode); |
| 226 | } | 169 | } |
| 227 | 170 | ||
diff --git a/gfx/src/scene/node_impl.h b/src/scene/node_impl.h index c79f252..9e65588 100644 --- a/gfx/src/scene/node_impl.h +++ b/src/scene/node_impl.h | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <gfx/scene/node.h> | 3 | #include <gfx/scene.h> |
| 4 | 4 | ||
| 5 | #include "types.h" | 5 | #include "../types.h" |
| 6 | 6 | ||
| 7 | #include <cstring.h> | 7 | #include <math/camera.h> |
| 8 | #include <math/mat4.h> | 8 | #include <math/mat4.h> |
| 9 | 9 | ||
| 10 | /// Scene node. | 10 | /// Scene node. |
diff --git a/gfx/src/scene/object.c b/src/scene/object.c index e8e3ee6..ac86b39 100644 --- a/gfx/src/scene/object.c +++ b/src/scene/object.c | |||
| @@ -2,9 +2,10 @@ | |||
| 2 | 2 | ||
| 3 | #include <gfx/core.h> | 3 | #include <gfx/core.h> |
| 4 | 4 | ||
| 5 | #include "mesh_impl.h" | 5 | #include "memory.h" |
| 6 | #include "node_impl.h" | 6 | #include "render/llr_impl.h" |
| 7 | #include "scene_memory.h" | 7 | #include "scene/mesh_impl.h" |
| 8 | #include "scene/node_impl.h" | ||
| 8 | 9 | ||
| 9 | #include <assert.h> | 10 | #include <assert.h> |
| 10 | 11 | ||
| @@ -57,11 +58,7 @@ SceneObject* gfx_make_object(const ObjectDesc* desc) { | |||
| 57 | 58 | ||
| 58 | void gfx_destroy_object(SceneObject** object) { | 59 | void gfx_destroy_object(SceneObject** object) { |
| 59 | assert(object); | 60 | assert(object); |
| 60 | |||
| 61 | if (*object) { | 61 | if (*object) { |
| 62 | if ((*object)->parent.val) { | ||
| 63 | gfx_del_node((*object)->parent); | ||
| 64 | } | ||
| 65 | mem_free_object(object); | 62 | mem_free_object(object); |
| 66 | } | 63 | } |
| 67 | } | 64 | } |
diff --git a/gfx/src/scene/object_impl.h b/src/scene/object_impl.h index 88f8e31..345d615 100644 --- a/gfx/src/scene/object_impl.h +++ b/src/scene/object_impl.h | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <gfx/scene/object.h> | 3 | #include <gfx/scene.h> |
| 4 | 4 | ||
| 5 | #include "types.h" | 5 | #include "../types.h" |
| 6 | |||
| 7 | #include <math/mat4.h> | ||
| 8 | 6 | ||
| 9 | typedef struct MeshLink { | 7 | typedef struct MeshLink { |
| 10 | mesh_idx mesh; | 8 | mesh_idx mesh; |
| @@ -21,6 +19,5 @@ 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. | ||
| 25 | aabb3 box; | 22 | aabb3 box; |
| 26 | } SceneObject; | 23 | } SceneObject; |
diff --git a/src/scene/scene.c b/src/scene/scene.c new file mode 100644 index 0000000..52ddb58 --- /dev/null +++ b/src/scene/scene.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #include "scene_impl.h" | ||
| 2 | |||
| 3 | #include "memory.h" | ||
| 4 | #include "node_impl.h" | ||
| 5 | |||
| 6 | #include <assert.h> | ||
| 7 | |||
| 8 | Scene* gfx_make_scene(void) { | ||
| 9 | Scene* scene = mem_alloc_scene(); | ||
| 10 | scene->root = mem_get_node_index(gfx_make_node()); | ||
| 11 | return scene; | ||
| 12 | } | ||
| 13 | |||
| 14 | void gfx_destroy_scene(Scene** scene) { | ||
| 15 | assert(scene); | ||
| 16 | if (*scene) { | ||
| 17 | SceneNode* node = mem_get_node((*scene)->root); | ||
| 18 | gfx_destroy_node(&node); | ||
| 19 | mem_free_scene(scene); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | const SceneNode* gfx_get_scene_root(const Scene* scene) { | ||
| 24 | assert(scene); | ||
| 25 | return mem_get_node(scene->root); | ||
| 26 | } | ||
| 27 | |||
| 28 | SceneNode* gfx_get_scene_root_mut(Scene* scene) { | ||
| 29 | assert(scene); | ||
| 30 | return (SceneNode*)gfx_get_scene_root(scene); | ||
| 31 | } | ||
diff --git a/gfx/src/scene/scene_graph.h b/src/scene/scene_graph.h index a26f828..36c3a98 100644 --- a/gfx/src/scene/scene_graph.h +++ b/src/scene/scene_graph.h | |||
| @@ -1,40 +1,46 @@ | |||
| 1 | /// Functions for list manipulation. | 1 | /// Functions for list manipulation. |
| 2 | #pragma once | 2 | #pragma once |
| 3 | 3 | ||
| 4 | #include "scene_memory.h" | 4 | #include "memory.h" |
| 5 | 5 | ||
| 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 | Camera *: 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/src/scene/scene_impl.h b/src/scene/scene_impl.h new file mode 100644 index 0000000..ad2e892 --- /dev/null +++ b/src/scene/scene_impl.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene.h> | ||
| 4 | |||
| 5 | #include "../types.h" | ||
| 6 | |||
| 7 | typedef struct Scene { | ||
| 8 | node_idx root; | ||
| 9 | } Scene; | ||
diff --git a/gfx/src/scene/types.h b/src/types.h index d0ffc41..d0ffc41 100644 --- a/gfx/src/scene/types.h +++ b/src/types.h | |||
diff --git a/gfx/src/util/geometry.c b/src/util/geometry.c index afe0109..2ea0c82 100644 --- a/gfx/src/util/geometry.c +++ b/src/util/geometry.c | |||
| @@ -17,12 +17,13 @@ static void make_quad_01_positions(vec2 positions[4]) { | |||
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | static GeometryDesc make_quad_desc(vec2 positions[4]) { | 19 | static GeometryDesc make_quad_desc(vec2 positions[4]) { |
| 20 | GeometryDesc desc = (GeometryDesc){0}; | 20 | return (GeometryDesc){ |
| 21 | desc.positions2d.data = positions; | 21 | .positions2d = (BufferView2d){.data = positions, |
| 22 | desc.positions2d.size_bytes = 4 * sizeof(vec2); | 22 | .size_bytes = 4 * sizeof(vec2), |
| 23 | desc.num_verts = 4; | 23 | .count = 4}, |
| 24 | desc.type = TriangleStrip; | 24 | .num_verts = 4, |
| 25 | return desc; | 25 | .type = TriangleStrip |
| 26 | }; | ||
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | Geometry* gfx_make_quad_11(GfxCore* gfxcore) { | 29 | Geometry* gfx_make_quad_11(GfxCore* gfxcore) { |
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..f4561f2 100644 --- a/gfx/src/util/skyquad.c +++ b/src/util/skyquad.c | |||
| @@ -1,18 +1,11 @@ | |||
| 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/render/llr.h> |
| 5 | #include <gfx/scene/light.h> | 5 | #include <gfx/scene.h> |
| 6 | #include <gfx/scene/material.h> | ||
| 7 | #include <gfx/scene/mesh.h> | ||
| 8 | #include <gfx/scene/node.h> | ||
| 9 | #include <gfx/scene/object.h> | ||
| 10 | #include <gfx/scene/scene.h> | ||
| 11 | #include <gfx/util/geometry.h> | 6 | #include <gfx/util/geometry.h> |
| 12 | #include <gfx/util/shader.h> | 7 | #include <gfx/util/shader.h> |
| 13 | 8 | ||
| 14 | #include <math/vec4.h> | ||
| 15 | |||
| 16 | #include <assert.h> | 9 | #include <assert.h> |
| 17 | 10 | ||
| 18 | SceneObject* gfx_make_skyquad(GfxCore* gfxcore, const Texture* texture) { | 11 | SceneObject* gfx_make_skyquad(GfxCore* gfxcore, const Texture* texture) { |
| @@ -36,10 +29,9 @@ SceneObject* gfx_make_skyquad(GfxCore* gfxcore, const Texture* texture) { | |||
| 36 | } | 29 | } |
| 37 | 30 | ||
| 38 | MaterialDesc material_desc = (MaterialDesc){0}; | 31 | MaterialDesc material_desc = (MaterialDesc){0}; |
| 39 | material_desc.uniforms[0] = (ShaderUniform){ | 32 | material_desc.uniforms[0] = (ShaderUniform){.type = UniformTexture, |
| 40 | .type = UniformTexture, | 33 | .value.texture = texture, |
| 41 | .value.texture = texture, | 34 | .name = sstring_make("Skyquad")}; |
| 42 | .name = sstring_make("Skyquad")}; | ||
| 43 | material_desc.num_uniforms = 1; | 35 | material_desc.num_uniforms = 1; |
| 44 | material = gfx_make_material(&material_desc); | 36 | material = gfx_make_material(&material_desc); |
| 45 | if (!material) { | 37 | if (!material) { |
