cleanup & documentation
This commit is contained in:
parent
837c5ce807
commit
fc581a0dd8
@ -1,51 +1,54 @@
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.SceneManagement;
|
using UnityEditor.SceneManagement;
|
||||||
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Passer.CreatureControl {
|
namespace Passer.CreatureControl {
|
||||||
|
|
||||||
[CustomEditor(typeof(Creature), true)]
|
[CustomEditor(typeof(Creature), true)]
|
||||||
public class Creature_Editor : Editor {
|
public class Creature_Editor : Editor {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The creature managed by this editor
|
||||||
|
/// </summary>
|
||||||
protected Creature creature;
|
protected Creature creature;
|
||||||
|
|
||||||
#region Init
|
#region Start
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enable the creature editor
|
||||||
|
/// </summary>
|
||||||
public virtual void OnEnable() {
|
public virtual void OnEnable() {
|
||||||
creature = target as Creature;
|
this.creature = target as Creature;
|
||||||
|
|
||||||
|
// Keep track if anything changed while enabling the creature editor
|
||||||
bool anythingChanged = false;
|
bool anythingChanged = false;
|
||||||
|
|
||||||
if (!IsPrefab(creature)) {
|
if (IsPrefab(this.creature) == false) {
|
||||||
anythingChanged |= creature.CheckTargetRig();
|
// Only do this when it is not a prefab
|
||||||
anythingChanged |= creature.CheckModel();
|
anythingChanged |= this.creature.CheckTargetRig();
|
||||||
|
anythingChanged |= this.creature.CheckModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
creature.targetRig.MatchTo(creature, ref anythingChanged);
|
this.creature.targetRig.MatchTo(this.creature, ref anythingChanged);
|
||||||
|
|
||||||
|
// As the above functions do not use the serialized object
|
||||||
|
// We need to manually persist the changes.
|
||||||
if (anythingChanged) {
|
if (anythingChanged) {
|
||||||
EditorUtility.SetDirty(creature);
|
EditorUtility.SetDirty(this.creature);
|
||||||
AssetDatabase.SaveAssets();
|
AssetDatabase.SaveAssets();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the given creature is a prefab
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="creature">The creature to check</param>
|
||||||
|
/// <returns>True when it is a prefab</returns>
|
||||||
public static bool IsPrefab(Creature creature) {
|
public static bool IsPrefab(Creature creature) {
|
||||||
PrefabStage prefabStage = PrefabStageUtility.GetPrefabStage(creature.gameObject);
|
PrefabStage prefabStage = PrefabStageUtility.GetPrefabStage(creature.gameObject);
|
||||||
return prefabStage != null;
|
return prefabStage != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Init
|
#endregion Start
|
||||||
|
|
||||||
#region Scene
|
|
||||||
|
|
||||||
public virtual void OnSceneGUI() {
|
|
||||||
if (Application.isPlaying || creature.enabled == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
creature.UpdatePose();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Scene
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,15 +3,24 @@ using UnityEngine;
|
|||||||
namespace Passer.CreatureControl {
|
namespace Passer.CreatureControl {
|
||||||
|
|
||||||
public class Creature : MonoBehaviour {
|
public class Creature : MonoBehaviour {
|
||||||
|
/// <summary>
|
||||||
|
/// The (hopefully rigged) 3D model of the creature
|
||||||
|
/// </summary>
|
||||||
|
public Transform model;
|
||||||
|
|
||||||
/// <summary>The target bones rig</summary>
|
/// <summary>The target bones rig</summary>
|
||||||
/// The target bones rig contain the target pose of the creature
|
/// The target bones rig contain the target pose of the creature
|
||||||
/// The creature movements will try to move the creature such that the target pose is reached
|
/// The creature movements will try to move the creature such that the target pose is reached
|
||||||
/// as closely as possible
|
/// as closely as possible
|
||||||
public Transform model;
|
|
||||||
|
|
||||||
public TargetRig targetRig;
|
public TargetRig targetRig;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The positional different between the target rig and model root
|
||||||
|
/// </summary>
|
||||||
public Vector3 targetToModelTranslation;
|
public Vector3 targetToModelTranslation;
|
||||||
|
/// <summary>
|
||||||
|
/// The rotational difference between the target rig and the model root
|
||||||
|
/// </summary>
|
||||||
public Quaternion targetToModelRotation;
|
public Quaternion targetToModelRotation;
|
||||||
|
|
||||||
#region Init
|
#region Init
|
||||||
@ -20,27 +29,32 @@ namespace Passer.CreatureControl {
|
|||||||
/// Ensure a target rig is available
|
/// Ensure a target rig is available
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="targetRigResourceName">The name of the target rig resource</param>
|
/// <param name="targetRigResourceName">The name of the target rig resource</param>
|
||||||
/// <returns>True when the creature rig has been updated</returns>
|
/// <returns>True when the target rig has been updated</returns>
|
||||||
/// The parameter is used to instantiate a new target rig when none has been found.
|
/// The parameter is used to instantiate a new target rig when none has been found.
|
||||||
public bool CheckTargetRig(string targetRigResourceName) {
|
public bool CheckTargetRig(string targetRigResourceName) {
|
||||||
if (this.targetRig == null) {
|
if (this.targetRig != null)
|
||||||
// See if there is a target rig, but we just haven't found it
|
|
||||||
this.targetRig = this.GetComponentInChildren<InsectRig>();
|
|
||||||
if (this.targetRig == null) {
|
|
||||||
GameObject targetsRigPrefab = Resources.Load<GameObject>(targetRigResourceName);
|
|
||||||
GameObject targetRig = Instantiate(targetsRigPrefab);
|
|
||||||
targetRig.name = "Target Rig";
|
|
||||||
|
|
||||||
targetRig.transform.SetPositionAndRotation(this.transform.position, this.transform.rotation);
|
|
||||||
targetRig.transform.SetParent(this.transform);
|
|
||||||
this.targetRig = targetRig.GetComponent<InsectRig>();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// See if there is a target rig, but we just haven't found it
|
||||||
|
this.targetRig = this.GetComponentInChildren<TargetRig>();
|
||||||
|
if (this.targetRig == null) {
|
||||||
|
// Nope, there is no target rig, so instantiate it using the given resource name
|
||||||
|
GameObject targetsRigPrefab = Resources.Load<GameObject>(targetRigResourceName);
|
||||||
|
GameObject targetRig = Instantiate(targetsRigPrefab);
|
||||||
|
targetRig.name = "Target Rig";
|
||||||
|
|
||||||
|
targetRig.transform.SetPositionAndRotation(this.transform.position, this.transform.rotation);
|
||||||
|
targetRig.transform.SetParent(this.transform);
|
||||||
|
this.targetRig = targetRig.GetComponent<TargetRig>();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure a target rig is available
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True when the target rig has been updated</returns>
|
||||||
|
/// This tries to instantiate the default target rig resource
|
||||||
public virtual bool CheckTargetRig() {
|
public virtual bool CheckTargetRig() {
|
||||||
return CheckTargetRig("TargetRig");
|
return CheckTargetRig("TargetRig");
|
||||||
}
|
}
|
||||||
@ -50,25 +64,30 @@ namespace Passer.CreatureControl {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True when the creature rig has been updated</returns>
|
/// <returns>True when the creature rig has been updated</returns>
|
||||||
public bool CheckModel() {
|
public bool CheckModel() {
|
||||||
if (this.model == null) {
|
if (this.model != null)
|
||||||
SkinnedMeshRenderer[] skinnedMeshRenderers = this.GetComponentsInChildren<SkinnedMeshRenderer>();
|
|
||||||
foreach (SkinnedMeshRenderer skinnedMeshRenderer in skinnedMeshRenderers) {
|
|
||||||
Transform rendererParent = skinnedMeshRenderer.transform.parent;
|
|
||||||
if (this.model == null || this.model == rendererParent)
|
|
||||||
this.model = rendererParent;
|
|
||||||
else
|
|
||||||
Debug.LogWarning("Unclear model root");
|
|
||||||
}
|
|
||||||
return this.model != null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// We determine the model root as the parent of the renderers
|
||||||
|
SkinnedMeshRenderer[] skinnedMeshRenderers = this.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||||
|
foreach (SkinnedMeshRenderer skinnedMeshRenderer in skinnedMeshRenderers) {
|
||||||
|
Transform rendererParent = skinnedMeshRenderer.transform.parent;
|
||||||
|
if (this.model == null || this.model == rendererParent)
|
||||||
|
this.model = rendererParent;
|
||||||
|
else
|
||||||
|
// Oops! There are multiple renders with different parents....
|
||||||
|
Debug.LogWarning("Unclear model root");
|
||||||
|
// We still return a model root, but this may not be the correct one...
|
||||||
|
}
|
||||||
|
return this.model != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Init
|
#endregion Init
|
||||||
|
|
||||||
#region Start
|
#region Start
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start the creature
|
||||||
|
/// </summary>
|
||||||
protected virtual void Start() {
|
protected virtual void Start() {
|
||||||
this.CheckTargetRig();
|
this.CheckTargetRig();
|
||||||
this.CheckModel();
|
this.CheckModel();
|
||||||
@ -79,28 +98,38 @@ namespace Passer.CreatureControl {
|
|||||||
|
|
||||||
#region Update
|
#region Update
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the creature
|
||||||
|
/// </summary>
|
||||||
public virtual void Update() {
|
public virtual void Update() {
|
||||||
if (this.targetRig == null)
|
if (this.targetRig == null)
|
||||||
|
// Without a target rig, the creature cannot move
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UpdatePose();
|
UpdatePose();
|
||||||
|
|
||||||
// copy animator root motion to the humanoid
|
// copy animator root motion to the creature
|
||||||
this.transform.SetPositionAndRotation(targetRig.transform.position, targetRig.transform.rotation);
|
this.transform.SetPositionAndRotation(targetRig.transform.position, targetRig.transform.rotation);
|
||||||
// As target rig is probably a child of this.transform,
|
// As target rig is probably a child of this.transform,
|
||||||
// We need to restore the position/rotation of the targetsRig.
|
// We need to restore the position/rotation of the targetsRig.
|
||||||
targetRig.transform.SetPositionAndRotation(this.transform.position, this.transform.rotation);
|
targetRig.transform.SetPositionAndRotation(this.transform.position, this.transform.rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the pose of the creature using the target rig
|
||||||
|
/// </summary>
|
||||||
public void UpdatePose() {
|
public void UpdatePose() {
|
||||||
if (this.targetRig == null)
|
if (this.targetRig == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.targetRig.PoseLimbs();
|
this.targetRig.Pose();
|
||||||
UpdateBones();
|
UpdateModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void UpdateBones() {
|
/// <summary>
|
||||||
|
/// Update the bones of the creature's rig from the target rig pose
|
||||||
|
/// </summary>
|
||||||
|
public virtual void UpdateModel() {
|
||||||
Vector3 newPosition = this.targetRig.transform.position + this.targetToModelTranslation;
|
Vector3 newPosition = this.targetRig.transform.position + this.targetToModelTranslation;
|
||||||
Quaternion newOrientation = this.targetRig.transform.rotation * this.targetToModelRotation;
|
Quaternion newOrientation = this.targetRig.transform.rotation * this.targetToModelRotation;
|
||||||
this.model.SetPositionAndRotation(newPosition, newOrientation);
|
this.model.SetPositionAndRotation(newPosition, newOrientation);
|
||||||
@ -108,12 +137,19 @@ namespace Passer.CreatureControl {
|
|||||||
|
|
||||||
#endregion Update
|
#endregion Update
|
||||||
|
|
||||||
|
#region Scene view
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the pose of the creature when the application is not running
|
||||||
|
/// </summary>
|
||||||
void OnDrawGizmos() {
|
void OnDrawGizmos() {
|
||||||
// This ensures that the model is always following the target rig
|
// This ensures that the model is always following the target rig
|
||||||
if (Application.isPlaying == false) {
|
if (Application.isPlaying == false) {
|
||||||
this.UpdatePose();
|
this.UpdatePose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion Scene view
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -31,8 +31,8 @@ namespace Passer.CreatureControl {
|
|||||||
|
|
||||||
#region Update
|
#region Update
|
||||||
|
|
||||||
public override void UpdateBones() {
|
public override void UpdateModel() {
|
||||||
base.UpdateBones();
|
base.UpdateModel();
|
||||||
|
|
||||||
if (this.insectRig.leftFrontLeg != null)
|
if (this.insectRig.leftFrontLeg != null)
|
||||||
this.insectRig.leftFrontLeg.UpdateBones(this.leftFrontLeg);
|
this.insectRig.leftFrontLeg.UpdateBones(this.leftFrontLeg);
|
||||||
|
|||||||
@ -12,7 +12,7 @@ namespace Passer.CreatureControl {
|
|||||||
public TargetLeg rightMiddleLeg;
|
public TargetLeg rightMiddleLeg;
|
||||||
public TargetLeg rightBackLeg;
|
public TargetLeg rightBackLeg;
|
||||||
|
|
||||||
public override void PoseLimbs() {
|
public override void Pose() {
|
||||||
this.leftBackLeg.PoseLimb();
|
this.leftBackLeg.PoseLimb();
|
||||||
this.leftMiddleLeg.PoseLimb();
|
this.leftMiddleLeg.PoseLimb();
|
||||||
this.leftFrontLeg.PoseLimb();
|
this.leftFrontLeg.PoseLimb();
|
||||||
|
|||||||
@ -14,6 +14,19 @@ namespace Passer.CreatureControl {
|
|||||||
public Quaternion targetToBoneFemur;
|
public Quaternion targetToBoneFemur;
|
||||||
public Quaternion targetToBoneTibia;
|
public Quaternion targetToBoneTibia;
|
||||||
|
|
||||||
|
public float femurLength;
|
||||||
|
public float tibiaLength;
|
||||||
|
public float length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the lenghts of the leg bones
|
||||||
|
/// </summary>
|
||||||
|
private void CalculateLengths() {
|
||||||
|
this.femurLength = Vector3.Distance(this.femurTarget.position, this.tibiaTarget.position);
|
||||||
|
this.tibiaLength = Vector3.Distance(this.tibiaTarget.position, this.tarsusTarget.position);
|
||||||
|
this.length = femurLength + tibiaLength;
|
||||||
|
}
|
||||||
|
|
||||||
public void MatchTo(Leg leg) {
|
public void MatchTo(Leg leg) {
|
||||||
this.femurTarget.position = leg.femur.position;
|
this.femurTarget.position = leg.femur.position;
|
||||||
this.tibiaTarget.position = leg.tibia.position;
|
this.tibiaTarget.position = leg.tibia.position;
|
||||||
@ -21,15 +34,13 @@ namespace Passer.CreatureControl {
|
|||||||
targetToBoneFemur = TargetRig.TargetToBoneRotation(leg.femur, leg.tibia);
|
targetToBoneFemur = TargetRig.TargetToBoneRotation(leg.femur, leg.tibia);
|
||||||
targetToBoneTibia = TargetRig.TargetToBoneRotation(leg.tibia, leg.tarsus);
|
targetToBoneTibia = TargetRig.TargetToBoneRotation(leg.tibia, leg.tarsus);
|
||||||
|
|
||||||
float femurLength = Vector3.Distance(this.femurTarget.position, this.tibiaTarget.position);
|
CalculateLengths();
|
||||||
float tibiaLength = Vector3.Distance(this.tibiaTarget.position, this.tarsusTarget.position);
|
|
||||||
float leglength = femurLength + tibiaLength;
|
|
||||||
|
|
||||||
|
// Put the end-effector target for IK in a sensible place
|
||||||
Vector3 legDirection = (this.tarsusTarget.position - this.femurTarget.position).normalized;
|
Vector3 legDirection = (this.tarsusTarget.position - this.femurTarget.position).normalized;
|
||||||
this.target.position = this.femurTarget.position + 0.7f * leglength * legDirection.normalized;
|
Vector3 targetPosition = this.femurTarget.position + 0.7f * this.length * legDirection.normalized;
|
||||||
this.target.rotation = Quaternion.LookRotation(legDirection);
|
Quaternion targetRotation = Quaternion.LookRotation(legDirection);
|
||||||
|
this.target.SetPositionAndRotation(targetPosition, targetRotation);
|
||||||
//this.target.SetPositionAndRotation(this.tarsusTarget.position, this.tarsusTarget.rotation);
|
|
||||||
this.target.localPosition = new(this.target.localPosition.x, 0, this.target.localPosition.z);
|
this.target.localPosition = new(this.target.localPosition.x, 0, this.target.localPosition.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,14 +9,26 @@ namespace Passer.CreatureControl {
|
|||||||
|
|
||||||
public Animator animator;
|
public Animator animator;
|
||||||
|
|
||||||
public virtual void PoseLimbs() {
|
/// <summary>
|
||||||
|
/// Pose the target rig using the IK targets
|
||||||
|
/// </summary>
|
||||||
|
public virtual void Pose() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Align the target rig with a creature
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="creature">The creature to align to</param>
|
||||||
public void MatchTo(Creature creature) {
|
public void MatchTo(Creature creature) {
|
||||||
bool anythingChangedDummy = false;
|
bool anythingChangedDummy = false;
|
||||||
MatchTo(creature, ref anythingChangedDummy);
|
MatchTo(creature, ref anythingChangedDummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Align the target rig with a creature
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="creature">The creature to align to</param>
|
||||||
|
/// <param name="anythingChanged">True when any property of the creature has changed</param>
|
||||||
public virtual void MatchTo(Creature creature, ref bool anythingChanged) {
|
public virtual void MatchTo(Creature creature, ref bool anythingChanged) {
|
||||||
Vector3 targetToModelTranslation = creature.model.position - this.transform.position;
|
Vector3 targetToModelTranslation = creature.model.position - this.transform.position;
|
||||||
bool changed = targetToModelTranslation != creature.targetToModelTranslation;
|
bool changed = targetToModelTranslation != creature.targetToModelTranslation;
|
||||||
@ -33,6 +45,14 @@ namespace Passer.CreatureControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compute the rotation from the target bone to a creature's bone
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bone">The creature's bone for this target bone</param>
|
||||||
|
/// <param name="nextBone">The next bone in the creature's hierarchy</param>
|
||||||
|
/// <returns>The rotation from the target bone rotation to the creature bone rotation</returns>
|
||||||
|
/// The next bone is used to compute the direction of the bone.
|
||||||
|
/// The 'up'-direction of the bone is currently fixed to (world) up.
|
||||||
public static Quaternion TargetToBoneRotation(Transform bone, Transform nextBone) {
|
public static Quaternion TargetToBoneRotation(Transform bone, Transform nextBone) {
|
||||||
if (bone == null || nextBone == null)
|
if (bone == null || nextBone == null)
|
||||||
return Quaternion.identity;
|
return Quaternion.identity;
|
||||||
@ -42,24 +62,6 @@ namespace Passer.CreatureControl {
|
|||||||
Quaternion toBoneRotation = Quaternion.Inverse(targetRotation) * bone.rotation;
|
Quaternion toBoneRotation = Quaternion.Inverse(targetRotation) * bone.rotation;
|
||||||
return toBoneRotation;
|
return toBoneRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Quaternion TargetToBoneRotation(Transform target, Transform nextTarget, Transform bone) {
|
|
||||||
Vector3 direction = nextTarget.position - bone.position;
|
|
||||||
Quaternion targetRotation = Quaternion.LookRotation(direction, Vector3.up);
|
|
||||||
Quaternion toBoneRotation = Quaternion.Inverse(targetRotation) * bone.rotation;
|
|
||||||
return toBoneRotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Quaternion TargetToBoneRotation_Debug(Transform target, Transform nextTarget, Transform bone) {
|
|
||||||
Vector3 direction = nextTarget.position - target.position;
|
|
||||||
Quaternion targetRotation = Quaternion.LookRotation(direction, Vector3.up);
|
|
||||||
Debug.DrawRay(bone.position, targetRotation * Vector3.forward, Color.blue);
|
|
||||||
Debug.DrawRay(bone.position, targetRotation * Vector3.up, Color.green);
|
|
||||||
|
|
||||||
Quaternion toBoneRotation = Quaternion.Inverse(targetRotation) * bone.rotation;
|
|
||||||
Debug.Log($"{targetRotation.eulerAngles} {bone.rotation.eulerAngles} -> {toBoneRotation.eulerAngles}");
|
|
||||||
return toBoneRotation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user