GXS.MeshOptimizer.pas 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.MeshOptimizer;
  5. (* Mesh optimization *)
  6. interface
  7. uses
  8. System.Classes,
  9. System.SysUtils,
  10. Stage.VectorGeometry,
  11. Stage.VectorTypes,
  12. GXS.VectorFileObjects,
  13. GXS.PersistentClasses,
  14. GXS.VectorLists,
  15. GXS.MeshUtils;
  16. type
  17. TMeshOptimizerOption = (mooStandardize, mooVertexCache, mooSortByMaterials, mooMergeObjects);
  18. TMeshOptimizerOptions = set of TMeshOptimizerOption;
  19. var
  20. vDefaultMeshOptimizerOptions : TMeshOptimizerOptions = [mooStandardize, mooVertexCache, mooSortByMaterials, mooMergeObjects];
  21. procedure OptimizeMesh(aList : TgxMeshObjectList; options : TMeshOptimizerOptions); overload;
  22. procedure OptimizeMesh(aList : TgxMeshObjectList); overload;
  23. procedure OptimizeMesh(aMeshObject : TgxMeshObject; options : TMeshOptimizerOptions); overload;
  24. procedure OptimizeMesh(aMeshObject : TgxMeshObject); overload;
  25. procedure FacesSmooth(aMeshObj: TgxMeshObject; aWeldDistance: Single=0.0000001; aThreshold: Single=35.0; InvertNormals:boolean=false);
  26. // ------------------------------------------------------------------
  27. implementation
  28. // ------------------------------------------------------------------
  29. procedure OptimizeMesh(aList : TgxMeshObjectList);
  30. begin
  31. OptimizeMesh(aList, vDefaultMeshOptimizerOptions);
  32. end;
  33. procedure OptimizeMesh(aList : TgxMeshObjectList; options : TMeshOptimizerOptions);
  34. var
  35. i, k : Integer;
  36. mob, mo : TgxMeshObject;
  37. fg : TgxFaceGroup;
  38. fgvi : TgxFGVertexIndexList;
  39. begin
  40. // optimize all mesh objects
  41. for i:=0 to aList.Count-1 do begin
  42. OptimizeMesh(aList[i], options);
  43. end;
  44. if (mooStandardize in options) then begin
  45. // drop mesh objects that have become empty
  46. for i:=aList.Count-1 downto 0 do begin
  47. if (aList[i].Mode=momFaceGroups) and (aList[i].FaceGroups.Count=0) then
  48. aList[i].Free;
  49. end;
  50. end;
  51. if (aList.Count>0) and (mooMergeObjects in options) then begin
  52. mob:=aList[0];
  53. Assert(mob.Mode=momFaceGroups);
  54. for i:=1 to aList.Count-1 do begin
  55. mo:=aList[i];
  56. Assert(mo.Mode=momFaceGroups);
  57. k:=mob.Vertices.Count;
  58. mob.Vertices.Add(mo.Vertices);
  59. mob.Normals.Add(mo.Normals);
  60. mob.TexCoords.Add(mo.TexCoords);
  61. while mo.FaceGroups.Count>0 do begin
  62. fg:=mo.FaceGroups[0];
  63. fgvi:=(fg as TgxFGVertexIndexList);
  64. fgvi.Owner:=mob.FaceGroups;
  65. mob.FaceGroups.Add(fgvi);
  66. mo.FaceGroups.Delete(0);
  67. fgvi.VertexIndices.Offset(k);
  68. end;
  69. end;
  70. for i:=aList.Count-1 downto 1 do
  71. aList[i].Free;
  72. end;
  73. end;
  74. procedure OptimizeMesh(aMeshObject : TgxMeshObject);
  75. begin
  76. OptimizeMesh(aMeshObject, vDefaultMeshOptimizerOptions);
  77. end;
  78. procedure OptimizeMesh(aMeshObject : TgxMeshObject; options : TMeshOptimizerOptions);
  79. var
  80. i : Integer;
  81. fg : TgxFaceGroup;
  82. coords, texCoords, normals : TgxAffineVectorList;
  83. il : TgxIntegerList;
  84. materialName : String;
  85. begin
  86. if (mooMergeObjects in options) then begin
  87. if aMeshObject.Mode=momFaceGroups then begin
  88. // remove empty facegroups
  89. for i:=aMeshObject.FaceGroups.Count-1 downto 0 do begin
  90. fg:=aMeshObject.FaceGroups[i];
  91. if fg.TriangleCount=0 then
  92. fg.Free;
  93. end;
  94. end;
  95. end;
  96. if (mooStandardize in options) then begin
  97. if (aMeshObject.Mode<>momFaceGroups) or (aMeshObject.FaceGroups.Count<=1) then begin
  98. if aMeshObject.FaceGroups.Count=1 then
  99. materialName:=aMeshObject.FaceGroups[0].MaterialName;
  100. texCoords:=TgxAffineVectorList.Create;
  101. normals:=TgxAffineVectorList.Create;
  102. coords:=aMeshObject.ExtractTriangles(texCoords, normals);
  103. try
  104. il:=BuildVectorCountOptimizedIndices(coords, normals, texCoords);
  105. try
  106. aMeshObject.Clear;
  107. if il.Count>0 then begin
  108. RemapReferences(normals, il);
  109. RemapReferences(texCoords, il);
  110. RemapAndCleanupReferences(coords, il);
  111. aMeshObject.Vertices:=coords;
  112. aMeshObject.Normals:=normals;
  113. aMeshObject.TexCoords:=texCoords;
  114. fg:=TgxFGVertexIndexList.CreateOwned(aMeshObject.FaceGroups);
  115. fg.MaterialName:=materialName;
  116. TgxFGVertexIndexList(fg).VertexIndices:=il;
  117. end;
  118. finally
  119. il.Free;
  120. end;
  121. finally
  122. coords.Free;
  123. normals.Free;
  124. texCoords.Free;
  125. end;
  126. end else
  127. Assert(False, 'Standardization with multiple facegroups not supported');
  128. end;
  129. if (mooVertexCache in options) and (aMeshObject.Mode=momFaceGroups) then begin
  130. for i:=0 to aMeshObject.FaceGroups.Count-1 do begin
  131. fg:=aMeshObject.FaceGroups[i];
  132. if fg.ClassType=TgxFGVertexIndexList then with TgxFGVertexIndexList(fg) do begin
  133. if Mode in [fgmmTriangles, fgmmFlatTriangles] then
  134. IncreaseCoherency(VertexIndices, 12);
  135. end;
  136. end;
  137. end;
  138. if mooSortByMaterials in options then
  139. aMeshObject.FaceGroups.SortByMaterial;
  140. end;
  141. procedure FacesSmooth(aMeshObj: TgxMeshObject; aWeldDistance: Single=0.0000001; aThreshold: Single=35.0; InvertNormals:boolean=false);
  142. Var
  143. I, J, K, L: integer;
  144. WeldedVertex: TgxAffineVectorList;
  145. TmpIntegerList: TgxIntegerList;
  146. IndexMap: TStringList;
  147. n: TAffineVector;
  148. indicesMap : TgxIntegerList;
  149. Index: Integer;
  150. FaceList: TgxIntegerList;
  151. NormalList: TgxAffineVectorList;
  152. FaceNormalList: TgxAffineVectorList;
  153. FaceGroup: TgxFaceGroup;
  154. FG, FG1: TgxFGVertexIndexList;
  155. Threshold: Single;
  156. Angle: Single;
  157. ReferenceMap: TgxIntegerList;
  158. ID1, ID2: Integer;
  159. Index1, Index2, Index3: Integer;
  160. function FindReferenceIndex(aID: Integer): Integer;
  161. begin
  162. Result := ReferenceMap[aID];
  163. end;
  164. function iMin(a, b: Integer): Integer;
  165. begin
  166. if a<b then
  167. Result := a
  168. else
  169. Result := b;
  170. end;
  171. function iMax(a, b: Integer): Integer;
  172. begin
  173. if a>b then
  174. Result := a
  175. else
  176. Result := b;
  177. end;
  178. begin
  179. Threshold := aThreshold * Pi/180.0;
  180. //build the vectices reference map
  181. ReferenceMap := TgxIntegerList.Create;
  182. WeldedVertex := TgxAffineVectorList.Create;
  183. WeldedVertex.Assign(aMeshObj.Vertices);
  184. indicesMap := TgxIntegerList.Create;
  185. //first of all, weld the very closed vertices
  186. WeldVertices(WeldedVertex, indicesMap, aWeldDistance);
  187. //then, rebuild the map list
  188. IndexMap := TStringList.Create;
  189. for I:=0 to WeldedVertex.Count-1 do
  190. begin
  191. ReferenceMap.Assign(indicesMap);
  192. TmpIntegerList := TgxIntegerList.Create;
  193. Index := ReferenceMap.IndexOf(I);
  194. while Index>=0 do
  195. begin
  196. TmpIntegerList.Add(Index);
  197. ReferenceMap[Index] := -99999;
  198. Index := ReferenceMap.IndexOf(I);
  199. end;
  200. IndexMap.AddObject(IntToStr(I), TmpIntegerList);
  201. end;
  202. ReferenceMap.Assign(indicesMap);
  203. //never used these, free them all
  204. WeldedVertex.free;
  205. indicesMap.free;
  206. //create a TexPoint list for save face infomation, where s=facegroup index, t=face index
  207. FaceList := TgxIntegerList.Create;
  208. NormalList := TgxAffineVectorList.Create;
  209. FaceNormalList := TgxAffineVectorList.Create;
  210. //NormalIndex := TgxIntegerList.Create;
  211. for I:=0 to aMeshObj.FaceGroups.Count-1 do
  212. begin
  213. FaceGroup := aMeshObj.FaceGroups[I];
  214. TmpIntegerList := TgxFGVertexIndexList(FaceGroup).VertexIndices;
  215. for J:=0 to (TmpIntegerList.Count div 3)-1 do
  216. begin
  217. FaceList.Add(I);
  218. FaceList.Add(J);
  219. CalcPlaneNormal(aMeshObj.Vertices[TmpIntegerList[J * 3 + 0]],
  220. aMeshObj.Vertices[TmpIntegerList[J * 3 + 1]],
  221. aMeshObj.Vertices[TmpIntegerList[J * 3 + 2]],
  222. n);
  223. //add three normals for one trangle
  224. FaceNormalList.Add(n);
  225. NormalList.Add(n);
  226. NormalList.Add(n);
  227. NormalList.Add(n);
  228. end;
  229. end;
  230. //do smooth
  231. for I:=0 to (FaceList.Count div 2)-1 do
  232. begin
  233. Index := FaceList[I*2+0];
  234. Index1 := FaceList[I*2+1];
  235. FG := TgxFGVertexIndexList(aMeshObj.FaceGroups[Index]);
  236. for J:=0 to 2 do
  237. begin
  238. for K:=0 to (FaceList.Count div 2)-1 do
  239. begin
  240. Index2 := FaceList[K*2+0];
  241. Index3 := FaceList[K*2+1];
  242. FG1 := TgxFGVertexIndexList(aMeshObj.FaceGroups[Index2]);
  243. if I<>K then
  244. begin
  245. for L:=0 to 2 do
  246. begin
  247. //two face contain the same vertex
  248. ID1 := FindReferenceIndex(FG.VertexIndices[Index1*3+J]);
  249. ID2 := FindReferenceIndex(FG1.VertexIndices[Index3*3+L]);
  250. if ID1=ID2 then
  251. begin
  252. Angle := VectorDotProduct(FaceNormalList[I],FaceNormalList[K]);
  253. if angle>Threshold then
  254. NormalList[I*3+J] := VectorAdd(NormalList[I*3+J],FaceNormalList[K]);
  255. end;
  256. end;
  257. end;
  258. end;
  259. n := NormalList[I*3+J];
  260. NormalizeVector(n);
  261. NormalList[I*3+J] := n;
  262. end;
  263. end;
  264. for I:=0 to (FaceList.Count div 2)-1 do
  265. begin
  266. Index := FaceList[I*2+0];
  267. FG := TgxFGVertexIndexList(aMeshObj.FaceGroups[Index]);
  268. Index := FaceList[I*2+1];
  269. aMeshObj.Normals[FG.VertexIndices[(Index*3+0)]] := NormalList[(I*3+0)];
  270. aMeshObj.Normals[FG.VertexIndices[(Index*3+1)]] := NormalList[(I*3+1)];
  271. aMeshObj.Normals[FG.VertexIndices[(Index*3+2)]] := NormalList[(I*3+2)];
  272. if InvertNormals then
  273. begin
  274. aMeshObj.Normals[FG.VertexIndices[(Index*3+0)]] := VectorNegate(aMeshObj.Normals[FG.VertexIndices[(Index*3+0)]]);
  275. aMeshObj.Normals[FG.VertexIndices[(Index*3+1)]] := VectorNegate(aMeshObj.Normals[FG.VertexIndices[(Index*3+1)]]);
  276. aMeshObj.Normals[FG.VertexIndices[(Index*3+2)]] := VectorNegate(aMeshObj.Normals[FG.VertexIndices[(Index*3+2)]]);
  277. end;
  278. end;
  279. FaceList.free;
  280. NormalList.free;
  281. FaceNormalList.free;
  282. ReferenceMap.free;
  283. for I:=0 to IndexMap.Count-1 do
  284. IndexMap.Objects[I].free;
  285. IndexMap.free;
  286. end;
  287. end.