void VDebugPathRenderer::OnRender(IVisCallbackDataObject_cl* pCallbackData)
{
  IVPathRenderingData* pData = m_spPathRenderingData;
  if (pData == NULL)
    return;

  float fLinkExtent = (pData->GetLinkLength() - pData->GetLinkGap()) / 2.0f;
  for (unsigned int i = 0; i < pData->GetNumLinks(); ++i)
  {
    hkvVec3 vTranslation;
    hkvMat3 mRotation;
    if (!pData->GetLinkTransform(i, mRotation, vTranslation))
      continue;

    hkvVec3 v1 = hkvVec3(-fLinkExtent, 0.0f, 0.0f);
    hkvVec3 v2 = -v1;

    v1 = (mRotation * v1) + vTranslation;
    v2 = (mRotation * v2) + vTranslation;

    Vision::Game.DrawSingleLine(v1, v2, RenderColor);
  }
}
bool VCablePathRenderer::RebuildModel()
{
  m_spChainMesh = NULL;

  IVPathRenderingData* pData = m_spPathRenderingData;
  if (pData == NULL || !pData->IsValid())
    return false;
  
  int iNumLinks = pData->GetNumLinks();
  m_iLastKnownNumLinks = iNumLinks;

  if (iNumLinks < 1)
  {
    hkvLog::Warning("VCablePathRenderer::RebuildModel: Can't create cable rendering - constraint chain has no links.");
    return false;
  }
  else if (iNumLinks > MAX_NUM_LINKS)
  {
    hkvLog::Warning("VCablePathRenderer::RebuildModel: Path chain contains too many links; clamping to %d.", MAX_NUM_LINKS);
    iNumLinks = MAX_NUM_LINKS;
  }

  int iVerticesPerRing = hkvMath::Max(6, VerticesPerRing);
  int iRingsPerLink = hkvMath::Max(1, RingsPerLink);

//#define ENDS_ONLY

  // Vertices for each ring (final vertex is at the position of the first
  // vertex), times the number of rings
#ifdef ENDS_ONLY
  int iNumVertices = 0;
#else
  int iNumVertices = ((iNumLinks * iRingsPerLink) + 1) * (iVerticesPerRing + 1);
#endif
  // Extra vertices for the end surfaces
  iNumVertices += (iVerticesPerRing + 1) * 2;
  if (iNumVertices > 65535)
  {
    hkvLog::Warning("VCablePathRenderer::RebuildModel: Can't create cable rendering - too many verticess.");
    return false;
  }

#ifdef ENDS_ONLY
  int iNumTriangles = 0;
#else
  int iNumTriangles = iRingsPerLink * iNumLinks * iVerticesPerRing * 2;
#endif
  // Extra triangles for the end surfaces
  iNumTriangles += iVerticesPerRing * 2;

  float fRadius = pData->GetDiameter() / 2.0f;
  float fCircumference = fRadius * 2.0f * hkvMath::pi();
  
  float fRingHeight = pData->GetLinkLength() / iRingsPerLink;
  float fTextureVPerRing = fRingHeight / fCircumference;

  // Load the model that provides the surface material of the cable
  VDynamicMeshPtr spTemplateMesh;
  if (!ModelFile.IsEmpty())
    spTemplateMesh = VDynamicMesh::LoadDynamicMesh(ModelFile);

  // Pre-calculate the coordinates, normals and texture offsets of the ring
  // and end cap vertices
  VScopedArray<hkvVec3> rgvVertexTemplates(new hkvVec3[iVerticesPerRing + 1]);
  VScopedArray<hkvVec3> rgvNormalTemplates(new hkvVec3[iVerticesPerRing + 1]);
  VScopedArray<float> rgfTextureU(new float[iVerticesPerRing + 1]);
  VScopedArray<hkvVec2> rgvEndTexCoords(new hkvVec2[iVerticesPerRing]);
  {
    float fMaxEndTexOffset = 1.0f / (2.0f * hkvMath::pi());
    for (int i = 0; i < iVerticesPerRing; ++i)
    {
      float fRatio = (float)i / (float)iVerticesPerRing;
      float fAngle = fRatio * 2.0f * hkvMath::pi();
      rgvNormalTemplates[i].set(0.0f, hkvMath::cosRad(fAngle), hkvMath::sinRad(fAngle));
      rgvVertexTemplates[i] = rgvNormalTemplates[i] * fRadius;
      rgfTextureU[i] = fRatio;
      rgvEndTexCoords[i].set(
        0.5f + (hkvMath::cosRad(fAngle) * fMaxEndTexOffset),
        0.5f + (hkvMath::sinRad(fAngle) * fMaxEndTexOffset));
    }
    rgvNormalTemplates[iVerticesPerRing] = rgvNormalTemplates[0];
    rgvVertexTemplates[iVerticesPerRing] = rgvVertexTemplates[0];
    rgfTextureU[iVerticesPerRing] = 1.0f;
  }

  VColorRef cableColor(V_RGBA_YELLOW);
  hkvVec3 vTangent(1.f, 0.f, 0.f);

  int usageFlags = VIS_MEMUSAGE_STREAM;
  int bindFlags = VIS_BIND_SHADER_RESOURCE;
  
#ifdef _VR_DX11
  // Only specify this flag if this is a true DX11 card, since the engine currently
  // just passes it along to the device. This fails if the feature is not supported.
  // It is needed for Compute Shader Skinning
  if (Vision::Video.GetDXFeatureLevel() >= D3D_FEATURE_LEVEL_11_0)
  {
    usageFlags |= VIS_MEMUSAGE_UAV_BYTEADDRESS;
    bindFlags |= VIS_BIND_UNORDERED_ACCESS;
  }
#endif

  VDynamicMeshBuilder meshBuilder(iNumVertices, iNumTriangles, iNumLinks + 2, 1, usageFlags, bindFlags);

  if (spTemplateMesh && (spTemplateMesh->GetSurfaceCount() > 0))
    meshBuilder.CopySurfaceFrom(0, *spTemplateMesh->GetSurface(0));

#ifndef ENDS_ONLY
  for (int iCurrentLink = 0; iCurrentLink < iNumLinks; ++iCurrentLink)
  {
    /// The vertices of a chain link are influenced by three bones.
    int iPrevBone = iCurrentLink;
    int iThisBone = iCurrentLink + 1;
    int iNextBone = iCurrentLink + 2;

    /// The last link has an additional ring of vertices at its end
    int iLocalNumRings = iCurrentLink == (iNumLinks - 1)
      ? iRingsPerLink + 1 : iRingsPerLink;

    /// Compute the vertices for each ring of the current chain link:
    for (int iCurrentRing = 0; iCurrentRing < iLocalNumRings; ++iCurrentRing)
    {
      /// Normalized offset of this ring from the start of the chain link (range 0..1)
      float fNormOffset = (float)iCurrentRing / (float)iRingsPerLink;

      /// Calculate the influence factors of the three bones that might affect
      /// the current ring
      float fPrevBoneWeight = hkvMath::Max(0.0f, 0.5f - fNormOffset);
      float fThisBoneWeight = 1.0f - hkvMath::Abs(fNormOffset - 0.5f);
      float fNextBoneWeight = hkvMath::Max(0.0f, fNormOffset - 0.5f);

      /// Compute the vertices of one ring. The start/end vertices are at the same
      /// position, since two different texture coordinates are needed here.
      int iStartVertex = meshBuilder.GetNextVertexIndex();
      for (int iLocalVertex = 0; iLocalVertex <= iVerticesPerRing; ++iLocalVertex)
      {
        hkvVec3 vPos(rgvVertexTemplates[iLocalVertex]);

        // Texture v coordinate increases throughout the chain
        float fTextureV = ((iCurrentLink * iRingsPerLink) + iCurrentRing) * fTextureVPerRing;
        hkvVec2 vTexCoords(rgfTextureU[iLocalVertex], fTextureV);

        // Add vertex data...
        int iCurrentVertex = meshBuilder.AddVertex(vPos, rgvNormalTemplates[iLocalVertex], vTangent, vTexCoords, cableColor);
        VASSERT(iCurrentVertex >= 0);

        // ...and bone weights.
        if (fPrevBoneWeight > 0.f)
          meshBuilder.AddBoneWeight(iPrevBone, fPrevBoneWeight);
        meshBuilder.AddBoneWeight(iThisBone, fThisBoneWeight);
        if (fNextBoneWeight > 0.f)
          meshBuilder.AddBoneWeight(iNextBone, fNextBoneWeight);

        // Unless we are at the last vertex in a ring or at the last ring of the
        // last link, compute the vertex indices that make up the triangles for 
        // the cable.
        if ((iCurrentRing < iRingsPerLink) && (iLocalVertex < iVerticesPerRing))
        {
          unsigned short i1, i2, i3;
          int iNextRing = iStartVertex + iVerticesPerRing + 1;

          i1 = iStartVertex + iLocalVertex;
          i2 = iStartVertex + iLocalVertex + 1;
          i3 = iNextRing + iLocalVertex;
          meshBuilder.AddTriangle(i1, i2, i3);

          i1 = iStartVertex + iLocalVertex + 1;
          i2 = iNextRing + iLocalVertex + 1;
          i3 = iNextRing + iLocalVertex;
          meshBuilder.AddTriangle(i1, i2, i3);
        }

        //iCurrentVertex++;
      } // End of vertex loop
    } // End of rings per chain link loop
  } // End of chain link loop
#endif

  // Add the end surfaces
  {
    hkvVec3 vEndNormal = hkvVec3(-1.0f, 0.0f, 0.0f);
    hkvVec3 vEndTangent = hkvVec3(0.0f, 1.0f, 0.0f);

    // Center vertex (Top)
    int iCenterVertexIndex = meshBuilder.GetNextVertexIndex();
    meshBuilder.AddVertex(hkvVec3::ZeroVector(), vEndNormal, vEndTangent, hkvVec2(0.5f, 0.5f), cableColor);
    meshBuilder.AddBoneWeight(0, 0.5f);
    meshBuilder.AddBoneWeight(1, 0.5f);

    // Outer vertices (Top)
    int iStartVertexIndex = meshBuilder.GetNextVertexIndex();
    for (int iLocalVertex = 0; iLocalVertex < iVerticesPerRing; ++iLocalVertex)
    {
      meshBuilder.AddVertex(rgvVertexTemplates[iLocalVertex], vEndNormal, vEndTangent, rgvEndTexCoords[iLocalVertex], cableColor);
      meshBuilder.AddBoneWeight(0, 0.5f);
      meshBuilder.AddBoneWeight(1, 0.5f);
      int iNextLocalVertex = iLocalVertex + 1;
      if (iNextLocalVertex == iVerticesPerRing)
        iNextLocalVertex = 0;

      meshBuilder.AddTriangle(iCenterVertexIndex, iStartVertexIndex + iLocalVertex, iStartVertexIndex + iNextLocalVertex);
    }

    // Bottom cap: normal points the other way (tangent remains)
    vEndNormal.set(1.0f, 0.0f, 0.0f);

    // Center vertex (Bottom)
    iCenterVertexIndex = meshBuilder.GetNextVertexIndex();
    meshBuilder.AddVertex(hkvVec3::ZeroVector(), vEndNormal, vEndTangent, hkvVec2(0.5f, 0.5f), cableColor);
    meshBuilder.AddBoneWeight(iNumLinks, 0.5f);
    meshBuilder.AddBoneWeight(iNumLinks + 1, 0.5f);

    // Outer vertices (Top)
    iStartVertexIndex = meshBuilder.GetNextVertexIndex();
    for (int iLocalVertex = 0; iLocalVertex < iVerticesPerRing; ++iLocalVertex)
    {
      meshBuilder.AddVertex(rgvVertexTemplates[iLocalVertex], vEndNormal, vEndTangent, rgvEndTexCoords[iLocalVertex], cableColor);
      meshBuilder.AddBoneWeight(iNumLinks, 0.5f);
      meshBuilder.AddBoneWeight(iNumLinks + 1, 0.5f);
      int iNextLocalVertex = iLocalVertex + 1;
      if (iNextLocalVertex == iVerticesPerRing)
        iNextLocalVertex = 0;

      meshBuilder.AddTriangle(iCenterVertexIndex, iStartVertexIndex + iLocalVertex, iStartVertexIndex + iNextLocalVertex);
    }
  }

  VASSERT(meshBuilder.GetNextVertexIndex() == iNumVertices);
  VASSERT(meshBuilder.GetNextIndexIndex() == (iNumTriangles * 3));

  m_spChainMesh = meshBuilder.Finalize();
  VASSERT(m_spChainMesh);
  m_spChainMesh->SetResourceFlag(VRESOURCEFLAG_AUTODELETE);

  // Create the entity and the animation state
  if (!m_spChainEntity)
  {
    m_spChainEntity = static_cast<VCableChainEntity*>(Vision::Game.CreateEntity("VCableChainEntity", hkvVec3::ZeroVector()));
  }
  m_spChainEntity->SetRenderingData(pData);
  m_spChainEntity->SetMesh(m_spChainMesh);
  m_spChainEntity->SetCastShadows(CastDynamicShadows);

  VisAnimFinalSkeletalResult_cl* pFinalSkeletalResult;
  VisAnimConfig_cl* pAnimConfig = VisAnimConfig_cl::CreateSkeletalConfig(m_spChainMesh, &pFinalSkeletalResult);
  m_spChainEntity->SetAnimConfig(pAnimConfig);

  return true;
}