Panda3D
material.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file material.cxx
10  * @author mike
11  * @date 1997-01-09
12  */
13 
14 #include "pandabase.h"
15 #include "material.h"
16 #include "indent.h"
17 #include "datagram.h"
18 #include "datagramIterator.h"
19 #include "bamReader.h"
20 #include "bamWriter.h"
21 
22 TypeHandle Material::_type_handle;
23 PT(Material) Material::_default;
24 
25 /**
26  *
27  */
28 void Material::
29 operator = (const Material &copy) {
30  Namable::operator = (copy);
31 
32  if (is_used_by_auto_shader()) {
33  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
34  }
35 
36  _base_color = copy._base_color;
37  _ambient = copy._ambient;
38  _diffuse = copy._diffuse;
39  _specular = copy._specular;
40  _emission = copy._emission;
41  _shininess = copy._shininess;
42  _roughness = copy._roughness;
43  _metallic = copy._metallic;
44  _refractive_index = copy._refractive_index;
45  _flags = (copy._flags & ~(F_attrib_lock | F_used_by_auto_shader)) | (_flags & (F_attrib_lock | F_used_by_auto_shader));
46 }
47 
48 /**
49  * Specifies the base color of the material. In conjunction with
50  * set_metallic, this is an alternate way to specify the color of a material.
51  * For dielectrics, this will determine the value of the diffuse color, and
52  * for metals, this will determine the value of the specular color.
53  *
54  * Setting this will clear an explicit specular, diffuse or ambient color
55  * assignment.
56  *
57  * If this is not set, the object color will be used.
58  */
59 void Material::
60 set_base_color(const LColor &color) {
61  if (!has_base_color() && is_used_by_auto_shader()) {
62  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
63  }
64  _base_color = color;
65  _flags |= F_base_color | F_metallic;
66  _flags &= ~(F_ambient | F_diffuse | F_specular);
67 
68  // Recalculate the diffuse and specular colors.
69  _ambient = _base_color;
70  _diffuse = _base_color * (1 - _metallic);
71 
72  PN_stdfloat f0 = 0;
73  if (_refractive_index >= 1) {
74  f0 = (_refractive_index - 1) / (_refractive_index + 1);
75  f0 *= f0;
76  f0 *= (1 - _metallic);
77  }
78  _specular.set(f0, f0, f0, 0);
79  _specular += _base_color * _metallic;
80 }
81 
82 /**
83  * Removes the explicit base_color color from the material.
84  */
85 void Material::
86 clear_base_color() {
87  if (has_base_color() && is_used_by_auto_shader()) {
88  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
89  }
90  _flags &= ~F_base_color;
91  _base_color.set(0.0f, 0.0f, 0.0f, 0.0f);
92 
93  if ((_flags & F_ambient) == 0) {
94  _ambient.set(0, 0, 0, 0);
95  }
96  if ((_flags & F_diffuse) == 0) {
97  _diffuse.set(0, 0, 0, 0);
98  }
99  if ((_flags & F_specular) == 0) {
100  // Recalculate the specular color.
101  PN_stdfloat f0 = 0;
102  if (_refractive_index >= 1) {
103  f0 = (_refractive_index - 1) / (_refractive_index + 1);
104  f0 *= f0;
105  }
106  _specular.set(f0, f0, f0, 0);
107  }
108 }
109 
110 /**
111  * Specifies the ambient color setting of the material. This will be the
112  * multiplied by any ambient lights in effect on the material to set its base
113  * color.
114  *
115  * This is the color of the object as it appears in the absence of direct
116  * light.
117  *
118  * If this is not set, the object color will be used.
119  */
120 void Material::
121 set_ambient(const LColor &color) {
122  if (!has_ambient() && is_used_by_auto_shader()) {
123  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
124  }
125  _ambient = color;
126  _flags |= F_ambient;
127 }
128 
129 /**
130  * Specifies the diffuse color setting of the material. This will be
131  * multiplied by any lights in effect on the material to get the color in the
132  * parts of the object illuminated by the lights.
133  *
134  * This is the primary color of an object; the color of the object as it
135  * appears in direct light, in the absence of highlights.
136  *
137  * If this is not set, the object color will be used.
138  */
139 void Material::
140 set_diffuse(const LColor &color) {
141  if (!has_diffuse() && is_used_by_auto_shader()) {
142  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
143  }
144  _diffuse = color;
145  _flags |= F_diffuse;
146 }
147 
148 /**
149  * Specifies the specular color setting of the material. This will be
150  * multiplied by any lights in effect on the material to compute the color of
151  * specular highlights on the object.
152  *
153  * This is the highlight color of an object: the color of small highlight
154  * reflections.
155  *
156  * If this is not set, the specular color is taken from the index of
157  * refraction, which is 1 by default (meaning no specular reflections are
158  * generated).
159  */
160 void Material::
161 set_specular(const LColor &color) {
162  if (!has_specular() && is_used_by_auto_shader()) {
163  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
164  }
165  _specular = color;
166  _flags |= F_specular;
167 }
168 
169 /**
170  * Removes the explicit specular color from the material.
171  */
172 void Material::
173 clear_specular() {
174  if (has_specular() && is_used_by_auto_shader()) {
175  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
176  }
177  _flags &= ~F_specular;
178 
179  // Recalculate the specular color from the refractive index.
180  PN_stdfloat f0 = 0;
181  if (_refractive_index >= 1) {
182  f0 = (_refractive_index - 1) / (_refractive_index + 1);
183  f0 *= f0;
184  f0 *= (1 - _metallic);
185  }
186  _specular.set(f0, f0, f0, 0);
187  _specular += _base_color * _metallic;
188 }
189 
190 /**
191  * Specifies the emission color setting of the material. This is the color of
192  * the object as it appears in the absence of any light whatsover, including
193  * ambient light. It is as if the object is glowing by this color (although
194  * of course it will not illuminate neighboring objects).
195  *
196  * If this is not set, the object will not glow by its own light and will only
197  * appear visible in the presence of one or more lights.
198  */
199 void Material::
200 set_emission(const LColor &color) {
201  if (!has_emission() && is_used_by_auto_shader()) {
202  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
203  }
204  _emission = color;
205  _flags |= F_emission;
206 }
207 
208 /**
209  * Sets the shininess exponent of the material. This controls the size of the
210  * specular highlight spot. In general, larger number produce a smaller
211  * specular highlight, which makes the object appear shinier. Smaller numbers
212  * produce a larger highlight, which makes the object appear less shiny.
213  *
214  * This is usually in the range 0..128.
215  *
216  * Setting a shininess value removes any previous roughness assignment.
217  */
218 void Material::
219 set_shininess(PN_stdfloat shininess) {
220  _shininess = shininess;
221  _flags &= ~F_roughness;
222 }
223 
224 /**
225  * Returns the roughness previously specified by set_roughness. If none was
226  * previously set, this value is computed from the shininess value.
227  */
228 PN_stdfloat Material::
229 get_roughness() const {
230  if ((_flags & F_roughness) == 0) {
231  // Calculate roughness from blinn-phong shininess.
232  return csqrt(csqrt(2 / (_shininess + 2)));
233  } else {
234  return _roughness;
235  }
236 }
237 
238 /**
239  * Sets the roughness exponent of the material, where 0 is completely shiny
240  * (infinite shininess), and 1 is a completely dull object (0 shininess).
241  * This is a different, more perceptually intuitive way of controlling the
242  * size of the specular spot, and more commonly used in physically-based
243  * rendering.
244  *
245  * Setting a roughness recalculates the shininess value.
246  */
247 void Material::
248 set_roughness(PN_stdfloat roughness) {
249  _roughness = roughness;
250  _flags |= F_roughness;
251 
252  // Calculate the specular exponent from the roughness as it is used in
253  // Blinn-Phong shading model. We use the popular Disney method of squaring
254  // the roughness to get a more perceptually linear scale. From:
255  // http://graphicrants.blogspot.de/2013/08/specular-brdf-reference.html
256  if (roughness <= 0 || IS_NEARLY_ZERO(roughness)) {
257  _shininess = make_inf((PN_stdfloat)0);
258  } else {
259  PN_stdfloat alpha = roughness * roughness;
260  _shininess = 2 / (alpha * alpha) - 2;
261  }
262 }
263 
264 /**
265  * Sets the metallic setting of the material, which is is used for physically-
266  * based rendering models. This is usually 0 for dielectric materials and 1
267  * for metals. It really does not make sense to set this to a value other
268  * than 0 or 1, but it is nonetheless a float for compatibility with tools
269  * that allow setting this to values other than 0 or 1.
270  */
271 void Material::
272 set_metallic(PN_stdfloat metallic) {
273  _metallic = metallic;
274  _flags |= F_metallic;
275 
276  // Recalculate the diffuse and specular.
277  if ((_flags & F_diffuse) == 0) {
278  _diffuse = _base_color * (1 - _metallic);
279  }
280  if ((_flags & F_specular) == 0) {
281  // Recalculate the specular color.
282  PN_stdfloat f0 = 0;
283  if (_refractive_index >= 1) {
284  f0 = (_refractive_index - 1) / (_refractive_index + 1);
285  f0 *= f0;
286  f0 *= (1 - _metallic);
287  }
288  _specular.set(f0, f0, f0, 0);
289  _specular += _base_color * _metallic;
290  }
291 }
292 
293 /**
294  * Removes the explicit metallic setting from the material.
295  */
296 void Material::
298  _flags &= ~F_metallic;
299  _metallic = 0;
300 
301  // If we had a base color, recalculate the diffuse and specular.
302  if (_flags & F_base_color) {
303  if ((_flags & F_diffuse) == 0) {
304  _diffuse = _base_color;
305  }
306  if ((_flags & F_specular) == 0) {
307  // Recalculate the specular color.
308  PN_stdfloat f0 = 0;
309  if (_refractive_index >= 1) {
310  f0 = (_refractive_index - 1) / (_refractive_index + 1);
311  f0 *= f0;
312  }
313  _specular.set(f0, f0, f0, 0);
314  }
315  }
316 }
317 
318 /**
319  * Sets the index of refraction of the material, which is used to determine
320  * the specular color in absence of an explicit specular color assignment.
321  * This is usually 1.5 for dielectric materials. It is not very useful for
322  * metals, since they cannot be described as easily with a single number.
323  *
324  * Should be 1 or higher. The default is 1.
325  */
326 void Material::
327 set_refractive_index(PN_stdfloat refractive_index) {
328  _refractive_index = refractive_index;
329  _flags |= F_refractive_index;
330 
331  if ((_flags & F_specular) == 0) {
332  // Recalculate the specular color.
333  PN_stdfloat f0 = 0;
334  if (_refractive_index >= 1) {
335  f0 = (_refractive_index - 1) / (_refractive_index + 1);
336  f0 *= f0;
337  }
338  _specular.set(f0, f0, f0, 0);
339  }
340 }
341 
342 /**
343  * Returns a number less than zero if this material sorts before the other
344  * one, greater than zero if it sorts after, or zero if they are equivalent.
345  * The sorting order is arbitrary and largely meaningless, except to
346  * differentiate different materials.
347  */
348 int Material::
349 compare_to(const Material &other) const {
350  if (_flags != other._flags) {
351  return _flags - other._flags;
352  }
353  if (has_base_color() && get_base_color() != other.get_base_color()) {
354  return get_base_color().compare_to(other.get_base_color());
355  }
356  if (has_ambient() && get_ambient() != other.get_ambient()) {
357  return get_ambient().compare_to(other.get_ambient());
358  }
359  if (has_diffuse() && get_diffuse() != other.get_diffuse()) {
360  return get_diffuse().compare_to(other.get_diffuse());
361  }
362  if (has_specular() && get_specular() != other.get_specular()) {
363  return get_specular().compare_to(other.get_specular());
364  }
365  if (has_emission() && get_emission() != other.get_emission()) {
366  return get_emission().compare_to(other.get_emission());
367  }
368  if (get_shininess() != other.get_shininess()) {
369  return get_shininess() < other.get_shininess() ? -1 : 1;
370  }
371  if (get_metallic() != other.get_metallic()) {
372  return get_metallic() < other.get_metallic() ? -1 : 1;
373  }
374  if (get_refractive_index() != other.get_refractive_index()) {
375  return get_refractive_index() < other.get_refractive_index() ? -1 : 1;
376  }
377 
378  return strcmp(get_name().c_str(), other.get_name().c_str());
379 }
380 
381 /**
382  *
383  */
384 void Material::
385 output(std::ostream &out) const {
386  out << "Material " << get_name();
387  if (has_base_color()) {
388  out << " c(" << get_base_color() << ")";
389  } else {
390  if (has_ambient()) {
391  out << " a(" << get_ambient() << ")";
392  }
393  if (has_diffuse()) {
394  out << " d(" << get_diffuse() << ")";
395  }
396  if (has_specular()) {
397  out << " s(" << get_specular() << ")";
398  }
399  }
400  if (has_refractive_index()) {
401  out << " ior" << get_refractive_index();
402  }
403  if (has_emission()) {
404  out << " e(" << get_emission() << ")";
405  }
406  if (_flags & F_roughness) {
407  out << " r" << get_roughness();
408  } else {
409  out << " s" << get_shininess();
410  }
411  if (_flags & F_metallic) {
412  out << " m" << _metallic;
413  }
414  out << " l" << get_local()
415  << " t" << get_twoside();
416 }
417 
418 /**
419  *
420  */
421 void Material::
422 write(std::ostream &out, int indent_level) const {
423  indent(out, indent_level) << "Material " << get_name() << "\n";
424  if (has_base_color()) {
425  indent(out, indent_level + 2) << "base_color = " << get_base_color() << "\n";
426  }
427  if (has_ambient()) {
428  indent(out, indent_level + 2) << "ambient = " << get_ambient() << "\n";
429  }
430  if (has_diffuse()) {
431  indent(out, indent_level + 2) << "diffuse = " << get_diffuse() << "\n";
432  }
433  if (has_specular()) {
434  indent(out, indent_level + 2) << "specular = " << get_specular() << "\n";
435  } else {
436  indent(out, indent_level + 2) << "refractive_index = " << get_refractive_index() << "\n";
437  }
438  if (has_emission()) {
439  indent(out, indent_level + 2) << "emission = " << get_emission() << "\n";
440  }
441  if (_flags & F_roughness) {
442  indent(out, indent_level + 2) << "roughness = " << get_roughness() << "\n";
443  } else {
444  indent(out, indent_level + 2) << "shininess = " << get_shininess() << "\n";
445  }
446  if (has_metallic()) {
447  indent(out, indent_level + 2) << "metallic = " << get_metallic() << "\n";
448  }
449  indent(out, indent_level + 2) << "local = " << get_local() << "\n";
450  indent(out, indent_level + 2) << "twoside = " << get_twoside() << "\n";
451 }
452 
453 
454 
455 /**
456  * Factory method to generate a Material object
457  */
458 void Material::
460  BamReader::get_factory()->register_factory(get_class_type(), make_Material);
461 }
462 
463 /**
464  * Function to write the important information in the particular object to a
465  * Datagram
466  */
467 void Material::
469  me.add_string(get_name());
470 
471  if (manager->get_file_minor_ver() >= 39) {
472  me.add_int32(_flags & ~F_used_by_auto_shader);
473 
474  if (_flags & F_metallic) {
475  // Metalness workflow.
476  _base_color.write_datagram(me);
477  me.add_stdfloat(_metallic);
478  } else {
479  _ambient.write_datagram(me);
480  _diffuse.write_datagram(me);
481  _specular.write_datagram(me);
482  }
483  _emission.write_datagram(me);
484 
485  if (_flags & F_roughness) {
486  me.add_stdfloat(_roughness);
487  } else {
488  me.add_stdfloat(_shininess);
489  }
490 
491  me.add_stdfloat(_refractive_index);
492  } else {
493  _ambient.write_datagram(me);
494  _diffuse.write_datagram(me);
495  _specular.write_datagram(me);
496  _emission.write_datagram(me);
497  me.add_stdfloat(_shininess);
498  me.add_int32(_flags & 0x7f);
499  }
500 }
501 
502 /**
503  * Factory method to generate a Material object
504  */
505 TypedWritable *Material::
506 make_Material(const FactoryParams &params) {
507  Material *me = new Material;
508  DatagramIterator scan;
509  BamReader *manager;
510 
511  parse_params(params, scan, manager);
512  me->fillin(scan, manager);
513  return me;
514 }
515 
516 /**
517  * Function that reads out of the datagram (or asks manager to read) all of
518  * the data that is needed to re-create this object and stores it in the
519  * appropiate place
520  */
521 void Material::
522 fillin(DatagramIterator &scan, BamReader *manager) {
523  set_name(scan.get_string());
524 
525  if (manager->get_file_minor_ver() >= 39) {
526  _flags = scan.get_int32();
527 
528  if (_flags & F_metallic) {
529  // Metalness workflow: read base color and metallic
530  _base_color.read_datagram(scan);
531  set_metallic(scan.get_stdfloat());
532 
533  } else {
534  _ambient.read_datagram(scan);
535  _diffuse.read_datagram(scan);
536  _specular.read_datagram(scan);
537  }
538  _emission.read_datagram(scan);
539 
540  if (_flags & F_roughness) {
541  set_roughness(scan.get_stdfloat());
542  } else {
543  _shininess = scan.get_stdfloat();
544  }
545  _refractive_index = scan.get_stdfloat();
546 
547  } else {
548  _ambient.read_datagram(scan);
549  _diffuse.read_datagram(scan);
550  _specular.read_datagram(scan);
551  _emission.read_datagram(scan);
552  _shininess = scan.get_stdfloat();
553  _flags = scan.get_int32();
554 
555  if (_flags & F_roughness) {
556  // The shininess we read is actually a roughness value.
557  set_roughness(_shininess);
558  }
559  }
560 
561  if (is_used_by_auto_shader()) {
562  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
563  }
564 }
set_specular
Specifies the specular color setting of the material.
Definition: material.h:118
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
Definition: material.cxx:468
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
has_ambient
Returns true if the ambient color has been explicitly set for this material, false otherwise.
Definition: material.h:114
set_ambient
Specifies the ambient color setting of the material.
Definition: material.h:114
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
bool has_metallic() const
Returns true if the metallic has been explicitly set for this material, false otherwise.
Definition: material.I:219
set_metallic
Sets the metallic setting of the material, which is is used for physically- based rendering models.
Definition: material.h:124
set_shininess
Sets the shininess exponent of the material.
Definition: material.h:122
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
get_twoside
Returns the state of the two-sided lighting flag.
Definition: material.h:129
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear_metallic()
Removes the explicit metallic setting from the material.
Definition: material.cxx:297
get_roughness
Returns the roughness previously specified by set_roughness.
Definition: material.h:123
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
int32_t get_int32()
Extracts a signed 32-bit integer.
std::string get_string()
Extracts a variable-length string.
get_local
Returns the local viewer flag.
Definition: material.h:128
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
set_base_color
Specifies the base color of the material.
Definition: material.h:112
set_diffuse
Specifies the diffuse color setting of the material.
Definition: material.h:116
get_diffuse
Returns the diffuse color setting, if it has been set.
Definition: material.h:116
has_base_color
Returns true if the base color has been explicitly set for this material, false otherwise.
Definition: material.h:112
get_emission
Returns the emission color setting, if it has been set.
Definition: material.h:120
has_diffuse
Returns true if the diffuse color has been explicitly set for this material, false otherwise.
Definition: material.h:116
int compare_to(const Material &other) const
Returns a number less than zero if this material sorts before the other one, greater than zero if it ...
Definition: material.cxx:349
void add_stdfloat(PN_stdfloat value)
Adds either a 32-bit or a 64-bit floating-point number, according to set_stdfloat_double().
Definition: datagram.I:133
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
set_emission
Specifies the emission color setting of the material.
Definition: material.h:120
get_metallic
Returns the metallic setting, if it has been set.
Definition: material.h:124
get_base_color
Returns the base_color color setting, if it has been set.
Definition: material.h:112
get_shininess
Returns the shininess exponent of the material.
Definition: material.h:122
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
get_refractive_index
Returns the index of refraction, or 1 if none has been set for this material.
Definition: material.h:126
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
get_ambient
Returns the ambient color setting, if it has been set.
Definition: material.h:114
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_refractive_index() const
Returns true if a refractive index has explicitly been specified for this material.
Definition: material.I:237
Defines the way an object appears in the presence of lighting.
Definition: material.h:43
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
set_roughness
Sets the roughness exponent of the material, where 0 is completely shiny (infinite shininess),...
Definition: material.h:123
A class to retrieve the individual data elements previously stored in a Datagram.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
get_specular
Returns the specular color setting, if it has been set.
Definition: material.h:118
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
static void register_with_read_factory()
Factory method to generate a Material object.
Definition: material.cxx:459
set_refractive_index
Sets the index of refraction of the material, which is used to determine the specular color in absenc...
Definition: material.h:126
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
has_specular
Returns true if the specular color has been explicitly set for this material, false otherwise.
Definition: material.h:118
has_emission
Returns true if the emission color has been explicitly set for this material, false otherwise.
Definition: material.h:120