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
22TypeHandle Material::_type_handle;
23PT(Material) Material::_default;
24
25/**
26 *
27 */
28void Material::
29operator = (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 */
59void Material::
60set_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 */
85void Material::
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 */
120void Material::
121set_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 */
139void Material::
140set_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 */
160void Material::
161set_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 */
172void Material::
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 */
199void Material::
200set_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 */
218void Material::
219set_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 */
228PN_stdfloat Material::
229get_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 */
247void Material::
248set_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 */
271void Material::
272set_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 */
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 */
326void Material::
327set_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 */
349compare_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 }
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 */
384void Material::
385output(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 */
421void Material::
422write(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 */
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 */
468write_datagram(BamWriter *manager, Datagram &me) {
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 */
505TypedWritable *Material::
506make_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 */
521void Material::
522fillin(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);
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) {
542 } else {
543 _shininess = scan.get_stdfloat();
544 }
545 _refractive_index = scan.get_stdfloat();
546
547 if ((_flags & (F_base_color | F_metallic)) == (F_base_color | F_metallic)) {
548 // Compute the ambient, diffuse and specular settings.
549 set_base_color(_base_color);
550 }
551
552 } else {
553 _ambient.read_datagram(scan);
554 _diffuse.read_datagram(scan);
555 _specular.read_datagram(scan);
556 _emission.read_datagram(scan);
557 _shininess = scan.get_stdfloat();
558 _flags = scan.get_int32();
559
560 if (_flags & F_roughness) {
561 // The shininess we read is actually a roughness value.
562 set_roughness(_shininess);
563 }
564 }
565
566 if (is_used_by_auto_shader()) {
567 GraphicsStateGuardianBase::mark_rehash_generated_shaders();
568 }
569}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
A class to retrieve the individual data elements previously stored in a Datagram.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
std::string get_string()
Extracts a variable-length string.
int32_t get_int32()
Extracts a signed 32-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
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 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
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
Defines the way an object appears in the presence of lighting.
Definition: material.h:43
get_ambient
Returns the ambient color setting, if it has been set.
Definition: material.h:114
has_specular
Returns true if the specular color has been explicitly set for this material, false otherwise.
Definition: material.h:118
set_emission
Specifies the emission color setting of the material.
Definition: material.h:120
bool has_metallic() const
Returns true if the metallic has been explicitly set for this material, false otherwise.
Definition: material.I:219
set_diffuse
Specifies the diffuse color setting of the material.
Definition: material.h:116
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
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
static void register_with_read_factory()
Factory method to generate a Material object.
Definition: material.cxx:459
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
has_base_color
Returns true if the base color has been explicitly set for this material, false otherwise.
Definition: material.h:112
set_roughness
Sets the roughness exponent of the material, where 0 is completely shiny (infinite shininess),...
Definition: material.h:123
void clear_metallic()
Removes the explicit metallic setting from the material.
Definition: material.cxx:297
set_base_color
Specifies the base color of the material.
Definition: material.h:112
get_base_color
Returns the base_color color setting, if it has been set.
Definition: material.h:112
get_refractive_index
Returns the index of refraction, or 1 if none has been set for this material.
Definition: material.h:126
clear_specular
Removes the explicit specular color from the material.
Definition: material.h:118
set_specular
Specifies the specular color setting of the material.
Definition: material.h:118
get_twoside
Returns the state of the two-sided lighting flag.
Definition: material.h:129
set_ambient
Specifies the ambient color setting of the material.
Definition: material.h:114
get_specular
Returns the specular color setting, if it has been set.
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
has_ambient
Returns true if the ambient color has been explicitly set for this material, false otherwise.
Definition: material.h:114
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
get_metallic
Returns the metallic setting, if it has been set.
Definition: material.h:124
get_roughness
Returns the roughness previously specified by set_roughness.
Definition: material.h:123
clear_base_color
Removes the explicit base_color color from the material.
Definition: material.h:112
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
get_shininess
Returns the shininess exponent of the material.
Definition: material.h:122
bool has_refractive_index() const
Returns true if a refractive index has explicitly been specified for this material.
Definition: material.I:237
get_diffuse
Returns the diffuse color setting, if it has been set.
Definition: material.h:116
get_local
Returns the local viewer flag.
Definition: material.h:128
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.