GLS.Silhouette.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.Silhouette;
  5. (*
  6. Enhanced silhouette classes.
  7. Introduces more evolved/specific silhouette generation and management
  8. classes.
  9. CAUTION : both connectivity classes leak memory.
  10. *)
  11. interface
  12. {$I GLScene.inc}
  13. uses
  14. System.Classes,
  15. System.SysUtils,
  16. GLS.VectorTypes,
  17. GLS.VectorGeometry,
  18. GLS.VectorLists;
  19. type
  20. TGLSilhouetteStyle = (ssOmni, ssParallel);
  21. (* Silouhette generation parameters.
  22. SeenFrom and LightDirection are expected in local coordinates. *)
  23. TGLSilhouetteParameters = packed record
  24. SeenFrom, LightDirection: TAffineVector;
  25. Style: TGLSilhouetteStyle;
  26. CappingRequired: Boolean;
  27. end;
  28. (* Base class storing a volume silhouette.
  29. Made of a set of indexed vertices defining an outline, and another set
  30. of indexed vertices defining a capping volume. Coordinates system
  31. is the object's unscaled local coordinates system.
  32. This is the base class, you can use the TGLSilhouette subclass if you
  33. need some helper methods for generating the indexed sets. *)
  34. TGLSilhouette = class
  35. private
  36. FVertices: TGLVectorList;
  37. FIndices: TGLIntegerList;
  38. FCapIndices: TGLIntegerList;
  39. FParameters: TGLSilhouetteParameters;
  40. protected
  41. procedure SetIndices(const value: TGLIntegerList);
  42. procedure SetCapIndices(const value: TGLIntegerList);
  43. procedure SetVertices(const value: TGLVectorList);
  44. public
  45. constructor Create; virtual;
  46. destructor Destroy; override;
  47. property Parameters: TGLSilhouetteParameters read FParameters write FParameters;
  48. property Vertices: TGLVectorList read FVertices write SetVertices;
  49. property Indices: TGLIntegerList read FIndices write SetIndices;
  50. property CapIndices: TGLIntegerList read FCapIndices write SetCapIndices;
  51. procedure Flush; virtual;
  52. procedure Clear; inline;
  53. procedure ExtrudeVerticesToInfinity(const origin: TAffineVector);
  54. (* Adds an edge (two vertices) to the silhouette.
  55. If TightButSlow is true, no vertices will be doubled in the
  56. silhouette list. This should only be used when creating re-usable
  57. silhouettes, because it's much slower. *)
  58. procedure AddEdgeToSilhouette(const v0, v1: TAffineVector; tightButSlow: Boolean); inline;
  59. procedure AddIndexedEdgeToSilhouette(const Vi0, Vi1: integer); inline;
  60. (* Adds a capping triangle to the silhouette.
  61. If TightButSlow is true, no vertices will be doubled in the
  62. silhouette list. This should only be used when creating re-usable
  63. silhouettes, because it's much slower. *)
  64. procedure AddCapToSilhouette(const v0, v1, v2: TAffineVector; tightButSlow: Boolean); inline;
  65. procedure AddIndexedCapToSilhouette(const Vi0, Vi1, vi2: integer); inline;
  66. end;
  67. TGLBaseConnectivity = class
  68. protected
  69. FPrecomputeFaceNormal: Boolean;
  70. function GetEdgeCount: integer; virtual;
  71. function GetFaceCount: integer; virtual;
  72. public
  73. property EdgeCount: integer read GetEdgeCount;
  74. property FaceCount: integer read GetFaceCount;
  75. property PrecomputeFaceNormal: Boolean read FPrecomputeFaceNormal;
  76. procedure CreateSilhouette(const ASilhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
  77. AddToSilhouette: Boolean); virtual;
  78. constructor Create(APrecomputeFaceNormal: Boolean); virtual;
  79. end;
  80. TGLConnectivity = class(TGLBaseConnectivity)
  81. protected
  82. (* All storage of faces and adges are cut up into tiny pieces for a reason,
  83. it'd be nicer with Structs or classes, but it's actually faster this way.
  84. The reason it's faster is because of less cache overwrites when we only
  85. access a tiny bit of a triangle (for instance), not all data. *)
  86. FEdgeVertices: TGLIntegerList;
  87. FEdgeFaces: TGLIntegerList;
  88. FFaceVisible: TGLByteList;
  89. FFaceVertexIndex: TGLIntegerList;
  90. FFaceNormal: TGLAffineVectorList;
  91. FVertexMemory: TGLIntegerList;
  92. FVertices: TGLAffineVectorList;
  93. function GetEdgeCount: integer;
  94. function GetFaceCount: integer;
  95. function ReuseOrFindVertexID(const SeenFrom: TAffineVector; ASilhouette: TGLSilhouette; index: integer): integer;
  96. public
  97. // Clears out all connectivity information.
  98. procedure Clear; virtual;
  99. procedure CreateSilhouette(const silhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
  100. AddToSilhouette: Boolean);
  101. function AddIndexedEdge(vertexIndex0, vertexIndex1: integer; FaceID: integer): integer;
  102. function AddIndexedFace(Vi0, Vi1, vi2: integer): integer;
  103. function AddFace(const vertex0, vertex1, vertex2: TAffineVector): integer; inline;
  104. function AddQuad(const vertex0, vertex1, vertex2, vertex3: TAffineVector): integer; inline;
  105. property EdgeCount: integer read GetEdgeCount;
  106. property FaceCount: integer read GetFaceCount;
  107. constructor Create(APrecomputeFaceNormal: Boolean); override;
  108. destructor Destroy; override;
  109. end;
  110. // -------------------------------------------------------------
  111. implementation
  112. // -------------------------------------------------------------
  113. // ------------------
  114. // ------------------ TGLSilhouette ------------------
  115. // ------------------
  116. constructor TGLSilhouette.Create;
  117. begin
  118. inherited;
  119. FVertices := TGLVectorList.Create;
  120. FIndices := TGLIntegerList.Create;
  121. FCapIndices := TGLIntegerList.Create;
  122. end;
  123. destructor TGLSilhouette.Destroy;
  124. begin
  125. FCapIndices.Free;
  126. FIndices.Free;
  127. FVertices.Free;
  128. inherited;
  129. end;
  130. procedure TGLSilhouette.SetIndices(const value: TGLIntegerList);
  131. begin
  132. FIndices.Assign(value);
  133. end;
  134. procedure TGLSilhouette.SetCapIndices(const value: TGLIntegerList);
  135. begin
  136. FCapIndices.Assign(value);
  137. end;
  138. procedure TGLSilhouette.SetVertices(const value: TGLVectorList);
  139. begin
  140. FVertices.Assign(value);
  141. end;
  142. procedure TGLSilhouette.Flush;
  143. begin
  144. FVertices.Flush;
  145. FIndices.Flush;
  146. FCapIndices.Flush;
  147. end;
  148. procedure TGLSilhouette.Clear;
  149. begin
  150. FVertices.Clear;
  151. FIndices.Clear;
  152. FCapIndices.Clear;
  153. end;
  154. procedure TGLSilhouette.ExtrudeVerticesToInfinity(const origin: TAffineVector);
  155. var
  156. i, nv, ni, nc, k: integer;
  157. vList, vListN: PVectorArray;
  158. iList, iList2: PIntegerArray;
  159. begin
  160. // extrude vertices
  161. nv := Vertices.Count;
  162. Vertices.Count := 2 * nv;
  163. vList := Vertices.List;
  164. vListN := @vList[nv];
  165. for i := 0 to nv - 1 do
  166. begin
  167. vListN^[i].W := 0;
  168. VectorSubtract(PAffineVector(@vList[i])^, origin, PAffineVector(@vListN[i])^);
  169. end;
  170. // change silhouette indices to quad indices
  171. ni := Indices.Count;
  172. Indices.Count := 2 * ni;
  173. iList := Indices.List;
  174. i := ni - 2;
  175. while i >= 0 do
  176. begin
  177. iList2 := @iList^[2 * i];
  178. iList2^[0] := iList^[i];
  179. iList2^[1] := iList^[i + 1];
  180. iList2^[2] := iList^[i + 1] + nv;
  181. iList2^[3] := iList^[i] + nv;
  182. Dec(i, 2);
  183. end;
  184. // add extruded triangles to capIndices
  185. nc := CapIndices.Count;
  186. CapIndices.Capacity := 2 * nc;
  187. iList := CapIndices.List;
  188. for i := nc - 1 downto 0 do
  189. begin
  190. k := iList^[i];
  191. CapIndices.Add(k);
  192. iList^[i] := k + nv;
  193. end;
  194. end;
  195. // ------------------
  196. // ------------------ TGLSilhouette ------------------
  197. // ------------------
  198. procedure TGLSilhouette.AddEdgeToSilhouette(const v0, v1: TAffineVector; tightButSlow: Boolean);
  199. begin
  200. if tightButSlow then
  201. Indices.Add(Vertices.FindOrAddPoint(v0), Vertices.FindOrAddPoint(v1))
  202. else
  203. Indices.Add(Vertices.Add(v0, 1), Vertices.Add(v1, 1));
  204. end;
  205. procedure TGLSilhouette.AddIndexedEdgeToSilhouette(const Vi0, Vi1: integer);
  206. begin
  207. Indices.Add(Vi0, Vi1);
  208. end;
  209. procedure TGLSilhouette.AddCapToSilhouette(const v0, v1, v2: TAffineVector; tightButSlow: Boolean);
  210. begin
  211. if tightButSlow then
  212. CapIndices.Add(Vertices.FindOrAddPoint(v0), Vertices.FindOrAddPoint(v1), Vertices.FindOrAddPoint(v2))
  213. else
  214. CapIndices.Add(Vertices.Add(v0, 1), Vertices.Add(v1, 1), Vertices.Add(v2, 1));
  215. end;
  216. procedure TGLSilhouette.AddIndexedCapToSilhouette(const Vi0, Vi1, vi2: integer);
  217. begin
  218. CapIndices.Add(Vi0, Vi1, vi2);
  219. end;
  220. // ------------------
  221. // ------------------ TGLBaseConnectivity ------------------
  222. // ------------------
  223. constructor TGLBaseConnectivity.Create(APrecomputeFaceNormal: Boolean);
  224. begin
  225. FPrecomputeFaceNormal := APrecomputeFaceNormal;
  226. end;
  227. procedure TGLBaseConnectivity.CreateSilhouette(const ASilhouetteParameters: TGLSilhouetteParameters;
  228. var ASilhouette: TGLSilhouette; AddToSilhouette: Boolean);
  229. begin
  230. // Purely virtual!
  231. end;
  232. // ------------------
  233. // ------------------ TGLConnectivity ------------------
  234. // ------------------
  235. function TGLBaseConnectivity.GetEdgeCount: integer;
  236. begin
  237. result := 0;
  238. end;
  239. function TGLBaseConnectivity.GetFaceCount: integer;
  240. begin
  241. result := 0;
  242. end;
  243. constructor TGLConnectivity.Create(APrecomputeFaceNormal: Boolean);
  244. begin
  245. FFaceVisible := TGLByteList.Create;
  246. FFaceVertexIndex := TGLIntegerList.Create;
  247. FFaceNormal := TGLAffineVectorList.Create;
  248. FEdgeVertices := TGLIntegerList.Create;
  249. FEdgeFaces := TGLIntegerList.Create;
  250. FPrecomputeFaceNormal := APrecomputeFaceNormal;
  251. FVertexMemory := TGLIntegerList.Create;
  252. FVertices := TGLAffineVectorList.Create;
  253. end;
  254. destructor TGLConnectivity.Destroy;
  255. begin
  256. Clear;
  257. FFaceVisible.Free;
  258. FFaceVertexIndex.Free;
  259. FFaceNormal.Free;
  260. FEdgeVertices.Free;
  261. FEdgeFaces.Free;
  262. FVertexMemory.Free;
  263. if Assigned(FVertices) then
  264. FVertices.Free;
  265. inherited;
  266. end;
  267. procedure TGLConnectivity.Clear;
  268. begin
  269. FEdgeVertices.Clear;
  270. FEdgeFaces.Clear;
  271. FFaceVisible.Clear;
  272. FFaceVertexIndex.Clear;
  273. FFaceNormal.Clear;
  274. FVertexMemory.Clear;
  275. if FVertices <> nil then
  276. FVertices.Clear;
  277. end;
  278. procedure TGLConnectivity.CreateSilhouette(const silhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
  279. AddToSilhouette: Boolean);
  280. var
  281. i: integer;
  282. vis: PIntegerArray;
  283. tVi0, tVi1: integer;
  284. faceNormal: TAffineVector;
  285. face0ID, face1ID: integer;
  286. faceIsVisible: Boolean;
  287. verticesList: PAffineVectorArray;
  288. begin
  289. if not Assigned(ASilhouette) then
  290. ASilhouette := TGLSilhouette.Create
  291. else if not AddToSilhouette then
  292. ASilhouette.Flush;
  293. // Clear the vertex memory
  294. FVertexMemory.Flush;
  295. // Update visibility information for all Faces
  296. vis := FFaceVertexIndex.List;
  297. for i := 0 to FaceCount - 1 do
  298. begin
  299. if FPrecomputeFaceNormal then
  300. faceIsVisible := (PointProject(silhouetteParameters.SeenFrom, FVertices.List^[vis^[0]], FFaceNormal.List^[i]) >= 0)
  301. else
  302. begin
  303. verticesList := FVertices.List;
  304. faceNormal := CalcPlaneNormal(verticesList^[vis^[0]], verticesList^[vis^[1]], verticesList^[vis^[2]]);
  305. faceIsVisible := (PointProject(silhouetteParameters.SeenFrom, FVertices.List^[vis^[0]], faceNormal) >= 0);
  306. end;
  307. FFaceVisible[i] := Byte(faceIsVisible);
  308. if (not faceIsVisible) and silhouetteParameters.CappingRequired then
  309. ASilhouette.CapIndices.Add(ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]),
  310. ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]),
  311. ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[2]));
  312. vis := @vis[3];
  313. end;
  314. for i := 0 to EdgeCount - 1 do
  315. begin
  316. face0ID := FEdgeFaces[i * 2 + 0];
  317. face1ID := FEdgeFaces[i * 2 + 1];
  318. if (face1ID = -1) or (FFaceVisible.List^[face0ID] <> FFaceVisible.List^[face1ID]) then
  319. begin
  320. // Retrieve the two vertice values add add them to the Silhouette list
  321. vis := @FEdgeVertices.List[i * 2];
  322. // In this moment, we _know_ what vertex id the vertex had in the old
  323. // mesh. We can remember this information and re-use it for a speedup
  324. if FFaceVisible.List^[face0ID] = 0 then
  325. begin
  326. tVi0 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]);
  327. tVi1 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]);
  328. ASilhouette.Indices.Add(tVi0, tVi1);
  329. end
  330. else if face1ID > -1 then
  331. begin
  332. tVi0 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]);
  333. tVi1 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]);
  334. ASilhouette.Indices.Add(tVi1, tVi0);
  335. end;
  336. end;
  337. end;
  338. end;
  339. function TGLConnectivity.GetEdgeCount: integer;
  340. begin
  341. result := FEdgeVertices.Count div 2;
  342. end;
  343. function TGLConnectivity.GetFaceCount: integer;
  344. begin
  345. result := FFaceVisible.Count;
  346. end;
  347. function TGLConnectivity.ReuseOrFindVertexID(const SeenFrom: TAffineVector; ASilhouette: TGLSilhouette; index: integer): integer;
  348. var
  349. pMemIndex: PInteger;
  350. memIndex, i: integer;
  351. oldCount: integer;
  352. List: PIntegerArray;
  353. begin
  354. if index >= FVertexMemory.Count then
  355. begin
  356. oldCount := FVertexMemory.Count;
  357. FVertexMemory.Count := index + 1;
  358. List := FVertexMemory.List;
  359. for i := oldCount to FVertexMemory.Count - 1 do
  360. List^[i] := -1;
  361. end;
  362. pMemIndex := @FVertexMemory.List[index];
  363. if pMemIndex^ = -1 then
  364. begin
  365. // Add the "near" vertex
  366. memIndex := ASilhouette.Vertices.Add(FVertices.List^[index], 1);
  367. pMemIndex^ := memIndex;
  368. result := memIndex;
  369. end
  370. else
  371. result := pMemIndex^;
  372. end;
  373. function TGLConnectivity.AddIndexedEdge(vertexIndex0, vertexIndex1: integer; FaceID: integer): integer;
  374. var
  375. i: integer;
  376. edgesVertices: PIntegerArray;
  377. begin
  378. // Make sure that the edge doesn't already exists
  379. edgesVertices := FEdgeVertices.List;
  380. for i := 0 to EdgeCount - 1 do
  381. begin
  382. // Retrieve the two vertices in the edge
  383. if ((edgesVertices^[0] = vertexIndex0) and (edgesVertices^[1] = vertexIndex1)) or
  384. ((edgesVertices^[0] = vertexIndex1) and (edgesVertices^[1] = vertexIndex0)) then
  385. begin
  386. // Update the second Face of the edge and we're done (this _MAY_
  387. // overwrite a previous Face in a broken mesh)
  388. FEdgeFaces[i * 2 + 1] := FaceID;
  389. result := i * 2 + 1;
  390. Exit;
  391. end;
  392. edgesVertices := @edgesVertices[2];
  393. end;
  394. // No edge was found, create a new one
  395. FEdgeVertices.Add(vertexIndex0, vertexIndex1);
  396. FEdgeFaces.Add(FaceID, -1);
  397. result := EdgeCount - 1;
  398. end;
  399. function TGLConnectivity.AddIndexedFace(Vi0, Vi1, vi2: integer): integer;
  400. var
  401. FaceID: integer;
  402. begin
  403. FFaceVertexIndex.Add(Vi0, Vi1, vi2);
  404. if FPrecomputeFaceNormal then
  405. FFaceNormal.Add(CalcPlaneNormal(FVertices.List^[Vi0], FVertices.List^[Vi1], FVertices.List^[vi2]));
  406. FaceID := FFaceVisible.Add(0);
  407. AddIndexedEdge(Vi0, Vi1, FaceID);
  408. AddIndexedEdge(Vi1, vi2, FaceID);
  409. AddIndexedEdge(vi2, Vi0, FaceID);
  410. result := FaceID;
  411. end;
  412. function TGLConnectivity.AddFace(const vertex0, vertex1, vertex2: TAffineVector): integer;
  413. var
  414. Vi0, Vi1, vi2: integer;
  415. begin
  416. Vi0 := FVertices.FindOrAdd(vertex0);
  417. Vi1 := FVertices.FindOrAdd(vertex1);
  418. vi2 := FVertices.FindOrAdd(vertex2);
  419. result := AddIndexedFace(Vi0, Vi1, vi2);
  420. end;
  421. function TGLConnectivity.AddQuad(const vertex0, vertex1, vertex2, vertex3: TAffineVector): integer;
  422. var
  423. Vi0, Vi1, vi2, Vi3: integer;
  424. begin
  425. Vi0 := FVertices.FindOrAdd(vertex0);
  426. Vi1 := FVertices.FindOrAdd(vertex1);
  427. vi2 := FVertices.FindOrAdd(vertex2);
  428. Vi3 := FVertices.FindOrAdd(vertex3);
  429. // First face
  430. result := AddIndexedFace(Vi0, Vi1, vi2);
  431. // Second face
  432. AddIndexedFace(vi2, Vi3, Vi0);
  433. end;
  434. end.