Scripting files for Arbgeom, August 13, 2024
Arbgeom beta » Devlog
Since I haven't put out an update in a long time, I wanted to show off what the scripting files will look like for Arbgeom. I've attached the current assets file and pasted some of the . I'm still developing right now so some of these files might be a little jank/incomplete/broken. Also it looks like itch.io mangled some of the generic arguments on the geometry description files, so it should look like this: <shape: Proj+Sdf>.
Geometry Descriptions: Structured Scaffolding Language
Language Requirements:
- Needs to be interpreted by rust (for physics) and compiled into wgsl (for rendering) -- CHECK
- Allow for nesting of different objects -- CHECK
- Lightweight -- CHECK
- Fast & Optimized -- CHECK-ish, WIP (much faster than using rhai for this though)
- Hard to do since the optimizations need to be done at the AST level for rust to interpret and to be turned into WGSL which is then compiled.
Basic shapes:
class Plane4D { normal: vec4, Proj::proj(vector: vec4) -> vec4 { vector - normal * dot(vector, normal) }, Sdf::sdf(vector: vec4) -> f32 { dot(vector, normal) }, Aabb::aabb() -> (vec4, vec4) { (-INFINITY * ONES, INFINITY * ONES) } } class Box4D { size: vec4, Proj::proj(vector: vec4) -> vec4 { let q: vec4 = abs(vector) - size; let add_to_proj: vec4 = select( select( select(vec4(0, 0, 0, -q.w), vec4(0, 0, -q.z, 0), q.z > q.w ), vec4(0, -q.y, 0, 0), (q.y > q.z) && (q.y > q.w) ), vec4(-q.x, 0, 0, 0), q.x > q.y && q.x > q.z && q.x > q.w ); select( { let proj: vec4 = abs(vector); proj = proj + add_to_proj; proj * sign(vector) }, min(abs(vector), size) * sign(vector), length(max(q, vec4(0, 0, 0, 0))) > 0.0000001 ) }, Sdf::sdf(vector: vec4) -> f32 { let q: vec4 = abs(vector) - size; let max_q: f32 = max( max(q.x, q.y), max(q.z, q.w) ); length(max(q, vec4(0, 0, 0, 0))) + min(max_q, 0.0) }, Aabb::aabb() -> (vec4, vec4) { (-size, size) } } class Sphere4D { radius: f32, Proj::proj(vector: vec4) -> vec4 { radius * normalize(vector + vec4(0.000001, 0.0, 0.0, 0.0)) }, Sdf::sdf(vector: vec4) -> f32 { length(vector) - radius }, Aabb::aabb() -> (vec4, vec4) { (-radius * ONES, radius * ONES) } } class Ditorus { r01: f32, r012: f32, r0123: f32, Proj::proj(vector: vec4) -> vec4 { let proj1: vec4 = r01 * normalize(vec4(vector.x, vector.y, 0.0, 0.0)); let proj2: vec4 = r012 * normalize(vec4(vector.x, vector.y, vector.z, 0.0) - proj1); let proj3: vec4 = r0123 * normalize(vector - proj2 - proj1); proj3 + proj2 + proj1 }, Sdf::sdf(vector: vec4) -> f32 { let proj1: vec4 = r01 * normalize(vec4(vector.x, vector.y, 0.0, 0.0)); let proj2: vec4 = r012 * normalize(vec4(vector.x, vector.y, vector.z, 0.0) - proj1); length(vector - proj1 - proj2) - r0123 }, Aabb::aabb() -> (vec4, vec4) { (-r01 - r012 - r0123, r01 + r012 + r0123) } } class Torisphere { r012: f32, r0123: f32, Proj::proj(vector: vec4) -> vec4 { let proj1: vec4 = r012 * normalize(vec4(vector.x, vector.y, vector.z, 0.0)); proj1 + r0123 * normalize(vector - proj1) }, Sdf::sdf(vector: vec4) -> f32 { let proj1: vec4 = r012 * normalize(vec4(vector.x, vector.y, vector.z, 0.0)); length(vector - proj1) - r0123 }, Aabb::aabb() -> (vec4, vec4) { ((-r012 - r0123) * ONES, (r012 + r0123) * ONES) } } class Spheritorus { r01: f32, r0123: f32, Proj::proj(vector: vec4) -> vec4 { let proj1: vec4 = r01 * normalize(vec4(vector.x, vector.y, 0.0, 0.0)); proj1 + r0123 * normalize(vector - proj1) }, Sdf::sdf(vector: vec4) -> f32 { let proj1: vec4 = r01 * normalize(vec4(vector.x, vector.y, 0.0, 0.0)); length(vector - proj1) - r0123 }, Aabb::aabb() -> (vec4, vec4) { ((-r01 - r0123) * ONES, (r01 + r0123) * ONES) } } class Nothing { Proj::proj(vector: vec4) -> vec4 { vec4(1000000, 10000000, 1000000, 1000000) }, Sdf::sdf(vector: vec4) -> f32 { 1000000000.0 }, Aabb::aabb() -> (vec4, vec4) { (INFINITY * ONES, -INFINITY * ONES) } } class Marker { Proj::proj(vector: vec4) -> vec4 { vec4(0, 0, 0, 0) }, Sdf::sdf(vector: vec4) -> f32 { length(vector) }, Aabb::aabb() -> (vec4, vec4) { (vec4(0, 0, 0, 0), vec4(0, 0, 0, 0)) } }
Operations:
class Shell { offset: f32, shape: Class, Proj::proj<shape: Proj>(vector: vec4) -> vec4 { let proj: vec4 = shape.proj(vector); proj + offset * normalize(vector - proj) }, Sdf::sdf<shape: Sdf>(vector: vec4) -> f32 { abs(shape.sdf(vector)) - offset }, Aabb::aabb<shape: Aabb>() -> (vec4, vec4) { let aabb: (vec4, vec4) = shape.aabb(); (-offset * ONES + aabb.0, offset * ONES + aabb.1) } } class Extrude { direction: vec4, shape: Class, Proj::proj<shape: proj="">(vector: vec4) -> vec4 { let dot: f32 = dot(vector, direction); direction * dot + shape.proj(vector - direction * dot) }, Sdf::sdf<shape: sdf="">(vector: vec4) -> f32 { let dot: f32 = dot(vector, direction); shape.sdf(vector - direction * dot) }, Aabb::aabb() -> (vec4, vec4) { (-INFINITY * ONES, INFINITY * ONES) } } class Shift { shift: vec4, shape: Class, Proj::proj<shape: proj="">(vector: vec4) -> vec4 { shape.proj(vector - shift) + shift }, Sdf::sdf<shape: sdf="">(vector: vec4) -> f32 { shape.sdf(vector - shift) }, Aabb::aabb<shape: aabb="">() -> (vec4, vec4) { let aabb: (vec4, vec4) = shape.aabb(); (aabb.0 + shift, aabb.1 + shift) } } // TODO: Need to add support for precomputed data like orientation & inverted_orientation class Reorient { inv_orientation: mat4x4, shape: Class, Proj::proj<shape: proj="">(vector: vec4) -> vec4 { shape.proj(inv_orientation * vector) }, Sdf::sdf<shape: sdf="">(vector: vec4) -> f32 { shape.sdf(inv_orientation * vector) }, // TODO: Improve this later, by actually computing the new AABB Aabb::aabb<shape: aabb="">() -> (vec4, vec4) { (-INFINITY * ONES, INFINITY * ONES) } } class Invert { shape: Class, Proj::proj<shape: proj="">(vector: vec4) -> vec4 { shape.proj(vector) }, Sdf::sdf<shape: sdf="">(vector: vec4) -> f32 { -shape.sdf(vector) }, Aabb::aabb<shape: aabb="">() -> (vec4, vec4) { shape.aabb() } } // TODO: Falls apart in some cases, needs improvement class Intersect { shape1: Class, shape2: Class, Proj::proj<shape1: proj="" +="" sdf,="" shape2:="" sdf="">(vector: vec4) -> vec4 { let proj1: vec4 = shape1.proj(vector); let proj2: vec4 = shape2.proj(vector); let proj3: vec4 = shape2.proj(proj1); let sdf1: f32 = shape1.sdf(vector); let sdf2: f32 = shape2.sdf(vector); select( select(proj2, proj1, sdf1 > sdf2), proj3, sign(sdf1) == sign(sdf2) ) }, Sdf::sdf<shape1: sdf="" +="" proj,="" shape2:="" proj="">(vector: vec4) -> f32 { let proj1: vec4 = shape1.proj(vector); let proj2: vec4 = shape2.proj(vector); let proj3: vec4 = shape2.proj(proj1); let sdf1: f32 = shape1.sdf(vector); let sdf2: f32 = shape2.sdf(vector); select( select(length(proj2) * sign(sdf2), length(proj1) * sign(sdf1), sdf1 > sdf2), length(proj3) * sign(sdf1), sign(sdf1) == sign(sdf2) ) }, Aabb::aabb<shape1: aabb,="" shape2:="" aabb="">() -> (vec4, vec4) { let aabb1: (vec4, vec4) = shape1.aabb(); let aabb2: (vec4, vec4) = shape2.aabb(); (max(aabb1.0, aabb2.0), min(aabb1.1, aabb2.1)) } } // TODO: Doesn't work at all :( class Interpolate { shape1: Class, shape2: Class, t: f32, Proj::proj<shape1: Proj + sdf, shape2= Sdf>(vector: vec4) -> vec4 { let d1: vec4 = vector - shape1.proj(vector); let l1: f32 = shape1.sdf(vector); let d2: vec4 = vector - shape2.proj(vector); let l2: f32 = shape2.sdf(vector); let cur_t: f32 = l1 / (l1 + l2); let dot: f32 = dot(d1, d2) / (l1 * l2); select( vector + d1 / l1 * (l2 * t - l1 * (1 - t)) / (1 - t - dot * t), vector + d2 / l2 * (l2 * t - l1 * (1 - t)) / (dot - dot * t - t), cur_t < t ) }, } /* [IGNORE] OLD operations.rhai FILE: //! shell { offset: f32, shape: Shape } fn shell_proj(shape_proj, offset, vector) { let proj = shape_proj.call(vector); proj + offset * (vector - proj).normalize() } fn shell_sdf(shape_sdf, offset, vector) { -offset + abs(shape_sdf.call(vector)) } //! extrude { direction: Vec4, shape: Shape } fn extrude_proj(shape_proj, direction, vector) { let dot = vector.dot(direction); direction * dot + shape_proj.call(vector - direction * dot) } fn extrude_sdf(shape_sdf, direction, vector) { let dot = vector.dot(direction); shape_sdf.call(vector - direction * dot) } //! shift { shift: Vec4, shape: Shape } fn shift_proj(shape_proj, shift, vector) { shift + shape_proj.call(vector - shift) } fn shift_sdf(shape_sdf, shift, vector) { shape_sdf.call(vector - shift) } //! reorient { inv_orientation: Mat4, shape: Shape } fn reorient_proj(shape_proj, inv_orientation, vector) { shape_proj.call(inv_orientation * vector) } fn reorient_sdf(shape_sdf, inv_orientation, vector) { shape_sdf.call(inv_orientation * vector) } //! invert { shape: Shape } fn invert_proj(shape_proj, vector) { shape_proj.call(vector) } fn invert_sdf(shape_sdf, vector) { -shape_sdf.call(vector) } fn invert_aabb() { [vec4(-100000000, -100000000, -100000000, -100000000), vec4(100000000, 100000000, 100000000, 100000000)] } //! intersection { shape1: Shape, shape2: Shape } fn intersection_proj(shape1_proj, shape2_proj, shape1_sdf, shape2_sdf, vector) { let proj1 = shape1_proj.call(vector); let proj2 = shape2_proj.call(vector); let proj3 = shape2_proj.call(proj1); let sdf1 = shape1_sdf.call(vector); let sdf2 = shape2_sdf.call(vector); select( select(proj2, proj1, sdf1 > sdf2), proj3, sign(sdf1) == sign(sdf2) ) } fn intersection_sdf(shape1_proj, shape2_proj, shape1_sdf, shape2_sdf, vector) { let proj1 = shape1_proj.call(vector); let proj2 = shape2_proj.call(vector); let proj3 = shape2_proj.call(proj1); let sdf1 = shape1_sdf.call(vector); let sdf2 = shape2_sdf.call(vector); select( select(length(proj2) * sign(sdf2), length(proj1) * sign(sdf1), sdf1 > sdf2), length(proj3) * sign(sdf1), sign(sdf1) == sign(sdf2) ) } fn intersection_aabb() { [vec4(-100000000, -100000000, -100000000, -100000000), vec4(100000000, 100000000, 100000000, 100000000)] } */ </shape1:></shape1:></shape1:></shape1:></shape:></shape:></shape:></shape:></shape:></shape:></shape:></shape:></shape:></shape:></shape:></shape:></shape:></shape:>
Combined:
class Wormhole { shape: Class, offset: f32, } => Shell { offset, shape: Intersect { shape1: Shell { shape: Plane4D { normal: vec4(0, 0, 0, 1) }, offset: 0 }, shape2: Invert { shape: Extrude { direction: vec4(0, 0, 0, 1), shape } } } } class WormholeWrapped { shape: Class, offset: f32, dist: f32 } => Shell { offset, shape: Intersect { shape1: Intersect { shape1: Shell { shape: Plane4D { normal: vec4(0, 0, 0, 1) }, offset: 0 }, shape2: Shift { shift: vec4(0, 0, dist, 0), shape: Plane4D { normal: vec4(0, 0, 1, 0) } } }, shape2: Invert { shape: Extrude { direction: vec4(0, 0, 0, 1), shape } } } }
Complex:
class Portal { width: f32, height: f32, bridge_radius: f32, cutout_size: f32, Proj::proj(vector: vec4) -> vec4 { let bend_proj: vec4 = bridge_radius * normalize(vec4(0.0, 0.0, vector.z, vector.w)) + vec4(vector.x, vector.y, 0.0, 0.0); let plane_proj: vec4 = vec4(vector.x, vector.y, vector.z, bridge_radius * sign(vector.w)); let is_in_cutout: bool = abs(vector.x) < width && abs(vector.y) < height && 0 < vector.z && vector.z < cutout_size; select( plane_proj, bend_proj, abs(vector.w) < bridge_radius - 0.1 || is_in_cutout ) }, // TODO: Sdf shouldn't be required just because the parent impls it! (Same for other fns) Sdf::sdf(vector: vec4) -> f32 { 0 }, Aabb::aabb() -> (vec4, vec4) { (ZERO, ZERO) } }
Sdf_only:
class Fence { spacing: f32, thickness: f32, size: vec4, Sdf::sdf(vector: vec4) -> f32 { let v: vec4 = vector - spacing * round(clamp(vector, -size, size) / spacing); v = vec4(v.x, v.y, vector.z, v.w); let v1: vec4 = v - vec4(v.x, 0, 0, 0); let v2: vec4 = v - vec4(0, v.y, 0, 0); min(length(v1) - thickness, length(v2) - thickness) }, // TODO: In the future you should only try to load what you can, but I'll just give a wrong implementation for now Proj::proj(vector: vec4) -> vec4 { vector }, } class ReorientSdf { inv_orientation: mat4x4, shape: Class, Sdf::sdf<shape: sdf="">(vector: vec4) -> f32 { shape.sdf(inv_orientation * vector) }, }
Level Descriptions: Rusty Object Notation (RON) (with some pre-processing)
Language Requirements:
- Customizable and able to ergonomically hold shape data -- CHECK
- Easy to extend with preprocessor -- CHECK
Test world:
World( name: "Dev world", description: "A bunch of levels I created to help test/debug the game or just to explore what the system can do...", levels: [ Level( name: "Plane", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: Plane4D(normal: [0, 0, 0, 1]), spawn_pose: Pose(position: [30, 30, 0, 0]), geometries: [ Geometry( pose: Pose(position: [0, -3, 0, 0]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "gray" ), ] ), Level( name: "Many much wormholen", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], // TODO: Because wormhole is an instance class it breaks when other classes use it manifold: Wormhole( shape: Sphere4D(radius: 20), offset: 10 ), spawn_pose: Pose(position: [0, 10, 30, 10]), geometries: [ /*Geometry( pose: Pose(position: [0, 10, -30, 10]), shape: Sphere4D(radius: 5), material: "basic", color: "pink", is_dynamic: true ),*/ Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [60, 40, 60, 60])), material: "basic", ), ] ), Level( name: "Cup", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: Shell( offset: 5, shape: Intersect( shape1: Plane4D( normal: [0, 0, 0, 1] ), shape2: Invert(shape: Shell( offset: 10, shape: Box4D(size: [10, 5, 10, 40]) )) ) ), spawn_pose: Pose(position: [80, 0, 0, 10]), geometries: [ Geometry( pose: Pose(position: [0, 0, 0, 0]), shape: Invert(shape: Box4D(size: [90, 40, 60, 60])), material: "basic" ), ] ), Level( name: "Bare Portal", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: Portal( width: Expr("qe+10"), height: Expr("tg+20"), bridge_radius: Expr("ol+20"), cutout_size: Expr("uj+6") ), spawn_pose: Pose(position: [40, 10, 0, 30]), geometries: [ Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [90, 40, 60, 60])), material: "basic" ), Geometry( pose: Pose(position: [-40, 40, -40, 20]), shape: ReorientSdf( inv_orientation: [[.707107, .707107, 0, 0], [-.707107, .707107, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], shape: Fence(spacing: 2, thickness: 0.04, size: [100, 100, 100, 3]) ), collider: Box4D(size: [100, 100, 0.1, 3]), material: "basic", color: "gray" ), /*Geometry( pose: Pose(position: [-30, 10, 0, -30]), shape: Sphere4D(radius: 5), material: "basic", color: "pink", is_dynamic: true ),*/ ] ), Level( name: "Portal??", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: Portal( width: 10.0, height: 20.0, bridge_radius: 20.0, cutout_size: 6.0 ), spawn_pose: Pose(position: [40, 10, 0, 30]), geometries: [ Geometry( pose: Pose(position: [10.0, 0.0, 3.4, 0]), shape: Box4D(size: [0.4, 20.0, 3.4, 60]), material: "rough" ), Geometry( pose: Pose(position: [-10.0, 0.0, 3.4, 0]), shape: Box4D(size: [0.4, 20.0, 3.4, 60]), material: "rough" ), Geometry( pose: Pose(position: [0.0, 20.0, 3.4, 0]), shape: Box4D(size: [10.0, 0.4, 3.4, 60]), material: "rough" ), Geometry( pose: Pose(position: [-10.0, 0.0, 10.0, 0]), shape: Box4D(size: [0.4, 20.0, 10.0, 19.0]), material: "rough" ), Geometry( pose: Pose(position: [10.0, 0.0, 10.0, 0]), shape: Box4D(size: [0.4, 20.0, 10.0, 19.0]), material: "rough" ), Geometry( pose: Pose(position: [0.0, 20.0, 10.0, 0]), shape: Box4D(size: [10.0, 0.4, 10.0, 19.0]), material: "rough" ), Geometry( pose: Pose(position: [0.0, 0.0, 6.0, 20]), shape: Box4D(size: [10.4, 20.4, 0.7, 0.1]), material: "rough" ), Geometry( pose: Pose(position: [0.0, 0.0, 6.0, -20]), shape: Box4D(size: [10.0, 20.0, 0.4, 0.1]), material: "rough" ), Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [90, 40, 60, 60])), material: "basic" ), Geometry( pose: Pose(position: [-40, 40, -40, 20]), shape: ReorientSdf( inv_orientation: [[.707107, .707107, 0, 0], [-.707107, .707107, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], shape: Fence(spacing: 2, thickness: 0.04, size: [100, 100, 100, 3]) ), collider: Box4D(size: [100, 100, 0.1, 3]), material: "basic", color: "gray" ), Geometry( pose: Pose(position: [-30, 10, 0, -30]), shape: Sphere4D(radius: 1), material: "basic", color: "pink", is_dynamic: true ), ] ), Level( name: "Plane", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: Plane4D(normal: [0, 0, 0, 1]), spawn_pose: Pose(position: [30, 30, 0, 0]), geometries: [ Geometry( pose: Pose(position: [-30, 10, 0, 0]), shape: Sphere4D(radius: Expr("0.5*sin(t)+3")), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, -3, 0, 0]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "gray" ), ] ), Level( name: "Wormhole gun", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], // TODO: Because wormhole is an instance class it breaks when other classes use it manifold: Wormhole( shape: Shift(shift: Expr("(vec4(20, 0, select(-20, 20, sin(t) > 0), 0))"), shape: Sphere4D(radius: Expr("abs(20*sin(t))"))), offset: Expr("tg + 10") ), spawn_pose: Pose(position: [0, 10, 30, 10]), geometries: [ Geometry( pose: Pose(position: [0, 10, -30, 10]), shape: Sphere4D(radius: 5), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [60, 40, 60, 60])), material: "basic", ), Geometry( pose: Pose(position: [0, 40, 0, 10]), shape: ReorientSdf( inv_orientation: [[.707107, .707107, 0, 0], [-.707107, .707107, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], shape: Fence(spacing: 2, thickness: 0.04, size: [100, 100, 100, 3]) ), collider: Box4D(size: [100, 100, 0.1, 3]), material: "basic", color: "gray" ), ] ), Level( name: "Wormhole gun cube-ey", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], // TODO: Because wormhole is an instance class it breaks when other classes use it manifold: Wormhole( shape: Shift( shift: Expr("(vec4(20, -10, select(-30, 30, sin(t/3) > 0), 0))"), shape: Shell( offset: 11, shape: Box4D(size: Expr("vec4(0.1, 40*abs(sin(t/3)), 10, 100)")) ) ), offset: 10 ), spawn_pose: Pose(position: [0, 10, 30, 10]), geometries: [ Geometry( pose: Pose(position: [0, 10, -30, 10]), shape: Sphere4D(radius: 5), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [60, 40, 60, 60])), material: "basic", ), Geometry( pose: Pose(position: [0, 40, 0, 10]), shape: ReorientSdf( inv_orientation: [ [.707107, .707107, 0, 0], [-.707107, .707107, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], shape: Fence(spacing: 2, thickness: 0.04, size: [100, 100, 100, 3]) ), collider: Box4D(size: [100, 100, 0.1, 3]), material: "basic", color: "gray" ), ] ), Level( name: "Wormhole gun portally", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: Shift( shift: Expr("vec4(select(-30, 30, sin(t/3) > 0), 0, 20, 0)"), shape: Portal( width: 10.0, height: Expr("30*abs(sin(t/3))"), bridge_radius: 10.0, cutout_size: 6.0 ) ), spawn_pose: Pose(position: [30, 10, 0, 10]), geometries: [ Geometry( pose: Pose(position: [-30, 10, 0, 10]), shape: Sphere4D(radius: 5), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [60, 40, 60, 60])), material: "basic", ), Geometry( pose: Pose(position: [0, 40, 0, 10]), shape: ReorientSdf( inv_orientation: [ [0, 0, 1, 0], [-.707107, .707107, 0, 0], [.707107, .707107, 0, 0], [0, 0, 0, 1] ], shape: Fence(spacing: 2, thickness: 0.04, size: [100, 100, 100, 3]) ), collider: Box4D(size: [0.1, 100, 100.0, 3]), material: "basic", color: "gray" ), ] ), Level( name: "Interp pt.2", description: "more desc >:D muahahaha", scripts: ["worlds/test_world/script1.rhai"], manifold: Shell( shape: Box4D(size: [25, 25, 25, 25]), offset: Expr("qe + 5") ), spawn_pose: Pose(position: [30, 30, 0, 0]), geometries: [ Geometry( pose: Pose(position: [-100, -3, 0, 0]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "pink" ), Geometry( pose: Pose(position: [100, -3, 0, 0]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "gray", id: "floor" ), ] ), Level( name: "Wormulous Holicules", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: WormholeWrapped( shape: Sphere4D(radius: Expr("qe + 10")), offset: Expr("tg + 5"), dist: Expr("tg + 10") ), spawn_pose: Pose(position: [30, 30, 0, 5]), geometries: [ Geometry( pose: Pose(position: [-30, 10, 0, -5]), shape: Sphere4D(radius: Expr("0.5*sin(t)+3")), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, -3, 0, 0]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "gray", id: "floor" ), ] ), Level( name: "Wormulous Holicules -- Cube", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: WormholeWrapped( shape: Shell(offset: Expr("tg + 6 + qe"), shape: Box4D(size: Expr("vec4(ol + 5, 20 * yh + 5, ik + 5, 5)"))), offset: Expr("tg + 5"), dist: Expr("uj + 30") ), spawn_pose: Pose(position: [30, 30, 0, 5]), geometries: [ Geometry( pose: Pose(position: [-30, 10, 0, -5]), shape: Sphere4D(radius: Expr("0.5*sin(t)+3")), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [50, 40, 50, 50])), material: "basic", id: "floor" ), ] ), Level( name: "Wormulous Holicules -- Cube no rounding", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: WormholeWrapped( shape: Box4D(size: Expr("vec4(10*qe + 20, 20 * tg + 20, 10*yh + 20, 10)")), offset: Expr("uj + 5"), dist: Expr("ik + 40") ), spawn_pose: Pose(position: [30, 30, 0, 5]), geometries: [ Geometry( pose: Pose(position: [-30, 10, 0, -5]), shape: Sphere4D(radius: 3), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [60, 40, 60, 60])), material: "basic", id: "floor" ), ] ), Level( name: "Wormulous Holicules -- Torus", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: WormholeWrapped( shape: Shift(shift: Expr("vec4(5 * uj, 5 * qe, 5 * tg, 0)"), shape: Spheritorus(r01: 15, r012: 10, r0123: 10)), offset: Expr("ik + 5"), dist: 30 ), spawn_pose: Pose(position: [30, 30, 0, 5]), geometries: [ Geometry( pose: Pose(position: [100, -3, 0, 0]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "gray" ), Geometry( pose: Pose(position: [-100, -3, 0, -100]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "purple" ), Geometry( pose: Pose(position: [-100, -3, 100, 100]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "orange" ), ] ), Level( name: "Weird cut in box", description: "more desc >:D muahahaha", scripts: ["worlds/test_world/script1.rhai"], manifold: Shell( offset: 10, shape: Intersect( shape1: Intersect( shape1: Box4D(size: [25, 25, 25, 25]), shape2: Invert( shape: Shell( shape: Shift( shift: [25, 0, 25, 25], shape: Box4D(size: [30, 30, 30, 30])), offset: 20 )) ), shape2: Invert(shape: Shift(shift: [0, 0, 0, -25], shape: Sphere4D(radius: 15))))), spawn_pose: Pose(position: [-30, 30, 0, 0]), geometries: [ Geometry( pose: Pose(position: [-100, -3, 0, 0]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "pink" ), Geometry( pose: Pose(position: [100, -3, 0, 0]), shape: Box4D(size: [100, 3, 100, 100]), material: "basic", color: "gray" ), Geometry( pose: Pose(position: [-30, 10, 0, 0]), shape: Sphere4D(radius: 5), material: "basic", color: "yellow", is_dynamic: true ), ] ), Level( name: "Spherical test", description: "ABCD", scripts: ["worlds/test_world/script1.rhai"], manifold: Sphere4D(radius: 30), spawn_pose: Pose(position: [30, 30, 0, 0]), geometries: [ Geometry( pose: Pose(position: [-30, 0, 0, 0]), shape: Sphere4D(radius: Expr("0.5*sin(t)+3")), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, -1000, 0, 0]), shape: Sphere4D(radius: 1000), material: "basic", color: "gray" ), // Geometry( // pose: Pose(position: [0, -3, 0, 0]), // shape: Box4D(size: [100, 3, 100, 100]), // material: "basic", // color: "gray" // ), ] ), Level( name: "RUNNN!!", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: Wormhole( shape: Sphere4D(radius: Expr("max(0, 200 * cos(2 * 3.141592 * t / 20))")), offset: 20 ), spawn_pose: Pose(position: [0, 5, -150, 20]), geometries: [ Geometry( pose: Pose(position: [0, 5, -200, -20]), shape: Sphere4D(radius: 3), material: "basic", color: "pink", is_dynamic: true ), Geometry( pose: Pose(position: [0, -0.5, 0, 0]), shape: Box4D(size: [5, 0.5, 1000, 100]), material: "basic", color: "gray" ), Geometry( pose: Pose(position: [0, -0.9999, -200, -200]), shape: Box4D(size: [10, 1, 10, 200]), material: "basic", color: "red" ), Geometry( pose: Pose(position: [0, 0, -110, 0]), shape: Invert(shape: Box4D(size: [80, 80, 110, 80])), material: "basic" ), ] ), Level( name: "Failed Tube -- intersection is hard :(", description: "more desc >:D", scripts: ["worlds/test_world/script1.rhai"], manifold: Plane4D(normal: [0, 0, 0, 1]), spawn_pose: Pose(position: [0, 10, 0, 0]), geometries: [ Geometry( pose: Pose(position: [0, 40, 0, 0]), shape: Invert(shape: Box4D(size: [50, 40, 50, 80])), material: "basic" ), Geometry( pose: Pose(position: [30, 0, 0, 0]), shape: Intersect( shape1: Plane4D(normal: [0, 0, 1, 0]), shape2: Shell( offset: 1, shape: Sphere4D(radius: 10) ) ), material: "basic" ), ] ), ] )
Scripting: Rhai
Language Requirements:
- Easy to integrate into the game loop -- CHECK
- Nice APIs -- CHECK
- Able to interact nicely with rust structs and data -- CHECK
Test World Script (script1.rhai):
let qe = 0; let tg = 0; let yh = 0; let uj = 0; let ik = 0; let ol = 0; fn init() { print("init called from rhai"); } fn update() { if "floor" in geometries { geometries["floor"].pose.position.y = ol; } } fn on_key_press_q() { print("q pressed"); qe += 0.1; } fn on_key_press_e() { print("e pressed"); qe -= 0.1; } fn on_key_press_t() { print("t pressed"); tg += 0.1; } fn on_key_press_g() { print("g pressed"); tg -= 0.1; } fn on_key_press_y() { print("y pressed"); yh += 0.1; } fn on_key_press_h() { print("h pressed"); yh -= 0.1; } fn on_key_press_u() { print("u pressed"); uj += 0.1; } fn on_key_press_j() { print("j pressed"); uj -= 0.1; } fn on_key_press_i() { print("i pressed"); ik += 0.1; } fn on_key_press_k() { print("k pressed"); ik -= 0.1; } fn on_key_press_o() { print("o pressed"); ol += 0.1; } fn on_key_press_l() { print("l pressed"); ol -= 0.1; }
Assets Dowload
If you want to download the assets folder to play around with it is available hereGet Arbgeom beta
Arbgeom beta
Curved space marble game
Status | In development |
Author | Tomwol |
Tags | bevy, marble-game, non-eucledian, non-euclidean |
More posts
- Implementing 4D PGA into XPBDJan 20, 2024
Comments
Log in with itch.io to leave a comment.
How do I enter the test/dev worlds exactly?
Wait i forgot it was hardcoded! never mind, take your time though