ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

emihead.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002 *
00003 * ResidualVM is the legal property of its developers, whose names
00004 * are too numerous to list here. Please refer to the COPYRIGHT
00005 * file distributed with this source distribution.
00006 *
00007 * This program is free software; you can redistribute it and/or
00008 * modify it under the terms of the GNU General Public License
00009 * as published by the Free Software Foundation; either version 2
00010 * of the License, or (at your option) any later version.
00011 *
00012 * This program is distributed in the hope that it will be useful,
00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 * GNU General Public License for more details.
00016 *
00017 * You should have received a copy of the GNU General Public License
00018 * along with this program; if not, write to the Free Software
00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 *
00021 */
00022 
00023 #include "engines/grim/grim.h"
00024 #include "engines/grim/emi/costume/emihead.h"
00025 #include "engines/grim/emi/costumeemi.h"
00026 #include "engines/grim/emi/skeleton.h"
00027 
00028 namespace Grim {
00029 
00030 EMIHead::EMIHead(EMICostume *costume) : _yawRange(80.0f), _minPitch(-30.0f), _maxPitch(30.0f) {
00031     _cost = costume;
00032 }
00033 
00034 void EMIHead::setJoint(const char *joint, const Math::Vector3d &offset) {
00035     _jointName = joint;
00036     _offset = offset;
00037 }
00038 
00039 void EMIHead::setLimits(float yawRange, float maxPitch, float minPitch) {
00040     _yawRange = yawRange;
00041     _maxPitch = maxPitch;
00042     _minPitch = minPitch;
00043 }
00044 
00045 void EMIHead::lookAt(bool entering, const Math::Vector3d &point, float rate, const Math::Matrix4 &matrix) {
00046     if (!_cost->_emiSkel || !_cost->_emiSkel->_obj)
00047         return;
00048 
00049     if (_jointName.empty())
00050         return;
00051 
00052     Joint *joint = _cost->_emiSkel->_obj->getJointNamed(_jointName);
00053     if (!joint)
00054         return;
00055 
00056     Math::Quaternion lookAtQuat; // Note: Identity if not looking at anything.
00057 
00058     if (entering) {
00059         Math::Matrix4 jointToWorld = _cost->getOwner()->getFinalMatrix() * joint->_finalMatrix;
00060         Math::Vector3d jointWorldPos = jointToWorld.getPosition();
00061         Math::Matrix4 worldToJoint = jointToWorld;
00062         worldToJoint.invertAffineOrthonormal();
00063 
00064         Math::Vector3d targetDir = (point + _offset) - jointWorldPos;
00065         targetDir.normalize();
00066 
00067         const Math::Vector3d worldUp(0, 1, 0);
00068         Math::Vector3d frontDir = Math::Vector3d(worldToJoint(0, 1), worldToJoint(1, 1), worldToJoint(2, 1)); // Look straight ahead. (+Y)
00069         Math::Vector3d modelFront(0, 0, 1);
00070         Math::Vector3d modelUp(0, 1, 0);
00071 
00072         joint->_absMatrix.inverseRotate(&modelFront);
00073         joint->_absMatrix.inverseRotate(&modelUp);
00074 
00075         // Generate a world-space look at matrix.
00076         Math::Matrix4 lookAtTM;
00077         lookAtTM.setToIdentity();
00078 
00079         if (Math::Vector3d::dotProduct(targetDir, worldUp) >= 0.98f) // Avoid singularity if trying to look straight up.
00080             lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, -frontDir); // Instead of orienting head towards scene up, orient head towards character "back",
00081         else if (Math::Vector3d::dotProduct(targetDir, worldUp) <= -0.98f) // Avoid singularity if trying to look straight down.
00082             lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, frontDir); // Instead of orienting head towards scene down, orient head towards character "front",
00083         else
00084             lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, worldUp);
00085 
00086         // Convert from world-space to joint-space.
00087         lookAtTM = worldToJoint * lookAtTM;
00088 
00089         // Apply angle limits.
00090         Math::Angle p, y, r;
00091         lookAtTM.getEuler(&y, &p, &r, Math::EO_ZXY);
00092 
00093         y.clampDegrees(_yawRange);
00094         p.clampDegrees(_minPitch, _maxPitch);
00095         r.clampDegrees(30.0f);
00096 
00097         lookAtTM.buildFromEuler(y, p, r, Math::EO_ZXY);
00098 
00099         lookAtQuat.fromMatrix(lookAtTM.getRotation());
00100     }
00101 
00102     if (_headRot != lookAtQuat) {
00103         Math::Quaternion diff = _headRot.inverse() * lookAtQuat;
00104         float angle = 2 * acos(diff.w());
00105         if (diff.w() < 0.0f) {
00106             angle = 2 * (float)M_PI - angle;
00107         }
00108 
00109         float turnAmount = g_grim->getPerSecond(rate * ((float)M_PI / 180.0f));
00110         if (turnAmount < angle)
00111             _headRot = _headRot.slerpQuat(lookAtQuat, turnAmount / angle);
00112         else
00113             _headRot = lookAtQuat;
00114     }
00115 
00116     if (_headRot != Math::Quaternion()) { // If not identity..
00117         joint->_animMatrix = joint->_animMatrix * _headRot.toMatrix();
00118         joint->_animQuat = joint->_animQuat * _headRot;
00119         _cost->_emiSkel->_obj->commitAnim();
00120     }
00121 }
00122 
00123 void EMIHead::saveState(SaveGame *state) const {
00124     state->writeString(_jointName);
00125     state->writeVector3d(_offset);
00126     state->writeFloat(_headRot.x());
00127     state->writeFloat(_headRot.y());
00128     state->writeFloat(_headRot.z());
00129     state->writeFloat(_headRot.w());
00130     state->writeFloat(_yawRange);
00131     state->writeFloat(_minPitch);
00132     state->writeFloat(_maxPitch);
00133 }
00134 
00135 void EMIHead::restoreState(SaveGame *state) {
00136     if (state->saveMinorVersion() >= 15) {
00137         _jointName = state->readString();
00138         _offset = state->readVector3d();
00139         _headRot.x() = state->readFloat();
00140         _headRot.y() = state->readFloat();
00141         _headRot.z() = state->readFloat();
00142         _headRot.w() = state->readFloat();
00143         if (state->saveMinorVersion() >= 16) {
00144             _yawRange = state->readFloat();
00145             _minPitch = state->readFloat();
00146             _maxPitch = state->readFloat();
00147         }
00148     } else {
00149         state->readLESint32();
00150         state->readLESint32();
00151         state->readLESint32();
00152         state->readFloat();
00153         state->readFloat();
00154         state->readFloat();
00155         if (state->saveMinorVersion() < 2) {
00156             state->readFloat();
00157             state->readFloat();
00158         } else {
00159             for (int i = 0; i < 3; ++i) {
00160                 state->readFloat();
00161                 state->readFloat();
00162                 state->readFloat();
00163             }
00164         }
00165     }
00166 }
00167 
00168 } // end of namespace Grim


Generated on Sat Feb 23 2019 05:01:02 for ResidualVM by doxygen 1.7.1
curved edge   curved edge