tirbofish/dropbear · commit
39245f24435f938d5c3cfd3080f626f183cb133e
feature: outline shaders (incomplete)
merging back to main and dealing with this another time.
Signature present but could not be verified.
Unverified
@@ -1,12 +1,12 @@ [workspace] package.version = "0.1.2" package.edition = "2024" -package.license-file = "LICENSE.md" +package.license = "MIT OR Apache-2.0" package.repository = "https://github.com/4tkbytes/dropbear" package.readme = "README.md" resolver = "3" -members = [ "dropbear-engine", "dropbear_future-queue", "eucalyptus-core", "eucalyptus-editor", "magna-carta", "redback-runtime"] +members = [ "dropbear-engine", "dropbear-shader", "dropbear_future-queue", "eucalyptus-core", "eucalyptus-editor", "magna-carta", "redback-runtime"] [workspace.dependencies] @@ -38,7 +38,6 @@ log = "0.4" log-once = "0.4" model_to_image = "0.1.1" once_cell = "1.21" -open = "5" parking_lot = {version = "0.12", features = ["deadlock_detection"] } rfd = "0.15.4" ron = "0.11" @@ -51,7 +50,6 @@ winit = { version = "0.30", features = [] } zip = "5.1" walkdir = "2.3" rayon = "1.11" -reqwest = { version = "0.11", features = ["stream"] } backtrace = "0.3" gltf = "1" os_info = "3.12" @@ -61,10 +59,8 @@ tree-sitter = "0.22" tree-sitter-kotlin = "0.3.8" libloading = "0.8" indexmap = "2.11" -rand = "0.9" -num_enum = "0.7" sha2 = "0.10" -stdext = "0.3" +wesl = "0.2" [workspace.dependencies.image] version = "0.25" @@ -1,35 +0,0 @@ -dropbear Engine License – Version 1.2 - -Copyright (c) 2025 4tkbytes (tk) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software ("dropbear-engine") and associated components (including eucalyptus-editor, -and redback-runtime), to use, copy, modify, and integrate -them into their own projects, subject to the following conditions: - -1. Attribution - - Any project that uses the engine must give visible credit to "dropbear-engine", - and "eucalyptus-editor" in documentation, credits, - or about sections. - - For redback-runtime, credit must also be given, even if it is renamed per project. - -2. No Rebranding - - You may not distribute the dropbear-engine or the eucalyptus-editor - itself under a different name, or claim ownership of them. - - **Exception:** redback-runtime may be renamed to match the project name if necessary for execution, - provided credit to "redback-runtime" is included. - -3. Derivative Works - - You may create your own projects, libraries, or engines based on the engine. - - These projects may be licensed under any license you choose, as long as they - respect sections 1 and 2 above. - -4. Distribution - - You may distribute modified or unmodified projects built on top of the engine - as long as attribution and no-rebranding rules are followed. - -5. Disclaimer - - THE ENGINE AND ALL COMPONENTS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. - - THE AUTHOR SHALL NOT BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY. - -By using the engine or its components, you agree to abide by this license. @@ -9,6 +9,7 @@ If you might have not realised, all the crates/projects names are after Australi ## Projects - [dropbear-engine](https://github.com/4tkbytes/dropbear/tree/main/dropbear-engine) is the rendering engine that uses wgpu and the main name of the project. +- [dropbear-shader](https://github.com/4tkbytes/dropbear/tree/main/dropbear-shader) contains WESL shaders for users to import - [eucalyptus-editor](https://github.com/4tkbytes/dropbear/tree/main/eucalyptus-editor) is the visual editor used to create games visually, taking inspiration from Unity, Unreal, Roblox Studio and other engines. - [eucalyptus-core](https://github.com/4tkbytes/dropbear/tree/main/eucalyptus-core) is the library used by both `redback-runtime` and `eucalyptus-editor` to share configs and metadata between each other. - [redback-runtime](https://github.com/4tkbytes/dropbear/tree/main/redback-runtime) is the runtime used to load .eupak files and run the game loaded on them. @@ -104,7 +105,7 @@ Yeah, yeah, go ahead and contribute. Make sure it works, and its not spam, and a # Licensing -In the case someone actually makes something with my engine and distributes it, the projects (meaning **dropbear-engine**, -**eucalyptus** and **redback-runtime**) must abide by the license in [LICENSE.md](LICENSE.md). +All projects made in this repository is under the license of MIT or Apache 2.0 depending on your choice. -The **dropbear_future-queue** rust library is available under the `MIT` license, which can be used by anyone. +It is recommended that you give us (the engine) credit, as it allows for more community support. This could be +in the form of a credit scene or in the splash screen. @@ -4,12 +4,14 @@ description = "A game engine made by 4tkbytes. Thats really it..." version.workspace = true edition.workspace = true -license-file.workspace = true +license.workspace = true repository.workspace = true readme.workspace = true [dependencies] dropbear_future-queue = { path = "../dropbear_future-queue" } +dropbear-shader = { path = "../dropbear-shader" } + anyhow.workspace = true app_dirs2.workspace = true bytemuck.workspace = true @@ -36,7 +38,6 @@ rayon.workspace = true backtrace.workspace = true os_info.workspace = true rustc_version_runtime.workspace = true -crossbeam-channel.workspace = true ron.workspace = true [target.'cfg(not(target_os = "android"))'.dependencies] @@ -1,7 +1,7 @@ use crate::{ - State, egui_renderer::EguiRenderer, model::{self, Vertex}, + State, }; use dropbear_future_queue::FutureQueue; use egui::{Context, TextureId}; @@ -10,14 +10,15 @@ use image::GenericImageView; use parking_lot::Mutex; use std::{fs, path::PathBuf, sync::Arc, time::Instant}; use wgpu::{ - BindGroup, BindGroupLayout, Buffer, BufferAddress, BufferUsages, Color, CommandEncoder, - CompareFunction, DepthBiasState, Device, Extent3d, LoadOp, Operations, Queue, RenderPass, - RenderPassDepthStencilAttachment, RenderPipeline, Sampler, ShaderModule, StencilState, - SurfaceConfiguration, TextureDescriptor, TextureFormat, TextureUsages, TextureView, - TextureViewDescriptor, VertexBufferLayout, - util::{BufferInitDescriptor, DeviceExt}, + util::{BufferInitDescriptor, DeviceExt}, BindGroup, BindGroupLayout, Buffer, BufferAddress, BufferUsages, Color, + CommandEncoder, CompareFunction, DepthBiasState, Device, Extent3d, LoadOp, Operations, Queue, + RenderPass, RenderPassDepthStencilAttachment, RenderPipeline, Sampler, + StencilState, SurfaceConfiguration, TextureDescriptor, TextureFormat, TextureUsages, + TextureView, TextureViewDescriptor, + VertexBufferLayout, }; use winit::window::Window; +use crate::shader::Shader; pub const NO_TEXTURE: &[u8] = include_bytes!("../../resources/textures/no-texture.png"); pub const NO_MODEL: &[u8] = include_bytes!("../../resources/models/error.glb"); @@ -242,37 +243,6 @@ impl<'a> RenderContext<'a> { } } -/// A nice little struct that stored basic information about a WGPU shader. -pub struct Shader { - pub label: String, - pub module: ShaderModule, -} - -impl Shader { - pub fn new( - graphics: Arc<SharedGraphicsContext>, - shader_file_contents: &str, - label: Option<&str>, - ) -> Self { - let module = graphics - .device - .create_shader_module(wgpu::ShaderModuleDescriptor { - label, - source: wgpu::ShaderSource::Wgsl(shader_file_contents.into()), - }); - - log::debug!("Created new shader under the label: {:?}", label); - - Self { - label: match label { - Some(label) => label.into(), - None => "shader".into(), - }, - module, - } - } -} - #[derive(Clone)] /// Describes a texture, like an image of some sort. Can be a normal texture on a model or a viewport or depth texture. pub struct Texture { @@ -13,6 +13,7 @@ pub mod resources; pub mod scene; pub mod utils; pub mod colour; +pub mod shader; use app_dirs2::{AppDataType, AppInfo}; use bytemuck::Contiguous; @@ -45,18 +46,19 @@ use winit::{ keyboard::{KeyCode, PhysicalKey}, window::Window, }; +use dropbear_future_queue::FutureQueue; +use log::LevelFilter; +use ron::ser::PrettyConfig; +use serde::{Deserialize, Serialize}; +use winit::event::{DeviceEvent, DeviceId}; use crate::{egui_renderer::EguiRenderer, graphics::Texture}; pub use dropbear_future_queue as future; -use dropbear_future_queue::FutureQueue; pub use gilrs; -use log::LevelFilter; -use ron::ser::PrettyConfig; -use serde::{Deserialize, Serialize}; pub use wgpu; pub use winit; -use winit::event::{DeviceEvent, DeviceId}; + /// The backend information, such as the device, queue, config, surface, renderer, window and more. pub struct State { @@ -2,8 +2,8 @@ use glam::{DMat4, DQuat, DVec3}; use std::fmt::{Display, Formatter}; use std::sync::Arc; use wgpu::{ - BindGroup, BindGroupLayout, Buffer, BufferAddress, CompareFunction, DepthBiasState, - RenderPipeline, StencilState, VertexBufferLayout, util::DeviceExt, + util::DeviceExt, BindGroup, BindGroupLayout, Buffer, BufferAddress, CompareFunction, + DepthBiasState, RenderPipeline, StencilState, VertexBufferLayout, }; use crate::attenuation::{Attenuation, RANGE_50}; @@ -12,9 +12,9 @@ use crate::model::{LazyModel, LazyType}; use crate::{ camera::Camera, entity::Transform, - graphics::Shader, model::{self, Model, Vertex}, }; +use crate::shader::Shader; pub const MAX_LIGHTS: usize = 8; @@ -584,7 +584,7 @@ impl LightManager { camera: &Camera, label: Option<&str>, ) { - use crate::graphics::Shader; + use crate::shader::Shader; let shader = Shader::new(graphics.clone(), shader_contents, label); @@ -0,0 +1,38 @@ +//! Deals with shaders, including WESL shaders +use std::sync::Arc; +use wgpu::ShaderModule; +use crate::graphics::SharedGraphicsContext; + +pub use dropbear_shader as shader_wesl; + +/// A nice little struct that stored basic information about a WGPU shader. +pub struct Shader { + pub label: String, + pub module: ShaderModule, +} + +impl Shader { + /// Creates a new [`ShaderModule`] from its file contents. + pub fn new( + graphics: Arc<SharedGraphicsContext>, + shader_file_contents: &str, + label: Option<&str>, + ) -> Self { + let module = graphics + .device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label, + source: wgpu::ShaderSource::Wgsl(shader_file_contents.into()), + }); + + log::debug!("Created new shader under the label: {:?}", label); + + Self { + label: match label { + Some(label) => label.into(), + None => "shader".into(), + }, + module, + } + } +} @@ -0,0 +1,13 @@ +[package] +name = "dropbear-shader" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +readme.workspace = true + +[dependencies] +wesl = { workspace = true } + +[build-dependencies] +wesl = { workspace = true, features = ["package"] } @@ -0,0 +1,10 @@ +# dropbear-shader + +This crate is used for shaders created with the WESL language, such as rendering models and generating +mipmaps. It also provides templates for users to create their own shaders for their own +projects with the WESL language. + +## What is WESL? + +WESL (or WebGPU Extension Shader Language) is a shader language that is fully compatible with WGPU shaders, and +adds extension features such as import statements and rust-like #[cfg] functions. @@ -0,0 +1,31 @@ +use std::fs; +use std::path::Path; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let shader_dir = Path::new("src/shaders"); + + println!("cargo:rerun-if-changed={}", shader_dir.display()); + if let Ok(entries) = fs::read_dir(shader_dir) { + for entry in entries.flatten() { + let path = entry.path(); + if path.is_file() { + println!("cargo:rerun-if-changed={}", path.display()); + } + } + } + + wesl::PkgBuilder::new("dropbear") + .scan_root("src/shaders") + .expect("failed to scan for dropbear wesl shaders") + .validate() + .map_err(|e| eprintln!("{e}")) + .expect("validation error") + .build_artifact() + .expect("failed to build artifact"); + + wesl::Wesl::new("src/shaders").build_artifact(&"package::light".parse().unwrap(), "dropbear_light"); + wesl::Wesl::new("src/shaders").build_artifact(&"package::shader".parse().unwrap(), "dropbear_shader"); + wesl::Wesl::new("src/shaders").build_artifact(&"package::outline".parse().unwrap(), "dropbear_outline"); +} @@ -0,0 +1,7 @@ +use wesl::include_wesl; + +wesl::wesl_pkg!(dropbear); + +pub const LIGHT_SHADER: &str = include_wesl!("dropbear_light"); +pub const SHADER_SHADER: &str = include_wesl!("dropbear_shader"); +pub const OUTLINE_SHADER: &str = include_wesl!("dropbear_outline"); @@ -0,0 +1 @@ +const MAX_LIGHTS: u32 = 8; @@ -0,0 +1,27 @@ +import package::config; + +struct CameraUniform { + view_pos: vec4<f32>, + view_proj: mat4x4<f32>, +}; + +struct OutlineUniform { + outline_width: f32, + outline_color: vec4<f32>, +}; + +struct Light { + position: vec4<f32>, + direction: vec4<f32>, // x, y, z, outer_cutoff_angle + color: vec4<f32>, // r, g, b, light_type (0, 1, 2) + constant: f32, + lin: f32, + quadratic: f32, + cutoff: f32, +} + +struct LightArray { + _lights: array<Light, config::MAX_LIGHTS>, + light_count: u32, + ambient_strength: f32, +} @@ -0,0 +1,52 @@ +// Shader for rendering the white cube for lighting (or diff depending on colour) + +import package::input::{ + CameraUniform, + Light, +}; + +@group(0) @binding(0) +var<uniform> camera: CameraUniform; + +@group(1) @binding(0) +var<uniform> light: Light; + +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>, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4<f32>, + @location(0) color: vec3<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.clip_position = camera.view_proj * model_matrix * vec4<f32>(model.position, 1.0); + out.color = light.color.xyz; + return out; +} + +// Fragment shader + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { + return vec4<f32>(in.color, 1.0); +} @@ -0,0 +1,65 @@ +// outline.wgsl +import package::input::CameraUniform; +import package::input::OutlineUniform; + +@group(1) @binding(0) +var<uniform> camera: CameraUniform; + +@group(0) @binding(0) +var<uniform> outline: OutlineUniform; + +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>, + + @location(9) normal_matrix_0: vec3<f32>, + @location(10) normal_matrix_1: vec3<f32>, + @location(11) normal_matrix_2: vec3<f32>, +} + +struct VertexInput { + @location(0) position: vec3<f32>, + @location(1) tex_coords: vec2<f32>, // unused, but must match vertex layout + @location(2) normal: vec3<f32>, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4<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, + ); + + let normal_matrix = mat3x3<f32>( + instance.normal_matrix_0, + instance.normal_matrix_1, + instance.normal_matrix_2, + ); + + // Transform normal to world space + let world_normal = normalize(normal_matrix * model.normal); + + // Extrude position in WORLD SPACE + let world_position = (model_matrix * vec4<f32>(model.position, 1.0)).xyz; + let expanded_world_position = world_position + world_normal * outline.outline_width; + + var out: VertexOutput; + out.clip_position = camera.view_proj * vec4<f32>(expanded_world_position, 1.0); + return out; +} + +@fragment +fn fs_main() -> @location(0) vec4<f32> { + return outline.outline_color; // Use the uniform color! +} @@ -0,0 +1,193 @@ +// Main shader for standard objects. +import package::config::MAX_LIGHTS; + +import package::input::{ + CameraUniform, + Light, + LightArray, +}; + +@group(0) @binding(0) +var t_diffuse: texture_2d<f32>; +@group(0) @binding(1) +var s_diffuse: sampler; + +@group(1) @binding(0) +var<uniform> camera: CameraUniform; + +@group(2) @binding(0) +var<uniform> light_array: LightArray; + +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>, + + @location(9) normal_matrix_0: vec3<f32>, + @location(10) normal_matrix_1: vec3<f32>, + @location(11) normal_matrix_2: vec3<f32>, +}; + +struct VertexInput { + @location(0) position: vec3<f32>, + @location(1) tex_coords: vec2<f32>, + @location(2) normal: vec3<f32>, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4<f32>, + @location(0) tex_coords: vec2<f32>, + @location(1) world_normal: vec3<f32>, + @location(2) world_position: vec3<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, + ); + let normal_matrix = mat3x3<f32>( + instance.normal_matrix_0, + instance.normal_matrix_1, + instance.normal_matrix_2, + ); + var out: VertexOutput; + out.tex_coords = model.tex_coords; + out.world_normal = normal_matrix * model.normal; + var world_position: vec4<f32> = model_matrix * vec4<f32>(model.position, 1.0); + out.world_position = world_position.xyz; + out.clip_position = camera.view_proj * world_position; + return out; +} + +fn calculate_light(light: Light, world_pos: vec3<f32>, world_normal: vec3<f32>, view_dir: vec3<f32>) -> vec3<f32> { + let light_dir = normalize(light.position.xyz - world_pos); + + // dihfuse + let diffuse_strength = max(dot(world_normal, light_dir), 0.0); + let diffuse_color = light.color.xyz * diffuse_strength; + + // specular + let half_dir = normalize(view_dir + light_dir); + let specular_strength = pow(max(dot(world_normal, half_dir), 0.0), 32.0); + let specular_color = specular_strength * light.color.xyz; + + return diffuse_color + specular_color; +} + +fn directional_light( + light: Light, + world_normal: vec3<f32>, + view_dir: vec3<f32>, + tex_color: vec3<f32> +) -> vec3<f32> { + let light_dir = normalize(-light.direction.xyz); + + let ambient = light.color.xyz * light_array.ambient_strength * tex_color; + + let diff = max(dot(world_normal, light_dir), 0.0); + let diffuse = light.color.xyz * diff * tex_color; + + let reflect_dir = reflect(-light_dir, world_normal); + let spec = pow(max(dot(view_dir, reflect_dir), 0.0), 32.0); + let specular = light.color.xyz * spec * tex_color; + + return ambient + diffuse + specular; +} + +// https://learnopengl.com/code_viewer_gh.php?code=src/2.lighting/5.2.light_casters_point/5.2.light_casters.fs +// deal with later. current issue: it is showing only yellow and white in point light (weird...) +// note: fixed, forgot to push attenuation values to gpu lol +fn point_light(light: Light, world_pos: vec3<f32>, world_normal: vec3<f32>, view_dir: vec3<f32>, tex_color: vec3<f32>) -> vec3<f32> { + let norm = normalize(world_normal); + let light_dir = normalize(light.position.xyz - world_pos); + let diff = max(dot(norm, light_dir), 0.0); + let diffuse = light.color.xyz * diff * tex_color; + + let shininess = 32.0; + let reflect_dir = reflect(-light_dir, norm); + let spec = pow(max(dot(view_dir, reflect_dir), 0.0), shininess); + let specular = light.color.xyz * spec * tex_color; + + let distance = length(light.position.xyz - world_pos); + let attenuation = 1.0 / (light.constant + (light.lin * distance) + (light.quadratic * (distance * distance))); + + return (diffuse + specular) * attenuation; +} + +fn spot_light(light: Light, world_pos: vec3<f32>, world_normal: vec3<f32>, view_dir: vec3<f32>, tex_color: vec3<f32>) -> vec3<f32> { + let outer_cutoff = light.direction.w; + let ambient = light.color.xyz * light_array.ambient_strength * tex_color; + + let norm = normalize(world_normal); + let light_dir = normalize(light.position.xyz - world_pos); + let diff = max(dot(norm, light_dir), 0.0); + var diffuse = light.color.xyz * diff * tex_color; + + let shininess = 32.0; + let reflect_dir = reflect(-light_dir, norm); + let spec = pow(max(dot(view_dir, reflect_dir), 0.0), shininess); + var specular = light.color.xyz * spec * tex_color; + + let theta = dot(light_dir, normalize(-light.direction.xyz)); + let epsilon = light.cutoff - outer_cutoff; + let intensity = clamp((theta - outer_cutoff) / epsilon, 0.0, 1.0); + + diffuse *= intensity; + specular *= intensity; + + let distance = length(light.position.xyz - world_pos); + let attenuation = 1.0 / (light.constant + (light.lin * distance) + (light.quadratic * (distance * distance))); + + let ambient_attenuated = ambient * attenuation; + let diffuse_attenuated = diffuse * attenuation; + let specular_attenuated = specular * attenuation; + + return ambient_attenuated + diffuse_attenuated + specular_attenuated; +} + +@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; + } + + let view_dir = normalize(camera.view_pos.xyz - in.world_position); + let world_normal = normalize(in.world_normal); + + var final_color = vec3<f32>(0.0); + + var total_ambient = vec3<f32>(0.0); + for (var i: u32 = 0u; i < light_array.light_count; i = i + 1u) { + let light = light_array._lights[i]; + total_ambient += light.color.xyz * light_array.ambient_strength; + } + + for (var i: u32 = 0u; i < light_array.light_count; i = i + 1u) { + let light = light_array._lights[i]; + + // light type is color.w + if light.color.w == 0.0 { + // dir + final_color += directional_light(light, world_normal, view_dir, tex_color.xyz); + } else if light.color.w == 1.0 { + // point + final_color += point_light(light, in.world_position, world_normal, view_dir, tex_color.xyz); + } else if light.color.w == 2.0 { + // spot + final_color += spot_light(light, in.world_position, world_normal, view_dir, tex_color.xyz); + } + } + + final_color = (total_ambient * tex_color.xyz) + final_color; + + return vec4<f32>(final_color, tex_color.a); +} @@ -2,7 +2,7 @@ name = "eucalyptus-core" version.workspace = true edition.workspace = true -license-file.workspace = true +license.workspace = true repository.workspace = true readme = "README.md" @@ -14,9 +14,6 @@ use std::path::{Path, PathBuf}; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::Command; -// why tf do you exist? oh well :shrug: -pub const TEMPLATE_SCRIPT: &str = include_str!("../../resources/scripting/kotlin/Template.kt"); - /// The target of the script. This can be either a JVM or a native library. #[derive(Default, Clone)] pub enum ScriptTarget { @@ -2,7 +2,7 @@ name = "eucalyptus-editor" version.workspace = true edition.workspace = true -license-file.workspace = true +license.workspace = true repository.workspace = true default-run = "eucalyptus-editor" readme = "README.md" @@ -26,13 +26,13 @@ pub struct EditorTabViewer<'a> { pub tex_size: Extent3d, pub gizmo: &'a mut Gizmo, pub world: &'a mut World, - pub selected_entity: &'a mut Option<hecs::Entity>, + pub selected_entity: &'a mut Option<Entity>, pub viewport_mode: &'a mut ViewportMode, pub undo_stack: &'a mut Vec<UndoableAction>, pub signal: &'a mut Signal, pub gizmo_mode: &'a mut EnumSet<GizmoMode>, pub editor_mode: &'a mut EditorState, - pub active_camera: &'a mut Arc<Mutex<Option<hecs::Entity>>>, + pub active_camera: &'a mut Arc<Mutex<Option<Entity>>>, pub plugin_registry: &'a mut PluginRegistry, // "wah wah its unsafe, its using raw pointers" shut the fuck up if it breaks i will know pub editor: *mut Editor, @@ -511,7 +511,9 @@ impl<'a> TabViewer for EditorTabViewer<'a> { "Model thumbnail [{}] does not exist, generating one now", name ); - let mut model = match model_to_image::ModelToImageBuilder::new(path) + let project_path = { PROJECT.read().project_path.clone() }; + let path = ResourceReference::from_path(path).unwrap().to_project_path(project_path).unwrap(); + let mut model = match model_to_image::ModelToImageBuilder::new(&path) .with_size((600, 600)) .build() { Ok(v) => v, @@ -13,26 +13,26 @@ use crossbeam_channel::Receiver; use dropbear_engine::{ camera::Camera, entity::{AdoptedEntity, Transform}, - graphics::{RenderContext, Shader, SharedGraphicsContext}, + future::FutureHandle, + graphics::{RenderContext, SharedGraphicsContext}, lighting::{Light, LightManager}, - scene::SceneCommand, model::ModelId, - future::FutureHandle, + scene::SceneCommand, }; use egui::{self, Context}; use egui_dock_fork::{DockArea, DockState, NodeIndex, Style}; use eucalyptus_core::{ - fatal, info, states, success, warn, success_without_console, - camera::{CameraComponent, CameraType, DebugCamera}, - window::GRAPHICS_COMMAND, - utils::ViewportMode, + camera::{CameraComponent, CameraType, DebugCamera}, fatal, info, input::InputState, ptr::{GraphicsPtr, InputStatePtr, WorldPtr}, scripting::{BuildStatus, ScriptManager, ScriptTarget}, + states, states::{ - CameraConfig, EditorTab, EntityNode, LightConfig, ModelProperties, PROJECT, SCENES, - SceneEntity, ScriptComponent, WorldLoadingStatus, + CameraConfig, EditorTab, EntityNode, LightConfig, ModelProperties, SceneEntity, ScriptComponent, + WorldLoadingStatus, PROJECT, SCENES, }, - scripting::{BuildStatus, ScriptManager, ScriptTarget}, - ptr::{GraphicsPtr, InputStatePtr, WorldPtr}, - input::InputState + success, + success_without_console, + utils::ViewportMode, + warn, + window::GRAPHICS_COMMAND }; use hecs::{Entity, World}; use parking_lot::Mutex; @@ -49,6 +49,7 @@ use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode}; use wgpu::{Color, Extent3d, RenderPipeline}; use winit::window::CursorGrabMode; use winit::{keyboard::KeyCode, window::Window}; +use dropbear_engine::shader::Shader; use crate::graphics::OutlineShader; pub struct Editor { @@ -838,9 +839,10 @@ impl Editor { /// /// **Note**: To be ran AFTER [`Editor::load_project_config`] pub fn load_wgpu_nerdy_stuff<'a>(&mut self, graphics: &mut RenderContext<'a>) { + log::debug!("Contents of viewport shader: \n{:#?}", dropbear_engine::shader::shader_wesl::SHADER_SHADER); let shader = Shader::new( graphics.shared.clone(), - include_str!("../../../resources/shaders/shader.wgsl"), + dropbear_engine::shader::shader_wesl::SHADER_SHADER, Some("viewport_shader"), ); @@ -864,16 +866,17 @@ impl Editor { ); self.render_pipeline = Some(pipeline); + log::debug!("Contents of light shader: \n{:#?}", dropbear_engine::shader::shader_wesl::LIGHT_SHADER); self.light_manager.create_render_pipeline( graphics.shared.clone(), - include_str!("../../../resources/shaders/light.wgsl"), + dropbear_engine::shader::shader_wesl::LIGHT_SHADER, camera, Some("Light Pipeline"), ); + log::debug!("Contents of outline shader: \n{:#?}", dropbear_engine::shader::shader_wesl::OUTLINE_SHADER); let outline_shader = OutlineShader::init(graphics.shared.clone(), camera.layout()); self.outline_pipeline = Some(outline_shader); - } else { log_once::warn_once!( "Unable to fetch the query result of camera: {:?}", @@ -359,6 +359,19 @@ impl Scene for Editor { usage: wgpu::BufferUsages::VERTEX, }); + { // normal model rendering + let mut render_pass = graphics.continue_pass(); + render_pass.set_pipeline(pipeline); + + render_pass.set_vertex_buffer(1, instance_buffer.slice(..)); + render_pass.draw_model_instanced( + &model, + 0..instances.len() as u32, + camera.bind_group(), + self.light_manager.bind_group(), + ); + } + // outline rendering let has_selected = entities.iter() .any(|e| e.model.id == model_ptr && e.is_selected); @@ -386,19 +399,6 @@ impl Scene for Editor { ); } } - - { // normal model rendering - let mut render_pass = graphics.continue_pass(); - render_pass.set_pipeline(pipeline); - - render_pass.set_vertex_buffer(1, instance_buffer.slice(..)); - render_pass.draw_model_instanced( - &model, - 0..instances.len() as u32, - camera.bind_group(), - self.light_manager.bind_group(), - ); - } log_once::debug_once!("Rendered {:?}", model_ptr); } else { log_once::error_once!("No such MODEL as {:?}", model_ptr); @@ -2,10 +2,9 @@ use std::sync::Arc; use dropbear_engine::colour::Colour; -use dropbear_engine::graphics::{InstanceRaw, Shader, SharedGraphicsContext, Texture}; +use dropbear_engine::graphics::{InstanceRaw, SharedGraphicsContext, Texture}; use dropbear_engine::model::{ModelVertex, Vertex}; - -pub const OUTLINE_SHADER: &str = include_str!("../../resources/shaders/outline.wgsl"); +use dropbear_engine::shader::Shader; #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -34,7 +33,7 @@ pub struct OutlineShader { impl OutlineShader { pub fn init(graphics: Arc<SharedGraphicsContext>, camera_layout: &wgpu::BindGroupLayout) -> Self { - let shader = Shader::new(graphics.clone(), OUTLINE_SHADER, Some("outline_shader")); + let shader = Shader::new(graphics.clone(), dropbear_engine::shader::shader_wesl::OUTLINE_SHADER, Some("outline_shader")); log::trace!("Created outline shader"); let bind_group_layout = @@ -2,7 +2,7 @@ name = "magna-carta" version.workspace = true edition.workspace = true -license = "MIT" +license.workspace = true repository.workspace = true readme = "README.md" @@ -11,6 +11,4 @@ anyhow.workspace = true tree-sitter.workspace = true tree-sitter-kotlin.workspace = true clap = { version = "4.0", features = ["derive"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" chrono = "0.4" Binary files a/resources/dependencies/hotswap-agent-2.0.0.jar and /dev/null differ @@ -1,11 +0,0 @@ -import com.dropbear - -class Example(override var engine: DropbearEngine) : RunnableScript { - override fun load() { - TODO("Not yet implemented") - } - - override fun update() { - TODO("Not yet implemented") - } -} @@ -1,61 +0,0 @@ -// Shader for rendering the white cube for lighting (or diff depending on colour) - -struct Camera { - view_pos: vec4<f32>, - view_proj: mat4x4<f32>, -} -@group(0) @binding(0) -var<uniform> camera: Camera; - -struct Light { - position: vec4<f32>, - direction: vec4<f32>, // x, y, z, outer_cutoff_angle - color: vec4<f32>, // r, g, b, light_type (0, 1, 2) - constant: f32, - lin: f32, - quadratic: f32, - cutoff: f32, -} - -@group(1) @binding(0) -var<uniform> light: Light; - -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>, -}; - -struct VertexOutput { - @builtin(position) clip_position: vec4<f32>, - @location(0) color: vec3<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.clip_position = camera.view_proj * model_matrix * vec4<f32>(model.position, 1.0); - out.color = light.color.xyz; - return out; -} - -// Fragment shader - -@fragment -fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { - return vec4<f32>(in.color, 1.0); -} @@ -1,69 +0,0 @@ -struct CameraUniform { - view_pos: vec4<f32>, - view_proj: mat4x4<f32>, -}; - -@group(1) @binding(0) -var<uniform> camera: CameraUniform; - -struct OutlineUniform { - outline_width: f32, - outline_color: vec4<f32>, -// _padding: vec3<f32>, -}; - -@group(0) @binding(0) -var<uniform> outline: OutlineUniform; - -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>, - - @location(9) normal_matrix_0: vec3<f32>, - @location(10) normal_matrix_1: vec3<f32>, - @location(11) normal_matrix_2: vec3<f32>, -}; - -struct VertexInput { - @location(0) position: vec3<f32>, - @location(1) tex_coords: vec2<f32>, - @location(2) normal: vec3<f32>, -}; - -struct VertexOutput { - @builtin(position) clip_position: vec4<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, - ); - let normal_matrix = mat3x3<f32>( - instance.normal_matrix_0, - instance.normal_matrix_1, - instance.normal_matrix_2, - ); - - let world_normal = normalize(normal_matrix * model.normal); - let expanded_position = model.position + model.normal * outline.outline_width; - let world_position = model_matrix * vec4<f32>(expanded_position, 1.0); - - var out: VertexOutput; - out.clip_position = camera.view_proj * world_position; - - return out; -} - -@fragment -fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { - return outline.outline_color; -} @@ -1,206 +0,0 @@ -// Main shader for standard objects. -const MAX_LIGHTS: u32 = 8; - -struct CameraUniform { - view_pos: vec4<f32>, - view_proj: mat4x4<f32>, -}; -@group(1) @binding(0) -var<uniform> camera: CameraUniform; - -struct Light { - position: vec4<f32>, // x, y, z, unused - direction: vec4<f32>, // x, y, z, unused - color: vec4<f32>, // r, g, b, light_type (0, 1, 2) - constant: f32, - lin: f32, // linear - quadratic: f32, - cutoff: f32, -} - -struct LightArray { - _lights: array<Light, MAX_LIGHTS>, - light_count: u32, - ambient_strength: f32, -} - -@group(2) @binding(0) -var<uniform> light_array: LightArray; - -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>, - - @location(9) normal_matrix_0: vec3<f32>, - @location(10) normal_matrix_1: vec3<f32>, - @location(11) normal_matrix_2: vec3<f32>, -}; - -struct VertexInput { - @location(0) position: vec3<f32>, - @location(1) tex_coords: vec2<f32>, - @location(2) normal: vec3<f32>, -}; - -struct VertexOutput { - @builtin(position) clip_position: vec4<f32>, - @location(0) tex_coords: vec2<f32>, - @location(1) world_normal: vec3<f32>, - @location(2) world_position: vec3<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, - ); - let normal_matrix = mat3x3<f32>( - instance.normal_matrix_0, - instance.normal_matrix_1, - instance.normal_matrix_2, - ); - var out: VertexOutput; - out.tex_coords = model.tex_coords; - out.world_normal = normal_matrix * model.normal; - var world_position: vec4<f32> = model_matrix * vec4<f32>(model.position, 1.0); - out.world_position = world_position.xyz; - out.clip_position = camera.view_proj * world_position; - return out; -} - -@group(0) @binding(0) -var t_diffuse: texture_2d<f32>; -@group(0) @binding(1) -var s_diffuse: sampler; - -fn calculate_light(light: Light, world_pos: vec3<f32>, world_normal: vec3<f32>, view_dir: vec3<f32>) -> vec3<f32> { - let light_dir = normalize(light.position.xyz - world_pos); - - // dihfuse - let diffuse_strength = max(dot(world_normal, light_dir), 0.0); - let diffuse_color = light.color.xyz * diffuse_strength; - - // specular - let half_dir = normalize(view_dir + light_dir); - let specular_strength = pow(max(dot(world_normal, half_dir), 0.0), 32.0); - let specular_color = specular_strength * light.color.xyz; - - return diffuse_color + specular_color; -} - -fn directional_light( - light: Light, - world_normal: vec3<f32>, - view_dir: vec3<f32>, - tex_color: vec3<f32> -) -> vec3<f32> { - let light_dir = normalize(-light.direction.xyz); - - let ambient = light.color.xyz * light_array.ambient_strength * tex_color; - - let diff = max(dot(world_normal, light_dir), 0.0); - let diffuse = light.color.xyz * diff * tex_color; - - let reflect_dir = reflect(-light_dir, world_normal); - let spec = pow(max(dot(view_dir, reflect_dir), 0.0), 32.0); - let specular = light.color.xyz * spec * tex_color; - - return ambient + diffuse + specular; -} - -// https://learnopengl.com/code_viewer_gh.php?code=src/2.lighting/5.2.light_casters_point/5.2.light_casters.fs -// deal with later. current issue: it is showing only yellow and white in point light (weird...) -// note: fixed, forgot to push attenuation values to gpu lol -fn point_light(light: Light, world_pos: vec3<f32>, world_normal: vec3<f32>, view_dir: vec3<f32>, tex_color: vec3<f32>) -> vec3<f32> { - let norm = normalize(world_normal); - let light_dir = normalize(light.position.xyz - world_pos); - let diff = max(dot(norm, light_dir), 0.0); - let diffuse = light.color.xyz * diff * tex_color; - - let shininess = 32.0; - let reflect_dir = reflect(-light_dir, norm); - let spec = pow(max(dot(view_dir, reflect_dir), 0.0), shininess); - let specular = light.color.xyz * spec * tex_color; - - let distance = length(light.position.xyz - world_pos); - let attenuation = 1.0 / (light.constant + (light.lin * distance) + (light.quadratic * (distance * distance))); - - return (diffuse + specular) * attenuation; -} - -fn spot_light(light: Light, world_pos: vec3<f32>, world_normal: vec3<f32>, view_dir: vec3<f32>, tex_color: vec3<f32>) -> vec3<f32> { - let outer_cutoff = light.direction.w; - let ambient = light.color.xyz * light_array.ambient_strength * tex_color; - - let norm = normalize(world_normal); - let light_dir = normalize(light.position.xyz - world_pos); - let diff = max(dot(norm, light_dir), 0.0); - var diffuse = light.color.xyz * diff * tex_color; - - let shininess = 32.0; - let reflect_dir = reflect(-light_dir, norm); - let spec = pow(max(dot(view_dir, reflect_dir), 0.0), shininess); - var specular = light.color.xyz * spec * tex_color; - - let theta = dot(light_dir, normalize(-light.direction.xyz)); - let epsilon = light.cutoff - outer_cutoff; - let intensity = clamp((theta - outer_cutoff) / epsilon, 0.0, 1.0); - - diffuse *= intensity; - specular *= intensity; - - let distance = length(light.position.xyz - world_pos); - let attenuation = 1.0 / (light.constant + (light.lin * distance) + (light.quadratic * (distance * distance))); - - let ambient_attenuated = ambient * attenuation; - let diffuse_attenuated = diffuse * attenuation; - let specular_attenuated = specular * attenuation; - - return ambient_attenuated + diffuse_attenuated + specular_attenuated; -} - -@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; - } - - let view_dir = normalize(camera.view_pos.xyz - in.world_position); - let world_normal = normalize(in.world_normal); - - var final_color = vec3<f32>(0.0); - - var total_ambient = vec3<f32>(0.0); - for (var i: u32 = 0u; i < light_array.light_count; i = i + 1u) { - let light = light_array._lights[i]; - total_ambient += light.color.xyz * light_array.ambient_strength; - } - - for (var i: u32 = 0u; i < light_array.light_count; i = i + 1u) { - let light = light_array._lights[i]; - - // light type is color.w - if light.color.w == 0.0 { - final_color += directional_light(light, world_normal, view_dir, tex_color.xyz); - } else if light.color.w == 1.0 { - // point - final_color += point_light(light, in.world_position, world_normal, view_dir, tex_color.xyz); - } else if light.color.w == 2.0 { - final_color += spot_light(light, in.world_position, world_normal, view_dir, tex_color.xyz); - } - } - - // Combine ambient and lighting - final_color = (total_ambient * tex_color.xyz) + final_color; - - return vec4<f32>(final_color, tex_color.a); -}