Skip to content

Commit

Permalink
avm2: Implement Matrix3D with 2D support only
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cookie-s authored Dec 2, 2024
1 parent 2eb5ff3 commit 858fceb
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 10 deletions.
3 changes: 3 additions & 0 deletions core/src/avm2/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -270,6 +271,7 @@ impl<'gc> SystemClasses<'gc> {
transform: object,
colortransform: object,
matrix: object,
matrix3d: object,
illegaloperationerror: object,
eventdispatcher: object,
rectangle: object,
Expand Down Expand Up @@ -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),
Expand Down
12 changes: 2 additions & 10 deletions core/src/avm2/globals/flash/geom/Transform.as
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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");
Expand Down
68 changes: 68 additions & 0 deletions core/src/avm2/globals/flash/geom/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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<Value<'gc>, 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<Value<'gc>, 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>,
Expand Down Expand Up @@ -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<Value<'gc>, 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<Matrix3D, Error<'gc>> {
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<f64> = (0..16)
.map(|i| -> Result<f64, Error<'gc>> {
raw_data.get(i, activation)?.coerce_to_number(activation)
})
.collect::<Result<Vec<f64>, _>>()?;
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>,
Expand Down
1 change: 1 addition & 0 deletions render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")))]
Expand Down
50 changes: 50 additions & 0 deletions render/src/matrix3d.rs
Original file line number Diff line number Diff line change
@@ -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<Matrix> 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<Matrix3D> 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]),
}
}
}

0 comments on commit 858fceb

Please sign in to comment.