Overte C++ Documentation
Material.h
1 //
2 // Material.h
3 // libraries/graphics/src/graphics
4 //
5 // Created by Sam Gateau on 12/10/2014.
6 // Copyright 2014 High Fidelity, Inc.
7 // Copyright 2024 Overte e.V.
8 //
9 // Distributed under the Apache License, Version 2.0.
10 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
11 //
12 #ifndef hifi_model_Material_h
13 #define hifi_model_Material_h
14 
15 #include <mutex>
16 #include <bitset>
17 #include <map>
18 #include <unordered_map>
19 #include <queue>
20 
21 #include <ColorUtils.h>
22 
23 #include <gpu/Resource.h>
24 #include <gpu/TextureTable.h>
25 
26 #include "MaterialMappingMode.h"
27 
28 class Transform;
29 
30 namespace graphics {
31 
32 class TextureMap;
33 typedef std::shared_ptr< TextureMap > TextureMapPointer;
34 
35 // Material Key is a coarse trait description of a material used to classify the materials
36 class MaterialKey {
37 public:
38  // Be careful changing these, they need to match up with the bits in graphics/Material.slh
39  enum FlagBit {
40  EMISSIVE_VAL_BIT = 0,
41  UNLIT_VAL_BIT,
42  ALBEDO_VAL_BIT,
43  METALLIC_VAL_BIT,
44  GLOSSY_VAL_BIT,
45  OPACITY_VAL_BIT,
46  OPACITY_MASK_MAP_BIT, // Opacity Map and Opacity MASK map are mutually exclusive
47  OPACITY_TRANSLUCENT_MAP_BIT,
48  OPACITY_MAP_MODE_BIT, // Opacity map mode bit is set if the value has set explicitely and not deduced from the textures assigned
49  OPACITY_CUTOFF_VAL_BIT,
50  SCATTERING_VAL_BIT,
51 
52  // THe map bits must be in the same sequence as the enum names for the map channels
53  EMISSIVE_MAP_BIT,
54  ALBEDO_MAP_BIT,
55  METALLIC_MAP_BIT,
56  ROUGHNESS_MAP_BIT,
57  NORMAL_MAP_BIT,
58  OCCLUSION_MAP_BIT,
59  LIGHT_MAP_BIT,
60  SCATTERING_MAP_BIT,
61 
62  EXTRA_1_BIT,
63  EXTRA_2_BIT,
64  EXTRA_3_BIT,
65  EXTRA_4_BIT,
66  EXTRA_5_BIT,
67 
68  NUM_FLAGS,
69  };
70  typedef std::bitset<NUM_FLAGS> Flags;
71 
72  enum MapChannel {
73  EMISSIVE_MAP = 0,
74  ALBEDO_MAP,
75  METALLIC_MAP,
76  ROUGHNESS_MAP,
77  NORMAL_MAP,
78  OCCLUSION_MAP,
79  LIGHT_MAP,
80  SCATTERING_MAP,
81 
82  NUM_MAP_CHANNELS,
83  };
84 
85  enum OpacityMapMode {
86  OPACITY_MAP_OPAQUE = 0,
87  OPACITY_MAP_MASK,
88  OPACITY_MAP_BLEND,
89  };
90  static std::string getOpacityMapModeName(OpacityMapMode mode);
91  // find the enum value from a string, return true if match found
92  static bool getOpacityMapModeFromName(const std::string& modeName, OpacityMapMode& mode);
93 
94  enum CullFaceMode {
95  CULL_NONE = 0,
96  CULL_FRONT,
97  CULL_BACK,
98 
99  NUM_CULL_FACE_MODES
100  };
101  static std::string getCullFaceModeName(CullFaceMode mode);
102  static bool getCullFaceModeFromName(const std::string& modeName, CullFaceMode& mode);
103 
104  // The signature is the Flags
105  Flags _flags;
106 
107  MaterialKey() : _flags(0) {}
108  MaterialKey(const Flags& flags) : _flags(flags) {}
109 
110  class Builder {
111  Flags _flags{ 0 };
112  public:
113  Builder() {}
114 
115  MaterialKey build() const { return MaterialKey(_flags); }
116 
117  Builder& withEmissive() { _flags.set(EMISSIVE_VAL_BIT); return (*this); }
118  Builder& withUnlit() { _flags.set(UNLIT_VAL_BIT); return (*this); }
119 
120  Builder& withAlbedo() { _flags.set(ALBEDO_VAL_BIT); return (*this); }
121  Builder& withMetallic() { _flags.set(METALLIC_VAL_BIT); return (*this); }
122  Builder& withGlossy() { _flags.set(GLOSSY_VAL_BIT); return (*this); }
123 
124  Builder& withTranslucentFactor() { _flags.set(OPACITY_VAL_BIT); return (*this); }
125  Builder& withTranslucentMap() { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
126  Builder& withMaskMap() { _flags.set(OPACITY_MASK_MAP_BIT); return (*this); }
127  Builder& withOpacityMapMode(OpacityMapMode mode) {
128  switch (mode) {
129  case OPACITY_MAP_OPAQUE:
130  _flags.reset(OPACITY_TRANSLUCENT_MAP_BIT);
131  _flags.reset(OPACITY_MASK_MAP_BIT);
132  break;
133  case OPACITY_MAP_MASK:
134  _flags.reset(OPACITY_TRANSLUCENT_MAP_BIT);
135  _flags.set(OPACITY_MASK_MAP_BIT);
136  break;
137  case OPACITY_MAP_BLEND:
138  _flags.set(OPACITY_TRANSLUCENT_MAP_BIT);
139  _flags.reset(OPACITY_MASK_MAP_BIT);
140  break;
141  };
142  _flags.set(OPACITY_MAP_MODE_BIT); // Intentionally set the mode!
143  return (*this);
144  }
145  Builder& withOpacityCutoff() { _flags.set(OPACITY_CUTOFF_VAL_BIT); return (*this); }
146 
147  Builder& withScattering() { _flags.set(SCATTERING_VAL_BIT); return (*this); }
148 
149  Builder& withEmissiveMap() { _flags.set(EMISSIVE_MAP_BIT); return (*this); }
150  Builder& withAlbedoMap() { _flags.set(ALBEDO_MAP_BIT); return (*this); }
151  Builder& withMetallicMap() { _flags.set(METALLIC_MAP_BIT); return (*this); }
152  Builder& withRoughnessMap() { _flags.set(ROUGHNESS_MAP_BIT); return (*this); }
153 
154  Builder& withNormalMap() { _flags.set(NORMAL_MAP_BIT); return (*this); }
155  Builder& withOcclusionMap() { _flags.set(OCCLUSION_MAP_BIT); return (*this); }
156  Builder& withLightMap() { _flags.set(LIGHT_MAP_BIT); return (*this); }
157  Builder& withScatteringMap() { _flags.set(SCATTERING_MAP_BIT); return (*this); }
158 
159  // Convenient standard keys that we will keep on using all over the place
160  static MaterialKey opaqueAlbedo() { return Builder().withAlbedo().build(); }
161  };
162 
163  void setEmissive(bool value) { _flags.set(EMISSIVE_VAL_BIT, value); }
164  bool isEmissive() const { return _flags[EMISSIVE_VAL_BIT]; }
165 
166  void setUnlit(bool value) { _flags.set(UNLIT_VAL_BIT, value); }
167  bool isUnlit() const { return _flags[UNLIT_VAL_BIT]; }
168 
169  void setEmissiveMap(bool value) { _flags.set(EMISSIVE_MAP_BIT, value); }
170  bool isEmissiveMap() const { return _flags[EMISSIVE_MAP_BIT]; }
171 
172  void setAlbedo(bool value) { _flags.set(ALBEDO_VAL_BIT, value); }
173  bool isAlbedo() const { return _flags[ALBEDO_VAL_BIT]; }
174 
175  void setAlbedoMap(bool value) { _flags.set(ALBEDO_MAP_BIT, value); }
176  bool isAlbedoMap() const { return _flags[ALBEDO_MAP_BIT]; }
177 
178  void setMetallic(bool value) { _flags.set(METALLIC_VAL_BIT, value); }
179  bool isMetallic() const { return _flags[METALLIC_VAL_BIT]; }
180 
181  void setMetallicMap(bool value) { _flags.set(METALLIC_MAP_BIT, value); }
182  bool isMetallicMap() const { return _flags[METALLIC_MAP_BIT]; }
183 
184  void setGlossy(bool value) { _flags.set(GLOSSY_VAL_BIT, value); }
185  bool isGlossy() const { return _flags[GLOSSY_VAL_BIT]; }
186  bool isRough() const { return !_flags[GLOSSY_VAL_BIT]; }
187 
188  void setRoughnessMap(bool value) { _flags.set(ROUGHNESS_MAP_BIT, value); }
189  bool isRoughnessMap() const { return _flags[ROUGHNESS_MAP_BIT]; }
190 
191  void setTranslucentFactor(bool value) { _flags.set(OPACITY_VAL_BIT, value); }
192  bool isTranslucentFactor() const { return _flags[OPACITY_VAL_BIT]; }
193 
194  void setTranslucentMap(bool value) { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT, value); }
195  bool isTranslucentMap() const { return _flags[OPACITY_TRANSLUCENT_MAP_BIT]; }
196 
197  void setOpacityMaskMap(bool value) { _flags.set(OPACITY_MASK_MAP_BIT, value); }
198  bool isOpacityMaskMap() const { return _flags[OPACITY_MASK_MAP_BIT]; }
199 
200  void setOpacityCutoff(bool value) { _flags.set(OPACITY_CUTOFF_VAL_BIT, value); }
201  bool isOpacityCutoff() const { return _flags[OPACITY_CUTOFF_VAL_BIT]; }
202 
203  void setNormalMap(bool value) { _flags.set(NORMAL_MAP_BIT, value); }
204  bool isNormalMap() const { return _flags[NORMAL_MAP_BIT]; }
205 
206  void setOcclusionMap(bool value) { _flags.set(OCCLUSION_MAP_BIT, value); }
207  bool isOcclusionMap() const { return _flags[OCCLUSION_MAP_BIT]; }
208 
209  void setLightMap(bool value) { _flags.set(LIGHT_MAP_BIT, value); }
210  bool isLightMap() const { return _flags[LIGHT_MAP_BIT]; }
211 
212  void setScattering(bool value) { _flags.set(SCATTERING_VAL_BIT, value); }
213  bool isScattering() const { return _flags[SCATTERING_VAL_BIT]; }
214 
215  void setScatteringMap(bool value) { _flags.set(SCATTERING_MAP_BIT, value); }
216  bool isScatteringMap() const { return _flags[SCATTERING_MAP_BIT]; }
217 
218  void setMapChannel(MapChannel channel, bool value) { _flags.set(EMISSIVE_MAP_BIT + channel, value); }
219  bool isMapChannel(MapChannel channel) const { return _flags[EMISSIVE_MAP_BIT + channel]; }
220 
221 
222  // Translucency and Opacity Heuristics are combining several flags:
223  void setOpacityMapMode(OpacityMapMode mode) {
224  switch (mode) {
225  case OPACITY_MAP_OPAQUE:
226  _flags.reset(OPACITY_TRANSLUCENT_MAP_BIT);
227  _flags.reset(OPACITY_MASK_MAP_BIT);
228  break;
229  case OPACITY_MAP_MASK:
230  _flags.reset(OPACITY_TRANSLUCENT_MAP_BIT);
231  _flags.set(OPACITY_MASK_MAP_BIT);
232  break;
233  case OPACITY_MAP_BLEND:
234  _flags.set(OPACITY_TRANSLUCENT_MAP_BIT);
235  _flags.reset(OPACITY_MASK_MAP_BIT);
236  break;
237  };
238  _flags.set(OPACITY_MAP_MODE_BIT); // Intentionally set the mode!
239  }
240  bool isOpacityMapMode() const { return _flags[OPACITY_MAP_MODE_BIT]; }
241  OpacityMapMode getOpacityMapMode() const { return (isOpacityMaskMap() ? OPACITY_MAP_MASK : (isTranslucentMap() ? OPACITY_MAP_BLEND : OPACITY_MAP_OPAQUE)); }
242 
243  bool isTranslucent() const { return isTranslucentFactor() || isTranslucentMap(); }
244  bool isOpaque() const { return !isTranslucent(); }
245  bool isSurfaceOpaque() const { return isOpaque() && !isOpacityMaskMap(); }
246  bool isTexelOpaque() const { return isOpaque() && isOpacityMaskMap(); }
247 };
248 
249 class MaterialFilter {
250 public:
251  MaterialKey::Flags _value{ 0 };
252  MaterialKey::Flags _mask{ 0 };
253 
254 
255  MaterialFilter(const MaterialKey::Flags& value = MaterialKey::Flags(0), const MaterialKey::Flags& mask = MaterialKey::Flags(0)) : _value(value), _mask(mask) {}
256 
257  class Builder {
258  MaterialKey::Flags _value{ 0 };
259  MaterialKey::Flags _mask{ 0 };
260  public:
261  Builder() {}
262 
263  MaterialFilter build() const { return MaterialFilter(_value, _mask); }
264 
265  Builder& withoutEmissive() { _value.reset(MaterialKey::EMISSIVE_VAL_BIT); _mask.set(MaterialKey::EMISSIVE_VAL_BIT); return (*this); }
266  Builder& withEmissive() { _value.set(MaterialKey::EMISSIVE_VAL_BIT); _mask.set(MaterialKey::EMISSIVE_VAL_BIT); return (*this); }
267 
268  Builder& withoutEmissiveMap() { _value.reset(MaterialKey::EMISSIVE_MAP_BIT); _mask.set(MaterialKey::EMISSIVE_MAP_BIT); return (*this); }
269  Builder& withEmissiveMap() { _value.set(MaterialKey::EMISSIVE_MAP_BIT); _mask.set(MaterialKey::EMISSIVE_MAP_BIT); return (*this); }
270 
271  Builder& withoutUnlit() { _value.reset(MaterialKey::UNLIT_VAL_BIT); _mask.set(MaterialKey::UNLIT_VAL_BIT); return (*this); }
272  Builder& withUnlit() { _value.set(MaterialKey::UNLIT_VAL_BIT); _mask.set(MaterialKey::UNLIT_VAL_BIT); return (*this); }
273 
274  Builder& withoutAlbedo() { _value.reset(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
275  Builder& withAlbedo() { _value.set(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
276 
277  Builder& withoutAlbedoMap() { _value.reset(MaterialKey::ALBEDO_MAP_BIT); _mask.set(MaterialKey::ALBEDO_MAP_BIT); return (*this); }
278  Builder& withAlbedoMap() { _value.set(MaterialKey::ALBEDO_MAP_BIT); _mask.set(MaterialKey::ALBEDO_MAP_BIT); return (*this); }
279 
280  Builder& withoutMetallic() { _value.reset(MaterialKey::METALLIC_VAL_BIT); _mask.set(MaterialKey::METALLIC_VAL_BIT); return (*this); }
281  Builder& withMetallic() { _value.set(MaterialKey::METALLIC_VAL_BIT); _mask.set(MaterialKey::METALLIC_VAL_BIT); return (*this); }
282 
283  Builder& withoutMetallicMap() { _value.reset(MaterialKey::METALLIC_MAP_BIT); _mask.set(MaterialKey::METALLIC_MAP_BIT); return (*this); }
284  Builder& withMetallicMap() { _value.set(MaterialKey::METALLIC_MAP_BIT); _mask.set(MaterialKey::METALLIC_MAP_BIT); return (*this); }
285 
286  Builder& withoutGlossy() { _value.reset(MaterialKey::GLOSSY_VAL_BIT); _mask.set(MaterialKey::GLOSSY_VAL_BIT); return (*this); }
287  Builder& withGlossy() { _value.set(MaterialKey::GLOSSY_VAL_BIT); _mask.set(MaterialKey::GLOSSY_VAL_BIT); return (*this); }
288 
289  Builder& withoutRoughnessMap() { _value.reset(MaterialKey::ROUGHNESS_MAP_BIT); _mask.set(MaterialKey::ROUGHNESS_MAP_BIT); return (*this); }
290  Builder& withRoughnessMap() { _value.set(MaterialKey::ROUGHNESS_MAP_BIT); _mask.set(MaterialKey::ROUGHNESS_MAP_BIT); return (*this); }
291 
292  Builder& withoutTranslucentFactor() { _value.reset(MaterialKey::OPACITY_VAL_BIT); _mask.set(MaterialKey::OPACITY_VAL_BIT); return (*this); }
293  Builder& withTranslucentFactor() { _value.set(MaterialKey::OPACITY_VAL_BIT); _mask.set(MaterialKey::OPACITY_VAL_BIT); return (*this); }
294 
295  Builder& withoutTranslucentMap() { _value.reset(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); _mask.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
296  Builder& withTranslucentMap() { _value.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); _mask.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
297 
298  Builder& withoutMaskMap() { _value.reset(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); }
299  Builder& withMaskMap() { _value.set(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); }
300 
301  Builder& withoutOpacityMapMode() { _value.reset(MaterialKey::OPACITY_MAP_MODE_BIT); _mask.set(MaterialKey::OPACITY_MAP_MODE_BIT); return (*this); }
302  Builder& withOpacityMapMode() { _value.set(MaterialKey::OPACITY_MAP_MODE_BIT); _mask.set(MaterialKey::OPACITY_MAP_MODE_BIT); return (*this); }
303 
304  Builder& withoutOpacityCutoff() { _value.reset(MaterialKey::OPACITY_CUTOFF_VAL_BIT); _mask.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); return (*this); }
305  Builder& withOpacityCutoff() { _value.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); _mask.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); return (*this); }
306 
307  Builder& withoutNormalMap() { _value.reset(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
308  Builder& withNormalMap() { _value.set(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
309 
310  Builder& withoutOcclusionMap() { _value.reset(MaterialKey::OCCLUSION_MAP_BIT); _mask.set(MaterialKey::OCCLUSION_MAP_BIT); return (*this); }
311  Builder& withOcclusionMap() { _value.set(MaterialKey::OCCLUSION_MAP_BIT); _mask.set(MaterialKey::OCCLUSION_MAP_BIT); return (*this); }
312 
313  Builder& withoutLightMap() { _value.reset(MaterialKey::LIGHT_MAP_BIT); _mask.set(MaterialKey::LIGHT_MAP_BIT); return (*this); }
314  Builder& withLightMap() { _value.set(MaterialKey::LIGHT_MAP_BIT); _mask.set(MaterialKey::LIGHT_MAP_BIT); return (*this); }
315 
316  Builder& withoutScattering() { _value.reset(MaterialKey::SCATTERING_VAL_BIT); _mask.set(MaterialKey::SCATTERING_VAL_BIT); return (*this); }
317  Builder& withScattering() { _value.set(MaterialKey::SCATTERING_VAL_BIT); _mask.set(MaterialKey::SCATTERING_VAL_BIT); return (*this); }
318 
319  Builder& withoutScatteringMap() { _value.reset(MaterialKey::SCATTERING_MAP_BIT); _mask.set(MaterialKey::SCATTERING_MAP_BIT); return (*this); }
320  Builder& withScatteringMap() { _value.set(MaterialKey::SCATTERING_MAP_BIT); _mask.set(MaterialKey::SCATTERING_MAP_BIT); return (*this); }
321 
322 
323  // Convenient standard keys that we will keep on using all over the place
324  static MaterialFilter opaqueAlbedo() { return Builder().withAlbedo().withoutTranslucentFactor().build(); }
325  };
326 
327  // Item Filter operator testing if a key pass the filter
328  bool test(const MaterialKey& key) const { return (key._flags & _mask) == (_value & _mask); }
329 
330  class Less {
331  public:
332  bool operator() (const MaterialFilter& left, const MaterialFilter& right) const {
333  if (left._value.to_ulong() == right._value.to_ulong()) {
334  return left._mask.to_ulong() < right._mask.to_ulong();
335  } else {
336  return left._value.to_ulong() < right._value.to_ulong();
337  }
338  }
339  };
340 };
341 
342 class Material {
343 public:
344  typedef MaterialKey::MapChannel MapChannel;
345  typedef std::map<MapChannel, TextureMapPointer> TextureMaps;
346 
347  Material();
348  Material(const Material& material);
349  virtual ~Material() = default;
350  Material& operator= (const Material& material);
351 
352  virtual MaterialKey getKey() const { return _key; }
353 
354  static const float DEFAULT_EMISSIVE;
355  void setEmissive(const glm::vec3& emissive, bool isSRGB = true);
356  virtual glm::vec3 getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_emissive) : _emissive); }
357 
358  static const float DEFAULT_OPACITY;
359  void setOpacity(float opacity);
360  virtual float getOpacity() const { return _opacity; }
361 
362  static const MaterialKey::OpacityMapMode DEFAULT_OPACITY_MAP_MODE;
363  void setOpacityMapMode(MaterialKey::OpacityMapMode opacityMapMode);
364  virtual MaterialKey::OpacityMapMode getOpacityMapMode() const;
365 
366  static const float DEFAULT_OPACITY_CUTOFF;
367  void setOpacityCutoff(float opacityCutoff);
368  virtual float getOpacityCutoff() const { return _opacityCutoff; }
369 
370  static const MaterialKey::CullFaceMode DEFAULT_CULL_FACE_MODE;
371  void setCullFaceMode(MaterialKey::CullFaceMode cullFaceMode) { _cullFaceMode = cullFaceMode; }
372  virtual MaterialKey::CullFaceMode getCullFaceMode() const { return _cullFaceMode; }
373 
374  void setUnlit(bool value);
375  virtual bool isUnlit() const { return _key.isUnlit(); }
376 
377  static const float DEFAULT_ALBEDO;
378  void setAlbedo(const glm::vec3& albedo, bool isSRGB = true);
379  virtual glm::vec3 getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_albedo) : _albedo); }
380 
381  static const float DEFAULT_METALLIC;
382  void setMetallic(float metallic);
383  virtual float getMetallic() const { return _metallic; }
384 
385  static const float DEFAULT_ROUGHNESS;
386  void setRoughness(float roughness);
387  virtual float getRoughness() const { return _roughness; }
388 
389  static const float DEFAULT_SCATTERING;
390  void setScattering(float scattering);
391  virtual float getScattering() const { return _scattering; }
392 
393  // The texture map to channel association
394  static const int NUM_TEXCOORD_TRANSFORMS { 2 };
395  void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap);
396  virtual TextureMaps getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe...
397  const TextureMapPointer getTextureMap(MapChannel channel) const;
398 
399  // Albedo maps cannot have opacity detected until they are loaded
400  // This method allows const changing of the key/schemaBuffer without touching the map
401  // return true if the opacity changed, flase otherwise
402  virtual bool resetOpacityMap() const;
403 
404  // conversion from legacy material properties to PBR equivalent
405  static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; }
406 
407  void setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat);
408 
409  const std::string& getName() const { return _name; }
410  void setName(const std::string& name) { _name = name; }
411 
412  const std::string& getModel() const { return _model; }
413  void setModel(const std::string& model) { _model = model; }
414 
415  virtual glm::mat4 getTexCoordTransform(uint i) const { return _texcoordTransforms[i]; }
416  void setTexCoordTransform(uint i, const glm::mat4& mat4) { _texcoordTransforms[i] = mat4; }
417  virtual glm::vec2 getLightmapParams() const { return _lightmapParams; }
418  virtual glm::vec2 getMaterialParams() const { return _materialParams; }
419 
420  virtual bool getDefaultFallthrough() const { return _defaultFallthrough; }
421  void setDefaultFallthrough(bool defaultFallthrough) { _defaultFallthrough = defaultFallthrough; }
422 
423  enum ExtraFlagBit {
424  TEXCOORDTRANSFORM0 = MaterialKey::NUM_FLAGS,
425  TEXCOORDTRANSFORM1,
426  LIGHTMAP_PARAMS,
427  MATERIAL_PARAMS,
428  CULL_FACE_MODE,
429 
430  EXTRA_1_BIT,
431  EXTRA_2_BIT,
432  EXTRA_3_BIT,
433 
434  NUM_TOTAL_FLAGS
435  };
436  std::unordered_map<uint, bool> getPropertyFallthroughs() { return _propertyFallthroughs; }
437  bool getPropertyFallthrough(uint property) { return _propertyFallthroughs[property]; }
438  void setPropertyDoesFallthrough(uint property) { _propertyFallthroughs[property] = true; }
439 
440  virtual bool isProcedural() const { return false; }
441  virtual bool isEnabled() const { return true; }
442  virtual bool isReady() const { return true; }
443  virtual QString getProceduralString() const { return QString(); }
444 
445  virtual bool isReference() const { return false; }
446 
447  virtual bool isMToon() const { return false; }
448  static const glm::vec3 DEFAULT_SHADE;
449  virtual glm::vec3 getShade(bool SRGB = true) const { return glm::vec3(0.0f); }
450  static const float DEFAULT_SHADING_SHIFT;
451  virtual float getShadingShift() const { return 0.0f; }
452  static const float DEFAULT_SHADING_TOONY;
453  virtual float getShadingToony() const { return 0.0f; }
454  static const glm::vec3 DEFAULT_MATCAP;
455  virtual glm::vec3 getMatcap(bool SRGB = true) const { return glm::vec3(0.0f); }
456  static const glm::vec3 DEFAULT_PARAMETRIC_RIM;
457  virtual glm::vec3 getParametricRim(bool SRGB = true) const { return glm::vec3(0.0f); }
458  static const float DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER;
459  virtual float getParametricRimFresnelPower() const { return 0.0f; }
460  static const float DEFAULT_PARAMETRIC_RIM_LIFT;
461  virtual float getParametricRimLift() const { return 0.0f; }
462  static const float DEFAULT_RIM_LIGHTING_MIX;
463  virtual float getRimLightingMix() const { return 0.0f; }
464  static const float DEFAULT_UV_ANIMATION_SCROLL_SPEED;
465  virtual float getUVAnimationScrollXSpeed() const { return 0.0f; }
466  virtual float getUVAnimationScrollYSpeed() const { return 0.0f; }
467  virtual float getUVAnimationRotationSpeed() const { return 0.0f; }
468 
469  static const glm::vec3 DEFAULT_OUTLINE;
470  virtual uint8_t getOutlineWidthMode() { return 0; }
471  virtual float getOutlineWidth() { return 0.0f; }
472  virtual glm::vec3 getOutline(bool SRGB = true) const { return glm::vec3(0.0f); }
473 
474  static const std::string HIFI_PBR;
475  static const std::string HIFI_SHADER_SIMPLE;
476  static const std::string VRM_MTOON;
477 
478 protected:
479  std::string _name { "" };
480  mutable MaterialKey _key { 0 };
481  std::string _model { HIFI_PBR };
482 
483 private:
484  // Material properties
485  glm::vec3 _emissive { DEFAULT_EMISSIVE };
486  float _opacity { DEFAULT_OPACITY };
487  glm::vec3 _albedo { DEFAULT_ALBEDO };
488  float _roughness { DEFAULT_ROUGHNESS };
489  float _metallic { DEFAULT_METALLIC };
490  float _scattering { DEFAULT_SCATTERING };
491  float _opacityCutoff { DEFAULT_OPACITY_CUTOFF };
492  std::array<glm::mat4, NUM_TEXCOORD_TRANSFORMS> _texcoordTransforms;
493  glm::vec2 _lightmapParams { 0.0, 1.0 };
494  glm::vec2 _materialParams { 0.0, 1.0 };
495  MaterialKey::CullFaceMode _cullFaceMode { DEFAULT_CULL_FACE_MODE };
496  TextureMaps _textureMaps;
497 
498  bool _defaultFallthrough { false };
499  std::unordered_map<uint, bool> _propertyFallthroughs { NUM_TOTAL_FLAGS };
500 
501  mutable std::recursive_mutex _textureMapsMutex;
502 };
503 typedef std::shared_ptr<Material> MaterialPointer;
504 
505 class MaterialLayer {
506 public:
507  MaterialLayer(MaterialPointer material, quint16 priority) : material(material), priority(priority) {}
508 
509  MaterialPointer material { nullptr };
510  quint16 priority { 0 };
511 };
512 
513 class MaterialLayerCompare {
514 public:
515  bool operator() (MaterialLayer left, MaterialLayer right) {
516  return left.priority < right.priority;
517  }
518 };
519 typedef std::priority_queue<MaterialLayer, std::vector<MaterialLayer>, MaterialLayerCompare> MaterialLayerQueue;
520 
521 class MultiMaterial : public MaterialLayerQueue {
522 public:
523  MultiMaterial();
524 
525  void push(const MaterialLayer& value) {
526  MaterialLayerQueue::push(value);
527  _hasCalculatedTextureInfo = false;
528  _needsUpdate = true;
529  }
530 
531  bool remove(const MaterialPointer& value) {
532  auto it = c.begin();
533  while (it != c.end()) {
534  if (it->material == value) {
535  break;
536  }
537  it++;
538  }
539  if (it != c.end()) {
540  c.erase(it);
541  std::make_heap(c.begin(), c.end(), comp);
542  _hasCalculatedTextureInfo = false;
543  _needsUpdate = true;
544  return true;
545  } else {
546  return false;
547  }
548  }
549 
550  // Schema to access the attribute values of the material
551  class Schema {
552  public:
553  glm::vec3 _emissive { Material::DEFAULT_EMISSIVE }; // No Emissive
554  float _opacity { Material::DEFAULT_OPACITY }; // Opacity = 1 => Not Transparent
555 
556  glm::vec3 _albedo { Material::DEFAULT_ALBEDO }; // Grey albedo => isAlbedo
557  float _roughness { Material::DEFAULT_ROUGHNESS }; // Roughness = 1 => Not Glossy
558 
559  float _metallic { Material::DEFAULT_METALLIC }; // Not Metallic
560  float _scattering { Material::DEFAULT_SCATTERING }; // Scattering info
561  float _opacityCutoff { Material::DEFAULT_OPACITY_CUTOFF }; // Opacity cutoff applyed when using opacityMap as Mask
562  uint32_t _key { 0 }; // a copy of the materialKey
563 
564  // Texture Coord Transform Array
565  glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS];
566 
567  // x: material mode (0 for UV, 1 for PROJECTED)
568  // y: 1 for texture repeat, 0 for discard outside of 0 - 1
569  glm::vec2 _materialParams { 0.0, 1.0 };
570 
571  glm::vec2 _lightmapParams { 0.0, 1.0 };
572 
573  Schema() {
574  for (auto& transform : _texcoordTransforms) {
575  transform = glm::mat4();
576  }
577  }
578  };
579 
580  class MToonSchema {
581  public:
582  glm::vec3 _emissive { Material::DEFAULT_EMISSIVE }; // No Emissive
583  float _opacity { Material::DEFAULT_OPACITY }; // Opacity = 1 => Not Transparent
584 
585  glm::vec3 _albedo { Material::DEFAULT_ALBEDO }; // Grey albedo => isAlbedo
586  float _opacityCutoff { Material::DEFAULT_OPACITY_CUTOFF }; // Opacity cutoff applyed when using opacityMap as Mask
587 
588  glm::vec3 _shade { Material::DEFAULT_SHADE };
589  float _shadingShift { Material::DEFAULT_SHADING_SHIFT };
590 
591  glm::vec3 _matcap { Material::DEFAULT_MATCAP };
592  float _shadingToony { Material::DEFAULT_SHADING_TOONY };
593 
594  glm::vec3 _parametricRim { Material::DEFAULT_PARAMETRIC_RIM };
595  float _parametricRimFresnelPower { Material::DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER };
596 
597  float _parametricRimLift { Material::DEFAULT_PARAMETRIC_RIM_LIFT };
598  float _rimLightingMix { Material::DEFAULT_RIM_LIGHTING_MIX };
599  glm::vec2 _uvAnimationScrollSpeed { Material::DEFAULT_UV_ANIMATION_SCROLL_SPEED };
600 
601  float _uvAnimationScrollRotationSpeed { Material::DEFAULT_UV_ANIMATION_SCROLL_SPEED };
602  float _time { 0.0f };
603  uint32_t _key { 0 }; // a copy of the materialKey
604  float _spare { 0.0f };
605 
606  // Texture Coord Transform Array
607  glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS];
608 
609  // x: material mode (0 for UV, 1 for PROJECTED)
610  // y: 1 for texture repeat, 0 for discard outside of 0 - 1
611  glm::vec2 _materialParams { 0.0, 1.0 };
612 
613  MToonSchema() {
614  for (auto& transform : _texcoordTransforms) {
615  transform = glm::mat4();
616  }
617  }
618  };
619 
620  gpu::BufferView& getSchemaBuffer() { return _schemaBuffer; }
621  graphics::MaterialKey getMaterialKey() const {
622  if (_isMToon) {
623  return graphics::MaterialKey(_schemaBuffer.get<graphics::MultiMaterial::MToonSchema>()._key);
624  } else {
625  return graphics::MaterialKey(_schemaBuffer.get<graphics::MultiMaterial::Schema>()._key);
626  }
627  }
628  glm::vec4 getColor() const {
629  glm::vec3 albedo;
630  float opacity;
631  if (_isMToon) {
632  const auto& schema = _schemaBuffer.get<graphics::MultiMaterial::MToonSchema>();
633  albedo = schema._albedo;
634  opacity = schema._opacity;
635  } else {
636  const auto& schema = _schemaBuffer.get<graphics::MultiMaterial::Schema>();
637  albedo = schema._albedo;
638  opacity = schema._opacity;
639  }
640  return glm::vec4(ColorUtils::tosRGBVec3(albedo), opacity);
641  }
642  const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; }
643 
644  void setCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) { _cullFaceMode = cullFaceMode; }
645  graphics::MaterialKey::CullFaceMode getCullFaceMode() const { return _cullFaceMode; }
646 
647  void setNeedsUpdate(bool needsUpdate) { _needsUpdate = needsUpdate; }
648  void setTexturesLoading(bool value) { _texturesLoading = value; }
649  void setInitialized() { _initialized = true; }
650 
651  bool shouldUpdate() const { return !_initialized || _needsUpdate || _texturesLoading || anyReferenceMaterialsOrTexturesChanged(); }
652 
653  int getTextureCount() const { calculateMaterialInfo(); return _textureCount; }
654  size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; }
655  bool hasTextureInfo() const { return _hasCalculatedTextureInfo; }
656 
657  void resetReferenceTexturesAndMaterials();
658  void addReferenceTexture(const std::function<gpu::TexturePointer()>& textureOperator);
659  void addReferenceMaterial(const std::function<graphics::MaterialPointer()>& materialOperator);
660 
661  void setisMToon(bool isMToon);
662  bool isMToon() const { return _isMToon; }
663  void setMToonTime();
664  bool hasOutline() const { return _outlineWidthMode != 0 && _outlineWidth > 0.0f; }
665  uint8_t getOutlineWidthMode() const { return _outlineWidthMode; }
666  float getOutlineWidth() const { return _outlineWidth; }
667  glm::vec3 getOutline() const { return _outline; }
668  void resetOutline() { _outlineWidthMode = 0; _outlineWidth = 0.0f; _outline = glm::vec3(0.0f); }
669  void setOutlineWidthMode(uint8_t mode) { _outlineWidthMode = mode; }
670  void setOutlineWidth(float width) { _outlineWidth = width; }
671  void setOutline(const glm::vec3& outline) { _outline = outline; }
672 
673 private:
674  gpu::BufferView _schemaBuffer;
675  graphics::MaterialKey::CullFaceMode _cullFaceMode { graphics::Material::DEFAULT_CULL_FACE_MODE };
676  gpu::TextureTablePointer _textureTable { std::make_shared<gpu::TextureTable>() };
677  bool _needsUpdate { false };
678  bool _texturesLoading { false };
679  bool _initialized { false };
680 
681  mutable size_t _textureSize { 0 };
682  mutable int _textureCount { 0 };
683  mutable bool _hasCalculatedTextureInfo { false };
684  void calculateMaterialInfo() const;
685 
686  bool anyReferenceMaterialsOrTexturesChanged() const;
687 
688  std::vector<std::pair<std::function<gpu::TexturePointer()>, gpu::TexturePointer>> _referenceTextures;
689  std::vector<std::pair<std::function<graphics::MaterialPointer()>, graphics::MaterialPointer>> _referenceMaterials;
690 
691  bool _isMToon { false };
692  uint8_t _outlineWidthMode { 0 };
693  float _outlineWidth { 0.0f };
694  glm::vec3 _outline { graphics::Material::DEFAULT_OUTLINE };
695 };
696 
697 };
698 
699 #endif