Compare commits
4 commits
0f6cde2b52
...
7ac84da35a
Author | SHA1 | Date | |
---|---|---|---|
7ac84da35a | |||
4b70b6f59f | |||
4c0fa9e6ee | |||
fd8b343683 |
31 changed files with 3125 additions and 1221 deletions
876
Cargo.lock
generated
876
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
57
Cargo.toml
57
Cargo.toml
|
@ -2,6 +2,7 @@
|
|||
name = "minemod"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
default-run = "minemod"
|
||||
|
||||
[package.metadata.vita]
|
||||
title_id = "PKDC10002"
|
||||
|
@ -9,7 +10,7 @@ title_name = "MineMod"
|
|||
# assets = "somewhere" # TODO make this a build.rs output folder so we can use build.rs to manage vita assets
|
||||
|
||||
[profile.release]
|
||||
opt-level = 2
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
# panic = "abort" # Vita platform doesnt support abort currently
|
||||
|
@ -40,9 +41,9 @@ members = []
|
|||
# namespace = { path = "path/to/thing" }
|
||||
|
||||
[build-dependencies]
|
||||
shaderc = "0.8.3" # APACHE 2.0
|
||||
num_cpus = "1.16.0" # MIT
|
||||
jobserver = "0.1.32" # MIT
|
||||
shaderc = "0.8.3" # APACHE 2.0
|
||||
num_cpus = "1.16.0" # MIT
|
||||
jobserver = "0.1.32" # MIT
|
||||
gl_generator = "0.14" # APACHE 2.0
|
||||
|
||||
[dependencies]
|
||||
|
@ -53,39 +54,55 @@ tracing = { version = "0.1.41", features = [
|
|||
tracing-subscriber = "0.3.19" # MIT
|
||||
log = "0.4.25" # MIT
|
||||
|
||||
anyhow = "1.0.95" # MIT
|
||||
anyhow = "1.0.95" # MIT
|
||||
thiserror = "2.0.11" # MIT
|
||||
|
||||
nalgebra = "0.33.2" # APACHE 2.0
|
||||
cgmath = "0.18.0" # APACHE 2.0
|
||||
image = "0.25.5" # MIT
|
||||
glam = "0.29.2"
|
||||
cgmath = "0.18.0" # APACHE 2.0
|
||||
image = "0.25.5" # MIT
|
||||
# png = "0.17.16" # MIT
|
||||
tobj = { version = "4.0.2", features = ["log"] } # MIT
|
||||
rand = "0.9.0"
|
||||
#image_full = { package = "image", version = "0.25.5" }
|
||||
#image = { version = "0.25.5", default-features = false }
|
||||
#image_dds = "0.7.1"
|
||||
tbc = "0.3.0"
|
||||
|
||||
[target.'cfg(any(target_os = "windows",target_os = "linux",target_os = "macos"))'.dependencies]
|
||||
# vulkan
|
||||
tokio = { version = "1.43.0", features = ["full"] } # MIT
|
||||
ctrlc = "3.4.5" # MIT
|
||||
glfw = "0.59.0"
|
||||
#glfw = {version = "0.59.0", default-features = false, features = ["raw-window-handle-v0-6"]} # we literally just need the window management parts # nevermind this crashes immediately
|
||||
|
||||
## vulkan
|
||||
vulkanalia = { version = "0.26.0", features = [
|
||||
"libloading",
|
||||
"provisional",
|
||||
"window",
|
||||
] } # APACHE 2.0
|
||||
# kept for now so the vulkan demo works (also the tutorial is using it so once the tutorial is done and i fully understand how vulkan works i will be tossing winit fully and reworking the vulkan structure to better suit the engine)
|
||||
old_winit = { package = "winit", version = "0.29" } # APACHE 2.0 # dont update, many many breaking changes with no guides
|
||||
# end vulkan
|
||||
# opengl
|
||||
## end vulkan
|
||||
|
||||
## opengl
|
||||
glutin = "0.32.2" # APACHE 2.0
|
||||
glutin-winit = "0.5.0" # MIT
|
||||
raw-window-handle = "0.6" # MIT
|
||||
winit = { version = "0.30.0", default-features = false, features = ["rwh_06"] } # APACHE 2.0
|
||||
drm = { version = "0.14.1", optional = true } # MIT
|
||||
# end opengl
|
||||
ctrlc = "3.4.5" # MIT
|
||||
tokio = { version = "1.43.0", features = ["full"] } # MIT
|
||||
#glutin-winit = "0.5.0" # MIT # no winit >:(
|
||||
raw-window-handle = "0.6.2" # MIT
|
||||
# winit is hereby banished from this realm because it pissed me off >:(
|
||||
# its designed around mobile and desktop, but its mobile side sticks around even when its only being used for desktop and theres no way to cut it out
|
||||
# on top of its piss poor documentation around the breaking changes in update 0.3
|
||||
#winit = { version = "0.30.0", features = ["rwh_06"] }
|
||||
# drm = { version = "0.14.1", optional = true } # MIT
|
||||
## end opengl
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
wgpu = "24.0.0" # MIT
|
||||
|
||||
[target.'cfg(target_os = "vita")'.dependencies]
|
||||
vitasdk-sys = { version = "0.3.3", features = ["all-stubs"] } # MIT and VITASDK themselves use MIT as well
|
||||
vitasdk-sys = { version = "0.3.3", features = [
|
||||
"all-stubs",
|
||||
] } # MIT and VITASDK themselves use MIT as well
|
||||
concat-idents = "1.1.5" # MIT
|
||||
libc = "0.2.153" # MIT
|
||||
rand = "0.9.0" # MIT
|
||||
|
@ -103,9 +120,11 @@ tokio = { version = "1.36.0", features = [
|
|||
] } # MIT
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
gfx-backend-dx12 = "0.9.1"
|
||||
gfx-backend-dx11 = "0.9.0"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
metal = "0.31.0" # MIT
|
||||
objc = "0.2.7" # MIT
|
||||
objc = "0.2.7" # MIT
|
||||
|
|
4
build.rs
4
build.rs
|
@ -12,14 +12,14 @@ static MAX_THREAD_COUNT:std::sync::LazyLock<usize> = std::sync::LazyLock::new(||
|
|||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
setup_gl_bindings()?;
|
||||
generate_opengl_bindings()?;
|
||||
|
||||
compile_shaders()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_gl_bindings() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn generate_opengl_bindings() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let path = std::path::PathBuf::from(&std::env::var("OUT_DIR")?).join("gl_bindings.rs");
|
||||
|
||||
// Skip generation if the file is already there
|
||||
|
|
21
src/bin/bcntool.rs
Normal file
21
src/bin/bcntool.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use log::*;
|
||||
|
||||
// This tool will be used to convert any common image format to BCn of a specified level or BC1 by default.
|
||||
fn main() {
|
||||
tracing_subscriber::fmt()
|
||||
.compact()
|
||||
.with_timer(tracing_subscriber::fmt::time::uptime())
|
||||
.with_ansi(true)
|
||||
.with_level(true)
|
||||
.with_thread_names(true)
|
||||
.with_max_level(if cfg!(debug_assertions) {
|
||||
tracing::level_filters::LevelFilter::DEBUG
|
||||
} else {
|
||||
tracing::level_filters::LevelFilter::INFO
|
||||
})
|
||||
.init();
|
||||
// pretty_env_logger::formatted_timed_builder().filter_level(LevelFilter::Info).init();
|
||||
info!("We support BC1, BC3, and BC4 via the tbc crate.");
|
||||
info!("Unfortunately this tool is not currently implemented.");
|
||||
unimplemented!();
|
||||
}
|
|
@ -1,16 +1,114 @@
|
|||
pub(crate) trait GraphicsCommander {}
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use glam::Vec3;
|
||||
use resources::*;
|
||||
|
||||
//use crate::graphics_engines::Renderable;
|
||||
|
||||
pub mod resources;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cataclysm {
|
||||
game_objects:Vec<GameObject>,
|
||||
pub creation_time:Instant,
|
||||
textures: Arc<ResourceTable<GraphicsTexture>>,
|
||||
vertex_objects: Arc<ResourceTable<GraphicsVBO>>,
|
||||
shader_objects: Arc<ResourceTable<GraphicsShader>>,
|
||||
sounds: Arc<ResourceTable<AudioSFX>>,
|
||||
music: Arc<ResourceTable<AudioMusic>>,
|
||||
pub chunks: HashMap<(u32, u32, u32), CataclysmChunk>,
|
||||
}
|
||||
|
||||
pub struct GameObject {
|
||||
pos:(f64, f64, f64),
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CataclysmChunk {
|
||||
pub simple_blocks:HashMap<(u8, u8, u8), SimpleBlock>,
|
||||
pub fancy_blocks: HashMap<Vec3, FancyBlock>,
|
||||
pub smart_blocks: HashMap<Vec3, SmartBlock>,
|
||||
pub entities: Vec<Entity>,
|
||||
}
|
||||
|
||||
pub enum ModelKind {
|
||||
Cube,
|
||||
Complex(),
|
||||
impl Cataclysm {
|
||||
pub const CHUNK_SIZE:u8 = 32;
|
||||
pub const ENTITY_RUN_LIMIT:u8 = 5;
|
||||
pub const WORLD_SIZE:(u8, u8) = (2, 2);
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
creation_time: Instant::now(),
|
||||
textures: ResourceTable::new(),
|
||||
vertex_objects:ResourceTable::new(),
|
||||
shader_objects:ResourceTable::new(),
|
||||
sounds: ResourceTable::new(),
|
||||
music: ResourceTable::new(),
|
||||
chunks: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResourceReference {}
|
||||
impl CataclysmChunk {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
simple_blocks:HashMap::new(),
|
||||
fancy_blocks: HashMap::new(),
|
||||
smart_blocks: HashMap::new(),
|
||||
entities: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SimpleBlock {}
|
||||
|
||||
//impl Renderable for SimpleBlock {
|
||||
#[rustfmt::skip]
|
||||
fn vertext_data<'a>() -> (&'a [f32], &'a [u32]) {
|
||||
(&[
|
||||
-1.0, -1.0, 0.5, //0
|
||||
1.0, -1.0, 0.5, //1
|
||||
-1.0, 1.0, 0.5, //2
|
||||
1.0, 1.0, 0.5, //3
|
||||
-1.0, -1.0, -0.5, //4
|
||||
1.0, -1.0, -0.5, //5
|
||||
-1.0, 1.0, -0.5, //6
|
||||
1.0, 1.0, -0.5 //7
|
||||
],
|
||||
&[
|
||||
//Top
|
||||
2, 6, 7,
|
||||
2, 3, 7,
|
||||
|
||||
//Bottom
|
||||
0, 4, 5,
|
||||
0, 1, 5,
|
||||
|
||||
//Left
|
||||
0, 2, 6,
|
||||
0, 4, 6,
|
||||
|
||||
//Right
|
||||
1, 3, 7,
|
||||
1, 5, 7,
|
||||
|
||||
//Front
|
||||
0, 2, 3,
|
||||
0, 1, 3,
|
||||
|
||||
//Back
|
||||
4, 6, 7,
|
||||
4, 5, 7
|
||||
]
|
||||
)
|
||||
}
|
||||
//}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FancyBlock {
|
||||
pub pos:Vec3,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SmartBlock {
|
||||
pub pos:Vec3,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entity {
|
||||
pub pos:Vec3,
|
||||
}
|
||||
|
|
172
src/cataclysm/resources/mod.rs
Normal file
172
src/cataclysm/resources/mod.rs
Normal file
|
@ -0,0 +1,172 @@
|
|||
use ::std::collections::HashMap;
|
||||
|
||||
use ::std::sync::Arc;
|
||||
use ::std::sync::Weak;
|
||||
use ::std::sync::atomic::AtomicBool;
|
||||
|
||||
/// You should prefer cloning a resource out and replacing the resource when it becomes stale
|
||||
#[derive(Debug)]
|
||||
pub struct ResourceTable<T> {
|
||||
resources:HashMap<String, Resource<T>>,
|
||||
}
|
||||
|
||||
pub const DEFAULT_RESOURCE_NAME:&&'static str = &"resource.default";
|
||||
|
||||
impl<T> ResourceTable<T> {
|
||||
pub fn add_resource(self: &Arc<Self>, name:&dyn ToString, thing:T) {
|
||||
let resource = Resource {
|
||||
inner:Arc::new(ResourceInner::new(thing)),
|
||||
name: Arc::new(name.to_string()),
|
||||
table:Arc::downgrade(&self),
|
||||
};
|
||||
}
|
||||
|
||||
// could be a macro? id prefer if it could work on self like it does for add_resource
|
||||
pub fn upgrade(table:&Weak<Self>) -> Arc<Self> {
|
||||
if let Some(table) = table.upgrade() {
|
||||
table
|
||||
} else {
|
||||
panic!(
|
||||
"A resource replace was attempted but it appears the owning table doesnt exist? Please ensure the owning table is not dropped, this is not expected to occur."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn get_resource(&self, name:&dyn ToString) -> Option<Resource<T>> { self.resources.get(&name.to_string()).cloned() }
|
||||
|
||||
///
|
||||
pub fn get_resource_or_default(&self, name:&dyn ToString) -> Resource<T> {
|
||||
let resource = self.resources.get(&name.to_string());
|
||||
|
||||
// we do this instead of .unwrap_or(T) because unwrap_or is not lazy
|
||||
// and will calculate T immediately whether it is needed or not
|
||||
if let Some(resource) = resource {
|
||||
resource.clone()
|
||||
} else {
|
||||
if let Some(resource) = self.get_resource(DEFAULT_RESOURCE_NAME) {
|
||||
resource
|
||||
} else {
|
||||
unreachable!(
|
||||
"Since resource tables get created with the `{DEFAULT_RESOURCE_NAME}` resource and you cannot remove it, this situation is impossible to occur."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Default> ResourceTable<T> {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let table = Arc::new(Self { resources:HashMap::new() });
|
||||
table.add_resource(&"default", T::default());
|
||||
table
|
||||
}
|
||||
}
|
||||
|
||||
// make an enum when mutable resources pop up?
|
||||
// mutable resources are very strange since they are global
|
||||
// instead any mutable *instances* of resource should be just that, an instance
|
||||
// there is currently no valid reason for a mutable resource
|
||||
// game logic has no business here
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Resource<T> {
|
||||
inner: Arc<ResourceInner<T>>,
|
||||
name: Arc<String>,
|
||||
/// Fast access to the table the resource belongs to
|
||||
pub table:Weak<ResourceTable<T>>,
|
||||
}
|
||||
|
||||
impl<T> Resource<T> {
|
||||
pub fn is_stale(&self) -> bool { self.inner.stale.load(std::sync::atomic::Ordering::SeqCst) }
|
||||
|
||||
// This permanently marks this resource as stale and all usages of it should be upgraded via the replace function
|
||||
pub fn expire(&mut self) {
|
||||
//self.table
|
||||
self.inner.stale.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
// This function will fetch the new resource under the same name, or will return itself as an error if there is none
|
||||
// Typically you should prefer replace_or_default unless you have a specific reason to handle the failure/need to keep using the original on failure
|
||||
pub fn replace(self) -> Result<Self, Self> {
|
||||
let table = ResourceTable::upgrade(&self.table);
|
||||
if let Some(resource) = table.get_resource(&self.name) {
|
||||
Ok(resource)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Default> Resource<T> {
|
||||
pub fn replace_or_default(self) -> Self {
|
||||
if let Some(table) = self.table.upgrade() {
|
||||
table.get_resource_or_default(&self.name)
|
||||
} else {
|
||||
panic!(
|
||||
"A resource replace was attempted but it appears the owning table doesnt exist? Please ensure the owning table is not dropped, this is not expected to occur."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Resource<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner:self.inner.clone(),
|
||||
name: self.name.clone(),
|
||||
table:self.table.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ResourceInner<T> {
|
||||
stale:AtomicBool,
|
||||
thing:T,
|
||||
}
|
||||
|
||||
impl<T> ResourceInner<T> {
|
||||
fn new(thing:T) -> Self {
|
||||
Self {
|
||||
stale:AtomicBool::new(false),
|
||||
thing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// should contain both source and compiled version?
|
||||
#[derive(Debug)]
|
||||
pub struct GraphicsShader {}
|
||||
|
||||
impl Default for GraphicsShader {
|
||||
fn default() -> Self { todo!() }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GraphicsTexture {}
|
||||
|
||||
impl Default for GraphicsTexture {
|
||||
fn default() -> Self { todo!() }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GraphicsVBO {}
|
||||
|
||||
impl Default for GraphicsVBO {
|
||||
fn default() -> Self { todo!() }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AudioSFX {}
|
||||
|
||||
impl Default for AudioSFX {
|
||||
fn default() -> Self { todo!() }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AudioMusic {}
|
||||
|
||||
impl Default for AudioMusic {
|
||||
fn default() -> Self { todo!() }
|
||||
}
|
|
@ -1,6 +1,15 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use glfw::PWindow;
|
||||
|
||||
use crate::cataclysm::Cataclysm;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||
pub mod vulkan;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||
pub mod vulkan_gc;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||
pub mod opengl;
|
||||
|
||||
|
@ -11,3 +20,91 @@ pub mod opengl;
|
|||
#[cfg(target_os = "windows")] pub mod directx;
|
||||
|
||||
#[cfg(target_os = "vita")] pub mod gxm;
|
||||
|
||||
type Vec2f = cgmath::Vector2<f32>;
|
||||
type Vec3f = cgmath::Vector3<f32>;
|
||||
type Vec4f = cgmath::Vector4<f32>;
|
||||
|
||||
type Vec2d = cgmath::Vector2<f64>;
|
||||
type Vec3d = cgmath::Vector3<f64>;
|
||||
type Vec4d = cgmath::Vector4<f64>;
|
||||
|
||||
type Mat2f = cgmath::Matrix2<f32>;
|
||||
type Mat3f = cgmath::Matrix3<f32>;
|
||||
type Mat4f = cgmath::Matrix4<f32>;
|
||||
|
||||
type Mat2d = cgmath::Matrix2<f64>;
|
||||
type Mat3d = cgmath::Matrix3<f64>;
|
||||
type Mat4d = cgmath::Matrix4<f64>;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct VertexData {
|
||||
pub verts: Box<[Vertex]>,
|
||||
pub indices:Box<[u32]>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Vertex {
|
||||
pub pos: Vec2f,
|
||||
pub color:Vec3f,
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
pub const fn new(pos:Vec2f, color:Vec3f) -> Self { Self { pos, color } }
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub trait GraphicsCommander {
|
||||
/// Used to halt all render calls and calculations
|
||||
/// May deallocate graphics memory
|
||||
fn suspend_rendering(&mut self);
|
||||
/// Used to resume all render calls and calculations
|
||||
/// May allocate graphics memory
|
||||
fn resume_rendering(&mut self);
|
||||
|
||||
/// The first called function, used to register self to window and collect needed info from the system
|
||||
fn initialize_controller_system(&mut self, window: &mut PWindow);
|
||||
|
||||
/// Returns a formatted multiline string detailing various aspects of the render system, meant to be immediately presented to the user through the engine
|
||||
fn get_renderer_information(&self) -> String;
|
||||
|
||||
/// Used to load and potentially compile a shader
|
||||
fn load_shader_program(&mut self);
|
||||
|
||||
// TODO cataclysm render object
|
||||
|
||||
// will need a reference to game engine objects
|
||||
// replace with generic draw call? draw call will draw instructions to image, image may be framebuffer or not, allows gpu accelerated dynamic texture stuff
|
||||
fn render(&mut self, window:&mut PWindow, cataclysm:&mut Cataclysm) -> Result<()>;
|
||||
|
||||
fn create_vxos(&mut self, data:&[VertexData]);
|
||||
fn destroy_vxos(&mut self);
|
||||
// make generic for assets?
|
||||
fn register_texture(&mut self);
|
||||
fn destroy_texture(&mut self);
|
||||
|
||||
// cleans up all data from memory, used when exiting or switching graphics backends
|
||||
//fn cleanup(&mut self);
|
||||
fn exit(&mut self);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct UniformBufferObject {
|
||||
model:Mat4f,
|
||||
view: Mat4f,
|
||||
proj: Mat4f,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub enum GMode {
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
#[cfg(target_os = "macos")]
|
||||
Metal,
|
||||
#[cfg(target_os = "windows")]
|
||||
DirectX,
|
||||
}
|
||||
|
|
|
@ -1,378 +1,229 @@
|
|||
use std::error::Error;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::Deref;
|
||||
use crate::cataclysm::Cataclysm;
|
||||
use crate::graphics_engines::Vertex;
|
||||
|
||||
use gl::types::GLfloat;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::KeyEvent;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::keyboard::Key;
|
||||
use winit::keyboard::NamedKey;
|
||||
use winit::raw_window_handle::HasWindowHandle;
|
||||
use winit::window::Window;
|
||||
use winit::window::WindowAttributes;
|
||||
use super::GraphicsCommander;
|
||||
use super::Mat4f;
|
||||
use super::UniformBufferObject;
|
||||
|
||||
use glutin::config::Config;
|
||||
use glutin::config::ConfigTemplateBuilder;
|
||||
use glutin::config::GetGlConfig;
|
||||
use glutin::context::ContextApi;
|
||||
use glutin::context::ContextAttributesBuilder;
|
||||
use glutin::context::NotCurrentContext;
|
||||
use glutin::context::PossiblyCurrentContext;
|
||||
use glutin::context::Version;
|
||||
use glutin::display::GetGlDisplay;
|
||||
use glutin::prelude::*;
|
||||
use glutin::surface::Surface;
|
||||
use glutin::surface::SwapInterval;
|
||||
use glutin::surface::WindowSurface;
|
||||
use anyhow::Result;
|
||||
|
||||
use glutin_winit::DisplayBuilder;
|
||||
use glutin_winit::GlWindow;
|
||||
use cgmath::point3;
|
||||
use cgmath::vec3;
|
||||
use cgmath::Deg;
|
||||
use glfw::Context;
|
||||
use glfw::PWindow;
|
||||
|
||||
use log::*;
|
||||
|
||||
pub mod gl {
|
||||
// #![allow(clippy::all)]
|
||||
#![allow(clippy::all)]
|
||||
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
||||
|
||||
pub use Gles2 as Gl;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
||||
}
|
||||
|
||||
pub fn main(event_loop:winit::event_loop::EventLoop<()>) -> Result<(), Box<dyn Error>> {
|
||||
// The template will match only the configurations supporting rendering
|
||||
// to windows.
|
||||
//
|
||||
// XXX We force transparency only on macOS, given that EGL on X11 doesn't
|
||||
// have it, but we still want to show window. The macOS situation is like
|
||||
// that, because we can query only one config at a time on it, but all
|
||||
// normal platforms will return multiple configs, so we can find the config
|
||||
// with transparency ourselves inside the `reduce`.
|
||||
let template = ConfigTemplateBuilder::new().with_alpha_size(8).with_transparency(cfg!(cgl_backend));
|
||||
impl GraphicsCommander for CataclysmOGL {
|
||||
fn suspend_rendering(&mut self) {
|
||||
}
|
||||
|
||||
let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes()));
|
||||
fn resume_rendering(&mut self) {
|
||||
}
|
||||
|
||||
let mut app = App::new(template, display_builder);
|
||||
event_loop.run_app(&mut app)?;
|
||||
fn render(&mut self, window:&mut glfw::PWindow, cataclysm:&mut Cataclysm) -> anyhow::Result<()> {
|
||||
// let model = Mat4f::from_axis_angle(vec3(0.0, 0.0, 1.0), Deg(45.0) * cataclysm.creation_time.elapsed().as_secs_f32());
|
||||
|
||||
app.exit_state
|
||||
}
|
||||
// let view = Mat4f::look_at_rh(point3(2.0, 5.0, 2.0), point3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0));
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop:&ActiveEventLoop) {
|
||||
let (window, gl_config) = match &self.gl_display {
|
||||
// We just created the event loop, so initialize the display, pick the config, and
|
||||
// create the context.
|
||||
GlDisplayCreationState::Builder(display_builder) => {
|
||||
let (window, gl_config) = match display_builder.clone().build(event_loop, self.template.clone(), gl_config_picker) {
|
||||
Ok((window, gl_config)) => (window.unwrap(), gl_config),
|
||||
Err(err) => {
|
||||
self.exit_state = Err(err);
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
};
|
||||
// let mut proj = cgmath::perspective(
|
||||
// Deg(45.0),
|
||||
// self.data.swapchain_extent.width as f32 / self.data.swapchain_extent.height as f32,
|
||||
// 0.1,
|
||||
// 10.0,
|
||||
// );
|
||||
|
||||
println!("Picked a config with {} samples", gl_config.num_samples());
|
||||
// // cgmath was made with opengl in mind, this fixes it to work with vulkan
|
||||
// proj[1][1] *= -1.0;
|
||||
|
||||
// Mark the display as initialized to not recreate it on resume, since the
|
||||
// display is valid until we explicitly destroy it.
|
||||
self.gl_display = GlDisplayCreationState::Init;
|
||||
// let ubo = UniformBufferObject { model, view, proj };
|
||||
|
||||
// Create gl context.
|
||||
self.gl_context = Some(create_gl_context(&window, &gl_config).treat_as_possibly_current());
|
||||
//let memory = self.device.map_memory(
|
||||
// self.data.uniform_buffers_memory[image_index],
|
||||
// 0,
|
||||
// size_of::<UniformBufferObject>() as u64,
|
||||
// vk::MemoryMapFlags::empty(),
|
||||
//)?;
|
||||
|
||||
(window, gl_config)
|
||||
},
|
||||
GlDisplayCreationState::Init => {
|
||||
println!("Recreating window in `resumed`");
|
||||
// Pick the config which we already use for the context.
|
||||
let gl_config = self.gl_context.as_ref().unwrap().config();
|
||||
match glutin_winit::finalize_window(event_loop, window_attributes(), &gl_config) {
|
||||
Ok(window) => (window, gl_config),
|
||||
Err(err) => {
|
||||
self.exit_state = Err(err.into());
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
//memcpy(&ubo, memory.cast(), 1);
|
||||
|
||||
//self.device.unmap_memory(self.data.uniform_buffers_memory[image_index]);
|
||||
|
||||
let (red, green, blue, alpha) = (0.0125, 0.0094, 0.0071, 1.0);
|
||||
unsafe {
|
||||
self.gl.UseProgram(self.program);
|
||||
|
||||
//info!("Clearing screen with color {red}|{green}|{blue}|{alpha}.");
|
||||
self.gl.ClearColor(red, green, blue, alpha);
|
||||
self.gl.Clear(gl::COLOR_BUFFER_BIT);
|
||||
|
||||
for vxo in &self.vxos {
|
||||
//info!("Bind.");
|
||||
self.gl.BindVertexArray(vxo.0);
|
||||
for vbo in &vxo.1 {
|
||||
self.gl.BindBuffer(gl::ARRAY_BUFFER, *vbo);
|
||||
//info!("DrawArrays.");
|
||||
self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let attrs = window.build_surface_attributes(Default::default()).expect("Failed to build surface attributes");
|
||||
let gl_surface = unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() };
|
||||
|
||||
// The context needs to be current for the Renderer to set up shaders and
|
||||
// buffers. It also performs function loading, which needs a current context on
|
||||
// WGL.
|
||||
let gl_context = self.gl_context.as_ref().unwrap();
|
||||
gl_context.make_current(&gl_surface).unwrap();
|
||||
|
||||
self.renderer.get_or_insert_with(|| Renderer::new(&gl_config.display()));
|
||||
|
||||
// Try setting vsync.
|
||||
if let Err(res) = gl_surface.set_swap_interval(gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) {
|
||||
eprintln!("Error setting vsync: {res:?}");
|
||||
}
|
||||
}
|
||||
|
||||
assert!(self.state.replace(AppState { gl_surface, window }).is_none());
|
||||
window.swap_buffers();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn suspended(&mut self, _event_loop:&ActiveEventLoop) {
|
||||
// This event is only raised on Android, where the backing NativeWindow for a GL
|
||||
// Surface can appear and disappear at any moment.
|
||||
println!("Android window removed");
|
||||
fn register_texture(&mut self) { todo!() }
|
||||
|
||||
// Destroy the GL Surface and un-current the GL Context before ndk-glue releases
|
||||
// the window back to the system.
|
||||
self.state = None;
|
||||
fn destroy_texture(&mut self) { todo!() }
|
||||
|
||||
// Make context not current.
|
||||
self.gl_context = Some(self.gl_context.take().unwrap().make_not_current().unwrap().treat_as_possibly_current());
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop:&ActiveEventLoop, _window_id:winit::window::WindowId, event:WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::Resized(size) if size.width != 0 && size.height != 0 => {
|
||||
// Some platforms like EGL require resizing GL surface to update the size
|
||||
// Notable platforms here are Wayland and macOS, other don't require it
|
||||
// and the function is no-op, but it's wise to resize it for portability
|
||||
// reasons.
|
||||
if let Some(AppState { gl_surface, window: _ }) = self.state.as_ref() {
|
||||
let gl_context = self.gl_context.as_ref().unwrap();
|
||||
gl_surface.resize(gl_context, NonZeroU32::new(size.width).unwrap(), NonZeroU32::new(size.height).unwrap());
|
||||
|
||||
let renderer = self.renderer.as_ref().unwrap();
|
||||
renderer.resize(size.width as i32, size.height as i32);
|
||||
}
|
||||
},
|
||||
WindowEvent::CloseRequested |
|
||||
WindowEvent::KeyboardInput {
|
||||
event: KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => event_loop.exit(),
|
||||
_ => (),
|
||||
fn exit(&mut self) {
|
||||
info!("Destroying OpenGL instance.");
|
||||
unsafe {
|
||||
self.gl.DeleteProgram(self.program);
|
||||
self.destroy_vxos();
|
||||
}
|
||||
}
|
||||
|
||||
fn exiting(&mut self, _event_loop:&ActiveEventLoop) {
|
||||
// NOTE: The handling below is only needed due to nvidia on Wayland to not crash
|
||||
// on exit due to nvidia driver touching the Wayland display from on
|
||||
// `exit` hook.
|
||||
let _gl_display = self.gl_context.take().unwrap().display();
|
||||
fn create_vxos(&mut self, data:&[super::VertexData]) {
|
||||
for data in data {
|
||||
info!("Creating OpenGL VAO.");
|
||||
let vao = unsafe {
|
||||
let mut vao = std::mem::zeroed();
|
||||
self.gl.GenVertexArrays(1, &mut vao);
|
||||
self.gl.BindVertexArray(vao);
|
||||
vao
|
||||
};
|
||||
|
||||
// Clear the window.
|
||||
self.state = None;
|
||||
#[cfg(egl_backend)]
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
if let glutin::display::Display::Egl(display) = _gl_display {
|
||||
unsafe {
|
||||
display.terminate();
|
||||
info!("Creating OpenGL VBO for vertices.");
|
||||
let vbo = unsafe {
|
||||
let mut vbo = std::mem::zeroed();
|
||||
self.gl.GenBuffers(1, &mut vbo);
|
||||
self.gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
|
||||
self.gl.BufferData(
|
||||
gl::ARRAY_BUFFER,
|
||||
dbg!(data.verts.len() * std::mem::size_of::<Vertex>()) as gl::types::GLsizeiptr,
|
||||
data.verts.as_ptr() as *const _,
|
||||
gl::STATIC_DRAW,
|
||||
);
|
||||
vbo
|
||||
};
|
||||
|
||||
let (pos, col) = unsafe {
|
||||
info!("Applying shader attributes.");
|
||||
let pos_attrib = self.gl.GetAttribLocation(self.program, b"position\0".as_ptr() as *const _);
|
||||
self.gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
|
||||
self.gl.VertexAttribPointer(
|
||||
pos_attrib as gl::types::GLuint,
|
||||
2,
|
||||
gl::FLOAT,
|
||||
gl::FALSE,
|
||||
std::mem::size_of::<f32>() as gl::types::GLsizei,
|
||||
std::ptr::null(),
|
||||
);
|
||||
let color_attrib = self.gl.GetAttribLocation(self.program, b"color\0".as_ptr() as *const _);
|
||||
self.gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
|
||||
self.gl.VertexAttribPointer(
|
||||
color_attrib as gl::types::GLuint,
|
||||
3,
|
||||
gl::FLOAT,
|
||||
gl::FALSE,
|
||||
std::mem::size_of::<f32>() as gl::types::GLsizei,
|
||||
(2 * std::mem::size_of::<f32>()) as *const () as *const _,
|
||||
);
|
||||
(pos_attrib, color_attrib)
|
||||
};
|
||||
|
||||
self.vxos.push((vao, vec![vbo]));
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy_vxos(&mut self) {
|
||||
unsafe {
|
||||
for _ in 0..self.vxos.len() {
|
||||
let vxo = self.vxos.remove(0);
|
||||
self.gl.DeleteVertexArrays(1, &vxo.0);
|
||||
for vbo in vxo.1.iter().take(vxo.1.len()) {
|
||||
self.gl.DeleteBuffers(1, vbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop:&ActiveEventLoop) {
|
||||
if let Some(AppState { gl_surface, window }) = self.state.as_ref() {
|
||||
let gl_context = self.gl_context.as_ref().unwrap();
|
||||
let renderer = self.renderer.as_ref().unwrap();
|
||||
renderer.draw();
|
||||
window.request_redraw();
|
||||
|
||||
gl_surface.swap_buffers(gl_context).unwrap();
|
||||
|
||||
fn initialize_controller_system(&mut self, window: &mut PWindow) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_gl_context(window:&Window, gl_config:&Config) -> NotCurrentContext {
|
||||
let raw_window_handle = window.window_handle().ok().map(|wh| wh.as_raw());
|
||||
|
||||
// The context creation part.
|
||||
let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);
|
||||
|
||||
// Since glutin by default tries to create OpenGL core context, which may not be
|
||||
// present we should try gles.
|
||||
let fallback_context_attributes = ContextAttributesBuilder::new()
|
||||
.with_context_api(ContextApi::Gles(None))
|
||||
.build(raw_window_handle);
|
||||
|
||||
// There are also some old devices that support neither modern OpenGL nor GLES.
|
||||
// To support these we can try and create a 2.1 context.
|
||||
let legacy_context_attributes = ContextAttributesBuilder::new()
|
||||
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
|
||||
.build(raw_window_handle);
|
||||
|
||||
// Reuse the uncurrented context from a suspended() call if it exists, otherwise
|
||||
// this is the first time resumed() is called, where the context still
|
||||
// has to be created.
|
||||
let gl_display = gl_config.display();
|
||||
|
||||
unsafe {
|
||||
gl_display.create_context(gl_config, &context_attributes).unwrap_or_else(|_| {
|
||||
gl_display.create_context(gl_config, &fallback_context_attributes).unwrap_or_else(|_| {
|
||||
gl_display
|
||||
.create_context(gl_config, &legacy_context_attributes)
|
||||
.expect("failed to create context")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn window_attributes() -> WindowAttributes {
|
||||
Window::default_attributes()
|
||||
.with_transparent(true)
|
||||
.with_title("Glutin triangle gradient example (press Escape to exit)")
|
||||
}
|
||||
|
||||
enum GlDisplayCreationState {
|
||||
/// The display was not build yet.
|
||||
Builder(DisplayBuilder),
|
||||
/// The display was already created for the application.
|
||||
Init,
|
||||
}
|
||||
|
||||
struct App {
|
||||
template: ConfigTemplateBuilder,
|
||||
renderer: Option<Renderer>,
|
||||
// NOTE: `AppState` carries the `Window`, thus it should be dropped after everything else.
|
||||
state: Option<AppState>,
|
||||
gl_context:Option<PossiblyCurrentContext>,
|
||||
gl_display:GlDisplayCreationState,
|
||||
exit_state:Result<(), Box<dyn Error>>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new(template:ConfigTemplateBuilder, display_builder:DisplayBuilder) -> Self {
|
||||
Self {
|
||||
template,
|
||||
gl_display:GlDisplayCreationState::Builder(display_builder),
|
||||
exit_state:Ok(()),
|
||||
gl_context:None,
|
||||
state:None,
|
||||
renderer:None,
|
||||
|
||||
fn get_renderer_information(&self) -> String {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn load_shader_program(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
gl_surface:Surface<WindowSurface>,
|
||||
// NOTE: Window should be dropped after all resources created using its
|
||||
// raw-window-handle.
|
||||
window: Window,
|
||||
}
|
||||
use std::ffi::CStr;
|
||||
use std::ops::Deref;
|
||||
|
||||
// Find the config with the maximum number of samples, so our triangle will be
|
||||
// smooth.
|
||||
pub fn gl_config_picker(configs:Box<dyn Iterator<Item=Config>+'_>) -> Config {
|
||||
configs
|
||||
.reduce(|accum, config| {
|
||||
let transparency_check = config.supports_transparency().unwrap_or(false) & !accum.supports_transparency().unwrap_or(false);
|
||||
type VAO = gl::types::GLuint;
|
||||
type VBO = gl::types::GLuint;
|
||||
|
||||
if transparency_check || config.num_samples() > accum.num_samples() {
|
||||
config
|
||||
} else {
|
||||
accum
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
pub struct CataclysmOGL {
|
||||
program:gl::types::GLuint,
|
||||
vao: gl::types::GLuint,
|
||||
vbo: gl::types::GLuint,
|
||||
vxos: Vec<(VAO, Vec<VBO>)>,
|
||||
gl: gl::Gl,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new<D:GlDisplay>(gl_display:&D) -> Self {
|
||||
impl CataclysmOGL {
|
||||
pub fn create(window:&mut PWindow) -> Result<Self> {
|
||||
info!("Creating OpenGL renderer instance.");
|
||||
|
||||
unsafe {
|
||||
let gl = gl::Gl::load_with(|symbol| {
|
||||
let symbol = CString::new(symbol).unwrap();
|
||||
gl_display.get_proc_address(symbol.as_c_str()).cast()
|
||||
});
|
||||
let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));
|
||||
|
||||
if let Some(renderer) = get_gl_string(&gl, gl::RENDERER) {
|
||||
println!("Running on {}", renderer.to_string_lossy());
|
||||
info!("Running on {}", renderer.to_string_lossy());
|
||||
}
|
||||
if let Some(version) = get_gl_string(&gl, gl::VERSION) {
|
||||
println!("OpenGL Version {}", version.to_string_lossy());
|
||||
info!("OpenGL Version {}", version.to_string_lossy());
|
||||
}
|
||||
|
||||
if let Some(shaders_version) = get_gl_string(&gl, gl::SHADING_LANGUAGE_VERSION) {
|
||||
println!("Shaders version on {}", shaders_version.to_string_lossy());
|
||||
info!("Shaders version on {}", shaders_version.to_string_lossy());
|
||||
}
|
||||
|
||||
info!("Loading shaders.");
|
||||
let vertex_shader = create_shader(&gl, gl::VERTEX_SHADER, VERTEX_SHADER_SOURCE);
|
||||
let fragment_shader = create_shader(&gl, gl::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE);
|
||||
|
||||
info!("Creating OpenGL shader program.");
|
||||
let program = gl.CreateProgram();
|
||||
|
||||
gl.AttachShader(program, vertex_shader);
|
||||
gl.AttachShader(program, fragment_shader);
|
||||
|
||||
gl.LinkProgram(program);
|
||||
|
||||
gl.UseProgram(program);
|
||||
|
||||
info!("Deleting shader objects.");
|
||||
gl.DeleteShader(vertex_shader);
|
||||
gl.DeleteShader(fragment_shader);
|
||||
|
||||
let mut vao = std::mem::zeroed();
|
||||
gl.GenVertexArrays(1, &mut vao);
|
||||
gl.BindVertexArray(vao);
|
||||
info!("Linking shader program.");
|
||||
gl.LinkProgram(program);
|
||||
|
||||
let mut vbo = std::mem::zeroed();
|
||||
gl.GenBuffers(1, &mut vbo);
|
||||
gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
|
||||
gl.BufferData(
|
||||
gl::ARRAY_BUFFER,
|
||||
(VERTEX_DATA.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr,
|
||||
VERTEX_DATA.as_ptr() as *const _,
|
||||
gl::STATIC_DRAW,
|
||||
);
|
||||
|
||||
let pos_attrib = gl.GetAttribLocation(program, b"position\0".as_ptr() as *const _);
|
||||
let color_attrib = gl.GetAttribLocation(program, b"color\0".as_ptr() as *const _);
|
||||
gl.VertexAttribPointer(
|
||||
pos_attrib as gl::types::GLuint,
|
||||
2,
|
||||
gl::FLOAT,
|
||||
0,
|
||||
5 * std::mem::size_of::<f32>() as gl::types::GLsizei,
|
||||
std::ptr::null(),
|
||||
);
|
||||
gl.VertexAttribPointer(
|
||||
color_attrib as gl::types::GLuint,
|
||||
3,
|
||||
gl::FLOAT,
|
||||
0,
|
||||
5 * std::mem::size_of::<f32>() as gl::types::GLsizei,
|
||||
(2 * std::mem::size_of::<f32>()) as *const () as *const _,
|
||||
);
|
||||
gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
|
||||
gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
|
||||
|
||||
Self { program, vao, vbo, gl }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self) { self.draw_with_clear_color(0.1, 0.1, 0.1, 0.9) }
|
||||
|
||||
pub fn draw_with_clear_color(&self, red:GLfloat, green:GLfloat, blue:GLfloat, alpha:GLfloat) {
|
||||
unsafe {
|
||||
self.gl.UseProgram(self.program);
|
||||
|
||||
self.gl.BindVertexArray(self.vao);
|
||||
self.gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo);
|
||||
|
||||
self.gl.ClearColor(red, green, blue, alpha);
|
||||
self.gl.Clear(gl::COLOR_BUFFER_BIT);
|
||||
self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
|
||||
info!("OpenGL renderer instance created.");
|
||||
Ok(Self {
|
||||
program,
|
||||
vxos:vec![/*(vao, vec![vbo])*/],
|
||||
gl,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,22 +234,12 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
impl Deref for Renderer {
|
||||
impl Deref for CataclysmOGL {
|
||||
type Target = gl::Gl;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.gl }
|
||||
}
|
||||
|
||||
impl Drop for Renderer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.gl.DeleteProgram(self.program);
|
||||
self.gl.DeleteBuffers(1, &self.vbo);
|
||||
self.gl.DeleteVertexArrays(1, &self.vao);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_shader(gl:&gl::Gl, shader:gl::types::GLenum, source:&[u8]) -> gl::types::GLuint {
|
||||
let shader = gl.CreateShader(shader);
|
||||
gl.ShaderSource(shader, 1, [source.as_ptr().cast()].as_ptr(), std::ptr::null());
|
||||
|
@ -413,12 +254,25 @@ fn get_gl_string(gl:&gl::Gl, variant:gl::types::GLenum) -> Option<&'static CStr>
|
|||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
static VERTEX_DATA: [f32; 15] = [
|
||||
-0.5, -0.5, 1.0, 0.0, 0.0,
|
||||
0.0, 0.5, 0.0, 1.0, 0.0,
|
||||
0.5, -0.5, 0.0, 0.0, 1.0,
|
||||
];
|
||||
const VERTEX_SHADER_SOURCE2:&[u8] = b"
|
||||
#version 450
|
||||
|
||||
layout(binding = 0) uniform UniformBufferObject {
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
} ubo;
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
||||
fragColor = inColor;
|
||||
}
|
||||
\0";
|
||||
|
||||
const VERTEX_SHADER_SOURCE:&[u8] = b"
|
||||
#version 100
|
||||
|
@ -430,8 +284,20 @@ attribute vec3 color;
|
|||
varying vec3 v_color;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
v_color = color;
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
v_color = color;
|
||||
}
|
||||
\0";
|
||||
|
||||
const FRAGMENT_SHADER_SOURCE2:&[u8] = b"
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
}
|
||||
\0";
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@ use cgmath::vec3;
|
|||
|
||||
use cgmath::Deg;
|
||||
use log::*;
|
||||
use old_winit::dpi::LogicalSize;
|
||||
use old_winit::event::Event;
|
||||
use old_winit::event::WindowEvent;
|
||||
use old_winit::event_loop::EventLoop;
|
||||
use old_winit::window::WindowBuilder;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CStr;
|
||||
|
@ -95,6 +100,73 @@ macro_rules! const_shaders {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn start() -> Result<()> {
|
||||
info!("Initializing event loop and winit window instance.");
|
||||
|
||||
// Window
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("WHAT")
|
||||
.with_inner_size(LogicalSize::new(1024, 768))
|
||||
.build(&event_loop)?;
|
||||
|
||||
info!("Creating app and starting event loop.");
|
||||
|
||||
// App
|
||||
|
||||
let mut app = unsafe { App::create(&window)? };
|
||||
let mut minimized = false;
|
||||
|
||||
//let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
//let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
|
||||
//if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
// if receiver.try_recv().is_ok() {
|
||||
// info!("Closing event loop and destroying Vulkan instance.");
|
||||
// elwt.exit();
|
||||
// unsafe {
|
||||
// app.device.device_wait_idle().unwrap();
|
||||
// app.destroy();
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
|
||||
match event {
|
||||
// Request a redraw when all events were processed.
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
// Render a frame if our Vulkan app is not being destroyed.
|
||||
WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
unsafe { app.render(&window) }.unwrap();
|
||||
},
|
||||
WindowEvent::Resized(size) =>
|
||||
if size.width == 0 || size.height == 0 {
|
||||
minimized = true;
|
||||
} else {
|
||||
minimized = false;
|
||||
app.resized = true;
|
||||
},
|
||||
// Destroy our Vulkan app.
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Our Vulkan app.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct App {
|
||||
|
|
1471
src/graphics_engines/vulkan_gc.rs
Normal file
1471
src/graphics_engines/vulkan_gc.rs
Normal file
File diff suppressed because it is too large
Load diff
114
src/linux.rs
114
src/linux.rs
|
@ -1,114 +0,0 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use log::*;
|
||||
|
||||
use vulkanalia::prelude::v1_0::*;
|
||||
|
||||
use crate::graphics_engines;
|
||||
use crate::graphics_engines::vulkan::App;
|
||||
|
||||
const WINDOW_TITLE:&'static str = "MineMod";
|
||||
const MODE:GMode = GMode::Vulkan;
|
||||
|
||||
pub enum GMode {
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
}
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
super::init_logging();
|
||||
|
||||
info!("Registering CTRLC hook.");
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
shutdown_tx.send(()).expect("Failed to send shutdown signal");
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
info!("Initializing event loop and winit window instance.");
|
||||
|
||||
// Window
|
||||
|
||||
match MODE {
|
||||
GMode::Vulkan => {
|
||||
use old_winit::dpi::LogicalSize;
|
||||
use old_winit::event::Event;
|
||||
use old_winit::event::WindowEvent;
|
||||
use old_winit::event_loop::EventLoop;
|
||||
use old_winit::window::WindowBuilder;
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(WINDOW_TITLE)
|
||||
.with_inner_size(LogicalSize::new(1024, 768))
|
||||
.build(&event_loop)?;
|
||||
|
||||
info!("Creating app and starting event loop.");
|
||||
|
||||
// App
|
||||
|
||||
let mut app = unsafe { App::create(&window)? };
|
||||
let mut minimized = false;
|
||||
|
||||
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
|
||||
if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
if receiver.try_recv().is_ok() {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match event {
|
||||
// Request a redraw when all events were processed.
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
// Render a frame if our Vulkan app is not being destroyed.
|
||||
WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
unsafe { app.render(&window) }.unwrap();
|
||||
},
|
||||
WindowEvent::Resized(size) =>
|
||||
if size.width == 0 || size.height == 0 {
|
||||
minimized = true;
|
||||
} else {
|
||||
minimized = false;
|
||||
app.resized = true;
|
||||
},
|
||||
// Destroy our Vulkan app.
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
})?;
|
||||
},
|
||||
GMode::OpenGL => {
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
if let Err(err) = graphics_engines::opengl::main(EventLoop::new().unwrap()) {
|
||||
error!("An error occured in the opengl backend: {err}");
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
info!("Exiting program.");
|
||||
|
||||
Ok(())
|
||||
}
|
96
src/mac.rs
96
src/mac.rs
|
@ -1,96 +0,0 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use log::*;
|
||||
|
||||
use vulkanalia::prelude::v1_0::*;
|
||||
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::Event;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
use crate::graphics_engines::vulkan::App;
|
||||
|
||||
const WINDOW_TITLE:&'static str = "MineMod";
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
super::init_logging();
|
||||
|
||||
info!("Registering CTRLC hook.");
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
shutdown_tx.send(()).expect("Failed to send shutdown signal");
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
info!("Initializing event loop and winit window instance.");
|
||||
|
||||
// Window
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(WINDOW_TITLE)
|
||||
.with_inner_size(LogicalSize::new(1024, 768))
|
||||
.build(&event_loop)?;
|
||||
|
||||
info!("Creating app and starting event loop.");
|
||||
|
||||
// App
|
||||
|
||||
let mut app = unsafe { App::create(&window)? };
|
||||
let mut minimized = false;
|
||||
|
||||
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
|
||||
if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
if receiver.try_recv().is_ok() {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match event {
|
||||
// Request a redraw when all events were processed.
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
// Render a frame if our Vulkan app is not being destroyed.
|
||||
WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
unsafe { app.render(&window) }.unwrap();
|
||||
},
|
||||
WindowEvent::Resized(size) =>
|
||||
if size.width == 0 || size.height == 0 {
|
||||
minimized = true;
|
||||
} else {
|
||||
minimized = false;
|
||||
app.resized = true;
|
||||
},
|
||||
// Destroy our Vulkan app.
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
})?;
|
||||
|
||||
info!("Exiting program.");
|
||||
|
||||
Ok(())
|
||||
}
|
80
src/main.rs
80
src/main.rs
|
@ -8,8 +8,33 @@
|
|||
unsafe_op_in_unsafe_fn
|
||||
)]
|
||||
|
||||
mod audio_engines;
|
||||
mod cataclysm;
|
||||
mod graphics_engines;
|
||||
mod input_systems;
|
||||
mod targets;
|
||||
|
||||
/// i hope you will forgive me for these crimes, but as far as i can tell this is the best way
|
||||
/// to handle all these platforms with wildly different requirements and capabilities
|
||||
pub use targets::*;
|
||||
|
||||
// Logging is currently common across all platforms so it may live here
|
||||
fn init_logging() {
|
||||
tracing_subscriber::fmt()
|
||||
.compact()
|
||||
.with_timer(tracing_subscriber::fmt::time::uptime())
|
||||
.with_ansi(true)
|
||||
.with_level(true)
|
||||
.with_thread_names(true)
|
||||
.with_max_level(if cfg!(debug_assertions) {
|
||||
tracing::level_filters::LevelFilter::DEBUG
|
||||
} else {
|
||||
tracing::level_filters::LevelFilter::INFO
|
||||
})
|
||||
.init();
|
||||
|
||||
// pretty_env_logger::init_timed();
|
||||
}
|
||||
|
||||
// NOTE use crate:: for things in engine that depend on the target platform
|
||||
// this allows an incredibly easy way to handle gui and others per platform
|
||||
|
@ -39,57 +64,4 @@ mod graphics_engines;
|
|||
|
||||
// research why texture atlases exist, it seems like an easy way to reduce the allocation calls since they are limited, and it seems to provide better usage of memory overall
|
||||
|
||||
// Planned system targets
|
||||
#[cfg(target_os = "linux")] mod linux;
|
||||
#[cfg(target_os = "linux")] pub use linux::*;
|
||||
#[cfg(target_os = "windows")] mod windows;
|
||||
#[cfg(target_os = "windows")] pub use windows::*;
|
||||
#[cfg(target_os = "macos")] mod mac;
|
||||
#[cfg(target_os = "macos")] pub use mac::*;
|
||||
#[cfg(target_family = "wasm")] mod wasm;
|
||||
#[cfg(target_family = "wasm")] pub use wasm::*;
|
||||
#[cfg(target_os = "vita")] mod vita;
|
||||
#[cfg(target_os = "vita")] pub use vita::*;
|
||||
|
||||
// Potential system targets
|
||||
|
||||
// bindgen, cc, https://github.com/rust-console
|
||||
|
||||
// switch // c lib: libnx, crate: rust-switch, toolchain: devkitPro
|
||||
// #[cfg(target_os = "horizon")]
|
||||
// mod switch;
|
||||
// #[cfg(target_os = "horizon")]
|
||||
// pub use switch::*;
|
||||
// ps3 // sdk: PSL1GHT, target: powerpc64-unknown-linux-gnu
|
||||
// #[cfg(target_os = "psl1ght")]
|
||||
// mod ps3;
|
||||
// #[cfg(target_os = "psl1ght")]
|
||||
// pub use ps3::*;
|
||||
// x360 // c lib: libxenon, make custom spec for xenon target
|
||||
// #[cfg(target_os = "xenon")]
|
||||
// mod x369;
|
||||
// #[cfg(target_os = "xenon")]
|
||||
// pub use x360::*;
|
||||
// wii // toolchain: devkitPPC, target: powerpc-unknown-eabi, c lib: libogc
|
||||
// #[cfg(target_os = "wii")]
|
||||
// mod wii;
|
||||
// #[cfg(target_os = "wii")]
|
||||
// pub use wii::*;
|
||||
|
||||
fn init_logging() {
|
||||
tracing_subscriber::fmt()
|
||||
.compact()
|
||||
.with_timer(tracing_subscriber::fmt::time::uptime())
|
||||
.with_ansi(true)
|
||||
.with_level(true)
|
||||
// .with_thread_ids(true)
|
||||
.with_thread_names(true)
|
||||
.with_max_level(if cfg!(debug_assertions) {
|
||||
tracing::level_filters::LevelFilter::DEBUG
|
||||
} else {
|
||||
tracing::level_filters::LevelFilter::INFO
|
||||
})
|
||||
.init();
|
||||
|
||||
// pretty_env_logger::init_timed();
|
||||
}
|
||||
// multirender system where both vulkan and opengl are active at once, each rendering half of the screen, can use to compare the two systems while working on them
|
||||
|
|
13
src/targets/desktop/linux.rs
Normal file
13
src/targets/desktop/linux.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
//use anyhow::Result;
|
||||
|
||||
//use log::*;
|
||||
//use winit::event_loop::EventLoop;
|
||||
|
||||
//pub fn main() -> Result<()> {
|
||||
// crate::init_logging();
|
||||
|
||||
|
||||
|
||||
|
||||
// Ok(())
|
||||
//}
|
0
src/targets/desktop/mac/metal_bindgen/metal.rs
Normal file
0
src/targets/desktop/mac/metal_bindgen/metal.rs
Normal file
96
src/targets/desktop/mac/mod.rs
Normal file
96
src/targets/desktop/mac/mod.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
//use anyhow::Result;
|
||||
|
||||
//use log::*;
|
||||
|
||||
//use vulkanalia::prelude::v1_0::*;
|
||||
|
||||
//use winit::dpi::LogicalSize;
|
||||
//use winit::event::Event;
|
||||
//use winit::event::WindowEvent;
|
||||
//use winit::event_loop::EventLoop;
|
||||
//use winit::window::WindowBuilder;
|
||||
|
||||
//use crate::graphics_engines::vulkan::App;
|
||||
|
||||
//const WINDOW_TITLE:&'static str = "MineMod";
|
||||
|
||||
//pub fn main() -> Result<()> {
|
||||
// super::init_logging();
|
||||
|
||||
// info!("Registering CTRLC hook.");
|
||||
|
||||
// let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
||||
|
||||
// ctrlc::set_handler(move || {
|
||||
// shutdown_tx.send(()).expect("Failed to send shutdown signal");
|
||||
// })
|
||||
// .expect("Error setting Ctrl-C handler");
|
||||
|
||||
// info!("Initializing event loop and winit window instance.");
|
||||
|
||||
// // Window
|
||||
|
||||
// let event_loop = EventLoop::new()?;
|
||||
// let window = WindowBuilder::new()
|
||||
// .with_title(WINDOW_TITLE)
|
||||
// .with_inner_size(LogicalSize::new(1024, 768))
|
||||
// .build(&event_loop)?;
|
||||
|
||||
// info!("Creating app and starting event loop.");
|
||||
|
||||
// // App
|
||||
|
||||
// let mut app = unsafe { App::create(&window)? };
|
||||
// let mut minimized = false;
|
||||
|
||||
// let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
|
||||
// event_loop.run(move |event, elwt| {
|
||||
// let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
|
||||
// if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
// if receiver.try_recv().is_ok() {
|
||||
// info!("Closing event loop and destroying Vulkan instance.");
|
||||
// elwt.exit();
|
||||
// unsafe {
|
||||
// app.device.device_wait_idle().unwrap();
|
||||
// app.destroy();
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// match event {
|
||||
// // Request a redraw when all events were processed.
|
||||
// Event::AboutToWait => window.request_redraw(),
|
||||
// Event::WindowEvent { event, .. } => match event {
|
||||
// // Render a frame if our Vulkan app is not being destroyed.
|
||||
// WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
// unsafe { app.render(&window) }.unwrap();
|
||||
// },
|
||||
// WindowEvent::Resized(size) =>
|
||||
// if size.width == 0 || size.height == 0 {
|
||||
// minimized = true;
|
||||
// } else {
|
||||
// minimized = false;
|
||||
// app.resized = true;
|
||||
// },
|
||||
// // Destroy our Vulkan app.
|
||||
// WindowEvent::CloseRequested => {
|
||||
// info!("Closing event loop and destroying Vulkan instance.");
|
||||
// elwt.exit();
|
||||
// unsafe {
|
||||
// app.device.device_wait_idle().unwrap();
|
||||
// app.destroy();
|
||||
// }
|
||||
// },
|
||||
// _ => {},
|
||||
// },
|
||||
// _ => {},
|
||||
// }
|
||||
// })?;
|
||||
|
||||
// info!("Exiting program.");
|
||||
|
||||
// Ok(())
|
||||
//}
|
288
src/targets/desktop/mod.rs
Normal file
288
src/targets/desktop/mod.rs
Normal file
|
@ -0,0 +1,288 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use cgmath::vec2;
|
||||
use cgmath::vec3;
|
||||
|
||||
use glfw::Action;
|
||||
use glfw::Context;
|
||||
use glfw::Key;
|
||||
|
||||
use log::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::cataclysm::Cataclysm;
|
||||
use crate::cataclysm::CataclysmChunk;
|
||||
use crate::cataclysm::SimpleBlock;
|
||||
|
||||
use crate::graphics_engines::GMode;
|
||||
use crate::graphics_engines::GraphicsCommander;
|
||||
use crate::graphics_engines::Vertex;
|
||||
use crate::graphics_engines::VertexData;
|
||||
|
||||
//#[cfg(target_os = "linux")] mod linux;
|
||||
//#[cfg(target_os = "linux")] pub use linux::*;
|
||||
//#[cfg(target_os = "windows")] mod windows;
|
||||
//#[cfg(target_os = "windows")] pub use windows::*;
|
||||
//#[cfg(target_os = "macos")] mod mac;
|
||||
//#[cfg(target_os = "macos")] pub use mac::*;
|
||||
|
||||
pub const WINDOW_TITLE:&'static str = "MineMod";
|
||||
pub const WINDOW_DECORATION_LEFT:bool = false;
|
||||
|
||||
pub fn shutdown_handler(sender:std::sync::mpsc::Sender<()>) {
|
||||
static SHUTDOWN_COUNT:std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(1);
|
||||
if SHUTDOWN_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst) >= 3 {
|
||||
warn!("3 exit events have piled up. Forcing exit.");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
sender.send(()).expect("Failed to send shutdown signal");
|
||||
}
|
||||
|
||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
crate::init_logging();
|
||||
|
||||
info!("Registering ^C hook.");
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
||||
|
||||
{
|
||||
let tx = shutdown_tx.clone();
|
||||
ctrlc::set_handler(move || shutdown_handler(tx.clone())).expect("Error setting Ctrl-C handler");
|
||||
}
|
||||
//std::thread::sleep(std::time::Duration::from_secs(10));
|
||||
|
||||
let mut glfw = glfw::init(|err, string| match err {
|
||||
// we dont give a shit because we supply our own render system
|
||||
glfw::Error::NoWindowContext => warn!("GLFW Error: {err} {string}"),
|
||||
_ => panic!("GLFW Error! {err}, {string}"),
|
||||
})?;
|
||||
|
||||
let mut graphics_mode = GMode::OpenGL;
|
||||
|
||||
// if we do not want to use opengl, disable it
|
||||
if graphics_mode != GMode::OpenGL {
|
||||
glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi));
|
||||
}
|
||||
|
||||
let (mut window, events) = if let Some(glfw) = glfw.create_window(1024, 768, &WINDOW_TITLE, glfw::WindowMode::Windowed) {
|
||||
glfw
|
||||
} else {
|
||||
return Err("Could not create glfw window!".into());
|
||||
};
|
||||
|
||||
window.set_key_polling(true);
|
||||
window.make_current();
|
||||
window.set_close_callback(move |_| shutdown_handler(shutdown_tx.clone()));
|
||||
|
||||
let mut graphics_commander = make_graphics_commander(graphics_mode, &mut window)?;
|
||||
|
||||
let mut opengl_cache = None;
|
||||
|
||||
// Register models
|
||||
|
||||
let verts:Box<[Vertex]> = Box::new([
|
||||
Vertex::new(vec2(-0.5, -0.5), vec3(0.3, 0.0, 1.0)),
|
||||
Vertex::new(vec2(0.5, -0.5), vec3(0.3, 0.0, 1.0)),
|
||||
Vertex::new(vec2(0.5, 0.5), vec3(0.3, 0.0, 1.0)),
|
||||
Vertex::new(vec2(-0.5, 0.5), vec3(0.3, 0.0, 1.0)),
|
||||
]);
|
||||
|
||||
graphics_commander.create_vxos(&[VertexData { verts, ..Default::default() }]);
|
||||
|
||||
// create game world
|
||||
|
||||
let mut game_world = Cataclysm::new();
|
||||
|
||||
//let mut inners = vec![];
|
||||
//let mut mids = vec![];
|
||||
//let mut outers = vec![];
|
||||
|
||||
let mut threads = vec![];
|
||||
let (sender, recv) = std::sync::mpsc::channel();
|
||||
let instant = std::time::Instant::now();
|
||||
//let mut count = 0;
|
||||
const RANGE:std::ops::Range<u32> = 0..1;
|
||||
for x in RANGE {
|
||||
'ml: loop {
|
||||
if threads.len() >= 4 {
|
||||
//info!("MAX");
|
||||
recv.recv().unwrap();
|
||||
break 'ml;
|
||||
} else {
|
||||
break 'ml;
|
||||
}
|
||||
}
|
||||
let sender = sender.clone();
|
||||
threads.push(std::thread::spawn(move || {
|
||||
info!("Spawned");
|
||||
let mut chunks:HashMap<(u32, u32, u32), CataclysmChunk> = HashMap::new();
|
||||
|
||||
let instant = std::time::Instant::now();
|
||||
for y in RANGE {
|
||||
//let instant = std::time::Instant::now();
|
||||
for z in RANGE {
|
||||
//let instant = std::time::Instant::now();
|
||||
chunks.insert((x, y, z), CataclysmChunk::new());
|
||||
for bx in 0..Cataclysm::CHUNK_SIZE {
|
||||
for by in 0..Cataclysm::CHUNK_SIZE {
|
||||
for bz in 0..Cataclysm::CHUNK_SIZE {
|
||||
chunks.get_mut(&(x, y, z)).unwrap().simple_blocks.insert((bx, by, bz), SimpleBlock {});
|
||||
//count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//inners.push(std::time::Instant::now() - instant);
|
||||
}
|
||||
//mids.push(std::time::Instant::now() - instant);
|
||||
}
|
||||
//outers.push(std::time::Instant::now() - instant);
|
||||
|
||||
sender.send(()).unwrap();
|
||||
chunks
|
||||
}));
|
||||
}
|
||||
//dbg!(count);
|
||||
|
||||
let mut master_chunkmap:HashMap<(u32, u32, u32), CataclysmChunk> = HashMap::new();
|
||||
for t in threads {
|
||||
let chunks = t.join().unwrap();
|
||||
for (_id, (key, value)) in chunks.iter().enumerate() {
|
||||
if let Some(val) = master_chunkmap.insert(*key, value.clone()) {
|
||||
warn!("Existing value present! {:?}", val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
game_world.chunks = master_chunkmap;
|
||||
|
||||
//{
|
||||
// let mut avg = 0.0;
|
||||
// for i in &inners {
|
||||
// avg += i.as_secs_f32();
|
||||
// }
|
||||
// avg = avg / inners.len() as f32;
|
||||
// info!("inners:{avg}");
|
||||
// avg = 0.0;
|
||||
// for i in &mids {
|
||||
// avg += i.as_secs_f32();
|
||||
// }
|
||||
// avg = avg / mids.len() as f32;
|
||||
// info!("mids:{avg}");
|
||||
// avg = 0.0;
|
||||
// for i in &outers {
|
||||
// avg += i.as_secs_f32();
|
||||
// }
|
||||
// avg = avg / outers.len() as f32;
|
||||
// info!("outers:{avg}");
|
||||
//}
|
||||
info!("MASTER: {}", (std::time::Instant::now() - instant).as_secs_f32());
|
||||
|
||||
//return Ok(());
|
||||
|
||||
let mut graphics_commander = Some(graphics_commander);
|
||||
|
||||
//'mainloop: while !window.should_close() {
|
||||
loop {
|
||||
if shutdown_rx.try_recv().is_ok() {
|
||||
info!("^C triggered.");
|
||||
window.set_should_close(true);
|
||||
}
|
||||
|
||||
// dont flood the logs
|
||||
//std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
|
||||
trace!("Polling GLFW events.");
|
||||
// Poll for and process events
|
||||
glfw.poll_events();
|
||||
|
||||
for (e, event) in glfw::flush_messages(&events) {
|
||||
info!("GLFW Event: {:?}", event);
|
||||
match event {
|
||||
//glfw::WindowEvent::FramebufferSize(x, y) => {},
|
||||
// minimize? doesnt appear to be
|
||||
glfw::WindowEvent::Iconify(e) =>
|
||||
if let Some(graphics_commander) = graphics_commander.as_mut() {
|
||||
if e {
|
||||
graphics_commander.suspend_rendering();
|
||||
} else {
|
||||
graphics_commander.resume_rendering();
|
||||
}
|
||||
},
|
||||
glfw::WindowEvent::Key(key, _, Action::Press, _) => match key {
|
||||
Key::Escape | Key::Q => window.set_should_close(true),
|
||||
Key::E => {
|
||||
match graphics_mode {
|
||||
GMode::Vulkan => {
|
||||
info!("v1 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
|
||||
|
||||
if let Some(mut graphics_commander) = graphics_commander.take() {
|
||||
graphics_commander.exit();
|
||||
}
|
||||
info!("v2 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
|
||||
graphics_mode = GMode::OpenGL;
|
||||
graphics_commander = opengl_cache.take();
|
||||
if graphics_commander.is_none() {
|
||||
error!("OpenGL was expected but not present");
|
||||
graphics_commander = Some(make_graphics_commander(graphics_mode, &mut window)?);
|
||||
}
|
||||
info!("v3 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
|
||||
},
|
||||
GMode::OpenGL => {
|
||||
info!("o1 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
|
||||
//graphics_commander.clean();
|
||||
opengl_cache = graphics_commander.take();
|
||||
info!("o2 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
|
||||
|
||||
graphics_mode = GMode::Vulkan;
|
||||
graphics_commander = Some(make_graphics_commander(graphics_mode, &mut window)?);
|
||||
info!("o3 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
|
||||
},
|
||||
#[cfg(target_os = "macos")]
|
||||
GMode::Metal => todo!(),
|
||||
#[cfg(target_os = "windows")]
|
||||
GMode::DirectX => todo!(),
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if window.should_close() {
|
||||
info!("Breaking loop.");
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(graphics_commander) = graphics_commander.as_mut() {
|
||||
trace!("Rendering.");
|
||||
//cmd.render_simple_blocks(game_world.chunks.get_mut(&(0, 0, 0)).unwrap().simple_blocks);
|
||||
graphics_commander.render(&mut window, &mut game_world)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(graphics_commander) = graphics_commander.as_mut() {
|
||||
info!("Closing GC instance.");
|
||||
graphics_commander.exit();
|
||||
}
|
||||
|
||||
info!("Exiting.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_graphics_commander(graphics_mode:GMode, window:&mut glfw::PWindow) -> Result<Box<dyn GraphicsCommander>> {
|
||||
Ok(match graphics_mode {
|
||||
GMode::Vulkan => Box::new(unsafe { crate::graphics_engines::vulkan_gc::App::create(&window)? }),
|
||||
GMode::OpenGL => Box::new(crate::graphics_engines::opengl::CataclysmOGL::create(window)?),
|
||||
#[cfg(target_os = "macos")]
|
||||
GMode::Metal => todo!(),
|
||||
#[cfg(target_os = "windows")]
|
||||
GMode::DirectX => todo!(),
|
||||
})
|
||||
}
|
||||
|
||||
//fn make_glfw() -> Result<glfw::Glfw, Box<dyn std::error::Error>> {
|
||||
|
||||
// Ok(glfw)
|
||||
//}
|
96
src/targets/desktop/windows.rs
Normal file
96
src/targets/desktop/windows.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
//use anyhow::Result;
|
||||
|
||||
//use log::*;
|
||||
|
||||
//use vulkanalia::prelude::v1_0::*;
|
||||
|
||||
//use winit::dpi::LogicalSize;
|
||||
//use winit::event::Event;
|
||||
//use winit::event::WindowEvent;
|
||||
//use winit::event_loop::EventLoop;
|
||||
//use winit::window::WindowBuilder;
|
||||
|
||||
//use crate::graphics_engines::vulkan::App;
|
||||
|
||||
//const WINDOW_TITLE:&'static str = "MineMod";
|
||||
|
||||
//pub fn main() -> Result<()> {
|
||||
// super::init_logging();
|
||||
|
||||
// info!("Registering CTRLC hook.");
|
||||
|
||||
// let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel::<()>();
|
||||
|
||||
// ctrlc::set_handler(move || {
|
||||
// shutdown_tx.send(()).expect("Failed to send shutdown signal");
|
||||
// })
|
||||
// .expect("Error setting Ctrl-C handler");
|
||||
|
||||
// info!("Initializing event loop and winit window instance.");
|
||||
|
||||
// // Window
|
||||
|
||||
// let event_loop = EventLoop::new()?;
|
||||
// let window = WindowBuilder::new()
|
||||
// .with_title(WINDOW_TITLE)
|
||||
// .with_inner_size(LogicalSize::new(1024, 768))
|
||||
// .build(&event_loop)?;
|
||||
|
||||
// info!("Creating app and starting event loop.");
|
||||
|
||||
// // App
|
||||
|
||||
// let mut app = unsafe { App::create(&window)? };
|
||||
// let mut minimized = false;
|
||||
|
||||
// let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
|
||||
// event_loop.run(move |event, elwt| {
|
||||
// let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
|
||||
// if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
// if receiver.try_recv().is_ok() {
|
||||
// info!("Closing event loop and destroying Vulkan instance.");
|
||||
// elwt.exit();
|
||||
// unsafe {
|
||||
// app.device.device_wait_idle().unwrap();
|
||||
// app.destroy();
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// match event {
|
||||
// // Request a redraw when all events were processed.
|
||||
// Event::AboutToWait => window.request_redraw(),
|
||||
// Event::WindowEvent { event, .. } => match event {
|
||||
// // Render a frame if our Vulkan app is not being destroyed.
|
||||
// WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
// unsafe { app.render(&window) }.unwrap();
|
||||
// },
|
||||
// WindowEvent::Resized(size) =>
|
||||
// if size.width == 0 || size.height == 0 {
|
||||
// minimized = true;
|
||||
// } else {
|
||||
// minimized = false;
|
||||
// app.resized = true;
|
||||
// },
|
||||
// // Destroy our Vulkan app.
|
||||
// WindowEvent::CloseRequested => {
|
||||
// info!("Closing event loop and destroying Vulkan instance.");
|
||||
// elwt.exit();
|
||||
// unsafe {
|
||||
// app.device.device_wait_idle().unwrap();
|
||||
// app.destroy();
|
||||
// }
|
||||
// },
|
||||
// _ => {},
|
||||
// },
|
||||
// _ => {},
|
||||
// }
|
||||
// })?;
|
||||
|
||||
// info!("Exiting program.");
|
||||
|
||||
// Ok(())
|
||||
//}
|
37
src/targets/mod.rs
Normal file
37
src/targets/mod.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Planned system targets
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||
mod desktop;
|
||||
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||
pub use desktop::*;
|
||||
|
||||
// wasm may count for desktop? it does not need windowing however
|
||||
//#[cfg(target_family = "wasm")] mod wasm;
|
||||
//#[cfg(target_family = "wasm")] pub use wasm::*;
|
||||
//#[cfg(target_os = "vita")] mod vita;
|
||||
//#[cfg(target_os = "vita")] pub use vita::*;
|
||||
|
||||
// Potential system targets
|
||||
|
||||
// bindgen, cc, https://github.com/rust-console
|
||||
|
||||
// switch // c lib: libnx, crate: rust-switch, toolchain: devkitPro
|
||||
// #[cfg(target_os = "horizon")]
|
||||
// mod switch;
|
||||
// #[cfg(target_os = "horizon")]
|
||||
// pub use switch::*;
|
||||
// ps3 // sdk: PSL1GHT, target: powerpc64-unknown-linux-gnu
|
||||
// #[cfg(target_os = "psl1ght")]
|
||||
// mod ps3;
|
||||
// #[cfg(target_os = "psl1ght")]
|
||||
// pub use ps3::*;
|
||||
// x360 // c lib: libxenon, make custom spec for xenon target
|
||||
// #[cfg(target_os = "xenon")]
|
||||
// mod x369;
|
||||
// #[cfg(target_os = "xenon")]
|
||||
// pub use x360::*;
|
||||
// wii // toolchain: devkitPPC, target: powerpc-unknown-eabi, c lib: libogc
|
||||
// #[cfg(target_os = "wii")]
|
||||
// mod wii;
|
||||
// #[cfg(target_os = "wii")]
|
||||
// pub use wii::*;
|
114
src/windows.rs
114
src/windows.rs
|
@ -1,114 +0,0 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use log::*;
|
||||
|
||||
use vulkanalia::prelude::v1_0::*;
|
||||
|
||||
use crate::graphics_engines;
|
||||
use crate::graphics_engines::vulkan::App;
|
||||
|
||||
const WINDOW_TITLE:&'static str = "MineMod";
|
||||
const MODE:GMode = GMode::Vulkan;
|
||||
|
||||
pub enum GMode {
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
}
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
super::init_logging();
|
||||
|
||||
info!("Registering CTRLC hook.");
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
shutdown_tx.send(()).expect("Failed to send shutdown signal");
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
info!("Initializing event loop and winit window instance.");
|
||||
|
||||
// Window
|
||||
|
||||
match MODE {
|
||||
GMode::Vulkan => {
|
||||
use old_winit::dpi::LogicalSize;
|
||||
use old_winit::event::Event;
|
||||
use old_winit::event::WindowEvent;
|
||||
use old_winit::event_loop::EventLoop;
|
||||
use old_winit::window::WindowBuilder;
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(WINDOW_TITLE)
|
||||
.with_inner_size(LogicalSize::new(1024, 768))
|
||||
.build(&event_loop)?;
|
||||
|
||||
info!("Creating app and starting event loop.");
|
||||
|
||||
// App
|
||||
|
||||
let mut app = unsafe { App::create(&window)? };
|
||||
let mut minimized = false;
|
||||
|
||||
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
|
||||
if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
if receiver.try_recv().is_ok() {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match event {
|
||||
// Request a redraw when all events were processed.
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
// Render a frame if our Vulkan app is not being destroyed.
|
||||
WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
unsafe { app.render(&window) }.unwrap();
|
||||
},
|
||||
WindowEvent::Resized(size) =>
|
||||
if size.width == 0 || size.height == 0 {
|
||||
minimized = true;
|
||||
} else {
|
||||
minimized = false;
|
||||
app.resized = true;
|
||||
},
|
||||
// Destroy our Vulkan app.
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
})?;
|
||||
},
|
||||
GMode::OpenGL => {
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
if let Err(err) = graphics_engines::opengl::main(EventLoop::new().unwrap()) {
|
||||
error!("An error occured in the opengl backend: {err}");
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
info!("Exiting program.");
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Add table
Reference in a new issue