summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Rowe <17525998+omnisci3nce@users.noreply.github.com>2024-05-20 10:50:11 +1000
committerGitHub <noreply@github.com>2024-05-20 10:50:11 +1000
commite904c22003c3a134201b222e6619e782fbe63947 (patch)
tree5295c8ce5f855ca4a0f1bebe50beee80bae66682
parent02e84ee4d18e705e3362be1e327fdb6f1397a032 (diff)
parent73d4145f46d2305f45761b8e456df692d1962dfb (diff)
Merge pull request #14 from omnisci3nce/realign
Realign
-rw-r--r--README.md6
-rw-r--r--assets/models/gltf/AnimatedCube/LICENSE.md15
-rw-r--r--assets/models/gltf/AnimatedCube/README.body.md3
-rw-r--r--assets/models/gltf/AnimatedCube/README.md27
-rw-r--r--assets/models/gltf/AnimatedCube/glTF/AnimatedCube.binbin0 -> 1860 bytes
-rw-r--r--assets/models/gltf/AnimatedCube/glTF/AnimatedCube.gltf262
-rw-r--r--assets/models/gltf/AnimatedCube/glTF/AnimatedCube_BaseColor.pngbin0 -> 891995 bytes
-rw-r--r--assets/models/gltf/AnimatedCube/glTF/AnimatedCube_MetallicRoughness.pngbin0 -> 319 bytes
-rw-r--r--assets/models/gltf/AnimatedCube/metadata.json25
-rw-r--r--assets/models/gltf/AnimatedCube/screenshot/screenshot.gifbin0 -> 517169 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/LICENSE.md15
-rw-r--r--assets/models/gltf/SimpleSkin/README.body.md18
-rw-r--r--assets/models/gltf/SimpleSkin/README.md42
-rw-r--r--assets/models/gltf/SimpleSkin/glTF-Embedded/SimpleSkin.gltf131
-rw-r--r--assets/models/gltf/SimpleSkin/glTF/SimpleSkin.gltf131
-rw-r--r--assets/models/gltf/SimpleSkin/glTF/inverseBindMatrices.binbin0 -> 128 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/glTF/skinAnimation.binbin0 -> 240 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/glTF/skinGeometry.binbin0 -> 168 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/glTF/skinningData.binbin0 -> 320 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/metadata.json26
-rw-r--r--assets/models/gltf/SimpleSkin/screenshot/inverseBindMatrices.pngbin0 -> 43880 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/screenshot/screenshot.gifbin0 -> 2000860 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/screenshot/skinAnimation.pngbin0 -> 100791 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/screenshot/skinGeometry.pngbin0 -> 74566 bytes
-rw-r--r--assets/models/gltf/SimpleSkin/screenshot/skinningData.pngbin0 -> 86339 bytes
-rw-r--r--assets/shaders/blinn_phong.frag4
-rw-r--r--assets/shaders/blinn_phong.vert2
-rw-r--r--assets/shaders/cube.frag12
-rw-r--r--assets/shaders/cube.vert21
-rw-r--r--assets/shaders/object.frag15
-rw-r--r--assets/shaders/object.vert29
-rw-r--r--assets/shaders/skinned.vert59
-rw-r--r--assets/shaders/triangle.frag6
-rw-r--r--assets/shaders/triangle.vert11
-rw-r--r--assets/textures/texture.jpgbin0 -> 77120 bytes
-rw-r--r--examples/README.md11
-rw-r--r--examples/cube/ex_cube.c158
-rw-r--r--examples/example_scene.h31
-rw-r--r--examples/gltf_loading/ex_gltf_loading.c4
-rw-r--r--examples/input/ex_input.c120
-rw-r--r--examples/main_loop/ex_main_loop.c14
-rw-r--r--examples/obj_loading/ex_obj_loading.c4
-rw-r--r--examples/property_animation/ex_property_animation.c103
-rw-r--r--examples/skinned_animation/ex_skinned_animation.c122
-rw-r--r--examples/transforms/ex_transforms.c2
-rw-r--r--examples/triangle/ex_triangle.c104
-rw-r--r--include/celeritas.h65
-rw-r--r--scratchpad.md71
-rw-r--r--scripts/api_gen/modules.md18
-rw-r--r--scripts/api_gen/template.html91
-rw-r--r--src/animation.c38
-rw-r--r--src/animation.h72
-rw-r--r--src/camera.h9
-rw-r--r--src/colours.h50
-rw-r--r--src/core.c56
-rw-r--r--src/core.h29
-rw-r--r--src/defines.h17
-rw-r--r--src/log.h12
-rw-r--r--src/logos/README.md6
-rw-r--r--src/logos/jobs.h3
-rw-r--r--src/maths/maths.h118
-rw-r--r--src/maths/maths_types.h42
-rw-r--r--src/maths/primitives.c138
-rw-r--r--src/maths/primitives.h13
-rw-r--r--src/physics/broadphase.h10
-rw-r--r--src/physics/collision.h30
-rw-r--r--src/physics/narrowphase.h10
-rw-r--r--src/physics/physics.c1
-rw-r--r--src/physics/physics.h23
-rw-r--r--src/platform/file.c30
-rw-r--r--src/platform/file.h9
-rw-r--r--src/platform/mutex.c9
-rw-r--r--src/platform/mutex.h28
-rw-r--r--src/platform/path.c6
-rw-r--r--src/platform/thread.c0
-rw-r--r--src/platform/thread.h17
-rw-r--r--src/renderer/archive/old_backend_vulkan.c1990
-rw-r--r--src/renderer/archive/render.c413
-rw-r--r--src/renderer/archive/render.h46
-rw-r--r--src/renderer/archive/render_backend.h39
-rw-r--r--src/renderer/archive/render_types.h210
-rw-r--r--src/renderer/backends/backend_dx11.c5
-rw-r--r--src/renderer/backends/backend_dx11.h29
-rw-r--r--src/renderer/backends/backend_opengl.c31
-rw-r--r--src/renderer/backends/backend_vulkan.c1690
-rw-r--r--src/renderer/backends/backend_vulkan.h115
-rw-r--r--src/renderer/backends/metal/README.md1
-rw-r--r--src/renderer/backends/opengl/README.md1
-rw-r--r--src/renderer/backends/vulkan/README.md1
-rw-r--r--src/renderer/backends/vulkan/vulkan_glossary.md18
-rw-r--r--src/renderer/backends/vulkan_helpers.h199
-rw-r--r--src/renderer/bind_group_layouts.h30
-rw-r--r--src/renderer/immediate.c18
-rw-r--r--src/renderer/immediate.h20
-rw-r--r--src/renderer/ral.c42
-rw-r--r--src/renderer/ral.h171
-rw-r--r--src/renderer/ral_types.h255
-rw-r--r--src/renderer/render.c402
-rw-r--r--src/renderer/render.h92
-rw-r--r--src/renderer/render_backend.h22
-rw-r--r--src/renderer/render_types.h209
-rw-r--r--src/renderer/renderpasses.h28
-rw-r--r--src/resources/gltf.c592
-rw-r--r--src/resources/obj.c594
-rw-r--r--src/scene.c56
-rw-r--r--src/scene.h54
-rw-r--r--src/std/buf.h17
-rw-r--r--src/std/containers/darray.h49
-rw-r--r--src/std/containers/graphs.h14
-rw-r--r--src/std/containers/hashset.h10
-rw-r--r--src/std/containers/hashtable.h10
-rw-r--r--src/std/mem.c97
-rw-r--r--src/std/mem.h57
-rw-r--r--src/std/utils.h4
-rw-r--r--src/systems/input.c21
-rw-r--r--src/systems/keys.h15
-rw-r--r--src/systems/screenspace.h4
-rw-r--r--src/systems/terrain.c0
-rw-r--r--src/systems/terrain.h55
-rw-r--r--src/systems/text.h3
-rw-r--r--src/transform_hierarchy.c2
-rw-r--r--src/transform_hierarchy.h1
-rw-r--r--tests/pool_test_runner.c12
-rw-r--r--tests/pool_tests.c72
-rw-r--r--xmake.lua180
125 files changed, 9467 insertions, 1083 deletions
diff --git a/README.md b/README.md
index 87da707..ccd9652 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,8 @@
![Backpack model with lighting](examples/obj_loading/backpack_screenshot.png)
-Not all files are fleshed out right now.
-**Work-in-progress**: porting over code from old repository.
+**Work-in-progress**: This code is currently in flux as I port from OpenGL to a Vulkan backend with a homemade graphics API abstraction layer I'm developing named **RAL** (render abstraction layer) in the source code.
All third-party dependencies are licensed under their own license.
@@ -21,10 +20,9 @@ All third-party dependencies are licensed under their own license.
* Lint (no change) `find src/ -iname *.h -o -iname *.c | xargs clang-format --style=file --dry-run --Werror`
* Format (edit in place) `find src/ \( -iname "*.h" -o -iname "*.c" \) | xargs clang-format -i --style=file`
* `clang-format` must be installed!
-
* Documentation
* serve mkdocs locally
* `docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material`
* Build docs static site
- * `docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material build` \ No newline at end of file
+ * `docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material build`
diff --git a/assets/models/gltf/AnimatedCube/LICENSE.md b/assets/models/gltf/AnimatedCube/LICENSE.md
new file mode 100644
index 0000000..c001f8f
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/LICENSE.md
@@ -0,0 +1,15 @@
+# LICENSE file for the model: Animated Cube
+
+All files in this directory tree are licensed as indicated below.
+
+* All files directly associated with the model including all text, image and binary files:
+
+ * [CC0 1.0 Universal]("https://creativecommons.org/publicdomain/zero/1.0/legalcode") [SPDX license identifier: "CC0-1.0"]
+
+* This file and all other metadocumentation files including "metadata.json":
+
+ * [Creative Commons Attribtution 4.0 International]("https://creativecommons.org/licenses/by/4.0/legalcode") [SPDX license identifier: "CC-BY-4.0"]
+
+Full license text of these licenses are available at the links above
+
+#### Generated by modelmetadata \ No newline at end of file
diff --git a/assets/models/gltf/AnimatedCube/README.body.md b/assets/models/gltf/AnimatedCube/README.body.md
new file mode 100644
index 0000000..1efdf8c
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/README.body.md
@@ -0,0 +1,3 @@
+## Screenshot
+
+![screenshot](screenshot/screenshot.gif)
diff --git a/assets/models/gltf/AnimatedCube/README.md b/assets/models/gltf/AnimatedCube/README.md
new file mode 100644
index 0000000..d5cf0a3
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/README.md
@@ -0,0 +1,27 @@
+# Animated Cube
+
+## Tags
+
+[core](../../Models-core.md), [testing](../../Models-testing.md)
+
+## Summary
+
+Same as 'Cube', but having a linear rotation animation.
+
+## Operations
+
+* [Display](https://github.khronos.org/glTF-Sample-Viewer-Release/?model=https://raw.GithubUserContent.com/KhronosGroup/glTF-Sample-Assets/main/./Models/AnimatedCube/glTF/AnimatedCube.gltf) in SampleViewer
+* [Model Directory](./)
+
+## Screenshot
+
+![screenshot](screenshot/screenshot.gif)
+
+
+## Legal
+
+&copy; 2017, UX3D. [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/legalcode)
+
+ - Norbert Nopper for Everything
+
+#### Assembled by modelmetadata \ No newline at end of file
diff --git a/assets/models/gltf/AnimatedCube/glTF/AnimatedCube.bin b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube.bin
new file mode 100644
index 0000000..72f7d2d
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube.bin
Binary files differ
diff --git a/assets/models/gltf/AnimatedCube/glTF/AnimatedCube.gltf b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube.gltf
new file mode 100644
index 0000000..ff117b0
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube.gltf
@@ -0,0 +1,262 @@
+{
+ "accessors" : [
+ {
+ "bufferView" : 0,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 3,
+ "max" : [
+ 2.000000
+ ],
+ "min" : [
+ 0.000000
+ ],
+ "type" : "SCALAR"
+ },
+ {
+ "bufferView" : 1,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 3,
+ "max" : [
+ 0.000000,
+ 1.000000,
+ 0.000000,
+ 1.000000
+ ],
+ "min" : [
+ 0.000000,
+ -8.742278e-008,
+ 0.000000,
+ -1.000000
+ ],
+ "type" : "VEC4"
+ },
+ {
+ "bufferView" : 2,
+ "byteOffset" : 0,
+ "componentType" : 5123,
+ "count" : 36,
+ "max" : [
+ 35
+ ],
+ "min" : [
+ 0
+ ],
+ "type" : "SCALAR"
+ },
+ {
+ "bufferView" : 3,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 36,
+ "max" : [
+ 1.000000,
+ 1.000000,
+ 1.000001
+ ],
+ "min" : [
+ -1.000000,
+ -1.000000,
+ -1.000000
+ ],
+ "type" : "VEC3"
+ },
+ {
+ "bufferView" : 4,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 36,
+ "max" : [
+ 1.000000,
+ 1.000000,
+ 1.000000
+ ],
+ "min" : [
+ -1.000000,
+ -1.000000,
+ -1.000000
+ ],
+ "type" : "VEC3"
+ },
+ {
+ "bufferView" : 5,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 36,
+ "max" : [
+ 1.000000,
+ -0.000000,
+ -0.000000,
+ 1.000000
+ ],
+ "min" : [
+ 0.000000,
+ -0.000000,
+ -1.000000,
+ -1.000000
+ ],
+ "type" : "VEC4"
+ },
+ {
+ "bufferView" : 6,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 36,
+ "max" : [
+ 1.000000,
+ 1.000000
+ ],
+ "min" : [
+ -1.000000,
+ -1.000000
+ ],
+ "type" : "VEC2"
+ }
+ ],
+ "animations" : [
+ {
+ "channels" : [
+ {
+ "sampler" : 0,
+ "target" : {
+ "node" : 0,
+ "path" : "rotation"
+ }
+ }
+ ],
+ "name" : "animation_AnimatedCube",
+ "samplers" : [
+ {
+ "input" : 0,
+ "interpolation" : "LINEAR",
+ "output" : 1
+ }
+ ]
+ }
+ ],
+ "asset" : {
+ "generator" : "VKTS glTF 2.0 exporter",
+ "version" : "2.0"
+ },
+ "bufferViews" : [
+ {
+ "buffer" : 0,
+ "byteLength" : 12,
+ "byteOffset" : 0
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 48,
+ "byteOffset" : 12
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 72,
+ "byteOffset" : 60,
+ "target" : 34963
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 432,
+ "byteOffset" : 132,
+ "target" : 34962
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 432,
+ "byteOffset" : 564,
+ "target" : 34962
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 576,
+ "byteOffset" : 996,
+ "target" : 34962
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 288,
+ "byteOffset" : 1572,
+ "target" : 34962
+ }
+ ],
+ "buffers" : [
+ {
+ "byteLength" : 1860,
+ "uri" : "AnimatedCube.bin"
+ }
+ ],
+ "images" : [
+ {
+ "uri" : "AnimatedCube_BaseColor.png"
+ },
+ {
+ "uri" : "AnimatedCube_MetallicRoughness.png"
+ }
+ ],
+ "materials" : [
+ {
+ "name" : "AnimatedCube",
+ "pbrMetallicRoughness" : {
+ "baseColorTexture" : {
+ "index" : 0
+ },
+ "metallicRoughnessTexture" : {
+ "index" : 1
+ }
+ }
+ }
+ ],
+ "meshes" : [
+ {
+ "name" : "AnimatedCube",
+ "primitives" : [
+ {
+ "attributes" : {
+ "NORMAL" : 4,
+ "POSITION" : 3,
+ "TANGENT" : 5,
+ "TEXCOORD_0" : 6
+ },
+ "indices" : 2,
+ "material" : 0,
+ "mode" : 4
+ }
+ ]
+ }
+ ],
+ "nodes" : [
+ {
+ "mesh" : 0,
+ "name" : "AnimatedCube",
+ "rotation" : [
+ 0.000000,
+ -1.000000,
+ 0.000000,
+ 0.000000
+ ]
+ }
+ ],
+ "samplers" : [
+ {}
+ ],
+ "scene" : 0,
+ "scenes" : [
+ {
+ "nodes" : [
+ 0
+ ]
+ }
+ ],
+ "textures" : [
+ {
+ "sampler" : 0,
+ "source" : 0
+ },
+ {
+ "sampler" : 0,
+ "source" : 1
+ }
+ ]
+} \ No newline at end of file
diff --git a/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_BaseColor.png b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_BaseColor.png
new file mode 100644
index 0000000..5e5cb20
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_BaseColor.png
Binary files differ
diff --git a/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_MetallicRoughness.png b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_MetallicRoughness.png
new file mode 100644
index 0000000..efd2026
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_MetallicRoughness.png
Binary files differ
diff --git a/assets/models/gltf/AnimatedCube/metadata.json b/assets/models/gltf/AnimatedCube/metadata.json
new file mode 100644
index 0000000..3af6fdb
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/metadata.json
@@ -0,0 +1,25 @@
+{
+ "version": 2,
+ "legal": [
+ {
+ "license": "CC0",
+ "licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/legalcode",
+ "artist": "Norbert Nopper",
+ "year": "2017",
+ "owner": "UX3D",
+ "what": "Everything",
+ "text": "CC0 1.0 Universal",
+ "spdx": "CC0-1.0",
+ "icon": "https://licensebuttons.net/p/zero/1.0/88x31.png"
+ }
+ ],
+ "tags": [
+ "core",
+ "testing"
+ ],
+ "screenshot": "screenshot/screenshot.gif",
+ "name": "Animated Cube",
+ "path": "./Models/AnimatedCube",
+ "summary": "Same as 'Cube', but having a linear rotation animation.",
+ "createReadme": true
+} \ No newline at end of file
diff --git a/assets/models/gltf/AnimatedCube/screenshot/screenshot.gif b/assets/models/gltf/AnimatedCube/screenshot/screenshot.gif
new file mode 100644
index 0000000..eea74ee
--- /dev/null
+++ b/assets/models/gltf/AnimatedCube/screenshot/screenshot.gif
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/LICENSE.md b/assets/models/gltf/SimpleSkin/LICENSE.md
new file mode 100644
index 0000000..e602203
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/LICENSE.md
@@ -0,0 +1,15 @@
+# LICENSE file for the model: Simple Skin
+
+All files in this directory tree are licensed as indicated below.
+
+* All files directly associated with the model including all text, image and binary files:
+
+ * [CC0 1.0 Universal]("https://creativecommons.org/publicdomain/zero/1.0/legalcode") [SPDX license identifier: "CC0-1.0"]
+
+* This file and all other metadocumentation files including "metadata.json":
+
+ * [Creative Commons Attribtution 4.0 International]("https://creativecommons.org/licenses/by/4.0/legalcode") [SPDX license identifier: "CC-BY-4.0"]
+
+Full license text of these licenses are available at the links above
+
+#### Generated by modelmetadata \ No newline at end of file
diff --git a/assets/models/gltf/SimpleSkin/README.body.md b/assets/models/gltf/SimpleSkin/README.body.md
new file mode 100644
index 0000000..00cd41e
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/README.body.md
@@ -0,0 +1,18 @@
+## Screenshot
+
+![screenshot](screenshot/screenshot.gif)
+
+## Notes
+
+Details about skinning using this particular model are explained in the
+[skinning section of the glTF tutorial](https://github.com/javagl/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_019_SimpleSkin.md).
+
+## Data layout
+
+The following images show the data layout of this sample:
+
+![skinGeometry](screenshot/skinGeometry.png)
+![skinAnimation](screenshot/skinAnimation.png)
+![inverseBindMatrices](screenshot/inverseBindMatrices.png)
+![skinningData](screenshot/skinningData.png)
+
diff --git a/assets/models/gltf/SimpleSkin/README.md b/assets/models/gltf/SimpleSkin/README.md
new file mode 100644
index 0000000..ec08353
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/README.md
@@ -0,0 +1,42 @@
+# Simple Skin
+
+## Tags
+
+[core](../../Models-core.md), [testing](../../Models-testing.md), [written](../../Models-written.md)
+
+## Summary
+
+A simple example of vertex skinning in glTF
+
+## Operations
+
+* [Display](https://github.khronos.org/glTF-Sample-Viewer-Release/?model=https://raw.GithubUserContent.com/KhronosGroup/glTF-Sample-Assets/main/./Models/SimpleSkin/glTF/SimpleSkin.gltf) in SampleViewer
+* [Model Directory](./)
+
+## Screenshot
+
+![screenshot](screenshot/screenshot.gif)
+
+## Notes
+
+Details about skinning using this particular model are explained in the
+[skinning section of the glTF tutorial](https://github.com/javagl/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_019_SimpleSkin.md).
+
+## Data layout
+
+The following images show the data layout of this sample:
+
+![skinGeometry](screenshot/skinGeometry.png)
+![skinAnimation](screenshot/skinAnimation.png)
+![inverseBindMatrices](screenshot/inverseBindMatrices.png)
+![skinningData](screenshot/skinningData.png)
+
+
+
+## Legal
+
+&copy; 2017, Public. [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/legalcode)
+
+ - javagl for Everything
+
+#### Assembled by modelmetadata \ No newline at end of file
diff --git a/assets/models/gltf/SimpleSkin/glTF-Embedded/SimpleSkin.gltf b/assets/models/gltf/SimpleSkin/glTF-Embedded/SimpleSkin.gltf
new file mode 100644
index 0000000..e9f4e4d
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/glTF-Embedded/SimpleSkin.gltf
@@ -0,0 +1,131 @@
+{
+ "scene" : 0,
+ "scenes" : [ {
+ "nodes" : [ 0, 1 ]
+ } ],
+
+ "nodes" : [ {
+ "skin" : 0,
+ "mesh" : 0
+ }, {
+ "children" : [ 2 ]
+ }, {
+ "translation" : [ 0.0, 1.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
+ } ],
+
+ "meshes" : [ {
+ "primitives" : [ {
+ "attributes" : {
+ "POSITION" : 1,
+ "JOINTS_0" : 2,
+ "WEIGHTS_0" : 3
+ },
+ "indices" : 0
+ } ]
+ } ],
+
+ "skins" : [ {
+ "inverseBindMatrices" : 4,
+ "joints" : [ 1, 2 ]
+ } ],
+
+ "animations" : [ {
+ "channels" : [ {
+ "sampler" : 0,
+ "target" : {
+ "node" : 2,
+ "path" : "rotation"
+ }
+ } ],
+ "samplers" : [ {
+ "input" : 5,
+ "interpolation" : "LINEAR",
+ "output" : 6
+ } ]
+ } ],
+
+ "buffers" : [ {
+ "uri" : "data:application/gltf-buffer;base64,AAABAAMAAAADAAIAAgADAAUAAgAFAAQABAAFAAcABAAHAAYABgAHAAkABgAJAAgAAAAAvwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAvwAAAD8AAAAAAAAAPwAAAD8AAAAAAAAAvwAAgD8AAAAAAAAAPwAAgD8AAAAAAAAAvwAAwD8AAAAAAAAAPwAAwD8AAAAAAAAAvwAAAEAAAAAAAAAAPwAAAEAAAAAA",
+ "byteLength" : 168
+ }, {
+ "uri" : "data:application/gltf-buffer;base64,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAABAPwAAgD4AAAAAAAAAAAAAQD8AAIA+AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAPwAAAD8AAAAAAAAAAAAAgD4AAEA/AAAAAAAAAAAAAIA+AABAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAA=",
+ "byteLength" : 320
+ }, {
+ "uri" : "data:application/gltf-buffer;base64,AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8=",
+ "byteLength" : 128
+ }, {
+ "uri" : "data:application/gltf-buffer;base64,AAAAAAAAAD8AAIA/AADAPwAAAEAAACBAAABAQAAAYEAAAIBAAACQQAAAoEAAALBAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAPT9ND/0/TQ/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAPT9NL/0/TQ/AAAAAAAAAAD0/TS/9P00PwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAAAAAAAAAIA/",
+ "byteLength" : 240
+ } ],
+
+ "bufferViews" : [ {
+ "buffer" : 0,
+ "byteLength" : 48,
+ "target" : 34963
+ }, {
+ "buffer" : 0,
+ "byteOffset" : 48,
+ "byteLength" : 120,
+ "target" : 34962
+ }, {
+ "buffer" : 1,
+ "byteLength" : 320,
+ "byteStride" : 16
+ }, {
+ "buffer" : 2,
+ "byteLength" : 128
+ }, {
+ "buffer" : 3,
+ "byteLength" : 240
+ } ],
+
+ "accessors" : [ {
+ "bufferView" : 0,
+ "componentType" : 5123,
+ "count" : 24,
+ "type" : "SCALAR"
+ }, {
+ "bufferView" : 1,
+ "componentType" : 5126,
+ "count" : 10,
+ "type" : "VEC3",
+ "max" : [ 0.5, 2.0, 0.0 ],
+ "min" : [ -0.5, 0.0, 0.0 ]
+ }, {
+ "bufferView" : 2,
+ "componentType" : 5123,
+ "count" : 10,
+ "type" : "VEC4"
+ }, {
+ "bufferView" : 2,
+ "byteOffset" : 160,
+ "componentType" : 5126,
+ "count" : 10,
+ "type" : "VEC4"
+ }, {
+ "bufferView" : 3,
+ "componentType" : 5126,
+ "count" : 2,
+ "type" : "MAT4"
+ }, {
+ "bufferView" : 4,
+ "componentType" : 5126,
+ "count" : 12,
+ "type" : "SCALAR",
+ "max" : [ 5.5 ],
+ "min" : [ 0.0 ]
+ }, {
+ "bufferView" : 4,
+ "byteOffset" : 48,
+ "componentType" : 5126,
+ "count" : 12,
+ "type" : "VEC4",
+ "max" : [ 0.0, 0.0, 0.707, 1.0 ],
+ "min" : [ 0.0, 0.0, -0.707, 0.707 ]
+ } ],
+
+ "asset" : {
+ "version" : "2.0"
+ }
+} \ No newline at end of file
diff --git a/assets/models/gltf/SimpleSkin/glTF/SimpleSkin.gltf b/assets/models/gltf/SimpleSkin/glTF/SimpleSkin.gltf
new file mode 100644
index 0000000..52f0aa0
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/glTF/SimpleSkin.gltf
@@ -0,0 +1,131 @@
+{
+ "scene" : 0,
+ "scenes" : [ {
+ "nodes" : [ 0, 1 ]
+ } ],
+
+ "nodes" : [ {
+ "skin" : 0,
+ "mesh" : 0
+ }, {
+ "children" : [ 2 ]
+ }, {
+ "translation" : [ 0.0, 1.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
+ } ],
+
+ "meshes" : [ {
+ "primitives" : [ {
+ "attributes" : {
+ "POSITION" : 1,
+ "JOINTS_0" : 2,
+ "WEIGHTS_0" : 3
+ },
+ "indices" : 0
+ } ]
+ } ],
+
+ "skins" : [ {
+ "inverseBindMatrices" : 4,
+ "joints" : [ 1, 2 ]
+ } ],
+
+ "animations" : [ {
+ "channels" : [ {
+ "sampler" : 0,
+ "target" : {
+ "node" : 2,
+ "path" : "rotation"
+ }
+ } ],
+ "samplers" : [ {
+ "input" : 5,
+ "interpolation" : "LINEAR",
+ "output" : 6
+ } ]
+ } ],
+
+ "buffers" : [ {
+ "uri" : "skinGeometry.bin",
+ "byteLength" : 168
+ }, {
+ "uri" : "skinningData.bin",
+ "byteLength" : 320
+ }, {
+ "uri" : "inverseBindMatrices.bin",
+ "byteLength" : 128
+ }, {
+ "uri" : "skinAnimation.bin",
+ "byteLength" : 240
+ } ],
+
+ "bufferViews" : [ {
+ "buffer" : 0,
+ "byteLength" : 48,
+ "target" : 34963
+ }, {
+ "buffer" : 0,
+ "byteOffset" : 48,
+ "byteLength" : 120,
+ "target" : 34962
+ }, {
+ "buffer" : 1,
+ "byteLength" : 320,
+ "byteStride" : 16
+ }, {
+ "buffer" : 2,
+ "byteLength" : 128
+ }, {
+ "buffer" : 3,
+ "byteLength" : 240
+ } ],
+
+ "accessors" : [ {
+ "bufferView" : 0,
+ "componentType" : 5123,
+ "count" : 24,
+ "type" : "SCALAR"
+ }, {
+ "bufferView" : 1,
+ "componentType" : 5126,
+ "count" : 10,
+ "type" : "VEC3",
+ "max" : [ 0.5, 2.0, 0.0 ],
+ "min" : [ -0.5, 0.0, 0.0 ]
+ }, {
+ "bufferView" : 2,
+ "componentType" : 5123,
+ "count" : 10,
+ "type" : "VEC4"
+ }, {
+ "bufferView" : 2,
+ "byteOffset" : 160,
+ "componentType" : 5126,
+ "count" : 10,
+ "type" : "VEC4"
+ }, {
+ "bufferView" : 3,
+ "componentType" : 5126,
+ "count" : 2,
+ "type" : "MAT4"
+ }, {
+ "bufferView" : 4,
+ "componentType" : 5126,
+ "count" : 12,
+ "type" : "SCALAR",
+ "max" : [ 5.5 ],
+ "min" : [ 0.0 ]
+ }, {
+ "bufferView" : 4,
+ "byteOffset" : 48,
+ "componentType" : 5126,
+ "count" : 12,
+ "type" : "VEC4",
+ "max" : [ 0.0, 0.0, 0.707, 1.0 ],
+ "min" : [ 0.0, 0.0, -0.707, 0.707 ]
+ } ],
+
+ "asset" : {
+ "version" : "2.0"
+ }
+} \ No newline at end of file
diff --git a/assets/models/gltf/SimpleSkin/glTF/inverseBindMatrices.bin b/assets/models/gltf/SimpleSkin/glTF/inverseBindMatrices.bin
new file mode 100644
index 0000000..5af0b5e
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/glTF/inverseBindMatrices.bin
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/glTF/skinAnimation.bin b/assets/models/gltf/SimpleSkin/glTF/skinAnimation.bin
new file mode 100644
index 0000000..ff550c6
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/glTF/skinAnimation.bin
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/glTF/skinGeometry.bin b/assets/models/gltf/SimpleSkin/glTF/skinGeometry.bin
new file mode 100644
index 0000000..15375c4
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/glTF/skinGeometry.bin
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/glTF/skinningData.bin b/assets/models/gltf/SimpleSkin/glTF/skinningData.bin
new file mode 100644
index 0000000..fd0c165
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/glTF/skinningData.bin
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/metadata.json b/assets/models/gltf/SimpleSkin/metadata.json
new file mode 100644
index 0000000..764a837
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/metadata.json
@@ -0,0 +1,26 @@
+{
+ "version": 2,
+ "legal": [
+ {
+ "license": "CC0",
+ "licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/legalcode",
+ "artist": "javagl",
+ "year": "2017",
+ "owner": "Public",
+ "what": "Everything",
+ "text": "CC0 1.0 Universal",
+ "spdx": "CC0-1.0",
+ "icon": "https://licensebuttons.net/p/zero/1.0/88x31.png"
+ }
+ ],
+ "tags": [
+ "core",
+ "testing",
+ "written"
+ ],
+ "screenshot": "screenshot/screenshot.gif",
+ "name": "Simple Skin",
+ "path": "./Models/SimpleSkin",
+ "summary": "A simple example of vertex skinning in glTF",
+ "createReadme": true
+} \ No newline at end of file
diff --git a/assets/models/gltf/SimpleSkin/screenshot/inverseBindMatrices.png b/assets/models/gltf/SimpleSkin/screenshot/inverseBindMatrices.png
new file mode 100644
index 0000000..a069f59
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/screenshot/inverseBindMatrices.png
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/screenshot/screenshot.gif b/assets/models/gltf/SimpleSkin/screenshot/screenshot.gif
new file mode 100644
index 0000000..c8fb353
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/screenshot/screenshot.gif
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/screenshot/skinAnimation.png b/assets/models/gltf/SimpleSkin/screenshot/skinAnimation.png
new file mode 100644
index 0000000..20257e1
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/screenshot/skinAnimation.png
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/screenshot/skinGeometry.png b/assets/models/gltf/SimpleSkin/screenshot/skinGeometry.png
new file mode 100644
index 0000000..2569d9f
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/screenshot/skinGeometry.png
Binary files differ
diff --git a/assets/models/gltf/SimpleSkin/screenshot/skinningData.png b/assets/models/gltf/SimpleSkin/screenshot/skinningData.png
new file mode 100644
index 0000000..bd25b70
--- /dev/null
+++ b/assets/models/gltf/SimpleSkin/screenshot/skinningData.png
Binary files differ
diff --git a/assets/shaders/blinn_phong.frag b/assets/shaders/blinn_phong.frag
index 095b19a..a0ba905 100644
--- a/assets/shaders/blinn_phong.frag
+++ b/assets/shaders/blinn_phong.frag
@@ -33,6 +33,7 @@ in VS_OUT {
vec3 Normal;
vec2 TexCoords;
vec4 FragPosLightSpace;
+ vec4 Color;
} fs_in;
// --- Uniforms
@@ -55,7 +56,8 @@ void main() {
result += CalcPointLight(pointLights[i], norm, fs_in.FragPos, viewDir);
}
- FragColor = vec4(result, 1.0);
+// FragColor = vec4(result, 1.0);
+ FragColor = fs_in.Color + 0.5;
}
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
diff --git a/assets/shaders/blinn_phong.vert b/assets/shaders/blinn_phong.vert
index 6028178..06dc5e7 100644
--- a/assets/shaders/blinn_phong.vert
+++ b/assets/shaders/blinn_phong.vert
@@ -15,6 +15,7 @@ out VS_OUT {
vec3 Normal;
vec2 TexCoords;
vec4 FragPosLightSpace;
+ vec4 Color;
} vs_out;
void main() {
@@ -22,5 +23,6 @@ void main() {
vs_out.Normal = inNormal;
vs_out.TexCoords = inTexCoords;
vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
+ vs_out.Color = vec4(1.0);
gl_Position = projection * view * model * vec4(inPos, 1.0);
} \ No newline at end of file
diff --git a/assets/shaders/cube.frag b/assets/shaders/cube.frag
new file mode 100644
index 0000000..292578f
--- /dev/null
+++ b/assets/shaders/cube.frag
@@ -0,0 +1,12 @@
+#version 450
+
+layout(location = 0) in vec3 fragColor;
+layout(location = 1) in vec2 fragTexCoord;
+
+layout(binding = 1) uniform sampler2D texSampler;
+
+layout(location = 0) out vec4 outColor;
+
+void main() {
+ outColor = texture(texSampler, fragTexCoord); // vec4(fragTexCoord, 0.0);
+}
diff --git a/assets/shaders/cube.vert b/assets/shaders/cube.vert
new file mode 100644
index 0000000..dc625ec
--- /dev/null
+++ b/assets/shaders/cube.vert
@@ -0,0 +1,21 @@
+#version 450
+
+layout(binding = 0) uniform UniformBufferObject {
+ mat4 model;
+ mat4 view;
+ mat4 proj;
+}
+ubo;
+
+layout(location = 0) in vec3 inPosition;
+layout(location = 1) in vec3 inNormal;
+layout(location = 2) in vec2 inTexCoords;
+
+layout(location = 0) out vec3 fragColor;
+layout(location = 1) out vec2 fragTexCoord;
+
+void main() {
+ gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
+ fragColor = abs(inNormal);
+ fragTexCoord = inTexCoords;
+}
diff --git a/assets/shaders/object.frag b/assets/shaders/object.frag
new file mode 100644
index 0000000..fa50fcf
--- /dev/null
+++ b/assets/shaders/object.frag
@@ -0,0 +1,15 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 in_position;
+layout(location = 1) in vec3 in_normal;
+layout(location = 2) in vec2 in_tex_coord;
+
+layout(set = 0, binding = 1) uniform sampler2D texSampler;
+
+layout(location = 0) out vec4 out_color;
+
+void main() {
+ // out_color = vec4(1.0);
+ out_color = texture(texSampler, in_tex_coord);
+} \ No newline at end of file
diff --git a/assets/shaders/object.vert b/assets/shaders/object.vert
new file mode 100644
index 0000000..23c0ffb
--- /dev/null
+++ b/assets/shaders/object.vert
@@ -0,0 +1,29 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 in_position;
+layout(location = 1) in vec3 in_normal;
+layout(location = 2) in vec2 in_tex_coord;
+
+layout(location = 0) out vec3 out_position;
+layout(location = 1) out vec3 out_normal;
+layout(location = 2) out vec2 out_tex_coord;
+
+layout(set = 0, binding = 0) uniform global_object_uniform {
+ mat4 projection;
+ mat4 view;
+}
+global_ubo;
+
+layout(push_constant) uniform push_constants {
+ mat4 model; // 64 bytes
+}
+u_push_constants;
+
+void main() {
+ gl_Position = global_ubo.projection * global_ubo.view * u_push_constants.model *
+ vec4(in_position.x, in_position.y, in_position.z, 1.0);
+ out_position = in_position;
+ out_normal = in_normal;
+ out_tex_coord = in_tex_coord;
+} \ No newline at end of file
diff --git a/assets/shaders/skinned.vert b/assets/shaders/skinned.vert
new file mode 100644
index 0000000..cb2ca85
--- /dev/null
+++ b/assets/shaders/skinned.vert
@@ -0,0 +1,59 @@
+#version 410 core
+// Inputs
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inTexCoords;
+layout (location = 3) in ivec4 inBoneIndices;
+layout (location = 4) in vec4 inWeights;
+
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
+uniform mat4 lightSpaceMatrix;
+
+const int MAX_BONES = 100;
+const int MAX_BONE_INFLUENCES = 4;
+uniform mat4 boneMatrices[MAX_BONES]; // TODO!
+
+// Output
+out VS_OUT {
+ vec3 FragPos;
+ vec3 Normal;
+ vec2 TexCoords;
+ vec4 FragPosLightSpace;
+ vec4 Color;
+} vs_out;
+
+void main() {
+ // vec4 totalPosition = vec4(0.0f);
+ // for(int i = 0 ; i < MAX_BONE_INFLUENCES ; i++) {
+ // if(inBoneIndices[i] == 0)
+ // continue;
+ // if(inBoneIndices[i] >=MAX_BONES)
+ // {
+ // totalPosition = vec4(inPos,1.0f);
+ // break;
+ // }
+ // vec4 localPosition = finalBoneMatrices[inBoneIndices[i]] * vec4(inPos,1.0f);
+ // totalPosition += localPosition * inWeights[i];
+ // vec3 localNormal = mat3(finalBoneMatrices[inBoneIndices[i]]) * inNormal;
+ // vs_out.Normal = localNormal;
+ // }
+
+
+ mat4 skinMatrix = // mat4(1.0)
+ // boneMatrices[int(inBoneIndices.z)]; // should just be the identtiy
+ inWeights.x * boneMatrices[int(inBoneIndices.x)] +
+ inWeights.y * boneMatrices[int(inBoneIndices.y)] +
+ inWeights.z * boneMatrices[int(inBoneIndices.z)] +
+ inWeights.w * boneMatrices[int(inBoneIndices.w)];
+
+ vec4 totalPosition = skinMatrix * vec4(inPos, 1.0);
+
+ vs_out.FragPos = vec3(model * totalPosition);
+ // vs_out.Normal = inNormal;
+ vs_out.TexCoords = inTexCoords;
+ vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
+ vs_out.Color = inWeights;
+ gl_Position = projection * view * model * totalPosition;
+} \ No newline at end of file
diff --git a/assets/shaders/triangle.frag b/assets/shaders/triangle.frag
new file mode 100644
index 0000000..c3ff328
--- /dev/null
+++ b/assets/shaders/triangle.frag
@@ -0,0 +1,6 @@
+#version 450
+
+layout(location = 0) in vec3 fragColor;
+layout(location = 0) out vec4 outColor;
+
+void main() { outColor = vec4(fragColor, 0.0); }
diff --git a/assets/shaders/triangle.vert b/assets/shaders/triangle.vert
new file mode 100644
index 0000000..8030561
--- /dev/null
+++ b/assets/shaders/triangle.vert
@@ -0,0 +1,11 @@
+#version 450
+
+layout(location = 0) in vec2 inPos;
+layout(location = 1) in vec3 inColor;
+
+layout(location = 0) out vec3 fragColor;
+
+void main() {
+ gl_Position = vec4(inPos, 0.0, 1.0);
+ fragColor = inColor;
+}
diff --git a/assets/textures/texture.jpg b/assets/textures/texture.jpg
new file mode 100644
index 0000000..975e338
--- /dev/null
+++ b/assets/textures/texture.jpg
Binary files differ
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..f289e80
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,11 @@
+
+### RAL examples
+
+These examples show off how the lowest register of rendering, the Rendering Abstraction Layer (RAL), is used to draw things to the screen.
+
+- triangle
+- cube
+
+### Renderer
+
+The 'renderer' is the default renderer provided by Celeritas.
diff --git a/examples/cube/ex_cube.c b/examples/cube/ex_cube.c
new file mode 100644
index 0000000..fc59e2d
--- /dev/null
+++ b/examples/cube/ex_cube.c
@@ -0,0 +1,158 @@
+#include <glfw3.h>
+
+#include "buf.h"
+#include "camera.h"
+#include "core.h"
+#include "file.h"
+#include "log.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "mem.h"
+#include "primitives.h"
+#include "ral.h"
+#include "ral_types.h"
+#include "render.h"
+#include "render_types.h"
+
+extern core g_core;
+
+// Define the shader data
+typedef struct mvp_uniforms {
+ mat4 model;
+ mat4 view;
+ mat4 projection;
+} mvp_uniforms;
+typedef struct my_shader_bind_group {
+ mvp_uniforms mvp;
+ texture_handle tex;
+} my_shader_bind_group;
+
+// We also must create a function that knows how to return a `shader_data_layout`
+shader_data_layout mvp_uniforms_layout(void* data) {
+ my_shader_bind_group* d = (my_shader_bind_group*)data;
+ bool has_data = data != NULL;
+
+ shader_binding b1 = { .label = "mvp_uniforms",
+ .type = SHADER_BINDING_BYTES,
+ .stores_data = has_data,
+ .data = { .bytes = { .size = sizeof(mvp_uniforms) } } };
+
+ shader_binding b2 = { .label = "texture_sampler",
+ .type = SHADER_BINDING_TEXTURE,
+ .stores_data = has_data };
+ if (has_data) {
+ b1.data.bytes.data = &d->mvp;
+ b2.data.texture.handle = d->tex;
+ }
+ return (shader_data_layout){ .name = "global_ubo", .bindings = { b1, b2 }, .bindings_count = 2 };
+}
+
+int main() {
+ core_bringup();
+ arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024);
+
+ DEBUG("render capacity %d", g_core.default_scene.renderables->capacity);
+
+ vec3 camera_pos = vec3(2., 2., 2.);
+ vec3 camera_front = vec3_normalise(vec3_negate(camera_pos));
+ camera cam = camera_create(camera_pos, camera_front, VEC3_Y, deg_to_rad(45.0));
+
+ vertex_description vertex_input = { .use_full_vertex_size = true };
+ vertex_input.debug_label = "Standard Static 3D Vertex Format";
+ vertex_desc_add(&vertex_input, "inPosition", ATTR_F32x3);
+ vertex_desc_add(&vertex_input, "inNormal", ATTR_F32x3);
+ vertex_desc_add(&vertex_input, "inTexCoords", ATTR_F32x2);
+
+ shader_data mvp_uniforms_data = { .data = NULL, .shader_data_get_layout = &mvp_uniforms_layout };
+
+ gpu_renderpass_desc pass_description = {};
+ gpu_renderpass* renderpass = gpu_renderpass_create(&pass_description);
+
+ str8 vert_path = str8lit("build/linux/x86_64/debug/cube.vert.spv");
+ str8 frag_path = str8lit("build/linux/x86_64/debug/cube.frag.spv");
+ str8_opt vertex_shader = str8_from_file(&scratch, vert_path);
+ str8_opt fragment_shader = str8_from_file(&scratch, frag_path);
+ if (!vertex_shader.has_value || !fragment_shader.has_value) {
+ ERROR_EXIT("Failed to load shaders from disk")
+ }
+
+ struct graphics_pipeline_desc pipeline_description = {
+ .debug_name = "Basic Pipeline",
+ .vertex_desc = vertex_input,
+ .data_layouts = { mvp_uniforms_data },
+ .data_layouts_count = 1,
+ .vs = { .debug_name = "Triangle Vertex Shader",
+ .filepath = vert_path,
+ .code = vertex_shader.contents,
+ .is_spirv = true },
+ .fs = { .debug_name = "Triangle Fragment Shader",
+ .filepath = frag_path,
+ .code = fragment_shader.contents,
+ .is_spirv = true },
+ .renderpass = renderpass,
+ .wireframe = false,
+ .depth_test = false
+ };
+ gpu_pipeline* gfx_pipeline = gpu_graphics_pipeline_create(pipeline_description);
+
+ // Geometry
+ geometry_data cube_data = geo_create_cuboid(f32x3(1, 1, 1));
+ mesh cube = mesh_create(&cube_data, false);
+
+ // Texture
+ texture_data tex_data = texture_data_load("assets/textures/texture.jpg", false);
+ texture_handle texture = texture_data_upload(tex_data, true);
+ printf("Texture %d", texture.raw);
+
+ // Main loop
+ while (!should_exit(&g_core)) {
+ input_update(&g_core.input);
+
+ if (!gpu_backend_begin_frame()) {
+ continue;
+ }
+ gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder();
+ // begin recording
+ gpu_cmd_encoder_begin(*enc);
+ gpu_cmd_encoder_begin_render(enc, renderpass);
+ encode_bind_pipeline(enc, PIPELINE_GRAPHICS, gfx_pipeline);
+ encode_set_default_settings(enc);
+
+ static f32 x = 0.0;
+ x += 0.01;
+ quat rotation = quat_from_axis_angle(VEC3_Y, x, true);
+ mat4 translation = mat4_translation(vec3(-0.5, -0.5, -0.5));
+ mat4 model = mat4_rotation(rotation);
+ model = mat4_mult(translation, model);
+ mat4 view, proj;
+ camera_view_projection(&cam, g_core.renderer.swapchain.extent.width,
+ g_core.renderer.swapchain.extent.height, &view, &proj);
+ mvp_uniforms mvp_data = { .model = model, .view = view, .projection = proj };
+ my_shader_bind_group shader_bind_data = { .mvp = mvp_data, .tex = texture };
+ mvp_uniforms_data.data = &shader_bind_data;
+ encode_bind_shader_data(enc, 0, &mvp_uniforms_data);
+
+ // Record draw calls
+ // -- NEW
+ draw_mesh(&cube, &model);
+ // -- OLD
+ /* encode_set_vertex_buffer(enc, triangle_vert_buf); */
+ /* encode_set_index_buffer(enc, triangle_index_buf); */
+ /* gpu_temp_draw(6); */
+
+ // End recording
+ gpu_cmd_encoder_end_render(enc);
+
+ gpu_cmd_buffer buf = gpu_cmd_encoder_finish(enc);
+ gpu_queue_submit(&buf);
+ // Submit
+ gpu_backend_end_frame();
+
+ // render_frame_end(&g_core.renderer);
+ // glfwSwapBuffers(core->renderer.window);
+ }
+
+ renderer_shutdown(&g_core.renderer);
+
+ return 0;
+}
diff --git a/examples/example_scene.h b/examples/example_scene.h
new file mode 100644
index 0000000..eb0be18
--- /dev/null
+++ b/examples/example_scene.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "render_types.h"
+
+const vec3 pointlight_positions[4] = {
+ { 0.7, 0.2, 2.0 },
+ { 2.3, -3.3, -4.0 },
+ { -4.0, 2.0, -12.0 },
+ { 0.0, 0.0, -3.0 },
+};
+static point_light point_lights[4];
+
+static scene make_default_scene() {
+ directional_light dir_light = { .direction = (vec3){ -0.2, -1.0, -0.3 },
+ .ambient = (vec3){ 0.2, 0.2, 0.2 },
+ .diffuse = (vec3){ 0.5, 0.5, 0.5 },
+ .specular = (vec3){ 1.0, 1.0, 1.0 } };
+
+ for (int i = 0; i < 4; i++) {
+ point_lights[i].position = pointlight_positions[i];
+ point_lights[i].ambient = (vec3){ 0.05, 0.05, 0.05 };
+ point_lights[i].diffuse = (vec3){ 0.8, 0.8, 0.8 };
+ point_lights[i].specular = (vec3){ 1.0, 1.0, 1.0 };
+ point_lights[i].constant = 1.0;
+ point_lights[i].linear = 0.09;
+ point_lights[i].quadratic = 0.032;
+ }
+
+ scene our_scene = { .dir_light = dir_light, .n_point_lights = 4 };
+ memcpy(&our_scene.point_lights, &point_lights, sizeof(point_light[4]));
+ return our_scene;
+} \ No newline at end of file
diff --git a/examples/gltf_loading/ex_gltf_loading.c b/examples/gltf_loading/ex_gltf_loading.c
index 0c47fec..fa74ada 100644
--- a/examples/gltf_loading/ex_gltf_loading.c
+++ b/examples/gltf_loading/ex_gltf_loading.c
@@ -1,8 +1,10 @@
#include <glfw3.h>
+#include "animation.h"
#include "camera.h"
#include "core.h"
#include "loaders.h"
+#include "log.h"
#include "maths.h"
#include "maths_types.h"
#include "render.h"
@@ -52,7 +54,7 @@ int main() {
scene our_scene = { .dir_light = dir_light, .n_point_lights = 4 };
memcpy(&our_scene.point_lights, &point_lights, sizeof(point_light[4]));
- while (!glfwWindowShouldClose(core->renderer.window)) {
+ while (!should_exit(core)) {
currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
diff --git a/examples/input/ex_input.c b/examples/input/ex_input.c
new file mode 100644
index 0000000..576b364
--- /dev/null
+++ b/examples/input/ex_input.c
@@ -0,0 +1,120 @@
+#include <glfw3.h>
+
+#include "camera.h"
+#include "core.h"
+#include "input.h"
+#include "keys.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "primitives.h"
+#include "render.h"
+#include "render_backend.h"
+#include "render_types.h"
+
+typedef struct game_state {
+ camera camera;
+ vec3 camera_euler;
+ bool first_mouse_update; // so the camera doesnt lurch when you run the first
+ // process_camera_rotation
+} game_state;
+
+void update_camera_rotation(input_state* input, game_state* game, camera* cam);
+
+int main() {
+ core* core = core_bringup();
+
+ vec3 cam_pos = vec3_create(5, 5, 5);
+ game_state game = {
+ .camera = camera_create(cam_pos, vec3_negate(cam_pos), VEC3_Y, deg_to_rad(45.0)),
+ .camera_euler = vec3_create(90, 0, 0),
+ .first_mouse_update = true,
+ };
+
+ // load a texture
+ texture tex = texture_data_load("assets/models/obj/cube/container.jpg", false);
+ texture_data_upload(&tex);
+
+ printf("Starting look direction: ");
+ print_vec3(game.camera.front);
+
+ // Main loop
+ const f32 camera_lateral_speed = 0.2;
+ const f32 camera_zoom_speed = 0.15;
+
+ while (!should_exit(core)) {
+ input_update(&core->input);
+
+ vec3 translation = VEC3_ZERO;
+ if (key_is_pressed(KEYCODE_W) || key_is_pressed(KEYCODE_KEY_UP)) {
+ printf("Move Forwards\n");
+ translation = vec3_mult(game.camera.front, camera_zoom_speed);
+ } else if (key_is_pressed(KEYCODE_S) || key_is_pressed(KEYCODE_KEY_DOWN)) {
+ printf("Move Backwards\n");
+ translation = vec3_mult(game.camera.front, -camera_zoom_speed);
+ } else if (key_is_pressed(KEYCODE_A) || key_is_pressed(KEYCODE_KEY_LEFT)) {
+ printf("Move Left\n");
+ vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up));
+ translation = vec3_mult(lateral, -camera_lateral_speed);
+ } else if (key_is_pressed(KEYCODE_D) || key_is_pressed(KEYCODE_KEY_RIGHT)) {
+ printf("Move Right\n");
+ vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up));
+ translation = vec3_mult(lateral, camera_lateral_speed);
+ }
+ game.camera.position = vec3_add(game.camera.position, translation);
+ // update_camera_rotation(&core->input, &game, &game.camera);
+
+ // UNUSED: threadpool_process_results(&core->threadpool, 1);
+
+ render_frame_begin(&core->renderer);
+
+ mat4 model = mat4_translation(VEC3_ZERO);
+
+ gfx_backend_draw_frame(&core->renderer, &game.camera, model, &tex);
+
+ render_frame_end(&core->renderer);
+ }
+
+ core_shutdown(core);
+
+ return 0;
+}
+
+void update_camera_rotation(input_state* input, game_state* game, camera* cam) {
+ float xoffset = -input->mouse.x_delta; // xpos - lastX;
+ float yoffset = -input->mouse.y_delta; // reversed since y-coordinates go from bottom to top
+ if (game->first_mouse_update) {
+ xoffset = 0.0;
+ yoffset = 0.0;
+ game->first_mouse_update = false;
+ }
+
+ float sensitivity = 0.2f; // change this value to your liking
+ xoffset *= sensitivity;
+ yoffset *= sensitivity;
+
+ // x = yaw
+ game->camera_euler.x += xoffset;
+ // y = pitch
+ game->camera_euler.y += yoffset;
+ // we dont update roll
+
+ f32 yaw = game->camera_euler.x;
+ f32 pitch = game->camera_euler.y;
+
+ // make sure that when pitch is out of bounds, screen doesn't get flipped
+ if (game->camera_euler.y > 89.0f) game->camera_euler.y = 89.0f;
+ if (game->camera_euler.y < -89.0f) game->camera_euler.y = -89.0f;
+
+ vec3 front = cam->front;
+ front.x = cos(deg_to_rad(yaw) * cos(deg_to_rad(pitch)));
+ front.y = sin(deg_to_rad(pitch));
+ front.z = sin(deg_to_rad(yaw)) * cos(deg_to_rad(pitch));
+
+ front = vec3_normalise(front);
+ // save it back
+ cam->front.x = front.x;
+ cam->front.y = front.y;
+ // roll is static
+
+ print_vec3(cam->front);
+}
diff --git a/examples/main_loop/ex_main_loop.c b/examples/main_loop/ex_main_loop.c
index 3b2354a..4e31313 100644
--- a/examples/main_loop/ex_main_loop.c
+++ b/examples/main_loop/ex_main_loop.c
@@ -1,21 +1,33 @@
#include <glfw3.h>
+#include "camera.h"
#include "core.h"
+#include "maths.h"
#include "render.h"
int main() {
core* core = core_bringup();
+ camera camera = camera_create(vec3_create(0, 0, 20), VEC3_NEG_Z, VEC3_Y, deg_to_rad(45.0));
+
// Main loop
while (!glfwWindowShouldClose(core->renderer.window)) {
input_update(&core->input);
- threadpool_process_results(&core->threadpool, 1);
+ // threadpool_process_results(&core->threadpool, 1);
render_frame_begin(&core->renderer);
+ static f32 x = 0.0;
+ x += 0.01;
+ mat4 model = mat4_translation(vec3(x, 0, 0));
+
+ gfx_backend_draw_frame(&core->renderer, &camera, model, NULL);
+
// insert work here
render_frame_end(&core->renderer);
+ glfwSwapBuffers(core->renderer.window);
+ glfwPollEvents();
}
return 0;
diff --git a/examples/obj_loading/ex_obj_loading.c b/examples/obj_loading/ex_obj_loading.c
index e225cb2..906816b 100644
--- a/examples/obj_loading/ex_obj_loading.c
+++ b/examples/obj_loading/ex_obj_loading.c
@@ -1,3 +1,4 @@
+#include <assert.h>
#include <glfw3.h>
#include <string.h>
@@ -25,6 +26,7 @@ int main() {
model_handle backpack_handle =
model_load_obj(core, "assets/models/obj/backpack/backpack.obj", true);
model* backpack = &core->models->data[backpack_handle.raw];
+ assert(backpack->meshes->data->is_skinned == false);
// 2. upload vertex data to gpu
model_upload_meshes(&core->renderer, backpack);
// 3. create a camera
@@ -53,7 +55,7 @@ int main() {
memcpy(&our_scene.point_lights, &point_lights, sizeof(point_light[4]));
// --- Enter Main loop
- while (!glfwWindowShouldClose(core->renderer.window)) {
+ while (!should_exit(core)) {
input_update(&core->input);
threadpool_process_results(&core->threadpool, 1);
diff --git a/examples/property_animation/ex_property_animation.c b/examples/property_animation/ex_property_animation.c
new file mode 100644
index 0000000..c548db4
--- /dev/null
+++ b/examples/property_animation/ex_property_animation.c
@@ -0,0 +1,103 @@
+#include <glfw3.h>
+
+#include "../example_scene.h"
+#include "animation.h"
+#include "camera.h"
+#include "core.h"
+#include "input.h"
+#include "keys.h"
+#include "log.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "primitives.h"
+#include "render.h"
+#include "render_backend.h"
+#include "render_types.h"
+
+typedef struct game_state {
+ camera camera;
+ vec3 camera_euler;
+ bool first_mouse_update; // so the camera doesnt lurch when you run the first
+ // process_camera_rotation
+} game_state;
+
+void update_camera_rotation(input_state* input, game_state* game, camera* cam);
+
+int main() {
+ double currentFrame = glfwGetTime();
+ double lastFrame = currentFrame;
+ double deltaTime;
+
+ core* core = core_bringup();
+
+ model_handle animated_cube_handle =
+ model_load_gltf(core, "assets/models/gltf/AnimatedCube/glTF/AnimatedCube.gltf", false);
+ model* cube = &core->models->data[animated_cube_handle.raw];
+ model_upload_meshes(&core->renderer, cube);
+
+ scene our_scene = make_default_scene();
+
+ vec3 cam_pos = vec3_create(5, 5, 5);
+ game_state game = {
+ .camera = camera_create(cam_pos, vec3_negate(cam_pos), VEC3_Y, deg_to_rad(45.0)),
+ .camera_euler = vec3_create(90, 0, 0),
+ .first_mouse_update = true,
+ };
+
+ print_vec3(game.camera.front);
+
+ // Main loop
+ const f32 camera_lateral_speed = 0.2;
+ const f32 camera_zoom_speed = 0.15;
+
+ // animation
+ animation_clip track = cube->animations->data[0];
+ f64 total_time = 0.0;
+
+ while (!should_exit(core)) {
+ input_update(&core->input);
+
+ currentFrame = glfwGetTime();
+ deltaTime = currentFrame - lastFrame;
+ lastFrame = currentFrame;
+ total_time += deltaTime;
+ printf("delta time %f\n", deltaTime);
+ f64 t = fmod(total_time, 1.0);
+ INFO("Total time: %f", t);
+
+ vec3 translation = VEC3_ZERO;
+ if (key_is_pressed(KEYCODE_W) || key_is_pressed(KEYCODE_KEY_UP)) {
+ translation = vec3_mult(game.camera.front, camera_zoom_speed);
+ } else if (key_is_pressed(KEYCODE_S) || key_is_pressed(KEYCODE_KEY_DOWN)) {
+ translation = vec3_mult(game.camera.front, -camera_zoom_speed);
+ } else if (key_is_pressed(KEYCODE_A) || key_is_pressed(KEYCODE_KEY_LEFT)) {
+ vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up));
+ translation = vec3_mult(lateral, -camera_lateral_speed);
+ } else if (key_is_pressed(KEYCODE_D) || key_is_pressed(KEYCODE_KEY_RIGHT)) {
+ vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up));
+ translation = vec3_mult(lateral, camera_lateral_speed);
+ }
+ game.camera.position = vec3_add(game.camera.position, translation);
+
+ // UNUSED: threadpool_process_results(&core->threadpool, 1);
+
+ render_frame_begin(&core->renderer);
+
+ quat rot = animation_sample(track.rotation, t).rotation;
+ // quat rot = quat_ident();
+ transform tf = transform_create(VEC3_ZERO, rot, 1.0);
+ mat4 model_tf = transform_to_mat(&tf);
+ draw_model(&core->renderer, &game.camera, cube, &model_tf, &our_scene);
+
+ // gfx_backend_draw_frame(&core->renderer, &game.camera, model, NULL);
+
+ render_frame_end(&core->renderer);
+ }
+
+ INFO("Shutting down");
+ model_destroy(cube);
+
+ core_shutdown(core);
+
+ return 0;
+}
diff --git a/examples/skinned_animation/ex_skinned_animation.c b/examples/skinned_animation/ex_skinned_animation.c
new file mode 100644
index 0000000..c31e93c
--- /dev/null
+++ b/examples/skinned_animation/ex_skinned_animation.c
@@ -0,0 +1,122 @@
+#include <assert.h>
+#include <glfw3.h>
+
+#include "../example_scene.h"
+#include "animation.h"
+#include "camera.h"
+#include "core.h"
+#include "input.h"
+#include "keys.h"
+#include "log.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "primitives.h"
+#include "render.h"
+#include "render_backend.h"
+#include "render_types.h"
+
+typedef struct game_state {
+ camera camera;
+ vec3 camera_euler;
+ bool first_mouse_update; // so the camera doesnt lurch when you run the first
+ // process_camera_rotation
+} game_state;
+
+void update_camera_rotation(input_state* input, game_state* game, camera* cam);
+
+int main() {
+ double currentFrame = glfwGetTime();
+ double lastFrame = currentFrame;
+ double deltaTime;
+
+ core* core = core_bringup();
+
+ model_handle handle =
+ model_load_gltf(core, "assets/models/gltf/SimpleSkin/glTF/SimpleSkin.gltf", false);
+
+ model* simple_skin = &core->models->data[handle.raw];
+
+ // Okay, right here we've loaded the model. let's assert some facts
+ assert(simple_skin->animations->len == 1);
+ assert(simple_skin->animations->data[0].rotation != NULL);
+ assert(simple_skin->animations->data[0].translation == NULL);
+ assert(simple_skin->animations->data[0].scale == NULL);
+
+ mesh* m = &simple_skin->meshes->data[0];
+ assert(m->is_skinned);
+ assert(m->bones->len == 2); // 1 root and 1 extra joint
+
+ // assert(false);
+
+ model_upload_meshes(&core->renderer, simple_skin);
+
+ scene our_scene = make_default_scene();
+
+ vec3 cam_pos = vec3_create(0, 5, -8);
+ game_state game = {
+ .camera = camera_create(cam_pos, vec3_negate(cam_pos), VEC3_Y, deg_to_rad(45.0)),
+ .camera_euler = vec3_create(90, 0, 0),
+ .first_mouse_update = true,
+ };
+
+ print_vec3(game.camera.front);
+
+ // Main loop
+ const f32 camera_lateral_speed = 0.2;
+ const f32 camera_zoom_speed = 0.10;
+
+ // animation
+ animation_clip track = simple_skin->animations->data[0];
+ f64 total_time = 0.0;
+
+ while (!should_exit(core)) {
+ input_update(&core->input);
+
+ currentFrame = glfwGetTime();
+ deltaTime = currentFrame - lastFrame;
+ lastFrame = currentFrame;
+ total_time += deltaTime;
+ // printf("delta time %f\n", deltaTime);
+ f64 t = fmod(total_time, track.rotation->max);
+ // INFO("Total time: %f", t);
+
+ vec3 translation = VEC3_ZERO;
+ if (key_is_pressed(KEYCODE_W)) {
+ translation = vec3_mult(game.camera.front, camera_zoom_speed);
+ } else if (key_is_pressed(KEYCODE_KEY_UP)) {
+ translation = vec3_mult(game.camera.up, camera_lateral_speed);
+ } else if (key_is_pressed(KEYCODE_KEY_DOWN)) {
+ translation = vec3_mult(game.camera.up, -camera_lateral_speed);
+ } else if (key_is_pressed(KEYCODE_S) || key_is_pressed(KEYCODE_KEY_DOWN)) {
+ translation = vec3_mult(game.camera.front, -camera_zoom_speed);
+ } else if (key_is_pressed(KEYCODE_A) || key_is_pressed(KEYCODE_KEY_LEFT)) {
+ vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up));
+ translation = vec3_mult(lateral, -camera_lateral_speed);
+ } else if (key_is_pressed(KEYCODE_D) || key_is_pressed(KEYCODE_KEY_RIGHT)) {
+ vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up));
+ translation = vec3_mult(lateral, camera_lateral_speed);
+ }
+ game.camera.position = vec3_add(game.camera.position, translation);
+
+ render_frame_begin(&core->renderer);
+
+ // bone rotation
+ quat rot = animation_sample(track.rotation, t).rotation;
+
+ m->bones->data[1].transform_components.rotation = rot;
+
+ // quat rot = quat_ident();
+ transform tf = transform_create(VEC3_ZERO, quat_ident(), 1.0);
+
+ draw_skinned_model(&core->renderer, &game.camera, simple_skin, tf, &our_scene);
+
+ render_frame_end(&core->renderer);
+ }
+
+ INFO("Shutting down");
+ model_destroy(simple_skin);
+
+ core_shutdown(core);
+
+ return 0;
+}
diff --git a/examples/transforms/ex_transforms.c b/examples/transforms/ex_transforms.c
index 689c49d..fc225b4 100644
--- a/examples/transforms/ex_transforms.c
+++ b/examples/transforms/ex_transforms.c
@@ -5,7 +5,7 @@
#include "maths_types.h"
#include "mem.h"
#include "render.h"
-#include "render_types.h"
+// #include "render_types.h"
#include "transform_hierarchy.h"
const vec3 pointlight_positions[4] = {
diff --git a/examples/triangle/ex_triangle.c b/examples/triangle/ex_triangle.c
new file mode 100644
index 0000000..d57e224
--- /dev/null
+++ b/examples/triangle/ex_triangle.c
@@ -0,0 +1,104 @@
+#include <glfw3.h>
+
+#include "backend_vulkan.h"
+#include "buf.h"
+#include "camera.h"
+#include "core.h"
+#include "file.h"
+#include "log.h"
+#include "maths.h"
+#include "mem.h"
+#include "ral.h"
+#include "ral_types.h"
+#include "render.h"
+#include "render_types.h"
+
+extern core g_core;
+
+const custom_vertex vertices[] = {
+ (custom_vertex){ .pos = vec2(-0.5, -0.5), .color = vec3(1.0, 1.0, 1.0) },
+ (custom_vertex){ .pos = vec2(0.5, -0.5), .color = vec3(1.0, 0.0, 0.0) },
+ (custom_vertex){ .pos = vec2(-0.5, 0.5), .color = vec3(0.0, 0.0, 1.0) },
+ (custom_vertex){ .pos = vec2(0.5, 0.5), .color = vec3(0.0, 1.0, 0.0) },
+};
+const u32 indices[] = { 2, 1, 0, 1, 2, 3 };
+
+int main() {
+ core_bringup();
+ arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024);
+
+ vertex_description vertex_input = { .use_full_vertex_size = false };
+ vertex_input.debug_label = "Hello";
+ vertex_desc_add(&vertex_input, "inPos", ATTR_F32x2);
+ vertex_desc_add(&vertex_input, "inColor", ATTR_F32x3);
+
+ gpu_renderpass_desc pass_description = {};
+ gpu_renderpass* renderpass = gpu_renderpass_create(&pass_description);
+
+ str8 vert_path = str8lit("build/linux/x86_64/debug/triangle.vert.spv");
+ str8 frag_path = str8lit("build/linux/x86_64/debug/triangle.frag.spv");
+ str8_opt vertex_shader = str8_from_file(&scratch, vert_path);
+ str8_opt fragment_shader = str8_from_file(&scratch, frag_path);
+ if (!vertex_shader.has_value || !fragment_shader.has_value) {
+ ERROR_EXIT("Failed to load shaders from disk")
+ }
+
+ struct graphics_pipeline_desc pipeline_description = {
+ .debug_name = "Basic Pipeline",
+ .vertex_desc = vertex_input,
+ // .data_layouts = {0},
+ // .data_layouts_count = 0,
+ .vs = { .debug_name = "Triangle Vertex Shader",
+ .filepath = vert_path,
+ .code = vertex_shader.contents,
+ .is_spirv = true },
+ .fs = { .debug_name = "Triangle Fragment Shader",
+ .filepath = frag_path,
+ .code = fragment_shader.contents,
+ .is_spirv = true },
+ .renderpass = renderpass,
+ .wireframe = false,
+ .depth_test = false
+ };
+ gpu_pipeline* gfx_pipeline = gpu_graphics_pipeline_create(pipeline_description);
+
+ // Load triangle vertex and index data
+ buffer_handle triangle_vert_buf =
+ gpu_buffer_create(4 * sizeof(vertex), CEL_BUFFER_VERTEX, CEL_BUFFER_FLAG_GPU, vertices);
+
+ buffer_handle triangle_index_buf =
+ gpu_buffer_create(sizeof(indices), CEL_BUFFER_INDEX, CEL_BUFFER_FLAG_GPU, indices);
+
+ // Main loop
+ while (!should_exit()) {
+ input_update(&g_core.input);
+
+ if (!gpu_backend_begin_frame()) {
+ continue;
+ }
+ gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder();
+ // Begin recording
+ gpu_cmd_encoder_begin(*enc);
+ gpu_cmd_encoder_begin_render(enc, renderpass);
+ encode_bind_pipeline(enc, PIPELINE_GRAPHICS, gfx_pipeline);
+ encode_set_default_settings(enc);
+
+ // Record draw calls
+ encode_set_vertex_buffer(enc, triangle_vert_buf);
+ encode_set_index_buffer(enc, triangle_index_buf);
+ encode_draw_indexed(enc, 6);
+
+ // End recording
+ gpu_cmd_encoder_end_render(enc);
+
+ gpu_cmd_buffer buf = gpu_cmd_encoder_finish(
+ enc); // Command buffer is no longer recording and is ready to submit
+ // Submit
+ gpu_queue_submit(&buf);
+ gpu_backend_end_frame();
+ }
+
+ renderer_shutdown(&g_core.renderer);
+
+ return 0;
+}
diff --git a/include/celeritas.h b/include/celeritas.h
new file mode 100644
index 0000000..9ba741b
--- /dev/null
+++ b/include/celeritas.h
@@ -0,0 +1,65 @@
+/**
+ * @file celeritas.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-05-09
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+
+/* The Goal of this file is to test ocaml-bindgen on it to start moving development over into OCaml */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+// Forward Declarations
+typedef struct core core;
+
+// Handles
+typedef uint32_t model_handle;
+
+// Maths
+typedef struct vec2 { float x, y; } vec2;
+typedef struct vec3 { float x, y, z; } vec3;
+typedef struct vec4 { float x, y, z, w; } vec4;
+typedef struct mat4 { float data[16]; } mat4;
+typedef struct transform3d { vec3 translation; vec4 rotation; float scale; } transform3d;
+
+// Lifecycle functions
+void core_bringup();
+void core_shutdown();
+bool should_window_close();
+
+void frame_begin();
+void frame_draw();
+void frame_end();
+
+// Assets
+model_handle model_load(const char* filepath);
+
+// Rendering
+typedef struct render_entity {
+ model_handle model;
+ // TODO: material
+ transform3d transform;
+} render_entity;
+
+void render_frame_begin();
+
+// Scene
+typedef struct directional_light {} directional_light;
+typedef struct point_light {} point_light;
+void scene_add_dir_light(directional_light light);
+void scene_add_point_light(directional_light light);
+void scene_add_model(model_handle model, transform3d transform);
+bool scene_remove_model(model_handle model);
+
+void scene_set_model_transform(model_handle model, transform3d new_transform);
+void scene_set_camera(vec3 pos, vec3 front);
+
+// Immediate mode drawing
+
+// Input \ No newline at end of file
diff --git a/scratchpad.md b/scratchpad.md
deleted file mode 100644
index 19ad8b4..0000000
--- a/scratchpad.md
+++ /dev/null
@@ -1,71 +0,0 @@
-
-```c
-// engine.h
-#pragma once
-
-#include <celstd.h>
-#include <renderer.h>
-#include <renderer_types.h>
-#include <maths_types.h>
-#include "threadpool.h"
-
-typedef struct frame_stats {} frame_stats;
-
-typedef struct engine_stats {
- frame_stats frame;
-} engine_stats;
-
-typedef struct engine {
- // timing
- float startTime;
- float deltaTime; // time between current frame and last frame
- float lastFrame;
- // stats
- engine_stats stats;
-} engine;
-
-static bool engine_init(engine *engine) {
- engine->startTime = glfwGetTime();
- engine->deltaTime = 0.0f;
- engine->lastFrame = 0.0f;
-
- return true;
-}
-
-static inline void engine_tick_start(engine *engine) {
- float currentFrame = glfwGetTime();
- engine->deltaTime = currentFrame - engine->lastFrame;
- engine->lastFrame = currentFrame;
-}
-
-static inline void engine_tick_end(engine *engine) {
- // TODO: clear frame stats
-}
-
-void celeritas_print_type_sizes();
-```
-
-```c
-// engine.c
-#include "engine.h"
-
-#include <stdio.h>
-
-#include "animation.h"
-#include "application.h"
-
-void celeritas_print_type_sizes() {
- printf("\e[1mType sizes: \e[m \n");
- printf("transform: %ld bytes\n", sizeof(transform));
- printf("mesh: %ld bytes\n", sizeof(mesh));
- printf("bh_material: %ld bytes\n", sizeof(bh_material));
- printf("animation: %ld bytes\n", sizeof(animation_clip));
- printf("model: %ld bytes\n", sizeof(model));
- printf("rend_buffer: %ld bytes\n", sizeof(rend_buffer));
-
- printf("application: %ld bytes\n", sizeof(cel_application));
- printf("renderer: %ld bytes\n", sizeof(renderer));
- printf("threadpool: %ld bytes\n", sizeof(threadpool));
- printf("engine: %ld bytes\n", sizeof(engine));
-}
-``` \ No newline at end of file
diff --git a/scripts/api_gen/modules.md b/scripts/api_gen/modules.md
new file mode 100644
index 0000000..7298844
--- /dev/null
+++ b/scripts/api_gen/modules.md
@@ -0,0 +1,18 @@
+
+
+- core lifecycle
+- memory
+ - arena
+ - pool
+- containers
+ - darray
+ - hashtbl
+ - ring_queue
+- maths
+- physics
+- platform
+ - file
+ - path
+ - mutex
+ - thread
+- threadpool \ No newline at end of file
diff --git a/scripts/api_gen/template.html b/scripts/api_gen/template.html
new file mode 100644
index 0000000..1e1c7a7
--- /dev/null
+++ b/scripts/api_gen/template.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <script src="https://cdn.tailwindcss.com"></script>
+</head>
+<body class="flex p-4 bg-neutral-800 text-neutral-200">
+ <nav class="pr-8">
+ <h3 class="text-sm font-semibold">Modules</h3>
+ <br />
+ <ul class="flex flex-col">
+ <li class="text-neutral-400 text-sm"><span class="pr-1">1.1</span> core lifecycle</li>
+ <li class="text-neutral-400 text-sm"><span class="pr-1">1.2</span> threadpool</li>
+ <h5 class="text-xs text-neutral-300 tracking-wide pt-2">maths</h5>
+ <li class="text-neutral-400 text-sm"><span class="pr-1">1.3</span> vector</li>
+ <li class="text-neutral-400 text-sm"><span class="pr-1">1.4</span> quaternion</li>
+ <li class="text-neutral-400 text-sm"><span class="pr-1">1.5</span> matrix</li>
+ <!-- <li class="text-neutral-400 text-sm"><span class="pr-1">3.0</span> memory</li> -->
+ <h5 class="text-xs text-neutral-300 tracking-wide pt-2">memory</h5>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.6</span> arena</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.7</span> pool</li>
+ <!-- <li class="text-neutral-400 text-sm"><span class="pr-1">4.0</span> containers</li> -->
+ <h5 class="text-xs text-neutral-300 tracking-wide pt-2">containers</h5>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.8</span> darray</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.9</span> hashtbl</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.10</span> ring_queue</li>
+
+ <h5 class="text-xs text-neutral-300 tracking-wide pt-2">std</h5>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.11</span> str8</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.12</span> bytebuffer</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.13</span> utils</li>
+
+ <h5 class="text-xs text-neutral-300 tracking-wide pt-2">render</h5>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.14</span> ral</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.15</span> render</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.16</span> immdraw</li>
+
+ <h5 class="text-xs text-neutral-300 tracking-wide pt-2">platform</h5>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.17</span> file</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.18</span> path</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.19</span> mutex</li>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.20</span> thread</li>
+
+ <h5 class="text-xs text-neutral-300 tracking-wide pt-2"></h5>
+ <li class="text-neutral-400 text-sm "><span class="pr-1">1.21</span> scene</li>
+
+<!--
+- core lifecycle
+- memory
+ - arena
+ - pool
+- containers
+ - darray
+ - hashtbl
+ - ring_queue
+- maths
+- physics
+- platform
+ - file
+ - path
+ - mutex
+ - thread
+- threadpool
+ -->
+ </ul>
+ </nav>
+ <main class="px-8">
+ <h2 class="font-semibold text-amber-400">Celeritas Core API</h2>
+
+ <div class="my-8"></div>
+ <div class="bg-neutral-700 px-2 py-1 min-w-[600px]" style="font-family: monospace;">
+ <span class="text-[#ECBE7B]">arena</span>
+ <span class="text-[#80A0C2]">arena_create</span>(<span class="text-[#D2876D]">void*</span>
+ <span class="text-neutral-300">backing_buffer</span>,
+ <span class="text-[#D2876D]">size_t</span>
+ <span class="text-neutral-300">capacity</span>);
+ </div>
+ <div class="bg-neutral-700 px-2 py-1 min-w-[600px]" style="font-family: monospace;">
+ <span class="text-[#ECBE7B]">void*</span>
+ <span class="text-[#80A0C2]">arena_alloc</span>(<span class="text-[#D2876D]">arena*</span>
+ <span class="text-neutral-300">a</span>,
+ <span class="text-[#D2876D]">size_t</span>
+ <span class="text-neutral-300">size</span>);
+ </div>
+<!-- void* arena_alloc(arena* a, size_t size); -->
+ </main>
+
+
+</body>
+</html> \ No newline at end of file
diff --git a/src/animation.c b/src/animation.c
new file mode 100644
index 0000000..7a79529
--- /dev/null
+++ b/src/animation.c
@@ -0,0 +1,38 @@
+#include "animation.h"
+#include "log.h"
+#include "maths.h"
+
+keyframe animation_sample(animation_sampler *sampler, f32 t) {
+ size_t previous_index = 0;
+ f32 previous_time = 0.0;
+ // look forwards
+ // DEBUG("%d\n", sampler->animation.values.kind);
+ TRACE("Total timestamps %d", sampler->animation.n_timestamps);
+ for (u32 i = 0; i < sampler->animation.n_timestamps; i++) {
+ f32 current_time = sampler->animation.timestamps[i];
+ if (current_time > t) {
+ break;
+ }
+ previous_time = sampler->animation.timestamps[i];
+ previous_index = i;
+ }
+
+ size_t next_index = (previous_index + 1) % sampler->animation.n_timestamps;
+ f32 next_time = sampler->animation.timestamps[next_index];
+ printf("%d %f %d %f\n", previous_index, previous_time, next_index, next_time);
+
+ keyframe prev_value = sampler->animation.values.values[previous_index];
+ keyframe next_value = sampler->animation.values.values[next_index];
+
+ printf("%d %d\n", previous_index, next_index);
+
+ f32 time_diff =
+ sampler->animation.timestamps[next_index] - sampler->animation.timestamps[previous_index];
+ f32 percent = (t - previous_time) / time_diff;
+
+ quat interpolated_rot =
+ quat_slerp(sampler->animation.values.values[previous_index].rotation,
+ sampler->animation.values.values[next_index].rotation, percent);
+
+ return (keyframe){ .rotation = interpolated_rot };
+} \ No newline at end of file
diff --git a/src/animation.h b/src/animation.h
new file mode 100644
index 0000000..5462e65
--- /dev/null
+++ b/src/animation.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "darray.h"
+#include "defines.h"
+#include "maths_types.h"
+
+KITC_DECL_TYPED_ARRAY(f32)
+
+typedef enum interpolation { INTERPOLATION_LINEAR, INTERPOLATION_COUNT } interpolation;
+
+typedef enum keyframe_kind {
+ KEYFRAME_ROTATION,
+ KEYFRAME_TRANSLATION,
+ KEYFRAME_SCALE,
+ KEYFRAME_WEIGHTS,
+} keyframe_kind;
+
+typedef union keyframe {
+ quat rotation;
+ vec3 translation;
+ vec3 scale;
+ float* weights;
+} keyframe;
+
+typedef struct keyframes {
+ keyframe_kind kind;
+ keyframe* values;
+ size_t count;
+} keyframes;
+
+typedef struct joint {
+ char* name; // optional
+ transform transform_components;
+ mat4 inverse_bind_matrix;
+ mat4 local_transform;
+} joint;
+
+typedef struct animation_spline {
+ f32* timestamps;
+ size_t n_timestamps;
+ keyframes values;
+ interpolation interpolation;
+} animation_spline;
+
+typedef struct animation_sampler {
+ int current_index;
+ f32 min;
+ f32 max;
+ animation_spline animation;
+} animation_sampler;
+
+/** @brief Sample an animation at a given time `t` */
+keyframe animation_sample(animation_sampler* sampler, f32 t);
+
+typedef struct animation_clip {
+ // A clip contains one or more animation curves
+ // for now I think we can just enumerate all of the properties (assuming *only* one per type is in
+ // a clip) NULL = this property is not animated in this clip
+ animation_sampler* rotation;
+ animation_sampler* translation;
+ animation_sampler* scale;
+ animation_sampler* weights;
+} animation_clip;
+
+typedef struct skinned_animation {
+ mat4* joint_matrices;
+ size_t n_joints;
+} skinned_animation;
+
+// void animation_update_joint_matrices(animation_clip* )
+
+void animation_play(animation_clip* clip); \ No newline at end of file
diff --git a/src/camera.h b/src/camera.h
index f7bc6eb..ec867c5 100644
--- a/src/camera.h
+++ b/src/camera.h
@@ -24,4 +24,11 @@ camera camera_create(vec3 pos, vec3 front, vec3 up, f32 fov);
/** @brief get a 4x4 transform matrix for the view and perspective projection */
void camera_view_projection(camera *c, f32 screen_height, f32 screen_width, mat4 *out_view,
- mat4 *out_proj); \ No newline at end of file
+ mat4 *out_proj);
+
+// TODO: Basic reusable camera controls
+/*
+Right click + move = pan
+Left click = orbit camera
+WASD = forward/backward/left/right
+*/ \ No newline at end of file
diff --git a/src/colours.h b/src/colours.h
index bbd9476..a981c6c 100644
--- a/src/colours.h
+++ b/src/colours.h
@@ -12,6 +12,8 @@ typedef struct rgba {
#define COLOUR_SEA_GREEN ((rgba){ 0.18, 0.77, 0.71, 1.0 })
#define COLOUR_WHITE ((rgba){ 1.0, 1.0, 1.0, 1.0 })
+#define rgba_to_vec4(color) (vec4(color.r, color.g, color.b, color.a))
+
// Thanks ChatGPT
#define STONE_50 ((rgba){ 0.980, 0.980, 0.976, 1.0 })
#define STONE_100 ((rgba){ 0.961, 0.961, 0.957, 1.0 })
@@ -36,3 +38,51 @@ typedef struct rgba {
#define CYAN_800 ((rgba){ 0.082, 0.369, 0.459, 1.0 })
#define CYAN_900 ((rgba){ 0.086, 0.306, 0.388, 1.0 })
#define CYAN_950 ((rgba){ 0.033, 0.200, 0.263, 1.0 })
+
+#define GRAY_50 ((rgba){ 0.976, 0.980, 0.984, 1.0 })
+#define GRAY_100 ((rgba){ 0.953, 0.957, 0.965, 1.0 })
+#define GRAY_200 ((rgba){ 0.898, 0.906, 0.922, 1.0 })
+#define GRAY_300 ((rgba){ 0.820, 0.835, 0.859, 1.0 })
+#define GRAY_400 ((rgba){ 0.612, 0.639, 0.686, 1.0 })
+#define GRAY_500 ((rgba){ 0.420, 0.447, 0.502, 1.0 })
+#define GRAY_600 ((rgba){ 0.294, 0.333, 0.388, 1.0 })
+#define GRAY_700 ((rgba){ 0.216, 0.255, 0.318, 1.0 })
+#define GRAY_800 ((rgba){ 0.122, 0.161, 0.216, 1.0 })
+#define GRAY_900 ((rgba){ 0.067, 0.094, 0.153, 1.0 })
+#define GRAY_950 ((rgba){ 0.012, 0.027, 0.071, 1.0 })
+
+#define RED_50 ((rgba){ 0.996, 0.949, 0.949, 1.0 })
+#define RED_100 ((rgba){ 0.996, 0.886, 0.886, 1.0 })
+#define RED_200 ((rgba){ 0.996, 0.792, 0.792, 1.0 })
+#define RED_300 ((rgba){ 0.988, 0.647, 0.647, 1.0 })
+#define RED_400 ((rgba){ 0.973, 0.443, 0.443, 1.0 })
+#define RED_500 ((rgba){ 0.937, 0.267, 0.267, 1.0 })
+#define RED_600 ((rgba){ 0.863, 0.149, 0.149, 1.0 })
+#define RED_700 ((rgba){ 0.725, 0.110, 0.110, 1.0 })
+#define RED_800 ((rgba){ 0.600, 0.106, 0.106, 1.0 })
+#define RED_900 ((rgba){ 0.498, 0.114, 0.114, 1.0 })
+#define RED_950 ((rgba){ 0.271, 0.039, 0.039, 1.0 })
+
+#define ORANGE_50 ((rgba){ 1.000, 0.969, 0.929, 1.0 })
+#define ORANGE_100 ((rgba){ 1.000, 0.929, 0.835, 1.0 })
+#define ORANGE_200 ((rgba){ 0.996, 0.843, 0.667, 1.0 })
+#define ORANGE_300 ((rgba){ 0.992, 0.729, 0.455, 1.0 })
+#define ORANGE_400 ((rgba){ 0.984, 0.573, 0.235, 1.0 })
+#define ORANGE_500 ((rgba){ 0.976, 0.451, 0.086, 1.0 })
+#define ORANGE_600 ((rgba){ 0.918, 0.345, 0.047, 1.0 })
+#define ORANGE_700 ((rgba){ 0.761, 0.255, 0.047, 1.0 })
+#define ORANGE_800 ((rgba){ 0.604, 0.204, 0.071, 1.0 })
+#define ORANGE_900 ((rgba){ 0.486, 0.176, 0.071, 1.0 })
+#define ORANGE_950 ((rgba){ 0.263, 0.078, 0.027, 1.0 })
+
+#define AMBER_50 ((rgba){ 1.000, 0.984, 0.922, 1.0 })
+#define AMBER_100 ((rgba){ 0.996, 0.953, 0.780, 1.0 })
+#define AMBER_200 ((rgba){ 0.992, 0.902, 0.541, 1.0 })
+#define AMBER_300 ((rgba){ 0.988, 0.827, 0.302, 1.0 })
+#define AMBER_400 ((rgba){ 0.984, 0.749, 0.141, 1.0 })
+#define AMBER_500 ((rgba){ 0.961, 0.620, 0.043, 1.0 })
+#define AMBER_600 ((rgba){ 0.851, 0.467, 0.024, 1.0 })
+#define AMBER_700 ((rgba){ 0.706, 0.325, 0.035, 1.0 })
+#define AMBER_800 ((rgba){ 0.573, 0.251, 0.055, 1.0 })
+#define AMBER_900 ((rgba){ 0.471, 0.208, 0.059, 1.0 })
+#define AMBER_950 ((rgba){ 0.271, 0.102, 0.012, 1.0 })
diff --git a/src/core.c b/src/core.c
index 024b2d7..84c9cae 100644
--- a/src/core.c
+++ b/src/core.c
@@ -2,33 +2,40 @@
#include <stdlib.h>
+#include "glfw3.h"
+#include "input.h"
+#include "keys.h"
#include "log.h"
#include "render.h"
#include "render_types.h"
-#include "threadpool.h"
+#include "scene.h"
+// #include "threadpool.h"
-#define SCR_WIDTH 1080
-#define SCR_HEIGHT 800
+#define SCR_WIDTH 1000
+#define SCR_HEIGHT 1000
-core* core_bringup() {
+core g_core; /** @brief global `core` that other files can use */
+
+inline core* get_global_core() { return &g_core; }
+
+void core_bringup() {
INFO("Initiate Core bringup");
- core* c = malloc(sizeof(core));
renderer_config conf = { .window_name = { "Celeritas Engine Core" },
.scr_width = SCR_WIDTH,
.scr_height = SCR_HEIGHT,
.clear_colour = (vec3){ .08, .08, .1 } };
- c->renderer.config = conf;
- c->renderer.backend_state = NULL;
+ g_core.renderer.config = conf;
+ g_core.renderer.backend_context = NULL;
- threadpool_create(&c->threadpool, 6, 256);
- threadpool_set_ctx(&c->threadpool, c); // Gives the threadpool access to the core
+ // threadpool_create(&c->threadpool, 6, 256);
+ // threadpool_set_ctx(&c->threadpool, c); // Gives the threadpool access to the core
// initialise all subsystems
- if (!renderer_init(&c->renderer)) {
+ if (!renderer_init(&g_core.renderer)) {
// FATAL("Failed to start renderer");
ERROR_EXIT("Failed to start renderer\n");
}
- if (!input_system_init(&c->input, c->renderer.window)) {
+ if (!input_system_init(&g_core.input, g_core.renderer.window)) {
// the input system needs the glfw window which is created by the renderer
// hence the order here is important
FATAL("Failed to start input system");
@@ -45,7 +52,30 @@ core* core_bringup() {
}
*/
- c->models = model_darray_new(10);
+ INFO("Creating default scene");
+ scene_init(&g_core.default_scene);
+}
+
+#include <glfw3.h>
+
+/* bool should_window_close(core* core) { glfwWindowShouldClose(core->renderer.window); } */
+void core_input_update() { input_update(&g_core.input); }
+void core_frame_begin(core* core) { render_frame_begin(&core->renderer); }
+void core_frame_end(core* core) { render_frame_end(&core->renderer); }
+
+void core_shutdown() {
+ // threadpool_destroy(&core->threadpool);
+ input_system_shutdown(&g_core.input);
+ renderer_shutdown(&g_core.renderer);
+}
+
+bool should_exit() {
+ return key_just_released(KEYCODE_ESCAPE) || glfwWindowShouldClose(g_core.renderer.window);
+}
- return c;
+void frame_begin() {
+ glfwPollEvents();
+ render_frame_begin(&g_core.renderer);
}
+void frame_draw() {}
+void frame_end() { render_frame_end(&g_core.renderer); }
diff --git a/src/core.h b/src/core.h
index 1b3e28b..1031868 100644
--- a/src/core.h
+++ b/src/core.h
@@ -1,23 +1,38 @@
#pragma once
-#include "defines.h"
#include "input.h"
-#include "render_types.h"
+#include "scene.h"
#include "screenspace.h"
+#include "terrain.h"
#include "text.h"
-#include "threadpool.h"
+// #include "threadpool.h"
typedef struct core {
+ const char* app_name;
+ // foundations
renderer renderer;
- threadpool threadpool;
+ // threadpool threadpool;
+ // systems
input_state input;
text_system_state text;
+ terrain_state terrain;
screenspace_state screenspace;
+ // data storage
+ scene default_scene;
model_darray* models;
} core;
+core* get_global_core();
+
// --- Lifecycle
-core* core_bringup();
-void core_shutdown(core* core);
-void core_input_update(core* core);
+/** @brief Throws error if the core cannot be instantiated */
+void core_bringup();
+void core_shutdown();
+bool should_exit();
+
+void frame_begin();
+void frame_draw();
+void frame_end();
+
+void core_input_update();
diff --git a/src/defines.h b/src/defines.h
index 52aa7b0..4b6f8c7 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -1,6 +1,6 @@
/**
* @file defines.h
- * @brief
+ * @brief Typedefs for common integer/floating point types and very basic macros
* @date 2024-02-24
* @copyright Copyright (c) 2024
*/
@@ -65,12 +65,17 @@ Renderer backend defines:
*/
// Platform will inform renderer backend (unless user overrides)
-#if defined(CEL_PLATFORM_LINUX) || defined(CEL_PLATFORM_WINDOWS)
-#define CEL_REND_BACKEND_OPENGL 1
-// #define CEL_REND_BACKEND_VULKAN 1
+#if defined(CEL_PLATFORM_LINUX)
+// #define CEL_REND_BACKEND_OPENGL 1
+#define CEL_REND_BACKEND_VULKAN 1
+#endif
+
+#if defined(CEL_PLATFORM_WINDOWS)
+// #define CEL_REND_BACKEND_DX11 1
+#define CEL_REND_BACKEND_VULKAN 1
#endif
#if defined(CEL_PLATFORM_MAC)
-#define CEL_REND_BACKEND_METAL 1
-// #define CEL_REND_BACKEND_OPENGL 1
+// #define CEL_REND_BACKEND_METAL 1
+#define CEL_REND_BACKEND_OPENGL 1
#endif \ No newline at end of file
diff --git a/src/log.h b/src/log.h
index b0c355b..d954684 100644
--- a/src/log.h
+++ b/src/log.h
@@ -38,19 +38,19 @@ void logger_shutdown();
void log_output(log_level level, const char* message, ...);
-#define FATAL(message, ...) log_output(LOG_LEVEL_FATAL, message, ##__VA_ARGS__);
-#define ERROR(message, ...) log_output(LOG_LEVEL_ERROR, message, ##__VA_ARGS__);
-#define WARN(message, ...) log_output(LOG_LEVEL_WARN, message, ##__VA_ARGS__);
-#define INFO(message, ...) log_output(LOG_LEVEL_INFO, message, ##__VA_ARGS__);
+#define FATAL(message, ...) log_output(LOG_LEVEL_FATAL, message, ##__VA_ARGS__)
+#define ERROR(message, ...) log_output(LOG_LEVEL_ERROR, message, ##__VA_ARGS__)
+#define WARN(message, ...) log_output(LOG_LEVEL_WARN, message, ##__VA_ARGS__)
+#define INFO(message, ...) log_output(LOG_LEVEL_INFO, message, ##__VA_ARGS__)
#if LOG_DEBUG_ENABLED == 1
-#define DEBUG(message, ...) log_output(LOG_LEVEL_DEBUG, message, ##__VA_ARGS__);
+#define DEBUG(message, ...) log_output(LOG_LEVEL_DEBUG, message, ##__VA_ARGS__)
#else
#define DEBUG(message, ...)
#endif
#if LOG_TRACE_ENABLED == 1
-#define TRACE(message, ...) log_output(LOG_LEVEL_TRACE, message, ##__VA_ARGS__);
+#define TRACE(message, ...) log_output(LOG_LEVEL_TRACE, message, ##__VA_ARGS__)
#else
#define TRACE(message, ...)
#endif \ No newline at end of file
diff --git a/src/logos/README.md b/src/logos/README.md
new file mode 100644
index 0000000..25b7bef
--- /dev/null
+++ b/src/logos/README.md
@@ -0,0 +1,6 @@
+# Logos
+
+Logos is the namespace for threadpool & job system code. This is not a 'system' as it is underlying core unit of the engine.
+
+Threadpool currently gets initialised at core bringup with a set number of threads and results are processed once per frame
+on the main thread. This is subject to change but multithreading is not the highest priority right now. \ No newline at end of file
diff --git a/src/logos/jobs.h b/src/logos/jobs.h
new file mode 100644
index 0000000..ef4eed7
--- /dev/null
+++ b/src/logos/jobs.h
@@ -0,0 +1,3 @@
+/**
+ * Common jobs that get run
+ */ \ No newline at end of file
diff --git a/src/maths/maths.h b/src/maths/maths.h
index 3301634..217f2e0 100644
--- a/src/maths/maths.h
+++ b/src/maths/maths.h
@@ -9,6 +9,7 @@
#pragma once
#include <math.h>
+#include <stdio.h>
#include "maths_types.h"
// --- Helpers
@@ -20,7 +21,7 @@
// Dimension 3
static inline vec3 vec3_create(f32 x, f32 y, f32 z) { return (vec3){ x, y, z }; }
-#define vec3(x, y, z) (vec3_create(x, y, z))
+#define vec3(x, y, z) ((vec3){ x, y, z })
static inline vec3 vec3_add(vec3 a, vec3 b) { return (vec3){ a.x + b.x, a.y + b.y, a.z + b.z }; }
static inline vec3 vec3_sub(vec3 a, vec3 b) { return (vec3){ a.x - b.x, a.y - b.y, a.z - b.z }; }
static inline vec3 vec3_mult(vec3 a, f32 s) { return (vec3){ a.x * s, a.y * s, a.z * s }; }
@@ -48,10 +49,16 @@ static inline vec3 vec3_cross(vec3 a, vec3 b) {
#define VEC3_Z ((vec3){ .x = 0.0, .y = 0.0, .z = 1.0 })
#define VEC3_NEG_Z ((vec3){ .x = 0.0, .y = 0.0, .z = -1.0 })
+static inline void print_vec3(vec3 v) { printf("{ x: %f, y: %f, z: %f )\n", v.x, v.y, v.z); }
+
// TODO: Dimension 2
static inline vec2 vec2_create(f32 x, f32 y) { return (vec2){ x, y }; }
+#define vec2(x, y) ((vec2){ x, y })
+static inline vec2 vec2_div(vec2 a, f32 s) { return (vec2){ a.x / s, a.y / s }; }
// TODO: Dimension 4
+static inline vec4 vec4_create(f32 x, f32 y, f32 z, f32 w) { return (vec4){ x, y, z, w }; }
+#define vec4(x, y, z, w) (vec4_create(x, y, z, w))
#define VEC4_ZERO ((vec4){ .x = 0.0, .y = 0.0, .z = 0.0, .w = 0.0 })
// --- Quaternion Implementations
@@ -78,6 +85,52 @@ static quat quat_from_axis_angle(vec3 axis, f32 angle, bool normalize) {
return q;
}
+// TODO: grok this.
+static inline quat quat_slerp(quat a, quat b, f32 percentage) {
+ quat out_quaternion;
+
+ quat q0 = quat_normalise(a);
+ quat q1 = quat_normalise(b);
+
+ // Compute the cosine of the angle between the two vectors.
+ f32 dot = quat_dot(q0, q1);
+
+ // If the dot product is negative, slerp won't take
+ // the shorter path. Note that v1 and -v1 are equivalent when
+ // the negation is applied to all four components. Fix by
+ // reversing one quaternion.
+ if (dot < 0.0f) {
+ q1.x = -q1.x;
+ q1.y = -q1.y;
+ q1.z = -q1.z;
+ q1.w = -q1.w;
+ dot = -dot;
+ }
+
+ const f32 DOT_THRESHOLD = 0.9995f;
+ if (dot > DOT_THRESHOLD) {
+ // If the inputs are too close for comfort, linearly interpolate
+ // and normalize the result.
+ out_quaternion =
+ (quat){ q0.x + ((q1.x - q0.x) * percentage), q0.y + ((q1.y - q0.y) * percentage),
+ q0.z + ((q1.z - q0.z) * percentage), q0.w + ((q1.w - q0.w) * percentage) };
+
+ return quat_normalise(out_quaternion);
+ }
+
+ // Since dot is in range [0, DOT_THRESHOLD], acos is safe
+ f32 theta_0 = cos(dot); // theta_0 = angle between input vectors
+ f32 theta = theta_0 * percentage; // theta = angle between v0 and result
+ f32 sin_theta = sin(theta); // compute this value only once
+ f32 sin_theta_0 = sin(theta_0); // compute this value only once
+
+ f32 s0 = cos(theta) - dot * sin_theta / sin_theta_0; // == sin(theta_0 - theta) / sin(theta_0)
+ f32 s1 = sin_theta / sin_theta_0;
+
+ return (quat){ (q0.x * s0) + (q1.x * s1), (q0.y * s0) + (q1.y * s1), (q0.z * s0) + (q1.z * s1),
+ (q0.w * s0) + (q1.w * s1) };
+}
+
// --- Matrix Implementations
static inline mat4 mat4_ident() {
@@ -139,6 +192,43 @@ static inline mat4 mat4_mult(mat4 lhs, mat4 rhs) {
return out_matrix;
}
+static mat4 mat4_transposed(mat4 matrix) {
+ mat4 out_matrix = mat4_ident();
+ out_matrix.data[0] = matrix.data[0];
+ out_matrix.data[1] = matrix.data[4];
+ out_matrix.data[2] = matrix.data[8];
+ out_matrix.data[3] = matrix.data[12];
+ out_matrix.data[4] = matrix.data[1];
+ out_matrix.data[5] = matrix.data[5];
+ out_matrix.data[6] = matrix.data[9];
+ out_matrix.data[7] = matrix.data[13];
+ out_matrix.data[8] = matrix.data[2];
+ out_matrix.data[9] = matrix.data[6];
+ out_matrix.data[10] = matrix.data[10];
+ out_matrix.data[11] = matrix.data[14];
+ out_matrix.data[12] = matrix.data[3];
+ out_matrix.data[13] = matrix.data[7];
+ out_matrix.data[14] = matrix.data[11];
+ out_matrix.data[15] = matrix.data[15];
+ return out_matrix;
+}
+
+#if defined(CEL_REND_BACKEND_VULKAN)
+/** @brief Creates a perspective projection matrix compatible with Vulkan */
+static inline mat4 mat4_perspective(f32 fov_radians, f32 aspect_ratio, f32 near_clip,
+ f32 far_clip) {
+ f32 half_tan_fov = tanf(fov_radians * 0.5f);
+ mat4 out_matrix = { .data = { 0 } };
+
+ out_matrix.data[0] = 1.0f / (aspect_ratio * half_tan_fov);
+ out_matrix.data[5] = -1.0f / half_tan_fov; // Flip Y-axis for Vulkan
+ out_matrix.data[10] = -((far_clip + near_clip) / (far_clip - near_clip));
+ out_matrix.data[11] = -1.0f;
+ out_matrix.data[14] = -((2.0f * far_clip * near_clip) / (far_clip - near_clip));
+
+ return out_matrix;
+}
+#else
/** @brief Creates a perspective projection matrix */
static inline mat4 mat4_perspective(f32 fov_radians, f32 aspect_ratio, f32 near_clip,
f32 far_clip) {
@@ -151,6 +241,7 @@ static inline mat4 mat4_perspective(f32 fov_radians, f32 aspect_ratio, f32 near_
out_matrix.data[14] = -((2.0f * far_clip * near_clip) / (far_clip - near_clip));
return out_matrix;
}
+#endif
/** @brief Creates an orthographic projection matrix */
static inline mat4 mat4_orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 near_clip,
@@ -210,7 +301,7 @@ static inline mat4 mat4_look_at(vec3 position, vec3 target, vec3 up) {
#define TRANSFORM_DEFAULT \
((transform){ .position = VEC3_ZERO, \
- .rotation = (quat){ .x = 0., .y = 0., .z = 0., .w = 0. }, \
+ .rotation = (quat){ .x = 0., .y = 0., .z = 0., .w = 1. }, \
.scale = 1.0, \
.is_dirty = false })
@@ -219,10 +310,10 @@ static transform transform_create(vec3 pos, quat rot, f32 scale) {
}
static inline mat4 transform_to_mat(transform *tf) {
- mat4 trans = mat4_translation(tf->position);
- mat4 rot = mat4_rotation(tf->rotation);
mat4 scale = mat4_scale(tf->scale);
- return mat4_mult(trans, mat4_mult(rot, scale));
+ mat4 rotation = mat4_rotation(tf->rotation);
+ mat4 translation = mat4_translation(tf->position);
+ return mat4_mult(translation, mat4_mult(rotation, scale));
}
// --- Sizing asserts
@@ -231,20 +322,3 @@ _Static_assert(alignof(vec3) == 4, "vec3 is 4 byte aligned");
_Static_assert(sizeof(vec3) == 12, "vec3 is 12 bytes so has no padding");
_Static_assert(alignof(vec4) == 4, "vec4 is 4 byte aligned");
-
-// --- Some other types
-typedef struct u32x3 {
- union {
- struct {
- u32 x;
- u32 y;
- u32 z;
- };
- struct {
- u32 r;
- u32 g;
- u32 b;
- };
- };
-} u32x3;
-#define u32x3(x, y, z) ((u32x3){ x, y, z })
diff --git a/src/maths/maths_types.h b/src/maths/maths_types.h
index ba741b9..5ef09db 100644
--- a/src/maths/maths_types.h
+++ b/src/maths/maths_types.h
@@ -60,4 +60,44 @@ typedef struct transform {
quat rotation;
f32 scale;
bool is_dirty;
-} transform; \ No newline at end of file
+} transform;
+typedef transform transform3d;
+
+typedef struct vec4i {
+ i32 x, y, z, w;
+} vec4i;
+
+typedef struct vec4u {
+ u32 x, y, z, w;
+} vec4u;
+
+// --- Some other types
+typedef struct u32x3 {
+ union {
+ struct {
+ u32 x;
+ u32 y;
+ u32 z;
+ };
+ struct {
+ u32 r;
+ u32 g;
+ u32 b;
+ };
+ };
+} u32x3;
+#define u32x3(x, y, z) ((u32x3){ x, y, z })
+
+typedef struct u32x2 {
+ u32 x;
+ u32 y;
+} u32x2;
+#define u32x2(x, y) ((u32x3){ x, y })
+
+// Type aliass
+
+typedef struct vec2 f32x2;
+#define f32x2(x, y) ((f32x2){ x, y })
+
+typedef struct vec3 f32x3;
+#define f32x3(x, y, z) ((f32x3){ x, y, z })
diff --git a/src/maths/primitives.c b/src/maths/primitives.c
new file mode 100644
index 0000000..b9ec868
--- /dev/null
+++ b/src/maths/primitives.c
@@ -0,0 +1,138 @@
+#include "primitives.h"
+#include "colours.h"
+#include "maths.h"
+#include "ral_types.h"
+#include "render_types.h"
+
+// TODO: move to another file
+void geo_free_data(geometry_data* geo) {
+ vertex_darray_free(geo->vertices);
+ geo->vertices = NULL;
+ // TODO: do indices as well
+ /* if (geo->has_indices) { */
+ /* u32_darray_free(&geo->indices); */
+ /* } */
+}
+
+// vertices
+f32 plane_vertex_positions[] = {
+ // triangle 1
+ -0.5, 0, -0.5, -0.5, 0, 0.5, 0.5, 0, -0.5,
+ // triangle 2
+ 0.5, 0, -0.5, -0.5, 0, 0.5, 0.5, 0, 0.5
+};
+
+geometry_data geo_create_plane(f32x2 extents) {
+ f32x2 half_extents = vec2_div(extents, 2.0);
+ vertex_format format = VERTEX_STATIC_3D;
+ vertex_darray* vertices = vertex_darray_new(4);
+
+ // vertex_darray_push(vertices, (vertex){ .static_3d = { .position = } });
+
+ // return (geometry_data) { .format = format, .vertices =.has_indices = true, }
+}
+
+// OLD
+
+static const vec3 BACK_BOT_LEFT = (vec3){ 0, 0, 0 };
+static const vec3 BACK_BOT_RIGHT = (vec3){ 1, 0, 0 };
+static const vec3 BACK_TOP_LEFT = (vec3){ 0, 1, 0 };
+static const vec3 BACK_TOP_RIGHT = (vec3){ 1, 1, 0 };
+static const vec3 FRONT_BOT_LEFT = (vec3){ 0, 0, 1 };
+static const vec3 FRONT_BOT_RIGHT = (vec3){ 1, 0, 1 };
+static const vec3 FRONT_TOP_LEFT = (vec3){ 0, 1, 1 };
+static const vec3 FRONT_TOP_RIGHT = (vec3){ 1, 1, 1 };
+
+#define VERT_3D(arr, pos, norm, uv) \
+ { \
+ vertex v = { .static_3d = { .position = pos, .normal = norm, .tex_coords = uv } }; \
+ vertex_darray_push(arr, v); \
+ }
+
+geometry_data geo_create_cuboid(f32x3 extents) {
+ /* static mesh prim_cube_mesh_create() { */
+ vertex_darray* vertices = vertex_darray_new(36);
+
+ // back faces
+ VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_NEG_Z, vec2(1, 0));
+ VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_Z, vec2(0, 1));
+ VERT_3D(vertices, BACK_TOP_LEFT, VEC3_NEG_Z, vec2(0, 0));
+ VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_NEG_Z, vec2(1, 0));
+ VERT_3D(vertices, BACK_BOT_RIGHT, VEC3_NEG_Z, vec2(1, 1));
+ VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_Z, vec2(0, 1));
+
+ // front faces
+ VERT_3D(vertices, FRONT_BOT_LEFT, VEC3_Z, vec2(0, 1));
+ VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_Z, vec2(1, 0));
+ VERT_3D(vertices, FRONT_TOP_LEFT, VEC3_Z, vec2(0, 0));
+ VERT_3D(vertices, FRONT_BOT_LEFT, VEC3_Z, vec2(0, 1));
+ VERT_3D(vertices, FRONT_BOT_RIGHT, VEC3_Z, vec2(1, 1));
+ VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_Z, vec2(1, 0));
+
+ // top faces
+ VERT_3D(vertices, BACK_TOP_LEFT, VEC3_Y, vec2(0, 0));
+ VERT_3D(vertices, FRONT_TOP_LEFT, VEC3_Y, vec2(0, 1));
+ VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_Y, vec2(1, 1));
+ VERT_3D(vertices, BACK_TOP_LEFT, VEC3_Y, vec2(0, 0));
+ VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_Y, vec2(1, 1));
+ VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_Y, vec2(1, 0));
+
+ // bottom faces
+ VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_Y, vec2(0, 1));
+ VERT_3D(vertices, FRONT_BOT_RIGHT, VEC3_NEG_Y, vec2(1, 1));
+ VERT_3D(vertices, FRONT_BOT_LEFT, VEC3_NEG_Y, vec2(0, 1));
+ VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_Y, vec2(0, 1));
+ VERT_3D(vertices, BACK_BOT_RIGHT, VEC3_NEG_Y, vec2(1, 1));
+ VERT_3D(vertices, FRONT_BOT_RIGHT, VEC3_NEG_Y, vec2(0, 1));
+
+ // right faces
+ VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_X, vec2(0, 0));
+ VERT_3D(vertices, BACK_BOT_RIGHT, VEC3_X, vec2(1, 1));
+ VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_X, vec2(1, 0));
+ VERT_3D(vertices, BACK_BOT_RIGHT, VEC3_X, vec2(1, 1));
+ VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_X, vec2(0, 0));
+ VERT_3D(vertices, FRONT_BOT_RIGHT, VEC3_X, vec2(0, 1));
+
+ // left faces
+ VERT_3D(vertices, FRONT_TOP_LEFT, VEC3_NEG_X, vec2(0, 0));
+ VERT_3D(vertices, BACK_TOP_LEFT, VEC3_NEG_X, vec2(0, 0));
+ VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_X, vec2(0, 0));
+ VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_X, vec2(0, 0));
+ VERT_3D(vertices, FRONT_BOT_LEFT, VEC3_NEG_X, vec2(0, 0));
+ VERT_3D(vertices, FRONT_TOP_LEFT, VEC3_NEG_X, vec2(0, 0));
+
+ u32_darray* indices = u32_darray_new(vertices->len);
+
+ for (u32 i = 0; i < vertices->len; i++) {
+ u32_darray_push(indices, i);
+ }
+
+ geometry_data geo = {
+ .format = VERTEX_STATIC_3D,
+ .vertices = vertices,
+ .has_indices = true,
+ .indices = *indices, // FIXME: make darray methods that return stack allocated struct
+ .colour = vec3(0, 0, 0),
+ };
+
+ return geo;
+}
+
+/* /\** @brief create a new model with the shape of a cube *\/ */
+/* static model_handle prim_cube_new(core* core) { */
+/* model model = { 0 }; */
+/* mesh cube = prim_cube_mesh_create(); */
+
+/* mesh_darray_push(model.meshes, cube); */
+/* assert(mesh_darray_len(model.meshes) == 1); */
+
+/* u32 index = (u32)model_darray_len(core->models); */
+/* model_darray_push_copy(core->models, &model); */
+/* return (model_handle){ .raw = index }; */
+/* } */
+
+// --- Spheres
+
+geometry_data geo_create_uvsphere(f32 radius, f32 north_south_lines, f32 east_west_lines) {
+ // TODO
+}
diff --git a/src/maths/primitives.h b/src/maths/primitives.h
new file mode 100644
index 0000000..3e0cc5f
--- /dev/null
+++ b/src/maths/primitives.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <assert.h>
+#include <stdlib.h>
+#include "core.h"
+#include "maths_types.h"
+#include "render_types.h"
+
+geometry_data geo_create_plane(f32x2 extents);
+geometry_data geo_create_cuboid(f32x3 extents);
+geometry_data geo_create_cylinder(f32 radius, f32 height, u32 resolution);
+geometry_data geo_create_uvsphere(f32 radius, f32 north_south_lines, f32 east_west_lines);
+geometry_data geo_create_icosphere(f32 radius, f32 n_subdivisions); \ No newline at end of file
diff --git a/src/physics/broadphase.h b/src/physics/broadphase.h
new file mode 100644
index 0000000..8b49716
--- /dev/null
+++ b/src/physics/broadphase.h
@@ -0,0 +1,10 @@
+/**
+ * @file broadphase.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-05-12
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */ \ No newline at end of file
diff --git a/src/physics/collision.h b/src/physics/collision.h
new file mode 100644
index 0000000..cca6042
--- /dev/null
+++ b/src/physics/collision.h
@@ -0,0 +1,30 @@
+/**
+ * @file collision.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-05-12
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+#include "geometry.h"
+
+enum collider_type {
+ cuboid_collider,
+ sphere_collider,
+};
+
+/** @brief generic collider structure */
+typedef struct physics_collider {
+ u64 id; // ? Replace with handle?
+ enum collider_type shape;
+ union collider_data {
+ cuboid cuboid;
+ sphere sphere;
+ } geometry;
+ transform transform;
+ u8 layer;
+ bool on_ground;
+} physics_collider; \ No newline at end of file
diff --git a/src/physics/narrowphase.h b/src/physics/narrowphase.h
new file mode 100644
index 0000000..2368c49
--- /dev/null
+++ b/src/physics/narrowphase.h
@@ -0,0 +1,10 @@
+/**
+ * @file narrowphase.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-05-12
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */ \ No newline at end of file
diff --git a/src/physics/physics.c b/src/physics/physics.c
new file mode 100644
index 0000000..299c0c1
--- /dev/null
+++ b/src/physics/physics.c
@@ -0,0 +1 @@
+#include "physics.h" \ No newline at end of file
diff --git a/src/physics/physics.h b/src/physics/physics.h
new file mode 100644
index 0000000..e0e3b89
--- /dev/null
+++ b/src/physics/physics.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "geometry.h"
+#include "maths_types.h"
+
+// 'system' means that it gets called per frame
+
+typedef struct physics_settings {
+ f32 gravity_strength;
+} physics_settings;
+
+// What else do I need?
+// intersection methods
+
+typedef struct physics_world {
+ physics_settings settings;
+} physics_world;
+
+physics_world physics_init(physics_settings settings);
+void physics_shutdown(physics_world* phys_world);
+
+/** @brief perform one or more simulation steps */
+void physics_system_update(physics_world* phys_world, f64 deltatime); \ No newline at end of file
diff --git a/src/platform/file.c b/src/platform/file.c
index ec9259a..6030620 100644
--- a/src/platform/file.c
+++ b/src/platform/file.c
@@ -61,3 +61,33 @@ str8_opt str8_from_file(arena *a, str8 path) {
return result;
}
+
+FileData load_spv_file(const char *path) {
+ FILE *f = fopen(path, "rb");
+ if (f == NULL) {
+ perror("Error opening file");
+ return (FileData){ NULL, 0 };
+ }
+
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ rewind(f);
+
+ char *data = (char *)malloc(fsize);
+ if (data == NULL) {
+ perror("Memory allocation failed");
+ fclose(f);
+ return (FileData){ NULL, 0 };
+ }
+
+ size_t bytesRead = fread(data, 1, fsize, f);
+ if (bytesRead < fsize) {
+ perror("Failed to read the entire file");
+ free(data);
+ fclose(f);
+ return (FileData){ NULL, 0 };
+ }
+
+ fclose(f);
+ return (FileData){ data, bytesRead };
+} \ No newline at end of file
diff --git a/src/platform/file.h b/src/platform/file.h
index 8bb22c8..a8aa8ea 100644
--- a/src/platform/file.h
+++ b/src/platform/file.h
@@ -16,4 +16,11 @@ typedef struct str8_opt {
const char* string_from_file(const char* path);
-str8_opt str8_from_file(arena* a, str8 path); \ No newline at end of file
+str8_opt str8_from_file(arena* a, str8 path);
+
+typedef struct {
+ char* data;
+ size_t size;
+} FileData;
+
+FileData load_spv_file(const char* path); \ No newline at end of file
diff --git a/src/platform/mutex.c b/src/platform/mutex.c
new file mode 100644
index 0000000..2aeb825
--- /dev/null
+++ b/src/platform/mutex.c
@@ -0,0 +1,9 @@
+#include "mutex.h"
+
+#if defined(CEL_PLATFORM_LINUX) || defined(CEL_PLATFORM_MAC)
+// TODO: implement in terms of pthreads
+#endif
+
+#if defined(CEL_PLATFORM_WINDOWS)
+// TODO: implement using win32 api
+#endif \ No newline at end of file
diff --git a/src/platform/mutex.h b/src/platform/mutex.h
new file mode 100644
index 0000000..a0a4208
--- /dev/null
+++ b/src/platform/mutex.h
@@ -0,0 +1,28 @@
+/**
+ * @file mutex.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#include <stdbool.h>
+
+typedef struct cel_mutex cel_mutex;
+
+cel_mutex mutex_create();
+
+void mutex_destroy(cel_mutex* mutex);
+
+/** @brief Blocks until the mutex can be acquired. if returns false then an error occurred and can
+ * be checked (TODO) */
+bool mutex_lock(cel_mutex* mutex);
+
+/** @brief Tries to acquire the mutex like `mutex_lock` but returns immediately if the mutex has
+ * already been locked */
+bool mutex_try_lock(cel_mutex* mutex);
+
+/** @brief Releases a mutex. If it is already unlocked then does nothing */
+void mutex_unlock(cel_mutex* mutex); \ No newline at end of file
diff --git a/src/platform/path.c b/src/platform/path.c
index 9572941..e49aa3a 100644
--- a/src/platform/path.c
+++ b/src/platform/path.c
@@ -1,12 +1,12 @@
#include "path.h"
-#include <libgen.h>
#include <stdlib.h>
#include <string.h>
#include "mem.h"
#include "str.h"
#if defined(CEL_PLATFORM_LINUX) || defined(CEL_PLATFORM_MAC)
+#include <libgen.h>
path_opt path_parent(arena* a, const char* path) {
// Duplicate the string because dirname doesnt like const literals
char* path_copy = arena_alloc(a, strlen(path) + 1);
@@ -16,5 +16,5 @@ path_opt path_parent(arena* a, const char* path) {
}
#endif
#ifdef CEL_PLATFORM_WINDOWS
-// TODO: path_opt path_parent(const char* path)
-#endif \ No newline at end of file
+path_opt path_parent(arena* a, const char* path) {}
+#endif
diff --git a/src/platform/thread.c b/src/platform/thread.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/platform/thread.c
diff --git a/src/platform/thread.h b/src/platform/thread.h
new file mode 100644
index 0000000..af07d3f
--- /dev/null
+++ b/src/platform/thread.h
@@ -0,0 +1,17 @@
+/**
+ * @file thread.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+
+typedef struct cel_thread cel_thread;
+
+cel_thread thread_create();
+void thread_destroy(cel_thread* thread);
+
+// join \ No newline at end of file
diff --git a/src/renderer/archive/old_backend_vulkan.c b/src/renderer/archive/old_backend_vulkan.c
new file mode 100644
index 0000000..a18ca70
--- /dev/null
+++ b/src/renderer/archive/old_backend_vulkan.c
@@ -0,0 +1,1990 @@
+#include "camera.h"
+#include "primitives.h"
+#define CDEBUG
+#define CEL_REND_BACKEND_VULKAN 1
+#if CEL_REND_BACKEND_VULKAN
+// ^ Temporary
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <vk_platform.h>
+#include <vulkan.h>
+#include <vulkan_core.h>
+#include "colours.h"
+#include "str.h"
+
+#include "darray.h"
+#include "defines.h"
+#include "file.h"
+#include "log.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "render_backend.h"
+#include "render_types.h"
+// #include "vulkan_helpers.h"
+
+#include <stdlib.h>
+
+#define SCR_WIDTH 1000
+#define SCR_HEIGHT 1000
+
+#include <glad/glad.h>
+
+#include <glfw3.h>
+
+KITC_DECL_TYPED_ARRAY(VkLayerProperties)
+
+typedef struct vulkan_device {
+ VkPhysicalDevice physical_device;
+ VkDevice logical_device;
+ vulkan_swapchain_support_info swapchain_support;
+ i32 graphics_queue_index;
+ i32 present_queue_index;
+ i32 compute_queue_index;
+ i32 transfer_queue_index;
+ VkQueue graphics_queue;
+ VkQueue present_queue;
+ VkQueue compute_queue;
+ VkQueue transfer_queue;
+ VkCommandPool gfx_command_pool;
+ VkPhysicalDeviceProperties properties;
+ VkPhysicalDeviceFeatures features;
+ VkPhysicalDeviceMemoryProperties memory;
+ VkFormat depth_format;
+} vulkan_device;
+
+typedef struct vulkan_image {
+ VkImage handle;
+ VkDeviceMemory memory;
+ VkImageView view;
+ u32 width;
+ u32 height;
+} vulkan_image;
+
+typedef struct vulkan_texture_data {
+ vulkan_image image;
+ VkSampler sampler;
+} vulkan_texture_data;
+
+typedef enum vulkan_renderpass_state {
+ READY,
+ RECORDING,
+ IN_RENDER_PASS,
+ RECORDING_ENDING,
+ SUBMITTED,
+ NOT_ALLOCATED
+} vulkan_renderpass_state;
+
+typedef struct vulkan_renderpass {
+ VkRenderPass handle;
+ vec4 render_area;
+ vec4 clear_colour;
+ f32 depth;
+ u32 stencil;
+ vulkan_renderpass_state state;
+} vulkan_renderpass;
+
+typedef struct vulkan_framebuffer {
+ VkFramebuffer handle;
+ u32 attachment_count;
+ VkImageView* attachments;
+ vulkan_renderpass* renderpass;
+} vulkan_framebuffer;
+
+KITC_DECL_TYPED_ARRAY(vulkan_framebuffer)
+
+typedef struct vulkan_swapchain {
+ VkSurfaceFormatKHR image_format;
+ u8 max_frames_in_flight;
+ VkSwapchainKHR handle;
+ u32 image_count;
+ VkImage* images;
+ VkImageView* views;
+ vulkan_image depth_attachment;
+ vulkan_framebuffer_darray* framebuffers;
+} vulkan_swapchain;
+
+// overengineered
+typedef enum vulkan_command_buffer_state {
+ COMMAND_BUFFER_STATE_READY,
+ COMMAND_BUFFER_STATE_IN_RENDER_PASS,
+ COMMAND_BUFFER_STATE_RECORDING,
+ COMMAND_BUFFER_STATE_RECORDING_ENDED,
+ COMMAND_BUFFER_STATE_SUBMITTED,
+ COMMAND_BUFFER_STATE_NOT_ALLOCATED,
+} vulkan_command_buffer_state;
+
+typedef struct vulkan_command_buffer {
+ VkCommandBuffer handle;
+ vulkan_command_buffer_state state;
+} vulkan_command_buffer;
+
+KITC_DECL_TYPED_ARRAY(vulkan_command_buffer)
+
+typedef struct vulkan_fence {
+ VkFence handle;
+ bool is_signaled;
+} vulkan_fence;
+
+typedef struct vulkan_shader_stage {
+ VkShaderModuleCreateInfo create_info;
+ VkShaderModule handle;
+ VkPipelineShaderStageCreateInfo stage_create_info;
+} vulkan_shader_stage;
+
+typedef struct vulkan_pipeline {
+ VkPipeline handle;
+ VkPipelineLayout layout;
+} vulkan_pipeline;
+
+typedef struct global_object_uniform {
+ mat4 projection; // 64 bytes
+ mat4 view; // 64 bytes
+ f32 padding[32];
+} global_object_uniform;
+
+typedef struct object_uniform {
+ vec4 diffuse_colour;
+ vec4 v_reserved0;
+ vec4 v_reserved1;
+ vec4 v_reserved2;
+} object_uniform;
+
+#define MAX_OBJECT_COUNT 1024
+#define VULKAN_OBJECT_SHADER_DESCRIPTOR_COUNT 1
+
+typedef struct geometry_render_data {
+ u32 id;
+ mat4 model;
+ texture* textures[16];
+} geometry_render_data;
+
+typedef struct vulkan_buffer {
+ u64 total_size;
+ VkBuffer handle;
+ VkBufferUsageFlagBits usage;
+ bool is_locked;
+ VkDeviceMemory memory;
+ i32 memory_index;
+ u32 memory_property_flags;
+} vulkan_buffer;
+
+#define SHADER_STAGE_COUNT 2
+
+typedef struct vulkan_shader {
+ // vertex, fragment
+ vulkan_shader_stage stages[SHADER_STAGE_COUNT];
+ vulkan_pipeline pipeline;
+
+ // descriptors
+ VkDescriptorPool descriptor_pool;
+ VkDescriptorSetLayout descriptor_set_layout;
+ VkDescriptorSet descriptor_sets[3]; // one for each in-flight frame
+
+ vulkan_buffer global_uniforms_buffer;
+
+ // Data that's global for all objects drawn
+ global_object_uniform global_ubo;
+ object_uniform object_ubo;
+ vulkan_texture_data* texture_data;
+} vulkan_shader;
+
+typedef struct vulkan_context {
+ VkInstance instance;
+ VkAllocationCallbacks* allocator;
+ VkSurfaceKHR surface;
+ vulkan_device device;
+ u32 framebuffer_width;
+ u32 framebuffer_height;
+ vulkan_swapchain swapchain;
+ vulkan_renderpass main_renderpass;
+ vulkan_buffer object_vertex_buffer;
+ vulkan_buffer object_index_buffer;
+ u64 geometry_vertex_offset;
+ u64 geometry_index_offset;
+
+ vulkan_command_buffer_darray* gfx_command_buffers;
+
+ VkSemaphore* image_available_semaphores;
+ VkSemaphore* queue_complete_semaphores;
+ u32 in_flight_fence_count;
+ vulkan_fence* in_flight_fences;
+ vulkan_fence** images_in_flight;
+
+ u32 image_index;
+ u32 current_frame;
+
+ vulkan_shader object_shader;
+
+ // TODO: swapchain recreation
+
+#if defined(DEBUG)
+ VkDebugUtilsMessengerEXT vk_debugger;
+#endif
+} vulkan_context;
+
+static vulkan_context context;
+
+static i32 find_memory_index(vulkan_context* context, u32 type_filter, u32 property_flags) {
+ VkPhysicalDeviceMemoryProperties memory_properties;
+ vkGetPhysicalDeviceMemoryProperties(context->device.physical_device, &memory_properties);
+
+ for (u32 i = 0; i < memory_properties.memoryTypeCount; ++i) {
+ // Check each memory type to see if its bit is set to 1.
+ if (type_filter & (1 << i) &&
+ (memory_properties.memoryTypes[i].propertyFlags & property_flags) == property_flags) {
+ return i;
+ }
+ }
+
+ WARN("Unable to find suitable memory type!");
+ return -1;
+}
+
+/** @brief Internal backend state */
+typedef struct vulkan_state {
+} vulkan_state;
+
+typedef struct vertex_pos {
+ vec3 pos;
+ vec3 normal;
+} vertex_pos;
+
+// pipeline stuff
+bool vulkan_graphics_pipeline_create(vulkan_context* context, vulkan_renderpass* renderpass,
+ u32 attribute_count,
+ VkVertexInputAttributeDescription* attributes,
+ u32 descriptor_set_layout_count,
+ VkDescriptorSetLayout* descriptor_set_layouts, u32 stage_count,
+ VkPipelineShaderStageCreateInfo* stages, VkViewport viewport,
+ VkRect2D scissor, bool is_wireframe,
+ vulkan_pipeline* out_pipeline) {
+ VkPipelineViewportStateCreateInfo viewport_state = {
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO
+ };
+ viewport_state.viewportCount = 1;
+ viewport_state.pViewports = &viewport;
+ viewport_state.scissorCount = 1;
+ viewport_state.pScissors = &scissor;
+
+ // Rasterizer
+ VkPipelineRasterizationStateCreateInfo rasterizer_create_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO
+ };
+ rasterizer_create_info.depthClampEnable = VK_FALSE;
+ rasterizer_create_info.rasterizerDiscardEnable = VK_FALSE;
+ rasterizer_create_info.polygonMode = is_wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL;
+ rasterizer_create_info.lineWidth = 1.0f;
+ rasterizer_create_info.cullMode = VK_CULL_MODE_BACK_BIT;
+ rasterizer_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterizer_create_info.depthBiasEnable = VK_FALSE;
+ rasterizer_create_info.depthBiasConstantFactor = 0.0;
+ rasterizer_create_info.depthBiasClamp = 0.0;
+ rasterizer_create_info.depthBiasSlopeFactor = 0.0;
+
+ // Multisampling
+ VkPipelineMultisampleStateCreateInfo ms_create_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO
+ };
+ ms_create_info.sampleShadingEnable = VK_FALSE;
+ ms_create_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ ms_create_info.minSampleShading = 1.0;
+ ms_create_info.pSampleMask = 0;
+ ms_create_info.alphaToCoverageEnable = VK_FALSE;
+ ms_create_info.alphaToOneEnable = VK_FALSE;
+
+ // Depth and stencil testing
+ VkPipelineDepthStencilStateCreateInfo depth_stencil = {
+ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
+ };
+ depth_stencil.depthTestEnable = VK_TRUE;
+ depth_stencil.depthWriteEnable = VK_TRUE;
+ depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS;
+ depth_stencil.depthBoundsTestEnable = VK_FALSE;
+ depth_stencil.stencilTestEnable = VK_FALSE;
+ depth_stencil.pNext = 0;
+
+ VkPipelineColorBlendAttachmentState color_blend_attachment_state;
+ color_blend_attachment_state.blendEnable = VK_TRUE;
+ color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD;
+ color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD;
+ color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
+ VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+
+ VkPipelineColorBlendStateCreateInfo color_blend = {
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO
+ };
+ color_blend.logicOpEnable = VK_FALSE;
+ color_blend.logicOp = VK_LOGIC_OP_COPY;
+ color_blend.attachmentCount = 1;
+ color_blend.pAttachments = &color_blend_attachment_state;
+
+ const u32 dynamic_state_count = 3;
+ VkDynamicState dynamic_states[3] = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR,
+ VK_DYNAMIC_STATE_LINE_WIDTH,
+ };
+
+ VkPipelineDynamicStateCreateInfo dynamic_state = {
+ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO
+ };
+ dynamic_state.dynamicStateCount = dynamic_state_count;
+ dynamic_state.pDynamicStates = dynamic_states;
+
+ // Vertex input
+ VkVertexInputBindingDescription binding_desc;
+ binding_desc.binding = 0;
+ binding_desc.stride = sizeof(vertex);
+ binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ VkPipelineVertexInputStateCreateInfo vertex_input_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
+ };
+ vertex_input_info.vertexBindingDescriptionCount = 1;
+ vertex_input_info.pVertexBindingDescriptions = &binding_desc;
+ vertex_input_info.vertexAttributeDescriptionCount = attribute_count;
+ vertex_input_info.pVertexAttributeDescriptions = attributes;
+
+ VkPipelineInputAssemblyStateCreateInfo input_assembly = {
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO
+ };
+ input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ input_assembly.primitiveRestartEnable = VK_FALSE;
+
+ VkPipelineLayoutCreateInfo pipeline_layout_create_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO
+ };
+
+ // Pushconstants
+ VkPushConstantRange push_constant;
+ push_constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+ push_constant.offset = sizeof(mat4) * 0;
+ push_constant.size = sizeof(mat4) * 2;
+
+ pipeline_layout_create_info.pushConstantRangeCount = 1;
+ pipeline_layout_create_info.pPushConstantRanges = &push_constant;
+
+ pipeline_layout_create_info.setLayoutCount = descriptor_set_layout_count;
+ pipeline_layout_create_info.pSetLayouts = descriptor_set_layouts;
+
+ vkCreatePipelineLayout(context->device.logical_device, &pipeline_layout_create_info,
+ context->allocator, &out_pipeline->layout);
+
+ VkGraphicsPipelineCreateInfo pipeline_create_info = {
+ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO
+ };
+ pipeline_create_info.stageCount = stage_count;
+ pipeline_create_info.pStages = stages;
+ pipeline_create_info.pVertexInputState = &vertex_input_info;
+ pipeline_create_info.pInputAssemblyState = &input_assembly;
+
+ pipeline_create_info.pViewportState = &viewport_state;
+ pipeline_create_info.pRasterizationState = &rasterizer_create_info;
+ pipeline_create_info.pMultisampleState = &ms_create_info;
+ pipeline_create_info.pDepthStencilState = &depth_stencil;
+ pipeline_create_info.pColorBlendState = &color_blend;
+ pipeline_create_info.pDynamicState = &dynamic_state;
+ pipeline_create_info.pTessellationState = 0;
+
+ pipeline_create_info.layout = out_pipeline->layout;
+
+ pipeline_create_info.renderPass = renderpass->handle;
+ pipeline_create_info.subpass = 0;
+ pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
+ pipeline_create_info.basePipelineIndex = -1;
+
+ VkResult result =
+ vkCreateGraphicsPipelines(context->device.logical_device, VK_NULL_HANDLE, 1,
+ &pipeline_create_info, context->allocator, &out_pipeline->handle);
+ if (result != VK_SUCCESS) {
+ FATAL("graphics pipeline creation failed. its fked mate");
+ ERROR_EXIT("Doomed");
+ }
+
+ return true;
+}
+
+void vulkan_pipeline_bind(vulkan_command_buffer* command_buffer, VkPipelineBindPoint bind_point,
+ vulkan_pipeline* pipeline) {
+ vkCmdBindPipeline(command_buffer->handle, bind_point, pipeline->handle);
+}
+
+void vulkan_buffer_bind(vulkan_context* context, vulkan_buffer* buffer, u64 offset) {
+ vkBindBufferMemory(context->device.logical_device, buffer->handle, buffer->memory, offset);
+}
+
+bool vulkan_buffer_create(vulkan_context* context, u64 size, VkBufferUsageFlagBits usage,
+ u32 memory_property_flags, bool bind_on_create,
+ vulkan_buffer* out_buffer) {
+ memset(out_buffer, 0, sizeof(vulkan_buffer));
+ out_buffer->total_size = size;
+ out_buffer->usage = usage;
+ out_buffer->memory_property_flags = memory_property_flags;
+
+ VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+ buffer_info.size = size;
+ buffer_info.usage = usage;
+ buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ vkCreateBuffer(context->device.logical_device, &buffer_info, context->allocator,
+ &out_buffer->handle);
+
+ VkMemoryRequirements requirements;
+ vkGetBufferMemoryRequirements(context->device.logical_device, out_buffer->handle, &requirements);
+ out_buffer->memory_index =
+ find_memory_index(context, requirements.memoryTypeBits, out_buffer->memory_property_flags);
+
+ // Allocate
+ VkMemoryAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ allocate_info.allocationSize = requirements.size;
+ allocate_info.memoryTypeIndex = (u32)out_buffer->memory_index;
+
+ vkAllocateMemory(context->device.logical_device, &allocate_info, context->allocator,
+ &out_buffer->memory);
+
+ if (bind_on_create) {
+ vulkan_buffer_bind(context, out_buffer, 0);
+ }
+
+ DEBUG("Created buffer.");
+
+ return true;
+}
+
+// lock and unlock?
+
+void* vulkan_buffer_lock_memory(vulkan_context* context, vulkan_buffer* buffer, u64 offset,
+ u64 size, u32 flags) {
+ void* data;
+ vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data);
+ return data;
+}
+void* vulkan_buffer_unlock_memory(vulkan_context* context, vulkan_buffer* buffer) {
+ vkUnmapMemory(context->device.logical_device, buffer->memory);
+}
+
+void vulkan_buffer_load_data(vulkan_context* context, vulkan_buffer* buffer, u64 offset, u64 size,
+ u32 flags, const void* data) {
+ void* data_ptr = 0;
+ VK_CHECK(
+ vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data_ptr));
+ memcpy(data_ptr, data, size);
+ vkUnmapMemory(context->device.logical_device, buffer->memory);
+}
+
+// TODO: destroy
+
+bool create_shader_module(vulkan_context* context, const char* filename, const char* type_str,
+ VkShaderStageFlagBits flag, u32 stage_index,
+ vulkan_shader_stage* shader_stages) {
+ memset(&shader_stages[stage_index].create_info, 0, sizeof(VkShaderModuleCreateInfo));
+ memset(&shader_stages[stage_index].stage_create_info, 0, sizeof(VkPipelineShaderStageCreateInfo));
+
+ shader_stages[stage_index].create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+
+ // todo: file input
+ FileData file_contents = load_spv_file(filename);
+
+ shader_stages[stage_index].create_info.codeSize = file_contents.size;
+ shader_stages[stage_index].create_info.pCode = (u32*)file_contents.data;
+
+ vkCreateShaderModule(context->device.logical_device, &shader_stages[stage_index].create_info,
+ context->allocator, &shader_stages[stage_index].handle);
+
+ shader_stages[stage_index].stage_create_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ shader_stages[stage_index].stage_create_info.stage = flag;
+ shader_stages[stage_index].stage_create_info.module = shader_stages[stage_index].handle;
+ shader_stages[stage_index].stage_create_info.pName = "main";
+
+ free(file_contents.data);
+
+ // TODO: Descriptors
+
+ return true;
+}
+
+bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_shader) {
+ char stage_type_strs[SHADER_STAGE_COUNT][5] = { "vert", "frag" };
+ char stage_filenames[SHADER_STAGE_COUNT][256] = { "build/linux/x86_64/debug/object.vert.spv",
+ "build/linux/x86_64/debug/object.frag.spv" };
+ VkShaderStageFlagBits stage_types[SHADER_STAGE_COUNT] = { VK_SHADER_STAGE_VERTEX_BIT,
+ VK_SHADER_STAGE_FRAGMENT_BIT };
+ for (u8 i = 0; i < SHADER_STAGE_COUNT; i++) {
+ DEBUG("Loading %s", stage_filenames[i]);
+ create_shader_module(context, stage_filenames[i], stage_type_strs[i], stage_types[i], i,
+ out_shader->stages);
+ }
+
+ // descriptors
+ VkDescriptorSetLayoutBinding global_ubo_layout_binding;
+ global_ubo_layout_binding.binding = 0;
+ global_ubo_layout_binding.descriptorCount = 1;
+ global_ubo_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ global_ubo_layout_binding.pImmutableSamplers = 0;
+ global_ubo_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+
+ VkDescriptorSetLayoutBinding sampler_layout_binding;
+ sampler_layout_binding.binding = 1;
+ sampler_layout_binding.descriptorCount = 1;
+ sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ sampler_layout_binding.pImmutableSamplers = 0;
+ sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+
+ VkDescriptorSetLayoutCreateInfo global_layout_info = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO
+ };
+
+ VkDescriptorSetLayoutBinding bindings[2] = { global_ubo_layout_binding, sampler_layout_binding };
+
+ global_layout_info.bindingCount = 2;
+ global_layout_info.pBindings = bindings;
+
+ VK_CHECK(vkCreateDescriptorSetLayout(context->device.logical_device, &global_layout_info,
+ context->allocator, &out_shader->descriptor_set_layout));
+
+ VkDescriptorPoolSize global_pool_size;
+ global_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ global_pool_size.descriptorCount = 3;
+
+ VkDescriptorPoolSize sampler_pool_size;
+ sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ sampler_pool_size.descriptorCount = 3;
+
+ VkDescriptorPoolSize pool_sizes[2] = { global_pool_size, sampler_pool_size };
+
+ VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
+ pool_info.poolSizeCount = 2;
+ pool_info.pPoolSizes = pool_sizes;
+ pool_info.maxSets = 3;
+
+ VK_CHECK(vkCreateDescriptorPool(context->device.logical_device, &pool_info, context->allocator,
+ &out_shader->descriptor_pool));
+
+ // Pipeline creation
+ VkViewport viewport;
+ viewport.x = 0;
+ viewport.y = 0;
+ viewport.width = (f32)context->framebuffer_width;
+ viewport.height = (f32)context->framebuffer_height;
+ viewport.minDepth = 0.0;
+ viewport.maxDepth = 1.0;
+
+ VkRect2D scissor;
+ scissor.offset.x = scissor.offset.y = 0;
+ scissor.extent.width = context->framebuffer_width;
+ scissor.extent.height = context->framebuffer_height;
+
+ // Attributes
+ u32 offset = 0;
+ const i32 attribute_count = 3;
+ VkVertexInputAttributeDescription attribute_descs[3];
+ // Position
+ VkFormat formats[3] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT,
+ VK_FORMAT_R32G32_SFLOAT };
+
+ u64 sizes[3] = { sizeof(vec3), sizeof(vec3), sizeof(vec2) };
+
+ for (u32 i = 0; i < attribute_count; i++) {
+ attribute_descs[i].binding = 0;
+ attribute_descs[i].location = i;
+ attribute_descs[i].format = formats[i];
+ attribute_descs[i].offset = offset;
+ offset += sizes[i];
+ }
+
+ // Descriptor set layouts
+ VkDescriptorSetLayout layouts[1] = { out_shader->descriptor_set_layout };
+
+ // Stages
+ VkPipelineShaderStageCreateInfo stage_create_infos[SHADER_STAGE_COUNT];
+ memset(stage_create_infos, 0, sizeof(stage_create_infos));
+ for (u32 i = 0; i < SHADER_STAGE_COUNT; i++) {
+ stage_create_infos[i].sType = out_shader->stages[i].stage_create_info.sType;
+ stage_create_infos[i] = out_shader->stages[i].stage_create_info;
+ }
+
+ vulkan_graphics_pipeline_create(
+ context, &context->main_renderpass, attribute_count, attribute_descs, 1, layouts,
+ SHADER_STAGE_COUNT, stage_create_infos, viewport, scissor, false, &out_shader->pipeline);
+ INFO("Graphics pipeline created!");
+
+ // Uniform buffer
+ if (!vulkan_buffer_create(context, sizeof(global_object_uniform),
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ true, &out_shader->global_uniforms_buffer)) {
+ ERROR("Couldnt create uniforms buffer");
+ return false;
+ }
+
+ VkDescriptorSetLayout global_layouts[3] = {
+ out_shader->descriptor_set_layout,
+ out_shader->descriptor_set_layout,
+ out_shader->descriptor_set_layout,
+ };
+
+ VkDescriptorSetAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
+ alloc_info.descriptorPool = out_shader->descriptor_pool;
+ alloc_info.descriptorSetCount = 3;
+ alloc_info.pSetLayouts = global_layouts;
+ VK_CHECK(vkAllocateDescriptorSets(context->device.logical_device, &alloc_info,
+ out_shader->descriptor_sets));
+
+ return true;
+}
+void vulkan_object_shader_destroy(vulkan_context* context, vulkan_shader* shader) {}
+void vulkan_object_shader_use(vulkan_context* context, vulkan_shader* shader) {
+ u32 image_index = context->image_index;
+ vulkan_pipeline_bind(&context->gfx_command_buffers->data[image_index],
+ VK_PIPELINE_BIND_POINT_GRAPHICS, &shader->pipeline);
+}
+void vulkan_object_shader_update_global_state(vulkan_context* context, vulkan_shader* shader) {
+ u32 image_index = context->image_index;
+ VkCommandBuffer cmd_buffer = context->gfx_command_buffers->data[image_index].handle;
+ VkDescriptorSet global_descriptors = shader->descriptor_sets[image_index];
+
+ u32 range = sizeof(global_object_uniform);
+ u64 offset = 0;
+
+ // copy data to buffer
+ vulkan_buffer_load_data(context, &shader->global_uniforms_buffer, offset, range, 0,
+ &shader->global_ubo);
+
+ VkDescriptorBufferInfo buffer_info;
+ buffer_info.buffer = shader->global_uniforms_buffer.handle;
+ buffer_info.offset = offset;
+ buffer_info.range = range;
+
+ VkDescriptorImageInfo image_info;
+ image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ image_info.imageView = shader->texture_data->image.view;
+ image_info.sampler = shader->texture_data->sampler;
+
+ VkWriteDescriptorSet uniform_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
+ uniform_write.dstSet = shader->descriptor_sets[image_index];
+ uniform_write.dstBinding = 0;
+ uniform_write.dstArrayElement = 0;
+ uniform_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ uniform_write.descriptorCount = 1;
+ uniform_write.pBufferInfo = &buffer_info;
+
+ VkWriteDescriptorSet texture_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
+ texture_write.dstSet = shader->descriptor_sets[image_index];
+ texture_write.dstBinding = 1;
+ texture_write.dstArrayElement = 0;
+ texture_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ texture_write.descriptorCount = 1;
+ texture_write.pImageInfo = &image_info;
+
+ VkWriteDescriptorSet writes[2] = { uniform_write, texture_write };
+
+ vkUpdateDescriptorSets(context->device.logical_device, 2, writes, 0, 0);
+
+ vkCmdBindDescriptorSets(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, shader->pipeline.layout, 0,
+ 1, &global_descriptors, 0, 0);
+}
+
+void vulkan_object_shader_update_object(vulkan_context* context, vulkan_shader* shader,
+ mat4 model) {
+ u32 image_index = context->image_index;
+ VkCommandBuffer cmd_buffer = context->gfx_command_buffers->data[image_index].handle;
+ // vulkan_command_buffer* cmd_buffer = &context->gfx_command_buffers->data[context.image_index];
+
+ vkCmdPushConstants(cmd_buffer, shader->pipeline.layout, VK_SHADER_STAGE_VERTEX_BIT, 0,
+ sizeof(mat4), &model);
+
+ // vulkan_object_shader_use(context, &context->object_shader);
+ VkDeviceSize offsets[1] = { 0 };
+ vkCmdBindVertexBuffers(cmd_buffer, 0, 1, &context->object_vertex_buffer.handle,
+ (VkDeviceSize*)offsets);
+
+ vkCmdBindIndexBuffer(cmd_buffer, context->object_index_buffer.handle, 0, VK_INDEX_TYPE_UINT32);
+
+ vkCmdDrawIndexed(cmd_buffer, 36, 1, 0, 0, 0);
+ // vkCmdDraw(cmd_buffer, 36, 1, 0, 0);
+}
+
+bool select_physical_device(vulkan_context* ctx) {
+ u32 physical_device_count = 0;
+ VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, 0));
+ if (physical_device_count == 0) {
+ FATAL("No devices that support vulkan were found");
+ return false;
+ }
+ TRACE("Number of devices found %d", physical_device_count);
+
+ VkPhysicalDevice physical_devices[physical_device_count];
+ VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, physical_devices));
+
+ for (u32 i = 0; i < physical_device_count; i++) {
+ VkPhysicalDeviceProperties properties;
+ vkGetPhysicalDeviceProperties(physical_devices[i], &properties);
+
+ VkPhysicalDeviceFeatures features;
+ vkGetPhysicalDeviceFeatures(physical_devices[i], &features);
+
+ VkPhysicalDeviceMemoryProperties memory;
+ vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &memory);
+
+ vulkan_physical_device_requirements requirements = {};
+ requirements.graphics = true;
+ requirements.present = true;
+ requirements.compute = true;
+ requirements.transfer = true;
+
+ requirements.sampler_anistropy = true;
+ requirements.discrete_gpu = true;
+ requirements.device_ext_names[0] = str8lit(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+ requirements.device_ext_name_count = 1;
+
+ vulkan_physical_device_queue_family_info queue_info = {};
+
+ bool result = physical_device_meets_requirements(physical_devices[i], ctx->surface, &properties,
+ &features, &requirements, &queue_info,
+ &ctx->device.swapchain_support);
+
+ if (result) {
+ INFO("GPU Driver version: %d.%d.%d", VK_VERSION_MAJOR(properties.driverVersion),
+ VK_VERSION_MINOR(properties.driverVersion), VK_VERSION_PATCH(properties.driverVersion));
+
+ INFO("Vulkan API version: %d.%d.%d", VK_VERSION_MAJOR(properties.apiVersion),
+ VK_VERSION_MINOR(properties.apiVersion), VK_VERSION_PATCH(properties.apiVersion));
+
+ // TODO: print gpu memory information -
+ // https://youtu.be/6Kj3O2Ov1RU?si=pXfP5NvXXcXjJsrG&t=2439
+
+ ctx->device.physical_device = physical_devices[i];
+ ctx->device.graphics_queue_index = queue_info.graphics_family_index;
+ ctx->device.present_queue_index = queue_info.present_family_index;
+ ctx->device.compute_queue_index = queue_info.compute_family_index;
+ ctx->device.transfer_queue_index = queue_info.transfer_family_index;
+ ctx->device.properties = properties;
+ ctx->device.features = features;
+ ctx->device.memory = memory;
+ break;
+ }
+ }
+
+ if (!ctx->device.physical_device) {
+ ERROR("No suitable physical devices were found :(");
+ return false;
+ }
+
+ INFO("Physical device selected: %s\n", ctx->device.properties.deviceName);
+ return true;
+}
+
+bool vulkan_device_create(vulkan_context* context) {
+ // Physical device - NOTE: mutates the context directly
+ if (!select_physical_device(context)) {
+ return false;
+ }
+
+// Logical device - NOTE: mutates the context directly
+
+// queues
+#define VULKAN_QUEUES_COUNT 2
+ const char* queue_names[VULKAN_QUEUES_COUNT] = {
+ "GRAPHICS",
+ "TRANSFER",
+ };
+ i32 indices[VULKAN_QUEUES_COUNT] = {
+ context->device.graphics_queue_index,
+ context->device.transfer_queue_index,
+ };
+ f32 prio_one = 1.0;
+ VkDeviceQueueCreateInfo queue_create_info[2]; // HACK: make 2 queues, graphics and transfer
+ // graphics
+ queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queue_create_info[0].queueFamilyIndex = 0;
+ queue_create_info[0].queueCount = 1;
+ queue_create_info[0].flags = 0;
+ queue_create_info[0].pNext = 0;
+ queue_create_info[0].pQueuePriorities = &prio_one;
+ // transfer
+ queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queue_create_info[1].queueFamilyIndex = 1;
+ queue_create_info[1].queueCount = 1;
+ queue_create_info[1].flags = 0;
+ queue_create_info[1].pNext = 0;
+ queue_create_info[1].pQueuePriorities = &prio_one;
+
+ // for (int i = 0; i < 2; i++) {
+ // TRACE("Configure %s queue", queue_names[i]);
+ // queue_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ // queue_create_info[i].queueFamilyIndex = indices[i];
+ // queue_create_info[i].queueCount = 1; // make just one of them
+ // queue_create_info[i].flags = 0;
+ // queue_create_info[i].pNext = 0;
+ // f32 priority = 1.0;
+ // queue_create_info[i].pQueuePriorities = &priority;
+ // }
+
+ // features
+ VkPhysicalDeviceFeatures device_features = {};
+ device_features.samplerAnisotropy = VK_TRUE; // request anistrophy
+
+ // device itself
+ VkDeviceCreateInfo device_create_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
+ device_create_info.queueCreateInfoCount = VULKAN_QUEUES_COUNT;
+ device_create_info.pQueueCreateInfos = queue_create_info;
+ device_create_info.pEnabledFeatures = &device_features;
+ device_create_info.enabledExtensionCount = 1;
+ const char* extension_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
+ device_create_info.ppEnabledExtensionNames = &extension_names;
+
+ // deprecated
+ device_create_info.enabledLayerCount = 0;
+ device_create_info.ppEnabledLayerNames = 0;
+
+ VkResult result = vkCreateDevice(context->device.physical_device, &device_create_info,
+ context->allocator, &context->device.logical_device);
+ if (result != VK_SUCCESS) {
+ printf("error creating logical device with status %u\n", result);
+ ERROR_EXIT("Bye bye");
+ }
+ INFO("Logical device created");
+
+ // get queues
+ vkGetDeviceQueue(context->device.logical_device, context->device.graphics_queue_index, 0,
+ &context->device.graphics_queue);
+ // vkGetDeviceQueue(context->device.logical_device, context->device.present_queue_index, 0,
+ // &context->device.present_queue);
+ // vkGetDeviceQueue(context->device.logical_device, context->device.compute_queue_index, 0,
+ // &context->device.compute_queue);
+ vkGetDeviceQueue(context->device.logical_device, context->device.transfer_queue_index, 0,
+ &context->device.transfer_queue);
+
+ // create command pool for graphics queue
+ VkCommandPoolCreateInfo pool_create_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
+ pool_create_info.queueFamilyIndex = context->device.graphics_queue_index;
+ pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ vkCreateCommandPool(context->device.logical_device, &pool_create_info, context->allocator,
+ &context->device.gfx_command_pool);
+ INFO("Created Command Pool")
+
+ return true;
+}
+void vulkan_device_destroy(vulkan_context* context) {
+ context->device.physical_device = 0; // release
+ // TODO: reset other memory
+}
+
+bool vulkan_device_detect_depth_format(vulkan_device* device) {
+ const size_t n_candidates = 3;
+ VkFormat candidates[3] = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT,
+ VK_FORMAT_D24_UNORM_S8_UINT };
+ u32 flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ for (u64 i = 0; i < n_candidates; i++) {
+ VkFormatProperties properties;
+ vkGetPhysicalDeviceFormatProperties(device->physical_device, candidates[i], &properties);
+
+ if ((properties.linearTilingFeatures & flags) == flags) {
+ device->depth_format = candidates[i];
+ return true;
+ }
+ if ((properties.optimalTilingFeatures & flags) == flags) {
+ device->depth_format = candidates[i];
+ return true;
+ }
+ }
+ return false;
+}
+
+void vulkan_image_view_create(vulkan_context* context, VkFormat format, vulkan_image* image,
+ VkImageAspectFlags aspect_flags) {
+ VkImageViewCreateInfo view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
+ view_create_info.image = image->handle;
+ view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ view_create_info.format = format;
+ view_create_info.subresourceRange.aspectMask = aspect_flags;
+
+ view_create_info.subresourceRange.baseMipLevel = 0;
+ view_create_info.subresourceRange.levelCount = 1;
+ view_create_info.subresourceRange.baseArrayLayer = 0;
+ view_create_info.subresourceRange.layerCount = 1;
+
+ vkCreateImageView(context->device.logical_device, &view_create_info, context->allocator,
+ &image->view);
+}
+
+void vulkan_image_transition_layout(vulkan_context* context, vulkan_command_buffer* command_buffer,
+ vulkan_image* image, VkFormat format, VkImageLayout old_layout,
+ VkImageLayout new_layout) {
+ VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
+ void vulkan_image_transition_layout(
+ vulkan_context * context, vulkan_command_buffer * command_buffer, vulkan_image * image,
+ VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout) {
+ VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
+ barrier.oldLayout = old_layout;
+ barrier.newLayout = new_layout;
+ barrier.srcQueueFamilyIndex = context->device.graphics_queue_index;
+ barrier.dstQueueFamilyIndex = context->device.graphics_queue_index;
+ barrier.image = image->handle;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = 1;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = 1;
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = 0;
+
+ VkPipelineStageFlags source_stage;
+ VkPipelineStageFlags dest_stage;
+
+ if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED &&
+ new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dest_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+ } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
+ new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dest_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ } else {
+ FATAL("Unsupported image layout transition");
+ return;
+ }
+
+ vkCmdPipelineBarrier(command_buffer->handle, source_stage, dest_stage, 0, 0, 0, 0, 0, 1,
+ &barrier);
+ }
+
+ void vulkan_image_copy_from_buffer(vulkan_image * image, VkBuffer buffer,
+ vulkan_command_buffer * command_buffer) {
+ VkBufferImageCopy region;
+ region.bufferOffset = 0;
+ region.bufferRowLength = 0;
+ region.bufferImageHeight = 0;
+ region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.imageSubresource.mipLevel = 0;
+ region.imageSubresource.baseArrayLayer = 0;
+ region.imageSubresource.layerCount = 1;
+ printf("Image details width: %d height %d\n", image->width, image->height);
+ region.imageOffset.x = 0;
+ region.imageOffset.y = 0;
+ region.imageOffset.z = 0;
+ region.imageExtent.width = image->width;
+ region.imageExtent.height = image->height;
+ region.imageExtent.depth = 1;
+
+ vkCmdCopyBufferToImage(command_buffer->handle, buffer, image->handle,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+ }
+
+ void vulkan_image_create(vulkan_context * context, VkImageType image_type, u32 width, u32 height,
+ VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage,
+ VkMemoryPropertyFlags memory_flags, bool create_view,
+ VkImageAspectFlags aspect_flags, vulkan_image* out_image) {
+ // copy params
+ out_image->width = width;
+ out_image->height = height;
+
+ // create info
+ VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
+ image_create_info.imageType = image_type;
+ image_create_info.extent.width = width;
+ image_create_info.extent.height = height;
+ image_create_info.extent.depth = 1;
+ image_create_info.mipLevels = 1;
+ image_create_info.arrayLayers = 1;
+ image_create_info.format = format;
+ image_create_info.tiling = tiling;
+ image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ image_create_info.usage = usage;
+ image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
+ image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ VK_CHECK(vkCreateImage(context->device.logical_device, &image_create_info, context->allocator,
+ &out_image->handle));
+
+ VkMemoryRequirements memory_reqs;
+ vkGetImageMemoryRequirements(context->device.logical_device, out_image->handle, &memory_reqs);
+
+ i32 memory_type = -1;
+ VkPhysicalDeviceMemoryProperties memory_properties;
+ vkGetPhysicalDeviceMemoryProperties(context->device.physical_device, &memory_properties);
+
+ for (u32 i = 0; i < memory_properties.memoryTypeCount; i++) {
+ // typefilter = memoryTypeBits , prop filter = memory_flags
+ if (memory_reqs.memoryTypeBits & (1 << i) &&
+ (memory_properties.memoryTypes[i].propertyFlags & memory_flags)) {
+ memory_type = i;
+ break;
+ }
+ }
+
+ if (memory_type < 0) {
+ ERROR_EXIT("couldnt find a suitable memory type for the image");
+ }
+
+ // allocate memory
+ VkMemoryAllocateInfo memory_allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ memory_allocate_info.allocationSize = memory_reqs.size;
+ memory_allocate_info.memoryTypeIndex = memory_type;
+ vkAllocateMemory(context->device.logical_device, &memory_allocate_info, context->allocator,
+ &out_image->memory);
+
+ // bind memory
+ // TODO: maybe bind context->device.logical_device to device at the top of the functions?
+ vkBindImageMemory(context->device.logical_device, out_image->handle, out_image->memory, 0);
+
+ if (create_view) {
+ out_image->view = 0;
+ vulkan_image_view_create(context, format, out_image, aspect_flags);
+ }
+ }
+
+ // TODO: vulkan_image_destroy
+
+ void vulkan_framebuffer_create(vulkan_context * context, vulkan_renderpass * renderpass,
+ u32 width, u32 height, u32 attachment_count,
+ VkImageView * attachments, vulkan_framebuffer * out_framebuffer) {
+ out_framebuffer->attachments = malloc(sizeof(VkImageView) * attachment_count);
+ for (u32 i = 0; i < attachment_count; i++) {
+ out_framebuffer->attachments[i] = attachments[i];
+ }
+ out_framebuffer->attachment_count = attachment_count;
+ out_framebuffer->renderpass = renderpass;
+
+ VkFramebufferCreateInfo framebuffer_create_info = {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO
+ }; // TODO
+
+ framebuffer_create_info.renderPass = renderpass->handle;
+ framebuffer_create_info.attachmentCount = attachment_count;
+ framebuffer_create_info.pAttachments = out_framebuffer->attachments;
+ framebuffer_create_info.width = width;
+ framebuffer_create_info.height = height;
+ framebuffer_create_info.layers = 1;
+
+ vkCreateFramebuffer(context->device.logical_device, &framebuffer_create_info,
+ context->allocator, &out_framebuffer->handle);
+ }
+
+ // TODO: vulkan_framebuffer_destroy
+
+ void vulkan_command_buffer_allocate(vulkan_context * context, VkCommandPool pool, bool is_primary,
+ vulkan_command_buffer* out_command_buffer) {
+ VkCommandBufferAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
+ allocate_info.commandPool = pool;
+ allocate_info.level =
+ is_primary ? VK_COMMAND_BUFFER_LEVEL_PRIMARY : VK_COMMAND_BUFFER_LEVEL_SECONDARY;
+ allocate_info.commandBufferCount = 1;
+ allocate_info.pNext = 0;
+
+ out_command_buffer->state = COMMAND_BUFFER_STATE_NOT_ALLOCATED;
+ vkAllocateCommandBuffers(context->device.logical_device, &allocate_info,
+ &out_command_buffer->handle);
+ out_command_buffer->state = COMMAND_BUFFER_STATE_READY;
+ }
+
+ void vulkan_command_buffer_free(vulkan_context * context, VkCommandPool pool,
+ vulkan_command_buffer * out_command_buffer) {
+ // TODO: implement freeing
+ }
+
+ void vulkan_command_buffer_begin(vulkan_command_buffer * command_buffer, bool is_single_use,
+ bool is_renderpass_continue, bool is_simultaneous_use) {
+ VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
+ begin_info.flags = 0;
+ if (is_single_use) {
+ begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+ }
+ // TODO: RENDER_PASS_CONTINUE_BIT & SIMULTANEOUS_USE_BIT
+
+ begin_info.pNext = 0;
+ begin_info.pInheritanceInfo = 0;
+ vkBeginCommandBuffer(command_buffer->handle, &begin_info);
+
+ command_buffer->state = COMMAND_BUFFER_STATE_RECORDING;
+ }
+
+ void vulkan_command_buffer_end(vulkan_command_buffer * command_buffer) {
+ VK_CHECK(vkEndCommandBuffer(command_buffer->handle));
+ command_buffer->state = COMMAND_BUFFER_STATE_RECORDING_ENDED;
+ }
+ void vulkan_command_buffer_update_submitted(vulkan_command_buffer * command_buffer) {
+ command_buffer->state = COMMAND_BUFFER_STATE_SUBMITTED;
+ }
+ void vulkan_command_buffer_reset(vulkan_command_buffer * command_buffer) {
+ command_buffer->state = COMMAND_BUFFER_STATE_READY;
+ }
+
+ void vulkan_command_buffer_allocate_and_begin_oneshot(
+ vulkan_context * context, VkCommandPool pool, vulkan_command_buffer * out_command_buffer) {
+ vulkan_command_buffer_allocate(context, pool, true, out_command_buffer);
+ vulkan_command_buffer_begin(out_command_buffer, true, false, false);
+ }
+
+ void vulkan_command_buffer_end_oneshot(vulkan_context * context, VkCommandPool pool,
+ vulkan_command_buffer * command_buffer, VkQueue queue) {
+ vulkan_command_buffer_end(command_buffer);
+
+ // submit to queue
+ VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &command_buffer->handle;
+ VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, 0));
+ // wait for it to finish
+ VK_CHECK(vkQueueWaitIdle(queue));
+
+ vulkan_command_buffer_free(context, pool, command_buffer);
+ }
+
+ void vulkan_buffer_copy_to(vulkan_context * context, VkCommandPool pool, VkFence fence,
+ VkQueue queue, VkBuffer source, u64 source_offset, VkBuffer dest,
+ u64 dest_offset, u64 size) {
+ vkQueueWaitIdle(queue);
+
+ vulkan_command_buffer temp_cmd_buf;
+ vulkan_command_buffer_allocate_and_begin_oneshot(context, pool, &temp_cmd_buf);
+
+ VkBufferCopy copy_region;
+ copy_region.srcOffset = source_offset;
+ copy_region.dstOffset = dest_offset;
+ copy_region.size = size;
+
+ vkCmdCopyBuffer(temp_cmd_buf.handle, source, dest, 1, &copy_region);
+
+ vulkan_command_buffer_end_oneshot(context, pool, &temp_cmd_buf, queue);
+ }
+
+ void vulkan_swapchain_create(vulkan_context * context, u32 width, u32 height,
+ vulkan_swapchain * out_swapchain) {
+ VkExtent2D swapchain_extent = { width, height };
+ out_swapchain->max_frames_in_flight = 2; // support triple buffering
+
+ // find a format
+ bool found;
+ for (u32 i = 0; i < context->device.swapchain_support.format_count; i++) {
+ VkSurfaceFormatKHR format = context->device.swapchain_support.formats[i];
+ if (format.format == VK_FORMAT_B8G8R8A8_UNORM &&
+ format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+ out_swapchain->image_format = format;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ out_swapchain->image_format = context->device.swapchain_support.formats[0];
+ }
+
+ VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; // guaranteed to be implemented
+ // TODO: look for mailbox - https://youtu.be/jWKVb_QdSNM?si=bHcd3sEf-M0x3QwH&t=1687
+
+ // TODO: requery swapchain support
+
+ u32 image_count = context->device.swapchain_support.capabilities.minImageCount;
+
+ VkSwapchainCreateInfoKHR swapchain_create_info = {
+ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
+ };
+ swapchain_create_info.surface = context->surface;
+ swapchain_create_info.minImageCount = image_count;
+ swapchain_create_info.imageFormat = out_swapchain->image_format.format;
+ swapchain_create_info.imageColorSpace = out_swapchain->image_format.colorSpace;
+ DEBUG("Image extent %d %d\n", swapchain_extent.width, swapchain_extent.height);
+ swapchain_create_info.imageExtent = swapchain_extent;
+ swapchain_create_info.imageArrayLayers = 1;
+ swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchain_create_info.queueFamilyIndexCount = 0;
+ swapchain_create_info.pQueueFamilyIndices = 0;
+
+ swapchain_create_info.preTransform =
+ context->device.swapchain_support.capabilities.currentTransform;
+ swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+ swapchain_create_info.presentMode = present_mode;
+ swapchain_create_info.clipped = VK_TRUE;
+ swapchain_create_info.oldSwapchain = 0;
+
+ TRACE("Create swapchain");
+ VK_CHECK(vkCreateSwapchainKHR(context->device.logical_device, &swapchain_create_info,
+ context->allocator, &out_swapchain->handle));
+
+ context->current_frame = 0;
+
+ // images
+ out_swapchain->image_count = 0;
+ vkGetSwapchainImagesKHR(context->device.logical_device, out_swapchain->handle,
+ &out_swapchain->image_count, 0);
+
+ if (!out_swapchain->images) {
+ out_swapchain->images = (VkImage*)malloc(sizeof(VkImage) * out_swapchain->image_count);
+ }
+ if (!out_swapchain->views) {
+ out_swapchain->views = (VkImageView*)malloc(sizeof(VkImage) * out_swapchain->image_count);
+ }
+ VK_CHECK(vkGetSwapchainImagesKHR(context->device.logical_device, out_swapchain->handle,
+ &out_swapchain->image_count, out_swapchain->images));
+
+ // views
+ for (int i = 0; i < out_swapchain->image_count; i++) {
+ VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
+ view_info.image = out_swapchain->images[i];
+ view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ view_info.format = out_swapchain->image_format.format;
+ view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ view_info.subresourceRange.baseMipLevel = 0;
+ view_info.subresourceRange.levelCount = 1;
+ view_info.subresourceRange.baseArrayLayer = 0;
+ view_info.subresourceRange.layerCount = 1;
+
+ VK_CHECK(vkCreateImageView(context->device.logical_device, &view_info, context->allocator,
+ &out_swapchain->views[i]));
+ }
+
+ // depth attachment
+ if (!vulkan_device_detect_depth_format(&context->device)) {
+ ERROR_EXIT("Failed to find a supported depth format");
+ }
+ vulkan_image_create(context, VK_IMAGE_TYPE_2D, swapchain_extent.width, swapchain_extent.height,
+ context->device.depth_format, VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_DEPTH_BIT,
+ &out_swapchain->depth_attachment);
+ INFO("Depth attachment created");
+
+ INFO("Swapchain created successfully");
+ }
+
+ // TODO: swapchain destroy
+ void vulkan_swapchain_recreate(vulkan_context * context, u32 width, u32 height,
+ vulkan_swapchain * swapchain) {
+ // TODO
+ }
+ bool vulkan_swapchain_acquire_next_image_index(
+ vulkan_context * context, vulkan_swapchain * swapchain, u64 timeout_ns,
+ VkSemaphore image_available_semaphore, VkFence fence, u32 * out_image_index) {
+ VkResult result =
+ vkAcquireNextImageKHR(context->device.logical_device, swapchain->handle, timeout_ns,
+ image_available_semaphore, fence, out_image_index);
+
+ if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
+ FATAL("Failed to acquire swapchain image");
+ return false;
+ }
+
+ return true;
+ }
+
+ void vulkan_swapchain_present(vulkan_context * context, vulkan_swapchain * swapchain,
+ VkQueue graphics_queue, VkQueue present_queue,
+ VkSemaphore render_complete_semaphore, u32 present_image_index) {
+ // return image to swapchain for presentation
+ VkPresentInfoKHR present_info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
+ present_info.waitSemaphoreCount = 1;
+ present_info.pWaitSemaphores = &render_complete_semaphore;
+ present_info.swapchainCount = 1;
+ present_info.pSwapchains = &swapchain->handle;
+ present_info.pImageIndices = &present_image_index;
+ present_info.pResults = 0;
+
+ VkResult result = vkQueuePresentKHR(present_queue, &present_info);
+ if (result != VK_SUCCESS) {
+ if (result == VK_SUBOPTIMAL_KHR) {
+ // WARN("Swapchain suboptimal - maybe resize needed?");
+ } else {
+ FATAL("Failed to present swapchain iamge");
+ }
+ }
+
+ // advance the current frame
+ context->current_frame = (context->current_frame + 1) % swapchain->max_frames_in_flight;
+ }
+
+ void vulkan_renderpass_create(vulkan_context * context, vulkan_renderpass * out_renderpass,
+ vec4 render_area, vec4 clear_colour, f32 depth, u32 stencil) {
+ out_renderpass->render_area = render_area;
+ out_renderpass->clear_colour = clear_colour;
+ out_renderpass->depth = depth;
+ out_renderpass->stencil = stencil;
+
+ // main subpass
+ VkSubpassDescription subpass = {};
+ subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+
+ // attachments
+ u32 attachment_desc_count = 2;
+ VkAttachmentDescription attachment_descriptions[2];
+
+ // Colour attachment
+ VkAttachmentDescription color_attachment;
+ color_attachment.format = context->swapchain.image_format.format;
+ color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+ color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ color_attachment.flags = 0;
+
+ attachment_descriptions[0] = color_attachment;
+
+ VkAttachmentReference color_attachment_reference;
+ color_attachment_reference.attachment = 0;
+ color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+ subpass.colorAttachmentCount = 1;
+ subpass.pColorAttachments = &color_attachment_reference;
+
+ // Depth attachment
+ VkAttachmentDescription depth_attachment;
+ depth_attachment.format = context->device.depth_format;
+ depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+ depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ depth_attachment.flags = 0;
+
+ attachment_descriptions[1] = depth_attachment;
+
+ VkAttachmentReference depth_attachment_reference;
+ depth_attachment_reference.attachment = 1;
+ depth_attachment_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+ subpass.pDepthStencilAttachment = &depth_attachment_reference;
+
+ // TODO: other attachment styles
+
+ subpass.inputAttachmentCount = 0;
+ subpass.pInputAttachments = 0;
+ subpass.pResolveAttachments = 0;
+ subpass.preserveAttachmentCount = 0;
+ subpass.preserveAttachmentCount = 0;
+
+ // renderpass dependencies
+ VkSubpassDependency dependency;
+ dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+ dependency.dstSubpass = 0;
+ dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependency.srcAccessMask = 0;
+ dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependency.dstAccessMask =
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ dependency.dependencyFlags = 0;
+
+ VkRenderPassCreateInfo render_pass_create_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
+ render_pass_create_info.attachmentCount = attachment_desc_count;
+ render_pass_create_info.pAttachments = attachment_descriptions;
+ render_pass_create_info.subpassCount = 1;
+ render_pass_create_info.pSubpasses = &subpass;
+ render_pass_create_info.dependencyCount = 1;
+ render_pass_create_info.pDependencies = &dependency;
+ render_pass_create_info.pNext = 0;
+ render_pass_create_info.flags = 0;
+
+ VK_CHECK(vkCreateRenderPass(context->device.logical_device, &render_pass_create_info,
+ context->allocator, &out_renderpass->handle));
+ }
+
+ // TODO: renderpass destroy
+
+ void vulkan_renderpass_begin(vulkan_command_buffer * command_buffer,
+ vulkan_renderpass * renderpass, VkFramebuffer framebuffer) {
+ VkRenderPassBeginInfo begin_info = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
+ begin_info.renderPass = renderpass->handle;
+ begin_info.framebuffer = framebuffer;
+ begin_info.renderArea.offset.x = renderpass->render_area.x;
+ begin_info.renderArea.offset.y = renderpass->render_area.y;
+ begin_info.renderArea.extent.width = renderpass->render_area.z;
+ begin_info.renderArea.extent.height = renderpass->render_area.w;
+
+ VkClearValue clear_values[2];
+ memset(&clear_values, 0, sizeof(VkClearValue) * 2);
+ clear_values[0].color.float32[0] = renderpass->clear_colour.x;
+ clear_values[0].color.float32[1] = renderpass->clear_colour.y;
+ clear_values[0].color.float32[2] = renderpass->clear_colour.z;
+ clear_values[0].color.float32[3] = renderpass->clear_colour.w;
+ clear_values[1].depthStencil.depth = renderpass->depth;
+ clear_values[1].depthStencil.stencil = renderpass->stencil;
+
+ begin_info.clearValueCount = 2;
+ begin_info.pClearValues = clear_values;
+
+ vkCmdBeginRenderPass(command_buffer->handle, &begin_info, VK_SUBPASS_CONTENTS_INLINE);
+ command_buffer->state = COMMAND_BUFFER_STATE_IN_RENDER_PASS;
+ }
+
+ void vulkan_renderpass_end(vulkan_command_buffer * command_buffer,
+ vulkan_renderpass * renderpass) {
+ vkCmdEndRenderPass(command_buffer->handle);
+ command_buffer->state = COMMAND_BUFFER_STATE_RECORDING;
+ }
+
+ bool create_buffers(vulkan_context * context) {
+ VkMemoryPropertyFlagBits mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+
+ const u64 vertex_buffer_size = sizeof(vertex_pos) * 1024 * 1024;
+ if (!vulkan_buffer_create(context, vertex_buffer_size,
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ mem_prop_flags, true, &context->object_vertex_buffer)) {
+ ERROR("couldnt create vertex buffer");
+ return false;
+ }
+
+ context->geometry_vertex_offset = 0;
+
+ const u64 index1_buffer_size = sizeof(u32) * 1024 * 1024;
+ if (!vulkan_buffer_create(context, index1_buffer_size,
+ VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ mem_prop_flags, true, &context->object_index_buffer)) {
+ ERROR("couldnt create vertex buffer");
+ return false;
+ }
+ context->geometry_index_offset = 0;
+
+ return true;
+ }
+
+ void create_command_buffers(renderer * ren) {
+ if (!context.gfx_command_buffers) {
+ context.gfx_command_buffers = vulkan_command_buffer_darray_new(context.swapchain.image_count);
+ }
+
+ for (u32 i = 0; i < context.swapchain.image_count; i++) {
+ vulkan_command_buffer_allocate(&context, context.device.gfx_command_pool, true,
+ &context.gfx_command_buffers->data[i]);
+ }
+ }
+
+ void upload_data_range(vulkan_context * context, VkCommandPool pool, VkFence fence, VkQueue queue,
+ vulkan_buffer * buffer, u64 offset, u64 size, void* data) {
+ VkBufferUsageFlags flags =
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ vulkan_buffer staging;
+ vulkan_buffer_create(context, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, flags, true, &staging);
+ // load data into staging buffer
+ printf("Size: %ld\n", size);
+ vulkan_buffer_load_data(context, &staging, 0, size, 0, data);
+
+ // copy
+ vulkan_buffer_copy_to(context, pool, fence, queue, staging.handle, 0, buffer->handle, offset,
+ size);
+
+ vkDestroyBuffer(context->device.logical_device, staging.handle, context->allocator);
+ }
+
+ void regenerate_framebuffers(renderer * ren, vulkan_swapchain * swapchain,
+ vulkan_renderpass * renderpass) {
+ for (u32 i = 0; i < swapchain->image_count; i++) {
+ u32 attachment_count = 2; // one for depth, one for colour
+
+ VkImageView attachments[2] = { swapchain->views[i], swapchain->depth_attachment.view };
+
+ vulkan_framebuffer_create(&context, renderpass, context.framebuffer_width,
+ context.framebuffer_height, 2, attachments,
+ &swapchain->framebuffers->data[i]);
+ }
+ }
+
+ void vulkan_fence_create(vulkan_context * context, bool create_signaled,
+ vulkan_fence* out_fence) {
+ out_fence->is_signaled = create_signaled;
+ VkFenceCreateInfo fence_create_info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
+ if (out_fence->is_signaled) {
+ fence_create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+ }
+
+ vkCreateFence(context->device.logical_device, &fence_create_info, context->allocator,
+ &out_fence->handle);
+ }
+
+ // TODO: vulkan_fence_destroy
+
+ bool vulkan_fence_wait(vulkan_context * context, vulkan_fence * fence, u64 timeout_ns) {
+ if (!fence->is_signaled) {
+ VkResult result =
+ vkWaitForFences(context->device.logical_device, 1, &fence->handle, true, timeout_ns);
+ switch (result) {
+ case VK_SUCCESS:
+ fence->is_signaled = true;
+ return true;
+ case VK_TIMEOUT:
+ WARN("vk_fence_wait - Timed out");
+ break;
+ default:
+ ERROR("vk_fence_wait - Unhanlded error type");
+ break;
+ }
+ } else {
+ return true;
+ }
+
+ return false;
+ }
+ void vulkan_fence_reset(vulkan_context * context, vulkan_fence * fence) {
+ if (fence->is_signaled) {
+ vkResetFences(context->device.logical_device, 1, &fence->handle);
+ fence->is_signaled = false;
+ }
+ }
+
+ bool gfx_backend_init(renderer * ren) {
+ INFO("loading Vulkan backend");
+
+ vulkan_state* internal = malloc(sizeof(vulkan_state));
+ ren->backend_state = (void*)internal;
+
+ context.allocator = 0; // TODO: custom allocator
+
+ context.framebuffer_width = SCR_WIDTH;
+ context.framebuffer_height = SCR_HEIGHT;
+
+ // Setup Vulkan instance
+ VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
+ app_info.apiVersion = VK_API_VERSION_1_3;
+ app_info.pApplicationName = ren->config.window_name;
+ app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
+ app_info.pEngineName = "Celeritas Engine";
+ app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
+
+ VkInstanceCreateInfo create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
+ create_info.pApplicationInfo = &app_info;
+
+ cstr_darray* required_extensions = cstr_darray_new(2);
+ cstr_darray_push(required_extensions, VK_KHR_SURFACE_EXTENSION_NAME);
+
+ plat_get_required_extension_names(required_extensions);
+
+#if defined(CDEBUG)
+ cstr_darray_push(required_extensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+
+ DEBUG("Required extensions:");
+ for (u32 i = 0; i < cstr_darray_len(required_extensions); i++) {
+ DEBUG(" %s", required_extensions->data[i]);
+ }
+#endif
+
+ create_info.enabledExtensionCount = cstr_darray_len(required_extensions);
+ create_info.ppEnabledExtensionNames = required_extensions->data;
+
+ // Validation layers
+ create_info.enabledLayerCount = 0;
+ create_info.ppEnabledLayerNames = 0;
+#if defined(CDEBUG)
+ INFO("Validation layers enabled");
+ cstr_darray* desired_validation_layers = cstr_darray_new(1);
+ cstr_darray_push(desired_validation_layers, "VK_LAYER_KHRONOS_validation");
+
+ u32 n_available_layers = 0;
+ VK_CHECK(vkEnumerateInstanceLayerProperties(&n_available_layers, 0));
+ TRACE("%d available layers", n_available_layers);
+ VkLayerProperties_darray* available_layers = VkLayerProperties_darray_new(n_available_layers);
+ VK_CHECK(vkEnumerateInstanceLayerProperties(&n_available_layers, available_layers->data));
+
+ for (int i = 0; i < cstr_darray_len(desired_validation_layers); i++) {
+ // look through layers to make sure we can find the ones we want
+ bool found = false;
+ for (int j = 0; j < n_available_layers; j++) {
+ if (str8_equals(str8_cstr_view(desired_validation_layers->data[i]),
+ str8_cstr_view(available_layers->data[j].layerName))) {
+ found = true;
+ TRACE("Found layer %s", desired_validation_layers->data[i]);
+ break;
+ }
+ }
+
+ if (!found) {
+ FATAL("Required validation is missing %s", desired_validation_layers->data[i]);
+ return false;
+ }
+ }
+ INFO("All validation layers are present");
+ create_info.enabledLayerCount = cstr_darray_len(desired_validation_layers);
+ create_info.ppEnabledLayerNames = desired_validation_layers->data;
+#endif
+
+ VkResult result = vkCreateInstance(&create_info, NULL, &context.instance);
+ if (result != VK_SUCCESS) {
+ ERROR("vkCreateInstance failed with result: %u", result);
+ return false;
+ }
+
+ // Debugger
+#if defined(CDEBUG)
+ DEBUG("Creating Vulkan debugger")
+ u32 log_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
+ VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {
+ VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT
+ };
+ debug_create_info.messageSeverity = log_severity;
+ debug_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
+ debug_create_info.pfnUserCallback = vk_debug_callback;
+
+ PFN_vkCreateDebugUtilsMessengerEXT func =
+ (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context.instance,
+ "vkCreateDebugUtilsMessengerEXT");
+ assert(func);
+ VK_CHECK(func(context.instance, &debug_create_info, context.allocator, &context.vk_debugger));
+ DEBUG("Vulkan debugger created");
+
+#endif
+
+ // Surface creation
+ DEBUG("Create SurfaceKHR")
+ VkSurfaceKHR surface;
+ VK_CHECK(glfwCreateWindowSurface(context.instance, ren->window, NULL, &surface));
+ context.surface = surface;
+ DEBUG("Vulkan surface created")
+
+ // Device creation
+ if (!vulkan_device_create(&context)) {
+ FATAL("device creation failed");
+ return false;
+ }
+
+ // Swapchain creation
+ vulkan_swapchain_create(&context, SCR_WIDTH, SCR_HEIGHT, &context.swapchain);
+
+ // Renderpass creation
+ vulkan_renderpass_create(&context, &context.main_renderpass,
+ vec4(0, 0, context.framebuffer_width, context.framebuffer_height),
+ rgba_to_vec4(COLOUR_SEA_GREEN), 1.0, 0);
+
+ // Framebiffers creation
+ context.swapchain.framebuffers = vulkan_framebuffer_darray_new(context.swapchain.image_count);
+ regenerate_framebuffers(ren, &context.swapchain, &context.main_renderpass);
+ INFO("Framebuffers created");
+
+ // Command buffers creation
+ create_command_buffers(ren);
+ INFO("Command buffers created");
+
+ // Sync objects
+ context.image_available_semaphores =
+ calloc(context.swapchain.max_frames_in_flight, sizeof(VkSemaphore));
+ context.queue_complete_semaphores =
+ calloc(context.swapchain.max_frames_in_flight, sizeof(VkSemaphore));
+ context.in_flight_fences = calloc(context.swapchain.max_frames_in_flight, sizeof(vulkan_fence));
+
+ for (u8 i = 0; i < context.swapchain.max_frames_in_flight; i++) {
+ VkSemaphoreCreateInfo semaphore_create_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
+ vkCreateSemaphore(context.device.logical_device, &semaphore_create_info, context.allocator,
+ &context.image_available_semaphores[i]);
+ vkCreateSemaphore(context.device.logical_device, &semaphore_create_info, context.allocator,
+ &context.queue_complete_semaphores[i]);
+
+ // create the fence in a signaled state
+ vulkan_fence_create(&context, true, &context.in_flight_fences[i]);
+ }
+
+ context.images_in_flight =
+ malloc(sizeof(vulkan_fence*) * context.swapchain.max_frames_in_flight);
+ for (u8 i = 0; i < context.swapchain.max_frames_in_flight; i++) {
+ context.images_in_flight[i] = 0;
+ }
+ INFO("Sync objects created");
+
+ // Shader modules
+ vulkan_object_shader_create(&context, &context.object_shader);
+ INFO("Compiled shader modules")
+
+ create_buffers(&context);
+ INFO("Created buffers");
+
+ // TODO: temporary test code
+
+ mesh cube = prim_cube_mesh_create();
+
+ vertex* verts = malloc(sizeof(vertex) * cube.vertices->len);
+
+ f32 scale = 3.0;
+ for (size_t i = 0; i < cube.vertices->len; i++) {
+ verts[i].position = vec3_mult(cube.vertices->data[i].position, scale);
+ verts[i].normal = cube.vertices->data[i].normal;
+ verts[i].uv = cube.vertices->data[i].uv;
+ }
+
+ // const f32 s = 1.0;
+ // const u32 vert_count = 4;
+ // vertex_pos verts[4] = { 0 };
+
+ // verts[0].pos.x = -0.5 * s;
+ // verts[0].pos.y = -0.5 * s;
+
+ // verts[1].pos.x = 0.5 * s;
+ // verts[1].pos.y = 0.5 * s;
+
+ // verts[2].pos.x = -0.5 * s;
+ // verts[2].pos.y = 0.5 * s;
+
+ // verts[3].pos.x = 0.5 * s;
+ // verts[3].pos.y = -0.5 * s;
+
+ // const u32 index_count = 6;
+ // u32 indices[6] = { 0, 1, 2, 0, 3, 1 };
+
+ upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue,
+ &context.object_vertex_buffer, 0, sizeof(vertex) * cube.vertices->len, verts);
+ TRACE("Uploaded vertex data");
+ upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue,
+ &context.object_index_buffer, 0, sizeof(u32) * cube.indices_len,
+ cube.indices);
+ TRACE("Uploaded index data");
+ vertex_darray_free(cube.vertices);
+ free(cube.indices);
+
+ // upload texture
+
+ // --- End test code
+
+ INFO("Vulkan renderer initialisation succeeded");
+ return true;
+ }
+
+ void gfx_backend_shutdown(renderer * ren) {
+ DEBUG("Destroying Vulkan debugger");
+ if (context.vk_debugger) {
+ PFN_vkDestroyDebugUtilsMessengerEXT func =
+ (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
+ context.instance, "vkDestroyDebugUtilsMessengerEXT");
+ func(context.instance, context.vk_debugger, context.allocator);
+ }
+
+ DEBUG("Destroying Vulkan instance...");
+ vkDestroyInstance(context.instance, context.allocator);
+ }
+
+ void backend_begin_frame(renderer * ren, f32 delta_time) {
+ vulkan_device* device = &context.device;
+
+ // TODO: resize gubbins
+
+ if (!vulkan_fence_wait(&context, &context.in_flight_fences[context.current_frame],
+ UINT64_MAX)) {
+ WARN("In-flight fence wait failure");
+ }
+
+ if (!vulkan_swapchain_acquire_next_image_index(
+ &context, &context.swapchain, UINT64_MAX,
+ context.image_available_semaphores[context.current_frame], 0, &context.image_index)) {
+ WARN("couldnt acquire swapchain next image");
+ }
+
+ vulkan_command_buffer* command_buffer = &context.gfx_command_buffers->data[context.image_index];
+ vulkan_command_buffer_reset(command_buffer);
+ vulkan_command_buffer_begin(command_buffer, false, false, false);
+
+ VkViewport viewport;
+ viewport.x = 0.0;
+ viewport.y = 0;
+ viewport.width = (f32)context.framebuffer_width;
+ viewport.height = (f32)context.framebuffer_height;
+ viewport.minDepth = 0.0;
+ viewport.maxDepth = 1.0;
+
+ VkRect2D scissor;
+ scissor.offset.x = scissor.offset.y = 0;
+ scissor.extent.width = context.framebuffer_width;
+ scissor.extent.height = context.framebuffer_height;
+
+ vkCmdSetViewport(command_buffer->handle, 0, 1, &viewport);
+ vkCmdSetScissor(command_buffer->handle, 0, 1, &scissor);
+
+ context.main_renderpass.render_area.z = context.framebuffer_width;
+ context.main_renderpass.render_area.w = context.framebuffer_height;
+
+ vulkan_renderpass_begin(command_buffer, &context.main_renderpass,
+ context.swapchain.framebuffers->data[context.image_index].handle);
+ }
+
+ void texture_data_upload(texture * tex) {
+ printf("Texture name %s\n", tex->name);
+ tex->backend_data = malloc(sizeof(vulkan_texture_data));
+ vulkan_texture_data* data = (vulkan_texture_data*)tex->backend_data;
+ printf("Texture (%s) details: \n width %d\n height %d\n channel count %d\n", tex->name,
+ tex->width, tex->height, tex->channel_count);
+ VkDeviceSize image_size = tex->width * tex->height * max(tex->channel_count, 4);
+
+ TRACE("Creating buffer of size %ld", image_size);
+
+ VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB;
+
+ VkBufferUsageFlags usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ VkMemoryPropertyFlags memory_prop_flags =
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ vulkan_buffer staging;
+ vulkan_buffer_create(&context, image_size, usage, memory_prop_flags, true, &staging);
+ DEBUG("Uploading image data");
+ vulkan_buffer_load_data(&context, &staging, 0, image_size, 0, tex->image_data);
+ INFO("Loaded iamge data!");
+
+ vulkan_image_create(
+ &context, VK_IMAGE_TYPE_2D, tex->width, tex->height, image_format, VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_COLOR_BIT, &data->image);
+
+ vulkan_command_buffer temp_buffer;
+ vulkan_command_buffer_allocate_and_begin_oneshot(&context, context.device.gfx_command_pool,
+ &temp_buffer);
+
+ vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+
+ vulkan_image_copy_from_buffer(&data->image, staging.handle, &temp_buffer);
+
+ vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ vulkan_command_buffer_end_oneshot(&context, context.device.gfx_command_pool, &temp_buffer,
+ context.device.graphics_queue);
+
+ VkSamplerCreateInfo sampler_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
+ sampler_info.magFilter = VK_FILTER_LINEAR;
+ sampler_info.minFilter = VK_FILTER_LINEAR;
+ sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.anisotropyEnable = VK_TRUE;
+ sampler_info.maxAnisotropy = 16;
+ sampler_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
+ sampler_info.unnormalizedCoordinates = VK_FALSE;
+ sampler_info.compareEnable = VK_FALSE;
+ sampler_info.compareOp = VK_COMPARE_OP_ALWAYS;
+ sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ sampler_info.mipLodBias = 0.0;
+ sampler_info.minLod = 0.0;
+ sampler_info.maxLod = 0.0;
+
+ VkResult res = vkCreateSampler(context.device.logical_device, &sampler_info, context.allocator,
+ &data->sampler);
+ if (res != VK_SUCCESS) {
+ ERROR("Error creating texture sampler for image %s", tex->name);
+ return;
+ }
+
+ tex->image_data = (void*)data;
+ }
+
+ // TODO: destroy texture
+
+ void backend_end_frame(renderer * ren, f32 delta_time) {
+ vulkan_command_buffer* command_buffer = &context.gfx_command_buffers->data[context.image_index];
+
+ vulkan_renderpass_end(command_buffer, &context.main_renderpass);
+
+ vulkan_command_buffer_end(command_buffer);
+
+ // TODO: wait on fence - https://youtu.be/hRL71D1f3pU?si=nLJx-ZsemDBeQiQ1&t=1037
+
+ context.images_in_flight[context.image_index] =
+ &context.in_flight_fences[context.current_frame];
+
+ vulkan_fence_reset(&context, &context.in_flight_fences[context.current_frame]);
+
+ VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &command_buffer->handle;
+ submit_info.signalSemaphoreCount = 1;
+ submit_info.pSignalSemaphores = &context.queue_complete_semaphores[context.current_frame];
+ submit_info.waitSemaphoreCount = 1;
+ submit_info.pWaitSemaphores = &context.image_available_semaphores[context.current_frame];
+
+ VkPipelineStageFlags flags[1] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
+ submit_info.pWaitDstStageMask = flags;
+
+ VkResult result = vkQueueSubmit(context.device.graphics_queue, 1, &submit_info,
+ context.in_flight_fences[context.current_frame].handle);
+
+ if (result != VK_SUCCESS) {
+ ERROR("queue submission failed. fark.");
+ }
+
+ vulkan_command_buffer_update_submitted(command_buffer);
+
+ vulkan_swapchain_present(
+ &context, &context.swapchain, context.device.graphics_queue, context.device.graphics_queue,
+ context.queue_complete_semaphores[context.current_frame], context.image_index);
+ }
+
+ void gfx_backend_draw_frame(renderer * ren, camera * cam, mat4 model, texture * tex) {
+ backend_begin_frame(ren, 16.0);
+
+ mat4 proj;
+ mat4 view;
+
+ camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj);
+
+ context.object_shader.texture_data = (vulkan_texture_data*)tex->image_data;
+ gfx_backend_update_global_state(proj, view, cam->position, vec4(1.0, 1.0, 1.0, 1.0), 0);
+
+ vulkan_object_shader_update_object(&context, &context.object_shader, model);
+
+ backend_end_frame(ren, 16.0);
+ }
+
+ void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos,
+ vec4 ambient_colour, i32 mode) {
+ vulkan_object_shader_use(&context, &context.object_shader);
+
+ vulkan_object_shader_update_global_state(&context, &context.object_shader);
+ context.object_shader.global_ubo.projection = projection;
+ context.object_shader.global_ubo.view = view;
+ // TODO: other UBO properties
+ }
+
+ void clear_screen(vec3 colour) {}
+
+ void bind_texture(shader s, texture * tex, u32 slot) {}
+ void bind_mesh_vertex_buffer(void* backend, mesh* mesh) {}
+ void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count) {}
+
+ shader shader_create_separate(const char* vert_shader, const char* frag_shader) {}
+ void set_shader(shader s) {}
+
+ void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value) {}
+ void uniform_f32(u32 program_id, const char* uniform_name, f32 value) {}
+ void uniform_i32(u32 program_id, const char* uniform_name, i32 value) {}
+ void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value) {}
+
+ VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags,
+ const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) {
+ switch (severity) {
+ default:
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
+ ERROR("%s", callback_data->pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
+ WARN("%s", callback_data->pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
+ INFO("%s", callback_data->pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
+ TRACE("%s", callback_data->pMessage);
+ break;
+ }
+ return VK_FALSE;
+ }
+
+#endif \ No newline at end of file
diff --git a/src/renderer/archive/render.c b/src/renderer/archive/render.c
new file mode 100644
index 0000000..b1e2a46
--- /dev/null
+++ b/src/renderer/archive/render.c
@@ -0,0 +1,413 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "animation.h"
+#include "maths_types.h"
+#include "mem.h"
+#include "transform_hierarchy.h"
+#define STB_IMAGE_IMPLEMENTATION
+#include <stb_image.h>
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#include <stb_truetype.h>
+
+#include "render.h"
+#include "render_types.h"
+
+#include <glad/glad.h>
+#include <glfw3.h>
+
+#include "defines.h"
+#include "log.h"
+#include "maths.h"
+#include "render_backend.h"
+
+// FIXME: get rid of these and store dynamic screen realestate
+// in renderer
+#define SCR_WIDTH 1000
+#define SCR_HEIGHT 1000
+
+material DEFAULT_MATERIAL = { 0 };
+
+bool renderer_init(renderer* ren) {
+ INFO("Renderer init");
+
+ // NOTE: all platforms use GLFW at the moment but thats subject to change
+ glfwInit();
+
+#if defined(CEL_REND_BACKEND_OPENGL)
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+#elif defined(CEL_REND_BACKEND_VULKAN)
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+#endif
+
+ // glfw window creation
+ GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height,
+ ren->config.window_name, NULL, NULL);
+ if (window == NULL) {
+ ERROR("Failed to create GLFW window\n");
+ glfwTerminate();
+ return false;
+ }
+ ren->window = window;
+
+ glfwMakeContextCurrent(ren->window);
+
+ DEBUG("init graphics api backend");
+ if (!gfx_backend_init(ren)) {
+ FATAL("Couldnt load graphics api backend");
+ return false;
+ }
+
+ ren->blinn_phong =
+ shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag");
+
+ ren->skinned =
+ shader_create_separate("assets/shaders/skinned.vert", "assets/shaders/blinn_phong.frag");
+
+ default_material_init();
+
+ return true;
+}
+
+void renderer_shutdown(renderer* ren) {}
+
+void render_frame_begin(renderer* ren) {
+ vec3 color = ren->config.clear_colour;
+ clear_screen(color);
+}
+void render_frame_end(renderer* ren) {
+ // present frame
+ glfwSwapBuffers(ren->window);
+ glfwPollEvents();
+}
+
+void default_material_init() {
+ INFO("Load default material")
+ DEFAULT_MATERIAL.ambient_colour = (vec3){ 0.5, 0.5, 0.5 };
+ DEFAULT_MATERIAL.diffuse = (vec3){ 0.8, 0.8, 0.8 };
+ DEFAULT_MATERIAL.specular = (vec3){ 1.0, 1.0, 1.0 };
+ DEFAULT_MATERIAL.diffuse_texture = texture_data_load("assets/textures/white1x1.png", false);
+ DEFAULT_MATERIAL.specular_texture = texture_data_load("assets/textures/black1x1.png", false);
+ DEFAULT_MATERIAL.spec_exponent = 32.0;
+ strcpy(DEFAULT_MATERIAL.name, "Default");
+ texture_data_upload(&DEFAULT_MATERIAL.diffuse_texture);
+ texture_data_upload(&DEFAULT_MATERIAL.specular_texture);
+}
+
+void model_destroy(model* model) {
+ TRACE("Freeing all data for model %s", model->name);
+ arena_free_all(&model->animation_data_arena);
+ arena_free_storage(&model->animation_data_arena);
+ mesh_darray_free(model->meshes);
+ material_darray_free(model->materials);
+ if (model->is_uploaded) {
+ // Delete gpu buffer data
+ for (u32 i = 0; i < mesh_darray_len(model->meshes); i++) {
+ // FIXME: dont leak Opengl
+ glDeleteBuffers(1, &model->meshes->data[i].vbo);
+ glDeleteVertexArrays(1, &model->meshes->data[i].vao);
+ }
+ }
+}
+
+typedef struct draw_ctx {
+ model_darray* models;
+ renderer* ren;
+ camera* cam;
+ scene* scene;
+} draw_ctx;
+bool draw_scene_node(transform_node* node, void* ctx_data) {
+ if (!node || !node->parent) return true;
+ draw_ctx* ctx = ctx_data;
+ model* m = &ctx->models->data[node->model.raw];
+ draw_model(ctx->ren, ctx->cam, m, &node->world_matrix_tf, ctx->scene);
+ return true;
+}
+
+void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera,
+ transform_hierarchy* tfh, scene* scene) {
+ draw_ctx* ctx = arena_alloc(frame, sizeof(draw_ctx));
+ ctx->models = models;
+ ctx->ren = ren;
+ ctx->cam = camera;
+ ctx->scene = scene;
+ transform_hierarchy_dfs(transform_hierarchy_root_node(tfh), draw_scene_node, true, ctx);
+}
+
+void draw_model(renderer* ren, camera* camera, model* model, mat4* model_tf, scene* scene) {
+ // TRACE("Drawing model: %s", model->name);
+ mat4 view;
+ mat4 proj;
+ camera_view_projection(camera, SCR_HEIGHT, SCR_WIDTH, &view, &proj);
+
+ set_shader(ren->blinn_phong);
+
+ // set camera uniform
+ uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &camera->position);
+ // set light uniforms
+ dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light);
+ for (int i = 0; i < scene->n_point_lights; i++) {
+ point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i);
+ }
+
+ for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) {
+ mesh* m = &model->meshes->data[i];
+ if (vertex_darray_len(m->vertices) == 0) {
+ continue;
+ }
+ // TRACE("Drawing mesh %d", i);
+ material* mat = &model->materials->data[m->material_index];
+ draw_mesh(ren, m, model_tf, mat, &view, &proj);
+ }
+}
+
+void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* view, mat4* proj) {
+ shader lighting_shader = ren->blinn_phong;
+
+ // bind buffer
+ bind_mesh_vertex_buffer(ren->backend_state, mesh);
+
+ // bind textures
+ bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0
+ bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1
+ uniform_f32(lighting_shader.program_id, "material.shininess", 32.);
+
+ // upload model, view, and projection matrices
+ uniform_mat4f(lighting_shader.program_id, "model", model_tf);
+ uniform_mat4f(lighting_shader.program_id, "view", view);
+ uniform_mat4f(lighting_shader.program_id, "projection", proj);
+
+ // draw triangles
+ u32 num_vertices = vertex_darray_len(mesh->vertices);
+ draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices);
+}
+
+void draw_skinned_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view,
+ mat4* proj) {
+ shader lighting_shader = ren->skinned;
+
+ // bind buffer
+ bind_mesh_vertex_buffer(ren->backend_state, mesh);
+
+ // bind textures
+ bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0
+ bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1
+
+ // Uniforms
+ uniform_f32(lighting_shader.program_id, "material.shininess", 32.);
+ mat4 trans = mat4_translation(tf.position);
+ mat4 rot = mat4_rotation(tf.rotation);
+ mat4 scale = mat4_scale(tf.scale);
+ mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale));
+ uniform_mat4f(lighting_shader.program_id, "model", &model_tf);
+ uniform_mat4f(lighting_shader.program_id, "view", view);
+ uniform_mat4f(lighting_shader.program_id, "projection", proj);
+
+ // bone transforms
+ size_t n_bones = mesh->bones->len;
+
+ // for now assume correct ordering
+ mat4* bone_transforms = malloc(n_bones * sizeof(mat4));
+ mat4 parent = mat4_ident();
+ for (int bone_i = 0; bone_i < n_bones; bone_i++) {
+ joint j = mesh->bones->data[bone_i];
+ transform tf = mesh->bones->data[bone_i].transform_components;
+ tf.position.y = -tf.position.y;
+ mat4 local = transform_to_mat(&tf);
+ mat4 inverse = j.inverse_bind_matrix;
+ inverse.data[13] = -inverse.data[13];
+ mat4 intemediate = mat4_mult(local, inverse);
+
+ bone_transforms[bone_i] = intemediate;
+ parent = bone_transforms[bone_i];
+ }
+
+ // premultiply the inverses
+ // for (int bone_i = 0; bone_i < n_bones; bone_i++) {
+ // joint j = mesh->bones->data[bone_i];
+ // // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix);
+ // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix);
+ // }
+
+ glUniformMatrix4fv(glGetUniformLocation(lighting_shader.program_id, "boneMatrices"), n_bones,
+ GL_FALSE, &bone_transforms->data[0]);
+
+ free(bone_transforms);
+
+ // draw triangles
+ u32 num_vertices = vertex_darray_len(mesh->vertices);
+ draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices);
+}
+
+void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene) {
+ mat4 view;
+ mat4 proj;
+ camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj);
+
+ set_shader(ren->skinned);
+
+ // set camera uniform
+ uniform_vec3f(ren->skinned.program_id, "viewPos", &cam->position);
+ // set light uniforms
+ dir_light_upload_uniforms(ren->skinned, &scene->dir_light);
+ for (int i = 0; i < scene->n_point_lights; i++) {
+ point_light_upload_uniforms(ren->skinned, &scene->point_lights[i], '0' + i);
+ }
+
+ for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) {
+ mesh* m = &model->meshes->data[i];
+ if (vertex_darray_len(m->vertices) == 0) {
+ continue;
+ }
+ // material* mat = &model->materials->data[m->material_index];
+ material* mat = &DEFAULT_MATERIAL;
+ draw_skinned_mesh(ren, m, tf, mat, &view, &proj);
+ }
+}
+
+void model_upload_meshes(renderer* ren, model* model) {
+ INFO("Upload mesh vertex data to GPU for model %s", model->name);
+
+ size_t num_meshes = mesh_darray_len(model->meshes);
+ u32 VBOs[num_meshes];
+ u32 VAOs[num_meshes];
+ glGenBuffers(num_meshes, VBOs);
+ glGenVertexArrays(num_meshes, VAOs);
+
+ u64 total_verts = 0;
+
+ TRACE("num meshes %d", num_meshes);
+
+ // upload each mesh to the GPU
+ for (int mesh_i = 0; mesh_i < num_meshes; mesh_i++) {
+ mesh mesh = model->meshes->data[mesh_i];
+ model->meshes->data[mesh_i].vao = VAOs[mesh_i];
+ model->meshes->data[mesh_i].vbo = VBOs[mesh_i];
+ // 3. bind buffers
+ glBindBuffer(GL_ARRAY_BUFFER, VBOs[mesh_i]);
+
+ size_t num_vertices = vertex_darray_len(model->meshes->data[mesh_i].vertices);
+ // TRACE("Uploading vertex array data: %d verts", num_vertices);
+ total_verts += num_vertices;
+
+ size_t static_vertex_size = 2 * sizeof(vec3) + sizeof(vec2);
+ size_t skinned_vertex_size = 2 * sizeof(vec3) + sizeof(vec2) + 4 * sizeof(u32) + sizeof(vec4);
+ size_t vertex_size = mesh.is_skinned ? skinned_vertex_size : static_vertex_size;
+
+ // TRACE("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size);
+ if (mesh.is_skinned) {
+ assert(vertex_size == (12 + 12 + 8 + 16 + 16));
+ } else {
+ assert(vertex_size == sizeof(vertex));
+ assert(vertex_size == 8 * sizeof(float));
+ }
+
+ size_t buffer_size = vertex_size * num_vertices;
+ u8* bytes = malloc(buffer_size);
+
+ for (int i = 0; i < num_vertices; i++) {
+ u8* p = bytes + vertex_size * i;
+ memcpy(p, &mesh.vertices->data[i], static_vertex_size);
+ if (mesh.is_skinned) {
+ u8* bone_data_offset = p + static_vertex_size;
+ memcpy(bone_data_offset, &mesh.vertex_bone_data->data[i], sizeof(vertex_bone_data));
+ }
+ }
+
+ // 4. upload data
+ glBufferData(GL_ARRAY_BUFFER, buffer_size, bytes, GL_STATIC_DRAW);
+
+ // 5. cont. set mesh vertex layout
+ glBindVertexArray(model->meshes->data[mesh_i].vao);
+ // position attribute
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)0);
+ glEnableVertexAttribArray(0);
+ // normal vector attribute
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)(3 * sizeof(float)));
+ glEnableVertexAttribArray(1);
+ // tex coords
+ glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, vertex_size, (void*)(6 * sizeof(float)));
+ glEnableVertexAttribArray(2);
+
+ // skinning (optional)
+ if (mesh.is_skinned) {
+ glEnableVertexAttribArray(3);
+ glVertexAttribIPointer(3, 4, GL_INT, vertex_size, (void*)(8 * sizeof(float)));
+
+ glEnableVertexAttribArray(4);
+ glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, vertex_size, (void*)(12 * sizeof(float)));
+ }
+ }
+
+ INFO("Uploaded %d submeshes with a total of %d vertices\n", num_meshes, total_verts);
+
+ // 6. reset buffer
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+texture texture_data_load(const char* path, bool invert_y) {
+ TRACE("Load texture %s", path);
+
+ // load the file data
+ // texture loading
+ int width, height, num_channels;
+ stbi_set_flip_vertically_on_load(invert_y);
+
+#pragma GCC diagnostic ignored "-Wpointer-sign"
+ char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha);
+ if (data) {
+ DEBUG("loaded texture: %s", path);
+ } else {
+ WARN("failed to load texture");
+ }
+
+ unsigned int channel_type;
+ if (num_channels == 4) {
+ channel_type = GL_RGBA;
+ } else {
+ channel_type = GL_RGB;
+ }
+
+ return (texture){ .texture_id = 0,
+ .width = width,
+ .height = height,
+ .channel_count = num_channels,
+ .channel_type = channel_type,
+ .name = "TODO: Texture names",
+ .image_data = data };
+}
+
+void dir_light_upload_uniforms(shader shader, directional_light* light) {
+ uniform_vec3f(shader.program_id, "dirLight.direction", &light->direction);
+ uniform_vec3f(shader.program_id, "dirLight.ambient", &light->ambient);
+ uniform_vec3f(shader.program_id, "dirLight.diffuse", &light->diffuse);
+ uniform_vec3f(shader.program_id, "dirLight.specular", &light->specular);
+}
+
+void point_light_upload_uniforms(shader shader, point_light* light, char index) {
+ char position_str[] = "pointLights[x].position";
+ position_str[12] = (char)index;
+ char ambient_str[] = "pointLights[x].ambient";
+ ambient_str[12] = (char)index;
+ char diffuse_str[] = "pointLights[x].diffuse";
+ diffuse_str[12] = (char)index;
+ char specular_str[] = "pointLights[x].specular";
+ specular_str[12] = (char)index;
+ char constant_str[] = "pointLights[x].constant";
+ constant_str[12] = (char)index;
+ char linear_str[] = "pointLights[x].linear";
+ linear_str[12] = (char)index;
+ char quadratic_str[] = "pointLights[x].quadratic";
+ quadratic_str[12] = (char)index;
+ uniform_vec3f(shader.program_id, position_str, &light->position);
+ uniform_vec3f(shader.program_id, ambient_str, &light->ambient);
+ uniform_vec3f(shader.program_id, diffuse_str, &light->diffuse);
+ uniform_vec3f(shader.program_id, specular_str, &light->specular);
+ uniform_f32(shader.program_id, constant_str, light->constant);
+ uniform_f32(shader.program_id, linear_str, light->linear);
+ uniform_f32(shader.program_id, quadratic_str, light->quadratic);
+} \ No newline at end of file
diff --git a/src/renderer/archive/render.h b/src/renderer/archive/render.h
new file mode 100644
index 0000000..31cf3b0
--- /dev/null
+++ b/src/renderer/archive/render.h
@@ -0,0 +1,46 @@
+/**
+ * @file render.h
+ * @author your name (you@domain.com)
+ * @brief Renderer frontend
+ * @version 0.1
+ * @date 2024-03-21
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+
+#include "camera.h"
+#include "loaders.h"
+#include "render_types.h"
+#include "transform_hierarchy.h"
+
+// --- Lifecycle
+/** @brief initialise the render system frontend */
+bool renderer_init(renderer* ren);
+/** @brief shutdown the render system frontend */
+void renderer_shutdown(renderer* ren);
+
+void renderer_on_resize(renderer* ren);
+
+struct render_packet;
+
+// --- Frame
+
+void render_frame_begin(renderer* ren);
+void render_frame_end(renderer* ren);
+void render_frame_draw(renderer* ren);
+
+// --- models meshes
+void model_upload_meshes(renderer* ren, model* model);
+void draw_model(renderer* ren, camera* camera, model* model, mat4* tf, scene* scene);
+void draw_mesh(renderer* ren, mesh* mesh, mat4* tf, material* mat, mat4* view, mat4* proj);
+void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera,
+ transform_hierarchy* tfh, scene* scene);
+
+void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene);
+
+void model_destroy(model* model);
+
+// ---
+texture texture_data_load(const char* path, bool invert_y); // #frontend
diff --git a/src/renderer/archive/render_backend.h b/src/renderer/archive/render_backend.h
new file mode 100644
index 0000000..da30bcc
--- /dev/null
+++ b/src/renderer/archive/render_backend.h
@@ -0,0 +1,39 @@
+/**
+ * @brief Renderer backend
+ */
+#pragma once
+
+#include "camera.h"
+#include "maths_types.h"
+#include "render_types.h"
+
+/// --- Lifecycle
+
+/** @brief Initialise the graphics API backend */
+bool gfx_backend_init(renderer* ren);
+void gfx_backend_shutdown(renderer* ren);
+
+void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex);
+void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, vec4 ambient_colour,
+ i32 mode);
+
+void clear_screen(vec3 colour);
+
+void texture_data_upload(texture* tex);
+void bind_texture(shader s, texture* tex, u32 slot);
+void bind_mesh_vertex_buffer(void* backend, mesh* mesh);
+void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count);
+
+shader shader_create_separate(const char* vert_shader, const char* frag_shader);
+void set_shader(shader s);
+
+// --- Uniforms
+
+/** @brief upload a vec3 of f32 to a uniform */
+void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value);
+/** @brief upload a single f32 to a uniform */
+void uniform_f32(u32 program_id, const char* uniform_name, f32 value);
+/** @brief upload a integer to a uniform */
+void uniform_i32(u32 program_id, const char* uniform_name, i32 value);
+/** @brief upload a mat4 of f32 to a uniform */
+void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value); \ No newline at end of file
diff --git a/src/renderer/archive/render_types.h b/src/renderer/archive/render_types.h
new file mode 100644
index 0000000..f5ea986
--- /dev/null
+++ b/src/renderer/archive/render_types.h
@@ -0,0 +1,210 @@
+// /**
+// * @file render_types.h
+// * @author Omniscient
+// * @brief Type definitions for the majority of data required by the renderer system
+// * @date 2024-02-24
+// *
+// */
+// #pragma once
+
+#include "animation.h"
+#include "darray.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "str.h"
+
+struct GLFWwindow;
+
+#define MAX_MATERIAL_NAME_LEN 256
+#define MAX_TEXTURE_NAME_LEN 256
+
+// #ifndef RESOURCE_HANDLE_DEFS
+// // CORE_DEFINE_HANDLE(model_handle);
+// #define ABSENT_MODEL_HANDLE 999999999
+// // CORE_DEFINE_HANDLE(texture_handle);
+// #define RESOURCE_HANDLE_DEFS
+// #endif
+
+/* @brief Opaque wrapper around a shader program */
+typedef struct shader {
+ u32 program_id;
+} shader;
+
+/** @brief configuration passed to the renderer at init time */
+typedef struct renderer_config {
+ char window_name[256];
+ u32 scr_width, scr_height;
+ vec3 clear_colour; /** colour that the screen gets cleared to every frame */
+} renderer_config;
+
+// typedef struct frame_stats {
+// u64 last_time;
+// } frame_stats;
+
+typedef struct renderer {
+ struct GLFWwindow *window; /** Currently all platforms use GLFW*/
+ void *backend_state; /** Graphics API-specific state */
+ renderer_config config;
+ // shaders
+ shader blinn_phong;
+ shader skinned;
+} renderer;
+
+// // --- Lighting & Materials
+
+typedef struct texture {
+ u32 texture_id;
+ char name[MAX_TEXTURE_NAME_LEN];
+ void *image_data;
+ void *backend_data;
+ u32 width;
+ u32 height;
+ u8 channel_count;
+ u32 channel_type;
+} texture;
+
+typedef struct blinn_phong_material {
+ char name[MAX_MATERIAL_NAME_LEN];
+ texture diffuse_texture;
+ char diffuse_tex_path[256];
+ texture specular_texture;
+ char specular_tex_path[256];
+ vec3 ambient_colour;
+ vec3 diffuse;
+ vec3 specular;
+ f32 spec_exponent;
+ bool is_loaded;
+ bool is_uploaded;
+} blinn_phong_material;
+typedef blinn_phong_material material; // when we start using PBR, this will no longer be the
+// case
+
+// // the default blinn-phong material. MUST be initialised with the function below
+// extern material DEFAULT_MATERIAL;
+// void default_material_init();
+
+#ifndef TYPED_MATERIAL_ARRAY
+KITC_DECL_TYPED_ARRAY(material) // creates "material_darray"
+#define TYPED_MATERIAL_ARRAY
+#endif
+
+#ifndef TYPED_ANIMATION_CLIP_ARRAY
+KITC_DECL_TYPED_ARRAY(animation_clip) // creates "material_darray"
+#define TYPED_ANIMATION_CLIP_ARRAY
+#endif
+
+// // lights
+typedef struct point_light {
+ vec3 position;
+ f32 constant, linear, quadratic;
+ vec3 ambient;
+ vec3 diffuse;
+ vec3 specular;
+} point_light;
+
+typedef struct directional_light {
+ vec3 direction;
+ vec3 ambient;
+ vec3 diffuse;
+ vec3 specular;
+} directional_light;
+
+// void point_light_upload_uniforms(shader shader, point_light *light, char index);
+// void dir_light_upload_uniforms(shader shader, directional_light *light);
+
+// // --- Models & Meshes
+
+// /** @brief Vertex format for a static mesh */
+typedef struct vertex {
+ vec3 position;
+ vec3 normal;
+ vec2 uv;
+} vertex;
+
+typedef struct vertex_bone_data {
+ vec4u joints; /** @brief 4 indices of joints that influence vectors position */
+ vec4 weights; /** @brief weight (0,1) of each joint */
+} vertex_bone_data;
+
+#include "animation.h"
+#ifndef TYPED_VERTEX_ARRAY
+KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray"
+KITC_DECL_TYPED_ARRAY(vertex_bone_data) // creates "skinned_vertex_darray"
+KITC_DECL_TYPED_ARRAY(joint)
+#define TYPED_VERTEX_ARRAY
+#endif
+
+typedef struct mesh {
+ vertex_darray *vertices;
+ vertex_bone_data_darray *vertex_bone_data; // only used if model needs it
+ joint_darray *bones;
+ bool is_skinned;
+ u32 vertex_size; /** size in bytes of each vertex including necessary padding */
+ bool has_indices;
+ u32 *indices;
+ u32 indices_len;
+ size_t material_index;
+ u32 vbo, vao; /** OpenGL data. TODO: dont leak OpenGL details */
+} mesh;
+
+// #ifndef TYPED_MESH_ARRAY
+// KITC_DECL_TYPED_ARRAY(mesh) // creates "mesh_darray"
+// #define TYPED_MESH_ARRAY
+// #endif
+
+typedef struct model {
+ str8 name;
+ mesh_darray *meshes;
+ aabb_3d bbox;
+ material_darray *materials;
+ animation_clip_darray *animations;
+ arena animation_data_arena;
+ bool is_loaded;
+ bool is_uploaded;
+} model;
+
+// #ifndef TYPED_MODEL_ARRAY
+// KITC_DECL_TYPED_ARRAY(model) // creates "model_darray"
+// #define TYPED_MODEL_ARRAY
+// #endif
+
+// // --- Scene
+
+// // NOTE: This struct won't stay like this for a long time. It's somewhat temporary
+// // in order to get a basic scene working without putting burden on the caller of
+// // draw_model()
+// typedef struct scene {
+// directional_light dir_light;
+// point_light point_lights[4];
+// size_t n_point_lights;
+// } scene;
+
+// // --- Graphics API related
+
+// // typedef enum cel_primitive_topology {
+// // CEL_PRIMITIVE_TOPOLOGY_POINT,
+// // CEL_PRIMITIVE_TOPOLOGY_LINE,
+// // CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP,
+// // CEL_PRIMITIVE_TOPOLOGY_TRIANGLE,
+// // CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
+// // CEL_PRIMITIVE_TOPOLOGY_COUNT
+// // } cel_primitive_topology;
+
+// // typedef enum gpu_texture_type {
+// // TEXTURE_TYPE_2D,
+// // TEXTURE_TYPE_3D,
+// // TEXTURE_TYPE_2D_ARRAY,
+// // TEXTURE_TYPE_CUBE_MAP,
+// // TEXTURE_TYPE_COUNT
+// // } gpu_texture_type;
+
+// // typedef enum gpu_texture_format {
+// // TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM,
+// // TEXTURE_FORMAT_DEPTH_DEFAULT,
+// // TEXTURE_FORMAT_COUNT
+// // } gpu_texture_format;
+
+// // typedef enum pipeline_kind {
+// // GRAPHICS,
+// // COMPUTE,
+// // } pipeline_kind;
diff --git a/src/renderer/backends/backend_dx11.c b/src/renderer/backends/backend_dx11.c
new file mode 100644
index 0000000..7e48853
--- /dev/null
+++ b/src/renderer/backends/backend_dx11.c
@@ -0,0 +1,5 @@
+#if defined(CEL_REND_BACKEND_DX11)
+#include <d3d11.h>
+#include <d3dcompiler.h>
+
+#endif \ No newline at end of file
diff --git a/src/renderer/backends/backend_dx11.h b/src/renderer/backends/backend_dx11.h
new file mode 100644
index 0000000..53738aa
--- /dev/null
+++ b/src/renderer/backends/backend_dx11.h
@@ -0,0 +1,29 @@
+#pragma once
+#include <d3d11.h>
+#include <d3dcompiler.h>
+
+#include "ral.h"
+
+#define GPU_SWAPCHAIN_IMG_COUNT 2
+
+// typedef struct gpu_swapchain gpu_swapchain;
+typedef struct gpu_device {
+ // VkPhysicalDevice physical_device;
+ // VkDevice logical_device;
+ // VkPhysicalDeviceProperties properties;
+ // VkPhysicalDeviceFeatures features;
+ // VkPhysicalDeviceMemoryProperties memory;
+ // VkCommandPool pool;
+} gpu_device;
+typedef struct gpu_pipeline {
+} gpu_pipeline;
+
+typedef struct gpu_renderpass {
+ // VkRenderPass vk_handle;
+ // VkFramebuffer framebuffers[GPU_SWAPCHAIN_IMG_COUNT];
+ // u32
+} gpu_renderpass;
+
+typedef struct gpu_cmd_encoder {
+ // VkCommandBuffer cmd_buffer;
+} gpu_cmd_encoder; \ No newline at end of file
diff --git a/src/renderer/backends/backend_opengl.c b/src/renderer/backends/backend_opengl.c
index ea6cb00..4cd97b5 100644
--- a/src/renderer/backends/backend_opengl.c
+++ b/src/renderer/backends/backend_opengl.c
@@ -1,11 +1,12 @@
#include <stdlib.h>
+#include "camera.h"
#define CEL_PLATFORM_LINUX
#include "defines.h"
#include "file.h"
#include "log.h"
#include "maths_types.h"
-#include "render_types.h"
+#include "ral.h"
#if CEL_REND_BACKEND_OPENGL
@@ -36,10 +37,13 @@ bool gfx_backend_init(renderer *ren) {
glEnable(GL_DEPTH_TEST);
opengl_state *internal = malloc(sizeof(opengl_state));
- ren->backend_state = (void *)internal;
+ ren->backend_context = (void *)internal;
return true;
}
+
+void gfx_backend_draw_frame(renderer *ren, camera *cam, mat4 model, texture *tex) {}
+
void gfx_backend_shutdown(renderer *ren) {}
void uniform_vec3f(u32 program_id, const char *uniform_name, vec3 *value) {
@@ -60,6 +64,29 @@ void clear_screen(vec3 colour) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
+void texture_data_upload(texture *tex) {
+ printf("Texture name %s\n", tex->name);
+ TRACE("Upload texture data");
+ u32 texture_id;
+ glGenTextures(1, &texture_id);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ tex->texture_id = texture_id;
+
+ // set the texture wrapping parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ // set texture filtering parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex->width, tex->height, 0, tex->channel_type,
+ GL_UNSIGNED_BYTE, tex->image_data);
+ glGenerateMipmap(GL_TEXTURE_2D);
+ DEBUG("Freeing texture image data after uploading to GPU");
+ // stbi_image_free(tex->image_data); // data is on gpu now so we dont need it around
+}
+
void bind_texture(shader s, texture *tex, u32 slot) {
// printf("bind texture slot %d with texture id %d \n", slot, tex->texture_id);
glActiveTexture(GL_TEXTURE0 + slot);
diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c
index 6347e27..ef266e0 100644
--- a/src/renderer/backends/backend_vulkan.c
+++ b/src/renderer/backends/backend_vulkan.c
@@ -1 +1,1689 @@
-// #FUTURE \ No newline at end of file
+#include <assert.h>
+#include <glfw3.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vulkan/vk_platform.h>
+#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_core.h>
+
+#include "backend_vulkan.h"
+#include "buf.h"
+#include "darray.h"
+#include "maths_types.h"
+#include "mem.h"
+#include "ral_types.h"
+#include "str.h"
+#include "vulkan_helpers.h"
+
+#include "defines.h"
+#include "file.h"
+#include "log.h"
+#include "ral.h"
+#include "utils.h"
+
+// TEMP
+#define SCREEN_WIDTH 1000
+#define SCREEN_HEIGHT 1000
+#define VULKAN_QUEUES_COUNT 2
+#define MAX_DESCRIPTOR_SETS 10
+
+const char* queue_names[VULKAN_QUEUES_COUNT] = { "GRAPHICS", "TRANSFER" };
+
+KITC_DECL_TYPED_ARRAY(VkDescriptorSet)
+
+typedef struct vulkan_context {
+ VkInstance instance;
+ VkAllocationCallbacks* allocator;
+ VkSurfaceKHR surface;
+ vulkan_swapchain_support_info swapchain_support;
+
+ arena temp_arena;
+ gpu_device* device;
+ gpu_swapchain* swapchain;
+ u32 framebuffer_count;
+ VkFramebuffer*
+ swapchain_framebuffers; // TODO: Move this data into the swapchain as its own struct
+
+ u32 current_img_index;
+ u32 current_frame; // super important
+ gpu_cmd_encoder main_cmd_bufs[MAX_FRAMES_IN_FLIGHT];
+ VkSemaphore image_available_semaphores[MAX_FRAMES_IN_FLIGHT];
+ VkSemaphore render_finished_semaphores[MAX_FRAMES_IN_FLIGHT];
+ VkFence in_flight_fences[MAX_FRAMES_IN_FLIGHT];
+
+ // HACK
+ VkRenderPass main_renderpass;
+
+ u32 screen_width;
+ u32 screen_height;
+ bool is_resizing;
+ GLFWwindow* window;
+
+ // Storage
+ gpu_buffer buffers[1024];
+ size_t buffer_count;
+ VkDescriptorSet_darray* free_set_queue;
+ struct resource_pools* resource_pools;
+
+ VkDebugUtilsMessengerEXT vk_debugger;
+} vulkan_context;
+
+static vulkan_context context;
+
+// --- Function forward declarations
+
+/** @brief Enumerates and selects the most appropriate graphics device */
+bool select_physical_device(gpu_device* out_device);
+
+bool is_physical_device_suitable(VkPhysicalDevice device);
+
+queue_family_indices find_queue_families(VkPhysicalDevice device);
+
+bool create_logical_device(gpu_device* out_device);
+void create_swapchain_framebuffers();
+void create_sync_objects();
+void create_descriptor_pools();
+size_t vertex_attrib_size(vertex_attrib_type attr);
+
+VkShaderModule create_shader_module(str8 spirv);
+
+/** @brief Helper function for creating array of all extensions we want */
+cstr_darray* get_all_extensions();
+
+VkImage vulkan_image_create(u32x2 dimensions, VkImageType image_type, VkFormat format,
+ VkImageUsageFlags usage);
+void vulkan_transition_image_layout(gpu_texture* texture, VkFormat format, VkImageLayout old_layout,
+ VkImageLayout new_layout);
+
+// --- Handy macros
+#define BUFFER_GET(h) (buffer_pool_get(&context.resource_pools->buffers, h))
+#define TEXTURE_GET(h) (texture_pool_get(&context.resource_pools->textures, h))
+
+bool gpu_backend_init(const char* window_name, GLFWwindow* window) {
+ memset(&context, 0, sizeof(vulkan_context));
+ context.allocator = 0; // TODO: use an allocator
+ context.screen_width = SCREEN_WIDTH;
+ context.screen_height = SCREEN_HEIGHT;
+ context.window = window;
+ context.current_img_index = 0;
+ context.current_frame = 0;
+ context.free_set_queue = VkDescriptorSet_darray_new(100);
+
+ // Create an allocator
+ size_t temp_arena_size = 1024 * 1024;
+ context.temp_arena = arena_create(malloc(temp_arena_size), temp_arena_size);
+
+ // Setup Vulkan instance
+ VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
+ app_info.apiVersion = VK_API_VERSION_1_2;
+ app_info.pApplicationName = window_name;
+ app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
+ app_info.pEngineName = "Celeritas Engine";
+ app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
+
+ VkInstanceCreateInfo create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
+ create_info.pApplicationInfo = &app_info;
+
+ // Extensions
+ cstr_darray* required_extensions = cstr_darray_new(2);
+ // cstr_darray_push(required_extensions, VK_KHR_SURFACE_EXTENSION_NAME);
+
+ uint32_t count;
+ const char** extensions = glfwGetRequiredInstanceExtensions(&count);
+ for (u32 i = 0; i < count; i++) {
+ cstr_darray_push(required_extensions, extensions[i]);
+ }
+
+ cstr_darray_push(required_extensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+
+ DEBUG("Required extensions:");
+ for (u32 i = 0; i < cstr_darray_len(required_extensions); i++) {
+ DEBUG(" %s", required_extensions->data[i]);
+ }
+
+ create_info.enabledExtensionCount = cstr_darray_len(required_extensions);
+ create_info.ppEnabledExtensionNames = required_extensions->data;
+
+ // TODO: Validation layers
+ create_info.enabledLayerCount = 0;
+ create_info.ppEnabledLayerNames = NULL;
+
+ INFO("Validation layers enabled");
+ cstr_darray* desired_validation_layers = cstr_darray_new(1);
+ cstr_darray_push(desired_validation_layers, "VK_LAYER_KHRONOS_validation");
+
+ u32 n_available_layers = 0;
+ VK_CHECK(vkEnumerateInstanceLayerProperties(&n_available_layers, 0));
+ TRACE("%d available layers", n_available_layers);
+ VkLayerProperties* available_layers =
+ arena_alloc(&context.temp_arena, n_available_layers * sizeof(VkLayerProperties));
+ VK_CHECK(vkEnumerateInstanceLayerProperties(&n_available_layers, available_layers));
+
+ for (int i = 0; i < cstr_darray_len(desired_validation_layers); i++) {
+ // look through layers to make sure we can find the ones we want
+ bool found = false;
+ for (int j = 0; j < n_available_layers; j++) {
+ if (str8_equals(str8_cstr_view(desired_validation_layers->data[i]),
+ str8_cstr_view(available_layers[j].layerName))) {
+ found = true;
+ TRACE("Found layer %s", desired_validation_layers->data[i]);
+ break;
+ }
+ }
+
+ if (!found) {
+ FATAL("Required validation is missing %s", desired_validation_layers->data[i]);
+ return false;
+ }
+ }
+ INFO("All validation layers are present");
+ create_info.enabledLayerCount = cstr_darray_len(desired_validation_layers);
+ create_info.ppEnabledLayerNames = desired_validation_layers->data;
+
+ VkResult result = vkCreateInstance(&create_info, NULL, &context.instance);
+ if (result != VK_SUCCESS) {
+ ERROR("vkCreateInstance failed with result: %u", result);
+ return false;
+ }
+ TRACE("Vulkan Instance created");
+
+ DEBUG("Creating Vulkan debugger");
+ u32 log_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
+ VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {
+ VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT
+ };
+ debug_create_info.messageSeverity = log_severity;
+ debug_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
+ debug_create_info.pfnUserCallback = vk_debug_callback;
+
+ PFN_vkCreateDebugUtilsMessengerEXT func =
+ (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context.instance,
+ "vkCreateDebugUtilsMessengerEXT");
+ assert(func);
+ VK_CHECK(func(context.instance, &debug_create_info, context.allocator, &context.vk_debugger));
+ DEBUG("Vulkan Debugger created");
+
+ // Surface creation
+ VkSurfaceKHR surface;
+ VK_CHECK(glfwCreateWindowSurface(context.instance, window, NULL, &surface));
+ context.surface = surface;
+ TRACE("Vulkan Surface created");
+
+ return true;
+}
+
+void gpu_backend_shutdown() {
+ gpu_swapchain_destroy(context.swapchain);
+
+ vkDestroySurfaceKHR(context.instance, context.surface, context.allocator);
+ vkDestroyInstance(context.instance, context.allocator);
+ arena_free_storage(&context.temp_arena);
+}
+
+bool gpu_device_create(gpu_device* out_device) {
+ // First things first store this poitner from the renderer
+ context.device = out_device;
+
+ arena_save savept = arena_savepoint(&context.temp_arena);
+ // Physical device
+ if (!select_physical_device(out_device)) {
+ return false;
+ }
+ TRACE("Physical device selected");
+
+ // Logical device & Queues
+ create_logical_device(out_device);
+
+ // Create the command pool
+ VkCommandPoolCreateInfo pool_create_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
+ pool_create_info.queueFamilyIndex = out_device->queue_family_indicies.graphics_family_index;
+ pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ vkCreateCommandPool(out_device->logical_device, &pool_create_info, context.allocator,
+ &out_device->pool);
+ TRACE("Command Pool created");
+
+ // Synchronisation objects
+ create_sync_objects();
+ TRACE("Synchronisation primitives created");
+
+ arena_rewind(savept); // Free any temp data
+ return true;
+}
+
+bool gpu_swapchain_create(gpu_swapchain* out_swapchain) {
+ context.swapchain = out_swapchain;
+
+ out_swapchain->swapchain_arena = arena_create(malloc(1024), 1024);
+
+ vulkan_device_query_swapchain_support(context.device->physical_device, context.surface,
+ &context.swapchain_support);
+ vulkan_swapchain_support_info swapchain_support = context.swapchain_support;
+
+ // TODO: custom swapchain extents VkExtent2D swapchain_extent = { width, height };
+
+ VkSurfaceFormatKHR image_format = choose_swapchain_format(&swapchain_support);
+ out_swapchain->image_format = image_format;
+ VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; // guaranteed to be implemented
+ out_swapchain->present_mode = present_mode;
+
+ u32 image_count = swapchain_support.capabilities.minImageCount + 1;
+ out_swapchain->image_count = image_count;
+
+ VkSwapchainCreateInfoKHR swapchain_create_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
+ swapchain_create_info.surface = context.surface;
+ swapchain_create_info.minImageCount = image_count;
+ swapchain_create_info.imageFormat = image_format.format;
+ swapchain_create_info.imageColorSpace = image_format.colorSpace;
+ swapchain_create_info.imageExtent = swapchain_support.capabilities.currentExtent;
+ swapchain_create_info.imageArrayLayers = 1;
+ swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchain_create_info.queueFamilyIndexCount = 0;
+ swapchain_create_info.pQueueFamilyIndices = NULL;
+
+ swapchain_create_info.preTransform = swapchain_support.capabilities.currentTransform;
+ swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+ swapchain_create_info.presentMode = present_mode;
+ swapchain_create_info.clipped = VK_TRUE;
+ swapchain_create_info.oldSwapchain = VK_NULL_HANDLE;
+
+ out_swapchain->extent = swapchain_support.capabilities.currentExtent;
+
+ VK_CHECK(vkCreateSwapchainKHR(context.device->logical_device, &swapchain_create_info,
+ context.allocator, &out_swapchain->handle));
+ TRACE("Vulkan Swapchain created");
+
+ // Retrieve Images
+ // out_swapchain->images =
+ // arena_alloc(&out_swapchain->swapchain_arena, image_count * sizeof(VkImage));
+ out_swapchain->images = malloc(image_count * sizeof(VkImage));
+ VK_CHECK(vkGetSwapchainImagesKHR(context.device->logical_device, out_swapchain->handle,
+ &image_count, out_swapchain->images));
+
+ // Create ImageViews
+ // TODO: Move this to a separate function
+ out_swapchain->image_views = malloc(image_count * sizeof(VkImageView));
+ // arena_alloc(&out_swapchain->swapchain_arena, image_count * sizeof(VkImageView));
+ for (u32 i = 0; i < image_count; i++) {
+ VkImageViewCreateInfo view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
+ view_create_info.image = out_swapchain->images[i];
+ view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ view_create_info.format = image_format.format;
+ view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+ view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+ view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+ view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+ view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ view_create_info.subresourceRange.baseMipLevel = 0;
+ view_create_info.subresourceRange.levelCount = 1;
+ view_create_info.subresourceRange.baseArrayLayer = 0;
+ view_create_info.subresourceRange.layerCount = 1;
+ vkCreateImageView(context.device->logical_device, &view_create_info, context.allocator,
+ &out_swapchain->image_views[i]);
+ }
+
+ return true;
+}
+
+void gpu_swapchain_destroy(gpu_swapchain* swapchain) {
+ // Destroy Framebuffers
+ DEBUG("Image count %d", swapchain->image_count);
+ for (u32 i = 0; i < swapchain->image_count; i++) {
+ DEBUG("Framebuffer handle %d", context.swapchain_framebuffers[i]);
+ vkDestroyFramebuffer(context.device->logical_device, context.swapchain_framebuffers[i],
+ context.allocator);
+ }
+ for (u32 i = 0; i < swapchain->image_count; i++) {
+ vkDestroyImageView(context.device->logical_device, swapchain->image_views[i],
+ context.allocator);
+ }
+ arena_free_all(&swapchain->swapchain_arena);
+ vkDestroySwapchainKHR(context.device->logical_device, swapchain->handle, context.allocator);
+ TRACE("Vulkan Swapchain destroyed");
+}
+
+static void recreate_swapchain(gpu_swapchain* swapchain) {
+ int width = 0, height = 0;
+ glfwGetFramebufferSize(context.window, &width, &height);
+ while (width == 0 || height == 0) {
+ glfwGetFramebufferSize(context.window, &width, &height);
+ glfwWaitEvents();
+ }
+ DEBUG("Recreating swapchain...");
+ vkDeviceWaitIdle(context.device->logical_device);
+
+ gpu_swapchain_destroy(swapchain);
+ gpu_swapchain_create(swapchain);
+ create_swapchain_framebuffers();
+}
+
+VkFormat format_from_vertex_attr(vertex_attrib_type attr) {
+ switch (attr) {
+ case ATTR_F32:
+ return VK_FORMAT_R32_SFLOAT;
+ case ATTR_U32:
+ return VK_FORMAT_R32_UINT;
+ case ATTR_I32:
+ return VK_FORMAT_R32_SINT;
+ case ATTR_F32x2:
+ return VK_FORMAT_R32G32_SFLOAT;
+ case ATTR_U32x2:
+ return VK_FORMAT_R32G32_UINT;
+ case ATTR_I32x2:
+ return VK_FORMAT_R32G32_UINT;
+ case ATTR_F32x3:
+ return VK_FORMAT_R32G32B32_SFLOAT;
+ case ATTR_U32x3:
+ return VK_FORMAT_R32G32B32_UINT;
+ case ATTR_I32x3:
+ return VK_FORMAT_R32G32B32_SINT;
+ case ATTR_F32x4:
+ return VK_FORMAT_R32G32B32A32_SFLOAT;
+ case ATTR_U32x4:
+ return VK_FORMAT_R32G32B32A32_UINT;
+ case ATTR_I32x4:
+ return VK_FORMAT_R32G32B32A32_SINT;
+ }
+}
+
+gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description) {
+ // Allocate
+ gpu_pipeline_layout* layout = malloc(sizeof(gpu_pipeline_layout));
+ gpu_pipeline* pipeline = malloc(sizeof(gpu_pipeline));
+
+ // Shaders
+ printf("Vertex shader: %s\n", description.vs.filepath.buf);
+ printf("Fragment shader: %s\n", description.fs.filepath.buf);
+ VkShaderModule vertex_shader = create_shader_module(description.vs.code);
+ VkShaderModule fragment_shader = create_shader_module(description.fs.code);
+
+ // Vertex
+ VkPipelineShaderStageCreateInfo vert_shader_stage_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO
+ };
+ vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
+ vert_shader_stage_info.module = vertex_shader;
+ vert_shader_stage_info.pName = "main";
+ // Fragment
+ VkPipelineShaderStageCreateInfo frag_shader_stage_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO
+ };
+ frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ frag_shader_stage_info.module = fragment_shader;
+ frag_shader_stage_info.pName = "main";
+
+ VkPipelineShaderStageCreateInfo shader_stages[2] = { vert_shader_stage_info,
+ frag_shader_stage_info };
+
+ // Attributes
+ u32 attr_count = description.vertex_desc.attributes_count;
+ printf("N attributes %d\n", attr_count);
+ VkVertexInputAttributeDescription attribute_descs[attr_count];
+ memset(attribute_descs, 0, attr_count * sizeof(VkVertexInputAttributeDescription));
+ u32 offset = 0;
+ for (u32 i = 0; i < description.vertex_desc.attributes_count; i++) {
+ attribute_descs[i].binding = 0;
+ attribute_descs[i].location = i;
+ attribute_descs[i].format = format_from_vertex_attr(description.vertex_desc.attributes[i]);
+ attribute_descs[i].offset = offset;
+ size_t this_offset = vertex_attrib_size(description.vertex_desc.attributes[i]);
+ printf("offset total %d this attr %ld\n", offset, this_offset);
+ printf("sizeof vertex %ld\n", sizeof(vertex));
+ offset += this_offset;
+ }
+
+ // Vertex input
+ // TODO: Generate this from descroiption now
+ VkVertexInputBindingDescription binding_desc;
+ binding_desc.binding = 0;
+ binding_desc.stride = description.vertex_desc.use_full_vertex_size
+ ? sizeof(vertex)
+ : description.vertex_desc.stride;
+ binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ VkPipelineVertexInputStateCreateInfo vertex_input_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
+ };
+ vertex_input_info.vertexBindingDescriptionCount = 1;
+ vertex_input_info.pVertexBindingDescriptions = &binding_desc;
+ vertex_input_info.vertexAttributeDescriptionCount =
+ attr_count; // description.vertex_desc.attributes_count;
+ vertex_input_info.pVertexAttributeDescriptions = attribute_descs;
+
+ // Input Assembly
+ VkPipelineInputAssemblyStateCreateInfo input_assembly = {
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO
+ };
+ input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ input_assembly.primitiveRestartEnable = VK_FALSE;
+
+ // Viewport
+ VkViewport viewport = { .x = 0,
+ .y = 0,
+ .width = (f32)context.swapchain->extent.width,
+ .height = (f32)context.swapchain->extent.height,
+ .minDepth = 0.0,
+ .maxDepth = 1.0 };
+ VkRect2D scissor = { .offset = { .x = 0, .y = 0 }, .extent = context.swapchain->extent };
+ VkPipelineViewportStateCreateInfo viewport_state = {
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO
+ };
+ viewport_state.viewportCount = 1;
+ // viewport_state.pViewports = &viewport;
+ viewport_state.scissorCount = 1;
+ // viewport_state.pScissors = &scissor;
+
+ // Rasterizer
+ VkPipelineRasterizationStateCreateInfo rasterizer_create_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO
+ };
+ rasterizer_create_info.depthClampEnable = VK_FALSE;
+ rasterizer_create_info.rasterizerDiscardEnable = VK_FALSE;
+ rasterizer_create_info.polygonMode =
+ description.wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL;
+ rasterizer_create_info.lineWidth = 1.0f;
+ rasterizer_create_info.cullMode = VK_CULL_MODE_BACK_BIT;
+ rasterizer_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ /* rasterizer_create_info.frontFace = VK_FRONT_FACE_CLOCKWISE; */
+ rasterizer_create_info.depthBiasEnable = VK_FALSE;
+ rasterizer_create_info.depthBiasConstantFactor = 0.0;
+ rasterizer_create_info.depthBiasClamp = 0.0;
+ rasterizer_create_info.depthBiasSlopeFactor = 0.0;
+
+ // Multisampling
+ VkPipelineMultisampleStateCreateInfo ms_create_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO
+ };
+ ms_create_info.sampleShadingEnable = VK_FALSE;
+ ms_create_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ ms_create_info.minSampleShading = 1.0;
+ ms_create_info.pSampleMask = 0;
+ ms_create_info.alphaToCoverageEnable = VK_FALSE;
+ ms_create_info.alphaToOneEnable = VK_FALSE;
+
+ // TODO: Depth and stencil testing
+ // VkPipelineDepthStencilStateCreateInfo depth_stencil = {
+ // VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
+ // };
+ // depth_stencil.depthTestEnable = description.depth_test ? VK_TRUE : VK_FALSE;
+ // depth_stencil.depthWriteEnable = description.depth_test ? VK_TRUE : VK_FALSE;
+ // depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS;
+ // depth_stencil.depthBoundsTestEnable = VK_FALSE;
+ // depth_stencil.stencilTestEnable = VK_FALSE;
+ // depth_stencil.pNext = 0;
+
+ // Blending
+ VkPipelineColorBlendAttachmentState color_blend_attachment_state;
+ color_blend_attachment_state.blendEnable = VK_FALSE;
+ color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD;
+ color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD;
+ color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
+ VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+
+ VkPipelineColorBlendStateCreateInfo color_blend = {
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO
+ };
+ color_blend.logicOpEnable = VK_FALSE;
+ color_blend.logicOp = VK_LOGIC_OP_COPY;
+ color_blend.attachmentCount = 1;
+ color_blend.pAttachments = &color_blend_attachment_state;
+
+// Dynamic state
+#define DYNAMIC_STATE_COUNT 2
+ VkDynamicState dynamic_states[DYNAMIC_STATE_COUNT] = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR,
+ };
+
+ VkPipelineDynamicStateCreateInfo dynamic_state = {
+ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO
+ };
+ dynamic_state.dynamicStateCount = DYNAMIC_STATE_COUNT;
+ dynamic_state.pDynamicStates = dynamic_states;
+
+ // Descriptor Set layouts
+
+ VkDescriptorSetLayout* desc_set_layouts =
+ malloc(description.data_layouts_count * sizeof(VkDescriptorSetLayout));
+ pipeline->desc_set_layouts = desc_set_layouts;
+ pipeline->desc_set_layouts_count = description.data_layouts_count;
+ if (description.data_layouts_count > 0) {
+ pipeline->uniform_pointers =
+ malloc(description.data_layouts_count * sizeof(desc_set_uniform_buffer));
+ } else {
+ pipeline->uniform_pointers = NULL;
+ }
+
+ // assert(description.data_layouts_count == 1);
+ printf("data layouts %d\n", description.data_layouts_count);
+ for (u32 layout_i = 0; layout_i < description.data_layouts_count; layout_i++) {
+ shader_data_layout sdl = description.data_layouts[layout_i].shader_data_get_layout(NULL);
+ TRACE("Got shader data layout %d's bindings! . found %d", layout_i, sdl.bindings_count);
+
+ VkDescriptorSetLayoutBinding desc_set_bindings[sdl.bindings_count];
+
+ // Bindings
+ assert(sdl.bindings_count == 2);
+ for (u32 binding_j = 0; binding_j < sdl.bindings_count; binding_j++) {
+ desc_set_bindings[binding_j].binding = binding_j;
+ desc_set_bindings[binding_j].descriptorCount = 1;
+ switch (sdl.bindings[binding_j].type) {
+ case SHADER_BINDING_BUFFER:
+ case SHADER_BINDING_BYTES:
+ desc_set_bindings[binding_j].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ desc_set_bindings[binding_j].stageFlags =
+ VK_SHADER_STAGE_VERTEX_BIT; // FIXME: dont hardcode
+
+ u64 buffer_size = sdl.bindings[binding_j].data.bytes.size;
+ VkDeviceSize uniform_buf_size = buffer_size;
+ // TODO: Create backing buffer
+
+ VkBuffer buffers[MAX_FRAMES_IN_FLIGHT];
+ VkDeviceMemory uniform_buf_memorys[MAX_FRAMES_IN_FLIGHT];
+ void* uniform_buf_mem_mappings[MAX_FRAMES_IN_FLIGHT];
+ // void* s?
+ for (size_t frame_i = 0; frame_i < MAX_FRAMES_IN_FLIGHT; frame_i++) {
+ buffer_handle uniform_buf_handle =
+ gpu_buffer_create(buffer_size, CEL_BUFFER_UNIFORM, CEL_BUFFER_FLAG_CPU, NULL);
+
+ gpu_buffer* created_gpu_buffer =
+ BUFFER_GET(uniform_buf_handle); // context.buffers[uniform_buf_handle.raw];
+ buffers[frame_i] = created_gpu_buffer->handle;
+ uniform_buf_memorys[frame_i] = created_gpu_buffer->memory;
+ vkMapMemory(context.device->logical_device, uniform_buf_memorys[frame_i], 0,
+ uniform_buf_size, 0, &uniform_buf_mem_mappings[frame_i]);
+ // now we have a pointer in unifrom_buf_mem_mappings we can write to
+ }
+
+ desc_set_uniform_buffer uniform_data;
+ memcpy(&uniform_data.buffers, &buffers, sizeof(buffers));
+ memcpy(&uniform_data.uniform_buf_memorys, &uniform_buf_memorys,
+ sizeof(uniform_buf_memorys));
+ memcpy(&uniform_data.uniform_buf_mem_mappings, &uniform_buf_mem_mappings,
+ sizeof(uniform_buf_mem_mappings));
+ uniform_data.size = buffer_size;
+
+ pipeline->uniform_pointers[binding_j] = uniform_data;
+
+ break;
+ case SHADER_BINDING_TEXTURE:
+ desc_set_bindings[binding_j].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ desc_set_bindings[binding_j].stageFlags =
+ VK_SHADER_STAGE_FRAGMENT_BIT; // FIXME: dont hardcode
+ desc_set_bindings[binding_j].pImmutableSamplers = NULL;
+
+ break;
+ default:
+ ERROR_EXIT("Unimplemented binding type!! in backend_vulkan");
+ }
+ switch (sdl.bindings[binding_j].vis) {
+ case VISIBILITY_VERTEX:
+ desc_set_bindings[binding_j].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+ break;
+ case VISIBILITY_FRAGMENT:
+ desc_set_bindings[binding_j].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+ break;
+ case VISIBILITY_COMPUTE:
+ WARN("Compute is not implemented yet");
+ break;
+ }
+ }
+
+ VkDescriptorSetLayoutCreateInfo desc_set_layout_info = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO
+ };
+ desc_set_layout_info.bindingCount = sdl.bindings_count;
+ desc_set_layout_info.pBindings = desc_set_bindings;
+
+ VK_CHECK(vkCreateDescriptorSetLayout(context.device->logical_device, &desc_set_layout_info,
+ context.allocator, &desc_set_layouts[layout_i]));
+ }
+ printf("Descriptor set layouts\n");
+
+ // Layout
+ VkPipelineLayoutCreateInfo pipeline_layout_create_info = {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO
+ };
+ pipeline_layout_create_info.setLayoutCount = description.data_layouts_count;
+ pipeline_layout_create_info.pSetLayouts = desc_set_layouts;
+ pipeline_layout_create_info.pushConstantRangeCount = 0;
+ pipeline_layout_create_info.pPushConstantRanges = NULL;
+ VK_CHECK(vkCreatePipelineLayout(context.device->logical_device, &pipeline_layout_create_info,
+ context.allocator, &layout->handle));
+ pipeline->layout_handle = layout->handle; // keep a copy of the layout on the pipeline object
+
+ VkGraphicsPipelineCreateInfo pipeline_create_info = {
+ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO
+ };
+
+ pipeline_create_info.stageCount = 2;
+ pipeline_create_info.pStages = shader_stages;
+ pipeline_create_info.pVertexInputState = &vertex_input_info;
+ pipeline_create_info.pInputAssemblyState = &input_assembly;
+
+ pipeline_create_info.pViewportState = &viewport_state;
+ pipeline_create_info.pRasterizationState = &rasterizer_create_info;
+ pipeline_create_info.pMultisampleState = &ms_create_info;
+ pipeline_create_info.pDepthStencilState = NULL; // &depth_stencil;
+ pipeline_create_info.pColorBlendState = &color_blend;
+ pipeline_create_info.pDynamicState = &dynamic_state;
+ pipeline_create_info.pTessellationState = 0;
+
+ pipeline_create_info.layout = layout->handle;
+
+ pipeline_create_info.renderPass = description.renderpass->handle;
+ pipeline_create_info.subpass = 0;
+ pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
+ pipeline_create_info.basePipelineIndex = -1;
+
+ printf("About to create graphics pipeline\n");
+
+ VkResult result =
+ vkCreateGraphicsPipelines(context.device->logical_device, VK_NULL_HANDLE, 1,
+ &pipeline_create_info, context.allocator, &pipeline->handle);
+ if (result != VK_SUCCESS) {
+ FATAL("graphics pipeline creation failed. its fked mate");
+ ERROR_EXIT("Doomed");
+ }
+ TRACE("Vulkan Graphics pipeline created");
+
+ // once the pipeline has been created we can destroy these
+ vkDestroyShaderModule(context.device->logical_device, vertex_shader, context.allocator);
+ vkDestroyShaderModule(context.device->logical_device, fragment_shader, context.allocator);
+
+ // Framebuffers
+ create_swapchain_framebuffers();
+ TRACE("Swapchain Framebuffers created");
+
+ for (u32 frame_i = 0; frame_i < MAX_FRAMES_IN_FLIGHT; frame_i++) {
+ context.main_cmd_bufs[frame_i] = gpu_cmd_encoder_create();
+ }
+ TRACE("main Command Buffer created");
+
+ TRACE("Graphics pipeline created");
+ return pipeline;
+}
+
+void gpu_pipeline_destroy(gpu_pipeline* pipeline) {
+ vkDestroyPipeline(context.device->logical_device, pipeline->handle, context.allocator);
+ vkDestroyPipelineLayout(context.device->logical_device, pipeline->layout_handle,
+ context.allocator);
+}
+
+gpu_cmd_encoder* gpu_get_default_cmd_encoder() {
+ return &context.main_cmd_bufs[context.current_frame];
+}
+
+gpu_renderpass* gpu_renderpass_create(const gpu_renderpass_desc* description) {
+ // TEMP: allocate with malloc. in the future we will have a pool allocator on the context
+ gpu_renderpass* renderpass = malloc(sizeof(gpu_renderpass));
+
+ // attachments
+ u32 attachment_desc_count = 2;
+ VkAttachmentDescription attachment_descriptions[2];
+
+ // Colour attachment
+ VkAttachmentDescription color_attachment;
+ color_attachment.format = context.swapchain->image_format.format;
+ color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+ color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ color_attachment.flags = 0;
+
+ attachment_descriptions[0] = color_attachment;
+
+ VkAttachmentReference color_attachment_reference;
+ color_attachment_reference.attachment = 0;
+ color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+ // Depth attachment
+ u32x2 ext = { .x = context.swapchain_support.capabilities.currentExtent.width,
+ .y = context.swapchain_support.capabilities.currentExtent.height };
+ texture_desc depth_desc = { .extents = ext,
+ .format = CEL_TEXTURE_FORMAT_DEPTH_DEFAULT,
+ .tex_type = CEL_TEXTURE_TYPE_2D };
+ texture_handle depth_texture_handle = gpu_texture_create(depth_desc, true, NULL);
+ gpu_texture* depth = TEXTURE_GET(depth_texture_handle);
+
+ VkAttachmentDescription depth_attachment;
+ depth_attachment.format = // TODO: context->device.depth_format;
+ depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+ depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ depth_attachment.flags = 0;
+
+ attachment_descriptions[1] = depth_attachment;
+
+ VkAttachmentReference depth_attachment_reference;
+ depth_attachment_reference.attachment = 1;
+ depth_attachment_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+ // main subpass
+ VkSubpassDescription subpass = { 0 };
+ subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpass.colorAttachmentCount = 1;
+ subpass.pColorAttachments = &color_attachment_reference;
+
+ // sets everything up
+ // renderpass dependencies
+ VkSubpassDependency dependency;
+ dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+ dependency.dstSubpass = 0;
+ dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependency.srcAccessMask = 0;
+ dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependency.dstAccessMask =
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ dependency.dependencyFlags = 0;
+
+ // Finally, create the RenderPass
+ VkRenderPassCreateInfo render_pass_create_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
+ render_pass_create_info.attachmentCount = 1;
+ render_pass_create_info.pAttachments = &color_attachment;
+ render_pass_create_info.subpassCount = 1;
+ render_pass_create_info.pSubpasses = &subpass;
+ render_pass_create_info.dependencyCount = 1;
+ render_pass_create_info.pDependencies = &dependency;
+ render_pass_create_info.flags = 0;
+ render_pass_create_info.pNext = 0;
+
+ VK_CHECK(vkCreateRenderPass(context.device->logical_device, &render_pass_create_info,
+ context.allocator, &renderpass->handle));
+
+ // HACK
+ context.main_renderpass = renderpass->handle;
+
+ return renderpass;
+}
+
+gpu_cmd_encoder gpu_cmd_encoder_create() {
+ // gpu_cmd_encoder* encoder = malloc(sizeof(gpu_cmd_encoder)); // TODO: fix leaking mem
+ gpu_cmd_encoder encoder = { 0 };
+
+ VkCommandBufferAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
+ allocate_info.commandPool = context.device->pool;
+ allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ allocate_info.commandBufferCount = 1;
+ allocate_info.pNext = NULL;
+
+ VK_CHECK(vkAllocateCommandBuffers(context.device->logical_device, &allocate_info,
+ &encoder.cmd_buffer););
+
+ VkDescriptorPoolSize pool_sizes[2];
+ // Uniforms pool
+ pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ pool_sizes[0].descriptorCount = MAX_FRAMES_IN_FLIGHT * MAX_DESCRIPTOR_SETS;
+ // Samplers pool
+ pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ pool_sizes[1].descriptorCount = MAX_FRAMES_IN_FLIGHT * MAX_DESCRIPTOR_SETS;
+
+ VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
+ pool_info.poolSizeCount = 2;
+ pool_info.pPoolSizes = pool_sizes;
+ pool_info.maxSets = 100;
+
+ VK_CHECK(vkCreateDescriptorPool(context.device->logical_device, &pool_info, context.allocator,
+ &encoder.descriptor_pool));
+
+ return encoder;
+}
+void gpu_cmd_encoder_destroy(gpu_cmd_encoder* encoder) {
+ vkFreeCommandBuffers(context.device->logical_device, context.device->pool, 1,
+ &encoder->cmd_buffer);
+}
+
+void gpu_cmd_encoder_begin(gpu_cmd_encoder encoder) {
+ VK_CHECK(vkResetDescriptorPool(context.device->logical_device, encoder.descriptor_pool, 0));
+
+ VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
+ VK_CHECK(vkBeginCommandBuffer(encoder.cmd_buffer, &begin_info));
+}
+
+void gpu_cmd_encoder_begin_render(gpu_cmd_encoder* encoder, gpu_renderpass* renderpass) {
+ VkRenderPassBeginInfo begin_info = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
+ begin_info.renderPass = renderpass->handle;
+ /* printf("Current img: %d Current frame %d\n", context.current_img_index, context.current_frame);
+ */
+ begin_info.framebuffer = context.swapchain_framebuffers[context.current_img_index];
+ begin_info.renderArea.offset = (VkOffset2D){ 0, 0 };
+ begin_info.renderArea.extent = context.swapchain->extent;
+
+ // VkClearValue clear_values[2];
+ VkClearValue clear_color = { { { 0.02f, 0.02f, 0.02f, 1.0f } } };
+ // clear_values[1].depthStencil.depth = renderpass->depth;
+ // clear_values[1].depthStencil.stencil = renderpass->stencil;
+
+ begin_info.clearValueCount = 1;
+ begin_info.pClearValues = &clear_color;
+
+ vkCmdBeginRenderPass(encoder->cmd_buffer, &begin_info, VK_SUBPASS_CONTENTS_INLINE);
+ // command_buffer->state = COMMAND_BUFFER_STATE_IN_RENDER_PASS;
+}
+
+void gpu_cmd_encoder_end_render(gpu_cmd_encoder* encoder) {
+ vkCmdEndRenderPass(encoder->cmd_buffer);
+}
+
+gpu_cmd_buffer gpu_cmd_encoder_finish(gpu_cmd_encoder* encoder) {
+ vkEndCommandBuffer(encoder->cmd_buffer);
+
+ // TEMP: submit
+ return (gpu_cmd_buffer){ .cmd_buffer = encoder->cmd_buffer };
+}
+
+// --- Binding
+void encode_bind_pipeline(gpu_cmd_encoder* encoder, pipeline_kind kind, gpu_pipeline* pipeline) {
+ vkCmdBindPipeline(encoder->cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle);
+ encoder->pipeline = pipeline;
+}
+
+void encode_bind_shader_data(gpu_cmd_encoder* encoder, u32 group, shader_data* data) {
+ arena tmp = arena_create(malloc(1024), 1024);
+
+ assert(data->data != NULL);
+
+ // Update the local buffer
+ desc_set_uniform_buffer ubo = encoder->pipeline->uniform_pointers[group];
+ memcpy(ubo.uniform_buf_mem_mappings[context.current_frame], data->data, ubo.size);
+
+ VkDescriptorSetAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
+ alloc_info.descriptorPool = encoder->descriptor_pool;
+ alloc_info.descriptorSetCount = 1;
+ alloc_info.pSetLayouts = &encoder->pipeline->desc_set_layouts[group];
+
+ shader_data_layout sdl = data->shader_data_get_layout(data->data);
+ size_t binding_count = sdl.bindings_count;
+ assert(binding_count == 2);
+
+ VkDescriptorSet sets[0];
+ VK_CHECK(vkAllocateDescriptorSets(context.device->logical_device, &alloc_info, sets));
+ // FIXME: hardcoded
+ VkDescriptorSet_darray_push(context.free_set_queue, sets[0]);
+ /* VkDescriptorSet_darray_push(context.free_set_queue, sets[1]); */
+
+ VkWriteDescriptorSet write_sets[binding_count];
+ memset(&write_sets, 0, binding_count * sizeof(VkWriteDescriptorSet));
+
+ for (u32 i = 0; i < sdl.bindings_count; i++) {
+ shader_binding binding = sdl.bindings[i];
+
+ if (binding.type == SHADER_BINDING_BUFFER || binding.type == SHADER_BINDING_BYTES) {
+ VkDescriptorBufferInfo* buffer_info = arena_alloc(&tmp, sizeof(VkDescriptorBufferInfo));
+ buffer_info->buffer = ubo.buffers[context.current_frame];
+ buffer_info->offset = 0;
+ buffer_info->range = binding.data.bytes.size;
+
+ write_sets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write_sets[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ write_sets[i].descriptorCount = 1;
+ write_sets[i].dstSet = sets[0];
+ write_sets[i].dstBinding = i;
+ write_sets[i].dstArrayElement = 0;
+ write_sets[i].pBufferInfo = buffer_info;
+ } else if (binding.type == SHADER_BINDING_TEXTURE) {
+ gpu_texture* texture = TEXTURE_GET(binding.data.texture.handle);
+ VkDescriptorImageInfo* image_info = arena_alloc(&tmp, sizeof(VkDescriptorImageInfo));
+ image_info->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ image_info->imageView = texture->view;
+ image_info->sampler = texture->sampler;
+
+ write_sets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write_sets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ write_sets[i].descriptorCount = 1;
+ write_sets[i].dstSet = sets[0];
+ write_sets[i].dstBinding = i;
+ write_sets[i].dstArrayElement = 0;
+ write_sets[i].pImageInfo = image_info;
+ } else {
+ WARN("Unknown binding");
+ }
+ }
+
+ // Update
+ vkUpdateDescriptorSets(context.device->logical_device, binding_count, write_sets, 0, NULL);
+
+ // Bind
+ vkCmdBindDescriptorSets(encoder->cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ encoder->pipeline->layout_handle, 0, 1, sets, 0, NULL);
+
+ arena_free_storage(&tmp);
+}
+
+void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf) {
+ gpu_buffer* buffer = BUFFER_GET(buf); // context.buffers[buf.raw];
+ VkBuffer vbs[] = { buffer->handle };
+ VkDeviceSize offsets[] = { 0 };
+ vkCmdBindVertexBuffers(encoder->cmd_buffer, 0, 1, vbs, offsets);
+}
+
+void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf) {
+ gpu_buffer* buffer = BUFFER_GET(buf); // context.buffers[buf.raw];
+ vkCmdBindIndexBuffer(encoder->cmd_buffer, buffer->handle, 0, VK_INDEX_TYPE_UINT32);
+}
+
+// TEMP
+void encode_set_default_settings(gpu_cmd_encoder* encoder) {
+ VkViewport viewport = { 0 };
+ viewport.x = 0.0f;
+ viewport.y = 0.0f;
+ viewport.width = context.swapchain->extent.width;
+ viewport.height = context.swapchain->extent.height;
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+ vkCmdSetViewport(encoder->cmd_buffer, 0, 1, &viewport);
+
+ VkRect2D scissor = { 0 };
+ scissor.offset = (VkOffset2D){ 0, 0 };
+ scissor.extent = context.swapchain->extent;
+ vkCmdSetScissor(encoder->cmd_buffer, 0, 1, &scissor);
+}
+
+// --- Drawing
+
+bool gpu_backend_begin_frame() {
+ u32 current_frame = context.current_frame;
+ vkWaitForFences(context.device->logical_device, 1, &context.in_flight_fences[current_frame],
+ VK_TRUE, UINT64_MAX);
+
+ u32 image_index;
+ VkResult result = vkAcquireNextImageKHR(
+ context.device->logical_device, context.swapchain->handle, UINT64_MAX,
+ context.image_available_semaphores[current_frame], VK_NULL_HANDLE, &image_index);
+ if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || context.is_resizing) {
+ ERROR("Acquire next image failure. recreate swapchain");
+ context.is_resizing = false;
+ recreate_swapchain(context.swapchain);
+ return false;
+ } else if (result != VK_SUCCESS) {
+ ERROR_EXIT("failed to acquire swapchain image");
+ }
+
+ vkResetFences(context.device->logical_device, 1, &context.in_flight_fences[current_frame]);
+
+ context.current_img_index = image_index;
+ VK_CHECK(vkResetCommandBuffer(context.main_cmd_bufs[current_frame].cmd_buffer, 0));
+ return true;
+}
+
+void gpu_temp_draw(size_t n_indices) {
+ gpu_cmd_encoder* encoder = gpu_get_default_cmd_encoder(); // &context.main_cmd_buf;
+ /* vkCmdDraw(encoder->cmd_buffer, n_verts, 1, 0, 0); */
+ vkCmdDrawIndexed(encoder->cmd_buffer, n_indices, 1, 0, 0, 0);
+}
+
+void gpu_backend_end_frame() {
+ VkPresentInfoKHR present_info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
+ present_info.waitSemaphoreCount = 1;
+ present_info.pWaitSemaphores = &context.render_finished_semaphores[context.current_frame];
+
+ VkSwapchainKHR swapchains[] = { context.swapchain->handle };
+ present_info.swapchainCount = 1;
+ present_info.pSwapchains = swapchains;
+ present_info.pImageIndices = &context.current_img_index;
+
+ VkResult result = vkQueuePresentKHR(context.device->present_queue, &present_info);
+ if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
+ ERROR("Queue present error. recreate swapchain");
+ recreate_swapchain(context.swapchain);
+ return;
+ } else if (result != VK_SUCCESS) {
+ ERROR_EXIT("failed to present swapchain image");
+ }
+ context.current_frame = (context.current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
+
+ /* vkDeviceWaitIdle(context.device->logical_device); */
+}
+
+// TODO: Move into better order in file
+void gpu_queue_submit(gpu_cmd_buffer* buffer) {
+ VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
+
+ // Specify semaphore to wait on
+ VkSemaphore wait_semaphores[] = { context.image_available_semaphores[context.current_frame] };
+ VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
+
+ submit_info.waitSemaphoreCount = 1;
+ submit_info.pWaitSemaphores = wait_semaphores;
+ submit_info.pWaitDstStageMask = wait_stages;
+
+ // Specify semaphore to signal when finished executing buffer
+ VkSemaphore signal_semaphores[] = { context.render_finished_semaphores[context.current_frame] };
+ submit_info.signalSemaphoreCount = 1;
+ submit_info.pSignalSemaphores = signal_semaphores;
+
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &buffer->cmd_buffer;
+
+ VK_CHECK(vkQueueSubmit(context.device->graphics_queue, 1, &submit_info,
+ context.in_flight_fences[context.current_frame]));
+}
+
+inline void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count) {
+ vkCmdDrawIndexed(encoder->cmd_buffer, index_count, 1, 0, 0, 0);
+}
+
+bool select_physical_device(gpu_device* out_device) {
+ u32 physical_device_count = 0;
+ VK_CHECK(vkEnumeratePhysicalDevices(context.instance, &physical_device_count, 0));
+ if (physical_device_count == 0) {
+ FATAL("No devices that support vulkan were found");
+ return false;
+ }
+ TRACE("Number of devices found %d", physical_device_count);
+
+ VkPhysicalDevice* physical_devices =
+ arena_alloc(&context.temp_arena, physical_device_count * sizeof(VkPhysicalDevice));
+ VK_CHECK(vkEnumeratePhysicalDevices(context.instance, &physical_device_count, physical_devices));
+
+ bool found = false;
+ for (u32 device_i = 0; device_i < physical_device_count; device_i++) {
+ if (is_physical_device_suitable(physical_devices[device_i])) {
+ out_device->physical_device = physical_devices[device_i];
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ FATAL("Couldn't find a suitable physical device");
+ return false;
+ }
+
+ vkGetPhysicalDeviceProperties(out_device->physical_device, &out_device->properties);
+ vkGetPhysicalDeviceFeatures(out_device->physical_device, &out_device->features);
+ vkGetPhysicalDeviceMemoryProperties(out_device->physical_device, &out_device->memory);
+
+ return true;
+}
+
+bool is_physical_device_suitable(VkPhysicalDevice device) {
+ VkPhysicalDeviceProperties properties;
+ vkGetPhysicalDeviceProperties(device, &properties);
+
+ VkPhysicalDeviceFeatures features;
+ vkGetPhysicalDeviceFeatures(device, &features);
+
+ VkPhysicalDeviceMemoryProperties memory;
+ vkGetPhysicalDeviceMemoryProperties(device, &memory);
+
+ // TODO: Check against these device properties
+
+ queue_family_indices indices = find_queue_families(device);
+
+ vulkan_device_query_swapchain_support(device, context.surface, &context.swapchain_support);
+
+ return indices.has_graphics && indices.has_present && context.swapchain_support.mode_count > 0 &&
+ context.swapchain_support.format_count > 0;
+}
+
+queue_family_indices find_queue_families(VkPhysicalDevice device) {
+ queue_family_indices indices = { 0 };
+
+ u32 queue_family_count = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, 0);
+
+ VkQueueFamilyProperties* queue_families =
+ arena_alloc(&context.temp_arena, queue_family_count * sizeof(VkQueueFamilyProperties));
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families);
+
+ for (u32 q_fam_i = 0; q_fam_i < queue_family_count; q_fam_i++) {
+ // Graphics queue
+ if (queue_families[q_fam_i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ indices.graphics_family_index = q_fam_i;
+ indices.has_graphics = true;
+ }
+
+ VkBool32 present_support = false;
+ vkGetPhysicalDeviceSurfaceSupportKHR(device, q_fam_i, context.surface, &present_support);
+ if (present_support && !indices.has_present) {
+ indices.present_family_index = q_fam_i;
+ indices.has_present = true;
+ }
+ }
+
+ return indices;
+}
+
+bool create_logical_device(gpu_device* out_device) {
+ queue_family_indices indices = find_queue_families(out_device->physical_device);
+ INFO(" %s | %s | %s | %s | %s", bool_str(indices.has_graphics), bool_str(indices.has_present),
+ bool_str(indices.has_compute), bool_str(indices.has_transfer),
+ out_device->properties.deviceName);
+ TRACE("Graphics Family queue index: %d", indices.graphics_family_index);
+ TRACE("Present Family queue index: %d", indices.present_family_index);
+ TRACE("Compute Family queue index: %d", indices.compute_family_index);
+ TRACE("Transfer Family queue index: %d", indices.transfer_family_index);
+
+ // Queues
+ f32 prio_one = 1.0;
+ VkDeviceQueueCreateInfo queue_create_infos[1] = { 0 };
+ queue_create_infos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queue_create_infos[0].queueFamilyIndex = indices.graphics_family_index;
+ queue_create_infos[0].queueCount = 1;
+ queue_create_infos[0].pQueuePriorities = &prio_one;
+ queue_create_infos[0].flags = 0;
+ queue_create_infos[0].pNext = 0;
+
+ // queue_create_infos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ // queue_create_infos[1].queueFamilyIndex = indices.present_family_index;
+ // queue_create_infos[1].queueCount = 1;
+ // queue_create_infos[1].pQueuePriorities = &prio_one;
+ // queue_create_infos[1].flags = 0;
+ // queue_create_infos[1].pNext = 0;
+
+ // Features
+ VkPhysicalDeviceFeatures device_features = { 0 };
+ device_features.samplerAnisotropy = VK_TRUE; // request anistrophy
+
+ // Device itself
+ VkDeviceCreateInfo device_create_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
+ device_create_info.queueCreateInfoCount = 1;
+ device_create_info.pQueueCreateInfos = queue_create_infos;
+ device_create_info.pEnabledFeatures = &device_features;
+ device_create_info.enabledExtensionCount = 1;
+ const char* extension_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
+ device_create_info.ppEnabledExtensionNames = &extension_names;
+
+ // deprecated
+ device_create_info.enabledLayerCount = 0;
+ device_create_info.ppEnabledLayerNames = 0;
+
+ VkResult result = vkCreateDevice(context.device->physical_device, &device_create_info,
+ context.allocator, &context.device->logical_device);
+ if (result != VK_SUCCESS) {
+ printf("error creating logical device with status %u\n", result);
+ ERROR_EXIT("Unable to create vulkan logical device. Exiting..");
+ }
+ TRACE("Logical device created");
+
+ context.device->queue_family_indicies = indices;
+
+ // Retrieve queue handles
+ vkGetDeviceQueue(context.device->logical_device, indices.graphics_family_index, 0,
+ &context.device->graphics_queue);
+ vkGetDeviceQueue(context.device->logical_device, indices.present_family_index, 0,
+ &context.device->present_queue);
+
+ return true;
+}
+
+VkShaderModule create_shader_module(str8 spirv) {
+ VkShaderModuleCreateInfo create_info = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
+ create_info.codeSize = spirv.len;
+ create_info.pCode = (uint32_t*)spirv.buf;
+
+ VkShaderModule shader_module;
+ VK_CHECK(vkCreateShaderModule(context.device->logical_device, &create_info, context.allocator,
+ &shader_module));
+
+ return shader_module;
+}
+
+void create_descriptor_pools() {}
+
+void create_swapchain_framebuffers() {
+ WARN("Recreating framebuffers...");
+ u32 image_count = context.swapchain->image_count;
+ context.swapchain_framebuffers =
+ arena_alloc(&context.swapchain->swapchain_arena, image_count * sizeof(VkFramebuffer));
+ for (u32 i = 0; i < image_count; i++) {
+ VkImageView attachments[1] = { context.swapchain->image_views[i] };
+
+ VkFramebufferCreateInfo framebuffer_create_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
+ framebuffer_create_info.attachmentCount = 1;
+ framebuffer_create_info.pAttachments = attachments;
+
+ framebuffer_create_info.renderPass =
+ context.main_renderpass; // TODO: description.renderpass->handle;
+ framebuffer_create_info.width = context.swapchain->extent.width;
+ framebuffer_create_info.height = context.swapchain->extent.height;
+ framebuffer_create_info.layers = 1;
+
+ VK_CHECK(vkCreateFramebuffer(context.device->logical_device, &framebuffer_create_info,
+ context.allocator, &context.swapchain_framebuffers[i]));
+ }
+}
+
+void create_sync_objects() {
+ VkSemaphoreCreateInfo semaphore_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
+ VkFenceCreateInfo fence_info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
+ fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+ for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ VK_CHECK(vkCreateSemaphore(context.device->logical_device, &semaphore_info, context.allocator,
+ &context.image_available_semaphores[i]););
+ VK_CHECK(vkCreateSemaphore(context.device->logical_device, &semaphore_info, context.allocator,
+ &context.render_finished_semaphores[i]););
+
+ VK_CHECK(vkCreateFence(context.device->logical_device, &fence_info, context.allocator,
+ &context.in_flight_fences[i]));
+ }
+}
+
+static i32 find_memory_index(u32 type_filter, u32 property_flags) {
+ VkPhysicalDeviceMemoryProperties memory_properties;
+ vkGetPhysicalDeviceMemoryProperties(context.device->physical_device, &memory_properties);
+
+ for (u32 i = 0; i < memory_properties.memoryTypeCount; ++i) {
+ // Check each memory type to see if its bit is set to 1.
+ if (type_filter & (1 << i) &&
+ (memory_properties.memoryTypes[i].propertyFlags & property_flags) == property_flags) {
+ return i;
+ }
+ }
+
+ WARN("Unable to find suitable memory type!");
+ return -1;
+}
+
+buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_flags flags,
+ const void* data) {
+ VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+ buffer_info.size = size;
+ buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+ switch (buf_type) {
+ case CEL_BUFFER_DEFAULT:
+ buffer_info.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+ break;
+ case CEL_BUFFER_VERTEX:
+ buffer_info.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ break;
+ case CEL_BUFFER_INDEX:
+ buffer_info.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
+ break;
+ case CEL_BUFFER_UNIFORM:
+ buffer_info.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ break;
+ case CEL_BUFFER_COUNT:
+ WARN("Incorrect gpu_buffer_type provided. using default");
+ break;
+ }
+
+ buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ // "allocating" the cpu-side buffer struct
+ /* gpu_buffer buffer; */
+ /* buffer.size = size; */
+ buffer_handle handle;
+ gpu_buffer* buffer = buffer_pool_alloc(&context.resource_pools->buffers, &handle);
+ buffer->size = size;
+
+ VK_CHECK(vkCreateBuffer(context.device->logical_device, &buffer_info, context.allocator,
+ &buffer->handle));
+
+ VkMemoryRequirements requirements;
+ vkGetBufferMemoryRequirements(context.device->logical_device, buffer->handle, &requirements);
+
+ // Just make them always need all of them for now
+ i32 memory_index =
+ find_memory_index(requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+ // Allocate the actual VRAM
+ VkMemoryAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ allocate_info.allocationSize = requirements.size;
+ allocate_info.memoryTypeIndex = (u32)memory_index;
+
+ vkAllocateMemory(context.device->logical_device, &allocate_info, context.allocator,
+ &buffer->memory);
+ vkBindBufferMemory(context.device->logical_device, buffer->handle, buffer->memory, 0);
+
+ /* Now there are two options:
+ * 1. create CPU-accessible memory -> map memory -> memcpy -> unmap
+ * 2. use a staging buffer thats CPU-accessible and copy its contents to a
+ * GPU-only buffer
+ */
+
+ /* context.buffers[context.buffer_count] = buffer; */
+ /* context.buffer_count++; */
+
+ if (data) {
+ TRACE("Upload data as part of buffer creation");
+ if (flags & CEL_BUFFER_FLAG_CPU) {
+ // map memory -> copy data in -> unmap memory
+ buffer_upload_bytes(handle, (bytebuffer){ .buf = (u8*)data, .size = size }, 0, size);
+ } else if (flags & CEL_BUFFER_FLAG_GPU) {
+ TRACE("Uploading data to buffer using staging buffer");
+ // Create a staging buffer
+ buffer_handle staging = gpu_buffer_create(size, buf_type, CEL_BUFFER_FLAG_CPU, NULL);
+
+ // Copy data into it
+ buffer_upload_bytes(staging, (bytebuffer){ .buf = (u8*)data, .size = size }, 0, size);
+
+ // Enqueue a copy from the staging buffer into the DEVICE_LOCAL buffer
+ gpu_cmd_encoder temp_encoder = gpu_cmd_encoder_create();
+ gpu_cmd_encoder_begin(temp_encoder);
+ encode_buffer_copy(&temp_encoder, staging, 0, handle, 0, size);
+ gpu_cmd_buffer copy_cmd_buffer = gpu_cmd_encoder_finish(&temp_encoder);
+
+ VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &temp_encoder.cmd_buffer;
+ vkQueueSubmit(context.device->graphics_queue, 1, &submit_info, VK_NULL_HANDLE);
+
+ // Cleanup
+ vkQueueWaitIdle(context.device->graphics_queue);
+ gpu_cmd_encoder_destroy(&temp_encoder);
+ gpu_buffer_destroy(staging);
+ }
+ }
+
+ return handle;
+}
+
+void gpu_buffer_destroy(buffer_handle buffer) {
+ gpu_buffer* b = buffer_pool_get(&context.resource_pools->buffers, buffer);
+ vkDestroyBuffer(context.device->logical_device, b->handle, context.allocator);
+ vkFreeMemory(context.device->logical_device, b->memory, context.allocator);
+ buffer_pool_dealloc(&context.resource_pools->buffers, buffer);
+}
+
+// Upload data to a
+void buffer_upload_bytes(buffer_handle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size) {
+ gpu_buffer* buffer = buffer_pool_get(&context.resource_pools->buffers, gpu_buf);
+ void* data_ptr;
+ vkMapMemory(context.device->logical_device, buffer->memory, 0, size, 0, &data_ptr);
+ DEBUG("Uploading %d bytes to buffer", size);
+ memcpy(data_ptr, cpu_buf.buf, size);
+ vkUnmapMemory(context.device->logical_device, buffer->memory);
+}
+
+void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_offset,
+ buffer_handle dst, u64 dst_offset, u64 copy_size) {
+ VkBufferCopy copy_region;
+ copy_region.srcOffset = src_offset;
+ copy_region.dstOffset = dst_offset;
+ copy_region.size = copy_size;
+
+ gpu_buffer* src_buf = buffer_pool_get(&context.resource_pools->buffers, src);
+ gpu_buffer* dst_buf = buffer_pool_get(&context.resource_pools->buffers, dst);
+ vkCmdCopyBuffer(encoder->cmd_buffer, src_buf->handle, dst_buf->handle, 1, &copy_region);
+}
+
+// one-shot command buffers
+VkCommandBuffer vulkan_command_buffer_create_oneshot() {
+ VkCommandBufferAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
+ alloc_info.commandPool = context.device->pool;
+ alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ alloc_info.commandBufferCount = 1;
+ alloc_info.pNext = 0;
+
+ VkCommandBuffer cmd_buffer;
+ vkAllocateCommandBuffers(context.device->logical_device, &alloc_info, &cmd_buffer);
+
+ VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
+ begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+
+ vkBeginCommandBuffer(cmd_buffer, &begin_info);
+
+ return cmd_buffer;
+}
+
+void vulkan_command_buffer_finish_oneshot(VkCommandBuffer cmd_buffer) {
+ VK_CHECK(vkEndCommandBuffer(cmd_buffer));
+
+ // submit to queue
+ VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &cmd_buffer;
+ VK_CHECK(vkQueueSubmit(context.device->graphics_queue, 1, &submit_info, 0));
+ VK_CHECK(vkQueueWaitIdle(context.device->graphics_queue));
+
+ vkFreeCommandBuffers(context.device->logical_device, context.device->pool, 1, &cmd_buffer);
+}
+
+void copy_buffer_to_buffer_oneshot(buffer_handle src, u64 src_offset, buffer_handle dst,
+ u64 dst_offset, u64 copy_size) {
+ VkBufferCopy copy_region;
+ copy_region.srcOffset = src_offset;
+ copy_region.dstOffset = dst_offset;
+ copy_region.size = copy_size;
+
+ gpu_buffer* src_buf = buffer_pool_get(&context.resource_pools->buffers, src);
+ gpu_buffer* dst_buf = buffer_pool_get(&context.resource_pools->buffers, dst);
+ VkCommandBuffer temp_cmd_buffer = vulkan_command_buffer_create_oneshot();
+ vkCmdCopyBuffer(temp_cmd_buffer, src_buf->handle, dst_buf->handle, 1, &copy_region);
+ vulkan_command_buffer_finish_oneshot(temp_cmd_buffer);
+}
+
+void copy_buffer_to_image_oneshot(buffer_handle src, texture_handle dst) {
+ gpu_buffer* src_buf = buffer_pool_get(&context.resource_pools->buffers, src);
+ gpu_texture* dst_tex = texture_pool_get(&context.resource_pools->textures, dst);
+
+ VkCommandBuffer temp_cmd_buffer = vulkan_command_buffer_create_oneshot();
+
+ VkBufferImageCopy region;
+ region.bufferOffset = 0;
+ region.bufferRowLength = 0;
+ region.bufferImageHeight = 0;
+ region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.imageSubresource.mipLevel = 0;
+ region.imageSubresource.baseArrayLayer = 0;
+ region.imageSubresource.layerCount = 1;
+ printf("Image details width: %d height %d\n", dst_tex->desc.extents.x, dst_tex->desc.extents.y);
+ region.imageOffset.x = 0;
+ region.imageOffset.y = 0;
+ region.imageOffset.z = 0;
+ region.imageExtent.width = dst_tex->desc.extents.x;
+ region.imageExtent.height = dst_tex->desc.extents.y;
+ region.imageExtent.depth = 1;
+
+ vkCmdCopyBufferToImage(temp_cmd_buffer, src_buf->handle, dst_tex->handle,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+
+ vulkan_command_buffer_finish_oneshot(temp_cmd_buffer);
+}
+
+VkImage vulkan_image_create(u32x2 dimensions, VkImageType image_type, VkFormat format,
+ VkImageUsageFlags usage) {
+ VkImage image;
+
+ VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
+ image_create_info.imageType = VK_IMAGE_TYPE_2D;
+ image_create_info.extent.width = dimensions.x;
+ image_create_info.extent.height = dimensions.y;
+ image_create_info.extent.depth = 1;
+ image_create_info.mipLevels = 1;
+ image_create_info.arrayLayers = 1;
+ image_create_info.format = format;
+ image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+ image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ image_create_info.usage = usage; // VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+ image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
+
+ VK_CHECK(
+ vkCreateImage(context.device->logical_device, &image_create_info, context.allocator, &image));
+
+ return image;
+}
+
+texture_handle gpu_texture_create(texture_desc desc, bool create_view, const void* data) {
+ VkDeviceSize image_size = desc.extents.x * desc.extents.y * 4;
+ // FIXME: handle this properly
+ VkFormat format = desc.format == CEL_TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM ? VK_FORMAT_R8G8B8A8_SRGB
+ : VK_FORMAT_D32_SFLOAT;
+
+ VkImage image; // vulkan_image_create(desc.extents, VK_IMAGE_TYPE_2D, format,
+ // VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
+ VkDeviceMemory image_memory;
+
+ VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
+ image_create_info.imageType = VK_IMAGE_TYPE_2D;
+ image_create_info.extent.width = desc.extents.x;
+ image_create_info.extent.height = desc.extents.y;
+ image_create_info.extent.depth = 1;
+ image_create_info.mipLevels = 1;
+ image_create_info.arrayLayers = 1;
+ image_create_info.format = format;
+ image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+ image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+ if (format == VK_FORMAT_D32_SFLOAT) {
+ image_create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ }
+ image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
+
+ VK_CHECK(
+ vkCreateImage(context.device->logical_device, &image_create_info, context.allocator, &image));
+
+ VkMemoryRequirements memory_reqs;
+ vkGetImageMemoryRequirements(context.device->logical_device, image, &memory_reqs);
+
+ VkMemoryAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ alloc_info.allocationSize = memory_reqs.size;
+ alloc_info.memoryTypeIndex =
+ find_memory_index(memory_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ vkAllocateMemory(context.device->logical_device, &alloc_info, context.allocator, &image_memory);
+
+ vkBindImageMemory(context.device->logical_device, image, image_memory, 0);
+
+ texture_handle handle;
+ gpu_texture* texture = texture_pool_alloc(&context.resource_pools->textures, &handle);
+ DEBUG("Allocated texture with handle %d", handle.raw);
+ texture->handle = image;
+ texture->debug_label = "Test Texture";
+ texture->desc = desc;
+ texture->memory = image_memory;
+ texture->size = image_size;
+
+ if (data) {
+ TRACE("Uploading pixel data to texture using staging buffer");
+ // Create a staging buffer
+ buffer_handle staging =
+ gpu_buffer_create(image_size, CEL_BUFFER_DEFAULT, CEL_BUFFER_FLAG_CPU, NULL);
+ // Copy data into it
+ vulkan_transition_image_layout(texture, format, VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+ buffer_upload_bytes(staging, (bytebuffer){ .buf = (u8*)data, .size = image_size }, 0,
+ image_size);
+ copy_buffer_to_image_oneshot(staging, handle);
+ vulkan_transition_image_layout(texture, format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ gpu_buffer_destroy(staging);
+ }
+
+ // Texture View
+ if (create_view) {
+ VkImageViewCreateInfo view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
+ view_create_info.image = image;
+ view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ view_create_info.format = format;
+ view_create_info.subresourceRange.aspectMask =
+ format == VK_FORMAT_D32_SFLOAT ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
+
+ view_create_info.subresourceRange.baseMipLevel = 0;
+ view_create_info.subresourceRange.levelCount = 1;
+ view_create_info.subresourceRange.baseArrayLayer = 0;
+ view_create_info.subresourceRange.layerCount = 1;
+
+ VK_CHECK(vkCreateImageView(context.device->logical_device, &view_create_info, context.allocator,
+ &texture->view));
+ }
+
+ // Sampler
+ VkSamplerCreateInfo sampler_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
+ sampler_info.magFilter = VK_FILTER_LINEAR;
+ sampler_info.minFilter = VK_FILTER_LINEAR;
+ sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.anisotropyEnable = VK_TRUE;
+ sampler_info.maxAnisotropy = 16;
+ sampler_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
+ sampler_info.unnormalizedCoordinates = VK_FALSE;
+ sampler_info.compareEnable = VK_FALSE;
+ sampler_info.compareOp = VK_COMPARE_OP_ALWAYS;
+ sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ sampler_info.mipLodBias = 0.0;
+ sampler_info.minLod = 0.0;
+ sampler_info.maxLod = 0.0;
+
+ VkResult res = vkCreateSampler(context.device->logical_device, &sampler_info, context.allocator,
+ &texture->sampler);
+ if (res != VK_SUCCESS) {
+ ERROR("Error creating texture sampler for image %s", texture->debug_label);
+ return;
+ }
+
+ return handle;
+}
+
+void vulkan_transition_image_layout(gpu_texture* texture, VkFormat format, VkImageLayout old_layout,
+ VkImageLayout new_layout) {
+ VkCommandBuffer temp_cmd_buffer = vulkan_command_buffer_create_oneshot();
+
+ VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
+ barrier.oldLayout = old_layout;
+ barrier.newLayout = new_layout;
+ barrier.srcQueueFamilyIndex = context.device->queue_family_indicies.graphics_family_index;
+ barrier.dstQueueFamilyIndex = context.device->queue_family_indicies.graphics_family_index;
+ barrier.image = texture->handle;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = 1;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = 1;
+ barrier.srcAccessMask = 0; // TODO
+ barrier.dstAccessMask = 0; // TODO
+
+ VkPipelineStageFlags source_stage;
+ VkPipelineStageFlags dest_stage;
+
+ if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED &&
+ new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dest_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+ } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
+ new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dest_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ } else {
+ FATAL("Unsupported image layout transition");
+ return;
+ }
+
+ vkCmdPipelineBarrier(temp_cmd_buffer, source_stage, dest_stage, 0, 0, 0, 0, 0, 1, &barrier);
+
+ vulkan_command_buffer_finish_oneshot(temp_cmd_buffer);
+}
+
+/* TYPED_POOL(gpu_buffer, buffer); */
+/* TYPED_POOL(gpu_texture, texture); */
+
+void resource_pools_init(arena* a, struct resource_pools* res_pools) {
+ buffer_pool buf_pool = buffer_pool_create(a, MAX_BUFFERS, sizeof(gpu_buffer));
+ res_pools->buffers = buf_pool;
+ texture_pool tex_pool = texture_pool_create(a, MAX_TEXTURES, sizeof(gpu_texture));
+ res_pools->textures = tex_pool;
+
+ context.resource_pools = res_pools;
+}
diff --git a/src/renderer/backends/backend_vulkan.h b/src/renderer/backends/backend_vulkan.h
new file mode 100644
index 0000000..dc0f7bd
--- /dev/null
+++ b/src/renderer/backends/backend_vulkan.h
@@ -0,0 +1,115 @@
+#pragma once
+#include <vulkan/vk_platform.h>
+#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_core.h>
+
+#include "defines.h"
+#include "mem.h"
+#include "ral.h"
+#include "ral_types.h"
+
+#define MAX_FRAMES_IN_FLIGHT 2
+#define GPU_SWAPCHAIN_IMG_COUNT 2
+
+/*
+Conventions:
+ - Place the 'handle' as the first field of a struct
+ - Vulkan specific data goes at the top, followed by our internal data
+*/
+
+typedef struct queue_family_indices {
+ u32 graphics_family_index;
+ u32 present_family_index;
+ u32 compute_family_index;
+ u32 transfer_family_index;
+ bool has_graphics;
+ bool has_present;
+ bool has_compute;
+ bool has_transfer;
+} queue_family_indices;
+
+// typedef struct vulkan_framebuffer {
+// } vulkan_framebuffer;
+
+typedef struct gpu_swapchain {
+ VkSwapchainKHR handle;
+ arena swapchain_arena;
+ VkExtent2D extent;
+ VkSurfaceFormatKHR image_format;
+ VkPresentModeKHR present_mode;
+ u32 image_count;
+ VkImage* images;
+ VkImageView* image_views;
+} gpu_swapchain;
+
+typedef struct gpu_device {
+ // In Vulkan we store both physical and logical device here
+ VkPhysicalDevice physical_device;
+ VkDevice logical_device;
+ VkPhysicalDeviceProperties properties;
+ VkPhysicalDeviceFeatures features;
+ VkPhysicalDeviceMemoryProperties memory;
+ queue_family_indices queue_family_indicies;
+ VkQueue graphics_queue;
+ VkQueue present_queue;
+ VkQueue compute_queue;
+ VkQueue transfer_queue;
+ VkCommandPool pool;
+} gpu_device;
+
+typedef struct gpu_pipeline_layout {
+ VkPipelineLayout handle;
+} gpu_pipeline_layout;
+
+typedef struct desc_set_uniform_buffer {
+ VkBuffer buffers[MAX_FRAMES_IN_FLIGHT];
+ VkDeviceMemory uniform_buf_memorys[MAX_FRAMES_IN_FLIGHT];
+ void* uniform_buf_mem_mappings[MAX_FRAMES_IN_FLIGHT];
+ size_t size;
+} desc_set_uniform_buffer;
+
+typedef struct gpu_pipeline {
+ VkPipeline handle;
+ VkPipelineLayout layout_handle;
+
+ // Descriptor gubbins
+ shader_data data_layouts[MAX_SHADER_DATA_LAYOUTS];
+ u32 data_layouts_count;
+
+ VkDescriptorSetLayout* desc_set_layouts;
+ // Based on group, we know which data to load
+ desc_set_uniform_buffer* uniform_pointers;
+ u32 desc_set_layouts_count;
+
+} gpu_pipeline;
+
+typedef struct gpu_renderpass {
+ VkRenderPass handle;
+ // TODO: Where to store framebuffers? VkFramebuffer framebuffers[GPU_SWAPCHAIN_IMG_COUNT];
+} gpu_renderpass;
+
+typedef struct gpu_cmd_encoder {
+ VkCommandBuffer cmd_buffer;
+ VkDescriptorPool descriptor_pool;
+ gpu_pipeline* pipeline;
+} gpu_cmd_encoder;
+
+typedef struct gpu_cmd_buffer {
+ VkCommandBuffer cmd_buffer;
+} gpu_cmd_buffer;
+
+typedef struct gpu_buffer {
+ VkBuffer handle;
+ VkDeviceMemory memory;
+ u64 size;
+} gpu_buffer;
+
+typedef struct gpu_texture {
+ VkImage handle;
+ VkDeviceMemory memory;
+ u64 size;
+ texture_desc desc;
+ VkImageView view;
+ VkSampler sampler;
+ char* debug_label;
+} gpu_texture;
diff --git a/src/renderer/backends/metal/README.md b/src/renderer/backends/metal/README.md
new file mode 100644
index 0000000..f87f5c1
--- /dev/null
+++ b/src/renderer/backends/metal/README.md
@@ -0,0 +1 @@
+# TODO \ No newline at end of file
diff --git a/src/renderer/backends/opengl/README.md b/src/renderer/backends/opengl/README.md
new file mode 100644
index 0000000..f87f5c1
--- /dev/null
+++ b/src/renderer/backends/opengl/README.md
@@ -0,0 +1 @@
+# TODO \ No newline at end of file
diff --git a/src/renderer/backends/vulkan/README.md b/src/renderer/backends/vulkan/README.md
new file mode 100644
index 0000000..220ed64
--- /dev/null
+++ b/src/renderer/backends/vulkan/README.md
@@ -0,0 +1 @@
+# Vulkan Backend Overview \ No newline at end of file
diff --git a/src/renderer/backends/vulkan/vulkan_glossary.md b/src/renderer/backends/vulkan/vulkan_glossary.md
new file mode 100644
index 0000000..4214f9d
--- /dev/null
+++ b/src/renderer/backends/vulkan/vulkan_glossary.md
@@ -0,0 +1,18 @@
+# Vulkan Glossary
+
+*from https://vkguide.dev/docs/introduction/vulkan_execution/*
+
+- **VkInstance**: The Vulkan context, used to access drivers.
+- **VkPhysicalDevice**: A GPU. Used to query physical GPU details, like features, capabilities, memory size, etc.
+- **VkDevice**: The “logical” GPU context that you actually execute things on.
+- **VkBuffer**: A chunk of GPU visible memory.
+- **VkImage**: A texture you can write to and read from.
+- **VkPipeline**: Holds the state of the gpu needed to draw. For example: shaders, rasterization options, depth settings.
+- **VkRenderPass**: Holds information about the images you are rendering into. All drawing commands have to be done inside a renderpass. Only used in legacy vkguide.
+- **VkFrameBuffer**: Holds the target images for a renderpass. Only used in legacy vkguide.
+- **VkCommandBuffer**: Encodes GPU commands. All execution that is performed on the GPU itself (not in the driver) has to be encoded in a VkCommandBuffer.
+- **VkQueue**: Execution “port” for commands. GPUs will have a set of queues with different properties. Some allow only graphics commands, others only allow memory commands, etc. Command buffers are executed by submitting them into a queue, which will copy the rendering commands onto the GPU for execution.
+- **VkDescriptorSet**: Holds the binding information that connects shader inputs to data such as VkBuffer resources and VkImage textures. Think of it as a set of gpu-side pointers that you bind once.
+- **VkSwapchainKHR**: Holds the images for the screen. It allows you to render things into a visible window. The KHR suffix shows that it comes from an extension, which in this case is VK_KHR_swapchain.
+- **VkSemaphore**: Synchronizes GPU to GPU execution of commands. Used for syncing multiple command buffer submissions one after another.
+- **VkFence**: Synchronizes GPU to CPU execution of commands. Used to know if a command buffer has finished being executed on the GPU.
diff --git a/src/renderer/backends/vulkan_helpers.h b/src/renderer/backends/vulkan_helpers.h
new file mode 100644
index 0000000..23666c6
--- /dev/null
+++ b/src/renderer/backends/vulkan_helpers.h
@@ -0,0 +1,199 @@
+#pragma once
+
+#include <assert.h>
+#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_core.h>
+
+#include "darray.h"
+#include "defines.h"
+#include "log.h"
+#include "str.h"
+
+#define VULKAN_PHYS_DEVICE_MAX_EXTENSION_NAMES 36
+
+DECL_TYPED_ARRAY(const char*, cstr)
+
+static void plat_get_required_extension_names(cstr_darray* extensions) {
+#ifdef CEL_PLATFORM_LINUX
+ cstr_darray_push(extensions, "VK_KHR_xcb_surface");
+#endif
+}
+
+// TODO(omni): port to using internal assert functions
+#define VK_CHECK(vulkan_expr) \
+ do { \
+ VkResult res = vulkan_expr; \
+ if (res != VK_SUCCESS) { \
+ ERROR_EXIT("Vulkan error: %u (%s:%d)", res, __FILE__, __LINE__); \
+ } \
+ } while (0)
+
+// TODO: typedef struct vk_debugger {} vk_debugger;
+
+typedef struct vulkan_physical_device_requirements {
+ bool graphics;
+ bool present;
+ bool compute;
+ bool transfer;
+ str8 device_ext_names[VULKAN_PHYS_DEVICE_MAX_EXTENSION_NAMES];
+ size_t device_ext_name_count;
+ bool sampler_anistropy;
+ bool discrete_gpu;
+} vulkan_physical_device_requirements;
+
+#define VULKAN_MAX_DEFAULT 32
+
+typedef struct vulkan_swapchain_support_info {
+ VkSurfaceCapabilitiesKHR capabilities;
+ VkSurfaceFormatKHR formats[VULKAN_MAX_DEFAULT];
+ u32 format_count;
+ VkPresentModeKHR present_modes[VULKAN_MAX_DEFAULT];
+ u32 mode_count;
+} vulkan_swapchain_support_info;
+
+VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags,
+ const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data);
+
+static void vulkan_device_query_swapchain_support(VkPhysicalDevice device, VkSurfaceKHR surface,
+ vulkan_swapchain_support_info* out_support_info) {
+ // TODO: add VK_CHECK to these calls!
+
+ // Surface capabilities
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &out_support_info->capabilities);
+
+ // Surface formats
+ vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &out_support_info->format_count,
+ 0); // Get number of formats
+ if (out_support_info->format_count > 0) {
+ vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &out_support_info->format_count,
+ out_support_info->formats);
+ }
+
+ // Present Modes
+ vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &out_support_info->mode_count,
+ 0); // Get number of formats
+ if (out_support_info->mode_count > 0) {
+ vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &out_support_info->mode_count,
+ out_support_info->present_modes);
+ }
+}
+
+static VkSurfaceFormatKHR choose_swapchain_format(
+ vulkan_swapchain_support_info* swapchain_support) {
+ assert(swapchain_support->format_count > 0);
+ // find a format
+ for (u32 i = 0; i < swapchain_support->format_count; i++) {
+ VkSurfaceFormatKHR format = swapchain_support->formats[i];
+ if (format.format == VK_FORMAT_B8G8R8A8_SRGB &&
+ format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+ return format;
+ }
+ }
+ return swapchain_support->formats[0];
+}
+
+// static bool physical_device_meets_requirements(
+// VkPhysicalDevice device, VkSurfaceKHR surface, const VkPhysicalDeviceProperties* properties,
+// const VkPhysicalDeviceFeatures* features,
+// const vulkan_physical_device_requirements* requirements,
+// vulkan_physical_device_queue_family_info* out_queue_info,
+// vulkan_swapchain_support_info* out_swapchain_support) {
+// // TODO: pass in an arena
+
+// out_queue_info->graphics_family_index = -1;
+// out_queue_info->present_family_index = -1;
+// out_queue_info->compute_family_index = -1;
+// out_queue_info->transfer_family_index = -1;
+
+// if (requirements->discrete_gpu) {
+// if (properties->deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
+// TRACE("Device is not a physical GPU. Skipping.");
+// return false;
+// }
+// }
+
+// u32 queue_family_count = 0;
+// vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, 0);
+// VkQueueFamilyProperties queue_families[queue_family_count];
+// vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families);
+
+// INFO("Graphics | Present | Compute | Transfer | Name");
+// u8 min_transfer_score = 255;
+// for (u32 i = 0; i < queue_family_count; i++) {
+// u8 current_transfer_score = 0;
+
+// // Graphics queue
+// if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+// out_queue_info->graphics_family_index = i;
+// current_transfer_score++;
+// }
+
+// // Compute queue
+// if (queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
+// out_queue_info->compute_family_index = i;
+// current_transfer_score++;
+// }
+
+// // Transfer queue
+// if (queue_families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) {
+// // always take the lowest score transfer index
+// if (current_transfer_score <= min_transfer_score) {
+// min_transfer_score = current_transfer_score;
+// out_queue_info->transfer_family_index = i;
+// }
+// }
+
+// // Present Queue
+// VkBool32 supports_present = VK_FALSE;
+// vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &supports_present);
+// if (supports_present) {
+// out_queue_info->present_family_index = i;
+// }
+// }
+
+// INFO(" %d | %d | %d | %d | %s",
+// out_queue_info->graphics_family_index != -1, out_queue_info->present_family_index != -1,
+// out_queue_info->compute_family_index != -1, out_queue_info->transfer_family_index != -1,
+// properties->deviceName);
+// TRACE("Graphics Family queue index: %d", out_queue_info->graphics_family_index);
+// TRACE("Present Family queue index: %d", out_queue_info->present_family_index);
+// TRACE("Compute Family queue index: %d", out_queue_info->compute_family_index);
+// TRACE("Transfer Family queue index: %d", out_queue_info->transfer_family_index);
+
+// if ((!requirements->graphics ||
+// (requirements->graphics && out_queue_info->graphics_family_index != -1))) {
+// INFO("Physical device meets our requirements! Proceed.");
+
+// vulkan_device_query_swapchain_support(
+// device, surface, out_swapchain_support
+
+// // TODO: error handling i.e. format count = 0 or present mode = 0
+
+// );
+// return true;
+// }
+
+// return false;
+// }
+
+VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags,
+ const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) {
+ switch (severity) {
+ default:
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
+ ERROR("%s", callback_data->pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
+ WARN("%s", callback_data->pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
+ INFO("%s", callback_data->pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
+ TRACE("%s", callback_data->pMessage);
+ break;
+ }
+ return VK_FALSE;
+} \ No newline at end of file
diff --git a/src/renderer/bind_group_layouts.h b/src/renderer/bind_group_layouts.h
new file mode 100644
index 0000000..246d1ef
--- /dev/null
+++ b/src/renderer/bind_group_layouts.h
@@ -0,0 +1,30 @@
+/**
+ * @file bind_group_layouts.h
+ * @author your name (you@domain.com)
+ * @brief Common bindgroups (descriptor set layouts)
+ * @version 0.1
+ * @date 2024-04-28
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+#include "defines.h"
+#include "maths_types.h"
+
+// Three major sets
+
+// 1. Scene / Global
+typedef struct bg_globals {
+ mat4 view;
+ mat4 projection;
+ f32 total_time;
+ f32 delta_time;
+} bg_globals;
+
+// 2. Material (once per object)
+
+// 3. Per draw call
+typedef struct bg_model {
+ mat4 model;
+} bg_model;
diff --git a/src/renderer/immediate.c b/src/renderer/immediate.c
new file mode 100644
index 0000000..e76d102
--- /dev/null
+++ b/src/renderer/immediate.c
@@ -0,0 +1,18 @@
+#include "immediate.h"
+#include "maths.h"
+#include "primitives.h"
+#include "ral_types.h"
+#include "render.h"
+
+void imm_draw_sphere(vec3 pos, f32 radius, vec4 colour) {
+ // Create the vertices
+ geometry_data geometry = geo_create_uvsphere(radius, 16, 16);
+ geo_set_vertex_colours(&geometry, colour);
+
+ // Upload to GPU
+ mat4 model = mat4_translation(pos);
+
+ // Set pipeline
+
+ // Draw
+} \ No newline at end of file
diff --git a/src/renderer/immediate.h b/src/renderer/immediate.h
new file mode 100644
index 0000000..b9d7c61
--- /dev/null
+++ b/src/renderer/immediate.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "geometry.h"
+#include "maths_types.h"
+
+// 3. SIMA (simplified immediate mode api) / render.h
+// - dont need to worry about uploading mesh data
+// - very useful for debugging
+void imm_draw_cuboid(vec3 pos, quat rotation, f32x3 extents, vec4 colour);
+void imm_draw_sphere(vec3 pos, f32 radius, vec4 colour);
+void imm_draw_camera_frustum();
+
+// static void imm_draw_model(
+// const char* model_filepath); // tracks internally whether the model is loaded
+
+// static void imm_draw_model(const char* model_filepath) {
+// check that model is loaded
+// if not loaded, load model and upload to gpu - LRU cache for models
+// else submit draw call
+// } \ No newline at end of file
diff --git a/src/renderer/ral.c b/src/renderer/ral.c
new file mode 100644
index 0000000..7d868be
--- /dev/null
+++ b/src/renderer/ral.c
@@ -0,0 +1,42 @@
+#include "ral.h"
+
+size_t vertex_attrib_size(vertex_attrib_type attr) {
+ switch (attr) {
+ case ATTR_F32:
+ case ATTR_U32:
+ case ATTR_I32:
+ return 4;
+ case ATTR_F32x2:
+ case ATTR_U32x2:
+ case ATTR_I32x2:
+ return 8;
+ case ATTR_F32x3:
+ case ATTR_U32x3:
+ case ATTR_I32x3:
+ return 12;
+ case ATTR_F32x4:
+ case ATTR_U32x4:
+ case ATTR_I32x4:
+ return 16;
+ break;
+ }
+}
+
+void vertex_desc_add(vertex_description* builder, const char* name, vertex_attrib_type type) {
+ u32 i = builder->attributes_count;
+
+ size_t size = vertex_attrib_size(type);
+ builder->attributes[i] = type;
+ builder->stride += size;
+ builder->attr_names[i] = name;
+
+ builder->attributes_count++;
+}
+
+vertex_description static_3d_vertex_description() {
+ vertex_description builder = { .debug_label = "vertex" };
+ vertex_desc_add(&builder, "position", ATTR_F32x3);
+ vertex_desc_add(&builder, "normal", ATTR_F32x3);
+ vertex_desc_add(&builder, "texCoords", ATTR_F32x2);
+ return builder;
+}
diff --git a/src/renderer/ral.h b/src/renderer/ral.h
new file mode 100644
index 0000000..376898f
--- /dev/null
+++ b/src/renderer/ral.h
@@ -0,0 +1,171 @@
+/**
+ * @file ral.h
+ * @author your name (you@domain.com)
+ * @brief Render Abstraction Layer
+ * @details API that a graphics backend *must* implement
+ * @version 0.1
+ * @date 2024-03-31
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+
+#include "buf.h"
+#include "defines.h"
+#include "mem.h"
+#include "ral_types.h"
+#include "str.h"
+
+// Unrelated forward declares
+struct GLFWwindow;
+
+// Forward declare structs - these must be defined in the backend implementation
+typedef struct gpu_swapchain gpu_swapchain;
+typedef struct gpu_device gpu_device;
+typedef struct gpu_pipeline_layout gpu_pipeline_layout;
+typedef struct gpu_pipeline gpu_pipeline;
+typedef struct gpu_renderpass gpu_renderpass;
+typedef struct gpu_cmd_encoder gpu_cmd_encoder; // Recording
+typedef struct gpu_cmd_buffer gpu_cmd_buffer; // Ready for submission
+typedef struct gpu_buffer gpu_buffer;
+typedef struct gpu_texture gpu_texture;
+
+#define MAX_SHADER_DATA_LAYOUTS 5
+#define MAX_BUFFERS 256
+#define MAX_TEXTURES 256
+
+TYPED_POOL(gpu_buffer, buffer);
+TYPED_POOL(gpu_texture, texture);
+
+// --- Pools
+typedef struct gpu_backend_pools { /* TODO: pools for each gpu structure */
+} gpu_backend_pools;
+
+struct resource_pools {
+ buffer_pool buffers;
+ texture_pool textures;
+};
+
+typedef enum pipeline_kind {
+ PIPELINE_GRAPHICS,
+ PIPELINE_COMPUTE,
+} pipeline_kind;
+
+typedef struct shader_desc {
+ const char* debug_name;
+ str8 filepath; // where it came from
+ str8 code; // Either GLSL or SPIRV bytecode
+ bool is_spirv;
+} shader_desc;
+
+struct graphics_pipeline_desc {
+ const char* debug_name;
+ vertex_description vertex_desc;
+ shader_desc vs; /** @brief Vertex shader stage */
+ shader_desc fs; /** @brief Fragment shader stage */
+
+ // Roughly equivalent to a descriptor set layout each. each layout can have multiple bindings
+ // examples:
+ // - uniform buffer reprensenting view projection matrix
+ // - texture for shadow map
+ shader_data data_layouts[MAX_SHADER_DATA_LAYOUTS];
+ u32 data_layouts_count;
+
+ // gpu_pipeline_layout* layout;
+ gpu_renderpass* renderpass;
+
+ bool wireframe;
+ bool depth_test;
+};
+
+typedef struct gpu_renderpass_desc { /* TODO */
+} gpu_renderpass_desc;
+
+// --- Lifecycle functions
+bool gpu_backend_init(const char* window_name, struct GLFWwindow* window);
+void gpu_backend_shutdown();
+void resource_pools_init(arena* a, struct resource_pools* res_pools);
+
+bool gpu_device_create(gpu_device* out_device);
+void gpu_device_destroy();
+
+// --- Render Pipeline
+gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description);
+void gpu_pipeline_destroy(gpu_pipeline* pipeline);
+
+// --- Renderpass
+gpu_renderpass* gpu_renderpass_create(const gpu_renderpass_desc* description);
+void gpu_renderpass_destroy(gpu_renderpass* pass);
+
+// --- Swapchain
+bool gpu_swapchain_create(gpu_swapchain* out_swapchain);
+void gpu_swapchain_destroy(gpu_swapchain* swapchain);
+
+// --- Command buffer
+gpu_cmd_encoder gpu_cmd_encoder_create();
+void gpu_cmd_encoder_destroy(gpu_cmd_encoder* encoder);
+void gpu_cmd_encoder_begin(gpu_cmd_encoder encoder);
+void gpu_cmd_encoder_begin_render(gpu_cmd_encoder* encoder, gpu_renderpass* renderpass);
+void gpu_cmd_encoder_end_render(gpu_cmd_encoder* encoder);
+void gpu_cmd_encoder_begin_compute();
+gpu_cmd_encoder* gpu_get_default_cmd_encoder();
+
+// --- Data copy commands
+/** @brief Copy data from one buffer to another */
+void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_offset,
+ buffer_handle dst, u64 dst_offset, u64 copy_size);
+/** @brief Upload CPU-side data as array of bytes to a GPU buffer */
+void buffer_upload_bytes(buffer_handle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size);
+
+/** @brief Copy data from buffer to buffer using a one time submit command buffer and a wait */
+void copy_buffer_to_buffer_oneshot(buffer_handle src, u64 src_offset, buffer_handle dst,
+ u64 dst_offset, u64 copy_size);
+/** @brief Copy data from buffer to an image using a one time submit command buffer */
+void copy_buffer_to_image_oneshot(buffer_handle src, texture_handle dst);
+
+// --- Render commands
+void encode_bind_pipeline(gpu_cmd_encoder* encoder, pipeline_kind kind, gpu_pipeline* pipeline);
+void encode_bind_shader_data(gpu_cmd_encoder* encoder, u32 group, shader_data* data);
+void encode_set_default_settings(gpu_cmd_encoder* encoder);
+void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
+void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
+void encode_set_bind_group(); // TODO
+void encode_draw(gpu_cmd_encoder* encoder);
+void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count);
+void encode_clear_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
+
+/** @brief Finish recording and return a command buffer that can be submitted to a queue */
+gpu_cmd_buffer gpu_cmd_encoder_finish(gpu_cmd_encoder* encoder);
+
+void gpu_queue_submit(gpu_cmd_buffer* buffer);
+
+// --- Buffers
+buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_flags flags,
+ const void* data);
+void gpu_buffer_destroy(buffer_handle buffer);
+void gpu_buffer_upload(const void* data);
+
+// Textures
+/** @brief Create a new GPU texture resource.
+ * @param create_view creates a texture view (with same dimensions) at the same time
+ * @param data if not NULL then the data stored at the pointer will be uploaded to the GPU texture
+ * @note automatically creates a sampler for you */
+texture_handle gpu_texture_create(texture_desc desc, bool create_view, const void* data);
+void gpu_texture_destroy(texture_handle);
+void gpu_texture_upload(texture_handle texture, const void* data);
+
+// --- Vertex formats
+bytebuffer vertices_as_bytebuffer(arena* a, vertex_format format, vertex_darray* vertices);
+
+void vertex_desc_add(vertex_description* builder, const char* name, vertex_attrib_type type);
+
+// --- TEMP
+bool gpu_backend_begin_frame();
+void gpu_backend_end_frame();
+void gpu_temp_draw(size_t n_verts);
+
+// TODO: --- Compute
+
+// --- Helpers
+vertex_description static_3d_vertex_description();
diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h
new file mode 100644
index 0000000..62a2f1d
--- /dev/null
+++ b/src/renderer/ral_types.h
@@ -0,0 +1,255 @@
+/**
+ * @file ral_types.h
+ * @author your name (you@domain.com)
+ * @brief Struct and enum definitions for RAL
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+
+#include "darray.h"
+#include "defines.h"
+#include "maths_types.h"
+
+#define MAX_VERTEX_ATTRIBUTES 16
+
+/* #ifndef RENDERER_TYPED_HANDLES */
+CORE_DEFINE_HANDLE(buffer_handle);
+CORE_DEFINE_HANDLE(texture_handle);
+CORE_DEFINE_HANDLE(sampler_handle);
+CORE_DEFINE_HANDLE(shader_handle);
+CORE_DEFINE_HANDLE(model_handle);
+#define ABSENT_MODEL_HANDLE 999999999
+
+/* #define RENDERER_TYPED_HANDLES */
+/* #endif */
+
+/* typedef struct gpu_buffer { */
+/* u32 a; */
+/* } gpu_buffer; */
+
+/* #ifndef RAL_TYPED_POOLS */
+/* #define RAL_TYPED_POOLS */
+/* #endif */
+
+// gpu types
+typedef enum gpu_primitive_topology {
+ CEL_PRIMITIVE_TOPOLOGY_POINT,
+ CEL_PRIMITIVE_TOPOLOGY_LINE,
+ CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP,
+ CEL_PRIMITIVE_TOPOLOGY_TRIANGLE,
+ CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
+ CEL_PRIMITIVE_TOPOLOGY_COUNT
+} cel_primitive_topology;
+
+typedef enum gpu_texture_type {
+ CEL_TEXTURE_TYPE_2D,
+ CEL_TEXTURE_TYPE_3D,
+ CEL_TEXTURE_TYPE_2D_ARRAY,
+ CEL_TEXTURE_TYPE_CUBE_MAP,
+ CEL_TEXTURE_TYPE_COUNT
+} gpu_texture_type;
+
+typedef enum gpu_texture_format {
+ CEL_TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM,
+ CEL_TEXTURE_FORMAT_DEPTH_DEFAULT,
+ CEL_TEXTURE_FORMAT_COUNT
+} gpu_texture_format;
+
+/** @brief Texture Description - used by texture creation functions */
+typedef struct texture_desc {
+ gpu_texture_type tex_type;
+ gpu_texture_format format;
+ u32x2 extents;
+} texture_desc;
+
+typedef enum gpu_buffer_type {
+ CEL_BUFFER_DEFAULT, // on Vulkan this would be a storage buffer?
+ CEL_BUFFER_VERTEX,
+ CEL_BUFFER_INDEX,
+ CEL_BUFFER_UNIFORM,
+ CEL_BUFFER_COUNT
+} gpu_buffer_type;
+
+typedef enum gpu_buffer_flag {
+ CEL_BUFFER_FLAG_CPU = 1 << 0,
+ CEL_BUFFER_FLAG_GPU = 1 << 1,
+ CEL_BUFFER_FLAG_STORAGE = 1 << 2,
+ CEL_BUFFER_FLAG_COUNT
+} gpu_buffer_flag;
+typedef u32 gpu_buffer_flags;
+
+typedef enum vertex_format {
+ VERTEX_STATIC_3D,
+ VERTEX_SPRITE,
+ VERTEX_SKINNED,
+ VERTEX_COLOURED_STATIC_3D,
+ VERTEX_RAW_POS_COLOUR,
+ VERTEX_COUNT
+} vertex_format;
+
+typedef union vertex {
+ struct {
+ vec3 position;
+ vec3 normal;
+ vec2 tex_coords;
+ } static_3d; /** @brief standard vertex format for static geometry in 3D */
+
+ struct {
+ vec2 position;
+ vec4 colour;
+ vec2 tex_coords;
+ } sprite; /** @brief vertex format for 2D sprites or quads */
+
+ struct {
+ vec3 position;
+ vec4 colour;
+ vec2 tex_coords;
+ vec3 normal;
+ vec4i bone_ids; // Integer vector for bone IDs
+ vec4 bone_weights; // Weight of each bone's influence
+ } skinned_3d; /** @brief vertex format for skeletal (animated) geometry in 3D */
+
+ struct {
+ vec3 position;
+ vec2 tex_coords;
+ vec3 normal;
+ vec4 colour;
+ } coloured_static_3d; /** @brief vertex format used for debugging */
+
+ struct {
+ vec2 position;
+ vec3 colour;
+ } raw_pos_colour;
+} vertex;
+
+#ifndef TYPED_VERTEX_ARRAY
+KITC_DECL_TYPED_ARRAY(vertex)
+KITC_DECL_TYPED_ARRAY(u32)
+#define TYPED_VERTEX_ARRAY
+#endif
+
+// TEMP
+typedef struct custom_vertex {
+ vec2 pos;
+ vec3 color;
+} custom_vertex;
+
+// Vertex attributes
+/// @strip_prefix(ATTR_)
+typedef enum vertex_attrib_type {
+ ATTR_F32,
+ ATTR_F32x2,
+ ATTR_F32x3,
+ ATTR_F32x4,
+ ATTR_U32,
+ ATTR_U32x2,
+ ATTR_U32x3,
+ ATTR_U32x4,
+ ATTR_I32,
+ ATTR_I32x2,
+ ATTR_I32x3,
+ ATTR_I32x4,
+} vertex_attrib_type;
+
+typedef struct vertex_description {
+ char* debug_label;
+ const char* attr_names[MAX_VERTEX_ATTRIBUTES];
+ vertex_attrib_type attributes[MAX_VERTEX_ATTRIBUTES];
+ u32 attributes_count;
+ size_t stride;
+ bool use_full_vertex_size;
+} vertex_description;
+
+// --- Shaders & Bindings
+
+typedef enum shader_visibility {
+ VISIBILITY_VERTEX = 1 << 0,
+ VISIBILITY_FRAGMENT = 1 << 1,
+ VISIBILITY_COMPUTE = 1 << 2,
+} shader_visibility;
+
+/** @brief Describes the kind of binding a `shader_binding` is for. This changes how we create
+ * backing data for it. */
+typedef enum shader_binding_type {
+ /**
+ * @brief Binds a buffer to a shader
+ * @note Vulkan: Becomes a Storage Buffer
+ */
+ SHADER_BINDING_BUFFER,
+ SHADER_BINDING_BUFFER_ARRAY,
+ SHADER_BINDING_TEXTURE,
+ SHADER_BINDING_TEXTURE_ARRAY,
+ SHADER_BINDING_SAMPLER,
+ /**
+ * @brief Binds raw data to a shader
+ * @note Vulkan: Becomes a Uniform Buffer
+ */
+ SHADER_BINDING_BYTES,
+ // TODO: Acceleration Structure
+ SHADER_BINDING_COUNT
+} shader_binding_type;
+
+// pub trait ShaderBindable: Clone + Copy {
+// fn bind_to(&self, context: &mut PipelineContext, index: u32);
+// }
+
+typedef struct shader_binding {
+ const char* label;
+ shader_binding_type type;
+ shader_visibility vis;
+ bool stores_data; /** @brief if this is true then the shader binding has references to live data,
+ if false then its just being used to describe a layout and .data
+ should be zeroed */
+ union {
+ struct {
+ buffer_handle handle;
+ } buffer;
+ struct {
+ void* data;
+ size_t size;
+ } bytes;
+ struct {
+ texture_handle handle;
+ } texture;
+ } data; /** @brief can store any kind of data that we can bind to a shader / descriptor set */
+} shader_binding;
+
+#define MAX_LAYOUT_BINDINGS 8
+
+/** @brief A list of bindings that describe what data a shader / pipeline expects
+ @note This roughly correlates to a descriptor set layout in Vulkan
+*/
+typedef struct shader_data_layout {
+ char* name;
+ shader_binding bindings[MAX_LAYOUT_BINDINGS];
+ u32 bindings_count;
+} shader_data_layout;
+
+typedef struct shader_data {
+ shader_data_layout (*shader_data_get_layout)(void* data);
+ void* data;
+} shader_data;
+
+/*
+ Usage:
+ 1. When we create the pipeline, we must call a function that return a layout without .data
+ fields
+ 2. When binding
+*/
+
+typedef enum gpu_cull_mode { CULL_BACK_FACE, CULL_FRONT_FACE, CULL_COUNT } gpu_cull_mode;
+
+// ? How to tie together materials and shaders
+
+// Three registers
+// 1. low level graphics api calls "ral"
+// 2. higher level render calls
+// 3. simplified immediate mode API
+
+// 3 - you don't need to know how the renderer works at all
+// 2 - you need to know how the overall renderer is designed
+// 1 - you need to understand graphics API specifics
diff --git a/src/renderer/render.c b/src/renderer/render.c
index ac6b80a..5723c9e 100644
--- a/src/renderer/render.c
+++ b/src/renderer/render.c
@@ -1,47 +1,40 @@
-#include <stdlib.h>
-#include "mem.h"
-#include "transform_hierarchy.h"
+#include <glfw3.h>
+#include "maths_types.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
-#define STB_TRUETYPE_IMPLEMENTATION
-#include <stb_truetype.h>
-
-#include "render.h"
-#include "render_types.h"
-
-#include <glad/glad.h>
-#include <glfw3.h>
-
-#include "defines.h"
+#include "camera.h"
+#include "file.h"
#include "log.h"
-#include "maths.h"
-#include "render_backend.h"
-
-// FIXME: get rid of these and store dynamic screen realestate
-// in renderer
-#define SCR_WIDTH 1080
-#define SCR_HEIGHT 800
+#include "mem.h"
+#include "ral.h"
+#include "ral_types.h"
+#include "render.h"
-material DEFAULT_MATERIAL = { 0 };
+/** @brief Creates the pipelines built into Celeritas such as rendering static opaque geometry,
+ debug visualisations, immediate mode UI, etc */
+void default_pipelines_init(renderer* ren);
bool renderer_init(renderer* ren) {
- INFO("Renderer init");
+ // INFO("Renderer init");
// NOTE: all platforms use GLFW at the moment but thats subject to change
glfwInit();
- DEBUG("init graphics api (OpenGL) backend");
+#if defined(CEL_REND_BACKEND_OPENGL)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+#elif defined(CEL_REND_BACKEND_VULKAN)
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+#endif
// glfw window creation
GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height,
ren->config.window_name, NULL, NULL);
if (window == NULL) {
- printf("Failed to create GLFW window\n");
+ // ERROR("Failed to create GLFW window\n");
glfwTerminate();
return false;
}
@@ -49,205 +42,174 @@ bool renderer_init(renderer* ren) {
glfwMakeContextCurrent(ren->window);
- if (!gfx_backend_init(ren)) {
+ DEBUG("Start gpu backend init");
+
+ if (!gpu_backend_init("Celeritas Engine - Vulkan", window)) {
FATAL("Couldnt load graphics api backend");
return false;
}
+ gpu_device_create(&ren->device); // TODO: handle errors
+ gpu_swapchain_create(&ren->swapchain);
- ren->blinn_phong =
- shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag");
+ DEBUG("Initialise GPU resource pools");
+ arena pool_arena = arena_create(malloc(1024 * 1024), 1024 * 1024);
+ ren->resource_pools = arena_alloc(&pool_arena, sizeof(struct resource_pools));
+ resource_pools_init(&pool_arena, ren->resource_pools);
- default_material_init();
+ // ren->blinn_phong =
+ // shader_create_separate("assets/shaders/blinn_phong.vert",
+ // "assets/shaders/blinn_phong.frag");
- return true;
-}
+ // ren->skinned =
+ // shader_create_separate("assets/shaders/skinned.vert", "assets/shaders/blinn_phong.frag");
-void render_frame_begin(renderer* ren) {
- vec3 color = ren->config.clear_colour;
- clear_screen(color);
-}
-void render_frame_end(renderer* ren) {
- // present frame
- glfwSwapBuffers(ren->window);
- glfwPollEvents();
-}
+ // default_material_init();
-void default_material_init() {
- INFO("Load default material")
- DEFAULT_MATERIAL.ambient_colour = (vec3){ 0.5, 0.5, 0.5 };
- DEFAULT_MATERIAL.diffuse = (vec3){ 0.8, 0.8, 0.8 };
- DEFAULT_MATERIAL.specular = (vec3){ 1.0, 1.0, 1.0 };
- DEFAULT_MATERIAL.diffuse_texture = texture_data_load("assets/textures/white1x1.png", false);
- DEFAULT_MATERIAL.specular_texture = texture_data_load("assets/textures/black1x1.png", false);
- DEFAULT_MATERIAL.spec_exponent = 32.0;
- strcpy(DEFAULT_MATERIAL.name, "Default");
- texture_data_upload(&DEFAULT_MATERIAL.diffuse_texture);
- texture_data_upload(&DEFAULT_MATERIAL.specular_texture);
-}
+ // Create default rendering pipeline
+ /* default_pipelines_init(ren); */
-typedef struct draw_ctx {
- model_darray* models;
- renderer* ren;
- camera* cam;
- scene* scene;
-} draw_ctx;
-bool draw_scene_node(transform_node* node, void* ctx_data) {
- if (!node || !node->parent) return true;
- draw_ctx* ctx = ctx_data;
- model* m = &ctx->models->data[node->model.raw];
- draw_model(ctx->ren, ctx->cam, m, &node->world_matrix_tf, ctx->scene);
return true;
}
-
-void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera,
- transform_hierarchy* tfh, scene* scene) {
- draw_ctx* ctx = arena_alloc(frame, sizeof(draw_ctx));
- ctx->models = models;
- ctx->ren = ren;
- ctx->cam = camera;
- ctx->scene = scene;
- transform_hierarchy_dfs(transform_hierarchy_root_node(tfh), draw_scene_node, true, ctx);
+void renderer_shutdown(renderer* ren) {
+ gpu_swapchain_destroy(&ren->swapchain);
+ gpu_pipeline_destroy(&ren->static_opaque_pipeline);
+ gpu_backend_shutdown();
}
-void draw_model(renderer* ren, camera* camera, model* model, mat4* model_tf, scene* scene) {
- // TRACE("Drawing model: %s", model->name);
- mat4 view;
- mat4 proj;
- camera_view_projection(camera, SCR_HEIGHT, SCR_WIDTH, &view, &proj);
+void default_pipelines_init(renderer* ren) {
+ // Static opaque geometry
+ arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024);
+
+ gpu_renderpass_desc pass_description = {};
+ gpu_renderpass* renderpass = gpu_renderpass_create(&pass_description);
+
+ ren->default_renderpass = *renderpass;
+
+ printf("Load shaders\n");
+ str8 vert_path = str8lit("build/linux/x86_64/debug/triangle.vert.spv");
+ str8 frag_path = str8lit("build/linux/x86_64/debug/triangle.frag.spv");
+ /* str8 vert_path =
+ * str8lit("/home/void/code/celeritas-engine/celeritas-core/build/linux/x86_64/debug/triangle.vert.spv");
+ */
+ /* str8 frag_path =
+ * str8lit("/home/void/code/celeritas-engine/celeritas-core/build/linux/x86_64/debug/triangle.frag.spv");
+ */
+ str8_opt vertex_shader = str8_from_file(&scratch, vert_path);
+ str8_opt fragment_shader = str8_from_file(&scratch, frag_path);
+ if (!vertex_shader.has_value || !fragment_shader.has_value) {
+ ERROR_EXIT("Failed to load shaders from disk")
+ }
- set_shader(ren->blinn_phong);
+ vertex_description vertex_input;
+ vertex_input.debug_label = "Standard Static 3D Vertex Format";
+ vertex_desc_add(&vertex_input, "inPosition", ATTR_F32x3);
+ vertex_desc_add(&vertex_input, "inNormal", ATTR_F32x3);
+ vertex_desc_add(&vertex_input, "inTexCoords", ATTR_F32x2);
+
+ struct graphics_pipeline_desc pipeline_description = {
+ .debug_name = "Basic Pipeline",
+ .vertex_desc = vertex_input,
+ // .data_layouts
+ // .data_layouts_count
+ .vs = { .debug_name = "Triangle Vertex Shader",
+ .filepath = vert_path,
+ .code = vertex_shader.contents,
+ .is_spirv = true },
+ .fs = { .debug_name = "Triangle Fragment Shader",
+ .filepath = frag_path,
+ .code = fragment_shader.contents,
+ .is_spirv = true },
+ .renderpass = renderpass,
+ .wireframe = false,
+ .depth_test = false
+ };
+ gpu_pipeline* gfx_pipeline = gpu_graphics_pipeline_create(pipeline_description);
+ ren->static_opaque_pipeline = *gfx_pipeline;
+}
- // set camera uniform
- uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &camera->position);
- // set light uniforms
- dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light);
- for (int i = 0; i < scene->n_point_lights; i++) {
- point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i);
+void render_frame_begin(renderer* ren) {
+ ren->frame_aborted = false;
+ if (!gpu_backend_begin_frame()) {
+ ren->frame_aborted = true;
+ return;
+ }
+ gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder();
+ // begin recording
+ gpu_cmd_encoder_begin(*enc);
+ gpu_cmd_encoder_begin_render(enc, &ren->default_renderpass);
+ encode_bind_pipeline(enc, PIPELINE_GRAPHICS, &ren->static_opaque_pipeline);
+ encode_set_default_settings(enc);
+}
+void render_frame_end(renderer* ren) {
+ if (ren->frame_aborted) {
+ return;
}
+ // gpu_temp_draw(3);
+ gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder();
+ gpu_cmd_encoder_end_render(enc);
+ gpu_cmd_buffer buf = gpu_cmd_encoder_finish(enc);
+ gpu_queue_submit(&buf);
+ gpu_backend_end_frame();
+}
+void render_frame_draw(renderer* ren) {}
- for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) {
- mesh* m = &model->meshes->data[i];
- if (vertex_darray_len(m->vertices) == 0) {
- continue;
- }
- // TRACE("Drawing mesh %d", i);
- material* mat = &model->materials->data[m->material_index];
- draw_mesh(ren, m, model_tf, mat, &view, &proj);
+void draw_mesh(mesh* mesh, mat4* model) { // , mat4* view, mat4* proj) {
+ gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder();
+ encode_set_vertex_buffer(enc, mesh->vertex_buffer);
+ if (mesh->has_indices) {
+ encode_set_index_buffer(enc, mesh->index_buffer);
}
+ // Assume this has already been done
+ /* encode_bind_shader_data(enc, 0, &mvp_uniforms_data); */
+ encode_draw_indexed(enc, mesh->index_count);
}
-void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* view, mat4* proj) {
- shader lighting_shader = ren->blinn_phong;
+void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex) {}
- // bind buffer
- bind_mesh_vertex_buffer(ren->backend_state, mesh);
+void geo_set_vertex_colours(geometry_data* geo, vec4 colour) {}
- // bind textures
- bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0
- bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1
- uniform_f32(lighting_shader.program_id, "material.shininess", 32.);
+// --- NEW
- // upload model transform
- // mat4 trans = mat4_translation(tf.position);
- // mat4 rot = mat4_rotation(tf.rotation);
- // mat4 scale = mat4_scale(tf.scale);
- // mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale));
+mesh mesh_create(geometry_data* geometry, bool free_on_upload) {
+ mesh m = { 0 };
- uniform_mat4f(lighting_shader.program_id, "model", model_tf);
- // upload view & projection matrices
- uniform_mat4f(lighting_shader.program_id, "view", view);
- uniform_mat4f(lighting_shader.program_id, "projection", proj);
+ // Create and upload vertex buffer
+ size_t vert_bytes = geometry->vertices->len * sizeof(vertex);
+ INFO("Creating vertex buffer with size %d (%d x %d)", vert_bytes, geometry->vertices->len,
+ sizeof(vertex));
+ m.vertex_buffer = gpu_buffer_create(vert_bytes, CEL_BUFFER_VERTEX, CEL_BUFFER_FLAG_GPU,
+ geometry->vertices->data);
- // draw triangles
- u32 num_vertices = vertex_darray_len(mesh->vertices);
- draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices);
-}
+ // Create and upload index buffer
+ size_t index_bytes = geometry->indices.len * sizeof(u32);
+ INFO("Creating index buffer with size %d (len: %d)", index_bytes, geometry->indices.len);
+ m.index_buffer =
+ gpu_buffer_create(index_bytes, CEL_BUFFER_INDEX, CEL_BUFFER_FLAG_GPU, geometry->indices.data);
-void model_upload_meshes(renderer* ren, model* model) {
- INFO("Upload mesh vertex data to GPU for model %s", model->name);
-
- size_t num_meshes = mesh_darray_len(model->meshes);
- u32 VBOs[num_meshes];
- u32 VAOs[num_meshes];
- glGenBuffers(num_meshes, VBOs);
- glGenVertexArrays(num_meshes, VAOs);
-
- u64 total_verts = 0;
-
- TRACE("num meshes %d", num_meshes);
-
- // upload each mesh to the GPU
- for (int mesh_i = 0; mesh_i < num_meshes; mesh_i++) {
- model->meshes->data[mesh_i].vao = VAOs[mesh_i];
- model->meshes->data[mesh_i].vbo = VBOs[mesh_i];
- // 3. bind buffers
- glBindBuffer(GL_ARRAY_BUFFER, VBOs[mesh_i]);
-
- size_t num_vertices = vertex_darray_len(model->meshes->data[mesh_i].vertices);
- // TRACE("Uploading vertex array data: %d verts", num_vertices);
- total_verts += num_vertices;
-
- // TODO: convert this garbage into a function
- f32 verts[num_vertices * 8];
- // for each face
- for (int i = 0; i < (num_vertices / 3); i++) {
- // for each vert in face
- for (int j = 0; j < 3; j++) {
- size_t stride = (i * 24) + j * 8;
- // printf("i: %d, stride: %ld, loc %d\n", i, stride, i * 3 + j);
- vertex vert = model->meshes->data[mesh_i].vertices->data[i];
- // printf("pos %f %f %f\n", vert.position.x, vert.position.y, vert.position.z);
- // printf("norm %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z);
- // printf("tex %f %f\n", vert.uv.x, vert.uv.y);
- verts[stride + 0] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.x;
- verts[stride + 1] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.y;
- verts[stride + 2] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.z;
- verts[stride + 3] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.x;
- verts[stride + 4] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.y;
- verts[stride + 5] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.z;
- verts[stride + 6] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.x;
- verts[stride + 7] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.y;
- }
- }
-
- // 4. upload data
- glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
-
- // 5. cont. set mesh vertex layout
- glBindVertexArray(model->meshes->data[mesh_i].vao);
- // position attribute
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
- glEnableVertexAttribArray(0);
- // normal vector attribute
- glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
- glEnableVertexAttribArray(1);
- // tex coords
- glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
- glEnableVertexAttribArray(2);
+ m.is_uploaded = true;
+ m.has_indices = geometry->has_indices;
+ m.index_count = geometry->indices.len;
+ m.vertices = geometry;
+ if (free_on_upload) {
+ geo_free_data(geometry);
}
- INFO("Uploaded %d submeshes with a total of %d vertices\n", num_meshes, total_verts);
+ // TODO: materials?
- // 6. reset buffer
- glBindBuffer(GL_ARRAY_BUFFER, 0);
+ return m;
}
-texture texture_data_load(const char* path, bool invert_y) {
+// --- Textures
+
+texture_data texture_data_load(const char* path, bool invert_y) {
TRACE("Load texture %s", path);
// load the file data
- // texture loading
int width, height, num_channels;
stbi_set_flip_vertically_on_load(invert_y);
#pragma GCC diagnostic ignored "-Wpointer-sign"
- char* data = stbi_load(path, &width, &height, &num_channels, 0);
+ char* data = stbi_load(path, &width, &height, &num_channels, STBI_rgb_alpha);
if (data) {
DEBUG("loaded texture: %s", path);
} else {
@@ -260,66 +222,18 @@ texture texture_data_load(const char* path, bool invert_y) {
} else {
channel_type = GL_RGB;
}
+ texture_desc desc = { .extents = { width, height },
+ .format = CEL_TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM,
+ .tex_type = CEL_TEXTURE_TYPE_2D };
- return (texture){ .texture_id = 0,
- .width = width,
- .height = height,
- .channel_count = num_channels,
- .channel_type = channel_type,
- .name = "TODO: Texture names",
- .image_data = data };
+ return (texture_data){ .description = desc, .image_data = data };
}
-void texture_data_upload(texture* tex) {
- printf("Texture name %s\n", tex->name);
- TRACE("Upload texture data");
- u32 texture_id;
- glGenTextures(1, &texture_id);
- glBindTexture(GL_TEXTURE_2D, texture_id);
- tex->texture_id = texture_id;
-
- // set the texture wrapping parameters
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
- GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- // set texture filtering parameters
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex->width, tex->height, 0, tex->channel_type,
- GL_UNSIGNED_BYTE, tex->image_data);
- glGenerateMipmap(GL_TEXTURE_2D);
- DEBUG("Freeing texture image data after uploading to GPU");
- // stbi_image_free(tex->image_data); // data is on gpu now so we dont need it around
-}
-
-void dir_light_upload_uniforms(shader shader, directional_light* light) {
- uniform_vec3f(shader.program_id, "dirLight.direction", &light->direction);
- uniform_vec3f(shader.program_id, "dirLight.ambient", &light->ambient);
- uniform_vec3f(shader.program_id, "dirLight.diffuse", &light->diffuse);
- uniform_vec3f(shader.program_id, "dirLight.specular", &light->specular);
+texture_handle texture_data_upload(texture_data data, bool free_on_upload) {
+ texture_handle handle = gpu_texture_create(data.description, true, data.image_data);
+ if (free_on_upload) {
+ TRACE("Freed stb_image data");
+ stbi_image_free(data.image_data);
+ }
+ return handle;
}
-
-void point_light_upload_uniforms(shader shader, point_light* light, char index) {
- char position_str[] = "pointLights[x].position";
- position_str[12] = (char)index;
- char ambient_str[] = "pointLights[x].ambient";
- ambient_str[12] = (char)index;
- char diffuse_str[] = "pointLights[x].diffuse";
- diffuse_str[12] = (char)index;
- char specular_str[] = "pointLights[x].specular";
- specular_str[12] = (char)index;
- char constant_str[] = "pointLights[x].constant";
- constant_str[12] = (char)index;
- char linear_str[] = "pointLights[x].linear";
- linear_str[12] = (char)index;
- char quadratic_str[] = "pointLights[x].quadratic";
- quadratic_str[12] = (char)index;
- uniform_vec3f(shader.program_id, position_str, &light->position);
- uniform_vec3f(shader.program_id, ambient_str, &light->ambient);
- uniform_vec3f(shader.program_id, diffuse_str, &light->diffuse);
- uniform_vec3f(shader.program_id, specular_str, &light->specular);
- uniform_f32(shader.program_id, constant_str, light->constant);
- uniform_f32(shader.program_id, linear_str, light->linear);
- uniform_f32(shader.program_id, quadratic_str, light->quadratic);
-} \ No newline at end of file
diff --git a/src/renderer/render.h b/src/renderer/render.h
index 1a35488..c193ff9 100644
--- a/src/renderer/render.h
+++ b/src/renderer/render.h
@@ -1,39 +1,73 @@
+/**
+ * @file render.h
+ * @author your name (you@domain.com)
+ * @brief Renderer frontend
+ * @version 0.1
+ * @date 2024-03-21
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
#pragma once
-#include "camera.h"
-#include "loaders.h"
+#include "ral_types.h"
#include "render_types.h"
-#include "transform_hierarchy.h"
-// --- Lifecycle
-/** @brief initialise the render system frontend */
bool renderer_init(renderer* ren);
-/** @brief shutdown the render system frontend */
void renderer_shutdown(renderer* ren);
-// --- Frame
-
void render_frame_begin(renderer* ren);
+void render_frame_update_globals(renderer* ren);
void render_frame_end(renderer* ren);
+void render_frame_draw(renderer* ren);
+
+// ! TEMP
+typedef struct camera camera;
+void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex);
+
+typedef struct render_ctx {
+ mat4 view;
+ mat4 projection;
+} render_ctx;
+
+// frontend -- these can be called from say a loop in an example, or via FFI
+texture_handle texture_create(const char* debug_name, texture_desc description, const u8* data);
+
+// Frontend Resources
+texture_data texture_data_load(const char* path, bool invert_y);
+
+/**
+ * @brief
+ *
+ * @param data
+ * @param free_on_upload frees the CPU-side pixel data stored in `data`
+ * @return texture_handle
+ */
+texture_handle texture_data_upload(texture_data data, bool free_on_upload);
+
+buffer_handle buffer_create(const char* debug_name, u64 size);
+bool buffer_destroy(buffer_handle buffer);
+sampler_handle sampler_create();
+
+void shader_hot_reload(const char* filepath);
+
+// models and meshes are implemented **in terms of the above**
+
+/**
+ * @brief Creates buffers and returns a struct that holds handles to our resources
+ *
+ * @param geometry
+ * @param free_on_upload frees the CPU-side vertex/index data stored in `geometry` when we
+ successfully upload that data to the GPU-side buffer
+ * @return mesh
+ */
+mesh mesh_create(geometry_data* geometry, bool free_on_upload);
+
+void draw_mesh(mesh* mesh, mat4* model); //, mat4* view, mat4* proj); // TODO: material
+
+model_handle model_load(const char* debug_name, const char* filepath);
+
+void geo_free_data(geometry_data* geo);
+void geo_set_vertex_colours(geometry_data* geo, vec4 colour);
-// --- models meshes
-void model_upload_meshes(renderer* ren, model* model);
-void draw_model(renderer* ren, camera* camera, model* model, mat4* tf, scene* scene);
-void draw_mesh(renderer* ren, mesh* mesh, mat4* tf, material* mat, mat4* view, mat4* proj);
-void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera,
- transform_hierarchy* tfh, scene* scene);
-
-// ---
-texture texture_data_load(const char* path, bool invert_y); // #frontend
-void texture_data_upload(texture* tex); // #backend
-
-// --- Uniforms
-
-/** @brief upload a vec3 of f32 to a uniform */
-void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value);
-/** @brief upload a single f32 to a uniform */
-void uniform_f32(u32 program_id, const char* uniform_name, f32 value);
-/** @brief upload a integer to a uniform */
-void uniform_i32(u32 program_id, const char* uniform_name, i32 value);
-/** @brief upload a mat4 of f32 to a uniform */
-void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value); \ No newline at end of file
+vertex_description static_3d_vertex_description();
diff --git a/src/renderer/render_backend.h b/src/renderer/render_backend.h
deleted file mode 100644
index 7e1c16c..0000000
--- a/src/renderer/render_backend.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @brief
- */
-#pragma once
-
-#include "maths_types.h"
-#include "render_types.h"
-
-/// --- Lifecycle
-bool gfx_backend_init(renderer* ren);
-void gfx_backend_shutdown(renderer* ren);
-
-void clear_screen(vec3 colour);
-
-void bind_texture(shader s, texture* tex, u32 slot);
-void bind_mesh_vertex_buffer(void* backend, mesh* mesh);
-void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count);
-
-shader shader_create_separate(const char* vert_shader, const char* frag_shader);
-void set_shader(shader s);
-
-// --- Uniforms
diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h
index aed18db..cc5fd93 100644
--- a/src/renderer/render_types.h
+++ b/src/renderer/render_types.h
@@ -1,32 +1,23 @@
/**
* @file render_types.h
- * @author Omniscient
- * @brief Type definitions for the majority of data required by the renderer system
- * @date 2024-02-24
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
*
*/
#pragma once
-#include "darray.h"
-#include "maths_types.h"
-#include "str.h"
-
-struct GLFWwindow;
-
-#define MAX_MATERIAL_NAME_LEN 256
-#define MAX_TEXTURE_NAME_LEN 256
-
-#ifndef RESOURCE_HANDLE_DEFS
-CORE_DEFINE_HANDLE(model_handle);
-#define ABSENT_MODEL_HANDLE 999999999
-CORE_DEFINE_HANDLE(texture_handle);
-#define RESOURCE_HANDLE_DEFS
+#include "ral.h"
+#include "ral_types.h"
+#if defined(CEL_PLATFORM_WINDOWS)
+// #include "backend_dx11.h"
#endif
+#include "backend_vulkan.h"
-/* @brief Opaque wrapper around a shader program */
-typedef struct shader {
- u32 program_id;
-} shader;
+struct GLFWwindow;
/** @brief configuration passed to the renderer at init time */
typedef struct renderer_config {
@@ -36,27 +27,58 @@ typedef struct renderer_config {
} renderer_config;
typedef struct renderer {
- struct GLFWwindow *window; /** Currently all platforms use GLFW*/
- void *backend_state; /** Graphics API-specific state */
+ struct GLFWwindow* window;
+ void* backend_context;
renderer_config config;
- // shaders
- shader blinn_phong;
+ gpu_device device;
+ gpu_swapchain swapchain;
+ gpu_renderpass default_renderpass;
+ gpu_pipeline static_opaque_pipeline;
+ bool frame_aborted;
+ struct resource_pools* resource_pools;
} renderer;
-// --- Lighting & Materials
+typedef struct geometry_data {
+ vertex_format format;
+ vertex_darray* vertices; // TODO: make it not a pointer
+ bool has_indices;
+ u32_darray indices;
+ vec3 colour; /** Optional: set vertex colours */
+} geometry_data;
+
+// 'Upload' a geometry_data (to GPU) -> get back a mesh
+typedef struct mesh {
+ buffer_handle vertex_buffer;
+ buffer_handle index_buffer;
+ u32 index_count;
+ bool has_indices;
+ geometry_data* vertices; // NULL means it has been freed
+ bool is_uploaded;
+ bool is_latent;
+} mesh;
+
+/* Hot reloading:
+C side - reload_model():
+ - load model from disk using existing loader
+ - remove from transform graph so it isnt tried to be drawn
+*/
+
+typedef struct model {
+ str8 name;
+ mesh* meshes;
+ u32 mesh_count;
+} model;
typedef struct texture {
- u32 texture_id;
- char name[MAX_TEXTURE_NAME_LEN];
- void *image_data;
- u32 width;
- u32 height;
- u8 channel_count;
- u32 channel_type;
} texture;
+typedef struct texture_data {
+ texture_desc description;
+ void* image_data;
+} texture_data;
+
typedef struct blinn_phong_material {
- char name[MAX_MATERIAL_NAME_LEN];
+ char name[256];
texture diffuse_texture;
char diffuse_tex_path[256];
texture specular_texture;
@@ -68,18 +90,49 @@ typedef struct blinn_phong_material {
bool is_loaded;
bool is_uploaded;
} blinn_phong_material;
-typedef blinn_phong_material material; // when we start using PBR, this will no longer be the case
+typedef blinn_phong_material material;
// the default blinn-phong material. MUST be initialised with the function below
extern material DEFAULT_MATERIAL;
void default_material_init();
+#ifndef TYPED_MESH_ARRAY
+KITC_DECL_TYPED_ARRAY(mesh)
+#define TYPED_MESH_ARRAY
+#endif
+
+#ifndef TYPED_MODEL_ARRAY
+KITC_DECL_TYPED_ARRAY(model)
+#define TYPED_MODEL_ARRAY
+#endif
+
#ifndef TYPED_MATERIAL_ARRAY
-KITC_DECL_TYPED_ARRAY(material) // creates "material_darray"
+KITC_DECL_TYPED_ARRAY(material)
#define TYPED_MATERIAL_ARRAY
#endif
-// lights
+#ifndef TYPED_ANIMATION_CLIP_ARRAY
+#include "animation.h"
+KITC_DECL_TYPED_ARRAY(animation_clip)
+#define TYPED_ANIMATION_CLIP_ARRAY
+#endif
+
+/** @brief Describes all the data required for the renderer to start executing draws */
+typedef struct render_entity {
+ /* buffer_handle index_buffer; */
+ /* u32 index_count; */
+ /* u32 index_offset; */
+ /* buffer_handle vertex_buffer; */
+ model_handle model;
+ transform tf;
+} render_entity;
+
+#ifndef TYPED_RENDER_ENTITY_ARRAY
+KITC_DECL_TYPED_ARRAY(render_entity)
+#define TYPED_RENDER_ENTITY_ARRAY
+#endif
+
+// --- Lights
typedef struct point_light {
vec3 position;
f32 constant, linear, quadratic;
@@ -94,85 +147,3 @@ typedef struct directional_light {
vec3 diffuse;
vec3 specular;
} directional_light;
-
-void point_light_upload_uniforms(shader shader, point_light *light, char index);
-void dir_light_upload_uniforms(shader shader, directional_light *light);
-
-// --- Models & Meshes
-
-/** @brief Vertex format for a static mesh */
-typedef struct vertex {
- vec3 position;
- vec3 normal;
- vec2 uv;
-} vertex;
-
-#ifndef TYPED_VERTEX_ARRAY
-KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray"
-#define TYPED_VERTEX_ARRAY
-#endif
-
-typedef struct mesh {
- vertex_darray *vertices;
- u32 vertex_size; /** size in bytes of each vertex including necessary padding */
- bool has_indices;
- u32 *indices;
- u32 indices_len;
- size_t material_index;
- u32 vbo, vao; /** OpenGL data. TODO: dont leak OpenGL details */
-} mesh;
-
-#ifndef TYPED_MESH_ARRAY
-KITC_DECL_TYPED_ARRAY(mesh) // creates "mesh_darray"
-#define TYPED_MESH_ARRAY
-#endif
-
-typedef struct model {
- str8 name;
- mesh_darray *meshes;
- aabb_3d bbox;
- material_darray *materials;
- bool is_loaded;
- bool is_uploaded;
-} model;
-
-#ifndef TYPED_MODEL_ARRAY
-KITC_DECL_TYPED_ARRAY(model) // creates "model_darray"
-#define TYPED_MODEL_ARRAY
-#endif
-
-// --- Scene
-
-// NOTE: This struct won't stay like this for a long time. It's somewhat temporary
-// in order to get a basic scene working without putting burden on the caller of
-// draw_model()
-typedef struct scene {
- directional_light dir_light;
- point_light point_lights[4];
- size_t n_point_lights;
-} scene;
-
-// --- Graphics API related
-
-typedef enum cel_primitive_topology {
- CEL_PRIMITIVE_TOPOLOGY_POINT,
- CEL_PRIMITIVE_TOPOLOGY_LINE,
- CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP,
- CEL_PRIMITIVE_TOPOLOGY_TRIANGLE,
- CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
- CEL_PRIMITIVE_TOPOLOGY_COUNT
-} cel_primitive_topology;
-
-typedef enum gpu_texture_type {
- TEXTURE_TYPE_2D,
- TEXTURE_TYPE_3D,
- TEXTURE_TYPE_2D_ARRAY,
- TEXTURE_TYPE_CUBE_MAP,
- TEXTURE_TYPE_COUNT
-} gpu_texture_type;
-
-typedef enum gpu_texture_format {
- TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM,
- TEXTURE_FORMAT_DEPTH_DEFAULT,
- TEXTURE_FORMAT_COUNT
-} gpu_texture_format;
diff --git a/src/renderer/renderpasses.h b/src/renderer/renderpasses.h
new file mode 100644
index 0000000..951ff6e
--- /dev/null
+++ b/src/renderer/renderpasses.h
@@ -0,0 +1,28 @@
+/**
+ * @file renderpasses.h
+ * @author your name (you@domain.com)
+ * @brief Built-in renderpasses to the engine
+ * @version 0.1
+ * @date 2024-04-28
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+#include "maths_types.h"
+#include "ral.h"
+#include "render.h"
+#include "render_types.h"
+
+// Shadowmap pass
+// Blinn-phong pass
+// Unlit pass
+// Debug visualisations pass
+
+// Don't need to pass in *anything*.
+gpu_renderpass* renderpass_blinn_phong_create();
+void renderpass_blinn_phong_execute(gpu_renderpass* pass, render_entity* entities,
+ size_t entity_count);
+
+gpu_renderpass* renderpass_shadows_create();
+void renderpass_shadows_execute(gpu_renderpass* pass, render_entity* entities, size_t entity_count); \ No newline at end of file
diff --git a/src/resources/gltf.c b/src/resources/gltf.c
index b269fcd..022bf95 100644
--- a/src/resources/gltf.c
+++ b/src/resources/gltf.c
@@ -1,19 +1,23 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include "animation.h"
#include "core.h"
#include "defines.h"
#include "file.h"
#include "loaders.h"
#include "log.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "mem.h"
#include "path.h"
#include "render.h"
+// #include "render_backend.h"
#include "render_types.h"
#include "str.h"
#define CGLTF_IMPLEMENTATION
#include <cgltf.h>
-// TODO: Port code from old repo
struct face {
cgltf_uint indices[3];
@@ -22,8 +26,10 @@ typedef struct face face;
KITC_DECL_TYPED_ARRAY(vec3)
KITC_DECL_TYPED_ARRAY(vec2)
-KITC_DECL_TYPED_ARRAY(u32)
+KITC_DECL_TYPED_ARRAY(vec4u)
+KITC_DECL_TYPED_ARRAY(vec4)
KITC_DECL_TYPED_ARRAY(face)
+// KITC_DECL_TYPED_ARRAY(joint)
bool model_load_gltf_str(const char *file_string, const char *filepath, str8 relative_path,
model *out_model, bool invert_textures_y);
@@ -42,7 +48,7 @@ model_handle model_load_gltf(struct core *core, const char *path, bool invert_te
model model = { 0 };
model.name = str8_cstr_view(path);
model.meshes = mesh_darray_new(1);
- model.materials = material_darray_new(1);
+ // model.materials = material_darray_new(1);
bool success =
model_load_gltf_str(file_string, path, relative_path.path, &model, invert_texture_y);
@@ -60,174 +66,426 @@ model_handle model_load_gltf(struct core *core, const char *path, bool invert_te
return (model_handle){ .raw = index };
}
+void assert_path_type_matches_component_type(cgltf_animation_path_type target_path,
+ cgltf_accessor *output) {
+ if (target_path == cgltf_animation_path_type_rotation) {
+ assert(output->component_type == cgltf_component_type_r_32f);
+ assert(output->type == cgltf_type_vec4);
+ }
+}
+
// TODO: Brainstorm how I can make this simpler and break it up into more testable pieces
bool model_load_gltf_str(const char *file_string, const char *filepath, str8 relative_path,
model *out_model, bool invert_textures_y) {
- TRACE("Load GLTF from string");
-
- // Setup temps
- vec3_darray *tmp_positions = vec3_darray_new(1000);
- vec3_darray *tmp_normals = vec3_darray_new(1000);
- vec2_darray *tmp_uvs = vec2_darray_new(1000);
- face_darray *tmp_faces = face_darray_new(1000);
-
- cgltf_options options = { 0 };
- cgltf_data *data = NULL;
- cgltf_result result = cgltf_parse_file(&options, filepath, &data);
- if (result != cgltf_result_success) {
- WARN("gltf load failed");
- // TODO: cleanup arrays(allocate all from arena ?)
- return false;
- }
-
- cgltf_load_buffers(&options, data, filepath);
- DEBUG("loaded buffers");
-
- // --- Materials
- TRACE("Num materials %d", data->materials_count);
- size_t num_materials = data->materials_count;
- for (size_t m = 0; m < num_materials; m++) {
- cgltf_material gltf_material = data->materials[m];
- material our_material = DEFAULT_MATERIAL;
-
- strcpy(our_material.name, gltf_material.name);
-
- cgltf_pbr_metallic_roughness pbr = gltf_material.pbr_metallic_roughness;
- if (gltf_material.has_pbr_metallic_roughness) {
- // we will use base color texture like blinn phong
- cgltf_texture_view diff_tex_view = pbr.base_color_texture;
-
- char diffuse_map_path[1024];
- snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf,
- diff_tex_view.texture->image->uri);
-
- strcpy(our_material.diffuse_tex_path, diffuse_map_path);
- texture diffuse_texture = texture_data_load(our_material.diffuse_tex_path, false);
- texture_data_upload(&diffuse_texture);
- our_material.diffuse_texture = diffuse_texture;
-
- cgltf_texture_view specular_tex_view = pbr.metallic_roughness_texture;
-
- char specular_map_path[1024];
- snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf,
- specular_tex_view.texture->image->uri);
-
- strcpy(our_material.specular_tex_path, specular_map_path);
- texture specular_texture = texture_data_load(our_material.specular_tex_path, false);
- texture_data_upload(&specular_texture);
- our_material.specular_texture = specular_texture;
- }
-
- material_darray_push(out_model->materials, our_material);
- }
-
- // --- Meshes
- TRACE("Num meshes %d", data->meshes_count);
- size_t num_meshes = data->meshes_count;
- for (size_t m = 0; m < num_meshes; m++) {
- cgltf_primitive primitive = data->meshes[m].primitives[0];
- DEBUG("Found %d attributes", primitive.attributes_count);
-
- for (int a = 0; a < data->meshes[m].primitives[0].attributes_count; a++) {
- cgltf_attribute attribute = data->meshes[m].primitives[0].attributes[a];
- if (attribute.type == cgltf_attribute_type_position) {
- TRACE("Load positions from accessor");
-
- cgltf_accessor *accessor = attribute.data;
- assert(accessor->component_type == cgltf_component_type_r_32f);
- // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Vertex positions should be a vec3");
-
- for (cgltf_size v = 0; v < accessor->count; ++v) {
- vec3 pos;
- cgltf_accessor_read_float(accessor, v, &pos.x, 3);
- vec3_darray_push(tmp_positions, pos);
- }
-
- } else if (attribute.type == cgltf_attribute_type_normal) {
- TRACE("Load normals from accessor");
-
- cgltf_accessor *accessor = attribute.data;
- assert(accessor->component_type == cgltf_component_type_r_32f);
- // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Normal vectors should be a vec3");
-
- for (cgltf_size v = 0; v < accessor->count; ++v) {
- vec3 pos;
- cgltf_accessor_read_float(accessor, v, &pos.x, 3);
- vec3_darray_push(tmp_normals, pos);
- }
-
- } else if (attribute.type == cgltf_attribute_type_texcoord) {
- TRACE("Load texture coordinates from accessor");
- cgltf_accessor *accessor = attribute.data;
- assert(accessor->component_type == cgltf_component_type_r_32f);
- // CASSERT_MSG(accessor->type == cgltf_type_vec2, "Texture coordinates should be a vec2");
-
- for (cgltf_size v = 0; v < accessor->count; ++v) {
- vec2 tex;
- bool success = cgltf_accessor_read_float(accessor, v, &tex.x, 2);
- if (!success) {
- ERROR("Error loading tex coord");
- }
- vec2_darray_push(tmp_uvs, tex);
- }
- } else if (attribute.type == cgltf_attribute_type_joints) {
- // TODO: handle joints
- } else {
- WARN("Unhandled cgltf_attribute_type: %s. skipping..", attribute.name);
- }
- }
-
- mesh mesh;
- mesh.vertices = vertex_darray_new(10);
-
- cgltf_accessor *indices = primitive.indices;
- if (primitive.indices > 0) {
- mesh.has_indices = true;
-
- mesh.indices = malloc(indices->count * sizeof(u32));
- mesh.indices_len = indices->count;
-
- // store indices
- for (cgltf_size i = 0; i < indices->count; ++i) {
- cgltf_uint ei;
- cgltf_accessor_read_uint(indices, i, &ei, 1);
- mesh.indices[i] = ei;
- }
-
- // fetch and store vertices for each index
- for (cgltf_size i = 0; i < indices->count; ++i) {
- vertex vert;
- cgltf_uint index = mesh.indices[i];
- vert.position = tmp_positions->data[index];
- vert.normal = tmp_normals->data[index];
- vert.uv = tmp_uvs->data[index];
- vertex_darray_push(mesh.vertices, vert);
- }
- } else {
- mesh.has_indices = false;
- return false; // TODO
- }
-
- if (primitive.material != NULL) {
- for (int i = 0; i < material_darray_len(out_model->materials); i++) {
- if (strcmp(primitive.material->name, out_model->materials->data[i].name)) {
- TRACE("Found material");
- mesh.material_index = i;
- break;
- }
- }
- }
-
- mesh_darray_push(out_model->meshes, mesh);
- }
-
- for (int i = 0; i < out_model->meshes->len; i++) {
- u32 mat_idx = out_model->meshes->data[i].material_index;
- printf("Mesh %d Mat index %d Mat name %s\n", i, mat_idx,
- out_model->materials->data[mat_idx].name);
- }
- return true;
+ return false;
+ // TRACE("Load GLTF from string");
+
+ // // Setup temps
+ // vec3_darray *tmp_positions = vec3_darray_new(1000);
+ // vec3_darray *tmp_normals = vec3_darray_new(1000);
+ // vec2_darray *tmp_uvs = vec2_darray_new(1000);
+ // vec4u_darray *tmp_joint_indices = vec4u_darray_new(1000);
+ // vec4_darray *tmp_weights = vec4_darray_new(1000);
+ // // FIXME
+ // // joint_darray *tmp_joints = joint_darray_new(256);
+ // // vertex_bone_data_darray *tmp_vertex_bone_data = vertex_bone_data_darray_new(1000);
+
+ // cgltf_options options = { 0 };
+ // cgltf_data *data = NULL;
+ // cgltf_result result = cgltf_parse_file(&options, filepath, &data);
+ // if (result != cgltf_result_success) {
+ // WARN("gltf load failed");
+ // // TODO: cleanup arrays(allocate all from arena ?)
+ // return false;
+ // }
+
+ // cgltf_load_buffers(&options, data, filepath);
+ // DEBUG("loaded buffers");
+
+ // // --- Skin
+ // size_t num_skins = data->skins_count;
+ // bool is_skinned = false;
+ // if (num_skins == 1) {
+ // is_skinned = true;
+ // } else if (num_skins > 1) {
+ // WARN("GLTF files with more than 1 skin are not supported");
+ // return false;
+ // }
+
+ // if (is_skinned) {
+ // cgltf_skin *gltf_skin = data->skins;
+ // DEBUG("loading skin %s", gltf_skin->name);
+ // size_t num_joints = gltf_skin->joints_count;
+ // DEBUG("# Joints %d", num_joints);
+
+ // cgltf_accessor *gltf_inverse_bind_matrices = gltf_skin->inverse_bind_matrices;
+
+ // // for each one we'll spit out a joint
+ // for (size_t i = 0; i < num_joints; i++) {
+ // cgltf_node *joint_node = gltf_skin->joints[i];
+
+ // joint joint_i = { .name = "testjoint" };
+ // if (joint_node->children_count > 0 && !joint_node->has_translation &&
+ // !joint_node->has_rotation) {
+ // WARN("joint Node with index %d is the root node", i);
+ // joint_i.transform_components = TRANSFORM_DEFAULT;
+ // } else {
+ // TRACE("Storing joint transform");
+ // joint_i.transform_components = TRANSFORM_DEFAULT;
+ // if (joint_node->has_translation) {
+ // memcpy(&joint_i.transform_components.position, &joint_node->translation, 3 *
+ // sizeof(f32));
+ // }
+ // if (joint_node->has_rotation) {
+ // memcpy(&joint_i.transform_components.rotation, &joint_node->rotation, 4 *
+ // sizeof(f32));
+ // }
+ // // TODO: support scaling as vec instead of float
+ // }
+ // joint_i.local_transform = transform_to_mat(&joint_i.transform_components);
+ // cgltf_accessor_read_float(gltf_inverse_bind_matrices, i,
+ // &joint_i.inverse_bind_matrix.data[0],
+ // 16);
+ // // joint_darray_push(tmp_joints, joint_i);
+ // }
+ // }
+
+ // // --- Materials
+ // TRACE("Num materials %d", data->materials_count);
+ // size_t num_materials = data->materials_count;
+ // for (size_t m = 0; m < num_materials; m++) {
+ // cgltf_material gltf_material = data->materials[m];
+ // material our_material = DEFAULT_MATERIAL;
+
+ // strcpy(our_material.name, gltf_material.name);
+
+ // cgltf_pbr_metallic_roughness pbr = gltf_material.pbr_metallic_roughness;
+ // if (gltf_material.has_pbr_metallic_roughness) {
+ // // we will use base color texture like blinn phong
+ // cgltf_texture_view diff_tex_view = pbr.base_color_texture;
+
+ // char diffuse_map_path[1024];
+ // snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf,
+ // diff_tex_view.texture->image->uri);
+
+ // strcpy(our_material.diffuse_tex_path, diffuse_map_path);
+ // texture diffuse_texture = texture_data_load(our_material.diffuse_tex_path, false);
+ // texture_data_upload(&diffuse_texture);
+ // our_material.diffuse_texture = diffuse_texture;
+
+ // cgltf_texture_view specular_tex_view = pbr.metallic_roughness_texture;
+
+ // char specular_map_path[1024];
+ // snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf,
+ // specular_tex_view.texture->image->uri);
+
+ // strcpy(our_material.specular_tex_path, specular_map_path);
+ // texture specular_texture = texture_data_load(our_material.specular_tex_path, false);
+ // texture_data_upload(&specular_texture);
+ // our_material.specular_texture = specular_texture;
+ // }
+
+ // // material_darray_push(out_model->materials, our_material);
+ // }
+
+ // // --- Meshes
+ // TRACE("Num meshes %d", data->meshes_count);
+ // size_t num_meshes = data->meshes_count;
+ // for (size_t m = 0; m < num_meshes; m++) {
+ // cgltf_primitive primitive = data->meshes[m].primitives[0];
+ // DEBUG("Found %d attributes", primitive.attributes_count);
+ // // DEBUG("Number of this primitive %d", primitive.)
+
+ // for (int a = 0; a < data->meshes[m].primitives[0].attributes_count; a++) {
+ // cgltf_attribute attribute = data->meshes[m].primitives[0].attributes[a];
+ // if (attribute.type == cgltf_attribute_type_position) {
+ // TRACE("Load positions from accessor");
+
+ // cgltf_accessor *accessor = attribute.data;
+ // assert(accessor->component_type == cgltf_component_type_r_32f);
+ // // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Vertex positions should be a vec3");
+
+ // TRACE("Loading %d vec3 components", accessor->count);
+
+ // for (cgltf_size v = 0; v < accessor->count; ++v) {
+ // vec3 pos;
+ // cgltf_accessor_read_float(accessor, v, &pos.x, 3);
+ // vec3_darray_push(tmp_positions, pos);
+ // }
+
+ // } else if (attribute.type == cgltf_attribute_type_normal) {
+ // TRACE("Load normals from accessor");
+
+ // cgltf_accessor *accessor = attribute.data;
+ // assert(accessor->component_type == cgltf_component_type_r_32f);
+ // // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Normal vectors should be a vec3");
+
+ // for (cgltf_size v = 0; v < accessor->count; ++v) {
+ // vec3 pos;
+ // cgltf_accessor_read_float(accessor, v, &pos.x, 3);
+ // vec3_darray_push(tmp_normals, pos);
+ // }
+
+ // } else if (attribute.type == cgltf_attribute_type_texcoord) {
+ // TRACE("Load texture coordinates from accessor");
+ // cgltf_accessor *accessor = attribute.data;
+ // assert(accessor->component_type == cgltf_component_type_r_32f);
+ // // CASSERT_MSG(accessor->type == cgltf_type_vec2, "Texture coordinates should be a
+ // vec2");
+
+ // for (cgltf_size v = 0; v < accessor->count; ++v) {
+ // vec2 tex;
+ // bool success = cgltf_accessor_read_float(accessor, v, &tex.x, 2);
+ // if (!success) {
+ // ERROR("Error loading tex coord");
+ // }
+ // vec2_darray_push(tmp_uvs, tex);
+ // }
+ // } else if (attribute.type == cgltf_attribute_type_joints) {
+ // TRACE("Load joint indices from accessor");
+ // cgltf_accessor *accessor = attribute.data;
+ // assert(accessor->component_type == cgltf_component_type_r_16u);
+ // assert(accessor->type == cgltf_type_vec4);
+ // vec4u joint_indices;
+ // vec4 joints_as_floats;
+ // for (cgltf_size v = 0; v < accessor->count; ++v) {
+ // cgltf_accessor_read_float(accessor, v, &joints_as_floats.x, 4);
+ // joint_indices.x = (u32)joints_as_floats.x;
+ // joint_indices.y = (u32)joints_as_floats.y;
+ // joint_indices.z = (u32)joints_as_floats.z;
+ // joint_indices.w = (u32)joints_as_floats.w;
+ // printf("Joints affecting vertex %d : %d %d %d %d\n", v, joint_indices.x,
+ // joint_indices.y,
+ // joint_indices.z, joint_indices.w);
+ // vec4u_darray_push(tmp_joint_indices, joint_indices);
+ // }
+
+ // } else if (attribute.type == cgltf_attribute_type_weights) {
+ // TRACE("Load joint weights from accessor");
+ // cgltf_accessor *accessor = attribute.data;
+ // assert(accessor->component_type == cgltf_component_type_r_32f);
+ // assert(accessor->type == cgltf_type_vec4);
+
+ // for (cgltf_size v = 0; v < accessor->count; ++v) {
+ // vec4 weights;
+ // cgltf_accessor_read_float(accessor, v, &weights.x, 4);
+ // printf("Weights affecting vertex %d : %f %f %f %f\n", v, weights.x, weights.y,
+ // weights.z,
+ // weights.w);
+ // vec4_darray_push(tmp_weights, weights);
+ // }
+ // } else {
+ // WARN("Unhandled cgltf_attribute_type: %s. skipping..", attribute.name);
+ // }
+ // }
+
+ // mesh mesh = { 0 };
+ // mesh.vertices = vertex_darray_new(10);
+ // // mesh.vertex_bone_data = vertex_bone_data_darray_new(1);
+
+ // if (primitive.material != NULL) {
+ // // for (int i = 0; i < material_darray_len(out_model->materials); i++) {
+ // // printf("%s vs %s \n", primitive.material->name, out_model->materials->data[i].name);
+ // // if (strcmp(primitive.material->name, out_model->materials->data[i].name) == 0) {
+ // // TRACE("Found material");
+ // // mesh.material_index = i;
+ // // break;
+ // // }
+ // // }
+ // }
+
+ // // FIXME
+ // // if (is_skinned) {
+ // // mesh.is_skinned = true;
+ // // // mesh.vertex_bone_data = vertex_bone_data_darray_new(tmp_joint_indices->len);
+ // // mesh.bones = joint_darray_new(tmp_joints->len);
+ // // for (int i = 0; i < tmp_joint_indices->len; i++) {
+ // // vertex_bone_data data;
+ // // data.joints = tmp_joint_indices->data[i];
+ // // data.weights = tmp_weights->data[i];
+ // // vertex_bone_data_darray_push(tmp_vertex_bone_data,
+ // // data); // Push the temp data that aligns with raw
+ // vertices
+ // // }
+ // // for (int i = 0; i < tmp_joints->len; i++) {
+ // // joint data = tmp_joints->data[i];
+ // // joint_darray_push(mesh.bones, data);
+ // // }
+ // // }
+
+ // cgltf_accessor *indices = primitive.indices;
+ // if (primitive.indices > 0) {
+ // WARN("indices!");
+ // mesh.has_indices = true;
+
+ // mesh.indices = malloc(indices->count * sizeof(u32));
+ // mesh.indices_len = indices->count;
+
+ // // store indices
+ // for (cgltf_size i = 0; i < indices->count; ++i) {
+ // cgltf_uint ei;
+ // cgltf_accessor_read_uint(indices, i, &ei, 1);
+ // mesh.indices[i] = ei;
+ // }
+
+ // // fetch and store vertices for each index
+ // for (cgltf_size i = 0; i < indices->count; ++i) {
+ // vertex vert;
+ // cgltf_uint index = mesh.indices[i];
+ // vert.position = tmp_positions->data[index];
+ // vert.normal = tmp_normals->data[index];
+ // vert.uv = tmp_uvs->data[index];
+ // vertex_darray_push(mesh.vertices, vert);
+
+ // if (is_skinned) {
+ // vertex_bone_data vbd = tmp_vertex_bone_data->data[index]; // create a copy
+ // vertex_bone_data_darray_push(mesh.vertex_bone_data, vbd);
+ // }
+ // // for each vertex do the bone data
+ // }
+ // } else {
+ // mesh.has_indices = false;
+ // return false; // TODO
+ // }
+
+ // mesh_darray_push(out_model->meshes, mesh);
+
+ // // clear data for each mesh
+ // vec3_darray_clear(tmp_positions);
+ // vec3_darray_clear(tmp_normals);
+ // vec2_darray_free(tmp_uvs);
+ // vec4u_darray_clear(tmp_joint_indices);
+ // vec4_darray_clear(tmp_weights);
+ // joint_darray_clear(tmp_joints);
+ // }
+
+ // for (int i = 0; i < out_model->meshes->len; i++) {
+ // u32 mat_idx = out_model->meshes->data[i].material_index;
+ // printf("Mesh %d Mat index %d Mat name %s\n", i, mat_idx,
+ // out_model->materials->data[mat_idx].name);
+ // }
+
+ // // Animations
+ // TRACE("Num animations %d", data->animations_count);
+ // size_t num_animations = data->animations_count;
+ // if (num_animations > 0) {
+ // // Create an arena for all animation related data
+ // #define ANIMATION_STORAGE_ARENA_SIZE (1024 * 1024 * 1024)
+ // char *animation_backing_storage = malloc(ANIMATION_STORAGE_ARENA_SIZE);
+ // // We'll store data on this arena so we can easily free it all at once later
+ // out_model->animation_data_arena =
+ // arena_create(animation_backing_storage, ANIMATION_STORAGE_ARENA_SIZE);
+ // arena *arena = &out_model->animation_data_arena;
+
+ // if (!out_model->animations) {
+ // out_model->animations = animation_clip_darray_new(num_animations);
+ // }
+
+ // for (int anim_idx = 0; anim_idx < data->animations_count; anim_idx++) {
+ // cgltf_animation animation = data->animations[anim_idx];
+ // animation_clip clip = { 0 };
+
+ // for (size_t c = 0; c < animation.channels_count; c++) {
+ // cgltf_animation_channel channel = animation.channels[c];
+
+ // animation_sampler *sampler = arena_alloc(arena, sizeof(animation_sampler));
+
+ // animation_sampler **target_property;
+ // keyframe_kind data_type;
+
+ // switch (channel.target_path) {
+ // case cgltf_animation_path_type_rotation:
+ // target_property = &clip.rotation;
+ // data_type = KEYFRAME_ROTATION;
+ // break;
+ // case cgltf_animation_path_type_translation:
+ // target_property = &clip.translation;
+ // data_type = KEYFRAME_TRANSLATION;
+ // break;
+ // case cgltf_animation_path_type_scale:
+ // target_property = &clip.scale;
+ // data_type = KEYFRAME_SCALE;
+ // break;
+ // case cgltf_animation_path_type_weights:
+ // target_property = &clip.weights;
+ // data_type = KEYFRAME_WEIGHTS;
+ // WARN("Morph target weights arent supported yet");
+ // return false;
+ // default:
+ // WARN("unsupported animation type");
+ // return false;
+ // }
+ // *target_property = sampler;
+
+ // sampler->current_index = 0;
+ // printf("1 %d index\n", sampler->current_index);
+ // sampler->animation.interpolation = INTERPOLATION_LINEAR;
+
+ // // keyframe times
+ // size_t n_frames = channel.sampler->input->count;
+ // assert(channel.sampler->input->component_type == cgltf_component_type_r_32f);
+ // // FIXME: CASSERT_MSG function "Expected animation sampler input component to be type
+ // f32
+ // // (keyframe times)");
+ // f32 *times = arena_alloc(arena, n_frames * sizeof(f32));
+ // sampler->animation.n_timestamps = n_frames;
+ // sampler->animation.timestamps = times;
+ // cgltf_accessor_unpack_floats(channel.sampler->input, times, n_frames);
+
+ // assert_path_type_matches_component_type(channel.target_path, channel.sampler->output);
+
+ // // keyframe values
+ // size_t n_values = channel.sampler->output->count;
+ // assert(n_frames == n_values);
+
+ // keyframes keyframes = { 0 };
+ // keyframes.kind = KEYFRAME_ROTATION;
+ // keyframes.count = n_values;
+ // keyframes.values = arena_alloc(arena, n_values * sizeof(keyframe));
+ // for (cgltf_size v = 0; v < channel.sampler->output->count; ++v) {
+ // switch (data_type) {
+ // case KEYFRAME_ROTATION: {
+ // quat rot;
+ // cgltf_accessor_read_float(channel.sampler->output, v, &rot.x, 4);
+ // // printf("Quat %f %f %f %f\n", rot.x, rot.y, rot.z, rot.w);
+ // keyframes.values[v].rotation = rot;
+ // break;
+ // }
+ // case KEYFRAME_TRANSLATION: {
+ // vec3 trans;
+ // cgltf_accessor_read_float(channel.sampler->output, v, &trans.x, 3);
+ // keyframes.values[v].translation = trans;
+ // break;
+ // }
+ // case KEYFRAME_SCALE: {
+ // vec3 scale;
+ // cgltf_accessor_read_float(channel.sampler->output, v, &scale.x, 3);
+ // keyframes.values[v].scale = scale;
+ // break;
+ // }
+ // case KEYFRAME_WEIGHTS: {
+ // // TODO
+ // break;
+ // }
+ // }
+ // }
+ // sampler->animation.values = keyframes;
+
+ // sampler->min = channel.sampler->input->min[0];
+ // sampler->max = channel.sampler->input->max[0];
+
+ // // clip.rotation = sampler;
+ // // printf("%d timestamps\n", sampler->animation.n_timestamps);
+ // // printf("%d index\n", sampler->current_index);
+ // }
+
+ // WARN("stuff %ld", clip.rotation->animation.n_timestamps);
+ // animation_clip_darray_push(out_model->animations, clip);
+ // }
+ // }
+
+ // return true;
}
/*
diff --git a/src/resources/obj.c b/src/resources/obj.c
index c6e9fa6..888e16e 100644
--- a/src/resources/obj.c
+++ b/src/resources/obj.c
@@ -18,6 +18,7 @@
#include "mem.h"
#include "path.h"
#include "render.h"
+// #include "render_backend.h"
#include "render_types.h"
#include "str.h"
@@ -54,7 +55,7 @@ model_handle model_load_obj(core *core, const char *path, bool invert_textures_y
model model = { 0 };
model.name = str8_cstr_view(path);
model.meshes = mesh_darray_new(1);
- model.materials = material_darray_new(1);
+ // model.materials = material_darray_new(1);
bool success = model_load_obj_str(file_string, relative_path.path, &model, invert_textures_y);
@@ -75,151 +76,157 @@ bool model_load_obj_str(const char *file_string, str8 relative_path, model *out_
bool invert_textures_y) {
TRACE("Load OBJ from string");
- // Setup temps
- vec3_darray *tmp_positions = vec3_darray_new(1000);
- vec3_darray *tmp_normals = vec3_darray_new(1000);
- vec2_darray *tmp_uvs = vec2_darray_new(1000);
- face_darray *tmp_faces = face_darray_new(1000);
- // TODO: In the future I'd like these temporary arrays to be allocated from an arena provided
- // by the function one level up, model_load_obj. That way we can just `return false;` anywhere in
- // this code to indicate an error, and be sure that all that memory will be cleaned up without
- // having to call vec3_darray_free in every single error case before returning.
-
- // Other state
- bool object_set = false;
- bool material_loaded = false;
- char current_material_name[64];
-
- char *pch;
- char *rest = file_string;
- pch = strtok_r((char *)file_string, "\n", &rest);
-
- int line_num = 0;
- char last_char_type = 'a';
-
- while (pch != NULL) {
- line_num++;
- char line_header[128];
- int offset = 0;
-
- // skip whitespace
- char *p = pch;
-
- skip_space(pch);
-
- if (*p == '\0') {
- /* the string is empty */
- } else {
- // read the first word of the line
- int res = sscanf(pch, "%s %n", line_header, &offset);
- /* printf("header: %s, offset : %d res: %d\n",line_header, offset, res); */
- if (res != 1) {
- break;
- }
-
- if (strcmp(line_header, "o") == 0 || strcmp(line_header, "g") == 0) {
- // if we're currently parsing one
- if (!object_set) {
- object_set = true;
- } else {
- create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
- out_model->materials, material_loaded, current_material_name);
- object_set = false;
- }
- } else if (strcmp(line_header, "v") == 0) {
- // special logic: if we went from faces back to vertices trigger a mesh output.
- // PS: I hate OBJ
- if (last_char_type == 'f') {
- create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
- out_model->materials, material_loaded, current_material_name);
- object_set = false;
- }
-
- last_char_type = 'v';
- vec3 vertex;
- sscanf(pch + offset, "%f %f %f", &vertex.x, &vertex.y, &vertex.z);
-
- vec3_darray_push(tmp_positions, vertex);
- } else if (strcmp(line_header, "vt") == 0) {
- last_char_type = 't';
- vec2 uv;
- char copy[1024];
- memcpy(copy, pch + offset, strlen(pch + offset) + 1);
- char *p = pch + offset;
- while (isspace((unsigned char)*p)) ++p;
-
- // I can't remember what is going on here
- memset(copy, 0, 1024);
- memcpy(copy, pch + offset, strlen(pch + offset) + 1);
- int res = sscanf(copy, "%f %f", &uv.x, &uv.y);
- memset(copy, 0, 1024);
- memcpy(copy, pch + offset, strlen(pch + offset) + 1);
- if (res != 1) {
- // da frick? some .obj files have 3 uvs instead of 2
- f32 dummy;
- int res2 = sscanf(copy, "%f %f %f", &uv.x, &uv.y, &dummy);
- }
-
- if (invert_textures_y) {
- uv.y = -uv.y; // flip Y axis to be consistent with how other PNGs are being handled
- // `texture_load` will flip it again
- }
- vec2_darray_push(tmp_uvs, uv);
- } else if (strcmp(line_header, "vn") == 0) {
- last_char_type = 'n';
- vec3 normal;
- sscanf(pch + offset, "%f %f %f", &normal.x, &normal.y, &normal.z);
- vec3_darray_push(tmp_normals, normal);
- } else if (strcmp(line_header, "f") == 0) {
- last_char_type = 'f';
- struct face f;
- sscanf(pch + offset, "%d/%d/%d %d/%d/%d %d/%d/%d", &f.vertex_indices[0], &f.uv_indices[0],
- &f.normal_indices[0], &f.vertex_indices[1], &f.uv_indices[1], &f.normal_indices[1],
- &f.vertex_indices[2], &f.uv_indices[2], &f.normal_indices[2]);
- // printf("f %d/%d/%d %d/%d/%d %d/%d/%d\n", f.vertex_indices[0], f.uv_indices[0],
- // f.normal_indices[0],
- // f.vertex_indices[1], f.uv_indices[1], f.normal_indices[1],
- // f.vertex_indices[2], f.uv_indices[2], f.normal_indices[2]);
- face_darray_push(tmp_faces, f);
- } else if (strcmp(line_header, "mtllib") == 0) {
- char filename[1024];
- sscanf(pch + offset, "%s", filename);
- char mtllib_path[1024];
- snprintf(mtllib_path, sizeof(mtllib_path), "%s/%s", relative_path.buf, filename);
- if (!load_material_lib(mtllib_path, relative_path, out_model->materials)) {
- ERROR("couldnt load material lib");
- return false;
- }
- } else if (strcmp(line_header, "usemtl") == 0) {
- material_loaded = true;
- sscanf(pch + offset, "%s", current_material_name);
- }
- }
-
- pch = strtok_r(NULL, "\n", &rest);
- }
-
- // last mesh or if one wasnt created with 'o' directive
- if (face_darray_len(tmp_faces) > 0) {
- TRACE("Last leftover mesh");
- create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
- out_model->materials, material_loaded, current_material_name);
- }
-
- // Free data
- free((char *)file_string);
- vec3_darray_free(tmp_positions);
- vec3_darray_free(tmp_normals);
- vec2_darray_free(tmp_uvs);
- face_darray_free(tmp_faces);
- TRACE("Freed temporary OBJ loading data");
-
- if (mesh_darray_len(out_model->meshes) > 256) {
- printf("num meshes: %ld\n", mesh_darray_len(out_model->meshes));
- }
-
- // TODO: bounding box calculation for each mesh
- // TODO: bounding box calculation for model
+ // // Setup temps
+ // vec3_darray *tmp_positions = vec3_darray_new(1000);
+ // vec3_darray *tmp_normals = vec3_darray_new(1000);
+ // vec2_darray *tmp_uvs = vec2_darray_new(1000);
+ // face_darray *tmp_faces = face_darray_new(1000);
+ // // TODO: In the future I'd like these temporary arrays to be allocated from an arena provided
+ // // by the function one level up, model_load_obj. That way we can just `return false;` anywhere
+ // in
+ // // this code to indicate an error, and be sure that all that memory will be cleaned up without
+ // // having to call vec3_darray_free in every single error case before returning.
+
+ // // Other state
+ // bool object_set = false;
+ // bool material_loaded = false;
+ // char current_material_name[64];
+
+ // char *pch;
+ // char *rest = file_string;
+ // pch = strtok_r((char *)file_string, "\n", &rest);
+
+ // int line_num = 0;
+ // char last_char_type = 'a';
+
+ // while (pch != NULL) {
+ // line_num++;
+ // char line_header[128];
+ // int offset = 0;
+
+ // // skip whitespace
+ // char *p = pch;
+
+ // skip_space(pch);
+
+ // if (*p == '\0') {
+ // /* the string is empty */
+ // } else {
+ // // read the first word of the line
+ // int res = sscanf(pch, "%s %n", line_header, &offset);
+ // /* printf("header: %s, offset : %d res: %d\n",line_header, offset, res); */
+ // if (res != 1) {
+ // break;
+ // }
+
+ // if (strcmp(line_header, "o") == 0 || strcmp(line_header, "g") == 0) {
+ // // if we're currently parsing one
+ // if (!object_set) {
+ // object_set = true;
+ // } else {
+ // create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
+ // NULL, // out_model->materials,
+ // material_loaded, current_material_name);
+ // object_set = false;
+ // }
+ // } else if (strcmp(line_header, "v") == 0) {
+ // // special logic: if we went from faces back to vertices trigger a mesh output.
+ // // PS: I hate OBJ
+ // if (last_char_type == 'f') {
+ // create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
+ // NULL, // FIXME: out_model->materials,
+ // material_loaded, current_material_name);
+ // object_set = false;
+ // }
+
+ // last_char_type = 'v';
+ // vec3 vertex;
+ // sscanf(pch + offset, "%f %f %f", &vertex.x, &vertex.y, &vertex.z);
+
+ // vec3_darray_push(tmp_positions, vertex);
+ // } else if (strcmp(line_header, "vt") == 0) {
+ // last_char_type = 't';
+ // vec2 uv;
+ // char copy[1024];
+ // memcpy(copy, pch + offset, strlen(pch + offset) + 1);
+ // char *p = pch + offset;
+ // while (isspace((unsigned char)*p)) ++p;
+
+ // // I can't remember what is going on here
+ // memset(copy, 0, 1024);
+ // memcpy(copy, pch + offset, strlen(pch + offset) + 1);
+ // int res = sscanf(copy, "%f %f", &uv.x, &uv.y);
+ // memset(copy, 0, 1024);
+ // memcpy(copy, pch + offset, strlen(pch + offset) + 1);
+ // if (res != 1) {
+ // // da frick? some .obj files have 3 uvs instead of 2
+ // f32 dummy;
+ // int res2 = sscanf(copy, "%f %f %f", &uv.x, &uv.y, &dummy);
+ // }
+
+ // if (invert_textures_y) {
+ // uv.y = -uv.y; // flip Y axis to be consistent with how other PNGs are being handled
+ // // `texture_load` will flip it again
+ // }
+ // vec2_darray_push(tmp_uvs, uv);
+ // } else if (strcmp(line_header, "vn") == 0) {
+ // last_char_type = 'n';
+ // vec3 normal;
+ // sscanf(pch + offset, "%f %f %f", &normal.x, &normal.y, &normal.z);
+ // vec3_darray_push(tmp_normals, normal);
+ // } else if (strcmp(line_header, "f") == 0) {
+ // last_char_type = 'f';
+ // struct face f;
+ // sscanf(pch + offset, "%d/%d/%d %d/%d/%d %d/%d/%d", &f.vertex_indices[0],
+ // &f.uv_indices[0],
+ // &f.normal_indices[0], &f.vertex_indices[1], &f.uv_indices[1],
+ // &f.normal_indices[1], &f.vertex_indices[2], &f.uv_indices[2],
+ // &f.normal_indices[2]);
+ // // printf("f %d/%d/%d %d/%d/%d %d/%d/%d\n", f.vertex_indices[0], f.uv_indices[0],
+ // // f.normal_indices[0],
+ // // f.vertex_indices[1], f.uv_indices[1], f.normal_indices[1],
+ // // f.vertex_indices[2], f.uv_indices[2], f.normal_indices[2]);
+ // face_darray_push(tmp_faces, f);
+ // } else if (strcmp(line_header, "mtllib") == 0) {
+ // char filename[1024];
+ // sscanf(pch + offset, "%s", filename);
+ // char mtllib_path[1024];
+ // snprintf(mtllib_path, sizeof(mtllib_path), "%s/%s", relative_path.buf, filename);
+ // if (!load_material_lib(mtllib_path, relative_path, out_model->materials)) {
+ // ERROR("couldnt load material lib");
+ // return false;
+ // }
+ // } else if (strcmp(line_header, "usemtl") == 0) {
+ // material_loaded = true;
+ // sscanf(pch + offset, "%s", current_material_name);
+ // }
+ // }
+
+ // pch = strtok_r(NULL, "\n", &rest);
+ // }
+
+ // // last mesh or if one wasnt created with 'o' directive
+ // if (face_darray_len(tmp_faces) > 0) {
+ // TRACE("Last leftover mesh");
+ // create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
+ // NULL, // TODO: out_model->materials,
+ // material_loaded, current_material_name);
+ // }
+
+ // // Free data
+ // free((char *)file_string);
+ // vec3_darray_free(tmp_positions);
+ // vec3_darray_free(tmp_normals);
+ // vec2_darray_free(tmp_uvs);
+ // face_darray_free(tmp_faces);
+ // TRACE("Freed temporary OBJ loading data");
+
+ // if (mesh_darray_len(out_model->meshes) > 256) {
+ // printf("num meshes: %ld\n", mesh_darray_len(out_model->meshes));
+ // }
+
+ // // TODO: bounding box calculation for each mesh
+ // // TODO: bounding box calculation for model
return true;
}
@@ -231,158 +238,159 @@ bool model_load_obj_str(const char *file_string, str8 relative_path, model *out_
void create_submesh(mesh_darray *meshes, vec3_darray *tmp_positions, vec3_darray *tmp_normals,
vec2_darray *tmp_uvs, face_darray *tmp_faces, material_darray *materials,
bool material_loaded, char current_material_name[256]) {
- size_t num_verts = face_darray_len(tmp_faces) * 3;
- vertex_darray *out_vertices = vertex_darray_new(num_verts);
-
- face_darray_iter face_iter = face_darray_iter_new(tmp_faces);
- struct face *f;
-
- while ((f = face_darray_iter_next(&face_iter))) {
- for (int j = 0; j < 3; j++) {
- vertex vert = { 0 };
- vert.position = tmp_positions->data[f->vertex_indices[j] - 1];
- if (vec3_darray_len(tmp_normals) == 0) {
- vert.normal = vec3_create(0.0, 0.0, 0.0);
- } else {
- vert.normal = tmp_normals->data[f->normal_indices[j] - 1];
- }
- vert.uv = tmp_uvs->data[f->uv_indices[j] - 1];
- vertex_darray_push(out_vertices, vert);
- }
- }
-
- DEBUG("Loaded submesh\n vertices: %zu\n uvs: %zu\n normals: %zu\n faces: %zu",
- vec3_darray_len(tmp_positions), vec2_darray_len(tmp_uvs), vec3_darray_len(tmp_normals),
- face_darray_len(tmp_faces));
-
- // Clear current object faces
- face_darray_clear(tmp_faces);
-
- mesh m = { .vertices = out_vertices };
- if (material_loaded) {
- // linear scan to find material
- bool found = false;
- DEBUG("Num of materials : %ld", material_darray_len(materials));
- material_darray_iter mat_iter = material_darray_iter_new(materials);
- blinn_phong_material *cur_material;
- while ((cur_material = material_darray_iter_next(&mat_iter))) {
- if (strcmp(cur_material->name, current_material_name) == 0) {
- DEBUG("Found match");
- m.material_index = mat_iter.current_idx - 1;
- found = true;
- break;
- }
- }
-
- if (!found) {
- // TODO: default material
- m.material_index = 0;
- DEBUG("Set default material");
- }
- }
- mesh_darray_push(meshes, m);
+ // size_t num_verts = face_darray_len(tmp_faces) * 3;
+ // vertex_darray *out_vertices = vertex_darray_new(num_verts);
+
+ // face_darray_iter face_iter = face_darray_iter_new(tmp_faces);
+ // struct face *f;
+
+ // while ((f = face_darray_iter_next(&face_iter))) {
+ // for (int j = 0; j < 3; j++) {
+ // vertex vert = { 0 };
+ // vert.position = tmp_positions->data[f->vertex_indices[j] - 1];
+ // if (vec3_darray_len(tmp_normals) == 0) {
+ // vert.normal = vec3_create(0.0, 0.0, 0.0);
+ // } else {
+ // vert.normal = tmp_normals->data[f->normal_indices[j] - 1];
+ // }
+ // vert.uv = tmp_uvs->data[f->uv_indices[j] - 1];
+ // vertex_darray_push(out_vertices, vert);
+ // }
+ // }
+
+ // DEBUG("Loaded submesh\n vertices: %zu\n uvs: %zu\n normals: %zu\n faces: %zu",
+ // vec3_darray_len(tmp_positions), vec2_darray_len(tmp_uvs), vec3_darray_len(tmp_normals),
+ // face_darray_len(tmp_faces));
+
+ // // Clear current object faces
+ // face_darray_clear(tmp_faces);
+
+ // mesh m = { .vertices = out_vertices };
+ // if (material_loaded) {
+ // // linear scan to find material
+ // bool found = false;
+ // DEBUG("Num of materials : %ld", material_darray_len(materials));
+ // material_darray_iter mat_iter = material_darray_iter_new(materials);
+ // blinn_phong_material *cur_material;
+ // while ((cur_material = material_darray_iter_next(&mat_iter))) {
+ // if (strcmp(cur_material->name, current_material_name) == 0) {
+ // DEBUG("Found match");
+ // m.material_index = mat_iter.current_idx - 1;
+ // found = true;
+ // break;
+ // }
+ // }
+
+ // if (!found) {
+ // // TODO: default material
+ // m.material_index = 0;
+ // DEBUG("Set default material");
+ // }
+ // }
+ // mesh_darray_push(meshes, m);
}
bool load_material_lib(const char *path, str8 relative_path, material_darray *materials) {
TRACE("BEGIN load material lib at %s", path);
- const char *file_string = string_from_file(path);
- if (file_string == NULL) {
- ERROR("couldnt load %s", path);
- return false;
- }
-
- char *pch;
- char *saveptr;
- pch = strtok_r((char *)file_string, "\n", &saveptr);
-
- material current_material = DEFAULT_MATERIAL;
-
- bool material_set = false;
-
- while (pch != NULL) {
- char line_header[128];
- int offset = 0;
- // read the first word of the line
- int res = sscanf(pch, "%s %n", line_header, &offset);
- if (res != 1) {
- break;
- }
-
- // When we see "newmtl", start a new material, or flush the previous one
- if (strcmp(line_header, "newmtl") == 0) {
- if (material_set) {
- // a material was being parsed, so flush that one and start a new one
- material_darray_push(materials, current_material);
- DEBUG("pushed material with name %s", current_material.name);
- WARN("Reset current material");
- current_material = DEFAULT_MATERIAL;
- } else {
- material_set = true;
- }
- // scan the new material name
- char material_name[64];
- sscanf(pch + offset, "%s", current_material.name);
- DEBUG("material name %s\n", current_material.name);
- // current_material.name = material_name;
- } else if (strcmp(line_header, "Ka") == 0) {
- // ambient
- sscanf(pch + offset, "%f %f %f", &current_material.ambient_colour.x,
- &current_material.ambient_colour.y, &current_material.ambient_colour.z);
- } else if (strcmp(line_header, "Kd") == 0) {
- // diffuse
- sscanf(pch + offset, "%f %f %f", &current_material.diffuse.x, &current_material.diffuse.y,
- &current_material.diffuse.z);
- } else if (strcmp(line_header, "Ks") == 0) {
- // specular
- sscanf(pch + offset, "%f %f %f", &current_material.specular.x, &current_material.specular.y,
- &current_material.specular.z);
- } else if (strcmp(line_header, "Ns") == 0) {
- // specular exponent
- sscanf(pch + offset, "%f", &current_material.spec_exponent);
- } else if (strcmp(line_header, "map_Kd") == 0) {
- char diffuse_map_filename[1024];
- sscanf(pch + offset, "%s", diffuse_map_filename);
- char diffuse_map_path[1024];
- snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf,
- diffuse_map_filename);
- printf("load from %s\n", diffuse_map_path);
-
- // --------------
- texture diffuse_texture = texture_data_load(diffuse_map_path, true);
- current_material.diffuse_texture = diffuse_texture;
- strcpy(current_material.diffuse_tex_path, diffuse_map_path);
- texture_data_upload(&current_material.diffuse_texture);
- // --------------
- } else if (strcmp(line_header, "map_Ks") == 0) {
- // char specular_map_path[1024] = "assets/";
- // sscanf(pch + offset, "%s", specular_map_path + 7);
- char specular_map_filename[1024];
- sscanf(pch + offset, "%s", specular_map_filename);
- char specular_map_path[1024];
- snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf,
- specular_map_filename);
- printf("load from %s\n", specular_map_path);
- // --------------
- texture specular_texture = texture_data_load(specular_map_path, true);
- current_material.specular_texture = specular_texture;
- strcpy(current_material.specular_tex_path, specular_map_path);
- texture_data_upload(&current_material.specular_texture);
- // --------------
- } else if (strcmp(line_header, "map_Bump") == 0) {
- // TODO
- }
-
- pch = strtok_r(NULL, "\n", &saveptr);
- }
-
- TRACE("end load material lib");
-
- // last mesh or if one wasnt created with 'o' directive
- // TRACE("Last leftover material");
- material_darray_push(materials, current_material);
-
- INFO("Loaded %ld materials", material_darray_len(materials));
+ // const char *file_string = string_from_file(path);
+ // if (file_string == NULL) {
+ // ERROR("couldnt load %s", path);
+ // return false;
+ // }
+
+ // char *pch;
+ // char *saveptr;
+ // pch = strtok_r((char *)file_string, "\n", &saveptr);
+
+ // material current_material = DEFAULT_MATERIAL;
+
+ // bool material_set = false;
+
+ // while (pch != NULL) {
+ // char line_header[128];
+ // int offset = 0;
+ // // read the first word of the line
+ // int res = sscanf(pch, "%s %n", line_header, &offset);
+ // if (res != 1) {
+ // break;
+ // }
+
+ // // When we see "newmtl", start a new material, or flush the previous one
+ // if (strcmp(line_header, "newmtl") == 0) {
+ // if (material_set) {
+ // // a material was being parsed, so flush that one and start a new one
+ // material_darray_push(materials, current_material);
+ // DEBUG("pushed material with name %s", current_material.name);
+ // WARN("Reset current material");
+ // current_material = DEFAULT_MATERIAL;
+ // } else {
+ // material_set = true;
+ // }
+ // // scan the new material name
+ // char material_name[64];
+ // sscanf(pch + offset, "%s", current_material.name);
+ // DEBUG("material name %s\n", current_material.name);
+ // // current_material.name = material_name;
+ // } else if (strcmp(line_header, "Ka") == 0) {
+ // // ambient
+ // sscanf(pch + offset, "%f %f %f", &current_material.ambient_colour.x,
+ // &current_material.ambient_colour.y, &current_material.ambient_colour.z);
+ // } else if (strcmp(line_header, "Kd") == 0) {
+ // // diffuse
+ // sscanf(pch + offset, "%f %f %f", &current_material.diffuse.x, &current_material.diffuse.y,
+ // &current_material.diffuse.z);
+ // } else if (strcmp(line_header, "Ks") == 0) {
+ // // specular
+ // sscanf(pch + offset, "%f %f %f", &current_material.specular.x,
+ // &current_material.specular.y,
+ // &current_material.specular.z);
+ // } else if (strcmp(line_header, "Ns") == 0) {
+ // // specular exponent
+ // sscanf(pch + offset, "%f", &current_material.spec_exponent);
+ // } else if (strcmp(line_header, "map_Kd") == 0) {
+ // char diffuse_map_filename[1024];
+ // sscanf(pch + offset, "%s", diffuse_map_filename);
+ // char diffuse_map_path[1024];
+ // snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf,
+ // diffuse_map_filename);
+ // printf("load from %s\n", diffuse_map_path);
+
+ // // --------------
+ // texture diffuse_texture = texture_data_load(diffuse_map_path, true);
+ // current_material.diffuse_texture = diffuse_texture;
+ // strcpy(current_material.diffuse_tex_path, diffuse_map_path);
+ // texture_data_upload(&current_material.diffuse_texture);
+ // // --------------
+ // } else if (strcmp(line_header, "map_Ks") == 0) {
+ // // char specular_map_path[1024] = "assets/";
+ // // sscanf(pch + offset, "%s", specular_map_path + 7);
+ // char specular_map_filename[1024];
+ // sscanf(pch + offset, "%s", specular_map_filename);
+ // char specular_map_path[1024];
+ // snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf,
+ // specular_map_filename);
+ // printf("load from %s\n", specular_map_path);
+ // // --------------
+ // texture specular_texture = texture_data_load(specular_map_path, true);
+ // current_material.specular_texture = specular_texture;
+ // strcpy(current_material.specular_tex_path, specular_map_path);
+ // texture_data_upload(&current_material.specular_texture);
+ // // --------------
+ // } else if (strcmp(line_header, "map_Bump") == 0) {
+ // // TODO
+ // }
+
+ // pch = strtok_r(NULL, "\n", &saveptr);
+ // }
+
+ // TRACE("end load material lib");
+
+ // // last mesh or if one wasnt created with 'o' directive
+ // // TRACE("Last leftover material");
+ // material_darray_push(materials, current_material);
+
+ // INFO("Loaded %ld materials", material_darray_len(materials));
TRACE("END load material lib");
return true;
}
diff --git a/src/scene.c b/src/scene.c
new file mode 100644
index 0000000..d9fea05
--- /dev/null
+++ b/src/scene.c
@@ -0,0 +1,56 @@
+#include "scene.h"
+#include "core.h"
+#include "log.h"
+#include "maths.h"
+#include "render_types.h"
+
+extern core g_core;
+
+void scene_init(scene *s) {
+ memset(s, 0, sizeof(scene));
+ s->renderables = render_entity_darray_new(10);
+ // default camera position - moved slightly along Z axis looking at 0,0,0
+ vec3 cam_pos = vec3_create(0, 0, -5);
+ s->camera = camera_create(cam_pos, vec3_negate(cam_pos), VEC3_Y, deg_to_rad(45.0));
+}
+void scene_free(scene *s) { render_entity_darray_free(s->renderables); }
+
+void scene_set_dir_light(directional_light light) { g_core.default_scene.dir_light = light; }
+void scene_add_point_light(point_light light) {
+ scene s = g_core.default_scene;
+ if (s.point_lights_count == 4) {
+ WARN("Already 4 point lights, we can't add more.");
+ } else {
+ s.point_lights[s.point_lights_count] = light;
+ s.point_lights_count++;
+ }
+}
+void scene_add_model(model_handle model, transform3d transform) {
+ render_entity renderable = { .model = model, .tf = transform };
+ render_entity_darray_push(g_core.default_scene.renderables, renderable);
+}
+
+bool scene_remove_model(model_handle model) {
+ scene s = g_core.default_scene;
+ for (u32 i = 0; i <= s.renderables->len; i++) {
+ if (s.renderables->data[i].model.raw == model.raw) {
+ // TODO: add remove function to darray
+ }
+ }
+ return true;
+}
+
+void scene_set_model_transform(model_handle model, transform3d new_transform) {
+ scene s = g_core.default_scene;
+ for (u32 i = 0; i <= s.renderables->len; i++) {
+ if (s.renderables->data[i].model.raw == model.raw) {
+ s.renderables->data[i].tf = new_transform;
+ }
+ }
+}
+
+void scene_set_camera(vec3 pos, vec3 front) {
+ scene s = g_core.default_scene;
+ s.camera.position = pos;
+ s.camera.front = front;
+}
diff --git a/src/scene.h b/src/scene.h
new file mode 100644
index 0000000..5ac7542
--- /dev/null
+++ b/src/scene.h
@@ -0,0 +1,54 @@
+/**
+ * @file scene.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+#include "camera.h"
+#include "defines.h"
+#include "maths_types.h"
+#include "render_types.h"
+
+typedef struct scene {
+ // camera
+ camera camera;
+ // lights
+ directional_light dir_light;
+ point_light point_lights[4];
+ size_t point_lights_count;
+ // geometry
+ render_entity_darray* renderables;
+ // TODO: tree - transform_hierarchy
+} scene;
+
+void scene_init(scene* s);
+void scene_free(scene* s);
+
+// Simplified API - no scene pointer; gets and sets global scene
+
+// Add/Remove objects from the scene
+/* vec3 direction; */
+/* vec3 ambient; */
+/* vec3 diffuse; */
+/* vec3 specular; */
+void scene_set_dir_light(directional_light light);
+void _scene_set_dir_light(vec3 ambient, vec3 diffuse, vec3 specular, vec3 direction);
+
+void scene_add_point_light(point_light light);
+void scene_add_model(model_handle model, transform3d transform);
+bool scene_remove_model(model_handle model);
+
+// Getter & Setters
+void scene_set_model_transform(model_handle model, transform3d new_transform);
+void scene_set_camera(vec3 pos, vec3 front);
+
+/* // There can only be one heightmap terrain at a time right now. */
+/* bool scene_add_heightmap(scene* s /\* TODO *\/); */
+/* bool scene_delete_heightmap(scene* s); */
+
+// TODO: functions to load and save scenes from disk
diff --git a/src/std/buf.h b/src/std/buf.h
new file mode 100644
index 0000000..de093ec
--- /dev/null
+++ b/src/std/buf.h
@@ -0,0 +1,17 @@
+/**
+ * @file buf.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-28
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+#include "defines.h"
+
+typedef struct bytebuffer {
+ u8* buf;
+ size_t size;
+} bytebuffer;
diff --git a/src/std/containers/darray.h b/src/std/containers/darray.h
index 25bf846..b15d269 100644
--- a/src/std/containers/darray.h
+++ b/src/std/containers/darray.h
@@ -5,6 +5,11 @@
*/
// COPIED FROM KITC WITH SOME MINOR ADJUSTMENTS
+/* TODO:
+ - a 'find' function that takes a predicate (maybe wrap with a macro so we dont have to define a
+ new function?)
+*/
+
#ifndef KITC_TYPED_ARRAY_H
#define KITC_TYPED_ARRAY_H
@@ -39,15 +44,17 @@
/* } else {\ */
/* }\ */
-#define KITC_DECL_TYPED_ARRAY(T) \
- typedef typed_array(T) T##_darray; \
- typedef typed_array_iterator(T) T##_darray_iter; \
+#define KITC_DECL_TYPED_ARRAY(T) DECL_TYPED_ARRAY(T, T)
+
+#define DECL_TYPED_ARRAY(T, Type) \
+ typedef typed_array(T) Type##_darray; \
+ typedef typed_array_iterator(Type) Type##_darray_iter; \
\
/* Create a new one growable array */ \
- PREFIX T##_darray *T##_darray_new(size_t starting_capacity) { \
- T##_darray *d; \
+ PREFIX Type##_darray *Type##_darray_new(size_t starting_capacity) { \
+ Type##_darray *d; \
T *data; \
- d = malloc(sizeof(T##_darray)); \
+ d = malloc(sizeof(Type##_darray)); \
data = malloc(starting_capacity * sizeof(T)); \
\
d->len = 0; \
@@ -57,14 +64,14 @@
return d; \
} \
\
- PREFIX void T##_darray_free(T##_darray *d) { \
+ PREFIX void Type##_darray_free(Type##_darray *d) { \
if (d != NULL) { \
free(d->data); \
free(d); \
} \
} \
\
- PREFIX T *T##_darray_resize(T##_darray *d, size_t capacity) { \
+ PREFIX T *Type##_darray_resize(Type##_darray *d, size_t capacity) { \
/* resize the internal data block */ \
T *new_data = realloc(d->data, sizeof(T) * capacity); \
/* TODO: handle OOM error */ \
@@ -74,22 +81,22 @@
return new_data; \
} \
\
- PREFIX void T##_darray_push(T##_darray *d, T value) { \
+ PREFIX void Type##_darray_push(Type##_darray *d, T value) { \
if (d->len >= d->capacity) { \
size_t new_capacity = \
d->capacity > 0 ? d->capacity * DARRAY_RESIZE_FACTOR : DARRAY_DEFAULT_CAPACITY; \
- T *resized = T##_darray_resize(d, new_capacity); \
+ T *resized = Type##_darray_resize(d, new_capacity); \
} \
\
d->data[d->len] = value; \
d->len += 1; \
} \
\
- PREFIX void T##_darray_push_copy(T##_darray *d, const T *value) { \
+ PREFIX void Type##_darray_push_copy(Type##_darray *d, const T *value) { \
if (d->len >= d->capacity) { \
size_t new_capacity = \
d->capacity > 0 ? d->capacity * DARRAY_RESIZE_FACTOR : DARRAY_DEFAULT_CAPACITY; \
- T *resized = T##_darray_resize(d, new_capacity); \
+ T *resized = Type##_darray_resize(d, new_capacity); \
} \
\
T *place = d->data + d->len; \
@@ -97,18 +104,18 @@
memcpy(place, value, sizeof(T)); \
} \
\
- PREFIX void T##_darray_pop(T##_darray *d, T *dest) { \
+ PREFIX void Type##_darray_pop(Type##_darray *d, T *dest) { \
T *item = d->data + (d->len - 1); \
d->len -= 1; \
memcpy(dest, item, sizeof(T)); \
} \
\
- PREFIX void T##_darray_ins(T##_darray *d, const T *value, size_t index) { \
+ PREFIX void Type##_darray_ins(Type##_darray *d, const T *value, size_t index) { \
/* check if requires resize */ \
if (d->len + 1 > d->capacity) { \
size_t new_capacity = \
d->capacity > 0 ? d->capacity * DARRAY_RESIZE_FACTOR : DARRAY_DEFAULT_CAPACITY; \
- T *resized = T##_darray_resize(d, new_capacity); \
+ T *resized = Type##_darray_resize(d, new_capacity); \
} \
\
/* shift existing data after index */ \
@@ -122,14 +129,14 @@
memcpy(insert_dest, value, sizeof(T)); \
} \
\
- PREFIX void T##_darray_clear(T##_darray *d) { \
+ PREFIX void Type##_darray_clear(Type##_darray *d) { \
d->len = 0; \
memset(d->data, 0, d->capacity * sizeof(T)); \
} \
\
- PREFIX size_t T##_darray_len(T##_darray *d) { return d->len; } \
+ PREFIX size_t Type##_darray_len(Type##_darray *d) { return d->len; } \
\
- PREFIX void T##_darray_print(T##_darray *d) { \
+ PREFIX void Type##_darray_print(Type##_darray *d) { \
printf("len: %zu ", d->len); \
printf("capacity: %zu\n", d->capacity); \
for (int i = 0; i < d->len; i++) { \
@@ -137,14 +144,14 @@
} \
} \
\
- PREFIX T##_darray_iter T##_darray_iter_new(T##_darray *d) { \
- T##_darray_iter iterator; \
+ PREFIX Type##_darray_iter Type##_darray_iter_new(Type##_darray *d) { \
+ Type##_darray_iter iterator; \
iterator.array = d; \
iterator.current_idx = 0; \
return iterator; \
} \
\
- PREFIX void *T##_darray_iter_next(T##_darray_iter *iterator) { \
+ PREFIX void *Type##_darray_iter_next(Type##_darray_iter *iterator) { \
if (iterator->current_idx < iterator->array->len) { \
return &iterator->array->data[iterator->current_idx++]; \
} else { \
diff --git a/src/std/containers/graphs.h b/src/std/containers/graphs.h
new file mode 100644
index 0000000..5dbec97
--- /dev/null
+++ b/src/std/containers/graphs.h
@@ -0,0 +1,14 @@
+/**
+ * @file graphs.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+
+// Adjacency list backed graphs
+
+// Matrix backed graphs (not as useful) \ No newline at end of file
diff --git a/src/std/containers/hashset.h b/src/std/containers/hashset.h
new file mode 100644
index 0000000..d153fd2
--- /dev/null
+++ b/src/std/containers/hashset.h
@@ -0,0 +1,10 @@
+/**
+ * @file hashset.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */ \ No newline at end of file
diff --git a/src/std/containers/hashtable.h b/src/std/containers/hashtable.h
new file mode 100644
index 0000000..f5d98e7
--- /dev/null
+++ b/src/std/containers/hashtable.h
@@ -0,0 +1,10 @@
+/**
+ * @file hashtable.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */ \ No newline at end of file
diff --git a/src/std/mem.c b/src/std/mem.c
index 00f9c39..a5321fb 100644
--- a/src/std/mem.c
+++ b/src/std/mem.c
@@ -1,13 +1,17 @@
-#include "mem.h"
+#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
+
#include "log.h"
+#include "mem.h"
#ifndef DEFAULT_ALIGNMENT
#define DEFAULT_ALIGNMENT (2 * sizeof(void*))
#endif
+// --- Arena
+
void* arena_alloc_align(arena* a, size_t size, size_t align) {
ptrdiff_t padding = -(uintptr_t)a->curr & (align - 1);
ptrdiff_t available = a->end - a->curr - padding;
@@ -15,7 +19,7 @@ void* arena_alloc_align(arena* a, size_t size, size_t align) {
if (available < 0 || (ptrdiff_t)size > available) {
ERROR_EXIT("Arena ran out of memory\n");
}
- void* p = a->begin + padding;
+ void* p = a->curr + padding;
a->curr += padding + size;
return memset(p, 0, size);
}
@@ -31,4 +35,91 @@ void arena_free_all(arena* a) {
a->curr = a->begin; // pop everything at once and reset to the start.
}
-void arena_free_storage(arena* a) { free(a->begin); } \ No newline at end of file
+void arena_free_storage(arena* a) { free(a->begin); }
+
+arena_save arena_savepoint(arena* a) {
+ arena_save savept = { .arena = a, .savepoint = a->curr };
+ return savept;
+}
+
+void arena_rewind(arena_save savepoint) { savepoint.arena->curr = savepoint.savepoint; }
+
+// --- Pool
+
+void_pool void_pool_create(arena* a, u64 capacity, u64 entry_size) {
+ size_t memory_requirements = capacity * entry_size;
+ void* backing_buf = arena_alloc(a, memory_requirements);
+
+ void_pool pool = { .capacity = capacity,
+ .entry_size = entry_size,
+ .count = 0,
+ .backing_buffer = backing_buf,
+ .free_list_head = NULL };
+
+ void_pool_free_all(&pool);
+
+ return pool;
+}
+
+void void_pool_free_all(void_pool* pool) {
+ // set all entries to be free
+ for (u64 i = 0; i < pool->capacity; i++) {
+ void* ptr = &pool->backing_buffer[i * pool->entry_size];
+ void_pool_header* free_node =
+ (void_pool_header*)ptr; // we reuse the actual entry itself to hold the header
+ if (i == (pool->capacity - 1)) {
+ // if the last one we make its next pointer NULL indicating its full
+ free_node->next = NULL;
+ }
+ free_node->next = pool->free_list_head;
+ // now the head points to this entry
+ pool->free_list_head = free_node;
+ }
+}
+
+void* void_pool_get(void_pool* pool, u32 raw_handle) {
+ // An handle is an index into the array essentially
+ void* ptr = pool->backing_buffer + (raw_handle * pool->entry_size);
+ return ptr;
+}
+
+void* void_pool_alloc(void_pool* pool, u32* out_raw_handle) {
+ // get the next free node
+ if (pool->count == pool->capacity) {
+ WARN("Pool is full!");
+ return NULL;
+ }
+ if (pool->free_list_head == NULL) {
+ ERROR("Pool is full (head = null)");
+ return NULL;
+ }
+ void_pool_header* free_node = pool->free_list_head;
+
+ // What index does this become?
+ uintptr_t start = (uintptr_t)pool->backing_buffer;
+ uintptr_t cur = (uintptr_t)free_node;
+ TRACE("%ld %ld ", start, cur);
+ /* assert(cur > start); */
+ u32 index = (u32)((cur - start) / pool->entry_size);
+ printf("Index %d\n", index);
+ if (out_raw_handle != NULL) {
+ *out_raw_handle = index;
+ }
+
+ pool->free_list_head = free_node->next;
+
+ memset(free_node, 0, pool->entry_size);
+ pool->count++;
+ return (void*)free_node;
+}
+
+void void_pool_dealloc(void_pool* pool, u32 raw_handle) {
+ // push free node back onto the free list
+ void* ptr = void_pool_get(pool, raw_handle);
+ void_pool_header* freed_node = (void_pool_header*)ptr;
+
+ freed_node->next = pool->free_list_head;
+ pool->free_list_head = freed_node;
+
+ pool->count--;
+}
diff --git a/src/std/mem.h b/src/std/mem.h
index 2f92894..26da778 100644
--- a/src/std/mem.h
+++ b/src/std/mem.h
@@ -10,6 +10,9 @@
#pragma once
#include <stddef.h>
+#include "defines.h"
+
+// --- Arena
// Inspired by https://nullprogram.com/blog/2023/09/27/
typedef struct arena {
@@ -18,9 +21,61 @@ typedef struct arena {
char* end;
} arena;
+typedef struct arena_save {
+ arena* arena;
+ char* savepoint;
+} arena_save;
+
arena arena_create(void* backing_buffer, size_t capacity);
void* arena_alloc(arena* a, size_t size);
void* arena_alloc_align(arena* a, size_t size, size_t align);
void arena_free_all(arena* a);
void arena_free_storage(arena* a);
-// TODO: arena_resize \ No newline at end of file
+arena_save arena_savepoint(arena* a);
+void arena_rewind(arena_save savepoint);
+// TODO: arena_resize
+
+// --- Pool
+
+typedef struct void_pool_header void_pool_header;
+struct void_pool_header {
+ void_pool_header* next;
+};
+
+typedef struct void_pool {
+ u64 capacity;
+ u64 entry_size;
+ u64 count;
+ void* backing_buffer;
+ void_pool_header* free_list_head;
+} void_pool;
+
+void_pool void_pool_create(arena* a, u64 capacity, u64 entry_size);
+void void_pool_free_all(void_pool* pool);
+bool void_pool_is_empty(void_pool* pool);
+bool void_pool_is_full(void_pool* pool);
+void* void_pool_get(void_pool* pool, u32 raw_handle);
+void* void_pool_alloc(void_pool* pool, u32* out_raw_handle);
+void void_pool_dealloc(void_pool* pool, u32 raw_handle);
+
+// TODO: macro that lets us specialise
+
+/* typedef struct Name##_handle Name##_handle; \ */
+#define TYPED_POOL(T, Name) \
+ typedef struct Name##_pool { \
+ void_pool inner; \
+ } Name##_pool; \
+ \
+ static Name##_pool Name##_pool_create(arena* a, u64 cap, u64 entry_size) { \
+ void_pool p = void_pool_create(a, cap, entry_size); \
+ return (Name##_pool){ .inner = p }; \
+ } \
+ static inline T* Name##_pool_get(Name##_pool* pool, Name##_handle handle) { \
+ return (T*)void_pool_get(&pool->inner, handle.raw); \
+ } \
+ static inline T* Name##_pool_alloc(Name##_pool* pool, Name##_handle* out_handle) { \
+ return (T*)void_pool_alloc(&pool->inner, &out_handle->raw); \
+ } \
+ static inline void Name##_pool_dealloc(Name##_pool* pool, Name##_handle handle) { \
+ void_pool_dealloc(&pool->inner, handle.raw); \
+ }\
diff --git a/src/std/utils.h b/src/std/utils.h
new file mode 100644
index 0000000..c9827a3
--- /dev/null
+++ b/src/std/utils.h
@@ -0,0 +1,4 @@
+#pragma once
+#include <stdbool.h>
+
+const char* bool_str(bool input) { return input ? "True" : "False"; } \ No newline at end of file
diff --git a/src/systems/input.c b/src/systems/input.c
index 292d438..0c8f768 100644
--- a/src/systems/input.c
+++ b/src/systems/input.c
@@ -1,11 +1,17 @@
#include "input.h"
+#include <assert.h>
#include <glfw3.h>
+#include <string.h>
#include "log.h"
+static input_state *g_input; // Use a global to simplify caller code
+
bool input_system_init(input_state *input, GLFWwindow *window) {
INFO("Input init");
+ memset(input, 0, sizeof(input_state));
+
input->window = window;
// Set everything to false. Could just set memory to zero but where's the fun in that
for (int i = 0; i < KEYCODE_MAX; i++) {
@@ -14,10 +20,19 @@ bool input_system_init(input_state *input, GLFWwindow *window) {
input->just_released_keys[i] = false;
}
+ g_input = input;
+
+ assert(input->mouse.x_delta == 0);
+ assert(input->mouse.y_delta == 0);
+
+ INFO("Finish input init");
return true;
}
+void input_system_shutdown(input_state *input) {}
+
void input_update(input_state *input) {
+ glfwPollEvents();
// --- update keyboard input
// if we go from un-pressed -> pressed, set as "just pressed"
@@ -75,3 +90,9 @@ void input_update(input_state *input) {
input->mouse = new_mouse_state;
}
+
+bool key_is_pressed(keycode key) { return g_input->depressed_keys[key]; }
+
+bool key_just_pressed(keycode key) { return g_input->just_pressed_keys[key]; }
+
+bool key_just_released(keycode key) { return g_input->just_released_keys[key]; } \ No newline at end of file
diff --git a/src/systems/keys.h b/src/systems/keys.h
index 090bb49..a76e101 100644
--- a/src/systems/keys.h
+++ b/src/systems/keys.h
@@ -2,5 +2,18 @@
typedef enum keycode {
// TODO: add all keycodes
- KEYCODE_MAX
+ KEYCODE_SPACE = 32,
+ KEYCODE_A = 65,
+ KEYCODE_D = 68,
+ KEYCODE_S = 83,
+ KEYCODE_W = 87,
+ KEYCODE_ESCAPE = 256,
+ KEYCODE_ENTER = 257,
+ KEYCODE_TAB = 258,
+ KEYCODE_BACKSPACE = 259,
+ KEYCODE_KEY_RIGHT = 262,
+ KEYCODE_KEY_LEFT = 263,
+ KEYCODE_KEY_DOWN = 264,
+ KEYCODE_KEY_UP = 265,
+ KEYCODE_MAX = 348
} keycode; \ No newline at end of file
diff --git a/src/systems/screenspace.h b/src/systems/screenspace.h
index 2250847..5f0c579 100644
--- a/src/systems/screenspace.h
+++ b/src/systems/screenspace.h
@@ -25,7 +25,7 @@ struct draw_circle {
/** @brief Tagged union that represents a UI shape to be drawn. */
typedef struct draw_cmd {
- enum { RECT, CIRCLE } draw_cmd_type;
+ enum { DRAW_RECT, CIRCLE } draw_cmd_type;
union {
struct draw_rect rect;
struct draw_circle circle;
@@ -37,7 +37,7 @@ KITC_DECL_TYPED_ARRAY(draw_cmd)
typedef struct screenspace_state {
u32 rect_vbo;
u32 rect_vao;
- shader rect_shader;
+ // shader rect_shader;
draw_cmd_darray* draw_cmd_buf;
} screenspace_state;
diff --git a/src/systems/terrain.c b/src/systems/terrain.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/systems/terrain.c
diff --git a/src/systems/terrain.h b/src/systems/terrain.h
new file mode 100644
index 0000000..bfd90b5
--- /dev/null
+++ b/src/systems/terrain.h
@@ -0,0 +1,55 @@
+/**
+ * @file terrain.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+
+/*
+Future:
+ - Chunked terrain
+ - Dynamic LOD
+*/
+
+#include "defines.h"
+#include "maths_types.h"
+#include "mem.h"
+#include "render_types.h"
+#include "str.h"
+
+typedef struct heightmap {
+ str8 filepath;
+ u32x2 size;
+ void* image_data;
+ bool is_uploaded;
+} heightmap;
+
+typedef struct terrain_state {
+ arena terrain_allocator;
+ heightmap* heightmap; // NULL = no heightmap
+} terrain_state;
+
+bool terrain_system_init(terrain_state* state);
+void terrain_system_shutdown(terrain_state* state);
+void terrain_system_render_hmap(renderer* rend, terrain_state* state);
+
+heightmap heightmap_from_image(str8 filepath);
+heightmap heightmap_from_perlin(/* TODO: perlin noise generation parameters */);
+
+/** @brief Get the height (the Y component) for a vertex at a particular coordinate in the heightmap
+ */
+f32 heightmap_height_at_xz(heightmap* hmap, f32 x, f32 z);
+
+/** @brief Calculate the normal vector of a vertex at a particular coordinate in the heightmap */
+vec3 heightmap_normal_at_xz(heightmap* hmap, f32 x, f32 z);
+
+/** @brief Generate the `geometry_data` for a heightmap ready to be uploaded to the GPU */
+geometry_data geo_heightmap(arena* a, heightmap heightmap);
+
+// somewhere there will be an easy way to add a heightmap
+
+// scene_add_heightmap \ No newline at end of file
diff --git a/src/systems/text.h b/src/systems/text.h
index 19248a6..f40cfd6 100644
--- a/src/systems/text.h
+++ b/src/systems/text.h
@@ -7,6 +7,7 @@
#include "darray.h"
#include "defines.h"
+#include "ral.h"
#include "render_types.h"
struct core;
@@ -29,7 +30,7 @@ KITC_DECL_TYPED_ARRAY(draw_text_packet)
typedef struct text_system_state {
font default_font;
- shader glyph_shader;
+ shader_handle glyph_shader;
u32 glyph_vbo;
u32 glyph_vao;
draw_text_packet_darray *draw_cmd_buf;
diff --git a/src/transform_hierarchy.c b/src/transform_hierarchy.c
index f1c859a..2f2ff01 100644
--- a/src/transform_hierarchy.c
+++ b/src/transform_hierarchy.c
@@ -11,7 +11,7 @@
#include "log.h"
#include "maths.h"
#include "maths_types.h"
-#include "render_types.h"
+// #include "render_types.h"
struct transform_hierarchy {
transform_node root;
diff --git a/src/transform_hierarchy.h b/src/transform_hierarchy.h
index af77ee1..0921c19 100644
--- a/src/transform_hierarchy.h
+++ b/src/transform_hierarchy.h
@@ -4,6 +4,7 @@
#pragma once
#include "maths_types.h"
+#include "ral.h"
#include "render_types.h"
#define MAX_TF_NODE_CHILDREN \
diff --git a/tests/pool_test_runner.c b/tests/pool_test_runner.c
new file mode 100644
index 0000000..a6eff69
--- /dev/null
+++ b/tests/pool_test_runner.c
@@ -0,0 +1,12 @@
+#include "unity.h"
+#include "unity_fixture.h"
+
+TEST_GROUP_RUNNER(Pool) {
+ // TODO: test cases
+ RUN_TEST_CASE(Pool, Initialisation);
+ RUN_TEST_CASE(Pool, TypedPool);
+}
+
+static void RunAllTests(void) { RUN_TEST_GROUP(Pool); }
+
+int main(int argc, const char* argv[]) { return UnityMain(argc, argv, RunAllTests); }
diff --git a/tests/pool_tests.c b/tests/pool_tests.c
new file mode 100644
index 0000000..df65773
--- /dev/null
+++ b/tests/pool_tests.c
@@ -0,0 +1,72 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include "defines.h"
+#include "unity.h"
+#include "unity_fixture.h"
+
+#include "mem.h"
+
+#define ARENA_SIZE (1024 * 1024)
+
+static arena a;
+
+TEST_GROUP(Pool);
+
+TEST_SETUP(Pool) { a = arena_create(malloc(ARENA_SIZE), ARENA_SIZE); }
+
+TEST_TEAR_DOWN(Pool) { arena_free_all(&a); }
+
+TEST(Pool, SanityCheckTest) { TEST_ASSERT_EQUAL(true, true); }
+
+TEST(Pool, Initialisation) {
+ // u32 pool
+ void_pool pool = void_pool_create(&a, 256, sizeof(u32));
+ u32 x_handle;
+ u32* x_ptr = (u32*)void_pool_alloc(&pool, &x_handle);
+ // store something in it
+ *x_ptr = 1024;
+ u32* x = void_pool_get(&pool, x_handle);
+
+ TEST_ASSERT_EQUAL_UINT32(1024, *x);
+ /* TEST_ASSERT_EQUAL_MEMORY(&expected, &actual, sizeof(vec3)); */
+}
+
+typedef struct foo {
+ u32 a;
+ f32 b;
+ char c;
+} foo;
+
+CORE_DEFINE_HANDLE(bar);
+typedef struct bar_handle {
+ u32 raw;
+} bar_handle;
+TYPED_POOL(foo, bar);
+
+TEST(Pool, TypedPool) {
+ printf("Typed pool test\n");
+ // create pool
+ bar_pool pool = bar_pool_create(&a, 2, sizeof(foo));
+
+ bar_handle first_handle, second_handle, third_handle;
+ foo* first = bar_pool_alloc(&pool, &first_handle);
+ foo* second = bar_pool_alloc(&pool, &second_handle);
+ // Third one shouldnt work
+ foo* third = bar_pool_alloc(&pool, &third_handle);
+ TEST_ASSERT_NULL(third);
+
+ first->a = 32;
+ first->b = 2.0;
+ first->c = 'X';
+
+ foo* my_foo = bar_pool_get(&pool, first_handle);
+ TEST_ASSERT_EQUAL_UINT32(32, my_foo->a);
+ TEST_ASSERT_EQUAL(2.0, my_foo->b);
+ TEST_ASSERT_EQUAL_CHAR('X', my_foo->c);
+
+ bar_pool_dealloc(&pool, first_handle);
+
+ // next alloc should succeed
+ third = bar_pool_alloc(&pool, &third_handle);
+ TEST_ASSERT_NOT_NULL(third);
+}
diff --git a/xmake.lua b/xmake.lua
index 453fb55..40ef961 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -4,8 +4,6 @@ set_config("cc", "gcc")
add_rules("mode.debug", "mode.release") -- we have two modes: debug & release
-add_syslinks("m") -- these are must have when compiling
-
-- -Wall : base set of warnings
-- -Wextra : additional warnings not covered by -Wall
-- -Wundef : undefined macros
@@ -14,6 +12,7 @@ add_cflags("-Wall", "-Wextra", "-Wundef", "-Wdouble-promotion")
if is_mode("debug") then
add_cflags("-g") -- Add debug symbols in debug mode
+ add_defines("CDEBUG")
elseif is_mode("release") then
add_defines("CRELEASE")
end
@@ -22,9 +21,13 @@ end
-- Platform defines and system packages
if is_plat("linux") then
add_defines("CEL_PLATFORM_LINUX")
- add_syslinks("dl", "X11", "pthread")
+ add_syslinks("vulkan", "dl", "X11", "pthread")
elseif is_plat("windows") then
add_defines("CEL_PLATFORM_WINDOWS")
+ add_syslinks("user32", "gdi32", "kernel32", "shell32")
+ add_requires("vulkansdk", {system=true})
+
+ -- add_links("pthreadVC2-w64")
elseif is_plat("macosx") then
add_defines("CEL_PLATFORM_MAC")
add_frameworks("Cocoa", "IOKit", "CoreVideo", "OpenGL")
@@ -53,8 +56,10 @@ add_requires("local_glfw")
local core_sources = {
"deps/glad/src/glad.c",
"src/*.c",
- "src/logos/*.c",
+ -- "src/logos/*.c",
+ "src/maths/*.c",
"src/platform/*.c",
+ "src/physics/*.c",
"src/renderer/*.c",
"src/renderer/backends/*.c",
"src/resources/*.c",
@@ -63,10 +68,37 @@ local core_sources = {
"src/systems/*.c",
}
+local unity_sources = {
+ "deps/Unity/src/unity.c",
+ "deps/Unity/extras/fixture/src/unity_fixture.c",
+ "deps/Unity/extras/memory/src/unity_memory.c",
+}
+
+rule("compile_glsl_vert_shaders")
+ set_extensions(".vert")
+ on_buildcmd_file(function (target, batchcmds, sourcefile, opt)
+ local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".vert.spv")
+
+ print("Compiling shader: %s to %s", sourcefile, targetfile)
+ batchcmds:vrunv('glslc', {sourcefile, "-o", targetfile})
+ -- batchcmds:add_depfiles(sourcefile)
+end)
+rule("compile_glsl_frag_shaders")
+ set_extensions(".frag")
+ on_buildcmd_file(function (target, batchcmds, sourcefile, opt)
+ local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".frag.spv")
+
+ print("Compiling shader: %s to %s", sourcefile, targetfile)
+ batchcmds:vrunv('glslc', {sourcefile, "-o", targetfile})
+ -- batchcmds:add_depfiles(sourcefile)
+end)
+-- TODO: Metal shaders compilation
+
+--
+
-- common configuration for both static and shared libraries
target("core_config")
set_kind("static") -- kind is required but you can ignore it since it's just for common settings
- add_syslinks("dl")
add_packages("local_glfw")
add_includedirs("deps/cgltf", {public = true})
add_includedirs("deps/glfw-3.3.8/include/GLFW", {public = true})
@@ -75,9 +107,10 @@ target("core_config")
add_includedirs("deps/stb_image_write", {public = true})
add_includedirs("deps/stb_truetype", {public = true})
add_includedirs("src/", {public = true})
- add_includedirs("src/logos/", {public = true})
+ -- add_includedirs("src/logos/", {public = true})
add_includedirs("src/maths/", {public = true})
add_includedirs("src/platform/", {public = true})
+ add_includedirs("src/physics/", {public = true})
add_includedirs("src/renderer/", {public = true})
add_includedirs("src/renderer/backends/", {public = true})
add_includedirs("src/resources/", {public = true})
@@ -85,6 +118,18 @@ target("core_config")
add_includedirs("src/std/containers", {public = true})
add_includedirs("src/systems/", {public = true})
add_files("src/empty.c") -- for some reason we need this on Mac so it doesnt call 'ar' with no files and error
+ add_rules("compile_glsl_vert_shaders")
+ add_rules("compile_glsl_frag_shaders")
+ add_files("assets/shaders/triangle.vert")
+ add_files("assets/shaders/triangle.frag")
+ add_files("assets/shaders/cube.vert")
+ add_files("assets/shaders/cube.frag")
+ -- add_files("assets/shaders/*.frag")
+ if is_plat("windows") then
+ add_includedirs("$(env VULKAN_SDK)/Include", {public = true})
+ add_linkdirs("$(env VULKAN_SDK)/Lib", {public = true})
+ add_links("vulkan-1")
+ end
set_default(false) -- prevents standalone building of this target
target("core_static")
@@ -92,50 +137,113 @@ target("core_static")
add_deps("core_config") -- inherit common configurations
set_policy("build.merge_archive", true)
add_files(core_sources)
+ -- Link against static CRT
+ if is_plat("windows") then
+ add_links("libcmt", "legacy_stdio_definitions") -- for release builds
+ add_links("libcmtd", "legacy_stdio_definitions") -- for debug builds
+ end
target("core_shared")
set_kind("shared")
add_deps("core_config") -- inherit common configurations
add_files(core_sources)
-
-target("main_loop")
- set_kind("binary")
- set_group("examples")
- add_deps("core_shared")
- add_files("examples/main_loop/ex_main_loop.c")
- set_rundir("$(projectdir)")
-
-target("std")
+ -- Link against dynamic CRT
+ if is_plat("windows") then
+ add_links("msvcrt", "legacy_stdio_definitions") -- for release builds
+ add_links("msvcrtd", "legacy_stdio_definitions") -- for debug builds
+ end
+
+-- target("main_loop")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_static")
+-- add_files("examples/main_loop/ex_main_loop.c")
+-- set_rundir("$(projectdir)")
+
+target("tri")
set_kind("binary")
set_group("examples")
add_deps("core_static")
- add_files("examples/standard_lib/ex_std.c")
+ add_files("examples/triangle/ex_triangle.c")
set_rundir("$(projectdir)")
-target("obj")
+target("cube")
set_kind("binary")
set_group("examples")
add_deps("core_static")
- add_files("examples/obj_loading/ex_obj_loading.c")
+ add_files("examples/cube/ex_cube.c")
set_rundir("$(projectdir)")
-target("gltf")
+-- target("std")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_static")
+-- add_files("examples/standard_lib/ex_std.c")
+-- set_rundir("$(projectdir)")
+
+-- target("obj")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_static")
+-- add_files("examples/obj_loading/ex_obj_loading.c")
+-- set_rundir("$(projectdir)")
+
+-- target("input")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_static")
+-- add_files("examples/input/ex_input.c")
+-- set_rundir("$(projectdir)")
+
+-- target("gltf")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_static")
+-- add_files("examples/gltf_loading/ex_gltf_loading.c")
+-- set_rundir("$(projectdir)")
+
+-- target("transforms")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_shared")
+-- add_files("examples/transforms/ex_transforms.c")
+-- set_rundir("$(projectdir)")
+
+-- target("animation")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_shared")
+-- add_files("examples/property_animation/ex_property_animation.c")
+-- set_rundir("$(projectdir)")
+
+-- target("skinned")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_shared")
+-- add_files("examples/skinned_animation/ex_skinned_animation.c")
+-- set_rundir("$(projectdir)")
+
+-- target("input")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_static")
+-- add_files("examples/input/ex_input.c")
+-- set_rundir("$(projectdir)")
+
+-- target("demo")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_static")
+-- add_files("examples/demo/demo.c")
+-- set_rundir("$(projectdir)")
+
+target("pool_tests")
set_kind("binary")
- set_group("examples")
+ set_group("tests")
add_deps("core_static")
- add_files("examples/gltf_loading/ex_gltf_loading.c")
- set_rundir("$(projectdir)")
-
-target("transforms")
- set_kind("binary")
- set_group("examples")
- add_deps("core_shared")
- add_files("examples/transforms/ex_transforms.c")
- set_rundir("$(projectdir)")
-
-target("demo")
- set_kind("binary")
- set_group("examples")
- add_deps("core_static")
- add_files("examples/demo/demo.c")
- set_rundir("$(projectdir)")
+ add_files(unity_sources)
+ add_includedirs("deps/Unity/src", {public = true})
+ add_includedirs("deps/Unity/extras/fixture/src", {public = true})
+ add_includedirs("deps/Unity/extras/memory/src", {public = true})
+ add_files("tests/pool_tests.c")
+ add_files("tests/pool_test_runner.c")