|
@@ -1,508 +1,506 @@
|
|
-//
|
|
|
|
-// This unit is part of the GLScene Engine, http://glscene.org
|
|
|
|
-//
|
|
|
|
-
|
|
|
|
-unit GLSilhouette;
|
|
|
|
-
|
|
|
|
-(*
|
|
|
|
- Enhanced silhouette classes.
|
|
|
|
- Introduces more evolved/specific silhouette generation and management
|
|
|
|
- classes.
|
|
|
|
- CAUTION : both connectivity classes leak memory.
|
|
|
|
-*)
|
|
|
|
-
|
|
|
|
-interface
|
|
|
|
-
|
|
|
|
-{$I GLScene.inc}
|
|
|
|
-
|
|
|
|
-uses
|
|
|
|
- System.Classes,
|
|
|
|
- System.SysUtils,
|
|
|
|
-
|
|
|
|
- Scene.VectorTypes,
|
|
|
|
- Scene.VectorGeometry,
|
|
|
|
- Scene.VectorLists;
|
|
|
|
-
|
|
|
|
-type
|
|
|
|
- TGLSilhouetteStyle = (ssOmni, ssParallel);
|
|
|
|
-
|
|
|
|
- (* Silouhette generation parameters.
|
|
|
|
- SeenFrom and LightDirection are expected in local coordinates. *)
|
|
|
|
- TGLSilhouetteParameters = packed record
|
|
|
|
- SeenFrom, LightDirection: TAffineVector;
|
|
|
|
- Style: TGLSilhouetteStyle;
|
|
|
|
- CappingRequired: Boolean;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- (* Base class storing a volume silhouette.
|
|
|
|
- Made of a set of indexed vertices defining an outline, and another set
|
|
|
|
- of indexed vertices defining a capping volume. Coordinates system
|
|
|
|
- is the object's unscaled local coordinates system.
|
|
|
|
- This is the base class, you can use the TGLSilhouette subclass if you
|
|
|
|
- need some helper methods for generating the indexed sets. *)
|
|
|
|
- TGLSilhouette = class
|
|
|
|
- private
|
|
|
|
- FVertices: TVectorList;
|
|
|
|
- FIndices: TIntegerList;
|
|
|
|
- FCapIndices: TIntegerList;
|
|
|
|
- FParameters: TGLSilhouetteParameters;
|
|
|
|
- protected
|
|
|
|
- procedure SetIndices(const value: TIntegerList);
|
|
|
|
- procedure SetCapIndices(const value: TIntegerList);
|
|
|
|
- procedure SetVertices(const value: TVectorList);
|
|
|
|
- public
|
|
|
|
- constructor Create; virtual;
|
|
|
|
- destructor Destroy; override;
|
|
|
|
- property Parameters: TGLSilhouetteParameters read FParameters write FParameters;
|
|
|
|
- property Vertices: TVectorList read FVertices write SetVertices;
|
|
|
|
- property Indices: TIntegerList read FIndices write SetIndices;
|
|
|
|
- property CapIndices: TIntegerList read FCapIndices write SetCapIndices;
|
|
|
|
- procedure Flush; virtual;
|
|
|
|
- procedure Clear; inline;
|
|
|
|
- procedure ExtrudeVerticesToInfinity(const origin: TAffineVector);
|
|
|
|
- (* Adds an edge (two vertices) to the silhouette.
|
|
|
|
- If TightButSlow is true, no vertices will be doubled in the
|
|
|
|
- silhouette list. This should only be used when creating re-usable
|
|
|
|
- silhouettes, because it's much slower. *)
|
|
|
|
- procedure AddEdgeToSilhouette(const v0, v1: TAffineVector; tightButSlow: Boolean); inline;
|
|
|
|
- procedure AddIndexedEdgeToSilhouette(const Vi0, Vi1: integer); inline;
|
|
|
|
-
|
|
|
|
- (* Adds a capping triangle to the silhouette.
|
|
|
|
- If TightButSlow is true, no vertices will be doubled in the
|
|
|
|
- silhouette list. This should only be used when creating re-usable
|
|
|
|
- silhouettes, because it's much slower. *)
|
|
|
|
- procedure AddCapToSilhouette(const v0, v1, v2: TAffineVector; tightButSlow: Boolean); inline;
|
|
|
|
- procedure AddIndexedCapToSilhouette(const Vi0, Vi1, vi2: integer); inline;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- TBaseConnectivity = class
|
|
|
|
- protected
|
|
|
|
- FPrecomputeFaceNormal: Boolean;
|
|
|
|
- function GetEdgeCount: integer; virtual;
|
|
|
|
- function GetFaceCount: integer; virtual;
|
|
|
|
- public
|
|
|
|
- property EdgeCount: integer read GetEdgeCount;
|
|
|
|
- property FaceCount: integer read GetFaceCount;
|
|
|
|
- property PrecomputeFaceNormal: Boolean read FPrecomputeFaceNormal;
|
|
|
|
- procedure CreateSilhouette(const ASilhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
|
|
|
|
- AddToSilhouette: Boolean); virtual;
|
|
|
|
- constructor Create(APrecomputeFaceNormal: Boolean); virtual;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- TConnectivity = class(TBaseConnectivity)
|
|
|
|
- protected
|
|
|
|
- (* All storage of faces and adges are cut up into tiny pieces for a reason,
|
|
|
|
- it'd be nicer with Structs or classes, but it's actually faster this way.
|
|
|
|
- The reason it's faster is because of less cache overwrites when we only
|
|
|
|
- access a tiny bit of a triangle (for instance), not all data. *)
|
|
|
|
- FEdgeVertices: TIntegerList;
|
|
|
|
- FEdgeFaces: TIntegerList;
|
|
|
|
- FFaceVisible: TByteList;
|
|
|
|
- FFaceVertexIndex: TIntegerList;
|
|
|
|
- FFaceNormal: TAffineVectorList;
|
|
|
|
- FVertexMemory: TIntegerList;
|
|
|
|
- FVertices: TAffineVectorList;
|
|
|
|
- function GetEdgeCount: integer;
|
|
|
|
- function GetFaceCount: integer;
|
|
|
|
- function ReuseOrFindVertexID(const SeenFrom: TAffineVector; ASilhouette: TGLSilhouette; index: integer): integer;
|
|
|
|
- public
|
|
|
|
- // Clears out all connectivity information.
|
|
|
|
- procedure Clear; virtual;
|
|
|
|
- procedure CreateSilhouette(const silhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
|
|
|
|
- AddToSilhouette: Boolean);
|
|
|
|
- function AddIndexedEdge(vertexIndex0, vertexIndex1: integer; FaceID: integer): integer;
|
|
|
|
- function AddIndexedFace(Vi0, Vi1, vi2: integer): integer;
|
|
|
|
- function AddFace(const vertex0, vertex1, vertex2: TAffineVector): integer; inline;
|
|
|
|
- function AddQuad(const vertex0, vertex1, vertex2, vertex3: TAffineVector): integer; inline;
|
|
|
|
- property EdgeCount: integer read GetEdgeCount;
|
|
|
|
- property FaceCount: integer read GetFaceCount;
|
|
|
|
- constructor Create(APrecomputeFaceNormal: Boolean); override;
|
|
|
|
- destructor Destroy; override;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
-// -------------------------------------------------------------
|
|
|
|
-implementation
|
|
|
|
-// -------------------------------------------------------------
|
|
|
|
-
|
|
|
|
-// ------------------
|
|
|
|
-// ------------------ TGLSilhouette ------------------
|
|
|
|
-// ------------------
|
|
|
|
-
|
|
|
|
-constructor TGLSilhouette.Create;
|
|
|
|
-begin
|
|
|
|
- inherited;
|
|
|
|
- FVertices := TVectorList.Create;
|
|
|
|
- FIndices := TIntegerList.Create;
|
|
|
|
- FCapIndices := TIntegerList.Create;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-destructor TGLSilhouette.Destroy;
|
|
|
|
-begin
|
|
|
|
- FCapIndices.Free;
|
|
|
|
- FIndices.Free;
|
|
|
|
- FVertices.Free;
|
|
|
|
- inherited;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.SetIndices(const value: TIntegerList);
|
|
|
|
-begin
|
|
|
|
- FIndices.Assign(value);
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.SetCapIndices(const value: TIntegerList);
|
|
|
|
-begin
|
|
|
|
- FCapIndices.Assign(value);
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.SetVertices(const value: TVectorList);
|
|
|
|
-begin
|
|
|
|
- FVertices.Assign(value);
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.Flush;
|
|
|
|
-begin
|
|
|
|
- FVertices.Flush;
|
|
|
|
- FIndices.Flush;
|
|
|
|
- FCapIndices.Flush;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.Clear;
|
|
|
|
-begin
|
|
|
|
- FVertices.Clear;
|
|
|
|
- FIndices.Clear;
|
|
|
|
- FCapIndices.Clear;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.ExtrudeVerticesToInfinity(const origin: TAffineVector);
|
|
|
|
-var
|
|
|
|
- i, nv, ni, nc, k: integer;
|
|
|
|
- vList, vListN: PVectorArray;
|
|
|
|
- iList, iList2: PIntegerArray;
|
|
|
|
-begin
|
|
|
|
- // extrude vertices
|
|
|
|
- nv := Vertices.Count;
|
|
|
|
- Vertices.Count := 2 * nv;
|
|
|
|
- vList := Vertices.List;
|
|
|
|
- vListN := @vList[nv];
|
|
|
|
- for i := 0 to nv - 1 do
|
|
|
|
- begin
|
|
|
|
- vListN^[i].W := 0;
|
|
|
|
- VectorSubtract(PAffineVector(@vList[i])^, origin, PAffineVector(@vListN[i])^);
|
|
|
|
- end;
|
|
|
|
- // change silhouette indices to quad indices
|
|
|
|
- ni := Indices.Count;
|
|
|
|
- Indices.Count := 2 * ni;
|
|
|
|
- iList := Indices.List;
|
|
|
|
- i := ni - 2;
|
|
|
|
- while i >= 0 do
|
|
|
|
- begin
|
|
|
|
- iList2 := @iList^[2 * i];
|
|
|
|
- iList2^[0] := iList^[i];
|
|
|
|
- iList2^[1] := iList^[i + 1];
|
|
|
|
- iList2^[2] := iList^[i + 1] + nv;
|
|
|
|
- iList2^[3] := iList^[i] + nv;
|
|
|
|
- Dec(i, 2);
|
|
|
|
- end;
|
|
|
|
- // add extruded triangles to capIndices
|
|
|
|
- nc := CapIndices.Count;
|
|
|
|
- CapIndices.Capacity := 2 * nc;
|
|
|
|
- iList := CapIndices.List;
|
|
|
|
- for i := nc - 1 downto 0 do
|
|
|
|
- begin
|
|
|
|
- k := iList^[i];
|
|
|
|
- CapIndices.Add(k);
|
|
|
|
- iList^[i] := k + nv;
|
|
|
|
- end;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-// ------------------
|
|
|
|
-// ------------------ TGLSilhouette ------------------
|
|
|
|
-// ------------------
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.AddEdgeToSilhouette(const v0, v1: TAffineVector; tightButSlow: Boolean);
|
|
|
|
-begin
|
|
|
|
- if tightButSlow then
|
|
|
|
- Indices.Add(Vertices.FindOrAddPoint(v0), Vertices.FindOrAddPoint(v1))
|
|
|
|
- else
|
|
|
|
- Indices.Add(Vertices.Add(v0, 1), Vertices.Add(v1, 1));
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.AddIndexedEdgeToSilhouette(const Vi0, Vi1: integer);
|
|
|
|
-
|
|
|
|
-begin
|
|
|
|
- Indices.Add(Vi0, Vi1);
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.AddCapToSilhouette(const v0, v1, v2: TAffineVector; tightButSlow: Boolean);
|
|
|
|
-begin
|
|
|
|
- if tightButSlow then
|
|
|
|
- CapIndices.Add(Vertices.FindOrAddPoint(v0), Vertices.FindOrAddPoint(v1), Vertices.FindOrAddPoint(v2))
|
|
|
|
- else
|
|
|
|
- CapIndices.Add(Vertices.Add(v0, 1), Vertices.Add(v1, 1), Vertices.Add(v2, 1));
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TGLSilhouette.AddIndexedCapToSilhouette(const Vi0, Vi1, vi2: integer);
|
|
|
|
-begin
|
|
|
|
- CapIndices.Add(Vi0, Vi1, vi2);
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-// ------------------
|
|
|
|
-// ------------------ TBaseConnectivity ------------------
|
|
|
|
-// ------------------
|
|
|
|
-
|
|
|
|
-constructor TBaseConnectivity.Create(APrecomputeFaceNormal: Boolean);
|
|
|
|
-begin
|
|
|
|
- FPrecomputeFaceNormal := APrecomputeFaceNormal;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TBaseConnectivity.CreateSilhouette(const ASilhouetteParameters: TGLSilhouetteParameters;
|
|
|
|
- var ASilhouette: TGLSilhouette; AddToSilhouette: Boolean);
|
|
|
|
-begin
|
|
|
|
- // Purely virtual!
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-// ------------------
|
|
|
|
-// ------------------ TConnectivity ------------------
|
|
|
|
-// ------------------
|
|
|
|
-
|
|
|
|
-function TBaseConnectivity.GetEdgeCount: integer;
|
|
|
|
-begin
|
|
|
|
- result := 0;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-function TBaseConnectivity.GetFaceCount: integer;
|
|
|
|
-begin
|
|
|
|
- result := 0;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-constructor TConnectivity.Create(APrecomputeFaceNormal: Boolean);
|
|
|
|
-begin
|
|
|
|
- FFaceVisible := TByteList.Create;
|
|
|
|
-
|
|
|
|
- FFaceVertexIndex := TIntegerList.Create;
|
|
|
|
- FFaceNormal := TAffineVectorList.Create;
|
|
|
|
-
|
|
|
|
- FEdgeVertices := TIntegerList.Create;
|
|
|
|
- FEdgeFaces := TIntegerList.Create;
|
|
|
|
-
|
|
|
|
- FPrecomputeFaceNormal := APrecomputeFaceNormal;
|
|
|
|
-
|
|
|
|
- FVertexMemory := TIntegerList.Create;
|
|
|
|
-
|
|
|
|
- FVertices := TAffineVectorList.Create;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-destructor TConnectivity.Destroy;
|
|
|
|
-begin
|
|
|
|
- Clear;
|
|
|
|
-
|
|
|
|
- FFaceVisible.Free;
|
|
|
|
- FFaceVertexIndex.Free;
|
|
|
|
- FFaceNormal.Free;
|
|
|
|
-
|
|
|
|
- FEdgeVertices.Free;
|
|
|
|
- FEdgeFaces.Free;
|
|
|
|
-
|
|
|
|
- FVertexMemory.Free;
|
|
|
|
-
|
|
|
|
- if Assigned(FVertices) then
|
|
|
|
- FVertices.Free;
|
|
|
|
-
|
|
|
|
- inherited;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TConnectivity.Clear;
|
|
|
|
-begin
|
|
|
|
- FEdgeVertices.Clear;
|
|
|
|
- FEdgeFaces.Clear;
|
|
|
|
- FFaceVisible.Clear;
|
|
|
|
- FFaceVertexIndex.Clear;
|
|
|
|
- FFaceNormal.Clear;
|
|
|
|
- FVertexMemory.Clear;
|
|
|
|
-
|
|
|
|
- if FVertices <> nil then
|
|
|
|
- FVertices.Clear;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TConnectivity.CreateSilhouette(const silhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
|
|
|
|
- AddToSilhouette: Boolean);
|
|
|
|
-var
|
|
|
|
- i: integer;
|
|
|
|
- vis: PIntegerArray;
|
|
|
|
- tVi0, tVi1: integer;
|
|
|
|
- faceNormal: TAffineVector;
|
|
|
|
- face0ID, face1ID: integer;
|
|
|
|
- faceIsVisible: Boolean;
|
|
|
|
- verticesList: PAffineVectorArray;
|
|
|
|
-begin
|
|
|
|
- if not Assigned(ASilhouette) then
|
|
|
|
- ASilhouette := TGLSilhouette.Create
|
|
|
|
- else if not AddToSilhouette then
|
|
|
|
- ASilhouette.Flush;
|
|
|
|
-
|
|
|
|
- // Clear the vertex memory
|
|
|
|
- FVertexMemory.Flush;
|
|
|
|
-
|
|
|
|
- // Update visibility information for all Faces
|
|
|
|
- vis := FFaceVertexIndex.List;
|
|
|
|
- for i := 0 to FaceCount - 1 do
|
|
|
|
- begin
|
|
|
|
- if FPrecomputeFaceNormal then
|
|
|
|
- faceIsVisible := (PointProject(silhouetteParameters.SeenFrom, FVertices.List^[vis^[0]], FFaceNormal.List^[i]) >= 0)
|
|
|
|
- else
|
|
|
|
- begin
|
|
|
|
- verticesList := FVertices.List;
|
|
|
|
- faceNormal := CalcPlaneNormal(verticesList^[vis^[0]], verticesList^[vis^[1]], verticesList^[vis^[2]]);
|
|
|
|
- faceIsVisible := (PointProject(silhouetteParameters.SeenFrom, FVertices.List^[vis^[0]], faceNormal) >= 0);
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- FFaceVisible[i] := Byte(faceIsVisible);
|
|
|
|
-
|
|
|
|
- if (not faceIsVisible) and silhouetteParameters.CappingRequired then
|
|
|
|
- ASilhouette.CapIndices.Add(ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]),
|
|
|
|
- ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]),
|
|
|
|
- ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[2]));
|
|
|
|
- vis := @vis[3];
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- for i := 0 to EdgeCount - 1 do
|
|
|
|
- begin
|
|
|
|
- face0ID := FEdgeFaces[i * 2 + 0];
|
|
|
|
- face1ID := FEdgeFaces[i * 2 + 1];
|
|
|
|
-
|
|
|
|
- if (face1ID = -1) or (FFaceVisible.List^[face0ID] <> FFaceVisible.List^[face1ID]) then
|
|
|
|
- begin
|
|
|
|
- // Retrieve the two vertice values add add them to the Silhouette list
|
|
|
|
- vis := @FEdgeVertices.List[i * 2];
|
|
|
|
-
|
|
|
|
- // In this moment, we _know_ what vertex id the vertex had in the old
|
|
|
|
- // mesh. We can remember this information and re-use it for a speedup
|
|
|
|
- if FFaceVisible.List^[face0ID] = 0 then
|
|
|
|
- begin
|
|
|
|
- tVi0 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]);
|
|
|
|
- tVi1 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]);
|
|
|
|
- ASilhouette.Indices.Add(tVi0, tVi1);
|
|
|
|
- end
|
|
|
|
- else if face1ID > -1 then
|
|
|
|
- begin
|
|
|
|
- tVi0 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]);
|
|
|
|
- tVi1 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]);
|
|
|
|
- ASilhouette.Indices.Add(tVi1, tVi0);
|
|
|
|
- end;
|
|
|
|
- end;
|
|
|
|
- end;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-function TConnectivity.GetEdgeCount: integer;
|
|
|
|
-begin
|
|
|
|
- result := FEdgeVertices.Count div 2;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-function TConnectivity.GetFaceCount: integer;
|
|
|
|
-begin
|
|
|
|
- result := FFaceVisible.Count;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-function TConnectivity.ReuseOrFindVertexID(const SeenFrom: TAffineVector; ASilhouette: TGLSilhouette; index: integer): integer;
|
|
|
|
-var
|
|
|
|
- pMemIndex: PInteger;
|
|
|
|
- memIndex, i: integer;
|
|
|
|
- oldCount: integer;
|
|
|
|
- List: PIntegerArray;
|
|
|
|
-begin
|
|
|
|
- if index >= FVertexMemory.Count then
|
|
|
|
- begin
|
|
|
|
- oldCount := FVertexMemory.Count;
|
|
|
|
- FVertexMemory.Count := index + 1;
|
|
|
|
-
|
|
|
|
- List := FVertexMemory.List;
|
|
|
|
- for i := oldCount to FVertexMemory.Count - 1 do
|
|
|
|
- List^[i] := -1;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- pMemIndex := @FVertexMemory.List[index];
|
|
|
|
-
|
|
|
|
- if pMemIndex^ = -1 then
|
|
|
|
- begin
|
|
|
|
- // Add the "near" vertex
|
|
|
|
- memIndex := ASilhouette.Vertices.Add(FVertices.List^[index], 1);
|
|
|
|
- pMemIndex^ := memIndex;
|
|
|
|
- result := memIndex;
|
|
|
|
- end
|
|
|
|
- else
|
|
|
|
- result := pMemIndex^;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-function TConnectivity.AddIndexedEdge(vertexIndex0, vertexIndex1: integer; FaceID: integer): integer;
|
|
|
|
-var
|
|
|
|
- i: integer;
|
|
|
|
- edgesVertices: PIntegerArray;
|
|
|
|
-begin
|
|
|
|
- // Make sure that the edge doesn't already exists
|
|
|
|
- edgesVertices := FEdgeVertices.List;
|
|
|
|
- for i := 0 to EdgeCount - 1 do
|
|
|
|
- begin
|
|
|
|
- // Retrieve the two vertices in the edge
|
|
|
|
- if ((edgesVertices^[0] = vertexIndex0) and (edgesVertices^[1] = vertexIndex1)) or
|
|
|
|
- ((edgesVertices^[0] = vertexIndex1) and (edgesVertices^[1] = vertexIndex0)) then
|
|
|
|
- begin
|
|
|
|
- // Update the second Face of the edge and we're done (this _MAY_
|
|
|
|
- // overwrite a previous Face in a broken mesh)
|
|
|
|
- FEdgeFaces[i * 2 + 1] := FaceID;
|
|
|
|
- result := i * 2 + 1;
|
|
|
|
- Exit;
|
|
|
|
- end;
|
|
|
|
- edgesVertices := @edgesVertices[2];
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- // No edge was found, create a new one
|
|
|
|
- FEdgeVertices.Add(vertexIndex0, vertexIndex1);
|
|
|
|
- FEdgeFaces.Add(FaceID, -1);
|
|
|
|
-
|
|
|
|
- result := EdgeCount - 1;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-function TConnectivity.AddIndexedFace(Vi0, Vi1, vi2: integer): integer;
|
|
|
|
-var
|
|
|
|
- FaceID: integer;
|
|
|
|
-begin
|
|
|
|
- FFaceVertexIndex.Add(Vi0, Vi1, vi2);
|
|
|
|
-
|
|
|
|
- if FPrecomputeFaceNormal then
|
|
|
|
- FFaceNormal.Add(CalcPlaneNormal(FVertices.List^[Vi0], FVertices.List^[Vi1], FVertices.List^[vi2]));
|
|
|
|
-
|
|
|
|
- FaceID := FFaceVisible.Add(0);
|
|
|
|
- AddIndexedEdge(Vi0, Vi1, FaceID);
|
|
|
|
- AddIndexedEdge(Vi1, vi2, FaceID);
|
|
|
|
- AddIndexedEdge(vi2, Vi0, FaceID);
|
|
|
|
- result := FaceID;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-function TConnectivity.AddFace(const vertex0, vertex1, vertex2: TAffineVector): integer;
|
|
|
|
-var
|
|
|
|
- Vi0, Vi1, vi2: integer;
|
|
|
|
-begin
|
|
|
|
- Vi0 := FVertices.FindOrAdd(vertex0);
|
|
|
|
- Vi1 := FVertices.FindOrAdd(vertex1);
|
|
|
|
- vi2 := FVertices.FindOrAdd(vertex2);
|
|
|
|
-
|
|
|
|
- result := AddIndexedFace(Vi0, Vi1, vi2);
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-function TConnectivity.AddQuad(const vertex0, vertex1, vertex2, vertex3: TAffineVector): integer;
|
|
|
|
-var
|
|
|
|
- Vi0, Vi1, vi2, Vi3: integer;
|
|
|
|
-begin
|
|
|
|
- Vi0 := FVertices.FindOrAdd(vertex0);
|
|
|
|
- Vi1 := FVertices.FindOrAdd(vertex1);
|
|
|
|
- vi2 := FVertices.FindOrAdd(vertex2);
|
|
|
|
- Vi3 := FVertices.FindOrAdd(vertex3);
|
|
|
|
-
|
|
|
|
- // First face
|
|
|
|
- result := AddIndexedFace(Vi0, Vi1, vi2);
|
|
|
|
-
|
|
|
|
- // Second face
|
|
|
|
- AddIndexedFace(vi2, Vi3, Vi0);
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-end.
|
|
|
|
|
|
+//
|
|
|
|
+// This unit is part of the GLScene Engine, http://glscene.org
|
|
|
|
+//
|
|
|
|
+
|
|
|
|
+unit Scene.Silhouette;
|
|
|
|
+
|
|
|
|
+(*
|
|
|
|
+ Enhanced silhouette classes.
|
|
|
|
+ Introduces more evolved/specific silhouette generation and management
|
|
|
|
+ classes.
|
|
|
|
+ CAUTION : both connectivity classes leak memory.
|
|
|
|
+*)
|
|
|
|
+
|
|
|
|
+interface
|
|
|
|
+
|
|
|
|
+{$I GLScene.inc}
|
|
|
|
+
|
|
|
|
+uses
|
|
|
|
+ System.Classes,
|
|
|
|
+ System.SysUtils,
|
|
|
|
+
|
|
|
|
+ Scene.VectorTypes,
|
|
|
|
+ Scene.VectorGeometry,
|
|
|
|
+ Scene.VectorLists;
|
|
|
|
+
|
|
|
|
+type
|
|
|
|
+ TGLSilhouetteStyle = (ssOmni, ssParallel);
|
|
|
|
+
|
|
|
|
+ (* Silouhette generation parameters.
|
|
|
|
+ SeenFrom and LightDirection are expected in local coordinates. *)
|
|
|
|
+ TGLSilhouetteParameters = packed record
|
|
|
|
+ SeenFrom, LightDirection: TAffineVector;
|
|
|
|
+ Style: TGLSilhouetteStyle;
|
|
|
|
+ CappingRequired: Boolean;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ (* Base class storing a volume silhouette.
|
|
|
|
+ Made of a set of indexed vertices defining an outline, and another set
|
|
|
|
+ of indexed vertices defining a capping volume. Coordinates system
|
|
|
|
+ is the object's unscaled local coordinates system.
|
|
|
|
+ This is the base class, you can use the TGLSilhouette subclass if you
|
|
|
|
+ need some helper methods for generating the indexed sets. *)
|
|
|
|
+ TGLSilhouette = class
|
|
|
|
+ private
|
|
|
|
+ FVertices: TVectorList;
|
|
|
|
+ FIndices: TIntegerList;
|
|
|
|
+ FCapIndices: TIntegerList;
|
|
|
|
+ FParameters: TGLSilhouetteParameters;
|
|
|
|
+ protected
|
|
|
|
+ procedure SetIndices(const value: TIntegerList);
|
|
|
|
+ procedure SetCapIndices(const value: TIntegerList);
|
|
|
|
+ procedure SetVertices(const value: TVectorList);
|
|
|
|
+ public
|
|
|
|
+ constructor Create; virtual;
|
|
|
|
+ destructor Destroy; override;
|
|
|
|
+ property Parameters: TGLSilhouetteParameters read FParameters write FParameters;
|
|
|
|
+ property Vertices: TVectorList read FVertices write SetVertices;
|
|
|
|
+ property Indices: TIntegerList read FIndices write SetIndices;
|
|
|
|
+ property CapIndices: TIntegerList read FCapIndices write SetCapIndices;
|
|
|
|
+ procedure Flush; virtual;
|
|
|
|
+ procedure Clear; inline;
|
|
|
|
+ procedure ExtrudeVerticesToInfinity(const origin: TAffineVector);
|
|
|
|
+ (* Adds an edge (two vertices) to the silhouette.
|
|
|
|
+ If TightButSlow is true, no vertices will be doubled in the
|
|
|
|
+ silhouette list. This should only be used when creating re-usable
|
|
|
|
+ silhouettes, because it's much slower. *)
|
|
|
|
+ procedure AddEdgeToSilhouette(const v0, v1: TAffineVector; tightButSlow: Boolean); inline;
|
|
|
|
+ procedure AddIndexedEdgeToSilhouette(const Vi0, Vi1: integer); inline;
|
|
|
|
+
|
|
|
|
+ (* Adds a capping triangle to the silhouette.
|
|
|
|
+ If TightButSlow is true, no vertices will be doubled in the
|
|
|
|
+ silhouette list. This should only be used when creating re-usable
|
|
|
|
+ silhouettes, because it's much slower. *)
|
|
|
|
+ procedure AddCapToSilhouette(const v0, v1, v2: TAffineVector; tightButSlow: Boolean); inline;
|
|
|
|
+ procedure AddIndexedCapToSilhouette(const Vi0, Vi1, vi2: integer); inline;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ TBaseConnectivity = class
|
|
|
|
+ protected
|
|
|
|
+ FPrecomputeFaceNormal: Boolean;
|
|
|
|
+ function GetEdgeCount: integer; virtual;
|
|
|
|
+ function GetFaceCount: integer; virtual;
|
|
|
|
+ public
|
|
|
|
+ property EdgeCount: integer read GetEdgeCount;
|
|
|
|
+ property FaceCount: integer read GetFaceCount;
|
|
|
|
+ property PrecomputeFaceNormal: Boolean read FPrecomputeFaceNormal;
|
|
|
|
+ procedure CreateSilhouette(const ASilhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
|
|
|
|
+ AddToSilhouette: Boolean); virtual;
|
|
|
|
+ constructor Create(APrecomputeFaceNormal: Boolean); virtual;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ TConnectivity = class(TBaseConnectivity)
|
|
|
|
+ protected
|
|
|
|
+ (* All storage of faces and adges are cut up into tiny pieces for a reason,
|
|
|
|
+ it'd be nicer with Structs or classes, but it's actually faster this way.
|
|
|
|
+ The reason it's faster is because of less cache overwrites when we only
|
|
|
|
+ access a tiny bit of a triangle (for instance), not all data. *)
|
|
|
|
+ FEdgeVertices: TIntegerList;
|
|
|
|
+ FEdgeFaces: TIntegerList;
|
|
|
|
+ FFaceVisible: TByteList;
|
|
|
|
+ FFaceVertexIndex: TIntegerList;
|
|
|
|
+ FFaceNormal: TAffineVectorList;
|
|
|
|
+ FVertexMemory: TIntegerList;
|
|
|
|
+ FVertices: TAffineVectorList;
|
|
|
|
+ function GetEdgeCount: integer;
|
|
|
|
+ function GetFaceCount: integer;
|
|
|
|
+ function ReuseOrFindVertexID(const SeenFrom: TAffineVector; ASilhouette: TGLSilhouette; index: integer): integer;
|
|
|
|
+ public
|
|
|
|
+ // Clears out all connectivity information.
|
|
|
|
+ procedure Clear; virtual;
|
|
|
|
+ procedure CreateSilhouette(const silhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
|
|
|
|
+ AddToSilhouette: Boolean);
|
|
|
|
+ function AddIndexedEdge(vertexIndex0, vertexIndex1: integer; FaceID: integer): integer;
|
|
|
|
+ function AddIndexedFace(Vi0, Vi1, vi2: integer): integer;
|
|
|
|
+ function AddFace(const vertex0, vertex1, vertex2: TAffineVector): integer; inline;
|
|
|
|
+ function AddQuad(const vertex0, vertex1, vertex2, vertex3: TAffineVector): integer; inline;
|
|
|
|
+ property EdgeCount: integer read GetEdgeCount;
|
|
|
|
+ property FaceCount: integer read GetFaceCount;
|
|
|
|
+ constructor Create(APrecomputeFaceNormal: Boolean); override;
|
|
|
|
+ destructor Destroy; override;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+// -------------------------------------------------------------
|
|
|
|
+implementation
|
|
|
|
+// -------------------------------------------------------------
|
|
|
|
+
|
|
|
|
+// ------------------
|
|
|
|
+// ------------------ TGLSilhouette ------------------
|
|
|
|
+// ------------------
|
|
|
|
+
|
|
|
|
+constructor TGLSilhouette.Create;
|
|
|
|
+begin
|
|
|
|
+ inherited;
|
|
|
|
+ FVertices := TVectorList.Create;
|
|
|
|
+ FIndices := TIntegerList.Create;
|
|
|
|
+ FCapIndices := TIntegerList.Create;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+destructor TGLSilhouette.Destroy;
|
|
|
|
+begin
|
|
|
|
+ FCapIndices.Free;
|
|
|
|
+ FIndices.Free;
|
|
|
|
+ FVertices.Free;
|
|
|
|
+ inherited;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.SetIndices(const value: TIntegerList);
|
|
|
|
+begin
|
|
|
|
+ FIndices.Assign(value);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.SetCapIndices(const value: TIntegerList);
|
|
|
|
+begin
|
|
|
|
+ FCapIndices.Assign(value);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.SetVertices(const value: TVectorList);
|
|
|
|
+begin
|
|
|
|
+ FVertices.Assign(value);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.Flush;
|
|
|
|
+begin
|
|
|
|
+ FVertices.Flush;
|
|
|
|
+ FIndices.Flush;
|
|
|
|
+ FCapIndices.Flush;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.Clear;
|
|
|
|
+begin
|
|
|
|
+ FVertices.Clear;
|
|
|
|
+ FIndices.Clear;
|
|
|
|
+ FCapIndices.Clear;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.ExtrudeVerticesToInfinity(const origin: TAffineVector);
|
|
|
|
+var
|
|
|
|
+ i, nv, ni, nc, k: integer;
|
|
|
|
+ vList, vListN: PVectorArray;
|
|
|
|
+ iList, iList2: PIntegerArray;
|
|
|
|
+begin
|
|
|
|
+ // extrude vertices
|
|
|
|
+ nv := Vertices.Count;
|
|
|
|
+ Vertices.Count := 2 * nv;
|
|
|
|
+ vList := Vertices.List;
|
|
|
|
+ vListN := @vList[nv];
|
|
|
|
+ for i := 0 to nv - 1 do
|
|
|
|
+ begin
|
|
|
|
+ vListN^[i].W := 0;
|
|
|
|
+ VectorSubtract(PAffineVector(@vList[i])^, origin, PAffineVector(@vListN[i])^);
|
|
|
|
+ end;
|
|
|
|
+ // change silhouette indices to quad indices
|
|
|
|
+ ni := Indices.Count;
|
|
|
|
+ Indices.Count := 2 * ni;
|
|
|
|
+ iList := Indices.List;
|
|
|
|
+ i := ni - 2;
|
|
|
|
+ while i >= 0 do
|
|
|
|
+ begin
|
|
|
|
+ iList2 := @iList^[2 * i];
|
|
|
|
+ iList2^[0] := iList^[i];
|
|
|
|
+ iList2^[1] := iList^[i + 1];
|
|
|
|
+ iList2^[2] := iList^[i + 1] + nv;
|
|
|
|
+ iList2^[3] := iList^[i] + nv;
|
|
|
|
+ Dec(i, 2);
|
|
|
|
+ end;
|
|
|
|
+ // add extruded triangles to capIndices
|
|
|
|
+ nc := CapIndices.Count;
|
|
|
|
+ CapIndices.Capacity := 2 * nc;
|
|
|
|
+ iList := CapIndices.List;
|
|
|
|
+ for i := nc - 1 downto 0 do
|
|
|
|
+ begin
|
|
|
|
+ k := iList^[i];
|
|
|
|
+ CapIndices.Add(k);
|
|
|
|
+ iList^[i] := k + nv;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// ------------------
|
|
|
|
+// ------------------ TGLSilhouette ------------------
|
|
|
|
+// ------------------
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.AddEdgeToSilhouette(const v0, v1: TAffineVector; tightButSlow: Boolean);
|
|
|
|
+begin
|
|
|
|
+ if tightButSlow then
|
|
|
|
+ Indices.Add(Vertices.FindOrAddPoint(v0), Vertices.FindOrAddPoint(v1))
|
|
|
|
+ else
|
|
|
|
+ Indices.Add(Vertices.Add(v0, 1), Vertices.Add(v1, 1));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.AddIndexedEdgeToSilhouette(const Vi0, Vi1: integer);
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ Indices.Add(Vi0, Vi1);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.AddCapToSilhouette(const v0, v1, v2: TAffineVector; tightButSlow: Boolean);
|
|
|
|
+begin
|
|
|
|
+ if tightButSlow then
|
|
|
|
+ CapIndices.Add(Vertices.FindOrAddPoint(v0), Vertices.FindOrAddPoint(v1), Vertices.FindOrAddPoint(v2))
|
|
|
|
+ else
|
|
|
|
+ CapIndices.Add(Vertices.Add(v0, 1), Vertices.Add(v1, 1), Vertices.Add(v2, 1));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TGLSilhouette.AddIndexedCapToSilhouette(const Vi0, Vi1, vi2: integer);
|
|
|
|
+begin
|
|
|
|
+ CapIndices.Add(Vi0, Vi1, vi2);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// ------------------
|
|
|
|
+// ------------------ TBaseConnectivity ------------------
|
|
|
|
+// ------------------
|
|
|
|
+
|
|
|
|
+constructor TBaseConnectivity.Create(APrecomputeFaceNormal: Boolean);
|
|
|
|
+begin
|
|
|
|
+ FPrecomputeFaceNormal := APrecomputeFaceNormal;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TBaseConnectivity.CreateSilhouette(const ASilhouetteParameters: TGLSilhouetteParameters;
|
|
|
|
+ var ASilhouette: TGLSilhouette; AddToSilhouette: Boolean);
|
|
|
|
+begin
|
|
|
|
+ // Purely virtual!
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// ------------------
|
|
|
|
+// ------------------ TConnectivity ------------------
|
|
|
|
+// ------------------
|
|
|
|
+
|
|
|
|
+function TBaseConnectivity.GetEdgeCount: integer;
|
|
|
|
+begin
|
|
|
|
+ result := 0;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TBaseConnectivity.GetFaceCount: integer;
|
|
|
|
+begin
|
|
|
|
+ result := 0;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+constructor TConnectivity.Create(APrecomputeFaceNormal: Boolean);
|
|
|
|
+begin
|
|
|
|
+ FFaceVisible := TByteList.Create;
|
|
|
|
+
|
|
|
|
+ FFaceVertexIndex := TIntegerList.Create;
|
|
|
|
+ FFaceNormal := TAffineVectorList.Create;
|
|
|
|
+
|
|
|
|
+ FEdgeVertices := TIntegerList.Create;
|
|
|
|
+ FEdgeFaces := TIntegerList.Create;
|
|
|
|
+
|
|
|
|
+ FPrecomputeFaceNormal := APrecomputeFaceNormal;
|
|
|
|
+ FVertexMemory := TIntegerList.Create;
|
|
|
|
+ FVertices := TAffineVectorList.Create;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+destructor TConnectivity.Destroy;
|
|
|
|
+begin
|
|
|
|
+ Clear;
|
|
|
|
+
|
|
|
|
+ FFaceVisible.Free;
|
|
|
|
+ FFaceVertexIndex.Free;
|
|
|
|
+ FFaceNormal.Free;
|
|
|
|
+
|
|
|
|
+ FEdgeVertices.Free;
|
|
|
|
+ FEdgeFaces.Free;
|
|
|
|
+
|
|
|
|
+ FVertexMemory.Free;
|
|
|
|
+
|
|
|
|
+ if Assigned(FVertices) then
|
|
|
|
+ FVertices.Free;
|
|
|
|
+
|
|
|
|
+ inherited;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TConnectivity.Clear;
|
|
|
|
+begin
|
|
|
|
+ FEdgeVertices.Clear;
|
|
|
|
+ FEdgeFaces.Clear;
|
|
|
|
+ FFaceVisible.Clear;
|
|
|
|
+ FFaceVertexIndex.Clear;
|
|
|
|
+ FFaceNormal.Clear;
|
|
|
|
+ FVertexMemory.Clear;
|
|
|
|
+
|
|
|
|
+ if FVertices <> nil then
|
|
|
|
+ FVertices.Clear;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TConnectivity.CreateSilhouette(const silhouetteParameters: TGLSilhouetteParameters; var ASilhouette: TGLSilhouette;
|
|
|
|
+ AddToSilhouette: Boolean);
|
|
|
|
+var
|
|
|
|
+ i: integer;
|
|
|
|
+ vis: PIntegerArray;
|
|
|
|
+ tVi0, tVi1: integer;
|
|
|
|
+ faceNormal: TAffineVector;
|
|
|
|
+ face0ID, face1ID: integer;
|
|
|
|
+ faceIsVisible: Boolean;
|
|
|
|
+ verticesList: PAffineVectorArray;
|
|
|
|
+begin
|
|
|
|
+ if not Assigned(ASilhouette) then
|
|
|
|
+ ASilhouette := TGLSilhouette.Create
|
|
|
|
+ else if not AddToSilhouette then
|
|
|
|
+ ASilhouette.Flush;
|
|
|
|
+
|
|
|
|
+ // Clear the vertex memory
|
|
|
|
+ FVertexMemory.Flush;
|
|
|
|
+
|
|
|
|
+ // Update visibility information for all Faces
|
|
|
|
+ vis := FFaceVertexIndex.List;
|
|
|
|
+ for i := 0 to FaceCount - 1 do
|
|
|
|
+ begin
|
|
|
|
+ if FPrecomputeFaceNormal then
|
|
|
|
+ faceIsVisible := (PointProject(silhouetteParameters.SeenFrom, FVertices.List^[vis^[0]], FFaceNormal.List^[i]) >= 0)
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ verticesList := FVertices.List;
|
|
|
|
+ faceNormal := CalcPlaneNormal(verticesList^[vis^[0]], verticesList^[vis^[1]], verticesList^[vis^[2]]);
|
|
|
|
+ faceIsVisible := (PointProject(silhouetteParameters.SeenFrom, FVertices.List^[vis^[0]], faceNormal) >= 0);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ FFaceVisible[i] := Byte(faceIsVisible);
|
|
|
|
+
|
|
|
|
+ if (not faceIsVisible) and silhouetteParameters.CappingRequired then
|
|
|
|
+ ASilhouette.CapIndices.Add(ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]),
|
|
|
|
+ ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]),
|
|
|
|
+ ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[2]));
|
|
|
|
+ vis := @vis[3];
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ for i := 0 to EdgeCount - 1 do
|
|
|
|
+ begin
|
|
|
|
+ face0ID := FEdgeFaces[i * 2 + 0];
|
|
|
|
+ face1ID := FEdgeFaces[i * 2 + 1];
|
|
|
|
+
|
|
|
|
+ if (face1ID = -1) or (FFaceVisible.List^[face0ID] <> FFaceVisible.List^[face1ID]) then
|
|
|
|
+ begin
|
|
|
|
+ // Retrieve the two vertice values add add them to the Silhouette list
|
|
|
|
+ vis := @FEdgeVertices.List[i * 2];
|
|
|
|
+
|
|
|
|
+ // In this moment, we _know_ what vertex id the vertex had in the old
|
|
|
|
+ // mesh. We can remember this information and re-use it for a speedup
|
|
|
|
+ if FFaceVisible.List^[face0ID] = 0 then
|
|
|
|
+ begin
|
|
|
|
+ tVi0 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]);
|
|
|
|
+ tVi1 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]);
|
|
|
|
+ ASilhouette.Indices.Add(tVi0, tVi1);
|
|
|
|
+ end
|
|
|
|
+ else if face1ID > -1 then
|
|
|
|
+ begin
|
|
|
|
+ tVi0 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[0]);
|
|
|
|
+ tVi1 := ReuseOrFindVertexID(silhouetteParameters.SeenFrom, ASilhouette, vis^[1]);
|
|
|
|
+ ASilhouette.Indices.Add(tVi1, tVi0);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TConnectivity.GetEdgeCount: integer;
|
|
|
|
+begin
|
|
|
|
+ result := FEdgeVertices.Count div 2;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TConnectivity.GetFaceCount: integer;
|
|
|
|
+begin
|
|
|
|
+ result := FFaceVisible.Count;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TConnectivity.ReuseOrFindVertexID(const SeenFrom: TAffineVector; ASilhouette: TGLSilhouette; index: integer): integer;
|
|
|
|
+var
|
|
|
|
+ pMemIndex: PInteger;
|
|
|
|
+ memIndex, i: integer;
|
|
|
|
+ oldCount: integer;
|
|
|
|
+ List: PIntegerArray;
|
|
|
|
+begin
|
|
|
|
+ if index >= FVertexMemory.Count then
|
|
|
|
+ begin
|
|
|
|
+ oldCount := FVertexMemory.Count;
|
|
|
|
+ FVertexMemory.Count := index + 1;
|
|
|
|
+
|
|
|
|
+ List := FVertexMemory.List;
|
|
|
|
+ for i := oldCount to FVertexMemory.Count - 1 do
|
|
|
|
+ List^[i] := -1;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ pMemIndex := @FVertexMemory.List[index];
|
|
|
|
+
|
|
|
|
+ if pMemIndex^ = -1 then
|
|
|
|
+ begin
|
|
|
|
+ // Add the "near" vertex
|
|
|
|
+ memIndex := ASilhouette.Vertices.Add(FVertices.List^[index], 1);
|
|
|
|
+ pMemIndex^ := memIndex;
|
|
|
|
+ result := memIndex;
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ result := pMemIndex^;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TConnectivity.AddIndexedEdge(vertexIndex0, vertexIndex1: integer; FaceID: integer): integer;
|
|
|
|
+var
|
|
|
|
+ i: integer;
|
|
|
|
+ edgesVertices: PIntegerArray;
|
|
|
|
+begin
|
|
|
|
+ // Make sure that the edge doesn't already exists
|
|
|
|
+ edgesVertices := FEdgeVertices.List;
|
|
|
|
+ for i := 0 to EdgeCount - 1 do
|
|
|
|
+ begin
|
|
|
|
+ // Retrieve the two vertices in the edge
|
|
|
|
+ if ((edgesVertices^[0] = vertexIndex0) and (edgesVertices^[1] = vertexIndex1)) or
|
|
|
|
+ ((edgesVertices^[0] = vertexIndex1) and (edgesVertices^[1] = vertexIndex0)) then
|
|
|
|
+ begin
|
|
|
|
+ // Update the second Face of the edge and we're done (this _MAY_
|
|
|
|
+ // overwrite a previous Face in a broken mesh)
|
|
|
|
+ FEdgeFaces[i * 2 + 1] := FaceID;
|
|
|
|
+ result := i * 2 + 1;
|
|
|
|
+ Exit;
|
|
|
|
+ end;
|
|
|
|
+ edgesVertices := @edgesVertices[2];
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ // No edge was found, create a new one
|
|
|
|
+ FEdgeVertices.Add(vertexIndex0, vertexIndex1);
|
|
|
|
+ FEdgeFaces.Add(FaceID, -1);
|
|
|
|
+
|
|
|
|
+ result := EdgeCount - 1;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TConnectivity.AddIndexedFace(Vi0, Vi1, vi2: integer): integer;
|
|
|
|
+var
|
|
|
|
+ FaceID: integer;
|
|
|
|
+begin
|
|
|
|
+ FFaceVertexIndex.Add(Vi0, Vi1, vi2);
|
|
|
|
+
|
|
|
|
+ if FPrecomputeFaceNormal then
|
|
|
|
+ FFaceNormal.Add(CalcPlaneNormal(FVertices.List^[Vi0], FVertices.List^[Vi1], FVertices.List^[vi2]));
|
|
|
|
+
|
|
|
|
+ FaceID := FFaceVisible.Add(0);
|
|
|
|
+ AddIndexedEdge(Vi0, Vi1, FaceID);
|
|
|
|
+ AddIndexedEdge(Vi1, vi2, FaceID);
|
|
|
|
+ AddIndexedEdge(vi2, Vi0, FaceID);
|
|
|
|
+ result := FaceID;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TConnectivity.AddFace(const vertex0, vertex1, vertex2: TAffineVector): integer;
|
|
|
|
+var
|
|
|
|
+ Vi0, Vi1, vi2: integer;
|
|
|
|
+begin
|
|
|
|
+ Vi0 := FVertices.FindOrAdd(vertex0);
|
|
|
|
+ Vi1 := FVertices.FindOrAdd(vertex1);
|
|
|
|
+ vi2 := FVertices.FindOrAdd(vertex2);
|
|
|
|
+
|
|
|
|
+ result := AddIndexedFace(Vi0, Vi1, vi2);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TConnectivity.AddQuad(const vertex0, vertex1, vertex2, vertex3: TAffineVector): integer;
|
|
|
|
+var
|
|
|
|
+ Vi0, Vi1, vi2, Vi3: integer;
|
|
|
|
+begin
|
|
|
|
+ Vi0 := FVertices.FindOrAdd(vertex0);
|
|
|
|
+ Vi1 := FVertices.FindOrAdd(vertex1);
|
|
|
|
+ vi2 := FVertices.FindOrAdd(vertex2);
|
|
|
|
+ Vi3 := FVertices.FindOrAdd(vertex3);
|
|
|
|
+
|
|
|
|
+ // First face
|
|
|
|
+ result := AddIndexedFace(Vi0, Vi1, vi2);
|
|
|
|
+
|
|
|
|
+ // Second face
|
|
|
|
+ AddIndexedFace(vi2, Vi3, Vi0);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+end.
|