# SetEffectOrientation with facing

Discussion in 'Triggers & Scripts' started by IcemanBo, Aug 18, 2018.

1. ### IcemanBo

Joined:
Sep 6, 2013
Messages:
6,236
Resources:
22
Maps:
3
Spells:
11
Template:
1
Tutorials:
4
JASS:
3
Resources:
22
Hello guys,

some time ago when trying around with Effect natives I had no luck to effecively bring to work
`BlzSetSpecialEffectOrientation(yaw, pitch, roll)`
for my needs. The other parts I needed worked pretty fast, so this is now pretty frustrating for me, as I tried too many attempts already without real success.

My problem is the EffectOrientation works only with yaw, pitch roll, (x,y, z - axis rotation respectively), and not with facing.
In my case, I want to convert a 3D facing between two points to those axis rotations so I can use the native.

Here is some attempt, but I changed it too many times, so the current state is kind of pending ( so don't care is something makes no sense):

Code (vJASS):

function SetEffectFacingXYZ takes effect e, real x_target, real y_target, real z_target  returns nothing
local real x_effect = BlzGetLocalSpecialEffectX(e)
local real y_effect = BlzGetLocalSpecialEffectY(e)
local real z_effect = BlzGetLocalSpecialEffectZ(e)

local real dx = x_target - x_effect
local real dy = y_target - y_effect
local real dz = z_target - z_effect

local real effect_abs = SquareRoot(x_effect*x_effect + y_effect*y_effect + z_effect*z_effect)
local real target_abs = SquareRoot(x_target*x_target + y_target*y_target + z_target*z_target)
local real distance   = SquareRoot(dx*dx + dy*dy + dz*dz)

local vector v
local vector v_plane

local real yaw   = 0
local real pitch = 0
local real roll  = 0

call ClearTextMessages()

//Projektion in xy
set v = vector.create(dx, dy, dz)
set v_plane = vector.create(0, 0, 1)
call v.projectPlane(v_plane)

//set v_plane.x = dx
//set v_plane.y = 0//dy
set v_plane.z = 0//dz
set v_plane.x = 1
if (v.x < 0) then
set roll = vector.getAngle(v, v_plane)
else
set roll = vector.getAngle(v, v_plane)
endif

// Projektion in xz
/*
set v.x = dx
set v.y = dy
set v.z = dz
set v_plane.x = 0
set v_plane.y = 1
set v_plane.z = 0
call v.projectPlane(v_plane)
if v.x <= 0 then
set v_plane.z = -1
set yaw = vector.getAngle(v, v_plane)
if (yaw != 0) then
set yaw = yaw - bj_PI/2
endif

call BJDebugMsg("yaw 1: " + R2S(yaw))
else
set v_plane.z = 1
set yaw = vector.getAngle(v, v_plane)
if (yaw != 0) then
set yaw = yaw - bj_PI/2
endif
call BJDebugMsg("yaw 2: " + R2S(yaw))
endif
set yaw = RAbsBJ(yaw)
*/

/*
// Projektion in yz
set v.x = dx
set v.y = dy
set v.z = dz
set v_plane.x = 1
set v_plane.y = 0
set v_plane.z = 0
call v.projectPlane(v_plane)
set v_plane.x = 0
set v_plane.y = 0
if v.z <= 0 then
set v_plane.y = 1
set pitch = vector.getAngle(v, v_plane) - bj_PI/4// + bj_PI/2
call BJDebugMsg("pitch 1: " + R2S(pitch))
else
set v_plane.y = -1
set pitch = vector.getAngle(v, v_plane) + bj_PI/2 + bj_PI*2 + bj_PI/4
call BJDebugMsg("pitch 2: " + R2S(pitch))
endif
*/

//call BJDebugMsg("yaw: " + R2S(yaw))
call BJDebugMsg("roll: " + R2S(roll))
call BlzSetSpecialEffectOrientation(e, yaw, pitch, roll)

//call BlzSetSpecialEffectOrientation(e, 0, 0, 0)
//call BlzSetSpecialEffectOrientation(e, yaw, pitch, roll)

call v.destroy()
call v_plane.destroy()
endfunction

endlibrary

Basically, I want a function that lets an effect face the point xyz.

I'm using this in an orbital movement system, I tried to finish. You can ignore maybe the texts, but the demo, as the texts are about movement, and not orientation.
In the attached demo, there's a trigger "Orbit Demo 2 Copy" (the only demo that is activated^^), which calls the function
`SetEffectFacingXYZ`
. In my opinon the given parameters are always connrect, and I tested it much. The effect movement also always works as expected, so this also would mean x/y/z are given correctly. But my problem is the called 3D facing-to-orientation is not working.

---

It's pretty easy to get it working with converting only 1 axis rotation, so let's say only x-axix, so on a plane, but somehow I don't get it done properly when rotating around all three axis.

2. ### Drake53

Joined:
Jan 1, 2018
Messages:
208
Resources:
0
Resources:
0
This also took me awhile to get working (although still didn't manage to get the pitch/yaw/roll directly from the vectors).
I had it working for a cameraobject before so I used that as basis to get a rotation matrix (camera and special effect have different order of rotation if I'm not mistaken).

If you're not using roll, your xyz input is the tangent vector, and you can get the other two by using cross products.
Here is my code, hope this helps.

Code (vJASS):
globals
constant real EPISILON = 0.000001
endglobals

function GetAnglesZXY takes Vector tangent, Vector normal, Vector binormal returns Vector
local real sqrt = SquareRoot( tangent.x * tangent.x + tangent.y * tangent.y )
local real x = Atan2( tangent.z * -1, sqrt )
local real y = 0
local real z = 0
if sqrt < EPSILON then
if tangent.z > 0 then
set z = Atan2( -1 * binormal.y, normal.y   )
else
set z = Atan2(      binormal.y, normal.y   )
endif
else
set y = Atan2(      tangent.x,  tangent.y  )
set z = Atan2( -1 * normal.z,   binormal.z ) + bj_PI / 2
endif
return Vector.Create( x, y, z )
endfunction

function GetCameraFromTNB takes Vector tangent, Vector normal, Vector binormal returns Vector
local Vector result = GetAnglesZXY( tangent, normal, binormal )
set result.x =   0 - bj_RADTODEG * result.x //Angle of Attack
set result.y =  90 - bj_RADTODEG * result.y //Rotation
set result.z = 360 - bj_RADTODEG * result.z //Roll
return result
endfunction

struct Matrix
// m00 m01 m02
// m10 m11 m12
// m20 m21 m22
real m00
real m01
real m02
real m10
real m11
real m12
real m20
real m21
real m22

//RzRxRy
method GetRotationsYXZ takes nothing returns Vector
local Vector v = Vector.create()
if this.m21 < 1 - EPSILON then
if this.m21 > EPSILON - 1 then
//set v.x = Asin( this.m21 )
set v.x = Atan2( this.m21, SquareRoot( this.m20 * this.m20 + this.m22 * this.m22 ) )
set v.y = Atan2( -1 * this.m20, this.m22 )
set v.z = Atan2( -1 * this.m01, this.m11 )
else
debug call BJDebugMsg( "-Pole (YXZ)" )
set v.x = bj_PI / -2
set v.y = 0
set v.z = -1 * Atan2( this.m02, this.m00 )
endif
else
debug call BJDebugMsg( "+Pole (YXZ)" )
set v.x = bj_PI / 2
set v.y = 0
set v.z = Atan2( this.m02, this.m00 )
endif
return v
endmethod

//RyRxRz
static method GetMatrixZXY takes real Rx, real Ry, real Rz returns thistype
local thistype m = thistype.create()
local real cx = Cos( Rx )
local real cy = Cos( Ry )
local real cz = Cos( Rz )
local real sx = Sin( Rx )
local real sy = Sin( Ry )
local real sz = Sin( Rz )

set m.m00 = cy * cz + sx * sy * sz
set m.m01 = sx * sy * cz - cy * sz
set m.m02 = cx * sy
set m.m10 = cx * sz
set m.m11 = cx * cz
set m.m12 = -1 * sx
set m.m20 = sx * cy * sz - sy * cz
set m.m21 = sx * cy * cz + sy * sz
set m.m22 = cx * cy
return m
endmethod
endstruct

struct Effect
Vector position

Vector forward
Vector up
Vector side

method UpdateEffect takes nothing returns nothing
local Vector cam = GetCameraFromTNB( this.forward, this.up, this.side ).Scale( bj_DEGTORAD )
local Matrix m = Matrix.GetMatrixZXY( cam.x * -1, cam.y, cam.z )
call Vector.destroy( cam )

//local Matrix m = Matrix.GetMatrixFromTNB( this.forward, this.up, this.side ) - see next post

set cam = m.GetRotationsYXZ()
call Matrix.destroy( m )
call BlzSetSpecialEffectOrientation( this.e, cam.z, cam.x, cam.y ) //roll, pitch, yaw
call Vector.destroy( cam )

call BlzSetSpecialEffectPosition( this.e, this.pos.x, this.pos.y, this.pos.z )
endmethod
endstruct

Last edited: Aug 20, 2018
3. ### AGD

Joined:
Mar 29, 2016
Messages:
400
Resources:
13
Spells:
7
Tutorials:
1
JASS:
5
Resources:
13
Maybe I'll try to work out the yaw and the pitch tomorrow.

Btw, I tested your demo map and I noticed that the rotation about the z-axis (roll) doesn't behave as intended. The flying sheep does not always face the center while it is at the upper semicircle of the orbit. The problem is caused by the fact that
`getAngle()`
only returns angle < PI radians. You need to add a little tweak when dy < 0.00, i.e., when angle should be > PI rads.
Code (vJASS):

set v_plane.x = 1
set v_plane.y = 0
set v_plane.z = 0

if dy < 0.00 then
set roll = 2.00*bj_PI - vector.getAngle(v, v_plane)
else
set roll = vector.getAngle(v, v_plane)
endif

4. ### IcemanBo

Joined:
Sep 6, 2013
Messages:
6,236
Resources:
22
Maps:
3
Spells:
11
Template:
1
Tutorials:
4
JASS:
3
Resources:
22
I had similar attempt before. z-axis rotation I already had working, but the problem is I get it working only on a plane, like for example only xy-plane. Always when I mix-in other axis rotations, I get wrong behaviour. (so posted formulas above currently don't necessarily make sense)

5. ### Wareditor

Joined:
Jan 16, 2009
Messages:
681
Resources:
3
Maps:
3
Resources:
3
I have worked with them when they were released. I haven't completed or touched that library since then so it's not perfect and might not even work.

Code (vJASS):

// movement is a Vector 3D
// new is the target position (Vector 3D)
// fx.position is the fx position (Vector 3D)
set movement =  new - fx.position
call fx.setOrientation(movement.angleX,movement.angleY,movement.angleZ)

// those are the 'angle' method operators from the Vector 3D struct
method operator angleZ takes nothing returns real
return Atan2(this.y,this.x)
endmethod

method operator angleY takes nothing returns real
local real ix = Math.abs(this.x)
local real iy = Math.abs(this.y)
local real px = (1-(iy/(ix+iy)))
if not (this.x == 0) then
return -Atan(this.z/this.x)*px
else
return 0.
endif
endmethod

method operator angleX takes nothing returns real
local real ix = Math.abs(this.x)
local real iy = Math.abs(this.y)
local real py = (1-(ix/(ix+iy)))

if not (this.y == 0) then
return Atan(this.z/this.y)*py
else
return 0.
endif
endmethod

6. ### IcemanBo

Joined:
Sep 6, 2013
Messages:
6,236
Resources:
22
Maps:
3
Spells:
11
Template:
1
Tutorials:
4
JASS:
3
Resources:
22
@Drake53 , thank you. I'm already getting correct vector through the rotation matrix. The yaw pitch roll was the thing. What is the vector exactly meaning you are creating?

@Wareditor , it is working, thanks much! I made only a little change in x-, y- axis if something is negative but it seems good. [need to test]

I'll make some more tests and then just post the orbit movement system tomorrow, if all works good.

Thanks to everyone.

Last edited: Aug 19, 2018
7. ### Drake53

Joined:
Jan 1, 2018
Messages:
208
Resources:
0
Resources:
0
@IcemanBo , the camera vector doesn't really have a meaning, it just combines the three rotations, roll, pitch, and yaw (in that order).

In case anyone needs it, I also have some additional code to create the input vectors in case you use a roll value as well:

Code (vJASS):
struct Vector
real x
real y
real z

static method Normal takes real pitch, real yaw, real roll returns Vector //rotation order = Rz-Rx-Ry
local Vector v = Vector.create()
set v.x = Sin( pitch ) * Sin( yaw ) * Cos( roll ) -                Cos( yaw ) * Sin( roll )
set v.y =                Sin( yaw ) * Sin( roll ) + Sin( pitch ) * Cos( yaw ) * Cos( roll )
set v.z = Cos( pitch )              * Cos( roll )
return v
endmethod
static method NormalTan takes Vector tangent, real roll returns Vector
local real pitch = Atan2( tangent.z * -1, SquareRoot( tangent.x * tangent.x + tangent.y * tangent.y ) )
local real yaw   = Atan2( tangent.x, tangent.y )
return Normal( pitch, yaw, roll )
endmethod

static method Difference takes Vector a, Vector b returns Vector
local Vector v = Vector.create()
set v.x = b.x - a.x
set v.y = b.y - a.y
set v.z = b.z - a.z
return v
endmethod

static method Cross takes Vector a, Vector b returns Vector
local Vector v = Vector.create()
set v.x = a.y * b.z - a.z * b.y
set v.y = a.z * b.x - a.x * b.z
set v.z = a.x * b.y - a.y * b.x
return v
endmethod
endstruct

function SetEffectFacingXYZWithRoll takes effect e, real x_target, real y_target, real z_target, real roll returns nothing
local real x_effect = BlzGetLocalSpecialEffectX(e)
local real y_effect = BlzGetLocalSpecialEffectY(e)
local real z_effect = BlzGetLocalSpecialEffectZ(e)

local Vector origin = Vector.Create( x_effect, y_effect, z_effect )
local Vector target = Vector.Create( x_target, y_target, z_target )

local Vector tangent  = Vector.Difference( origin, target  ).Normalize() //Forward vector
local Vector normal   = Vector.NormalTan(  tangent, roll   ).Normalize() //Up vector
local Vector binormal = Vector.Cross(      tangent, normal ).Normalize() //Side vector

//call UpdateEffect( e, tangent, normal, binormal ) - see code in previous post
endfunction

Edit: Did some more testing, and figured out how to create the rotation matrix directly from the TNB vectors, which is a lot faster:

Code (vJASS):
struct Matrix
real m00
real m01
real m02
real m10
real m11
real m12
real m20
real m21
real m22

static method GetMatrixFromTNB takes Vector tangent, Vector normal, Vector binormal returns thistype
local thistype m = thistype.create()
set m.m00 = -1 * binormal.y
set m.m01 =        normal.y
set m.m02 =       tangent.y
set m.m10 = -1 * binormal.z
set m.m11 =        normal.z
set m.m12 =       tangent.z
set m.m20 = -1 * binormal.x
set m.m21 =        normal.x
set m.m22 =       tangent.x
return m
endmethod
endstruct

struct Effect
effect e
Vector forward
Vector up
Vector side

method UpdateEffect takes nothing returns nothing
local Matrix m = Matrix.GetMatrixFromTNB( this.forward, this.up, this.side )
local Vector cam = m.GetRotationsYXZ()
call Matrix.destroy( m )
call BlzSetSpecialEffectOrientation( this.e, cam.z, cam.x, cam.y ) //roll, pitch, yaw
call Vector.destroy( cam )
endmethod
endstruct

Last edited: Aug 20, 2018