GXS.BSP.pas 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.BSP;
  5. (*
  6. Binary Space Partion mesh support for GXScene.
  7. The classes of this unit are designed to operate within a TgxBaseMesh.
  8. *)
  9. interface
  10. {$I Stage.Defines.inc}
  11. uses
  12. System.SysUtils,
  13. System.Classes,
  14. System.Math,
  15. Stage.VectorTypes,
  16. GXS.VectorFileObjects,
  17. GXS.Material,
  18. Stage.VectorGeometry,
  19. GXS.VectorLists,
  20. GXS.Color,
  21. GXS.RenderContextInfo;
  22. type
  23. TBSPCullingSphere = record
  24. position: TVector4f;
  25. radius: Single;
  26. end;
  27. TBSPRenderContextInfo = record
  28. // Local coordinates of the camera (can be a vector or point)
  29. cameraLocal: TVector4f;
  30. rci: PGXRenderContextInfo;
  31. faceGroups: TList;
  32. cullingSpheres: array of TBSPCullingSphere;
  33. end;
  34. TBSPRenderSort = (rsNone, rsBackToFront, rsFrontToBack);
  35. TBSPClusterVisibility = class
  36. private
  37. FData: PByteArray;
  38. FSize, FBytesPerCluster, FCount: Integer;
  39. protected
  40. procedure SetCount(NumClusters: Integer);
  41. function GetVisibility(Source, Destination: Integer): Boolean;
  42. procedure SetVisibility(Source, Destination: Integer; const Value: Boolean);
  43. public
  44. constructor Create;
  45. destructor Destroy; override;
  46. procedure SetData(Source: PByte; NumClusters: Integer);
  47. property Count: Integer read FCount write SetCount;
  48. property Visibility[src, dst: Integer]: Boolean read GetVisibility
  49. write SetVisibility;
  50. end;
  51. TFGBSPNode = class;
  52. (* A BSP mesh object.
  53. Stores the geometry information, BSP rendering options and offers some
  54. basic BSP utility methods. Geometry information is indexed in the facegroups,
  55. the 1st facegroup (of index 0) being the root node of the BSP tree. *)
  56. TBSPMeshObject = class(TgxMeshObject)
  57. private
  58. FRenderSort: TBSPRenderSort;
  59. FClusterVisibility: TBSPClusterVisibility;
  60. FUseClusterVisibility: Boolean;
  61. public
  62. constructor CreateOwned(AOwner: TgxMeshObjectList);
  63. destructor Destroy; override;
  64. procedure BuildList(var mrci: TgxRenderContextInfo); override;
  65. (* Drops all unused nodes from the facegroups list.
  66. An unused node is a node that renders nothing and whose children
  67. render nothing. Indices are remapped in the process. *)
  68. procedure CleanupUnusedNodes;
  69. (* Returns the average BSP tree depth of end nodes.
  70. Sums up the depth each end node (starting at 1 for root node),
  71. divides by the number of end nodes. This is a simple estimator
  72. of tree balancing (structurally speaking, not polygon-wise). *)
  73. function AverageDepth: Single;
  74. // Traverses the tree to the given point and returns the node index.
  75. function FindNodeByPoint(const aPoint: TVector4f): TFGBSPNode;
  76. (* Rendering sort mode.
  77. This sort mode can currently *not* blend with the sort by materials
  78. flag, default mode is rsBackToFront.
  79. Note that in rsNone mode, the hierarchical nature of the tree is
  80. still honoured (positive subnode, self, then negative subnode). *)
  81. property RenderSort: TBSPRenderSort read FRenderSort write FRenderSort;
  82. (* Cluster visibility.
  83. A property for defining node cluster-cluster visibility potential. *)
  84. property ClusterVisibility: TBSPClusterVisibility read FClusterVisibility;
  85. (* Use cluster visibility.
  86. Toggles the use of the visibility set for culling clusters of nodes
  87. when rendering. *)
  88. property UseClusterVisibility: Boolean read FUseClusterVisibility
  89. write FUseClusterVisibility;
  90. end;
  91. (* A node in the BSP tree.
  92. The description does not explicitly differentiates nodes and leafs,
  93. nodes are referred by their index. *)
  94. TFGBSPNode = class(TgxFGVertexIndexList)
  95. private
  96. FSplitPlane: THmgPlane;
  97. FPositiveSubNodeIndex: Integer;
  98. FNegativeSubNodeIndex: Integer;
  99. FCluster: Integer;
  100. protected
  101. function AddLerp(iA, iB: Integer; fB, fA: Single): Integer;
  102. function AddLerpIfDistinct(iA, iB, iMid: Integer): Integer;
  103. public
  104. constructor CreateOwned(AOwner: TgxFaceGroups); override;
  105. destructor Destroy; override;
  106. procedure IsCulled(const bsprci: TBSPRenderContextInfo;
  107. var positive, negative: Boolean);
  108. procedure CollectNoSort(var bsprci: TBSPRenderContextInfo);
  109. procedure CollectFrontToBack(var bsprci: TBSPRenderContextInfo);
  110. procedure CollectBackToFront(var bsprci: TBSPRenderContextInfo);
  111. (* Try to find a 'decent' split plane for the node.
  112. Use this function to build a BSP tree, on leafy nodes. The split
  113. plane is chosen among the polygon planes, the coefficient are used
  114. to determine what a 'good' split plane is by assigning a cost
  115. to splitted triangles (cut by the split plane) and tree imbalance. *)
  116. function FindSplitPlane(triangleSplitCost: Single = 1;
  117. triangleImbalanceCost: Single = 0.5): THmgPlane;
  118. (* Evaluates a split plane.
  119. Used by FindSplitPlane. For splitted triangles, the extra spawned
  120. triangles required are accounted for in the nbXxxTriangles values. *)
  121. procedure EvaluateSplitPlane(const splitPlane: THmgPlane;
  122. var nbTriangleSplit: Integer; var nbPositiveTriangles: Integer;
  123. var nbNegativeTriangles: Integer);
  124. (* Splits a leafy node along the specified plane.
  125. Will trigger an exception if the node already has subnodes. Currently
  126. also changes the mode from strips/fan to list. *)
  127. procedure PerformSplit(const splitPlane: THmgPlane;
  128. const maxTrianglesPerLeaf: Integer = MaxInt);
  129. (* Goes through all triangle edges, looking for tjunctions.
  130. The candidates are indices of points to lookup a tjunction vertices. *)
  131. procedure FixTJunctions(const tJunctionsCandidates: TgxIntegerList);
  132. (* BSP node split plane.
  133. Divides space between positive and negative half-space, positive
  134. half-space being the one were the evaluation of an homogeneous
  135. vector against the plane is positive. *)
  136. property splitPlane: THmgPlane read FSplitPlane write FSplitPlane;
  137. // Index of the positive sub-node index in the list. Zero if empty.
  138. property PositiveSubNodeIndex: Integer read FPositiveSubNodeIndex
  139. write FPositiveSubNodeIndex;
  140. // Index of the negative sub-node index in the list. Zero if empty.
  141. property NegativeSubNodeIndex: Integer read FNegativeSubNodeIndex
  142. write FNegativeSubNodeIndex;
  143. (* The index of the cluster that this node belongs to.
  144. Used for visibility determination. *)
  145. property Cluster: Integer read FCluster write FCluster;
  146. end;
  147. // ------------------------------------------------------------------
  148. implementation
  149. // ------------------------------------------------------------------
  150. const
  151. cOwnTriangleEpsilon = 1E-5;
  152. cTJunctionEpsilon = 1E-4;
  153. // ---------------------------------------
  154. // TBSPClusterVisibility
  155. // ---------------------------------------
  156. constructor TBSPClusterVisibility.Create;
  157. begin
  158. inherited;
  159. end;
  160. destructor TBSPClusterVisibility.Destroy;
  161. begin
  162. if Assigned(FData) then
  163. Dispose(FData);
  164. inherited;
  165. end;
  166. procedure TBSPClusterVisibility.SetCount(NumClusters: Integer);
  167. var
  168. NewSize: Integer;
  169. NewData: PByteArray;
  170. begin
  171. if FCount = NumClusters then
  172. Exit;
  173. FCount := NumClusters;
  174. FBytesPerCluster := (NumClusters div 8 + 2);
  175. NewSize := NumClusters * FBytesPerCluster;
  176. if NewSize <> FSize then
  177. begin
  178. if NewSize > 0 then
  179. begin
  180. GetMem(NewData, NewSize);
  181. if Assigned(FData) then
  182. begin
  183. Move(FData[0], NewData[0], FSize);
  184. Dispose(FData);
  185. end;
  186. FData := @NewData^;
  187. end
  188. else
  189. begin
  190. if Assigned(FData) then
  191. begin
  192. Dispose(FData);
  193. FData := nil;
  194. end;
  195. end;
  196. FSize := NewSize;
  197. end;
  198. end;
  199. function TBSPClusterVisibility.GetVisibility(Source,
  200. Destination: Integer): Boolean;
  201. var
  202. ByteIdx, BitIdx: Integer;
  203. begin
  204. Assert((Source < Count) and (Destination < Count) and (Source >= 0) and
  205. (Destination >= 0), 'Node index out of bounds!');
  206. ByteIdx := Source * FBytesPerCluster + Destination div 8;
  207. BitIdx := Destination mod 8;
  208. Result := (FData^[ByteIdx] and (1 shl BitIdx)) > 0;
  209. end;
  210. procedure TBSPClusterVisibility.SetVisibility(Source, Destination: Integer;
  211. const Value: Boolean);
  212. var
  213. ByteIdx, BitIdx: Integer;
  214. begin
  215. Assert((Source < Count) and (Destination < Count) and (Source >= 0) and
  216. (Destination >= 0), 'Node index out of bounds!');
  217. ByteIdx := Source * FBytesPerCluster + Destination div 8;
  218. BitIdx := Destination mod 8;
  219. if Value then
  220. FData^[ByteIdx] := FData^[ByteIdx] or (1 shl BitIdx)
  221. else
  222. FData^[ByteIdx] := FData^[ByteIdx] and not(1 shl BitIdx);
  223. end;
  224. procedure TBSPClusterVisibility.SetData(Source: PByte; NumClusters: Integer);
  225. begin
  226. Count := NumClusters;
  227. Move(Source^, FData[0], FSize);
  228. end;
  229. // ------------------
  230. // ------------------ TBSPMeshObject ------------------
  231. // ------------------
  232. constructor TBSPMeshObject.CreateOwned(AOwner: TgxMeshObjectList);
  233. begin
  234. inherited;
  235. Mode := momFaceGroups;
  236. RenderSort := rsBackToFront;
  237. FClusterVisibility := TBSPClusterVisibility.Create;
  238. FUseClusterVisibility := False;
  239. end;
  240. destructor TBSPMeshObject.Destroy;
  241. begin
  242. FClusterVisibility.Free;
  243. inherited;
  244. end;
  245. procedure TBSPMeshObject.BuildList(var mrci: TgxRenderContextInfo);
  246. var
  247. i, j, k, n, camCluster: Integer;
  248. bsprci: TBSPRenderContextInfo;
  249. libMat: TgxLibMaterial;
  250. faceGroupList: TList;
  251. bspNodeList: PPointerList;
  252. renderNode: Boolean;
  253. camNode: TFGBSPNode;
  254. procedure AbsoluteSphereToLocal(const absPos: TVector4f; absRadius: Single;
  255. var local: TBSPCullingSphere);
  256. var
  257. v: TVector4f;
  258. begin
  259. local.position := Owner.Owner.AbsoluteToLocal(absPos);
  260. SetVector(v, absRadius, absRadius, absRadius, 0);
  261. v := Owner.Owner.AbsoluteToLocal(v);
  262. local.radius := MaxFloat(v.X, v.Y, v.Z);
  263. end;
  264. begin
  265. if Mode <> momFaceGroups then
  266. begin
  267. inherited BuildList(mrci);
  268. Exit;
  269. end;
  270. // render BSP
  271. if faceGroups.Count > 0 then
  272. begin
  273. bsprci.cameraLocal := Owner.Owner.AbsoluteToLocal(mrci.cameraPosition);
  274. SetLength(bsprci.cullingSpheres, 2);
  275. AbsoluteSphereToLocal(mrci.cameraPosition, 1, bsprci.cullingSpheres[0]);
  276. AbsoluteSphereToLocal(VectorCombine(mrci.cameraPosition,
  277. mrci.rcci.clippingDirection, 1, mrci.rcci.farClippingDistance),
  278. mrci.rcci.viewPortRadius * mrci.rcci.farClippingDistance,
  279. bsprci.cullingSpheres[1]);
  280. bsprci.rci := @mrci;
  281. faceGroupList := TList.Create;
  282. try
  283. bsprci.faceGroups := faceGroupList;
  284. bsprci.faceGroups.Capacity := faceGroups.Count div 2;
  285. // collect all facegroups
  286. case RenderSort of
  287. rsNone:
  288. (faceGroups[0] as TFGBSPNode).CollectNoSort(bsprci);
  289. rsBackToFront:
  290. (faceGroups[0] as TFGBSPNode).CollectBackToFront(bsprci);
  291. rsFrontToBack:
  292. (faceGroups[0] as TFGBSPNode).CollectFrontToBack(bsprci);
  293. else
  294. Assert(False);
  295. end;
  296. camCluster := 0;
  297. camNode := nil;
  298. if UseClusterVisibility then
  299. begin
  300. camNode := FindNodeByPoint
  301. (Owner.Owner.AbsoluteToLocal(mrci.cameraPosition));
  302. if Assigned(camNode) then
  303. camCluster := camNode.Cluster;
  304. end;
  305. // render facegroups
  306. bspNodeList :=
  307. @faceGroupList.List[0];
  308. n := bsprci.faceGroups.Count;
  309. i := 0;
  310. j := 0;
  311. while i < n do
  312. begin
  313. if UseClusterVisibility and Assigned(camNode) then
  314. renderNode := ClusterVisibility.Visibility[camCluster,
  315. TFGBSPNode(bspNodeList^[i]).Cluster]
  316. else
  317. renderNode := True;
  318. if renderNode then
  319. with TFGBSPNode(bspNodeList^[i]) do
  320. begin
  321. libMat := MaterialCache;
  322. if Assigned(libMat) then
  323. begin
  324. j := i + 1;
  325. while (j < n) and
  326. (TFGBSPNode(bspNodeList^[j]).MaterialCache = libMat) do
  327. Inc(j);
  328. libMat.Apply(mrci);
  329. repeat
  330. for k := i to j - 1 do
  331. TFGBSPNode(bspNodeList^[k]).BuildList(mrci);
  332. until not libMat.UnApply(mrci);
  333. end
  334. else
  335. begin
  336. j := i;
  337. while (j < n) and
  338. (TFGBSPNode(bspNodeList^[j]).MaterialCache = nil) do
  339. begin
  340. TFGBSPNode(bspNodeList^[j]).BuildList(mrci);
  341. Inc(j);
  342. end;
  343. end;
  344. end;
  345. i := j;
  346. end;
  347. finally
  348. faceGroupList.Free;
  349. end;
  350. end;
  351. DisableOpenGLArrays(mrci);
  352. end;
  353. procedure TBSPMeshObject.CleanupUnusedNodes;
  354. var
  355. i, j, n: Integer;
  356. nodeParents: array of Integer;
  357. remapIndex: array of Integer;
  358. indicesToCheck: TgxIntegerList;
  359. node: TFGBSPNode;
  360. begin
  361. n := faceGroups.Count;
  362. if n = 0 then
  363. Exit;
  364. SetLength(nodeParents, n);
  365. indicesToCheck := TgxIntegerList.Create;
  366. try
  367. // build nodes parent information
  368. FillChar(nodeParents[0], SizeOf(Integer) * n, 255);
  369. for i := 0 to n - 1 do
  370. with TFGBSPNode(faceGroups[i]) do
  371. begin
  372. if PositiveSubNodeIndex > 0 then
  373. nodeParents[PositiveSubNodeIndex] := i;
  374. if NegativeSubNodeIndex > 0 then
  375. nodeParents[NegativeSubNodeIndex] := i;
  376. end;
  377. // now proceed to deleting all the unused nodes
  378. indicesToCheck.AddSerie(n - 1, -1, n);
  379. while indicesToCheck.Count > 0 do
  380. begin
  381. i := indicesToCheck.Pop;
  382. node := TFGBSPNode(faceGroups[i]);
  383. if Assigned(node) then
  384. begin
  385. if node.PositiveSubNodeIndex > 0 then
  386. begin
  387. if TFGBSPNode(faceGroups[node.PositiveSubNodeIndex]) = nil then
  388. node.PositiveSubNodeIndex := 0;
  389. end;
  390. if node.NegativeSubNodeIndex > 0 then
  391. begin
  392. if TFGBSPNode(faceGroups[node.NegativeSubNodeIndex]) = nil then
  393. node.NegativeSubNodeIndex := 0;
  394. end;
  395. if (node.PositiveSubNodeIndex <= 0) and (node.NegativeSubNodeIndex <= 0)
  396. then
  397. begin
  398. if node.VertexIndices.Count = 0 then
  399. begin
  400. if nodeParents[i] >= 0 then
  401. indicesToCheck.Push(nodeParents[i]);
  402. faceGroups.List^[i] := nil;
  403. node.Owner := nil;
  404. node.Free;
  405. end;
  406. end;
  407. end;
  408. end;
  409. // build a remap index
  410. SetLength(remapIndex, n);
  411. j := 0;
  412. for i := 0 to n - 1 do
  413. begin
  414. remapIndex[i] := j;
  415. if faceGroups[i] <> nil then
  416. Inc(j);
  417. end;
  418. // apply remap index
  419. for i := 0 to n - 1 do
  420. begin
  421. node := TFGBSPNode(faceGroups[i]);
  422. if Assigned(node) then
  423. begin
  424. node.PositiveSubNodeIndex := remapIndex[node.PositiveSubNodeIndex];
  425. node.NegativeSubNodeIndex := remapIndex[node.NegativeSubNodeIndex];
  426. end;
  427. end;
  428. // and pack then FaceGroups, done, pfew!! The things we do to remain fast...
  429. faceGroups.Pack;
  430. finally
  431. indicesToCheck.Free;
  432. end;
  433. end;
  434. function TBSPMeshObject.AverageDepth: Single;
  435. var
  436. depthSum, endNodesCount: Integer;
  437. procedure RecurseEstimate(nodeIdx, depth: Integer);
  438. var
  439. node: TFGBSPNode;
  440. begin
  441. node := TFGBSPNode(faceGroups[nodeIdx]);
  442. if node.PositiveSubNodeIndex > 0 then
  443. begin
  444. RecurseEstimate(node.PositiveSubNodeIndex, depth + 1);
  445. if node.NegativeSubNodeIndex > 0 then
  446. RecurseEstimate(node.NegativeSubNodeIndex, depth + 1);
  447. end
  448. else if node.NegativeSubNodeIndex > 0 then
  449. RecurseEstimate(node.NegativeSubNodeIndex, depth + 1)
  450. else
  451. begin
  452. Inc(depthSum, depth);
  453. Inc(endNodesCount);
  454. end;
  455. end;
  456. begin
  457. if faceGroups.Count = 0 then
  458. Result := 1
  459. else
  460. begin
  461. depthSum := 0;
  462. endNodesCount := 0;
  463. RecurseEstimate(0, 1);
  464. Assert(endNodesCount > 0);
  465. Result := depthSum / endNodesCount;
  466. end;
  467. end;
  468. function TBSPMeshObject.FindNodeByPoint(const aPoint: TVector4f): TFGBSPNode;
  469. function Traverse(nodeIndex: Integer): Integer;
  470. var
  471. idx: Integer;
  472. node: TFGBSPNode;
  473. eval: Single;
  474. begin
  475. node := TFGBSPNode(faceGroups[nodeIndex]);
  476. if node.VertexIndices.Count > 0 then
  477. Result := nodeIndex
  478. else
  479. begin
  480. eval := node.splitPlane.X * aPoint.X +
  481. node.splitPlane.Y * aPoint.Y +
  482. node.splitPlane.Z * aPoint.Z -
  483. node.splitPlane.W;
  484. if eval >= 0 then
  485. idx := node.PositiveSubNodeIndex
  486. else
  487. idx := node.NegativeSubNodeIndex;
  488. if idx > 0 then
  489. Result := Traverse(idx)
  490. else
  491. Result := -1;
  492. end;
  493. end;
  494. var
  495. idx: Integer;
  496. begin
  497. Result := nil;
  498. idx := -1;
  499. if faceGroups.Count > 0 then
  500. idx := Traverse(0);
  501. if idx >= 0 then
  502. Result := TFGBSPNode(faceGroups[idx]);
  503. end;
  504. // ------------------
  505. // ------------------ TFGBSPNode ------------------
  506. // ------------------
  507. constructor TFGBSPNode.CreateOwned(AOwner: TgxFaceGroups);
  508. begin
  509. inherited;
  510. FPositiveSubNodeIndex := 0;
  511. FNegativeSubNodeIndex := 0;
  512. end;
  513. destructor TFGBSPNode.Destroy;
  514. begin
  515. inherited;
  516. end;
  517. procedure TFGBSPNode.IsCulled(const bsprci: TBSPRenderContextInfo;
  518. var positive, negative: Boolean);
  519. var
  520. i, n: Integer;
  521. d: Single;
  522. begin
  523. n := Length(bsprci.cullingSpheres);
  524. if n > 0 then
  525. begin
  526. positive := True;
  527. negative := True;
  528. for i := 0 to n - 1 do
  529. with bsprci.cullingSpheres[i] do
  530. begin
  531. d := PlaneEvaluatePoint(splitPlane, position);
  532. if d >= -radius then
  533. positive := False;
  534. if d <= radius then
  535. negative := False;
  536. end;
  537. end
  538. else
  539. begin
  540. positive := False;
  541. negative := False;
  542. end;
  543. end;
  544. procedure TFGBSPNode.CollectNoSort(var bsprci: TBSPRenderContextInfo);
  545. begin
  546. if (PositiveSubNodeIndex > 0) then
  547. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectNoSort(bsprci);
  548. if VertexIndices.Count > 0 then
  549. bsprci.faceGroups.Add(Self);
  550. if (NegativeSubNodeIndex > 0) then
  551. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectNoSort(bsprci);
  552. end;
  553. procedure TFGBSPNode.CollectFrontToBack(var bsprci: TBSPRenderContextInfo);
  554. begin
  555. if PlaneEvaluatePoint(splitPlane, bsprci.cameraLocal) >= 0 then
  556. begin
  557. if PositiveSubNodeIndex > 0 then
  558. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectFrontToBack(bsprci);
  559. if VertexIndices.Count > 0 then
  560. bsprci.faceGroups.Add(Self);
  561. if NegativeSubNodeIndex > 0 then
  562. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectFrontToBack(bsprci);
  563. end
  564. else
  565. begin
  566. if NegativeSubNodeIndex > 0 then
  567. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectFrontToBack(bsprci);
  568. if VertexIndices.Count > 0 then
  569. bsprci.faceGroups.Add(Self);
  570. if PositiveSubNodeIndex > 0 then
  571. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectFrontToBack(bsprci);
  572. end;
  573. end;
  574. procedure TFGBSPNode.CollectBackToFront(var bsprci: TBSPRenderContextInfo);
  575. begin
  576. if PlaneEvaluatePoint(splitPlane, bsprci.cameraLocal) >= 0 then
  577. begin
  578. if NegativeSubNodeIndex > 0 then
  579. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectBackToFront(bsprci);
  580. if VertexIndices.Count > 0 then
  581. bsprci.faceGroups.Add(Self);
  582. if PositiveSubNodeIndex > 0 then
  583. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectBackToFront(bsprci);
  584. end
  585. else
  586. begin
  587. if PositiveSubNodeIndex > 0 then
  588. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectBackToFront(bsprci);
  589. if VertexIndices.Count > 0 then
  590. bsprci.faceGroups.Add(Self);
  591. if NegativeSubNodeIndex > 0 then
  592. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectBackToFront(bsprci);
  593. end;
  594. end;
  595. function TFGBSPNode.FindSplitPlane(triangleSplitCost: Single = 1;
  596. triangleImbalanceCost: Single = 0.5): THmgPlane;
  597. var
  598. i, k, n: Integer;
  599. ns, np, nn: Integer;
  600. evalPlane: THmgPlane;
  601. bestEval, eval: Single;
  602. vertices: TgxAffineVectorList;
  603. begin
  604. Result := NullHmgVector;
  605. bestEval := 1E30;
  606. n := VertexIndices.Count;
  607. vertices := Owner.Owner.vertices;
  608. if n > 0 then
  609. for k := 0 to n div 4 do
  610. begin
  611. case Mode of
  612. fgmmTriangles, fgmmFlatTriangles:
  613. begin
  614. i := Random((n div 3) - 1) * 3;
  615. evalPlane := PlaneMake(vertices[VertexIndices[i]],
  616. vertices[VertexIndices[i + 1]], vertices[VertexIndices[i + 2]]);
  617. end;
  618. fgmmTriangleStrip:
  619. begin
  620. i := Random(n - 2);
  621. evalPlane := PlaneMake(vertices[VertexIndices[i]],
  622. vertices[VertexIndices[i + 1]], vertices[VertexIndices[i + 2]]);
  623. end;
  624. else
  625. // fgmmTriangleFan
  626. i := Random(n - 2);
  627. evalPlane := PlaneMake(vertices[VertexIndices[0]],
  628. vertices[VertexIndices[i]], vertices[VertexIndices[i + 1]]);
  629. end;
  630. EvaluateSplitPlane(evalPlane, ns, np, nn);
  631. eval := ns * triangleSplitCost + Abs(np - nn) * 0.5 *
  632. triangleImbalanceCost;
  633. if eval < bestEval then
  634. begin
  635. bestEval := eval;
  636. Result := evalPlane;
  637. end;
  638. end;
  639. end;
  640. procedure TFGBSPNode.EvaluateSplitPlane(const splitPlane: THmgPlane;
  641. var nbTriangleSplit: Integer; var nbPositiveTriangles: Integer;
  642. var nbNegativeTriangles: Integer);
  643. var
  644. i, n, inci, lookupIdx: Integer;
  645. a, b, c: Boolean;
  646. vertices: TgxAffineVectorList;
  647. const
  648. // case resolution lookup tables (node's tris unaccounted for)
  649. cTriangleSplit: array [0 .. 7] of Integer = (0, 1, 1, 1, 1, 1, 1, 0);
  650. cPositiveTris: array [0 .. 7] of Integer = (0, 1, 1, 2, 1, 2, 2, 1);
  651. cNegativeTris: array [0 .. 7] of Integer = (1, 2, 2, 1, 2, 1, 1, 0);
  652. begin
  653. nbTriangleSplit := 0;
  654. nbPositiveTriangles := 0;
  655. nbNegativeTriangles := 0;
  656. n := VertexIndices.Count;
  657. if n < 3 then
  658. Exit;
  659. vertices := Owner.Owner.vertices;
  660. case Mode of
  661. fgmmTriangleStrip, fgmmTriangleFan:
  662. begin
  663. n := n - 2;
  664. inci := 1;
  665. end;
  666. else
  667. inci := 3;
  668. end;
  669. i := 0;
  670. while i < n do
  671. begin
  672. case Mode of
  673. fgmmTriangleFan:
  674. begin
  675. a := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[0]]) > 0;
  676. b := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[i]]) > 0;
  677. c := PlaneEvaluatePoint(splitPlane,
  678. vertices[VertexIndices[i + 1]]) > 0;
  679. end;
  680. else
  681. // fgmmTriangles, fgmmFlatTriangles, fgmmTriangleStrip
  682. a := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[i]]) > 0;
  683. b := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[i + 1]]) > 0;
  684. c := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[i + 2]]) > 0;
  685. end;
  686. lookupIdx := (Integer(a) shl 2) + (Integer(b) shl 1) + Integer(c);
  687. Inc(nbTriangleSplit, cTriangleSplit[lookupIdx]);
  688. Inc(nbPositiveTriangles, cPositiveTris[lookupIdx]);
  689. Inc(nbNegativeTriangles, cNegativeTris[lookupIdx]);
  690. Inc(i, inci);
  691. end;
  692. end;
  693. function TFGBSPNode.AddLerp(iA, iB: Integer; fB, fA: Single): Integer;
  694. begin
  695. with Owner.Owner do
  696. begin
  697. with vertices do
  698. Result := Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  699. with Normals do
  700. if Count > 0 then
  701. Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  702. with Colors do
  703. if Count > 0 then
  704. Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  705. with TexCoords do
  706. if Count > 0 then
  707. Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  708. with LightMapTexCoords do
  709. if Count > 0 then
  710. Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  711. end;
  712. end;
  713. function TFGBSPNode.AddLerpIfDistinct(iA, iB, iMid: Integer): Integer;
  714. var
  715. midNormal: TAffineVector;
  716. midColor: TgxColorVector;
  717. midTexCoord: TAffineVector;
  718. midLightmapTexCoord: TAffineVector;
  719. f: Single;
  720. spawn: Boolean;
  721. begin
  722. with Owner.Owner do
  723. begin
  724. with vertices do
  725. f := VectorDistance(List^[iA], List^[iMid]) / VectorDistance(List^[iA],
  726. List^[iB]);
  727. spawn := False;
  728. with Normals do
  729. if Count > 0 then
  730. begin
  731. midNormal := VectorLerp(List^[iA], List^[iB], f);
  732. spawn := (VectorSpacing(midNormal, List^[iMid]) > cTJunctionEpsilon);
  733. end;
  734. with Colors do
  735. if Count > 0 then
  736. begin
  737. midColor := VectorLerp(List^[iA], List^[iB], f);
  738. spawn := spawn or (VectorSpacing(midColor, List^[iMid]) >
  739. cTJunctionEpsilon);
  740. end;
  741. with TexCoords do
  742. if Count > 0 then
  743. begin
  744. midTexCoord := VectorLerp(List^[iA], List^[iB], f);
  745. spawn := spawn or (VectorSpacing(midTexCoord, List^[iMid]) >
  746. cTJunctionEpsilon);
  747. end;
  748. with LightMapTexCoords do
  749. if Count > 0 then
  750. begin
  751. midLightmapTexCoord := VectorLerp(List^[iA], List^[iB], f);
  752. spawn := spawn or (VectorSpacing(midLightmapTexCoord, List^[iMid]) >
  753. cTJunctionEpsilon);
  754. end;
  755. if spawn then
  756. begin
  757. with vertices do
  758. Result := Add(List^[iMid]);
  759. with Normals do
  760. if Count > 0 then
  761. Add(midNormal);
  762. with Colors do
  763. if Count > 0 then
  764. Add(midColor);
  765. with TexCoords do
  766. if Count > 0 then
  767. Add(midTexCoord);
  768. with LightMapTexCoords do
  769. if Count > 0 then
  770. Add(midLightmapTexCoord);
  771. end
  772. else
  773. Result := iMid;
  774. end;
  775. end;
  776. procedure TFGBSPNode.PerformSplit(const splitPlane: THmgPlane;
  777. const maxTrianglesPerLeaf: Integer = MaxInt);
  778. var
  779. fgPos, fgNeg: TFGBSPNode;
  780. fgPosIndices, fgNegIndices: TgxIntegerList;
  781. indices: TgxIntegerList;
  782. procedure SplitTriangleMid(strayID, strayNext, strayPrev: Integer;
  783. eNext, ePrev: Single);
  784. var
  785. iOpp: Integer;
  786. invSum: Single;
  787. begin
  788. invSum := 1 / (Abs(eNext) + Abs(ePrev));
  789. iOpp := AddLerp(strayNext, strayPrev, Abs(eNext) * invSum,
  790. Abs(ePrev) * invSum);
  791. if eNext > 0 then
  792. begin
  793. fgPosIndices.Add(strayID, strayNext, iOpp);
  794. fgNegIndices.Add(iOpp, strayPrev, strayID);
  795. end
  796. else
  797. begin
  798. fgNegIndices.Add(strayID, strayNext, iOpp);
  799. fgPosIndices.Add(iOpp, strayPrev, strayID);
  800. end;
  801. end;
  802. procedure SplitTriangle(strayID, strayNext, strayPrev: Integer;
  803. eStray, eNext, ePrev: Single);
  804. var
  805. iNext, iPrev: Integer;
  806. invSum: Single;
  807. begin
  808. invSum := 1 / (Abs(eNext) + Abs(eStray));
  809. iNext := AddLerp(strayNext, strayID, Abs(eNext) * invSum,
  810. Abs(eStray) * invSum);
  811. invSum := 1 / (Abs(ePrev) + Abs(eStray));
  812. iPrev := AddLerp(strayPrev, strayID, Abs(ePrev) * invSum,
  813. Abs(eStray) * invSum);
  814. if eStray > 0 then
  815. begin
  816. fgPos.VertexIndices.Add(strayID, iNext, iPrev);
  817. fgNeg.VertexIndices.Add(strayNext, strayPrev, iPrev);
  818. fgNeg.VertexIndices.Add(iPrev, iNext, strayNext);
  819. end
  820. else if eStray < 0 then
  821. begin
  822. fgNeg.VertexIndices.Add(strayID, iNext, iPrev);
  823. fgPos.VertexIndices.Add(strayNext, strayPrev, iPrev);
  824. fgPos.VertexIndices.Add(iPrev, iNext, strayNext);
  825. end;
  826. end;
  827. var
  828. i, i1, i2, i3, se1, se2, se3: Integer;
  829. e1, e2, e3: Single;
  830. vertices: TgxAffineVectorList;
  831. subSplitPlane: THmgPlane;
  832. begin
  833. Assert((PositiveSubNodeIndex = 0) and (NegativeSubNodeIndex = 0));
  834. ConvertToList;
  835. // prepare sub nodes
  836. FPositiveSubNodeIndex := Owner.Count;
  837. fgPos := TFGBSPNode.CreateOwned(Owner);
  838. fgPosIndices := fgPos.VertexIndices;
  839. FNegativeSubNodeIndex := Owner.Count;
  840. fgNeg := TFGBSPNode.CreateOwned(Owner);
  841. fgNegIndices := fgNeg.VertexIndices;
  842. // initiate split
  843. Self.FSplitPlane := splitPlane;
  844. indices := TgxIntegerList.Create;
  845. vertices := Owner.Owner.vertices;
  846. i := 0;
  847. while i < VertexIndices.Count do
  848. begin
  849. // evaluate all points
  850. i1 := VertexIndices[i];
  851. e1 := PlaneEvaluatePoint(splitPlane, vertices.List^[i1]);
  852. i2 := VertexIndices[i + 1];
  853. e2 := PlaneEvaluatePoint(splitPlane, vertices.List^[i2]);
  854. i3 := VertexIndices[i + 2];
  855. e3 := PlaneEvaluatePoint(splitPlane, vertices.List^[i3]);
  856. if Abs(e1) < cOwnTriangleEpsilon then
  857. begin
  858. e1 := 0;
  859. se1 := 0;
  860. end
  861. else
  862. se1 := Sign(e1);
  863. if Abs(e2) < cOwnTriangleEpsilon then
  864. begin
  865. e2 := 0;
  866. se2 := 0;
  867. end
  868. else
  869. se2 := Sign(e2);
  870. if Abs(e3) < cOwnTriangleEpsilon then
  871. begin
  872. e3 := 0;
  873. se3 := 0;
  874. end
  875. else
  876. se3 := Sign(e3);
  877. // case disjunction
  878. case se1 of
  879. - 1:
  880. case se2 of
  881. - 1:
  882. case se3 of
  883. - 1, 0:
  884. fgNegIndices.Add(i1, i2, i3);
  885. +1:
  886. SplitTriangle(i3, i1, i2, e3, e1, e2);
  887. end;
  888. 0:
  889. case se3 of
  890. - 1, 0:
  891. fgNegIndices.Add(i1, i2, i3);
  892. +1:
  893. SplitTriangleMid(i2, i3, i1, e3, e1);
  894. end;
  895. +1:
  896. case se3 of
  897. - 1:
  898. SplitTriangle(i2, i3, i1, e2, e3, e1);
  899. 0:
  900. SplitTriangleMid(i3, i1, i2, e1, e2);
  901. +1:
  902. SplitTriangle(i1, i2, i3, e1, e2, e3);
  903. end;
  904. end;
  905. 0:
  906. case se2 of
  907. - 1:
  908. case se3 of
  909. - 1, 0:
  910. fgNegIndices.Add(i1, i2, i3);
  911. +1:
  912. SplitTriangleMid(i1, i2, i3, e2, e3);
  913. end;
  914. 0:
  915. case se3 of
  916. - 1:
  917. fgNegIndices.Add(i1, i2, i3);
  918. 0:
  919. indices.Add(i1, i2, i3);
  920. +1:
  921. fgPosIndices.Add(i1, i2, i3);
  922. end;
  923. +1:
  924. case se3 of
  925. - 1:
  926. SplitTriangleMid(i1, i2, i3, e2, e3);
  927. 0, +1:
  928. fgPosIndices.Add(i1, i2, i3);
  929. end;
  930. end;
  931. +1:
  932. case se2 of
  933. - 1:
  934. case se3 of
  935. - 1:
  936. SplitTriangle(i1, i2, i3, e1, e2, e3);
  937. 0:
  938. SplitTriangleMid(i3, i1, i2, e1, e2);
  939. +1:
  940. SplitTriangle(i2, i3, i1, e2, e3, e1);
  941. end;
  942. 0:
  943. case se3 of
  944. - 1:
  945. SplitTriangleMid(i2, i3, i1, e3, e1);
  946. 0, +1:
  947. fgPosIndices.Add(i1, i2, i3);
  948. end;
  949. +1:
  950. case se3 of
  951. - 1:
  952. SplitTriangle(i3, i1, i2, e3, e1, e2);
  953. 0, +1:
  954. fgPosIndices.Add(i1, i2, i3);
  955. end;
  956. end;
  957. end;
  958. Inc(i, 3);
  959. end;
  960. VertexIndices := indices;
  961. indices.Free;
  962. if fgPos.TriangleCount = 0 then
  963. begin
  964. FPositiveSubNodeIndex := 0;
  965. FNegativeSubNodeIndex := FNegativeSubNodeIndex - 1;
  966. FreeAndNil(fgPos);
  967. end;
  968. if fgNeg.TriangleCount = 0 then
  969. begin
  970. FNegativeSubNodeIndex := 0;
  971. FreeAndNil(fgNeg);
  972. end;
  973. if Assigned(fgPos) and (fgPos.TriangleCount > maxTrianglesPerLeaf) then
  974. begin
  975. subSplitPlane := fgPos.FindSplitPlane;
  976. fgPos.PerformSplit(subSplitPlane, maxTrianglesPerLeaf);
  977. end;
  978. if Assigned(fgNeg) and (fgNeg.TriangleCount > maxTrianglesPerLeaf) then
  979. begin
  980. subSplitPlane := fgNeg.FindSplitPlane;
  981. fgNeg.PerformSplit(subSplitPlane, maxTrianglesPerLeaf);
  982. end;
  983. end;
  984. procedure TFGBSPNode.FixTJunctions(const tJunctionsCandidates: TgxIntegerList);
  985. function FindTJunction(iA, iB, iC: Integer;
  986. candidatesList: TgxIntegerList): Integer;
  987. var
  988. i, k: Integer;
  989. vertices: PAffineVectorArray;
  990. candidate, vA, vB: PAffineVector;
  991. boxMin, boxMax, vector, invVector: TAffineVector;
  992. f: TAffineVector;
  993. begin
  994. Result := -1;
  995. vertices := Owner.Owner.vertices.List;
  996. vA := @vertices[iA];
  997. vB := @vertices[iB];
  998. // compute bounding box of the segment
  999. boxMin := vA^;
  1000. MinVector(boxMin, vB^);
  1001. boxMax := vA^;
  1002. MaxVector(boxMax, vB^);
  1003. // compute extent and its inversion
  1004. vector := VectorSubtract(vB^, vA^);
  1005. for i := 0 to 2 do
  1006. if vector.V[i] <> 0 then
  1007. invVector.V[i] := 1 / vector.V[i]
  1008. else
  1009. invVector.V[i] := 0;
  1010. // lookup all candidates
  1011. for i := 0 to candidatesList.Count - 1 do
  1012. begin
  1013. k := candidatesList.List^[i];
  1014. if (k = iA) or (k = iB) or (k = iC) then
  1015. Continue;
  1016. candidate := @vertices[k];
  1017. if (candidate^.X > boxMin.X) and
  1018. (candidate^.Y > boxMin.Y) and
  1019. (candidate^.Z > boxMin.Z) and
  1020. (candidate^.X < boxMax.X) and
  1021. (candidate^.Y < boxMax.Y) and
  1022. (candidate^.Z < boxMax.Z) then
  1023. begin
  1024. f := candidate^;
  1025. SubtractVector(f, vA^);
  1026. ScaleVector(f, invVector);
  1027. if (Abs(f.X - f.Y) < cTJunctionEpsilon) and
  1028. (Abs(f.X - f.Z) < cTJunctionEpsilon) and
  1029. (Abs(f.Y - f.Z) < cTJunctionEpsilon) then
  1030. begin
  1031. Result := AddLerpIfDistinct(iA, iB, k);
  1032. Break;
  1033. end;
  1034. end;
  1035. end;
  1036. end;
  1037. var
  1038. i, tj: Integer;
  1039. indices: PIntegerArray;
  1040. mark: TgxIntegerList;
  1041. begin
  1042. Assert(Mode in [fgmmTriangles, fgmmFlatTriangles]);
  1043. mark := TgxIntegerList.Create;
  1044. mark.AddSerie(1, 0, VertexIndices.Count);
  1045. indices := VertexIndices.List;
  1046. i := 0;
  1047. while i < VertexIndices.Count do
  1048. begin
  1049. if mark[i] <> 0 then
  1050. begin
  1051. tj := FindTJunction(indices^[i], indices^[i + 1], indices^[i + 2],
  1052. tJunctionsCandidates);
  1053. if tj >= 0 then
  1054. begin
  1055. VertexIndices.Add(tj, VertexIndices[i + 1], VertexIndices[i + 2]);
  1056. mark.Add(1, 1, 0);
  1057. indices := VertexIndices.List;
  1058. indices^[i + 1] := tj;
  1059. mark[i + 1] := 0;
  1060. Continue;
  1061. end;
  1062. end;
  1063. if mark[i + 1] <> 0 then
  1064. begin
  1065. tj := FindTJunction(indices^[i + 1], indices^[i + 2], indices^[i],
  1066. tJunctionsCandidates);
  1067. if tj >= 0 then
  1068. begin
  1069. VertexIndices.Add(tj, VertexIndices[i + 2], VertexIndices[i]);
  1070. mark.Add(1, 1, 0);
  1071. indices := VertexIndices.List;
  1072. indices^[i + 2] := tj;
  1073. mark[i + 2] := 0;
  1074. Continue;
  1075. end;
  1076. end;
  1077. if mark[i + 2] <> 0 then
  1078. begin
  1079. tj := FindTJunction(indices^[i + 2], indices^[i], indices^[i + 1],
  1080. tJunctionsCandidates);
  1081. if tj >= 0 then
  1082. begin
  1083. VertexIndices.Add(tj, VertexIndices[i], VertexIndices[i + 1]);
  1084. mark.Add(1, 1, 0);
  1085. indices := VertexIndices.List;
  1086. indices^[i] := tj;
  1087. mark[i] := 0;
  1088. Continue;
  1089. end;
  1090. end;
  1091. Inc(i, 3);
  1092. end;
  1093. mark.Free;
  1094. end;
  1095. // ------------------------------------------------------------------
  1096. initialization
  1097. // ------------------------------------------------------------------
  1098. RegisterClasses([TBSPMeshObject, TFGBSPNode]);
  1099. end.