Import Oxide engine (Stages 0–10) under MIT license

Full project snapshot migrated to new Gitea remote without history:
engine, editor, physics, script, examples, tests, docs, and assets.
Relicensed from GPLv3 to MIT and updated repo URLs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Homer Simpson
2026-07-05 20:41:02 +02:00
parent 2afb56b329
commit 9eead719b0
157 changed files with 47270 additions and 2 deletions
+134
View File
@@ -0,0 +1,134 @@
//! Renderable scene components: [`MeshRenderer`] and [`PrimitiveShape`].
//!
//! A [`MeshRenderer`] is the component that makes a scene entity show up in the
//! 3D viewport: it pairs a mesh source with a [`Material`]. Stage 4 ships the
//! built-in [`PrimitiveShape`] source (cube/sphere/plane) — lightweight and
//! serializable, so the editor (and later scripts/AI agents) can author what an
//! entity renders. Imported meshes attach later via a mesh-asset handle.
use serde::{Deserialize, Serialize};
use super::{Material, Mesh};
use crate::math::{Aabb, Vec3};
/// A built-in primitive mesh an entity can render.
///
/// This names a shape rather than embedding vertex data, so it stays tiny,
/// serializable, and cheap to edit; the renderer resolves it to a (cached)
/// [`Mesh`]/GPU buffer.
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
Default,
Serialize,
Deserialize,
crate::reflect::ReflectEnum,
)]
pub enum PrimitiveShape {
/// Unit cube centered at the origin.
#[default]
Cube,
/// Unit-radius UV sphere.
Sphere,
/// A 1×1 ground plane on the XZ axes, facing `+Y`.
Plane,
}
impl PrimitiveShape {
/// All shapes, for building caches / editor menus.
pub const ALL: [PrimitiveShape; 3] = [
PrimitiveShape::Cube,
PrimitiveShape::Sphere,
PrimitiveShape::Plane,
];
/// A human-readable label.
pub fn label(self) -> &'static str {
match self {
PrimitiveShape::Cube => "Cube",
PrimitiveShape::Sphere => "Sphere",
PrimitiveShape::Plane => "Plane",
}
}
/// Builds the CPU [`Mesh`] for this shape.
pub fn mesh(self) -> Mesh {
match self {
PrimitiveShape::Cube => Mesh::cube(),
PrimitiveShape::Sphere => Mesh::uv_sphere(1.0, 32, 16),
PrimitiveShape::Plane => Mesh::plane(1.0),
}
}
/// The object-space bounds of this shape, without building a mesh — used for
/// ray-picking and culling.
pub fn local_bounds(self) -> Aabb {
let half = match self {
PrimitiveShape::Cube => Vec3::splat(0.5),
PrimitiveShape::Sphere => Vec3::ONE,
PrimitiveShape::Plane => Vec3::new(0.5, 0.0, 0.5),
};
Aabb::from_center_half_extents(Vec3::ZERO, half)
}
}
/// Component: what an entity renders.
///
/// Attach to a scene entity (via the ECS) to make it appear in a forward pass.
/// Stage 4 sources the mesh from a [`PrimitiveShape`]; the [`Material`] is
/// edited in the inspector. Both are serializable, supporting the engine's
/// dual-editable (editor + script/AI) component goal.
#[derive(
Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, crate::reflect::Reflect,
)]
pub struct MeshRenderer {
/// The mesh to draw.
pub shape: PrimitiveShape,
/// The surface material.
pub material: Material,
}
impl MeshRenderer {
/// A renderer for `shape` with the default material.
pub fn new(shape: PrimitiveShape) -> Self {
Self {
shape,
material: Material::default(),
}
}
/// A renderer for `shape` with an explicit `material`.
pub fn with_material(shape: PrimitiveShape, material: Material) -> Self {
Self { shape, material }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::math::Color;
#[test]
fn every_shape_builds_a_nonempty_mesh() {
for shape in PrimitiveShape::ALL {
let mesh = shape.mesh();
assert!(!mesh.vertices.is_empty(), "{shape:?} has no vertices");
assert!(mesh.triangle_count() > 0, "{shape:?} has no triangles");
}
}
#[test]
fn mesh_renderer_round_trips_through_ron() {
let mr = MeshRenderer::with_material(
PrimitiveShape::Sphere,
Material::metal(Color::rgb(0.2, 0.4, 0.8), 0.25),
);
let ron = ron::to_string(&mr).unwrap();
let back: MeshRenderer = ron::from_str(&ron).unwrap();
assert_eq!(mr, back);
}
}