Nicht besonders schnell, aber relativ einfach zu bedienen.
Vielleicht findet jemand das nützlich. :3
Beizeiten werden noch 'n paar andere Funktionen dazukommen, sowie 'ne schöne Dokumentation.
Code: Alles auswählen
;Creates an auto-cleaning end-node-value only octree.
;Pros: Enables creating a 3D grid of much higher dimensions without preallocating the whole array.
;Cons: Memory usage is much higher and grows with the amount of elements.
;Note: This implementation behaves pretty much like a 3D-array, but does not support parent node values as of now.
Structure Octree
  *Child[8]
EndStructure
Procedure.i SetOctree(*Octree.Octree, X.i, Y.i, Z.i, Depth.i, Value.i, FirstNode.i = 1)
  ;Traverses the octree, creates new memory cells, and if Value is 0, destroys everything that is empty on its way back.
  
  ;Input variables:
  ;*Octree: Pointer to the tree root.
  ;X/Y/Z: Position in the tree.
  ;Depth: The node depth in the tree. The maximum dimensions are (2^Depth)-1.
  ;Value: This is the value the tree end note is set to.
  ;FirstNode: This is a flag that prevents out-of-bounds allocation and deletion of the tree root.
  
  ;Return values:
  ;Internally, it returns 1 (data exists), or 0 (data does not exist). If 0, it deletes unused elements until a node with used data is reached.
  ;Externally, there are 3 return values: -1 (out of bounds), 0 (no data left in tree), and 1 (data left in tree).
  
  Protected TwoPowerDepth.i = 1<<(Depth-1)
  
  If FirstNode ;Prevent out-of-bounds allocation. Only check on first node.
    If Depth = 0 Or X > TwoPowerDepth*2-1 Or Y > TwoPowerDepth*2-1 Or Z > TwoPowerDepth*2-1 Or X < 0 Or Y < 0 Or Z < 0
      ProcedureReturn -1
    EndIf
  EndIf
  
  ;Chunks indicate the cell that will be worked with.
  Protected ChunkX.i = Bool(X>=TwoPowerDepth)
  Protected ChunkY.i = Bool(Y>=TwoPowerDepth)
  Protected ChunkZ.i = Bool(Z>=TwoPowerDepth)
  Protected ChunkIndex.i = (ChunkX)+(ChunkY<<1)+(ChunkZ<<2)
  
  ;To prune the tree correctly, we need an always-empty memory location to compare an tree cell to.
  Protected *Null = AllocateMemory(SizeOf(Octree))
  
  Select Depth
    Case 1 ;End node
      Select Value
        Case 0
          ;Last node has only values, and does not point to other nodes.
          ;If the user assigns a pointer as a value, he has to keep track of it, lest it will cause memory leaks.
          *Octree\Child[ChunkIndex] = 0
          ;If the node is empty, flag for deletion.
          If CompareMemory(*Octree, *Null, SizeOf(Octree))
            FreeMemory(*Null)
            ProcedureReturn 0
          EndIf
          FreeMemory(*Null) ;Else, do not.
          ProcedureReturn 1
        Default
          ;Any other value will just be assigned.
          *Octree\Child[ChunkIndex] = Value
          FreeMemory(*Null)
          ProcedureReturn 1
      EndSelect
      
    Default ;Normal node
      ;If a child node does not exist, create it. Could optimize with (Value = 0) check.
      If *Octree\Child[ChunkIndex] = 0 
        *Octree\Child[ChunkIndex] = AllocateMemory(SizeOf(Octree))
      EndIf
      Select SetOctree(*Octree\Child[ChunkIndex], X-TwoPowerDepth*ChunkX, Y-TwoPowerDepth*ChunkY, Z-TwoPowerDepth*ChunkZ, Depth-1, Value, 0) ;Recursively call
        Case 0 ;Flag for deletion.
          FreeMemory(*Octree\Child[ChunkIndex]) ;Free unused memory.
          *Octree\Child[ChunkIndex] = 0
          If CompareMemory(*Octree, *Null, SizeOf(Octree)) ;If the node is empty, flag for deletion.
            FreeMemory(*Null)
            ProcedureReturn 0
          EndIf
          FreeMemory(*Null)
          ProcedureReturn 1 ;Else, stop deletion check of parent nodes.
        Case 1 ;Do not delete.
          FreeMemory(*Null)
          ProcedureReturn 1
      EndSelect
      
      
  EndSelect
EndProcedure
Procedure.i GetOctree(*Octree.Octree, X.i, Y.i, Z.i, Depth.i, Value.i = 0, FirstNode.i = 1)
  ;Gets a value from a point in an octree.
  
  ;Input variables:
  ;*Octree: Pointer to the tree root.
  ;X/Y/Z: Position in the tree.
  ;Depth: The node depth in the quadtree. The maximum dimensions are (2^Depth)-1.
  ;Value: The starting value. If the node does not exist, this value will be returned.
  ;FirstNode: This is a flag that prevents out-of-bounds queries.
  
  ;Return values:
  ;Value: Well, uh, the value that is stored in the tree, or the value that you put in if the node doesn't exist.
  
  Protected TwoPowerDepth.i = 1<<(Depth-1)
  
  If FirstNode ;Out of bounds check on first node.
    If Depth = 0 Or X > TwoPowerDepth*2-1 Or Y > TwoPowerDepth*2-1 Or Z > TwoPowerDepth*2-1 Or X < 0 Or Y < 0 Or Z < 0
      ProcedureReturn Value
    EndIf
  EndIf
  
  Protected ChunkX.i = Bool(X>=TwoPowerDepth)
  Protected ChunkY.i = Bool(Y>=TwoPowerDepth)
  Protected ChunkZ.i = Bool(Z>=TwoPowerDepth)
  Protected ChunkIndex.i = (ChunkX)+(ChunkY<<1)+(ChunkZ<<2)
  
  Select Depth
    Case 1 ;End node.
      Value = *Octree\Child[ChunkIndex]
    Default ;Normal node.
      If *Octree\Child[ChunkIndex]
        Value = GetOctree(*Octree\Child[ChunkIndex], X-TwoPowerDepth*ChunkX, Y-TwoPowerDepth*ChunkY, Z-TwoPowerDepth*ChunkZ, Depth-1, Value, 0)
      EndIf
  EndSelect
  
  ProcedureReturn Value
  
EndProcedure
Procedure.i PurgeOctree(*Octree.Octree, Depth.i, FirstNode.i = 1)
  ;Frees all elements in an octree.
  
  ;Input variables:
  ;*Octree: Pointer to the tree root.
  ;Depth: The node depth in the octree.
  ;Value: This is the value the octree end note is set to.
  ;FirstNode: This is a flag that prevents out-of-bounds allocation and deletion of the tree root.
  
  ;Return values: Always 0.
  Protected a.i
  If Depth > 1
    For a = 0 To 7
      If *Octree\Child[a] <> 0
        PurgeOctree(*Octree\Child[a], Depth-1, 0)
      EndIf
    Next
  EndIf
  
  If Not FirstNode
    FreeMemory(*Octree)
  Else
    For a = 0 To 7
      *Octree\Child[a] = 0
    Next
  EndIf
  ProcedureReturn 0
  
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
  Octree.Octree
  F.f = 0.3
  O.l = 0
  X.i = 13
  Y.i = 12
  Z.i = 0
  D.i = 8
  
  ;Can you assign floating point values? Kinda.
  ;Be careful with 32/64bit differences.
  SetOctree(@Octree, X, Y, Z, D, PeekI(@F))
  O = GetOctree(@Octree, X, Y, Z, D)
  Debug "___"
  Debug F
  Debug O
  Debug PeekF(@O)
  CallDebugger
  ;Integers are always the same size as pointers.
  ;These pointers are handled as values, and are NOT freed when set to 0 or purged.
  
  ;Basic test.
  SetOctree(@Octree, X, Y, Z, D, 15)
  SetOctree(@Octree, X-1, Y, Z, D, 13)
  Debug "___"
  ;Should read 15 and 13, respectively.
  Debug GetOctree(@Octree, X, Y, Z, D)
  Debug GetOctree(@Octree, X-1, Y, Z, D)
  CallDebugger
  
  ;Stress test time!
  For c = 0 To (1<<D)-1
    For b = 0 To (1<<D)-1
      For a = 0 To (1<<D)-1
        SetOctree(@Octree, a, b, c, D, 32)
      Next
    Next
  Next
  CallDebugger
  ;Now free the whole tree!
  PurgeOctree(@Octree, D)
  CallDebugger
  
  ;If the purge didn't mess up, this should not bring up an error.
  SetOctree(@Octree, X, Y, Z, D, 30)
  SetOctree(@Octree, X-1, Y, Z, D, 35)
  Debug "___"
  Debug GetOctree(@Octree, X, Y, Z, D)
  Debug GetOctree(@Octree, X-1, Y, Z, D)
  
  
  
  CallDebugger
  End
  
CompilerEndIf