Merge branch 'feature/gltf-loading'
ci/woodpecker/push/woodpecker Pipeline failed
Details
ci/woodpecker/push/woodpecker Pipeline failed
Details
This commit is contained in:
commit
e6da582ee3
|
@ -1 +1 @@
|
|||
/target
|
||||
target
|
|
@ -7,20 +7,20 @@
|
|||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'lyra-engine'",
|
||||
"name": "Debug lyra testbed",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=lyra-engine",
|
||||
"--package=lyra-engine"
|
||||
"--manifest-path", "${workspaceFolder}/examples/testbed/Cargo.toml"
|
||||
//"--bin=testbed",
|
||||
],
|
||||
"filter": {
|
||||
"name": "lyra-engine",
|
||||
"name": "testbed",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
"cwd": "${workspaceFolder}/examples/testbed"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
steps:
|
||||
build:
|
||||
image: rust:1.73
|
||||
commands:
|
||||
- cargo build --release
|
||||
- cargo test
|
|
@ -297,6 +297,18 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
|
@ -890,6 +902,44 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gltf"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad2dcfb6dd7a66f9eb3d181a29dcfb22d146b0bcdc2e1ed1713cbf03939a88ea"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"byteorder",
|
||||
"gltf-json",
|
||||
"image",
|
||||
"lazy_static",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gltf-derive"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2cbcea5dd47e7ad4e9ee6f040384fcd7204bbf671aa4f9e7ca7dfc9bfa1de20"
|
||||
dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gltf-json"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5b810806b78dde4b71a95cc0e6fdcab34c4c617da3574df166f9987be97d03"
|
||||
dependencies = [
|
||||
"gltf-derive",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpu-alloc"
|
||||
version = "0.5.4"
|
||||
|
@ -1041,6 +1091,18 @@ dependencies = [
|
|||
"hashbrown 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "infer"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199"
|
||||
|
||||
[[package]]
|
||||
name = "inflections"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
|
@ -1232,6 +1294,7 @@ dependencies = [
|
|||
"tracing-appender",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
"wgpu",
|
||||
"winit",
|
||||
]
|
||||
|
@ -1241,8 +1304,16 @@ name = "lyra-resource"
|
|||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.4",
|
||||
"edict",
|
||||
"glam",
|
||||
"gltf",
|
||||
"image",
|
||||
"infer",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
@ -1311,6 +1382,12 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
@ -1476,7 +1553,7 @@ checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1"
|
|||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand 0.4.6",
|
||||
"rustc-serialize",
|
||||
]
|
||||
|
||||
|
@ -1791,6 +1868,12 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-easy"
|
||||
version = "0.3.0"
|
||||
|
@ -1858,6 +1941,27 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
|
@ -1873,6 +1977,15 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "range-alloc"
|
||||
version = "0.1.3"
|
||||
|
@ -1963,6 +2076,12 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
|
@ -1994,6 +2113,28 @@ version = "1.0.185"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.179"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
|
@ -2372,12 +2513,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.4.1"
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -36,3 +36,4 @@ aligned-vec = "0.5.0"
|
|||
tracing-appender = "0.2.2"
|
||||
stopwatch = "0.0.7"
|
||||
petgraph = "0.6.4"
|
||||
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "testbed"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lyra-engine = { path = "../../", version = "0.0.1" }
|
||||
anyhow = "1.0.75"
|
||||
async-std = "1.12.0"
|
||||
tracing = "0.1.37"
|
||||
fps_counter = "2.0.0"
|
||||
|
||||
[workspace]
|
Binary file not shown.
|
@ -0,0 +1,121 @@
|
|||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v3.6.5",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Cube"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorFactor":[
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1
|
||||
],
|
||||
"metallicFactor":0,
|
||||
"roughnessFactor":0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Cube",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"TEXCOORD_0":1,
|
||||
"NORMAL":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"max":[
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min":[
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":36,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":192,
|
||||
"byteOffset":288,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":480,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":72,
|
||||
"byteOffset":768,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":840,
|
||||
"uri":"data:application/octet-stream;base64,AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAQAOABQAAQAUAAcACgAGABIACgASABYAFwATAAwAFwAMABAADwADAAkADwAJABUABQACAAgABQAIAAsAEQANAAAAEQAAAAQA"
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
@ -0,0 +1,137 @@
|
|||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v3.6.6",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Cube"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorTexture":{
|
||||
"index":0
|
||||
},
|
||||
"metallicFactor":0,
|
||||
"roughnessFactor":0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Cube",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"TEXCOORD_0":1,
|
||||
"NORMAL":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures":[
|
||||
{
|
||||
"sampler":0,
|
||||
"source":0
|
||||
}
|
||||
],
|
||||
"images":[
|
||||
{
|
||||
"mimeType":"image/png",
|
||||
"name":"uvgrid",
|
||||
"uri":"uvgrid.png"
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"max":[
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min":[
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":36,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":192,
|
||||
"byteOffset":288,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":480,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":72,
|
||||
"byteOffset":768,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"samplers":[
|
||||
{
|
||||
"magFilter":9729,
|
||||
"minFilter":9987
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":840,
|
||||
"uri":"texture-sep.bin"
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
|
@ -0,0 +1,214 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use lyra_engine::{math, ecs::{World, components::{mesh::MeshComponent, transform::TransformComponent, camera::CameraComponent, model::ModelComponent}, atomicell::{Ref, RefMut}, EventQueue}, render::{mesh::Mesh, material::Material, vertex::Vertex, window::{CursorGrabMode, WindowOptions}}, math::Transform, input::{KeyCode, InputButtons, MouseMotion}, game::Game, change_tracker::Ct};
|
||||
|
||||
use lyra_engine::assets::{ResourceManager, Texture, Model};
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
/* pub const VERTICES: &[Vertex] = &[
|
||||
Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A
|
||||
Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B
|
||||
Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.949397], }, // C
|
||||
Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.84732914], }, // D
|
||||
Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E
|
||||
]; */
|
||||
|
||||
pub const INDICES: &[u16] = &[
|
||||
0, 1, 4,
|
||||
1, 2, 4,
|
||||
2, 3, 4,
|
||||
];
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Point2d {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Point2d {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "(x={}, y={})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Point2d {
|
||||
pub fn new(x: i32, y: i32) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Point3d {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Point3d {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "(x={}, y={}, z={})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl Point3d {
|
||||
pub fn new(x: i32, y: i32, z: i32) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let setup_sys = |world: &mut World| -> anyhow::Result<()> {
|
||||
/* {
|
||||
let mut window_options = world.get_resource_mut::<Ct<WindowOptions>>().unwrap();
|
||||
window_options.cursor_grab = CursorGrabMode::Confined;
|
||||
window_options.cursor_visible = false;
|
||||
} */
|
||||
|
||||
let mut resman = world.get_resource_mut::<ResourceManager>().unwrap();
|
||||
let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
|
||||
let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
|
||||
let cube_model = resman.request::<Model>("assets/texture-sep/texture-sep.gltf").unwrap();
|
||||
drop(resman);
|
||||
|
||||
/* world.spawn((
|
||||
ModelComponent(cube_model.clone()),
|
||||
TransformComponent::from(Transform::from_xyz(3.0, 0.5, -2.2)),
|
||||
)); */
|
||||
|
||||
world.spawn((
|
||||
ModelComponent(antique_camera_model),
|
||||
TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)),
|
||||
));
|
||||
|
||||
let mut camera = CameraComponent::new_3d();
|
||||
camera.transform.translation += math::Vec3::new(0.0, 0.0, 7.5);
|
||||
//camera.transform.rotate_y(Angle::Degrees(-25.0));
|
||||
camera.transform.rotate_z(math::Angle::Degrees(-90.0));
|
||||
world.spawn((camera,));
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let fps_system = |world: &mut World| -> anyhow::Result<()> {
|
||||
let mut counter: RefMut<fps_counter::FPSCounter> = world.get_resource_mut().unwrap();
|
||||
|
||||
let fps = counter.tick();
|
||||
|
||||
debug!("FPS: {fps}");
|
||||
|
||||
Ok(())
|
||||
};
|
||||
let fps_plugin = move |game: &mut Game| {
|
||||
let world = game.world();
|
||||
world.insert_resource(fps_counter::FPSCounter::new());
|
||||
|
||||
game.with_system("fps", fps_system, &["input"]);
|
||||
};
|
||||
|
||||
let jiggle_system = |world: &mut World| -> anyhow::Result<()> {
|
||||
let keys = world.get_resource::<InputButtons<KeyCode>>();
|
||||
if keys.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let keys = keys.unwrap();
|
||||
|
||||
let speed = 0.01;
|
||||
let rot_speed = 1.0;
|
||||
|
||||
let mut dir_x = 0.0;
|
||||
let mut dir_y = 0.0;
|
||||
let mut dir_z = 0.0;
|
||||
|
||||
let mut rot_x = 0.0;
|
||||
let mut rot_y = 0.0;
|
||||
|
||||
if keys.is_pressed(KeyCode::A) {
|
||||
dir_x += speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::D) {
|
||||
dir_x -= speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::S) {
|
||||
dir_y += speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::W) {
|
||||
dir_y -= speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::E) {
|
||||
dir_z += speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::Q) {
|
||||
dir_z -= speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::Left) {
|
||||
rot_y -= rot_speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::Right) {
|
||||
rot_y += rot_speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::Up) {
|
||||
rot_x -= rot_speed;
|
||||
}
|
||||
|
||||
if keys.is_pressed(KeyCode::Down) {
|
||||
rot_x += rot_speed;
|
||||
}
|
||||
|
||||
drop(keys);
|
||||
|
||||
if dir_x == 0.0 && dir_y == 0.0 && dir_z == 0.0 && rot_x == 0.0 && rot_y == 0.0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
//debug!("moving by ({}, {})", dir_x, dir_y);
|
||||
|
||||
for transform in world.query_mut::<(&mut TransformComponent,)>().iter_mut() {
|
||||
let t = &mut transform.transform;
|
||||
//debug!("Translation: {}", t.translation);
|
||||
//debug!("Rotation: {}", t.rotation);
|
||||
|
||||
/* t.translation += glam::Vec3::new(0.0, 0.001, 0.0);
|
||||
t.translation.x *= -1.0; */
|
||||
t.translation.x += dir_x;
|
||||
t.translation.y += dir_y;
|
||||
t.translation.z += dir_z;
|
||||
t.rotate_x(math::Angle::Degrees(rot_x));
|
||||
t.rotate_y(math::Angle::Degrees(rot_y));
|
||||
}
|
||||
|
||||
let events = world.get_resource_mut::<EventQueue>().unwrap();
|
||||
if let Some(mm) = events.read_events::<MouseMotion>() {
|
||||
debug!("Mouse motion: {:?}", mm);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let jiggle_plugin = move |game: &mut Game| {
|
||||
game.with_system("jiggle", jiggle_system, &["input"]);
|
||||
};
|
||||
|
||||
Game::initialize().await
|
||||
.with_plugin(lyra_engine::DefaultPlugins)
|
||||
.with_startup_system(setup_sys)
|
||||
//.with_plugin(fps_plugin)
|
||||
.with_plugin(jiggle_plugin)
|
||||
.run().await;
|
||||
}
|
|
@ -7,6 +7,15 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.4"
|
||||
edict = "0.5.0"
|
||||
glam = "0.24.1"
|
||||
gltf = { version = "1.3.0", features = ["KHR_materials_pbrSpecularGlossiness"] }
|
||||
image = "0.24.7"
|
||||
# not using custom matcher, or file type from file path
|
||||
infer = { version = "0.15.0", default-features = false }
|
||||
mime = "0.3.17"
|
||||
percent-encoding = "2.3.0"
|
||||
thiserror = "1.0.48"
|
||||
tracing = "0.1.37"
|
||||
uuid = { version = "1.4.1", features = ["v4"] }
|
||||
|
|
|
@ -9,3 +9,11 @@ pub use texture::*;
|
|||
|
||||
pub mod loader;
|
||||
pub use loader::*;
|
||||
|
||||
pub mod model;
|
||||
pub use model::*;
|
||||
|
||||
pub mod material;
|
||||
pub use material::*;
|
||||
|
||||
pub(crate) mod util;
|
|
@ -0,0 +1,115 @@
|
|||
use std::{fs::File, sync::Arc, io::Read};
|
||||
|
||||
use image::ImageError;
|
||||
|
||||
use crate::{resource_manager::ResourceStorage, texture::Texture, resource::Resource, ResourceManager};
|
||||
|
||||
use super::{LoaderError, ResourceLoader};
|
||||
|
||||
impl From<ImageError> for LoaderError {
|
||||
fn from(value: ImageError) -> Self {
|
||||
LoaderError::DecodingError(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct that implements the `ResourceLoader` trait used for loading textures.
|
||||
#[derive(Default)]
|
||||
pub struct ImageLoader;
|
||||
|
||||
impl ResourceLoader for ImageLoader {
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&[
|
||||
// the extensions of these are the names of the formats
|
||||
"bmp", "dds", "gif", "ico", "jpeg", "jpg", "png", "qoi", "tga", "tiff", "webp",
|
||||
|
||||
// farbfeld
|
||||
"ff",
|
||||
|
||||
// pnm
|
||||
"pnm", "pbm", "pgm", "ppm",
|
||||
]
|
||||
}
|
||||
|
||||
fn mime_types(&self) -> &[&str] {
|
||||
&[
|
||||
"image/bmp", "image/vnd.ms-dds", "image/gif", "image/x-icon", "image/jpeg",
|
||||
"image/png", "image/qoi", "image/tga", "image/tiff", "image/webp",
|
||||
|
||||
// no known mime for farbfeld
|
||||
|
||||
// pnm, pbm, pgm, ppm
|
||||
"image/x-portable-anymap", "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap",
|
||||
]
|
||||
}
|
||||
|
||||
fn load(&self, _resource_manager: &mut ResourceManager, path: &str) -> Result<Arc<dyn ResourceStorage>, LoaderError> {
|
||||
// check if the file is supported by this loader
|
||||
if !self.does_support_file(path) {
|
||||
return Err(LoaderError::UnsupportedExtension(path.to_string()));
|
||||
}
|
||||
|
||||
// read file bytes
|
||||
let mut file = File::open(path)?;
|
||||
let mut buf = vec![];
|
||||
file.read_to_end(&mut buf)?;
|
||||
|
||||
// load the image and construct Resource<Texture>
|
||||
let image = image::load_from_memory(&buf)
|
||||
.map_err(|e| match e {
|
||||
ImageError::IoError(e) => LoaderError::IoError(e),
|
||||
_ => LoaderError::DecodingError(e.into()),
|
||||
})?;
|
||||
let texture = Texture {
|
||||
image,
|
||||
};
|
||||
let res = Resource::with_data(path, texture);
|
||||
|
||||
Ok(Arc::new(res))
|
||||
}
|
||||
|
||||
fn load_bytes(&self, _resource_manager: &mut ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> Result<Arc<dyn ResourceStorage>, LoaderError> {
|
||||
let image = image::load_from_memory(&bytes[offset..(length-offset)])
|
||||
.map_err(|e| match e {
|
||||
ImageError::IoError(e) => LoaderError::IoError(e),
|
||||
_ => LoaderError::DecodingError(e.into()),
|
||||
})?;
|
||||
let texture = Texture {
|
||||
image,
|
||||
};
|
||||
let res = Resource::with_data(&uuid::Uuid::new_v4().to_string(), texture);
|
||||
|
||||
Ok(Arc::new(res))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn get_image(path: &str) -> String {
|
||||
let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
|
||||
format!("{manifest}/test_files/img/{path}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_unsupport() {
|
||||
let loader = ImageLoader::default();
|
||||
assert_eq!(loader.does_support_file("test.gltf"), false);
|
||||
}
|
||||
|
||||
/// Tests loading an image
|
||||
#[test]
|
||||
fn image_load() {
|
||||
let mut manager = ResourceManager::new();
|
||||
let loader = ImageLoader::default();
|
||||
loader.load(&mut manager, &get_image("squiggles.png")).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_load_unsupported() {
|
||||
let mut manager = ResourceManager::new();
|
||||
let loader = ImageLoader::default();
|
||||
assert!(loader.load(&mut manager, &get_image("squiggles.gltf")).is_err());
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
pub mod texture;
|
||||
pub mod image;
|
||||
pub mod model;
|
||||
|
||||
use std::{io, sync::Arc, fs::File};
|
||||
use std::{io, sync::Arc, path::Path, ffi::OsStr};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::resource_manager::ResourceStorage;
|
||||
use crate::{resource_manager::ResourceStorage, ResourceManager};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LoaderError {
|
||||
|
@ -15,7 +16,7 @@ pub enum LoaderError {
|
|||
UnsupportedExtension(String),
|
||||
|
||||
#[error("IOError: '{0}'")]
|
||||
IOError(io::Error),
|
||||
IoError(io::Error),
|
||||
|
||||
// From is implemented for this field in each loader module
|
||||
#[error("Decoding error: '{0}'")]
|
||||
|
@ -24,12 +25,50 @@ pub enum LoaderError {
|
|||
|
||||
impl From<io::Error> for LoaderError {
|
||||
fn from(value: io::Error) -> Self {
|
||||
LoaderError::IOError(value)
|
||||
LoaderError::IoError(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResourceLoader: Send + Sync {
|
||||
/// Returns the extensions that this loader supports.
|
||||
fn extensions(&self) -> &[&str];
|
||||
fn does_support_file(&self, path: &str) -> bool;
|
||||
fn load(&self, path: &str) -> Result<Arc<dyn ResourceStorage>, LoaderError>;
|
||||
/// Returns the mime types that this loader supports.
|
||||
fn mime_types(&self) -> &[&str];
|
||||
|
||||
/// Returns true if this loader supports the file.
|
||||
fn does_support_file(&self, path: &str) -> bool {
|
||||
match Path::new(path).extension().and_then(OsStr::to_str) {
|
||||
Some(ext) => {
|
||||
self.extensions().contains(&ext)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this loader supports the mime type.
|
||||
fn does_support_mime(&self, mime: &str) -> bool {
|
||||
self.mime_types().contains(&mime)
|
||||
}
|
||||
|
||||
/// Load a resource from a path.
|
||||
fn load(&self, resource_manager: &mut ResourceManager, path: &str) -> Result<Arc<dyn ResourceStorage>, LoaderError>;
|
||||
/// Load a resource from bytes.
|
||||
fn load_bytes(&self, resource_manager: &mut ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> Result<Arc<dyn ResourceStorage>, LoaderError>;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{*, image::ImageLoader};
|
||||
|
||||
/// Ensure that `does_support_file` works
|
||||
#[test]
|
||||
fn check_support() {
|
||||
let loader = ImageLoader::default();
|
||||
let extensions = loader.extensions();
|
||||
let fake_paths: Vec<String> = extensions.iter().map(|e| format!("a.{}", e)).collect();
|
||||
for path in fake_paths.iter() {
|
||||
assert!(loader.does_support_file(&path));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
use std::{sync::Arc, path::{Path, PathBuf}};
|
||||
|
||||
use base64::Engine;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{ResourceLoader, LoaderError, Mesh, Model, MeshVertexAttribute, VertexAttributeData, Resource, Material, MeshIndices, ResourceManager, util};
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
impl From<gltf::Error> for LoaderError {
|
||||
fn from(value: gltf::Error) -> Self {
|
||||
LoaderError::DecodingError(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum ModelLoaderError {
|
||||
#[error("The model ({0}) is missing the BIN section in the gltf file")]
|
||||
MissingBin(String),
|
||||
#[error("There was an error with decoding a uri defined in the model: '{0}'")]
|
||||
UriDecodingError(util::UriReadError),
|
||||
}
|
||||
|
||||
impl From<ModelLoaderError> for LoaderError {
|
||||
fn from(value: ModelLoaderError) -> Self {
|
||||
LoaderError::DecodingError(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct GltfLoadContext<'a> {
|
||||
pub resource_manager: &'a mut ResourceManager,
|
||||
pub gltf: &'a gltf::Gltf,
|
||||
/// Path to the gltf
|
||||
pub gltf_path: &'a str,
|
||||
/// The path to the directory that the gltf is contained in.
|
||||
pub gltf_parent_path: &'a str,
|
||||
/// List of buffers in the gltf
|
||||
pub buffers: &'a Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ModelLoader;
|
||||
|
||||
impl ModelLoader {
|
||||
/* fn parse_uri(containing_path: &str, uri: &str) -> Option<Vec<u8>> {
|
||||
let uri = uri.strip_prefix("data")?;
|
||||
let (mime, data) = uri.split_once(",")?;
|
||||
|
||||
let (_mime, is_base64) = match mime.strip_suffix(";base64") {
|
||||
Some(mime) => (mime, true),
|
||||
None => (mime, false),
|
||||
};
|
||||
|
||||
if is_base64 {
|
||||
Some(base64::engine::general_purpose::STANDARD.decode(data).unwrap())
|
||||
} else {
|
||||
let full_path = format!("{containing_path}/{data}");
|
||||
let buf = std::fs::read(&full_path).unwrap();
|
||||
Some(buf)
|
||||
}
|
||||
} */
|
||||
|
||||
fn process_node(&self, buffers: &Vec<Vec<u8>>, materials: &Vec<Material>, node: gltf::Node<'_>) -> Vec<Mesh> {
|
||||
let mut meshes = vec![];
|
||||
if let Some(mesh) = node.mesh() {
|
||||
for prim in mesh.primitives() {
|
||||
let reader = prim.reader(|buf| Some(buffers[buf.index()].as_slice()));
|
||||
|
||||
let mut new_mesh = Mesh::default();
|
||||
|
||||
// read the positions
|
||||
if let Some(pos) = reader.read_positions() {
|
||||
if prim.mode() != gltf::mesh::Mode::Triangles {
|
||||
todo!("Load position primitives that aren't triangles"); // TODO
|
||||
}
|
||||
|
||||
let pos: Vec<glam::Vec3> = pos.map(|t| t.into()).collect();
|
||||
new_mesh.add_attribute(MeshVertexAttribute::Position, VertexAttributeData::Vec3(pos));
|
||||
}
|
||||
|
||||
// read the normals
|
||||
if let Some(norms) = reader.read_normals() {
|
||||
let norms: Vec<glam::Vec3> = norms.map(|t| t.into()).collect();
|
||||
new_mesh.add_attribute(MeshVertexAttribute::Normals, VertexAttributeData::Vec3(norms));
|
||||
}
|
||||
|
||||
// read the tangents
|
||||
if let Some(tangents) = reader.read_tangents() {
|
||||
let tangents: Vec<glam::Vec4> = tangents.map(|t| t.into()).collect();
|
||||
new_mesh.add_attribute(MeshVertexAttribute::Tangents, VertexAttributeData::Vec4(tangents));
|
||||
}
|
||||
|
||||
// read tex coords
|
||||
if let Some(tex_coords) = reader.read_tex_coords(0) {
|
||||
let tex_coords: Vec<glam::Vec2> = tex_coords.into_f32().map(|t| t.into()).collect();
|
||||
new_mesh.add_attribute(MeshVertexAttribute::TexCoords, VertexAttributeData::Vec2(tex_coords));
|
||||
}
|
||||
|
||||
// read the indices
|
||||
if let Some(indices) = reader.read_indices() {
|
||||
let indices: MeshIndices = match indices {
|
||||
// wpgu doesn't support u8 indices, so those must be converted to u16
|
||||
gltf::mesh::util::ReadIndices::U8(i) => MeshIndices::U16(i.map(|i| i as u16).collect()),
|
||||
gltf::mesh::util::ReadIndices::U16(i) => MeshIndices::U16(i.collect()),
|
||||
gltf::mesh::util::ReadIndices::U32(i) => MeshIndices::U32(i.collect()),
|
||||
};
|
||||
|
||||
new_mesh.indices = Some(indices);
|
||||
}
|
||||
|
||||
let mat = materials.get(prim.material().index().unwrap()).unwrap();
|
||||
new_mesh.set_material(mat.clone());
|
||||
//prim.material().
|
||||
|
||||
meshes.push(new_mesh);
|
||||
}
|
||||
}
|
||||
|
||||
for child in node.children() {
|
||||
let mut child_meshes = self.process_node(buffers, materials, child);
|
||||
meshes.append(&mut child_meshes);
|
||||
}
|
||||
|
||||
meshes
|
||||
}
|
||||
}
|
||||
|
||||
impl ResourceLoader for ModelLoader {
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&[
|
||||
"gltf", "glb"
|
||||
]
|
||||
}
|
||||
|
||||
fn mime_types(&self) -> &[&str] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn load(&self, resource_manager: &mut ResourceManager, path: &str) -> Result<std::sync::Arc<dyn crate::ResourceStorage>, crate::LoaderError> {
|
||||
// check if the file is supported by this loader
|
||||
if !self.does_support_file(path) {
|
||||
return Err(LoaderError::UnsupportedExtension(path.to_string()));
|
||||
}
|
||||
|
||||
let mut parent_path = PathBuf::from(path);
|
||||
parent_path.pop();
|
||||
let parent_path = parent_path.display().to_string();
|
||||
|
||||
/* let (document, buffers, images) = gltf::import(&path)?;
|
||||
let buffers: Vec<Vec<u8>> = buffers.into_iter().map(|b| b.0).collect();
|
||||
|
||||
let scene = document.scenes().next().unwrap();
|
||||
|
||||
let materials: Vec<Material> = document.materials()
|
||||
.map(|mat| Material::from_gltf(resource_manager, &parent_path, mat)).collect();
|
||||
|
||||
let meshes: Vec<Mesh> = scene.nodes()
|
||||
.map(|node| self.process_node(&buffers, &materials, node))
|
||||
.flatten().collect(); */
|
||||
|
||||
let gltf = gltf::Gltf::open(path)?;
|
||||
|
||||
let mut use_bin = false;
|
||||
let buffers: Vec<Vec<u8>> = gltf.buffers().map(|b| match b.source() {
|
||||
gltf::buffer::Source::Bin => {
|
||||
use_bin = true;
|
||||
gltf.blob.as_deref().map(|v| v.to_vec())
|
||||
.ok_or(ModelLoaderError::MissingBin(path.to_string()))
|
||||
},
|
||||
gltf::buffer::Source::Uri(uri) => util::gltf_read_buffer_uri(&parent_path, uri)
|
||||
.map_err(|e| ModelLoaderError::UriDecodingError(e)),
|
||||
}).flatten().collect();
|
||||
|
||||
// TODO: Read in multiple scenes
|
||||
let scene = gltf.scenes().next().unwrap();
|
||||
|
||||
let mut context = GltfLoadContext {
|
||||
resource_manager,
|
||||
gltf: &gltf,
|
||||
gltf_path: path,
|
||||
gltf_parent_path: &parent_path,
|
||||
buffers: &buffers,
|
||||
};
|
||||
|
||||
let materials: Vec<Material> = gltf.materials()
|
||||
.map(|mat| Material::from_gltf(&mut context, mat)).collect();
|
||||
|
||||
let meshes: Vec<Mesh> = scene.nodes()
|
||||
.map(|node| self.process_node(&buffers, &materials, node))
|
||||
.flatten().collect();
|
||||
debug!("Loaded {} meshes, and {} materials from '{}'", meshes.len(), materials.len(), path);
|
||||
|
||||
Ok(Arc::new(Resource::with_data(path, Model::new(meshes))))
|
||||
}
|
||||
|
||||
fn load_bytes(&self, resource_manager: &mut ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> Result<Arc<dyn crate::ResourceStorage>, LoaderError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ResourceLoader;
|
||||
use super::*;
|
||||
|
||||
fn test_file_path(path: &str) -> String {
|
||||
let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
|
||||
format!("{manifest}/test_files/gltf/{path}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_loading() {
|
||||
let path = test_file_path("texture-embedded.gltf");
|
||||
|
||||
let mut manager = ResourceManager::new();
|
||||
let loader = ModelLoader::default();
|
||||
let model = loader.load(&mut manager, &path).unwrap();
|
||||
let model = Arc::downcast::<Resource<Model>>(model.as_arc_any()).unwrap();
|
||||
let model = model.data.as_ref().unwrap();
|
||||
assert_eq!(model.meshes.len(), 1); // There should only be 1 mesh
|
||||
let mesh = &model.meshes[0];
|
||||
assert!(mesh.position().unwrap().len() > 0);
|
||||
assert!(mesh.normals().unwrap().len() > 0);
|
||||
assert!(mesh.tex_coords().unwrap().len() > 0);
|
||||
assert!(mesh.indices.clone().unwrap().len() > 0);
|
||||
assert!(mesh.material().base_color_texture.is_some());
|
||||
let _mesh_mat = mesh.material(); // inner panic if material was not loaded
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
use std::{fs::File, sync::Arc, path::Path, ffi::OsStr, io::Read};
|
||||
|
||||
use image::ImageError;
|
||||
|
||||
use crate::{resource_manager::ResourceStorage, texture::Texture, resource::Resource};
|
||||
|
||||
use super::{LoaderError, ResourceLoader};
|
||||
|
||||
impl From<ImageError> for LoaderError {
|
||||
fn from(value: ImageError) -> Self {
|
||||
LoaderError::DecodingError(anyhow::Error::from(value))
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct that implements the `ResourceLoader` trait used for loading textures.
|
||||
#[derive(Default)]
|
||||
pub struct TextureLoader;
|
||||
|
||||
impl ResourceLoader for TextureLoader {
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&[
|
||||
// the extensions of these are the names of the formats
|
||||
"bmp", "dds", "gif", "ico", "jpeg", "jpg", "png", "qoi", "tga", "tiff", "webp",
|
||||
|
||||
// farbfeld
|
||||
"ff",
|
||||
|
||||
// pnm
|
||||
"pnm", "pbm", "pgm", "ppm", "pam",
|
||||
]
|
||||
}
|
||||
|
||||
fn does_support_file(&self, path: &str) -> bool {
|
||||
match Path::new(path).extension().and_then(OsStr::to_str) {
|
||||
Some(ext) => {
|
||||
self.extensions().contains(&ext)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn load(&self, path: &str) -> Result<Arc<dyn ResourceStorage>, LoaderError> {
|
||||
// check if the file is supported by this loader
|
||||
if !self.does_support_file(path) {
|
||||
return Err(LoaderError::UnsupportedExtension(path.to_string()));
|
||||
}
|
||||
|
||||
// read file bytes
|
||||
let mut file = File::open(path)?;
|
||||
let mut buf = vec![];
|
||||
file.read_to_end(&mut buf)?;
|
||||
|
||||
// load the image and construct Resource<Texture>
|
||||
let image = image::load_from_memory(&buf)
|
||||
.map_err(|e| match e {
|
||||
ImageError::IoError(e) => LoaderError::IOError(e),
|
||||
_ => LoaderError::DecodingError(e.into()),
|
||||
})?;
|
||||
let texture = Texture {
|
||||
image,
|
||||
};
|
||||
let res = Resource::with_data(path, texture);
|
||||
|
||||
Ok(Arc::new(res))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn get_image(path: &str) -> String {
|
||||
let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
|
||||
format!("{manifest}/test_files/img/{path}")
|
||||
}
|
||||
|
||||
/// Ensure that `does_support_file` works
|
||||
#[test]
|
||||
fn check_support() {
|
||||
let loader = TextureLoader::default();
|
||||
let extensions = loader.extensions();
|
||||
let fake_paths: Vec<String> = extensions.iter().map(|e| format!("a.{}", e)).collect();
|
||||
for path in fake_paths.iter() {
|
||||
assert!(loader.does_support_file(&path));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_unsupport() {
|
||||
let loader = TextureLoader::default();
|
||||
assert_eq!(loader.does_support_file("test.gltf"), false);
|
||||
}
|
||||
|
||||
/// Tests loading an image
|
||||
#[test]
|
||||
fn image_load() {
|
||||
let loader = TextureLoader::default();
|
||||
loader.load(&get_image("squiggles.png")).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_load_unsupported() {
|
||||
let loader = TextureLoader::default();
|
||||
assert!(loader.load(&get_image("squiggles.gltf")).is_err());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
use std::{fs::File, io::{BufReader, Read}, collections::hash_map::DefaultHasher, hash::{Hash, Hasher}};
|
||||
|
||||
use crate::{Texture, ResHandle, ResourceManager, util, loader::model::GltfLoadContext};
|
||||
|
||||
/// PBR metallic roughness
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PbrRoughness {
|
||||
/// The rgba base color of the PBR material
|
||||
pub base_color: [f32; 4],
|
||||
/// The metalness of the material
|
||||
/// From 0.0 (non-metal) to 1.0 (metal)
|
||||
pub metallic: f32,
|
||||
/// The roughness of the material
|
||||
/// From 0.0 (smooth) to 1.0 (rough)
|
||||
pub roughness: f32,
|
||||
// TODO: base_color_texture and metallic_roughness_texture
|
||||
}
|
||||
|
||||
impl From<gltf::material::PbrMetallicRoughness<'_>> for PbrRoughness {
|
||||
fn from(value: gltf::material::PbrMetallicRoughness) -> Self {
|
||||
PbrRoughness {
|
||||
base_color: value.base_color_factor(),
|
||||
metallic: value.metallic_factor(),
|
||||
roughness: value.roughness_factor(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PbrGlossiness {
|
||||
/// The rgba diffuse color of the material
|
||||
pub diffuse_color: glam::Vec4,
|
||||
// The base color texture
|
||||
// pub diffuse_texture // TODO
|
||||
pub specular: glam::Vec3,
|
||||
/// The glossiness factor of the material.
|
||||
/// From 0.0 (no glossiness) to 1.0 (full glossiness)
|
||||
pub glossiness: f32,
|
||||
// pub glossiness_texture // TODO
|
||||
}
|
||||
|
||||
impl From<gltf::material::PbrSpecularGlossiness<'_>> for PbrGlossiness {
|
||||
fn from(value: gltf::material::PbrSpecularGlossiness) -> Self {
|
||||
PbrGlossiness {
|
||||
diffuse_color: value.diffuse_factor().into(),
|
||||
specular: value.specular_factor().into(),
|
||||
glossiness: value.glossiness_factor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The alpha rendering mode of a material.
|
||||
/// This is essentially a re-export of gltf::material::AlphaMode
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)]
|
||||
pub enum AlphaMode {
|
||||
/// The alpha value is ignored and the rendered output is fully opaque.
|
||||
#[default]
|
||||
Opaque = 1,
|
||||
|
||||
/// The rendered output is either fully opaque or fully transparent depending on
|
||||
/// the alpha value and the specified alpha cutoff value.
|
||||
Mask,
|
||||
|
||||
/// The alpha value is used, to determine the transparency of the rendered output.
|
||||
/// The alpha cutoff value is ignored.
|
||||
Blend,
|
||||
}
|
||||
|
||||
impl From<gltf::material::AlphaMode> for AlphaMode {
|
||||
fn from(value: gltf::material::AlphaMode) -> Self {
|
||||
match value {
|
||||
gltf::material::AlphaMode::Opaque => AlphaMode::Opaque,
|
||||
gltf::material::AlphaMode::Mask => AlphaMode::Mask,
|
||||
gltf::material::AlphaMode::Blend => AlphaMode::Blend,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Material {
|
||||
pub shader_uuid: Option<u64>,
|
||||
pub name: Option<String>,
|
||||
pub double_sided: bool,
|
||||
|
||||
//pub pbr_roughness: PbrRoughness,
|
||||
/// The RGBA base color of the model. If a texture is supplied with `base_color_texture`, this value
|
||||
/// will tint the texture. If a texture is not provided, this value would be the color of the Material.
|
||||
pub base_color: glam::Vec4,
|
||||
/// The metalness of the material
|
||||
/// From 0.0 (non-metal) to 1.0 (metal)
|
||||
pub metallic: f32,
|
||||
/// The roughness of the material
|
||||
/// From 0.0 (smooth) to 1.0 (rough)
|
||||
pub roughness: f32,
|
||||
/// The base color texture of the model.
|
||||
pub base_color_texture: Option<ResHandle<Texture>>,
|
||||
|
||||
/// The metallic-roughness texture.
|
||||
///
|
||||
/// The metalness values are sampled from the B channel. The roughness values are sampled from
|
||||
/// the G channel. These values are linear. If other channels are present (R or A), they are
|
||||
/// ignored for metallic-roughness calculations.
|
||||
pub metallic_roughness_texture: Option<ResHandle<Texture>>,
|
||||
|
||||
/// A set of parameter values that are used to define the specular-glossiness material model
|
||||
/// from Physically-Based Rendering (PBR) methodology.
|
||||
/// GLTF extension: [KHR_materials_pbrSpecularGlossiness](https://kcoley.github.io/glTF/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness)
|
||||
pub pbr_glossiness: Option<PbrGlossiness>,
|
||||
|
||||
/// The optional alpha cutoff value of the material.
|
||||
pub alpha_cutoff: Option<f32>,
|
||||
|
||||
/// The alpha rendering mode of the material. The material's alpha rendering
|
||||
/// mode enumeration specifying the interpretation of the alpha value of the main
|
||||
/// factor and texture.
|
||||
///
|
||||
/// * In `Opaque` mode (default) the alpha value is ignored
|
||||
/// and the rendered output is fully opaque.
|
||||
/// * In `Mask` mode, the rendered
|
||||
/// output is either fully opaque or fully transparent depending on the alpha
|
||||
/// value and the specified alpha cutoff value.
|
||||
/// * In `Blend` mode, the alpha value is used to composite the source and
|
||||
/// destination areas and the rendered output is combined with the background
|
||||
/// using the normal painting operation (i.e. the Porter and Duff over
|
||||
/// operator).
|
||||
pub alpha_mode: AlphaMode,
|
||||
|
||||
//pub texture: Option<ResHandle<Texture>>,
|
||||
}
|
||||
|
||||
impl Material {
|
||||
/// Get a uri's identifier
|
||||
///
|
||||
/// I'm not actually sure how identifiable this would be
|
||||
fn uri_ident(gltf_rel_path: &str, uri: &str) -> String {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
uri.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
format!("{gltf_rel_path};{hash}")
|
||||
}
|
||||
|
||||
fn source_ident(gltf_rel_path: &str, src: &gltf::image::Source) -> Option<String> {
|
||||
match src {
|
||||
gltf::image::Source::View { view, mime_type } => {
|
||||
let buf = view.buffer();
|
||||
let src = buf.source();
|
||||
|
||||
match src {
|
||||
gltf::buffer::Source::Bin => None,
|
||||
gltf::buffer::Source::Uri(uri) => {
|
||||
Some(Material::uri_ident(gltf_rel_path, uri))
|
||||
}
|
||||
}
|
||||
},
|
||||
gltf::image::Source::Uri { uri, mime_type } => {
|
||||
Some(Material::uri_ident(gltf_rel_path, uri))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn read_source(context: &mut GltfLoadContext, src: gltf::image::Source) -> Result<Vec<u8>, util::UriReadError> {
|
||||
let gltf_rel_path = context.gltf_parent_path;
|
||||
// TODO: Don't copy sources
|
||||
match src {
|
||||
gltf::image::Source::View { view, mime_type: _ } => {
|
||||
let buf = view.buffer();
|
||||
let src = buf.source();
|
||||
|
||||
let offset = view.offset();
|
||||
let len = view.length();
|
||||
|
||||
match src {
|
||||
gltf::buffer::Source::Bin => {
|
||||
let mut b = context.gltf.blob.clone().unwrap();
|
||||
b.drain(0..offset);
|
||||
b.truncate(len);
|
||||
Ok(b)
|
||||
},
|
||||
gltf::buffer::Source::Uri(uri) => {
|
||||
util::gltf_read_buffer_uri(gltf_rel_path, uri)
|
||||
.map(|mut buf| {
|
||||
buf.drain(0..offset);
|
||||
buf.truncate(len);
|
||||
buf
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
gltf::image::Source::Uri { uri, mime_type: _ } => {
|
||||
util::gltf_read_buffer_uri(gltf_rel_path, uri)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn load_texture(context: &mut GltfLoadContext, texture_info: gltf::texture::Info<'_>) -> ResHandle<Texture> {
|
||||
// TODO: texture_info.tex_coord()
|
||||
let tex = texture_info.texture();
|
||||
let img = tex.source();
|
||||
let src = img.source();
|
||||
|
||||
let buf = Material::read_source(context, src).unwrap();
|
||||
let buflen = buf.len();
|
||||
let mime_type = infer::get(&buf).expect("Failed to get file type").mime_type();
|
||||
|
||||
context.resource_manager.load_bytes::<Texture>(&uuid::Uuid::new_v4().to_string(), mime_type,
|
||||
buf, 0, buflen).unwrap()
|
||||
}
|
||||
|
||||
/// Load the Material from a gltf::Material.
|
||||
///
|
||||
/// `gltf_rel_path`: The relative path of the gltf file,
|
||||
/// e.g. gltf model path is "resource/models/player.gltf", the relative path would be "resource/models/"
|
||||
pub(crate) fn from_gltf(context: &mut GltfLoadContext, gltf_mat: gltf::Material) -> Self {
|
||||
let pbr_rough = gltf_mat.pbr_metallic_roughness();
|
||||
let base_color = pbr_rough.base_color_factor().into();
|
||||
let metallic = pbr_rough.metallic_factor();
|
||||
let roughness = pbr_rough.roughness_factor();
|
||||
|
||||
let base_color_texture = pbr_rough.base_color_texture()
|
||||
.map(|info| Material::load_texture(context, info));
|
||||
|
||||
let metallic_roughness_texture = pbr_rough.metallic_roughness_texture()
|
||||
.map(|info| Material::load_texture(context, info));
|
||||
|
||||
Material {
|
||||
name: gltf_mat.name()
|
||||
.map(|s| s.to_string()),
|
||||
double_sided: gltf_mat.double_sided(),
|
||||
base_color,
|
||||
metallic,
|
||||
roughness,
|
||||
pbr_glossiness: gltf_mat.pbr_specular_glossiness()
|
||||
.map(|o| o.into()),
|
||||
alpha_cutoff: gltf_mat.alpha_cutoff(),
|
||||
alpha_mode: gltf_mat.alpha_mode().into(),
|
||||
shader_uuid: None,
|
||||
|
||||
// TODO
|
||||
base_color_texture,
|
||||
metallic_roughness_texture,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::Material;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum MeshIndices {
|
||||
//U8(Vec<u8>),
|
||||
U16(Vec<u16>),
|
||||
U32(Vec<u32>),
|
||||
}
|
||||
|
||||
impl MeshIndices {
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
MeshIndices::U16(v) => v.len(),
|
||||
MeshIndices::U32(v) => v.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* impl From<Vec<u8>> for MeshIndices {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
MeshIndices::U8(value)
|
||||
}
|
||||
} */
|
||||
|
||||
impl From<Vec<u16>> for MeshIndices {
|
||||
fn from(value: Vec<u16>) -> Self {
|
||||
MeshIndices::U16(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u32>> for MeshIndices {
|
||||
fn from(value: Vec<u32>) -> Self {
|
||||
MeshIndices::U32(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum VertexAttributeData {
|
||||
Vec2(Vec<glam::Vec2>),
|
||||
Vec3(Vec<glam::Vec3>),
|
||||
Vec4(Vec<glam::Vec4>),
|
||||
}
|
||||
|
||||
impl VertexAttributeData {
|
||||
/// Take self as a list of Vec2's
|
||||
pub fn as_vec2(&self) -> &Vec<glam::Vec2> {
|
||||
match self {
|
||||
VertexAttributeData::Vec2(v) => v,
|
||||
_ => panic!("Attempt to get {self:?} as `Vec2`"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec3(&self) -> &Vec<glam::Vec3> {
|
||||
match self {
|
||||
VertexAttributeData::Vec3(v) => v,
|
||||
_ => panic!("Attempt to get {self:?} as `Vec3`"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum MeshVertexAttribute {
|
||||
Position,
|
||||
Normals,
|
||||
Tangents,
|
||||
Colors, // TODO: Figure out best way to store color data
|
||||
Joints, // TODO: Animation
|
||||
TexCoords,
|
||||
Weights, // TODO: Animation
|
||||
MorphTargets, // TODO: Animation
|
||||
/// Used during loading of the Mesh to process the materials taht it
|
||||
MaterialRef,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, edict::Component)]
|
||||
pub struct Mesh {
|
||||
pub uuid: uuid::Uuid,
|
||||
pub attributes: HashMap<MeshVertexAttribute, VertexAttributeData>,
|
||||
pub indices: Option<MeshIndices>,
|
||||
material: Option<Material>,
|
||||
}
|
||||
|
||||
impl Default for Mesh {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uuid: uuid::Uuid::new_v4(),
|
||||
attributes: Default::default(),
|
||||
indices: Default::default(),
|
||||
material: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn add_attribute(&mut self, attribute: MeshVertexAttribute, data: VertexAttributeData) {
|
||||
self.attributes.insert(attribute, data);
|
||||
}
|
||||
|
||||
/// Try to get the position attribute of the Mesh
|
||||
pub fn position(&self) -> Option<&Vec<glam::Vec3>> {
|
||||
self.attributes.get(&MeshVertexAttribute::Position)
|
||||
.map(|p| p.as_vec3())
|
||||
}
|
||||
|
||||
pub fn add_position(&mut self, pos: Vec<glam::Vec3>) {
|
||||
self.attributes.insert(MeshVertexAttribute::Position, VertexAttributeData::Vec3(pos));
|
||||
}
|
||||
|
||||
/// Try to get the normals attribute of the Mesh
|
||||
pub fn normals(&self) -> Option<&Vec<glam::Vec3>> {
|
||||
self.attributes.get(&MeshVertexAttribute::Normals)
|
||||
.map(|p| p.as_vec3())
|
||||
}
|
||||
|
||||
/// Try to get the texture coordinates attribute of the Mesh
|
||||
pub fn tex_coords(&self) -> Option<&Vec<glam::Vec2>> {
|
||||
self.attributes.get(&MeshVertexAttribute::TexCoords)
|
||||
.map(|p| p.as_vec2())
|
||||
}
|
||||
|
||||
pub fn material(&self) -> Material {
|
||||
self.material.clone().expect("This mesh is missing a material!")
|
||||
}
|
||||
|
||||
pub fn set_material(&mut self, val: Material) {
|
||||
self.material = Some(val);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Model {
|
||||
pub meshes: Vec<Mesh>,
|
||||
//pub material
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn new(meshes: Vec<Mesh>) -> Self {
|
||||
Self {
|
||||
meshes,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,9 @@ pub struct Resource<T: Send + Sync + 'static> {
|
|||
pub state: ResourceState,
|
||||
}
|
||||
|
||||
/// A helper type to make it easier to use resources
|
||||
pub type ResHandle<T> = Arc<Resource<T>>;
|
||||
|
||||
impl<T: Send + Sync + 'static> Resource<T> {
|
||||
/// Create the resource with data, its assumed the state is `Ready`
|
||||
pub fn with_data(path: &str, data: T) -> Self {
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{sync::Arc, collections::{HashMap, hash_map::DefaultHasher}, hash::{Has
|
|||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{resource::Resource, loader::{ResourceLoader, LoaderError, texture::TextureLoader}};
|
||||
use crate::{resource::Resource, loader::{ResourceLoader, LoaderError, image::ImageLoader, model::ModelLoader}};
|
||||
|
||||
pub trait ResourceStorage: Send + Sync + Any + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
@ -31,6 +31,10 @@ pub enum RequestError {
|
|||
Loader(LoaderError),
|
||||
#[error("The file extension is unsupported: '{0}'")]
|
||||
UnsupportedFileExtension(String),
|
||||
#[error("The mimetype is unsupported: '{0}'")]
|
||||
UnsupportedMime(String),
|
||||
#[error("The identifier is not found: '{0}'")]
|
||||
IdentNotFound(String),
|
||||
}
|
||||
|
||||
impl From<LoaderError> for RequestError {
|
||||
|
@ -39,16 +43,19 @@ impl From<LoaderError> for RequestError {
|
|||
}
|
||||
}
|
||||
|
||||
/// A struct that stores all Manager data. This is requried for sending
|
||||
//struct ManagerStorage
|
||||
|
||||
pub struct ResourceManager {
|
||||
resources: HashMap<String, Arc<dyn ResourceStorage>>,
|
||||
loaders: Vec<Box<dyn ResourceLoader>>,
|
||||
loaders: Vec<Arc<dyn ResourceLoader>>,
|
||||
}
|
||||
|
||||
impl ResourceManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
resources: HashMap::new(),
|
||||
loaders: vec![ Box::new(TextureLoader::default()) ],
|
||||
loaders: vec![ Arc::new(ImageLoader::default()), Arc::new(ModelLoader::default()) ],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,12 +72,14 @@ impl ResourceManager {
|
|||
.find(|l| l.does_support_file(path)) {
|
||||
|
||||
// Load the resource and store it
|
||||
let res = loader.load(path)?;
|
||||
let loader = Arc::clone(&loader); // stop borrowing from self
|
||||
let res = loader.load(self, path)?;
|
||||
self.resources.insert(path.to_string(), res.clone());
|
||||
|
||||
// convert Arc<dyn ResourceStorage> to Arc<Resource<T>
|
||||
// cast Arc<dyn ResourceStorage> to Arc<Resource<T>
|
||||
let res = res.as_arc_any();
|
||||
let res = res.downcast::<Resource<T>>().expect("Failure to downcast resource");
|
||||
let res = res.downcast::<Resource<T>>()
|
||||
.expect("Failure to downcast resource! Does the loader return an `Arc<Resource<T>>`?");
|
||||
|
||||
Ok(res)
|
||||
} else {
|
||||
|
@ -79,6 +88,49 @@ impl ResourceManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Store bytes in the manager. If there is already an entry with the same identifier it will be updated.
|
||||
///
|
||||
/// Panics: If there is already an entry with the same `ident`, and the entry is not bytes, this function will panic.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * `ident` - The identifier to store along with these bytes. Make sure its unique to avoid overriding something.
|
||||
/// * `bytes` - The bytes to store.
|
||||
///
|
||||
/// Returns: The `Arc` to the now stored resource
|
||||
pub fn load_bytes<T: Send + Sync + Any + 'static>(&mut self, ident: &str, mime_type: &str, bytes: Vec<u8>, offset: usize, length: usize) -> Result<Arc<Resource<T>>, RequestError> {
|
||||
if let Some(loader) = self.loaders.iter()
|
||||
.find(|l| l.does_support_mime(mime_type)) {
|
||||
let loader = loader.clone();
|
||||
let res = loader.load_bytes(self, bytes, offset, length)?;
|
||||
self.resources.insert(ident.to_string(), res.clone());
|
||||
// code here...
|
||||
|
||||
// cast Arc<dyn ResourceStorage> to Arc<Resource<T>
|
||||
let res = res.as_arc_any();
|
||||
let res = res.downcast::<Resource<T>>()
|
||||
.expect("Failure to downcast resource! Does the loader return an `Arc<Resource<T>>`?");
|
||||
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(RequestError::UnsupportedMime(mime_type.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Requests bytes from the manager.
|
||||
pub fn request_loaded_bytes<T: Send + Sync + Any + 'static>(&mut self, ident: &str) -> Result<Arc<Resource<T>>, RequestError> {
|
||||
match self.resources.get(&ident.to_string()) {
|
||||
Some(res) => {
|
||||
let res = res.clone().as_arc_any();
|
||||
let res = res.downcast::<Resource<T>>().expect("Failure to downcast resource");
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
None => {
|
||||
Err(RequestError::IdentNotFound(ident.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -125,7 +177,7 @@ mod tests {
|
|||
assert!(
|
||||
match err {
|
||||
// make sure the error is NotFound
|
||||
RequestError::Loader(LoaderError::IOError(e)) if e.kind() == io::ErrorKind::NotFound => true,
|
||||
RequestError::Loader(LoaderError::IoError(e)) if e.kind() == io::ErrorKind::NotFound => true,
|
||||
_ => false
|
||||
}
|
||||
);
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
use base64::Engine;
|
||||
use thiserror::Error;
|
||||
use std::io;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum UriReadError {
|
||||
#[error("IOError: '{0}'")]
|
||||
IoError(io::Error),
|
||||
|
||||
// From is implemented for this field in each loader module
|
||||
#[error("Base64 decoding error: '{0}'")]
|
||||
Base64Decode(base64::DecodeError),
|
||||
|
||||
#[error("Some data was missing from the uri")]
|
||||
None
|
||||
}
|
||||
|
||||
/// Read a buffer's uri string into a byte buffer.
|
||||
///
|
||||
/// * `containing_path`: The path of the containing folder of the buffers "parent",
|
||||
/// the parent being where this buffer is defined in,
|
||||
/// i.e. parent="resources/models/player.gltf", containing="resource/models"
|
||||
pub(crate) fn gltf_read_buffer_uri(containing_path: &str, uri: &str) -> Result<Vec<u8>, UriReadError> {
|
||||
if let Some((mime, data)) = uri.strip_prefix("data")
|
||||
.and_then(|uri| uri.split_once(",")) {
|
||||
let (_mime, is_base64) = match mime.strip_suffix(";base64") {
|
||||
Some(mime) => (mime, true),
|
||||
None => (mime, false),
|
||||
};
|
||||
|
||||
if is_base64 {
|
||||
base64::engine::general_purpose::STANDARD.decode(data)
|
||||
.map_err(|e| UriReadError::Base64Decode(e))
|
||||
} else {
|
||||
Ok(data.as_bytes().to_vec())
|
||||
}
|
||||
} else {
|
||||
let full_path = format!("{containing_path}/{uri}");
|
||||
std::fs::read(&full_path).map_err(|e| UriReadError::IoError(e))
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,121 @@
|
|||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v3.6.5",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Cube"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorFactor":[
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1
|
||||
],
|
||||
"metallicFactor":0,
|
||||
"roughnessFactor":0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Cube",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"TEXCOORD_0":1,
|
||||
"NORMAL":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"max":[
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min":[
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":36,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":192,
|
||||
"byteOffset":288,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":480,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":72,
|
||||
"byteOffset":768,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":840,
|
||||
"uri":"data:application/octet-stream;base64,AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAQAOABQAAQAUAAcACgAGABIACgASABYAFwATAAwAFwAMABAADwADAAkADwAJABUABQACAAgABQAIAAsAEQANAAAAEQAAAAQA"
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,121 @@
|
|||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v3.6.5",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Cube"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorFactor":[
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1
|
||||
],
|
||||
"metallicFactor":0,
|
||||
"roughnessFactor":0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Cube",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"TEXCOORD_0":1,
|
||||
"NORMAL":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"max":[
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min":[
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":36,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":192,
|
||||
"byteOffset":288,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":480,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":72,
|
||||
"byteOffset":768,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":840,
|
||||
"uri":"test-sep.bin"
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -0,0 +1,137 @@
|
|||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v3.6.6",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Cube"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorTexture":{
|
||||
"index":0
|
||||
},
|
||||
"metallicFactor":0,
|
||||
"roughnessFactor":0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Cube",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"TEXCOORD_0":1,
|
||||
"NORMAL":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures":[
|
||||
{
|
||||
"sampler":0,
|
||||
"source":0
|
||||
}
|
||||
],
|
||||
"images":[
|
||||
{
|
||||
"mimeType":"image/png",
|
||||
"name":"uvgrid",
|
||||
"uri":"uvgrid.png"
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"max":[
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min":[
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":36,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":192,
|
||||
"byteOffset":288,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":480,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":72,
|
||||
"byteOffset":768,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"samplers":[
|
||||
{
|
||||
"magFilter":9729,
|
||||
"minFilter":9987
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":840,
|
||||
"uri":"texture-sep.bin"
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
|
@ -0,0 +1 @@
|
|||
nightly
|
|
@ -1,18 +1,16 @@
|
|||
use edict::Component;
|
||||
|
||||
use crate::render::{vertex::Vertex, mesh::Mesh, material::Material};
|
||||
use lyra_resource::Mesh;
|
||||
|
||||
#[derive(Clone, Component)]
|
||||
pub struct MeshComponent {
|
||||
pub mesh: Mesh,
|
||||
pub material: Material,
|
||||
}
|
||||
|
||||
impl MeshComponent {
|
||||
pub fn new(mesh: Mesh, material: Material) -> Self {
|
||||
pub fn new(mesh: Mesh) -> Self {
|
||||
Self {
|
||||
mesh,
|
||||
material
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod mesh;
|
||||
pub mod model;
|
||||
pub mod transform;
|
||||
pub mod camera;
|
|
@ -0,0 +1,39 @@
|
|||
use lyra_resource::ResHandle;
|
||||
|
||||
use crate::assets::Model;
|
||||
|
||||
#[derive(Clone, edict::Component)]
|
||||
pub struct ModelComponent(pub ResHandle<Model>);
|
||||
|
||||
impl From<ResHandle<Model>> for ModelComponent {
|
||||
fn from(value: ResHandle<Model>) -> Self {
|
||||
ModelComponent(value)
|
||||
}
|
||||
}
|
||||
|
||||
/* impl From<ResHandle<Model> for ModelComponent {
|
||||
|
||||
} */
|
||||
|
||||
impl std::ops::Deref for ModelComponent {
|
||||
type Target = ResHandle<Model>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for ModelComponent {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
/* impl ModelComponent {
|
||||
pub fn new(model, material: Material) -> Self {
|
||||
Self {
|
||||
mesh,
|
||||
material
|
||||
}
|
||||
}
|
||||
} */
|
|
@ -297,7 +297,7 @@ impl Game {
|
|||
.with(fmt::layer().with_writer(stdout_layer))
|
||||
.with(filter::Targets::new()
|
||||
.with_target("lyra_engine", Level::TRACE)
|
||||
.with_target("wgpu_core", Level::INFO)
|
||||
.with_target("wgpu_core", Level::WARN)
|
||||
.with_default(Level::DEBUG))
|
||||
.init();
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(hash_extract_if)]
|
||||
|
||||
pub mod game;
|
||||
pub mod render;
|
||||
pub mod input_event;
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 545 B |
|
@ -2,53 +2,23 @@ use edict::EntityId;
|
|||
|
||||
use crate::math::Transform;
|
||||
|
||||
use super::{mesh::Mesh, material::Material};
|
||||
|
||||
pub struct RenderJob {
|
||||
mesh: Mesh,
|
||||
material: Material,
|
||||
entity: EntityId,
|
||||
pub entity: EntityId,
|
||||
pub shader_id: u64,
|
||||
pub mesh_buffer_id: uuid::Uuid,
|
||||
|
||||
transform: Transform,
|
||||
last_transform: Option<Transform>, // TODO: render interpolation
|
||||
pub transform: Transform,
|
||||
pub last_transform: Option<Transform>, // TODO: render interpolation
|
||||
}
|
||||
|
||||
impl RenderJob {
|
||||
pub fn new(mesh: Mesh, material: Material, entity: EntityId, transform: Transform, last_transform: Option<Transform>) -> Self {
|
||||
pub fn new(entity: EntityId, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform, last_transform: Option<Transform>) -> Self {
|
||||
Self {
|
||||
mesh,
|
||||
material,
|
||||
entity,
|
||||
shader_id,
|
||||
mesh_buffer_id,
|
||||
transform,
|
||||
last_transform,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mesh(&self)-> &Mesh {
|
||||
&self.mesh
|
||||
}
|
||||
|
||||
pub fn material(&self)-> &Material {
|
||||
&self.material
|
||||
}
|
||||
|
||||
pub fn entity(&self)-> EntityId {
|
||||
self.entity
|
||||
}
|
||||
|
||||
pub fn transform(&self)-> &Transform {
|
||||
&self.transform
|
||||
}
|
||||
|
||||
pub fn set_transform(&mut self, transform: Transform){
|
||||
self.transform = transform;
|
||||
}
|
||||
|
||||
pub fn last_transform(&self)-> Option<&Transform> {
|
||||
self.last_transform.as_ref()
|
||||
}
|
||||
|
||||
pub fn set_last_transform(&mut self, last_transform: Transform){
|
||||
self.last_transform = Some(last_transform);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use std::{ops::Range, cell::Ref};
|
||||
use std::ops::Range;
|
||||
|
||||
use wgpu::{PipelineLayout, RenderPipeline, RenderPass, VertexBufferLayout, BindGroupLayout};
|
||||
|
||||
use super::{render_job::RenderJob, vertex::Vertex, desc_buf_lay::DescVertexBufferLayout, texture::RenderTexture};
|
||||
use super::{render_job::RenderJob, texture::RenderTexture};
|
||||
|
||||
pub struct FullRenderPipeline {
|
||||
layout: PipelineLayout,
|
||||
|
|
|
@ -5,30 +5,27 @@ use std::num::NonZeroU64;
|
|||
use std::sync::Arc;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use aligned_vec::AVec;
|
||||
use async_std::sync::Mutex;
|
||||
use async_trait::async_trait;
|
||||
|
||||
use atomicell::{AtomicCell, RefMut};
|
||||
use edict::query::EpochOf;
|
||||
use edict::{EntityId, Entities};
|
||||
use glam::Mat4;
|
||||
use glam::Vec3;
|
||||
use tracing::{debug, warn};
|
||||
use wgpu::{BindGroup, BindGroupLayout, Limits, BufferBinding};
|
||||
use wgpu::{BindGroup, BindGroupLayout, Limits};
|
||||
use wgpu::util::DeviceExt;
|
||||
use winit::window::Window;
|
||||
|
||||
use crate::ecs::components::camera::CameraComponent;
|
||||
use crate::ecs::components::mesh::MeshComponent;
|
||||
use crate::ecs::components::model::ModelComponent;
|
||||
use crate::ecs::components::transform::TransformComponent;
|
||||
use crate::math::{Transform, Angle};
|
||||
use crate::resources;
|
||||
use crate::math::Transform;
|
||||
|
||||
use super::camera::RenderCamera;
|
||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||
use super::texture::RenderTexture;
|
||||
use super::vertex::Vertex;
|
||||
use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh};
|
||||
use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, render_job::RenderJob};
|
||||
|
||||
use lyra_resource::Mesh;
|
||||
|
||||
pub trait Renderer {
|
||||
fn prepare(&mut self, main_world: &mut edict::World);
|
||||
|
@ -36,16 +33,15 @@ pub trait Renderer {
|
|||
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
|
||||
|
||||
fn surface_size(&self) -> winit::dpi::PhysicalSize<u32>;
|
||||
fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc<FullRenderPipeline>);
|
||||
fn add_render_pipeline(&mut self, shader_id: u64, pipeline: Arc<FullRenderPipeline>);
|
||||
}
|
||||
|
||||
struct RenderBufferStorage {
|
||||
buffer_vertex: BufferStorage,
|
||||
buffer_indices: Option<BufferStorage>,
|
||||
buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>,
|
||||
|
||||
render_texture: Option<RenderTexture>,
|
||||
texture_bindgroup: Option<BindGroup>,
|
||||
texture_layout: Option<BindGroupLayout>,
|
||||
|
||||
/// The index of the transform for this entity.
|
||||
/// The tuple is structured like this: (transform index, index of transform inside the buffer)
|
||||
|
@ -73,13 +69,15 @@ struct TransformBuffers {
|
|||
|
||||
impl TransformBuffers {
|
||||
/// Update an entity's buffer with the new transform. Will panic if the entity isn't stored
|
||||
fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) {
|
||||
fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) -> TransformBufferIndices {
|
||||
let indices = self.not_updated.remove(&entity)
|
||||
.or_else(|| self.just_updated.remove(&entity))
|
||||
.expect("Use 'insert_entity' for new entities");
|
||||
self.just_updated.insert(entity, indices);
|
||||
|
||||
let (_, buffer, _) = self.buffer_bindgroups.get(indices.buffer_index).unwrap();
|
||||
queue.write_buffer(buffer, indices.transform_index as u64 * limits.min_uniform_buffer_offset_alignment as u64, bytemuck::bytes_of(&transform));
|
||||
indices
|
||||
}
|
||||
|
||||
/// Insert a new entity into the buffer, returns where it was stored.
|
||||
|
@ -109,6 +107,22 @@ impl TransformBuffers {
|
|||
indices
|
||||
}
|
||||
|
||||
/// Update or insert an entities transform
|
||||
fn update_or_insert<TFn>(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform_fn: TFn) -> TransformBufferIndices
|
||||
where TFn: Fn() -> glam::Mat4
|
||||
{
|
||||
if self.contains(entity) {
|
||||
self.update_entity(queue, limits, entity, transform_fn())
|
||||
} else {
|
||||
self.insert_entity(queue, limits, entity, transform_fn())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the entity's transform is stored (does not mean its up-to-date).
|
||||
fn contains(&self, entity: EntityId) -> bool {
|
||||
self.not_updated.contains_key(&entity) || self.just_updated.contains_key(&entity)
|
||||
}
|
||||
|
||||
/// Collect the dead entities, mark entities and not updated for next updates.
|
||||
fn tick(&mut self) {
|
||||
// take the dead entities, these were ones that were not updated this tick
|
||||
|
@ -116,7 +130,6 @@ impl TransformBuffers {
|
|||
.map(|t| t.clone()).collect();
|
||||
self.dead_indices = dead;
|
||||
|
||||
// swap just_updated into not_updated
|
||||
self.not_updated = self.just_updated.clone();
|
||||
self.just_updated.clear();
|
||||
}
|
||||
|
@ -145,10 +158,11 @@ pub struct BasicRenderer {
|
|||
|
||||
pub clear_color: wgpu::Color,
|
||||
|
||||
pub render_pipelines: HashMap<u32, Arc<FullRenderPipeline>>,
|
||||
pub render_pipelines: HashMap<u64, Arc<FullRenderPipeline>>,
|
||||
pub render_jobs: VecDeque<RenderJob>,
|
||||
|
||||
buffer_storage: HashMap<EntityId, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
||||
mesh_buffers: HashMap<uuid::Uuid, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
||||
entity_meshes: HashMap<EntityId, uuid::Uuid>,
|
||||
|
||||
transform_buffers: TransformBuffers,
|
||||
transform_bind_group_layout: BindGroupLayout,
|
||||
|
@ -160,6 +174,8 @@ pub struct BasicRenderer {
|
|||
camera_buffer: wgpu::Buffer,
|
||||
camera_bind_group: wgpu::BindGroup,
|
||||
|
||||
texture_bind_group_layout: BindGroupLayout,
|
||||
default_texture_bind_group: BindGroup,
|
||||
depth_buffer_texture: RenderTexture,
|
||||
}
|
||||
|
||||
|
@ -208,7 +224,7 @@ impl BasicRenderer {
|
|||
false => surface_caps.present_modes[0]
|
||||
}; */
|
||||
|
||||
println!("present mode: {:?}", present_mode);
|
||||
debug!("present mode: {:?}", present_mode);
|
||||
|
||||
let surface_format = surface_caps.formats.iter()
|
||||
.copied()
|
||||
|
@ -348,12 +364,27 @@ impl BasicRenderer {
|
|||
|
||||
let depth_texture = RenderTexture::create_depth_texture(&device, &config, "Depth Buffer");
|
||||
|
||||
let mut pipelines = HashMap::new();
|
||||
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&device, &config, &shader,
|
||||
vec![super::vertex::Vertex::desc(),],
|
||||
vec![&texture_bind_group_layout, &transform_bind_group_layout, &camera_bind_group_layout])));
|
||||
// load the default texture
|
||||
let bytes = include_bytes!("default_texture.png");
|
||||
let tex = RenderTexture::from_bytes(&device, &queue, bytes, "default_texture").unwrap();
|
||||
let default_tex_bindgroup = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout: &texture_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(tex.view()),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(tex.sampler()),
|
||||
}
|
||||
],
|
||||
label: Some("default_texture"),
|
||||
}
|
||||
);
|
||||
|
||||
Self {
|
||||
let mut s = Self {
|
||||
window,
|
||||
surface,
|
||||
device,
|
||||
|
@ -366,9 +397,10 @@ impl BasicRenderer {
|
|||
b: 0.3,
|
||||
a: 1.0,
|
||||
},
|
||||
render_pipelines: pipelines,
|
||||
render_pipelines: HashMap::new(),
|
||||
render_jobs: VecDeque::new(),
|
||||
buffer_storage: HashMap::new(),
|
||||
mesh_buffers: HashMap::new(),
|
||||
entity_meshes: HashMap::new(),
|
||||
|
||||
render_limits,
|
||||
transform_buffers,
|
||||
|
@ -378,29 +410,31 @@ impl BasicRenderer {
|
|||
camera_buffer,
|
||||
camera_bind_group,
|
||||
|
||||
texture_bind_group_layout,
|
||||
default_texture_bind_group: default_tex_bindgroup,
|
||||
depth_buffer_texture: depth_texture,
|
||||
}
|
||||
};
|
||||
|
||||
// create the default pipelines
|
||||
let mut pipelines = HashMap::new();
|
||||
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
||||
vec![super::vertex::Vertex::desc(),],
|
||||
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout])));
|
||||
s.render_pipelines = pipelines;
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn find_next_multiple(n: u32, mul: u32) -> u32 {
|
||||
if n % mul == 0 {
|
||||
n
|
||||
} else {
|
||||
n + (mul - n % mul)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: minimize how often model buffers are updated by checking if they changed
|
||||
fn update_model_buffers(&mut self, entity: EntityId, model: &MeshComponent) {
|
||||
if let Some(buffers) = self.buffer_storage.get_mut(&entity) {
|
||||
fn update_mesh_buffers(&mut self, _entity: EntityId, mesh: &Mesh) {
|
||||
if let Some(buffers) = self.mesh_buffers.get_mut(&mesh.uuid) {
|
||||
// check if the buffer sizes dont match. If they dont, completely remake the buffers
|
||||
let vertices = &model.mesh.vertices;
|
||||
let vertices = mesh.position().unwrap();
|
||||
if buffers.buffer_vertex.count() != vertices.len() {
|
||||
drop(buffers);
|
||||
let (vert, idx) = self.create_vertex_index_buffers(&model.mesh);
|
||||
debug!("Recreating buffers for mesh {}", mesh.uuid.to_string());
|
||||
let (vert, idx) = self.create_vertex_index_buffers(mesh);
|
||||
|
||||
// have to re-get buffers because of borrow checker
|
||||
let buffers = self.buffer_storage.get_mut(&entity).unwrap();
|
||||
let buffers = self.mesh_buffers.get_mut(&mesh.uuid).unwrap();
|
||||
buffers.buffer_indices = idx;
|
||||
buffers.buffer_vertex = vert;
|
||||
|
||||
|
@ -411,89 +445,80 @@ impl BasicRenderer {
|
|||
let vertex_buffer = buffers.buffer_vertex.buffer();
|
||||
let vertices = vertices.as_slice();
|
||||
// align the vertices to 4 bytes (u32 is 4 bytes, which is wgpu::COPY_BUFFER_ALIGNMENT)
|
||||
let (_, vertices, _) = bytemuck::pod_align_to::<Vertex, u32>(vertices);
|
||||
let (_, vertices, _) = bytemuck::pod_align_to::<Vec3, u32>(vertices);
|
||||
self.queue.write_buffer(&vertex_buffer, 0, bytemuck::cast_slice(&vertices));
|
||||
|
||||
// update the indices if they're given
|
||||
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
|
||||
let index_buffer = index_buffer.buffer();
|
||||
let indices = model.mesh.indices.as_ref().unwrap().as_slice();
|
||||
let (_, indices, _) = bytemuck::pod_align_to::<u16, u32>(indices);
|
||||
let aligned_indices = match mesh.indices.as_ref().unwrap() {
|
||||
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
|
||||
lyra_resource::MeshIndices::U16(v) => bytemuck::pod_align_to::<u16, u32>(v).1,
|
||||
lyra_resource::MeshIndices::U32(v) => bytemuck::pod_align_to::<u32, u32>(v).1,
|
||||
};
|
||||
|
||||
self.queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(&indices));
|
||||
let index_buffer = index_buffer.1.buffer();
|
||||
self.queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(&aligned_indices));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_vertex_index_buffers(&mut self, mesh: &Mesh) -> (BufferStorage, Option<BufferStorage>) {
|
||||
fn create_vertex_index_buffers(&mut self, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) {
|
||||
let positions = mesh.position().unwrap();
|
||||
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
|
||||
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]);
|
||||
|
||||
assert!(positions.len() == tex_coords.len());
|
||||
|
||||
let vertex_inputs: Vec<Vertex> = std::iter::zip(positions, tex_coords.into_iter())
|
||||
.map(|(v, t)| Vertex::new(v.clone(), t))
|
||||
.collect();
|
||||
|
||||
let vertex_buffer = self.device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
contents: bytemuck::cast_slice(mesh.vertices.as_slice()),
|
||||
contents: bytemuck::cast_slice(vertex_inputs.as_slice()),//vertex_combined.as_slice(),
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages:: COPY_DST,
|
||||
}
|
||||
);
|
||||
let vertex_buffer = BufferStorage::new(vertex_buffer, 0, mesh.vertices.len());
|
||||
let vertex_buffer = BufferStorage::new(vertex_buffer, 0, vertex_inputs.len());
|
||||
|
||||
let buffer_indices = match mesh.indices.as_ref() {
|
||||
let indices = match mesh.indices.as_ref() {
|
||||
Some(indices) => {
|
||||
let (idx_type, len, contents) = match indices {
|
||||
lyra_resource::MeshIndices::U16(v) => (wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(&v)),
|
||||
lyra_resource::MeshIndices::U32(v) => (wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(&v)),
|
||||
};
|
||||
|
||||
let index_buffer = self.device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Index Buffer"),
|
||||
contents: bytemuck::cast_slice(&indices),
|
||||
contents,
|
||||
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages:: COPY_DST,
|
||||
}
|
||||
);
|
||||
|
||||
let buffer_indices = BufferStorage::new(index_buffer, 0, indices.len());
|
||||
let buffer_indices = BufferStorage::new(index_buffer, 0, len);
|
||||
|
||||
Some(buffer_indices)
|
||||
Some((idx_type, buffer_indices))
|
||||
},
|
||||
None => {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
( vertex_buffer, buffer_indices )
|
||||
( vertex_buffer, indices )
|
||||
}
|
||||
|
||||
fn create_model_buffers(&mut self, model: &MeshComponent, transform_indices: TransformBufferIndices) -> RenderBufferStorage {
|
||||
let mesh = &model.mesh;
|
||||
|
||||
fn create_mesh_buffers(&mut self, mesh: &Mesh, transform_indices: TransformBufferIndices) -> RenderBufferStorage {
|
||||
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh);
|
||||
|
||||
let model_texture = &model.material.texture;
|
||||
let diffuse_bindgroup = if let Some(model_texture) = &mesh.material().base_color_texture {
|
||||
let image = &model_texture.data.as_ref().unwrap().image;
|
||||
let diffuse_texture = RenderTexture::from_image(&self.device, &self.queue, image, None).unwrap();
|
||||
|
||||
let texture_bind_group_layout =
|
||||
self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
// This should match the filterable field of the
|
||||
// corresponding Texture entry above.
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: Some("texture_bind_group_layout"),
|
||||
});
|
||||
|
||||
let diffuse_bind_group = self.device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout: &texture_bind_group_layout,
|
||||
layout: &self.texture_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
|
@ -508,12 +533,16 @@ impl BasicRenderer {
|
|||
}
|
||||
);
|
||||
|
||||
Some(diffuse_bind_group)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
RenderBufferStorage {
|
||||
buffer_vertex: vertex_buffer,
|
||||
buffer_indices,
|
||||
render_texture: None,
|
||||
texture_layout: None,
|
||||
texture_bindgroup: Some(diffuse_bind_group),
|
||||
texture_bindgroup: diffuse_bindgroup,
|
||||
transform_index: transform_indices
|
||||
}
|
||||
}
|
||||
|
@ -553,6 +582,28 @@ impl BasicRenderer {
|
|||
buffers.next_indices = indices;
|
||||
indices
|
||||
}
|
||||
|
||||
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
||||
fn process_mesh(&mut self, entity: EntityId, transform: Transform, mesh: &Mesh) -> bool {
|
||||
let indices = self.transform_buffers.update_or_insert(&self.queue, &self.render_limits,
|
||||
entity, || transform.calculate_mat4());
|
||||
|
||||
if self.mesh_buffers.contains_key(&mesh.uuid) {
|
||||
false
|
||||
} else {
|
||||
// check if the transform buffers need to be expanded
|
||||
if self.transform_buffers.should_expand() {
|
||||
self.expand_transform_buffers();
|
||||
}
|
||||
|
||||
// create the mesh's buffers
|
||||
let buffers = self.create_mesh_buffers(mesh, indices);
|
||||
self.mesh_buffers.insert(mesh.uuid, buffers);
|
||||
self.entity_meshes.insert(entity, mesh.uuid);
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for BasicRenderer {
|
||||
|
@ -561,36 +612,26 @@ impl Renderer for BasicRenderer {
|
|||
|
||||
let mut alive_entities = HashSet::new();
|
||||
|
||||
for (entity, model, model_epoch, transform) in main_world.query::<(Entities, &MeshComponent, EpochOf<MeshComponent>, &TransformComponent)>().iter() {
|
||||
// Create the render job and push it to the queue
|
||||
let job = RenderJob::new(model.mesh.clone(), model.material.clone(), entity, transform.transform, None);
|
||||
self.render_jobs.push_back(job);
|
||||
|
||||
for (entity, model, model_epoch, transform) in main_world.query::<(Entities, &ModelComponent, EpochOf<ModelComponent>, &TransformComponent)>().iter() {
|
||||
alive_entities.insert(entity);
|
||||
|
||||
if self.buffer_storage.get(&entity).is_none() {
|
||||
// check if the transform buffers need to be expanded
|
||||
if self.transform_buffers.should_expand() {
|
||||
self.expand_transform_buffers();
|
||||
}
|
||||
let model = model.data.as_ref().unwrap().as_ref();
|
||||
|
||||
// insert transform into buffers
|
||||
let indices = self.transform_buffers.insert_entity(&self.queue, &self.render_limits,
|
||||
entity, transform.transform.calculate_mat4());
|
||||
|
||||
// create the mesh's buffers
|
||||
let buffers = self.create_model_buffers(model, indices);
|
||||
self.buffer_storage.insert(entity, buffers);
|
||||
} else {
|
||||
// update entity transforms
|
||||
self.transform_buffers.update_entity(&self.queue, &self.render_limits,
|
||||
entity, transform.transform.calculate_mat4());
|
||||
|
||||
// if the model was updated, update its buffers
|
||||
for mesh in model.meshes.iter() {
|
||||
if !self.process_mesh(entity, transform.transform, mesh) {
|
||||
if model_epoch == last_epoch {
|
||||
self.update_model_buffers(entity, model);
|
||||
self.update_mesh_buffers(entity, mesh);
|
||||
}
|
||||
}
|
||||
|
||||
let shader = mesh.material().shader_uuid.unwrap_or(0);
|
||||
let job = RenderJob::new(entity, shader, mesh.uuid, transform.transform, None);
|
||||
self.render_jobs.push_back(job);
|
||||
}
|
||||
}
|
||||
|
||||
for (entity, mesh, mesh_epoch, transform) in main_world.query::<(Entities, &MeshComponent, EpochOf<MeshComponent>, &TransformComponent)>().iter() {
|
||||
debug!("TODO: Process MeshComponents"); // TODO: Process MeshComponents
|
||||
}
|
||||
|
||||
// collect dead entities
|
||||
|
@ -598,8 +639,12 @@ impl Renderer for BasicRenderer {
|
|||
|
||||
// when buffer storage length does not match the amount of iterated entities,
|
||||
// remove all dead entities, and their buffers, if they weren't iterated over
|
||||
if self.buffer_storage.len() != alive_entities.len() {
|
||||
self.buffer_storage.retain(|e, _| alive_entities.contains(e));
|
||||
if self.mesh_buffers.len() != alive_entities.len() {
|
||||
let removed_entities: Vec<uuid::Uuid> = self.entity_meshes
|
||||
.extract_if(|e, _| !alive_entities.contains(e))
|
||||
.map(|(_, v)| v)
|
||||
.collect();
|
||||
self.mesh_buffers.retain(|u, _| !removed_entities.contains(u));
|
||||
}
|
||||
|
||||
if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
|
||||
|
@ -644,17 +689,18 @@ impl Renderer for BasicRenderer {
|
|||
|
||||
// Pop off jobs from the queue as they're being processed
|
||||
while let Some(job) = self.render_jobs.pop_front() {
|
||||
if let Some(pipeline) = self.render_pipelines.get(&job.material().shader_id) {
|
||||
if let Some(pipeline) = self.render_pipelines.get(&job.shader_id) {
|
||||
// specify to use this pipeline
|
||||
render_pass.set_pipeline(pipeline.get_wgpu_pipeline());
|
||||
|
||||
// get the mesh (containing vertices) and the buffers from storage
|
||||
let mesh = job.mesh();
|
||||
let buffers = self.buffer_storage.get(&job.entity()).unwrap();
|
||||
let buffers = self.mesh_buffers.get(&job.mesh_buffer_id).unwrap();
|
||||
|
||||
// Bind the optional texture
|
||||
if let Some(tex) = buffers.texture_bindgroup.as_ref() {
|
||||
render_pass.set_bind_group(0, &tex, &[]);
|
||||
} else {
|
||||
render_pass.set_bind_group(0, &self.default_texture_bind_group, &[]);
|
||||
}
|
||||
|
||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||
|
@ -667,18 +713,18 @@ impl Renderer for BasicRenderer {
|
|||
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
|
||||
|
||||
// if this mesh uses indices, use them to draw the mesh
|
||||
if let Some(indices) = buffers.buffer_indices.as_ref() {
|
||||
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
||||
let indices_len = indices.count() as u32;
|
||||
|
||||
render_pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
|
||||
render_pass.set_index_buffer(indices.buffer().slice(..), wgpu::IndexFormat::Uint16);
|
||||
render_pass.set_index_buffer(indices.buffer().slice(..), idx_type.clone());
|
||||
render_pass.draw_indexed(0..indices_len, 0, 0..1);
|
||||
} else {
|
||||
let vertex_count = buffers.buffer_vertex.count();
|
||||
|
||||
render_pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
|
||||
render_pass.draw(0..mesh.vertices.len() as u32, 0..1);
|
||||
render_pass.draw(0..vertex_count as u32, 0..1);
|
||||
}
|
||||
} else {
|
||||
warn!("Failure to find RenderPipeline with shader id of '{}'!", job.material().shader_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -706,7 +752,7 @@ impl Renderer for BasicRenderer {
|
|||
self.size
|
||||
}
|
||||
|
||||
fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc<FullRenderPipeline>) {
|
||||
fn add_render_pipeline(&mut self, shader_id: u64, pipeline: Arc<FullRenderPipeline>) {
|
||||
self.render_pipelines.insert(shader_id, pipeline);
|
||||
}
|
||||
}
|
|
@ -3,9 +3,17 @@ use super::desc_buf_lay::DescVertexBufferLayout;
|
|||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Vertex {
|
||||
pub position: [f32; 3],
|
||||
pub position: glam::Vec3,
|
||||
pub tex_coords: glam::Vec2
|
||||
//pub color: [f32; 3], // TODO: add color again
|
||||
pub tex_coords: [f32; 2]
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
pub fn new(position: glam::Vec3, tex_coords: glam::Vec2) -> Self {
|
||||
Self {
|
||||
position, tex_coords
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DescVertexBufferLayout for Vertex {
|
||||
|
@ -17,12 +25,12 @@ impl DescVertexBufferLayout for Vertex {
|
|||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
format: wgpu::VertexFormat::Float32x3, // Vec3
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
format: wgpu::VertexFormat::Float32x2, // Vec2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue