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
47 changes: 36 additions & 11 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 { deepClone, ignoreClone } from "./clone/CloneManager";

/**
* Used to implement transformation related functions.
Expand Down Expand Up @@ -615,14 +615,20 @@ export class Transform extends Component {
* 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.
let worldScaleDirty = !this._isParentWorldUniformScaling();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad performance!Violates the core logic of performance optimization

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

this._worldAssociatedChange(worldScaleDirty ? TransformModifyFlags.WmWeWqWs : TransformModifyFlags.WmWeWq);
const children = this._entity._children;
const n = children.length;
if (n > 0) {
worldScaleDirty = worldScaleDirty || !this._isLocalUniformScaling();
for (let i = 0; i < n; i++) {
children[i].transform?._updateWorldPositionAndRotationFlag(worldScaleDirty); // Rotation update of parent entity will trigger world position, rotation and scale update of all child entity.
}
}
}
}
Expand All @@ -632,14 +638,19 @@ export class Transform extends Component {
* 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 _updateWorldPositionAndRotationFlag() {
private _updateWorldPositionAndRotationFlag(worldScaleDirty: boolean): void {
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();
this._worldAssociatedChange(worldScaleDirty ? TransformModifyFlags.WmWpWeWqWs : TransformModifyFlags.WmWpWeWq);
const children = this._entity._children;
const n = children.length;
if (n > 0) {
worldScaleDirty = worldScaleDirty || !this._isLocalUniformScaling();
for (let i = 0; i < n; i++) {
children[i].transform?._updateWorldPositionAndRotationFlag(worldScaleDirty);
}
}
}
}
Expand Down Expand Up @@ -709,6 +720,18 @@ export class Transform extends Component {
return parentCache;
}

private _isLocalUniformScaling(): boolean {
const { x, y, z } = this._scale;
return x === y && y === z;
}

private _isParentWorldUniformScaling(): boolean {
const parent = this._getParentTransform();
if (!parent) return true;
const { x, y, z } = parent.lossyWorldScale;
return x === y && y === z;
}

private _getScaleMatrix(): Matrix3x3 {
const invRotation = Transform._tempQuat0;
const invRotationMat = Transform._tempMat30;
Expand Down Expand Up @@ -739,7 +762,7 @@ export class Transform extends Component {

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 @@ -850,6 +873,8 @@ export enum TransformModifyFlags {
WmWp = 0x84,
/** WorldMatrix | WorldEuler | WorldQuat */
WmWeWq = 0x98,
/** WorldMatrix | WorldEuler | WorldQuat | WorldScale*/
WmWeWqWs = 0xb8,
/** WorldMatrix | WorldPosition | WorldEuler | WorldQuat */
WmWpWeWq = 0x9c,
/** WorldMatrix | WorldScale */
Expand Down
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