123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- //
- // The graphics engine GXScene https://github.com/glscene
- //
- unit GXS.MeshOptimizer;
- (* Mesh optimization *)
- interface
- uses
- System.Classes,
- System.SysUtils,
- Stage.VectorGeometry,
- Stage.VectorTypes,
- GXS.VectorFileObjects,
- GXS.PersistentClasses,
- GXS.VectorLists,
- GXS.MeshUtils;
- type
- TMeshOptimizerOption = (mooStandardize, mooVertexCache, mooSortByMaterials, mooMergeObjects);
- TMeshOptimizerOptions = set of TMeshOptimizerOption;
- var
- vDefaultMeshOptimizerOptions : TMeshOptimizerOptions = [mooStandardize, mooVertexCache, mooSortByMaterials, mooMergeObjects];
- procedure OptimizeMesh(aList : TgxMeshObjectList; options : TMeshOptimizerOptions); overload;
- procedure OptimizeMesh(aList : TgxMeshObjectList); overload;
- procedure OptimizeMesh(aMeshObject : TgxMeshObject; options : TMeshOptimizerOptions); overload;
- procedure OptimizeMesh(aMeshObject : TgxMeshObject); overload;
- procedure FacesSmooth(aMeshObj: TgxMeshObject; aWeldDistance: Single=0.0000001; aThreshold: Single=35.0; InvertNormals:boolean=false);
- // ------------------------------------------------------------------
- implementation
- // ------------------------------------------------------------------
- procedure OptimizeMesh(aList : TgxMeshObjectList);
- begin
- OptimizeMesh(aList, vDefaultMeshOptimizerOptions);
- end;
- procedure OptimizeMesh(aList : TgxMeshObjectList; options : TMeshOptimizerOptions);
- var
- i, k : Integer;
- mob, mo : TgxMeshObject;
- fg : TgxFaceGroup;
- fgvi : TgxFGVertexIndexList;
- begin
- // optimize all mesh objects
- for i:=0 to aList.Count-1 do begin
- OptimizeMesh(aList[i], options);
- end;
- if (mooStandardize in options) then begin
- // drop mesh objects that have become empty
- for i:=aList.Count-1 downto 0 do begin
- if (aList[i].Mode=momFaceGroups) and (aList[i].FaceGroups.Count=0) then
- aList[i].Free;
- end;
- end;
- if (aList.Count>0) and (mooMergeObjects in options) then begin
- mob:=aList[0];
- Assert(mob.Mode=momFaceGroups);
- for i:=1 to aList.Count-1 do begin
- mo:=aList[i];
- Assert(mo.Mode=momFaceGroups);
- k:=mob.Vertices.Count;
- mob.Vertices.Add(mo.Vertices);
- mob.Normals.Add(mo.Normals);
- mob.TexCoords.Add(mo.TexCoords);
- while mo.FaceGroups.Count>0 do begin
- fg:=mo.FaceGroups[0];
- fgvi:=(fg as TgxFGVertexIndexList);
- fgvi.Owner:=mob.FaceGroups;
- mob.FaceGroups.Add(fgvi);
- mo.FaceGroups.Delete(0);
- fgvi.VertexIndices.Offset(k);
- end;
- end;
- for i:=aList.Count-1 downto 1 do
- aList[i].Free;
- end;
- end;
- procedure OptimizeMesh(aMeshObject : TgxMeshObject);
- begin
- OptimizeMesh(aMeshObject, vDefaultMeshOptimizerOptions);
- end;
- procedure OptimizeMesh(aMeshObject : TgxMeshObject; options : TMeshOptimizerOptions);
- var
- i : Integer;
- fg : TgxFaceGroup;
- coords, texCoords, normals : TgxAffineVectorList;
- il : TgxIntegerList;
- materialName : String;
- begin
- if (mooMergeObjects in options) then begin
- if aMeshObject.Mode=momFaceGroups then begin
- // remove empty facegroups
- for i:=aMeshObject.FaceGroups.Count-1 downto 0 do begin
- fg:=aMeshObject.FaceGroups[i];
- if fg.TriangleCount=0 then
- fg.Free;
- end;
- end;
- end;
- if (mooStandardize in options) then begin
- if (aMeshObject.Mode<>momFaceGroups) or (aMeshObject.FaceGroups.Count<=1) then begin
- if aMeshObject.FaceGroups.Count=1 then
- materialName:=aMeshObject.FaceGroups[0].MaterialName;
- texCoords:=TgxAffineVectorList.Create;
- normals:=TgxAffineVectorList.Create;
- coords:=aMeshObject.ExtractTriangles(texCoords, normals);
- try
- il:=BuildVectorCountOptimizedIndices(coords, normals, texCoords);
- try
- aMeshObject.Clear;
- if il.Count>0 then begin
- RemapReferences(normals, il);
- RemapReferences(texCoords, il);
- RemapAndCleanupReferences(coords, il);
- aMeshObject.Vertices:=coords;
- aMeshObject.Normals:=normals;
- aMeshObject.TexCoords:=texCoords;
- fg:=TgxFGVertexIndexList.CreateOwned(aMeshObject.FaceGroups);
- fg.MaterialName:=materialName;
- TgxFGVertexIndexList(fg).VertexIndices:=il;
- end;
- finally
- il.Free;
- end;
- finally
- coords.Free;
- normals.Free;
- texCoords.Free;
- end;
- end else
- Assert(False, 'Standardization with multiple facegroups not supported');
- end;
- if (mooVertexCache in options) and (aMeshObject.Mode=momFaceGroups) then begin
- for i:=0 to aMeshObject.FaceGroups.Count-1 do begin
- fg:=aMeshObject.FaceGroups[i];
- if fg.ClassType=TgxFGVertexIndexList then with TgxFGVertexIndexList(fg) do begin
- if Mode in [fgmmTriangles, fgmmFlatTriangles] then
- IncreaseCoherency(VertexIndices, 12);
- end;
- end;
- end;
- if mooSortByMaterials in options then
- aMeshObject.FaceGroups.SortByMaterial;
- end;
- procedure FacesSmooth(aMeshObj: TgxMeshObject; aWeldDistance: Single=0.0000001; aThreshold: Single=35.0; InvertNormals:boolean=false);
- Var
- I, J, K, L: integer;
- WeldedVertex: TgxAffineVectorList;
- TmpIntegerList: TgxIntegerList;
- IndexMap: TStringList;
- n: TAffineVector;
- indicesMap : TgxIntegerList;
- Index: Integer;
- FaceList: TgxIntegerList;
- NormalList: TgxAffineVectorList;
- FaceNormalList: TgxAffineVectorList;
- FaceGroup: TgxFaceGroup;
- FG, FG1: TgxFGVertexIndexList;
- Threshold: Single;
- Angle: Single;
- ReferenceMap: TgxIntegerList;
- ID1, ID2: Integer;
- Index1, Index2, Index3: Integer;
- function FindReferenceIndex(aID: Integer): Integer;
- begin
- Result := ReferenceMap[aID];
- end;
- function iMin(a, b: Integer): Integer;
- begin
- if a<b then
- Result := a
- else
- Result := b;
- end;
- function iMax(a, b: Integer): Integer;
- begin
- if a>b then
- Result := a
- else
- Result := b;
- end;
- begin
- Threshold := aThreshold * Pi/180.0;
- //build the vectices reference map
- ReferenceMap := TgxIntegerList.Create;
- WeldedVertex := TgxAffineVectorList.Create;
- WeldedVertex.Assign(aMeshObj.Vertices);
- indicesMap := TgxIntegerList.Create;
- //first of all, weld the very closed vertices
- WeldVertices(WeldedVertex, indicesMap, aWeldDistance);
- //then, rebuild the map list
- IndexMap := TStringList.Create;
- for I:=0 to WeldedVertex.Count-1 do
- begin
- ReferenceMap.Assign(indicesMap);
- TmpIntegerList := TgxIntegerList.Create;
- Index := ReferenceMap.IndexOf(I);
- while Index>=0 do
- begin
- TmpIntegerList.Add(Index);
- ReferenceMap[Index] := -99999;
- Index := ReferenceMap.IndexOf(I);
- end;
- IndexMap.AddObject(IntToStr(I), TmpIntegerList);
- end;
- ReferenceMap.Assign(indicesMap);
- //never used these, free them all
- WeldedVertex.free;
- indicesMap.free;
- //create a TexPoint list for save face infomation, where s=facegroup index, t=face index
- FaceList := TgxIntegerList.Create;
- NormalList := TgxAffineVectorList.Create;
- FaceNormalList := TgxAffineVectorList.Create;
- //NormalIndex := TgxIntegerList.Create;
- for I:=0 to aMeshObj.FaceGroups.Count-1 do
- begin
- FaceGroup := aMeshObj.FaceGroups[I];
- TmpIntegerList := TgxFGVertexIndexList(FaceGroup).VertexIndices;
- for J:=0 to (TmpIntegerList.Count div 3)-1 do
- begin
- FaceList.Add(I);
- FaceList.Add(J);
- CalcPlaneNormal(aMeshObj.Vertices[TmpIntegerList[J * 3 + 0]],
- aMeshObj.Vertices[TmpIntegerList[J * 3 + 1]],
- aMeshObj.Vertices[TmpIntegerList[J * 3 + 2]],
- n);
- //add three normals for one trangle
- FaceNormalList.Add(n);
- NormalList.Add(n);
- NormalList.Add(n);
- NormalList.Add(n);
- end;
- end;
- //do smooth
- for I:=0 to (FaceList.Count div 2)-1 do
- begin
- Index := FaceList[I*2+0];
- Index1 := FaceList[I*2+1];
- FG := TgxFGVertexIndexList(aMeshObj.FaceGroups[Index]);
- for J:=0 to 2 do
- begin
- for K:=0 to (FaceList.Count div 2)-1 do
- begin
- Index2 := FaceList[K*2+0];
- Index3 := FaceList[K*2+1];
- FG1 := TgxFGVertexIndexList(aMeshObj.FaceGroups[Index2]);
- if I<>K then
- begin
- for L:=0 to 2 do
- begin
- //two face contain the same vertex
- ID1 := FindReferenceIndex(FG.VertexIndices[Index1*3+J]);
- ID2 := FindReferenceIndex(FG1.VertexIndices[Index3*3+L]);
- if ID1=ID2 then
- begin
- Angle := VectorDotProduct(FaceNormalList[I],FaceNormalList[K]);
- if angle>Threshold then
- NormalList[I*3+J] := VectorAdd(NormalList[I*3+J],FaceNormalList[K]);
- end;
- end;
- end;
- end;
- n := NormalList[I*3+J];
- NormalizeVector(n);
- NormalList[I*3+J] := n;
- end;
- end;
- for I:=0 to (FaceList.Count div 2)-1 do
- begin
- Index := FaceList[I*2+0];
- FG := TgxFGVertexIndexList(aMeshObj.FaceGroups[Index]);
- Index := FaceList[I*2+1];
- aMeshObj.Normals[FG.VertexIndices[(Index*3+0)]] := NormalList[(I*3+0)];
- aMeshObj.Normals[FG.VertexIndices[(Index*3+1)]] := NormalList[(I*3+1)];
- aMeshObj.Normals[FG.VertexIndices[(Index*3+2)]] := NormalList[(I*3+2)];
- if InvertNormals then
- begin
- aMeshObj.Normals[FG.VertexIndices[(Index*3+0)]] := VectorNegate(aMeshObj.Normals[FG.VertexIndices[(Index*3+0)]]);
- aMeshObj.Normals[FG.VertexIndices[(Index*3+1)]] := VectorNegate(aMeshObj.Normals[FG.VertexIndices[(Index*3+1)]]);
- aMeshObj.Normals[FG.VertexIndices[(Index*3+2)]] := VectorNegate(aMeshObj.Normals[FG.VertexIndices[(Index*3+2)]]);
- end;
- end;
- FaceList.free;
- NormalList.free;
- FaceNormalList.free;
- ReferenceMap.free;
- for I:=0 to IndexMap.Count-1 do
- IndexMap.Objects[I].free;
- IndexMap.free;
- end;
- end.
|