Go to Google Code Home
Google SketchUp SkpReader C++ API Documentation (Labs)

WriteTexturesAndUVs.cpp

This example demonstrates how to populate the texture writer with all the faces found in a model, how to write all the appropriate texture files, and how to generate UV coordinates corrected for perspective distortions. This is one of the most common pitfalls when using the SkpReader API.

Objects implementing the ISkpTextureWriter interface support the abilities to determine which image files are associated with a particular face, write those image files, and correct for perspective distortion.

This example is an abridged form of the SkpToXML exporter example code that comes with the SDK. It does not show how to handle inheritance of textures from component instances or groups to faces they contain. To see how that is done, see the SkpToXML exporter example.

See also:
ISkpTextureWriter
ISkpTextureWriter2
ISkpApplication::CreateTextureWriter
ISkpFace::GetUVHelper
void CXMLExporter::LoadAndWriteTextureFiles()
{
  HResult hr;

  // Get the ISkpApplication from the ISkpDocument
  CComPtr<ISkpApplication> pApp;
  hr = m_pDocument->get_Application(&pApp);

  // Get the ISkpTextureWriter2 interface from ISkpApplication.  You
  // first have to get the ISkpTextureWriter, then get the ISkpTextureWriter2
  // extension from that.
  CComPtr<ISkpTextureWriter> pTW;
  hr = pApp->CreateTextureWriter(&pTW);
  hr = pTW->QueryInterface(IID_ISkpTextureWriter2, (void**)&m_pTextureWriter);

  // Get the top-level ISkpEntityProvider interface from the ISkpDocument, 
  // which is the root component in the model
  CComPtr<ISkpEntityProvider> pEntProvider;
  hr = m_pDocument->QueryInterface(IID_ISkpEntityProvider, (void**) &pEntProvider);

  // Start recursing down the component hierarchy, loading the textures into
  // the ISkpTextureWriter2 along the way
  LoadTexturesFromEntities(pEntProvider);

  // Write out all the textures to a folder 
  std::string btextureDir = GetExportFolder();
  _bstr_t textDirBstr(btextureDir.c_str());
  hr = m_pTextureWriter->WriteAllTextures(textDirBstr, FALSE);
}


// This loads the textures for all the faces in the ISkpEntityProvider.  It recursively
// calls itself for all the component instances, groups and images in the entity set
// so that we load textures for faces all the way down the component hierarchy.
void CXMLExporter::LoadTexturesFromEntities(CComPtr<ISkpEntityProvider> pEntProvider)
{
  HResult hr;
  long nElements, i;

  // Recurse through any component instances in this set of entities
  CComPtr<ISkpComponentInstances> pInstances = NULL;
  hr = pEntProvider->get_ComponentInstances(&pInstances);
  hr = pInstances->get_Count(&nElements);

  for(i=0; i<nElements; i++)
  {
    // Get the ISkpComponentInstance from the ISkpComponentInstances
    CComPtr<ISkpComponentInstance> pInstance;
    hr = pInstances->get_Item(i, &pInstance);

    // Get the instance's component definition (ISkpComponentDefinition)
    CComPtr<ISkpComponentDefinition> pDef;
    hr = pInstance->get_ComponentDefinition(&pDef);

    // Get the ISkpEntityProvider interface from the ISkpComponentDefinition
    CComPtr<ISkpEntityProvider> pEntProvider;
    hr = pDef->QueryInterface(IID_ISkpEntityProvider, (void**) &pEntProvider);

    // Recursively call this function
    LoadTexturesFromEntities(pEntProvider);
  }

  // Recurse through any groups in this set of entities
  CComPtr<ISkpGroups> pGroups = NULL;
  hr = pEntProvider->get_Groups(&pGroups);
  hr = pGroups->get_Count(&nElements);

  for(i=0; i<nElements; i++)
  {
    // Get the ISkpGroup from the ISkpGroups
    CComPtr<ISkpGroup> pGroup;
    hr = pGroups->get_Item(i, &pGroup);

    // Get the ISkpEntityProvider interface from the ISkpGroup
    CComPtr<ISkpEntityProvider> pEntProvider;
    hr = pGroup->QueryInterface(IID_ISkpEntityProvider, (void**) &pEntProvider);

    // Recursively call this function
    LoadTexturesFromEntities(pEntProvider);
  }

  // Recurse through any images in this set of entities
  CComPtr<ISkpImages> pImages = NULL;
  hr = pEntProvider->get_Images(&pImages);
  hr = pImages->get_Count(&nElements);

  for(i=0; i<nElements; i++)
  {
    // Get the ISkpImage from the ISkpImages
    CComPtr<ISkpImage> pImage;
    hr = pImages->get_Item(i, &pImage);

    // Get the ISkpEntityProvider interface from the ISkpImage
    CComPtr<ISkpEntityProvider> pEntProvider;
    hr = pImage->QueryInterface(IID_ISkpEntityProvider, (void**) &pEntProvider);

    // Recursively call this function
    LoadTexturesFromEntities(pEntProvider);
  }

  // Load all the textures applied to faces in this set of entities
  CComPtr<ISkpFaces> pFaces = NULL;
  hr = pEntProvider->get_Faces(&pFaces);
  hr = pFaces->get_Count(&nElements);

  for(i=0; i<nElements; i++)
  {
    // Get the ISkpFace from the ISkpFaces
    CComPtr<ISkpFace> pFace;
    hr = pFaces->get_Item(i, &pFace);

    // Load the texture on the FRONT side of the face into the texture writer
    long handle = 0;
    hr = m_pTextureWriter->LoadFace(pFace, /*bFront*/ true, &handle);

    // Load the texture on the BACK side of the face into the texture writer
    handle=0;
    hr = m_pTextureWriter->LoadFace(pFace, /*bFront*/ false, &handle);
  }
}


// Eventually, you'll be writing out the vertices of each face and will want the UV texture 
// coordinates associated with each vertex.  This is an abridged version of the WriteFace() method
// in the SkpToXML exporter example.
void CXMLExporter::WriteFace(CComPtr<ISkpFace> pFace)
{
  HResult hr;

  //------------------------------------------------------------------------------------------------
  // Determine which sides of this face have textures applied to them

  BOOL bHasFrontTexture = false;
  BOOL bHasBackTexture = false;

  CComPtr<ISkpMaterial> pFrontMaterial = NULL;
  hr = pFace->get_FrontMaterial(pFrontMaterial);
  if (pFrontMaterial)
  {
    hr = pFrontMaterial->get_IsTexture(&bHasFrontTexture);
  }

  CComPtr<ISkpMaterial> pBackMaterial = NULL;
  hr = pFace->get_BackMaterial(pBackMaterial);
  if (pBackMaterial)
  {
    hr = pBackMaterial->get_IsTexture(&bHasBackTexture);
  }

  //------------------------------------------------------------------------------------------------
  // Write out the texture file names for the front and back 
  if (bHasFrontTexture)
  {
    // Get the name of the texture file and write it out
    long texture_handle = 0;
    hr = m_pTextureWriter->GetTextureHandleFromFace(pFace, /*bFront*/ TRUE, &texture_handle)
    if(texture_handle > 0)
    {
      BSTR bstrName;
      // from the handle, get the texture file name
      hr = m_pTextureWriter->GetTextureFile(texture_handle, &bstrName);

      // ***** Write out the name of the front texture file here... ******

      ::SysFreeString(bstrName);
    }
  }

  if (bHasBackTexture)
  {
    // Get the name of the texture file and write it out
    long texture_handle = 0;
    hr = m_pTextureWriter->GetTextureHandleFromFace(pFace, /*bFront*/ FALSE, &texture_handle)
    if(texture_handle > 0)
    {
      BSTR bstrName;
      // from the handle, get the texture file name
      hr = m_pTextureWriter->GetTextureFile(texture_handle, &bstrName);

      // ***** Write out the name of the back texture file here... ******

      ::SysFreeString(bstrName);
    }
  }

  //------------------------------------------------------------------------------------------------
  // If the face has a texture(s) applied to it, then create a UVHelper class so we can output the uv
  // coordinates at each vertex.
  BOOL bHasTexture = bHasFrontTexture | bHasBackTexture;
  if (bHasTexture)
  {
    // You have to create this object from the ISkpTexureWriter to pass into the GetUVHelper method
    // later.  Not creating it as passing in NULL to GetUVHelper will cause your to crash.
    CComPtr<ISkpCorrectPerspective> pCorrectPerspective;
    hr = m_pTextureWriter->QueryInterface(IID_ISkpCorrectPerspective, (void**)&pCorrectPerspective); 

    // Get the ISkpUVHelper for this face
    CComPtr<ISkpUVHelper> pUVHelper = NULL;
    hr = pFace->GetUVHelper(bHasFrontTexture, bHasBackTexture, pCorrectPerspective, &pUVHelper);

    // We're going to output the UV's for the vertices around the face's perimeter (outer loop) 
    // in this example.
    CComPtr<ISkpLoop> pLoop;
    hr = pFace->get_OuterLoop(&pLoop);

    // Get the vertices on the perimeter
    CComPtr<ISkpVertices> pVerts;
    hr = pLoop->get_Vertices(&pVerts);

    long nVerts;
    hr = pVerts->get_Count(&nVerts);

    // Loop through the vertices
    for (long i=0;i<nVerts;i++)
    {
      CComPtr<ISkpVertex> pVert;
      hr = pVerts->get_Item(i, &pVert);

      // Get the position of the vertex - not this in the local coordinate space (LCS)
      CComPtr<ISkpPoint3d> pSkpPoint;
      hr = pVert->get_Position(&pSkpPoint);

      // Transform the point into world coordinate space (WCS)
      CPoint3d point(pSkpPoint);
      CPoint3d worldPoint = m_InheritanceManager.GetCurrentTransform() * point;

      if (bHasFrontTexture)
      {     
        // Get the uv coordinates of the front texture at this point
        double u, v, q;
        pUVHelper->GetFrontUVQ(worldPoint.X(), worldPoint.Y(), worldPoint.Z(), &u, &v, &q);
        
        // ***** Write out the UV coordinates for the front texture on this face at this vertex... *****
      }

      if (bHasBackTexture)
      {
        // Get the uv coordinates of the back texture at this point
        double u, v, q;
        pUVHelper->GetBackUVQ(worldPoint.X(), worldPoint.Y(), worldPoint.Z(), &u, &v, &q);
        
        // ***** Write out the UV coordinates for the back texture on this face at this vertex... *****
      }
    }
  }
}

©2010 Google - Google Home - About Google