Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Transform world scale dirty error #2408

Merged
merged 14 commits into from
Nov 27, 2024
143 changes: 93 additions & 50 deletions packages/core/src/Transform.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { MathUtil, Matrix, Matrix3x3, Quaternion, Vector3 } from "@galacean/engine-math";
import { BoolUpdateFlag } from "./BoolUpdateFlag";
import { deepClone, ignoreClone } from "./clone/CloneManager";
import { Component } from "./Component";
import { Entity } from "./Entity";
import { UpdateFlagManager } from "./UpdateFlagManager";
import { assignmentClone, deepClone, ignoreClone } from "./clone/CloneManager";

/**
* Used to implement transformation related functions.
Expand All @@ -27,12 +27,16 @@
private _rotationQuaternion: Quaternion = new Quaternion();
@deepClone
private _scale: Vector3 = new Vector3(1, 1, 1);
@assignmentClone
private _localUniformScaling: boolean = true;
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved
@deepClone
private _worldPosition: Vector3 = new Vector3();
@deepClone
private _worldRotation: Vector3 = new Vector3();
@deepClone
private _worldRotationQuaternion: Quaternion = new Quaternion();
@assignmentClone
private _worldUniformScaling: boolean = true;
@deepClone
private _lossyWorldScale: Vector3 = new Vector3(1, 1, 1);
@deepClone
Expand All @@ -50,6 +54,7 @@
private _isParentDirty: boolean = true;
@ignoreClone
private _parentTransformCache: Transform = null;
@ignoreClone
private _dirtyFlag: number = TransformModifyFlags.WmWpWeWqWs;

/** @internal */
Expand Down Expand Up @@ -258,20 +263,26 @@
if (this._localMatrix !== value) {
this._localMatrix.copyFrom(value);
}

const { _position: position, _rotationQuaternion: rotationQuaternion, _scale: scale } = this;

Check warning on line 266 in packages/core/src/Transform.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/Transform.ts#L266

Added line #L266 was not covered by tests
// @ts-ignore
this._position._onValueChanged = this._rotationQuaternion._onValueChanged = this._scale._onValueChanged = null;
this._localMatrix.decompose(this._position, this._rotationQuaternion, this._scale);
position._onValueChanged = rotationQuaternion._onValueChanged = scale._onValueChanged = null;
this._localMatrix.decompose(position, rotationQuaternion, scale);

Check warning on line 269 in packages/core/src/Transform.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/Transform.ts#L268-L269

Added lines #L268 - L269 were not covered by tests
// @ts-ignore
this._position._onValueChanged = this._onPositionChanged;
position._onValueChanged = this._onPositionChanged;

Check warning on line 271 in packages/core/src/Transform.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/Transform.ts#L271

Added line #L271 was not covered by tests
// @ts-ignore
this._rotationQuaternion._onValueChanged = this._onRotationQuaternionChanged;
rotationQuaternion._onValueChanged = this._onRotationQuaternionChanged;

Check warning on line 273 in packages/core/src/Transform.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/Transform.ts#L273

Added line #L273 was not covered by tests
// @ts-ignore
this._scale._onValueChanged = this._onScaleChanged;
scale._onValueChanged = this._onScaleChanged;

Check warning on line 275 in packages/core/src/Transform.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/Transform.ts#L275

Added line #L275 was not covered by tests

this._setDirtyFlagTrue(TransformModifyFlags.LocalEuler);
this._setDirtyFlagFalse(TransformModifyFlags.LocalMatrix | TransformModifyFlags.LocalQuat);
this._updateAllWorldFlag();
const localUniformScaling = scale.x === scale.y && scale.y === scale.z;
if (this._localUniformScaling !== localUniformScaling) {
this._localUniformScaling = localUniformScaling;
this._updateAllWorldFlag(TransformModifyFlags.WmWpWeWqWsWus);

Check warning on line 282 in packages/core/src/Transform.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/Transform.ts#L281-L282

Added lines #L281 - L282 were not covered by tests
} else {
this._updateAllWorldFlag(TransformModifyFlags.WmWpWeWqWs);

Check warning on line 284 in packages/core/src/Transform.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/Transform.ts#L284

Added line #L284 was not covered by tests
}
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down Expand Up @@ -306,6 +317,18 @@
this._setDirtyFlagFalse(TransformModifyFlags.WorldMatrix);
}

/**
* @internal
*/
get worldUniformScaling(): boolean {
if (this._isContainDirtyFlag(TransformModifyFlags.WorldUniformScaling)) {
const parent = this._getParentTransform();
this._worldUniformScaling = this._localUniformScaling && (parent ? parent.worldUniformScaling : true);
this._setDirtyFlagFalse(TransformModifyFlags.WorldUniformScaling);
}
return this._worldUniformScaling;
}

/**
* @internal
*/
Expand Down Expand Up @@ -563,7 +586,7 @@
*/
_parentChange(): void {
this._isParentDirty = true;
this._updateAllWorldFlag();
this._updateAllWorldFlag(TransformModifyFlags.WmWpWeWqWsWus);
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down Expand Up @@ -603,10 +626,9 @@
private _updateWorldPositionFlag(): void {
if (!this._isContainDirtyFlags(TransformModifyFlags.WmWp)) {
this._worldAssociatedChange(TransformModifyFlags.WmWp);
const nodeChildren = this._entity._children;
for (let i: number = 0, n: number = nodeChildren.length; i < n; i++) {
nodeChildren[i].transform?._updateWorldPositionFlag();
}
this._entity._children.forEach((child) => {
child.transform?._updateWorldPositionFlag();
});
}
}

Expand All @@ -615,15 +637,19 @@
* Get worldPosition: Will trigger the worldMatrix, local position update of itself and the worldMatrix update of all parent entities.
* Get worldRotationQuaternion: Will trigger the world rotation (in quaternion) update of itself and all parent entities.
* Get worldRotation: Will trigger the world rotation(in euler and quaternion) update of itself and world rotation(in quaternion) update of all parent entities.
* Get worldScale: Will trigger the scaling update of itself and all parent entities.
* In summary, any update of related variables will cause the dirty mark of one of the full process (worldMatrix or worldRotationQuaternion) to be false.
*/
private _updateWorldRotationFlag() {
if (!this._isContainDirtyFlags(TransformModifyFlags.WmWeWq)) {
this._worldAssociatedChange(TransformModifyFlags.WmWeWq);
const nodeChildren = this._entity._children;
for (let i: number = 0, n: number = nodeChildren.length; i < n; i++) {
nodeChildren[i].transform?._updateWorldPositionAndRotationFlag(); // Rotation update of parent entity will trigger world position and rotation update of all child entity.
}
const parent = this._getParentTransform();
const parentWorldUniformScaling = parent ? parent.worldUniformScaling : true;
let flags = parentWorldUniformScaling ? TransformModifyFlags.WmWeWq : TransformModifyFlags.WmWeWqWs;
if (!this._isContainDirtyFlags(flags)) {
this._worldAssociatedChange(flags);
flags = this.worldUniformScaling ? TransformModifyFlags.WmWpWeWq : TransformModifyFlags.WmWpWeWqWs;
this._entity._children.forEach((child) => {
child.transform?._updateWorldPositionAndRotationFlag(flags); // Rotation update of parent entity will trigger world position, rotation and scale update of all child entity.
});
}
}

Expand All @@ -632,15 +658,17 @@
* Get worldPosition: Will trigger the worldMatrix, local position update of itself and the worldMatrix update of all parent entities.
* Get worldRotationQuaternion: Will trigger the world rotation (in quaternion) update of itself and all parent entities.
* Get worldRotation: Will trigger the world rotation(in euler and quaternion) update of itself and world rotation(in quaternion) update of all parent entities.
* Get worldScale: Will trigger the scaling update of itself and all parent entities.
* In summary, any update of related variables will cause the dirty mark of one of the full process (worldMatrix or worldRotationQuaternion) to be false.
* @param flags - Dirty flag
*/
private _updateWorldPositionAndRotationFlag() {
if (!this._isContainDirtyFlags(TransformModifyFlags.WmWpWeWq)) {
this._worldAssociatedChange(TransformModifyFlags.WmWpWeWq);
const nodeChildren = this._entity._children;
for (let i: number = 0, n: number = nodeChildren.length; i < n; i++) {
nodeChildren[i].transform?._updateWorldPositionAndRotationFlag();
}
private _updateWorldPositionAndRotationFlag(flags: TransformModifyFlags): void {
if (!this._isContainDirtyFlags(flags)) {
this._worldAssociatedChange(flags);
flags = this.worldUniformScaling ? TransformModifyFlags.WmWpWeWq : TransformModifyFlags.WmWpWeWqWs;
this._entity._children.forEach((child) => {
child.transform?._updateWorldPositionAndRotationFlag(flags);
});
}
}

Expand All @@ -649,14 +677,15 @@
* Get worldPosition: Will trigger the worldMatrix, local position update of itself and the worldMatrix update of all parent entities.
* Get worldScale: Will trigger the scaling update of itself and all parent entities.
* In summary, any update of related variables will cause the dirty mark of one of the full process (worldMatrix) to be false.
* @param flags - Dirty flag
*/
private _updateWorldScaleFlag() {
if (!this._isContainDirtyFlags(TransformModifyFlags.WmWs)) {
this._worldAssociatedChange(TransformModifyFlags.WmWs);
const nodeChildren = this._entity._children;
for (let i: number = 0, n: number = nodeChildren.length; i < n; i++) {
nodeChildren[i].transform?._updateWorldPositionAndScaleFlag();
}
private _updateWorldScaleFlag(flags: TransformModifyFlags): void {
if (!this._isContainDirtyFlags(flags)) {
this._worldAssociatedChange(flags);
flags |= TransformModifyFlags.WorldPosition;
this._entity._children.forEach((child) => {
child.transform?._updateWorldPositionAndScaleFlag(flags);
});
}
}

Expand All @@ -665,27 +694,27 @@
* Get worldPosition: Will trigger the worldMatrix, local position update of itself and the worldMatrix update of all parent entities.
* Get worldScale: Will trigger the scaling update of itself and all parent entities.
* In summary, any update of related variables will cause the dirty mark of one of the full process (worldMatrix) to be false.
* @param flags - Dirty flag
*/
private _updateWorldPositionAndScaleFlag(): void {
if (!this._isContainDirtyFlags(TransformModifyFlags.WmWpWs)) {
this._worldAssociatedChange(TransformModifyFlags.WmWpWs);
const nodeChildren = this._entity._children;
for (let i: number = 0, n: number = nodeChildren.length; i < n; i++) {
nodeChildren[i].transform?._updateWorldPositionAndScaleFlag();
}
private _updateWorldPositionAndScaleFlag(flags: TransformModifyFlags): void {
if (!this._isContainDirtyFlags(flags)) {
this._worldAssociatedChange(flags);
this._entity._children.forEach((child) => {

Check warning on line 702 in packages/core/src/Transform.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/Transform.ts#L701-L702

Added lines #L701 - L702 were not covered by tests
child.transform?._updateWorldPositionAndScaleFlag(flags);
});
}
}

/**
* Update all world transform property dirty flag, the principle is the same as above.
* @param flags - Dirty flag
*/
private _updateAllWorldFlag(): void {
if (!this._isContainDirtyFlags(TransformModifyFlags.WmWpWeWqWs)) {
this._worldAssociatedChange(TransformModifyFlags.WmWpWeWqWs);
const nodeChildren = this._entity._children;
for (let i: number = 0, n: number = nodeChildren.length; i < n; i++) {
nodeChildren[i].transform?._updateAllWorldFlag();
}
private _updateAllWorldFlag(flags: TransformModifyFlags): void {
if (!this._isContainDirtyFlags(flags)) {
this._worldAssociatedChange(flags);
this._entity._children.forEach((child) => {
child.transform?._updateAllWorldFlag(flags);
});
}
}

Expand Down Expand Up @@ -739,7 +768,7 @@

private _worldAssociatedChange(type: number): void {
this._dirtyFlag |= type;
this._updateFlagManager.dispatch(TransformModifyFlags.WorldMatrix);
this._updateFlagManager.dispatch(type);
}

private _rotateByQuat(rotateQuat: Quaternion, relativeToLocal: boolean): void {
Expand Down Expand Up @@ -828,8 +857,15 @@

@ignoreClone
private _onScaleChanged(): void {
const { x, y, z } = this._scale;
this._setDirtyFlagTrue(TransformModifyFlags.LocalMatrix);
this._updateWorldScaleFlag();
const localUniformScaling = x == y && y == z;
if (this._localUniformScaling !== localUniformScaling) {
this._localUniformScaling = localUniformScaling;
this._updateWorldScaleFlag(TransformModifyFlags.WmWsWus);
} else {
this._updateWorldScaleFlag(TransformModifyFlags.WmWs);
}
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -845,17 +881,24 @@
WorldScale = 0x20,
LocalMatrix = 0x40,
WorldMatrix = 0x80,
WorldUniformScaling = 0x100,

/** WorldMatrix | WorldPosition */
WmWp = 0x84,
/** WorldMatrix | WorldEuler | WorldQuat */
WmWeWq = 0x98,
/** WorldMatrix | WorldEuler | WorldQuat | WorldScale*/
WmWeWqWs = 0xb8,
/** WorldMatrix | WorldPosition | WorldEuler | WorldQuat */
WmWpWeWq = 0x9c,
/** WorldMatrix | WorldScale */
WmWs = 0xa0,
/** WorldMatrix | WorldScale | WorldUniformScaling */
WmWsWus = 0x1a0,
/** WorldMatrix | WorldPosition | WorldScale */
WmWpWs = 0xa4,
/** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale */
WmWpWeWqWs = 0xbc
WmWpWeWqWs = 0xbc,
/** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldUniformScaling */
WmWpWeWqWsWus = 0x1bc
}
12 changes: 12 additions & 0 deletions tests/src/core/Transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ describe("Transform test", function () {
expect(transform.worldUp).to.deep.equal(new Vector3(0, 1, 0));
});

it("World Scale", () => {
const root = scene.createRootEntity();
root.transform.setScale(1, 2, 3);
const entity = root.createChild();
const transform = entity.transform;
transform.setScale(4, 5, 6);
transform.setRotation(0, 0, 0);
expect(transform.lossyWorldScale).to.deep.equal(new Vector3(4, 10, 18));
transform.setRotation(90, 0, 0);
expect(transform.lossyWorldScale).to.deep.equal(new Vector3(4, 15, 12));
});

it("Parent Dirty", () => {
const root1 = scene.createRootEntity();
const root2 = scene.createRootEntity();
Expand Down
Loading