Directly set an object's rotation
-
While there's no native FUZE function to directly set an object's rotation to an arbitrary state starting from any other rotation state, it's possible to write a function that does this by building off of
objectPointAt()
.objectPointAt()
follows consistent rules about how it orients an object, and once we know these rules, we can use them as a starting point for our function.objectPointAt()
will always set the forward vector (the object's local z-axis) to the requested direction.objectPointAt()
will always set the right vector (the object's local x-axis) orthogonal to the global y-axis (that is, the right vector will always point along the floor).objectPointAt()
will always give the up vector (the object's local y-axis) a positive y value (that is, the up vector will always point upward and never downward).objectPointAt()
has a bug that will cause the object to disappear if you point it directly up or directly down.
In practical terms, this means we can set the object's forward vector with
objectPointAt()
, and then, because we know based on the four rules above how the up and right vectors must now be oriented, we can calculate how much we need to rotate the object around its local z-axis to reach our desired orientation.The code below does that and also provides some safeguards against the disappearing object bug and potential floating point errors. My implementation uses direction vectors as the inputs to represent a rotation state because that's generally how I prefer to work, but it would also be possible to use the same principles to create a function that takes, for example, pitch/yaw/roll as its rotation input (or you can convert with
eulerToDirVec()
/axisAngleToDirVec()
, included below).Once the share is approved, this code can be downloaded with 5LH73MNDDT.
(The vectors passed to
setObjRot()
as_fwd
and_up
should be normalized.)// Sets rotation for an object. function setObjRot(_obj, _pos, _fwd, _up) // A straight up or down forward wrecks the algorithm, so fudge the // vectors if necessary. if equals(abs(_fwd.y ), 1, 0.000001) then _fwd = axisRotVecBy(_fwd, _up, 0.015) endif // When called from any orientation, objectPointAt() will always // result in a positive-y up vector and a 0-y right vector, so it's // predictable enough that we can infer the object's up/right vectors // even without specific data about how they've been modified. objectPointAt(_obj, _pos + _fwd) var unrolledR = axisRotVecBy(normalize({_fwd.x, 0, _fwd.z}), {0, 1, 0}, -90) var unrolledUp = cross(unrolledR, _fwd) var roll = getAngleBetweenVecs(unrolledUp, _up) if roundVec(axisRotVecBy(unrolledUp, _fwd, roll), 2) != roundVec(_up, 2) then roll *= -1 endif rotateObject(_obj, {0, 0, 1}, roll) return void // Equality check with tolerance value. function equals(_num1, _num2, _tolerance) var isEqual = true if strBeginsWith(str(_num1), \''{\'') then // If a vector var i for i = 0 to 4 loop if abs(_num1[i] - _num2[i]) > abs(_tolerance) then isEqual = false break endif repeat else if abs(_num1 - _num2) > abs(_tolerance) then isEqual = false endif endif return isEqual // Rotates 3D vector _v around 3D vector _axis by _deg degrees. function axisRotVecBy(_v, _axis, _deg) // Euler-Rodrigues rotation formula var halfDeg = _deg / 2 var w = _axis * sin(halfDeg) var crossWV = cross(w, _v) return _v + 2 * cos(halfDeg) * crossWV + 2 * cross(w, crossWV) // Returns angle in degrees between two vectors. function getAngleBetweenVecs(_v1, _v2) angle = 0 if equals(_v1, _v2 * -1, 0.001) then angle = 180 else if !equals(_v1, _v2, 0.001) and !equals(_v1, {0, 0, 0}, 0.001) and !equals(_v2, {0, 0, 0}, 0.001) then angle = acos(dot(_v1, _v2) / (length(_v1) * length(_v2))) endif endif return angle // Round a float to a given number of decimal places. function roundDec(_num, _decimals) var factor = pow(10, _decimals) if _decimals <= 0 then factor = 1 endif _num = _num * factor _num = round(_num) return _num / factor // Rounds a vector to the given number of decimal places. function roundVec(_v, _decimals) _v[0] = roundDec(_v[0], _decimals) _v[1] = roundDec(_v[1], _decimals) _v[2] = roundDec(_v[2], _decimals) if _v[3] != 0 then _v[3] = roundDec(_v[3], _decimals) endif return _v // Converts an Euler angle to direction vectors. function eulerToDirVec(_yaw, _pitch, _roll) var up = axisRotVecBy({0, 1, 0}, {0, 0, 1}, _roll) var fwd = axisRotVecBy({0, 0, 1}, {-1, 0, 0}, _pitch) up = axisRotVecBy(up, {-1, 0, 0}, _pitch) fwd = axisRotVecBy(fwd, {0, 1, 0}, _yaw) up = axisRotVecBy(up, {0, 1, 0}, _yaw) var result = [ .fwd = fwd, .up = up ] return result // Converts degrees of rotation around an axis to direction vectors. function axisAngleToDirVec(_axis, _deg) if axis != {0, 0, 0} then _axis = normalize(_axis) endif var result = [ .fwd = axisRotVecBy({0, 0, 1}, _axis, _deg), .up = axisRotVecBy({0, 1, 0}, _axis, _deg) ] return result