Skip to content

Commit cdb474d

Browse files
authored
Fix transform bug (#678)
* fix: transform
1 parent cda1278 commit cdb474d

9 files changed

+377
-433
lines changed

packages/core/src/Transform.ts

+117-75
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,21 @@ export class Transform extends Component {
6464
* World position.
6565
*/
6666
get worldPosition(): Vector3 {
67+
const worldPosition = this._worldPosition;
6768
if (this._isContainDirtyFlag(TransformFlag.WorldPosition)) {
69+
//@ts-ignore
70+
worldPosition._onValueChanged = null;
6871
if (this._getParentTransform()) {
69-
this.worldMatrix.getTranslation(this._worldPosition);
72+
this.worldMatrix.getTranslation(worldPosition);
7073
} else {
71-
this._position.cloneTo(this._worldPosition);
74+
this._position.cloneTo(worldPosition);
7275
}
76+
//@ts-ignore
77+
worldPosition._onValueChanged = this._onWorldPositionChanged;
7378
this._setDirtyFlagFalse(TransformFlag.WorldPosition);
7479
}
75-
return this._worldPosition;
80+
81+
return worldPosition;
7682
}
7783

7884
set worldPosition(value: Vector3) {
@@ -86,12 +92,18 @@ export class Transform extends Component {
8692
* Rotations are performed around the Y axis, the X axis, and the Z axis, in that order.
8793
*/
8894
get rotation(): Vector3 {
95+
const rotation = this._rotation;
8996
if (this._isContainDirtyFlag(TransformFlag.LocalEuler)) {
90-
this._rotationQuaternion.toEuler(this._rotation);
91-
this._rotation.scale(MathUtil.radToDegreeFactor); // radians to degrees
97+
//@ts-ignore
98+
rotation._onValueChanged = null;
99+
this._rotationQuaternion.toEuler(rotation);
100+
//@ts-ignore
101+
rotation._onValueChanged = this._onRotationChanged;
102+
rotation.scale(MathUtil.radToDegreeFactor); // radians to degrees
92103
this._setDirtyFlagFalse(TransformFlag.LocalEuler);
93104
}
94-
return this._rotation;
105+
106+
return rotation;
95107
}
96108

97109
set rotation(value: Vector3) {
@@ -105,12 +117,17 @@ export class Transform extends Component {
105117
* Rotations are performed around the Y axis, the X axis, and the Z axis, in that order.
106118
*/
107119
get worldRotation(): Vector3 {
120+
const worldRotation = this._worldRotation;
108121
if (this._isContainDirtyFlag(TransformFlag.WorldEuler)) {
109-
this.worldRotationQuaternion.toEuler(this._worldRotation);
110-
this._worldRotation.scale(MathUtil.radToDegreeFactor); // Radian to angle
122+
//@ts-ignore
123+
worldRotation._onValueChanged = null;
124+
this.worldRotationQuaternion.toEuler(worldRotation);
125+
worldRotation.scale(MathUtil.radToDegreeFactor); // Radian to angle
126+
//@ts-ignore
127+
worldRotation._onValueChanged = this._onWorldRotationChanged;
111128
this._setDirtyFlagFalse(TransformFlag.WorldEuler);
112129
}
113-
return this._worldRotation;
130+
return worldRotation;
114131
}
115132

116133
set worldRotation(value: Vector3) {
@@ -123,16 +140,21 @@ export class Transform extends Component {
123140
* Local rotation, defining the rotation by using a unit quaternion.
124141
*/
125142
get rotationQuaternion(): Quaternion {
143+
const rotationQuaternion = this._rotationQuaternion;
126144
if (this._isContainDirtyFlag(TransformFlag.LocalQuat)) {
145+
//@ts-ignore
146+
rotationQuaternion._onValueChanged = null;
127147
Quaternion.rotationEuler(
128148
MathUtil.degreeToRadian(this._rotation.x),
129149
MathUtil.degreeToRadian(this._rotation.y),
130150
MathUtil.degreeToRadian(this._rotation.z),
131-
this._rotationQuaternion
151+
rotationQuaternion
132152
);
153+
//@ts-ignore
154+
rotationQuaternion._onValueChanged = this._onRotationQuaternionChanged;
133155
this._setDirtyFlagFalse(TransformFlag.LocalQuat);
134156
}
135-
return this._rotationQuaternion;
157+
return rotationQuaternion;
136158
}
137159

138160
set rotationQuaternion(value: Quaternion) {
@@ -145,16 +167,21 @@ export class Transform extends Component {
145167
* World rotation, defining the rotation by using a unit quaternion.
146168
*/
147169
get worldRotationQuaternion(): Quaternion {
170+
const worldRotationQuaternion = this._worldRotationQuaternion;
148171
if (this._isContainDirtyFlag(TransformFlag.WorldQuat)) {
172+
//@ts-ignore
173+
worldRotationQuaternion._onValueChanged = null;
149174
const parent = this._getParentTransform();
150175
if (parent != null) {
151-
Quaternion.multiply(parent.worldRotationQuaternion, this.rotationQuaternion, this._worldRotationQuaternion);
176+
Quaternion.multiply(parent.worldRotationQuaternion, this.rotationQuaternion, worldRotationQuaternion);
152177
} else {
153-
this.rotationQuaternion.cloneTo(this._worldRotationQuaternion);
178+
this.rotationQuaternion.cloneTo(worldRotationQuaternion);
154179
}
180+
//@ts-ignore
181+
worldRotationQuaternion._onValueChanged = this._onWorldRotationQuaternionChanged;
155182
this._setDirtyFlagFalse(TransformFlag.WorldQuat);
156183
}
157-
return this._worldRotationQuaternion;
184+
return worldRotationQuaternion;
158185
}
159186

160187
set worldRotationQuaternion(value: Quaternion) {
@@ -211,7 +238,9 @@ export class Transform extends Component {
211238
if (this._localMatrix !== value) {
212239
value.cloneTo(this._localMatrix);
213240
}
241+
214242
this._localMatrix.decompose(this._position, this._rotationQuaternion, this._scale);
243+
215244
this._setDirtyFlagTrue(TransformFlag.LocalEuler);
216245
this._setDirtyFlagFalse(TransformFlag.LocalMatrix);
217246
this._updateAllWorldFlag();
@@ -252,69 +281,28 @@ export class Transform extends Component {
252281
constructor(entity: Entity) {
253282
super(entity);
254283

255-
//@ts-ignore
256-
this._position._onValueChanged = () => {
257-
this._setDirtyFlagTrue(TransformFlag.LocalMatrix);
258-
this._updateWorldPositionFlag();
259-
};
284+
this._onPositionChanged = this._onPositionChanged.bind(this);
285+
this._onWorldPositionChanged = this._onWorldPositionChanged.bind(this);
286+
this._onRotationChanged = this._onRotationChanged.bind(this);
287+
this._onWorldRotationChanged = this._onWorldRotationChanged.bind(this);
288+
this._onRotationQuaternionChanged = this._onRotationQuaternionChanged.bind(this);
289+
this._onWorldRotationQuaternionChanged = this._onWorldRotationQuaternionChanged.bind(this);
290+
this._onScaleChanged = this._onScaleChanged.bind(this);
260291

261292
//@ts-ignore
262-
this._worldPosition._onValueChanged = () => {
263-
const worldPosition = this._worldPosition;
264-
const parent = this._getParentTransform();
265-
if (parent) {
266-
Matrix.invert(parent.worldMatrix, Transform._tempMat41);
267-
Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
268-
} else {
269-
worldPosition.cloneTo(this._position);
270-
}
271-
this._setDirtyFlagFalse(TransformFlag.WorldPosition);
272-
};
273-
293+
this._position._onValueChanged = this._onPositionChanged;
274294
//@ts-ignore
275-
this._rotation._onValueChanged = () => {
276-
this._setDirtyFlagTrue(TransformFlag.LocalMatrix | TransformFlag.LocalQuat);
277-
this._setDirtyFlagFalse(TransformFlag.LocalEuler);
278-
this._updateWorldRotationFlag();
279-
};
280-
295+
this._worldPosition._onValueChanged = this._onWorldPositionChanged;
281296
//@ts-ignore
282-
this._worldRotation._onValueChanged = () => {
283-
const worldRotation = this._worldRotation;
284-
Quaternion.rotationEuler(
285-
MathUtil.degreeToRadian(worldRotation.x),
286-
MathUtil.degreeToRadian(worldRotation.y),
287-
MathUtil.degreeToRadian(worldRotation.z),
288-
this._worldRotationQuaternion
289-
);
290-
this._setDirtyFlagFalse(TransformFlag.WorldEuler);
291-
};
292-
297+
this._rotation._onValueChanged = this._onRotationChanged;
293298
//@ts-ignore
294-
this._rotationQuaternion._onValueChanged = () => {
295-
this._setDirtyFlagTrue(TransformFlag.LocalMatrix | TransformFlag.LocalEuler);
296-
this._setDirtyFlagFalse(TransformFlag.LocalQuat);
297-
this._updateWorldRotationFlag();
298-
};
299-
299+
this._worldRotation._onValueChanged = this._onWorldRotationChanged;
300300
//@ts-ignore
301-
this._worldRotationQuaternion._onValueChanged = () => {
302-
const worldRotationQuaternion = this._worldRotationQuaternion;
303-
const parent = this._getParentTransform();
304-
if (parent) {
305-
Quaternion.invert(parent.worldRotationQuaternion, Transform._tempQuat0);
306-
Quaternion.multiply(worldRotationQuaternion, Transform._tempQuat0, this._rotationQuaternion);
307-
} else {
308-
worldRotationQuaternion.cloneTo(this._rotationQuaternion);
309-
}
310-
this._setDirtyFlagFalse(TransformFlag.WorldQuat);
311-
};
312-
301+
this._rotationQuaternion._onValueChanged = this._onRotationQuaternionChanged;
313302
//@ts-ignore
314-
this._scale._onValueChanged = () => {
315-
this._setDirtyFlagTrue(TransformFlag.LocalMatrix);
316-
this._updateWorldScaleFlag();
317-
};
303+
this._worldRotationQuaternion._onValueChanged = this._onWorldRotationQuaternionChanged;
304+
//@ts-ignore
305+
this._scale._onValueChanged = this._onScaleChanged;
318306
}
319307

320308
/**
@@ -511,11 +499,10 @@ export class Transform extends Component {
511499
return;
512500
}
513501
const rotMat = Transform._tempMat43;
514-
const worldRotationQuaternion = this._worldRotationQuaternion;
515502

516503
worldUp = worldUp ?? Transform._tempVec3.setValue(0, 1, 0);
517504
Matrix.lookAt(position, worldPosition, worldUp, rotMat);
518-
rotMat.getRotation(worldRotationQuaternion).invert();
505+
Quaternion.invert(rotMat.getRotation(Transform._tempQuat0), this._worldRotationQuaternion);
519506
}
520507

521508
/**
@@ -695,10 +682,8 @@ export class Transform extends Component {
695682
private _rotateByQuat(rotateQuat: Quaternion, relativeToLocal: boolean) {
696683
if (relativeToLocal) {
697684
Quaternion.multiply(this.rotationQuaternion, rotateQuat, this._rotationQuaternion);
698-
this.rotationQuaternion = this._rotationQuaternion;
699685
} else {
700686
Quaternion.multiply(this.worldRotationQuaternion, rotateQuat, this._worldRotationQuaternion);
701-
this.worldRotationQuaternion = this._worldRotationQuaternion;
702687
}
703688
}
704689

@@ -716,8 +701,65 @@ export class Transform extends Component {
716701
Quaternion.rotationEuler(x * radFactor, y * radFactor, z * radFactor, rotQuat);
717702
this._rotateByQuat(rotQuat, relativeToLocal);
718703
}
719-
}
720704

705+
private _onPositionChanged(): void {
706+
this._setDirtyFlagTrue(TransformFlag.LocalMatrix);
707+
this._updateWorldPositionFlag();
708+
}
709+
710+
private _onWorldPositionChanged(): void {
711+
const worldPosition = this._worldPosition;
712+
const parent = this._getParentTransform();
713+
if (parent) {
714+
Matrix.invert(parent.worldMatrix, Transform._tempMat41);
715+
Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
716+
} else {
717+
worldPosition.cloneTo(this._position);
718+
}
719+
this._setDirtyFlagFalse(TransformFlag.WorldPosition);
720+
}
721+
722+
private _onRotationChanged(): void {
723+
this._setDirtyFlagTrue(TransformFlag.LocalMatrix | TransformFlag.LocalQuat);
724+
this._setDirtyFlagFalse(TransformFlag.LocalEuler);
725+
this._updateWorldRotationFlag();
726+
}
727+
728+
private _onWorldRotationChanged(): void {
729+
const worldRotation = this._worldRotation;
730+
Quaternion.rotationEuler(
731+
MathUtil.degreeToRadian(worldRotation.x),
732+
MathUtil.degreeToRadian(worldRotation.y),
733+
MathUtil.degreeToRadian(worldRotation.z),
734+
this._worldRotationQuaternion
735+
);
736+
this._setDirtyFlagFalse(TransformFlag.WorldEuler);
737+
}
738+
739+
private _onRotationQuaternionChanged(): void {
740+
this._setDirtyFlagTrue(TransformFlag.LocalMatrix | TransformFlag.LocalEuler);
741+
this._setDirtyFlagFalse(TransformFlag.LocalQuat);
742+
this._updateWorldRotationFlag();
743+
}
744+
745+
private _onWorldRotationQuaternionChanged(): void {
746+
const worldRotationQuaternion = this._worldRotationQuaternion;
747+
const parent = this._getParentTransform();
748+
if (parent) {
749+
const invParentQuaternion = Transform._tempQuat0;
750+
Quaternion.invert(parent.worldRotationQuaternion, invParentQuaternion);
751+
Quaternion.multiply(worldRotationQuaternion, invParentQuaternion, this._rotationQuaternion);
752+
} else {
753+
worldRotationQuaternion.cloneTo(this._rotationQuaternion);
754+
}
755+
this._setDirtyFlagFalse(TransformFlag.WorldQuat);
756+
}
757+
758+
private _onScaleChanged(): void {
759+
this._setDirtyFlagTrue(TransformFlag.LocalMatrix);
760+
this._updateWorldScaleFlag();
761+
}
762+
}
721763
/**
722764
* Dirty flag of transform.
723765
*/

packages/core/tests/Camera.test.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe("camera test", function () {
2424
expect(camera.aspectRatio).toEqual(1);
2525
expect(camera._renderPipeline).not.toBeUndefined();
2626
expect(camera.entity.transform.worldPosition).not.toBeUndefined();
27-
expect(camera.viewport).toEqual({ x: 0, y: 0, z: 1, w: 1 });
27+
vector4CloseTo(camera.viewport, new Vector4(0, 0, 1, 1));
2828
expect(camera.fieldOfView).toEqual(45);
2929
expect(camera.isOrthographic).toEqual(false);
3030
});
@@ -94,12 +94,10 @@ describe("camera test", function () {
9494
camera.projectionMatrix;
9595
//@ts-ignore
9696
camera._orthographicSize = 4;
97-
9897
const width = (camera.orthographicSize * 400) / 400;
9998
const height = camera.orthographicSize;
10099
const result = new Matrix();
101100
Matrix.ortho(-width, width, -height, height, camera.nearClipPlane, camera.farClipPlane, result);
102-
103101
expect(camera.projectionMatrix).not.toEqual(result);
104102
});
105103

@@ -138,7 +136,7 @@ describe("camera test", function () {
138136
);
139137
camera.entity.transform.worldMatrix = new Matrix();
140138
const out = camera.worldToViewportPoint(new Vector3(1, 1, -100), new Vector3());
141-
expect(out).toEqual({ x: 0.5154036617279053, y: 0.4913397705554962, z: 100 });
139+
vector3CloseTo(out, new Vector3(0.5154036617279053, 0.4913397705554962, 100));
142140
});
143141

144142
it("viewport to world", () => {
@@ -186,7 +184,6 @@ describe("camera test", function () {
186184
);
187185
camera.entity.transform.worldMatrix = mat;
188186
const ray = camera.viewportPointToRay(new Vector2(0.4472140669822693, 0.4436090290546417), new Ray());
189-
190187
arrayCloseTo(
191188
[ray.origin.x, ray.origin.y, ray.origin.z],
192189
Float32Array.from([0.0017142786925635912, 5.017240249493299, 17.047073454417177])
@@ -221,3 +218,16 @@ function arrayCloseTo(arr1: ArrayLike<number>, arr2: ArrayLike<number>) {
221218
expect(arr1[i]).toBeCloseTo(arr2[i]);
222219
}
223220
}
221+
222+
function vector3CloseTo(vec1: Vector3, vec2: Vector3): void {
223+
expect(vec1.x).toBeCloseTo(vec2.x);
224+
expect(vec1.y).toBeCloseTo(vec2.y);
225+
expect(vec1.z).toBeCloseTo(vec2.z);
226+
}
227+
228+
function vector4CloseTo(vec1: Vector4, vec2: Vector4): void {
229+
expect(vec1.x).toBeCloseTo(vec2.x);
230+
expect(vec1.y).toBeCloseTo(vec2.y);
231+
expect(vec1.z).toBeCloseTo(vec2.z);
232+
expect(vec1.w).toBeCloseTo(vec2.w);
233+
}

0 commit comments

Comments
 (0)