#include "stdafx.h"
#include "DK2KMFModel.h"
//////////////////////////////////////////////////////////////////////////
#define MAKE_HEADER_ID(a,b,c,d) ((a) | ((b) 0;
currentMaterial.mFlags_DoubleSided = (materialFlags & KMF_MATERIAL_DOUBLE_SIDED) > 0;
currentMaterial.mFlags_AlphaAdditive = (materialFlags & KMF_MATERIAL_ALPHA_ADDITIVE) > 0;
currentMaterial.mFlags_Translucent = (materialFlags & KMF_MATERIAL_TRANSLUCENT) > 0;
currentMaterial.mFlags_HasSpecular = (materialFlags & KMF_MATERIAL_HAS_SPECULAR) > 0;
currentMaterial.mFlags_HasEmissive = (materialFlags & KMF_MATERIAL_HAS_EMISSIVE) > 0;
currentMaterial.mFlags_Invisible = (materialFlags & KMF_MATERIAL_INVISIBLE) > 0;
READ_FROM_BYTE_STREAM(theStream, currentMaterial.mEmissive);
READ_FROM_BYTE_STREAM(theStream, currentMaterial.mSpecular);
if (!KMF_ReadCString(theStream, currentMaterial.mEnvMappingTextureName))
return false;
}
return true;
}
static bool KMF_ReadSprites(std::istream& theStream, DK2KMFModel& outputData, int theNumSprites, int theNumLODs)
{
cxx_assert(theNumSprites > 0 && theNumLODs > 0);
KMFHeader header;
// allocate sprites
outputData.mMeshArray.resize(theNumSprites);
// read headers
for (DK2KMFModel::SubMesh& refSprite: outputData.mMeshArray)
{
//KMSH/MESH/SPRS/SPHD
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_SPRITES_HEADER))
return false;
// allocate lods for each submesh
if (theNumLODs > 0)
{
refSprite.mLODsArray.resize(theNumLODs);
}
// read triangles count for each lod
for (int iCurrentLOD = 0; iCurrentLOD < theNumLODs; ++iCurrentLOD)
{
int numTriangles = 0;
READ_FROM_BYTE_STREAM(theStream, numTriangles);
// allocate triangles for each lod
if (numTriangles > 0)
{
refSprite.mLODsArray[iCurrentLOD].mTriangleArray.resize(numTriangles);
}
}
// geom info
int numVertices;
float mmFactor;
READ_FROM_BYTE_STREAM(theStream, numVertices);
READ_FROM_BYTE_STREAM(theStream, mmFactor);
refSprite.mFrameVerticesCount = numVertices;
if (numVertices > 0)
{
refSprite.mVertexPositionArray.resize(numVertices);
refSprite.mVertexNormalArray.resize(numVertices);
refSprite.mVertexTexCoordArray.resize(numVertices);
}
} // for
bool hasDummySprites = false;
// read geometries
for (DK2KMFModel::SubMesh& refSprite: outputData.mMeshArray)
{
//KMSH/MESH/SPRS/SPRS
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_SPRITES_DATA_HEADER))
return false;
if (refSprite.mLODsArray.empty())
{
hasDummySprites = true;
SKIP_BYTES(theStream, header.mLength - KMF_CHUNK_HEADER_LENGTH);
continue;
}
int materialIndex;
READ_FROM_BYTE_STREAM(theStream, materialIndex);
refSprite.mMaterialIndex = materialIndex;
// read triangles for each lod
for (DK2KMFModel::SubMeshLOD& currentLOD: refSprite.mLODsArray)
{
for (glm::ivec3& refTriangle: currentLOD.mTriangleArray)
{
KMF_ReadTriangle(theStream, refTriangle);
}
}
// read vertices
for (int ivertex = 0; ivertex < refSprite.mFrameVerticesCount; ++ivertex)
{
unsigned short geometryIndex;
READ_FROM_BYTE_STREAM(theStream, geometryIndex);
// hack to save geometry index in position vector
KMF_EncodeGeomITab(refSprite.mVertexPositionArray[ivertex], geometryIndex);
KMF_ReadTexcoord(theStream, refSprite.mVertexTexCoordArray[ivertex]);
KMF_ReadVector3f(theStream, refSprite.mVertexNormalArray[ivertex]);
}
} // for sprite
if (hasDummySprites)
{
gConsole.LogMessage(eLogLevel_Info, "Dummy sprites found in mesh '%s'", outputData.mName.c_str());
}
return true;
}
static bool KMF_ReadAnimSprites(std::istream& theStream, DK2KMFModel& outputData, int theNumSprites, int theNumLODs)
{
cxx_assert(theNumSprites > 0 && theNumLODs > 0);
KMFHeader header;
// allocate sprites
outputData.mMeshArray.resize(theNumSprites);
// read headers
for (DK2KMFModel::SubMesh& refSprite: outputData.mMeshArray)
{
//KMSH/ANIM/SPRS/SPHD
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_SPRITES_HEADER))
return false;
// allocate lods for each submesh
if (theNumLODs > 0)
{
refSprite.mLODsArray.resize(theNumLODs);
}
// read triangles count for each lod
for (int iCurrentLOD = 0; iCurrentLOD < theNumLODs; ++iCurrentLOD)
{
int numTriangles = 0;
READ_FROM_BYTE_STREAM(theStream, numTriangles);
// allocate triangles for each lod
if (numTriangles > 0)
{
refSprite.mLODsArray[iCurrentLOD].mTriangleArray.resize(numTriangles);
}
}
// geom info
int numVertices;
float mmFactor;
READ_FROM_BYTE_STREAM(theStream, numVertices);
READ_FROM_BYTE_STREAM(theStream, mmFactor);
refSprite.mFrameVerticesCount = numVertices;
if (numVertices > 0)
{
refSprite.mVertexPositionArray.resize(numVertices);
refSprite.mVertexNormalArray.resize(numVertices);
refSprite.mVertexTexCoordArray.resize(numVertices);
}
}
// read geometries
for (DK2KMFModel::SubMesh& refSprite: outputData.mMeshArray)
{
//KMSH/ANIM/SPRS/SPRS
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_SPRITES_DATA_HEADER))
return false;
int materialIndex;
READ_FROM_BYTE_STREAM(theStream, materialIndex);
refSprite.mMaterialIndex = materialIndex;
//KMSH/ANIM/SPRS/SPRS/POLY
if (!KMF_ReadSectionHeader(theStream, header, KMF_ANIM_SPRITES_POLY_HEADER))
return false;
// read triangles for each lod
for (DK2KMFModel::SubMeshLOD& currentLOD: refSprite.mLODsArray)
{
for (glm::ivec3& refTriangle: currentLOD.mTriangleArray)
{
KMF_ReadTriangle(theStream, refTriangle);
}
}
//KMSH/ANIM/SPRS/SPRS/VERT
if (!KMF_ReadSectionHeader(theStream, header, KMF_ANIM_SPRITES_VERT_HEADER))
return false;
// read vertices
for (int ivertex = 0; ivertex < refSprite.mFrameVerticesCount; ++ivertex)
{
KMF_ReadTexcoord(theStream, refSprite.mVertexTexCoordArray[ivertex]);
KMF_ReadVector3f(theStream, refSprite.mVertexNormalArray[ivertex]);
unsigned short geometryIndex;
READ_FROM_BYTE_STREAM(theStream, geometryIndex);
// hack to save geometry index in position vector
KMF_EncodeGeomITab(refSprite.mVertexPositionArray[ivertex], geometryIndex);
}
}
return true;
}
static bool KMF_ReadStaticMeshGeometries(std::istream& theStream, DK2KMFModel& theOutputMesh, int theNumGeometies)
{
std::vector geometries;
geometries.resize(theNumGeometies);
// read geometries
for (glm::vec3& refGeom: geometries)
{
if (!KMF_ReadVector3f(theStream, refGeom))
return false;
}
// decode geometries
for (DK2KMFModel::SubMesh& refSprite: theOutputMesh.mMeshArray)
{
for (glm::vec3& thePosition: refSprite.mVertexPositionArray)
{
unsigned short geo;
KMF_DecodeGeomITab(thePosition, geo);
thePosition = geometries[geo]; // set vertex position
} // for
}
return true;
}
static bool KMF_ReadMeshCtrl(std::istream& theStream, DK2KMFModel& outputData)
{
struct control_t
{
unsigned short b1;
unsigned short b2;
unsigned int c;
};
unsigned int numControls;
READ_FROM_BYTE_STREAM(theStream, numControls);
if (numControls > 0)
{
auto currpos = theStream.tellg();
static std::vector controls;
controls.resize(numControls);
for (control_t& roller: controls)
{
READ_FROM_BYTE_STREAM(theStream, roller);
}
controls.clear();
}
return true;
}
static bool KMF_ReadStaticMesh(std::istream& theStream, DK2KMFModel& outputData)
{
KMFHeader header;
//KMSH/MESH/HEAD
if (!KMF_ReadSectionHeader(theStream, header, KMF_HEAD))
return false;
if (!KMF_ReadCString(theStream, outputData.mName))
return false;
outputData.mFramesCount = 1; // single frame for static mesh
unsigned int numSprites;
unsigned int numGeometries;
unsigned int numLODs;
READ_FROM_BYTE_STREAM(theStream, numSprites);
READ_FROM_BYTE_STREAM(theStream, numGeometries);
if (!KMF_ReadVector3f(theStream, outputData.mTranslation))
return false;
float scale;
READ_FROM_BYTE_STREAM(theStream, scale);
READ_FROM_BYTE_STREAM(theStream, numLODs);
outputData.mScale = scale;
//Controls
//KMSH/MATL/CTRL
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_CONTROL))
return false;
SKIP_BYTES(theStream, header.mLength - KMF_CHUNK_HEADER_LENGTH); // controls are unknown
//Sprites
//KMSH/MESH/SPRS
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_SPRITES))
return false;
if (!KMF_ReadSprites(theStream, outputData, numSprites, numLODs))
return false;
//KMSH/MESH/GEOM
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_GEOM))
return false;
if (!KMF_ReadStaticMeshGeometries(theStream, outputData, numGeometries))
return false;
// success
return true;
}
static bool KMF_ReadAnimMesh(std::istream& theStream, DK2KMFModel& outputData)
{
KMFHeader header;
//KMSH/ANIM/HEAD
if (!KMF_ReadSectionHeader(theStream, header, KMF_HEAD))
return false;
if (!KMF_ReadCString(theStream, outputData.mName))
return false;
unsigned int sprsCount;
unsigned int frameCount;
unsigned int indexCount;
unsigned int geomCount;
float cubeScale;
float scale;
unsigned int numLODs;
// read data
READ_FROM_BYTE_STREAM(theStream, sprsCount);
READ_FROM_BYTE_STREAM(theStream, frameCount);
READ_FROM_BYTE_STREAM(theStream, indexCount);
READ_FROM_BYTE_STREAM(theStream, geomCount);
unsigned int frameFactorFunction;
READ_FROM_BYTE_STREAM(theStream, frameFactorFunction);
cxx_assert((frameFactorFunction == 0) || (frameFactorFunction == 1));
outputData.mFrameFactorFunction = frameFactorFunction;
outputData.mFramesCount = frameCount;
if (!KMF_ReadVector3f(theStream, outputData.mTranslation))
return false;
READ_FROM_BYTE_STREAM(theStream, cubeScale);
READ_FROM_BYTE_STREAM(theStream, scale);
outputData.mCubeScale = cubeScale;
outputData.mScale = scale;
READ_FROM_BYTE_STREAM(theStream, numLODs);
//KMSH/ANIM/CTRL
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_CONTROL))
return false;
if (!KMF_ReadMeshCtrl(theStream, outputData))
return false;
//KMSH/ANIM/SPRS
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_SPRITES))
return false;
if (!KMF_ReadAnimSprites(theStream, outputData, sprsCount, numLODs))
return false;
//KMSH/ANIM/SPRS/ITAB
if (!KMF_ReadSectionHeader(theStream, header, KMF_ANIM_SPRITES_ITAB_HEADER))
return false;
const unsigned int NumItabChunks = static_cast(floorf((frameCount - 1) / 128.0f + 1.0f));
// allocate memory for itab chunks
std::vector itabChunks(NumItabChunks * indexCount);
for (unsigned int iChunk = 0; iChunk < NumItabChunks; ++iChunk)
for (unsigned int iIndex = 0; iIndex < indexCount; ++iIndex)
{
READ_FROM_BYTE_STREAM(theStream, itabChunks[iChunk * indexCount + iIndex]);
}
//KMSH/ANIM/SPRS/GEOM
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH_GEOM))
return false;
std::vector geometries;
geometries.resize(geomCount);
for (KMFAnimGeom& animGeometry : geometries)
{
//10 _BITS_ per coordinate (Z, Y, X) = 30 bits (2 last bits can be thrown away)
// ^ so read 4 bytes
// + 1 byte for frame base
int coordinates;
READ_FROM_BYTE_STREAM(theStream, coordinates);
const float x = (((coordinates >> 20) & 0x3ff) - 0x200) / 511.0f; // x
const float y = (((coordinates >> 10) & 0x3ff) - 0x200) / 511.0f; // y
const float z = (((coordinates >> 0) & 0x3ff) - 0x200) / 511.0f; // z
// prescale
animGeometry.mGeometry.x = x * outputData.mScale;
animGeometry.mGeometry.z = y * outputData.mScale;
animGeometry.mGeometry.y = -z * outputData.mScale;
// frame base
unsigned char frameBase;
READ_FROM_BYTE_STREAM(theStream, frameBase);
animGeometry.mFrameBase = frameBase;
}
//KMSH/ANIM/SPRS/VGEO
if (!KMF_ReadSectionHeader(theStream, header, KMF_ANIM_SPRITES_VGEO_HEADER))
return false;
std::vector offsets(indexCount * frameCount);
for (unsigned int iIndex = 0; iIndex < indexCount; ++iIndex)
for (unsigned int iFrame = 0; iFrame < frameCount; ++iFrame)
{
READ_FROM_BYTE_STREAM(theStream, offsets[iIndex * frameCount + iFrame]);
}
// unpack animation frames
for (DK2KMFModel::SubMesh& submesh: outputData.mMeshArray)
{
std::vector frame0_normals;
std::vector frame0_positions;
frame0_normals.swap(submesh.mVertexNormalArray);
frame0_positions.swap(submesh.mVertexPositionArray);
submesh.mVertexNormalArray.resize(frameCount * submesh.mFrameVerticesCount);
submesh.mVertexPositionArray.resize(frameCount * submesh.mFrameVerticesCount);
for (unsigned int iframe = 0; iframe < frameCount; ++iframe)
{
for (int ivertex = 0; ivertex < submesh.mFrameVerticesCount; ++ivertex)
{
unsigned short itabIndex;
KMF_DecodeGeomITab(frame0_positions[ivertex], itabIndex);
// magic
const int geomIndex = itabChunks[indexCount * (iframe >> 7) + itabIndex] + offsets[itabIndex * frameCount + iframe];
//Simple interpolation must be used to discover the progression from the most
//recent keyframe. The frameBase field of GEOM is used in this interpolation
//to determine percentage progressed for a given frame, and the .next. geom
//element is always geomIndex + 1. The offset value resets at each 128 frame
//chunk. Calculation of the the interpolation geomFactor for a given geomIndex
//is as follows:
//frameBase = GEOM.geom[geomIndex].frameBase
//nextFrameBase = GEOM.geom[geomIndex + 1].frameBase
//geomFactor = ((frame&0x7f) ?frameBase)/(nextFrameBase ?frameBase)
//This makes calculation of the interpolated position at a given geomIndex
//as follows:
//coord = GEOM.geom[geomIndex].coord
//nextCoord = GEOM.geom[geomIndex + 1].coord
//interpCoord = (nextCoord ? coord) ? geomFactor + coord
const int geomIndexNext = (geomIndex + 1) < (int)geomCount ? (geomIndex + 1) : geomIndex;
const KMFAnimGeom& currGeom = geometries[geomIndex];
const KMFAnimGeom& nextGeom = geometries[geomIndexNext];
float geomFactor = 1.0f;
if (currGeom.mFrameBase != nextGeom.mFrameBase)
{
geomFactor = (((iframe & 0x7F) - currGeom.mFrameBase) * 1.0f) / ((nextGeom.mFrameBase - currGeom.mFrameBase) * 1.0f);
}
int lastPose = ((iframe >> 7) * 128 + currGeom.mFrameBase);
int nextPose = ((iframe >> 7) * 128 + nextGeom.mFrameBase);
int idx = iframe * submesh.mFrameVerticesCount + ivertex;
submesh.mVertexNormalArray[idx] = frame0_normals[ivertex];
submesh.mVertexPositionArray[idx] = (nextGeom.mGeometry - currGeom.mGeometry) * geomFactor + currGeom.mGeometry;
}
}
}
return true;
}
static void KMF_ComputeBounds(DK2KMFModel& outputData)
{
outputData.mFramesBounds.resize(outputData.mFramesCount);
// process animation frames
for (int iAnimFrame = 0; iAnimFrame < outputData.mFramesCount; ++iAnimFrame)
{
cxx::aabbox& bounds = outputData.mFramesBounds[iAnimFrame];
bounds.reset();
for (const DK2KMFModel::SubMesh& subMesh: outputData.mMeshArray)
{
const glm::vec3* verticesPos = &subMesh.mVertexPositionArray[iAnimFrame * subMesh.mFrameVerticesCount];
for (int ivertex = 0; ivertex < subMesh.mFrameVerticesCount; ++ivertex)
{
bounds.extend(verticesPos[ivertex]);
}
}
} // for
}
//////////////////////////////////////////////////////////////////////////
bool DK2_KMF_LoadFromStream(std::istream& theStream, DK2KMFModel& outputData)
{
outputData.Clear();
KMFHeader header;
if (!KMF_ReadSectionHeader(theStream, header, KMF_HEADER_IDENTIFIER))
return false;
unsigned int formatVersion = 0;
READ_FROM_BYTE_STREAM(theStream, formatVersion);
//KMSH/HEAD
if (!KMF_ReadSectionHeader(theStream, header, KMF_HEAD))
return false;
KMF_MeshType meshType;
READ_FROM_BYTE_STREAM(theStream, meshType); // mesh type
bool meshTypeSupported = meshType == DK2_MESH_TYPE_STATIC ||
meshType == DK2_MESH_TYPE_ANIMATED;// ||
//meshType == DK2_MESH_TYPE_GROUP;
// TODO : add groups support
if (!meshTypeSupported)
return false;
SKIP_BYTES(theStream, 4); // unknown data
//KMSH/MATL
if (meshType != DK2_MESH_TYPE_GROUP)
{
if (!KMF_ReadSectionHeader(theStream, header, KMF_MATERIALS))
return false;
if (!KMF_ReadMaterials(theStream, outputData))
return false;
}
//KMSH/MESH
if (meshType == DK2_MESH_TYPE_STATIC)
{
if (!KMF_ReadSectionHeader(theStream, header, KMF_MESH))
return false;
if (!KMF_ReadStaticMesh(theStream, outputData))
return false;
}
//KMSH/ANIM
if (meshType == DK2_MESH_TYPE_ANIMATED)
{
if (!KMF_ReadSectionHeader(theStream, header, KMF_ANIM))
return false;
if (!KMF_ReadAnimMesh(theStream, outputData))
return false;
}
KMF_ComputeBounds(outputData);
return true;
}