[Scummvm-git-logs] scummvm master -> 9d26cb597d23975fae9326ef019520032c283b4c

aquadran noreply at scummvm.org
Wed Oct 2 19:44:57 UTC 2024


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:
9d26cb597d WINTERMUTE: WME3D: Switch skin and mesh code from core to behind API


Commit: 9d26cb597d23975fae9326ef019520032c283b4c
    https://github.com/scummvm/scummvm/commit/9d26cb597d23975fae9326ef019520032c283b4c
Author: Paweł Kołodziejski (aquadran at gmail.com)
Date: 2024-10-02T21:44:52+02:00

Commit Message:
WINTERMUTE: WME3D: Switch skin and mesh code from core to behind API

Changed paths:
  A engines/wintermute/base/gfx/xbuffer.h
  A engines/wintermute/base/gfx/xmath.cpp
  A engines/wintermute/base/gfx/xmath.h
  A engines/wintermute/base/gfx/xskinmesh.cpp
  A engines/wintermute/base/gfx/xskinmesh.h
    engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
    engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
    engines/wintermute/base/gfx/skin_mesh_helper.cpp
    engines/wintermute/base/gfx/skin_mesh_helper.h
    engines/wintermute/base/gfx/xfile_loader.cpp
    engines/wintermute/base/gfx/xfile_loader.h
    engines/wintermute/base/gfx/xframe_node.cpp
    engines/wintermute/base/gfx/xmaterial.cpp
    engines/wintermute/base/gfx/xmaterial.h
    engines/wintermute/base/gfx/xmesh.cpp
    engines/wintermute/base/gfx/xskinmesh_loader.cpp
    engines/wintermute/base/gfx/xskinmesh_loader.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 35620f66908..fe531eb34e3 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
@@ -58,11 +58,11 @@ bool XMeshOpenGL::render(XModel *model) {
 
 	for (uint32 i = 0; i < _numAttrs; 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);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, _materials[materialIndex]->_emissive.data);
-		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, _materials[materialIndex]->_shininess);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, _materials[materialIndex]->_material._diffuse._data);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, _materials[materialIndex]->_material._diffuse._data);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, _materials[materialIndex]->_material._specular._data);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, _materials[materialIndex]->_material._emissive._data);
+		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, _materials[materialIndex]->_material._power);
 
 		bool textureEnable = false;
 		if (_materials[materialIndex]->getSurface()) {
diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
index 205ab05ec28..a09605cd3ff 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
@@ -98,7 +98,7 @@ bool XMeshOpenGLShader::render(XModel *model) {
 		}
 
 		// wme does not seem to care about specular or emissive light values
-		Math::Vector4d diffuse(_materials[materialIndex]->_diffuse.data);
+		Math::Vector4d diffuse(_materials[materialIndex]->_material._diffuse._data);
 		_shader->setUniform("diffuse", diffuse);
 		_shader->setUniform("ambient", diffuse);
 
diff --git a/engines/wintermute/base/gfx/skin_mesh_helper.cpp b/engines/wintermute/base/gfx/skin_mesh_helper.cpp
index 16996230d4d..6bd472ba056 100644
--- a/engines/wintermute/base/gfx/skin_mesh_helper.cpp
+++ b/engines/wintermute/base/gfx/skin_mesh_helper.cpp
@@ -28,23 +28,28 @@
 #include "engines/wintermute/dcgf.h"
 #include "engines/wintermute/base/gfx/skin_mesh_helper.h"
 #include "engines/wintermute/base/gfx/xskinmesh_loader.h"
+#include "engines/wintermute/base/gfx/xskinmesh.h"
 #include "engines/wintermute/base/gfx/xfile_loader.h"
 
 namespace Wintermute {
 
 //////////////////////////////////////////////////////////////////////////
-SkinMeshHelper::SkinMeshHelper(XSkinMeshLoader *mesh) {
-	_mesh = mesh;
+SkinMeshHelper::SkinMeshHelper(XSkinMeshLoader *meshLoader, DXMesh *mesh, DXSkinInfo *skinInfo) {
+	_mesh = meshLoader;
+	_dxmesh = mesh;
+	_skinInfo = skinInfo;
 }
 
 //////////////////////////////////////////////////////////////////////////
 SkinMeshHelper::~SkinMeshHelper() {
 	delete _mesh;
+	delete _dxmesh;
+	delete _skinInfo;
 }
 
 //////////////////////////////////////////////////////////////////////////
 uint SkinMeshHelper::getNumFaces() {
-	return _mesh->_meshObject->_numFaces;
+	return _dxmesh->getNumFaces();
 }
 
 //////////////////////////////////////////////////////////////////////////
diff --git a/engines/wintermute/base/gfx/skin_mesh_helper.h b/engines/wintermute/base/gfx/skin_mesh_helper.h
index a8e847bd034..275d7c5723e 100644
--- a/engines/wintermute/base/gfx/skin_mesh_helper.h
+++ b/engines/wintermute/base/gfx/skin_mesh_helper.h
@@ -37,6 +37,8 @@ class XSkinMeshLoader;
 class XMesh;
 class XMeshOpenGL;
 class XMeshOpenGLShader;
+class DXMesh;
+class DXSkinInfo;
 
 class SkinMeshHelper {
 	friend class XMesh;
@@ -44,9 +46,9 @@ class SkinMeshHelper {
 	friend class XMeshOpenGLShader;
 
 public:
-	SkinMeshHelper(XSkinMeshLoader *mesh);
+	SkinMeshHelper(XSkinMeshLoader *meshLoader, DXMesh *mesh, DXSkinInfo *skinInfo);
 	virtual ~SkinMeshHelper();
-	
+
 	uint getNumFaces();
 	uint getNumBones();
 	bool getOriginalMesh(XSkinMeshLoader **mesh);
@@ -57,6 +59,8 @@ public:
 	
 private:
 	XSkinMeshLoader *_mesh;
+	DXMesh *_dxmesh;
+	DXSkinInfo *_skinInfo;
 };
 
 } // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xbuffer.h b/engines/wintermute/base/gfx/xbuffer.h
new file mode 100644
index 00000000000..486ea2bdd04
--- /dev/null
+++ b/engines/wintermute/base/gfx/xbuffer.h
@@ -0,0 +1,79 @@
+/* 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_XBUFFER_H
+#define WINTERMUTE_XBUFFER_H
+
+#include <common/types.h>
+#include <common/util.h>
+
+namespace Wintermute {
+
+struct DXBuffer {
+private:
+
+	byte *_ptr;
+	uint64 _size;
+
+public:
+
+	DXBuffer() {
+		_ptr = nullptr;
+		_size = 0;
+	}
+
+	DXBuffer(uint64 size) {
+		_ptr = new uint8_t[size];
+		if (_ptr == nullptr) {
+			size = 0;
+			return;
+		}
+		_size = size;
+	}
+
+	DXBuffer(const uint8 *ptr, uint64 size) {
+		_ptr = new uint8[size];
+		if (_ptr == nullptr) {
+			size = 0;
+			return;
+		}
+		memcpy(_ptr, ptr, size);
+		_size = size;
+	}
+
+	void free() {
+		delete[] _ptr;
+		_ptr = nullptr;
+		_size = 0;
+	}
+
+	byte *ptr() const {
+		return _ptr;
+	}
+
+	uint64 size() const {
+		return _size;
+	}
+};
+
+} // namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/xfile_loader.cpp b/engines/wintermute/base/gfx/xfile_loader.cpp
index 08bf5ea9733..7fb4836dc25 100644
--- a/engines/wintermute/base/gfx/xfile_loader.cpp
+++ b/engines/wintermute/base/gfx/xfile_loader.cpp
@@ -966,7 +966,7 @@ bool XFileLoader::parseObjectParts(XObject *object) {
 				return false;
 			}
 
-			objClass->_vertices = new XVector[objClass->_numVertices];
+			objClass->_vertices = new XVector3[objClass->_numVertices];
 			for (uint n = 0; n < objClass->_numVertices; n++) {
 				if (!getFloat(objClass->_vertices[n]._x) ||
 				    !getFloat(objClass->_vertices[n]._y) ||
@@ -1016,7 +1016,7 @@ bool XFileLoader::parseObjectParts(XObject *object) {
 				return false;
 			}
 
-			objClass->_normals = new XVector[objClass->_numNormals];
+			objClass->_normals = new XVector3[objClass->_numNormals];
 			for (uint n = 0; n < objClass->_numNormals; n++) {
 				if (!getFloat(objClass->_normals[n]._x) ||
 				    !getFloat(objClass->_normals[n]._y) ||
diff --git a/engines/wintermute/base/gfx/xfile_loader.h b/engines/wintermute/base/gfx/xfile_loader.h
index aa19a9fae46..147a67293ca 100644
--- a/engines/wintermute/base/gfx/xfile_loader.h
+++ b/engines/wintermute/base/gfx/xfile_loader.h
@@ -78,12 +78,19 @@ struct XToken {
 	float           _floatVal;
 };
 
-struct XVector {
+struct XVector3 {
 	float           _x;
 	float           _y;
 	float           _z;
 };
 
+struct XVector4 {
+	float           _x;
+	float           _y;
+	float           _z;
+	float           _w;
+};
+
 struct XCoords2d {
 	float           _u;
 	float           _v;
@@ -157,7 +164,7 @@ struct XSkinWeightsObject {
 
 struct XMeshObject {
 	uint32          _numVertices;
-	XVector         *_vertices{};
+    XVector3        *_vertices{};
 	uint32          _numFaces;
 	XMeshFace       *_faces{};
 
@@ -169,7 +176,7 @@ struct XMeshObject {
 
 struct XMeshNormalsObject {
 	uint32          _numNormals;
-	XVector         *_normals{};
+    XVector3        *_normals{};
 	uint32          _numFaceNormals;
 	XMeshFace       *_faceNormals{};
 
diff --git a/engines/wintermute/base/gfx/xframe_node.cpp b/engines/wintermute/base/gfx/xframe_node.cpp
index 0655a8e6a42..cf0d95c8e57 100644
--- a/engines/wintermute/base/gfx/xframe_node.cpp
+++ b/engines/wintermute/base/gfx/xframe_node.cpp
@@ -182,13 +182,6 @@ bool FrameNode::loadFromXData(const Common::String &filename, XModel *model, XFi
 			model->_ticksPerSecond = xobj->getXAnimTicksPerSecondObject()->_animTicksPerSecond;
 			return true;
 		}
-	} else if (objectType == kXClassMaterial) {
-		MaterialReference materialReference;
-		xobj->getName(materialReference._name);
-		materialReference._material = new Material(_gameRef);
-		materialReference._material->loadFromX(xobj, filename);
-		materialReferences.push_back(materialReference);
-		return true;
 	}
 
 	return true;
diff --git a/engines/wintermute/base/gfx/xmaterial.cpp b/engines/wintermute/base/gfx/xmaterial.cpp
index 6f57b4f2fe0..9f07d04c653 100644
--- a/engines/wintermute/base/gfx/xmaterial.cpp
+++ b/engines/wintermute/base/gfx/xmaterial.cpp
@@ -43,6 +43,7 @@ namespace Wintermute {
 
 //////////////////////////////////////////////////////////////////////////
 Material::Material(BaseGame *inGame) : BaseNamedObject(inGame) {
+	memset(&_material, 0, sizeof(_material));
 	_surface = nullptr;
 	_ownedSurface = false;
 	_sprite = nullptr;
@@ -141,45 +142,4 @@ BaseSurface *Material::getSurface() {
 	}
 }
 
-bool Material::loadFromX(XFileData *xobj, const Common::String &filename) {
-	XMaterialObject *material = xobj->getXMaterialObject();
-	if (!material)
-		return false;
-
-	_diffuse.r() = material->_colorR;
-	_diffuse.g() = material->_colorG;
-	_diffuse.b() = material->_colorB;
-	_diffuse.a() = material->_colorA;
-
-	_shininess = material->_power;
-
-	_specular.r() = material->_specularR;
-	_specular.g() = material->_specularG;
-	_specular.b() = material->_specularB;
-	_specular.a() = 1.0f;
-
-	_emissive.r() = material->_emissiveR;
-	_emissive.g() = material->_emissiveG;
-	_emissive.b() = material->_emissiveB;
-	_emissive.a() = 1.0f;
-
-	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 && objectType == kXClassTextureFilename) {
-				Common::String textureFilename = xchildData.getXTextureFilenameObject()->_filename;
-				setTexture(PathUtil::getDirectoryName(filename) + textureFilename);
-			}
-		}
-	}
-
-	return true;
-}
-
 } // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xmaterial.h b/engines/wintermute/base/gfx/xmaterial.h
index 540399291ec..87a217c6909 100644
--- a/engines/wintermute/base/gfx/xmaterial.h
+++ b/engines/wintermute/base/gfx/xmaterial.h
@@ -29,6 +29,7 @@
 #define WINTERMUTE_XMATERIAL_H
 
 #include "engines/wintermute/base/base_named_object.h"
+#include "engines/wintermute/base/gfx/xskinmesh.h"
 
 namespace Wintermute {
 
@@ -78,19 +79,13 @@ public:
 	Material(BaseGame *inGame);
 	virtual ~Material();
 
-	ColorValue _diffuse;
-	ColorValue _ambient;
-	ColorValue _specular;
-	ColorValue _emissive;
-	float _shininess;
+	DXMaterial _material;
 
 	bool setTexture(const Common::String &filename, bool adoptName = false);
 	bool setSprite(BaseSprite *sprite, bool adoptName = false);
 	bool setTheora(VideoTheoraPlayer *theora, bool adoptName = false);
 	BaseSurface *getSurface();
 
-	bool loadFromX(XFileData *xobj, const Common::String &filename);
-
 	bool invalidateDeviceObjects();
 	bool restoreDeviceObjects();
 
diff --git a/engines/wintermute/base/gfx/xmath.cpp b/engines/wintermute/base/gfx/xmath.cpp
new file mode 100644
index 00000000000..e906f623278
--- /dev/null
+++ b/engines/wintermute/base/gfx/xmath.cpp
@@ -0,0 +1,460 @@
+/* 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/>.
+ *
+ */
+
+/*
+ * Code originated from Wine sources.
+ * Copyright (C) 2008 David Adam
+ * Copyright (C) 2008 Luis Busquets
+ * Copyright (C) 2008 Jérôme Gardou
+ * Copyright (C) 2008 Philip Nilsson
+ * Copyright (C) 2008 Henri Verbeet
+ */
+
+#include "engines/wintermute/base/gfx/xmath.h"
+
+namespace Wintermute {
+
+DXQuaternion *DXQuaternionRotationMatrix(DXQuaternion *out, const DXMatrix *m) {
+	float s, trace;
+
+	trace = m->_m[0][0] + m->_m[1][1] + m->_m[2][2] + 1.0f;
+	if (trace > 1.0f) {
+		s = 2.0f * sqrtf(trace);
+		out->_x = (m->_m[1][2] - m->_m[2][1]) / s;
+		out->_y = (m->_m[2][0] - m->_m[0][2]) / s;
+		out->_z = (m->_m[0][1] - m->_m[1][0]) / s;
+		out->_w = 0.25f * s;
+	} else {
+		int i, maxi = 0;
+
+		for (i = 1; i < 3; i++) {
+			if (m->_m[i][i] > m->_m[maxi][maxi])
+				maxi = i;
+		}
+
+		switch (maxi) {
+		case 0:
+			s = 2.0f * sqrtf(1.0f + m->_m[0][0] - m->_m[1][1] - m->_m[2][2]);
+			out->_x = 0.25f * s;
+			out->_y = (m->_m[0][1] + m->_m[1][0]) / s;
+			out->_z = (m->_m[0][2] + m->_m[2][0]) / s;
+			out->_w = (m->_m[1][2] - m->_m[2][1]) / s;
+			break;
+
+		case 1:
+			s = 2.0f * sqrtf(1.0f + m->_m[1][1] - m->_m[0][0] - m->_m[2][2]);
+			out->_x = (m->_m[0][1] + m->_m[1][0]) / s;
+			out->_y = 0.25f * s;
+			out->_z = (m->_m[1][2] + m->_m[2][1]) / s;
+			out->_w = (m->_m[2][0] - m->_m[0][2]) / s;
+			break;
+
+		case 2:
+			s = 2.0f * sqrtf(1.0f + m->_m[2][2] - m->_m[0][0] - m->_m[1][1]);
+			out->_x = (m->_m[0][2] + m->_m[2][0]) / s;
+			out->_y = (m->_m[1][2] + m->_m[2][1]) / s;
+			out->_z = 0.25f * s;
+			out->_w = (m->_m[0][1] - m->_m[1][0]) / s;
+			break;
+		}
+	}
+
+	return out;
+}
+
+DXMatrix *DXMatrixPerspectiveFovLH(DXMatrix *pout,float fovy, float aspect, float zn, float zf) {
+	DXMatrixIdentity(pout);
+	pout->_m[0][0] = 1.0f / (aspect * tanf(fovy/2.0f));
+	pout->_m[1][1] = 1.0f / tanf(fovy/2.0f);
+	pout->_m[2][2] = zf / (zf - zn);
+	pout->_m[2][3] = 1.0f;
+	pout->_m[3][2] = (zf * zn) / (zn - zf);
+	pout->_m[3][3] = 0.0f;
+	return pout;
+}
+
+DXMatrix *DXMatrixPerspectiveFovRH(DXMatrix *pout, float fovy, float aspect, float zn, float zf) {
+	DXMatrixIdentity(pout);
+	pout->_m[0][0] = 1.0f / (aspect * tanf(fovy / 2.0f));
+	pout->_m[1][1] = 1.0f / tanf(fovy / 2.0f);
+	pout->_m[2][2] = zf / (zn - zf);
+	pout->_m[2][3] = -1.0f;
+	pout->_m[3][2] = (zf * zn) / (zn - zf);
+	pout->_m[3][3] = 0.0f;
+	return pout;
+}
+
+DXMatrix *DXMatrixInverse(DXMatrix *pout, float *pdeterminant, const DXMatrix *pm) {
+	float det, t[3], v[16];
+	int i, j;
+
+	t[0] = pm->_m[2][2] * pm->_m[3][3] - pm->_m[2][3] * pm->_m[3][2];
+	t[1] = pm->_m[1][2] * pm->_m[3][3] - pm->_m[1][3] * pm->_m[3][2];
+	t[2] = pm->_m[1][2] * pm->_m[2][3] - pm->_m[1][3] * pm->_m[2][2];
+	v[0] = pm->_m[1][1] * t[0] - pm->_m[2][1] * t[1] + pm->_m[3][1] * t[2];
+	v[4] = -pm->_m[1][0] * t[0] + pm->_m[2][0] * t[1] - pm->_m[3][0] * t[2];
+
+	t[0] = pm->_m[1][0] * pm->_m[2][1] - pm->_m[2][0] * pm->_m[1][1];
+	t[1] = pm->_m[1][0] * pm->_m[3][1] - pm->_m[3][0] * pm->_m[1][1];
+	t[2] = pm->_m[2][0] * pm->_m[3][1] - pm->_m[3][0] * pm->_m[2][1];
+	v[8] = pm->_m[3][3] * t[0] - pm->_m[2][3] * t[1] + pm->_m[1][3] * t[2];
+	v[12] = -pm->_m[3][2] * t[0] + pm->_m[2][2] * t[1] - pm->_m[1][2] * t[2];
+
+	det = pm->_m[0][0] * v[0] + pm->_m[0][1] * v[4] + pm->_m[0][2] * v[8] + pm->_m[0][3] * v[12];
+	if (det == 0.0f)
+		return nullptr;
+	if (pdeterminant)
+		*pdeterminant = det;
+
+	t[0] = pm->_m[2][2] * pm->_m[3][3] - pm->_m[2][3] * pm->_m[3][2];
+	t[1] = pm->_m[0][2] * pm->_m[3][3] - pm->_m[0][3] * pm->_m[3][2];
+	t[2] = pm->_m[0][2] * pm->_m[2][3] - pm->_m[0][3] * pm->_m[2][2];
+	v[1] = -pm->_m[0][1] * t[0] + pm->_m[2][1] * t[1] - pm->_m[3][1] * t[2];
+	v[5] = pm->_m[0][0] * t[0] - pm->_m[2][0] * t[1] + pm->_m[3][0] * t[2];
+
+	t[0] = pm->_m[0][0] * pm->_m[2][1] - pm->_m[2][0] * pm->_m[0][1];
+	t[1] = pm->_m[3][0] * pm->_m[0][1] - pm->_m[0][0] * pm->_m[3][1];
+	t[2] = pm->_m[2][0] * pm->_m[3][1] - pm->_m[3][0] * pm->_m[2][1];
+	v[9] = -pm->_m[3][3] * t[0] - pm->_m[2][3] * t[1]- pm->_m[0][3] * t[2];
+	v[13] = pm->_m[3][2] * t[0] + pm->_m[2][2] * t[1] + pm->_m[0][2] * t[2];
+
+	t[0] = pm->_m[1][2] * pm->_m[3][3] - pm->_m[1][3] * pm->_m[3][2];
+	t[1] = pm->_m[0][2] * pm->_m[3][3] - pm->_m[0][3] * pm->_m[3][2];
+	t[2] = pm->_m[0][2] * pm->_m[1][3] - pm->_m[0][3] * pm->_m[1][2];
+	v[2] = pm->_m[0][1] * t[0] - pm->_m[1][1] * t[1] + pm->_m[3][1] * t[2];
+	v[6] = -pm->_m[0][0] * t[0] + pm->_m[1][0] * t[1] - pm->_m[3][0] * t[2];
+
+	t[0] = pm->_m[0][0] * pm->_m[1][1] - pm->_m[1][0] * pm->_m[0][1];
+	t[1] = pm->_m[3][0] * pm->_m[0][1] - pm->_m[0][0] * pm->_m[3][1];
+	t[2] = pm->_m[1][0] * pm->_m[3][1] - pm->_m[3][0] * pm->_m[1][1];
+	v[10] = pm->_m[3][3] * t[0] + pm->_m[1][3] * t[1] + pm->_m[0][3] * t[2];
+
+	t[0] = pm->_m[1][2] * pm->_m[2][3] - pm->_m[1][3] * pm->_m[2][2];
+	t[1] = pm->_m[0][2] * pm->_m[2][3] - pm->_m[0][3] * pm->_m[2][2];
+	t[2] = pm->_m[0][2] * pm->_m[1][3] - pm->_m[0][3] * pm->_m[1][2];
+	v[3] = -pm->_m[0][1] * t[0] + pm->_m[1][1] * t[1] - pm->_m[2][1] * t[2];
+	v[7] = pm->_m[0][0] * t[0] - pm->_m[1][0] * t[1] + pm->_m[2][0] * t[2];
+
+	v[11] = -pm->_m[0][0] * (pm->_m[1][1] * pm->_m[2][3] - pm->_m[1][3] * pm->_m[2][1]) +
+	         pm->_m[1][0] * (pm->_m[0][1] * pm->_m[2][3] - pm->_m[0][3] * pm->_m[2][1]) -
+	         pm->_m[2][0] * (pm->_m[0][1] * pm->_m[1][3] - pm->_m[0][3] * pm->_m[1][1]);
+
+	v[15] = pm->_m[0][0] * (pm->_m[1][1] * pm->_m[2][2] - pm->_m[1][2] * pm->_m[2][1]) -
+	        pm->_m[1][0] * (pm->_m[0][1] * pm->_m[2][2] - pm->_m[0][2] * pm->_m[2][1]) +
+	        pm->_m[2][0] * (pm->_m[0][1] * pm->_m[1][2] - pm->_m[0][2] * pm->_m[1][1]);
+
+	det = 1.0f / det;
+
+	for (i = 0; i < 4; i++)
+		for (j = 0; j < 4; j++)
+			pout->_m[i][j] = v[4 * i + j] * det;
+
+	return pout;
+}
+
+DXPlane *DXPlaneFromPointNormal(DXPlane *pout, const DXVector3 *pvpoint, const DXVector3 *pvnormal) {
+	pout->_a = pvnormal->_x;
+	pout->_b = pvnormal->_y;
+	pout->_c = pvnormal->_z;
+	pout->_d = -DXVec3Dot(pvpoint, pvnormal);
+	return pout;
+}
+
+DXPlane *D3DXPlaneFromPoints(DXPlane *pout, const DXVector3 *pv1, const DXVector3 *pv2, const DXVector3 *pv3) {
+	DXVector3 edge1, edge2, normal, Nnormal;
+
+	edge1._x = 0.0f; edge1._y = 0.0f; edge1._z = 0.0f;
+	edge2._x = 0.0f; edge2._y = 0.0f; edge2._z = 0.0f;
+	DXVec3Subtract(&edge1, pv2, pv1);
+	DXVec3Subtract(&edge2, pv3, pv1);
+	DXVec3Cross(&normal, &edge1, &edge2);
+	DXVec3Normalize(&Nnormal, &normal);
+	DXPlaneFromPointNormal(pout, pv1, &Nnormal);
+	return pout;
+}
+
+DXVector3 *DXPlaneIntersectLine(DXVector3 *pout, const DXPlane *pp, const DXVector3 *pv1, const DXVector3 *pv2) {
+	DXVector3 direction, normal;
+	float dot, temp;
+
+	normal._x = pp->_a;
+	normal._y = pp->_b;
+	normal._z = pp->_c;
+	direction._x = pv2->_x - pv1->_x;
+	direction._y = pv2->_y - pv1->_y;
+	direction._z = pv2->_z - pv1->_z;
+	dot = DXVec3Dot(&normal, &direction);
+	if (!dot)
+		return nullptr;
+	temp = (pp->_d + DXVec3Dot(&normal, pv1) ) / dot;
+	pout->_x = pv1->_x - temp * direction._x;
+	pout->_y = pv1->_y - temp * direction._y;
+	pout->_z = pv1->_z - temp * direction._z;
+	return pout;
+}
+
+DXVector3 *DXVec3Normalize(DXVector3 *pout, const DXVector3 *pv) {
+	float norm;
+
+	norm = DXVec3Length(pv);
+	if (!norm) {
+		pout->_x = 0.0f;
+		pout->_y = 0.0f;
+		pout->_z = 0.0f;
+	} else {
+		pout->_x = pv->_x / norm;
+		pout->_y = pv->_y / norm;
+		pout->_z = pv->_z / norm;
+	}
+
+	return pout;
+}
+
+DXMatrix *DXMatrixTranslation(DXMatrix *pout, float x, float y, float z) {
+	DXMatrixIdentity(pout);
+	pout->_m[3][0] = x;
+	pout->_m[3][1] = y;
+	pout->_m[3][2] = z;
+	return pout;
+}
+
+DXMatrix *D3DXMatrixScaling(DXMatrix *pout, float sx, float sy, float sz) {
+	DXMatrixIdentity(pout);
+	pout->_m[0][0] = sx;
+	pout->_m[1][1] = sy;
+	pout->_m[2][2] = sz;
+	return pout;
+}
+
+DXMatrix *D3DXMatrixRotationZ(DXMatrix *pout, float angle) {
+	DXMatrixIdentity(pout);
+	pout->_m[0][0] = cosf(angle);
+	pout->_m[1][1] = cosf(angle);
+	pout->_m[0][1] = sinf(angle);
+	pout->_m[1][0] = -sinf(angle);
+	return pout;
+}
+
+DXMatrix *DXMatrixRotationYawPitchRoll(DXMatrix *out, float yaw, float pitch, float roll) {
+	float sroll, croll, spitch, cpitch, syaw, cyaw;
+
+	sroll = sinf(roll);
+	croll = cosf(roll);
+	spitch = sinf(pitch);
+	cpitch = cosf(pitch);
+	syaw = sinf(yaw);
+	cyaw = cosf(yaw);
+
+	out->_m[0][0] = sroll * spitch * syaw + croll * cyaw;
+	out->_m[0][1] = sroll * cpitch;
+	out->_m[0][2] = sroll * spitch * cyaw - croll * syaw;
+	out->_m[0][3] = 0.0f;
+	out->_m[1][0] = croll * spitch * syaw - sroll * cyaw;
+	out->_m[1][1] = croll * cpitch;
+	out->_m[1][2] = croll * spitch * cyaw + sroll * syaw;
+	out->_m[1][3] = 0.0f;
+	out->_m[2][0] = cpitch * syaw;
+	out->_m[2][1] = -spitch;
+	out->_m[2][2] = cpitch * cyaw;
+	out->_m[2][3] = 0.0f;
+	out->_m[3][0] = 0.0f;
+	out->_m[3][1] = 0.0f;
+	out->_m[3][2] = 0.0f;
+	out->_m[3][3] = 1.0f;
+
+	return out;
+}
+
+DXMatrix *DXMatrixRotationQuaternion(DXMatrix *pout, const DXQuaternion *pq) {
+	DXMatrixIdentity(pout);
+	pout->_m[0][0] = 1.0f - 2.0f * (pq->_y * pq->_y + pq->_z * pq->_z);
+	pout->_m[0][1] = 2.0f * (pq->_x *pq->_y + pq->_z * pq->_w);
+	pout->_m[0][2] = 2.0f * (pq->_x * pq->_z - pq->_y * pq->_w);
+	pout->_m[1][0] = 2.0f * (pq->_x * pq->_y - pq->_z * pq->_w);
+	pout->_m[1][1] = 1.0f - 2.0f * (pq->_x * pq->_x + pq->_z * pq->_z);
+	pout->_m[1][2] = 2.0f * (pq->_y *pq->_z + pq->_x *pq->_w);
+	pout->_m[2][0] = 2.0f * (pq->_x * pq->_z + pq->_y * pq->_w);
+	pout->_m[2][1] = 2.0f * (pq->_y *pq->_z - pq->_x *pq->_w);
+	pout->_m[2][2] = 1.0f - 2.0f * (pq->_x * pq->_x + pq->_y * pq->_y);
+	return pout;
+}
+
+DXQuaternion *DXQuaternionSlerp(DXQuaternion *out, const DXQuaternion *q1, const DXQuaternion *q2, float t) {
+	float dot, temp;
+
+	temp = 1.0f - t;
+	dot = DXQuaternionDot(q1, q2);
+	if (dot < 0.0f) {
+		t = -t;
+		dot = -dot;
+	}
+
+	if (1.0f - dot > 0.001f) {
+		float theta = acosf(dot);
+		temp = sinf(theta * temp) / sinf(theta);
+		t = sinf(theta * t) / sinf(theta);
+	}
+
+	out->_x = temp * q1->_x + t * q2->_x;
+	out->_y = temp * q1->_y + t * q2->_y;
+	out->_z = temp * q1->_z + t * q2->_z;
+	out->_w = temp * q1->_w + t * q2->_w;
+
+	return out;
+}
+
+float D3DXQuaternionDot(const DXVector4 *pq1, const DXVector4 *pq2) {
+	return (pq1->_x) * (pq2->_x) + (pq1->_y) * (pq2->_y) + (pq1->_z) * (pq2->_z) + (pq1->_w) * (pq2->_w);
+}
+
+DXVector4 *D3DXVec3Transform(DXVector4 *pout, const DXVector3 *pv, const DXMatrix *pm) {
+	DXVector4 out;
+
+	out._x = pm->_m[0][0] * pv->_x + pm->_m[1][0] * pv->_y + pm->_m[2][0] * pv->_z + pm->_m[3][0];
+	out._y = pm->_m[0][1] * pv->_x + pm->_m[1][1] * pv->_y + pm->_m[2][1] * pv->_z + pm->_m[3][1];
+	out._z = pm->_m[0][2] * pv->_x + pm->_m[1][2] * pv->_y + pm->_m[2][2] * pv->_z + pm->_m[3][2];
+	out._w = pm->_m[0][3] * pv->_x + pm->_m[1][3] * pv->_y + pm->_m[2][3] * pv->_z + pm->_m[3][3];
+	*pout = out;
+	return pout;
+}
+
+DXVector3 *DXVec3TransformCoord(DXVector3 *pout, const DXVector3 *pv, const DXMatrix *pm) {
+	DXVector3 out;
+
+	float norm = pm->_m[0][3] * pv->_x + pm->_m[1][3] * pv->_y + pm->_m[2][3] *pv->_z + pm->_m[3][3];
+
+	out._x = (pm->_m[0][0] * pv->_x + pm->_m[1][0] * pv->_y + pm->_m[2][0] * pv->_z + pm->_m[3][0]) / norm;
+	out._y = (pm->_m[0][1] * pv->_x + pm->_m[1][1] * pv->_y + pm->_m[2][1] * pv->_z + pm->_m[3][1]) / norm;
+	out._z = (pm->_m[0][2] * pv->_x + pm->_m[1][2] * pv->_y + pm->_m[2][2] * pv->_z + pm->_m[3][2]) / norm;
+
+	*pout = out;
+
+	return pout;
+}
+
+DXVector3 *DXVec3TransformNormal(DXVector3 *pout, const DXVector3 *pv, const DXMatrix *pm) {
+	const DXVector3 v = *pv;
+
+	pout->_x = pm->_m[0][0] * v._x + pm->_m[1][0] * v._y + pm->_m[2][0] * v._z;
+	pout->_y = pm->_m[0][1] * v._x + pm->_m[1][1] * v._y + pm->_m[2][1] * v._z;
+	pout->_z = pm->_m[0][2] * v._x + pm->_m[1][2] * v._y + pm->_m[2][2] * v._z;
+	return pout;
+}
+
+DXMatrix *DXMatrixMultiply(DXMatrix *pout, const DXMatrix *pm1, const DXMatrix *pm2) {
+	DXMatrix out;
+
+	for (int i = 0; i < 4; i++) {
+		for (int j = 0; j < 4; j++) {
+			out._m[i][j] = pm1->_m[i][0] * pm2->_m[0][j] + pm1->_m[i][1] * pm2->_m[1][j] + pm1->_m[i][2] * pm2->_m[2][j] + pm1->_m[i][3] * pm2->_m[3][j];
+		}
+	}
+
+	*pout = out;
+	return pout;
+}
+
+DXVector3 *DXVec3Project(DXVector3 *pout, const DXVector3 *pv, const Rect32 *pviewport,
+                         const DXMatrix *pprojection, const DXMatrix *pview, const DXMatrix *pworld) {
+	DXMatrix m;
+
+	DXMatrixIdentity(&m);
+	if (pworld)
+		DXMatrixMultiply(&m, &m, pworld);
+	if (pview)
+		DXMatrixMultiply(&m, &m, pview);
+	if (pprojection)
+		DXMatrixMultiply(&m, &m, pprojection);
+
+	DXVec3TransformCoord(pout, pv, &m);
+
+	if (pviewport) {
+		pout->_x = pviewport->left + (1.0f + pout->_x) * pviewport->width() / 2.0f;
+		pout->_y = pviewport->top  + (1.0f - pout->_y) * pviewport->height() / 2.0f;
+		pout->_z = (1.0f + pout->_z) / 2.0f;
+	}
+	return pout;
+}
+
+float D3DXVec3Length(const DXVector3 *pv) {
+	return sqrtf(pv->_x * pv->_x + pv->_y * pv->_y + pv->_z * pv->_z);
+}
+
+DXMatrix *DXMatrixLookAtLH(DXMatrix *out, const DXVector3 *eye, const DXVector3 *at, const DXVector3 *up) {
+	DXVector3 right, upn, vec;
+
+	DXVec3Subtract(&vec, at, eye);
+	DXVec3Normalize(&vec, &vec);
+	DXVec3Cross(&right, up, &vec);
+	DXVec3Cross(&upn, &vec, &right);
+	DXVec3Normalize(&right, &right);
+	DXVec3Normalize(&upn, &upn);
+	out->_m[0][0] = right._x;
+	out->_m[1][0] = right._y;
+	out->_m[2][0] = right._z;
+	out->_m[3][0] = -DXVec3Dot(&right, eye);
+	out->_m[0][1] = upn._x;
+	out->_m[1][1] = upn._y;
+	out->_m[2][1] = upn._z;
+	out->_m[3][1] = -DXVec3Dot(&upn, eye);
+	out->_m[0][2] = vec._x;
+	out->_m[1][2] = vec._y;
+	out->_m[2][2] = vec._z;
+	out->_m[3][2] = -DXVec3Dot(&vec, eye);
+	out->_m[0][3] = 0.0f;
+	out->_m[1][3] = 0.0f;
+	out->_m[2][3] = 0.0f;
+	out->_m[3][3] = 1.0f;
+
+	return out;
+}
+
+
+DXMatrix *DXMatrixLookAtRH(DXMatrix *out, const DXVector3 *eye, const DXVector3 *at, const DXVector3 *up) {
+	DXVector3 right, upn, vec;
+
+	DXVec3Subtract(&vec, at, eye);
+	DXVec3Normalize(&vec, &vec);
+	DXVec3Cross(&right, up, &vec);
+	DXVec3Cross(&upn, &vec, &right);
+	DXVec3Normalize(&right, &right);
+	DXVec3Normalize(&upn, &upn);
+	out->_m[0][0] = -right._x;
+	out->_m[1][0] = -right._y;
+	out->_m[2][0] = -right._z;
+	out->_m[3][0] = DXVec3Dot(&right, eye);
+	out->_m[0][1] = upn._x;
+	out->_m[1][1] = upn._y;
+	out->_m[2][1] = upn._z;
+	out->_m[3][1] = -DXVec3Dot(&upn, eye);
+	out->_m[0][2] = -vec._x;
+	out->_m[1][2] = -vec._y;
+	out->_m[2][2] = -vec._z;
+	out->_m[3][2] = DXVec3Dot(&vec, eye);
+	out->_m[0][3] = 0.0f;
+	out->_m[1][3] = 0.0f;
+	out->_m[2][3] = 0.0f;
+	out->_m[3][3] = 1.0f;
+
+	return out;
+}
+
+} // End of namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xmath.h b/engines/wintermute/base/gfx/xmath.h
new file mode 100644
index 00000000000..4f88ff80c7b
--- /dev/null
+++ b/engines/wintermute/base/gfx/xmath.h
@@ -0,0 +1,170 @@
+/* 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/>.
+ *
+ */
+
+/*
+ * Code originated from Wine sources.
+ * Copyright (C) 2008 David Adam
+ * Copyright (C) 2008 Luis Busquets
+ * Copyright (C) 2008 Jérôme Gardou
+ * Copyright (C) 2008 Philip Nilsson
+ * Copyright (C) 2008 Henri Verbeet
+ */
+
+#ifndef WINTERMUTE_XMATH_H
+#define WINTERMUTE_XMATH_H
+
+#include "engines/wintermute/math/rect32.h"
+
+namespace Wintermute {
+
+#if defined(SCUMMVM_USE_PRAGMA_PACK)
+#pragma pack(4)
+#endif
+
+struct DXVector2 {
+	float           _x;
+	float           _y;
+};
+
+struct DXVector3 {
+	float           _x;
+	float           _y;
+	float           _z;
+};
+
+struct DXVector4 {
+	float           _x;
+	float           _y;
+	float           _z;
+	float           _w;
+};
+
+struct DXQuaternion {
+	float           _x;
+	float           _y;
+	float           _z;
+	float           _w;
+};
+
+struct DXPlane {
+	float           _a;
+	float           _b;
+	float           _c;
+	float           _d;
+};
+
+union DXMatrix {
+	struct {
+		float _11, _12, _13, _14;
+		float _21, _22, _23, _24;
+		float _31, _32, _33, _34;
+		float _41, _42, _43, _44;
+	} matrix;
+	float _m[4][4];
+	float _m4x4[16];
+};
+
+#if defined(SCUMMVM_USE_PRAGMA_PACK)
+#pragma pack()
+#endif
+
+DXQuaternion *DXQuaternionRotationMatrix(DXQuaternion *out,const DXMatrix *m);
+DXMatrix *DXMatrixPerspectiveFovLH(DXMatrix *pout,float fovy, float aspect, float zn, float zf);
+DXMatrix *DXMatrixPerspectiveFovRH(DXMatrix *pout, float fovy, float aspect, float zn, float zf);
+DXMatrix *DXMatrixLookAtLH(DXMatrix *out, const DXVector3 *eye, const DXVector3 *at, const DXVector3 *up);
+DXMatrix *DXMatrixLookAtRH(DXMatrix *out, const DXVector3 *eye, const DXVector3 *at, const DXVector3 *up);
+DXMatrix *DXMatrixInverse(DXMatrix *pout, float *pdeterminant, const DXMatrix *pm);
+DXPlane *DXPlaneFromPointNormal(DXPlane *pout, const DXVector3 *pvpoint, const DXVector3 *pvnormal);
+DXPlane *DXPlaneFromPoints(DXPlane *pout, const DXVector3 *pv1, const DXVector3 *pv2, const DXVector3 *pv3);
+DXVector3 *DXPlaneIntersectLine(DXVector3 *pout, const DXPlane *pp, const DXVector3 *pv1, const DXVector3 *pv2);
+DXVector3 *DXVec3Normalize(DXVector3 *pout, const DXVector3 *pv);
+DXMatrix *DXMatrixTranslation(DXMatrix *pout, float x, float y, float z);
+DXMatrix *DXMatrixScaling(DXMatrix *pout, float sx, float sy, float sz);
+DXMatrix *DXMatrixRotationZ(DXMatrix *pout, float angle);
+DXMatrix *DXMatrixRotationYawPitchRoll(DXMatrix *out, float yaw, float pitch, float roll);
+DXMatrix *DXMatrixRotationQuaternion(DXMatrix *pout, const DXQuaternion *pq);
+DXQuaternion *DXQuaternionSlerp(DXQuaternion *out, const DXQuaternion *q1, const DXQuaternion *q2, float t);
+DXVector4 *DXVec3Transform(DXVector4 *pout, const DXVector3 *pv, const DXMatrix *pm);
+DXVector3 *DXVec3TransformCoord(DXVector3 *pout, const DXVector3 *pv, const DXMatrix *pm);
+DXVector3 *DXVec3TransformNormal(DXVector3 *pout, const DXVector3 *pv, const DXMatrix *pm);
+DXMatrix *DXMatrixMultiply(DXMatrix *pout, const DXMatrix *pm1, const DXMatrix *pm2);
+DXVector3 *DXVec3Project(DXVector3 *pout, const DXVector3 *pv, const Rect32 *pviewport,
+                         const DXVector3 *pprojection, const DXMatrix *pview, const DXMatrix *pworld);
+
+static inline DXMatrix *DXMatrixIdentity(DXMatrix *pout) {
+	(*pout)._m[0][1] = 0.0f;
+	(*pout)._m[0][2] = 0.0f;
+	(*pout)._m[0][3] = 0.0f;
+	(*pout)._m[1][0] = 0.0f;
+	(*pout)._m[1][2] = 0.0f;
+	(*pout)._m[1][3] = 0.0f;
+	(*pout)._m[2][0] = 0.0f;
+	(*pout)._m[2][1] = 0.0f;
+	(*pout)._m[2][3] = 0.0f;
+	(*pout)._m[3][0] = 0.0f;
+	(*pout)._m[3][1] = 0.0f;
+	(*pout)._m[3][2] = 0.0f;
+	(*pout)._m[0][0] = 1.0f;
+	(*pout)._m[1][1] = 1.0f;
+	(*pout)._m[2][2] = 1.0f;
+	(*pout)._m[3][3] = 1.0f;
+	return pout;
+}
+
+static inline DXVector3 *DXVec3Lerp(DXVector3 *pout, const DXVector3 *pv1, const DXVector3 *pv2, float s) {
+	pout->_x = (1-s) * (pv1->_x) + s * (pv2->_x);
+	pout->_y = (1-s) * (pv1->_y) + s * (pv2->_y);
+	pout->_z = (1-s) * (pv1->_z) + s * (pv2->_z);
+	return pout;
+}
+
+static inline float DXQuaternionDot(const DXQuaternion *pq1, const DXQuaternion *pq2) {
+	return (pq1->_x) * (pq2->_x) + (pq1->_y) * (pq2->_y) + (pq1->_z) * (pq2->_z) + (pq1->_w) * (pq2->_w);
+}
+
+static inline DXVector3 *DXVec3Cross(DXVector3 *pout, const DXVector3 *pv1, const DXVector3 *pv2) {
+	DXVector3 temp;
+
+	temp._x = (pv1->_y) * (pv2->_z) - (pv1->_z) * (pv2->_y);
+	temp._y = (pv1->_z) * (pv2->_x) - (pv1->_x) * (pv2->_z);
+	temp._z = (pv1->_x) * (pv2->_y) - (pv1->_y) * (pv2->_x);
+	*pout = temp;
+	return pout;
+}
+
+static inline float DXVec3Dot(const DXVector3 *pv1, const DXVector3 *pv2) {
+	return (pv1->_x) * (pv2->_x) + (pv1->_y) * (pv2->_y) + (pv1->_z) * (pv2->_z);
+}
+
+static inline DXVector3 *DXVec3Subtract(DXVector3 *pout, const DXVector3 *pv1, const DXVector3 *pv2) {
+	pout->_x = pv1->_x - pv2->_x;
+	pout->_y = pv1->_y - pv2->_y;
+	pout->_z = pv1->_z - pv2->_z;
+	return pout;
+}
+
+static inline float DXVec3Length(const DXVector3 *pv) {
+	return sqrtf(pv->_x * pv->_x + pv->_y * pv->_y + pv->_z * pv->_z);
+}
+
+} // End of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/xmesh.cpp b/engines/wintermute/base/gfx/xmesh.cpp
index 1ae0f669cbc..3aa52199065 100644
--- a/engines/wintermute/base/gfx/xmesh.cpp
+++ b/engines/wintermute/base/gfx/xmesh.cpp
@@ -33,8 +33,11 @@
 #include "engines/wintermute/base/gfx/xframe_node.h"
 #include "engines/wintermute/base/gfx/xfile_loader.h"
 #include "engines/wintermute/base/gfx/xmodel.h"
+#include "engines/wintermute/base/gfx/xbuffer.h"
+#include "engines/wintermute/base/gfx/xskinmesh.h"
 #include "engines/wintermute/base/base_engine.h"
 #include "engines/wintermute/math/math_util.h"
+#include "engines/wintermute/utils/path_util.h"
 
 namespace Wintermute {
 
@@ -63,13 +66,120 @@ bool XMesh::loadFromXData(const Common::String &filename, XFileData *xobj, Commo
 
 	XMeshObject *meshObject = xobj->getXMeshObject();
 	if (!meshObject) {
+		BaseEngine::LOG(0, "Error loading mesh");
+		return false;
+	}
+
+	// load mesh
+	DXBuffer bufMaterials;
+	DXBuffer bufAdjacency;
+	//DXBuffer bufBoneOffset;
+	//uint32 numFaces;
+	uint32 numMaterials;
+	DXMesh *mesh;
+	DXSkinInfo *skinInfo = nullptr;
+
+	auto res = DXLoadSkinMesh(xobj, bufAdjacency, bufMaterials, numMaterials, &skinInfo, &mesh);
+	if (!res) {
 		BaseEngine::LOG(0, "Error loading skin mesh");
 		return false;
 	}
 
-	XSkinMeshLoader *mesh = new XSkinMeshLoader(this, meshObject);
-	_skinMesh = new SkinMeshHelper(mesh);
-	mesh->loadMesh(filename, xobj, materialReferences);
+	XSkinMeshLoader *meshLoader = new XSkinMeshLoader(this, meshObject, mesh, skinInfo);
+	meshLoader->loadMesh(filename, xobj, materialReferences);
+
+	_skinMesh = new SkinMeshHelper(meshLoader, mesh, skinInfo);
+
+
+	// check for materials
+	if ((bufMaterials.ptr() == nullptr) || (numMaterials == 0)) {
+		// no materials are found, create default material
+		Material *mat = new Material(_gameRef);
+		mat->_material._diffuse.color._r = 0.5f;
+		mat->_material._diffuse.color._g = 0.5f;
+		mat->_material._diffuse.color._b = 0.5f;
+		mat->_material._specular = mat->_material._diffuse;
+		mat->_material._ambient = mat->_material._diffuse;
+
+		_materials.add(mat);
+		_numAttrs = 1;
+
+		meshLoader->_indexRanges.push_back(0);
+		meshLoader->_indexRanges.push_back(meshLoader->_indexData.size());
+	} else {
+		// load the materials
+		DXMaterial *fileMats = (DXMaterial *)bufMaterials.ptr();
+		for (uint i = 0; i < numMaterials; i++) {
+			Material *mat = new Material(_gameRef);
+			mat->_material = fileMats[i];
+			mat->_material._ambient = mat->_material._diffuse;
+			if (fileMats[i]._textureFilename[0] != '\0') {
+				mat->setTexture(PathUtil::getDirectoryName(filename) + fileMats[i]._textureFilename, true);
+			}
+
+			_materials.add(mat);
+		}
+
+		auto atribTable = mesh->getAttributeTable();
+		assert (atribTable);
+		_numAttrs = atribTable->_size;
+
+		for (uint i = 0; i < atribTable->_size; i++) {
+			meshLoader->_materialIndices.push_back(atribTable->_ptr[i]._attribId);
+			meshLoader->_indexRanges.push_back(atribTable->_ptr[i]._faceStart * 3);
+		}
+
+		meshLoader->_indexRanges.push_back((atribTable->_ptr[atribTable->_size - 1]._faceStart + atribTable->_ptr[atribTable->_size - 1]._faceCount) * 3);
+
+	}
+
+	_skinnedMesh = false;
+
+	if (skinInfo) {
+		_skinnedMesh = skinInfo->getNumBones() > 0;
+		for (uint index = 0; index < skinInfo->getNumBones(); index++) {
+			SkinWeights currSkinWeights;
+			DXBone *bone = skinInfo->getBone(index);
+			currSkinWeights._boneName = bone->_name;
+
+			int weightCount = bone->_numInfluences;
+			currSkinWeights._vertexIndices.resize(weightCount);
+			currSkinWeights._vertexWeights.resize(weightCount);
+			
+			for (int i = 0; i < weightCount; ++i) {
+				currSkinWeights._vertexIndices[i] = bone->_vertices[i];
+			}
+			
+			for (int i = 0; i < weightCount; ++i) {
+				currSkinWeights._vertexWeights[i] = bone->_weights[i];
+			}
+			
+			for (int r = 0; r < 4; ++r) {
+				for (int c = 0; c < 4; ++c) {
+					currSkinWeights._offsetMatrix(c, r) = bone->_transform._m4x4[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;
+
+			meshLoader->_skinWeightsList.push_back(currSkinWeights);
+		}
+	}
+
+	meshLoader->generateAdjacency(_adjacency);
+
+	bufAdjacency.free();
+	bufMaterials.free();
+	//bufBoneOffset.free();
 
 	return true;
 }
@@ -90,7 +200,7 @@ bool XMesh::findBones(FrameNode *rootFrame) {
 		if (frame) {
 			_boneMatrices[i] = frame->getCombinedMatrix();
 		} else {
-			warning("XMeshOpenGL::findBones could not find bone %s", skinWeightsList[i]._boneName.c_str());
+			warning("XMesh::findBones could not find bone %s", skinWeightsList[i]._boneName.c_str());
 		}
 	}
 
diff --git a/engines/wintermute/base/gfx/xskinmesh.cpp b/engines/wintermute/base/gfx/xskinmesh.cpp
new file mode 100644
index 00000000000..d7d4ca5267f
--- /dev/null
+++ b/engines/wintermute/base/gfx/xskinmesh.cpp
@@ -0,0 +1,1167 @@
+/* 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/>.
+ *
+ */
+
+/*
+ * Based on skin and mesh code from Wine sources.
+ * Copyright (C) 2005 Henri Verbeet
+ * Copyright (C) 2006 Ivan Gyurdiev
+ * Copyright (C) 2009 David Adam
+ * Copyright (C) 2010 Tony Wasserka
+ * Copyright (C) 2011 Dylan Smith
+ * Copyright (C) 2011 Michael Mc Donnell
+ * Copyright (C) 2013 Christian Costa
+ */
+
+#include "engines/wintermute/base/gfx/xskinmesh.h"
+#include "engines/wintermute/base/gfx/xmath.h"
+
+namespace Wintermute {
+
+struct MeshData {
+	uint32 _numVertices;
+	uint32 _numPolyFaces;
+	uint32 _numTriFaces;
+	DXVector3 *_vertices;
+	uint32 *_numTriPerFace;
+	uint32 *_indices;
+	uint32 _fvf;
+	uint32 _numNormals;
+	DXVector3 *_normals;
+	uint32 *_normalIndices;
+	DXVector3 *_vertexNormals;
+	DXVector2 *_texCoords;
+	DXColorValue *_vertexColors;
+	uint32 _numMaterials;
+	DXMaterial *_materials;
+	uint32 *_materialIndices;
+	DXSkinInfo *_skinInfo;
+	uint32 _boneCount;
+	uint32 _skinWeightsInfoCount;
+};
+
+uint32 DXGetFVFVertexSize(uint32 fvf) {
+	uint32 size = 0;
+
+	if (fvf & DXFVF_XYZ)
+		size += sizeof(DXVector3);
+	if (fvf & DXFVF_NORMAL)
+		size += sizeof(DXVector3);
+	if (fvf & DXFVF_DIFFUSE)
+		size += sizeof(DXVector4);
+	if (fvf & DXFVF_TEX1)
+		size += sizeof(DXVector2);
+
+	return size;
+}
+
+static bool createMesh(uint32 numFaces, uint32 numVertices, uint32 fvf, DXMesh **mesh) {
+	if (!mesh)
+		return false;
+
+	auto object = new DXMesh;
+	if (!object)
+		return false;
+
+	if (!object->create(numFaces, numVertices, fvf)) {
+		delete object;
+		return false;
+	}
+
+	*mesh = object;
+
+	return true;
+}
+
+static bool createSkinInfo(uint32 vertexCount, uint32 boneCount, DXSkinInfo **skinInfo) {
+	if (!skinInfo)
+		return false;
+
+	auto skin = new DXSkinInfo();
+	if (!skin)
+		return false;
+
+	if (!skin->create(vertexCount, boneCount)) {
+		delete skin;
+		return false;
+	}
+
+	*skinInfo = skin;
+
+	return true;
+}
+
+bool DXMesh::create(uint32 numFaces, uint32 numVertices, uint32 fvf) {
+	_numFaces = numFaces;
+	_numVertices = numVertices;
+	_fvf = fvf;
+	_vertexSize = DXGetFVFVertexSize(fvf);
+	_attribTable._size = 0;
+	_attribTable._ptr = nullptr;
+	_vertexBuffer = DXBuffer(numVertices * _vertexSize);
+	_indexBuffer = DXBuffer(numFaces * 3 * sizeof(uint32));
+	_attribBuffer = DXBuffer(numFaces * sizeof(uint32));
+	if (!_vertexBuffer.ptr() || !_indexBuffer.ptr() || !_attribBuffer.ptr()) {
+		destroy();
+		return false;
+	}
+
+	return true;
+}
+
+void DXMesh::destroy() {
+	_numFaces = 0;
+	_numVertices = 0;
+	_fvf = 0;
+	_vertexSize = 0;
+	_attribTable._size = 0;
+	_vertexBuffer.free();
+	_indexBuffer.free();
+	_attribBuffer.free();
+	delete[] _attribTable._ptr;
+	_attribTable._ptr = nullptr;
+}
+
+int DXMesh::compareVertexKeys(const void *a, const void *b) {
+	const DXVertexMetadata *left = static_cast<const DXVertexMetadata *>(a);
+	const DXVertexMetadata *right = static_cast<const DXVertexMetadata *>(b);
+	if (left->_key == right->_key)
+		return 0;
+	return left->_key < right->_key ? -1 : 1;
+}
+
+bool DXMesh::generateAdjacency(uint32 *adjacency) {
+	uint32 i;
+
+	if (!adjacency)
+		return false;
+
+	byte *vertices = const_cast<byte *>(_vertexBuffer.ptr());
+	const uint32 *indices = (const uint32 *)_indexBuffer.ptr();
+
+	uint32 bufferSize = _numFaces * 3 * sizeof(uint32) + _numVertices * sizeof(DXVertexMetadata);
+	uint32 *sharedIndices = new uint32[bufferSize];
+	if (!sharedIndices)
+		return false;
+	DXVertexMetadata *sortedVertices = (DXVertexMetadata *)(sharedIndices + _numFaces * 3);
+
+	for (i = 0; i < _numVertices; i++) {
+		DXVector3 *vertex = (DXVector3 *)(vertices + _vertexSize * i);
+		sortedVertices[i]._firstSharedIndex = -1;
+		sortedVertices[i]._key = vertex->_x + vertex->_y + vertex->_z;
+		sortedVertices[i]._vertexIndex = i;
+	}
+	for (i = 0; i < _numFaces * 3; i++) {
+		uint32 *firstSharedIndex = &sortedVertices[indices[i]]._firstSharedIndex;
+		sharedIndices[i] = *firstSharedIndex;
+		*firstSharedIndex = i;
+		adjacency[i] = -1;
+	}
+	qsort(sortedVertices, _numVertices, sizeof(DXVertexMetadata), compareVertexKeys);
+
+	for (i = 0; i < _numVertices; i++) {
+		DXVertexMetadata *sortedVertexA = &sortedVertices[i];
+		DXVector3 *vertex_a = (DXVector3 *)(vertices + sortedVertexA->_vertexIndex * _vertexSize);
+		uint32 sharedIndexA = sortedVertexA->_firstSharedIndex;
+
+		while (sharedIndexA != (uint32)-1) {
+			uint32 j = i;
+			uint32 sharedIndexB = sharedIndices[sharedIndexA];
+			struct DXVertexMetadata *sortedVertexB = sortedVertexA;
+
+			while (true) {
+				while (sharedIndexB != (uint32)-1) {
+					uint32 baseA = (sharedIndexA / 3) * 3;
+					uint32 baseB = (sharedIndexB / 3) * 3;
+					bool adjacent = true;
+					int k;
+
+					for (k = 0; k < 3; k++) {
+						if (adjacency[baseB + k] == sharedIndexA / 3) {
+							adjacent = true;
+							break;
+						}
+					}
+					if (!adjacent) {
+						for (k = 1; k <= 2; k++) {
+							uint32 vertex_index_a = baseA + (sharedIndexA + k) % 3;
+							uint32 vertex_index_b = baseB + (sharedIndexB + (3 - k)) % 3;
+							adjacent = indices[vertex_index_a] == indices[vertex_index_b];
+							if (!adjacent) {
+								DXVector3 delta = {0.0f, 0.0f, 0.0f};
+								float lengthSq;
+
+								DXVec3Subtract(&delta, (DXVector3 *)(vertices + indices[vertex_index_a] * _vertexSize), (DXVector3 *)(vertices + indices[vertex_index_b] * _vertexSize));
+								lengthSq = DXVec3Length(&delta);
+								adjacent = lengthSq == 0.0f;
+							}
+							if (adjacent) {
+								uint32 adjA = baseA + 2 - (vertex_index_a + sharedIndexA + 1) % 3;
+								uint32 adjB = baseB + 2 - (vertex_index_b + sharedIndexB + 1) % 3;
+								if (adjacency[adjA] == (uint32)-1 && adjacency[adjB] == (uint32)-1) {
+									adjacency[adjA] = baseB / 3;
+									adjacency[adjB] = baseA / 3;
+									break;
+								}
+							}
+						}
+					}
+
+					sharedIndexB = sharedIndices[sharedIndexB];
+				}
+				while (++j < _numVertices) {
+					DXVector3 *vertexB;
+
+					sortedVertexB++;
+					if (sortedVertexB->_key - sortedVertexA->_key > 0.0f) {
+						j = _numVertices;
+						break;
+					}
+					vertexB = (DXVector3 *)(vertices + sortedVertexB->_vertexIndex * _vertexSize);
+					if (fabsf(vertex_a->_x - vertexB->_x) <= 0.0f &&
+						fabsf(vertex_a->_y - vertexB->_y) <= 0.0f &&
+						fabsf(vertex_a->_z - vertexB->_z) <= 0.0f) {
+						break;
+					}
+				}
+				if (j >= _numVertices)
+					break;
+				sharedIndexB = sortedVertexB->_firstSharedIndex;
+			}
+
+			sortedVertexA->_firstSharedIndex = sharedIndices[sortedVertexA->_firstSharedIndex];
+			sharedIndexA = sortedVertexA->_firstSharedIndex;
+		}
+	}
+
+	delete[] sharedIndices;
+	return true;
+}
+
+bool DXMesh::cloneMesh(DXMesh **cloneMeshOut) {
+	DXMesh *clonedMesh;
+
+	if (!cloneMeshOut)
+		return false;
+
+	if (!createMesh(_numFaces, _numVertices, _fvf, &clonedMesh))
+		return false;
+
+	memcpy(clonedMesh->_vertexBuffer.ptr(), _vertexBuffer.ptr(), _vertexBuffer.size());
+	memcpy(clonedMesh->_indexBuffer.ptr(), _indexBuffer.ptr(), _indexBuffer.size());
+	memcpy(clonedMesh->_attribBuffer.ptr(), _attribBuffer.ptr(), _attribBuffer.size());
+
+	if (_attribTable._size) {
+		clonedMesh->_attribTable._size = _attribTable._size;
+		clonedMesh->_attribTable._ptr = new DXAttributeRange[_attribTable._size];
+		if (!clonedMesh->_attribTable._ptr) {
+			delete clonedMesh;
+			return false;
+		}
+		memcpy(clonedMesh->_attribTable._ptr, _attribTable._ptr, _attribTable._size * sizeof(DXAttributeRange));
+	}
+
+	*cloneMeshOut = clonedMesh;
+
+	return true;
+}
+
+static uint32 countAttributes(const uint32 *attribBuffer, uint32 numFaces) {
+	uint32 last_attribute = attribBuffer[0];
+	uint32 attribTableSize = 1;
+	for (uint32 i = 1; i < numFaces; i++) {
+		if (attribBuffer[i] != last_attribute) {
+			last_attribute = attribBuffer[i];
+			attribTableSize++;
+		}
+	}
+	return attribTableSize;
+}
+
+static void fillAttributeTable(const uint32 *attribBuffer, uint32 numfaces, const uint32 *indices, DXAttributeRange *attribTable) {
+	uint32 attribTableSize = 0;
+	uint32 lastAttribute = attribBuffer[0];
+	uint32 minVertex, maxVertex, i, j;
+
+	attribTable[0]._attribId = lastAttribute;
+	attribTable[0]._faceStart = 0;
+	minVertex = (uint32)-1;
+	maxVertex = 0;
+	for (i = 0; i < numfaces; i++) {
+		if (attribBuffer[i] != lastAttribute) {
+			lastAttribute = attribBuffer[i];
+			attribTable[attribTableSize]._faceCount = i - attribTable[attribTableSize]._faceStart;
+			attribTable[attribTableSize]._vertexStart = minVertex;
+			attribTable[attribTableSize]._vertexCount = maxVertex - minVertex + 1;
+			attribTableSize++;
+			attribTable[attribTableSize]._attribId = attribBuffer[i];
+			attribTable[attribTableSize]._faceStart = i;
+			minVertex = (uint32)-1;
+			maxVertex = 0;
+		}
+		for (j = 0; j < 3; j++) {
+			uint32 vertex_index = indices[i * 3 + j];
+			if (vertex_index < minVertex)
+				minVertex = vertex_index;
+			if (vertex_index > maxVertex)
+				maxVertex = vertex_index;
+		}
+	}
+	attribTable[attribTableSize]._faceCount = i - attribTable[attribTableSize]._faceStart;
+	attribTable[attribTableSize]._vertexStart = minVertex;
+	attribTable[attribTableSize]._vertexCount = maxVertex - minVertex + 1;
+	attribTableSize++;
+}
+
+bool DXSkinInfo::create(uint32 vertexCount, uint32 boneCount) {
+	_numVertices = vertexCount;
+	_numBones = boneCount;
+	_fvf = 0;
+
+	_bones = new DXBone[boneCount];
+	if (!_bones) {
+		return false;
+	}
+	memset(_bones, 0, boneCount * sizeof(DXBone));
+	return true;
+}
+
+void DXSkinInfo::destroy() {
+	delete[] _bones;
+	_bones = nullptr;
+}
+
+bool DXSkinInfo::updateSkinnedMesh(const DXMatrix *boneTransforms, void *srcVertices, void *dstVertices) {
+	uint32 size = DXGetFVFVertexSize(_fvf);
+	uint32 i, j;
+
+	for (i = 0; i < _numVertices; i++) {
+		DXVector3 *position = (DXVector3 *)((byte *)dstVertices + size * i);
+		position->_x = 0.0f;
+		position->_y = 0.0f;
+		position->_z = 0.0f;
+	}
+
+	for (i = 0; i < _numBones; i++) {
+		DXMatrix boneInverse, matrix;
+
+		DXMatrixInverse(&boneInverse, NULL, &_bones[i]._transform);
+		DXMatrixMultiply(&matrix, &boneTransforms[i], &boneInverse);
+		DXMatrixMultiply(&matrix, &matrix, &_bones[i]._transform);
+
+		for (j = 0; j < _bones[i]._numInfluences; j++) {
+			DXVector3 position;
+			DXVector3 *position_src = (DXVector3 *)((byte *)srcVertices + size * _bones[i]._vertices[j]);
+			DXVector3 *position_dest = (DXVector3 *)((byte *)dstVertices + size * _bones[i]._vertices[j]);
+			float weight = _bones[i]._weights[j];
+
+			DXVec3TransformCoord(&position, position_src, &matrix);
+			position_dest->_x += weight * position._x;
+			position_dest->_y += weight * position._y;
+			position_dest->_z += weight * position._z;
+		}
+	}
+
+	if (_fvf & DXFVF_NORMAL) {
+		for (i = 0; i < _numVertices; i++) {
+			DXVector3 *normal = (DXVector3 *)((byte *)dstVertices + size * i + sizeof(DXVector3));
+			normal->_x = 0.0f;
+			normal->_y = 0.0f;
+			normal->_z = 0.0f;
+		}
+
+		for (i = 0; i < _numBones; i++) {
+			DXMatrix boneInverse, matrix;
+
+			DXMatrixInverse(&boneInverse, nullptr, &_bones[i]._transform);
+			DXMatrixMultiply(&matrix, &_bones[i]._transform, &boneTransforms[i]);
+
+			for (j = 0; j < _bones[i]._numInfluences; j++) {
+				DXVector3 normal;
+				DXVector3 *normalSrc = (DXVector3 *)((byte *)srcVertices + size * _bones[i]._vertices[j] + sizeof(DXVector3));
+				DXVector3 *normalDest = (DXVector3 *)((byte *)dstVertices + size * _bones[i]._vertices[j] + sizeof(DXVector3));
+				float weight = _bones[i]._weights[j];
+
+				DXVec3TransformNormal(&normal, normalSrc, &boneInverse);
+				DXVec3TransformNormal(&normal, &normal, &matrix);
+				normalDest->_x += weight * normal._x;
+				normalDest->_y += weight * normal._y;
+				normalDest->_z += weight * normal._z;
+			}
+		}
+
+		for (i = 0; i < _numVertices; i++) {
+			DXVector3 *normalDest = (DXVector3 *)((byte *)dstVertices + (i * size) + sizeof(DXVector3));
+			if ((normalDest->_x != 0.0f) && (normalDest->_y != 0.0f) && (normalDest->_z != 0.0f)) {
+				DXVec3Normalize(normalDest, normalDest);
+			}
+		}
+	}
+
+	return true;
+}
+
+bool DXSkinInfo::setBoneName(uint32 boneIdx, const char *name) {
+	if (boneIdx >= _numBones || !name)
+		return false;
+
+	uint32 size = strlen(name) + 1;
+	char *newName = new char[size];
+	if (!newName)
+		return false;
+	memcpy(newName, name, size);
+	delete[] _bones[boneIdx]._name;
+	_bones[boneIdx]._name = newName;
+
+	return true;
+}
+
+bool DXSkinInfo::setBoneInfluence(uint32 boneIdx, uint32 numInfluences, const uint32 *vertices, const float *weights) {
+	DXBone *bone;
+	uint32 *newVertices = NULL;
+	float *newWeights = NULL;
+
+	if (boneIdx >= _numBones || !vertices || !weights) {
+		return false;
+	}
+
+	if (numInfluences) {
+		newVertices = new uint32[numInfluences];
+		if (!newVertices)
+			return false;
+		newWeights = new float[numInfluences];
+		if (!newWeights) {
+			delete[] newVertices;
+			return false;
+		}
+		memcpy(newVertices, vertices, numInfluences * sizeof(*vertices));
+		memcpy(newWeights, weights, numInfluences * sizeof(*weights));
+	}
+	bone = &_bones[boneIdx];
+	bone->_numInfluences = numInfluences;
+	delete[] bone->_vertices;
+	delete[] bone->_weights;
+	bone->_vertices = newVertices;
+	bone->_weights = newWeights;
+
+	return true;
+}
+
+DXBone *DXSkinInfo::getBone(uint32 boneIdx) {
+	return &_bones[boneIdx];
+}
+
+bool DXSkinInfo::setBoneOffsetMatrix(uint32 boneIdx, const float *boneTransform) {
+	if (boneIdx >= _numBones || !boneTransform)
+		return false;
+
+	for (int m = 0; m < 16; m++) {
+		_bones[boneIdx]._transform._m4x4[m] = boneTransform[m];
+	}
+	return true;
+}
+
+static bool parseVertexDuplicationIndices(XFileData &fileData, struct MeshData */*meshData*/) {
+	auto vertexDuplObj = fileData.getXVertexDuplicationIndicesObject();
+	if (!vertexDuplObj) {
+		return false;
+	}
+
+	// skipping this data
+	return true;
+}
+
+static bool parseFVFData(XFileData &fileData, struct MeshData */*meshData*/) {
+	auto vfvDataObj = fileData.getXFVFDataObject();
+	if (!vfvDataObj) {
+		return false;
+	}
+
+	// FVFData suppose contains proper layout for declared FVF
+	// instead they are not complete and duplicated to already loaded
+	// so skipping this data
+	return true;
+}
+
+static bool parseDeclData(XFileData &fileData, struct MeshData *meshData) {
+	uint32 i, vertexSize = 0;
+	uint32 normalOffset = -1;
+	uint32 textureOffset = -1;
+	//uint32 tangentOffset = -1;
+	//uint32 binormalOffset = -1;
+
+	auto declObj = fileData.getXDeclDataObject();
+	if (!declObj) {
+		return false;
+	}
+
+	for (i = 0; i < declObj->_numElements; ++i) {
+		switch (declObj->_elements[i]._usage) {
+		case DXDECLUSAGE_NORMAL:
+			assert(!(meshData->_fvf & DXFVF_NORMAL));
+			normalOffset = vertexSize;
+			break;
+		case DXDECLUSAGE_TEXCOORD:
+			assert(!(meshData->_fvf & DXFVF_TEX1));
+			textureOffset = vertexSize;
+			break;
+		case DXDECLUSAGE_TANGENT:
+			//tangentOffset = vertexSize;
+			break;
+		case DXDECLUSAGE_BINORMAL:
+			//binormalOffset = vertexSize;
+			break;
+		default:
+			error("parseDeclData() Error: not handled DeclUsage: %d", declObj->_elements[i]._usage);
+		}
+		switch (declObj->_elements[i]._type) {
+		case DXDECLTYPE_FLOAT2:
+			vertexSize += 2;
+			break;
+		case DXDECLTYPE_FLOAT3:
+			vertexSize += 3;
+			break;
+		default:
+			error("parseDeclData() Error: not handled DeclType: %d", declObj->_elements[i]._type);
+		}
+	}
+
+	if (vertexSize * meshData->_numVertices != declObj->_numData) {
+		return false;
+	}
+
+	if (normalOffset != (uint32)-1) {
+		if (!meshData->_indices)
+			return false;
+		delete[] meshData->_normals;
+		uint32 numFaceIndices = meshData->_numPolyFaces * 2 + meshData->_numTriFaces;
+		meshData->_numNormals = meshData->_numVertices;
+		meshData->_normals = new DXVector3[meshData->_numNormals];
+		meshData->_normalIndices = new uint32[numFaceIndices];
+		if (!meshData->_normals || !meshData->_normalIndices) {
+			return false;
+		}
+		memcpy(meshData->_normalIndices, meshData->_indices, numFaceIndices * sizeof(uint32));
+		meshData->_fvf |= DXFVF_NORMAL;
+	}
+
+	if (textureOffset != (uint32)-1) {
+		delete[] meshData->_texCoords;
+		meshData->_texCoords = new DXVector2[meshData->_numVertices];
+		if (!meshData->_texCoords) {
+			return false;
+		}
+		meshData->_fvf |= DXFVF_TEX1;
+	}
+
+	for (i = 0; i < meshData->_numVertices; ++i) {
+		if (meshData->_fvf & DXFVF_NORMAL) {
+			float *vertexNormalData = reinterpret_cast<float *>(declObj->_data + vertexSize * i + normalOffset);
+			meshData->_normals[i]._x = vertexNormalData[0];
+			meshData->_normals[i]._y = vertexNormalData[1];
+			meshData->_normals[i]._z = vertexNormalData[2];
+			DXVec3Normalize(&meshData->_normals[i], &meshData->_normals[i]);
+		}
+
+		if (meshData->_fvf & DXFVF_TEX1) {
+			float *vertexTextureCoordsData = reinterpret_cast<float *>(declObj->_data + vertexSize * i + textureOffset);
+			meshData->_texCoords[i]._x = vertexTextureCoordsData[0];
+			meshData->_texCoords[i]._y = vertexTextureCoordsData[1];
+		}
+
+		if (textureOffset != (uint32)-1) {
+			//float *vertexTangentData = reinterpret_cast<float *>(declObj->_data + vertexSize * i + tangentOffset);
+		}
+
+		if (textureOffset != (uint32)-1) {
+			//float *vertexBinormalData = reinterpret_cast<float *>(declObj->_data + vertexSize * i + binormalOffset);
+		}
+	}
+
+	return true;
+}
+
+static bool parseSkinWeightsInfo(XFileData &fileData, struct MeshData *meshData) {
+	uint32 influenceCount;
+	const char *name;
+	uint32 index = meshData->_skinWeightsInfoCount;
+
+	if (!meshData->_skinInfo) {
+		return false;
+	}
+
+	auto skinWeightsObj = fileData.getXSkinWeightsObject();
+	if (!skinWeightsObj) {
+		return false;
+	}
+
+	name = skinWeightsObj->_transformNodeName;
+	influenceCount = skinWeightsObj->_numWeights;
+
+	if (meshData->_skinInfo->setBoneName(index, name)) {
+		if (meshData->_skinInfo->setBoneInfluence(index, influenceCount, skinWeightsObj->_vertexIndices, skinWeightsObj->_weights)) {
+			if (meshData->_skinInfo->setBoneOffsetMatrix(index, skinWeightsObj->_matrixOffset)) {
+				++meshData->_skinWeightsInfoCount;
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+static bool parseSkinMeshHeader(XFileData &fileData, struct MeshData *meshData) {
+	if (meshData->_skinInfo) {
+		return false;
+	}
+
+	auto skinMeshHeaderObj = fileData.getXSkinMeshHeaderObject();
+	if (!skinMeshHeaderObj) {
+		return false;
+	}
+
+	meshData->_boneCount = skinMeshHeaderObj->_nBones;
+	return createSkinInfo(meshData->_numVertices, meshData->_boneCount, &meshData->_skinInfo);
+}
+
+static bool parseTextureFilename(XFileData &fileData, char *filenameOut) {
+	auto textureNameObj = fileData.getXTextureFilenameObject();
+	if (!textureNameObj) {
+		return false;
+	}
+
+	Common::strlcpy(filenameOut, textureNameObj->_filename, XMAX_NAME_LEN);
+
+	return true;
+}
+
+static bool parseMaterial(XFileData &fileData, DXMaterial *material) {
+	XFileData child;
+	XClassType type;
+	uint nbChildren;
+	uint32 i;
+
+	auto materialObj = fileData.getXMaterialObject();
+	if (!materialObj) {
+		return false;
+	}
+
+	material->_diffuse.color._r = materialObj->_colorR;
+	material->_diffuse.color._g = materialObj->_colorG;
+	material->_diffuse.color._b = materialObj->_colorB;
+	material->_diffuse.color._a = materialObj->_colorA;
+	material->_power = materialObj->_power;
+	material->_specular.color._r = materialObj->_specularR;
+	material->_specular.color._g = materialObj->_specularG;
+	material->_specular.color._b = materialObj->_specularB;
+	material->_specular.color._a = 1.0f;
+	material->_emissive.color._r = materialObj->_emissiveR;
+	material->_emissive.color._g = materialObj->_emissiveG;
+	material->_emissive.color._b = materialObj->_emissiveB;
+	material->_emissive.color._a = 1.0f;
+	material->_ambient.color._r = 0.0f;
+	material->_ambient.color._g = 0.0f;
+	material->_ambient.color._b = 0.0f;
+	material->_ambient.color._a = 1.0f;
+	material->_textureFilename[0] = '\0';
+
+	if (!fileData.getChildren(nbChildren)) {
+		return false;
+	}
+
+	for (i = 0; i < nbChildren; i++) {
+		if (!fileData.getChild(i, child)) {
+			return false;
+		}
+		if (!child.getType(type)) {
+			return false;
+		}
+
+		if (type == XClassType::kXClassTextureFilename) {
+			if (!parseTextureFilename(child, material->_textureFilename)) {
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+static void destroyMaterials(struct MeshData *meshData) {
+	delete[] meshData->_materials;
+	meshData->_materials = nullptr;
+	delete[] meshData->_materialIndices;
+	meshData->_materialIndices = nullptr;
+	meshData->_numMaterials = 0;
+}
+
+static bool parseMaterialList(XFileData &fileData, struct MeshData *meshData) {
+	XFileData child;
+	XClassType type;
+	uint nbChildren;
+	uint32 materialCount, i;
+
+	destroyMaterials(meshData);
+
+	auto materialListObj = fileData.getXMeshMaterialListObject();
+	if (!materialListObj) {
+		return false;
+	}
+
+	materialCount = materialListObj->_nMaterials;
+
+	if (materialListObj->_numFaceIndexes != meshData->_numPolyFaces) {
+		return false;
+	}
+	for (i = 0; i < meshData->_numPolyFaces; ++i) {
+		if (materialListObj->_faceIndexes[i] >= materialCount) {
+			return false;
+		}
+	}
+
+	meshData->_materials = new DXMaterial[materialCount];
+	meshData->_materialIndices = new uint32[meshData->_numPolyFaces];
+	if (!meshData->_materials || !meshData->_materialIndices) {
+		return false;
+	}
+	for (i = 0; i < meshData->_numPolyFaces; ++i) {
+		meshData->_materialIndices[i] = materialListObj->_faceIndexes[i];
+	}
+
+	if (!fileData.getChildren(nbChildren)) {
+		return false;
+	}
+
+	for (i = 0; i < nbChildren; i++) {
+		if (!fileData.getChild(i, child)) {
+			return false;
+		}
+		if (!child.getType(type)) {
+			return false;
+		}
+
+		if (type == XClassType::kXClassMaterial) {
+			if (meshData->_numMaterials >= materialCount) {
+				return false;
+			}
+			if (!parseMaterial(child, &meshData->_materials[meshData->_numMaterials++])) {
+				return false;
+			}
+		}
+		// missing referenced material
+		if (type == XClassType::kXClassUnknown) {
+			materialCount--;
+		}
+	}
+	if (materialCount != meshData->_numMaterials) {
+		return false;
+	}
+
+	return true;
+}
+
+static bool parseTextureCoords(XFileData &fileData, struct MeshData *meshData) {
+	uint32 i;
+
+	delete[] meshData->_texCoords;
+	meshData->_texCoords = nullptr;
+
+	auto textureCordsObj = fileData.getXMeshTextureCoordsObject();
+	if (!textureCordsObj) {
+		return false;
+	}
+
+	if (textureCordsObj->_numTextureCoords != meshData->_numVertices) {
+		return false;
+	}
+
+	meshData->_texCoords = new DXVector2[meshData->_numVertices];
+	if (!meshData->_texCoords) {
+		return false;
+	}
+	for (i = 0; i < meshData->_numVertices; i++) {
+		meshData->_texCoords[i]._x = textureCordsObj->_textureCoords[i]._u;
+		meshData->_texCoords[i]._y = textureCordsObj->_textureCoords[i]._v;
+	}
+
+	meshData->_fvf |= DXFVF_TEX1;
+
+	return true;
+}
+
+static bool parseVertexColors(XFileData &fileData, struct MeshData *meshData) {
+	uint32 i;
+
+	delete[] meshData->_vertexColors;
+	meshData->_vertexColors = nullptr;
+
+	auto colorsObj = fileData.getXMeshVertexColorsObject();
+	if (!colorsObj) {
+		return false;
+	}
+	uint32 colorCount = colorsObj->_numVertexColors;
+
+	meshData->_vertexColors = new DXColorValue[meshData->_numVertices];
+	if (!meshData->_vertexColors) {
+		return false;
+	}
+
+	for (i = 0; i < meshData->_numVertices; i++) {
+		meshData->_vertexColors[i].color._r = 1.0f;
+		meshData->_vertexColors[i].color._g = 1.0f;
+		meshData->_vertexColors[i].color._b = 1.0f;
+		meshData->_vertexColors[i].color._a = 0.0f;
+	}
+
+	for (i = 0; i < colorCount; ++i) {
+		DXColorValue color;
+		uint32 index = colorsObj->_vertexColors[i]._index;
+		if (index >= meshData->_numVertices) {
+			return false;
+		}
+		color.color._r = colorsObj->_vertexColors[i]._indexColorR;
+		color.color._g = colorsObj->_vertexColors[i]._indexColorG;
+		color.color._b = colorsObj->_vertexColors[i]._indexColorB;
+		color.color._a = colorsObj->_vertexColors[i]._indexColorA;
+		meshData->_vertexColors[index].color._r = MIN(1.0f, MAX(0.0f, color.color._r));
+		meshData->_vertexColors[index].color._g = MIN(1.0f, MAX(0.0f, color.color._g));
+		meshData->_vertexColors[index].color._b = MIN(1.0f, MAX(0.0f, color.color._b));
+		meshData->_vertexColors[index].color._a = MIN(1.0f, MAX(0.0f, color.color._a));
+	}
+
+	meshData->_fvf |= DXFVF_DIFFUSE;
+
+	return true;
+}
+
+static bool parseNormals(XFileData &fileData, struct MeshData *meshData) {
+	uint32 numFaceIndices = meshData->_numPolyFaces * 2 + meshData->_numTriFaces;
+	uint32 i, j;
+
+	auto normalsObj = fileData.getXMeshNormalsObject();
+	if (!normalsObj) {
+		return false;
+	}
+
+	delete[] meshData->_normals;
+	meshData->_normals = nullptr;
+
+	meshData->_fvf |= DXFVF_NORMAL;
+	meshData->_numNormals = normalsObj->_numNormals;
+	meshData->_normals = new DXVector3[meshData->_numNormals];
+	meshData->_normalIndices = new uint32[numFaceIndices];
+	if (!meshData->_normals || !meshData->_normalIndices) {
+		return false;
+	}
+
+	memcpy(meshData->_normals, normalsObj->_normals, meshData->_numNormals * sizeof(DXVector3));
+	for (i = 0; i < meshData->_numNormals; i++) {
+		DXVec3Normalize(&meshData->_normals[i], &meshData->_normals[i]);
+	}
+
+	if (normalsObj->_numFaceNormals != meshData->_numPolyFaces) {
+		return false;
+	}
+
+	uint32 index = 0;
+	for (i = 0; i < meshData->_numPolyFaces; i++) {
+		uint32 count = normalsObj->_faceNormals[i]._numFaceVertexIndices;
+		if (count != meshData->_numTriPerFace[i] + 2) {
+			return false;
+		}
+
+		for (j = 0; j < count; j++) {
+			uint32 normalIndex = normalsObj->_faceNormals[i]._faceVertexIndices[j];
+			if (normalIndex >= meshData->_numNormals) {
+				return false;
+			}
+			meshData->_normalIndices[index++] = normalIndex;
+		}
+	}
+
+	return true;
+}
+
+static bool parseMesh(XFileData *fileData, struct MeshData *meshData) {
+	XFileData child;
+	XClassType type;
+	uint32 childCount;
+	uint32 i, j;
+	bool result = true;
+
+	meshData->_skinWeightsInfoCount = 0;
+
+	XMeshObject *meshObj = fileData->getXMeshObject();
+	if (!meshObj) {
+		return false;
+	}
+
+	meshData->_numVertices = meshObj->_numVertices;
+	meshData->_numPolyFaces = meshObj->_numFaces;
+	meshData->_numTriFaces = 0;
+	for (i = 0; i < meshData->_numPolyFaces; i++) {
+		meshData->_numTriFaces += meshObj->_faces[i]._numFaceVertexIndices - 2;
+	}
+
+	meshData->_fvf = DXFVF_XYZ;
+
+	meshData->_vertices = new DXVector3[meshData->_numVertices];
+	meshData->_numTriPerFace = new uint32[meshData->_numPolyFaces];
+	meshData->_indices = new uint32[meshData->_numTriFaces + meshData->_numPolyFaces * 2];
+	if (!meshData->_vertices || !meshData->_numTriPerFace || !meshData->_indices) {
+		return false;
+	}
+
+	memcpy(meshData->_vertices, meshObj->_vertices, meshData->_numVertices * sizeof(DXVector3));
+
+	uint32 index = 0;
+	for (i = 0; i < meshData->_numPolyFaces; i++) {
+		uint32 count = meshObj->_faces[i]._numFaceVertexIndices;
+		meshData->_numTriPerFace[i] = count - 2;
+		for (j = 0; j < count; j++) {
+			meshData->_indices[index++] = meshObj->_faces[i]._faceVertexIndices[j];
+		}
+	}
+
+	if (!fileData->getChildren(childCount))
+		return false;
+
+	for (i = 0; i < childCount; i++) {
+		if (!fileData->getChild(i, child))
+			return false;
+		if (!child.getType(type))
+			return false;
+		switch (type) {
+		case XClassType::kXClassMeshNormals:
+			result = parseNormals(child, meshData);
+			break;
+		case XClassType::kXClassMeshVertexColors:
+			result = parseVertexColors(child, meshData);
+			break;
+		case XClassType::kXClassMeshTextureCoords:
+			result = parseTextureCoords(child, meshData);
+			break;
+		case XClassType::kXClassMeshMaterialList:
+			result = parseMaterialList(child, meshData);
+			break;
+		case XClassType::kXClassSkinMeshHeader:
+			result = parseSkinMeshHeader(child, meshData);
+			break;
+		case XClassType::kXClassSkinWeights:
+			result = parseSkinWeightsInfo(child, meshData);
+			break;
+		case XClassType::kXClassDeclData:
+			result = parseDeclData(child, meshData);
+			break;
+		case XClassType::kXClassFVFData:
+			result = parseFVFData(child, meshData);
+			break;
+		case XClassType::kXClassVertexDuplicationIndices:
+			result = parseVertexDuplicationIndices(child, meshData);
+			break;
+		default:
+			break;
+		}
+		if (!result)
+			return false;
+	}
+
+	if (meshData->_skinInfo && (meshData->_skinWeightsInfoCount != meshData->_boneCount)) {
+		return false;
+	}
+
+	if (!meshData->_skinInfo) {
+		/*result = createSkinInfo(meshData->_numVertices, meshData->_boneCount, &meshData->_skinInfo);
+		if (!result)
+			return false;*/
+	}
+
+	return true;
+}
+
+static void cleanupMeshData(MeshData *meshData, bool releaseSkin = true) {
+	delete[] meshData->_vertices;
+	delete[] meshData->_numTriPerFace;
+	delete[] meshData->_indices;
+	delete[] meshData->_normals;
+	delete[] meshData->_normalIndices;
+	delete[] meshData->_vertexNormals;
+	destroyMaterials(meshData);
+	delete[] meshData->_texCoords;
+	delete[] meshData->_vertexColors;
+	if (releaseSkin)
+		delete meshData->_skinInfo;
+}
+
+bool DXLoadSkinMesh(XFileData *fileData, DXBuffer &adjacencyOut, DXBuffer &materialsOut, uint32 &numMaterialsOut, DXSkinInfo **skinInfoOut, DXMesh **meshOut) {
+	MeshData meshData{};
+	DXMesh *mesh;
+	uint32 i;
+
+	bool result = parseMesh(fileData, &meshData);
+	if (!result) {
+		cleanupMeshData(&meshData, true);
+		return false;
+	}
+	if (meshData._numVertices == 0) {
+		createMesh(meshData._numTriFaces, meshData._numVertices, meshData._fvf, &mesh);
+		*meshOut = mesh;
+		adjacencyOut = DXBuffer(meshData._numTriFaces * 3 * sizeof(uint32));
+		numMaterialsOut = meshData._numMaterials;
+		materialsOut = DXBuffer(meshData._numMaterials * sizeof(DXMaterial));
+		*skinInfoOut = meshData._skinInfo;
+		cleanupMeshData(&meshData, false);
+		return true;
+	}
+
+	uint32 totalVertices = meshData._numVertices;
+	if (meshData._fvf & DXFVF_NORMAL) {
+		meshData._vertexNormals = new DXVector3[meshData._numVertices];
+		memset(meshData._vertexNormals, 0, meshData._numVertices * sizeof(DXVector3));
+		uint32 numFaceIndices = meshData._numPolyFaces * 2 + meshData._numTriFaces;
+		for (i = 0; i < numFaceIndices; i++) {
+			uint32 vertexIndex = meshData._indices[i];
+			uint32 normalIndex = meshData._normalIndices[i];
+			assert(vertexIndex < meshData._numVertices);
+			assert(normalIndex < meshData._numNormals);
+			meshData._vertexNormals[vertexIndex]._x = meshData._normals[normalIndex]._x;
+			meshData._vertexNormals[vertexIndex]._y = meshData._normals[normalIndex]._y;
+			meshData._vertexNormals[vertexIndex]._z = meshData._normals[normalIndex]._z;
+		}
+	}
+
+	result = createMesh(meshData._numTriFaces, totalVertices, meshData._fvf, &mesh);
+	if (!result) {
+		cleanupMeshData(&meshData);
+		delete mesh;
+		return false;
+	}
+
+	float *vertices = (float *)mesh->getVertexBuffer().ptr();
+	float *outPtr = vertices;
+	for (i = 0; i < meshData._numVertices; i++) {
+		if (meshData._fvf & DXFVF_XYZ) {
+			*outPtr++ = meshData._vertices[i]._x;
+			*outPtr++ = meshData._vertices[i]._y;
+			*outPtr++ = meshData._vertices[i]._z;
+		}
+		if (meshData._fvf & DXFVF_NORMAL) {
+			*outPtr++ = meshData._vertexNormals[i]._x;
+			*outPtr++ = meshData._vertexNormals[i]._y;
+			*outPtr++ = meshData._vertexNormals[i]._z;
+		}
+		if (meshData._fvf & DXFVF_DIFFUSE) {
+			*outPtr++ = meshData._vertexColors[i].color._r;
+			*outPtr++ = meshData._vertexColors[i].color._g;
+			*outPtr++ = meshData._vertexColors[i].color._b;
+			*outPtr++ = meshData._vertexColors[i].color._a;
+		}
+		if (meshData._fvf & (DXFVF_TEX1)) {
+			*outPtr++ = meshData._texCoords[i]._x;
+			*outPtr++ = meshData._texCoords[i]._y;
+		}
+	}
+
+
+	uint32 *indices = (uint32 *)mesh->getIndexBuffer().ptr();
+	uint32 *indexInPtr = meshData._indices;
+	for (i = 0; i < meshData._numPolyFaces; i++) {
+		uint32 count = meshData._numTriPerFace[i];
+		uint32 firstIndex = *indexInPtr++;
+		// 1 -> 1
+		// 2 -> 2
+		// 3 -> 3
+		// 1 -> 4
+		// 3 -> 5
+		// 4 -> 6
+		while (count--) {
+			*indices++ = firstIndex;
+			*indices++ = *indexInPtr;
+			indexInPtr++;
+			*indices++ = *indexInPtr;
+		}
+		indexInPtr++;
+	}
+
+	if (meshData._materialIndices) {
+		uint32 index = 0;
+		uint32 *attribBuffer = (uint32 *)mesh->getAtribBuffer().ptr();
+		for (i = 0; i < meshData._numPolyFaces; i++) {
+			uint32 count = meshData._numTriPerFace[i];
+			while (count--)
+				attribBuffer[index++] = meshData._materialIndices[i];
+		}
+
+		uint32 attribTableSize = countAttributes(attribBuffer, meshData._numTriFaces);
+		auto attribTable = new DXAttributeRange[attribTableSize];
+		if (!attribTable) {
+			cleanupMeshData(&meshData);
+			delete mesh;
+			return false;
+		}
+		auto rangeTable = mesh->getAttributeTable();
+		rangeTable->_size = attribTableSize;
+		rangeTable->_ptr = attribTable;
+
+		indices = (uint32 *)mesh->getIndexBuffer().ptr();
+		fillAttributeTable(attribBuffer, meshData._numTriFaces, indices, attribTable);
+	}
+
+	uint32 bufferSize = meshData._numMaterials * sizeof(DXMaterial);
+	DXBuffer materials = DXBuffer(bufferSize);
+	if (!materials.ptr()) {
+		cleanupMeshData(&meshData);
+		delete mesh;
+		return false;
+	}
+	memcpy(materials.ptr(), meshData._materials, meshData._numMaterials * sizeof(DXMaterial));
+
+	if (meshData._numTriFaces== 0) {
+		materials.free();
+		cleanupMeshData(&meshData);
+		delete mesh;
+		return false;
+	}
+	DXBuffer adjacency = DXBuffer(meshData._numTriFaces * 3 * sizeof(uint32));
+	if (!adjacency.ptr()) {
+		materials.free();
+		cleanupMeshData(&meshData);
+		delete mesh;
+		return false;
+	}
+	if (!mesh->generateAdjacency((uint32 *)adjacency.ptr())) {
+		materials.free();
+		adjacency.free();
+		cleanupMeshData(&meshData);
+		delete mesh;
+		return false;
+	}
+
+	*meshOut = mesh;
+	adjacencyOut = adjacency;
+	numMaterialsOut = meshData._numMaterials;
+	materialsOut = materials;
+	*skinInfoOut = meshData._skinInfo;
+
+	cleanupMeshData(&meshData, false);
+
+	return result;
+}
+
+} // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xskinmesh.h b/engines/wintermute/base/gfx/xskinmesh.h
new file mode 100644
index 00000000000..dfe37d251c7
--- /dev/null
+++ b/engines/wintermute/base/gfx/xskinmesh.h
@@ -0,0 +1,165 @@
+/* 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/>.
+ *
+ */
+
+/*
+ * Based on skin and mesh code from Wine sources.
+ * Copyright (C) 2005 Henri Verbeet
+ * Copyright (C) 2006 Ivan Gyurdiev
+ * Copyright (C) 2009 David Adam
+ * Copyright (C) 2010 Tony Wasserka
+ * Copyright (C) 2011 Dylan Smith
+ * Copyright (C) 2011 Michael Mc Donnell
+ * Copyright (C) 2013 Christian Costa
+ */
+
+#ifndef WINTERMUTE_XSKINMESH_H
+#define WINTERMUTE_XSKINMESH_H
+
+#include "engines/wintermute/base/gfx/xbuffer.h"
+#include "engines/wintermute/base/gfx/xfile_loader.h"
+#include "engines/wintermute/base/gfx/xmath.h"
+
+namespace Wintermute {
+
+#define DXFVF_XYZ             0x0002
+#define DXFVF_NORMAL          0x0010
+#define DXFVF_DIFFUSE         0x0040
+#define DXFVF_TEX1            0x0100
+
+typedef enum {
+	DXDECLUSAGE_NORMAL        = 3,
+	DXDECLUSAGE_TEXCOORD      = 5,
+	DXDECLUSAGE_TANGENT       = 6,
+	DXDECLUSAGE_BINORMAL      = 7,
+} DXDECLUSAGE;
+
+typedef enum {
+	DXDECLTYPE_FLOAT2         =  1,
+	DXDECLTYPE_FLOAT3         =  2,
+} DXDECLTYPE;
+
+#if defined(SCUMMVM_USE_PRAGMA_PACK)
+#pragma pack(4)
+#endif
+
+struct DXAttributeRange {
+	uint32 _attribId;
+	uint32 _faceStart;
+	uint32 _faceCount;
+	uint32 _vertexStart;
+	uint32 _vertexCount;
+};
+
+struct DXAttributeRangeTable {
+	uint32 _size;
+	DXAttributeRange *_ptr;
+};
+
+typedef union {
+	struct {
+		float _r;
+		float _g;
+		float _b;
+		float _a;
+	} color;
+	float _data[4];
+} DXColorValue;
+
+typedef struct {
+	DXColorValue   _diffuse;
+	DXColorValue   _ambient;
+	DXColorValue   _specular;
+	DXColorValue   _emissive;
+	float          _power;
+	char           _textureFilename[XMAX_NAME_LEN];
+} DXMaterial;
+
+struct DXBone {
+	char *_name;
+	DXMatrix _transform;
+	uint32 _numInfluences;
+	uint32 *_vertices;
+	float *_weights;
+};
+
+#if defined(SCUMMVM_USE_PRAGMA_PACK)
+#pragma pack()
+#endif
+
+class DXSkinInfo {
+	uint32 _fvf;
+	uint32 _numVertices{};
+	uint32 _numBones{};
+	DXBone *_bones{};
+
+public:
+	~DXSkinInfo() { destroy(); }
+	bool create(uint32 vertexCount, uint32 boneCount);
+	void destroy();
+	uint32 getNumBones() { return _numBones; }
+	bool setBoneName(uint32 boneIdx, const char *name);
+	char *getBoneName(uint32 boneIdx) { return _bones[boneIdx]._name; }
+	bool setBoneInfluence(uint32 boneIdx, uint32 numInfluences, const uint32 *vertices, const float *weights);
+	DXBone *getBone(uint32 boneIdx);
+	bool setBoneOffsetMatrix(uint32 boneIdx, const float *boneTransform);
+	DXMatrix *getBoneOffsetMatrix(uint32 boneIdx) { return &_bones[boneIdx]._transform; }
+	bool updateSkinnedMesh(const DXMatrix *boneTransforms, void *srcVertices, void *dstVertices);
+};
+
+class DXMesh {
+	uint32 _numFaces;
+	uint32 _numVertices;
+	uint32 _fvf;
+	uint32 _vertexSize;
+	DXBuffer _vertexBuffer;
+	DXBuffer _indexBuffer;
+	DXBuffer _attribBuffer;
+	DXAttributeRangeTable _attribTable;
+
+	struct DXVertexMetadata {
+		float _key;
+		uint32 _vertexIndex;
+		uint32 _firstSharedIndex;
+	};
+
+	static int compareVertexKeys(const void *a, const void *b);
+
+public:
+	~DXMesh() { destroy(); }
+	bool create(uint32 numFaces, uint32 numVertices, uint32 fvf);
+	void destroy();
+	bool cloneMesh(DXMesh **cloneMeshOut);
+	uint32 getNumFaces() { return _numFaces; }
+	uint32 getNumVertices() { return _numVertices; }
+	uint32 getFVF() { return _fvf; }
+	DXBuffer getVertexBuffer() { return _vertexBuffer; }
+	DXBuffer getIndexBuffer() { return _indexBuffer; }
+	DXBuffer getAtribBuffer() { return _attribBuffer; }
+	DXAttributeRangeTable *getAttributeTable() { return &_attribTable; }
+	bool generateAdjacency(uint32 *adjacency);
+};
+
+bool DXLoadSkinMesh(XFileData *fileData, DXBuffer &adjacencyOut, DXBuffer &materialsOut, uint32 &numMaterialsOut, DXSkinInfo **skinInfoOut, DXMesh **meshOut);
+uint32 DXGetFVFVertexSize(uint32 fvf);
+
+} // namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/xskinmesh_loader.cpp b/engines/wintermute/base/gfx/xskinmesh_loader.cpp
index bbd8dfde5a6..ab78622445b 100644
--- a/engines/wintermute/base/gfx/xskinmesh_loader.cpp
+++ b/engines/wintermute/base/gfx/xskinmesh_loader.cpp
@@ -25,6 +25,7 @@
 #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/xskinmesh.h"
 #include "engines/wintermute/base/gfx/xmodel.h"
 #include "engines/wintermute/base/base_engine.h"
 #include "engines/wintermute/math/math_util.h"
@@ -34,9 +35,11 @@ namespace Wintermute {
 // define constant to make it available to the linker
 const uint32 XSkinMeshLoader::kNullIndex;
 
-XSkinMeshLoader::XSkinMeshLoader(XMesh *mesh, XMeshObject *meshObject) {
+XSkinMeshLoader::XSkinMeshLoader(XMesh *mesh, XMeshObject *meshObject, DXMesh *dxmesh, DXSkinInfo *skinInfo) {
 	_mesh = mesh;
 	_meshObject = meshObject;
+	_dxmesh = dxmesh;
+	_skinInfo = skinInfo;
 
 	_vertexCount = meshObject->_numVertices;
 	// vertex format for .X meshes will be position + normals + textures
@@ -54,435 +57,95 @@ XSkinMeshLoader::~XSkinMeshLoader() {
 }
 
 void XSkinMeshLoader::loadMesh(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) {
-	parsePositionCoords(_meshObject);
+	auto fvf = _dxmesh->getFVF();
+	uint32 vertexSize = DXGetFVFVertexSize(fvf) / sizeof(float);
+	float *vertexBuffer = (float *)_dxmesh->getVertexBuffer().ptr();
+	uint32 offset = 0, normalOffset, /*diffuseOffset, */textureOffset;
 
-	uint numFaces = _meshObject->_numFaces;
-
-	Common::Array<int> indexCountPerFace;
-
-	parseFaces(_meshObject, numFaces, indexCountPerFace);
-
-	uint numChildren = 0;
-	xobj->getChildren(numChildren);
-
-	for (uint32 i = 0; i < numChildren; i++) {
-		XFileData xchildData;
-		XClassType objectType;
-		if (xobj->getChild(i, xchildData)) {
-			if (xchildData.getType(objectType)) {
-				if (objectType == kXClassMeshTextureCoords) {
-					parseTextureCoords(&xchildData);
-				} else if (objectType == kXClassMeshNormals) {
-					parseNormalCoords(&xchildData);
-				} else if (objectType == kXClassMeshMaterialList) {
-					parseMaterials(&xchildData, _mesh->_gameRef, numFaces, filename, materialReferences, indexCountPerFace);
-				} else if (objectType == kXClassMaterial) {
-					Material *mat = new Material(_mesh->_gameRef);
-					mat->loadFromX(&xchildData, filename);
-					_mesh->_materials.add(mat);
-
-					// one material = one index range
-					_mesh->_numAttrs = 1;
-					_indexRanges.push_back(0);
-					_indexRanges.push_back(_indexData.size());
-				} else if (objectType == kXClassSkinMeshHeader) {
-					int boneCount = xchildData.getXSkinMeshHeaderObject()->_nBones;
-					_mesh->_skinnedMesh = boneCount > 0;
-				} else if (objectType == kXClassSkinWeights) {
-					_mesh->_skinnedMesh = true;
-					parseSkinWeights(&xchildData);
-				} else if (objectType == kXClassDeclData) {
-					parseVertexDeclaration(&xchildData);
-				}
-			}
-		}
+	if (fvf & DXFVF_XYZ) {
+		offset += sizeof(DXVector3) / sizeof(float);
+	}
+	if (fvf & DXFVF_NORMAL) {
+		normalOffset = offset;
+		offset += sizeof(DXVector3) / sizeof(float);
+	}
+	if (fvf & DXFVF_DIFFUSE) {
+		//diffuseOffset = offset;
+		offset += sizeof(DXColorValue) / sizeof(float);
+	}
+	if (fvf & DXFVF_TEX1) {
+		textureOffset = offset;
+		offset += sizeof(DXVector2) / sizeof(float);
 	}
 
-	generateAdjacency(_mesh->_adjacency);
-}
-
-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;
+		_vertexPositionData[i * 3 + 0] = vertexBuffer[i * vertexSize + 0];
+		_vertexPositionData[i * 3 + 1] = vertexBuffer[i * vertexSize + 1];
+		_vertexPositionData[i * 3 + 2] = vertexBuffer[i * vertexSize + 2];
 		for (int j = 0; j < 3; ++j) {
 			_vertexData[i * kVertexComponentCount + kPositionOffset + j] = _vertexPositionData[i * 3 + j];
 		}
-
+		// mirror z coordinate to change to OpenGL coordinate system
 		_vertexPositionData[i * 3 + 2] *= -1.0f;
 		_vertexData[i * kVertexComponentCount + kPositionOffset + 2] *= -1.0f;
+
+		if (fvf & DXFVF_NORMAL) {
+			_vertexNormalData[i * 3 + 0] = vertexBuffer[i * vertexSize + normalOffset + 0];
+			_vertexNormalData[i * 3 + 1] = vertexBuffer[i * vertexSize + normalOffset + 1];
+			_vertexNormalData[i * 3 + 2] = vertexBuffer[i * vertexSize + normalOffset + 2];
+			for (int j = 0; j < 3; ++j) {
+				_vertexData[i * kVertexComponentCount + kNormalOffset + j] = _vertexNormalData[i * 3 + j];
+			}
+			// mirror z coordinate to change to OpenGL coordinate system
+			_vertexNormalData[i * 3 + 2] *= -1.0f;
+			_vertexData[i * kVertexComponentCount + kNormalOffset + 2] *= -1.0f;
+		}
+
+		if (fvf & DXFVF_DIFFUSE) {
+			// nothing
+		}
+
+		if (fvf & DXFVF_TEX1) {
+			_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 0] = vertexBuffer[i * vertexSize + textureOffset + 0];
+			_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 1] = vertexBuffer[i * vertexSize + textureOffset + 1];
+		}
 	}
 
-	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];
+	uint numFaces = _meshObject->_numFaces;
+
+	Common::Array<int> indexCountPerFace;
+
+	uint32 *indexPtr = (uint32 *)_dxmesh->getIndexBuffer().ptr();
+
+	for (uint i = 0; i < numFaces; ++i) {
+		XMeshFace *face = &_meshObject->_faces[i];
 		int indexCount = face->_numFaceVertexIndices;
+		uint16 index1, index2, index3, index4, index5, index6;
 		if (indexCount == 3) {
-			uint16 index1 = face->_faceVertexIndices[0];
-			uint16 index2 = face->_faceVertexIndices[1];
-			uint16 index3 = face->_faceVertexIndices[2];
-
+			index1 = *indexPtr++;
+			index2 = *indexPtr++;
+			index3 = *indexPtr++;
 			_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];
-
+		} else {
+			index1 = *indexPtr++;
+			index2 = *indexPtr++;
+			index3 = *indexPtr++;
+			index4 = *indexPtr++;
+			index5 = *indexPtr++;
+			index6 = *indexPtr++;
 			_indexData.push_back(index3);
 			_indexData.push_back(index2);
 			_indexData.push_back(index1);
-
+			_indexData.push_back(index6);
+			_indexData.push_back(index5);
 			_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;
 }
 
 //////////////////////////////////////////////////////////////////////////
diff --git a/engines/wintermute/base/gfx/xskinmesh_loader.h b/engines/wintermute/base/gfx/xskinmesh_loader.h
index c797764ec1e..cc9530e9713 100644
--- a/engines/wintermute/base/gfx/xskinmesh_loader.h
+++ b/engines/wintermute/base/gfx/xskinmesh_loader.h
@@ -36,6 +36,8 @@ class ShadowVolume;
 class SkinMeshHelper;
 class VideoTheoraPlayer;
 struct XMeshObject;
+class DXMesh;
+class DXSkinInfo;
 
 struct SkinWeights {
 	Common::String _boneName;
@@ -51,7 +53,7 @@ class XSkinMeshLoader {
 	friend class SkinMeshHelper;
 
 public:
-	XSkinMeshLoader(XMesh *mesh, XMeshObject *meshObject);
+	XSkinMeshLoader(XMesh *mesh, XMeshObject *meshObject, DXMesh *dxmesh, DXSkinInfo *skinInfo);
 	virtual ~XSkinMeshLoader();
 	void loadMesh(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences);
 
@@ -68,15 +70,10 @@ protected:
 	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);
 	
+	BaseArray<int> _indexRanges;
+	BaseArray<int> _materialIndices;
+
 protected:
 
 	float *_vertexData;
@@ -84,15 +81,14 @@ protected:
 	float *_vertexNormalData;
 	uint32 _vertexCount;
 	Common::Array<uint16> _indexData;
-	
+
 	BaseArray<Math::Matrix4 *> _boneMatrices;
 	BaseArray<SkinWeights> _skinWeightsList;
-	
-	BaseArray<int> _indexRanges;
-	BaseArray<int> _materialIndices;
 
 	XMesh *_mesh;
 	XMeshObject *_meshObject;
+	DXMesh *_dxmesh;
+	DXSkinInfo *_skinInfo;
 };
 
 } // namespace Wintermute
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
index 903fbe0a31d..fa1bc028f56 100644
--- a/engines/wintermute/module.mk
+++ b/engines/wintermute/module.mk
@@ -174,8 +174,10 @@ MODULE_OBJS += \
 	base/gfx/xfile_loader.o \
 	base/gfx/xframe_node.o \
 	base/gfx/xmaterial.o \
+	base/gfx/xmath.o \
 	base/gfx/xmesh.o \
 	base/gfx/xmodel.o \
+	base/gfx/xskinmesh.o \
 	base/gfx/xskinmesh_loader.o \
 	base/gfx/opengl/base_surface_opengl3d.o \
 	base/gfx/opengl/base_render_opengl3d.o \




More information about the Scummvm-git-logs mailing list