kitgit

tirbofish/dropbear · commit

21162277b96b70d4b80d5fe606164c711a3e4383

changed license from custom DEL 1.2 to MIT OR Apache 2.0. added support for wesl shader compilation (for dropbear only, not for users). also did some resource cleaning up... start of new branch! this branch will deal with the outline shaders.

Unverified

tk <4tkbytes@pm.me> · 2025-10-29 12:48

view full diff

diff --git a/Cargo.toml b/Cargo.toml
index 925138b..090d228 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/LICENSE.md b/LICENSE.md
deleted file mode 100644
index e578386..0000000
--- a/LICENSE.md
+++ /dev/null
@@ -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.
diff --git a/README.md b/README.md
index debd5bf..c3526be 100644
--- a/README.md
+++ b/README.md
@@ -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. 
diff --git a/dropbear-engine/Cargo.toml b/dropbear-engine/Cargo.toml
index 7734c38..78ec532 100644
--- a/dropbear-engine/Cargo.toml
+++ b/dropbear-engine/Cargo.toml
@@ -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]
diff --git a/dropbear-engine/src/graphics.rs b/dropbear-engine/src/graphics.rs
index 77898dd..5270140 100644
--- a/dropbear-engine/src/graphics.rs
+++ b/dropbear-engine/src/graphics.rs
@@ -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 {
diff --git a/dropbear-engine/src/lib.rs b/dropbear-engine/src/lib.rs
index 1b46611..0e987bb 100644
--- a/dropbear-engine/src/lib.rs
+++ b/dropbear-engine/src/lib.rs
@@ -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 {
diff --git a/dropbear-engine/src/lighting.rs b/dropbear-engine/src/lighting.rs
index 50c26ed..0f03345 100644
--- a/dropbear-engine/src/lighting.rs
+++ b/dropbear-engine/src/lighting.rs
@@ -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);
 
diff --git a/dropbear-engine/src/shader.rs b/dropbear-engine/src/shader.rs
new file mode 100644
index 0000000..5f453bc
--- /dev/null
+++ b/dropbear-engine/src/shader.rs
@@ -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,
+        }
+    }
+}
\ No newline at end of file
diff --git a/dropbear-shader/Cargo.toml b/dropbear-shader/Cargo.toml
new file mode 100644
index 0000000..b0b862b
--- /dev/null
+++ b/dropbear-shader/Cargo.toml
@@ -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"] }
diff --git a/dropbear-shader/README.md b/dropbear-shader/README.md
new file mode 100644
index 0000000..cdafabf
--- /dev/null
+++ b/dropbear-shader/README.md
@@ -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. 
\ No newline at end of file
diff --git a/dropbear-shader/build.rs b/dropbear-shader/build.rs
new file mode 100644
index 0000000..d22804f
--- /dev/null
+++ b/dropbear-shader/build.rs
@@ -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");
+}
\ No newline at end of file
diff --git a/dropbear-shader/src/lib.rs b/dropbear-shader/src/lib.rs
new file mode 100644
index 0000000..ea37992
--- /dev/null
+++ b/dropbear-shader/src/lib.rs
@@ -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");
\ No newline at end of file
diff --git a/dropbear-shader/src/shaders/config.wesl b/dropbear-shader/src/shaders/config.wesl
new file mode 100644
index 0000000..3779838
--- /dev/null
+++ b/dropbear-shader/src/shaders/config.wesl
@@ -0,0 +1 @@
+const MAX_LIGHTS: u32 = 8;
diff --git a/dropbear-shader/src/shaders/input.wesl b/dropbear-shader/src/shaders/input.wesl
new file mode 100644
index 0000000..c3ce311
--- /dev/null
+++ b/dropbear-shader/src/shaders/input.wesl
@@ -0,0 +1,28 @@
+import package::config;
+
+struct CameraUniform {
+    view_pos: vec4<f32>,
+    view_proj: mat4x4<f32>,
+};
+
+struct OutlineUniform {
+    outline_width: f32,
+    outline_color: vec4<f32>,
+//    _padding: vec3<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,
+}
\ No newline at end of file
diff --git a/dropbear-shader/src/shaders/light.wesl b/dropbear-shader/src/shaders/light.wesl
new file mode 100644
index 0000000..dca5467
--- /dev/null
+++ b/dropbear-shader/src/shaders/light.wesl
@@ -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);
+}
\ No newline at end of file
diff --git a/dropbear-shader/src/shaders/outline.wesl b/dropbear-shader/src/shaders/outline.wesl
new file mode 100644
index 0000000..c8c2b72
--- /dev/null
+++ b/dropbear-shader/src/shaders/outline.wesl
@@ -0,0 +1,58 @@
+@group(1) @binding(0)
+var<uniform> camera: package::input::CameraUniform;
+
+@group(0) @binding(0)
+var<uniform> outline: package::input::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;
+}
\ No newline at end of file
diff --git a/dropbear-shader/src/shaders/shader.wesl b/dropbear-shader/src/shaders/shader.wesl
new file mode 100644
index 0000000..5248cc5
--- /dev/null
+++ b/dropbear-shader/src/shaders/shader.wesl
@@ -0,0 +1,192 @@
+// Main shader for standard objects.
+import package::config::MAX_LIGHTS;
+
+import package::input::{
+    CameraUniform,
+    Light,
+    LightArray,
+};
+
+@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;
+}
+
+@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);
+}
\ No newline at end of file
diff --git a/eucalyptus-core/Cargo.toml b/eucalyptus-core/Cargo.toml
index 7ec1797..e5b0f7c 100644
--- a/eucalyptus-core/Cargo.toml
+++ b/eucalyptus-core/Cargo.toml
@@ -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"
 
diff --git a/eucalyptus-core/src/scripting.rs b/eucalyptus-core/src/scripting.rs
index 3d882a3..fdfeef9 100644
--- a/eucalyptus-core/src/scripting.rs
+++ b/eucalyptus-core/src/scripting.rs
@@ -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 {
diff --git a/eucalyptus-editor/Cargo.toml b/eucalyptus-editor/Cargo.toml
index 0b00577..4671451 100644
--- a/eucalyptus-editor/Cargo.toml
+++ b/eucalyptus-editor/Cargo.toml
@@ -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"
diff --git a/eucalyptus-editor/src/editor/mod.rs b/eucalyptus-editor/src/editor/mod.rs
index 6032aaf..4add870 100644
--- a/eucalyptus-editor/src/editor/mod.rs
+++ b/eucalyptus-editor/src/editor/mod.rs
@@ -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: {:?}",
diff --git a/eucalyptus-editor/src/graphics.rs b/eucalyptus-editor/src/graphics.rs
index 6d6b240..6afda4c 100644
--- a/eucalyptus-editor/src/graphics.rs
+++ b/eucalyptus-editor/src/graphics.rs
@@ -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 =
diff --git a/magna-carta/Cargo.toml b/magna-carta/Cargo.toml
index 6826fc3..a19a9b9 100644
--- a/magna-carta/Cargo.toml
+++ b/magna-carta/Cargo.toml
@@ -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"
\ No newline at end of file
diff --git a/resources/dependencies/hotswap-agent-2.0.0.jar b/resources/dependencies/hotswap-agent-2.0.0.jar
deleted file mode 100644
index cfd2326..0000000
Binary files a/resources/dependencies/hotswap-agent-2.0.0.jar and /dev/null differ
diff --git a/resources/scripting/kotlin/Template.kt b/resources/scripting/kotlin/Template.kt
deleted file mode 100644
index 97e2201..0000000
--- a/resources/scripting/kotlin/Template.kt
+++ /dev/null
@@ -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")
-    }
-}
\ No newline at end of file
diff --git a/resources/shaders/light.wgsl b/resources/shaders/light.wgsl
deleted file mode 100644
index 57bedfa..0000000
--- a/resources/shaders/light.wgsl
+++ /dev/null
@@ -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);
-}
\ No newline at end of file
diff --git a/resources/shaders/outline.wgsl b/resources/shaders/outline.wgsl
deleted file mode 100644
index a71f445..0000000
--- a/resources/shaders/outline.wgsl
+++ /dev/null
@@ -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;
-}
\ No newline at end of file
diff --git a/resources/shaders/shader.wgsl b/resources/shaders/shader.wgsl
deleted file mode 100644
index 91402fa..0000000
--- a/resources/shaders/shader.wgsl
+++ /dev/null
@@ -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);
-}
\ No newline at end of file