/* ResidualVM - A 3D game interpreter * * ResidualVM 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 library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * */ #include "common/endian.h" #include "engines/grim/debug.h" #include "engines/grim/grim.h" #include "engines/grim/material.h" #include "engines/grim/gfx_base.h" #include "engines/grim/resource.h" #include "engines/grim/emi/modelemi.h" #include "engines/grim/emi/animationemi.h" #include "engines/grim/emi/skeleton.h" namespace Grim { struct Vector3int { int _x; int _y; int _z; void setVal(int x, int y, int z) { _x = x; _y = y; _z = z; } }; struct BoneInfo { int _incFac; int _joint; float _weight; }; Common::String readLAString(Common::ReadStream *ms) { int strLength = ms->readUint32LE(); char* readString = new char[strLength]; ms->read(readString, strLength); Common::String retVal(readString); delete[] readString; return retVal; } void EMIMeshFace::loadFace(Common::SeekableReadStream *data) { _flags = data->readUint32LE(); _hasTexture = data->readUint32LE(); if(_hasTexture) _texID = data->readUint32LE(); _faceLength = data->readUint32LE(); _faceLength = _faceLength / 3; int x = 0, y = 0, z = 0; _indexes = new Vector3int[_faceLength]; int j = 0; int readsize; if (g_grim->getGamePlatform() == Common::kPlatformPS2) { readsize = 4; } else { readsize = 2; } for (uint32 i = 0; i < _faceLength; i ++) { data->read((char *)&x, readsize); data->read((char *)&y, readsize); data->read((char *)&z, readsize); _indexes[j++].setVal(x,y,z); } } EMIMeshFace::~EMIMeshFace() { delete[] _indexes; } void EMIModel::setTex(int index) { _mats[index]->select(); } // May be removed when I get through the conversion void EMIMeshFace::render() { if(_hasTexture) { _parent->setTex(_texID); } //glDrawElements(GL_TRIANGLES, _faceLength * 3, GL_UNSIGNED_INT, _indexes); } void EMIModel::loadMesh(Common::SeekableReadStream *data) { //int strLength = 0; // Usefull for PS2-strings Common::String nameString = readLAString(data); _sphereData->readFromStream(data); _boxData->readFromStream(data); _boxData2->readFromStream(data); _numTexSets = data->readUint32LE(); _setType = data->readUint32LE(); _numTextures = data->readUint32LE(); _texNames = new Common::String[_numTextures]; for(uint32 i = 0;i < _numTextures; i++) { _texNames[i] = readLAString(data); // Every texname seems to be followed by 4 0-bytes (Ref mk1.mesh, // this is intentional) data->skip(4); } // 4 unknown bytes - usually with value 19 data->skip(4); _numVertices = data->readUint32LE(); // Vertices _vertices = new Math::Vector3d[_numVertices]; _drawVertices = new Math::Vector3d[_numVertices]; for (int i = 0; i < _numVertices; i++) { _vertices[i].readFromStream(data); _drawVertices[i] = _vertices[i]; } _normals = new Math::Vector3d[_numVertices]; for (int i = 0; i < _numVertices; i++) { _normals[i].readFromStream(data); } _colorMap = new EMIColormap[_numVertices]; for (int i = 0; i < _numVertices; ++i) { _colorMap[i].r = data->readByte(); _colorMap[i].g = data->readByte(); _colorMap[i].b = data->readByte(); _colorMap[i].a = data->readByte(); } _texVerts = new Math::Vector2d[_numVertices]; for (int i = 0; i < _numVertices; i++) { _texVerts[i].readFromStream(data); } // Faces _numFaces = data->readUint32LE(); // Handle the empty-faced fx/screen?.mesh-files if (data->eos()) { _numFaces = 0; _faces = NULL; return; } _faces = new EMIMeshFace[_numFaces]; for(uint32 j = 0;j < _numFaces; j++) { _faces[j].setParent(this); _faces[j].loadFace(data); } int hasBones = data->readUint32LE(); if (hasBones == 1) { _numBones = data->readUint32LE(); _bones = new Bone[_numBones]; _boneNames = new Common::String[_numBones]; for(int i = 0;i < _numBones; i++) { _bones[i]._boneName = readLAString(data); _boneNames[i] = _bones[i]._boneName; } _numBoneInfos = data->readUint32LE(); _boneInfos = new BoneInfo[_numBoneInfos]; for(int i = 0;i < _numBoneInfos; i++) { _boneInfos[i]._incFac = data->readUint32LE(); _boneInfos[i]._joint = data->readUint32LE(); _boneInfos[i]._weight = data->readUint32LE(); } } else { _numBones = 0; _numBoneInfos = 0; } prepare(); // <- Initialize materials etc. } void EMIModel::setSkeleton(Skeleton *skel) { _skeleton = skel; int boneVert = 0; delete[] _vertexBoneInfo; _vertexBoneInfo = NULL; delete[] _vertexBone; _vertexBone = NULL; _vertexBoneInfo = new int[_numBoneInfos]; _vertexBone = new int[_numBoneInfos]; // Oversized, but yeah. for (int i = 0; i < _numBoneInfos; i++) { _vertexBoneInfo[i] = _skeleton->findJointIndex(_boneNames[_boneInfos[i]._joint], _skeleton->_numJoints); if (_boneInfos[i]._incFac == 1) { _vertexBone[boneVert] = i; boneVert++; } } Math::Vector3d vertex; Math::Matrix4 mat; for (int i = 0; i < _numVertices; i++) { vertex = _vertices[i]; if (_vertexBoneInfo[_vertexBone[i]] != -1) { mat = _skeleton->_joints[_vertexBoneInfo[_vertexBone[i]]]._absMatrix; // TODO, add in math (inverse translate & rotate the vertex by the matrix) } _vertices[i] = vertex; } } void EMIModel::prepareForRender() { if (!_skeleton) return; for (int i = 0; i < _numVertices; i++) { _drawVertices[i] = _vertices[i]; int animIndex = _vertexBoneInfo[_vertexBone[i]]; _skeleton->_joints[animIndex]._finalMatrix.transform(_drawVertices + i, true); } } void EMIModel::prepare() { _mats = new Material*[_numTextures]; for (uint32 i = 0; i < _numTextures; i++) { // HACK: As we dont know what specialty-textures are yet, we skip loading them if (!_texNames[i].contains("specialty")) _mats[i] = g_resourceloader->loadMaterial(_texNames[i].c_str(), NULL); } prepareForRender(); } void EMIModel::draw() { prepareForRender(); // We will need to add a call to the skeleton, to get the modified vertices, but for now, // I'll be happy with just static drawing for(uint32 i = 0; i < _numFaces; i++) { _faces[i].render(); g_driver->drawEMIModelFace(this, &_faces[i]); } } EMIModel::EMIModel(const Common::String &filename, Common::SeekableReadStream *data, EMIModel *parent) : _fname(filename) { _numVertices = 0; _vertices = NULL; _drawVertices = NULL; _normals = NULL; _colorMap = NULL; _texVerts = NULL; _numFaces = 0; _faces = NULL; _numTextures = 0; _texNames = NULL; _mats = NULL; _numBones = 0; _bones = NULL; _boneInfos = NULL; _numBoneInfos = 0; _vertexBoneInfo = NULL; _vertexBone = NULL; _skeleton = NULL; _sphereData = new Math::Vector4d(); _boxData = new Math::Vector3d(); _boxData2 = new Math::Vector3d(); _numTexSets = 0; _setType = 0; loadMesh(data); } EMIModel::~EMIModel() { delete[] _vertices; delete[] _drawVertices; delete[] _normals; delete[] _colorMap; delete[] _texVerts; delete[] _faces; delete[] _texNames; delete[] _mats; delete[] _bones; delete[] _boneInfos; delete[] _vertexBone; delete[] _vertexBoneInfo; delete _sphereData; delete _boxData; delete _boxData2; } } // end of namespace Grim