Cogs.Core
Material.cpp
1#include "Foundation/Logging/Logger.h"
2
3#include "Material.h"
4
5#include <algorithm>
6#include <limits>
7#include <sstream>
8#include "MaterialManager.h"
9
10#include "MaterialBuilder.h"
11
12
13namespace {
14 using namespace Cogs::Core;
15
16 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("Material");
17
18
19 void checkNameNotInBuffer(const ConstantBuffers & contantBuffers, const PropertyName & name)
20 {
21 for (auto & buffer : contantBuffers.buffers) {
22 if (buffer.name == name) {
23 assert(false && "Buffer with the given name already exists.");
24 }
25 }
26 }
27
28 bool checkVariableKey(const char* method, VariableKey key, size_t numKeys)
29 {
30 if (key >= numKeys) {
31 LOG_ERROR(logger, "%s: Invalid VariableKey: %d", method, key);
32 return false;
33 }
34 else {
35 return true;
36 }
37 }
38}
39
40Cogs::Core::ConstantBufferKey Cogs::Core::ConstantBuffers::addBuffer(const PropertyName & name, bool isPerInstance)
41{
42 checkNameNotInBuffer(*this, name);
43
44 buffers.emplace_back();
45
46 const ConstantBufferKey key = static_cast<ConstantBufferKey>(buffers.size() - 1);
47
48 auto & buffer = buffers[key];
49 buffer.index = key;
50 buffer.name = name;
51 buffer.isPerInstance = isPerInstance;
52
54
55 return key;
56}
57
58void Cogs::Core::enforcePropertyFlags(const MaterialProperty & prop, MaterialPropertyFlags flags, void * data)
59{
60 float * values = nullptr;
61 int length = 0;
62 switch (prop.type) {
63 case MaterialDataType::Float:
64 values = (float *)data;
65 length = 1;
66 break;
67 case MaterialDataType::Float2:
68 values = (float *)data;
69 length = 2;
70 break;
71 case MaterialDataType::Float3:
72 values = (float *)data;
73 length = 3;
74 break;
75 case MaterialDataType::Float4:
76 values = (float *)data;
77 length = 4;
78 break;
79 default:
80 break;
81 }
82
83 if (!values || !length) return;
84
85 if ((int)(flags) & (int)MaterialPropertyFlags::sRGB) {
86 for (int i = 0; i < std::min(3, length); i++) {
87 values[i] = std::pow(std::abs(values[i]), 2.2f);
88 }
89 }
90
91 if ((int)(flags) & (int)MaterialPropertyFlags::Normalized) {
92 float l = 0.f;
93 for (int i = 0; i < length; i++) {
94 l += values[i] * values[i];
95 }
96 l = l < std::numeric_limits<float>::epsilon() ? 0.f : 1.f / l;
97 for (int i = 0; i < length; i++) {
98 values[i] = l * values[i];
99 }
100 }
101
102 if ((int)(flags) & (int)MaterialPropertyFlags::PartitionOfUnity) {
103 float l = 0.f;
104 for (int i = 0; i < length; i++) {
105 values[i] = std::max(0.f, values[i]);
106 l += values[i];
107 }
108 l = l < std::numeric_limits<float>::epsilon() ? 0.f : 1.f / l;
109 for (int i = 0; i < length; i++) {
110 values[i] = l * values[i];
111 }
112 }
113}
114
115
117{
118 if (!checkVariableKey("setTextureProperty", key, textureProperties.size())) {
119 return;
120 }
121
122 TextureProperty& textureVariable = textureProperties[key];
123 if (value != textureVariable.texture.handle) {
124 textureVariable.texture.handle = value;
125 textureVariable.dirty = true;
126 setChanged();
127 }
128}
129
131{
132 const VariableKey variableKey = constantBuffers.getPropertyKey(key);
133
134 if (variableKey == NoProperty) {
135 return MaterialDataType::Unknown;
136 }
137
138 const auto& materialProperty = constantBuffers.variables[variableKey];
139 return materialProperty.type;
140}
141
143{
144 setTextureAddressMode(key, mode, mode, mode);
145}
146
148{
149 if (!checkVariableKey("setTextureAddressMode", key, textureProperties.size())) {
150 return;
151 }
152
153 TextureProperty& textureVariable = textureProperties[key];
154 if (smode != textureVariable.texture.sMode || tmode != textureVariable.texture.tMode || umode != textureVariable.texture.uMode) {
155 textureVariable.texture.sMode = smode;
156 textureVariable.texture.tMode = tmode;
157 textureVariable.texture.uMode = umode;
158 textureVariable.dirty = true;
159 setChanged();
160 }
161}
162
163
165{
166 if (!checkVariableKey("setTextureFilterMode", key, textureProperties.size())) {
167 return;
168 }
169
170 TextureProperty& textureVariable = textureProperties[key];
171 if (textureVariable.texture.filterMode != filterMode) {
172 textureVariable.texture.filterMode = filterMode;
173 textureVariable.dirty = true;
174 setChanged();
175 }
176}
177
178size_t Cogs::Core::Material::getVariantIndex(const StringView& key) const
179{
180 for (const ShaderVariantDefinition& v : definition.variants) {
181 if (key == v.name) {
182 return v.index;
183 }
184 }
185
186 return NoVariantIndex;
187}
188
189void Cogs::Core::Material::setVariant(size_t index, int value)
190{
191 if (definition.variants.size() <= index) {
192 LOG_ERROR(logger, "Variant index out of range.");
193 return;
194 }
195 assert(definition.variants[index].index == index && "Variant selector index invalid.");
196 ShaderVariantDefinition& variantDefinition = definition.variants[index];
197 if (!variantDefinition.isShared) {
198 LOG_ERROR_ONCE(logger, "Trying to set a non-shared variant via the material.");
199 return;
200 }
201
202 if (variantDefinition.type != ShaderVariantType::Int) {
203 LOG_ERROR(logger, "Variant type is not Int");
204 return;
205 }
206 if (variantDefinition.defaultValue != value) {
207 variantDefinition.defaultValue = value;
208 variantGeneration++;
209 setChanged();
210 }
211}
212
213void Cogs::Core::Material::setVariant(const StringView& key, int value)
214{
215 size_t ix = getVariantIndex(key);
216 if (ix == Material::NoVariantIndex) {
217 LOG_ERROR(logger, "Unrecognized variant name '%.*s'", StringViewFormat(key));
218 return;
219 }
220 setVariant(ix, value);
221}
222
224 const MaterialInstance* materialInstance,
225 const MeshStreamsLayout* streamsLayout,
226 const EnginePermutation* enginePermutation,
227 const RenderPassOptions& passOptions,
228 const ClipShapeType clipShape)
229{
230 for (auto & e : effects) {
231 if (e.code == code) return e.handle;
232 }
233
234 MaterialManager* materialManager = (MaterialManager *)getOwner();
235
236 EffectHandle effect = materialManager->loadMaterialVariant(this, materialInstance, streamsLayout, enginePermutation, passOptions, clipShape);
237
238 effects.push_back(EffectInstance{ effect, code });
239
240 return effect;
241}
242
243void Cogs::Core::Material::setProperty(const StringView& name, const void* data, const size_t sizeInBytes)
244{
245 VariableKey key = constantBuffers.getPropertyKey(name);
246 MaterialProperty& materialProperty = constantBuffers.variables[key];
247
248 // For smaller elements, take a copy so we may modify the value if necessary.
249 uint8_t valueBuffer[sizeof(glm::mat4)];
250 if (sizeInBytes <= sizeof(glm::mat4)) {
251 std::memcpy(valueBuffer, data, sizeInBytes);
252 if (materialProperty.flags != MaterialPropertyFlags::None) {
253 enforcePropertyFlags(materialProperty, materialProperty.flags, valueBuffer);
254 }
255 data = &valueBuffer;
256 }
257
258 bool okSize = true;
259 if (materialProperty.descriptor.size == 4) {
260 // Possibly Boolean - accept 1(bool) or 4(int)
261 if (sizeInBytes != 1 && sizeInBytes != 4) {
262 okSize = false;
263 }
264 }
265 else if (sizeInBytes != materialProperty.descriptor.size) {
266 // Size mismatch also for types with Size=0.
267 okSize = false;
268 }
269
270 if (!okSize) {
271 LOG_ERROR(logger, "setProperty: Invalid size given Property=%s, Expected=%zu, Given=%zu",
272 materialProperty.name.data(), materialProperty.descriptor.size, sizeInBytes);
273 return;
274 }
275 constantBuffers.buffers[materialProperty.buffer].setValue(materialProperty.descriptor, static_cast<const uint8_t*>(data));
276
277 setChanged();
278}
Material manager handling loading and processing of Material resources.
EffectHandle loadMaterialVariant(Material *material, const MaterialInstance *materialInstance, const MeshStreamsLayout *streamsLayout, const EnginePermutation *permutation, const RenderPassOptions &passOptions, const ClipShapeType clipShape)
Log implementation class.
Definition: LogManager.h:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
ClipShapeType
Specifices what kind of shape a clip shape has.
MaterialDataType
Defines available data types for material properties.
Definition: MaterialTypes.h:20
@ Normalized
Value is a direction vector is subject to normalization.
@ PartitionOfUnity
A set of non-negative weights that sum to one.
@ sRGB
Value is a color and is subject to gamma correction.
std::string PropertyName
Typedef for property names.
uint16_t VariableKey
Used to lookup material properties.
Definition: Resources.h:46
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
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.
Definition: FieldSetter.h:25
std::vector< MaterialPropertyBuffer > buffers
Constant buffer instances.
uint16_t buffersGeneration
If the constant buffer bindings need updates.
Material instances represent a specialized Material combined with state for all its buffers and prope...
Defines a single material property.
bool dirty
If the property is dirty.
MaterialPropertyDescriptor descriptor
Property descriptor.
ConstantBufferKey buffer
Key to the buffer the property value is stored in.
PropertyName name
Name of the property, used to reference named uniforms of constant buffer members in shaders.
MaterialDataType type
Type of data held by property.
EffectHandle getEffect(size_t code, const MaterialInstance *materialInstance, const MeshStreamsLayout *streamsLayout, const EnginePermutation *enginePermutation, const RenderPassOptions &passOptions, const ClipShapeType clipShape)
Definition: Material.cpp:223
MaterialDataType getPropertyDataType(const StringView &key)
Gets data type for the given property. Returns MaterialDataType::Unknown if not found.
Definition: Material.cpp:130
void setTextureFilterMode(const VariableKey key, SamplerState::FilterMode filterMode)
Set filtermode used for the texture property.
Definition: Material.cpp:164
void setTextureProperty(const VariableKey key, TextureHandle value)
Set the texture property with the given key to the texture resource held by value.
Definition: Material.cpp:116
void setTextureAddressMode(const VariableKey key, SamplerState::AddressMode mode)
Set the address mode used for the texture property with the given key to mode.
Definition: Material.cpp:142
Property value for texture samplers.
SamplerState::FilterMode filterMode
Filter mode to use when rendering with this texture.
SamplerState::AddressMode sMode
Address 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.
Definition: SamplerState.h:15
FilterMode
Filter modes to specify how texture data is treated when sampled.
Definition: SamplerState.h:31