]> OCCT Git - occt.git/commitdiff
0031703: Data Exchange, RWGltf_CafWriter - add option putting textures inside GLB...
authormkrylova <mkrylova@opencascade.com>
Fri, 6 Nov 2020 08:33:58 +0000 (11:33 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 17 Dec 2020 18:05:05 +0000 (21:05 +0300)
RWGltf_CafWriter::ToEmbedTexturesInGlb() - added option embedding textures
into GLB file enabled by default.
Fixed uninitialized class field RWGltf_CafWriter::myIsForcedUVExport.

Image_Texture::MimeType() - added method returning MIME type based on image file format.
Image_Texture::WriteImage() - added method writing image into stream.

12 files changed:
src/Image/Image_Texture.cxx
src/Image/Image_Texture.hxx
src/RWGltf/RWGltf_CafWriter.cxx
src/RWGltf/RWGltf_CafWriter.hxx
src/RWGltf/RWGltf_GltfBufferView.hxx
src/RWGltf/RWGltf_GltfMaterialMap.cxx
src/RWGltf/RWGltf_GltfMaterialMap.hxx
src/RWMesh/RWMesh_MaterialMap.cxx
src/RWMesh/RWMesh_MaterialMap.hxx
src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx
tests/de_mesh/gltf_write/helmetglb [new file with mode: 0644]
tests/de_mesh/gltf_write/lanternglb [new file with mode: 0644]

index ce9d2a540a9b52233ed1d555f59a7a9b6248be9e..7d1a168ac072390a40f1a88bc16c38f29102091a 100644 (file)
@@ -207,6 +207,36 @@ Handle(Image_PixMap) Image_Texture::loadImageOffset (const TCollection_AsciiStri
   return anImage;
 }
 
+// ================================================================
+// Function : MimeType
+// Purpose  :
+// ================================================================
+TCollection_AsciiString Image_Texture::MimeType() const
+{
+  const TCollection_AsciiString aType = ProbeImageFileFormat();
+  if (aType == "jpg")
+  {
+    return "image/jpeg";
+  }
+  else if (aType == "png"
+        || aType == "bmp"
+        || aType == "webp"
+        || aType == "gif"
+        || aType == "tiff")
+  {
+    return TCollection_AsciiString ("image/") + aType;
+  }
+  else if (aType == "dds")
+  {
+    return "image/vnd-ms.dds";
+  }
+  else if (!aType.IsEmpty())
+  {
+    return TCollection_AsciiString ("image/x-") + aType;
+  }
+  return TCollection_AsciiString();
+}
+
 // ================================================================
 // Function : ProbeImageFileFormat
 // Purpose  :
@@ -285,58 +315,92 @@ TCollection_AsciiString Image_Texture::ProbeImageFileFormat() const
 // ================================================================
 Standard_Boolean Image_Texture::WriteImage (const TCollection_AsciiString& theFile)
 {
-  Handle(NCollection_Buffer) aBuffer = myBuffer;
-  if (myBuffer.IsNull())
+  std::ofstream aFileOut;
+  OSD_OpenStream (aFileOut, theFile.ToCString(), std::ios::out | std::ios::binary | std::ios::trunc);
+  if (!aFileOut)
   {
-    std::ifstream aFileIn;
-    OSD_OpenStream (aFileIn, myImagePath.ToCString(), std::ios::in | std::ios::binary);
-    if (!aFileIn)
+    Message::SendFail (TCollection_AsciiString ("Error: Unable to create file '") + theFile + "'");
+    return false;
+  }
+
+  if (!WriteImage (aFileOut, theFile))
+  {
+    return false;
+  }
+
+  aFileOut.close();
+  if (!aFileOut.good())
+  {
+    Message::SendFail (TCollection_AsciiString ("Error: Unable to write file '") + theFile + "'");
+    return false;
+  }
+  return true;
+}
+
+// ================================================================
+// Function : WriteImage
+// Purpose  :
+// ================================================================
+Standard_Boolean Image_Texture::WriteImage (std::ostream& theStream,
+                                            const TCollection_AsciiString& theFile)
+{
+  if (!myBuffer.IsNull())
+  {
+    theStream.write ((const char* )myBuffer->Data(), myBuffer->Size());
+    if (!theStream.good())
     {
-      Message::SendFail (TCollection_AsciiString ("Error: Unable to open file ") + myImagePath + "!");
-      return Standard_False;
+      Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' cannot be written");
+      return false;
     }
+    return true;
+  }
 
-    Standard_Size aLen = (Standard_Size )myLength;
-    if (myOffset >= 0)
+  std::ifstream aFileIn;
+  OSD_OpenStream (aFileIn, myImagePath.ToCString(), std::ios::in | std::ios::binary);
+  if (!aFileIn)
+  {
+    Message::SendFail (TCollection_AsciiString ("Error: Unable to open file ") + myImagePath + "!");
+    return false;
+  }
+
+  int64_t aLen = myLength;
+  if (myOffset >= 0)
+  {
+    aFileIn.seekg ((std::streamoff )myOffset, std::ios_base::beg);
+    if (!aFileIn.good())
     {
-      aFileIn.seekg ((std::streamoff )myOffset, std::ios_base::beg);
-      if (!aFileIn.good())
-      {
-        Message::SendFail (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + myImagePath + "'");
-        return Standard_False;
-      }
+      Message::SendFail (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + myImagePath + "'");
+      return false;
     }
-    else
+  }
+  else
+  {
+    aFileIn.seekg (0, std::ios_base::end);
+    aLen = (int64_t )aFileIn.tellg();
+    aFileIn.seekg (0, std::ios_base::beg);
+  }
+
+  Standard_Integer aChunkSize = 4096;
+  NCollection_Array1<Standard_Byte> aBuffer (0, aChunkSize - 1);
+  for (int64_t aChunkIter = 0; aChunkIter < aLen; aChunkIter += aChunkSize)
+  {
+    if (aChunkIter + aChunkSize >= aLen)
     {
-      aFileIn.seekg (0, std::ios_base::end);
-      aLen = (Standard_Size )aFileIn.tellg();
-      aFileIn.seekg (0, std::ios_base::beg);
+      aChunkSize = Standard_Integer(aLen - aChunkIter);
     }
-
-    aBuffer = new NCollection_Buffer (NCollection_BaseAllocator::CommonBaseAllocator(), aLen);
-    if (!aFileIn.read ((char* )aBuffer->ChangeData(), aBuffer->Size()))
+    if (!aFileIn.read ((char* )&aBuffer.ChangeFirst(), aChunkSize))
     {
       Message::SendFail (TCollection_AsciiString ("Error: unable to read image file '") + myImagePath + "'");
-      return Standard_False;
+      return false;
     }
+    theStream.write ((const char* )&aBuffer.First(), aChunkSize);
   }
-
-  std::ofstream aFileOut;
-  OSD_OpenStream (aFileOut, theFile.ToCString(), std::ios::out | std::ios::binary | std::ios::trunc);
-  if (!aFileOut)
-  {
-    Message::SendFail (TCollection_AsciiString ("Error: Unable to create file '") + theFile + "'");
-    return Standard_False;
-  }
-
-  aFileOut.write ((const char* )aBuffer->Data(), aBuffer->Size());
-  aFileOut.close();
-  if (!aFileOut.good())
+  if (!theStream.good())
   {
-    Message::SendFail (TCollection_AsciiString ("Error: Unable to write file '") + theFile + "'");
-    return Standard_False;
+    Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' can not be written");
+    return false;
   }
-  return Standard_True;
+  return true;
 }
 
 //=======================================================================
index 7a6f53be6bef2e0bcbd06c7c13c3f882d1a371b5..57575a52831acd01d51fca8eba190d8ba6bc907b 100644 (file)
@@ -56,6 +56,9 @@ public:
   //! Return buffer holding encoded image content.
   const Handle(NCollection_Buffer)& DataBuffer() const { return myBuffer; }
 
+  //! Return mime-type of image file based on ProbeImageFileFormat().
+  Standard_EXPORT TCollection_AsciiString MimeType() const;
+
   //! Return image file format.
   Standard_EXPORT TCollection_AsciiString ProbeImageFileFormat() const;
 
@@ -68,6 +71,10 @@ public:
   //! Write image to specified file without decoding data.
   Standard_EXPORT virtual Standard_Boolean WriteImage (const TCollection_AsciiString& theFile);
 
+  //! Write image to specified stream without decoding data.
+  Standard_EXPORT virtual Standard_Boolean WriteImage (std::ostream& theStream,
+                                                       const TCollection_AsciiString& theFile);
+
 public: //! @name hasher interface
 
   //! Hash value, for Map interface.
index 0b4f2a0f5c204590ad505cfe5045c4f4a06ab29f..99b2e271f0af811a33e800b4b3d7846a1a87e5c9 100644 (file)
@@ -103,6 +103,8 @@ RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile,
 : myFile          (theFile),
   myTrsfFormat    (RWGltf_WriterTrsfFormat_Compact),
   myIsBinary      (theIsBinary),
+  myIsForcedUVExport (false),
+  myToEmbedTexturesInGlb (true),
   myBinDataLen64  (0)
 {
   myCSTrsf.SetOutputLengthUnit (1.0); // meters
@@ -303,6 +305,10 @@ bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
                                 const TColStd_IndexedDataMapOfStringString& theFileInfo,
                                 const Message_ProgressRange& theProgress)
 {
+  const Standard_Integer aDefSamplerId = 0;
+  myMaterialMap = new RWGltf_GltfMaterialMap (myFile, aDefSamplerId);
+  myMaterialMap->SetDefaultStyle (myDefaultStyle);
+
   Message_ProgressScope aPSentry (theProgress, "Writing glTF file", 2);
   if (!writeBinData (theDocument, theRootLabels, theLabelFilter, aPSentry.Next()))
   {
@@ -326,18 +332,25 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
                                      const TColStd_MapOfAsciiString* theLabelFilter,
                                      const Message_ProgressRange& theProgress)
 {
+  myBuffViewPos.Id               = RWGltf_GltfAccessor::INVALID_ID;
   myBuffViewPos.ByteOffset       = 0;
   myBuffViewPos.ByteLength       = 0;
   myBuffViewPos.ByteStride       = 12;
   myBuffViewPos.Target           = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
+
+  myBuffViewNorm.Id              = RWGltf_GltfAccessor::INVALID_ID;
   myBuffViewNorm.ByteOffset      = 0;
   myBuffViewNorm.ByteLength      = 0;
   myBuffViewNorm.ByteStride      = 12;
   myBuffViewNorm.Target          = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
+
+  myBuffViewTextCoord.Id         = RWGltf_GltfAccessor::INVALID_ID;
   myBuffViewTextCoord.ByteOffset = 0;
   myBuffViewTextCoord.ByteLength = 0;
   myBuffViewTextCoord.ByteStride = 8;
   myBuffViewTextCoord.Target     = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
+
+  myBuffViewInd.Id               = RWGltf_GltfAccessor::INVALID_ID;
   myBuffViewInd.ByteOffset       = 0;
   myBuffViewInd.ByteLength       = 0;
   myBuffViewInd.Target           = RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER;
@@ -515,6 +528,33 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
   }
   myBuffViewInd.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewInd.ByteOffset;
 
+  if (myIsBinary
+   && myToEmbedTexturesInGlb)
+  {
+    // save unique image textures
+    for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
+         aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
+    {
+      const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
+      if (theLabelFilter != NULL
+      && !theLabelFilter->Contains (aDocNode.Id))
+      {
+        continue;
+      }
+
+      for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style);
+           aFaceIter.More(); aFaceIter.Next())
+      {
+        if (toSkipFaceMesh (aFaceIter))
+        {
+          continue;
+        }
+
+        myMaterialMap->AddGlbImages (aBinFile, aFaceIter.FaceStyle());
+      }
+    }
+  }
+
   int aBuffViewId = 0;
   if (myBuffViewPos.ByteLength > 0)
   {
@@ -532,6 +572,7 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
   {
     myBuffViewInd.Id = aBuffViewId++;
   }
+  // myMaterialMap->FlushGlbBufferViews() will put image bufferView's IDs at the end of list
 
   myBinDataLen64 = aBinFile.tellp();
   aBinFile.close();
@@ -560,7 +601,6 @@ bool RWGltf_CafWriter::writeJson (const Handle(TDocStd_Document)&  theDocument,
   Message_ProgressScope aPSentryBin (theProgress, "Header data", 2);
 
   const Standard_Integer aBinDataBufferId = 0;
-  const Standard_Integer aDefSamplerId    = 0;
   const Standard_Integer aDefSceneId      = 0;
 
   const TCollection_AsciiString aFileNameGltf = myFile;
@@ -612,13 +652,11 @@ bool RWGltf_CafWriter::writeJson (const Handle(TDocStd_Document)&  theDocument,
   writeAsset (theFileInfo);
   writeBufferViews (aBinDataBufferId);
   writeBuffers();
-  writeExtensions ();
+  writeExtensions();
 
-  RWGltf_GltfMaterialMap aMaterialMap (myFile, aDefSamplerId);
-  aMaterialMap.SetDefaultStyle (myDefaultStyle);
-  writeImages    (aSceneNodeMap, aMaterialMap);
-  writeMaterials (aSceneNodeMap, aMaterialMap);
-  writeMeshes    (aSceneNodeMap, aMaterialMap);
+  writeImages    (aSceneNodeMap);
+  writeMaterials (aSceneNodeMap);
+  writeMeshes    (aSceneNodeMap);
 
   aPSentryBin.Next();
   if (!aPSentryBin.More())
@@ -629,11 +667,11 @@ bool RWGltf_CafWriter::writeJson (const Handle(TDocStd_Document)&  theDocument,
   // root nodes indices starting from 0
   NCollection_Sequence<Standard_Integer> aSceneRootNodeInds;
   writeNodes (theDocument, theRootLabels, theLabelFilter, aSceneNodeMap, aSceneRootNodeInds);
-  writeSamplers (aMaterialMap);
+  writeSamplers();
   writeScene (aDefSceneId);
   writeScenes (aSceneRootNodeInds);
   writeSkins();
-  writeTextures (aSceneNodeMap, aMaterialMap);
+  writeTextures (aSceneNodeMap);
 
   myWriter->EndObject();
 
@@ -1044,10 +1082,12 @@ void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBuffer
 #ifdef HAVE_RAPIDJSON
   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBufferViews()");
 
+  int aBuffViewId = 0;
   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_BufferViews));
   myWriter->StartArray();
   if (myBuffViewPos.Id != RWGltf_GltfAccessor::INVALID_ID)
   {
+    aBuffViewId++;
     myWriter->StartObject();
     myWriter->Key    ("buffer");
     myWriter->Int    (theBinDataBufferId);
@@ -1063,6 +1103,7 @@ void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBuffer
   }
   if (myBuffViewNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
   {
+    aBuffViewId++;
     myWriter->StartObject();
     myWriter->Key    ("buffer");
     myWriter->Int    (theBinDataBufferId);
@@ -1078,6 +1119,7 @@ void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBuffer
   }
   if (myBuffViewTextCoord.Id != RWGltf_GltfAccessor::INVALID_ID)
   {
+    aBuffViewId++;
     myWriter->StartObject();
     myWriter->Key    ("buffer");
     myWriter->Int    (theBinDataBufferId);
@@ -1093,6 +1135,7 @@ void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBuffer
   }
   if (myBuffViewInd.Id != RWGltf_GltfAccessor::INVALID_ID)
   {
+    aBuffViewId++;
     myWriter->StartObject();
     myWriter->Key    ("buffer");
     myWriter->Int    (theBinDataBufferId);
@@ -1104,6 +1147,9 @@ void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBuffer
     myWriter->Int    (myBuffViewInd.Target);
     myWriter->EndObject();
   }
+
+  myMaterialMap->FlushGlbBufferViews (myWriter.get(), theBinDataBufferId, aBuffViewId);
+
   myWriter->EndArray();
 #else
   (void )theBinDataBufferId;
@@ -1125,8 +1171,7 @@ void RWGltf_CafWriter::writeBuffers()
     myWriter->StartObject();
     {
       myWriter->Key   ("byteLength");
-      myWriter->Int64 (myBuffViewPos.ByteLength + myBuffViewNorm.ByteLength +
-                       myBuffViewTextCoord.ByteLength + myBuffViewInd.ByteLength);
+      myWriter->Int64 (myBinDataLen64);
       if (!myIsBinary)
       {
         myWriter->Key   ("uri");
@@ -1152,29 +1197,35 @@ void RWGltf_CafWriter::writeExtensions()
 // function : writeImages
 // purpose  :
 // =======================================================================
-void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
-                                    RWGltf_GltfMaterialMap& theMaterialMap)
+void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
 {
 #ifdef HAVE_RAPIDJSON
   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeImages()");
 
   // empty RWGltf_GltfRootElement_Images section should NOT be written to avoid validator errors
-  bool anIsStarted = false;
-  for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
+  if (myIsBinary
+   && myToEmbedTexturesInGlb)
   {
-    const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
-    {
-      theMaterialMap.AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
-    }
+    myMaterialMap->FlushGlbImages (myWriter.get());
   }
-  if (anIsStarted)
+  else
   {
-    myWriter->EndArray();
+    bool anIsStarted = false;
+    for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter(theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
+    {
+      const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
+      for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
+      {
+        myMaterialMap->AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
+      }
+    }
+    if (anIsStarted)
+    {
+      myWriter->EndArray();
+    }
   }
 #else
   (void )theSceneNodeMap;
-  (void )theMaterialMap;
 #endif
 }
 
@@ -1182,8 +1233,7 @@ void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeM
 // function : writeMaterials
 // purpose  :
 // =======================================================================
-void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
-                                       RWGltf_GltfMaterialMap& theMaterialMap)
+void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
 {
 #ifdef HAVE_RAPIDJSON
   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMaterials()");
@@ -1195,7 +1245,7 @@ void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNo
     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
     {
-      theMaterialMap.AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
+      myMaterialMap->AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
     }
   }
   if (anIsStarted)
@@ -1204,7 +1254,6 @@ void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNo
   }
 #else
   (void )theSceneNodeMap;
-  (void )theMaterialMap;
 #endif
 }
 
@@ -1212,8 +1261,7 @@ void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNo
 // function : writeMeshes
 // purpose  :
 // =======================================================================
-void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
-                                    const RWGltf_GltfMaterialMap&  theMaterialMap)
+void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
 {
 #ifdef HAVE_RAPIDJSON
   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMeshes()");
@@ -1248,7 +1296,7 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM
       }
 
       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
-      const TCollection_AsciiString aMatId = theMaterialMap.FindMaterial (aFaceIter.FaceStyle());
+      const TCollection_AsciiString aMatId = myMaterialMap->FindMaterial (aFaceIter.FaceStyle());
       myWriter->StartObject();
       {
         myWriter->Key ("attributes");
@@ -1287,7 +1335,6 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM
   myWriter->EndArray();
 #else
   (void )theSceneNodeMap;
-  (void )theMaterialMap;
 #endif
 }
 
@@ -1476,11 +1523,11 @@ void RWGltf_CafWriter::writeNodes (const Handle(TDocStd_Document)&  theDocument,
 // function : writeSamplers
 // purpose  :
 // =======================================================================
-void RWGltf_CafWriter::writeSamplers (const RWGltf_GltfMaterialMap& theMaterialMap)
+void RWGltf_CafWriter::writeSamplers()
 {
 #ifdef HAVE_RAPIDJSON
   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
-  if (theMaterialMap.NbImages() == 0)
+  if (myMaterialMap->NbImages() == 0)
   {
     return;
   }
@@ -1498,8 +1545,6 @@ void RWGltf_CafWriter::writeSamplers (const RWGltf_GltfMaterialMap& theMaterialM
     myWriter->EndObject();
   }
   myWriter->EndArray();
-#else
-  (void )theMaterialMap;
 #endif
 }
 
@@ -1565,8 +1610,7 @@ void RWGltf_CafWriter::writeSkins()
 // function : writeTextures
 // purpose  :
 // =======================================================================
-void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
-                                      RWGltf_GltfMaterialMap& theMaterialMap)
+void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
 {
 #ifdef HAVE_RAPIDJSON
   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextures()");
@@ -1578,7 +1622,7 @@ void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNod
     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
     {
-      theMaterialMap.AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
+      myMaterialMap->AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
     }
   }
   if (anIsStarted)
@@ -1587,6 +1631,5 @@ void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNod
   }
 #else
  (void )theSceneNodeMap;
- (void )theMaterialMap;
 #endif
 }
index 5444fdd458aaabaf4eda6ac4487146b6d0dda0aa..0d84cb1998821c83b4dfc1c8df130a4546f00c3c 100644 (file)
@@ -78,6 +78,14 @@ public:
   //! Set default material definition to be used for nodes with only color defined.
   void SetDefaultStyle (const XCAFPrs_Style& theStyle) { myDefaultStyle = theStyle; }
 
+  //! Return flag to write image textures into GLB file (binary gltf export); TRUE by default.
+  //! When set to FALSE, texture images will be written as separate files.
+  //! Has no effect on writing into non-binary format.
+  Standard_Boolean ToEmbedTexturesInGlb() { return myToEmbedTexturesInGlb; }
+
+  //! Set flag to write image textures into GLB file (binary gltf export).
+  void SetToEmbedTexturesInGlb (Standard_Boolean theToEmbedTexturesInGlb) { myToEmbedTexturesInGlb = theToEmbedTexturesInGlb; }
+
   //! Write glTF file and associated binary file.
   //! Triangulation data should be precomputed within shapes!
   //! @param theDocument    [in] input document
@@ -221,20 +229,17 @@ protected:
   //! Write RWGltf_GltfRootElement_Images section.
   //! @param theSceneNodeMap [in] ordered map of scene nodes
   //! @param theMaterialMap [out] map of materials, filled with image files used by textures
-  Standard_EXPORT virtual void writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
-                                            RWGltf_GltfMaterialMap& theMaterialMap);
+  Standard_EXPORT virtual void writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap);
 
   //! Write RWGltf_GltfRootElement_Materials section.
   //! @param theSceneNodeMap [in] ordered map of scene nodes
   //! @param theMaterialMap [out] map of materials, filled with materials
-  Standard_EXPORT virtual void writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
-                                               RWGltf_GltfMaterialMap& theMaterialMap);
+  Standard_EXPORT virtual void writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap);
 
   //! Write RWGltf_GltfRootElement_Meshes section.
   //! @param theSceneNodeMap [in] ordered map of scene nodes
   //! @param theMaterialMap  [in] map of materials
-  Standard_EXPORT virtual void writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
-                                            const RWGltf_GltfMaterialMap& theMaterialMap);
+  Standard_EXPORT virtual void writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap);
 
   //! Write RWGltf_GltfRootElement_Nodes section.
   //! @param theDocument     [in] input document
@@ -249,7 +254,7 @@ protected:
                                            NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds);
 
   //! Write RWGltf_GltfRootElement_Samplers section.
-  Standard_EXPORT virtual void writeSamplers (const RWGltf_GltfMaterialMap& theMaterialMap);
+  Standard_EXPORT virtual void writeSamplers();
 
   //! Write RWGltf_GltfRootElement_Scene section.
   //! @param theDefSceneId [in] index of default scene (0)
@@ -265,8 +270,7 @@ protected:
   //! Write RWGltf_GltfRootElement_Textures section.
   //! @param theSceneNodeMap [in] ordered map of scene nodes
   //! @param theMaterialMap [out] map of materials, filled with textures
-  Standard_EXPORT virtual void writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
-                                              RWGltf_GltfMaterialMap& theMaterialMap);
+  Standard_EXPORT virtual void writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap);
 
 protected:
 
@@ -276,11 +280,13 @@ protected:
   RWGltf_WriterTrsfFormat                       myTrsfFormat;        //!< transformation format to write into glTF file
   Standard_Boolean                              myIsBinary;          //!< flag to write into binary glTF format (.glb)
   Standard_Boolean                              myIsForcedUVExport;  //!< export UV coordinates even if there are no mapped texture
+  Standard_Boolean                              myToEmbedTexturesInGlb; //!< flag to write image textures into GLB file
   RWMesh_CoordinateSystemConverter              myCSTrsf;            //!< transformation from OCCT to glTF coordinate system
   XCAFPrs_Style                                 myDefaultStyle;      //!< default material definition to be used for nodes with only color defined
 
   opencascade::std::shared_ptr<RWGltf_GltfOStreamWriter>
                                                 myWriter;            //!< JSON writer
+  Handle(RWGltf_GltfMaterialMap)                myMaterialMap;       //!< map of defined materials
   RWGltf_GltfBufferView                         myBuffViewPos;       //!< current buffer view with nodes positions
   RWGltf_GltfBufferView                         myBuffViewNorm;      //!< current buffer view with nodes normals
   RWGltf_GltfBufferView                         myBuffViewTextCoord; //!< current buffer view with nodes UV coordinates
index dfdd6aee611fe712bd6afc61471fc55520e7848e..2dbc88a3d9fe8bb14dc80195ae37712b5acec0af 100644 (file)
@@ -24,9 +24,9 @@ struct RWGltf_GltfBufferView
   static const int INVALID_ID = -1;
 public:
 
-  int                         Id;
-  int64_t                     ByteOffset;
-  int64_t                     ByteLength;
+  int                         Id;         //!< index of bufferView in the array of bufferViews
+  int64_t                     ByteOffset; //!< offset to the beginning of the data in buffer
+  int64_t                     ByteLength; //!< length of the data
   int32_t                     ByteStride; //!< [0, 255]
   RWGltf_GltfBufferViewTarget Target;
 
index 763538de82de54fe82f5d99e82020dda15594aa6..b143e5b554b4c2b79d09139ba09c23459dfceb8d 100644 (file)
 
 #include <RWGltf_GltfMaterialMap.hxx>
 
+#include <Message.hxx>
+#include <NCollection_Array1.hxx>
+#include <OSD_OpenFile.hxx>
 #include <RWGltf_GltfRootElement.hxx>
 
 #ifdef HAVE_RAPIDJSON
   #include <RWGltf_GltfOStreamWriter.hxx>
 #endif
 
+IMPLEMENT_STANDARD_RTTIEXT(RWGltf_GltfMaterialMap, RWMesh_MaterialMap)
+
 // =======================================================================
 // function : baseColorTexture
 // purpose  :
@@ -51,8 +56,7 @@ RWGltf_GltfMaterialMap::RWGltf_GltfMaterialMap (const TCollection_AsciiString& t
                                                 const Standard_Integer theDefSamplerId)
 : RWMesh_MaterialMap (theFile),
   myWriter (NULL),
-  myDefSamplerId (theDefSamplerId),
-  myNbImages (0)
+  myDefSamplerId (theDefSamplerId)
 {
   myMatNameAsKey = false;
 }
@@ -88,6 +92,26 @@ void RWGltf_GltfMaterialMap::AddImages (RWGltf_GltfOStreamWriter* theWriter,
   addImage (theWriter, theStyle.Material()->PbrMaterial().OcclusionTexture, theIsStarted);
 }
 
+// =======================================================================
+// function : AddGlbImages
+// purpose  :
+// =======================================================================
+void RWGltf_GltfMaterialMap::AddGlbImages (std::ostream& theBinFile,
+                                           const XCAFPrs_Style& theStyle)
+{
+  if (theStyle.Material().IsNull()
+   || theStyle.Material()->IsEmpty())
+  {
+    return;
+  }
+
+  addGlbImage (theBinFile, baseColorTexture (theStyle.Material()));
+  addGlbImage (theBinFile, theStyle.Material()->PbrMaterial().MetallicRoughnessTexture);
+  addGlbImage (theBinFile, theStyle.Material()->PbrMaterial().NormalTexture);
+  addGlbImage (theBinFile, theStyle.Material()->PbrMaterial().EmissiveTexture);
+  addGlbImage (theBinFile, theStyle.Material()->PbrMaterial().OcclusionTexture);
+}
+
 // =======================================================================
 // function : addImage
 // purpose  :
@@ -98,27 +122,20 @@ void RWGltf_GltfMaterialMap::addImage (RWGltf_GltfOStreamWriter* theWriter,
 {
 #ifdef HAVE_RAPIDJSON
   if (theTexture.IsNull()
-   || myImageMap.IsBound1 (theTexture)
+   || myImageMap.Contains (theTexture)
    || myImageFailMap.Contains (theTexture))
   {
     return;
   }
 
-  TCollection_AsciiString aGltfImgKey = myNbImages;
-  ++myNbImages;
-  for (; myImageMap.IsBound2 (aGltfImgKey); ++myNbImages)
-  {
-    aGltfImgKey = myNbImages;
-  }
-
+  const TCollection_AsciiString aGltfImgKey = myImageMap.Extent();
   TCollection_AsciiString aTextureUri;
   if (!CopyTexture (aTextureUri, theTexture, aGltfImgKey))
   {
     myImageFailMap.Add (theTexture);
     return;
   }
-
-  myImageMap.Bind (theTexture, aGltfImgKey);
+  myImageMap.Add (theTexture, RWGltf_GltfBufferView());
 
   if (!theIsStarted)
   {
@@ -140,6 +157,133 @@ void RWGltf_GltfMaterialMap::addImage (RWGltf_GltfOStreamWriter* theWriter,
 #endif
 }
 
+// =======================================================================
+// function : addGlbImage
+// purpose  :
+// =======================================================================
+void RWGltf_GltfMaterialMap::addGlbImage (std::ostream& theBinFile,
+                                          const Handle(Image_Texture)& theTexture)
+{
+  if (theTexture.IsNull()
+   || myImageMap.Contains (theTexture)
+   || myImageFailMap.Contains (theTexture))
+  {
+    return;
+  }
+
+  RWGltf_GltfBufferView aBuffImage;
+  aBuffImage.ByteOffset = theBinFile.tellp();
+  if (!theTexture->WriteImage (theBinFile, myFileName))
+  {
+    myImageFailMap.Add (theTexture);
+    return;
+  }
+
+  // alignment by 4 bytes
+  int64_t aContentLen64 = (int64_t)theBinFile.tellp();
+  while (aContentLen64 % 4 != 0)
+  {
+    theBinFile.write (" ", 1);
+    ++aContentLen64;
+  }
+
+  //aBuffImage.Id = myBuffViewImages.Size(); // id will be corrected later
+  aBuffImage.ByteLength = (int64_t)theBinFile.tellp() - aBuffImage.ByteOffset;
+  if (aBuffImage.ByteLength <= 0)
+  {
+    myImageFailMap.Add (theTexture);
+    return;
+  }
+
+  myImageMap.Add (theTexture, aBuffImage);
+}
+
+// =======================================================================
+// function : FlushBufferViews
+// purpose  :
+// =======================================================================
+void RWGltf_GltfMaterialMap::FlushGlbBufferViews (RWGltf_GltfOStreamWriter* theWriter,
+                                                  const Standard_Integer theBinDataBufferId,
+                                                  Standard_Integer& theBuffViewId)
+{
+#ifdef HAVE_RAPIDJSON
+  for (NCollection_IndexedDataMap<Handle(Image_Texture), RWGltf_GltfBufferView, Image_Texture>::Iterator aBufViewIter (myImageMap);
+       aBufViewIter.More(); aBufViewIter.Next())
+  {
+    RWGltf_GltfBufferView& aBuffView = aBufViewIter.ChangeValue();
+    if (aBuffView.ByteLength <= 0)
+    {
+      continue;
+    }
+
+    aBuffView.Id = theBuffViewId++;
+    theWriter->StartObject();
+    theWriter->Key ("buffer");
+    theWriter->Int (theBinDataBufferId);
+    theWriter->Key ("byteLength");
+    theWriter->Int64 (aBuffView.ByteLength);
+    theWriter->Key ("byteOffset");
+    theWriter->Int64 (aBuffView.ByteOffset);
+    theWriter->EndObject();
+  }
+#else
+  (void )theWriter;
+  (void )theBinDataBufferId;
+  (void )theBuffViewId;
+#endif
+}
+
+// =======================================================================
+// function : FlushGlbImages
+// purpose  :
+// =======================================================================
+void RWGltf_GltfMaterialMap::FlushGlbImages (RWGltf_GltfOStreamWriter* theWriter)
+{
+#ifdef HAVE_RAPIDJSON
+  bool isStarted = false;
+  for (NCollection_IndexedDataMap<Handle(Image_Texture), RWGltf_GltfBufferView, Image_Texture>::Iterator aBufViewIter (myImageMap);
+       aBufViewIter.More(); aBufViewIter.Next())
+  {
+    const Handle(Image_Texture)& aTexture  = aBufViewIter.Key();
+    const RWGltf_GltfBufferView& aBuffView = aBufViewIter.Value();
+    if (aBuffView.ByteLength <= 0)
+    {
+      continue;
+    }
+
+    if (!isStarted)
+    {
+      theWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Images));
+      theWriter->StartArray();
+      isStarted = true;
+    }
+
+    theWriter->StartObject();
+    {
+      const TCollection_AsciiString anImageFormat = aTexture->MimeType();
+      if (anImageFormat != "image/png"
+       && anImageFormat != "image/jpeg")
+      {
+        Message::SendWarning (TCollection_AsciiString ("Warning! Non-standard mime-type ")
+                              + anImageFormat + " (texture " + aTexture->TextureId()
+                              + ") within glTF file");
+      }
+      theWriter->Key ("mimeType");
+      theWriter->String (anImageFormat.ToCString());
+      theWriter->Key ("bufferView");
+      theWriter->Int (aBuffView.Id);
+    }
+    theWriter->EndObject();
+  }
+  if (isStarted)
+  {
+    theWriter->EndArray();
+  }
+#else
+  (void )theWriter;
+#endif
+}
+
 // =======================================================================
 // function : AddMaterial
 // purpose  :
@@ -205,17 +349,13 @@ void RWGltf_GltfMaterialMap::addTexture (RWGltf_GltfOStreamWriter* theWriter,
 #ifdef HAVE_RAPIDJSON
   if (theTexture.IsNull()
   ||  myTextureMap.Contains (theTexture)
-  || !myImageMap  .IsBound1 (theTexture))
+  || !myImageMap  .Contains (theTexture))
   {
     return;
   }
 
-  const TCollection_AsciiString anImgKey = myImageMap.Find1 (theTexture);
+  const Standard_Integer anImgKey = myImageMap.FindIndex (theTexture) - 1; // glTF indexation starts from 0
   myTextureMap.Add (theTexture);
-  if (anImgKey.IsEmpty())
-  {
-    return;
-  }
 
   if (!theIsStarted)
   {
@@ -229,7 +369,7 @@ void RWGltf_GltfMaterialMap::addTexture (RWGltf_GltfOStreamWriter* theWriter,
     theWriter->Key ("sampler");
     theWriter->Int (myDefSamplerId); // mandatory field by specs
     theWriter->Key ("source");
-    theWriter->Int (anImgKey.IntegerValue());
+    theWriter->Int (anImgKey);
   }
   theWriter->EndObject();
 #else
@@ -303,17 +443,14 @@ void RWGltf_GltfMaterialMap::DefineMaterial (const XCAFPrs_Style& theStyle,
 
       if (const Handle(Image_Texture)& aBaseTexture = baseColorTexture (theStyle.Material()))
       {
-        if (myImageMap.IsBound1 (aBaseTexture))
+        const Standard_Integer aBaseImageIdx = myImageMap.FindIndex (aBaseTexture) - 1;
+        if (aBaseImageIdx != -1)
         {
           myWriter->Key ("baseColorTexture");
           myWriter->StartObject();
           {
             myWriter->Key ("index");
-            const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aBaseTexture);
-            if (!anImageIdx.IsEmpty())
-            {
-              myWriter->Int (anImageIdx.IntegerValue());
-            }
+            myWriter->Int (aBaseImageIdx);
           }
           myWriter->EndObject();
         }
@@ -326,18 +463,16 @@ void RWGltf_GltfMaterialMap::DefineMaterial (const XCAFPrs_Style& theStyle,
         myWriter->Double (aPbrMat.Metallic);
       }
 
-      if (!aPbrMat.MetallicRoughnessTexture.IsNull()
-        && myImageMap.IsBound1 (aPbrMat.MetallicRoughnessTexture))
+      const Standard_Integer aMetRoughImageIdx = !aPbrMat.MetallicRoughnessTexture.IsNull()
+                                               ? myImageMap.FindIndex (aPbrMat.MetallicRoughnessTexture) - 1
+                                               : -1;
+      if (aMetRoughImageIdx != -1)
       {
         myWriter->Key ("metallicRoughnessTexture");
         myWriter->StartObject();
         {
           myWriter->Key ("index");
-          const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aPbrMat.MetallicRoughnessTexture);
-          if (!anImageIdx.IsEmpty())
-          {
-            myWriter->Int (anImageIdx.IntegerValue());
-          }
+          myWriter->Int (aMetRoughImageIdx);
         }
         myWriter->EndObject();
       }
@@ -405,50 +540,45 @@ void RWGltf_GltfMaterialMap::DefineMaterial (const XCAFPrs_Style& theStyle,
       }
       myWriter->EndArray();
     }
-    if (!aPbrMat.EmissiveTexture.IsNull()
-      && myImageMap.IsBound1 (aPbrMat.EmissiveTexture))
+
+    const Standard_Integer anEmissImageIdx = !aPbrMat.EmissiveTexture.IsNull()
+                                           ? myImageMap.FindIndex (aPbrMat.EmissiveTexture) - 1
+                                           : -1;
+    if (anEmissImageIdx != -1)
     {
       myWriter->Key ("emissiveTexture");
       myWriter->StartObject();
       {
         myWriter->Key ("index");
-        const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aPbrMat.EmissiveTexture);
-        if (!anImageIdx.IsEmpty())
-        {
-          myWriter->Int (anImageIdx.IntegerValue());
-        }
+        myWriter->Int (anEmissImageIdx);
       }
       myWriter->EndObject();
     }
 
-    if (!aPbrMat.NormalTexture.IsNull()
-      && myImageMap.IsBound1 (aPbrMat.NormalTexture))
+    const Standard_Integer aNormImageIdx = !aPbrMat.NormalTexture.IsNull()
+                                         ? myImageMap.FindIndex (aPbrMat.NormalTexture) - 1
+                                         : -1;
+    if (aNormImageIdx != -1)
     {
       myWriter->Key ("normalTexture");
       myWriter->StartObject();
       {
         myWriter->Key ("index");
-        const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aPbrMat.NormalTexture);
-        if (!anImageIdx.IsEmpty())
-        {
-          myWriter->Int (anImageIdx.IntegerValue());
-        }
+        myWriter->Int (aNormImageIdx);
       }
       myWriter->EndObject();
     }
 
-    if (!aPbrMat.OcclusionTexture.IsNull()
-      && myImageMap.IsBound1 (aPbrMat.OcclusionTexture))
+    const Standard_Integer anOcclusImageIdx = !aPbrMat.OcclusionTexture.IsNull()
+                                            ? myImageMap.FindIndex (aPbrMat.OcclusionTexture) - 1
+                                            : -1;
+    if (anOcclusImageIdx != -1)
     {
       myWriter->Key ("occlusionTexture");
       myWriter->StartObject();
       {
         myWriter->Key ("index");
-        const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aPbrMat.OcclusionTexture);
-        if (!anImageIdx.IsEmpty())
-        {
-          myWriter->Int (anImageIdx.IntegerValue());
-        }
+        myWriter->Int (anOcclusImageIdx);
       }
       myWriter->EndObject();
     }
index 815999151feb7a9c99cbb45a3da16bd971d1172e..357d5586b78bf2ef2cc45b678b9701a67230d904 100644 (file)
 #define _RWGltf_GltfMaterialMap_HeaderFile
 
 #include <RWMesh_MaterialMap.hxx>
+#include <RWGltf_GltfBufferView.hxx>
 
 class RWGltf_GltfOStreamWriter;
 
 //! Material manager for exporting into glTF format.
 class RWGltf_GltfMaterialMap : public RWMesh_MaterialMap
 {
+  DEFINE_STANDARD_RTTIEXT(RWGltf_GltfMaterialMap, RWMesh_MaterialMap)
 public:
 
   //! Main constructor.
@@ -30,7 +32,26 @@ public:
   //! Destructor.
   Standard_EXPORT virtual ~RWGltf_GltfMaterialMap();
 
-  //! Add material images.
+public:
+
+  //! Add material images into GLB stream.
+  //! @param theBinFile [in] [out] output file stream
+  //! @param theStyle   [in] material images to add
+  Standard_EXPORT void AddGlbImages (std::ostream& theBinFile,
+                                     const XCAFPrs_Style& theStyle);
+
+  //! Add bufferView's into RWGltf_GltfRootElement_BufferViews section with images collected by AddImagesToGlb().
+  Standard_EXPORT void FlushGlbBufferViews (RWGltf_GltfOStreamWriter* theWriter,
+                                            const Standard_Integer theBinDataBufferId,
+                                            Standard_Integer& theBuffViewId);
+
+  //! Write RWGltf_GltfRootElement_Images section with images collected by AddImagesToGlb().
+  Standard_EXPORT void FlushGlbImages (RWGltf_GltfOStreamWriter* theWriter);
+
+public:
+
+  //! Add material images in case of non-GLB file
+  //! (an alternative to AddImagesToGlb() + FlushBufferViews() + FlushImagesGlb()).
   Standard_EXPORT void AddImages (RWGltf_GltfOStreamWriter* theWriter,
                                   const XCAFPrs_Style& theStyle,
                                   Standard_Boolean& theIsStarted);
@@ -62,6 +83,12 @@ protected:
                                  const Handle(Image_Texture)& theTexture,
                                  Standard_Boolean& theIsStarted);
 
+  //! Add texture image into GLB stream.
+  //! @param theBinFile [in] [out] output file stream
+  //! @param theTexture [in] texture image to add
+  Standard_EXPORT void addGlbImage (std::ostream& theBinFile,
+                                    const Handle(Image_Texture)& theTexture);
+
   //! Add texture.
   Standard_EXPORT void addTexture (RWGltf_GltfOStreamWriter* theWriter,
                                    const Handle(Image_Texture)& theTexture,
@@ -78,10 +105,10 @@ protected:
 protected:
 
   RWGltf_GltfOStreamWriter* myWriter;
-  NCollection_DoubleMap<Handle(Image_Texture), TCollection_AsciiString, Image_Texture, TCollection_AsciiString> myImageMap;
+  NCollection_IndexedDataMap<Handle(Image_Texture), RWGltf_GltfBufferView, Image_Texture> myImageMap;
   NCollection_Map<Handle(Image_Texture), Image_Texture> myTextureMap;
+
   Standard_Integer myDefSamplerId;
-  Standard_Integer myNbImages;
 
 };
 
index fcfb73ff3ed3f4db785c81e5ddbe2cabc9f256e5..1a8f2e93f534b1804d8a55a6a4e563bacbf11683 100644 (file)
@@ -22,6 +22,8 @@
 #include <TDataStd_Name.hxx>
 #include <TDF_Label.hxx>
 
+IMPLEMENT_STANDARD_RTTIEXT(RWMesh_MaterialMap, Standard_Transient)
+
 // =======================================================================
 // function : RWMesh_MaterialMap
 // purpose  :
index 2e9034c20a68e9cc4c3a5ad0fd35a54fcb398eab..13aea517b3251a70095c9b2092ee0ee30ae5d14d 100644 (file)
@@ -21,8 +21,9 @@
 //! Material manager.
 //! Provides an interface for collecting all materials within the document before writing it into file,
 //! and for copying associated image files (textures) into sub-folder near by exported model.
-class RWMesh_MaterialMap
+class RWMesh_MaterialMap : public Standard_Transient
 {
+  DEFINE_STANDARD_RTTIEXT(RWMesh_MaterialMap, Standard_Transient)
 public:
 
   //! Main constructor.
index 3d8b497b4e5ce8a076838c49bf9eef9123c346c0..b35f318b4162b0a56dca0c39bfda2e1e1194368c 100644 (file)
@@ -221,7 +221,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
   Handle(TDocStd_Application) anApp = DDocStd::GetApplication();
   TColStd_IndexedDataMapOfStringString aFileInfo;
   RWGltf_WriterTrsfFormat aTrsfFormat = RWGltf_WriterTrsfFormat_Compact;
-  bool toForceUVExport = false;
+  bool toForceUVExport = false, toEmbedTexturesInGlb = true;
   for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
   {
     TCollection_AsciiString anArgCase (theArgVec[anArgIter]);
@@ -291,6 +291,10 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
     {
       aGltfFilePath = theArgVec[anArgIter];
     }
+    else if (anArgCase == "-texturesSeparate")
+    {
+      toEmbedTexturesInGlb = false;
+    }
     else
     {
       Message::SendFail() << "Syntax error at '" << theArgVec[anArgIter] << "'";
@@ -313,6 +317,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
   RWGltf_CafWriter aWriter (aGltfFilePath, anExt.EndsWith (".glb"));
   aWriter.SetTransformationFormat (aTrsfFormat);
   aWriter.SetForcedUVExport (toForceUVExport);
+  aWriter.SetToEmbedTexturesInGlb (toEmbedTexturesInGlb);
   aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aSystemUnitFactor);
   aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (RWMesh_CoordinateSystem_Zup);
   aWriter.Perform (aDoc, aFileInfo, aProgress->Start());
@@ -1724,10 +1729,11 @@ void  XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands)
                    "\n\t\t: Same as ReadGltf but reads glTF file into a shape instead of a document.",
                    __FILE__, ReadGltf, g);
   theCommands.Add ("WriteGltf",
-                   "WriteGltf Doc file [-trsfFormat {compact|TRS|mat4}=compact] [-comments Text] [-author Name] [-forceUVExport]"
+                   "WriteGltf Doc file [-trsfFormat {compact|TRS|mat4}=compact] [-comments Text] [-author Name] [-forceUVExport] [-texturesSeparate]"
                    "\n\t\t: Write XDE document into glTF file."
                    "\n\t\t:   -trsfFormat preferred transformation format"
-                   "\n\t\t:   -forceUVExport always export UV coordinates",
+                   "\n\t\t:   -forceUVExport always export UV coordinates"
+                   "\n\t\t:   -texturesSeparate write textures to separate files",
                    __FILE__, WriteGltf, g);
   theCommands.Add ("writegltf",
                    "writegltf shape file",
diff --git a/tests/de_mesh/gltf_write/helmetglb b/tests/de_mesh/gltf_write/helmetglb
new file mode 100644 (file)
index 0000000..55c06b8
--- /dev/null
@@ -0,0 +1,20 @@
+puts "========"
+puts "0031703: Data Exchange, RWGltf_CafWriter - add option putting textures inside GLB file as alternative to external references"
+puts "Test case exporting glTF model into GLB file."
+puts "========"
+
+catch { Close D1 }
+ReadGltf D1 [locate_data_file bug30691_DamagedHelmet.gltf]
+
+set aTmpGltfBase "${imagedir}/${casename}_tmp"
+set aTmpGltf "${aTmpGltfBase}.glb"
+lappend occ_tmp_files $aTmpGltf
+lappend occ_tmp_files "${aTmpGltfBase}.bin"
+lappend occ_tmp_files "${aTmpGltfBase}_textures"
+
+WriteGltf D1 "$aTmpGltf"
+
+ReadGltf D "$aTmpGltf"
+XGetOneShape s D
+checknbshapes s -face 1 -compound 0
+checktrinfo s -tri 15452 -nod 14556
diff --git a/tests/de_mesh/gltf_write/lanternglb b/tests/de_mesh/gltf_write/lanternglb
new file mode 100644 (file)
index 0000000..f8650fa
--- /dev/null
@@ -0,0 +1,20 @@
+puts "========"
+puts "0031703: Data Exchange, RWGltf_CafWriter - add option putting textures inside GLB file as alternative to external references"
+puts "Test case exporting glTF model into GLB file."
+puts "========"
+
+catch { Close D1 }
+ReadGltf D1 [locate_data_file bug30691_Lantern.glb]
+
+set aTmpGltfBase "${imagedir}/${casename}_tmp"
+set aTmpGltf "${aTmpGltfBase}.glb"
+lappend occ_tmp_files $aTmpGltf
+lappend occ_tmp_files "${aTmpGltfBase}.bin"
+lappend occ_tmp_files "${aTmpGltfBase}_textures"
+
+WriteGltf D1 "$aTmpGltf"
+
+ReadGltf D "$aTmpGltf"
+XGetOneShape s D
+checknbshapes s -face 3 -compound 1
+checktrinfo s -tri 5394 -nod 4145