void myD3D11DeviceContext::VSSetConstantBuffers(UINT  StartSlot, UINT  NumBuffers, ID3D11Buffer * const *  ppConstantBuffers)
{
    if (g_logger->logInterfaceCalls)
    {
        string pointer = ppConstantBuffers == nullptr ? "<invalid>" : pointerToString(ppConstantBuffers[0]);
        g_logger->log("VSSetConstantBuffers StartSlot=" + to_string(StartSlot) + ", NumBuffers=" + to_string(NumBuffers) + ", buffers[0]=" + pointer);
    }

    base->VSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers);
}
Esempio n. 2
0
bool CMathObject::createExtensiveValueExpression(const CMetab * pSpecies,
    CMathContainer & container)
{
  bool success = true;

  // mConc * mpCompartment->getValue() * mpModel->getQuantity2NumberFactor();

  CObjectInterface * pDensity = NULL;
  CObjectInterface * pCompartment = NULL;

  if (mIsInitialValue)
    {
      pDensity = pSpecies->getInitialConcentrationReference();
      pCompartment = pSpecies->getCompartment()->getInitialValueReference();
    }
  else
    {
      pDensity = pSpecies->getConcentrationReference();
      pCompartment = pSpecies->getCompartment()->getValueReference();
    }

  std::ostringstream Infix;
  Infix.imbue(std::locale::classic());
  Infix.precision(16);

  Infix << container.getModel().getQuantity2NumberFactor();
  Infix << "*";
  Infix << pointerToString(container.getMathObject(pDensity)->getValuePointer());
  Infix << "*";
  Infix << pointerToString(container.getMathObject(pCompartment)->getValuePointer());

  CExpression E("ExtensiveValueExpression", &container);

  success &= E.setInfix(Infix.str());

  pdelete(mpExpression);
  mpExpression = new CMathExpression(E, container, !mIsInitialValue);
  compileExpression();

  return success;
}
void myD3D11DeviceContext::IASetInputLayout(ID3D11InputLayout *  pInputLayout)
{
    if (g_logger->logInterfaceCalls) g_logger->log("IASetInputLayout handle=" + pointerToString(pInputLayout));

    
    if (assets->vertexLayouts.count((UINT64)pInputLayout) == 0)
        assets->activeVertexLayout = nullptr;
    else
        assets->activeVertexLayout = assets->vertexLayouts.find((UINT64)pInputLayout)->second;

    base->IASetInputLayout(pInputLayout);
}
HRESULT myD3D11DeviceContext::Map(ID3D11Resource *  pResource, UINT  Subresource, D3D11_MAP  MapType, UINT  MapFlags, D3D11_MAPPED_SUBRESOURCE *  pMappedResource)
{
    if (g_logger->logInterfaceCalls) g_logger->log("Map resource=" + pointerToString(pResource) + " subResource=" + to_string(Subresource) + " type=" + to_string(MapType));

    //
    // special sentinel value used to report GameCube controller state
    //
    if (MapType == (D3D11_MAP)1235)
    {
        if (g_logger->curFrame == nullptr)
        {
            g_logger->logErrorFile << "g_logger->curFrame == nullptr" << endl;
            return S_OK;
        }
        GCPadStatus *padState = (GCPadStatus*)pResource;
        UINT padIndex = Subresource;
        if (padIndex < g_logger->curFrame->ControllerCount)
            g_logger->curFrame->padState[padIndex] = *padState;
        if (g_logger->logInterfaceCalls)
        {
            g_logger->log("Controller update button=" + to_string(padState->button) + " frameID=" + to_string(MapFlags) + " index=" + to_string(Subresource));
        }
        return S_OK;
    }

    UINT64 ptr = (UINT64)pResource;
    if (capturingAllBuffers &&
        (MapType == D3D11_MAP_WRITE_NO_OVERWRITE || MapType == D3D11_MAP_WRITE_DISCARD || MapType == D3D11_MAP_WRITE ||
         MapType == (D3D11_MAP)1234) &&
        assets->buffers.count(ptr) > 0)
    {
        auto &buffer = *assets->buffers.find(ptr)->second;

        bool shim = (buffer.data.size() < 1024 * 1024 || MapType == (D3D11_MAP)1234);
        
        if (shim)
        {
            *pMappedResource = buffer.mappedResource;
            if (MapType != (D3D11_MAP)1234)
            {
                buffer.mapped = true;
                buffer.mapType = MapType;
                buffer.mapFlags = MapFlags;
            }
            return S_OK;
        }
    }

    HRESULT result = base->Map(pResource, Subresource, MapType, MapFlags, pMappedResource);

    return result;
}
Esempio n. 5
0
bool CMathObject::compileTotalMass(CMathContainer & container)
{
  bool success = true;

  // The default value is NaN
  *mpValue = InvalidValue;

  // Reset the prerequisites
  mPrerequisites.clear();

  const CMoiety * pMoiety = static_cast< const CMoiety *>(mpDataObject->getObjectParent());

  std::ostringstream Infix;
  Infix.imbue(std::locale::classic());
  Infix.precision(16);

  std::vector< std::pair< C_FLOAT64, CMetab * > >::const_iterator it = pMoiety->getEquation().begin();
  std::vector< std::pair< C_FLOAT64, CMetab * > >::const_iterator end = pMoiety->getEquation().end();
  bool First = true;

  for (; it != end; ++it)
    {
      const C_FLOAT64 & Multiplicity = it->first;

      if (First || Multiplicity < 0.0)
        {
          Infix << Multiplicity;
        }
      else
        {
          Infix << "+" << Multiplicity;
        }

      First = false;

      Infix << "*";
      Infix << pointerToString(container.getMathObject(it->second->getValueReference())->getValuePointer());
    }

  CExpression E("TotalMass", &container);

  success &= E.setInfix(Infix.str());

  pdelete(mpExpression);
  mpExpression = new CMathExpression(E, container, !mIsInitialValue);
  compileExpression();

  return success;
}
Esempio n. 6
0
bool CMathObject::createIntensiveRateExpression(const CMetab * pSpecies,
    CMathContainer & container)
{
  bool success = true;

  /*
    mConcRate =
      (mRate * mpModel->getNumber2QuantityFactor() - mConc * mpCompartment->getRate())
      / mpCompartment->getValue();
   */

  std::ostringstream Infix;
  Infix.imbue(std::locale::classic());
  Infix.precision(16);

  Infix << "(";
  Infix << pointerToString(container.getMathObject(pSpecies->getRateReference())->getValuePointer());
  Infix << "*";
  Infix << container.getModel().getNumber2QuantityFactor();
  Infix << "-";
  Infix << pointerToString(container.getMathObject(pSpecies->getCompartment()->getValueReference())->getValuePointer());
  Infix << "*";
  Infix << pointerToString(container.getMathObject(pSpecies->getCompartment()->getRateReference())->getValuePointer());
  Infix << ")/";
  Infix << pointerToString(container.getMathObject(pSpecies->getCompartment()->getValueReference())->getValuePointer());

  CExpression E("IntensiveRateExpression", &container);

  success &= E.setInfix(Infix.str());

  pdelete(mpExpression);
  mpExpression = new CMathExpression(E, container, !mIsInitialValue);
  compileExpression();

  return success;
}
void myD3D11DeviceContext::Unmap(ID3D11Resource *  pResource, UINT  Subresource)
{
    if (g_logger->logInterfaceCalls) g_logger->log("Unmap resource=" + pointerToString(pResource) + " subResource=" + to_string(Subresource));

    UINT64 ptr = (UINT64)pResource;
    if (assets->buffers.count(ptr) > 0)
    {
        auto &buffer = *assets->buffers.find(ptr)->second;
        if (buffer.mapped)
        {
            D3D11_MAPPED_SUBRESOURCE mappedResource;
            base->Map(pResource, Subresource, buffer.mapType, buffer.mapFlags, &mappedResource);
            memcpy(mappedResource.pData, buffer.mappedResource.pData, buffer.mappedResource.RowPitch);
            buffer.mapped = false;
        }
    }

    base->Unmap(pResource, Subresource);
}
Esempio n. 8
0
bool CMathObject::compileFlux(CMathContainer & container)
{
  bool success = true;

  // The default value is NaN
  *mpValue = InvalidValue;

  // Reset the prerequisites
  mPrerequisites.clear();

  const CReaction * pReaction = static_cast< const CReaction * >(mpDataObject->getObjectParent());

  // We need to check whether this reaction is a single compartment reaction and scale it if true.
  //   mFlux = *mScalingFactor * mpFunction->calcValue(mMap.getPointers());
  //   mScalingFactor = compartment volume or 1

  pdelete(mpExpression);
  mpExpression = new CMathExpression(*pReaction->getFunction(),
                                     pReaction->getCallParameters(),
                                     container,
                                     !mIsInitialValue);

  if (pReaction->getScalingCompartment() != NULL &&
      pReaction->getEffectiveKineticLawUnitType() == CReaction::ConcentrationPerTime)
    {
      CExpression Tmp(mpExpression->getObjectName(), &container);

      std::string Infix = pointerToString(container.getMathObject(pReaction->getScalingCompartment()->getValueReference())->getValuePointer()) + "*(" + mpExpression->getInfix() + ")";
      success &= Tmp.setInfix(Infix);
      success &= Tmp.compile();

      pdelete(mpExpression);
      mpExpression = new CMathExpression(Tmp, container, false);
    }

  compileExpression();

  return success;
}
Esempio n. 9
0
const BufferCPU* MyD3DAssets::loadBufferFromGPU(ID3D11Buffer *buffer)
{
    if (buffer == nullptr)
        return nullptr;

    if (buffers.count((UINT64)buffer) == 0)
    {
        if (g_logger->logInterfaceCalls) g_logger->log("*** Creating vertex buffer: " + pointerToString(buffer));
        buffers[(UINT64)buffer] = new BufferCPU(0, buffer);
    }
    
    BufferCPU *cpu = buffers[(UINT64)buffer];
    readBuffer(buffer, cpu->data);
    
    return cpu;
}
Esempio n. 10
0
bool CMathObject::compilePropensity(CMathContainer & container)
{
  bool success = true;

  // The default value is NaN
  *mpValue = InvalidValue;

  // Reset the prerequisites
  mPrerequisites.clear();

  const CReaction * pReaction = static_cast< const CReaction * >(mpDataObject->getObjectParent());

  std::ostringstream Infix;
  Infix.imbue(std::locale::classic());
  Infix.precision(16);

  // Propensity for reversible reactions must be NaN
  if (pReaction->isReversible())
    {
      Infix << "NAN";
    }
  else
    {
      // Propensity is the same as the flux, but it must now be negative.
      Infix << "max(0," << pointerToString(container.getMathObject(pReaction->getParticleFluxReference())->getValuePointer());

      // Apply correction for deterministic models
      if (container.getModel().getModelType() == CModel::deterministic)
        {
          std::ostringstream Divisor;
          Divisor.imbue(std::locale::classic());
          Divisor.precision(16);

          const CCopasiVector<CChemEqElement> & Substrates = pReaction->getChemEq().getSubstrates();
          CCopasiVector< CChemEqElement >::const_iterator itSubstrate = Substrates.begin();
          CCopasiVector< CChemEqElement >::const_iterator endSubstrate = Substrates.end();
          bool first = true;

          for (; itSubstrate != endSubstrate; ++itSubstrate)
            {
              const std::string NumberPointer = pointerToString(container.getMathObject(itSubstrate->getMetabolite()->getValueReference())->getValuePointer());

              C_FLOAT64 Multiplicity = itSubstrate->getMultiplicity();

              Multiplicity -= 1.0; // Nothing to correct if the multiplicity is 1.

              if (Multiplicity > 2.0 - 100.0 * std::numeric_limits< C_FLOAT64 >::epsilon())
                {
                  if (!first)
                    {
                      Divisor << "*";
                    }

                  first = false;
                  Divisor << NumberPointer << "^" << Multiplicity;
                }
              else if (Multiplicity > 1.0 - 100.0 * std::numeric_limits< C_FLOAT64 >::epsilon())
                {
                  if (!first)
                    {
                      Divisor << "*";
                    }

                  first = false;
                  Divisor << NumberPointer;
                }

              while (Multiplicity > 1.0 - 100.0 * std::numeric_limits< C_FLOAT64 >::epsilon())
                {
                  Infix << "*(" << NumberPointer << "-" << Multiplicity << ")";
                  Multiplicity -= 1.0;
                }
            }

          if (Divisor.str() != "")
            {
              Infix << "/(" << Divisor.str() << ")";
            }
        }

      Infix << ")";
    }

  CExpression E("PropensityExpression", &container);

  success &= E.setInfix(Infix.str());

  pdelete(mpExpression);
  mpExpression = new CMathExpression(E, container, !mIsInitialValue);
  compileExpression();

  return success;
}
Esempio n. 11
0
bool CMathObject::createExtensiveReactionRateExpression(const CMetab * pSpecies,
    CMathContainer & container)
{
  bool success = true;

  std::ostringstream Infix;
  Infix.imbue(std::locale::classic());
  Infix.precision(16);

  std::string Key = pSpecies->getKey();
  bool First = true;

  CCopasiVectorN< CReaction >::const_iterator it = container.getModel().getReactions().begin();
  CCopasiVectorN< CReaction >::const_iterator end = container.getModel().getReactions().end();

  for (; it != end; ++it)
    {
      const CCopasiVector< CChemEqElement > &Balances =
        it->getChemEq().getBalances();
      CCopasiVector< CChemEqElement >::const_iterator itChem = Balances.begin();
      CCopasiVector< CChemEqElement >::const_iterator endChem = Balances.end();

      for (; itChem != endChem; ++itChem)
        if (itChem->getMetaboliteKey() == Key)
          break;

      if (itChem != endChem)
        {
          const C_FLOAT64 & Multiplicity = itChem->getMultiplicity();

          if (First || Multiplicity < 0.0)
            {
              if (Multiplicity == std::numeric_limits< C_FLOAT64 >::infinity())
                {
                  Infix << "infinity";
                }
              else if (Multiplicity == -std::numeric_limits< C_FLOAT64 >::infinity())
                {
                  Infix << "-infinity";
                }
              else
                {
                  Infix << Multiplicity;
                }
            }
          else
            {
              if (Multiplicity == std::numeric_limits< C_FLOAT64 >::infinity())
                {
                  Infix << "+infinity";
                }
              else
                {
                  Infix << "+" << Multiplicity;
                }
            }

          First = false;

          Infix << "*";
          Infix << pointerToString(container.getMathObject(it->getParticleFluxReference())->getValuePointer());
        }
    }

  CExpression E("ExtensiveReactionExpression", &container);

  success &= E.setInfix(Infix.str());

  pdelete(mpExpression);
  mpExpression = new CMathExpression(E, container, !mIsInitialValue);
  compileExpression();

  return success;
}
Esempio n. 12
0
bool CMathObject::compileTransitionTime(CMathContainer & container)
{
  bool success = true;

  // The default value is NaN
  *mpValue = InvalidValue;

  // Reset the prerequisites
  mPrerequisites.clear();

  const CMetab * pSpecies = static_cast< const CMetab *>(mpDataObject->getObjectParent());

  std::ostringstream Infix;
  Infix.imbue(std::locale::classic());
  Infix.precision(16);

  switch (pSpecies->getStatus())
    {
      case CModelEntity::ODE:
        // mTT = *mpValue / fabs(mRate);
        Infix << "abs(";
        Infix << pointerToString(container.getMathObject(pSpecies->getValueReference())->getValuePointer());
        Infix << "/";
        Infix << pointerToString(container.getMathObject(pSpecies->getRateReference())->getValuePointer());
        Infix << ")";
        break;

      case CModelEntity::REACTIONS:
      {
        std::ostringstream PositiveFlux;
        PositiveFlux.imbue(std::locale::classic());
        PositiveFlux.precision(16);

        std::ostringstream NegativeFlux;
        NegativeFlux.imbue(std::locale::classic());
        NegativeFlux.precision(16);

        std::string Key = pSpecies->getKey();
        bool First = true;

        CCopasiVectorN< CReaction >::const_iterator it = container.getModel().getReactions().begin();
        CCopasiVectorN< CReaction >::const_iterator end = container.getModel().getReactions().end();

        for (; it != end; ++it)
          {
            const CCopasiVector< CChemEqElement > &Balances =
              it->getChemEq().getBalances();
            CCopasiVector< CChemEqElement >::const_iterator itChem = Balances.begin();
            CCopasiVector< CChemEqElement >::const_iterator endChem = Balances.end();

            for (; itChem != endChem; ++itChem)
              if (itChem->getMetaboliteKey() == Key)
                break;

            if (itChem != endChem)
              {
                const C_FLOAT64 & Multiplicity = itChem->getMultiplicity();

                if (!First)
                  {
                    PositiveFlux << "+";
                    NegativeFlux << "+";
                  }

                PositiveFlux << "max(";
                NegativeFlux << "min(";

                if (Multiplicity == std::numeric_limits< C_FLOAT64 >::infinity())
                  {
                    PositiveFlux << "infinity";
                    NegativeFlux << "infinity";
                  }
                else if (Multiplicity == -std::numeric_limits< C_FLOAT64 >::infinity())
                  {
                    PositiveFlux << "-infinity";
                    NegativeFlux << "-infinity";
                  }
                else
                  {
                    PositiveFlux << Multiplicity;
                    NegativeFlux << Multiplicity;
                  }

                PositiveFlux << "*";
                NegativeFlux << "*";
                PositiveFlux << pointerToString(container.getMathObject(it->getParticleFluxReference())->getValuePointer());
                NegativeFlux << pointerToString(container.getMathObject(it->getParticleFluxReference())->getValuePointer());

                PositiveFlux << ",0)";
                NegativeFlux << ",0)";

                First = false;
              }
          }

        if (!First)
          {
            Infix << "abs(";
            Infix << pointerToString(container.getMathObject(pSpecies->getValueReference())->getValuePointer());
            Infix << ")/if(";
            Infix << pointerToString(container.getMathObject(pSpecies->getRateReference())->getValuePointer());
            Infix << "<0,-(" << NegativeFlux.str() << ")," << PositiveFlux.str() << ")";
          }
      }
      break;

      default:
        break;
    }

  CExpression E("TransitionTimeExpression", &container);

  success &= E.setInfix(Infix.str());

  pdelete(mpExpression);
  mpExpression = new CMathExpression(E, container, false);
  compileExpression();

  return success;
}
void myD3D11DeviceContext::CopyResource(ID3D11Resource *  pDstResource, ID3D11Resource *  pSrcResource)
{
    if (g_logger->logInterfaceCalls) g_logger->log("CopyResource dst=" + pointerToString(pDstResource) + " src=" + pointerToString(pSrcResource));

    base->CopyResource(pDstResource, pSrcResource);
}
void myD3D11DeviceContext::VSSetShader(ID3D11VertexShader *  pVertexShader, ID3D11ClassInstance * const *  ppClassInstances, UINT  NumClassInstances)
{
    if (g_logger->logInterfaceCalls) g_logger->log("VSSetShader " + pointerToString(pVertexShader));

    base->VSSetShader(pVertexShader, ppClassInstances, NumClassInstances);
}
Esempio n. 15
0
void Logger::recordDrawEvent(MyD3DAssets &assets, const DrawParameters &params)
{
    if (capturingFrame && assets.viewportFullScreen())
    {
        GPUDrawBuffers buffers(assets);

        LocalizedObject object;

        object.load(assets, params, buffers, true);
        
        if (object.vertices.size() > 0)
        {
            frameCaptureObjects.frames[0]->objectData.push_back(object.data);
            frameCaptureObjects.frames[0]->objectMeshes.push_back(object);
        }

        const BufferCPU *VSConstants = assets.getVSConstantBuffer();
        const auto &indexBuffer = assets.getActiveIndexBuffer();
        const auto &vertexBuffer = assets.getActiveVertexBuffer();
        
        const string imagePrefix = "render" + (useLinearCaptureNumbering ? util::zeroPad(frameOutputIndex, 5) : util::zeroPad(frameRenderIndex, 5));
        const string frameImageFile = imagePrefix + "_frame.png";
        const string frameDeltaImageFile = imagePrefix + "_delta.png";
        const string texImageFile = imagePrefix + "_tex";

        if (outputImages)
        {
            Bitmap image;
            assets.context->readRenderTarget(image);
            LodePNG::save(image, g_logger->captureDir + frameImageFile);
            frameOutputIndex++;

            if (prevCaptureImage.getDimensions() == image.getDimensions())
            {
                Bitmap deltaImage = image;
                for (auto &p : deltaImage)
                {
                    if (p.value == prevCaptureImage(p.x, p.y))
                        p.value = vec4uc(0, 0, 0, 255);
                }

                LodePNG::save(deltaImage, g_logger->captureDir + frameDeltaImageFile);
            }

            prevCaptureImage = image;
        }

        auto modifyImage = [](Bitmap &b)
        {
            if (b.size() == 0) return;
            for (auto &p : b)
            {
                p.value.r = unsigned char((int)p.value.r * (int)p.value.a / 255);
                p.value.g = unsigned char((int)p.value.g * (int)p.value.a / 255);
                p.value.b = unsigned char((int)p.value.b * (int)p.value.a / 255);
                p.value.a = 255;
            }
        };

        for (int textureIndex = 0; textureIndex < textureOutputCount; textureIndex++)
        {
            assets.loadPSTexture(textureIndex);
            modifyImage(assets.PSTexture);
            if (assets.PSTexture.size() > 0 && outputImages)
                LodePNG::save(assets.PSTexture, g_logger->captureDir + texImageFile + to_string(textureIndex) + ".png");
        }

        auto makeHTMLImage = [](const string &filename)
        {
            return "<img src=\"" + filename + "\" />";
        };

        logFrameCaptureHtml << "<tr>" << endl;
        logFrameCaptureHtml << "<td>" << frameRenderIndex << "</td>" << endl;
        logFrameCaptureHtml << "<td>" << makeHTMLImage(frameImageFile) << "</td>" << endl;
        logFrameCaptureHtml << "<td>" << makeHTMLImage(frameDeltaImageFile) << "</td>" << endl;

        for (int texIndex = 0; texIndex < textureOutputCount; texIndex++)
            logFrameCaptureHtml << "<td>" << makeHTMLImage(texImageFile + to_string(texIndex) + ".png") << "</td>" << endl;
        
        auto viewport = assets.getViewport();
        logFrameCaptureHtml << "<td>" << object.data.signature << "<br />" << "viewport: " << viewport.Width << "," << viewport.Height << "</td>" << endl;
        logFrameCaptureHtml << "<td>" << params.IndexCount << ", " << params.StartIndexLocation << ", " << params.BaseVertexLocation << "</td>" << endl;
        logFrameCaptureHtml << "<td>" << ((indexBuffer.buffer == nullptr) ? "invalid" : to_string(indexBuffer.buffer->data.size())) + " " + pointerToString(indexBuffer.buffer->GPUHandle) << "</td>" << endl;
        //logFrameCaptureHtml << "<td>" << indexBuffer.offset << "</td>" << endl;
        logFrameCaptureHtml << "<td>" << ((vertexBuffer.buffer == nullptr) ? "invalid" : to_string(vertexBuffer.buffer->data.size())) + " " + pointerToString(vertexBuffer.buffer->GPUHandle) << "</td>" << endl;
        //logFrameCaptureHtml << "<td>" << vertexBuffer.offset << "</td>" << endl;
        logFrameCaptureHtml << "<td>" << vertexBuffer.stride << "</td>" << endl;

        string v0Data = "<none>";
        if (params.BaseVertexLocation != -1 && vertexBuffer.buffer != nullptr && indexBuffer.buffer != nullptr)
        {
            const WORD *indexDataStart = (WORD *)indexBuffer.buffer->data.data() + params.StartIndexLocation;
            
            const BYTE *vertexData = vertexBuffer.buffer->data.data();

            v0Data = "";

            const auto *layout = assets.activeVertexLayout;

            if (layout == nullptr)
            {
                v0Data = "layout not found";
            }
            else
            {
                v0Data += layout->htmlDescription;
                v0Data += "data:<br />";

                for (int indexIndex = 0; indexIndex < min((int)params.IndexCount, 32); indexIndex++)
                {
                    string vertexPrefix = "V" + to_string(indexIndex) + " ";

                    const int curIndex = indexDataStart[indexIndex] + params.BaseVertexLocation;
                    const BYTE *curVertex = (vertexData + (vertexBuffer.stride * curIndex));

                    if (vertexBuffer.buffer->data.size() < vertexBuffer.stride * (curIndex + 1))
                    {
                        v0Data += "*out of bounds*<br />";
                        continue;
                    }

                    const int pOffset = assets.activeVertexLayout->positionOffset;
                    const int bOffset = assets.activeVertexLayout->blendOffset;
                    const int tOffset = assets.activeVertexLayout->tex0Offset;
                    
                    int blendMatrixIndex = -1;
                    if (bOffset != -1)
                    {
                        vec4uc blendIndices = *((const vec4uc *)(curVertex + bOffset));
                        blendMatrixIndex = blendIndices.x;
                    }

                    const float *pStart = (const float *)(curVertex + pOffset);
                    const vec3f basePos(pStart[0], pStart[1], pStart[2]);
                    vec3f pos = assets.transformObjectToWorldGamecube(VSConstants, basePos, blendMatrixIndex);

                    vec2f tex = vec2f::origin;
                    if (tOffset != -1)
                    {
                        const float *tStart = (const float *)(curVertex + tOffset);
                        tex = vec2f(tStart[0], tStart[1]);
                    }

                    v0Data += vertexPrefix + "world=" + pos.toString(", ") + " <br/>";
                    v0Data += vertexPrefix + "index=" + to_string(curIndex) + " <br/>";
                    v0Data += vertexPrefix + "tex=" + tex.toString(", ") + " <br/>";
                }
            }
        }

        logFrameCaptureHtml << "<td>" << v0Data << "</td>" << endl;
        logFrameCaptureHtml << "</tr>" << endl;
    }