diff options
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 Binary files differnew file mode 100644 index 0000000..72f7d2d --- /dev/null +++ b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube.bin 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 Binary files differnew file mode 100644 index 0000000..5e5cb20 --- /dev/null +++ b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_BaseColor.png diff --git a/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_MetallicRoughness.png b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_MetallicRoughness.png Binary files differnew file mode 100644 index 0000000..efd2026 --- /dev/null +++ b/assets/models/gltf/AnimatedCube/glTF/AnimatedCube_MetallicRoughness.png 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 Binary files differnew file mode 100644 index 0000000..eea74ee --- /dev/null +++ b/assets/models/gltf/AnimatedCube/screenshot/screenshot.gif 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 Binary files differnew file mode 100644 index 0000000..5af0b5e --- /dev/null +++ b/assets/models/gltf/SimpleSkin/glTF/inverseBindMatrices.bin diff --git a/assets/models/gltf/SimpleSkin/glTF/skinAnimation.bin b/assets/models/gltf/SimpleSkin/glTF/skinAnimation.bin Binary files differnew file mode 100644 index 0000000..ff550c6 --- /dev/null +++ b/assets/models/gltf/SimpleSkin/glTF/skinAnimation.bin diff --git a/assets/models/gltf/SimpleSkin/glTF/skinGeometry.bin b/assets/models/gltf/SimpleSkin/glTF/skinGeometry.bin Binary files differnew file mode 100644 index 0000000..15375c4 --- /dev/null +++ b/assets/models/gltf/SimpleSkin/glTF/skinGeometry.bin diff --git a/assets/models/gltf/SimpleSkin/glTF/skinningData.bin b/assets/models/gltf/SimpleSkin/glTF/skinningData.bin Binary files differnew file mode 100644 index 0000000..fd0c165 --- /dev/null +++ b/assets/models/gltf/SimpleSkin/glTF/skinningData.bin 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 Binary files differnew file mode 100644 index 0000000..a069f59 --- /dev/null +++ b/assets/models/gltf/SimpleSkin/screenshot/inverseBindMatrices.png diff --git a/assets/models/gltf/SimpleSkin/screenshot/screenshot.gif b/assets/models/gltf/SimpleSkin/screenshot/screenshot.gif Binary files differnew file mode 100644 index 0000000..c8fb353 --- /dev/null +++ b/assets/models/gltf/SimpleSkin/screenshot/screenshot.gif diff --git a/assets/models/gltf/SimpleSkin/screenshot/skinAnimation.png b/assets/models/gltf/SimpleSkin/screenshot/skinAnimation.png Binary files differnew file mode 100644 index 0000000..20257e1 --- /dev/null +++ b/assets/models/gltf/SimpleSkin/screenshot/skinAnimation.png diff --git a/assets/models/gltf/SimpleSkin/screenshot/skinGeometry.png b/assets/models/gltf/SimpleSkin/screenshot/skinGeometry.png Binary files differnew file mode 100644 index 0000000..2569d9f --- /dev/null +++ b/assets/models/gltf/SimpleSkin/screenshot/skinGeometry.png diff --git a/assets/models/gltf/SimpleSkin/screenshot/skinningData.png b/assets/models/gltf/SimpleSkin/screenshot/skinningData.png Binary files differnew file mode 100644 index 0000000..bd25b70 --- /dev/null +++ b/assets/models/gltf/SimpleSkin/screenshot/skinningData.png 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); } @@ -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") |