1#include "MaterialSystem.h"
5#include "Resources/MaterialManager.h"
6#include "Resources/DefaultMaterial.h"
8#include "Components/Core/MeshRenderComponent.h"
9#include "Components/Core/SubMeshRenderComponent.h"
11#include "Foundation/ComponentModel/Entity.h"
27 const uint8_t * beginByte =
reinterpret_cast<const uint8_t *
>(&component.diffuseMap);
28 const uint8_t * endByte =
reinterpret_cast<const uint8_t *
>(&component.enableTextureOverride) +
sizeof(component.enableTextureOverride);
30 return fnv1a(beginByte, endByte - beginByte);
43 if (renderComponent) {
44 if (renderComponent->material != data.instance && !renderComponent->customMaterial()) {
45 renderComponent->
material = data.instance;
51 if (subMeshRenderComponent) {
52 if (subMeshRenderComponent->materials.empty()) {
53 subMeshRenderComponent->
materials.resize(1);
56 if (subMeshRenderComponent->materials.front() != data.instance && !subMeshRenderComponent->customMaterial()) {
57 subMeshRenderComponent->materials.front() = data.instance;
66 auto & materialManager = *
context->materialManager;
67 auto & materialInstanceManager = *
context->materialInstanceManager;
69 for (
auto & component :
pool) {
70 auto & data = getData(&component);
71 auto masterMaterial = component.material.lock();
76 bool needsPropertyUpdate =
false;
77 bool needsRenderChange =
false;
79 if (!data.lockInstance) {
83 const auto mIt =
instances.find(materialHash);
88 auto instance = materialInstanceManager.get(data.instance);
90 if (!instance->isDefaultMaterial()) {
95 if (mIt->second != data.instance) {
96 data.instance = mIt->second;
97 data.sharedInstance =
true;
98 component.setChanged();
99 needsRenderChange =
true;
103 auto instance = materialInstanceManager.get(data.instance);
105 if (!instance->isDefaultMaterial()) {
107 }
else if (instance->referenceCount() == 1) {
109 data.sharedInstance =
true;
110 needsPropertyUpdate =
true;
112 data.instance = materialInstanceManager.createMaterialInstance(materialManager.getDefaultMaterial());
113 data.sharedInstance =
true;
114 component.setChanged();
117 needsPropertyUpdate =
true;
118 needsRenderChange =
true;
121 data.instance = materialInstanceManager.createMaterialInstance(materialManager.getDefaultMaterial());
122 data.sharedInstance =
true;
123 component.setChanged();
126 needsPropertyUpdate =
true;
127 needsRenderChange =
true;
132 data.instance = materialInstanceManager.createMaterialInstance(materialManager.getDefaultMaterial());
133 needsRenderChange =
true;
136 needsPropertyUpdate =
true;
139 if (needsPropertyUpdate) {
140 auto materialInstance = materialInstanceManager.get(data.instance);
142 if (!materialInstance->isDefaultMaterial())
continue;
144 auto diffuseColor = component.transparency != 0 ? glm::vec4(glm::vec3(component.diffuseColor), 1.0f - component.transparency) : component.diffuseColor;
146 materialInstance->setVec4Property(DefaultMaterial::DiffuseColor, diffuseColor);
147 materialInstance->setVec3Property(DefaultMaterial::EmissiveColor, glm::vec3(component.emissiveColor));
148 materialInstance->setVec3Property(DefaultMaterial::SpecularColor, glm::vec3(component.specularColor));
150 materialInstance->setFloatProperty(DefaultMaterial::SpecularPower, component.specularPower);
152 if (component.backdrop) {
153 materialInstance->setBackdrop();
155 materialInstance->unsetBackdrop();
158 if (component.transparency || component.diffuseColor.a != 1.0f) {
159 materialInstance->setTransparent();
161 materialInstance->setOpaque();
164 materialInstance->setBoolProperty(DefaultMaterial::EnableLighting, component.enableLighting);
166 materialInstance->setFloatProperty(DefaultMaterial::LineWidth, component.lineWidth);
168 if (component.diffuseMapScale != glm::vec2(1, 1)) {
169 materialInstance->setVec2Property(DefaultMaterial::DiffuseMapScale, component.diffuseMapScale);
172 materialInstance->setTextureProperty(DefaultMaterial::DiffuseMap, component.diffuseMap);
173 materialInstance->setTextureAddressMode(DefaultMaterial::DiffuseMap, component.addressMode);
174 materialInstance->setTextureFilterMode(DefaultMaterial::DiffuseMap, component.filterMode);
176 materialInstance->setTextureProperty(DefaultMaterial::NormalMap, component.normalMap);
177 materialInstance->setTextureAddressMode(DefaultMaterial::NormalMap, component.addressMode);
178 materialInstance->setVec2Property(DefaultMaterial::NormalMapScale, component.normalMapScale);
179 materialInstance->setFloatProperty(DefaultMaterial::NormalMapFactor, component.normalMapFactor);
181 materialInstance->setTextureProperty(DefaultMaterial::SpecularMap, component.specularMap);
182 materialInstance->setTextureAddressMode(DefaultMaterial::SpecularMap, component.addressMode);
183 materialInstance->setVec2Property(DefaultMaterial::SpecularMapScale, component.specularMapScale);
185 materialInstance->options.blendMode = component.blendMode;
186 materialInstance->options.drawOrder = component.drawOrder;
193 if (component.instancedLine) {
194 materialInstance->setPermutation(
"InstancedLine");
197 std::string linePermutation = hasAdjacency ?
"LineAdj" :
"Line";
198 materialInstance->setPermutation(linePermutation);
201 materialInstance->setVariant(
"Textured",
HandleIsValid(component.diffuseMap));
202 materialInstance->setUIntProperty(materialInstance->material->getUIntKey(
"stipplePattern"), component.stipplePattern);
203 materialInstance->setUIntProperty(materialInstance->material->getUIntKey(
"stippleFactor"), component.stippleFactor);
206 materialInstance->setPermutation(
"Points");
208 materialInstance->setFloatProperty(DefaultMaterial::PointSize, component.pointSize);
211 materialInstance->setPermutation(
"Default");
213 materialInstance->setVariant(
"EnableLighting", component.enableLighting);
214 materialInstance->setVariant(
"LightModel", component.enableLighting ?
"Phong" :
"BaseColor");
216 materialInstance->setVariant(
"ShadowReceiver", component.shadowReceiver);
218 if (component.diffuseMap || component.normalMap || component.specularMap) {
219 materialInstance->setVariant(
"Textured",
true);
221 if (component.normalMap) {
222 materialInstance->setVariant(
"TangentSpaceNormalMap",
true);
225 if (component.specularMap) {
226 materialInstance->setVariant(
"SpecularMap",
true);
231 if (component.vertexColor) {
232 materialInstance->setVariant(
"VertexColor",
true);
235 materialInstance->options.cullMode = component.cullMode;
237 materialInstance->options.depthWriteEnabled = component.depthWriteEnabled;
238 materialInstance->options.depthTestEnabled = component.depthTestEnabled;
239 materialInstance->options.depthBiasEnabled = component.depthBiasEnable;
240 materialInstance->options.depthBias.constant = component.depthBiasConstant;
241 materialInstance->options.depthBias.slope = component.depthBiasSlope;
242 materialInstance->options.depthBias.clamp = component.depthBiasClamp;
243 materialInstance->options.depthFunc = component.depthTestAlwaysPass ? DepthFunc::Always : DepthFunc::Less;
245 if (component.enableOverride) {
253 materialInstance->setChanged();
256 if (needsRenderChange) {
262 for (
auto & component :
pool) {
263 auto& data = getData(&component);
264 auto masterMaterial = component.material.lock();
266 if (masterMaterial) {
269 assert(masterComponent &&
"Entity without material components cannot be used as master material.");
271 if (component.hasChanged() || masterComponent->hasChanged()) {
272 auto& masterData = getData(masterComponent);
273 auto masterInstance = masterData.instance;
275 if (data.instance != masterInstance) {
277 data.instance = masterInstance;
279 component.setChanged();
292 std::vector<size_t> removal;
294 for (
auto & sharedInstance : instances) {
298 if (sharedInstance.second.resolve()->referenceCount() == 1) {
299 removal.push_back(sharedInstance.first);
303 for (
auto & r : removal) {
ComponentType * getComponent() const
Context * context
Pointer to the Context instance the system lives in.
void update()
Updates the system state to that of the current frame.
ComponentPool< ComponentType > pool
Pool of components managed by the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
void removeUnusedSharedInstances()
Removes any shared material instances no longer in use.
std::unordered_map< size_t, MaterialInstanceHandle > instances
Holds shared material instances used by one or more MaterialComponent instances.
void cleanup(Context *context) override
Cleanup the material system, releasing any shared material instances held.
Renders the contents of a MeshComponent using the given materials.
MaterialInstanceHandle material
Material used to render the mesh.
Renders a mesh with flexible submesh usage.
std::vector< MaterialInstanceHandle > materials
Materials used to render individual sub-meshes.
void applyRenderMaterial(const MaterialComponent &component, const MaterialData &data)
Apply the material instance from the given component to the mesh render component (if found) on the s...
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
bool isLine(PrimitiveType::EPrimitiveType primitiveType)
Check if the given primitive type represents lines.
@ Blend
Render with regular alpha blending.
size_t getMaterialHash(const MaterialComponent &component)
Creates a hash value from the material properties in the given component.
Contains all Cogs related functionality.
constexpr size_t fnv1a(uint8_t data, size_t hashValue) noexcept
Hashes a single byte using the fnv1a algorithm.
Exposes material properties for legacy entities and code.
@ Override
Override all properties of any inheriting materials.
@ OverrideAlpha
Override alpha of any inheriting materials.
@ OverrideColor
Override color of any inheriting materials.
@ OverrideTextures
Override textures of any inheriting materials.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
@ LineStripAdjacency
Line strip with adjacency.
@ PointList
List of points.
@ LineListAdjacency
List of lines with adjacency.