Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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;
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));
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
00076 Math::Matrix4 lookAtTM;
00077 lookAtTM.setToIdentity();
00078
00079 if (Math::Vector3d::dotProduct(targetDir, worldUp) >= 0.98f)
00080 lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, -frontDir);
00081 else if (Math::Vector3d::dotProduct(targetDir, worldUp) <= -0.98f)
00082 lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, frontDir);
00083 else
00084 lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, worldUp);
00085
00086
00087 lookAtTM = worldToJoint * lookAtTM;
00088
00089
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()) {
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 }