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);
}
}
}