[Scummvm-git-logs] scummvm master -> 2b24709eafa9c6a2bd4b8626e46509e514cce223

aquadran noreply at scummvm.org
Sun Feb 12 14:58:58 UTC 2023


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

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


Commit: 2b24709eafa9c6a2bd4b8626e46509e514cce223
    https://github.com/scummvm/scummvm/commit/2b24709eafa9c6a2bd4b8626e46509e514cce223
Author: Paweł Kołodziejski (aquadran at gmail.com)
Date: 2023-02-12T15:58:52+01:00

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

Changed paths:
  A engines/wintermute/base/gfx/skin_mesh_helper.cpp
  A engines/wintermute/base/gfx/skin_mesh_helper.h
  A engines/wintermute/base/gfx/xskinmesh_loader.cpp
  A engines/wintermute/base/gfx/xskinmesh_loader.h
    engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
    engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
    engines/wintermute/base/gfx/xmesh.cpp
    engines/wintermute/base/gfx/xmesh.h
    engines/wintermute/module.mk


diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp b/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
index f9f451a2925..35620f66908 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
@@ -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);
diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
index b2c14985c8c..031b2466547 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
@@ -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;
 }
diff --git a/engines/wintermute/base/gfx/skin_mesh_helper.cpp b/engines/wintermute/base/gfx/skin_mesh_helper.cpp
new file mode 100644
index 00000000000..9f95c6be3ca
--- /dev/null
+++ b/engines/wintermute/base/gfx/skin_mesh_helper.cpp
@@ -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
diff --git a/engines/wintermute/base/gfx/skin_mesh_helper.h b/engines/wintermute/base/gfx/skin_mesh_helper.h
new file mode 100644
index 00000000000..a8e847bd034
--- /dev/null
+++ b/engines/wintermute/base/gfx/skin_mesh_helper.h
@@ -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
diff --git a/engines/wintermute/base/gfx/xmesh.cpp b/engines/wintermute/base/gfx/xmesh.cpp
index ebcdc0b2062..7161944108b 100644
--- a/engines/wintermute/base/gfx/xmesh.cpp
+++ b/engines/wintermute/base/gfx/xmesh.cpp
@@ -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();
-
-	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;
-					}
-				}
-			}
-		}
-	}
+	mesh->generateAdjacency(_adjacency);
 
 	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());
diff --git a/engines/wintermute/base/gfx/xmesh.h b/engines/wintermute/base/gfx/xmesh.h
index 17542d61142..48349e4d3cf 100644
--- a/engines/wintermute/base/gfx/xmesh.h
+++ b/engines/wintermute/base/gfx/xmesh.h
@@ -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);
+	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;
 
-	float *_vertexData;
-	float *_vertexPositionData;
-	float *_vertexNormalData;
-	uint32 _vertexCount;
-	Common::Array<uint16> _indexData;
+	SkinMeshHelper *_skinMesh;
 
 	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;
 };
 
 } // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xskinmesh_loader.cpp b/engines/wintermute/base/gfx/xskinmesh_loader.cpp
new file mode 100644
index 00000000000..87c2862fb40
--- /dev/null
+++ b/engines/wintermute/base/gfx/xskinmesh_loader.cpp
@@ -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
diff --git a/engines/wintermute/base/gfx/xskinmesh_loader.h b/engines/wintermute/base/gfx/xskinmesh_loader.h
new file mode 100644
index 00000000000..5beea158374
--- /dev/null
+++ b/engines/wintermute/base/gfx/xskinmesh_loader.h
@@ -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
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
index 2e378d266c0..903fbe0a31d 100644
--- a/engines/wintermute/module.mk
+++ b/engines/wintermute/module.mk
@@ -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 \




More information about the Scummvm-git-logs mailing list