Merge branch 'opengl-dev' into jelly-dev

This commit is contained in:
deepCurse 2025-02-08 09:52:14 -04:00
commit 7ac84da35a
Signed by: u1
GPG key ID: AD770D25A908AFF4
31 changed files with 3125 additions and 1221 deletions

876
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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
View 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!();
}

View file

@ -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,
}

View 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!() }
}

View file

@ -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,
}

View file

@ -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";

View file

@ -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 {

File diff suppressed because it is too large Load diff

View file

@ -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(())
}

View file

@ -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(())
}

View file

@ -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

View file

@ -0,0 +1,13 @@
//use anyhow::Result;
//use log::*;
//use winit::event_loop::EventLoop;
//pub fn main() -> Result<()> {
// crate::init_logging();
// Ok(())
//}

View 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
View 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)
//}

View 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
View 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::*;

View file

@ -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(())
}