GLS.Silhouette.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. //
  2. // The graphics engine 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 Stage.Defines.inc}
  13. uses
  14. System.Classes,
  15. System.SysUtils,
  16. Stage.VectorTypes,
  17. Stage.VectorGeometry,
  18. GLS.VectorLists;
  19. type
  20. TGSilhouetteStyle = (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: TGSilhouetteStyle;
  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. implementation // -------------------------------------------------------------
  111. // ------------------
  112. // ------------------ TGLSilhouette ------------------
  113. // ------------------
  114. constructor TGLSilhouette.Create;
  115. begin
  116. inherited;
  117. FVertices := TGLVectorList.Create;
  118. FIndices := TGLIntegerList.Create;
  119. FCapIndices := TGLIntegerList.Create;
  120. end;
  121. destructor TGLSilhouette.Destroy;
  122. begin
  123. FCapIndices.Free;
  124. FIndices.Free;
  125. FVertices.Free;
  126. inherited;
  127. end;
  128. procedure TGLSilhouette.SetIndices(const value: TGLIntegerList);
  129. begin
  130. FIndices.Assign(value);
  131. end;
  132. procedure TGLSilhouette.SetCapIndices(const value: TGLIntegerList);
  133. begin
  134. FCapIndices.Assign(value);
  135. end;
  136. procedure TGLSilhouette.SetVertices(const value: TGLVectorList);
  137. begin
  138. FVertices.Assign(value);
  139. end;
  140. procedure TGLSilhouette.Flush;
  141. begin
  142. FVertices.Flush;
  143. FIndices.Flush;
  144. FCapIndices.Flush;
  145. end;
  146. procedure TGLSilhouette.Clear;
  147. begin
  148. FVertices.Clear;
  149. FIndices.Clear;
  150. FCapIndices.Clear;
  151. end;
  152. procedure TGLSilhouette.ExtrudeVerticesToInfinity(const origin: TAffineVector);
  153. var
  154. i, nv, ni, nc, k: integer;
  155. vList, vListN: PVectorArray;
  156. iList, iList2: PIntegerArray;
  157. begin
  158. // extrude vertices
  159. nv := Vertices.Count;
  160. Vertices.Count := 2 * nv;
  161. vList := Vertices.List;
  162. vListN := @vList[nv];
  163. for i := 0 to nv - 1 do
  164. begin
  165. vListN^[i].W := 0;
  166. VectorSubtract(PAffineVector(@vList[i])^, origin, PAffineVector(@vListN[i])^);
  167. end;
  168. // change silhouette indices to quad indices
  169. ni := Indices.Count;
  170. Indices.Count := 2 * ni;
  171. iList := Indices.List;
  172. i := ni - 2;
  173. while i >= 0 do
  174. begin
  175. iList2 := @iList^[2 * i];
  176. iList2^[0] := iList^[i];
  177. iList2^[1] := iList^[i + 1];
  178. iList2^[2] := iList^[i + 1] + nv;
  179. iList2^[3] := iList^[i] + nv;
  180. Dec(i, 2);
  181. end;
  182. // add extruded triangles to capIndices
  183. nc := CapIndices.Count;
  184. CapIndices.Capacity := 2 * nc;
  185. iList := CapIndices.List;
  186. for i := nc - 1 downto 0 do
  187. begin
  188. k := iList^[i];
  189. CapIndices.Add(k);
  190. iList^[i] := k + nv;
  191. end;
  192. end;
  193. // ------------------
  194. // ------------------ TGLSilhouette ------------------
  195. // ------------------
  196. procedure TGLSilhouette.AddEdgeToSilhouette(const v0, v1: TAffineVector; tightButSlow: Boolean);
  197. begin
  198. if tightButSlow then
  199. Indices.Add(Vertices.FindOrAddPoint(v0), Vertices.FindOrAddPoint(v1))
  200. else
  201. Indices.Add(Vertices.Add(v0, 1), Vertices.Add(v1, 1));
  202. end;
  203. procedure TGLSilhouette.AddIndexedEdgeToSilhouette(const Vi0, Vi1: integer);
  204. begin
  205. Indices.Add(Vi0, Vi1);
  206. end;
  207. procedure TGLSilhouette.AddCapToSilhouette(const v0, v1, v2: TAffineVector; tightButSlow: Boolean);
  208. begin
  209. if tightButSlow then
  210. CapIndices.Add(Vertices.FindOrAddPoint(v0), Vertices.FindOrAddPoint(v1), Vertices.FindOrAddPoint(v2))
  211. else
  212. CapIndices.Add(Vertices.Add(v0, 1), Vertices.Add(v1, 1), Vertices.Add(v2, 1));
  213. end;
  214. procedure TGLSilhouette.AddIndexedCapToSilhouette(const Vi0, Vi1, vi2: integer);
  215. begin
  216. CapIndices.Add(Vi0, Vi1, vi2);
  217. end;
  218. // ------------------
  219. // ------------------ TGLBaseConnectivity ------------------
  220. // ------------------
  221. constructor TGLBaseConnectivity.Create(APrecomputeFaceNormal: Boolean);
  222. begin
  223. FPrecomputeFaceNormal := APrecomputeFaceNormal;
  224. end;
  225. procedure TGLBaseConnectivity.CreateSilhouette(const ASilhouetteParameters: TGLSilhouetteParameters;
  226. var ASilhouette: TGLSilhouette; AddToSilhouette: Boolean);
  227. begin
  228. // Purely virtual!
  229. end;
  230. // ------------------
  231. // ------------------ TGLConnectivity ------------------
  232. // ------------------
  233. function TGLBaseConnectivity.GetEdgeCount: integer;
  234. begin
  235. result := 0;
  236. end;
  237. function TGLBaseConnectivity.GetFaceCount: integer;
  238. begin
  239. result := 0;
  240. end;
  241. constructor TGLConnectivity.Create(APrecomputeFaceNormal: Boolean);
  242. begin
  243. FFaceVisible := TGLByteList.Create;
  244. FFaceVertexIndex := TGLIntegerList.Create;
  245. FFaceNormal := TGLAffineVectorList.Create;
  246. FEdgeVertices := TGLIntegerList.Create;
  247. FEdgeFaces := TGLIntegerList.Create;
  248. FPrecomputeFaceNormal := APrecomputeFaceNormal;
  249. FVertexMemory := TGLIntegerList.Create;
  250. FVertices := TGLAffineVectorList.Create;
  251. end;
  252. destructor TGLConnectivity.Destroy;
  253. begin
  254. Clear;
  255. FFaceVisible.Free;
  256. FFaceVertexIndex.Free;
  257. FFaceNormal.Free;
  258. FEdgeVertices.Free;
  259. FEdgeFaces.Free;
  260. FVertexMemory.Free;
  261. if Assigned(FVertices) then
  262. FVertices.Free;
  263. inherited;
  264. end;
  265. procedure TGLConnectivity.Clear;
  266. begin
  267. FEdgeVertices.Clear;
  268. FEdgeFaces.Clear;
  269. FFaceVisible.Clear;
  270. FFaceVertexIndex.Clear;
  271. FFaceNormal.Clear;
  272. FVertexMemory.Clear;
  273. if FVertices <> nil then
  274. FVertices.Clear;
  275. end;
  276. procedure TGLConnectivity.CreateSilhouette(const silhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
  277. AddToSilhouette: Boolean);
  278. var
  279. i: integer;
  280. vis: PIntegerArray;
  281. tVi0, tVi1: integer;
  282. faceNormal: TAffineVector;
  283. face0ID, face1ID: integer;
  284. faceIsVisible: Boolean;
  285. verticesList: PAffineVectorArray;
  286. begin
  287. if not Assigned(ASilhouette) then
  288. ASilhouette := TGLSilhouette.Create
  289. else if not AddToSilhouette then
  290. ASilhouette.Flush;
  291. // Clear the vertex memory
  292. FVertexMemory.Flush;
  293. // Update visibility information for all Faces
  294. vis := FFaceVertexIndex.List;
  295. for i := 0 to FaceCount - 1 do
  296. begin
  297. if FPrecomputeFaceNormal then
  298. faceIsVisible := (PointProject(silhouetteParameters.SeenFrom, FVertices.List^[vis^[0]], FFaceNormal.List^[i]) >= 0)
  299. else
  300. begin
  301. verticesList := FVertices.List;
  302. faceNormal := CalcPlaneNormal(verticesList^[vis^[0]], verticesList^[vis^[1]], verticesList^[vis^[2]]);
  303. faceIsVisible := (PointProject(silhouetteParameters.SeenFrom, FVertices.List^[vis^[0]], faceNormal) >= 0);
  304. end;
  305. FFaceVisible[i] := Byte(faceIsVisible);
  306. if (not faceIsVisible) and silhouetteParameters.CappingRequired then
  307. ASilhouette.CapIndices.Add(ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]),
  308. ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]),
  309. ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[2]));
  310. vis := @vis[3];
  311. end;
  312. for i := 0 to EdgeCount - 1 do
  313. begin
  314. face0ID := FEdgeFaces[i * 2 + 0];
  315. face1ID := FEdgeFaces[i * 2 + 1];
  316. if (face1ID = -1) or (FFaceVisible.List^[face0ID] <> FFaceVisible.List^[face1ID]) then
  317. begin
  318. // Retrieve the two vertice values add add them to the Silhouette list
  319. vis := @FEdgeVertices.List[i * 2];
  320. // In this moment, we _know_ what vertex id the vertex had in the old
  321. // mesh. We can remember this information and re-use it for a speedup
  322. if FFaceVisible.List^[face0ID] = 0 then
  323. begin
  324. tVi0 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]);
  325. tVi1 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]);
  326. ASilhouette.Indices.Add(tVi0, tVi1);
  327. end
  328. else if face1ID > -1 then
  329. begin
  330. tVi0 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]);
  331. tVi1 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]);
  332. ASilhouette.Indices.Add(tVi1, tVi0);
  333. end;
  334. end;
  335. end;
  336. end;
  337. function TGLConnectivity.GetEdgeCount: integer;
  338. begin
  339. result := FEdgeVertices.Count div 2;
  340. end;
  341. function TGLConnectivity.GetFaceCount: integer;
  342. begin
  343. result := FFaceVisible.Count;
  344. end;
  345. function TGLConnectivity.ReuseOrFindVertexID(const SeenFrom: TAffineVector; ASilhouette: TGLSilhouette; index: integer): integer;
  346. var
  347. pMemIndex: PInteger;
  348. memIndex, i: integer;
  349. oldCount: integer;
  350. List: PIntegerArray;
  351. begin
  352. if index >= FVertexMemory.Count then
  353. begin
  354. oldCount := FVertexMemory.Count;
  355. FVertexMemory.Count := index + 1;
  356. List := FVertexMemory.List;
  357. for i := oldCount to FVertexMemory.Count - 1 do
  358. List^[i] := -1;
  359. end;
  360. pMemIndex := @FVertexMemory.List[index];
  361. if pMemIndex^ = -1 then
  362. begin
  363. // Add the "near" vertex
  364. memIndex := ASilhouette.Vertices.Add(FVertices.List^[index], 1);
  365. pMemIndex^ := memIndex;
  366. result := memIndex;
  367. end
  368. else
  369. result := pMemIndex^;
  370. end;
  371. function TGLConnectivity.AddIndexedEdge(vertexIndex0, vertexIndex1: integer; FaceID: integer): integer;
  372. var
  373. i: integer;
  374. edgesVertices: PIntegerArray;
  375. begin
  376. // Make sure that the edge doesn't already exists
  377. edgesVertices := FEdgeVertices.List;
  378. for i := 0 to EdgeCount - 1 do
  379. begin
  380. // Retrieve the two vertices in the edge
  381. if ((edgesVertices^[0] = vertexIndex0) and (edgesVertices^[1] = vertexIndex1)) or
  382. ((edgesVertices^[0] = vertexIndex1) and (edgesVertices^[1] = vertexIndex0)) then
  383. begin
  384. // Update the second Face of the edge and we're done (this _MAY_
  385. // overwrite a previous Face in a broken mesh)
  386. FEdgeFaces[i * 2 + 1] := FaceID;
  387. result := i * 2 + 1;
  388. Exit;
  389. end;
  390. edgesVertices := @edgesVertices[2];
  391. end;
  392. // No edge was found, create a new one
  393. FEdgeVertices.Add(vertexIndex0, vertexIndex1);
  394. FEdgeFaces.Add(FaceID, -1);
  395. result := EdgeCount - 1;
  396. end;
  397. function TGLConnectivity.AddIndexedFace(Vi0, Vi1, vi2: integer): integer;
  398. var
  399. FaceID: integer;
  400. begin
  401. FFaceVertexIndex.Add(Vi0, Vi1, vi2);
  402. if FPrecomputeFaceNormal then
  403. FFaceNormal.Add(CalcPlaneNormal(FVertices.List^[Vi0], FVertices.List^[Vi1], FVertices.List^[vi2]));
  404. FaceID := FFaceVisible.Add(0);
  405. AddIndexedEdge(Vi0, Vi1, FaceID);
  406. AddIndexedEdge(Vi1, vi2, FaceID);
  407. AddIndexedEdge(vi2, Vi0, FaceID);
  408. result := FaceID;
  409. end;
  410. function TGLConnectivity.AddFace(const vertex0, vertex1, vertex2: TAffineVector): integer;
  411. var
  412. Vi0, Vi1, vi2: integer;
  413. begin
  414. Vi0 := FVertices.FindOrAdd(vertex0);
  415. Vi1 := FVertices.FindOrAdd(vertex1);
  416. vi2 := FVertices.FindOrAdd(vertex2);
  417. result := AddIndexedFace(Vi0, Vi1, vi2);
  418. end;
  419. function TGLConnectivity.AddQuad(const vertex0, vertex1, vertex2, vertex3: TAffineVector): integer;
  420. var
  421. Vi0, Vi1, vi2, Vi3: integer;
  422. begin
  423. Vi0 := FVertices.FindOrAdd(vertex0);
  424. Vi1 := FVertices.FindOrAdd(vertex1);
  425. vi2 := FVertices.FindOrAdd(vertex2);
  426. Vi3 := FVertices.FindOrAdd(vertex3);
  427. // First face
  428. result := AddIndexedFace(Vi0, Vi1, vi2);
  429. // Second face
  430. AddIndexedFace(vi2, Vi3, Vi0);
  431. end;
  432. //----------------------------------------------------------------------------
  433. end.