| 1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
| 2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
| 3 | #include "Animation/BsAnimationClip.h" |
| 4 | #include "Resources/BsResources.h" |
| 5 | #include "Animation/BsSkeleton.h" |
| 6 | #include "Private/RTTI/BsAnimationClipRTTI.h" |
| 7 | |
| 8 | namespace bs |
| 9 | { |
| 10 | void AnimationCurves::addPositionCurve(const String& name, const TAnimationCurve<Vector3>& curve) |
| 11 | { |
| 12 | auto iterFind = std::find_if(position.begin(), position.end(), [&](auto x) { return x.name == name; }); |
| 13 | |
| 14 | if (iterFind != position.end()) |
| 15 | iterFind->curve = curve; |
| 16 | else |
| 17 | position.push_back({ name, AnimationCurveFlags(), curve }); |
| 18 | } |
| 19 | |
| 20 | void AnimationCurves::addRotationCurve(const String& name, const TAnimationCurve<Quaternion>& curve) |
| 21 | { |
| 22 | auto iterFind = std::find_if(rotation.begin(), rotation.end(), [&](auto x) { return x.name == name; }); |
| 23 | |
| 24 | if (iterFind != rotation.end()) |
| 25 | iterFind->curve = curve; |
| 26 | else |
| 27 | rotation.push_back({ name, AnimationCurveFlags(), curve }); |
| 28 | } |
| 29 | |
| 30 | void AnimationCurves::addScaleCurve(const String& name, const TAnimationCurve<Vector3>& curve) |
| 31 | { |
| 32 | auto iterFind = std::find_if(scale.begin(), scale.end(), [&](auto x) { return x.name == name; }); |
| 33 | |
| 34 | if (iterFind != scale.end()) |
| 35 | iterFind->curve = curve; |
| 36 | else |
| 37 | scale.push_back({ name, AnimationCurveFlags(), curve }); |
| 38 | } |
| 39 | |
| 40 | void AnimationCurves::addGenericCurve(const String& name, const TAnimationCurve<float>& curve) |
| 41 | { |
| 42 | auto iterFind = std::find_if(generic.begin(), generic.end(), [&](auto x) { return x.name == name; }); |
| 43 | |
| 44 | if (iterFind != generic.end()) |
| 45 | iterFind->curve = curve; |
| 46 | else |
| 47 | generic.push_back({ name, AnimationCurveFlags(), curve }); |
| 48 | } |
| 49 | |
| 50 | void AnimationCurves::removePositionCurve(const String& name) |
| 51 | { |
| 52 | auto iterFind = std::find_if(position.begin(), position.end(), [&](auto x) { return x.name == name; }); |
| 53 | |
| 54 | if (iterFind != position.end()) |
| 55 | position.erase(iterFind); |
| 56 | } |
| 57 | |
| 58 | void AnimationCurves::removeRotationCurve(const String& name) |
| 59 | { |
| 60 | auto iterFind = std::find_if(rotation.begin(), rotation.end(), [&](auto x) { return x.name == name; }); |
| 61 | |
| 62 | if (iterFind != rotation.end()) |
| 63 | rotation.erase(iterFind); |
| 64 | } |
| 65 | |
| 66 | void AnimationCurves::removeScaleCurve(const String& name) |
| 67 | { |
| 68 | auto iterFind = std::find_if(scale.begin(), scale.end(), [&](auto x) { return x.name == name; }); |
| 69 | |
| 70 | if (iterFind != scale.end()) |
| 71 | scale.erase(iterFind); |
| 72 | } |
| 73 | |
| 74 | void AnimationCurves::removeGenericCurve(const String& name) |
| 75 | { |
| 76 | auto iterFind = std::find_if(generic.begin(), generic.end(), [&](auto x) { return x.name == name; }); |
| 77 | |
| 78 | if (iterFind != generic.end()) |
| 79 | generic.erase(iterFind); |
| 80 | } |
| 81 | |
| 82 | AnimationClip::AnimationClip() |
| 83 | : Resource(false), mVersion(0), mCurves(bs_shared_ptr_new<AnimationCurves>()) |
| 84 | , mRootMotion(bs_shared_ptr_new<RootMotion>()), mIsAdditive(false), mLength(0.0f), mSampleRate(1) |
| 85 | { |
| 86 | |
| 87 | } |
| 88 | |
| 89 | AnimationClip::AnimationClip(const SPtr<AnimationCurves>& curves, bool isAdditive, UINT32 sampleRate, |
| 90 | const SPtr<RootMotion>& rootMotion) |
| 91 | : Resource(false), mVersion(0), mCurves(curves), mRootMotion(rootMotion), mIsAdditive(isAdditive), mLength(0.0f) |
| 92 | , mSampleRate(sampleRate) |
| 93 | { |
| 94 | if (mCurves == nullptr) |
| 95 | mCurves = bs_shared_ptr_new<AnimationCurves>(); |
| 96 | |
| 97 | if (mRootMotion == nullptr) |
| 98 | mRootMotion = bs_shared_ptr_new<RootMotion>(); |
| 99 | |
| 100 | buildNameMapping(); |
| 101 | calculateLength(); |
| 102 | } |
| 103 | |
| 104 | HAnimationClip AnimationClip::create(bool isAdditive) |
| 105 | { |
| 106 | return static_resource_cast<AnimationClip>(gResources()._createResourceHandle( |
| 107 | _createPtr(bs_shared_ptr_new<AnimationCurves>(), isAdditive))); |
| 108 | } |
| 109 | |
| 110 | HAnimationClip AnimationClip::create(const SPtr<AnimationCurves>& curves, bool isAdditive, UINT32 sampleRate, |
| 111 | const SPtr<RootMotion>& rootMotion) |
| 112 | { |
| 113 | return static_resource_cast<AnimationClip>(gResources()._createResourceHandle( |
| 114 | _createPtr(curves, isAdditive, sampleRate, rootMotion))); |
| 115 | } |
| 116 | |
| 117 | SPtr<AnimationClip> AnimationClip::createEmpty() |
| 118 | { |
| 119 | AnimationClip* rawPtr = new (bs_alloc<AnimationClip>()) AnimationClip(); |
| 120 | |
| 121 | SPtr<AnimationClip> newClip = bs_core_ptr<AnimationClip>(rawPtr); |
| 122 | newClip->_setThisPtr(newClip); |
| 123 | |
| 124 | return newClip; |
| 125 | } |
| 126 | |
| 127 | SPtr<AnimationClip> AnimationClip::_createPtr(const SPtr<AnimationCurves>& curves, bool isAdditive, UINT32 sampleRate, |
| 128 | const SPtr<RootMotion>& rootMotion) |
| 129 | { |
| 130 | AnimationClip* rawPtr = new (bs_alloc<AnimationClip>()) AnimationClip(curves, isAdditive, sampleRate, rootMotion); |
| 131 | |
| 132 | SPtr<AnimationClip> newClip = bs_core_ptr<AnimationClip>(rawPtr); |
| 133 | newClip->_setThisPtr(newClip); |
| 134 | newClip->initialize(); |
| 135 | |
| 136 | return newClip; |
| 137 | } |
| 138 | |
| 139 | void AnimationClip::setCurves(const AnimationCurves& curves) |
| 140 | { |
| 141 | *mCurves = curves; |
| 142 | |
| 143 | buildNameMapping(); |
| 144 | calculateLength(); |
| 145 | mVersion++; |
| 146 | } |
| 147 | |
| 148 | bool AnimationClip::hasRootMotion() const |
| 149 | { |
| 150 | return mRootMotion != nullptr && |
| 151 | (mRootMotion->position.getNumKeyFrames() > 0 || mRootMotion->rotation.getNumKeyFrames() > 0); |
| 152 | } |
| 153 | |
| 154 | void AnimationClip::calculateLength() |
| 155 | { |
| 156 | mLength = 0.0f; |
| 157 | |
| 158 | for (auto& entry : mCurves->position) |
| 159 | mLength = std::max(mLength, entry.curve.getLength()); |
| 160 | |
| 161 | for (auto& entry : mCurves->rotation) |
| 162 | mLength = std::max(mLength, entry.curve.getLength()); |
| 163 | |
| 164 | for (auto& entry : mCurves->scale) |
| 165 | mLength = std::max(mLength, entry.curve.getLength()); |
| 166 | |
| 167 | for (auto& entry : mCurves->generic) |
| 168 | mLength = std::max(mLength, entry.curve.getLength()); |
| 169 | } |
| 170 | |
| 171 | void AnimationClip::buildNameMapping() |
| 172 | { |
| 173 | mNameMapping.clear(); |
| 174 | |
| 175 | auto registerEntries = [&](auto& curve, CurveType type) |
| 176 | { |
| 177 | UINT32 typeIdx = (UINT32)type; |
| 178 | |
| 179 | for (UINT32 i = 0; i < (UINT32)curve.size(); i++) |
| 180 | { |
| 181 | auto& entry = curve[i]; |
| 182 | |
| 183 | auto iterFind = mNameMapping.find(entry.name); |
| 184 | if (iterFind == mNameMapping.end()) |
| 185 | { |
| 186 | UINT32* indices = mNameMapping[entry.name].data(); |
| 187 | memset(indices, -1, sizeof(UINT32) * (int)CurveType::Count); |
| 188 | |
| 189 | indices[typeIdx] = i; |
| 190 | } |
| 191 | else |
| 192 | mNameMapping[entry.name][typeIdx] = i; |
| 193 | } |
| 194 | }; |
| 195 | |
| 196 | registerEntries(mCurves->position, CurveType::Position); |
| 197 | registerEntries(mCurves->rotation, CurveType::Rotation); |
| 198 | registerEntries(mCurves->scale, CurveType::Scale); |
| 199 | |
| 200 | // Generic and morph curves |
| 201 | { |
| 202 | Vector<TNamedAnimationCurve<float>>& curve = mCurves->generic; |
| 203 | for (UINT32 i = 0; i < (UINT32)curve.size(); i++) |
| 204 | { |
| 205 | auto& entry = curve[i]; |
| 206 | |
| 207 | UINT32 typeIdx; |
| 208 | if (entry.flags.isSet(AnimationCurveFlag::MorphFrame)) |
| 209 | typeIdx = (UINT32)CurveType::MorphFrame; |
| 210 | else if (entry.flags.isSet(AnimationCurveFlag::MorphWeight)) |
| 211 | typeIdx = (UINT32)CurveType::MorphWeight; |
| 212 | else |
| 213 | typeIdx = (UINT32)CurveType::Generic; |
| 214 | |
| 215 | auto iterFind = mNameMapping.find(entry.name); |
| 216 | if (iterFind == mNameMapping.end()) |
| 217 | { |
| 218 | UINT32* indices = mNameMapping[entry.name].data(); |
| 219 | memset(indices, -1, sizeof(UINT32) * (int)CurveType::Count); |
| 220 | |
| 221 | indices[typeIdx] = i; |
| 222 | } |
| 223 | else |
| 224 | mNameMapping[entry.name][typeIdx] = i; |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | void AnimationClip::initialize() |
| 230 | { |
| 231 | buildNameMapping(); |
| 232 | |
| 233 | Resource::initialize(); |
| 234 | } |
| 235 | |
| 236 | void AnimationClip::getBoneMapping(const Skeleton& skeleton, AnimationCurveMapping* mapping) const |
| 237 | { |
| 238 | UINT32 numBones = skeleton.getNumBones(); |
| 239 | for(UINT32 i = 0; i < numBones; i++) |
| 240 | { |
| 241 | const SkeletonBoneInfo& boneInfo = skeleton.getBoneInfo(i); |
| 242 | |
| 243 | getCurveMapping(boneInfo.name, mapping[i]); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | void AnimationClip::getCurveMapping(const String& name, AnimationCurveMapping& mapping) const |
| 248 | { |
| 249 | auto iterFind = mNameMapping.find(name); |
| 250 | if (iterFind != mNameMapping.end()) |
| 251 | { |
| 252 | const UINT32* indices = iterFind->second.data(); |
| 253 | |
| 254 | mapping.position = indices[(UINT32)CurveType::Position]; |
| 255 | mapping.rotation = indices[(UINT32)CurveType::Rotation]; |
| 256 | mapping.scale = indices[(UINT32)CurveType::Scale]; |
| 257 | } |
| 258 | else |
| 259 | mapping = { (UINT32)-1, (UINT32)-1, (UINT32)-1 }; |
| 260 | } |
| 261 | |
| 262 | void AnimationClip::getMorphMapping(const String& name, UINT32& frameIdx, UINT32& weightIdx) const |
| 263 | { |
| 264 | auto iterFind = mNameMapping.find(name); |
| 265 | if (iterFind != mNameMapping.end()) |
| 266 | { |
| 267 | const UINT32* indices = iterFind->second.data(); |
| 268 | |
| 269 | frameIdx = indices[(UINT32)CurveType::MorphFrame]; |
| 270 | weightIdx = indices[(UINT32)CurveType::MorphWeight]; |
| 271 | } |
| 272 | else |
| 273 | { |
| 274 | frameIdx = (UINT32)-1; |
| 275 | weightIdx = (UINT32)-1; |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | RTTITypeBase* AnimationClip::getRTTIStatic() |
| 280 | { |
| 281 | return AnimationClipRTTI::instance(); |
| 282 | } |
| 283 | |
| 284 | RTTITypeBase* AnimationClip::getRTTI() const |
| 285 | { |
| 286 | return getRTTIStatic(); |
| 287 | } |
| 288 | } |