// Wild Magic Source Code
// David Eberly
// http://www.geometrictools.com
// Copyright (c) 1998-2007
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.  The license is available for reading at
// either of the locations:
//     http://www.gnu.org/copyleft/lgpl.html
//     http://www.geometrictools.com/License/WildMagicLicense.pdf
//
// Version: 4.0.0 (2006/06/28)

#include "WSceneBuilder.h"
#include "iparamb2.h"
#include "iskin.h"

//----------------------------------------------------------------------------
void WSceneBuilder::ProcessSkin (INode* pkMaxNode, Modifier* pkSkinMod)
{
    // Construct skin controller.  If the Max mesh required splitting during
    // the export, each Magic mesh created during the split will need its own
    // skin controller.  The skin vertex offsets are calculated using the
    // world transformed mesh vertices at the animation's starting time, and
    // the bones' world transforms.
    //
    // pkMaxNode:
    //     Pointer to Max node to which skin modifier is applied.
    // pkSkinMod:
    //     Pointer to the skin modifier object.

    bool bNeedDel;
    TriObject* pkTriObj = GetTriObject(pkMaxNode,&bNeedDel);
    Mesh* pkMesh = &pkTriObj->GetMesh();

    // get interface to MAX's skin controller classes
    ISkin* pkSkin = (ISkin*)pkSkinMod->GetInterface(I_SKIN);
    ISkinContextData* pkSkinData = pkSkin->GetContextInterface(pkMaxNode);

    // Loop through bones, storing the initial bone rotations and
    // translations, and retrieving the corresponding Magic nodes from the
    // scene.
    int iB, iBoneQuantity = pkSkin->GetNumBones();
    Wm4::Node** apkWm4Bones = WM4_NEW Wm4::Node*[iBoneQuantity]; 
    for (iB = 0; iB < iBoneQuantity; iB++)
    {
        INode* pkBoneNode = pkSkin->GetBone(iB);
        apkWm4Bones[iB] = Wm4::StaticCast<Wm4::Node>(
            m_spkScene->GetObjectByName(pkBoneNode->GetName()));
    }

    // Check whether the Magic node corresponding to the Max skin mesh is
    // itself a mesh node (in which case, the MAX mesh didn't require
    // splitting earlier in the export) or whether the node is a "link" node
    // (i.e. the parent node of a number of split meshes).
    std::vector<Wm4::TriMesh*> akWm4Meshes;
    Wm4::Object* pkObject = m_spkScene->GetObjectByName(pkMaxNode->GetName());
    if (pkObject->IsExactly(Wm4::TriMesh::TYPE))
    {
        akWm4Meshes.push_back(Wm4::StaticCast<Wm4::TriMesh>(pkObject));
    }
    else
    {
        Wm4::Node* pkNode = Wm4::StaticCast<Wm4::Node>(pkObject);
        const char* acName = pkNode->GetName().c_str();
        for (int iC = 0; iC < pkNode->GetQuantity(); iC++)
        {
            Wm4::Spatial* pkChild = pkNode->GetChild(iC);
            const char* acCName = pkChild->GetName().c_str();
            if (strncmp(acCName,acName,strlen(acName)) == 0)
            {
                akWm4Meshes.push_back(Wm4::StaticCast<Wm4::TriMesh>(pkChild));
            }
        }
    }

    // Build a skin controller for each of the Magic meshes corresponding to
    // the Max mesh.
    int* aiVerticesPerBone = WM4_NEW int[iBoneQuantity];
    for (int iM = 0; iM < (int)akWm4Meshes.size(); iM++)
    {
        Wm4::TriMesh* pkWm4Mesh = akWm4Meshes[iM];

        // Determine which vertices from the Max mesh are contained in this
        // Magic mesh, and store the Max vertex indices.  TO DO:  The
        // comparison is vertex to vertex.  What if the original vertex
        // array had duplicate vertices?  The hash data structures used in
        // PackVertices should be persistent so that we may use them here
        // rather than doing a potentially expensive double loop.
        Wm4::VertexBuffer* pkVB = pkWm4Mesh->VBuffer;
        int iVQuantity = pkVB->GetVertexQuantity();
        std::vector<int> kVIArray;
        int iV, i, j, k;
        for (iV = 0; iV < iVQuantity; iV++)
        {
            const Wm4::Vector3f& rkV = pkVB->Position3(iV);
            for (i = 0; i < pkMesh->getNumVerts(); i++)
            {
                if (pkMesh->verts[i].x == rkV.X()
                &&  pkMesh->verts[i].y == rkV.Y()
                &&  pkMesh->verts[i].z == rkV.Z())
                {
                    kVIArray.push_back(i);
                    break;
                }
            }
        }

        // determine how many vertices are influenced by each bone
        iVQuantity = (int)kVIArray.size();
        memset(aiVerticesPerBone,0,sizeof(int)*iBoneQuantity);
        for (i = 0; i < iVQuantity; i++)
        {
            iV = kVIArray[i];
            for (j = 0; j < pkSkinData->GetNumAssignedBones(iV); j++)
            {
                iB = pkSkinData->GetAssignedBone(iV,j);
                aiVerticesPerBone[iB]++;
            }
        }

        // If the Max mesh has been split, then it is possible that some
        // bones will have no influence on the current Magic mesh, hence we
        // need to determine the number of "active" bones for this mesh.
        int iBQuantity = 0;
        for (iB = 0; iB < iBoneQuantity; iB++)
        {
            if (aiVerticesPerBone[iB] > 0)
            {
                iBQuantity++;
            }
        }
        assert(iBQuantity > 0);
        if (iBQuantity == 0)
        {
            // mesh is not affected by any bones, go to the next mesh
            continue;
        }

        // allocate the arrays for the skin controller for this mesh
        Wm4::Node** apkBones = WM4_NEW Wm4::Node*[iBQuantity];
        float** aafWeight;
        Wm4::Allocate(iBQuantity,iVQuantity,aafWeight);
        memset(aafWeight[0],0,iBQuantity*iVQuantity*sizeof(float));
        Wm4::Vector3f** aakOffset;
        Wm4::Allocate(iBQuantity,iVQuantity,aakOffset);
        memset(aakOffset[0],0,iBQuantity*iVQuantity*sizeof(Wm4::Vector3f));

        // Store the Magic node pointers corresponding to the active bones
        // for this Magic mesh.
        std::vector<int> kBIArray(iBoneQuantity);
        for (iB = 0, k = 0; iB < iBoneQuantity; iB++)
        {
            if (aiVerticesPerBone[iB] > 0)
            {
                apkBones[k] = apkWm4Bones[iB];
                kBIArray[iB] = k;
                k++;
            }
        }

        // Loop through the list of Max vertices for this Magic mesh,
        // obtaining the vertex weights and calculating the vertex offsets.
        for (i = 0; i < iVQuantity; i++)
        {
            iV = kVIArray[i];
            for (j = 0; j < pkSkinData->GetNumAssignedBones(iV); j++)
            {
                iB = pkSkinData->GetAssignedBone(iV,j);
                k = kBIArray[iB];
                aafWeight[i][k] = pkSkinData->GetBoneWeight(iV,j);
                aakOffset[i][k] = apkBones[k]->World.ApplyInverse(
                    pkWm4Mesh->World.ApplyForward(pkVB->Position3(i)));
            }
        }
        
        Wm4::SkinController* pkSkinController = WM4_NEW Wm4::SkinController(
            iVQuantity,iBQuantity,apkBones,aafWeight,aakOffset);
        pkSkinController->MinTime = 0.0f;
        pkSkinController->MaxTime = TicksToSec(m_iTimeEnd - m_iTimeStart);
        pkWm4Mesh->AttachController(pkSkinController);
    }
    
    if (bNeedDel) 
    {
        WM4_DELETE pkTriObj;
    }

    WM4_DELETE[] apkWm4Bones;
    WM4_DELETE[] aiVerticesPerBone;
}
//----------------------------------------------------------------------------
