GXS.BSP.pas 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  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. Stage.VectorGeometry,
  17. GXS.VectorFileObjects,
  18. GXS.Material,
  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. implementation // --------------------------------------------------------
  148. const
  149. cOwnTriangleEpsilon = 1E-5;
  150. cTJunctionEpsilon = 1E-4;
  151. // ---------------------------------------
  152. // TBSPClusterVisibility
  153. // ---------------------------------------
  154. constructor TBSPClusterVisibility.Create;
  155. begin
  156. inherited;
  157. end;
  158. destructor TBSPClusterVisibility.Destroy;
  159. begin
  160. if Assigned(FData) then
  161. Dispose(FData);
  162. inherited;
  163. end;
  164. procedure TBSPClusterVisibility.SetCount(NumClusters: Integer);
  165. var
  166. NewSize: Integer;
  167. NewData: PByteArray;
  168. begin
  169. if FCount = NumClusters then
  170. Exit;
  171. FCount := NumClusters;
  172. FBytesPerCluster := (NumClusters div 8 + 2);
  173. NewSize := NumClusters * FBytesPerCluster;
  174. if NewSize <> FSize then
  175. begin
  176. if NewSize > 0 then
  177. begin
  178. GetMem(NewData, NewSize);
  179. if Assigned(FData) then
  180. begin
  181. Move(FData[0], NewData[0], FSize);
  182. Dispose(FData);
  183. end;
  184. FData := @NewData^;
  185. end
  186. else
  187. begin
  188. if Assigned(FData) then
  189. begin
  190. Dispose(FData);
  191. FData := nil;
  192. end;
  193. end;
  194. FSize := NewSize;
  195. end;
  196. end;
  197. function TBSPClusterVisibility.GetVisibility(Source,
  198. Destination: Integer): Boolean;
  199. var
  200. ByteIdx, BitIdx: Integer;
  201. begin
  202. Assert((Source < Count) and (Destination < Count) and (Source >= 0) and
  203. (Destination >= 0), 'Node index out of bounds!');
  204. ByteIdx := Source * FBytesPerCluster + Destination div 8;
  205. BitIdx := Destination mod 8;
  206. Result := (FData^[ByteIdx] and (1 shl BitIdx)) > 0;
  207. end;
  208. procedure TBSPClusterVisibility.SetVisibility(Source, Destination: Integer;
  209. const Value: Boolean);
  210. var
  211. ByteIdx, BitIdx: Integer;
  212. begin
  213. Assert((Source < Count) and (Destination < Count) and (Source >= 0) and
  214. (Destination >= 0), 'Node index out of bounds!');
  215. ByteIdx := Source * FBytesPerCluster + Destination div 8;
  216. BitIdx := Destination mod 8;
  217. if Value then
  218. FData^[ByteIdx] := FData^[ByteIdx] or (1 shl BitIdx)
  219. else
  220. FData^[ByteIdx] := FData^[ByteIdx] and not(1 shl BitIdx);
  221. end;
  222. procedure TBSPClusterVisibility.SetData(Source: PByte; NumClusters: Integer);
  223. begin
  224. Count := NumClusters;
  225. Move(Source^, FData[0], FSize);
  226. end;
  227. // ------------------
  228. // ------------------ TBSPMeshObject ------------------
  229. // ------------------
  230. constructor TBSPMeshObject.CreateOwned(AOwner: TgxMeshObjectList);
  231. begin
  232. inherited;
  233. Mode := momFaceGroups;
  234. RenderSort := rsBackToFront;
  235. FClusterVisibility := TBSPClusterVisibility.Create;
  236. FUseClusterVisibility := False;
  237. end;
  238. destructor TBSPMeshObject.Destroy;
  239. begin
  240. FClusterVisibility.Free;
  241. inherited;
  242. end;
  243. procedure TBSPMeshObject.BuildList(var mrci: TgxRenderContextInfo);
  244. var
  245. i, j, k, n, camCluster: Integer;
  246. bsprci: TBSPRenderContextInfo;
  247. libMat: TgxLibMaterial;
  248. faceGroupList: TList;
  249. bspNodeList: PPointerList;
  250. renderNode: Boolean;
  251. camNode: TFGBSPNode;
  252. procedure AbsoluteSphereToLocal(const absPos: TVector4f; absRadius: Single;
  253. var local: TBSPCullingSphere);
  254. var
  255. v: TVector4f;
  256. begin
  257. local.position := Owner.Owner.AbsoluteToLocal(absPos);
  258. SetVector(v, absRadius, absRadius, absRadius, 0);
  259. v := Owner.Owner.AbsoluteToLocal(v);
  260. local.radius := MaxFloat(v.X, v.Y, v.Z);
  261. end;
  262. begin
  263. if Mode <> momFaceGroups then
  264. begin
  265. inherited BuildList(mrci);
  266. Exit;
  267. end;
  268. // render BSP
  269. if faceGroups.Count > 0 then
  270. begin
  271. bsprci.cameraLocal := Owner.Owner.AbsoluteToLocal(mrci.cameraPosition);
  272. SetLength(bsprci.cullingSpheres, 2);
  273. AbsoluteSphereToLocal(mrci.cameraPosition, 1, bsprci.cullingSpheres[0]);
  274. AbsoluteSphereToLocal(VectorCombine(mrci.cameraPosition,
  275. mrci.rcci.clippingDirection, 1, mrci.rcci.farClippingDistance),
  276. mrci.rcci.viewPortRadius * mrci.rcci.farClippingDistance,
  277. bsprci.cullingSpheres[1]);
  278. bsprci.rci := @mrci;
  279. faceGroupList := TList.Create;
  280. try
  281. bsprci.faceGroups := faceGroupList;
  282. bsprci.faceGroups.Capacity := faceGroups.Count div 2;
  283. // collect all facegroups
  284. case RenderSort of
  285. rsNone:
  286. (faceGroups[0] as TFGBSPNode).CollectNoSort(bsprci);
  287. rsBackToFront:
  288. (faceGroups[0] as TFGBSPNode).CollectBackToFront(bsprci);
  289. rsFrontToBack:
  290. (faceGroups[0] as TFGBSPNode).CollectFrontToBack(bsprci);
  291. else
  292. Assert(False);
  293. end;
  294. camCluster := 0;
  295. camNode := nil;
  296. if UseClusterVisibility then
  297. begin
  298. camNode := FindNodeByPoint
  299. (Owner.Owner.AbsoluteToLocal(mrci.cameraPosition));
  300. if Assigned(camNode) then
  301. camCluster := camNode.Cluster;
  302. end;
  303. // render facegroups
  304. bspNodeList :=
  305. @faceGroupList.List[0];
  306. n := bsprci.faceGroups.Count;
  307. i := 0;
  308. j := 0;
  309. while i < n do
  310. begin
  311. if UseClusterVisibility and Assigned(camNode) then
  312. renderNode := ClusterVisibility.Visibility[camCluster,
  313. TFGBSPNode(bspNodeList^[i]).Cluster]
  314. else
  315. renderNode := True;
  316. if renderNode then
  317. with TFGBSPNode(bspNodeList^[i]) do
  318. begin
  319. libMat := MaterialCache;
  320. if Assigned(libMat) then
  321. begin
  322. j := i + 1;
  323. while (j < n) and
  324. (TFGBSPNode(bspNodeList^[j]).MaterialCache = libMat) do
  325. Inc(j);
  326. libMat.Apply(mrci);
  327. repeat
  328. for k := i to j - 1 do
  329. TFGBSPNode(bspNodeList^[k]).BuildList(mrci);
  330. until not libMat.UnApply(mrci);
  331. end
  332. else
  333. begin
  334. j := i;
  335. while (j < n) and
  336. (TFGBSPNode(bspNodeList^[j]).MaterialCache = nil) do
  337. begin
  338. TFGBSPNode(bspNodeList^[j]).BuildList(mrci);
  339. Inc(j);
  340. end;
  341. end;
  342. end;
  343. i := j;
  344. end;
  345. finally
  346. faceGroupList.Free;
  347. end;
  348. end;
  349. DisableOpenGLArrays(mrci);
  350. end;
  351. procedure TBSPMeshObject.CleanupUnusedNodes;
  352. var
  353. i, j, n: Integer;
  354. nodeParents: array of Integer;
  355. remapIndex: array of Integer;
  356. indicesToCheck: TgxIntegerList;
  357. node: TFGBSPNode;
  358. begin
  359. n := faceGroups.Count;
  360. if n = 0 then
  361. Exit;
  362. SetLength(nodeParents, n);
  363. indicesToCheck := TgxIntegerList.Create;
  364. try
  365. // build nodes parent information
  366. FillChar(nodeParents[0], SizeOf(Integer) * n, 255);
  367. for i := 0 to n - 1 do
  368. with TFGBSPNode(faceGroups[i]) do
  369. begin
  370. if PositiveSubNodeIndex > 0 then
  371. nodeParents[PositiveSubNodeIndex] := i;
  372. if NegativeSubNodeIndex > 0 then
  373. nodeParents[NegativeSubNodeIndex] := i;
  374. end;
  375. // now proceed to deleting all the unused nodes
  376. indicesToCheck.AddSerie(n - 1, -1, n);
  377. while indicesToCheck.Count > 0 do
  378. begin
  379. i := indicesToCheck.Pop;
  380. node := TFGBSPNode(faceGroups[i]);
  381. if Assigned(node) then
  382. begin
  383. if node.PositiveSubNodeIndex > 0 then
  384. begin
  385. if TFGBSPNode(faceGroups[node.PositiveSubNodeIndex]) = nil then
  386. node.PositiveSubNodeIndex := 0;
  387. end;
  388. if node.NegativeSubNodeIndex > 0 then
  389. begin
  390. if TFGBSPNode(faceGroups[node.NegativeSubNodeIndex]) = nil then
  391. node.NegativeSubNodeIndex := 0;
  392. end;
  393. if (node.PositiveSubNodeIndex <= 0) and (node.NegativeSubNodeIndex <= 0)
  394. then
  395. begin
  396. if node.VertexIndices.Count = 0 then
  397. begin
  398. if nodeParents[i] >= 0 then
  399. indicesToCheck.Push(nodeParents[i]);
  400. faceGroups.List^[i] := nil;
  401. node.Owner := nil;
  402. node.Free;
  403. end;
  404. end;
  405. end;
  406. end;
  407. // build a remap index
  408. SetLength(remapIndex, n);
  409. j := 0;
  410. for i := 0 to n - 1 do
  411. begin
  412. remapIndex[i] := j;
  413. if faceGroups[i] <> nil then
  414. Inc(j);
  415. end;
  416. // apply remap index
  417. for i := 0 to n - 1 do
  418. begin
  419. node := TFGBSPNode(faceGroups[i]);
  420. if Assigned(node) then
  421. begin
  422. node.PositiveSubNodeIndex := remapIndex[node.PositiveSubNodeIndex];
  423. node.NegativeSubNodeIndex := remapIndex[node.NegativeSubNodeIndex];
  424. end;
  425. end;
  426. // and pack then FaceGroups, done, pfew!! The things we do to remain fast...
  427. faceGroups.Pack;
  428. finally
  429. indicesToCheck.Free;
  430. end;
  431. end;
  432. function TBSPMeshObject.AverageDepth: Single;
  433. var
  434. depthSum, endNodesCount: Integer;
  435. procedure RecurseEstimate(nodeIdx, depth: Integer);
  436. var
  437. node: TFGBSPNode;
  438. begin
  439. node := TFGBSPNode(faceGroups[nodeIdx]);
  440. if node.PositiveSubNodeIndex > 0 then
  441. begin
  442. RecurseEstimate(node.PositiveSubNodeIndex, depth + 1);
  443. if node.NegativeSubNodeIndex > 0 then
  444. RecurseEstimate(node.NegativeSubNodeIndex, depth + 1);
  445. end
  446. else if node.NegativeSubNodeIndex > 0 then
  447. RecurseEstimate(node.NegativeSubNodeIndex, depth + 1)
  448. else
  449. begin
  450. Inc(depthSum, depth);
  451. Inc(endNodesCount);
  452. end;
  453. end;
  454. begin
  455. if faceGroups.Count = 0 then
  456. Result := 1
  457. else
  458. begin
  459. depthSum := 0;
  460. endNodesCount := 0;
  461. RecurseEstimate(0, 1);
  462. Assert(endNodesCount > 0);
  463. Result := depthSum / endNodesCount;
  464. end;
  465. end;
  466. function TBSPMeshObject.FindNodeByPoint(const aPoint: TVector4f): TFGBSPNode;
  467. function Traverse(nodeIndex: Integer): Integer;
  468. var
  469. idx: Integer;
  470. node: TFGBSPNode;
  471. eval: Single;
  472. begin
  473. node := TFGBSPNode(faceGroups[nodeIndex]);
  474. if node.VertexIndices.Count > 0 then
  475. Result := nodeIndex
  476. else
  477. begin
  478. eval := node.splitPlane.X * aPoint.X +
  479. node.splitPlane.Y * aPoint.Y +
  480. node.splitPlane.Z * aPoint.Z -
  481. node.splitPlane.W;
  482. if eval >= 0 then
  483. idx := node.PositiveSubNodeIndex
  484. else
  485. idx := node.NegativeSubNodeIndex;
  486. if idx > 0 then
  487. Result := Traverse(idx)
  488. else
  489. Result := -1;
  490. end;
  491. end;
  492. var
  493. idx: Integer;
  494. begin
  495. Result := nil;
  496. idx := -1;
  497. if faceGroups.Count > 0 then
  498. idx := Traverse(0);
  499. if idx >= 0 then
  500. Result := TFGBSPNode(faceGroups[idx]);
  501. end;
  502. // ------------------
  503. // ------------------ TFGBSPNode ------------------
  504. // ------------------
  505. constructor TFGBSPNode.CreateOwned(AOwner: TgxFaceGroups);
  506. begin
  507. inherited;
  508. FPositiveSubNodeIndex := 0;
  509. FNegativeSubNodeIndex := 0;
  510. end;
  511. destructor TFGBSPNode.Destroy;
  512. begin
  513. inherited;
  514. end;
  515. procedure TFGBSPNode.IsCulled(const bsprci: TBSPRenderContextInfo;
  516. var positive, negative: Boolean);
  517. var
  518. i, n: Integer;
  519. d: Single;
  520. begin
  521. n := Length(bsprci.cullingSpheres);
  522. if n > 0 then
  523. begin
  524. positive := True;
  525. negative := True;
  526. for i := 0 to n - 1 do
  527. with bsprci.cullingSpheres[i] do
  528. begin
  529. d := PlaneEvaluatePoint(splitPlane, position);
  530. if d >= -radius then
  531. positive := False;
  532. if d <= radius then
  533. negative := False;
  534. end;
  535. end
  536. else
  537. begin
  538. positive := False;
  539. negative := False;
  540. end;
  541. end;
  542. procedure TFGBSPNode.CollectNoSort(var bsprci: TBSPRenderContextInfo);
  543. begin
  544. if (PositiveSubNodeIndex > 0) then
  545. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectNoSort(bsprci);
  546. if VertexIndices.Count > 0 then
  547. bsprci.faceGroups.Add(Self);
  548. if (NegativeSubNodeIndex > 0) then
  549. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectNoSort(bsprci);
  550. end;
  551. procedure TFGBSPNode.CollectFrontToBack(var bsprci: TBSPRenderContextInfo);
  552. begin
  553. if PlaneEvaluatePoint(splitPlane, bsprci.cameraLocal) >= 0 then
  554. begin
  555. if PositiveSubNodeIndex > 0 then
  556. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectFrontToBack(bsprci);
  557. if VertexIndices.Count > 0 then
  558. bsprci.faceGroups.Add(Self);
  559. if NegativeSubNodeIndex > 0 then
  560. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectFrontToBack(bsprci);
  561. end
  562. else
  563. begin
  564. if NegativeSubNodeIndex > 0 then
  565. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectFrontToBack(bsprci);
  566. if VertexIndices.Count > 0 then
  567. bsprci.faceGroups.Add(Self);
  568. if PositiveSubNodeIndex > 0 then
  569. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectFrontToBack(bsprci);
  570. end;
  571. end;
  572. procedure TFGBSPNode.CollectBackToFront(var bsprci: TBSPRenderContextInfo);
  573. begin
  574. if PlaneEvaluatePoint(splitPlane, bsprci.cameraLocal) >= 0 then
  575. begin
  576. if NegativeSubNodeIndex > 0 then
  577. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectBackToFront(bsprci);
  578. if VertexIndices.Count > 0 then
  579. bsprci.faceGroups.Add(Self);
  580. if PositiveSubNodeIndex > 0 then
  581. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectBackToFront(bsprci);
  582. end
  583. else
  584. begin
  585. if PositiveSubNodeIndex > 0 then
  586. TFGBSPNode(Owner[PositiveSubNodeIndex]).CollectBackToFront(bsprci);
  587. if VertexIndices.Count > 0 then
  588. bsprci.faceGroups.Add(Self);
  589. if NegativeSubNodeIndex > 0 then
  590. TFGBSPNode(Owner[NegativeSubNodeIndex]).CollectBackToFront(bsprci);
  591. end;
  592. end;
  593. function TFGBSPNode.FindSplitPlane(triangleSplitCost: Single = 1;
  594. triangleImbalanceCost: Single = 0.5): THmgPlane;
  595. var
  596. i, k, n: Integer;
  597. ns, np, nn: Integer;
  598. evalPlane: THmgPlane;
  599. bestEval, eval: Single;
  600. vertices: TgxAffineVectorList;
  601. begin
  602. Result := NullHmgVector;
  603. bestEval := 1E30;
  604. n := VertexIndices.Count;
  605. vertices := Owner.Owner.vertices;
  606. if n > 0 then
  607. for k := 0 to n div 4 do
  608. begin
  609. case Mode of
  610. fgmmTriangles, fgmmFlatTriangles:
  611. begin
  612. i := Random((n div 3) - 1) * 3;
  613. evalPlane := PlaneMake(vertices[VertexIndices[i]],
  614. vertices[VertexIndices[i + 1]], vertices[VertexIndices[i + 2]]);
  615. end;
  616. fgmmTriangleStrip:
  617. begin
  618. i := Random(n - 2);
  619. evalPlane := PlaneMake(vertices[VertexIndices[i]],
  620. vertices[VertexIndices[i + 1]], vertices[VertexIndices[i + 2]]);
  621. end;
  622. else
  623. // fgmmTriangleFan
  624. i := Random(n - 2);
  625. evalPlane := PlaneMake(vertices[VertexIndices[0]],
  626. vertices[VertexIndices[i]], vertices[VertexIndices[i + 1]]);
  627. end;
  628. EvaluateSplitPlane(evalPlane, ns, np, nn);
  629. eval := ns * triangleSplitCost + Abs(np - nn) * 0.5 *
  630. triangleImbalanceCost;
  631. if eval < bestEval then
  632. begin
  633. bestEval := eval;
  634. Result := evalPlane;
  635. end;
  636. end;
  637. end;
  638. procedure TFGBSPNode.EvaluateSplitPlane(const splitPlane: THmgPlane;
  639. var nbTriangleSplit: Integer; var nbPositiveTriangles: Integer;
  640. var nbNegativeTriangles: Integer);
  641. var
  642. i, n, inci, lookupIdx: Integer;
  643. a, b, c: Boolean;
  644. vertices: TgxAffineVectorList;
  645. const
  646. // case resolution lookup tables (node's tris unaccounted for)
  647. cTriangleSplit: array [0 .. 7] of Integer = (0, 1, 1, 1, 1, 1, 1, 0);
  648. cPositiveTris: array [0 .. 7] of Integer = (0, 1, 1, 2, 1, 2, 2, 1);
  649. cNegativeTris: array [0 .. 7] of Integer = (1, 2, 2, 1, 2, 1, 1, 0);
  650. begin
  651. nbTriangleSplit := 0;
  652. nbPositiveTriangles := 0;
  653. nbNegativeTriangles := 0;
  654. n := VertexIndices.Count;
  655. if n < 3 then
  656. Exit;
  657. vertices := Owner.Owner.vertices;
  658. case Mode of
  659. fgmmTriangleStrip, fgmmTriangleFan:
  660. begin
  661. n := n - 2;
  662. inci := 1;
  663. end;
  664. else
  665. inci := 3;
  666. end;
  667. i := 0;
  668. while i < n do
  669. begin
  670. case Mode of
  671. fgmmTriangleFan:
  672. begin
  673. a := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[0]]) > 0;
  674. b := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[i]]) > 0;
  675. c := PlaneEvaluatePoint(splitPlane,
  676. vertices[VertexIndices[i + 1]]) > 0;
  677. end;
  678. else
  679. // fgmmTriangles, fgmmFlatTriangles, fgmmTriangleStrip
  680. a := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[i]]) > 0;
  681. b := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[i + 1]]) > 0;
  682. c := PlaneEvaluatePoint(splitPlane, vertices[VertexIndices[i + 2]]) > 0;
  683. end;
  684. lookupIdx := (Integer(a) shl 2) + (Integer(b) shl 1) + Integer(c);
  685. Inc(nbTriangleSplit, cTriangleSplit[lookupIdx]);
  686. Inc(nbPositiveTriangles, cPositiveTris[lookupIdx]);
  687. Inc(nbNegativeTriangles, cNegativeTris[lookupIdx]);
  688. Inc(i, inci);
  689. end;
  690. end;
  691. function TFGBSPNode.AddLerp(iA, iB: Integer; fB, fA: Single): Integer;
  692. begin
  693. with Owner.Owner do
  694. begin
  695. with vertices do
  696. Result := Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  697. with Normals do
  698. if Count > 0 then
  699. Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  700. with Colors do
  701. if Count > 0 then
  702. Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  703. with TexCoords do
  704. if Count > 0 then
  705. Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  706. with LightMapTexCoords do
  707. if Count > 0 then
  708. Add(VectorCombine(List^[iA], List^[iB], fA, fB));
  709. end;
  710. end;
  711. function TFGBSPNode.AddLerpIfDistinct(iA, iB, iMid: Integer): Integer;
  712. var
  713. midNormal: TAffineVector;
  714. midColor: TgxColorVector;
  715. midTexCoord: TAffineVector;
  716. midLightmapTexCoord: TAffineVector;
  717. f: Single;
  718. spawn: Boolean;
  719. begin
  720. with Owner.Owner do
  721. begin
  722. with vertices do
  723. f := VectorDistance(List^[iA], List^[iMid]) / VectorDistance(List^[iA],
  724. List^[iB]);
  725. spawn := False;
  726. with Normals do
  727. if Count > 0 then
  728. begin
  729. midNormal := VectorLerp(List^[iA], List^[iB], f);
  730. spawn := (VectorSpacing(midNormal, List^[iMid]) > cTJunctionEpsilon);
  731. end;
  732. with Colors do
  733. if Count > 0 then
  734. begin
  735. midColor := VectorLerp(List^[iA], List^[iB], f);
  736. spawn := spawn or (VectorSpacing(midColor, List^[iMid]) >
  737. cTJunctionEpsilon);
  738. end;
  739. with TexCoords do
  740. if Count > 0 then
  741. begin
  742. midTexCoord := VectorLerp(List^[iA], List^[iB], f);
  743. spawn := spawn or (VectorSpacing(midTexCoord, List^[iMid]) >
  744. cTJunctionEpsilon);
  745. end;
  746. with LightMapTexCoords do
  747. if Count > 0 then
  748. begin
  749. midLightmapTexCoord := VectorLerp(List^[iA], List^[iB], f);
  750. spawn := spawn or (VectorSpacing(midLightmapTexCoord, List^[iMid]) >
  751. cTJunctionEpsilon);
  752. end;
  753. if spawn then
  754. begin
  755. with vertices do
  756. Result := Add(List^[iMid]);
  757. with Normals do
  758. if Count > 0 then
  759. Add(midNormal);
  760. with Colors do
  761. if Count > 0 then
  762. Add(midColor);
  763. with TexCoords do
  764. if Count > 0 then
  765. Add(midTexCoord);
  766. with LightMapTexCoords do
  767. if Count > 0 then
  768. Add(midLightmapTexCoord);
  769. end
  770. else
  771. Result := iMid;
  772. end;
  773. end;
  774. procedure TFGBSPNode.PerformSplit(const splitPlane: THmgPlane;
  775. const maxTrianglesPerLeaf: Integer = MaxInt);
  776. var
  777. fgPos, fgNeg: TFGBSPNode;
  778. fgPosIndices, fgNegIndices: TgxIntegerList;
  779. indices: TgxIntegerList;
  780. procedure SplitTriangleMid(strayID, strayNext, strayPrev: Integer;
  781. eNext, ePrev: Single);
  782. var
  783. iOpp: Integer;
  784. invSum: Single;
  785. begin
  786. invSum := 1 / (Abs(eNext) + Abs(ePrev));
  787. iOpp := AddLerp(strayNext, strayPrev, Abs(eNext) * invSum,
  788. Abs(ePrev) * invSum);
  789. if eNext > 0 then
  790. begin
  791. fgPosIndices.Add(strayID, strayNext, iOpp);
  792. fgNegIndices.Add(iOpp, strayPrev, strayID);
  793. end
  794. else
  795. begin
  796. fgNegIndices.Add(strayID, strayNext, iOpp);
  797. fgPosIndices.Add(iOpp, strayPrev, strayID);
  798. end;
  799. end;
  800. procedure SplitTriangle(strayID, strayNext, strayPrev: Integer;
  801. eStray, eNext, ePrev: Single);
  802. var
  803. iNext, iPrev: Integer;
  804. invSum: Single;
  805. begin
  806. invSum := 1 / (Abs(eNext) + Abs(eStray));
  807. iNext := AddLerp(strayNext, strayID, Abs(eNext) * invSum,
  808. Abs(eStray) * invSum);
  809. invSum := 1 / (Abs(ePrev) + Abs(eStray));
  810. iPrev := AddLerp(strayPrev, strayID, Abs(ePrev) * invSum,
  811. Abs(eStray) * invSum);
  812. if eStray > 0 then
  813. begin
  814. fgPos.VertexIndices.Add(strayID, iNext, iPrev);
  815. fgNeg.VertexIndices.Add(strayNext, strayPrev, iPrev);
  816. fgNeg.VertexIndices.Add(iPrev, iNext, strayNext);
  817. end
  818. else if eStray < 0 then
  819. begin
  820. fgNeg.VertexIndices.Add(strayID, iNext, iPrev);
  821. fgPos.VertexIndices.Add(strayNext, strayPrev, iPrev);
  822. fgPos.VertexIndices.Add(iPrev, iNext, strayNext);
  823. end;
  824. end;
  825. var
  826. i, i1, i2, i3, se1, se2, se3: Integer;
  827. e1, e2, e3: Single;
  828. vertices: TgxAffineVectorList;
  829. subSplitPlane: THmgPlane;
  830. begin
  831. Assert((PositiveSubNodeIndex = 0) and (NegativeSubNodeIndex = 0));
  832. ConvertToList;
  833. // prepare sub nodes
  834. FPositiveSubNodeIndex := Owner.Count;
  835. fgPos := TFGBSPNode.CreateOwned(Owner);
  836. fgPosIndices := fgPos.VertexIndices;
  837. FNegativeSubNodeIndex := Owner.Count;
  838. fgNeg := TFGBSPNode.CreateOwned(Owner);
  839. fgNegIndices := fgNeg.VertexIndices;
  840. // initiate split
  841. Self.FSplitPlane := splitPlane;
  842. indices := TgxIntegerList.Create;
  843. vertices := Owner.Owner.vertices;
  844. i := 0;
  845. while i < VertexIndices.Count do
  846. begin
  847. // evaluate all points
  848. i1 := VertexIndices[i];
  849. e1 := PlaneEvaluatePoint(splitPlane, vertices.List^[i1]);
  850. i2 := VertexIndices[i + 1];
  851. e2 := PlaneEvaluatePoint(splitPlane, vertices.List^[i2]);
  852. i3 := VertexIndices[i + 2];
  853. e3 := PlaneEvaluatePoint(splitPlane, vertices.List^[i3]);
  854. if Abs(e1) < cOwnTriangleEpsilon then
  855. begin
  856. e1 := 0;
  857. se1 := 0;
  858. end
  859. else
  860. se1 := Sign(e1);
  861. if Abs(e2) < cOwnTriangleEpsilon then
  862. begin
  863. e2 := 0;
  864. se2 := 0;
  865. end
  866. else
  867. se2 := Sign(e2);
  868. if Abs(e3) < cOwnTriangleEpsilon then
  869. begin
  870. e3 := 0;
  871. se3 := 0;
  872. end
  873. else
  874. se3 := Sign(e3);
  875. // case disjunction
  876. case se1 of
  877. - 1:
  878. case se2 of
  879. - 1:
  880. case se3 of
  881. - 1, 0:
  882. fgNegIndices.Add(i1, i2, i3);
  883. +1:
  884. SplitTriangle(i3, i1, i2, e3, e1, e2);
  885. end;
  886. 0:
  887. case se3 of
  888. - 1, 0:
  889. fgNegIndices.Add(i1, i2, i3);
  890. +1:
  891. SplitTriangleMid(i2, i3, i1, e3, e1);
  892. end;
  893. +1:
  894. case se3 of
  895. - 1:
  896. SplitTriangle(i2, i3, i1, e2, e3, e1);
  897. 0:
  898. SplitTriangleMid(i3, i1, i2, e1, e2);
  899. +1:
  900. SplitTriangle(i1, i2, i3, e1, e2, e3);
  901. end;
  902. end;
  903. 0:
  904. case se2 of
  905. - 1:
  906. case se3 of
  907. - 1, 0:
  908. fgNegIndices.Add(i1, i2, i3);
  909. +1:
  910. SplitTriangleMid(i1, i2, i3, e2, e3);
  911. end;
  912. 0:
  913. case se3 of
  914. - 1:
  915. fgNegIndices.Add(i1, i2, i3);
  916. 0:
  917. indices.Add(i1, i2, i3);
  918. +1:
  919. fgPosIndices.Add(i1, i2, i3);
  920. end;
  921. +1:
  922. case se3 of
  923. - 1:
  924. SplitTriangleMid(i1, i2, i3, e2, e3);
  925. 0, +1:
  926. fgPosIndices.Add(i1, i2, i3);
  927. end;
  928. end;
  929. +1:
  930. case se2 of
  931. - 1:
  932. case se3 of
  933. - 1:
  934. SplitTriangle(i1, i2, i3, e1, e2, e3);
  935. 0:
  936. SplitTriangleMid(i3, i1, i2, e1, e2);
  937. +1:
  938. SplitTriangle(i2, i3, i1, e2, e3, e1);
  939. end;
  940. 0:
  941. case se3 of
  942. - 1:
  943. SplitTriangleMid(i2, i3, i1, e3, e1);
  944. 0, +1:
  945. fgPosIndices.Add(i1, i2, i3);
  946. end;
  947. +1:
  948. case se3 of
  949. - 1:
  950. SplitTriangle(i3, i1, i2, e3, e1, e2);
  951. 0, +1:
  952. fgPosIndices.Add(i1, i2, i3);
  953. end;
  954. end;
  955. end;
  956. Inc(i, 3);
  957. end;
  958. VertexIndices := indices;
  959. indices.Free;
  960. if fgPos.TriangleCount = 0 then
  961. begin
  962. FPositiveSubNodeIndex := 0;
  963. FNegativeSubNodeIndex := FNegativeSubNodeIndex - 1;
  964. FreeAndNil(fgPos);
  965. end;
  966. if fgNeg.TriangleCount = 0 then
  967. begin
  968. FNegativeSubNodeIndex := 0;
  969. FreeAndNil(fgNeg);
  970. end;
  971. if Assigned(fgPos) and (fgPos.TriangleCount > maxTrianglesPerLeaf) then
  972. begin
  973. subSplitPlane := fgPos.FindSplitPlane;
  974. fgPos.PerformSplit(subSplitPlane, maxTrianglesPerLeaf);
  975. end;
  976. if Assigned(fgNeg) and (fgNeg.TriangleCount > maxTrianglesPerLeaf) then
  977. begin
  978. subSplitPlane := fgNeg.FindSplitPlane;
  979. fgNeg.PerformSplit(subSplitPlane, maxTrianglesPerLeaf);
  980. end;
  981. end;
  982. procedure TFGBSPNode.FixTJunctions(const tJunctionsCandidates: TgxIntegerList);
  983. function FindTJunction(iA, iB, iC: Integer;
  984. candidatesList: TgxIntegerList): Integer;
  985. var
  986. i, k: Integer;
  987. vertices: PAffineVectorArray;
  988. candidate, vA, vB: PAffineVector;
  989. boxMin, boxMax, vector, invVector: TAffineVector;
  990. f: TAffineVector;
  991. begin
  992. Result := -1;
  993. vertices := Owner.Owner.vertices.List;
  994. vA := @vertices[iA];
  995. vB := @vertices[iB];
  996. // compute bounding box of the segment
  997. boxMin := vA^;
  998. MinVector(boxMin, vB^);
  999. boxMax := vA^;
  1000. MaxVector(boxMax, vB^);
  1001. // compute extent and its inversion
  1002. vector := VectorSubtract(vB^, vA^);
  1003. for i := 0 to 2 do
  1004. if vector.V[i] <> 0 then
  1005. invVector.V[i] := 1 / vector.V[i]
  1006. else
  1007. invVector.V[i] := 0;
  1008. // lookup all candidates
  1009. for i := 0 to candidatesList.Count - 1 do
  1010. begin
  1011. k := candidatesList.List^[i];
  1012. if (k = iA) or (k = iB) or (k = iC) then
  1013. Continue;
  1014. candidate := @vertices[k];
  1015. if (candidate^.X > boxMin.X) and
  1016. (candidate^.Y > boxMin.Y) and
  1017. (candidate^.Z > boxMin.Z) and
  1018. (candidate^.X < boxMax.X) and
  1019. (candidate^.Y < boxMax.Y) and
  1020. (candidate^.Z < boxMax.Z) then
  1021. begin
  1022. f := candidate^;
  1023. SubtractVector(f, vA^);
  1024. ScaleVector(f, invVector);
  1025. if (Abs(f.X - f.Y) < cTJunctionEpsilon) and
  1026. (Abs(f.X - f.Z) < cTJunctionEpsilon) and
  1027. (Abs(f.Y - f.Z) < cTJunctionEpsilon) then
  1028. begin
  1029. Result := AddLerpIfDistinct(iA, iB, k);
  1030. Break;
  1031. end;
  1032. end;
  1033. end;
  1034. end;
  1035. var
  1036. i, tj: Integer;
  1037. indices: PIntegerArray;
  1038. mark: TgxIntegerList;
  1039. begin
  1040. Assert(Mode in [fgmmTriangles, fgmmFlatTriangles]);
  1041. mark := TgxIntegerList.Create;
  1042. mark.AddSerie(1, 0, VertexIndices.Count);
  1043. indices := VertexIndices.List;
  1044. i := 0;
  1045. while i < VertexIndices.Count do
  1046. begin
  1047. if mark[i] <> 0 then
  1048. begin
  1049. tj := FindTJunction(indices^[i], indices^[i + 1], indices^[i + 2],
  1050. tJunctionsCandidates);
  1051. if tj >= 0 then
  1052. begin
  1053. VertexIndices.Add(tj, VertexIndices[i + 1], VertexIndices[i + 2]);
  1054. mark.Add(1, 1, 0);
  1055. indices := VertexIndices.List;
  1056. indices^[i + 1] := tj;
  1057. mark[i + 1] := 0;
  1058. Continue;
  1059. end;
  1060. end;
  1061. if mark[i + 1] <> 0 then
  1062. begin
  1063. tj := FindTJunction(indices^[i + 1], indices^[i + 2], indices^[i],
  1064. tJunctionsCandidates);
  1065. if tj >= 0 then
  1066. begin
  1067. VertexIndices.Add(tj, VertexIndices[i + 2], VertexIndices[i]);
  1068. mark.Add(1, 1, 0);
  1069. indices := VertexIndices.List;
  1070. indices^[i + 2] := tj;
  1071. mark[i + 2] := 0;
  1072. Continue;
  1073. end;
  1074. end;
  1075. if mark[i + 2] <> 0 then
  1076. begin
  1077. tj := FindTJunction(indices^[i + 2], indices^[i], indices^[i + 1],
  1078. tJunctionsCandidates);
  1079. if tj >= 0 then
  1080. begin
  1081. VertexIndices.Add(tj, VertexIndices[i], VertexIndices[i + 1]);
  1082. mark.Add(1, 1, 0);
  1083. indices := VertexIndices.List;
  1084. indices^[i] := tj;
  1085. mark[i] := 0;
  1086. Continue;
  1087. end;
  1088. end;
  1089. Inc(i, 3);
  1090. end;
  1091. mark.Free;
  1092. end;
  1093. // ------------------------------------------------------------------
  1094. initialization
  1095. // ------------------------------------------------------------------
  1096. RegisterClasses([TBSPMeshObject, TFGBSPNode]);
  1097. end.