From 858fcebc81c780e1b3ab9900eb54a62e76000b4b Mon Sep 17 00:00:00 2001 From: kcz <1732074+cookie-s@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:07:43 -0500 Subject: [PATCH] avm2: Implement Matrix3D with 2D support only Replace stubs for flash.geom.Transform.matrix3D getter/setter with an actual implementation of Matrix3D with limited support. This implementation is just a proxy to the existing 2D matrix implementation. Therefore transformations beyond 2D transformation work differently from the expected result. --- core/src/avm2/globals.rs | 3 + core/src/avm2/globals/flash/geom/Transform.as | 12 +--- core/src/avm2/globals/flash/geom/transform.rs | 68 +++++++++++++++++++ render/src/lib.rs | 1 + render/src/matrix3d.rs | 50 ++++++++++++++ 5 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 render/src/matrix3d.rs diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index ce474b19bc0a..7c4d6ca849b4 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -112,6 +112,7 @@ pub struct SystemClasses<'gc> { pub transform: ClassObject<'gc>, pub colortransform: ClassObject<'gc>, pub matrix: ClassObject<'gc>, + pub matrix3d: ClassObject<'gc>, pub illegaloperationerror: ClassObject<'gc>, pub eventdispatcher: ClassObject<'gc>, pub rectangle: ClassObject<'gc>, @@ -270,6 +271,7 @@ impl<'gc> SystemClasses<'gc> { transform: object, colortransform: object, matrix: object, + matrix3d: object, illegaloperationerror: object, eventdispatcher: object, rectangle: object, @@ -926,6 +928,7 @@ pub fn init_native_system_classes(activation: &mut Activation<'_, '_>) { ("flash.events", "ContextMenuEvent", contextmenuevent), ("flash.events", "FocusEvent", focusevent), ("flash.geom", "Matrix", matrix), + ("flash.geom", "Matrix3D", matrix3d), ("flash.geom", "Point", point), ("flash.geom", "Rectangle", rectangle), ("flash.geom", "Transform", transform), diff --git a/core/src/avm2/globals/flash/geom/Transform.as b/core/src/avm2/globals/flash/geom/Transform.as index 1dbaf532f203..7dd4db5e54af 100644 --- a/core/src/avm2/globals/flash/geom/Transform.as +++ b/core/src/avm2/globals/flash/geom/Transform.as @@ -10,7 +10,6 @@ package flash.geom { [Ruffle(InternalSlot)] private var displayObject:DisplayObject; - private var _matrix3D:Matrix3D = null; private var _perspectiveProjection:PerspectiveProjection = null; function Transform(object:DisplayObject) { @@ -30,15 +29,8 @@ package flash.geom { public native function get concatenatedMatrix():Matrix; public native function get pixelBounds():Rectangle; - public function get matrix3D():Matrix3D { - stub_getter("flash.geom.Transform", "matrix3D"); - return this._matrix3D; - } - - public function set matrix3D(m:Matrix3D):void { - stub_setter("flash.geom.Transform", "matrix3D"); - this._matrix3D = m; - } + public native function get matrix3D():Matrix3D; + public native function set matrix3D(value:Matrix3D):void; public function get perspectiveProjection():PerspectiveProjection { stub_getter("flash.geom.Transform", "perspectiveProjection"); diff --git a/core/src/avm2/globals/flash/geom/transform.rs b/core/src/avm2/globals/flash/geom/transform.rs index 8821333d2b53..563bd2f47bf0 100644 --- a/core/src/avm2/globals/flash/geom/transform.rs +++ b/core/src/avm2/globals/flash/geom/transform.rs @@ -3,6 +3,8 @@ use crate::avm2::parameters::ParametersExt; use crate::avm2::{Activation, Error, Object, TObject, Value}; use crate::display_object::TDisplayObject; use crate::prelude::{DisplayObject, Matrix, Twips}; +use crate::{avm2_stub_getter, avm2_stub_setter}; +use ruffle_render::matrix3d::Matrix3D; use ruffle_render::quality::StageQuality; use swf::{ColorTransform, Fixed8, Rectangle}; @@ -45,6 +47,36 @@ pub fn set_color_transform<'gc>( Ok(Value::Undefined) } +pub fn get_matrix_3d<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + // FIXME: This Matrix3D is generated from the 2D Matrix. + // It does not work when the matrix contains any transformation in 3D. + // Support native Matrix3D. + avm2_stub_getter!(activation, "flash.geom.Transform", "matrix3D"); + + let matrix = *get_display_object(this, activation)?.base().matrix(); + let matrix3d = Matrix3D::from(matrix); + matrix3d_to_object(matrix3d, activation) +} + +pub fn set_matrix_3d<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + // FIXME: This sets 2D Matrix generated from the given Matrix3D, ignoring 3D parameters. + // Support native Matrix3D. + avm2_stub_setter!(activation, "flash.geom.Transform", "matrix3D"); + + let matrix3d = object_to_matrix3d(args.get_object(activation, 0, "value")?, activation)?; + let matrix = Matrix::from(matrix3d); + let matrix = matrix_to_object(matrix, activation)?; + set_matrix(activation, this, &[matrix]) +} + pub fn get_matrix<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, @@ -173,6 +205,42 @@ pub fn color_transform_to_object<'gc>( Ok(object.into()) } +fn matrix3d_to_object<'gc>( + matrix: Matrix3D, + activation: &mut Activation<'_, 'gc>, +) -> Result, Error<'gc>> { + let args = matrix.raw_data.map(Into::into); + let object = activation + .avm2() + .classes() + .matrix3d + .construct(activation, &args)?; + Ok(object.into()) +} + +fn object_to_matrix3d<'gc>( + object: Object<'gc>, + activation: &mut Activation<'_, 'gc>, +) -> Result> { + let raw_data = object + .get_public_property("rawData", activation)? + .as_object() + .expect("rawData cannot be null"); + let raw_data = raw_data + .as_vector_storage() + .expect("rawData is not a Vector"); + let raw_data: Vec = (0..16) + .map(|i| -> Result> { + raw_data.get(i, activation)?.coerce_to_number(activation) + }) + .collect::, _>>()?; + let raw_data = raw_data + .as_slice() + .try_into() + .expect("rawData size must be 16"); + Ok(Matrix3D { raw_data }) +} + pub fn matrix_to_object<'gc>( matrix: Matrix, activation: &mut Activation<'_, 'gc>, diff --git a/render/src/lib.rs b/render/src/lib.rs index e0f51091f79b..03e26a8ce486 100644 --- a/render/src/lib.rs +++ b/render/src/lib.rs @@ -8,6 +8,7 @@ pub mod error; pub mod filters; pub mod lines; pub mod matrix; +pub mod matrix3d; pub mod pixel_bender; // The `renderdoc` crate doesn't compile on apple platforms #[cfg(all(feature = "renderdoc", not(target_vendor = "apple")))] diff --git a/render/src/matrix3d.rs b/render/src/matrix3d.rs new file mode 100644 index 000000000000..862f8d742fbb --- /dev/null +++ b/render/src/matrix3d.rs @@ -0,0 +1,50 @@ +use crate::matrix::Matrix; +use swf::Twips; + +/// The transformation matrix for 3D used by Flash display objects. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Matrix3D { + /// 4x4 matrix elements. + pub raw_data: [f64; 16], +} + +impl From for Matrix3D { + fn from(matrix: Matrix) -> Self { + Self { + raw_data: [ + // 1st column + matrix.a.into(), + matrix.b.into(), + 0.0, + 0.0, + // 2nd column + matrix.c.into(), + matrix.d.into(), + 0.0, + 0.0, + // 3rd column + 0.0, + 0.0, + 1.0, + 0.0, + // 4th column + matrix.tx.to_pixels(), + matrix.ty.to_pixels(), + 0.0, + 1.0, + ], + } + } +} +impl From for Matrix { + fn from(matrix: Matrix3D) -> Self { + Self { + a: matrix.raw_data[0] as f32, + b: matrix.raw_data[1] as f32, + c: matrix.raw_data[4] as f32, + d: matrix.raw_data[5] as f32, + tx: Twips::from_pixels(matrix.raw_data[12]), + ty: Twips::from_pixels(matrix.raw_data[13]), + } + } +}