Implement a basic engine with separate modules
This commit is contained in:
parent
0135974d08
commit
95070f854c
20 changed files with 1253 additions and 117 deletions
130
Cargo.lock
generated
130
Cargo.lock
generated
|
|
@ -173,6 +173,12 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit_field"
|
name = "bit_field"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
|
@ -224,6 +230,12 @@ version = "1.23.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder-lite"
|
name = "byteorder-lite"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -571,8 +583,12 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||||
name = "fps"
|
name = "fps"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"glam",
|
||||||
"glium",
|
"glium",
|
||||||
|
"gltf",
|
||||||
"glutin",
|
"glutin",
|
||||||
|
"hecs",
|
||||||
"image",
|
"image",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -636,6 +652,12 @@ dependencies = [
|
||||||
"xml-rs",
|
"xml-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glam"
|
||||||
|
version = "0.30.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50a99dbe56b72736564cfa4b85bf9a33079f16ae8b74983ab06af3b1a3696b11"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glium"
|
name = "glium"
|
||||||
version = "0.36.0"
|
version = "0.36.0"
|
||||||
|
|
@ -653,6 +675,45 @@ dependencies = [
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gltf"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3ce1918195723ce6ac74e80542c5a96a40c2b26162c1957a5cd70799b8cacf7"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"byteorder",
|
||||||
|
"gltf-json",
|
||||||
|
"image",
|
||||||
|
"lazy_static",
|
||||||
|
"serde_json",
|
||||||
|
"urlencoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gltf-derive"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14070e711538afba5d6c807edb74bcb84e5dbb9211a3bf5dea0dfab5b24f4c51"
|
||||||
|
dependencies = [
|
||||||
|
"inflections",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gltf-json"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6176f9d60a7eab0a877e8e96548605dedbde9190a7ae1e80bbcc1c9af03ab14"
|
||||||
|
dependencies = [
|
||||||
|
"gltf-derive",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glutin"
|
name = "glutin"
|
||||||
version = "0.32.3"
|
version = "0.32.3"
|
||||||
|
|
@ -729,6 +790,15 @@ dependencies = [
|
||||||
"crunchy",
|
"crunchy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.4"
|
version = "0.15.4"
|
||||||
|
|
@ -741,6 +811,16 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hecs"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1cbc675ee8d97b4d206a985137f8ad59666538f56f906474f554467a63c776d"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
|
@ -793,9 +873,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.15.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inflections"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "interpolate_name"
|
name = "interpolate_name"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
|
@ -816,6 +902,12 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
|
|
@ -870,6 +962,12 @@ version = "3.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lebe"
|
name = "lebe"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
|
@ -1718,6 +1816,12 @@ version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|
@ -1766,6 +1870,18 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.9"
|
version = "0.6.9"
|
||||||
|
|
@ -1842,6 +1958,12 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strict-num"
|
name = "strict-num"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|
@ -2002,6 +2124,12 @@ version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "v_frame"
|
name = "v_frame"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
||||||
15
Cargo.toml
15
Cargo.toml
|
|
@ -4,6 +4,15 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glium = "0.36.0"
|
anyhow = "1.0.98"
|
||||||
glutin = "0.32.3"
|
glam = "0.30.4"
|
||||||
image = "0.25.6"
|
|
||||||
|
# glium already re-exports glutin/winit, but we enable the helper features explicitly
|
||||||
|
glium = { version = "0.36.0", features = ["glutin_backend", "simple_window_builder"] }
|
||||||
|
gltf = { version = "1.4.1", features = ["import"] }
|
||||||
|
|
||||||
|
# explicit glutin for raw types (same semver as glium’s internal one)
|
||||||
|
glutin = { version = "0.32.3", default-features = false }
|
||||||
|
|
||||||
|
hecs = "0.10.5"
|
||||||
|
image = "0.25.6"
|
||||||
|
|
|
||||||
BIN
resources/cube.bin
Normal file
BIN
resources/cube.bin
Normal file
Binary file not shown.
121
resources/cube.gltf
Normal file
121
resources/cube.gltf
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.4.56",
|
||||||
|
"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,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":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":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":36,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":288,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":192,
|
||||||
|
"byteOffset":576,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":72,
|
||||||
|
"byteOffset":768,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":840,
|
||||||
|
"uri":"cube.bin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
33
resources/cube.obj
Normal file
33
resources/cube.obj
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# cube.obj
|
||||||
|
#
|
||||||
|
|
||||||
|
g cube
|
||||||
|
|
||||||
|
v 0.0 0.0 0.0
|
||||||
|
v 0.0 0.0 1.0
|
||||||
|
v 0.0 1.0 0.0
|
||||||
|
v 0.0 1.0 1.0
|
||||||
|
v 1.0 0.0 0.0
|
||||||
|
v 1.0 0.0 1.0
|
||||||
|
v 1.0 1.0 0.0
|
||||||
|
v 1.0 1.0 1.0
|
||||||
|
|
||||||
|
vn 0.0 0.0 1.0
|
||||||
|
vn 0.0 0.0 -1.0
|
||||||
|
vn 0.0 1.0 0.0
|
||||||
|
vn 0.0 -1.0 0.0
|
||||||
|
vn 1.0 0.0 0.0
|
||||||
|
vn -1.0 0.0 0.0
|
||||||
|
|
||||||
|
f 1//2 7//2 5//2
|
||||||
|
f 1//2 3//2 7//2
|
||||||
|
f 1//6 4//6 3//6
|
||||||
|
f 1//6 2//6 4//6
|
||||||
|
f 3//3 8//3 7//3
|
||||||
|
f 3//3 4//3 8//3
|
||||||
|
f 5//5 7//5 8//5
|
||||||
|
f 5//5 8//5 6//5
|
||||||
|
f 1//4 5//4 6//4
|
||||||
|
f 1//4 6//4 2//4
|
||||||
|
f 2//1 6//1 8//1
|
||||||
|
f 2//1 8//1 4//1
|
||||||
BIN
resources/monkey-smooth.bin
Normal file
BIN
resources/monkey-smooth.bin
Normal file
Binary file not shown.
104
resources/monkey-smooth.gltf
Normal file
104
resources/monkey-smooth.gltf
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.4.56",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"Suzanne"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Suzanne",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":555,
|
||||||
|
"max":[
|
||||||
|
1.3671875,
|
||||||
|
0.984375,
|
||||||
|
0.8515625
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-1.3671875,
|
||||||
|
-0.984375,
|
||||||
|
-0.8515625
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":1,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":555,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":555,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":2904,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":6660,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":6660,
|
||||||
|
"byteOffset":6660,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":4440,
|
||||||
|
"byteOffset":13320,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":5808,
|
||||||
|
"byteOffset":17760,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":23568,
|
||||||
|
"uri":"monkey-smooth.bin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
resources/monkey.bin
Normal file
BIN
resources/monkey.bin
Normal file
Binary file not shown.
104
resources/monkey.gltf
Normal file
104
resources/monkey.gltf
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.4.56",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"Suzanne"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Suzanne",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1966,
|
||||||
|
"max":[
|
||||||
|
1.3671875,
|
||||||
|
0.984375,
|
||||||
|
0.8515625
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-1.3671875,
|
||||||
|
-0.984375,
|
||||||
|
-0.8515625
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":1,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1966,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1966,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":2904,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":23592,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":23592,
|
||||||
|
"byteOffset":23592,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":15728,
|
||||||
|
"byteOffset":47184,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":5808,
|
||||||
|
"byteOffset":62912,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":68720,
|
||||||
|
"uri":"monkey.bin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
resources/uvsphere-smooth.bin
Normal file
BIN
resources/uvsphere-smooth.bin
Normal file
Binary file not shown.
109
resources/uvsphere-smooth.gltf
Normal file
109
resources/uvsphere-smooth.gltf
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.4.56",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"Sphere",
|
||||||
|
"translation":[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.003377079963684082
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Sphere",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":559,
|
||||||
|
"max":[
|
||||||
|
0.9999997019767761,
|
||||||
|
1,
|
||||||
|
0.9999993443489075
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-0.9999990463256836,
|
||||||
|
-1,
|
||||||
|
-1
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":1,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":559,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":559,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":2880,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":6708,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":6708,
|
||||||
|
"byteOffset":6708,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":4472,
|
||||||
|
"byteOffset":13416,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":5760,
|
||||||
|
"byteOffset":17888,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":23648,
|
||||||
|
"uri":"uvsphere-smooth.bin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
resources/uvsphere.bin
Normal file
BIN
resources/uvsphere.bin
Normal file
Binary file not shown.
104
resources/uvsphere.gltf
Normal file
104
resources/uvsphere.gltf
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.4.56",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"Sphere"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Sphere",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1984,
|
||||||
|
"max":[
|
||||||
|
0.9999997019767761,
|
||||||
|
1,
|
||||||
|
0.9999993443489075
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-0.9999990463256836,
|
||||||
|
-1,
|
||||||
|
-1
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":1,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1984,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1984,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":2880,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":23808,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":23808,
|
||||||
|
"byteOffset":23808,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":15872,
|
||||||
|
"byteOffset":47616,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":5760,
|
||||||
|
"byteOffset":63488,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":69248,
|
||||||
|
"uri":"uvsphere.bin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
resources/uvsphere2.bin
Normal file
BIN
resources/uvsphere2.bin
Normal file
Binary file not shown.
190
resources/uvsphere2.gltf
Normal file
190
resources/uvsphere2.gltf
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.4.56",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"Sphere",
|
||||||
|
"translation":[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
-1.0871706008911133
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mesh":1,
|
||||||
|
"name":"Cube",
|
||||||
|
"translation":[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1.0190757513046265
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Sphere",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Cube.001",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":4,
|
||||||
|
"NORMAL":5,
|
||||||
|
"TEXCOORD_0":6
|
||||||
|
},
|
||||||
|
"indices":7
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1984,
|
||||||
|
"max":[
|
||||||
|
0.9999997019767761,
|
||||||
|
1,
|
||||||
|
0.9999993443489075
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-0.9999990463256836,
|
||||||
|
-1,
|
||||||
|
-1
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":1,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1984,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":1984,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":2880,
|
||||||
|
"type":"SCALAR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":4,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"max":[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
-1
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":5,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":6,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":7,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":36,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":23808,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":23808,
|
||||||
|
"byteOffset":23808,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":15872,
|
||||||
|
"byteOffset":47616,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":5760,
|
||||||
|
"byteOffset":63488,
|
||||||
|
"target":34963
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":69248,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":69536,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":192,
|
||||||
|
"byteOffset":69824,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":72,
|
||||||
|
"byteOffset":70016,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":70088,
|
||||||
|
"uri":"uvsphere2.bin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
24
src/camera.rs
Normal file
24
src/camera.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
use glam::{Mat4, Vec3};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Camera {
|
||||||
|
pub eye: Vec3,
|
||||||
|
pub center: Vec3,
|
||||||
|
pub up: Vec3,
|
||||||
|
pub fovy: f32,
|
||||||
|
pub aspect: f32,
|
||||||
|
pub znear: f32,
|
||||||
|
pub zfar: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
pub fn view(&self) -> Mat4 {
|
||||||
|
Mat4::look_at_rh(self.eye, self.center, self.up)
|
||||||
|
}
|
||||||
|
pub fn projection(&self) -> Mat4 {
|
||||||
|
Mat4::perspective_rh(self.fovy, self.aspect, self.znear, self.zfar)
|
||||||
|
}
|
||||||
|
pub fn view_proj(&self) -> Mat4 {
|
||||||
|
self.projection() * self.view()
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/ecs.rs
Normal file
32
src/ecs.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use glam::{Mat4, Quat, Vec3};
|
||||||
|
use hecs::World;
|
||||||
|
|
||||||
|
/// ------------ components ------------
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Transform {
|
||||||
|
pub translation: Vec3,
|
||||||
|
pub rotation: Quat,
|
||||||
|
pub scale: Vec3,
|
||||||
|
}
|
||||||
|
impl Transform {
|
||||||
|
pub fn matrix(&self) -> Mat4 {
|
||||||
|
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MeshHandle(pub usize);
|
||||||
|
|
||||||
|
/// ------------ systems ------------
|
||||||
|
pub fn rotation_system(world: &mut World, dt: f32) {
|
||||||
|
for (_, transform) in world.query_mut::<&mut Transform>() {
|
||||||
|
transform.rotation *= Quat::from_rotation_y(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the aspect ratio for all camera components in the world.
|
||||||
|
pub fn set_camera_aspect(world: &mut World, aspect: f32) {
|
||||||
|
for (_, cam) in world.query_mut::<&mut crate::camera::Camera>() {
|
||||||
|
cam.aspect = aspect;
|
||||||
|
}
|
||||||
|
}
|
||||||
185
src/main.rs
185
src/main.rs
|
|
@ -1,127 +1,86 @@
|
||||||
#[macro_use]
|
mod camera;
|
||||||
extern crate glium;
|
mod ecs;
|
||||||
use glium::Surface;
|
mod model;
|
||||||
mod teapot;
|
mod render;
|
||||||
|
|
||||||
fn main() {
|
use anyhow::Result;
|
||||||
|
use camera::Camera;
|
||||||
|
use ecs::{rotation_system, MeshHandle, Transform};
|
||||||
|
use glam::{Quat, Vec3};
|
||||||
|
use glium::backend::glutin::SimpleWindowBuilder;
|
||||||
|
use render::{Renderer, GliumRenderer};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
let event_loop = glium::winit::event_loop::EventLoop::builder()
|
let event_loop = glium::winit::event_loop::EventLoop::builder()
|
||||||
.build()
|
.build()
|
||||||
.expect("event loop building");
|
.expect("create event-loop");
|
||||||
let (window, display) = glium::backend::glutin::SimpleWindowBuilder::new()
|
|
||||||
.with_title("Glium tutorial #3")
|
let (window, display) = SimpleWindowBuilder::new()
|
||||||
|
.with_title("fps")
|
||||||
|
.with_inner_size(1280, 720)
|
||||||
.build(&event_loop);
|
.build(&event_loop);
|
||||||
|
|
||||||
|
let mut world = hecs::World::new();
|
||||||
|
|
||||||
let positions = glium::VertexBuffer::new(&display, &teapot::VERTICES).unwrap();
|
let mesh = model::load_gltf("resources/monkey-smooth.gltf", &display)?;
|
||||||
let normals = glium::VertexBuffer::new(&display, &teapot::NORMALS).unwrap();
|
// let mesh = model::cube(&display)?;
|
||||||
let indices = glium::IndexBuffer::new(&display, glium::index::PrimitiveType::TrianglesList,
|
let mut renderer = GliumRenderer::new(display)?;
|
||||||
&teapot::INDICES).unwrap();
|
let mesh_id = renderer.meshes.len();
|
||||||
|
renderer.meshes.push(mesh);
|
||||||
|
|
||||||
let vertex_shader_src = r#"
|
world.spawn((
|
||||||
#version 140
|
Transform {
|
||||||
|
translation: Vec3::ZERO,
|
||||||
|
rotation: Quat::IDENTITY,
|
||||||
|
scale: Vec3::ONE,
|
||||||
|
},
|
||||||
|
MeshHandle(mesh_id),
|
||||||
|
));
|
||||||
|
|
||||||
in vec3 position;
|
{
|
||||||
in vec3 normal;
|
let (w, h): (u32, u32) = window.inner_size().into();
|
||||||
|
world.spawn((Camera {
|
||||||
|
eye: Vec3::new(3.0, 2.0, 3.0),
|
||||||
|
center: Vec3::ZERO,
|
||||||
|
up: Vec3::Y,
|
||||||
|
fovy: 45_f32.to_radians(),
|
||||||
|
aspect: w as f32 / h as f32,
|
||||||
|
znear: 0.1,
|
||||||
|
zfar: 100.0,
|
||||||
|
},));
|
||||||
|
}
|
||||||
|
|
||||||
out vec3 v_normal;
|
event_loop
|
||||||
|
.run(move |event, el| {
|
||||||
|
use glium::winit::event::{Event, WindowEvent};
|
||||||
|
|
||||||
uniform mat4 matrix;
|
match event {
|
||||||
uniform mat4 perspective;
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::CloseRequested => el.exit(),
|
||||||
void main() {
|
WindowEvent::Resized(sz) => {
|
||||||
v_normal = transpose(inverse(mat3(matrix))) * normal;
|
ecs::set_camera_aspect(&mut world, sz.width as f32 / sz.height as f32);
|
||||||
gl_Position = perspective * matrix * vec4(position, 1.0);
|
}
|
||||||
}
|
WindowEvent::RedrawRequested => {
|
||||||
"#;
|
renderer.render(&world);
|
||||||
let fragment_shader_src = r#"
|
}
|
||||||
#version 140
|
_ => {}
|
||||||
|
|
||||||
in vec3 v_normal;
|
|
||||||
out vec4 color;
|
|
||||||
uniform vec3 u_light;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
float brightness = dot(normalize(v_normal), normalize(u_light));
|
|
||||||
vec3 dark_color = vec3(0.6, 0.0, 0.0);
|
|
||||||
vec3 regular_color = vec3(1.0, 0.0, 0.0);
|
|
||||||
color = vec4(mix(dark_color, regular_color, brightness), 1.0);
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
let program = glium::Program::from_source(&display, vertex_shader_src, fragment_shader_src, None).unwrap();
|
|
||||||
|
|
||||||
let light = [-1.0, 0.4, 0.9f32];
|
|
||||||
let mut t: f32 = 0.0;
|
|
||||||
#[allow(deprecated)]
|
|
||||||
event_loop.run(move |ev, window_target| {
|
|
||||||
match ev {
|
|
||||||
glium::winit::event::Event::WindowEvent { event, .. } => match event {
|
|
||||||
glium::winit::event::WindowEvent::CloseRequested => {
|
|
||||||
window_target.exit();
|
|
||||||
},
|
},
|
||||||
// We now need to render everyting in response to a RedrawRequested event due to the animation
|
Event::AboutToWait => {
|
||||||
glium::winit::event::WindowEvent::RedrawRequested => {
|
// -- update logic --
|
||||||
let mut target = display.draw();
|
let now = Instant::now();
|
||||||
target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
|
static mut LAST: Option<Instant> = None;
|
||||||
t += 0.01;
|
let dt = unsafe { // FIXME
|
||||||
let x = t.sin() * 0.5;
|
let last = LAST.replace(now).unwrap_or(now);
|
||||||
let y = t.cos() * 0.5;
|
(now - last).as_secs_f32()
|
||||||
|
|
||||||
let perspective = {
|
|
||||||
let (width, height) = target.get_dimensions();
|
|
||||||
let aspect_ratio = height as f32 / width as f32;
|
|
||||||
|
|
||||||
let fov: f32 = 3.141592 / 3.0;
|
|
||||||
let zfar = 1024.0;
|
|
||||||
let znear = 0.1;
|
|
||||||
|
|
||||||
let f = 1.0 / (fov / 2.0).tan();
|
|
||||||
|
|
||||||
[
|
|
||||||
[f * aspect_ratio , 0.0, 0.0 , 0.0],
|
|
||||||
[ 0.0 , f , 0.0 , 0.0],
|
|
||||||
[ 0.0 , 0.0, (zfar+znear)/(zfar-znear) , 1.0],
|
|
||||||
[ 0.0 , 0.0, -(2.0*zfar*znear)/(zfar-znear), 0.0],
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
rotation_system(&mut world, dt);
|
||||||
|
|
||||||
let uniforms = uniform! {
|
// ask for next frame
|
||||||
matrix: [
|
window.request_redraw();
|
||||||
[0.01, 0.0, 0.0, 0.0],
|
}
|
||||||
[0.0, 0.01, 0.0, 0.0],
|
_ => {}
|
||||||
[0.0, 0.0, 0.01, 0.0],
|
}
|
||||||
[x, y, 2.0, 1.0f32 ],
|
})
|
||||||
],
|
.map_err(Into::into)
|
||||||
u_light: light,
|
|
||||||
perspective: perspective
|
|
||||||
};
|
|
||||||
|
|
||||||
let params = glium::DrawParameters {
|
|
||||||
depth: glium::Depth {
|
|
||||||
test: glium::draw_parameters::DepthTest::IfLess,
|
|
||||||
write: true,
|
|
||||||
.. Default::default()
|
|
||||||
},
|
|
||||||
.. Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
target.draw((&positions, &normals), &indices, &program, &uniforms,
|
|
||||||
¶ms).unwrap();
|
|
||||||
target.finish().unwrap();
|
|
||||||
},
|
|
||||||
// Because glium doesn't know about windows we need to resize the display
|
|
||||||
// when the window's size has changed.
|
|
||||||
glium::winit::event::WindowEvent::Resized(window_size) => {
|
|
||||||
display.resize(window_size.into());
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
// By requesting a redraw in response to a RedrawEventsCleared event we get continuous rendering.
|
|
||||||
// For applications that only change due to user input you could remove this handler.
|
|
||||||
glium::winit::event::Event::AboutToWait => {
|
|
||||||
window.request_redraw();
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
124
src/model.rs
Normal file
124
src/model.rs
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
//! GPU-ready mesh loader for **glTF 2.0**
|
||||||
|
//!
|
||||||
|
//! Loads the first mesh/primitive found in a .gltf/.glb file.
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use glium::{backend::Facade, implement_vertex, IndexBuffer, VertexBuffer};
|
||||||
|
use glium::index::PrimitiveType;
|
||||||
|
use gltf::mesh::util::ReadIndices;
|
||||||
|
use std::{fmt::Debug, path::Path};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Vertex {
|
||||||
|
pub position: [f32; 3],
|
||||||
|
pub normal: [f32; 3],
|
||||||
|
}
|
||||||
|
implement_vertex!(Vertex, position, normal);
|
||||||
|
|
||||||
|
pub struct Mesh {
|
||||||
|
pub vbuf: VertexBuffer<Vertex>,
|
||||||
|
pub ibuf: IndexBuffer<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a glTF 2.0 file from disk and upload the first primitive to the GPU.
|
||||||
|
pub fn load_gltf<P, F>(path: P, facade: &F) -> Result<Mesh>
|
||||||
|
where
|
||||||
|
P: AsRef<Path> + Debug, // `gltf::import` wants Debug for diagnostics :contentReference[oaicite:3]{index=3}
|
||||||
|
F: Facade + ?Sized,
|
||||||
|
{
|
||||||
|
// -- parse the asset & bring buffer blobs into memory --
|
||||||
|
let (doc, buffers, _images) =
|
||||||
|
gltf::import(path.as_ref()).context("failed to import glTF file")?; // :contentReference[oaicite:4]{index=4}
|
||||||
|
|
||||||
|
// -- grab the very first mesh / primitive --
|
||||||
|
let mesh = doc.meshes().next().context("glTF has no meshes")?;
|
||||||
|
let primitive = mesh.primitives().next().context("mesh has no primitives")?;
|
||||||
|
|
||||||
|
// -- read vertex and index streams using the util::Reader helper --
|
||||||
|
let reader = primitive.reader(|buf| Some(&buffers[buf.index()].0)); // Reader pattern :contentReference[oaicite:5]{index=5}
|
||||||
|
|
||||||
|
let positions : Vec<[f32; 3]> = reader
|
||||||
|
.read_positions()
|
||||||
|
.context("primitive is missing POSITION attribute")? // POSITION is mandatory :contentReference[oaicite:6]{index=6}
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let normals : Vec<[f32; 3]> = reader
|
||||||
|
.read_normals()
|
||||||
|
.context("primitive is missing NORMAL attribute")?
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let indices : Vec<u32> = reader
|
||||||
|
.read_indices()
|
||||||
|
.context("primitive has no indices")?
|
||||||
|
.into_u32()
|
||||||
|
.collect(); // ReadIndices enum :contentReference[oaicite:7]{index=7}
|
||||||
|
|
||||||
|
// -- interleave into our engine's Vertex struct --
|
||||||
|
let vertices: Vec<Vertex> = positions
|
||||||
|
.into_iter()
|
||||||
|
.zip(normals.into_iter())
|
||||||
|
.map(|(p, n)| Vertex { position: p, normal: n })
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// -- immutable GPU buffers (fast path in glium) --
|
||||||
|
let vbuf = VertexBuffer::immutable(facade, &vertices)?; // Immutable VBO :contentReference[oaicite:8]{index=8}
|
||||||
|
let ibuf = IndexBuffer ::immutable(facade, PrimitiveType::TrianglesList, &indices)?;
|
||||||
|
|
||||||
|
Ok(Mesh { vbuf, ibuf })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a unit cube (edge length = 2) with per-face normals.
|
||||||
|
pub fn cube<F>(facade: &F) -> Result<Mesh>
|
||||||
|
where
|
||||||
|
F: Facade + ?Sized,
|
||||||
|
{
|
||||||
|
// 24 unique vertices (4 per face) so that each face has a flat normal.
|
||||||
|
let vertices: [Vertex; 24] = [
|
||||||
|
// Front (+Z)
|
||||||
|
Vertex { position: [-1.0, -1.0, 1.0], normal: [ 0.0, 0.0, 1.0] },
|
||||||
|
Vertex { position: [ 1.0, -1.0, 1.0], normal: [ 0.0, 0.0, 1.0] },
|
||||||
|
Vertex { position: [ 1.0, 1.0, 1.0], normal: [ 0.0, 0.0, 1.0] },
|
||||||
|
Vertex { position: [-1.0, 1.0, 1.0], normal: [ 0.0, 0.0, 1.0] },
|
||||||
|
|
||||||
|
// Back (-Z)
|
||||||
|
Vertex { position: [ 1.0, -1.0, -1.0], normal: [ 0.0, 0.0, -1.0] },
|
||||||
|
Vertex { position: [-1.0, -1.0, -1.0], normal: [ 0.0, 0.0, -1.0] },
|
||||||
|
Vertex { position: [-1.0, 1.0, -1.0], normal: [ 0.0, 0.0, -1.0] },
|
||||||
|
Vertex { position: [ 1.0, 1.0, -1.0], normal: [ 0.0, 0.0, -1.0] },
|
||||||
|
|
||||||
|
// Left (-X)
|
||||||
|
Vertex { position: [-1.0, -1.0, -1.0], normal: [-1.0, 0.0, 0.0] },
|
||||||
|
Vertex { position: [-1.0, -1.0, 1.0], normal: [-1.0, 0.0, 0.0] },
|
||||||
|
Vertex { position: [-1.0, 1.0, 1.0], normal: [-1.0, 0.0, 0.0] },
|
||||||
|
Vertex { position: [-1.0, 1.0, -1.0], normal: [-1.0, 0.0, 0.0] },
|
||||||
|
|
||||||
|
// Right (+X)
|
||||||
|
Vertex { position: [ 1.0, -1.0, 1.0], normal: [ 1.0, 0.0, 0.0] },
|
||||||
|
Vertex { position: [ 1.0, -1.0, -1.0], normal: [ 1.0, 0.0, 0.0] },
|
||||||
|
Vertex { position: [ 1.0, 1.0, -1.0], normal: [ 1.0, 0.0, 0.0] },
|
||||||
|
Vertex { position: [ 1.0, 1.0, 1.0], normal: [ 1.0, 0.0, 0.0] },
|
||||||
|
|
||||||
|
// Top (+Y)
|
||||||
|
Vertex { position: [-1.0, 1.0, 1.0], normal: [ 0.0, 1.0, 0.0] },
|
||||||
|
Vertex { position: [ 1.0, 1.0, 1.0], normal: [ 0.0, 1.0, 0.0] },
|
||||||
|
Vertex { position: [ 1.0, 1.0, -1.0], normal: [ 0.0, 1.0, 0.0] },
|
||||||
|
Vertex { position: [-1.0, 1.0, -1.0], normal: [ 0.0, 1.0, 0.0] },
|
||||||
|
|
||||||
|
// Bottom (-Y)
|
||||||
|
Vertex { position: [-1.0, -1.0, -1.0], normal: [ 0.0, -1.0, 0.0] },
|
||||||
|
Vertex { position: [ 1.0, -1.0, -1.0], normal: [ 0.0, -1.0, 0.0] },
|
||||||
|
Vertex { position: [ 1.0, -1.0, 1.0], normal: [ 0.0, -1.0, 0.0] },
|
||||||
|
Vertex { position: [-1.0, -1.0, 1.0], normal: [ 0.0, -1.0, 0.0] },
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut indices: Vec<u32> = Vec::with_capacity(36);
|
||||||
|
for face in 0..6 {
|
||||||
|
let o = (face * 4) as u32;
|
||||||
|
indices.extend_from_slice(&[o, o + 1, o + 2, o, o + 2, o + 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let vbuf = VertexBuffer::immutable(facade, &vertices)?;
|
||||||
|
let ibuf = IndexBuffer::immutable(facade, PrimitiveType::TrianglesList, &indices)?;
|
||||||
|
|
||||||
|
Ok(Mesh { vbuf, ibuf })
|
||||||
|
}
|
||||||
95
src/render.rs
Normal file
95
src/render.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
use crate::camera::Camera;
|
||||||
|
use crate::ecs::{MeshHandle, Transform};
|
||||||
|
use crate::model::Mesh;
|
||||||
|
use glium::{uniform, Program, Surface};
|
||||||
|
use glam::Vec3;
|
||||||
|
use hecs::World;
|
||||||
|
use glium::glutin::surface::WindowSurface;
|
||||||
|
|
||||||
|
/// Generic rendering backend trait.
|
||||||
|
pub trait Renderer {
|
||||||
|
/// Render a single frame for the given `World`.
|
||||||
|
fn render(&mut self, world: &World);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Concrete OpenGL (glium) renderer implementing `Renderer`.
|
||||||
|
pub struct GliumRenderer {
|
||||||
|
display: glium::Display<WindowSurface>,
|
||||||
|
program: Program,
|
||||||
|
pub meshes: Vec<Mesh>,
|
||||||
|
params: glium::DrawParameters<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GliumRenderer {
|
||||||
|
/// Create a new OpenGL renderer consuming the provided `display`.
|
||||||
|
pub fn new(display: glium::Display<WindowSurface>) -> anyhow::Result<Self> {
|
||||||
|
const VERT: &str = r#"
|
||||||
|
#version 330 core
|
||||||
|
in vec3 position;
|
||||||
|
in vec3 normal;
|
||||||
|
uniform mat4 model;
|
||||||
|
uniform mat4 view;
|
||||||
|
uniform mat4 projection;
|
||||||
|
uniform vec3 light_dir;
|
||||||
|
out vec3 v_color;
|
||||||
|
void main() {
|
||||||
|
vec3 n = normalize(mat3(model) * normal);
|
||||||
|
float diff = max(dot(n, -light_dir), 0.0);
|
||||||
|
vec3 base = vec3(0.6, 0.6, 0.8);
|
||||||
|
v_color = base * diff + 0.1;
|
||||||
|
gl_Position = projection * view * model * vec4(position, 1.0);
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
const FRAG: &str = r#"
|
||||||
|
#version 330 core
|
||||||
|
in vec3 v_color;
|
||||||
|
out vec4 color;
|
||||||
|
void main() { color = vec4(v_color, 1.0); }"#;
|
||||||
|
|
||||||
|
let program = Program::from_source(&display, VERT, FRAG, None)?;
|
||||||
|
|
||||||
|
let params = glium::DrawParameters {
|
||||||
|
depth: glium::Depth {
|
||||||
|
test: glium::draw_parameters::DepthTest::IfLess,
|
||||||
|
write: true,
|
||||||
|
.. Default::default()
|
||||||
|
},
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self { display, program, meshes: Vec::new(), params })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderer for GliumRenderer {
|
||||||
|
fn render(&mut self, world: &World) {
|
||||||
|
let mut frame = self.display.draw();
|
||||||
|
frame.clear_color_and_depth((0.1, 0.1, 0.15, 1.0), 1.0);
|
||||||
|
|
||||||
|
// Expect exactly one active camera in the world.
|
||||||
|
let cam = match world.query::<&Camera>().iter().next() {
|
||||||
|
Some((_, cam)) => *cam,
|
||||||
|
None => {
|
||||||
|
eprintln!("[renderer] No camera component found – skipping frame");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let light_dir: Vec3 = Vec3::new(-1.0, -1.0, -1.0).normalize();
|
||||||
|
|
||||||
|
for (_, (tr, mh)) in world.query::<(&Transform, &MeshHandle)>().iter() {
|
||||||
|
let mesh = &self.meshes[mh.0];
|
||||||
|
let uniforms = uniform! {
|
||||||
|
model: tr.matrix().to_cols_array_2d(),
|
||||||
|
view: cam.view().to_cols_array_2d(),
|
||||||
|
projection: cam.projection().to_cols_array_2d(),
|
||||||
|
light_dir: [light_dir.x, light_dir.y, light_dir.z],
|
||||||
|
};
|
||||||
|
|
||||||
|
frame.draw(&mesh.vbuf, &mesh.ibuf, &self.program, &uniforms, &self.params)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.finish().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue