tirbofish/dropbear · commit
4c0b4a9606ee6e42f556047a1fef33cbe33233cf
publish: preparing to publish on crates.io
Signature present but could not be verified.
Unverified
@@ -1,6 +1,6 @@ [workspace] resolver = "3" -members = ["dropbear-engine", "eucalyptus"] +members = ["dropbear-engine", "eucalyptus", "redback"] [profile.dev] opt-level = 0 @@ -0,0 +1,10 @@ +# dropbear + +dropbear is a game engine used to create games. It's made in rust. + +If you might have nbot realised, + +## Related projects + + - [eucalyptus](https://github.com/4tkbytes/dropbear/eucalyptus) is the visual editor used to create games visually, taking inspiration from Unity and other engines. + - [redback](https://github.com/4tkbytes/dropbear/redback) is the build system used by [eucalyptus](https://github.com/4tkbytes/dropbear/eucalyptus) to bind, build and ship games made with the engine. @@ -1,6 +1,6 @@ [package] name = "dropbear-engine" -version = "0.1.0" +version = "0.1.2" edition = "2024" license = "GPL-3.0-or-later" description = "A game engine made by 4tkbytes. Thats really it..." @@ -14,8 +14,6 @@ env_logger = "0.11" log = "0.4" wgpu = "25" pollster = "0.4" -# Never block on with async, use tokio instead... -# Note to self: use pollster only for state blocking, any resource loading should not be blocked. tokio = { version = "1", features = ["full"] } futures = "0.3" async-trait = "0.1" @@ -35,15 +33,3 @@ hecs = "0.10" version = "0.25" default-features = false features = ["png", "jpeg"] - -[build-dependencies] -anyhow = "1.0" -fs_extra = "1.3" -glob = "0.3" - -[profile.dev] -opt-level = 0 -debug = true -codegen-units = 16 -incremental = true -lto = false @@ -1,17 +0,0 @@ -use anyhow::Result; -use std::env; - -use fs_extra::{copy_items, dir::CopyOptions}; - -fn main() -> Result<()> { - println!("cargo:rerun-if-changed=resources/**/*"); - - let out_dir = env::var("OUT_DIR")?; - let mut copy_options = CopyOptions::new(); - copy_options.overwrite = true; - let mut paths_to_copy = Vec::new(); - paths_to_copy.push("resources/"); - copy_items(&paths_to_copy, out_dir, ©_options)?; - - Ok(()) -} Binary files a/dropbear-engine/resources/models/low_poly_fox.glb and /dev/null differ Binary files a/dropbear-engine/resources/models/low_poly_horse.glb and /dev/null differ Binary files a/dropbear-engine/resources/models/maxwell_the_cat.glb and /dev/null differ Binary files a/dropbear-engine/resources/models/vpc0sdfhwkoyyz8o.glb and /dev/null differ @@ -1,59 +0,0 @@ -struct CameraUniform { - view_proj: mat4x4<f32>, -}; -@group(1) @binding(0) -var<uniform> camera: CameraUniform; - -struct ModelUniform { - model: mat4x4<f32> -}; -@group(2) @binding(0) -var<uniform> model_uniform: ModelUniform; - -struct InstanceInput { - @location(5) model_matrix_0: vec4<f32>, - @location(6) model_matrix_1: vec4<f32>, - @location(7) model_matrix_2: vec4<f32>, - @location(8) model_matrix_3: vec4<f32>, -}; - -struct VertexInput { - @location(0) position: vec3<f32>, - @location(1) tex_coords: vec2<f32>, -}; - -struct VertexOutput { - @builtin(position) clip_position: vec4<f32>, - @location(0) tex_coords: vec2<f32>, -}; - -@vertex -fn vs_main( - model: VertexInput, - instance: InstanceInput, -) -> VertexOutput { - let model_matrix = mat4x4<f32>( - instance.model_matrix_0, - instance.model_matrix_1, - instance.model_matrix_2, - instance.model_matrix_3, - ); - var out: VertexOutput; - out.tex_coords = model.tex_coords; - out.clip_position = camera.view_proj * model_matrix * vec4<f32>(model.position, 1.0); - return out; -} - -@group(0) @binding(0) -var t_diffuse: texture_2d<f32>; -@group(0) @binding(1) -var s_diffuse: sampler; - -@fragment -fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { - var tex_color = textureSample(t_diffuse, s_diffuse, in.tex_coords); - if (tex_color.a < 0.1) { - discard; - } - return tex_color; -} Binary files a/dropbear-engine/resources/textures/no-texture.png and /dev/null differ @@ -1,7 +1,13 @@ +use std::path::PathBuf; + use nalgebra::{Matrix4, UnitQuaternion, Vector3}; -use wgpu::{util::DeviceExt, Buffer, RenderPass}; +use wgpu::{Buffer, RenderPass, util::DeviceExt}; -use crate::{camera::Camera, graphics::{Graphics, Instance}, model::{DrawModel, Model}}; +use crate::{ + camera::Camera, + graphics::{Graphics, Instance}, + model::{DrawModel, Model}, +}; #[derive(Default)] pub struct AdoptedEntity { @@ -38,7 +44,7 @@ impl Transform { scale: Vector3::new(1.0, 1.0, 1.0), } } - + pub fn matrix(&self) -> Matrix4<f32> { Matrix4::new_translation(&self.position) * self.rotation.to_homogeneous() @@ -67,32 +73,36 @@ impl Transform { } impl AdoptedEntity { - pub fn new(graphics: &Graphics, file_name: &str, label: Option<&str>) -> anyhow::Result<Self> { - let model = Model::load(graphics, file_name)?; + pub fn new(graphics: &Graphics, path: &PathBuf, label: Option<&str>) -> anyhow::Result<Self> { + let model = Model::load(graphics, path)?; Ok(Self::adopt(graphics, model, label)) } pub fn adopt(graphics: &Graphics, model: Model, label: Option<&str>) -> Self { let uniform = ModelUniform::new(); let uniform_buffer = graphics.create_uniform(uniform, Some("Entity Model Uniform")); - - let instance = Instance::new( - Vector3::identity(), - UnitQuaternion::identity() - ); - - let instance_buffer = graphics.state.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: match label { Some(_) => label, None => Some("instance buffer")}, - contents: bytemuck::cast_slice(&[instance.to_raw()]), - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); + + let instance = Instance::new(Vector3::identity(), UnitQuaternion::identity()); + + let instance_buffer = + graphics + .state + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: match label { + Some(_) => label, + None => Some("instance buffer"), + }, + contents: bytemuck::cast_slice(&[instance.to_raw()]), + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + }); log::debug!("Successfully adopted Model"); Self { model: Some(model), uniform, uniform_buffer: Some(uniform_buffer), instance, - instance_buffer: Some(instance_buffer) + instance_buffer: Some(instance_buffer), } } @@ -100,11 +110,10 @@ impl AdoptedEntity { self.uniform.model = transform.matrix().into(); if let Some(buffer) = &self.uniform_buffer { - graphics.state.queue.write_buffer( - buffer, - 0, - bytemuck::cast_slice(&[self.uniform]), - ); + graphics + .state + .queue + .write_buffer(buffer, 0, bytemuck::cast_slice(&[self.uniform])); } self.instance = Instance::from_matrix(transform.matrix()); @@ -113,8 +122,8 @@ impl AdoptedEntity { let instance_raw = self.instance.to_raw(); graphics.state.queue.write_buffer( instance_buffer, - 0, - bytemuck::cast_slice(&[instance_raw]) + 0, + bytemuck::cast_slice(&[instance_raw]), ); } } @@ -1,3 +1,5 @@ +use std::{fs, path::PathBuf}; + use egui::Context; use image::GenericImageView; use nalgebra::{Matrix4, UnitQuaternion, Vector3}; @@ -13,7 +15,6 @@ use wgpu::{ use crate::{ State, model::{self, Vertex}, - resources::load_binary, }; pub struct Graphics<'a> { @@ -23,7 +24,7 @@ pub struct Graphics<'a> { pub screen_size: (f32, f32), } -pub const NO_TEXTURE: &'static [u8] = include_bytes!("../resources/textures/no-texture.png"); +pub const NO_TEXTURE: &'static [u8] = include_bytes!("no-texture.png"); impl<'a> Graphics<'a> { pub fn new(state: &'a State, view: &'a TextureView, encoder: &'a mut CommandEncoder) -> Self { @@ -355,8 +356,8 @@ impl Texture { } } - pub async fn load_texture(graphics: &Graphics<'_>, file_name: &str) -> anyhow::Result<Texture> { - let (_, data) = load_binary(file_name).await?; + pub async fn load_texture(graphics: &Graphics<'_>, path: &PathBuf) -> anyhow::Result<Texture> { + let data = fs::read(path)?; Ok(Self::new(graphics, &data)) } } @@ -348,7 +348,7 @@ impl App { env_logger::init(); - log::debug!("OUT_DIR: {}", std::env!("OUT_DIR")); + // log::debug!("OUT_DIR: {}", std::env!("OUT_DIR")); let event_loop = EventLoop::with_user_event().build()?; log::debug!("Created new event loop"); @@ -1,4 +1,4 @@ -use std::{mem, ops::Range}; +use std::{mem, ops::Range, path::PathBuf}; use russimp::{material::{DataContent, TextureType}, scene::{PostProcess, Scene}, Vector3D}; use wgpu::{util::DeviceExt, BufferAddress, VertexAttribute, VertexBufferLayout}; @@ -63,11 +63,9 @@ pub struct Mesh { } impl Model { - pub fn load(graphics: &Graphics<'_>, file_name: &str) -> anyhow::Result<Model> { + pub fn load(graphics: &Graphics<'_>, path: &PathBuf) -> anyhow::Result<Model> { + let file_name = path.file_name().unwrap().to_str().unwrap(); log::debug!("Loading model [{}]", file_name); - let path = std::path::Path::new(env!("OUT_DIR")) - .join("resources") - .join(file_name); let scene = Scene::from_file( path.to_str().unwrap(), Binary files /dev/null and b/dropbear-engine/src/no-texture.png differ @@ -1,30 +1,30 @@ -use std::path::PathBuf; -use std::sync::LazyLock; +// use std::path::PathBuf; +// use std::sync::LazyLock; -pub static RESOURCES_PATH: LazyLock<PathBuf> = LazyLock::new(|| { - std::path::Path::new(env!("OUT_DIR")).join("resources") -}); +// pub static RESOURCES_PATH: LazyLock<PathBuf> = LazyLock::new(|| { +// std::path::Path::new(env!("OUT_DIR")).join("resources") +// }); -pub async fn load_binary(file_name: &str) -> anyhow::Result<(PathBuf, Vec<u8>)> { - let path = std::path::Path::new(env!("OUT_DIR")) - .join("resources") - .join(file_name); - let data = { - log::info!("Loading binary file from: {:?}", path); - std::fs::read(&path)? - }; +// pub async fn load_binary(file_name: &str) -> anyhow::Result<(PathBuf, Vec<u8>)> { +// let path = std::path::Path::new(env!("OUT_DIR")) +// .join("resources") +// .join(file_name); +// let data = { +// log::info!("Loading binary file from: {:?}", path); +// std::fs::read(&path)? +// }; - Ok((path, data)) -} +// Ok((path, data)) +// } -pub async fn load_string(file_name: &str) -> anyhow::Result<(PathBuf, String)> { - let path = std::path::Path::new(env!("OUT_DIR")) - .join("resources") - .join(file_name); - let txt = { - log::info!("Loading string file from: {:?}", path); - std::fs::read_to_string(&path)? - }; +// pub async fn load_string(file_name: &str) -> anyhow::Result<(PathBuf, String)> { +// let path = std::path::Path::new(env!("OUT_DIR")) +// .join("resources") +// .join(file_name); +// let txt = { +// log::info!("Loading string file from: {:?}", path); +// std::fs::read_to_string(&path)? +// }; - Ok((path, txt)) -} +// Ok((path, txt)) +// } @@ -1,4 +1,3 @@ -use async_trait::async_trait; use winit::event_loop::ActiveEventLoop; use crate::{graphics::Graphics, input}; @@ -0,0 +1,59 @@ +struct CameraUniform { + view_proj: mat4x4<f32>, +}; +@group(1) @binding(0) +var<uniform> camera: CameraUniform; + +struct ModelUniform { + model: mat4x4<f32> +}; +@group(2) @binding(0) +var<uniform> model_uniform: ModelUniform; + +struct InstanceInput { + @location(5) model_matrix_0: vec4<f32>, + @location(6) model_matrix_1: vec4<f32>, + @location(7) model_matrix_2: vec4<f32>, + @location(8) model_matrix_3: vec4<f32>, +}; + +struct VertexInput { + @location(0) position: vec3<f32>, + @location(1) tex_coords: vec2<f32>, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4<f32>, + @location(0) tex_coords: vec2<f32>, +}; + +@vertex +fn vs_main( + model: VertexInput, + instance: InstanceInput, +) -> VertexOutput { + let model_matrix = mat4x4<f32>( + instance.model_matrix_0, + instance.model_matrix_1, + instance.model_matrix_2, + instance.model_matrix_3, + ); + var out: VertexOutput; + out.tex_coords = model.tex_coords; + out.clip_position = camera.view_proj * model_matrix * vec4<f32>(model.position, 1.0); + return out; +} + +@group(0) @binding(0) +var t_diffuse: texture_2d<f32>; +@group(0) @binding(1) +var s_diffuse: sampler; + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { + var tex_color = textureSample(t_diffuse, s_diffuse, in.tex_coords); + if (tex_color.a < 0.1) { + discard; + } + return tex_color; +} @@ -2,29 +2,25 @@ name = "eucalyptus" version = "0.1.0" edition = "2024" +license = "GPL-3.0-or-later" +description = "A visual game editor for the dropbear game engine" +repository = "https://github.com/4tkbytes/dropbear-engine" +readme = "README.md" [dependencies] serde = { version = "1.0.219", features = ["derive"] } -dropbear-engine = { path = "../dropbear-engine" } +# dropbear-engine = { path = "../dropbear-engine" } +dropbear-engine = "0.1.2" rfd = "0.15.4" git2 = "0.20.2" anyhow = "1.0.98" ron = "0.10.1" chrono = "0.4.41" -egui-toast = { git = "https://github.com/creativcoder/egui-toast" } -egui_dock = { git = "https://github.com/Adanos020/egui_dock", features = [ - "serde", -] } +egui-toast-fork = "0.18.0" +egui_dock-fork = { version = "0.17", features = ["serde"] } once_cell = "1.21" pyo3 = { version = "0.25", features = ["auto-initialize"] } [dependencies.rhai] -version = "*" +version = "1.22" features = ["std"] - -[profile.dev] -opt-level = 0 -debug = true -codegen-units = 16 -incremental = true -lto = false @@ -0,0 +1,3 @@ +# eucalyptus + +The game editor for the dropbear game engine. The game engine is powered by [dropbear](https://github.com/4tkbytes/dropbear/dropbear-engine) and uses the [redback](https://github.com/4tkbytes/dropbear/redback) to bind, build and ship your game. @@ -1,7 +1,6 @@ use std::{collections::HashSet, path::PathBuf, sync::Arc}; use dropbear_engine::{ - async_trait::async_trait, camera::Camera, egui, graphics::{Graphics, Shader}, @@ -13,8 +12,8 @@ use dropbear_engine::{ dpi::PhysicalPosition, event_loop::ActiveEventLoop, keyboard::KeyCode, window::Window, }, }; -use egui_dock::{DockArea, DockState, NodeIndex, Style, TabViewer}; -use egui_toast::{ToastOptions, Toasts}; +use egui_dock_fork::{DockArea, DockState, NodeIndex, Style, TabViewer}; +use egui_toast_fork::{ToastOptions, Toasts}; use serde::{Deserialize, Serialize}; use crate::states::PROJECT; @@ -68,7 +67,7 @@ impl Editor { pressed_keys: HashSet::new(), is_cursor_locked: false, window: None, - toasts: egui_toast::Toasts::new() + toasts: egui_toast_fork::Toasts::new() .anchor(egui::Align2::RIGHT_BOTTOM, (-10.0, -10.0)) .direction(egui::Direction::BottomUp), } @@ -100,8 +99,8 @@ impl Editor { Ok(_) => {} Err(e) => { log::error!("Error saving project: {}", e); - self.toasts.add(egui_toast::Toast { - kind: egui_toast::ToastKind::Error, + self.toasts.add(egui_toast_fork::Toast { + kind: egui_toast_fork::ToastKind::Error, text: format!("Error saving project: {}", e).into(), options: ToastOptions::default() .duration_in_seconds(5.0) @@ -111,8 +110,8 @@ impl Editor { } } log::info!("Successfully saved project"); - self.toasts.add(egui_toast::Toast { - kind: egui_toast::ToastKind::Success, + self.toasts.add(egui_toast_fork::Toast { + kind: egui_toast_fork::ToastKind::Success, text: format!("Successfully saved project").into(), options: ToastOptions::default() .duration_in_seconds(5.0) @@ -133,8 +132,8 @@ impl Editor { Ok(_) => {} Err(e) => { log::error!("Error saving project: {}", e); - self.toasts.add(egui_toast::Toast { - kind: egui_toast::ToastKind::Error, + self.toasts.add(egui_toast_fork::Toast { + kind: egui_toast_fork::ToastKind::Error, text: format!("Error saving project: {}", e).into(), options: ToastOptions::default() .duration_in_seconds(5.0) @@ -144,8 +143,8 @@ impl Editor { } } log::info!("Successfully saved project"); - self.toasts.add(egui_toast::Toast { - kind: egui_toast::ToastKind::Success, + self.toasts.add(egui_toast_fork::Toast { + kind: egui_toast_fork::ToastKind::Success, text: format!("Successfully saved project").into(), options: ToastOptions::default() .duration_in_seconds(5.0) @@ -220,7 +219,7 @@ impl Scene for Editor { let shader = Shader::new( graphics, - include_str!("../../dropbear-engine/resources/shaders/shader.wgsl"), + include_str!("shader.wgsl"), Some("viewport_shader"), ); @@ -265,7 +264,7 @@ impl Scene for Editor { self.window.as_mut().unwrap().set_cursor_visible(true); } - self.toasts = egui_toast::Toasts::new() + self.toasts = egui_toast_fork::Toasts::new() .anchor(egui::Align2::RIGHT_BOTTOM, (-10.0, -10.0)) .direction(egui::Direction::BottomUp); } @@ -330,10 +329,10 @@ impl Keyboard for Editor { match self.save_project_config() { Ok(_) => { log::info!("Successfully saved project"); - self.toasts.add(egui_toast::Toast { - kind: egui_toast::ToastKind::Success, + self.toasts.add(egui_toast_fork::Toast { + kind: egui_toast_fork::ToastKind::Success, text: format!("Successfully saved project").into(), - options: egui_toast::ToastOptions::default() + options: egui_toast_fork::ToastOptions::default() .duration_in_seconds(5.0) .show_progress(true), ..Default::default() @@ -341,10 +340,10 @@ impl Keyboard for Editor { } Err(e) => { log::error!("Error saving project: {}", e); - self.toasts.add(egui_toast::Toast { - kind: egui_toast::ToastKind::Error, + self.toasts.add(egui_toast_fork::Toast { + kind: egui_toast_fork::ToastKind::Error, text: format!("Error saving project: {}", e).into(), - options: egui_toast::ToastOptions::default() + options: egui_toast_fork::ToastOptions::default() .duration_in_seconds(5.0) .show_progress(true), ..Default::default() @@ -1,14 +1,13 @@ pub(crate) mod assets; mod editor; mod menu; -mod scene1; +// mod scene1; pub(crate) mod states; use std::{cell::RefCell, rc::Rc}; use dropbear_engine::{WindowConfiguration, scene, tokio}; -use crate::scene1::TestingScene1; #[tokio::main] async fn main() { @@ -19,16 +18,16 @@ async fn main() { }; let _app = dropbear_engine::run_app!(config, |mut scene_manager, mut input_manager| { - let testing_scene = Rc::new(RefCell::new(TestingScene1::new())); let main_menu = Rc::new(RefCell::new(menu::MainMenu::new())); let editor = Rc::new(RefCell::new(editor::Editor::new())); - scene::add_scene_with_input( - &mut scene_manager, - &mut input_manager, - testing_scene, - "testing_scene_1", - ); + // not needed anymore + // scene::add_scene_with_input( + // &mut scene_manager, + // &mut input_manager, + // testing_scene, + // "testing_scene_1", + // ); scene::add_scene_with_input( &mut scene_manager, &mut input_manager, @@ -14,7 +14,7 @@ use dropbear_engine::{ log::{self, debug}, scene::{Scene, SceneCommand}, }; -use egui_toast::{ToastOptions, Toasts}; +use egui_toast_fork::{ToastOptions, Toasts}; use git2::Repository; use crate::states::{PROJECT, ProjectConfig}; @@ -49,7 +49,7 @@ impl MainMenu { pub fn new() -> Self { Self { show_progress: false, - toast: egui_toast::Toasts::new() + toast: egui_toast_fork::Toasts::new() .anchor(egui::Align2::RIGHT_BOTTOM, (-10.0, -10.0)) .direction(egui::Direction::BottomUp), ..Default::default() @@ -270,8 +270,8 @@ impl Scene for MainMenu { SceneCommand::SwitchScene(String::from("editor")); } Err(e) => if e.to_string().contains("missing field") { - self.toast.add(egui_toast::Toast { - kind: egui_toast::ToastKind::Error, + self.toast.add(egui_toast_fork::Toast { + kind: egui_toast_fork::ToastKind::Error, text: format!("Your project version is not up to date with the current project version. To fix this, // TODO: create a way to backup").into(), options: ToastOptions::default() .duration_in_seconds(5.0) @@ -21,6 +21,8 @@ use dropbear_engine::{ winit::{event_loop::ActiveEventLoop, keyboard::KeyCode}, }; +// this file purely exists as reference. do not add to module, and probably will be deleted sooner or later + pub struct TestingScene1 { world: hecs::World, render_pipeline: Option<RenderPipeline>, @@ -0,0 +1,59 @@ +struct CameraUniform { + view_proj: mat4x4<f32>, +}; +@group(1) @binding(0) +var<uniform> camera: CameraUniform; + +struct ModelUniform { + model: mat4x4<f32> +}; +@group(2) @binding(0) +var<uniform> model_uniform: ModelUniform; + +struct InstanceInput { + @location(5) model_matrix_0: vec4<f32>, + @location(6) model_matrix_1: vec4<f32>, + @location(7) model_matrix_2: vec4<f32>, + @location(8) model_matrix_3: vec4<f32>, +}; + +struct VertexInput { + @location(0) position: vec3<f32>, + @location(1) tex_coords: vec2<f32>, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4<f32>, + @location(0) tex_coords: vec2<f32>, +}; + +@vertex +fn vs_main( + model: VertexInput, + instance: InstanceInput, +) -> VertexOutput { + let model_matrix = mat4x4<f32>( + instance.model_matrix_0, + instance.model_matrix_1, + instance.model_matrix_2, + instance.model_matrix_3, + ); + var out: VertexOutput; + out.tex_coords = model.tex_coords; + out.clip_position = camera.view_proj * model_matrix * vec4<f32>(model.position, 1.0); + return out; +} + +@group(0) @binding(0) +var t_diffuse: texture_2d<f32>; +@group(0) @binding(1) +var s_diffuse: sampler; + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { + var tex_color = textureSample(t_diffuse, s_diffuse, in.tex_coords); + if (tex_color.a < 0.1) { + discard; + } + return tex_color; +} @@ -12,7 +12,7 @@ use std::{ use chrono::Utc; use dropbear_engine::log; -use egui_dock::DockState; +use egui_dock_fork::DockState; use once_cell::sync::Lazy; use ron::ser::PrettyConfig; use serde::{Deserialize, Serialize}; @@ -0,0 +1,10 @@ +[package] +name = "redback" +version = "0.1.1" +edition = "2024" +license = "GPL-3.0-or-later" +description = "The build tool used by eucalyptus game engine" +repository = "https://github.com/4tkbytes/dropbear-engine" +readme = "README.md" + +[dependencies] @@ -0,0 +1,12 @@ +# redback + +This is the cli build system (+ library) used by games made with the Eucalyptus game editor. + +It compiles all the assets in a ready to ship package, with all your scripts, resources, shaders compiled +and batteries included. + +The project is named after the Redback Spider, which is a highly venomous spider from Australia, but also a spider to "glue". + +<!-- ## State + +Currently, this tool is name-squatting until the Eucalyptus game engine is in primitive completion state (basic features done, but not ready to ship out). This may take about a year (maybe). --> @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +}