using System; using Display; using Playback; using Settings; using UnityEngine; using UnityEngine.Serialization; namespace SMPLModel { /// /// Altered version of loadMoshAnim from MPI. Allows a MoSh animation to be played at any time with a call to PlayAnim, /// rather than running on initialization of the script in the start method (requiring instantiating a new prefab. /// This is setup to handle loading a MoSh animation without prior knowledge of gender, swapping in the correct SMPLConstants model. /// [SelectionBase] public class CharacterComponent : MonoBehaviour, SMPLCharacter { [SerializeField] ModelDefinition model = default; public ModelDefinition Model => model; [SerializeField] Gender gender = default; public Gender Gender => gender; [FormerlySerializedAs("bodyOptions")] [SerializeField] BodySettings bodySettings = default; public BodySettings RenderSettings => bodySettings; [FormerlySerializedAs("characterSetings")] [FormerlySerializedAs("characterDisplayOptions")] [SerializeField] DisplaySettings displaySetings = default; public DisplaySettings DisplaySettings => displaySetings; [SerializeField] MeshCorrection meshCorrection = default; public MeshCorrection MeshCorrection => meshCorrection; public IndividualizedBody Body => amassAnimation?.Body; Mesh originalMesh; AMASSAnimation amassAnimation; SMPLMesh smplMesh; SkinnedMeshRenderer skinnedMeshRenderer; public SkinnedMeshRenderer SkinnedMeshRender => skinnedMeshRenderer; CharacterEvents events; int animationIndex; Transform origin; public CharacterEvents Events => events ?? (events = new CharacterEvents()); void OnEnable() { smplMesh = GetComponentInChildren(); skinnedMeshRenderer = smplMesh.GetComponent(); if (skinnedMeshRenderer == null) throw new NullReferenceException("Can't find skinnedMeshRenderer in awake"); //Create clone of mesh so original is not affected by any of our fiddling originalMesh = skinnedMeshRenderer.sharedMesh; skinnedMeshRenderer.sharedMesh = Instantiate( originalMesh); } void OnDestroy() { skinnedMeshRenderer.sharedMesh = originalMesh; if (this.transform.parent != null) Destroy(transform.parent.gameObject); //Debug.Log("Resetting mesh to previous state on destroy"); } /// /// Rotate the FBX model in case up direction is not Y - axis; /// JL: this seems weird. /// void RotateToUnityCoordinates() { transform.Rotate(-90f, 0f, 0f); } /// /// Sets up and plays a mosh animation. /// public void StartAnimation(AMASSAnimation animationToStart, PlaybackSettings playbackSettings, DisplaySettings characterSettings, BodySettings renderSettings) { bodySettings = renderSettings; displaySetings = characterSettings; amassAnimation = animationToStart; if (model.RotateToUnityCoords) RotateToUnityCoordinates(); gameObject.SetActive(true); amassAnimation.AttachSkin(skinnedMeshRenderer); UpdateAnimation(); SetOffsetsFromIndex(playbackSettings); SetMaterialFromIndex(characterSettings.MeshDisplaySettings); } void SetOffsetsFromIndex(PlaybackSettings playbackSettings) { if (playbackSettings.OffsetMultipleAnimations) { CharacterTranslater translater = GetComponent(); translater.SetPlaybackOptions(playbackSettings); translater.AddOffset(animationIndex); } } void Update() { UpdateAnimation(); } void UpdateAnimation() { if (amassAnimation == null) return; if (amassAnimation.Finished) { StopAnimation(); return; } amassAnimation.PlayCurrentFrame(); } void LateUpdate() { } void StopAnimation() { if (gameObject == null) return; Debug.Log($"\t{gameObject.name}'s Animation Complete"); DestroyCharacter(); } public void InterruptAnimation() { if (this == null || gameObject == null) return; // order of check important here. amassAnimation.End(); //Debug.Log($"{gameObject.animationName}'s Animation Interrupted"); DestroyCharacter(); } public void SetIndex(int animationIndex) { this.animationIndex = animationIndex; } public void SetOrigin(Transform newOrigin) { origin = newOrigin; if (origin != null) { this.transform.parent.SetParent(newOrigin); this.transform.parent.localPosition = Vector3.zero; this.transform.parent.localRotation = Quaternion.identity; } } void SetMaterialFromIndex(MeshDisplaySettings displayOptions) { //Debug.Log($"index: {animationIndex}, count:{displayOptions.OptionalMaterialList.Count}"); if (animationIndex < displayOptions.OptionalMaterialList.Count) { MeshDisplay meshDisplay = GetComponent(); meshDisplay.overwriteMaterial = displayOptions.OptionalMaterialList[animationIndex]; } } void DestroyCharacter() { Destroy(gameObject); } } }