NazaraEngine/src/Nazara/Graphics/Material.cpp

498 lines
18 KiB
C++

// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Utility/MaterialData.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Material
* \brief Graphics class that represents a material
*/
/*!
* \brief Checks whether the parameters for the material are correct
* \return true If parameters are valid
*/
bool MaterialParams::IsValid() const
{
if (!UberShaderLibrary::Has(shaderName))
return false;
return true;
}
/*!
* \brief Applies shader to the material
*
* \param instance Pipeline instance to update
* \param textureUnit Unit for the texture GL_TEXTURE"i"
* \param lastUsedUnit Optional argument to get the last texture unit
*/
void Material::Apply(const MaterialPipeline::Instance& instance) const
{
const Shader* shader = instance.renderPipeline.GetInfo().shader;
if (instance.uniforms[MaterialUniform_AlphaThreshold] != -1)
shader->SendFloat(instance.uniforms[MaterialUniform_AlphaThreshold], m_alphaThreshold);
if (instance.uniforms[MaterialUniform_Ambient] != -1)
shader->SendColor(instance.uniforms[MaterialUniform_Ambient], m_ambientColor);
if (instance.uniforms[MaterialUniform_Diffuse] != -1)
shader->SendColor(instance.uniforms[MaterialUniform_Diffuse], m_diffuseColor);
if (instance.uniforms[MaterialUniform_Shininess] != -1)
shader->SendFloat(instance.uniforms[MaterialUniform_Shininess], m_shininess);
if (instance.uniforms[MaterialUniform_Specular] != -1)
shader->SendColor(instance.uniforms[MaterialUniform_Specular], m_specularColor);
if (m_alphaMap && instance.uniforms[MaterialUniform_AlphaMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Alpha];
Renderer::SetTexture(textureUnit, m_alphaMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_diffuseMap && instance.uniforms[MaterialUniform_DiffuseMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Diffuse];
Renderer::SetTexture(textureUnit, m_diffuseMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_emissiveMap && instance.uniforms[MaterialUniform_EmissiveMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Emissive];
Renderer::SetTexture(textureUnit, m_emissiveMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_heightMap && instance.uniforms[MaterialUniform_HeightMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Height];
Renderer::SetTexture(textureUnit, m_heightMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_normalMap && instance.uniforms[MaterialUniform_NormalMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Normal];
Renderer::SetTexture(textureUnit, m_normalMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_specularMap && instance.uniforms[MaterialUniform_SpecularMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Specular];
Renderer::SetTexture(textureUnit, m_specularMap);
Renderer::SetTextureSampler(textureUnit, m_specularSampler);
}
}
/*!
* \brief Builds the material from a parameter list
*
* \param matData Data information for the material
* \param matParams Additional parameters for the material
*/
void Material::BuildFromParameters(const ParameterList& matData, const MaterialParams& matParams)
{
Color color;
bool isEnabled;
double dValue;
long long iValue;
String path;
ErrorFlags errFlags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled, true);
if (matData.GetDoubleParameter(MaterialData::AlphaThreshold, &dValue))
SetAlphaThreshold(float(dValue));
if (matData.GetBooleanParameter(MaterialData::AlphaTest, &isEnabled))
EnableAlphaTest(isEnabled);
if (matData.GetColorParameter(MaterialData::AmbientColor, &color))
SetAmbientColor(color);
if (matData.GetIntegerParameter(MaterialData::CullingSide, &iValue))
SetFaceCulling(static_cast<FaceSide>(iValue));
if (matData.GetIntegerParameter(MaterialData::DepthFunc, &iValue))
SetDepthFunc(static_cast<RendererComparison>(iValue));
if (matData.GetBooleanParameter(MaterialData::DepthSorting, &isEnabled))
EnableDepthSorting(isEnabled);
if (matData.GetColorParameter(MaterialData::DiffuseColor, &color))
SetDiffuseColor(color);
if (matData.GetIntegerParameter(MaterialData::DstBlend, &iValue))
SetDstBlend(static_cast<BlendFunc>(iValue));
if (matData.GetIntegerParameter(MaterialData::FaceFilling, &iValue))
SetFaceFilling(static_cast<FaceFilling>(iValue));
if (matData.GetDoubleParameter(MaterialData::LineWidth, &dValue))
SetLineWidth(float(dValue));
if (matData.GetDoubleParameter(MaterialData::PointSize, &dValue))
SetPointSize(float(dValue));
if (matData.GetColorParameter(MaterialData::SpecularColor, &color))
SetSpecularColor(color);
if (matData.GetDoubleParameter(MaterialData::Shininess, &dValue))
SetShininess(float(dValue));
if (matData.GetIntegerParameter(MaterialData::SrcBlend, &iValue))
SetSrcBlend(static_cast<BlendFunc>(iValue));
// RendererParameter
if (matData.GetBooleanParameter(MaterialData::Blending, &isEnabled))
EnableBlending(isEnabled);
if (matData.GetBooleanParameter(MaterialData::ColorWrite, &isEnabled))
EnableColorWrite(isEnabled);
if (matData.GetBooleanParameter(MaterialData::DepthBuffer, &isEnabled))
EnableDepthBuffer(isEnabled);
if (matData.GetBooleanParameter(MaterialData::DepthWrite, &isEnabled))
EnableDepthWrite(isEnabled);
if (matData.GetBooleanParameter(MaterialData::FaceCulling, &isEnabled))
EnableFaceCulling(isEnabled);
if (matData.GetBooleanParameter(MaterialData::ScissorTest, &isEnabled))
EnableScissorTest(isEnabled);
if (matData.GetBooleanParameter(MaterialData::StencilTest, &isEnabled))
EnableStencilTest(isEnabled);
// Samplers
if (matData.GetIntegerParameter(MaterialData::DiffuseAnisotropyLevel, &iValue))
m_diffuseSampler.SetAnisotropyLevel(static_cast<UInt8>(iValue));
if (matData.GetIntegerParameter(MaterialData::DiffuseFilter, &iValue))
m_diffuseSampler.SetFilterMode(static_cast<SamplerFilter>(iValue));
if (matData.GetIntegerParameter(MaterialData::DiffuseWrap, &iValue))
m_diffuseSampler.SetWrapMode(static_cast<SamplerWrap>(iValue));
if (matData.GetIntegerParameter(MaterialData::SpecularAnisotropyLevel, &iValue))
m_specularSampler.SetAnisotropyLevel(static_cast<UInt8>(iValue));
if (matData.GetIntegerParameter(MaterialData::SpecularFilter, &iValue))
m_specularSampler.SetFilterMode(static_cast<SamplerFilter>(iValue));
if (matData.GetIntegerParameter(MaterialData::SpecularWrap, &iValue))
m_specularSampler.SetWrapMode(static_cast<SamplerWrap>(iValue));
// Stencil
if (matData.GetIntegerParameter(MaterialData::StencilCompare, &iValue))
m_pipelineInfo.stencilCompare.front = static_cast<RendererComparison>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilFail, &iValue))
m_pipelineInfo.stencilFail.front = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilPass, &iValue))
m_pipelineInfo.stencilPass.front = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilZFail, &iValue))
m_pipelineInfo.stencilDepthFail.front = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilMask, &iValue))
m_pipelineInfo.stencilWriteMask.front = static_cast<UInt32>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilReference, &iValue))
m_pipelineInfo.stencilReference.front = static_cast<unsigned int>(iValue);
// Stencil (back)
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilCompare, &iValue))
m_pipelineInfo.stencilCompare.back = static_cast<RendererComparison>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilFail, &iValue))
m_pipelineInfo.stencilFail.back = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilPass, &iValue))
m_pipelineInfo.stencilPass.back = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilZFail, &iValue))
m_pipelineInfo.stencilDepthFail.back = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilMask, &iValue))
m_pipelineInfo.stencilWriteMask.back = static_cast<UInt32>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilReference, &iValue))
m_pipelineInfo.stencilReference.back = static_cast<unsigned int>(iValue);
InvalidatePipeline();
// Textures
if (matParams.loadAlphaMap && matData.GetStringParameter(MaterialData::AlphaTexturePath, &path))
SetAlphaMap(path);
if (matParams.loadDiffuseMap && matData.GetStringParameter(MaterialData::DiffuseTexturePath, &path))
SetDiffuseMap(path);
if (matParams.loadEmissiveMap && matData.GetStringParameter(MaterialData::EmissiveTexturePath, &path))
SetEmissiveMap(path);
if (matParams.loadHeightMap && matData.GetStringParameter(MaterialData::HeightTexturePath, &path))
SetHeightMap(path);
if (matParams.loadNormalMap && matData.GetStringParameter(MaterialData::NormalTexturePath, &path))
SetNormalMap(path);
if (matParams.loadSpecularMap && matData.GetStringParameter(MaterialData::SpecularTexturePath, &path))
SetSpecularMap(path);
SetShader(matParams.shaderName);
}
/*!
* \brief Builds a ParameterList with material data
*
* \param matData Destination parameter list which will receive material data
*/
void Material::SaveToParameters(ParameterList* matData)
{
NazaraAssert(matData, "Invalid ParameterList");
matData->SetParameter(MaterialData::AlphaTest, IsAlphaTestEnabled());
matData->SetParameter(MaterialData::AlphaThreshold, GetAlphaThreshold());
matData->SetParameter(MaterialData::AmbientColor, GetAmbientColor());
matData->SetParameter(MaterialData::CullingSide, static_cast<long long>(GetFaceCulling()));
matData->SetParameter(MaterialData::DepthFunc, static_cast<long long>(GetDepthFunc()));
matData->SetParameter(MaterialData::DepthSorting, IsDepthSortingEnabled());
matData->SetParameter(MaterialData::DiffuseColor, GetDiffuseColor());
matData->SetParameter(MaterialData::DstBlend, static_cast<long long>(GetDstBlend()));
matData->SetParameter(MaterialData::FaceFilling, static_cast<long long>(GetFaceFilling()));
matData->SetParameter(MaterialData::LineWidth, GetLineWidth());
matData->SetParameter(MaterialData::PointSize, GetPointSize());
matData->SetParameter(MaterialData::Shininess, GetShininess());
matData->SetParameter(MaterialData::SpecularColor, GetSpecularColor());
matData->SetParameter(MaterialData::SrcBlend, static_cast<long long>(GetSrcBlend()));
// RendererParameter
matData->SetParameter(MaterialData::Blending, IsBlendingEnabled());
matData->SetParameter(MaterialData::ColorWrite, IsColorWriteEnabled());
matData->SetParameter(MaterialData::DepthBuffer, IsDepthBufferEnabled());
matData->SetParameter(MaterialData::DepthWrite, IsDepthWriteEnabled());
matData->SetParameter(MaterialData::FaceCulling, IsFaceCullingEnabled());
matData->SetParameter(MaterialData::ScissorTest, IsScissorTestEnabled());
matData->SetParameter(MaterialData::StencilTest, IsStencilTestEnabled());
// Samplers
matData->SetParameter(MaterialData::DiffuseAnisotropyLevel, static_cast<long long>(GetDiffuseSampler().GetAnisotropicLevel()));
matData->SetParameter(MaterialData::DiffuseFilter, static_cast<long long>(GetDiffuseSampler().GetFilterMode()));
matData->SetParameter(MaterialData::DiffuseWrap, static_cast<long long>(GetDiffuseSampler().GetWrapMode()));
matData->SetParameter(MaterialData::SpecularAnisotropyLevel, static_cast<long long>(GetSpecularSampler().GetAnisotropicLevel()));
matData->SetParameter(MaterialData::SpecularFilter, static_cast<long long>(GetSpecularSampler().GetFilterMode()));
matData->SetParameter(MaterialData::SpecularWrap, static_cast<long long>(GetSpecularSampler().GetWrapMode()));
// Stencil
matData->SetParameter(MaterialData::StencilCompare, static_cast<long long>(GetPipelineInfo().stencilCompare.front));
matData->SetParameter(MaterialData::StencilFail, static_cast<long long>(GetPipelineInfo().stencilFail.front));
matData->SetParameter(MaterialData::StencilPass, static_cast<long long>(GetPipelineInfo().stencilPass.front));
matData->SetParameter(MaterialData::StencilZFail, static_cast<long long>(GetPipelineInfo().stencilDepthFail.front));
matData->SetParameter(MaterialData::StencilMask, static_cast<long long>(GetPipelineInfo().stencilWriteMask.front));
matData->SetParameter(MaterialData::StencilReference, static_cast<long long>(GetPipelineInfo().stencilReference.front));
// Stencil (back)
matData->SetParameter(MaterialData::BackFaceStencilCompare, static_cast<long long>(GetPipelineInfo().stencilCompare.back));
matData->SetParameter(MaterialData::BackFaceStencilFail, static_cast<long long>(GetPipelineInfo().stencilFail.back));
matData->SetParameter(MaterialData::BackFaceStencilPass, static_cast<long long>(GetPipelineInfo().stencilPass.back));
matData->SetParameter(MaterialData::BackFaceStencilZFail, static_cast<long long>(GetPipelineInfo().stencilDepthFail.back));
matData->SetParameter(MaterialData::BackFaceStencilMask, static_cast<long long>(GetPipelineInfo().stencilWriteMask.back));
matData->SetParameter(MaterialData::BackFaceStencilReference, static_cast<long long>(GetPipelineInfo().stencilReference.back));
// Textures
if (HasAlphaMap())
{
const String& path = GetAlphaMap()->GetFilePath();
if (!path.IsEmpty())
matData->SetParameter(MaterialData::AlphaTexturePath, path);
}
if (HasDiffuseMap())
{
const String& path = GetDiffuseMap()->GetFilePath();
if (!path.IsEmpty())
matData->SetParameter(MaterialData::DiffuseTexturePath, path);
}
if (HasEmissiveMap())
{
const String& path = GetEmissiveMap()->GetFilePath();
if (!path.IsEmpty())
matData->SetParameter(MaterialData::EmissiveTexturePath, path);
}
if (HasHeightMap())
{
const String& path = GetHeightMap()->GetFilePath();
if (!path.IsEmpty())
matData->SetParameter(MaterialData::HeightTexturePath, path);
}
if (HasNormalMap())
{
const String& path = GetNormalMap()->GetFilePath();
if (!path.IsEmpty())
matData->SetParameter(MaterialData::NormalTexturePath, path);
}
if (HasSpecularMap())
{
const String& path = GetSpecularMap()->GetFilePath();
if (!path.IsEmpty())
matData->SetParameter(MaterialData::SpecularTexturePath, path);
}
}
/*!
* \brief Resets the material, cleans everything
*
* \remark Invalidates the pipeline
*/
void Material::Reset()
{
OnMaterialReset(this);
m_alphaMap.Reset();
m_depthMaterial.Reset();
m_diffuseMap.Reset();
m_emissiveMap.Reset();
m_heightMap.Reset();
m_normalMap.Reset();
m_specularMap.Reset();
m_alphaThreshold = 0.2f;
m_ambientColor = Color(128, 128, 128);
m_diffuseColor = Color::White;
m_diffuseSampler = TextureSampler();
m_reflectionMode = ReflectionMode_Skybox;
m_shadowCastingEnabled = true;
m_shininess = 50.f;
m_specularColor = Color::White;
m_specularSampler = TextureSampler();
m_pipelineInfo = MaterialPipelineInfo();
m_pipelineInfo.depthBuffer = true;
m_pipelineInfo.faceCulling = true;
m_reflectionSize = 256;
SetShader("Basic");
InvalidatePipeline();
}
/*!
* \brief Copies the other material
*
* \param material Material to copy into this
*/
void Material::Copy(const Material& material)
{
// Copy of base states
m_alphaThreshold = material.m_alphaThreshold;
m_ambientColor = material.m_ambientColor;
m_diffuseColor = material.m_diffuseColor;
m_diffuseSampler = material.m_diffuseSampler;
m_pipelineInfo = material.m_pipelineInfo;
m_shininess = material.m_shininess;
m_shadowCastingEnabled = material.m_shadowCastingEnabled;
m_specularColor = material.m_specularColor;
m_specularSampler = material.m_specularSampler;
// Copy of reference to the textures
m_alphaMap = material.m_alphaMap;
m_depthMaterial = material.m_depthMaterial;
m_diffuseMap = material.m_diffuseMap;
m_emissiveMap = material.m_emissiveMap;
m_heightMap = material.m_heightMap;
m_normalMap = material.m_normalMap;
m_specularMap = material.m_specularMap;
InvalidatePipeline();
}
/*!
* \brief Initializes the material librairies
* \return true If successful
*
* \remark Produces a NazaraError if the material library failed to be initialized
*/
bool Material::Initialize()
{
if (!MaterialLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
if (!MaterialManager::Initialize())
{
NazaraError("Failed to initialise manager");
return false;
}
s_defaultMaterial = New();
s_defaultMaterial->EnableFaceCulling(false);
s_defaultMaterial->SetFaceFilling(FaceFilling_Line);
MaterialLibrary::Register("Default", s_defaultMaterial);
unsigned int textureUnit = 0;
s_textureUnits[TextureMap_Diffuse] = textureUnit++;
s_textureUnits[TextureMap_Alpha] = textureUnit++;
s_textureUnits[TextureMap_Specular] = textureUnit++;
s_textureUnits[TextureMap_Normal] = textureUnit++;
s_textureUnits[TextureMap_Emissive] = textureUnit++;
s_textureUnits[TextureMap_Overlay] = textureUnit++;
s_textureUnits[TextureMap_ReflectionCube] = textureUnit++;
s_textureUnits[TextureMap_Height] = textureUnit++;
s_textureUnits[TextureMap_Shadow2D_1] = textureUnit++;
s_textureUnits[TextureMap_ShadowCube_1] = textureUnit++;
s_textureUnits[TextureMap_Shadow2D_2] = textureUnit++;
s_textureUnits[TextureMap_ShadowCube_2] = textureUnit++;
s_textureUnits[TextureMap_Shadow2D_3] = textureUnit++;
s_textureUnits[TextureMap_ShadowCube_3] = textureUnit++;
return true;
}
/*!
* \brief Uninitializes the material librairies
*/
void Material::Uninitialize()
{
s_defaultMaterial.Reset();
MaterialManager::Uninitialize();
MaterialLibrary::Uninitialize();
}
std::array<int, TextureMap_Max + 1> Material::s_textureUnits;
MaterialLibrary::LibraryMap Material::s_library;
MaterialLoader::LoaderList Material::s_loaders;
MaterialManager::ManagerMap Material::s_managerMap;
MaterialManager::ManagerParams Material::s_managerParameters;
MaterialRef Material::s_defaultMaterial = nullptr;
}