GXS.SpacePartition.pas 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.SpacePartition;
  5. (*
  6. Space Partition speeds up geometrical queries, like what objects does A
  7. overlap.
  8. Nota that the class TOctreeSpacePartition is optimized for dynamic scenes with
  9. objects that are small in relation to the size of the Octree space. This from
  10. Eric;
  11. The non-duplicating octree shouldn't really be used if you have big objects,
  12. and this especially if you have lots of big objects (the more objects you have
  13. the less efficient the partitionning, due to the "magnifying glass" effect of
  14. the non-discriminating volume).
  15. *)
  16. interface
  17. {$I Stage.Defines.inc}
  18. uses
  19. System.Classes,
  20. System.SysUtils,
  21. System.Math,
  22. Stage.VectorTypes,
  23. Stage.VectorGeometry,
  24. GXS.GeometryBB,
  25. GXS.PersistentClasses;
  26. const
  27. COctree_LEAF_TRHESHOLD = 30;
  28. COctree_MAX_TREE_DEPTH = 8;
  29. COctree_GROW_GRAVY = 0.1;
  30. type
  31. TBaseSpacePartition = class;
  32. // Describes a cone, and is used for cone collision
  33. TSPCone = record
  34. // The base of the cone
  35. Base: TAffineVector;
  36. // The axis of the cone
  37. Axis: TAffineVector;
  38. // Angle of the cone
  39. Angle: Single;
  40. // Length of the cone
  41. Length: Single;
  42. end;
  43. // Extended frustum, used for fast intersection testing
  44. TExtendedFrustum = record
  45. Frustum: TFrustum;
  46. BSphere: TBSphere;
  47. // SPCone : TSPCone;
  48. end;
  49. // Used to store the actual objects in the SpacePartition
  50. TSpacePartitionLeaf = class(TgxPersistentObject)
  51. private
  52. FSpacePartition: TBaseSpacePartition;
  53. procedure SetSpacePartition(const Value: TBaseSpacePartition);
  54. public
  55. // This can be used by the space partitioner as it sees fit
  56. FPartitionTag: Pointer;
  57. // Leaves cache their AABBs so they can easily be accessed when needed by the space partitioner
  58. FCachedAABB: TAABB;
  59. (* Leaves cache their BoundingSpheres so they can easily be accessed when
  60. needed by the space partitioner *)
  61. FCachedBSphere: TBSphere;
  62. (* Whenever the size or location of the leaf changes, the space partitioner
  63. should be notified through a call to Changed. In the basic version, all it
  64. does is update the cached AABB and BSphere. You do not need to override this
  65. method. *)
  66. procedure Changed; virtual;
  67. // *** Override this!
  68. (* AABBs and BSpheres are cached for leafs, and this function should be
  69. overriden to update the cache from the structure that the leaf stores. This
  70. is the only function you MUST override to use space partitions. *)
  71. procedure UpdateCachedAABBAndBSphere; virtual;
  72. // The TBaseSpacePartition that owns this leaf
  73. property SpacePartition: TBaseSpacePartition read FSpacePartition
  74. write SetSpacePartition;
  75. // This tag can be used by the space partition to store vital information in the leaf
  76. property PartitionTag: Pointer read FPartitionTag;
  77. constructor CreateOwned(SpacePartition: TBaseSpacePartition);
  78. destructor Destroy; override;
  79. end;
  80. // List for storing space partition leaves
  81. TSpacePartitionLeafList = class(TgxPersistentObjectList)
  82. private
  83. function GetItems(I: Integer): TSpacePartitionLeaf;
  84. procedure SetItems(I: Integer; const Value: TSpacePartitionLeaf);
  85. public
  86. property Items[I: Integer]: TSpacePartitionLeaf read GetItems
  87. write SetItems; default;
  88. constructor Create; override;
  89. end;
  90. TCullingMode = (CmFineCulling, CmGrossCulling);
  91. // Basic space partition, does not implement any actual space partitioning
  92. TBaseSpacePartition = class(TgxPersistentObject)
  93. private
  94. FCullingMode: TCullingMode;
  95. // Query space for Leaves that intersect a cone, result is returned in QueryResult
  96. function QueryCone(const ACone: TSPCone): Integer; virtual;
  97. protected
  98. FQueryResult: TSpacePartitionLeafList;
  99. FQueryInterObjectTests: Integer;
  100. // Empties the search result and resetting all search statistics
  101. procedure FlushQueryResult; virtual;
  102. public
  103. // The results from the last query
  104. property QueryResult: TSpacePartitionLeafList read FQueryResult;
  105. // Clear all internal storage Leaves
  106. procedure Clear; virtual;
  107. // ** Update space partition
  108. (* Add a leaf *)
  109. procedure AddLeaf(ALeaf: TSpacePartitionLeaf); virtual;
  110. // Remove a leaf
  111. procedure RemoveLeaf(ALeaf: TSpacePartitionLeaf); virtual;
  112. // Called by leaf when it has changed
  113. procedure LeafChanged(ALeaf: TSpacePartitionLeaf); virtual;
  114. // ** Query space partition
  115. (* Query space for Leaves that intersect the axis aligned bounding box,
  116. result is returned through QueryResult *)
  117. function QueryAABB(const AAABB: TAABB): Integer; virtual;
  118. (* Query space for Leaves that intersect the bounding sphere, result is
  119. returned through QueryResult *)
  120. function QueryBSphere(const ABSphere: TBSphere): Integer; virtual;
  121. (* Query space for Leaves that intersect the bounding sphere or box
  122. of a leaf. Result is returned through QueryResult *)
  123. function QueryLeaf(const ALeaf: TSpacePartitionLeaf): Integer; virtual;
  124. (* Query space for Leaves that intersect a plane. Result is returned through
  125. QueryResult *)
  126. function QueryPlane(const Location, Normal: TAffineVector)
  127. : Integer; virtual;
  128. (* Query space for Leaves that intersect a Frustum. Result is returned through
  129. QueryResult *)
  130. function QueryFrustum(const Frustum: TFrustum): Integer; virtual;
  131. (* Query space for Leaves that intersect an extended frustum. Result is
  132. returned through QueryResult. Extended frustum is slightly faster than the
  133. regular frustum because it uses a bounding sphere for the frustum *)
  134. function QueryFrustumEx(const ExtendedFrustum: TExtendedFrustum)
  135. : Integer; virtual;
  136. (* Once a query has been run, this number tells of how many inter object
  137. tests that were run. This value must be set by all that override the
  138. queries. *)
  139. property QueryInterObjectTests: Integer read FQueryInterObjectTests;
  140. (* Some space partitioners delay processing changes until all changes have
  141. been made. ProcessUpdated should be called when all changes have been
  142. performed. *)
  143. procedure ProcessUpdated; virtual;
  144. (* Determines if the spatial structure should do very simple preliminary
  145. culling (gross culling) or a more detailed form of culling (fine culling) *)
  146. property CullingMode: TCullingMode read FCullingMode write FCullingMode;
  147. constructor Create; override;
  148. destructor Destroy; override;
  149. end;
  150. (* Implements a list of all leaves added to the space partition, _not_ a
  151. good solution, but it can be used as a benchmark against more complex methods *)
  152. TLeavedSpacePartition = class(TBaseSpacePartition)
  153. private
  154. FLeaves: TSpacePartitionLeafList;
  155. (* Query space for Leaves that intersect a cone, result is returned through
  156. QueryResult *)
  157. function QueryCone(const ACone: TSPCone): Integer; override;
  158. public
  159. // Clear all internal storage Leaves
  160. procedure Clear; override;
  161. // ** Update space partition
  162. (* Add a leaf *)
  163. procedure AddLeaf(ALeaf: TSpacePartitionLeaf); override;
  164. // Remove a leaf
  165. procedure RemoveLeaf(ALeaf: TSpacePartitionLeaf); override;
  166. // ** Query space partition
  167. (* Query space for Leaves that intersect the axis aligned bounding box,
  168. result is returned through QueryResult. This override scans _all_ leaves
  169. in the list, so it's far from optimal. *)
  170. function QueryAABB(const AAABB: TAABB): Integer; override;
  171. (* Query space for Leaves that intersect the bounding sphere, result is
  172. returned through QueryResult. This override scans _all_ leaves
  173. in the list, so it's far from optimal. *)
  174. function QueryBSphere(const ABSphere: TBSphere): Integer; override;
  175. (* Query space for Leaves that intersect a plane. Result is returned through
  176. QueryResult *)
  177. function QueryPlane(const FLocation, FNormal: TAffineVector)
  178. : Integer; override;
  179. constructor Create; override;
  180. destructor Destroy; override;
  181. published
  182. property Leaves: TSpacePartitionLeafList read FLeaves;
  183. end;
  184. TSectoredSpacePartition = class;
  185. TSectorNode = class;
  186. TSectorNodeArray = array [0 .. 7] of TSectorNode;
  187. (* Implements a SectorNode node. Each node can have 0 or 8 children, each child
  188. being a portion of the size of the parent. For quadtrees, that's 1/4, for
  189. octrees, it's 1/8 *)
  190. TSectorNode = class
  191. private
  192. FLeaves: TSpacePartitionLeafList;
  193. FAABB: TAABB;
  194. FSectoredSpacePartition: TSectoredSpacePartition;
  195. FRecursiveLeafCount: Integer;
  196. FParent: TSectorNode;
  197. FNodeDepth: Integer;
  198. FChildCount: Integer;
  199. FChildren: TSectorNodeArray;
  200. FBSphere: TBSphere;
  201. function GetNoChildren: Boolean;
  202. procedure SetAABB(const Value: TAABB);
  203. function GetCenter: TAffineVector;
  204. protected
  205. (* Recursively counts the RecursiveLeafCount, this should only be used in
  206. debugging purposes, because the proprtyu RecursiveLeafCount is always up to
  207. date. *)
  208. function CalcRecursiveLeafCount: Integer;
  209. (* Places a leaf in one of the children of this node, or in the node itself
  210. if it doesn't fit in any of the children *)
  211. function PlaceLeafInChild(ALeaf: TSpacePartitionLeaf): TSectorNode;
  212. (* Debug method that checks that FRecursiveLeafCount and
  213. CalcRecursiveLeafCount actually agree *)
  214. function VerifyRecursiveLeafCount: string;
  215. // Executed whenever the children of the node has changed
  216. procedure ChildrenChanged; virtual;
  217. public
  218. (* Clear deletes all children and empties the leaves. It doesn't destroy
  219. the leaves, as they belong to the SpacePartition *)
  220. procedure Clear;
  221. (* The Axis Aligned Bounding Box for this node. All leaves MUST fit inside
  222. this box. *)
  223. property AABB: TAABB read FAABB write SetAABB;
  224. // BSphere for this node
  225. property BSphere: TBSphere read FBSphere;
  226. // Center of the AABB for this node.
  227. property Center: TAffineVector read GetCenter;
  228. // NoChildren is true if the node has no children.
  229. property NoChildren: Boolean read GetNoChildren;
  230. // A list of the children for this node, only ChildCount children are none nil
  231. property Children: TSectorNodeArray read FChildren;
  232. // The number of child sectors that have been created
  233. property ChildCount: Integer read FChildCount;
  234. // Computes which child the AABB should go in. Returns nil if no such child exists
  235. function GetChildForAABB(AABB: TAABB): TSectorNode; virtual;
  236. // The leaves that are stored in this node
  237. property Leaves: TSpacePartitionLeafList read FLeaves;
  238. // The Structure that owns this node
  239. property SectoredSpacePartition: TSectoredSpacePartition
  240. read FSectoredSpacePartition;
  241. (* The parent node of this node. If parent is nil, that means that this
  242. node is the root node *)
  243. property Parent: TSectorNode read FParent;
  244. // The number of leaves stored in this node and all it's children.
  245. property RecursiveLeafCount: Integer read FRecursiveLeafCount;
  246. (* The tree depth at which this node is located. For the root, this value
  247. is 0, for the roots children, it is 1 and so on *)
  248. property NodeDepth: Integer read FNodeDepth;
  249. // Checks if an AABB fits completely inside this node
  250. function AABBFitsInNode(const AAABB: TAABB): Boolean; virtual;
  251. // Checks if an AABB intersects this node
  252. function AABBIntersectsNode(const AAABB: TAABB): Boolean; virtual;
  253. // Checks if a BSphere fits completely inside this node
  254. function BSphereFitsInNode(const BSphere: TBSphere): Boolean; virtual;
  255. // Checks if a BSphere intersects this node
  256. function BSphereIntersectsNode(const BSphere: TBSphere): Boolean; virtual;
  257. // Checks if a AABB partially or completely contains this sector
  258. function AABBContainsSector(const AABB: TAABB): TSpaceContains; virtual;
  259. // Checks if a BSphere partially or completely contains this sector
  260. function BSphereContainsSector(const BSphere: TBSphere)
  261. : TSpaceContains; virtual;
  262. // Checks if this node partially or completely contains a BSphere
  263. function ContainsBSphere(const ABSphere: TBSphere): TSpaceContains; virtual;
  264. // Checks if this node partially or completely contains an AABB
  265. function ContainsAABB(const AAABB: TAABB): TSpaceContains; virtual;
  266. (* Adds leaf to this node - or one of it's children. If the node has enough
  267. leaves and has no children, children will be created and all leaves will be
  268. spread among the children. *)
  269. function AddLeaf(ALeaf: TSpacePartitionLeaf): TSectorNode;
  270. (* Remove leaf will remove a leaf from this node. If it is determined that
  271. this node has too few leaves after the delete, it may be collapsed. Returns
  272. true if the node was actually collapsed *)
  273. function RemoveLeaf(ALeaf: TSpacePartitionLeaf;
  274. OwnerByThis: Boolean): Boolean;
  275. // Query the node and its children for leaves that match the AABB
  276. procedure QueryAABB(const AAABB: TAABB;
  277. const QueryResult: TSpacePartitionLeafList);
  278. // Query the node and its children for leaves that match the BSphere
  279. procedure QueryBSphere(const ABSphere: TBSphere;
  280. const QueryResult: TSpacePartitionLeafList);
  281. // Query the node and its children for leaves that match the plane
  282. procedure QueryPlane(const Location, Normal: TAffineVector;
  283. const QueryResult: TSpacePartitionLeafList);
  284. // Query the node and its children for leaves that match the frustum.
  285. procedure QueryFrustum(const Frustum: TFrustum;
  286. const QueryResult: TSpacePartitionLeafList);
  287. // Query the node and its children for leaves that match the extended frustum.
  288. procedure QueryFrustumEx(const ExtendedFrustum: TExtendedFrustum;
  289. const QueryResult: TSpacePartitionLeafList);
  290. (* Adds all leaves to query result without testing if they intersect, and
  291. then do the same for all children. This is used when QueryAABB or
  292. QueryBSphere determines that a node fits completely in the searched space *)
  293. procedure AddAllLeavesRecursive(const QueryResult: TSpacePartitionLeafList);
  294. // Add children to this node and spread the leaves among it's children
  295. procedure ExpandNode;
  296. // Create the number of children this node type needs
  297. procedure CreateChildren; virtual;
  298. // Delete all children for this node, adding their leaves to this node
  299. procedure CollapseNode;
  300. // Returns the number of nodes in the Octree
  301. function GetNodeCount: Integer;
  302. constructor Create(ASectoredSpacePartition: TSectoredSpacePartition;
  303. AParent: TSectorNode);
  304. destructor Destroy; override;
  305. end;
  306. (* Implements sectored space partitioning, sectored space partitions include
  307. Octrees, Quadtrees and BSP-trees *)
  308. TGrowMethod = (gmNever, gmBestFit, gmIncreaseToFitAll);
  309. TSectoredSpacePartition = class(TLeavedSpacePartition)
  310. private
  311. FRootNode: TSectorNode;
  312. FLeafThreshold: Integer;
  313. FMaxTreeDepth: Integer;
  314. FGrowGravy: Single;
  315. FGrowMethod: TGrowMethod;
  316. procedure SetLeafThreshold(const Value: Integer);
  317. procedure SetMaxTreeDepth(const Value: Integer);
  318. protected
  319. FQueryNodeTests: Integer;
  320. // Empties the search result and resetting all search statistics
  321. procedure FlushQueryResult; override;
  322. public
  323. // ** Update space partition
  324. (* Add a leaf to the structure. If the leaf doesn't fit in the structure, the
  325. structure is either grown or an exception is raised. If GrowMethod is set to
  326. gmBestFit or gmIncreaseToFitAll, the octree will be grown. *)
  327. procedure AddLeaf(ALeaf: TSpacePartitionLeaf); override;
  328. // Remove a leaf from the structure.
  329. procedure RemoveLeaf(ALeaf: TSpacePartitionLeaf); override;
  330. // Called by leaf when it has changed, the leaf will be moved to an apropriate node
  331. procedure LeafChanged(ALeaf: TSpacePartitionLeaf); override;
  332. // ** Query space partition
  333. (* Query space for Leaves that intersect the axis aligned bounding box,
  334. result is returned through QueryResult. This method simply defers to the
  335. QueryAABB method of the root node. *)
  336. function QueryAABB(const AAABB: TAABB): Integer; override;
  337. (* Query space for Leaves that intersect the bounding sphere, result is
  338. returned through QueryResult. This method simply defers to the
  339. QueryBSphere method of the root node. *)
  340. function QueryBSphere(const ABSphere: TBSphere): Integer; override;
  341. (* Query space for Leaves that intersect the bounding sphere or box
  342. of a leaf. Result is returned through QueryResult *)
  343. function QueryLeaf(const ALeaf: TSpacePartitionLeaf): Integer; override;
  344. (* Query space for Leaves that intersect a plane. Result is returned through
  345. QueryResult *)
  346. function QueryPlane(const Location, Normal: TAffineVector)
  347. : Integer; override;
  348. (* Query space for Leaves that intersect a Frustum. Result is returned through
  349. QueryResult *)
  350. function QueryFrustum(const Frustum: TFrustum): Integer; override;
  351. (* Query space for Leaves that intersect an extended frustum. Result is
  352. returned through QueryResult *)
  353. function QueryFrustumEx(const ExtendedFrustum: TExtendedFrustum)
  354. : Integer; override;
  355. (* After a query has been run, this value will contain the number of nodes
  356. that were checked during the query *)
  357. property QueryNodeTests: Integer read FQueryNodeTests;
  358. // Returns the number of nodes in the structure
  359. function GetNodeCount: Integer;
  360. (* UpdateOctreeSize will grow and / or shrink the structure to fit the
  361. current leaves +-gravy *)
  362. procedure UpdateStructureSize(Gravy: Single);
  363. (* Rebuild tree will change the tree to the newAABB size, and completely
  364. rebuild it *)
  365. procedure RebuildTree(const NewAABB: TAABB);
  366. // Returns the _total_ AABB in structure
  367. function GetAABB: TAABB;
  368. (* CreateNewNode creates a new node of the TSectorNode subclass that this
  369. structure requires *)
  370. function CreateNewNode(AParent: TSectorNode): TSectorNode; virtual;
  371. procedure Clear; override;
  372. constructor Create; override;
  373. destructor Destroy; override;
  374. published
  375. // Root TSectorNode that all others stem from
  376. property RootNode: TSectorNode read FRootNode;
  377. // Determines how deep a tree should be allowed to grow.
  378. property MaxTreeDepth: Integer read FMaxTreeDepth write SetMaxTreeDepth;
  379. // Determines when a node should be split up to form children.
  380. property LeafThreshold: Integer read FLeafThreshold write SetLeafThreshold;
  381. (* Determines if the structure should grow with new leaves, or if an exception
  382. should be raised *)
  383. property GrowMethod: TGrowMethod read FGrowMethod write FGrowMethod;
  384. (* When the structure is recreated because it's no longer large enough to fit
  385. all leafs, it will become large enough to safely fit all leafs, plus
  386. GrowGravy. This is to prevent too many grows *)
  387. property GrowGravy: Single read FGrowGravy write FGrowGravy;
  388. end;
  389. // ** OCTTREE
  390. { Implements sector node that handles octrees }
  391. TSPOctreeNode = class(TSectorNode)
  392. public
  393. { Create 8 TSPOctreeNode children }
  394. procedure CreateChildren; override;
  395. { Checks if an AABB fits completely inside this node }
  396. function AABBFitsInNode(const AAABB: TAABB): Boolean; override;
  397. { Checks if an AABB intersects this node }
  398. function AABBIntersectsNode(const AAABB: TAABB): Boolean; override;
  399. { Checks if a BSphere fits completely inside this node }
  400. function BSphereFitsInNode(const BSphere: TBSphere): Boolean; override;
  401. { Checks if a BSphere intersects this node }
  402. function BSphereIntersectsNode(const BSphere: TBSphere): Boolean; override;
  403. end;
  404. { Implements octrees }
  405. TOctreeSpacePartition = class(TSectoredSpacePartition)
  406. public
  407. { Set size updates the size of the Octree }
  408. procedure SetSize(const Min, Max: TAffineVector);
  409. { CreateNewNode creates a new TSPOctreeNode }
  410. function CreateNewNode(AParent: TSectorNode): TSectorNode; override;
  411. end;
  412. // ** QUADTREE
  413. (* Implements sector node that handles quadtrees. *)
  414. TSPQuadtreeNode = class(TSPOctreeNode)
  415. protected
  416. (* Executed whenever the children of the node has changed. In the quadtree,
  417. we want to make sure the Y value of the AABB is correct up and down and that
  418. the bounding sphere is correct *)
  419. procedure ChildrenChanged; override;
  420. public
  421. // Create 4 TSPQuadtreeNode children
  422. procedure CreateChildren; override;
  423. // Checks if an AABB fits completely inside this node
  424. function AABBFitsInNode(const AAABB: TAABB): Boolean; override;
  425. // Checks if an AABB intersects this node
  426. function AABBIntersectsNode(const AAABB: TAABB): Boolean; override;
  427. // Checks if a BSphere fits completely inside this node
  428. function BSphereFitsInNode(const BSphere: TBSphere): Boolean; override;
  429. // Checks if a BSphere intersects this node
  430. function BSphereIntersectsNode(const BSphere: TBSphere): Boolean; override;
  431. // Computes which child the AABB should go in. Returns nil if no such child exists
  432. function GetChildForAABB(AABB: TAABB): TSectorNode; override;
  433. end;
  434. (* Implements quadtrees.
  435. Quadtrees are hardcoded to completely ignore the Y axis, only using X and Z
  436. to determine positioning.
  437. This means that they're well suited for 2d-ish situations (landscapes with
  438. trees for instance) but not for fully 3d situations (space fighting). *)
  439. TQuadtreeSpacePartition = class(TSectoredSpacePartition)
  440. public
  441. // Set size updates the size of the Octree
  442. procedure SetSize(const Min, Max: TAffineVector);
  443. // CreateNewNode creates a new TSPOctreeNode
  444. function CreateNewNode(AParent: TSectorNode): TSectorNode; override;
  445. end;
  446. // Determines to which extent one Cone contains an BSphere
  447. function ConeContainsBSphere(const Cone: TSPCone; BSphere: TBSphere)
  448. : TSpaceContains;
  449. // Determines if a extended frustum intersects an BSphere
  450. function ExtendedFrustumIntersectsBSphere(const AExtendedFrustum
  451. : TExtendedFrustum; ABSphere: TBSphere): Boolean;
  452. // Create an extended frustum from a number of values
  453. function ExtendedFrustumMake(const AFrustum: TFrustum;
  454. const ANearDist, AFarDist, AFieldOfViewRadians: Single;
  455. const ACameraPosition, ALookVector: TAffineVector (* ;
  456. const AScreenWidth, AScreenHeight : integer (* *) ): TExtendedFrustum;
  457. //-------------------------------------------------
  458. implementation
  459. //-------------------------------------------------
  460. (* This was copied from Octree.pas!
  461. Theory on FlagMax and FlagMin:
  462. When a node is subdivided, each of the 8 children assumes 1/8th ownership of its
  463. parent's bounding box (defined by parent extents). Calculating a child's min/max
  464. extent only requires 3 values: the parent's min extent, the parent's max extent
  465. and the midpoint of the parent's extents (since the cube is divided in half twice).
  466. The following arrays assume that the children are numbered from 0 to 7, named Upper
  467. and Lower (Upper = top 4 cubes on Y axis, Bottom = lower 4 cubes), Left and Right, and
  468. Fore and Back (Fore facing furthest away from you the viewer).
  469. Each node can use its corresponding element in the array to flag the operation needed
  470. to find its new min/max extent. Note that min, mid and max refer to an array of
  471. 3 coordinates (x,y,z); each of which are flagged separately. Also note that these
  472. flags are based on the Y vector being the up vector. *)
  473. const
  474. CMIN = 0;
  475. CMID = 1;
  476. CMAX = 2;
  477. COctFlagMIN: array [0 .. 7] of array [0 .. 2] of Byte = ((CMIN, CMID, CMID),
  478. // Upper Fore Left
  479. (CMID, CMID, CMID), // Upper Fore Right
  480. (CMIN, CMID, CMIN), // Upper Back Left
  481. (CMID, CMID, CMIN), // Upper Back Right
  482. (CMIN, CMIN, CMID), // Lower Fore Left (similar to above except height/2)
  483. (CMID, CMIN, CMID), // Lower Fore Right
  484. (CMIN, CMIN, CMIN), // Lower Back Left
  485. (CMID, CMIN, CMIN) // Lower Back Right
  486. );
  487. COctFlagMax: array [0 .. 7] of array [0 .. 2] of Byte = ((CMID, CMAX, CMAX),
  488. // Upper Fore Left
  489. (CMAX, CMAX, CMAX), // Upper Fore Right
  490. (CMID, CMAX, CMID), // Upper Back Left
  491. (CMAX, CMAX, CMID), // Upper Back Right
  492. (CMID, CMID, CMAX), // Lower Fore Left (similar to above except height/2)
  493. (CMAX, CMID, CMAX), // Lower Fore Right
  494. (CMID, CMID, CMID), // Lower Back Left
  495. (CMAX, CMID, CMID) // Lower Back Right
  496. );
  497. function ConeContainsBSphere(const Cone: TSPCone; BSphere: TBSphere)
  498. : TSpaceContains;
  499. var
  500. U, D: TAffineVector;
  501. E, Dsqr: Single;
  502. begin
  503. // NOTE: This code hasn't been verified
  504. // U = K.vertex - (Sphere.radius/K.sin)*K.axis;
  505. U := VectorSubtract(Cone.Base, VectorScale(Cone.Axis,
  506. BSphere.Radius / Sin(Cone.Angle)));
  507. // D = S.center - U;
  508. D := VectorSubtract(BSphere.Center, U);
  509. // dsqr = Dot(D,D)
  510. Dsqr := VectorDotProduct(D, D);
  511. // e = Dot(K.axis,D);
  512. E := VectorDotProduct(Cone.Axis, D);
  513. if (E > 0) and (E * E >= Dsqr * Sqr(Cos(Cone.Angle))) then
  514. begin
  515. // D = S.center - K.vertex;
  516. D := VectorSubtract(BSphere.Center, Cone.Base);
  517. // dsqr = Dot(D,D);
  518. Dsqr := VectorDotProduct(D, D);
  519. // e = -Dot(K.axis,D);
  520. E := -VectorDotProduct(Cone.Axis, D);
  521. if (E > 0) and (E * E >= Dsqr * (Sqr(Sin(Cone.Angle)))) then
  522. begin
  523. if Dsqr <= BSphere.Radius * BSphere.Radius then
  524. Result := ScContainsPartially
  525. else
  526. Result := ScNoOverlap;
  527. end
  528. else
  529. Result := ScContainsPartially;
  530. end
  531. else
  532. Result := ScNoOverlap;
  533. end; // }
  534. function ExtendedFrustumIntersectsBSphere(const AExtendedFrustum
  535. : TExtendedFrustum; ABSphere: TBSphere): Boolean;
  536. begin
  537. // Test if the bounding sphere of the node intersect the bounding sphere of the
  538. // frustum? This test is exremely fast
  539. if not BSphereIntersectsBSphere(ABSphere, AExtendedFrustum.BSphere) then
  540. Result := False
  541. // Test if the bsphere of the node intersects the frustum
  542. else if IsVolumeClipped(ABSphere.Center, ABSphere.Radius,
  543. AExtendedFrustum.Frustum) then
  544. Result := False
  545. else
  546. Result := True;
  547. end;
  548. function ExtendedFrustumMake(const AFrustum: TFrustum;
  549. const ANearDist, AFarDist, AFieldOfViewRadians: Single;
  550. const ACameraPosition, ALookVector: TAffineVector { ;
  551. const AScreenWidth, AScreenHeight : integer{ } ): TExtendedFrustum;
  552. var
  553. ViewLen: Single;
  554. Height, Width: Single;
  555. // Depth, Corner, NewFov : single;
  556. P, Q, VDiff: TAffineVector; // }
  557. begin
  558. // See http://www.flipcode.com/articles/article_frustumculling.shtml for
  559. // details calculate the radius of the frustum sphere
  560. Result.Frustum := AFrustum;
  561. // ************
  562. // Create a bounding sphere for the entire frustum - only bspheres that
  563. // intersect this bounding sphere can in turn intersect the frustum
  564. ViewLen := AFarDist - ANearDist;
  565. // use some trig to find the height of the frustum at the far plane
  566. Height := ViewLen * Sin(AFieldOfViewRadians / 2); // was tan( !?
  567. // with an aspect ratio of 1, the width will be the same
  568. Width := Height;
  569. // halfway point between near/far planes starting at the origin and extending along the z axis
  570. P := AffineVectorMake(0, 0, ANearDist + ViewLen / 2);
  571. // the calculate far corner of the frustum
  572. Q := AffineVectorMake(Width, Height, ViewLen);
  573. // the vector between P and Q
  574. VDiff := VectorSubtract(P, Q);
  575. // the radius becomes the length of this vector
  576. Result.BSphere.Radius := VectorLength(VDiff);
  577. // calculate the center of the sphere
  578. Result.BSphere.Center := VectorAdd(ACameraPosition,
  579. VectorScale(ALookVector, ViewLen / 2 + ANearDist));
  580. // ************
  581. // Create a cone
  582. // calculate the length of the fov triangle
  583. { Depth := AScreenHeight / tan(AFieldOfViewRadians / 2);
  584. // calculate the corner of the screen
  585. Corner := sqrt(AScreenHeight * AScreenHeight + AScreenWidth * AScreenWidth);
  586. // now calculate the new fov
  587. NewFov := ArcTan2(Corner, Depth);
  588. // apply to the cone
  589. result.SPCone.Axis := ALookVector;
  590. result.SPCone.Base := ACameraPosition;
  591. result.SPCone.Angle := NewFov; // }
  592. end;
  593. { TSpacePartitionLeaf }
  594. procedure TSpacePartitionLeaf.UpdateCachedAABBAndBSphere;
  595. begin
  596. // You MUST override TSpacePartitionLeaf.UpdateCachedAABBAndBSphere, if you
  597. // only have easy access to a bounding sphere, or only an axis aligned
  598. // bounding box, you can easily convert from one to the other by using
  599. // AABBToBSphere and BSphereToAABB.
  600. //
  601. // You MUST set both FCachedAABB AND FCachedBSphere
  602. Assert(False,
  603. 'You MUST override TSpacePartitionLeaf.UpdateCachedAABBAndBSphere!');
  604. end;
  605. procedure TSpacePartitionLeaf.Changed;
  606. begin
  607. UpdateCachedAABBAndBSphere;
  608. SpacePartition.LeafChanged(Self);
  609. end;
  610. constructor TSpacePartitionLeaf.CreateOwned(SpacePartition
  611. : TBaseSpacePartition);
  612. begin
  613. inherited Create;
  614. FSpacePartition := SpacePartition;
  615. if SpacePartition <> nil then
  616. SpacePartition.AddLeaf(Self);
  617. end;
  618. destructor TSpacePartitionLeaf.Destroy;
  619. begin
  620. if Assigned(FSpacePartition) then
  621. FSpacePartition.RemoveLeaf(Self);
  622. inherited;
  623. end;
  624. procedure TSpacePartitionLeaf.SetSpacePartition(const Value
  625. : TBaseSpacePartition);
  626. begin
  627. if Assigned(FSpacePartition) then
  628. FSpacePartition.RemoveLeaf(Self);
  629. FSpacePartition := Value;
  630. if Assigned(FSpacePartition) then
  631. FSpacePartition.AddLeaf(Self);
  632. end;
  633. { TSpacePartitionLeafList }
  634. constructor TSpacePartitionLeafList.Create;
  635. begin
  636. inherited;
  637. GrowthDelta := 128;
  638. end;
  639. function TSpacePartitionLeafList.GetItems(I: Integer): TSpacePartitionLeaf;
  640. begin
  641. Result := TSpacePartitionLeaf(Get(I));
  642. end;
  643. procedure TSpacePartitionLeafList.SetItems(I: Integer;
  644. const Value: TSpacePartitionLeaf);
  645. begin
  646. Put(I, Value);
  647. end;
  648. { TBaseSpacePartition }
  649. procedure TBaseSpacePartition.AddLeaf(ALeaf: TSpacePartitionLeaf);
  650. begin
  651. // Virtual
  652. ALeaf.UpdateCachedAABBAndBSphere;
  653. end;
  654. procedure TBaseSpacePartition.Clear;
  655. begin
  656. // Virtual
  657. end;
  658. constructor TBaseSpacePartition.Create;
  659. begin
  660. inherited;
  661. FQueryResult := TSpacePartitionLeafList.Create
  662. end;
  663. destructor TBaseSpacePartition.Destroy;
  664. begin
  665. FreeAndNil(FQueryResult);
  666. inherited;
  667. end;
  668. procedure TBaseSpacePartition.FlushQueryResult;
  669. begin
  670. FQueryResult.Count := 0;
  671. FQueryInterObjectTests := 0;
  672. end;
  673. procedure TBaseSpacePartition.LeafChanged(ALeaf: TSpacePartitionLeaf);
  674. begin
  675. // Virtual
  676. end;
  677. procedure TBaseSpacePartition.ProcessUpdated;
  678. begin
  679. // Virtual
  680. end;
  681. function TBaseSpacePartition.QueryAABB(const AAABB: TAABB): Integer;
  682. begin
  683. // Virtual
  684. Result := 0;
  685. end;
  686. function TBaseSpacePartition.QueryBSphere(const ABSphere: TBSphere): Integer;
  687. begin
  688. // Virtual
  689. Result := 0;
  690. end;
  691. function TBaseSpacePartition.QueryCone(const ACone: TSPCone): Integer;
  692. begin
  693. // Virtual
  694. Result := 0;
  695. end;
  696. function TBaseSpacePartition.QueryPlane(const Location,
  697. Normal: TAffineVector): Integer;
  698. begin
  699. // Virtual
  700. Result := 0;
  701. end;
  702. function TBaseSpacePartition.QueryLeaf(const ALeaf
  703. : TSpacePartitionLeaf): Integer;
  704. begin
  705. QueryBSphere(ALeaf.FCachedBSphere);
  706. // Remove self if it was included (it should have been)
  707. FQueryResult.Remove(ALeaf);
  708. Result := FQueryResult.Count;
  709. end;
  710. procedure TBaseSpacePartition.RemoveLeaf(ALeaf: TSpacePartitionLeaf);
  711. begin
  712. // Virtual
  713. end;
  714. function TBaseSpacePartition.QueryFrustum(const Frustum: TFrustum): Integer;
  715. begin
  716. // Virtual
  717. Result := 0;
  718. end;
  719. function TBaseSpacePartition.QueryFrustumEx(const ExtendedFrustum
  720. : TExtendedFrustum): Integer;
  721. begin
  722. // Virtual
  723. Result := 0;
  724. end;
  725. { TLeavedSpacePartition }
  726. procedure TLeavedSpacePartition.AddLeaf(ALeaf: TSpacePartitionLeaf);
  727. begin
  728. FLeaves.Add(ALeaf);
  729. ALeaf.UpdateCachedAABBAndBSphere;
  730. end;
  731. procedure TLeavedSpacePartition.Clear;
  732. var
  733. I: Integer;
  734. begin
  735. inherited;
  736. for I := 0 to FLeaves.Count - 1 do
  737. begin
  738. FLeaves[I].FSpacePartition := nil;
  739. FLeaves[I].Free;
  740. end;
  741. FLeaves.Clear;
  742. end;
  743. constructor TLeavedSpacePartition.Create;
  744. begin
  745. inherited;
  746. FLeaves := TSpacePartitionLeafList.Create;
  747. end;
  748. destructor TLeavedSpacePartition.Destroy;
  749. begin
  750. Clear;
  751. FreeAndNil(FLeaves);
  752. inherited;
  753. end;
  754. procedure TLeavedSpacePartition.RemoveLeaf(ALeaf: TSpacePartitionLeaf);
  755. begin
  756. FLeaves.Remove(ALeaf);
  757. end;
  758. function TLeavedSpacePartition.QueryAABB(const AAABB: TAABB): Integer;
  759. var
  760. I: Integer;
  761. begin
  762. // Very brute force!
  763. FlushQueryResult;
  764. for I := 0 to Leaves.Count - 1 do
  765. begin
  766. Inc(FQueryInterObjectTests);
  767. if IntersectAABBsAbsolute(AAABB, Leaves[I].FCachedAABB) then
  768. FQueryResult.Add(Leaves[I]);
  769. end;
  770. Result := FQueryResult.Count;
  771. end;
  772. function TLeavedSpacePartition.QueryBSphere(const ABSphere: TBSphere): Integer;
  773. var
  774. I: Integer;
  775. Distance2: Single;
  776. Leaf: TSpacePartitionLeaf;
  777. begin
  778. // Very brute force!
  779. FlushQueryResult;
  780. for I := 0 to Leaves.Count - 1 do
  781. begin
  782. Leaf := Leaves[I];
  783. Distance2 := VectorDistance2(Leaf.FCachedBSphere.Center, ABSphere.Center);
  784. Inc(FQueryInterObjectTests);
  785. if Distance2 < Sqr(Leaf.FCachedBSphere.Radius + ABSphere.Radius) then
  786. FQueryResult.Add(Leaf);
  787. end;
  788. Result := FQueryResult.Count;
  789. end;
  790. function TLeavedSpacePartition.QueryCone(const ACone: TSPCone): Integer;
  791. var
  792. I: Integer;
  793. begin
  794. // Very brute force!
  795. FlushQueryResult;
  796. for I := 0 to Leaves.Count - 1 do
  797. begin
  798. Inc(FQueryInterObjectTests);
  799. if ConeContainsBSphere(ACone, Leaves[I].FCachedBSphere) <> ScNoOverlap then
  800. FQueryResult.Add(Leaves[I]);
  801. end;
  802. Result := FQueryResult.Count;
  803. end;
  804. function TLeavedSpacePartition.QueryPlane(const FLocation,
  805. FNormal: TAffineVector): Integer;
  806. var
  807. I: Integer;
  808. CurrentPenetrationDepth: Single;
  809. Leaf: TSpacePartitionLeaf;
  810. begin
  811. // Very brute force!
  812. FlushQueryResult;
  813. for I := 0 to Leaves.Count - 1 do
  814. begin
  815. Inc(FQueryInterObjectTests);
  816. Leaf := Leaves[I];
  817. CurrentPenetrationDepth := -(PointPlaneDistance(Leaf.FCachedBSphere.Center,
  818. FLocation, FNormal) - Leaf.FCachedBSphere.Radius);
  819. // Correct the node location
  820. if CurrentPenetrationDepth > 0 then
  821. FQueryResult.Add(Leaves[I]);
  822. end; // }
  823. Result := FQueryResult.Count;
  824. end;
  825. { TSectorNode }
  826. function TSectorNode.AABBFitsInNode(const AAABB: TAABB): Boolean;
  827. begin
  828. Result := ContainsAABB(AAABB) in [ScContainsFully];
  829. end;
  830. function TSectorNode.AABBIntersectsNode(const AAABB: TAABB): Boolean;
  831. begin
  832. Result := ContainsAABB(AAABB) in [ScContainsPartially, ScContainsFully];
  833. end;
  834. procedure TSectorNode.AddAllLeavesRecursive(const QueryResult
  835. : TSpacePartitionLeafList);
  836. var
  837. I: Integer;
  838. begin
  839. for I := 0 to FLeaves.Count - 1 do
  840. QueryResult.Add(FLEaves[I]);
  841. for I := 0 to FChildCount - 1 do
  842. FChildren[I].AddAllLeavesRecursive(QueryResult);
  843. end;
  844. function TSectorNode.AddLeaf(ALeaf: TSpacePartitionLeaf): TSectorNode;
  845. begin
  846. // Time to grow the node?
  847. if NoChildren and (FLeaves.Count >= FSectoredSpacePartition.FLeafThreshold)
  848. and (FNodeDepth < FSectoredSpacePartition.FMaxTreeDepth) then
  849. begin
  850. ExpandNode;
  851. end;
  852. Inc(FRecursiveLeafCount);
  853. if NoChildren then
  854. begin
  855. FLeaves.Add(ALeaf);
  856. ChildrenChanged;
  857. ALeaf.FPartitionTag := Self;
  858. Result := Self;
  859. end
  860. else
  861. begin
  862. // Does it fit completely in any of the children?
  863. Result := PlaceLeafInChild(ALeaf);
  864. end;
  865. end;
  866. function TSectorNode.BSphereFitsInNode(const BSphere: TBSphere): Boolean;
  867. begin
  868. Result := ContainsBSphere(BSphere) in [ScContainsFully];
  869. end;
  870. function TSectorNode.BSphereIntersectsNode(const BSphere: TBSphere): Boolean;
  871. begin
  872. Result := ContainsBSphere(BSphere) in [ScContainsPartially, ScContainsFully];
  873. end;
  874. function TSectorNode.CalcRecursiveLeafCount: Integer;
  875. var
  876. I: Integer;
  877. begin
  878. Result := FLeaves.Count;
  879. for I := 0 to FChildCount - 1 do
  880. Result := Result + FChildren[I].CalcRecursiveLeafCount;
  881. end;
  882. procedure TSectorNode.Clear;
  883. var
  884. I: Integer;
  885. begin
  886. for I := 0 to FChildCount - 1 do
  887. FreeAndNil(FChildren[I]);
  888. FChildCount := 0;
  889. FLeaves.Clear;
  890. end;
  891. constructor TSectorNode.Create(ASectoredSpacePartition: TSectoredSpacePartition;
  892. AParent: TSectorNode);
  893. begin
  894. FLeaves := TSpacePartitionLeafList.Create;
  895. FChildCount := 0;
  896. FParent := AParent;
  897. FSectoredSpacePartition := ASectoredSpacePartition;
  898. if AParent = nil then
  899. FNodeDepth := 0
  900. else
  901. FNodeDepth := AParent.FNodeDepth + 1;
  902. end;
  903. procedure TSectorNode.ExpandNode;
  904. var
  905. I: Integer;
  906. OldLeaves: TSpacePartitionLeafList;
  907. begin
  908. CreateChildren;
  909. // The children have been added, now move all leaves to the children - if
  910. // we can
  911. OldLeaves := FLeaves;
  912. try
  913. FLeaves := TSpacePartitionLeafList.Create;
  914. for I := 0 to OldLeaves.Count - 1 do
  915. PlaceLeafInChild(OldLeaves[I]);
  916. finally
  917. OldLeaves.Free;
  918. end;
  919. end;
  920. procedure TSectorNode.CollapseNode;
  921. var
  922. I, J: Integer;
  923. begin
  924. for I := 0 to FChildCount - 1 do
  925. begin
  926. FChildren[I].CollapseNode;
  927. for J := 0 to FChildren[I].FLeaves.Count - 1 do
  928. begin
  929. FChildren[I].FLeaves[J].FPartitionTag := Self;
  930. FLeaves.Add(FChildren[I].FLeaves[J]);
  931. end;
  932. FChildren[I].FLeaves.Clear;
  933. FreeAndNil(FChildren[I]);
  934. end;
  935. FChildCount := 0;
  936. end;
  937. destructor TSectorNode.Destroy;
  938. begin
  939. Clear;
  940. FreeAndNil(FLeaves);
  941. inherited;
  942. end;
  943. function TSectorNode.GetNoChildren: Boolean;
  944. begin
  945. Result := FChildCount = 0;
  946. end;
  947. function TSectorNode.GetNodeCount: Integer;
  948. var
  949. I: Integer;
  950. begin
  951. Result := 1;
  952. for I := 0 to FChildCount - 1 do
  953. Result := Result + FChildren[I].GetNodeCount;
  954. end;
  955. function TSectorNode.PlaceLeafInChild(ALeaf: TSpacePartitionLeaf): TSectorNode;
  956. var
  957. ChildNode: TSectorNode;
  958. begin
  959. // Which child does it fit in?
  960. ChildNode := GetChildForAABB(ALeaf.FCachedAABB);
  961. if ChildNode <> nil then
  962. begin
  963. Result := ChildNode.AddLeaf(ALeaf);
  964. Exit;
  965. end; // }
  966. // Doesn't fit the any child
  967. ALeaf.FPartitionTag := Self;
  968. FLeaves.Add(ALeaf);
  969. ChildrenChanged;
  970. Result := Self;
  971. end;
  972. procedure TSectorNode.QueryAABB(const AAABB: TAABB;
  973. const QueryResult: TSpacePartitionLeafList);
  974. var
  975. I: Integer;
  976. SpaceContains: TSpaceContains;
  977. begin
  978. Inc(FSectoredSpacePartition.FQueryNodeTests);
  979. SpaceContains := AABBContainsSector(AAABB);
  980. if SpaceContains = ScContainsFully then
  981. begin
  982. AddAllLeavesRecursive(QueryResult);
  983. end
  984. else if SpaceContains = ScContainsPartially then
  985. begin
  986. // Add all leaves that overlap
  987. if FSectoredSpacePartition.CullingMode = CmFineCulling then
  988. begin
  989. for I := 0 to FLeaves.Count - 1 do
  990. begin
  991. Inc(FSectoredSpacePartition.FQueryInterObjectTests);
  992. if IntersectAABBsAbsolute(FLeaves[I].FCachedAABB, AAABB) then
  993. QueryResult.Add(FLeaves[I]);
  994. end;
  995. end
  996. else
  997. begin
  998. for I := 0 to FLeaves.Count - 1 do
  999. QueryResult.Add(FLeaves[I]);
  1000. end;
  1001. // Recursively let the children add their leaves
  1002. for I := 0 to FChildCount - 1 do
  1003. FChildren[I].QueryAABB(AAABB, QueryResult);
  1004. end;
  1005. end;
  1006. procedure TSectorNode.QueryBSphere(const ABSphere: TBSphere;
  1007. const QueryResult: TSpacePartitionLeafList);
  1008. var
  1009. I: Integer;
  1010. SpaceContains: TSpaceContains;
  1011. begin
  1012. Inc(FSectoredSpacePartition.FQueryNodeTests);
  1013. SpaceContains := BSphereContainsSector(ABSphere);
  1014. if SpaceContains = ScContainsFully then
  1015. begin
  1016. AddAllLeavesRecursive(QueryResult);
  1017. end
  1018. else if SpaceContains = ScContainsPartially then
  1019. begin
  1020. // Add all leaves that overlap
  1021. if FSectoredSpacePartition.CullingMode = CmFineCulling then
  1022. begin
  1023. for I := 0 to FLeaves.Count - 1 do
  1024. begin
  1025. Inc(FSectoredSpacePartition.FQueryInterObjectTests);
  1026. if BSphereContainsAABB(ABSphere, FLeaves[I].FCachedAABB) <> ScNoOverlap
  1027. then
  1028. QueryResult.Add(FLeaves[I]);
  1029. end;
  1030. end
  1031. else
  1032. for I := 0 to FLeaves.Count - 1 do
  1033. QueryResult.Add(FLeaves[I]);
  1034. // Recursively let the children add their leaves
  1035. for I := 0 to FChildCount - 1 do
  1036. FChildren[I].QueryBSphere(ABSphere, QueryResult);
  1037. end;
  1038. end;
  1039. procedure TSectorNode.QueryPlane(const Location, Normal: TAffineVector;
  1040. const QueryResult: TSpacePartitionLeafList);
  1041. var
  1042. I: Integer;
  1043. SpaceContains: TSpaceContains;
  1044. begin
  1045. Inc(FSectoredSpacePartition.FQueryNodeTests);
  1046. SpaceContains := PlaneContainsBSphere(Location, Normal, FBSphere);
  1047. if SpaceContains = ScContainsFully then
  1048. begin
  1049. AddAllLeavesRecursive(QueryResult);
  1050. end
  1051. else if SpaceContains = ScContainsPartially then
  1052. begin
  1053. // Add all leaves that overlap
  1054. if FSectoredSpacePartition.CullingMode = CmFineCulling then
  1055. begin
  1056. for I := 0 to FLeaves.Count - 1 do
  1057. if PlaneContainsBSphere(Location, Normal, FLeaves[I].FCachedBSphere) <> ScNoOverlap
  1058. then
  1059. QueryResult.Add(FLeaves[I]);
  1060. end
  1061. else
  1062. for I := 0 to FLeaves.Count - 1 do
  1063. QueryResult.Add(FLeaves[I]);
  1064. // Recursively let the children add their leaves
  1065. for I := 0 to FChildCount - 1 do
  1066. begin
  1067. Inc(FSectoredSpacePartition.FQueryInterObjectTests);
  1068. FChildren[I].QueryPlane(Location, Normal, QueryResult);
  1069. end;
  1070. end; // }
  1071. end;
  1072. function TSectorNode.RemoveLeaf(ALeaf: TSpacePartitionLeaf;
  1073. OwnerByThis: Boolean): Boolean;
  1074. begin
  1075. Result := False;
  1076. Dec(FRecursiveLeafCount);
  1077. if OwnerByThis then
  1078. begin
  1079. ALeaf.FPartitionTag := nil;
  1080. FLeaves.Remove(ALeaf);
  1081. ChildrenChanged;
  1082. end;
  1083. // If there aren't enough leaves anymore, it's time to remove the node!
  1084. if not NoChildren and
  1085. (FRecursiveLeafCount + 1 < FSectoredSpacePartition.FLeafThreshold) then
  1086. begin
  1087. CollapseNode;
  1088. Result := True;
  1089. end;
  1090. if Parent <> nil then
  1091. Parent.RemoveLeaf(ALeaf, False);
  1092. end;
  1093. function TSectorNode.VerifyRecursiveLeafCount: string;
  1094. var
  1095. I: Integer;
  1096. begin
  1097. if FRecursiveLeafCount <> CalcRecursiveLeafCount then
  1098. begin
  1099. Result := Format('Node at depth %d mismatches, %d<>%d!',
  1100. [FNodeDepth, FRecursiveLeafCount, CalcRecursiveLeafCount]);
  1101. Exit;
  1102. end;
  1103. for I := 0 to FChildCount - 1 do
  1104. begin
  1105. Result := FChildren[I].VerifyRecursiveLeafCount;
  1106. if Result <> '' then
  1107. Exit;
  1108. end;
  1109. end;
  1110. procedure TSectorNode.CreateChildren;
  1111. begin
  1112. Assert(False, 'You must override CreateChildren!');
  1113. end;
  1114. function TSectorNode.AABBContainsSector(const AABB: TAABB): TSpaceContains;
  1115. begin
  1116. Result := AABBContainsAABB(AABB, FAABB);
  1117. end;
  1118. function TSectorNode.BSphereContainsSector(const BSphere: TBSphere)
  1119. : TSpaceContains;
  1120. begin
  1121. Result := BSphereContainsAABB(BSphere, FAABB);
  1122. end;
  1123. function TSectorNode.ContainsAABB(const AAABB: TAABB): TSpaceContains;
  1124. begin
  1125. Result := AABBContainsAABB(FAABB, AAABB);
  1126. end;
  1127. function TSectorNode.ContainsBSphere(const ABSphere: TBSphere): TSpaceContains;
  1128. begin
  1129. Result := AABBContainsBSphere(FAABB, ABSphere);
  1130. end;
  1131. procedure TSectorNode.SetAABB(const Value: TAABB);
  1132. begin
  1133. FAABB := Value;
  1134. AABBToBSphere(FAABB, FBSphere);
  1135. end;
  1136. function TSectorNode.GetChildForAABB(AABB: TAABB): TSectorNode;
  1137. var
  1138. Location: TAffineVector;
  1139. ChildNode: TSectorNode;
  1140. ChildNodeIndex: Integer;
  1141. begin
  1142. Assert(FChildCount > 0, 'There are no children in this node!');
  1143. // Instead of looping through all children, we simply determine on which
  1144. // side of the center node the child is located
  1145. ChildNodeIndex := 0;
  1146. Location := AABB.Min;
  1147. // Upper / Lower
  1148. if Location.Y < FBSphere.Center.Y then
  1149. ChildNodeIndex := 4;
  1150. // Left / Right
  1151. if Location.Z < FBSphere.Center.Z then
  1152. ChildNodeIndex := ChildNodeIndex or 2;
  1153. // Fore / Back
  1154. if Location.X > FBSphere.Center.X then
  1155. ChildNodeIndex := ChildNodeIndex or 1;
  1156. Assert((ChildNodeIndex >= 0) and (ChildNodeIndex <= 8),
  1157. Format('ChildNodeIndex is out of range (%d)!', [ChildNodeIndex]));
  1158. ChildNode := FChildren[ChildNodeIndex];
  1159. Assert(Assigned(ChildNode), 'ChildNode not assigned');
  1160. if ChildNode.AABBFitsInNode(AABB) then
  1161. Result := ChildNode
  1162. else
  1163. Result := nil;
  1164. end;
  1165. function TSectorNode.GetCenter: TAffineVector;
  1166. begin
  1167. Result := FBSphere.Center;
  1168. end;
  1169. procedure TSectorNode.QueryFrustum(const Frustum: TFrustum;
  1170. const QueryResult: TSpacePartitionLeafList);
  1171. var
  1172. SpaceContains: TSpaceContains;
  1173. I: Integer;
  1174. begin
  1175. Inc(FSectoredSpacePartition.FQueryNodeTests);
  1176. // Check if the frustum contains the bsphere of the node
  1177. if not IsVolumeClipped(BSphere.Center, BSphere.Radius, Frustum) then
  1178. SpaceContains := FrustumContainsAABB(Frustum, AABB)
  1179. else
  1180. SpaceContains := ScNoOverlap;
  1181. // If the frustum fully contains the leaf, then we need not check every piece,
  1182. // just add them all
  1183. if SpaceContains = ScContainsFully then
  1184. begin
  1185. AddAllLeavesRecursive(QueryResult);
  1186. end
  1187. else
  1188. // If the frustum partiall contains the leaf, then we should add the leaves
  1189. // that intersect the frustum and recurse for all children
  1190. if SpaceContains = ScContainsPartially then
  1191. begin
  1192. for I := 0 to FLeaves.Count - 1 do
  1193. begin
  1194. Inc(FSectoredSpacePartition.FQueryInterObjectTests);
  1195. if not IsVolumeClipped(FLeaves[I].FCachedBSphere.Center,
  1196. FLeaves[I].FCachedBSphere.Radius, Frustum) then
  1197. QueryResult.Add(FLeaves[I]);
  1198. end;
  1199. // Recursively let the children add their leaves
  1200. for I := 0 to FChildCount - 1 do
  1201. FChildren[I].QueryFrustum(Frustum, QueryResult);
  1202. end;
  1203. end;
  1204. procedure TSectorNode.ChildrenChanged;
  1205. begin
  1206. // Do nothing in the basic case
  1207. end;
  1208. procedure TSectorNode.QueryFrustumEx(const ExtendedFrustum: TExtendedFrustum;
  1209. const QueryResult: TSpacePartitionLeafList);
  1210. var
  1211. SpaceContains: TSpaceContains;
  1212. I: Integer;
  1213. begin
  1214. Inc(FSectoredSpacePartition.FQueryNodeTests);
  1215. // Does the extended frustum intersect the bsphere at all?
  1216. if not ExtendedFrustumIntersectsBSphere(ExtendedFrustum, BSphere) then
  1217. SpaceContains := ScNoOverlap
  1218. else
  1219. // Test if the bounding frustum intersects the AABB of the node
  1220. SpaceContains := FrustumContainsAABB(ExtendedFrustum.Frustum, AABB); // }
  1221. // If the frustum fully contains the leaf, then we need not check every piece,
  1222. // just add them all
  1223. if SpaceContains = ScContainsFully then
  1224. begin
  1225. AddAllLeavesRecursive(QueryResult);
  1226. end
  1227. else
  1228. // If the frustum partially contains the leaf, then we should add the leaves
  1229. // that intersect the frustum and recurse for all children
  1230. if SpaceContains = ScContainsPartially then
  1231. begin
  1232. for I := 0 to FLeaves.Count - 1 do
  1233. begin
  1234. // Early out 1
  1235. if not BSphereIntersectsBSphere(FLeaves[I].FCachedBSphere,
  1236. ExtendedFrustum.BSphere) then
  1237. Continue;
  1238. Inc(FSectoredSpacePartition.FQueryInterObjectTests);
  1239. if not IsVolumeClipped(FLeaves[I].FCachedBSphere.Center,
  1240. FLeaves[I].FCachedBSphere.Radius, ExtendedFrustum.Frustum) then
  1241. QueryResult.Add(FLeaves[I]);
  1242. end;
  1243. // Recursively let the children add their leaves
  1244. for I := 0 to FChildCount - 1 do
  1245. FChildren[I].QueryFrustumEx(ExtendedFrustum, QueryResult);
  1246. end;
  1247. end;
  1248. { TSectoredSpacePartition }
  1249. procedure TSectoredSpacePartition.AddLeaf(ALeaf: TSpacePartitionLeaf);
  1250. begin
  1251. inherited;
  1252. FRootNode.AddLeaf(ALeaf);
  1253. if not FRootNode.AABBFitsInNode(ALeaf.FCachedAABB) then
  1254. begin
  1255. if FGrowMethod in [gmBestFit, gmIncreaseToFitAll] then
  1256. UpdateStructureSize(GrowGravy)
  1257. else
  1258. Assert(False, 'Node is outside Octree!');
  1259. end;
  1260. end;
  1261. procedure TSectoredSpacePartition.Clear;
  1262. begin
  1263. inherited Clear;
  1264. if Assigned(FRootNode) then
  1265. FRootNode.Clear;
  1266. end;
  1267. constructor TSectoredSpacePartition.Create;
  1268. begin
  1269. FLeafThreshold := COctree_LEAF_TRHESHOLD;
  1270. FMaxTreeDepth := COctree_MAX_TREE_DEPTH;
  1271. FRootNode := CreateNewNode(nil);
  1272. FGrowMethod := gmIncreaseToFitAll;
  1273. FGrowGravy := COctree_GROW_GRAVY;
  1274. inherited Create;
  1275. end;
  1276. function TSectoredSpacePartition.CreateNewNode(AParent: TSectorNode)
  1277. : TSectorNode;
  1278. begin
  1279. Result := TSectorNode.Create(Self, AParent);
  1280. end;
  1281. destructor TSectoredSpacePartition.Destroy;
  1282. begin
  1283. inherited Destroy;
  1284. FRootNode.Free;
  1285. end;
  1286. function TSectoredSpacePartition.GetAABB: TAABB;
  1287. var
  1288. I: Integer;
  1289. begin
  1290. if FLeaves.Count = 0 then
  1291. begin
  1292. MakeVector(Result.Min, 0, 0, 0);
  1293. MakeVector(Result.Max, 0, 0, 0);
  1294. end
  1295. else
  1296. begin
  1297. Result := FLeaves[0].FCachedAABB;
  1298. for I := 1 to FLeaves.Count - 1 do
  1299. AddAABB(Result, FLeaves[I].FCachedAABB);
  1300. end;
  1301. end;
  1302. function TSectoredSpacePartition.GetNodeCount: Integer;
  1303. begin
  1304. Result := FRootNode.GetNodeCount;
  1305. end;
  1306. procedure TSectoredSpacePartition.LeafChanged(ALeaf: TSpacePartitionLeaf);
  1307. var
  1308. Node: TSectorNode;
  1309. begin
  1310. // If the leaf still fits in the old node, leave it there - or in one of the
  1311. // children
  1312. Node := TSectorNode(ALeaf.FPartitionTag);
  1313. Assert(Node <> nil, 'No leaf node could be found!');
  1314. if Node.AABBFitsInNode(ALeaf.FCachedAABB) then
  1315. begin
  1316. // If the node has children, try to add the leaf to them - otherwise just
  1317. // leave it!
  1318. if Node.FChildCount > 0 then
  1319. begin
  1320. Node.FLeaves.Remove(ALeaf);
  1321. Node.PlaceLeafInChild(ALeaf);
  1322. Node.ChildrenChanged;
  1323. end;
  1324. end
  1325. else
  1326. begin
  1327. Node.RemoveLeaf(ALeaf, True);
  1328. // Does this leaf still fit in the Octree?
  1329. if not FRootNode.AABBFitsInNode(ALeaf.FCachedAABB) then
  1330. begin
  1331. if FGrowMethod in [gmBestFit, gmIncreaseToFitAll] then
  1332. UpdateStructureSize(COctree_GROW_GRAVY)
  1333. else
  1334. Assert(False, 'Node is outside Octree!');
  1335. end
  1336. else
  1337. FRootNode.AddLeaf(ALeaf);
  1338. end;
  1339. end;
  1340. function TSectoredSpacePartition.QueryAABB(const AAABB: TAABB): Integer;
  1341. begin
  1342. FlushQueryResult;
  1343. FRootNode.QueryAABB(AAABB, FQueryResult);
  1344. Result := FQueryResult.Count;
  1345. end;
  1346. function TSectoredSpacePartition.QueryBSphere(const ABSphere: TBSphere)
  1347. : Integer;
  1348. begin
  1349. FlushQueryResult;
  1350. FRootNode.QueryBSphere(ABSphere, FQueryResult);
  1351. Result := FQueryResult.Count;
  1352. end;
  1353. function TSectoredSpacePartition.QueryPlane(const Location,
  1354. Normal: TAffineVector): Integer;
  1355. begin
  1356. FlushQueryResult;
  1357. FRootNode.QueryPlane(Location, Normal, FQueryResult);
  1358. Result := FQueryResult.Count;
  1359. end;
  1360. function TSectoredSpacePartition.QueryLeaf(const ALeaf
  1361. : TSpacePartitionLeaf): Integer;
  1362. var
  1363. I: Integer;
  1364. Node: TSectorNode;
  1365. TestLeaf: TSpacePartitionLeaf;
  1366. begin
  1367. // Query current node and all nodes upwards until we find the root, no need
  1368. // to check intersections, because we know that the leaf partially intersects
  1369. // all it's parents.
  1370. Node := TSectorNode(ALeaf.FPartitionTag);
  1371. FlushQueryResult;
  1372. // First, query downwards!
  1373. Node.QueryAABB(ALeaf.FCachedAABB, QueryResult);
  1374. // Now, query parents and upwards!
  1375. Node := Node.Parent;
  1376. while Node <> nil do
  1377. begin
  1378. Inc(FQueryNodeTests);
  1379. // Add all leaves that overlap
  1380. for I := 0 to Node.FLeaves.Count - 1 do
  1381. begin
  1382. TestLeaf := Node.FLeaves[I];
  1383. Inc(FQueryInterObjectTests);
  1384. if IntersectAABBsAbsolute(TestLeaf.FCachedAABB, ALeaf.FCachedAABB) then
  1385. QueryResult.Add(TestLeaf);
  1386. end;
  1387. // Try the parent
  1388. Node := Node.Parent;
  1389. end;
  1390. QueryResult.Remove(ALeaf);
  1391. Result := QueryResult.Count;
  1392. end;
  1393. procedure TSectoredSpacePartition.RemoveLeaf(ALeaf: TSpacePartitionLeaf);
  1394. begin
  1395. inherited;
  1396. TSectorNode(ALeaf.FPartitionTag).RemoveLeaf(ALeaf, True);
  1397. end;
  1398. procedure TSectoredSpacePartition.SetLeafThreshold(const Value: Integer);
  1399. begin
  1400. FLeafThreshold := Value;
  1401. end;
  1402. procedure TSectoredSpacePartition.SetMaxTreeDepth(const Value: Integer);
  1403. begin
  1404. FMaxTreeDepth := Value;
  1405. end;
  1406. procedure TSectoredSpacePartition.RebuildTree(const NewAABB: TAABB);
  1407. var
  1408. I: Integer;
  1409. OldLeaves: TSpacePartitionLeafList;
  1410. TempGrowMethod: TGrowMethod;
  1411. begin
  1412. // Delete ALL nodes in the tree
  1413. FRootNode.Free;
  1414. FRootNode := CreateNewNode(nil);
  1415. FRootNode.AABB := NewAABB;
  1416. // Insert all nodes again
  1417. OldLeaves := FLeaves;
  1418. FLeaves := TSpacePartitionLeafList.Create;
  1419. // This will cause an except if the build goes badly, which is better than
  1420. // an infinite loop
  1421. TempGrowMethod := FGrowMethod;
  1422. for I := 0 to OldLeaves.Count - 1 do
  1423. AddLeaf(OldLeaves[I]);
  1424. OldLeaves.Free;
  1425. FGrowMethod := TempGrowMethod;
  1426. end;
  1427. procedure TSectoredSpacePartition.UpdateStructureSize(Gravy: Single);
  1428. var
  1429. MaxAABB, NewAABB: TAABB;
  1430. AABBSize: TAffineVector;
  1431. begin
  1432. // Create the new extents for the Octree
  1433. MaxAABB := GetAABB;
  1434. AABBSize := VectorSubtract(MaxAABB.Max, MaxAABB.Min);
  1435. if FGrowMethod = gmBestFit then
  1436. begin
  1437. NewAABB.Min := VectorSubtract(MaxAABB.Min, VectorScale(AABBSize, Gravy));
  1438. NewAABB.Max := VectorAdd(MaxAABB.Max, VectorScale(AABBSize, Gravy)); // }
  1439. end
  1440. else if FGrowMethod = gmIncreaseToFitAll then
  1441. begin
  1442. NewAABB.Min := VectorSubtract(MaxAABB.Min, VectorScale(AABBSize, Gravy));
  1443. NewAABB.Max := VectorAdd(MaxAABB.Max, VectorScale(AABBSize, Gravy)); // }
  1444. AddAABB(NewAABB, FRootNode.AABB);
  1445. end;
  1446. RebuildTree(NewAABB);
  1447. end;
  1448. procedure TSectoredSpacePartition.FlushQueryResult;
  1449. begin
  1450. inherited;
  1451. FQueryNodeTests := 0;
  1452. end;
  1453. function TSectoredSpacePartition.QueryFrustum(const Frustum: TFrustum): Integer;
  1454. begin
  1455. FlushQueryResult;
  1456. FRootNode.QueryFrustum(Frustum, FQueryResult);
  1457. Result := FQueryResult.Count;
  1458. end;
  1459. function TSectoredSpacePartition.QueryFrustumEx(const ExtendedFrustum
  1460. : TExtendedFrustum): Integer;
  1461. begin
  1462. FlushQueryResult;
  1463. FRootNode.QueryFrustumEx(ExtendedFrustum, FQueryResult);
  1464. Result := FQueryResult.Count;
  1465. end;
  1466. { TSPOctreeNode }
  1467. function TSPOctreeNode.AABBFitsInNode(const AAABB: TAABB): Boolean;
  1468. begin
  1469. // Faster than inherited method
  1470. Result := AABBFitsInAABBAbsolute(AAABB, FAABB);
  1471. end;
  1472. function TSPOctreeNode.AABBIntersectsNode(const AAABB: TAABB): Boolean;
  1473. begin
  1474. // Faster than inherited method
  1475. Result := IntersectAABBsAbsolute(FAABB, AAABB);
  1476. end;
  1477. function TSPOctreeNode.BSphereFitsInNode(const BSphere: TBSphere): Boolean;
  1478. var
  1479. AABB: TAABB;
  1480. begin
  1481. // Faster than inherited method
  1482. BSphereToAABB(BSphere, AABB);
  1483. Result := AABBFitsInAABBAbsolute(AABB, FAABB); // }
  1484. end;
  1485. function TSPOctreeNode.BSphereIntersectsNode(const BSphere: TBSphere): Boolean;
  1486. var
  1487. AABB: TAABB;
  1488. begin
  1489. // Faster than inherited method
  1490. BSphereToAABB(BSphere, AABB);
  1491. Result := IntersectAABBsAbsolute(AABB, FAABB); // }
  1492. end;
  1493. procedure TSPOctreeNode.CreateChildren;
  1494. var
  1495. I: Integer;
  1496. AABB: TAABB;
  1497. function GetExtent(const Flags: array of Byte): TAffineVector;
  1498. var
  1499. N: Integer;
  1500. begin
  1501. for N := 0 to 2 do
  1502. begin
  1503. case Flags[N] of
  1504. CMIN:
  1505. Result.V[N] := FAABB.Min.V[N];
  1506. CMID:
  1507. Result.V[N] := (FAABB.Max.V[N] + FAABB.Min.V[N]) / 2;
  1508. CMAX:
  1509. Result.V[N] := FAABB.Max.V[N];
  1510. end;
  1511. end;
  1512. end;
  1513. begin
  1514. Assert(FChildCount = 0, 'Children allready exist!');
  1515. for I := 0 to 7 do
  1516. begin
  1517. FChildren[I] := FSectoredSpacePartition.CreateNewNode(Self);
  1518. // Generate new extents based on parent's extents
  1519. AABB.Min := GetExtent(COctFlagMIN[I]);
  1520. AABB.Max := GetExtent(COctFlagMax[I]);
  1521. FChildren[I].AABB := AABB;
  1522. end;
  1523. FChildCount := 8;
  1524. end;
  1525. { TOctreeSpacePartition }
  1526. function TOctreeSpacePartition.CreateNewNode(AParent: TSectorNode): TSectorNode;
  1527. begin
  1528. Result := TSPOctreeNode.Create(Self, AParent);
  1529. end;
  1530. procedure TOctreeSpacePartition.SetSize(const Min, Max: TAffineVector);
  1531. var
  1532. AABB: TAABB;
  1533. begin
  1534. AABB.Min := Min;
  1535. AABB.Max := Max;
  1536. RebuildTree(AABB);
  1537. end;
  1538. { TSPQuadtreeNode }
  1539. function TSPQuadtreeNode.AABBFitsInNode(const AAABB: TAABB): Boolean;
  1540. begin
  1541. Result := (AAABB.Min.X >= FAABB.Min.X) and
  1542. (AAABB.Min.Z >= FAABB.Min.Z) and
  1543. (AAABB.Max.X <= FAABB.Max.X) and
  1544. (AAABB.Max.Z <= FAABB.Max.Z);
  1545. end;
  1546. function TSPQuadtreeNode.AABBIntersectsNode(const AAABB: TAABB): Boolean;
  1547. begin
  1548. Assert(False, Format('AABBIntersectsNode not implemented on %s',
  1549. [ClassName]));
  1550. Result := False;
  1551. end;
  1552. function TSPQuadtreeNode.BSphereFitsInNode(const BSphere: TBSphere): Boolean;
  1553. begin
  1554. Assert(False, Format('BSphereFitsInNode not implemented on %s', [ClassName]));
  1555. Result := False;
  1556. end;
  1557. function TSPQuadtreeNode.BSphereIntersectsNode(const BSphere: TBSphere)
  1558. : Boolean;
  1559. begin
  1560. Assert(False, Format('BSphereIntersectsNode not implemented on %s',
  1561. [ClassName]));
  1562. Result := False;
  1563. end;
  1564. procedure TSPQuadtreeNode.ChildrenChanged;
  1565. var
  1566. I: Integer;
  1567. NewMin, NewMax: Single;
  1568. begin
  1569. inherited;
  1570. // Establish a baseline
  1571. if Leaves.Count > 0 then
  1572. begin
  1573. NewMin := Leaves[0].FCachedAABB.Min.Y;
  1574. NewMax := Leaves[0].FCachedAABB.Max.Y;
  1575. end
  1576. else
  1577. if FChildCount > 0 then
  1578. begin
  1579. NewMin := FChildren[0].AABB.Min.Y;
  1580. NewMax := FChildren[0].AABB.Max.Y;
  1581. end
  1582. else
  1583. begin
  1584. // This should never happen!
  1585. NewMin := 1E9;
  1586. NewMax := -1E9;
  1587. end;
  1588. for I := 0 to Leaves.Count - 1 do
  1589. begin
  1590. NewMin := Min(NewMin, Leaves[I].FCachedAABB.Min.Y);
  1591. NewMax := Max(NewMax, Leaves[I].FCachedAABB.Max.Y);
  1592. end;
  1593. for I := 0 to FChildCount - 1 do
  1594. begin
  1595. NewMin := Min(NewMin, FChildren[I].AABB.Min.Y);
  1596. NewMax := Max(NewMax, FChildren[I].AABB.Max.Y);
  1597. end;
  1598. if (AABB.Max.Y <> NewMax) and (AABB.Min.Y <> NewMin) then
  1599. begin
  1600. FAABB.Max.Y := NewMax;
  1601. FAABB.Min.Y := NewMin;
  1602. // Make sure the parent updates it's bounds as well
  1603. if Assigned(Parent) then
  1604. Parent.ChildrenChanged; // }
  1605. end;
  1606. end;
  1607. procedure TSPQuadtreeNode.CreateChildren;
  1608. var
  1609. ChildNodeIndex: Integer;
  1610. AABB: TAABB;
  1611. X, Z: Integer;
  1612. begin
  1613. for ChildNodeIndex := 0 to 3 do
  1614. begin
  1615. FChildren[ChildNodeIndex] := FSectoredSpacePartition.CreateNewNode(Self);
  1616. // Y is ignored so it's set to a very large number
  1617. AABB.Min.Y := FAABB.Min.Y;
  1618. AABB.Max.Y := FAABB.Max.Y;
  1619. // Generate new extents based on parent's extents
  1620. if ((ChildNodeIndex and 1) > 0) then
  1621. X := 1
  1622. else
  1623. X := 0;
  1624. if ((ChildNodeIndex and 2) > 0) then
  1625. Z := 1
  1626. else
  1627. Z := 0;
  1628. if X = 0 then
  1629. begin
  1630. AABB.Min.X := FAABB.Min.X + (FAABB.Max.X + FAABB.Min.X) / 2 * X;
  1631. AABB.Max.X := (FAABB.Max.X + FAABB.Min.X) / 2 * (1 + X);
  1632. end
  1633. else
  1634. begin
  1635. AABB.Min.X := (FAABB.Max.X + FAABB.Min.X) / 2;
  1636. AABB.Max.X := FAABB.Max.X;
  1637. end;
  1638. if Z = 0 then
  1639. begin
  1640. AABB.Min.Z := FAABB.Min.Z;
  1641. AABB.Max.Z := (FAABB.Max.Z + FAABB.Min.Z) / 2;
  1642. end
  1643. else
  1644. begin
  1645. AABB.Min.Z := (FAABB.Max.Z + FAABB.Min.Z) / 2;
  1646. AABB.Max.Z := FAABB.Max.Z;
  1647. end;
  1648. FChildren[ChildNodeIndex].AABB := AABB;
  1649. end;
  1650. FChildCount := 4;
  1651. end;
  1652. function TSPQuadtreeNode.GetChildForAABB(AABB: TAABB): TSectorNode;
  1653. var
  1654. Location: TAffineVector;
  1655. ChildNode: TSectorNode;
  1656. ChildNodeIndex: Integer;
  1657. begin
  1658. // Instead of looping through all children, we simply determine on which
  1659. // side of the center node the child is located
  1660. ChildNodeIndex := 0;
  1661. Location := AABB.Min;
  1662. // Fore / Back
  1663. if Location.X > FBSphere.Center.X then
  1664. ChildNodeIndex := ChildNodeIndex or 1;
  1665. // Left / Right
  1666. if Location.Z > FBSphere.Center.Z then
  1667. ChildNodeIndex := ChildNodeIndex or 2;
  1668. Assert(ChildNodeIndex < ChildCount, 'Bad ChildNodeIndex!');
  1669. ChildNode := FChildren[ChildNodeIndex];
  1670. if ChildNode.AABBFitsInNode(AABB) then
  1671. begin
  1672. Result := ChildNode;
  1673. Exit;
  1674. end;
  1675. Result := nil;
  1676. end;
  1677. { TQuadtreeSpacePartition }
  1678. function TQuadtreeSpacePartition.CreateNewNode(AParent: TSectorNode)
  1679. : TSectorNode;
  1680. begin
  1681. Result := TSPQuadtreeNode.Create(Self, AParent);
  1682. end;
  1683. procedure TQuadtreeSpacePartition.SetSize(const Min, Max: TAffineVector);
  1684. var
  1685. AABB: TAABB;
  1686. begin
  1687. AABB.Min := Min;
  1688. AABB.Max := Max;
  1689. RebuildTree(AABB);
  1690. end;
  1691. end.