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

modelemi.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 "common/endian.h"
00024 #include "common/foreach.h"
00025 #include "engines/grim/debug.h"
00026 #include "engines/grim/grim.h"
00027 #include "engines/grim/material.h"
00028 #include "engines/grim/gfx_base.h"
00029 #include "engines/grim/resource.h"
00030 #include "engines/grim/set.h"
00031 #include "engines/grim/emi/costumeemi.h"
00032 #include "engines/grim/emi/modelemi.h"
00033 #include "engines/grim/emi/animationemi.h"
00034 #include "engines/grim/emi/skeleton.h"
00035 
00036 namespace Grim {
00037 
00038 struct Vector3int {
00039     int _x;
00040     int _y;
00041     int _z;
00042     void setVal(int x, int y, int z) {
00043         _x = x; _y = y; _z = z;
00044     }
00045 };
00046 
00047 struct BoneInfo {
00048     int _incFac;
00049     int _joint;
00050     float _weight;
00051 };
00052 
00053 Common::String readLAString(Common::ReadStream *ms) {
00054     int strLength = ms->readUint32LE();
00055     char *readString = new char[strLength];
00056     ms->read(readString, strLength);
00057 
00058     Common::String retVal(readString);
00059     delete[] readString;
00060 
00061     return retVal;
00062 }
00063 
00064 void EMIMeshFace::loadFace(Common::SeekableReadStream *data) {
00065     _flags = data->readUint32LE();
00066     _hasTexture = data->readUint32LE();
00067 
00068     if (_hasTexture)
00069         _texID = data->readUint32LE();
00070     _faceLength = data->readUint32LE();
00071     _faceLength = _faceLength / 3;
00072     int x = 0, y = 0, z = 0;
00073     _indexes = new Vector3int[_faceLength];
00074     int j = 0;
00075     for (uint32 i = 0; i < _faceLength; i ++) {
00076         // FIXME: Are these ever going to be < 0 ?
00077         if (g_grim->getGamePlatform() == Common::kPlatformPS2) {
00078             x = data->readSint32LE();
00079             y = data->readSint32LE();
00080             z = data->readSint32LE();
00081         } else {
00082             x = data->readSint16LE();
00083             y = data->readSint16LE();
00084             z = data->readSint16LE();
00085         }
00086         _indexes[j++].setVal(x, y, z);
00087     }
00088 }
00089 
00090 EMIMeshFace::~EMIMeshFace() {
00091     delete[] _indexes;
00092 }
00093 
00094 void EMIModel::setTex(uint32 index) {
00095     if (index < _numTextures && _mats[index]) {
00096         _mats[index]->select();
00097         g_driver->setBlendMode(_texFlags[index] & BlendAdditive);
00098     }
00099 }
00100 
00101 void EMIModel::loadMesh(Common::SeekableReadStream *data) {
00102     //int strLength = 0; // Usefull for PS2-strings
00103 
00104     Common::String nameString = readLAString(data);
00105 
00106     for (uint l = 0; l < nameString.size(); ++l) {
00107         if (nameString[l] == '\\') {
00108             nameString.setChar('/', l);
00109         }
00110     }
00111     _meshName = nameString;
00112     _radius = data->readFloatLE();
00113     _center->readFromStream(data);
00114 
00115     _boxData->readFromStream(data);
00116     _boxData2->readFromStream(data);
00117 
00118     _numTexSets = data->readUint32LE();
00119     _setType = data->readUint32LE();
00120     _numTextures = data->readUint32LE();
00121 
00122     _texNames = new Common::String[_numTextures];
00123     _texFlags = new uint32[_numTextures];
00124 
00125     for (uint32 i = 0; i < _numTextures; i++) {
00126         _texNames[i] = readLAString(data);
00127         _texFlags[i] = data->readUint32LE();
00128         if (_texFlags[i] & ~(BlendAdditive)) {
00129             Debug::debug(Debug::Models, "Model %s has unknown flags (%d) for texture %s", nameString.c_str(), _texFlags[i], _texNames[i].c_str());
00130         }
00131     }
00132 
00133     prepareTextures();
00134 
00135     int type = data->readUint32LE();
00136     // Check that it is one of the known types
00137     //3  is no texture vertecies
00138     //18 is no normals
00139     //19 is regular
00140     assert(type == 19 || type == 18 || type == 3);
00141 
00142     _numVertices = data->readUint32LE();
00143 
00144     _lighting = new Math::Vector3d[_numVertices];
00145     for (int i = 0; i < _numVertices; i++) {
00146         _lighting[i].set(1.0f, 1.0f, 1.0f);
00147     }
00148 
00149     // Vertices
00150     _vertices = new Math::Vector3d[_numVertices];
00151     _drawVertices = new Math::Vector3d[_numVertices];
00152     for (int i = 0; i < _numVertices; i++) {
00153         _vertices[i].readFromStream(data);
00154         _drawVertices[i] = _vertices[i];
00155     }
00156     _normals = new Math::Vector3d[_numVertices];
00157     _drawNormals = new Math::Vector3d[_numVertices];
00158     if (type != 18) {
00159         for (int i = 0; i < _numVertices; i++) {
00160             _normals[i].readFromStream(data);
00161             _drawNormals[i] = _normals[i];
00162         }
00163     }
00164     _colorMap = new EMIColormap[_numVertices];
00165     for (int i = 0; i < _numVertices; ++i) {
00166         _colorMap[i].r = data->readByte();
00167         _colorMap[i].g = data->readByte();
00168         _colorMap[i].b = data->readByte();
00169         _colorMap[i].a = data->readByte();
00170     }
00171     if (type != 3) {
00172         _texVerts = new Math::Vector2d[_numVertices];
00173         for (int i = 0; i < _numVertices; i++) {
00174             _texVerts[i].readFromStream(data);
00175         }
00176     }
00177     // Faces
00178 
00179     _numFaces = data->readUint32LE();
00180     if (data->eos()) {
00181         _numFaces = 0;
00182         _faces = nullptr;
00183         return;
00184     }
00185 
00186     _faces = new EMIMeshFace[_numFaces];
00187 
00188     for (uint32 j = 0; j < _numFaces; j++) {
00189         _faces[j].setParent(this);
00190         _faces[j].loadFace(data);
00191     }
00192 
00193     int hasBones = data->readUint32LE();
00194 
00195     if (hasBones == 1) {
00196         _numBones = data->readUint32LE();
00197         _boneNames = new Common::String[_numBones];
00198         for (int i = 0; i < _numBones; i++) {
00199             _boneNames[i] = readLAString(data);
00200         }
00201 
00202         _numBoneInfos =  data->readUint32LE();
00203         _boneInfos = new BoneInfo[_numBoneInfos];
00204 
00205         for (int i = 0; i < _numBoneInfos; i++) {
00206             _boneInfos[i]._incFac = data->readUint32LE();
00207             _boneInfos[i]._joint = data->readUint32LE();
00208             _boneInfos[i]._weight = data->readFloatLE();
00209         }
00210     } else {
00211         _numBones = 0;
00212         _numBoneInfos = 0;
00213     }
00214     prepareForRender();
00215 }
00216 
00217 void EMIModel::setSkeleton(Skeleton *skel) {
00218     if (_skeleton == skel) {
00219         return;
00220     }
00221     _skeleton = skel;
00222     if (!skel || !_numBoneInfos) {
00223         return;
00224     }
00225     delete[] _vertexBoneInfo; _vertexBoneInfo = nullptr;
00226     _vertexBoneInfo = new int[_numBoneInfos];
00227     for (int i = 0; i < _numBoneInfos; i++) {
00228         _vertexBoneInfo[i] = _skeleton->findJointIndex(_boneNames[_boneInfos[i]._joint]);
00229     }
00230 }
00231 
00232 void EMIModel::prepareForRender() {
00233     if (!_skeleton || !_vertexBoneInfo)
00234         return;
00235 
00236     for (int i = 0; i < _numVertices; i++) {
00237         _drawVertices[i].set(0.0f, 0.0f, 0.0f);
00238         _drawNormals[i].set(0.0f, 0.0f, 0.0f);
00239     }
00240 
00241     int boneVert = -1;
00242     for (int i = 0; i < _numBoneInfos; i++) {
00243         if (_boneInfos[i]._incFac == 1) {
00244             boneVert++;
00245         }
00246 
00247         int jointIndex = _vertexBoneInfo[i];
00248         const Math::Matrix4 &jointMatrix = _skeleton->_joints[jointIndex]._finalMatrix;
00249         const Math::Matrix4 &bindPose = _skeleton->_joints[jointIndex]._absMatrix;
00250 
00251         Math::Vector3d vert = _vertices[boneVert];
00252         bindPose.inverseTranslate(&vert);
00253         bindPose.inverseRotate(&vert);
00254         jointMatrix.transform(&vert, true);
00255         _drawVertices[boneVert] += vert * _boneInfos[i]._weight;
00256 
00257         Math::Vector3d normal = _normals[boneVert];
00258         bindPose.inverseRotate(&normal);
00259         jointMatrix.transform(&normal, false);
00260         _drawNormals[boneVert] += normal * _boneInfos[i]._weight;
00261     }
00262 
00263     for (int i = 0; i < _numVertices; i++) {
00264         _drawNormals[i].normalize();
00265     }
00266 
00267     g_driver->updateEMIModel(this);
00268 }
00269 
00270 void EMIModel::prepareTextures() {
00271     _mats = new Material*[_numTextures];
00272     for (uint32 i = 0; i < _numTextures; i++) {
00273         _mats[i] = _costume->loadMaterial(_texNames[i], false);
00274     }
00275 }
00276 
00277 void EMIModel::draw() {
00278     prepareForRender();
00279 
00280     Actor *actor = _costume->getOwner();
00281     Math::Matrix4 modelToWorld = actor->getFinalMatrix();
00282 
00283     if (!actor->isInOverworld()) {
00284         Math::AABB bounds = calculateWorldBounds(modelToWorld);
00285         if (bounds.isValid() && !g_grim->getCurrSet()->getFrustum().isInside(bounds))
00286             return;
00287     }
00288 
00289     if (!g_driver->supportsShaders()) {
00290         // If shaders are not available, we calculate lighting in software.
00291         Actor::LightMode lightMode = actor->getLightMode();
00292         if (lightMode != Actor::LightNone) {
00293             if (lightMode != Actor::LightStatic)
00294                 _lightingDirty = true;
00295 
00296             if (_lightingDirty) {
00297                 updateLighting(modelToWorld);
00298                 _lightingDirty = false;
00299             }
00300         }
00301     } else {
00302         if (actor->getLightMode() == Actor::LightNone) {
00303             g_driver->disableLights();
00304         }
00305     }
00306     // We will need to add a call to the skeleton, to get the modified vertices, but for now,
00307     // I'll be happy with just static drawing
00308     for (uint32 i = 0; i < _numFaces; i++) {
00309         setTex(_faces[i]._texID);
00310         g_driver->drawEMIModelFace(this, &_faces[i]);
00311     }
00312 
00313     if (g_driver->supportsShaders() && actor->getLightMode() == Actor::LightNone) {
00314         g_driver->enableLights();
00315     }
00316 }
00317 
00318 void EMIModel::updateLighting(const Math::Matrix4 &modelToWorld) {
00319     // Current lighting implementation mimics the NormDyn mode of the original game, even if
00320     // FastDyn is requested. We assume that FastDyn mode was used only for the purpose of
00321     // performance optimization, but NormDyn mode is visually superior in all cases.
00322 
00323     Common::Array<Grim::Light *> activeLights;
00324     bool hasAmbient = false;
00325 
00326     Actor *actor = _costume->getOwner();
00327 
00328     foreach(Light *l, g_grim->getCurrSet()->getLights(actor->isInOverworld())) {
00329         if (l->_enabled) {
00330             activeLights.push_back(l);
00331             if (l->_type == Light::Ambient)
00332                 hasAmbient = true;
00333         }
00334     }
00335 
00336     for (int i = 0; i < _numVertices; i++) {
00337         Math::Vector3d &result = _lighting[i];
00338         result.set(0.0f, 0.0f, 0.0f);
00339 
00340         Math::Vector3d normal = _drawNormals[i];
00341         Math::Vector3d vertex = _drawVertices[i];
00342         modelToWorld.transform(&vertex, true);
00343         modelToWorld.transform(&normal, false);
00344 
00345         for (uint j = 0; j < activeLights.size(); ++j) {
00346             Light *l = activeLights[j];
00347             float shade = l->_intensity;
00348         
00349             if (l->_type != Light::Ambient) {
00350                 // Direction of incident light
00351                 Math::Vector3d dir = l->_dir;
00352 
00353                 if (l->_type != Light::Direct) {
00354                     dir = l->_pos - vertex;
00355                     float distSq = dir.getSquareMagnitude();
00356                     if (distSq > l->_falloffFar * l->_falloffFar)
00357                         continue;
00358 
00359                     dir.normalize();
00360 
00361                     if (distSq > l->_falloffNear * l->_falloffNear) {
00362                         float dist = sqrt(distSq);
00363                         float attn = 1.0f - (dist - l->_falloffNear) / (l->_falloffFar - l->_falloffNear);
00364                         shade *= attn;
00365                     }
00366                 }
00367 
00368                 if (l->_type == Light::Spot) {
00369                     float cosAngle = l->_dir.dotProduct(dir);
00370                     if (cosAngle < 0.0f)
00371                         continue;
00372 
00373                     float angle = acos(cosAngle);
00374                     if (angle > l->_penumbraangle)
00375                         continue;
00376 
00377                     if (angle > l->_umbraangle)
00378                         shade *= 1.0f - (angle - l->_umbraangle) / (l->_penumbraangle - l->_umbraangle);
00379                 }
00380 
00381                 float dot = MAX(0.0f, normal.dotProduct(dir));
00382                 shade *= dot;
00383             }
00384 
00385             Math::Vector3d color;
00386             color.x() = l->_color.getRed() / 255.0f;
00387             color.y() = l->_color.getGreen() / 255.0f;
00388             color.z() = l->_color.getBlue() / 255.0f;
00389 
00390             result += color * shade;
00391         }
00392 
00393         if (!hasAmbient) {
00394             // If the set does not specify an ambient light, a default ambient light is used
00395             // instead. The effect of this is visible for example in the set gmi.
00396             result += Math::Vector3d(0.5f, 0.5f, 0.5f);
00397         }
00398 
00399         float max = MAX(MAX(result.x(), result.y()), result.z());
00400         if (max > 1.0f) {
00401             result.x() = result.x() / max;
00402             result.y() = result.y() / max;
00403             result.z() = result.z() / max;
00404         }
00405     }
00406 }
00407 
00408 void EMIModel::getBoundingBox(int *x1, int *y1, int *x2, int *y2) const {
00409     int winX1, winY1, winX2, winY2;
00410     g_driver->getScreenBoundingBox(this, &winX1, &winY1, &winX2, &winY2);
00411     if (winX1 != -1 && winY1 != -1 && winX2 != -1 && winY2 != -1) {
00412         *x1 = MIN(*x1, winX1);
00413         *y1 = MIN(*y1, winY1);
00414         *x2 = MAX(*x2, winX2);
00415         *y2 = MAX(*y2, winY2);
00416     }
00417 }
00418 
00419 Math::AABB EMIModel::calculateWorldBounds(const Math::Matrix4 &matrix) const {
00420     Math::AABB bounds;
00421     for (int i = 0; i < _numVertices; i++) {
00422         bounds.expand(_drawVertices[i]);
00423     }
00424     bounds.transform(matrix);
00425     return bounds;
00426 }
00427 
00428 EMIModel::EMIModel(const Common::String &filename, Common::SeekableReadStream *data, EMICostume *costume) :
00429         _fname(filename), _costume(costume) {
00430     _meshAlphaMode = Actor::AlphaOff;
00431     _meshAlpha = 1.0;
00432     _numVertices = 0;
00433     _vertices = nullptr;
00434     _drawVertices = nullptr;
00435     _normals = nullptr;
00436     _drawNormals = nullptr;
00437     _colorMap = nullptr;
00438     _texVerts = nullptr;
00439     _numFaces = 0;
00440     _faces = nullptr;
00441     _numTextures = 0;
00442     _texNames = nullptr;
00443     _mats = nullptr;
00444     _numBones = 0;
00445     _boneInfos = nullptr;
00446     _numBoneInfos = 0;
00447     _vertexBoneInfo = nullptr;
00448     _skeleton = nullptr;
00449     _radius = 0;
00450     _center = new Math::Vector3d();
00451     _boxData = new Math::Vector3d();
00452     _boxData2 = new Math::Vector3d();
00453     _numTexSets = 0;
00454     _setType = 0;
00455     _boneNames = nullptr;
00456     _lighting = nullptr;
00457     _lightingDirty = true;
00458     _texFlags = nullptr;
00459 
00460     loadMesh(data);
00461     g_driver->createEMIModel(this);
00462 }
00463 
00464 EMIModel::~EMIModel() {
00465     g_driver->destroyEMIModel(this);
00466 
00467     delete[] _vertices;
00468     delete[] _drawVertices;
00469     delete[] _normals;
00470     delete[] _drawNormals;
00471     delete[] _colorMap;
00472     delete[] _texVerts;
00473     delete[] _faces;
00474     delete[] _texNames;
00475     delete[] _mats;
00476     delete[] _boneInfos;
00477     delete[] _vertexBoneInfo;
00478     delete[] _boneNames;
00479     delete[] _lighting;
00480     delete[] _texFlags;
00481     delete _center;
00482     delete _boxData;
00483     delete _boxData2;
00484 }
00485 
00486 } // end of namespace Grim


Generated on Sat Mar 23 2019 05:01:51 for ResidualVM by doxygen 1.7.1
curved edge   curved edge