447 lines
13 KiB
C++
447 lines
13 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/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2006-2010 - Frictional Games
|
|
*
|
|
* This file is part of HPL1 Engine.
|
|
*/
|
|
|
|
#include "hpl1/engine/impl/MeshLoaderMSH.h"
|
|
|
|
#include "hpl1/engine/graphics/LowLevelGraphics.h"
|
|
#include "hpl1/engine/graphics/VertexBuffer.h"
|
|
#include "hpl1/engine/system/String.h"
|
|
#include "hpl1/engine/system/low_level_system.h"
|
|
|
|
#include "hpl1/engine/graphics/Material.h"
|
|
#include "hpl1/engine/graphics/Mesh.h"
|
|
#include "hpl1/engine/graphics/SubMesh.h"
|
|
#include "hpl1/engine/resources/MaterialManager.h"
|
|
|
|
#include "hpl1/engine/impl/tinyXML/tinyxml.h"
|
|
|
|
#include "hpl1/engine/math/Math.h"
|
|
|
|
namespace hpl {
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CONSTRUCTORS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cMeshLoaderMSH::cMeshLoaderMSH(iLowLevelGraphics *apLowLevelGraphics) : iMeshLoader(apLowLevelGraphics) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cMeshLoaderMSH::~cMeshLoaderMSH() {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PUBLIC METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
// Definition to make the error exit not so bloated.
|
|
#define ExitLoad() \
|
|
hplDelete(pMesh); \
|
|
hplDelete(pXmlDoc); \
|
|
return NULL;
|
|
|
|
cWorld3D *cMeshLoaderMSH::LoadWorld(const tString &asFile, cScene *apScene, tWorldLoadFlag aFlags) {
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cMesh *cMeshLoaderMSH::LoadMesh(const tString &asFile, tMeshLoadFlag aFlags) {
|
|
cMesh *pMesh = hplNew(cMesh, (cString::GetFileName(asFile), mpMaterialManager, mpAnimationManager));
|
|
// If the mesh is animated there are some property differences, the vertex buffer
|
|
// Must be Stream instead of static for example.
|
|
bool bIsAnimated = false;
|
|
|
|
/////////////////////////////////////////////////
|
|
// LOAD THE DOCUMENT
|
|
TiXmlDocument *pXmlDoc = hplNew(TiXmlDocument, (asFile.c_str()));
|
|
if (pXmlDoc->LoadFile() == false) {
|
|
Error("Couldn't load XML file '%s'!\n", asFile.c_str());
|
|
ExitLoad();
|
|
}
|
|
|
|
// Get the root.
|
|
TiXmlElement *pRootElem = pXmlDoc->RootElement();
|
|
|
|
/////////////////////////////////////////////////
|
|
// LOAD SUBMESHES
|
|
// Load the root
|
|
TiXmlElement *pSubMeshesRootElem = pRootElem->FirstChildElement("SubMeshes");
|
|
if (pSubMeshesRootElem == NULL) {
|
|
Error("No mesh data in XML file '%s'!\n", asFile.c_str());
|
|
ExitLoad();
|
|
}
|
|
|
|
// Iterate through the sub meshes
|
|
TiXmlElement *pSubMeshElem = pSubMeshesRootElem->FirstChildElement();
|
|
while (pSubMeshElem) {
|
|
//////////////////
|
|
// Create sub mesh
|
|
cSubMesh *pSubMesh = pMesh->CreateSubMesh(pSubMeshElem->Attribute("name"));
|
|
|
|
//////////////////
|
|
// Set material
|
|
const char *pMatName = pSubMeshElem->Attribute("material");
|
|
if (pMatName == NULL) {
|
|
Error("No material found for mesh '%s'\n", asFile.c_str());
|
|
ExitLoad();
|
|
}
|
|
|
|
iMaterial *pMaterial = mpMaterialManager->CreateMaterial(pMatName);
|
|
pSubMesh->SetMaterial(pMaterial);
|
|
|
|
////////////////////
|
|
// Get the vertices
|
|
TiXmlElement *pVtxElem = pSubMeshElem->FirstChildElement("Vertices");
|
|
int lVtxSize = cString::ToInt(pVtxElem->Attribute("size"), 0);
|
|
tVertexFlag vtxFlags = 0;
|
|
bool bTangents = false;
|
|
|
|
// Check what type of vertices are included.
|
|
if (pVtxElem->FirstChild("Normal"))
|
|
vtxFlags |= eVertexFlag_Normal;
|
|
if (pVtxElem->FirstChild("Position"))
|
|
vtxFlags |= eVertexFlag_Position;
|
|
if (pVtxElem->FirstChild("Texture"))
|
|
vtxFlags |= eVertexFlag_Texture0;
|
|
if (pVtxElem->FirstChild("Color"))
|
|
vtxFlags |= eVertexFlag_Color0;
|
|
if (pVtxElem->FirstChild("Tangent")) {
|
|
vtxFlags |= eVertexFlag_Texture1;
|
|
bTangents = true;
|
|
}
|
|
|
|
// Create the vertex buffer
|
|
eVertexBufferUsageType usageType = bIsAnimated ? eVertexBufferUsageType_Stream : eVertexBufferUsageType_Static;
|
|
iVertexBuffer *pVtxBuff = mpLowLevelGraphics->CreateVertexBuffer(vtxFlags,
|
|
eVertexBufferDrawType_Tri,
|
|
usageType,
|
|
0, 0);
|
|
|
|
pVtxBuff->SetTangents(bTangents);
|
|
|
|
// Fill the arrays
|
|
for (int i = 0; i < klNumOfVertexFlags; i++) {
|
|
if (kvVertexFlags[i] & vtxFlags) {
|
|
int lElemPerVtx = 3;
|
|
if (kvVertexFlags[i] & eVertexFlag_Texture1 || kvVertexFlags[i] & eVertexFlag_Color0) {
|
|
lElemPerVtx = 4;
|
|
}
|
|
|
|
TiXmlElement *pElem = pVtxElem->FirstChildElement(GetVertexName(kvVertexFlags[i]));
|
|
|
|
pVtxBuff->ResizeArray(kvVertexFlags[i], lVtxSize * lElemPerVtx);
|
|
float *pArray = pVtxBuff->GetArray(kvVertexFlags[i]);
|
|
|
|
// Log("TYPE: %s:\n",GetVertexName(kvVertexFlags[i]));
|
|
FillVtxArray(pArray, pElem->Attribute("data"), lVtxSize * lElemPerVtx);
|
|
}
|
|
}
|
|
|
|
////////////////////
|
|
// Get Indices
|
|
|
|
TiXmlElement *pIdxElem = pSubMeshElem->FirstChildElement("Indices");
|
|
int lIdxSize = cString::ToInt(pIdxElem->Attribute("size"), 0);
|
|
|
|
// Log("TYPE: Indices\n");
|
|
pVtxBuff->ResizeIndices(lIdxSize);
|
|
FillIdxArray(pVtxBuff->GetIndices(), pIdxElem->Attribute("data"), lIdxSize);
|
|
|
|
///////////////////
|
|
// Compile vertex buffer
|
|
pVtxBuff->Compile(0);
|
|
|
|
pSubMesh->SetVertexBuffer(pVtxBuff);
|
|
|
|
/////////////////
|
|
// Next element
|
|
pSubMeshElem = pSubMeshesRootElem->NextSiblingElement();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
// LOAD SKELETON
|
|
|
|
hplDelete(pXmlDoc);
|
|
|
|
return pMesh;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cMeshLoaderMSH::SaveMesh(cMesh *apMesh, const tString &asFile) {
|
|
TiXmlDocument *pXmlDoc = hplNew(TiXmlDocument, (asFile.c_str()));
|
|
|
|
// Root
|
|
TiXmlElement XmlRoot("HPL_Mesh");
|
|
TiXmlElement *pRootElem = static_cast<TiXmlElement *>(pXmlDoc->InsertEndChild(XmlRoot));
|
|
|
|
// SubMeshes
|
|
TiXmlElement XmlSubMeshes("SubMeshes");
|
|
TiXmlElement *pSubMeshesElem = static_cast<TiXmlElement *>(pRootElem->InsertEndChild(XmlSubMeshes));
|
|
|
|
// SubMesh
|
|
for (int i = 0; i < apMesh->GetSubMeshNum(); i++) {
|
|
cSubMesh *pSubMesh = apMesh->GetSubMesh(i);
|
|
iVertexBuffer *pVtxBuff = pSubMesh->GetVertexBuffer();
|
|
int lVtxSize = pVtxBuff->GetVertexNum();
|
|
int lIdxSize = pVtxBuff->GetIndexNum();
|
|
|
|
// Create element
|
|
TiXmlElement XmlSubMesh("SubMesh");
|
|
TiXmlElement *pSubMeshElem = static_cast<TiXmlElement *>(pSubMeshesElem->InsertEndChild(XmlSubMesh));
|
|
|
|
// Set data
|
|
pSubMeshElem->SetAttribute("name", pSubMesh->GetName().c_str());
|
|
iMaterial *pMat = pSubMesh->GetMaterial();
|
|
if (pMat)
|
|
pSubMeshElem->SetAttribute("material", pMat->GetName().c_str());
|
|
|
|
// Vertices
|
|
TiXmlElement XmlVtx("Vertices");
|
|
TiXmlElement *pVtxElem = static_cast<TiXmlElement *>(pSubMeshElem->InsertEndChild(XmlVtx));
|
|
pVtxElem->SetAttribute("size", lVtxSize);
|
|
|
|
for (int j = 0; j < klNumOfVertexFlags; j++) {
|
|
if (kvVertexFlags[j] & pVtxBuff->GetVertexFlags()) {
|
|
int lSizeMul = kvVertexElements[i];
|
|
// Only support texture1 coordinate as tangent for now.
|
|
if (kvVertexFlags[j] & eVertexFlag_Texture1)
|
|
lSizeMul = 4;
|
|
|
|
SaveFloatData(pVtxElem, lVtxSize * lSizeMul, GetVertexName(kvVertexFlags[j]),
|
|
pVtxBuff->GetArray(kvVertexFlags[j]));
|
|
}
|
|
}
|
|
|
|
// Indices
|
|
TiXmlElement XmlIdx("Indices");
|
|
TiXmlElement *pIdxElem = static_cast<TiXmlElement *>(pSubMeshElem->InsertEndChild(XmlIdx));
|
|
pIdxElem->SetAttribute("size", lIdxSize);
|
|
SaveIntData(pIdxElem, lIdxSize, pVtxBuff->GetIndices());
|
|
}
|
|
|
|
bool bRet = pXmlDoc->SaveFile();
|
|
if (bRet == false)
|
|
Error("Couldn't save mesh to '%s'", asFile.c_str());
|
|
|
|
hplDelete(pXmlDoc);
|
|
return bRet;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cMeshLoaderMSH::IsSupported(const tString asFileType) {
|
|
if (asFileType == "msh")
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cMeshLoaderMSH::AddSupportedTypes(tStringVec *avFileTypes) {
|
|
avFileTypes->push_back("msh");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PRIVATE METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cMeshLoaderMSH::SaveFloatData(TiXmlElement *apRoot, int alSize, const char *asName, float *apData) {
|
|
// Pos
|
|
TiXmlElement XmlData(asName);
|
|
TiXmlElement *pElem = static_cast<TiXmlElement *>(apRoot->InsertEndChild(XmlData));
|
|
|
|
tString sData = "";
|
|
char sTemp[20];
|
|
|
|
for (int i = 0; i < alSize; i++) {
|
|
float fNum = apData[i];
|
|
int j;
|
|
// get the number of decimals needed
|
|
for (j = 6; j > 0; j--) {
|
|
if (((int)(fNum * 10 * (float)j)) % 10 != 0) {
|
|
break;
|
|
}
|
|
}
|
|
int lDecimals = j;
|
|
|
|
switch (lDecimals) {
|
|
case 0:
|
|
snprintf(sTemp, 20, "%.0f", apData[i]);
|
|
break;
|
|
case 1:
|
|
snprintf(sTemp, 20, "%.1f", apData[i]);
|
|
break;
|
|
case 2:
|
|
snprintf(sTemp, 20, "%.2f", apData[i]);
|
|
break;
|
|
case 3:
|
|
snprintf(sTemp, 20, "%.3f", apData[i]);
|
|
break;
|
|
case 4:
|
|
snprintf(sTemp, 20, "%.4f", apData[i]);
|
|
break;
|
|
case 5:
|
|
snprintf(sTemp, 20, "%.5f", apData[i]);
|
|
break;
|
|
case 6:
|
|
snprintf(sTemp, 20, "%.6f", apData[i]);
|
|
break;
|
|
default:
|
|
error("trying to serialize a floating point value with a precision that is not in the range 1-6");
|
|
}
|
|
|
|
sData += sTemp;
|
|
|
|
if (i != alSize - 1)
|
|
sData += " ";
|
|
}
|
|
|
|
pElem->SetAttribute("data", sData.c_str());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
const char *cMeshLoaderMSH::GetVertexName(tVertexFlag aFlag) {
|
|
switch (aFlag) {
|
|
case eVertexFlag_Normal:
|
|
return "Normal";
|
|
case eVertexFlag_Position:
|
|
return "Position";
|
|
case eVertexFlag_Color0:
|
|
return "Color";
|
|
case eVertexFlag_Texture0:
|
|
return "Texture";
|
|
case eVertexFlag_Texture1:
|
|
return "Tangent";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cMeshLoaderMSH::SaveIntData(TiXmlElement *apElem, int alSize, unsigned int *apData) {
|
|
tString sData = "";
|
|
char sTemp[10];
|
|
|
|
for (int i = 0; i < alSize; i++) {
|
|
snprintf(sTemp, 10, "%d", apData[i]);
|
|
sData += sTemp;
|
|
|
|
if (i != alSize - 1)
|
|
sData += " ";
|
|
}
|
|
|
|
apElem->SetAttribute("data", sData.c_str());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cMeshLoaderMSH::FillVtxArray(float *apArray, const char *apString, int alSize) {
|
|
if (apString == NULL) {
|
|
Error("Data is NULL!\n");
|
|
return;
|
|
}
|
|
|
|
char vTempChar[20];
|
|
int lTempCharCount = 0;
|
|
|
|
int lArrayCount = 0;
|
|
int lStringCount = 0;
|
|
|
|
while (lArrayCount < alSize) {
|
|
char c = apString[lStringCount];
|
|
// if a space is found, convert the previous characters to a float.
|
|
if (c == ' ' || c == 0) {
|
|
if (lTempCharCount > 0) {
|
|
vTempChar[lTempCharCount] = 0;
|
|
apArray[lArrayCount] = (float)atof(vTempChar);
|
|
|
|
lTempCharCount = 0;
|
|
lArrayCount++;
|
|
}
|
|
}
|
|
// If character is not a space, add to temp char.
|
|
else {
|
|
vTempChar[lTempCharCount] = apString[lStringCount];
|
|
lTempCharCount++;
|
|
}
|
|
|
|
lStringCount++;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cMeshLoaderMSH::FillIdxArray(unsigned int *apArray, const char *apString, int alSize) {
|
|
char vTempChar[20];
|
|
int lTempCharCount = 0;
|
|
|
|
int lArrayCount = 0;
|
|
int lStringCount = 0;
|
|
|
|
while (lArrayCount < alSize) {
|
|
char c = apString[lStringCount];
|
|
|
|
// if a space is found, convert the previous characters to a float.
|
|
if (c == ' ' || c == 0) {
|
|
if (lTempCharCount > 0) {
|
|
vTempChar[lTempCharCount] = 0;
|
|
apArray[lArrayCount] = (unsigned int)atoi(vTempChar);
|
|
// Log("Adding: %d\n",apArray[lArrayCount]);
|
|
|
|
lTempCharCount = 0;
|
|
lArrayCount++;
|
|
}
|
|
}
|
|
// If character is not a space, add to temp char.
|
|
else {
|
|
vTempChar[lTempCharCount] = c;
|
|
lTempCharCount++;
|
|
}
|
|
|
|
lStringCount++;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
} // namespace hpl
|