GLS.MeshCSG.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.MeshCSG;
  5. (*
  6. Constructive Solid Geometry on mesh base.
  7. The CSG system uses BSP to optimize what triangles it considers.
  8. Its kept on a mesh basis to simplyfy things, it allways generates new BSP's,
  9. even if the meshes allready had BSP optimization.
  10. *)
  11. interface
  12. {$I GLScene.inc}
  13. uses
  14. System.SysUtils,
  15. System.Classes,
  16. System.Math,
  17. GLS.Scene,
  18. GLS.VectorTypes,
  19. GLS.VectorFileObjects,
  20. GLS.VectorGeometry,
  21. GLS.MeshBSP,
  22. GLS.VectorLists;
  23. type
  24. TCSGOperation = (CSG_Union, CSG_Subtraction, CSG_Intersection);
  25. procedure CSG_Operation(obj1, obj2: TGLMeshObject; Operation: TCSGOperation; Res: TGLMeshObject; const MaterialName1, MaterialName2: string);
  26. //-----------------------------------------------------------------------------
  27. implementation
  28. //-----------------------------------------------------------------------------
  29. const
  30. cOwnTriangleEpsilon = 1e-5;
  31. function IntersectPointToPointLinePlane(const point1, point2: TAffineVector; const plane: THmgPlane; intersectPoint: PAffineVector = nil): Boolean;
  32. var
  33. a, b: Extended;
  34. t: Single;
  35. Direction: TAffineVector;
  36. begin
  37. Result := False;
  38. VectorSubtract(Point2, Point1, Direction);
  39. a := VectorDotProduct(plane, Direction); // direction projected to plane normal
  40. if a <> 0 then
  41. begin // direction is parallel to plane
  42. b := PlaneEvaluatePoint(plane, point1); // distance to plane
  43. t := -b / a; // parameter of intersection
  44. Result := (t - EPSILON2 > 0) and (t + EPSILON2 < 1);
  45. if Result and (intersectPoint <> nil) then
  46. begin
  47. intersectPoint^ := VectorCombine(Point1, Direction, 1, t);
  48. if VectorEquals(intersectPoint^, point1) or VectorEquals(intersectPoint^, point2) then
  49. Result := False;
  50. end;
  51. end;
  52. end;
  53. type
  54. TCSGTri = array[0..2] of PAffineVector;
  55. function MakeCSGTri(const V1, V2, V3: PAffineVector): TCSGTri;
  56. begin
  57. Result[0] := v1;
  58. Result[1] := v2;
  59. Result[2] := v3;
  60. end;
  61. procedure CSG_Iterate_tri(const vec, nor: TCSGTri; BSP: TBSPMeshObject;
  62. Node: TFGBSPNode; ResMesh: TGLMeshObject; ResFG: TFGVertexNormalTexIndexList; keepinside, keepoutside, inverttriangle: Boolean);
  63. var
  64. vertex_offset: Integer;
  65. function Iterate_further(const vec, nor: TCSGTri; Node: TFGBSPNode): Boolean;
  66. function MustSplit(d1, d2, d3: Single): Integer;
  67. var
  68. side: Integer;
  69. function Ok(Int: Single): Boolean;
  70. begin
  71. Result := False;
  72. if Int < 0 then
  73. begin
  74. if Side > 0 then
  75. begin
  76. Result := True;
  77. Exit;
  78. end
  79. else
  80. Side := -1;
  81. end
  82. else if Int > 0 then
  83. begin
  84. if Side < 0 then
  85. begin
  86. Result := True;
  87. Exit;
  88. end
  89. else
  90. Side := 1;
  91. end;
  92. end;
  93. begin
  94. Result := 0; // no split
  95. side := 0;
  96. Ok(D1); // can't go wrong yet...
  97. if Ok(D2) then
  98. begin
  99. Result := 1; // pure split.
  100. Exit;
  101. end;
  102. if Ok(D3) then
  103. begin
  104. Result := 1; // pure split.
  105. Exit;
  106. end;
  107. if side = 0 then
  108. begin
  109. Result := 2; // on plane.
  110. Exit;
  111. end;
  112. end;
  113. var
  114. d: array[0..2] of Single;
  115. i, i1: Integer;
  116. b1, b2, b3: Boolean;
  117. intersect_points: array[0..2] of TAffineVector;
  118. intersect_lines: array[0..2] of Integer;
  119. intersect_count: Integer;
  120. p0, p1: Integer;
  121. NextNode: TFGBSPNode;
  122. plane: THmgPlane;
  123. begin
  124. // This have no effect, however it removes a warning...
  125. Result := False;
  126. b1 := False;
  127. b2 := False;
  128. b3 := False;
  129. // This have no effect, however it removes a warning...
  130. // normally we use the Node.SplitPlane, however on the last branch this is a NullPlane, so we have to calculate it.
  131. if VectorEquals(Node.SplitPlane, NullHmgVector) then
  132. plane := PlaneMake(BSP.Vertices[Node.VertexIndices[0]], BSP.Vertices[Node.VertexIndices[1]], BSP.Vertices[Node.VertexIndices[2]])
  133. else
  134. plane := Node.SplitPlane;
  135. // check the three points in the triangle against the plane
  136. for i := 0 to 2 do
  137. begin
  138. d[i] := PlaneEvaluatePoint(plane, Vec[i]^);
  139. if Abs(d[i]) < cOwnTriangleEpsilon then
  140. d[i] := 0;
  141. end;
  142. // based on points placement determine action
  143. case MustSplit(d[0], d[1], d[2]) of
  144. 0: // no split
  145. if (d[0] + d[1] + d[2] >= 0) then
  146. begin
  147. // check for sub node, and either iterate them or stop if none.
  148. if Node.PositiveSubNodeIndex = 0 then
  149. Result := keepoutside
  150. else
  151. Result := Iterate_further(Vec, nor, TFGBSPNode(BSP.FaceGroups[Node.PositiveSubNodeIndex]));
  152. end
  153. else
  154. begin
  155. // check for sub node, and either iterate them or stop if none.
  156. if Node.NegativeSubNodeIndex = 0 then
  157. Result := keepinside
  158. else
  159. Result := Iterate_further(Vec, nor, TFGBSPNode(BSP.FaceGroups[Node.NegativeSubNodeIndex]));
  160. end;
  161. 1: // must split.
  162. begin
  163. // determine if 2 or 3 triangles are needed for the split.
  164. intersect_count := 0;
  165. for i := 0 to 2 do
  166. begin
  167. i1 := (i + 1) mod 3;
  168. if IntersectPointToPointLinePlane(Vec[i]^, Vec[i1]^, plane, @intersect_points[intersect_count]) then
  169. begin
  170. intersect_lines[intersect_count] := i;
  171. Inc(intersect_count);
  172. end;
  173. end;
  174. // from here of its not thoroughly commented
  175. // the base concept is isolate the two or three triangles, findout which to keep.
  176. // If all is kept we can simply return true and the original triangle wont be split.
  177. // however if only some are needed, we have to return false and add them ourselves...
  178. case intersect_count of
  179. 1:
  180. begin
  181. // simple split, two tri's
  182. i := intersect_lines[0];
  183. i1 := (i + 2) mod 3;
  184. // cannot be 0, as then intersect_lines[0] would have other value
  185. if (d[i] > 0) then
  186. if Node.PositiveSubNodeIndex = 0 then
  187. begin
  188. NextNode := nil;
  189. b1 := keepoutside;
  190. end
  191. else
  192. NextNode := TFGBSPNode(BSP.FaceGroups[Node.PositiveSubNodeIndex])
  193. else if Node.NegativeSubNodeIndex = 0 then
  194. begin
  195. NextNode := nil;
  196. b1 := keepinside;
  197. end
  198. else
  199. NextNode := TFGBSPNode(BSP.FaceGroups[Node.NegativeSubNodeIndex]);
  200. if NextNode <> nil then
  201. b1 := Iterate_further(MakeCSGTri(Vec[i], @intersect_points[0], Vec[i1]), MakeCSGTri(Nor[i], Nor[i {}], Nor[i1]), NextNode);
  202. i := (intersect_lines[0] + 1) mod 3;
  203. i1 := (i + 1) mod 3;
  204. // cannot be 0, as then intersect_lines[0] would have other value
  205. if (d[i] > 0) then
  206. if Node.PositiveSubNodeIndex = 0 then
  207. begin
  208. NextNode := nil;
  209. b2 := keepoutside;
  210. end
  211. else
  212. NextNode := TFGBSPNode(BSP.FaceGroups[Node.PositiveSubNodeIndex])
  213. else if Node.NegativeSubNodeIndex = 0 then
  214. begin
  215. NextNode := nil;
  216. b2 := keepinside;
  217. end
  218. else
  219. NextNode := TFGBSPNode(BSP.FaceGroups[Node.NegativeSubNodeIndex]);
  220. if NextNode <> nil then
  221. b2 := Iterate_further(MakeCSGTri(Vec[i], Vec[i1], @intersect_points[0]), MakeCSGTri(Nor[i], Nor[i1], Nor[i {}]), NextNode);
  222. Result := b1 and b2;
  223. if not Result then
  224. begin
  225. if B1 then
  226. begin
  227. i := intersect_lines[0];
  228. i1 := (i + 2) mod 3;
  229. vertex_offset := ResMesh.Vertices.count;
  230. ResMesh.Vertices.Add(Vec[i]^, intersect_points[0], Vec[i1]^);
  231. ResMesh.TexCoords.Add(Vec[i]^, intersect_points[0], Vec[i1]^);
  232. if inverttriangle then
  233. begin
  234. ResMesh.Normals.Add(VectorScale(Nor[i]^, -1), VectorScale(Nor[i {}]^, -1), VectorScale(Nor[i1]^, -1));
  235. ResFG.VertexIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  236. ResFG.NormalIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  237. ResFG.TexCoordIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  238. end
  239. else
  240. begin
  241. ResMesh.Normals.Add(Nor[i]^, Nor[i {}]^, Nor[i1]^);
  242. ResFG.VertexIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  243. ResFG.NormalIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  244. ResFG.TexCoordIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  245. end;
  246. end
  247. else if B2 then
  248. begin
  249. i := (intersect_lines[0] + 1) mod 3;
  250. i1 := (i + 1) mod 3;
  251. vertex_offset := ResMesh.Vertices.count;
  252. ResMesh.Vertices.Add(Vec[i]^, Vec[i1]^, intersect_points[0]);
  253. ResMesh.TexCoords.Add(Vec[i]^, Vec[i1]^, intersect_points[0]);
  254. if inverttriangle then
  255. begin
  256. ResMesh.Normals.Add(VectorScale(Nor[i]^, -1), VectorScale(Nor[i {}]^, -1), VectorScale(Nor[i1]^, -1));
  257. ResFG.VertexIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  258. ResFG.NormalIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  259. ResFG.TexCoordIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  260. end
  261. else
  262. begin
  263. ResMesh.Normals.Add(Nor[i]^, Nor[i {}]^, Nor[i1]^);
  264. ResFG.VertexIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  265. ResFG.NormalIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  266. ResFG.TexCoordIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  267. end;
  268. end;
  269. end;
  270. end;
  271. 2:
  272. begin
  273. // complex split, three tri's
  274. if intersect_lines[0] + 1 = intersect_lines[1] then
  275. begin
  276. p0 := 0;
  277. p1 := 1;
  278. end
  279. else
  280. begin
  281. p0 := 1;
  282. p1 := 0;
  283. end;
  284. i := intersect_lines[p0];
  285. i1 := (i + 2) mod 3;
  286. // cannot be 0 as then there would be no intersection
  287. if (d[i] > 0) then
  288. if Node.PositiveSubNodeIndex = 0 then
  289. begin
  290. NextNode := nil;
  291. b1 := keepoutside;
  292. end
  293. else
  294. NextNode := TFGBSPNode(BSP.FaceGroups[Node.PositiveSubNodeIndex])
  295. else if Node.NegativeSubNodeIndex = 0 then
  296. begin
  297. NextNode := nil;
  298. b1 := keepinside;
  299. end
  300. else
  301. NextNode := TFGBSPNode(BSP.FaceGroups[Node.NegativeSubNodeIndex]);
  302. if NextNode <> nil then
  303. b1 := Iterate_further(MakeCSGTri(Vec[i], @intersect_points[p0], Vec[i1]), MakeCSGTri(Nor[i], Nor[i {}], Nor[i1]), NextNode);
  304. i1 := (i + 1) mod 3;
  305. // cannot be 0 as then there would be no intersection
  306. if (d[i1] > 0) then
  307. if Node.PositiveSubNodeIndex = 0 then
  308. begin
  309. NextNode := nil;
  310. b2 := keepoutside;
  311. end
  312. else
  313. NextNode := TFGBSPNode(BSP.FaceGroups[Node.PositiveSubNodeIndex])
  314. else if Node.NegativeSubNodeIndex = 0 then
  315. begin
  316. NextNode := nil;
  317. b2 := keepinside;
  318. end
  319. else
  320. NextNode := TFGBSPNode(BSP.FaceGroups[Node.NegativeSubNodeIndex]);
  321. if NextNode <> nil then
  322. b2 := Iterate_further(MakeCSGTri(@intersect_points[p0], Vec[i1], @intersect_points[p1]), MakeCSGTri(Nor[i1 {}], Nor[i1], Nor[i1 {}]), NextNode);
  323. i1 := (i + 2) mod 3;
  324. // cannot be 0 as then there would be no intersection
  325. if (d[i1] > 0) then
  326. if Node.PositiveSubNodeIndex = 0 then
  327. begin
  328. NextNode := nil;
  329. b3 := keepoutside;
  330. end
  331. else
  332. NextNode := TFGBSPNode(BSP.FaceGroups[Node.PositiveSubNodeIndex])
  333. else if Node.NegativeSubNodeIndex = 0 then
  334. begin
  335. NextNode := nil;
  336. b3 := keepinside;
  337. end
  338. else
  339. NextNode := TFGBSPNode(BSP.FaceGroups[Node.NegativeSubNodeIndex]);
  340. if NextNode <> nil then
  341. b3 := Iterate_further(MakeCSGTri(@intersect_points[p0], @intersect_points[p1], Vec[i1]), MakeCSGTri(Nor[i1 {}], Nor[i1 {}], Nor[i1]), NextNode);
  342. Result := b1 and b2 and b3;
  343. if not Result then
  344. begin
  345. if B1 then
  346. begin
  347. i1 := (i + 2) mod 3;
  348. vertex_offset := ResMesh.Vertices.count;
  349. ResMesh.Vertices.Add(Vec[i]^, intersect_points[p0], Vec[i1]^);
  350. ResMesh.TexCoords.Add(Vec[i]^, intersect_points[p0], Vec[i1]^);
  351. if inverttriangle then
  352. begin
  353. ResMesh.Normals.Add(VectorScale(Nor[i]^, -1), VectorScale(Nor[i {}]^, -1), VectorScale(Nor[i1]^, -1));
  354. ResFG.VertexIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  355. ResFG.NormalIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  356. ResFG.TexCoordIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  357. end
  358. else
  359. begin
  360. ResMesh.Normals.Add(Nor[i]^, Nor[i {}]^, Nor[i1]^);
  361. ResFG.VertexIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  362. ResFG.NormalIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  363. ResFG.TexCoordIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  364. end;
  365. end;
  366. if B2 then
  367. begin
  368. i1 := (i + 1) mod 3;
  369. vertex_offset := ResMesh.Vertices.count;
  370. ResMesh.Vertices.Add(intersect_points[p0], Vec[i1]^, intersect_points[p1]);
  371. ResMesh.TexCoords.Add(intersect_points[p0], Vec[i1]^, intersect_points[p1]);
  372. if inverttriangle then
  373. begin
  374. ResMesh.Normals.Add(VectorScale(Nor[i1 {}]^, -1), VectorScale(Nor[i1]^, -1), VectorScale(Nor[i1 {}]^, -1));
  375. ResFG.VertexIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  376. ResFG.NormalIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  377. ResFG.TexCoordIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  378. end
  379. else
  380. begin
  381. ResMesh.Normals.Add(Nor[i1 {}]^, Nor[i1]^, Nor[i1 {}]^);
  382. ResFG.VertexIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  383. ResFG.NormalIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  384. ResFG.TexCoordIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  385. end;
  386. end;
  387. if B3 then
  388. begin
  389. i1 := (i + 2) mod 3;
  390. vertex_offset := ResMesh.Vertices.count;
  391. ResMesh.Vertices.Add(intersect_points[p0], intersect_points[p1], Vec[i1]^);
  392. ResMesh.TexCoords.Add(intersect_points[p0], intersect_points[p1], Vec[i1]^);
  393. if inverttriangle then
  394. begin
  395. ResMesh.Normals.Add(VectorScale(Nor[i1 {}]^, -1), VectorScale(Nor[i1 {}]^, -1), VectorScale(Nor[i1]^, -1));
  396. ResFG.VertexIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  397. ResFG.NormalIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  398. ResFG.TexCoordIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  399. end
  400. else
  401. begin
  402. ResMesh.Normals.Add(Nor[i1 {}]^, Nor[i1 {}]^, Nor[i1]^);
  403. ResFG.VertexIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  404. ResFG.NormalIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  405. ResFG.TexCoordIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  406. end;
  407. end;
  408. end;
  409. end;
  410. end;
  411. end;
  412. 2: // on plane, no split but special logic
  413. begin
  414. // find out if they point the same direction.
  415. d[0] := PlaneEvaluatePoint(Plane, VectorAdd(Vec[0]^, Nor[0]^));
  416. // check for sub node, and either iterate them or stop if none.
  417. if d[0] >= 0 then
  418. if Node.PositiveSubNodeIndex = 0 then
  419. begin
  420. NextNode := nil;
  421. Result := keepoutside;
  422. end
  423. else
  424. NextNode := TFGBSPNode(BSP.FaceGroups[Node.PositiveSubNodeIndex])
  425. else if Node.NegativeSubNodeIndex = 0 then
  426. begin
  427. NextNode := nil;
  428. Result := keepinside;
  429. end
  430. else
  431. NextNode := TFGBSPNode(BSP.FaceGroups[Node.NegativeSubNodeIndex]);
  432. if NextNode <> nil then
  433. Result := Iterate_further(Vec, Nor, NextNode);
  434. end;
  435. end;
  436. end;
  437. begin
  438. // check this triangle.
  439. if Iterate_Further(Vec, nor, Node) then
  440. begin
  441. // Keep this triangle, logic based on the (keepinside, keepoutside) booleans.
  442. vertex_offset := ResMesh.Vertices.count;
  443. ResMesh.Vertices.Add(Vec[0]^, Vec[1]^, Vec[2]^);
  444. ResMesh.TexCoords.Add(Vec[0]^, Vec[1]^, Vec[2]^);
  445. if inverttriangle then
  446. begin
  447. ResMesh.Normals.Add(VectorScale(Nor[0]^, -1), VectorScale(Nor[1]^, -1), VectorScale(Nor[2]^, -1));
  448. ResFG.VertexIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  449. ResFG.NormalIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  450. ResFG.TexCoordIndices.Add(vertex_offset + 2, vertex_offset + 1, vertex_offset);
  451. end
  452. else
  453. begin
  454. ResMesh.Normals.Add(Nor[0]^, Nor[1]^, Nor[2]^);
  455. ResFG.VertexIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  456. ResFG.NormalIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  457. ResFG.TexCoordIndices.Add(vertex_offset, vertex_offset + 1, vertex_offset + 2);
  458. end;
  459. end;
  460. end;
  461. procedure CSG_Operation(obj1, obj2: TGLMeshObject; Operation: TCSGOperation;
  462. Res: TGLMeshObject; const MaterialName1, MaterialName2: string);
  463. var
  464. v1, t1, n1: TGLAffineVectorList;
  465. v2, t2, n2: TGLAffineVectorList;
  466. BSP1, BSP2: TBSPMeshObject;
  467. FG1, FG2: TFGBSPNode;
  468. i: Integer;
  469. FGR: TFGVertexNormalTexIndexList;
  470. begin
  471. // prepare containers, fill the triangle list from the objects
  472. BSP1 := TBSPMeshObject.Create;
  473. BSP2 := TBSPMeshObject.Create;
  474. BSP1.Mode := momFaceGroups;
  475. BSP2.Mode := momFaceGroups;
  476. FG1 := TFGBSPNode.CreateOwned(BSP1.FaceGroups);
  477. FG2 := TFGBSPNode.CreateOwned(BSP2.FaceGroups);
  478. t1 := TGLAffineVectorList.Create;
  479. n1 := TGLAffineVectorList.Create;
  480. v1 := obj1.ExtractTriangles(t1, n1);
  481. v1.TransformAsPoints(obj1.Owner.Owner.Matrix^);
  482. BSP1.Mode := momTriangles;
  483. BSP1.Vertices := v1;
  484. BSP1.Normals := n1;
  485. BSP1.TexCoords := t1;
  486. FG1.VertexIndices.AddSerie(0, 1, BSP1.Vertices.Count);
  487. t2 := TGLAffineVectorList.Create;
  488. n2 := TGLAffineVectorList.Create;
  489. v2 := obj2.ExtractTriangles(t2, n2);
  490. v2.TransformAsPoints(obj2.Owner.Owner.Matrix^);
  491. BSP2.Mode := momTriangles;
  492. BSP2.Vertices := v2;
  493. BSP2.Normals := n2;
  494. BSP2.TexCoords := t2;
  495. FG2.VertexIndices.AddSerie(0, 1, BSP2.Vertices.Count);
  496. // Build BSPs
  497. FG1.PerformSplit(FG1.FindSplitPlane, 1);
  498. FG2.PerformSplit(FG2.FindSplitPlane, 1);
  499. // Start creating result.
  500. FGR := TFGVertexNormalTexIndexList.CreateOwned(Res.FaceGroups);
  501. FGR.MaterialName := MaterialName1;
  502. // should be obj1.FaceGroups iteration for perfection and multiple materials!
  503. // First iterate all triangles of object 1, one at a time, down through the BSP tree of Object 2, the last booleans are the key to what actuelly happends.
  504. i := 0;
  505. while i < v1.Count - 2 do
  506. begin
  507. case Operation of
  508. CSG_Union:
  509. begin
  510. CSG_Iterate_tri(MakeCSGTri(@v1.List^[i], @v1.List^[i + 1], @v1.List^[i + 2]), MakeCSGTri(@n1.List^[i], @n1.List^[i + 1], @n1.List^[i + 2]), BSP2, FG2, Res, FGR, false, true, false);
  511. end;
  512. CSG_Subtraction:
  513. begin
  514. CSG_Iterate_tri(MakeCSGTri(@v1.List^[i], @v1.List^[i + 1], @v1.List^[i + 2]), MakeCSGTri(@n1.List^[i], @n1.List^[i + 1], @n1.List^[i + 2]), BSP2, FG2, Res, FGR, false, true, false);
  515. end;
  516. CSG_Intersection:
  517. begin
  518. CSG_Iterate_tri(MakeCSGTri(@v1.List^[i], @v1.List^[i + 1], @v1.List^[i + 2]), MakeCSGTri(@n1.List^[i], @n1.List^[i + 1], @n1.List^[i + 2]), BSP2, FG2, Res, FGR, true, false, false);
  519. end;
  520. end;
  521. inc(i, 3);
  522. end;
  523. // Then iterate all triangles of object 2, one at a time, down through the BSP tree of Object 1, the last booleans are the key to what actuelly happends.
  524. FGR := TFGVertexNormalTexIndexList.CreateOwned(Res.FaceGroups);
  525. FGR.MaterialName := MaterialName2;
  526. i := 0;
  527. while i < v2.Count - 2 do
  528. begin
  529. case Operation of
  530. CSG_Union:
  531. begin
  532. CSG_Iterate_tri(MakeCSGTri(@v2.List^[i], @v2.List^[i + 1], @v2.List^[i + 2]), MakeCSGTri(@n2.List^[i], @n2.List^[i + 1], @n2.List^[i + 2]), BSP1, FG1, Res, FGR, false, true, false);
  533. end;
  534. CSG_Subtraction:
  535. begin
  536. CSG_Iterate_tri(MakeCSGTri(@v2.List^[i], @v2.List^[i + 1], @v2.List^[i + 2]), MakeCSGTri(@n2.List^[i], @n2.List^[i + 1], @n2.List^[i + 2]), BSP1, FG1, Res, FGR, true, false, true);
  537. end;
  538. CSG_Intersection:
  539. begin
  540. CSG_Iterate_tri(MakeCSGTri(@v2.List^[i], @v2.List^[i + 1], @v2.List^[i + 2]), MakeCSGTri(@n2.List^[i], @n2.List^[i + 1], @n2.List^[i + 2]), BSP1, FG1, Res, FGR, true, false, false);
  541. end;
  542. end;
  543. inc(i, 3);
  544. end;
  545. // clean up.
  546. v1.Free;
  547. n1.Free;
  548. t1.Free;
  549. v2.Free;
  550. n2.Free;
  551. t2.Free;
  552. BSP2.Free;
  553. BSP1.Free;
  554. end;
  555. end.