Skip to content
This repository has been archived by the owner on Jul 22, 2023. It is now read-only.

Commit

Permalink
Add CFrame type (#48)
Browse files Browse the repository at this point in the history
* implement cframe type

* add entry to changelog

* fix copy paste typo

* refactor cframe.new

* address pr comments

* refactor cframe constructor

* rename cframe userdata struct
  • Loading branch information
jeparlefrancais authored May 10, 2021
1 parent 053f85f commit 51edda7
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased Changes

* Added support for CFrame ([#48](https://github.com/rojo-rbx/remodel/pull/48))
* Added support for Vector3, and improved Vector3int16 ([#46](https://github.com/rojo-rbx/remodel/pull/46))
* Added Color3.fromRGB(red, blue, green) ([#44](https://github.com/rojo-rbx/remodel/pull/44))

Expand Down
72 changes: 72 additions & 0 deletions src/roblox_api/cframe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use rbx_dom_weak::types::{CFrame, Matrix3, Vector3};
use rlua::{UserData, UserDataMethods, Value as LuaValue};

use crate::value::{CFrameValue, Vector3Value};

pub struct CFrameUserData;

impl CFrameUserData {
fn from_position(x: f32, y: f32, z: f32) -> CFrameValue {
CFrameValue::new(CFrame::new(
Vector3::new(x as f32, y as f32, z as f32),
// TODO: replace with `Matrix3::identity()` once
// a version higher than 0.3.0 of rbx_types ships
Matrix3::new(
Vector3::new(1.0, 0.0, 0.0),
Vector3::new(0.0, 1.0, 0.0),
Vector3::new(0.0, 0.0, 1.0),
),
))
}
}

fn try_into_f32(value: LuaValue<'_>) -> Option<f32> {
match value {
LuaValue::Number(num) => Some(num as f32),
LuaValue::Integer(int) => Some(int as f32),
_ => None,
}
}

impl UserData for CFrameUserData {
fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(methods: &mut T) {
methods.add_function(
"new",
|_context,
arguments: (
Option<LuaValue<'_>>,
Option<LuaValue<'_>>,
Option<LuaValue<'_>>,
)| {
match arguments {
(None, None, None) => return Ok(Self::from_position(0.0, 0.0, 0.0)),
(Some(LuaValue::UserData(user_data)), None, None) => {
let position = &*user_data.borrow::<Vector3Value>()?;
return Ok(CFrameValue::new(CFrame::new(
position.inner(),
// TODO: replace with `rbx_dom_weak::types::Matrix3::identity()` once
// a version higher than 0.3.0 of rbx_types ships
Matrix3::new(
Vector3::new(1.0, 0.0, 0.0),
Vector3::new(0.0, 1.0, 0.0),
Vector3::new(0.0, 0.0, 1.0),
),
)));
}
_ => {}
};

let x = arguments.0.and_then(try_into_f32);
let y = arguments.1.and_then(try_into_f32);
let z = arguments.2.and_then(try_into_f32);

match (x, y, z) {
(Some(x), Some(y), Some(z)) => Ok(Self::from_position(x, y, z)),
_ => Err(rlua::Error::external(
"invalid argument #1 to 'new' (Vector3 expected)",
)),
}
},
);
}
}
3 changes: 3 additions & 0 deletions src/roblox_api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod cframe;
mod instance;

use std::sync::Arc;
Expand All @@ -10,6 +11,7 @@ use crate::{
value::{Color3Value, Vector3Value, Vector3int16Value},
};

use cframe::CFrameUserData;
pub use instance::LuaInstance;

pub struct RobloxApi;
Expand All @@ -20,6 +22,7 @@ impl RobloxApi {
context.globals().set("Vector3", Vector3)?;
context.globals().set("Vector3int16", Vector3int16)?;
context.globals().set("Color3", Color3)?;
context.globals().set("CFrame", CFrameUserData)?;

Ok(())
}
Expand Down
99 changes: 97 additions & 2 deletions src/value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Defines how to turn Variant values into Lua values and back.
use rbx_dom_weak::types::{Color3, Color3uint8, Variant, VariantType, Vector3, Vector3int16};
use rbx_dom_weak::types::{
CFrame, Color3, Color3uint8, Variant, VariantType, Vector3, Vector3int16,
};
use rlua::{
Context, MetaMethod, Result as LuaResult, ToLua, UserData, UserDataMethods, Value as LuaValue,
};
Expand All @@ -21,7 +23,7 @@ pub fn rbxvalue_to_lua<'lua>(context: Context<'lua>, value: &Variant) -> LuaResu
}
Variant::BrickColor(_) => unimplemented_type("BrickColor"),
Variant::Bool(value) => value.to_lua(context),
Variant::CFrame(_) => unimplemented_type("CFrame"),
Variant::CFrame(cframe) => CFrameValue::new(*cframe).to_lua(context),
Variant::Color3(value) => Color3Value::new(*value).to_lua(context),
Variant::Color3uint8(value) => Color3uint8Value::new(*value).to_lua(context),
Variant::ColorSequence(_) => unimplemented_type("ColorSequence"),
Expand Down Expand Up @@ -234,6 +236,10 @@ impl Vector3Value {
Self(value)
}

pub fn inner(&self) -> Vector3 {
self.0
}

fn meta_index<'lua>(
&self,
context: Context<'lua>,
Expand Down Expand Up @@ -379,3 +385,92 @@ impl UserData for Vector3int16Value {
});
}
}

#[derive(Debug, Clone, Copy)]
pub struct CFrameValue(CFrame);

impl CFrameValue {
pub fn new(value: CFrame) -> Self {
Self(value)
}

fn meta_index<'lua>(
&self,
context: Context<'lua>,
key: &str,
) -> rlua::Result<rlua::Value<'lua>> {
match key {
"X" => self.0.position.x.to_lua(context),
"Y" => self.0.position.y.to_lua(context),
"Z" => self.0.position.z.to_lua(context),
"RightVector" => Vector3Value::new(Vector3::new(
self.0.orientation.x.x,
self.0.orientation.y.x,
self.0.orientation.z.x,
))
.to_lua(context),
"UpVector" => Vector3Value::new(Vector3::new(
self.0.orientation.x.y,
self.0.orientation.y.y,
self.0.orientation.z.y,
))
.to_lua(context),
"LookVector" => Vector3Value::new(Vector3::new(
-self.0.orientation.x.z,
-self.0.orientation.y.z,
-self.0.orientation.z.z,
))
.to_lua(context),
"XVector" => Vector3Value::new(self.0.orientation.x).to_lua(context),
"YVector" => Vector3Value::new(self.0.orientation.y).to_lua(context),
"ZVector" => Vector3Value::new(self.0.orientation.z).to_lua(context),
_ => Err(rlua::Error::external(format!(
"'{}' is not a valid member of CFrame",
key
))),
}
}
}

impl From<&CFrameValue> for Variant {
fn from(cframe: &CFrameValue) -> Variant {
Variant::CFrame(cframe.0)
}
}

impl fmt::Display for CFrameValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}",
self.0.position.x,
self.0.position.y,
self.0.position.z,
self.0.orientation.x.x,
self.0.orientation.y.x,
self.0.orientation.z.x,
self.0.orientation.x.y,
self.0.orientation.y.y,
self.0.orientation.z.y,
self.0.orientation.x.z,
self.0.orientation.y.z,
self.0.orientation.z.z,
)
}
}

impl UserData for CFrameValue {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Eq, |context, this, rhs: Self| {
(this.0 == rhs.0).to_lua(context)
});

methods.add_meta_method(MetaMethod::Index, |context, this, key: String| {
this.meta_index(context, &key)
});

methods.add_meta_method(MetaMethod::ToString, |context, this, _arg: ()| {
this.to_string().to_lua(context)
});
}
}
35 changes: 35 additions & 0 deletions test-scripts/type-cframe.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
local function assertCFramePosition(vec, x, y, z)
assert(vec.X == x, ("%f ~= %f"):format(vec.X, x))
assert(vec.Y == y, ("%f ~= %f"):format(vec.Y, y))
assert(vec.Z == z, ("%f ~= %f"):format(vec.Z, z))
end

local function assertVector(vec, x, y, z)
assert(vec.X == x, ("x: %f ~= %f (%s)"):format(vec.X, x, tostring(vec)))
assert(vec.Y == y, ("y: %f ~= %f (%s)"):format(vec.Y, y, tostring(vec)))
assert(vec.Z == z, ("z: %f ~= %f (%s)"):format(vec.Z, z, tostring(vec)))
end

-- new with combinations of integer and floats
assertCFramePosition(CFrame.new(), 0, 0, 0)
assertCFramePosition(CFrame.new(1, 2, 3), 1, 2, 3)
assertCFramePosition(CFrame.new(1.5, 2, 3), 1.5, 2, 3)
assertCFramePosition(CFrame.new(1, 2.5, 3), 1, 2.5, 3)
assertCFramePosition(CFrame.new(1, 2, 3.5), 1, 2, 3.5)
assertCFramePosition(CFrame.new(1.5, 2.5, 3), 1.5, 2.5, 3)
assertCFramePosition(CFrame.new(1, 2.5, 3.5), 1, 2.5, 3.5)
assertCFramePosition(CFrame.new(1.5, 2.5, 3.5), 1.5, 2.5, 3.5)

-- new from Vector3
assertCFramePosition(CFrame.new(Vector3.new(1, 2, 3)), 1, 2, 3)

-- properties
assertVector(CFrame.new().XVector, 1, 0, 0)
assertVector(CFrame.new().YVector, 0, 1, 0)
assertVector(CFrame.new().ZVector, 0, 0, 1)

assertVector(CFrame.new().RightVector, 1, 0, 0)
assertVector(CFrame.new().UpVector, 0, 1, 0)
assertVector(CFrame.new().LookVector, -0, -0, -1)

assert(tostring(CFrame.new(7, 8, 9)) == "7, 8, 9, 1, 0, 0, 0, 1, 0, 0, 0, 1", "got " .. tostring(CFrame.new()))

0 comments on commit 51edda7

Please sign in to comment.