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

head.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/model.h"
00024 #include "engines/grim/grim.h"
00025 #include "engines/grim/savegame.h"
00026 #include "engines/grim/costume/head.h"
00027 
00028 namespace Grim {
00029 
00030 Head::Joint::Joint() :
00031         _node(nullptr), _pitch(0.f), _yaw(0.f), _roll(0.f) {
00032 }
00033 
00034 void Head::Joint::init(ModelNode *node) {
00035     _node = node;
00036 }
00037 
00038 void Head::Joint::orientTowards(bool entering, const Math::Vector3d &point, float rate, const Math::Matrix4 &matrix,
00039                                 float maxPitch, float maxYaw, float maxRoll, float constrain) {
00040     float step = g_grim->getPerSecond(rate);
00041     float yawStep = step;
00042     float pitchStep = step / 3.0f;
00043     float rollStep = step / 3.0f;
00044 
00045     if (!_node)
00046         return;
00047 
00048     // Make sure we have up-to-date world transform matrices computed for the joint nodes of this character.
00049     _node->_needsUpdate = true;
00050     ModelNode *p = _node;
00051     while (p->_parent) {
00052         p = p->_parent;
00053         p->_needsUpdate = true;
00054     }
00055     p->setMatrix(matrix);
00056     p->update();
00057 
00058     Math::Vector3d modelFront; // the modeling convention for the forward direction.
00059     Math::Vector3d modelUp; // the modeling convention for the upward direction.
00060     Math::Vector3d frontDir; // Character front facing direction vector in world space (global scene coordinate space)
00061 
00062     // the character head coordinate frame is: +Y forward, +Z up, +X right.
00063     frontDir = Math::Vector3d(_node->_matrix(0, 1), _node->_matrix(1, 1), _node->_matrix(2, 1)); // Look straight ahead. (+Y)
00064     modelFront = Math::Vector3d(0, 1, 0);
00065     modelUp = Math::Vector3d(0, 0, 1);
00066 
00067     // v is the world space direction vector this character should be looking towards.
00068     Math::Vector3d targetDir = point - _node->_pivotMatrix.getPosition();
00069     if (!entering)
00070         targetDir = frontDir;
00071     if (targetDir.isZero())
00072         return;
00073 
00074     targetDir.normalize();
00075 
00076     // The vector v is in world space, so generate the world space lookat matrix for the desired head facing
00077     // orientation.
00078     Math::Matrix4 lookAtTM;
00079     lookAtTM.setToIdentity();
00080     const Math::Vector3d worldUp(0, 0, 1); // The Residual scene convention: +Z is world space up.
00081     if (Math::Vector3d::dotProduct(targetDir, worldUp) >= 0.98f) // Avoid singularity if trying to look straight up.
00082         lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, -frontDir); // Instead of orienting head towards scene up, orient head towards character "back",
00083                                                                     // i.e. when you look straight up, your head up vector tilts/arches to point straight backwards.
00084     else if (Math::Vector3d::dotProduct(targetDir, worldUp) <= -0.98f) // Avoid singularity if trying to look straight down.
00085         lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, frontDir); // Instead of orienting head towards scene down, orient head towards character "front",
00086                                                                    // i.e. when you look straight down, your head up vector tilts/arches to point straight forwards.
00087     else
00088         lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, worldUp);
00089     // The above specifies the world space orientation of this bone, but we need to output
00090     // the orientation in parent space (as yaw/pitch/roll).
00091 
00092     // Get the coordinate frame in which we need to produce the character head yaw/pitch/roll values.
00093     Math::Matrix4 parentWorldTM;
00094     if (_node->_parent)
00095         parentWorldTM = _node->_parent->_matrix;
00096 
00097     // While we could compute the desired lookat direction directly in the above coordinate frame,
00098     // it is preferrable to compute the lookat direction with respect to the head orientation in
00099     // the keyframe animation. This is because the LUA scripts specify the maximum head yaw, pitch and
00100     // roll values with respect to those keyframe animations. If the lookat was simply computed
00101     // directly in the space of the parent, we couldn't apply the head maxYaw/Pitch/Roll constraints
00102     // properly. So, compute the coordinate frame of this bone in the keyframe animation.
00103     Math::Matrix4 animFrame = _node->_localMatrix;
00104     parentWorldTM = parentWorldTM * animFrame;
00105     parentWorldTM.invertAffineOrthonormal();
00106 
00107     // Convert lookAtTM orientation from world space to parent-with-keyframe-animation space.
00108     lookAtTM = parentWorldTM * lookAtTM;
00109 
00110     // Decompose to yaw-pitch-roll (+Z, +X, +Y).
00111     // In this space, Yaw is +Z. Pitch is +X. Roll is +Y.
00112     Math::Angle y, pt, r;
00113     lookAtTM.getEuler(&y, &pt, &r, Math::EO_ZXY);
00114 
00115     y = y * constrain;
00116     pt = pt * constrain;
00117     r = r * constrain;
00118 
00119     // Constrain the maximum head movement, as desired by the game LUA scripts.
00120     y.clampDegrees(maxYaw);
00121     pt.clampDegrees(maxPitch);
00122     r.clampDegrees(maxRoll);
00123 
00124     // Also limit yaw, pitch and roll to make at most a movement as large as the given max step size during this frame.
00125     // This will produce a slow head-turning animation instead of immediately snapping to the
00126     // target lookat orientation.
00127     if (y - _yaw > yawStep)
00128         y = _yaw + yawStep;
00129     if (_yaw - y > yawStep)
00130         y = _yaw - yawStep;
00131 
00132     if (pt - _pitch > pitchStep)
00133         pt = _pitch + pitchStep;
00134     if (_pitch - pt > pitchStep)
00135         pt = _pitch - pitchStep;
00136 
00137     if (r - _roll > rollStep)
00138         r = _roll + rollStep;
00139     if (_roll - r > rollStep)
00140         r = _roll - rollStep;
00141 
00142     // Remember how far we animated the head this frame, and we'll continue from here the next frame.
00143     _pitch = pt;
00144     _yaw = y;
00145     _roll = r;
00146 
00147     // Assemble ypr to a quaternion.
00148     // This is the head orientation with respect to parent-with-keyframe-animation space.
00149     Math::Quaternion lookAtQuat = Math::Quaternion::fromEuler(y, pt, r, Math::EO_ZXY);
00150 
00151     _node->_animRot = _node->_animRot * lookAtQuat;
00152 }
00153 
00154 void Head::Joint::saveState(SaveGame *state) const {
00155     state->writeFloat(_pitch.getDegrees());
00156     state->writeFloat(_yaw.getDegrees());
00157     state->writeFloat(_roll.getDegrees());
00158 }
00159 
00160 void Head::Joint::restoreState(SaveGame *state) {
00161     _pitch = state->readFloat();
00162     _yaw = state->readFloat();
00163     _roll = state->readFloat();
00164 }
00165 
00166 Head::Head() :
00167     _maxPitch(0), _maxYaw(0), _maxRoll(0),
00168     _joint1Node(-1), _joint2Node(-1), _joint3Node(-1) {
00169 
00170 }
00171 
00172 void Head::setJoints(int joint1, int joint2, int joint3) {
00173     _joint1Node = joint1;
00174     _joint2Node = joint2;
00175     _joint3Node = joint3;
00176 }
00177 
00178 void Head::loadJoints(ModelNode *nodes) {
00179     if (_joint1Node >= 0 && _joint2Node >= 0 && _joint3Node >= 0 && nodes) {
00180         _joint1.init(nodes + _joint1Node);
00181         _joint2.init(nodes + _joint2Node);
00182         _joint3.init(nodes + _joint3Node);
00183     }
00184 }
00185 
00186 void Head::setMaxAngles(float maxPitch, float maxYaw, float maxRoll) {
00187     _maxRoll = maxRoll;
00188     _maxPitch = maxPitch;
00189     _maxYaw = maxYaw;
00190 }
00191 
00192 void Head::lookAt(bool entering, const Math::Vector3d &point, float rate, const Math::Matrix4 &matrix) {
00193     if (_joint1Node != -1) {
00194         // NOTE: By default, the _head.maxRoll for Manny's head is constrained to 165 degrees, which
00195         // comes in from the orignal Lua data scripts. (also, maxYaw == 80, maxPitch == 28).
00196         // The very small maxPitch angle, and a very large maxRoll angle causes problems when Manny
00197         // is trying to look straight up to an object, in which case the euler roll angles vary
00198         // wildly compared to the pitch angles, which get clamped to a much smaller interval. Therefore,
00199         // restrict the maximum roll angle to a smaller value than 165 degrees to avoid this behavior.
00200         // If you want to change this, good places to test are:
00201         // A) Year 1, outside the Department of Death, run/walk up & down the stairs, there's a sign
00202         //    right above the stairs, and Manny looks dead up.
00203         // B) Year 3, when Manny and Meche are imprisoned in the vault. Walk inside the room where Meche
00204         //    is in, to look straight up to the sprinklers.
00205 
00206         if (_joint1Node == _joint2Node && _joint1Node == _joint3Node) {
00207             // Most characters only have one head joint instead of three, so we can orient the head
00208             // with a single call.
00209             _joint3.orientTowards(entering, point, rate, matrix, _maxPitch, _maxYaw, 30.f, 1.0f);
00210         } else {
00211             // For characters like Manny, we'll have to orient each of the three head joints.
00212             _joint1.orientTowards(entering, point, rate / 3, matrix, _maxPitch / 3, _maxYaw / 3, 10.f, 0.333f);
00213             _joint2.orientTowards(entering, point, rate / 3, matrix, _maxPitch / 3, _maxYaw / 3, 10.f, 0.666f);
00214             _joint3.orientTowards(entering, point, rate / 3, matrix, _maxPitch / 3, _maxYaw / 3, 10.f, 1.000f);
00215         }
00216     }
00217 }
00218 
00219 void Head::saveState(SaveGame *state) const {
00220     state->writeLESint32(_joint1Node);
00221     state->writeLESint32(_joint2Node);
00222     state->writeLESint32(_joint3Node);
00223     state->writeFloat(_maxPitch);
00224     state->writeFloat(_maxYaw);
00225     state->writeFloat(_maxRoll);
00226 
00227     _joint1.saveState(state);
00228     _joint2.saveState(state);
00229     _joint3.saveState(state);
00230 }
00231 
00232 void Head::restoreState(SaveGame *state) {
00233     _joint1Node = state->readLESint32();
00234     _joint2Node = state->readLESint32();
00235     _joint3Node = state->readLESint32();
00236     _maxPitch = state->readFloat();
00237     _maxYaw = state->readFloat();
00238     _maxRoll = state->readFloat();
00239 
00240     if (state->saveMinorVersion() < 2) {
00241         state->readFloat();
00242         state->readFloat();
00243     } else {
00244         _joint1.restoreState(state);
00245         _joint2.restoreState(state);
00246         _joint3.restoreState(state);
00247     }
00248 }
00249 
00250 } // end of namespace Grim


Generated on Sat Feb 16 2019 05:00:54 for ResidualVM by doxygen 1.7.1
curved edge   curved edge