1 // Copyright (c) 2017-2019 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <RWGltf_GltfMaterialMap.hxx>
15
16 #include <Message.hxx>
17 #include <NCollection_Array1.hxx>
18 #include <OSD_OpenFile.hxx>
19 #include <RWGltf_GltfRootElement.hxx>
20
21 #ifdef HAVE_RAPIDJSON
22 #include <RWGltf_GltfOStreamWriter.hxx>
23 #endif
24
IMPLEMENT_STANDARD_RTTIEXT(RWGltf_GltfMaterialMap,RWMesh_MaterialMap) const25 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_GltfMaterialMap, RWMesh_MaterialMap)
26
27 // =======================================================================
28 // function : baseColorTexture
29 // purpose :
30 // =======================================================================
31 const Handle(Image_Texture)& RWGltf_GltfMaterialMap::baseColorTexture (const Handle(XCAFDoc_VisMaterial)& theMat)
32 {
33 static const Handle(Image_Texture) THE_NULL_TEXTURE;
34 if (theMat.IsNull())
35 {
36 return THE_NULL_TEXTURE;
37 }
38 else if (theMat->HasPbrMaterial()
39 && !theMat->PbrMaterial().BaseColorTexture.IsNull())
40 {
41 return theMat->PbrMaterial().BaseColorTexture;
42 }
43 else if (theMat->HasCommonMaterial()
44 && !theMat->CommonMaterial().DiffuseTexture.IsNull())
45 {
46 return theMat->CommonMaterial().DiffuseTexture;
47 }
48 return THE_NULL_TEXTURE;
49 }
50
51 // =======================================================================
52 // function : RWGltf_GltfMaterialMap
53 // purpose :
54 // =======================================================================
RWGltf_GltfMaterialMap(const TCollection_AsciiString & theFile,const Standard_Integer theDefSamplerId)55 RWGltf_GltfMaterialMap::RWGltf_GltfMaterialMap (const TCollection_AsciiString& theFile,
56 const Standard_Integer theDefSamplerId)
57 : RWMesh_MaterialMap (theFile),
58 myWriter (NULL),
59 myDefSamplerId (theDefSamplerId)
60 {
61 myMatNameAsKey = false;
62 }
63
64 // =======================================================================
65 // function : ~RWGltf_GltfMaterialMap
66 // purpose :
67 // =======================================================================
~RWGltf_GltfMaterialMap()68 RWGltf_GltfMaterialMap::~RWGltf_GltfMaterialMap()
69 {
70 //
71 }
72
73 // =======================================================================
74 // function : AddImages
75 // purpose :
76 // =======================================================================
AddImages(RWGltf_GltfOStreamWriter * theWriter,const XCAFPrs_Style & theStyle,Standard_Boolean & theIsStarted)77 void RWGltf_GltfMaterialMap::AddImages (RWGltf_GltfOStreamWriter* theWriter,
78 const XCAFPrs_Style& theStyle,
79 Standard_Boolean& theIsStarted)
80 {
81 if (theWriter == NULL
82 || theStyle.Material().IsNull()
83 || theStyle.Material()->IsEmpty())
84 {
85 return;
86 }
87
88 addImage (theWriter, baseColorTexture (theStyle.Material()), theIsStarted);
89 addImage (theWriter, theStyle.Material()->PbrMaterial().MetallicRoughnessTexture, theIsStarted);
90 addImage (theWriter, theStyle.Material()->PbrMaterial().NormalTexture, theIsStarted);
91 addImage (theWriter, theStyle.Material()->PbrMaterial().EmissiveTexture, theIsStarted);
92 addImage (theWriter, theStyle.Material()->PbrMaterial().OcclusionTexture, theIsStarted);
93 }
94
95 // =======================================================================
96 // function : AddGlbImages
97 // purpose :
98 // =======================================================================
AddGlbImages(std::ostream & theBinFile,const XCAFPrs_Style & theStyle)99 void RWGltf_GltfMaterialMap::AddGlbImages (std::ostream& theBinFile,
100 const XCAFPrs_Style& theStyle)
101 {
102 if (theStyle.Material().IsNull()
103 || theStyle.Material()->IsEmpty())
104 {
105 return;
106 }
107
108 addGlbImage (theBinFile, baseColorTexture (theStyle.Material()));
109 addGlbImage (theBinFile, theStyle.Material()->PbrMaterial().MetallicRoughnessTexture);
110 addGlbImage (theBinFile, theStyle.Material()->PbrMaterial().NormalTexture);
111 addGlbImage (theBinFile, theStyle.Material()->PbrMaterial().EmissiveTexture);
112 addGlbImage (theBinFile, theStyle.Material()->PbrMaterial().OcclusionTexture);
113 }
114
115 // =======================================================================
116 // function : addImage
117 // purpose :
118 // =======================================================================
addImage(RWGltf_GltfOStreamWriter * theWriter,const Handle (Image_Texture)& theTexture,Standard_Boolean & theIsStarted)119 void RWGltf_GltfMaterialMap::addImage (RWGltf_GltfOStreamWriter* theWriter,
120 const Handle(Image_Texture)& theTexture,
121 Standard_Boolean& theIsStarted)
122 {
123 #ifdef HAVE_RAPIDJSON
124 if (theTexture.IsNull()
125 || myImageMap.Contains (theTexture)
126 || myImageFailMap.Contains (theTexture))
127 {
128 return;
129 }
130
131 const TCollection_AsciiString aGltfImgKey = myImageMap.Extent();
132 TCollection_AsciiString aTextureUri;
133 if (!CopyTexture (aTextureUri, theTexture, aGltfImgKey))
134 {
135 myImageFailMap.Add (theTexture);
136 return;
137 }
138 myImageMap.Add (theTexture, RWGltf_GltfBufferView());
139
140 if (!theIsStarted)
141 {
142 theWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Images));
143 theWriter->StartArray();
144 theIsStarted = true;
145 }
146
147 theWriter->StartObject();
148 {
149 theWriter->Key ("uri");
150 theWriter->String (aTextureUri.ToCString());
151 }
152 theWriter->EndObject();
153 #else
154 (void )theWriter;
155 (void )theTexture;
156 (void )theIsStarted;
157 #endif
158 }
159
160 // =======================================================================
161 // function : addGlbImage
162 // purpose :
163 // =======================================================================
addGlbImage(std::ostream & theBinFile,const Handle (Image_Texture)& theTexture)164 void RWGltf_GltfMaterialMap::addGlbImage (std::ostream& theBinFile,
165 const Handle(Image_Texture)& theTexture)
166 {
167 if (theTexture.IsNull()
168 || myImageMap.Contains (theTexture)
169 || myImageFailMap.Contains (theTexture))
170 {
171 return;
172 }
173
174 RWGltf_GltfBufferView aBuffImage;
175 aBuffImage.ByteOffset = theBinFile.tellp();
176 if (!theTexture->WriteImage (theBinFile, myFileName))
177 {
178 myImageFailMap.Add (theTexture);
179 return;
180 }
181
182 // alignment by 4 bytes
183 int64_t aContentLen64 = (int64_t)theBinFile.tellp();
184 while (aContentLen64 % 4 != 0)
185 {
186 theBinFile.write (" ", 1);
187 ++aContentLen64;
188 }
189
190 //aBuffImage.Id = myBuffViewImages.Size(); // id will be corrected later
191 aBuffImage.ByteLength = (int64_t)theBinFile.tellp() - aBuffImage.ByteOffset;
192 if (aBuffImage.ByteLength <= 0)
193 {
194 myImageFailMap.Add (theTexture);
195 return;
196 }
197
198 myImageMap.Add (theTexture, aBuffImage);
199 }
200
201 // =======================================================================
202 // function : FlushBufferViews
203 // purpose :
204 // =======================================================================
FlushGlbBufferViews(RWGltf_GltfOStreamWriter * theWriter,const Standard_Integer theBinDataBufferId,Standard_Integer & theBuffViewId)205 void RWGltf_GltfMaterialMap::FlushGlbBufferViews (RWGltf_GltfOStreamWriter* theWriter,
206 const Standard_Integer theBinDataBufferId,
207 Standard_Integer& theBuffViewId)
208 {
209 #ifdef HAVE_RAPIDJSON
210 for (NCollection_IndexedDataMap<Handle(Image_Texture), RWGltf_GltfBufferView, Image_Texture>::Iterator aBufViewIter (myImageMap);
211 aBufViewIter.More(); aBufViewIter.Next())
212 {
213 RWGltf_GltfBufferView& aBuffView = aBufViewIter.ChangeValue();
214 if (aBuffView.ByteLength <= 0)
215 {
216 continue;
217 }
218
219 aBuffView.Id = theBuffViewId++;
220 theWriter->StartObject();
221 theWriter->Key ("buffer");
222 theWriter->Int (theBinDataBufferId);
223 theWriter->Key ("byteLength");
224 theWriter->Int64 (aBuffView.ByteLength);
225 theWriter->Key ("byteOffset");
226 theWriter->Int64 (aBuffView.ByteOffset);
227 theWriter->EndObject();
228 }
229 #else
230 (void )theWriter;
231 (void )theBinDataBufferId;
232 (void )theBuffViewId;
233 #endif
234 }
235
236 // =======================================================================
237 // function : FlushGlbImages
238 // purpose :
239 // =======================================================================
FlushGlbImages(RWGltf_GltfOStreamWriter * theWriter)240 void RWGltf_GltfMaterialMap::FlushGlbImages (RWGltf_GltfOStreamWriter* theWriter)
241 {
242 #ifdef HAVE_RAPIDJSON
243 bool isStarted = false;
244 for (NCollection_IndexedDataMap<Handle(Image_Texture), RWGltf_GltfBufferView, Image_Texture>::Iterator aBufViewIter (myImageMap);
245 aBufViewIter.More(); aBufViewIter.Next())
246 {
247 const Handle(Image_Texture)& aTexture = aBufViewIter.Key();
248 const RWGltf_GltfBufferView& aBuffView = aBufViewIter.Value();
249 if (aBuffView.ByteLength <= 0)
250 {
251 continue;
252 }
253
254 if (!isStarted)
255 {
256 theWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Images));
257 theWriter->StartArray();
258 isStarted = true;
259 }
260
261 theWriter->StartObject();
262 {
263 const TCollection_AsciiString anImageFormat = aTexture->MimeType();
264 if (anImageFormat != "image/png"
265 && anImageFormat != "image/jpeg")
266 {
267 Message::SendWarning (TCollection_AsciiString ("Warning! Non-standard mime-type ")
268 + anImageFormat + " (texture " + aTexture->TextureId()
269 + ") within glTF file");
270 }
271 theWriter->Key ("mimeType");
272 theWriter->String (anImageFormat.ToCString());
273 theWriter->Key ("bufferView");
274 theWriter->Int (aBuffView.Id);
275 }
276 theWriter->EndObject();
277 }
278 if (isStarted)
279 {
280 theWriter->EndArray();
281 }
282 #else
283 (void )theWriter;
284 #endif
285 }
286
287 // =======================================================================
288 // function : AddMaterial
289 // purpose :
290 // =======================================================================
AddMaterial(RWGltf_GltfOStreamWriter * theWriter,const XCAFPrs_Style & theStyle,Standard_Boolean & theIsStarted)291 void RWGltf_GltfMaterialMap::AddMaterial (RWGltf_GltfOStreamWriter* theWriter,
292 const XCAFPrs_Style& theStyle,
293 Standard_Boolean& theIsStarted)
294 {
295 #ifdef HAVE_RAPIDJSON
296 if (theWriter == NULL
297 || ((theStyle.Material().IsNull() || theStyle.Material()->IsEmpty())
298 && !theStyle.IsSetColorSurf()))
299 {
300 return;
301 }
302
303 if (!theIsStarted)
304 {
305 theWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Materials));
306 theWriter->StartArray();
307 theIsStarted = true;
308 }
309 myWriter = theWriter;
310 AddMaterial (theStyle);
311 myWriter = NULL;
312 #else
313 (void )theWriter;
314 (void )theStyle;
315 (void )theIsStarted;
316 #endif
317 }
318
319 // =======================================================================
320 // function : AddTextures
321 // purpose :
322 // =======================================================================
AddTextures(RWGltf_GltfOStreamWriter * theWriter,const XCAFPrs_Style & theStyle,Standard_Boolean & theIsStarted)323 void RWGltf_GltfMaterialMap::AddTextures (RWGltf_GltfOStreamWriter* theWriter,
324 const XCAFPrs_Style& theStyle,
325 Standard_Boolean& theIsStarted)
326 {
327 if (theWriter == NULL
328 || theStyle.Material().IsNull()
329 || theStyle.Material()->IsEmpty())
330 {
331 return;
332 }
333
334 addTexture (theWriter, baseColorTexture (theStyle.Material()), theIsStarted);
335 addTexture (theWriter, theStyle.Material()->PbrMaterial().MetallicRoughnessTexture, theIsStarted);
336 addTexture (theWriter, theStyle.Material()->PbrMaterial().NormalTexture, theIsStarted);
337 addTexture (theWriter, theStyle.Material()->PbrMaterial().EmissiveTexture, theIsStarted);
338 addTexture (theWriter, theStyle.Material()->PbrMaterial().OcclusionTexture, theIsStarted);
339 }
340
341 // =======================================================================
342 // function : addTexture
343 // purpose :
344 // =======================================================================
addTexture(RWGltf_GltfOStreamWriter * theWriter,const Handle (Image_Texture)& theTexture,Standard_Boolean & theIsStarted)345 void RWGltf_GltfMaterialMap::addTexture (RWGltf_GltfOStreamWriter* theWriter,
346 const Handle(Image_Texture)& theTexture,
347 Standard_Boolean& theIsStarted)
348 {
349 #ifdef HAVE_RAPIDJSON
350 if (theTexture.IsNull()
351 || myTextureMap.Contains (theTexture)
352 || !myImageMap .Contains (theTexture))
353 {
354 return;
355 }
356
357 const Standard_Integer anImgKey = myImageMap.FindIndex (theTexture) - 1; // glTF indexation starts from 0
358 myTextureMap.Add (theTexture);
359
360 if (!theIsStarted)
361 {
362 theWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Textures));
363 theWriter->StartArray();
364 theIsStarted = true;
365 }
366
367 theWriter->StartObject();
368 {
369 theWriter->Key ("sampler");
370 theWriter->Int (myDefSamplerId); // mandatory field by specs
371 theWriter->Key ("source");
372 theWriter->Int (anImgKey);
373 }
374 theWriter->EndObject();
375 #else
376 (void )theWriter;
377 (void )theTexture;
378 (void )theIsStarted;
379 #endif
380 }
381
382 // =======================================================================
383 // function : AddMaterial
384 // purpose :
385 // =======================================================================
AddMaterial(const XCAFPrs_Style & theStyle)386 TCollection_AsciiString RWGltf_GltfMaterialMap::AddMaterial (const XCAFPrs_Style& theStyle)
387 {
388 return RWMesh_MaterialMap::AddMaterial (theStyle);
389 }
390
391 // =======================================================================
392 // function : DefineMaterial
393 // purpose :
394 // =======================================================================
DefineMaterial(const XCAFPrs_Style & theStyle,const TCollection_AsciiString &,const TCollection_AsciiString & theName)395 void RWGltf_GltfMaterialMap::DefineMaterial (const XCAFPrs_Style& theStyle,
396 const TCollection_AsciiString& /*theKey*/,
397 const TCollection_AsciiString& theName)
398 {
399 #ifdef HAVE_RAPIDJSON
400 if (myWriter == NULL)
401 {
402 Standard_ProgramError::Raise ("RWGltf_GltfMaterialMap::DefineMaterial() should be called with JSON Writer");
403 return;
404 }
405
406 XCAFDoc_VisMaterialPBR aPbrMat;
407 const bool hasMaterial = !theStyle.Material().IsNull()
408 && !theStyle.Material()->IsEmpty();
409 if (hasMaterial)
410 {
411 aPbrMat = theStyle.Material()->ConvertToPbrMaterial();
412 }
413 else if (!myDefaultStyle.Material().IsNull()
414 && myDefaultStyle.Material()->HasPbrMaterial())
415 {
416 aPbrMat = myDefaultStyle.Material()->PbrMaterial();
417 }
418 if (theStyle.IsSetColorSurf())
419 {
420 aPbrMat.BaseColor.SetRGB (theStyle.GetColorSurf());
421 if (theStyle.GetColorSurfRGBA().Alpha() < 1.0f)
422 {
423 aPbrMat.Metallic = 0.0f;
424 aPbrMat.BaseColor.SetAlpha (theStyle.GetColorSurfRGBA().Alpha());
425 }
426 }
427 myWriter->StartObject();
428 {
429 myWriter->Key ("name");
430 myWriter->String (theName.ToCString());
431
432 myWriter->Key ("pbrMetallicRoughness");
433 myWriter->StartObject();
434 {
435 myWriter->Key ("baseColorFactor");
436 myWriter->StartArray();
437 {
438 myWriter->Double (aPbrMat.BaseColor.GetRGB().Red());
439 myWriter->Double (aPbrMat.BaseColor.GetRGB().Green());
440 myWriter->Double (aPbrMat.BaseColor.GetRGB().Blue());
441 myWriter->Double (aPbrMat.BaseColor.Alpha());
442 }
443 myWriter->EndArray();
444
445 if (const Handle(Image_Texture)& aBaseTexture = baseColorTexture (theStyle.Material()))
446 {
447 const Standard_Integer aBaseImageIdx = myImageMap.FindIndex (aBaseTexture) - 1;
448 if (aBaseImageIdx != -1)
449 {
450 myWriter->Key ("baseColorTexture");
451 myWriter->StartObject();
452 {
453 myWriter->Key ("index");
454 myWriter->Int (aBaseImageIdx);
455 }
456 myWriter->EndObject();
457 }
458 }
459
460 if (hasMaterial
461 || aPbrMat.Metallic != 1.0f)
462 {
463 myWriter->Key ("metallicFactor");
464 myWriter->Double (aPbrMat.Metallic);
465 }
466
467 const Standard_Integer aMetRoughImageIdx = !aPbrMat.MetallicRoughnessTexture.IsNull()
468 ? myImageMap.FindIndex (aPbrMat.MetallicRoughnessTexture) - 1
469 : -1;
470 if (aMetRoughImageIdx != -1)
471 {
472 myWriter->Key ("metallicRoughnessTexture");
473 myWriter->StartObject();
474 {
475 myWriter->Key ("index");
476 myWriter->Int (aMetRoughImageIdx);
477 }
478 myWriter->EndObject();
479 }
480
481 if (hasMaterial
482 || aPbrMat.Roughness != 1.0f)
483 {
484 myWriter->Key ("roughnessFactor");
485 myWriter->Double (aPbrMat.Roughness);
486 }
487 }
488 myWriter->EndObject();
489
490 // export automatic culling as doubleSided material;
491 // extra preprocessing is necessary to automatically export
492 // Solids with singleSided material and Shells with doubleSided material,
493 // as both may share the same material having "auto" flag
494 if (theStyle.Material().IsNull()
495 || theStyle.Material()->FaceCulling() == Graphic3d_TypeOfBackfacingModel_Auto
496 || theStyle.Material()->FaceCulling() == Graphic3d_TypeOfBackfacingModel_DoubleSided)
497 {
498 myWriter->Key ("doubleSided");
499 myWriter->Bool (true);
500 }
501
502 const Graphic3d_AlphaMode anAlphaMode = !theStyle.Material().IsNull() ? theStyle.Material()->AlphaMode() : Graphic3d_AlphaMode_BlendAuto;
503 switch (anAlphaMode)
504 {
505 case Graphic3d_AlphaMode_BlendAuto:
506 {
507 if (aPbrMat.BaseColor.Alpha() < 1.0f)
508 {
509 myWriter->Key ("alphaMode");
510 myWriter->String ("BLEND");
511 }
512 break;
513 }
514 case Graphic3d_AlphaMode_Opaque:
515 {
516 break;
517 }
518 case Graphic3d_AlphaMode_Mask:
519 {
520 myWriter->Key ("alphaMode");
521 myWriter->String ("MASK");
522 break;
523 }
524 case Graphic3d_AlphaMode_Blend:
525 case Graphic3d_AlphaMode_MaskBlend:
526 {
527 myWriter->Key ("alphaMode");
528 myWriter->String ("BLEND");
529 break;
530 }
531 }
532 if (!theStyle.Material().IsNull()
533 && theStyle.Material()->AlphaCutOff() != 0.5f)
534 {
535 myWriter->Key ("alphaCutoff");
536 myWriter->Double (theStyle.Material()->AlphaCutOff());
537 }
538
539 if (aPbrMat.EmissiveFactor != Graphic3d_Vec3 (0.0f, 0.0f, 0.0f))
540 {
541 myWriter->Key ("emissiveFactor");
542 myWriter->StartArray();
543 {
544 myWriter->Double (aPbrMat.EmissiveFactor.r());
545 myWriter->Double (aPbrMat.EmissiveFactor.g());
546 myWriter->Double (aPbrMat.EmissiveFactor.b());
547 }
548 myWriter->EndArray();
549 }
550
551 const Standard_Integer anEmissImageIdx = !aPbrMat.EmissiveTexture.IsNull()
552 ? myImageMap.FindIndex (aPbrMat.EmissiveTexture) - 1
553 : -1;
554 if (anEmissImageIdx != -1)
555 {
556 myWriter->Key ("emissiveTexture");
557 myWriter->StartObject();
558 {
559 myWriter->Key ("index");
560 myWriter->Int (anEmissImageIdx);
561 }
562 myWriter->EndObject();
563 }
564
565 const Standard_Integer aNormImageIdx = !aPbrMat.NormalTexture.IsNull()
566 ? myImageMap.FindIndex (aPbrMat.NormalTexture) - 1
567 : -1;
568 if (aNormImageIdx != -1)
569 {
570 myWriter->Key ("normalTexture");
571 myWriter->StartObject();
572 {
573 myWriter->Key ("index");
574 myWriter->Int (aNormImageIdx);
575 }
576 myWriter->EndObject();
577 }
578
579 const Standard_Integer anOcclusImageIdx = !aPbrMat.OcclusionTexture.IsNull()
580 ? myImageMap.FindIndex (aPbrMat.OcclusionTexture) - 1
581 : -1;
582 if (anOcclusImageIdx != -1)
583 {
584 myWriter->Key ("occlusionTexture");
585 myWriter->StartObject();
586 {
587 myWriter->Key ("index");
588 myWriter->Int (anOcclusImageIdx);
589 }
590 myWriter->EndObject();
591 }
592 }
593 myWriter->EndObject();
594 #else
595 (void )theStyle;
596 (void )theName;
597 #endif
598 }
599