OpenB3D: TQuaternion function issue

Started by Krischan, October 26, 2017, 21:09:21

Previous topic - Next topic

Krischan

I digged out a cool demo I wrote in miniB3D before I started with OpenB3D but I can't get it running in OpenB3D (it still runs fine in miniB3D). I compared the two TQuaternion.bmx includes and something has changed in OpenB3D with some vars but I don't know how to fix my code. Do you know what I need to change?

Just uncomment the "Framework openb3d.b3dglgraphics" line and comment or delete the "Framework sidesign.minib3d" line.

Code (Blitzmax) Select
SuperStrict

Framework sidesign.minib3d
'Framework openb3d.b3dglgraphics

Graphics3D DesktopWidth(), DesktopHeight(), 32, 2

SeedRnd 1

Const eccentricity:Float = 45 ' angle between 0 and 90
Const planets:Int = 10 ' number of planets
Const speed:Float = 0.1 ' simulation speed
Const maxsegments:Int = 1440 ' maximum segments of outer planet orbit

Global MouseWheel:Int = 1

' ------------------------------------------------------------------------------------------------
' Initialize Scene and Camera
' ------------------------------------------------------------------------------------------------
Global pivot:TPivot = CreatePivot()

Global cam:TCamera = CreateCamera(pivot)
PositionEntity cam, 0, 5, -15

Global star:TMesh = CreateSphere(32)
ScaleEntity star, 0.5, 0.5, 0.5
EntityColor star, 255, 192, 0
EntityFX star, 1

PointEntity cam, star

' ------------------------------------------------------------------------------------------------
' Stores Planet properties
' ------------------------------------------------------------------------------------------------
Type TPlanet

Field id:Int
Field rotation:Float[]
Field ent:TEntity
Field piv:TPivot
Field radius:Float
Field rgb:Int[] = [255, 255, 255]

Field orbit:TOrbit = New TOrbit

End Type

' ------------------------------------------------------------------------------------------------
' Stores Orbit Coordinates
' ------------------------------------------------------------------------------------------------
Type TOrbit

Field steps:Float
Field x:Float[maxsegments + 1]
Field y:Float[maxsegments + 1]
Field z:Float[maxsegments + 1]

End Type

' ------------------------------------------------------------------------------------------------
' Initialize Planets
' ------------------------------------------------------------------------------------------------
Global planetlist:TList = CreateList()
Global p:TPlanet

For Local i:Int = 1 To planets

p = New TPlanet
p.id = i
p.radius = Normalize(i, 1, planets, 1, 10)
p.rotation = [Float(Rnd(-eccentricity, eccentricity)), Float(Rnd(360)), 0.0]
p.rgb = [Rand(255), Rand(255), Rand(255)]

p.orbit.steps = maxsegments / (planets + 1 - p.id)

' planet pivot
p.piv = CreatePivot()

' create planet
p.ent = CreateSphere(8, p.piv)
EntityFX p.ent, 1
'EntityColor p.ent, p.rgb[0], p.rgb[1], p.rgb[2]
ScaleEntity p.ent, 0.1, 0.1, 0.1

' pivot initial rotation
RotateEntity p.piv, 0, 0, 0
Turn p.piv, p.rotation[0], p.rotation[1], p.rotation[2]

' position planet
PositionEntity p.ent, 0, 0, 0
MoveEntity p.ent, 0, 0, p.radius

ListAddLast planetlist, p

' store current position in Type for later use
For Local i:Int = 0 To p.orbit.steps

p.orbit.x[i] = EntityX(p.ent, 1)
p.orbit.y[i] = EntityY(p.ent, 1)
p.orbit.z[i] = EntityZ(p.ent, 1)

' rotate pivot
Turn p.piv, 0, 360.0 / p.orbit.steps, 0

Next

Next

MoveMouse GraphicsWidth() / 2, GraphicsHeight() / 2

' Mousewheel logic
AddHook EmitEventHook, EventHook

' ------------------------------------------------------------------------------------------------
' Main Loop
' ------------------------------------------------------------------------------------------------
While Not AppTerminate()

If KeyHit(KEY_ESCAPE) Then End

Turn pivot, Normalize(MouseY(), 0, GraphicsHeight() - 1, -1, 1), Normalize(MouseX(), 0, GraphicsWidth() - 1, 1, -1), 0

CameraZoom cam, MouseWheel

RenderWorld

' enable OpenGL Lines
' it is IMPORTANT to use this between Renderworld and BeginMax2D or it won't work
glEnable(GL_COLOR_MATERIAL)
glBegin(GL_LINES)

Local x1:Float, y1:Float, z1:Float
Local x2:Float, y2:Float, z2:Float

' cycle all planets
For Local p:TPlanet = EachIn planetlist

' a full rotation
For Local i:Int = 0 To p.orbit.steps

' look ahead for next XYZ position
Local j:Int = (i + 1) Mod p.orbit.steps

' get current coordinates
x1 = p.orbit.x[i]
y1 = p.orbit.y[i]
z1 = p.orbit.z[i]

' get next coordinates
x2 = p.orbit.x[j]
y2 = p.orbit.y[j]
z2 = p.orbit.z[j]

' draw 3D line
Line3D(x1, y1, z1, x2, y2, z2, p.rgb[0], p.rgb[1], p.rgb[2], 1)

Next

' turn pivot = move planet around star
Turn p.piv, 0, (planets + 1 - p.radius) * speed, 0

Next

glEnd
glDisable(GL_COLOR_MATERIAL)

BeginMax2D()

DrawText "Maximum Eccentricity: " + eccentricity, 0, 0

EndMax2D()

Flip True

Wend

End

' ------------------------------------------------------------------------------------------------
' Normalizes a value to given range
' ------------------------------------------------------------------------------------------------
Function Normalize:Float(Value:Float, vmin:Float, vmax:Float, nmin:Float, nmax:Float, limit:Int = False)

' normalize
Local result:Float = ((Value - vmin) / (vmax - vmin)) * (nmax - nmin) + nmin

' limit
If limit Then

If Value >= nmax Then result = nmax
If Value <= nmin Then result = nmin

EndIf

Return result

End Function

' ------------------------------------------------------------------------------------------------
' Draw a 3D line using OpenGL functions:
' glEnable(GL_COLOR_MATERIAL) / glBegin(GL_LINES) [...] glEnd() / glDisable(GL_COLOR_MATERIAL)
' ------------------------------------------------------------------------------------------------
Function Line3D:TMesh(x0:Float, y0:Float, z0:Float, x1:Float, y1:Float, z1:Float, r:Int = 255, g:Int = 255, b:Int = 255, a:Float = 1.0)

SetColor r, g, b
SetAlpha(a)

glVertex3f(x0, y0, -z0)
glVertex3f(x1, y1, -z1)

SetAlpha(1.0)

End Function

' ------------------------------------------------------------------------------------------------
' Turn Entity using Quaternions
' ------------------------------------------------------------------------------------------------
Function Turn(Ent:TEntity, X:Float, Y:Float, Z:Float, Glob:Int = False)

Local Pitch:Float = 0.0
Local Yaw:Float = 0.0
Local Roll:Float = 0.0

Local Quat:TQuaternion = EulerToQuat(0.0, 0.0, 0.0)
Local Turn_Quat:TQuaternion = EulerToQuat(0.0, 0.0, 0.0)

If Glob = False

Quat = EulerToQuat(EntityPitch(Ent, True), EntityYaw(Ent, True), EntityRoll(Ent, True))
Turn_Quat = EulerToQuat(X, Y, Z)
Quat = MultiplyQuats(Quat, Turn_Quat)
Quat = NormalizeQuat(Quat)
QuatToEuler2(Quat.x, Quat.y, Quat.z, Quat.w, Pitch, Yaw, Roll)
RotateEntity Ent, Pitch, Yaw, Roll
Else

RotateEntity Ent, EntityPitch(Ent) + X, EntityYaw(Ent) + Y, EntityRoll(Ent) + Z

EndIf

End Function

' ------------------------------------------------------------------------------------------------
' Euler to Quaternion
' ------------------------------------------------------------------------------------------------
Function EulerToQuat:TQuaternion(pitch:Float, yaw:Float, roll:Float)

Local cr:Float = Cos(-roll / 2.0)
Local cp:Float = Cos(pitch / 2.0)
Local cy:Float = Cos(yaw / 2.0)
Local sr:Float = Sin(-roll / 2.0)
Local sp:Float = Sin(pitch / 2.0)
Local sy:Float = Sin(yaw / 2.0)
Local cpcy:Float = cp * cy
Local spsy:Float = sp * sy
Local spcy:Float = sp * cy
Local cpsy:Float = cp * sy

Local q:TQuaternion = New TQuaternion

q.w:Float = cr * cpcy + sr * spsy
q.x:Float = sr * cpcy - cr * spsy
q.y:Float = cr * spcy + sr * cpsy
q.z:Float = cr * cpsy - sr * spcy

Return q

End Function

' ------------------------------------------------------------------------------------------------
' Quaternion to Euler
' ------------------------------------------------------------------------------------------------
Function QuatToEuler2(x:Float, y:Float, z:Float, w:Float, pitch:Float Var, yaw:Float Var, roll:Float Var)

Local QuatToEulerAccuracy:Double = 1.0 / 2 ^ 31

Local sint:Float = (2.0 * w * y) - (2.0 * x * z)
Local cost_temp:Float = 1.0 - (sint * sint)
Local cost:Float

If Abs(cost_temp) > QuatToEulerAccuracy

cost = Sqr(cost_temp)

Else

cost = 0.0

EndIf

Local sinv:Float, cosv:Float, sinf:Float, cosf:Float

If Abs(cost) > QuatToEulerAccuracy

sinv = ((2.0 * y * z) + (2.0 * w * x)) / cost
cosv = (1.0 - (2.0 * x * x) - (2.0 * y * y)) / cost
sinf = ((2.0 * x * y) + (2.0 * w * z)) / cost
cosf = (1.0 - (2.0 * y * y) - (2.0 * z * z)) / cost

Else

sinv = (2.0 * w * x) - (2.0 * y * z)
cosv = 1.0 - (2.0 * x * x) - (2.0 * z * z)
sinf = 0.0
cosf = 1.0

EndIf

pitch = ATan2(sint, cost)
yaw = ATan2(sinf, cosf)
roll = -ATan2(sinv, cosv)

End Function

' ------------------------------------------------------------------------------------------------
' Multiply Quaternion
' ------------------------------------------------------------------------------------------------
Function MultiplyQuats:TQuaternion(q1:TQuaternion, q2:TQuaternion)

Local q:TQuaternion = New TQuaternion

q.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
q.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y
q.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z
q.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x

Return q

End Function

' ------------------------------------------------------------------------------------------------
' Normalize Quaternion
' ------------------------------------------------------------------------------------------------
Function NormalizeQuat:TQuaternion(q:TQuaternion)

Local uv:Float = Sqr(q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z)

q.w = q.w / uv
q.x = q.x / uv
q.y = q.y / uv
q.z = q.z / uv

Return q

End Function

' ------------------------------------------------------------------------------------------------
' Events
' ------------------------------------------------------------------------------------------------
Function EventHook:Object(id:Int, Data:Object, Context:Object)

Local Event:TEvent = TEvent(data)
If Event = Null Then Return Event

Select Event.id

Case EVENT_MOUSEWHEEL

If Event.Data > 0 Then

MouseWheel:+1
If MouseWheel > 5 Then MouseWheel = 5

Else

MouseWheel:-1
If MouseWheel < 1 Then MouseWheel = 1

EndIf

End Select

End Function
Kind regards
Krischan

Windows 10 Pro | i7 9700K@ 3.6GHz | RTX 2080 8GB]
Metaverse | Blitzbasic Archive | My Github projects

markcwm

#1
Hi,

yes I changed Quaternions to a wrapper for Openb3d's because I noticed they didn't work exactly the same when I was porting the 3DS loader from Warner, the old quats were from Minib3d. So now you just use q:TQuaternion=CreateQuaternion() instead of New and q.w is now a pointer so q.w[0] etc. I edited your demo and it works fine now. I like what you're doing here, I might add some of these functions to the wrapper if that's okay. Where did you get the quat functions from?

Code (blitzmax) Select
    SuperStrict
     
    'Framework sidesign.minib3d
    Framework openb3d.b3dglgraphics
     
    Graphics3D DesktopWidth(), DesktopHeight(), 32, 2
     
    SeedRnd 1
     
    Const eccentricity:Float = 45           ' angle between 0 and 90
    Const planets:Int = 10                          ' number of planets
    Const speed:Float = 0.1                         ' simulation speed
    Const maxsegments:Int = 1440            ' maximum segments of outer planet orbit
     
    Global MouseWheel:Int = 1
     
    ' ------------------------------------------------------------------------------------------------
    ' Initialize Scene and Camera
    ' ------------------------------------------------------------------------------------------------
    Global pivot:TPivot = CreatePivot()
     
    Global cam:TCamera = CreateCamera(pivot)
    PositionEntity cam, 0, 5, -15
     
    Global star:TMesh = CreateSphere(32)
    ScaleEntity star, 0.5, 0.5, 0.5
    EntityColor star, 255, 192, 0
    EntityFX star, 1
     
    PointEntity cam, star
     
    ' ------------------------------------------------------------------------------------------------
    ' Stores Planet properties
    ' ------------------------------------------------------------------------------------------------
    Type TPlanet
     
            Field id:Int
            Field rotation:Float[]
            Field ent:TEntity
            Field piv:TPivot
            Field radius:Float
            Field rgb:Int[] = [255, 255, 255]
           
            Field orbit:TOrbit = New TOrbit
           
    End Type
     
    ' ------------------------------------------------------------------------------------------------
    ' Stores Orbit Coordinates
    ' ------------------------------------------------------------------------------------------------
    Type TOrbit
     
            Field steps:Float
            Field x:Float[maxsegments + 1]
            Field y:Float[maxsegments + 1]
            Field z:Float[maxsegments + 1]
     
    End Type
     
    ' ------------------------------------------------------------------------------------------------
    ' Initialize Planets
    ' ------------------------------------------------------------------------------------------------
    Global planetlist:TList = CreateList()
    Global p:TPlanet
     
    For Local i:Int = 1 To planets
           
            p = New TPlanet
            p.id = i
            p.radius = Normalize(i, 1, planets, 1, 10)
            p.rotation = [Float(Rnd(-eccentricity, eccentricity)), Float(Rnd(360)), 0.0]
            p.rgb = [Rand(255), Rand(255), Rand(255)]
           
            p.orbit.steps = maxsegments / (planets + 1 - p.id)
           
            ' planet pivot
            p.piv = CreatePivot()
           
            ' create planet
            p.ent = CreateSphere(8, p.piv)
            EntityFX p.ent, 1
            'EntityColor p.ent, p.rgb[0], p.rgb[1], p.rgb[2]
            ScaleEntity p.ent, 0.1, 0.1, 0.1
           
            ' pivot initial rotation
            RotateEntity p.piv, 0, 0, 0
            Turn p.piv, p.rotation[0], p.rotation[1], p.rotation[2]
           
            ' position planet
            PositionEntity p.ent, 0, 0, 0
            MoveEntity p.ent, 0, 0, p.radius
           
            ListAddLast planetlist, p
     
            ' store current position in Type for later use
            For Local i:Int = 0 To p.orbit.steps
           
                    p.orbit.x[i] = EntityX(p.ent, 1)
                    p.orbit.y[i] = EntityY(p.ent, 1)
                    p.orbit.z[i] = EntityZ(p.ent, 1)
                   
                    ' rotate pivot
                    Turn p.piv, 0, 360.0 / p.orbit.steps, 0
                                   
            Next
                   
    Next
     
    MoveMouse GraphicsWidth() / 2, GraphicsHeight() / 2
     
    ' Mousewheel logic
    AddHook EmitEventHook, EventHook
     
    ' ------------------------------------------------------------------------------------------------
    ' Main Loop
    ' ------------------------------------------------------------------------------------------------
    While Not AppTerminate()
     
            If KeyHit(KEY_ESCAPE) Then End
           
            Turn pivot, Normalize(MouseY(), 0, GraphicsHeight() - 1, -1, 1), Normalize(MouseX(), 0, GraphicsWidth() - 1, 1, -1), 0
                                   
            CameraZoom cam, MouseWheel
           
            RenderWorld
           
            ' enable OpenGL Lines
            ' it is IMPORTANT to use this between Renderworld and BeginMax2D or it won't work
            glEnable(GL_COLOR_MATERIAL)
            glBegin(GL_LINES)
           
            Local x1:Float, y1:Float, z1:Float
            Local x2:Float, y2:Float, z2:Float
           
            ' cycle all planets
            For Local p:TPlanet = EachIn planetlist
                           
                    ' a full rotation
                    For Local i:Int = 0 To p.orbit.steps
                   
                            ' look ahead for next XYZ position
                            Local j:Int = (i + 1) Mod p.orbit.steps
                           
                            ' get current coordinates
                            x1 = p.orbit.x[i]
                            y1 = p.orbit.y[i]
                            z1 = p.orbit.z[i]
                                                   
                            ' get next coordinates
                            x2 = p.orbit.x[j]
                            y2 = p.orbit.y[j]
                            z2 = p.orbit.z[j]
                                           
                            ' draw 3D line
                            Line3D(x1, y1, z1, x2, y2, z2, p.rgb[0], p.rgb[1], p.rgb[2], 1)
     
                    Next
     
                    ' turn pivot = move planet around star
                    Turn p.piv, 0, (planets + 1 - p.radius) * speed, 0
                                   
            Next
           
            glEnd
            glDisable(GL_COLOR_MATERIAL)
           
            BeginMax2D()
           
                    DrawText "Maximum Eccentricity: " + eccentricity, 0, 0
           
            EndMax2D()
           
            Flip True
     
    Wend
     
    End
     
    ' ------------------------------------------------------------------------------------------------
    ' Normalizes a value to given range
    ' ------------------------------------------------------------------------------------------------
    Function Normalize:Float(Value:Float, vmin:Float, vmax:Float, nmin:Float, nmax:Float, limit:Int = False)
     
            ' normalize   
            Local result:Float = ((Value - vmin) / (vmax - vmin)) * (nmax - nmin) + nmin
     
            ' limit
            If limit Then
           
                    If Value >= nmax Then result = nmax
                    If Value <= nmin Then result = nmin
     
            EndIf
     
            Return result
           
    End Function
     
    ' ------------------------------------------------------------------------------------------------
    ' Draw a 3D line using OpenGL functions:
    ' glEnable(GL_COLOR_MATERIAL) / glBegin(GL_LINES) [...] glEnd() / glDisable(GL_COLOR_MATERIAL)
    ' ------------------------------------------------------------------------------------------------
    Function Line3D:TMesh(x0:Float, y0:Float, z0:Float, x1:Float, y1:Float, z1:Float, r:Int = 255, g:Int = 255, b:Int = 255, a:Float = 1.0)
                   
            SetColor r, g, b
            SetAlpha(a)
                   
            glVertex3f(x0, y0, -z0)
            glVertex3f(x1, y1, -z1)
           
            SetAlpha(1.0)
                           
    End Function
     
    ' ------------------------------------------------------------------------------------------------
    ' Turn Entity using Quaternions
    ' ------------------------------------------------------------------------------------------------
    Function Turn(Ent:TEntity, X:Float, Y:Float, Z:Float, Glob:Int = False)
     
            Local Pitch:Float = 0.0
            Local Yaw:Float = 0.0
            Local Roll:Float = 0.0
           
            Local Quat:TQuaternion = EulerToQuat(0.0, 0.0, 0.0)
            Local Turn_Quat:TQuaternion = EulerToQuat(0.0, 0.0, 0.0)
           
            If Glob = False
           
                    Quat = EulerToQuat(EntityPitch(Ent, True), EntityYaw(Ent, True), EntityRoll(Ent, True))
                    Turn_Quat = EulerToQuat(X, Y, Z)
                    Quat = MultiplyQuats(Quat, Turn_Quat)
                    Quat = NormalizeQuat(Quat)
                    QuatToEuler2(Quat.x[0], Quat.y[0], Quat.z[0], Quat.w[0], Pitch, Yaw, Roll)
                    RotateEntity Ent, Pitch, Yaw, Roll
            Else
           
                    RotateEntity Ent, EntityPitch(Ent) + X, EntityYaw(Ent) + Y, EntityRoll(Ent) + Z
                   
            EndIf
           
    End Function
     
    ' ------------------------------------------------------------------------------------------------
    ' Euler to Quaternion
    ' ------------------------------------------------------------------------------------------------
    Function EulerToQuat:TQuaternion(pitch:Float, yaw:Float, roll:Float)
     
            Local cr:Float = Cos(-roll / 2.0)
            Local cp:Float = Cos(pitch / 2.0)
            Local cy:Float = Cos(yaw / 2.0)
            Local sr:Float = Sin(-roll / 2.0)
            Local sp:Float = Sin(pitch / 2.0)
            Local sy:Float = Sin(yaw / 2.0)
            Local cpcy:Float = cp * cy
            Local spsy:Float = sp * sy
            Local spcy:Float = sp * cy
            Local cpsy:Float = cp * sy
           
            Local q:TQuaternion = CreateQuaternion()
           
            q.w[0] = cr * cpcy + sr * spsy
            q.x[0] = sr * cpcy - cr * spsy
            q.y[0] = cr * spcy + sr * cpsy
            q.z[0] = cr * cpsy - sr * spcy
           
            Return q
           
    End Function
     
    ' ------------------------------------------------------------------------------------------------
    ' Quaternion to Euler
    ' ------------------------------------------------------------------------------------------------
    Function QuatToEuler2(x:Float, y:Float, z:Float, w:Float, pitch:Float Var, yaw:Float Var, roll:Float Var)
     
            Local QuatToEulerAccuracy:Double = 1.0 / 2 ^ 31
     
            Local sint:Float = (2.0 * w * y) - (2.0 * x * z)
            Local cost_temp:Float = 1.0 - (sint * sint)
            Local cost:Float
     
            If Abs(cost_temp) > QuatToEulerAccuracy
     
                    cost = Sqr(cost_temp)
     
            Else
     
                    cost = 0.0
     
            EndIf
     
            Local sinv:Float, cosv:Float, sinf:Float, cosf:Float
           
            If Abs(cost) > QuatToEulerAccuracy
           
                    sinv = ((2.0 * y * z) + (2.0 * w * x)) / cost
                    cosv = (1.0 - (2.0 * x * x) - (2.0 * y * y)) / cost
                    sinf = ((2.0 * x * y) + (2.0 * w * z)) / cost
                    cosf = (1.0 - (2.0 * y * y) - (2.0 * z * z)) / cost
                   
            Else
                   
                    sinv = (2.0 * w * x) - (2.0 * y * z)
                    cosv = 1.0 - (2.0 * x * x) - (2.0 * z * z)
                    sinf = 0.0
                    cosf = 1.0
                   
            EndIf
           
            pitch = ATan2(sint, cost)
            yaw = ATan2(sinf, cosf)
            roll = -ATan2(sinv, cosv)
           
    End Function
     
    ' ------------------------------------------------------------------------------------------------
    ' Multiply Quaternion
    ' ------------------------------------------------------------------------------------------------
    Function MultiplyQuats:TQuaternion(q1:TQuaternion, q2:TQuaternion)
     
            Local q:TQuaternion = CreateQuaternion()
           
            q.w[0] = q1.w[0] * q2.w[0] - q1.x[0] * q2.x[0] - q1.y[0] * q2.y[0] - q1.z[0] * q2.z[0]
            q.x[0] = q1.w[0] * q2.x[0] + q1.x[0] * q2.w[0] + q1.y[0] * q2.z[0] - q1.z[0] * q2.y[0]
            q.y[0] = q1.w[0] * q2.y[0] + q1.y[0] * q2.w[0] + q1.z[0] * q2.x[0] - q1.x[0] * q2.z[0]
            q.z[0] = q1.w[0] * q2.z[0] + q1.z[0] * q2.w[0] + q1.x[0] * q2.y[0] - q1.y[0] * q2.x[0]
     
            Return q
     
    End Function
     
    ' ------------------------------------------------------------------------------------------------
    ' Normalize Quaternion
    ' ------------------------------------------------------------------------------------------------
    Function NormalizeQuat:TQuaternion(q:TQuaternion)
     
            Local uv:Float = Sqr(q.w[0] * q.w[0] + q.x[0] * q.x[0] + q.y[0] * q.y[0] + q.z[0] * q.z[0])
     
            q.w[0] = q.w[0] / uv
            q.x[0] = q.x[0] / uv
            q.y[0] = q.y[0] / uv
            q.z[0] = q.z[0] / uv
     
            Return q
     
    End Function
     
    ' ------------------------------------------------------------------------------------------------
    ' Events
    ' ------------------------------------------------------------------------------------------------
    Function EventHook:Object(id:Int, Data:Object, Context:Object)
     
            Local Event:TEvent = TEvent(data)
            If Event = Null Then Return Event
     
            Select Event.id
                                                           
                    Case EVENT_MOUSEWHEEL
                                                   
                            If Event.Data > 0 Then
     
                                    MouseWheel:+1
                                    If MouseWheel > 5 Then MouseWheel = 5
     
                                    Else
     
                                            MouseWheel:-1
                                    If MouseWheel < 1 Then MouseWheel = 1
     
                            EndIf
     
            End Select
           
    End Function