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 bug #678

Merged
merged 23 commits into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 117 additions & 75 deletions packages/core/src/Transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,21 @@ export class Transform extends Component {
* World position.
*/
get worldPosition(): Vector3 {
const worldPosition = this._worldPosition;
if (this._isContainDirtyFlag(TransformFlag.WorldPosition)) {
//@ts-ignore
worldPosition._onValueChanged = null;
if (this._getParentTransform()) {
this.worldMatrix.getTranslation(this._worldPosition);
this.worldMatrix.getTranslation(worldPosition);
} else {
this._position.cloneTo(this._worldPosition);
this._position.cloneTo(worldPosition);
}
//@ts-ignore
worldPosition._onValueChanged = this._onWorldPositionChanged;
this._setDirtyFlagFalse(TransformFlag.WorldPosition);
}
return this._worldPosition;

return worldPosition;
}

set worldPosition(value: Vector3) {
Expand All @@ -86,12 +92,18 @@ export class Transform extends Component {
* Rotations are performed around the Y axis, the X axis, and the Z axis, in that order.
*/
get rotation(): Vector3 {
const rotation = this._rotation;
if (this._isContainDirtyFlag(TransformFlag.LocalEuler)) {
this._rotationQuaternion.toEuler(this._rotation);
this._rotation.scale(MathUtil.radToDegreeFactor); // radians to degrees
//@ts-ignore
rotation._onValueChanged = null;
this._rotationQuaternion.toEuler(rotation);
//@ts-ignore
rotation._onValueChanged = this._onRotationChanged;
rotation.scale(MathUtil.radToDegreeFactor); // radians to degrees
this._setDirtyFlagFalse(TransformFlag.LocalEuler);
}
return this._rotation;

return rotation;
}

set rotation(value: Vector3) {
Expand All @@ -105,12 +117,17 @@ export class Transform extends Component {
* Rotations are performed around the Y axis, the X axis, and the Z axis, in that order.
*/
get worldRotation(): Vector3 {
const worldRotation = this._worldRotation;
if (this._isContainDirtyFlag(TransformFlag.WorldEuler)) {
this.worldRotationQuaternion.toEuler(this._worldRotation);
this._worldRotation.scale(MathUtil.radToDegreeFactor); // Radian to angle
//@ts-ignore
worldRotation._onValueChanged = null;
this.worldRotationQuaternion.toEuler(worldRotation);
worldRotation.scale(MathUtil.radToDegreeFactor); // Radian to angle
//@ts-ignore
worldRotation._onValueChanged = this._onWorldRotationChanged;
this._setDirtyFlagFalse(TransformFlag.WorldEuler);
}
return this._worldRotation;
return worldRotation;
}

set worldRotation(value: Vector3) {
Expand All @@ -123,16 +140,21 @@ export class Transform extends Component {
* Local rotation, defining the rotation by using a unit quaternion.
*/
get rotationQuaternion(): Quaternion {
const rotationQuaternion = this._rotationQuaternion;
if (this._isContainDirtyFlag(TransformFlag.LocalQuat)) {
//@ts-ignore
rotationQuaternion._onValueChanged = null;
Quaternion.rotationEuler(
MathUtil.degreeToRadian(this._rotation.x),
MathUtil.degreeToRadian(this._rotation.y),
MathUtil.degreeToRadian(this._rotation.z),
this._rotationQuaternion
rotationQuaternion
);
//@ts-ignore
rotationQuaternion._onValueChanged = this._onRotationQuaternionChanged;
this._setDirtyFlagFalse(TransformFlag.LocalQuat);
}
return this._rotationQuaternion;
return rotationQuaternion;
}

set rotationQuaternion(value: Quaternion) {
Expand All @@ -145,16 +167,21 @@ export class Transform extends Component {
* World rotation, defining the rotation by using a unit quaternion.
*/
get worldRotationQuaternion(): Quaternion {
const worldRotationQuaternion = this._worldRotationQuaternion;
if (this._isContainDirtyFlag(TransformFlag.WorldQuat)) {
//@ts-ignore
worldRotationQuaternion._onValueChanged = null;
const parent = this._getParentTransform();
if (parent != null) {
Quaternion.multiply(parent.worldRotationQuaternion, this.rotationQuaternion, this._worldRotationQuaternion);
Quaternion.multiply(parent.worldRotationQuaternion, this.rotationQuaternion, worldRotationQuaternion);
} else {
this.rotationQuaternion.cloneTo(this._worldRotationQuaternion);
this.rotationQuaternion.cloneTo(worldRotationQuaternion);
}
//@ts-ignore
worldRotationQuaternion._onValueChanged = this._onWorldRotationQuaternionChanged;
this._setDirtyFlagFalse(TransformFlag.WorldQuat);
}
return this._worldRotationQuaternion;
return worldRotationQuaternion;
}

set worldRotationQuaternion(value: Quaternion) {
Expand Down Expand Up @@ -211,7 +238,9 @@ export class Transform extends Component {
if (this._localMatrix !== value) {
value.cloneTo(this._localMatrix);
}

this._localMatrix.decompose(this._position, this._rotationQuaternion, this._scale);

this._setDirtyFlagTrue(TransformFlag.LocalEuler);
this._setDirtyFlagFalse(TransformFlag.LocalMatrix);
this._updateAllWorldFlag();
Expand Down Expand Up @@ -252,69 +281,28 @@ export class Transform extends Component {
constructor(entity: Entity) {
super(entity);

//@ts-ignore
this._position._onValueChanged = () => {
this._setDirtyFlagTrue(TransformFlag.LocalMatrix);
this._updateWorldPositionFlag();
};
this._onPositionChanged = this._onPositionChanged.bind(this);
this._onWorldPositionChanged = this._onWorldPositionChanged.bind(this);
this._onRotationChanged = this._onRotationChanged.bind(this);
this._onWorldRotationChanged = this._onWorldRotationChanged.bind(this);
this._onRotationQuaternionChanged = this._onRotationQuaternionChanged.bind(this);
this._onWorldRotationQuaternionChanged = this._onWorldRotationQuaternionChanged.bind(this);
this._onScaleChanged = this._onScaleChanged.bind(this);

//@ts-ignore
this._worldPosition._onValueChanged = () => {
const worldPosition = this._worldPosition;
const parent = this._getParentTransform();
if (parent) {
Matrix.invert(parent.worldMatrix, Transform._tempMat41);
Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
} else {
worldPosition.cloneTo(this._position);
}
this._setDirtyFlagFalse(TransformFlag.WorldPosition);
};

this._position._onValueChanged = this._onPositionChanged;
//@ts-ignore
this._rotation._onValueChanged = () => {
this._setDirtyFlagTrue(TransformFlag.LocalMatrix | TransformFlag.LocalQuat);
this._setDirtyFlagFalse(TransformFlag.LocalEuler);
this._updateWorldRotationFlag();
};

this._worldPosition._onValueChanged = this._onWorldPositionChanged;
//@ts-ignore
this._worldRotation._onValueChanged = () => {
const worldRotation = this._worldRotation;
Quaternion.rotationEuler(
MathUtil.degreeToRadian(worldRotation.x),
MathUtil.degreeToRadian(worldRotation.y),
MathUtil.degreeToRadian(worldRotation.z),
this._worldRotationQuaternion
);
this._setDirtyFlagFalse(TransformFlag.WorldEuler);
};

this._rotation._onValueChanged = this._onRotationChanged;
//@ts-ignore
this._rotationQuaternion._onValueChanged = () => {
this._setDirtyFlagTrue(TransformFlag.LocalMatrix | TransformFlag.LocalEuler);
this._setDirtyFlagFalse(TransformFlag.LocalQuat);
this._updateWorldRotationFlag();
};

this._worldRotation._onValueChanged = this._onWorldRotationChanged;
//@ts-ignore
this._worldRotationQuaternion._onValueChanged = () => {
const worldRotationQuaternion = this._worldRotationQuaternion;
const parent = this._getParentTransform();
if (parent) {
Quaternion.invert(parent.worldRotationQuaternion, Transform._tempQuat0);
Quaternion.multiply(worldRotationQuaternion, Transform._tempQuat0, this._rotationQuaternion);
} else {
worldRotationQuaternion.cloneTo(this._rotationQuaternion);
}
this._setDirtyFlagFalse(TransformFlag.WorldQuat);
};

this._rotationQuaternion._onValueChanged = this._onRotationQuaternionChanged;
//@ts-ignore
this._scale._onValueChanged = () => {
this._setDirtyFlagTrue(TransformFlag.LocalMatrix);
this._updateWorldScaleFlag();
};
this._worldRotationQuaternion._onValueChanged = this._onWorldRotationQuaternionChanged;
//@ts-ignore
this._scale._onValueChanged = this._onScaleChanged;
}

/**
Expand Down Expand Up @@ -511,11 +499,10 @@ export class Transform extends Component {
return;
}
const rotMat = Transform._tempMat43;
const worldRotationQuaternion = this._worldRotationQuaternion;

worldUp = worldUp ?? Transform._tempVec3.setValue(0, 1, 0);
Matrix.lookAt(position, worldPosition, worldUp, rotMat);
rotMat.getRotation(worldRotationQuaternion).invert();
Quaternion.invert(rotMat.getRotation(Transform._tempQuat0), this._worldRotationQuaternion);
}

/**
Expand Down Expand Up @@ -695,10 +682,8 @@ export class Transform extends Component {
private _rotateByQuat(rotateQuat: Quaternion, relativeToLocal: boolean) {
if (relativeToLocal) {
Quaternion.multiply(this.rotationQuaternion, rotateQuat, this._rotationQuaternion);
this.rotationQuaternion = this._rotationQuaternion;
} else {
Quaternion.multiply(this.worldRotationQuaternion, rotateQuat, this._worldRotationQuaternion);
this.worldRotationQuaternion = this._worldRotationQuaternion;
}
}

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

private _onPositionChanged(): void {
this._setDirtyFlagTrue(TransformFlag.LocalMatrix);
this._updateWorldPositionFlag();
}

private _onWorldPositionChanged(): void {
const worldPosition = this._worldPosition;
const parent = this._getParentTransform();
if (parent) {
Matrix.invert(parent.worldMatrix, Transform._tempMat41);
Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
} else {
worldPosition.cloneTo(this._position);
}
this._setDirtyFlagFalse(TransformFlag.WorldPosition);
}

private _onRotationChanged(): void {
this._setDirtyFlagTrue(TransformFlag.LocalMatrix | TransformFlag.LocalQuat);
this._setDirtyFlagFalse(TransformFlag.LocalEuler);
this._updateWorldRotationFlag();
}

private _onWorldRotationChanged(): void {
const worldRotation = this._worldRotation;
Quaternion.rotationEuler(
MathUtil.degreeToRadian(worldRotation.x),
MathUtil.degreeToRadian(worldRotation.y),
MathUtil.degreeToRadian(worldRotation.z),
this._worldRotationQuaternion
);
this._setDirtyFlagFalse(TransformFlag.WorldEuler);
}

private _onRotationQuaternionChanged(): void {
this._setDirtyFlagTrue(TransformFlag.LocalMatrix | TransformFlag.LocalEuler);
this._setDirtyFlagFalse(TransformFlag.LocalQuat);
this._updateWorldRotationFlag();
}

private _onWorldRotationQuaternionChanged(): void {
const worldRotationQuaternion = this._worldRotationQuaternion;
const parent = this._getParentTransform();
if (parent) {
const invParentQuaternion = Transform._tempQuat0;
Quaternion.invert(parent.worldRotationQuaternion, invParentQuaternion);
Quaternion.multiply(worldRotationQuaternion, invParentQuaternion, this._rotationQuaternion);
} else {
worldRotationQuaternion.cloneTo(this._rotationQuaternion);
}
this._setDirtyFlagFalse(TransformFlag.WorldQuat);
}

private _onScaleChanged(): void {
this._setDirtyFlagTrue(TransformFlag.LocalMatrix);
this._updateWorldScaleFlag();
}
}
/**
* Dirty flag of transform.
*/
Expand Down
20 changes: 15 additions & 5 deletions packages/core/tests/Camera.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe("camera test", function () {
expect(camera.aspectRatio).toEqual(1);
expect(camera._renderPipeline).not.toBeUndefined();
expect(camera.entity.transform.worldPosition).not.toBeUndefined();
expect(camera.viewport).toEqual({ x: 0, y: 0, z: 1, w: 1 });
vector4CloseTo(camera.viewport, new Vector4(0, 0, 1, 1));
expect(camera.fieldOfView).toEqual(45);
expect(camera.isOrthographic).toEqual(false);
});
Expand Down Expand Up @@ -94,12 +94,10 @@ describe("camera test", function () {
camera.projectionMatrix;
//@ts-ignore
camera._orthographicSize = 4;

const width = (camera.orthographicSize * 400) / 400;
const height = camera.orthographicSize;
const result = new Matrix();
Matrix.ortho(-width, width, -height, height, camera.nearClipPlane, camera.farClipPlane, result);

expect(camera.projectionMatrix).not.toEqual(result);
});

Expand Down Expand Up @@ -138,7 +136,7 @@ describe("camera test", function () {
);
camera.entity.transform.worldMatrix = new Matrix();
const out = camera.worldToViewportPoint(new Vector3(1, 1, -100), new Vector3());
expect(out).toEqual({ x: 0.5154036617279053, y: 0.4913397705554962, z: 100 });
vector3CloseTo(out, new Vector3(0.5154036617279053, 0.4913397705554962, 100));
});

it("viewport to world", () => {
Expand Down Expand Up @@ -186,7 +184,6 @@ describe("camera test", function () {
);
camera.entity.transform.worldMatrix = mat;
const ray = camera.viewportPointToRay(new Vector2(0.4472140669822693, 0.4436090290546417), new Ray());

arrayCloseTo(
[ray.origin.x, ray.origin.y, ray.origin.z],
Float32Array.from([0.0017142786925635912, 5.017240249493299, 17.047073454417177])
Expand Down Expand Up @@ -221,3 +218,16 @@ function arrayCloseTo(arr1: ArrayLike<number>, arr2: ArrayLike<number>) {
expect(arr1[i]).toBeCloseTo(arr2[i]);
}
}

function vector3CloseTo(vec1: Vector3, vec2: Vector3): void {
expect(vec1.x).toBeCloseTo(vec2.x);
expect(vec1.y).toBeCloseTo(vec2.y);
expect(vec1.z).toBeCloseTo(vec2.z);
}

function vector4CloseTo(vec1: Vector4, vec2: Vector4): void {
expect(vec1.x).toBeCloseTo(vec2.x);
expect(vec1.y).toBeCloseTo(vec2.y);
expect(vec1.z).toBeCloseTo(vec2.z);
expect(vec1.w).toBeCloseTo(vec2.w);
}
Loading