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