[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/codenamecpp/GLKeeper/main/src/DK2KMFModel.cpp [Back]  [Original]

#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;
}

Web Proxy Viewer  |  New URL  |  Original Page