Panda3D
Loading...
Searching...
No Matches
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...
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.
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.