//****************************************************************************//
// physique.cpp                                                               //
// Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger                       //
//****************************************************************************//
// 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.                                            //
//****************************************************************************//

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

//****************************************************************************//
// Includes                                                                   //
//****************************************************************************//

#include "cal3d/error.h"
#include "cal3d/physique.h"
#include "cal3d/coremodel.h"
#include "cal3d/model.h"
#include "cal3d/mesh.h"
#include "cal3d/submesh.h"
#include "cal3d/skeleton.h"
#include "cal3d/bone.h"
#include "cal3d/coresubmesh.h"
#include "cal3d/coresubmorphtarget.h"

 /*****************************************************************************/
/** Constructs the physique instance.
  *
  * This function is the default constructor of the physique instance.
  *****************************************************************************/

CalPhysique::CalPhysique ():m_pModel (0), m_Normalize (true)
{
}

 /*****************************************************************************/
/** Destructs the physique instance.
  *
  * This function is the destructor of the physique instance.
  *****************************************************************************/

CalPhysique::~CalPhysique ()
{
}

 /*****************************************************************************/
/** Calculates the transformed vertex data.
  *
  * This function calculates and returns the transformed vertex data of a
  * specific submesh.
  *
  * @param pSubmesh A pointer to the submesh from which the vertex data should
  *                 be calculated and returned.
  * @param pVertexBuffer A pointer to the user-provided buffer where the vertex
  *                      data is written to.
  *
  * @return The number of vertices written to the buffer.
  *****************************************************************************/

int CalPhysique::calculateVertices (CalSubmesh * pSubmesh,
                                    float *pVertexBuffer)
{
  // get bone vector of the skeleton
  std::vector < CalBone * >&vectorBone =
    m_pModel->getSkeleton ()->getVectorBone ();

  // get vertex vector of the core submesh
  std::vector < CalCoreSubmesh::Vertex > &vectorVertex =
    pSubmesh->getCoreSubmesh ()->getVectorVertex ();

  // get physical property vector of the core submesh
  std::vector < CalCoreSubmesh::PhysicalProperty > &vectorPhysicalProperty =
    pSubmesh->getCoreSubmesh ()->getVectorPhysicalProperty ();

  // get the number of vertices
  int vertexCount;
  vertexCount = pSubmesh->getVertexCount ();

  // get the sub morph target vector from the core sub mesh
  std::vector < CalCoreSubMorphTarget * >&vectorSubMorphTarget =
    pSubmesh->getCoreSubmesh ()->getVectorCoreSubMorphTarget ();

  // calculate the base weight
  float baseWeight = pSubmesh->getBaseWeight ();

  // get the number of morph targets
  int morphTargetCount = pSubmesh->getMorphTargetWeightCount ();

  // calculate all submesh vertices
  int vertexId;
  for (vertexId = 0; vertexId < vertexCount; ++vertexId)
      {
        // get the vertex
        CalCoreSubmesh::Vertex & vertex = vectorVertex[vertexId];

        // blend the morph targets
        CalVector position (0, 0, 0);
        if (baseWeight == 1.0f)
            {
              position.x = vertex.position.x;
              position.y = vertex.position.y;
              position.z = vertex.position.z;
            }
        else
            {
              position.x = baseWeight * vertex.position.x;
              position.y = baseWeight * vertex.position.y;
              position.z = baseWeight * vertex.position.z;
              int morphTargetId;
              for (morphTargetId = 0; morphTargetId < morphTargetCount;
                   ++morphTargetId)
                  {
                    CalCoreSubMorphTarget::BlendVertex & blendVertex =
                      vectorSubMorphTarget[morphTargetId]->
                      getVectorBlendVertex ()[vertexId];
                    float currentWeight =
                      pSubmesh->getMorphTargetWeight (morphTargetId);
                    position.x += currentWeight * blendVertex.position.x;
                    position.y += currentWeight * blendVertex.position.y;
                    position.z += currentWeight * blendVertex.position.z;
                  }
            }

        // initialize vertex
        float x, y, z;
        x = 0.0f;
        y = 0.0f;
        z = 0.0f;

        // blend together all vertex influences
        int influenceId;
        int influenceCount = (int) vertex.vectorInfluence.size ();
        for (influenceId = 0; influenceId < influenceCount; ++influenceId)
            {
              // get the influence
              CalCoreSubmesh::Influence & influence =
                vertex.vectorInfluence[influenceId];

              // get the bone of the influence vertex
              CalBone *pBone;
              pBone = vectorBone[influence.boneId];

              // transform vertex with current state of the bone
              CalVector v (position);
              v *= pBone->getTransformMatrix ();
              v += pBone->getTranslationBoneSpace ();

              x += influence.weight * v.x;
              y += influence.weight * v.y;
              z += influence.weight * v.z;
            }

        // save vertex position
        if (pSubmesh->getCoreSubmesh ()->getSpringCount () > 0
            && pSubmesh->hasInternalData ())
            {
              // get the pgysical property of the vertex
              CalCoreSubmesh::PhysicalProperty & physicalProperty =
                vectorPhysicalProperty[vertexId];

              // assign new vertex position if there is no vertex weight
              if (physicalProperty.weight == 0.0f)
                  {
                    pVertexBuffer[0] = x;
                    pVertexBuffer[1] = y;
                    pVertexBuffer[2] = z;
                  }
            }
        else
            {
              pVertexBuffer[0] = x;
              pVertexBuffer[1] = y;
              pVertexBuffer[2] = z;
            }

        // next vertex position in buffer
        pVertexBuffer += 3;
      }

  return vertexCount;
}

 /*****************************************************************************/
/** Calculates the transformed tangent space data.
  *
  * This function calculates and returns the transformed tangent space data of a
  * specific submesh.
  *
  * @param pSubmesh A pointer to the submesh from which the tangent space data 
  *                 should be calculated and returned.
  * @param mapId
  * @param pTangentSpaceBuffer A pointer to the user-provided buffer where the tangent 
  *                 space data is written to.
  *
  * @return The number of tangent spaces written to the buffer.
  *****************************************************************************/

int CalPhysique::calculateTangentSpaces (CalSubmesh * pSubmesh, int mapId,
                                         float *pTangentSpaceBuffer)
{
  if ((mapId < 0)
      || (mapId >=
          (int) pSubmesh->getCoreSubmesh ()->getVectorVectorTangentSpace ().
          size ()))
    return false;

  // get bone vector of the skeleton
  std::vector < CalBone * >&vectorBone =
    m_pModel->getSkeleton ()->getVectorBone ();

  // get vertex vector of the submesh
  std::vector < CalCoreSubmesh::Vertex > &vectorVertex =
    pSubmesh->getCoreSubmesh ()->getVectorVertex ();

  // get tangent space vector of the submesh
  std::vector < CalCoreSubmesh::TangentSpace > &vectorTangentSpace =
    pSubmesh->getCoreSubmesh ()->getVectorVectorTangentSpace ()[mapId];

  // get the number of vertices
  int vertexCount;
  vertexCount = pSubmesh->getVertexCount ();

  // calculate normal for all submesh vertices
  int vertexId;
  for (vertexId = 0; vertexId < vertexCount; vertexId++)
      {
        CalCoreSubmesh::TangentSpace & tangentSpace =
          vectorTangentSpace[vertexId];

        // get the vertex
        CalCoreSubmesh::Vertex & vertex = vectorVertex[vertexId];

        // initialize tangent
        float tx, ty, tz;
        tx = 0.0f;
        ty = 0.0f;
        tz = 0.0f;

        // blend together all vertex influences
        int influenceId;
        int influenceCount = (int) vertex.vectorInfluence.size ();

        for (influenceId = 0; influenceId < influenceCount; influenceId++)
            {
              // get the influence
              CalCoreSubmesh::Influence & influence =
                vertex.vectorInfluence[influenceId];

              // get the bone of the influence vertex
              CalBone *pBone;
              pBone = vectorBone[influence.boneId];

              // transform normal with current state of the bone
              CalVector v (tangentSpace.tangent);
              v *= pBone->getTransformMatrix ();

              tx += influence.weight * v.x;
              ty += influence.weight * v.y;
              tz += influence.weight * v.z;
            }

        // re-normalize tangent if necessary
        if (m_Normalize)
            {
              float scale;
              scale = (float) (1.0f / sqrt (tx * tx + ty * ty + tz * tz));

              pTangentSpaceBuffer[0] = tx * scale;
              pTangentSpaceBuffer[1] = ty * scale;
              pTangentSpaceBuffer[2] = tz * scale;
            }
        else
            {
              pTangentSpaceBuffer[0] = tx;
              pTangentSpaceBuffer[1] = ty;
              pTangentSpaceBuffer[2] = tz;
            }

        pTangentSpaceBuffer[3] = tangentSpace.crossFactor;
        // next vertex position in buffer
        pTangentSpaceBuffer += 4;
      }

  return vertexCount;
}


 /*****************************************************************************/
/** Calculates the transformed normal data.
  *
  * This function calculates and returns the transformed normal data of a
  * specific submesh.
  *
  * @param pSubmesh A pointer to the submesh from which the normal data should
  *                 be calculated and returned.
  * @param pNormalBuffer A pointer to the user-provided buffer where the normal
  *                      data is written to.
  *
  * @return The number of normals written to the buffer.
  *****************************************************************************/

int CalPhysique::calculateNormals (CalSubmesh * pSubmesh,
                                   float *pNormalBuffer)
{
  // get bone vector of the skeleton
  std::vector < CalBone * >&vectorBone =
    m_pModel->getSkeleton ()->getVectorBone ();

  // get vertex vector of the submesh
  std::vector < CalCoreSubmesh::Vertex > &vectorVertex =
    pSubmesh->getCoreSubmesh ()->getVectorVertex ();

  // get the number of vertices
  int vertexCount;
  vertexCount = pSubmesh->getVertexCount ();

  // get the sub morph target vector from the core sub mesh
  std::vector < CalCoreSubMorphTarget * >&vectorSubMorphTarget =
    pSubmesh->getCoreSubmesh ()->getVectorCoreSubMorphTarget ();

  // calculate the base weight
  float baseWeight = pSubmesh->getBaseWeight ();

  // get the number of morph targets
  int morphTargetCount = pSubmesh->getMorphTargetWeightCount ();

  // calculate normal for all submesh vertices
  int vertexId;
  for (vertexId = 0; vertexId < vertexCount; ++vertexId)
      {
        // get the vertex
        CalCoreSubmesh::Vertex & vertex = vectorVertex[vertexId];

        // blend the morph targets
        CalVector normal (0, 0, 0);
        if (baseWeight == 1.0f)
            {
              normal.x = vertex.normal.x;
              normal.y = vertex.normal.y;
              normal.z = vertex.normal.z;
            }
        else
            {
              normal.x = baseWeight * vertex.normal.x;
              normal.y = baseWeight * vertex.normal.y;
              normal.z = baseWeight * vertex.normal.z;
              int morphTargetId;
              for (morphTargetId = 0; morphTargetId < morphTargetCount;
                   ++morphTargetId)
                  {
                    CalCoreSubMorphTarget::BlendVertex & blendVertex =
                      vectorSubMorphTarget[morphTargetId]->
                      getVectorBlendVertex ()[vertexId];
                    float currentWeight =
                      pSubmesh->getMorphTargetWeight (morphTargetId);
                    normal.x += currentWeight * blendVertex.normal.x;
                    normal.y += currentWeight * blendVertex.normal.y;
                    normal.z += currentWeight * blendVertex.normal.z;
                  }
            }

        // initialize normal
        float nx, ny, nz;
        nx = 0.0f;
        ny = 0.0f;
        nz = 0.0f;

        // blend together all vertex influences
        int influenceId;
        int influenceCount = (int) vertex.vectorInfluence.size ();
        for (influenceId = 0; influenceId < influenceCount; ++influenceId)
            {
              // get the influence
              CalCoreSubmesh::Influence & influence =
                vertex.vectorInfluence[influenceId];

              // get the bone of the influence vertex
              CalBone *pBone;
              pBone = vectorBone[influence.boneId];

              // transform normal with current state of the bone
              CalVector v (normal);
              v *= pBone->getTransformMatrix ();

              nx += influence.weight * v.x;
              ny += influence.weight * v.y;
              nz += influence.weight * v.z;
            }

        // re-normalize normal if necessary
        if (m_Normalize)
            {
              float scale;
              scale = (float) (1.0f / sqrt (nx * nx + ny * ny + nz * nz));

              pNormalBuffer[0] = nx * scale;
              pNormalBuffer[1] = ny * scale;
              pNormalBuffer[2] = nz * scale;
            }
        else
            {
              pNormalBuffer[0] = nx;
              pNormalBuffer[1] = ny;
              pNormalBuffer[2] = nz;
            }

        // next vertex position in buffer
        pNormalBuffer += 3;
      }

  return vertexCount;
}

 /*****************************************************************************/
/** Calculates the transformed vertex data.
  *
  * This function calculates and returns the transformed vertex and the transformed 
  * normal datadata of a specific submesh.
  *
  * @param pSubmesh A pointer to the submesh from which the vertex data should
  *                 be calculated and returned.
  * @param pVertexBuffer A pointer to the user-provided buffer where the vertex
  *                      data is written to.
  *
  * @return The number of vertices written to the buffer.
  *****************************************************************************/

int CalPhysique::calculateVerticesAndNormals (CalSubmesh * pSubmesh,
                                              float *pVertexBuffer)
{
  // get bone vector of the skeleton
  std::vector < CalBone * >&vectorBone =
    m_pModel->getSkeleton ()->getVectorBone ();

  // get vertex vector of the core submesh
  std::vector < CalCoreSubmesh::Vertex > &vectorVertex =
    pSubmesh->getCoreSubmesh ()->getVectorVertex ();

  // get physical property vector of the core submesh
  std::vector < CalCoreSubmesh::PhysicalProperty > &vectorPhysicalProperty =
    pSubmesh->getCoreSubmesh ()->getVectorPhysicalProperty ();

  // get the number of vertices
  int vertexCount;
  vertexCount = pSubmesh->getVertexCount ();

  // get the sub morph target vector from the core sub mesh
  std::vector < CalCoreSubMorphTarget * >&vectorSubMorphTarget =
    pSubmesh->getCoreSubmesh ()->getVectorCoreSubMorphTarget ();

  // calculate the base weight
  float baseWeight = pSubmesh->getBaseWeight ();

  // get the number of morph targets
  int morphTargetCount = pSubmesh->getMorphTargetWeightCount ();

  // calculate all submesh vertices
  int vertexId;
  for (vertexId = 0; vertexId < vertexCount; ++vertexId)
      {
        // get the vertex
        CalCoreSubmesh::Vertex & vertex = vectorVertex[vertexId];

        // blend the morph targets
        CalVector position (0, 0, 0);
        CalVector normal (0, 0, 0);
        if (baseWeight == 1.0f)
            {
              position.x = vertex.position.x;
              position.y = vertex.position.y;
              position.z = vertex.position.z;
              normal.x = vertex.normal.x;
              normal.y = vertex.normal.y;
              normal.z = vertex.normal.z;
            }
        else
            {
              position.x = baseWeight * vertex.position.x;
              position.y = baseWeight * vertex.position.y;
              position.z = baseWeight * vertex.position.z;
              normal.x = baseWeight * vertex.normal.x;
              normal.y = baseWeight * vertex.normal.y;
              normal.z = baseWeight * vertex.normal.z;
              int morphTargetId;
              for (morphTargetId = 0; morphTargetId < morphTargetCount;
                   ++morphTargetId)
                  {
                    CalCoreSubMorphTarget::BlendVertex & blendVertex =
                      vectorSubMorphTarget[morphTargetId]->
                      getVectorBlendVertex ()[vertexId];
                    float currentWeight =
                      pSubmesh->getMorphTargetWeight (morphTargetId);
                    position.x += currentWeight * blendVertex.position.x;
                    position.y += currentWeight * blendVertex.position.y;
                    position.z += currentWeight * blendVertex.position.z;
                    normal.x += currentWeight * blendVertex.normal.x;
                    normal.y += currentWeight * blendVertex.normal.y;
                    normal.z += currentWeight * blendVertex.normal.z;
                  }
            }

        // initialize vertex
        float x, y, z;
        x = 0.0f;
        y = 0.0f;
        z = 0.0f;

        // initialize normal
        float nx, ny, nz;
        nx = 0.0f;
        ny = 0.0f;
        nz = 0.0f;

        // blend together all vertex influences
        int influenceId;
        int influenceCount = (int) vertex.vectorInfluence.size ();
        for (influenceId = 0; influenceId < influenceCount; ++influenceId)
            {
              // get the influence
              CalCoreSubmesh::Influence & influence =
                vertex.vectorInfluence[influenceId];

              // get the bone of the influence vertex
              CalBone *pBone;
              pBone = vectorBone[influence.boneId];

              // transform vertex with current state of the bone
              CalVector v (position);
              v *= pBone->getTransformMatrix ();
              v += pBone->getTranslationBoneSpace ();

              x += influence.weight * v.x;
              y += influence.weight * v.y;
              z += influence.weight * v.z;

              // transform normal with current state of the bone
              CalVector n (normal);
              n *= pBone->getTransformMatrix ();

              nx += influence.weight * n.x;
              ny += influence.weight * n.y;
              nz += influence.weight * n.z;
            }

        // save vertex position
        if (pSubmesh->getCoreSubmesh ()->getSpringCount () > 0
            && pSubmesh->hasInternalData ())
            {
              // get the pgysical property of the vertex
              CalCoreSubmesh::PhysicalProperty & physicalProperty =
                vectorPhysicalProperty[vertexId];

              // assign new vertex position if there is no vertex weight
              if (physicalProperty.weight == 0.0f)
                  {
                    pVertexBuffer[0] = x;
                    pVertexBuffer[1] = y;
                    pVertexBuffer[2] = z;
                  }
            }
        else
            {
              pVertexBuffer[0] = x;
              pVertexBuffer[1] = y;
              pVertexBuffer[2] = z;
            }

        // re-normalize normal if necessary
        if (m_Normalize)
            {
              float scale;
              scale = (float) (1.0f / sqrt (nx * nx + ny * ny + nz * nz));

              pVertexBuffer[3] = nx * scale;
              pVertexBuffer[4] = ny * scale;
              pVertexBuffer[5] = nz * scale;
            }
        else
            {
              pVertexBuffer[3] = nx;
              pVertexBuffer[4] = ny;
              pVertexBuffer[5] = nz;
            }


        // next vertex position in buffer   
        pVertexBuffer += 6;

      }

  return vertexCount;
}

 /*****************************************************************************/
/** Calculates the transformed vertex data.
  *
  * This function calculates and returns the transformed vertex, the transformed 
  * normal datadata and the texture coords of a specific submesh.
  *
  * @param pSubmesh A pointer to the submesh from which the vertex data should
  *                 be calculated and returned.
  * 
  * @param pVertexBuffer A pointer to the user-provided buffer where the vertex
  *                      data is written to.
  *
  * @param NumTexCoords A integer with the number of texture coords
  *
  * @return The number of vertices written to the buffer.
  *****************************************************************************/


int CalPhysique::calculateVerticesNormalsAndTexCoords (CalSubmesh * pSubmesh,
                                                       float *pVertexBuffer,
                                                       int NumTexCoords)
{
  // get bone vector of the skeleton
  std::vector < CalBone * >&vectorBone =
    m_pModel->getSkeleton ()->getVectorBone ();

  // get vertex vector of the core submesh
  std::vector < CalCoreSubmesh::Vertex > &vectorVertex =
    pSubmesh->getCoreSubmesh ()->getVectorVertex ();

  // get the texture coordinate vector vector
  std::vector < std::vector < CalCoreSubmesh::TextureCoordinate >
    >&vectorvectorTextureCoordinate =
    pSubmesh->getCoreSubmesh ()->getVectorVectorTextureCoordinate ();

  int TextureCoordinateCount = (int) vectorvectorTextureCoordinate.size ();

  // check if the map id is valid
  if (((NumTexCoords < 0) || (NumTexCoords > TextureCoordinateCount)))
      {
        if (TextureCoordinateCount != 0)
            {
              CalError::setLastError (CalError::INVALID_HANDLE, __FILE__,
                                      __LINE__);
              return -1;
            }
      }

  // get physical property vector of the core submesh
  std::vector < CalCoreSubmesh::PhysicalProperty > &vectorPhysicalProperty =
    pSubmesh->getCoreSubmesh ()->getVectorPhysicalProperty ();

  // get the number of vertices
  int vertexCount;
  vertexCount = pSubmesh->getVertexCount ();

  // get the sub morph target vector from the core sub mesh
  std::vector < CalCoreSubMorphTarget * >&vectorSubMorphTarget =
    pSubmesh->getCoreSubmesh ()->getVectorCoreSubMorphTarget ();

  // calculate the base weight
  float baseWeight = pSubmesh->getBaseWeight ();

  // get the number of morph targets
  int morphTargetCount = pSubmesh->getMorphTargetWeightCount ();

  // calculate all submesh vertices
  int vertexId;
  for (vertexId = 0; vertexId < vertexCount; ++vertexId)
      {
        // get the vertex
        CalCoreSubmesh::Vertex & vertex = vectorVertex[vertexId];

        // blend the morph targets
        CalVector position (0, 0, 0);
        CalVector normal (0, 0, 0);
        if (baseWeight == 1.0f)
            {
              position.x = vertex.position.x;
              position.y = vertex.position.y;
              position.z = vertex.position.z;
              normal.x = vertex.normal.x;
              normal.y = vertex.normal.y;
              normal.z = vertex.normal.z;
            }
        else
            {
              position.x = baseWeight * vertex.position.x;
              position.y = baseWeight * vertex.position.y;
              position.z = baseWeight * vertex.position.z;
              normal.x = baseWeight * vertex.normal.x;
              normal.y = baseWeight * vertex.normal.y;
              normal.z = baseWeight * vertex.normal.z;
              int morphTargetId;
              for (morphTargetId = 0; morphTargetId < morphTargetCount;
                   ++morphTargetId)
                  {
                    CalCoreSubMorphTarget::BlendVertex & blendVertex =
                      vectorSubMorphTarget[morphTargetId]->
                      getVectorBlendVertex ()[vertexId];
                    float currentWeight =
                      pSubmesh->getMorphTargetWeight (morphTargetId);
                    position.x += currentWeight * blendVertex.position.x;
                    position.y += currentWeight * blendVertex.position.y;
                    position.z += currentWeight * blendVertex.position.z;
                    normal.x += currentWeight * blendVertex.normal.x;
                    normal.y += currentWeight * blendVertex.normal.y;
                    normal.z += currentWeight * blendVertex.normal.z;
                  }
            }

        // initialize vertex
        float x, y, z;
        x = 0.0f;
        y = 0.0f;
        z = 0.0f;

        // initialize normal
        float nx, ny, nz;
        nx = 0.0f;
        ny = 0.0f;
        nz = 0.0f;

        // blend together all vertex influences
        int influenceId;
        int influenceCount = (int) vertex.vectorInfluence.size ();
        for (influenceId = 0; influenceId < influenceCount; ++influenceId)
            {
              // get the influence
              CalCoreSubmesh::Influence & influence =
                vertex.vectorInfluence[influenceId];

              // get the bone of the influence vertex
              CalBone *pBone;
              pBone = vectorBone[influence.boneId];

              // transform vertex with current state of the bone
              CalVector v (position);
              v *= pBone->getTransformMatrix ();
              v += pBone->getTranslationBoneSpace ();

              x += influence.weight * v.x;
              y += influence.weight * v.y;
              z += influence.weight * v.z;

              // transform normal with current state of the bone
              CalVector n (normal);
              n *= pBone->getTransformMatrix ();

              nx += influence.weight * n.x;
              ny += influence.weight * n.y;
              nz += influence.weight * n.z;
            }

        // save vertex position
        if (pSubmesh->getCoreSubmesh ()->getSpringCount () > 0
            && pSubmesh->hasInternalData ())
            {
              // get the pgysical property of the vertex
              CalCoreSubmesh::PhysicalProperty & physicalProperty =
                vectorPhysicalProperty[vertexId];

              // assign new vertex position if there is no vertex weight
              if (physicalProperty.weight == 0.0f)
                  {
                    pVertexBuffer[0] = x;
                    pVertexBuffer[1] = y;
                    pVertexBuffer[2] = z;
                  }
            }
        else
            {
              pVertexBuffer[0] = x;
              pVertexBuffer[1] = y;
              pVertexBuffer[2] = z;
            }

        // re-normalize normal if necessary
        if (m_Normalize)
            {
              float scale;
              scale = (float) (1.0f / sqrt (nx * nx + ny * ny + nz * nz));

              pVertexBuffer[3] = nx * scale;
              pVertexBuffer[4] = ny * scale;
              pVertexBuffer[5] = nz * scale;
            }
        else
            {
              pVertexBuffer[3] = nx;
              pVertexBuffer[4] = ny;
              pVertexBuffer[5] = nz;
            }

        pVertexBuffer += 6;

        if (TextureCoordinateCount == 0)
            {
              pVertexBuffer += NumTexCoords * 2;
            }
        else
            {
              for (int mapId = 0; mapId < NumTexCoords; ++mapId)
                  {
                    pVertexBuffer[0] =
                      vectorvectorTextureCoordinate[mapId][vertexId].u;
                    pVertexBuffer[1] =
                      vectorvectorTextureCoordinate[mapId][vertexId].v;
                    pVertexBuffer += 2;
                  }
            }
      }

  return vertexCount;
}



 /*****************************************************************************/
/** Creates the physique instance.
  *
  * This function creates the physique instance.
  *
  * @param pModel A pointer to the model that should be managed with this
  *               physique instance.
  *
  * @return One of the following values:
  *         \li \b true if successful
  *         \li \b false if an error happend
  *****************************************************************************/

bool CalPhysique::create (CalModel * pModel)
{
  if (pModel == 0)
      {
        CalError::setLastError (CalError::INVALID_HANDLE, __FILE__, __LINE__);
        return false;
      }

  m_pModel = pModel;

  CalCoreModel *pCoreModel;
  pCoreModel = m_pModel->getCoreModel ();
  if (pCoreModel == 0)
      {
        CalError::setLastError (CalError::INVALID_HANDLE, __FILE__, __LINE__);
        return false;
      }

  return true;
}

 /*****************************************************************************/
/** Destroys the physique instance.
  *
  * This function destroys all data stored in the physique instance and frees
  * all allocated memory.
  *****************************************************************************/

void CalPhysique::destroy ()
{
  m_pModel = 0;
}

 /*****************************************************************************/
/** Updates all the internally handled attached meshes.
  *
  * This function updates all the attached meshes of the model that are handled
  * internally.
  *****************************************************************************/

void CalPhysique::update ()
{
  // get the attached meshes vector
  std::vector < CalMesh * >&vectorMesh = m_pModel->getVectorMesh ();

  // loop through all the attached meshes
  std::vector < CalMesh * >::iterator iteratorMesh;
  for (iteratorMesh = vectorMesh.begin (); iteratorMesh != vectorMesh.end ();
       ++iteratorMesh)
      {
        // get the submesh vector of the mesh
        std::vector < CalSubmesh * >&vectorSubmesh =
          (*iteratorMesh)->getVectorSubmesh ();

        // loop through all the submeshes of the mesh
        std::vector < CalSubmesh * >::iterator iteratorSubmesh;
        for (iteratorSubmesh = vectorSubmesh.begin ();
             iteratorSubmesh != vectorSubmesh.end (); ++iteratorSubmesh)
            {
              // check if the submesh handles vertex data internally
              if ((*iteratorSubmesh)->hasInternalData ())
                  {
                    // calculate the transformed vertices and store them in the submesh
                    std::vector < CalVector > &vectorVertex =
                      (*iteratorSubmesh)->getVectorVertex ();
                    calculateVertices (*iteratorSubmesh,
                                       (float *) &vectorVertex[0]);

                    // calculate the transformed normals and store them in the submesh
                    std::vector < CalVector > &vectorNormal =
                      (*iteratorSubmesh)->getVectorNormal ();
                    calculateNormals (*iteratorSubmesh,
                                      (float *) &vectorNormal[0]);

                    unsigned mapId;
                    for (mapId = 0;
                         mapId <
                         (*iteratorSubmesh)->getVectorVectorTangentSpace ().
                         size (); mapId++)
                        {
                          if ((*iteratorSubmesh)->isTangentsEnabled (mapId))
                              {
                                std::vector < CalSubmesh::TangentSpace >
                                  &vectorTangentSpace =
                                  (*iteratorSubmesh)->
                                  getVectorVectorTangentSpace ()[mapId];
                                calculateTangentSpaces (*iteratorSubmesh,
                                                        mapId,
                                                        (float *)
                                                        &vectorTangentSpace
                                                        [0]);
                              }
                        }

                  }
            }
      }
}

 /*****************************************************************************/
/** Sets the normalization flag to true or false.
  *
  * This function sets the normalization flag on or off. If off, the normals
  * calculated by Cal3D will not be normalized. Instead, this transform is left
  * up to the user.
  *****************************************************************************/

void CalPhysique::setNormalization (bool normalize)
{
  m_Normalize = normalize;
}

//****************************************************************************//
