# SOLVED: Tilting, not rotating

Tweet
(Posted 3 months ago)

I want to tilt a ring of neck verts on my animated mesh (neck bone) to match my head bone (same position in space). Twisting is out of the question.

I've tried the quick fixes by unturning the neck yaw after rotating it, but there are sign flips too as the head looks straight up.

I've experimented with the information on Wavey's quat.bb codearc website (very informative)
But I haven't been able to apply it other than to remove gimbal lock.

(Posted 2 months ago)

I don't really understand your description but the simplest way to apply quaternion transforms is to use a pivot parented to the neck bone and then position this at the vertex position and then tilt and you should get that position and update the vertex with that. It should be possible to use one pivot for all vertexes but you may have to reset the pivot between each vertex.

(Posted 2 months ago)

I've added my .zip package to the bottom of my posts below for examination.
Boy am I glad I found the way to make it work. Deductive reasoning.

(Posted 2 months ago)

Here's Tilt.bb:

``````
;SOLVED:
;So the solution for 'Tilting' the neck without
;twisting it was simply to make the neck the
;in the first place.
;We Then twist ONLY the head.
;Kinda like a toothpaste cap.

;This method of thinking by inserting an
;additional joint at the same point in space
;and making it the parent allows the separation
;of axis motion solves pretty much EVERYTHING.
;You're welcome.

;I RE-ITERATE: We only ADD rotation to a
;joint, NEVER try to subtract rotation if
;you can avoid it! Use a different joint.

Include "Quat.bb"
;Setup
Graphics3D 800,600,0,0
SeedRnd (MilliSecs())
AppTitle "Neck Tilt Demo Solved"
Global Cam=CreateCamera()
CameraRange Cam,1,20000
Global WorldLight=CreateLight(1)
LightColor WorldLight,100,100,100
MoveMouse GraphicsWidth()/16, GraphicsHeight()/16:FlushMouse()

Global p1=CreateCube():EntityColor p1,0,255,0
Global p2=CreateCube()
Global p3=CreateCube():EntityColor p3,0,255,255

Global n1=CreateCube():EntityColor n1,0,255,0
Global n2=CreateCube()
Global n3=CreateCube():EntityColor n3,0,255,255

Global e2=FindChild(Ac,"z_Neck")

;**********************************************
;Each pivot in Easter has 0 rotation.
;I believe this means that -z is towards
;the camera, and that +Y is correctly upwards.
;**********************************************

PositionEntity Cam,-50,40,-80,True
PointEntity Cam,E1
MoveEntity Cam,0,20,0
DemoChildPoints()

EntityParent E1,E2 ;Convenient:
;I'll make it permanent in my file now.

;Reasonable ragdoll (not 90 or upside-down)
TurnEntity FindChild(Ac,"z_Body"),0,0,25,False

;Primary Render Loop
OldMillis=MilliSecs()
While Bail=0
If Abs(MilliSecs()-OldMillis)>10 Then ;Frame
OldMillis=MilliSecs()

rx#=Rnd(0,1):ry#=1.0:rz#=Rnd(0,1)
;Twist and grind the head around a bit, try
;comment the turn, and hit the T/U keys a while

;So:
;NEVER ADD Yaw to z_Neck in the first place
RotE2 rx#,0,rz# ;Just an axis gimbal fix
TurnEntity e1,0,ry#,0,False

;NeckFix() ;<-NOT NEEDED ANYMORE
UpdateWorld
RenderWorld
Flip
Bail=UpdateInput()
End If
Wend
FreeEntity Ac
FreeEntity Cam
End

Function UpdateInput()
;Bunch 'O FPS camera controls (Simplified)
;TKkeyname to set variable names uniquely
If KeyHit(1) Then Return 1 ;Bail out-QUIT
TKF=0:If KeyDown(33) Then TKF=1
TKV=0:If KeyDown(47) Then TKV=1
TKShift=0:If (KeyDown(42) + KeyDown(54))<>0 Then TKShift=1
TKLt=0:If KeyDown(203) Or KeyDown(75) Then TKLt=1
TKRt=0:If KeyDown(205) Or KeyDown(77) Then TKRt=1
TKPgUp=0:If KeyDown(201)    Or KeyDown(73) Then TKPgUp=1
TKPgDn=0:If KeyDown(209) Or KeyDown(81) Then TKPgDn=1
TKUp=0:If KeyDown(200) Or KeyDown(72) Then TKUp=1 ;Move Forward
TKDn=0:If KeyDown(208) Or KeyDown(80) Then TKDn=1 ;Move Backward
TKT=0:If KeyDown(20) Then TKT=1 ;T key
TKU=0:If KeyDown(22) Then TKU=1 ;U key
TKSLideL=0:If KeyDown(51) Then TKSlideL=1
TKSLideR=0:If KeyDown(52) Then TKSlideR=1
TKJump=0:If KeyDown(57) Then TKJump=1

If TKT=1 Then ;T-Turn Neck
RotE2 4,0,1
End If

TurnEntity E1,0,4,0,False ;Do only YAW here
End If

;Camera controls
pitch#=0:yaw#=0:movX#=0:movY#=0:movZ#=0
If TKPgUp=1 Then pitch#=-3
If TKPgDn=1 Then pitch#=3
;Avoid camera gimbal problem
If Int((EntityPitch(Cam)+pitch#)/90)=(EntityPitch(Cam)+pitch#)/90 Then pitch#=pitch#*2
If TKRt=1 Then yaw#=-3 ;(Yes they're reversed)
If TKLt=1 Then yaw#=3
If TKShift=1 Then pitch#=pitch#*0.1:yaw#=yaw#*0.1
;Camera motion keys
If TKUp=1 Then  movZ#=10           ;Fwd
If TKDn=1 Then  movZ#=-10          ;Bac
If TKSlideL=1 Then movX#=-1       ;<
If TKSlideR=1 Then movX#=1        ;>
If TKF=1 Or TKJump=1 Then movY#=1 ;Up
If TKV=1 Then movY#=-1            ;Dn
If TKShift=1 Then ;Fine motion
movX#=movX#*0.1:movY#=movY#*0.1:movZ#=movZ#*0.1
End If
;Turn the Camera
TurnEntity Cam,0,yaw#,0
If EntityYaw(Cam)<0 Then TurnEntity Cam,0,360,0
If EntityYaw(Cam)>360 Then TurnEntity Cam,0,-360,0
TurnEntity Cam,pitch#,0,0
If EntityPitch#(Cam)<0 Then TurnEntity Cam,360,0,0
If EntityPitch#(Cam)>360 Then TurnEntity Cam,-360,0,0
UnRollCam()
;Move the Camera
TranslateEntity Cam,-Sin(EntityYaw#(Cam))*movZ#,movY#,Cos(EntityYaw#(Cam))*movZ#,False
MoveEntity Cam,movX#,0,0
FlushKeys
End Function

;Extra functions for anybody needing to
;Figure out something complicated
Function EntYaw#(e1,e2)
;Caveat:Object orientation in space
Return Rec2Pol#(EntityX#(e1,True),EntityZ#(e1,True),EntityX#(e2,True),EntityZ#(e2,True))
End Function

Function EntPit#(e1,e2)
;Caveat:Object orientation in space
Return Rec2Pol#(EntityZ#(e1,True),EntityY#(e1,True),EntityZ#(e2,True),EntityY#(e2,True))-90.0
End Function

Function EntRol#(e1,e2)
;Caveat:Object orientation in space
Return Rec2Pol#(EntityX#(e1,True),EntityY#(e1,True),EntityX#(e2,True),EntityY#(e2,True))
End Function

;This always was handy for me
;(2D top views mainly, but above all applies)
Function Rec2Pol#(X1#,Y1#,X2#,Y2#)
C# = MyPi / 180
OX# = X2# - X1#: OY# = Y2# - Y1#:R# = Sqr((OX# ^ 2) + (OY# ^ 2))
If Sgn(OX#)=0 And Sgn(OY#)=0 Then Ret#=0:Goto XitR
If OX<0 And OY<0 Then QT=180
If OX<0 And OY>0 Then QT=180
If OX=0 And OY>0 Then Ret#=0:Goto XitR
If OX>0 And OY=0 Then Ret#=90:Goto XitR
If OX=0 And OY<0 Then Ret#=180:Goto XitR
If OX<0 And OY=0 Then Ret#=270:Goto XitR
Ret# = 90-ATan((OY / R#) / (OX# / R#))+QT
.XitR
Return Ret#
End Function

Function UnRollCam()
;Works reasonably well FOR ME...
tmp=CreatePivot()
RotateEntity tmp,EntityPitch#(Cam,True),EntityYaw#(Cam,True),0,True
RotateEntity Cam,0,0,0,True
RotateEntity Cam,EntityPitch#(tmp,True),EntityYaw#(tmp,True),0,True
FreeEntity tmp
End Function

;Not needed anymore
Function RotE1(xr#,yr#,zr#)
; work out the quats for each new axis
If xr#<=0 Then xrm#=xr# Else xrp#=xr#
If yr#<=0 Then yrm#=yr# Else yrp#=yr#
If zr#<=0 Then zrm#=zr# Else zrp#=zr#
FillEuler(tempEul, xrm#, 0, 0) : EulerToQuat minusP, tempEul
FillEuler(tempEul, xrp#, 0, 0) : EulerToQuat plusP, tempEul
FillEuler(tempEul, 0, yrm#, 0) : EulerToQuat minusY, tempEul
FillEuler(tempEul, 0, yrp#, 0) : EulerToQuat plusY, tempEul
FillEuler(tempEul, 0, 0, zrm#) : EulerToQuat minusR, tempEul
FillEuler(tempEul, 0, 0, zrp#) : EulerToQuat plusR, tempEul
; concatenated rotations are acheived by multiplying quats
MultiplyQuat(cubeQuat, cubeQuat, plusP)
MultiplyQuat(cubeQuat, cubeQuat, minusP)
MultiplyQuat(cubeQuat, cubeQuat, plusR)
MultiplyQuat(cubeQuat, cubeQuat, minusR)
MultiplyQuat(cubeQuat, cubeQuat, plusY)
MultiplyQuat(cubeQuat, cubeQuat, minusY)
QuatToEuler tempEul, cubeQuat
RotateEntity e1,tempEul\pitch,tempEul\yaw,tempEul\roll,False
End Function

;Essential!
Function RotE2(xr#,yr#,zr#)
; work out the quats for each new axis
If xr#<=0 Then xrm#=xr# Else xrp#=xr#
If yr#<=0 Then yrm#=yr# Else yrp#=yr#
If zr#<=0 Then zrm#=zr# Else zrp#=zr#
FillEuler(tempEul, xrm#, 0, 0) : EulerToQuat minusP, tempEul
FillEuler(tempEul, xrp#, 0, 0) : EulerToQuat plusP, tempEul
FillEuler(tempEul, 0, yrm#, 0) : EulerToQuat minusY, tempEul
FillEuler(tempEul, 0, yrp#, 0) : EulerToQuat plusY, tempEul
FillEuler(tempEul, 0, 0, zrm#) : EulerToQuat minusR, tempEul
FillEuler(tempEul, 0, 0, zrp#) : EulerToQuat plusR, tempEul
; concatenated rotations are acheived by multiplying quats
MultiplyQuat(neckQuat, neckQuat, plusP)
MultiplyQuat(neckQuat, neckQuat, minusP)
MultiplyQuat(neckQuat, neckQuat, plusR)
MultiplyQuat(neckQuat, neckQuat, minusR)
MultiplyQuat(neckQuat, neckQuat, plusY)
MultiplyQuat(neckQuat, neckQuat, minusY)
QuatToEuler tempEul, neckQuat
RotateEntity e2,tempEul\pitch,tempEul\yaw,tempEul\roll,False
End Function

Function DemoChildPoints()
;EntityParent e2,e1 ;(NO. What was I smoking?)
pit#=EntityPitch#(e1,False):pit#=pit# Mod 360.0
yaw#=EntityYaw#(e1,False):yaw#=yaw# Mod 360.0
rol#=EntityRoll#(e1,False):rol#=rol# Mod 360.0
EntityParent p1,0
EntityParent p2,0
EntityParent p3,0
PositionEntity p1,EntityX#(e1,True),EntityY#(e1,True),EntityZ#(e1,True),True
PositionEntity p2,EntityX#(e1,True),EntityY#(e1,True),EntityZ#(e1,True),True
PositionEntity p3,EntityX#(e1,True),EntityY#(e1,True),EntityZ#(e1,True),True
RotateEntity p1,pit#,yaw#,rol#,True
RotateEntity p2,pit#,yaw#,rol#,True
RotateEntity p3,pit#,yaw#,rol#,True
EntityParent p1,e1
EntityParent p2,e1
EntityParent p3,e1
MoveEntity p1,0,0,55 ;Grn Z
MoveEntity p2,55,0,0 ;Wht X
MoveEntity p3,0,55,0 ;Blu Y

pit#=EntityPitch#(e2,False):pit#=pit# Mod 360.0
yaw#=EntityYaw#(e2,False):yaw#=yaw# Mod 360.0
rol#=EntityRoll#(e2,False):rol#=rol# Mod 360.0
EntityParent n1,0
EntityParent n2,0
EntityParent n3,0
PositionEntity n1,EntityX#(e2,True),EntityY#(e2,True),EntityZ#(e2,True),True
PositionEntity n2,EntityX#(e2,True),EntityY#(e2,True),EntityZ#(e2,True),True
PositionEntity n3,EntityX#(e2,True),EntityY#(e2,True),EntityZ#(e2,True),True
RotateEntity n1,pit#,yaw#,rol#,True
RotateEntity n2,pit#,yaw#,rol#,True
RotateEntity n3,pit#,yaw#,rol#,True
EntityParent n1,e2
EntityParent n2,e2
EntityParent n3,e2
MoveEntity n1,0,0,52 ;Grn Z
MoveEntity n2,52,0,0 ;Wht X
MoveEntity n3,0,52,0 ;Blu Y
End Function

Function NeckFix()
;Closest working function to
;SUBTRACT YAW from an object
;Does NOT work for EVERY orientation
yw#=EntityYaw#(e2,False) ;<-Neck Itself
TurnEntity e2,0,-yw#,0,False
End Function

;I'm leaving the other fixes here
;Just because they required a lot of thought
;and do show just HOW MUCH trouble you can get
;into with the wrong train of thought!

Function NeckFix2()
;Rec2Pol angle method
pit#=EntityPitch#(e2,False)
yaw#=EntityYaw#(e2,False)
rol#=EntityRoll#(e2,False)
;RotE2 -pit#,0,0
yw1#=entyaw#(e2,n1)
;RotE2 pit#,0,0
;RotE2 0,0,-rol#
yw2#=entyaw#(e2,n1)
;RotE2 0,0,rol#
;RotE2 0,-yw2#-yw1#,0 ;wild guess
TurnEntity e2,0,-yw2#,0
;I'm just doing this very wrong...
End Function

Function NeckFix3()
;PointEntity method
;Manual says PointEntity doesn't do roll?
;Turn universe on right side.
TurnEntity Ac,0,0,-90.0,True
PointEntity e2,p1
TurnEntity Ac,0,0,90.0,True
;Nope, even though the green points
;do show correct alignment!
End Function

Function NeckFix4()
;My camera unroll method re-applied
;Trash. Sigh...
tmp=CreatePivot()
RotateEntity tmp,EntityPitch#(e1,True),0,EntityRoll#(e1,True),True
RotateEntity e2,0,0,0,False
RotateEntity e2,EntityPitch#(tmp,True),0,EntityRoll#(tmp,True),False
FreeEntity tmp
End Function
``````

Here's Quat.bb:

``````
; Quat.bb : v1.0 : Wavey : 15/11/02
; A tutorial on how to use this file is at http://www.dscho.co.uk/blitz/tutorials/quaternions.shtml

Type Euler
Field pitch#, yaw#, roll#
End Type

Type Quat
Field w#, x#, y#, z#
End Type

; Change these constants if you notice slips in accuracy
Const QuatToEulerAccuracy# = 0.001
Const QuatSlerpAccuracy#   = 0.0001
Global tempEul.Euler = New Euler
Global minusP.Quat = New Quat
Global plusP.Quat  = New Quat
Global minusY.Quat = New Quat
Global plusY.Quat  = New Quat
Global minusR.Quat = New Quat
Global plusR.Quat  = New Quat
Global cubeQuat.Quat = New Quat
FillEuler(tempEul, 0, 0, 0):EulerToQuat cubeQuat, tempEul
Global neckQuat.Quat = New Quat
FillEuler(tempEul, 0, 0, 0):EulerToQuat neckQuat, tempEul

; convert a Rotation to a Quat
Function EulerToQuat(out.Quat, src.Euler)
;Roll is inverted due to change in handedness of coordinate systems (DirectX vs. OpenGL or 3DS Max)
Local cr# = Cos(-src\roll*0.5)
Local cp# = Cos(src\pitch*0.5)
Local cy# = Cos(src\yaw*0.5)

Local sr# = Sin(-src\roll*0.5)
Local sp# = Sin(src\pitch*0.5)
Local sy# = Sin(src\yaw*0.5)

; These variables are only here to cut down on the number of multiplications
Local cpcy# = cp * cy
Local spsy# = sp * sy
Local spcy# = sp * cy
Local cpsy# = cp * sy

; Generate the output quat
out\w = cr * cpcy + sr * spsy
out\x = sr * cpcy - cr * spsy
out\y = cr * spcy + sr * cpsy
out\z = cr * cpsy - sr * spcy
End Function

; convert a Quat to a Rotation
Function QuatToEuler(out.Euler, src.Quat)
Local sint#, cost#, sinv#, cosv#, sinf#, cosf#
Local cost_temp#

sint = (2 * src\w * src\y) - (2 * src\x * src\z)
cost_temp = 1.0 - (sint * sint)

If Abs(cost_temp) > QuatToEulerAccuracy
cost = Sqr(cost_temp)
Else
cost = 0
EndIf

If Abs(cost) > QuatToEulerAccuracy
sinv = ((2 * src\y * src\z) + (2 * src\w * src\x)) / cost
cosv = (1 - (2 * src\x * src\x) - (2 * src\y * src\y)) / cost
sinf = ((2 * src\x * src\y) + (2 * src\w * src\z)) / cost
cosf = (1 - (2 * src\y * src\y) - (2 * src\z * src\z)) / cost
Else
sinv = (2 * src\w * src\x) - (2 * src\y * src\z)
cosv = 1 - (2 * src\x * src\x) - (2 * src\z * src\z)
sinf = 0
cosf = 1
EndIf

; Generate the output rotation
out\roll = -ATan2(sinv, cosv) ;  inverted due to change in handedness of coordinate system
out\pitch = ATan2(sint, cost)
out\yaw = ATan2(sinf, cosf)
End Function

; result will be the same rotation as doing q1 then q2 (order matters!)
Function MultiplyQuat(result.Quat, q1.Quat, q2.Quat)
Local a#, b#, c#, d#, e#, f#, g#, h#

a = (q1\w + q1\x) * (q2\w + q2\x)
b = (q1\z - q1\y) * (q2\y - q2\z)
c = (q1\w - q1\x) * (q2\y + q2\z)
d = (q1\y + q1\z) * (q2\w - q2\x)
e = (q1\x + q1\z) * (q2\x + q2\y)
f = (q1\x - q1\z) * (q2\x - q2\y)
g = (q1\w + q1\y) * (q2\w - q2\z)
h = (q1\w - q1\y) * (q2\w + q2\z)

result\w = b + (-e - f + g + h) * 0.5
result\x = a - ( e + f + g + h) * 0.5
result\y = c + ( e - f + g - h) * 0.5
result\z = d + ( e - f - g + h) * 0.5
End Function

; convenience function to fill in a rotation structure
Function FillEuler(r.Euler, pitch#, yaw#, roll#)
r\pitch = pitch
r\yaw = yaw
r\roll = roll
End Function

; use this to interpolate between quaternions
Function QuatSlerp(res.Quat, start.Quat, fin.Quat, t#)
Local scaler_w#, scaler_x#, scaler_y#, scaler_z#
Local omega#, cosom#, sinom#, scale0#, scale1#

cosom = start\x * fin\x + start\y * fin\y + start\z * fin\z + start\w * fin\w

If cosom <= 0.0
cosom = -cosom
scaler_w = -fin\w
scaler_x = -fin\x
scaler_y = -fin\y
scaler_z = -fin\z
Else
scaler_w = fin\w
scaler_x = fin\x
scaler_y = fin\y
scaler_z = fin\z
EndIf

If (1 - cosom) > QuatSlerpAccuracy
omega = ACos(cosom)
sinom = Sin(omega)
scale0 = Sin((1 - t) * omega) / sinom
scale1 = Sin(t * omega) / sinom
Else
; Angle too small: use linear interpolation instead
scale0 = 1 - t
scale1 = t
EndIf

res\x = scale0 * start\x + scale1 * scaler_x
res\y = scale0 * start\y + scale1 * scaler_y
res\z = scale0 * start\z + scale1 * scaler_z
res\w = scale0 * start\w + scale1 * scaler_w
End Function
``````
(Posted 2 months ago)

Hi Muddy,

BB tags are not supported on this site, it uses markdown, click the ? icon above the post box to see the markdown guide. Multi-line code starts and ends with 3 grave accents (or back ticks) in a row. You can also add a "blitzbasic" after them for syntax highlighting.

Just to warn you, I don't understand quaternion maths very well and probably won't be able to help you there.

I recommend you to instead use bones to manipulate vertexes manually on skinned meshes, bones are easy to rotate. I assume by tilt you mean roll (bank left/right z-axis) and not pitch (nose up/down x-axis) or yaw (turn left/right y-axis).

Have a look at this Blitz3d example showing how to rotate (turn y-axis) a neck bone while also animating the model.

``````; rotate_bones.bb
; using manual animation mode

Graphics3D 800,600
SetBuffer BackBuffer()

Local camera=CreateCamera()
Local light=CreateLight()

PositionEntity camera,0,10,-30
;Animate anim_ent,1,0.1 ; set mode: 1=loop, 2=ping-pong, 3=one-time

Local joint1 = FindChild(anim_ent,"Joint8")
Local joint2 = FindChild(anim_ent,"Joint10")
Local anim_time#=0, joint1_y#, joint2_y#

While Not KeyHit(1) ;ESC

If KeyDown(30) Then joint1_y=joint1_y-1 ;A
If KeyDown(32) Then joint1_y=joint1_y+1 ;D
If KeyDown(16) Then joint2_y=joint2_y-1 ;Q
If KeyDown(18) Then joint2_y=joint2_y+1 ;E

; in Blitz3d, UpdateWorld is for automatic animations, SetAnimTime is for manual
anim_time=anim_time+0.25
If anim_time>20 Then anim_time=2
SetAnimTime anim_ent,anim_time ; set animation frame, also disables mode

; move bones
If joint1<>0 Then RotateEntity joint1,0,joint1_y,0
If joint2<>0 Then RotateEntity joint2,0,joint2_y,0

;UpdateWorld
RenderWorld

Text 0,20,"+/- to animate, anim_time: "+anim_time
Text 0,40,"Q/E: rotate joint1, A/D: rotate joint2"

Flip
Wend
End``````
(Posted 2 months ago)

Yes. My animmesh is a skin.

(Posted 2 months ago)

yes you can set a pivot / entity as a child of a bone so that it will follow the bone rotation / translation while animating.
bone = findchild(riggedmesh,"bonename")
entityparent(entity,bone,true)

and maybe use turnentity(entity,turnpitch,turnyaw,turnroll) to oriente the entity as you want, related to the bone.

(Posted 2 months ago)
``````
a\$="504B030414000000080013707C543C23064012080000042D00000A0000004561"
a\$=a\$+"737465722E623364D59A45ACD54018461FEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
a\$=a\$+"EEEE61090B9610C28A84050909F64DEF813E287406C28686C3E9EDFDF8A79D4E"
a\$=a\$+"9F1EBD7B8E1C33AA7FD4BEBD79ABBD79D5C7232A6ABA30FF2AB27CB12C4D9BD5"
a\$=a\$+"18137AF7ED7B3D2A2A7BCDA8A8EDD74DFEFDE4F925A8E12F915CF568B9A8909C"
a\$=a\$+"BEAFEF58CF29E7EDDF972FD96E98CF853FBCAC5AEC43EF6BBFAAE7924B1C33C7"
a\$=a\$+"F999DFED9FCECD8D68E7F7DAAFEAB9E4D83F6BCED473CC5537EDC6B6F74B0D97"
a\$=a\$+"9CA967DA8D1DE8BF603D3FE7677E55CFB15F6AB8E44C3D977E31F55C721962BF"
a\$=a\$+"BB7EE2CBD31AF67E7957DD3177FDCB97D735625BC7DFBBEA2667EFBFC8FE391C"
a\$=a\$+"4775C79CD93F975C75979CFA58DF6F57CE3B5E3F13CCD570CC79F53E7FD956DD"
a\$=a\$+"A59E434EF516D7FCF4659BD7AE7F3F0ED673CC79F5741CD57F3ABFC17A7ECEAF"
a\$=a\$+"FFB17F3752F07DA46EB0FF4CCED4CD407BDFEA868DC16FE3FE37CF118ECF254E"
a\$=a\$+"77CC7DEF97E8FD16D6AE4BCEA19EDFCFFEB536FD37C7E19C8BEC9F9FFBDBE7A1"
a\$=a\$+"9CCBF373F4E7D83FC879F70DB7F31B9A8BDE7FD5ECEF01913EB69F0F7BCEECDF"
a\$=a\$+"B79CFD78FDDCDF3F9FFED5F3B3F53D20FA38757B6E0FCFC50E1BCF7EBBD6F705"
a\$=a\$+"8BF4B1FD7CD873D1C7A9FD78FDDCDFBE2FFCE5FB8CF5BDCC3E4EDF5DFF935C6C"
a\$=a\$+"EBFDD96BD7E5FD2DD22FD6FBAE57CF29E7F87EE9BDE7D9C73339EB7DF75DF53F"
a\$=a\$+"D59EF3DB2D7AA552A9DFBC9FD770CF79EDDA72371C73DFDBFD3606ECFD1CC9D9"
a\$=a\$+"FBEF8F7251F6F77D3FE7701C5ECE365E123ECB752A920FF69F7BCE3B0E5BAE86"
a\$=a\$+"63EE7BBBDFCE977D5C4572F671FAC7B9D079157BEEC7E3F883799528C7F912B3"
a\$=a\$+"FC57F3253F1FACE3BC8AC939BCEB474DFF37F32F5A7E9A7F71C84D37B9B0F366"
a\$=a\$+"CF6DAFE1968B38389F13ACE796FBA1DE74930B19570EB9ED35DC725E3D7FBEC9"
a\$=a\$+"5EEF7B2E643C3BE4ECF35CD1EB05E7B982F57ECE855D47F69C7D7E2D7ABDE0FC"
a\$=a\$+"5AB0DECFB9B0EBD79EB3CFEB45AF179CD70BD6FB3917723F70C8D9E713A3D5F3"
a\$=a\$+"E71343EAFD9C0BB9BF38E4ECF398D1EB7DBB7E5279C7F3FBF9E2602ED8AE5BBD"
a\$=a\$+"C862CFD9E759A3DF877EC859EFE5915CD8FDE0C75CF0BA746BD7BF3EFE60FFA6"
a\$=a\$+"3BD6FB9E0B1BF7D17321E3CFCB39EEDFF7F799F07A7E2E64BCFC90731C07815C"
a\$=a\$+"EB960D5ABD881559378AC9F60AFC90A1BCA828E288D8A2B2A8C43653D05C2855"
a\$=a\$+"A7E82BFA88DE6280E82FFAC140B665119944463158641683C410914EA415C3C5"
a\$=a\$+"3031548C1419C40818C5B63122BD182DC68B7162AC9864AE3B310126B32D87C8"
a\$=a\$+"2EB28AA9229B9822F2885C22270321B798266689996206CC66DB3C3157CC110B"
a\$=a\$+"8875A220EBEB4521D63788C2AC6F144558DF2C8A8A4D62ABD8C27A31515C6C13"
a\$=a\$+"3BC50E33FAC56E5142EC12FBC45EB1471C1025C57E71481C64FD8828250E8B63"
a\$=a\$+"E228EB274469715C9C1227593F23CA88D3E29C38CBFA0551569C1797C445D6AF"
a\$=a\$+"A9D9AC699DFC3122A7CD7CB7066FC2F9F016BC0D6FC7D7707EBC16EFC0BBF01E"
a\$=a\$+"5C00AFC37BF17E5C10AFC707F1615C086FC047F1715C186FC427F1695C046FC6"
a\$=a\$+"67F1795C146FC517F1656CBA79586CD3CDD17ED1B5BD864B37978FED9F8E0AB8"
a\$=a\$+"0AAE8363E0AAB8168E892BE1DA38162E8F2BE21A3836AE8CABE3BA380EAE866B"
a\$=a\$+"E2B8B8316E8BE3E126383E6E8313E0E6B8354E88EBE346B81D4E841BE086B819"
a\$=a\$+"4E8C5BE096B83D4E82EB7D3B4DB8154E8ABBE2FE3819EE8693E37E3805EE89FB"
a\$=a\$+"E294B823EE8207E054B813EE8C7BE0D4B817EE8D07E234B803EE8EFBE0B47838"
a\$=a\$+"9E88D3E111383D9E8033E0D1783CCE8807E3617812CE8487E0A17814CE8CC7E0"
a\$=a\$+"B17832CE8207E191781CCE8AA7E2197839CE8667E2397821CE8E17E3657835CE"
a\$=a\$+"81A7E0B97811CE89A7E3D978055E8973E1597801CE8D97E055380F9E86E7E1F9"
a\$=a\$+"78292E1715596EE2BBF8A1F777E4EEDE2CA6B9ED34EDD3CBEB44E7DB4E7AFDB3"
a\$=a\$+"B47C1E8E27E2747804CE8807E3617812CE8487E0A17814CE8AA7E2197839CE86"
a\$=a\$+"67E2397821CE8E17E3657835CE81A7E0B978112E467F5CC1D7F1135C1CEFC4BB"
a\$=a\$+"F10D5C02EFC307F02D5C121FC247F06D5C0A1FC327F01D7C0F97C6A7F0197C1F"
a\$=a\$+"97C1E7F005FC003FC265F1257C153FFE36ACF04D7C173FC45F01504B03041400"
a\$=a\$+"0000080041BF7E540EBF46E2A50500009210000007000000517561742E6262A5"
a\$=a\$+"576D8FD3300CFECC49F71F8CF6653BD82BE20B0748883721F1CE49808484B236"
a\$=a\$+"DD2ABA74D7A4EBDA5F8FEDA449B7F50E04D2F5923A8F1FDB891D7797F0A91466"
a\$=a\$+"B25CC223D8CD27331CBE8A9DAC719C3F9CCEE7D3D9E2FCEC129E81294D5EA422"
a\$=a\$+"835CC13AAFC0E4506A09669D6A48D24C028EC2C0DA98EDA3E9B4AAAA49ACA375"
a\$=a\$+"3E89F249F96BBACC52D34C5B0E3DBD46A3B25069AEF444AFCD263B3F3B3FBBAA"
a\$=a\$+"B7125E96992CCECFEEBC4A6516C33635D17A701F6A51E1FF22CFB2C1F9D94B15"
a\$=a\$+"03615B1D0EC1AB10704F2AF83407680AE4F95AA815B92DD1F908CD1BA18C8634"
a\$=a\$+"813A2F41E5268D24E82CDDA24C8188A2B210517D7EF69CA06CE82A67179FB9A5"
a\$=a\$+"013C81D964369B77315F10B00D08B01806BDCEF225EEA2919B2DF24C980B97DF"
a\$=a\$+"56EDCF376B47E55292E054FF559A651CE5D0C57F1F66F66FF488E55739013DC3"
a\$=a\$+"FD769F3CB792D1AFFFE3760C819B13837261270B03023EE74618CC4C4A71D192"
a\$=a\$+"972A6259876998976662997411D9931C611A5E7EC634A572489951C6109792C8"
a\$=a\$+"22CE3DCA2B9CC43256526BC813349D1771AA84C1CCAB357AA561F8222D6464BE"
a\$=a\$+"1F7E50815CCC260F3B806D0BA0752EA72340DD05609DB9650FD06CE24BAA6E32"
a\$=a\$+"A1B70E7083090CCA014E4D5CC21557E24EE0BDB0CC24DE2185C42B26AB612D0B"
a\$=a\$+"BB6BA58138AF144AA96C41959BA52C68DF366566D26D96467C5CBA1333078523"
a\$=a\$+"A27FD7F602C2B71F152914A8405EC03DD03867CB6E7D4F86FCFA18B187EB75AB"
a\$=a\$+"AF3BFA51586F02BF267DDD62ED4DD726EA715273EA9BBC93DE21A9BBD71A2735"
a\$=a\$+"78F7481FFD1F2ED067CA85CA8DF508C641BA776343863D03EA617F221CB35CB0"
a\$=a\$+"33368BDE24F06CA9871E3A82A77D573522998E92F1BAE8C071E165A665677D86"
a\$=a\$+"73DCCE37C931FF2DD4B41D149C8FA3F671C0BDD398F7A3114C79839C5D569EF7"
a\$=a\$+"6DC4FE607BEA763C20A0ED3FB0BE6F61BDD69B63EBC989F5BAF7709A138276F7"
a\$=a\$+"C216046BFD01788A4EE47F117830DD0978D6F17FDE9EDA2DA55AB8F4B7E5C4D7"
a\$=a\$+"196A8E9F5D09B51892FF36A1477009FF7E815B727B1722BB2737B67646769DAE"
a\$=a\$+"C2EE6A62AB67D457C985D478DB4195A2BB4B1B90161BE9C301A1F1964CD50AAE"
a\$=a\$+"E7B4AAE07A0143740BAFCA8D30F8B1A5EF8E3A25FFCEDE9D3537322677BD0CF5"
a\$=a\$+"DBC9E2A8FA0596F7924A1C9F181F890F95FC0A9FB52D7041A77F3DC783BF8744"
a\$=a\$+"747E17F8BEE0771CB8732D1DA6C1B3C5A16E3135BD2F5CD57B9EF1010FDE881E"
a\$=a\$+"135B0CCB882ED81A7B5BD261F687187E273AC2241E333EC28C3D667518571D6C"
a\$=a\$+"059FD7C1E7434CF01951BCD7B64D2CA930C712E109CE56F8AC49079B638BB3ED"
a\$=a\$+"42501180C4F59B70AE6D101F04BEF109CEB68FB8831B1FF0DDD843542A157E18"
a\$=a\$+"276E858A21C154E46FE49083DA146564CA4276122D7C9615AEAFF47DD48FC843"
a\$=a\$+"5F2F3C92C45508FEA7B7B65869E8F1D4FF1E21EFB0D6F0133CCFA82697D25492"
a\$=a\$+"2A22FCE8080E86EF752A83F67BCE88C2D503C6A9DCCC0C3A9D10FFC9E22706D0"
a\$=a\$+"4EF7615A8769137A61BE912B613B64BEB10D1347879CB593B92D2406C113EB09"
a\$=a\$+"698FBA33372E1B1DF6E7935F604CC1BB4FB72D7DEA5A2889F910DCD72943586A"
a\$=a\$+"8FC48999DC508558004CEDD179E0DC01CD2924848FBFA1D52AA3369283DE882C"
a\$=a\$+"7BC4F99AA54A8AC2A72BD711BE611311F18127ECC481491376C1D6375F164EE1"
a\$=a\$+"C267CE3D701A173E531DBC3E85D73DF0DAC19B5378D3036F1CBC3A85573DF0EA"
a\$=a\$+"B8927F03504B03041400000008003ABF7E5467A4E412470D00006C2900000700"
a\$=a\$+"000054696C742E6262ED5A6D73DA4610FE5CCFF83F5CE20F8116A810719A9890"
a\$=a\$+"0E313871631B07706C3A9D76043A1B3542A7E8C584FCFA3EBBA7170498D036FD"
a\$=a\$+"56278074F7ECDEDEDEEEDE6A4FCD41EFEC43B773B4BFD71C28114DA508951B47"
a\$=a\$+"2CCF1617DD0FDDBE68773A62D4BE266287783B1EC36E9D208C84EF5A135943EB"
a\$=a\$+"B514C3A944178D2C7A1767A38C1975BF733CDB12AE83C12D7052D1D4B7C2488A"
a\$=a\$+"89E5A31B80E1D409C54C6202B616C4F13ED204C60BE178A10C6836100A48CBB6"
a\$=a\$+"5BCB75D53CD494126D16F1040106B73E932C8A1A48E7F732147E20A3682166F1"
a\$=a\$+"6F0FBB4702AA519EBB6045062AE281489B16903C834AA2E928E0250AE3711458"
a\$=a\$+"9328073BB7802E540C7579C2BA578E8DC93C7AF44888AB90146A3BB7B79267C5"
a\$=a\$+"FC5888536FE2C6B6148FDFC756541B8F1F931DC928F6F7F7DE04963F752661A3"
a\$=a\$+"239E1B46E5193EF8B7BF3790D2EE435FA573C7759D819C84A572797FAFEDFB43"
a\$=a\$+"2503AB04627DD5B7BC3B49FD957AC534F097D15CABC0B5CF9CBB699490F275A9"
a\$=a\$+"0E52BE3A56AE0A965095BA61E80F313957F7F25CC550423AA76BC78EA6A5F28F"
a\$=a\$+"1A828C0608F28BEFFFD61F08BA16FCCD77EE15FBB4964F4C11D08CCC396AEC6F"
a\$=a\$+"63E93AF21E914D8711CB23CF4660A87E110E2ED5DC0AEC104872F7099B248738"
a\$=a\$+"0DFA6144A0890A023989E0A8B1CFF0DA3F90787FEF52851C9EF402B0CD570F8D"
a\$=a\$+"CA53A3527D6E5486412C81212F5D0274EBDA94F3265A30F6497234D626D384DA"
a\$=a\$+"9ED48171B640E886337B6AAE83555F5A21A2E9186D8175672B10963C28FC8521"
a\$=a\$+"96E3B63075717A2BDAE370391A5533C2F2ABBAA1779CE6498005057C9D69A2AE"
a\$=a\$+"E625AC40C51E7EC4D8892A14A20146A49F419D0C8AA0126D4BD3648319FE7825"
a\$=a\$+"3ECA45089A3926A4D53050B42C4B3BAA35A758AFDD43ACEFA900B73FF44E3B0C"
a\$=a\$+"BE74A2C99456A38FD52132ED77FB7B7D1561ED313F681D1312CD5F629E85DEC2"
a\$=a\$+"76BA18FE6274DEEB77A15E71E5DB882F1C8A719B2C68767BE23A3E7EF5EA69E8"
a\$=a\$+"A9E7C708BB68ED7A3656130B2C3D804F02999A7A7BA26F73CB87751306CDB137"
a\$=a\$+"E1DD6F8559F3353AA6E2494F9C5C0E126786F37A51A0DC50940694EA38B78EB4"
a\$=a\$+"7959DF61253C6078639591B8B702876D9D1A43117BCEA758BA585418DB3BB978"
a\$=a\$+"EBD0DEC3868509D2DA8ABA68D29C0432ABEAFBABD32174F9EEA4651C69820E1C"
a\$=a\$+"A3D4682424D45327C08722E0E94F19E083060CA6CE6DA441A50C6596C50F19CD"
a\$=a\$+"E1D372F9E5ABC4C813024D7B1615B99B06C6EF05D9FD4F87D9686709497F8DE4"
a\$=a\$+"B048920BD84F482EEFAEFC55A27AF9BB65A274DA1A9C9275BC55B21785B19EA7"
a\$=a\$+"2068C6E0B535F9987218AE30C80986841F9253B3A82BB825C9087795E2066788"
a\$=a\$+"9C6745F0613EF781CBDDF50CDA5F819A45685F437F8967FE0A305D3BDD493032"
a\$=a\$+"672D77122387D521D932793939A9D081E3294200D0DA3B33AAAB8CEAAA3ABC3E"
a\$=a\$+"1D0C391032D95214E95214799AC610D1EC28CE72F951618A6474996DF3B8E8A5"
a\$=a\$+"FB7B3E45B403CC6361CDE967A6EE6F92DF51F2FB2B7E49A2C4BA52A112CA6A23"
a\$=a\$+"EBEB782B7D0D0A9E942527D1210D847EA0E0F933263CF5A25229D94789AA0411"
a\$=a\$+"CB3F6806E51F5F18E5D6B6DEC270FAE77B93F826FEA3FB31371254344B23C94F"
a\$=a\$+"1A0B7A520890A70421C253823F2BE2795EB9BF6F1AC9A8D5B5DEF0C577998693"
a\$=a\$+"A716DE7E123E99E6BED33AC5B699FF354FE67682EB78455C7519D8849F102E33"
a\$=a\$+"DB54305A37C2A6B897CBA87E1155CFB8BD22940E9670D1D46E73F0E8801DC94F"
a\$=a\$+"601F96BBD2D132661D6F93C69AC84FA45607FA1309E85BAB4F73A26F7DAFE74C"
a\$=a\$+"19D995479908DA6843E6485A54518004DCC5A6BD4C3C70BC52365D3D52F97BD6"
a\$=a\$+"7885D7A172ACC207003AC6ACE5C8B49CC9DCF9B1EE1DAC5EAF5C9A3B70F0E97E"
a\$=a\$+"8EE019B74953C8D513CB5B8C91B00A0F8FBC541B88148027CE5D1C48DAE8F1D8"
a\$=a\$+"4FB508EA9928CA2726988C9DE724A4341612B99434CBEC7FF778FA3AEA8DFFC4"
a\$=a\$+"3304523524E2C9C37B5E8C48128ABE9C9897CA3D48267B435CF8C1A05CD12DBF"
a\$=a\$+"2EB5E418B388C95BCAAB732ECA8995FD2672AE4B35DA28B9B982C9E5ACBE306A"
a\$=a\$+"C676616156DF44D89B1D84BDD92A6C51CEBC4665B9736B1172556D8AAC7FC1E6"
a\$=a\$+"34A37CBD64766047BEB877E43CC4C39783EDB022C6B0266B4C1E622177B77C18"
a\$=a\$+"930CCBF9AC73C96FEA0795113E37267ECD032A9E1C889638876F8A1F45FD3954"
a\$=a\$+"D7BBA116004455007E247A236A1871C3080D7DBA1D7C0A4A2582FE2E38A32C01"
a\$=a\$+"C5D77ACB19DC79D45B6E19A2EDD9FA7644B759DA4B3BF01B858CF9C689FA4CD3"
a\$=a\$+"BB79C9680C988694F7C316645AED7DB5A9B795F56E1FE3558A2BC8F2620D9833"
a\$=a\$+"7CB904A401D7C5DEC8D2FCA980441BE9ED85516D0F2DAF047540E1FD8332BE49"
a\$=a\$+"53FAA6FCC37BE4FEB58C220E34B32D065D0893A28967A58FA108D2E7ED05150D"
a\$=a\$+"B6E6CB8D06FF665C0A5B8AF1706F710C0CBA3646DE9873597A9043EF0607BA50"
a\$=a\$+"11475C6953009EA940E6CAE294B45EFA1C1C5416F87C09C8019A620E9D5144E6"
a\$=a\$+"FDE5134A993A7E4B1486C06ACE0FBABCD4207C99AC31AE67072D34882EB60DDC"
a\$=a\$+"F97CC7B0C5126C41B0450A5B106C91C0BE2CC1BE10EC4B02C39DCF77901C5586"
a\$=a\$+"6EECCAA014C9998FAB0A0F5C1106FE97C591E0CEA1A202AC98395E1C5E564402"
a\$=a\$+"7D80DA7F80DA77BF4A0C329ACF43438F76A0E6D1D787DE8518FF494D1B87EEEF"
a\$=a\$+"46ED1FAC0F5D246ED20304EDC41E3E765601444CC6AE0D8390CE3D9AC754A6C7"
a\$=a\$+"D1080E3CB07F6B9B41EA903411E3D2043552BAA888EC4AABB8BC031073DA11C9"
a\$=a\$+"BD67D3A9A676007D957BF60E40CC694724CBBF0B102C7744F26AEC02DCE6D929"
a\$=a\$+"6EDDB3CD7FE9D999336F3890682E9F47D050124FEFA58B5E4D5C4F2D7D007D2A"
a\$=a\$+"C299A253D89FCB5CFC396815320C59D7E3968FB88FBEC4394E821BCFF889818B"
a\$=a\$+"1D79EE91C3B33248018E79A470CABF96F1DC85AF027E597C3AED5B6B32579BF8"
a\$=a\$+"306FF5AC8748777FFA587A9CE2EF756EE637E5D6F857DCB431E5F3A415D2850F"
a\$=a\$+"52E64690B90BA8B109B4BE22B2BEBE24EB6D0D6ECB8B04BC96F87778289A6F90"
a\$=a\$+"F937CDD6DCDD6C7154BCD664AE376D305B90EEF240BCFC84BFC5D030EA37E5D6"
a\$=a\$+"CB669B0B888E55B3CD0563BACC6CB7C4F1F4C08FEB2CAE0A6518514A46313AAB"
a\$=a\$+"95E9B2D8E0EAF5B0DF3E1E7269FF3650333A60545C93416F47A1B68D034326A6"
a\$=a\$+"3C807F8A9D809E4185AB92D79E548C374492D7856C9C144ED55CFC49946F7BD7"
a\$=a\$+"62FB6893DE4D567C52FB8142EFC05ABFFEB435D06C092A5B02080DA54F7CAAC4"
a\$=a\$+"9BAC87D6A47ED0C2122D122E5E3D87A5A8ACC1A0B520FE4467AED215608CCA1B"
a\$=a\$+"6805A354F275BB6EB07E975EB4C8B4DB3CB7BC18A7412195F19601362CD97B42"
a\$=a\$+"830ACA847E4E8AFF7446CC273602DC037A9348D02B11B5C204DA137659AA7016"
a\$=a\$+"5EF2C8E7E7D737116478AE8EF8B222703AE469CBD1367517483A00E2640BA0C4"
a\$=a\$+"38D3B755505474EE3C7A19E1D1D775F25497A1CE17C9B9186646134D3403BFA8"
a\$=a\$+"EAF2A44D28D4F0C3694D0C30E17F5A9B4A7308CC33B7CABC79B5F8945B8291B9"
a\$=a\$+"FC7AEF7A656A7D88BC9DD97CBD36F517504B0102140014000000080013707C54"
a\$=a\$+"3C23064012080000042D00000A00000000000000000020200000000000004561"
a\$=a\$+"737465722E623364504B0102140014000000080041BF7E540EBF46E2A5050000"
a\$=a\$+"9210000007000000000000000000202000003A080000517561742E6262504B01"
a\$=a\$+"0214001400000008003ABF7E5467A4E412470D00006C29000007000000000000"
a\$=a\$+"00000020200000040E000054696C742E6262504B05060000000003000300A200"
a\$=a\$+"0000701B00000000"
Fil\$ = "Neck Tilt.zip"
N\$ = CurrentDir\$() + Fil\$
If FileType(N\$) <> 0 Then RuntimeError N\$ + " file already exists!"
Print N\$
Print "What folder to write " + Fil\$ + " to: [Enter=Default]"
Nm\$=Input\$()
If Nm\$ = "" Then
Nm\$ = N\$
Else
If Right\$(Nm\$, 1) <> "\" Then Nm\$ = Nm\$ + "\"
Nm\$ = Nm\$ + Fil\$
End If
If FileType(Nm\$) <> 0 Then RuntimeError Nm\$ + " file already exists!"
f = WriteFile(Nm\$)
For t = 1 To Len(a\$) Step 2
nib1 = InStr("0123456789ABCDEF", Mid\$(a\$, t + 0, 1)) - 1
nib2 = InStr("0123456789ABCDEF", Mid\$(a\$, t + 1, 1)) - 1
WriteByte f, (nib1 * 16) + nib2: Next
CloseFile (f)
``````

Thanks, RemiD, Sorry for these lengthy posts, but here's a file-recreator that will provide all the above source code along with the skinned.b3d file in a .zip archive.
This will make everything easier to understand for everybody!

Yes. I already use this as a bandaid to subtracting rotation.
Best to avoid adding it in the FIRST PLACE!

``E2=FindChild(Ac,"z_Neck"):TurnEntity E2,0,-EntityYaw#(E2,false),0,False``

So, see my fixes for the problem. It works correctly now.
Wrongthinking has been punished.
Thanks everyone!

You have my permission to put it in the CodeArc for posterity if you desire.
You can change and alter my code at will.

(Posted 2 months ago)

Hi Muddy,

here's a file host and screenie for people. So can you rotate the head in the other way without causing chaos?

Neck-Tilt.zip

(Posted 2 months ago)

My last post shows the only solution I ever found. Subtraction of 1 axis of rotation from cumulative 3 axis changes is imperfect. You can try the NeckFix function to see what at first APPEARS to work, but it just doesn't compare with the perfect linear results of a small forethought.