From b8795817272f1e41ddbbda738db7258da41f94a4 Mon Sep 17 00:00:00 2001 From: kcz Date: Thu, 5 Dec 2024 23:35:14 -0500 Subject: [PATCH] avm2: Implement mode_3d switch in Transform --- core/src/avm1/globals/bitmap_data.rs | 1 + core/src/avm2/globals/flash/geom/transform.rs | 26 +++++-- core/src/display_object.rs | 17 ++++- core/src/display_object/edit_text.rs | 1 + core/src/display_object/stage.rs | 1 + render/src/transform.rs | 2 + render/wgpu/src/surface/commands.rs | 1 + tests/tests/swfs/avm2/geom_transform/Test.as | 65 +++++++++++++++--- .../tests/swfs/avm2/geom_transform/output.txt | 13 ++-- tests/tests/swfs/avm2/geom_transform/test.swf | Bin 1832 -> 2037 bytes 10 files changed, 106 insertions(+), 21 deletions(-) diff --git a/core/src/avm1/globals/bitmap_data.rs b/core/src/avm1/globals/bitmap_data.rs index 038107780f2d2..a4e6fd9a72d37 100644 --- a/core/src/avm1/globals/bitmap_data.rs +++ b/core/src/avm1/globals/bitmap_data.rs @@ -568,6 +568,7 @@ fn draw<'gc>( Transform { matrix, color_transform, + mode_3d: Default::default(), }, smoothing, blend_mode, diff --git a/core/src/avm2/globals/flash/geom/transform.rs b/core/src/avm2/globals/flash/geom/transform.rs index f4692f0d5a28c..9de88c37ffbcf 100644 --- a/core/src/avm2/globals/flash/geom/transform.rs +++ b/core/src/avm2/globals/flash/geom/transform.rs @@ -59,7 +59,11 @@ pub fn get_matrix_3d<'gc>( // Support native Matrix3D. avm2_stub_getter!(activation, "flash.geom.Transform", "matrix3D"); - let matrix = *get_display_object(this, activation)?.base().matrix(); + let dobj = get_display_object(this, activation)?; + if !dobj.base().mode_3d() { + return Ok(Value::Null); + } + let matrix = *dobj.base().matrix(); let matrix3d = Matrix3D::from(matrix); matrix3d_to_object(matrix3d, activation) } @@ -75,8 +79,16 @@ pub fn set_matrix_3d<'gc>( 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]) + + let dobj = get_display_object(this, activation)?; + dobj.set_matrix(activation.context.gc_context, matrix); + if let Some(parent) = dobj.parent() { + // Self-transform changes are automatically handled, + // we only want to inform ancestors to avoid unnecessary invalidations for tx/ty + parent.invalidate_cached_bitmap(activation.context.gc_context); + } + dobj.set_mode_3d(activation.context.gc_context, true); + Ok(Value::Undefined) } pub fn get_matrix<'gc>( @@ -84,7 +96,11 @@ pub fn get_matrix<'gc>( this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - let matrix = *get_display_object(this, activation)?.base().matrix(); + let dobj = get_display_object(this, activation)?; + if dobj.base().mode_3d() { + return Ok(Value::Null); + } + let matrix = *dobj.base().matrix(); matrix_to_object(matrix, activation) } @@ -97,6 +113,7 @@ pub fn set_matrix<'gc>( // null when trying to get the matrix- but the DO's actual transform matrix will // remain its previous non-null value. let matrix = object_to_matrix(args.get_object(activation, 0, "value")?, activation)?; + let dobj = get_display_object(this, activation)?; dobj.set_matrix(activation.context.gc_context, matrix); if let Some(parent) = dobj.parent() { @@ -104,6 +121,7 @@ pub fn set_matrix<'gc>( // we only want to inform ancestors to avoid unnecessary invalidations for tx/ty parent.invalidate_cached_bitmap(activation.context.gc_context); } + dobj.set_mode_3d(activation.context.gc_context, false); Ok(Value::Undefined) } diff --git a/core/src/display_object.rs b/core/src/display_object.rs index 59a43340caac1..10a48f7bfa4dd 100644 --- a/core/src/display_object.rs +++ b/core/src/display_object.rs @@ -328,6 +328,14 @@ impl<'gc> DisplayObjectBase<'gc> { &self.transform } + pub fn mode_3d(&self) -> bool { + self.transform.mode_3d + } + + pub fn set_mode_3d(&mut self, mode_3d: bool) { + self.transform.mode_3d = mode_3d; + } + pub fn matrix(&self) -> &Matrix { &self.transform.matrix } @@ -917,12 +925,13 @@ pub fn render_base<'gc>(this: DisplayObject<'gc>, context: &mut RenderContext<'_ if cache_info.dirty { let mut transform_stack = TransformStack::new(); transform_stack.push(&Transform { - color_transform: Default::default(), matrix: Matrix { tx: -offset_x, ty: -offset_y, ..cache_info.base_transform.matrix }, + color_transform: Default::default(), + mode_3d: Default::default(), }); let mut offscreen_context = RenderContext { renderer: context.renderer, @@ -955,6 +964,7 @@ pub fn render_base<'gc>(this: DisplayObject<'gc>, context: &mut RenderContext<'_ ..Default::default() }, color_transform: cache_info.base_transform.color_transform, + mode_3d: Default::default(), }, true, PixelSnapping::Always, // cacheAsBitmap forces pixel snapping @@ -1027,6 +1037,7 @@ pub fn apply_standard_mask_and_scroll<'gc, F>( context.transform_stack.push(&Transform { matrix: Matrix::translate(-rect.x_min, -rect.y_min), color_transform: Default::default(), + mode_3d: Default::default(), }); } @@ -1233,6 +1244,10 @@ pub trait TDisplayObject<'gc>: self.base_mut(gc_context).set_matrix(matrix); } + fn set_mode_3d(&self, gc_context: &Mutation<'gc>, mode_3d: bool) { + self.base_mut(gc_context).set_mode_3d(mode_3d); + } + /// Sets the color transform of this object. /// This does NOT invalidate the cache, as it's often used with other operations. /// It is the callers responsibility to do so. diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 23a0552064523..f5f25f09e1549 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -1161,6 +1161,7 @@ impl<'gc> EditText<'gc> { context.transform_stack.push(&Transform { matrix: transform.matrix, color_transform: ColorTransform::IDENTITY, + mode_3d: Default::default(), }); } else { context.transform_stack.push(transform); diff --git a/core/src/display_object/stage.rs b/core/src/display_object/stage.rs index 187c9e139cdc0..c8b7be5cf2df8 100644 --- a/core/src/display_object/stage.rs +++ b/core/src/display_object/stage.rs @@ -829,6 +829,7 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> { context.transform_stack.push(&Transform { matrix: self.0.read().viewport_matrix, color_transform: Default::default(), + mode_3d: Default::default(), }); // All of our Stage3D instances get rendered *underneath* the main stage. diff --git a/render/src/transform.rs b/render/src/transform.rs index 47fb749bd016c..6393fc5dbba89 100644 --- a/render/src/transform.rs +++ b/render/src/transform.rs @@ -5,6 +5,7 @@ use swf::ColorTransform; /// This includes both the transformation matrix and the color transform. #[derive(Clone, Debug, Default)] pub struct Transform { + pub mode_3d: bool, // Indicate whether the latest set matrix is 3D or not. pub matrix: Matrix, pub color_transform: ColorTransform, } @@ -23,6 +24,7 @@ impl TransformStack { self.0.push(Transform { matrix, color_transform, + ..Default::default() }); } diff --git a/render/wgpu/src/surface/commands.rs b/render/wgpu/src/surface/commands.rs index e708c719d9049..c81427188ece0 100644 --- a/render/wgpu/src/surface/commands.rs +++ b/render/wgpu/src/surface/commands.rs @@ -642,6 +642,7 @@ impl CommandHandler for WgpuCommandHandler<'_> { let transform = Transform { matrix: Matrix::scale(target.width() as f32, target.height() as f32), color_transform: Default::default(), + mode_3d: Default::default(), }; let texture = target.take_color_texture(); let bind_group = diff --git a/tests/tests/swfs/avm2/geom_transform/Test.as b/tests/tests/swfs/avm2/geom_transform/Test.as index a2a52baa21a0a..850b2266a0dae 100644 --- a/tests/tests/swfs/avm2/geom_transform/Test.as +++ b/tests/tests/swfs/avm2/geom_transform/Test.as @@ -14,19 +14,26 @@ package { test3D(); trace(""); + testCopy2D(); + trace(""); + + //// FIXME: transformation copy is unsupported now. + // testCopy3D(); + // trace(""); + testImageComparison(); } private function test2D() : void { var sprite2D : Sprite = new Sprite(); - var mat2D : Matrix = new Matrix(); - mat2D.identity(); trace("// sprite2D: new Sprite has null matrix3D and valid matrix"); trace("sprite2D.transform.matrix", sprite2D.transform.matrix); trace("sprite2D.transform.matrix3D", sprite2D.transform.matrix3D); trace("// sprite2D: set identity matrix"); + var mat2D : Matrix = new Matrix(); + mat2D.identity(); sprite2D.transform.matrix = mat2D; trace("sprite2D.transform.matrix", sprite2D.transform.matrix); trace("sprite2D.transform.matrix3D", sprite2D.transform.matrix3D); @@ -42,25 +49,61 @@ package { private function test3D() : void { var sprite3D : Sprite = new Sprite(); - var mat3D : Matrix3D = new Matrix3D(); - mat3D.identity(); trace("// sprite3D: set identity matrix3D"); + var mat3D : Matrix3D = new Matrix3D(); + mat3D.identity(); sprite3D.transform.matrix3D = mat3D; trace("sprite3D.transform.matrix", sprite3D.transform.matrix); trace("sprite3D.transform.matrix3D", sprite3D.transform.matrix3D); trace("sprite3D.transform.matrix3D.rawData", sprite3D.transform.matrix3D.rawData); trace("mat3D.rawData", mat3D.rawData); - trace("// sprite3D: set x = 30, y = 50"); - sprite3D.x = 30; - sprite3D.y = 50; - trace("sprite3D.transform.matrix", sprite3D.transform.matrix); - trace("sprite3D.transform.matrix3D", sprite3D.transform.matrix3D); - trace("sprite3D.transform.matrix3D.rawData", sprite3D.transform.matrix3D.rawData); - trace("mat3D.rawData", mat3D.rawData); + //// FIXME: matrix3D.rawData should be updated by x/y update. + // trace("// sprite3D: set x = 30, y = 50"); + // sprite3D.x = 30; + // sprite3D.y = 50; + // trace("sprite3D.transform.matrix", sprite3D.transform.matrix); + // trace("sprite3D.transform.matrix3D", sprite3D.transform.matrix3D); + // trace("sprite3D.transform.matrix3D.rawData", sprite3D.transform.matrix3D.rawData); + // trace("mat3D", mat3D); } + private function testCopy2D() : void { + var sprite1 : Sprite = new Sprite(); + var sprite2 : Sprite = new Sprite(); + + trace("// Copy2D"); + var mat2D : Matrix = new Matrix(1, 2, 3, 4, 5, 6); + sprite1.transform.matrix = mat2D; + sprite2.transform = sprite1.transform; + trace("sprite1.transform.matrix", sprite1.transform.matrix); + trace("sprite1.transform.matrix3D", sprite1.transform.matrix3D); + trace("sprite2.transform.matrix", sprite2.transform.matrix); + trace("sprite2.transform.matrix3D", sprite2.transform.matrix3D); + } + + //// FIXME + // private function testCopy3D() : void { + // var sprite1 : Sprite = new Sprite(); + // var sprite2 : Sprite = new Sprite(); + // + // trace("// Copy3D"); + // var mat3D : Matrix3D = new Matrix3D(); + // mat3D.appendRotation(1, Vector3D.Z_AXIS); + // mat3D.appendScale(2, 3, 4); + // mat3D.appendTranslation(5, 6, 7); + // sprite1.transform.matrix3D = mat3D; + // sprite2.transform = sprite1.transform; + // trace("sprite1.transform.matrix", sprite1.transform.matrix); + // trace("sprite1.transform.matrix3D", sprite1.transform.matrix3D); + // trace("sprite1.transform.matrix3D.rawData", sprite1.transform.matrix3D.rawData); + // trace("sprite2.transform.matrix", sprite2.transform.matrix); + // trace("sprite2.transform.matrix3D", sprite2.transform.matrix3D); + // trace("sprite2.transform.matrix3D.rawData", sprite2.transform.matrix3D.rawData); + // } + + private function testImageComparison() : void { var m : Matrix3D = new Matrix3D(); diff --git a/tests/tests/swfs/avm2/geom_transform/output.txt b/tests/tests/swfs/avm2/geom_transform/output.txt index 1c4ee5651bdc0..2455e3e6bfa90 100644 --- a/tests/tests/swfs/avm2/geom_transform/output.txt +++ b/tests/tests/swfs/avm2/geom_transform/output.txt @@ -9,13 +9,16 @@ mat2D (a=1, b=0, c=0, d=1, tx=0, ty=0) sprite2D.transform.matrix (a=1, b=0, c=0, d=1, tx=30, ty=50) sprite2D.transform.matrix3D null mat2D (a=1, b=0, c=0, d=1, tx=0, ty=0) + // sprite3D: set identity matrix3D sprite3D.transform.matrix null sprite3D.transform.matrix3D [object Matrix3D] sprite3D.transform.matrix3D.rawData 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 mat3D.rawData 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 -// sprite3D: set x = 30, y = 50 -sprite3D.transform.matrix null -sprite3D.transform.matrix3D [object Matrix3D] -sprite3D.transform.matrix3D.rawData 1,0,0,0,0,1,0,0,0,0,1,0,30,50,0,1 -mat3D.rawData 1,0,0,0,0,1,0,0,0,0,1,0,30,50,0,1 + +// Copy2D +sprite1.transform.matrix (a=1, b=2, c=3, d=4, tx=5, ty=6) +sprite1.transform.matrix3D null +sprite2.transform.matrix (a=1, b=2, c=3, d=4, tx=5, ty=6) +sprite2.transform.matrix3D null + diff --git a/tests/tests/swfs/avm2/geom_transform/test.swf b/tests/tests/swfs/avm2/geom_transform/test.swf index c962f5aff75a60c510757211a06e7a270e8b1257..ae49d7394ebe362c6b9eb552333f9dfe8e50b988 100644 GIT binary patch literal 2037 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 WF{ldxOk}A4Kh%eC%>4&M@pPIS9is&R