524 lines
16 KiB
C#
524 lines
16 KiB
C#
|
using BrainFailProductions.PolyFew;
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using UnityEngine;
|
|||
|
using UnityEngine.EventSystems;
|
|||
|
using UnityEngine.UI;
|
|||
|
|
|||
|
|
|||
|
namespace BrainFailProductions.PolyFewRuntime
|
|||
|
{
|
|||
|
|
|||
|
public class PolygonReduction : MonoBehaviour
|
|||
|
{
|
|||
|
|
|||
|
public Slider reductionStrength;
|
|||
|
public Slider preservationStrength;
|
|||
|
public Toggle preserveUVFoldover;
|
|||
|
public Toggle preserveUVSeams;
|
|||
|
public Toggle preserveBorders;
|
|||
|
public Toggle enableSmartLinking;
|
|||
|
public Toggle preserveFace;
|
|||
|
public Toggle recalculateNormals;
|
|||
|
public Toggle regardCurvature;
|
|||
|
public InputField trianglesCount;
|
|||
|
public Text message;
|
|||
|
public Text progress;
|
|||
|
public Button exportButton;
|
|||
|
public Button importFromFileSystem;
|
|||
|
public Button importFromWeb;
|
|||
|
public Slider progressSlider;
|
|||
|
public GameObject uninteractivePanel;
|
|||
|
|
|||
|
public GameObject targetObject;
|
|||
|
public Transform preservationSphere;
|
|||
|
public EventSystem eventSystem;
|
|||
|
|
|||
|
private PolyfewRuntime.ObjectMeshPairs objectMeshPairs;
|
|||
|
private bool didApplyLosslessLast = false;
|
|||
|
private bool disableTemporary = false;
|
|||
|
private GameObject barabarianRef;
|
|||
|
private PolyfewRuntime.ReferencedNumeric<float> downloadProgress = new PolyfewRuntime.ReferencedNumeric<float>(0);
|
|||
|
#pragma warning disable
|
|||
|
private bool isImportingFromNetwork;
|
|||
|
private bool isWebGL;
|
|||
|
|
|||
|
|
|||
|
// Start is called before the first frame update
|
|||
|
void Start()
|
|||
|
{
|
|||
|
if (Application.platform == RuntimePlatform.WebGLPlayer)
|
|||
|
{
|
|||
|
isWebGL = true;
|
|||
|
}
|
|||
|
|
|||
|
uninteractivePanel.SetActive(false);
|
|||
|
exportButton.interactable = false;
|
|||
|
barabarianRef = targetObject;
|
|||
|
objectMeshPairs = PolyfewRuntime.GetObjectMeshPairs(targetObject, true);
|
|||
|
trianglesCount.text = PolyfewRuntime.CountTriangles(true, targetObject) + "";
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// Update is called once per frame
|
|||
|
void Update()
|
|||
|
{
|
|||
|
if (!eventSystem) { return; }
|
|||
|
|
|||
|
if (eventSystem.currentSelectedGameObject && eventSystem.currentSelectedGameObject.GetComponent<RectTransform>())
|
|||
|
{
|
|||
|
FlyCamera.deactivated = true;
|
|||
|
}
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
FlyCamera.deactivated = false;
|
|||
|
}
|
|||
|
|
|||
|
if (isWebGL)
|
|||
|
{
|
|||
|
exportButton.gameObject.SetActive(false);
|
|||
|
importFromFileSystem.gameObject.SetActive(false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public void OnReductionChange(float value)
|
|||
|
{
|
|||
|
|
|||
|
if (disableTemporary) { return; }
|
|||
|
|
|||
|
didApplyLosslessLast = false;
|
|||
|
|
|||
|
|
|||
|
if (targetObject == null) { return; }
|
|||
|
if (Mathf.Approximately(0, value))
|
|||
|
{
|
|||
|
AssignMeshesFromPairs();
|
|||
|
trianglesCount.text = PolyfewRuntime.CountTriangles(true, targetObject) + "";
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
PolyfewRuntime.SimplificationOptions options = new PolyfewRuntime.SimplificationOptions();
|
|||
|
|
|||
|
options.simplificationStrength = value;
|
|||
|
options.enableSmartlinking = enableSmartLinking.isOn;
|
|||
|
options.preserveBorderEdges = preserveBorders.isOn;
|
|||
|
options.preserveUVSeamEdges = preserveUVSeams.isOn;
|
|||
|
options.preserveUVFoldoverEdges = preserveUVFoldover.isOn;
|
|||
|
options.recalculateNormals = recalculateNormals.isOn;
|
|||
|
options.regardCurvature = regardCurvature.isOn;
|
|||
|
|
|||
|
|
|||
|
if (preserveFace.isOn)
|
|||
|
{
|
|||
|
options.regardPreservationSpheres = true;
|
|||
|
options.preservationSpheres.Add(new PolyfewRuntime.PreservationSphere(preservationSphere.position, preservationSphere.lossyScale.x, preservationStrength.value));
|
|||
|
}
|
|||
|
|
|||
|
else { options.regardPreservationSpheres = false; }
|
|||
|
|
|||
|
trianglesCount.text = PolyfewRuntime.SimplifyObjectDeep(objectMeshPairs, options, (GameObject go, PolyfewRuntime.MeshRendererPair mInfo) =>
|
|||
|
{
|
|||
|
//Debug.Log("Simplified mesh " + mInfo.mesh.name + " on GameObject " + go.name);
|
|||
|
}) + "";
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public void SimplifyLossless()
|
|||
|
{
|
|||
|
//System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch();
|
|||
|
//w.Start();
|
|||
|
|
|||
|
disableTemporary = true;
|
|||
|
reductionStrength.value = 0;
|
|||
|
disableTemporary = false;
|
|||
|
|
|||
|
didApplyLosslessLast = true;
|
|||
|
|
|||
|
PolyfewRuntime.SimplificationOptions options = new PolyfewRuntime.SimplificationOptions
|
|||
|
{
|
|||
|
enableSmartlinking = enableSmartLinking.isOn,
|
|||
|
preserveBorderEdges = preserveBorders.isOn,
|
|||
|
preserveUVSeamEdges = preserveUVSeams.isOn,
|
|||
|
preserveUVFoldoverEdges = preserveUVFoldover.isOn,
|
|||
|
recalculateNormals = recalculateNormals.isOn,
|
|||
|
regardCurvature = regardCurvature.isOn,
|
|||
|
simplifyMeshLossless = true
|
|||
|
};
|
|||
|
|
|||
|
if (preserveFace.isOn)
|
|||
|
{
|
|||
|
options.regardPreservationSpheres = true;
|
|||
|
}
|
|||
|
|
|||
|
else { options.regardPreservationSpheres = false; }
|
|||
|
|
|||
|
|
|||
|
|
|||
|
trianglesCount.text = PolyfewRuntime.SimplifyObjectDeep(objectMeshPairs, options, (GameObject go, PolyfewRuntime.MeshRendererPair mInfo) =>
|
|||
|
{
|
|||
|
//Debug.Log("Simplified mesh " + mInfo.mesh.name + " on GameObject " + go.name);
|
|||
|
}) + "";
|
|||
|
|
|||
|
|
|||
|
//w.Stop();
|
|||
|
//Debug.Log("Elapsed " + w.ElapsedMilliseconds);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public void ImportOBJ()
|
|||
|
{
|
|||
|
// This function loads an abj file named Meat.obj from the project's asset directory
|
|||
|
// This also loads the associated textures and materials
|
|||
|
|
|||
|
GameObject importedObject;
|
|||
|
|
|||
|
PolyfewRuntime.OBJImportOptions importOptions = new PolyfewRuntime.OBJImportOptions();
|
|||
|
importOptions.zUp = false;
|
|||
|
importOptions.localPosition = new Vector3(-2.199f, -1, -1.7349f);
|
|||
|
importOptions.localScale = new Vector3(0.045f, 0.045f, 0.045f);
|
|||
|
|
|||
|
string objPath = Application.dataPath + "/PolyFew/demo/TestModels/Meat.obj";
|
|||
|
string texturesFolderPath = Application.dataPath + "/PolyFew/demo/TestModels/textures";
|
|||
|
string materialsFolderPath = Application.dataPath + "/PolyFew/demo/TestModels/materials";
|
|||
|
|
|||
|
|
|||
|
PolyfewRuntime.ImportOBJFromFileSystem(objPath, texturesFolderPath, materialsFolderPath, (GameObject imp) =>
|
|||
|
{
|
|||
|
|
|||
|
importedObject = imp;
|
|||
|
Debug.Log("Successfully imported GameObject: " + importedObject.name);
|
|||
|
barabarianRef.SetActive(false);
|
|||
|
targetObject = importedObject;
|
|||
|
ResetSettings();
|
|||
|
objectMeshPairs = PolyfewRuntime.GetObjectMeshPairs(targetObject, true);
|
|||
|
trianglesCount.text = PolyfewRuntime.CountTriangles(true, targetObject) + "";
|
|||
|
exportButton.interactable = true;
|
|||
|
importFromWeb.interactable = false;
|
|||
|
importFromFileSystem.interactable = false;
|
|||
|
preserveFace.interactable = false;
|
|||
|
preservationStrength.interactable = false;
|
|||
|
disableTemporary = true;
|
|||
|
preservationSphere.gameObject.SetActive(false);
|
|||
|
disableTemporary = false;
|
|||
|
|
|||
|
},
|
|||
|
(Exception ex) =>
|
|||
|
{
|
|||
|
Debug.LogError("Failed to load OBJ file. " + ex.ToString());
|
|||
|
|
|||
|
}, importOptions);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public void ImportOBJFromNetwork()
|
|||
|
{
|
|||
|
// This function downloads and imports an obj file named onion.obj from the url below
|
|||
|
// This also loads the associated textures and materials given by the absolute URLs
|
|||
|
|
|||
|
GameObject importedObject;
|
|||
|
isImportingFromNetwork = true;
|
|||
|
|
|||
|
|
|||
|
PolyfewRuntime.OBJImportOptions importOptions = new PolyfewRuntime.OBJImportOptions();
|
|||
|
importOptions.zUp = false;
|
|||
|
importOptions.localPosition = new Vector3(0.87815f, 1.4417f, -4.4708f);
|
|||
|
importOptions.localScale = new Vector3(0.0042f, 0.0042f, 0.0042f);
|
|||
|
|
|||
|
|
|||
|
string objURL = "https://dl.dropbox.com/s/v09bh0hiivja10e/onion.obj?dl=1";
|
|||
|
string objName = "onion";
|
|||
|
string diffuseTexURL = "https://dl.dropbox.com/s/0u4ij6sddi7a3gc/onion.jpg?dl=1";
|
|||
|
string bumpTexURL = "";
|
|||
|
string specularTexURL = "";
|
|||
|
string opacityTexURL = "";
|
|||
|
string materialURL = "https://dl.dropbox.com/s/fuzryqigs4gxwvv/onion.mtl?dl=1";
|
|||
|
|
|||
|
|
|||
|
progressSlider.value = 0;
|
|||
|
uninteractivePanel.SetActive(true);
|
|||
|
downloadProgress = new PolyfewRuntime.ReferencedNumeric<float>(0);
|
|||
|
|
|||
|
StartCoroutine(UpdateProgress());
|
|||
|
|
|||
|
PolyfewRuntime.ImportOBJFromNetwork(objURL, objName, diffuseTexURL, bumpTexURL, specularTexURL, opacityTexURL, materialURL, downloadProgress, (GameObject imp) =>
|
|||
|
{
|
|||
|
AssignMeshesFromPairs();
|
|||
|
isImportingFromNetwork = false;
|
|||
|
importedObject = imp;
|
|||
|
barabarianRef.SetActive(false);
|
|||
|
targetObject = importedObject;
|
|||
|
ResetSettings();
|
|||
|
objectMeshPairs = PolyfewRuntime.GetObjectMeshPairs(targetObject, true);
|
|||
|
trianglesCount.text = PolyfewRuntime.CountTriangles(true, targetObject) + "";
|
|||
|
exportButton.interactable = true;
|
|||
|
uninteractivePanel.SetActive(false);
|
|||
|
importFromWeb.interactable = false;
|
|||
|
importFromFileSystem.interactable = false;
|
|||
|
preserveFace.interactable = false;
|
|||
|
preservationStrength.interactable = false;
|
|||
|
disableTemporary = true;
|
|||
|
preservationSphere.gameObject.SetActive(false);
|
|||
|
disableTemporary = false;
|
|||
|
},
|
|||
|
(Exception ex) =>
|
|||
|
{
|
|||
|
uninteractivePanel.SetActive(false);
|
|||
|
isImportingFromNetwork = false;
|
|||
|
Debug.LogError("Failed to download and import OBJ file. " + ex.Message);
|
|||
|
|
|||
|
}, importOptions);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public void ExportGameObjectToOBJ()
|
|||
|
{
|
|||
|
|
|||
|
//The following exports the GameObject Onion to the persistent data path
|
|||
|
|
|||
|
string exportPath = Application.persistentDataPath;
|
|||
|
|
|||
|
GameObject exportObject = GameObject.Find("onion");
|
|||
|
|
|||
|
if (exportObject)
|
|||
|
{
|
|||
|
exportObject = exportObject.transform.GetChild(0).GetChild(0).gameObject;
|
|||
|
}
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
exportObject = GameObject.Find("Meat");
|
|||
|
if (!exportObject)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
exportObject = exportObject.transform.GetChild(0).GetChild(0).gameObject;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PolyfewRuntime.OBJExportOptions exportOptions = new PolyfewRuntime.OBJExportOptions(true, true, true, true, true);
|
|||
|
|
|||
|
PolyfewRuntime.ExportGameObjectToOBJ(exportObject, exportPath, () =>
|
|||
|
{
|
|||
|
|
|||
|
Debug.Log("Successfully exported GameObject: " + exportObject.name);
|
|||
|
string message = "Successfully exported the file to: \n" + Application.persistentDataPath;
|
|||
|
StartCoroutine(ShowMessage(message));
|
|||
|
|
|||
|
},
|
|||
|
(Exception ex) =>
|
|||
|
{
|
|||
|
Debug.LogError("Failed to export OBJ. " + ex.ToString());
|
|||
|
|
|||
|
}, exportOptions);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public void OnToggleStateChanged(bool isOn)
|
|||
|
{
|
|||
|
if (disableTemporary) { return; }
|
|||
|
|
|||
|
preservationSphere.gameObject.SetActive(preserveFace.isOn);
|
|||
|
|
|||
|
if (didApplyLosslessLast)
|
|||
|
{
|
|||
|
SimplifyLossless();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
preservationStrength.interactable = preserveFace.isOn;
|
|||
|
OnReductionChange(reductionStrength.value);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public void OnPreservationStrengthChange(float value)
|
|||
|
{
|
|||
|
OnToggleStateChanged(true);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#region HelperFunctions
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public void Reset()
|
|||
|
{
|
|||
|
ResetSettings();
|
|||
|
|
|||
|
AssignMeshesFromPairs();
|
|||
|
|
|||
|
GameObject obj = GameObject.Find("onion");
|
|||
|
|
|||
|
if (obj) { targetObject.SetActive(false); }
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
obj = GameObject.Find("Meat");
|
|||
|
if (obj) { targetObject.SetActive(false); }
|
|||
|
}
|
|||
|
|
|||
|
targetObject = barabarianRef;
|
|||
|
preserveFace.interactable = true;
|
|||
|
preservationStrength.interactable = preserveFace.isOn;
|
|||
|
|
|||
|
|
|||
|
targetObject.SetActive(true);
|
|||
|
objectMeshPairs = PolyfewRuntime.GetObjectMeshPairs(targetObject, true);
|
|||
|
trianglesCount.text = PolyfewRuntime.CountTriangles(true, targetObject) + "";
|
|||
|
|
|||
|
exportButton.interactable = false;
|
|||
|
importFromWeb.interactable = true;
|
|||
|
importFromFileSystem.interactable = true;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public static void OnSliderSelect()
|
|||
|
{
|
|||
|
FlyCamera.deactivated = true;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public static void OnSliderDeselect()
|
|||
|
{
|
|||
|
FlyCamera.deactivated = false;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
private bool IsMouseOverUI(RectTransform uiElement)
|
|||
|
{
|
|||
|
Vector2 localMousePosition = uiElement.InverseTransformPoint(Input.mousePosition);
|
|||
|
|
|||
|
if (uiElement.rect.Contains(localMousePosition))
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
private IEnumerator ShowMessage(string message)
|
|||
|
{
|
|||
|
Debug.Log(message);
|
|||
|
|
|||
|
this.message.text = message;
|
|||
|
|
|||
|
yield return new WaitForSeconds(4.5f);
|
|||
|
|
|||
|
this.message.text = "";
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
private void ResetSettings()
|
|||
|
{
|
|||
|
disableTemporary = true;
|
|||
|
reductionStrength.value = 0;
|
|||
|
preserveUVSeams.isOn = false;
|
|||
|
preserveUVFoldover.isOn = false;
|
|||
|
preserveBorders.isOn = false;
|
|||
|
enableSmartLinking.isOn = true;
|
|||
|
preserveFace.isOn = false;
|
|||
|
preservationSphere.gameObject.SetActive(false);
|
|||
|
disableTemporary = false;
|
|||
|
preservationStrength.value = 100;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
private IEnumerator UpdateProgress()
|
|||
|
{
|
|||
|
while (true)
|
|||
|
{
|
|||
|
yield return new WaitForSeconds(0.1f);
|
|||
|
progressSlider.value = downloadProgress.Value;
|
|||
|
progress.text = (int)downloadProgress.Value + "%";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
private void AssignMeshesFromPairs()
|
|||
|
{
|
|||
|
|
|||
|
if (objectMeshPairs != null)
|
|||
|
{
|
|||
|
foreach (GameObject gameObject in objectMeshPairs.Keys)
|
|||
|
{
|
|||
|
if (gameObject != null)
|
|||
|
{
|
|||
|
|
|||
|
PolyfewRuntime.MeshRendererPair meshRendererPair = objectMeshPairs[gameObject];
|
|||
|
|
|||
|
if (meshRendererPair.mesh == null) { continue; }
|
|||
|
|
|||
|
if (meshRendererPair.attachedToMeshFilter)
|
|||
|
{
|
|||
|
MeshFilter filter = gameObject.GetComponent<MeshFilter>();
|
|||
|
|
|||
|
if (filter == null) { continue; }
|
|||
|
|
|||
|
//Debug.Log("Is attached to meshfilter GAMOBJECT: " + gameObject.name + " CurrentMesh name: " + filter.sharedMesh.name + " set sharedMesh to " + meshRendererPair.mesh.name);
|
|||
|
|
|||
|
filter.sharedMesh = meshRendererPair.mesh;
|
|||
|
}
|
|||
|
|
|||
|
else if (!meshRendererPair.attachedToMeshFilter)
|
|||
|
{
|
|||
|
SkinnedMeshRenderer sRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
|
|||
|
|
|||
|
if (sRenderer == null) { continue; }
|
|||
|
|
|||
|
//Debug.Log("Is attached to SkinnedMeshRendere GAMOBJECT: " + gameObject.name + " CurrentMesh name: " + sRenderer.sharedMesh.name + " set sharedMesh to " + meshRendererPair.mesh.name);
|
|||
|
|
|||
|
sRenderer.sharedMesh = meshRendererPair.mesh;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endregion Helper Functions
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|