aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt91
-rw-r--r--README.md (renamed from gfx/README.md)0
-rwxr-xr-xbin/txt2c.py (renamed from gfx/bin/txt2c.py)0
-rw-r--r--cmake/shader.txt (renamed from gfx/cmake/shader.txt)0
-rw-r--r--cmake/txt2c.txt (renamed from gfx/cmake/txt2c.txt)0
-rw-r--r--contrib/cgltf-tangents/CMakeLists.txt (renamed from gfx/contrib/cgltf-tangents/CMakeLists.txt)0
-rw-r--r--contrib/cgltf-tangents/LICENSE (renamed from gfx/contrib/cgltf-tangents/LICENSE)0
-rw-r--r--contrib/cgltf-tangents/MikkTSpace/README.md (renamed from gfx/contrib/cgltf-tangents/MikkTSpace/README.md)0
-rw-r--r--contrib/cgltf-tangents/MikkTSpace/mikktspace.c (renamed from gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.c)0
-rw-r--r--contrib/cgltf-tangents/MikkTSpace/mikktspace.h (renamed from gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.h)0
-rw-r--r--contrib/cgltf-tangents/README.md (renamed from gfx/contrib/cgltf-tangents/README.md)0
-rw-r--r--contrib/cgltf-tangents/cgltf_tangents.c (renamed from gfx/contrib/cgltf-tangents/cgltf_tangents.c)0
-rw-r--r--contrib/cgltf-tangents/cgltf_tangents.h (renamed from gfx/contrib/cgltf-tangents/cgltf_tangents.h)0
-rw-r--r--contrib/cgltf-tangents/test/CMakeLists.txt (renamed from gfx/contrib/cgltf-tangents/test/CMakeLists.txt)0
-rw-r--r--contrib/cgltf-tangents/test/main.c (renamed from gfx/contrib/cgltf-tangents/test/main.c)0
-rw-r--r--contrib/cgltf/CMakeLists.txt (renamed from gfx/contrib/cgltf/CMakeLists.txt)0
-rw-r--r--contrib/cgltf/LICENSE (renamed from gfx/contrib/cgltf/LICENSE)0
-rw-r--r--contrib/cgltf/README.md (renamed from gfx/contrib/cgltf/README.md)0
-rw-r--r--contrib/cgltf/cgltf.h (renamed from gfx/contrib/cgltf/cgltf.h)0
-rw-r--r--contrib/cgltf/cgltf_write.h (renamed from gfx/contrib/cgltf/cgltf_write.h)0
-rw-r--r--contrib/stb/CMakeLists.txt (renamed from gfx/contrib/stb/CMakeLists.txt)0
-rw-r--r--contrib/stb/stb_image.h (renamed from gfx/contrib/stb/stb_image.h)0
-rw-r--r--doc/extern/2013SiggraphPresentationsNotes-26915738.pdf (renamed from gfx/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf)bin2947929 -> 2947929 bytes
-rw-r--r--doc/extern/Scene Graph - CSE 167.pdf (renamed from gfx/doc/extern/Scene Graph - CSE 167.pdf)bin890801 -> 890801 bytes
-rw-r--r--doc/gfx.png (renamed from gfx/doc/gfx.png)bin6272 -> 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)bin4757973 -> 4757973 bytes
-rw-r--r--doc/pipeline.png (renamed from gfx/doc/pipeline.png)bin10318 -> 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)bin13144 -> 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)bin59119 -> 59119 bytes
-rw-r--r--doc/scene.txt (renamed from gfx/doc/scene.txt)0
-rw-r--r--game/CMakeLists.txt22
-rw-r--r--game/src/game.c218
-rw-r--r--game/src/game.h21
-rw-r--r--game/src/plugins/CMakeLists.txt29
-rw-r--r--game/src/plugins/plugin.h52
-rw-r--r--game/src/plugins/pong.c237
-rw-r--r--game/src/plugins/texture_view.c144
-rw-r--r--game/src/plugins/viewer.c374
-rw-r--r--gfx-iso/CMakeLists.txt42
-rw-r--r--gfx-iso/demos/CMakeLists.txt2
-rw-r--r--gfx-iso/demos/checkerboard/CMakeLists.txt16
-rw-r--r--gfx-iso/demos/checkerboard/checkerboard.c166
-rw-r--r--gfx-iso/demos/isomap/CMakeLists.txt16
-rw-r--r--gfx-iso/demos/isomap/isomap.c105
-rw-r--r--gfx-iso/include/isogfx/backend.h28
-rw-r--r--gfx-iso/include/isogfx/isogfx.h136
-rw-r--r--gfx-iso/src/backend.c199
-rw-r--r--gfx-iso/src/isogfx.c952
-rw-r--r--gfx-iso/tools/mkasset.py324
-rw-r--r--gfx/CMakeLists.txt89
-rw-r--r--include/gfx/asset.h (renamed from gfx/include/gfx/asset.h)0
-rw-r--r--include/gfx/core.h (renamed from gfx/include/gfx/core.h)0
-rw-r--r--include/gfx/gfx.h (renamed from gfx/include/gfx/gfx.h)0
-rw-r--r--include/gfx/renderer.h (renamed from gfx/include/gfx/renderer.h)0
-rw-r--r--include/gfx/scene.h (renamed from gfx/include/gfx/scene.h)0
-rw-r--r--include/gfx/scene/animation.h (renamed from gfx/include/gfx/scene/animation.h)0
-rw-r--r--include/gfx/scene/camera.h (renamed from gfx/include/gfx/scene/camera.h)0
-rw-r--r--include/gfx/scene/light.h (renamed from gfx/include/gfx/scene/light.h)0
-rw-r--r--include/gfx/scene/material.h (renamed from gfx/include/gfx/scene/material.h)0
-rw-r--r--include/gfx/scene/mesh.h (renamed from gfx/include/gfx/scene/mesh.h)0
-rw-r--r--include/gfx/scene/model.h (renamed from gfx/include/gfx/scene/model.h)0
-rw-r--r--include/gfx/scene/node.h (renamed from gfx/include/gfx/scene/node.h)0
-rw-r--r--include/gfx/scene/object.h (renamed from gfx/include/gfx/scene/object.h)0
-rw-r--r--include/gfx/scene/scene.h (renamed from gfx/include/gfx/scene/scene.h)0
-rw-r--r--include/gfx/sizes.h (renamed from gfx/include/gfx/sizes.h)0
-rw-r--r--include/gfx/util/geometry.h (renamed from gfx/include/gfx/util/geometry.h)0
-rw-r--r--include/gfx/util/ibl.h (renamed from gfx/include/gfx/util/ibl.h)0
-rw-r--r--include/gfx/util/shader.h (renamed from gfx/include/gfx/util/shader.h)0
-rw-r--r--include/gfx/util/skyquad.h (renamed from gfx/include/gfx/util/skyquad.h)0
-rw-r--r--shaders/brdf_integration_map.frag (renamed from gfx/shaders/brdf_integration_map.frag)0
-rw-r--r--shaders/cook_torrance.frag (renamed from gfx/shaders/cook_torrance.frag)0
-rw-r--r--shaders/cook_torrance.vert (renamed from gfx/shaders/cook_torrance.vert)0
-rw-r--r--shaders/cubemap_filtering.vert (renamed from gfx/shaders/cubemap_filtering.vert)0
-rw-r--r--shaders/debug3d.frag (renamed from gfx/shaders/debug3d.frag)0
-rw-r--r--shaders/debug3d.vert (renamed from gfx/shaders/debug3d.vert)0
-rw-r--r--shaders/immediate_mode.frag (renamed from gfx/shaders/immediate_mode.frag)0
-rw-r--r--shaders/immediate_mode.vert (renamed from gfx/shaders/immediate_mode.vert)0
-rw-r--r--shaders/irradiance_map.frag (renamed from gfx/shaders/irradiance_map.frag)0
-rw-r--r--shaders/prefiltered_environment_map.frag (renamed from gfx/shaders/prefiltered_environment_map.frag)0
-rw-r--r--shaders/quad.vert (renamed from gfx/shaders/quad.vert)0
-rw-r--r--shaders/skyquad.frag (renamed from gfx/shaders/skyquad.frag)0
-rw-r--r--shaders/skyquad.vert (renamed from gfx/shaders/skyquad.vert)0
-rw-r--r--shaders/view_normal_mapped_normals.frag (renamed from gfx/shaders/view_normal_mapped_normals.frag)0
-rw-r--r--shaders/view_normal_mapped_normals.vert (renamed from gfx/shaders/view_normal_mapped_normals.vert)0
-rw-r--r--shaders/view_normals.frag (renamed from gfx/shaders/view_normals.frag)0
-rw-r--r--shaders/view_normals.vert (renamed from gfx/shaders/view_normals.vert)0
-rw-r--r--shaders/view_tangents.frag (renamed from gfx/shaders/view_tangents.frag)0
-rw-r--r--shaders/view_tangents.vert (renamed from gfx/shaders/view_tangents.vert)0
-rw-r--r--shaders/view_texture.frag (renamed from gfx/shaders/view_texture.frag)0
-rw-r--r--shaders/view_texture.vert (renamed from gfx/shaders/view_texture.vert)0
-rw-r--r--src/asset/asset_cache.c (renamed from gfx/src/asset/asset_cache.c)0
-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)0
-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)0
-rw-r--r--src/asset/texture.h (renamed from gfx/src/asset/texture.h)0
-rw-r--r--src/core/buffer.c (renamed from gfx/src/core/buffer.c)0
-rw-r--r--src/core/buffer.h (renamed from gfx/src/core/buffer.h)0
-rw-r--r--src/core/constants.h (renamed from gfx/src/core/constants.h)0
-rw-r--r--src/core/core.c (renamed from gfx/src/core/core.c)0
-rw-r--r--src/core/core_impl.h (renamed from gfx/src/core/core_impl.h)0
-rw-r--r--src/core/framebuffer.c (renamed from gfx/src/core/framebuffer.c)0
-rw-r--r--src/core/framebuffer.h (renamed from gfx/src/core/framebuffer.h)0
-rw-r--r--src/core/geometry.c (renamed from gfx/src/core/geometry.c)0
-rw-r--r--src/core/geometry.h (renamed from gfx/src/core/geometry.h)0
-rw-r--r--src/core/gl_util.h (renamed from gfx/src/core/gl_util.h)0
-rw-r--r--src/core/renderbuffer.c (renamed from gfx/src/core/renderbuffer.c)0
-rw-r--r--src/core/renderbuffer.h (renamed from gfx/src/core/renderbuffer.h)0
-rw-r--r--src/core/shader.c (renamed from gfx/src/core/shader.c)0
-rw-r--r--src/core/shader.h (renamed from gfx/src/core/shader.h)0
-rw-r--r--src/core/shader_program.c (renamed from gfx/src/core/shader_program.c)0
-rw-r--r--src/core/shader_program.h (renamed from gfx/src/core/shader_program.h)0
-rw-r--r--src/core/texture.c (renamed from gfx/src/core/texture.c)0
-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)0
-rw-r--r--src/gfx_assert.h (renamed from gfx/src/gfx_assert.h)0
-rw-r--r--src/renderer/imm_renderer.c (renamed from gfx/src/renderer/imm_renderer.c)0
-rw-r--r--src/renderer/imm_renderer_impl.h (renamed from gfx/src/renderer/imm_renderer_impl.h)0
-rw-r--r--src/renderer/renderer.c (renamed from gfx/src/renderer/renderer.c)0
-rw-r--r--src/renderer/renderer_impl.h (renamed from gfx/src/renderer/renderer_impl.h)0
-rw-r--r--src/scene/animation.c (renamed from gfx/src/scene/animation.c)0
-rw-r--r--src/scene/animation_impl.h (renamed from gfx/src/scene/animation_impl.h)0
-rw-r--r--src/scene/camera.c (renamed from gfx/src/scene/camera.c)0
-rw-r--r--src/scene/camera_impl.h (renamed from gfx/src/scene/camera_impl.h)0
-rw-r--r--src/scene/light.c (renamed from gfx/src/scene/light.c)0
-rw-r--r--src/scene/light_impl.h (renamed from gfx/src/scene/light_impl.h)0
-rw-r--r--src/scene/material.c (renamed from gfx/src/scene/material.c)0
-rw-r--r--src/scene/material_impl.h (renamed from gfx/src/scene/material_impl.h)0
-rw-r--r--src/scene/mesh.c (renamed from gfx/src/scene/mesh.c)0
-rw-r--r--src/scene/mesh_impl.h (renamed from gfx/src/scene/mesh_impl.h)0
-rw-r--r--src/scene/model.c (renamed from gfx/src/scene/model.c)0
-rw-r--r--src/scene/model_impl.h (renamed from gfx/src/scene/model_impl.h)0
-rw-r--r--src/scene/node.c (renamed from gfx/src/scene/node.c)0
-rw-r--r--src/scene/node_impl.h (renamed from gfx/src/scene/node_impl.h)0
-rw-r--r--src/scene/object.c (renamed from gfx/src/scene/object.c)0
-rw-r--r--src/scene/object_impl.h (renamed from gfx/src/scene/object_impl.h)0
-rw-r--r--src/scene/scene.c (renamed from gfx/src/scene/scene.c)0
-rw-r--r--src/scene/scene_graph.h (renamed from gfx/src/scene/scene_graph.h)0
-rw-r--r--src/scene/scene_impl.h (renamed from gfx/src/scene/scene_impl.h)0
-rw-r--r--src/scene/scene_memory.c (renamed from gfx/src/scene/scene_memory.c)0
-rw-r--r--src/scene/scene_memory.h (renamed from gfx/src/scene/scene_memory.h)0
-rw-r--r--src/scene/types.h (renamed from gfx/src/scene/types.h)0
-rw-r--r--src/util/geometry.c (renamed from gfx/src/util/geometry.c)0
-rw-r--r--src/util/ibl.c (renamed from gfx/src/util/ibl.c)0
-rw-r--r--src/util/shader.c (renamed from gfx/src/util/shader.c)0
-rw-r--r--src/util/skyquad.c (renamed from gfx/src/util/skyquad.c)0
149 files changed, 87 insertions, 3176 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 25c7560..b13f28b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,8 +1,91 @@
1cmake_minimum_required(VERSION 3.0) 1cmake_minimum_required(VERSION 3.0)
2 2
3project(gfx-all) 3include(cmake/shader.txt)
4
5add_subdirectory(contrib/cgltf)
6add_subdirectory(contrib/cgltf-tangents)
7add_subdirectory(contrib/stb)
4 8
5add_subdirectory(app) 9add_subdirectory(app)
6add_subdirectory(gfx) 10
7add_subdirectory(gfx-iso) 11project(gfx)
8add_subdirectory(game) 12
13add_shader_library(shaders
14 shaders/brdf_integration_map.frag
15 shaders/cook_torrance.frag
16 shaders/cook_torrance.vert
17 shaders/cubemap_filtering.vert
18 shaders/debug3d.frag
19 shaders/debug3d.vert
20 shaders/immediate_mode.frag
21 shaders/immediate_mode.vert
22 shaders/irradiance_map.frag
23 shaders/prefiltered_environment_map.frag
24 shaders/quad.vert
25 shaders/skyquad.frag
26 shaders/skyquad.vert
27 shaders/view_normal_mapped_normals.frag
28 shaders/view_normal_mapped_normals.vert
29 shaders/view_normals.frag
30 shaders/view_normals.vert
31 shaders/view_tangents.frag
32 shaders/view_tangents.vert
33 shaders/view_texture.frag
34 shaders/view_texture.vert)
35
36add_library(gfx SHARED
37 src/asset/asset_cache.c
38 src/asset/model.c
39 src/asset/texture.c
40 src/core/buffer.c
41 src/core/core.c
42 src/core/framebuffer.c
43 src/core/geometry.c
44 src/core/renderbuffer.c
45 src/core/shader_program.c
46 src/core/shader.c
47 src/core/texture.c
48 src/renderer/imm_renderer.c
49 src/renderer/renderer.c
50 src/scene/animation.c
51 src/scene/camera.c
52 src/scene/light.c
53 src/scene/material.c
54 src/scene/mesh.c
55 src/scene/model.c
56 src/scene/node.c
57 src/scene/object.c
58 src/scene/scene.c
59 src/scene/scene_memory.c
60 src/gfx.c
61 src/util/geometry.c
62 src/util/ibl.c
63 src/util/shader.c
64 src/util/skyquad.c)
65
66target_include_directories(gfx PUBLIC
67 include)
68
69target_include_directories(gfx PRIVATE
70 src)
71
72target_compile_options(gfx PRIVATE -std=gnu11 -Wall -Wextra -Wpedantic)
73
74target_link_libraries(gfx PUBLIC
75 cstring
76 math)
77
78target_link_libraries(gfx PRIVATE
79 cassert
80 cgltf
81 cgltf-tangents
82 error
83 gfx-app
84 log
85 mempool
86 shaders
87 stb
88 # System libraries.
89 GL
90 # Required to initialize GLAD.
91 -ldl)
diff --git a/gfx/README.md b/README.md
index f0b103d..f0b103d 100644
--- a/gfx/README.md
+++ b/README.md
diff --git a/gfx/bin/txt2c.py b/bin/txt2c.py
index 1d8ff70..1d8ff70 100755
--- a/gfx/bin/txt2c.py
+++ b/bin/txt2c.py
diff --git a/gfx/cmake/shader.txt b/cmake/shader.txt
index 8273f4d..8273f4d 100644
--- a/gfx/cmake/shader.txt
+++ b/cmake/shader.txt
diff --git a/gfx/cmake/txt2c.txt b/cmake/txt2c.txt
index 0cb11a9..0cb11a9 100644
--- a/gfx/cmake/txt2c.txt
+++ b/cmake/txt2c.txt
diff --git a/gfx/contrib/cgltf-tangents/CMakeLists.txt b/contrib/cgltf-tangents/CMakeLists.txt
index 2c0771e..2c0771e 100644
--- a/gfx/contrib/cgltf-tangents/CMakeLists.txt
+++ b/contrib/cgltf-tangents/CMakeLists.txt
diff --git a/gfx/contrib/cgltf-tangents/LICENSE b/contrib/cgltf-tangents/LICENSE
index 7796e37..7796e37 100644
--- a/gfx/contrib/cgltf-tangents/LICENSE
+++ b/contrib/cgltf-tangents/LICENSE
diff --git a/gfx/contrib/cgltf-tangents/MikkTSpace/README.md b/contrib/cgltf-tangents/MikkTSpace/README.md
index 9fda155..9fda155 100644
--- a/gfx/contrib/cgltf-tangents/MikkTSpace/README.md
+++ b/contrib/cgltf-tangents/MikkTSpace/README.md
diff --git a/gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.c b/contrib/cgltf-tangents/MikkTSpace/mikktspace.c
index 0342ae0..0342ae0 100644
--- a/gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.c
+++ b/contrib/cgltf-tangents/MikkTSpace/mikktspace.c
diff --git a/gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.h b/contrib/cgltf-tangents/MikkTSpace/mikktspace.h
index 52c44a7..52c44a7 100644
--- a/gfx/contrib/cgltf-tangents/MikkTSpace/mikktspace.h
+++ b/contrib/cgltf-tangents/MikkTSpace/mikktspace.h
diff --git a/gfx/contrib/cgltf-tangents/README.md b/contrib/cgltf-tangents/README.md
index 2a68b27..2a68b27 100644
--- a/gfx/contrib/cgltf-tangents/README.md
+++ b/contrib/cgltf-tangents/README.md
diff --git a/gfx/contrib/cgltf-tangents/cgltf_tangents.c b/contrib/cgltf-tangents/cgltf_tangents.c
index 80b1e56..80b1e56 100644
--- a/gfx/contrib/cgltf-tangents/cgltf_tangents.c
+++ b/contrib/cgltf-tangents/cgltf_tangents.c
diff --git a/gfx/contrib/cgltf-tangents/cgltf_tangents.h b/contrib/cgltf-tangents/cgltf_tangents.h
index 79e3502..79e3502 100644
--- a/gfx/contrib/cgltf-tangents/cgltf_tangents.h
+++ b/contrib/cgltf-tangents/cgltf_tangents.h
diff --git a/gfx/contrib/cgltf-tangents/test/CMakeLists.txt b/contrib/cgltf-tangents/test/CMakeLists.txt
index 422c950..422c950 100644
--- a/gfx/contrib/cgltf-tangents/test/CMakeLists.txt
+++ b/contrib/cgltf-tangents/test/CMakeLists.txt
diff --git a/gfx/contrib/cgltf-tangents/test/main.c b/contrib/cgltf-tangents/test/main.c
index 0d70008..0d70008 100644
--- a/gfx/contrib/cgltf-tangents/test/main.c
+++ b/contrib/cgltf-tangents/test/main.c
diff --git a/gfx/contrib/cgltf/CMakeLists.txt b/contrib/cgltf/CMakeLists.txt
index 0ac840a..0ac840a 100644
--- a/gfx/contrib/cgltf/CMakeLists.txt
+++ b/contrib/cgltf/CMakeLists.txt
diff --git a/gfx/contrib/cgltf/LICENSE b/contrib/cgltf/LICENSE
index 0afe8c7..0afe8c7 100644
--- a/gfx/contrib/cgltf/LICENSE
+++ b/contrib/cgltf/LICENSE
diff --git a/gfx/contrib/cgltf/README.md b/contrib/cgltf/README.md
index 3b49d52..3b49d52 100644
--- a/gfx/contrib/cgltf/README.md
+++ b/contrib/cgltf/README.md
diff --git a/gfx/contrib/cgltf/cgltf.h b/contrib/cgltf/cgltf.h
index 077cf36..077cf36 100644
--- a/gfx/contrib/cgltf/cgltf.h
+++ b/contrib/cgltf/cgltf.h
diff --git a/gfx/contrib/cgltf/cgltf_write.h b/contrib/cgltf/cgltf_write.h
index 2096a5b..2096a5b 100644
--- a/gfx/contrib/cgltf/cgltf_write.h
+++ b/contrib/cgltf/cgltf_write.h
diff --git a/gfx/contrib/stb/CMakeLists.txt b/contrib/stb/CMakeLists.txt
index 8cee003..8cee003 100644
--- a/gfx/contrib/stb/CMakeLists.txt
+++ b/contrib/stb/CMakeLists.txt
diff --git a/gfx/contrib/stb/stb_image.h b/contrib/stb/stb_image.h
index 97038e6..97038e6 100644
--- a/gfx/contrib/stb/stb_image.h
+++ b/contrib/stb/stb_image.h
diff --git a/gfx/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf b/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf
index 989658e..989658e 100644
--- a/gfx/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf
+++ b/doc/extern/2013SiggraphPresentationsNotes-26915738.pdf
Binary files differ
diff --git a/gfx/doc/extern/Scene Graph - CSE 167.pdf b/doc/extern/Scene Graph - CSE 167.pdf
index 5fbbb10..5fbbb10 100644
--- a/gfx/doc/extern/Scene Graph - CSE 167.pdf
+++ b/doc/extern/Scene Graph - CSE 167.pdf
Binary files differ
diff --git a/gfx/doc/gfx.png b/doc/gfx.png
index e64f6e1..e64f6e1 100644
--- a/gfx/doc/gfx.png
+++ b/doc/gfx.png
Binary files differ
diff --git a/gfx/doc/gfx.txt b/doc/gfx.txt
index d3ce01b..d3ce01b 100644
--- a/gfx/doc/gfx.txt
+++ b/doc/gfx.txt
diff --git a/gfx/doc/gltfOverview-2.0.0b.png b/doc/gltfOverview-2.0.0b.png
index 6a5bb61..6a5bb61 100644
--- a/gfx/doc/gltfOverview-2.0.0b.png
+++ b/doc/gltfOverview-2.0.0b.png
Binary files differ
diff --git a/gfx/doc/pipeline.png b/doc/pipeline.png
index 426f39e..426f39e 100644
--- a/gfx/doc/pipeline.png
+++ b/doc/pipeline.png
Binary files differ
diff --git a/gfx/doc/pipeline.txt b/doc/pipeline.txt
index 51523d6..51523d6 100644
--- a/gfx/doc/pipeline.txt
+++ b/doc/pipeline.txt
diff --git a/gfx/doc/renderer.png b/doc/renderer.png
index d0516b0..d0516b0 100644
--- a/gfx/doc/renderer.png
+++ b/doc/renderer.png
Binary files differ
diff --git a/gfx/doc/renderer.txt b/doc/renderer.txt
index 90b18f8..90b18f8 100644
--- a/gfx/doc/renderer.txt
+++ b/doc/renderer.txt
diff --git a/gfx/doc/scene.png b/doc/scene.png
index 85d2447..85d2447 100644
--- a/gfx/doc/scene.png
+++ b/doc/scene.png
Binary files differ
diff --git a/gfx/doc/scene.txt b/doc/scene.txt
index a771488..a771488 100644
--- a/gfx/doc/scene.txt
+++ b/doc/scene.txt
diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt
deleted file mode 100644
index 3a88bb7..0000000
--- a/game/CMakeLists.txt
+++ /dev/null
@@ -1,22 +0,0 @@
1cmake_minimum_required(VERSION 3.0)
2
3add_subdirectory(src/plugins)
4
5project(game)
6
7add_executable(game
8 src/game.c)
9
10target_include_directories(game PRIVATE
11 src/)
12
13target_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 51f5cbe..0000000
--- a/game/src/game.c
+++ /dev/null
@@ -1,218 +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
18#include <error.h>
19#include <log/log.h>
20#include <plugin.h>
21
22#include <assert.h>
23#include <stdbool.h>
24#include <stdio.h>
25#include <stdlib.h>
26
27#include <linux/limits.h>
28
29#include <unistd.h>
30
31#undef _GNU_SOURCE
32
33static const int WIDTH = 1350;
34static const int HEIGHT = 900;
35static const int MAX_FPS = 60;
36
37typedef struct GfxAppState {
38 Game game;
39} GfxAppState;
40
41/// Initialize the game's plugin.
42static bool init_plugin(Game* game) {
43 assert(game);
44 assert(game->plugin);
45 // Plugin state is allowed to be null, either when the plugin does not
46 // expose an init() or when init() does not initialize a state.
47 if (plugin_resolve(game->plugin, plugin_init, "init")) {
48 State* plugin_state = 0;
49 if (!plugin_call(game->plugin, plugin_init, "init", game, &plugin_state)) {
50 return false;
51 }
52 set_plugin_state(game->plugin, plugin_state);
53 }
54 return true; // Plugin does not need to expose an init().
55}
56
57/// Shutdown the game's plugin.
58/// The game's plugin is allowed to be null in the call to this function.
59static void shutdown_plugin(Game* game) {
60 assert(game);
61 if (game->plugin &&
62 (plugin_resolve(game->plugin, plugin_shutdown, "shutdown"))) {
63 void* plugin_state = get_plugin_state(game->plugin);
64 plugin_call(game->plugin, plugin_shutdown, "shutdown", game, plugin_state);
65 set_plugin_state(game->plugin, 0);
66 }
67}
68
69/// Boot the game's plugin.
70static bool boot_plugin(Game* game) {
71 assert(game);
72 assert(game->plugin);
73 if (plugin_resolve(game->plugin, plugin_boot, "boot")) {
74 void* plugin_state = get_plugin_state(game->plugin);
75 return plugin_call(game->plugin, plugin_boot, "boot", game, plugin_state);
76 }
77 return true; // Plugin does not need to expose a boot().
78}
79
80/// Update the plugin's state.
81static void update_plugin(Game* game, double t, double dt) {
82 assert(game);
83 assert(game->plugin);
84 if (plugin_resolve(game->plugin, plugin_update, "update")) {
85 void* plugin_state = get_plugin_state(game->plugin);
86 plugin_call(
87 game->plugin, plugin_update, "update", game, plugin_state, t, dt);
88 }
89}
90
91/// Plugin render.
92static void render_plugin(const Game* game) {
93 assert(game);
94 assert(game->plugin);
95 if (plugin_resolve(game->plugin, plugin_render, "render")) {
96 void* plugin_state = get_plugin_state(game->plugin);
97 plugin_call(game->plugin, plugin_render, "render", game, plugin_state);
98 }
99}
100
101/// Plugin resize.
102static void resize_plugin(Game* game, int width, int height) {
103 assert(game);
104 assert(game->plugin);
105 if (plugin_resolve(game->plugin, plugin_resize, "resize")) {
106 void* plugin_state = get_plugin_state(game->plugin);
107 plugin_call(
108 game->plugin, plugin_resize, "resize", game, plugin_state, width,
109 height);
110 }
111}
112
113static void Shutdown(Game* game);
114
115static bool Init(Game* game, int argc, const char** argv) {
116 assert(game);
117
118 if (argc <= 1) {
119 LOGE("Usage: %s <plugin> [plugin args]", argv[0]);
120 return false;
121 }
122
123 // Syntax: game <plugin> [plugin args]
124 //
125 // Here we consume the <plugin> arg so that plugins receive the remainder
126 // args starting from 0.
127 game->argc = argc - 1;
128 game->argv = argv + 1;
129
130 char exe_path_buf[NAME_MAX] = {0};
131 if (readlink("/proc/self/exe", exe_path_buf, sizeof(exe_path_buf)) == -1) {
132 LOGE("readlink(/proc/self/exe) failed");
133 goto cleanup;
134 }
135
136 // Replace the last / with a null terminator to remove the exe file from the
137 // path. This gets the file's parent directory.
138 *strrchr(exe_path_buf, '/') = 0;
139
140 const mstring exe_dir = mstring_make(exe_path_buf);
141 const mstring plugins_path = mstring_concat_cstr(exe_dir, "/src/plugins");
142
143 if (!(game->plugin_engine = new_plugin_engine(
144 &(PluginEngineDesc){.plugins_dir = mstring_cstr(&plugins_path)}))) {
145 goto cleanup;
146 }
147
148 const char* plugin = argv[1];
149 if (!(game->plugin = load_plugin(game->plugin_engine, plugin))) {
150 goto cleanup;
151 }
152
153 if (!(game->gfx = gfx_init())) {
154 goto cleanup;
155 }
156
157 if (!init_plugin(game)) {
158 goto cleanup;
159 }
160 if (!boot_plugin(game)) {
161 goto cleanup;
162 }
163
164 return true;
165
166cleanup:
167 LOGE("Gfx error: %s", get_error());
168 Shutdown(game);
169 return false;
170}
171
172static void Shutdown(Game* game) {
173 assert(game);
174 shutdown_plugin(game);
175 if (game->gfx) {
176 gfx_destroy(&game->gfx);
177 }
178 if (game->plugin) {
179 delete_plugin(&game->plugin);
180 }
181 if (game->plugin_engine) {
182 delete_plugin_engine(&game->plugin_engine);
183 }
184}
185
186static void Update(Game* game, double t, double dt) {
187 plugin_engine_update(game->plugin_engine);
188 if (plugin_reloaded(game->plugin)) {
189 shutdown_plugin(game);
190 const bool result = init_plugin(game);
191 assert(result); // TODO: handle error better.
192
193 // Trigger a resize just like the initial resize that occurs when the gfx
194 // application starts.
195 resize_plugin(game, game->width, game->height);
196 }
197
198 update_plugin(game, t, dt);
199}
200
201static void Render(const Game* game) {
202 GfxCore* gfxcore = gfx_get_core(game->gfx);
203 gfx_start_frame(gfxcore);
204 render_plugin(game);
205 gfx_end_frame(gfxcore);
206}
207
208static void Resize(Game* game, int width, int height) {
209 game->width = width;
210 game->height = height;
211
212 GfxCore* gfxcore = gfx_get_core(game->gfx);
213 gfx_set_viewport(gfxcore, 0, 0, width, height);
214
215 resize_plugin(game, width, height);
216}
217
218GFX_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
6typedef struct PluginEngine PluginEngine;
7typedef struct Plugin Plugin;
8typedef struct Gfx Gfx;
9typedef struct Scene Scene;
10typedef struct SceneCamera SceneCamera;
11
12/// Game state.
13typedef 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 @@
1cmake_minimum_required(VERSION 3.0)
2
3project(plugins)
4
5set(LINK_LIBRARIES cstring math gfx gfx-app)
6
7# Viewer
8
9add_library(viewer SHARED
10 viewer.c)
11
12target_link_libraries(viewer PUBLIC
13 ${LINK_LIBRARIES})
14
15# Texture viewer
16
17add_library(texture_view SHARED
18 texture_view.c)
19
20target_link_libraries(texture_view PUBLIC
21 ${LINK_LIBRARIES})
22
23# Pong
24
25add_library(pong SHARED
26 pong.c)
27
28target_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
13typedef 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.)
22bool 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.
30void 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.
35bool boot(Game*, State*);
36
37/// Update the plugin's and the game's state.
38void update(Game*, State*, double t, double dt);
39
40/// Render hook.
41void render(const Game*, const State*);
42
43/// Called when the game's window is resized.
44void resize(Game*, State*, int width, int height);
45
46// Signatures for the plugin's exposed functions.
47typedef bool (*plugin_init)(Game*, State**);
48typedef bool (*plugin_shutdown)(Game*, State*);
49typedef bool (*plugin_boot)(Game*, State*);
50typedef void (*plugin_update)(Game*, State*, double t, double dt);
51typedef void (*plugin_render)(const Game*, const State*);
52typedef 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
13static const vec2 PAD_SIZE = (vec2){120, 20};
14static const R PLAYER_Y_OFFSET = 50;
15static const R PLAYER_SPEED = 800;
16
17static const R ENEMY_SPEED = 2;
18
19static const R BALL_SIZE = 18;
20static const R BALL_SPEED = 360; // In each dimension.
21
22static const R EPS = (R)1e-3;
23
24typedef struct Player {
25 vec2 position;
26} Player;
27
28typedef struct Ball {
29 vec2 position;
30 vec2 velocity;
31} Ball;
32
33typedef struct State {
34 bool game_started;
35 Player human;
36 Player enemy;
37 Ball ball;
38 mat4 viewProjection;
39} State;
40
41bool 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
52cleanup:
53 free(state);
54 return false;
55}
56
57void shutdown(Game* game, State* state) {
58 assert(game);
59 assert(state);
60}
61
62static 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
91void 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
97void 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
111void 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
126void 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
154void 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
168static 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
181static 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
193void 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
208static R clamp_to_width(int width, R x, R extent) {
209 return min(x, (R)width - extent);
210}
211
212void 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.
16static const char* DEFAULT_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp";
17// static const char* DEFAULT_TEXTURE = "/assets/checkerboard.jpg";
18
19struct State {
20 Scene* scene;
21 SceneCamera* camera;
22};
23
24bool 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
103cleanup:
104 shutdown(game, state);
105 if (state) {
106 free(state);
107 }
108 return false;
109}
110
111void 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
120void 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
132void 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 23b9ffb..0000000
--- a/game/src/plugins/viewer.c
+++ /dev/null
@@ -1,374 +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// Skybox.
16static const char* skybox[6] = {
17 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_east.bmp",
18 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_west.bmp",
19 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_up.bmp",
20 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_down.bmp",
21 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_south.bmp",
22 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_north.bmp",
23};
24
25// Paths to various scene files.
26static const char* BOX = "/home/jeanne/Nextcloud/assets/models/box.gltf";
27static const char* SUZANNE =
28 "/home/jeanne/Nextcloud/assets/models/suzanne.gltf";
29static const char* SPONZA = "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/"
30 "2.0/Sponza/glTF/Sponza.gltf";
31static const char* FLIGHT_HELMET =
32 "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/"
33 "FlightHelmet.gltf";
34static const char* DAMAGED_HELMET =
35 "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/"
36 "DamagedHelmet.gltf";
37static const char* GIRL =
38 "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf";
39static const char* BOXES =
40 "/home/jeanne/Nextcloud/assets/models/boxes/boxes.gltf";
41
42#define DEFAULT_SCENE_FILE GIRL
43
44static const bool RenderBoundingBoxes = false;
45static const R DefaultCameraSpeed = (R)6.0;
46static const R DefaultMouseSensitivity = (R)(10 * TO_RAD);
47static const vec3 DefaultCameraPosition = (vec3){0, 2, 5};
48
49typedef struct CameraCommand {
50 bool CameraMoveLeft : 1;
51 bool CameraMoveRight : 1;
52 bool CameraMoveForward : 1;
53 bool CameraMoveBackward : 1;
54} CameraCommand;
55
56typedef struct CameraController {
57 R camera_speed; // Camera movement speed.
58 R mouse_sensitivity; // Controls the degree with which mouse movements
59 // rotate the camera.
60 vec2 prev_mouse_position; // Mouse position in the previous frame.
61 bool rotating; // When true, subsequent mouse movements cause the
62 // camera to rotate.
63} CameraController;
64
65typedef struct State {
66 Scene* scene;
67 Model* model;
68 SceneCamera* camera;
69 CameraController camera_controller;
70} State;
71
72/// Load the skyquad texture.
73static const Texture* load_environment_map(Gfx* gfx) {
74 assert(gfx);
75 return gfx_load_texture(
76 gfx, &(LoadTextureCmd){
77 .origin = AssetFromFile,
78 .type = LoadCubemap,
79 .colour_space = sRGB,
80 .filtering = NearestFiltering,
81 .mipmaps = false,
82 .data.cubemap.filepaths = {
83 mstring_make(skybox[0]), mstring_make(skybox[1]),
84 mstring_make(skybox[2]), mstring_make(skybox[3]),
85 mstring_make(skybox[4]), mstring_make(skybox[5])}
86 });
87}
88
89/// Load the skyquad and return the environment light node.
90static SceneNode* load_skyquad(Gfx* gfx, SceneNode* root) {
91 assert(gfx);
92 assert(root);
93
94 GfxCore* gfxcore = gfx_get_core(gfx);
95
96 const Texture* environment_map = load_environment_map(gfx);
97 if (!environment_map) {
98 return 0;
99 }
100
101 return gfx_setup_skyquad(gfxcore, root, environment_map);
102}
103
104/// Load the 3D scene.
105/// Return the loaded model.
106static Model* load_scene(Game* game, State* state, const char* scene_filepath) {
107 assert(game);
108 assert(game->gfx);
109 assert(state);
110 assert(state->scene);
111
112 Camera* camera = gfx_get_camera_camera(state->camera);
113 spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2));
114
115 SceneNode* root = gfx_get_scene_root(state->scene);
116 SceneNode* sky_light_node = load_skyquad(game->gfx, root);
117 if (!sky_light_node) {
118 return 0; // test
119 }
120
121 Model* model = gfx_load_model(
122 game->gfx, &(LoadModelCmd){.origin = AssetFromFile,
123 .filepath = mstring_make(scene_filepath)});
124 if (!model) {
125 return 0;
126 }
127 SceneNode* model_node = gfx_make_model_node(model);
128 if (!model_node) {
129 return 0;
130 }
131 gfx_set_node_parent(model_node, sky_light_node);
132
133 gfx_log_node_hierarchy(root);
134
135 return model;
136}
137
138bool init(Game* game, State** pp_state) {
139 assert(game);
140
141 // Usage: <scene file>
142 const char* scene_filepath =
143 game->argc > 1 ? game->argv[1] : DEFAULT_SCENE_FILE;
144
145 State* state = calloc(1, sizeof(State));
146 if (!state) {
147 goto cleanup;
148 }
149
150 if (!(state->scene = gfx_make_scene())) {
151 goto cleanup;
152 }
153 if (!(state->camera = gfx_make_camera())) {
154 goto cleanup;
155 }
156
157 state->model = load_scene(game, state, scene_filepath);
158 if (!state->model) {
159 goto cleanup;
160 }
161
162 Anima* anima = gfx_get_model_anima(state->model);
163 if (anima) {
164 gfx_play_animation(
165 anima, &(AnimationPlaySettings){.name = "Walk", .loop = true});
166 // TODO: Interpolate animations.
167 /*gfx_play_animation(
168 anima,
169 &(AnimationPlaySettings){.name = "Jumping-jack-lower", .loop = true});
170 gfx_play_animation(
171 anima, &(AnimationPlaySettings){
172 .name = "Jumping-jack-arms-mid", .loop = true});*/
173 }
174
175 spatial3_set_position(
176 &gfx_get_camera_camera(state->camera)->spatial, DefaultCameraPosition);
177
178 state->camera_controller.camera_speed = DefaultCameraSpeed;
179 state->camera_controller.mouse_sensitivity = DefaultMouseSensitivity;
180
181 *pp_state = state;
182 return true;
183
184cleanup:
185 shutdown(game, state);
186 if (state) {
187 free(state);
188 }
189 return false;
190}
191
192void shutdown(Game* game, State* state) {
193 assert(game);
194 if (state) {
195 gfx_destroy_camera(&state->camera);
196 gfx_destroy_scene(&state->scene);
197 // State freed by plugin engine.
198 }
199}
200
201static void update_camera(
202 CameraController* controller, R dt, vec2 mouse_position,
203 CameraCommand command, Spatial3* camera) {
204 assert(controller);
205 assert(camera);
206
207 // Translation.
208 const R move_x = (R)(command.CameraMoveLeft ? -1 : 0) +
209 (R)(command.CameraMoveRight ? 1 : 0);
210 const R move_y = (R)(command.CameraMoveForward ? 1 : 0) +
211 (R)(command.CameraMoveBackward ? -1 : 0);
212 const vec2 translation =
213 vec2_scale(vec2_make(move_x, move_y), controller->camera_speed * dt);
214 spatial3_move_right(camera, translation.x);
215 spatial3_move_forwards(camera, translation.y);
216
217 // Rotation.
218 if (controller->rotating) {
219 const vec2 mouse_delta =
220 vec2_sub(mouse_position, controller->prev_mouse_position);
221
222 const vec2 rotation =
223 vec2_scale(mouse_delta, controller->mouse_sensitivity * dt);
224
225 spatial3_global_yaw(camera, -rotation.x);
226 spatial3_pitch(camera, -rotation.y);
227 }
228
229 // Update controller state.
230 controller->prev_mouse_position = mouse_position;
231}
232
233void update(Game* game, State* state, double t, double dt) {
234 assert(game);
235 assert(state);
236 assert(state->scene);
237 assert(state->camera);
238
239 double mouse_x, mouse_y;
240 gfx_app_get_mouse_position(&mouse_x, &mouse_y);
241 const vec2 mouse_position = {(R)mouse_x, (R)mouse_y};
242
243 const CameraCommand camera_command = (CameraCommand){
244 .CameraMoveLeft = gfx_app_is_key_pressed(KeyA),
245 .CameraMoveRight = gfx_app_is_key_pressed(KeyD),
246 .CameraMoveForward = gfx_app_is_key_pressed(KeyW),
247 .CameraMoveBackward = gfx_app_is_key_pressed(KeyS),
248 };
249
250 state->camera_controller.rotating = gfx_app_is_mouse_button_pressed(LMB);
251
252 update_camera(
253 &state->camera_controller, (R)dt, mouse_position, camera_command,
254 &gfx_get_camera_camera(state->camera)->spatial);
255
256 // const vec3 orbit_point = vec3_make(0, 2, 0);
257 // Camera* camera = gfx_get_camera_camera(state->camera);
258 // spatial3_orbit(
259 // &camera->spatial, orbit_point,
260 // /*radius=*/5,
261 // /*azimuth=*/(R)(t * 0.5), /*zenith=*/0);
262 // spatial3_lookat(&camera->spatial, orbit_point);
263
264 gfx_update(state->scene, state->camera, (R)t);
265}
266
267/// Render the bounding boxes of all scene objects.
268static void render_bounding_boxes_rec(
269 ImmRenderer* imm, const Anima* anima, const mat4* parent_model_matrix,
270 const SceneNode* node) {
271 assert(imm);
272 assert(node);
273
274 const mat4 model_matrix =
275 mat4_mul(*parent_model_matrix, gfx_get_node_transform(node));
276
277 const NodeType node_type = gfx_get_node_type(node);
278
279 if (node_type == ModelNode) {
280 const Model* model = gfx_get_node_model(node);
281 const SceneNode* root = gfx_get_model_root(model);
282 render_bounding_boxes_rec(imm, anima, &model_matrix, root);
283 } else if (node_type == AnimaNode) {
284 anima = gfx_get_node_anima(node);
285 } else if (node_type == ObjectNode) {
286 gfx_imm_set_model_matrix(imm, &model_matrix);
287
288 const SceneObject* obj = gfx_get_node_object(node);
289 const Skeleton* skeleton = gfx_get_object_skeleton(obj);
290
291 if (skeleton) { // Animated model.
292 assert(anima);
293 const size_t num_joints = gfx_get_skeleton_num_joints(skeleton);
294 for (size_t i = 0; i < num_joints; ++i) {
295 if (gfx_joint_has_box(anima, skeleton, i)) {
296 const Box box = gfx_get_joint_box(anima, skeleton, i);
297 gfx_imm_draw_box3(imm, box.vertices);
298 }
299 }
300 } else { // Static model.
301 const aabb3 box = gfx_get_object_aabb(obj);
302 gfx_imm_draw_aabb3(imm, box);
303 }
304 }
305
306 // Render children's boxes.
307 const SceneNode* child = gfx_get_node_child(node);
308 while (child) {
309 render_bounding_boxes_rec(imm, anima, &model_matrix, child);
310 child = gfx_get_node_sibling(child);
311 }
312}
313
314/// Render the bounding boxes of all scene objects.
315static void render_bounding_boxes(const Game* game, const State* state) {
316 assert(game);
317 assert(state);
318
319 GfxCore* gfxcore = gfx_get_core(game->gfx);
320 ImmRenderer* imm = gfx_get_imm_renderer(game->gfx);
321 assert(gfxcore);
322 assert(imm);
323
324 const mat4 id = mat4_id();
325 Anima* anima = 0;
326
327 gfx_set_blending(gfxcore, true);
328 gfx_set_depth_mask(gfxcore, false);
329 gfx_set_polygon_offset(gfxcore, -1.5f, -1.0f);
330
331 gfx_imm_start(imm);
332 gfx_imm_set_camera(imm, gfx_get_camera_camera(state->camera));
333 gfx_imm_set_colour(imm, vec4_make(0.3, 0.3, 0.9, 0.1));
334 render_bounding_boxes_rec(imm, anima, &id, gfx_get_scene_root(state->scene));
335 gfx_imm_end(imm);
336
337 gfx_reset_polygon_offset(gfxcore);
338 gfx_set_depth_mask(gfxcore, true);
339 gfx_set_blending(gfxcore, false);
340}
341
342void render(const Game* game, const State* state) {
343 assert(state);
344 assert(game);
345 assert(game->gfx);
346 assert(state->scene);
347 assert(state->camera);
348
349 Renderer* renderer = gfx_get_renderer(game->gfx);
350 assert(renderer);
351
352 gfx_render_scene(
353 renderer, &(RenderSceneParams){.mode = RenderDefault,
354 .scene = state->scene,
355 .camera = state->camera});
356
357 if (RenderBoundingBoxes) {
358 render_bounding_boxes(game, state);
359 }
360}
361
362void resize(Game* game, State* state, int width, int height) {
363 assert(game);
364 assert(state);
365
366 const R fovy = 60 * TO_RAD;
367 const R aspect = (R)width / (R)height;
368 const R near = 0.1;
369 const R far = 1000;
370 const mat4 projection = mat4_perspective(fovy, aspect, near, far);
371
372 Camera* camera = gfx_get_camera_camera(state->camera);
373 camera->projection = projection;
374}
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 @@
1cmake_minimum_required(VERSION 3.0)
2
3project(isogfx)
4
5set(CMAKE_C_STANDARD 17)
6set(CMAKE_C_STANDARD_REQUIRED On)
7set(CMAKE_C_EXTENSIONS Off)
8
9# isogfx
10
11add_library(isogfx
12 src/isogfx.c)
13
14target_include_directories(isogfx PUBLIC
15 include)
16
17target_link_libraries(isogfx PUBLIC
18 filesystem
19 mem
20 mempool)
21
22target_compile_options(isogfx PRIVATE -Wall -Wextra -Wpedantic)
23
24# Backend
25
26add_library(isogfx-backend
27 src/backend.c)
28
29target_include_directories(isogfx-backend PUBLIC
30 include)
31
32target_link_libraries(isogfx-backend PUBLIC
33 isogfx)
34
35target_link_libraries(isogfx-backend PRIVATE
36 gfx)
37
38target_compile_options(isogfx-backend PRIVATE -Wall -Wextra -Wpedantic)
39
40# Demos
41
42add_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 @@
1add_subdirectory(checkerboard)
2add_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 @@
1cmake_minimum_required(VERSION 3.0)
2
3project(checkerboard)
4
5set(CMAKE_C_STANDARD 17)
6set(CMAKE_C_STANDARD_REQUIRED On)
7set(CMAKE_C_EXTENSIONS Off)
8
9add_executable(checkerboard
10 checkerboard.c)
11
12target_link_libraries(checkerboard PRIVATE
13 gfx-app
14 isogfx-backend)
15
16target_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
10static const int WINDOW_WIDTH = 1408;
11static const int WINDOW_HEIGHT = 960;
12static const int MAX_FPS = 60;
13
14// Virtual screen dimensions.
15static const int SCREEN_WIDTH = 704;
16static const int SCREEN_HEIGHT = 480;
17
18static const int TILE_WIDTH = 32;
19static const int TILE_HEIGHT = TILE_WIDTH / 2;
20static const int WORLD_WIDTH = 20;
21static const int WORLD_HEIGHT = 20;
22
23static 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
38typedef enum Colour {
39 Black,
40 White,
41 Red,
42} Colour;
43
44typedef struct GfxAppState {
45 IsoBackend* backend;
46 IsoGfx* iso;
47 Tile red;
48 int xpick;
49 int ypick;
50} GfxAppState;
51
52static 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
64static 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
99static void shutdown(GfxAppState* state) {
100 assert(state);
101
102 IsoBackendShutdown(&state->backend);
103 isogfx_del(&state->iso);
104}
105
106static 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
127static 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
141static void resize(GfxAppState* state, int width, int height) {
142 assert(state);
143
144 IsoBackendResizeWindow(state->backend, state->iso, width, height);
145}
146
147int 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 @@
1cmake_minimum_required(VERSION 3.0)
2
3project(isomap)
4
5set(CMAKE_C_STANDARD 17)
6set(CMAKE_C_STANDARD_REQUIRED On)
7set(CMAKE_C_EXTENSIONS Off)
8
9add_executable(isomap
10 isomap.c)
11
12target_link_libraries(isomap PRIVATE
13 gfx-app
14 isogfx-backend)
15
16target_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
9static const int WINDOW_WIDTH = 1408;
10static const int WINDOW_HEIGHT = 960;
11static const int MAX_FPS = 60;
12
13// Virtual screen dimensions.
14static const int SCREEN_WIDTH = 704;
15static const int SCREEN_HEIGHT = 480;
16
17typedef struct GfxAppState {
18 IsoBackend* backend;
19 IsoGfx* iso;
20 int xpick;
21 int ypick;
22 SpriteSheet stag_sheet;
23 Sprite stag;
24} GfxAppState;
25
26static 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
59static void shutdown(GfxAppState* state) {
60 assert(state);
61 //
62}
63
64static 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
72static void render(GfxAppState* state) {
73 assert(state);
74
75 IsoGfx* iso = state->iso;
76 isogfx_render(iso);
77 IsoBackendRender(state->backend, iso);
78}
79
80static void resize(GfxAppState* state, int width, int height) {
81 assert(state);
82
83 IsoBackendResizeWindow(state->backend, state->iso, width, height);
84}
85
86int 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
5typedef struct Gfx Gfx;
6typedef struct IsoGfx IsoGfx;
7
8typedef struct IsoBackend IsoBackend;
9
10/// Initialize the backend.
11IsoBackend* IsoBackendInit(const IsoGfx*);
12
13/// Shut down the backend.
14void 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.
19void IsoBackendResizeWindow(IsoBackend*, const IsoGfx*, int width, int height);
20
21/// Render the iso screen to the graphics window.
22void 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().
27bool 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
9typedef struct IsoGfx IsoGfx;
10
11/// Sprite sheet handle.
12typedef uint16_t SpriteSheet;
13
14/// Sprite handle.
15typedef uint16_t Sprite;
16
17/// Tile handle.
18typedef uint16_t Tile;
19
20/// Colour channel.
21typedef uint8_t Channel;
22
23typedef struct Pixel {
24 Channel r, g, b, a;
25} Pixel;
26
27typedef enum TileDescType {
28 TileFromColour,
29 TileFromFile,
30 TileFromMemory,
31} TileDescType;
32
33typedef 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
48typedef 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
56typedef 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.
64IsoGfx* isogfx_new(const IsoGfxDesc*);
65
66/// Destroy the isometric graphics engine.
67void isogfx_del(IsoGfx**);
68
69/// Create an empty world.
70bool isogfx_make_world(IsoGfx*, const WorldDesc*);
71
72/// Load a world from a tile map (.TM) file.
73bool isogfx_load_world(IsoGfx*, const char* filepath);
74
75/// Return the world's width.
76int isogfx_world_width(const IsoGfx*);
77
78/// Return the world's height.
79int isogfx_world_height(const IsoGfx*);
80
81/// Create a new tile.
82Tile isogfx_make_tile(IsoGfx*, const TileDesc*);
83
84/// Set the tile at position (x,y).
85void isogfx_set_tile(IsoGfx*, int x, int y, Tile);
86
87/// Set the tiles in positions in the range (x0,y0) - (x1,y1).
88void isogfx_set_tiles(IsoGfx*, int x0, int y0, int x1, int y1, Tile);
89
90/// Load a sprite sheet (.SS) file.
91bool isogfx_load_sprite_sheet(IsoGfx*, const char* filepath, SpriteSheet*);
92
93/// Create an animated sprite.
94Sprite isogfx_make_sprite(IsoGfx*, SpriteSheet);
95
96/// Destroy the sprite.
97void isogfx_del_sprite(IsoGfx*, Sprite);
98
99/// Destroy all the sprites.
100void isogfx_del_sprites(IsoGfx*);
101
102/// Set the sprite's position.
103void isogfx_set_sprite_position(IsoGfx*, Sprite, int x, int y);
104
105/// Set the sprite's current animation.
106void isogfx_set_sprite_animation(IsoGfx*, Sprite, int animation);
107
108/// Update the renderer.
109///
110/// Currently this updates the sprite animations.
111void isogfx_update(IsoGfx*, double t);
112
113/// Render the world.
114void 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().
121void isogfx_draw_tile(IsoGfx*, int x, int y, Tile);
122
123/// Resize the virtual screen's dimensions.
124bool isogfx_resize(IsoGfx*, int screen_width, int screen_height);
125
126/// Get the virtual screen's dimensions.
127void 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.
132const Pixel* isogfx_get_screen_buffer(const IsoGfx*);
133
134/// Translate Cartesian to isometric coordinates.
135void 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
14typedef 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
32IsoBackend* 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
100cleanup:
101 if (backend->gfx) {
102 gfx_destroy(&backend->gfx);
103 }
104 free(backend);
105 return 0;
106}
107
108void 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
119void 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
148void 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
177bool 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
32typedef struct ivec2 {
33 int x, y;
34} ivec2;
35
36typedef 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
47typedef 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
53typedef 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
60typedef 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
67typedef 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
76static 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
85static 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.
106typedef 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
111typedef 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.
119typedef 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
127static 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
138static 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
153typedef 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.
160typedef Ss_Row SpriteSheetRow;
161typedef Ss_SpriteSheet SpriteSheetData;
162
163typedef 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
170DEF_MEMPOOL_DYN(TilePool, TileData)
171DEF_MEM_DYN(PixelPool, Pixel)
172
173DEF_MEMPOOL_DYN(SpritePool, SpriteData)
174DEF_MEM_DYN(SpriteSheetPool, SpriteSheetData)
175
176typedef 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
198static inline ivec2 ivec2_add(ivec2 a, ivec2 b) {
199 return (ivec2){.x = a.x + b.x, .y = a.y + b.y};
200}
201
202static inline ivec2 ivec2_scale(ivec2 a, int s) {
203 return (ivec2){.x = a.x * s, .y = a.y * s};
204}
205
206static 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.
220static 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
229static 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
244static 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
248static 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
257static inline Tile world_xy(const IsoGfx* iso, int x, int y) {
258 return *world_xy_const_ref(iso, x, y);
259}
260
261static inline Tile* world_xy_mut(IsoGfx* iso, int x, int y) {
262 return (Tile*)world_xy_const_ref(iso, x, y);
263}
264
265static 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
275static inline Pixel screen_xy(IsoGfx* iso, int x, int y) {
276 return *screen_xy_const_ref(iso, x, y);
277}
278
279static inline Pixel* screen_xy_mut(IsoGfx* iso, int x, int y) {
280 return (Pixel*)screen_xy_const_ref(iso, x, y);
281}
282
283static 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
296IsoGfx* 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
326cleanup:
327 isogfx_del(&iso);
328 return 0;
329}
330
331/// Destroy the world, its tile set, and the underlying pools.
332static 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.
343static void destroy_sprites(IsoGfx* iso) {
344 assert(iso);
345 mempool_del(&iso->sprites);
346 mem_del(&iso->sheets);
347}
348
349void 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
364bool 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
400cleanup:
401 destroy_world(iso);
402 return false;
403}
404
405bool 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
508cleanup:
509 if (map) {
510 free(map);
511 }
512 if (!success) {
513 destroy_world(iso);
514 }
515 return success;
516}
517
518int isogfx_world_width(const IsoGfx* iso) {
519 assert(iso);
520 return iso->world_width;
521}
522
523int isogfx_world_height(const IsoGfx* iso) {
524 assert(iso);
525 return iso->world_height;
526}
527
528/// Create a tile mask procedurally.
529static 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
555Tile 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
590void isogfx_set_tile(IsoGfx* iso, int x, int y, Tile tile) {
591 assert(iso);
592 *world_xy_mut(iso, x, y) = tile;
593}
594
595void 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
604bool 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
644cleanup:
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
657Sprite 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
675void 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
683void isogfx_set_sprite_animation(IsoGfx* iso, Sprite sprite, int animation) {
684 assert(iso);
685 with_sprite(sprite, { data->animation = animation; });
686}
687
688void 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
717typedef 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.
725static 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).
735static 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
745static 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.
773static 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).
809static 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
826static 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
850static 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
867static 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
882void isogfx_render(IsoGfx* iso) {
883 assert(iso);
884 draw_world(iso);
885 draw_sprites(iso);
886}
887
888void 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
900bool 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
921void 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
929const Pixel* isogfx_get_screen_buffer(const IsoGfx* iso) {
930 assert(iso);
931 return iso->screen;
932}
933
934void 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#
13import argparse
14import ctypes
15import os
16from PIL import Image
17import sys
18from xml.etree import ElementTree
19
20# Maximum length of path strings in .TS and .TM files.
21# Must match the engine's value.
22MAX_PATH_LENGTH = 128
23
24
25def drop_extension(filepath):
26 return filepath[:filepath.rfind('.')]
27
28
29def 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
41def 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
83def 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
145def 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
168def 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
206def 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
221def 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
293def 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
323if __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 @@
1cmake_minimum_required(VERSION 3.0)
2
3include(cmake/shader.txt)
4
5add_subdirectory(contrib/cgltf)
6add_subdirectory(contrib/cgltf-tangents)
7add_subdirectory(contrib/stb)
8
9project(gfx)
10
11add_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
34add_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
64target_include_directories(gfx PUBLIC
65 include)
66
67target_include_directories(gfx PRIVATE
68 src)
69
70target_compile_options(gfx PRIVATE -std=gnu11 -Wall -Wextra -Wpedantic)
71
72target_link_libraries(gfx PUBLIC
73 cstring
74 math)
75
76target_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/asset.h b/include/gfx/asset.h
index caf40c1..caf40c1 100644
--- a/gfx/include/gfx/asset.h
+++ b/include/gfx/asset.h
diff --git a/gfx/include/gfx/core.h b/include/gfx/core.h
index 44509c9..44509c9 100644
--- a/gfx/include/gfx/core.h
+++ b/include/gfx/core.h
diff --git a/gfx/include/gfx/gfx.h b/include/gfx/gfx.h
index 7c670a5..7c670a5 100644
--- a/gfx/include/gfx/gfx.h
+++ b/include/gfx/gfx.h
diff --git a/gfx/include/gfx/renderer.h b/include/gfx/renderer.h
index 2a4ada1..2a4ada1 100644
--- a/gfx/include/gfx/renderer.h
+++ b/include/gfx/renderer.h
diff --git a/gfx/include/gfx/scene.h b/include/gfx/scene.h
index abcaa70..abcaa70 100644
--- a/gfx/include/gfx/scene.h
+++ b/include/gfx/scene.h
diff --git a/gfx/include/gfx/scene/animation.h b/include/gfx/scene/animation.h
index d95b895..d95b895 100644
--- a/gfx/include/gfx/scene/animation.h
+++ b/include/gfx/scene/animation.h
diff --git a/gfx/include/gfx/scene/camera.h b/include/gfx/scene/camera.h
index 99d83fe..99d83fe 100644
--- a/gfx/include/gfx/scene/camera.h
+++ b/include/gfx/scene/camera.h
diff --git a/gfx/include/gfx/scene/light.h b/include/gfx/scene/light.h
index 132e344..132e344 100644
--- a/gfx/include/gfx/scene/light.h
+++ b/include/gfx/scene/light.h
diff --git a/gfx/include/gfx/scene/material.h b/include/gfx/scene/material.h
index bca664e..bca664e 100644
--- a/gfx/include/gfx/scene/material.h
+++ b/include/gfx/scene/material.h
diff --git a/gfx/include/gfx/scene/mesh.h b/include/gfx/scene/mesh.h
index 0d3b4d4..0d3b4d4 100644
--- a/gfx/include/gfx/scene/mesh.h
+++ b/include/gfx/scene/mesh.h
diff --git a/gfx/include/gfx/scene/model.h b/include/gfx/scene/model.h
index 42f85d4..42f85d4 100644
--- a/gfx/include/gfx/scene/model.h
+++ b/include/gfx/scene/model.h
diff --git a/gfx/include/gfx/scene/node.h b/include/gfx/scene/node.h
index a2c2836..a2c2836 100644
--- a/gfx/include/gfx/scene/node.h
+++ b/include/gfx/scene/node.h
diff --git a/gfx/include/gfx/scene/object.h b/include/gfx/scene/object.h
index 7579d29..7579d29 100644
--- a/gfx/include/gfx/scene/object.h
+++ b/include/gfx/scene/object.h
diff --git a/gfx/include/gfx/scene/scene.h b/include/gfx/scene/scene.h
index 0d96210..0d96210 100644
--- a/gfx/include/gfx/scene/scene.h
+++ b/include/gfx/scene/scene.h
diff --git a/gfx/include/gfx/sizes.h b/include/gfx/sizes.h
index 076113c..076113c 100644
--- a/gfx/include/gfx/sizes.h
+++ b/include/gfx/sizes.h
diff --git a/gfx/include/gfx/util/geometry.h b/include/gfx/util/geometry.h
index a962291..a962291 100644
--- a/gfx/include/gfx/util/geometry.h
+++ b/include/gfx/util/geometry.h
diff --git a/gfx/include/gfx/util/ibl.h b/include/gfx/util/ibl.h
index 6e39180..6e39180 100644
--- a/gfx/include/gfx/util/ibl.h
+++ b/include/gfx/util/ibl.h
diff --git a/gfx/include/gfx/util/shader.h b/include/gfx/util/shader.h
index bd058f4..bd058f4 100644
--- a/gfx/include/gfx/util/shader.h
+++ b/include/gfx/util/shader.h
diff --git a/gfx/include/gfx/util/skyquad.h b/include/gfx/util/skyquad.h
index 2b3fe17..2b3fe17 100644
--- a/gfx/include/gfx/util/skyquad.h
+++ b/include/gfx/util/skyquad.h
diff --git a/gfx/shaders/brdf_integration_map.frag b/shaders/brdf_integration_map.frag
index bb2cebd..bb2cebd 100644
--- a/gfx/shaders/brdf_integration_map.frag
+++ b/shaders/brdf_integration_map.frag
diff --git a/gfx/shaders/cook_torrance.frag b/shaders/cook_torrance.frag
index 1975491..1975491 100644
--- a/gfx/shaders/cook_torrance.frag
+++ b/shaders/cook_torrance.frag
diff --git a/gfx/shaders/cook_torrance.vert b/shaders/cook_torrance.vert
index 5f126c0..5f126c0 100644
--- a/gfx/shaders/cook_torrance.vert
+++ b/shaders/cook_torrance.vert
diff --git a/gfx/shaders/cubemap_filtering.vert b/shaders/cubemap_filtering.vert
index d0cf73f..d0cf73f 100644
--- a/gfx/shaders/cubemap_filtering.vert
+++ b/shaders/cubemap_filtering.vert
diff --git a/gfx/shaders/debug3d.frag b/shaders/debug3d.frag
index 54568d4..54568d4 100644
--- a/gfx/shaders/debug3d.frag
+++ b/shaders/debug3d.frag
diff --git a/gfx/shaders/debug3d.vert b/shaders/debug3d.vert
index d51684f..d51684f 100644
--- a/gfx/shaders/debug3d.vert
+++ b/shaders/debug3d.vert
diff --git a/gfx/shaders/immediate_mode.frag b/shaders/immediate_mode.frag
index ac23b5c..ac23b5c 100644
--- a/gfx/shaders/immediate_mode.frag
+++ b/shaders/immediate_mode.frag
diff --git a/gfx/shaders/immediate_mode.vert b/shaders/immediate_mode.vert
index 65070bb..65070bb 100644
--- a/gfx/shaders/immediate_mode.vert
+++ b/shaders/immediate_mode.vert
diff --git a/gfx/shaders/irradiance_map.frag b/shaders/irradiance_map.frag
index 8200e73..8200e73 100644
--- a/gfx/shaders/irradiance_map.frag
+++ b/shaders/irradiance_map.frag
diff --git a/gfx/shaders/prefiltered_environment_map.frag b/shaders/prefiltered_environment_map.frag
index 8327950..8327950 100644
--- a/gfx/shaders/prefiltered_environment_map.frag
+++ b/shaders/prefiltered_environment_map.frag
diff --git a/gfx/shaders/quad.vert b/shaders/quad.vert
index ef1834c..ef1834c 100644
--- a/gfx/shaders/quad.vert
+++ b/shaders/quad.vert
diff --git a/gfx/shaders/skyquad.frag b/shaders/skyquad.frag
index 9b44bfd..9b44bfd 100644
--- a/gfx/shaders/skyquad.frag
+++ b/shaders/skyquad.frag
diff --git a/gfx/shaders/skyquad.vert b/shaders/skyquad.vert
index c0c46e6..c0c46e6 100644
--- a/gfx/shaders/skyquad.vert
+++ b/shaders/skyquad.vert
diff --git a/gfx/shaders/view_normal_mapped_normals.frag b/shaders/view_normal_mapped_normals.frag
index a372c02..a372c02 100644
--- a/gfx/shaders/view_normal_mapped_normals.frag
+++ b/shaders/view_normal_mapped_normals.frag
diff --git a/gfx/shaders/view_normal_mapped_normals.vert b/shaders/view_normal_mapped_normals.vert
index 004ed9a..004ed9a 100644
--- a/gfx/shaders/view_normal_mapped_normals.vert
+++ b/shaders/view_normal_mapped_normals.vert
diff --git a/gfx/shaders/view_normals.frag b/shaders/view_normals.frag
index e90189c..e90189c 100644
--- a/gfx/shaders/view_normals.frag
+++ b/shaders/view_normals.frag
diff --git a/gfx/shaders/view_normals.vert b/shaders/view_normals.vert
index d51684f..d51684f 100644
--- a/gfx/shaders/view_normals.vert
+++ b/shaders/view_normals.vert
diff --git a/gfx/shaders/view_tangents.frag b/shaders/view_tangents.frag
index 11d1455..11d1455 100644
--- a/gfx/shaders/view_tangents.frag
+++ b/shaders/view_tangents.frag
diff --git a/gfx/shaders/view_tangents.vert b/shaders/view_tangents.vert
index 561ad22..561ad22 100644
--- a/gfx/shaders/view_tangents.vert
+++ b/shaders/view_tangents.vert
diff --git a/gfx/shaders/view_texture.frag b/shaders/view_texture.frag
index 12fa367..12fa367 100644
--- a/gfx/shaders/view_texture.frag
+++ b/shaders/view_texture.frag
diff --git a/gfx/shaders/view_texture.vert b/shaders/view_texture.vert
index 4e3c7d7..4e3c7d7 100644
--- a/gfx/shaders/view_texture.vert
+++ b/shaders/view_texture.vert
diff --git a/gfx/src/asset/asset_cache.c b/src/asset/asset_cache.c
index 16c4d5c..16c4d5c 100644
--- a/gfx/src/asset/asset_cache.c
+++ b/src/asset/asset_cache.c
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..25f2780 100644
--- a/gfx/src/asset/model.c
+++ b/src/asset/model.c
diff --git a/gfx/src/asset/model.h b/src/asset/model.h
index d6399b1..d6399b1 100644
--- a/gfx/src/asset/model.h
+++ b/src/asset/model.h
diff --git a/gfx/src/asset/texture.c b/src/asset/texture.c
index c790394..c790394 100644
--- a/gfx/src/asset/texture.c
+++ b/src/asset/texture.c
diff --git a/gfx/src/asset/texture.h b/src/asset/texture.h
index 0d38bd9..0d38bd9 100644
--- a/gfx/src/asset/texture.h
+++ b/src/asset/texture.h
diff --git a/gfx/src/core/buffer.c b/src/core/buffer.c
index 3b7e4bc..3b7e4bc 100644
--- a/gfx/src/core/buffer.c
+++ b/src/core/buffer.c
diff --git a/gfx/src/core/buffer.h b/src/core/buffer.h
index b9080f0..b9080f0 100644
--- a/gfx/src/core/buffer.h
+++ b/src/core/buffer.h
diff --git a/gfx/src/core/constants.h b/src/core/constants.h
index a6a3b94..a6a3b94 100644
--- a/gfx/src/core/constants.h
+++ b/src/core/constants.h
diff --git a/gfx/src/core/core.c b/src/core/core.c
index 90038c6..90038c6 100644
--- a/gfx/src/core/core.c
+++ b/src/core/core.c
diff --git a/gfx/src/core/core_impl.h b/src/core/core_impl.h
index eefdfbe..eefdfbe 100644
--- a/gfx/src/core/core_impl.h
+++ b/src/core/core_impl.h
diff --git a/gfx/src/core/framebuffer.c b/src/core/framebuffer.c
index 76d9002..76d9002 100644
--- a/gfx/src/core/framebuffer.c
+++ b/src/core/framebuffer.c
diff --git a/gfx/src/core/framebuffer.h b/src/core/framebuffer.h
index 1a3439c..1a3439c 100644
--- a/gfx/src/core/framebuffer.h
+++ b/src/core/framebuffer.h
diff --git a/gfx/src/core/geometry.c b/src/core/geometry.c
index cfc749f..cfc749f 100644
--- a/gfx/src/core/geometry.c
+++ b/src/core/geometry.c
diff --git a/gfx/src/core/geometry.h b/src/core/geometry.h
index c37a76f..c37a76f 100644
--- a/gfx/src/core/geometry.h
+++ b/src/core/geometry.h
diff --git a/gfx/src/core/gl_util.h b/src/core/gl_util.h
index d2d6e22..d2d6e22 100644
--- a/gfx/src/core/gl_util.h
+++ b/src/core/gl_util.h
diff --git a/gfx/src/core/renderbuffer.c b/src/core/renderbuffer.c
index 2753f3b..2753f3b 100644
--- a/gfx/src/core/renderbuffer.c
+++ b/src/core/renderbuffer.c
diff --git a/gfx/src/core/renderbuffer.h b/src/core/renderbuffer.h
index ea11610..ea11610 100644
--- a/gfx/src/core/renderbuffer.h
+++ b/src/core/renderbuffer.h
diff --git a/gfx/src/core/shader.c b/src/core/shader.c
index dded084..dded084 100644
--- a/gfx/src/core/shader.c
+++ b/src/core/shader.c
diff --git a/gfx/src/core/shader.h b/src/core/shader.h
index b9f5679..b9f5679 100644
--- a/gfx/src/core/shader.h
+++ b/src/core/shader.h
diff --git a/gfx/src/core/shader_program.c b/src/core/shader_program.c
index 3cbe48d..3cbe48d 100644
--- a/gfx/src/core/shader_program.c
+++ b/src/core/shader_program.c
diff --git a/gfx/src/core/shader_program.h b/src/core/shader_program.h
index 1443663..1443663 100644
--- a/gfx/src/core/shader_program.h
+++ b/src/core/shader_program.h
diff --git a/gfx/src/core/texture.c b/src/core/texture.c
index 89f7ec0..89f7ec0 100644
--- a/gfx/src/core/texture.c
+++ b/src/core/texture.c
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..cd2ac90 100644
--- a/gfx/src/gfx.c
+++ b/src/gfx.c
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/renderer/imm_renderer.c b/src/renderer/imm_renderer.c
index 8cf3a10..8cf3a10 100644
--- a/gfx/src/renderer/imm_renderer.c
+++ b/src/renderer/imm_renderer.c
diff --git a/gfx/src/renderer/imm_renderer_impl.h b/src/renderer/imm_renderer_impl.h
index 5ece354..5ece354 100644
--- a/gfx/src/renderer/imm_renderer_impl.h
+++ b/src/renderer/imm_renderer_impl.h
diff --git a/gfx/src/renderer/renderer.c b/src/renderer/renderer.c
index c2a7dda..c2a7dda 100644
--- a/gfx/src/renderer/renderer.c
+++ b/src/renderer/renderer.c
diff --git a/gfx/src/renderer/renderer_impl.h b/src/renderer/renderer_impl.h
index fc14dcb..fc14dcb 100644
--- a/gfx/src/renderer/renderer_impl.h
+++ b/src/renderer/renderer_impl.h
diff --git a/gfx/src/scene/animation.c b/src/scene/animation.c
index 08d02ce..08d02ce 100644
--- a/gfx/src/scene/animation.c
+++ b/src/scene/animation.c
diff --git a/gfx/src/scene/animation_impl.h b/src/scene/animation_impl.h
index 4408158..4408158 100644
--- a/gfx/src/scene/animation_impl.h
+++ b/src/scene/animation_impl.h
diff --git a/gfx/src/scene/camera.c b/src/scene/camera.c
index be7d806..be7d806 100644
--- a/gfx/src/scene/camera.c
+++ b/src/scene/camera.c
diff --git a/gfx/src/scene/camera_impl.h b/src/scene/camera_impl.h
index 20c3890..20c3890 100644
--- a/gfx/src/scene/camera_impl.h
+++ b/src/scene/camera_impl.h
diff --git a/gfx/src/scene/light.c b/src/scene/light.c
index adbec8d..adbec8d 100644
--- a/gfx/src/scene/light.c
+++ b/src/scene/light.c
diff --git a/gfx/src/scene/light_impl.h b/src/scene/light_impl.h
index 1aa0bb4..1aa0bb4 100644
--- a/gfx/src/scene/light_impl.h
+++ b/src/scene/light_impl.h
diff --git a/gfx/src/scene/material.c b/src/scene/material.c
index 3248243..3248243 100644
--- a/gfx/src/scene/material.c
+++ b/src/scene/material.c
diff --git a/gfx/src/scene/material_impl.h b/src/scene/material_impl.h
index a6aa95b..a6aa95b 100644
--- a/gfx/src/scene/material_impl.h
+++ b/src/scene/material_impl.h
diff --git a/gfx/src/scene/mesh.c b/src/scene/mesh.c
index 1a93bed..1a93bed 100644
--- a/gfx/src/scene/mesh.c
+++ b/src/scene/mesh.c
diff --git a/gfx/src/scene/mesh_impl.h b/src/scene/mesh_impl.h
index 560b77e..560b77e 100644
--- a/gfx/src/scene/mesh_impl.h
+++ b/src/scene/mesh_impl.h
diff --git a/gfx/src/scene/model.c b/src/scene/model.c
index cc41a9a..cc41a9a 100644
--- a/gfx/src/scene/model.c
+++ b/src/scene/model.c
diff --git a/gfx/src/scene/model_impl.h b/src/scene/model_impl.h
index a99d32c..a99d32c 100644
--- a/gfx/src/scene/model_impl.h
+++ b/src/scene/model_impl.h
diff --git a/gfx/src/scene/node.c b/src/scene/node.c
index 67ce93c..67ce93c 100644
--- a/gfx/src/scene/node.c
+++ b/src/scene/node.c
diff --git a/gfx/src/scene/node_impl.h b/src/scene/node_impl.h
index c79f252..c79f252 100644
--- a/gfx/src/scene/node_impl.h
+++ b/src/scene/node_impl.h
diff --git a/gfx/src/scene/object.c b/src/scene/object.c
index e8e3ee6..e8e3ee6 100644
--- a/gfx/src/scene/object.c
+++ b/src/scene/object.c
diff --git a/gfx/src/scene/object_impl.h b/src/scene/object_impl.h
index 88f8e31..88f8e31 100644
--- a/gfx/src/scene/object_impl.h
+++ b/src/scene/object_impl.h
diff --git a/gfx/src/scene/scene.c b/src/scene/scene.c
index 54452dd..54452dd 100644
--- a/gfx/src/scene/scene.c
+++ b/src/scene/scene.c
diff --git a/gfx/src/scene/scene_graph.h b/src/scene/scene_graph.h
index a26f828..a26f828 100644
--- a/gfx/src/scene/scene_graph.h
+++ b/src/scene/scene_graph.h
diff --git a/gfx/src/scene/scene_impl.h b/src/scene/scene_impl.h
index 992f620..992f620 100644
--- a/gfx/src/scene/scene_impl.h
+++ b/src/scene/scene_impl.h
diff --git a/gfx/src/scene/scene_memory.c b/src/scene/scene_memory.c
index 85c27e7..85c27e7 100644
--- a/gfx/src/scene/scene_memory.c
+++ b/src/scene/scene_memory.c
diff --git a/gfx/src/scene/scene_memory.h b/src/scene/scene_memory.h
index d175cba..d175cba 100644
--- a/gfx/src/scene/scene_memory.h
+++ b/src/scene/scene_memory.h
diff --git a/gfx/src/scene/types.h b/src/scene/types.h
index d0ffc41..d0ffc41 100644
--- a/gfx/src/scene/types.h
+++ b/src/scene/types.h
diff --git a/gfx/src/util/geometry.c b/src/util/geometry.c
index afe0109..afe0109 100644
--- a/gfx/src/util/geometry.c
+++ b/src/util/geometry.c
diff --git a/gfx/src/util/ibl.c b/src/util/ibl.c
index 5a79990..5a79990 100644
--- a/gfx/src/util/ibl.c
+++ b/src/util/ibl.c
diff --git a/gfx/src/util/shader.c b/src/util/shader.c
index f5c22cc..f5c22cc 100644
--- a/gfx/src/util/shader.c
+++ b/src/util/shader.c
diff --git a/gfx/src/util/skyquad.c b/src/util/skyquad.c
index 08fa044..08fa044 100644
--- a/gfx/src/util/skyquad.c
+++ b/src/util/skyquad.c