diff --git a/panda/src/egg/eggMaterial.I b/panda/src/egg/eggMaterial.I index 7aff647b4b..9fcee231c3 100644 --- a/panda/src/egg/eggMaterial.I +++ b/panda/src/egg/eggMaterial.I @@ -246,6 +246,96 @@ get_shininess() const { } } +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::set_roughness +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggMaterial:: +set_roughness(double roughness) { + _roughness = roughness; + _flags |= F_roughness; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::clear_roughness +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggMaterial:: +clear_roughness() { + _flags &= ~F_roughness; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::has_roughness +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggMaterial:: +has_roughness() const { + return (_flags & F_roughness) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::get_roughness +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE double EggMaterial:: +get_roughness() const { + if (has_roughness()) { + return _roughness; + } else { + return 1.0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::set_metallic +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggMaterial:: +set_metallic(double metallic) { + _metallic = metallic; + _flags |= F_metallic; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::clear_metallic +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggMaterial:: +clear_metallic() { + _flags &= ~F_metallic; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::has_metallic +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggMaterial:: +has_metallic() const { + return (_flags & F_metallic) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::get_metallic +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE double EggMaterial:: +get_metallic() const { + if (has_metallic()) { + return _metallic; + } else { + return 0.0; + } +} + //////////////////////////////////////////////////////////////////// // Function: EggMaterial::set_local // Access: Public diff --git a/panda/src/egg/eggMaterial.cxx b/panda/src/egg/eggMaterial.cxx index 6f76930d59..3a7007d025 100644 --- a/panda/src/egg/eggMaterial.cxx +++ b/panda/src/egg/eggMaterial.cxx @@ -44,6 +44,8 @@ EggMaterial(const EggMaterial ©) _emit(copy._emit), _spec(copy._spec), _shininess(copy._shininess), + _roughness(copy._roughness), + _metallic(copy._metallic), _local(copy._local), _flags(copy._flags) { @@ -117,6 +119,16 @@ write(ostream &out, int indent_level) const { << " shininess { " << get_shininess() << " }\n"; } + if (has_roughness()) { + indent(out, indent_level + 2) + << " roughness { " << get_roughness() << " }\n"; + } + + if (has_metallic()) { + indent(out, indent_level + 2) + << " metallic { " << get_metallic() << " }\n"; + } + if (has_local()) { indent(out, indent_level + 2) << " local { " << get_local() << " }\n"; @@ -152,6 +164,8 @@ is_equivalent_to(const EggMaterial &other, int eq) const { (has_emit() && get_emit() != other.get_emit()) || (has_spec() && get_spec() != other.get_spec()) || (has_shininess() && get_shininess() != other.get_shininess()) || + (has_roughness() && get_roughness() != other.get_roughness()) || + (has_metallic() && get_metallic() != other.get_metallic()) || (has_local() && get_local() != other.get_local())) { return false; } @@ -196,6 +210,12 @@ sorts_less_than(const EggMaterial &other, int eq) const { if (has_shininess() && get_shininess() != other.get_shininess()) { return get_shininess() < other.get_shininess(); } + if (has_roughness() && get_roughness() != other.get_roughness()) { + return get_roughness() < other.get_roughness(); + } + if (has_metallic() && get_metallic() != other.get_metallic()) { + return get_metallic() < other.get_metallic(); + } if (has_local() && get_local() != other.get_local()) { return get_local() < other.get_local(); } diff --git a/panda/src/egg/eggMaterial.h b/panda/src/egg/eggMaterial.h index 5f2813345f..33fcbf8abf 100644 --- a/panda/src/egg/eggMaterial.h +++ b/panda/src/egg/eggMaterial.h @@ -65,11 +65,32 @@ PUBLISHED: INLINE bool has_shininess() const; INLINE double get_shininess() const; + INLINE void set_roughness(double roughness); + INLINE void clear_roughness(); + INLINE bool has_roughness() const; + INLINE double get_roughness() const; + + INLINE void set_metallic(double metallic); + INLINE void clear_metallic(); + INLINE bool has_metallic() const; + INLINE double get_metallic() const; + INLINE void set_local(bool local); INLINE void clear_local(); INLINE bool has_local() const; INLINE bool get_local() const; +PUBLISHED: + MAKE_PROPERTY2(diff, has_diff, get_diff, set_diff, clear_diff); + MAKE_PROPERTY2(amb, has_amb, get_amb, set_amb, clear_amb); + MAKE_PROPERTY2(emit, has_emit, get_emit, set_emit, clear_emit); + MAKE_PROPERTY2(spec, has_spec, get_spec, set_spec, clear_spec); + MAKE_PROPERTY2(shininess, has_shininess, get_shininess, set_shininess, clear_shininess); + MAKE_PROPERTY2(roughness, has_roughness, get_roughness, set_roughness, clear_roughness); + MAKE_PROPERTY2(metallic, has_metallic, get_metallic, set_metallic, clear_metallic); + + MAKE_PROPERTY2(local, has_local, get_local, set_local, clear_local); + private: enum Flags { F_diff = 0x001, @@ -77,7 +98,9 @@ private: F_emit = 0x004, F_spec = 0x008, F_shininess = 0x010, - F_local = 0x020 + F_roughness = 0x020, + F_metallic = 0x040, + F_local = 0x080 }; LColor _diff; @@ -85,6 +108,8 @@ private: LColor _emit; LColor _spec; double _shininess; + double _roughness; + double _metallic; bool _local; int _flags; diff --git a/panda/src/egg/parser.cxx.prebuilt b/panda/src/egg/parser.cxx.prebuilt index 38f301df49..63c27af88e 100644 --- a/panda/src/egg/parser.cxx.prebuilt +++ b/panda/src/egg/parser.cxx.prebuilt @@ -3004,6 +3004,12 @@ yyreduce: } else if (cmp_nocase_uh(name, "shininess") == 0) { material->set_shininess(value); + } else if (cmp_nocase_uh(name, "roughness") == 0) { + material->set_roughness(value); + + } else if (cmp_nocase_uh(name, "metallic") == 0) { + material->set_metallic(value); + } else if (cmp_nocase_uh(name, "local") == 0) { material->set_local(value != 0.0); diff --git a/panda/src/egg/parser.yxx b/panda/src/egg/parser.yxx index 57f9488ef9..f786ac8d71 100644 --- a/panda/src/egg/parser.yxx +++ b/panda/src/egg/parser.yxx @@ -828,6 +828,12 @@ material_body: } else if (cmp_nocase_uh(name, "shininess") == 0) { material->set_shininess(value); + } else if (cmp_nocase_uh(name, "roughness") == 0) { + material->set_roughness(value); + + } else if (cmp_nocase_uh(name, "metallic") == 0) { + material->set_metallic(value); + } else if (cmp_nocase_uh(name, "local") == 0) { material->set_local(value != 0.0); diff --git a/panda/src/egg2pg/eggRenderState.cxx b/panda/src/egg2pg/eggRenderState.cxx index 508687e734..426240d998 100644 --- a/panda/src/egg2pg/eggRenderState.cxx +++ b/panda/src/egg2pg/eggRenderState.cxx @@ -513,6 +513,12 @@ get_material_attrib(const EggMaterial *egg_mat, bool bface) { if (egg_mat->has_shininess()) { mat->set_shininess(egg_mat->get_shininess()); } + if (egg_mat->has_roughness()) { + mat->set_roughness(egg_mat->get_roughness()); + } + if (egg_mat->has_metallic()) { + mat->set_metallic(egg_mat->get_metallic()); + } if (egg_mat->has_local()) { mat->set_local(egg_mat->get_local()); } diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index ba4ee35d7f..a8be565b83 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -1047,7 +1047,16 @@ get_egg_material(Material *mat) { temp.set_emit(mat->get_emission()); } - temp.set_shininess(mat->get_shininess()); + if (mat->has_roughness()) { + temp.set_roughness(mat->get_roughness()); + } else { + temp.set_shininess(mat->get_shininess()); + } + + if (mat->has_metallic()) { + temp.set_metallic(mat->get_metallic()); + } + temp.set_local(mat->get_local()); return _materials.create_unique_material(temp, ~EggMaterial::E_mref_name); diff --git a/panda/src/gobj/material.I b/panda/src/gobj/material.I index fd72d42545..724d33c5e2 100644 --- a/panda/src/gobj/material.I +++ b/panda/src/gobj/material.I @@ -24,7 +24,9 @@ Material(const string &name) : Namable(name) { _diffuse.set(1.0f, 1.0f, 1.0f, 1.0f); _specular.set(0.0f, 0.0f, 0.0f, 1.0f); _emission.set(0.0f, 0.0f, 0.0f, 1.0f); - _shininess = 0.0; + _shininess = 0; + _roughness = 1; + _metallic = 0; _flags = 0; } @@ -219,18 +221,50 @@ get_shininess() const { } //////////////////////////////////////////////////////////////////// -// Function: Material::set_shininess +// Function: Material::has_metallic // Access: Published -// Description: Sets the shininess exponent of the material. This -// controls the size of the specular highlight spot. In -// general, larger number produce a smaller specular -// highlight, which makes the object appear shinier. -// Smaller numbers produce a larger highlight, which -// makes the object appear less shiny. +// Description: Returns true if the metallic has been explicitly +// set for this material, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool Material:: +has_metallic() const { + return (_flags & F_metallic) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_metallic +// Access: Published +// Description: Returns the metallic setting, if it has been set. +// Returns 0 if it has not been set. +//////////////////////////////////////////////////////////////////// +INLINE PN_stdfloat Material:: +get_metallic() const { + return _metallic; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::clear_metallic +// Access: Published +// Description: Removes the explicit metallic setting from the material. //////////////////////////////////////////////////////////////////// INLINE void Material:: -set_shininess(PN_stdfloat shininess) { - _shininess = shininess; +clear_metallic() { + if (enforce_attrib_lock) { + nassertv(!is_attrib_locked()); + } + _flags &= ~F_metallic; + _metallic = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::has_roughness +// Access: Published +// Description: Returns true if the roughness has been explicitly +// set for this material, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool Material:: +has_roughness() const { + return (_flags & F_roughness) != 0; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/material.cxx b/panda/src/gobj/material.cxx index f16e9a714d..5d202f0c41 100644 --- a/panda/src/gobj/material.cxx +++ b/panda/src/gobj/material.cxx @@ -36,6 +36,8 @@ operator = (const Material ©) { _specular = copy._specular; _emission = copy._emission; _shininess = copy._shininess; + _roughness = copy._roughness; + _metallic = copy._metallic; _flags = copy._flags & (~F_attrib_lock); } @@ -136,6 +138,93 @@ set_emission(const LColor &color) { _flags |= F_emission; } +//////////////////////////////////////////////////////////////////// +// Function: Material::set_shininess +// Access: Published +// Description: Sets the shininess exponent of the material. This +// controls the size of the specular highlight spot. In +// general, larger number produce a smaller specular +// highlight, which makes the object appear shinier. +// Smaller numbers produce a larger highlight, which +// makes the object appear less shiny. +// +// This is usually in the range 0..128. +// +// Setting a shininess value removes any previous +// roughness assignment. +//////////////////////////////////////////////////////////////////// +void Material:: +set_shininess(PN_stdfloat shininess) { + _shininess = shininess; + _flags &= ~F_roughness; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_roughness +// Access: Published +// Description: Returns the roughness previously specified by +// set_roughness. If none was previously set, this +// value is computed from the shininess value. +//////////////////////////////////////////////////////////////////// +PN_stdfloat Material:: +get_roughness() const { + if ((_flags & F_roughness) == 0) { + // Calculate roughness from blinn-phong shininess. + return csqrt(csqrt(2 / (_shininess + 2))); + } else { + return _roughness; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_roughness +// Access: Published +// Description: Sets the roughness exponent of the material, where +// 0 is completely shiny (infinite shininess), and +// 1 is a completely dull object (0 shininess). This +// is a different, more perceptually intuitive way of +// controlling the size of the specular spot, and more +// commonly used in physically-based rendering. +// +// Setting a roughness recalculates the shininess value. +//////////////////////////////////////////////////////////////////// +void Material:: +set_roughness(PN_stdfloat roughness) { + _roughness = roughness; + _flags |= F_roughness; + + // Calculate the specular exponent from the roughness as it is used + // in Blinn-Phong shading model. We use the popular Disney method + // of squaring the roughness to get a more perceptually linear scale. + // From: http://graphicrants.blogspot.de/2013/08/specular-brdf-reference.html + if (roughness <= 0 || IS_NEARLY_ZERO(roughness)) { + _shininess = make_inf((PN_stdfloat)0); + } else { + PN_stdfloat alpha = roughness * roughness; + _shininess = 2 / (alpha * alpha) - 2; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_metallic +// Access: Published +// Description: Sets the metallic setting of the material, which is +// is used for physically-based rendering models. +// This is usually 0 for dielectric materials and 1 +// for metals. It usually does not make sense to set +// this to a value other than 0 or 1. +//////////////////////////////////////////////////////////////////// +void Material:: +set_metallic(PN_stdfloat metallic) { + if (enforce_attrib_lock) { + if ((_flags & F_metallic) == 0) { + nassertv(!is_attrib_locked()); + } + } + _metallic = metallic; + _flags |= F_metallic; +} + //////////////////////////////////////////////////////////////////// // Function: Material::compare_to // Access: Published @@ -189,8 +278,15 @@ output(ostream &out) const { if (has_emission()) { out << " e(" << get_emission() << ")"; } - out << " s" << get_shininess() - << " l" << get_local() + if (_flags & F_roughness) { + out << " r" << get_roughness(); + } else { + out << " s" << get_shininess(); + } + if (has_metallic()) { + out << " m" << _metallic; + } + out << " l" << get_local() << " t" << get_twoside(); } @@ -214,7 +310,14 @@ write(ostream &out, int indent_level) const { if (has_emission()) { indent(out, indent_level + 2) << "emission = " << get_emission() << "\n"; } - indent(out, indent_level + 2) << "shininess = " << get_shininess() << "\n"; + if (_flags & F_roughness) { + indent(out, indent_level + 2) << "roughness = " << get_roughness() << "\n"; + } else { + indent(out, indent_level + 2) << "shininess = " << get_shininess() << "\n"; + } + if (has_metallic()) { + indent(out, indent_level + 2) << "metallic = " << get_metallic() << "\n"; + } indent(out, indent_level + 2) << "local = " << get_local() << "\n"; indent(out, indent_level + 2) << "twoside = " << get_twoside() << "\n"; } @@ -245,7 +348,18 @@ write_datagram(BamWriter *manager, Datagram &me) { _specular.write_datagram(me); _emission.write_datagram(me); me.add_stdfloat(_shininess); + + if (_flags & F_roughness) { + me.add_stdfloat(_roughness); + } else { + me.add_stdfloat(_shininess); + } + me.add_int32(_flags); + + if (_flags & F_metallic) { + me.add_stdfloat(_metallic); + } } //////////////////////////////////////////////////////////////////// @@ -281,4 +395,13 @@ fillin(DatagramIterator &scan, BamReader *manager) { _emission.read_datagram(scan); _shininess = scan.get_stdfloat(); _flags = scan.get_int32(); + + if (_flags & F_roughness) { + // The shininess we read is actually a roughness value. + set_roughness(_shininess); + } + + if (_flags & F_metallic) { + _metallic = scan.get_stdfloat(); + } } diff --git a/panda/src/gobj/material.h b/panda/src/gobj/material.h index d9accd24dc..d693bd9281 100644 --- a/panda/src/gobj/material.h +++ b/panda/src/gobj/material.h @@ -44,40 +44,38 @@ PUBLISHED: INLINE const LColor &get_ambient() const; void set_ambient(const LColor &color); INLINE void clear_ambient(); - MAKE_PROPERTY2(ambient, has_ambient, get_ambient, - set_ambient, clear_ambient); INLINE bool has_diffuse() const; INLINE const LColor &get_diffuse() const; void set_diffuse(const LColor &color); INLINE void clear_diffuse(); - MAKE_PROPERTY2(diffuse, has_diffuse, get_diffuse, - set_diffuse, clear_diffuse); INLINE bool has_specular() const; INLINE const LColor &get_specular() const; void set_specular(const LColor &color); INLINE void clear_specular(); - MAKE_PROPERTY2(specular, has_specular, get_specular, - set_specular, clear_specular); INLINE bool has_emission() const; INLINE const LColor &get_emission() const; void set_emission(const LColor &color); INLINE void clear_emission(); - MAKE_PROPERTY2(emission, has_emission, get_emission, - set_emission, clear_emission); INLINE PN_stdfloat get_shininess() const; - INLINE void set_shininess(PN_stdfloat shininess); - MAKE_PROPERTY(shininess, get_shininess, set_shininess); + void set_shininess(PN_stdfloat shininess); + + INLINE bool has_roughness() const; + PN_stdfloat get_roughness() const; + void set_roughness(PN_stdfloat roughness); + + INLINE bool has_metallic() const; + INLINE PN_stdfloat get_metallic() const; + void set_metallic(PN_stdfloat metallic); + INLINE void clear_metallic(); INLINE bool get_local() const; INLINE void set_local(bool local); INLINE bool get_twoside() const; INLINE void set_twoside(bool twoside); - MAKE_PROPERTY(local, get_local, set_local); - MAKE_PROPERTY(twoside, get_twoside, set_twoside); INLINE bool operator == (const Material &other) const; INLINE bool operator != (const Material &other) const; @@ -91,12 +89,31 @@ PUBLISHED: INLINE bool is_attrib_locked() const; INLINE void set_attrib_lock(); +PUBLISHED: + MAKE_PROPERTY2(ambient, has_ambient, get_ambient, + set_ambient, clear_ambient); + MAKE_PROPERTY2(diffuse, has_diffuse, get_diffuse, + set_diffuse, clear_diffuse); + MAKE_PROPERTY2(specular, has_specular, get_specular, + set_specular, clear_specular); + MAKE_PROPERTY2(emission, has_emission, get_emission, + set_emission, clear_emission); + + MAKE_PROPERTY(shininess, get_shininess, set_shininess); + MAKE_PROPERTY(roughness, get_roughness, set_roughness); + MAKE_PROPERTY(metallic, get_metallic, set_metallic); + + MAKE_PROPERTY(local, get_local, set_local); + MAKE_PROPERTY(twoside, get_twoside, set_twoside); + private: LColor _ambient; LColor _diffuse; LColor _specular; LColor _emission; PN_stdfloat _shininess; + PN_stdfloat _roughness; + PN_stdfloat _metallic; static PT(Material) _default; @@ -107,7 +124,9 @@ private: F_emission = 0x008, F_local = 0x010, F_twoside = 0x020, - F_attrib_lock = 0x040 + F_attrib_lock = 0x040, + F_roughness = 0x080, + F_metallic = 0x100, }; int _flags;