예제 #1
0
    public override void Analyze(GameObject o)
    {
        GameObject gameObject = o;

        animation   = orig.animation;
        name        = animation.name + "_bk";
        motionType  = orig.motionType;
        motionGroup = orig.motionGroup;

        // Initialize legs and cycle data
        LegController legC = gameObject.GetComponent(typeof(LegController)) as LegController;
        int           legs = legC.legs.Length;

        m_cycles = new LegCycleData[legs];
        for (int leg = 0; leg < legs; leg++)
        {
            cycles[leg]                  = new LegCycleData();
            cycles[leg].cycleCenter      = orig.cycles[leg].cycleCenter;
            cycles[leg].cycleScaling     = orig.cycles[leg].cycleScaling;
            cycles[leg].cycleDirection   = -orig.cycles[leg].cycleDirection;
            cycles[leg].stanceTime       = 1 - orig.cycles[leg].stanceTime;
            cycles[leg].liftTime         = 1 - orig.cycles[leg].landTime;
            cycles[leg].liftoffTime      = 1 - orig.cycles[leg].strikeTime;
            cycles[leg].postliftTime     = 1 - orig.cycles[leg].prelandTime;
            cycles[leg].prelandTime      = 1 - orig.cycles[leg].postliftTime;
            cycles[leg].strikeTime       = 1 - orig.cycles[leg].liftoffTime;
            cycles[leg].landTime         = 1 - orig.cycles[leg].liftTime;
            cycles[leg].cycleDistance    = orig.cycles[leg].cycleDistance;
            cycles[leg].stancePosition   = orig.cycles[leg].stancePosition;
            cycles[leg].heelToetipVector = orig.cycles[leg].heelToetipVector;
        }
    }
	public override void Analyze(GameObject o) {
		GameObject gameObject = o;
		animation = orig.animation;
		name = animation.name + "_bk";
		motionType = orig.motionType;
		motionGroup = orig.motionGroup;
		
		// Initialize legs and cycle data
		LegController legC = gameObject.GetComponent(typeof(LegController)) as LegController;
		int legs = legC.legs.Length;
		m_cycles = new LegCycleData[legs];
		for (int leg=0; leg<legs; leg++) {
			cycles[leg] = new LegCycleData();
			cycles[leg].cycleCenter = orig.cycles[leg].cycleCenter;
			cycles[leg].cycleScaling = orig.cycles[leg].cycleScaling;
			cycles[leg].cycleDirection = -orig.cycles[leg].cycleDirection;
			cycles[leg].stanceTime = 1-orig.cycles[leg].stanceTime;
			cycles[leg].liftTime = 1-orig.cycles[leg].landTime;
			cycles[leg].liftoffTime = 1-orig.cycles[leg].strikeTime;
			cycles[leg].postliftTime = 1-orig.cycles[leg].prelandTime;
			cycles[leg].prelandTime = 1-orig.cycles[leg].postliftTime;
			cycles[leg].strikeTime = 1-orig.cycles[leg].liftoffTime;
			cycles[leg].landTime = 1-orig.cycles[leg].liftTime;
			cycles[leg].cycleDistance = orig.cycles[leg].cycleDistance;
			cycles[leg].stancePosition = orig.cycles[leg].stancePosition;
			cycles[leg].heelToetipVector = orig.cycles[leg].heelToetipVector;
		}
		
	}
예제 #3
0
	public override void Analyze(GameObject o) {
		Debug.Log("Starting analysis");
		gameObject = o;
		name = animation.name;
		m_samples = 50;
		
		// Initialize legs and cycle data
		legC = gameObject.GetComponent(typeof(LegController)) as LegController;
		legs = legC.legs.Length;
		m_cycles = new LegCycleData[legs];
		for (int leg=0; leg<legs; leg++) {
			cycles[leg] = new LegCycleData();
			cycles[leg].samples = new LegCycleSample[samples+1];
			for (int i=0; i<samples+1; i++) {
				cycles[leg].samples[i] = new LegCycleSample();
			}
			cycles[leg].debugInfo = new CycleDebugInfo();
		}
		
		graphMin = new Vector3(0, 1000, 1000);
		graphMax = new Vector3(0,-1000,-1000);
		
		for (int leg=0; leg<legs; leg++) {
			// Sample ankle, heel, toe, and toetip positions over the length of the animation.
			Transform ankleT = legC.legs[leg].ankle;
			Transform toeT = legC.legs[leg].toe;
			
			float rangeMax = 0;
			float ankleMin; float ankleMax; float toeMin; float toeMax;
			ankleMin = 1000;
			ankleMax = -1000;
			toeMin = 1000;
			toeMax = -1000;
			for (int i=0; i<samples+1; i++) {
				LegCycleSample s = cycles[leg].samples[i];
				gameObject.SampleAnimation(animation,i*1.0f/samples*animation.length);
				s.ankleMatrix = Util.RelativeMatrix(ankleT,gameObject.transform);
				s.toeMatrix = Util.RelativeMatrix(toeT,gameObject.transform);
				s.heel = s.ankleMatrix.MultiplyPoint(legC.legs[leg].ankleHeelVector);
				s.toetip = s.toeMatrix.MultiplyPoint(legC.legs[leg].toeToetipVector);
				s.middle = (s.heel+s.toetip)/2;
				
				// For each sample in time we want to know if the heel or toetip is closer to the ground.
				// We need a smooth curve with 0 = ankle is closer and 1 = toe is closer.
				s.balance = MotionAnalyzer.GetFootBalance(s.heel.y, s.toetip.y, legC.legs[leg].footLength);
				
				// Find the minimum and maximum extends on all axes of the ankle and toe positions.
				ankleMin = Mathf.Min(ankleMin,s.heel.y);
				toeMin = Mathf.Min(toeMin,s.toetip.y);
				ankleMax = Mathf.Max(ankleMax,s.heel.y);
				toeMax = Mathf.Max(toeMax,s.toetip.y);
			}
			rangeMax = Mathf.Max(ankleMax-ankleMin,toeMax-toeMin);
			
			// Determine motion type
			/*if (motionType==MotionType.AutoDetect) {
				motionType = MotionType.WalkCycle;
			}*/
			
			if (motionType==MotionType.WalkCycle) {
				FindCycleAxis(leg);
				
				// Foot stance time
				// Find the time when the foot stands most firmly on the ground.
				float stanceValue = Mathf.Infinity;
				for (int i=0; i<samples+1; i++) {
					LegCycleSample s = cycles[leg].samples[i];
					
					float sampleValue =
					// We want the point in time when the max of the heel height and the toe height is lowest
					Mathf.Max(s.heel.y, s.toetip.y)/rangeMax
					// Add some bias to poses where the leg is in the middle of the swing
					// i.e. the foot position is close to the middle of the foot curve
					+Mathf.Abs(
						Util.ProjectOntoPlane(s.middle-cycles[leg].cycleCenter, Vector3.up).magnitude
					)/cycles[leg].cycleScaling;
					
					// Use the new value if it is lower (better).
					if (sampleValue<stanceValue) {
						cycles[leg].stanceIndex = i;
						stanceValue = sampleValue;
					}
				}
			}
			else {
				cycles[leg].cycleDirection = Vector3.forward;
				cycles[leg].cycleScaling = 0;
				cycles[leg].stanceIndex = 0;
			}
			// The stance time
			cycles[leg].stanceTime = GetTimeFromIndex(cycles[leg].stanceIndex);
			
			// The stance index sample
			LegCycleSample ss = cycles[leg].samples[cycles[leg].stanceIndex];
			// Sample the animation at stance time
			gameObject.SampleAnimation(animation,cycles[leg].stanceTime*animation.length);
			
			// Using the stance sample as reference we can now determine:
			
			// The vector from heel to toetip at the stance pose 
			cycles[leg].heelToetipVector = (
				ss.toeMatrix.MultiplyPoint(legC.legs[leg].toeToetipVector)
				- ss.ankleMatrix.MultiplyPoint(legC.legs[leg].ankleHeelVector)
			);
			cycles[leg].heelToetipVector = Util.ProjectOntoPlane(cycles[leg].heelToetipVector, Vector3.up);
			cycles[leg].heelToetipVector = cycles[leg].heelToetipVector.normalized * legC.legs[leg].footLength;
			
			// Calculate foot flight path based on weighted average between ankle flight path and toe flight path,
			// using foot balance as weight.
			// The distance between ankle and toe is accounted for, using the stance pose for reference.
			for (int i=0; i<samples+1; i++) {
				LegCycleSample s = cycles[leg].samples[i];
				s.footBase = (
					(s.heel)*(1-s.balance)
					+(s.toetip-cycles[leg].heelToetipVector)*(s.balance)
				);
			}
			
			// The position of the footbase in the stance pose
			cycles[leg].stancePosition = ss.footBase;
			cycles[leg].stancePosition.y = legC.groundPlaneHeight;
			
			if (motionType==MotionType.WalkCycle) {
				// Find contact times:
				// Strike time: foot first touches the ground (0% grounding)
				// Down time: all of the foot touches the ground (100% grounding)
				// Lift time: all of the foot still touches the ground but begins to lift (100% grounding)
				// Liftoff time: last part of the foot leaves the ground (0% grounding)
				float timeA;
				float timeB;
				
				// Find upwards contact times for projected ankle and toe
				// Use the first occurance as lift time and the second as liftoff time
				timeA = FindContactTime(cycles[leg], false, +1, rangeMax, 0.1f);
				cycles[leg].debugInfo.ankleLiftTime = timeA;
				timeB = FindContactTime(cycles[leg], true,  +1, rangeMax, 0.1f);
				cycles[leg].debugInfo.toeLiftTime = timeB;
				if (timeA<timeB) {
					cycles[leg].liftTime = timeA;
					cycles[leg].liftoffTime = timeB;
				}
				else {
					cycles[leg].liftTime = timeB;
					cycles[leg].liftoffTime = timeA;
				}
				
				// Find time where swing direction and speed changes significantly.
				// If this happens sooner than the found liftoff time,
				// then the liftoff time must be overwritten with this value.
				timeA = FindSwingChangeTime(cycles[leg], +1, 0.5f);
				cycles[leg].debugInfo.footLiftTime = timeA;
				if (cycles[leg].liftoffTime > timeA) {
					cycles[leg].liftoffTime = timeA;
					if (cycles[leg].liftTime > cycles[leg].liftoffTime) {
						cycles[leg].liftTime = cycles[leg].liftoffTime;
					}
				}
				
				// Find downwards contact times for projected ankle and toe
				// Use the first occurance as strike time and the second as down time
				timeA = FindContactTime(cycles[leg], false, -1, rangeMax, 0.1f);
				timeB = FindContactTime(cycles[leg], true,  -1, rangeMax, 0.1f);
				if (timeA<timeB) {
					cycles[leg].strikeTime = timeA;
					cycles[leg].landTime = timeB;
				}
				else {
					cycles[leg].strikeTime = timeB;
					cycles[leg].landTime = timeA;
				}
				
				// Find time where swing direction and speed changes significantly.
				// If this happens later than the found strike time,
				// then the strike time must be overwritten with this value.
				timeA = FindSwingChangeTime(cycles[leg], -1, 0.5f);
				cycles[leg].debugInfo.footLandTime = timeA;
				if (cycles[leg].strikeTime < timeA) {
					cycles[leg].strikeTime = timeA;
					if (cycles[leg].landTime < cycles[leg].strikeTime) {
						cycles[leg].landTime = cycles[leg].strikeTime;
					}
				}
				
				// Set postliftTime and prelandTime
				float softening = 0.2f;
				
				cycles[leg].postliftTime = cycles[leg].liftoffTime;
				if (cycles[leg].postliftTime < cycles[leg].liftTime+softening) {
					cycles[leg].postliftTime = cycles[leg].liftTime+softening;
				}
				
				cycles[leg].prelandTime = cycles[leg].strikeTime;
				if (cycles[leg].prelandTime > cycles[leg].landTime-softening) {
					cycles[leg].prelandTime = cycles[leg].landTime-softening;
				}
				
				// Calculate the distance traveled during one cycle (for this foot).
				Vector3 stanceSlideVector = (
					cycles[leg].samples[GetIndexFromTime(Util.Mod(cycles[leg].liftoffTime+cycles[leg].stanceTime))].footBase
					-cycles[leg].samples[GetIndexFromTime(Util.Mod(cycles[leg].strikeTime+cycles[leg].stanceTime))].footBase
				);
				// FIXME: Assumes horizontal ground plane
				stanceSlideVector.y = 0;
				cycles[leg].cycleDistance = stanceSlideVector.magnitude/(cycles[leg].liftoffTime-cycles[leg].strikeTime+1);
				cycles[leg].cycleDirection = -(stanceSlideVector.normalized);
			}
			else {
				cycles[leg].cycleDirection = Vector3.zero;
				cycles[leg].cycleDistance = 0;
			}
			
			graphMax.y = Mathf.Max(graphMax.y,Mathf.Max(ankleMax,toeMax));
		}
		
		// Find the overall speed and direction traveled during one cycle,
		// based on average of speed values for each individual foot.
		// (They should be very close, but animations are often imperfect,
		// leading to different speeds for different legs.)
		m_cycleDistance = 0;
		m_cycleDirection = Vector3.zero;
		for (int leg=0; leg<legs; leg++) {
			m_cycleDistance += cycles[leg].cycleDistance;
			m_cycleDirection += cycles[leg].cycleDirection;
			Debug.Log("Cycle direction of leg "+leg+" is "+cycles[leg].cycleDirection+" with step distance "+cycles[leg].cycleDistance);
		}
		m_cycleDistance /= legs;
		m_cycleDirection /= legs;
		m_cycleDuration = animation.length;
		m_cycleSpeed = cycleDistance/cycleDuration;
		Debug.Log("Overall cycle direction is "+m_cycleDirection+" with step distance "+m_cycleDistance+" and speed "+m_cycleSpeed);
		nativeSpeed = m_cycleSpeed * gameObject.transform.localScale.x;
		
		// Calculate normalized foot flight path
		for (int leg=0; leg<legs; leg++) {
			if (motionType==MotionType.WalkCycle) {
				for (int j=0; j<samples; j++) {
					int i = Util.Mod(j+cycles[leg].stanceIndex,samples);
					LegCycleSample s = cycles[leg].samples[i];
					float time = GetTimeFromIndex(j);
					s.footBaseNormalized = s.footBase;
					
					if (fixFootSkating) {
						// Calculate normalized foot flight path
						// based on the calculated cycle distance of each individual foot
						Vector3 reference = (
							-cycles[leg].cycleDistance * cycles[leg].cycleDirection * (time-cycles[leg].liftoffTime)
							+cycles[leg].samples[
								GetIndexFromTime(cycles[leg].liftoffTime+cycles[leg].stanceTime)
							].footBase
						);
						
						s.footBaseNormalized = (s.footBaseNormalized-reference);
						if (cycles[leg].cycleDirection!=Vector3.zero) {
							s.footBaseNormalized = Quaternion.Inverse(
								Quaternion.LookRotation(cycles[leg].cycleDirection)
							) * s.footBaseNormalized;
						}
						
						s.footBaseNormalized.z /= cycles[leg].cycleDistance;
						if (time<=cycles[leg].liftoffTime) { s.footBaseNormalized.z = 0; }
						if (time>=cycles[leg].strikeTime) { s.footBaseNormalized.z = 1; }
						
						s.footBaseNormalized.y = s.footBase.y - legC.groundPlaneHeight;
					}
					else {
						// Calculate normalized foot flight path
						// based on the cycle distance of the whole motion
						// (the calculated average cycle distance)
						Vector3 reference = (
							-m_cycleDistance * m_cycleDirection * (time-cycles[leg].liftoffTime*0)
							// FIXME: Is same as stance position:
							+cycles[leg].samples[
								GetIndexFromTime(cycles[leg].liftoffTime*0+cycles[leg].stanceTime)
							].footBase
						);
						
						s.footBaseNormalized = (s.footBaseNormalized-reference);
						if (cycles[leg].cycleDirection!=Vector3.zero) {
							s.footBaseNormalized = Quaternion.Inverse(
								Quaternion.LookRotation(m_cycleDirection)
							) * s.footBaseNormalized;
						}
						
						s.footBaseNormalized.z /= m_cycleDistance;
						
						s.footBaseNormalized.y = s.footBase.y - legC.groundPlaneHeight;
					}
				}
				//cycles[leg].samples[cycles[leg].stanceIndex].footBaseNormalized.z = 0;
				cycles[leg].samples[samples] = cycles[leg].samples[0];
			}
			else {
				for (int j=0; j<samples; j++) {
					int i = Util.Mod(j+cycles[leg].stanceIndex,samples);
					LegCycleSample s = cycles[leg].samples[i];
					s.footBaseNormalized = s.footBase - cycles[leg].stancePosition;
				}
			}
		}
		
		for (int leg=0; leg<legs; leg++) {
			float heelToeZDist = Vector3.Dot(cycles[leg].heelToetipVector,cycleDirection);
			for (int i=0; i<samples; i++) {
				LegCycleSample s = cycles[leg].samples[i];
				float zVal = Vector3.Dot(s.footBase, cycleDirection);
				if (zVal < graphMin.z) graphMin.z = zVal;
				if (zVal > graphMax.z) graphMax.z = zVal;
				if (zVal+heelToeZDist < graphMin.z) graphMin.z = zVal+heelToeZDist;
				if (zVal+heelToeZDist > graphMax.z) graphMax.z = zVal+heelToeZDist;
			}
		}
		graphMin.y = legC.groundPlaneHeight;
	}
예제 #4
0
	private float FindSwingChangeTime(LegCycleData data, int searchDirection, float threshold) {
		// Find the contact time on the height curve, where the (projected ankle or toe)
		// hits or leaves the ground (depending on search direction in time).
		int spread = samples/5; // FIXME magic number for spread value
		float stanceSpeed = 0;
		for (int i=0; i<samples && i>-samples; i+=searchDirection) {
			// Find speed by sampling curve value ahead and behind.
			int[] j = new int[3];
			float[] value = new float[3];
			for (int s=0; s<3; s++) {
				j[s] = Util.Mod(i+data.stanceIndex-spread+spread*s,samples);
				value[s] = Vector3.Dot(data.samples[j[s]].footBase, data.cycleDirection);
			}
			float currentSpeed = value[2]-value[0];
			if (i==0) stanceSpeed = currentSpeed;
			// If speed is too different from speed at stance time,
			// the current time is determined as the swing change time
			if (Mathf.Abs((currentSpeed-stanceSpeed)/stanceSpeed)>threshold) {
				return GetTimeFromIndex(Util.Mod(j[1]-data.stanceIndex,samples));
			}
		}
		return Util.Mod(searchDirection*-0.01f);
	}
예제 #5
0
	private float FindContactTime(LegCycleData data, bool useToe, int searchDirection, float yRange, float threshold) {
		// Find the contact time on the height curve, where the (projected ankle or toe)
		// hits or leaves the ground (depending on search direction in time).
		int spread = 5; // FIXME magic number for spread value
		float curvatureMax = 0;
		int curvatureMaxIndex = data.stanceIndex;
		for (int i=0; i<samples && i>-samples; i+=searchDirection) {
			// Find second derived by sampling three positions on curve.
			// Spred samples a bit to ignore high frequency noise.
			// FIXME magic number for spread value
			int[] j = new int[3];
			float[] value = new float[3];
			for (int s=0; s<3; s++) {
				j[s] = Util.Mod(i+data.stanceIndex-spread+spread*s,samples);
				if (useToe) value[s] = data.samples[j[s]].toetip.y;
				else		value[s] = data.samples[j[s]].heel.y;
			}
			//float curvatureCurrent = value[0]+value[2]-2*value[1];
			float curvatureCurrent = Mathf.Atan((value[2]-value[1])*10/yRange)-Mathf.Atan((value[1]-value[0])*10/yRange);
			if (
				// Sample must be above the ground
				(value[1]>legC.groundPlaneHeight)
				// Sample must have highest curvature
				&& (curvatureCurrent>curvatureMax)
				// Slope at sample must go upwards (when going in search direction)
				&& (Mathf.Sign(value[2]-value[0])==Mathf.Sign(searchDirection))
			) {
				curvatureMax = curvatureCurrent;
				curvatureMaxIndex = j[1];
			}
			// Terminate search when foot height is above threshold height above ground
			if (value[1]>legC.groundPlaneHeight+yRange*threshold) {
				break;
			}
		}
		return GetTimeFromIndex(Util.Mod(curvatureMaxIndex-data.stanceIndex,samples));
	}