summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/object.frag14
-rw-r--r--assets/shaders/object.vert11
-rw-r--r--assets/shaders/skinned.vert59
-rw-r--r--examples/example_scene.h31
-rw-r--r--examples/gltf_loading/ex_gltf_loading.c4
-rw-r--r--examples/input/ex_input.c6
-rw-r--r--examples/main_loop/ex_main_loop.c2
-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--src/animation.c38
-rw-r--r--src/animation.h72
-rw-r--r--src/colours.h48
-rw-r--r--src/defines.h4
-rw-r--r--src/maths/maths.h54
-rw-r--r--src/maths/maths_types.h6
-rw-r--r--src/renderer/backends/backend_opengl.c4
-rw-r--r--src/renderer/backends/backend_vulkan.c133
-rw-r--r--src/renderer/render.c181
-rw-r--r--src/renderer/render.h4
-rw-r--r--src/renderer/render_backend.h2
-rw-r--r--src/renderer/render_types.h114
-rw-r--r--src/resources/gltf.c277
-rw-r--r--src/resources/obj.c1
-rw-r--r--src/std/mem.c2
-rw-r--r--xmake.lua21
52 files changed, 1869 insertions, 149 deletions
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
+
+© 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
+
+© 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/object.frag b/assets/shaders/object.frag
index 11b33e1..fa50fcf 100644
--- a/assets/shaders/object.frag
+++ b/assets/shaders/object.frag
@@ -1,9 +1,15 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
-layout(location = 0) in vec3 in_normal;
-layout(location = 1) in vec3 in_position;
+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 vec4 out_colour;
+layout(set = 0, binding = 1) uniform sampler2D texSampler;
-void main() { out_colour = vec4(in_normal, 1.0); } \ No newline at end of file
+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
index a7237bb..23c0ffb 100644
--- a/assets/shaders/object.vert
+++ b/assets/shaders/object.vert
@@ -3,9 +3,11 @@
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_normal;
-layout(location = 1) out vec3 out_position;
+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;
@@ -19,8 +21,9 @@ layout(push_constant) uniform push_constants {
u_push_constants;
void main() {
- out_position = in_position;
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_normal = in_normal; // forward normal vectors
+ 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/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
index 3101968..576b364 100644
--- a/examples/input/ex_input.c
+++ b/examples/input/ex_input.c
@@ -30,6 +30,10 @@ int main() {
.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);
@@ -65,7 +69,7 @@ int main() {
mat4 model = mat4_translation(VEC3_ZERO);
- gfx_backend_draw_frame(&core->renderer, &game.camera, model);
+ gfx_backend_draw_frame(&core->renderer, &game.camera, model, &tex);
render_frame_end(&core->renderer);
}
diff --git a/examples/main_loop/ex_main_loop.c b/examples/main_loop/ex_main_loop.c
index 31514bf..8819f7c 100644
--- a/examples/main_loop/ex_main_loop.c
+++ b/examples/main_loop/ex_main_loop.c
@@ -22,7 +22,7 @@ int main() {
x += 0.01;
mat4 model = mat4_translation(vec3(x, 0, 0));
- gfx_backend_draw_frame(&core->renderer, &camera, model);
+ gfx_backend_draw_frame(&core->renderer, &camera, model, NULL);
// insert work here
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/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/colours.h b/src/colours.h
index fa5f54b..a981c6c 100644
--- a/src/colours.h
+++ b/src/colours.h
@@ -38,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/defines.h b/src/defines.h
index d36bb09..8cd4f98 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -66,8 +66,8 @@ 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
+#define CEL_REND_BACKEND_OPENGL 1
+// #define CEL_REND_BACKEND_VULKAN 1
#endif
#if defined(CEL_PLATFORM_MAC)
diff --git a/src/maths/maths.h b/src/maths/maths.h
index 638d9f6..e0d39d7 100644
--- a/src/maths/maths.h
+++ b/src/maths/maths.h
@@ -83,6 +83,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() {
@@ -253,7 +299,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 })
@@ -262,10 +308,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
diff --git a/src/maths/maths_types.h b/src/maths/maths_types.h
index 6d38fc7..53cac55 100644
--- a/src/maths/maths_types.h
+++ b/src/maths/maths_types.h
@@ -64,4 +64,8 @@ typedef struct transform {
typedef struct vec4i {
i32 x, y, z, w;
-} vec4i; \ No newline at end of file
+} vec4i;
+
+typedef struct vec4u {
+ u32 x, y, z, w;
+} vec4u; \ No newline at end of file
diff --git a/src/renderer/backends/backend_opengl.c b/src/renderer/backends/backend_opengl.c
index ffeb051..7467416 100644
--- a/src/renderer/backends/backend_opengl.c
+++ b/src/renderer/backends/backend_opengl.c
@@ -1,4 +1,5 @@
#include <stdlib.h>
+#include "camera.h"
#define CEL_PLATFORM_LINUX
#include "defines.h"
@@ -42,6 +43,9 @@ bool gfx_backend_init(renderer *ren) {
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) {
diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c
index 4a4b09e..4d3a14e 100644
--- a/src/renderer/backends/backend_vulkan.c
+++ b/src/renderer/backends/backend_vulkan.c
@@ -145,6 +145,22 @@ typedef struct global_object_uniform {
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;
@@ -171,6 +187,8 @@ typedef struct vulkan_shader {
// 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 {
@@ -324,7 +342,7 @@ bool vulkan_graphics_pipeline_create(vulkan_context* context, vulkan_renderpass*
// Vertex input
VkVertexInputBindingDescription binding_desc;
binding_desc.binding = 0;
- binding_desc.stride = sizeof(vertex_pos);
+ binding_desc.stride = sizeof(vertex);
binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkPipelineVertexInputStateCreateInfo vertex_input_info = {
@@ -436,6 +454,8 @@ bool vulkan_buffer_create(vulkan_context* context, u64 size, VkBufferUsageFlagBi
vulkan_buffer_bind(context, out_buffer, 0);
}
+ DEBUG("Created buffer.");
+
return true;
}
@@ -512,11 +532,21 @@ bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_sha
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
};
- global_layout_info.bindingCount = 1;
- global_layout_info.pBindings = &global_ubo_layout_binding;
+
+ 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));
@@ -525,9 +555,15 @@ bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_sha
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 = 1;
- pool_info.pPoolSizes = &global_pool_size;
+ pool_info.poolSizeCount = 2;
+ pool_info.pPoolSizes = pool_sizes;
pool_info.maxSets = 3;
VK_CHECK(vkCreateDescriptorPool(context->device.logical_device, &pool_info, context->allocator,
@@ -549,12 +585,13 @@ bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_sha
// Attributes
u32 offset = 0;
- const i32 attribute_count = 2;
- VkVertexInputAttributeDescription attribute_descs[2];
+ const i32 attribute_count = 3;
+ VkVertexInputAttributeDescription attribute_descs[3];
// Position
- VkFormat formats[2] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT };
+ VkFormat formats[3] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT,
+ VK_FORMAT_R32G32_SFLOAT };
- u64 sizes[2] = { sizeof(vec3), sizeof(vec3) };
+ u64 sizes[3] = { sizeof(vec3), sizeof(vec3), sizeof(vec2) };
for (u32 i = 0; i < attribute_count; i++) {
attribute_descs[i].binding = 0;
@@ -629,15 +666,30 @@ void vulkan_object_shader_update_global_state(vulkan_context* context, vulkan_sh
buffer_info.offset = offset;
buffer_info.range = range;
- VkWriteDescriptorSet descriptor_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
- descriptor_write.dstSet = shader->descriptor_sets[image_index];
- descriptor_write.dstBinding = 0;
- descriptor_write.dstArrayElement = 0;
- descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
- descriptor_write.descriptorCount = 1;
- descriptor_write.pBufferInfo = &buffer_info;
+ 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;
- vkUpdateDescriptorSets(context->device.logical_device, 1, &descriptor_write, 0, 0);
+ 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);
@@ -871,6 +923,10 @@ void vulkan_image_transition_layout(vulkan_context* context, vulkan_command_buff
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;
@@ -891,8 +947,10 @@ void vulkan_image_transition_layout(vulkan_context* context, vulkan_command_buff
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_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;
@@ -912,10 +970,16 @@ 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;
@@ -938,7 +1002,7 @@ void vulkan_image_create(vulkan_context* context, VkImageType image_type, u32 wi
image_create_info.extent.width = width;
image_create_info.extent.height = height;
image_create_info.extent.depth = 1;
- image_create_info.mipLevels = 4;
+ image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.format = format;
image_create_info.tiling = tiling;
@@ -947,8 +1011,8 @@ void vulkan_image_create(vulkan_context* context, VkImageType image_type, u32 wi
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- vkCreateImage(context->device.logical_device, &image_create_info, context->allocator,
- &out_image->handle);
+ 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);
@@ -1645,12 +1709,13 @@ bool gfx_backend_init(renderer* ren) {
mesh cube = prim_cube_mesh_create();
- vertex_pos* verts = malloc(sizeof(vertex_pos) * cube.vertices->len);
+ vertex* verts = malloc(sizeof(vertex) * cube.vertices->len);
f32 scale = 3.0;
for (size_t i = 0; i < cube.vertices->len; i++) {
- verts[i].pos = vec3_mult(cube.vertices->data[i].position, scale);
+ 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;
@@ -1673,14 +1738,16 @@ bool gfx_backend_init(renderer* ren) {
// 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_pos) * cube.vertices->len,
- verts);
+ &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");
@@ -1746,16 +1813,22 @@ 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;
- VkDeviceSize image_size = tex->width * tex->height * tex->channel_count;
+ 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_SNORM;
+ 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,
@@ -1802,6 +1875,8 @@ void texture_data_upload(texture* tex) {
ERROR("Error creating texture sampler for image %s", tex->name);
return;
}
+
+ tex->image_data = (void*)data;
}
// TODO: destroy texture
@@ -1844,7 +1919,7 @@ void backend_end_frame(renderer* ren, f32 delta_time) {
context.queue_complete_semaphores[context.current_frame], context.image_index);
}
-void gfx_backend_draw_frame(renderer* ren, camera* cam, mat4 model) {
+void gfx_backend_draw_frame(renderer* ren, camera* cam, mat4 model, texture* tex) {
backend_begin_frame(ren, 16.0);
mat4 proj;
@@ -1852,7 +1927,9 @@ void gfx_backend_draw_frame(renderer* ren, camera* cam, mat4 model) {
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);
diff --git a/src/renderer/render.c b/src/renderer/render.c
index 2b7f95c..b1e2a46 100644
--- a/src/renderer/render.c
+++ b/src/renderer/render.c
@@ -1,4 +1,8 @@
+#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
@@ -61,6 +65,9 @@ bool renderer_init(renderer* ren) {
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;
@@ -91,6 +98,22 @@ void default_material_init() {
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;
@@ -153,14 +176,8 @@ void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* v
bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1
uniform_f32(lighting_shader.program_id, "material.shininess", 32.);
- // 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));
-
+ // upload model, view, and projection matrices
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);
@@ -169,6 +186,89 @@ void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* v
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);
@@ -184,6 +284,7 @@ void model_upload_meshes(renderer* ren, model* model) {
// 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
@@ -193,49 +294,53 @@ void model_upload_meshes(renderer* ren, model* model) {
// 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;
+ 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, sizeof(verts), verts, GL_STATIC_DRAW);
+ 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, 8 * sizeof(float), (void*)0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)0);
glEnableVertexAttribArray(0);
// normal vector attribute
- glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// tex coords
- glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
+ 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);
@@ -253,7 +358,7 @@ texture texture_data_load(const char* path, bool invert_y) {
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, 0); // STBI_rgb_alpha);
if (data) {
DEBUG("loaded texture: %s", path);
} else {
diff --git a/src/renderer/render.h b/src/renderer/render.h
index b687ad1..31cf3b0 100644
--- a/src/renderer/render.h
+++ b/src/renderer/render.h
@@ -38,5 +38,9 @@ void draw_mesh(renderer* ren, mesh* mesh, mat4* tf, material* mat, mat4* view, m
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/render_backend.h b/src/renderer/render_backend.h
index af62f9f..da30bcc 100644
--- a/src/renderer/render_backend.h
+++ b/src/renderer/render_backend.h
@@ -13,7 +13,7 @@
bool gfx_backend_init(renderer* ren);
void gfx_backend_shutdown(renderer* ren);
-void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model);
+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);
diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h
index af20999..387ac81 100644
--- a/src/renderer/render_types.h
+++ b/src/renderer/render_types.h
@@ -7,10 +7,11 @@
// */
// #pragma once
-// #include "darray.h"
-// #include "maths.h"
-// #include "maths_types.h"
-// #include "str.h"
+#include "animation.h"
+#include "darray.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "str.h"
// struct GLFWwindow;
@@ -40,26 +41,27 @@
// 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;
-// } renderer;
+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 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];
@@ -80,10 +82,15 @@
// 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_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 {
@@ -113,34 +120,47 @@
// 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;
+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;
-// bool is_loaded;
-// bool is_uploaded;
-// } model;
+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"
diff --git a/src/resources/gltf.c b/src/resources/gltf.c
index f4e11f2..81992d1 100644
--- a/src/resources/gltf.c
+++ b/src/resources/gltf.c
@@ -1,14 +1,19 @@
#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_types.h"
+#include "render_backend.h"
+#include "render_types.h"
#include "str.h"
#define CGLTF_IMPLEMENTATION
@@ -22,7 +27,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);
@@ -59,6 +67,14 @@ 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,
@@ -69,7 +85,10 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
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);
+ vec4u_darray *tmp_joint_indices = vec4u_darray_new(1000);
+ vec4_darray *tmp_weights = vec4_darray_new(1000);
+ 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;
@@ -83,6 +102,51 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
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;
@@ -127,6 +191,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
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];
@@ -137,6 +202,8 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
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);
@@ -171,17 +238,76 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
vec2_darray_push(tmp_uvs, tex);
}
} else if (attribute.type == cgltf_attribute_type_joints) {
- // TODO: handle 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;
+ 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;
+ }
+ }
+ }
+
+ 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));
@@ -202,23 +328,27 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
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
}
- 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);
+
+ // 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++) {
@@ -226,6 +356,125 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
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..ea73ffa 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"
diff --git a/src/std/mem.c b/src/std/mem.c
index 00f9c39..5468898 100644
--- a/src/std/mem.c
+++ b/src/std/mem.c
@@ -15,7 +15,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);
}
diff --git a/xmake.lua b/xmake.lua
index 8f2603e..63b3d9c 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -168,6 +168,27 @@ target("transforms")
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")