1#include "MaterialInstance.h"
3#include "Foundation/Logging/Logger.h"
7#include "Utilities/Parsing.h"
18 void initializeInstanceTextureVariables(std::vector<TextureValue>& variables,
const std::span<const TextureProperty>& properties)
24 v.texture = p.texture;
30 float* values =
nullptr;
33 case MaterialDataType::Float:
34 values = (
float*)data;
37 case MaterialDataType::Float2:
38 values = (
float*)data;
41 case MaterialDataType::Float3:
42 values = (
float*)data;
45 case MaterialDataType::Float4:
46 values = (
float*)data;
53 if (!values || !length)
return;
55 if ((
int)(flags) & (
int)MaterialPropertyFlags::sRGB) {
56 for (
int i = 0; i < std::min(3, length); i++) {
57 values[i] = std::pow(std::abs(values[i]), 1.0f / 2.2f);
62 static constexpr const char* shaderVariantTypeName[] = {
73 if (definition.type != type) {
74 LOG_ERROR(logger,
"Variant type is not %s", shaderVariantTypeName[
size_t(type)]);
77 if (definition.isShared) {
78 LOG_ERROR(logger,
"Cannot set shared variant through material instance");
84 assert(selector.index == definition.index);
85 if (selector.value != value) {
86 selector.value = value;
93 bool setVariantImpl(
MaterialInstance* that,
size_t index, ShaderVariantType type,
size_t value)
95 if (that->
material->definition.variants.size() <= index) {
96 LOG_ERROR(logger,
"Variant index %zu out of range.", index);
100 assert(definition.index == index);
101 return setVariantImpl(that, definition, type, value);
106 if (definition.isShared)
return definition.defaultValue;
111 assert(selector.index == definition.index);
112 return selector.value;
117 index = that->
material->getVariantIndex(key);
118 if (index != Material::NoVariantIndex) {
121 LOG_ERROR(logger,
"Unrecognized variant name '%.*s'", StringViewFormat(key));
136 if (buffer.isPerInstance) {
137 instanceBuffer.content = buffer.content;
139 instanceBuffer.name = buffer.name;
140 instanceBuffer.isPerInstance = buffer.isPerInstance;
141 instanceBuffer.index = buffer.index;
142 instanceBuffer.size = buffer.size;
149 for (
auto & v :
material->definition.variants) {
159 if (material != instance->
material) {
182 LOG_ERROR(logger,
"Material instance not valid.");
186 if (!material || !instance->
material) {
187 LOG_ERROR(logger,
"Invalid or missing materials.");
193 for (
auto & dp : material->constantBuffers.variables) {
195 if (dp.name == sp.name && dp.type == sp.type && dp.flags == sp.flags) {
196 auto numBytes = DataTypeSizes[(unsigned)dp.type];
198 union alignas(
float) {
202 if (dp.type == MaterialDataType::Bool) {
204 setBoolProperty(dp.key, value);
205 }
else if (instance->getProperty(sp.key, buffer, numBytes)) {
206 setProperty(dp.key, buffer, numBytes);
209 LOG_ERROR(logger,
"Could not clone property %s.", dp.name.c_str());
215 for (
auto & tp : material->textureProperties) {
216 for (
auto & dp : instance->
material->textureProperties) {
217 if (dp.name == tp.name) {
229 LOG_ERROR(logger,
"Material instance not valid.");
233 if (!material || !srcInstance->
material) {
234 LOG_ERROR(logger,
"Invalid or missing materials.");
239 if (srcDefinition.isShared)
continue;
242 if (
size_t dstIndex = material->getVariantIndex(srcDefinition.name); dstIndex != Material::NoVariantIndex) {
245 if (srcDefinition.type != dstDefinition.type)
continue;
246 if (dstDefinition.isShared)
continue;
248 size_t srcValue = lookupVariantValue(srcInstance, srcDefinition);
249 if (dstDefinition.type == ShaderVariantType::String) {
250 assert(srcValue < srcInstance->variantStrings.size());
254 setVariantImpl(
this, dstDefinition, srcDefinition.type, srcValue);
264 for (
size_t i = 0; i < material->constantBuffers.buffers.size(); ++i) {
265 auto & buffer = material->constantBuffers.buffers[i];
266 auto & instanceBuffer = buffers[i];
268 if (buffer.isPerInstance) {
269 instanceBuffer.content = buffer.content;
273 options = material->options;
275 textureVariables.clear();
276 initializeInstanceTextureVariables(textureVariables, material->textureProperties);
283 switch (options.transparencyMode)
285 case TransparencyMode::Off:
287 case TransparencyMode::On:
289 case TransparencyMode::Auto:
291 VariableKey diffuseKey = material->getVec4Key(
"diffuseColor");
292 if (diffuseKey != NoProperty && getVec4Property(diffuseKey).a < 1.0f) {
296 if (
HandleIsValid(t.texture.handle) && t.texture.handle->hasAlpha) {
311 options.transparencyMode = TransparencyMode::On;
312 setVariant(
"ShadowCast",
"Transparent");
317 options.transparencyMode = TransparencyMode::Off;
318 setVariant(
"ShadowCast",
"Opaque");
323 applyMaterialOption(options, key, value);
326void Cogs::Core::MaterialInstance::setProperty(
const StringView & name,
const void * data,
const size_t sizeInBytes)
328 auto key = material->constantBuffers.getPropertyKey(name);
330 if (key == NoProperty) {
331 LOG_ERROR(logger,
"Could not get key for property %.*s.", StringViewFormat(name));
335 setProperty(key, data, sizeInBytes);
338void Cogs::Core::MaterialInstance::setProperty(
VariableKey key,
const void * data,
const size_t sizeInBytes)
340 auto & materialProperty = material->constantBuffers.variables[key];
344 uint8_t valueBuffer[
sizeof(glm::mat4)];
345 if (sizeInBytes <=
sizeof(glm::mat4)) {
346 std::memcpy(valueBuffer, data, sizeInBytes);
348 enforcePropertyFlags(materialProperty, materialProperty.flags, valueBuffer);
354 if (materialProperty.descriptor.size == 4) {
356 if (sizeInBytes != 1 && sizeInBytes != 4) {
360 else if (sizeInBytes != materialProperty.descriptor.size) {
366 LOG_ERROR(logger,
"setProperty: Invalid size given Property=%s, Expected=%zu, Given=%zu",
367 materialProperty.name.data(), materialProperty.descriptor.size, sizeInBytes);
371 buffers[materialProperty.buffer].setValue(materialProperty.descriptor,
static_cast<const uint8_t *
>(data));
376bool Cogs::Core::MaterialInstance::getProperty(
const StringView & name,
void * value,
const size_t sizeInBytes)
const
378 auto key = material->constantBuffers.getPropertyKey(name);
380 if (key == NoProperty) {
381 LOG_ERROR(logger,
"Could not get key for property %.*s.", StringViewFormat(name));
385 return getProperty(key, value, sizeInBytes);
388bool Cogs::Core::MaterialInstance::getProperty(
VariableKey key,
void * value,
const size_t sizeInBytes)
const
390 if (key > material->constantBuffers.variables.size()) {
391 LOG_ERROR(logger,
"Key %d out of range.", key);
395 auto & materialProperty = material->constantBuffers.variables[key];
397 if (materialProperty.isPerInstance) {
398 std::memcpy(value, buffers[materialProperty.buffer].content.data() + materialProperty.descriptor.offset, sizeInBytes);
401 std::memcpy(value, material->constantBuffers.buffers[materialProperty.buffer].content.data() + materialProperty.descriptor.offset, sizeInBytes);
406 reversePropertyFlags(materialProperty, materialProperty.flags, value, sizeInBytes);
414 if (
VariableKey key = material->getTextureKey(name); key != NoProperty) {
438 LOG_WARNING_ONCE(logger,
"Unrecognized address mode '%.*s'", StringViewFormat(addressMode));
446 if (
VariableKey key = material->getTextureKey(name); key != NoProperty) {
470 LOG_WARNING_ONCE(logger,
"Unrecognized filter mode '%.*s'", StringViewFormat(filterMode));
478 setTextureProperty(material->getTextureKey(key), value);
483 if (key == NoProperty) {
484 LOG_ERROR(logger,
"Cannot set texture property with invalid key.");
488 auto & textureVariable = textureVariables[key];
490 if (value != textureVariable.texture.handle) {
491 textureVariable.texture.handle = value;
492 textureVariable.dirty =
true;
500 setTextureAddressMode(key, mode, mode, mode);
505 if (key == NoProperty) {
506 LOG_ERROR(logger,
"Cannot set texture property with invalid key.");
510 auto & textureVariable = textureVariables[key];
512 if (sMode != textureVariable.texture.sMode || tMode != textureVariable.texture.tMode || uMode != textureVariable.texture.uMode) {
513 textureVariable.texture.sMode = sMode;
514 textureVariable.texture.tMode = tMode;
515 textureVariable.texture.uMode = uMode;
516 textureVariable.dirty =
true;
524 if (key == NoProperty) {
525 LOG_ERROR(logger,
"Cannot set texture property with invalid key.");
537size_t Cogs::Core::MaterialInstance::getPermutationIndex(
const StringView & key)
const
539 for (
size_t i = 0; i < material->permutationKeys.size(); ++i) {
540 if (key == material->permutationKeys[i]) {
548void Cogs::Core::MaterialInstance::setPermutation(
const StringView & key)
550 size_t ix = getPermutationIndex(key);
551 if (permutationIndex == ix)
return;
554 permutationIndex = ix;
557 const MaterialDefinition& definition = material->definition.permutations[permutationIndex];
558 const ShaderVariants& variants = definition.variants;
560 variantSelectors.resize(variants.size());
561 for (
auto& v : variants) {
562 variantSelectors[v.index].index = v.index;
563 variantSelectors[v.index].value = v.defaultValue;
571 return material->permutationKeys[permutationIndex];
574void Cogs::Core::MaterialInstance::setVariant(
size_t index,
bool value)
576 setVariantImpl(
this, index, ShaderVariantType::Bool, value ? 1 : 0);
579void Cogs::Core::MaterialInstance::setVariant(
size_t index,
int value)
581 setVariantImpl(
this, index, ShaderVariantType::Int,
size_t(value));
584void Cogs::Core::MaterialInstance::setVariant(
size_t index,
const StringView & value)
586 assert(index < material->definition.variants.size());
588 switch (variantDefinition.type) {
589 case ShaderVariantType::None:
590 if (!value.
empty()) {
591 LOG_ERROR(logger,
"Trying to set variant %s with no type to '%.*s'.", variantDefinition.name.c_str(), StringViewFormat(value));
595 case ShaderVariantType::Bool:
596 if (
bool val =
false;
parseBool_(val, value)) {
597 setVariantImpl(
this, variantDefinition, ShaderVariantType::Bool, val ? 1 : 0);
601 case ShaderVariantType::Int:
602 if (
int val = 0;
parseInt_(val, value)) {
603 setVariantImpl(
this, variantDefinition, ShaderVariantType::Int, val);
607 case ShaderVariantType::Format:
608 if (Cogs::DataFormat format = Cogs::parseDataFormat(value); format != Cogs::DataFormat::Unknown) {
609 setVariantImpl(
this, variantDefinition, ShaderVariantType::Format,
size_t(format));
612 LOG_ERROR(logger,
"Failed to parse data format \"%.*s\"", StringViewFormat(value));
616 case ShaderVariantType::Enum:
618 if (value == e.key) {
619 setVariantImpl(
this, variantDefinition, Cogs::Core::ShaderVariantType::Enum, e.index);
623 LOG_ERROR(logger,
"Unrecognized enum value '%.*s'", StringViewFormat(value));
626 case ShaderVariantType::String:
627 for (
size_t strIx = 0, strCnt = variantStrings.size(); strIx < strCnt; strIx++) {
628 if (variantStrings[strIx] == value) {
629 setVariantImpl(
this, variantDefinition, Cogs::Core::ShaderVariantType::String, strIx);
633 setVariantImpl(
this, variantDefinition, Cogs::Core::ShaderVariantType::String, variantStrings.size());
634 variantStrings.emplace_back(value.
begin(), value.
end());
638 assert(
false &&
"Invalid ShaderVariantType enum value");
643void Cogs::Core::MaterialInstance::setVariant(
const StringView& key,
bool value)
645 if (
size_t index; lookupVariantIndex(
this, index, key)) {
646 setVariant(index, value);
650void Cogs::Core::MaterialInstance::setVariant(
const StringView& key,
int value)
652 if (
size_t index; lookupVariantIndex(
this, index, key)) {
653 setVariant(index, value);
659 if (
size_t index; lookupVariantIndex(
this, index, key)) {
660 setVariant(index, value);
664std::string Cogs::Core::MaterialInstance::getVariant(
const StringView& key)
const
667 if (!lookupVariantIndex(
this, index, key)) {
668 return std::string();
670 assert(index < material->definition.variants.size());
673 size_t value = lookupVariantValue(
this, definition);
675 switch (definition.type) {
676 case ShaderVariantType::None:
677 return std::string();
679 case ShaderVariantType::Bool:
680 return value ?
"true" :
"false";
682 case ShaderVariantType::Int:
683 return std::to_string(
int(value));
685 case ShaderVariantType::Format:
686 if (
const Cogs::FormatInfo* info = Cogs::getFormatInfo(Cogs::Format(value)); info) {
689 LOG_ERROR(logger,
"Variant '%.*s' has invalid format enum %zd", StringViewFormat(key), value);
692 case ShaderVariantType::Enum:
694 if (value == e.index) {
698 LOG_ERROR(logger,
"Variant '%.*s' contains illegal enum index %zd", StringViewFormat(key), value);
701 case ShaderVariantType::String:
702 if (value < variantStrings.size()) {
703 return variantStrings[value];
705 LOG_ERROR(logger,
"Variant '%.*s' contains illegal string index %zd", StringViewFormat(key), value);
709 assert(
false &&
"Invalid ShaderVariantType enum value");
713 return std::string();
Log implementation class.
Provides a weakly referenced view over the contents of a string.
constexpr iterator begin() noexcept
Iterator to the beginning of the string.
constexpr iterator end() noexcept
Iterator to the end of the string.
constexpr bool empty() const noexcept
Check if the string is empty.
size_t hashLowercase(size_t hashValue=Cogs::hash()) const noexcept
Get the hash code of the string converted to lowercase.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
bool parseBool_(bool &rv, const StringView &token)
Parse a bool putting return value in rv and returning whether or not parsing was successful.
bool parseInt_(int32_t &rv, const StringView &token)
Parse an int putting return value in rv and returning whether or not parsing was successful.
uint16_t VariableKey
Used to lookup material properties.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
void setChanged(Cogs::Core::Context *context, Cogs::ComponentModel::Component *component, Reflection::FieldId fieldId)
Must be Called after changing a Component field. Mark field changed. Request engine update.
std::vector< MaterialPropertyBuffer > buffers
Constant buffer instances.
std::vector< MaterialProperty > variables
Individual variables from all buffer instances.
@ MasterTransparency
Material contains transparency.
Material instances represent a specialized Material combined with state for all its buffers and prope...
std::vector< TextureValue > textureVariables
Texture property values for this instance.
bool hasTransparency() const
Get if this instance has any transparency and should be rendered with blending enabled.
int cloneMatchingProperties(MaterialInstance *instance)
Clones matching property values from the given instance.
void setTransparent()
Set the material instance to transparent, indicating to the renderer that blending should be enabled ...
void cloneMatchingVariants(MaterialInstance *instance)
Clones matching varient values from the given instance.
bool getBoolProperty(const VariableKey key) const
Get the value of the property with the given key.
void setupInstance(Material *material)
Setup the material instance from the given material.
TextureValue getTextureProperty(const VariableKey key) const
Get the value of the property with the given key.
std::vector< MaterialPropertyBufferInstance > buffers
Buffer instances matching the buffers and layout of the parent material.
void reset()
Reset the material instance properties.
std::vector< std::string > variantStrings
String storage for string variants.
void setOption(const StringView &key, const StringView &value)
Sets the option with the given key to a value parsed from the value string.
size_t variantGeneration
If the variant or definitions need updates.
void setTextureAddressMode(const StringView &key, const StringView &addressMode)
Set texture address mode with textual name.
void clone(MaterialInstance *instance)
Clone the the given material instance.
MaterialInstanceHandle masterInstance
Master material instance overriding properties in this instance if override is enabled.
void setTextureProperty(const StringView &key, TextureHandle value)
Set the texture property with the given key to the texture resource held by value.
void setTextureFilterMode(const StringView &key, const StringView &filterMode)
Set texture filter mode with textual name.
MaterialOptions options
Material rendering options used by this instance.
size_t permutationIndex
Index of material permutation to use.
Material * material
Material resource this MaterialInstance is created from.
ShaderVariantSelectors variantSelectors
Variant selectors.
uint16_t instanceFlags
Material instance flags.
void setOpaque()
Set the material instance to opaque, indicating to the renderer that blending should be disabled for ...
Material property buffer instances are created from MaterialPropertyBuffers, and have the same set of...
Defines a single material property.
MaterialDataType type
Type of data held by property.
Material resources define the how of geometry rendering (the what is defined by Mesh and Texture reso...
MaterialOptions options
Material rendering options.
Property value for texture samplers.
TextureWithSampler texture
Value of the property for the material instance this belongs to.
SamplerState::FilterMode filterMode
Filter mode to use when rendering with this texture.
TextureHandle handle
Handle to a texture resource, or TextureHandle::NoHandle if texture should be disabled.
AddressMode
Addressing modes to use when sampling textures.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
@ Border
Texture color is set to the border color when outside [0, 1] range.
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
@ Mirror
Texture coordinates are mirrored when outside [0, 1] range.
FilterMode
Filter modes to specify how texture data is treated when sampled.
@ ComparisonMinMagMipPoint
Comparison filter for depth sample comparisons using point sampling.
@ ComparisonMinMagMipLinear
Comparison filter for depth sample comparisons using linear interpolation sampling.
@ MinMagMipPoint
Point sampling for both minification and magnification.
@ MinMagMipLinear
Linear sampling for both minification and magnification.