mirror of https://github.com/AxioDL/metaforce.git
Remove old aurora code
This commit is contained in:
parent
c7f05d0a59
commit
7186f5d4e5
|
@ -1,17 +0,0 @@
|
||||||
# Rust
|
|
||||||
target/
|
|
||||||
**/*.rs.bk
|
|
||||||
generated/
|
|
||||||
|
|
||||||
# cargo-mobile
|
|
||||||
.cargo/
|
|
||||||
/gen
|
|
||||||
|
|
||||||
# macOS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# JetBrains
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Generated SPIR-V
|
|
||||||
*.spv
|
|
|
@ -1,42 +0,0 @@
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
||||||
set(CARGO_CMD cargo build --verbose)
|
|
||||||
set(TARGET_DIR "debug")
|
|
||||||
else ()
|
|
||||||
set(CARGO_CMD cargo build --release --verbose)
|
|
||||||
set(TARGET_DIR "release")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if(USE_LTO)
|
|
||||||
set(RUST_FLAGS "-Clinker-plugin-lto" "-Clinker=clang" "-Clink-arg=-fuse-ld=lld")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(AURORA_LIB "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/libaurora.a")
|
|
||||||
|
|
||||||
set(AURORA_CXX "${CMAKE_CURRENT_BINARY_DIR}/aurora.cpp")
|
|
||||||
set(AURORA_SHADERS_CXX "${CMAKE_CURRENT_BINARY_DIR}/aurora_shaders.cpp")
|
|
||||||
set(AURORA_IMGUI_CXX "${CMAKE_CURRENT_BINARY_DIR}/aurora_imgui.cpp")
|
|
||||||
add_library(aurora STATIC ${AURORA_CXX} ${AURORA_SHADERS_CXX})
|
|
||||||
target_include_directories(aurora PRIVATE ../Runtime)
|
|
||||||
target_include_directories(aurora PUBLIC include ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${AURORA_CXX} ${AURORA_SHADERS_CXX} ${AURORA_IMGUI_CXX} ${AURORA_LIB} #__no_exist
|
|
||||||
COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} RUSTFLAGS="${RUST_FLAGS}" ${CARGO_CMD}
|
|
||||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/cxxbridge.rs.cc ${AURORA_CXX}
|
|
||||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/cxxbridge.rs.h ${CMAKE_CURRENT_BINARY_DIR}/aurora.h
|
|
||||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/shaders/cxxbridge.rs.cc ${AURORA_SHADERS_CXX}
|
|
||||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/shaders/cxxbridge.rs.h ${CMAKE_CURRENT_BINARY_DIR}/aurora_shaders.h
|
|
||||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/imgui/cxxbridge.rs.cc ${AURORA_IMGUI_CXX}
|
|
||||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/imgui/cxxbridge.rs.h ${CMAKE_CURRENT_BINARY_DIR}/aurora_imgui.h
|
|
||||||
DEPENDS src/cxxbridge.rs
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
add_custom_target(
|
|
||||||
always_run ALL
|
|
||||||
DEPENDS __no_exist
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(aurora pthread dl ${AURORA_LIB} zeus)
|
|
||||||
|
|
||||||
add_test(NAME aurora_test
|
|
||||||
COMMAND cargo test
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,77 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "aurora"
|
|
||||||
version = "0.0.1"
|
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
#[[bin]]
|
|
||||||
#name = "metaforce"
|
|
||||||
#path = "src/main.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
cxx = "1.0.64"
|
|
||||||
env_logger = "0.9.0"
|
|
||||||
pollster = "0.2.4"
|
|
||||||
binrw = "0.8.4"
|
|
||||||
modular-bitfield = "0.11.2"
|
|
||||||
num-traits = "0.2.14"
|
|
||||||
num_enum = "0.5.6"
|
|
||||||
bytemuck = "1.7.3"
|
|
||||||
bytemuck_derive = "1.0.1"
|
|
||||||
log = "0.4.14"
|
|
||||||
log-panics = "2.0.0"
|
|
||||||
cgmath = "0.18.0"
|
|
||||||
smallvec = "1.7.0"
|
|
||||||
scopeguard = "1.1.0"
|
|
||||||
twox-hash = "1.6.2"
|
|
||||||
winit = "0.26.1"
|
|
||||||
bitflags = "1.3.2"
|
|
||||||
# custom sdl2
|
|
||||||
sdl2 = { git = "https://github.com/encounter/rust-sdl2.git", rev = "f39d7a7549fd59bebb1fb42ec12973200bb3080b", features = ["no-link", "hidapi"] }
|
|
||||||
|
|
||||||
[dependencies.imgui]
|
|
||||||
git = "https://github.com/imgui-rs/imgui-rs"
|
|
||||||
rev = "55a76370466953d5c044afc123330d0262b50e17"
|
|
||||||
[dependencies.imgui-winit-support]
|
|
||||||
git = "https://github.com/imgui-rs/imgui-rs"
|
|
||||||
rev = "55a76370466953d5c044afc123330d0262b50e17"
|
|
||||||
default-features = false
|
|
||||||
features = ["winit-26"]
|
|
||||||
[patch.'https://github.com/imgui-rs/imgui-rs']
|
|
||||||
imgui-sys = { path = "../imgui-sys" }
|
|
||||||
[patch.crates-io]
|
|
||||||
imgui-sys = { path = "../imgui-sys" }
|
|
||||||
naga = { git = "https://github.com/gfx-rs/naga", rev = "d6f8958b346676396db97053771b8d95684c47ee" }
|
|
||||||
|
|
||||||
[dependencies.wgpu]
|
|
||||||
#path = "../../wgpu/wgpu"
|
|
||||||
git = "https://github.com/gfx-rs/wgpu"
|
|
||||||
rev = "766c6cda1917c1265fa8a5f610ef7516d4314d1b"
|
|
||||||
features = ["spirv"]
|
|
||||||
|
|
||||||
#[patch.'https://github.com/gfx-rs/naga']
|
|
||||||
#naga = { path = "../../naga" }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
#naga = { path = "../../naga", features = ['spv-out', 'wgsl-in'] }
|
|
||||||
naga = { git = "https://github.com/gfx-rs/naga", rev = "d6f8958b346676396db97053771b8d95684c47ee", features = ['spv-out', 'wgsl-in'] }
|
|
||||||
bytemuck = "1.7.3"
|
|
||||||
cxx-build = "1.0.64"
|
|
||||||
bindgen = "0.59.2"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
|
||||||
android_logger = "0.10.1"
|
|
||||||
ndk-glue = "0.5.0"
|
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
|
||||||
wgpu-subscriber = "0.1.0"
|
|
||||||
|
|
||||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
|
||||||
console_error_panic_hook = "0.1.7"
|
|
||||||
console_log = "0.2.0"
|
|
||||||
wasm-bindgen-futures = "0.4.28"
|
|
||||||
#features = ["web-sys"]
|
|
|
@ -1,54 +0,0 @@
|
||||||
fn main() {
|
|
||||||
let zeus_include = std::path::PathBuf::from(format!(
|
|
||||||
"{}/../extern/zeus/include",
|
|
||||||
std::env::var("CARGO_MANIFEST_DIR").unwrap()
|
|
||||||
))
|
|
||||||
.canonicalize()
|
|
||||||
.unwrap();
|
|
||||||
let imgui_include = std::path::PathBuf::from(format!(
|
|
||||||
"{}/../extern/imgui",
|
|
||||||
std::env::var("CARGO_MANIFEST_DIR").unwrap()
|
|
||||||
))
|
|
||||||
.canonicalize()
|
|
||||||
.unwrap();
|
|
||||||
let imgui_engine_include = std::path::PathBuf::from(format!(
|
|
||||||
"{}/../imgui",
|
|
||||||
std::env::var("CARGO_MANIFEST_DIR").unwrap()
|
|
||||||
))
|
|
||||||
.canonicalize()
|
|
||||||
.unwrap();
|
|
||||||
// let include_dir = include_path.to_string_lossy();
|
|
||||||
// let bindings = bindgen::Builder::default()
|
|
||||||
// .header(format!("{}/zeus/CVector3f.hpp", include_dir))
|
|
||||||
// .clang_arg(format!("-I{}", include_dir))
|
|
||||||
// .parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
|
||||||
// .generate()
|
|
||||||
// .expect("Unable to generate bindings");
|
|
||||||
// let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
|
||||||
// bindings.write_to_file(out_path.join("zeus.rs")).expect("Couldn't write bindings!");
|
|
||||||
|
|
||||||
#[cfg(any(not(unix), target_os = "macos", target_os = "ios"))]
|
|
||||||
let cxx_flag = "-std=c++17";
|
|
||||||
#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
|
|
||||||
let cxx_flag = "-std=gnu++17";
|
|
||||||
cxx_build::bridge("src/cxxbridge.rs")
|
|
||||||
.include("include")
|
|
||||||
.include(zeus_include.clone())
|
|
||||||
.flag(cxx_flag)
|
|
||||||
.compile("aurora");
|
|
||||||
println!("cargo:rerun-if-changed=src/cxxbridge.rs");
|
|
||||||
cxx_build::bridge("src/shaders/cxxbridge.rs")
|
|
||||||
.include("include")
|
|
||||||
.include(zeus_include.clone())
|
|
||||||
.flag(cxx_flag)
|
|
||||||
.compile("aurora_shaders");
|
|
||||||
println!("cargo:rerun-if-changed=src/shaders/cxxbridge.rs");
|
|
||||||
cxx_build::bridge("src/imgui/cxxbridge.rs")
|
|
||||||
.include("include")
|
|
||||||
.include(zeus_include.clone())
|
|
||||||
.include(imgui_include.clone())
|
|
||||||
.include(imgui_engine_include.clone())
|
|
||||||
.flag(cxx_flag)
|
|
||||||
.compile("aurora_imgui");
|
|
||||||
println!("cargo:rerun-if-changed=src/imgui/cxxbridge.rs");
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
|
||||||
#ifndef ENABLE_BITWISE_ENUM
|
|
||||||
#define ENABLE_BITWISE_ENUM(type) \
|
|
||||||
constexpr type operator|(type a, type b) noexcept { \
|
|
||||||
using T = std::underlying_type_t<type>; \
|
|
||||||
return type(static_cast<T>(a) | static_cast<T>(b)); \
|
|
||||||
} \
|
|
||||||
constexpr type operator&(type a, type b) noexcept { \
|
|
||||||
using T = std::underlying_type_t<type>; \
|
|
||||||
return type(static_cast<T>(a) & static_cast<T>(b)); \
|
|
||||||
} \
|
|
||||||
constexpr type& operator|=(type& a, type b) noexcept { \
|
|
||||||
using T = std::underlying_type_t<type>; \
|
|
||||||
a = type(static_cast<T>(a) | static_cast<T>(b)); \
|
|
||||||
return a; \
|
|
||||||
} \
|
|
||||||
constexpr type& operator&=(type& a, type b) noexcept { \
|
|
||||||
using T = std::underlying_type_t<type>; \
|
|
||||||
a = type(static_cast<T>(a) & static_cast<T>(b)); \
|
|
||||||
return a; \
|
|
||||||
} \
|
|
||||||
constexpr type operator~(type key) noexcept { \
|
|
||||||
using T = std::underlying_type_t<type>; \
|
|
||||||
return type(~static_cast<T>(key)); \
|
|
||||||
} \
|
|
||||||
constexpr bool True(type key) noexcept { \
|
|
||||||
using T = std::underlying_type_t<type>; \
|
|
||||||
return static_cast<T>(key) != 0; \
|
|
||||||
} \
|
|
||||||
constexpr bool False(type key) noexcept { \
|
|
||||||
using T = std::underlying_type_t<type>; \
|
|
||||||
return static_cast<T>(key) == 0; \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace aurora {
|
|
||||||
enum class SpecialKey : uint8_t;
|
|
||||||
enum class ModifierKey : uint16_t {
|
|
||||||
None = 0,
|
|
||||||
LeftShift = 1 << 0,
|
|
||||||
RightShift = 1 << 1,
|
|
||||||
LeftControl = 1 << 2,
|
|
||||||
RightControl = 1 << 3,
|
|
||||||
LeftAlt = 1 << 3,
|
|
||||||
RightAlt = 1 << 4,
|
|
||||||
LeftGui = 1 << 5,
|
|
||||||
RightGui = 1 << 6,
|
|
||||||
Num = 1 << 7,
|
|
||||||
Caps = 1 << 8,
|
|
||||||
Mode = 1 << 9,
|
|
||||||
// SDL has a reserved value we don't need
|
|
||||||
};
|
|
||||||
ENABLE_BITWISE_ENUM(ModifierKey);
|
|
||||||
|
|
||||||
enum class ControllerButton : uint8_t;
|
|
||||||
enum class ControllerAxis : uint8_t;
|
|
||||||
|
|
||||||
struct WindowSize;
|
|
||||||
|
|
||||||
struct App;
|
|
||||||
struct AppDelegate {
|
|
||||||
AppDelegate() = default;
|
|
||||||
virtual ~AppDelegate() noexcept = default;
|
|
||||||
AppDelegate(const AppDelegate&) = delete;
|
|
||||||
AppDelegate& operator=(const AppDelegate&) = delete;
|
|
||||||
AppDelegate(AppDelegate&&) = delete;
|
|
||||||
AppDelegate& operator=(AppDelegate&&) = delete;
|
|
||||||
|
|
||||||
virtual void onAppLaunched() noexcept = 0;
|
|
||||||
virtual bool onAppIdle(float dt) noexcept = 0;
|
|
||||||
virtual void onAppDraw() noexcept = 0;
|
|
||||||
virtual void onAppPostDraw() noexcept = 0;
|
|
||||||
virtual void onAppWindowResized(const WindowSize& size) noexcept = 0;
|
|
||||||
virtual void onAppWindowMoved(int32_t x, int32_t y) noexcept = 0;
|
|
||||||
virtual void onAppExiting() noexcept = 0;
|
|
||||||
|
|
||||||
// Input
|
|
||||||
virtual void onCharKeyDown(uint8_t charCode, ModifierKey mods, bool isRepeat) noexcept = 0;
|
|
||||||
virtual void onCharKeyUp(uint8_t charCode, ModifierKey mods) noexcept = 0;
|
|
||||||
virtual void onSpecialKeyDown(SpecialKey key, ModifierKey mods, bool isRepeat) noexcept = 0;
|
|
||||||
virtual void onSpecialKeyUp(SpecialKey key, ModifierKey mods) noexcept = 0;
|
|
||||||
|
|
||||||
// Controller
|
|
||||||
virtual void onControllerAdded(uint32_t which) noexcept = 0;
|
|
||||||
virtual void onControllerRemoved(uint32_t which) noexcept = 0;
|
|
||||||
virtual void onControllerButton(uint32_t which, ControllerButton button, bool pressed) noexcept = 0;
|
|
||||||
virtual void onControllerAxis(uint32_t which, ControllerAxis axis, int16_t value) noexcept = 0;
|
|
||||||
|
|
||||||
// virtual void resized([[maybe_unused]] const WindowSize& rect, [[maybe_unused]] bool sync) noexcept {}
|
|
||||||
// virtual void mouseDown([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] EMouseButton button,
|
|
||||||
// [[maybe_unused]] EModifierKey mods) noexcept {}
|
|
||||||
// virtual void mouseUp([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] EMouseButton button,
|
|
||||||
// [[maybe_unused]] EModifierKey mods) noexcept {}
|
|
||||||
// virtual void mouseMove([[maybe_unused]] const SWindowCoord& coord) noexcept {}
|
|
||||||
// virtual void mouseEnter([[maybe_unused]] const SWindowCoord& coord) noexcept {}
|
|
||||||
// virtual void mouseLeave([[maybe_unused]] const SWindowCoord& coord) noexcept {}
|
|
||||||
// virtual void scroll([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] const SScrollDelta& scroll)
|
|
||||||
// noexcept {}
|
|
||||||
//
|
|
||||||
// virtual void touchDown([[maybe_unused]] const STouchCoord& coord, [[maybe_unused]] uintptr_t tid) noexcept {}
|
|
||||||
// virtual void touchUp([[maybe_unused]] const STouchCoord& coord, [[maybe_unused]] uintptr_t tid) noexcept {}
|
|
||||||
// virtual void touchMove([[maybe_unused]] const STouchCoord& coord, [[maybe_unused]] uintptr_t tid) noexcept {}
|
|
||||||
//
|
|
||||||
// virtual void charKeyDown([[maybe_unused]] unsigned long charCode, [[maybe_unused]] EModifierKey mods,
|
|
||||||
// [[maybe_unused]] bool isRepeat) noexcept {}
|
|
||||||
// virtual void charKeyUp([[maybe_unused]] unsigned long charCode, [[maybe_unused]] EModifierKey mods) noexcept {}
|
|
||||||
// virtual void specialKeyDown([[maybe_unused]] ESpecialKey key, [[maybe_unused]] EModifierKey mods,
|
|
||||||
// [[maybe_unused]] bool isRepeat) noexcept {}
|
|
||||||
// virtual void specialKeyUp([[maybe_unused]] ESpecialKey key, [[maybe_unused]] EModifierKey mods) noexcept {}
|
|
||||||
// virtual void modKeyDown([[maybe_unused]] EModifierKey mod, [[maybe_unused]] bool isRepeat) noexcept {}
|
|
||||||
// virtual void modKeyUp([[maybe_unused]] EModifierKey mod) noexcept {}
|
|
||||||
};
|
|
||||||
} // namespace aurora
|
|
|
@ -1,22 +0,0 @@
|
||||||
#include "aurora.hpp"
|
|
||||||
#include <cinttypes>
|
|
||||||
|
|
||||||
namespace aurora {
|
|
||||||
void App_onAppLaunched(AppDelegate& cb) noexcept;
|
|
||||||
bool App_onAppIdle(AppDelegate& cb, float dt) noexcept;
|
|
||||||
void App_onAppDraw(AppDelegate& cb) noexcept;
|
|
||||||
void App_onAppPostDraw(AppDelegate& cb) noexcept;
|
|
||||||
void App_onAppWindowResized(AppDelegate& cb, const WindowSize& size) noexcept;
|
|
||||||
void App_onAppWindowMoved(AppDelegate& cb, int32_t x, int32_t y) noexcept;
|
|
||||||
void App_onAppExiting(AppDelegate& cb) noexcept;
|
|
||||||
// Input
|
|
||||||
void App_onCharKeyDown(AppDelegate& cb, uint8_t code, std::uint16_t mods, bool isRepeat) noexcept;
|
|
||||||
void App_onCharKeyUp(AppDelegate& cb, uint8_t code, std::uint16_t mods) noexcept;
|
|
||||||
void App_onSpecialKeyDown(AppDelegate& cb, SpecialKey key, std::uint16_t mods, bool isRepeat) noexcept;
|
|
||||||
void App_onSpecialKeyUp(AppDelegate& cb, SpecialKey key, std::uint16_t mods) noexcept;
|
|
||||||
// Controller
|
|
||||||
void App_onControllerAdded(AppDelegate& cb, uint32_t which) noexcept;
|
|
||||||
void App_onControllerRemoved(AppDelegate& cb, uint32_t which) noexcept;
|
|
||||||
void App_onControllerButton(AppDelegate& cb, uint32_t which, ControllerButton button, bool pressed) noexcept;
|
|
||||||
void App_onControllerAxis(AppDelegate& cb, uint32_t which, ControllerAxis axis, int16_t value) noexcept;
|
|
||||||
} // namespace aurora
|
|
|
@ -1,35 +0,0 @@
|
||||||
#include "include/lib.hpp"
|
|
||||||
|
|
||||||
namespace aurora {
|
|
||||||
void App_onAppLaunched(AppDelegate& cb) noexcept { return cb.onAppLaunched(); }
|
|
||||||
bool App_onAppIdle(AppDelegate& cb, float dt) noexcept { return cb.onAppIdle(dt); }
|
|
||||||
void App_onAppDraw(AppDelegate& cb) noexcept { cb.onAppDraw(); }
|
|
||||||
void App_onAppPostDraw(AppDelegate& cb) noexcept { cb.onAppPostDraw(); }
|
|
||||||
void App_onAppWindowResized(AppDelegate& cb, const WindowSize& size) noexcept { cb.onAppWindowResized(size); }
|
|
||||||
void App_onAppWindowMoved(AppDelegate& cb, int32_t x, int32_t y) noexcept { cb.onAppWindowMoved(x, y); }
|
|
||||||
void App_onAppExiting(AppDelegate& cb) noexcept { cb.onAppExiting(); }
|
|
||||||
|
|
||||||
// Input
|
|
||||||
void App_onCharKeyDown(AppDelegate& cb, uint8_t code, std::uint16_t mods, bool isRepeat) noexcept {
|
|
||||||
cb.onCharKeyDown(code, static_cast<ModifierKey>(mods), isRepeat);
|
|
||||||
}
|
|
||||||
void App_onCharKeyUp(AppDelegate& cb, uint8_t code, std::uint16_t mods) noexcept {
|
|
||||||
cb.onCharKeyUp(code, static_cast<ModifierKey>(mods));
|
|
||||||
}
|
|
||||||
void App_onSpecialKeyDown(AppDelegate& cb, SpecialKey key, std::uint16_t mods, bool isRepeat) noexcept {
|
|
||||||
cb.onSpecialKeyDown(key, static_cast<ModifierKey>(mods), isRepeat);
|
|
||||||
}
|
|
||||||
void App_onSpecialKeyUp(AppDelegate& cb, SpecialKey key, std::uint16_t mods) noexcept {
|
|
||||||
cb.onSpecialKeyUp(key, static_cast<ModifierKey>(mods));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Controller
|
|
||||||
void App_onControllerAdded(AppDelegate& cb, uint32_t which) noexcept { cb.onControllerAdded(which); }
|
|
||||||
void App_onControllerRemoved(AppDelegate& cb, uint32_t which) noexcept { cb.onControllerRemoved(which); }
|
|
||||||
void App_onControllerButton(AppDelegate& cb, uint32_t which, ControllerButton button, bool pressed) noexcept {
|
|
||||||
cb.onControllerButton(which, button, pressed);
|
|
||||||
}
|
|
||||||
void App_onControllerAxis(AppDelegate& cb, uint32_t which, ControllerAxis axis, int16_t value) noexcept {
|
|
||||||
cb.onControllerAxis(which, axis, value);
|
|
||||||
}
|
|
||||||
} // namespace aurora
|
|
|
@ -1,8 +0,0 @@
|
||||||
fn_single_line = true
|
|
||||||
group_imports = "StdExternalCrate"
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
overflow_delimited_expr = true
|
|
||||||
reorder_impl_items = true
|
|
||||||
use_field_init_shorthand = true
|
|
||||||
use_small_heuristics = "Max"
|
|
||||||
where_single_line = true
|
|
|
@ -1,266 +0,0 @@
|
||||||
use sdl2::controller::{Axis, Button};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
App, app_run, get_args, get_backend, get_backend_string,
|
|
||||||
get_dxt_compression_supported,
|
|
||||||
get_window_size,
|
|
||||||
sdl::{
|
|
||||||
get_controller_name,
|
|
||||||
get_controller_player_index,
|
|
||||||
is_controller_gamecube,
|
|
||||||
set_controller_player_index,
|
|
||||||
}, set_fullscreen, set_window_title, WindowContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cxx::bridge(namespace = "aurora")]
|
|
||||||
pub(crate) mod ffi {
|
|
||||||
unsafe extern "C++" {
|
|
||||||
include!("aurora.hpp");
|
|
||||||
pub(crate) type AppDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C++" {
|
|
||||||
include!("lib.hpp");
|
|
||||||
pub(crate) fn App_onAppLaunched(cb: Pin<&mut AppDelegate>);
|
|
||||||
pub(crate) fn App_onAppIdle(cb: Pin<&mut AppDelegate>, dt: f32) -> bool;
|
|
||||||
pub(crate) fn App_onAppDraw(cb: Pin<&mut AppDelegate>);
|
|
||||||
pub(crate) fn App_onAppPostDraw(cb: Pin<&mut AppDelegate>);
|
|
||||||
pub(crate) fn App_onAppWindowResized(cb: Pin<&mut AppDelegate>, size: &WindowSize);
|
|
||||||
pub(crate) fn App_onAppWindowMoved(cb: Pin<&mut AppDelegate>, x: i32, y: i32);
|
|
||||||
pub(crate) fn App_onAppExiting(cb: Pin<&mut AppDelegate>);
|
|
||||||
// Input
|
|
||||||
pub(crate) fn App_onCharKeyDown(cb: Pin<&mut AppDelegate>,
|
|
||||||
code: u8,
|
|
||||||
mods: u16,
|
|
||||||
is_repeat: bool,
|
|
||||||
);
|
|
||||||
pub(crate) fn App_onCharKeyUp(
|
|
||||||
cb: Pin<&mut AppDelegate>,
|
|
||||||
code: u8,
|
|
||||||
mods: u16,
|
|
||||||
);
|
|
||||||
pub(crate) fn App_onSpecialKeyDown(
|
|
||||||
cb: Pin<&mut AppDelegate>,
|
|
||||||
key: SpecialKey,
|
|
||||||
keymod: u16,
|
|
||||||
is_repeat: bool,
|
|
||||||
);
|
|
||||||
pub(crate) fn App_onSpecialKeyUp(
|
|
||||||
cb: Pin<&mut AppDelegate>,
|
|
||||||
key: SpecialKey,
|
|
||||||
mods: u16,
|
|
||||||
);
|
|
||||||
// Controller
|
|
||||||
pub(crate) fn App_onControllerAdded(cb: Pin<&mut AppDelegate>, which: u32);
|
|
||||||
pub(crate) fn App_onControllerRemoved(cb: Pin<&mut AppDelegate>, which: u32);
|
|
||||||
pub(crate) fn App_onControllerButton(
|
|
||||||
cb: Pin<&mut AppDelegate>,
|
|
||||||
idx: u32,
|
|
||||||
button: ControllerButton,
|
|
||||||
pressed: bool,
|
|
||||||
);
|
|
||||||
pub(crate) fn App_onControllerAxis(
|
|
||||||
cb: Pin<&mut AppDelegate>,
|
|
||||||
idx: u32,
|
|
||||||
axis: ControllerAxis,
|
|
||||||
value: i16,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Window {
|
|
||||||
pub(crate) inner: Box<WindowContext>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WindowSize {
|
|
||||||
pub width: u32,
|
|
||||||
pub height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Backend {
|
|
||||||
Invalid,
|
|
||||||
Vulkan,
|
|
||||||
Metal,
|
|
||||||
D3D12,
|
|
||||||
D3D11,
|
|
||||||
OpenGL,
|
|
||||||
WebGPU,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ElementState {
|
|
||||||
Pressed,
|
|
||||||
Released,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum MouseButton {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Middle,
|
|
||||||
Other,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum SpecialKey {
|
|
||||||
None = 0,
|
|
||||||
F1 = 1,
|
|
||||||
F2 = 2,
|
|
||||||
F3 = 3,
|
|
||||||
F4 = 4,
|
|
||||||
F5 = 5,
|
|
||||||
F6 = 6,
|
|
||||||
F7 = 7,
|
|
||||||
F8 = 8,
|
|
||||||
F9 = 9,
|
|
||||||
F10 = 10,
|
|
||||||
F11 = 11,
|
|
||||||
F12 = 12,
|
|
||||||
F13 = 13,
|
|
||||||
F14 = 14,
|
|
||||||
F15 = 15,
|
|
||||||
F16 = 16,
|
|
||||||
F17 = 17,
|
|
||||||
F18 = 18,
|
|
||||||
F19 = 19,
|
|
||||||
F20 = 20,
|
|
||||||
F21 = 21,
|
|
||||||
F22 = 22,
|
|
||||||
F23 = 23,
|
|
||||||
F24 = 24,
|
|
||||||
Esc = 25,
|
|
||||||
Enter = 26,
|
|
||||||
Backspace = 27,
|
|
||||||
Insert = 28,
|
|
||||||
Delete = 29,
|
|
||||||
Home = 30,
|
|
||||||
End = 31,
|
|
||||||
PgUp = 32,
|
|
||||||
PgDown = 33,
|
|
||||||
Left = 34,
|
|
||||||
Right = 35,
|
|
||||||
Up = 36,
|
|
||||||
Down = 37,
|
|
||||||
Tab = 38,
|
|
||||||
PrintScreen = 39,
|
|
||||||
ScrollLock = 40,
|
|
||||||
Pause = 41,
|
|
||||||
NumLockClear = 42,
|
|
||||||
KpDivide = 43,
|
|
||||||
KpMultiply = 44,
|
|
||||||
KpMinus = 45,
|
|
||||||
KpPlus = 46,
|
|
||||||
KpEnter = 47,
|
|
||||||
KpNum0 = 48,
|
|
||||||
KpNum1 = 49,
|
|
||||||
KpNum2 = 51,
|
|
||||||
KpNum3 = 52,
|
|
||||||
KpNum4 = 53,
|
|
||||||
KpNum5 = 54,
|
|
||||||
KpNum6 = 55,
|
|
||||||
KpNum7 = 56,
|
|
||||||
KpNum8 = 57,
|
|
||||||
KpNum9 = 58,
|
|
||||||
KpPercent = 59,
|
|
||||||
KpPeriod = 60,
|
|
||||||
KpComma = 61,
|
|
||||||
KpEquals = 62,
|
|
||||||
Application = 63,
|
|
||||||
Power = 64,
|
|
||||||
Execute = 65,
|
|
||||||
Help = 66,
|
|
||||||
Menu = 67,
|
|
||||||
Select = 68,
|
|
||||||
Stop = 69,
|
|
||||||
Again = 70,
|
|
||||||
Undo = 71,
|
|
||||||
Cut = 72,
|
|
||||||
Paste = 73,
|
|
||||||
Find = 74,
|
|
||||||
VolumeUp = 75,
|
|
||||||
VolumeDown = 76,
|
|
||||||
MAX,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ControllerButton {
|
|
||||||
A,
|
|
||||||
B,
|
|
||||||
X,
|
|
||||||
Y,
|
|
||||||
Back,
|
|
||||||
Guide,
|
|
||||||
Start,
|
|
||||||
LeftStick,
|
|
||||||
RightStick,
|
|
||||||
LeftShoulder,
|
|
||||||
RightShoulder,
|
|
||||||
DPadUp,
|
|
||||||
DPadDown,
|
|
||||||
DPadLeft,
|
|
||||||
DPadRight,
|
|
||||||
Other,
|
|
||||||
MAX,
|
|
||||||
}
|
|
||||||
pub enum ControllerAxis {
|
|
||||||
LeftX,
|
|
||||||
LeftY,
|
|
||||||
RightX,
|
|
||||||
RightY,
|
|
||||||
TriggerLeft,
|
|
||||||
TriggerRight,
|
|
||||||
MAX,
|
|
||||||
}
|
|
||||||
pub struct Icon {
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
pub width: u32,
|
|
||||||
pub height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Rust" {
|
|
||||||
type WindowContext;
|
|
||||||
type App;
|
|
||||||
fn app_run(mut delegate: UniquePtr<AppDelegate>, icon: Icon);
|
|
||||||
fn get_args() -> Vec<String>;
|
|
||||||
fn get_window_size() -> WindowSize;
|
|
||||||
fn set_window_title(title: &CxxString);
|
|
||||||
fn get_dxt_compression_supported() -> bool;
|
|
||||||
fn get_backend() -> Backend;
|
|
||||||
fn get_backend_string() -> &'static str;
|
|
||||||
fn set_fullscreen(v: bool);
|
|
||||||
fn get_controller_player_index(which: u32) -> i32;
|
|
||||||
fn set_controller_player_index(which: u32, index: i32);
|
|
||||||
fn is_controller_gamecube(which: u32) -> bool;
|
|
||||||
fn get_controller_name(which: u32) -> String;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Button> for ffi::ControllerButton {
|
|
||||||
fn from(button: Button) -> Self {
|
|
||||||
match button {
|
|
||||||
Button::A => ffi::ControllerButton::A,
|
|
||||||
Button::B => ffi::ControllerButton::B,
|
|
||||||
Button::X => ffi::ControllerButton::X,
|
|
||||||
Button::Y => ffi::ControllerButton::Y,
|
|
||||||
Button::Back => ffi::ControllerButton::Back,
|
|
||||||
Button::Guide => ffi::ControllerButton::Guide,
|
|
||||||
Button::Start => ffi::ControllerButton::Start,
|
|
||||||
Button::LeftStick => ffi::ControllerButton::LeftStick,
|
|
||||||
Button::RightStick => ffi::ControllerButton::RightStick,
|
|
||||||
Button::LeftShoulder => ffi::ControllerButton::LeftShoulder,
|
|
||||||
Button::RightShoulder => ffi::ControllerButton::RightShoulder,
|
|
||||||
Button::DPadUp => ffi::ControllerButton::DPadUp,
|
|
||||||
Button::DPadDown => ffi::ControllerButton::DPadDown,
|
|
||||||
Button::DPadLeft => ffi::ControllerButton::DPadLeft,
|
|
||||||
Button::DPadRight => ffi::ControllerButton::DPadRight,
|
|
||||||
_ => ffi::ControllerButton::Other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Axis> for ffi::ControllerAxis {
|
|
||||||
fn from(axis: Axis) -> Self {
|
|
||||||
match axis {
|
|
||||||
Axis::LeftX => ffi::ControllerAxis::LeftX,
|
|
||||||
Axis::LeftY => ffi::ControllerAxis::LeftY,
|
|
||||||
Axis::RightX => ffi::ControllerAxis::RightX,
|
|
||||||
Axis::RightY => ffi::ControllerAxis::RightY,
|
|
||||||
Axis::TriggerLeft => ffi::ControllerAxis::TriggerLeft,
|
|
||||||
Axis::TriggerRight => ffi::ControllerAxis::TriggerRight,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,194 +0,0 @@
|
||||||
use std::{num::NonZeroU8, sync::Arc};
|
|
||||||
|
|
||||||
use pollster::block_on;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct GraphicsConfig {
|
|
||||||
pub(crate) color_format: wgpu::TextureFormat,
|
|
||||||
pub(crate) depth_format: wgpu::TextureFormat,
|
|
||||||
pub(crate) texture_anistropy: Option<NonZeroU8>,
|
|
||||||
pub(crate) msaa_samples: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DeviceHolder {
|
|
||||||
pub(crate) instance: wgpu::Instance,
|
|
||||||
pub(crate) surface: wgpu::Surface,
|
|
||||||
pub(crate) adapter: wgpu::Adapter,
|
|
||||||
pub(crate) backend: wgpu::Backend,
|
|
||||||
pub(crate) device: Arc<wgpu::Device>,
|
|
||||||
pub(crate) queue: Arc<wgpu::Queue>,
|
|
||||||
pub(crate) config: GraphicsConfig,
|
|
||||||
pub(crate) surface_config: wgpu::SurfaceConfiguration,
|
|
||||||
pub(crate) framebuffer: TextureWithSampler,
|
|
||||||
pub(crate) depth: TextureWithSampler,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct TextureWithSampler {
|
|
||||||
pub(crate) texture: wgpu::Texture,
|
|
||||||
pub(crate) view: wgpu::TextureView,
|
|
||||||
pub(crate) sampler: wgpu::Sampler,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_render_texture(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
config: &wgpu::SurfaceConfiguration,
|
|
||||||
graphics_config: &GraphicsConfig,
|
|
||||||
) -> TextureWithSampler {
|
|
||||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: Some("Render texture"),
|
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: config.width,
|
|
||||||
height: config.height,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: graphics_config.msaa_samples,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: graphics_config.color_format,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
});
|
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
|
||||||
label: Some("Render sampler"),
|
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
|
||||||
mipmap_filter: wgpu::FilterMode::Linear,
|
|
||||||
lod_min_clamp: 0.0,
|
|
||||||
lod_max_clamp: f32::MAX,
|
|
||||||
compare: None,
|
|
||||||
anisotropy_clamp: None,
|
|
||||||
border_color: None,
|
|
||||||
});
|
|
||||||
TextureWithSampler { texture, view, sampler }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_depth_texture(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
config: &wgpu::SurfaceConfiguration,
|
|
||||||
graphics_config: &GraphicsConfig,
|
|
||||||
) -> TextureWithSampler {
|
|
||||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: Some("Depth texture"),
|
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: config.width,
|
|
||||||
height: config.height,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: graphics_config.msaa_samples,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: graphics_config.depth_format,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
});
|
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
|
||||||
label: Some("Depth sampler"),
|
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
|
||||||
mipmap_filter: wgpu::FilterMode::Linear,
|
|
||||||
lod_min_clamp: 0.0,
|
|
||||||
lod_max_clamp: f32::MAX,
|
|
||||||
compare: None,
|
|
||||||
anisotropy_clamp: None,
|
|
||||||
border_color: None,
|
|
||||||
});
|
|
||||||
TextureWithSampler { texture, view, sampler }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn initialize_gpu(window: &winit::window::Window) -> DeviceHolder {
|
|
||||||
log::info!("Creating wgpu instance");
|
|
||||||
let instance = wgpu::Instance::new(wgpu::Backends::all());
|
|
||||||
let surface = unsafe { instance.create_surface(window) };
|
|
||||||
let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
|
||||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
|
||||||
compatible_surface: Some(&surface),
|
|
||||||
force_fallback_adapter: false,
|
|
||||||
}))
|
|
||||||
.expect("Failed to find an appropriate adapter");
|
|
||||||
let best_limits = adapter.limits();
|
|
||||||
let backend = adapter.get_info().backend;
|
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
|
||||||
let needs_denorm = true;
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
let _needs_denorm = backend == wgpu::Backend::Gl;
|
|
||||||
|
|
||||||
let required_features = if backend == wgpu::Backend::Vulkan {
|
|
||||||
wgpu::Features::SPIRV_SHADER_PASSTHROUGH
|
|
||||||
} else {
|
|
||||||
wgpu::Features::empty()
|
|
||||||
};
|
|
||||||
let optional_features = wgpu::Features::TEXTURE_COMPRESSION_BC
|
|
||||||
| wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER
|
|
||||||
| required_features;
|
|
||||||
log::info!("Requesting device with features: {:?}", optional_features);
|
|
||||||
let limits = wgpu::Limits::downlevel_defaults()
|
|
||||||
.using_resolution(best_limits.clone())
|
|
||||||
.using_alignment(best_limits);
|
|
||||||
let (device, queue) = block_on(adapter.request_device(
|
|
||||||
&wgpu::DeviceDescriptor {
|
|
||||||
label: Some("Device with optional features"),
|
|
||||||
features: optional_features,
|
|
||||||
limits: limits.clone(),
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
log::warn!("Failed... requesting device with no features");
|
|
||||||
block_on(adapter.request_device(
|
|
||||||
&wgpu::DeviceDescriptor {
|
|
||||||
label: Some("Device without optional features"),
|
|
||||||
features: required_features,
|
|
||||||
limits,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.expect("Failed to create device")
|
|
||||||
});
|
|
||||||
log::info!("Successfully created device");
|
|
||||||
|
|
||||||
let mut swapchain_format = surface.get_preferred_format(&adapter).unwrap();
|
|
||||||
if backend != wgpu::Backend::Gl {
|
|
||||||
swapchain_format = match swapchain_format {
|
|
||||||
wgpu::TextureFormat::Rgba8UnormSrgb => wgpu::TextureFormat::Rgba8Unorm,
|
|
||||||
wgpu::TextureFormat::Bgra8UnormSrgb => wgpu::TextureFormat::Bgra8Unorm,
|
|
||||||
_ => swapchain_format,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let graphics_config = GraphicsConfig {
|
|
||||||
color_format: swapchain_format,
|
|
||||||
depth_format: wgpu::TextureFormat::Depth32Float,
|
|
||||||
texture_anistropy: NonZeroU8::new(16),
|
|
||||||
msaa_samples: 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
let size = window.inner_size();
|
|
||||||
let surface_config = wgpu::SurfaceConfiguration {
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
format: swapchain_format,
|
|
||||||
width: size.width,
|
|
||||||
height: size.height,
|
|
||||||
present_mode: wgpu::PresentMode::Fifo,
|
|
||||||
};
|
|
||||||
surface.configure(&device, &surface_config);
|
|
||||||
let depth = create_depth_texture(&device, &surface_config, &graphics_config);
|
|
||||||
let framebuffer = create_render_texture(&device, &surface_config, &graphics_config);
|
|
||||||
|
|
||||||
DeviceHolder {
|
|
||||||
instance,
|
|
||||||
surface,
|
|
||||||
adapter,
|
|
||||||
backend,
|
|
||||||
device: Arc::new(device),
|
|
||||||
queue: Arc::new(queue),
|
|
||||||
config: graphics_config,
|
|
||||||
surface_config,
|
|
||||||
framebuffer,
|
|
||||||
depth,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
use crate::{
|
|
||||||
gpu::DeviceHolder,
|
|
||||||
imgui::{ImGuiEngine_AddTexture, ImGuiState},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cxx::bridge(namespace = "metaforce")]
|
|
||||||
pub(crate) mod ffi {
|
|
||||||
unsafe extern "C++" {
|
|
||||||
include!("ImGuiEngine.hpp");
|
|
||||||
pub(crate) fn ImGuiEngine_Initialize(scale: f32);
|
|
||||||
pub(crate) fn ImGuiEngine_AddTextures(state: &mut ImGuiState, device: &DeviceHolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Rust" {
|
|
||||||
type ImGuiState;
|
|
||||||
type DeviceHolder;
|
|
||||||
fn ImGuiEngine_AddTexture(
|
|
||||||
state: &mut ImGuiState,
|
|
||||||
device: &DeviceHolder,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
data: &[u8],
|
|
||||||
) -> usize;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
use cxxbridge::ffi;
|
|
||||||
|
|
||||||
use crate::{gpu::DeviceHolder, imgui_backend};
|
|
||||||
|
|
||||||
mod cxxbridge;
|
|
||||||
|
|
||||||
pub struct ImGuiState {
|
|
||||||
pub(crate) context: imgui::Context,
|
|
||||||
pub(crate) platform: imgui_winit_support::WinitPlatform,
|
|
||||||
pub(crate) backend: imgui_backend::Renderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn initialize_imgui(window: &winit::window::Window, gpu: &DeviceHolder) -> ImGuiState {
|
|
||||||
let hidpi_factor = window.scale_factor();
|
|
||||||
let mut imgui = imgui::Context::create();
|
|
||||||
let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui);
|
|
||||||
platform.attach_window(imgui.io_mut(), &window, imgui_winit_support::HiDpiMode::Default);
|
|
||||||
imgui.set_ini_filename(None);
|
|
||||||
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ffi::ImGuiEngine_Initialize(hidpi_factor as f32);
|
|
||||||
}
|
|
||||||
|
|
||||||
let renderer_config = imgui_backend::RendererConfig::new_srgb();
|
|
||||||
|
|
||||||
let imgui_backend = imgui_backend::Renderer::new(
|
|
||||||
&mut imgui,
|
|
||||||
&gpu.device,
|
|
||||||
&gpu.queue,
|
|
||||||
imgui_backend::RendererConfig {
|
|
||||||
texture_format: gpu.config.color_format,
|
|
||||||
depth_format: Some(gpu.config.depth_format),
|
|
||||||
sample_count: gpu.config.msaa_samples,
|
|
||||||
..renderer_config
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let mut state = ImGuiState { context: imgui, platform, backend: imgui_backend };
|
|
||||||
unsafe {
|
|
||||||
ffi::ImGuiEngine_AddTextures(&mut state, &gpu);
|
|
||||||
}
|
|
||||||
state
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn ImGuiEngine_AddTexture(
|
|
||||||
state: &mut ImGuiState,
|
|
||||||
gpu: &DeviceHolder,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
data: &[u8],
|
|
||||||
) -> usize {
|
|
||||||
let texture =
|
|
||||||
imgui_backend::Texture::new(&gpu.device, &state.backend, imgui_backend::TextureConfig {
|
|
||||||
size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
|
|
||||||
format: Some(wgpu::TextureFormat::Rgba8Unorm),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
texture.write(&gpu.queue, data, width, height);
|
|
||||||
state.backend.textures.insert(texture).id()
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
u_Matrix: mat4x4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) a_Pos: vec2<f32>;
|
|
||||||
@location(1) a_UV: vec2<f32>;
|
|
||||||
@location(2) a_Color: vec4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@location(0) v_UV: vec2<f32>;
|
|
||||||
@location(1) v_Color: vec4<f32>;
|
|
||||||
@builtin(position) v_Position: vec4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<uniform> uniforms: Uniforms;
|
|
||||||
|
|
||||||
@stage(vertex)
|
|
||||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.v_UV = in.a_UV;
|
|
||||||
out.v_Color = in.a_Color;
|
|
||||||
out.v_Position = uniforms.u_Matrix * vec4<f32>(in.a_Pos.xy, 0.0, 1.0);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var u_Texture: texture_2d<f32>;
|
|
||||||
@group(1) @binding(1)
|
|
||||||
var u_Sampler: sampler;
|
|
||||||
|
|
||||||
fn srgb_to_linear(srgb: vec4<f32>) -> vec4<f32> {
|
|
||||||
let color_srgb = srgb.rgb;
|
|
||||||
let selector = ceil(color_srgb - 0.04045); // 0 if under value, 1 if over
|
|
||||||
let under = color_srgb / 12.92;
|
|
||||||
let over = pow((color_srgb + 0.055) / 1.055, vec3<f32>(2.4));
|
|
||||||
let result = mix(under, over, selector);
|
|
||||||
return vec4<f32>(result, srgb.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(fragment)
|
|
||||||
fn fs_main_linear(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
let color = srgb_to_linear(in.v_Color);
|
|
||||||
return color * textureSample(u_Texture, u_Sampler, in.v_UV);
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(fragment)
|
|
||||||
fn fs_main_srgb(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
let color = in.v_Color;
|
|
||||||
return color * textureSample(u_Texture, u_Sampler, in.v_UV);
|
|
||||||
}
|
|
|
@ -1,637 +0,0 @@
|
||||||
use std::{borrow::BorrowMut, error::Error, fmt, mem::size_of, num::NonZeroU32};
|
|
||||||
|
|
||||||
use imgui::{
|
|
||||||
Context, DrawCmd::Elements, DrawData, DrawIdx, DrawList, DrawVert, TextureId, Textures,
|
|
||||||
};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use wgpu::{
|
|
||||||
util::{BufferInitDescriptor, DeviceExt},
|
|
||||||
*,
|
|
||||||
};
|
|
||||||
|
|
||||||
static VS_ENTRY_POINT: &str = "vs_main";
|
|
||||||
static FS_ENTRY_POINT_LINEAR: &str = "fs_main_linear";
|
|
||||||
static FS_ENTRY_POINT_SRGB: &str = "fs_main_srgb";
|
|
||||||
|
|
||||||
pub type RendererResult<T> = Result<T, RendererError>;
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct DrawVertPod(DrawVert);
|
|
||||||
|
|
||||||
unsafe impl bytemuck::Zeroable for DrawVertPod {}
|
|
||||||
|
|
||||||
unsafe impl bytemuck::Pod for DrawVertPod {}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum RendererError {
|
|
||||||
BadTexture(TextureId),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for RendererError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
RendererError::BadTexture(id) => {
|
|
||||||
write!(f, "imgui render error: bad texture id '{}'", id.id())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for RendererError {}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
enum ShaderStage {
|
|
||||||
Vertex,
|
|
||||||
Fragment,
|
|
||||||
Compute,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Config for creating a texture.
|
|
||||||
///
|
|
||||||
/// Uses the builder pattern.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TextureConfig<'a> {
|
|
||||||
/// The size of the texture.
|
|
||||||
pub size: Extent3d,
|
|
||||||
/// An optional label for the texture used for debugging.
|
|
||||||
pub label: Option<&'a str>,
|
|
||||||
/// The format of the texture, if not set uses the format from the renderer.
|
|
||||||
pub format: Option<TextureFormat>,
|
|
||||||
/// The usage of the texture.
|
|
||||||
pub usage: TextureUsages,
|
|
||||||
/// The mip level of the texture.
|
|
||||||
pub mip_level_count: u32,
|
|
||||||
/// The sample count of the texture.
|
|
||||||
pub sample_count: u32,
|
|
||||||
/// The dimension of the texture.
|
|
||||||
pub dimension: TextureDimension,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Default for TextureConfig<'a> {
|
|
||||||
/// Create a new texture config.
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
size: Extent3d { width: 0, height: 0, depth_or_array_layers: 1 },
|
|
||||||
label: None,
|
|
||||||
format: None,
|
|
||||||
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: TextureDimension::D2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A container for a bindable texture.
|
|
||||||
pub struct Texture {
|
|
||||||
texture: wgpu::Texture,
|
|
||||||
view: wgpu::TextureView,
|
|
||||||
bind_group: BindGroup,
|
|
||||||
size: Extent3d,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texture {
|
|
||||||
/// Create a `Texture` from its raw parts.
|
|
||||||
pub fn from_raw_parts(
|
|
||||||
texture: wgpu::Texture,
|
|
||||||
view: wgpu::TextureView,
|
|
||||||
bind_group: BindGroup,
|
|
||||||
size: Extent3d,
|
|
||||||
) -> Self {
|
|
||||||
Self { texture, view, bind_group, size }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new GPU texture width the specified `config`.
|
|
||||||
pub fn new(device: &Device, renderer: &Renderer, config: TextureConfig) -> Self {
|
|
||||||
// Create the wgpu texture.
|
|
||||||
let texture = device.create_texture(&TextureDescriptor {
|
|
||||||
label: config.label,
|
|
||||||
size: config.size,
|
|
||||||
mip_level_count: config.mip_level_count,
|
|
||||||
sample_count: config.sample_count,
|
|
||||||
dimension: config.dimension,
|
|
||||||
format: config.format.unwrap_or(renderer.config.texture_format),
|
|
||||||
usage: config.usage,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Extract the texture view.
|
|
||||||
let view = texture.create_view(&TextureViewDescriptor::default());
|
|
||||||
|
|
||||||
// Create the texture sampler.
|
|
||||||
let sampler = device.create_sampler(&SamplerDescriptor {
|
|
||||||
label: Some("imgui-wgpu sampler"),
|
|
||||||
address_mode_u: AddressMode::ClampToEdge,
|
|
||||||
address_mode_v: AddressMode::ClampToEdge,
|
|
||||||
address_mode_w: AddressMode::ClampToEdge,
|
|
||||||
mag_filter: FilterMode::Linear,
|
|
||||||
min_filter: FilterMode::Linear,
|
|
||||||
mipmap_filter: FilterMode::Linear,
|
|
||||||
lod_min_clamp: 0.0,
|
|
||||||
lod_max_clamp: f32::MAX,
|
|
||||||
compare: None,
|
|
||||||
anisotropy_clamp: None,
|
|
||||||
border_color: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the texture bind group from the layout.
|
|
||||||
let bind_group = device.create_bind_group(&BindGroupDescriptor {
|
|
||||||
label: config.label,
|
|
||||||
layout: &renderer.texture_layout,
|
|
||||||
entries: &[
|
|
||||||
BindGroupEntry { binding: 0, resource: BindingResource::TextureView(&view) },
|
|
||||||
BindGroupEntry { binding: 1, resource: BindingResource::Sampler(&sampler) },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
Self { texture, view, bind_group, size: config.size }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write `data` to the texture.
|
|
||||||
///
|
|
||||||
/// - `data`: 32-bit RGBA bitmap data.
|
|
||||||
/// - `width`: The width of the source bitmap (`data`) in pixels.
|
|
||||||
/// - `height`: The height of the source bitmap (`data`) in pixels.
|
|
||||||
pub fn write(&self, queue: &Queue, data: &[u8], width: u32, height: u32) {
|
|
||||||
queue.write_texture(
|
|
||||||
// destination (sub)texture
|
|
||||||
ImageCopyTexture {
|
|
||||||
texture: &self.texture,
|
|
||||||
mip_level: 0,
|
|
||||||
origin: Origin3d { x: 0, y: 0, z: 0 },
|
|
||||||
aspect: TextureAspect::All,
|
|
||||||
},
|
|
||||||
// source bitmap data
|
|
||||||
data,
|
|
||||||
// layout of the source bitmap
|
|
||||||
ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: NonZeroU32::new(width * 4),
|
|
||||||
rows_per_image: NonZeroU32::new(height),
|
|
||||||
},
|
|
||||||
// size of the source bitmap
|
|
||||||
Extent3d { width, height, depth_or_array_layers: 1 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The width of the texture in pixels.
|
|
||||||
pub fn width(&self) -> u32 { self.size.width }
|
|
||||||
|
|
||||||
/// The height of the texture in pixels.
|
|
||||||
pub fn height(&self) -> u32 { self.size.height }
|
|
||||||
|
|
||||||
/// The depth of the texture.
|
|
||||||
pub fn depth(&self) -> u32 { self.size.depth_or_array_layers }
|
|
||||||
|
|
||||||
/// The size of the texture in pixels.
|
|
||||||
pub fn size(&self) -> Extent3d { self.size }
|
|
||||||
|
|
||||||
/// The underlying `wgpu::Texture`.
|
|
||||||
pub fn texture(&self) -> &wgpu::Texture { &self.texture }
|
|
||||||
|
|
||||||
/// The `wgpu::TextureView` of the underlying texture.
|
|
||||||
pub fn view(&self) -> &wgpu::TextureView { &self.view }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CombinedShader<'s> {
|
|
||||||
pub shader: ShaderModuleDescriptor<'s>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SeparateShaders<'s> {
|
|
||||||
pub vertex_shader: ShaderModuleDescriptorSpirV<'s>,
|
|
||||||
pub fragment_shader: ShaderModuleDescriptorSpirV<'s>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ShaderType<'s> {
|
|
||||||
None,
|
|
||||||
Combined(CombinedShader<'s>),
|
|
||||||
Separate(SeparateShaders<'s>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configuration for the renderer.
|
|
||||||
pub struct RendererConfig<'s> {
|
|
||||||
pub texture_format: TextureFormat,
|
|
||||||
pub depth_format: Option<TextureFormat>,
|
|
||||||
pub sample_count: u32,
|
|
||||||
pub shader: ShaderType<'s>,
|
|
||||||
pub vertex_shader_entry_point: String,
|
|
||||||
pub fragment_shader_entry_point: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RendererConfig<'_> {
|
|
||||||
/// Create a new renderer config with custom shaders.
|
|
||||||
pub fn with_combined_shaders<'s>(
|
|
||||||
shader: ShaderModuleDescriptor<'s>,
|
|
||||||
fragment_shader_entry_point: &str,
|
|
||||||
) -> RendererConfig<'s> {
|
|
||||||
RendererConfig {
|
|
||||||
texture_format: TextureFormat::Rgba8Unorm,
|
|
||||||
depth_format: None,
|
|
||||||
sample_count: 1,
|
|
||||||
shader: ShaderType::Combined(CombinedShader { shader }),
|
|
||||||
vertex_shader_entry_point: VS_ENTRY_POINT.to_string(),
|
|
||||||
fragment_shader_entry_point: fragment_shader_entry_point.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new renderer config with custom shaders.
|
|
||||||
pub fn with_separate_shaders<'s>(
|
|
||||||
vertex_shader: ShaderModuleDescriptorSpirV<'s>,
|
|
||||||
fragment_shader: ShaderModuleDescriptorSpirV<'s>,
|
|
||||||
fragment_shader_entry_point: &str,
|
|
||||||
) -> RendererConfig<'s> {
|
|
||||||
RendererConfig {
|
|
||||||
texture_format: TextureFormat::Rgba8Unorm,
|
|
||||||
depth_format: None,
|
|
||||||
sample_count: 1,
|
|
||||||
shader: ShaderType::Separate(SeparateShaders { vertex_shader, fragment_shader }),
|
|
||||||
vertex_shader_entry_point: VS_ENTRY_POINT.to_string(),
|
|
||||||
fragment_shader_entry_point: fragment_shader_entry_point.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RendererConfig<'_> {
|
|
||||||
/// Create a new renderer config with precompiled default shaders outputting linear color.
|
|
||||||
///
|
|
||||||
/// If you write to a Bgra8UnormSrgb framebuffer, this is what you want.
|
|
||||||
fn default() -> Self { Self::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RendererConfig<'_> {
|
|
||||||
/// Create a new renderer config with precompiled default shaders outputting linear color.
|
|
||||||
///
|
|
||||||
/// If you write to a Bgra8UnormSrgb framebuffer, this is what you want.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::with_combined_shaders(include_wgsl!("imgui.wgsl"), FS_ENTRY_POINT_LINEAR)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new renderer config with precompiled default shaders outputting srgb color.
|
|
||||||
///
|
|
||||||
/// If you write to a Bgra8Unorm framebuffer, this is what you want.
|
|
||||||
pub fn new_srgb() -> Self {
|
|
||||||
Self::with_combined_shaders(include_wgsl!("imgui.wgsl"), FS_ENTRY_POINT_SRGB)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Renderer {
|
|
||||||
pipeline: RenderPipeline,
|
|
||||||
uniform_buffer: Buffer,
|
|
||||||
uniform_bind_group: BindGroup,
|
|
||||||
/// Textures of the font atlas and all images.
|
|
||||||
pub textures: Textures<Texture>,
|
|
||||||
texture_layout: BindGroupLayout,
|
|
||||||
index_buffers: SmallVec<[Buffer; 4]>,
|
|
||||||
vertex_buffers: SmallVec<[Buffer; 4]>,
|
|
||||||
config: RendererConfig<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Renderer {
|
|
||||||
/// Create an entirely new imgui wgpu renderer.
|
|
||||||
pub fn new(
|
|
||||||
imgui: &mut Context,
|
|
||||||
device: &Device,
|
|
||||||
queue: &Queue,
|
|
||||||
config: RendererConfig,
|
|
||||||
) -> Self {
|
|
||||||
let RendererConfig {
|
|
||||||
texture_format,
|
|
||||||
depth_format,
|
|
||||||
sample_count,
|
|
||||||
shader,
|
|
||||||
vertex_shader_entry_point,
|
|
||||||
fragment_shader_entry_point,
|
|
||||||
} = config;
|
|
||||||
|
|
||||||
// Load shaders.
|
|
||||||
let shader1: Option<wgpu::ShaderModule>;
|
|
||||||
let shader2: Option<wgpu::ShaderModule>;
|
|
||||||
let (vs, fs) = match shader {
|
|
||||||
ShaderType::Combined(d) => {
|
|
||||||
shader1 = Some(device.create_shader_module(&d.shader));
|
|
||||||
let r = shader1.as_ref().unwrap();
|
|
||||||
(r, r)
|
|
||||||
}
|
|
||||||
ShaderType::Separate(d) => unsafe {
|
|
||||||
shader1 = Some(device.create_shader_module_spirv(&d.vertex_shader));
|
|
||||||
shader2 = Some(device.create_shader_module_spirv(&d.fragment_shader));
|
|
||||||
(shader1.as_ref().unwrap(), shader2.as_ref().unwrap())
|
|
||||||
},
|
|
||||||
ShaderType::None => panic!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the uniform matrix buffer.
|
|
||||||
let size = 64;
|
|
||||||
let uniform_buffer = device.create_buffer(&BufferDescriptor {
|
|
||||||
label: Some("imgui-wgpu uniform buffer"),
|
|
||||||
size,
|
|
||||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the uniform matrix buffer bind group layout.
|
|
||||||
let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
entries: &[BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX,
|
|
||||||
ty: BindingType::Buffer {
|
|
||||||
ty: BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: None,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the uniform matrix buffer bind group.
|
|
||||||
let uniform_bind_group = device.create_bind_group(&BindGroupDescriptor {
|
|
||||||
label: Some("imgui-wgpu bind group"),
|
|
||||||
layout: &uniform_layout,
|
|
||||||
entries: &[BindGroupEntry { binding: 0, resource: uniform_buffer.as_entire_binding() }],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the texture layout for further usage.
|
|
||||||
let texture_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
|
||||||
label: Some("imgui-wgpu bind group layout"),
|
|
||||||
entries: &[
|
|
||||||
BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: BindingType::Texture {
|
|
||||||
multisampled: false,
|
|
||||||
sample_type: TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: TextureViewDimension::D2,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the render pipeline layout.
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
|
||||||
label: Some("imgui-wgpu pipeline layout"),
|
|
||||||
bind_group_layouts: &[&uniform_layout, &texture_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the render pipeline.
|
|
||||||
// Create the render pipeline.
|
|
||||||
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
|
|
||||||
label: Some("imgui-wgpu pipeline"),
|
|
||||||
layout: Some(&pipeline_layout),
|
|
||||||
vertex: VertexState {
|
|
||||||
module: vs,
|
|
||||||
entry_point: &vertex_shader_entry_point,
|
|
||||||
buffers: &[VertexBufferLayout {
|
|
||||||
array_stride: size_of::<DrawVert>() as BufferAddress,
|
|
||||||
step_mode: VertexStepMode::Vertex,
|
|
||||||
attributes: &vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Unorm8x4],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
primitive: PrimitiveState {
|
|
||||||
topology: PrimitiveTopology::TriangleList,
|
|
||||||
strip_index_format: None,
|
|
||||||
front_face: FrontFace::Cw,
|
|
||||||
cull_mode: None,
|
|
||||||
polygon_mode: PolygonMode::Fill,
|
|
||||||
unclipped_depth: false,
|
|
||||||
conservative: false,
|
|
||||||
},
|
|
||||||
depth_stencil: depth_format.map(|format| wgpu::DepthStencilState {
|
|
||||||
format,
|
|
||||||
depth_write_enabled: false,
|
|
||||||
depth_compare: wgpu::CompareFunction::Always,
|
|
||||||
stencil: wgpu::StencilState::default(),
|
|
||||||
bias: DepthBiasState::default(),
|
|
||||||
}),
|
|
||||||
multisample: MultisampleState { count: sample_count, ..Default::default() },
|
|
||||||
fragment: Some(FragmentState {
|
|
||||||
module: fs,
|
|
||||||
entry_point: &fragment_shader_entry_point,
|
|
||||||
targets: &[ColorTargetState {
|
|
||||||
format: texture_format,
|
|
||||||
blend: Some(BlendState {
|
|
||||||
color: BlendComponent {
|
|
||||||
src_factor: BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: BlendFactor::OneMinusSrcAlpha,
|
|
||||||
operation: BlendOperation::Add,
|
|
||||||
},
|
|
||||||
alpha: BlendComponent {
|
|
||||||
src_factor: BlendFactor::OneMinusDstAlpha,
|
|
||||||
dst_factor: BlendFactor::One,
|
|
||||||
operation: BlendOperation::Add,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
write_mask: ColorWrites::ALL,
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
multiview: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut renderer = Self {
|
|
||||||
pipeline,
|
|
||||||
uniform_buffer,
|
|
||||||
uniform_bind_group,
|
|
||||||
textures: Textures::new(),
|
|
||||||
texture_layout,
|
|
||||||
vertex_buffers: SmallVec::new(),
|
|
||||||
index_buffers: SmallVec::new(),
|
|
||||||
config: RendererConfig {
|
|
||||||
texture_format,
|
|
||||||
depth_format,
|
|
||||||
sample_count,
|
|
||||||
shader: ShaderType::None,
|
|
||||||
vertex_shader_entry_point,
|
|
||||||
fragment_shader_entry_point,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Immediately load the font texture to the GPU.
|
|
||||||
renderer.reload_font_texture(imgui, device, queue);
|
|
||||||
|
|
||||||
renderer
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render the current imgui frame.
|
|
||||||
pub fn render<'r>(
|
|
||||||
&'r mut self,
|
|
||||||
draw_data: &DrawData,
|
|
||||||
queue: &Queue,
|
|
||||||
device: &Device,
|
|
||||||
rpass: &mut RenderPass<'r>,
|
|
||||||
) -> RendererResult<()> {
|
|
||||||
rpass.push_debug_group("imgui-wgpu");
|
|
||||||
let mut rpass = scopeguard::guard(rpass, |rpass| {
|
|
||||||
rpass.pop_debug_group();
|
|
||||||
});
|
|
||||||
|
|
||||||
let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
|
|
||||||
let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
|
|
||||||
|
|
||||||
// If the render area is <= 0, exit here and now.
|
|
||||||
if !(fb_width > 0.0 && fb_height > 0.0) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let width = draw_data.display_size[0];
|
|
||||||
let height = draw_data.display_size[1];
|
|
||||||
|
|
||||||
let offset_x = draw_data.display_pos[0] / width;
|
|
||||||
let offset_y = draw_data.display_pos[1] / height;
|
|
||||||
|
|
||||||
// Create and update the transform matrix for the current frame.
|
|
||||||
// This is required to adapt to vulkan coordinates.
|
|
||||||
// let matrix = [
|
|
||||||
// [2.0 / width, 0.0, 0.0, 0.0],
|
|
||||||
// [0.0, 2.0 / height as f32, 0.0, 0.0],
|
|
||||||
// [0.0, 0.0, -1.0, 0.0],
|
|
||||||
// [-1.0, -1.0, 0.0, 1.0],
|
|
||||||
// ];
|
|
||||||
let matrix = [
|
|
||||||
[2.0 / width, 0.0, 0.0, 0.0],
|
|
||||||
[0.0, 2.0 / -height as f32, 0.0, 0.0],
|
|
||||||
[0.0, 0.0, 1.0, 0.0],
|
|
||||||
[-1.0 - offset_x * 2.0, 1.0 + offset_y * 2.0, 0.0, 1.0],
|
|
||||||
];
|
|
||||||
self.update_uniform_buffer(queue, &matrix);
|
|
||||||
|
|
||||||
rpass.set_pipeline(&self.pipeline);
|
|
||||||
rpass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
|
||||||
|
|
||||||
self.vertex_buffers.clear();
|
|
||||||
self.index_buffers.clear();
|
|
||||||
|
|
||||||
for draw_list in draw_data.draw_lists() {
|
|
||||||
self.vertex_buffers.push(self.upload_vertex_buffer(device, draw_list.vtx_buffer()));
|
|
||||||
self.index_buffers.push(self.upload_index_buffer(device, draw_list.idx_buffer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute all the imgui render work.
|
|
||||||
for (draw_list_buffers_index, draw_list) in draw_data.draw_lists().enumerate() {
|
|
||||||
self.render_draw_list(
|
|
||||||
rpass.borrow_mut(),
|
|
||||||
draw_list,
|
|
||||||
draw_data.display_pos,
|
|
||||||
draw_data.framebuffer_scale,
|
|
||||||
draw_list_buffers_index,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render a given `DrawList` from imgui onto a wgpu frame.
|
|
||||||
fn render_draw_list<'render>(
|
|
||||||
&'render self,
|
|
||||||
rpass: &mut RenderPass<'render>,
|
|
||||||
draw_list: &DrawList,
|
|
||||||
clip_off: [f32; 2],
|
|
||||||
clip_scale: [f32; 2],
|
|
||||||
draw_list_buffers_index: usize,
|
|
||||||
) -> RendererResult<()> {
|
|
||||||
let mut start = 0;
|
|
||||||
|
|
||||||
let index_buffer = &self.index_buffers[draw_list_buffers_index];
|
|
||||||
let vertex_buffer = &self.vertex_buffers[draw_list_buffers_index];
|
|
||||||
|
|
||||||
// Make sure the current buffers are attached to the render pass.
|
|
||||||
rpass.set_index_buffer(index_buffer.slice(..), IndexFormat::Uint16);
|
|
||||||
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
|
|
||||||
|
|
||||||
for cmd in draw_list.commands() {
|
|
||||||
if let Elements { count, cmd_params } = cmd {
|
|
||||||
let clip_rect = [
|
|
||||||
(cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0],
|
|
||||||
(cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1],
|
|
||||||
(cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0],
|
|
||||||
(cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1],
|
|
||||||
];
|
|
||||||
|
|
||||||
// Set the current texture bind group on the renderpass.
|
|
||||||
let texture_id = cmd_params.texture_id;
|
|
||||||
let tex =
|
|
||||||
self.textures.get(texture_id).ok_or(RendererError::BadTexture(texture_id))?;
|
|
||||||
rpass.set_bind_group(1, &tex.bind_group, &[]);
|
|
||||||
|
|
||||||
// Set scissors on the renderpass.
|
|
||||||
let scissors = (
|
|
||||||
clip_rect[0].max(0.0).floor() as u32,
|
|
||||||
clip_rect[1].max(0.0).floor() as u32,
|
|
||||||
(clip_rect[2] - clip_rect[0]).abs().ceil() as u32,
|
|
||||||
(clip_rect[3] - clip_rect[1]).abs().ceil() as u32,
|
|
||||||
);
|
|
||||||
rpass.set_scissor_rect(scissors.0, scissors.1, scissors.2, scissors.3);
|
|
||||||
|
|
||||||
// Draw the current batch of vertices with the renderpass.
|
|
||||||
let end = start + count as u32;
|
|
||||||
rpass.draw_indexed(start..end, 0, 0..1);
|
|
||||||
start = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the current uniform buffer containing the transform matrix.
|
|
||||||
fn update_uniform_buffer(&mut self, queue: &Queue, matrix: &[[f32; 4]; 4]) {
|
|
||||||
let data = bytemuck::bytes_of(matrix);
|
|
||||||
|
|
||||||
queue.write_buffer(&self.uniform_buffer, 0, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Upload the vertex buffer to the GPU.
|
|
||||||
fn upload_vertex_buffer(&self, device: &Device, vertices: &[DrawVert]) -> Buffer {
|
|
||||||
// Safety: DrawVertPod is #[repr(transparent)] over DrawVert and DrawVert _should_ be Pod.
|
|
||||||
let vertices = unsafe {
|
|
||||||
std::slice::from_raw_parts(vertices.as_ptr() as *mut DrawVertPod, vertices.len())
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = bytemuck::cast_slice(vertices);
|
|
||||||
device.create_buffer_init(&BufferInitDescriptor {
|
|
||||||
label: Some("imgui-wgpu vertex buffer"),
|
|
||||||
contents: data,
|
|
||||||
usage: BufferUsages::VERTEX,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Upload the index buffer to the GPU.
|
|
||||||
fn upload_index_buffer(&self, device: &Device, indices: &[DrawIdx]) -> Buffer {
|
|
||||||
let data = bytemuck::cast_slice(indices);
|
|
||||||
|
|
||||||
device.create_buffer_init(&BufferInitDescriptor {
|
|
||||||
label: Some("imgui-wgpu index buffer"),
|
|
||||||
contents: data,
|
|
||||||
usage: BufferUsages::INDEX,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the texture on the GPU corresponding to the current imgui font atlas.
|
|
||||||
///
|
|
||||||
/// This has to be called after loading a font.
|
|
||||||
pub fn reload_font_texture(&mut self, imgui: &mut Context, device: &Device, queue: &Queue) {
|
|
||||||
let mut fonts = imgui.fonts();
|
|
||||||
// Remove possible font atlas texture.
|
|
||||||
self.textures.remove(fonts.tex_id);
|
|
||||||
|
|
||||||
// Create font texture and upload it.
|
|
||||||
let handle = fonts.build_rgba32_texture();
|
|
||||||
let font_texture_cnfig = TextureConfig {
|
|
||||||
label: Some("imgui-wgpu font atlas"),
|
|
||||||
size: Extent3d { width: handle.width, height: handle.height, ..Default::default() },
|
|
||||||
format: Some(wgpu::TextureFormat::Rgba8Unorm),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let font_texture = Texture::new(device, self, font_texture_cnfig);
|
|
||||||
font_texture.write(queue, handle.data, handle.width, handle.height);
|
|
||||||
fonts.tex_id = self.textures.insert(font_texture);
|
|
||||||
// Clear imgui texture data to save memory.
|
|
||||||
fonts.clear_tex_data();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,286 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
#![allow(unused_unsafe)]
|
|
||||||
#![allow(non_upper_case_globals)]
|
|
||||||
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use cxxbridge::ffi;
|
|
||||||
use wgpu::Backend;
|
|
||||||
use winit::{
|
|
||||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
|
||||||
event_loop::ControlFlow,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
gpu::{create_depth_texture, create_render_texture, initialize_gpu, DeviceHolder},
|
|
||||||
imgui::{initialize_imgui, ImGuiState},
|
|
||||||
sdl::{initialize_sdl, poll_sdl_events, SdlState},
|
|
||||||
shaders::render_into_pass,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod cxxbridge;
|
|
||||||
mod gpu;
|
|
||||||
mod imgui;
|
|
||||||
mod imgui_backend;
|
|
||||||
mod sdl;
|
|
||||||
mod shaders;
|
|
||||||
mod util;
|
|
||||||
mod zeus;
|
|
||||||
|
|
||||||
pub struct App {
|
|
||||||
window: ffi::Window,
|
|
||||||
gpu: DeviceHolder,
|
|
||||||
imgui: ImGuiState,
|
|
||||||
sdl: SdlState,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WindowContext {
|
|
||||||
window: winit::window::Window,
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut APP: Option<App> = None;
|
|
||||||
|
|
||||||
fn app_run(mut delegate: cxx::UniquePtr<ffi::AppDelegate>, icon: ffi::Icon) {
|
|
||||||
if delegate.is_null() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
env_logger::init();
|
|
||||||
log::info!("Running app");
|
|
||||||
let event_loop = winit::event_loop::EventLoop::new();
|
|
||||||
let window_icon = winit::window::Icon::from_rgba(icon.data, icon.width, icon.height)
|
|
||||||
.expect("Failed to load icon");
|
|
||||||
let window = winit::window::WindowBuilder::new()
|
|
||||||
.with_inner_size(winit::dpi::LogicalSize::new(1280, 720))
|
|
||||||
.with_window_icon(Some(window_icon))
|
|
||||||
.build(&event_loop)
|
|
||||||
.unwrap();
|
|
||||||
let sdl = initialize_sdl();
|
|
||||||
let gpu = initialize_gpu(&window);
|
|
||||||
let imgui = initialize_imgui(&window, &gpu);
|
|
||||||
shaders::construct_state(gpu.device.clone(), gpu.queue.clone(), &gpu.config);
|
|
||||||
let app =
|
|
||||||
App { window: ffi::Window { inner: Box::new(WindowContext { window }) }, gpu, imgui, sdl };
|
|
||||||
let window_size = ffi::WindowSize {
|
|
||||||
width: app.gpu.surface_config.width,
|
|
||||||
height: app.gpu.surface_config.height,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
APP.replace(app);
|
|
||||||
ffi::App_onAppLaunched(delegate.as_mut().unwrap());
|
|
||||||
ffi::App_onAppWindowResized(delegate.as_mut().unwrap_unchecked(), &window_size);
|
|
||||||
};
|
|
||||||
let mut last_frame: Option<Instant> = None;
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
|
||||||
// Have the closure take ownership of the resources.
|
|
||||||
// `event_loop.run` never returns, therefore we must do this to ensure
|
|
||||||
// the resources are properly cleaned up.
|
|
||||||
let _ = &delegate;
|
|
||||||
|
|
||||||
let app = get_app();
|
|
||||||
let imgui = &mut app.imgui;
|
|
||||||
let window_ctx = get_window_context();
|
|
||||||
let gpu = &mut app.gpu;
|
|
||||||
|
|
||||||
// SDL event loop
|
|
||||||
if !poll_sdl_events(&mut app.sdl, &mut delegate) {
|
|
||||||
*control_flow = ControlFlow::Exit;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// winit event loop
|
|
||||||
*control_flow = ControlFlow::Poll;
|
|
||||||
match event {
|
|
||||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
|
||||||
*control_flow = ControlFlow::Exit;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Event::WindowEvent { event: WindowEvent::Resized(size), .. } => {
|
|
||||||
// Reconfigure the surface with the new size
|
|
||||||
app.gpu.surface_config.width = size.width;
|
|
||||||
app.gpu.surface_config.height = size.height;
|
|
||||||
app.gpu.surface.configure(&app.gpu.device, &app.gpu.surface_config);
|
|
||||||
app.gpu.depth =
|
|
||||||
create_depth_texture(&app.gpu.device, &app.gpu.surface_config, &app.gpu.config);
|
|
||||||
app.gpu.framebuffer = create_render_texture(
|
|
||||||
&app.gpu.device,
|
|
||||||
&app.gpu.surface_config,
|
|
||||||
&app.gpu.config,
|
|
||||||
);
|
|
||||||
let window_size = ffi::WindowSize {
|
|
||||||
width: app.gpu.surface_config.width,
|
|
||||||
height: app.gpu.surface_config.height,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
ffi::App_onAppWindowResized(delegate.as_mut().unwrap_unchecked(), &window_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::WindowEvent { event: WindowEvent::Moved(loc), .. } => unsafe {
|
|
||||||
ffi::App_onAppWindowMoved(delegate.as_mut().unwrap_unchecked(), loc.x, loc.y);
|
|
||||||
},
|
|
||||||
Event::MainEventsCleared => {
|
|
||||||
log::trace!("Requesting redraw");
|
|
||||||
window_ctx.window.request_redraw();
|
|
||||||
}
|
|
||||||
Event::RedrawRequested(_) => {
|
|
||||||
let io = imgui.context.io_mut();
|
|
||||||
let mut dt: f32 = 1.0 / 60.0;
|
|
||||||
if let Some(last_frame) = last_frame {
|
|
||||||
let duration = last_frame.elapsed();
|
|
||||||
dt = duration.as_secs_f32();
|
|
||||||
io.update_delta_time(duration);
|
|
||||||
}
|
|
||||||
last_frame = Some(Instant::now());
|
|
||||||
|
|
||||||
imgui
|
|
||||||
.platform
|
|
||||||
.prepare_frame(io, &window_ctx.window)
|
|
||||||
.expect("Failed to prepare frame");
|
|
||||||
let ui = imgui.context.frame();
|
|
||||||
|
|
||||||
#[allow(unused_unsafe)] // rust bug?
|
|
||||||
if !unsafe { ffi::App_onAppIdle(delegate.as_mut().unwrap(), dt) } {
|
|
||||||
*control_flow = ControlFlow::Exit;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("Redrawing");
|
|
||||||
let frame_result = gpu.surface.get_current_texture();
|
|
||||||
if let Err(err) = frame_result {
|
|
||||||
log::warn!("Failed to acquire frame {}", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let frame = frame_result.unwrap();
|
|
||||||
|
|
||||||
#[allow(unused_unsafe)] // rust bug?
|
|
||||||
unsafe {
|
|
||||||
ffi::App_onAppDraw(delegate.as_mut().unwrap_unchecked());
|
|
||||||
}
|
|
||||||
|
|
||||||
let fb_view =
|
|
||||||
gpu.framebuffer.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let mut encoder =
|
|
||||||
gpu.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("Redraw Encoder"),
|
|
||||||
});
|
|
||||||
{
|
|
||||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: None,
|
|
||||||
color_attachments: &[if gpu.config.msaa_samples > 1 {
|
|
||||||
wgpu::RenderPassColorAttachment {
|
|
||||||
view: &fb_view,
|
|
||||||
resolve_target: Some(&view),
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
|
||||||
store: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wgpu::RenderPassColorAttachment {
|
|
||||||
view: &view,
|
|
||||||
resolve_target: None,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
|
||||||
store: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
|
||||||
view: &gpu.depth.view,
|
|
||||||
depth_ops: Some(wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(1.0),
|
|
||||||
store: true,
|
|
||||||
}),
|
|
||||||
stencil_ops: None,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
render_into_pass(&mut rpass);
|
|
||||||
|
|
||||||
imgui.platform.prepare_render(&ui, &window_ctx.window);
|
|
||||||
imgui
|
|
||||||
.backend
|
|
||||||
.render(ui.render(), &gpu.queue, &gpu.device, &mut rpass)
|
|
||||||
.expect("Rendering failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu.queue.submit(Some(encoder.finish()));
|
|
||||||
frame.present();
|
|
||||||
|
|
||||||
#[allow(unused_unsafe)] // rust bug?
|
|
||||||
unsafe {
|
|
||||||
ffi::App_onAppPostDraw(delegate.as_mut().unwrap_unchecked());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::LoopDestroyed => unsafe {
|
|
||||||
ffi::App_onAppExiting(delegate.as_mut().unwrap_unchecked());
|
|
||||||
APP.take();
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
imgui.platform.handle_event(imgui.context.io_mut(), &window_ctx.window, &event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_app() -> &'static mut App {
|
|
||||||
unsafe {
|
|
||||||
if let Some(ref mut app) = APP {
|
|
||||||
app
|
|
||||||
} else {
|
|
||||||
panic!("Application not initialized");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_window_context() -> &'static mut WindowContext { &mut get_app().window.inner }
|
|
||||||
|
|
||||||
fn get_args() -> Vec<String> { std::env::args().skip(1).collect() }
|
|
||||||
|
|
||||||
fn get_window_size() -> ffi::WindowSize {
|
|
||||||
let size = get_window_context().window.inner_size();
|
|
||||||
ffi::WindowSize { width: size.width, height: size.height }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_window_title(title: &cxx::CxxString) {
|
|
||||||
get_window_context().window.set_title(&title.to_string_lossy());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_backend() -> ffi::Backend {
|
|
||||||
match get_app().gpu.backend {
|
|
||||||
Backend::Vulkan => ffi::Backend::Vulkan,
|
|
||||||
Backend::Metal => ffi::Backend::Metal,
|
|
||||||
Backend::Dx12 => ffi::Backend::D3D12,
|
|
||||||
Backend::Dx11 => ffi::Backend::D3D11,
|
|
||||||
Backend::Gl => ffi::Backend::OpenGL,
|
|
||||||
Backend::BrowserWebGpu => ffi::Backend::WebGPU,
|
|
||||||
Backend::Empty => ffi::Backend::Invalid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_dxt_compression_supported() -> bool {
|
|
||||||
return get_app().gpu.adapter.features().contains(wgpu::Features::TEXTURE_COMPRESSION_BC);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_backend_string() -> &'static str {
|
|
||||||
match get_app().gpu.backend {
|
|
||||||
Backend::Vulkan => "Vulkan",
|
|
||||||
Backend::Metal => "Metal",
|
|
||||||
Backend::Dx12 => "D3D12",
|
|
||||||
Backend::Dx11 => "D3D11",
|
|
||||||
Backend::Gl => "OpenGL ES",
|
|
||||||
Backend::BrowserWebGpu => "WebGPU",
|
|
||||||
Backend::Empty => "Invalid",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_fullscreen(v: bool) {
|
|
||||||
get_window_context().window.set_fullscreen(if v {
|
|
||||||
Some(winit::window::Fullscreen::Borderless(None))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,314 +0,0 @@
|
||||||
use std::collections::{BTreeMap, HashMap};
|
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use sdl2::{
|
|
||||||
controller::{Axis, GameController},
|
|
||||||
event::Event,
|
|
||||||
GameControllerSubsystem, Sdl,
|
|
||||||
};
|
|
||||||
use sdl2::keyboard::{Keycode, Mod};
|
|
||||||
|
|
||||||
use crate::{cxxbridge::ffi, get_app};
|
|
||||||
use crate::ffi::SpecialKey;
|
|
||||||
|
|
||||||
pub(crate) struct SdlState {
|
|
||||||
context: Sdl,
|
|
||||||
events: sdl2::EventPump,
|
|
||||||
controller_sys: GameControllerSubsystem,
|
|
||||||
open_controllers: HashMap<u32, GameController>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn initialize_sdl() -> SdlState {
|
|
||||||
let context = sdl2::init().unwrap();
|
|
||||||
let events = context.event_pump().unwrap();
|
|
||||||
let controller_sys = context.game_controller().unwrap();
|
|
||||||
SdlState { context, events, controller_sys, open_controllers: Default::default() }
|
|
||||||
}
|
|
||||||
bitflags! {
|
|
||||||
pub struct ModifierKey : u16 {
|
|
||||||
const None = 0;
|
|
||||||
const LeftShift = 0x0001;
|
|
||||||
const RightShift = 0x0002;
|
|
||||||
const LeftControl = 0x0004;
|
|
||||||
const RightControl = 0x0008;
|
|
||||||
const LeftAlt = 0x0010;
|
|
||||||
const RightAlt = 0x0020;
|
|
||||||
const LeftGui = 0x0040;
|
|
||||||
const RightGui = 0x0080;
|
|
||||||
const Num = 0x0100;
|
|
||||||
const Caps = 0x0200;
|
|
||||||
const Mode = 0x0400;
|
|
||||||
// SDL has a reserved value we don't need
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_modifiers(keymod: Mod) -> ModifierKey {
|
|
||||||
let mut use_mod: ModifierKey = ModifierKey::None;
|
|
||||||
if keymod.contains(Mod::LSHIFTMOD) {
|
|
||||||
use_mod.insert(ModifierKey::LeftShift);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::RSHIFTMOD) {
|
|
||||||
use_mod.insert(ModifierKey::RightShift);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::LCTRLMOD) {
|
|
||||||
use_mod.insert(ModifierKey::LeftControl);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::RCTRLMOD) {
|
|
||||||
use_mod.insert(ModifierKey::RightControl);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::LALTMOD) {
|
|
||||||
use_mod.insert(ModifierKey::LeftAlt);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::RALTMOD) {
|
|
||||||
use_mod.insert(ModifierKey::RightAlt);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::LGUIMOD) {
|
|
||||||
use_mod.insert(ModifierKey::LeftGui);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::RGUIMOD) {
|
|
||||||
use_mod.insert(ModifierKey::RightGui);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::NUMMOD) {
|
|
||||||
use_mod.insert(ModifierKey::Num);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::CAPSMOD) {
|
|
||||||
use_mod.insert(ModifierKey::Caps);
|
|
||||||
}
|
|
||||||
if keymod.contains(Mod::MODEMOD) {
|
|
||||||
use_mod.insert(ModifierKey::Mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
use_mod as ModifierKey
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_special_key(keycode: Keycode) -> ffi::SpecialKey {
|
|
||||||
match keycode {
|
|
||||||
Keycode::F1 => ffi::SpecialKey::F1,
|
|
||||||
Keycode::F2 => ffi::SpecialKey::F2,
|
|
||||||
Keycode::F3 => ffi::SpecialKey::F3,
|
|
||||||
Keycode::F4 => ffi::SpecialKey::F4,
|
|
||||||
Keycode::F5 => ffi::SpecialKey::F5,
|
|
||||||
Keycode::F6 => ffi::SpecialKey::F6,
|
|
||||||
Keycode::F7 => ffi::SpecialKey::F7,
|
|
||||||
Keycode::F8 => ffi::SpecialKey::F8,
|
|
||||||
Keycode::F9 => ffi::SpecialKey::F9,
|
|
||||||
Keycode::F10 => ffi::SpecialKey::F10,
|
|
||||||
Keycode::F11 => ffi::SpecialKey::F11,
|
|
||||||
Keycode::F12 => ffi::SpecialKey::F12,
|
|
||||||
Keycode::F13 => ffi::SpecialKey::F13,
|
|
||||||
Keycode::F14 => ffi::SpecialKey::F14,
|
|
||||||
Keycode::F15 => ffi::SpecialKey::F15,
|
|
||||||
Keycode::F16 => ffi::SpecialKey::F16,
|
|
||||||
Keycode::F17 => ffi::SpecialKey::F17,
|
|
||||||
Keycode::F18 => ffi::SpecialKey::F18,
|
|
||||||
Keycode::F19 => ffi::SpecialKey::F19,
|
|
||||||
Keycode::F20 => ffi::SpecialKey::F20,
|
|
||||||
Keycode::F21 => ffi::SpecialKey::F21,
|
|
||||||
Keycode::F22 => ffi::SpecialKey::F22,
|
|
||||||
Keycode::F23 => ffi::SpecialKey::F23,
|
|
||||||
Keycode::F24 => ffi::SpecialKey::F23,
|
|
||||||
Keycode::Escape => ffi::SpecialKey::Esc,
|
|
||||||
Keycode::Return => ffi::SpecialKey::Enter,
|
|
||||||
Keycode::Backspace => ffi::SpecialKey::Backspace,
|
|
||||||
Keycode::Insert => ffi::SpecialKey::Insert,
|
|
||||||
Keycode::Delete => ffi::SpecialKey::Delete,
|
|
||||||
Keycode::Home => ffi::SpecialKey::Home,
|
|
||||||
Keycode::End => ffi::SpecialKey::End,
|
|
||||||
Keycode::PageUp => ffi::SpecialKey::PgUp,
|
|
||||||
Keycode::PageDown => ffi::SpecialKey::PgDown,
|
|
||||||
Keycode::Left => ffi::SpecialKey::Left,
|
|
||||||
Keycode::Right => ffi::SpecialKey::Right,
|
|
||||||
Keycode::Up => ffi::SpecialKey::Up,
|
|
||||||
Keycode::Down => ffi::SpecialKey::Down,
|
|
||||||
Keycode::Tab => ffi::SpecialKey::Tab,
|
|
||||||
Keycode::PrintScreen => ffi::SpecialKey::PrintScreen,
|
|
||||||
Keycode::ScrollLock => ffi::SpecialKey::ScrollLock,
|
|
||||||
Keycode::Pause => ffi::SpecialKey::Pause,
|
|
||||||
Keycode::NumLockClear => ffi::SpecialKey::NumLockClear,
|
|
||||||
Keycode::KpDivide => ffi::SpecialKey::KpDivide,
|
|
||||||
Keycode::KpMultiply => ffi::SpecialKey::KpMultiply,
|
|
||||||
Keycode::KpMinus => ffi::SpecialKey::KpMinus,
|
|
||||||
Keycode::KpPlus => ffi::SpecialKey::KpPlus,
|
|
||||||
Keycode::KpEquals => ffi::SpecialKey::KpEquals,
|
|
||||||
Keycode::Kp0 => ffi::SpecialKey::KpNum0,
|
|
||||||
Keycode::Kp1 => ffi::SpecialKey::KpNum1,
|
|
||||||
Keycode::Kp2 => ffi::SpecialKey::KpNum2,
|
|
||||||
Keycode::Kp3 => ffi::SpecialKey::KpNum3,
|
|
||||||
Keycode::Kp4 => ffi::SpecialKey::KpNum4,
|
|
||||||
Keycode::Kp5 => ffi::SpecialKey::KpNum5,
|
|
||||||
Keycode::Kp6 => ffi::SpecialKey::KpNum6,
|
|
||||||
Keycode::Kp7 => ffi::SpecialKey::KpNum7,
|
|
||||||
Keycode::Kp8 => ffi::SpecialKey::KpNum8,
|
|
||||||
Keycode::Kp9 => ffi::SpecialKey::KpNum9,
|
|
||||||
Keycode::KpPeriod => ffi::SpecialKey::KpPeriod,
|
|
||||||
_ => ffi::SpecialKey::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn poll_sdl_events(
|
|
||||||
state: &mut SdlState,
|
|
||||||
delegate: &mut cxx::UniquePtr<ffi::AppDelegate>,
|
|
||||||
) -> bool {
|
|
||||||
for event in state.events.poll_iter() {
|
|
||||||
match event {
|
|
||||||
Event::ControllerDeviceAdded { which, .. } => {
|
|
||||||
match state.controller_sys.open(which) {
|
|
||||||
Ok(controller) => {
|
|
||||||
log::info!("Opened SDL controller \"{}\"", controller.name());
|
|
||||||
if let Some(new_mapping) = remap_controller_layout(controller.mapping()) {
|
|
||||||
state
|
|
||||||
.controller_sys
|
|
||||||
.add_mapping(new_mapping.as_str())
|
|
||||||
.expect("Failed to overwrite mapping");
|
|
||||||
}
|
|
||||||
let instance_id: u32 = controller.instance_id();
|
|
||||||
state.open_controllers.insert(instance_id, controller);
|
|
||||||
unsafe { ffi::App_onControllerAdded(delegate.as_mut().unwrap_unchecked(), instance_id); }
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
log::warn!("Failed to open SDL controller {} ({:?})", which, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::ControllerDeviceRemoved { which, .. } => {
|
|
||||||
unsafe { ffi::App_onControllerRemoved(delegate.as_mut().unwrap_unchecked(), which); }
|
|
||||||
state.open_controllers.remove(&which);
|
|
||||||
}
|
|
||||||
Event::ControllerButtonDown { which, button, .. } => unsafe {
|
|
||||||
ffi::App_onControllerButton(
|
|
||||||
delegate.as_mut().unwrap_unchecked(),
|
|
||||||
which,
|
|
||||||
button.into(),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
Event::ControllerButtonUp { which, button, .. } => unsafe {
|
|
||||||
ffi::App_onControllerButton(
|
|
||||||
delegate.as_mut().unwrap_unchecked(),
|
|
||||||
which,
|
|
||||||
button.into(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
Event::ControllerAxisMotion { which, axis, value, .. } => unsafe {
|
|
||||||
ffi::App_onControllerAxis(
|
|
||||||
delegate.as_mut().unwrap_unchecked(),
|
|
||||||
which,
|
|
||||||
axis.into(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
Event::KeyDown { keycode, keymod, repeat, .. } => {
|
|
||||||
let special_key = translate_special_key(keycode.unwrap());
|
|
||||||
let use_mod = translate_modifiers(keymod);
|
|
||||||
if special_key != ffi::SpecialKey::None {
|
|
||||||
unsafe {
|
|
||||||
ffi::App_onSpecialKeyDown(delegate.as_mut().unwrap_unchecked(),
|
|
||||||
special_key,
|
|
||||||
use_mod.bits,
|
|
||||||
repeat);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let tmp = keycode.unwrap() as u8;
|
|
||||||
if tmp >= '\x20' as u8 && tmp <= 'z' as u8 {
|
|
||||||
unsafe {
|
|
||||||
ffi::App_onCharKeyDown(delegate.as_mut().unwrap_unchecked(),
|
|
||||||
tmp,
|
|
||||||
use_mod.bits,
|
|
||||||
repeat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Event::KeyUp { keycode, keymod, .. } => {
|
|
||||||
let special_key = translate_special_key(keycode.unwrap());
|
|
||||||
let use_mod = translate_modifiers(keymod);
|
|
||||||
if special_key != ffi::SpecialKey::None {
|
|
||||||
unsafe {
|
|
||||||
ffi::App_onSpecialKeyUp(delegate.as_mut().unwrap_unchecked(),
|
|
||||||
special_key,
|
|
||||||
use_mod.bits);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let tmp = keycode.unwrap() as u8;
|
|
||||||
if tmp >= '\x20' as u8 && tmp <= 'z' as u8 {
|
|
||||||
unsafe {
|
|
||||||
ffi::App_onCharKeyUp(delegate.as_mut().unwrap_unchecked(),
|
|
||||||
tmp,
|
|
||||||
use_mod.bits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// SDL overrides exit signals
|
|
||||||
Event::Quit { .. } => {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remap_controller_layout(mapping: String) -> Option<String> {
|
|
||||||
let mut new_mapping = String::with_capacity(mapping.len());
|
|
||||||
let mut entries = BTreeMap::<&str, &str>::new();
|
|
||||||
for (i, v) in mapping.split(',').enumerate() {
|
|
||||||
if i < 2 {
|
|
||||||
if i > 0 {
|
|
||||||
new_mapping.push(',');
|
|
||||||
}
|
|
||||||
new_mapping.push_str(v);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let split = v.splitn(2, ':').collect::<Vec<&str>>();
|
|
||||||
if split.len() != 2 {
|
|
||||||
panic!("Invalid mapping entry: {}", v);
|
|
||||||
}
|
|
||||||
entries.insert(split[0], split[1]);
|
|
||||||
}
|
|
||||||
if entries.contains_key("rightshoulder") && !entries.contains_key("leftshoulder") {
|
|
||||||
log::debug!("Remapping GameCube controller layout");
|
|
||||||
// TODO trigger buttons may differ per platform
|
|
||||||
entries.insert("leftshoulder", "b11");
|
|
||||||
let z_button = entries.insert("rightshoulder", "b10").unwrap();
|
|
||||||
entries.insert("back", z_button);
|
|
||||||
} else if entries.contains_key("leftshoulder")
|
|
||||||
&& entries.contains_key("rightshoulder")
|
|
||||||
&& entries.contains_key("back")
|
|
||||||
{
|
|
||||||
log::debug!("Controller has standard layout");
|
|
||||||
return None;
|
|
||||||
} else {
|
|
||||||
log::error!("Controller has unsupported layout: {}", mapping);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
for (k, v) in entries {
|
|
||||||
new_mapping.push(',');
|
|
||||||
new_mapping.push_str(k);
|
|
||||||
new_mapping.push(':');
|
|
||||||
new_mapping.push_str(v);
|
|
||||||
}
|
|
||||||
return Some(new_mapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_controller_player_index(which: u32) -> i32 {
|
|
||||||
get_app().sdl.open_controllers.get(&which).map_or(-1, |c| c.player_index())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_controller_player_index(which: u32, index: i32) {
|
|
||||||
if let Some(c) = get_app().sdl.open_controllers.get(&which) {
|
|
||||||
c.set_player_index(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_controller_gamecube(which: u32) -> bool {
|
|
||||||
get_app().sdl.open_controllers.get(&which)
|
|
||||||
.map_or(false, |c| c.name()
|
|
||||||
.to_lowercase()
|
|
||||||
.eq("nintendo gamecube controller"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_controller_name(which: u32) -> String {
|
|
||||||
get_app().sdl.open_controllers.get(&which).map_or(String::from(""), |c| c.name())
|
|
||||||
}
|
|
|
@ -1,206 +0,0 @@
|
||||||
use std::{hash::Hash, ops::Range};
|
|
||||||
|
|
||||||
use bytemuck_derive::{Pod, Zeroable};
|
|
||||||
use wgpu::{include_wgsl, vertex_attr_array};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
gpu::GraphicsConfig,
|
|
||||||
shaders::{
|
|
||||||
bind_pipeline, get_combined_matrix, pipeline_ref, push_draw_command, push_uniform,
|
|
||||||
push_verts, BuiltBuffers, PipelineCreateCommand, PipelineHolder, PipelineRef,
|
|
||||||
ShaderDrawCommand,
|
|
||||||
},
|
|
||||||
zeus::{CAABox, CColor, CMatrix4f, CVector3f},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct DrawData {
|
|
||||||
pipeline: PipelineRef,
|
|
||||||
vert_range: Range<u64>,
|
|
||||||
uniform_range: Range<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Hash)]
|
|
||||||
pub(crate) struct PipelineConfig {
|
|
||||||
z_only: bool,
|
|
||||||
}
|
|
||||||
pub(crate) const INITIAL_PIPELINES: &[PipelineCreateCommand] = &[
|
|
||||||
PipelineCreateCommand::Aabb(PipelineConfig { z_only: false }),
|
|
||||||
PipelineCreateCommand::Aabb(PipelineConfig { z_only: true }),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub(crate) struct State {
|
|
||||||
shader: wgpu::ShaderModule,
|
|
||||||
uniform_layout: wgpu::BindGroupLayout,
|
|
||||||
uniform_bind_group: wgpu::BindGroup,
|
|
||||||
pipeline_layout: wgpu::PipelineLayout,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_state(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
_queue: &wgpu::Queue,
|
|
||||||
buffers: &BuiltBuffers,
|
|
||||||
graphics_config: &GraphicsConfig,
|
|
||||||
) -> State {
|
|
||||||
let shader = device.create_shader_module(&include_wgsl!("shader.wgsl"));
|
|
||||||
let uniform_size = wgpu::BufferSize::new(std::mem::size_of::<Uniform>() as u64);
|
|
||||||
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("AABB Bind Group Layout"),
|
|
||||||
entries: &[wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: true,
|
|
||||||
min_binding_size: uniform_size,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: Some("AABB Bind Group"),
|
|
||||||
layout: &uniform_layout,
|
|
||||||
entries: &[wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
|
||||||
buffer: &buffers.uniform_buffer,
|
|
||||||
offset: 0, // dynamic
|
|
||||||
size: uniform_size,
|
|
||||||
}),
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("AABB Pipeline Layout"),
|
|
||||||
bind_group_layouts: &[&uniform_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
State { shader, uniform_layout, uniform_bind_group, pipeline_layout }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_pipeline(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
graphics: &GraphicsConfig,
|
|
||||||
state: &State,
|
|
||||||
config: &PipelineConfig,
|
|
||||||
) -> PipelineHolder {
|
|
||||||
PipelineHolder {
|
|
||||||
pipeline: device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: Some("AABB Pipeline"),
|
|
||||||
layout: Some(&state.pipeline_layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &state.shader,
|
|
||||||
entry_point: "vs_main",
|
|
||||||
buffers: &[wgpu::VertexBufferLayout {
|
|
||||||
array_stride: std::mem::size_of::<CVector3f>() as u64,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &vertex_attr_array![0 => Float32x3],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
depth_stencil: Some(wgpu::DepthStencilState {
|
|
||||||
format: graphics.depth_format,
|
|
||||||
depth_write_enabled: true,
|
|
||||||
depth_compare: wgpu::CompareFunction::LessEqual,
|
|
||||||
stencil: Default::default(),
|
|
||||||
bias: Default::default(),
|
|
||||||
}),
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: graphics.msaa_samples,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &state.shader,
|
|
||||||
entry_point: "fs_main",
|
|
||||||
targets: &[wgpu::ColorTargetState {
|
|
||||||
format: graphics.color_format,
|
|
||||||
blend: Some(wgpu::BlendState {
|
|
||||||
color: wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
alpha: wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
write_mask: if config.z_only {
|
|
||||||
wgpu::ColorWrites::empty()
|
|
||||||
} else {
|
|
||||||
wgpu::ColorWrites::ALL
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
multiview: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Uniform {
|
|
||||||
xf: CMatrix4f,
|
|
||||||
color: CColor,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn queue_aabb(aabb: CAABox, color: CColor, z_only: bool) {
|
|
||||||
let pipeline = pipeline_ref(&PipelineCreateCommand::Aabb(PipelineConfig { z_only }));
|
|
||||||
let vert_range = push_verts(&[
|
|
||||||
CVector3f::new(aabb.max.x, aabb.max.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.min.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.max.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.max.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.max.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.max.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.max.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.max.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.max.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.max.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.max.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.max.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.min.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.min.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.max.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.max.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.max.y, aabb.max.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.min.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.min.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.min.x, aabb.max.y, aabb.min.z),
|
|
||||||
CVector3f::new(aabb.max.x, aabb.max.y, aabb.min.z),
|
|
||||||
]);
|
|
||||||
let uniform_range = push_uniform(&Uniform { xf: get_combined_matrix(), color });
|
|
||||||
push_draw_command(ShaderDrawCommand::Aabb(DrawData { pipeline, vert_range, uniform_range }));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn draw_aabb<'a>(
|
|
||||||
data: &DrawData,
|
|
||||||
state: &'a State,
|
|
||||||
pass: &mut wgpu::RenderPass<'a>,
|
|
||||||
buffers: &'a BuiltBuffers,
|
|
||||||
) {
|
|
||||||
if !bind_pipeline(data.pipeline, pass) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pass.set_bind_group(0, &state.uniform_bind_group, &[
|
|
||||||
data.uniform_range.start as wgpu::DynamicOffset
|
|
||||||
]);
|
|
||||||
pass.set_vertex_buffer(0, buffers.vertex_buffer.slice(data.vert_range.clone()));
|
|
||||||
pass.draw(0..34, 0..1);
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
struct Uniform {
|
|
||||||
xf: mat4x4<f32>;
|
|
||||||
color: vec4<f32>;
|
|
||||||
};
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<uniform> ubuf: Uniform;
|
|
||||||
|
|
||||||
@stage(vertex)
|
|
||||||
fn vs_main(@location(0) in_pos: vec3<f32>) -> @builtin(position) vec4<f32> {
|
|
||||||
return ubuf.xf * vec4<f32>(in_pos, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(fragment)
|
|
||||||
fn fs_main() -> @location(0) vec4<f32> {
|
|
||||||
return ubuf.color;
|
|
||||||
}
|
|
|
@ -1,319 +0,0 @@
|
||||||
use std::{collections::HashMap, hash::Hash, ops::Range};
|
|
||||||
|
|
||||||
use bytemuck_derive::{Pod, Zeroable};
|
|
||||||
use wgpu::{include_wgsl, vertex_attr_array};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
get_app,
|
|
||||||
gpu::GraphicsConfig,
|
|
||||||
shaders::{
|
|
||||||
bind_pipeline, cxxbridge::ffi, get_combined_matrix, pipeline_ref, push_draw_command,
|
|
||||||
push_uniform, push_verts, texture::create_sampler, BuiltBuffers, PipelineCreateCommand,
|
|
||||||
PipelineHolder, PipelineRef, ShaderDrawCommand, STATE,
|
|
||||||
},
|
|
||||||
util::{align, Vec2, Vec3},
|
|
||||||
zeus::{CColor, CMatrix4f, CRectangle, CVector2f, CVector3f, CVector4f},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct DrawData {
|
|
||||||
pipeline: PipelineRef,
|
|
||||||
vert_range: Range<u64>,
|
|
||||||
uniform_range: Range<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Hash)]
|
|
||||||
pub(crate) struct PipelineConfig {
|
|
||||||
filter_type: ffi::CameraFilterType,
|
|
||||||
z_comparison: ffi::ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
}
|
|
||||||
pub(crate) const INITIAL_PIPELINES: &[PipelineCreateCommand] = &[
|
|
||||||
// PipelineCreateCommand::ColoredQuad(PipelineConfig { z_only: false }),
|
|
||||||
// PipelineCreateCommand::ColoredQuad(PipelineConfig { z_only: true }),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub(crate) struct State {
|
|
||||||
shader: wgpu::ShaderModule,
|
|
||||||
uniform_layout: wgpu::BindGroupLayout,
|
|
||||||
uniform_bind_group: wgpu::BindGroup,
|
|
||||||
sampler: wgpu::Sampler,
|
|
||||||
pipeline_layout: wgpu::PipelineLayout,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_state(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
_queue: &wgpu::Queue,
|
|
||||||
buffers: &BuiltBuffers,
|
|
||||||
graphics_config: &GraphicsConfig,
|
|
||||||
) -> State {
|
|
||||||
let shader = device.create_shader_module(&include_wgsl!("shader.wgsl"));
|
|
||||||
let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment;
|
|
||||||
let uniform_size = wgpu::BufferSize::new(align(
|
|
||||||
std::mem::size_of::<Uniform>() as u64,
|
|
||||||
uniform_alignment as u64,
|
|
||||||
));
|
|
||||||
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("Colored Quad Uniform Bind Group Layout"),
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: true,
|
|
||||||
min_binding_size: uniform_size,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let sampler = create_sampler(device, wgpu::AddressMode::Repeat, None);
|
|
||||||
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: Some("Colored Quad Uniform Bind Group"),
|
|
||||||
layout: &uniform_layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
|
||||||
buffer: &buffers.uniform_buffer,
|
|
||||||
offset: 0, // dynamic
|
|
||||||
size: uniform_size,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("Colored Quad Pipeline Layout"),
|
|
||||||
bind_group_layouts: &[&uniform_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
State {
|
|
||||||
shader,
|
|
||||||
uniform_layout,
|
|
||||||
uniform_bind_group,
|
|
||||||
sampler,
|
|
||||||
pipeline_layout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_pipeline(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
graphics: &GraphicsConfig,
|
|
||||||
state: &State,
|
|
||||||
config: &PipelineConfig,
|
|
||||||
) -> PipelineHolder {
|
|
||||||
let (blend_component, alpha_write) = match config.filter_type {
|
|
||||||
ffi::CameraFilterType::Multiply => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::Zero,
|
|
||||||
dst_factor: wgpu::BlendFactor::Src,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
ffi::CameraFilterType::Add => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::One,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
ffi::CameraFilterType::Subtract => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::One,
|
|
||||||
operation: wgpu::BlendOperation::Subtract,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
ffi::CameraFilterType::Blend => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
ffi::CameraFilterType::InvDstMultiply => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::Zero,
|
|
||||||
dst_factor: wgpu::BlendFactor::OneMinusSrc,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
PipelineHolder {
|
|
||||||
pipeline: device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: Some("Colored Quad Pipeline"),
|
|
||||||
layout: Some(&state.pipeline_layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &state.shader,
|
|
||||||
entry_point: "vs_main",
|
|
||||||
buffers: &[wgpu::VertexBufferLayout {
|
|
||||||
array_stride: std::mem::size_of::<Vert>() as u64,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &vertex_attr_array![0 => Float32x3],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
depth_stencil: Some(wgpu::DepthStencilState {
|
|
||||||
format: graphics.depth_format,
|
|
||||||
depth_write_enabled: config.z_test,
|
|
||||||
depth_compare: match config.z_comparison {
|
|
||||||
ffi::ZTest::Never => wgpu::CompareFunction::Never,
|
|
||||||
ffi::ZTest::Less => wgpu::CompareFunction::Less,
|
|
||||||
ffi::ZTest::Equal => wgpu::CompareFunction::Equal,
|
|
||||||
ffi::ZTest::LEqual => wgpu::CompareFunction::LessEqual,
|
|
||||||
ffi::ZTest::Greater => wgpu::CompareFunction::Greater,
|
|
||||||
ffi::ZTest::NEqual => wgpu::CompareFunction::NotEqual,
|
|
||||||
ffi::ZTest::GEqual => wgpu::CompareFunction::GreaterEqual,
|
|
||||||
ffi::ZTest::Always => wgpu::CompareFunction::Always,
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
stencil: Default::default(),
|
|
||||||
bias: Default::default(),
|
|
||||||
}),
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: graphics.msaa_samples,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &state.shader,
|
|
||||||
entry_point: "fs_main",
|
|
||||||
targets: &[wgpu::ColorTargetState {
|
|
||||||
format: graphics.color_format,
|
|
||||||
blend: Some(wgpu::BlendState {
|
|
||||||
color: blend_component,
|
|
||||||
alpha: blend_component,
|
|
||||||
}),
|
|
||||||
write_mask: if alpha_write {
|
|
||||||
wgpu::ColorWrites::ALL
|
|
||||||
} else {
|
|
||||||
wgpu::ColorWrites::COLOR
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
multiview: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Uniform {
|
|
||||||
xf: CMatrix4f,
|
|
||||||
color: CColor,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Vert {
|
|
||||||
pos: Vec3<f32>,
|
|
||||||
//norm: Vec3<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn queue_colored_quad(
|
|
||||||
filter_type: ffi::CameraFilterType,
|
|
||||||
z_comparison: ffi::ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
color: CColor,
|
|
||||||
rect: CRectangle,
|
|
||||||
z: f32,
|
|
||||||
) {
|
|
||||||
let pipeline = pipeline_ref(&PipelineCreateCommand::ColoredQuad(PipelineConfig {
|
|
||||||
filter_type,
|
|
||||||
z_comparison,
|
|
||||||
z_test,
|
|
||||||
}));
|
|
||||||
let vert_range = push_verts(&[
|
|
||||||
Vert { pos: Vec3::new(0.0, 0.0, z) },
|
|
||||||
Vert { pos: Vec3::new(0.0, 1.0, z) },
|
|
||||||
Vert { pos: Vec3::new(1.0, 0.0, z) },
|
|
||||||
Vert { pos: Vec3::new(1.0, 1.0, z) },
|
|
||||||
]);
|
|
||||||
let uniform_range = push_uniform(&Uniform {
|
|
||||||
xf: CMatrix4f::new(
|
|
||||||
CVector4f::new(rect.size.x * 2.0, 0.0, 0.0, 0.0),
|
|
||||||
CVector4f::new(0.0, rect.size.y * 2.0, 0.0, 0.0),
|
|
||||||
CVector4f::new(0.0, 0.0, 1.0, 0.0),
|
|
||||||
CVector4f::new(rect.position.x * 2.0 - 1.0, rect.position.y * 2.0 - 1.0, 0.0, 1.0),
|
|
||||||
),
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
|
|
||||||
push_colored_quad(pipeline, vert_range, uniform_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn queue_colored_quad_verts(
|
|
||||||
filter_type: ffi::CameraFilterType,
|
|
||||||
z_comparison: ffi::ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
color: CColor,
|
|
||||||
pos: &[CVector3f],
|
|
||||||
) {
|
|
||||||
if pos.len() != 4 {
|
|
||||||
panic!("Invalid pos: {}", pos.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
let pipeline = pipeline_ref(&PipelineCreateCommand::ColoredQuad(PipelineConfig {
|
|
||||||
filter_type,
|
|
||||||
z_comparison,
|
|
||||||
z_test,
|
|
||||||
}));
|
|
||||||
let vert_range = push_verts(
|
|
||||||
&pos.iter().map(|pos| Vert { pos: pos.into() })
|
|
||||||
.collect::<Vec<Vert>>(),
|
|
||||||
);
|
|
||||||
let uniform_range = push_uniform(&Uniform { xf: get_combined_matrix(), color});
|
|
||||||
|
|
||||||
push_colored_quad(pipeline, vert_range, uniform_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_colored_quad(
|
|
||||||
pipeline: PipelineRef,
|
|
||||||
vert_range: Range<u64>,
|
|
||||||
uniform_range: Range<u64>,
|
|
||||||
) {
|
|
||||||
// TODO defer bind group creation to draw time or another thread?
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
|
|
||||||
push_draw_command(ShaderDrawCommand::ColoredQuad(DrawData {
|
|
||||||
pipeline,
|
|
||||||
vert_range,
|
|
||||||
uniform_range,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn draw_colored_quad<'a>(
|
|
||||||
data: &DrawData,
|
|
||||||
state: &'a State,
|
|
||||||
pass: &mut wgpu::RenderPass<'a>,
|
|
||||||
buffers: &'a BuiltBuffers,
|
|
||||||
) {
|
|
||||||
if !bind_pipeline(data.pipeline, pass) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uniform bind group
|
|
||||||
pass.set_bind_group(0, &state.uniform_bind_group, &[
|
|
||||||
data.uniform_range.start as wgpu::DynamicOffset
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Vertex buffer
|
|
||||||
pass.set_vertex_buffer(0, buffers.vertex_buffer.slice(data.vert_range.clone()));
|
|
||||||
pass.draw(0..4, 0..1);
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
struct Uniform {
|
|
||||||
xf: mat4x4<f32>;
|
|
||||||
color: vec4<f32>;
|
|
||||||
};
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<uniform> ubuf: Uniform;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) pos: vec4<f32>;
|
|
||||||
//@builtin(normal) norm: vec4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
@stage(vertex)
|
|
||||||
fn vs_main(@location(0) in_pos: vec3<f32>) -> VertexOutput {//, @location(1) in_norm: vec3<f32>) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);
|
|
||||||
//out.norm = in_norm;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(fragment)
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
return ubuf.color;
|
|
||||||
}
|
|
|
@ -1,269 +0,0 @@
|
||||||
use crate::shaders::{
|
|
||||||
aabb::queue_aabb,
|
|
||||||
fog_volume_filter::queue_fog_volume_filter,
|
|
||||||
fog_volume_plane::queue_fog_volume_plane,
|
|
||||||
model::{add_material_set, add_model},
|
|
||||||
movie_player::queue_movie_player,
|
|
||||||
resolve_color, resolve_depth, set_scissor, set_viewport,
|
|
||||||
texture::{
|
|
||||||
create_dynamic_texture_2d, create_render_texture, create_static_texture_2d, drop_texture,
|
|
||||||
write_texture,
|
|
||||||
},
|
|
||||||
textured_quad::{queue_textured_quad, queue_textured_quad_verts},
|
|
||||||
colored_quad::{queue_colored_quad, queue_colored_quad_verts},
|
|
||||||
update_fog_state, update_model_view, update_projection,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cxx::bridge]
|
|
||||||
pub(crate) mod ffi {
|
|
||||||
unsafe extern "C++" {
|
|
||||||
include!("zeus/CVector2f.hpp");
|
|
||||||
include!("zeus/CVector3f.hpp");
|
|
||||||
include!("zeus/CVector4f.hpp");
|
|
||||||
include!("zeus/CMatrix4f.hpp");
|
|
||||||
include!("zeus/CColor.hpp");
|
|
||||||
include!("zeus/CRectangle.hpp");
|
|
||||||
include!("zeus/CAABox.hpp");
|
|
||||||
#[namespace = "zeus"]
|
|
||||||
type CVector2f = crate::zeus::CVector2f;
|
|
||||||
#[namespace = "zeus"]
|
|
||||||
type CVector3f = crate::zeus::CVector3f;
|
|
||||||
#[namespace = "zeus"]
|
|
||||||
type CVector4f = crate::zeus::CVector4f;
|
|
||||||
#[namespace = "zeus"]
|
|
||||||
type CMatrix4f = crate::zeus::CMatrix4f;
|
|
||||||
#[namespace = "zeus"]
|
|
||||||
type CColor = crate::zeus::CColor;
|
|
||||||
#[namespace = "zeus"]
|
|
||||||
type CRectangle = crate::zeus::CRectangle;
|
|
||||||
#[namespace = "zeus"]
|
|
||||||
type CAABox = crate::zeus::CAABox;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone, Hash)]
|
|
||||||
pub(crate) enum CameraFilterType {
|
|
||||||
Passthru,
|
|
||||||
Multiply,
|
|
||||||
Invert,
|
|
||||||
Add,
|
|
||||||
Subtract,
|
|
||||||
Blend,
|
|
||||||
Widescreen,
|
|
||||||
SceneAdd,
|
|
||||||
NoColor,
|
|
||||||
InvDstMultiply,
|
|
||||||
}
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone, Hash)]
|
|
||||||
pub(crate) enum ZTest {
|
|
||||||
Never,
|
|
||||||
Less,
|
|
||||||
Equal,
|
|
||||||
LEqual,
|
|
||||||
Greater,
|
|
||||||
NEqual,
|
|
||||||
GEqual,
|
|
||||||
Always,
|
|
||||||
}
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub(crate) enum TextureFormat {
|
|
||||||
RGBA8,
|
|
||||||
R8,
|
|
||||||
R32Float,
|
|
||||||
DXT1,
|
|
||||||
DXT3,
|
|
||||||
DXT5,
|
|
||||||
BPTC,
|
|
||||||
}
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub(crate) enum TextureClampMode {
|
|
||||||
Repeat,
|
|
||||||
ClampToEdge,
|
|
||||||
ClampToWhite,
|
|
||||||
ClampToBlack,
|
|
||||||
}
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone, Hash)]
|
|
||||||
pub(crate) struct TextureRef {
|
|
||||||
pub(crate) id: u32,
|
|
||||||
pub(crate) render: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub(crate) enum FogMode {
|
|
||||||
None = 0x00,
|
|
||||||
|
|
||||||
PerspLin = 0x02,
|
|
||||||
PerspExp = 0x04,
|
|
||||||
PerspExp2 = 0x05,
|
|
||||||
PerspRevExp = 0x06,
|
|
||||||
PerspRevExp2 = 0x07,
|
|
||||||
|
|
||||||
OrthoLin = 0x0A,
|
|
||||||
OrthoExp = 0x0C,
|
|
||||||
OrthoExp2 = 0x0D,
|
|
||||||
OrthoRevExp = 0x0E,
|
|
||||||
OrthoRevExp2 = 0x0F,
|
|
||||||
}
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub(crate) struct FogState {
|
|
||||||
#[cxx_name = "m_color"]
|
|
||||||
pub(crate) color: CColor,
|
|
||||||
#[cxx_name = "m_A"]
|
|
||||||
pub(crate) a: f32,
|
|
||||||
#[cxx_name = "m_B"]
|
|
||||||
pub(crate) b: f32,
|
|
||||||
#[cxx_name = "m_C"]
|
|
||||||
pub(crate) c: f32,
|
|
||||||
#[cxx_name = "m_mode"]
|
|
||||||
pub(crate) mode: FogMode,
|
|
||||||
}
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub(crate) struct ClipRect {
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub(crate) struct MaterialPassInfo {
|
|
||||||
tex_idx: u32,
|
|
||||||
constant_color: CColor,
|
|
||||||
has_constant_color: bool, // would love to use Option...
|
|
||||||
uv_source: u8,
|
|
||||||
uv_anim_type: u8,
|
|
||||||
uv_anim_params: [f32; 9],
|
|
||||||
normalize: bool, // TODO might be static based on uv type?
|
|
||||||
sample_alpha: bool,
|
|
||||||
}
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct MaterialInfo {
|
|
||||||
pass_info: [MaterialPassInfo; 8],
|
|
||||||
texture_ids: Vec<TextureRef>,
|
|
||||||
shader_type: u8,
|
|
||||||
blend_mode: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[namespace = "aurora::shaders"]
|
|
||||||
extern "Rust" {
|
|
||||||
fn update_model_view(mv: CMatrix4f, mv_inv: CMatrix4f);
|
|
||||||
fn update_projection(proj: CMatrix4f);
|
|
||||||
fn update_fog_state(state: FogState);
|
|
||||||
fn set_viewport(rect: CRectangle, znear: f32, zfar: f32);
|
|
||||||
fn set_scissor(x: u32, y: u32, w: u32, h: u32);
|
|
||||||
|
|
||||||
fn resolve_color(rect: ClipRect, bind: u32, clear_depth: bool);
|
|
||||||
fn resolve_depth(rect: ClipRect, bind: u32);
|
|
||||||
|
|
||||||
fn add_material_set(materials: Vec<MaterialInfo>) -> u32;
|
|
||||||
fn add_model(verts: &[u8], indices: &[u8]) -> u32;
|
|
||||||
|
|
||||||
fn queue_aabb(aabb: CAABox, color: CColor, z_only: bool);
|
|
||||||
fn queue_fog_volume_plane(verts: &CxxVector<CVector4f>, pass: u8);
|
|
||||||
fn queue_fog_volume_filter(color: CColor, two_way: bool);
|
|
||||||
fn queue_textured_quad_verts(
|
|
||||||
filter_type: CameraFilterType,
|
|
||||||
texture: TextureRef,
|
|
||||||
z_comparison: ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
color: CColor,
|
|
||||||
pos: &[CVector3f],
|
|
||||||
uvs: &[CVector2f],
|
|
||||||
lod: f32,
|
|
||||||
);
|
|
||||||
fn queue_textured_quad(
|
|
||||||
filter_type: CameraFilterType,
|
|
||||||
texture: TextureRef,
|
|
||||||
z_comparison: ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
color: CColor,
|
|
||||||
uv_scale: f32,
|
|
||||||
rect: CRectangle,
|
|
||||||
z: f32,
|
|
||||||
);
|
|
||||||
fn queue_colored_quad_verts(
|
|
||||||
filter_type: CameraFilterType,
|
|
||||||
z_comparison: ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
color: CColor,
|
|
||||||
pos: &[CVector3f],
|
|
||||||
);
|
|
||||||
fn queue_colored_quad(
|
|
||||||
filter_type: CameraFilterType,
|
|
||||||
z_comparison: ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
color: CColor,
|
|
||||||
rect: CRectangle,
|
|
||||||
z: f32,
|
|
||||||
);
|
|
||||||
fn queue_movie_player(
|
|
||||||
tex_y: TextureRef,
|
|
||||||
tex_u: TextureRef,
|
|
||||||
tex_v: TextureRef,
|
|
||||||
color: CColor,
|
|
||||||
h_pad: f32,
|
|
||||||
v_pad: f32,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn create_static_texture_2d(
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
mips: u32,
|
|
||||||
format: TextureFormat,
|
|
||||||
data: &[u8],
|
|
||||||
label: &str,
|
|
||||||
) -> TextureRef;
|
|
||||||
fn create_dynamic_texture_2d(
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
mips: u32,
|
|
||||||
format: TextureFormat,
|
|
||||||
label: &str,
|
|
||||||
) -> TextureRef;
|
|
||||||
fn create_render_texture(
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
// clamp_mode: TextureClampMode,
|
|
||||||
color_bind_count: u32,
|
|
||||||
depth_bind_count: u32,
|
|
||||||
label: &str,
|
|
||||||
) -> TextureRef;
|
|
||||||
fn write_texture(handle: TextureRef, data: &[u8]);
|
|
||||||
fn drop_texture(handle: TextureRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl bytemuck::Zeroable for ffi::FogMode {}
|
|
||||||
unsafe impl bytemuck::Pod for ffi::FogMode {}
|
|
||||||
unsafe impl bytemuck::Zeroable for ffi::FogState {}
|
|
||||||
unsafe impl bytemuck::Pod for ffi::FogState {}
|
|
||||||
impl Default for ffi::FogState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { color: Default::default(), a: 0.0, b: 0.5, c: 0.0, mode: ffi::FogMode::None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Into<u32> for ffi::TextureFormat {
|
|
||||||
// noinspection RsUnreachablePatterns
|
|
||||||
fn into(self) -> u32 {
|
|
||||||
match self {
|
|
||||||
ffi::TextureFormat::RGBA8 => 1,
|
|
||||||
ffi::TextureFormat::R8 => 2,
|
|
||||||
ffi::TextureFormat::R32Float => 3,
|
|
||||||
ffi::TextureFormat::DXT1 => 4,
|
|
||||||
ffi::TextureFormat::DXT3 => 5,
|
|
||||||
ffi::TextureFormat::DXT5 => 6,
|
|
||||||
ffi::TextureFormat::BPTC => 7,
|
|
||||||
_ => panic!("Invalid texture format {:?}", self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
use crate::zeus::CColor;
|
|
||||||
|
|
||||||
pub(crate) fn queue_fog_volume_filter(color: CColor, two_way: bool) {}
|
|
|
@ -1,5 +0,0 @@
|
||||||
use cxx::CxxVector;
|
|
||||||
|
|
||||||
use crate::zeus::CVector4f;
|
|
||||||
|
|
||||||
pub(crate) fn queue_fog_volume_plane(verts: &CxxVector<CVector4f>, pass: u8) {}
|
|
|
@ -1,460 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::{HashMap, VecDeque},
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
ops::Range,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bytemuck::Pod;
|
|
||||||
use bytemuck_derive::{Pod, Zeroable};
|
|
||||||
use cxx::ExternType;
|
|
||||||
use cxxbridge::ffi;
|
|
||||||
use twox_hash::Xxh3Hash64;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
gpu::GraphicsConfig,
|
|
||||||
shaders::texture::{RenderTexture, TextureWithView},
|
|
||||||
zeus::{CColor, CMatrix4f, CRectangle, CVector2f, CVector3f, IDENTITY_MATRIX4F},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod aabb;
|
|
||||||
mod cxxbridge;
|
|
||||||
mod fog_volume_filter;
|
|
||||||
mod fog_volume_plane;
|
|
||||||
mod model;
|
|
||||||
mod movie_player;
|
|
||||||
mod texture;
|
|
||||||
mod textured_quad;
|
|
||||||
mod colored_quad;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
enum ColoredStripMode {
|
|
||||||
Alpha,
|
|
||||||
Additive,
|
|
||||||
FullAdditive,
|
|
||||||
Subtractive,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct ColoredStripVert {
|
|
||||||
position: CVector3f,
|
|
||||||
color: CColor,
|
|
||||||
uv: CVector2f,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
enum Command {
|
|
||||||
SetViewport(CRectangle, f32, f32),
|
|
||||||
SetScissor(u32, u32, u32, u32),
|
|
||||||
Draw(ShaderDrawCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
enum ShaderDrawCommand {
|
|
||||||
Aabb(aabb::DrawData),
|
|
||||||
CameraBlurFilter {
|
|
||||||
amount: f32,
|
|
||||||
clear_depth: bool,
|
|
||||||
},
|
|
||||||
ColoredQuad(colored_quad::DrawData),
|
|
||||||
ColoredStripFilter {
|
|
||||||
mode: ColoredStripMode,
|
|
||||||
verts: Vec<ColoredStripVert>,
|
|
||||||
color: CColor,
|
|
||||||
},
|
|
||||||
Decal { /* TODO */ },
|
|
||||||
ElementGen { /* TODO */ },
|
|
||||||
EnergyBar { /* TODO */ },
|
|
||||||
EnvFx { /* TODO */ },
|
|
||||||
FluidPlane { /* TODO */ },
|
|
||||||
FogVolumeFilter {
|
|
||||||
two_way: bool,
|
|
||||||
color: CColor,
|
|
||||||
},
|
|
||||||
FogVolumePlane {
|
|
||||||
pass: u8,
|
|
||||||
verts: Vec<CVector3f>,
|
|
||||||
},
|
|
||||||
LineRenderer { /* TODO */ },
|
|
||||||
MapSurface { /* TODO */ },
|
|
||||||
Model {
|
|
||||||
pipeline_id: u32,
|
|
||||||
material_id: u32,
|
|
||||||
ambient_color: CColor,
|
|
||||||
lights: u32,
|
|
||||||
post_type: u32,
|
|
||||||
game_blend_mode: u32,
|
|
||||||
model_flags: u32,
|
|
||||||
},
|
|
||||||
MoviePlayer(movie_player::DrawData),
|
|
||||||
NES { /* TODO */ },
|
|
||||||
ParticleSwoosh { /* TODO */ },
|
|
||||||
PhazonSuitFilter { /* TODO */ },
|
|
||||||
RadarPaint { /* TODO */ },
|
|
||||||
RandomStaticFilter { /* TODO */ },
|
|
||||||
ScanLinesFilter { /* TODO */ },
|
|
||||||
TextSupport { /* TODO */ },
|
|
||||||
TexturedQuad(textured_quad::DrawData),
|
|
||||||
ThermalCold,
|
|
||||||
ThermalHot,
|
|
||||||
WorldShadow {
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
extent: f32,
|
|
||||||
},
|
|
||||||
XRayBlur {
|
|
||||||
palette_tex: u32, /* TODO */
|
|
||||||
amount: f32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RenderState {
|
|
||||||
device: Arc<wgpu::Device>,
|
|
||||||
queue: Arc<wgpu::Queue>,
|
|
||||||
graphics_config: GraphicsConfig,
|
|
||||||
pipelines: HashMap<u64, PipelineHolder>,
|
|
||||||
current_pipeline: u64,
|
|
||||||
uniform_alignment: usize,
|
|
||||||
storage_alignment: usize,
|
|
||||||
buffers: BuiltBuffers,
|
|
||||||
commands: VecDeque<Command>,
|
|
||||||
textures: HashMap<u32, TextureWithView>,
|
|
||||||
render_textures: HashMap<u32, RenderTexture>,
|
|
||||||
// Shader states
|
|
||||||
aabb: aabb::State,
|
|
||||||
colored_quad: colored_quad::State,
|
|
||||||
textured_quad: textured_quad::State,
|
|
||||||
movie_player: movie_player::State,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_state(
|
|
||||||
device: Arc<wgpu::Device>,
|
|
||||||
queue: Arc<wgpu::Queue>,
|
|
||||||
graphics_config: &GraphicsConfig,
|
|
||||||
) {
|
|
||||||
let limits = device.limits();
|
|
||||||
let buffers = BuiltBuffers {
|
|
||||||
uniform_buffer: device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: Some("Shared Uniform Buffer"),
|
|
||||||
size: 134_217_728, // 128mb
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
}),
|
|
||||||
vertex_buffer: device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: Some("Shared Vertex Buffer"),
|
|
||||||
size: 16_777_216, // 16mb
|
|
||||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
let aabb = aabb::construct_state(&device, &queue, &buffers, graphics_config);
|
|
||||||
let colored_quad = colored_quad::construct_state(&device, &queue, &buffers, graphics_config);
|
|
||||||
let textured_quad = textured_quad::construct_state(&device, &queue, &buffers, graphics_config);
|
|
||||||
let movie_player = movie_player::construct_state(&device, &queue, &buffers, graphics_config);
|
|
||||||
let mut state = RenderState {
|
|
||||||
device: device.clone(),
|
|
||||||
queue: queue.clone(),
|
|
||||||
graphics_config: graphics_config.clone(),
|
|
||||||
pipelines: Default::default(),
|
|
||||||
current_pipeline: u64::MAX,
|
|
||||||
uniform_alignment: limits.min_uniform_buffer_offset_alignment as usize,
|
|
||||||
storage_alignment: limits.min_storage_buffer_offset_alignment as usize,
|
|
||||||
buffers,
|
|
||||||
commands: Default::default(),
|
|
||||||
textures: Default::default(),
|
|
||||||
render_textures: Default::default(),
|
|
||||||
aabb,
|
|
||||||
colored_quad,
|
|
||||||
textured_quad,
|
|
||||||
movie_player,
|
|
||||||
};
|
|
||||||
for config in aabb::INITIAL_PIPELINES {
|
|
||||||
construct_pipeline(&mut state, config);
|
|
||||||
}
|
|
||||||
for config in textured_quad::INITIAL_PIPELINES {
|
|
||||||
construct_pipeline(&mut state, config);
|
|
||||||
}
|
|
||||||
for config in movie_player::INITIAL_PIPELINES {
|
|
||||||
construct_pipeline(&mut state, config);
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
STATE = Some(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static mut STATE: Option<RenderState> = None;
|
|
||||||
|
|
||||||
#[derive(Default, Pod, Zeroable, Copy, Clone)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct GlobalUniform {
|
|
||||||
mv: CMatrix4f,
|
|
||||||
mv_inv: CMatrix4f,
|
|
||||||
proj: CMatrix4f,
|
|
||||||
ambient: CColor, // TODO can this be combined with model?
|
|
||||||
lightmap_mul: CColor,
|
|
||||||
fog: ffi::FogState,
|
|
||||||
}
|
|
||||||
#[derive(Default)]
|
|
||||||
struct GlobalBuffers {
|
|
||||||
uniforms: Vec<u8>,
|
|
||||||
verts: Vec<u8>,
|
|
||||||
|
|
||||||
global_current: GlobalUniform,
|
|
||||||
global_dirty: bool,
|
|
||||||
last_global: Option<Range<u64>>,
|
|
||||||
}
|
|
||||||
static mut GLOBAL_BUFFERS: GlobalBuffers = GlobalBuffers {
|
|
||||||
uniforms: vec![],
|
|
||||||
verts: vec![],
|
|
||||||
global_current: GlobalUniform {
|
|
||||||
mv: IDENTITY_MATRIX4F,
|
|
||||||
mv_inv: IDENTITY_MATRIX4F,
|
|
||||||
proj: IDENTITY_MATRIX4F,
|
|
||||||
ambient: CColor::new(0.0, 0.0, 0.0, 1.0),
|
|
||||||
lightmap_mul: CColor::new(0.0, 0.0, 0.0, 1.0),
|
|
||||||
fog: ffi::FogState {
|
|
||||||
color: CColor::new(0.0, 0.0, 0.0, 0.0),
|
|
||||||
a: 0.0,
|
|
||||||
b: 0.5,
|
|
||||||
c: 0.0,
|
|
||||||
mode: ffi::FogMode::None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
global_dirty: false,
|
|
||||||
last_global: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) struct PipelineHolder {
|
|
||||||
pipeline: wgpu::RenderPipeline,
|
|
||||||
}
|
|
||||||
pub(crate) struct BuiltBuffers {
|
|
||||||
uniform_buffer: wgpu::Buffer,
|
|
||||||
vertex_buffer: wgpu::Buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
const EMPTY_SLICE: &[u8] = &[0u8; 256];
|
|
||||||
|
|
||||||
fn push_value<T: Pod>(target: &mut Vec<u8>, buf: &T, alignment: usize) -> Range<u64> {
|
|
||||||
let size_of = std::mem::size_of::<T>();
|
|
||||||
let padding = if alignment == 0 { 0 } else { alignment - size_of % alignment };
|
|
||||||
let begin = target.len() as u64;
|
|
||||||
target.extend_from_slice(bytemuck::bytes_of(buf));
|
|
||||||
if padding > 0 {
|
|
||||||
target.extend_from_slice(&EMPTY_SLICE[..padding]);
|
|
||||||
}
|
|
||||||
begin..begin + size_of as u64
|
|
||||||
}
|
|
||||||
fn push_slice<T: Pod>(target: &mut Vec<u8>, buf: &[T], alignment: usize) -> Range<u64> {
|
|
||||||
let size_of = std::mem::size_of::<T>();
|
|
||||||
let padding = if alignment == 0 { 0 } else { alignment - size_of % alignment };
|
|
||||||
let begin = target.len() as u64;
|
|
||||||
target.extend_from_slice(bytemuck::cast_slice(buf));
|
|
||||||
if padding > 0 {
|
|
||||||
target.extend_from_slice(&EMPTY_SLICE[..padding]);
|
|
||||||
}
|
|
||||||
begin..begin + size_of as u64 * buf.len() as u64
|
|
||||||
}
|
|
||||||
fn push_verts<T: Pod>(buf: &[T]) -> Range<u64> {
|
|
||||||
let global_buffers = unsafe { &mut GLOBAL_BUFFERS };
|
|
||||||
push_slice(&mut global_buffers.verts, buf, 0 /* TODO? */)
|
|
||||||
}
|
|
||||||
fn push_uniform<T: Pod>(buf: &T) -> Range<u64> {
|
|
||||||
let global_buffers = unsafe { &mut GLOBAL_BUFFERS };
|
|
||||||
push_value(&mut global_buffers.uniforms, buf, unsafe {
|
|
||||||
STATE.as_ref().unwrap().uniform_alignment
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct PipelineRef {
|
|
||||||
id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) enum PipelineCreateCommand {
|
|
||||||
Aabb(aabb::PipelineConfig),
|
|
||||||
ColoredQuad(colored_quad::PipelineConfig),
|
|
||||||
TexturedQuad(textured_quad::PipelineConfig),
|
|
||||||
MoviePlayer(movie_player::PipelineConfig),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn hash_with_seed<T: Hash>(value: &T, seed: u64) -> u64 {
|
|
||||||
let mut state = Xxh3Hash64::with_seed(seed);
|
|
||||||
value.hash(&mut state);
|
|
||||||
state.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn construct_pipeline(state: &mut RenderState, cmd: &PipelineCreateCommand) -> u64 {
|
|
||||||
let id = match cmd {
|
|
||||||
PipelineCreateCommand::Aabb(ref config) => hash_with_seed(config, 0xAABB),
|
|
||||||
PipelineCreateCommand::ColoredQuad(ref config) => hash_with_seed(config, 0xC013B),
|
|
||||||
PipelineCreateCommand::TexturedQuad(ref config) => hash_with_seed(config, 0xEEAD),
|
|
||||||
PipelineCreateCommand::MoviePlayer(ref config) => hash_with_seed(config, 0xFAAE),
|
|
||||||
};
|
|
||||||
if !state.pipelines.contains_key(&id) {
|
|
||||||
let pipeline = match cmd {
|
|
||||||
PipelineCreateCommand::Aabb(ref config) => aabb::construct_pipeline(
|
|
||||||
state.device.as_ref(),
|
|
||||||
&state.graphics_config,
|
|
||||||
&state.aabb,
|
|
||||||
config,
|
|
||||||
),
|
|
||||||
PipelineCreateCommand::ColoredQuad(ref config) => colored_quad::construct_pipeline(
|
|
||||||
state.device.as_ref(),
|
|
||||||
&state.graphics_config,
|
|
||||||
&state.colored_quad,
|
|
||||||
config,
|
|
||||||
),
|
|
||||||
PipelineCreateCommand::TexturedQuad(ref config) => textured_quad::construct_pipeline(
|
|
||||||
state.device.as_ref(),
|
|
||||||
&state.graphics_config,
|
|
||||||
&state.textured_quad,
|
|
||||||
config,
|
|
||||||
),
|
|
||||||
PipelineCreateCommand::MoviePlayer(ref config) => movie_player::construct_pipeline(
|
|
||||||
state.device.as_ref(),
|
|
||||||
&state.graphics_config,
|
|
||||||
&state.movie_player,
|
|
||||||
config,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
state.pipelines.insert(id, pipeline);
|
|
||||||
}
|
|
||||||
id
|
|
||||||
}
|
|
||||||
fn pipeline_ref(cmd: &PipelineCreateCommand) -> PipelineRef {
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
// TODO queue for creation if not found
|
|
||||||
let id = construct_pipeline(state, cmd);
|
|
||||||
PipelineRef { id }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bind_pipeline(pipeline_ref: PipelineRef, pass: &mut wgpu::RenderPass) -> bool {
|
|
||||||
let state = unsafe { STATE.as_ref().unwrap_unchecked() };
|
|
||||||
if pipeline_ref.id == state.current_pipeline {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if let Some(PipelineHolder { pipeline }) = state.pipelines.get(&pipeline_ref.id) {
|
|
||||||
pass.set_pipeline(pipeline);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn render_into_pass(pass: &mut wgpu::RenderPass) {
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
{
|
|
||||||
let global_buffers = unsafe { &mut GLOBAL_BUFFERS };
|
|
||||||
state.queue.write_buffer(&state.buffers.vertex_buffer, 0, &global_buffers.verts);
|
|
||||||
state.queue.write_buffer(&state.buffers.uniform_buffer, 0, &global_buffers.uniforms);
|
|
||||||
global_buffers.verts.clear();
|
|
||||||
global_buffers.uniforms.clear();
|
|
||||||
global_buffers.global_dirty = true;
|
|
||||||
}
|
|
||||||
for cmd in &state.commands {
|
|
||||||
match cmd {
|
|
||||||
Command::SetViewport(rect, znear, zfar) => {
|
|
||||||
pass.set_viewport(
|
|
||||||
rect.position.x,
|
|
||||||
rect.position.y,
|
|
||||||
rect.size.x,
|
|
||||||
rect.size.y,
|
|
||||||
*znear,
|
|
||||||
*zfar,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Command::SetScissor(x, y, w, h) => {
|
|
||||||
pass.set_scissor_rect(*x, *y, *w, *h);
|
|
||||||
}
|
|
||||||
Command::Draw(cmd) => match cmd {
|
|
||||||
ShaderDrawCommand::Aabb(data) => {
|
|
||||||
aabb::draw_aabb(data, &state.aabb, pass, &state.buffers);
|
|
||||||
}
|
|
||||||
ShaderDrawCommand::ColoredQuad(data) => {
|
|
||||||
colored_quad::draw_colored_quad(data, &state.colored_quad, pass, &state.buffers);
|
|
||||||
}
|
|
||||||
ShaderDrawCommand::TexturedQuad(data) => {
|
|
||||||
textured_quad::draw_textured_quad(
|
|
||||||
data,
|
|
||||||
&state.textured_quad,
|
|
||||||
pass,
|
|
||||||
&state.buffers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ShaderDrawCommand::MoviePlayer(data) => {
|
|
||||||
movie_player::draw_movie_player(
|
|
||||||
data,
|
|
||||||
&state.movie_player,
|
|
||||||
pass,
|
|
||||||
&state.buffers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.commands.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_model_view(mv: CMatrix4f, mv_inv: CMatrix4f) {
|
|
||||||
let global_buffers = unsafe { &mut GLOBAL_BUFFERS };
|
|
||||||
global_buffers.global_current.mv = mv;
|
|
||||||
global_buffers.global_current.mv_inv = mv_inv;
|
|
||||||
global_buffers.global_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_projection(proj: CMatrix4f) {
|
|
||||||
let global_buffers = unsafe { &mut GLOBAL_BUFFERS };
|
|
||||||
global_buffers.global_current.proj = proj;
|
|
||||||
global_buffers.global_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_fog_state(state: ffi::FogState) {
|
|
||||||
let global_buffers = unsafe { &mut GLOBAL_BUFFERS };
|
|
||||||
global_buffers.global_current.fog = state;
|
|
||||||
global_buffers.global_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_projection_matrix() -> CMatrix4f {
|
|
||||||
let global_buffers = unsafe { &GLOBAL_BUFFERS };
|
|
||||||
global_buffers.global_current.proj
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_combined_matrix() -> CMatrix4f {
|
|
||||||
let global_buffers = unsafe { &GLOBAL_BUFFERS };
|
|
||||||
CMatrix4f::from(
|
|
||||||
cgmath::Matrix4::from(global_buffers.global_current.proj)
|
|
||||||
* cgmath::Matrix4::from(global_buffers.global_current.mv),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finalize_global_uniform() -> Range<u64> {
|
|
||||||
let global_buffers = unsafe { &mut GLOBAL_BUFFERS };
|
|
||||||
if global_buffers.global_dirty || global_buffers.last_global.is_none() {
|
|
||||||
global_buffers.last_global = Some(push_uniform(&global_buffers.global_current));
|
|
||||||
global_buffers.global_dirty = false;
|
|
||||||
}
|
|
||||||
global_buffers.last_global.as_ref().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_draw_command(cmd: ShaderDrawCommand) {
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
state.commands.push_back(Command::Draw(cmd));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_viewport(rect: CRectangle, znear: f32, zfar: f32) {
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
state.commands.push_back(Command::SetViewport(rect, znear, zfar));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_scissor(x: u32, y: u32, w: u32, h: u32) {
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
state.commands.push_back(Command::SetScissor(x, y, w, h));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_color(rect: ffi::ClipRect, bind: u32, clear_depth: bool) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_depth(rect: ffi::ClipRect, bind: u32) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
|
@ -1,349 +0,0 @@
|
||||||
struct PassTrait {
|
|
||||||
constant_color: vec4<f32>;
|
|
||||||
tex_idx: i32;
|
|
||||||
uv_source: i32;
|
|
||||||
tcg_mtx_idx: i32;
|
|
||||||
normalize: u32;
|
|
||||||
sample_alpha: u32;
|
|
||||||
has_constant_color: u32;
|
|
||||||
};
|
|
||||||
struct TCGUniform {
|
|
||||||
mode: i32;
|
|
||||||
f0: f32;
|
|
||||||
f1: f32;
|
|
||||||
f2: f32;
|
|
||||||
f3: f32;
|
|
||||||
f4: f32;
|
|
||||||
f5: f32;
|
|
||||||
f6: f32;
|
|
||||||
f7: f32;
|
|
||||||
f8: f32;
|
|
||||||
pad1: u32;
|
|
||||||
pad2: u32;
|
|
||||||
};
|
|
||||||
struct ConstShaderTraits {
|
|
||||||
shader_type: u32;
|
|
||||||
world_shadow: u32;
|
|
||||||
short_uvs: u32;
|
|
||||||
alpha_discard: u32;
|
|
||||||
samus_reflection: u32;
|
|
||||||
@align(16) tcgs: @stride(48) array<TCGUniform, 4>;
|
|
||||||
};
|
|
||||||
struct MutShaderTraits {
|
|
||||||
post_type: u32;
|
|
||||||
game_blend_mode: u32;
|
|
||||||
model_flags: u32;
|
|
||||||
};
|
|
||||||
struct ConstTraits {
|
|
||||||
shader: ConstShaderTraits;
|
|
||||||
lightmap: PassTrait;
|
|
||||||
diffuse: PassTrait;
|
|
||||||
diffuse_mod: PassTrait;
|
|
||||||
emissive: PassTrait;
|
|
||||||
specular: PassTrait;
|
|
||||||
extended_specular: PassTrait;
|
|
||||||
reflection: PassTrait;
|
|
||||||
alpha: PassTrait;
|
|
||||||
alpha_mod: PassTrait;
|
|
||||||
};
|
|
||||||
@group(2) @binding(10)
|
|
||||||
var<uniform> c_traits: ConstTraits;
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
pos: vec3<f32>;
|
|
||||||
dir: vec3<f32>;
|
|
||||||
color: vec4<f32>;
|
|
||||||
lin_att: vec3<f32>;
|
|
||||||
ang_att: vec3<f32>;
|
|
||||||
};
|
|
||||||
struct FogState {
|
|
||||||
color: vec4<f32>;
|
|
||||||
a: f32;
|
|
||||||
b: f32;
|
|
||||||
c: f32;
|
|
||||||
mode: u32;
|
|
||||||
};
|
|
||||||
struct ModelUniform {
|
|
||||||
position: vec3<f32>;
|
|
||||||
orientation: vec3<f32>;
|
|
||||||
scale: vec3<f32>;
|
|
||||||
ambient_color: vec4<f32>;
|
|
||||||
lights: array<Light, 8>;
|
|
||||||
};
|
|
||||||
struct GlobalUniform {
|
|
||||||
view: mat4x4<f32>;
|
|
||||||
proj: mat4x4<f32>;
|
|
||||||
ambient: vec4<f32>;
|
|
||||||
lightmap_mul: vec4<f32>;
|
|
||||||
fog: FogState;
|
|
||||||
seconds: f32;
|
|
||||||
};
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> u_model: ModelUniform;
|
|
||||||
@group(1) @binding(1)
|
|
||||||
var<uniform> u_global: GlobalUniform;
|
|
||||||
@group(1) @binding(2)
|
|
||||||
var<uniform> u_traits: MutShaderTraits;
|
|
||||||
|
|
||||||
@group(2) @binding(0)
|
|
||||||
var t_lightmap: texture_2d<f32>;
|
|
||||||
@group(2) @binding(1)
|
|
||||||
var t_diffuse: texture_2d<f32>;
|
|
||||||
@group(2) @binding(2)
|
|
||||||
var t_diffuse_mod: texture_2d<f32>;
|
|
||||||
@group(2) @binding(3)
|
|
||||||
var t_emissive: texture_2d<f32>;
|
|
||||||
@group(2) @binding(4)
|
|
||||||
var t_specular: texture_2d<f32>;
|
|
||||||
@group(2) @binding(5)
|
|
||||||
var t_extended_specular: texture_2d<f32>;
|
|
||||||
@group(2) @binding(6)
|
|
||||||
var t_reflection: texture_2d<f32>;
|
|
||||||
@group(2) @binding(7)
|
|
||||||
var t_alpha: texture_2d<f32>;
|
|
||||||
@group(2) @binding(8)
|
|
||||||
var t_alpha_mod: texture_2d<f32>;
|
|
||||||
@group(2) @binding(9)
|
|
||||||
var t_material: texture_2d<f32>;
|
|
||||||
|
|
||||||
@group(3) @binding(0)
|
|
||||||
var s_repeat: sampler;
|
|
||||||
@group(3) @binding(1)
|
|
||||||
var s_clamp: sampler;
|
|
||||||
@group(3) @binding(2)
|
|
||||||
var s_reflect: sampler;
|
|
||||||
@group(3) @binding(3)
|
|
||||||
var s_clamp_edge: sampler;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position: vec4<f32>;
|
|
||||||
@location(0) lighting: vec3<f32>;
|
|
||||||
@location(1) uv_lightmap: vec2<f32>;
|
|
||||||
@location(2) uv_diffuse: vec2<f32>;
|
|
||||||
@location(3) uv_diffuse_mod: vec2<f32>;
|
|
||||||
@location(4) uv_emissive: vec2<f32>;
|
|
||||||
@location(5) uv_specular: vec2<f32>;
|
|
||||||
@location(6) uv_extended_specular: vec2<f32>;
|
|
||||||
@location(7) uv_reflection: vec2<f32>;
|
|
||||||
@location(8) uv_alpha: vec2<f32>;
|
|
||||||
@location(9) uv_alpha_mod: vec2<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn make_rotate_x(r: f32) -> mat4x4<f32> {
|
|
||||||
var s = sin(r);
|
|
||||||
var c = cos(r);
|
|
||||||
return mat4x4<f32>(
|
|
||||||
vec4<f32>(1.0, 0.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, c, s, 0.0),
|
|
||||||
vec4<f32>(0.0, -s, c, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 0.0, 1.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_rotate_y(r: f32) -> mat4x4<f32> {
|
|
||||||
var s = sin(r);
|
|
||||||
var c = cos(r);
|
|
||||||
return mat4x4<f32>(
|
|
||||||
vec4<f32>(c, 0.0, -s, 0.0),
|
|
||||||
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(s, 0.0, c, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 0.0, 1.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_rotate_z(r: f32) -> mat4x4<f32> {
|
|
||||||
var s = sin(r);
|
|
||||||
var c = cos(r);
|
|
||||||
return mat4x4<f32>(
|
|
||||||
vec4<f32>(c, s, 0.0, 0.0),
|
|
||||||
vec4<f32>(-s, c, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 0.0, 1.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_rotate(r: vec3<f32>) -> mat4x4<f32> {
|
|
||||||
return make_rotate_z(r.z)
|
|
||||||
* make_rotate_y(r.y)
|
|
||||||
* make_rotate_x(r.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_scale(s: vec3<f32>) -> mat4x4<f32> {
|
|
||||||
return mat4x4<f32>(
|
|
||||||
vec4<f32>(s.x, 0.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, s.y, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, s.z, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 0.0, 1.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_translate(translate: vec3<f32>) -> mat4x4<f32> {
|
|
||||||
return mat4x4<f32>(
|
|
||||||
vec4<f32>(1.0, 0.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
|
||||||
vec4<f32>(translate, 1.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TCGMatrix {
|
|
||||||
mtx: mat4x4<f32>;
|
|
||||||
post_mtx: mat4x4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
let identity_mtx: mat4x4<f32> = mat4x4<f32>(
|
|
||||||
vec4<f32>(1.0, 0.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 0.0, 1.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
fn process_uv_anim(
|
|
||||||
model_matrix: ptr<function, mat4x4<f32>>,
|
|
||||||
mv_inv: ptr<function, mat4x4<f32>>,
|
|
||||||
traits: PassTrait,
|
|
||||||
) -> TCGMatrix {
|
|
||||||
var out = TCGMatrix(identity_mtx, identity_mtx);
|
|
||||||
var tu = c_traits.shader.tcgs[traits.tcg_mtx_idx];
|
|
||||||
switch (tu.mode) {
|
|
||||||
case 0: {
|
|
||||||
out.mtx = *mv_inv;
|
|
||||||
out.mtx[3][3] = 1.0;
|
|
||||||
out.post_mtx[0][0] = 0.5;
|
|
||||||
out.post_mtx[1][1] = 0.5;
|
|
||||||
out.post_mtx[3][0] = 0.5;
|
|
||||||
out.post_mtx[3][1] = 0.5;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
out.mtx = *mv_inv;
|
|
||||||
out.mtx[3] = u_global.view * vec4<f32>(u_model.position, 1.0);
|
|
||||||
out.mtx[3][3] = 1.0;
|
|
||||||
out.post_mtx[0][0] = 0.5;
|
|
||||||
out.post_mtx[1][1] = 0.5;
|
|
||||||
out.post_mtx[3][0] = 0.5;
|
|
||||||
out.post_mtx[3][1] = 0.5;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
out.mtx[3][0] = u_global.seconds * tu.f2 + tu.f0;
|
|
||||||
out.mtx[3][1] = u_global.seconds * tu.f3 + tu.f1;
|
|
||||||
}
|
|
||||||
case 3: {
|
|
||||||
var angle = u_global.seconds * tu.f1 + tu.f0;
|
|
||||||
var acos = cos(angle);
|
|
||||||
var asin = sin(angle);
|
|
||||||
out.mtx[0][0] = acos;
|
|
||||||
out.mtx[0][1] = asin;
|
|
||||||
out.mtx[1][0] = -asin;
|
|
||||||
out.mtx[1][1] = acos;
|
|
||||||
out.mtx[3][0] = (1.0 - (acos - asin)) * 0.5;
|
|
||||||
out.mtx[3][1] = (1.0 - (asin + acos)) * 0.5;
|
|
||||||
}
|
|
||||||
case 4: {
|
|
||||||
var n: f32 = tu.f2 * tu.f0 * (tu.f3 + u_global.seconds);
|
|
||||||
out.mtx[3][0] = trunc(tu.f1 * fract(n)) * tu.f2;
|
|
||||||
}
|
|
||||||
case 5: {
|
|
||||||
var n: f32 = tu.f2 * tu.f0 * (tu.f3 + u_global.seconds);
|
|
||||||
out.mtx[3][1] = trunc(tu.f1 * fract(n)) * tu.f2;
|
|
||||||
}
|
|
||||||
case 6: {
|
|
||||||
let pos = u_model.position;
|
|
||||||
out.mtx = *model_matrix;
|
|
||||||
out.mtx[3] = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
||||||
out.post_mtx[0][0] = 0.5;
|
|
||||||
out.post_mtx[1][1] = 0.0;
|
|
||||||
out.post_mtx[2][1] = 0.5;
|
|
||||||
out.post_mtx[3][0] = pos.x * 0.05;
|
|
||||||
out.post_mtx[3][1] = pos.y * 0.05;
|
|
||||||
}
|
|
||||||
default: {}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn t_sample(
|
|
||||||
traits: PassTrait,
|
|
||||||
tex: texture_2d<f32>,
|
|
||||||
uv: vec2<f32>,
|
|
||||||
) -> vec3<f32> {
|
|
||||||
// For control flow uniformity, we always have to sample
|
|
||||||
var color: vec4<f32> = textureSample(tex, s_repeat, uv);
|
|
||||||
if (traits.has_constant_color == 1u) {
|
|
||||||
color = traits.constant_color;
|
|
||||||
}
|
|
||||||
if (traits.sample_alpha == 1u) {
|
|
||||||
return vec3<f32>(color.w);
|
|
||||||
}
|
|
||||||
return color.xyz;
|
|
||||||
}
|
|
||||||
|
|
||||||
let kRGBToYPrime: vec3<f32> = vec3<f32>(0.299, 0.587, 0.114);
|
|
||||||
|
|
||||||
fn t_sample_alpha(
|
|
||||||
traits: PassTrait,
|
|
||||||
tex: texture_2d<f32>,
|
|
||||||
uv: vec2<f32>,
|
|
||||||
) -> f32 {
|
|
||||||
// For control flow uniformity, we always have to sample
|
|
||||||
var color: vec4<f32> = textureSample(tex, s_repeat, uv);
|
|
||||||
if (traits.has_constant_color == 1u) {
|
|
||||||
color = traits.constant_color;
|
|
||||||
}
|
|
||||||
if (traits.sample_alpha == 1u) {
|
|
||||||
return color.w;
|
|
||||||
}
|
|
||||||
return dot(color.xyz, kRGBToYPrime);
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(fragment)
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
var color: vec4<f32>;
|
|
||||||
|
|
||||||
let mat_sample = textureSample(t_material, s_repeat, in.uv_diffuse);
|
|
||||||
|
|
||||||
if (c_traits.shader.shader_type == 0u) {
|
|
||||||
// Invalid
|
|
||||||
color = vec4<f32>(
|
|
||||||
t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse),
|
|
||||||
t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
|
|
||||||
);
|
|
||||||
} else if (c_traits.shader.shader_type == 1u) {
|
|
||||||
// RetroShader
|
|
||||||
var rgb = t_sample(c_traits.lightmap, t_lightmap, in.uv_lightmap)
|
|
||||||
* u_global.lightmap_mul.xyz + in.lighting;
|
|
||||||
rgb = rgb * t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse)
|
|
||||||
+ t_sample(c_traits.emissive, t_emissive, in.uv_emissive);
|
|
||||||
let specular = t_sample(c_traits.specular, t_specular, in.uv_specular)
|
|
||||||
+ t_sample(c_traits.extended_specular, t_extended_specular, in.uv_extended_specular)
|
|
||||||
* in.lighting;
|
|
||||||
rgb = rgb + specular * t_sample(c_traits.reflection, t_reflection, in.uv_reflection);
|
|
||||||
// TODO DynReflectionSample
|
|
||||||
color = vec4<f32>(rgb, t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
|
|
||||||
* t_sample_alpha(c_traits.alpha_mod, t_alpha_mod, in.uv_alpha_mod));
|
|
||||||
} else if (c_traits.shader.shader_type == 2u) {
|
|
||||||
// RetroDynamicShader
|
|
||||||
var rgb = t_sample(c_traits.lightmap, t_lightmap, in.uv_lightmap)
|
|
||||||
* u_global.lightmap_mul.xyz + in.lighting;
|
|
||||||
rgb = rgb * t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse)
|
|
||||||
+ t_sample(c_traits.emissive, t_emissive, in.uv_emissive);
|
|
||||||
let specular = t_sample(c_traits.specular, t_specular, in.uv_specular)
|
|
||||||
+ t_sample(c_traits.extended_specular, t_extended_specular, in.uv_extended_specular)
|
|
||||||
* in.lighting;
|
|
||||||
rgb = rgb + specular * t_sample(c_traits.reflection, t_reflection, in.uv_reflection);
|
|
||||||
// TODO DynReflectionSample
|
|
||||||
color = vec4<f32>(rgb, t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
|
|
||||||
* t_sample_alpha(c_traits.alpha_mod, t_alpha_mod, in.uv_alpha_mod));
|
|
||||||
} else {
|
|
||||||
// TODO RetroDynamicAlphaShader + RetroDynamicCharacterShader
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO fog shader
|
|
||||||
|
|
||||||
// TODO post type
|
|
||||||
|
|
||||||
if (c_traits.shader.alpha_discard == 1u && color.w < 0.25) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::shaders::cxxbridge::ffi;
|
|
||||||
|
|
||||||
struct MaterialShaderData {
|
|
||||||
shader_type: u32, // 5 values (incl. Invalid)
|
|
||||||
world_shadow: u32, // bool
|
|
||||||
alpha_discard: u32, // bool
|
|
||||||
samus_reflection: u32, // bool
|
|
||||||
}
|
|
||||||
struct MaterialPassData {
|
|
||||||
constant_color: [f32; 4],
|
|
||||||
has_constant_color: u32, // bool
|
|
||||||
uv_source: i32, // 8 values
|
|
||||||
normalize: u32, // bool
|
|
||||||
sample_alpha: u32, // bool
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ModelRenderData {
|
|
||||||
// position: [f32; 3], // for UV anims
|
|
||||||
ambient_color: [f32; 4],
|
|
||||||
lights: [f32; 8], // TODO
|
|
||||||
post_type: u32,
|
|
||||||
game_blend_mode: u32,
|
|
||||||
// model_flags: u32, TODO only needed for render
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
struct ModelPipelineConfig {
|
|
||||||
material_blend_mode: u32,
|
|
||||||
material_depth_write: bool,
|
|
||||||
render_blend_mode: u32,
|
|
||||||
render_model_flags: u32,
|
|
||||||
render_cull_mode: u32,
|
|
||||||
render_dst_alpha: bool,
|
|
||||||
render_color_update: bool,
|
|
||||||
render_alpha_update: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn add_material_set(materials: Vec<ffi::MaterialInfo>) -> u32 { return u32::MAX; }
|
|
||||||
pub(crate) fn add_model(verts: &[u8], indices: &[u8]) -> u32 { return u32::MAX; }
|
|
|
@ -1,141 +0,0 @@
|
||||||
struct Vec3Block {
|
|
||||||
data: @stride(16) array<vec3<f32>>;
|
|
||||||
};
|
|
||||||
struct Vec2Block {
|
|
||||||
data: @stride(8) array<vec2<f32>>;
|
|
||||||
};
|
|
||||||
struct TCGUniforms {
|
|
||||||
data: array<TCGUniform>;
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<storage, read> v_verts: Vec3Block;
|
|
||||||
@group(0) @binding(1)
|
|
||||||
var<storage, read> v_norms: Vec3Block;
|
|
||||||
@group(0) @binding(2)
|
|
||||||
var<storage, read> v_uvs: Vec2Block;
|
|
||||||
@group(0) @binding(3)
|
|
||||||
var<storage, read> v_short_uvs: Vec2Block;
|
|
||||||
//@group(0) @binding(4)
|
|
||||||
//var<uniform> v_tcg: TCGUniforms;
|
|
||||||
|
|
||||||
fn calculate_tcg(
|
|
||||||
traits: PassTrait,
|
|
||||||
obj_pos: ptr<function, vec4<f32>>,
|
|
||||||
obj_norm: ptr<function, vec4<f32>>,
|
|
||||||
model_matrix: ptr<function, mat4x4<f32>>,
|
|
||||||
mv_inv: ptr<function, mat4x4<f32>>,
|
|
||||||
uv_0_4_idx: vec4<i32>,
|
|
||||||
uv_4_7_idx: vec4<i32>,
|
|
||||||
) -> vec2<f32> {
|
|
||||||
if (traits.uv_source == -1) {
|
|
||||||
return vec2<f32>(0.0);
|
|
||||||
}
|
|
||||||
if (traits.tcg_mtx_idx >= 0) {
|
|
||||||
var src: vec4<f32>;
|
|
||||||
switch (traits.uv_source) {
|
|
||||||
case 0: { src = vec4<f32>((*obj_pos).xyz, 1.0); }
|
|
||||||
case 1: { src = vec4<f32>((*obj_norm).xyz, 1.0); }
|
|
||||||
case 2: {
|
|
||||||
if (c_traits.shader.short_uvs == 1u) {
|
|
||||||
src = vec4<f32>(v_short_uvs.data[uv_0_4_idx.x], 0.0, 1.0);
|
|
||||||
} else {
|
|
||||||
src = vec4<f32>(v_uvs.data[uv_0_4_idx.x], 0.0, 1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 3: { src = vec4<f32>(v_uvs.data[uv_0_4_idx.y], 0.0, 1.0); }
|
|
||||||
case 4: { src = vec4<f32>(v_uvs.data[uv_0_4_idx.z], 0.0, 1.0); }
|
|
||||||
case 5: { src = vec4<f32>(v_uvs.data[uv_0_4_idx.w], 0.0, 1.0); }
|
|
||||||
case 6: { src = vec4<f32>(v_uvs.data[uv_4_7_idx.x], 0.0, 1.0); }
|
|
||||||
case 7: { src = vec4<f32>(v_uvs.data[uv_4_7_idx.y], 0.0, 1.0); }
|
|
||||||
case 8: { src = vec4<f32>(v_uvs.data[uv_4_7_idx.z], 0.0, 1.0); }
|
|
||||||
default: {}
|
|
||||||
}
|
|
||||||
let tcgm = process_uv_anim(model_matrix, mv_inv, traits);
|
|
||||||
var tmp = (tcgm.mtx * src).xyz;
|
|
||||||
if (traits.normalize == 1u) {
|
|
||||||
tmp = normalize(tmp);
|
|
||||||
}
|
|
||||||
let tmp_proj = tcgm.post_mtx * vec4<f32>(tmp, 1.0);
|
|
||||||
return (tmp_proj / tmp_proj.w).xy;
|
|
||||||
} else {
|
|
||||||
switch (traits.uv_source) {
|
|
||||||
case 0: { return (*obj_pos).xy; }
|
|
||||||
case 1: { return (*obj_norm).xy; }
|
|
||||||
case 2: {
|
|
||||||
if (c_traits.shader.short_uvs == 1u) {
|
|
||||||
return v_short_uvs.data[uv_0_4_idx.x];
|
|
||||||
} else {
|
|
||||||
return v_uvs.data[uv_0_4_idx.x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 3: { return v_uvs.data[uv_0_4_idx.y]; }
|
|
||||||
case 4: { return v_uvs.data[uv_0_4_idx.z]; }
|
|
||||||
case 5: { return v_uvs.data[uv_0_4_idx.w]; }
|
|
||||||
case 6: { return v_uvs.data[uv_4_7_idx.x]; }
|
|
||||||
case 7: { return v_uvs.data[uv_4_7_idx.y]; }
|
|
||||||
case 8: { return v_uvs.data[uv_4_7_idx.z]; }
|
|
||||||
default: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vec2<f32>(0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(vertex)
|
|
||||||
fn vs_main(
|
|
||||||
@location(0) pos_norm_idx: vec2<i32>,
|
|
||||||
@location(1) uv_0_4_idx: vec4<i32>,
|
|
||||||
@location(2) uv_4_7_idx: vec4<i32>,
|
|
||||||
) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
var obj_pos = vec4<f32>(v_verts.data[pos_norm_idx.x], 1.0);
|
|
||||||
var obj_norm = vec4<f32>(v_norms.data[pos_norm_idx.y], 0.0);
|
|
||||||
var model_matrix_no_trans = make_rotate(radians(u_model.orientation)) * make_scale(u_model.scale);
|
|
||||||
var model_matrix = make_translate(u_model.position) * model_matrix_no_trans;
|
|
||||||
var mv = u_global.view * model_matrix;
|
|
||||||
var mv_inv = transpose(u_global.view * model_matrix_no_trans);
|
|
||||||
var mv_pos = mv * obj_pos;
|
|
||||||
var mv_norm = mv_inv * obj_norm;
|
|
||||||
|
|
||||||
out.position = u_global.proj * mv_pos;
|
|
||||||
out.uv_lightmap = calculate_tcg(c_traits.lightmap, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
out.uv_diffuse = calculate_tcg(c_traits.diffuse, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
out.uv_diffuse_mod = calculate_tcg(c_traits.diffuse_mod, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
out.uv_emissive = calculate_tcg(c_traits.emissive, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
out.uv_specular = calculate_tcg(c_traits.specular, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
out.uv_extended_specular = calculate_tcg(c_traits.extended_specular, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
out.uv_reflection = calculate_tcg(c_traits.reflection, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
out.uv_alpha = calculate_tcg(c_traits.alpha, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
out.uv_alpha_mod = calculate_tcg(c_traits.alpha_mod, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
|
|
||||||
|
|
||||||
if (c_traits.shader.samus_reflection == 1u) {
|
|
||||||
// TODO dyn reflection
|
|
||||||
}
|
|
||||||
|
|
||||||
if (u_traits.post_type > 0u && u_traits.post_type < 5u) {
|
|
||||||
out.lighting = vec3<f32>(1.0);
|
|
||||||
} else {
|
|
||||||
var lighting = u_global.ambient.xyz + u_model.ambient_color.xyz;
|
|
||||||
for (var i = 0; i < 8; i = i + 1) {
|
|
||||||
var light = u_model.lights[i];
|
|
||||||
var delta = mv_pos.xyz - light.pos;
|
|
||||||
var dist = length(delta);
|
|
||||||
var delta_norm = delta / dist;
|
|
||||||
var ang_dot = max(dot(delta_norm, light.dir), 0.0);
|
|
||||||
var lin_att = light.lin_att;
|
|
||||||
var att = 1.0 / (lin_att.z * dist * dist * lin_att.y * dist + lin_att.x);
|
|
||||||
var ang_att = light.ang_att;
|
|
||||||
var ang_att_d = ang_att.z * ang_dot * ang_dot * ang_att.y * ang_dot + ang_att.x;
|
|
||||||
var this_color = light.color.xyz * ang_att_d * att * max(dot(-delta_norm, mv_norm.xyz), 0.0);
|
|
||||||
if (i == 0 && c_traits.shader.world_shadow == 1u) {
|
|
||||||
// TODO ExtTex0 sample
|
|
||||||
}
|
|
||||||
lighting = lighting + this_color;
|
|
||||||
}
|
|
||||||
out.lighting = clamp(lighting, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO dyn reflection sample
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
fn calculate_tcg(
|
|
||||||
traits: PassTrait,
|
|
||||||
uv_in: vec3<f32>,
|
|
||||||
obj_pos: ptr<function, vec4<f32>>,
|
|
||||||
obj_norm: ptr<function, vec4<f32>>,
|
|
||||||
model_matrix: ptr<function, mat4x4<f32>>,
|
|
||||||
mv_inv: ptr<function, mat4x4<f32>>,
|
|
||||||
) -> vec2<f32> {
|
|
||||||
var uv: vec4<f32> = vec4<f32>(uv_in, 1.0);
|
|
||||||
if (traits.tcg_mtx_idx >= 0) {
|
|
||||||
let tcgm = process_uv_anim(model_matrix, mv_inv, traits);
|
|
||||||
var tmp = (tcgm.mtx * uv).xyz;
|
|
||||||
if (traits.normalize == 1u) {
|
|
||||||
tmp = normalize(tmp);
|
|
||||||
}
|
|
||||||
let tmp_proj = tcgm.post_mtx * vec4<f32>(tmp, 1.0);
|
|
||||||
return (tmp_proj / tmp_proj.w).xy;
|
|
||||||
}
|
|
||||||
return uv.xy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(vertex)
|
|
||||||
fn vs_main(
|
|
||||||
@location(0) pos: vec3<f32>,
|
|
||||||
@location(1) norm: vec3<f32>,
|
|
||||||
@location(2) in_uv_lightmap: vec3<f32>,
|
|
||||||
@location(3) in_uv_diffuse: vec2<f32>,
|
|
||||||
@location(4) in_uv_diffuse_mod: vec2<f32>,
|
|
||||||
@location(5) in_uv_emissive: vec2<f32>,
|
|
||||||
@location(6) in_uv_specular: vec2<f32>,
|
|
||||||
@location(7) in_uv_extended_specular: vec2<f32>,
|
|
||||||
@location(8) in_uv_reflection: vec2<f32>,
|
|
||||||
@location(9) in_uv_alpha: vec2<f32>,
|
|
||||||
@location(10) in_uv_alpha_mod: vec2<f32>,
|
|
||||||
) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
var obj_pos = vec4<f32>(pos, 1.0);
|
|
||||||
var obj_norm = vec4<f32>(norm, 0.0);
|
|
||||||
var model_matrix_no_trans = make_rotate(radians(u_model.orientation)) * make_scale(u_model.scale);
|
|
||||||
var model_matrix = make_translate(u_model.position) * model_matrix_no_trans;
|
|
||||||
var mv = u_global.view * model_matrix;
|
|
||||||
var mv_inv = transpose(u_global.view * model_matrix_no_trans);
|
|
||||||
var mv_pos = mv * obj_pos;
|
|
||||||
var mv_norm = mv_inv * obj_norm;
|
|
||||||
|
|
||||||
out.position = u_global.proj * mv_pos;
|
|
||||||
out.uv_lightmap = calculate_tcg(c_traits.lightmap, in_uv_lightmap, &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
out.uv_diffuse = calculate_tcg(c_traits.diffuse, vec3<f32>(in_uv_diffuse, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
out.uv_diffuse_mod = calculate_tcg(c_traits.diffuse_mod, vec3<f32>(in_uv_diffuse_mod, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
out.uv_emissive = calculate_tcg(c_traits.emissive, vec3<f32>(in_uv_emissive, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
out.uv_specular = calculate_tcg(c_traits.specular, vec3<f32>(in_uv_specular, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
out.uv_extended_specular = calculate_tcg(c_traits.extended_specular, vec3<f32>(in_uv_extended_specular, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
out.uv_reflection = calculate_tcg(c_traits.reflection, vec3<f32>(in_uv_reflection, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
out.uv_alpha = calculate_tcg(c_traits.alpha, vec3<f32>(in_uv_alpha, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
out.uv_alpha_mod = calculate_tcg(c_traits.alpha_mod, vec3<f32>(in_uv_alpha_mod, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
|
|
||||||
|
|
||||||
if (c_traits.shader.samus_reflection == 1u) {
|
|
||||||
// TODO dyn reflection
|
|
||||||
}
|
|
||||||
|
|
||||||
if (u_traits.post_type > 0u && u_traits.post_type < 5u) {
|
|
||||||
out.lighting = vec3<f32>(1.0);
|
|
||||||
} else {
|
|
||||||
var lighting = u_global.ambient.xyz + u_model.ambient_color.xyz;
|
|
||||||
for (var i = 0; i < 8; i = i + 1) {
|
|
||||||
var light = u_model.lights[i];
|
|
||||||
var delta = mv_pos.xyz - light.pos;
|
|
||||||
var dist = length(delta);
|
|
||||||
var delta_norm = delta / dist;
|
|
||||||
var ang_dot = max(dot(delta_norm, light.dir), 0.0);
|
|
||||||
var lin_att = light.lin_att;
|
|
||||||
var att = 1.0 / (lin_att.z * dist * dist * lin_att.y * dist + lin_att.x);
|
|
||||||
var ang_att = light.ang_att;
|
|
||||||
var ang_att_d = ang_att.z * ang_dot * ang_dot * ang_att.y * ang_dot + ang_att.x;
|
|
||||||
var this_color = light.color.xyz * ang_att_d * att * max(dot(-delta_norm, mv_norm.xyz), 0.0);
|
|
||||||
if (i == 0 && c_traits.shader.world_shadow == 1u) {
|
|
||||||
// TODO ExtTex0 sample
|
|
||||||
}
|
|
||||||
lighting = lighting + this_color;
|
|
||||||
}
|
|
||||||
out.lighting = clamp(lighting, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO dyn reflection sample
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
|
@ -1,299 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
ops::Range,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bytemuck_derive::{Pod, Zeroable};
|
|
||||||
use wgpu::{include_wgsl, vertex_attr_array};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
get_app,
|
|
||||||
gpu::GraphicsConfig,
|
|
||||||
shaders::{
|
|
||||||
bind_pipeline, cxxbridge::ffi, pipeline_ref, push_draw_command, push_uniform, push_verts,
|
|
||||||
texture::create_sampler, BuiltBuffers, PipelineCreateCommand, PipelineHolder, PipelineRef,
|
|
||||||
ShaderDrawCommand, STATE,
|
|
||||||
},
|
|
||||||
util::{align, Vec2, Vec3},
|
|
||||||
zeus::{CColor, CMatrix4f},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct DrawData {
|
|
||||||
pipeline: PipelineRef,
|
|
||||||
vert_range: Range<u64>,
|
|
||||||
uniform_range: Range<u64>,
|
|
||||||
bind_group_id: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Hash)]
|
|
||||||
pub(crate) struct PipelineConfig {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
pub(crate) const INITIAL_PIPELINES: &[PipelineCreateCommand] =
|
|
||||||
&[PipelineCreateCommand::MoviePlayer(PipelineConfig {})];
|
|
||||||
|
|
||||||
pub(crate) struct State {
|
|
||||||
shader: wgpu::ShaderModule,
|
|
||||||
uniform_layout: wgpu::BindGroupLayout,
|
|
||||||
uniform_bind_group: wgpu::BindGroup,
|
|
||||||
texture_layout: wgpu::BindGroupLayout,
|
|
||||||
sampler: wgpu::Sampler,
|
|
||||||
pipeline_layout: wgpu::PipelineLayout,
|
|
||||||
// Transient state
|
|
||||||
texture_bind_groups: HashMap<u32, wgpu::BindGroup>,
|
|
||||||
frame_used_textures: Vec<u32>, // TODO use to clear bind groups
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_state(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
_queue: &wgpu::Queue,
|
|
||||||
buffers: &BuiltBuffers,
|
|
||||||
graphics_config: &GraphicsConfig,
|
|
||||||
) -> State {
|
|
||||||
let shader = device.create_shader_module(&include_wgsl!("shader.wgsl"));
|
|
||||||
let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment;
|
|
||||||
let uniform_size = wgpu::BufferSize::new(align(
|
|
||||||
std::mem::size_of::<Uniform>() as u64,
|
|
||||||
uniform_alignment as u64,
|
|
||||||
));
|
|
||||||
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("Textured Quad Uniform Bind Group Layout"),
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: true,
|
|
||||||
min_binding_size: uniform_size,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let sampler = create_sampler(device, wgpu::AddressMode::Repeat, None);
|
|
||||||
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: Some("Textured Quad Uniform Bind Group"),
|
|
||||||
layout: &uniform_layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
|
||||||
buffer: &buffers.uniform_buffer,
|
|
||||||
offset: 0, // dynamic
|
|
||||||
size: uniform_size,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let texture_binding = wgpu::BindingType::Texture {
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
multisampled: false,
|
|
||||||
};
|
|
||||||
let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("Textured Quad Texture Bind Group Layout"),
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: texture_binding,
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: texture_binding,
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 2,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: texture_binding,
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("Textured Quad Pipeline Layout"),
|
|
||||||
bind_group_layouts: &[&uniform_layout, &texture_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
State {
|
|
||||||
shader,
|
|
||||||
uniform_layout,
|
|
||||||
uniform_bind_group,
|
|
||||||
texture_layout,
|
|
||||||
sampler,
|
|
||||||
pipeline_layout,
|
|
||||||
texture_bind_groups: Default::default(),
|
|
||||||
frame_used_textures: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_pipeline(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
graphics: &GraphicsConfig,
|
|
||||||
state: &State,
|
|
||||||
config: &PipelineConfig,
|
|
||||||
) -> PipelineHolder {
|
|
||||||
PipelineHolder {
|
|
||||||
pipeline: device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: Some("Textured Quad Pipeline"),
|
|
||||||
layout: Some(&state.pipeline_layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &state.shader,
|
|
||||||
entry_point: "vs_main",
|
|
||||||
buffers: &[wgpu::VertexBufferLayout {
|
|
||||||
array_stride: std::mem::size_of::<Vert>() as u64,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &vertex_attr_array![0 => Float32x3, 1 => Float32x2],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
depth_stencil: Some(wgpu::DepthStencilState {
|
|
||||||
format: graphics.depth_format,
|
|
||||||
depth_write_enabled: false,
|
|
||||||
depth_compare: wgpu::CompareFunction::Always,
|
|
||||||
stencil: Default::default(),
|
|
||||||
bias: Default::default(),
|
|
||||||
}),
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: graphics.msaa_samples,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &state.shader,
|
|
||||||
entry_point: "fs_main",
|
|
||||||
targets: &[wgpu::ColorTargetState {
|
|
||||||
format: graphics.color_format,
|
|
||||||
blend: Some(wgpu::BlendState {
|
|
||||||
color: wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
alpha: wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
write_mask: wgpu::ColorWrites::COLOR,
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
multiview: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Uniform {
|
|
||||||
xf: CMatrix4f,
|
|
||||||
color: CColor,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Vert {
|
|
||||||
pos: Vec3<f32>,
|
|
||||||
uv: Vec2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn queue_movie_player(
|
|
||||||
tex_y: ffi::TextureRef,
|
|
||||||
tex_u: ffi::TextureRef,
|
|
||||||
tex_v: ffi::TextureRef,
|
|
||||||
color: CColor,
|
|
||||||
h_pad: f32,
|
|
||||||
v_pad: f32,
|
|
||||||
) {
|
|
||||||
let pipeline = pipeline_ref(&PipelineCreateCommand::MoviePlayer(PipelineConfig {}));
|
|
||||||
let vert_range = push_verts(&[
|
|
||||||
Vert { pos: Vec3::new(-h_pad, v_pad, 0.0), uv: Vec2::new(0.0, 0.0) },
|
|
||||||
Vert { pos: Vec3::new(-h_pad, -v_pad, 0.0), uv: Vec2::new(0.0, 1.0) },
|
|
||||||
Vert { pos: Vec3::new(h_pad, v_pad, 0.0), uv: Vec2::new(1.0, 0.0) },
|
|
||||||
Vert { pos: Vec3::new(h_pad, -v_pad, 0.0), uv: Vec2::new(1.0, 1.0) },
|
|
||||||
]);
|
|
||||||
let uniform_range = push_uniform(&Uniform { xf: CMatrix4f::default(), color });
|
|
||||||
|
|
||||||
// TODO defer bind group creation to draw time or another thread?
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
let groups = &mut state.movie_player.texture_bind_groups;
|
|
||||||
let mut hasher = twox_hash::XxHash32::default();
|
|
||||||
tex_y.id.hash(&mut hasher);
|
|
||||||
tex_u.id.hash(&mut hasher);
|
|
||||||
tex_v.id.hash(&mut hasher);
|
|
||||||
let bind_group_id = hasher.finish() as u32;
|
|
||||||
if !groups.contains_key(&bind_group_id) {
|
|
||||||
let tex_y = state.textures.get(&tex_y.id).unwrap();
|
|
||||||
let tex_u = state.textures.get(&tex_u.id).unwrap();
|
|
||||||
let tex_v = state.textures.get(&tex_v.id).unwrap();
|
|
||||||
let bind_group = get_app().gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: &state.movie_player.texture_layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&tex_y.view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&tex_u.view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 2,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&tex_v.view),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
groups.insert(bind_group_id, bind_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
push_draw_command(ShaderDrawCommand::MoviePlayer(DrawData {
|
|
||||||
pipeline,
|
|
||||||
vert_range,
|
|
||||||
uniform_range,
|
|
||||||
bind_group_id,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn draw_movie_player<'a>(
|
|
||||||
data: &DrawData,
|
|
||||||
state: &'a State,
|
|
||||||
pass: &mut wgpu::RenderPass<'a>,
|
|
||||||
buffers: &'a BuiltBuffers,
|
|
||||||
) {
|
|
||||||
if !bind_pipeline(data.pipeline, pass) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uniform bind group
|
|
||||||
pass.set_bind_group(0, &state.uniform_bind_group, &[
|
|
||||||
data.uniform_range.start as wgpu::DynamicOffset
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Texture bind group
|
|
||||||
let texture_bind_group = state
|
|
||||||
.texture_bind_groups
|
|
||||||
.get(&data.bind_group_id)
|
|
||||||
.expect("Failed to find texture bind group");
|
|
||||||
pass.set_bind_group(1, texture_bind_group, &[]);
|
|
||||||
|
|
||||||
// Vertex buffer
|
|
||||||
pass.set_vertex_buffer(0, buffers.vertex_buffer.slice(data.vert_range.clone()));
|
|
||||||
pass.draw(0..4, 0..1);
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
struct Uniform {
|
|
||||||
xf: mat4x4<f32>;
|
|
||||||
color: vec4<f32>;
|
|
||||||
};
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<uniform> ubuf: Uniform;
|
|
||||||
@group(0) @binding(1)
|
|
||||||
var tex_sampler: sampler;
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var tex_y: texture_2d<f32>;
|
|
||||||
@group(1) @binding(1)
|
|
||||||
var tex_u: texture_2d<f32>;
|
|
||||||
@group(1) @binding(2)
|
|
||||||
var tex_v: texture_2d<f32>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) pos: vec4<f32>;
|
|
||||||
@location(0) uv: vec2<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
@stage(vertex)
|
|
||||||
fn vs_main(@location(0) in_pos: vec3<f32>, @location(1) in_uv: vec2<f32>) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);
|
|
||||||
out.uv = in_uv;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(fragment)
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
var yuv = vec3<f32>(
|
|
||||||
1.1643 * (textureSample(tex_y, tex_sampler, in.uv).x - 0.0625),
|
|
||||||
textureSample(tex_u, tex_sampler, in.uv).x - 0.5,
|
|
||||||
textureSample(tex_v, tex_sampler, in.uv).x - 0.5
|
|
||||||
);
|
|
||||||
return ubuf.color * vec4<f32>(
|
|
||||||
yuv.x + 1.5958 * yuv.z,
|
|
||||||
yuv.x - 0.39173 * yuv.y - 0.8129 * yuv.z,
|
|
||||||
yuv.x + 2.017 * yuv.y,
|
|
||||||
1.0
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,290 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::hash_map::Entry::{Occupied, Vacant},
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
num::{NonZeroU32, NonZeroU8},
|
|
||||||
};
|
|
||||||
|
|
||||||
use twox_hash::XxHash32;
|
|
||||||
use wgpu::{util::DeviceExt, ImageDataLayout};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
get_app,
|
|
||||||
shaders::{cxxbridge::ffi, STATE},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) struct TextureWithView {
|
|
||||||
pub(crate) texture: wgpu::Texture,
|
|
||||||
pub(crate) view: wgpu::TextureView,
|
|
||||||
pub(crate) format: wgpu::TextureFormat,
|
|
||||||
pub(crate) extent: wgpu::Extent3d,
|
|
||||||
}
|
|
||||||
impl TextureWithView {
|
|
||||||
fn new(texture: wgpu::Texture, format: wgpu::TextureFormat, extent: wgpu::Extent3d) -> Self {
|
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
Self { texture, view, format, extent }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct RenderTexture {
|
|
||||||
pub(crate) color_texture: Option<TextureWithView>,
|
|
||||||
pub(crate) depth_texture: Option<TextureWithView>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_static_texture_2d(
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
mips: u32,
|
|
||||||
format: ffi::TextureFormat,
|
|
||||||
data: &[u8],
|
|
||||||
label: &str,
|
|
||||||
) -> ffi::TextureRef {
|
|
||||||
let gpu = &get_app().gpu;
|
|
||||||
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: 1 };
|
|
||||||
let wgpu_format = match format {
|
|
||||||
ffi::TextureFormat::RGBA8 => wgpu::TextureFormat::Rgba8Unorm,
|
|
||||||
ffi::TextureFormat::R8 => wgpu::TextureFormat::R8Unorm,
|
|
||||||
ffi::TextureFormat::R32Float => wgpu::TextureFormat::R32Float,
|
|
||||||
ffi::TextureFormat::DXT1 => wgpu::TextureFormat::Bc1RgbaUnorm,
|
|
||||||
ffi::TextureFormat::DXT3 => wgpu::TextureFormat::Bc3RgbaUnorm,
|
|
||||||
ffi::TextureFormat::DXT5 => wgpu::TextureFormat::Bc5RgUnorm,
|
|
||||||
ffi::TextureFormat::BPTC => wgpu::TextureFormat::Bc7RgbaUnorm,
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
let texture = gpu.device.create_texture_with_data(
|
|
||||||
&gpu.queue,
|
|
||||||
&wgpu::TextureDescriptor {
|
|
||||||
label: Some(label),
|
|
||||||
size: extent,
|
|
||||||
mip_level_count: mips,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu_format,
|
|
||||||
usage: wgpu::TextureUsages::TEXTURE_BINDING,
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Generate texture hash as ID
|
|
||||||
let mut hasher = XxHash32::with_seed(format.into());
|
|
||||||
width.hash(&mut hasher);
|
|
||||||
height.hash(&mut hasher);
|
|
||||||
mips.hash(&mut hasher);
|
|
||||||
data.hash(&mut hasher);
|
|
||||||
label.hash(&mut hasher);
|
|
||||||
let id = hasher.finish() as u32;
|
|
||||||
|
|
||||||
// Store texture and return reference
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
match state.textures.entry(id) {
|
|
||||||
Occupied(entry) => panic!("Hash collision ({:x}) on texture creation: {}", id, label),
|
|
||||||
Vacant(entry) => {
|
|
||||||
state.textures.insert(id, TextureWithView::new(texture, wgpu_format, extent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ffi::TextureRef { id, render: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_dynamic_texture_2d(
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
mips: u32,
|
|
||||||
format: ffi::TextureFormat,
|
|
||||||
label: &str,
|
|
||||||
) -> ffi::TextureRef {
|
|
||||||
let gpu = &get_app().gpu;
|
|
||||||
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: 1 };
|
|
||||||
let wgpu_format = match format {
|
|
||||||
ffi::TextureFormat::RGBA8 => wgpu::TextureFormat::Rgba8Unorm,
|
|
||||||
ffi::TextureFormat::R8 => wgpu::TextureFormat::R8Unorm,
|
|
||||||
ffi::TextureFormat::R32Float => wgpu::TextureFormat::R32Float,
|
|
||||||
ffi::TextureFormat::DXT1 => wgpu::TextureFormat::Bc1RgbaUnorm,
|
|
||||||
ffi::TextureFormat::DXT3 => wgpu::TextureFormat::Bc3RgbaUnorm,
|
|
||||||
ffi::TextureFormat::DXT5 => wgpu::TextureFormat::Bc5RgUnorm,
|
|
||||||
ffi::TextureFormat::BPTC => wgpu::TextureFormat::Bc7RgbaUnorm,
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
let texture = gpu.device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: Some(label),
|
|
||||||
size: extent,
|
|
||||||
mip_level_count: mips,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu_format,
|
|
||||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Generate texture hash as ID
|
|
||||||
let mut hasher = XxHash32::with_seed(format.into());
|
|
||||||
width.hash(&mut hasher);
|
|
||||||
height.hash(&mut hasher);
|
|
||||||
mips.hash(&mut hasher);
|
|
||||||
label.hash(&mut hasher);
|
|
||||||
let id = hasher.finish() as u32;
|
|
||||||
|
|
||||||
// Store texture and return reference
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
match state.textures.entry(id) {
|
|
||||||
Occupied(entry) => panic!("Hash collision ({:x}) on texture creation: {}", id, label),
|
|
||||||
Vacant(entry) => {
|
|
||||||
state.textures.insert(id, TextureWithView::new(texture, wgpu_format, extent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ffi::TextureRef { id, render: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_render_texture(
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
// clamp_mode: TextureClampMode,
|
|
||||||
color_bind_count: u32,
|
|
||||||
depth_bind_count: u32,
|
|
||||||
label: &str,
|
|
||||||
) -> ffi::TextureRef {
|
|
||||||
let gpu = &get_app().gpu;
|
|
||||||
let color_texture = if color_bind_count > 0 {
|
|
||||||
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: color_bind_count };
|
|
||||||
Some(TextureWithView::new(
|
|
||||||
gpu.device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: Some(format!("{} Color", label).as_str()),
|
|
||||||
size: extent,
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: gpu.config.color_format,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
}),
|
|
||||||
gpu.config.color_format,
|
|
||||||
extent,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let depth_texture = if depth_bind_count > 0 {
|
|
||||||
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: depth_bind_count };
|
|
||||||
Some(TextureWithView::new(
|
|
||||||
gpu.device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: Some(format!("{} Depth", label).as_str()),
|
|
||||||
size: extent,
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: gpu.config.depth_format,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
}),
|
|
||||||
gpu.config.depth_format,
|
|
||||||
extent,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
// let (clamp_mode, border_color) = match clamp_mode {
|
|
||||||
// TextureClampMode::Repeat => (wgpu::AddressMode::Repeat, None),
|
|
||||||
// TextureClampMode::ClampToWhite => {
|
|
||||||
// (wgpu::AddressMode::ClampToBorder, Some(wgpu::SamplerBorderColor::OpaqueWhite))
|
|
||||||
// }
|
|
||||||
// TextureClampMode::ClampToBlack => {
|
|
||||||
// (wgpu::AddressMode::ClampToBorder, Some(wgpu::SamplerBorderColor::OpaqueBlack))
|
|
||||||
// }
|
|
||||||
// TextureClampMode::ClampToEdge => (wgpu::AddressMode::ClampToEdge, None),
|
|
||||||
// };
|
|
||||||
// let sampler = gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
|
||||||
// label: Some(format!("{} Color Sampler", label).as_str()),
|
|
||||||
// address_mode_u: clamp_mode,
|
|
||||||
// address_mode_v: clamp_mode,
|
|
||||||
// address_mode_w: clamp_mode,
|
|
||||||
// mag_filter: wgpu::FilterMode::Linear,
|
|
||||||
// min_filter: wgpu::FilterMode::Linear,
|
|
||||||
// mipmap_filter: wgpu::FilterMode::Linear,
|
|
||||||
// lod_min_clamp: 0.0,
|
|
||||||
// lod_max_clamp: f32::MAX,
|
|
||||||
// compare: None,
|
|
||||||
// anisotropy_clamp: None,
|
|
||||||
// border_color,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Generate texture hash as ID
|
|
||||||
let mut hasher = XxHash32::default();
|
|
||||||
width.hash(&mut hasher);
|
|
||||||
height.hash(&mut hasher);
|
|
||||||
color_bind_count.hash(&mut hasher);
|
|
||||||
depth_bind_count.hash(&mut hasher);
|
|
||||||
label.hash(&mut hasher);
|
|
||||||
let id = hasher.finish() as u32;
|
|
||||||
|
|
||||||
// Store texture and return reference
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
match state.textures.entry(id) {
|
|
||||||
Occupied(entry) => panic!("Hash collision ({:x}) on texture creation: {}", id, label),
|
|
||||||
Vacant(entry) => {
|
|
||||||
state.render_textures.insert(id, RenderTexture { color_texture, depth_texture });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ffi::TextureRef { id, render: true }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn write_texture(handle: ffi::TextureRef, data: &[u8]) {
|
|
||||||
if handle.render {
|
|
||||||
panic!("Can't write to render texture");
|
|
||||||
}
|
|
||||||
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
if let Some(TextureWithView { texture, format, extent, .. }) = state.textures.get(&handle.id) {
|
|
||||||
state.queue.write_texture(
|
|
||||||
texture.as_image_copy(),
|
|
||||||
data,
|
|
||||||
ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: match format {
|
|
||||||
wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::R32Float => {
|
|
||||||
NonZeroU32::new(extent.width * 4)
|
|
||||||
}
|
|
||||||
wgpu::TextureFormat::R8Unorm => NonZeroU32::new(extent.width),
|
|
||||||
_ => todo!("Unimplemented format for write_texture: {:?}", format),
|
|
||||||
},
|
|
||||||
rows_per_image: match extent.depth_or_array_layers {
|
|
||||||
1 => None,
|
|
||||||
_ => todo!("Unimplemented write_texture for 3D/2DArray textures"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
*extent,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
panic!("Failed to find texture {}", handle.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn drop_texture(handle: ffi::TextureRef) {
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
if handle.render {
|
|
||||||
state.render_textures.remove(&handle.id).expect("Render texture already dropped");
|
|
||||||
} else {
|
|
||||||
state.textures.remove(&handle.id).expect("Texture already dropped");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_sampler(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
mut address_mode: wgpu::AddressMode,
|
|
||||||
mut border_color: Option<wgpu::SamplerBorderColor>,
|
|
||||||
) -> wgpu::Sampler {
|
|
||||||
if address_mode == wgpu::AddressMode::ClampToBorder
|
|
||||||
&& !device.features().contains(wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER)
|
|
||||||
{
|
|
||||||
address_mode = wgpu::AddressMode::ClampToEdge;
|
|
||||||
border_color = None;
|
|
||||||
}
|
|
||||||
device.create_sampler(&wgpu::SamplerDescriptor {
|
|
||||||
label: None,
|
|
||||||
address_mode_u: address_mode,
|
|
||||||
address_mode_v: address_mode,
|
|
||||||
address_mode_w: address_mode,
|
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
|
||||||
mipmap_filter: wgpu::FilterMode::Linear,
|
|
||||||
lod_min_clamp: 0.0,
|
|
||||||
lod_max_clamp: f32::MAX,
|
|
||||||
compare: None,
|
|
||||||
anisotropy_clamp: NonZeroU8::new(16),
|
|
||||||
border_color,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,369 +0,0 @@
|
||||||
use std::{collections::HashMap, hash::Hash, ops::Range};
|
|
||||||
|
|
||||||
use bytemuck_derive::{Pod, Zeroable};
|
|
||||||
use wgpu::{include_wgsl, vertex_attr_array};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
get_app,
|
|
||||||
gpu::GraphicsConfig,
|
|
||||||
shaders::{
|
|
||||||
bind_pipeline, cxxbridge::ffi, get_combined_matrix, pipeline_ref, push_draw_command,
|
|
||||||
push_uniform, push_verts, texture::create_sampler, BuiltBuffers, PipelineCreateCommand,
|
|
||||||
PipelineHolder, PipelineRef, ShaderDrawCommand, STATE,
|
|
||||||
},
|
|
||||||
util::{align, Vec2, Vec3},
|
|
||||||
zeus::{CColor, CMatrix4f, CRectangle, CVector2f, CVector3f, CVector4f},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct DrawData {
|
|
||||||
pipeline: PipelineRef,
|
|
||||||
vert_range: Range<u64>,
|
|
||||||
uniform_range: Range<u64>,
|
|
||||||
texture: ffi::TextureRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Hash)]
|
|
||||||
pub(crate) struct PipelineConfig {
|
|
||||||
filter_type: ffi::CameraFilterType,
|
|
||||||
z_comparison: ffi::ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
}
|
|
||||||
pub(crate) const INITIAL_PIPELINES: &[PipelineCreateCommand] = &[
|
|
||||||
// PipelineCreateCommand::TexturedQuad(PipelineConfig { z_only: false }),
|
|
||||||
// PipelineCreateCommand::TexturedQuad(PipelineConfig { z_only: true }),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub(crate) struct State {
|
|
||||||
shader: wgpu::ShaderModule,
|
|
||||||
uniform_layout: wgpu::BindGroupLayout,
|
|
||||||
uniform_bind_group: wgpu::BindGroup,
|
|
||||||
texture_layout: wgpu::BindGroupLayout,
|
|
||||||
sampler: wgpu::Sampler,
|
|
||||||
pipeline_layout: wgpu::PipelineLayout,
|
|
||||||
// Transient state
|
|
||||||
texture_bind_groups: HashMap<u32, wgpu::BindGroup>,
|
|
||||||
frame_used_textures: Vec<u32>, // TODO use to clear bind groups
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_state(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
_queue: &wgpu::Queue,
|
|
||||||
buffers: &BuiltBuffers,
|
|
||||||
graphics_config: &GraphicsConfig,
|
|
||||||
) -> State {
|
|
||||||
let shader = device.create_shader_module(&include_wgsl!("shader.wgsl"));
|
|
||||||
let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment;
|
|
||||||
let uniform_size = wgpu::BufferSize::new(align(
|
|
||||||
std::mem::size_of::<Uniform>() as u64,
|
|
||||||
uniform_alignment as u64,
|
|
||||||
));
|
|
||||||
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("Textured Quad Uniform Bind Group Layout"),
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: true,
|
|
||||||
min_binding_size: uniform_size,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let sampler = create_sampler(device, wgpu::AddressMode::Repeat, None);
|
|
||||||
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: Some("Textured Quad Uniform Bind Group"),
|
|
||||||
layout: &uniform_layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
|
||||||
buffer: &buffers.uniform_buffer,
|
|
||||||
offset: 0, // dynamic
|
|
||||||
size: uniform_size,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("Textured Quad Texture Bind Group Layout"),
|
|
||||||
entries: &[wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
multisampled: false,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("Textured Quad Pipeline Layout"),
|
|
||||||
bind_group_layouts: &[&uniform_layout, &texture_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
State {
|
|
||||||
shader,
|
|
||||||
uniform_layout,
|
|
||||||
uniform_bind_group,
|
|
||||||
texture_layout,
|
|
||||||
sampler,
|
|
||||||
pipeline_layout,
|
|
||||||
texture_bind_groups: Default::default(),
|
|
||||||
frame_used_textures: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn construct_pipeline(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
graphics: &GraphicsConfig,
|
|
||||||
state: &State,
|
|
||||||
config: &PipelineConfig,
|
|
||||||
) -> PipelineHolder {
|
|
||||||
let (blend_component, alpha_write) = match config.filter_type {
|
|
||||||
ffi::CameraFilterType::Multiply => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::Zero,
|
|
||||||
dst_factor: wgpu::BlendFactor::Src,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
ffi::CameraFilterType::Add => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::One,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
ffi::CameraFilterType::Subtract => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::One,
|
|
||||||
operation: wgpu::BlendOperation::Subtract,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
ffi::CameraFilterType::Blend => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
|
||||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
ffi::CameraFilterType::InvDstMultiply => (
|
|
||||||
wgpu::BlendComponent {
|
|
||||||
src_factor: wgpu::BlendFactor::Zero,
|
|
||||||
dst_factor: wgpu::BlendFactor::OneMinusSrc,
|
|
||||||
operation: wgpu::BlendOperation::Add,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
PipelineHolder {
|
|
||||||
pipeline: device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: Some("Textured Quad Pipeline"),
|
|
||||||
layout: Some(&state.pipeline_layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &state.shader,
|
|
||||||
entry_point: "vs_main",
|
|
||||||
buffers: &[wgpu::VertexBufferLayout {
|
|
||||||
array_stride: std::mem::size_of::<Vert>() as u64,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &vertex_attr_array![0 => Float32x3, 1 => Float32x2],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
depth_stencil: Some(wgpu::DepthStencilState {
|
|
||||||
format: graphics.depth_format,
|
|
||||||
depth_write_enabled: config.z_test,
|
|
||||||
depth_compare: match config.z_comparison {
|
|
||||||
ffi::ZTest::Never => wgpu::CompareFunction::Never,
|
|
||||||
ffi::ZTest::Less => wgpu::CompareFunction::Less,
|
|
||||||
ffi::ZTest::Equal => wgpu::CompareFunction::Equal,
|
|
||||||
ffi::ZTest::LEqual => wgpu::CompareFunction::LessEqual,
|
|
||||||
ffi::ZTest::Greater => wgpu::CompareFunction::Greater,
|
|
||||||
ffi::ZTest::NEqual => wgpu::CompareFunction::NotEqual,
|
|
||||||
ffi::ZTest::GEqual => wgpu::CompareFunction::GreaterEqual,
|
|
||||||
ffi::ZTest::Always => wgpu::CompareFunction::Always,
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
stencil: Default::default(),
|
|
||||||
bias: Default::default(),
|
|
||||||
}),
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: graphics.msaa_samples,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &state.shader,
|
|
||||||
entry_point: "fs_main",
|
|
||||||
targets: &[wgpu::ColorTargetState {
|
|
||||||
format: graphics.color_format,
|
|
||||||
blend: Some(wgpu::BlendState {
|
|
||||||
color: blend_component,
|
|
||||||
alpha: blend_component,
|
|
||||||
}),
|
|
||||||
write_mask: if alpha_write {
|
|
||||||
wgpu::ColorWrites::ALL
|
|
||||||
} else {
|
|
||||||
wgpu::ColorWrites::COLOR
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
multiview: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Uniform {
|
|
||||||
xf: CMatrix4f,
|
|
||||||
color: CColor,
|
|
||||||
lod: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Vert {
|
|
||||||
pos: Vec3<f32>,
|
|
||||||
uv: Vec2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn queue_textured_quad(
|
|
||||||
filter_type: ffi::CameraFilterType,
|
|
||||||
texture: ffi::TextureRef,
|
|
||||||
z_comparison: ffi::ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
color: CColor,
|
|
||||||
uv_scale: f32,
|
|
||||||
rect: CRectangle,
|
|
||||||
z: f32,
|
|
||||||
) {
|
|
||||||
let pipeline = pipeline_ref(&PipelineCreateCommand::TexturedQuad(PipelineConfig {
|
|
||||||
filter_type,
|
|
||||||
z_comparison,
|
|
||||||
z_test,
|
|
||||||
}));
|
|
||||||
let vert_range = push_verts(&[
|
|
||||||
Vert { pos: Vec3::new(0.0, 0.0, z), uv: Vec2::new(0.0, 0.0) },
|
|
||||||
Vert { pos: Vec3::new(0.0, 1.0, z), uv: Vec2::new(0.0, uv_scale) },
|
|
||||||
Vert { pos: Vec3::new(1.0, 0.0, z), uv: Vec2::new(uv_scale, 0.0) },
|
|
||||||
Vert { pos: Vec3::new(1.0, 1.0, z), uv: Vec2::new(uv_scale, uv_scale) },
|
|
||||||
]);
|
|
||||||
let uniform_range = push_uniform(&Uniform {
|
|
||||||
xf: CMatrix4f::new(
|
|
||||||
CVector4f::new(rect.size.x * 2.0, 0.0, 0.0, 0.0),
|
|
||||||
CVector4f::new(0.0, rect.size.y * 2.0, 0.0, 0.0),
|
|
||||||
CVector4f::new(0.0, 0.0, 1.0, 0.0),
|
|
||||||
CVector4f::new(rect.position.x * 2.0 - 1.0, rect.position.y * 2.0 - 1.0, 0.0, 1.0),
|
|
||||||
),
|
|
||||||
color,
|
|
||||||
lod: 0.0,
|
|
||||||
});
|
|
||||||
|
|
||||||
push_textured_quad(texture, pipeline, vert_range, uniform_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn queue_textured_quad_verts(
|
|
||||||
filter_type: ffi::CameraFilterType,
|
|
||||||
texture: ffi::TextureRef,
|
|
||||||
z_comparison: ffi::ZTest,
|
|
||||||
z_test: bool,
|
|
||||||
color: CColor,
|
|
||||||
pos: &[CVector3f],
|
|
||||||
uvs: &[CVector2f],
|
|
||||||
lod: f32,
|
|
||||||
) {
|
|
||||||
if pos.len() != 4 || uvs.len() != 4 {
|
|
||||||
panic!("Invalid pos/uv sizes: {}/{}", pos.len(), uvs.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
let pipeline = pipeline_ref(&PipelineCreateCommand::TexturedQuad(PipelineConfig {
|
|
||||||
filter_type,
|
|
||||||
z_comparison,
|
|
||||||
z_test,
|
|
||||||
}));
|
|
||||||
let vert_range = push_verts(
|
|
||||||
&pos.iter()
|
|
||||||
.zip(uvs)
|
|
||||||
.map(|(pos, uv)| Vert { pos: pos.into(), uv: uv.into() })
|
|
||||||
.collect::<Vec<Vert>>(),
|
|
||||||
);
|
|
||||||
let uniform_range = push_uniform(&Uniform { xf: get_combined_matrix(), color, lod: 0.0 });
|
|
||||||
|
|
||||||
push_textured_quad(texture, pipeline, vert_range, uniform_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_textured_quad(
|
|
||||||
texture: ffi::TextureRef,
|
|
||||||
pipeline: PipelineRef,
|
|
||||||
vert_range: Range<u64>,
|
|
||||||
uniform_range: Range<u64>,
|
|
||||||
) {
|
|
||||||
// TODO defer bind group creation to draw time or another thread?
|
|
||||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
|
||||||
let groups = &mut state.textured_quad.texture_bind_groups;
|
|
||||||
if !groups.contains_key(&texture.id) {
|
|
||||||
let tex = state.textures.get(&texture.id).unwrap();
|
|
||||||
let bind_group = get_app().gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: &state.textured_quad.texture_layout,
|
|
||||||
entries: &[wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&tex.view),
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
groups.insert(texture.id, bind_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
push_draw_command(ShaderDrawCommand::TexturedQuad(DrawData {
|
|
||||||
pipeline,
|
|
||||||
vert_range,
|
|
||||||
uniform_range,
|
|
||||||
texture,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn draw_textured_quad<'a>(
|
|
||||||
data: &DrawData,
|
|
||||||
state: &'a State,
|
|
||||||
pass: &mut wgpu::RenderPass<'a>,
|
|
||||||
buffers: &'a BuiltBuffers,
|
|
||||||
) {
|
|
||||||
if !bind_pipeline(data.pipeline, pass) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uniform bind group
|
|
||||||
pass.set_bind_group(0, &state.uniform_bind_group, &[
|
|
||||||
data.uniform_range.start as wgpu::DynamicOffset
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Texture bind group
|
|
||||||
let texture_bind_group =
|
|
||||||
state.texture_bind_groups.get(&data.texture.id).expect("Failed to find texture bind group");
|
|
||||||
pass.set_bind_group(1, texture_bind_group, &[]);
|
|
||||||
|
|
||||||
// Vertex buffer
|
|
||||||
pass.set_vertex_buffer(0, buffers.vertex_buffer.slice(data.vert_range.clone()));
|
|
||||||
pass.draw(0..4, 0..1);
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
struct Uniform {
|
|
||||||
xf: mat4x4<f32>;
|
|
||||||
color: vec4<f32>;
|
|
||||||
lod: f32;
|
|
||||||
};
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<uniform> ubuf: Uniform;
|
|
||||||
@group(0) @binding(1)
|
|
||||||
var texture_sampler: sampler;
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var texture: texture_2d<f32>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) pos: vec4<f32>;
|
|
||||||
@location(0) uv: vec2<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
@stage(vertex)
|
|
||||||
fn vs_main(@location(0) in_pos: vec3<f32>, @location(1) in_uv: vec2<f32>) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);
|
|
||||||
out.uv = in_uv;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@stage(fragment)
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
return ubuf.color * textureSampleBias(texture, texture_sampler, in.uv, ubuf.lod);
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
//! This contains regular vector types for use in packed buffers (i.e. vertex).
|
|
||||||
//! zeus types are all aligned to f32x4
|
|
||||||
use binrw::BinRead;
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, BinRead, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub(crate) struct Vec3<T: 'static + BinRead<Args = ()> + Pod> {
|
|
||||||
pub(crate) x: T,
|
|
||||||
pub(crate) y: T,
|
|
||||||
pub(crate) z: T,
|
|
||||||
}
|
|
||||||
impl<T: 'static + BinRead<Args = ()> + Pod> Vec3<T> {
|
|
||||||
// TODO const
|
|
||||||
pub fn new(x: T, y: T, z: T) -> Self { Self { x, y, z } }
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: BinRead<Args = ()> + Pod> Zeroable for Vec3<T> {}
|
|
||||||
|
|
||||||
unsafe impl<T: BinRead<Args = ()> + Pod> Pod for Vec3<T> {}
|
|
||||||
|
|
||||||
impl<T: BinRead<Args = ()> + Pod> From<T> for Vec3<T> {
|
|
||||||
fn from(v: T) -> Self { Vec3::<T> { x: v, y: v, z: v } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BinRead<Args = ()> + Pod> From<cgmath::Vector3<T>> for Vec3<T> {
|
|
||||||
fn from(v: cgmath::Vector3<T>) -> Self { Self { x: v.x, y: v.y, z: v.z } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BinRead<Args = ()> + Pod> From<Vec3<T>> for cgmath::Vector3<T> {
|
|
||||||
fn from(v: Vec3<T>) -> Self { Self { x: v.x, y: v.y, z: v.z } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BinRead<Args = ()> + Pod> From<cgmath::Point3<T>> for Vec3<T> {
|
|
||||||
fn from(v: cgmath::Point3<T>) -> Self { Self { x: v.x, y: v.y, z: v.z } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BinRead<Args = ()> + Pod> From<Vec3<T>> for cgmath::Point3<T> {
|
|
||||||
fn from(v: Vec3<T>) -> Self { Self { x: v.x, y: v.y, z: v.z } }
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub(crate) const fn vec3_splat<T: BinRead<Args = ()> + Pod>(v: T) -> Vec3<T> {
|
|
||||||
// Vec3::<T> { x: v, y: v, z: v }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, BinRead, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub(crate) struct Vec2<T: 'static + BinRead<Args = ()> + Pod> {
|
|
||||||
pub(crate) x: T,
|
|
||||||
pub(crate) y: T,
|
|
||||||
}
|
|
||||||
impl<T: 'static + BinRead<Args = ()> + Pod> Vec2<T> {
|
|
||||||
// TODO const
|
|
||||||
pub fn new(x: T, y: T) -> Self { Self { x, y } }
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: BinRead<Args = ()> + Pod> Zeroable for Vec2<T> {}
|
|
||||||
|
|
||||||
unsafe impl<T: BinRead<Args = ()> + Pod> Pod for Vec2<T> {}
|
|
||||||
|
|
||||||
impl<T: BinRead<Args = ()> + Pod> From<T> for Vec2<T> {
|
|
||||||
fn from(v: T) -> Self { Self { x: v, y: v } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BinRead<Args = ()> + Pod> From<cgmath::Vector2<T>> for Vec2<T> {
|
|
||||||
fn from(v: cgmath::Vector2<T>) -> Self { Self { x: v.x, y: v.y } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BinRead<Args = ()> + Pod> From<Vec2<T>> for cgmath::Vector2<T> {
|
|
||||||
fn from(v: Vec2<T>) -> Self { Self { x: v.x, y: v.y } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec2<i16>> for Vec2<f32> {
|
|
||||||
fn from(v: Vec2<i16>) -> Self { Self { x: v.x as f32 / 32768.0, y: v.y as f32 / 32768.0 } }
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub(crate) const fn vec2_splat<T: BinRead<Args = ()> + Pod>(v: T) -> Vec2<T> {
|
|
||||||
// Vec2::<T> { x: v, y: v }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn align<
|
|
||||||
T: Copy
|
|
||||||
+ std::ops::Sub<Output = T>
|
|
||||||
+ std::ops::Add<Output = T>
|
|
||||||
+ std::ops::Not<Output = T>
|
|
||||||
+ std::ops::BitAnd<Output = T>
|
|
||||||
+ num_traits::One
|
|
||||||
+ num_traits::Zero
|
|
||||||
+ std::cmp::PartialEq,
|
|
||||||
>(
|
|
||||||
n: T,
|
|
||||||
a: T,
|
|
||||||
) -> T {
|
|
||||||
if a == num_traits::Zero::zero() {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
(n + (a - num_traits::One::one())) & !(a - num_traits::One::one())
|
|
||||||
}
|
|
|
@ -1,193 +0,0 @@
|
||||||
use bytemuck_derive::{Pod, Zeroable};
|
|
||||||
use cxx::{type_id, ExternType};
|
|
||||||
|
|
||||||
use crate::util::{Vec2, Vec3};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CVector2f {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
_p1: f32,
|
|
||||||
_p2: f32,
|
|
||||||
}
|
|
||||||
impl CVector2f {
|
|
||||||
pub const fn new(x: f32, y: f32) -> Self { Self { x, y, _p1: 0.0, _p2: 0.0 } }
|
|
||||||
}
|
|
||||||
unsafe impl ExternType for CVector2f {
|
|
||||||
type Id = type_id!("zeus::CVector2f");
|
|
||||||
type Kind = cxx::kind::Trivial;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CVector3f {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub z: f32,
|
|
||||||
_p: f32,
|
|
||||||
}
|
|
||||||
impl CVector3f {
|
|
||||||
pub const fn new(x: f32, y: f32, z: f32) -> Self { Self { x, y, z, _p: 0.0 } }
|
|
||||||
}
|
|
||||||
unsafe impl ExternType for CVector3f {
|
|
||||||
type Id = type_id!("zeus::CVector3f");
|
|
||||||
type Kind = cxx::kind::Trivial;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CVector4f {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub z: f32,
|
|
||||||
pub w: f32,
|
|
||||||
}
|
|
||||||
impl CVector4f {
|
|
||||||
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self { Self { x, y, z, w } }
|
|
||||||
}
|
|
||||||
unsafe impl ExternType for CVector4f {
|
|
||||||
type Id = type_id!("zeus::CVector4f");
|
|
||||||
type Kind = cxx::kind::Trivial;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CColor {
|
|
||||||
pub r: f32,
|
|
||||||
pub g: f32,
|
|
||||||
pub b: f32,
|
|
||||||
pub a: f32,
|
|
||||||
}
|
|
||||||
impl CColor {
|
|
||||||
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self { Self { r, g, b, a } }
|
|
||||||
}
|
|
||||||
unsafe impl ExternType for CColor {
|
|
||||||
type Id = type_id!("zeus::CColor");
|
|
||||||
type Kind = cxx::kind::Trivial;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CRectangle {
|
|
||||||
pub position: CVector2f,
|
|
||||||
pub size: CVector2f,
|
|
||||||
}
|
|
||||||
impl CRectangle {
|
|
||||||
pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
|
|
||||||
Self { position: CVector2f::new(x, y), size: CVector2f::new(w, h) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unsafe impl ExternType for CRectangle {
|
|
||||||
type Id = type_id!("zeus::CRectangle");
|
|
||||||
type Kind = cxx::kind::Trivial;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CAABox {
|
|
||||||
pub min: CVector3f,
|
|
||||||
pub max: CVector3f,
|
|
||||||
}
|
|
||||||
impl CAABox {
|
|
||||||
pub const fn new(min: CVector3f, max: CVector3f) -> Self { Self { min, max } }
|
|
||||||
}
|
|
||||||
unsafe impl ExternType for CAABox {
|
|
||||||
type Id = type_id!("zeus::CAABox");
|
|
||||||
type Kind = cxx::kind::Trivial;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const IDENTITY_MATRIX4F: CMatrix4f = CMatrix4f {
|
|
||||||
x: CVector4f::new(1.0, 0.0, 0.0, 0.0),
|
|
||||||
y: CVector4f::new(0.0, 1.0, 0.0, 0.0),
|
|
||||||
z: CVector4f::new(0.0, 0.0, 1.0, 0.0),
|
|
||||||
w: CVector4f::new(0.0, 0.0, 0.0, 1.0),
|
|
||||||
};
|
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CMatrix4f {
|
|
||||||
pub x: CVector4f,
|
|
||||||
pub y: CVector4f,
|
|
||||||
pub z: CVector4f,
|
|
||||||
pub w: CVector4f,
|
|
||||||
}
|
|
||||||
impl CMatrix4f {
|
|
||||||
pub const fn new(x: CVector4f, y: CVector4f, z: CVector4f, w: CVector4f) -> Self {
|
|
||||||
Self { x, y, z, w }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for CMatrix4f {
|
|
||||||
fn default() -> Self { IDENTITY_MATRIX4F }
|
|
||||||
}
|
|
||||||
unsafe impl ExternType for CMatrix4f {
|
|
||||||
type Id = type_id!("zeus::CMatrix4f");
|
|
||||||
type Kind = cxx::kind::Trivial;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cxx::bridge(namespace = "zeus")]
|
|
||||||
mod ffi {
|
|
||||||
unsafe extern "C++" {
|
|
||||||
include!("zeus/CVector2f.hpp");
|
|
||||||
include!("zeus/CVector3f.hpp");
|
|
||||||
include!("zeus/CVector4f.hpp");
|
|
||||||
include!("zeus/CMatrix4f.hpp");
|
|
||||||
include!("zeus/CColor.hpp");
|
|
||||||
include!("zeus/CRectangle.hpp");
|
|
||||||
include!("zeus/CAABox.hpp");
|
|
||||||
type CVector2f = crate::zeus::CVector2f;
|
|
||||||
type CVector3f = crate::zeus::CVector3f;
|
|
||||||
type CVector4f = crate::zeus::CVector4f;
|
|
||||||
type CMatrix4f = crate::zeus::CMatrix4f;
|
|
||||||
type CColor = crate::zeus::CColor;
|
|
||||||
type CRectangle = crate::zeus::CRectangle;
|
|
||||||
type CAABox = crate::zeus::CAABox;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<cgmath::Vector4<f32>> for CVector4f {
|
|
||||||
fn from(v: cgmath::Vector4<f32>) -> Self { Self { x: v.x, y: v.y, z: v.z, w: v.w } }
|
|
||||||
}
|
|
||||||
impl From<CVector4f> for cgmath::Vector4<f32> {
|
|
||||||
fn from(v: CVector4f) -> Self { Self { x: v.x, y: v.y, z: v.z, w: v.w } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<cgmath::Matrix4<f32>> for CMatrix4f {
|
|
||||||
fn from(m: cgmath::Matrix4<f32>) -> Self {
|
|
||||||
Self {
|
|
||||||
x: CVector4f::from(m.x),
|
|
||||||
y: CVector4f::from(m.y),
|
|
||||||
z: CVector4f::from(m.z),
|
|
||||||
w: CVector4f::from(m.w),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<CMatrix4f> for cgmath::Matrix4<f32> {
|
|
||||||
fn from(m: CMatrix4f) -> Self {
|
|
||||||
Self {
|
|
||||||
x: cgmath::Vector4::from(m.x),
|
|
||||||
y: cgmath::Vector4::from(m.y),
|
|
||||||
z: cgmath::Vector4::from(m.z),
|
|
||||||
w: cgmath::Vector4::from(m.w),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec2<f32>> for CVector2f {
|
|
||||||
fn from(v: Vec2<f32>) -> Self { Self { x: v.x, y: v.y, _p1: 0.0, _p2: 0.0 } }
|
|
||||||
}
|
|
||||||
impl From<CVector2f> for Vec2<f32> {
|
|
||||||
fn from(v: CVector2f) -> Self { Self { x: v.x, y: v.y } }
|
|
||||||
}
|
|
||||||
impl From<&CVector2f> for Vec2<f32> {
|
|
||||||
fn from(v: &CVector2f) -> Self { Self { x: v.x, y: v.y } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec3<f32>> for CVector3f {
|
|
||||||
fn from(v: Vec3<f32>) -> Self { Self { x: v.x, y: v.y, z: v.z, _p: 0.0 } }
|
|
||||||
}
|
|
||||||
impl From<CVector3f> for Vec3<f32> {
|
|
||||||
fn from(v: CVector3f) -> Self { Self { x: v.x, y: v.y, z: v.z } }
|
|
||||||
}
|
|
||||||
impl From<&CVector3f> for Vec3<f32> {
|
|
||||||
fn from(v: &CVector3f) -> Self { Self { x: v.x, y: v.y, z: v.z } }
|
|
||||||
}
|
|
Loading…
Reference in New Issue