/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * This file is based on WME. * http://dead-code.org/redir.php?target=wme * Copyright (c) 2003-2013 Jan Nedoma and contributors */ #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_engine.h" #include "engines/wintermute/base/gfx/3dutils.h" #include "engines/wintermute/base/gfx/xanimation.h" #include "engines/wintermute/base/gfx/xanimation_set.h" #include "engines/wintermute/base/gfx/xframe_node.h" #include "engines/wintermute/base/gfx/xmodel.h" #include "engines/wintermute/base/gfx/xfile_loader.h" #include "engines/wintermute/dcgf.h" namespace Wintermute { ////////////////////////////////////////////////////////////////////////// Animation::Animation(BaseGame *inGame) : BaseClass(inGame) { _targetFrame = nullptr; } ////////////////////////////////////////////////////////////////////////// Animation::~Animation() { for (int32 i = 0; i < _posKeys.getSize(); i++) { delete _posKeys[i]; } _posKeys.removeAll(); for (int32 i = 0; i < _rotKeys.getSize(); i++) { delete _rotKeys[i]; } _rotKeys.removeAll(); for (int32 i = 0; i < _scaleKeys.getSize(); i++) { delete _scaleKeys[i]; } _scaleKeys.removeAll(); } ////////////////////////////////////////////////////////////////////////// bool Animation::findBone(FrameNode *rootFrame) { if (!_targetName.empty()) { _targetFrame = rootFrame->findFrame(_targetName.c_str()); } return true; } ////////////////////////////////////////////////////////////////////////// bool Animation::load(XFileData *xobj, AnimationSet *parentAnimSet) { bool result; XClassType objectType; // Query the child for it's FileDataReference if (xobj->isReference()) { // The original data is found result = xobj->getType(objectType); if (!result) { BaseEngine::LOG(0, "Couldn't retrieve object type while loading animation"); return result; } // The object must be a frame if (objectType == kXClassFrame) { // The frame is found, get its name // The name will be used later by the findBone function to get // a pointer to the target frame if (_targetFrame) { BaseEngine::LOG(0, "Animation frame name reference duplicated"); return false; } // get name result = XModel::loadName(_targetName, xobj); if (!result) { BaseEngine::LOG(0, "Error retrieving frame name while loading animation"); return false; } } } else { // a data object is found, get its type result = xobj->getType(objectType); if (!result) return false; if (objectType == kXClassAnimationKey) { // an animation key is found, load the data XAnimationKeyObject *animationKey = xobj->getXAnimationKeyObject(); if (!animationKey) return false; result = loadAnimationKeyData(animationKey); if (!result) return false; } else if (objectType == kXClassAnimationOptions) { XAnimationOptionsObject *animationOptions = xobj->getXAnimationOptionsObject(); if (!animationOptions) return false; result = loadAnimationOptionData(animationOptions, parentAnimSet); if (!result) return false; } } return true; } ////////////////////////////////////////////////////////////////////////// bool Animation::loadAnimationOptionData(XAnimationOptionsObject *animationOptionData, AnimationSet *parentAnimSet) { if (animationOptionData->_openclosed && parentAnimSet) parentAnimSet->_looping = true; return true; } ////////////////////////////////////////////////////////////////////////// bool Animation::loadAnimationKeyData(XAnimationKeyObject *animationKey) { // get the type and count of the key uint32 keyType = animationKey->_keyType; uint32 numKeys = animationKey->_numKeys; if (keyType == 0) { // rotation key if (_rotKeys.getSize() != 0) { BaseEngine::LOG(0, "Rotation key duplicated"); return false; } for (uint32 key = 0; key < numKeys; key++) { const XTimedFloatKeys *fileRotKey = &animationKey->_keys[key]; assert(fileRotKey->_numTfkeys == 4); BoneRotationKey *rotKey = new BoneRotationKey; rotKey->_time = fileRotKey->_time; // NOTE x files are w x y z and QUATERNIONS are x y z w rotKey->_rotation._w = fileRotKey->_tfkeys[0]; rotKey->_rotation._x = fileRotKey->_tfkeys[1]; rotKey->_rotation._y = fileRotKey->_tfkeys[2]; rotKey->_rotation._z = fileRotKey->_tfkeys[3]; _rotKeys.add(rotKey); } } else if (keyType == 1) { // scale key if (_scaleKeys.getSize() != 0) { BaseEngine::LOG(0, "Scale key duplicated"); return false; } for (uint32 key = 0; key < numKeys; key++) { const XTimedFloatKeys *fileScaleKey = &animationKey->_keys[key]; assert(fileScaleKey->_numTfkeys == 3); BoneScaleKey *scaleKey = new BoneScaleKey; scaleKey->_time = fileScaleKey->_time; scaleKey->_scale._x = fileScaleKey->_tfkeys[0]; scaleKey->_scale._y = fileScaleKey->_tfkeys[1]; scaleKey->_scale._z = fileScaleKey->_tfkeys[2]; _scaleKeys.add(scaleKey); } } else if (keyType == 2) { // position key if (_posKeys.getSize() != 0) { BaseEngine::LOG(0, "Position key duplicated"); return false; } for (uint32 key = 0; key < numKeys; key++) { const XTimedFloatKeys *filePosKey = &animationKey->_keys[key]; assert(filePosKey->_numTfkeys == 3); BonePositionKey *posKey = new BonePositionKey; posKey->_time = filePosKey->_time; posKey->_pos._x = filePosKey->_tfkeys[0]; posKey->_pos._y = filePosKey->_tfkeys[1]; posKey->_pos._z = filePosKey->_tfkeys[2]; _posKeys.add(posKey); } } else if (keyType == 4) { // matrix key if (_rotKeys.getSize() != 0 || _scaleKeys.getSize() != 0 || _posKeys.getSize() != 0) { BaseEngine::LOG(0, "Matrix key duplicated"); return false; } DXQuaternion qRot; DXVector3 transVec; DXVector3 scaleVec; for (uint32 key = 0; key < numKeys; key++) { const XTimedFloatKeys *fileMatrixKey = &animationKey->_keys[key]; uint32 time = fileMatrixKey->_time; assert(fileMatrixKey->_numTfkeys == 16); DXMatrix keyData; for (uint32 i = 0; i < 16; ++i) { keyData._m4x4[i] = fileMatrixKey->_tfkeys[i]; } // we always convert matrix keys to T-R-S C3DUtils::decomposeMatrixSimple(&keyData, &transVec, &scaleVec, &qRot); BonePositionKey *positionKey = new BonePositionKey; positionKey->_time = time; positionKey->_pos = transVec; _posKeys.add(positionKey); BoneScaleKey *scaleKey = new BoneScaleKey; scaleKey->_time = time; scaleKey->_scale = scaleVec; _scaleKeys.add(scaleKey); BoneRotationKey *rotationKey = new BoneRotationKey; rotationKey->_time = time; rotationKey->_rotation = qRot; rotationKey->_rotation._x = -rotationKey->_rotation._x; rotationKey->_rotation._y = -rotationKey->_rotation._y; rotationKey->_rotation._z = -rotationKey->_rotation._z; _rotKeys.add(rotationKey); } } else { // the type is unknown, report the error BaseEngine::LOG(0, "Unexpected animation key type (%d)", keyType); } return true; } ////////////////////////////////////////////////////////////////////////// bool Animation::update(int slot, uint32 localTime, float animLerpValue) { // no target frame = no animation keys if (!_targetFrame) { return true; } DXVector3 resultPos(0.0f, 0.0f, 0.0f); DXVector3 resultScale(1.0f, 1.0f, 1.0f); DXQuaternion resultRot(0.0f, 0.0f, 0.0f, 1.0f); int keyIndex1, keyIndex2; uint32 time1, time2; float lerpValue; bool animate = false; // scale keys if (_scaleKeys.getSize() > 0) { keyIndex1 = keyIndex2 = 0; // get the two keys between which the time is currently in for (int32 key = 0; key < _scaleKeys.getSize(); key++) { if (_scaleKeys[key]->_time > localTime) { keyIndex2 = key; if (key > 0) { keyIndex1 = key - 1; } else { // when ikey == 0, then dwp2 == 0 keyIndex1 = key; } break; } } time1 = _scaleKeys[keyIndex1]->_time; time2 = _scaleKeys[keyIndex2]->_time; // get the lerp value if ((time2 - time1) == 0) { lerpValue = 0; } else { lerpValue = float(localTime - time1) / float(time2 - time1); } // apply the lerp function on the scale vector DXVec3Lerp(&resultScale, &_scaleKeys[keyIndex1]->_scale, &_scaleKeys[keyIndex2]->_scale, lerpValue); animate = true; } // rotation keys if (_rotKeys.getSize() > 0) { keyIndex1 = keyIndex2 = 0; // get the two keys surrounding the current time value for (int32 key = 0; key < _rotKeys.getSize(); key++) { if (_rotKeys[key]->_time > localTime) { keyIndex2 = key; if (key > 0) { keyIndex1 = key - 1; } else { // when ikey == 0, then dwp2 == 0 keyIndex1 = key; } break; } } time1 = _rotKeys[keyIndex1]->_time; time2 = _rotKeys[keyIndex2]->_time; // get the lerp value if ((time2 - time1) == 0) { lerpValue = 0; } else { lerpValue = float(localTime - time1) / float(time2 - time1); } // apply spherical lerp function DXQuaternion q1, q2; q1._x = -_rotKeys[keyIndex1]->_rotation._x; q1._y = -_rotKeys[keyIndex1]->_rotation._y; q1._z = -_rotKeys[keyIndex1]->_rotation._z; q1._w = _rotKeys[keyIndex1]->_rotation._w; q2._x = -_rotKeys[keyIndex2]->_rotation._x; q2._y = -_rotKeys[keyIndex2]->_rotation._y; q2._z = -_rotKeys[keyIndex2]->_rotation._z; q2._w = _rotKeys[keyIndex2]->_rotation._w; DXQuaternionSlerp(&resultRot, &q1, &q2, lerpValue); animate = true; } // position keys if (_posKeys.getSize() > 0) { keyIndex1 = keyIndex2 = 0; // get the two keys surrounding the time value for (int32 key = 0; key < _posKeys.getSize(); key++) { if (_posKeys[key]->_time > localTime) { keyIndex2 = key; if (key > 0) { keyIndex1 = key - 1; } else { // when ikey == 0, then dwp2 == 0 keyIndex1 = key; } break; } } time1 = _posKeys[keyIndex1]->_time; time2 = _posKeys[keyIndex2]->_time; // get the lerp value if (time2 - time1 == 0) lerpValue = 0; else lerpValue = float(localTime - time1) / float(time2 - time1); // apply the lerp function DXVec3Lerp(&resultPos, &_posKeys[keyIndex1]->_pos, &_posKeys[keyIndex2]->_pos, lerpValue); animate = true; } if (animate) { _targetFrame->setTransformation(slot, resultPos, resultScale, resultRot, animLerpValue); } return true; } ////////////////////////////////////////////////////////////////////////// int Animation::getFrameTime() { uint32 frameTime = 0; uint32 prevTime; // get the shortest frame time prevTime = 0; for (int32 i = 0; i < _rotKeys.getSize(); i++) { if (frameTime == 0 || _rotKeys[i]->_time - prevTime < frameTime) frameTime = _rotKeys[i]->_time - prevTime; prevTime = _rotKeys[i]->_time; } prevTime = 0; for (int32 i = 0; i < _posKeys.getSize(); i++) { if (frameTime == 0 || _posKeys[i]->_time - prevTime < frameTime) frameTime = _posKeys[i]->_time - prevTime; prevTime = _posKeys[i]->_time; } prevTime = 0; for (int32 i = 0; i < _scaleKeys.getSize(); i++) { if (frameTime == 0 || _scaleKeys[i]->_time - prevTime < frameTime) frameTime = _scaleKeys[i]->_time - prevTime; prevTime = _scaleKeys[i]->_time; } return frameTime; } ////////////////////////////////////////////////////////////////////////// uint32 Animation::getTotalTime() { uint32 totalTime = 0; if (_rotKeys.getSize() > 0) { totalTime = MAX(totalTime, _rotKeys[_rotKeys.getSize() - 1]->_time); } if (_posKeys.getSize() > 0) { totalTime = MAX(totalTime, _posKeys[_posKeys.getSize() - 1]->_time); } if (_scaleKeys.getSize() > 0) { totalTime = MAX(totalTime, _scaleKeys[_scaleKeys.getSize() - 1]->_time); } return totalTime; } } // namespace Wintermute