Results 1 to 4 of 4

Thread: [VB6] - 3D rotation as a basis change

Threaded View

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Sep 2010
    Location
    Italy
    Posts
    732

    [VB6] - 3D rotation as a basis change

    I propose a way to change the 3D coordinate basis.

    The bases are expressed as a triad of perpendicular vectors.

    Since in my case these vectors are all of length 1 and are perpendicular, this topic it's not about a "true" basis change but only the one useful for a 3D rotation.


    The World basis (which is implicit) is composed of 3 vectors X,Y,Z of which only one direction has value 1 and all the others 0
    WorldBase X = 1, 0, 0
    WorldBase Y = 0, 1, 0
    WorldBase Z = 0, 0, 1


    This class allows you to provide a transformation basis formed by 3 perpendicular vectors.
    It can be provided by directly entering these three vectors (Already normalized)

    SetBasisNormalized

    or by providing only two Vectors (preferably but not necessarily perpendicular)

    SetBasisXY and SetBasisXZ

    Once the transformation basis is provided, it is possible to transform a vector from one basis to another with the subs:

    ToBasis and ToWorld.

    ToBasis (WorldPosition , [out] BasePosition)
    ToWorld (BasePosition , [out] WorldPosition)



    For example, if you had to perform a rotation along the Z axis, it will be sufficient to give as baseX a vector that has X=cos(ang), Y=sin(ang) and as baseZ a vector that only has the value Z = 1
    Code:
    BX.X = Cos( A )
    BX.Y = Sin( A )
    BX.Z = 0
    
    BZ.X = 0
    BZ.Y = 0
    BZ.Z = 1
    
    SetBasisXZ  BX,BZ


    NOTE:
    To specify the center of rotation, proceed as follows:
    - The Center of Rotation must first be subtracted from the vector to be transformed.
    - The transformation is applied.
    - The Center of Rotation is added to the result of the transformation.


    Another example:
    You want your object to point to the Camera vector.
    (Shortening)

    VectorUP = Vector ( 0 , 1, 0 )
    BASIS.SetBasisXY CameraPos - CameraLookAt , VectorUP
    BASIS.ToBasis VectorIn - ObjCenter, TransformedV
    TransformedV = TransformedV + ObjCenter




    I hope it can be useful to someone.

    (PS: in case you have a better idea you can express it here but to develop the discussion on it, in case, you are required to start a new thread, Thanks)



    MODULE:
    Code:
    Option Explicit
    
    Public Type tVec3
        X             As Double
        Y             As Double
        Z             As Double
    EndType
    
    Public Function DOT3(A As tVec3, B As tVec3) As Double
        DOT3 = A.X * B.X + A.Y * B.Y + A.Z * B.Z
    End Function
    
    Public Function CROSS3(A As tVec3, B As tVec3) As tVec3
        With CROSS3
            .X = A.Y * B.Z - A.Z * B.Y
            .Y = A.Z * B.X - A.X * B.Z
            .Z = A.X * B.Y - A.Y * B.X
        End With
    End Function
    
    Public Function LEN32(V As tVec3) As Double
        With V
            LEN32 = (.X * .X + .Y * .Y + .Z * .Z)
        End With
    End Function
    
    Public Function Normalize3(V As tVec3) As tVec3
        Dim D         As Double
        With V
            D = (.X * .X + .Y * .Y + .Z * .Z)
            If D Then
                D = 1# / Sqr(D)
                Normalize3.X = .X * D
                Normalize3.Y = .Y * D
                Normalize3.Z = .Z * D
            End If
        End With
    End Function
    CLASS:
    Code:
    Option Explicit
    
    Private BaseX     As tVec3
    Private BaseY     As tVec3
    Private BaseZ     As tVec3
    
    Friend Sub GetBasis(bX As tVec3, bY As tVec3, bZ As tVec3)
        bX = BaseX
        bY = BaseY
        bZ = BaseZ
    End Sub
    
    Friend Function GetBaseZ() As tVec3
        GetBaseZ = BaseZ
    End Function
    
    Friend Sub SetBasisNormalized(baX As tVec3, baY As tVec3, baZ As tVec3)
        BaseX = baX
        BaseY = baY
        BaseZ = baZ
    End Sub
    
    Friend Sub SetBasisXY(bX As tVec3, bY As tVec3)
        BaseX = bX
        BaseY = bY
        If LEN32(BaseX) <> 1# Then BaseX = Normalize3(BaseX)
        If LEN32(BaseY) <> 1# Then BaseY = Normalize3(BaseY)
        BaseZ = Normalize3(CROSS3(BaseX, BaseY))
        If DOT3(BaseX, BaseY) <> 0 Then
            BaseY = Normalize3(CROSS3(BaseZ, BaseX))
        End If
    End Sub
    
    Friend Sub SetBasisXZ(bX As tVec3, bZ As tVec3)
        BaseX = bX
        BaseZ = bZ
        If LEN32(BaseX) <> 1# Then BaseX = Normalize3(BaseX)
        If LEN32(BaseZ) <> 1# Then BaseZ = Normalize3(BaseZ)
        BaseY = Normalize3(CROSS3(BaseZ, BaseX))
        If DOT3(BaseX, BaseZ) <> 0 Then
            BaseZ = Normalize3(CROSS3(BaseX, BaseY))
        End If
    End Sub
    
    Friend Sub ToBasis(V As tVec3, vOUT As tVec3)
        'Equivalent to MAT3x3 MUL
        '    vOUT.X = DOT3(V, BaseX)
        '    vOUT.Y = DOT3(V, BaseY)
        '    vOUT.Z = DOT3(V, BaseZ)
        ' SAME:
        vOUT.X = V.X * BaseX.X + V.Y * BaseX.Y + V.Z * BaseX.Z
        vOUT.Y = V.X * BaseY.X + V.Y * BaseY.Y + V.Z * BaseY.Z
        vOUT.Z = V.X * BaseZ.X + V.Y * BaseZ.Y + V.Z * BaseZ.Z
    End Sub
    
    Friend Sub ToWorld(V As tVec3, vOUT As tVec3)
        '    Vout = MUL3(BaseX, V.X)
        '    Vout = SUM3(Vout, MUL3(BaseY, V.Y))
        '    Vout = SUM3(Vout, MUL3(BaseZ, V.Z))
        'SAME:
        vOUT.X = BaseX.X * V.X + BaseY.X * V.Y + BaseZ.X * V.Z
        vOUT.Y = BaseX.Y * V.X + BaseY.Y * V.Y + BaseZ.Y * V.Z
        vOUT.Z = BaseX.Z * V.X + BaseY.Z * V.Y + BaseZ.Z * V.Z
    End Sub


    EDIT -----------------------------------------------------------------------


    Description by Gemini:


    Coordinate System Transformation: This code defines a transformation between two 3D coordinate systems: a "world" coordinate system and a local coordinate system defined by the basis vectors BaseX, BaseY, and BaseZ.
    Basis Vectors: The basis vectors are three mutually perpendicular vectors that define the orientation of the local coordinate system. They are typically normalized (unit length).
    Normalization: The code includes checks and normalization steps to ensure that the basis vectors are unit length, which is essential for proper coordinate system transformation.
    Orthogonality: The code also ensures that the basis vectors are orthogonal (perpendicular to each other) by using the cross product and dot product.
    Cross Product: The CROSS3 function calculates the cross product of two vectors, which results in a vector perpendicular to both input vectors. This is used to calculate the Z-axis vector from the X and Y axes, and vice-versa.
    Dot Product: The DOT3 function calculates the dot product of two vectors, which is used to check if the vectors are orthogonal. If the dot product is zero, the vectors are perpendicular.
    Transformation Subroutines:

    ToBasis: Transforms a vector from the world coordinate system to the local coordinate system.
    ToWorld: Transforms a vector from the local coordinate system to the world coordinate system.

    Efficiency: The ToBasis and ToWorld subroutines are implemented efficiently by directly accessing the vector components instead of using separate dot product and multiplication functions.

    Code COMMENTS by Gemini:

    Code:
    ' Module: Module1 (or the actual name of your module)
    Option Explicit ' Forces explicit declaration of all variables.
    
    ' Define a user-defined type (structure) to represent a 3D vector.
    Public Type tVec3
        X             As Double ' The X component of the vector.
        Y             As Double ' The Y component of the vector.
        Z             As Double ' The Z component of the vector.
    End Type
    
    ' Public function to calculate the dot product of two 3D vectors.
    ' The dot product is a scalar value.
    Public Function DOT3(A As tVec3, B As tVec3) As Double
        ' Calculate the dot product using the formula:
        ' A.X * B.X + A.Y * B.Y + A.Z * B.Z
        DOT3 = A.X * B.X + A.Y * B.Y + A.Z * B.Z
    End Function
    
    ' Public function to calculate the cross product of two 3D vectors.
    ' The cross product is a vector that is perpendicular to both input vectors.
    Public Function CROSS3(A As tVec3, B As tVec3) As tVec3
        ' Use a With block to simplify referencing the members of the
        ' CROSS3 function's return value (which is a tVec3).
        With CROSS3
            ' Calculate the X, Y, and Z components of the cross product using the formula:
            ' X = A.Y * B.Z - A.Z * B.Y
            ' Y = A.Z * B.X - A.X * B.Z
            ' Z = A.X * B.Y - A.Y * B.X
            .X = A.Y * B.Z - A.Z * B.Y
            .Y = A.Z * B.X - A.X * B.Z
            .Z = A.X * B.Y - A.Y * B.X
        End With
    End Function
    
    ' Public function to calculate the square of the magnitude (length) of a 3D vector.
    ' This is often more efficient than calculating the actual magnitude (square root is slow).
    Public Function LEN32(V As tVec3) As Double
        With V
            ' Calculate the square of the length using the formula:
            ' X^2 + Y^2 + Z^2
            LEN32 = (.X * .X + .Y * .Y + .Z * .Z)
        End With
    End Function
    
    ' Public function to normalize a 3D vector (convert it to a unit vector).
    ' A unit vector has a magnitude of 1.
    Public Function Normalize3(V As tVec3) As tVec3
        Dim D As Double ' Variable to store the square of the magnitude.
        With V
            ' Calculate the square of the magnitude.
            D = (.X * .X + .Y * .Y + .Z * .Z)
            
            ' Check if the magnitude is not zero.  If it is zero, the vector cannot be normalized.
            If D Then
                ' Calculate the inverse of the square root of the magnitude.
                ' This is used to scale the vector components.
                D = 1# / Sqr(D) ' 1# is used to ensure Double precision.
                
                ' Calculate the normalized vector components.
                Normalize3.X = .X * D
                Normalize3.Y = .Y * D
                Normalize3.Z = .Z * D
            End If
        End With
    End Function
    
    
    
    ' Class: cBasis (or the actual name of your class)
    Option Explicit ' Forces explicit declaration of all variables.
    
    ' Define private member variables to hold the basis vectors.
    ' These are of type tVec3, which is the user-defined type from the module.
    ' These variables are private to the class.
    Private BaseX As tVec3 ' Represents the X-axis of the coordinate system.
    Private BaseY As tVec3 ' Represents the Y-axis of the coordinate system.
    Private BaseZ As tVec3 ' Represents the Z-axis of the coordinate system.
    
    ' Friend subroutine to retrieve the current basis vectors.
    ' The vectors are passed back to the caller through the parameters.
    Friend Sub GetBasis(bX As tVec3, bY As tVec3, bZ As tVec3)
        ' Copy the class's basis vectors to the provided variables.
        ' The caller must have declared variables to receive these values.
        bX = BaseX ' Copy the X-axis vector.
        bY = BaseY ' Copy the Y-axis vector.
        bZ = BaseZ ' Copy the Z-axis vector.
    End Sub
    
    ' Friend function to retrieve the Z-axis basis vector.
    ' Returns a tVec3 representing the Z-axis.
    Friend Function GetBaseZ() As tVec3
        ' Return the Z-axis vector.
        GetBaseZ = BaseZ
    End Function
    
    ' Friend subroutine to set the basis vectors.
    ' This subroutine assumes the input vectors are already normalized.
    Friend Sub SetBasisNormalized(baX As tVec3, baY As tVec3, baZ As tVec3)
        ' Set the class's basis vectors to the provided values.
        BaseX = baX ' Set the X-axis vector.
        BaseY = baY ' Set the Y-axis vector.
        BaseZ = baZ ' Set the Z-axis vector.
    End Sub
    
    ' Friend subroutine to set the basis vectors from X and Y axes.
    ' This subroutine calculates the Z-axis vector as the cross product
    ' of the X and Y axes, ensuring the basis is orthonormal.
    Friend Sub SetBasisXY(bX As tVec3, bY As tVec3)
        ' Set the X and Y basis vectors.
        BaseX = bX
        BaseY = bY
        
        ' Normalize the X and Y vectors if they are not already.
        ' Calls the Normalize3 function from the module.
        ' LEN32 likely calculates the magnitude squared.
        If LEN32(BaseX) <> 1# Then BaseX = Normalize3(BaseX)
        If LEN32(BaseY) <> 1# Then BaseY = Normalize3(BaseY)
        
        ' Calculate the Z-axis vector as the cross product of X and Y.
        ' Calls the CROSS3 function from the module.
        BaseZ = Normalize3(CROSS3(BaseX, BaseY))
        
        ' Check if X and Y are orthogonal (dot product is zero).
        ' If they are not, recalculate Y to ensure orthogonality.
        ' Calls the DOT3 function from the module.
        If DOT3(BaseX, BaseY) <> 0 Then
            BaseY = Normalize3(CROSS3(BaseZ, BaseX)) ' Recalculate Y using Z and X.
        End If
    End Sub
    
    ' Friend subroutine to set the basis vectors from X and Z axes.
    ' This subroutine calculates the Y-axis vector as the cross product
    ' of the Z and X axes, ensuring the basis is orthonormal.
    Friend Sub SetBasisXZ(bX As tVec3, bZ As tVec3)
        ' Set the X and Z basis vectors.
        BaseX = bX
        BaseZ = bZ
        
        ' Normalize the X and Z vectors if they are not already.
        If LEN32(BaseX) <> 1# Then BaseX = Normalize3(BaseX)
        If LEN32(BaseZ) <> 1# Then BaseZ = Normalize3(BaseZ)
        
        ' Calculate the Y-axis vector as the cross product of Z and X.
        BaseY = Normalize3(CROSS3(BaseZ, BaseX))
        
        ' Check if X and Z are orthogonal.  If they are not, recalculate Z.
        If DOT3(BaseX, BaseZ) <> 0 Then
            BaseZ = Normalize3(CROSS3(BaseX, BaseY)) ' Recalculate Z using X and Y.
        End If
    End Sub
    
    ' Friend subroutine to transform a vector from the world coordinate system
    ' to the local coordinate system defined by the basis vectors.
    ' This is equivalent to a matrix multiplication (V_local = M^-1 * V_world).
    Friend Sub ToBasis(V As tVec3, vOUT As tVec3)
        ' The commented-out code shows the matrix multiplication in a more
        ' mathematical notation, using dot products.  The following code
        ' performs the same calculation more efficiently by directly
        ' accessing the vector components.
        
        'vOUT.X = DOT3(V, BaseX)
        'vOUT.Y = DOT3(V, BaseY)
        'vOUT.Z = DOT3(V, BaseZ)
        
        ' Calculate the components of the transformed vector.
        ' This is done by taking the dot product of the input vector V
        ' with each of the basis vectors.
        vOUT.X = V.X * BaseX.X + V.Y * BaseX.Y + V.Z * BaseX.Z
        vOUT.Y = V.X * BaseY.X + V.Y * BaseY.Y + V.Z * BaseY.Z
        vOUT.Z = V.X * BaseZ.X + V.Y * BaseZ.Y + V.Z * BaseZ.Z
    End Sub
    
    ' Friend subroutine to transform a vector from the local coordinate system
    ' defined by the basis vectors to the world coordinate system.
    ' This is equivalent to a matrix multiplication (V_world = M * V_local).
    Friend Sub ToWorld(V As tVec3, vOUT As tVec3)
        ' The commented-out code shows the matrix multiplication using vector
        ' addition and scalar multiplication. The following code performs the
        ' same calculation more efficiently.
        
        'Vout = MUL3(BaseX, V.X)
        'Vout = SUM3(Vout, MUL3(BaseY, V.Y))
        'Vout = SUM3(Vout, MUL3(BaseZ, V.Z))
        
        ' Calculate the components of the transformed vector.
        ' This is done by multiplying each component of the input vector V
        ' by the corresponding basis vector and summing the results.
        vOUT.X = BaseX.X * V.X + BaseY.X * V.Y + BaseZ.X * V.Z
        vOUT.Y = BaseX.Y * V.X + BaseY.Y * V.Y + BaseZ.Y * V.Z
        vOUT.Z = BaseX.Z * V.X + BaseY.Z * V.Y + BaseZ.Z * V.Z
    End Sub

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width