using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Rendering; using Object = UnityEngine.Object; using Random = UnityEngine.Random; #if (UNITY_EDITOR || UNITY_STANDALONE || UNITY_IOS || UNITY_ANDROID || UNITY_XBOXONE) using RayFire.DotNet; namespace RayFire { // Static class to handle all shatter methods public static class RFFragment { static bool silentMode = true; static List meshListStatic = new List(); static List pivotListStatic = new List(); static List> subIdsListStatic = new List>(); /// ///////////////////////////////////////////////////////// /// Shatter /// ///////////////////////////////////////////////////////// // Cache for shatter public static void CacheMeshes(ref Mesh[] meshes, ref Vector3[] pivots, ref RFDictionary[] rfOrigSubMeshIds, RayfireShatter scrShatter) { // TODO check vars by type: slice list, etc // Min face filter for internal slice ops int removeMinFaceFilter = 4; // Turn off fast mode for tets and slices: // 0:classic: Custom, tets, clustering: old original algorithm. // 1:voro simple: Decompose type or Rigid without Shatter. // 2:slice // 3:bricks int shatterMode = GetShatterMode (scrShatter); // Get mesh Mesh mesh = GetDemolitionMesh (scrShatter); ; // Decompose in Editor only, slice runtime only // Runtime = 0, // Editor = 1 FragmentMode fragmentMode = scrShatter.mode; if (scrShatter.type == FragType.Decompose) // TODO FIX fragmentMode = FragmentMode.Editor; if (scrShatter.type == FragType.Slices) { fragmentMode = FragmentMode.Runtime; removeMinFaceFilter = 1; } // Set up shatter RFShatter shatter = SetShatter ( fragmentMode, shatterMode, mesh, scrShatter.transform, scrShatter.material, scrShatter.advanced.decompose, scrShatter.advanced.removeCollinear, scrShatter.advanced.seed, scrShatter.advanced.inputPrecap, scrShatter.advanced.outputPrecap, scrShatter.advanced.removeDoubleFaces, scrShatter.advanced.inner, scrShatter.advanced.smooth, scrShatter.advanced.elementSizeThreshold, removeMinFaceFilter, scrShatter.advanced.postWeld); // Failed input if (shatter == null) { meshes = null; pivots = null; return; } // Get innerSubId int innerSubId = RFSurface.SetInnerSubId (scrShatter); // Set fragmentation properties SetShatterFragmentProperties (shatter, scrShatter); // Custom/HexGrid points check if (NoCustomPoints (scrShatter) == true) { Debug.Log ("Point Cloud is empty"); meshes = null; pivots = null; return; } // Calculate fragments List> origSubMeshIds = new List>(); bool successState = Compute ( shatterMode, shatter, scrShatter.transform, ref meshes, ref pivots, mesh, innerSubId, ref origSubMeshIds, scrShatter); // Create RF dictionary if (origSubMeshIds != null) rfOrigSubMeshIds = RFDictionary.GetRFDictionary (origSubMeshIds); // Failed fragmentation. Increase bad mesh if (successState == false) { Debug.Log ("Bad shatter output mesh: " + scrShatter.name); return; } // Filter out planar meshes RFShatterAdvanced.RemovePlanar (ref meshes, ref pivots, ref rfOrigSubMeshIds, scrShatter); // Filter out meshes by size RFShatterAdvanced.RemoveBySize (ref meshes, ref pivots, ref rfOrigSubMeshIds, scrShatter); // Set name string nameApp = scrShatter.name + "_"; for (int i = 0; i < meshes.Length; i++) meshes[i].name = nameApp + i; /* // Bake collider meshes if (scrShatter.advanced.bake == true) { Debug.Log ("bake"); for (int i = 0; i < meshes.Length; i++) Physics.BakeMesh (meshes[i].GetInstanceID(), true); } */ } // Check for no custom or hex grid points static bool NoCustomPoints(RayfireShatter scrShatter) { if (scrShatter.type == FragType.Custom && scrShatter.custom.noPoints == true) return true; if (scrShatter.type == FragType.Hexagon && scrShatter.hexagon.noPc == true) return true; return false; } /// ///////////////////////////////////////////////////////// /// Rigid /// ///////////////////////////////////////////////////////// // Prepare rigid component to cache fragment meshes public static bool InputMesh(RayfireRigid scr) { // Set up shatter if (SetRigidShatter (scr) == false) return false; // Get innerSubId scr.meshDemolition.innerSubId = RFSurface.SetInnerSubId (scr); // Set fragmentation properties SetRigidFragmentProperties (scr.meshDemolition.rfShatter, scr.meshDemolition.sht, scr); return true; } // Set up rigid shatter static bool SetRigidShatter(RayfireRigid scr) { // Set up shatter if (scr.meshDemolition.rfShatter == null) { // Save rotation at caching to fix fragments rotation at demolition scr.cacheRotation = scr.transForm.rotation; // Turn off fast mode for tets and slices scr.meshDemolition.shatterMode = GetShatterMode (scr.meshDemolition.sht); // Get innerSubId scr.meshDemolition.mesh = GetDemolitionMesh (scr); // Get shatter scr.meshDemolition.rfShatter = SetShatter ( FragmentMode.Runtime, scr.meshDemolition.shatterMode, scr.meshDemolition.mesh, scr.transform, scr.materials, scr.meshDemolition.prp.dec, scr.meshDemolition.prp.rem, scr.meshDemolition.sd, true, false, false, false, false, 3, // elementSizeThreshold 4, // RemoveMinFaceFilter true); } // Failed input. Instant bad mesh. if (scr.meshDemolition.rfShatter == null) { scr.limitations.demolitionShould = false; scr.meshDemolition.badMesh += 10; scr.meshDemolition.mesh = null; return false; } return true; } // Cache for rigid public static void CacheMeshesInst(ref Mesh[] meshes, ref Vector3[] pivots, ref RFDictionary[] rfOrigSubMeshIds, RayfireRigid scrRigid) { // Local data lists List> origSubMeshIds = new List>(); // Calculate fragments bool successState = Compute ( scrRigid.meshDemolition.shatterMode, scrRigid.meshDemolition.rfShatter, scrRigid.transform, ref meshes, ref pivots, scrRigid.meshDemolition.mesh, scrRigid.meshDemolition.innerSubId, ref origSubMeshIds, scrRigid); // Create RF dictionary if (origSubMeshIds != null) rfOrigSubMeshIds = RFDictionary.GetRFDictionary (origSubMeshIds); // Failed fragmentation. Increase bad mesh if (successState == false) { scrRigid.meshDemolition.badMesh++; Debug.Log ("Bad mesh: " + scrRigid.name); } // Mesh naming else for (int i = 0; i < meshes.Length; i++) meshes[i].name = i.ToString(); } // Cache for rigid public static void CacheMeshesMult(Transform tmSaved, ref List meshesList, ref List pivotsList, ref List subList, RayfireRigid scrRigid, List batchAmount, int batchInd) { // Get list of meshes to calc List markedElements = RFRuntimeCaching.GetMarkedElements (batchInd, batchAmount); // Local iteration data lists Mesh[] meshesLocal = new Mesh[batchAmount.Count]; Vector3[] pivotsLocal = new Vector3[batchAmount.Count]; List> origSubMeshIds = new List>(); // Compute bool state = scrRigid.meshDemolition.rfShatter.SimpleCompute ( tmSaved, ref meshesLocal, ref pivotsLocal, scrRigid.meshDemolition.mesh, scrRigid.meshDemolition.innerSubId, ref origSubMeshIds, markedElements, batchInd == 0); // Set names if (state == false || meshesLocal == null || meshesLocal.Length == 0) return; // Set names for (int i = 0; i < meshesLocal.Length; i++) { meshesLocal[i].RecalculateTangents(); meshesLocal[i].name = scrRigid.name + "_fr"; // + markedElements[i].ToString(); } // Add data to main lists for (int i = 0; i < origSubMeshIds.Count; i++) subList.Add (new RFDictionary (origSubMeshIds[i])); meshesList.AddRange (meshesLocal); pivotsList.AddRange (pivotsLocal); } // Get demolition mesh static Mesh GetDemolitionMesh(RayfireRigid scr) { if (scr.skr != null) return RFMesh.BakeMesh (scr.skr); return scr.meshFilter.sharedMesh; } // Get demolition mesh static Mesh GetDemolitionMesh(RayfireShatter scr) { // Multymesh fragmentation if (scr.advanced.combineChildren == true && scr.meshFilters.Count > 0) return RFCombineMesh.CombineShatter (scr, scr.transform, scr.meshFilters); // Skinned mesh if (scr.skinnedMeshRend != null) return RFMesh.BakeMesh (scr.skinnedMeshRend); return scr.meshFilter.sharedMesh; } /// ///////////////////////////////////////////////////////// /// Slice /// ///////////////////////////////////////////////////////// // Cache for slice public static void SliceMeshes(ref Mesh[] meshes, ref Vector3[] pivots, ref RFDictionary[] rfOrigSubMeshIds, RayfireRigid scr, List sliceData) { // Get mesh scr.meshDemolition.mesh = GetDemolitionMesh (scr); // Set up shatter RFShatter shatter = SetShatter ( FragmentMode.Runtime, 2, scr.meshDemolition.mesh, scr.transform, scr.materials, true, scr.meshDemolition.prp.rem, scr.meshDemolition.sd, true, false, false, false, false, 3, // ElementSizeThreshold 1, // RemoveMinFaceFilter true); // Debug.Log ("slice"); // Failed input if (shatter == null) { meshes = null; pivots = null; scr.meshDemolition.badMesh++; return; } // Get innerSubId scr.meshDemolition.innerSubId = RFSurface.SetInnerSubId (scr); // Get slice data List points = new List (sliceData.Count / 2); List norms = new List (sliceData.Count / 2); for (int i = 0; i < sliceData.Count; i++) { points.Add (sliceData[i]); norms.Add (sliceData[i + 1]); i++; } // Set params shatter.SetBricksParams (points.ToArray(), norms.ToArray(), scr.transform); // Calculate fragments List> origSubMeshIds = new List>(); bool successState = Compute ( 2, shatter, scr.transform, ref meshes, ref pivots, scr.meshDemolition.mesh, scr.meshDemolition.innerSubId, ref origSubMeshIds, scr.gameObject); // Create RF dictionary if (origSubMeshIds != null) rfOrigSubMeshIds = RFDictionary.GetRFDictionary (origSubMeshIds); // Failed fragmentation. Increase bad mesh if (successState == false) { scr.meshes = null; scr.meshDemolition.badMesh++; Debug.Log ("Bad mesh: " + scr.name, scr.gameObject); } else { for (int i = 0; i < meshes.Length; i++) meshes[i].name = i.ToString(); } } /// ///////////////////////////////////////////////////////// /// Compute /// ///////////////////////////////////////////////////////// // Compute static bool Compute(int shatterMode, RFShatter shatter, Transform tm, ref Mesh[] meshes, ref Vector3[] pivots, Mesh mesh, int innerSubId, ref List> origSubMeshIDs, Object obj, List markedElements = null) { // Compute fragments bool state = shatterMode == 0 ? shatter.Compute (tm, ref meshes, ref pivots, mesh, innerSubId, ref origSubMeshIDs) : shatter.SimpleCompute (tm, ref meshes, ref pivots, mesh, innerSubId, ref origSubMeshIDs, markedElements); // Mesh singleMesh = RFShatter.WeldMeshes (ref meshes, ref pivots, ref origSubMeshIDs, 0.5f); // Failed fragmentation if (state == false) { meshes = null; pivots = null; origSubMeshIDs = null; return false; } // Null check if (meshes == null) { Debug.Log ("RFShatter: Null mesh", obj); meshes = null; pivots = null; origSubMeshIDs = null; return false; } // Empty mesh fix if (EmptyMeshState (meshes) == true) { for (int i = 0; i < meshes.Length; i++) { if (meshes[i].vertexCount > 2) { meshListStatic.Add (meshes[i]); pivotListStatic.Add (pivots[i]); subIdsListStatic.Add (origSubMeshIDs[i]); } } pivots = pivotListStatic.ToArray(); meshes = meshListStatic.ToArray(); origSubMeshIDs = subIdsListStatic; meshListStatic.Clear(); pivotListStatic.Clear(); subIdsListStatic.Clear(); Debug.Log ("RFShatter: Empty Mesh", obj); } // Single mesh after mesh fix check if (meshes.Length <= 1) { Debug.Log ("RFShatter: Mesh amount " + meshes.Length, obj); meshes = null; pivots = null; origSubMeshIDs = null; return false; } // Post for (int i = 0; i < meshes.Length; i++) { //Debug.Log(meshes[i].vertexCount); //meshes[i].indexFormat = IndexFormat.UInt32; meshes[i].RecalculateTangents(); } return true; } // Get shatter mode // 0:classic: Custom, tets, clustering: old original algorithm. // 1:voro simple: Decompose type or Rigid without Shatter. // 2:slice // 3:bricks static int GetShatterMode(RayfireShatter scrShatter = null) { // Voro Simple mode: for rigid without shatter if (scrShatter == null) return 1; // Brick Simple mode if (scrShatter.type == FragType.Slices) return 2; // Voro Simple mode if (scrShatter.type == FragType.Decompose) return 1; // Voro Simple mode if (scrShatter.type == FragType.Bricks || scrShatter.type == FragType.Voxels) if (scrShatter.type == FragType.Bricks || scrShatter.type == FragType.Voxels) return 3; // Classic mode int shatterMode = scrShatter.shatterMode; if (scrShatter.type == FragType.Hexagon) shatterMode = 0; if (scrShatter.type == FragType.Custom) shatterMode = 0; //if (scrShatter.type == FragType.Mirrored) // 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; } // Check for at least one empty mesh in cached meshes static bool EmptyMeshState(Mesh[] meshes) { for (int i = 0; i < meshes.Length; i++) if (meshes[i].vertexCount <= 2) return true; return false; } /// ///////////////////////////////////////////////////////// /// Set properties by fragment type /// ///////////////////////////////////////////////////////// // Set fragmentation properties static void SetShatterFragmentProperties(RFShatter shatter, RayfireShatter scrSh) { // Center position from local to global Vector3 centerPos = scrSh.transform.TransformPoint (scrSh.centerPosition); // Clustering if (scrSh.clusters.enable == true) SetClusters (shatter, scrSh.clusters); // 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.Amount, scrSh.splinters, scrSh.transform, centerPos, scrSh.splinters.centerBias); else if (scrSh.type == FragType.Slabs) SetSlabs (shatter, scrSh.slabs.Amount, 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.Hexagon) SetHexGrid (shatter, scrSh.hexagon, scrSh.transform, centerPos, scrSh.centerDirection, scrSh.bound, scrSh.advanced.seed); else if (scrSh.type == FragType.Custom) SetCustom (shatter, scrSh.custom, scrSh.transform, scrSh.bound, scrSh.advanced.seed); // else if (scrSh.type == FragType.Mirrored) // SetMirrored (shatter, scrSh.mirrored, scrSh.transform, scrSh.bound, scrSh.advanced.seed); else if (scrSh.type == FragType.Slices) SetSlices (shatter, scrSh.transform, scrSh.slice); else if (scrSh.type == FragType.Bricks) SetBricks (shatter, scrSh.transform, scrSh.bricks, scrSh.bound); else if (scrSh.type == FragType.Voxels) SetVoxels (shatter, scrSh.transform, scrSh.voxels, scrSh.bound); else if (scrSh.type == FragType.Tets) SetTet (shatter, scrSh.bound, scrSh.tets); else if (scrSh.type == FragType.Decompose) SetDecompose (shatter); } // Set fragmentation properties static void SetRigidFragmentProperties(RFShatter shatter, RayfireShatter scrSh, RayfireRigid scrRigid) { // Rigid demolition without shatter. Set and exit. if (scrSh == null) { // Get final amount int percVar = Random.Range (0, scrRigid.meshDemolition.am * scrRigid.meshDemolition.var / 100); scrRigid.meshDemolition.totalAmount = scrRigid.meshDemolition.am + percVar; // Set Voronoi Uniform properties SetVoronoi (shatter, scrRigid.meshDemolition.totalAmount, scrRigid.transform, scrRigid.limitations.contactVector3, scrRigid.meshDemolition.bias); return; } // Set shatter center as contact point for awake precache and prefragment if (scrRigid.limitations.contactVector3.magnitude == 0) scrRigid.limitations.contactVector3 = scrSh.transform.TransformPoint (scrSh.centerPosition); // 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; // Get final amount with variation if (scrRigid.meshDemolition.var > 0) { int percVar = Random.Range (0, scrRigid.meshDemolition.totalAmount * scrRigid.meshDemolition.var / 100); scrRigid.meshDemolition.totalAmount += percVar; } // Clustering if (scrSh.clusters.enable == true) SetClusters (shatter, scrSh.clusters); // Set properties if (scrSh.type == FragType.Voronoi) SetVoronoi (shatter, scrRigid.meshDemolition.totalAmount, scrSh.transform, scrRigid.limitations.contactVector3, scrSh.voronoi.centerBias); else if (scrSh.type == FragType.Splinters) SetSplinters (shatter, scrRigid.meshDemolition.totalAmount, scrSh.splinters, scrSh.transform, scrRigid.limitations.contactVector3, scrSh.splinters.centerBias); else if (scrSh.type == FragType.Slabs) SetSlabs (shatter, scrRigid.meshDemolition.totalAmount, scrSh.slabs, scrSh.transform, scrRigid.limitations.contactVector3, scrSh.splinters.centerBias); else if (scrSh.type == FragType.Radial) SetRadial (shatter, scrSh.radial, scrSh.transform, scrRigid.limitations.contactVector3, scrSh.centerDirection); else if (scrSh.type == FragType.Custom) SetCustom (shatter, scrSh.custom, scrSh.transform, scrSh.bound, scrSh.advanced.seed); // else if (scrSh.type == FragType.Mirrored) // SetMirrored (shatter, scrSh.mirrored, scrSh.transform, scrSh.bound, scrSh.advanced.seed); else if (scrSh.type == FragType.Slices) SetSlices (shatter, scrSh.transform, scrSh.slice); else if (scrSh.type == FragType.Bricks) SetBricks (shatter, scrSh.transform, scrSh.bricks, scrSh.bound); else if (scrSh.type == FragType.Voxels) SetVoxels (shatter, scrSh.transform, scrSh.voxels, scrSh.bound); else if (scrSh.type == FragType.Tets) SetTet (shatter, scrSh.bound, scrSh.tets); else if (scrSh.type == FragType.Decompose) SetDecompose (shatter); } /// ///////////////////////////////////////////////////////// /// Properties setup /// ///////////////////////////////////////////////////////// // Set common fragmentation properties static RFShatter SetShatter( FragmentMode fragmentMode, int shatterMode, Mesh mesh, Transform transform, RFSurface interior, bool decompose, bool delete_collinear = false, int seed = 1, bool pre_cap = true, bool remove_cap_faces = false, bool remove_double_faces = true, bool exclude_inside = false, bool post_normals_smooth = false, int min_bbox_diag_size_filter_perc = 3, int meshRemoveMinFaceFilter = 4, bool postWeld = true ) { // Creating shatter RFShatter shatter = new RFShatter ((RFShatter.RFShatterMode)shatterMode, true); // Safe/unsafe properties if (fragmentMode == FragmentMode.Editor) { float min_bbox_diag_size_filter = mesh.bounds.size.magnitude * min_bbox_diag_size_filter_perc / 100f; // TODO check render bound size SetShatterEditorMode (shatter, min_bbox_diag_size_filter, pre_cap, remove_cap_faces, remove_double_faces, exclude_inside, meshRemoveMinFaceFilter); } else { SetShatterRuntimeMode (shatter, pre_cap, meshRemoveMinFaceFilter); } // Detach by elements shatter.DecomposeResultMesh (decompose); // Set custom UV coordinate for inner surface SetCustomUV (shatter, interior); // Set custom vertex color for inner surface SetCustomColor (shatter, interior); // Set silent mode shatter.SilentModeEnable (silentMode); // Set seed if (seed == 0) seed = Random.Range (0, 100); // Set properties shatter.SetFragmentParameter (RFShatter.FragmentParams.seed, seed); shatter.SetGeneralParameter (RFShatter.GeneralParams.pre_weld_threshold, 0.001f); shatter.SetGeneralParameter (RFShatter.GeneralParams.delete_collinear, delete_collinear); shatter.SetGeneralParameter (RFShatter.GeneralParams.post_normals_smooth, post_normals_smooth); shatter.SetGeneralParameter (RFShatter.GeneralParams.post_weld, false); shatter.SetGeneralParameter (RFShatter.GeneralParams.maping_scale, interior.mScl); shatter.SetGeneralParameter (RFShatter.GeneralParams.restore_normals, true); // Setting shatter params bool inputState = shatter.SetInputMesh (transform, mesh); // Failed input if (inputState == false) { Debug.Log ("Bad input mesh: " + transform.name, transform.gameObject); return null; } return shatter; } // Set Shatter Editor Mode properties static void SetShatterEditorMode( RFShatter shatter, float min_bbox_diag_size_filter, bool pre_cap, bool remove_cap_faces, bool remove_double_faces, bool exclude_inside, int meshRemoveMinFaceFilter) { shatter.EditorMode (true); shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_min_bbox_diag_size_filter, min_bbox_diag_size_filter); shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_pre_cap, pre_cap); shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_remove_cap_faces, remove_cap_faces); shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_remove_double_faces, remove_double_faces); shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_remove_inversed_double_faces, remove_double_faces); shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_exclude_inside, exclude_inside); shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_meshRemoveMinFaceFilter, meshRemoveMinFaceFilter); // Minimum amount of triangles for element to be fragmented, will be removed otherwise shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_elliminateCollinears_maxIterFuse, 150); shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_separate_only, false); shatter.SetGeneralParameter (RFShatter.GeneralParams.minFacesFilter, 0); } // Set Shatter Runtime Mode properties static void SetShatterRuntimeMode(RFShatter shatter, bool pre_cap, int meshRemoveMinFaceFilter) { shatter.EditorMode (false); shatter.SetGeneralParameter (RFShatter.GeneralParams.pre_shatter, true); shatter.SetGeneralParameter (RFShatter.GeneralParams.pre_cap, pre_cap); shatter.SetGeneralParameter (RFShatter.GeneralParams.pre_weld, true); shatter.SetGeneralParameter (RFShatter.GeneralParams.minFacesFilter, 3); shatter.SetGeneralParameter (RFShatter.GeneralParams.meshRemoveMinFaceFilter, meshRemoveMinFaceFilter); } // Set custom UV coordinate for inner surface static void SetCustomUV(RFShatter shatter, RFSurface interior) { shatter.SetGeneralParameter (RFShatter.GeneralParams.enable_custUVs, interior.uvE); if (interior.uvE == true) shatter.SetPoint3Parameter ((int)RFShatter.GeneralParams.cust_UVs, new Vector3 (interior.uvC.x, interior.uvC.y, 0)); } // Set custom vertex color for inner surface static void SetCustomColor(RFShatter shatter, RFSurface interior) { if (interior.cE == true) { Vector3 vc = new Vector3 (interior.cC.r, interior.cC.g, interior.cC.b); shatter.SetPoint3Parameter ((int)RFShatter.GeneralParams.inner_color, vc); } } /// ///////////////////////////////////////////////////////// /// Fragmentation types /// ///////////////////////////////////////////////////////// // Set Uniform static void SetVoronoi(RFShatter shatter, int numFragments, Transform tm, Vector3 centerPos, float centerBias) { // Get amount int amount = numFragments; if (amount < 1) amount = 1; if (amount > 20000) amount = 2; // Set properties shatter.SetFragmentParameter (RFShatter.FragmentParams.type, (int)RFShatter.FragmentType.voronoi); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_type, (int)RFShatter.VoronoiType.irregular); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_irr_num, amount); // Set bias to center if (centerBias > 0) { shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_irr_bias, centerBias); shatter.SetCenterParameter (centerPos, tm, Vector3.forward); } } // Set Splinters static void SetSplinters(RFShatter shatter, int numFragments, RFSplinters splint, Transform tm, Vector3 centerPos, float centerBias) { // Set properties shatter.SetFragmentParameter (RFShatter.FragmentParams.type, (int)RFShatter.FragmentType.voronoi); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_type, (int)RFShatter.VoronoiType.irregular); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_irr_num, numFragments); // Set center shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_irr_bias, centerBias); shatter.SetCenterParameter (centerPos, tm, Vector3.forward); // Set Stretching for slabs SetStretching (shatter, splint.axis, splint.strength, FragType.Splinters); } // Set Slabs static void SetSlabs(RFShatter shatter, int numFragments, RFSplinters slabs, Transform tm, Vector3 centerPos, float centerBias) { // Set properties shatter.SetFragmentParameter (RFShatter.FragmentParams.type, (int)RFShatter.FragmentType.voronoi); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_type, (int)RFShatter.VoronoiType.irregular); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_irr_num, numFragments); // Set center shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_irr_bias, centerBias); shatter.SetCenterParameter (centerPos, tm, Vector3.forward); // Set Stretching for slabs SetStretching (shatter, slabs.axis, slabs.strength, FragType.Slabs); } // Set Radial static void SetRadial(RFShatter shatter, RFRadial radial, Transform tm, Vector3 centerPos, Quaternion centerDirection) { // Set radial properties shatter.SetFragmentParameter (RFShatter.FragmentParams.type, (int)RFShatter.FragmentType.voronoi); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_type, (int)RFShatter.VoronoiType.radial); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_radius, radial.radius); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_divergence, radial.divergence); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_restrict, radial.restrictToPlane); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_rings_count, radial.rings); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_rings_focus, radial.focus); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_rings_strenght, radial.focusStr); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_rings_random, radial.randomRings); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_rays_count, radial.rays); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_rays_random, radial.randomRays); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_rad_rays_twist, radial.twist); // Get direction axis Vector3 directionAxis = DirectionAxis (radial.centerAxis); Vector3 centerRot = tm.rotation * centerDirection * directionAxis; shatter.SetCenterParameter (centerPos, tm, centerRot); } // Set hexgrid custom point cloud static void SetHexGrid(RFShatter shatter, RFHexagon hexagon, Transform tm, Vector3 centerPos, Quaternion centerDirection, Bounds bound, int seed) { // Set properties shatter.SetFragmentParameter (RFShatter.FragmentParams.type, (int)RFShatter.FragmentType.voronoi); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_type, (int)RFShatter.VoronoiType.custom); // Get Point Cloud List pointCloud = RFHexagon.GetHexPointCLoud (hexagon, tm, centerPos, centerDirection, seed, bound); // Set points shatter.SetVoroCustomPoints (pointCloud.ToArray(), tm); } // Set custom point cloud static void SetCustom(RFShatter shatter, RFCustom custom, Transform tm, Bounds bound, int seed) { // Set properties shatter.SetFragmentParameter (RFShatter.FragmentParams.type, (int)RFShatter.FragmentType.voronoi); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_type, (int)RFShatter.VoronoiType.custom); // Get Point Cloud List pointCloud = RFCustom.GetCustomPointCLoud (custom, tm, seed, bound); // Set points shatter.SetVoroCustomPoints (pointCloud.ToArray(), tm); // Set Stretching TODO point cloud rescale by transform // if (custom.modifier == RFCustom.RFModifierType.Splinters) // SetStretching (shatter, splint.axis, splint.strength, FragType.Splinters); // else if (custom.modifier == RFCustom.RFModifierType.Slabs) // SetStretching (shatter, slabs.axis, slabs.strength, FragType.Slabs); } // Set custom mirrored point cloud static void SetMirrored(RFShatter shatter, RFMirrored mirror, Transform tm, Bounds bound, int seed) { // Set properties shatter.SetFragmentParameter (RFShatter.FragmentParams.type, (int)RFShatter.FragmentType.voronoi); shatter.SetFragmentParameter (RFShatter.FragmentParams.voronoi_type, (int)RFShatter.VoronoiType.custom); // Get Point Cloud List pointCloud = RFMirrored.GetMirroredPointCLoud (mirror, tm, seed, bound); // Set points shatter.SetVoroCustomPoints (pointCloud.ToArray(), tm); } // Set slicing objects static void SetSlices(RFShatter shatter, Transform tm, RFSlice slices) { // Filter List list = new List(); for (int i = 0; i < slices.sliceList.Count; i++) if (slices.sliceList[i] != null) list.Add (slices.sliceList[i]); // No objects if (list.Count == 0) return; // Get slice data Vector3[] points = list.Select (t => t.position).ToArray(); Vector3[] norms = list.Select (t => slices.Axis (t)).ToArray(); // Set params shatter.SetBricksParams (points, norms, tm); } // Set bricks properties static void SetBricks(RFShatter shatter, Transform tm, RFBricks bricks, Bounds bounds) { // Amount size if (bricks.amountType == RFBricks.RFBrickType.ByAmount) { float X = bricks.amount_X * bricks.mult; if (X == 0) X = 1; float Y = bricks.amount_Y * bricks.mult; if (Y == 0) Y = 1; float Z = bricks.amount_Z * bricks.mult; if (Z == 0) Z = 1; Vector3 amount = new Vector3 (X, Y, Z); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_num_bricks, amount); } else if (bricks.amountType == RFBricks.RFBrickType.BySize) { // TODO small size check Vector3 size = new Vector3 (bricks.size_X * bricks.mult, bricks.size_Y * bricks.mult, bricks.size_Z * bricks.mult); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_num_bricks, Vector3.zero); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_brick_size, size); } // Random size Vector3 random_size = new Vector3 (bricks.sizeVar_X / 100f, bricks.sizeVar_Y / 100f, bricks.sizeVar_Z / 100f); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_random_size, random_size); // Offset Vector3 offsets = new Vector3 (bricks.offset_X, bricks.offset_Y, bricks.offset_Z); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_offsets, offsets); // 0-1 // Split Vector3 random_split = new Vector3 (BoolToFloat (bricks.split_X), BoolToFloat (bricks.split_Y), BoolToFloat (bricks.split_Z)); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_random_split, random_split); shatter.SetFragmentParameter (RFShatter.FragmentParams.brick_slice_probability, bricks.split_probability * 0.01f); shatter.SetFragmentParameter (RFShatter.FragmentParams.brick_slice_offset, bricks.split_offset); shatter.SetFragmentParameter (RFShatter.FragmentParams.brick_slice_rotate, bricks.split_rotation); } // Set voxels properties static void SetVoxels(RFShatter shatter, Transform tm, RFVoxels voxels, Bounds bounds) { // TODO small size check Vector3 size = new Vector3 (voxels.size, voxels.size, voxels.size); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_num_bricks, Vector3.zero); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_brick_size, size); // Offset shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.bricks_offsets, Vector3.zero); } static float BoolToFloat(bool v) { if (v == false) return 0; return 1f; } // Set Custom Voronoi properties static void SetTet(RFShatter shatter, Bounds bounds, RFTets tets) { // Main shatter.SetFragmentParameter (RFShatter.FragmentParams.type, (int)RFShatter.FragmentType.tetra); shatter.SetFragmentParameter (RFShatter.FragmentParams.tetra_type, (int)tets.lattice); // Get max float max = bounds.size.x; if (bounds.size.y > max) max = bounds.size.y; if (bounds.size.z > max) max = bounds.size.z; if (max == 0) max = 0.01f; // Get density Vector3Int density = new Vector3Int ( (int)Mathf.Ceil (bounds.size.x / max * tets.density), (int)Mathf.Ceil (bounds.size.y / max * tets.density), (int)Mathf.Ceil (bounds.size.z / max * tets.density)); // Limit if (density.x > 30) density.x = 30; else if (density.x < 1) density.x = 1; if (density.y > 30) density.y = 30; else if (density.y < 1) density.y = 1; if (density.z > 30) density.z = 30; else if (density.z < 1) density.z = 1; // Set density shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.tetra2_density, density); shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.tetra1_density, density); // Noise shatter.SetFragmentParameter (RFShatter.FragmentParams.tetra_noise, tets.noise); } // Decompose to elements static void SetDecompose(RFShatter shatter) { shatter.SetGeneralParameter (RFShatter.GeneralParams.editor_mode_separate_only, true); } /// ///////////////////////////////////////////////////////// /// Clusters /// ///////////////////////////////////////////////////////// // Set clusters static void SetClusters(RFShatter shatter, RFShatterCluster gluing) { // Set seed int glueSeed = gluing.seed; if (glueSeed == 0) glueSeed = Random.Range (0, 1000); shatter.InitClustering (true); shatter.SetClusterParameter (RFShatter.ClusterParams.enabled, true); shatter.SetClusterParameter (RFShatter.ClusterParams.by_pcloud_count, gluing.count); shatter.SetClusterParameter (RFShatter.ClusterParams.options_seed, glueSeed); shatter.SetClusterParameter (RFShatter.ClusterParams.preview_scale, 100f); // shatter.SetClusterParameter(RFShatter.ClusterParams.clust_center_tm, ); // Debris shatter.SetClusterParameter (RFShatter.ClusterParams.debris_layers_count, gluing.layers); shatter.SetClusterParameter (RFShatter.ClusterParams.debris_count, gluing.amount); shatter.SetClusterParameter (RFShatter.ClusterParams.debris_scale, gluing.scale); shatter.SetClusterParameter (RFShatter.ClusterParams.debris_min, gluing.min); shatter.SetClusterParameter (RFShatter.ClusterParams.debris_max, gluing.max); shatter.SetClusterParameter (RFShatter.ClusterParams.debris_tessellate, false); shatter.SetClusterParameter (RFShatter.ClusterParams.debris_remove, false); // Glue shatter.SetGeneralParameter (RFShatter.GeneralParams.glue, true); shatter.SetGeneralParameter (RFShatter.GeneralParams.glue_weld_threshold, 0.001f); shatter.SetGeneralParameter (RFShatter.GeneralParams.relax, gluing.relax); } /// ///////////////////////////////////////////////////////// /// Stretching /// ///////////////////////////////////////////////////////// // Set stretching static void SetStretching(RFShatter shatter, AxisType axis, float strength, FragType fragType) { // Get slab vector Vector3 stretchDir = DirectionAxis (axis); // Adjust for slabs if (fragType == FragType.Slabs) { Vector3 vector = new Vector3(); if (stretchDir.x <= 0) vector.x = 1f; if (stretchDir.x >= 1f) vector.x = 0; if (stretchDir.y <= 0) vector.y = 1f; if (stretchDir.y >= 1f) vector.y = 0; if (stretchDir.z <= 0) vector.z = 1f; if (stretchDir.z >= 1f) vector.z = 0; stretchDir = vector; } // Set stretch vector shatter.SetPoint3Parameter ((int)RFShatter.FragmentParams.stretching, stretchDir * Mathf.Lerp (40f, 99f, strength)); } // Get axis by type static Vector3 DirectionAxis(AxisType axisType) { if (axisType == AxisType.YGreen) return Vector3.up; if (axisType == AxisType.ZBlue) return Vector3.forward; return Vector3.right; } /// ///////////////////////////////////////////////////////// /// Mesh /// ///////////////////////////////////////////////////////// // Scale mesh public static void RescaleMesh(Mesh mesh, float scale) { Vector3[] verts = mesh.vertices; for (int j = 0; j < verts.Length; j++) verts[j] /= scale; mesh.vertices = verts; } } } // Static dummy class for other platforms #else namespace RayFire { public static class RFFragment { public static bool PrepareCacheMeshes(RayfireRigid scr) { BuildTest(scr); return false; } public static void CacheMeshesMult(Transform tmSaved, ref List meshesList, ref List pivotsList, ref List subList, RayfireRigid scrRigid, List batchAmount, int batchInd) { BuildTest(); } public static void CacheMeshesInst(ref Mesh[] meshes, ref Vector3[] pivots, ref RFDictionary[] origSubMeshIdsRf, RayfireRigid scrRigid) { BuildTest(); } public static void CacheMeshes(ref Mesh[] meshes, ref Vector3[] pivots, ref RFDictionary[] origSubMeshIdsRf, RayfireShatter scrShatter) { BuildTest(); } public static void SliceMeshes(ref Mesh[] meshes, ref Vector3[] pivots, ref RFDictionary[] origSubMeshIdsRf, RayfireRigid scrRigid, List sliceData) { BuildTest(); } public static void RescaleMesh (Mesh mesh, float scale) { BuildTest(); } public static bool InputMesh(RayfireRigid scr) { BuildTest(); scr.meshes = null; return false; } static void BuildTest(RayfireRigid scr) { //Debug.Log ("Dummy"); } static void BuildTest() { //Debug.Log ("Dummy"); } } public class RFShatter{} } #endif