GLSpacePartition.pas 65 KB

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