WME3D: Separate XMesh and skin mesh loader to much original code

This commit is contained in:
Paweł Kołodziejski 2023-02-12 15:58:52 +01:00
parent fc20af02d9
commit 2b24709eaf
No known key found for this signature in database
GPG Key ID: 0BDADC9E74440FF7
9 changed files with 858 additions and 560 deletions

View File

@ -26,6 +26,8 @@
*/
#include "engines/wintermute/base/gfx/xmaterial.h"
#include "engines/wintermute/base/gfx/xskinmesh_loader.h"
#include "engines/wintermute/base/gfx/skin_mesh_helper.h"
#include "graphics/opengl/system_headers.h"
@ -46,12 +48,16 @@ XMeshOpenGL::~XMeshOpenGL() {
//////////////////////////////////////////////////////////////////////////
bool XMeshOpenGL::render(XModel *model) {
if (_vertexData == nullptr) {
float *vertexData = _skinMesh->_mesh->_vertexData;
auto indexData = _skinMesh->_mesh->_indexData;
auto indexRanges = _skinMesh->_mesh->_indexRanges;
auto materialIndices = _skinMesh->_mesh->_materialIndices;
if (vertexData == nullptr) {
return false;
}
for (uint32 i = 0; i < _numAttrs; i++) {
int materialIndex = _materialIndices[i];
int materialIndex = materialIndices[i];
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, _materials[materialIndex]->_diffuse.data);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, _materials[materialIndex]->_diffuse.data);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, _materials[materialIndex]->_specular.data);
@ -73,12 +79,12 @@ bool XMeshOpenGL::render(XModel *model) {
if (textureEnable)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, kVertexComponentCount * sizeof(float), _vertexData + kPositionOffset);
glNormalPointer(GL_FLOAT, kVertexComponentCount * sizeof(float), _vertexData + kNormalOffset);
glVertexPointer(3, GL_FLOAT, XSkinMeshLoader::kVertexComponentCount * sizeof(float), vertexData + XSkinMeshLoader::kPositionOffset);
glNormalPointer(GL_FLOAT, XSkinMeshLoader::kVertexComponentCount * sizeof(float), vertexData + XSkinMeshLoader::kNormalOffset);
if (textureEnable)
glTexCoordPointer(2, GL_FLOAT, kVertexComponentCount * sizeof(float), _vertexData + kTextureCoordOffset);
glTexCoordPointer(2, GL_FLOAT, XSkinMeshLoader::kVertexComponentCount * sizeof(float), vertexData + XSkinMeshLoader::kTextureCoordOffset);
glDrawElements(GL_TRIANGLES, _indexRanges[i + 1] - _indexRanges[i], GL_UNSIGNED_SHORT, _indexData.data() + _indexRanges[i]);
glDrawElements(GL_TRIANGLES, indexRanges[i + 1] - indexRanges[i], GL_UNSIGNED_SHORT, indexData.data() + indexRanges[i]);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);

View File

@ -26,6 +26,8 @@
*/
#include "engines/wintermute/base/gfx/xmaterial.h"
#include "engines/wintermute/base/gfx/xskinmesh_loader.h"
#include "engines/wintermute/base/gfx/skin_mesh_helper.h"
#include "graphics/opengl/system_headers.h"
@ -50,12 +52,16 @@ XMeshOpenGLShader::~XMeshOpenGLShader() {
}
bool XMeshOpenGLShader::loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) {
auto indexData = _skinMesh->_mesh->_indexData;
float *vertexData = _skinMesh->_mesh->_vertexData;
uint32 vertexCount = _skinMesh->_mesh->_vertexCount;
if (XMesh::loadFromXData(filename, xobj, materialReferences)) {
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, 4 * kVertexComponentCount * _vertexCount, _vertexData, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, 4 * XSkinMeshLoader::kVertexComponentCount * vertexCount, vertexData, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 2 * _indexData.size(), _indexData.data(), GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 2 * indexData.size(), indexData.data(), GL_STATIC_DRAW);
return true;
}
@ -65,20 +71,23 @@ bool XMeshOpenGLShader::loadFromXData(const Common::String &filename, XFileData
//////////////////////////////////////////////////////////////////////////
bool XMeshOpenGLShader::render(XModel *model) {
if (_vertexData == nullptr) {
float *vertexData = _skinMesh->_mesh->_vertexData;
auto indexRanges = _skinMesh->_mesh->_indexRanges;
auto materialIndices = _skinMesh->_mesh->_materialIndices;
if (vertexData == nullptr) {
return false;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
_shader->enableVertexAttribute("position", _vertexBuffer, 3, GL_FLOAT, false, 4 * kVertexComponentCount, 4 * kPositionOffset);
_shader->enableVertexAttribute("texcoord", _vertexBuffer, 2, GL_FLOAT, false, 4 * kVertexComponentCount, 4 * kTextureCoordOffset);
_shader->enableVertexAttribute("normal", _vertexBuffer, 3, GL_FLOAT, false, 4 * kVertexComponentCount, 4 * kNormalOffset);
_shader->enableVertexAttribute("position", _vertexBuffer, 3, GL_FLOAT, false, 4 * XSkinMeshLoader::kVertexComponentCount, 4 * XSkinMeshLoader::kPositionOffset);
_shader->enableVertexAttribute("texcoord", _vertexBuffer, 2, GL_FLOAT, false, 4 * XSkinMeshLoader::kVertexComponentCount, 4 * XSkinMeshLoader::kTextureCoordOffset);
_shader->enableVertexAttribute("normal", _vertexBuffer, 3, GL_FLOAT, false, 4 * XSkinMeshLoader::kVertexComponentCount, 4 * XSkinMeshLoader::kNormalOffset);
_shader->use(true);
for (uint32 i = 0; i < _numAttrs; i++) {
int materialIndex = _materialIndices[i];
int materialIndex = materialIndices[i];
if (_materials[materialIndex]->getSurface()) {
glEnable(GL_TEXTURE_2D);
@ -93,8 +102,8 @@ bool XMeshOpenGLShader::render(XModel *model) {
_shader->setUniform("diffuse", diffuse);
_shader->setUniform("ambient", diffuse);
size_t offset = 2 * _indexRanges[i];
glDrawElements(GL_TRIANGLES, _indexRanges[i + 1] - _indexRanges[i], GL_UNSIGNED_SHORT, (void *)offset);
size_t offset = 2 * indexRanges[i];
glDrawElements(GL_TRIANGLES, indexRanges[i + 1] - indexRanges[i], GL_UNSIGNED_SHORT, (void *)offset);
}
glBindTexture(GL_TEXTURE_2D, 0);
@ -107,16 +116,18 @@ bool XMeshOpenGLShader::render(XModel *model) {
}
bool XMeshOpenGLShader::renderFlatShadowModel() {
if (_vertexData == nullptr) {
float *vertexData = _skinMesh->_mesh->_vertexData;
auto indexRanges = _skinMesh->_mesh->_indexRanges;
if (vertexData == nullptr) {
return false;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
_flatShadowShader->enableVertexAttribute("position", _vertexBuffer, 3, GL_FLOAT, false, 4 * kVertexComponentCount, 4 * kPositionOffset);
_flatShadowShader->enableVertexAttribute("position", _vertexBuffer, 3, GL_FLOAT, false, 4 * XSkinMeshLoader::kVertexComponentCount, 4 * XSkinMeshLoader::kPositionOffset);
_flatShadowShader->use(true);
glDrawElements(GL_TRIANGLES, _indexRanges.back(), GL_UNSIGNED_SHORT, 0);
glDrawElements(GL_TRIANGLES, indexRanges.back(), GL_UNSIGNED_SHORT, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@ -127,8 +138,11 @@ bool XMeshOpenGLShader::renderFlatShadowModel() {
bool XMeshOpenGLShader::update(FrameNode *parentFrame) {
XMesh::update(parentFrame);
float *vertexData = _skinMesh->_mesh->_vertexData;
uint32 vertexCount = _skinMesh->_mesh->_vertexCount;
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * kVertexComponentCount * _vertexCount, _vertexData);
glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * XSkinMeshLoader::kVertexComponentCount * vertexCount, vertexData);
return true;
}

View File

@ -0,0 +1,84 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM 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 program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on WME.
* http://dead-code.org/redir.php?target=wme
* Copyright (c) 2003-2013 Jan Nedoma and contributors
*/
#include "engines/wintermute/dcgf.h"
#include "engines/wintermute/base/gfx/skin_mesh_helper.h"
#include "engines/wintermute/base/gfx/xskinmesh_loader.h"
namespace Wintermute {
//////////////////////////////////////////////////////////////////////////
SkinMeshHelper::SkinMeshHelper(XSkinMeshLoader *mesh) {
_mesh = mesh;
}
//////////////////////////////////////////////////////////////////////////
SkinMeshHelper::~SkinMeshHelper() {
delete _mesh;
}
//////////////////////////////////////////////////////////////////////////
uint SkinMeshHelper::getNumFaces() {
return 0;//_mesh->getNumFaces();
}
//////////////////////////////////////////////////////////////////////////
uint SkinMeshHelper::getNumBones() {
return 0;//_mesh->getNumBones();
}
//////////////////////////////////////////////////////////////////////////
bool SkinMeshHelper::getOriginalMesh(XSkinMeshLoader **mesh) {
return true;//_mesh->cloneMeshFVF(_mesh->getOptions(), _mesh->getFVF(), mesh);
}
//////////////////////////////////////////////////////////////////////////
bool SkinMeshHelper::generateSkinnedMesh(uint32 options, float minWeight, uint32 *adjacencyOut, XSkinMeshLoader **mesh) {
bool res = getOriginalMesh(mesh);
/* if (res) {
(*mesh)->generateAdjacency(adjacencyOut);
}*/
return res;
}
//////////////////////////////////////////////////////////////////////////
bool SkinMeshHelper::updateSkinnedMesh(const Math::Matrix4 *boneTransforms, XSkinMeshLoader *mesh) {
return true;//_mesh->updateSkinnedMesh(boneTransforms);
}
//////////////////////////////////////////////////////////////////////////
const char *SkinMeshHelper::getBoneName(uint boneIndex) {
return "";//_mesh->getBoneName(boneIndex);
}
//////////////////////////////////////////////////////////////////////////
Math::Matrix4 SkinMeshHelper::getBoneOffsetMatrix(uint boneIndex) {
return Math::Matrix4();//_mesh->getBoneOffsetMatrix(boneIndex);
}
} // namespace Wintermute

View File

@ -0,0 +1,64 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM 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 program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on WME.
* http://dead-code.org/redir.php?target=wme
* Copyright (c) 2003-2013 Jan Nedoma and contributors
*/
#ifndef WINTERMUTE_SKIN_MESH_HELPER_H
#define WINTERMUTE_SKIN_MESH_HELPER_H
#include "math/matrix4.h"
#include "math/vector3d.h"
namespace Wintermute {
class XSkinMeshLoader;
class XMesh;
class XMeshOpenGL;
class XMeshOpenGLShader;
class SkinMeshHelper {
friend class XMesh;
friend class XMeshOpenGL;
friend class XMeshOpenGLShader;
public:
SkinMeshHelper(XSkinMeshLoader *mesh);
virtual ~SkinMeshHelper();
uint getNumFaces();
uint getNumBones();
bool getOriginalMesh(XSkinMeshLoader **mesh);
bool generateSkinnedMesh(uint32 options, float minWeight, uint32 *adjacencyOut, XSkinMeshLoader **mesh);
bool updateSkinnedMesh(const Math::Matrix4 *boneTransforms, XSkinMeshLoader *mesh);
const char *getBoneName(uint boneIndex);
Math::Matrix4 getBoneOffsetMatrix(uint boneIndex);
private:
XSkinMeshLoader *_mesh;
};
} // namespace Wintermute
#endif

View File

@ -28,6 +28,8 @@
#include "engines/wintermute/base/gfx/3dshadow_volume.h"
#include "engines/wintermute/base/gfx/xmaterial.h"
#include "engines/wintermute/base/gfx/xmesh.h"
#include "engines/wintermute/base/gfx/xskinmesh_loader.h"
#include "engines/wintermute/base/gfx/skin_mesh_helper.h"
#include "engines/wintermute/base/gfx/xframe_node.h"
#include "engines/wintermute/base/gfx/xfile_loader.h"
#include "engines/wintermute/base/gfx/xmodel.h"
@ -36,27 +38,18 @@
namespace Wintermute {
// define constant to make it available to the linker
const uint32 XMesh::kNullIndex;
XMesh::XMesh(Wintermute::BaseGame *inGame) : BaseNamedObject(inGame) {
_numAttrs = 0;
_skinMesh = nullptr;
_skinnedMesh = false;
_BBoxStart = Math::Vector3d(0.0f, 0.0f, 0.0f);
_BBoxEnd = Math::Vector3d(0.0f, 0.0f, 0.0f);
_vertexData = nullptr;
_vertexPositionData = nullptr;
_vertexNormalData = nullptr;
_vertexCount = 0;
}
XMesh::~XMesh() {
delete[] _vertexData;
delete[] _vertexPositionData;
delete[] _vertexNormalData;
delete _skinMesh;
_materials.clear();
}
@ -68,28 +61,31 @@ bool XMesh::loadFromXData(const Common::String &filename, XFileData *xobj, Commo
return false;
}
XMeshObject *mesh = xobj->getXMeshObject();
if (!mesh) {
XMeshObject *meshObject = xobj->getXMeshObject();
if (!meshObject) {
BaseEngine::LOG(0, "Error loading skin mesh");
return false;
}
_vertexCount = mesh->_numVertices;
XSkinMeshLoader *mesh = new XSkinMeshLoader(this);
_skinMesh = new SkinMeshHelper(mesh);
mesh->_vertexCount = meshObject->_numVertices;
// vertex format for .X meshes will be position + normals + textures
_vertexData = new float[kVertexComponentCount * _vertexCount]();
_vertexPositionData = new float[3 * _vertexCount]();
mesh->_vertexData = new float[XSkinMeshLoader::kVertexComponentCount * mesh->_vertexCount]();
mesh->_vertexPositionData = new float[3 * mesh->_vertexCount]();
// we already know how big this is supposed to be
// TODO: might have to generate normals if file does not contain any
_vertexNormalData = new float[3 * _vertexCount]();
mesh->_vertexNormalData = new float[3 * mesh->_vertexCount]();
parsePositionCoords(mesh);
mesh->parsePositionCoords(meshObject);
int faceCount = mesh->_numFaces;
int faceCount = meshObject->_numFaces;
Common::Array<int> indexCountPerFace;
parseFaces(mesh, faceCount, indexCountPerFace);
mesh->parseFaces(meshObject, faceCount, indexCountPerFace);
uint numChildren = 0;
xobj->getChildren(numChildren);
@ -100,11 +96,11 @@ bool XMesh::loadFromXData(const Common::String &filename, XFileData *xobj, Commo
if (xobj->getChild(i, xchildData)) {
if (xchildData.getType(objectType)) {
if (objectType == kXClassMeshTextureCoords) {
parseTextureCoords(&xchildData);
mesh->parseTextureCoords(&xchildData);
} else if (objectType == kXClassMeshNormals) {
parseNormalCoords(&xchildData);
mesh->parseNormalCoords(&xchildData);
} else if (objectType == kXClassMeshMaterialList) {
parseMaterials(&xchildData, faceCount, filename, materialReferences, indexCountPerFace);
mesh->parseMaterials(&xchildData, _gameRef, faceCount, filename, materialReferences, indexCountPerFace);
} else if (objectType == kXClassMaterial) {
Material *mat = new Material(_gameRef);
mat->loadFromX(&xchildData, filename);
@ -112,77 +108,33 @@ bool XMesh::loadFromXData(const Common::String &filename, XFileData *xobj, Commo
// one material = one index range
_numAttrs = 1;
_indexRanges.push_back(0);
_indexRanges.push_back(_indexData.size());
mesh->_indexRanges.push_back(0);
mesh->_indexRanges.push_back(mesh->_indexData.size());
} else if (objectType == kXClassSkinMeshHeader) {
int boneCount = xchildData.getXSkinMeshHeaderObject()->_nBones;
_skinnedMesh = boneCount > 0;
} else if (objectType == kXClassSkinWeights) {
_skinnedMesh = true;
parseSkinWeights(&xchildData);
mesh->parseSkinWeights(&xchildData);
} else if (objectType == kXClassDeclData) {
parseVertexDeclaration(&xchildData);
mesh->parseVertexDeclaration(&xchildData);
}
}
}
}
generateAdjacency();
mesh->generateAdjacency(_adjacency);
return true;
}
//////////////////////////////////////////////////////////////////////////
bool XMesh::generateAdjacency() {
_adjacency = Common::Array<uint32>(_indexData.size(), kNullIndex);
for (uint32 i = 0; i < _indexData.size() / 3; ++i) {
for (uint32 j = i + 1; j < _indexData.size() / 3; ++j) {
for (int edge1 = 0; edge1 < 3; ++edge1) {
uint16 index1 = _indexData[i * 3 + edge1];
uint16 index2 = _indexData[i * 3 + (edge1 + 1) % 3];
for (int edge2 = 0; edge2 < 3; ++edge2) {
uint16 index3 = _indexData[j * 3 + edge2];
uint16 index4 = _indexData[j * 3 + (edge2 + 1) % 3];
if (_adjacency[i * 3 + edge1] == kNullIndex && _adjacency[j * 3 + edge2] == kNullIndex && adjacentEdge(index1, index2, index3, index4)) {
_adjacency[i * 3 + edge1] = j;
_adjacency[j * 3 + edge2] = i;
break;
}
}
}
}
}
return true;
}
bool XMesh::adjacentEdge(uint16 index1, uint16 index2, uint16 index3, uint16 index4) {
Math::Vector3d vertex1(_vertexPositionData + 3 * index1);
Math::Vector3d vertex2(_vertexPositionData + 3 * index2);
Math::Vector3d vertex3(_vertexPositionData + 3 * index3);
Math::Vector3d vertex4(_vertexPositionData + 3 * index4);
// wme uses a function from the D3DX library, which takes in an epsilon for floating point comparison
// wme passes in zero, so we just do a direct comparison
if (vertex1 == vertex3 && vertex2 == vertex4) {
return true;
} else if (vertex1 == vertex4 && vertex2 == vertex3) {
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool XMesh::findBones(FrameNode *rootFrame) {
// normal meshes don't have bones
if (!_skinnedMesh) {
return true;
}
auto skinWeightsList = _skinMesh->_mesh->_skinWeightsList;
_boneMatrices.resize(skinWeightsList.size());
@ -201,10 +153,16 @@ bool XMesh::findBones(FrameNode *rootFrame) {
//////////////////////////////////////////////////////////////////////////
bool XMesh::update(FrameNode *parentFrame) {
if (_vertexData == nullptr) {
float *vertexData = _skinMesh->_mesh->_vertexData;
if (vertexData == nullptr) {
return false;
}
float *vertexPositionData = _skinMesh->_mesh->_vertexPositionData;
float *vertexNormalData = _skinMesh->_mesh->_vertexNormalData;
uint32 vertexCount = _skinMesh->_mesh->_vertexCount;
auto skinWeightsList = _skinMesh->_mesh->_skinWeightsList;
// update skinned mesh
if (_skinnedMesh) {
BaseArray<Math::Matrix4> finalBoneMatrices;
@ -217,9 +175,9 @@ bool XMesh::update(FrameNode *parentFrame) {
// the new vertex coordinates are the weighted sum of the product
// of the combined bone transformation matrices and the static pose coordinates
// to be able too add the weighted summands together, we reset everything to zero first
for (uint32 i = 0; i < _vertexCount; ++i) {
for (uint32 i = 0; i < vertexCount; ++i) {
for (int j = 0; j < 3; ++j) {
_vertexData[i * kVertexComponentCount + kPositionOffset + j] = 0.0f;
vertexData[i * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset + j] = 0.0f;
}
}
@ -231,12 +189,12 @@ bool XMesh::update(FrameNode *parentFrame) {
for (uint i = 0; i < skinWeightsList[boneIndex]._vertexIndices.size(); ++i) {
uint32 vertexIndex = skinWeightsList[boneIndex]._vertexIndices[i];
Math::Vector3d pos;
pos.setData(_vertexPositionData + vertexIndex * 3);
pos.setData(vertexPositionData + vertexIndex * 3);
finalBoneMatrices[boneIndex].transform(&pos, true);
pos *= skinWeightsList[boneIndex]._vertexWeights[i];
for (uint j = 0; j < 3; ++j) {
_vertexData[vertexIndex * kVertexComponentCount + kPositionOffset + j] += pos.getData()[j];
vertexData[vertexIndex * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset + j] += pos.getData()[j];
}
}
}
@ -248,9 +206,9 @@ bool XMesh::update(FrameNode *parentFrame) {
}
// reset so we can form the weighted sums
for (uint32 i = 0; i < _vertexCount; ++i) {
for (uint32 i = 0; i < vertexCount; ++i) {
for (int j = 0; j < 3; ++j) {
_vertexData[i * kVertexComponentCount + kNormalOffset + j] = 0.0f;
vertexData[i * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kNormalOffset + j] = 0.0f;
}
}
@ -258,24 +216,24 @@ bool XMesh::update(FrameNode *parentFrame) {
for (uint i = 0; i < skinWeightsList[boneIndex]._vertexIndices.size(); ++i) {
uint32 vertexIndex = skinWeightsList[boneIndex]._vertexIndices[i];
Math::Vector3d pos;
pos.setData(_vertexNormalData + vertexIndex * 3);
pos.setData(vertexNormalData + vertexIndex * 3);
finalBoneMatrices[boneIndex].transform(&pos, true);
pos *= skinWeightsList[boneIndex]._vertexWeights[i];
for (uint j = 0; j < 3; ++j) {
_vertexData[vertexIndex * kVertexComponentCount + kNormalOffset + j] += pos.getData()[j];
vertexData[vertexIndex * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kNormalOffset + j] += pos.getData()[j];
}
}
}
//updateNormals();
} else { // update static
for (uint32 i = 0; i < _vertexCount; ++i) {
Math::Vector3d pos(_vertexPositionData + 3 * i);
for (uint32 i = 0; i < vertexCount; ++i) {
Math::Vector3d pos(vertexPositionData + 3 * i);
parentFrame->getCombinedMatrix()->transform(&pos, true);
for (uint j = 0; j < 3; ++j) {
_vertexData[i * kVertexComponentCount + kPositionOffset + j] = pos.getData()[j];
vertexData[i * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset + j] = pos.getData()[j];
}
}
}
@ -287,7 +245,8 @@ bool XMesh::update(FrameNode *parentFrame) {
//////////////////////////////////////////////////////////////////////////
bool XMesh::updateShadowVol(ShadowVolume *shadow, Math::Matrix4 &modelMat, const Math::Vector3d &light, float extrusionDepth) {
if (_vertexData == nullptr) {
float *vertexData = _skinMesh->_mesh->_vertexData;
if (vertexData == nullptr) {
return false;
}
@ -298,17 +257,18 @@ bool XMesh::updateShadowVol(ShadowVolume *shadow, Math::Matrix4 &modelMat, const
uint32 numEdges = 0;
Common::Array<bool> isFront(_indexData.size() / 3, false);
auto indexData = _skinMesh->_mesh->_indexData;
Common::Array<bool> isFront(indexData.size() / 3, false);
// First pass : for each face, record if it is front or back facing the light
for (uint32 i = 0; i < _indexData.size() / 3; i++) {
uint16 index0 = _indexData[3 * i + 0];
uint16 index1 = _indexData[3 * i + 1];
uint16 index2 = _indexData[3 * i + 2];
for (uint32 i = 0; i < indexData.size() / 3; i++) {
uint16 index0 = indexData[3 * i + 0];
uint16 index1 = indexData[3 * i + 1];
uint16 index2 = indexData[3 * i + 2];
Math::Vector3d v0(_vertexData + index0 * kVertexComponentCount + kPositionOffset);
Math::Vector3d v1(_vertexData + index1 * kVertexComponentCount + kPositionOffset);
Math::Vector3d v2(_vertexData + index2 * kVertexComponentCount + kPositionOffset);
Math::Vector3d v0(vertexData + index0 * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset);
Math::Vector3d v1(vertexData + index1 * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset);
Math::Vector3d v2(vertexData + index2 * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset);
// Transform vertices or transform light?
Math::Vector3d vNormal = Math::Vector3d::crossProduct(v2 - v1, v1 - v0);
@ -321,32 +281,32 @@ bool XMesh::updateShadowVol(ShadowVolume *shadow, Math::Matrix4 &modelMat, const
}
// Allocate a temporary edge list
Common::Array<uint16> edges(_indexData.size() * 2, 0);
Common::Array<uint16> edges(indexData.size() * 2, 0);
// First pass : for each face, record if it is front or back facing the light
for (uint32 i = 0; i < _indexData.size() / 3; i++) {
for (uint32 i = 0; i < indexData.size() / 3; i++) {
if (isFront[i]) {
uint16 wFace0 = _indexData[3 * i + 0];
uint16 wFace1 = _indexData[3 * i + 1];
uint16 wFace2 = _indexData[3 * i + 2];
uint16 wFace0 = indexData[3 * i + 0];
uint16 wFace1 = indexData[3 * i + 1];
uint16 wFace2 = indexData[3 * i + 2];
uint32 adjacent0 = _adjacency[3 * i + 0];
uint32 adjacent1 = _adjacency[3 * i + 1];
uint32 adjacent2 = _adjacency[3 * i + 2];
if (adjacent0 == kNullIndex || isFront[adjacent0] == false) {
if (adjacent0 == XSkinMeshLoader::kNullIndex || isFront[adjacent0] == false) {
// add edge v0-v1
edges[2 * numEdges + 0] = wFace0;
edges[2 * numEdges + 1] = wFace1;
numEdges++;
}
if (adjacent1 == kNullIndex || isFront[adjacent1] == false) {
if (adjacent1 == XSkinMeshLoader::kNullIndex || isFront[adjacent1] == false) {
// add edge v1-v2
edges[2 * numEdges + 0] = wFace1;
edges[2 * numEdges + 1] = wFace2;
numEdges++;
}
if (adjacent2 == kNullIndex || isFront[adjacent2] == false) {
if (adjacent2 == XSkinMeshLoader::kNullIndex || isFront[adjacent2] == false) {
// add edge v2-v0
edges[2 * numEdges + 0] = wFace2;
edges[2 * numEdges + 1] = wFace0;
@ -356,8 +316,8 @@ bool XMesh::updateShadowVol(ShadowVolume *shadow, Math::Matrix4 &modelMat, const
}
for (uint32 i = 0; i < numEdges; i++) {
Math::Vector3d v1(_vertexData + edges[2 * i + 0] * kVertexComponentCount + kPositionOffset);
Math::Vector3d v2(_vertexData + edges[2 * i + 1] * kVertexComponentCount + kPositionOffset);
Math::Vector3d v1(vertexData + edges[2 * i + 0] * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset);
Math::Vector3d v2(vertexData + edges[2 * i + 1] * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset);
Math::Vector3d v3 = v1 - invLight * extrusionDepth;
Math::Vector3d v4 = v2 - invLight * extrusionDepth;
@ -375,23 +335,25 @@ bool XMesh::updateShadowVol(ShadowVolume *shadow, Math::Matrix4 &modelMat, const
//////////////////////////////////////////////////////////////////////////
bool XMesh::pickPoly(Math::Vector3d *pickRayOrig, Math::Vector3d *pickRayDir) {
if (_vertexData == nullptr) {
float *vertexData = _skinMesh->_mesh->_vertexData;
if (vertexData == nullptr) {
return false;
}
bool res = false;
for (uint16 i = 0; i < _indexData.size(); i += 3) {
uint16 index1 = _indexData[i + 0];
uint16 index2 = _indexData[i + 1];
uint16 index3 = _indexData[i + 2];
auto indexData = _skinMesh->_mesh->_indexData;
for (uint16 i = 0; i < indexData.size(); i += 3) {
uint16 index1 = indexData[i + 0];
uint16 index2 = indexData[i + 1];
uint16 index3 = indexData[i + 2];
Math::Vector3d v0;
v0.setData(&_vertexData[index1 * kVertexComponentCount + kPositionOffset]);
v0.setData(&vertexData[index1 * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset]);
Math::Vector3d v1;
v1.setData(&_vertexData[index2 * kVertexComponentCount + kPositionOffset]);
v1.setData(&vertexData[index2 * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset]);
Math::Vector3d v2;
v2.setData(&_vertexData[index3 * kVertexComponentCount + kPositionOffset]);
v2.setData(&vertexData[index3 * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset]);
if (isnan(v0.x()))
continue;
@ -444,405 +406,25 @@ bool XMesh::restoreDeviceObjects() {
}
if (_skinnedMesh) {
return generateAdjacency();
return _skinMesh->_mesh->generateAdjacency(_adjacency);
} else {
return true;
}
}
bool XMesh::parsePositionCoords(XMeshObject *mesh) {
for (uint i = 0; i < _vertexCount; ++i) {
_vertexPositionData[i * 3 + 0] = mesh->_vertices[i]._x;
_vertexPositionData[i * 3 + 1] = mesh->_vertices[i]._y;
_vertexPositionData[i * 3 + 2] = mesh->_vertices[i]._z;
for (int j = 0; j < 3; ++j) {
_vertexData[i * kVertexComponentCount + kPositionOffset + j] = _vertexPositionData[i * 3 + j];
}
_vertexPositionData[i * 3 + 2] *= -1.0f;
_vertexData[i * kVertexComponentCount + kPositionOffset + 2] *= -1.0f;
}
return true;
}
bool XMesh::parseFaces(XMeshObject *mesh, int faceCount, Common::Array<int> &indexCountPerFace) {
for (int i = 0; i < faceCount; ++i) {
XMeshFace *face = &mesh->_faces[i];
int indexCount = face->_numFaceVertexIndices;
if (indexCount == 3) {
uint16 index1 = face->_faceVertexIndices[0];
uint16 index2 = face->_faceVertexIndices[1];
uint16 index3 = face->_faceVertexIndices[2];
_indexData.push_back(index3);
_indexData.push_back(index2);
_indexData.push_back(index1);
indexCountPerFace.push_back(3);
} else if (indexCount == 4) {
uint16 index1 = face->_faceVertexIndices[0];
uint16 index2 = face->_faceVertexIndices[1];
uint16 index3 = face->_faceVertexIndices[2];
uint16 index4 = face->_faceVertexIndices[3];
_indexData.push_back(index3);
_indexData.push_back(index2);
_indexData.push_back(index1);
_indexData.push_back(index4);
_indexData.push_back(index3);
_indexData.push_back(index1);
indexCountPerFace.push_back(6);
} else {
warning("XMeshOpenGL::loadFromX faces with more than four vertices are not supported");
return false;
}
}
return true;
}
bool XMesh::parseTextureCoords(XFileData *xobj) {
XMeshTextureCoordsObject *texCoords = xobj->getXMeshTextureCoordsObject();
if (!texCoords)
return false;
// should be the same as _vertexCount
int textureCoordCount = texCoords->_numTextureCoords;
for (int i = 0; i < textureCoordCount; ++i) {
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 0] = texCoords->_textureCoords[i]._u;
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 1] = texCoords->_textureCoords[i]._v;
}
return true;
}
bool XMesh::parseNormalCoords(XFileData *xobj) {
XMeshNormalsObject *normals = xobj->getXMeshNormalsObject();
if (!normals)
return false;
// should be the same as _vertex count
uint vertexNormalCount = normals->_numNormals;
//assert(vertexNormalCount == _vertexCount);
Common::Array<float> vertexNormalData;
vertexNormalData.resize(3 * vertexNormalCount);
for (uint i = 0; i < vertexNormalCount; ++i) {
vertexNormalData[i * 3 + 0] = normals->_normals[i]._x;
vertexNormalData[i * 3 + 1] = normals->_normals[i]._y;
// mirror z coordinate to change to OpenGL coordinate system
vertexNormalData[i * 3 + 2] = -normals->_normals[i]._z;
}
uint faceNormalCount = normals->_numFaceNormals;
Common::Array<int> faceNormals;
for (uint i = 0; i < faceNormalCount; ++i) {
XMeshFace *normalFace = &normals->_faceNormals[i];
int indexCount = normalFace->_numFaceVertexIndices;
if (indexCount == 3) {
uint16 index1 = normalFace->_faceVertexIndices[0];
uint16 index2 = normalFace->_faceVertexIndices[1];
uint16 index3 = normalFace->_faceVertexIndices[2];
faceNormals.push_back(index3);
faceNormals.push_back(index2);
faceNormals.push_back(index1);
} else if (indexCount == 4) {
uint16 index1 = normalFace->_faceVertexIndices[0];
uint16 index2 = normalFace->_faceVertexIndices[1];
uint16 index3 = normalFace->_faceVertexIndices[2];
uint16 index4 = normalFace->_faceVertexIndices[3];
faceNormals.push_back(index3);
faceNormals.push_back(index2);
faceNormals.push_back(index1);
faceNormals.push_back(index4);
faceNormals.push_back(index3);
faceNormals.push_back(index1);
} else {
warning("XMeshOpenGL::loadFromX faces with more than four vertices are not supported");
return false;
}
}
assert(_indexData.size() == faceNormals.size());
for (uint i = 0; i < faceNormals.size(); ++i) {
uint16 vertexIndex = _indexData[i];
int normalIndex = faceNormals[i];
for (int j = 0; j < 3; ++j) {
_vertexData[vertexIndex * kVertexComponentCount + kNormalOffset + j] = vertexNormalData[3 * normalIndex + j];
_vertexNormalData[3 * vertexIndex + j] = vertexNormalData[3 * normalIndex + j];
}
}
return true;
}
bool XMesh::parseMaterials(XFileData *xobj, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace) {
XMeshMaterialListObject *materialList = xobj->getXMeshMaterialListObject();
if (!materialList)
return false;
// there can be unused materials inside a .X file
// so this piece of information is probably useless
// should be the same as faceCount
int faceMaterialCount = materialList->_numFaceIndexes;
assert(faceMaterialCount == faceCount);
_indexRanges.push_back(0);
int currentMaterialIndex = materialList->_faceIndexes[0];
_materialIndices.push_back(currentMaterialIndex);
int currentIndex = indexCountPerFace[0];
for (int i = 1; i < faceMaterialCount; ++i) {
int currentMaterialIndexTmp = materialList->_faceIndexes[i];
if (currentMaterialIndex != currentMaterialIndexTmp) {
currentMaterialIndex = currentMaterialIndexTmp;
_indexRanges.push_back(currentIndex);
_materialIndices.push_back(currentMaterialIndex);
}
currentIndex += indexCountPerFace[i];
}
_indexRanges.push_back(currentIndex);
_numAttrs = _indexRanges.size() - 1;
uint numChildren = 0;
xobj->getChildren(numChildren);
for (uint32 i = 0; i < numChildren; i++) {
XFileData xchildData;
XClassType objectType;
bool res = xobj->getChild(i, xchildData);
if (res) {
res = xchildData.getType(objectType);
if (res) {
if (xchildData.isReference()) {
Common::String materialReference;
xchildData.getName(materialReference);
for (uint32 j = 0; j < materialReferences.size(); j++) {
if (materialReferences[j]._name == materialReference) {
_materials.add(materialReferences[j]._material);
break;
}
}
} else if (objectType == kXClassMaterial) {
Material *mat = new Material(_gameRef);
mat->loadFromX(&xchildData, filename);
_materials.add(mat);
MaterialReference materialReference;
materialReference._material = mat;
materialReferences.push_back(materialReference);
}
}
}
}
return true;
}
bool XMesh::parseSkinWeights(XFileData *xobj) {
XSkinWeightsObject *skinWeights = xobj->getXSkinWeightsObject();
if (!skinWeights)
return false;
skinWeightsList.resize(skinWeightsList.size() + 1);
SkinWeights &currSkinWeights = skinWeightsList.back();
currSkinWeights._boneName = skinWeights->_transformNodeName;
int weightCount = skinWeights->_numWeights;
currSkinWeights._vertexIndices.resize(weightCount);
currSkinWeights._vertexWeights.resize(weightCount);
for (int i = 0; i < weightCount; ++i) {
currSkinWeights._vertexIndices[i] = skinWeights->_vertexIndices[i];
}
for (int i = 0; i < weightCount; ++i) {
currSkinWeights._vertexWeights[i] = skinWeights->_weights[i];
}
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
currSkinWeights._offsetMatrix(c, r) = skinWeights->_matrixOffset[r * 4 + c];
}
}
// mirror at orign
currSkinWeights._offsetMatrix(2, 3) *= -1.0f;
// mirror base vectors
currSkinWeights._offsetMatrix(2, 0) *= -1.0f;
currSkinWeights._offsetMatrix(2, 1) *= -1.0f;
// change handedness
currSkinWeights._offsetMatrix(0, 2) *= -1.0f;
currSkinWeights._offsetMatrix(1, 2) *= -1.0f;
return true;
}
bool XMesh::parseVertexDeclaration(XFileData *xobj) {
XDeclDataObject *declData = xobj->getXDeclDataObject();
if (!declData)
return false;
int vertexElementCount = declData->_numElements;
// size of a vertex measured in four byte blocks
int vertexSize = 0;
int normalOffset = -1;
int textureOffset = -1;
for (int i = 0; i < vertexElementCount; ++i) {
int type = declData->_elements[i]._type;
int method = declData->_elements[i]._method;
int usage = declData->_elements[i]._usage;
int usageIndex = declData->_elements[i]._usageIndex;
debug("Vertex Element: Type: %i, Method: %i, Usage: %i, Usage index: %i", type, method, usage, usageIndex);
// we only care about normal vectors and texture coords
switch (usage) {
case 3:
normalOffset = vertexSize;
break;
case 5:
textureOffset = vertexSize;
break;
}
// This is a first guess, based on
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/vertexelement
switch (type) {
case 0:
vertexSize += 1;
warning("D3DDECLTYPE_FLOAT1 encountered in .X model");
break;
case 1:
vertexSize += 2;
break;
case 2:
vertexSize += 3;
break;
case 3:
vertexSize += 4;
warning("D3DDECLTYPE_FLOAT4 encountered in .X model");
break;
case 4:
vertexSize += 1;
warning("D3DDECLTYPE_D3DCOLOR encountered in .X model");
break;
case 5:
vertexSize += 1;
warning("D3DDECLTYPE_UBYTE4 encountered in .X model");
break;
case 6:
vertexSize += 2;
warning("D3DDECLTYPE_SHORT2 encountered in .X model");
break;
case 7:
vertexSize += 4;
warning("D3DDECLTYPE_SHORT4 encountered in .X model");
break;
case 8:
vertexSize += 1;
warning("D3DDECLTYPE_UBYTE4N encountered in .X model");
break;
case 9:
vertexSize += 2;
warning("D3DDECLTYPE_SHORT2N encountered in .X model");
break;
case 10:
vertexSize += 4;
warning("D3DDECLTYPE_SHORT4N encountered in .X model");
break;
case 11:
vertexSize += 2;
warning("D3DDECLTYPE_USHORT2N encountered in .X model");
break;
case 12:
vertexSize += 4;
warning("D3DDECLTYPE_USHORT4N encountered in .X model");
break;
case 13:
vertexSize += 3;
warning("D3DDECLTYPE_UDEC3 encountered in .X model");
break;
case 14:
vertexSize += 3;
warning("D3DDECLTYPE_DEC3N encountered in .X model");
break;
case 15:
vertexSize += 2;
warning("D3DDECLTYPE_FLOAT16_2 encountered in .X model");
break;
case 16:
vertexSize += 4;
warning("D3DDECLTYPE_FLOAT16_4 encountered in .X model");
break;
default:
warning("Unknown type in vertex declaration encountered");
break;
}
}
int dataSize = declData->_numData;
Common::Array<uint32> data;
data.reserve(dataSize);
for (int i = 0; i < dataSize; ++i) {
data.push_back(declData->_data[i]);
}
assert(dataSize % vertexSize == 0);
assert(dataSize / vertexSize == static_cast<int>(_vertexCount));
for (uint i = 0; i < _vertexCount; ++i) {
if (normalOffset != -1) {
float *vertexNormalData = reinterpret_cast<float *>(data.data() + vertexSize * i + normalOffset);
for (int j = 0; j < 3; ++j) {
_vertexNormalData[3 * i + j] = vertexNormalData[j];
_vertexData[kVertexComponentCount * i + kNormalOffset + j] = vertexNormalData[j];
}
}
if (textureOffset != -1) {
float *vertexTextureCoordsData = reinterpret_cast<float *>(data.data() + vertexSize * i + textureOffset);
_vertexData[kVertexComponentCount * i + kTextureCoordOffset + 0] = vertexTextureCoordsData[0];
_vertexData[kVertexComponentCount * i + kTextureCoordOffset + 1] = vertexTextureCoordsData[1];
}
}
return true;
}
void XMesh::updateBoundingBox() {
if (_vertexData == nullptr || _vertexCount == 0) {
float *vertexData = _skinMesh->_mesh->_vertexData;
uint32 vertexCount = _skinMesh->_mesh->_vertexCount;
if (vertexData == nullptr || vertexCount == 0) {
return;
}
_BBoxStart.setData(&_vertexData[0 + kPositionOffset]);
_BBoxEnd.setData(&_vertexData[0 + kPositionOffset]);
for (uint16 i = 1; i < _vertexCount; ++i) {
_BBoxStart.setData(&vertexData[0 + XSkinMeshLoader::kPositionOffset]);
_BBoxEnd.setData(&vertexData[0 + XSkinMeshLoader::kPositionOffset]);
for (uint16 i = 1; i < vertexCount; ++i) {
Math::Vector3d v;
v.setData(&_vertexData[i * kVertexComponentCount + kPositionOffset]);
v.setData(&vertexData[i * XSkinMeshLoader::kVertexComponentCount + XSkinMeshLoader::kPositionOffset]);
_BBoxStart.x() = MIN(_BBoxStart.x(), v.x());
_BBoxStart.y() = MIN(_BBoxStart.y(), v.y());

View File

@ -40,19 +40,13 @@ namespace Wintermute {
class BaseSprite;
class FrameNode;
class Material;
class XModel;
class ShadowVolume;
class VideoTheoraPlayer;
class SkinMeshHelper;
struct XMeshObject;
struct SkinWeights {
Common::String _boneName;
Math::Matrix4 _offsetMatrix;
BaseArray<uint32> _vertexIndices;
BaseArray<float> _vertexWeights;
};
class XMesh : public BaseNamedObject {
friend class XSkinMeshLoader;
public:
XMesh(BaseGame *inGame);
virtual ~XMesh();
@ -76,47 +70,23 @@ public:
bool restoreDeviceObjects();
protected:
static const int kVertexComponentCount = 8;
static const int kPositionOffset = 5;
static const int kTextureCoordOffset = 0;
static const int kNormalOffset = 2;
// anything which does not fit into 16 bits would we fine
static const uint32 kNullIndex = 0xFFFFFFFF;
bool parsePositionCoords(XMeshObject *mesh);
bool parseFaces(XMeshObject *mesh, int faceCount, Common::Array<int> &indexCountPerFace);
bool parseTextureCoords(XFileData *xobj);
bool parseNormalCoords(XFileData *xobj);
bool parseMaterials(XFileData *xobj, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace);
bool parseSkinWeights(XFileData *xobj);
bool parseVertexDeclaration(XFileData *xobj);
void updateBoundingBox();
bool generateAdjacency();
bool adjacentEdge(uint16 index1, uint16 index2, uint16 index3, uint16 index4);
float *_vertexData;
float *_vertexPositionData;
float *_vertexNormalData;
uint32 _vertexCount;
Common::Array<uint16> _indexData;
BaseArray<Math::Matrix4 *> _boneMatrices;
BaseArray<SkinWeights> skinWeightsList;
Common::Array<uint32> _adjacency;
BaseArray<Material *> _materials;
BaseArray<int> _indexRanges;
BaseArray<int> _materialIndices;
uint32 _numAttrs;
// Wintermute3D used the ID3DXSKININFO interface
// we will only store, whether this mesh is skinned at all
// and factor out the necessary computations into some functions
bool _skinnedMesh;
SkinMeshHelper *_skinMesh;
BaseArray<Math::Matrix4 *> _boneMatrices;
Common::Array<uint32> _adjacency;
BaseArray<Material *> _materials;
};
} // namespace Wintermute

View File

@ -0,0 +1,480 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM 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 program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "engines/wintermute/base/gfx/3dshadow_volume.h"
#include "engines/wintermute/base/gfx/xmaterial.h"
#include "engines/wintermute/base/gfx/xskinmesh_loader.h"
#include "engines/wintermute/base/gfx/xframe_node.h"
#include "engines/wintermute/base/gfx/xfile_loader.h"
#include "engines/wintermute/base/gfx/xskinmesh_loader.h"
#include "engines/wintermute/base/gfx/xmodel.h"
#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/math/math_util.h"
namespace Wintermute {
// define constant to make it available to the linker
const uint32 XSkinMeshLoader::kNullIndex;
XSkinMeshLoader::XSkinMeshLoader(XMesh *mesh) {
_mesh = mesh;
_vertexData = nullptr;
_vertexPositionData = nullptr;
_vertexNormalData = nullptr;
_vertexCount = 0;
}
XSkinMeshLoader::~XSkinMeshLoader() {
delete[] _vertexData;
delete[] _vertexPositionData;
delete[] _vertexNormalData;
}
bool XSkinMeshLoader::parsePositionCoords(XMeshObject *mesh) {
for (uint i = 0; i < _vertexCount; ++i) {
_vertexPositionData[i * 3 + 0] = mesh->_vertices[i]._x;
_vertexPositionData[i * 3 + 1] = mesh->_vertices[i]._y;
_vertexPositionData[i * 3 + 2] = mesh->_vertices[i]._z;
for (int j = 0; j < 3; ++j) {
_vertexData[i * kVertexComponentCount + kPositionOffset + j] = _vertexPositionData[i * 3 + j];
}
_vertexPositionData[i * 3 + 2] *= -1.0f;
_vertexData[i * kVertexComponentCount + kPositionOffset + 2] *= -1.0f;
}
return true;
}
bool XSkinMeshLoader::parseFaces(XMeshObject *mesh, int faceCount, Common::Array<int> &indexCountPerFace) {
for (int i = 0; i < faceCount; ++i) {
XMeshFace *face = &mesh->_faces[i];
int indexCount = face->_numFaceVertexIndices;
if (indexCount == 3) {
uint16 index1 = face->_faceVertexIndices[0];
uint16 index2 = face->_faceVertexIndices[1];
uint16 index3 = face->_faceVertexIndices[2];
_indexData.push_back(index3);
_indexData.push_back(index2);
_indexData.push_back(index1);
indexCountPerFace.push_back(3);
} else if (indexCount == 4) {
uint16 index1 = face->_faceVertexIndices[0];
uint16 index2 = face->_faceVertexIndices[1];
uint16 index3 = face->_faceVertexIndices[2];
uint16 index4 = face->_faceVertexIndices[3];
_indexData.push_back(index3);
_indexData.push_back(index2);
_indexData.push_back(index1);
_indexData.push_back(index4);
_indexData.push_back(index3);
_indexData.push_back(index1);
indexCountPerFace.push_back(6);
} else {
warning("XMeshLoader::parseFaces faces with more than four vertices are not supported");
return false;
}
}
return true;
}
bool XSkinMeshLoader::parseTextureCoords(XFileData *xobj) {
XMeshTextureCoordsObject *texCoords = xobj->getXMeshTextureCoordsObject();
if (!texCoords)
return false;
// should be the same as _vertexCount
int textureCoordCount = texCoords->_numTextureCoords;
for (int i = 0; i < textureCoordCount; ++i) {
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 0] = texCoords->_textureCoords[i]._u;
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 1] = texCoords->_textureCoords[i]._v;
}
return true;
}
bool XSkinMeshLoader::parseNormalCoords(XFileData *xobj) {
XMeshNormalsObject *normals = xobj->getXMeshNormalsObject();
if (!normals)
return false;
// should be the same as _vertex count
uint vertexNormalCount = normals->_numNormals;
//assert(vertexNormalCount == _vertexCount);
Common::Array<float> vertexNormalData;
vertexNormalData.resize(3 * vertexNormalCount);
for (uint i = 0; i < vertexNormalCount; ++i) {
vertexNormalData[i * 3 + 0] = normals->_normals[i]._x;
vertexNormalData[i * 3 + 1] = normals->_normals[i]._y;
// mirror z coordinate to change to OpenGL coordinate system
vertexNormalData[i * 3 + 2] = -normals->_normals[i]._z;
}
uint faceNormalCount = normals->_numFaceNormals;
Common::Array<int> faceNormals;
for (uint i = 0; i < faceNormalCount; ++i) {
XMeshFace *normalFace = &normals->_faceNormals[i];
int indexCount = normalFace->_numFaceVertexIndices;
if (indexCount == 3) {
uint16 index1 = normalFace->_faceVertexIndices[0];
uint16 index2 = normalFace->_faceVertexIndices[1];
uint16 index3 = normalFace->_faceVertexIndices[2];
faceNormals.push_back(index3);
faceNormals.push_back(index2);
faceNormals.push_back(index1);
} else if (indexCount == 4) {
uint16 index1 = normalFace->_faceVertexIndices[0];
uint16 index2 = normalFace->_faceVertexIndices[1];
uint16 index3 = normalFace->_faceVertexIndices[2];
uint16 index4 = normalFace->_faceVertexIndices[3];
faceNormals.push_back(index3);
faceNormals.push_back(index2);
faceNormals.push_back(index1);
faceNormals.push_back(index4);
faceNormals.push_back(index3);
faceNormals.push_back(index1);
} else {
warning("XMeshLoader::parseNormalCoords faces with more than four vertices are not supported");
return false;
}
}
assert(_indexData.size() == faceNormals.size());
for (uint i = 0; i < faceNormals.size(); ++i) {
uint16 vertexIndex = _indexData[i];
int normalIndex = faceNormals[i];
for (int j = 0; j < 3; ++j) {
_vertexData[vertexIndex * kVertexComponentCount + kNormalOffset + j] = vertexNormalData[3 * normalIndex + j];
_vertexNormalData[3 * vertexIndex + j] = vertexNormalData[3 * normalIndex + j];
}
}
return true;
}
bool XSkinMeshLoader::parseMaterials(XFileData *xobj, BaseGame *inGame,
int faceCount, const Common::String &filename,
Common::Array<MaterialReference> &materialReferences,
const Common::Array<int> &indexCountPerFace) {
XMeshMaterialListObject *materialList = xobj->getXMeshMaterialListObject();
if (!materialList)
return false;
// there can be unused materials inside a .X file
// so this piece of information is probably useless
// should be the same as faceCount
int faceMaterialCount = materialList->_numFaceIndexes;
assert(faceMaterialCount == faceCount);
_indexRanges.push_back(0);
int currentMaterialIndex = materialList->_faceIndexes[0];
_materialIndices.push_back(currentMaterialIndex);
int currentIndex = indexCountPerFace[0];
for (int i = 1; i < faceMaterialCount; ++i) {
int currentMaterialIndexTmp = materialList->_faceIndexes[i];
if (currentMaterialIndex != currentMaterialIndexTmp) {
currentMaterialIndex = currentMaterialIndexTmp;
_indexRanges.push_back(currentIndex);
_materialIndices.push_back(currentMaterialIndex);
}
currentIndex += indexCountPerFace[i];
}
_indexRanges.push_back(currentIndex);
_mesh->_numAttrs = _indexRanges.size() - 1;
uint numChildren = 0;
xobj->getChildren(numChildren);
for (uint32 i = 0; i < numChildren; i++) {
XFileData xchildData;
XClassType objectType;
bool res = xobj->getChild(i, xchildData);
if (res) {
res = xchildData.getType(objectType);
if (res) {
if (xchildData.isReference()) {
Common::String materialReference;
xchildData.getName(materialReference);
for (uint32 j = 0; j < materialReferences.size(); j++) {
if (materialReferences[j]._name == materialReference) {
_mesh->_materials.add(materialReferences[j]._material);
break;
}
}
} else if (objectType == kXClassMaterial) {
Material *mat = new Material(inGame);
mat->loadFromX(&xchildData, filename);
_mesh->_materials.add(mat);
MaterialReference materialReference;
materialReference._material = mat;
materialReferences.push_back(materialReference);
}
}
}
}
return true;
}
bool XSkinMeshLoader::parseSkinWeights(XFileData *xobj) {
XSkinWeightsObject *skinWeights = xobj->getXSkinWeightsObject();
if (!skinWeights)
return false;
_skinWeightsList.resize(_skinWeightsList.size() + 1);
SkinWeights &currSkinWeights = _skinWeightsList.back();
currSkinWeights._boneName = skinWeights->_transformNodeName;
int weightCount = skinWeights->_numWeights;
currSkinWeights._vertexIndices.resize(weightCount);
currSkinWeights._vertexWeights.resize(weightCount);
for (int i = 0; i < weightCount; ++i) {
currSkinWeights._vertexIndices[i] = skinWeights->_vertexIndices[i];
}
for (int i = 0; i < weightCount; ++i) {
currSkinWeights._vertexWeights[i] = skinWeights->_weights[i];
}
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
currSkinWeights._offsetMatrix(c, r) = skinWeights->_matrixOffset[r * 4 + c];
}
}
// mirror at orign
currSkinWeights._offsetMatrix(2, 3) *= -1.0f;
// mirror base vectors
currSkinWeights._offsetMatrix(2, 0) *= -1.0f;
currSkinWeights._offsetMatrix(2, 1) *= -1.0f;
// change handedness
currSkinWeights._offsetMatrix(0, 2) *= -1.0f;
currSkinWeights._offsetMatrix(1, 2) *= -1.0f;
return true;
}
bool XSkinMeshLoader::parseVertexDeclaration(XFileData *xobj) {
XDeclDataObject *declData = xobj->getXDeclDataObject();
if (!declData)
return false;
int vertexElementCount = declData->_numElements;
// size of a vertex measured in four byte blocks
int vertexSize = 0;
int normalOffset = -1;
int textureOffset = -1;
for (int i = 0; i < vertexElementCount; ++i) {
int type = declData->_elements[i]._type;
int method = declData->_elements[i]._method;
int usage = declData->_elements[i]._usage;
int usageIndex = declData->_elements[i]._usageIndex;
debug(2, "Vertex Element: Type: %i, Method: %i, Usage: %i, Usage index: %i", type, method, usage, usageIndex);
// we only care about normal vectors and texture coords
switch (usage) {
case 3:
normalOffset = vertexSize;
break;
case 5:
textureOffset = vertexSize;
break;
}
// This is a first guess, based on
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/vertexelement
switch (type) {
case 0:
vertexSize += 1;
warning("D3DDECLTYPE_FLOAT1 encountered in .X model");
break;
case 1:
vertexSize += 2;
break;
case 2:
vertexSize += 3;
break;
case 3:
vertexSize += 4;
warning("D3DDECLTYPE_FLOAT4 encountered in .X model");
break;
case 4:
vertexSize += 1;
warning("D3DDECLTYPE_D3DCOLOR encountered in .X model");
break;
case 5:
vertexSize += 1;
warning("D3DDECLTYPE_UBYTE4 encountered in .X model");
break;
case 6:
vertexSize += 2;
warning("D3DDECLTYPE_SHORT2 encountered in .X model");
break;
case 7:
vertexSize += 4;
warning("D3DDECLTYPE_SHORT4 encountered in .X model");
break;
case 8:
vertexSize += 1;
warning("D3DDECLTYPE_UBYTE4N encountered in .X model");
break;
case 9:
vertexSize += 2;
warning("D3DDECLTYPE_SHORT2N encountered in .X model");
break;
case 10:
vertexSize += 4;
warning("D3DDECLTYPE_SHORT4N encountered in .X model");
break;
case 11:
vertexSize += 2;
warning("D3DDECLTYPE_USHORT2N encountered in .X model");
break;
case 12:
vertexSize += 4;
warning("D3DDECLTYPE_USHORT4N encountered in .X model");
break;
case 13:
vertexSize += 3;
warning("D3DDECLTYPE_UDEC3 encountered in .X model");
break;
case 14:
vertexSize += 3;
warning("D3DDECLTYPE_DEC3N encountered in .X model");
break;
case 15:
vertexSize += 2;
warning("D3DDECLTYPE_FLOAT16_2 encountered in .X model");
break;
case 16:
vertexSize += 4;
warning("D3DDECLTYPE_FLOAT16_4 encountered in .X model");
break;
default:
warning("Unknown type in vertex declaration encountered");
break;
}
}
int dataSize = declData->_numData;
Common::Array<uint32> data;
data.reserve(dataSize);
for (int i = 0; i < dataSize; ++i) {
data.push_back(declData->_data[i]);
}
assert(dataSize % vertexSize == 0);
assert(dataSize / vertexSize == static_cast<int>(_vertexCount));
for (uint i = 0; i < _vertexCount; ++i) {
if (normalOffset != -1) {
float *vertexNormalData = reinterpret_cast<float *>(data.data() + vertexSize * i + normalOffset);
for (int j = 0; j < 3; ++j) {
_vertexNormalData[3 * i + j] = vertexNormalData[j];
_vertexData[kVertexComponentCount * i + kNormalOffset + j] = vertexNormalData[j];
}
}
if (textureOffset != -1) {
float *vertexTextureCoordsData = reinterpret_cast<float *>(data.data() + vertexSize * i + textureOffset);
_vertexData[kVertexComponentCount * i + kTextureCoordOffset + 0] = vertexTextureCoordsData[0];
_vertexData[kVertexComponentCount * i + kTextureCoordOffset + 1] = vertexTextureCoordsData[1];
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool XSkinMeshLoader::generateAdjacency(Common::Array<uint32> &adjacency) {
adjacency = Common::Array<uint32>(_indexData.size(), XSkinMeshLoader::kNullIndex);
for (uint32 i = 0; i < _indexData.size() / 3; ++i) {
for (uint32 j = i + 1; j < _indexData.size() / 3; ++j) {
for (int edge1 = 0; edge1 < 3; ++edge1) {
uint16 index1 = _indexData[i * 3 + edge1];
uint16 index2 = _indexData[i * 3 + (edge1 + 1) % 3];
for (int edge2 = 0; edge2 < 3; ++edge2) {
uint16 index3 = _indexData[j * 3 + edge2];
uint16 index4 = _indexData[j * 3 + (edge2 + 1) % 3];
if (adjacency[i * 3 + edge1] == XSkinMeshLoader::kNullIndex && adjacency[j * 3 + edge2] == XSkinMeshLoader::kNullIndex && adjacentEdge(index1, index2, index3, index4)) {
adjacency[i * 3 + edge1] = j;
adjacency[j * 3 + edge2] = i;
break;
}
}
}
}
}
return true;
}
bool XSkinMeshLoader::adjacentEdge(uint16 index1, uint16 index2, uint16 index3, uint16 index4) {
Math::Vector3d vertex1(_vertexPositionData + 3 * index1);
Math::Vector3d vertex2(_vertexPositionData + 3 * index2);
Math::Vector3d vertex3(_vertexPositionData + 3 * index3);
Math::Vector3d vertex4(_vertexPositionData + 3 * index4);
// wme uses a function from the D3DX library, which takes in an epsilon for floating point comparison
// wme passes in zero, so we just do a direct comparison
if (vertex1 == vertex3 && vertex2 == vertex4) {
return true;
} else if (vertex1 == vertex4 && vertex2 == vertex3) {
return true;
}
return false;
}
} // namespace Wintermute

View File

@ -0,0 +1,96 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM 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 program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef WINTERMUTE_XSKINMESH_LOADER_H
#define WINTERMUTE_XSKINMESH_LOADER_H
#include "engines/wintermute/base/gfx/xmodel.h"
#include "math/matrix4.h"
#include "math/vector3d.h"
namespace Wintermute {
class Material;
class XModel;
class XMesh;
class ShadowVolume;
class VideoTheoraPlayer;
struct XMeshObject;
struct SkinWeights {
Common::String _boneName;
Math::Matrix4 _offsetMatrix;
BaseArray<uint32> _vertexIndices;
BaseArray<float> _vertexWeights;
};
class XSkinMeshLoader {
friend class XMesh;
friend class XMeshOpenGL;
friend class XMeshOpenGLShader;
public:
XSkinMeshLoader(XMesh *mesh);
virtual ~XSkinMeshLoader();
protected:
static const int kVertexComponentCount = 8;
static const int kPositionOffset = 5;
static const int kTextureCoordOffset = 0;
static const int kNormalOffset = 2;
// anything which does not fit into 16 bits would we fine
static const uint32 kNullIndex = 0xFFFFFFFF;
bool generateAdjacency(Common::Array<uint32> &adjacency);
bool adjacentEdge(uint16 index1, uint16 index2, uint16 index3, uint16 index4);
public:
bool parsePositionCoords(XMeshObject *mesh);
bool parseFaces(XMeshObject *mesh, int faceCount, Common::Array<int> &indexCountPerFace);
bool parseTextureCoords(XFileData *xobj);
bool parseNormalCoords(XFileData *xobj);
bool parseMaterials(XFileData *xobj, BaseGame *inGame, int faceCount, const Common::String &filename,
Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace);
bool parseSkinWeights(XFileData *xobj);
bool parseVertexDeclaration(XFileData *xobj);
protected:
float *_vertexData;
float *_vertexPositionData;
float *_vertexNormalData;
uint32 _vertexCount;
Common::Array<uint16> _indexData;
BaseArray<Math::Matrix4 *> _boneMatrices;
BaseArray<SkinWeights> _skinWeightsList;
BaseArray<int> _indexRanges;
BaseArray<int> _materialIndices;
XMesh *_mesh;
};
} // namespace Wintermute
#endif

View File

@ -165,6 +165,7 @@ MODULE_OBJS += \
base/gfx/3dmesh.o \
base/gfx/3dshadow_volume.o \
base/gfx/base_renderer3d.o \
base/gfx/skin_mesh_helper.o \
base/gfx/xactive_animation.o \
base/gfx/xanimation.o \
base/gfx/xanimation_channel.o \
@ -175,6 +176,7 @@ MODULE_OBJS += \
base/gfx/xmaterial.o \
base/gfx/xmesh.o \
base/gfx/xmodel.o \
base/gfx/xskinmesh_loader.o \
base/gfx/opengl/base_surface_opengl3d.o \
base/gfx/opengl/base_render_opengl3d.o \
base/gfx/opengl/base_render_opengl3d_shader.o \