summaryrefslogtreecommitdiff
path: root/Runtime/Animation/MecanimClipBuilder.h
blob: d79dd20d7640fcfef303b25286a23d82b1f0d3f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#pragma once

#include "PPtrKeyframes.h"
#include "Runtime/Math/AnimationCurve.h"
#include "AnimationClipBindings.h"
#include "Runtime/mecanim/animation/clipmuscle.h"

/// Builds a full mecanim clip from source curve data
/// When building a mecanim clip we classify all curves into:
/// streamedclip: hermite curve polynomials
/// denseclip: linearly interpolated non-sparse keyframes
/// constantclip: value doesn't change over time


struct AnimationClipSettings;

enum ClipOptType { kInvalidCurve = -1, kStreamedClip = 0, kDenseClip, kConstantClip, kClipOptCount };

struct MecanimClipBuilder
{
	struct Curves
	{
		dynamic_array<AnimationCurveVec3*>				positionCurves;
		dynamic_array<AnimationCurveQuat*>				rotationCurves;
		dynamic_array<AnimationCurveVec3*>				scaleCurves;
		dynamic_array<AnimationCurve*>					genericCurves;
		dynamic_array<PPtrKeyframes*>					pptrCurves;
		
		size_t											totalCurveCount;
		size_t											totalKeyCount;
		
		dynamic_array<UnityEngine::Animation::GenericBinding>					bindings;
	};
	
	MecanimClipBuilder ();
	
	mecanim::uint32_t muscleIndexArray[mecanim::animation::s_ClipMuscleCurveCount];
	
	Curves	curves[kClipOptCount];
	size_t  totalBindingCount;
	size_t  totalCurveCount;
	bool    hasAnimationEvents;
	float	startTime;
	float	stopTime;
	float	sampleRate;
};

void AddPositionCurveToClipBuilder (AnimationCurveVec3& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve);
void AddRotationCurveToClipBuilder (AnimationCurveQuat& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve);
void AddScaleCurveToClipBuilder (AnimationCurveVec3& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve);
void AddGenericCurveToClipBuilder (AnimationCurve& curve, const UnityEngine::Animation::GenericBinding& binding, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve);
void AddPPtrCurveToClipBuilder (PPtrKeyframes& curve, const UnityEngine::Animation::GenericBinding& binding, MecanimClipBuilder& clipBuilder);

bool PrepareClipBuilder (MecanimClipBuilder& clipBuilder);
mecanim::animation::ClipMuscleConstant* BuildMuscleClip (const MecanimClipBuilder& clipBuilder, const AnimationClipSettings& muslceClipInfo, bool isHumanClip, UnityEngine::Animation::AnimationClipBindingConstant& outClipBindings, mecanim::memory::Allocator& allocator);

void PatchMuscleClipWithInfo (const AnimationClipSettings& clipInfo, bool isHumanoid, mecanim::animation::ClipMuscleConstant *cst);
void CstToAnimationClipSettings (mecanim::animation::ClipMuscleConstant const *cst, AnimationClipSettings &clipInfo);

template<class T>
static bool IsConstantCurve (AnimationCurveTpl<T>& curve)
{
	Assert(curve.GetKeyCount() != 0);

	KeyframeTpl<T> firstKey = curve.GetKey(0);
	for (int i=0;i<curve.GetKeyCount();i++)
	{
		if (!CompareApproximately(curve.GetKey(i).value, firstKey.value))
			return false;
		if (!CompareApproximately(curve.GetKey(i).inSlope, Zero<T> ()))
			return false;
		if (!CompareApproximately(curve.GetKey(i).outSlope, Zero<T> ()))
			return false;
	}
	
	return true;
}

template<class T>
static bool IsStepKey(KeyframeTpl<T> const& key)
{
	return !IsFinite(key.inSlope) || !IsFinite(key.outSlope);
}

template<class T>
static bool IsTooDense(KeyframeTpl<T> const& key, KeyframeTpl<T> const& previousKey, float sampleStep)
{
	float delta = std::abs(key.time - previousKey.time);

	// epsilon is too small here, use a bigger threshold
	return (delta - sampleStep) < -1e-5f /*-std::numeric_limits<float>::epsilon()*/;
}

template<class T>
static bool IsDenseCurve (AnimationCurveTpl<T> const& curve)
{
	Assert(curve.GetKeyCount() != 0);

	const float samplePerSec = 30.f;
	const float sampleStep = 1.0f/samplePerSec;

	// Remember that default curve classification is Streamed curve,
	// which are ~8 time bigger in memory than a Dense curve( ~8x = constant cost + memory const)
	// 
	std::pair<float, float> range = curve.GetRange();
	float diff = range.second - range.first;
	if(diff * samplePerSec > curve.GetKeyCount() * 8)
		return false;

	if( IsStepKey(curve.GetKey(0)) )
			return false;

	// Look for step curve, they cannot be represented by a dense curve
	for (int i=1;i<curve.GetKeyCount();i++)
	{
		KeyframeTpl<T> const& previousKey = curve.GetKey(i-1);
		KeyframeTpl<T> const& key = curve.GetKey(i);
		
		if( IsStepKey(key) )
			return false;

		// For now if there is more key than sampling rate, revert back to streamed clip.
		if( IsTooDense( key, previousKey, sampleStep) )
			return false;
	}
	
	return true;
}