SOLVED: Tilting, not rotating


Tweet
(Posted 3 months ago) MuddyWaters

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) markcwm commented:

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) MuddyWaters commented:

Thank you for replying mark!

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) MuddyWaters commented:

Here's Tilt.bb:


;SOLVED:
;So the solution for 'Tilting' the neck without
;twisting it was simply to make the neck the
;parent of the head, and NEVER ADD YAW to it
;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 Ac=LoadAnimMesh("Easter.b3d") ;Actor skin
Global e1=FindChild(Ac,"z_Head")
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
;AVOID ADD Pitch or Roll to z_Head
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

If TKU=1 Then ;U-TWIST head
   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) markcwm commented:

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()
Local anim_ent=LoadAnimMesh("media/zombie.b3d")

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) MuddyWaters commented:

Yes. My animmesh is a skin.

(Posted 2 months ago) RemiD commented:

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) MuddyWaters commented:

a$="504B030414000000080013707C543C23064012080000042D00000A0000004561"
a$=a$+"737465722E623364D59A45ACD54018461FEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
a$=a$+"EEEE61090B9610C28A84050909F64DEF813E287406C28686C3E9EDFDF8A79D4E"
a$=a$+"4B3BB9356B96AAFDB17054548CA8A8A8D675DAB78AD252B3659B560DE5F8A27E"
a$=a$+"9F1EBD7B8E1C33AA7FD4BEBD79ABBD79D5C7232A6ABA30FF2AB27CB12C4D9BD5"
a$=a$+"AEB3B670A45A54F4C514FA01FDF58D26755AD52F9C3B52BC6DCBD6AD2A678F34"
a$=a$+"18137AF7ED7B3D2A2A7BCDA8A8EDD74DFEFDE4F925A8E12F915CF568B9A8909C"
a$=a$+"BEAFEF58CF29E7EDDF972FD96E98CF853FBCAC5AEC43EF6BBFAAE7924B1C33C7"
a$=a$+"8DE75FDAD58C45BBFA73CDF34F4B86D833AFBBE4544FED0DFF9EF38F2158CFCF"
a$=a$+"F999DFED9FCECD8D68E7F7DAAFEAB9E4D83F6BCED473CC5537EDC6B6F74B0D97"
a$=a$+"9CA967DA8D1DE8BF603D3FE7677E55CFB15F6AB8E44C3D977E31F55C721962BF"
a$=a$+"BB7EE2CBD31AF67E7957DD3177FDCB97D735625BC7DFBBEA2667EFBFC8FE391C"
a$=a$+"4775C79CD93F975C75979CFA58DF6F57CE3B5E3F13CCD570CC79F53E7FD956DD"
a$=a$+"A59E434EF516D7FCF4659BD7AE7F3F0ED673CC79F5741CD57F3ABFC17A7ECEAF"
a$=a$+"675D7E3DAECCB1BAE48AD7ED59E39BC372130F0F3CF3AD6E58FF95A8DBF346A0"
a$=a$+"DE2F97EDDFC6C9D9B05C51EA9594C3729D63142F4BDDDFE6A853235ADDE921FB"
a$=a$+"57E3DBB1769D35D83B6769DEDEAAFA8BFEF372C63FE682FDF7ADE6B7EFBB79F9"
a$=a$+"60FF51CFE4AEFAED07FB8FFD3BAB3ADE31A4FC45BB45A967FAAF2BB90C5EDD60"
a$=a$+"FFB17F3752F07DA46EB0FF4CCED4CD407BDFEA868DC16FE3FE37CF118ECF254E"
a$=a$+"CF2FDFEB599E37AEBBE7ECCF25FEFEF9F7ABA78F1F97FAD5FEFD49CEB46BC95D"
a$=a$+"77CC7DEF97E8FD16D6AE4BCEA19EDFCFFEB536FD37C7E19C8BEC9F9FFBDBE7A1"
a$=a$+"BF7C5EB33E779ADCB75A6ECF89E1B95861FF0FFAED5A9F4FA3F7CBB75C483DA7"
a$=a$+"9CCBF373F4E7D83FC879F70DB7F31B9A8BDE7FD5ECEF01913EB69F0F7BCEECDF"
a$=a$+"B79CFD78FDDCDF3F9FFED5F3B3F53D20FA38757B6E0FCFC50E1BCF7EBBD6F705"
a$=a$+"937319CF26E7329E4DCEE57DC61C87CB78363997716ADAFD935CB4FEAB667F2F"
a$=a$+"8BF4B1FD7CD873D1C7A9FD78FDDCDFBE2FFCE5FB8CF5BDCC3E4EDF5DFF935C6C"
a$=a$+"EBFDD96BD7E5FD2DD22FD6FBAE57CF29E7F87EE9BDE7D9C73339EB7DF75DF53F"
a$=a$+"C9D9C773A45DFB38F5CE8773CE619C9A76ADB93F7BBF747AAFADE1F8DEF8FD7D"
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$+"A03ADB6A899AC2345E57D411B5451291482414F54562514F3410F1445CD15834"
a$=a$+"120D455391403481666C6BC1CF309A9BFD17AD444BD14EB4156DA03DDBD2986B"
a$=a$+"45A4141D456AD1417412C9445273AD8B2EE63C88EE2285B9CEA007DB7A89E4A2"
a$=a$+"A7E82BFA88DE6280E82FFAC140B665119944463158641683C410914EA415C3C5"
a$=a$+"3031548C1419C40818C5B63122BD182DC68B7162AC9864AE3B310126B32D87C8"
a$=a$+"2EB28AA9229B9822F2885C22270321B798266689996206CC66DB3C3157CC110B"
a$=a$+"C502315F2C118BC52258CAB61562B95826568B5562A5C827F28BBC62AD2820D6"
a$=a$+"8875A220EBEB4521D63788C2AC6F144558DF2C8A8A4D62ABD8C27A31515C6C13"
a$=a$+"3BC50E33FAC56E5142EC12FBC45EB1471C1025C57E71481C64FD8828250E8B63"
a$=a$+"E228EB274469715C9C1227593F23CA88D3E29C38CBFA0551569C1797C445D6AF"
a$=a$+"8A2BE232BEC6BAB990AF8B72E2169F6F8ADBE633EB77CC67D6EF8B7BE2AE7860"
a$=a$+"3EB3FE583C120FC513F39975F343A322F13556BAD51CD67B4254F425F853237F"
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$+"CBCA86EE119B5495FAE384E8DC3A4DFDF236F3AB47CB41FBFBADDADFFFA0FDF9"
a$=a$+"56EDCF376B47E55292E054FF559A651CE5D0C57F1F66F66FF488E55739013DC3"
a$=a$+"FD769F3CB792D1AFFFE3760C819B13837261270B03023EE74618CC4C4A71D192"
a$=a$+"972A6259876998976662997411D9931C611A5E7EC634A572489951C6109792C8"
a$=a$+"22CE3DCA2B9CC43256526BC813349D1771AA84C1CCAB357AA561F8222D6464BE"
a$=a$+"C14E4FE0C356AAD76F212FE0C18B2FF04EECC9CADB3CA2AD2E28FF9EE77A3846"
a$=a$+"1F7E50815CCC260F3B806D0BA0752EA72340DD05609DB9650FD06CE24BAA6E32"
a$=a$+"A1B70E7083090CCA014E4D5CC21557E24EE0BDB0CC24DE2185C42B26AB612D0B"
a$=a$+"BB6BA58138AF144AA96C41959BA52C68DF366566D26D96467C5CBA1333078523"
a$=a$+"5C607C1D47AD279AE4BA2B8FACFC081F397C64F1CEE1D752C902CF8A9DC114D8"
a$=a$+"A27FD7F602C2B71F152914A8405EC03DD03867CB6E7D4F86FCFA18B187EB75AB"
a$=a$+"AF3BFA51586F02BF267DDD62ED4DD726EA715273EA9BBC93DE21A9BBD71A2735"
a$=a$+"CF6C56D352E724536506F79156E3406F3BFBB6B36F897D4B06AD02237F529D0D"
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$+"DD87E407A5AD93D44E527B49E3248D97544E52B54DCC923EE61F2328F146C63C"
a$=a$+"21491B1609AD6A10EE5BE1BE23AC5B61DD1136ADB071BDE188BA8FB98FB88F97"
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$+"8EF2C4AD0AC493A1E3468E77F784BB3C39F928E64E345571048268EE84D4299C"
a$=a$+"48CCAD5084CECC7717225262667D9419055D00ED5B81F422A16EB9632A2DBB22"
a$=a$+"2CCF1617DD0FDDBE68773A62D4BE266287783B1EC36E9D208C84EF5A135943EB"
a$=a$+"B514C3A944178D2C7A1767A38C1975BF733CDB12AE83C12D7052D1D4B7C2488A"
a$=a$+"89E5A31B80E1D409C54C6202B616C4F13ED204C60BE178A10C6836100A48CBB6"
a$=a$+"1DD282E58A3F9503B9AD486BC79A49E1730B440C7D080634E64173665D685C32"
a$=a$+"5BCB75D53CD494126D16F1040106B73E932C8A1A48E7F732147E20A3682166F1"
a$=a$+"642A482BA3E1DBD38B3734B3918A9F0452CCA53B5133A927732AFADDEAE9B0DB"
a$=a$+"6F0FBB4702AA519EBB6045062AE281489B16903C834AA2E928E0250AE3711458"
a$=a$+"9328073BB7802E540C7579C2BA578E8DC93C7AF44888AB90146A3BB7B79267C5"
a$=a$+"FC5888536FE2C6B6148FDFC756541B8F1F931DC928F6F7F7DE04963F752661A3"
a$=a$+"239E1B46E5193EF8B7BF3790D2EE435FA573C7759D819C84A572797FAFEDFB43"
a$=a$+"2772C1EA826C86EC4E74E44C8901E9C67E0C86AE1A63398EAD59EB3890562471"
a$=a$+"2503AB04627DD5B7BC3B49FD957AC534F097D15CABC0B5CF9CBB699490F275A9"
a$=a$+"0E52BE3A56AE0A965095BA61E80F313957F7F25CC550423AA76BC78EA6A5F28F"
a$=a$+"F56795ACEDAD244A6E3C3A71E370CA24245D26865F4F258FC7E839EA7A91132D"
a$=a$+"7870F4413DE6E161C5C8E166019EB737B6B0696836F4591AD9DB32B2B73EB2F7"
a$=a$+"C0C85E630B9BCD23B727AD3365D96DCF999DCB705A7ADC25970C6AE386FDB82C"
a$=a$+"9AED4904DA10AE9351C87AEB048E7C3C755CBBD49E541E7FF9E3ADB480CE11E6"
a$=a$+"1A828C0608F28BEFFFD61F08BA16FCCD77EE15FBB4964F4C11D08CCC396AEC6F"
a$=a$+"63E93AF21E914D8711CB23CF4660A87E110E2ED5DC0AEC104872F7099B248738"
a$=a$+"0DFA6144A0890A023989E0A8B1CFF0DA3F90787FEF52851C9EF402B0CD570F8D"
a$=a$+"CA53A3527D6E5486412C81212F5D0274EBDA94F3265A30F6497234D626D384DA"
a$=a$+"6835EE5207B26EBDD23545F35879F7D273D072441A79E2BA3AD63B08D2329859"
a$=a$+"9ED48171B640E886337B6AAE83555F5A21A2E9186D8175672B10963C28FC8521"
a$=a$+"54005D848E2DABB69A7B187A1807E9C45697F9B5B2178FCB15834DAD7262B9A1"
a$=a$+"64F6978133B310D8FAD2B3B178674A2102F55C9B634CD8CA430DD85F4F49B2D7"
a$=a$+"96E3B63075717A2BDAE370391A5533C2F2ABBAA1779CE6498005057C9D69A2AE"
a$=a$+"E0F3410B71AD84A0513E0A1607AD7ACD380ABEE48DB4FFD0BEC52671176066D9"
a$=a$+"E625AC40C51E7EC4D8892A14A20146A49F419D0C8AA0126D4BD3648319FE7825"
a$=a$+"3ECA45089A3926A4D53050B42C4B3BAA35A758AFDD43ACEFA900B73FF44E3B0C"
a$=a$+"BE74A2C99456A38FD52132ED77FB7B7D1561ED313F681D1312CD5F629E85DEC2"
a$=a$+"EE9C197C125C3F17564ED609BD209A7C99488C13E773098EFFB27AD11B624BEA"
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$+"0AD6604DB63E965114D04C88188A35A178294E5440A19A18AC8FFABC38AA9130"
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$+"DF7C9F1B6F1326AF3767ADE16C27CD13355A858A3655DD8EBD9D0DE66562276B"
a$=a$+"148D67C6468257E87880A49AD0AC74688BA814D95D72E33611C06C23CD7629AA"
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$+"5D71D2EB8BF36EAD5603249AF94919E7928A1E4446A95A16490950590DD84513"
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$+"F2EF0204CB1D91BC1ABB00C15223E976A858ADA92673DCFE5EC1F3B1412690DF"
a$=a$+"38B1CCEE90E266D701425A9A90ADE75A6128C1CB721FAD78B6F9BF67FFEFD9FF"
a$=a$+"BD67D3A9A676007D957BF60E40CC694724CBBF0B102C7744F26AEC02DCE6D929"
a$=a$+"6EDDB3CD7FE9D999336F3890682E9F47D050124FEFA58B5E4D5C4F2D7D007D2A"
a$=a$+"C299A253D89FCB5CFC396815320C59D7E3968FB88FBEC4394E821BCFF889818B"
a$=a$+"1D79EE91C3B33248018E79A470CABF96F1DC85AF027E597C3AED5B6B32579BF8"
a$=a$+"306FF5AC8748777FFA587A9CE2EF756EE637E5D6F857DCB431E5F3A415D2850F"
a$=a$+"52E64690B90BA8B109B4BE22B2BEBE24EB6D0D6ECB8B04BC96F87778289A6F90"
a$=a$+"ADFF5AEC34D141FD48CCA791B8297636988E3A5FBBB118EDEF6D345BF3EF99AD"
a$=a$+"F937CDD6DCDD6C7154BCD664AE376D305B90EEF240BCFC84BFC5D030EA37E5D6"
a$=a$+"F857DC0AC6E6ED62B6DE2E66EBED62B61E5531D69764BDAD81B682F17989D99A"
a$=a$+"CB669B0B888E55B3CD0563BACC6CB7C4F1F4C08FEB2CAE0A6518514A46313AAB"
a$=a$+"95E9B2D8E0EAF5B0DF3E1E7269FF3650333A60545C93416F47A1B68D034326A6"
a$=a$+"0C4EBF1FB35CAD81276C76043E6CE403D0D32894EE6DF19892665905257E1E4A"
a$=a$+"374F9FCC842BAD7B08CD69A4C25740079E324C0E21F489E8584E2CBCF2C13578"
a$=a$+"3C807F8A9D809E4185AB92D79E548C374492D7856C9C144ED55CFC49946F7BD7"
a$=a$+"E2FCEAF8AD88700E4CE786E96B387752BF0C15297EEF8A879F078A040950FA59"
a$=a$+"62FB6893DE4D567C52FB8142EFC05ABFFEB435D06C092A5B02080DA54F7CAAC4"
a$=a$+"9BAC87D6A47ED0C2122D122E5E3D87A5A8ACC1A0B520FE4467AED215608CCA1B"
a$=a$+"AA84AFD260649573240EE22E9661B871AD4D80F4B26AEDDB8A5796EA6E383459"
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) markcwm commented:

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) MuddyWaters commented:

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.

Reply To Topic (minimum 10 characters)

Please log in to reply