kitgit

tirbofish/dropbear · diff

98781d6 · Thribhu K

created a new euca://internal URI for any models that are stored in the executables memory. first one i implemented was euca://internal/dropbear/cube also created a way to package eucalyptus projects into one uniform data.eupak file. also created a way to build for kotlin native. also planning on creating a dropbear gradle plugin for depenedency management and neater building. jarvis, run github actions

Unverified

diff --git a/dropbear-engine/src/utils.rs b/dropbear-engine/src/utils.rs
index c1acb30..9e74b46 100644
--- a/dropbear-engine/src/utils.rs
+++ b/dropbear-engine/src/utils.rs
@@ -6,6 +6,8 @@ use std::path::Path;
 
 pub const EUCA_SCHEME: &str = "euca://";
 
+pub const INTERNAL_MODELS: &[&str] = &["cube"];
+
 /// Converts any supported resource reference into the canonical `euca://` form.
 ///
 /// The function trims whitespace, normalizes path separators, ensures the scheme
diff --git a/dropbear-gradle-plugin/build.gradle.kts b/dropbear-gradle-plugin/build.gradle.kts
new file mode 100644
index 0000000..7ab6cfb
--- /dev/null
+++ b/dropbear-gradle-plugin/build.gradle.kts
@@ -0,0 +1,72 @@
+import org.gradle.kotlin.dsl.compileOnly
+
+plugins {
+    `kotlin-dsl`
+    `maven-publish`
+    id("com.gradle.plugin-publish") version "2.0.0"
+}
+
+group = "com.dropbear"
+version = "1.0-SNAPSHOT"
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    implementation("de.undercouch:gradle-download-task:5.6.0")
+    compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:${KotlinVersion.CURRENT}")
+}
+
+gradlePlugin {
+    website.set("https://github.com/4tkbytes/dropbear")
+    vcsUrl.set("https://github.com/4tkbytes/dropbear")
+    plugins {
+
+        create("dropbearGradlePlugin") {
+            id = "dropbear-gradle-plugin"
+            implementationClass = "com.dropbear.gradle.DropbearGradlePlugin"
+            displayName = "dropbear-gradle-plugin"
+            description = "Gradle plugin for dependency management for dropbear-based projects"
+            version = version as String
+        }
+    }
+}
+
+publishing {
+    publications {
+        withType<MavenPublication>().configureEach {
+            pom {
+                name.set("dropbear-gradle-plugin")
+                description.set("Gradle plugin for dropbear dependency management")
+                url.set("https://4tkbytes.github.io/dropbear/")
+
+                licenses {
+                    license {
+                        name.set("MIT License")
+                        url.set("https://opensource.org/licenses/MIT")
+                    }
+                }
+                developers {
+                    developer {
+                        id.set("4tkbytes")
+                        name.set("tk")
+                        email.set("4tkbytes@pm.me")
+                    }
+                }
+                scm {
+                    connection.set("scm:git:git://github.com/4tkbytes/dropbear.git")
+                    developerConnection.set("scm:git:ssh://github.com/4tkbytes/dropbear.git")
+                    url.set("https://github.com/4tkbytes/dropbear")
+                }
+            }
+        }
+    }
+
+    repositories {
+        maven {
+            name = "GitHubPages"
+            url = uri("${layout.buildDirectory}/repo")
+        }
+    }
+}
\ No newline at end of file
diff --git a/dropbear-gradle-plugin/src/main/kotlin/com/dropbear/gradle/DropbearGradlePlugin.kt b/dropbear-gradle-plugin/src/main/kotlin/com/dropbear/gradle/DropbearGradlePlugin.kt
new file mode 100644
index 0000000..5d0c24c
--- /dev/null
+++ b/dropbear-gradle-plugin/src/main/kotlin/com/dropbear/gradle/DropbearGradlePlugin.kt
@@ -0,0 +1,10 @@
+package com.dropbear.gradle
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+class DropbearGradlePlugin: Plugin<Project> {
+    override fun apply(target: Project) {
+        TODO("Not yet implemented")
+    }
+}
\ No newline at end of file
diff --git a/eucalyptus-core/Cargo.toml b/eucalyptus-core/Cargo.toml
index 429ede5..1e32321 100644
--- a/eucalyptus-core/Cargo.toml
+++ b/eucalyptus-core/Cargo.toml
@@ -12,6 +12,7 @@ crate-type = ["rlib", "dylib"]
 [dependencies]
 dropbear-traits = { path = "../dropbear-traits" }
 dropbear-macro = { path = "../dropbear-macro" }
+magna-carta = { path = "../magna-carta" }
 
 anyhow.workspace = true
 bincode.workspace = true
diff --git a/eucalyptus-core/src/config.rs b/eucalyptus-core/src/config.rs
index 61ceb8d..dd90c81 100644
--- a/eucalyptus-core/src/config.rs
+++ b/eucalyptus-core/src/config.rs
@@ -8,6 +8,7 @@ use ron::ser::PrettyConfig;
 use serde::{Deserialize, Serialize};
 use std::fs;
 use std::path::{Path, PathBuf};
+use crate::runtime::RuntimeSettings;
 
 /// The root config file, responsible for building and other metadata.
 ///
@@ -21,8 +22,13 @@ pub struct ProjectConfig {
     pub date_last_accessed: String,
     #[serde(default)]
     pub dock_layout: Option<DockState<EditorTab>>,
+
     #[serde(default)]
     pub editor_settings: EditorSettings,
+
+    #[serde(default)]
+    pub runtime_settings: RuntimeSettings,
+
     #[serde(default)]
     pub last_opened_scene: Option<String>,
 }
@@ -42,6 +48,7 @@ impl ProjectConfig {
             editor_settings: Default::default(),
             dock_layout: None,
             last_opened_scene: None,
+            runtime_settings: Default::default(),
         };
         let _ = result.load_config_to_memory();
         result
diff --git a/eucalyptus-core/src/runtime.rs b/eucalyptus-core/src/runtime.rs
index 9343baf..99fe08e 100644
--- a/eucalyptus-core/src/runtime.rs
+++ b/eucalyptus-core/src/runtime.rs
@@ -1,27 +1,64 @@
-use crate::scene::SceneConfig;
-use std::collections::HashMap;
-// #[derive(bincode::Decode, bincode::Encode, serde::Serialize, serde::Deserialize, Debug)]
-// pub struct RuntimeData {
-//     #[bincode(with_serde)]
-//     pub project_config: ProjectConfig,
-//     #[bincode(with_serde)]
-//     pub source_config: SourceConfig,
-//     #[bincode(with_serde)]
-//     pub scene_data: Vec<SceneConfig>,
-//     #[bincode(with_serde)]
-//     pub scripts: HashMap<String, String>, // name, script_content
-// }
+use crate::scene::{SceneConfig};
+use crate::states::{PROJECT, SCENES};
 
+/// The settings of a project in its runtime. 
+/// 
+/// This is different to [`SceneSettings`], which contains settings for ONLY
+/// that specific scene. This is for any configurations of the project during its runtime, 
+/// such as initial scene and stuff like that. 
+#[derive(
+    bincode::Decode,
+    bincode::Encode,
+    serde::Serialize,
+    serde::Deserialize,
+    Debug,
+    Clone,
+)]
+pub struct RuntimeSettings {}
+
+impl RuntimeSettings {
+    /// Creates a new [`RuntimeSettings`] config. 
+    pub fn new() -> Self {
+        Self { }
+    }
+}
+
+impl Default for RuntimeSettings {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// The configuration of a packaged eucalyptus project. 
+/// 
+/// Often stored as a single .eupak file, it contains all the scenes and the references of different
+/// resources. 
 #[derive(bincode::Decode, bincode::Encode, serde::Serialize, serde::Deserialize, Debug)]
 pub struct RuntimeProjectConfig {
     #[bincode(with_serde)]
     pub project_name: String,
-    #[bincode(with_serde)]
-    pub scene_map: RuntimeSceneIndex,
-}
 
-#[derive(bincode::Decode, bincode::Encode, serde::Serialize, serde::Deserialize, Debug)]
-pub struct RuntimeSceneIndex {
+    // authoring stuff needs to be added, maybe later
+
+    // versioning stuff too
+
+    #[bincode(with_serde)]
+    pub runtime_settings: RuntimeSettings,
+    
     #[bincode(with_serde)]
-    pub scene_index: HashMap<String, SceneConfig>,
+    pub scenes: Vec<SceneConfig>,
 }
+
+impl RuntimeProjectConfig {
+    /// Creates a [RuntimeProjectConfig] from a loaded [PROJECT] and [SCENES] states. 
+    pub fn from_memory() -> Self {
+        let project = PROJECT.read();
+        let scenes = SCENES.read();
+        
+        Self {
+            project_name: project.project_name.clone(),
+            runtime_settings: project.runtime_settings.clone(),
+            scenes: scenes.to_vec(),
+        }
+    }
+}
\ No newline at end of file
diff --git a/eucalyptus-core/src/scene.rs b/eucalyptus-core/src/scene.rs
index 761913d..d0e7b3c 100644
--- a/eucalyptus-core/src/scene.rs
+++ b/eucalyptus-core/src/scene.rs
@@ -11,7 +11,7 @@ use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform};
 use dropbear_engine::graphics::SharedGraphicsContext;
 use dropbear_engine::lighting::{Light as EngineLight, LightComponent};
 use dropbear_engine::model::Model;
-use dropbear_engine::utils::ResourceReferenceType;
+use dropbear_engine::utils::{ResourceReference, ResourceReferenceType};
 use dropbear_traits::SerializableComponent;
 use dropbear_traits::registry::ComponentRegistry;
 use glam::{DQuat, DVec3};
@@ -60,15 +60,19 @@ impl SceneEntity {
     }
 }
 
+/// The specific settings of a scene.
 #[derive(Default, Debug, Serialize, Deserialize, Clone)]
 pub struct SceneSettings {/* *crickets* */}
 
 impl SceneSettings {
+    /// Creates a new [`SceneSettings`] config. 
     pub fn new() -> Self {
         Self {}
     }
 }
 
+/// Specifies the configuration of a scene, such as its entities, hierarchies and any settings that 
+/// may be necessary. 
 #[derive(Default, Debug, Serialize, Deserialize, Clone)]
 pub struct SceneConfig {
     #[serde(default)]
@@ -127,16 +131,34 @@ impl SceneConfig {
                     return Ok(());
                 }
                 ResourceReferenceType::File(reference) => {
-                    let path = &renderer.handle.resolve()?;
+                    if reference == "euca://internal/dropbear/models/cube" {
+                        log::info!("Loading entity from internal cube reference");
+                        let mut loaded_model = Model::load_from_memory(
+                            graphics.clone(),
+                            include_bytes!("../../resources/models/cube.glb"),
+                            Some(label),
+                        )
+                        .await?;
+
+                        let model = loaded_model.make_mut();
+                        model.path =
+                            ResourceReference::from_euca_uri("euca://internal/dropbear/models/cube")?;
+
+                        loaded_model.refresh_registry();
+
+                        MeshRenderer::from_handle(loaded_model)
+                    } else {
+                        let path = &renderer.handle.resolve()?;
 
-                    log::debug!(
-                        "Path for entity {} is {} from reference {}",
-                        label,
-                        path.display(),
-                        reference
-                    );
+                        log::debug!(
+                            "Path for entity {} is {} from reference {}",
+                            label,
+                            path.display(),
+                            reference
+                        );
 
-                    MeshRenderer::from_path(graphics.clone(), &path, Some(label)).await?
+                        MeshRenderer::from_path(graphics.clone(), &path, Some(label)).await?
+                    }
                 }
                 ResourceReferenceType::Bytes(bytes) => {
                     log::info!("Loading entity from bytes [Len: {}]", bytes.len());
@@ -149,13 +171,19 @@ impl SceneConfig {
                 ResourceReferenceType::Cube => {
                     log::info!("Loading entity from cube");
 
-                    let model = Model::load_from_memory(
+                    let mut loaded_model = Model::load_from_memory(
                         graphics.clone(),
                         include_bytes!("../../resources/models/cube.glb"),
                         Some(label),
                     )
                     .await?;
-                    MeshRenderer::from_handle(model)
+
+                    let model = loaded_model.make_mut();
+                    model.path = ResourceReference::from_euca_uri("euca://internal/dropbear/models/cube")?;
+
+                    loaded_model.refresh_registry();
+
+                    MeshRenderer::from_handle(loaded_model)
                 }
             };
 
diff --git a/eucalyptus-core/src/scripting.rs b/eucalyptus-core/src/scripting.rs
index 4a747c5..69ba43e 100644
--- a/eucalyptus-core/src/scripting.rs
+++ b/eucalyptus-core/src/scripting.rs
@@ -303,6 +303,7 @@ fn get_gradle_command(project_root: impl AsRef<Path>) -> String {
     }
 }
 
+/// Asynchronously builds a project for the JVM using gradle. 
 pub async fn build_jvm(
     project_root: impl AsRef<Path>,
     status_sender: Sender<BuildStatus>,
@@ -420,3 +421,127 @@ pub async fn build_jvm(
     let _ = status_sender.send(BuildStatus::Completed);
     Ok(jar_path)
 }
+
+/// Asynchronously builds a project for Kotlin/Native using gradle.
+pub async fn build_native(
+    project_root: impl AsRef<Path>,
+    status_sender: Sender<BuildStatus>,
+) -> anyhow::Result<PathBuf> {
+    let project_root = project_root.as_ref();
+
+    if !project_root.exists() {
+        let err = format!("Project root does not exist: {:?}", project_root);
+        let _ = status_sender.send(BuildStatus::Failed(err.clone()));
+        return Err(anyhow::anyhow!(err));
+    }
+
+    let _ = status_sender.send(BuildStatus::Building("Copying core library...".to_string()));
+    let libs_dir = project_root.join("libs");
+    if !libs_dir.exists() {
+        std::fs::create_dir_all(&libs_dir).context("Failed to create libs directory")?;
+    }
+
+    let (lib_name, lib_ext) = if cfg!(target_os = "windows") {
+        ("eucalyptus_core", "dll")
+    } else if cfg!(target_os = "macos") {
+        ("libeucalyptus_core", "dylib")
+    } else {
+        ("libeucalyptus_core", "so")
+    };
+
+    let lib_filename = format!("{}.{}", lib_name, lib_ext);
+    
+    let current_exe = std::env::current_exe().context("Failed to get current executable path")?;
+    let exe_dir = current_exe.parent().context("Failed to get executable directory")?;
+    let source_lib_path = exe_dir.join(&lib_filename);
+
+    if source_lib_path.exists() {
+        std::fs::copy(&source_lib_path, libs_dir.join(&lib_filename))
+            .context(format!("Failed to copy {} to libs", lib_filename))?;
+    } else {
+        let cwd_lib_path = std::env::current_dir()?.join(&lib_filename);
+        if cwd_lib_path.exists() {
+             std::fs::copy(&cwd_lib_path, libs_dir.join(&lib_filename))
+                .context(format!("Failed to copy {} to libs", lib_filename))?;
+        } else {
+             let err = format!("Could not find core library {} to copy", lib_filename);
+             let _ = status_sender.send(BuildStatus::Failed(err.clone()));
+             return Err(anyhow::anyhow!(err));
+        }
+    }
+
+    let _ = status_sender.send(BuildStatus::Started);
+
+    let gradle_cmd = get_gradle_command(project_root);
+    let _ = status_sender.send(BuildStatus::Building(format!("Running: {} build", gradle_cmd)));
+
+    let mut child = Command::new(&gradle_cmd)
+        .current_dir(project_root)
+        .args(["--console=plain", "build"])
+        .stdout(std::process::Stdio::piped())
+        .stderr(std::process::Stdio::piped())
+        .spawn()
+        .context(format!("Failed to spawn `{} build`", gradle_cmd))?;
+
+    let stdout = child.stdout.take().expect("Stdout was piped");
+    let stderr = child.stderr.take().expect("Stderr was piped");
+
+    let tx_out = status_sender.clone();
+    let stdout_task = tokio::spawn(async move {
+        let mut reader = BufReader::new(stdout).lines();
+        while let Ok(Some(line)) = reader.next_line().await {
+            let _ = tx_out.send(BuildStatus::Building(line));
+        }
+    });
+
+    let tx_err = status_sender.clone();
+    let stderr_task = tokio::spawn(async move {
+        let mut reader = BufReader::new(stderr).lines();
+        while let Ok(Some(line)) = reader.next_line().await {
+            let _ = tx_err.send(BuildStatus::Building(line));
+        }
+    });
+
+    let status = child
+        .wait()
+        .await
+        .context("Failed to wait for gradle process")?;
+
+    let _ = tokio::join!(stdout_task, stderr_task);
+
+    if !status.success() {
+        let code = status.code().unwrap_or(-1);
+        let err_msg = format!("Gradle build failed with exit code {}", code);
+        let _ = status_sender.send(BuildStatus::Failed(err_msg.clone()));
+        return Err(anyhow::anyhow!(err_msg));
+    }
+
+    let output_dir = project_root.join("build/bin/nativeLib/releaseShared");
+    if !output_dir.exists() {
+        let err = format!("Build succeeded but output directory missing: {:?}", output_dir);
+        let _ = status_sender.send(BuildStatus::Failed(err.clone()));
+        return Err(anyhow::anyhow!(err));
+    }
+
+    let mut found_lib = None;
+    if let Ok(entries) = std::fs::read_dir(&output_dir) {
+        for entry in entries.flatten() {
+            let path = entry.path();
+            if let Some(ext) = path.extension() {
+                if ext == lib_ext {
+                    found_lib = Some(path);
+                    break;
+                }
+            }
+        }
+    }
+
+    if let Some(lib_path) = found_lib {
+        let _ = status_sender.send(BuildStatus::Completed);
+        Ok(lib_path)
+    } else {
+        let err = format!("No .{} file found in {:?}", lib_ext, output_dir);
+        let _ = status_sender.send(BuildStatus::Failed(err.clone()));
+        Err(anyhow::anyhow!(err))
+    }
+}
diff --git a/eucalyptus-core/src/states.rs b/eucalyptus-core/src/states.rs
index a8621df..517230d 100644
--- a/eucalyptus-core/src/states.rs
+++ b/eucalyptus-core/src/states.rs
@@ -18,6 +18,7 @@ use std::fmt::{Display, Formatter};
 use std::ops::{Deref, DerefMut};
 use std::path::PathBuf;
 
+/// A global "singleton" that contains the configuration of a project.  
 pub static PROJECT: Lazy<RwLock<ProjectConfig>> =
     Lazy::new(|| RwLock::new(ProjectConfig::default()));
 
diff --git a/eucalyptus-editor/Cargo.toml b/eucalyptus-editor/Cargo.toml
index d6c9258..389d10d 100644
--- a/eucalyptus-editor/Cargo.toml
+++ b/eucalyptus-editor/Cargo.toml
@@ -46,8 +46,7 @@ env_logger.workspace = true
 colored.workspace = true
 chrono.workspace = true
 egui_ltreeview.workspace = true
-
-[target.'cfg(not(target_os = "android"))'.dependencies]
+ron.workspace = true
 rfd.workspace = true
 
 [features]
diff --git a/eucalyptus-editor/src/build.rs b/eucalyptus-editor/src/build.rs
index d04bf84..1ffd9b4 100644
--- a/eucalyptus-editor/src/build.rs
+++ b/eucalyptus-editor/src/build.rs
@@ -1,521 +1,101 @@
-use clap::ArgMatches;
 use eucalyptus_core::config::ProjectConfig;
-use eucalyptus_core::states::{RuntimeData, SCENES, SOURCE};
-use std::path::Path;
-use std::{collections::HashMap, fs, path::PathBuf, process::Command};
-use zip::write::SimpleFileOptions;
-
-pub fn package(project_path: PathBuf, _sub_matches: &ArgMatches) -> anyhow::Result<()> {
-    if !project_path.exists() {
-        return Err(anyhow::anyhow!("Unable to locate project config file"));
-    }
-
-    let build_dir = project_path
-        .parent()
-        .ok_or(anyhow::anyhow!("Unable to get parent"))?
-        .join("build");
-
-    // check health
-    println!("Checking health (checking if commands exist)");
-    health()?;
-    println!("Health check completed!");
-
-    let clone_dir = build_dir.join("redback-runtime");
-
-    if clone_dir.exists() {
-        println!("Repository directory exists, checking for updates...");
-        if should_update_repository(&clone_dir)? {
-            println!("Repository has changes or is outdated, removing and re-cloning...");
-            std::fs::remove_dir_all(&clone_dir)?;
-            clone_repository(&build_dir)?;
-        } else {
-            println!("Repository is up to date, skipping clone");
-        }
-    } else {
-        println!("Cloning repository");
-        clone_repository(&build_dir)?;
-    }
-
-    let project_config = ProjectConfig::read_from(&project_path)?;
-    let project_name = project_config.project_name.clone();
-
-    // cd into redback-runtime folder and compile redback-runtime using cargo
-    let runtime_dir = build_dir.join("redback-runtime");
-    if !runtime_dir.exists() {
-        return Err(anyhow::anyhow!(
-            "redback-runtime directory not found after cloning"
-        ));
-    }
-
-    let cargo_toml_path = runtime_dir.join("Cargo.toml");
-    if cargo_toml_path.exists() {
-        let cargo_toml_content = std::fs::read_to_string(&cargo_toml_path)?;
-        let modified_content = cargo_toml_content.replace(
-            r#"name = "redback-runtime""#,
-            &format!(r#"name = "{}""#, project_name),
-        );
-        std::fs::write(&cargo_toml_path, modified_content)?;
-        println!("Updated Cargo.toml with project name: {}", project_name);
-    }
-
-    println!("Building {} for release", project_name);
-    let mut cargo_build = Command::new("cargo")
-        .args(["build", "--release"])
-        .current_dir(&runtime_dir)
-        .stdout(std::process::Stdio::inherit())
-        .stderr(std::process::Stdio::inherit())
-        .spawn()?;
-
-    let exit_status = cargo_build.wait()?;
-
-    if !exit_status.success() {
-        return Err(anyhow::anyhow!("Failed to build {}", project_name));
-    }
-    println!("{} built successfully!", project_name);
-
-    let target_dir = runtime_dir.join("target").join("release");
-    let exe_name = if cfg!(target_os = "windows") {
-        format!("{}.exe", project_name)
-    } else {
-        project_name.clone()
-    };
-
-    let built_exe = target_dir.join(&exe_name);
-    if !built_exe.exists() {
-        return Err(anyhow::anyhow!(
-            "Built executable not found at: {}",
-            built_exe.display()
-        ));
-    }
-
-    println!("Building project data (.eupak file)");
-    build(project_path.clone())?;
-
-    let output_dir = project_path
-        .parent()
-        .ok_or(anyhow::anyhow!("Unable to get parent"))?
-        .join("build")
-        .join("package");
-    std::fs::create_dir_all(&output_dir)?;
-
-    let output_exe = output_dir.join(&exe_name);
-
-    println!("Copying executable to: {}", output_exe.display());
-    std::fs::copy(&built_exe, &output_exe)?;
-
-    let eupak_source = project_path
+use eucalyptus_core::runtime::RuntimeProjectConfig;
+use eucalyptus_core::scene::SceneConfig;
+use std::fs;
+use std::path::{Path, PathBuf};
+
+/// Builds a eucalyptus project into a single bundle. 
+/// 
+/// Returns the path of the build directory
+pub fn build(project_config: PathBuf) -> anyhow::Result<PathBuf> {
+    log::info!("Started project building");
+    // create a build directory
+    let project_root = project_config
         .parent()
-        .ok_or(anyhow::anyhow!("Unable to get parent"))?
-        .join("build")
-        .join("output")
-        .join(format!("{}.eupak", project_name));
-    let eupak_dest = output_dir.join(format!("{}.eupak", project_name));
-
-    if !eupak_source.exists() {
-        return Err(anyhow::anyhow!(
-            "Expected .eupak file not found at: {}",
-            eupak_source.display()
-        ));
-    }
-
-    println!("Copying .eupak file to: {}", eupak_dest.display());
-    std::fs::copy(&eupak_source, &eupak_dest)?;
-
-    let project_resources = project_path
-        .parent()
-        .ok_or(anyhow::anyhow!("Unable to get parent"))?
-        .join("resources");
-    if project_resources.exists() {
-        println!("Copying resources folder...");
-        let output_resources = output_dir.join("resources");
-        copy_resources_folder(&project_resources, &output_resources)?;
-    }
-
-    #[cfg(unix)]
-    {
-        use std::os::unix::fs::PermissionsExt;
-        let mut perms = std::fs::metadata(&output_exe)?.permissions();
-        perms.set_mode(0o755); // rwxr-xr-x
-        std::fs::set_permissions(&output_exe, perms)?;
-    }
-
-    copy_system_libraries(&output_dir)?;
-
-    println!("Creating zip package...");
-    let zip_path = project_path
-        .parent()
-        .ok_or(anyhow::anyhow!("Unable to get parent"))?
-        .join("build")
-        .join(format!("{}.zip", project_name));
-    create_zip_package(&output_dir, &zip_path)?;
-
-    println!("Cleaning up temporary files");
-    if build_dir.join("redback-runtime").exists() {
-        std::fs::remove_dir_all(build_dir.join("redback-runtime"))?;
-    }
-    if build_dir.join("output").exists() {
-        std::fs::remove_dir_all(build_dir.join("output"))?;
-    }
-
-    println!("\n✓ Package completed successfully!");
-    println!("Output directory: {}", output_dir.display());
-    println!("Zip package: {}", zip_path.display());
-    println!("Executable: {}", exe_name);
-    println!("Data file: {}.eupak", project_name);
-
-    Ok(())
-}
-
-fn clone_repository(build_dir: impl AsRef<Path>) -> anyhow::Result<()> {
-    git2::build::RepoBuilder::new().clone(
-        "https://github.com/4tkbytes/redback-runtime",
-        &build_dir.as_ref().join("redback-runtime"),
-    )?;
-    println!("Repository cloned successfully!");
-    Ok(())
-}
-
-fn should_update_repository(repo_dir: impl AsRef<Path>) -> anyhow::Result<bool> {
-    let repo = match git2::Repository::open(repo_dir) {
-        Ok(repo) => repo,
-        Err(_) => {
-            return Ok(true);
-        }
-    };
-
-    let statuses = repo.statuses(None)?;
-    if !statuses.is_empty() {
-        println!("Found local changes in repository");
-        return Ok(true);
-    }
-
-    let mut remote = repo.find_remote("origin")?;
-    remote.fetch(&["refs/heads/*:refs/remotes/origin/*"], None, None)?;
-
-    let head = repo
-        .head()?
-        .target()
-        .ok_or(anyhow::anyhow!("No HEAD commit"))?;
-
-    let remote_ref = if let Ok(remote_main) = repo.find_reference("refs/remotes/origin/main") {
-        remote_main
-    } else if let Ok(remote_master) = repo.find_reference("refs/remotes/origin/master") {
-        remote_master
-    } else {
-        return Ok(true);
-    };
-
-    let remote_commit = remote_ref
-        .target()
-        .ok_or(anyhow::anyhow!("No remote commit"))?;
-
-    Ok(head != remote_commit)
-}
-
-fn copy_resources_folder(src: impl AsRef<Path>, dest: impl AsRef<Path>) -> anyhow::Result<()> {
-    fs::create_dir_all(dest.as_ref())?;
-
-    for entry in fs::read_dir(src)? {
-        let entry = entry?;
-        let src_path = entry.path();
-        let file_name = entry.file_name();
-
-        if file_name == "resources.eucc" {
-            continue;
-        }
-
-        let dest_path = dest.as_ref().join(&file_name);
-
-        if src_path.is_dir() {
-            copy_resources_folder(&src_path, &dest_path)?;
-        } else {
-            std::fs::copy(&src_path, &dest_path)?;
-        }
-    }
-
-    Ok(())
-}
-
-fn copy_system_libraries(output_dir: impl AsRef<Path>) -> anyhow::Result<()> {
-    #[cfg(target_os = "windows")]
-    {
-        let dll_paths = vec![
-            "C:\\vcpkg\\installed\\x64-windows\\bin\\assimp-vc143-mt.dll",
-            "C:\\vcpkg\\installed\\x64-windows\\bin\\assimp.dll",
-            "C:\\Program Files\\Assimp\\bin\\assimp.dll",
-            "C:\\Program Files (x86)\\Assimp\\bin\\assimp.dll",
-        ];
-
-        for dll_path in dll_paths {
-            if std::path::Path::new(dll_path).exists() {
-                let dll_name = std::path::Path::new(dll_path).file_name().unwrap();
-                let dest = output_dir.as_ref().join(dll_name);
-                std::fs::copy(dll_path, dest)?;
-                println!("Copied system library: {}", dll_name.to_string_lossy());
-                break;
-            }
-        }
-    }
-
-    #[cfg(any(target_os = "linux", target_os = "macos"))]
-    {
-        let lib_dir = output_dir.as_ref().join("lib");
-        std::fs::create_dir_all(&lib_dir)?;
-
-        let lib_paths = vec![
-            "/usr/lib/libassimp.so",
-            "/usr/lib/x86_64-linux-gnu/libassimp.so",
-            "/usr/lib64/libassimp.so",
-            "/usr/local/lib/libassimp.so",
-            "/opt/homebrew/lib/libassimp.dylib",
-            "/usr/local/lib/libassimp.dylib",
-        ];
-
-        for lib_path in lib_paths {
-            if std::path::Path::new(lib_path).exists() {
-                let lib_name = std::path::Path::new(lib_path).file_name().unwrap();
-                let dest = lib_dir.join(lib_name);
-                std::fs::copy(lib_path, dest)?;
-                println!("Copied system library: {}", lib_name.to_string_lossy());
-                break;
-            }
-        }
-    }
-
-    Ok(())
-}
-
-fn create_zip_package(
-    source_dir: impl AsRef<Path>,
-    zip_path: impl AsRef<Path>,
-) -> anyhow::Result<()> {
-    let file = fs::File::create(zip_path.as_ref())?;
-    let mut zip = zip::ZipWriter::new(file);
-
-    let walkdir = walkdir::WalkDir::new(source_dir.as_ref());
-    for entry in walkdir {
-        let entry = entry?;
-        let path = entry.path();
-
-        if path.is_file() {
-            let relative_path = path.strip_prefix(source_dir.as_ref())?;
-            let name = relative_path.to_string_lossy();
+        .ok_or(anyhow::anyhow!("Unable to locate parent folder of config"))?
+        .to_path_buf();
+    let build_dir = project_root.join("build/output");
 
-            let options: SimpleFileOptions = Default::default();
-            zip.start_file(name, options)?;
-            let mut file = std::fs::File::open(path)?;
-            std::io::copy(&mut file, &mut zip)?;
-        }
-    }
-
-    zip.finish()?;
-    println!("Created zip package: {}", zip_path.as_ref().display());
-    Ok(())
-}
-
-pub fn read_from_eupak(eupak_path: PathBuf) -> anyhow::Result<()> {
-    let bytes = std::fs::read(&eupak_path)?;
-    let (content, _): (RuntimeData, usize) =
-        bincode::decode_from_slice(&bytes, bincode::config::standard())?;
-    println!("{} contents: {:#?}", eupak_path.display(), content);
-    Ok(())
-}
-
-pub fn build(
-    project_path: PathBuf,
-    // _sub_matches: &ArgMatches
-) -> anyhow::Result<PathBuf> {
-    println!(" > Starting build");
-    if !project_path.exists() {
-        return Err(anyhow::anyhow!(format!(
-            "Unable to locate project config file: [{}]",
-            project_path.display()
-        )));
+    if build_dir.exists() {
+        fs::remove_dir_all(&build_dir)?;
     }
-    // ProjectConfig::read_from(&project_path)?.load_config_to_memory()?;
-
-    let mut project_config = ProjectConfig::read_from(&project_path)?;
-    log::info!(" > Reading from project config");
-    project_config.load_config_to_memory()?;
-    log::info!(" > Loading config to memory");
-
-    let scene_data = {
-        let scenes_guard = SCENES.read();
-        scenes_guard.clone()
-    };
-    log::info!(" > Copied scene data");
-
-    let source_config = {
-        let source_guard = SOURCE.read();
-        source_guard.clone()
-    };
-    log::info!(" > Captured source config");
-
-    let build_dir = project_path.parent().unwrap().join("build").join("output");
     fs::create_dir_all(&build_dir)?;
-    log::info!(" > Created build dir");
-
-    let project_name = project_config.project_name.clone();
-
-    let mut scripts = HashMap::new();
-    let script_dir = project_path.parent().unwrap().join("src");
-    if script_dir.exists() {
-        for entry in fs::read_dir(&script_dir)? {
+    log::debug!("Readied build directory");
+
+    // load the project config manually to avoid overwriting global state
+    let ron_str = fs::read_to_string(&project_config)?;
+    let mut config: ProjectConfig = ron::de::from_str(&ron_str)?;
+    config.project_path = project_root.clone();
+    log::debug!("Loaded project config");
+
+    // load scenes
+    let mut scenes = Vec::new();
+    let scene_folder = project_root.join("scenes");
+    if scene_folder.exists() {
+        for entry in fs::read_dir(scene_folder)? {
             let entry = entry?;
             let path = entry.path();
-            if let Some(ext) = path.extension()
-                && ext == "rhai"
-            {
-                let name = path.file_name().unwrap().to_string_lossy().to_string();
-                let contents = fs::read_to_string(&path)?;
-                println!(" > Copied script info from [{}]", name);
-                scripts.insert(name, contents);
-            }
-        }
-    }
-
-    let runtime_data = RuntimeData {
-        project_config,
-        source_config,
-        scene_data,
-        scripts,
-    };
-    log::info!(" > Created runtime data structures");
-
-    let runtime_file = build_dir.join(format!("{}.eupak", project_name));
-    let serialized = bincode::serde::encode_to_vec(runtime_data, bincode::config::standard())?;
-    std::fs::write(&runtime_file, serialized)?;
-    log::info!(" > Written the file to build location");
-
-    println!(
-        "Build completed successfully. Output at {:?}",
-        runtime_file.display()
-    );
-    Ok(runtime_file)
-}
-
-pub fn health() -> anyhow::Result<()> {
-    let mut all_healthy = true;
-
-    match Command::new("cargo").arg("--version").output() {
-        Ok(output) => {
-            if output.status.success() {
-                let version = String::from_utf8_lossy(&output.stdout).trim().to_string();
-                println!("Does cargo exist? ✓ YES - {}", version);
-
-                match Command::new("rustc").arg("--version").output() {
-                    Ok(rustc_output) => {
-                        if rustc_output.status.success() {
-                            let rustc_version = String::from_utf8_lossy(&rustc_output.stdout)
-                                .trim()
-                                .to_string();
-                            println!("Does rustc compiler exist? ✓ YES - {}", rustc_version);
-                        } else {
-                            println!("Does rustc compiler exist? ✗ NO - rustc command failed");
-                            all_healthy = false;
-                        }
+            if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("eucs") {
+                match SceneConfig::read_from(&path) {
+                    Ok(scene) => {
+                        scenes.push(scene);
                     }
-                    Err(_) => {
-                        println!("Does rustc compiler exist? ✗ NO - rustc not found in PATH");
-                        all_healthy = false;
+                    Err(e) => {
+                        log::warn!("Failed to load scene {:?} during build: {}", path, e);
                     }
                 }
-            } else {
-                println!("Does cargo exist? ✗ NO - cargo command failed");
-                println!("Does rustc compiler exist? ✗ NO - cargo failed, cannot check rustc");
-                all_healthy = false;
             }
         }
-        Err(_) => {
-            println!("Does cargo exist? ✗ NO - cargo not found in PATH");
-            println!("Does rustc compiler exist? ✗ NO - cargo not found, cannot check rustc");
-            all_healthy = false;
-        }
-    }
-
-    let assimp_found = check_assimp_availability();
-    if assimp_found {
-        println!("Does assimp lib exist? ✓ YES - Found assimp library");
-    } else {
-        println!(
-            "Does assimp lib exist? ⚠ MAYBE - Could not definitively locate assimp, but it may be available through system package manager or vcpkg"
-        );
-    }
-
-    if all_healthy {
-        println!("\n✓ All core tools are available!");
-    } else {
-        println!("\n✗ Some required tools are missing. Please install Rust and Cargo.");
-        return Err(anyhow::anyhow!(
-            "Health check failed - missing required tools"
-        ));
     }
 
-    Ok(())
-}
-
-fn check_assimp_availability() -> bool {
-    #[cfg(target_os = "windows")]
-    {
-        let common_paths = vec![
-            "C:\\vcpkg\\installed\\x64-windows\\lib\\assimp-vc143-mt.lib",
-            "C:\\vcpkg\\installed\\x64-windows\\lib\\assimp.lib",
-            "C:\\vcpkg\\installed\\x86-windows\\lib\\assimp-vc143-mt.lib",
-            "C:\\vcpkg\\installed\\x86-windows\\lib\\assimp.lib",
-            "C:\\Program Files\\Assimp\\lib\\assimp.lib",
-            "C:\\Program Files (x86)\\Assimp\\lib\\assimp.lib",
-        ];
-
-        for path in common_paths {
-            if std::path::Path::new(path).exists() {
-                return true;
-            }
-        }
+    // convert to runtime project config
+    let runtime_config = RuntimeProjectConfig {
+        project_name: config.project_name.clone(),
+        runtime_settings: config.runtime_settings.clone(),
+        scenes,
+    };
+    log::debug!("Converted to runtime project config");
 
-        if let Ok(output) = Command::new("where").arg("assimp.dll").output()
-            && output.status.success()
-        {
-            return true;
-        }
+    // export to .eupak
+    let eupak_path = build_dir.join("data.eupak");
+    let config_bytes = bincode::encode_to_vec(&runtime_config, bincode::config::standard())?;
+    fs::write(&eupak_path, config_bytes)?;
+    log::debug!("Exported scene config to {:?}", eupak_path);
+    
+    // copy resources
+    let resources_src = project_root.join("resources");
+    let resources_dst = build_dir.join("resources");
+    if resources_src.exists() {
+        copy_dir_recursive(&resources_src, &resources_dst)?;
+        log::debug!("Copied resources to {:?}", resources_dst);
     }
 
-    #[cfg(any(target_os = "linux", target_os = "macos"))]
-    {
-        let common_paths = vec![
-            "/usr/lib/libassimp.so",
-            "/usr/lib/x86_64-linux-gnu/libassimp.so",
-            "/usr/lib64/libassimp.so",
-            "/usr/local/lib/libassimp.so",
-            "/opt/homebrew/lib/libassimp.dylib",
-            "/usr/local/lib/libassimp.dylib",
-        ];
+    log::info!("Done!");
 
-        for path in common_paths {
-            if std::path::Path::new(path).exists() {
-                return true;
-            }
-        }
-
-        if let Ok(output) = Command::new("pkg-config")
-            .args(["--exists", "assimp"])
-            .output()
-            && output.status.success()
-        {
-            return true;
-        }
+    Ok(build_dir)
+}
 
-        #[cfg(target_os = "linux")]
-        {
-            if let Ok(output) = Command::new("ldconfig").args(["-p"]).output()
-                && output.status.success()
-            {
-                let output_str = String::from_utf8_lossy(&output.stdout);
-                if output_str.contains("libassimp") {
-                    return true;
-                }
-            }
+fn copy_dir_recursive(src: &Path, dst: &Path) -> anyhow::Result<()> {
+    fs::create_dir_all(dst)?;
+    for entry in fs::read_dir(src)? {
+        let entry = entry?;
+        let ty = entry.file_type()?;
+        if ty.is_dir() {
+            copy_dir_recursive(&entry.path(), &dst.join(entry.file_name()))?;
+        } else {
+            fs::copy(entry.path(), dst.join(entry.file_name()))?;
         }
     }
-
-    false
+    Ok(())
 }
+
+/// Reads the contents of a data.eupak file into a pretty print format. 
+/// 
+/// Returns the contents of the project config. 
+pub fn read(eupak: PathBuf) -> anyhow::Result<RuntimeProjectConfig> {
+    let bytes = std::fs::read(&eupak)?;
+    let (content, _): (RuntimeProjectConfig, usize) =
+        bincode::decode_from_slice(&bytes, bincode::config::standard())?;
+    println!("{} contents: {:#?}", eupak.display(), content);
+    Ok(content)
+}
\ No newline at end of file
diff --git a/eucalyptus-editor/src/editor/component.rs b/eucalyptus-editor/src/editor/component.rs
index fae6018..b5d2645 100644
--- a/eucalyptus-editor/src/editor/component.rs
+++ b/eucalyptus-editor/src/editor/component.rs
@@ -760,8 +760,16 @@ impl InspectableComponent for MeshRenderer {
             CollapsingHeader::new("Model").show(ui, |ui| {
                 let mut selected_model: Option<AssetHandle> = None;
 
+                let selected_text = if let Some(uri) = self.handle().path.as_uri()
+                    && uri == "euca://internal/dropbear/models/cube"
+                {
+                    "Cube".to_string()
+                } else {
+                    self.handle().label.clone()
+                };
+
                 ComboBox::from_id_salt("model_dropdown")
-                    .selected_text(format!("{}", self.handle().label))
+                    .selected_text(selected_text)
                     .width(ui.available_width())
                     .show_ui(ui, |ui| {
                         let iter = ASSET_REGISTRY.iter_model();
diff --git a/eucalyptus-editor/src/editor/dock.rs b/eucalyptus-editor/src/editor/dock.rs
index ca29645..88604b8 100644
--- a/eucalyptus-editor/src/editor/dock.rs
+++ b/eucalyptus-editor/src/editor/dock.rs
@@ -989,10 +989,31 @@ impl<'a> EditorTabViewer<'a> {
             Self::build_resource_branch(builder, &project_root);
             Self::build_scripts_branch(builder, &project_root);
             Self::build_scene_branch(builder, &project_root);
+            Self::build_internal_models_branch(builder);
             builder.close_dir();
         });
     }
 
+    fn build_internal_models_branch(builder: &mut TreeViewBuilder<u64>) {
+        let label = "euca://internal";
+        builder.node(Self::dir_node_labeled(label, "internal"));
+
+        let dropbear_label = "euca://internal/dropbear";
+        builder.node(Self::dir_node_labeled(dropbear_label, "dropbear"));
+
+        let models_label = "euca://internal/dropbear/models";
+        builder.node(Self::dir_node_labeled(models_label, "models"));
+
+        for model_name in dropbear_engine::utils::INTERNAL_MODELS {
+            let model_uri = format!("euca://internal/dropbear/models/{}", model_name);
+            builder.node(Self::leaf_node_labeled(&model_uri, model_name));
+        }
+
+        builder.close_dir(); // close models
+        builder.close_dir(); // close dropbear
+        builder.close_dir(); // close internal
+    }
+
     fn build_resource_branch(builder: &mut TreeViewBuilder<u64>, project_root: &Path) {
         let label = "euca://resources";
         builder.node(Self::dir_node_labeled(label, "resources"));
diff --git a/eucalyptus-editor/src/main.rs b/eucalyptus-editor/src/main.rs
index c96bd03..89389da 100644
--- a/eucalyptus-editor/src/main.rs
+++ b/eucalyptus-editor/src/main.rs
@@ -115,34 +115,15 @@ async fn main() -> anyhow::Result<()> {
                 ),
         )
         .subcommand(
-            Command::new("package")
-                .about("Package a eucalyptus project, which compiles the runtime and the resource .eupak file")
+            Command::new("read")
+                .about("Reads a .eupak file")
                 .arg(
-                    Arg::new("project")
-                        .help("Path to the .eucp project file")
-                        .value_name("PROJECT_FILE")
-                        .required(true),
-                ),
-        )
-        .subcommand(Command::new("read")
-            .about("Reads and displays the contents of a .eupak file for debugging")
-            .arg(
                     Arg::new("eupak_file")
-                        .help("Path to the .eupak file")
-                        .value_name("RESOURCE_FILE")
-                        .required(true),
+                        .help("Path to the .eupak data file")
+                        .value_name("EUPAK_FILE")
+                        .required(true)
                 ),
         )
-        .subcommand(Command::new("health").about("Check the health of the eucalyptus installation"))
-        .subcommand(Command::new("compile")
-            .about("Compiles a project's script into WebAssembly, primarily used for testing")
-            .arg(
-                Arg::new("project")
-                    .help("Path to the .eucp project file")
-                    .value_name("PROJECT_FILE")
-                    .required(true),
-            )
-        )
         .get_matches();
 
     match matches.subcommand() {
@@ -152,7 +133,7 @@ async fn main() -> anyhow::Result<()> {
                 None => match find_eucp_file() {
                     Ok(path) => path,
                     Err(e) => {
-                        eprintln!("Error: {}", e);
+                        log::error!("Error: {}", e);
                         std::process::exit(1);
                     }
                 },
@@ -160,51 +141,13 @@ async fn main() -> anyhow::Result<()> {
 
             build::build(project_path)?;
         }
-        Some(("package", sub_matches)) => {
-            let project_path = match sub_matches.get_one::<String>("project") {
-                Some(path) => PathBuf::from(path),
-                None => match find_eucp_file() {
-                    Ok(path) => path,
-                    Err(e) => {
-                        eprintln!("Error: {}", e);
-                        std::process::exit(1);
-                    }
-                },
-            };
-
-            build::package(project_path, sub_matches)?;
-        }
-        Some(("health", _)) => {
-            build::health()?;
-        }
         Some(("read", sub_matches)) => {
-            let project_path = match sub_matches.get_one::<String>("eupak_file") {
-                Some(path) => PathBuf::from(path),
-                None => match find_eucp_file() {
-                    Ok(path) => path,
-                    Err(e) => {
-                        eprintln!("Error: {}", e);
-                        std::process::exit(1);
-                    }
-                },
-            };
-
-            crate::build::read_from_eupak(project_path)?;
-        }
-        Some(("compile", sub_matches)) => {
-            let _project_path = match sub_matches.get_one::<String>("project") {
+            let eupak = match sub_matches.get_one::<String>("eupak_file") {
                 Some(path) => PathBuf::from(path),
-                None => match find_eucp_file() {
-                    Ok(path) => path,
-                    Err(e) => {
-                        eprintln!("Error: {}", e);
-                        std::process::exit(1);
-                    }
-                },
+                None => {log::error!("Eupak file returned none"); std::process::exit(1)},
             };
 
-            println!("\"Compile\" command not implemented yet");
-            // crate::build::compile(project_path).await?;
+            build::read(eupak)?;
         }
         None => {
             let config = WindowConfiguration {
diff --git a/eucalyptus-editor/src/signal.rs b/eucalyptus-editor/src/signal.rs
index aa4ac57..e9bb78a 100644
--- a/eucalyptus-editor/src/signal.rs
+++ b/eucalyptus-editor/src/signal.rs
@@ -625,12 +625,22 @@ impl SignalController for Editor {
                 if component_name == "MeshRenderer" {
                     let graphics_clone = graphics.clone();
                     let future = async move {
-                        let model= dropbear_engine::model::Model::load_from_memory(
+                        let mut loaded_model = dropbear_engine::model::Model::load_from_memory(
                             graphics_clone.clone(),
                             include_bytes!("../../resources/models/cube.glb"),
                             Some("Cube"),
-                        ).await?;
-                        Ok::<MeshRenderer, anyhow::Error>(dropbear_engine::entity::MeshRenderer::from_handle(model))
+                        )
+                        .await?;
+
+                        let model = loaded_model.make_mut();
+                        model.path =
+                            ResourceReference::from_euca_uri("euca://internal/dropbear/models/cube")?;
+
+                        loaded_model.refresh_registry();
+
+                        Ok::<MeshRenderer, anyhow::Error>(
+                            dropbear_engine::entity::MeshRenderer::from_handle(loaded_model),
+                        )
                     };
                     let handle = graphics.future_queue.push(Box::pin(future));
                     self.pending_components.push((*entity, handle));
@@ -680,21 +690,42 @@ impl SignalController for Editor {
                 let graphics_clone = graphics.clone();
                 let uri_clone = uri.clone();
                 let future = async move {
-                    let path = if uri_clone.starts_with("euca://") {
-                        let path_str = uri_clone.trim_start_matches("euca://");
-                        let project_path = PROJECT.read().project_path.clone();
-                        project_path.join(path_str)
+                    if uri_clone == "euca://internal/dropbear/models/cube" {
+                        let mut loaded_model = dropbear_engine::model::Model::load_from_memory(
+                            graphics_clone,
+                            include_bytes!("../../resources/models/cube.glb"),
+                            Some("Cube"),
+                        )
+                        .await?;
+
+                        let model = loaded_model.make_mut();
+                        model.path = ResourceReference::from_euca_uri(&uri_clone)?;
+
+                        loaded_model.refresh_registry();
+
+                        Ok::<MeshRenderer, anyhow::Error>(
+                            dropbear_engine::entity::MeshRenderer::from_handle(loaded_model),
+                        )
                     } else {
-                        PathBuf::from(&uri_clone)
-                    };
-                    
-                    let model = dropbear_engine::model::Model::load(
-                        graphics_clone,
-                        &path,
-                        Some(&uri_clone)
-                    ).await?;
-                    
-                    Ok::<MeshRenderer, anyhow::Error>(dropbear_engine::entity::MeshRenderer::from_handle(model))
+                        let path = if uri_clone.starts_with("euca://") {
+                            let path_str = uri_clone.trim_start_matches("euca://");
+                            let project_path = PROJECT.read().project_path.clone();
+                            project_path.join(path_str)
+                        } else {
+                            PathBuf::from(&uri_clone)
+                        };
+
+                        let model = dropbear_engine::model::Model::load(
+                            graphics_clone,
+                            &path,
+                            Some(&uri_clone),
+                        )
+                        .await?;
+
+                        Ok::<MeshRenderer, anyhow::Error>(
+                            dropbear_engine::entity::MeshRenderer::from_handle(model),
+                        )
+                    }
                 };
                 
                 let handle = graphics.future_queue.push(Box::pin(future));
diff --git a/eucalyptus-editor/src/spawn.rs b/eucalyptus-editor/src/spawn.rs
index 13af8b4..e93dcfa 100644
--- a/eucalyptus-editor/src/spawn.rs
+++ b/eucalyptus-editor/src/spawn.rs
@@ -6,7 +6,7 @@ use dropbear_engine::future::FutureQueue;
 use dropbear_engine::graphics::SharedGraphicsContext;
 use dropbear_engine::lighting::{Light, LightComponent};
 use dropbear_engine::model::Model;
-use dropbear_engine::utils::ResourceReferenceType;
+use dropbear_engine::utils::{ResourceReference, ResourceReferenceType};
 use eucalyptus_core::camera::CameraComponent;
 use eucalyptus_core::scene::SceneEntity;
 pub(crate) use eucalyptus_core::spawn::{PENDING_SPAWNS, PendingSpawnController};
@@ -218,9 +218,25 @@ async fn load_renderer_from_serialized(
             "Renderer for '{}' does not specify an asset reference",
             label
         ),
-        ResourceReferenceType::File(_) => {
-            let path = renderer.handle.resolve()?;
-            MeshRenderer::from_path(graphics.clone(), &path, Some(&label)).await?
+        ResourceReferenceType::File(reference) => {
+            if reference == "euca://internal/dropbear/models/cube" {
+                let mut loaded_model = Model::load_from_memory(
+                    graphics.clone(),
+                    include_bytes!("../../resources/models/cube.glb"),
+                    Some(&label),
+                )
+                .await?;
+
+                let model = loaded_model.make_mut();
+                model.path = ResourceReference::from_euca_uri("euca://internal/dropbear/models/cube")?;
+
+                loaded_model.refresh_registry();
+
+                MeshRenderer::from_handle(loaded_model)
+            } else {
+                let path = renderer.handle.resolve()?;
+                MeshRenderer::from_path(graphics.clone(), &path, Some(&label)).await?
+            }
         }
         ResourceReferenceType::Bytes(bytes) => {
             let model =
@@ -231,13 +247,19 @@ async fn load_renderer_from_serialized(
             anyhow::bail!("Procedural planes are not supported in pending spawns yet");
         }
         ResourceReferenceType::Cube => {
-            let model = Model::load_from_memory(
+            let mut loaded_model = Model::load_from_memory(
                 graphics.clone(),
                 include_bytes!("../../resources/models/cube.glb"),
                 Some(&label),
             )
             .await?;
-            MeshRenderer::from_handle(model)
+
+            let model = loaded_model.make_mut();
+            model.path = ResourceReference::from_euca_uri("euca://internal/dropbear/models/cube")?;
+
+            loaded_model.refresh_registry();
+
+            MeshRenderer::from_handle(loaded_model)
         }
     };
 
diff --git a/redback-runtime/src/main.rs b/redback-runtime/src/main.rs
index 7f755fb..5e753ff 100644
--- a/redback-runtime/src/main.rs
+++ b/redback-runtime/src/main.rs
@@ -1,2 +1,4 @@
 #[tokio::main]
-async fn main() {}
+async fn main() {
+
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index b22da72..5a41f17 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -15,4 +15,5 @@ buildCache {
     local {
         isEnabled = true
     }
-}
\ No newline at end of file
+}
+include("dropbear-gradle-plugin")
\ No newline at end of file