This commit is contained in:
Jérôme Leclercq 2020-09-22 17:40:26 +02:00
parent abf58857d7
commit 3b2e375382
27 changed files with 5502 additions and 8 deletions

View File

@ -0,0 +1,72 @@
// 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
#pragma once
#ifndef NAZARA_BASIC_MATERIAL_HPP
#define NAZARA_BASIC_MATERIAL_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Material.hpp>
namespace Nz
{
class NAZARA_GRAPHICS_API BasicMaterial
{
friend class MaterialPipeline;
public:
BasicMaterial(Material* material);
inline const TextureRef& GetAlphaMap() const;
float GetAlphaThreshold() const;
Color GetDiffuseColor() const;
inline const TextureRef& GetDiffuseMap() const;
inline bool HasAlphaMap() const;
inline bool HasAlphaThreshold() const;
inline bool HasDiffuseColor() const;
inline bool HasDiffuseMap() const;
inline bool SetAlphaMap(const String& textureName);
inline void SetAlphaMap(TextureRef alphaMap);
void SetAlphaThreshold(float alphaThreshold);
void SetDiffuseColor(const Color& diffuse);
inline bool SetDiffuseMap(const String& textureName);
inline void SetDiffuseMap(TextureRef diffuseMap);
static const std::shared_ptr<MaterialSettings>& GetSettings();
private:
struct UniformOffsets
{
std::size_t alphaThreshold;
std::size_t diffuseColor;
};
struct TextureIndexes
{
std::size_t alpha;
std::size_t diffuse;
};
static bool Initialize();
static void Uninitialize();
MaterialRef m_material;
std::size_t m_uniformBlockIndex;
TextureIndexes m_textureIndexes;
UniformOffsets m_uniformOffsets;
static std::shared_ptr<MaterialSettings> s_materialSettings;
static std::size_t s_uniformBlockIndex;
static RenderPipelineLayoutRef s_renderPipelineLayout;
static TextureIndexes s_textureIndexes;
static UniformOffsets s_uniformOffsets;
};
}
#include <Nazara/Graphics/BasicMaterial.inl>
#endif // NAZARA_BASIC_MATERIAL_HPP

View File

@ -0,0 +1,91 @@
// 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/BasicMaterial.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
inline const TextureRef& BasicMaterial::GetAlphaMap() const
{
NazaraAssert(HasAlphaMap(), "Material has no alpha map slot");
return m_material->GetTexture(m_textureIndexes.alpha);
}
inline const TextureRef& BasicMaterial::GetDiffuseMap() const
{
NazaraAssert(HasDiffuseMap(), "Material has no alpha map slot");
return m_material->GetTexture(m_textureIndexes.diffuse);
}
inline bool BasicMaterial::HasAlphaMap() const
{
return m_textureIndexes.alpha != MaterialSettings::InvalidIndex;
}
inline bool BasicMaterial::HasAlphaThreshold() const
{
return m_uniformOffsets.alphaThreshold != MaterialSettings::InvalidIndex;
}
inline bool BasicMaterial::HasDiffuseColor() const
{
return m_uniformOffsets.diffuseColor != MaterialSettings::InvalidIndex;
}
inline bool BasicMaterial::HasDiffuseMap() const
{
return m_textureIndexes.diffuse != MaterialSettings::InvalidIndex;
}
inline bool BasicMaterial::SetAlphaMap(const String& textureName)
{
TextureRef texture = TextureLibrary::Query(textureName);
if (!texture)
{
texture = TextureManager::Get(textureName);
if (!texture)
{
NazaraError("Failed to get alpha map \"" + textureName + "\"");
return false;
}
}
SetAlphaMap(std::move(texture));
return true;
}
inline void BasicMaterial::SetAlphaMap(TextureRef alphaMap)
{
NazaraAssert(HasAlphaMap(), "Material has no alpha map slot");
m_material->SetTexture(m_textureIndexes.alpha, std::move(alphaMap));
}
inline bool BasicMaterial::SetDiffuseMap(const String& textureName)
{
TextureRef texture = TextureLibrary::Query(textureName);
if (!texture)
{
texture = TextureManager::Get(textureName);
if (!texture)
{
NazaraError("Failed to get diffuse map \"" + textureName + "\"");
return false;
}
}
SetDiffuseMap(std::move(texture));
return true;
}
inline void BasicMaterial::SetDiffuseMap(TextureRef diffuseMap)
{
NazaraAssert(HasDiffuseMap(), "Material has no diffuse map slot");
m_material->SetTexture(m_textureIndexes.diffuse, std::move(diffuseMap));
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -0,0 +1,215 @@
// 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
#pragma once
#ifndef NAZARA_CULLINGLIST_HPP
#define NAZARA_CULLINGLIST_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/Signal.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Math/BoundingVolume.hpp>
#include <Nazara/Math/Frustum.hpp>
#include <Nazara/Math/Sphere.hpp>
#include <vector>
namespace Nz
{
template<typename T>
class CullingList
{
public:
template<CullTest> class Entry;
class BoxEntry;
class NoTestEntry;
class SphereEntry;
class VolumeEntry;
template<CullTest> friend class Entry;
friend BoxEntry;
friend NoTestEntry;
friend SphereEntry;
friend VolumeEntry;
using ResultContainer = std::vector<const T*>;
CullingList() = default;
CullingList(const CullingList& renderable) = delete;
CullingList(CullingList&& renderable) = delete;
~CullingList();
std::size_t Cull(const Frustumf& frustum, bool* forceInvalidation = nullptr);
std::size_t FillWithAllEntries(bool* forceInvalidation = nullptr);
const ResultContainer& GetFullyVisibleResults() const;
const ResultContainer& GetPartiallyVisibleResults() const;
BoxEntry RegisterBoxTest(const T* renderable);
NoTestEntry RegisterNoTest(const T* renderable);
SphereEntry RegisterSphereTest(const T* renderable);
VolumeEntry RegisterVolumeTest(const T* renderable);
CullingList& operator=(const CullingList& renderable) = delete;
CullingList& operator=(CullingList&& renderable) = delete;
NazaraSignal(OnCullingListRelease, CullingList* /*cullingList*/);
private:
inline void NotifyBoxUpdate(std::size_t index, const Boxf& boundingVolume);
inline void NotifyForceInvalidation(CullTest type, std::size_t index);
inline void NotifyMovement(CullTest type, std::size_t index, void* oldPtr, void* newPtr);
inline void NotifyRelease(CullTest type, std::size_t index);
inline void NotifySphereUpdate(std::size_t index, const Spheref& sphere);
inline void NotifyVolumeUpdate(std::size_t index, const BoundingVolumef& boundingVolume);
struct BoxVisibilityEntry
{
Boxf box;
BoxEntry* entry;
const T* renderable;
bool forceInvalidation;
};
struct NoTestVisibilityEntry
{
NoTestEntry* entry;
const T* renderable;
bool forceInvalidation;
};
struct SphereVisibilityEntry
{
Spheref sphere;
SphereEntry* entry;
const T* renderable;
bool forceInvalidation;
};
struct VolumeVisibilityEntry
{
BoundingVolumef volume;
VolumeEntry* entry;
const T* renderable;
bool forceInvalidation;
};
std::vector<BoxVisibilityEntry> m_boxTestList;
std::vector<NoTestVisibilityEntry> m_noTestList;
std::vector<SphereVisibilityEntry> m_sphereTestList;
std::vector<VolumeVisibilityEntry> m_volumeTestList;
ResultContainer m_fullyVisibleResults;
ResultContainer m_partiallyVisibleResults;
};
template<typename T>
template<CullTest Type>
class CullingList<T>::Entry
{
public:
Entry();
Entry(const Entry&) = delete;
Entry(Entry&& entry);
~Entry();
void ForceInvalidation();
CullingList* GetParent() const;
void UpdateIndex(std::size_t index);
Entry& operator=(const Entry&) = delete;
Entry& operator=(Entry&& entry);
protected:
Entry(CullingList* parent, std::size_t index);
std::size_t m_index;
CullingList* m_parent;
};
template<typename T>
class CullingList<T>::BoxEntry : public CullingList<T>::template Entry<CullTest::Box>
{
friend CullingList;
using ParentType = Entry<CullTest::Box>;
public:
BoxEntry();
BoxEntry(BoxEntry&&) = default;
~BoxEntry() = default;
void UpdateBox(const Boxf& box);
BoxEntry& operator=(BoxEntry&&) = default;
private:
BoxEntry(CullingList* parent, std::size_t index);
};
template<typename T>
class CullingList<T>::NoTestEntry : public CullingList<T>::template Entry<CullTest::NoTest>
{
friend CullingList;
using ParentType = Entry<CullTest::NoTest>;
public:
NoTestEntry();
NoTestEntry(NoTestEntry&&) = default;
~NoTestEntry() = default;
NoTestEntry& operator=(NoTestEntry&&) = default;
private:
NoTestEntry(CullingList* parent, std::size_t index);
};
template<typename T>
class CullingList<T>::SphereEntry : public CullingList<T>::template Entry<CullTest::Sphere>
{
friend CullingList;
using ParentType = Entry<CullTest::Sphere>;
public:
SphereEntry();
SphereEntry(SphereEntry&&) = default;
~SphereEntry() = default;
void UpdateSphere(const Spheref& sphere);
SphereEntry& operator=(SphereEntry&&) = default;
private:
SphereEntry(CullingList* parent, std::size_t index);
};
template<typename T>
class CullingList<T>::VolumeEntry : public CullingList<T>::template Entry<CullTest::Volume>
{
friend CullingList;
using ParentType = Entry<CullTest::Volume>;
public:
VolumeEntry();
VolumeEntry(VolumeEntry&&) = default;
~VolumeEntry() = default;
void UpdateVolume(const BoundingVolumef& sphere);
VolumeEntry& operator=(VolumeEntry&&) = default;
private:
VolumeEntry(CullingList* parent, std::size_t index);
};
}
#include <Nazara/Graphics/CullingList.inl>
#endif // NAZARA_CULLINGLIST_HPP

View File

@ -0,0 +1,498 @@
// 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/CullingList.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
template<typename T>
CullingList<T>::~CullingList()
{
OnCullingListRelease(this);
}
template<typename T>
std::size_t CullingList<T>::Cull(const Frustumf& frustum, bool* forceInvalidation)
{
m_fullyVisibleResults.clear();
m_partiallyVisibleResults.clear();
bool forcedInvalidation = false;
std::size_t fullyVisibleHash = 5U;
std::size_t partiallyVisibleHash = 5U;
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
{
return currentHash * 23 + newHash;
};
for (BoxVisibilityEntry& entry : m_boxTestList)
{
switch (frustum.Intersect(entry.box))
{
case IntersectionSide_Inside:
m_fullyVisibleResults.push_back(entry.renderable);
fullyVisibleHash = CombineHash(fullyVisibleHash, std::hash<const T*>()(entry.renderable));
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
entry.forceInvalidation = false;
break;
case IntersectionSide_Intersecting:
m_partiallyVisibleResults.push_back(entry.renderable);
partiallyVisibleHash = CombineHash(partiallyVisibleHash, std::hash<const T*>()(entry.renderable));
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
entry.forceInvalidation = false;
break;
case IntersectionSide_Outside:
break;
}
}
for (NoTestVisibilityEntry& entry : m_noTestList)
{
m_fullyVisibleResults.push_back(entry.renderable);
CombineHash(fullyVisibleHash, std::hash<const T*>()(entry.renderable));
if (entry.forceInvalidation)
{
forcedInvalidation = true;
entry.forceInvalidation = false;
}
}
for (SphereVisibilityEntry& entry : m_sphereTestList)
{
switch (frustum.Intersect(entry.sphere))
{
case IntersectionSide_Inside:
m_fullyVisibleResults.push_back(entry.renderable);
fullyVisibleHash = CombineHash(fullyVisibleHash, std::hash<const T*>()(entry.renderable));
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
entry.forceInvalidation = false;
break;
case IntersectionSide_Intersecting:
m_partiallyVisibleResults.push_back(entry.renderable);
partiallyVisibleHash = CombineHash(partiallyVisibleHash, std::hash<const T*>()(entry.renderable));
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
entry.forceInvalidation = false;
break;
case IntersectionSide_Outside:
break;
}
}
for (VolumeVisibilityEntry& entry : m_volumeTestList)
{
switch (frustum.Intersect(entry.volume))
{
case IntersectionSide_Inside:
m_fullyVisibleResults.push_back(entry.renderable);
fullyVisibleHash = CombineHash(fullyVisibleHash, std::hash<const T*>()(entry.renderable));
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
entry.forceInvalidation = false;
break;
case IntersectionSide_Intersecting:
m_partiallyVisibleResults.push_back(entry.renderable);
partiallyVisibleHash = CombineHash(partiallyVisibleHash, std::hash<const T*>()(entry.renderable));
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
entry.forceInvalidation = false;
break;
case IntersectionSide_Outside:
break;
}
}
if (forceInvalidation)
*forceInvalidation = forcedInvalidation;
return 5 + partiallyVisibleHash * 17 + fullyVisibleHash;
}
template<typename T>
std::size_t CullingList<T>::FillWithAllEntries(bool* forceInvalidation)
{
m_fullyVisibleResults.clear();
m_partiallyVisibleResults.clear();
bool forcedInvalidation = false;
std::size_t visibleHash = 5U;
auto FillWithList = [&](auto& testList)
{
for (auto& entry : testList)
{
m_fullyVisibleResults.push_back(entry.renderable);
visibleHash = visibleHash * 23 + std::hash<const T*>()(entry.renderable);
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
entry.forceInvalidation = false;
}
};
FillWithList(m_boxTestList);
FillWithList(m_noTestList);
FillWithList(m_sphereTestList);
FillWithList(m_volumeTestList);
if (forceInvalidation)
*forceInvalidation = forcedInvalidation;
return visibleHash;
}
template<typename T>
auto CullingList<T>::GetFullyVisibleResults() const -> const ResultContainer&
{
return m_fullyVisibleResults;
}
template<typename T>
auto CullingList<T>::GetPartiallyVisibleResults() const -> const ResultContainer&
{
return m_partiallyVisibleResults;
}
template<typename T>
auto CullingList<T>::RegisterBoxTest(const T* renderable) -> BoxEntry
{
BoxEntry newEntry(this, m_boxTestList.size());
m_boxTestList.emplace_back(BoxVisibilityEntry{ Nz::Boxf(), &newEntry, renderable, false }); //< Address of entry will be updated when moving
return newEntry;
}
template<typename T>
auto CullingList<T>::RegisterNoTest(const T* renderable) -> NoTestEntry
{
NoTestEntry newEntry(this, m_volumeTestList.size());
m_noTestList.emplace_back(NoTestVisibilityEntry{&newEntry, renderable, false}); //< Address of entry will be updated when moving
return newEntry;
}
template<typename T>
auto CullingList<T>::RegisterSphereTest(const T* renderable) -> SphereEntry
{
SphereEntry newEntry(this, m_sphereTestList.size());
m_sphereTestList.emplace_back(SphereVisibilityEntry{Nz::Spheref(), &newEntry, renderable, false}); //< Address of entry will be updated when moving
return newEntry;
}
template<typename T>
auto CullingList<T>::RegisterVolumeTest(const T* renderable) -> VolumeEntry
{
VolumeEntry newEntry(this, m_volumeTestList.size());
m_volumeTestList.emplace_back(VolumeVisibilityEntry{Nz::BoundingVolumef(), &newEntry, renderable, false}); //< Address of entry will be updated when moving
return newEntry;
}
template<typename T>
inline void CullingList<T>::NotifyBoxUpdate(std::size_t index, const Boxf& box)
{
m_boxTestList[index].box = box;
}
template<typename T>
void CullingList<T>::NotifyForceInvalidation(CullTest type, std::size_t index)
{
switch (type)
{
case CullTest::Box:
{
m_boxTestList[index].forceInvalidation = true;
break;
}
case CullTest::NoTest:
{
m_noTestList[index].forceInvalidation = true;
break;
}
case CullTest::Sphere:
{
m_sphereTestList[index].forceInvalidation = true;
break;
}
case CullTest::Volume:
{
m_volumeTestList[index].forceInvalidation = true;
break;
}
default:
NazaraInternalError("Unhandled culltype");
break;
}
}
template<typename T>
void CullingList<T>::NotifyMovement(CullTest type, std::size_t index, void* oldPtr, void* newPtr)
{
NazaraUnused(oldPtr);
switch (type)
{
case CullTest::Box:
{
BoxVisibilityEntry& entry = m_boxTestList[index];
NazaraAssert(entry.entry == oldPtr, "Invalid box entry");
entry.entry = static_cast<BoxEntry*>(newPtr);
break;
}
case CullTest::NoTest:
{
NoTestVisibilityEntry& entry = m_noTestList[index];
NazaraAssert(entry.entry == oldPtr, "Invalid entry");
entry.entry = static_cast<NoTestEntry*>(newPtr);
break;
}
case CullTest::Sphere:
{
SphereVisibilityEntry& entry = m_sphereTestList[index];
NazaraAssert(entry.entry == oldPtr, "Invalid sphere entry");
entry.entry = static_cast<SphereEntry*>(newPtr);
break;
}
case CullTest::Volume:
{
VolumeVisibilityEntry& entry = m_volumeTestList[index];
NazaraAssert(entry.entry == oldPtr, "Invalid volume entry");
entry.entry = static_cast<VolumeEntry*>(newPtr);
break;
}
default:
NazaraInternalError("Unhandled culltype");
break;
}
}
template<typename T>
void CullingList<T>::NotifyRelease(CullTest type, std::size_t index)
{
switch (type)
{
case CullTest::Box:
{
m_boxTestList[index] = std::move(m_boxTestList.back());
m_boxTestList[index].entry->UpdateIndex(index);
m_boxTestList.pop_back();
break;
}
case CullTest::NoTest:
{
m_noTestList[index] = std::move(m_noTestList.back());
m_noTestList[index].entry->UpdateIndex(index);
m_noTestList.pop_back();
break;
}
case CullTest::Sphere:
{
m_sphereTestList[index] = std::move(m_sphereTestList.back());
m_sphereTestList[index].entry->UpdateIndex(index);
m_sphereTestList.pop_back();
break;
}
case CullTest::Volume:
{
m_volumeTestList[index] = std::move(m_volumeTestList.back());
m_volumeTestList[index].entry->UpdateIndex(index);
m_volumeTestList.pop_back();
break;
}
default:
NazaraInternalError("Unhandled culltype");
break;
}
}
template<typename T>
void CullingList<T>::NotifySphereUpdate(std::size_t index, const Spheref& sphere)
{
m_sphereTestList[index].sphere = sphere;
}
template<typename T>
void CullingList<T>::NotifyVolumeUpdate(std::size_t index, const BoundingVolumef& boundingVolume)
{
m_volumeTestList[index].volume = boundingVolume;
}
//////////////////////////////////////////////////////////////////////////
template<typename T>
template<CullTest Type>
CullingList<T>::Entry<Type>::Entry() :
m_parent(nullptr)
{
}
template<typename T>
template<CullTest Type>
CullingList<T>::Entry<Type>::Entry(CullingList* parent, std::size_t index) :
m_index(index),
m_parent(parent)
{
}
template<typename T>
template<CullTest Type>
CullingList<T>::Entry<Type>::Entry(Entry&& entry) :
m_index(entry.m_index),
m_parent(entry.m_parent)
{
if (m_parent)
m_parent->NotifyMovement(Type, m_index, &entry, this);
entry.m_parent = nullptr;
}
template<typename T>
template<CullTest Type>
CullingList<T>::Entry<Type>::~Entry()
{
if (m_parent)
m_parent->NotifyRelease(Type, m_index);
}
template<typename T>
template<CullTest Type>
void CullingList<T>::Entry<Type>::ForceInvalidation()
{
m_parent->NotifyForceInvalidation(Type, m_index);
}
template<typename T>
template<CullTest Type>
CullingList<T>* CullingList<T>::Entry<Type>::GetParent() const
{
return m_parent;
}
template<typename T>
template<CullTest Type>
void CullingList<T>::Entry<Type>::UpdateIndex(std::size_t index)
{
m_index = index;
}
template<typename T>
template<CullTest Type>
typename CullingList<T>::template Entry<Type>& CullingList<T>::Entry<Type>::operator=(Entry&& entry)
{
m_index = entry.m_index;
m_parent = entry.m_parent;
if (m_parent)
m_parent->NotifyMovement(Type, m_index, &entry, this);
entry.m_parent = nullptr;
return *this;
}
//////////////////////////////////////////////////////////////////////////
template<typename T>
CullingList<T>::BoxEntry::BoxEntry() :
ParentType()
{
}
template<typename T>
CullingList<T>::BoxEntry::BoxEntry(CullingList* parent, std::size_t index) :
ParentType(parent, index)
{
}
template<typename T>
void CullingList<T>::BoxEntry::UpdateBox(const Boxf& box)
{
this->m_parent->NotifyBoxUpdate(this->m_index, box);
}
//////////////////////////////////////////////////////////////////////////
template<typename T>
CullingList<T>::NoTestEntry::NoTestEntry() :
ParentType()
{
}
template<typename T>
CullingList<T>::NoTestEntry::NoTestEntry(CullingList* parent, std::size_t index) :
ParentType(parent, index)
{
}
//////////////////////////////////////////////////////////////////////////
template<typename T>
CullingList<T>::SphereEntry::SphereEntry() :
ParentType()
{
}
template<typename T>
CullingList<T>::SphereEntry::SphereEntry(CullingList* parent, std::size_t index) :
ParentType(parent, index)
{
}
template<typename T>
void CullingList<T>::SphereEntry::UpdateSphere(const Spheref& sphere)
{
this->m_parent->NotifySphereUpdate(this->m_index, sphere);
}
//////////////////////////////////////////////////////////////////////////
template<typename T>
CullingList<T>::VolumeEntry::VolumeEntry() :
ParentType()
{
}
template<typename T>
CullingList<T>::VolumeEntry::VolumeEntry(CullingList* parent, std::size_t index) :
ParentType(parent, index)
{
}
template<typename T>
void CullingList<T>::VolumeEntry::UpdateVolume(const BoundingVolumef& volume)
{
this->m_parent->NotifyVolumeUpdate(this->m_index, volume);
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -9,6 +9,24 @@
namespace Nz
{
enum class CullTest
{
Box,
NoTest,
Sphere,
Volume
};
enum PredefinedShaderBinding
{
PredefinedShaderBinding_TexOverlay,
PredefinedShaderBinding_UboInstanceData,
PredefinedShaderBinding_UboLighData,
PredefinedShaderBinding_UboViewerData,
PredefinedShaderBinding_Max = PredefinedShaderBinding_UboViewerData
};
}
#endif // NAZARA_ENUMS_GRAPHICS_HPP

View File

@ -10,9 +10,12 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
namespace Nz
{
class RenderDevice;
class NAZARA_GRAPHICS_API Graphics : public ModuleBase<Graphics>
{
friend ModuleBase;
@ -20,14 +23,25 @@ namespace Nz
public:
using Dependencies = TypeList<Renderer>;
struct Config {};
struct Config;
Graphics(Config /*config*/);
~Graphics();
Graphics(Config config);
~Graphics() = default;
inline RenderDevice& GetRenderDevice();
struct Config
{
bool useDedicatedRenderDevice = true;
};
private:
std::shared_ptr<RenderDevice> m_renderDevice;
static Graphics* s_instance;
};
}
#include <Nazara/Graphics/Graphics.inl>
#endif

View File

@ -0,0 +1,16 @@
// 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/Graphics.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
inline RenderDevice& Graphics::GetRenderDevice()
{
return *m_renderDevice;
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -0,0 +1,150 @@
// 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
#pragma once
#ifndef NAZARA_BASE_MATERIAL_HPP
#define NAZARA_BASE_MATERIAL_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/Color.hpp>
#include <Nazara/Core/ObjectLibrary.hpp>
#include <Nazara/Core/ObjectRef.hpp>
#include <Nazara/Core/RefCounted.hpp>
#include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceLoader.hpp>
#include <Nazara/Core/ResourceManager.hpp>
#include <Nazara/Core/ResourceParameters.hpp>
#include <Nazara/Core/Signal.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/MaterialPipeline.hpp>
#include <Nazara/Renderer/Texture.hpp>
#include <Nazara/Renderer/TextureSampler.hpp>
#include <Nazara/Utility/UniformBuffer.hpp>
#include <string>
#include <vector>
namespace Nz
{
struct NAZARA_GRAPHICS_API MaterialParams : ResourceParameters
{
bool loadAlphaMap = true;
bool loadDiffuseMap = true;
bool loadEmissiveMap = true;
bool loadHeightMap = true;
bool loadNormalMap = true;
bool loadSpecularMap = true;
std::string shaderName = "Basic";
bool IsValid() const;
};
class NAZARA_GRAPHICS_API Material : public RefCounted, public Resource
{
public:
Material(std::shared_ptr<const MaterialSettings> settings);
inline Material(const Material& material);
inline ~Material();
void Apply(const MaterialPipeline::Instance& instance) const;
void BuildFromParameters(const ParameterList& matData, const MaterialParams& matParams = MaterialParams());
inline void Configure(const MaterialPipeline* pipeline);
inline void Configure(const MaterialPipelineInfo& pipelineInfo);
inline bool Configure(const String& pipelineName);
inline void EnableAlphaTest(bool alphaTest);
inline void EnableBlending(bool blending);
inline void EnableColorWrite(bool colorWrite);
inline void EnableDepthBuffer(bool depthBuffer);
inline void EnableDepthSorting(bool depthSorting);
inline void EnableDepthWrite(bool depthWrite);
inline void EnableFaceCulling(bool faceCulling);
inline void EnableReflectionMapping(bool reflection);
inline void EnableScissorTest(bool scissorTest);
inline void EnableShadowCasting(bool castShadows);
inline void EnableShadowReceive(bool receiveShadows);
inline void EnableStencilTest(bool stencilTest);
inline void EnableVertexColor(bool vertexColor);
inline void EnsurePipelineUpdate() const;
inline RendererComparison GetDepthFunc() const;
inline BlendFunc GetDstBlend() const;
inline FaceSide GetFaceCulling() const;
inline FaceFilling GetFaceFilling() const;
inline float GetLineWidth() const;
inline const MaterialPipeline* GetPipeline() const;
inline const MaterialPipelineInfo& GetPipelineInfo() const;
inline float GetPointSize() const;
inline const std::shared_ptr<const MaterialSettings>& GetSettings() const;
inline const std::shared_ptr<Shader>& GetShader() const;
inline BlendFunc GetSrcBlend() const;
inline UniformBuffer* GetSharedUniformBuffer(std::size_t bufferIndex) const;
inline const std::shared_ptr<Texture>& GetTexture(std::size_t textureIndex) const;
inline const std::shared_ptr<TextureSampler>& GetTextureSampler(std::size_t textureIndex) const;
inline UniformBufferRef& GetUniformBuffer(std::size_t bufferIndex);
inline const UniformBufferRef& GetUniformBuffer(std::size_t bufferIndex) const;
inline bool HasDepthMaterial() const;
inline bool HasTexture(std::size_t textureIndex) const;
inline bool HasVertexColor() const;
inline bool IsAlphaTestEnabled() const;
inline bool IsBlendingEnabled() const;
inline bool IsColorWriteEnabled() const;
inline bool IsDepthBufferEnabled() const;
inline bool IsDepthSortingEnabled() const;
inline bool IsDepthWriteEnabled() const;
inline bool IsFaceCullingEnabled() const;
inline bool IsReflectionMappingEnabled() const;
inline bool IsScissorTestEnabled() const;
inline bool IsStencilTestEnabled() const;
inline bool IsShadowCastingEnabled() const;
inline bool IsShadowReceiveEnabled() const;
void SaveToParameters(ParameterList* matData);
inline void SetDepthFunc(RendererComparison depthFunc);
inline void SetDstBlend(BlendFunc func);
inline void SetFaceCulling(FaceSide faceSide);
inline void SetFaceFilling(FaceFilling filling);
inline void SetLineWidth(float lineWidth);
inline void SetPointSize(float pointSize);
inline void SetShader(std::shared_ptr<Shader> shader);
inline void SetUniformBuffer(std::size_t bufferIndex, UniformBuffer* uniformBuffer);
inline void SetSrcBlend(BlendFunc func);
inline void SetTexture(std::size_t textureIndex, Texture* texture);
inline void SetTextureSampler(std::size_t textureIndex, const TextureSampler& sampler);
inline Material& operator=(const Material& material);
// Signals:
NazaraSignal(OnMaterialRelease, const Material* /*material*/);
private:
inline void InvalidatePipeline();
inline void UpdatePipeline() const;
struct MaterialTexture
{
std::shared_ptr<TextureSampler> sampler;
std::shared_ptr<Texture> texture;
};
std::shared_ptr<const MaterialSettings> m_settings;
std::vector<MaterialTexture> m_textures;
std::vector<UniformBufferRef> m_uniformBuffers;
mutable const MaterialPipeline* m_pipeline;
MaterialPipelineInfo m_pipelineInfo;
mutable bool m_pipelineUpdated;
bool m_shadowCastingEnabled;
};
}
#include <Nazara/Graphics/Material.inl>
#endif // NAZARA_BASE_MATERIAL_HPP

View File

@ -0,0 +1,981 @@
// 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 <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \brief Constructs a Material object by assignation
*
* \param material Material to copy into this
*/
inline Material::Material(const Material& material) :
RefCounted(),
Resource(material)
{
operator=(material);
}
/*!
* \brief Destructs the object and calls OnMaterialRelease
*
* \see OnMaterialRelease
*/
inline Material::~Material()
{
OnMaterialRelease(this);
}
/*!
* \brief Reset material pipeline state
*
* Sets the material pipeline
*
* \remark pipeline must be valid
*
* \see Configure
*/
inline void Material::Configure(const MaterialPipeline* pipeline)
{
NazaraAssert(pipeline, "Invalid material pipeline");
m_pipeline = pipeline;
m_pipelineInfo = m_pipeline->GetInfo();
m_pipelineUpdated = true;
}
/*!
* \brief Reset material pipeline state
*
* Sets the material pipeline using pipeline info
*
* \remark pipeline must be valid
*
* \see Configure
*/
inline void Material::Configure(const MaterialPipelineInfo& pipelineInfo)
{
m_pipelineInfo = pipelineInfo;
InvalidatePipeline();
}
/*!
* \brief Reset material pipeline state
*
* Sets the material pipeline using a name to lookup in the MaterialPipelineLibrary
*
* \return True if the material pipeline was found in the library
*
* \see Configure
*/
inline bool Material::Configure(const String& pipelineName)
{
MaterialPipelineRef pipeline = MaterialPipelineLibrary::Query(pipelineName);
if (!pipeline)
{
NazaraError("Failed to get pipeline \"" + pipelineName + "\"");
return false;
}
Configure(std::move(pipeline));
return true;
}
/*!
* \brief Enable/Disable alpha test for this material
*
* When enabled, all objects using this material will be rendered using alpha testing,
* rejecting pixels if their alpha component is under a defined threshold.
* This allows some kind of transparency with a much cheaper cost as it doesn't prevent any optimization (as deferred rendering or batching).
*
* \param alphaTest Defines if this material will use alpha testing
*
* \remark Invalidates the pipeline
*
* \see IsAlphaTestEnabled
* \see SetAlphaThreshold
*/
inline void Material::EnableAlphaTest(bool alphaTest)
{
m_pipelineInfo.alphaTest = alphaTest;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable blending for this material
*
* When enabled, all objects using this material will be rendered using blending, obeying the dstBlend and srcBlend parameters
* This is useful with translucent objects, but will reduces performance as it prevents some optimizations (as deferred rendering)
*
* \param blending Defines if this material will use blending
*
* \remark Invalidates the pipeline
*
* \see IsBlendingEnabled
* \see SetDstBlend
* \see SetSrcBlend
*/
inline void Material::EnableBlending(bool blending)
{
m_pipelineInfo.blending = blending;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable color writing for this material
*
* \param colorWrite Defines if this material will use color writing
*
* \remark Invalidates the pipeline
*
* \see IsColorWritingEnabled
*/
inline void Material::EnableColorWrite(bool colorWrite)
{
m_pipelineInfo.colorWrite = colorWrite;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable depth buffer for this material
*
* When enabled, all objects using this material will be rendered using a depth buffer, if the RenderTarget has one.
* This will enable Depth Test, preventing further fragments to render on top of closer ones.
*
* This parameter is required for depth writing.
*
* In order to enable depth writing without enabling depth test, set the depth comparison function to RendererComparison_Never
*
* \param depthBuffer Defines if this material will use depth buffer
*
* \remark Invalidates the pipeline
*
* \see EnableDepthWrite
* \see IsDepthBufferEnabled
* \see SetDepthFunc
*/
inline void Material::EnableDepthBuffer(bool depthBuffer)
{
m_pipelineInfo.depthBuffer = depthBuffer;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable depth sorting for this material
*
* When enabled, all objects using this material will be rendered far from near
* This is useful with translucent objects, but will reduces performance as it breaks batching
*
* \param depthSorting Defines if this material will use depth sorting
*
* \remark Depth sorting may not be perfect (may be object-sorting instead of triangle-sorting)
* \remark Invalidates the pipeline
*
* \see IsDepthSortingEnabled
*/
inline void Material::EnableDepthSorting(bool depthSorting)
{
m_pipelineInfo.depthSorting = depthSorting;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable depth writing for this material
*
* When enabled, and if depth buffer is enabled and present, all fragments generated with this material will write
* to the depth buffer if they pass depth test.
*
* This is usually disabled with translucent objects, as depth test is wanted to prevent them from rendering on top of opaque objects but
* not depth writing (which could make other translucent fragments to fail depth test)
*
* \param depthBuffer Defines if this material will use depth write
*
* \remark Invalidates the pipeline
*
* \see EnableDepthBuffer
* \see IsDepthWriteEnabled
*/
inline void Material::EnableDepthWrite(bool depthWrite)
{
m_pipelineInfo.depthWrite = depthWrite;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable face culling for this material
*
* When enabled, the material prevents front and/or back faces from rendering.
* This is commonly used as an optimization to prevent processing of hidden faces by the rendering device.
*
* Use SetFaceCulling to control which side will be eliminated.
*
* \param faceCulling Defines if this material will use face culling
*
* \remark Invalidates the pipeline
*
* \see IsFaceCullingEnabled
* \see SetFaceCulling
*/
inline void Material::EnableFaceCulling(bool faceCulling)
{
m_pipelineInfo.faceCulling = faceCulling;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable reflection mapping for this material
*
* When enabled, the material will render reflections from the object environment according to the reflection mode.
* Whether or not this is expensive depends of the reflection mode and size.
*
* Please note this is only a hint for the render technique, and reflections can be forcefully enabled or disabled depending on the material shader.
*
* Use SetReflectionMode and SetReflectionSize to control reflection quality.
*
* \param reflection Defines if this material should use reflection mapping
*
* \remark May invalidates the pipeline
*
* \see IsReflectionMappingEnabled
* \see SetReflectionMode
* \see SetReflectionSize
*/
inline void Material::EnableReflectionMapping(bool reflection)
{
m_pipelineInfo.reflectionMapping = reflection;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable scissor test for this material
*
* When enabled, the material prevents fragments out of the scissor box to be rendered.
* This can be useful with GUI, where widgets must not be rendered outside of their parent rendering area.
*
* \param scissorTest Defines if this material will use scissor test
*
* \remark Invalidates the pipeline
*
* \see IsScissorTestEnabled
*/
inline void Material::EnableScissorTest(bool scissorTest)
{
m_pipelineInfo.scissorTest = scissorTest;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable shadow casting for this material
*
* When enabled, all objects using this material will be allowed to cast shadows upon any objects using a material with shadow receiving enabled.
* The depth material replaces this one when rendering shadows.
*
* \param castShadows Defines if this material will be allowed to cast shadows
*
* \remark Does not invalidate the pipeline
*
* \see EnableShadowReceive
* \see IsShadowCastingEnabled
* \see SetDepthMaterial
*/
inline void Material::EnableShadowCasting(bool castShadows)
{
// Has no influence on pipeline
m_shadowCastingEnabled = castShadows;
}
/*!
* \brief Enable/Disable shadow receiving for this material
*
* When enabled, all objects using this material will be allowed to be casted shadows upon themselves
* Disabling this can be helpful to prevent some rendering artifacts (especially with translucent objects)
*
* \param receiveShadows Defines if this material will be able to receive shadows
*
* \remark Invalidates the pipeline
*
* \see IsShadowReceiveEnabled
*/
inline void Material::EnableShadowReceive(bool receiveShadows)
{
m_pipelineInfo.shadowReceive = receiveShadows;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable stencil test for this material
*
* When enabled, all fragments must pass the stencil test to be rendered.
*
* \param scissorTest Defines if this material will use stencil test
*
* \remark Invalidates the pipeline
*
* \see IsStencilTestEnabled
*/
inline void Material::EnableStencilTest(bool stencilTest)
{
m_pipelineInfo.stencilTest = stencilTest;
InvalidatePipeline();
}
/*!
* \brief Enable/Disable vertex coloring on this material
*
* This is a temporary option, until the new material pipeline system is ready, allowing to enable vertex coloring.
* This option only works with meshes using vertex colors.
*
* \param vertexColor Defines if this material will use vertex color or not
*
* \remark Invalidates the pipeline
*
* \see HasVertexColor
*/
inline void Material::EnableVertexColor(bool vertexColor)
{
m_pipelineInfo.hasVertexColor = vertexColor;
InvalidatePipeline();
}
/*!
* \brief Ensures the pipeline gets updated
*
* When the pipeline gets invalidated, it's not updated until required (per example by calling GetPipeline).
* Using this function forces the pipeline update, making GetPipeline thread-safe as long as the pipeline does not get invalidated.
*
* \see GetPipeline
*/
inline void Material::EnsurePipelineUpdate() const
{
if (!m_pipelineUpdated)
UpdatePipeline();
}
/*!
* \brief Gets the function to compare depth
*
* \return Function comparing the depth of two materials
*
* \see EnableDepthTest
* \see SetAmbientColor
*/
inline RendererComparison Material::GetDepthFunc() const
{
return m_pipelineInfo.depthCompare;
}
/*!
* \brief Gets the depth material
*
* \return Constant reference to the depth material
*
* \see EnableShadowCasting
*/
inline const MaterialRef& Material::GetDepthMaterial() const
{
return m_depthMaterial;
}
/*!
* \brief Gets the dst in blend
*
* \return Function for dst blending
*
* \see SetDstBlend
*/
inline BlendFunc Material::GetDstBlend() const
{
return m_pipelineInfo.dstBlend;
}
/*!
* \brief Gets the face culling
*
* \return Current face culling side
*
* \see SetFaceCulling
*/
inline FaceSide Material::GetFaceCulling() const
{
return m_pipelineInfo.cullingSide;
}
/*!
* \brief Gets the face filling
* \return Current face filling
*/
inline FaceFilling Material::GetFaceFilling() const
{
return m_pipelineInfo.faceFilling;
}
/*!
* \brief Gets the line width of this material
* \return Line width
*/
inline float Material::GetLineWidth() const
{
return m_pipelineInfo.lineWidth;
}
/*!
* \brief Gets the render states
* \return Constant reference to the render states
*/
inline const MaterialPipeline* Material::GetPipeline() const
{
EnsurePipelineUpdate();
return m_pipeline;
}
/*!
* \brief Gets the pipeline informations
* \return Constant reference to the pipeline info
*/
inline const MaterialPipelineInfo& Material::GetPipelineInfo() const
{
return m_pipelineInfo;
}
/*!
* \brief Gets the point size of this material
* \return Point size
*/
inline float Material::GetPointSize() const
{
return m_pipelineInfo.pointSize;
}
/*!
* \brief Gets the reflection mode of the material
*
* \return Current reflection mode
*
* \see SetReflectionMode
*/
inline ReflectionMode Material::GetReflectionMode() const
{
return m_reflectionMode;
}
inline const std::shared_ptr<const MaterialSettings>& Material::GetSettings() const
{
return m_settings;
}
/*!
* \brief Gets the über-shader used by this material
* \return Constant pointer to the über-shader used
*/
inline const UberShader* Material::GetShader() const
{
return m_pipelineInfo.uberShader;
}
/*!
* \brief Gets the src in blend
* \return Function for src blending
*/
inline BlendFunc Material::GetSrcBlend() const
{
return m_pipelineInfo.srcBlend;
}
inline const TextureRef& Material::GetTexture(std::size_t textureIndex) const
{
NazaraAssert(textureIndex < m_textures.size(), "Invalid texture index");
return m_textures[textureIndex].texture;
}
inline TextureSampler& Material::GetTextureSampler(std::size_t textureIndex)
{
NazaraAssert(textureIndex < m_textures.size(), "Invalid texture index");
return m_textures[textureIndex].sampler;
}
inline const TextureSampler& Material::GetTextureSampler(std::size_t textureIndex) const
{
NazaraAssert(textureIndex < m_textures.size(), "Invalid texture index");
return m_textures[textureIndex].sampler;
}
inline UniformBufferRef& Material::GetUniformBuffer(std::size_t bufferIndex)
{
NazaraAssert(bufferIndex < m_uniformBuffers.size(), "Invalid uniform buffer index");
return m_uniformBuffers[bufferIndex];
}
inline const UniformBufferRef& Material::GetUniformBuffer(std::size_t bufferIndex) const
{
NazaraAssert(bufferIndex < m_uniformBuffers.size(), "Invalid uniform buffer index");
return m_uniformBuffers[bufferIndex];
}
inline bool Material::HasDepthMaterial() const
{
return m_depthMaterial.IsValid();
}
inline bool Material::HasTexture(std::size_t textureIndex) const
{
Texture* texture = GetTexture(textureIndex);
return texture && texture->IsValid();
}
/*!
* \brief Checks whether this material uses vertex coloring
* \return true If it is the case
*/
inline bool Material::HasVertexColor() const
{
return m_pipelineInfo.hasVertexColor;
}
/*!
* \brief Checks whether this material has alpha test enabled
* \return true If it is the case
*/
inline bool Material::IsAlphaTestEnabled() const
{
return m_pipelineInfo.alphaTest;
}
/*!
* \brief Checks whether this material has blending enabled
* \return true If it is the case
*/
inline bool Material::IsBlendingEnabled() const
{
return m_pipelineInfo.blending;
}
/*!
* \brief Checks whether this material has color write enabled
* \return true If it is the case
*/
inline bool Material::IsColorWriteEnabled() const
{
return m_pipelineInfo.colorWrite;
}
/*!
* \brief Checks whether this material has depth buffer enabled
* \return true If it is the case
*/
inline bool Material::IsDepthBufferEnabled() const
{
return m_pipelineInfo.depthBuffer;
}
/*!
* \brief Checks whether this material has depth sorting enabled
* \return true If it is the case
*/
inline bool Material::IsDepthSortingEnabled() const
{
return m_pipelineInfo.depthSorting;
}
/*!
* \brief Checks whether this material has depth writing enabled
* \return true If it is the case
*/
inline bool Material::IsDepthWriteEnabled() const
{
return m_pipelineInfo.depthWrite;
}
/*!
* \brief Checks whether this material has face culling enabled
* \return true If it is the case
*/
inline bool Material::IsFaceCullingEnabled() const
{
return m_pipelineInfo.faceCulling;
}
/*!
* \brief Checks whether this material has reflection mapping enabled
* \return true If it is the case
*
* \see EnableReflectionMapping
*/
inline bool Material::IsReflectionMappingEnabled() const
{
return m_pipelineInfo.reflectionMapping;
}
/*!
* \brief Checks whether this material has scissor test enabled
* \return true If it is the case
*/
inline bool Material::IsScissorTestEnabled() const
{
return m_pipelineInfo.scissorTest;
}
/*!
* \brief Checks whether this material has stencil test enabled
* \return true If it is the case
*/
inline bool Material::IsStencilTestEnabled() const
{
return m_pipelineInfo.stencilTest;
}
/*!
* \brief Checks whether this material cast shadow
* \return true If it is the case
*/
inline bool Material::IsShadowCastingEnabled() const
{
return m_shadowCastingEnabled;
}
/*!
* \brief Checks whether this material receive shadow
* \return true If it is the case
*/
inline bool Material::IsShadowReceiveEnabled() const
{
return m_pipelineInfo.shadowReceive;
}
/*!
* \brief Sets the depth functor
*
* \param depthFunc
*
* \remark Invalidates the pipeline
*/
inline void Material::SetDepthFunc(RendererComparison depthFunc)
{
m_pipelineInfo.depthFunc = depthFunc;
InvalidatePipeline();
}
/*!
* \brief Sets the depth material
* \return true If successful
*
* \param depthMaterial Material for depth
*/
inline void Material::SetDepthMaterial(MaterialRef depthMaterial)
{
m_depthMaterial = std::move(depthMaterial);
}
/*!
* \brief Sets the dst in blend
*
* \param func Function for dst blending
*
* \remark Invalidates the pipeline
*/
inline void Material::SetDstBlend(BlendFunc func)
{
m_pipelineInfo.dstBlend = func;
InvalidatePipeline();
}
/*!
* \brief Sets the face culling
*
* \param faceSide Face to cull
*
* \remark Invalidates the pipeline
*/
inline void Material::SetFaceCulling(FaceSide faceSide)
{
m_pipelineInfo.cullingSide = faceSide;
InvalidatePipeline();
}
/*!
* \brief Sets the face filling
*
* \param filling Face to fill
*
* \remark Invalidates the pipeline
*/
inline void Material::SetFaceFilling(FaceFilling filling)
{
m_pipelineInfo.faceFilling = filling;
InvalidatePipeline();
}
/*!
* \brief Sets the line width for this material
*
* This parameter is used when rendering lines, to define the width (in pixels) the line will take on the framebuffer
*
* \param lineWidth Width of the line
*
* \remark Invalidates the pipeline
*
* \see GetLineWidth
*/
inline void Material::SetLineWidth(float lineWidth)
{
m_pipelineInfo.lineWidth = lineWidth;
InvalidatePipeline();
}
/*!
* \brief Sets the point size for this material
*
* This parameter is used when rendering points, to define the size (in pixels) the point will take on the framebuffer
*
* \param pointSize Size of the point
*
* \remark Invalidates the pipeline
*
* \see GetPointSize
*/
inline void Material::SetPointSize(float pointSize)
{
m_pipelineInfo.pointSize = pointSize;
InvalidatePipeline();
}
/*!
* \brief Changes reflection mode of the material
*
* When reflections are enabled, the material will render reflections from the object environment according to the reflection mode.
* This function does change the reflection mode used by the material.
*
* Skyboxes reflections are the cheapest but are static and thus can't reflect other objects.
* Probes reflections are cheap, depending on probes reflection mode, but require regular probe finding from objects using it.
* Real-time reflections are expensive but provide the most accurate reflection map (and can reflect other objects around).
*
* \param reflectionMode The new reflection mode this material should use
*
* \remark May invalidates the pipeline
*
* \see EnableReflectionMapping
* \see IsReflectionMappingEnabled
* \see SetReflectionSize
*/
inline void Material::SetReflectionMode(ReflectionMode reflectionMode)
{
if (m_reflectionMode != reflectionMode)
{
OnMaterialReflectionModeChange(this, reflectionMode);
m_reflectionMode = reflectionMode;
}
}
/*!
* \brief Sets the shader with a constant reference to a ubershader
*
* \param uberShader Uber shader to apply
*
* \remark Invalidates the pipeline
*
* \see GetShader
*/
inline void Material::SetShader(UberShaderConstRef uberShader)
{
m_pipelineInfo.uberShader = std::move(uberShader);
InvalidatePipeline();
}
/*!
* \brief Sets the shader by name
* \return true If successful
*
* \param uberShaderName Named shader
*/
inline bool Material::SetShader(const String& uberShaderName)
{
UberShaderConstRef uberShader = UberShaderLibrary::Get(uberShaderName);
if (!uberShader)
return false;
SetShader(std::move(uberShader));
return true;
}
inline void Material::SetUniformBuffer(std::size_t bufferIndex, UniformBuffer* uniformBuffer)
{
NazaraAssert(bufferIndex < m_uniformBuffers.size(), "Invalid shared uniform buffer index");
m_uniformBuffers[bufferIndex] = uniformBuffer;
}
inline void Material::SetTexture(std::size_t textureIndex, Texture* texture)
{
NazaraAssert(textureIndex < m_textures.size(), "Invalid texture index");
m_textures[textureIndex].texture = texture;
if (texture)
m_pipelineInfo.textures |= UInt64(1) << UInt64(textureIndex);
else
m_pipelineInfo.textures &= ~(UInt64(1) << UInt64(textureIndex));
InvalidatePipeline();
}
inline void Material::SetTextureSampler(std::size_t textureIndex, const TextureSampler& sampler)
{
NazaraAssert(textureIndex < m_textures.size(), "Invalid texture index");
m_textures[textureIndex].sampler = sampler;
}
/*!
* \brief Sets the src in blend
*
* \param func Function for src blending
*
* \remark Invalidates the pipeline
*
* \see GetSrcBlend
*/
inline void Material::SetSrcBlend(BlendFunc func)
{
m_pipelineInfo.srcBlend = func;
InvalidatePipeline();
}
/*!
* \brief Sets the current material with the content of the other one
* \return A reference to this
*
* \param material The other Material
*/
inline Material& Material::operator=(const Material& material)
{
Resource::operator=(material);
m_settings = material.m_settings;
m_textures = material.m_textures;
m_depthMaterial = material.m_depthMaterial;
m_pipeline = material.m_pipeline;
m_pipelineInfo = material.m_pipelineInfo;
m_pipelineUpdated = material.m_pipelineUpdated;
m_shadowCastingEnabled = material.m_shadowCastingEnabled;
m_reflectionSize = material.m_reflectionSize;
m_pipelineInfo.settings = m_settings;
for (std::size_t i = 0; i < m_uniformBuffers.size(); ++i)
{
const UniformBuffer* sourceBuffer = material.GetUniformBuffer(i);
UniformBuffer* targetBuffer = m_uniformBuffers[i] = UniformBuffer::New(sourceBuffer->GetEndOffset() - sourceBuffer->GetStartOffset(), DataStorage_Hardware, BufferUsage_Dynamic);
if (!targetBuffer->CopyContent(sourceBuffer))
NazaraError("Failed to copy uniform buffer content");
}
SetReflectionMode(material.GetReflectionMode());
return *this;
}
/*!
* \brief Gets the default material
*
* \return Reference to the default material
*
* \remark This material should NOT be modified as it would affect all objects using it
*/
inline MaterialRef Material::GetDefault()
{
return s_defaultMaterial;
}
inline int Material::GetTextureUnit(TextureMap textureMap)
{
return s_textureUnits[textureMap];
}
/*!
* \brief Loads the material from file
* \return true if loading is successful
*
* \param filePath Path to the file
* \param params Parameters for the material
*/
inline MaterialRef Material::LoadFromFile(const String& filePath, const MaterialParams& params)
{
return MaterialLoader::LoadFromFile(filePath, params);
}
/*!
* \brief Loads the material from memory
* \return true if loading is successful
*
* \param data Raw memory
* \param size Size of the memory
* \param params Parameters for the material
*/
inline MaterialRef Material::LoadFromMemory(const void* data, std::size_t size, const MaterialParams& params)
{
return MaterialLoader::LoadFromMemory(data, size, params);
}
/*!
* \brief Loads the material from stream
* \return true if loading is successful
*
* \param stream Stream to the material
* \param params Parameters for the material
*/
inline MaterialRef Material::LoadFromStream(Stream& stream, const MaterialParams& params)
{
return MaterialLoader::LoadFromStream(stream, params);
}
inline void Material::InvalidatePipeline()
{
m_pipelineUpdated = false;
}
inline void Material::UpdatePipeline() const
{
m_pipeline = MaterialPipeline::GetPipeline(m_pipelineInfo);
m_pipelineUpdated = true;
}
/*!
* \brief Creates a new material from the arguments
* \return A reference to the newly created material
*
* \param args Arguments for the material
*/
template<typename... Args>
MaterialRef Material::New(Args&&... args)
{
std::unique_ptr<Material> object(new Material(std::forward<Args>(args)...));
object->SetPersistent(false);
return object.release();
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -0,0 +1,87 @@
// 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
#pragma once
#ifndef NAZARA_MATERIALPIPELINE_HPP
#define NAZARA_MATERIALPIPELINE_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/MaterialSettings.hpp>
#include <Nazara/Renderer/RenderPipeline.hpp>
#include <array>
#include <memory>
namespace Nz
{
class Shader;
struct MaterialPipelineInfo : RenderStates
{
bool alphaTest = false;
bool depthSorting = false;
bool hasVertexColor = false;
bool reflectionMapping = false;
bool shadowReceive = true;
Nz::UInt64 textures = 0;
std::shared_ptr<RenderPipelineLayout> pipelineLayout;
std::shared_ptr<const MaterialSettings> settings;
std::shared_ptr<Shader> shader;
};
inline bool operator==(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs);
inline bool operator!=(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs);
class NAZARA_GRAPHICS_API MaterialPipeline
{
public:
struct Instance;
MaterialPipeline(const MaterialPipeline&) = delete;
MaterialPipeline(MaterialPipeline&&) = delete;
~MaterialPipeline() = default;
inline const Instance& Apply(UInt32 flags = ShaderFlags_None) const;
MaterialPipeline& operator=(const MaterialPipeline&) = delete;
MaterialPipeline& operator=(MaterialPipeline&&) = delete;
inline const MaterialPipelineInfo& GetInfo() const;
inline const Instance& GetInstance(UInt32 flags = ShaderFlags_None) const;
static MaterialPipelineRef GetPipeline(const MaterialPipelineInfo& pipelineInfo);
struct Instance
{
RenderPipeline renderPipeline;
//Shader::LayoutBindings bindings;
//UberShaderInstance* uberInstance = nullptr;
};
private:
inline MaterialPipeline(const MaterialPipelineInfo& pipelineInfo);
void GenerateRenderPipeline(UInt32 flags) const;
static bool Initialize();
template<typename... Args> static MaterialPipelineRef New(Args&&... args);
static void Uninitialize();
MaterialPipelineInfo m_pipelineInfo;
mutable std::array<Instance, ShaderFlags_Max + 1> m_instances;
using PipelineCache = std::unordered_map<MaterialPipelineInfo, MaterialPipelineRef>;
static PipelineCache s_pipelineCache;
static MaterialPipelineLibrary::LibraryMap s_library;
};
}
#include <Nazara/Graphics/MaterialPipeline.inl>
#endif // NAZARA_MATERIALPIPELINE_HPP

View File

@ -0,0 +1,138 @@
// 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/Renderer/Renderer.hpp>
#include <Nazara/Renderer/UberShaderInstance.hpp>
#include <functional>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
inline MaterialPipeline::MaterialPipeline(const MaterialPipelineInfo& pipelineInfo) :
m_pipelineInfo(pipelineInfo)
{
}
/*!
* \brief Enable pipeline states for rendering
*
* \param flags Shader flags
*/
inline const MaterialPipeline::Instance& MaterialPipeline::Apply(UInt32 flags) const
{
const Instance& instance = GetInstance(flags);
instance.uberInstance->Activate();
Renderer::SetRenderStates(m_pipelineInfo);
return instance;
}
/*!
* \brief Retrieve a MaterialPipelineInfo object describing this pipeline
*
* \return Pipeline informations
*/
const MaterialPipelineInfo& MaterialPipeline::GetInfo() const
{
return m_pipelineInfo;
}
/*!
* \brief Retrieve (and generate if required) a pipeline instance using shader flags without applying it
*
* \param flags Shader flags
*
* \return Pipeline instance
*/
inline const MaterialPipeline::Instance& MaterialPipeline::GetInstance(UInt32 flags) const
{
const Instance& instance = m_instances[flags];
if (!instance.uberInstance)
GenerateRenderPipeline(flags);
return instance;
}
bool operator==(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs)
{
if (!operator==(static_cast<const RenderStates&>(lhs), static_cast<const RenderStates&>(rhs)))
return false;
#define NazaraPipelineMember(field) if (lhs.field != rhs.field) return false
#define NazaraPipelineBoolMember NazaraPipelineMember
NazaraPipelineBoolMember(depthSorting);
NazaraPipelineBoolMember(hasVertexColor);
NazaraPipelineBoolMember(reflectionMapping);
NazaraPipelineBoolMember(shadowReceive);
NazaraPipelineMember(pipelineLayout);
NazaraPipelineMember(settings);
NazaraPipelineMember(shader);
#undef NazaraPipelineMember
#undef NazaraPipelineBoolMember
return true;
}
bool operator!=(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs)
{
return !operator==(lhs, rhs);
}
/*!
* \brief Creates a new MaterialPipeline from the arguments
* \return A reference to the newly created material pipeline
*
* \param args Arguments for the material pipeline
*/
template<typename... Args>
MaterialPipelineRef MaterialPipeline::New(Args&&... args)
{
std::unique_ptr<MaterialPipeline> object(new MaterialPipeline(std::forward<Args>(args)...));
object->SetPersistent(false);
return object.release();
}
}
namespace std
{
template<>
struct hash<Nz::MaterialPipelineInfo>
{
size_t operator()(const Nz::MaterialPipelineInfo& pipelineInfo) const
{
hash<Nz::RenderStates> parentHash;
std::size_t seed = parentHash(pipelineInfo);
Nz::UInt16 parameterHash = 0;
Nz::UInt16 parameterIndex = 0;
#define NazaraPipelineMember(member) Nz::HashCombine(seed, pipelineInfo.member)
#define NazaraPipelineBoolMember(member) parameterHash |= ((pipelineInfo.member) ? 1U : 0U) << (parameterIndex++)
NazaraPipelineBoolMember(alphaTest);
NazaraPipelineBoolMember(depthSorting);
NazaraPipelineBoolMember(hasVertexColor);
NazaraPipelineBoolMember(reflectionMapping);
NazaraPipelineBoolMember(shadowReceive);
NazaraPipelineMember(pipelineLayout.get()); //< Hash pointer
NazaraPipelineMember(settings.get()); //< Hash pointer
NazaraPipelineMember(shader.get());
#undef NazaraPipelineMember
#undef NazaraPipelineBoolMember
Nz::HashCombine(seed, parameterHash);
return seed;
}
};
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -0,0 +1,92 @@
// 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
#pragma once
#ifndef NAZARA_MATERIALSETTINGS_HPP
#define NAZARA_MATERIALSETTINGS_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Renderer/RenderPipelineLayout.hpp>
#include <Nazara/Utility/Enums.hpp>
#include <array>
#include <limits>
#include <string>
#include <vector>
namespace Nz
{
class MaterialSettings
{
public:
using PredefinedBinding = std::array<std::size_t, PredefinedShaderBinding_Max + 1>;
struct SharedUniformBlock;
struct Texture;
struct UniformBlock;
inline MaterialSettings();
inline MaterialSettings(std::vector<Texture> textures, std::vector<UniformBlock> uniformBlocks, std::vector<SharedUniformBlock> sharedUniformBlocks, const PredefinedBinding& predefinedBinding);
MaterialSettings(const MaterialSettings&) = default;
MaterialSettings(MaterialSettings&&) = delete;
~MaterialSettings() = default;
inline std::size_t GetPredefinedBindingIndex(PredefinedShaderBinding binding) const;
inline const std::shared_ptr<RenderPipelineLayout>& GetRenderPipelineLayout() const;
inline const std::vector<SharedUniformBlock>& GetSharedUniformBlocks() const;
inline std::size_t GetSharedUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const;
inline std::size_t GetSharedUniformBlockIndex(const std::string_view& name) const;
inline const std::vector<Texture>& GetTextures() const;
inline std::size_t GetTextureIndex(const std::string_view& name) const;
inline const std::vector<UniformBlock>& GetUniformBlocks() const;
inline std::size_t GetUniformBlockIndex(const std::string_view& name) const;
inline std::size_t GetUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const;
MaterialSettings& operator=(const MaterialSettings&) = delete;
MaterialSettings& operator=(MaterialSettings&&) = delete;
static constexpr std::size_t InvalidIndex = std::numeric_limits<std::size_t>::max();
struct UniformVariable
{
std::string name;
std::size_t offset;
};
struct SharedUniformBlock
{
std::string name;
std::string bindingPoint;
std::vector<UniformVariable> uniforms;
};
struct Texture
{
std::string bindingPoint;
std::string name;
ImageType type;
};
struct UniformBlock
{
std::size_t blockSize;
std::string name;
std::string bindingPoint;
std::vector<UniformVariable> uniforms;
std::vector<UInt8> defaultValues;
};
private:
std::shared_ptr<RenderPipelineLayout> m_pipelineLayout;
std::vector<SharedUniformBlock> m_sharedUniformBlocks;
std::vector<Texture> m_textures;
std::vector<UniformBlock> m_uniformBlocks;
PredefinedBinding m_predefinedBinding;
};
}
#include <Nazara/Graphics/MaterialSettings.inl>
#endif // NAZARA_MATERIALPIPELINESETTINGS_HPP

View File

@ -0,0 +1,147 @@
// 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/MaterialSettings.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <cassert>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
inline MaterialSettings::MaterialSettings() :
MaterialSettings({}, {}, {}, { InvalidIndex })
{
}
inline MaterialSettings::MaterialSettings(std::vector<Texture> textures, std::vector<UniformBlock> uniformBlocks, std::vector<SharedUniformBlock> sharedUniformBlocks, const PredefinedBinding& predefinedBindings) :
m_sharedUniformBlocks(std::move(sharedUniformBlocks)),
m_textures(std::move(textures)),
m_uniformBlocks(std::move(uniformBlocks)),
m_predefinedBinding(predefinedBindings)
{
RenderPipelineLayoutInfo info;
unsigned int bindingIndex = 0;
for (const Texture& textureInfo : m_textures)
{
info.bindings.push_back({
textureInfo.bindingPoint,
ShaderBindingType::Texture,
ShaderStageType_All,
bindingIndex++
});
}
for (const UniformBlock& ubo : m_uniformBlocks)
{
info.bindings.push_back({
ubo.bindingPoint,
ShaderBindingType::UniformBuffer,
ShaderStageType_All,
bindingIndex++
});
}
for (const SharedUniformBlock& ubo : m_sharedUniformBlocks)
{
info.bindings.push_back({
ubo.bindingPoint,
ShaderBindingType::UniformBuffer,
ShaderStageType_All,
bindingIndex++
});
}
m_pipelineLayout = Graphics::Instance()->GetRenderDevice().InstantiateRenderPipelineLayout(std::move(info));
}
inline std::size_t MaterialSettings::GetPredefinedBindingIndex(PredefinedShaderBinding binding) const
{
return m_predefinedBinding[static_cast<std::size_t>(binding)];
}
inline const std::shared_ptr<RenderPipelineLayout>& MaterialSettings::GetRenderPipelineLayout() const
{
return m_pipelineLayout;
}
inline auto MaterialSettings::GetSharedUniformBlocks() const -> const std::vector<SharedUniformBlock>&
{
return m_sharedUniformBlocks;
}
inline std::size_t MaterialSettings::GetSharedUniformBlockIndex(const std::string_view& name) const
{
for (std::size_t i = 0; i < m_sharedUniformBlocks.size(); ++i)
{
if (m_sharedUniformBlocks[i].name == name)
return i;
}
return InvalidIndex;
}
inline auto MaterialSettings::GetTextures() const -> const std::vector<Texture>&
{
return m_textures;
}
inline std::size_t MaterialSettings::GetSharedUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const
{
assert(uniformBlockIndex < m_sharedUniformBlocks.size());
const std::vector<UniformVariable>& variables = m_sharedUniformBlocks[uniformBlockIndex].uniforms;
for (std::size_t i = 0; i < variables.size(); ++i)
{
if (variables[i].name == name)
return i;
}
return InvalidIndex;
}
inline std::size_t MaterialSettings::GetTextureIndex(const std::string_view& name) const
{
for (std::size_t i = 0; i < m_textures.size(); ++i)
{
if (m_textures[i].name == name)
return i;
}
return InvalidIndex;
}
inline auto MaterialSettings::GetUniformBlocks() const -> const std::vector<UniformBlock>&
{
return m_uniformBlocks;
}
inline std::size_t MaterialSettings::GetUniformBlockIndex(const std::string_view& name) const
{
for (std::size_t i = 0; i < m_uniformBlocks.size(); ++i)
{
if (m_uniformBlocks[i].name == name)
return i;
}
return InvalidIndex;
}
inline std::size_t MaterialSettings::GetUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const
{
assert(uniformBlockIndex < m_uniformBlocks.size());
const std::vector<UniformVariable>& variables = m_uniformBlocks[uniformBlockIndex].uniforms;
for (std::size_t i = 0; i < variables.size(); ++i)
{
if (variables[i].name == name)
return i;
}
return InvalidIndex;
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -0,0 +1,110 @@
// 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
#pragma once
#ifndef NAZARA_PHONG_LIGHTING_MATERIAL_HPP
#define NAZARA_PHONG_LIGHTING_MATERIAL_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Material.hpp>
namespace Nz
{
class NAZARA_GRAPHICS_API PhongLightingMaterial
{
friend class MaterialPipeline;
public:
PhongLightingMaterial(Material* material);
inline const TextureRef& GetAlphaMap() const;
float GetAlphaThreshold() const;
Color GetAmbientColor() const;
Color GetDiffuseColor() const;
inline const TextureRef& GetDiffuseMap() const;
inline TextureSampler& GetDiffuseSampler();
inline const TextureSampler& GetDiffuseSampler() const;
inline const TextureRef& GetEmissiveMap() const;
inline const TextureRef& GetHeightMap() const;
inline const TextureRef& GetNormalMap() const;
float GetShininess() const;
Color GetSpecularColor() const;
inline const TextureRef& GetSpecularMap() const;
inline TextureSampler& GetSpecularSampler();
inline const TextureSampler& GetSpecularSampler() const;
inline bool HasAlphaMap() const;
inline bool HasAlphaThreshold() const;
inline bool HasAmbientColor() const;
inline bool HasDiffuseColor() const;
inline bool HasDiffuseMap() const;
inline bool HasEmissiveMap() const;
inline bool HasHeightMap() const;
inline bool HasNormalMap() const;
inline bool HasShininess() const;
inline bool HasSpecularColor() const;
inline bool HasSpecularMap() const;
inline bool SetAlphaMap(const String& textureName);
inline void SetAlphaMap(TextureRef alphaMap);
void SetAlphaThreshold(float alphaThreshold);
void SetAmbientColor(const Color& ambient);
void SetDiffuseColor(const Color& diffuse);
inline bool SetDiffuseMap(const String& textureName);
inline void SetDiffuseMap(TextureRef diffuseMap);
inline void SetDiffuseSampler(const TextureSampler& sampler);
inline bool SetEmissiveMap(const String& textureName);
inline void SetEmissiveMap(TextureRef textureName);
inline bool SetHeightMap(const String& textureName);
inline void SetHeightMap(TextureRef textureName);
inline bool SetNormalMap(const String& textureName);
inline void SetNormalMap(TextureRef textureName);
void SetShininess(float shininess);
void SetSpecularColor(const Color& specular);
inline bool SetSpecularMap(const String& textureName);
inline void SetSpecularMap(TextureRef specularMap);
inline void SetSpecularSampler(const TextureSampler& sampler);
static const std::shared_ptr<MaterialSettings>& GetSettings();
private:
struct PhongUniformOffsets
{
std::size_t alphaThreshold;
std::size_t shininess;
std::size_t ambientColor;
std::size_t diffuseColor;
std::size_t specularColor;
};
struct TextureIndexes
{
std::size_t alpha;
std::size_t diffuse;
std::size_t emissive;
std::size_t height;
std::size_t normal;
std::size_t specular;
};
static bool Initialize();
static void Uninitialize();
MaterialRef m_material;
std::size_t m_phongUniformIndex;
TextureIndexes m_textureIndexes;
PhongUniformOffsets m_phongUniformOffsets;
static std::shared_ptr<MaterialSettings> s_materialSettings;
static std::size_t s_phongUniformBlockIndex;
static RenderPipelineLayoutRef s_renderPipelineLayout;
static TextureIndexes s_textureIndexes;
static PhongUniformOffsets s_phongUniformOffsets;
};
}
#include <Nazara/Graphics/PhongLightingMaterial.inl>
#endif // NAZARA_PHONG_LIGHTING_MATERIAL_HPP

View File

@ -0,0 +1,156 @@
// 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/PhongLightingMaterial.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
inline const TextureRef& PhongLightingMaterial::GetAlphaMap() const
{
NazaraAssert(HasAlphaMap(), "Material has no alpha map slot");
return m_material->GetTexture(m_textureIndexes.alpha);
}
inline const TextureRef& PhongLightingMaterial::GetDiffuseMap() const
{
NazaraAssert(HasDiffuseMap(), "Material has no alpha map slot");
return m_material->GetTexture(m_textureIndexes.diffuse);
}
inline const TextureRef& PhongLightingMaterial::GetEmissiveMap() const
{
NazaraAssert(HasEmissiveMap(), "Material has no alpha map slot");
return m_material->GetTexture(m_textureIndexes.emissive);
}
inline const TextureRef& PhongLightingMaterial::GetHeightMap() const
{
NazaraAssert(HasHeightMap(), "Material has no alpha map slot");
return m_material->GetTexture(m_textureIndexes.height);
}
inline const TextureRef& PhongLightingMaterial::GetNormalMap() const
{
NazaraAssert(HasNormalMap(), "Material has no alpha map slot");
return m_material->GetTexture(m_textureIndexes.normal);
}
inline const TextureRef& PhongLightingMaterial::GetSpecularMap() const
{
NazaraAssert(HasSpecularMap(), "Material has no alpha map slot");
return m_material->GetTexture(m_textureIndexes.specular);
}
inline bool PhongLightingMaterial::HasAlphaMap() const
{
return m_textureIndexes.alpha != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasAlphaThreshold() const
{
return m_phongUniformOffsets.alphaThreshold != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasAmbientColor() const
{
return m_phongUniformOffsets.ambientColor != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasDiffuseColor() const
{
return m_phongUniformOffsets.diffuseColor != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasDiffuseMap() const
{
return m_textureIndexes.diffuse != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasEmissiveMap() const
{
return m_textureIndexes.emissive != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasHeightMap() const
{
return m_textureIndexes.height != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasNormalMap() const
{
return m_textureIndexes.normal != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasShininess() const
{
return m_phongUniformOffsets.shininess != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasSpecularColor() const
{
return m_phongUniformOffsets.specularColor != MaterialSettings::InvalidIndex;
}
inline bool PhongLightingMaterial::HasSpecularMap() const
{
return m_textureIndexes.specular != MaterialSettings::InvalidIndex;
}
inline void PhongLightingMaterial::SetAlphaMap(TextureRef alphaMap)
{
NazaraAssert(HasAlphaMap(), "Material has no alpha map slot");
m_material->SetTexture(m_textureIndexes.alpha, std::move(alphaMap));
}
inline bool PhongLightingMaterial::SetDiffuseMap(const String& textureName)
{
TextureRef texture = TextureLibrary::Query(textureName);
if (!texture)
{
texture = TextureManager::Get(textureName);
if (!texture)
{
NazaraError("Failed to get diffuse map \"" + textureName + "\"");
return false;
}
}
SetDiffuseMap(std::move(texture));
return true;
}
inline void PhongLightingMaterial::SetDiffuseMap(TextureRef diffuseMap)
{
NazaraAssert(HasDiffuseMap(), "Material has no diffuse map slot");
m_material->SetTexture(m_textureIndexes.diffuse, std::move(diffuseMap));
}
inline bool PhongLightingMaterial::SetNormalMap(const String& textureName)
{
TextureRef texture = TextureLibrary::Query(textureName);
if (!texture)
{
texture = TextureManager::Get(textureName);
if (!texture)
{
NazaraError("Failed to get normal map \"" + textureName + "\"");
return false;
}
}
SetNormalMap(std::move(texture));
return true;
}
inline void PhongLightingMaterial::SetNormalMap(TextureRef normalMap)
{
NazaraAssert(HasNormalMap(), "Material has no normal map slot");
m_material->SetTexture(m_textureIndexes.normal, std::move(normalMap));
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -0,0 +1,68 @@
// 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
#pragma once
#ifndef NAZARA_PREDEFINEDSHADERSTRUCTS_HPP
#define NAZARA_PREDEFINEDSHADERSTRUCTS_HPP
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/MaterialSettings.hpp>
#include <array>
namespace Nz
{
struct NAZARA_GRAPHICS_API PredefinedLightData
{
struct InnerStruct
{
std::size_t type;
std::size_t color;
std::size_t factor;
std::size_t parameter1;
std::size_t parameter2;
std::size_t parameter3;
std::size_t shadowMappingFlag;
std::size_t totalSize;
};
InnerStruct innerOffsets;
std::array<std::size_t, 3> lightArray;
std::size_t lightArraySize;
static PredefinedLightData GetOffset();
static MaterialSettings::SharedUniformBlock GetUniformBlock();
};
struct NAZARA_GRAPHICS_API PredefinedInstanceData
{
std::size_t invWorldMatrixOffset;
std::size_t totalSize;
std::size_t worldMatrixOffset;
static PredefinedInstanceData GetOffset();
static MaterialSettings::SharedUniformBlock GetUniformBlock();
};
struct NAZARA_GRAPHICS_API PredefinedViewerData
{
std::size_t eyePositionOffset;
std::size_t invProjMatrixOffset;
std::size_t invTargetSizeOffset;
std::size_t invViewMatrixOffset;
std::size_t invViewProjMatrixOffset;
std::size_t projMatrixOffset;
std::size_t targetSizeOffset;
std::size_t totalSize;
std::size_t viewMatrixOffset;
std::size_t viewProjMatrixOffset;
static PredefinedViewerData GetOffset();
static MaterialSettings::SharedUniformBlock GetUniformBlock();
};
}
#include <Nazara/Graphics/PredefinedShaderStructs.inl>
#endif // NAZARA_PREDEFINEDSHADERSTRUCTS_HPP

View File

@ -0,0 +1,9 @@
// 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/PredefinedShaderStructs.hpp>
namespace Nz
{
}

View File

@ -0,0 +1,101 @@
// 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
#pragma once
#ifndef NAZARA_RENDERQUEUE_HPP
#define NAZARA_RENDERQUEUE_HPP
#include <Nazara/Prerequisites.hpp>
#include <memory>
#include <vector>
namespace Nz
{
class RenderQueueInternal
{
public:
using Index = Nz::UInt64;
RenderQueueInternal() = default;
RenderQueueInternal(const RenderQueueInternal&) = default;
RenderQueueInternal(RenderQueueInternal&&) = default;
~RenderQueueInternal() = default;
RenderQueueInternal& operator=(const RenderQueueInternal&) = default;
RenderQueueInternal& operator=(RenderQueueInternal&&) = default;
protected:
using RenderDataPair = std::pair<Index, std::size_t>;
void Sort();
std::vector<RenderDataPair> m_orderedRenderQueue;
};
template<typename RenderData>
class RenderQueue : public RenderQueueInternal
{
public:
class const_iterator;
friend const_iterator;
using size_type = std::size_t;
RenderQueue() = default;
RenderQueue(const RenderQueue&) = default;
RenderQueue(RenderQueue&&) noexcept = default;
~RenderQueue() = default;
void Clear();
void Insert(RenderData&& data);
template<typename IndexFunc> void Sort(IndexFunc&& func);
// STL API
inline const_iterator begin() const;
inline bool empty() const;
inline const_iterator end() const;
inline size_type size() const;
RenderQueue& operator=(const RenderQueue&) = default;
RenderQueue& operator=(RenderQueue&&) noexcept = default;
private:
const RenderData& GetData(std::size_t i) const;
std::vector<RenderData> m_data;
};
template<typename RenderData>
class RenderQueue<RenderData>::const_iterator : public std::iterator<std::forward_iterator_tag, const RenderData>
{
friend RenderQueue;
public:
const_iterator(const const_iterator& it);
const RenderData& operator*() const;
const_iterator& operator=(const const_iterator& it);
const_iterator& operator++();
const_iterator operator++(int);
bool operator==(const const_iterator& rhs) const;
bool operator!=(const const_iterator& rhs) const;
void swap(const_iterator& rhs);
private:
const_iterator(const RenderQueue* queue, std::size_t nextId);
std::size_t m_nextDataId;
const RenderQueue* m_queue;
};
}
#include <Nazara/Graphics/RenderQueue.inl>
#endif // NAZARA_RENDERQUEUE_HPP

View File

@ -0,0 +1,136 @@
// 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/RenderQueue.hpp>
#include <Nazara/Core/Error.hpp>
namespace Nz
{
template<typename RenderData>
void RenderQueue<RenderData>::Clear()
{
m_orderedRenderQueue.clear();
m_data.clear();
}
template<typename RenderData>
void RenderQueue<RenderData>::Insert(RenderData&& data)
{
m_data.emplace_back(std::move(data));
}
template<typename RenderData>
template<typename IndexFunc>
void RenderQueue<RenderData>::Sort(IndexFunc&& func)
{
m_orderedRenderQueue.clear();
m_orderedRenderQueue.reserve(m_data.size());
std::size_t dataIndex = 0;
for (const RenderData& renderData : m_data)
m_orderedRenderQueue.emplace_back(func(renderData), dataIndex++);
RenderQueueInternal::Sort();
}
template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator RenderQueue<RenderData>::begin() const
{
return const_iterator(this, 0);
}
template<typename RenderData>
bool RenderQueue<RenderData>::empty() const
{
return m_orderedRenderQueue.empty();
}
template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator RenderQueue<RenderData>::end() const
{
return const_iterator(this, m_orderedRenderQueue.size());
}
template<typename RenderData>
typename RenderQueue<RenderData>::size_type RenderQueue<RenderData>::size() const
{
return m_orderedRenderQueue.size();
}
template<typename RenderData>
const RenderData& RenderQueue<RenderData>::GetData(std::size_t i) const
{
NazaraAssert(i < m_orderedRenderQueue.size(), "Cannot dereference post-end iterator");
return m_data[m_orderedRenderQueue[i].second];
}
template<typename RenderData>
RenderQueue<RenderData>::const_iterator::const_iterator(const RenderQueue* queue, std::size_t nextId) :
m_nextDataId(nextId),
m_queue(queue)
{
}
template<typename RenderData>
RenderQueue<RenderData>::const_iterator::const_iterator(const const_iterator& it) :
m_nextDataId(it.m_nextDataId),
m_queue(it.m_queue)
{
}
template<typename RenderData>
const RenderData& RenderQueue<RenderData>::const_iterator::operator*() const
{
return m_queue->GetData(m_nextDataId);
}
template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator& RenderQueue<RenderData>::const_iterator::operator=(const const_iterator& it)
{
m_nextDataId = it.m_nextDataId;
m_queue = it.m_queue;
return *this;
}
template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator& RenderQueue<RenderData>::const_iterator::operator++()
{
++m_nextDataId;
return *this;
}
template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator RenderQueue<RenderData>::const_iterator::operator++(int)
{
return iterator(m_queue, m_nextDataId++);
}
template<typename RenderData>
bool RenderQueue<RenderData>::const_iterator::operator==(const typename RenderQueue<RenderData>::const_iterator& rhs) const
{
NazaraAssert(m_queue == rhs.m_queue, "Cannot compare iterator coming from different queues");
return m_nextDataId == rhs.m_nextDataId;
}
template<typename RenderData>
bool RenderQueue<RenderData>::const_iterator::operator!=(const typename RenderQueue<RenderData>::const_iterator& rhs) const
{
return !operator==(rhs);
}
template<typename RenderData>
void RenderQueue<RenderData>::const_iterator::swap(typename RenderQueue<RenderData>::const_iterator& rhs)
{
NazaraAssert(m_queue == rhs.m_queue, "Cannot swap iterator coming from different queues");
using std::swap;
swap(m_nextDataId, rhs.m_nextDataId);
}
}

View File

@ -10,7 +10,6 @@
#include <Nazara/Utility/Enums.hpp>
#include <Nazara/Renderer/RenderPipelineLayout.hpp>
#include <Nazara/Renderer/RenderStates.hpp>
//#include <Nazara/Renderer/Shader.hpp>
namespace Nz
{

View File

@ -0,0 +1,203 @@
// 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/BasicMaterial.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Utility/BufferMapper.hpp>
#include <Nazara/Utility/FieldOffsets.hpp>
#include <Nazara/Utility/MaterialData.hpp>
#include <cassert>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
constexpr std::size_t AlphaMapBinding = 0;
constexpr std::size_t DiffuseMapBinding = 1;
constexpr std::size_t TextureOverlayBinding = 2;
}
BasicMaterial::BasicMaterial(Material* material) :
m_material(material)
{
NazaraAssert(material, "Invalid material");
// Most common case: don't fetch texture indexes as a little optimization
const std::shared_ptr<const MaterialSettings>& materialSettings = material->GetSettings();
if (materialSettings == s_materialSettings)
{
m_textureIndexes = s_textureIndexes;
m_uniformBlockIndex = s_uniformBlockIndex;
m_uniformOffsets = s_uniformOffsets;
}
else
{
m_textureIndexes.alpha = materialSettings->GetTextureIndex("Alpha");
m_textureIndexes.diffuse = materialSettings->GetTextureIndex("Diffuse");
m_uniformBlockIndex = materialSettings->GetUniformBlockIndex("BasicSettings");
m_uniformOffsets.alphaThreshold = materialSettings->GetUniformBlockVariableOffset(m_uniformBlockIndex, "AlphaThreshold");
m_uniformOffsets.diffuseColor = materialSettings->GetUniformBlockVariableOffset(m_uniformBlockIndex, "DiffuseColor");
}
}
float BasicMaterial::GetAlphaThreshold() const
{
NazaraAssert(HasAlphaThreshold(), "Material has no alpha threshold uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_uniformBlockIndex), BufferAccess_ReadOnly);
return *AccessByOffset<const float>(mapper.GetPointer(), m_uniformOffsets.alphaThreshold);
}
Color BasicMaterial::GetDiffuseColor() const
{
NazaraAssert(HasDiffuseColor(), "Material has no diffuse color uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_uniformBlockIndex), BufferAccess_ReadOnly);
const float* colorPtr = AccessByOffset<const float>(mapper.GetPointer(), m_uniformOffsets.diffuseColor);
return Color(colorPtr[0] * 255, colorPtr[1] * 255, colorPtr[2] * 255, colorPtr[3] * 255); //< TODO: Make color able to use float
}
void BasicMaterial::SetAlphaThreshold(float alphaThreshold)
{
NazaraAssert(HasAlphaThreshold(), "Material has no alpha threshold uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_uniformBlockIndex), BufferAccess_WriteOnly);
*AccessByOffset<float>(mapper.GetPointer(), m_uniformOffsets.alphaThreshold) = alphaThreshold;
}
void BasicMaterial::SetDiffuseColor(const Color& diffuse)
{
NazaraAssert(HasDiffuseColor(), "Material has no diffuse color uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_uniformBlockIndex), BufferAccess_WriteOnly);
float* colorPtr = AccessByOffset<float>(mapper.GetPointer(), m_uniformOffsets.diffuseColor);
colorPtr[0] = diffuse.r / 255.f;
colorPtr[1] = diffuse.g / 255.f;
colorPtr[2] = diffuse.b / 255.f;
colorPtr[3] = diffuse.a / 255.f;
}
const std::shared_ptr<MaterialSettings>& BasicMaterial::GetSettings()
{
return s_materialSettings;
}
bool BasicMaterial::Initialize()
{
RenderPipelineLayoutInfo info;
info.bindings.assign({
{
"MaterialAlphaMap",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
AlphaMapBinding
},
{
"MaterialDiffuseMap",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
DiffuseMapBinding
},
{
"TextureOverlay",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
TextureOverlayBinding
}
});
s_renderPipelineLayout = RenderPipelineLayout::New();
s_renderPipelineLayout->Create(info);
FieldOffsets fieldOffsets(StructLayout_Std140);
s_uniformOffsets.diffuseColor = fieldOffsets.AddField(StructFieldType_Float4);
s_uniformOffsets.alphaThreshold = fieldOffsets.AddField(StructFieldType_Float1);
MaterialSettings::PredefinedBinding predefinedBinding;
predefinedBinding.fill(MaterialSettings::InvalidIndex);
std::vector<MaterialSettings::UniformVariable> variables;
variables.assign({
{
"AlphaThreshold",
s_uniformOffsets.alphaThreshold
},
{
"DiffuseColor",
s_uniformOffsets.diffuseColor
}
});
static_assert(sizeof(Vector4f) == 4 * sizeof(float), "Vector4f is expected to be exactly 4 floats wide");
std::vector<UInt8> defaultValues(fieldOffsets.GetSize());
*AccessByOffset<Vector4f>(defaultValues.data(), s_uniformOffsets.diffuseColor) = Vector4f(1.f, 1.f, 1.f, 1.f);
*AccessByOffset<float>(defaultValues.data(), s_uniformOffsets.alphaThreshold) = 0.2f;
std::vector<MaterialSettings::UniformBlock> uniformBlocks;
s_uniformBlockIndex = uniformBlocks.size();
uniformBlocks.assign({
{
"BasicSettings",
fieldOffsets.GetSize(),
"MaterialBasicSettings",
std::move(variables),
std::move(defaultValues)
}
});
std::vector<MaterialSettings::SharedUniformBlock> sharedUniformBlock;
predefinedBinding[PredefinedShaderBinding_UboInstanceData] = sharedUniformBlock.size();
sharedUniformBlock.push_back(PredefinedInstanceData::GetUniformBlock());
predefinedBinding[PredefinedShaderBinding_UboViewerData] = sharedUniformBlock.size();
sharedUniformBlock.push_back(PredefinedViewerData::GetUniformBlock());
std::vector<MaterialSettings::Texture> textures;
s_textureIndexes.alpha = textures.size();
textures.push_back({
"Alpha",
ImageType_2D,
"MaterialAlphaMap"
});
s_textureIndexes.diffuse = textures.size();
textures.push_back({
"Diffuse",
ImageType_2D,
"MaterialDiffuseMap"
});
predefinedBinding[PredefinedShaderBinding_TexOverlay] = textures.size();
textures.push_back({
"Overlay",
ImageType_2D,
"TextureOverlay"
});
s_materialSettings = std::make_shared<MaterialSettings>(std::move(textures), std::move(uniformBlocks), std::move(sharedUniformBlock), predefinedBinding);
return true;
}
void BasicMaterial::Uninitialize()
{
s_renderPipelineLayout.Reset();
s_materialSettings.reset();
}
std::shared_ptr<MaterialSettings> BasicMaterial::s_materialSettings;
std::size_t BasicMaterial::s_uniformBlockIndex;
RenderPipelineLayoutRef BasicMaterial::s_renderPipelineLayout;
BasicMaterial::TextureIndexes BasicMaterial::s_textureIndexes;
BasicMaterial::UniformOffsets BasicMaterial::s_uniformOffsets;
}

View File

@ -0,0 +1,956 @@
// 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/BasicRenderQueue.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <limits>
#include <Nazara/Graphics/Debug.hpp>
///TODO: Replace sinus/cosinus by a lookup table (which will lead to a speed up about 10x)
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::BasicRenderQueue
* \brief Graphics class that represents a simple rendering queue
*/
/*!
* \brief Adds multiple billboards to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the billboards
* \param count Number of billboards
* \param positionPtr Position of the billboards
* \param sizePtr Sizes of the billboards
* \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used
* \param colorPtr Color of the billboards if null, Color::White is used
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr<const Vector3f> positionPtr, SparsePtr<const Vector2f> sizePtr, SparsePtr<const Vector2f> sinCosPtr, SparsePtr<const Color> colorPtr)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1
if (!sinCosPtr)
sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile
if (!colorPtr)
colorPtr.Reset(&Color::White, 0); // Same
if (material->IsDepthSortingEnabled())
{
for (std::size_t i = 0; i < billboardCount; ++i)
{
depthSortedBillboards.Insert({
renderOrder,
material,
scissorRect,
{
*colorPtr++,
*positionPtr++,
*sizePtr++,
*sinCosPtr++
}
});
}
}
else
{
std::size_t billboardIndex = m_billboards.size();
m_billboards.resize(billboardIndex + billboardCount);
BillboardData* data = &m_billboards[billboardIndex];
for (std::size_t i = 0; i < billboardCount; ++i)
{
data->center = *positionPtr++;
data->color = *colorPtr++;
data->sinCos = *sinCosPtr++;
data->size = *sizePtr++;
data++;
}
billboards.Insert({
renderOrder,
material,
scissorRect,
billboardCount,
billboardIndex
});
}
}
/*!
* \brief Adds multiple billboards to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the billboards
* \param count Number of billboards
* \param positionPtr Position of the billboards
* \param sizePtr Sizes of the billboards
* \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used
* \param alphaPtr Alpha parameters of the billboards if null, 1.f is used
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr<const Vector3f> positionPtr, SparsePtr<const Vector2f> sizePtr, SparsePtr<const Vector2f> sinCosPtr, SparsePtr<const float> alphaPtr)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1
if (!sinCosPtr)
sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile
float defaultAlpha = 1.f;
if (!alphaPtr)
alphaPtr.Reset(&defaultAlpha, 0); // Same
if (material->IsDepthSortingEnabled())
{
for (std::size_t i = 0; i < billboardCount; ++i)
{
depthSortedBillboards.Insert({
renderOrder,
material,
scissorRect,
{
ComputeColor(*alphaPtr++),
*positionPtr++,
*sizePtr++,
*sinCosPtr++
}
});
}
}
else
{
std::size_t billboardIndex = m_billboards.size();
m_billboards.resize(billboardIndex + billboardCount);
BillboardData* data = &m_billboards[billboardIndex];
for (std::size_t i = 0; i < billboardCount; ++i)
{
data->center = *positionPtr++;
data->color = ComputeColor(*alphaPtr++);
data->sinCos = *sinCosPtr++;
data->size = *sizePtr++;
data++;
}
billboards.Insert({
renderOrder,
material,
scissorRect,
billboardCount,
billboardIndex
});
}
}
/*!
* \brief Adds multiple billboards to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the billboards
* \param count Number of billboards
* \param positionPtr Position of the billboards
* \param sizePtr Sizes of the billboards
* \param anglePtr Rotation of the billboards if null, 0.f is used
* \param colorPtr Color of the billboards if null, Color::White is used
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr<const Vector3f> positionPtr, SparsePtr<const Vector2f> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const Color> colorPtr)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
float defaultRotation = 0.f;
if (!anglePtr)
anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile
if (!colorPtr)
colorPtr.Reset(&Color::White, 0); // Same
if (material->IsDepthSortingEnabled())
{
for (std::size_t i = 0; i < billboardCount; ++i)
{
depthSortedBillboards.Insert({
renderOrder,
material,
scissorRect,
{
*colorPtr++,
*positionPtr++,
*sizePtr++,
ComputeSinCos(*anglePtr++)
}
});
}
}
else
{
std::size_t billboardIndex = m_billboards.size();
m_billboards.resize(billboardIndex + billboardCount);
BillboardData* data = &m_billboards[billboardIndex];
for (std::size_t i = 0; i < billboardCount; ++i)
{
data->center = *positionPtr++;
data->color = *colorPtr++;
data->sinCos = ComputeSinCos(*anglePtr++);
data->size = *sizePtr++;
data++;
}
billboards.Insert({
renderOrder,
material,
scissorRect,
billboardCount,
billboardIndex
});
}
}
/*!
* \brief Adds multiple billboards to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the billboards
* \param count Number of billboards
* \param positionPtr Position of the billboards
* \param sizePtr Sizes of the billboards
* \param anglePtr Rotation of the billboards if null, 0.f is used
* \param alphaPtr Alpha parameters of the billboards if null, 1.f is used
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr<const Vector3f> positionPtr, SparsePtr<const Vector2f> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const float> alphaPtr)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
float defaultRotation = 0.f;
if (!anglePtr)
anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile
float defaultAlpha = 1.f;
if (!alphaPtr)
alphaPtr.Reset(&defaultAlpha, 0); // Same
if (material->IsDepthSortingEnabled())
{
for (std::size_t i = 0; i < billboardCount; ++i)
{
depthSortedBillboards.Insert({
renderOrder,
material,
scissorRect,
{
ComputeColor(*alphaPtr++),
*positionPtr++,
*sizePtr++,
ComputeSinCos(*anglePtr++)
}
});
}
}
else
{
std::size_t billboardIndex = m_billboards.size();
m_billboards.resize(billboardIndex + billboardCount);
BillboardData* data = &m_billboards[billboardIndex];
for (std::size_t i = 0; i < billboardCount; ++i)
{
data->center = *positionPtr++;
data->color = ComputeColor(*alphaPtr++);
data->sinCos = ComputeSinCos(*anglePtr++);
data->size = *sizePtr++;
data++;
}
billboards.Insert({
renderOrder,
material,
scissorRect,
billboardCount,
billboardIndex
});
}
}
/*!
* \brief Adds multiple billboards to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the billboards
* \param count Number of billboards
* \param positionPtr Position of the billboards
* \param sizePtr Size of the billboards
* \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used
* \param colorPtr Color of the billboards if null, Color::White is used
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const Vector2f> sinCosPtr, SparsePtr<const Color> colorPtr)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1
if (!sinCosPtr)
sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile
if (!colorPtr)
colorPtr.Reset(&Color::White, 0); // Same
if (material->IsDepthSortingEnabled())
{
for (std::size_t i = 0; i < billboardCount; ++i)
{
depthSortedBillboards.Insert({
renderOrder,
material,
scissorRect,
{
*colorPtr++,
*positionPtr++,
ComputeSize(*sizePtr++),
*sinCosPtr++
}
});
}
}
else
{
std::size_t billboardIndex = m_billboards.size();
m_billboards.resize(billboardIndex + billboardCount);
BillboardData* data = &m_billboards[billboardIndex];
for (std::size_t i = 0; i < billboardCount; ++i)
{
data->center = *positionPtr++;
data->color = *colorPtr++;
data->sinCos = *sinCosPtr++;
data->size = ComputeSize(*sizePtr++);
data++;
}
billboards.Insert({
renderOrder,
material,
scissorRect,
billboardCount,
billboardIndex
});
}
}
/*!
* \brief Adds multiple billboards to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the billboards
* \param count Number of billboards
* \param positionPtr Position of the billboards
* \param sizePtr Size of the billboards
* \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used
* \param alphaPtr Alpha parameters of the billboards if null, 1.f is used
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const Vector2f> sinCosPtr, SparsePtr<const float> alphaPtr)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1
if (!sinCosPtr)
sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile
float defaultAlpha = 1.f;
if (!alphaPtr)
alphaPtr.Reset(&defaultAlpha, 0); // Same
if (material->IsDepthSortingEnabled())
{
for (std::size_t i = 0; i < billboardCount; ++i)
{
depthSortedBillboards.Insert({
renderOrder,
material,
scissorRect,
{
ComputeColor(*alphaPtr++),
*positionPtr++,
ComputeSize(*sizePtr++),
*sinCosPtr++
}
});
}
}
else
{
std::size_t billboardIndex = m_billboards.size();
m_billboards.resize(billboardIndex + billboardCount);
BillboardData* data = &m_billboards[billboardIndex];
for (std::size_t i = 0; i < billboardCount; ++i)
{
data->center = *positionPtr++;
data->color = ComputeColor(*alphaPtr++);
data->sinCos = *sinCosPtr++;
data->size = ComputeSize(*sizePtr++);
data++;
}
billboards.Insert({
renderOrder,
material,
scissorRect,
billboardCount,
billboardIndex
});
}
}
/*!
* \brief Adds multiple billboards to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the billboards
* \param count Number of billboards
* \param positionPtr Position of the billboards
* \param sizePtr Size of the billboards
* \param anglePtr Rotation of the billboards if null, 0.f is used
* \param colorPtr Color of the billboards if null, Color::White is used
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const Color> colorPtr)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
float defaultRotation = 0.f;
if (!anglePtr)
anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile
if (!colorPtr)
colorPtr.Reset(&Color::White, 0); // Same
if (material->IsDepthSortingEnabled())
{
for (std::size_t i = 0; i < billboardCount; ++i)
{
depthSortedBillboards.Insert({
renderOrder,
material,
scissorRect,
{
*colorPtr++,
*positionPtr++,
ComputeSize(*sizePtr++),
ComputeSinCos(*anglePtr++)
}
});
}
}
else
{
std::size_t billboardIndex = m_billboards.size();
m_billboards.resize(billboardIndex + billboardCount);
BillboardData* data = &m_billboards[billboardIndex];
for (std::size_t i = 0; i < billboardCount; ++i)
{
data->center = *positionPtr++;
data->color = *colorPtr++;
data->sinCos = ComputeSinCos(*anglePtr++);
data->size = ComputeSize(*sizePtr++);
data++;
}
billboards.Insert({
renderOrder,
material,
scissorRect,
billboardCount,
billboardIndex
});
}
}
/*!
* \brief Adds multiple billboards to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the billboards
* \param count Number of billboards
* \param positionPtr Position of the billboards
* \param sizePtr Size of the billboards
* \param anglePtr Rotation of the billboards if null, 0.f is used
* \param alphaPtr Alpha parameters of the billboards if null, 1.f is used
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const float> alphaPtr)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
float defaultRotation = 0.f;
if (!anglePtr)
anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile
float defaultAlpha = 1.f;
if (!alphaPtr)
alphaPtr.Reset(&defaultAlpha, 0); // Same
if (material->IsDepthSortingEnabled())
{
for (std::size_t i = 0; i < billboardCount; ++i)
{
depthSortedBillboards.Insert({
renderOrder,
material,
scissorRect,
{
ComputeColor(*alphaPtr++),
*positionPtr++,
ComputeSize(*sizePtr++),
ComputeSinCos(*anglePtr++)
}
});
}
}
else
{
std::size_t billboardIndex = m_billboards.size();
m_billboards.resize(billboardIndex + billboardCount);
BillboardData* data = &m_billboards[billboardIndex];
for (std::size_t i = 0; i < billboardCount; ++i)
{
data->center = *positionPtr++;
data->color = ComputeColor(*alphaPtr++);
data->sinCos = ComputeSinCos(*anglePtr++);
data->size = ComputeSize(*sizePtr++);
data++;
}
billboards.Insert({
renderOrder,
material,
scissorRect,
billboardCount,
billboardIndex
});
}
}
/*!
* \brief Adds drawable to the queue
*
* \param renderOrder Order of rendering
* \param drawable Drawable user defined
*
* \remark Produces a NazaraError if drawable is invalid
*/
void BasicRenderQueue::AddDrawable(int renderOrder, const Drawable* drawable)
{
NazaraAssert(drawable, "Invalid material");
RegisterLayer(renderOrder);
customDrawables.Insert({
renderOrder,
drawable
});
}
/*!
* \brief Adds mesh to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the mesh
* \param meshData Data of the mesh
* \param meshAABB Box of the mesh
* \param transformMatrix Matrix of the mesh
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Nz::Matrix4f& transformMatrix, std::size_t instanceIndex, const Recti& scissorRect)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
Spheref obbSphere(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius());
if (material->IsDepthSortingEnabled())
{
depthSortedModels.Insert({
renderOrder,
instanceIndex,
meshData,
material,
scissorRect,
obbSphere
});
}
else
{
models.Insert({
renderOrder,
instanceIndex,
meshData,
material,
scissorRect,
obbSphere
});
}
}
/*!
* \brief Adds sprites to the queue
*
* \param renderOrder Order of rendering
* \param material Material of the sprites
* \param vertices Buffer of data for the sprites
* \param spriteCount Number of sprites
* \param overlay Texture of the sprites
*
* \remark Produces a NazaraAssert if material is invalid
*/
void BasicRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay /*= nullptr*/)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
if (material->IsDepthSortingEnabled())
{
depthSortedSprites.Insert({
renderOrder,
spriteCount,
material,
overlay,
vertices,
scissorRect
});
}
else
{
basicSprites.Insert({
renderOrder,
spriteCount,
material,
overlay,
vertices,
scissorRect
});
}
}
/*!
* \brief Clears the queue
*
* \param fully Should everything be cleared or we can keep layers
*/
void BasicRenderQueue::Clear(bool fully)
{
AbstractRenderQueue::Clear(fully);
basicSprites.Clear();
billboards.Clear();
depthSortedBillboards.Clear();
depthSortedModels.Clear();
depthSortedSprites.Clear();
models.Clear();
m_pipelineCache.clear();
m_materialCache.clear();
m_overlayCache.clear();
m_shaderCache.clear();
m_textureCache.clear();
m_vertexBufferCache.clear();
m_billboards.clear();
m_renderLayers.clear();
}
/*!
* \brief Sorts the object according to the viewer position, furthest to nearest
*
* \param viewer Viewer of the scene
*/
void BasicRenderQueue::Sort(const AbstractViewer* viewer)
{
m_layerCache.clear();
for (int layer : m_renderLayers)
m_layerCache.emplace(layer, m_layerCache.size());
auto GetOrInsert = [](auto& container, auto&& value)
{
auto it = container.find(value);
if (it == container.end())
it = container.emplace(value, container.size()).first;
return it->second;
};
basicSprites.Sort([&](const SpriteChain& vertices)
{
// RQ index:
// - Layer (16bits)
// - Pipeline (8bits)
// - Material (8bits)
// - Shader? (8bits)
// - Textures (8bits)
// - Overlay (8bits)
// - Scissor (4bits)
// - ??? (4bits)
UInt64 layerIndex = m_layerCache[vertices.layerIndex];
UInt64 pipelineIndex = GetOrInsert(m_pipelineCache, vertices.material->GetPipeline());
UInt64 materialIndex = GetOrInsert(m_materialCache, vertices.material);
UInt64 shaderIndex = GetOrInsert(m_shaderCache, vertices.material->GetShader());
UInt64 textureIndex = 0;/* GetOrInsert(m_textureCache, vertices.material->GetDiffuseMap());*/
UInt64 overlayIndex = GetOrInsert(m_overlayCache, vertices.overlay);
UInt64 scissorIndex = 0; //< TODO
UInt64 index = (layerIndex & 0xFFFF) << 48 |
(pipelineIndex & 0xFF) << 40 |
(materialIndex & 0xFF) << 32 |
(shaderIndex & 0xFF) << 24 |
(textureIndex & 0xFF) << 16 |
(overlayIndex & 0xFF) << 8 |
(scissorIndex & 0x0F) << 4;
return index;
});
billboards.Sort([&](const BillboardChain& billboard)
{
// RQ index:
// - Layer (16bits)
// - Pipeline (8bits)
// - Material (8bits)
// - Shader? (8bits)
// - Textures (8bits)
// - Scissor (4bits)
// - ??? (12bits)
UInt64 layerIndex = m_layerCache[billboard.layerIndex];
UInt64 pipelineIndex = GetOrInsert(m_pipelineCache, billboard.material->GetPipeline());
UInt64 materialIndex = GetOrInsert(m_materialCache, billboard.material);
UInt64 shaderIndex = GetOrInsert(m_shaderCache, billboard.material->GetShader());
UInt64 textureIndex = 0; /*GetOrInsert(m_textureCache, billboard.material->GetDiffuseMap())*/;
UInt64 unknownIndex = 0; //< ???
UInt64 scissorIndex = 0; //< TODO
UInt64 index = (layerIndex & 0xFFFF) << 48 |
(pipelineIndex & 0xFF) << 40 |
(materialIndex & 0xFF) << 32 |
(shaderIndex & 0xFF) << 24 |
(textureIndex & 0xFF) << 16 |
(scissorIndex & 0x0F) << 12 |
(unknownIndex & 0xFF) << 0;
return index;
});
customDrawables.Sort([&](const CustomDrawable& drawable)
{
// RQ index:
// - Layer (16bits)
UInt64 layerIndex = m_layerCache[drawable.layerIndex];
UInt64 index = (layerIndex & 0xFFFF) << 48;
return index;
});
models.Sort([&](const Model& renderData)
{
// RQ index:
// - Layer (16bits)
// - Pipeline (8bits)
// - Material (8bits)
// - Shader? (8bits)
// - Textures (8bits)
// - Buffers (8bits)
// - Scissor (4bits)
// - ??? (4bits)
UInt64 layerIndex = m_layerCache[renderData.layerIndex];
UInt64 pipelineIndex = GetOrInsert(m_pipelineCache, renderData.material->GetPipeline());
UInt64 materialIndex = GetOrInsert(m_materialCache, renderData.material);
UInt64 shaderIndex = GetOrInsert(m_shaderCache, renderData.material->GetShader());
UInt64 textureIndex = 0;/* GetOrInsert(m_textureCache, renderData.material->GetDiffuseMap()) */;
UInt64 bufferIndex = GetOrInsert(m_vertexBufferCache, renderData.meshData.vertexBuffer);
UInt64 scissorIndex = 0; //< TODO
UInt64 depthIndex = 0; //< TODO
UInt64 index = (layerIndex & 0xFFFF) << 48 |
(pipelineIndex & 0xFF) << 40 |
(materialIndex & 0xFF) << 32 |
(shaderIndex & 0xFF) << 24 |
(textureIndex & 0xFF) << 16 |
(bufferIndex & 0xFF) << 8 |
(scissorIndex & 0x0F) << 4;
return index;
});
static_assert(std::numeric_limits<float>::is_iec559, "The following sorting functions relies on IEEE 754 floatings-points");
#if defined(arm) && \
((defined(__MAVERICK__) && defined(NAZARA_BIG_ENDIAN)) || \
(!defined(__SOFTFP__) && !defined(__VFP_FP__) && !defined(__MAVERICK__)))
#error The following code relies on native-endian IEEE-754 representation, which your platform does not guarantee
#endif
Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near);
depthSortedBillboards.Sort([&](const Billboard& billboard)
{
// RQ index:
// - Layer (16bits)
// - Depth (32bits)
// - ?? (16bits)
// Reinterpret depth as UInt32 (this will work as long as they're all either positive or negative,
// a negative distance may happen with billboard behind the camera which we don't care about since they'll not be rendered)
float depth = nearPlane.Distance(billboard.data.center);
UInt64 layerIndex = m_layerCache[billboard.layerIndex];
UInt64 depthIndex = ~reinterpret_cast<UInt32&>(depth);
UInt64 index = (layerIndex & 0xFFFF) << 48 |
(depthIndex & 0xFFFFFFFF) << 16;
return index;
});
if (viewer->GetProjectionType() == ProjectionType_Orthogonal)
{
depthSortedModels.Sort([&](const Model& model)
{
// RQ index:
// - Layer (16bits)
// - Depth (32bits)
// - ?? (16bits)
float depth = nearPlane.Distance(model.obbSphere.GetPosition());
UInt64 layerIndex = m_layerCache[model.layerIndex];
UInt64 depthIndex = ~reinterpret_cast<UInt32&>(depth);
UInt64 index = (layerIndex & 0xFFFF) << 48 |
(depthIndex & 0xFFFFFFFF) << 16;
return index;
});
depthSortedSprites.Sort([&](const SpriteChain& spriteChain)
{
// RQ index:
// - Layer (16bits)
// - Depth (32bits)
// - ?? (16bits)
float depth = nearPlane.Distance(spriteChain.vertices[0].position);
UInt64 layerIndex = m_layerCache[spriteChain.layerIndex];
UInt64 depthIndex = ~reinterpret_cast<UInt32&>(depth);
UInt64 index = (layerIndex & 0xFFFF) << 48 |
(depthIndex & 0xFFFFFFFF) << 16;
return index;
});
}
else
{
Vector3f viewerPos = viewer->GetEyePosition();
depthSortedModels.Sort([&](const Model& model)
{
// RQ index:
// - Layer (16bits)
// - Depth (32bits)
// - ?? (16bits)
float depth = viewerPos.SquaredDistance(model.obbSphere.GetPosition());
UInt64 layerIndex = m_layerCache[model.layerIndex];
UInt64 depthIndex = ~reinterpret_cast<UInt32&>(depth);
UInt64 index = (layerIndex & 0x0F) << 48 |
(depthIndex & 0xFFFFFFFF) << 16;
return index;
});
depthSortedSprites.Sort([&](const SpriteChain& sprites)
{
// RQ index:
// - Layer (16bits)
// - Depth (32bits)
// - ?? (16bits)
float depth = viewerPos.SquaredDistance(sprites.vertices[0].position);
UInt64 layerIndex = m_layerCache[sprites.layerIndex];
UInt64 depthIndex = ~reinterpret_cast<UInt32&>(depth);
UInt64 index = (layerIndex & 0xFFFF) << 48 |
(depthIndex & 0xFFFFFFFF) << 16;
return index;
});
}
}
}

View File

@ -3,6 +3,7 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/Graphics.hpp>
#include <stdexcept>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
@ -13,13 +14,29 @@ namespace Nz
* \brief Audio class that represents the module initializer of Graphics
*/
Graphics::Graphics(Config /*config*/) :
Graphics::Graphics(Config config) :
ModuleBase("Graphics", this)
{
}
Renderer* renderer = Renderer::Instance();
RendererImpl* rendererImpl = renderer->GetRendererImpl(); //< FIXME
std::vector<RenderDeviceInfo> renderDeviceInfo = rendererImpl->QueryRenderDevices();
if (renderDeviceInfo.empty())
throw std::runtime_error("no render device available");
Graphics::~Graphics()
{
std::size_t bestRenderDeviceIndex = 0;
for (std::size_t i = 0; i < renderDeviceInfo.size(); ++i)
{
const auto& deviceInfo = renderDeviceInfo[i];
if (config.useDedicatedRenderDevice && deviceInfo.type == RenderDeviceType::Dedicated)
{
bestRenderDeviceIndex = i;
break;
}
}
m_renderDevice = rendererImpl->InstanciateRenderDevice(bestRenderDeviceIndex);
if (!m_renderDevice)
throw std::runtime_error("failed to instantiate render device");
}
Graphics* Graphics::s_instance = nullptr;

View File

@ -0,0 +1,487 @@
// 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/Graphics/BasicMaterial.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 Constructs a Material object with default states
*
* \see Reset
*/
Material::Material(std::shared_ptr<const MaterialSettings> settings) :
m_settings(std::move(settings)),
m_reflectionMode(ReflectionMode_Skybox),
m_pipelineUpdated(false),
m_shadowCastingEnabled(true),
m_reflectionSize(256)
{
m_pipelineInfo.settings = m_settings;
SetShader("Basic");
m_textures.resize(m_settings->GetTextures().size());
m_uniformBuffers.reserve(m_settings->GetUniformBlocks().size());
for (const auto& uniformBufferInfo : m_settings->GetUniformBlocks())
{
//TODO: Use pools
UniformBufferRef ubo = UniformBuffer::New(static_cast<UInt32>(uniformBufferInfo.blockSize), DataStorage_Hardware, BufferUsage_Dynamic);
ubo->Fill(uniformBufferInfo.defaultValues.data(), 0, uniformBufferInfo.defaultValues.size());
m_uniformBuffers.emplace_back(std::move(ubo));
}
}
/*!
* \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;
unsigned int bindingIndex = 0;
for (const MaterialTexture& textureData : m_textures)
{
auto it = instance.bindings.find(bindingIndex++);
assert(it != instance.bindings.end());
unsigned int textureIndex = it->second;
Renderer::SetTexture(textureIndex, textureData.texture);
Renderer::SetTextureSampler(textureIndex, textureData.sampler);
}
for (const UniformBufferRef& ubo : m_uniformBuffers)
{
auto it = instance.bindings.find(bindingIndex++);
assert(it != instance.bindings.end());
unsigned int uniformBufferIndex = it->second;
Renderer::SetUniformBuffer(uniformBufferIndex, ubo);
}
/*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);
if (matData.GetBooleanParameter(MaterialData::VertexColor, &isEnabled))
EnableVertexColor(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());
matData->SetParameter(MaterialData::VertexColor, HasVertexColor());
// 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 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(BasicMaterial::GetSettings());
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;
}

View File

@ -0,0 +1,235 @@
// 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/MaterialPipeline.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Graphics/BasicMaterial.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/MaterialSettings.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
const UInt8 r_basicFragmentShader[] = {
#include <Nazara/Graphics/Resources/Shaders/Basic/core.frag.h>
};
const UInt8 r_basicVertexShader[] = {
#include <Nazara/Graphics/Resources/Shaders/Basic/core.vert.h>
};
const UInt8 r_phongLightingFragmentShader[] = {
#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h>
};
const UInt8 r_phongLightingVertexShader[] = {
#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h>
};
void OverrideShader(const String& path, String* source)
{
ErrorFlags errFlags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled);
File shaderFile(path, Nz::OpenMode_ReadOnly | Nz::OpenMode_Text);
if (shaderFile.IsOpen())
{
StringStream shaderSource;
while (!shaderFile.EndOfFile())
{
shaderSource << shaderFile.ReadLine();
shaderSource << '\n';
}
*source = shaderSource;
NazaraNotice(path + " will be used to override built-in shader");
}
}
}
/*!
* \ingroup graphics
* \class Nz::MaterialPipeline
*
* \brief Graphics class used to contains all rendering states that are not allowed to change individually on rendering devices
*/
/*!
* \brief Returns a reference to a MaterialPipeline built with MaterialPipelineInfo
*
* This function is using a cache, calling it multiples times with the same MaterialPipelineInfo will returns references to a single MaterialPipeline
*
* \param pipelineInfo Pipeline informations used to build/retrieve a MaterialPipeline object
*/
MaterialPipelineRef MaterialPipeline::GetPipeline(const MaterialPipelineInfo& pipelineInfo)
{
auto it = s_pipelineCache.find(pipelineInfo);
if (it == s_pipelineCache.end())
it = s_pipelineCache.insert(it, PipelineCache::value_type(pipelineInfo, New(pipelineInfo)));
return it->second;
}
void MaterialPipeline::GenerateRenderPipeline(UInt32 flags) const
{
NazaraAssert(m_pipelineInfo.settings, "Material pipeline has no settings");
NazaraAssert(m_pipelineInfo.uberShader, "Material pipeline has no uber shader");
const auto& textures = m_pipelineInfo.settings->GetTextures();
ParameterList list;
for (std::size_t i = 0, texCount = textures.size(); i < texCount; ++i)
{
const auto& texture = textures[i];
String parameterName = "HAS_" + texture.name.ToUpper() + "_TEXTURE";
list.SetParameter(parameterName, (m_pipelineInfo.textures & (1 << i)) != 0);
}
list.SetParameter("ALPHA_TEST", m_pipelineInfo.alphaTest);
list.SetParameter("REFLECTION_MAPPING", m_pipelineInfo.reflectionMapping);
list.SetParameter("SHADOW_MAPPING", m_pipelineInfo.shadowReceive);
list.SetParameter("TEXTURE_MAPPING", m_pipelineInfo.textures != 0 ||
m_pipelineInfo.reflectionMapping || flags & ShaderFlags_TextureOverlay);
list.SetParameter("TRANSFORM", true);
list.SetParameter("FLAG_BILLBOARD", static_cast<bool>((flags & ShaderFlags_Billboard) != 0));
list.SetParameter("FLAG_DEFERRED", static_cast<bool>((flags & ShaderFlags_Deferred) != 0));
list.SetParameter("FLAG_INSTANCING", static_cast<bool>((flags & ShaderFlags_Instancing) != 0));
list.SetParameter("FLAG_TEXTUREOVERLAY", static_cast<bool>((flags & ShaderFlags_TextureOverlay) != 0));
list.SetParameter("FLAG_VERTEXCOLOR", m_pipelineInfo.hasVertexColor || static_cast<bool>((flags & ShaderFlags_VertexColor) != 0));
Instance& instance = m_instances[flags];
instance.uberInstance = m_pipelineInfo.uberShader->Get(list);
RenderPipelineInfo renderPipelineInfo;
static_cast<RenderStates&>(renderPipelineInfo).operator=(m_pipelineInfo); // Not my proudest line
renderPipelineInfo.shader = instance.uberInstance->GetShader();
instance.renderPipeline.Create(renderPipelineInfo);
// Send texture units (those never changes)
const RenderPipelineLayout* pipelineLayout = m_pipelineInfo.pipelineLayout;
if (!pipelineLayout)
pipelineLayout = m_pipelineInfo.settings->GetRenderPipelineLayout();
instance.bindings = renderPipelineInfo.shader->ApplyLayout(pipelineLayout);
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("ReflectionMap"), Material::GetTextureUnit(TextureMap_ReflectionCube));
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("DirectionalSpotLightShadowMap[0]"), Material::GetTextureUnit(TextureMap_Shadow2D_1));
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("DirectionalSpotLightShadowMap[1]"), Material::GetTextureUnit(TextureMap_Shadow2D_2));
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("DirectionalSpotLightShadowMap[2]"), Material::GetTextureUnit(TextureMap_Shadow2D_3));
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("PointLightShadowMap[0]"), Material::GetTextureUnit(TextureMap_ShadowCube_1));
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("PointLightShadowMap[1]"), Material::GetTextureUnit(TextureMap_ShadowCube_2));
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("PointLightShadowMap[2]"), Material::GetTextureUnit(TextureMap_ShadowCube_3));
}
bool MaterialPipeline::Initialize()
{
// Basic shader
{
UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();
String fragmentShader(reinterpret_cast<const char*>(r_basicFragmentShader), sizeof(r_basicFragmentShader));
String vertexShader(reinterpret_cast<const char*>(r_basicVertexShader), sizeof(r_basicVertexShader));
#ifdef NAZARA_DEBUG
OverrideShader("Shaders/Basic/core.frag", &fragmentShader);
OverrideShader("Shaders/Basic/core.vert", &vertexShader);
#endif
uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_TEXTUREOVERLAY AUTO_TEXCOORDS HAS_ALPHA_TEXTURE HAS_DIFFUSE_TEXTURE TEXTURE_MAPPING");
uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
UberShaderLibrary::Register("Basic", uberShader);
}
if (!BasicMaterial::Initialize())
{
NazaraError("Failed to initialize phong lighting materials");
return false;
}
// PhongLighting shader
{
UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();
String fragmentShader(reinterpret_cast<const char*>(r_phongLightingFragmentShader), sizeof(r_phongLightingFragmentShader));
String vertexShader(reinterpret_cast<const char*>(r_phongLightingVertexShader), sizeof(r_phongLightingVertexShader));
#ifdef NAZARA_DEBUG
OverrideShader("Shaders/PhongLighting/core.frag", &fragmentShader);
OverrideShader("Shaders/PhongLighting/core.vert", &vertexShader);
#endif
uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_DEFERRED FLAG_TEXTUREOVERLAY ALPHA_TEST AUTO_TEXCOORDS HAS_ALPHA_TEXTURE HAS_DIFFUSE_TEXTURE HAS_EMISSIVE_TEXTURE HAS_NORMAL_TEXTURE HAS_HEIGHT_TEXTURE HAS_SPECULAR_TEXTURE REFLECTION_MAPPING SHADOW_MAPPING");
uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR HAS_NORMAL_TEXTURE SHADOW_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
UberShaderLibrary::Register("PhongLighting", uberShader);
}
if (!PhongLightingMaterial::Initialize())
{
NazaraError("Failed to initialize phong lighting materials");
return false;
}
// Once the base shaders are registered, we can now set some default materials
MaterialPipelineInfo pipelineInfo;
pipelineInfo.settings = BasicMaterial::GetSettings();
pipelineInfo.uberShader = UberShaderLibrary::Get("Basic");
// Basic 2D - No depth write/face culling with scissoring
pipelineInfo.depthWrite = false;
pipelineInfo.faceCulling = false;
pipelineInfo.scissorTest = true;
MaterialPipelineLibrary::Register("Basic2D", GetPipeline(pipelineInfo));
// Translucent 2D - Alpha blending with no depth write/face culling and scissoring
pipelineInfo.blending = true;
pipelineInfo.depthWrite = false;
pipelineInfo.faceCulling = false;
pipelineInfo.depthSorting = false;
pipelineInfo.scissorTest = true;
pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
pipelineInfo.srcBlend = BlendFunc_SrcAlpha;
MaterialPipelineLibrary::Register("Translucent2D", GetPipeline(pipelineInfo));
// Translucent 3D - Alpha blending with depth buffer and no depth write/face culling
pipelineInfo.blending = true;
pipelineInfo.depthBuffer = true;
pipelineInfo.depthWrite = false;
pipelineInfo.faceCulling = false;
pipelineInfo.depthSorting = true;
pipelineInfo.scissorTest = false;
pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
pipelineInfo.srcBlend = BlendFunc_SrcAlpha;
MaterialPipelineLibrary::Register("Translucent3D", GetPipeline(pipelineInfo));
return true;
}
void MaterialPipeline::Uninitialize()
{
s_pipelineCache.clear();
UberShaderLibrary::Unregister("PhongLighting");
PhongLightingMaterial::Uninitialize();
UberShaderLibrary::Unregister("Basic");
BasicMaterial::Uninitialize();
MaterialPipelineLibrary::Uninitialize();
}
MaterialPipelineLibrary::LibraryMap MaterialPipeline::s_library;
MaterialPipeline::PipelineCache MaterialPipeline::s_pipelineCache;
}

View File

@ -0,0 +1,331 @@
// 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/PhongLightingMaterial.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Utility/BufferMapper.hpp>
#include <Nazara/Utility/FieldOffsets.hpp>
#include <Nazara/Utility/MaterialData.hpp>
#include <cassert>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
constexpr std::size_t AlphaMapBinding = 0;
constexpr std::size_t DiffuseMapBinding = 1;
constexpr std::size_t EmissiveMapBinding = 2;
constexpr std::size_t HeightMapBinding = 3;
constexpr std::size_t NormalMapBinding = 4;
constexpr std::size_t SpecularMapBinding = 5;
constexpr std::size_t TextureOverlayBinding = 6;
}
PhongLightingMaterial::PhongLightingMaterial(Material* material) :
m_material(material)
{
NazaraAssert(material, "Invalid material");
// Most common case: don't fetch texture indexes as a little optimization
const std::shared_ptr<const MaterialSettings>& materialSettings = material->GetSettings();
if (materialSettings == s_materialSettings)
{
m_textureIndexes = s_textureIndexes;
m_phongUniformIndex = s_phongUniformBlockIndex;
m_phongUniformOffsets = s_phongUniformOffsets;
}
else
{
m_textureIndexes.alpha = materialSettings->GetTextureIndex("Alpha");
m_textureIndexes.diffuse = materialSettings->GetTextureIndex("Diffuse");
m_textureIndexes.emissive = materialSettings->GetTextureIndex("Emissive");
m_textureIndexes.height = materialSettings->GetTextureIndex("Height");
m_textureIndexes.normal = materialSettings->GetTextureIndex("Normal");
m_textureIndexes.specular = materialSettings->GetTextureIndex("Specular");
m_phongUniformIndex = materialSettings->GetUniformBlockIndex("PhongSettings");
m_phongUniformOffsets.alphaThreshold = materialSettings->GetUniformBlockVariableOffset(m_phongUniformIndex, "AlphaThreshold");
m_phongUniformOffsets.ambientColor = materialSettings->GetUniformBlockVariableOffset(m_phongUniformIndex, "AmbientColor");
m_phongUniformOffsets.diffuseColor = materialSettings->GetUniformBlockVariableOffset(m_phongUniformIndex, "DiffuseColor");
m_phongUniformOffsets.shininess = materialSettings->GetUniformBlockVariableOffset(m_phongUniformIndex, "Shininess");
m_phongUniformOffsets.specularColor = materialSettings->GetUniformBlockVariableOffset(m_phongUniformIndex, "SpecularColor");
}
}
float PhongLightingMaterial::GetAlphaThreshold() const
{
NazaraAssert(HasAlphaThreshold(), "Material has no alpha threshold uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_ReadOnly);
return *AccessByOffset<const float>(mapper.GetPointer(), m_phongUniformOffsets.alphaThreshold);
}
Color PhongLightingMaterial::GetAmbientColor() const
{
NazaraAssert(HasAmbientColor(), "Material has no ambient color uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_ReadOnly);
const float* colorPtr = AccessByOffset<const float>(mapper.GetPointer(), m_phongUniformOffsets.ambientColor);
return Color(colorPtr[0] * 255, colorPtr[1] * 255, colorPtr[2] * 255, colorPtr[3] * 255); //< TODO: Make color able to use float
}
Color PhongLightingMaterial::GetDiffuseColor() const
{
NazaraAssert(HasDiffuseColor(), "Material has no diffuse color uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_ReadOnly);
const float* colorPtr = AccessByOffset<const float>(mapper.GetPointer(), m_phongUniformOffsets.diffuseColor);
return Color(colorPtr[0] * 255, colorPtr[1] * 255, colorPtr[2] * 255, colorPtr[3] * 255); //< TODO: Make color able to use float
}
float Nz::PhongLightingMaterial::GetShininess() const
{
NazaraAssert(HasShininess(), "Material has no shininess uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_ReadOnly);
return *AccessByOffset<const float>(mapper.GetPointer(), m_phongUniformOffsets.shininess);
}
Color PhongLightingMaterial::GetSpecularColor() const
{
NazaraAssert(HasSpecularColor(), "Material has no specular color uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_ReadOnly);
const float* colorPtr = AccessByOffset<const float>(mapper.GetPointer(), m_phongUniformOffsets.specularColor);
return Color(colorPtr[0] * 255, colorPtr[1] * 255, colorPtr[2] * 255, colorPtr[3] * 255); //< TODO: Make color able to use float
}
void PhongLightingMaterial::SetAlphaThreshold(float alphaThreshold)
{
NazaraAssert(HasAlphaThreshold(), "Material has no alpha threshold uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_WriteOnly);
*AccessByOffset<float>(mapper.GetPointer(), m_phongUniformOffsets.alphaThreshold) = alphaThreshold;
}
void PhongLightingMaterial::SetAmbientColor(const Color& ambient)
{
NazaraAssert(HasAmbientColor(), "Material has no ambient color uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_WriteOnly);
float* colorPtr = AccessByOffset<float>(mapper.GetPointer(), m_phongUniformOffsets.ambientColor);
colorPtr[0] = ambient.r / 255.f;
colorPtr[1] = ambient.g / 255.f;
colorPtr[2] = ambient.b / 255.f;
colorPtr[3] = ambient.a / 255.f;
}
void PhongLightingMaterial::SetDiffuseColor(const Color& diffuse)
{
NazaraAssert(HasDiffuseColor(), "Material has no diffuse color uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_WriteOnly);
float* colorPtr = AccessByOffset<float>(mapper.GetPointer(), m_phongUniformOffsets.diffuseColor);
colorPtr[0] = diffuse.r / 255.f;
colorPtr[1] = diffuse.g / 255.f;
colorPtr[2] = diffuse.b / 255.f;
colorPtr[3] = diffuse.a / 255.f;
}
void PhongLightingMaterial::SetSpecularColor(const Color& diffuse)
{
NazaraAssert(HasSpecularColor(), "Material has no specular color uniform");
BufferMapper<UniformBuffer> mapper(m_material->GetUniformBuffer(m_phongUniformIndex), BufferAccess_WriteOnly);
float* colorPtr = AccessByOffset<float>(mapper.GetPointer(), m_phongUniformOffsets.specularColor);
colorPtr[0] = diffuse.r / 255.f;
colorPtr[1] = diffuse.g / 255.f;
colorPtr[2] = diffuse.b / 255.f;
colorPtr[3] = diffuse.a / 255.f;
}
const std::shared_ptr<MaterialSettings>& PhongLightingMaterial::GetSettings()
{
return s_materialSettings;
}
bool PhongLightingMaterial::Initialize()
{
RenderPipelineLayoutInfo info;
info.bindings.assign({
{
"MaterialAlphaMap",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
AlphaMapBinding
},
{
"MaterialDiffuseMap",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
DiffuseMapBinding
},
{
"MaterialEmissiveMap",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
EmissiveMapBinding
},
{
"MaterialHeightMap",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
HeightMapBinding
},
{
"MaterialNormalMap",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
NormalMapBinding
},
{
"MaterialSpecularMap",
ShaderBindingType_Texture,
ShaderStageType_Fragment,
SpecularMapBinding
}
});
s_renderPipelineLayout = RenderPipelineLayout::New();
s_renderPipelineLayout->Create(info);
std::vector<MaterialSettings::UniformBlock> uniformBlocks;
// MaterialPhongSettings
FieldOffsets phongUniformStruct(StructLayout_Std140);
s_phongUniformOffsets.alphaThreshold = phongUniformStruct.AddField(StructFieldType_Float1);
s_phongUniformOffsets.shininess = phongUniformStruct.AddField(StructFieldType_Float1);
s_phongUniformOffsets.ambientColor = phongUniformStruct.AddField(StructFieldType_Float4);
s_phongUniformOffsets.diffuseColor = phongUniformStruct.AddField(StructFieldType_Float4);
s_phongUniformOffsets.specularColor = phongUniformStruct.AddField(StructFieldType_Float4);
MaterialSettings::PredefinedBinding predefinedBinding;
predefinedBinding.fill(MaterialSettings::InvalidIndex);
std::vector<MaterialSettings::UniformVariable> phongVariables;
phongVariables.assign({
{
"AlphaThreshold",
s_phongUniformOffsets.alphaThreshold
},
{
"Shininess",
s_phongUniformOffsets.shininess
},
{
"AmbientColor",
s_phongUniformOffsets.ambientColor
},
{
"DiffuseColor",
s_phongUniformOffsets.diffuseColor
},
{
"SpecularColor",
s_phongUniformOffsets.specularColor
}
});
static_assert(sizeof(Vector4f) == 4 * sizeof(float), "Vector4f is expected to be exactly 4 floats wide");
std::vector<UInt8> defaultValues(phongUniformStruct.GetSize());
*AccessByOffset<Vector4f>(defaultValues.data(), s_phongUniformOffsets.ambientColor) = Vector4f(0.5f, 0.5f, 0.5f, 1.f);
*AccessByOffset<Vector4f>(defaultValues.data(), s_phongUniformOffsets.diffuseColor) = Vector4f(1.f, 1.f, 1.f, 1.f);
*AccessByOffset<Vector4f>(defaultValues.data(), s_phongUniformOffsets.specularColor) = Vector4f(1.f, 1.f, 1.f, 1.f);
*AccessByOffset<float>(defaultValues.data(), s_phongUniformOffsets.alphaThreshold) = 0.2f;
*AccessByOffset<float>(defaultValues.data(), s_phongUniformOffsets.shininess) = 50.f;
s_phongUniformBlockIndex = uniformBlocks.size();
uniformBlocks.push_back({
"PhongSettings",
phongUniformStruct.GetSize(),
"MaterialPhongSettings",
std::move(phongVariables),
std::move(defaultValues)
});
std::vector<MaterialSettings::SharedUniformBlock> sharedUniformBlock;
predefinedBinding[PredefinedShaderBinding_UboInstanceData] = sharedUniformBlock.size();
sharedUniformBlock.push_back(PredefinedInstanceData::GetUniformBlock());
predefinedBinding[PredefinedShaderBinding_UboLighData] = sharedUniformBlock.size();
sharedUniformBlock.push_back(PredefinedLightData::GetUniformBlock());
predefinedBinding[PredefinedShaderBinding_UboViewerData] = sharedUniformBlock.size();
sharedUniformBlock.push_back(PredefinedViewerData::GetUniformBlock());
std::vector<MaterialSettings::Texture> textures;
s_textureIndexes.alpha = textures.size();
textures.push_back({
"Alpha",
ImageType_2D,
"MaterialAlphaMap"
});
s_textureIndexes.diffuse = textures.size();
textures.push_back({
"Diffuse",
ImageType_2D,
"MaterialDiffuseMap"
});
s_textureIndexes.emissive = textures.size();
textures.push_back({
"Emissive",
ImageType_2D,
"MaterialEmissiveMap"
});
s_textureIndexes.height = textures.size();
textures.push_back({
"Height",
ImageType_2D,
"MaterialHeightMap"
});
s_textureIndexes.normal = textures.size();
textures.push_back({
"Normal",
ImageType_2D,
"MaterialNormalMap"
});
s_textureIndexes.specular = textures.size();
textures.push_back({
"Specular",
ImageType_2D,
"MaterialSpecularMap"
});
predefinedBinding[PredefinedShaderBinding_TexOverlay] = textures.size();
textures.push_back({
"Overlay",
ImageType_2D,
"TextureOverlay"
});
s_materialSettings = std::make_shared<MaterialSettings>(std::move(textures), std::move(uniformBlocks), std::move(sharedUniformBlock), predefinedBinding);
return true;
}
void PhongLightingMaterial::Uninitialize()
{
s_renderPipelineLayout.Reset();
s_materialSettings.reset();
}
std::shared_ptr<MaterialSettings> PhongLightingMaterial::s_materialSettings;
std::size_t PhongLightingMaterial::s_phongUniformBlockIndex;
RenderPipelineLayoutRef PhongLightingMaterial::s_renderPipelineLayout;
PhongLightingMaterial::TextureIndexes PhongLightingMaterial::s_textureIndexes;
PhongLightingMaterial::PhongUniformOffsets PhongLightingMaterial::s_phongUniformOffsets;
}

View File

@ -0,0 +1,167 @@
// 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/PredefinedShaderStructs.hpp>
#include <Nazara/Utility/FieldOffsets.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
PredefinedLightData PredefinedLightData::GetOffset()
{
PredefinedLightData lightData;
FieldOffsets lightStruct(StructLayout_Std140);
lightData.innerOffsets.type = lightStruct.AddField(StructFieldType_Int1);
lightData.innerOffsets.color = lightStruct.AddField(StructFieldType_Float4);
lightData.innerOffsets.factor = lightStruct.AddField(StructFieldType_Float2);
lightData.innerOffsets.parameter1 = lightStruct.AddField(StructFieldType_Float4);
lightData.innerOffsets.parameter2 = lightStruct.AddField(StructFieldType_Float4);
lightData.innerOffsets.parameter3 = lightStruct.AddField(StructFieldType_Float2);
lightData.innerOffsets.shadowMappingFlag = lightStruct.AddField(StructFieldType_Bool1);
lightData.innerOffsets.totalSize = lightStruct.GetSize();
FieldOffsets lightDataStruct(StructLayout_Std140);
for (std::size_t& lightOffset : lightData.lightArray)
lightOffset = lightDataStruct.AddStruct(lightStruct);
lightData.lightArraySize = lightDataStruct.GetSize();
return lightData;
}
MaterialSettings::SharedUniformBlock PredefinedLightData::GetUniformBlock()
{
PredefinedLightData lightData = GetOffset();
std::vector<MaterialSettings::UniformVariable> lightDataVariables;
for (std::size_t i = 0; i < lightData.lightArray.size(); ++i)
{
lightDataVariables.push_back({
"LightData[" + std::to_string(i) + "]",
lightData.lightArray[i]
});
}
MaterialSettings::SharedUniformBlock uniformBlock = {
"Light",
"LightData",
std::move(lightDataVariables)
};
return uniformBlock;
}
PredefinedInstanceData PredefinedInstanceData::GetOffset()
{
FieldOffsets viewerStruct(StructLayout_Std140);
PredefinedInstanceData instanceData;
instanceData.worldMatrixOffset = viewerStruct.AddMatrix(StructFieldType_Float1, 4, 4, true);
instanceData.invWorldMatrixOffset = viewerStruct.AddMatrix(StructFieldType_Float1, 4, 4, true);
instanceData.totalSize = viewerStruct.GetSize();
return instanceData;
}
MaterialSettings::SharedUniformBlock PredefinedInstanceData::GetUniformBlock()
{
PredefinedInstanceData instanceData = GetOffset();
std::vector<MaterialSettings::UniformVariable> instanceDataVariables;
instanceDataVariables.assign({
{
"WorldMatrix",
instanceData.worldMatrixOffset
},
{
"InvWorldMatrix",
instanceData.invWorldMatrixOffset
},
});
MaterialSettings::SharedUniformBlock uniformBlock = {
"Instance",
"InstanceData",
std::move(instanceDataVariables)
};
return uniformBlock;
}
PredefinedViewerData PredefinedViewerData::GetOffset()
{
FieldOffsets viewerStruct(StructLayout_Std140);
PredefinedViewerData viewerData;
viewerData.projMatrixOffset = viewerStruct.AddMatrix(StructFieldType_Float1, 4, 4, true);
viewerData.invProjMatrixOffset = viewerStruct.AddMatrix(StructFieldType_Float1, 4, 4, true);
viewerData.viewMatrixOffset = viewerStruct.AddMatrix(StructFieldType_Float1, 4, 4, true);
viewerData.invViewMatrixOffset = viewerStruct.AddMatrix(StructFieldType_Float1, 4, 4, true);
viewerData.viewProjMatrixOffset = viewerStruct.AddMatrix(StructFieldType_Float1, 4, 4, true);
viewerData.invViewProjMatrixOffset = viewerStruct.AddMatrix(StructFieldType_Float1, 4, 4, true);
viewerData.targetSizeOffset = viewerStruct.AddField(StructFieldType_Float2);
viewerData.invTargetSizeOffset = viewerStruct.AddField(StructFieldType_Float2);
viewerData.eyePositionOffset = viewerStruct.AddField(StructFieldType_Float3);
viewerData.totalSize = viewerStruct.GetSize();
return viewerData;
}
MaterialSettings::SharedUniformBlock PredefinedViewerData::GetUniformBlock()
{
PredefinedViewerData viewerData = GetOffset();
std::vector<MaterialSettings::UniformVariable> viewerDataVariables;
viewerDataVariables.assign({
{
"ProjMatrix",
viewerData.projMatrixOffset
},
{
"InvProjMatrix",
viewerData.invProjMatrixOffset
},
{
"ViewMatrix",
viewerData.viewMatrixOffset
},
{
"InvViewMatrix",
viewerData.invViewMatrixOffset
},
{
"ViewProjMatrix",
viewerData.viewProjMatrixOffset
},
{
"InvViewProjMatrix",
viewerData.invViewProjMatrixOffset
},
{
"TargetSize",
viewerData.targetSizeOffset
},
{
"InvTargetSize",
viewerData.invTargetSizeOffset
},
{
"EyePosition",
viewerData.eyePositionOffset
}
});
MaterialSettings::SharedUniformBlock uniformBlock = {
"Viewer",
"ViewerData",
std::move(viewerDataVariables)
};
return uniformBlock;
}
}