Files
2026-02-02 04:50:13 +01:00

1186 lines
33 KiB
C++

/* 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;
}
bool DXComputeBoundingBox(DXVector3 *pfirstposition, uint32 numvertices, uint32 dwstride, DXVector3 *pmin, DXVector3 *pmax) {
DXVector3 vec;
uint32 i;
if (!pfirstposition || !pmin || !pmax )
return false;
*pmin = *pfirstposition;
*pmax = *pmin;
for (i = 0; i < numvertices; i++) {
vec = *((DXVector3 *)((byte *)pfirstposition + dwstride * i));
if (vec._x < pmin->_x)
pmin->_x = vec._x;
if (vec._x > pmax->_x)
pmax->_x = vec._x;
if (vec._y < pmin->_y)
pmin->_y = vec._y;
if (vec._y > pmax->_y)
pmax->_y = vec._y;
if (vec._z < pmin->_z)
pmin->_z = vec._z;
if (vec._z > pmax->_z)
pmax->_z = vec._z;
}
return true;
}
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 fvf, uint32 boneCount, DXSkinInfo **skinInfo) {
if (!skinInfo)
return false;
auto skin = new DXSkinInfo();
if (!skin)
return false;
if (!skin->create(vertexCount, fvf, 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 = (uint32)-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] = (uint32)-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 fvf, uint32 boneCount) {
_numVertices = vertexCount;
_numBones = boneCount;
_fvf = fvf;
_bones = new DXBone[boneCount];
if (!_bones) {
return false;
}
for (uint32 i = 0; i < boneCount; i++) {
_bones[i]._name = nullptr;
_bones[i]._numInfluences = 0;
_bones[i]._vertices = nullptr;
_bones[i]._weights = nullptr;
}
return true;
}
void DXSkinInfo::destroy() {
delete[] _bones;
_bones = nullptr;
}
bool DXSkinInfo::updateSkinnedMesh(const DXMatrix *boneTransforms, void *srcVertices, void *dstVertices) {
uint32 vertexSize = DXGetFVFVertexSize(_fvf);
uint32 normalOffset = sizeof(DXVector3);
uint32 i, j;
for (i = 0; i < _numVertices; i++) {
DXVector3 *position = (DXVector3 *)((byte *)dstVertices + vertexSize * i);
position->_x = 0.0f;
position->_y = 0.0f;
position->_z = 0.0f;
}
for (i = 0; i < _numBones; i++) {
DXMatrix boneMatrix = boneTransforms[i];
for (j = 0; j < _bones[i]._numInfluences; j++) {
DXVector3 position;
DXVector3 *positionSrc = (DXVector3 *)((byte *)srcVertices + vertexSize * _bones[i]._vertices[j]);
DXVector3 *positionDst = (DXVector3 *)((byte *)dstVertices + vertexSize * _bones[i]._vertices[j]);
float weight = _bones[i]._weights[j];
DXVec3TransformCoord(&position, positionSrc, &boneMatrix);
positionDst->_x += weight * position._x;
positionDst->_y += weight * position._y;
positionDst->_z += weight * position._z;
}
}
if (_fvf & DXFVF_NORMAL) {
for (i = 0; i < _numVertices; i++) {
DXVector3 *normal = (DXVector3 *)((byte *)dstVertices + vertexSize * i + normalOffset);
normal->_x = 0.0f;
normal->_y = 0.0f;
normal->_z = 0.0f;
}
for (i = 0; i < _numBones; i++) {
DXMatrix boneInverse = boneTransforms[i];
DXMatrixInverse(&boneInverse, NULL, &boneInverse);
DXMatrixTranspose(&boneInverse, &boneInverse);
for (j = 0; j < _bones[i]._numInfluences; j++) {
DXVector3 normal;
DXVector3 *normalSrc = (DXVector3 *)((byte *)srcVertices + vertexSize * _bones[i]._vertices[j] + normalOffset);
DXVector3 *normalDst = (DXVector3 *)((byte *)dstVertices + vertexSize * _bones[i]._vertices[j] + normalOffset);
float weight = _bones[i]._weights[j];
DXVec3TransformNormal(&normal, normalSrc, &boneInverse);
normalDst->_x += weight * normal._x;
normalDst->_y += weight * normal._y;
normalDst->_z += weight * normal._z;
}
}
for (i = 0; i < _numVertices; i++) {
DXVector3 *normalDest = (DXVector3 *)((byte *)dstVertices + (i * vertexSize) + normalOffset);
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 = (uint32)-1;
uint32 textureOffset = (uint32)-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:
normalOffset = vertexSize;
meshData->_fvf |= DXFVF_NORMAL;
break;
case DXDECLUSAGE_TEXCOORD:
textureOffset = vertexSize;
meshData->_fvf |= DXFVF_TEX1;
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 (meshData->_fvf & DXFVF_NORMAL) {
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));
}
if (meshData->_fvf & DXFVF_TEX1) {
delete[] meshData->_texCoords;
meshData->_texCoords = new DXVector2[meshData->_numVertices];
if (!meshData->_texCoords) {
return false;
}
}
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->_fvf, 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;
uint32 nbChildren, 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;
uint32 nbChildren, 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;
}
for (i = 0; i < meshData->_numNormals; i++) {
meshData->_normals[i]._x = normalsObj->_normals[i]._x;
meshData->_normals[i]._y = normalsObj->_normals[i]._y;
meshData->_normals[i]._z = normalsObj->_normals[i]._z;
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;
}
for (i = 0; i < meshData->_numVertices; i++) {
meshData->_vertices[i]._x = meshObj->_vertices[i]._x;
meshData->_vertices[i]._y = meshObj->_vertices[i]._y;
meshData->_vertices[i]._z = meshObj->_vertices[i]._z;
}
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->_fvf, 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 &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;
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];
for (i = 0; i < meshData._numVertices; i++) {
meshData._vertexNormals[i]._x = 0.0f;
meshData._vertexNormals[i]._y = 0.0f;
meshData._vertexNormals[i]._z = 0.0f;
}
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));
*meshOut = mesh;
numMaterialsOut = meshData._numMaterials;
materialsOut = materials;
*skinInfoOut = meshData._skinInfo;
cleanupMeshData(&meshData, false);
return result;
}
} // namespace Wintermute