GXS.MeshCSG.pas 23 KB

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