0032143: Visualization - add option excluding transparent object from sorting
[occt.git] / src / RWGltf / RWGltf_GltfMaterialMap.cxx
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
25 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 // =======================================================================
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 // =======================================================================
68 RWGltf_GltfMaterialMap::~RWGltf_GltfMaterialMap()
69 {
70   //
71 }
72
73 // =======================================================================
74 // function : AddImages
75 // purpose  :
76 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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 // =======================================================================
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.BaseColor.SetAlpha (theStyle.GetColorSurfRGBA().Alpha());
424     }
425   }
426   myWriter->StartObject();
427   {
428     myWriter->Key ("name");
429     myWriter->String (theName.ToCString());
430
431     myWriter->Key ("pbrMetallicRoughness");
432     myWriter->StartObject();
433     {
434       myWriter->Key ("baseColorFactor");
435       myWriter->StartArray();
436       {
437         myWriter->Double (aPbrMat.BaseColor.GetRGB().Red());
438         myWriter->Double (aPbrMat.BaseColor.GetRGB().Green());
439         myWriter->Double (aPbrMat.BaseColor.GetRGB().Blue());
440         myWriter->Double (aPbrMat.BaseColor.Alpha());
441       }
442       myWriter->EndArray();
443
444       if (const Handle(Image_Texture)& aBaseTexture = baseColorTexture (theStyle.Material()))
445       {
446         const Standard_Integer aBaseImageIdx = myImageMap.FindIndex (aBaseTexture) - 1;
447         if (aBaseImageIdx != -1)
448         {
449           myWriter->Key ("baseColorTexture");
450           myWriter->StartObject();
451           {
452             myWriter->Key ("index");
453             myWriter->Int (aBaseImageIdx);
454           }
455           myWriter->EndObject();
456         }
457       }
458
459       if (hasMaterial
460        || aPbrMat.Metallic != 1.0f)
461       {
462         myWriter->Key ("metallicFactor");
463         myWriter->Double (aPbrMat.Metallic);
464       }
465
466       const Standard_Integer aMetRoughImageIdx = !aPbrMat.MetallicRoughnessTexture.IsNull()
467                                                ? myImageMap.FindIndex (aPbrMat.MetallicRoughnessTexture) - 1
468                                                : -1;
469       if (aMetRoughImageIdx != -1)
470       {
471         myWriter->Key ("metallicRoughnessTexture");
472         myWriter->StartObject();
473         {
474           myWriter->Key ("index");
475           myWriter->Int (aMetRoughImageIdx);
476         }
477         myWriter->EndObject();
478       }
479
480       if (hasMaterial
481        || aPbrMat.Roughness != 1.0f)
482       {
483         myWriter->Key ("roughnessFactor");
484         myWriter->Double (aPbrMat.Roughness);
485       }
486     }
487     myWriter->EndObject();
488
489     if (theStyle.Material().IsNull()
490      || theStyle.Material()->IsDoubleSided())
491     {
492       myWriter->Key ("doubleSided");
493       myWriter->Bool (true);
494     }
495
496     const Graphic3d_AlphaMode anAlphaMode = !theStyle.Material().IsNull() ? theStyle.Material()->AlphaMode() : Graphic3d_AlphaMode_BlendAuto;
497     switch (anAlphaMode)
498     {
499       case Graphic3d_AlphaMode_BlendAuto:
500       {
501         if (aPbrMat.BaseColor.Alpha() < 1.0f)
502         {
503           myWriter->Key ("alphaMode");
504           myWriter->String ("BLEND");
505         }
506         break;
507       }
508       case Graphic3d_AlphaMode_Opaque:
509       {
510         break;
511       }
512       case Graphic3d_AlphaMode_Mask:
513       {
514         myWriter->Key ("alphaMode");
515         myWriter->String ("MASK");
516         break;
517       }
518       case Graphic3d_AlphaMode_Blend:
519       case Graphic3d_AlphaMode_MaskBlend:
520       {
521         myWriter->Key ("alphaMode");
522         myWriter->String ("BLEND");
523         break;
524       }
525     }
526     if (!theStyle.Material().IsNull()
527       && theStyle.Material()->AlphaCutOff() != 0.5f)
528     {
529       myWriter->Key ("alphaCutoff");
530       myWriter->Double (theStyle.Material()->AlphaCutOff());
531     }
532
533     if (aPbrMat.EmissiveFactor != Graphic3d_Vec3 (0.0f, 0.0f, 0.0f))
534     {
535       myWriter->Key ("emissiveFactor");
536       myWriter->StartArray();
537       {
538         myWriter->Double (aPbrMat.EmissiveFactor.r());
539         myWriter->Double (aPbrMat.EmissiveFactor.g());
540         myWriter->Double (aPbrMat.EmissiveFactor.b());
541       }
542       myWriter->EndArray();
543     }
544
545     const Standard_Integer anEmissImageIdx = !aPbrMat.EmissiveTexture.IsNull()
546                                            ? myImageMap.FindIndex (aPbrMat.EmissiveTexture) - 1
547                                            : -1;
548     if (anEmissImageIdx != -1)
549     {
550       myWriter->Key ("emissiveTexture");
551       myWriter->StartObject();
552       {
553         myWriter->Key ("index");
554         myWriter->Int (anEmissImageIdx);
555       }
556       myWriter->EndObject();
557     }
558
559     const Standard_Integer aNormImageIdx = !aPbrMat.NormalTexture.IsNull()
560                                          ? myImageMap.FindIndex (aPbrMat.NormalTexture) - 1
561                                          : -1;
562     if (aNormImageIdx != -1)
563     {
564       myWriter->Key ("normalTexture");
565       myWriter->StartObject();
566       {
567         myWriter->Key ("index");
568         myWriter->Int (aNormImageIdx);
569       }
570       myWriter->EndObject();
571     }
572
573     const Standard_Integer anOcclusImageIdx = !aPbrMat.OcclusionTexture.IsNull()
574                                             ? myImageMap.FindIndex (aPbrMat.OcclusionTexture) - 1
575                                             : -1;
576     if (anOcclusImageIdx != -1)
577     {
578       myWriter->Key ("occlusionTexture");
579       myWriter->StartObject();
580       {
581         myWriter->Key ("index");
582         myWriter->Int (anOcclusImageIdx);
583       }
584       myWriter->EndObject();
585     }
586   }
587   myWriter->EndObject();
588 #else
589   (void )theStyle;
590   (void )theName;
591 #endif
592 }