///|
fn Property::slot(self : Property) -> String {
match self {
Key(_) => "key"
Name(_) => "name"
Position(_, _, _) => "position"
Rotation(_, _, _) => "rotation"
Scale(_, _, _) => "scale"
Visible(_) => "visible"
CastShadow(_) => "castShadow"
ReceiveShadow(_) => "receiveShadow"
Geometry(_) => "geometry"
Material(_) => "material"
Light(_, _) => "light"
Perspective(_, _, _, _) => "perspective"
Object(_) => "object"
}
}
///|
impl Eq for Material with fn equal(a, b) {
match (a, b) {
(Basic(a), Basic(b)) => a == b
(Standard(a, b, c), Standard(x, y, z)) => a == x && b == y && c == z
(Normal, Normal) => true
(Borrowed(a), Borrowed(b)) => a.id() == b.id()
_ => false
}
}
///|
impl Eq for Property with fn equal(a, b) {
match (a, b) {
(Key(a), Key(b)) | (Name(a), Name(b)) => a == b
(Position(a, b, c), Position(x, y, z))
| (Rotation(a, b, c), Rotation(x, y, z))
| (Scale(a, b, c), Scale(x, y, z)) => a == x && b == y && c == z
(Visible(a), Visible(b))
| (CastShadow(a), CastShadow(b))
| (ReceiveShadow(a), ReceiveShadow(b)) => a == b
(Geometry(a), Geometry(b)) => a == b
(Material(a), Material(b)) => a == b
(Light(a, b), Light(x, y)) => a == x && b == y
(Perspective(a, b, c, d), Perspective(w, x, y, z)) =>
a == w && b == x && c == y && d == z
(Object(a), Object(b)) => a.same_reference(b)
_ => false
}
}
///|
fn object_defaults(object : @three.Object3D) -> Map[String, Property] {
let p = object.position()
let r = object.rotation()
let s = object.scale()
Map::from_array([
("name", Name(object.name())),
("position", Position(p.x(), p.y(), p.z())),
("rotation", Rotation(r.x(), r.y(), r.z())),
("scale", Scale(s.x(), s.y(), s.z())),
("visible", Visible(object.visible())),
("castShadow", CastShadow(object.cast_shadow())),
("receiveShadow", ReceiveShadow(object.receive_shadow())),
])
}
///|
fn valid_property(tag : String, prop : Property) -> Bool {
match prop {
Geometry(_) | Material(_) => tag == "mesh"
Object(_) => tag == "primitive"
Light(_, _) => tag == "ambientLight" || tag == "directionalLight"
Perspective(_, _, _, _) => tag == "perspectiveCamera"
_ => true
}
}
///|
fn apply_properties(node : Mounted, props : Map[String, Property]) -> Unit {
for _, prop in props {
if !valid_property(node.tag, prop) {
node.error = Some(
"Property " + prop.slot() + " is not supported on " + node.tag,
)
return
}
}
if node.tag == "mesh" &&
!(props.get("geometry") is Some(Geometry(_)) &&
props.get("material") is Some(Material(_))) {
node.error = Some("mesh requires Geometry and Material")
return
}
if node.object is None {
let object = match node.tag {
"group" => @three.Group().as_object3d()
"mesh" => {
guard props.get("geometry") is Some(Geometry(geometry)) &&
props.get("material") is Some(Material(material)) else {
node.error = Some("mesh requires Geometry and Material")
return
}
let g = geometry.create()
let m = material.create()
node.geometry = Some((geometry, g))
node.material = Some((material, m))
@three.Mesh(g, m).as_object3d()
}
"primitive" => {
guard props.get("object") is Some(Object(object)) else {
node.error = Some("primitive requires a static Object")
return
}
if node.root.claims.contains(object.id()) {
node.error = Some("An Object3D can only occupy one slot in a root")
return
}
let mut ancestor = Some(node.root.container)
while ancestor is Some(parent) {
if parent.same_reference(object) {
node.error = Some("A primitive cannot contain its mount container")
return
}
ancestor = parent.parent()
}
object
}
"ambientLight" => @three.AmbientLight(0xffffff, 1).as_object3d()
"directionalLight" => @three.DirectionalLight(0xffffff, 1).as_object3d()
"perspectiveCamera" =>
@three.PerspectiveCamera(50, 1, 0.1, 2000).as_object3d()
_ => {
node.error = Some("Unsupported element: " + node.tag)
return
}
}
node.object = Some(object)
node.root.claims.set(object.id(), true)
node.defaults = object_defaults(object)
if node.tag == "ambientLight" || node.tag == "directionalLight" {
node.defaults.set("light", Light(0xffffff, 1))
}
if node.tag == "perspectiveCamera" {
node.defaults.set("perspective", Perspective(50, 1, 0.1, 2000))
}
}
let object = node.object.unwrap()
let effective = node.defaults.copy()
for key, value in props {
effective.set(key, value)
}
for key, value in effective {
if node.previous.get(key) == Some(value) {
continue
}
match value {
Key(_) | Object(_) => ()
Name(value) => object.set_name(value)
Position(x, y, z) => object.set_position_xyz(x, y, z)
Rotation(x, y, z) => object.set_rotation_xyz(x, y, z)
Scale(x, y, z) => object.set_scale_xyz(x, y, z)
Visible(value) => object.set_visible(value)
CastShadow(value) => object.set_cast_shadow(value)
ReceiveShadow(value) => object.set_receive_shadow(value)
Geometry(spec) =>
if node.geometry is Some((previous, old)) && previous != spec {
let geometry = spec.create()
object.as_mesh().unwrap().set_geometry(geometry)
node.geometry = Some((spec, geometry))
if old.id() != geometry.id() {
dispose_geometry(previous, old)
}
}
Material(spec) =>
if node.material is Some((previous, old)) {
if spec.update(previous, old) {
node.material = Some((spec, old))
} else {
let material = spec.create()
object.as_mesh().unwrap().set_material(material)
node.material = Some((spec, material))
if old.id() != material.id() {
dispose_material(previous, old)
}
}
}
Light(color, intensity) => {
let light = object.as_light().unwrap()
light.color().set_hex(color) |> ignore
light.set_intensity(intensity)
}
Perspective(fov, aspect, near, far) => {
let camera = object
.as_camera()
.unwrap()
.as_perspective_camera()
.unwrap()
camera.set_fov(fov)
camera.set_aspect(aspect)
camera.set_near(near)
camera.set_far(far)
camera.update_projection_matrix()
}
}
}
node.previous = effective
node.error = None
}