Thank you for the reply, @Evgeni_Popov. I made a playground that I think more succinctly illustrates the issue I am having.
https://www.babylonjs-playground.com/#4JNT09#6
Note: The .blend file that associated with the model(s) in the playground is located in a comment in the playground.
What I am trying to do is orient the camera in the same position and rotation as the cameras I set up in the .blend file. I have the cameras nested under Empty Axis nodes in Blender. I adjust the empty nodes’ position and rotation, and keep the cameras’ positions and rotations both at 0,0,0, so that the parent node controls their orientation. In Babylon, I try to utilize these values to orient the camera in the same manner as the “cameraAnchors”.
However, there is consistently an issue with the rotation of the camera around the Y axis. This can be seen in that the rotated topdown camera anchor version of the model, which has the topdown camera anchor rotated 90 degrees ((0,0,90) in Blender), still has the Babylon camera facing the same direction (0,0,0) as the regular model. Additionally, when you move the camera to one of the upside down camera anchors (upside-down, as seen in the .blend file), the cameras are still oriented “up” instead of flipped upside down (the ground plane should always be below the camera, no matter which side you are on). The upside-down cameras are supposed to be rotated 180 degrees about the y axis, and yet they are not. Since the topdown camera anchor is supposed to be rotated 90 degrees about the y axis as well, and is not, it confirms that the issue is that the y axis rotation is not being updated/matched properly.
I tried to deduce where this issue is with the y axis. Below is the associated code and global variables (as far as I could tell), starting with the setTarget function you graciously provided me.
public _initialFocalDistance = 1;
public _camMatrix = Matrix.Zero();
public _referencePoint = new Vector3(0, 0, 1);
public _transformedReferencePoint = Vector3.Zero();
private _defaultUp = Vector3.Up(); //-> Returns a new Vector3 set to (0.0, 1.0, 0.0)
public setTarget(target: Vector3): void {
this.upVector.normalize();
this._initialFocalDistance = target.subtract(this.position).length();
if (this.position.z === target.z) {
this.position.z += Epsilon;
}
this._referencePoint.normalize().scaleInPlace(this._initialFocalDistance);
Matrix.LookAtLHToRef(this.position, target, this._defaultUp, this._camMatrix);
this._camMatrix.invert();
this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
var vDir = target.subtract(this.position);
if (vDir.x >= 0.0) {
this.rotation.y = (-Math.atan(vDir.z / vDir.x) + Math.PI / 2.0);
} else {
this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
}
this.rotation.z = 0;
if (isNaN(this.rotation.x)) {
this.rotation.x = 0;
}
if (isNaN(this.rotation.y)) {
this.rotation.y = 0;
}
if (isNaN(this.rotation.z)) {
this.rotation.z = 0;
}
if (this.rotationQuaternion) {
Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this.rotationQuaternion);
}
}
(the below is found in: https://raw.githubusercontent.com/BabylonJS/Babylon.js/b1ba8b7fed51641ca727214826c92c2dfa2ed81a/dist/babylon.max.js)
/**
* Sets the given "result" Matrix to a rotation matrix used to rotate an entity so that it looks at the target vector3, from the eye vector3 position, the up vector3 being oriented like "up".
* This function works in left handed mode
* @param eye defines the final position of the entity
* @param target defines where the entity should look at
* @param up defines the up vector for the entity
* @param result defines the target matrix
*/
public LookAtLHToRef = function (eye, target, up, result) {
var xAxis = MathTmp.Vector3[0];
var yAxis = MathTmp.Vector3[1];
var zAxis = MathTmp.Vector3[2];
// Z axis
target.subtractToRef(eye, zAxis);
zAxis.normalize();
// X axis
Vector3.CrossToRef(up, zAxis, xAxis);
var xSquareLength = xAxis.lengthSquared();
if (xSquareLength === 0) {
xAxis.x = 1.0;
}
else {
xAxis.normalizeFromLength(Math.sqrt(xSquareLength));
}
// Y axis
Vector3.CrossToRef(zAxis, xAxis, yAxis);
yAxis.normalize();
// Eye angles
var ex = -Vector3.Dot(xAxis, eye);
var ey = -Vector3.Dot(yAxis, eye);
var ez = -Vector3.Dot(zAxis, eye);
Matrix.FromValuesToRef(xAxis._x, yAxis._x, zAxis._x, 0.0, xAxis._y, yAxis._y, zAxis._y, 0.0, xAxis._z, yAxis._z, zAxis._z, 0.0, ex, ey, ez, 1.0, result);
};
// Same as Tmp but not exported to keep it only for math functions to avoid conflicts
var MathTmp = /** @class */ (function () {
function MathTmp() {
}
MathTmp.Vector3 = Misc_arrayTools__WEBPACK_IMPORTED_MODULE_2_[“ArrayTools”].BuildArray(6, Vector3.Zero);
MathTmp.Matrix = Misc_arrayTools__WEBPACK_IMPORTED_MODULE_2_[“ArrayTools”].BuildArray(2, Matrix.Identity);
MathTmp.Quaternion = Misc_arrayTools__WEBPACK_IMPORTED_MODULE_2_[“ArrayTools”].BuildArray(3, Quaternion.Zero);
return MathTmp;
}());
private _cachedRotationZ = 0;
private _cachedQuaternionRotationZ = 0;
public _getViewMatrix(): Matrix {
if (this.lockedTarget) {
this.setTarget(this._getLockedTargetPosition()!);
}
// Compute
this._updateCameraRotationMatrix();
// Apply the changed rotation to the upVector
if (this.rotationQuaternion && this._cachedQuaternionRotationZ != this.rotationQuaternion.z) {
this._rotateUpVectorWithCameraRotationMatrix();
this._cachedQuaternionRotationZ = this.rotationQuaternion.z;
} else if (this._cachedRotationZ != this.rotation.z) {
this._rotateUpVectorWithCameraRotationMatrix();
this._cachedRotationZ = this.rotation.z;
}
Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
// Computing target and final matrix
this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
if (this.updateUpVectorFromRotation) {
if (this.rotationQuaternion) {
Axis.Y.rotateByQuaternionToRef(this.rotationQuaternion, this.upVector);
} else {
Quaternion.FromEulerVectorToRef(this.rotation, this._tmpQuaternion);
Axis.Y.rotateByQuaternionToRef(this._tmpQuaternion, this.upVector);
}
}
this._computeViewMatrix(this.position, this._currentTarget, this.upVector);
return this._viewMatrix;
}
protected _computeViewMatrix(position: Vector3, target: Vector3, up: Vector3): void {
if (this.ignoreParentScaling) {
if (this.parent) {
const parentWorldMatrix = this.parent.getWorldMatrix();
Vector3.TransformCoordinatesToRef(position, parentWorldMatrix, this._globalPosition);
Vector3.TransformCoordinatesToRef(target, parentWorldMatrix, this._tmpTargetVector);
Vector3.TransformNormalToRef(up, parentWorldMatrix, this._tmpUpVector);
this._markSyncedWithParent();
} else {
this._globalPosition.copyFrom(position);
this._tmpTargetVector.copyFrom(target);
this._tmpUpVector.copyFrom(up);
}
if (this.getScene().useRightHandedSystem) {
Matrix.LookAtRHToRef(this._globalPosition, this._tmpTargetVector, this._tmpUpVector, this._viewMatrix);
} else {
Matrix.LookAtLHToRef(this._globalPosition, this._tmpTargetVector, this._tmpUpVector, this._viewMatrix);
}
return;
}
if (this.getScene().useRightHandedSystem) {
Matrix.LookAtRHToRef(position, target, up, this._viewMatrix);
} else {
Matrix.LookAtLHToRef(position, target, up, this._viewMatrix);
}
if (this.parent) {
const parentWorldMatrix = this.parent.getWorldMatrix();
this._viewMatrix.invert();
this._viewMatrix.multiplyToRef(parentWorldMatrix, this._viewMatrix);
this._viewMatrix.getTranslationToRef(this._globalPosition);
this._viewMatrix.invert();
this._markSyncedWithParent();
} else {
this._globalPosition.copyFrom(position);
}
}
I’m not sure why the yAxis rotation is turning out funky, but my hunch is because the UpVector is used for the X axis rotation calculation in the LookAtLHToRef function, and the X axis and Z axis vectors are used to calculate the Y axis vector. I’ve been trying to dig into the formatting and the Matrix objects that are used, but I could use some help to figure out what is going on. For instance, why does the calculated matrix need to be inversed?
Would I have to make the “upVector” negative? I tried that and it didn’t seem to work, though I am open to someone proving me wrong. But I don’t think that would solve the issue with the 90 degrees rotation about the y axis not being applied, even on the positive side of the ground plane.
Additionally, I was recommended, I think by @PirateJC in a different forum post to use:
var dir = cameraAnchor.getDirection(new BABYLON.Vector3(0, -1, 0));
var newCameraTarget = newCameraPosition.add(dir);
when trying to match the camera’s rotation up with the cameraAnchor transformNode’s rotation.
I’ve tried tinkering with this and looking at the function, but cannot figure it out in my head – How does using the down vector direction and adding it to the camera’s position (which would result in the camera’s new target being a spot 1 unit below the camera’s new position (cameraAnchor’s position)), end up with the camera facing in the same orientation as the cameraAnchor, but only for cameraAnchors with non-zero x, y, and z values and orientations aligned with the upVector? Any light shed on this matter would be most appreciated!
Thanks in advance!