1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-24 13:50:49 +03:00

0031703: Data Exchange, RWGltf_CafWriter - add option putting textures inside GLB file as alternative to external references

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.
This commit is contained in:
mkrylova
2020-11-06 11:33:58 +03:00
committed by bugmaster
parent a2cb8561eb
commit 6eb502b27b
12 changed files with 477 additions and 151 deletions

View 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)
{
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 ("File '") + theFile + "' cannot be written");
return false;
}
return true;
}
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 Standard_False;
return false;
}
Standard_Size aLen = (Standard_Size )myLength;
int64_t aLen = myLength;
if (myOffset >= 0)
{
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;
return false;
}
}
else
{
aFileIn.seekg (0, std::ios_base::end);
aLen = (Standard_Size )aFileIn.tellg();
aLen = (int64_t )aFileIn.tellg();
aFileIn.seekg (0, std::ios_base::beg);
}
aBuffer = new NCollection_Buffer (NCollection_BaseAllocator::CommonBaseAllocator(), aLen);
if (!aFileIn.read ((char* )aBuffer->ChangeData(), aBuffer->Size()))
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)
{
aChunkSize = Standard_Integer(aLen - aChunkIter);
}
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)
if (!theStream.good())
{
Message::SendFail (TCollection_AsciiString ("Error: Unable to create file '") + theFile + "'");
return Standard_False;
Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' can not be written");
return false;
}
aFileOut.write ((const char* )aBuffer->Data(), aBuffer->Size());
aFileOut.close();
if (!aFileOut.good())
{
Message::SendFail (TCollection_AsciiString ("Error: Unable to write file '") + theFile + "'");
return Standard_False;
}
return Standard_True;
return true;
}
//=======================================================================

View 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.

View 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
if (myIsBinary
&& myToEmbedTexturesInGlb)
{
myMaterialMap->FlushGlbImages (myWriter.get());
}
else
{
bool anIsStarted = false;
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
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())
{
theMaterialMap.AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
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
}

View 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

View 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;

View File

@@ -13,12 +13,17 @@
#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();
}

View File

@@ -15,12 +15,14 @@
#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;
};

View File

@@ -22,6 +22,8 @@
#include <TDataStd_Name.hxx>
#include <TDF_Label.hxx>
IMPLEMENT_STANDARD_RTTIEXT(RWMesh_MaterialMap, Standard_Transient)
// =======================================================================
// function : RWMesh_MaterialMap
// purpose :

View 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.

View 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",

View File

@@ -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

View File

@@ -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