Texture not correctly applied on shape when exporting to glTF

I suspect it's an issue with the UVs in the shape. With the code below, I see that the UV coordinates are very large (shouldn't they be between 0-1?),

TopExp_Explorer exp(s, TopAbs_FACE);
while (exp.More())
{
    TopoDS_Face face = TopoDS::Face(exp.Current());
    TopLoc_Location loc;
    auto triangulation = BRep_Tool::Triangulation(face, loc);
    for (size_t i = 1; i <= triangulation->NbNodes(); i++)
    {
                // uv seems wrong.(720,0),(-12,0)...
        auto uv = triangulation->UVNode(i);
    }
    exp.Next();
}

but I don't know how to fix the issue with the UV coordinates in the shape.

Attachments: 
jack-wangss's picture

Here is the code to reproduce

BRepPrimAPI_MakeBox box(gp_Pnt(0, 0, 0), 100, 100, 100);
    auto texturePath = "C:\\Users\\shengshu_wang\\Desktop\\texture.png";
    auto glbPath = "C:\\Users\\shengshu_wang\\Desktop\\box.glb";
    {
        Handle(TDocStd_Application) app = new TDocStd_Application();
        BinXCAFDrivers::DefineFormat(app);
        Handle(TDocStd_Document) doc;
        app->NewDocument("CONVERTXCAF-DOC", doc);
        Handle(XCAFDoc_ShapeTool) m_ShapeTool = XCAFDoc_DocumentTool::ShapeTool(doc->Main());
        TDF_Label label = m_ShapeTool->AddShape(box, false);
        TDataStd_Name::Set(label, "shape_name");

        // material
        Handle(XCAFDoc_VisMaterialTool)  m_VisMaterialTool = XCAFDoc_DocumentTool::VisMaterialTool(doc->Main());
        XCAFDoc_VisMaterialPBR pbrm;
        pbrm.BaseColorTexture = new Image_Texture(texturePath);
        pbrm.IsDefined = true;
        Handle(XCAFDoc_VisMaterial) floorMat = new XCAFDoc_VisMaterial();
        floorMat->SetPbrMaterial(pbrm);


        TDF_Label matLabel = m_VisMaterialTool->AddMaterial(floorMat, "mat_name");
        m_VisMaterialTool->SetShapeMaterial(label, matLabel);

        // perform meshing
        Handle(Prs3d_Drawer) aDrawer = new Prs3d_Drawer(); // holds visualization defaults
        BRepMesh_IncrementalMesh anAlgo;
        anAlgo.ChangeParameters().Deflection = StdPrs_ToolTriangulatedShape::GetDeflection(box, aDrawer);
        anAlgo.ChangeParameters().Angle = 20.0 * M_PI / 180.0; // 20 degrees
        anAlgo.ChangeParameters().InParallel = true;
        anAlgo.SetShape(box);
        anAlgo.Perform();

        RWGltf_CafWriter aGltfWriter(glbPath, true);
        TColStd_IndexedDataMapOfStringString gltfMetaData;
        auto ret = aGltfWriter.Perform(doc, gltfMetaData, Message_ProgressRange());
        if (!ret)
            std::cout << "export error\n";

        app->Close(doc);
    }
Attachments: 
gkv311 n's picture

UV coordinates in Poly_Triangulation define points within underlying analytical surface - so that you may use these parameters to fetch 3D point, surface normals and compute derivatives. Their range could be normalized to [0..1], but there is no such a requirement in B-Rep definition.

In this way, these UV coordinates could be used for texture mapping, but strictly speaking they are not specifically designed for this task. To handle your specific issue, you may consider modifying Poly_Triangulation before export (normalize or rescale UV coordinates), change/override glTF export routines or perform postprocessing (change generated glTF file externally).

  TopoDS_Shape aShape = DBRep::Get(theArgVec[1]);
  TopLoc_Location aFaceLoc;
  for (TopExp_Explorer aFaceIter (aShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
  {
    const TopoDS_Face& aFace = TopoDS::Face (aFaceIter.Value());
    const Handle(Poly_Triangulation)& aTris = BRep_Tool::Triangulation (aFace, aFaceLoc);
    if (aTris.IsNull() || !aTris->HasUVNodes()) { continue; }

    // filter planar surfaces
    //Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace);
    //Handle(Geom_Plane) aPlane = Handle(Geom_Surface)::DownCast (aSurf);
    //if (aPlane.IsNull()) { continue; }

    // compute surface normals before UV parameters will be modified
    BRepLib_ToolTriangulatedShape::ComputeNormals (aFace, aTris);

    // normalize UV range in triangulation
    double aUMin = 0.0, aUMax = 0.0, aVMin = 0.0, aVMax = 0.0;
    BRepTools::UVBounds (aFace, aUMin, aUMax, aVMin, aVMax);
    for (int aNodeIter = 1; aNodeIter <= aTris->NbNodes(); ++aNodeIter)
    {
      gp_Pnt2d aUV = aTris->UVNode (aNodeIter);
      aUV.SetX ((aUV.X() - aUMin) / (aUMax - aUMin));
      aUV.SetY ((aUV.Y() - aVMin) / (aVMax - aVMin));
      aTris->SetUVNode (aNodeIter, aUV);
    }
  }

Note that after modification of UV coordinates in Poly_Triangulation, OCCT algorithms expecting parameters to original surface might produce incorrect results. Hence, make sure to make face copies or something like that before export, if you would like to go this way.

jack-wangss's picture

Thank you, It worked fine.

jack-wangss's picture

Hi,

I meet another issue, I copy the shape since the original shape has other useage, the Brep_Tool::Triangulation return null.

Attachments: 
Dmitrii Pasukhin's picture

Hi, by default BRepBuilderAPI_Copy has no copid of mesh. You need to use parameters BRepBuilderAPI_Copy(aShape, true, true)

Best regards, Dmitrii.