You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

745 lines
38 KiB
C#

3 years ago
#if GPU_INSTANCER
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.Animations;
namespace GPUInstancer.CrowdAnimations
{
public class GPUICrowdAnimationBaker
{
private bool _isBakingAnimation;
private GPUIAnimationBakeData _animationBakeData;
private ComputeShader _skinnedMeshBakeComputeShader;
private int _animationToTextureKernelID;
private AnimatorController _bakerAnimatorController;
private AnimationClip _bakerAnimationClip;
class GPUIAnimationBakeData
{
public GPUICrowdPrototype crowdPrototype;
public int[] textureSize;
public RenderTexture animationRenderTexture;
public GameObject sampleObject;
public Animator sampleAnimator;
public SkinnedMeshRenderer[] sampleSkinnedMeshRenderers;
public int currentClipIndex;
public int currentClipFrame;
public Matrix4x4[] boneMatrices;
public AnimatorOverrideController animatorOverrideController;
public bool hasTransformHierarchy;
public ModelImporter modelImporter;
public bool optimizeGameObjects;
public string[] extraExposedTransformPaths;
public bool isBakeInitialized;
public List<Transform> boneTransforms;
public List<Matrix4x4> bindPoses;
public List<int> bindPosesBoneCount;
}
private Queue<GPUInstancerPrototype> _bakeQueue;
#region SkinnedMesh Bake Methods
public void SkinnedMeshBakeAnimations(List<GPUInstancerPrototype> crowdPrototypes)
{
_bakeQueue = new Queue<GPUInstancerPrototype>(crowdPrototypes);
SkinnedMeshBakeAnimations((GPUICrowdPrototype)_bakeQueue.Dequeue());
}
public void SkinnedMeshBakeAnimations(GPUICrowdPrototype crowdPrototype)
{
GPUInstancerUtility.SetPlatformDependentVariables();
if (crowdPrototype.prefabObject == null || crowdPrototype.prefabObject.GetComponentInChildren<SkinnedMeshRenderer>() == null)
return;
_animationBakeData = new GPUIAnimationBakeData();
_animationBakeData.crowdPrototype = crowdPrototype;
GPUICrowdPrefab gpuiPrefab = crowdPrototype.prefabObject.GetComponent<GPUICrowdPrefab>();
Animator prefabAnimator = gpuiPrefab.GetAnimator();
if (prefabAnimator != null)
_animationBakeData.hasTransformHierarchy = prefabAnimator.hasTransformHierarchy;
else
_animationBakeData.hasTransformHierarchy = true;
if (!_animationBakeData.hasTransformHierarchy)
{
_animationBakeData.modelImporter = (ModelImporter)AssetImporter.GetAtPath(_animationBakeData.crowdPrototype.modelPrefabPath);
_animationBakeData.optimizeGameObjects = _animationBakeData.modelImporter.optimizeGameObjects;
_animationBakeData.extraExposedTransformPaths = _animationBakeData.modelImporter.extraExposedTransformPaths;
if (_animationBakeData.modelImporter.optimizeGameObjects)
{
_animationBakeData.modelImporter.optimizeGameObjects = false;
_animationBakeData.modelImporter.SaveAndReimport();
}
}
EditorApplication.update += AnimationBakerInitialize;
}
public void AnimationBakerInitialize()
{
EditorApplication.update -= AnimationBakerInitialize;
try
{
if (_animationBakeData.crowdPrototype.animationData == null)
GPUICrowdUtility.GenerateCrowdAnimationData(_animationBakeData.crowdPrototype);
_animationBakeData.crowdPrototype.animationData.bakedCAVersion = GPUICrowdEditorConstants.GPUI_CA_VERSION_NO;
_bakerAnimatorController = Resources.Load<AnimatorController>("Editor/BakerAnimator");
_bakerAnimationClip = Resources.Load<AnimationClip>("Editor/BakerAnimation");
_animationBakeData.sampleObject = GetSampleGameObject(_animationBakeData.crowdPrototype, !_animationBakeData.hasTransformHierarchy);
_animationBakeData.sampleObject.transform.localScale = Vector3.one;
_animationBakeData.sampleObject.name += "(Baker)";
_animationBakeData.sampleObject.hideFlags = HideFlags.DontSave;
_animationBakeData.sampleObject.transform.position = Vector3.zero;
_animationBakeData.sampleObject.transform.rotation = Quaternion.identity;
_animationBakeData.sampleSkinnedMeshRenderers = _animationBakeData.sampleObject.GetComponentsInChildren<SkinnedMeshRenderer>(true);
_animationBakeData.sampleAnimator = _animationBakeData.crowdPrototype.hasNoAnimator ?
_animationBakeData.sampleObject.AddComponent<Animator>()
: _animationBakeData.sampleObject.GetComponent<Animator>();
_animationBakeData.sampleAnimator.runtimeAnimatorController = _animationBakeData.crowdPrototype.hasNoAnimator ?
_bakerAnimatorController
: _animationBakeData.crowdPrototype.prefabObject.GetComponent<Animator>().runtimeAnimatorController;
_animationBakeData.sampleAnimator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
//_animationBakeData.crowdPrototype.animationData.applyRootMotion = _animationBakeData.sampleAnimator.applyRootMotion;
_animationBakeData.sampleAnimator.applyRootMotion = true;
_animationBakeData.isBakeInitialized = false;
_animationBakeData.boneTransforms = new List<Transform>();
_animationBakeData.bindPoses = new List<Matrix4x4>();
_animationBakeData.bindPosesBoneCount = new List<int>();
if (_animationBakeData.crowdPrototype.hasNoAnimator && (_animationBakeData.crowdPrototype.clipList == null || _animationBakeData.crowdPrototype.clipList.Count == 0))
throw new Exception("No animation clips found. Please add animation clips to the Clip List.");
}
catch (Exception e)
{
ClearBakingData();
Debug.LogError(e);
return;
}
AnimationBakerStart();
}
public void AnimationBakerStart()
{
try
{
if (EditorUtility.DisplayCancelableProgressBar("GPU Instancer Animation Baker", "Baking animations for GPUI...", 0.1f))
{
ClearBakingData();
return;
}
if (_animationBakeData.sampleAnimator.runtimeAnimatorController == null)
throw new Exception("Can not find Animator Controller on prefab:" + _animationBakeData.crowdPrototype.prefabObject.name);
GPUICrowdAnimationData animationData = _animationBakeData.crowdPrototype.animationData;
if (_animationBakeData.crowdPrototype.clipList == null)
_animationBakeData.crowdPrototype.clipList = new List<AnimationClip>();
_animationBakeData.crowdPrototype.clipList.RemoveAll(c => c == null);
List<AnimationClip> previousClipList = _animationBakeData.crowdPrototype.clipList;
if (!_animationBakeData.crowdPrototype.hasNoAnimator)
{
_animationBakeData.crowdPrototype.clipList.Clear();
AnimationClip[] animationClips = _animationBakeData.sampleAnimator.runtimeAnimatorController.animationClips;
for (int i = 0; i < animationClips.Length; i++)
{
if (!_animationBakeData.crowdPrototype.clipList.Contains(animationClips[i]))
_animationBakeData.crowdPrototype.clipList.Add(animationClips[i]);
}
}
AnimatorController animatorController = null;
if (_animationBakeData.sampleAnimator.runtimeAnimatorController is AnimatorOverrideController)
animatorController = (AnimatorController)((AnimatorOverrideController)_animationBakeData.sampleAnimator.runtimeAnimatorController).runtimeAnimatorController;
else
animatorController = (AnimatorController)_animationBakeData.sampleAnimator.runtimeAnimatorController;
animationData.states = new List<GPUIAnimatorState>();
AddChildStatesFromStateMachine(animationData, animatorController.layers[0].stateMachine, animatorController.layers[0].name);
animationData.totalFrameCount = 0;
#region Collect Skinned Mesh Renderer Data
List<string> optionalSkinnedMeshes = new List<string>();
if (animationData.skinnedMeshDataList == null)
animationData.skinnedMeshDataList = new List<GPUISkinnedMeshData>();
else
{
foreach (GPUISkinnedMeshData smd in animationData.skinnedMeshDataList)
{
if (smd.isOptional)
optionalSkinnedMeshes.Add(smd.transformName);
}
animationData.skinnedMeshDataList.Clear();
}
List<string> dontDestroyBoneNames = new List<string>();
if (animationData.bones == null)
animationData.bones = new List<GPUIBone>();
else
{
foreach (GPUIBone bone in animationData.bones)
{
if (bone.dontDestroy)
dontDestroyBoneNames.Add(bone.boneTransformName);
}
animationData.bones.Clear();
}
// collect bone data from SkinnedMeshRenderers
foreach (SkinnedMeshRenderer skinnedMeshRenderer in _animationBakeData.sampleSkinnedMeshRenderers)
{
if (skinnedMeshRenderer.sharedMesh == null)
{
throw new Exception("Mesh reference is not set for SkinnedMeshRenderer: " + skinnedMeshRenderer.gameObject.name);
}
GPUISkinnedMeshData smd = new GPUISkinnedMeshData
{
transformName = skinnedMeshRenderer.gameObject.name,
boneIndexes = new int[skinnedMeshRenderer.bones.Length],
hasBindPoseOffset = false,
bindPoseOffset = Matrix4x4.identity,
isOptional = optionalSkinnedMeshes.Contains(skinnedMeshRenderer.gameObject.name)
};
for (int b = 0; b < skinnedMeshRenderer.bones.Length; b++)
{
Transform boneTransform = skinnedMeshRenderer.bones[b];
GPUIBone bone = animationData.GetBoneByTransform(boneTransform.name);
if (bone == null)
{
bone = new GPUIBone
{
boneTransformName = boneTransform.name,
boneIndex = animationData.bones.Count,
dontDestroy = dontDestroyBoneNames.Contains(boneTransform.name)
};
animationData.bones.Add(bone);
_animationBakeData.boneTransforms.Add(boneTransform);
_animationBakeData.bindPoses.Add(skinnedMeshRenderer.sharedMesh.bindposes[b]);
_animationBakeData.bindPosesBoneCount.Add(skinnedMeshRenderer.bones.Length);
}
smd.boneIndexes[b] = bone.boneIndex;
if (boneTransform == skinnedMeshRenderer.rootBone)
smd.rootBoneIndex = bone.boneIndex;
if (_animationBakeData.bindPoses[bone.boneIndex] != skinnedMeshRenderer.sharedMesh.bindposes[b])
{
if (_animationBakeData.bindPosesBoneCount[bone.boneIndex] < skinnedMeshRenderer.bones.Length)
{
_animationBakeData.bindPoses[bone.boneIndex] = skinnedMeshRenderer.sharedMesh.bindposes[b];
_animationBakeData.bindPosesBoneCount[bone.boneIndex] = skinnedMeshRenderer.bones.Length;
}
}
}
animationData.skinnedMeshDataList.Add(smd);
}
animationData.bindPoses = _animationBakeData.bindPoses.ToArray();
foreach (SkinnedMeshRenderer skinnedMeshRenderer in _animationBakeData.sampleSkinnedMeshRenderers)
{
GPUISkinnedMeshData smd = animationData.GetSkinnedMeshDataByName(skinnedMeshRenderer.gameObject.name);
if (_animationBakeData.bindPoses[smd.boneIndexes[0]] != skinnedMeshRenderer.sharedMesh.bindposes[0])
{
smd.hasBindPoseOffset = true;
smd.bindPoseOffset = _animationBakeData.bindPoses[smd.boneIndexes[0]].inverse * skinnedMeshRenderer.sharedMesh.bindposes[0];
}
}
// Set bone parent-child relations
int smdIndex = 0;
foreach (SkinnedMeshRenderer skinnedMeshRenderer in _animationBakeData.sampleSkinnedMeshRenderers)
{
GPUISkinnedMeshData smd = animationData.skinnedMeshDataList[smdIndex];
for (int i = 0; i < smd.boneIndexes.Length; i++)
{
GPUIBone bone = animationData.GetBoneByTransform(skinnedMeshRenderer.bones[i].name);
Transform parentTransform = skinnedMeshRenderer.bones[i].parent;
bool foundParent = false;
while (parentTransform != null)
{
GPUIBone parentBone = animationData.GetBoneByTransform(parentTransform.name);
if (parentBone != null)
{
foundParent = true;
animationData.BoneSetParent(bone, parentBone);
break;
}
parentTransform = parentTransform.parent;
}
if (!foundParent)
{
bone.isRoot = true;
}
}
smdIndex++;
}
animationData.totalBoneCount = animationData.bones.Count;
if (animationData.totalBoneCount == 0)
{
Debug.LogError("GPUI can not find any bone data for: " + _animationBakeData.crowdPrototype.prefabObject.name);
ClearBakingData();
return;
}
#endregion Collect Skinned Mesh Renderer Data
#region Collect Animation Clip Data
List<GPUIAnimationClipData> previousClipDataList = animationData.clipDataList;
animationData.clipDataList = new List<GPUIAnimationClipData>();
foreach (AnimationClip animationClip in _animationBakeData.crowdPrototype.clipList)
{
if (animationData.clipDataList.Exists(cd => cd.GetHashCode() == animationClip.GetHashCode()))
continue;
int frameCount = Mathf.CeilToInt(animationClip.length * _animationBakeData.crowdPrototype.frameRate + 1);
int isLoopDisabled = animationClip.isLooping ? 0 : 1;
if (previousClipDataList != null && previousClipList != null)
{
int previousClipIndex = previousClipList.IndexOf(animationClip);
if (previousClipIndex >= 0 && previousClipDataList.Count > previousClipIndex)
isLoopDisabled = previousClipDataList[previousClipIndex].isLoopDisabled;
}
GPUIAnimationClipData acd = new GPUIAnimationClipData
{
clipIndex = animationData.clipDataList.Count,
clipStartFrame = animationData.totalFrameCount,
clipFrameCount = frameCount,
hasRootMotion = 0,
length = (frameCount - 1.0f) / _animationBakeData.crowdPrototype.frameRate,
isLoopDisabled = isLoopDisabled,
isClipLooping = animationClip.isLooping ? 1 : 0
};
animationData.totalFrameCount += acd.clipFrameCount;
animationData.clipDataList.Add(acd);
}
#endregion Collect Animation Clip Data
#region Calculate Texture Size
int totalData = animationData.totalBoneCount * animationData.totalFrameCount * 4;
animationData.textureSizeX =
Mathf.NextPowerOfTwo(Mathf.CeilToInt(Mathf.Sqrt(totalData)));
animationData.textureSizeY =
Mathf.NextPowerOfTwo(Mathf.CeilToInt(totalData / (float)animationData.textureSizeX));
_animationBakeData.textureSize = new int[2] { animationData.textureSizeX, animationData.textureSizeY };
if (animationData.textureSizeX > 8192 || animationData.textureSizeY > 8192)
{
throw new Exception("Bone and animation frame amount is too high.");
}
#endregion Calculate Texture Size
#region Create Render Texture
_animationBakeData.animationRenderTexture = new RenderTexture(animationData.textureSizeX, animationData.textureSizeY, 0, RenderTextureFormat.ARGBHalf)
{
filterMode = FilterMode.Point,
enableRandomWrite = true,
useMipMap = false,
autoGenerateMips = false
};
_animationBakeData.animationRenderTexture.Create();
#endregion Crete Render Texture
_isBakingAnimation = true;
_animationBakeData.currentClipIndex = 0;
_animationBakeData.currentClipFrame = 0;
_animationBakeData.boneMatrices = new Matrix4x4[animationData.totalBoneCount * animationData.totalFrameCount];
animationData.rootMotions = new GPUIRootMotion[animationData.totalFrameCount];
_animationBakeData.sampleAnimator.runtimeAnimatorController = _bakerAnimatorController;
EditorApplication.update += AnimationBakerUpdate;
}
catch (Exception e)
{
ClearBakingData();
Debug.LogError(e);
}
}
public void OverrideAnimator(AnimationClip animationClip)
{
if (_animationBakeData.animatorOverrideController == null)
_animationBakeData.animatorOverrideController = new AnimatorOverrideController(_animationBakeData.sampleAnimator.runtimeAnimatorController);
_animationBakeData.animatorOverrideController[_bakerAnimationClip] = animationClip;
_animationBakeData.sampleAnimator.runtimeAnimatorController = _animationBakeData.animatorOverrideController;
}
public void AnimationBakerUpdate()
{
if (!_isBakingAnimation)
{
ClearBakingData();
return;
}
try
{
GPUICrowdAnimationData animationData = _animationBakeData.crowdPrototype.animationData;
if (animationData.clipDataList.Count == 0)
{
throw new Exception("No animation clips found.");
}
do
{
GPUIAnimationClipData animationClipData = animationData.clipDataList[_animationBakeData.currentClipIndex];
if (EditorUtility.DisplayCancelableProgressBar("GPU Instancer Animation Baker", "Frame No: " + _animationBakeData.currentClipFrame + "/" + animationClipData.clipFrameCount
+ " Clip: " + (_animationBakeData.currentClipIndex + 1) + "/" + _animationBakeData.crowdPrototype.animationData.clipDataList.Count + " (" + _animationBakeData.crowdPrototype.clipList[animationClipData.clipIndex].name + ")",
0.1f + ((_animationBakeData.currentClipFrame + animationClipData.clipStartFrame) / (float)_animationBakeData.crowdPrototype.animationData.totalFrameCount) * 0.8f))
{
ClearBakingData();
return;
}
#region Initialize Sample Object
if (!_animationBakeData.isBakeInitialized)
{
_animationBakeData.isBakeInitialized = true;
OverrideAnimator(_animationBakeData.crowdPrototype.clipList[animationClipData.clipIndex]);
_animationBakeData.sampleAnimator.Update(0);
//animationClipData.animationClip.SampleAnimation(_animationBakeData.sampleObject, 0);
return;
}
#endregion Initialize Sample Object
Matrix4x4 motionMatrix = _animationBakeData.sampleObject.transform.localToWorldMatrix;
GPUIRootMotion rootMotion = new GPUIRootMotion()
{
hasMotion = motionMatrix != Matrix4x4.identity ? 1 : 0,
isPositionOnly = _animationBakeData.sampleObject.transform.rotation == Quaternion.identity ? 1 : 0,
motionMatrix = motionMatrix
};
animationData.rootMotions[_animationBakeData.currentClipFrame + animationClipData.clipStartFrame] = rootMotion;
if (rootMotion.HasMotion() && !animationClipData.HasRootMotion())
{
animationClipData.hasRootMotion = 1;
animationData.clipDataList[_animationBakeData.currentClipIndex] = animationClipData;
}
_animationBakeData.sampleObject.transform.position = Vector3.zero;
_animationBakeData.sampleObject.transform.rotation = Quaternion.identity;
foreach (Transform boneTransform in _animationBakeData.boneTransforms)
{
int boneIndex = animationData.GetBoneIndexByTransform(boneTransform.name);
// ordered by frames (all bone data of first frame then all bone data of second frame)
int index = (_animationBakeData.currentClipFrame + animationClipData.clipStartFrame) * animationData.totalBoneCount
+ boneIndex;
_animationBakeData.boneMatrices[index] = boneTransform.localToWorldMatrix * _animationBakeData.bindPoses[boneIndex];
}
#region Move To Next Frame
_animationBakeData.currentClipFrame++;
if (animationClipData.clipFrameCount == _animationBakeData.currentClipFrame)
{
_animationBakeData.currentClipIndex++;
_animationBakeData.currentClipFrame = 0;
}
if (_animationBakeData.currentClipIndex == _animationBakeData.crowdPrototype.animationData.clipDataList.Count)
{
EditorApplication.update -= AnimationBakerUpdate;
AnimationBakerFinish();
return;
}
GPUIAnimationClipData nextAnimationClip = animationData.clipDataList[_animationBakeData.currentClipIndex];
if (animationClipData.clipIndex != nextAnimationClip.clipIndex)
{
OverrideAnimator(_animationBakeData.crowdPrototype.clipList[nextAnimationClip.clipIndex]);
_animationBakeData.sampleAnimator.Play(_animationBakeData.sampleAnimator.GetCurrentAnimatorStateInfo(0).shortNameHash, 0, 0);
_animationBakeData.sampleAnimator.Update(0);
return;
}
_animationBakeData.sampleAnimator.Update(nextAnimationClip.length / nextAnimationClip.clipFrameCount);
#endregion Move To Next Frame
}
while (!GPUICrowdConstants.gpuiCrowdSettings.bakeUpdatePerFrame);
}
catch (Exception e)
{
ClearBakingData();
Debug.LogError(e);
}
}
public void AnimationBakerFinish()
{
try
{
#region Write To Render Texture With Compute Shader
if (_skinnedMeshBakeComputeShader == null)
{
_skinnedMeshBakeComputeShader = (ComputeShader)Resources.Load(GPUICrowdConstants.COMPUTE_SKINNED_MESH_BAKE_PATH);
_animationToTextureKernelID = _skinnedMeshBakeComputeShader.FindKernel(GPUICrowdConstants.COMPUTE_ANIMATION_TO_TEXTURE_KERNEL);
}
ComputeBuffer boneBuffer = new ComputeBuffer(_animationBakeData.boneMatrices.Length, GPUInstancerConstants.STRIDE_SIZE_MATRIX4X4);
boneBuffer.SetData(_animationBakeData.boneMatrices);
_skinnedMeshBakeComputeShader.SetTexture(_animationToTextureKernelID, "outputTexture", _animationBakeData.animationRenderTexture);
_skinnedMeshBakeComputeShader.SetBuffer(_animationToTextureKernelID, "boneData", boneBuffer);
_skinnedMeshBakeComputeShader.SetInt("dataCount", _animationBakeData.boneMatrices.Length);
_skinnedMeshBakeComputeShader.SetInts("textureSize", _animationBakeData.textureSize);
_skinnedMeshBakeComputeShader.Dispatch(_animationToTextureKernelID,
Mathf.CeilToInt(_animationBakeData.boneMatrices.Length / GPUInstancerConstants.COMPUTE_SHADER_THREAD_COUNT), 1, 1);
boneBuffer.Release();
#endregion Write To Render Texture With Compute Shader
#region Write Render Texture To Texture2D
if(EditorUtility.DisplayCancelableProgressBar("GPU Instancer Animaton Baker", "Writing to Texture...", 0.91f))
{
ClearBakingData();
return;
}
string texturePath = null;
if (_animationBakeData.crowdPrototype.animationData.animationTexture != null)
texturePath = AssetDatabase.GetAssetPath(_animationBakeData.crowdPrototype.animationData.animationTexture);
_animationBakeData.crowdPrototype.animationData.animationTexture = new Texture2D(
_animationBakeData.crowdPrototype.animationData.textureSizeX,
_animationBakeData.crowdPrototype.animationData.textureSizeY,
TextureFormat.RGBAHalf, false);
Rect readingRect = new Rect(0, 0,
_animationBakeData.crowdPrototype.animationData.textureSizeX,
_animationBakeData.crowdPrototype.animationData.textureSizeY);
RenderTexture.active = _animationBakeData.animationRenderTexture;
_animationBakeData.crowdPrototype.animationData.animationTexture.ReadPixels(readingRect, 0, 0);
_animationBakeData.crowdPrototype.animationData.animationTexture.Apply();
RenderTexture.active = null;
_animationBakeData.animationRenderTexture.Release();
//string texturePath = "Assets/" + prefabPrototype.prefabObject.name + "_AnimationData.png";
//byte[] bytes = prefabPrototype.animationData.animationTexture.EncodeToPNG();
//System.IO.File.WriteAllBytes(texturePath, bytes);
//AssetDatabase.SaveAssets();
//AssetDatabase.Refresh();
//// allow for larger textures if necessary:
//TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(texturePath);
//importer.maxTextureSize = prefabPrototype.animationData.textureSizeX;
//AssetDatabase.ImportAsset(texturePath);
if (string.IsNullOrEmpty(texturePath))
{
texturePath = GPUInstancerConstants.GetDefaultPath() + GPUICrowdConstants.PROTOTYPES_ANIMATION_TEXTURES_PATH +
_animationBakeData.crowdPrototype.name + "_AnimationData.asset";
if (!System.IO.Directory.Exists(GPUInstancerConstants.GetDefaultPath() + GPUICrowdConstants.PROTOTYPES_ANIMATION_TEXTURES_PATH))
System.IO.Directory.CreateDirectory(GPUInstancerConstants.GetDefaultPath() + GPUICrowdConstants.PROTOTYPES_ANIMATION_TEXTURES_PATH);
}
AssetDatabase.CreateAsset(_animationBakeData.crowdPrototype.animationData.animationTexture, texturePath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
_animationBakeData.crowdPrototype.animationData.animationTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(texturePath);
EditorUtility.SetDirty(_animationBakeData.crowdPrototype.animationData);
EditorUtility.ClearProgressBar();
#endregion Write Render Texture To Texture2D
}
catch (Exception e)
{
ClearBakingData();
Debug.LogError(e);
}
ClearBakingData();
if (_bakeQueue != null)
{
if (_bakeQueue.Count > 0)
SkinnedMeshBakeAnimations((GPUICrowdPrototype)_bakeQueue.Dequeue());
else
_bakeQueue = null;
}
}
public void ClearBakingData()
{
EditorApplication.update -= AnimationBakerUpdate;
EditorUtility.ClearProgressBar();
if (_animationBakeData != null)
{
if (_animationBakeData.sampleObject)
GameObject.DestroyImmediate(_animationBakeData.sampleObject);
if (!_animationBakeData.hasTransformHierarchy && _animationBakeData.optimizeGameObjects)
{
_animationBakeData.modelImporter.optimizeGameObjects = true;
_animationBakeData.modelImporter.extraExposedTransformPaths = _animationBakeData.extraExposedTransformPaths;
_animationBakeData.modelImporter.SaveAndReimport();
}
}
_isBakingAnimation = false;
_animationBakeData = null;
}
public GameObject GetSampleGameObject(GPUICrowdPrototype crowdPrototype, bool fromModel = false, int lodIndex = 0)
{
if (fromModel)
{
return GameObject.Instantiate(AssetDatabase.LoadAssetAtPath<GameObject>(crowdPrototype.modelPrefabPath));
}
//else
//{
// LODGroup lodGroup = crowdPrototype.prefabObject.GetComponent<LODGroup>();
// if (lodGroup != null)
// {
// LOD[] lods = lodGroup.GetLODs();
// if (lods.Length > lodIndex)
// {
// GameObject lodGO = lods[lodIndex].renderers.FirstOrDefault(r => r != null && r is SkinnedMeshRenderer).gameObject;
// while (lodGO.transform.parent.gameObject != crowdPrototype.prefabObject)
// {
// lodGO = lodGO.transform.parent.gameObject;
// if (lodGO == null)
// return null;
// }
// return GameObject.Instantiate(lodGO);
// }
// return null;
// }
else
{
return GameObject.Instantiate(crowdPrototype.prefabObject);
}
//}
}
private void AddChildStatesFromStateMachine(GPUICrowdAnimationData animationData, AnimatorStateMachine stateMachine, string parentPath)
{
foreach (ChildAnimatorState childAnimatorState in stateMachine.states)
{
GPUIAnimatorState state = new GPUIAnimatorState();
state.fullPathName = parentPath + "." + childAnimatorState.state.name;
state.hashCode = Animator.StringToHash(state.fullPathName);
state.isBlend = childAnimatorState.state.motion is BlendTree;
state.cycleOffset = childAnimatorState.state.cycleOffset;
animationData.states.Add(state);
}
foreach (ChildAnimatorStateMachine casm in stateMachine.stateMachines)
{
AddChildStatesFromStateMachine(animationData, casm.stateMachine, parentPath + "." + casm.stateMachine.name);
}
}
#endregion SkinnedMesh Bake Methods
public void GenerateTestInstance(GPUICrowdPrototype prototype)
{
GameObject generatedGO = new GameObject(prototype.prefabObject.name + "(Debugger)");
generatedGO.hideFlags = HideFlags.DontSave;
GameObject renderersGO = new GameObject("Renderers");
renderersGO.hideFlags = HideFlags.DontSave;
renderersGO.transform.SetParent(generatedGO.transform);
SkinnedMeshRenderer[] skinnedMeshRenderers = prototype.prefabObject.GetComponentsInChildren<SkinnedMeshRenderer>(true);
LODGroup lodGroup = prototype.prefabObject.GetComponent<LODGroup>();
if (lodGroup != null)
{
Renderer[] lod0Renderers = lodGroup.GetLODs()[0].renderers;
List<SkinnedMeshRenderer> lod0SkinnedMeshRenderers = new List<SkinnedMeshRenderer>();
foreach (Renderer renderer in lod0Renderers)
{
if (renderer is SkinnedMeshRenderer)
lod0SkinnedMeshRenderers.Add((SkinnedMeshRenderer)renderer);
}
skinnedMeshRenderers = lod0SkinnedMeshRenderers.ToArray();
}
List<Material> replacementMaterials = new List<Material>();
Material originalMaterial;
int s = 0;
List<Mesh> generatedMeshList = new List<Mesh>();
foreach (SkinnedMeshRenderer smr in skinnedMeshRenderers)
{
GPUISkinnedMeshData smd = prototype.animationData.skinnedMeshDataList[s];
Material[] newMaterials = new Material[smr.sharedMaterials.Length];
for (int i = 0; i < smr.sharedMaterials.Length; i++)
{
originalMaterial = smr.sharedMaterials[i];
Material newMaterial = new Material(GPUInstancerConstants.gpuiSettings.shaderBindings.GetInstancedShader(originalMaterial.shader.name, GPUICrowdConstants.GPUI_EXTENSION_CODE));
newMaterial.CopyPropertiesFromMaterial(originalMaterial);
newMaterial.name = originalMaterial.name + "_Test";
newMaterial.SetTexture("gpuiAnimationTexture", prototype.animationData.animationTexture);
newMaterial.SetVector("animationTextureSize", new Vector2(prototype.animationData.textureSizeX, prototype.animationData.textureSizeY));
newMaterial.SetFloat("totalNumberOfBones", prototype.animationData.totalBoneCount);
newMaterial.SetMatrix("bindPoseOffset", smd.hasBindPoseOffset ? smd.bindPoseOffset : Matrix4x4.identity);
newMaterial.EnableKeyword("GPUI_CA_TEXTURE");
replacementMaterials.Add(newMaterial);
newMaterials[i] = newMaterial;
newMaterial.hideFlags = HideFlags.DontSave;
}
GameObject meshRendererGO = new GameObject(smr.gameObject.name);
meshRendererGO.hideFlags = HideFlags.DontSave;
meshRendererGO.transform.SetParent(renderersGO.transform);
MeshRenderer mr = meshRendererGO.AddComponent<MeshRenderer>();
MeshFilter mf = meshRendererGO.AddComponent<MeshFilter>();
mr.sharedMaterials = newMaterials;
Mesh mesh = GPUICrowdUtility.GenerateMeshWithUVs(smr.sharedMesh, smd);
mf.sharedMesh = mesh;
generatedMeshList.Add(mesh);
mr.gameObject.transform.position = Vector3.zero;
mr.gameObject.transform.rotation = Quaternion.identity;
mr.gameObject.transform.localScale = Vector3.one;
s++;
}
GPUICrowdPrefabDebugger debugger = generatedGO.AddComponent<GPUICrowdPrefabDebugger>();
debugger.crowdPrototype = prototype;
debugger.testMaterials = replacementMaterials;
debugger.frameIndex = 0;
Selection.activeGameObject = generatedGO;
SceneView.lastActiveSceneView.FrameSelected();
foreach (Mesh m in generatedMeshList)
{
// scaling mesh bounds so that they are not frustum culled
m.bounds = new Bounds(m.bounds.center, m.bounds.size * 20);
}
}
}
}
#endif //GPU_INSTANCER