working on material fallthrough

This commit is contained in:
SamGondelman 2018-12-20 16:03:58 -08:00
parent 21ec4e0c53
commit 9155853889
11 changed files with 776 additions and 395 deletions

View file

@ -228,7 +228,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
gpu::Batch& batch = *args->_batch; gpu::Batch& batch = *args->_batch;
std::shared_ptr<graphics::Material> mat; graphics::MultiMaterial materials;
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
GeometryCache::Shape geometryShape; GeometryCache::Shape geometryShape;
bool proceduralRender = false; bool proceduralRender = false;
@ -236,9 +236,11 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
withReadLock([&] { withReadLock([&] {
geometryShape = geometryCache->getShapeForEntityShape(_shape); geometryShape = geometryCache->getShapeForEntityShape(_shape);
batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation
mat = _materials["0"].top().material; materials = _materials["0"];
if (mat) { auto topMat = materials.top().material;
outColor = glm::vec4(mat->getAlbedo(), mat->getOpacity()); if (topMat) {
// FIXME: fallthrough to get proper albedo and opacity?
outColor = glm::vec4(topMat->getAlbedo(), topMat->getOpacity());
if (_procedural.isReady()) { if (_procedural.isReady()) {
outColor = _procedural.getColor(outColor); outColor = _procedural.getColor(outColor);
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f; outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
@ -248,10 +250,6 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
} }
}); });
if (!mat) {
return;
}
if (proceduralRender) { if (proceduralRender) {
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
geometryCache->renderWireShape(batch, geometryShape, outColor); geometryCache->renderWireShape(batch, geometryShape, outColor);
@ -269,7 +267,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
} }
} else { } else {
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing); RenderPipelines::bindMaterials(materials, batch, args->_enableTexturing);
args->_details._materialSwitches++; args->_details._materialSwitches++;
} }

View file

@ -40,13 +40,13 @@ namespace scriptable {
* @typedef {object} Graphics.Material * @typedef {object} Graphics.Material
* @property {string} name * @property {string} name
* @property {string} model * @property {string} model
* @property {number} opacity * @property {number|string} opacity
* @property {number} roughness * @property {number|string} roughness
* @property {number} metallic * @property {number|string} metallic
* @property {number} scattering * @property {number|string} scattering
* @property {boolean} unlit * @property {boolean|string} unlit
* @propety {Vec3} emissive * @propety {Vec3|string} emissive
* @propety {Vec3} albedo * @propety {Vec3|string} albedo
* @property {string} emissiveMap * @property {string} emissiveMap
* @property {string} albedoMap * @property {string} albedoMap
* @property {string} opacityMap * @property {string} opacityMap
@ -59,6 +59,7 @@ namespace scriptable {
* @property {string} occlusionMap * @property {string} occlusionMap
* @property {string} lightmapMap * @property {string} lightmapMap
* @property {string} scatteringMap * @property {string} scatteringMap
* @property {boolean} defaultFallthrough
*/ */
class ScriptableMaterial { class ScriptableMaterial {
public: public:
@ -88,6 +89,9 @@ namespace scriptable {
QString occlusionMap; QString occlusionMap;
QString lightmapMap; QString lightmapMap;
QString scatteringMap; QString scatteringMap;
bool defaultFallthrough;
std::unordered_map<graphics::MaterialKey::FlagBit, bool> propertyFallthroughs; // not actually exposed to script
}; };
/**jsdoc /**jsdoc

View file

@ -362,25 +362,44 @@ namespace scriptable {
QScriptValue obj = engine->newObject(); QScriptValue obj = engine->newObject();
obj.setProperty("name", material.name); obj.setProperty("name", material.name);
obj.setProperty("model", material.model); obj.setProperty("model", material.model);
obj.setProperty("opacity", material.opacity);
obj.setProperty("roughness", material.roughness); const QScriptValue FALLTHROUGH("fallthrough");
obj.setProperty("metallic", material.metallic); obj.setProperty("opacity", material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT) ? FALLTHROUGH : material.opacity);
obj.setProperty("scattering", material.scattering); obj.setProperty("roughness", material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT) ? FALLTHROUGH : material.roughness);
obj.setProperty("unlit", material.unlit); obj.setProperty("metallic", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT) ? FALLTHROUGH : material.metallic);
obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive)); obj.setProperty("scattering", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT) ? FALLTHROUGH : material.scattering);
obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo)); obj.setProperty("unlit", material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT) ? FALLTHROUGH : material.unlit);
obj.setProperty("emissiveMap", material.emissiveMap); obj.setProperty("emissive", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.emissive));
obj.setProperty("albedoMap", material.albedoMap); obj.setProperty("albedo", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.albedo));
obj.setProperty("emissiveMap", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_MAP_BIT) ? FALLTHROUGH : material.emissiveMap);
obj.setProperty("albedoMap", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT) ? FALLTHROUGH : material.albedoMap);
obj.setProperty("opacityMap", material.opacityMap); obj.setProperty("opacityMap", material.opacityMap);
obj.setProperty("metallicMap", material.metallicMap); obj.setProperty("occlusionMap", material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT) ? FALLTHROUGH : material.occlusionMap);
obj.setProperty("specularMap", material.specularMap); obj.setProperty("lightmapMap", material.propertyFallthroughs.at(graphics::MaterialKey::LIGHTMAP_MAP_BIT) ? FALLTHROUGH : material.lightmapMap);
obj.setProperty("roughnessMap", material.roughnessMap); obj.setProperty("scatteringMap", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT) ? FALLTHROUGH : material.scatteringMap);
obj.setProperty("glossMap", material.glossMap);
obj.setProperty("normalMap", material.normalMap); // Only set one of each of these
obj.setProperty("bumpMap", material.bumpMap); if (!material.metallicMap.isEmpty()) {
obj.setProperty("occlusionMap", material.occlusionMap); obj.setProperty("metallicMap", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT) ? FALLTHROUGH : material.metallicMap);
obj.setProperty("lightmapMap", material.lightmapMap); } else {
obj.setProperty("scatteringMap", material.scatteringMap); obj.setProperty("specularMap", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT) ? FALLTHROUGH : material.specularMap);
}
if (!material.roughnessMap.isEmpty()) {
obj.setProperty("roughnessMap", material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT) ? FALLTHROUGH : material.roughnessMap);
} else {
obj.setProperty("glossMap", material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT) ? FALLTHROUGH : material.glossMap);
}
if (!material.normalMap.isEmpty()) {
obj.setProperty("normalMap", material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT) ? FALLTHROUGH : material.normalMap);
} else {
obj.setProperty("bumpMap", material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT) ? FALLTHROUGH : material.bumpMap);
}
obj.setProperty("defaultFallthrough", material.defaultFallthrough);
return obj; return obj;
} }

View file

@ -42,6 +42,9 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const
lightmapMap = material.lightmapMap; lightmapMap = material.lightmapMap;
scatteringMap = material.scatteringMap; scatteringMap = material.scatteringMap;
defaultFallthrough = material.defaultFallthrough;
propertyFallthroughs = material.propertyFallthroughs;
return *this; return *this;
} }
@ -54,7 +57,9 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint
scattering(material->getScattering()), scattering(material->getScattering()),
unlit(material->isUnlit()), unlit(material->isUnlit()),
emissive(material->getEmissive()), emissive(material->getEmissive()),
albedo(material->getAlbedo()) albedo(material->getAlbedo()),
defaultFallthrough(material->getDefaultFallthrough()),
propertyFallthroughs(material->getPropertyFallthroughs())
{ {
auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP); auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP);
if (map && map->getTextureSource()) { if (map && map->getTextureSource()) {

View file

@ -17,125 +17,126 @@
using namespace graphics; using namespace graphics;
using namespace gpu; using namespace gpu;
Material::Material() : const float Material::DEFAULT_EMISSIVE { 0.0f };
_key(0), const float Material::DEFAULT_OPACITY { 1.0f };
_schemaBuffer(), const float Material::DEFAULT_ALBEDO { 0.5f };
_textureMaps() const float Material::DEFAULT_METALLIC { 0.0f };
{ const float Material::DEFAULT_ROUGHNESS { 1.0f };
// created from nothing: create the Buffer to store the properties const float Material::DEFAULT_SCATTERING { 0.0f };
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); Material::Material() {
for (int i = 0; i < graphics::MaterialKey::NUM_FLAGS; i++) {
_propertyFallthroughs[graphics::MaterialKey::FlagBit(i)] = false;
}
} }
Material::Material(const Material& material) : Material::Material(const Material& material) :
_name(material._name), _name(material._name),
_model(material._model),
_key(material._key), _key(material._key),
_textureMaps(material._textureMaps) _emissive(material._emissive),
_opacity(material._opacity),
_albedo(material._albedo),
_roughness(material._roughness),
_metallic(material._metallic),
_scattering(material._scattering),
_texcoordTransforms(material._texcoordTransforms),
_lightmapParams(material._lightmapParams),
_materialParams(material._materialParams),
_textureMaps(material._textureMaps),
_defaultFallthrough(material._defaultFallthrough),
_propertyFallthroughs(material._propertyFallthroughs)
{ {
// copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
_schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>();
} }
Material& Material::operator= (const Material& material) { Material& Material::operator=(const Material& material) {
QMutexLocker locker(&_textureMapsMutex); QMutexLocker locker(&_textureMapsMutex);
_name = material._name; _name = material._name;
_model = material._model;
_key = material._key;
_emissive = material._emissive;
_opacity = material._opacity;
_albedo = material._albedo;
_roughness = material._roughness;
_metallic = material._metallic;
_scattering = material._scattering;
_texcoordTransforms = material._texcoordTransforms;
_lightmapParams = material._lightmapParams;
_materialParams = material._materialParams;
_textureMaps = material._textureMaps;
_key = (material._key); _defaultFallthrough = material._defaultFallthrough;
_textureMaps = (material._textureMaps); _propertyFallthroughs = material._propertyFallthroughs;
_hasCalculatedTextureInfo = false;
// copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
_schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>();
return (*this); return (*this);
} }
Material::~Material() { void Material::setEmissive(const glm::vec3& emissive, bool isSRGB) {
} _key.setEmissive(glm::any(glm::greaterThan(emissive, glm::vec3(0.0f))));
_emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive);
void Material::setEmissive(const Color& emissive, bool isSRGB) {
_key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f))));
_schemaBuffer.edit<Schema>()._key = (uint32) _key._flags.to_ulong();
_schemaBuffer.edit<Schema>()._emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive);
} }
void Material::setOpacity(float opacity) { void Material::setOpacity(float opacity) {
_key.setTranslucentFactor((opacity < 1.0f)); _key.setTranslucentFactor((opacity < 1.0f));
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); _opacity = opacity;
_schemaBuffer.edit<Schema>()._opacity = opacity;
} }
void Material::setUnlit(bool value) { void Material::setUnlit(bool value) {
_key.setUnlit(value); _key.setUnlit(value);
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
} }
void Material::setAlbedo(const Color& albedo, bool isSRGB) { void Material::setAlbedo(const glm::vec3& albedo, bool isSRGB) {
_key.setAlbedo(glm::any(glm::greaterThan(albedo, Color(0.0f)))); _key.setAlbedo(glm::any(glm::greaterThan(albedo, glm::vec3(0.0f))));
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); _albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo);
_schemaBuffer.edit<Schema>()._albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo);
} }
void Material::setRoughness(float roughness) { void Material::setRoughness(float roughness) {
roughness = std::min(1.0f, std::max(roughness, 0.0f)); roughness = std::min(1.0f, std::max(roughness, 0.0f));
_key.setGlossy((roughness < 1.0f)); _key.setGlossy(roughness < 1.0f);
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); _roughness = roughness;
_schemaBuffer.edit<Schema>()._roughness = roughness;
} }
void Material::setMetallic(float metallic) { void Material::setMetallic(float metallic) {
metallic = glm::clamp(metallic, 0.0f, 1.0f); metallic = glm::clamp(metallic, 0.0f, 1.0f);
_key.setMetallic(metallic > 0.0f); _key.setMetallic(metallic > 0.0f);
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); _metallic = metallic;
_schemaBuffer.edit<Schema>()._metallic = metallic;
} }
void Material::setScattering(float scattering) { void Material::setScattering(float scattering) {
scattering = glm::clamp(scattering, 0.0f, 1.0f); scattering = glm::clamp(scattering, 0.0f, 1.0f);
_key.setMetallic(scattering > 0.0f); _key.setMetallic(scattering > 0.0f);
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); _scattering = scattering;
_schemaBuffer.edit<Schema>()._scattering = scattering;
} }
void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) { void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) {
QMutexLocker locker(&_textureMapsMutex); QMutexLocker locker(&_textureMapsMutex);
if (textureMap) { if (textureMap) {
_key.setMapChannel(channel, (true)); _key.setMapChannel(channel, true);
_textureMaps[channel] = textureMap; _textureMaps[channel] = textureMap;
} else { } else {
_key.setMapChannel(channel, (false)); _key.setMapChannel(channel, false);
_textureMaps.erase(channel); _textureMaps.erase(channel);
} }
_hasCalculatedTextureInfo = false; _hasCalculatedTextureInfo = false;
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
if (channel == MaterialKey::ALBEDO_MAP) { if (channel == MaterialKey::ALBEDO_MAP) {
resetOpacityMap(); resetOpacityMap();
_texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
// update the texcoord0 with albedo
_schemaBuffer.edit<Schema>()._texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
} }
if (channel == MaterialKey::OCCLUSION_MAP) { if (channel == MaterialKey::OCCLUSION_MAP) {
_schemaBuffer.edit<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); _texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
} }
if (channel == MaterialKey::LIGHTMAP_MAP) { if (channel == MaterialKey::LIGHTMAP_MAP) {
// update the texcoord1 with lightmap // update the texcoord1 with lightmap
_schemaBuffer.edit<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); _texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
_schemaBuffer.edit<Schema>()._lightmapParams = (textureMap ? glm::vec2(textureMap->getLightmapOffsetScale()) : glm::vec2(0.0, 1.0)); _lightmapParams = (textureMap ? glm::vec2(textureMap->getLightmapOffsetScale()) : glm::vec2(0.0, 1.0));
} }
_schemaBuffer.edit<Schema>()._materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0)); _materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0));
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
} }
@ -163,11 +164,8 @@ void Material::resetOpacityMap() const {
} }
} }
} }
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
} }
const TextureMapPointer Material::getTextureMap(MapChannel channel) const { const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
QMutexLocker locker(&_textureMapsMutex); QMutexLocker locker(&_textureMapsMutex);
@ -179,7 +177,6 @@ const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
} }
} }
bool Material::calculateMaterialInfo() const { bool Material::calculateMaterialInfo() const {
if (!_hasCalculatedTextureInfo) { if (!_hasCalculatedTextureInfo) {
QMutexLocker locker(&_textureMapsMutex); QMutexLocker locker(&_textureMapsMutex);
@ -222,7 +219,12 @@ void Material::setTextureTransforms(const Transform& transform, MaterialMappingM
} }
} }
for (int i = 0; i < NUM_TEXCOORD_TRANSFORMS; i++) { for (int i = 0; i < NUM_TEXCOORD_TRANSFORMS; i++) {
_schemaBuffer.edit<Schema>()._texcoordTransforms[i] = transform.getMatrix(); _texcoordTransforms[i] = transform.getMatrix();
} }
_schemaBuffer.edit<Schema>()._materialParams = glm::vec2(mode, repeat); _materialParams = glm::vec2(mode, repeat);
} }
MultiMaterial::MultiMaterial() {
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
}

View file

@ -14,7 +14,7 @@
#include <QMutex> #include <QMutex>
#include <bitset> #include <bitset>
#include <map> #include <unordered_map>
#include <queue> #include <queue>
#include <ColorUtils.h> #include <ColorUtils.h>
@ -266,84 +266,44 @@ public:
class Material { class Material {
public: public:
typedef gpu::BufferView UniformBufferView;
typedef glm::vec3 Color;
// Texture Map Array Schema
static const int NUM_TEXCOORD_TRANSFORMS{ 2 };
typedef MaterialKey::MapChannel MapChannel; typedef MaterialKey::MapChannel MapChannel;
typedef std::map<MapChannel, TextureMapPointer> TextureMaps; typedef std::unordered_map<MapChannel, TextureMapPointer> TextureMaps;
typedef std::bitset<MaterialKey::NUM_MAP_CHANNELS> MapFlags;
Material(); Material();
Material(const Material& material); Material(const Material& material);
Material& operator= (const Material& material); Material& operator= (const Material& material);
virtual ~Material();
const MaterialKey& getKey() const { return _key; } const MaterialKey& getKey() const { return _key; }
void setEmissive(const Color& emissive, bool isSRGB = true); static const float DEFAULT_EMISSIVE;
Color getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get<Schema>()._emissive) : _schemaBuffer.get<Schema>()._emissive); } void setEmissive(const glm::vec3& emissive, bool isSRGB = true);
glm::vec3 getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_emissive) : _emissive); }
static const float DEFAULT_OPACITY;
void setOpacity(float opacity); void setOpacity(float opacity);
float getOpacity() const { return _schemaBuffer.get<Schema>()._opacity; } float getOpacity() const { return _opacity; }
void setUnlit(bool value); void setUnlit(bool value);
bool isUnlit() const { return _key.isUnlit(); } bool isUnlit() const { return _key.isUnlit(); }
void setAlbedo(const Color& albedo, bool isSRGB = true); static const float DEFAULT_ALBEDO;
Color getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get<Schema>()._albedo) : _schemaBuffer.get<Schema>()._albedo); } void setAlbedo(const glm::vec3& albedo, bool isSRGB = true);
glm::vec3 getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_albedo) : _albedo); }
static const float DEFAULT_METALLIC;
void setMetallic(float metallic); void setMetallic(float metallic);
float getMetallic() const { return _schemaBuffer.get<Schema>()._metallic; } float getMetallic() const { return _metallic; }
static const float DEFAULT_ROUGHNESS;
void setRoughness(float roughness); void setRoughness(float roughness);
float getRoughness() const { return _schemaBuffer.get<Schema>()._roughness; } float getRoughness() const { return _roughness; }
static const float DEFAULT_SCATTERING;
void setScattering(float scattering); void setScattering(float scattering);
float getScattering() const { return _schemaBuffer.get<Schema>()._scattering; } float getScattering() const { return _scattering; }
// Schema to access the attribute values of the material
class Schema {
public:
glm::vec3 _emissive { 0.0f }; // No Emissive
float _opacity { 1.0f }; // Opacity = 1 => Not Transparent
glm::vec3 _albedo { 0.5f }; // Grey albedo => isAlbedo
float _roughness { 1.0f }; // Roughness = 1 => Not Glossy
float _metallic { 0.0f }; // Not Metallic
float _scattering { 0.0f }; // Scattering info
#if defined(__clang__)
__attribute__((unused))
#endif
glm::vec2 _spare { 0.0f }; // Padding
uint32_t _key { 0 }; // a copy of the materialKey
#if defined(__clang__)
__attribute__((unused))
#endif
glm::vec3 _spare2 { 0.0f };
// for alignment beauty, Material size == Mat4x4
// Texture Coord Transform Array
glm::mat4 _texcoordTransforms[NUM_TEXCOORD_TRANSFORMS];
glm::vec2 _lightmapParams { 0.0, 1.0 };
// x: material mode (0 for UV, 1 for PROJECTED)
// y: 1 for texture repeat, 0 for discard outside of 0 - 1
glm::vec2 _materialParams { 0.0, 1.0 };
Schema() {}
};
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
// The texture map to channel association // The texture map to channel association
static const int NUM_TEXCOORD_TRANSFORMS { 2 };
void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap); void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap);
const TextureMaps& getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe... const TextureMaps& getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe...
const TextureMapPointer getTextureMap(MapChannel channel) const; const TextureMapPointer getTextureMap(MapChannel channel) const;
@ -366,18 +326,34 @@ public:
const std::string& getModel() const { return _model; } const std::string& getModel() const { return _model; }
void setModel(const std::string& model) { _model = model; } void setModel(const std::string& model) { _model = model; }
const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } bool getDefaultFallthrough() const { return _defaultFallthrough; }
void setDefaultFallthrough(bool defaultFallthrough) { _defaultFallthrough = defaultFallthrough; }
std::unordered_map<MaterialKey::FlagBit, bool> getPropertyFallthroughs() { return _propertyFallthroughs; }
bool getPropertyFallthrough(MaterialKey::FlagBit property) { return _propertyFallthroughs[property]; }
void setPropertyDoesFallthrough(MaterialKey::FlagBit property) { _propertyFallthroughs[property] = true; }
protected: protected:
std::string _name { "" }; std::string _name { "" };
private: private:
mutable MaterialKey _key; mutable MaterialKey _key { 0 };
mutable UniformBufferView _schemaBuffer;
mutable gpu::TextureTablePointer _textureTable{ std::make_shared<gpu::TextureTable>() };
// Material properties
glm::vec3 _emissive { DEFAULT_EMISSIVE };
float _opacity { DEFAULT_OPACITY };
glm::vec3 _albedo { DEFAULT_ALBEDO };
float _roughness { DEFAULT_ROUGHNESS };
float _metallic { DEFAULT_METALLIC };
float _scattering { DEFAULT_SCATTERING };
std::array<glm::mat4, NUM_TEXCOORD_TRANSFORMS> _texcoordTransforms;
glm::vec2 _lightmapParams { 0.0, 1.0 };
glm::vec2 _materialParams { 0.0, 1.0 };
TextureMaps _textureMaps; TextureMaps _textureMaps;
bool _defaultFallthrough { false };
std::unordered_map<MaterialKey::FlagBit, bool> _propertyFallthroughs;
mutable QMutex _textureMapsMutex { QMutex::Recursive }; mutable QMutex _textureMapsMutex { QMutex::Recursive };
mutable size_t _textureSize { 0 }; mutable size_t _textureSize { 0 };
mutable int _textureCount { 0 }; mutable int _textureCount { 0 };
@ -385,7 +361,6 @@ private:
bool calculateMaterialInfo() const; bool calculateMaterialInfo() const;
std::string _model { "hifi_pbr" }; std::string _model { "hifi_pbr" };
}; };
typedef std::shared_ptr< Material > MaterialPointer; typedef std::shared_ptr< Material > MaterialPointer;
@ -406,6 +381,8 @@ public:
class MultiMaterial : public std::priority_queue<MaterialLayer, std::vector<MaterialLayer>, MaterialLayerCompare> { class MultiMaterial : public std::priority_queue<MaterialLayer, std::vector<MaterialLayer>, MaterialLayerCompare> {
public: public:
MultiMaterial();
bool remove(const MaterialPointer& value) { bool remove(const MaterialPointer& value) {
auto it = c.begin(); auto it = c.begin();
while (it != c.end()) { while (it != c.end()) {
@ -422,6 +399,49 @@ public:
return false; return false;
} }
} }
// Schema to access the attribute values of the material
class Schema {
public:
glm::vec3 _emissive { Material::DEFAULT_EMISSIVE }; // No Emissive
float _opacity { Material::DEFAULT_OPACITY }; // Opacity = 1 => Not Transparent
glm::vec3 _albedo { Material::DEFAULT_ALBEDO }; // Grey albedo => isAlbedo
float _roughness { Material::DEFAULT_ROUGHNESS }; // Roughness = 1 => Not Glossy
float _metallic { Material::DEFAULT_METALLIC }; // Not Metallic
float _scattering { Material::DEFAULT_SCATTERING }; // Scattering info
#if defined(__clang__)
__attribute__((unused))
#endif
glm::vec2 _spare { 0.0f }; // Padding
uint32_t _key { 0 }; // a copy of the materialKey
#if defined(__clang__)
__attribute__((unused))
#endif
glm::vec3 _spare2 { 0.0f };
// for alignment beauty, Material size == Mat4x4
// Texture Coord Transform Array
glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS];
glm::vec2 _lightmapParams { 0.0, 1.0 };
// x: material mode (0 for UV, 1 for PROJECTED)
// y: 1 for texture repeat, 0 for discard outside of 0 - 1
glm::vec2 _materialParams { 0.0, 1.0 };
Schema() {}
};
gpu::BufferView& getSchemaBuffer() { return _schemaBuffer; }
const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; }
private:
gpu::BufferView _schemaBuffer;
gpu::TextureTablePointer _textureTable { std::make_shared<gpu::TextureTable>() };
}; };
}; };

View file

@ -111,146 +111,253 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
/**jsdoc /**jsdoc
* A material such as may be used by a {@link Entities.EntityType|Material} entity. * A material such as may be used by a {@link Entities.EntityType|Material} entity.
* @typedef {object} Material * @typedef {object} Material
* @property {string} name="" - A name for the material. * @property {string} model="hifi_pbr" - Different material models support different properties and rendering modes.
* @property {string} model="hifi_pbr" - <em>Currently not used.</em> * Supported models are: "hifi_pbr"
* @property {Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value * @property {string} name="" - A name for the material. Supported by all material models.
* is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB. * @property {Color|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value
* @property {number} opacity=1.0 - The opacity, <code>0.0</code> &ndash; <code>1.0</code>. * is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB. Set to <code>"fallthrough"</code> to fallthrough to
* @property {boolean} unlit=false - If <code>true</code>, the material is not lit. * the material below. "hifi_pbr" model only.
* @property {Color|RGBS} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can * @property {number|string} opacity=1.0 - The opacity, <code>0.0</code> &ndash; <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to
* be either RGB or sRGB. * the material below. "hifi_pbr" model only.
* @property {number} roughness - The roughness, <code>0.0</code> &ndash; <code>1.0</code>. * @property {boolean|string} unlit=false - If <code>true</code>, the material is not lit. Set to <code>"fallthrough"</code> to fallthrough to
* @property {number} metallic - The metallicness, <code>0.0</code> &ndash; <code>1.0</code>. * the material below. "hifi_pbr" model only.
* @property {number} scattering - The scattering, <code>0.0</code> &ndash; <code>1.0</code>. * @property {Color|RGBS|string} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can
* @property {string} emissiveMap - URL of emissive texture image. * be either RGB or sRGB. Set to <code>"fallthrough"</code> to fallthrough to the material below. Set to <code>"fallthrough"</code> to fallthrough to
* @property {string} albedoMap - URL of albedo texture image. * the material below. "hifi_pbr" model only.
* @property {number|string} roughness - The roughness, <code>0.0</code> &ndash; <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to
* the material below. "hifi_pbr" model only.
* @property {number|string} metallic - The metallicness, <code>0.0</code> &ndash; <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to
* the material below. "hifi_pbr" model only.
* @property {number|string} scattering - The scattering, <code>0.0</code> &ndash; <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to
* the material below. "hifi_pbr" model only.
* @property {string} emissiveMap - URL of emissive texture image. Set to <code>"fallthrough"</code> to fallthrough to
* the material below. "hifi_pbr" model only.
* @property {string} albedoMap - URL of albedo texture image. Set to <code>"fallthrough"</code> to fallthrough to
* the material below. "hifi_pbr" model only.
* @property {string} opacityMap - URL of opacity texture image. Set value the same as the <code>albedoMap</code> value for * @property {string} opacityMap - URL of opacity texture image. Set value the same as the <code>albedoMap</code> value for
* transparency. * transparency. "hifi_pbr" model only.
* @property {string} roughnessMap - URL of roughness texture image. Can use this or <code>glossMap</code>, but not both. * @property {string} roughnessMap - URL of roughness texture image. Can use this or <code>glossMap</code>, but not both. Set to <code>"fallthrough"</code>
* @property {string} glossMap - URL of gloss texture image. Can use this or <code>roughnessMap</code>, but not both. * to fallthrough to the material below. "hifi_pbr" model only.
* @property {string} metallicMap - URL of metallic texture image. Can use this or <code>specularMap</code>, but not both. * @property {string} glossMap - URL of gloss texture image. Can use this or <code>roughnessMap</code>, but not both. Set to <code>"fallthrough"</code>
* @property {string} specularMap - URL of specular texture image. Can use this or <code>metallicMap</code>, but not both. * to fallthrough to the material below. "hifi_pbr" model only.
* @property {string} normalMap - URL of normal texture image. Can use this or <code>bumpMap</code>, but not both. * @property {string} metallicMap - URL of metallic texture image. Can use this or <code>specularMap</code>, but not both. Set to <code>"fallthrough"</code>
* @property {string} bumpMap - URL of bump texture image. Can use this or <code>normalMap</code>, but not both. * to fallthrough to the material below. "hifi_pbr" model only.
* @property {string} occlusionMap - URL of occlusion texture image. * @property {string} specularMap - URL of specular texture image. Can use this or <code>metallicMap</code>, but not both. Set to <code>"fallthrough"</code>
* to fallthrough to the material below. "hifi_pbr" model only.
* @property {string} normalMap - URL of normal texture image. Can use this or <code>bumpMap</code>, but not both. Set to <code>"fallthrough"</code>
* to fallthrough to the material below. "hifi_pbr" model only.
* @property {string} bumpMap - URL of bump texture image. Can use this or <code>normalMap</code>, but not both. Set to <code>"fallthrough"</code>
* to fallthrough to the material below. "hifi_pbr" model only.
* @property {string} occlusionMap - URL of occlusion texture image. Set to <code>"fallthrough"</code> to fallthrough to the material below. "hifi_pbr" model only.
* @property {string} scatteringMap - URL of scattering texture image. Only used if <code>normalMap</code> or * @property {string} scatteringMap - URL of scattering texture image. Only used if <code>normalMap</code> or
* <code>bumpMap</code> is specified. * <code>bumpMap</code> is specified. Set to <code>"fallthrough"</code> to fallthrough to the material below. "hifi_pbr" model only.
* @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em> * @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em>. Set to <code>"fallthrough"</code>
* to fallthrough to the material below. "hifi_pbr" model only.
* @property {bool} defaultFallthrough=false - If <code>true</code>, all properties will fallthrough to the material below unless they are set. If
* <code>false</code>, they will respect the individual properties' fallthrough state. "hifi_pbr" model only.
*/ */
// Note: See MaterialEntityItem.h for default values used in practice. // Note: See MaterialEntityItem.h for default values used in practice.
std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) { std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) {
std::string name = ""; std::string name = "";
std::shared_ptr<NetworkMaterial> material = std::make_shared<NetworkMaterial>(); std::shared_ptr<NetworkMaterial> material = std::make_shared<NetworkMaterial>();
for (auto& key : materialJSON.keys()) { std::string modelString = "hifi_pbr";
if (key == "name") { auto modelJSON = materialJSON.value("model");
auto nameJSON = materialJSON.value(key); if (modelJSON.isString()) {
if (nameJSON.isString()) { modelString = modelJSON.toString().toStdString();
name = nameJSON.toString().toStdString(); material->setModel(modelString);
} }
} else if (key == "model") { if (modelString == "hifi_pbr") {
auto modelJSON = materialJSON.value(key); const QString FALLTHROUGH("fallthrough");
if (modelJSON.isString()) { for (auto& key : materialJSON.keys()) {
material->setModel(modelJSON.toString().toStdString()); if (key == "name") {
} auto nameJSON = materialJSON.value(key);
} else if (key == "emissive") { if (nameJSON.isString()) {
glm::vec3 color; name = nameJSON.toString().toStdString();
bool isSRGB; }
bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB); } else if (key == "model") {
if (valid) { auto modelJSON = materialJSON.value(key);
material->setEmissive(color, isSRGB); if (modelJSON.isString()) {
} material->setModel(modelJSON.toString().toStdString());
} else if (key == "opacity") { }
auto value = materialJSON.value(key); } else if (key == "emissive") {
if (value.isDouble()) { auto value = materialJSON.value(key);
material->setOpacity(value.toDouble()); if (value.isString() && value.toString() == FALLTHROUGH) {
} material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_VAL_BIT);
} else if (key == "unlit") { } else {
auto value = materialJSON.value(key); glm::vec3 color;
if (value.isBool()) { bool isSRGB;
material->setUnlit(value.toBool()); bool valid = parseJSONColor(value, color, isSRGB);
} if (valid) {
} else if (key == "albedo") { material->setEmissive(color, isSRGB);
glm::vec3 color; }
bool isSRGB; }
bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB); } else if (key == "opacity") {
if (valid) { auto value = materialJSON.value(key);
material->setAlbedo(color, isSRGB); if (value.isString() && value.toString() == FALLTHROUGH) {
} material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_VAL_BIT);
} else if (key == "roughness") { } else if (value.isDouble()) {
auto value = materialJSON.value(key); material->setOpacity(value.toDouble());
if (value.isDouble()) { }
material->setRoughness(value.toDouble()); } else if (key == "unlit") {
} auto value = materialJSON.value(key);
} else if (key == "metallic") { if (value.isString() && value.toString() == FALLTHROUGH) {
auto value = materialJSON.value(key); material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT);
if (value.isDouble()) { } else if (value.isBool()) {
material->setMetallic(value.toDouble()); material->setUnlit(value.toBool());
} }
} else if (key == "scattering") { } else if (key == "albedo") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isDouble()) { if (value.isString() && value.toString() == FALLTHROUGH) {
material->setScattering(value.toDouble()); material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_VAL_BIT);
} } else {
} else if (key == "emissiveMap") { glm::vec3 color;
auto value = materialJSON.value(key); bool isSRGB;
if (value.isString()) { bool valid = parseJSONColor(value, color, isSRGB);
material->setEmissiveMap(baseUrl.resolved(value.toString())); if (valid) {
} material->setAlbedo(color, isSRGB);
} else if (key == "albedoMap") { }
auto value = materialJSON.value(key); }
if (value.isString()) { } else if (key == "roughness") {
QString urlString = value.toString(); auto value = materialJSON.value(key);
bool useAlphaChannel = false; if (value.isString() && value.toString() == FALLTHROUGH) {
auto opacityMap = materialJSON.find("opacityMap"); material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT);
if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == urlString) { } else if (value.isDouble()) {
useAlphaChannel = true; material->setRoughness(value.toDouble());
}
} else if (key == "metallic") {
auto value = materialJSON.value(key);
if (value.isString() && value.toString() == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT);
} else if (value.isDouble()) {
material->setMetallic(value.toDouble());
}
} else if (key == "scattering") {
auto value = materialJSON.value(key);
if (value.isString() && value.toString() == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT);
} else if (value.isDouble()) {
material->setScattering(value.toDouble());
}
} else if (key == "emissiveMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_MAP_BIT);
} else {
material->setEmissiveMap(baseUrl.resolved(valueString));
}
}
} else if (key == "albedoMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
QString valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_MAP_BIT);
} else {
bool useAlphaChannel = false;
auto opacityMap = materialJSON.find("opacityMap");
if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == valueString) {
useAlphaChannel = true;
}
material->setAlbedoMap(baseUrl.resolved(valueString), useAlphaChannel);
}
}
} else if (key == "roughnessMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT);
} else {
material->setRoughnessMap(baseUrl.resolved(valueString), false);
}
}
} else if (key == "glossMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT);
} else {
material->setRoughnessMap(baseUrl.resolved(valueString), true);
}
}
} else if (key == "metallicMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT);
} else {
material->setMetallicMap(baseUrl.resolved(valueString), false);
}
}
} else if (key == "specularMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT);
} else {
material->setMetallicMap(baseUrl.resolved(valueString), true);
}
}
} else if (key == "normalMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT);
} else {
material->setNormalMap(baseUrl.resolved(valueString), false);
}
}
} else if (key == "bumpMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT);
} else {
material->setNormalMap(baseUrl.resolved(valueString), true);
}
}
} else if (key == "occlusionMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT);
} else {
material->setOcclusionMap(baseUrl.resolved(valueString));
}
}
} else if (key == "scatteringMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT);
} else {
material->setScatteringMap(baseUrl.resolved(valueString));
}
}
} else if (key == "lightMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
auto valueString = value.toString();
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::LIGHTMAP_MAP_BIT);
} else {
material->setLightmapMap(baseUrl.resolved(valueString));
}
}
} else if (key == "defaultFallthrough") {
auto value = materialJSON.value(key);
if (value.isBool()) {
material->setDefaultFallthrough(value.toBool());
} }
material->setAlbedoMap(baseUrl.resolved(urlString), useAlphaChannel);
}
} else if (key == "roughnessMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setRoughnessMap(baseUrl.resolved(value.toString()), false);
}
} else if (key == "glossMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setRoughnessMap(baseUrl.resolved(value.toString()), true);
}
} else if (key == "metallicMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setMetallicMap(baseUrl.resolved(value.toString()), false);
}
} else if (key == "specularMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setMetallicMap(baseUrl.resolved(value.toString()), true);
}
} else if (key == "normalMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setNormalMap(baseUrl.resolved(value.toString()), false);
}
} else if (key == "bumpMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setNormalMap(baseUrl.resolved(value.toString()), true);
}
} else if (key == "occlusionMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setOcclusionMap(baseUrl.resolved(value.toString()));
}
} else if (key == "scatteringMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setScatteringMap(baseUrl.resolved(value.toString()));
}
} else if (key == "lightMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setLightmapMap(baseUrl.resolved(value.toString()));
} }
} }
} }

View file

@ -49,8 +49,6 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr
} }
} }
const graphics::MaterialPointer MeshPartPayload::DEFAULT_MATERIAL = std::make_shared<graphics::Material>();
MeshPartPayload::MeshPartPayload(const std::shared_ptr<const graphics::Mesh>& mesh, int partIndex, graphics::MaterialPointer material) { MeshPartPayload::MeshPartPayload(const std::shared_ptr<const graphics::Mesh>& mesh, int partIndex, graphics::MaterialPointer material) {
updateMeshPart(mesh, partIndex); updateMeshPart(mesh, partIndex);
addMaterial(graphics::MaterialLayer(material, 0)); addMaterial(graphics::MaterialLayer(material, 0));
@ -158,7 +156,7 @@ void MeshPartPayload::render(RenderArgs* args) {
// apply material properties // apply material properties
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing); RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing);
args->_details._materialSwitches++; args->_details._materialSwitches++;
} }
@ -434,7 +432,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
// apply material properties // apply material properties
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing); RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing);
args->_details._materialSwitches++; args->_details._materialSwitches++;
} }

View file

@ -74,7 +74,6 @@ public:
void removeMaterial(graphics::MaterialPointer material); void removeMaterial(graphics::MaterialPointer material);
protected: protected:
static const graphics::MaterialPointer DEFAULT_MATERIAL;
render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() };
bool topMaterialExists() const { return !_drawMaterials.empty() && _drawMaterials.top().material; } bool topMaterialExists() const { return !_drawMaterials.empty() && _drawMaterials.top().material; }

View file

@ -308,24 +308,33 @@ void addPlumberPipeline(ShapePlumber& plumber,
void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args) { void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args) {
// Set a default albedo map // Set a default albedo map
batch.setResourceTexture(gr::Texture::MaterialAlbedo, batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get<TextureCache>()->getWhiteTexture());
DependencyManager::get<TextureCache>()->getWhiteTexture());
// Set a default material // Set a default material
if (pipeline.locations->materialBufferUnit) { if (pipeline.locations->materialBufferUnit) {
// Create a default schema // Create a default schema
static bool isMaterialSet = false; static gpu::BufferView schemaBuffer;
static graphics::Material material; static std::once_flag once;
if (!isMaterialSet) { std::call_once(once, [] {
material.setAlbedo(vec3(1.0f)); graphics::MultiMaterial::Schema schema;
material.setOpacity(1.0f); graphics::MaterialKey schemaKey;
material.setMetallic(0.1f);
material.setRoughness(0.9f);
isMaterialSet = true;
}
// Set a default schema schema._albedo = vec3(1.0f);
batch.setUniformBuffer(gr::Buffer::Material, material.getSchemaBuffer()); schema._opacity = 1.0f;
schema._metallic = 0.1f;
schema._roughness = 0.9f;
schemaKey.setAlbedo(true);
schemaKey.setTranslucentFactor(false);
schemaKey.setMetallic(true);
schemaKey.setGlossy(true);
schema._key = (uint32_t)schemaKey._flags.to_ulong();
auto schemaSize = sizeof(graphics::MultiMaterial::Schema);
schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(schemaSize, (const gpu::Byte*) &schema, schemaSize));
});
batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer);
} }
} }
@ -364,103 +373,322 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, con
gpu::Shader::createProgram(deformed_model_shadow_fade_dq), state, extraBatchSetter, itemSetter); gpu::Shader::createProgram(deformed_model_shadow_fade_dq), state, extraBatchSetter, itemSetter);
} }
void RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) {
graphics::MultiMaterial multiMaterial;
multiMaterial.push(graphics::MaterialLayer(material, 0));
bindMaterials(multiMaterial, batch, enableTextures);
}
// FIXME find a better way to setup the default textures // FIXME find a better way to setup the default textures
void RenderPipelines::bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) { void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) {
if (!material) { if (multiMaterial.size() == 0) {
return; return;
} }
auto textureCache = DependencyManager::get<TextureCache>(); auto textureCache = DependencyManager::get<TextureCache>();
auto& drawMaterialTextures = multiMaterial.getTextureTable();
auto& schemaBuffer = multiMaterial.getSchemaBuffer();
batch.setUniformBuffer(gr::Buffer::Material, material->getSchemaBuffer()); // The total list of things we need to look for
static std::set<graphics::MaterialKey::FlagBit> allFlagBits;
static std::once_flag once;
std::call_once(once, [] {
for (int i = 0; i < graphics::MaterialKey::NUM_FLAGS; i++) {
auto flagBit = graphics::MaterialKey::FlagBit(i);
// The opacity mask/map are derived from the albedo map
if (flagBit != graphics::MaterialKey::OPACITY_MASK_MAP_BIT &&
flagBit != graphics::MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT) {
allFlagBits.insert(flagBit);
}
}
});
const auto& materialKey = material->getKey(); graphics::MultiMaterial materials = multiMaterial;
const auto& textureMaps = material->getTextureMaps(); graphics::MultiMaterial::Schema schema;
graphics::MaterialKey schemaKey;
int numUnlit = 0; std::set<graphics::MaterialKey::FlagBit> flagBitsToCheck = allFlagBits;
if (materialKey.isUnlit()) { std::set<graphics::MaterialKey::FlagBit> flagBitsToSetDefault;
numUnlit++;
auto material = materials.top().material;
while (material) {
bool defaultFallthrough = material->getDefaultFallthrough();
const auto& materialKey = material->getKey();
const auto& textureMaps = material->getTextureMaps();
auto it = flagBitsToCheck.begin();
while (it != flagBitsToCheck.end()) {
auto flagBit = *it;
bool wasSet = false;
bool forceDefault = false;
switch (flagBit) {
case graphics::MaterialKey::EMISSIVE_VAL_BIT:
if (materialKey.isEmissive()) {
schema._emissive = material->getEmissive(false);
schemaKey.setEmissive(true);
wasSet = true;
}
break;
case graphics::MaterialKey::UNLIT_VAL_BIT:
if (materialKey.isUnlit()) {
schemaKey.setUnlit(true);
wasSet = true;
}
break;
case graphics::MaterialKey::ALBEDO_VAL_BIT:
if (materialKey.isAlbedo()) {
schema._albedo = material->getAlbedo(false);
schemaKey.setAlbedo(true);
wasSet = true;
}
break;
case graphics::MaterialKey::METALLIC_VAL_BIT:
if (materialKey.isMetallic()) {
schema._metallic = material->getMetallic();
schemaKey.setMetallic(true);
wasSet = true;
}
break;
case graphics::MaterialKey::GLOSSY_VAL_BIT:
if (materialKey.isRough() || materialKey.isGlossy()) {
schema._roughness = material->getRoughness();
schemaKey.setGlossy(materialKey.isGlossy());
wasSet = true;
}
break;
case graphics::MaterialKey::OPACITY_VAL_BIT:
if (materialKey.isTranslucentFactor()) {
schema._opacity = material->getOpacity();
schemaKey.setTranslucentFactor(true);
wasSet = true;
}
break;
case graphics::MaterialKey::SCATTERING_VAL_BIT:
if (materialKey.isScattering()) {
schema._scattering = material->getScattering();
schemaKey.setScattering(true);
wasSet = true;
}
break;
case graphics::MaterialKey::ALBEDO_MAP_BIT:
if (materialKey.isAlbedoMap()) {
if (!enableTextures) {
forceDefault = true;
} else {
auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP);
if (itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView());
wasSet = true;
} else {
forceDefault = true;
}
}
schemaKey.setAlbedoMap(true);
schemaKey.setOpacityMaskMap(materialKey.isOpacityMaskMap());
schemaKey.setTranslucentMap(materialKey.isTranslucentMap());
}
break;
case graphics::MaterialKey::METALLIC_MAP_BIT:
if (materialKey.isMetallicMap()) {
if (!enableTextures) {
forceDefault = true;
} else {
auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP);
if (itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView());
wasSet = true;
} else {
forceDefault = true;
}
}
schemaKey.setMetallicMap(true);
}
break;
case graphics::MaterialKey::ROUGHNESS_MAP_BIT:
if (materialKey.isRoughnessMap()) {
if (!enableTextures) {
forceDefault = true;
} else {
auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP);
if (itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView());
wasSet = true;
} else {
forceDefault = true;
}
}
schemaKey.setRoughnessMap(true);
}
break;
case graphics::MaterialKey::NORMAL_MAP_BIT:
if (materialKey.isNormalMap()) {
if (!enableTextures) {
forceDefault = true;
} else {
auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP);
if (itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView());
wasSet = true;
} else {
forceDefault = true;
}
}
schemaKey.setNormalMap(true);
}
break;
case graphics::MaterialKey::OCCLUSION_MAP_BIT:
if (materialKey.isOcclusionMap()) {
if (!enableTextures) {
forceDefault = true;
} else {
auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP);
if (itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView());
wasSet = true;
} else {
forceDefault = true;
}
}
schemaKey.setOcclusionMap(true);
}
break;
case graphics::MaterialKey::SCATTERING_MAP_BIT:
if (materialKey.isScatteringMap()) {
if (!enableTextures) {
forceDefault = true;
} else {
auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP);
if (itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView());
wasSet = true;
} else {
forceDefault = true;
}
}
schemaKey.setScattering(true);
}
break;
case graphics::MaterialKey::EMISSIVE_MAP_BIT:
// Lightmap takes precendence over emissive map for legacy reasons
if (materialKey.isEmissiveMap() && !materialKey.isLightmapMap()) {
if (!enableTextures) {
forceDefault = true;
} else {
auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP);
if (itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView());
wasSet = true;
} else {
forceDefault = true;
}
}
schemaKey.setEmissiveMap(true);
} else if (materialKey.isLightmapMap()) {
// We'll set this later when we check the lightmap
wasSet = true;
}
break;
case graphics::MaterialKey::LIGHTMAP_MAP_BIT:
if (materialKey.isLightmapMap()) {
if (!enableTextures) {
forceDefault = true;
} else {
auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP);
if (itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView());
wasSet = true;
} else {
forceDefault = true;
}
}
schemaKey.setLightmapMap(true);
}
break;
default:
break;
}
if (wasSet) {
flagBitsToCheck.erase(it++);
} else if (forceDefault || !defaultFallthrough || !material->getPropertyFallthrough(flagBit)) {
flagBitsToSetDefault.insert(flagBit);
flagBitsToCheck.erase(it++);
} else {
++it;
}
}
if (flagBitsToCheck.empty()) {
break;
}
materials.pop();
material = materials.top().material;
} }
const auto& drawMaterialTextures = material->getTextureTable(); for (auto flagBit : flagBitsToCheck) {
flagBitsToSetDefault.insert(flagBit);
}
// Albedo // Handle defaults
if (materialKey.isAlbedoMap()) { for (auto flagBit : flagBitsToSetDefault) {
auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); switch (flagBit) {
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { case graphics::MaterialKey::EMISSIVE_VAL_BIT:
drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); case graphics::MaterialKey::UNLIT_VAL_BIT:
} else { case graphics::MaterialKey::ALBEDO_VAL_BIT:
drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); case graphics::MaterialKey::METALLIC_VAL_BIT:
case graphics::MaterialKey::GLOSSY_VAL_BIT:
case graphics::MaterialKey::OPACITY_VAL_BIT:
case graphics::MaterialKey::SCATTERING_VAL_BIT:
// these are initialized to the correct default values in Schema()
break;
case graphics::MaterialKey::ALBEDO_MAP_BIT:
if (schemaKey.isAlbedoMap()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture());
}
break;
case graphics::MaterialKey::METALLIC_MAP_BIT:
if (schemaKey.isMetallicMap()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture());
}
break;
case graphics::MaterialKey::ROUGHNESS_MAP_BIT:
if (schemaKey.isRoughnessMap()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture());
}
break;
case graphics::MaterialKey::NORMAL_MAP_BIT:
if (schemaKey.isNormalMap()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture());
}
break;
case graphics::MaterialKey::OCCLUSION_MAP_BIT:
if (schemaKey.isOcclusionMap()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture());
}
break;
case graphics::MaterialKey::SCATTERING_MAP_BIT:
if (schemaKey.isScatteringMap()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture());
}
break;
case graphics::MaterialKey::EMISSIVE_MAP_BIT:
if (schemaKey.isEmissiveMap() && !schemaKey.isLightmapMap()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture());
}
break;
case graphics::MaterialKey::LIGHTMAP_MAP_BIT:
if (schemaKey.isLightmapMap()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture());
}
break;
default:
break;
} }
} }
// Roughness map schema._key = (uint32_t)schemaKey._flags.to_ulong();
if (materialKey.isRoughnessMap()) { schemaBuffer.edit<graphics::MultiMaterial::Schema>() = schema;
auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer);
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTextureTable(drawMaterialTextures);
drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView());
} else {
drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture());
}
}
// Normal map
if (materialKey.isNormalMap()) {
auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP);
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView());
} else {
drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture());
}
}
// Metallic map
if (materialKey.isMetallicMap()) {
auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP);
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView());
} else {
drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture());
}
}
// Occlusion map
if (materialKey.isOcclusionMap()) {
auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP);
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView());
} else {
drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture());
}
}
// Scattering map
if (materialKey.isScatteringMap()) {
auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP);
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView());
} else {
drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture());
}
}
// Emissive / Lightmap
if (materialKey.isLightmapMap()) {
auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP);
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView());
} else {
drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture());
}
} else if (materialKey.isEmissiveMap()) {
auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP);
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView());
} else {
drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture());
}
}
batch.setResourceTextureTable(material->getTextureTable());
} }

View file

@ -15,7 +15,8 @@
class RenderPipelines { class RenderPipelines {
public: public:
static void bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures); static void bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures);
static void bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures);
}; };