Merge branch 'opengl-dev' into jelly-dev
This commit is contained in:
commit
7ac84da35a
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