Attribute VB_Name = "Item"
Option Explicit

'This module handle static instances that are mostly too far away from the camera to be seen.
'It is using a 2D grid that refer to items.
'It refuses to create more items where the bucket is full to keep it simple.

Private Type ItemHolder
    Instance As Long
    RigidBody As Long
    Damage As Long
    Model(Damage_None To Damage_Full) As Long
    CollisionShape(Damage_None To Damage_Full) As Long
    Position As Vector3
    Longitude As Single
    Lattitude As Single
    Size As Single
    ReservedRadius As Single
    Health As Single
    ObstacleX As Long 'Pixel coordinate X on the obstacle map
    ObstacleZ As Long 'Pixel coordinate Y on the obstacle map
    SourceUV As Vector4
End Type
Const MaxNumberOfItemsPerBucket As Integer = 32
Const GridSizeX As Long = 32
Const GridSizeZ As Long = GridSizeX

Dim Item_Count(0 To GridSizeX - 1, 0 To GridSizeZ - 1) As Integer
Dim Item_Grid(0 To GridSizeX - 1, 0 To GridSizeZ - 1, 0 To MaxNumberOfItemsPerBucket - 1) As ItemHolder
Dim VisibleRect As BucketRect
Dim MaxReservedRadius As Single

Public Type BucketRect
    MinX As Long
    MaxX As Long
    MinZ As Long
    MaxZ As Long
End Type

Public Sub Item_Init()
    VisibleRect.MinX = 0
    VisibleRect.MaxX = -1
    VisibleRect.MinZ = 0
    VisibleRect.MaxZ = -1
    MaxReservedRadius = 0
End Sub

Public Sub Item_Reset()
    Item_Init
    Dim X As Long
    Dim Z As Long
    Dim I As Long
    For X = 0 To GridSizeX - 1
        For Z = 0 To GridSizeZ - 1
            For I = Item_Count(X, Z) - 1 To 0 Step -1
                Item_Delete X, Z, I
            Next I
            Item_Count(X, Z) = 0
        Next Z
    Next X
End Sub

Public Function MakeSafeBucketRect(MinX As Long, MaxX As Long, MinZ As Long, MaxZ As Long) As BucketRect
    'Insert the values bounded by the level size
    MakeSafeBucketRect.MinX = Max_Long(0, MinX)
    MakeSafeBucketRect.MaxX = Min_Long(GridSizeX - 1, MaxX)
    MakeSafeBucketRect.MinZ = Max_Long(0, MinZ)
    MakeSafeBucketRect.MaxZ = Min_Long(GridSizeZ - 1, MaxZ)
End Function

Public Function BucketRect_Overlap(A As BucketRect, B As BucketRect) As Boolean
    If A.MaxX < B.MinX - 1 Then
        BucketRect_Overlap = False
    ElseIf B.MaxX < A.MinX - 1 Then
        BucketRect_Overlap = False
    ElseIf A.MaxZ < B.MinZ - 1 Then
        BucketRect_Overlap = False
    ElseIf B.MaxZ < A.MinZ - 1 Then
        BucketRect_Overlap = False
    Else
        BucketRect_Overlap = True
    End If
End Function

Public Function BucketRect_Inside(A As BucketRect, ByVal X As Long, ByVal Z As Long) As Boolean
    BucketRect_Inside = (A.MinX <= X And X <= A.MaxX And A.MinZ <= Z And Z <= A.MaxZ)
End Function

Public Function BucketRect_IsEqual(A As BucketRect, B As BucketRect) As Boolean
    BucketRect_IsEqual = (A.MinX = B.MinX And A.MaxX = B.MaxX And A.MinZ = B.MinZ And A.MaxZ = B.MaxZ)
End Function

Public Function Item_IsRadiusAvailable(Position As Vector3, ReservedRadius As Single) As Boolean
    'Calculate how far away a colliding reserved radius may be
    Dim MaxDistance As Single
    MaxDistance = MaxReservedRadius + ReservedRadius
    
    'Calculate what rectangular area we have to search in
    Dim SearchArea As BucketRect
    SearchArea = MakeSafeBucketRect(Int(Terrain_WorldXToMapU(Position.X - MaxDistance) * GridSizeX), Int(Terrain_WorldXToMapU(Position.X + MaxDistance) * GridSizeX), Int(Terrain_WorldZToMapV(Position.Z - MaxDistance) * GridSizeZ), Int(Terrain_WorldZToMapV(Position.Z + MaxDistance) * GridSizeZ))
    
    'Scan the rectangle for overlapping items
    Dim X As Long
    Dim Z As Long
    Dim Index As Long
    Item_IsRadiusAvailable = True
    For X = SearchArea.MinX To SearchArea.MaxX
        For Z = SearchArea.MinZ To SearchArea.MaxZ
            For Index = 0 To Item_Count(X, Z) - 1
                Dim Distance As Single
                Distance = AbsVector2(ToVector2(Vector3ToVector2(Item_Grid(X, Z, Index).Position), Vector3ToVector2(Position)))
                If Distance < (Item_Grid(X, Z, Index).ReservedRadius + ReservedRadius) Then
                    Item_IsRadiusAvailable = False
                    Exit Function
                End If
            Next Index
        Next Z
    Next X
End Function

Public Sub CreateRigidBody(ByVal X As Long, ByVal Z As Long, ByVal Index As Long, CollisionShape As Long)
    If Item_Grid(X, Z, Index).RigidBody > 0 Then
        frmMain.DGE.RigidBody_Delete Item_Grid(X, Z, Index).RigidBody: RE
        Item_Grid(X, Z, Index).RigidBody = 0
    End If
    If CollisionShape > 0 Then
        Dim M As Matrix3
        M = MakeAxisSystem_Polar(Item_Grid(X, Z, Index).Longitude, Item_Grid(X, Z, Index).Lattitude)
        Dim NewRigidBody As Long
        Dim Position As Vector3
        Position = Item_Grid(X, Z, Index).Position
        NewRigidBody = frmMain.DGE.RigidBody_Create_Static(CollisionShape, Position.X, Position.Y, Position.Z, M.XAxis.X, M.XAxis.Y, M.XAxis.Z, M.YAxis.X, M.YAxis.Y, M.YAxis.Z): RE
        frmMain.DGE.RigidBody_SetRestitution NewRigidBody, DefaultRestitution: RE
        frmMain.DGE.RigidBody_SetUserData NewRigidBody, RBUDI_Collection, RBC_Item: RE
        frmMain.DGE.RigidBody_SetUserData NewRigidBody, RBUDI_Index, Index: RE
        frmMain.DGE.RigidBody_SetUserData NewRigidBody, RBUDI_Coordinate_X, X: RE
        frmMain.DGE.RigidBody_SetUserData NewRigidBody, RBUDI_Coordinate_Z, Z: RE
        Item_Grid(X, Z, Index).RigidBody = NewRigidBody
        
        '¤¤¤¤ Testing contact buffer
        'Dim I As Long
        'Dim NumberOfContacts As Long
        'NumberOfContacts = frmMain.DGE.RigidBody_GetCollisionPointsFromBody_OutCB(Item_Grid(X, Z, Index).RigidBody): RE
        'Debug.Assert NumberOfContacts = frmMain.DGE.GetLengthOfContactBuffer
        'Debug.Print "New item: ID = " & Item_Grid(X, Z, Index).RigidBody
        'For I = 0 To NumberOfContacts - 1
        '    Dim BodyA As Long
        '    Dim BodyB As Long
        '    Dim Pos As Vector3
        '    Dim Normal As Vector3
        '    BodyA = frmMain.DGE.ReadFromContactBuffer_BodyA(I): RE
        '    Debug.Assert (BodyA = Item_Grid(X, Z, Index).RigidBody)
        '    BodyB = frmMain.DGE.ReadFromContactBuffer_BodyB(I): RE
        '    frmMain.DGE.ReadFromContactBuffer_Position I: Pos = GetVector3FromMatrixBuffer: RE
        '    frmMain.DGE.ReadFromContactBuffer_Normal I: Normal = GetVector3FromMatrixBuffer: RE
        '    If BodyB = Terrain_HeightField_RigidBody Then
        '        Debug.Print "    Contact with ground at " & Pos.X & " , " & Pos.Y & " , " & Pos.Z & " facing " & Normal.X & " , " & Normal.Y & " , " & Normal.Z
        '    Else
        '        Debug.Print "    Contact with " & BodyB & " at " & Pos.X & " , " & Pos.Y & " , " & Pos.Z & " facing " & Normal.X & " , " & Normal.Y & " , " & Normal.Z
        '    End If
        'Next I
    End If
End Sub

Public Sub Item_Add(Model As Long, BrokenModel As Long, CollisionShape As Long, BrokenCollisionShape As Long, InitialDamage As Integer, Position As Vector3, Longitude As Single, Lattitude As Single, Size As Single, ReservedRadius As Single, InitialHealth As Single, SourceUV As Vector4)
    Dim X As Long
    Dim Z As Long
    
    'Find the X,Z coordinate for the new item's bucket
    X = Int(Terrain_WorldXToMapU(Position.X) * GridSizeX)
    Z = Int(Terrain_WorldZToMapV(Position.Z) * GridSizeZ)
    
    If X >= 0 And X < GridSizeX And Z >= 0 And Z < GridSizeZ Then
        If Item_Count(X, Z) < MaxNumberOfItemsPerBucket Then
            If Item_IsRadiusAvailable(Position, ReservedRadius) Then 'Don't create items too close to each other
                Dim ObstacleX As Long
                Dim ObstacleZ As Long
                Dim FreePlace As Boolean
                ObstacleX = Int(Terrain_WorldXToMapU(Position.X) * Terrain_Map_Width)
                ObstacleZ = Int(Terrain_WorldZToMapV(Position.Z) * Terrain_Map_Height)
                If CollisionShape = 0 Then
                    FreePlace = True
                Else
                    FreePlace = (Terrain_GetObstacle(ObstacleX, ObstacleZ) < -0.5)
                End If
                
                If FreePlace Then 'Don't create items on the same pixel in the obstacle map
                    Dim Index As Long
                    Index = Item_Count(X, Z)
                    
                    'Reset references to zero before use
                    Item_Grid(X, Z, Index).Instance = 0
                    Item_Grid(X, Z, Index).RigidBody = 0
                    
                    'Remember models and shapes
                    Item_Grid(X, Z, Index).Damage = InitialDamage
                    Item_Grid(X, Z, Index).Model(Damage_None) = Model
                    Item_Grid(X, Z, Index).Model(Damage_Full) = BrokenModel
                    Item_Grid(X, Z, Index).CollisionShape(Damage_None) = CollisionShape
                    Item_Grid(X, Z, Index).CollisionShape(Damage_Full) = BrokenCollisionShape
                    
                    'Store information for later use
                    Item_Grid(X, Z, Index).Position = Position
                    Item_Grid(X, Z, Index).Longitude = Longitude
                    Item_Grid(X, Z, Index).Lattitude = Lattitude
                    Item_Grid(X, Z, Index).Size = Size
                    Item_Grid(X, Z, Index).ReservedRadius = ReservedRadius
                    Item_Grid(X, Z, Index).Health = InitialHealth
                    Item_Grid(X, Z, Index).ObstacleX = ObstacleX
                    Item_Grid(X, Z, Index).ObstacleZ = ObstacleZ
                    Item_Grid(X, Z, Index).SourceUV = SourceUV
                    
                    'Create a rigid body if we get a collision shape
                    CreateRigidBody X, Z, Index, Item_Grid(X, Z, Index).CollisionShape(InitialDamage)
                    
                    'Draw the item as an obstacle if needed
                    If Item_Grid(X, Z, Index).RigidBody > 0 Then
                        Dim BoundMin As Vector3
                        Dim BoundMax As Vector3
                        Dim FlatRadius As Single
                        frmMain.DGE.RigidBody_GetBoundingBoxMinimum_OutV3 Item_Grid(X, Z, Index).RigidBody: BoundMin = GetVector3FromMatrixBuffer: RE
                        frmMain.DGE.RigidBody_GetBoundingBoxMaximum_OutV3 Item_Grid(X, Z, Index).RigidBody: BoundMax = GetVector3FromMatrixBuffer: RE
                        FlatRadius = DistVector2(MakeVector2(BoundMax.X, BoundMax.Y), MakeVector2(BoundMin.X, BoundMin.Y)) / (2 * Terrain_SmallestGridSize)
                        Terrain_SetObstacle ObstacleX, ObstacleZ, FlatRadius
                    End If
                    
                    'Increase use of the bucket to include our new item
                    Item_Count(X, Z) = Item_Count(X, Z) + 1
                    
                    'Remember the maximum reserved radius
                    MaxReservedRadius = Max_Float(MaxReservedRadius, ReservedRadius)
                End If
            End If
        End If
    End If
End Sub

Public Sub Item_Delete(X As Long, Z As Long, Index As Long)
    If X >= 0 And X < GridSizeX And Z >= 0 And Z < GridSizeZ And Index < Item_Count(X, Z) Then
        'Delete any visual instance
        If Item_Grid(X, Z, Index).Instance > 0 Then
            frmMain.DGE.Instance_Delete Item_Grid(X, Z, Index).Instance: RE
            Item_Grid(X, Z, Index).Instance = 0
        End If
        
        'Delete any rigid body
        If Item_Grid(X, Z, Index).RigidBody > 0 Then
            frmMain.DGE.RigidBody_Delete Item_Grid(X, Z, Index).RigidBody: RE
            Item_Grid(X, Z, Index).RigidBody = 0
            
            'Remove it from the obstacle map
            Terrain_ClearObstacle Item_Grid(X, Z, Index).ObstacleX, Item_Grid(X, Z, Index).ObstacleZ
        End If
        
        'If the item is not last in the bucket
        If Index < Item_Count(X, Z) - 1 Then
            'Overwrite the removed item with the last item
            Item_Grid(X, Z, Index) = Item_Grid(X, Z, Item_Count(X, Z) - 1)
        End If
        
        'Remove the last item in the bucket
        Item_Count(X, Z) = Item_Count(X, Z) - 1
    End If
End Sub

Public Sub Item_Delete_InsideCircle(CenterX As Single, CenterZ As Single, Radius As Single)
    Dim X As Long
    Dim Z As Long
    Dim Index As Long
    Dim StartX As Long
    Dim StartZ As Long
    Dim EndX As Long
    Dim EndZ As Long
    Dim Pos As Vector3
    Dim Dist As Single
    
    'Find the minimum and maximum X,Z coordinates to look in
    StartX = Int(Terrain_WorldXToMapU(CenterX - Radius) * GridSizeX)
    StartZ = Int(Terrain_WorldZToMapV(CenterZ - Radius) * GridSizeZ)
    EndX = Int(Terrain_WorldXToMapU(CenterX + Radius) * GridSizeX)
    EndZ = Int(Terrain_WorldZToMapV(CenterZ + Radius) * GridSizeZ)
    
    'Loop through every tile that touch the infinite cylinder's axis aligned bounding box
    For X = Max_Long(0, StartX) To Min_Long(GridSizeX - 1, EndX)
        For Z = Max_Long(0, StartZ) To Min_Long(GridSizeZ - 1, EndZ)
            'Loop through every bucket in the tile backwards to avoid affecting the buckets that we have not yet iterated over
            For Index = Item_Count(X, Z) - 1 To 0 Step -1
                'Get the item's position
                Pos = Item_Grid(X, Z, Index).Position
                
                'Calculate the distance between the center and the item's position without the Y axis
                Dist = Sqr((CenterX - Pos.X) ^ 2 + (CenterZ - Pos.Z) ^ 2)
                
                'If the item's center is inside the infinite cylinder
                If Dist < Radius Then
                    'Delete the item in the circle
                    Item_Delete X, Z, Index
                End If
            Next Index
        Next Z
    Next X
End Sub

Public Sub Item_Damage(X As Long, Z As Long, Index As Long, HealthPoints As Single)
    If X >= 0 And X < GridSizeX And Z >= 0 And Z < GridSizeZ And Index < Item_Count(X, Z) Then
        If Item_Grid(X, Z, Index).Damage < Damage_Full Then
            Item_Grid(X, Z, Index).Health = Item_Grid(X, Z, Index).Health - HealthPoints
            If Item_Grid(X, Z, Index).Health <= 0 Then
                Item_Grid(X, Z, Index).Health = 0
                Item_Grid(X, Z, Index).Damage = Item_Grid(X, Z, Index).Damage + 1
                If Item_Grid(X, Z, Index).Model(Item_Grid(X, Z, Index).Damage) = 0 Then
                    'No damage model. Just delete it.
                    Item_Delete X, Z, Index
                Else
                    If Item_Grid(X, Z, Index).Instance > 0 Then
                        'Replace the visual model if it is currently visible
                        frmMain.DGE.Instance_ReplaceModel Item_Grid(X, Z, Index).Instance, Item_Grid(X, Z, Index).Model(Item_Grid(X, Z, Index).Damage): RE
                    End If
                    
                    'Replace rigid body
                    CreateRigidBody X, Z, Index, Item_Grid(X, Z, Index).CollisionShape(Item_Grid(X, Z, Index).Damage)
                    
                    If Item_Grid(X, Z, Index).RigidBody = 0 Then
                        Terrain_ClearObstacle Item_Grid(X, Z, Index).ObstacleX, Item_Grid(X, Z, Index).ObstacleZ
                        Terrain_ObstacleMapHasChanged = True
                    End If
                End If
            End If
        End If
    End If
End Sub

Public Sub Item_SetVisibleRect(MinX As Single, MaxX As Single, MinZ As Single, MaxZ As Single)
    Dim X As Long
    Dim Z As Long
    Dim OldRect As BucketRect
    Dim NewRect As BucketRect
    NewRect = MakeSafeBucketRect(Int(Terrain_WorldXToMapU(MinX) * GridSizeX), Int(Terrain_WorldXToMapU(MaxX) * GridSizeX), Int(Terrain_WorldZToMapV(MinZ) * GridSizeZ), Int(Terrain_WorldZToMapV(MaxZ) * GridSizeZ))
    If BucketRect_IsEqual(VisibleRect, NewRect) = False Then
        If BucketRect_Overlap(VisibleRect, NewRect) Then
            'Remember the old rectangle
            OldRect = VisibleRect
            
            'Set the new value
            VisibleRect = NewRect
            
            'Hide buckets that belong to the old visible area but not the new visible area
            'This method assume that the rectangles are overlapping each other
                'Left
                For X = OldRect.MinX To NewRect.MinX - 1
                    For Z = OldRect.MinZ To OldRect.MaxZ
                        Debug.Assert BucketRect_Inside(OldRect, X, Z) = True
                        Debug.Assert BucketRect_Inside(NewRect, X, Z) = False
                        SetBucketVisibility X, Z, False
                    Next Z
                Next X
                'Right
                For X = NewRect.MaxX + 1 To OldRect.MaxX
                    For Z = OldRect.MinZ To OldRect.MaxZ
                        Debug.Assert BucketRect_Inside(OldRect, X, Z) = True
                        Debug.Assert BucketRect_Inside(NewRect, X, Z) = False
                        SetBucketVisibility X, Z, False
                    Next Z
                Next X
                'Top
                For X = Max_Long(OldRect.MinX, NewRect.MinX) To Min_Long(OldRect.MaxX, NewRect.MaxX)
                    For Z = OldRect.MinZ To NewRect.MinZ - 1
                        Debug.Assert BucketRect_Inside(OldRect, X, Z) = True
                        Debug.Assert BucketRect_Inside(NewRect, X, Z) = False
                        SetBucketVisibility X, Z, False
                    Next Z
                Next X
                'Bottom
                For X = Max_Long(OldRect.MinX, NewRect.MinX) To Min_Long(OldRect.MaxX, NewRect.MaxX)
                    For Z = NewRect.MaxZ + 1 To OldRect.MaxZ
                        Debug.Assert BucketRect_Inside(OldRect, X, Z) = True
                        Debug.Assert BucketRect_Inside(NewRect, X, Z) = False
                        SetBucketVisibility X, Z, False
                    Next Z
                Next X
            
            'Show buckets that belong to the new visible area but not the old visible area
            'This method assume that the rectangles are overlapping each other
                'Left
                For X = NewRect.MinX To OldRect.MinX - 1
                    For Z = NewRect.MinZ To NewRect.MaxZ
                        Debug.Assert BucketRect_Inside(OldRect, X, Z) = False
                        Debug.Assert BucketRect_Inside(NewRect, X, Z) = True
                        SetBucketVisibility X, Z, True
                    Next Z
                Next X
                'Right
                For X = OldRect.MaxX + 1 To NewRect.MaxX
                    For Z = NewRect.MinZ To NewRect.MaxZ
                        Debug.Assert BucketRect_Inside(OldRect, X, Z) = False
                        Debug.Assert BucketRect_Inside(NewRect, X, Z) = True
                        SetBucketVisibility X, Z, True
                    Next Z
                Next X
                'Top
                For X = Max_Long(OldRect.MinX, NewRect.MinX) To Min_Long(OldRect.MaxX, NewRect.MaxX)
                    For Z = NewRect.MinZ To OldRect.MinZ - 1
                        Debug.Assert BucketRect_Inside(OldRect, X, Z) = False
                        Debug.Assert BucketRect_Inside(NewRect, X, Z) = True
                        SetBucketVisibility X, Z, True
                    Next Z
                Next X
                'Bottom
                For X = Max_Long(OldRect.MinX, NewRect.MinX) To Min_Long(OldRect.MaxX, NewRect.MaxX)
                    For Z = OldRect.MaxZ + 1 To NewRect.MaxZ
                        Debug.Assert BucketRect_Inside(OldRect, X, Z) = False
                        Debug.Assert BucketRect_Inside(NewRect, X, Z) = True
                        SetBucketVisibility X, Z, True
                    Next Z
                Next X
        Else
            'This method always works but must loop through buckets that we don't need to change if the old and new visible rectangle is overlapping
            
            'Hide the old rectangle
            SetVisibleRectVisibility False
            
            'Set the new value
            VisibleRect = NewRect
            
            'Show the old rectangle
            SetVisibleRectVisibility True
        End If
    End If
End Sub

Private Sub SetVisibleRectVisibility(Visible As Boolean)
    Dim X As Long
    Dim Z As Long
    
    'Hide the visible rectangle of tiles
    For X = VisibleRect.MinX To VisibleRect.MaxX
        For Z = VisibleRect.MinZ To VisibleRect.MaxZ
            SetBucketVisibility X, Z, Visible
        Next Z
    Next X
End Sub

Private Sub SetBucketVisibility(X As Long, Z As Long, Visible As Boolean)
    Dim Item As Long
    Dim NewInstance As Long
    Dim Pos As Vector3
    Dim Size As Single
    Debug.Assert X >= 0 And Z >= 0 And X < GridSizeX And Z < GridSizeZ
    For Item = 0 To Item_Count(X, Z) - 1
        If Visible And Item_Grid(X, Z, Item).Instance = 0 Then
            'Get the position and size from the grid
            Pos = Item_Grid(X, Z, Item).Position
            Size = Item_Grid(X, Z, Item).Size
            
            'Show the item by creating it's visual instance
            NewInstance = frmMain.DGE.Instance_Create(Item_Grid(X, Z, Item).Model(Item_Grid(X, Z, Item).Damage)): RE
            
            'Set the argument
            Dim Arg As Vector4
            Arg = Item_Grid(X, Z, Item).SourceUV
            If Arg.X <> 0 Or Arg.Y <> 0 Or Arg.Z <> 0 Or Arg.W <> 0 Then
                'Set the data if there is any
                frmMain.DGE.Instance_SetUserDefinedData NewInstance, 0, Arg.X, Arg.Y, Arg.Z, Arg.W: RE
            End If
            
            'If it has no rigid body
            If Item_Grid(X, Z, Item).RigidBody = 0 Then
                'Place without rigid body
                frmMain.DGE.Instance_SetPosition NewInstance, Pos.X, Pos.Y, Pos.Z: RE
                SetInstanceAxisSystem NewInstance, MulMatVec3(MakeAxisSystem_Polar(Item_Grid(X, Z, Item).Longitude, Item_Grid(X, Z, Item).Lattitude), MakeVector3(Size, Size, Size))
            Else
                'Let the visual instance follow the rigid body.
                'Following a static or kinematic rigid body do not require any extra call for updating since it can only move when you tell it to move.
                'If you decide to move the rigid body, you should make it kinematic so that it can activate sleeping rigid bodies that it collide with.
                frmMain.DGE.Instance_FollowRigidBody NewInstance, Item_Grid(X, Z, Item).RigidBody, True: RE
                
                'This alternative method would do the same thing in a different way without creating any relation
                'frmMain.DGE.Instance_PlaceAtRigidBody NewInstance, Item_Grid(X, Z, Item).RigidBody, True: RE
            End If
            
            'Tell the engine that the visual instance will listen to the Enviroment_UpdateAutomaticDetailLevels call
            frmMain.DGE.Instance_SetAutoDetailLevel NewInstance, True: RE
            
            'Store a reference to the visual instance so that we can delete it
            Item_Grid(X, Z, Item).Instance = NewInstance
        Else
            'Delete the visual instance
            frmMain.DGE.Instance_Delete Item_Grid(X, Z, Item).Instance: RE
            Item_Grid(X, Z, Item).Instance = 0
        End If
    Next Item
End Sub

Public Function Item_LineIntersection(StartPosition As Vector3, EndPosition As Vector3) As IntersectionResult
    Dim CurrentEndPosition As Vector3
    Dim Collided As Boolean
    Dim X As Long
    Dim Z As Long
    Dim RX As Single
    Dim RZ As Single
    Dim CX As Long
    Dim CZ As Long
    Dim Index As Long
    Dim Radius As Single
    Dim StartX As Long
    Dim StartZ As Long
    Dim EndX As Long
    Dim EndZ As Long
    Const SampleThickness As Long = 1
    
    Item_LineIntersection.Collided = False
    Item_LineIntersection.Type = IRT_Nothing
    Item_LineIntersection.Index = 0
    Item_LineIntersection.X = 0
    Item_LineIntersection.Z = 0
    CurrentEndPosition = EndPosition
    
    If Abs(StartPosition.X - EndPosition.X) < 1 And Abs(StartPosition.Z - EndPosition.Z) < 1 Then
        'Sample small rectangle
        StartX = Int(Terrain_WorldXToMapU(Min_Float(StartPosition.X, EndPosition.X)) * GridSizeX)
        EndX = Int(Terrain_WorldXToMapU(Max_Float(StartPosition.X, EndPosition.X)) * GridSizeX)
        StartZ = Int(Terrain_WorldZToMapV(Min_Float(StartPosition.Z, EndPosition.Z)) * GridSizeZ)
        EndZ = Int(Terrain_WorldZToMapV(Max_Float(StartPosition.Z, EndPosition.Z)) * GridSizeZ)
        For X = Max_Long(0, StartX - SampleThickness) To Min_Long(GridSizeX - 1, EndX + SampleThickness)
            For Z = Max_Long(0, StartZ - SampleThickness) To Min_Long(GridSizeZ - 1, EndZ + SampleThickness)
                For Index = 0 To Item_Count(X, Z) - 1
                    If Item_Grid(X, Z, Index).RigidBody > 0 Then
                        Collided = (frmMain.DGE.RigidBody_GetFirstIntersection_Out2V3(StartPosition.X, StartPosition.Y, StartPosition.Z, CurrentEndPosition.X, CurrentEndPosition.Y, CurrentEndPosition.Z, Item_Grid(X, Z, Index).RigidBody) > 0): RE
                        If Collided Then
                            Item_LineIntersection.Collided = True
                            Item_LineIntersection.Position = GetVector3FromMatrixBuffer
                            Item_LineIntersection.Normal = GetSecondVector3FromMatrixBuffer
                            Item_LineIntersection.Type = IRT_Item
                            Item_LineIntersection.Index = Index
                            Item_LineIntersection.X = X
                            Item_LineIntersection.Z = Z
                            CurrentEndPosition = Item_LineIntersection.Position 'Update the line
                        End If
                    End If
                Next Index
            Next Z
        Next X
    ElseIf Abs(StartPosition.X - EndPosition.X) > Abs(StartPosition.Z - EndPosition.Z) Then
        'Sample line along X
        StartX = Int(Terrain_WorldXToMapU(Min_Float(StartPosition.X, EndPosition.X)) * GridSizeX)
        EndX = Int(Terrain_WorldXToMapU(Max_Float(StartPosition.X, EndPosition.X)) * GridSizeX)
        For X = Max_Long(0, StartX - SampleThickness) To Min_Long(GridSizeX - 1, EndX + SampleThickness)
            RX = Terrain_MapUToWorldX((X + 0.5) / GridSizeX)
            CZ = Int(Terrain_WorldZToMapV(Lerp(StartPosition.Z, EndPosition.Z, InverseLerp(StartPosition.X, EndPosition.X, RX))) * GridSizeX)
            For Z = Max_Long(0, CZ - SampleThickness) To Min_Long(GridSizeZ - 1, CZ + SampleThickness)
                For Index = 0 To Item_Count(X, Z) - 1
                    If Item_Grid(X, Z, Index).RigidBody > 0 Then
                        Collided = (frmMain.DGE.RigidBody_GetFirstIntersection_Out2V3(StartPosition.X, StartPosition.Y, StartPosition.Z, CurrentEndPosition.X, CurrentEndPosition.Y, CurrentEndPosition.Z, Item_Grid(X, Z, Index).RigidBody) > 0): RE
                        If Collided Then
                            Item_LineIntersection.Collided = True
                            Item_LineIntersection.Position = GetVector3FromMatrixBuffer
                            Item_LineIntersection.Normal = GetSecondVector3FromMatrixBuffer
                            Item_LineIntersection.Type = IRT_Item
                            Item_LineIntersection.Index = Index
                            Item_LineIntersection.X = X
                            Item_LineIntersection.Z = Z
                            CurrentEndPosition = Item_LineIntersection.Position 'Update the line
                        End If
                    End If
                Next Index
            Next Z
        Next X
    Else
        'Sample line along Z
        StartZ = Int(Terrain_WorldZToMapV(Min_Float(StartPosition.Z, EndPosition.Z)) * GridSizeZ)
        EndZ = Int(Terrain_WorldZToMapV(Max_Float(StartPosition.Z, EndPosition.Z)) * GridSizeZ)
        For Z = Max_Long(0, StartZ - SampleThickness) To Min_Long(GridSizeZ - 1, EndZ + SampleThickness)
            RZ = Terrain_MapVToWorldZ((Z + 0.5) / GridSizeZ)
            CX = Int(Terrain_WorldXToMapU(Lerp(StartPosition.X, EndPosition.X, InverseLerp(StartPosition.Z, EndPosition.Z, RZ))) * GridSizeZ)
            For X = Max_Long(0, CX - SampleThickness) To Min_Long(GridSizeX - 1, CX + SampleThickness)
                For Index = 0 To Item_Count(X, Z) - 1
                    If Item_Grid(X, Z, Index).RigidBody > 0 Then
                        Collided = (frmMain.DGE.RigidBody_GetFirstIntersection_Out2V3(StartPosition.X, StartPosition.Y, StartPosition.Z, CurrentEndPosition.X, CurrentEndPosition.Y, CurrentEndPosition.Z, Item_Grid(X, Z, Index).RigidBody) > 0): RE
                        If Collided Then
                            Item_LineIntersection.Collided = True
                            Item_LineIntersection.Position = GetVector3FromMatrixBuffer
                            Item_LineIntersection.Normal = GetSecondVector3FromMatrixBuffer
                            Item_LineIntersection.Type = IRT_Item
                            Item_LineIntersection.Index = Index
                            Item_LineIntersection.X = X
                            Item_LineIntersection.Z = Z
                            CurrentEndPosition = Item_LineIntersection.Position 'Update the line
                        End If
                    End If
                Next Index
            Next X
        Next Z
    End If
End Function
