-
-
Notifications
You must be signed in to change notification settings - Fork 311
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
Changes from 7 commits
25bc421
47759ec
57572dc
ea32723
9ed1133
e1d2d24
c62b4de
a135e63
2040b50
7f06062
ac54dcc
2f71d7a
fb35241
6c2ea9a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. | ||
|
@@ -50,6 +50,11 @@ | |
private _isParentDirty: boolean = true; | ||
@ignoreClone | ||
private _parentTransformCache: Transform = null; | ||
@ignoreClone | ||
private _worldUniformScaling: boolean = true; | ||
@ignoreClone | ||
private _localUniformScaling: boolean = true; | ||
@ignoreClone | ||
private _dirtyFlag: number = TransformModifyFlags.WmWpWeWqWs; | ||
|
||
/** @internal */ | ||
|
@@ -563,6 +568,13 @@ | |
*/ | ||
_parentChange(): void { | ||
this._isParentDirty = true; | ||
const worldUniformScaling = this._localUniformScaling; | ||
if (worldUniformScaling !== this._worldUniformScaling) { | ||
this._worldUniformScaling = worldUniformScaling; | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateWorldPositionScaleFlagAndUniformScaling(worldUniformScaling); | ||
}); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Add test coverage for uniform scaling propagation The test file
Example locations needing coverage:
🔗 Analysis chainAdd test coverage for uniform scaling propagation. The parent change handling for uniform scaling propagation is not covered by tests. Consider adding test cases for:
🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check existing test coverage for parent-child scaling
rg -l "test.*parent.*scale" tests/
Length of output: 36 Script: #!/bin/bash
# Let's try a broader search for transform and scale related tests
rg -l "transform.*scale|scale.*transform" tests/
# Also search for any test files related to Transform
fd "transform.*test" tests/
# Check for existing test implementations involving parent-child relationships
rg -A 5 "parent.*child.*transform|transform.*parent.*child" tests/
Length of output: 304 Script: #!/bin/bash
# Let's examine the Transform test file content specifically
cat tests/src/core/Transform.test.ts
# Also check the implementation of _updateWorldPositionScaleFlagAndUniformScaling
ast-grep --pattern 'class Transform {
$$$
_updateWorldPositionScaleFlagAndUniformScaling($_) {
$$$
}
$$$
}'
Length of output: 2408 🧰 Tools🪛 GitHub Check: codecov/patch[warning] 573-574: packages/core/src/Transform.ts#L573-L574 |
||
this._updateAllWorldFlag(); | ||
} | ||
|
||
|
@@ -603,10 +615,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(); | ||
}); | ||
} | ||
} | ||
|
||
|
@@ -615,15 +626,16 @@ | |
* 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 dirtyFlag = this._worldUniformScaling ? TransformModifyFlags.WmWeWq : TransformModifyFlags.WmWeWqWs; | ||
if (!this._isContainDirtyFlags(dirtyFlag)) { | ||
this._worldAssociatedChange(dirtyFlag); | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateWorldPositionAndRotationFlag(); // Rotation update of parent entity will trigger world position, rotation and scale update of all child entity. | ||
}); | ||
} | ||
} | ||
|
||
|
@@ -632,15 +644,16 @@ | |
* 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() { | ||
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(): void { | ||
const dirtyFlag = this._worldUniformScaling ? TransformModifyFlags.WmWpWeWq : TransformModifyFlags.WmWpWeWqWs; | ||
if (!this._isContainDirtyFlags(dirtyFlag)) { | ||
this._worldAssociatedChange(dirtyFlag); | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateWorldPositionAndRotationFlag(); | ||
}); | ||
} | ||
} | ||
|
||
|
@@ -649,14 +662,49 @@ | |
* 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 worldUniformScaling - Whether the world scaling is uniform | ||
*/ | ||
private _updateWorldScaleFlag() { | ||
private _updateWorldScaleFlagAndUniformScaling(worldUniformScaling: boolean): void { | ||
this._isContainDirtyFlags(TransformModifyFlags.WmWs) || this._worldAssociatedChange(TransformModifyFlags.WmWs); | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateWorldPositionScaleFlagAndUniformScaling(worldUniformScaling); | ||
}); | ||
} | ||
|
||
/** | ||
* Get worldMatrix: Will trigger the worldMatrix update of itself and all parent entities. | ||
* 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 parentWorldUniformScaling - Whether the parent world scaling is uniform | ||
*/ | ||
private _updateWorldPositionScaleFlagAndUniformScaling(parentWorldUniformScaling: boolean): void { | ||
this._isContainDirtyFlags(TransformModifyFlags.WmWpWs) || this._worldAssociatedChange(TransformModifyFlags.WmWpWs); | ||
const worldUniformScaling = this._localUniformScaling && parentWorldUniformScaling; | ||
if (worldUniformScaling !== this._worldUniformScaling) { | ||
this._worldUniformScaling = worldUniformScaling; | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateWorldPositionScaleFlagAndUniformScaling(worldUniformScaling); | ||
}); | ||
} else { | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateWorldPositionAndScaleFlag(); | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Get worldMatrix: Will trigger the worldMatrix update of itself and all parent entities. | ||
* 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. | ||
*/ | ||
private _updateWorldScaleFlag(): void { | ||
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(); | ||
} | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateWorldPositionAndScaleFlag(); | ||
}); | ||
} | ||
} | ||
|
||
|
@@ -669,10 +717,9 @@ | |
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(); | ||
} | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateWorldPositionAndScaleFlag(); | ||
}); | ||
} | ||
} | ||
|
||
|
@@ -682,10 +729,9 @@ | |
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(); | ||
} | ||
this._entity._children.forEach((child) => { | ||
child.transform?._updateAllWorldFlag(); | ||
}); | ||
} | ||
} | ||
|
||
|
@@ -739,7 +785,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 { | ||
|
@@ -828,8 +874,23 @@ | |
|
||
@ignoreClone | ||
private _onScaleChanged(): void { | ||
const { x, y, z } = this._scale; | ||
this._setDirtyFlagTrue(TransformModifyFlags.LocalMatrix); | ||
this._updateWorldScaleFlag(); | ||
const isLocalUniformScaling = x == y && y == z; | ||
if (this._localUniformScaling !== isLocalUniformScaling) { | ||
this._localUniformScaling = isLocalUniformScaling; | ||
const parentTransform = this._getParentTransform(); | ||
const parentWorldUniformScaling = parentTransform ? parentTransform._worldUniformScaling : true; | ||
const worldUniformScaling = parentWorldUniformScaling && isLocalUniformScaling; | ||
if (this._worldUniformScaling !== worldUniformScaling) { | ||
this._worldUniformScaling = worldUniformScaling; | ||
this._updateWorldScaleFlagAndUniformScaling(worldUniformScaling); | ||
} else { | ||
this._updateWorldScaleFlag(); | ||
} | ||
} else { | ||
this._updateWorldScaleFlag(); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -850,6 +911,8 @@ | |
WmWp = 0x84, | ||
/** WorldMatrix | WorldEuler | WorldQuat */ | ||
WmWeWq = 0x98, | ||
/** WorldMatrix | WorldEuler | WorldQuat | WorldScale*/ | ||
WmWeWqWs = 0xb8, | ||
/** WorldMatrix | WorldPosition | WorldEuler | WorldQuat */ | ||
WmWpWeWq = 0x9c, | ||
/** WorldMatrix | WorldScale */ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Add test coverage for scale-related code paths.
Several critical code paths lack test coverage:
Would you like me to help generate comprehensive test cases for these scenarios?
Also applies to: 685-686, 690-690, 720-720, 889-889
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 573-574: packages/core/src/Transform.ts#L573-L574
Added lines #L573 - L574 were not covered by tests