public void Simplify(WMesh inputMesh, WMesh outputMesh, float quality) { var enableLogging = false; int totalTriangleCount; var sourceMesh = ToMeshDecimatorMesh(inputMesh, out totalTriangleCount); int targetTriangleCount = Mathf.CeilToInt(totalTriangleCount * quality); var algorithm = MeshDecimation.CreateAlgorithm(Algorithm.Default); DecimationAlgorithm.StatusReportCallback statusCallback = (iteration, tris, currentTris, targetTris) => { Debug.LogFormat("Iteration {0}: tris {1} current {2} target {3}", iteration, tris, currentTris, targetTris); }; if (enableLogging) { algorithm.StatusReport += statusCallback; } var destMesh = MeshDecimation.DecimateMesh(algorithm, sourceMesh, targetTriangleCount); if (enableLogging) { algorithm.StatusReport -= statusCallback; } FromMeshDecimatorMesh(destMesh, false, ref outputMesh); }
public static WorkingMesh ToWorkingMesh(this Mesh mesh) { var wm = new WorkingMesh(); mesh.ApplyToWorkingMesh(wm); return(wm); }
IEnumerator Simplify(WorkingMesh inputMesh, WorkingMesh outputMesh, float quality) { Renderer renderer = null; MonoBehaviourHelper.ExecuteOnMainThread(() => { var go = EditorUtility.CreateGameObjectWithHideFlags("Temp", HideFlags.HideAndDontSave, typeof(MeshRenderer), typeof(MeshFilter)); var mf = go.GetComponent <MeshFilter>(); var mesh = new Mesh(); inputMesh.ApplyToMesh(mesh); mf.sharedMesh = mesh; renderer = go.GetComponent <MeshRenderer>(); var material = new Material(Shader.Find("Standard")); var sharedMaterials = new Material[mesh.subMeshCount]; for (int i = 0; i < mesh.subMeshCount; i++) { sharedMaterials[i] = material; } renderer.sharedMaterials = sharedMaterials; renderer.enabled = false; }); var settings = new InstaLODOptimizeSettings(quality); settings.PercentTriangles = quality; var nativeMeshSettings = new InstaLODNativeMeshOperationSettings(true); nativeMeshSettings.hideSourceGameObjects = false; while (InstaLODNative.currentMeshOperationState != null) { yield return(null); } MonoBehaviourHelper.ExecuteOnMainThread(() => { EditorWindow.GetWindow <InstaLODToolkitWindow>(); // Necessary for background processing InstaLODNative.Optimize(new List <Renderer>() { renderer }, settings, nativeMeshSettings); Selection.activeGameObject = null; // Necessary to avoid errors from InstaLOD trying to add settings component to imported model }); while (InstaLODNative.currentMeshOperationState != null) { yield return(null); } MonoBehaviourHelper.ExecuteOnMainThread(() => { var mf = renderer.GetComponent <MeshFilter>(); mf.sharedMesh.ApplyToWorkingMesh(outputMesh); UnityObject.DestroyImmediate(mf.gameObject); }); }
public static WorkingMesh ToWorkingMesh(this Mesh mesh, Allocator allocator) { var bindposes = mesh.bindposes; var wm = new WorkingMesh(allocator, mesh.vertexCount, mesh.GetTriangleCount(), mesh.subMeshCount, bindposes.Length); mesh.ApplyToWorkingMesh(ref wm, bindposes); return(wm); }
static void GenerateMeshLOD(MeshLOD meshLOD, HashSet <int> preprocessMeshes) { // A NOP to make sure we have an instance before launching into threads that may need to execute on the main thread MonoBehaviourHelper.ExecuteOnMainThread(() => { }); WorkingMesh inputMesh = null; var inputMeshID = meshLOD.inputMesh.GetInstanceID(); if (!preprocessMeshes.Contains(inputMeshID)) { inputMesh = meshLOD.inputMesh.ToWorkingMesh(); } var meshSimplifier = (IMeshSimplifier)Activator.CreateInstance(meshLOD.meshSimplifierType); var worker = new BackgroundWorker(); worker.DoWork += (sender, args) => { // If this mesh is dependent on another mesh, then let it complete first if (inputMesh == null) { while (preprocessMeshes.Contains(inputMeshID)) { Thread.Sleep(100); } MonoBehaviourHelper.ExecuteOnMainThread(() => inputMesh = meshLOD.inputMesh.ToWorkingMesh()); } var outputMesh = new WorkingMesh(); #if UNITY_2017_3_OR_NEWER outputMesh.indexFormat = inputMesh.indexFormat; #endif meshSimplifier.Simplify(inputMesh, outputMesh, meshLOD.quality, () => { var outMesh = meshLOD.outputMesh; Debug.Log("Completed LOD " + outMesh.name); outputMesh.name = outMesh.name; outputMesh.ApplyToMesh(outMesh); outMesh.RecalculateBounds(); var outputMeshID = outMesh.GetInstanceID(); if (preprocessMeshes.Remove(outputMeshID)) { Debug.Log("Pre-process mesh complete: " + outputMeshID); } });; }; worker.RunWorkerAsync(); }
// Taking bindposes optional parameter is ugly, but saves an additional array allocation if it was already // accessed to get the length public static void ApplyToWorkingMesh(this Mesh mesh, ref WorkingMesh wm, Matrix4x4[] bindposes = null) { wm.indexFormat = mesh.indexFormat; wm.vertices = mesh.vertices; wm.normals = mesh.normals; wm.tangents = mesh.tangents; wm.uv = mesh.uv; wm.uv2 = mesh.uv2; wm.uv3 = mesh.uv3; wm.uv4 = mesh.uv4; wm.colors = mesh.colors; wm.boneWeights = mesh.boneWeights; wm.bindposes = bindposes ?? mesh.bindposes; wm.subMeshCount = mesh.subMeshCount; for (int i = 0; i < mesh.subMeshCount; i++) { wm.SetTriangles(mesh.GetTriangles(i), i); } wm.name = mesh.name; wm.bounds = mesh.bounds; }
IEnumerator Simplify(Mesh inputMesh, Mesh outputMesh, float quality) { var meshSimplifier = new MeshSimplifier(); meshSimplifier.Vertices = inputMesh.vertices; meshSimplifier.Normals = inputMesh.normals; meshSimplifier.Tangents = inputMesh.tangents; meshSimplifier.UV1 = inputMesh.uv; meshSimplifier.UV2 = inputMesh.uv2; meshSimplifier.UV3 = inputMesh.uv3; meshSimplifier.UV4 = inputMesh.uv4; meshSimplifier.Colors = inputMesh.colors; var triangles = new int[inputMesh.subMeshCount][]; for (var submesh = 0; submesh < inputMesh.subMeshCount; submesh++) { triangles[submesh] = inputMesh.GetTriangles(submesh); } meshSimplifier.AddSubMeshTriangles(triangles); meshSimplifier.SimplifyMesh(quality); outputMesh.vertices = meshSimplifier.Vertices; outputMesh.normals = meshSimplifier.Normals; outputMesh.tangents = meshSimplifier.Tangents; outputMesh.uv = meshSimplifier.UV1; outputMesh.uv2 = meshSimplifier.UV2; outputMesh.uv3 = meshSimplifier.UV3; outputMesh.uv4 = meshSimplifier.UV4; outputMesh.colors = meshSimplifier.Colors; outputMesh.subMeshCount = meshSimplifier.SubMeshCount; for (var submesh = 0; submesh < outputMesh.subMeshCount; submesh++) { outputMesh.SetTriangles(meshSimplifier.GetSubMeshTriangles(submesh), submesh); } yield break; }
public static void ApplyToWorkingMesh(this Mesh mesh, WorkingMesh wm) { #if UNITY_2017_3_OR_NEWER wm.indexFormat = mesh.indexFormat; #endif wm.vertices = mesh.vertices; wm.normals = mesh.normals; wm.tangents = mesh.tangents; wm.uv = mesh.uv; wm.uv2 = mesh.uv2; wm.uv3 = mesh.uv3; wm.uv4 = mesh.uv4; wm.colors = mesh.colors; wm.boneWeights = mesh.boneWeights; wm.bindposes = mesh.bindposes; wm.subMeshCount = mesh.subMeshCount; for (int i = 0; i < mesh.subMeshCount; i++) { wm.SetTriangles(mesh.GetTriangles(i), i); } wm.name = mesh.name; wm.bounds = mesh.bounds; }
public JobHandle Generate() { // A NOP to make sure we have an instance before launching into threads that may need to execute on the main thread MonoBehaviourHelper.ExecuteOnMainThread(() => { }); var job = default(T); var inputMesh = InputMesh; job.InputMesh = inputMesh.ToWorkingMesh(Allocator.Persistent); job.Quality = Quality; // Allocate a persistent working mesh for output, so that we can apply it after the job completes (i.e. memory // allocated in a job is freed when the job completes) var workingMesh = new WorkingMesh(Allocator.Persistent, inputMesh.vertexCount, inputMesh.GetTriangleCount(), inputMesh.subMeshCount, inputMesh.blendShapeCount); workingMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; job.OutputMesh = workingMesh; var jobHandle = job.Schedule(); MonoBehaviourHelper.StartCoroutine(UpdateMesh(jobHandle, job)); return(jobHandle); }
public void Simplify(WorkingMesh inputMesh, WorkingMesh outputMesh, float quality) { var isMainThread = MonoBehaviourHelper.IsMainThread(); Renderer renderer = null; UnityCloudJob job = null; string jobName = null; var assembly = typeof(SharedData).Assembly; var cloudJobType = assembly.GetType("Simplygon.Cloud.Yoda.IntegrationClient.CloudJob"); var jobNameField = cloudJobType.GetField("name", BindingFlags.NonPublic | BindingFlags.Instance); lock (executionLock) { const int kSimultaneousJobs = 4; //var processSubscriptionRestrictionsType = assembly.GetType("Simplygon.Cloud.Yoda.Client.ProcessSubscriptionRestrictions23"); //var simultaneousJobsProperty = processSubscriptionRestrictionsType.GetProperty("SimultaneousJobs"); //var accountType = assembly.GetType("Simplygon.Cloud.Yoda.Client.User23"); //var processSubscriptionsRestrictionsProperty = accountType.GetProperty("ProcessSubscriptionRestrictions"); //var processSubscriptionRestrictions = processSubscriptionsRestrictionsProperty.GetValue(SharedData.Instance.Account, null); //var simultaneousJobs = (int)simultaneousJobsProperty.GetValue(processSubscriptionRestrictions, null); while (SharedData.Instance.GeneralManager.JobManager.ProcessingJobCount >= kSimultaneousJobs) { if (!isMainThread) { Thread.Sleep(1000); } } MonoBehaviourHelper.ExecuteOnMainThread(() => { var go = EditorUtility.CreateGameObjectWithHideFlags("Temp", HideFlags.HideAndDontSave, typeof(MeshRenderer), typeof(MeshFilter)); var mf = go.GetComponent <MeshFilter>(); var mesh = new Mesh(); inputMesh.ApplyToMesh(mesh); mf.sharedMesh = mesh; renderer = go.GetComponent <MeshRenderer>(); var material = new Material(Shader.Find("Standard")); var sharedMaterials = new Material[mesh.subMeshCount]; for (int i = 0; i < mesh.subMeshCount; i++) { sharedMaterials[i] = material; } renderer.sharedMaterials = sharedMaterials; renderer.enabled = false; EditorWindow.GetWindow <Window>(); // Must be visible for background processing SharedData.Instance.Settings.SetDownloadAssetsAutomatically(true); var lodChainProperty = typeof(SharedData).GetProperty("LODChain"); var lodChainList = lodChainProperty.GetValue(SharedData.Instance, null) as IList; var lodChain = lodChainList[0]; var processNodeType = assembly.GetType("Simplygon.SPL.v80.Node.ProcessNode"); var processorProperty = processNodeType.GetProperty("Processor"); var processor = processorProperty.GetValue(lodChain, null); var reductionProcessorType = assembly.GetType("Simplygon.SPL.v80.Processor.ReductionProcessor"); var reductionSettingsProperty = reductionProcessorType.GetProperty("ReductionSettings"); var reductionSettingsType = assembly.GetType("Simplygon.SPL.v80.Settings.ReductionSettings"); var reductionSettings = reductionSettingsProperty.GetValue(processor, null); var triangleRatioProperty = reductionSettingsType.GetProperty("TriangleRatio"); triangleRatioProperty.SetValue(reductionSettings, quality, null); jobName = Path.GetRandomFileName().Replace(".", string.Empty); var prefabList = PrefabUtilityEx.GetPrefabsForSelection(new List <GameObject>() { go }); var generalManager = SharedData.Instance.GeneralManager; generalManager.CreateJob(jobName, "myPriority", prefabList, () => { foreach (var j in generalManager.JobManager.Jobs) { var name = (string)jobNameField.GetValue(j.CloudJob); if (name == jobName) { job = j; } } }); }); while (job == null) { if (!isMainThread) { Thread.Sleep(100); } } } while (string.IsNullOrEmpty(job.AssetDirectory)) { if (!isMainThread) { Thread.Sleep(100); } } MonoBehaviourHelper.ExecuteOnMainThread(() => { var customDataType = assembly.GetType("Simplygon.Cloud.Yoda.IntegrationClient.CloudJob+CustomData"); var pendingFolderNameProperty = customDataType.GetProperty("UnityPendingLODFolderName"); var jobCustomDataProperty = cloudJobType.GetProperty("JobCustomData"); var jobCustomData = jobCustomDataProperty.GetValue(job.CloudJob, null); var jobFolderName = pendingFolderNameProperty.GetValue(jobCustomData, null) as string; var lodAssetDir = "Assets/LODs/" + job.AssetDirectory; var mesh = AssetDatabase.LoadAssetAtPath <Mesh>(string.Format("{0}/{1}_LOD1.prefab", lodAssetDir, jobName)); mesh.ApplyToWorkingMesh(ref outputMesh); //job.CloudJob.StateHandler.RequestJobDeletion(); AssetDatabaseEx.DeletePendingLODFolder(jobFolderName); AssetDatabase.DeleteAsset(lodAssetDir); UnityObject.DestroyImmediate(renderer.gameObject); }); }
public void Simplify(WorkingMesh inputMesh, WorkingMesh outputMesh, float quality, Action completeAction) { SimplifierRunner.instance.EnqueueSimplification(Simplify(inputMesh, outputMesh, quality), completeAction); }
static void FromMeshDecimatorMesh(DMesh mesh, bool recalculateNormals, ref WMesh destMesh) { if (recalculateNormals) { // If we recalculate the normals, we also recalculate the tangents mesh.RecalculateNormals(); mesh.RecalculateTangents(); } int subMeshCount = mesh.SubMeshCount; var newNormals = FromVector3(mesh.Normals); var newTangents = FromVector4(mesh.Tangents); var newUV1 = FromVector2(mesh.UV1); var newUV2 = FromVector2(mesh.UV2); var newUV3 = FromVector2(mesh.UV3); var newUV4 = FromVector2(mesh.UV4); var newColors = FromColor(mesh.Colors); //var newBoneWeights = FromSimplifyBoneWeights(mesh.BoneWeights); //if (bindposes != null) newMesh.bindposes = bindposes; destMesh.subMeshCount = subMeshCount; destMesh.vertices = FromVector3d(mesh.Vertices); if (newNormals != null) { destMesh.normals = newNormals; } if (newTangents != null) { destMesh.tangents = newTangents; } if (newUV1 != null) { destMesh.uv = newUV1; } if (newUV2 != null) { destMesh.uv2 = newUV2; } if (newUV3 != null) { destMesh.uv3 = newUV3; } if (newUV4 != null) { destMesh.uv4 = newUV4; } if (newColors != null) { destMesh.colors = newColors; } //if (newBoneWeights != null) // newMesh.boneWeights = newBoneWeights; for (int i = 0; i < subMeshCount; i++) { var subMeshIndices = mesh.GetIndices(i); destMesh.SetTriangles(subMeshIndices, i); } destMesh.RecalculateBounds(); }
DMesh ToMeshDecimatorMesh(WMesh mesh, out int totalTriangleCount) { var vertices = ToVector3d(mesh.vertices); int subMeshCount = mesh.subMeshCount; var meshNormals = mesh.normals; var meshTangents = mesh.tangents; var meshUV1 = mesh.uv; var meshUV2 = mesh.uv2; var meshUV3 = mesh.uv3; var meshUV4 = mesh.uv4; var meshColors = mesh.colors; //var meshBoneWeights = mesh.boneWeights; //var meshBindposes = mesh.bindposes; totalTriangleCount = 0; var meshIndices = new int[subMeshCount][]; for (int i = 0; i < subMeshCount; i++) { meshIndices[i] = mesh.GetTriangles(i); totalTriangleCount += meshIndices[i].Length / 3; } var dmesh = new DMesh(vertices, meshIndices); if (meshNormals != null && meshNormals.Length > 0) { dmesh.Normals = ToVector3(meshNormals); } if (meshTangents != null && meshTangents.Length > 0) { dmesh.Tangents = ToVector4(meshTangents); } if (meshUV1 != null && meshUV1.Length > 0) { dmesh.UV1 = ToVector2(meshUV1); } if (meshUV2 != null && meshUV2.Length > 0) { dmesh.UV2 = ToVector2(meshUV2); } if (meshUV3 != null && meshUV3.Length > 0) { dmesh.UV3 = ToVector2(meshUV3); } if (meshUV4 != null && meshUV4.Length > 0) { dmesh.UV4 = ToVector2(meshUV4); } if (meshColors != null && meshColors.Length > 0) { dmesh.Colors = ToVector4(meshColors); } //if (meshBoneWeights != null && meshBoneWeights.Length > 0) // dmesh.BoneWeights = ToSimplifyBoneWeights(meshBoneWeights); return(dmesh); }
IEnumerator Simplify(WorkingMesh inputMesh, WorkingMesh outputMesh, float quality) { Renderer renderer = null; UnityCloudJob job = null; string jobName = null; var assembly = typeof(SharedData).Assembly; var cloudJobType = assembly.GetType("Simplygon.Cloud.Yoda.IntegrationClient.CloudJob"); var jobNameField = cloudJobType.GetField("name", BindingFlags.NonPublic | BindingFlags.Instance); MonoBehaviourHelper.ExecuteOnMainThread(() => { var go = EditorUtility.CreateGameObjectWithHideFlags("Temp", HideFlags.HideAndDontSave, typeof(MeshRenderer), typeof(MeshFilter)); var mf = go.GetComponent <MeshFilter>(); var mesh = new Mesh(); inputMesh.ApplyToMesh(mesh); mf.sharedMesh = mesh; renderer = go.GetComponent <MeshRenderer>(); var sharedMaterials = new Material[mesh.subMeshCount]; if (Directory.Exists(materialPath) == false) { Directory.CreateDirectory(materialPath); } //For submesh, we should create material asset. //otherwise, simplygon will be combine uv of submesh. for (int i = 0; i < mesh.subMeshCount; i++) { var material = new Material(Shader.Find("Standard")); material.name = "Material " + i.ToString(); AssetDatabase.CreateAsset(material, materialPath + "/" + material.name); sharedMaterials[i] = material; } renderer.sharedMaterials = sharedMaterials; renderer.enabled = false; EditorWindow.GetWindow <Window>(); // Must be visible for background processing SharedData.Instance.Settings.SetDownloadAssetsAutomatically(true); var lodChainProperty = typeof(SharedData).GetProperty("LODChain"); var lodChainList = lodChainProperty.GetValue(SharedData.Instance, null) as IList; var lodChain = lodChainList[0]; var processNodeType = assembly.GetType("Simplygon.SPL.v80.Node.ProcessNode"); var processorProperty = processNodeType.GetProperty("Processor"); var processor = processorProperty.GetValue(lodChain, null); var reductionProcessorType = assembly.GetType("Simplygon.SPL.v80.Processor.ReductionProcessor"); var reductionSettingsProperty = reductionProcessorType.GetProperty("ReductionSettings"); var reductionSettingsType = assembly.GetType("Simplygon.SPL.v80.Settings.ReductionSettings"); var reductionSettings = reductionSettingsProperty.GetValue(processor, null); var triangleRatioProperty = reductionSettingsType.GetProperty("TriangleRatio"); triangleRatioProperty.SetValue(reductionSettings, quality, null); jobName = Path.GetRandomFileName().Replace(".", string.Empty); var prefabList = PrefabUtilityEx.GetPrefabsForSelection(new List <GameObject>() { go }); var generalManager = SharedData.Instance.GeneralManager; generalManager.CreateJob(jobName, "myPriority", prefabList, () => { foreach (var j in generalManager.JobManager.Jobs) { var name = (string)jobNameField.GetValue(j.CloudJob); if (name == jobName) { job = j; } } }); }); while (job == null) { yield return(null); } while (string.IsNullOrEmpty(job.AssetDirectory)) { yield return(null); } MonoBehaviourHelper.ExecuteOnMainThread(() => { var customDataType = assembly.GetType("Simplygon.Cloud.Yoda.IntegrationClient.CloudJob+CustomData"); var pendingFolderNameProperty = customDataType.GetProperty("UnityPendingLODFolderName"); var jobCustomDataProperty = cloudJobType.GetProperty("JobCustomData"); var jobCustomData = jobCustomDataProperty.GetValue(job.CloudJob, null); var jobFolderName = pendingFolderNameProperty.GetValue(jobCustomData, null) as string; var lodAssetDir = lodPath + job.AssetDirectory; var mesh = AssetDatabase.LoadAssetAtPath <Mesh>(string.Format("{0}/{1}_LOD1.prefab", lodAssetDir, jobName)); mesh.ApplyToWorkingMesh(outputMesh); //job.CloudJob.StateHandler.RequestJobDeletion(); AssetDatabaseEx.DeletePendingLODFolder(jobFolderName); AssetDatabase.DeleteAsset(lodAssetDir); AssetDatabase.DeleteAsset(materialPath); UnityObject.DestroyImmediate(renderer.gameObject); }); }