1#include "MaterialInstance.h"
3#include "Foundation/Logging/Logger.h"
7#include "Utilities/Parsing.h"
17 void initializeInstanceTextureVariables(std::vector<TextureValue>& variables,
const std::span<const TextureProperty>& properties)
23 v.texture = p.texture;
29 float* values =
nullptr;
32 case MaterialDataType::Float:
33 values = (
float*)data;
36 case MaterialDataType::Float2:
37 values = (
float*)data;
40 case MaterialDataType::Float3:
41 values = (
float*)data;
44 case MaterialDataType::Float4:
45 values = (
float*)data;
52 if (!values || !length)
return;
54 if ((
int)(flags) & (
int)MaterialPropertyFlags::sRGB) {
55 for (
int i = 0; i < std::min(3, length); i++) {
56 values[i] = std::pow(std::abs(values[i]), 1.0f / 2.2f);
61 static constexpr const char* shaderVariantTypeName[] = {
72 if (definition.type != type) {
73 LOG_ERROR(logger,
"Variant type is not %s", shaderVariantTypeName[
size_t(type)]);
76 if (definition.isShared) {
77 LOG_ERROR(logger,
"Cannot set shared variant through material instance");
83 assert(selector.index == definition.index);
84 if (selector.value != value) {
85 selector.value = value;
92 bool setVariantImpl(
MaterialInstance* that,
size_t index, ShaderVariantType type,
size_t value)
94 if (that->
material->definition.variants.size() <= index) {
95 LOG_ERROR(logger,
"Variant index %zu out of range.", index);
99 assert(definition.index == index);
100 return setVariantImpl(that, definition, type, value);
105 if (definition.isShared)
return definition.defaultValue;
110 assert(selector.index == definition.index);
111 return selector.value;
116 index = that->
material->getVariantIndex(key);
117 if (index != Material::NoVariantIndex) {
120 LOG_ERROR(logger,
"Unrecognized variant name '%.*s'", StringViewFormat(key));
135 if (buffer.isPerInstance) {
136 instanceBuffer.content = buffer.content;
138 instanceBuffer.name = buffer.name;
139 instanceBuffer.isPerInstance = buffer.isPerInstance;
140 instanceBuffer.index = buffer.index;
141 instanceBuffer.size = buffer.size;
148 for (
auto & v :
material->definition.variants) {
158 if (material != instance->
material) {
181 LOG_ERROR(logger,
"Material instance not valid.");
185 if (!material || !instance->
material) {
186 LOG_ERROR(logger,
"Invalid or missing materials.");
192 for (
auto & dp : material->constantBuffers.variables) {
194 if (dp.name == sp.name && dp.type == sp.type && dp.flags == sp.flags) {
195 auto numBytes = DataTypeSizes[(unsigned)dp.type];
197 union alignas(
float) {
201 if (dp.type == MaterialDataType::Bool) {
203 setBoolProperty(dp.key, value);
204 }
else if (instance->getProperty(sp.key, buffer, numBytes)) {
205 setProperty(dp.key, buffer, numBytes);
208 LOG_ERROR(logger,
"Could not clone property %s.", dp.name.c_str());
214 for (
auto & tp : material->textureProperties) {
215 for (
auto & dp : instance->
material->textureProperties) {
216 if (dp.name == tp.name) {
228 LOG_ERROR(logger,
"Material instance not valid.");
232 if (!material || !srcInstance->
material) {
233 LOG_ERROR(logger,
"Invalid or missing materials.");
238 if (srcDefinition.isShared)
continue;
241 if (
size_t dstIndex = material->getVariantIndex(srcDefinition.name); dstIndex != Material::NoVariantIndex) {
244 if (srcDefinition.type != dstDefinition.type)
continue;
245 if (dstDefinition.isShared)
continue;
247 size_t srcValue = lookupVariantValue(srcInstance, srcDefinition);
248 if (dstDefinition.type == ShaderVariantType::String) {
249 assert(srcValue < srcInstance->variantStrings.size());
253 setVariantImpl(
this, dstDefinition, srcDefinition.type, srcValue);
263 for (
size_t i = 0; i < material->constantBuffers.buffers.size(); ++i) {
264 auto & buffer = material->constantBuffers.buffers[i];
265 auto & instanceBuffer = buffers[i];
267 if (buffer.isPerInstance) {
268 instanceBuffer.content = buffer.content;
272 options = material->options;
274 textureVariables.clear();
275 initializeInstanceTextureVariables(textureVariables, material->textureProperties);
282 switch (options.transparencyMode)
284 case TransparencyMode::Off:
286 case TransparencyMode::On:
288 case TransparencyMode::Auto:
290 VariableKey diffuseKey = material->getVec4Key(
"diffuseColor");
291 if (diffuseKey != NoProperty && getVec4Property(diffuseKey).a < 1.0f) {
295 if (
HandleIsValid(t.texture.handle) && t.texture.handle->hasAlpha) {
310 options.transparencyMode = TransparencyMode::On;
311 setVariant(
"ShadowCast",
"Transparent");
316 options.transparencyMode = TransparencyMode::Off;
317 setVariant(
"ShadowCast",
"Opaque");
322 applyMaterialOption(options, key, value);
325void Cogs::Core::MaterialInstance::setProperty(
const StringView & name,
const void * data,
const size_t sizeInBytes)
327 auto key = material->constantBuffers.getPropertyKey(name);
329 if (key == NoProperty) {
330 LOG_ERROR(logger,
"Could not get key for property %.*s.", StringViewFormat(name));
334 setProperty(key, data, sizeInBytes);
337void Cogs::Core::MaterialInstance::setProperty(
VariableKey key,
const void * data,
const size_t sizeInBytes)
339 auto & materialProperty = material->constantBuffers.variables[key];
343 uint8_t valueBuffer[
sizeof(glm::mat4)];
344 if (sizeInBytes <=
sizeof(glm::mat4)) {
345 std::memcpy(valueBuffer, data, sizeInBytes);
347 enforcePropertyFlags(materialProperty, materialProperty.flags, valueBuffer);
353 if (materialProperty.descriptor.size == 4) {
355 if (sizeInBytes != 1 && sizeInBytes != 4) {
359 else if (sizeInBytes != materialProperty.descriptor.size) {
365 LOG_ERROR(logger,
"setProperty: Invalid size given Property=%s, Expected=%zu, Given=%zu",
366 materialProperty.name.data(), materialProperty.descriptor.size, sizeInBytes);
370 buffers[materialProperty.buffer].setValue(materialProperty.descriptor,
static_cast<const uint8_t *
>(data));
375bool Cogs::Core::MaterialInstance::getProperty(
const StringView & name,
void * value,
const size_t sizeInBytes)
const
377 auto key = material->constantBuffers.getPropertyKey(name);
379 if (key == NoProperty) {
380 LOG_ERROR(logger,
"Could not get key for property %.*s.", StringViewFormat(name));
384 return getProperty(key, value, sizeInBytes);
387bool Cogs::Core::MaterialInstance::getProperty(
VariableKey key,
void * value,
const size_t sizeInBytes)
const
389 if (key > material->constantBuffers.variables.size()) {
390 LOG_ERROR(logger,
"Key %d out of range.", key);
394 auto & materialProperty = material->constantBuffers.variables[key];
396 if (materialProperty.isPerInstance) {
397 std::memcpy(value, buffers[materialProperty.buffer].content.data() + materialProperty.descriptor.offset, sizeInBytes);
400 std::memcpy(value, material->constantBuffers.buffers[materialProperty.buffer].content.data() + materialProperty.descriptor.offset, sizeInBytes);
405 reversePropertyFlags(materialProperty, materialProperty.flags, value, sizeInBytes);
413 if (
VariableKey key = material->getTextureKey(name); key != NoProperty) {
437 LOG_WARNING_ONCE(logger,
"Unrecognized address mode '%.*s'", StringViewFormat(addressMode));
445 if (
VariableKey key = material->getTextureKey(name); key != NoProperty) {
469 LOG_WARNING_ONCE(logger,
"Unrecognized filter mode '%.*s'", StringViewFormat(filterMode));
477 setTextureProperty(material->getTextureKey(key), value);
482 if (key == NoProperty) {
483 LOG_ERROR(logger,
"Cannot set texture property with invalid key.");
487 auto & textureVariable = textureVariables[key];
489 if (value != textureVariable.texture.handle) {
490 textureVariable.texture.handle = value;
491 textureVariable.dirty =
true;
499 setTextureAddressMode(key, mode, mode, mode);
504 if (key == NoProperty) {
505 LOG_ERROR(logger,
"Cannot set texture property with invalid key.");
509 auto & textureVariable = textureVariables[key];
511 if (sMode != textureVariable.texture.sMode || tMode != textureVariable.texture.tMode || uMode != textureVariable.texture.uMode) {
512 textureVariable.texture.sMode = sMode;
513 textureVariable.texture.tMode = tMode;
514 textureVariable.texture.uMode = uMode;
515 textureVariable.dirty =
true;
523 if (key == NoProperty) {
524 LOG_ERROR(logger,
"Cannot set texture property with invalid key.");
536size_t Cogs::Core::MaterialInstance::getPermutationIndex(
const StringView & key)
const
538 for (
size_t i = 0; i < material->permutationKeys.size(); ++i) {
539 if (key == material->permutationKeys[i]) {
547void Cogs::Core::MaterialInstance::setPermutation(
const StringView & key)
549 size_t ix = getPermutationIndex(key);
550 if (permutationIndex == ix)
return;
553 permutationIndex = ix;
556 const MaterialDefinition& definition = material->definition.permutations[permutationIndex];
557 const ShaderVariants& variants = definition.variants;
559 variantSelectors.resize(variants.size());
560 for (
auto& v : variants) {
561 variantSelectors[v.index].index = v.index;
562 variantSelectors[v.index].value = v.defaultValue;
570 return material->permutationKeys[permutationIndex];
573void Cogs::Core::MaterialInstance::setVariant(
size_t index,
bool value)
575 setVariantImpl(
this, index, ShaderVariantType::Bool, value ? 1 : 0);
578void Cogs::Core::MaterialInstance::setVariant(
size_t index,
int value)
580 setVariantImpl(
this, index, ShaderVariantType::Int,
size_t(value));
583void Cogs::Core::MaterialInstance::setVariant(
size_t index,
const StringView & value)
585 assert(index < material->definition.variants.size());
587 switch (variantDefinition.type) {
588 case ShaderVariantType::None:
589 if (!value.
empty()) {
590 LOG_ERROR(logger,
"Trying to set variant %s with no type to '%.*s'.", variantDefinition.name.c_str(), StringViewFormat(value));
594 case ShaderVariantType::Bool:
595 if (
bool val =
false;
parseBool_(val, value)) {
596 setVariantImpl(
this, variantDefinition, ShaderVariantType::Bool, val ? 1 : 0);
600 case ShaderVariantType::Int:
601 if (
int val = 0;
parseInt_(val, value)) {
602 setVariantImpl(
this, variantDefinition, ShaderVariantType::Int, val);
606 case ShaderVariantType::Format:
607 if (Cogs::DataFormat format = Cogs::parseDataFormat(value); format != Cogs::DataFormat::Unknown) {
608 setVariantImpl(
this, variantDefinition, ShaderVariantType::Format,
size_t(format));
611 LOG_ERROR(logger,
"Failed to parse data format \"%.*s\"", StringViewFormat(value));
615 case ShaderVariantType::Enum:
617 if (value == e.key) {
618 setVariantImpl(
this, variantDefinition, Cogs::Core::ShaderVariantType::Enum, e.index);
622 LOG_ERROR(logger,
"Unrecognized enum value '%.*s'", StringViewFormat(value));
625 case ShaderVariantType::String:
626 for (
size_t strIx = 0, strCnt = variantStrings.size(); strIx < strCnt; strIx++) {
627 if (variantStrings[strIx] == value) {
628 setVariantImpl(
this, variantDefinition, Cogs::Core::ShaderVariantType::String, strIx);
632 setVariantImpl(
this, variantDefinition, Cogs::Core::ShaderVariantType::String, variantStrings.size());
633 variantStrings.emplace_back(value.
begin(), value.
end());
637 assert(
false &&
"Invalid ShaderVariantType enum value");
642void Cogs::Core::MaterialInstance::setVariant(
const StringView& key,
bool value)
644 if (
size_t index; lookupVariantIndex(
this, index, key)) {
645 setVariant(index, value);
649void Cogs::Core::MaterialInstance::setVariant(
const StringView& key,
int value)
651 if (
size_t index; lookupVariantIndex(
this, index, key)) {
652 setVariant(index, value);
658 if (
size_t index; lookupVariantIndex(
this, index, key)) {
659 setVariant(index, value);
663std::string Cogs::Core::MaterialInstance::getVariant(
const StringView& key)
const
666 if (!lookupVariantIndex(
this, index, key)) {
667 return std::string();
669 assert(index < material->definition.variants.size());
672 size_t value = lookupVariantValue(
this, definition);
674 switch (definition.type) {
675 case ShaderVariantType::None:
676 return std::string();
678 case ShaderVariantType::Bool:
679 return value ?
"true" :
"false";
681 case ShaderVariantType::Int:
682 return std::to_string(
int(value));
684 case ShaderVariantType::Format:
685 if (
const Cogs::FormatInfo* info = Cogs::getFormatInfo(Cogs::Format(value)); info) {
688 LOG_ERROR(logger,
"Variant '%.*s' has invalid format enum %zd", StringViewFormat(key), value);
691 case ShaderVariantType::Enum:
693 if (value == e.index) {
697 LOG_ERROR(logger,
"Variant '%.*s' contains illegal enum index %zd", StringViewFormat(key), value);
700 case ShaderVariantType::String:
701 if (value < variantStrings.size()) {
702 return variantStrings[value];
704 LOG_ERROR(logger,
"Variant '%.*s' contains illegal string index %zd", StringViewFormat(key), value);
708 assert(
false &&
"Invalid ShaderVariantType enum value");
712 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.