Skip to content

Commit 781832a

Browse files
authored
Merge pull request #82 from Unity-Technologies/simplygon9
Simplygon9
2 parents cbb89da + 3a55b1f commit 781832a

File tree

9 files changed

+339
-168
lines changed

9 files changed

+339
-168
lines changed

Editor/AutoLOD.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ static IEnumerator GetDefaultSimplifier()
199199
"You are missing a default mesh simplifier. Would you like to install one?",
200200
"Yes", "No"))
201201
{
202-
var request = Client.Add("https://github.com/Unity-Technologies/UnityMeshSimplifier.git");
202+
var request = Client.Add("https://github.com/Whinarn/UnityMeshSimplifier.git");
203203
while (!request.IsCompleted)
204204
yield return null;
205205

@@ -735,7 +735,7 @@ static void PreferencesGUI()
735735
if (meshSimplifierType != null && typeof(IMeshSimplifier).IsAssignableFrom(meshSimplifierType))
736736
{
737737
if (s_SimplifierPreferences == null || s_SimplifierPreferences.GetType() != meshSimplifierType)
738-
s_SimplifierPreferences = (IPreferences)Activator.CreateInstance(meshSimplifierType);
738+
s_SimplifierPreferences = Activator.CreateInstance(meshSimplifierType) as IPreferences;
739739

740740
if (s_SimplifierPreferences != null)
741741
{

Editor/MeshSimplifiers/Simplygon.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
#if ENABLE_SIMPLYGON
2+
using Simplygon;
3+
using Simplygon.Unity.EditorPlugin;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using UnityEditor;
8+
using UnityEngine;
9+
using Unity.Formats.USD;
10+
using USD.NET;
11+
using USD.NET.Unity;
12+
13+
namespace Unity.AutoLOD
14+
{
15+
public struct SimplygonMeshSimplifier : IMeshSimplifier
16+
{
17+
static pxr.TfToken s_MaterialBindToken = new pxr.TfToken("materialBind");
18+
static pxr.TfToken s_SubMeshesToken = new pxr.TfToken("subMeshes");
19+
20+
static object s_ExecutionLock = new object();
21+
static ISimplygon s_Simplygon;
22+
static int s_ReferenceCount;
23+
24+
public void Simplify(WorkingMesh inputMesh, WorkingMesh outputMesh, float quality)
25+
{
26+
// We can only have one instance of Simplygon, but we can use it from multiple threads once initialized
27+
lock (s_ExecutionLock)
28+
{
29+
if (s_Simplygon == null)
30+
{
31+
s_Simplygon = Loader.InitSimplygon(out var simplygonErrorCode, out var simplygonErrorMessage);
32+
if (s_Simplygon == null && simplygonErrorCode != EErrorCodes.NoError)
33+
Debug.Log($"Initializing failed! {simplygonErrorCode}: {simplygonErrorMessage}");
34+
}
35+
36+
s_ReferenceCount++;
37+
}
38+
39+
var simplygon = s_Simplygon;
40+
if (simplygon != null)
41+
{
42+
string exportTempDirectory = SimplygonUtils.GetNewTempDirectory();
43+
44+
using (spScene sgScene = ExportSimplygonScene(simplygon, exportTempDirectory, inputMesh))
45+
{
46+
using (spReductionPipeline reductionPipeline = simplygon.CreateReductionPipeline())
47+
using (spReductionSettings reductionSettings = reductionPipeline.GetReductionSettings())
48+
{
49+
reductionSettings.SetReductionTargets(EStopCondition.All, true, false, false, false);
50+
reductionSettings.SetReductionTargetTriangleRatio(quality);
51+
52+
reductionPipeline.RunScene(sgScene, EPipelineRunMode.RunInThisProcess);
53+
54+
MonoBehaviourHelper.ExecuteOnMainThread(() =>
55+
{
56+
string baseFolder = "Assets/SimplygonTemp";
57+
if (!AssetDatabase.IsValidFolder(baseFolder))
58+
AssetDatabase.CreateFolder("Assets", "SimplygonTemp");
59+
60+
string meshName = inputMesh.name;
61+
string assetFolderGuid = AssetDatabase.CreateFolder(baseFolder, meshName);
62+
string assetFolderPath = AssetDatabase.GUIDToAssetPath(assetFolderGuid);
63+
64+
int startingLodIndex = 0;
65+
List<GameObject> importedGameObjects = new List<GameObject>();
66+
SimplygonImporter.Import(simplygon, reductionPipeline, ref startingLodIndex,
67+
assetFolderPath, meshName, importedGameObjects);
68+
69+
Debug.Assert(importedGameObjects.Count == 1, "AutoLOD: There should only be one imported mesh.");
70+
if (importedGameObjects.Count == 1)
71+
{
72+
GameObject go = importedGameObjects[0];
73+
MeshFilter mf = go.GetComponentInChildren<MeshFilter>();
74+
mf.sharedMesh.ApplyToWorkingMesh(ref outputMesh);
75+
}
76+
77+
foreach (var go in importedGameObjects)
78+
{
79+
GameObject.DestroyImmediate(go);
80+
}
81+
82+
AssetDatabase.DeleteAsset(baseFolder);
83+
});
84+
}
85+
}
86+
}
87+
88+
lock (s_ExecutionLock)
89+
{
90+
s_ReferenceCount--;
91+
92+
// Clean up on our way out if we are the last thread using the Simplygon singleton
93+
if (s_Simplygon != null && s_ReferenceCount == 0)
94+
{
95+
s_Simplygon.Dispose();
96+
s_Simplygon = null;
97+
}
98+
}
99+
}
100+
101+
static spScene ExportSimplygonScene(ISimplygon simplygon, string tempDirectory, WorkingMesh mesh)
102+
{
103+
if (string.IsNullOrEmpty(tempDirectory))
104+
return (spScene)null;
105+
106+
string filePath = Path.Combine(tempDirectory, "export.usd");
107+
InitUsd.Initialize();
108+
Scene scene = Scene.Create(filePath);
109+
110+
var context = new ExportContext();
111+
context.scene = scene;
112+
context.basisTransform = BasisTransformation.SlowAndSafe;
113+
// context.exportRoot = root.transform.parent;
114+
115+
ExportMesh(context, mesh);
116+
117+
scene.Save();
118+
scene.Close();
119+
120+
using (spSceneImporter sceneImporter = simplygon.CreateSceneImporter())
121+
{
122+
sceneImporter.SetImportFilePath(filePath);
123+
sceneImporter.RunImport();
124+
// SimplygonExporter.ExportSelectionSetsInSelection(simplygon, sceneImporter.GetScene(), selectedGameObjects, rootName);
125+
return sceneImporter.GetScene();
126+
}
127+
}
128+
129+
static void ExportMesh(ExportContext exportContext, WorkingMesh mesh)
130+
{
131+
// path = /build_bighouse_02/build_bighouse_01_dragonhead_01_LOD0
132+
var path = new pxr.SdfPath($"/{mesh.name}");
133+
134+
var scene = exportContext.scene;
135+
bool slowAndSafeConversion = exportContext.basisTransform == BasisTransformation.SlowAndSafe;
136+
var sample = new MeshSample();
137+
138+
if (mesh.bounds.center == Vector3.zero && mesh.bounds.extents == Vector3.zero)
139+
{
140+
mesh.RecalculateBounds();
141+
}
142+
143+
sample.extent = mesh.bounds;
144+
145+
if (slowAndSafeConversion)
146+
{
147+
// Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change of
148+
// basis is required. There are shortcuts, but this is fully general.
149+
sample.ConvertTransform();
150+
sample.extent.center = UnityTypeConverter.ChangeBasis(sample.extent.center);
151+
}
152+
153+
// TODO: Technically a mesh could be the root transform, which is not handled correctly here.
154+
// It should have the same logic for root prims as in ExportXform.
155+
// sample.transform = XformExporter.GetLocalTransformMatrix(
156+
// null,
157+
// scene.UpAxis == Scene.UpAxes.Z,
158+
// new pxr.SdfPath(path).IsRootPrimPath(),
159+
// exportContext.basisTransform);
160+
161+
sample.normals = mesh.normals;
162+
sample.points = mesh.vertices;
163+
sample.tangents = mesh.tangents;
164+
165+
sample.colors = mesh.colors;
166+
if (sample.colors != null && sample.colors.Length == 0)
167+
{
168+
sample.colors = null;
169+
}
170+
171+
// Gah. There is no way to inspect a meshes UVs.
172+
sample.st = mesh.uv;
173+
174+
// Set face vertex counts and indices.
175+
var tris = mesh.triangles;
176+
177+
if (slowAndSafeConversion)
178+
{
179+
// Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change
180+
// of basis is required. There are shortcuts, but this is fully general.
181+
182+
for (int i = 0; i < sample.points.Length; i++)
183+
{
184+
sample.points[i] = UnityTypeConverter.ChangeBasis(sample.points[i]);
185+
if (sample.normals != null && sample.normals.Length == sample.points.Length)
186+
{
187+
sample.normals[i] = UnityTypeConverter.ChangeBasis(sample.normals[i]);
188+
}
189+
190+
if (sample.tangents != null && sample.tangents.Length == sample.points.Length)
191+
{
192+
var w = sample.tangents[i].w;
193+
var t = UnityTypeConverter.ChangeBasis(sample.tangents[i]);
194+
sample.tangents[i] = new Vector4(t.x, t.y, t.z, w);
195+
}
196+
}
197+
198+
for (int i = 0; i < tris.Length; i += 3)
199+
{
200+
var t = tris[i];
201+
tris[i] = tris[i + 1];
202+
tris[i + 1] = t;
203+
}
204+
205+
sample.SetTriangles(tris);
206+
207+
scene.Write(path, sample);
208+
209+
// TODO: this is a bit of a half-measure, we need real support for primvar interpolation.
210+
// Set interpolation based on color count.
211+
if (sample.colors != null && sample.colors.Length == 1)
212+
{
213+
pxr.UsdPrim usdPrim = scene.GetPrimAtPath(path);
214+
var colorPrimvar =
215+
new pxr.UsdGeomPrimvar(usdPrim.GetAttribute(pxr.UsdGeomTokens.primvarsDisplayColor));
216+
colorPrimvar.SetInterpolation(pxr.UsdGeomTokens.constant);
217+
var opacityPrimvar =
218+
new pxr.UsdGeomPrimvar(usdPrim.GetAttribute(pxr.UsdGeomTokens.primvarsDisplayOpacity));
219+
opacityPrimvar.SetInterpolation(pxr.UsdGeomTokens.constant);
220+
}
221+
222+
// In USD subMeshes are represented as UsdGeomSubsets.
223+
// When there are multiple subMeshes, convert them into UsdGeomSubsets.
224+
if (mesh.subMeshCount > 1)
225+
{
226+
// Build a table of face indices, used to convert the subMesh triangles to face indices.
227+
var faceTable = new Dictionary<Vector3, int>();
228+
for (int i = 0; i < tris.Length; i += 3)
229+
{
230+
if (!slowAndSafeConversion)
231+
{
232+
faceTable.Add(new Vector3(tris[i], tris[i + 1], tris[i + 2]), i / 3);
233+
}
234+
else
235+
{
236+
// Under slow and safe export, index 0 and 1 are swapped.
237+
// This swap will not be present in the subMesh indices, so must be undone here.
238+
faceTable.Add(new Vector3(tris[i + 1], tris[i], tris[i + 2]), i / 3);
239+
}
240+
}
241+
242+
var usdPrim = scene.GetPrimAtPath(path);
243+
var usdGeomMesh = new pxr.UsdGeomMesh(usdPrim);
244+
245+
// Process each subMesh and create a UsdGeomSubset of faces this subMesh targets.
246+
for (int si = 0; si < mesh.subMeshCount; si++)
247+
{
248+
int[] indices = mesh.GetTriangles(si);
249+
int[] faceIndices = new int[indices.Length / 3];
250+
251+
for (int i = 0; i < indices.Length; i += 3)
252+
{
253+
faceIndices[i / 3] = faceTable[new Vector3(indices[i], indices[i + 1], indices[i + 2])];
254+
}
255+
256+
var vtIndices = UnityTypeConverter.ToVtArray(faceIndices);
257+
var subset = pxr.UsdGeomSubset.CreateUniqueGeomSubset(
258+
usdGeomMesh, // The object of which this subset belongs.
259+
s_SubMeshesToken, // An arbitrary name for the subset.
260+
pxr.UsdGeomTokens.face, // Indicator that these represent face indices
261+
vtIndices, // The actual face indices.
262+
s_MaterialBindToken // familyName = "materialBind"
263+
);
264+
}
265+
}
266+
}
267+
}
268+
}
269+
}
270+
#endif

Editor/MeshSimplifiers/SimplygonMeshSimplifier.cs.meta renamed to Editor/MeshSimplifiers/Simplygon/SimplygonMeshSimplifier.cs.meta

File renamed without changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "Unity.AutoLOD.Editor.Simplygon",
3+
"references": [
4+
"Unity.AutoLOD.Editor",
5+
"Unity.AutoLOD",
6+
"Unity.Formats.USD.Runtime"
7+
],
8+
"includePlatforms": [
9+
"Editor"
10+
],
11+
"excludePlatforms": [],
12+
"allowUnsafeCode": false,
13+
"overrideReferences": false,
14+
"precompiledReferences": [],
15+
"autoReferenced": true,
16+
"defineConstraints": [],
17+
"versionDefines": [],
18+
"noEngineReferences": false
19+
}

Editor/MeshSimplifiers/Simplygon/Unity.AutoLOD.Editor.Simplygon.asmdef.meta

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)