Navigation

    Fuze Arena Logo
    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Popular
    • Users
    • Groups
    • Help
    • Discord

    Directly set an object's rotation

    Functions
    1
    1
    372
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • mozipha
      mozipha last edited by

      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.

      1. objectPointAt() will always set the forward vector (the object's local z-axis) to the requested direction.
      2. 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).
      3. 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).
      4. 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
      
      1 Reply Last reply Reply Quote 2
      • First post
        Last post