• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

SetEffectOrientation with facing

Status
Not open for further replies.
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):

JASS:
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.
 
Level 18
Joined
Jan 1, 2018
Messages
728
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.

JASS:
    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:

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
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.
JASS:
    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
 
Level 14
Joined
Jan 16, 2009
Messages
716
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.

JASS:
    // 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
 
@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:
Level 18
Joined
Jan 1, 2018
Messages
728
@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:

JASS:
    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

    //Roll in radians
    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:

JASS:
    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:
Status
Not open for further replies.
Top