ThrowBall/Assets/Plugins/RayFire/Scripts/Components/RayfireShatter.cs

743 lines
25 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
#if (UNITY_EDITOR || UNITY_STANDALONE || UNITY_IOS || UNITY_ANDROID || UNITY_XBOXONE)
using RayFire.DotNet;
#endif
namespace RayFire
{
[AddComponentMenu ("RayFire/Rayfire Shatter")]
[HelpURL ("https://rayfirestudios.com/unity-online-help/components/unity-shatter-component/")]
public class RayfireShatter : MonoBehaviour
{
public enum RFEngineType
{
Max = 0,
UnityBeta = 1
}
public enum FragLastMode
{
New = 0,
ToLast = 1
}
// UI
public RFEngineType engine = RFEngineType.Max;
public FragType type = FragType.Voronoi;
public RFVoronoi voronoi = new RFVoronoi();
public RFSplinters splinters = new RFSplinters();
public RFSplinters slabs = new RFSplinters();
public RFRadial radial = new RFRadial();
public RFHexagon hexagon = new RFHexagon();
public RFCustom custom = new RFCustom();
public RFMirrored mirrored = new RFMirrored();
public RFSlice slice = new RFSlice();
public RFBricks bricks = new RFBricks();
public RFVoxels voxels = new RFVoxels();
public RFTets tets = new RFTets();
public RFSurface material = new RFSurface();
public RFShatterCluster clusters = new RFShatterCluster();
public FragmentMode mode = FragmentMode.Editor;
public RFShatterAdvanced advanced = new RFShatterAdvanced();
public RFMeshExport export = new RFMeshExport();
// Center
public bool showCenter;
public Vector3 centerPosition;
public Quaternion centerDirection;
// Components
public Transform transForm;
public MeshFilter meshFilter;
public MeshRenderer meshRenderer;
public SkinnedMeshRenderer skinnedMeshRend;
public List<MeshFilter> meshFilters;
// Vars
[NonSerialized] public Mesh[] meshes;
[NonSerialized] public Vector3[] pivots;
[NonSerialized] public RFDictionary[] rfOrigSubMeshIds;
public List<Transform> rootChildList = new List<Transform>();
public List<GameObject> fragmentsAll = new List<GameObject>();
public List<GameObject> fragmentsLast = new List<GameObject>();
public Material[] materials;
// Hidden
public int shatterMode = 1;
public bool colorPreview;
public bool scalePreview = true;
public float previewScale;
public float size;
public float rescaleFix = 1f;
public Vector3 originalScale;
public Bounds bound;
public bool resetState;
// Interactive
public bool interactive;
public RFShatter shatterInt;
public GameObject intGo;
public MeshFilter intMf;
public MeshRenderer intMr;
// Static
static float minSize = 0.01f;
public string fragAddStr = "_sh_";
public string shatterStr = "RayFire Shatter: ";
/// /////////////////////////////////////////////////////////
/// Common
/// /////////////////////////////////////////////////////////
// Reset
private void Reset()
{
InteractiveStop();
ResetCenter();
}
// Set default vars before fragment
void SetVariables()
{
size = 0f;
rescaleFix = 1f;
originalScale = transForm.localScale;
rfOrigSubMeshIds = null;
}
/// /////////////////////////////////////////////////////////
/// Checks
/// /////////////////////////////////////////////////////////
// Basic proceed check
bool MainCheck()
{
// Check if prefab
if (gameObject.scene.rootCount == 0)
{
Debug.Log (shatterStr + name + " Can't fragment prefab because prefab unable to store Unity mesh. Fragment prefab in scene.", gameObject);
return false;
}
// Single mesh mode
if (advanced.combineChildren == false)
if (SingleMeshCheck() == false)
return false;
// Multiple mesh mode
if (advanced.combineChildren == true)
{
// Has no children meshes
if (meshFilters.Count == 1)
if (SingleMeshCheck() == false)
return false;
// Remove no meshes
if (meshFilters.Count > 0)
for (int i = meshFilters.Count - 1; i >= 0; i--)
if (meshFilters[i].sharedMesh == null)
{
Debug.Log (shatterStr + meshFilters[i].name + " MeshFilter has no Mesh, object excluded.", meshFilters[i].gameObject);
meshFilters.RemoveAt (i);
}
// Remove no readable meshes
if (meshFilters.Count > 0)
for (int i = meshFilters.Count - 1; i >= 0; i--)
if (meshFilters[i].sharedMesh.isReadable == false)
{
Debug.Log (shatterStr + meshFilters[i].name + " Mesh is not Readable, object excluded.", meshFilters[i].gameObject);
meshFilters.RemoveAt (i);
}
// No meshes left
if (meshFilters.Count == 0)
return false;
}
return true;
}
// Single mesh mode checks
bool SingleMeshCheck()
{
// No mesh storage components
if (meshFilter == null && skinnedMeshRend == null)
{
Debug.Log (shatterStr + name + " Object has no mesh to fragment.", gameObject);
return false;
}
// Has mesh filter
if (meshFilter != null)
{
// No shared mesh
if (meshFilter.sharedMesh == null)
{
Debug.Log (shatterStr + name + " Object has no mesh to fragment.", gameObject);
return false;
}
// Not readable mesh
if (meshFilter.sharedMesh.isReadable == false)
{
Debug.Log (shatterStr + name + "Mesh is not readable. Open Import Settings and turn On Read/Write Enabled", gameObject);
return false;
}
}
// Has skinned mesh
if (skinnedMeshRend != null && skinnedMeshRend.sharedMesh == null)
{
Debug.Log (shatterStr + name + " Object has no mesh to fragment.", gameObject);
return false;
}
return true;
}
/// /////////////////////////////////////////////////////////
/// Methods
/// /////////////////////////////////////////////////////////
// Cache variables
bool DefineComponents()
{
// Mesh storage
transForm = GetComponent<Transform>();
meshFilter = GetComponent<MeshFilter>();
meshRenderer = GetComponent<MeshRenderer>();
skinnedMeshRend = GetComponent<SkinnedMeshRenderer>();
// Multymesh fragmentation
meshFilters = new List<MeshFilter>();
if (advanced.combineChildren == true)
meshFilters = GetComponentsInChildren<MeshFilter>().ToList();
// Basic proceed check
if (MainCheck() == false)
return false;
// Mesh renderer
if (skinnedMeshRend == null)
{
if (meshRenderer == null)
meshRenderer = gameObject.AddComponent<MeshRenderer>();
bound = meshRenderer.bounds;
}
// Skinned mesh
if (skinnedMeshRend != null)
bound = skinnedMeshRend.bounds;
return true;
}
// Get bounds
public Bounds GetBound()
{
// Mesh renderer
if (meshRenderer == null)
{
meshRenderer = GetComponent<MeshRenderer>();
if (meshRenderer != null)
return meshRenderer.bounds;
}
else
return meshRenderer.bounds;
// Skinned mesh
if (skinnedMeshRend == null)
{
skinnedMeshRend = GetComponent<SkinnedMeshRenderer>();
if (skinnedMeshRend != null)
return skinnedMeshRend.bounds;
}
return new Bounds();
}
/// /////////////////////////////////////////////////////////
/// Methods
/// /////////////////////////////////////////////////////////
// Fragment this object by shatter properties List<GameObject>
public void Fragment(FragLastMode fragmentMode = FragLastMode.New)
{
System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start();
// Engine
if (engine == RFEngineType.Max)
FragmentMax(fragmentMode);
if (engine == RFEngineType.UnityBeta)
FragmentUnity();
stopWatch.Stop();
Debug.Log("Total Time == " + stopWatch.Elapsed.TotalMilliseconds + " ms)");
}
// New Unity fragmentation engine
void FragmentUnity()
{
}
// Original 3dsMax engine fragmentation
void FragmentMax(FragLastMode fragmentMode)
{
// Prepare to cache fragments
if (PreCache() == false)
return;
// Cache
RFFragment.CacheMeshes (ref meshes, ref pivots, ref rfOrigSubMeshIds, this);
// Stop
if (meshes == null)
return;
// Create fragments
if (fragmentMode == FragLastMode.ToLast)
{
if (rootChildList[rootChildList.Count - 1] != null)
fragmentsLast = CreateFragments (rootChildList[rootChildList.Count - 1]);
else
fragmentMode = FragLastMode.New;
}
// Create new fragments
if (fragmentMode == FragLastMode.New)
fragmentsLast = CreateFragments();
// Post create fragments operations
PostFragments();
}
// Prepare and cache fragments
public bool PreCache()
{
// Cache variables
if (DefineComponents() == false)
return false;
// Cache default vars
SetVariables();
// Check if object is too small
ScaleCheck();
return true;
}
// Create fragments by mesh and pivots array
List<GameObject> CreateFragments(Transform root = null)
{
// No mesh were cached
if (meshes == null)
return null;
// Clear array for new fragments
GameObject[] fragArray = new GameObject[meshes.Length];
// Create root object
if (root == null)
{
GameObject rootGo = new GameObject (gameObject.name + "_root");
rootGo.transform.position = transForm.position;
rootGo.transform.rotation = transForm.rotation;
rootGo.tag = gameObject.tag;
rootGo.layer = gameObject.layer;
root = rootGo.transform;
rootChildList.Add (root);
}
// Create instance for fragments
GameObject fragInstance;
if (advanced.copyComponents == true)
{
fragInstance = Instantiate (gameObject);
fragInstance.transform.rotation = Quaternion.identity;
fragInstance.transform.localScale = Vector3.one;
// Destroy shatter
DestroyImmediate (fragInstance.GetComponent<RayfireShatter>());
}
else
{
fragInstance = new GameObject();
fragInstance.AddComponent<MeshFilter>();
fragInstance.AddComponent<MeshRenderer>();
}
// Get original mats. in case of combined meshes it is already defined in CombineShatter()
if (advanced.combineChildren == false)
materials = skinnedMeshRend != null
? skinnedMeshRend.sharedMaterials
: meshRenderer.sharedMaterials;
// Vars
string baseName = gameObject.name + fragAddStr;
// Create fragment objects
MeshFilter mf;
GameObject go;
MeshCollider mc;
MeshRenderer rn;
for (int i = 0; i < meshes.Length; ++i)
{
// Rescale mesh
if (rescaleFix != 1f)
RFFragment.RescaleMesh (meshes[i], rescaleFix);
// Instantiate. IMPORTANT do not parent when Instantiate
go = Instantiate (fragInstance);
go.transform.localScale = Vector3.one;
// Set multymaterial
rn = go.GetComponent<MeshRenderer>();
RFSurface.SetMaterial (rfOrigSubMeshIds, materials, material, rn, i, meshes.Length);
// Set fragment object name and tm
go.name = baseName + (i + 1);
go.transform.position = root.transform.position + (pivots[i] / rescaleFix);
go.transform.parent = root.transform;
go.tag = gameObject.tag;
go.layer = gameObject.layer;
// Set fragment mesh
mf = go.GetComponent<MeshFilter>();
mf.sharedMesh = meshes[i];
mf.sharedMesh.name = go.name;
// Set mesh collider
mc = go.GetComponent<MeshCollider>();
if (mc != null)
mc.sharedMesh = meshes[i];
// Add in array
fragArray[i] = go;
}
// Root back to original parent
root.transform.parent = transForm.parent;
// Reset scale for mesh fragments. IMPORTANT: skinned mesh fragments root should not be rescaled
if (skinnedMeshRend == null)
root.transform.localScale = Vector3.one;
// Destroy instance
DestroyImmediate (fragInstance);
// Empty lists
meshes = null;
pivots = null;
rfOrigSubMeshIds = null;
return fragArray.ToList();
}
// Post create fragments operations
void PostFragments()
{
// Limitation fragment
RFShatterAdvanced.Limitations (this);
// Collect to all fragments
fragmentsAll.AddRange (fragmentsLast);
// Reset original object back if it was scaled
transForm.localScale = originalScale;
}
// Fragment by limitations
public void LimitationFragment(int ind)
{
RayfireShatter shat = fragmentsLast[ind].AddComponent<RayfireShatter>();
shat.voronoi.amount = 10;
shat.Fragment();
if (shat.fragmentsLast.Count > 0)
{
fragmentsLast.AddRange (shat.fragmentsLast);
DestroyImmediate (shat.gameObject);
fragmentsLast.RemoveAt (ind);
// Parent and destroy root
foreach (var frag in shat.fragmentsLast)
frag.transform.parent = rootChildList[rootChildList.Count - 1];
DestroyImmediate (shat.rootChildList[rootChildList.Count - 1].gameObject);
}
}
/// /////////////////////////////////////////////////////////
/// Deleting
/// /////////////////////////////////////////////////////////
// Delete fragments from last Fragment method
public void DeleteFragmentsLast(int destroyMode = 0)
{
// Destroy last fragments
if (destroyMode == 1)
for (int i = fragmentsLast.Count - 1; i >= 0; i--)
if (fragmentsLast[i] != null)
DestroyImmediate (fragmentsLast[i]);
// Clean fragments list pre
fragmentsLast.Clear();
for (int i = fragmentsAll.Count - 1; i >= 0; i--)
if (fragmentsAll[i] == null)
fragmentsAll.RemoveAt (i);
// Check for all roots
for (int i = rootChildList.Count - 1; i >= 0; i--)
if (rootChildList[i] == null)
rootChildList.RemoveAt (i);
// No roots
if (rootChildList.Count == 0)
return;
// Destroy with root
if (destroyMode == 0)
{
// Destroy root with fragments
DestroyImmediate (rootChildList[rootChildList.Count - 1].gameObject);
// Remove from list
rootChildList.RemoveAt (rootChildList.Count - 1);
}
// Clean all fragments list post
for (int i = fragmentsAll.Count - 1; i >= 0; i--)
if (fragmentsAll[i] == null)
fragmentsAll.RemoveAt (i);
}
// Delete all fragments and roots
public void DeleteFragmentsAll()
{
// Clear lists
fragmentsLast.Clear();
fragmentsAll.Clear();
// Check for all roots
for (int i = rootChildList.Count - 1; i >= 0; i--)
if (rootChildList[i] != null)
DestroyImmediate (rootChildList[i].gameObject);
rootChildList.Clear();
}
// Reset center helper
public void ResetCenter()
{
centerPosition = Vector3.zero;
centerDirection = Quaternion.identity;
Renderer rend = GetComponent<Renderer>();
if (rend != null)
centerPosition = transform.InverseTransformPoint (rend.bounds.center);
}
/// /////////////////////////////////////////////////////////
/// Scale
/// /////////////////////////////////////////////////////////
// Check if object is too small
void ScaleCheck()
{
// Geе size from renderers
if (meshRenderer != null)
size = meshRenderer.bounds.size.magnitude;
if (skinnedMeshRend != null)
size = skinnedMeshRend.bounds.size.magnitude;
// Get rescaleFix if too small
if (size != 0f && size < minSize)
{
// Get rescaleFix factor
rescaleFix = 1f / size;
// Scale small object up to shatter
Vector3 newScale = transForm.localScale * rescaleFix;
transForm.localScale = newScale;
// Warning
Debug.Log ("Warning. Object " + name + " is too small.");
}
}
// Reset original object and fragments scale
public void ResetScale(float scaleValue)
{
// Reset scale
if (resetState == true && scaleValue == 0f)
{
if (skinnedMeshRend != null)
skinnedMeshRend.enabled = true;
if (meshRenderer != null)
meshRenderer.enabled = true;
if (fragmentsLast.Count > 0)
foreach (GameObject fragment in fragmentsLast)
if (fragment != null)
fragment.transform.localScale = Vector3.one;
resetState = false;
}
}
/// /////////////////////////////////////////////////////////
/// Copy
/// /////////////////////////////////////////////////////////
// Copy shatter component
public static void CopyRootMeshShatter(RayfireRigid source, List<RayfireRigid> targets)
{
// No shatter
if (source.mshDemol.sht == null)
return;
// Copy shatter
for (int i = 0; i < targets.Count; i++)
{
targets[i].mshDemol.sht = targets[i].gameObject.AddComponent<RayfireShatter>();
targets[i].mshDemol.sht.CopyFrom (source.mshDemol.sht);
}
}
// 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);
}
/// /////////////////////////////////////////////////////////
/// Interactive
/// /////////////////////////////////////////////////////////
// Create Interactive object
public void InteractiveCreate()
{
if (intGo == null)
{
intGo = new GameObject (name + "_Interactive");
intMf = intGo.AddComponent<MeshFilter>();
}
if (intMf == null)
{
intMf = intGo.GetComponent<MeshFilter>();
if (intMf == null)
intMf = intGo.AddComponent<MeshFilter>();
}
// Copy tm
intGo.transform.position = transForm.position;
intGo.tag = gameObject.tag;
intGo.layer = gameObject.layer;
if (meshRenderer != null)
{
if (intMr == null)
{
intMr = intGo.AddComponent<MeshRenderer>();
intMr.sharedMaterials = meshRenderer.sharedMaterials;
}
}
else if (skinnedMeshRend != null)
{
// TODO skinned mesh renderer support
}
}
// Fragment all meshes into own mesh
public void InteractiveStart()
{
RFFragment.InteractiveStart (this);
}
// Property changed
public void InteractiveChange()
{
RFFragment.InteractiveChange (this);
}
// Create interactively cached fragments
public void InteractiveFragment()
{
// Create new fragments
fragmentsLast = CreateFragments();
// Post create fragments operations
PostFragments();
// Reset original mesh
InteractiveStop();
}
// Revert original mesh
public void InteractiveStop()
{
// Enable own Renderer
OriginalRenderer(true);
// Destroy interactive object
if (intGo != null)
DestroyImmediate (intGo);
// Reset
intGo = null;
intMf = null;
intMr = null;
shatterInt = null;
interactive = false;
}
// Set original renderer state
public void OriginalRenderer(bool state)
{
if (meshRenderer != null)
meshRenderer.enabled = state;
if (skinnedMeshRend != null)
skinnedMeshRend.enabled = state;
}
public void InteractiveReset()
{
if (interactive == true)
{
InteractiveStop();
}
}
// Final preview scale
public float PreviewScale()
{
if (scalePreview == false)
return 1f;
return Mathf.Lerp (1f, 0.3f, previewScale);
}
}
}