// Show center move handle private void OnSceneGUI() { // Get shatter RayfireShatter shatter = target as RayfireShatter; if (shatter == null) { return; } Transform transform = shatter.transform; Vector3 centerWorldPos = transform.TransformPoint(shatter.centerPosition); Quaternion centerWorldQuat = transform.rotation * shatter.centerDirection; // Point3 handle if (shatter.showCenter == true) { EditorGUI.BeginChangeCheck(); centerWorldPos = Handles.PositionHandle(centerWorldPos, centerWorldQuat.RFNormalize()); if (EditorGUI.EndChangeCheck() == true) { Undo.RecordObject(shatter, "Center Move"); } EditorGUI.BeginChangeCheck(); centerWorldQuat = Handles.RotationHandle(centerWorldQuat, centerWorldPos); if (EditorGUI.EndChangeCheck() == true) { Undo.RecordObject(shatter, "Center Rotate"); } } shatter.centerDirection = Quaternion.Inverse(transform.rotation) * centerWorldQuat; shatter.centerPosition = transform.InverseTransformPoint(centerWorldPos); }
// Scale fragments void ScalePreview(RayfireShatter shatter) { if (shatter.fragmentsLast.Count > 0 && shatter.previewScale > 0f) { // Do not scale if (shatter.skinnedMeshRend != null) { shatter.skinnedMeshRend.enabled = false; } if (shatter.meshRenderer != null) { shatter.meshRenderer.enabled = false; } foreach (GameObject fragment in shatter.fragmentsLast) { if (fragment != null) { fragment.transform.localScale = Vector3.one * Mathf.Lerp(1f, 0.3f, shatter.previewScale); } } shatter.resetState = true; } if (shatter.previewScale == 0f) { shatter.ResetScale(0f); } }
/// ///////////////////////////////////////////////////////// /// Other /// ///////////////////////////////////////////////////////// // Vertex limitation void VertexLimitation() { // Vertex limitation if (advanced.vertexLimitation == true) { for (int i = fragmentsLast.Count - 1; i >= 0; i--) { MeshFilter mf = fragmentsLast[i].GetComponent <MeshFilter>(); if (mf.sharedMesh.vertexCount > advanced.vertexAmount) { RayfireShatter shat = fragmentsLast[i].AddComponent <RayfireShatter>(); shat.voronoi.amount = 4; shat.Fragment(); Debug.Log(shat.name); if (shat.fragmentsLast.Count > 0) { fragmentsLast.AddRange(shat.fragmentsLast); DestroyImmediate(shat.gameObject); fragmentsLast.RemoveAt(i); } } } } }
static void DrawGizmosSelected(RayfireShatter shatter, GizmoType gizmoType) { // Color preview if (shatter.colorPreview == true) { ColorPreview(shatter); } // Custom point cloud preview if (shatter.type == FragType.Custom) { if (shatter.custom.enable == true) { Gizmos.color = Color.green; // Get bounds for preview Bounds bound = shatter.GetBound(); if (bound.size.magnitude > 0) { List <Vector3> pointCloud = RFFragment.GetCustomPointCLoud(shatter.custom, shatter.transform, shatter.advanced.seed, bound); if (pointCloud.Count > 0) { for (int i = 0; i < pointCloud.Count; i++) { Gizmos.DrawSphere(pointCloud[i], shatter.custom.size); } } } } } }
// Get demolition mesh static Mesh GetDemolitionMesh(RayfireShatter scr) { if (scr.skinnedMeshRend != null) { return(RFMesh.BakeMesh(scr.skinnedMeshRend)); } return(scr.meshFilter.sharedMesh); }
// Copy from void CopyFrom(RayfireShatter shatter) { type = shatter.type; voronoi = new RFVoronoi(shatter.voronoi); splinters = new RFSplinters(shatter.splinters); slabs = new RFSplinters(shatter.slabs); radial = new RFRadial(shatter.radial); custom = new RFCustom(shatter.custom); slice = new RFSlice(shatter.slice); tets = new RFTets(shatter.tets); mode = shatter.mode; material.CopyFrom(shatter.material); clusters = new RFShatterCluster(shatter.clusters); advanced = new RFShatterAdvanced(shatter.advanced); }
// Color preview static void ColorPreview(RayfireShatter shatter) { if (shatter.fragmentsLast.Count > 0) { Random.InitState(1); foreach (Transform root in shatter.rootChildList) { if (root != null) { MeshFilter[] meshFilters = root.GetComponentsInChildren <MeshFilter>(); foreach (var mf in meshFilters) { Gizmos.color = new Color(Random.Range(0.2f, 0.8f), Random.Range(0.2f, 0.8f), Random.Range(0.2f, 0.8f)); Gizmos.DrawMesh(mf.sharedMesh, mf.transform.position, mf.transform.rotation, mf.transform.lossyScale * 1.01f); } } } } }
// Get shatter mode static int GetShatterMode(RayfireShatter scrShatter = null) { // Simple voronoi if (scrShatter == null) { return(1); } // Always 2 if (scrShatter.type == FragType.Slices) { return(2); } if (scrShatter.type == FragType.Decompose) { return(1); } // Turn off fast mode for tests and radial int shatterMode = scrShatter.shatterMode; if (scrShatter.type == FragType.Custom) { shatterMode = 0; } if (scrShatter.type == FragType.Tets) { shatterMode = 0; } // Classic way for clustering. Not for slices if (scrShatter.clusters.enable == true) { shatterMode = 0; } return(shatterMode); }
// Get inner faces sub mesh id public static int SetInnerSubId(RayfireShatter scr) { // No inner material if (scr.material.innerMaterial == null) { return(0); } // Get materials Material[] mats = scr.skinnedMeshRend != null ? scr.skinnedMeshRend.sharedMaterials : scr.meshRenderer.sharedMaterials; // Get outer id if outer already has it for (int i = 0; i < mats.Length; i++) { if (mats[i] == scr.material.innerMaterial) { return(i); } } return(-1); }
// Save mesh as asset public static void SaveFragments(RayfireShatter shatter, string path) { // Get asset name string saveName = shatter.gameObject.name + shatter.export.suffix; // Save path string savePath = EditorUtility.SaveFilePanel("Save Fragments To Asset", shatterPath, saveName, "asset"); //string saveFolder = EditorUtility.SaveFolderPanel ("Save Fragments To Asset", "Assets/", saveName); // Debug.Log (saveFolder); // Convert path savePath = FileUtil.GetProjectRelativePath(savePath); // No path if (string.IsNullOrEmpty(savePath) == true) { return; } // Save path for next save shatterPath = Path.GetDirectoryName(savePath); // Collect all meshes to save bool hasMesh = false; List <Mesh> meshes = new List <Mesh>(); List <MeshFilter> meshFilters = new List <MeshFilter>(); List <GameObject> gameObjects = new List <GameObject>(); // Collect fragments meshes if (shatter.export.source == RFMeshExport.MeshExportType.LastFragments) { // No fragments if (shatter.fragmentsLast.Count == 0) { return; } gameObjects = shatter.fragmentsLast; } else if (shatter.export.source == RFMeshExport.MeshExportType.Children) { // No children if (shatter.transform.childCount == 0) { return; } gameObjects.AddRange(shatter.gameObject.GetComponentsInChildren <MeshFilter>().Select(mf => mf.gameObject)); } // Collect meshes foreach (var frag in gameObjects) { // Get mf MeshFilter mf = frag.GetComponent <MeshFilter>(); meshFilters.Add(mf); // No mf if (mf == null) { meshes.Add(null); } // No mesh if (mf != null && mf.sharedMesh == null) { meshes.Add(null); } // New mesh Mesh tempMesh = Object.Instantiate(mf.sharedMesh); tempMesh.name = mf.sharedMesh.name; // Collect meshes.Add(tempMesh); // List has mesh hasMesh = true; } // List has no meshes to save if (hasMesh == false) { return; } // Empty mesh Mesh emptyMesh = new Mesh(); emptyMesh.name = saveName; // Create asset AssetDatabase.CreateAsset(emptyMesh, savePath); // Save each fragment mesh for (int i = 0; i < meshFilters.Count; i++) { // Skip if no mesh if (meshFilters[i] == null) { continue; } // Apply to meshfilter to avoid save of already referenced mesh meshFilters[i].sharedMesh = meshes[i]; // Add all meshes AssetDatabase.AddObjectToAsset(meshFilters[i].sharedMesh, savePath); } // Save AssetDatabase.SaveAssets(); }
// Inspector public override void OnInspectorGUI() { // Get shatter RayfireShatter shatter = target as RayfireShatter; if (shatter == null) { return; } // Get inspector width // float width = EditorGUIUtility.currentViewWidth - 20f; // Space GUILayout.Space(8); // Fragment if (GUILayout.Button("Fragment", GUILayout.Height(25))) { foreach (var targ in targets) { if (targ as RayfireShatter != null) { (targ as RayfireShatter).Fragment(); // TODO APPLY LOCAL SHATTER PREVIEW PROPS TO ALL SELECTED } } // Scale preview if preview turn on if (shatter.previewScale > 0 && shatter.scalePreview == true) { ScalePreview(shatter); } } // Space GUILayout.Space(1); // Fragmentation section Begin GUILayout.BeginHorizontal(); // Delete last if (shatter.fragmentsLast.Count > 0) // TODO SUPPORT MASS CHECK { if (GUILayout.Button(" Fragment to Last ", GUILayout.Height(22))) { foreach (var targ in targets) { if (targ as RayfireShatter != null) { (targ as RayfireShatter).DeleteFragmentsLast(1); (targ as RayfireShatter).resetState = true; (targ as RayfireShatter).Fragment(1); // Scale preview if preview turn on if ((targ as RayfireShatter).previewScale > 0 && (targ as RayfireShatter).scalePreview == true) { ScalePreview(targ as RayfireShatter); } } } } if (GUILayout.Button(" Delete Last ", GUILayout.Height(22))) { foreach (var targ in targets) { if (targ as RayfireShatter != null) { (targ as RayfireShatter).DeleteFragmentsLast(); (targ as RayfireShatter).resetState = true; (targ as RayfireShatter).ResetScale(0f); } } } } // Delete all fragments if (shatter.fragmentsAll.Count > 0 && shatter.fragmentsAll.Count > shatter.fragmentsLast.Count) { if (GUILayout.Button(" Delete All ", GUILayout.Height(22))) { foreach (var targ in targets) { if (targ as RayfireShatter != null) { (targ as RayfireShatter).DeleteFragmentsAll(); (targ as RayfireShatter).resetState = true; (targ as RayfireShatter).ResetScale(0f); } } } } // Fragmentation section End EditorGUILayout.EndHorizontal(); // Space GUILayout.Space(1); // Preview if (shatter.fragmentsLast.Count > 0) { // Label GUILayout.Label(" Preview", EditorStyles.boldLabel); // Preview toggles begin GUILayout.BeginHorizontal(); // Start check for scale toggle change EditorGUI.BeginChangeCheck(); shatter.scalePreview = GUILayout.Toggle(shatter.scalePreview, "Scale", "Button"); if (EditorGUI.EndChangeCheck() == true) { if (shatter.scalePreview == true) { ScalePreview(shatter); } else { shatter.resetState = true; shatter.ResetScale(0f); } } // Color preview toggle shatter.colorPreview = GUILayout.Toggle(shatter.colorPreview, "Color", "Button"); // Preview toggles end EditorGUILayout.EndHorizontal(); // Space GUILayout.Space(3); // Preview section Begin GUILayout.BeginHorizontal(); // Label GUILayout.Label("Scale Preview", GUILayout.Width(90)); // Start check for slider change EditorGUI.BeginChangeCheck(); shatter.previewScale = GUILayout.HorizontalSlider(shatter.previewScale, 0f, 0.99f); if (EditorGUI.EndChangeCheck() == true) { if (shatter.scalePreview == true) { ScalePreview(shatter); } } // Preview section End EditorGUILayout.EndHorizontal(); } // Reset scale if fragments were deleted shatter.ResetScale(shatter.previewScale); // Space GUILayout.Space(5); // Draw script UI DrawDefaultInspector(); // Space GUILayout.Space(3); // Export Last fragments if (shatter.export.source == RFMeshExport.MeshExportType.LastFragments && shatter.fragmentsLast.Count > 0) { if (GUILayout.Button("Export Last Fragments", GUILayout.Height(25))) { RFMeshAsset.SaveFragments(shatter); } } // Export children if (shatter.export.source == RFMeshExport.MeshExportType.Children && shatter.transform.childCount > 0) { if (GUILayout.Button("Export Children", GUILayout.Height(25))) { RFMeshAsset.SaveFragments(shatter); } } // Export FBX // if (GUILayout.Button ("Get FBX Exporter", GUILayout.Height (20))) // { // Debug.Log ("NOTE: The latest Unity FBX Exporter is available in Preview via the Package Manager starting from Unity 2018.3+."); // Application.OpenURL ("https://assetstore.unity.com/packages/essentials/fbx-exporter-101408"); // } GUILayout.Space(5); // Info if (shatter.fragmentsLast.Count > 0 || shatter.fragmentsAll.Count > 0) { // Label GUILayout.Label(" Info", EditorStyles.boldLabel); // Info section Begin GUILayout.BeginHorizontal(); // Label GUILayout.Label("Roots: " + shatter.rootChildList.Count); // Label GUILayout.Label("Last Fragments: " + shatter.fragmentsLast.Count); // Label GUILayout.Label("Total Fragments: " + shatter.fragmentsAll.Count); // Info section End EditorGUILayout.EndHorizontal(); } // Center if ((int)shatter.type <= 5) { // Label GUILayout.Label(" Center", EditorStyles.boldLabel); // Preview section Begin GUILayout.BeginHorizontal(); // Show center toggle shatter.showCenter = GUILayout.Toggle(shatter.showCenter, " Show ", "Button"); // Reset center if (GUILayout.Button("Reset ")) { foreach (var targ in targets) { if (targ as RayfireShatter != null) { (targ as RayfireShatter).ResetCenter(); } } SceneView.RepaintAll(); } // Preview section End EditorGUILayout.EndHorizontal(); } }
/// ///////////////////////////////////////////////////////// /// Awake ops /// ///////////////////////////////////////////////////////// // Init mesh root. Copy Rigid component for children with mesh bool SetRootMesh() { if (objectType == ObjectType.MeshRoot) { // Stop if already initiated if (limitations.demolished == true || physics.exclude == true) { return(true); } // Get children List <Transform> children = new List <Transform>(); for (int i = 0; i < transform.childCount; i++) { children.Add(transform.GetChild(i)); } // Add Rigid to child with mesh fragments = new List <RayfireRigid>(); for (int i = 0; i < children.Count; i++) { if (children[i].GetComponent <MeshFilter>() != null) { // Get rigid // TODO check if fragment already has Rigid, Reinit in this case. RayfireRigid childRigid = children[i].gameObject.GetComponent <RayfireRigid>(); if (childRigid == null) { childRigid = children[i].gameObject.AddComponent <RayfireRigid>(); } fragments.Add(childRigid); // Copy parent properties CopyPropertiesTo(childRigid); // Init childRigid.Initialize(); } } // Copy components RayfireShatter.CopyRootMeshShatter(this, fragments); RFParticles.CopyRootMeshParticles(this, fragments); RFSound.CopyRootMeshSound(this, fragments); // TODO Setup as clusters root children with transform only // Check for Unyielding component RayfireUnyielding[] unyArray = transform.GetComponents <RayfireUnyielding>(); for (int i = 0; i < unyArray.Length; i++) { unyArray[i].SetUnyByOverlap(this); } // Turn off demolition and physics demolitionType = DemolitionType.None; physics.exclude = true; return(true); } return(false); }
/// ///////////////////////////////////////////////////////// /// Methods /// ///////////////////////////////////////////////////////// // Fragment this object by shatter properties public void Fragment(int fragmentMode = 0) { // Cache variables if (DefineComponents() == false) { return; } // Cache default vars SetVariables(); // Check if object is too small ScaleCheck(); // Cache RFFragment.CacheMeshes(ref meshes, ref pivots, ref origSubMeshIdsRF, this); // Stop if (meshes == null) { return; } // Create fragments if (fragmentMode == 1) { if (rootChildList[rootChildList.Count - 1] != null) { fragmentsLast = CreateFragments(rootChildList[rootChildList.Count - 1].gameObject); } else { fragmentMode = 0; } } if (fragmentMode == 0) { fragmentsLast = CreateFragments(); } // Vertex limitation if (advanced.vertexLimitation == true) { for (int i = fragmentsLast.Count - 1; i >= 0; i--) { MeshFilter mf = fragmentsLast[i].GetComponent <MeshFilter>(); if (mf.sharedMesh.vertexCount > advanced.vertexAmount) { RayfireShatter shat = fragmentsLast[i].AddComponent <RayfireShatter>(); shat.voronoi.amount = 4; shat.Fragment(); Debug.Log(shat.name); if (shat.fragmentsLast.Count > 0) { fragmentsLast.AddRange(shat.fragmentsLast); DestroyImmediate(shat.gameObject); fragmentsLast.RemoveAt(i); } } } } // Collect to all fragments fragmentsAll.AddRange(fragmentsLast); // Reset original object back if it was scaled transForm.localScale = originalScale; }
// Set fragmentation properties static void SetFragmentProperties(RFShatter shatter, RayfireShatter scrSh, RayfireRigid scrRigid) { // Rigid demolition without shatter. Set and exit. if (scrRigid != null && scrSh == null) { // Get final amount int percVar = Random.Range(0, scrRigid.meshDemolition.amount * scrRigid.meshDemolition.variation / 100); scrRigid.meshDemolition.totalAmount = scrRigid.meshDemolition.amount + percVar; // Set Voronoi Uniform properties SetVoronoi(shatter, scrRigid.meshDemolition.totalAmount, scrRigid.transform, scrRigid.limitations.contactPoint, scrRigid.meshDemolition.contactBias); return; } // Rigid demolition with shatter. if (scrRigid != null && scrSh != null) { // Set Contact point to shatter component scrSh.centerPosition = scrRigid.transForm.InverseTransformPoint(scrRigid.limitations.contactPoint); // Set total amount by rigid component if (scrSh.type == FragType.Voronoi) { scrRigid.meshDemolition.totalAmount = scrSh.voronoi.Amount; } else if (scrSh.type == FragType.Splinters) { scrRigid.meshDemolition.totalAmount = scrSh.splinters.Amount; } else if (scrSh.type == FragType.Slabs) { scrRigid.meshDemolition.totalAmount = scrSh.slabs.Amount; } else if (scrSh.type == FragType.Radial) { scrRigid.meshDemolition.totalAmount = scrSh.radial.rings * scrSh.radial.rays; } } // Shatter fragmentation if (scrSh != null) { // Center position and direction Vector3 centerPos = scrSh.transform.TransformPoint(scrSh.centerPosition); // Set properties if (scrSh.type == FragType.Voronoi) { SetVoronoi(shatter, scrSh.voronoi.Amount, scrSh.transform, centerPos, scrSh.voronoi.centerBias); } else if (scrSh.type == FragType.Splinters) { SetSplinters(shatter, scrSh.splinters, scrSh.transform, centerPos, scrSh.splinters.centerBias); } else if (scrSh.type == FragType.Slabs) { SetSlabs(shatter, scrSh.slabs, scrSh.transform, centerPos, scrSh.splinters.centerBias); } else if (scrSh.type == FragType.Radial) { SetRadial(shatter, scrSh.radial, scrSh.transform, centerPos, scrSh.centerDirection); } else if (scrSh.type == FragType.Custom) { SetCustom(shatter, scrSh.custom, scrSh.transform, scrSh.meshFilter, scrSh.bound, scrSh.splinters, scrSh.slabs, scrSh.advanced.seed); } else if (scrSh.type == FragType.Slices) { SetSlices(shatter, scrSh.transform, scrSh.slice); } else if (scrSh.type == FragType.Tets) { SetTet(shatter, scrSh.bound, scrSh.tets); } else if (scrSh.type == FragType.Decompose) { SetDecompose(shatter); } // Clustering if (scrSh.clusters.enable == true) { SetClusters(shatter, scrSh.clusters); } } }
/// ///////////////////////////////////////////////////////// /// Shatter /// ///////////////////////////////////////////////////////// // Cache for shatter public static void CacheMeshes(ref Mesh[] meshes, ref Vector3[] pivots, ref List <RFDictionary> origSubMeshIdsRf, RayfireShatter scrShatter) { // TODO check vars by type: slice list, etc // Turn off fast mode for tets and slices int shatterMode = GetShatterMode(scrShatter); // Get mesh Mesh mesh = GetDemolitionMesh(scrShatter);; // Decompose in Editor only, slice runtime only FragmentMode mode = scrShatter.mode; if (scrShatter.type == FragType.Decompose) // TODO FIX { mode = FragmentMode.Editor; } if (scrShatter.type == FragType.Slices) { mode = FragmentMode.Runtime; } // Set up shatter RFShatter shatter = SetShatter( shatterMode, mesh, scrShatter.transform, scrShatter.material, scrShatter.advanced.decompose, scrShatter.advanced.removeCollinear, scrShatter.advanced.seed, mode, scrShatter.advanced.inputPrecap, scrShatter.advanced.outputPrecap, scrShatter.advanced.removeDoubleFaces, scrShatter.advanced.excludeInnerFragments, scrShatter.advanced.elementSizeThreshold); // Failed input if (shatter == null) { meshes = null; pivots = null; return; } // Get innerSubId int innerSubId = RFSurface.SetInnerSubId(scrShatter); // Set fragmentation properties SetFragmentProperties(shatter, scrShatter, null); // Custom points check if (scrShatter.type == FragType.Custom && scrShatter.custom.noPoints == true) { meshes = null; pivots = null; Debug.Log("No custom ponts"); return; } // Calculate fragments List <Dictionary <int, int> > origSubMeshIds = new List <Dictionary <int, int> >(); bool successState = Compute( shatterMode, shatter, scrShatter.transform, ref meshes, ref pivots, mesh, innerSubId, ref origSubMeshIds, scrShatter); // Create RF dictionary origSubMeshIdsRf = new List <RFDictionary>(); for (int i = 0; i < origSubMeshIds.Count; i++) { origSubMeshIdsRf.Add(new RFDictionary(origSubMeshIds[i])); } // Failed fragmentation. Increase bad mesh if (successState == false) { Debug.Log("Bad shatter output mesh: " + scrShatter.name); } else { for (int i = 0; i < meshes.Length; i++) { meshes[i].name = scrShatter.name + "_" + i; } } }
public static void CacheMeshes(ref Mesh[] meshes, ref Vector3[] pivots, ref List <RFDictionary> origSubMeshIdsRf, RayfireShatter scrShatter) { BuildTest(); }