Remove Graphics module and fix compilation

This commit is contained in:
Jérôme Leclercq
2020-08-27 19:43:31 +02:00
parent bc92d030ed
commit 2a875c8bdc
283 changed files with 16 additions and 32474 deletions

View File

@@ -1,21 +0,0 @@
// Copyright (C) 2020 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/AbstractBackground.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::AbstractBackground
* \brief Graphics class that represents the background for our scene
*
* \remark This class is abstract
*/
AbstractBackground::~AbstractBackground() = default;
BackgroundLibrary::LibraryMap AbstractBackground::s_library;
}

View File

@@ -1,67 +0,0 @@
// Copyright (C) 2020 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/AbstractRenderQueue.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::AbstractRenderQueue
* \brief Graphics class that represents the rendering queue for our scene
*
* \remark This class is abstract
*/
AbstractRenderQueue::~AbstractRenderQueue() = default;
/*!
* \brief Adds a directional light to the rendering queue
*
* \param light Directional light
*/
void AbstractRenderQueue::AddDirectionalLight(const DirectionalLight& light)
{
directionalLights.push_back(light);
}
/*!
* \brief Adds a point light to the rendering queue
*
* \param light Point light
*/
void AbstractRenderQueue::AddPointLight(const PointLight& light)
{
pointLights.push_back(light);
}
/*!
* \brief Adds a spot light to the rendering queue
*
* \param light Spot light
*/
void AbstractRenderQueue::AddSpotLight(const SpotLight& light)
{
spotLights.push_back(light);
}
/*!
* \brief Clears the rendering queue
*
* \param fully Should everything be cleared ?
*/
void AbstractRenderQueue::Clear(bool fully)
{
NazaraUnused(fully);
directionalLights.clear();
pointLights.clear();
spotLights.clear();
}
}

View File

@@ -1,62 +0,0 @@
// Copyright (C) 2020 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/AbstractRenderTechnique.hpp>
#include <Nazara/Graphics/RenderTechniques.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::AbstractRenderTechnique
* \brief Graphics class that represents the rendering technique for our scene
*
* \remark This class is abstract
*/
/*!
* \brief Constructs a AbstractRenderTechnique object
*/
AbstractRenderTechnique::AbstractRenderTechnique() :
m_instancingEnabled(true)
{
}
AbstractRenderTechnique::~AbstractRenderTechnique() = default;
/*!
* \brief Enables the instancing
*
* \param instancing Should instancing be enabled
*
* \remark This may improve performances
*/
void AbstractRenderTechnique::EnableInstancing(bool instancing)
{
m_instancingEnabled = instancing;
}
/*!
* \brief Gets the name of the actual technique
* \return Name of the technique being used
*/
String AbstractRenderTechnique::GetName() const
{
return RenderTechniques::ToString(GetType());
}
/*!
* \brief Checks whether the instancing is enabled
* \return true If it is the case
*/
bool AbstractRenderTechnique::IsInstancingEnabled() const
{
return m_instancingEnabled;
}
}

View File

@@ -1,66 +0,0 @@
// Copyright (C) 2020 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/AbstractViewer.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::AbstractViewer
* \brief Graphics class that represents the viewer for our scene
*
* \remark This class is abstract
*/
AbstractViewer::~AbstractViewer() = default;
Vector3f AbstractViewer::Project(const Nz::Vector3f& worldPosition) const
{
Vector4f pos4D(worldPosition, 1.f);
pos4D = GetViewMatrix() * pos4D;
pos4D = GetProjectionMatrix() * pos4D;
pos4D /= pos4D.w;
Rectf viewport = Rectf(GetViewport());
Nz::Vector3f screenPosition(pos4D.x * 0.5f + 0.5f, -pos4D.y * 0.5f + 0.5f, pos4D.z * 0.5f + 0.5f);
screenPosition.x = screenPosition.x * viewport.width + viewport.x;
screenPosition.y = screenPosition.y * viewport.height + viewport.y;
return screenPosition;
}
float AbstractViewer::ProjectDepth(float depth)
{
const Matrix4f& projectionMatrix = GetProjectionMatrix();
float a = projectionMatrix(2, 2);
float b = projectionMatrix(2, 3);
return (0.5f * (-a * depth + b) / depth + 0.5f);
}
Vector3f AbstractViewer::Unproject(const Nz::Vector3f& screenPos) const
{
Rectf viewport = Rectf(GetViewport());
Nz::Vector4f normalizedPosition;
normalizedPosition.x = (screenPos.x - viewport.x) / viewport.width * 2.f - 1.f;
normalizedPosition.y = (screenPos.y - viewport.y) / viewport.height * 2.f - 1.f;
normalizedPosition.z = screenPos.z * 2.f - 1.f;
normalizedPosition.w = 1.f;
normalizedPosition.y = -normalizedPosition.y;
Nz::Matrix4f invMatrix = GetViewMatrix() * GetProjectionMatrix();
invMatrix.Inverse();
Nz::Vector4f worldPos = invMatrix * normalizedPosition;
worldPos /= worldPos.w;
return Nz::Vector3f(worldPos.x, worldPos.y, worldPos.z);
}
}

View File

@@ -1,957 +0,0 @@
// Copyright (C) 2020 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 Matrix4f& transformMatrix, const Recti& scissorRect)
{
NazaraAssert(material, "Invalid material");
RegisterLayer(renderOrder);
Spheref obbSphere(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius());
if (material->IsDepthSortingEnabled())
{
depthSortedModels.Insert({
renderOrder,
meshData,
material,
transformMatrix,
scissorRect,
obbSphere
});
}
else
{
models.Insert({
renderOrder,
meshData,
material,
transformMatrix,
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 = 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 = 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 = 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

@@ -1,53 +0,0 @@
// Copyright (C) 2020 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/Billboard.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Billboard
* \brief Graphics class that represents a billboard, a 2D surface which simulates a 3D object
*/
/*!
* \brief Adds this billboard to the render queue
*
* \param renderQueue Queue to be added
* \param instanceData Data used for instance
*/
void Billboard::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const
{
Nz::Vector3f position = instanceData.transformMatrix.GetTranslation();
renderQueue->AddBillboards(instanceData.renderOrder, GetMaterial(), 1, scissorRect, &position, &m_size, &m_sinCos, &m_color);
}
/*!
* \brief Clones this billboard
*/
std::unique_ptr<InstancedRenderable> Billboard::Clone() const
{
return std::make_unique<Billboard>(*this);
}
/*
* \brief Makes the bounding volume of this billboard
*/
void Billboard::MakeBoundingVolume() const
{
// As billboard always face the screen, we must take its maximum size in account on every axis
float maxSize = float(M_SQRT2) * std::max(m_size.x, m_size.y);
Nz::Vector3f halfSize = (maxSize * Vector3f::Right() + maxSize * Vector3f::Down() + maxSize * Vector3f::Forward()) / 2.f;
m_boundingVolume.Set(-halfSize, halfSize);
}
BillboardLibrary::LibraryMap Billboard::s_library;
}

View File

@@ -1,114 +0,0 @@
// Copyright (C) 2020 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/ColorBackground.hpp>
#include <Nazara/Core/ParameterList.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderStates.hpp>
#include <Nazara/Renderer/UberShaderInstance.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
/*!
* \brief Defines render states
* \return RenderStates for the color background
*/
RenderStates BuildRenderStates()
{
RenderStates states;
states.cullingSide = FaceSide_Back;
states.depthFunc = RendererComparison_Equal;
states.depthBuffer = true;
states.depthWrite = false;
states.faceCulling = true;
return states;
}
}
/*!
* \ingroup graphics
* \class Nz::ColorBackground
* \brief Graphics class that represents a background with uniform color
*/
/*!
* \brief Constructs a ColorBackground object with a color
*
* \param color Uniform color (by default Black)
*/
ColorBackground::ColorBackground(const Color& color) :
m_color(color)
{
m_uberShader = UberShaderLibrary::Get("Basic");
ParameterList list;
list.SetParameter("UNIFORM_VERTEX_DEPTH", true);
m_uberShaderInstance = m_uberShader->Get(list);
const Shader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_vertexDepthUniform = shader->GetUniformLocation("VertexDepth");
}
/*!
* \brief Draws this relatively to the viewer
*
* \param viewer Viewer for the background
*/
void ColorBackground::Draw(const AbstractViewer* viewer) const
{
NazaraUnused(viewer);
static RenderStates states(BuildRenderStates());
Renderer::SetRenderStates(states);
m_uberShaderInstance->Activate();
const Shader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, m_color);
shader->SendFloat(m_vertexDepthUniform, 1.f);
Renderer::DrawFullscreenQuad();
}
/*!
* \brief Gets the background type
* \return Type of background
*/
BackgroundType ColorBackground::GetBackgroundType() const
{
return BackgroundType_Color;
}
/*!
* \brief Gets the color of the background
* \return Background color
*/
Color ColorBackground::GetColor() const
{
return m_color;
}
/*!
* \brief Sets the color of the background
*
* \param color Background color
*/
void ColorBackground::SetColor(const Color& color)
{
m_color = color;
}
}

View File

@@ -1,31 +0,0 @@
// Copyright (C) 2020 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/Config.hpp>
#if NAZARA_GRAPHICS_MANAGE_MEMORY
#include <Nazara/Core/MemoryManager.hpp>
#include <new> // Nécessaire ?
void* operator new(std::size_t size)
{
return Nz::MemoryManager::Allocate(size, false);
}
void* operator new[](std::size_t size)
{
return Nz::MemoryManager::Allocate(size, true);
}
void operator delete(void* pointer) noexcept
{
Nz::MemoryManager::Free(pointer, false);
}
void operator delete[](void* pointer) noexcept
{
Nz::MemoryManager::Free(pointer, true);
}
#endif // NAZARA_GRAPHICS_MANAGE_MEMORY

View File

@@ -1,248 +0,0 @@
// Copyright (C) 2020 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/DeferredBloomPass.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::DeferredBloomPass
* \brief Graphics class that represents the pass for bloom in deferred rendering
*/
/*!
* \brief Constructs a DeferredBloomPass object by default
*/
DeferredBloomPass::DeferredBloomPass() :
m_uniformUpdated(false),
m_brightLuminance(0.8f),
m_brightMiddleGrey(0.5f),
m_brightThreshold(0.4f),
m_blurPassCount(5)
{
m_bilinearSampler.SetAnisotropyLevel(1);
m_bilinearSampler.SetFilterMode(SamplerFilter_Bilinear);
m_bilinearSampler.SetWrapMode(SamplerWrap_Clamp);
m_bloomBrightShader = ShaderLibrary::Get("DeferredBloomBright");
m_bloomFinalShader = ShaderLibrary::Get("DeferredBloomFinal");
m_bloomStates.depthBuffer = false;
m_gaussianBlurShader = ShaderLibrary::Get("DeferredGaussianBlur");
m_gaussianBlurShaderFilterLocation = m_gaussianBlurShader->GetUniformLocation("Filter");
for (unsigned int i = 0; i < 2; ++i)
m_bloomTextures[i] = Texture::New();
}
DeferredBloomPass::~DeferredBloomPass() = default;
/*!
* \brief Gets the number of pass for blur
* \return Number of pass for blur
*/
unsigned int DeferredBloomPass::GetBlurPassCount() const
{
return m_blurPassCount;
}
/*!
* \brief Gets the coefficiant for luminosity
* \return Luminosity of bright elements
*/
float DeferredBloomPass::GetBrightLuminance() const
{
return m_brightLuminance;
}
/*!
* \brief Gets the coefficiant for the middle grey
* \return Luminosity of grey elements
*/
float DeferredBloomPass::GetBrightMiddleGrey() const
{
return m_brightMiddleGrey;
}
/*!
* \brief Gets the coefficiant for things to be bright
* \return Threshold for bright elements
*/
float DeferredBloomPass::GetBrightThreshold() const
{
return m_brightThreshold;
}
/*!
* \brief Gets the ith texture
* \return Texture computed
*
* \param i Index of the texture
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if index is invalid
*/
Texture* DeferredBloomPass::GetTexture(unsigned int i) const
{
#if NAZARA_GRAPHICS_SAFE
if (i >= 2)
{
NazaraError("Texture index out of range (" + String::Number(i) + " >= 2)");
return nullptr;
}
#endif
return m_bloomTextures[i];
}
/*!
* \brief Processes the work on the data while working with textures
* \return true
*
* \param sceneData Data for the scene
* \param firstWorkTexture Index of the first texture to work with
* \param firstWorkTexture Index of the second texture to work with
*/
bool DeferredBloomPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const
{
NazaraUnused(sceneData);
Renderer::SetRenderStates(m_bloomStates);
Renderer::SetTextureSampler(0, m_bilinearSampler);
Renderer::SetTextureSampler(1, m_bilinearSampler);
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetShader(m_bloomBrightShader);
if (!m_uniformUpdated)
{
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightLuminance"), m_brightLuminance);
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightMiddleGrey"), m_brightMiddleGrey);
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightThreshold"), m_brightThreshold);
m_uniformUpdated = true;
}
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::DrawFullscreenQuad();
Renderer::SetTarget(&m_bloomRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x / 8, m_dimensions.y / 8));
Renderer::SetShader(m_gaussianBlurShader);
for (unsigned int i = 0; i < m_blurPassCount; ++i)
{
m_bloomRTT.SetColorTarget(0); // bloomTextureA
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, Vector2f(1.f, 0.f));
Renderer::SetTexture(0, (i == 0) ? m_workTextures[firstWorkTexture] : static_cast<const Texture*>(m_bloomTextures[1]));
Renderer::DrawFullscreenQuad();
m_bloomRTT.SetColorTarget(1); // bloomTextureB
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, Vector2f(0.f, 1.f));
Renderer::SetTexture(0, m_bloomTextures[0]);
Renderer::DrawFullscreenQuad();
}
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetShader(m_bloomFinalShader);
Renderer::SetTexture(0, m_bloomTextures[1]);
Renderer::SetTexture(1, m_workTextures[secondWorkTexture]);
Renderer::DrawFullscreenQuad();
return true;
}
/*!
* \brief Resizes the texture sizes
* \return true If successful
*
* \param dimensions Dimensions for the compute texture
*/
bool DeferredBloomPass::Resize(const Vector2ui& dimensions)
{
DeferredRenderPass::Resize(dimensions);
m_bloomRTT.Create(true);
for (unsigned int i = 0; i < 2; ++i)
{
m_bloomTextures[i]->Create(ImageType_2D, PixelFormat_RGBA8, dimensions.x / 8, dimensions.y / 8);
m_bloomRTT.AttachTexture(AttachmentPoint_Color, i, m_bloomTextures[i]);
}
m_bloomRTT.Unlock();
if (!m_bloomRTT.IsComplete())
{
NazaraError("Incomplete RTT");
return false;
}
return true;
}
/*!
* \brief Sets the number of pass for blur
*
* \param passCount Number of pass for blur
*/
void DeferredBloomPass::SetBlurPassCount(unsigned int passCount)
{
m_blurPassCount = passCount; // N'est pas une uniforme
}
/*!
* \brief Sets the coefficiant for luminosity
*
* \param luminance Luminosity of bright elements
*/
void DeferredBloomPass::SetBrightLuminance(float luminance)
{
m_brightLuminance = luminance;
m_uniformUpdated = false;
}
/*!
* \brief Sets the coefficiant for the middle grey
*
* \param middleGrey Luminosity of grey elements
*/
void DeferredBloomPass::SetBrightMiddleGrey(float middleGrey)
{
m_brightMiddleGrey = middleGrey;
m_uniformUpdated = false;
}
/*!
* \brief Sets the coefficiant for things to be bright
*
* \param threshold Threshold for bright elements
*/
void DeferredBloomPass::SetBrightThreshold(float threshold)
{
m_brightThreshold = threshold;
m_uniformUpdated = false;
}
}

View File

@@ -1,213 +0,0 @@
// Copyright (C) 2020 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/DeferredDOFPass.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTexture.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
/*!
* \brief Builds the shader for the depth of field
* \return Reference to the shader newly created
*/
// http://digitalerr0r.wordpress.com/2009/05/16/xna-shader-programming-tutorial-20-depth-of-field/
ShaderRef BuildDepthOfFieldShader()
{
const char* fragmentSource =
"#version 140\n"
"out vec4 RenderTarget0;\n"
"uniform sampler2D BlurTexture;\n"
"uniform sampler2D ColorTexture;\n"
"uniform sampler2D GBuffer1;\n"
"uniform vec2 InvTargetSize;" "\n"
"float Distance = 30.0;\n"
"float Range = 10.0;\n"
"float Near = 0.1;\n"
"float Far = (1000.0) / (1000.0 - 0.1);\n"
//"float Far = 50.0;\n"
"void main()\n"
"{\n"
"vec2 texCoord = gl_FragCoord.xy * InvTargetSize;\n"
"// Get our original pixel from ColorMap\n"
"vec3 color = textureLod(ColorTexture, texCoord, 0.0).rgb;\n"
"// Get our bloom pixel from bloom texture\n"
"vec3 blur = textureLod(BlurTexture, texCoord, 0.0).rgb;\n"
"float depth = textureLod(GBuffer1, texCoord, 0.0).w;\n"
"depth = (2.0 * 0.1) / (1000.0 + 0.1 - depth * (1000.0 - 0.1));"
"depth = 1.0 - depth;\n"
"float fSceneZ = ( -Near * Far ) / ( depth - Far);\n"
"float blurFactor = clamp(abs(fSceneZ - Distance)/Range, 0.0, 1.0);\n"
"RenderTarget0 = vec4(mix(color, blur, blurFactor), 1.0);\n"
"}\n";
const char* vertexSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"void main()\n"
"{\n"
"\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n"
"}\n";
///TODO: Remplacer ça par des ShaderNode
ShaderRef shader = Shader::New();
if (!shader->Create())
{
NazaraError("Failed to load create shader");
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Fragment, fragmentSource))
{
NazaraError("Failed to load fragment shader");
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Vertex, vertexSource))
{
NazaraError("Failed to load vertex shader");
return nullptr;
}
if (!shader->Link())
{
NazaraError("Failed to link shader");
return nullptr;
}
return shader;
}
}
/*!
* \ingroup graphics
* \class Nz::DeferredDOFPass
* \brief Graphics class that represents the pass for depth of field in deferred rendering
*/
/*!
* \brief Constructs a DeferredDOFPass object by default
*/
DeferredDOFPass::DeferredDOFPass()
{
m_dofShader = BuildDepthOfFieldShader();
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("ColorTexture"), 0);
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("BlurTexture"), 1);
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("GBuffer1"), 2);
m_gaussianBlurShader = ShaderLibrary::Get("DeferredGaussianBlur");
m_gaussianBlurShaderFilterLocation = m_gaussianBlurShader->GetUniformLocation("Filter");
for (unsigned int i = 0; i < 2; ++i)
m_dofTextures[i] = Texture::New();
m_bilinearSampler.SetAnisotropyLevel(1);
m_bilinearSampler.SetFilterMode(SamplerFilter_Bilinear);
m_bilinearSampler.SetWrapMode(SamplerWrap_Clamp);
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_states.depthBuffer = false;
}
DeferredDOFPass::~DeferredDOFPass() = default;
/*!
* \brief Processes the work on the data while working with textures
* \return true
*
* \param sceneData Data for the scene
* \param firstWorkTexture Index of the first texture to work with
* \param firstWorkTexture Index of the second texture to work with
*/
bool DeferredDOFPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const
{
NazaraUnused(sceneData);
Renderer::SetTextureSampler(0, m_pointSampler);
Renderer::SetTextureSampler(1, m_bilinearSampler);
Renderer::SetTextureSampler(2, m_pointSampler);
Renderer::SetTarget(&m_dofRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x/4, m_dimensions.y/4));
Renderer::SetShader(m_gaussianBlurShader);
const unsigned int dofBlurPass = 2;
for (unsigned int i = 0; i < dofBlurPass; ++i)
{
m_dofRTT.SetColorTarget(0); // dofTextureA
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, Vector2f(1.f, 0.f));
Renderer::SetTexture(0, (i == 0) ? m_workTextures[secondWorkTexture] : static_cast<const Texture*>(m_dofTextures[1]));
Renderer::DrawFullscreenQuad();
m_dofRTT.SetColorTarget(1); // dofTextureB
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, Vector2f(0.f, 1.f));
Renderer::SetTexture(0, m_dofTextures[0]);
Renderer::DrawFullscreenQuad();
}
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetShader(m_dofShader);
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::SetTexture(1, m_dofTextures[1]);
Renderer::SetTexture(2, m_GBuffer[1]);
Renderer::DrawFullscreenQuad();
return true;
}
/*!
* \brief Resizes the texture sizes
* \return true If successful
*
* \param dimensions Dimensions for the compute texture
*/
bool DeferredDOFPass::Resize(const Vector2ui& dimensions)
{
DeferredRenderPass::Resize(dimensions);
m_dofRTT.Create(true);
for (unsigned int i = 0; i < 2; ++i)
{
m_dofTextures[i]->Create(ImageType_2D, PixelFormat_RGBA8, dimensions.x/4, dimensions.y/4);
m_dofRTT.AttachTexture(AttachmentPoint_Color, i, m_dofTextures[i]);
}
m_dofRTT.Unlock();
if (!m_dofRTT.IsComplete())
{
NazaraError("Incomplete RTT");
return false;
}
return true;
}
}

View File

@@ -1,61 +0,0 @@
// Copyright (C) 2020 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/DeferredFXAAPass.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTexture.hpp>
#include <Nazara/Renderer/UberShaderInstance.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::DeferredFXAAPass
* \brief Graphics class that represents the pass for FXAA in deferred rendering
*/
/*!
* \brief Constructs a DeferredFXAAPass object by default
*/
DeferredFXAAPass::DeferredFXAAPass()
{
m_fxaaShader = ShaderLibrary::Get("DeferredFXAA");
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_states.depthBuffer = false;
}
DeferredFXAAPass::~DeferredFXAAPass() = default;
/*!
* \brief Processes the work on the data while working with textures
* \return true
*
* \param sceneData Data for the scene
* \param firstWorkTexture Index of the first texture to work with
* \param firstWorkTexture Index of the second texture to work with
*/
bool DeferredFXAAPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const
{
NazaraUnused(sceneData);
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetRenderStates(m_states);
Renderer::SetShader(m_fxaaShader);
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::SetTextureSampler(0, m_pointSampler);
Renderer::DrawFullscreenQuad();
return true;
}
}

View File

@@ -1,81 +0,0 @@
// Copyright (C) 2020 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/DeferredFinalPass.hpp>
#include <Nazara/Core/ParameterList.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/Shader.hpp>
#include <Nazara/Renderer/UberShaderInstance.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::DeferredFinalPass
* \brief Graphics class that represents the final pass in deferred rendering
*/
/*!
* \brief Constructs a DeferredFinalPass object by default
*/
DeferredFinalPass::DeferredFinalPass()
{
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_states.depthBuffer = false;
m_uberShader = UberShaderLibrary::Get("Basic");
ParameterList list;
list.SetParameter("AUTO_TEXCOORDS", true);
list.SetParameter("DIFFUSE_MAPPING", true);
list.SetParameter("TEXTURE_MAPPING", false);
m_uberShaderInstance = m_uberShader->Get(list);
const Shader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_materialDiffuseMapUniform = shader->GetUniformLocation("MaterialDiffuseMap");
}
DeferredFinalPass::~DeferredFinalPass() = default;
/*!
* \brief Processes the work on the data while working with textures
* \return true
*
* \param sceneData Data for the scene
* \param firstWorkTexture Index of the first texture to work with
* \param firstWorkTexture Index of the second texture to work with
*/
bool DeferredFinalPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(firstWorkTexture);
sceneData.viewer->ApplyView();
Renderer::SetRenderStates(m_states);
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::SetTextureSampler(0, m_pointSampler);
m_uberShaderInstance->Activate();
const Shader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, Color::White);
shader->SendInteger(m_materialDiffuseMapUniform, 0);
Renderer::DrawFullscreenQuad();
return false;
}
}

View File

@@ -1,178 +0,0 @@
// Copyright (C) 2020 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/DeferredFogPass.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTexture.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
/*!
* \brief Builds the shader for the fog
* \return Reference to the shader newly created
*/
ShaderRef BuildFogShader()
{
/*const UInt8 fragmentSource[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/FXAA.frag.h>
};*/
const char* fragmentSource =
"#version 140\n"
"out vec4 RenderTarget0;\n"
"uniform sampler2D ColorTexture;\n"
"uniform sampler2D GBuffer2;\n"
"uniform mat4 InvViewProjMatrix;\n"
"uniform vec2 InvTargetSize;\n"
"uniform vec3 EyePosition;\n"
"float n = 0.1;"
"float f = 1000.0;"
"float color_to_float(vec3 color)\n"
"{\n"
"const vec3 byte_to_float = vec3(1.0, 1.0/256, 1.0/(256*256));\n"
"return dot(color, byte_to_float);\n"
"}\n"
"void main()\n"
"{"
"vec2 texCoord = gl_FragCoord.xy * InvTargetSize;\n"
"\t" "vec3 color = texture(ColorTexture, texCoord).xyz;\n"
"vec4 gVec2 = textureLod(GBuffer2, texCoord, 0.0);\n"
"float depth = color_to_float(gVec2.xyz)*2.0 - 1.0;\n"
"float linearDepth = (2 * n) / (f + n - depth * (f - n));"
"vec3 viewSpace = vec3(texCoord*2.0 - 1.0, depth);\n"
"vec4 worldPos = InvViewProjMatrix * vec4(viewSpace, 1.0);\n"
"worldPos.xyz /= worldPos.w;\n"
/*"float lumThreshold = 0.1;"
"float lumMultipler = 2.0;"
//"float lumFactor = max(dot(color, vec3(0.299, 0.587, 0.114)) - lumThreshold, 0.0) / (1.0-lumThreshold);"
"float fogFactor = (1.0 - clamp(worldPos.y-2.0, 0.0, 1.0)) - lumFactor*lumMultipler;"
"fogFactor += (1.0 - clamp(EyePosition.y-2.5, 0.0, 1.0));"
"fogFactor = clamp(fogFactor, 0.0, 1.0);"*/
"float lumThreshold = 0.8;"
"float lumMultipler = 2.0;"
"float luminosity = dot(color, vec3(0.299, 0.587, 0.114));"
"float lumFactor = max(luminosity - lumThreshold, 0.0) / (1.0-lumThreshold);"
"vec4 fogColor = vec4(0.5, 0.5, 0.5, 1.0);\n"
"vec2 fogrange = vec2(0, 50);\n"
"float fogeffect = clamp( 1.0 - (fogrange.y - linearDepth*0.5*f) / (fogrange.y - fogrange.x) , 0.0, 1.0 ) * fogColor.w;\n"
"fogeffect = max(fogeffect-lumFactor, 0.0);"
//fogeffect*=(1.0 - int(depth));
"\t" "vec3 fragmentColor = color*(1.0-fogeffect) + fogColor.rgb * fogeffect;\n"
"\t" "RenderTarget0 = vec4(fragmentColor, 1.0);\n"
"}";
const char* vertexSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"void main()\n"
"{\n"
"\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n"
"}\n";
///TODO: Remplacer ça par des ShaderNode
ShaderRef shader = Shader::New();
if (!shader->Create())
{
NazaraError("Failed to load create shader");
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Fragment, fragmentSource/*String(reinterpret_cast<const char*>(fragmentSource), sizeof(fragmentSource))*/))
{
NazaraError("Failed to load fragment shader");
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Vertex, vertexSource))
{
NazaraError("Failed to load vertex shader");
return nullptr;
}
if (!shader->Link())
{
NazaraError("Failed to link shader");
return nullptr;
}
shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0);
shader->SendInteger(shader->GetUniformLocation("GBuffer2"), 1);
return shader;
}
}
/*!
* \ingroup graphics
* \class Nz::DeferredFogPass
* \brief Graphics class that represents the pass for fog in deferred rendering
*/
/*!
* \brief Constructs a DeferredFogPass object by default
*/
DeferredFogPass::DeferredFogPass()
{
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_shader = BuildFogShader();
m_shaderEyePositionLocation = m_shader->GetUniformLocation("EyePosition");
m_states.depthBuffer = false;
}
DeferredFogPass::~DeferredFogPass() = default;
/*!
* \brief Processes the work on the data while working with textures
* \return true
*
* \param sceneData Data for the scene
* \param firstWorkTexture Index of the first texture to work with
* \param firstWorkTexture Index of the second texture to work with
*/
bool DeferredFogPass::Process( const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetShader(m_shader);
m_shader->SendVector(m_shaderEyePositionLocation, sceneData.viewer->GetEyePosition());
Renderer::SetRenderStates(m_states);
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::SetTexture(1, m_GBuffer[2]);
Renderer::SetTextureSampler(0, m_pointSampler);
Renderer::SetTextureSampler(1, m_pointSampler);
Renderer::DrawFullscreenQuad();
return true;
}
}

View File

@@ -1,67 +0,0 @@
// Copyright (C) 2020 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/DeferredForwardPass.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Graphics/AbstractBackground.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Graphics/DeferredRenderTechnique.hpp>
#include <Nazara/Graphics/ForwardRenderTechnique.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::DeferredForwardPass
* \brief Graphics class that represents the forward pass in deferred rendering
*/
DeferredForwardPass::DeferredForwardPass() = default;
DeferredForwardPass::~DeferredForwardPass() = default;
/*!
* \brief Initializes the deferred forward pass which needs the forward technique
*
* \param technique Rendering technique
*/
void DeferredForwardPass::Initialize(DeferredRenderTechnique* technique)
{
DeferredRenderPass::Initialize(technique);
m_forwardTechnique = technique->GetForwardTechnique();
}
/*!
* \brief Processes the work on the data while working with textures
* \return true
*
* \param sceneData Data for the scene
* \param firstWorkTexture Index of the first texture to work with
* \param firstWorkTexture Index of the second texture to work with
*/
bool DeferredForwardPass::Process(const SceneData& sceneData, unsigned int workTexture, unsigned int sceneTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(workTexture);
m_workRTT->SetColorTarget(sceneTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
if (sceneData.background)
sceneData.background->Draw(sceneData.viewer);
Renderer::SetMatrix(MatrixType_Projection, sceneData.viewer->GetProjectionMatrix());
Renderer::SetMatrix(MatrixType_View, sceneData.viewer->GetViewMatrix());
m_forwardTechnique->Draw(sceneData);
return false;
}
}

View File

@@ -1,716 +0,0 @@
// Copyright (C) 2020 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/DeferredGeometryPass.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/OffsetOf.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/DeferredRenderTechnique.hpp>
#include <Nazara/Graphics/DeferredProxyRenderQueue.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTexture.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
struct BillboardPoint
{
Color color;
Vector3f position;
Vector2f size;
Vector2f sinCos; // must follow `size` (both will be sent as a Vector4f)
Vector2f uv;
};
constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB
constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV);
}
/*!
* \ingroup graphics
* \class Nz::DeferredGeometryPass
* \brief Graphics class that represents the pass for geometries in deferred rendering
*/
/*!
* \brief Constructs a DeferredGeometryPass object by default
*/
DeferredGeometryPass::DeferredGeometryPass() :
m_vertexBuffer(BufferType_Vertex)
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
m_whiteTexture = Nz::TextureLibrary::Get("White2D");
m_vertexBuffer.Create(s_vertexBufferSize, DataStorage_Hardware, BufferUsage_Dynamic);
m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer);
m_spriteBuffer.Reset(VertexDeclaration::Get(VertexLayout_XYZ_Color_UV), &m_vertexBuffer);
m_clearShader = ShaderLibrary::Get("DeferredGBufferClear");
m_clearStates.depthBuffer = true;
m_clearStates.faceCulling = true;
m_clearStates.stencilTest = true;
m_clearStates.depthFunc = RendererComparison_Always;
m_clearStates.stencilCompare.front = RendererComparison_Always;
m_clearStates.stencilPass.front = StencilOperation_Zero;
}
DeferredGeometryPass::~DeferredGeometryPass() = default;
/*!
* \brief Processes the work on the data while working with textures
* \return false
*
* \param sceneData Data for the scene
* \param firstWorkTexture Index of the first texture to work with
* \param firstWorkTexture Index of the second texture to work with
*/
bool DeferredGeometryPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(firstWorkTexture);
NazaraUnused(secondWorkTexture);
bool instancingEnabled = m_deferredTechnique->IsInstancingEnabled();
m_GBufferRTT->SetColorTargets({0, 1, 2}); // G-Buffer
Renderer::SetTarget(m_GBufferRTT);
Renderer::SetScissorRect(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetRenderStates(m_clearStates);
Renderer::SetShader(m_clearShader);
Renderer::DrawFullscreenQuad();
Renderer::SetMatrix(MatrixType_Projection, sceneData.viewer->GetProjectionMatrix());
Renderer::SetMatrix(MatrixType_View, sceneData.viewer->GetViewMatrix());
BasicRenderQueue& renderQueue = *m_renderQueue->GetDeferredRenderQueue();
renderQueue.Sort(sceneData.viewer);
if (!renderQueue.models.empty())
DrawModels(sceneData, renderQueue, renderQueue.models);
if (!renderQueue.basicSprites.empty())
DrawSprites(sceneData, renderQueue, renderQueue.basicSprites);
if (!renderQueue.billboards.empty())
DrawBillboards(sceneData, renderQueue, renderQueue.billboards);
if (!renderQueue.depthSortedModels.empty())
DrawModels(sceneData, renderQueue, renderQueue.depthSortedModels);
if (!renderQueue.depthSortedSprites.empty())
DrawSprites(sceneData, renderQueue, renderQueue.depthSortedSprites);
if (!renderQueue.depthSortedBillboards.empty())
DrawBillboards(sceneData, renderQueue, renderQueue.depthSortedBillboards);
return false; // We only fill the G-Buffer, the work texture are unchanged
}
/*!
* \brief Resizes the texture sizes
* \return true If successful
*
* \param dimensions Dimensions for the compute texture
*/
bool DeferredGeometryPass::Resize(const Vector2ui& dimensions)
{
DeferredRenderPass::Resize(dimensions);
/*
G-Buffer:
Texture0: Diffuse Color + Flags
Texture1: Encoded normal
Texture2: Specular value + Shininess
Texture3: N/A
*/
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
unsigned int width = dimensions.x;
unsigned int height = dimensions.y;
m_depthStencilTexture->Create(ImageType_2D, PixelFormat_Depth24Stencil8, width, height);
m_GBuffer[0]->Create(ImageType_2D, PixelFormat_RGBA8, width, height); // Texture 0 : Diffuse Color + Specular
m_GBuffer[1]->Create(ImageType_2D, PixelFormat_RG16F, width, height); // Texture 1 : Encoded normal
m_GBuffer[2]->Create(ImageType_2D, PixelFormat_RGBA8, width, height); // Texture 2 : Depth (24bits) + Shininess
m_GBufferRTT->Create(true);
m_GBufferRTT->AttachTexture(AttachmentPoint_Color, 0, m_GBuffer[0]);
m_GBufferRTT->AttachTexture(AttachmentPoint_Color, 1, m_GBuffer[1]);
m_GBufferRTT->AttachTexture(AttachmentPoint_Color, 2, m_GBuffer[2]);
// Texture 3 : Emission map ?
m_GBufferRTT->AttachTexture(AttachmentPoint_DepthStencil, 0, m_depthStencilTexture);
m_GBufferRTT->Unlock();
m_workRTT->Create(true);
for (unsigned int i = 0; i < 2; ++i)
{
m_workTextures[i]->Create(ImageType_2D, PixelFormat_RGBA8, width, height);
m_workRTT->AttachTexture(AttachmentPoint_Color, i, m_workTextures[i]);
}
m_workRTT->AttachTexture(AttachmentPoint_DepthStencil, 0, m_depthStencilTexture);
m_workRTT->Unlock();
if (!m_workRTT->IsComplete() || !m_GBufferRTT->IsComplete())
{
NazaraError("Incomplete RTT");
return false;
}
return true;
}
catch (const std::exception& e)
{
NazaraError("Failed to create G-Buffer RTT: " + String(e.what()));
return false;
}
}
void DeferredGeometryPass::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::Billboard>& billboards) const
{
VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer();
instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration);
Renderer::SetVertexBuffer(&s_quadVertexBuffer);
Nz::BufferMapper<VertexBuffer> instanceBufferMapper;
std::size_t billboardCount = 0;
std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount();
auto Commit = [&]()
{
if (billboardCount > 0)
{
instanceBufferMapper.Unmap();
Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4);
billboardCount = 0;
}
};
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
for (const BasicRenderQueue::Billboard& billboard : billboards)
{
const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect;
if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
Commit();
const MaterialPipeline* pipeline = billboard.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Deferred | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != billboard.material)
{
billboard.material->Apply(*pipelineInstance);
lastMaterial = billboard.material;
}
if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
if (!instanceBufferMapper.GetBuffer())
instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite);
std::memcpy(static_cast<Nz::UInt8*>(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, &billboard.data, sizeof(BasicRenderQueue::BillboardData));
if (++billboardCount >= maxBillboardPerDraw)
Commit();
}
Commit();
}
void DeferredGeometryPass::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::BillboardChain>& billboards) const
{
VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer();
instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration);
Renderer::SetVertexBuffer(&s_quadVertexBuffer);
Nz::BufferMapper<VertexBuffer> instanceBufferMapper;
std::size_t billboardCount = 0;
std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount();
auto Commit = [&]()
{
if (billboardCount > 0)
{
instanceBufferMapper.Unmap();
Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4);
billboardCount = 0;
}
};
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
for (const BasicRenderQueue::BillboardChain& billboard : billboards)
{
const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect;
if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
Commit();
const MaterialPipeline* pipeline = billboard.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Deferred | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != billboard.material)
{
billboard.material->Apply(*pipelineInstance);
lastMaterial = billboard.material;
}
if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
std::size_t billboardRemaining = billboard.billboardCount;
const BasicRenderQueue::BillboardData* billboardData = renderQueue.GetBillboardData(billboard.billboardIndex);
do
{
std::size_t renderedBillboardCount = std::min(billboardRemaining, maxBillboardPerDraw - billboardCount);
billboardRemaining -= renderedBillboardCount;
if (!instanceBufferMapper.GetBuffer())
instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite);
std::memcpy(static_cast<Nz::UInt8*>(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, billboardData, renderedBillboardCount * sizeof(BasicRenderQueue::BillboardData));
billboardCount += renderedBillboardCount;
billboardData += renderedBillboardCount;
if (billboardCount >= maxBillboardPerDraw)
Commit();
}
while (billboardRemaining > 0);
}
Commit();
}
void DeferredGeometryPass::DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const Nz::RenderQueue<Nz::BasicRenderQueue::Model>& models) const
{
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
///TODO: Reimplement instancing
for (const BasicRenderQueue::Model& model : models)
{
const MaterialPipeline* pipeline = model.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &model.material->GetPipeline()->Apply(ShaderFlags_Deferred);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != model.material)
{
model.material->Apply(*pipelineInstance);
lastMaterial = model.material;
}
if (model.material->IsScissorTestEnabled())
{
const Nz::Recti& scissorRect = (model.scissorRect.width > 0) ? model.scissorRect : fullscreenScissorRect;
if (scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
// Handle draw call before rendering loop
Renderer::DrawCall drawFunc;
Renderer::DrawCallInstanced instancedDrawFunc;
unsigned int indexCount;
if (model.meshData.indexBuffer)
{
drawFunc = Renderer::DrawIndexedPrimitives;
instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced;
indexCount = model.meshData.indexBuffer->GetIndexCount();
}
else
{
drawFunc = Renderer::DrawPrimitives;
instancedDrawFunc = Renderer::DrawPrimitivesInstanced;
indexCount = model.meshData.vertexBuffer->GetVertexCount();
}
Renderer::SetIndexBuffer(model.meshData.indexBuffer);
Renderer::SetVertexBuffer(model.meshData.vertexBuffer);
Renderer::SetMatrix(MatrixType_World, model.matrix);
drawFunc(model.meshData.primitiveMode, 0, indexCount);
}
}
void DeferredGeometryPass::DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::SpriteChain>& spriteList) const
{
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4);
const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
auto Draw = [&]()
{
unsigned int firstIndex = 0;
for (const auto& batch : m_spriteBatches)
{
const MaterialPipeline* pipeline = batch.material->GetPipeline();
if (pipeline != lastPipeline)
{
pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
// Overlay texture unit
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
lastShader = shader;
}
lastPipeline = pipeline;
}
if (batch.material != lastMaterial)
{
batch.material->Apply(*pipelineInstance);
Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler());
lastMaterial = batch.material;
}
if (batch.overlayTexture != lastOverlay)
{
Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture);
lastOverlay = batch.overlayTexture;
}
if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(batch.scissorRect);
lastScissorRect = batch.scissorRect;
}
unsigned int indexCount = batch.spriteCount * 6;
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount);
firstIndex += indexCount;
}
};
m_spriteBatches.clear();
{
BufferMapper<VertexBuffer> vertexMapper;
VertexStruct_XYZ_Color_UV* vertices = nullptr;
std::size_t remainingSprite = maxSpriteCount;
const Material* lastMaterial = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList)
{
const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get();
const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect;
const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices;
std::size_t spriteCount = basicSprites.spriteCount;
for (;;)
{
if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
m_spriteBatches.emplace_back();
SpriteBatch& newBatch = m_spriteBatches.back();
newBatch.material = basicSprites.material;
newBatch.overlayTexture = overlayTexture;
newBatch.scissorRect = scissorRect;
newBatch.spriteCount = 0;
lastMaterial = basicSprites.material;
lastOverlay = overlayTexture;
lastScissorRect = scissorRect;
}
SpriteBatch& currentBatch = m_spriteBatches.back();
if (!vertices)
{
vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite);
vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
}
std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount);
std::size_t processedVertices = processedSpriteCount * 4;
std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV));
vertices += processedVertices;
spriteVertices += processedVertices;
currentBatch.spriteCount += processedSpriteCount;
spriteCount -= processedSpriteCount;
remainingSprite -= processedSpriteCount;
if (remainingSprite == 0)
{
vertexMapper.Unmap();
vertices = nullptr;
Draw();
remainingSprite = maxSpriteCount;
m_spriteBatches.clear();
}
if (spriteCount == 0)
break;
}
}
}
Draw();
}
const DeferredGeometryPass::ShaderUniforms* DeferredGeometryPass::GetShaderUniforms(const Shader* shader) const
{
auto it = m_shaderUniforms.find(shader);
if (it == m_shaderUniforms.end())
{
ShaderUniforms uniforms;
uniforms.shaderReleaseSlot.Connect(shader->OnShaderRelease, this, &DeferredGeometryPass::OnShaderInvalidated);
uniforms.shaderUniformInvalidatedSlot.Connect(shader->OnShaderUniformInvalidated, this, &DeferredGeometryPass::OnShaderInvalidated);
uniforms.eyePosition = shader->GetUniformLocation("EyePosition");
uniforms.sceneAmbient = shader->GetUniformLocation("SceneAmbient");
uniforms.textureOverlay = shader->GetUniformLocation("TextureOverlay");
it = m_shaderUniforms.emplace(shader, std::move(uniforms)).first;
}
return &it->second;
}
/*!
* \brief Handle the invalidation of a shader
*
* \param shader Shader being invalidated
*/
void DeferredGeometryPass::OnShaderInvalidated(const Shader* shader) const
{
m_shaderUniforms.erase(shader);
}
bool DeferredGeometryPass::Initialize()
{
try
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0);
BufferMapper<IndexBuffer> mapper(s_quadIndexBuffer, BufferAccess_WriteOnly);
UInt32* indices = static_cast<UInt32*>(mapper.GetPointer());
for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i)
{
*indices++ = i * 4 + 0;
*indices++ = i * 4 + 2;
*indices++ = i * 4 + 1;
*indices++ = i * 4 + 2;
*indices++ = i * 4 + 3;
*indices++ = i * 4 + 1;
}
mapper.Unmap(); // No point to keep the buffer open any longer
// Quad buffer (used for instancing of billboards and sprites)
//Note: UV are computed in the shader
s_quadVertexBuffer.Reset(VertexDeclaration::Get(VertexLayout_XY), 4, DataStorage_Hardware, 0);
float vertices[2 * 4] = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
0.5f, 0.5f,
};
s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices));
// Declaration used when rendering the vertex billboards
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(BillboardPoint, color));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(BillboardPoint, position));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(BillboardPoint, uv));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Userdata0, ComponentType_Float4, NazaraOffsetOf(BillboardPoint, size)); // Includes sincos
// Declaration used when rendering the billboards with intancing
// The main advantage is the direct copy (std::memcpy) of data in the RenderQueue to the GPU buffer
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(BasicRenderQueue::BillboardData, center));
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(BasicRenderQueue::BillboardData, size)); // Englobe sincos
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(BasicRenderQueue::BillboardData, color));
}
catch (const std::exception& e)
{
NazaraError("Failed to initialise: " + String(e.what()));
return false;
}
return true;
}
void DeferredGeometryPass::Uninitialize()
{
s_quadIndexBuffer.Reset();
s_quadVertexBuffer.Reset();
}
IndexBuffer DeferredGeometryPass::s_quadIndexBuffer;
VertexBuffer DeferredGeometryPass::s_quadVertexBuffer;
VertexDeclaration DeferredGeometryPass::s_billboardInstanceDeclaration;
VertexDeclaration DeferredGeometryPass::s_billboardVertexDeclaration;
}

View File

@@ -1,327 +0,0 @@
// Copyright (C) 2020 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/DeferredPhongLightingPass.hpp>
#include <Nazara/Core/Primitive.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/DeferredProxyRenderQueue.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTexture.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::DeferredPhongLightingPass
* \brief Graphics class that represents the pass for phong lighting in deferred rendering
*/
/*!
* \brief Constructs a DeferredPhongLightingPass object by default
*/
DeferredPhongLightingPass::DeferredPhongLightingPass() :
m_lightMeshesDrawing(false)
{
m_directionalLightShader = ShaderLibrary::Get("DeferredDirectionnalLight");
m_directionalLightShaderEyePositionLocation = m_directionalLightShader->GetUniformLocation("EyePosition");
m_directionalLightShaderSceneAmbientLocation = m_directionalLightShader->GetUniformLocation("SceneAmbient");
m_directionalLightUniforms.ubo = false;
m_directionalLightUniforms.locations.type = -1; // Type already known
m_directionalLightUniforms.locations.color = m_directionalLightShader->GetUniformLocation("LightColor");
m_directionalLightUniforms.locations.factors = m_directionalLightShader->GetUniformLocation("LightFactors");
m_directionalLightUniforms.locations.parameters1 = m_directionalLightShader->GetUniformLocation("LightDirection");
m_directionalLightUniforms.locations.parameters2 = -1;
m_directionalLightUniforms.locations.parameters3 = -1;
m_pointSpotLightShader = ShaderLibrary::Get("DeferredPointSpotLight");
m_pointSpotLightShaderDiscardLocation = m_pointSpotLightShader->GetUniformLocation("Discard");
m_pointSpotLightShaderEyePositionLocation = m_pointSpotLightShader->GetUniformLocation("EyePosition");
m_pointSpotLightShaderSceneAmbientLocation = m_pointSpotLightShader->GetUniformLocation("SceneAmbient");
m_pointSpotLightUniforms.ubo = false;
m_pointSpotLightUniforms.locations.type = m_pointSpotLightShader->GetUniformLocation("LightType");
m_pointSpotLightUniforms.locations.color = m_pointSpotLightShader->GetUniformLocation("LightColor");
m_pointSpotLightUniforms.locations.factors = m_pointSpotLightShader->GetUniformLocation("LightFactors");
m_pointSpotLightUniforms.locations.parameters1 = m_pointSpotLightShader->GetUniformLocation("LightParameters1");
m_pointSpotLightUniforms.locations.parameters2 = m_pointSpotLightShader->GetUniformLocation("LightParameters2");
m_pointSpotLightUniforms.locations.parameters3 = m_pointSpotLightShader->GetUniformLocation("LightParameters3");
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_cone = Mesh::New();
m_cone->CreateStatic();
m_coneMesh = static_cast<StaticMesh*>(m_cone->BuildSubMesh(Primitive::Cone(1.f, 1.f, 16, Matrix4f::Rotate(EulerAnglesf(90.f, 0.f, 0.f)))));
m_sphere = Mesh::New();
m_sphere->CreateStatic();
m_sphereMesh = static_cast<StaticMesh*>(m_sphere->BuildSubMesh(Primitive::IcoSphere(1.f, 1)));
}
DeferredPhongLightingPass::~DeferredPhongLightingPass() = default;
/*!
* \brief Enables the drawing of meshes with light
*
* \param enable Should meshes with light parameter be drawed
*/
void DeferredPhongLightingPass::EnableLightMeshesDrawing(bool enable)
{
m_lightMeshesDrawing = enable;
}
/*!
* \brief Checks whether the drawing of meshes with light is enabled
* \return true If it is the case
*/
bool DeferredPhongLightingPass::IsLightMeshesDrawingEnabled() const
{
return m_lightMeshesDrawing;
}
/*!
* \brief Processes the work on the data while working with textures
* \return true
*
* \param sceneData Data for the scene
* \param firstWorkTexture Index of the first texture to work with
* \param firstWorkTexture Index of the second texture to work with
*/
bool DeferredPhongLightingPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(secondWorkTexture);
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetTexture(0, m_GBuffer[0]);
Renderer::SetTextureSampler(0, m_pointSampler);
Renderer::SetTexture(1, m_GBuffer[1]);
Renderer::SetTextureSampler(1, m_pointSampler);
Renderer::SetTexture(2, m_GBuffer[2]);
Renderer::SetTextureSampler(2, m_pointSampler);
Renderer::SetTexture(3, m_depthStencilTexture);
Renderer::SetTextureSampler(3, m_pointSampler);
Renderer::SetClearColor(Color::Black);
Renderer::Clear(RendererBuffer_Color);
RenderStates lightStates;
lightStates.dstBlend = BlendFunc_One;
lightStates.srcBlend = BlendFunc_One;
lightStates.blending = true;
lightStates.depthBuffer = false;
lightStates.depthWrite = false;
// Directional lights
if (!m_renderQueue->directionalLights.empty())
{
Renderer::SetRenderStates(lightStates);
Renderer::SetShader(m_directionalLightShader);
m_directionalLightShader->SendColor(m_directionalLightShaderSceneAmbientLocation, sceneData.ambientColor);
m_directionalLightShader->SendVector(m_directionalLightShaderEyePositionLocation, sceneData.viewer->GetEyePosition());
for (auto& light : m_renderQueue->directionalLights)
{
m_directionalLightShader->SendColor(m_directionalLightUniforms.locations.color, light.color);
m_directionalLightShader->SendVector(m_directionalLightUniforms.locations.factors, Vector2f(light.ambientFactor, light.diffuseFactor));
m_directionalLightShader->SendVector(m_directionalLightUniforms.locations.parameters1, Vector4f(light.direction));
Renderer::DrawFullscreenQuad();
}
}
// Point lights/Spot lights
if (!m_renderQueue->pointLights.empty() || !m_renderQueue->spotLights.empty())
{
// http://www.altdevblogaday.com/2011/08/08/stencil-buffer-optimisation-for-deferred-lights/
lightStates.cullingSide = FaceSide_Front;
lightStates.stencilTest = true;
lightStates.stencilDepthFail.back = StencilOperation_Invert;
lightStates.stencilDepthFail.front = StencilOperation_Invert;
lightStates.stencilFail.back = StencilOperation_Keep;
lightStates.stencilFail.front = StencilOperation_Keep;
lightStates.stencilPass.back = StencilOperation_Keep;
lightStates.stencilPass.front = StencilOperation_Keep;
lightStates.stencilReference.back = 0;
lightStates.stencilReference.front = 0;
lightStates.stencilWriteMask.back = 0xFF;
lightStates.stencilWriteMask.front = 0xFF;
Renderer::SetRenderStates(lightStates);
Renderer::SetShader(m_pointSpotLightShader);
m_pointSpotLightShader->SendColor(m_pointSpotLightShaderSceneAmbientLocation, sceneData.ambientColor);
m_pointSpotLightShader->SendVector(m_pointSpotLightShaderEyePositionLocation, sceneData.viewer->GetEyePosition());
Matrix4f lightMatrix;
lightMatrix.MakeIdentity();
if (!m_renderQueue->pointLights.empty())
{
const IndexBuffer* indexBuffer = m_sphereMesh->GetIndexBuffer();
Renderer::SetIndexBuffer(indexBuffer);
Renderer::SetVertexBuffer(m_sphereMesh->GetVertexBuffer());
m_pointSpotLightShader->SendInteger(m_pointSpotLightUniforms.locations.type, LightType_Point);
for (const auto& light : m_renderQueue->pointLights)
{
m_pointSpotLightShader->SendColor(m_pointSpotLightUniforms.locations.color, light.color);
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.factors, Vector2f(light.ambientFactor, light.diffuseFactor));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters1, Vector4f(light.position, light.attenuation));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters2, Vector4f(0.f, 0.f, 0.f, light.invRadius));
lightMatrix.SetScale(Vector3f(light.radius * 1.1f)); // To correct imperfections due to the sphere
lightMatrix.SetTranslation(light.position);
Renderer::SetMatrix(MatrixType_World, lightMatrix);
// Sphere rendering in the stencil buffer
Renderer::Enable(RendererParameter_ColorWrite, false);
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_FaceCulling, false);
Renderer::SetStencilCompareFunction(RendererComparison_Always);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, true);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
// Sphere rendering as effect zone
Renderer::Enable(RendererParameter_ColorWrite, true);
Renderer::Enable(RendererParameter_DepthBuffer, false);
Renderer::Enable(RendererParameter_FaceCulling, true);
Renderer::SetStencilCompareFunction(RendererComparison_NotEqual, FaceSide_Back);
Renderer::SetStencilPassOperation(StencilOperation_Zero, FaceSide_Back);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, false);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
}
if (m_lightMeshesDrawing)
{
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_DepthWrite, true);
Renderer::Enable(RendererParameter_FaceCulling, false);
Renderer::Enable(RendererParameter_StencilTest, false);
Renderer::SetFaceFilling(FaceFilling_Line);
const Shader* shader = ShaderLibrary::Get("DebugSimple");
static int colorLocation = shader->GetUniformLocation("Color");
Renderer::SetShader(shader);
for (const auto& light : m_renderQueue->pointLights)
{
lightMatrix.SetScale(Vector3f(light.radius * 1.1f)); // To correct imperfections due to the sphere
lightMatrix.SetTranslation(light.position);
Renderer::SetMatrix(MatrixType_World, lightMatrix);
shader->SendColor(colorLocation, light.color);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
}
Renderer::Enable(RendererParameter_DepthBuffer, false);
Renderer::Enable(RendererParameter_DepthWrite, false);
Renderer::Enable(RendererParameter_FaceCulling, true);
Renderer::Enable(RendererParameter_StencilTest, true);
Renderer::SetFaceFilling(FaceFilling_Fill);
}
}
if (!m_renderQueue->spotLights.empty())
{
const IndexBuffer* indexBuffer = m_coneMesh->GetIndexBuffer();
Renderer::SetIndexBuffer(indexBuffer);
Renderer::SetVertexBuffer(m_coneMesh->GetVertexBuffer());
m_pointSpotLightShader->SendInteger(m_pointSpotLightUniforms.locations.type, LightType_Spot);
for (const auto& light : m_renderQueue->spotLights)
{
m_pointSpotLightShader->SendColor(m_pointSpotLightUniforms.locations.color, light.color);
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.factors, Vector2f(light.ambientFactor, light.diffuseFactor));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters1, Vector4f(light.position, light.attenuation));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters2, Vector4f(light.direction, light.invRadius));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters3, Vector2f(light.innerAngleCosine, light.outerAngleCosine));
float baseRadius = light.radius * light.outerAngleTangent * 1.1f;
lightMatrix.MakeTransform(light.position, Quaternionf::RotationBetween(Vector3f::Forward(), light.direction), Vector3f(baseRadius, baseRadius, light.radius));
Renderer::SetMatrix(MatrixType_World, lightMatrix);
// Sphere rendering in the stencil buffer
Renderer::Enable(RendererParameter_ColorWrite, false);
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_FaceCulling, false);
Renderer::SetStencilCompareFunction(RendererComparison_Always);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, true);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
// Sphere rendering as effect zone
Renderer::Enable(RendererParameter_ColorWrite, true);
Renderer::Enable(RendererParameter_DepthBuffer, false);
Renderer::Enable(RendererParameter_FaceCulling, true);
Renderer::SetFaceCulling(FaceSide_Front);
Renderer::SetStencilCompareFunction(RendererComparison_NotEqual, FaceSide_Back);
Renderer::SetStencilPassOperation(StencilOperation_Zero, FaceSide_Back);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, false);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
}
if (m_lightMeshesDrawing)
{
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_DepthWrite, true);
Renderer::Enable(RendererParameter_FaceCulling, false);
Renderer::Enable(RendererParameter_StencilTest, false);
Renderer::SetFaceFilling(FaceFilling_Line);
const Shader* shader = ShaderLibrary::Get("DebugSimple");
static int colorLocation = shader->GetUniformLocation("Color");
Renderer::SetShader(shader);
for (const auto& light : m_renderQueue->spotLights)
{
float baseRadius = light.radius * light.outerAngleTangent * 1.1f;
lightMatrix.MakeTransform(light.position, Quaternionf::RotationBetween(Vector3f::Forward(), light.direction), Vector3f(baseRadius, baseRadius, light.radius));
Renderer::SetMatrix(MatrixType_World, lightMatrix);
shader->SendColor(colorLocation, light.color);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
}
Renderer::Enable(RendererParameter_DepthBuffer, false);
Renderer::Enable(RendererParameter_DepthWrite, false);
Renderer::Enable(RendererParameter_FaceCulling, true);
Renderer::Enable(RendererParameter_StencilTest, true);
Renderer::SetFaceFilling(FaceFilling_Fill);
}
}
Renderer::Enable(RendererParameter_StencilTest, false);
}
return true;
}
}

View File

@@ -1,261 +0,0 @@
// Copyright (C) 2020 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/DeferredProxyRenderQueue.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/BasicRenderQueue.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::DeferredProxyRenderQueue
* \brief Graphics class sorting the objects into a deferred and forward render queue (depending on blending)
*/
/*!
* \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
*/
void DeferredProxyRenderQueue::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");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr);
else
m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr);
}
/*!
* \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
*/
void DeferredProxyRenderQueue::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");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr);
else
m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr);
}
/*!
* \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
*/
void DeferredProxyRenderQueue::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");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr);
else
m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr);
}
/*!
* \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
*/
void DeferredProxyRenderQueue::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");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr);
else
m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr);
}
/*!
* \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
*/
void DeferredProxyRenderQueue::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");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr);
else
m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr);
}
/*!
* \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
*/
void DeferredProxyRenderQueue::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");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr);
else
m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr);
}
/*!
* \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
*/
void DeferredProxyRenderQueue::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");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr);
else
m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr);
}
/*!
* \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
*/
void DeferredProxyRenderQueue::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");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr);
else
m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr);
}
/*!
* \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 DeferredProxyRenderQueue::AddDrawable(int renderOrder, const Drawable* drawable)
{
m_forwardRenderQueue->AddDrawable(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
*/
void DeferredProxyRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect)
{
NazaraAssert(material, "Invalid material");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix, scissorRect);
else
m_forwardRenderQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix, scissorRect);
}
/*!
* \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
*/
void DeferredProxyRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay)
{
NazaraAssert(material, "Invalid material");
if (!material->IsBlendingEnabled())
m_deferredRenderQueue->AddSprites(renderOrder, material, vertices, spriteCount, scissorRect, overlay);
else
m_forwardRenderQueue->AddSprites(renderOrder, material, vertices, spriteCount, scissorRect, overlay);
}
/*!
* \brief Clears the queue
*
* \param fully Should everything be cleared or we can keep layers
*/
void DeferredProxyRenderQueue::Clear(bool fully)
{
AbstractRenderQueue::Clear(fully);
m_deferredRenderQueue->Clear(fully);
m_forwardRenderQueue->Clear(fully);
}
}

View File

@@ -1,86 +0,0 @@
// Copyright (C) 2020 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/DeferredRenderPass.hpp>
#include <Nazara/Graphics/DeferredProxyRenderQueue.hpp>
#include <Nazara/Graphics/DeferredRenderTechnique.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::DeferredRenderPass
* \brief Graphics class that represents the pass for rendering in deferred rendering
*/
/*!
* \brief Constructs a DeferredRenderPass object by default
*/
DeferredRenderPass::DeferredRenderPass() :
m_enabled(true)
{
}
DeferredRenderPass::~DeferredRenderPass() = default;
/*!
* \brief Enables the deferred rendering
*
* \param enable Should deferred rendering be activated
*/
void DeferredRenderPass::Enable(bool enable)
{
m_enabled = enable;
}
/*!
* \brief Initializes the deferred forward pass which needs the deferred technique
*
* \param technique Rendering technique
*/
void DeferredRenderPass::Initialize(DeferredRenderTechnique* technique)
{
m_deferredTechnique = technique;
m_renderQueue = static_cast<DeferredProxyRenderQueue*>(technique->GetRenderQueue());
m_depthStencilTexture = technique->GetDepthStencilTexture();
m_GBufferRTT = technique->GetGBufferRTT();
for (unsigned int i = 0; i < 3; ++i)
m_GBuffer[i] = technique->GetGBuffer(i);
m_workRTT = technique->GetWorkRTT();
for (unsigned int i = 0; i < 2; ++i)
m_workTextures[i] = technique->GetWorkTexture(i);
}
/*!
* \brief Checks whether the deferred rendering is enabled
* \return true If it the case
*/
bool DeferredRenderPass::IsEnabled() const
{
return m_enabled;
}
/*!
* \brief Resizes the texture sizes
* \return true If successful
*
* \param dimensions Dimensions for the compute texture
*/
bool DeferredRenderPass::Resize(const Vector2ui& dimensions)
{
m_dimensions = dimensions;
return true;
}
}

View File

@@ -1,742 +0,0 @@
// Copyright (C) 2020 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
#ifndef NAZARA_RENDERER_OPENGL
#define NAZARA_RENDERER_OPENGL // Nécessaire pour inclure les headers OpenGL
#endif
#include <Nazara/Graphics/DeferredRenderTechnique.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/DeferredBloomPass.hpp>
#include <Nazara/Graphics/DeferredDOFPass.hpp>
#include <Nazara/Graphics/DeferredFinalPass.hpp>
#include <Nazara/Graphics/DeferredFogPass.hpp>
#include <Nazara/Graphics/DeferredForwardPass.hpp>
#include <Nazara/Graphics/DeferredFXAAPass.hpp>
#include <Nazara/Graphics/DeferredGeometryPass.hpp>
#include <Nazara/Graphics/DeferredPhongLightingPass.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/Shader.hpp>
#include <Nazara/Renderer/ShaderStage.hpp>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
const UInt8 r_fragmentSource_BloomBright[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/BloomBright.frag.h>
};
const UInt8 r_fragmentSource_BloomFinal[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/BloomFinal.frag.h>
};
const UInt8 r_fragmentSource_DirectionalLight[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/DirectionalLight.frag.h>
};
const UInt8 r_fragmentSource_FXAA[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/FXAA.frag.h>
};
const UInt8 r_fragmentSource_GBufferClear[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/GBufferClear.frag.h>
};
const UInt8 r_fragmentSource_GaussianBlur[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/GaussianBlur.frag.h>
};
const UInt8 r_fragmentSource_PointSpotLight[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/PointSpotLight.frag.h>
};
unsigned int RenderPassPriority[] =
{
6, // RenderPassType_AA
4, // RenderPassType_Bloom
7, // RenderPassType_DOF
0xFF, // RenderPassType_Final
5, // RenderPassType_Fog
2, // RenderPassType_Forward
1, // RenderPassType_Lighting
0, // RenderPassType_Geometry
3, // RenderPassType_SSAO
};
static_assert(sizeof(RenderPassPriority) / sizeof(unsigned int) == RenderPassType_Max + 1, "Render pass priority array is incomplete");
/*!
* \brief Registers the deferred shader
* \return Reference to the newly created shader
*
* \param name Name of the shader
* \param fragmentSource Raw data to fragment shader
* \param fragmentSourceLength Size of the fragment source
* \param vertexStage Stage of the shader
* \param err Pointer to string to contain error message
*/
inline ShaderRef RegisterDeferredShader(const String& name, const UInt8* fragmentSource, unsigned int fragmentSourceLength, const ShaderStage& vertexStage, String* err)
{
ErrorFlags errFlags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled);
ShaderRef shader = Shader::New();
if (!shader->Create())
{
err->Set("Failed to create shader: " + Error::GetLastError());
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Fragment, reinterpret_cast<const char*>(fragmentSource), fragmentSourceLength))
{
err->Set("Failed to attach fragment stage: " + Error::GetLastError());
return nullptr;
}
shader->AttachStage(ShaderStageType_Vertex, vertexStage);
if (!shader->Link())
{
err->Set("Failed to link shader: " + Error::GetLastError());
return nullptr;
}
ShaderLibrary::Register(name, shader);
return shader;
}
}
/*!
* \ingroup graphics
* \class Nz::DeferredRenderTechnique
* \brief Graphics class that represents the technique used in deferred rendering
*/
/*!
* \brief Constructs a DeferredRenderTechnique object by default
*
* \remark Produces a NazaraError if one pass could not be created
*/
DeferredRenderTechnique::DeferredRenderTechnique() :
m_renderQueue(&m_deferredRenderQueue, static_cast<BasicRenderQueue*>(m_forwardTechnique.GetRenderQueue())),
m_GBufferSize(0U)
{
m_depthStencilTexture = Texture::New();
for (unsigned int i = 0; i < 2; ++i)
m_workTextures[i] = Texture::New();
for (unsigned int i = 0; i < 3; ++i)
m_GBuffer[i] = Texture::New();
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
ResetPass(RenderPassType_Final, 0);
ResetPass(RenderPassType_Geometry, 0);
ResetPass(RenderPassType_Lighting, 0);
}
catch (const std::exception& e)
{
ErrorFlags errFlags(ErrorFlag_ThrowExceptionDisabled);
NazaraError("Failed to add geometry and/or phong lighting pass: " + String(e.what()));
throw;
}
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
ResetPass(RenderPassType_AA, 0);
}
catch (const std::exception& e)
{
NazaraWarning("Failed to add FXAA pass: " + String(e.what()));
}
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
ResetPass(RenderPassType_Bloom, 0);
}
catch (const std::exception& e)
{
NazaraWarning("Failed to add bloom pass: " + String(e.what()));
}
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
DeferredRenderPass* dofPass = ResetPass(RenderPassType_DOF, 0);
dofPass->Enable(false);
}
catch (const std::exception& e)
{
NazaraWarning("Failed to add DOF pass: " + String(e.what()));
}
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
DeferredRenderPass* fogPass = ResetPass(RenderPassType_Fog, 0);
fogPass->Enable(false);
}
catch (const std::exception& e)
{
NazaraWarning("Failed to add fog pass: " + String(e.what()));
}
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
ResetPass(RenderPassType_Forward, 0);
}
catch (const std::exception& e)
{
NazaraWarning("Failed to add forward pass: " + String(e.what()));
}
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
ResetPass(RenderPassType_SSAO, 0);
}
catch (const std::exception& e)
{
NazaraWarning("Failed to add SSAO pass: " + String(e.what()));
}
}
DeferredRenderTechnique::~DeferredRenderTechnique() = default;
/*!
* \brief Clears the data
*
* \param sceneData Data of the scene
*/
void DeferredRenderTechnique::Clear(const SceneData& sceneData) const
{
NazaraUnused(sceneData);
}
/*!
* \brief Draws the data of the scene
* \return true If successful
*
* \param sceneData Data of the scene
*
* \remark Produces a NazaraAssert if viewer of the scene is invalid
* \remark Produces a NazaraError if updating viewport dimensions failed
*/
bool DeferredRenderTechnique::Draw(const SceneData& sceneData) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
Recti viewerViewport = sceneData.viewer->GetViewport();
Vector2ui viewportDimensions(viewerViewport.width, viewerViewport.height);
if (viewportDimensions != m_GBufferSize)
{
if (!Resize(viewportDimensions))
{
NazaraError("Failed to update RTT");
return false;
}
}
unsigned int sceneTexture = 0;
unsigned int workTexture = 1;
for (auto& passIt : m_passes)
{
for (auto& passIt2 : passIt.second)
{
const DeferredRenderPass* pass = passIt2.second.get();
if (pass->IsEnabled())
{
if (pass->Process(sceneData, workTexture, sceneTexture))
std::swap(workTexture, sceneTexture);
}
}
}
return true;
}
/*!
* \brief Enables a pass
*
* \param renderPass Enumeration for the pass
* \param position Position of the pass
* \param enable Should the pass be enabled
*/
void DeferredRenderTechnique::EnablePass(RenderPassType renderPass, int position, bool enable)
{
auto it = m_passes.find(renderPass);
if (it != m_passes.end())
{
auto it2 = it->second.find(position);
if (it2 != it->second.end())
it2->second->Enable(enable);
}
}
/*!
* \brief Gets the stencil buffer
* \return Pointer to the rendering buffer
*/
Texture* DeferredRenderTechnique::GetDepthStencilTexture() const
{
return m_depthStencilTexture;
}
/*!
* \brief Gets the G-buffer
* \return Pointer to the ith texture
*
* \param i Index of the G-buffer
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if index is invalid
*/
Texture* DeferredRenderTechnique::GetGBuffer(unsigned int i) const
{
#if NAZARA_GRAPHICS_SAFE
if (i >= 3)
{
NazaraError("GBuffer texture index out of range (" + String::Number(i) + " >= 3)");
return nullptr;
}
#endif
return m_GBuffer[i];
}
/*!
* \brief Gets the rendering texture of the G-buffer
* \return Pointer to the rendering buffer
*/
RenderTexture* DeferredRenderTechnique::GetGBufferRTT() const
{
return &m_GBufferRTT;
}
/*!
* \brief Gets the forward technique
* \return Constant pointer to the forward technique
*/
const ForwardRenderTechnique* DeferredRenderTechnique::GetForwardTechnique() const
{
return &m_forwardTechnique;
}
/*!
* \brief Gets the pass
* \return Pointer to the deferred render pass
*
* \param renderPass Enumeration for the pass
* \param position Position of the pass
*/
DeferredRenderPass* DeferredRenderTechnique::GetPass(RenderPassType renderPass, int position)
{
auto it = m_passes.find(renderPass);
if (it != m_passes.end())
{
auto it2 = it->second.find(position);
if (it2 != it->second.end())
return it2->second.get();
}
return nullptr;
}
/*!
* \brief Gets the render queue
* \return Pointer to the render queue
*/
AbstractRenderQueue* DeferredRenderTechnique::GetRenderQueue()
{
return &m_renderQueue;
}
/*!
* \brief Gets the type of the current technique
* \return Type of the render technique
*/
RenderTechniqueType DeferredRenderTechnique::GetType() const
{
return RenderTechniqueType_DeferredShading;
}
/*!
* \brief Gets the render texture used to work
* \return Pointer to the rendering texture
*/
RenderTexture* DeferredRenderTechnique::GetWorkRTT() const
{
return &m_workRTT;
}
/*!
* \brief Gets the ith texture to work
* \return Pointer to the texture
*
* \param i Index of the texture used to work
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if index is invalid
*/
Texture* DeferredRenderTechnique::GetWorkTexture(unsigned int i) const
{
#if NAZARA_GRAPHICS_SAFE
if (i >= 2)
{
NazaraError("Work texture index out of range (" + String::Number(i) + " >= 2)");
return nullptr;
}
#endif
return m_workTextures[i];
}
/*!
* \brief Checks whether the pass is enable
* \return true If it is the case
*
* \param renderPass Enumeration for the pass
* \param position Position of the pass
*/
bool DeferredRenderTechnique::IsPassEnabled(RenderPassType renderPass, int position)
{
auto it = m_passes.find(renderPass);
if (it != m_passes.end())
{
auto it2 = it->second.find(position);
if (it2 != it->second.end())
return it2->second->IsEnabled();
}
return false;
}
/*!
* \brief Resets the pass
* \return Pointer to the new deferred render pass
*
* \param renderPass Enumeration for the pass
* \param position Position of the pass
*/
DeferredRenderPass* DeferredRenderTechnique::ResetPass(RenderPassType renderPass, int position)
{
std::unique_ptr<DeferredRenderPass> smartPtr; // We avoid to leak in case of exception
switch (renderPass)
{
case RenderPassType_AA:
smartPtr = std::make_unique<DeferredFXAAPass>();
break;
case RenderPassType_Bloom:
smartPtr = std::make_unique<DeferredBloomPass>();
break;
case RenderPassType_DOF:
smartPtr = std::make_unique<DeferredDOFPass>();
break;
case RenderPassType_Final:
smartPtr = std::make_unique<DeferredFinalPass>();
break;
case RenderPassType_Fog:
smartPtr = std::make_unique<DeferredFogPass>();
break;
case RenderPassType_Forward:
smartPtr = std::make_unique<DeferredForwardPass>();
break;
case RenderPassType_Geometry:
smartPtr = std::make_unique<DeferredGeometryPass>();
break;
case RenderPassType_Lighting:
smartPtr = std::make_unique<DeferredPhongLightingPass>();
break;
case RenderPassType_SSAO:
//smartPtr.reset(new DeferredSSAOPass);
break;
}
DeferredRenderPass* oldPass = GetPass(renderPass, position);
if (oldPass && !oldPass->IsEnabled())
smartPtr->Enable(false);
SetPass(renderPass, position, smartPtr.get());
return smartPtr.release();
}
/*!
* \brief Sets the pass
*
* \param relativeTo Enumeration for the pass
* \param position Position of the pass
* \param pass Render pass to set
*/
void DeferredRenderTechnique::SetPass(RenderPassType relativeTo, int position, DeferredRenderPass* pass)
{
if (pass)
{
pass->Initialize(this);
if (m_GBufferSize != Vector2ui(0U))
pass->Resize(m_GBufferSize);
m_passes[relativeTo][position].reset(pass);
}
else
m_passes[relativeTo].erase(position);
}
/*!
* \brief Checks whether the technique is supported
* \return true if it is the case
*/
bool DeferredRenderTechnique::IsSupported()
{
// Since OpenGL 3.3 is the minimal version, the Renderer supports what it needs, but we are never sure...
return Renderer::GetMaxColorAttachments() >= 4 && Renderer::GetMaxRenderTargets() >= 4;
}
/*!
* \brief Resizes the texture sizes used for the render technique
* \return true If successful
*
* \param dimensions Dimensions for the render technique
*
* \param Produces a NazaraError if one pass could not be resized
*/
bool DeferredRenderTechnique::Resize(const Vector2ui& dimensions) const
{
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
for (auto& passIt : m_passes)
for (auto& passIt2 : passIt.second)
passIt2.second->Resize(dimensions);
m_GBufferSize = dimensions;
return true;
}
catch (const std::exception& e)
{
NazaraError("Failed to create work RTT/G-Buffer: " + String(e.what()));
return false;
}
}
/*!
* \brief Initializes the deferred render technique
* \return true If successful
*
* \remark Produces a NazaraError if one shader creation failed
*/
bool DeferredRenderTechnique::Initialize()
{
const char vertexSource_Basic[] =
"#version 140\n"
"in vec3 VertexPosition;\n"
"uniform mat4 WorldViewProjMatrix;\n"
"void main()\n"
"{\n"
"gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);\n"
"}\n";
const char vertexSource_PostProcess[] =
"#version 140\n"
"in vec3 VertexPosition;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(VertexPosition, 1.0);"
"}\n";
ShaderStage basicVertexStage(ShaderStageType_Vertex);
if (!basicVertexStage.IsValid())
{
NazaraError("Failed to create basic vertex shader");
return false;
}
basicVertexStage.SetSource(vertexSource_Basic, sizeof(vertexSource_Basic));
if (!basicVertexStage.Compile())
{
NazaraError("Failed to compile basic vertex shader");
return false;
}
ShaderStage ppVertexStage(ShaderStageType_Vertex);
if (!ppVertexStage.IsValid())
{
NazaraError("Failed to create vertex shader");
return false;
}
ppVertexStage.SetSource(vertexSource_PostProcess, sizeof(vertexSource_PostProcess));
if (!ppVertexStage.Compile())
{
NazaraError("Failed to compile vertex shader");
return false;
}
String error;
Shader* shader;
// Shaders critiques (Nécessaires pour le Deferred Shading minimal)
shader = RegisterDeferredShader("DeferredGBufferClear", r_fragmentSource_GBufferClear, sizeof(r_fragmentSource_GBufferClear), ppVertexStage, &error);
if (!shader)
{
NazaraError("Failed to register critical shader: " + error);
return false;
}
shader = RegisterDeferredShader("DeferredDirectionnalLight", r_fragmentSource_DirectionalLight, sizeof(r_fragmentSource_DirectionalLight), ppVertexStage, &error);
if (!shader)
{
NazaraError("Failed to register critical shader: " + error);
return false;
}
shader->SendInteger(shader->GetUniformLocation("GBuffer0"), 0);
shader->SendInteger(shader->GetUniformLocation("GBuffer1"), 1);
shader->SendInteger(shader->GetUniformLocation("GBuffer2"), 2);
shader->SendInteger(shader->GetUniformLocation("DepthBuffer"), 3);
shader = RegisterDeferredShader("DeferredPointSpotLight", r_fragmentSource_PointSpotLight, sizeof(r_fragmentSource_PointSpotLight), basicVertexStage, &error);
if (!shader)
{
NazaraError("Failed to register critical shader: " + error);
return false;
}
shader->SendInteger(shader->GetUniformLocation("GBuffer0"), 0);
shader->SendInteger(shader->GetUniformLocation("GBuffer1"), 1);
shader->SendInteger(shader->GetUniformLocation("GBuffer2"), 2);
shader->SendInteger(shader->GetUniformLocation("DepthBuffer"), 3);
// Shaders optionnels (S'ils ne sont pas présents, le rendu minimal sera quand même assuré)
shader = RegisterDeferredShader("DeferredBloomBright", r_fragmentSource_BloomBright, sizeof(r_fragmentSource_BloomBright), ppVertexStage, &error);
if (shader)
shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0);
else
{
NazaraWarning("Failed to register bloom (bright pass) shader, certain features will not work: " + error);
}
shader = RegisterDeferredShader("DeferredBloomFinal", r_fragmentSource_BloomFinal, sizeof(r_fragmentSource_BloomFinal), ppVertexStage, &error);
if (shader)
{
shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0);
shader->SendInteger(shader->GetUniformLocation("BloomTexture"), 1);
}
else
{
NazaraWarning("Failed to register bloom (final pass) shader, certain features will not work: " + error);
}
shader = RegisterDeferredShader("DeferredFXAA", r_fragmentSource_FXAA, sizeof(r_fragmentSource_FXAA), ppVertexStage, &error);
if (shader)
shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0);
else
{
NazaraWarning("Failed to register FXAA shader, certain features will not work: " + error);
}
shader = RegisterDeferredShader("DeferredGaussianBlur", r_fragmentSource_GaussianBlur, sizeof(r_fragmentSource_GaussianBlur), ppVertexStage, &error);
if (shader)
shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0);
else
{
NazaraWarning("Failed to register gaussian blur shader, certain features will not work: " + error);
}
if (!DeferredGeometryPass::Initialize())
{
NazaraError("Failed to initialize geometry pass");
return false;
}
return true;
}
/*!
* \brief Uninitializes the deferred render technique
*/
void DeferredRenderTechnique::Uninitialize()
{
DeferredGeometryPass::Uninitialize();
ShaderLibrary::Unregister("DeferredGBufferClear");
ShaderLibrary::Unregister("DeferredDirectionnalLight");
ShaderLibrary::Unregister("DeferredPointSpotLight");
ShaderLibrary::Unregister("DeferredBloomBright");
ShaderLibrary::Unregister("DeferredBloomFinal");
ShaderLibrary::Unregister("DeferredFXAA");
ShaderLibrary::Unregister("DeferredGaussianBlur");
}
/*!
* \brief Functor to compare two render pass
* \return true If first render pass is "smaller" than the second one
*
* \param pass1 First render pass to compare
* \param pass2 Second render pass to compare
*/
bool DeferredRenderTechnique::RenderPassComparator::operator()(RenderPassType pass1, RenderPassType pass2) const
{
return RenderPassPriority[pass1] < RenderPassPriority[pass2];
}
}

View File

@@ -1,372 +0,0 @@
// Copyright (C) 2020 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/DepthRenderQueue.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::DepthRenderQueue
* \brief Graphics class that represents the rendering queue for depth rendering
*/
/*!
* \brief Constructs a DepthRenderTechnique object by default
*/
DepthRenderQueue::DepthRenderQueue()
{
// Material
m_baseMaterial = Material::New();
m_baseMaterial->EnableColorWrite(false);
m_baseMaterial->EnableFaceCulling(false);
//m_baseMaterial->SetFaceCulling(FaceSide_Front);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr);
}
/*!
* \brief Adds a directional light to the queue
*
* \param light Light to add
*
* \remark Produces a NazaraAssert
*/
void DepthRenderQueue::AddDirectionalLight(const DirectionalLight& light)
{
NazaraAssert(false, "Depth render queue doesn't handle lights");
NazaraUnused(light);
}
/*!
* \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 DepthRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect)
{
NazaraAssert(material, "Invalid material");
NazaraUnused(renderOrder);
NazaraUnused(meshAABB);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddMesh(0, material, meshData, meshAABB, transformMatrix, scissorRect);
}
/*!
* \brief Adds a point light to the queue
*
* \param light Light to add
*
* \remark Produces a NazaraAssert
*/
void DepthRenderQueue::AddPointLight(const PointLight& light)
{
NazaraAssert(false, "Depth render queue doesn't handle lights");
NazaraUnused(light);
}
/*!
* \brief Adds a spot light to the queue
*
* \param light Light to add
*
* \remark Produces a NazaraAssert
*/
void DepthRenderQueue::AddSpotLight(const SpotLight& light)
{
NazaraAssert(false, "Depth render queue doesn't handle lights");
NazaraUnused(light);
}
/*!
* \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 DepthRenderQueue::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");
NazaraUnused(renderOrder);
NazaraUnused(overlay);
if (!IsMaterialSuitable(material))
return;
if (material->HasDepthMaterial())
material = material->GetDepthMaterial();
else
material = m_baseMaterial;
BasicRenderQueue::AddSprites(0, material, vertices, spriteCount, scissorRect, overlay);
}
}

View File

@@ -1,674 +0,0 @@
// Copyright (C) 2020 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/DepthRenderTechnique.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/OffsetOf.hpp>
#include <Nazara/Graphics/AbstractBackground.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/Drawable.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Utility/BufferMapper.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <limits>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
struct BillboardPoint
{
Color color;
Vector3f position;
Vector2f size;
Vector2f sinCos; // must follow `size` (both will be sent as a Vector4f)
Vector2f uv;
};
constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB
constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV);
}
/*!
* \ingroup graphics
* \class Nz::DepthRenderTechnique
* \brief Graphics class that represents the technique used in depth rendering
*/
/*!
* \brief Constructs a DepthRenderTechnique object by default
*/
DepthRenderTechnique::DepthRenderTechnique() :
m_vertexBuffer(BufferType_Vertex)
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
m_whiteTexture = Nz::TextureLibrary::Get("White2D");
m_vertexBuffer.Create(s_vertexBufferSize, DataStorage_Hardware, BufferUsage_Dynamic);
m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer);
m_spriteBuffer.Reset(VertexDeclaration::Get(VertexLayout_XYZ_Color_UV), &m_vertexBuffer);
}
/*!
* \brief Clears the data
*
* \param sceneData Data of the scene
*/
void DepthRenderTechnique::Clear(const SceneData& sceneData) const
{
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
Renderer::SetScissorRect(fullscreenScissorRect);
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_DepthWrite, true);
Renderer::Clear(RendererBuffer_Depth);
// Just in case the background does render depth
if (sceneData.background)
sceneData.background->Draw(sceneData.viewer);
}
/*!
* \brief Draws the data of the scene
* \return true If successful
*
* \param sceneData Data of the scene
*/
bool DepthRenderTechnique::Draw(const SceneData& sceneData) const
{
m_renderQueue.Sort(sceneData.viewer);
if (!m_renderQueue.models.empty())
DrawModels(sceneData, m_renderQueue, m_renderQueue.models);
if (!m_renderQueue.basicSprites.empty())
DrawSprites(sceneData, m_renderQueue, m_renderQueue.basicSprites);
if (!m_renderQueue.billboards.empty())
DrawBillboards(sceneData, m_renderQueue, m_renderQueue.billboards);
if (!m_renderQueue.depthSortedModels.empty())
DrawModels(sceneData, m_renderQueue, m_renderQueue.depthSortedModels);
if (!m_renderQueue.depthSortedSprites.empty())
DrawSprites(sceneData, m_renderQueue, m_renderQueue.depthSortedSprites);
if (!m_renderQueue.depthSortedBillboards.empty())
DrawBillboards(sceneData, m_renderQueue, m_renderQueue.depthSortedBillboards);
if (!m_renderQueue.customDrawables.empty())
DrawCustomDrawables(sceneData, m_renderQueue, m_renderQueue.customDrawables);
return true;
}
/*!
* \brief Gets the render queue
* \return Pointer to the render queue
*/
AbstractRenderQueue* DepthRenderTechnique::GetRenderQueue()
{
return &m_renderQueue;
}
/*!
* \brief Gets the type of the current technique
* \return Type of the render technique
*/
RenderTechniqueType DepthRenderTechnique::GetType() const
{
return RenderTechniqueType_Depth;
}
/*!
* \brief Initializes the depth render technique
* \return true If successful
*
* \remark Produces a NazaraError if one shader creation failed
*/
bool DepthRenderTechnique::Initialize()
{
try
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0);
BufferMapper<IndexBuffer> mapper(s_quadIndexBuffer, BufferAccess_WriteOnly);
UInt32* indices = static_cast<UInt32*>(mapper.GetPointer());
for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i)
{
*indices++ = i * 4 + 0;
*indices++ = i * 4 + 2;
*indices++ = i * 4 + 1;
*indices++ = i * 4 + 2;
*indices++ = i * 4 + 3;
*indices++ = i * 4 + 1;
}
mapper.Unmap(); // Inutile de garder le buffer ouvert plus longtemps
// Quad buffer (utilisé pour l'instancing de billboard et de sprites)
//Note: Les UV sont calculés dans le shader
s_quadVertexBuffer.Reset(VertexDeclaration::Get(VertexLayout_XY), 4, DataStorage_Hardware, 0);
float vertices[2 * 4] = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
0.5f, 0.5f,
};
s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices));
// Déclaration lors du rendu des billboards par sommet
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(BillboardPoint, color));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(BillboardPoint, position));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(BillboardPoint, uv));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Userdata0, ComponentType_Float4, NazaraOffsetOf(BillboardPoint, size)); // Englobe sincos
// Declaration utilisée lors du rendu des billboards par instancing
// L'avantage ici est la copie directe (std::memcpy) des données de la RenderQueue vers le buffer GPU
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(BasicRenderQueue::BillboardData, center));
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(BasicRenderQueue::BillboardData, size)); // Englobe sincos
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(BasicRenderQueue::BillboardData, color));
}
catch (const std::exception& e)
{
NazaraError("Failed to initialise: " + String(e.what()));
return false;
}
return true;
}
/*!
* \brief Uninitializes the depth render technique
*/
void DepthRenderTechnique::Uninitialize()
{
s_quadIndexBuffer.Reset();
s_quadVertexBuffer.Reset();
}
void DepthRenderTechnique::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::Billboard>& billboards) const
{
VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer();
instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration);
Renderer::SetVertexBuffer(&s_quadVertexBuffer);
Nz::BufferMapper<VertexBuffer> instanceBufferMapper;
std::size_t billboardCount = 0;
std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount();
auto Commit = [&]()
{
if (billboardCount > 0)
{
instanceBufferMapper.Unmap();
Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4);
billboardCount = 0;
}
};
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
for (const BasicRenderQueue::Billboard& billboard : billboards)
{
const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect;
if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
Commit();
const MaterialPipeline* pipeline = billboard.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Deferred | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != billboard.material)
{
billboard.material->Apply(*pipelineInstance);
lastMaterial = billboard.material;
}
if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
if (!instanceBufferMapper.GetBuffer())
instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite);
std::memcpy(static_cast<Nz::UInt8*>(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, &billboard.data, sizeof(BasicRenderQueue::BillboardData));
if (++billboardCount >= maxBillboardPerDraw)
Commit();
}
Commit();
}
void DepthRenderTechnique::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::BillboardChain>& billboards) const
{
VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer();
instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration);
Renderer::SetVertexBuffer(&s_quadVertexBuffer);
Nz::BufferMapper<VertexBuffer> instanceBufferMapper;
std::size_t billboardCount = 0;
std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount();
auto Commit = [&]()
{
if (billboardCount > 0)
{
instanceBufferMapper.Unmap();
Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4);
billboardCount = 0;
}
};
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
for (const BasicRenderQueue::BillboardChain& billboard : billboards)
{
const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect;
if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
Commit();
const MaterialPipeline* pipeline = billboard.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Deferred | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != billboard.material)
{
billboard.material->Apply(*pipelineInstance);
lastMaterial = billboard.material;
}
if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
std::size_t billboardRemaining = billboard.billboardCount;
const BasicRenderQueue::BillboardData* billboardData = renderQueue.GetBillboardData(billboard.billboardIndex);
do
{
std::size_t renderedBillboardCount = std::min(billboardRemaining, maxBillboardPerDraw - billboardCount);
billboardRemaining -= renderedBillboardCount;
if (!instanceBufferMapper.GetBuffer())
instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite);
std::memcpy(static_cast<Nz::UInt8*>(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, billboardData, renderedBillboardCount * sizeof(BasicRenderQueue::BillboardData));
billboardCount += renderedBillboardCount;
billboardData += renderedBillboardCount;
if (billboardCount >= maxBillboardPerDraw)
Commit();
}
while (billboardRemaining > 0);
}
Commit();
}
void DepthRenderTechnique::DrawCustomDrawables(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::CustomDrawable>& customDrawables) const
{
for (const BasicRenderQueue::CustomDrawable& customDrawable : customDrawables)
customDrawable.drawable->Draw();
}
void DepthRenderTechnique::DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const Nz::RenderQueue<Nz::BasicRenderQueue::Model>& models) const
{
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
///TODO: Reimplement instancing
for (const BasicRenderQueue::Model& model : models)
{
const MaterialPipeline* pipeline = model.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &model.material->GetPipeline()->Apply(ShaderFlags_Deferred);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != model.material)
{
model.material->Apply(*pipelineInstance);
lastMaterial = model.material;
}
if (model.material->IsScissorTestEnabled())
{
const Nz::Recti& scissorRect = (model.scissorRect.width > 0) ? model.scissorRect : fullscreenScissorRect;
if (scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
// Handle draw call before rendering loop
Renderer::DrawCall drawFunc;
Renderer::DrawCallInstanced instancedDrawFunc;
unsigned int indexCount;
if (model.meshData.indexBuffer)
{
drawFunc = Renderer::DrawIndexedPrimitives;
instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced;
indexCount = model.meshData.indexBuffer->GetIndexCount();
}
else
{
drawFunc = Renderer::DrawPrimitives;
instancedDrawFunc = Renderer::DrawPrimitivesInstanced;
indexCount = model.meshData.vertexBuffer->GetVertexCount();
}
Renderer::SetIndexBuffer(model.meshData.indexBuffer);
Renderer::SetVertexBuffer(model.meshData.vertexBuffer);
Renderer::SetMatrix(MatrixType_World, model.matrix);
drawFunc(model.meshData.primitiveMode, 0, indexCount);
}
}
void DepthRenderTechnique::DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::SpriteChain>& spriteList) const
{
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4);
const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
auto Draw = [&]()
{
unsigned int firstIndex = 0;
for (const auto& batch : m_spriteBatches)
{
const MaterialPipeline* pipeline = batch.material->GetPipeline();
if (pipeline != lastPipeline)
{
pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Overlay texture unit
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
lastShader = shader;
}
lastPipeline = pipeline;
}
if (batch.material != lastMaterial)
{
batch.material->Apply(*pipelineInstance);
Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler());
lastMaterial = batch.material;
}
if (batch.overlayTexture != lastOverlay)
{
Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture);
lastOverlay = batch.overlayTexture;
}
if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(batch.scissorRect);
lastScissorRect = batch.scissorRect;
}
unsigned int indexCount = batch.spriteCount * 6;
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount);
firstIndex += indexCount;
}
};
m_spriteBatches.clear();
{
BufferMapper<VertexBuffer> vertexMapper;
VertexStruct_XYZ_Color_UV* vertices = nullptr;
std::size_t remainingSprite = maxSpriteCount;
const Material* lastMaterial = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList)
{
const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get();
const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect;
const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices;
std::size_t spriteCount = basicSprites.spriteCount;
for (;;)
{
if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
m_spriteBatches.emplace_back();
SpriteBatch& newBatch = m_spriteBatches.back();
newBatch.material = basicSprites.material;
newBatch.overlayTexture = overlayTexture;
newBatch.scissorRect = scissorRect;
newBatch.spriteCount = 0;
lastMaterial = basicSprites.material;
lastOverlay = overlayTexture;
lastScissorRect = scissorRect;
}
SpriteBatch& currentBatch = m_spriteBatches.back();
if (!vertices)
{
vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite);
vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
}
std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount);
std::size_t processedVertices = processedSpriteCount * 4;
std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV));
vertices += processedVertices;
spriteVertices += processedVertices;
currentBatch.spriteCount += processedSpriteCount;
spriteCount -= processedSpriteCount;
remainingSprite -= processedSpriteCount;
if (remainingSprite == 0)
{
vertexMapper.Unmap();
vertices = nullptr;
Draw();
remainingSprite = maxSpriteCount;
m_spriteBatches.clear();
}
if (spriteCount == 0)
break;
}
}
}
Draw();
}
/*!
* \brief Gets the shader uniforms
* \return Uniforms of the shader
*
* \param shader Shader to get uniforms from
*/
const DepthRenderTechnique::ShaderUniforms* DepthRenderTechnique::GetShaderUniforms(const Shader* shader) const
{
auto it = m_shaderUniforms.find(shader);
if (it == m_shaderUniforms.end())
{
ShaderUniforms uniforms;
uniforms.shaderReleaseSlot.Connect(shader->OnShaderRelease, this, &DepthRenderTechnique::OnShaderInvalidated);
uniforms.shaderUniformInvalidatedSlot.Connect(shader->OnShaderUniformInvalidated, this, &DepthRenderTechnique::OnShaderInvalidated);
uniforms.sceneAmbient = shader->GetUniformLocation("SceneAmbient");
uniforms.textureOverlay = shader->GetUniformLocation("TextureOverlay");
it = m_shaderUniforms.emplace(shader, std::move(uniforms)).first;
}
return &it->second;
}
/*!
* \brief Handle the invalidation of a shader
*
* \param shader Shader being invalidated
*/
void DepthRenderTechnique::OnShaderInvalidated(const Shader* shader) const
{
m_shaderUniforms.erase(shader);
}
IndexBuffer DepthRenderTechnique::s_quadIndexBuffer;
VertexBuffer DepthRenderTechnique::s_quadVertexBuffer;
VertexDeclaration DepthRenderTechnique::s_billboardInstanceDeclaration;
VertexDeclaration DepthRenderTechnique::s_billboardVertexDeclaration;
}

View File

@@ -1,19 +0,0 @@
// Copyright (C) 2020 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/Drawable.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Drawable
* \brief Graphics class that represents something drawable for our scene
*
* \remark This class is abstract
*/
Drawable::~Drawable() = default;
}

View File

@@ -1,100 +0,0 @@
// Copyright (C) 2020 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/Formats/MeshLoader.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/Model.hpp>
#include <Nazara/Graphics/SkeletalModel.hpp>
#include <Nazara/Utility/MaterialData.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
void LoadMaterials(Model* model, const ModelParameters& parameters)
{
unsigned int matCount = model->GetMaterialCount();
for (unsigned int i = 0; i < matCount; ++i)
{
const ParameterList& matData = model->GetMesh()->GetMaterialData(i);
String path;
if (matData.GetStringParameter(MaterialData::FilePath, &path))
{
std::filesystem::path filePath = path.ToStdString();
if (!std::filesystem::exists(filePath))
{
NazaraWarning("Shader name does not refer to an existing file, \".tga\" is used by default");
filePath.replace_extension(".tga");
}
if (MaterialRef material = Material::LoadFromFile(filePath, parameters.material))
model->SetMaterial(i, std::move(material));
else
NazaraWarning("Failed to load material from file " + String::Number(i));
}
else
{
MaterialRef material = Material::New();
material->BuildFromParameters(matData, parameters.material);
model->SetMaterial(i, std::move(material));
}
}
}
Ternary Check(Stream& stream, const ModelParameters& parameters)
{
NazaraUnused(stream);
bool skip;
if (parameters.custom.GetBooleanParameter("SkipNativeMeshLoader", &skip) && skip)
return Ternary_False;
return Ternary_Unknown;
}
ModelRef Load(Stream& stream, const ModelParameters& parameters)
{
NazaraUnused(parameters);
MeshRef mesh = Mesh::LoadFromStream(stream, parameters.mesh);
if (!mesh)
{
NazaraError("Failed to load model mesh");
return nullptr;
}
ModelRef model;
if (mesh->IsAnimable())
model = SkeletalModel::New();
else
model = Model::New();
model->SetMesh(mesh);
if (parameters.loadMaterials)
LoadMaterials(model, parameters);
return model;
}
}
namespace Loaders
{
void RegisterMesh()
{
ModelLoader::RegisterLoader(MeshLoader::IsExtensionSupported, Check, Load);
}
void UnregisterMesh()
{
ModelLoader::UnregisterLoader(MeshLoader::IsExtensionSupported, Check, Load);
}
}
}

View File

@@ -1,21 +0,0 @@
// Copyright (C) 2020 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_LOADERS_MESH_HPP
#define NAZARA_LOADERS_MESH_HPP
#include <Nazara/Prerequisites.hpp>
namespace Nz
{
namespace Loaders
{
void RegisterMesh();
void UnregisterMesh();
}
}
#endif // NAZARA_LOADERS_MESH_HPP

View File

@@ -1,56 +0,0 @@
// Copyright (C) 2020 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/Formats/TextureLoader.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Renderer/Texture.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
Ternary Check(Stream& stream, const MaterialParams& parameters)
{
NazaraUnused(stream);
bool skip;
if (parameters.custom.GetBooleanParameter("SkipNativeTextureLoader", &skip) && skip)
return Ternary_False;
return Ternary_Unknown;
}
MaterialRef Load(Stream& stream, const MaterialParams& parameters)
{
NazaraUnused(parameters);
TextureRef texture = Texture::LoadFromStream(stream);
if (!texture)
{
NazaraError("Failed to load diffuse map");
return nullptr;
}
MaterialRef material = Material::New();
material->SetDiffuseMap(texture);
material->SetShader(parameters.shaderName);
return material;
}
}
namespace Loaders
{
void RegisterTexture()
{
MaterialLoader::RegisterLoader(ImageLoader::IsExtensionSupported, Check, Load);
}
void UnregisterTexture()
{
MaterialLoader::UnregisterLoader(ImageLoader::IsExtensionSupported, Check, Load);
}
}
}

View File

@@ -1,21 +0,0 @@
// Copyright (C) 2020 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_LOADERS_TEXTURE_HPP
#define NAZARA_LOADERS_TEXTURE_HPP
#include <Nazara/Prerequisites.hpp>
namespace Nz
{
namespace Loaders
{
void RegisterTexture();
void UnregisterTexture();
}
}
#endif // NAZARA_LOADERS_TEXTURE_HPP

View File

@@ -1,924 +0,0 @@
// Copyright (C) 2020 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/ForwardRenderTechnique.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/OffsetOf.hpp>
#include <Nazara/Graphics/AbstractBackground.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/Drawable.hpp>
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Utility/BufferMapper.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <limits>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
struct BillboardPoint
{
Color color;
Vector3f position;
Vector2f size;
Vector2f sinCos; // must follow `size` (both will be sent as a Vector4f)
Vector2f uv;
};
constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB
constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV);
}
/*!
* \ingroup graphics
* \class Nz::ForwardRenderTechnique
* \brief Graphics class that represents the technique used in forward rendering
*/
/*!
* \brief Constructs a ForwardRenderTechnique object by default
*/
ForwardRenderTechnique::ForwardRenderTechnique() :
m_vertexBuffer(BufferType_Vertex),
m_maxLightPassPerObject(3)
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
m_whiteCubemap = Nz::TextureLibrary::Get("WhiteCubemap");
m_whiteTexture = Nz::TextureLibrary::Get("White2D");
m_vertexBuffer.Create(s_vertexBufferSize, DataStorage_Hardware, BufferUsage_Dynamic);
m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer);
m_spriteBuffer.Reset(VertexDeclaration::Get(VertexLayout_XYZ_Color_UV), &m_vertexBuffer);
}
/*!
* \brief Clears the data
*
* \param sceneData Data of the scene
*/
void ForwardRenderTechnique::Clear(const SceneData& sceneData) const
{
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
Renderer::SetScissorRect(fullscreenScissorRect);
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_DepthWrite, true);
Renderer::Clear(RendererBuffer_Depth);
if (sceneData.background)
sceneData.background->Draw(sceneData.viewer);
}
/*!
* \brief Draws the data of the scene
* \return true If successful
*
* \param sceneData Data of the scene
*
* \remark Produces a NazaraAssert if viewer of the scene is invalid
*/
bool ForwardRenderTechnique::Draw(const SceneData& sceneData) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
m_renderQueue.Sort(sceneData.viewer);
if (!m_renderQueue.models.empty())
DrawModels(sceneData, m_renderQueue, m_renderQueue.models);
if (!m_renderQueue.basicSprites.empty())
DrawSprites(sceneData, m_renderQueue, m_renderQueue.basicSprites);
if (!m_renderQueue.billboards.empty())
DrawBillboards(sceneData, m_renderQueue, m_renderQueue.billboards);
if (!m_renderQueue.depthSortedModels.empty())
DrawModels(sceneData, m_renderQueue, m_renderQueue.depthSortedModels);
if (!m_renderQueue.depthSortedSprites.empty())
DrawSprites(sceneData, m_renderQueue, m_renderQueue.depthSortedSprites);
if (!m_renderQueue.depthSortedBillboards.empty())
DrawBillboards(sceneData, m_renderQueue, m_renderQueue.depthSortedBillboards);
if (!m_renderQueue.customDrawables.empty())
DrawCustomDrawables(sceneData, m_renderQueue, m_renderQueue.customDrawables);
return true;
}
/*!
* \brief Gets the maximum number of lights available per pass per object
* \return Maximum number of light simultaneously per object
*/
unsigned int ForwardRenderTechnique::GetMaxLightPassPerObject() const
{
return m_maxLightPassPerObject;
}
/*!
* \brief Gets the render queue
* \return Pointer to the render queue
*/
AbstractRenderQueue* ForwardRenderTechnique::GetRenderQueue()
{
return &m_renderQueue;
}
/*!
* \brief Gets the type of the current technique
* \return Type of the render technique
*/
RenderTechniqueType ForwardRenderTechnique::GetType() const
{
return RenderTechniqueType_BasicForward;
}
/*!
* \brief Sets the maximum number of lights available per pass per object
*
* \param passCount Maximum number of light simulatenously per object
*/
void ForwardRenderTechnique::SetMaxLightPassPerObject(unsigned int maxLightPassPerObject)
{
m_maxLightPassPerObject = maxLightPassPerObject;
}
/*!
* \brief Initializes the forward render technique
* \return true If successful
*
* \remark Produces a NazaraError if one shader creation failed
*/
bool ForwardRenderTechnique::Initialize()
{
try
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0);
BufferMapper<IndexBuffer> mapper(s_quadIndexBuffer, BufferAccess_WriteOnly);
UInt32* indices = static_cast<UInt32*>(mapper.GetPointer());
for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i)
{
*indices++ = i * 4 + 0;
*indices++ = i * 4 + 2;
*indices++ = i * 4 + 1;
*indices++ = i * 4 + 2;
*indices++ = i * 4 + 3;
*indices++ = i * 4 + 1;
}
mapper.Unmap(); // No point to keep the buffer open any longer
// Quad buffer (used for instancing of billboards and sprites)
//Note: UV are computed in the shader
s_quadVertexBuffer.Reset(VertexDeclaration::Get(VertexLayout_XY), 4, DataStorage_Hardware, 0);
float vertices[2 * 4] = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
0.5f, 0.5f,
};
s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices));
// Declaration used when rendering the vertex billboards
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(BillboardPoint, color));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(BillboardPoint, position));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(BillboardPoint, uv));
s_billboardVertexDeclaration.EnableComponent(VertexComponent_Userdata0, ComponentType_Float4, NazaraOffsetOf(BillboardPoint, size)); // Includes sincos
// Declaration used when rendering the billboards with intancing
// The main advantage is the direct copy (std::memcpy) of data in the RenderQueue to the GPU buffer
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(BasicRenderQueue::BillboardData, center));
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(BasicRenderQueue::BillboardData, size)); // Englobe sincos
s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(BasicRenderQueue::BillboardData, color));
s_reflectionSampler.SetFilterMode(SamplerFilter_Bilinear);
s_reflectionSampler.SetWrapMode(SamplerWrap_Clamp);
s_shadowSampler.SetFilterMode(SamplerFilter_Bilinear);
s_shadowSampler.SetWrapMode(SamplerWrap_Clamp);
}
catch (const std::exception& e)
{
NazaraError("Failed to initialise: " + String(e.what()));
return false;
}
return true;
}
/*!
* \brief Uninitializes the forward render technique
*/
void ForwardRenderTechnique::Uninitialize()
{
s_quadIndexBuffer.Reset();
s_quadVertexBuffer.Reset();
}
/*!
* \brief Chooses the nearest lights for one object
*
* \param object Sphere symbolizing the object
* \param includeDirectionalLights Should directional lights be included in the computation
*/
void ForwardRenderTechnique::ChooseLights(const Spheref& object, bool includeDirectionalLights) const
{
m_lights.clear();
// First step: add all the lights into a common list and compute their score, exlucing those who have no chance of lighting the object
// (Those who are too far away).
if (includeDirectionalLights)
{
for (unsigned int i = 0; i < m_renderQueue.directionalLights.size(); ++i)
{
const auto& light = m_renderQueue.directionalLights[i];
if (IsDirectionalLightSuitable(object, light))
m_lights.push_back({LightType_Directional, ComputeDirectionalLightScore(object, light), i});
}
}
for (unsigned int i = 0; i < m_renderQueue.pointLights.size(); ++i)
{
const auto& light = m_renderQueue.pointLights[i];
if (IsPointLightSuitable(object, light))
m_lights.push_back({LightType_Point, ComputePointLightScore(object, light), i});
}
for (unsigned int i = 0; i < m_renderQueue.spotLights.size(); ++i)
{
const auto& light = m_renderQueue.spotLights[i];
if (IsSpotLightSuitable(object, light))
m_lights.push_back({LightType_Spot, ComputeSpotLightScore(object, light), i});
}
// Then, sort the lights according to their score
std::sort(m_lights.begin(), m_lights.end(), [](const LightIndex& light1, const LightIndex& light2)
{
return light1.score < light2.score;
});
}
void ForwardRenderTechnique::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::Billboard>& billboards) const
{
VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer();
instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration);
Renderer::SetVertexBuffer(&s_quadVertexBuffer);
Nz::BufferMapper<VertexBuffer> instanceBufferMapper;
std::size_t billboardCount = 0;
std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount();
auto Commit = [&]()
{
if (billboardCount > 0)
{
instanceBufferMapper.Unmap();
Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4);
billboardCount = 0;
}
};
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
for (const BasicRenderQueue::Billboard& billboard : billboards)
{
const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect;
if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
Commit();
const MaterialPipeline* pipeline = billboard.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != billboard.material)
{
billboard.material->Apply(*pipelineInstance);
lastMaterial = billboard.material;
}
if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
if (!instanceBufferMapper.GetBuffer())
instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite);
std::memcpy(static_cast<Nz::UInt8*>(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, &billboard.data, sizeof(BasicRenderQueue::BillboardData));
if (++billboardCount >= maxBillboardPerDraw)
Commit();
}
Commit();
}
void ForwardRenderTechnique::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::BillboardChain>& billboards) const
{
VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer();
instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration);
Renderer::SetVertexBuffer(&s_quadVertexBuffer);
Nz::BufferMapper<VertexBuffer> instanceBufferMapper;
std::size_t billboardCount = 0;
std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount();
auto Commit = [&]()
{
if (billboardCount > 0)
{
instanceBufferMapper.Unmap();
Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4);
billboardCount = 0;
}
};
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
for (const BasicRenderQueue::BillboardChain& billboard : billboards)
{
const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect;
if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
Commit();
const MaterialPipeline* pipeline = billboard.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != billboard.material)
{
billboard.material->Apply(*pipelineInstance);
lastMaterial = billboard.material;
}
if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
std::size_t billboardRemaining = billboard.billboardCount;
const BasicRenderQueue::BillboardData* billboardData = renderQueue.GetBillboardData(billboard.billboardIndex);
do
{
std::size_t renderedBillboardCount = std::min(billboardRemaining, maxBillboardPerDraw - billboardCount);
billboardRemaining -= renderedBillboardCount;
if (!instanceBufferMapper.GetBuffer())
instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite);
std::memcpy(static_cast<Nz::UInt8*>(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, billboardData, renderedBillboardCount * sizeof(BasicRenderQueue::BillboardData));
billboardCount += renderedBillboardCount;
billboardData += renderedBillboardCount;
if (billboardCount >= maxBillboardPerDraw)
Commit();
}
while (billboardRemaining > 0);
}
Commit();
}
void ForwardRenderTechnique::DrawCustomDrawables(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::CustomDrawable>& customDrawables) const
{
for (const BasicRenderQueue::CustomDrawable& customDrawable : customDrawables)
customDrawable.drawable->Draw();
}
void ForwardRenderTechnique::DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const Nz::RenderQueue<Nz::BasicRenderQueue::Model>& models) const
{
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
///TODO: Reimplement instancing
for (const BasicRenderQueue::Model& model : models)
{
const MaterialPipeline* pipeline = model.material->GetPipeline();
if (lastPipeline != pipeline)
{
pipelineInstance = &model.material->GetPipeline()->Apply();
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
lastPipeline = pipeline;
}
if (lastMaterial != model.material)
{
model.material->Apply(*pipelineInstance);
lastMaterial = model.material;
}
if (model.material->IsScissorTestEnabled())
{
const Nz::Recti& scissorRect = (model.scissorRect.width > 0) ? model.scissorRect : fullscreenScissorRect;
if (scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
}
if (shaderUniforms->reflectionMap != -1)
{
unsigned int textureUnit = Material::GetTextureUnit(TextureMap_ReflectionCube);
Renderer::SetTexture(textureUnit, sceneData.globalReflectionTexture);
Renderer::SetTextureSampler(textureUnit, s_reflectionSampler);
}
// Handle draw call before rendering loop
Renderer::DrawCall drawFunc;
Renderer::DrawCallInstanced instancedDrawFunc;
unsigned int indexCount;
if (model.meshData.indexBuffer)
{
drawFunc = Renderer::DrawIndexedPrimitives;
instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced;
indexCount = model.meshData.indexBuffer->GetIndexCount();
}
else
{
drawFunc = Renderer::DrawPrimitives;
instancedDrawFunc = Renderer::DrawPrimitivesInstanced;
indexCount = model.meshData.vertexBuffer->GetVertexCount();
}
Renderer::SetIndexBuffer(model.meshData.indexBuffer);
Renderer::SetVertexBuffer(model.meshData.vertexBuffer);
if (shaderUniforms->hasLightUniforms)
{
ChooseLights(model.obbSphere);
std::size_t lightCount = m_lights.size();
Nz::Renderer::SetMatrix(Nz::MatrixType_World, model.matrix);
std::size_t lightIndex = 0;
RendererComparison oldDepthFunc = Renderer::GetDepthFunc(); // In the case where we have to change it
std::size_t passCount = (lightCount == 0) ? 1 : (lightCount - 1) / NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1;
for (std::size_t pass = 0; pass < passCount; ++pass)
{
lightCount -= std::min<std::size_t>(lightCount, NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS);
if (pass == 1)
{
// To add the result of light computations
// We won't interfere with materials parameters because we only render opaques objects
// (A.K.A., without blending)
// About the depth function, it must be applied only the first time
Renderer::Enable(RendererParameter_Blend, true);
Renderer::SetBlendFunc(BlendFunc_One, BlendFunc_One);
Renderer::SetDepthFunc(RendererComparison_Equal);
}
// Sends the light uniforms to the shader
for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
SendLightUniforms(lastShader, shaderUniforms->lightUniforms, i, lightIndex++, shaderUniforms->lightOffset*i);
// And we draw
drawFunc(model.meshData.primitiveMode, 0, indexCount);
}
Renderer::Enable(RendererParameter_Blend, false);
Renderer::SetDepthFunc(oldDepthFunc);
}
else
{
Renderer::SetMatrix(MatrixType_World, model.matrix);
drawFunc(model.meshData.primitiveMode, 0, indexCount);
}
}
}
void ForwardRenderTechnique::DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue<BasicRenderQueue::SpriteChain>& spriteList) const
{
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4);
const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
const MaterialPipeline::Instance* pipelineInstance = nullptr;
Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
auto Draw = [&]()
{
unsigned int firstIndex = 0;
for (const auto& batch : m_spriteBatches)
{
const MaterialPipeline* pipeline = batch.material->GetPipeline();
if (pipeline != lastPipeline)
{
pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
// Overlay texture unit
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
lastShader = shader;
}
lastPipeline = pipeline;
}
if (batch.material != lastMaterial)
{
batch.material->Apply(*pipelineInstance);
Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler());
lastMaterial = batch.material;
}
if (batch.overlayTexture != lastOverlay)
{
Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture);
lastOverlay = batch.overlayTexture;
}
if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(batch.scissorRect);
lastScissorRect = batch.scissorRect;
}
unsigned int indexCount = batch.spriteCount * 6;
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount);
firstIndex += indexCount;
}
};
m_spriteBatches.clear();
{
BufferMapper<VertexBuffer> vertexMapper;
VertexStruct_XYZ_Color_UV* vertices = nullptr;
std::size_t remainingSprite = maxSpriteCount;
const Material* lastMaterial = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList)
{
const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get();
const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect;
const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices;
std::size_t spriteCount = basicSprites.spriteCount;
for (;;)
{
if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
m_spriteBatches.emplace_back();
SpriteBatch& newBatch = m_spriteBatches.back();
newBatch.material = basicSprites.material;
newBatch.overlayTexture = overlayTexture;
newBatch.scissorRect = scissorRect;
newBatch.spriteCount = 0;
lastMaterial = basicSprites.material;
lastOverlay = overlayTexture;
lastScissorRect = scissorRect;
}
SpriteBatch& currentBatch = m_spriteBatches.back();
if (!vertices)
{
vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite);
vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
}
std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount);
std::size_t processedVertices = processedSpriteCount * 4;
std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV));
vertices += processedVertices;
spriteVertices += processedVertices;
currentBatch.spriteCount += processedSpriteCount;
spriteCount -= processedSpriteCount;
remainingSprite -= processedSpriteCount;
if (remainingSprite == 0)
{
vertexMapper.Unmap();
vertices = nullptr;
Draw();
remainingSprite = maxSpriteCount;
m_spriteBatches.clear();
}
if (spriteCount == 0)
break;
}
}
}
Draw();
}
const ForwardRenderTechnique::ShaderUniforms* ForwardRenderTechnique::GetShaderUniforms(const Shader* shader) const
{
auto it = m_shaderUniforms.find(shader);
if (it == m_shaderUniforms.end())
{
ShaderUniforms uniforms;
uniforms.shaderReleaseSlot.Connect(shader->OnShaderRelease, this, &ForwardRenderTechnique::OnShaderInvalidated);
uniforms.shaderUniformInvalidatedSlot.Connect(shader->OnShaderUniformInvalidated, this, &ForwardRenderTechnique::OnShaderInvalidated);
uniforms.eyePosition = shader->GetUniformLocation("EyePosition");
uniforms.reflectionMap = shader->GetUniformLocation("ReflectionMap");
uniforms.sceneAmbient = shader->GetUniformLocation("SceneAmbient");
uniforms.textureOverlay = shader->GetUniformLocation("TextureOverlay");
int type0Location = shader->GetUniformLocation("Lights[0].type");
int type1Location = shader->GetUniformLocation("Lights[1].type");
if (type0Location > 0 && type1Location > 0)
{
uniforms.hasLightUniforms = true;
uniforms.lightOffset = type1Location - type0Location;
uniforms.lightUniforms.ubo = false;
uniforms.lightUniforms.locations.type = type0Location;
uniforms.lightUniforms.locations.color = shader->GetUniformLocation("Lights[0].color");
uniforms.lightUniforms.locations.factors = shader->GetUniformLocation("Lights[0].factors");
uniforms.lightUniforms.locations.lightViewProjMatrix = shader->GetUniformLocation("LightViewProjMatrix[0]");
uniforms.lightUniforms.locations.parameters1 = shader->GetUniformLocation("Lights[0].parameters1");
uniforms.lightUniforms.locations.parameters2 = shader->GetUniformLocation("Lights[0].parameters2");
uniforms.lightUniforms.locations.parameters3 = shader->GetUniformLocation("Lights[0].parameters3");
uniforms.lightUniforms.locations.shadowMapping = shader->GetUniformLocation("Lights[0].shadowMapping");
}
else
uniforms.hasLightUniforms = false;
it = m_shaderUniforms.emplace(shader, std::move(uniforms)).first;
}
return &it->second;
}
/*!
* \brief Handle the invalidation of a shader
*
* \param shader Shader being invalidated
*/
void ForwardRenderTechnique::OnShaderInvalidated(const Shader* shader) const
{
m_shaderUniforms.erase(shader);
}
/*!
* \brief Sends the uniforms for light
*
* \param shader Shader to send uniforms to
* \param uniforms Uniforms to send
* \param index Index of the light
* \param uniformOffset Offset for the uniform
* \param availableTextureUnit Unit texture available
*/
void ForwardRenderTechnique::SendLightUniforms(const Shader* shader, const LightUniforms& uniforms, unsigned int index, unsigned int lightIndex, unsigned int uniformOffset) const
{
if (lightIndex < m_lights.size())
{
const LightIndex& lightInfo = m_lights[lightIndex];
shader->SendInteger(uniforms.locations.type + uniformOffset, lightInfo.type); //< Sends the light type
switch (lightInfo.type)
{
case LightType_Directional:
{
const auto& light = m_renderQueue.directionalLights[lightInfo.index];
shader->SendColor(uniforms.locations.color + uniformOffset, light.color);
shader->SendVector(uniforms.locations.factors + uniformOffset, Vector2f(light.ambientFactor, light.diffuseFactor));
shader->SendVector(uniforms.locations.parameters1 + uniformOffset, Vector4f(light.direction));
if (uniforms.locations.shadowMapping != -1)
shader->SendBoolean(uniforms.locations.shadowMapping + uniformOffset, light.shadowMap != nullptr);
if (light.shadowMap)
{
unsigned int textureUnit2D = Material::GetTextureUnit(static_cast<TextureMap>(TextureMap_Shadow2D_1 + index));
Renderer::SetTexture(textureUnit2D, light.shadowMap);
Renderer::SetTextureSampler(textureUnit2D, s_shadowSampler);
if (uniforms.locations.lightViewProjMatrix != -1)
shader->SendMatrix(uniforms.locations.lightViewProjMatrix + index, light.transformMatrix);
}
break;
}
case LightType_Point:
{
const auto& light = m_renderQueue.pointLights[lightInfo.index];
shader->SendColor(uniforms.locations.color + uniformOffset, light.color);
shader->SendVector(uniforms.locations.factors + uniformOffset, Vector2f(light.ambientFactor, light.diffuseFactor));
shader->SendVector(uniforms.locations.parameters1 + uniformOffset, Vector4f(light.position, light.attenuation));
shader->SendVector(uniforms.locations.parameters2 + uniformOffset, Vector4f(0.f, 0.f, 0.f, light.invRadius));
if (uniforms.locations.shadowMapping != -1)
shader->SendBoolean(uniforms.locations.shadowMapping + uniformOffset, light.shadowMap != nullptr);
if (light.shadowMap)
{
unsigned int textureUnitCube = Material::GetTextureUnit(static_cast<TextureMap>(TextureMap_ShadowCube_1 + index));
Renderer::SetTexture(textureUnitCube, light.shadowMap);
Renderer::SetTextureSampler(textureUnitCube, s_shadowSampler);
}
break;
}
case LightType_Spot:
{
const auto& light = m_renderQueue.spotLights[lightInfo.index];
shader->SendColor(uniforms.locations.color + uniformOffset, light.color);
shader->SendVector(uniforms.locations.factors + uniformOffset, Vector2f(light.ambientFactor, light.diffuseFactor));
shader->SendVector(uniforms.locations.parameters1 + uniformOffset, Vector4f(light.position, light.attenuation));
shader->SendVector(uniforms.locations.parameters2 + uniformOffset, Vector4f(light.direction, light.invRadius));
shader->SendVector(uniforms.locations.parameters3 + uniformOffset, Vector2f(light.innerAngleCosine, light.outerAngleCosine));
if (uniforms.locations.shadowMapping != -1)
shader->SendBoolean(uniforms.locations.shadowMapping + uniformOffset, light.shadowMap != nullptr);
if (light.shadowMap)
{
unsigned int textureUnit2D = Material::GetTextureUnit(static_cast<TextureMap>(TextureMap_Shadow2D_1 + index));
Renderer::SetTexture(textureUnit2D, light.shadowMap);
Renderer::SetTextureSampler(textureUnit2D, s_shadowSampler);
if (uniforms.locations.lightViewProjMatrix != -1)
shader->SendMatrix(uniforms.locations.lightViewProjMatrix + index, light.transformMatrix);
}
break;
}
}
}
else
{
if (uniforms.locations.type != -1)
shader->SendInteger(uniforms.locations.type + uniformOffset, -1); //< Disable the light in the shader
}
}
IndexBuffer ForwardRenderTechnique::s_quadIndexBuffer;
TextureSampler ForwardRenderTechnique::s_reflectionSampler;
TextureSampler ForwardRenderTechnique::s_shadowSampler;
VertexBuffer ForwardRenderTechnique::s_quadVertexBuffer;
VertexDeclaration ForwardRenderTechnique::s_billboardInstanceDeclaration;
VertexDeclaration ForwardRenderTechnique::s_billboardVertexDeclaration;
}

View File

@@ -1,269 +0,0 @@
// Copyright (C) 2020 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/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/DeferredRenderTechnique.hpp>
#include <Nazara/Graphics/DepthRenderTechnique.hpp>
#include <Nazara/Graphics/ForwardRenderTechnique.hpp>
#include <Nazara/Graphics/GuillotineTextureAtlas.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/ParticleController.hpp>
#include <Nazara/Graphics/ParticleDeclaration.hpp>
#include <Nazara/Graphics/ParticleGenerator.hpp>
#include <Nazara/Graphics/ParticleRenderer.hpp>
#include <Nazara/Graphics/RenderTechniques.hpp>
#include <Nazara/Graphics/SkinningManager.hpp>
#include <Nazara/Graphics/SkyboxBackground.hpp>
#include <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Graphics/TileMap.hpp>
#include <Nazara/Graphics/Formats/MeshLoader.hpp>
#include <Nazara/Graphics/Formats/TextureLoader.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Utility/Font.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Graphics
* \brief Graphics class that represents the module initializer of Graphics
*/
/*!
* \brief Initializes the Graphics module
* \return true if initialization is successful
*
* \remark Produces a NazaraNotice
* \remark Produces a NazaraError if one submodule failed
*/
bool Graphics::Initialize()
{
if (IsInitialized())
{
s_moduleReferenceCounter++;
return true; // Already initialized
}
// Initialisation of dependances
if (!Renderer::Initialize())
{
NazaraError("Failed to initialize Renderer module");
return false;
}
s_moduleReferenceCounter++;
// Initialisation of the module
CallOnExit onExit(Graphics::Uninitialize);
// Materials
if (!MaterialPipeline::Initialize())
{
NazaraError("Failed to initialize material pipelines");
return false;
}
if (!Material::Initialize())
{
NazaraError("Failed to initialize materials");
return false;
}
// Renderables
if (!ParticleController::Initialize())
{
NazaraError("Failed to initialize particle controllers");
return false;
}
if (!ParticleDeclaration::Initialize())
{
NazaraError("Failed to initialize particle declarations");
return false;
}
if (!ParticleGenerator::Initialize())
{
NazaraError("Failed to initialize particle generators");
return false;
}
if (!ParticleRenderer::Initialize())
{
NazaraError("Failed to initialize particle renderers");
return false;
}
if (!SkinningManager::Initialize())
{
NazaraError("Failed to initialize skinning manager");
return false;
}
if (!SkyboxBackground::Initialize())
{
NazaraError("Failed to initialize skybox backgrounds");
return false;
}
if (!Sprite::Initialize())
{
NazaraError("Failed to initialize sprites");
return false;
}
if (!TileMap::Initialize())
{
NazaraError("Failed to initialize tilemaps");
return false;
}
// Generic loaders
Loaders::RegisterMesh();
Loaders::RegisterTexture();
// Render techniques
if (!DepthRenderTechnique::Initialize())
{
NazaraError("Failed to initialize Depth Rendering");
return false;
}
if (!ForwardRenderTechnique::Initialize())
{
NazaraError("Failed to initialize Forward Rendering");
return false;
}
RenderTechniques::Register(RenderTechniques::ToString(RenderTechniqueType_BasicForward), 0, []() -> AbstractRenderTechnique* { return new ForwardRenderTechnique; });
if (DeferredRenderTechnique::IsSupported())
{
if (DeferredRenderTechnique::Initialize())
RenderTechniques::Register(RenderTechniques::ToString(RenderTechniqueType_DeferredShading), 20, []() -> AbstractRenderTechnique* { return new DeferredRenderTechnique; });
else
{
NazaraWarning("Failed to initialize Deferred Rendering");
}
}
Font::SetDefaultAtlas(std::make_shared<GuillotineTextureAtlas>());
// Textures
std::array<UInt8, 6> whitePixels = { { 255, 255, 255, 255, 255, 255 } };
Nz::TextureRef whiteTexture = Nz::Texture::New();
whiteTexture->Create(ImageType_2D, PixelFormat_L8, 1, 1);
whiteTexture->Update(whitePixels.data());
TextureLibrary::Register("White2D", std::move(whiteTexture));
Nz::TextureRef whiteCubemap = Nz::Texture::New();
whiteCubemap->Create(ImageType_Cubemap, PixelFormat_L8, 1, 1);
whiteCubemap->Update(whitePixels.data());
TextureLibrary::Register("WhiteCubemap", std::move(whiteCubemap));
onExit.Reset();
NazaraNotice("Initialized: Graphics module");
return true;
}
/*!
* \brief Checks whether the module is initialized
* \return true if module is initialized
*/
bool Graphics::IsInitialized()
{
return s_moduleReferenceCounter != 0;
}
/*!
* \brief Uninitializes the Graphics module
*
* \remark Produces a NazaraNotice
*/
void Graphics::Uninitialize()
{
if (s_moduleReferenceCounter != 1)
{
// The module is still in use, or can not be uninitialized
if (s_moduleReferenceCounter > 1)
s_moduleReferenceCounter--;
return;
}
// Free of module
s_moduleReferenceCounter = 0;
// Free of atlas if it is ours
std::shared_ptr<AbstractAtlas> defaultAtlas = Font::GetDefaultAtlas();
if (defaultAtlas && defaultAtlas->GetStorage() == DataStorage_Hardware)
{
Font::SetDefaultAtlas(nullptr);
// The default police can make live one hardware atlas after the free of a module (which could be problematic)
// So, if the default police use a hardware atlas, we stole it.
// I don't like this solution, but I don't have any better
if (!defaultAtlas.unique())
{
// Still at least one police use the atlas
Font* defaultFont = Font::GetDefault();
defaultFont->SetAtlas(nullptr);
if (!defaultAtlas.unique())
{
// Still not the only one to own it ? Then crap.
NazaraWarning("Default font atlas uses hardware storage and is still used");
}
}
}
defaultAtlas.reset();
// Textures
TextureLibrary::Unregister("White2D");
TextureLibrary::Unregister("WhiteCubemap");
// Loaders
Loaders::UnregisterMesh();
Loaders::UnregisterTexture();
// Renderables
ParticleRenderer::Uninitialize();
ParticleGenerator::Uninitialize();
ParticleDeclaration::Uninitialize();
ParticleController::Uninitialize();
SkyboxBackground::Uninitialize();
Sprite::Uninitialize();
TileMap::Uninitialize();
// Render techniques
DeferredRenderTechnique::Uninitialize();
DepthRenderTechnique::Uninitialize();
ForwardRenderTechnique::Uninitialize();
SkinningManager::Uninitialize();
// Materials
Material::Uninitialize();
MaterialPipeline::Uninitialize();
NazaraNotice("Uninitialized: Graphics module");
// Free of dependances
Renderer::Uninitialize();
}
unsigned int Graphics::s_moduleReferenceCounter = 0;
}

View File

@@ -1,70 +0,0 @@
// Copyright (C) 2020 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/GuillotineTextureAtlas.hpp>
#include <Nazara/Renderer/Texture.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::GuillotineTextureAtlas
* \brief Graphics class that represents an atlas texture for guillotine
*/
/*!
* \brief Gets the underlying data storage
* \return Value of the enumeration of the underlying data storage
*/
UInt32 GuillotineTextureAtlas::GetStorage() const
{
return DataStorage_Hardware;
}
/*!
* \brief Resizes the image
* \return Updated texture
*
* \param oldImage Old image to resize
* \param size New image size
*
* \remark Produces a NazaraError if resize failed
*/
AbstractImage* GuillotineTextureAtlas::ResizeImage(AbstractImage* oldImage, const Vector2ui& size) const
{
std::unique_ptr<Texture> newTexture(new Texture);
if (newTexture->Create(ImageType_2D, PixelFormat_A8, size.x, size.y, 1))
{
if (oldImage)
{
Texture* oldTexture = static_cast<Texture*>(oldImage);
// Copy of old data
///TODO: Copy from texture to texture
Image image;
if (!oldTexture->Download(&image))
{
NazaraError("Failed to download old texture");
return nullptr;
}
if (!newTexture->Update(&image, Rectui(0, 0, image.GetWidth(), image.GetHeight())))
{
NazaraError("Failed to update texture");
return nullptr;
}
}
return newTexture.release();
}
else
{
// If we are here, it is that the size is too big for the graphic card or we don't have enough
return nullptr;
}
}
}

View File

@@ -1,101 +0,0 @@
// Copyright (C) 2020 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/InstancedRenderable.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::InstancedRenderable
* \brief Graphics class that represents an instancer renderable
*
* \remark This class is abstract
*/
/*!
* \brief Destructs the object and calls OnInstancedRenderableRelease
*
* \see OnInstancedRenderableRelease
*/
InstancedRenderable::~InstancedRenderable()
{
OnInstancedRenderableRelease(this);
}
/*!
* \brief Culls the instanced if not in the frustum
* \return true If instanced is in the frustum
*
* \param frustum Symbolizing the field of view
* \param transformMatrix Matrix transformation for our object
*/
bool InstancedRenderable::Cull(const Frustumf& frustum, const InstanceData& instanceData) const
{
return frustum.Contains(instanceData.volume);
}
/*!
* \brief Gets the bounding volume
* \return Bounding volume of the instanced
*/
const BoundingVolumef& InstancedRenderable::GetBoundingVolume() const
{
EnsureBoundingVolumeUpdated();
return m_boundingVolume;
}
/*!
* \brief Invalidates data for instanced
*
* \param instanceData Pointer to data of instances
* \param flags Flags for the instances
*
* \remark Produces a NazaraAssert if instanceData is invalid
*/
void InstancedRenderable::InvalidateData(InstanceData* instanceData, UInt32 flags) const
{
NazaraAssert(instanceData, "Invalid instance data");
instanceData->flags |= flags;
}
/*!
* \brief Updates the bounding volume
*
* \param instanceData Pointer to data of instances
*
* \remark Produces a NazaraAssert if instanceData is invalid
*/
void InstancedRenderable::UpdateBoundingVolume(InstanceData* instanceData) const
{
NazaraAssert(instanceData, "Invalid instance data");
NazaraUnused(instanceData);
instanceData->volume.Update(instanceData->transformMatrix);
}
/*!
* \brief Updates the instance data
*
* \param instanceData Pointer to data of instances
*
* \remark Produces a NazaraAssert if instanceData is invalid
*/
void InstancedRenderable::UpdateData(InstanceData* instanceData) const
{
NazaraAssert(instanceData, "Invalid instance data");
NazaraUnused(instanceData);
}
InstancedRenderableLibrary::LibraryMap InstancedRenderable::s_library;
}

View File

@@ -1,267 +0,0 @@
// Copyright (C) 2020 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/Light.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Math/Algorithm.hpp>
#include <Nazara/Math/Sphere.hpp>
#include <Nazara/Graphics/Debug.hpp>
///TODO: Use of UBOs
///TODO: Scale ?
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Light
* \brief Graphics class that represents a light
*/
/*!
* \brief Constructs a Light object with a type
*
* \param type Type of the light
*/
Light::Light(LightType type) :
m_type(type),
m_shadowMapFormat(PixelFormat_Depth16),
m_shadowMapSize(512, 512),
m_shadowCastingEnabled(false),
m_shadowMapUpdated(false)
{
SetAmbientFactor((type == LightType_Directional) ? 0.2f : 0.f);
SetAttenuation(0.9f);
SetColor(Color::White);
SetDiffuseFactor(1.f);
SetInnerAngle(15.f);
SetOuterAngle(45.f);
SetRadius(5.f);
}
/*!
* \brief Adds this light to the render queue
*
* \param renderQueue Queue to be added
* \param transformMatrix Matrix transformation for this light
*
* \remark Produces a NazaraError if type is invalid
*/
void Light::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const
{
static Matrix4f biasMatrix(0.5f, 0.f, 0.f, 0.f,
0.f, 0.5f, 0.f, 0.f,
0.f, 0.f, 0.5f, 0.f,
0.5f, 0.5f, 0.5f, 1.f);
switch (m_type)
{
case LightType_Directional:
{
AbstractRenderQueue::DirectionalLight light;
light.ambientFactor = m_ambientFactor;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.direction = transformMatrix.Transform(Vector3f::Forward(), 0.f);
light.shadowMap = m_shadowMap.Get();
light.transformMatrix = Matrix4f::ViewMatrix(transformMatrix.GetRotation() * Vector3f::Forward() * 100.f, transformMatrix.GetRotation()) * Matrix4f::Ortho(0.f, 100.f, 100.f, 0.f, 1.f, 100.f) * biasMatrix;
renderQueue->AddDirectionalLight(light);
break;
}
case LightType_Point:
{
AbstractRenderQueue::PointLight light;
light.ambientFactor = m_ambientFactor;
light.attenuation = m_attenuation;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.invRadius = m_invRadius;
light.position = transformMatrix.GetTranslation();
light.radius = m_radius;
light.shadowMap = m_shadowMap.Get();
renderQueue->AddPointLight(light);
break;
}
case LightType_Spot:
{
AbstractRenderQueue::SpotLight light;
light.ambientFactor = m_ambientFactor;
light.attenuation = m_attenuation;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.direction = transformMatrix.Transform(Vector3f::Forward(), 0.f);
light.innerAngleCosine = m_innerAngleCosine;
light.invRadius = m_invRadius;
light.outerAngleCosine = m_outerAngleCosine;
light.outerAngleTangent = m_outerAngleTangent;
light.position = transformMatrix.GetTranslation();
light.radius = m_radius;
light.shadowMap = m_shadowMap.Get();
light.transformMatrix = Matrix4f::ViewMatrix(transformMatrix.GetTranslation(), transformMatrix.GetRotation()) * Matrix4f::Perspective(m_outerAngle*2.f, 1.f, 0.1f, m_radius) * biasMatrix;
renderQueue->AddSpotLight(light);
break;
}
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
/*!
* \brief Clones this light
* \return Pointer to newly allocated Light
*/
Light* Light::Clone() const
{
return new Light(*this);
}
/*!
* \brief Creates a default light
* \return Pointer to newly allocated light
*/
Light* Light::Create() const
{
return new Light;
}
/*!
* \brief Culls the light if not in the frustum
* \return true If light is in the frustum
*
* \param frustum Symbolizing the field of view
* \param transformMatrix Matrix transformation for our object
*
* \remark Produces a NazaraError if type is invalid
*/
bool Light::Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const
{
switch (m_type)
{
case LightType_Directional:
return true; // Always visible
case LightType_Point:
return frustum.Contains(Spheref(transformMatrix.GetTranslation(), m_radius)); // A sphere test is much faster (and precise)
case LightType_Spot:
return frustum.Contains(m_boundingVolume);
}
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
return false;
}
/*!
* \brief Updates the bounding volume by a matrix
*
* \param transformMatrix Matrix transformation for our bounding volume
*
* \remark Produces a NazaraError if type is invalid
*/
void Light::UpdateBoundingVolume(const Matrix4f& transformMatrix)
{
switch (m_type)
{
case LightType_Directional:
break; // Nothing to do (bounding volume should be infinite)
case LightType_Point:
m_boundingVolume.Update(transformMatrix.GetTranslation()); // The bounding volume only needs to be shifted
break;
case LightType_Spot:
m_boundingVolume.Update(transformMatrix);
break;
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
/*
* \brief Makes the bounding volume of this light
*
* \remark Produces a NazaraError if type is invalid
*/
void Light::MakeBoundingVolume() const
{
switch (m_type)
{
case LightType_Directional:
m_boundingVolume.MakeInfinite();
break;
case LightType_Point:
{
Vector3f radius(m_radius * float(M_SQRT3));
m_boundingVolume.Set(-radius, radius);
break;
}
case LightType_Spot:
{
// We make a box center in the origin
Boxf box(Vector3f::Zero());
// We compute the other points
Vector3f base(Vector3f::Forward() * m_radius);
// Now we need the radius of the projected circle depending on the distance
// Tangent = Opposite/Adjacent <=> Opposite = Adjacent * Tangent
float radius = m_radius * m_outerAngleTangent;
Vector3f lExtend = Vector3f::Left() * radius;
Vector3f uExtend = Vector3f::Up() * radius;
// And we add the four extremities of our pyramid
box.ExtendTo(base + lExtend + uExtend);
box.ExtendTo(base + lExtend - uExtend);
box.ExtendTo(base - lExtend + uExtend);
box.ExtendTo(base - lExtend - uExtend);
m_boundingVolume.Set(box);
break;
}
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
/*!
* \brief Updates the shadow map
*/
void Light::UpdateShadowMap() const
{
if (m_shadowCastingEnabled)
{
if (!m_shadowMap)
m_shadowMap = Texture::New();
m_shadowMap->Create((m_type == LightType_Point) ? ImageType_Cubemap : ImageType_2D, m_shadowMapFormat, m_shadowMapSize.x, m_shadowMapSize.y);
}
else
m_shadowMap.Reset();
m_shadowMapUpdated = true;
}
}

View File

@@ -1,503 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Utility/MaterialData.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Material
* \brief Graphics class that represents a material
*/
/*!
* \brief Checks whether the parameters for the material are correct
* \return true If parameters are valid
*/
bool MaterialParams::IsValid() const
{
if (!UberShaderLibrary::Has(shaderName))
return false;
return true;
}
/*!
* \brief Applies shader to the material
*
* \param instance Pipeline instance to update
* \param textureUnit Unit for the texture GL_TEXTURE"i"
* \param lastUsedUnit Optional argument to get the last texture unit
*/
void Material::Apply(const MaterialPipeline::Instance& instance) const
{
const Shader* shader = instance.renderPipeline.GetInfo().shader;
if (instance.uniforms[MaterialUniform_AlphaThreshold] != -1)
shader->SendFloat(instance.uniforms[MaterialUniform_AlphaThreshold], m_alphaThreshold);
if (instance.uniforms[MaterialUniform_Ambient] != -1)
shader->SendColor(instance.uniforms[MaterialUniform_Ambient], m_ambientColor);
if (instance.uniforms[MaterialUniform_Diffuse] != -1)
shader->SendColor(instance.uniforms[MaterialUniform_Diffuse], m_diffuseColor);
if (instance.uniforms[MaterialUniform_Shininess] != -1)
shader->SendFloat(instance.uniforms[MaterialUniform_Shininess], m_shininess);
if (instance.uniforms[MaterialUniform_Specular] != -1)
shader->SendColor(instance.uniforms[MaterialUniform_Specular], m_specularColor);
if (m_alphaMap && instance.uniforms[MaterialUniform_AlphaMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Alpha];
Renderer::SetTexture(textureUnit, m_alphaMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_diffuseMap && instance.uniforms[MaterialUniform_DiffuseMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Diffuse];
Renderer::SetTexture(textureUnit, m_diffuseMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_emissiveMap && instance.uniforms[MaterialUniform_EmissiveMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Emissive];
Renderer::SetTexture(textureUnit, m_emissiveMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_heightMap && instance.uniforms[MaterialUniform_HeightMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Height];
Renderer::SetTexture(textureUnit, m_heightMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_normalMap && instance.uniforms[MaterialUniform_NormalMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Normal];
Renderer::SetTexture(textureUnit, m_normalMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
}
if (m_specularMap && instance.uniforms[MaterialUniform_SpecularMap] != -1)
{
unsigned int textureUnit = s_textureUnits[TextureMap_Specular];
Renderer::SetTexture(textureUnit, m_specularMap);
Renderer::SetTextureSampler(textureUnit, m_specularSampler);
}
}
/*!
* \brief Builds the material from a parameter list
*
* \param matData Data information for the material
* \param matParams Additional parameters for the material
*/
void Material::BuildFromParameters(const ParameterList& matData, const MaterialParams& matParams)
{
Color color;
bool isEnabled;
double dValue;
long long iValue;
Nz::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.ToStdString());
if (matParams.loadDiffuseMap && matData.GetStringParameter(MaterialData::DiffuseTexturePath, &path))
SetDiffuseMap(path.ToStdString());
if (matParams.loadEmissiveMap && matData.GetStringParameter(MaterialData::EmissiveTexturePath, &path))
SetEmissiveMap(path.ToStdString());
if (matParams.loadHeightMap && matData.GetStringParameter(MaterialData::HeightTexturePath, &path))
SetHeightMap(path.ToStdString());
if (matParams.loadNormalMap && matData.GetStringParameter(MaterialData::NormalTexturePath, &path))
SetNormalMap(path.ToStdString());
if (matParams.loadSpecularMap && matData.GetStringParameter(MaterialData::SpecularTexturePath, &path))
SetSpecularMap(path.ToStdString());
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 std::filesystem::path& path = GetAlphaMap()->GetFilePath();
if (!path.empty())
matData->SetParameter(MaterialData::AlphaTexturePath, path.generic_u8string());
}
if (HasDiffuseMap())
{
const std::filesystem::path& path = GetDiffuseMap()->GetFilePath();
if (!path.empty())
matData->SetParameter(MaterialData::DiffuseTexturePath, path.generic_u8string());
}
if (HasEmissiveMap())
{
const std::filesystem::path& path = GetEmissiveMap()->GetFilePath();
if (!path.empty())
matData->SetParameter(MaterialData::EmissiveTexturePath, path.generic_u8string());
}
if (HasHeightMap())
{
const std::filesystem::path& path = GetHeightMap()->GetFilePath();
if (!path.empty())
matData->SetParameter(MaterialData::HeightTexturePath, path.generic_u8string());
}
if (HasNormalMap())
{
const std::filesystem::path& path = GetNormalMap()->GetFilePath();
if (!path.empty())
matData->SetParameter(MaterialData::NormalTexturePath, path.generic_u8string());
}
if (HasSpecularMap())
{
const std::filesystem::path& path = GetSpecularMap()->GetFilePath();
if (!path.empty())
matData->SetParameter(MaterialData::SpecularTexturePath, path.generic_u8string());
}
}
/*!
* \brief Resets the material, cleans everything
*
* \remark Invalidates the pipeline
*/
void Material::Reset()
{
OnMaterialReset(this);
m_alphaMap.Reset();
m_depthMaterial.Reset();
m_diffuseMap.Reset();
m_emissiveMap.Reset();
m_heightMap.Reset();
m_normalMap.Reset();
m_specularMap.Reset();
m_alphaThreshold = 0.2f;
m_ambientColor = Color(128, 128, 128);
m_diffuseColor = Color::White;
m_diffuseSampler = TextureSampler();
m_reflectionMode = ReflectionMode_Skybox;
m_shadowCastingEnabled = true;
m_shininess = 50.f;
m_specularColor = Color::White;
m_specularSampler = TextureSampler();
m_pipelineInfo = MaterialPipelineInfo();
m_pipelineInfo.depthBuffer = true;
m_pipelineInfo.faceCulling = true;
m_reflectionSize = 256;
SetShader("Basic");
InvalidatePipeline();
}
/*!
* \brief Copies the other material
*
* \param material Material to copy into this
*/
void Material::Copy(const Material& material)
{
// Copy of base states
m_alphaThreshold = material.m_alphaThreshold;
m_ambientColor = material.m_ambientColor;
m_diffuseColor = material.m_diffuseColor;
m_diffuseSampler = material.m_diffuseSampler;
m_pipelineInfo = material.m_pipelineInfo;
m_shininess = material.m_shininess;
m_shadowCastingEnabled = material.m_shadowCastingEnabled;
m_specularColor = material.m_specularColor;
m_specularSampler = material.m_specularSampler;
// Copy of reference to the textures
m_alphaMap = material.m_alphaMap;
m_depthMaterial = material.m_depthMaterial;
m_diffuseMap = material.m_diffuseMap;
m_emissiveMap = material.m_emissiveMap;
m_heightMap = material.m_heightMap;
m_normalMap = material.m_normalMap;
m_specularMap = material.m_specularMap;
SetReflectionMode(material.GetReflectionMode());
InvalidatePipeline();
}
/*!
* \brief Initializes the material librairies
* \return true If successful
*
* \remark Produces a NazaraError if the material library failed to be initialized
*/
bool Material::Initialize()
{
if (!MaterialLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
if (!MaterialManager::Initialize())
{
NazaraError("Failed to initialise manager");
return false;
}
s_defaultMaterial = New();
s_defaultMaterial->EnableFaceCulling(false);
s_defaultMaterial->SetFaceFilling(FaceFilling_Line);
MaterialLibrary::Register("Default", s_defaultMaterial);
unsigned int textureUnit = 0;
s_textureUnits[TextureMap_Diffuse] = textureUnit++;
s_textureUnits[TextureMap_Alpha] = textureUnit++;
s_textureUnits[TextureMap_Specular] = textureUnit++;
s_textureUnits[TextureMap_Normal] = textureUnit++;
s_textureUnits[TextureMap_Emissive] = textureUnit++;
s_textureUnits[TextureMap_Overlay] = textureUnit++;
s_textureUnits[TextureMap_ReflectionCube] = textureUnit++;
s_textureUnits[TextureMap_Height] = textureUnit++;
s_textureUnits[TextureMap_Shadow2D_1] = textureUnit++;
s_textureUnits[TextureMap_ShadowCube_1] = textureUnit++;
s_textureUnits[TextureMap_Shadow2D_2] = textureUnit++;
s_textureUnits[TextureMap_ShadowCube_2] = textureUnit++;
s_textureUnits[TextureMap_Shadow2D_3] = textureUnit++;
s_textureUnits[TextureMap_ShadowCube_3] = textureUnit++;
return true;
}
/*!
* \brief Uninitializes the material librairies
*/
void Material::Uninitialize()
{
s_defaultMaterial.Reset();
MaterialManager::Uninitialize();
MaterialLibrary::Uninitialize();
}
std::array<int, TextureMap_Max + 1> Material::s_textureUnits;
MaterialLibrary::LibraryMap Material::s_library;
MaterialLoader::LoaderList Material::s_loaders;
MaterialManager::ManagerMap Material::s_managerMap;
MaterialManager::ManagerParams Material::s_managerParameters;
MaterialRef Material::s_defaultMaterial = nullptr;
}

View File

@@ -1,235 +0,0 @@
// Copyright (C) 2020 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/Material.hpp>
#include <Nazara/Renderer/UberShaderPreprocessor.hpp>
#include <filesystem>
#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 std::filesystem::path& 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.generic_u8string() + " 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.uberShader, "Material pipeline has no uber shader");
ParameterList list;
list.SetParameter("ALPHA_MAPPING", m_pipelineInfo.hasAlphaMap);
list.SetParameter("ALPHA_TEST", m_pipelineInfo.alphaTest);
list.SetParameter("COMPUTE_TBNMATRIX", m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap);
list.SetParameter("DIFFUSE_MAPPING", m_pipelineInfo.hasDiffuseMap);
list.SetParameter("EMISSIVE_MAPPING", m_pipelineInfo.hasEmissiveMap);
list.SetParameter("NORMAL_MAPPING", m_pipelineInfo.hasNormalMap);
list.SetParameter("PARALLAX_MAPPING", m_pipelineInfo.hasHeightMap);
list.SetParameter("REFLECTION_MAPPING", m_pipelineInfo.reflectionMapping);
list.SetParameter("SHADOW_MAPPING", m_pipelineInfo.shadowReceive);
list.SetParameter("SPECULAR_MAPPING", m_pipelineInfo.hasSpecularMap);
list.SetParameter("TEXTURE_MAPPING", m_pipelineInfo.hasAlphaMap || m_pipelineInfo.hasDiffuseMap || m_pipelineInfo.hasEmissiveMap ||
m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap || m_pipelineInfo.hasSpecularMap ||
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);
#define CacheUniform(name) instance.uniforms[MaterialUniform_##name] = renderPipelineInfo.shader->GetUniformLocation("Material" #name)
CacheUniform(AlphaMap);
CacheUniform(AlphaThreshold);
CacheUniform(Ambient);
CacheUniform(Diffuse);
CacheUniform(DiffuseMap);
CacheUniform(EmissiveMap);
CacheUniform(HeightMap);
CacheUniform(NormalMap);
CacheUniform(Shininess);
CacheUniform(Specular);
CacheUniform(SpecularMap);
#undef CacheUniform
// Send texture units (those never changes)
renderPipelineInfo.shader->SendInteger(instance.uniforms[MaterialUniform_AlphaMap], Material::GetTextureUnit(TextureMap_Alpha));
renderPipelineInfo.shader->SendInteger(instance.uniforms[MaterialUniform_DiffuseMap], Material::GetTextureUnit(TextureMap_Diffuse));
renderPipelineInfo.shader->SendInteger(instance.uniforms[MaterialUniform_EmissiveMap], Material::GetTextureUnit(TextureMap_Emissive));
renderPipelineInfo.shader->SendInteger(instance.uniforms[MaterialUniform_HeightMap], Material::GetTextureUnit(TextureMap_Height));
renderPipelineInfo.shader->SendInteger(instance.uniforms[MaterialUniform_NormalMap], Material::GetTextureUnit(TextureMap_Normal));
renderPipelineInfo.shader->SendInteger(instance.uniforms[MaterialUniform_SpecularMap], Material::GetTextureUnit(TextureMap_Specular));
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("ReflectionMap"), Material::GetTextureUnit(TextureMap_ReflectionCube));
renderPipelineInfo.shader->SendInteger(renderPipelineInfo.shader->GetUniformLocation("TextureOverlay"), Material::GetTextureUnit(TextureMap_Overlay));
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 ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING TEXTURE_MAPPING");
uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
UberShaderLibrary::Register("Basic", uberShader);
}
// 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_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING EMISSIVE_MAPPING NORMAL_MAPPING PARALLAX_MAPPING REFLECTION_MAPPING SHADOW_MAPPING SPECULAR_MAPPING");
uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR COMPUTE_TBNMATRIX PARALLAX_MAPPING SHADOW_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
UberShaderLibrary::Register("PhongLighting", uberShader);
}
// Once the base shaders are registered, we can now set some default materials
MaterialPipelineInfo pipelineInfo;
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");
UberShaderLibrary::Unregister("Basic");
MaterialPipelineLibrary::Uninitialize();
}
MaterialPipelineLibrary::LibraryMap MaterialPipeline::s_library;
MaterialPipeline::PipelineCache MaterialPipeline::s_pipelineCache;
}

View File

@@ -1,289 +0,0 @@
// Copyright (C) 2020 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/Model.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Utility/MeshData.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Model
* \brief Graphics class that represents a model
*/
/*!
* \brief Constructs a ModelParameters object by default
*/
ModelParameters::ModelParameters()
{
material.shaderName = "PhongLighting";
}
/*!
* \brief Checks whether the parameters for the model are correct
* \return true If parameters are valid
*/
bool ModelParameters::IsValid() const
{
if (loadMaterials && !material.IsValid())
return false;
return mesh.IsValid();
}
/*!
* \brief Destructs the object and cleans resources
*/
Model::~Model() = default;
/*!
* \brief Adds this model to the render queue
*
* \param renderQueue Queue to be added
* \param instanceData Data used for this instance
*/
void Model::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const
{
unsigned int submeshCount = m_mesh->GetSubMeshCount();
for (unsigned int i = 0; i < submeshCount; ++i)
{
const StaticMesh* mesh = static_cast<const StaticMesh*>(m_mesh->GetSubMesh(i));
const MaterialRef& material = GetMaterial(mesh->GetMaterialIndex());
MeshData meshData;
meshData.indexBuffer = mesh->GetIndexBuffer();
meshData.primitiveMode = mesh->GetPrimitiveMode();
meshData.vertexBuffer = mesh->GetVertexBuffer();
renderQueue->AddMesh(instanceData.renderOrder, material, meshData, mesh->GetAABB(), instanceData.transformMatrix, scissorRect);
}
}
/*!
* \brief Clones this model
*/
std::unique_ptr<InstancedRenderable> Model::Clone() const
{
return std::make_unique<Model>(*this);
}
/*!
* \brief Gets the material of the named submesh
* \return Pointer to the current material
*
* \param subMeshName Name of the subMesh
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no mesh
* \remark Produces a NazaraError if there is no subMesh with that name
* \remark Produces a NazaraError if material is invalid
*/
const MaterialRef& Model::GetMaterial(const std::string& subMeshName) const
{
NazaraAssert(m_mesh, "Model has no mesh");
SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
static MaterialRef Invalid;
return Invalid;
}
return GetMaterial(subMesh->GetMaterialIndex());
}
/*!
* \brief Gets the material by index of the named submesh
* \return Pointer to the current material
*
* \param skinIndex Index of the skin
* \param subMeshName Name of the subMesh
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if skinIndex is invalid
* \remark Produces a NazaraError if there is no subMesh with that name
* \remark Produces a NazaraError if material index is invalid
*/
const MaterialRef& Model::GetMaterial(std::size_t skinIndex, const std::string& subMeshName) const
{
NazaraAssert(m_mesh, "Model has no mesh");
SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
static MaterialRef Invalid;
return Invalid;
}
return GetMaterial(subMesh->GetMaterialIndex());
}
/*!
* \brief Gets the mesh
* \return Current mesh
*/
Mesh* Model::GetMesh() const
{
return m_mesh;
}
/*!
* \brief Checks whether the model is animated
* \return false
*/
bool Model::IsAnimated() const
{
return false;
}
/*!
* \brief Sets the material of the named submesh
* \return true If successful
*
* \param subMeshName Name of the subMesh
* \param material Pointer to the material
*
* \remark Produces a NazaraError if there is no subMesh with that name
* \remark Produces a NazaraError if material index is invalid
*/
bool Model::SetMaterial(const std::string& subMeshName, MaterialRef material)
{
SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return false;
}
SetMaterial(subMesh->GetMaterialIndex(), std::move(material));
return true;
}
/*!
* \brief Sets the material by index of the named submesh
* \return true If successful
*
* \param skinIndex Index of the skin
* \param subMeshName Name of the subMesh
* \param material Pointer to the material
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if skinIndex is invalid
* \remark Produces a NazaraError if there is no subMesh with that name
* \remark Produces a NazaraError if material index is invalid
*/
bool Model::SetMaterial(std::size_t skinIndex, const std::string& subMeshName, MaterialRef material)
{
SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return false;
}
SetMaterial(skinIndex, subMesh->GetMaterialIndex(), std::move(material));
return true;
}
/*!
* \brief Sets the mesh
*
* \param pointer to the mesh
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if mesh is invalid
*/
void Model::SetMesh(Mesh* mesh)
{
#if NAZARA_GRAPHICS_SAFE
if (mesh && !mesh->IsValid())
{
NazaraError("Invalid mesh");
return;
}
#endif
m_mesh = mesh;
if (m_mesh)
{
ResetMaterials(mesh->GetMaterialCount());
m_meshAABBInvalidationSlot.Connect(m_mesh->OnMeshInvalidateAABB, [this](const Nz::Mesh*) { InvalidateBoundingVolume(); });
}
else
{
ResetMaterials(0);
m_meshAABBInvalidationSlot.Disconnect();
}
InvalidateBoundingVolume();
}
/*!
* \brief Loads the model from file
* \return true if loading is successful
*
* \param filePath Path to the file
* \param params Parameters for the model
*/
ModelRef Model::LoadFromFile(const std::filesystem::path& filePath, const ModelParameters& params)
{
return ModelLoader::LoadFromFile(filePath, params);
}
/*!
* \brief Loads the model from memory
* \return true if loading is successful
*
* \param data Raw memory
* \param size Size of the memory
* \param params Parameters for the model
*/
ModelRef Model::LoadFromMemory(const void* data, std::size_t size, const ModelParameters& params)
{
return ModelLoader::LoadFromMemory(data, size, params);
}
/*!
* \brief Loads the model from stream
* \return true if loading is successful
*
* \param stream Stream to the model
* \param params Parameters for the model
*/
ModelRef Model::LoadFromStream(Stream& stream, const ModelParameters& params)
{
return ModelLoader::LoadFromStream(stream, params);
}
/*
* \brief Makes the bounding volume of this billboard
*/
void Model::MakeBoundingVolume() const
{
if (m_mesh)
m_boundingVolume.Set(m_mesh->GetAABB());
else
m_boundingVolume.MakeNull();
}
ModelLibrary::LibraryMap Model::s_library;
ModelLoader::LoaderList Model::s_loaders;
ModelManager::ManagerMap Model::s_managerMap;
ModelManager::ManagerParams Model::s_managerParameters;
ModelSaver::SaverList Model::s_savers;
}

View File

@@ -1,69 +0,0 @@
// Copyright (C) 2020 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/ParticleController.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleController
* \brief Graphics class which controls a flow of particles
*
* \remark This class is abstract
*/
/*!
* \brief Constructs a ParticleController object by assignation
*
* \param controller ParticleController to copy into this
*/
ParticleController::ParticleController(const ParticleController& controller) :
RefCounted()
{
NazaraUnused(controller);
}
/*!
* \brief Destructs the object and calls OnParticleControllerRelease
*
* \see OnParticleControllerRelease
*/
ParticleController::~ParticleController()
{
OnParticleControllerRelease(this);
}
/*!
* \brief Initializes the particle controller librairies
* \return true If successful
*
* \remark Produces a NazaraError if the particle controller library failed to be initialized
*/
bool ParticleController::Initialize()
{
if (!ParticleControllerLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
return true;
}
/*!
* \brief Uninitializes the particle controller librairies
*/
void ParticleController::Uninitialize()
{
ParticleControllerLibrary::Uninitialize();
}
ParticleControllerLibrary::LibraryMap ParticleController::s_library;
}

View File

@@ -1,337 +0,0 @@
// Copyright (C) 2020 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/ParticleDeclaration.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/OffsetOf.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/ParticleStruct.hpp>
#include <Nazara/Utility/Utility.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleDeclaration
* \brief Graphics class that represents the declaration of the particle, works like an ECS
*/
/*!
* \brief Constructs a ParticleDeclaration object by default
*/
ParticleDeclaration::ParticleDeclaration() :
m_stride(0)
{
}
/*!
* \brief Constructs a ParticleDeclaration object by assignation
*
* \param declaration ParticleDeclaration to copy into this
*/
ParticleDeclaration::ParticleDeclaration(const ParticleDeclaration& declaration) :
RefCounted(),
m_components(declaration.m_components),
m_stride(declaration.m_stride)
{
}
/*!
* \brief Destructs the object and calls OnParticleDeclarationRelease
*
* \see OnParticleDeclarationRelease
*/
ParticleDeclaration::~ParticleDeclaration()
{
OnParticleDeclarationRelease(this);
}
/*!
* \brief Disables a component
*
* \param component Component to disable in the declaration
*
* \remark Produces a NazaraError with NAZARA_DEBUG defined if enumeration is invalid
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if enumeration is equal to ParticleComponent_Unused
*/
void ParticleDeclaration::DisableComponent(ParticleComponent component)
{
#ifdef NAZARA_DEBUG
if (component > ParticleComponent_Max)
{
NazaraError("Vertex component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (component == ParticleComponent_Unused)
{
NazaraError("Cannot disable \"unused\" component");
return;
}
#endif
Component& vertexComponent = m_components[component];
if (vertexComponent.enabled)
{
vertexComponent.enabled = false;
m_stride -= Utility::ComponentStride[vertexComponent.type];
}
}
/*!
* \brief Enables a component
*
* \param component Component to enable in the declaration
* \param type Type of this component
* \param offset Offset in the declaration
*
* \remark Produces a NazaraError with NAZARA_DEBUG defined if enumeration is invalid
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if type is not supported
*/
void ParticleDeclaration::EnableComponent(ParticleComponent component, ComponentType type, std::size_t offset)
{
#ifdef NAZARA_DEBUG
if (component > ParticleComponent_Max)
{
NazaraError("Vertex component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (!IsTypeSupported(type))
{
NazaraError("Component type 0x" + String::Number(type, 16) + " is not supported by particle declarations");
return;
}
#endif
if (component != ParticleComponent_Unused)
{
Component& particleComponent = m_components[component];
if (particleComponent.enabled)
m_stride -= Utility::ComponentStride[particleComponent.type];
else
particleComponent.enabled = true;
particleComponent.offset = offset;
particleComponent.type = type;
}
m_stride += Utility::ComponentStride[type];
}
/*!
* \brief Gets a component
*
* \param component Component in the declaration
* \param enabled Optional argument to get if this component is enabled
* \param type Optional argument to get if the type of the component
* \param offset Optional argument to get if the offset in the declaration
*
* \remark Produces a NazaraError with NAZARA_DEBUG defined if enumeration is invalid
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if enumeration is equal to ParticleComponent_Unused
*/
void ParticleDeclaration::GetComponent(ParticleComponent component, bool* enabled, ComponentType* type, std::size_t* offset) const
{
#ifdef NAZARA_DEBUG
if (component > ParticleComponent_Max)
{
NazaraError("Particle component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (component == ParticleComponent_Unused)
{
NazaraError("Cannot get \"unused\" component");
return;
}
#endif
const Component& particleComponent = m_components[component];
if (enabled)
*enabled = particleComponent.enabled;
if (type)
*type = particleComponent.type;
if (offset)
*offset = particleComponent.offset;
}
/*!
* \brief Gets the stride of the declaration
* \return Stride of the declaration
*/
std::size_t ParticleDeclaration::GetStride() const
{
return m_stride;
}
/*!
* \brief Sets the stride of the declaration
*
* \param stride Stride of the declaration
*/
void ParticleDeclaration::SetStride(unsigned int stride)
{
m_stride = stride;
}
/*!
* \brief Sets the current particle declaration with the content of the other one
* \return A reference to this
*
* \param declaration The other ParticleDeclaration
*/
ParticleDeclaration& ParticleDeclaration::operator=(const ParticleDeclaration& declaration)
{
m_components = declaration.m_components;
m_stride = declaration.m_stride;
return *this;
}
/*!
* \brief Gets the particle declaration based on the layout
* \return Pointer to the declaration
*
* \param layout Layout of the particle declaration
*
* \remark Produces a NazaraError with NAZARA_DEBUG if enumeration is invalid
*/
ParticleDeclaration* ParticleDeclaration::Get(ParticleLayout layout)
{
NazaraAssert(layout <= ParticleLayout_Max, "Particle layout out of enum");
return &s_declarations[layout];
}
/*!
* \brief Checks whether the type is supported
* \return true If it is the case
*
* \param type Type of the component
*
* \remark Produces a NazaraError if enumeration is invalid
*/
bool ParticleDeclaration::IsTypeSupported(ComponentType type)
{
switch (type)
{
case ComponentType_Color:
case ComponentType_Double1:
case ComponentType_Double2:
case ComponentType_Double3:
case ComponentType_Double4:
case ComponentType_Float1:
case ComponentType_Float2:
case ComponentType_Float3:
case ComponentType_Float4:
case ComponentType_Int1:
case ComponentType_Int2:
case ComponentType_Int3:
case ComponentType_Int4:
case ComponentType_Quaternion:
return true;
}
NazaraError("Component type not handled (0x" + String::Number(type, 16) + ')');
return false;
}
/*!
* \brief Initializes the particle declaration librairies
* \return true If successful
*
* \remark Produces a NazaraError if the particle declaration library failed to be initialized
* \remark Produces a NazaraAssert if memory layout of declaration does not match the corresponding structure
*/
bool ParticleDeclaration::Initialize()
{
if (!ParticleDeclarationLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
try
{
ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowException);
// Layout : Type
ParticleDeclaration* declaration;
// ParticleLayout_Billboard : ParticleStruct_Billboard
declaration = &s_declarations[ParticleLayout_Billboard];
declaration->EnableComponent(ParticleComponent_Color, ComponentType_Color, NazaraOffsetOf(ParticleStruct_Billboard, color));
declaration->EnableComponent(ParticleComponent_Life, ComponentType_Float1, NazaraOffsetOf(ParticleStruct_Billboard, life));
declaration->EnableComponent(ParticleComponent_Normal, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Billboard, normal));
declaration->EnableComponent(ParticleComponent_Position, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Billboard, position));
declaration->EnableComponent(ParticleComponent_Rotation, ComponentType_Float1, NazaraOffsetOf(ParticleStruct_Billboard, rotation));
declaration->EnableComponent(ParticleComponent_Size, ComponentType_Float2, NazaraOffsetOf(ParticleStruct_Billboard, size));
declaration->EnableComponent(ParticleComponent_Velocity, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Billboard, velocity));
NazaraAssert(declaration->GetStride() == sizeof(ParticleStruct_Billboard), "Invalid stride for declaration ParticleLayout_Billboard");
// ParticleLayout_Model : ParticleStruct_Model
declaration = &s_declarations[ParticleLayout_Model];
declaration->EnableComponent(ParticleComponent_Life, ComponentType_Float1, NazaraOffsetOf(ParticleStruct_Model, life));
declaration->EnableComponent(ParticleComponent_Position, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Model, position));
declaration->EnableComponent(ParticleComponent_Rotation, ComponentType_Quaternion, NazaraOffsetOf(ParticleStruct_Model, rotation));
declaration->EnableComponent(ParticleComponent_Velocity, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Model, velocity));
NazaraAssert(declaration->GetStride() == sizeof(ParticleStruct_Model), "Invalid stride for declaration ParticleLayout_Model");
// ParticleLayout_Sprite : ParticleStruct_Sprite
declaration = &s_declarations[ParticleLayout_Sprite];
declaration->EnableComponent(ParticleComponent_Color, ComponentType_Color, NazaraOffsetOf(ParticleStruct_Sprite, color));
declaration->EnableComponent(ParticleComponent_Life, ComponentType_Float1, NazaraOffsetOf(ParticleStruct_Sprite, life));
declaration->EnableComponent(ParticleComponent_Position, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Sprite, position));
declaration->EnableComponent(ParticleComponent_Rotation, ComponentType_Float1, NazaraOffsetOf(ParticleStruct_Sprite, rotation));
declaration->EnableComponent(ParticleComponent_Velocity, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Sprite, velocity));
NazaraAssert(declaration->GetStride() == sizeof(ParticleStruct_Sprite), "Invalid stride for declaration ParticleLayout_Sprite");
}
catch (const std::exception& e)
{
NazaraError("Failed to initialize particle declarations: " + String(e.what()));
return false;
}
return true;
}
/*!
* \brief Uninitializes the particle declaration librairies
*/
void ParticleDeclaration::Uninitialize()
{
ParticleDeclarationLibrary::Uninitialize();
}
std::array<ParticleDeclaration, ParticleLayout_Max + 1> ParticleDeclaration::s_declarations;
ParticleDeclarationLibrary::LibraryMap ParticleDeclaration::s_library;
}

View File

@@ -1,169 +0,0 @@
// Copyright (C) 2020 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/ParticleEmitter.hpp>
#include <Nazara/Graphics/ParticleMapper.hpp>
#include <Nazara/Graphics/ParticleGroup.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleEmitter
* \brief Graphics class that represents an emitter of particles
*/
/*!
* \brief Constructs a ParticleEmitter object by default
*/
ParticleEmitter::ParticleEmitter() :
m_lagCompensationEnabled(false),
m_emissionAccumulator(0.f),
m_emissionRate(0.f),
m_emissionCount(1)
{
}
ParticleEmitter::ParticleEmitter(const ParticleEmitter& emitter) :
m_lagCompensationEnabled(emitter.m_lagCompensationEnabled),
m_emissionAccumulator(0.f),
m_emissionRate(emitter.m_emissionRate),
m_emissionCount(emitter.m_emissionCount)
{
}
ParticleEmitter::ParticleEmitter(ParticleEmitter&& emitter) :
m_lagCompensationEnabled(emitter.m_lagCompensationEnabled),
m_emissionAccumulator(0.f),
m_emissionRate(emitter.m_emissionRate),
m_emissionCount(emitter.m_emissionCount)
{
OnParticleEmitterMove(&emitter, this);
}
ParticleEmitter::~ParticleEmitter()
{
OnParticleEmitterRelease(this);
}
/*!
* \brief Emits particles according to the delta time between the previous frame
*
* \param system Particle system to work on
* \param elapsedTime Delta time between the previous frame
*/
void ParticleEmitter::Emit(ParticleGroup& system, float elapsedTime) const
{
if (m_emissionRate > 0.f)
{
// We accumulate the real part (to avoid that a high emission rate prevents particles to form)
m_emissionAccumulator += elapsedTime * m_emissionRate;
float emissionCount = std::floor(m_emissionAccumulator); // The number of emissions in this update
m_emissionAccumulator -= emissionCount; // We get rid off the integer part
if (emissionCount >= 1.f)
{
// We compute the maximum number of particles which can be emitted
std::size_t emissionCountInt = static_cast<std::size_t>(emissionCount);
std::size_t maxParticleCount = emissionCountInt * m_emissionCount;
// We get the number of particles that we are able to create (depending on the free space)
std::size_t particleCount = std::min(maxParticleCount, system.GetMaxParticleCount() - system.GetParticleCount());
if (particleCount == 0)
return;
// And we emit our particles
void* particles = system.GenerateParticles(particleCount);
ParticleMapper mapper(particles, system.GetDeclaration());
SetupParticles(mapper, particleCount);
if (m_lagCompensationEnabled)
{
// We will now apply our controllers
float invEmissionRate = 1.f / m_emissionRate;
for (unsigned int i = 1; i <= emissionCountInt; ++i)
system.ApplyControllers(mapper, std::min(m_emissionCount * i, particleCount), invEmissionRate);
}
}
}
}
/*!
* \brief Enables the lag compensation
*
* \param enable Should lag compensation be enabled
*/
void ParticleEmitter::EnableLagCompensation(bool enable)
{
m_lagCompensationEnabled = enable;
}
/*!
* \brief Gets the emission count
* \return Current emission count
*/
std::size_t ParticleEmitter::GetEmissionCount() const
{
return m_emissionCount;
}
/*!
* \brief Gets the emission rate
* \return Current emission rate
*/
float ParticleEmitter::GetEmissionRate() const
{
return m_emissionRate;
}
/*!
* \brief Checks whether the lag compensation is enabled
* \return true If it is the case
*/
bool ParticleEmitter::IsLagCompensationEnabled() const
{
return m_lagCompensationEnabled;
}
/*!
* \brief Sets the emission count
*
* \param count Emission count
*/
void ParticleEmitter::SetEmissionCount(std::size_t count)
{
m_emissionCount = count;
}
/*!
* \brief Sets the emission rate
*
* \param rate Emission rate
*/
void ParticleEmitter::SetEmissionRate(float rate)
{
m_emissionRate = rate;
}
ParticleEmitter& ParticleEmitter::operator=(ParticleEmitter && emitter)
{
m_emissionCount = emitter.m_emissionCount;
m_emissionRate = emitter.m_emissionRate;
m_lagCompensationEnabled = emitter.m_lagCompensationEnabled;
OnParticleEmitterMove(&emitter, this);
return *this;
}
}

View File

@@ -1,29 +0,0 @@
// Copyright (C) 2020 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/ParticleFunctionController.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleFunctionController
* \brief Helper class used to provide a function as a particle controller without going in the process of making a new class
*/
/*!
* \brief Calls the controller function
*
* \param group Particle group responsible of the particles
* \param mapper Particle mapper, allowing access to the particle data
* \param startId The first ID of the particle to update (inclusive)
* \param endId The last ID of the particle to update (inclusive)
* \param elapsedTime Elapsed time in seconds since the last update
*/
void ParticleFunctionController::Apply(ParticleGroup& group, ParticleMapper& mapper, unsigned int startId, unsigned int endId, float elapsedTime)
{
m_controller(group, mapper, startId, endId, elapsedTime);
}
}

View File

@@ -1,28 +0,0 @@
// Copyright (C) 2020 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/ParticleFunctionGenerator.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleFunctionGenerator
* \brief Helper class used to provide a function as a particle generator without going in the process of making a new class
*/
/*!
* \brief Calls the generator function
*
* \param group Particle group responsible of the particles
* \param mapper Particle mapper, allowing access to the particle data
* \param startId The first ID of the particle to update (inclusive)
* \param endId The last ID of the particle to update (inclusive)
*/
void ParticleFunctionGenerator::Generate(ParticleGroup& group, ParticleMapper& mapper, unsigned int startId, unsigned int endId)
{
m_generator(group, mapper, startId, endId);
}
}

View File

@@ -1,29 +0,0 @@
// Copyright (C) 2020 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/ParticleFunctionRenderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleFunctionRenderer
* \brief Helper class used to provide a function as a particle renderer without going in the process of making a new class
*/
/*!
* \brief Calls the renderer function
*
* \param group Particle group responsible of the particles
* \param mapper Particle mapper, allowing constant access to the particle data
* \param startId The first ID of the particle to update (inclusive)
* \param endId The last ID of the particle to update (inclusive)
* \param renderQueue The concerned render queue that will receive drawable informations
*/
void ParticleFunctionRenderer::Render(const ParticleGroup& group, const ParticleMapper& mapper, unsigned int startId, unsigned int endId, AbstractRenderQueue* renderQueue)
{
m_renderer(group, mapper, startId, endId, renderQueue);
}
}

View File

@@ -1,69 +0,0 @@
// Copyright (C) 2020 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/ParticleGenerator.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleGenerator
* \brief Graphics class which generates particles
*
* \remark This class is abstract
*/
/*!
* \brief Constructs a ParticleGenerator object by assignation
*
* \param generator ParticleGenerator to copy into this
*/
ParticleGenerator::ParticleGenerator(const ParticleGenerator& generator) :
RefCounted()
{
NazaraUnused(generator);
}
/*!
* \brief Destructs the object and calls OnParticleGeneratorRelease
*
* \see OnParticleGeneratorRelease
*/
ParticleGenerator::~ParticleGenerator()
{
OnParticleGeneratorRelease(this);
}
/*!
* \brief Initializes the particle generator librairies
* \return true If successful
*
* \remark Produces a NazaraError if the particle generator library failed to be initialized
*/
bool ParticleGenerator::Initialize()
{
if (!ParticleGeneratorLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
return true;
}
/*!
* \brief Uninitializes the particle generator librairies
*/
void ParticleGenerator::Uninitialize()
{
ParticleGeneratorLibrary::Uninitialize();
}
ParticleGeneratorLibrary::LibraryMap ParticleGenerator::s_library;
}

View File

@@ -1,492 +0,0 @@
// Copyright (C) 2020 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/ParticleGroup.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <Nazara/Graphics/ParticleMapper.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleSystem
* \brief Graphics class that represents the system to handle particles
*/
/*!
* \brief Constructs a ParticleSystem object with a maximal number of particles and a layout
*
* \param maxParticleCount Maximum number of particles to generate
* \param layout Enumeration for the layout of data information for the particles
*/
ParticleGroup::ParticleGroup(unsigned int maxParticleCount, ParticleLayout layout) :
ParticleGroup(maxParticleCount, ParticleDeclaration::Get(layout))
{
}
/*!
* \brief Constructs a ParticleSystem object with a maximal number of particles and a particle declaration
*
* \param maxParticleCount Maximum number of particles to generate
* \param declaration Data information for the particles
*/
ParticleGroup::ParticleGroup(unsigned int maxParticleCount, ParticleDeclarationConstRef declaration) :
m_maxParticleCount(maxParticleCount),
m_particleCount(0),
m_declaration(std::move(declaration)),
m_processing(false)
{
// In case of error, the constructor can only throw an exception
ErrorFlags flags(ErrorFlag_ThrowException, true);
m_particleSize = m_declaration->GetStride(); // The size of each particle
ResizeBuffer();
}
/*!
* \brief Constructs a ParticleSystem object by assignation
*
* \param system ParticleSystem to copy into this
*/
ParticleGroup::ParticleGroup(const ParticleGroup& system) :
Renderable(system),
m_maxParticleCount(system.m_maxParticleCount),
m_particleCount(system.m_particleCount),
m_particleSize(system.m_particleSize),
m_controllers(system.m_controllers),
m_generators(system.m_generators),
m_declaration(system.m_declaration),
m_renderer(system.m_renderer),
m_processing(false)
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
ResizeBuffer();
// We only copy alive particles
std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);
}
ParticleGroup::~ParticleGroup()
{
OnParticleGroupRelease(this);
}
/*!
* \brief Adds a controller to the particles
*
* \param controller Controller for the particles
*
* \remark Produces a NazaraAssert if controller is invalid
*/
void ParticleGroup::AddController(ParticleControllerRef controller)
{
NazaraAssert(controller, "Invalid particle controller");
m_controllers.emplace_back(std::move(controller));
}
/*!
* \brief Adds an emitter to the particles
*
* \param emitter Emitter for the particles
*
* \remark Produces a NazaraAssert if emitter is invalid
*/
void ParticleGroup::AddEmitter(ParticleEmitter* emitter)
{
NazaraAssert(emitter, "Invalid particle emitter");
EmitterEntry entry;
entry.emitter = emitter;
entry.moveSlot.Connect(emitter->OnParticleEmitterMove, this, &ParticleGroup::OnEmitterMove);
entry.releaseSlot.Connect(emitter->OnParticleEmitterRelease, this, &ParticleGroup::OnEmitterRelease);
m_emitters.emplace_back(std::move(entry));
}
/*!
* \brief Adds a generator to the particles
*
* \param generator Generator for the particles
*
* \remark Produces a NazaraAssert if generator is invalid
*/
void ParticleGroup::AddGenerator(ParticleGeneratorRef generator)
{
NazaraAssert(generator, "Invalid particle generator");
m_generators.emplace_back(std::move(generator));
}
/*!
* \brief Adds the particle system to the rendering queue
*
* \param renderQueue Queue to be added
* \param transformMatrix Transformation matrix for the system
*
* \remark Produces a NazaraAssert if inner renderer is invalid
* \remark Produces a NazaraAssert if renderQueue is invalid
*/
void ParticleGroup::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& /*transformMatrix*/) const
{
NazaraAssert(m_renderer, "Invalid particle renderer");
NazaraAssert(renderQueue, "Invalid renderqueue");
if (m_particleCount > 0)
{
ParticleMapper mapper(m_buffer.data(), m_declaration);
m_renderer->Render(*this, mapper, 0, m_particleCount - 1, renderQueue);
}
}
/*!
* \brief Applies the controllers
*
* \param mapper Mapper containing layout information of each particle
* \param particleCount Number of particles
* \param elapsedTime Delta time between the previous frame
*/
void ParticleGroup::ApplyControllers(ParticleMapper& mapper, unsigned int particleCount, float elapsedTime)
{
m_processing = true;
// To avoid a lock in case of exception
CallOnExit onExit([this]()
{
m_processing = false;
});
for (ParticleController* controller : m_controllers)
controller->Apply(*this, mapper, 0, particleCount - 1, elapsedTime);
onExit.CallAndReset();
// We only kill now the dead particles during the update
if (m_dyingParticles.size() < m_particleCount)
{
// We kill them in reverse order, std::set sorting them via std::greater
// The reason is simple, as the death of a particle means moving the last particle in the buffer,
// without this solution, certain particles could avoid death
for (unsigned int index : m_dyingParticles)
KillParticle(index);
}
else
KillParticles(); // Every particles are dead, this is way faster
m_dyingParticles.clear();
}
/*!
* \brief Creates one particle
* \return Pointer to the particle memory buffer
*/
void* ParticleGroup::CreateParticle()
{
return CreateParticles(1);
}
/*!
* \brief Creates multiple particles
* \return Pointer to the first particle memory buffer
*/
void* ParticleGroup::CreateParticles(unsigned int count)
{
if (count == 0)
return nullptr;
if (m_particleCount + count > m_maxParticleCount)
return nullptr;
std::size_t particlesIndex = m_particleCount;
m_particleCount += count;
return &m_buffer[particlesIndex * m_particleSize];
}
/*!
* \brief Generates one particle
* \return Pointer to the particle memory buffer
*/
void* ParticleGroup::GenerateParticle()
{
return GenerateParticles(1);
}
/*!
* \brief Generates multiple particles
* \return Pointer to the first particle memory buffer
*/
void* ParticleGroup::GenerateParticles(unsigned int count)
{
void* ptr = CreateParticles(count);
if (!ptr)
return nullptr;
ParticleMapper mapper(ptr, m_declaration);
for (ParticleGenerator* generator : m_generators)
generator->Generate(*this, mapper, 0, count - 1);
return ptr;
}
/*!
* \brief Gets the particle declaration
* \return Particle declaration
*/
const ParticleDeclarationConstRef& ParticleGroup::GetDeclaration() const
{
return m_declaration;
}
/*!
* \brief Gets the maximum number of particles
* \return Current maximum number
*/
std::size_t ParticleGroup::GetMaxParticleCount() const
{
return m_maxParticleCount;
}
/*!
* \brief Gets the number of particles
* \return Current number
*/
std::size_t ParticleGroup::GetParticleCount() const
{
return m_particleCount;
}
/*!
* \brief Gets the size of particles
* \return Current size
*/
std::size_t ParticleGroup::GetParticleSize() const
{
return m_particleSize;
}
/*!
* \brief Kills one particle
*
* \param index Index of the particle
*/
void ParticleGroup::KillParticle(std::size_t index)
{
///FIXME: Verify the index
if (m_processing)
{
// The buffer is being modified, we can not reduce its size, we put the particle in the waiting list
m_dyingParticles.insert(index);
return;
}
// We move the last alive particle to the place of this one
if (--m_particleCount > 0)
std::memcpy(&m_buffer[index * m_particleSize], &m_buffer[m_particleCount * m_particleSize], m_particleSize);
}
/*!
* \brief Kills every particles
*/
void ParticleGroup::KillParticles()
{
m_particleCount = 0;
}
/*!
* \brief Removes a controller to the particles
*
* \param controller Controller for the particles to remove
*/
void ParticleGroup::RemoveController(ParticleController* controller)
{
auto it = std::find(m_controllers.begin(), m_controllers.end(), controller);
if (it != m_controllers.end())
m_controllers.erase(it);
}
/*!
* \brief Removes an emitter to the particles
*
* \param emitter Emitter for the particles to remove
*/
void ParticleGroup::RemoveEmitter(ParticleEmitter* emitter)
{
for (auto it = m_emitters.begin(); it != m_emitters.end(); ++it)
{
if (it->emitter == emitter)
{
m_emitters.erase(it);
break;
}
}
}
/*!
* \brief Removes a generator to the particles
*
* \param generator Generator for the particles to remove
*/
void ParticleGroup::RemoveGenerator(ParticleGenerator* generator)
{
auto it = std::find(m_generators.begin(), m_generators.end(), generator);
if (it != m_generators.end())
m_generators.erase(it);
}
/*!
* \brief Sets the renderer of the particles
*
* \param renderer Renderer for the particles
*/
void ParticleGroup::SetRenderer(ParticleRenderer* renderer)
{
m_renderer = renderer;
}
/*!
* \brief Updates the system
*
* \param elapsedTime Delta time between the previous frame
*/
void ParticleGroup::Update(float elapsedTime)
{
// Emission
for (const EmitterEntry& entry : m_emitters)
entry.emitter->Emit(*this, elapsedTime);
// Update
if (m_particleCount > 0)
{
///TODO: Update using threads
ParticleMapper mapper(m_buffer.data(), m_declaration);
ApplyControllers(mapper, m_particleCount, elapsedTime);
}
}
/*!
* \brief Updates the bounding volume by a matrix
*
* \param transformMatrix Matrix transformation for our bounding volume
*/
void ParticleGroup::UpdateBoundingVolume(const Matrix4f& /*transformMatrix*/)
{
// Nothing to do here (our bounding volume is global)
}
/*!
* \brief Sets the current particle system with the content of the other one
* \return A reference to this
*
* \param system The other ParticleSystem
*/
ParticleGroup& ParticleGroup::operator=(const ParticleGroup& system)
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
Renderable::operator=(system);
m_controllers = system.m_controllers;
m_declaration = system.m_declaration;
m_generators = system.m_generators;
m_maxParticleCount = system.m_maxParticleCount;
m_particleCount = system.m_particleCount;
m_particleSize = system.m_particleSize;
m_renderer = system.m_renderer;
// The copy can not (or should not) happen during the update, there is no use to copy
m_dyingParticles.clear();
m_processing = false;
m_buffer.clear(); // To avoid a copy due to resize() which will be pointless
ResizeBuffer();
// We only copy alive particles
std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount * m_particleSize);
return *this;
}
/*!
* \brief Makes the bounding volume of this text
*/
void ParticleGroup::MakeBoundingVolume() const
{
///TODO: Compute the AABB (taking into account the size of particles)
m_boundingVolume.MakeInfinite();
}
void ParticleGroup::OnEmitterMove(ParticleEmitter* oldEmitter, ParticleEmitter* newEmitter)
{
for (EmitterEntry& entry : m_emitters)
{
if (entry.emitter == oldEmitter)
entry.emitter = newEmitter;
}
}
void ParticleGroup::OnEmitterRelease(const ParticleEmitter* emitter)
{
for (auto it = m_emitters.begin(); it != m_emitters.end();)
{
if (it->emitter == emitter)
it = m_emitters.erase(it);
else
++it;
}
}
/*!
* \brief Resizes the internal buffer
*
* \remark Produces a NazaraError if resize did not work
*/
void ParticleGroup::ResizeBuffer()
{
// Just to have a better description of our problem in case of error
try
{
m_buffer.resize(m_maxParticleCount*m_particleSize);
}
catch (const std::exception& e)
{
StringStream stream;
stream << "Failed to allocate particle buffer (" << e.what() << ") for " << m_maxParticleCount << " particles of size " << m_particleSize;
NazaraError(stream.ToString());
}
}
}

View File

@@ -1,31 +0,0 @@
// Copyright (C) 2020 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/ParticleMapper.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleMapper
* \brief Graphics class that represents the mapping between the internal buffer and the particle declaration
*/
/*!
* \brief Constructs a ParticleMapper object with a raw buffer and a particle declaration
*
* \param buffer Raw buffer to store particles data
* \param declaration Declaration of the particle
*/
ParticleMapper::ParticleMapper(void* buffer, const ParticleDeclaration* declaration) :
m_declaration(declaration),
m_ptr(static_cast<UInt8*>(buffer))
{
}
ParticleMapper::~ParticleMapper() = default;
}

View File

@@ -1,67 +0,0 @@
// Copyright (C) 2020 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/ParticleRenderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::ParticleRenderer
* \brief Graphics class that represents the rendering of the particle
*/
/*!
* \brief Constructs a ParticleRenderer object by assignation
*
* \param renderer ParticleRenderer to copy into this
*/
ParticleRenderer::ParticleRenderer(const ParticleRenderer& renderer) :
RefCounted()
{
NazaraUnused(renderer);
}
/*!
* \brief Destructs the object and calls OnParticleRendererRelease
*
* \see OnParticleRendererRelease
*/
ParticleRenderer::~ParticleRenderer()
{
OnParticleRendererRelease(this);
}
/*!
* \brief Initializes the particle renderer librairies
* \return true If successful
*
* \remark Produces a NazaraError if the particle renderer library failed to be initialized
*/
bool ParticleRenderer::Initialize()
{
if (!ParticleRendererLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
return true;
}
/*!
* \brief Uninitializes the particle renderer librairies
*/
void ParticleRenderer::Uninitialize()
{
ParticleRendererLibrary::Uninitialize();
}
ParticleRendererLibrary::LibraryMap ParticleRenderer::s_library;
}

View File

@@ -1,18 +0,0 @@
// Copyright (C) 2020 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/TaskScheduler.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
void RenderQueueInternal::Sort()
{
std::sort(m_orderedRenderQueue.begin(), m_orderedRenderQueue.end(), [](const RenderDataPair& lhs, const RenderDataPair& rhs)
{
return lhs.first < rhs.first;
});
}
}

View File

@@ -1,247 +0,0 @@
// Copyright (C) 2020 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/RenderTechniques.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <limits>
#include <unordered_map>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
const char* techniquesName[] =
{
"Advanced Forward",
"Basic Forward",
"Deferred Shading",
"Depth Pass",
"Light Pre-Pass",
"User"
};
static_assert(sizeof(techniquesName) / sizeof(const char*) == RenderTechniqueType_Max + 1, "Render technique type name array is incomplete");
struct RenderTechnique
{
RenderTechniques::RenderTechniqueFactory factory;
int ranking;
};
std::unordered_map<String, RenderTechnique> s_renderTechniques;
}
/*!
* \ingroup graphics
* \class Nz::RenderTechniques
* \brief Graphics class that represents the techniques used in rendering
*/
/*!
* \brief Gets the technique by enumeration
* \return A reference to the newly created technique
*
* \param renderTechnique Enumeration of the technique
* \param techniqueRanking Ranking for the technique
*
* \remark Produces a NazaraError if renderTechnique does not exist
*/
AbstractRenderTechnique* RenderTechniques::GetByEnum(RenderTechniqueType renderTechnique, int* techniqueRanking)
{
#ifdef NAZARA_DEBUG
if (renderTechnique > RenderTechniqueType_Max)
{
NazaraError("Render technique type out of enum");
return nullptr;
}
#endif
return GetByName(techniquesName[renderTechnique], techniqueRanking);
}
/*!
* \brief Gets the technique by index
* \return A reference to the newly created technique
*
* \param index Index of the technique
* \param techniqueRanking Ranking for the technique
*
* \remark Produces a NazaraError if index is out or range
*/
AbstractRenderTechnique* RenderTechniques::GetByIndex(unsigned int index, int* techniqueRanking)
{
#if NAZARA_GRAPHICS_SAFE
if (index >= s_renderTechniques.size())
{
NazaraError("Technique index out of range (" + String::Number(index) + " >= " + String::Number(s_renderTechniques.size()) + ')');
return nullptr;
}
#endif
auto it = s_renderTechniques.begin();
std::advance(it, index);
if (techniqueRanking)
*techniqueRanking = it->second.ranking;
return it->second.factory();
}
/*!
* \brief Gets the technique by name
* \return A reference to the newly created technique
*
* \param name Name of the technique
* \param techniqueRanking Ranking for the technique
*
* \remark Produces a NazaraError if name does not exist or is invalid
*/
AbstractRenderTechnique* RenderTechniques::GetByName(const String& name, int* techniqueRanking)
{
#if NAZARA_GRAPHICS_SAFE
if (name.IsEmpty())
{
NazaraError("Technique name cannot be empty");
return nullptr;
}
#endif
auto it = s_renderTechniques.find(name);
if (it == s_renderTechniques.end())
{
NazaraError("Technique not found");
return nullptr;
}
if (techniqueRanking)
*techniqueRanking = it->second.ranking;
return it->second.factory();
}
/*!
* \brief Gets the technique by ranking
* \return A reference to the newly created technique
*
* \param maxRanking Ranking maximum of the technique
* \param techniqueRanking Ranking for the technique
*
* \remark Produces a NazaraError if name does not exist or is invalid
*/
AbstractRenderTechnique* RenderTechniques::GetByRanking(int maxRanking, int* techniqueRanking)
{
if (maxRanking < 0)
maxRanking = std::numeric_limits<int>::max();
int currentRanking = -1;
RenderTechnique* technique = nullptr;
for (auto it = s_renderTechniques.begin(); it != s_renderTechniques.end(); ++it)
{
int ranking = it->second.ranking;
if (ranking > currentRanking && ranking <= maxRanking)
{
currentRanking = ranking;
technique = &(it->second);
}
}
if (!technique)
{
NazaraError("No technique found");
return nullptr;
}
if (techniqueRanking)
*techniqueRanking = currentRanking;
return technique->factory();
}
/*!
* \brief Gets the number of techniques available
* \return Number of techniques
*/
std::size_t RenderTechniques::GetCount()
{
return s_renderTechniques.size();
}
/*!
* \brief Registers a technique
*
* \param name Name of the technique
* \param ranking Ranking of the technique
* \param factory Factory to create the technique
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if name is empty
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if ranking is negative
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if factory is invalid is invalid
*/
void RenderTechniques::Register(const String& name, int ranking, RenderTechniqueFactory factory)
{
#if NAZARA_GRAPHICS_SAFE
if (name.IsEmpty())
{
NazaraError("Technique name cannot be empty");
return;
}
if (ranking < 0)
{
NazaraError("Technique ranking cannot be negative");
return;
}
if (!factory)
{
NazaraError("Technique function must be valid");
return;
}
#endif
s_renderTechniques[name] = {factory, ranking};
}
/*!
* \brief Converts the enumeration to string
* \return String symbolizing the technique
*
* \param renderTechnique Enumeration of the technique
*
* \remark Produces a NazaraError if renderTechnique does not exist and returns "Error"
*/
String RenderTechniques::ToString(RenderTechniqueType renderTechnique)
{
#ifdef NAZARA_DEBUG
if (renderTechnique > RenderTechniqueType_Max)
{
NazaraError("Render technique type out of enum");
return String("Error");
}
#endif
return techniquesName[renderTechnique];
}
/*!
* \brief Unregisters a technique
*
* \param name Name of the technique
*/
void RenderTechniques::Unregister(const String& name)
{
s_renderTechniques.erase(name);
}
}

View File

@@ -1,57 +0,0 @@
// Copyright (C) 2020 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/Renderable.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Renderable
* \brief Graphics class that represents a renderable element for our scene
*
* \remark This class is abstract
*/
Renderable::~Renderable() = default;
/*!
* \brief Culls the model if not in the frustum
* \return true If renderable is in the frustum
*
* \param frustum Symbolizing the field of view
* \param transformMatrix Matrix transformation for our object
*/
bool Renderable::Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const
{
NazaraUnused(transformMatrix);
return frustum.Contains(m_boundingVolume);
}
/*!
* \brief Gets the bounding volume
* \return Bounding volume of the renderable element
*/
const BoundingVolumef& Renderable::GetBoundingVolume() const
{
EnsureBoundingVolumeUpdated();
return m_boundingVolume;
}
/*!
* \brief Updates the bounding volume by a matrix
*
* \param transformMatrix Matrix transformation for our bounding volume
*/
void Renderable::UpdateBoundingVolume(const Matrix4f& transformMatrix)
{
m_boundingVolume.Update(transformMatrix);
}
}

View File

@@ -1,23 +0,0 @@
#version 140
out vec4 RenderTarget0;
uniform float BrightLuminance = 0.8;
uniform float BrightMiddleGrey = 0.5;
uniform float BrightThreshold = 0.8;
uniform sampler2D ColorTexture;
uniform vec2 InvTargetSize;
void main()
{
vec2 texCoord = gl_FragCoord.xy * InvTargetSize;
vec3 color = textureLod(ColorTexture, texCoord, 0.0).rgb;
color *= BrightMiddleGrey/BrightLuminance;
color *= 1.0 + (color / (BrightThreshold*BrightThreshold));
color -= 0.5;
color /= (1.0 + color);
RenderTarget0 = vec4(color, 1.0);
}

View File

@@ -1 +0,0 @@
35,118,101,114,115,105,111,110,32,49,52,48,10,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,10,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,66,114,105,103,104,116,76,117,109,105,110,97,110,99,101,32,61,32,48,46,56,59,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,66,114,105,103,104,116,77,105,100,100,108,101,71,114,101,121,32,61,32,48,46,53,59,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,66,114,105,103,104,116,84,104,114,101,115,104,111,108,100,32,61,32,48,46,56,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,67,111,108,111,114,84,101,120,116,117,114,101,59,10,117,110,105,102,111,114,109,32,118,101,99,50,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,10,118,111,105,100,32,109,97,105,110,40,41,10,123,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,103,108,95,70,114,97,103,67,111,111,114,100,46,120,121,32,42,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,10,9,118,101,99,51,32,99,111,108,111,114,32,61,32,116,101,120,116,117,114,101,76,111,100,40,67,111,108,111,114,84,101,120,116,117,114,101,44,32,116,101,120,67,111,111,114,100,44,32,48,46,48,41,46,114,103,98,59,10,10,9,99,111,108,111,114,32,42,61,32,66,114,105,103,104,116,77,105,100,100,108,101,71,114,101,121,47,66,114,105,103,104,116,76,117,109,105,110,97,110,99,101,59,10,9,99,111,108,111,114,32,42,61,32,49,46,48,32,43,32,40,99,111,108,111,114,32,47,32,40,66,114,105,103,104,116,84,104,114,101,115,104,111,108,100,42,66,114,105,103,104,116,84,104,114,101,115,104,111,108,100,41,41,59,10,9,99,111,108,111,114,32,45,61,32,48,46,53,59,10,9,99,111,108,111,114,32,47,61,32,40,49,46,48,32,43,32,99,111,108,111,114,41,59,10,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,99,111,108,111,114,44,32,49,46,48,41,59,10,125,10,

View File

@@ -1,17 +0,0 @@
#version 140
out vec4 RenderTarget0;
uniform sampler2D BloomTexture;
uniform sampler2D ColorTexture;
uniform vec2 InvTargetSize;
void main()
{
vec2 texCoord = gl_FragCoord.xy * InvTargetSize;
vec3 bloomColor = textureLod(BloomTexture, texCoord, 0.0).rgb;
vec3 originalColor = textureLod(ColorTexture, texCoord, 0.0).rgb;
RenderTarget0 = vec4(originalColor + bloomColor, 1.0);
}

View File

@@ -1 +0,0 @@
35,118,101,114,115,105,111,110,32,49,52,48,10,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,10,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,66,108,111,111,109,84,101,120,116,117,114,101,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,67,111,108,111,114,84,101,120,116,117,114,101,59,10,117,110,105,102,111,114,109,32,118,101,99,50,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,10,118,111,105,100,32,109,97,105,110,40,41,10,123,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,103,108,95,70,114,97,103,67,111,111,114,100,46,120,121,32,42,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,10,9,118,101,99,51,32,98,108,111,111,109,67,111,108,111,114,32,61,32,116,101,120,116,117,114,101,76,111,100,40,66,108,111,111,109,84,101,120,116,117,114,101,44,32,116,101,120,67,111,111,114,100,44,32,48,46,48,41,46,114,103,98,59,10,9,118,101,99,51,32,111,114,105,103,105,110,97,108,67,111,108,111,114,32,61,32,116,101,120,116,117,114,101,76,111,100,40,67,111,108,111,114,84,101,120,116,117,114,101,44,32,116,101,120,67,111,111,114,100,44,32,48,46,48,41,46,114,103,98,59,10,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,111,114,105,103,105,110,97,108,67,111,108,111,114,32,43,32,98,108,111,111,109,67,111,108,111,114,44,32,49,46,48,41,59,10,125,10,

View File

@@ -1,77 +0,0 @@
#version 140
out vec4 RenderTarget0;
uniform vec3 EyePosition;
uniform vec4 LightColor;
uniform vec2 LightFactors;
uniform vec4 LightDirection;
uniform sampler2D GBuffer0;
uniform sampler2D GBuffer1;
uniform sampler2D GBuffer2;
uniform sampler2D DepthBuffer;
uniform mat4 InvViewProjMatrix;
uniform vec2 InvTargetSize;
uniform vec4 SceneAmbient;
#define kPI 3.1415926536
vec3 DecodeNormal(in vec4 encodedNormal)
{
//return encodedNormal.xyz*2.0 - 1.0;
float a = encodedNormal.x * kPI;
vec2 scth = vec2(sin(a), cos(a));
vec2 scphi = vec2(sqrt(1.0 - encodedNormal.y*encodedNormal.y), encodedNormal.y);
return vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y);
}
void main()
{
vec2 texCoord = gl_FragCoord.xy * InvTargetSize;
vec4 gVec0 = textureLod(GBuffer0, texCoord, 0.0);
vec4 gVec1 = textureLod(GBuffer1, texCoord, 0.0);
vec4 gVec2 = textureLod(GBuffer2, texCoord, 0.0);
vec3 diffuseColor = gVec0.xyz;
vec3 normal = DecodeNormal(gVec1);
float specularMultiplier = gVec0.w;
float depth = textureLod(DepthBuffer, texCoord, 0.0).r;
float shininess = (gVec2.w == 0.0) ? 0.0 : exp2(gVec2.w*10.5);
vec3 lightDir = -LightDirection.xyz;
// Ambient
vec3 lightAmbient = LightColor.rgb * LightFactors.x * (vec3(1.0) + SceneAmbient.rgb);
// Diffuse
float lambert = max(dot(normal, lightDir), 0.0);
vec3 lightDiffuse = lambert * LightColor.rgb * LightFactors.y;
// Specular
vec3 lightSpecular;
if (shininess > 0.0)
{
vec3 viewSpace = vec3(texCoord*2.0 - 1.0, depth*2.0 - 1.0);
vec4 worldPos = InvViewProjMatrix * vec4(viewSpace, 1.0);
worldPos.xyz /= worldPos.w;
vec3 eyeVec = normalize(EyePosition - worldPos.xyz);
vec3 reflection = reflect(-lightDir, normal);
float specularFactor = max(dot(reflection, eyeVec), 0.0);
specularFactor = pow(specularFactor, shininess);
lightSpecular = specularFactor * LightColor.rgb * specularMultiplier;
}
else
lightSpecular = vec3(0.0);
vec3 fragmentColor = diffuseColor * (lightAmbient + lightDiffuse + lightSpecular);
RenderTarget0 = vec4(fragmentColor, 1.0);
}

File diff suppressed because one or more lines are too long

View File

@@ -1,47 +0,0 @@
#version 140
out vec4 RenderTarget0;
uniform float FXAAReduceMul = 0.0; // 1.0/8.0
uniform float FXAASpanMax = 8.0;
uniform sampler2D ColorTexture;
uniform vec2 InvTargetSize;
void main()
{
#define FXAA_REDUCE_MIN (1.0/128.0)
vec2 texCoord = gl_FragCoord.xy * InvTargetSize;
vec3 rgbNW = textureLodOffset(ColorTexture, texCoord, 0.0, ivec2(-1,-1)).rgb;
vec3 rgbNE = textureLodOffset(ColorTexture, texCoord, 0.0, ivec2(1,-1)).rgb;
vec3 rgbSW = textureLodOffset(ColorTexture, texCoord, 0.0, ivec2(-1,1)).rgb;
vec3 rgbSE = textureLodOffset(ColorTexture, texCoord, 0.0, ivec2(1,1)).rgb;
vec3 rgbM = textureLod(ColorTexture, texCoord, 0.0).rgb;
vec3 luma = vec3(0.299, 0.587, 0.114);
float lumaNW = dot(rgbNW, luma);
float lumaNE = dot(rgbNE, luma);
float lumaSW = dot(rgbSW, luma);
float lumaSE = dot(rgbSE, luma);
float lumaM = dot(rgbM, luma);
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
vec2 dir;
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAAReduceMul), FXAA_REDUCE_MIN);
float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
dir = min(vec2(FXAASpanMax, FXAASpanMax), max(vec2(-FXAASpanMax, -FXAASpanMax), dir * rcpDirMin)) * InvTargetSize;
vec3 rgbA = (1.0/2.0) * (textureLod(ColorTexture, texCoord + dir * (1.0/3.0 - 0.5), 0.0).rgb + textureLod(ColorTexture, texCoord + dir * (2.0/3.0 - 0.5), 0.0).rgb);
vec3 rgbB = rgbA * 1.0/2.0 + 1.0/4.0 * (textureLod(ColorTexture, texCoord + dir * (0.0/3.0 - 0.5), 0.0).rgb + textureLod(ColorTexture, texCoord + dir * (3.0/3.0 - 0.5), 0.0).rgb);
float lumaB = dot(rgbB, luma);
vec3 fragmentColor = (lumaB < lumaMin || lumaB > lumaMax) ? rgbA : rgbB;
RenderTarget0 = vec4(fragmentColor, 1.0);
}

File diff suppressed because one or more lines are too long

View File

@@ -1,13 +0,0 @@
#version 140
out vec4 RenderTarget0;
out vec4 RenderTarget1;
out vec4 RenderTarget2;
void main()
{
RenderTarget0 = vec4(0.0, 0.0, 0.0, 0.0);
RenderTarget1 = vec4(0.0, 0.0, 0.0, 0.0);
RenderTarget2 = vec4(1.0, 0.0, 0.0, 0.0);
gl_FragDepth = 1.0;
}

View File

@@ -1 +0,0 @@
35,118,101,114,115,105,111,110,32,49,52,48,10,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,49,59,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,50,59,10,10,118,111,105,100,32,109,97,105,110,40,41,10,123,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,48,46,48,44,32,48,46,48,44,32,48,46,48,44,32,48,46,48,41,59,10,9,82,101,110,100,101,114,84,97,114,103,101,116,49,32,61,32,118,101,99,52,40,48,46,48,44,32,48,46,48,44,32,48,46,48,44,32,48,46,48,41,59,10,9,82,101,110,100,101,114,84,97,114,103,101,116,50,32,61,32,118,101,99,52,40,49,46,48,44,32,48,46,48,44,32,48,46,48,44,32,48,46,48,41,59,10,9,103,108,95,70,114,97,103,68,101,112,116,104,32,61,32,49,46,48,59,10,125,10,

View File

@@ -1,25 +0,0 @@
// http://www.geeks3d.com/20100909/shader-library-gaussian-blur-post-processing-filter-in-glsl/
#version 140
out vec4 RenderTarget0;
uniform sampler2D ColorTexture;
uniform vec2 Filter;
uniform vec2 InvTargetSize;
float offset[3] = float[](0.0, 1.3846153846, 3.2307692308);
float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703);
void main()
{
vec2 texCoord = gl_FragCoord.xy * InvTargetSize;
vec3 color = textureLod(ColorTexture, texCoord, 0.0).rgb * weight[0];
for (int i = 1; i < 3; i++)
{
color += textureLod(ColorTexture, texCoord + Filter*vec2(offset[i])*InvTargetSize, 0.0).rgb * weight[i];
color += textureLod(ColorTexture, texCoord - Filter*vec2(offset[i])*InvTargetSize, 0.0).rgb * weight[i];
}
RenderTarget0 = vec4(color, 1.0);
}

View File

@@ -1 +0,0 @@
47,47,32,104,116,116,112,58,47,47,119,119,119,46,103,101,101,107,115,51,100,46,99,111,109,47,50,48,49,48,48,57,48,57,47,115,104,97,100,101,114,45,108,105,98,114,97,114,121,45,103,97,117,115,115,105,97,110,45,98,108,117,114,45,112,111,115,116,45,112,114,111,99,101,115,115,105,110,103,45,102,105,108,116,101,114,45,105,110,45,103,108,115,108,47,10,35,118,101,114,115,105,111,110,32,49,52,48,10,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,10,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,67,111,108,111,114,84,101,120,116,117,114,101,59,10,117,110,105,102,111,114,109,32,118,101,99,50,32,70,105,108,116,101,114,59,10,117,110,105,102,111,114,109,32,118,101,99,50,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,10,102,108,111,97,116,32,111,102,102,115,101,116,91,51,93,32,61,32,102,108,111,97,116,91,93,40,48,46,48,44,32,49,46,51,56,52,54,49,53,51,56,52,54,44,32,51,46,50,51,48,55,54,57,50,51,48,56,41,59,10,102,108,111,97,116,32,119,101,105,103,104,116,91,51,93,32,61,32,102,108,111,97,116,91,93,40,48,46,50,50,55,48,50,55,48,50,55,48,44,32,48,46,51,49,54,50,49,54,50,49,54,50,44,32,48,46,48,55,48,50,55,48,50,55,48,51,41,59,10,10,118,111,105,100,32,109,97,105,110,40,41,10,123,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,103,108,95,70,114,97,103,67,111,111,114,100,46,120,121,32,42,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,9,118,101,99,51,32,99,111,108,111,114,32,61,32,116,101,120,116,117,114,101,76,111,100,40,67,111,108,111,114,84,101,120,116,117,114,101,44,32,116,101,120,67,111,111,114,100,44,32,48,46,48,41,46,114,103,98,32,42,32,119,101,105,103,104,116,91,48,93,59,10,10,9,102,111,114,32,40,105,110,116,32,105,32,61,32,49,59,32,105,32,60,32,51,59,32,105,43,43,41,10,9,123,10,9,9,99,111,108,111,114,32,43,61,32,116,101,120,116,117,114,101,76,111,100,40,67,111,108,111,114,84,101,120,116,117,114,101,44,32,116,101,120,67,111,111,114,100,32,43,32,70,105,108,116,101,114,42,118,101,99,50,40,111,102,102,115,101,116,91,105,93,41,42,73,110,118,84,97,114,103,101,116,83,105,122,101,44,32,48,46,48,41,46,114,103,98,32,42,32,119,101,105,103,104,116,91,105,93,59,10,9,9,99,111,108,111,114,32,43,61,32,116,101,120,116,117,114,101,76,111,100,40,67,111,108,111,114,84,101,120,116,117,114,101,44,32,116,101,120,67,111,111,114,100,32,45,32,70,105,108,116,101,114,42,118,101,99,50,40,111,102,102,115,101,116,91,105,93,41,42,73,110,118,84,97,114,103,101,116,83,105,122,101,44,32,48,46,48,41,46,114,103,98,32,42,32,119,101,105,103,104,116,91,105,93,59,10,9,125,10,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,99,111,108,111,114,44,32,49,46,48,41,59,10,125,10,

View File

@@ -1,108 +0,0 @@
#version 140
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
#define LIGHT_SPOT 2
out vec4 RenderTarget0;
uniform vec3 EyePosition;
uniform int LightType;
uniform vec4 LightColor;
uniform vec2 LightFactors;
uniform vec4 LightDirection;
uniform vec4 LightParameters1;
uniform vec4 LightParameters2;
uniform vec2 LightParameters3;
uniform sampler2D GBuffer0;
uniform sampler2D GBuffer1;
uniform sampler2D GBuffer2;
uniform sampler2D DepthBuffer;
uniform mat4 InvViewProjMatrix;
uniform vec2 InvTargetSize;
uniform vec4 SceneAmbient;
uniform bool Discard = false;
float ColorToFloat(vec3 color)
{
const vec3 byte_to_float = vec3(1.0, 1.0/256, 1.0/(256*256));
return dot(color, byte_to_float);
}
#define kPI 3.1415926536
vec3 DecodeNormal(in vec4 encodedNormal)
{
//return encodedNormal.xyz*2.0 - 1.0;
float a = encodedNormal.x * kPI;
vec2 scth = vec2(sin(a), cos(a));
vec2 scphi = vec2(sqrt(1.0 - encodedNormal.y*encodedNormal.y), encodedNormal.y);
return vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y);
}
void main()
{
if (Discard)
return;
vec2 texCoord = gl_FragCoord.xy * InvTargetSize;
vec4 gVec0 = textureLod(GBuffer0, texCoord, 0.0);
vec4 gVec1 = textureLod(GBuffer1, texCoord, 0.0);
vec4 gVec2 = textureLod(GBuffer2, texCoord, 0.0);
vec3 diffuseColor = gVec0.xyz;
vec3 normal = DecodeNormal(gVec1);
float specularMultiplier = gVec0.w;
float depth = textureLod(DepthBuffer, texCoord, 0.0).r;
float shininess = (gVec2.w == 0.0) ? 0.0 : exp2(gVec2.w*10.5);
vec3 viewSpace = vec3(texCoord*2.0 - 1.0, depth*2.0 - 1.0);
vec4 worldPos = InvViewProjMatrix * vec4(viewSpace, 1.0);
worldPos.xyz /= worldPos.w;
vec3 lightDir = LightParameters1.xyz - worldPos.xyz;
float lightDirLength = length(lightDir);
lightDir /= lightDirLength;
float att = max(LightParameters1.w - LightParameters2.w*lightDirLength, 0.0);
// Ambient
vec3 lightAmbient = att * LightColor.rgb * LightFactors.x * (vec3(1.0) + SceneAmbient.rgb);
if (LightType == LIGHT_SPOT)
{
// Modification de l'atténuation pour gérer le spot
float curAngle = dot(LightParameters2.xyz, -lightDir);
float outerAngle = LightParameters3.y;
float innerMinusOuterAngle = LightParameters3.x - outerAngle;
att *= max((curAngle - outerAngle) / innerMinusOuterAngle, 0.0);
}
// Diffuse
float lambert = max(dot(normal, lightDir), 0.0);
vec3 lightDiffuse = att * lambert * LightColor.rgb * LightFactors.y;
// Specular
vec3 lightSpecular;
if (shininess > 0.0)
{
vec3 eyeVec = normalize(EyePosition - worldPos.xyz);
vec3 reflection = reflect(-lightDir, normal);
float specularFactor = max(dot(reflection, eyeVec), 0.0);
specularFactor = pow(specularFactor, shininess);
lightSpecular = att * specularFactor * LightColor.rgb * specularMultiplier;
}
else
lightSpecular = vec3(0.0);
vec3 fragmentColor = diffuseColor * (lightAmbient + lightDiffuse + lightSpecular);
RenderTarget0 = vec4(fragmentColor, 1.0);
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,49 +0,0 @@
#if EARLY_FRAGMENT_TESTS && !ALPHA_TEST
layout(early_fragment_tests) in;
#endif
/********************Entrant********************/
in vec4 vColor;
in vec2 vTexCoord;
/********************Sortant********************/
out vec4 RenderTarget0;
/********************Uniformes********************/
uniform vec2 InvTargetSize;
uniform sampler2D MaterialAlphaMap;
uniform float MaterialAlphaThreshold;
uniform vec4 MaterialDiffuse;
uniform sampler2D MaterialDiffuseMap;
uniform sampler2D TextureOverlay;
/********************Fonctions********************/
void main()
{
vec4 fragmentColor = MaterialDiffuse * vColor;
#if AUTO_TEXCOORDS
vec2 texCoord = gl_FragCoord.xy * InvTargetSize;
#elif TEXTURE_MAPPING
vec2 texCoord = vTexCoord;
#endif
#if DIFFUSE_MAPPING
fragmentColor *= texture(MaterialDiffuseMap, texCoord);
#endif
#if ALPHA_MAPPING
fragmentColor.a *= texture(MaterialAlphaMap, texCoord).r;
#endif
#if FLAG_TEXTUREOVERLAY
fragmentColor *= texture(TextureOverlay, texCoord);
#endif
#if ALPHA_TEST
if (fragmentColor.a < MaterialAlphaThreshold)
discard;
#endif
RenderTarget0 = fragmentColor;
}

View File

@@ -1 +0,0 @@
35,105,102,32,69,65,82,76,89,95,70,82,65,71,77,69,78,84,95,84,69,83,84,83,32,38,38,32,33,65,76,80,72,65,95,84,69,83,84,10,108,97,121,111,117,116,40,101,97,114,108,121,95,102,114,97,103,109,101,110,116,95,116,101,115,116,115,41,32,105,110,59,10,35,101,110,100,105,102,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,69,110,116,114,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,105,110,32,118,101,99,52,32,118,67,111,108,111,114,59,10,105,110,32,118,101,99,50,32,118,84,101,120,67,111,111,114,100,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,83,111,114,116,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,85,110,105,102,111,114,109,101,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,117,110,105,102,111,114,109,32,118,101,99,50,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,59,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,59,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,84,101,120,116,117,114,101,79,118,101,114,108,97,121,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,70,111,110,99,116,105,111,110,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,118,111,105,100,32,109,97,105,110,40,41,10,123,10,9,118,101,99,52,32,102,114,97,103,109,101,110,116,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,32,42,32,118,67,111,108,111,114,59,10,10,35,105,102,32,65,85,84,79,95,84,69,88,67,79,79,82,68,83,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,103,108,95,70,114,97,103,67,111,111,114,100,46,120,121,32,42,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,35,101,108,105,102,32,84,69,88,84,85,82,69,95,77,65,80,80,73,78,71,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,118,84,101,120,67,111,111,114,100,59,10,35,101,110,100,105,102,10,10,35,105,102,32,68,73,70,70,85,83,69,95,77,65,80,80,73,78,71,10,9,102,114,97,103,109,101,110,116,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,44,32,116,101,120,67,111,111,114,100,41,59,10,35,101,110,100,105,102,10,10,35,105,102,32,65,76,80,72,65,95,77,65,80,80,73,78,71,10,9,102,114,97,103,109,101,110,116,67,111,108,111,114,46,97,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,10,35,101,110,100,105,102,10,10,35,105,102,32,70,76,65,71,95,84,69,88,84,85,82,69,79,86,69,82,76,65,89,10,9,102,114,97,103,109,101,110,116,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,84,101,120,116,117,114,101,79,118,101,114,108,97,121,44,32,116,101,120,67,111,111,114,100,41,59,10,35,101,110,100,105,102,10,10,35,105,102,32,65,76,80,72,65,95,84,69,83,84,10,9,105,102,32,40,102,114,97,103,109,101,110,116,67,111,108,111,114,46,97,32,60,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,41,10,9,9,100,105,115,99,97,114,100,59,10,35,101,110,100,105,102,10,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,102,114,97,103,109,101,110,116,67,111,108,111,114,59,10,125,

View File

@@ -1,103 +0,0 @@
/********************Entrant********************/
#if FLAG_BILLBOARD
in vec3 InstanceData0; // center
in vec4 InstanceData1; // size | sin cos
in vec4 InstanceData2; // color
#else
in mat4 InstanceData0;
#endif
in vec4 VertexColor;
in vec3 VertexPosition;
in vec2 VertexTexCoord;
in vec4 VertexUserdata0;
/********************Sortant********************/
out vec4 vColor;
out vec2 vTexCoord;
/********************Uniformes********************/
uniform float VertexDepth;
uniform mat4 ViewMatrix;
uniform mat4 ViewProjMatrix;
uniform mat4 WorldViewProjMatrix;
/********************Fonctions********************/
void main()
{
#if FLAG_VERTEXCOLOR
vec4 color = VertexColor;
#else
vec4 color = vec4(1.0);
#endif
vec2 texCoords;
#if FLAG_BILLBOARD
#if FLAG_INSTANCING
vec3 billboardCenter = InstanceData0;
vec2 billboardSize = InstanceData1.xy;
vec2 billboardSinCos = InstanceData1.zw;
vec4 billboardColor = InstanceData2;
vec2 rotatedPosition;
rotatedPosition.x = VertexPosition.x*billboardSinCos.y - VertexPosition.y*billboardSinCos.x;
rotatedPosition.y = VertexPosition.y*billboardSinCos.y + VertexPosition.x*billboardSinCos.x;
rotatedPosition *= billboardSize;
vec3 cameraRight = vec3(ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]);
vec3 cameraUp = vec3(ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]);
vec3 vertexPos = billboardCenter + cameraRight*rotatedPosition.x + cameraUp*rotatedPosition.y;
gl_Position = ViewProjMatrix * vec4(vertexPos, 1.0);
color = billboardColor;
texCoords = VertexPosition.xy + 0.5;
#else
vec2 billboardCorner = VertexTexCoord - 0.5;
vec2 billboardSize = VertexUserdata0.xy;
vec2 billboardSinCos = VertexUserdata0.zw;
vec2 rotatedPosition;
rotatedPosition.x = billboardCorner.x*billboardSinCos.y - billboardCorner.y*billboardSinCos.x;
rotatedPosition.y = billboardCorner.y*billboardSinCos.y + billboardCorner.x*billboardSinCos.x;
rotatedPosition *= billboardSize;
vec3 cameraRight = vec3(ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]);
vec3 cameraUp = vec3(ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]);
vec3 vertexPos = VertexPosition + cameraRight*rotatedPosition.x + cameraUp*rotatedPosition.y;
gl_Position = ViewProjMatrix * vec4(vertexPos, 1.0);
texCoords = VertexTexCoord;
#endif
texCoords.y = 1.0 - texCoords.y;
#else
#if FLAG_INSTANCING
#if TRANSFORM
gl_Position = ViewProjMatrix * InstanceData0 * vec4(VertexPosition, 1.0);
#else
#if UNIFORM_VERTEX_DEPTH
gl_Position = InstanceData0 * vec4(VertexPosition.xy, VertexDepth, 1.0);
#else
gl_Position = InstanceData0 * vec4(VertexPosition, 1.0);
#endif
#endif
#else
#if TRANSFORM
gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);
#else
#if UNIFORM_VERTEX_DEPTH
gl_Position = vec4(VertexPosition.xy, VertexDepth, 1.0);
#else
gl_Position = vec4(VertexPosition, 1.0);
#endif
#endif
#endif
texCoords = VertexTexCoord;
#endif
vColor = color;
#if TEXTURE_MAPPING
vTexCoord = vec2(texCoords);
#endif
}

File diff suppressed because one or more lines are too long

View File

@@ -1,475 +0,0 @@
#if EARLY_FRAGMENT_TESTS && !ALPHA_TEST
layout(early_fragment_tests) in;
#endif
// HACK UNTIL PROPER FIX
#if GLSL_VERSION < 400
#undef SHADOW_MAPPING
#define SHADOW_MAPPING 0
#endif
// HACK
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
#define LIGHT_SPOT 2
/********************Entrant********************/
in vec4 vColor;
in vec4 vLightSpacePos[3];
in mat3 vLightToWorld;
in vec3 vNormal;
in vec2 vTexCoord;
in vec3 vViewDir;
in vec3 vWorldPos;
/********************Sortant********************/
out vec4 RenderTarget0;
out vec4 RenderTarget1;
out vec4 RenderTarget2;
/********************Uniformes********************/
struct Light
{
int type;
vec4 color;
vec2 factors;
vec4 parameters1;
vec4 parameters2;
vec2 parameters3;
bool shadowMapping;
};
// Lumières
uniform Light Lights[3];
uniform samplerCube PointLightShadowMap[3];
uniform sampler2D DirectionalSpotLightShadowMap[3];
// Matériau
uniform sampler2D MaterialAlphaMap;
uniform float MaterialAlphaThreshold;
uniform vec4 MaterialAmbient;
uniform vec4 MaterialDiffuse;
uniform sampler2D MaterialDiffuseMap;
uniform sampler2D MaterialEmissiveMap;
uniform sampler2D MaterialHeightMap;
uniform sampler2D MaterialNormalMap;
uniform float MaterialShininess;
uniform vec4 MaterialSpecular;
uniform sampler2D MaterialSpecularMap;
// Autres
uniform float ParallaxBias = -0.03;
uniform float ParallaxScale = 0.02;
uniform vec2 InvTargetSize;
uniform vec3 EyePosition;
uniform samplerCube ReflectionMap;
uniform vec4 SceneAmbient;
uniform sampler2D TextureOverlay;
/********************Fonctions********************/
#define kPI 3.1415926536
vec4 EncodeNormal(in vec3 normal)
{
//return vec4(normal*0.5 + 0.5, 0.0);
return vec4(vec2(atan(normal.y, normal.x)/kPI, normal.z), 0.0, 0.0);
}
float VectorToDepthValue(vec3 vec, float zNear, float zFar)
{
vec3 absVec = abs(vec);
float localZ = max(absVec.x, max(absVec.y, absVec.z));
float normZ = ((zFar + zNear) * localZ - (2.0*zFar*zNear)) / ((zFar - zNear)*localZ);
return (normZ + 1.0) * 0.5;
}
#if SHADOW_MAPPING
float CalculateDirectionalShadowFactor(int lightIndex)
{
vec4 lightSpacePos = vLightSpacePos[lightIndex];
return (texture(DirectionalSpotLightShadowMap[lightIndex], lightSpacePos.xy).x >= (lightSpacePos.z - 0.0005)) ? 1.0 : 0.0;
}
float CalculatePointShadowFactor(int lightIndex, vec3 lightToWorld, float zNear, float zFar)
{
return (texture(PointLightShadowMap[lightIndex], vec3(lightToWorld.x, -lightToWorld.y, -lightToWorld.z)).x >= VectorToDepthValue(lightToWorld, zNear, zFar)) ? 1.0 : 0.0;
}
float CalculateSpotShadowFactor(int lightIndex)
{
vec4 lightSpacePos = vLightSpacePos[lightIndex];
float visibility = 1.0;
float x,y;
for (y = -3.5; y <= 3.5; y+= 1.0)
for (x = -3.5; x <= 3.5; x+= 1.0)
visibility += (textureProj(DirectionalSpotLightShadowMap[lightIndex], lightSpacePos.xyw + vec3(x/1024.0 * lightSpacePos.w, y/1024.0 * lightSpacePos.w, 0.0)).x >= (lightSpacePos.z - 0.0005)/lightSpacePos.w) ? 1.0 : 0.0;
visibility /= 64.0;
return visibility;
}
#endif
void main()
{
vec4 diffuseColor = MaterialDiffuse * vColor;
#if AUTO_TEXCOORDS
vec2 texCoord = gl_FragCoord.xy * InvTargetSize;
#else
vec2 texCoord = vTexCoord;
#endif
#if PARALLAX_MAPPING
float height = texture(MaterialHeightMap, texCoord).r;
float v = height*ParallaxScale + ParallaxBias;
vec3 viewDir = normalize(vViewDir);
texCoord += v * viewDir.xy;
#endif
#if DIFFUSE_MAPPING
diffuseColor *= texture(MaterialDiffuseMap, texCoord);
#endif
#if FLAG_TEXTUREOVERLAY
diffuseColor *= texture(TextureOverlay, texCoord);
#endif
#if FLAG_DEFERRED
#if ALPHA_TEST
// Inutile de faire de l'alpha-mapping sans alpha-test en Deferred (l'alpha n'est pas sauvegardé dans le G-Buffer)
#if ALPHA_MAPPING
diffuseColor.a *= texture(MaterialAlphaMap, texCoord).r;
#endif
if (diffuseColor.a < MaterialAlphaThreshold)
discard;
#endif // ALPHA_TEST
#if NORMAL_MAPPING
vec3 normal = normalize(vLightToWorld * (2.0 * vec3(texture(MaterialNormalMap, texCoord)) - 1.0));
#else
vec3 normal = normalize(vNormal);
#endif // NORMAL_MAPPING
vec3 specularColor = MaterialSpecular.rgb;
#if SPECULAR_MAPPING
specularColor *= texture(MaterialSpecularMap, texCoord).rgb;
#endif
/*
Texture0: Diffuse Color + Specular
Texture1: Normal + Specular
Texture2: Encoded depth + Shininess
*/
RenderTarget0 = vec4(diffuseColor.rgb, dot(specularColor, vec3(0.3, 0.59, 0.11)));
RenderTarget1 = vec4(EncodeNormal(normal));
RenderTarget2 = vec4(0.0, 0.0, 0.0, (MaterialShininess == 0.0) ? 0.0 : max(log2(MaterialShininess), 0.1)/10.5); // http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf
#else // FLAG_DEFERRED
#if ALPHA_MAPPING
diffuseColor.a *= texture(MaterialAlphaMap, texCoord).r;
#endif
#if ALPHA_TEST
if (diffuseColor.a < MaterialAlphaThreshold)
discard;
#endif
vec3 lightAmbient = vec3(0.0);
vec3 lightDiffuse = vec3(0.0);
vec3 lightSpecular = vec3(0.0);
#if NORMAL_MAPPING
vec3 normal = normalize(vLightToWorld * (2.0 * vec3(texture(MaterialNormalMap, texCoord)) - 1.0));
#else
vec3 normal = normalize(vNormal);
#endif
if (MaterialShininess > 0.0)
{
vec3 eyeVec = normalize(EyePosition - vWorldPos);
for (int i = 0; i < 3; ++i)
{
vec4 lightColor = Lights[i].color;
float lightAmbientFactor = Lights[i].factors.x;
float lightDiffuseFactor = Lights[i].factors.y;
switch (Lights[i].type)
{
case LIGHT_DIRECTIONAL:
{
vec3 lightDir = -Lights[i].parameters1.xyz;
// Ambient
lightAmbient += lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb);
float att = 1.0;
#if SHADOW_MAPPING
if (Lights[i].shadowMapping)
{
float shadowFactor = CalculateDirectionalShadowFactor(i);
if (shadowFactor == 0.0)
break;
att *= shadowFactor;
}
#endif
// Diffuse
float lambert = max(dot(normal, lightDir), 0.0);
lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor;
// Specular
vec3 reflection = reflect(-lightDir, normal);
float specularFactor = max(dot(reflection, eyeVec), 0.0);
specularFactor = pow(specularFactor, MaterialShininess);
lightSpecular += att * specularFactor * lightColor.rgb;
break;
}
case LIGHT_POINT:
{
vec3 lightPos = Lights[i].parameters1.xyz;
float lightAttenuation = Lights[i].parameters1.w;
float lightInvRadius = Lights[i].parameters2.w;
vec3 worldToLight = lightPos - vWorldPos;
float lightDirLength = length(worldToLight);
vec3 lightDir = worldToLight / lightDirLength; // Normalisation
float att = max(lightAttenuation - lightInvRadius * lightDirLength, 0.0);
// Ambient
lightAmbient += att * lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb);
#if SHADOW_MAPPING
if (Lights[i].shadowMapping)
{
float shadowFactor = CalculatePointShadowFactor(i, vWorldPos - lightPos, 0.1, 50.0);
if (shadowFactor == 0.0)
break;
att *= shadowFactor;
}
#endif
// Diffuse
float lambert = max(dot(normal, lightDir), 0.0);
lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor;
// Specular
vec3 reflection = reflect(-lightDir, normal);
float specularFactor = max(dot(reflection, eyeVec), 0.0);
specularFactor = pow(specularFactor, MaterialShininess);
lightSpecular += att * specularFactor * lightColor.rgb;
break;
}
case LIGHT_SPOT:
{
vec3 lightPos = Lights[i].parameters1.xyz;
vec3 lightDir = Lights[i].parameters2.xyz;
float lightAttenuation = Lights[i].parameters1.w;
float lightInvRadius = Lights[i].parameters2.w;
float lightInnerAngle = Lights[i].parameters3.x;
float lightOuterAngle = Lights[i].parameters3.y;
vec3 worldToLight = lightPos - vWorldPos;
float lightDistance = length(worldToLight);
worldToLight /= lightDistance; // Normalisation
float att = max(lightAttenuation - lightInvRadius * lightDistance, 0.0);
// Ambient
lightAmbient += att * lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb);
#if SHADOW_MAPPING
if (Lights[i].shadowMapping)
{
float shadowFactor = CalculateSpotShadowFactor(i);
if (shadowFactor == 0.0)
break;
att *= shadowFactor;
}
#endif
// Modification de l'atténuation pour gérer le spot
float curAngle = dot(lightDir, -worldToLight);
float innerMinusOuterAngle = lightInnerAngle - lightOuterAngle;
att *= max((curAngle - lightOuterAngle) / innerMinusOuterAngle, 0.0);
// Diffuse
float lambert = max(dot(normal, worldToLight), 0.0);
lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor;
// Specular
vec3 reflection = reflect(-worldToLight, normal);
float specularFactor = max(dot(reflection, eyeVec), 0.0);
specularFactor = pow(specularFactor, MaterialShininess);
lightSpecular += att * specularFactor * lightColor.rgb;
break;
}
default:
break;
}
}
}
else
{
for (int i = 0; i < 3; ++i)
{
vec4 lightColor = Lights[i].color;
float lightAmbientFactor = Lights[i].factors.x;
float lightDiffuseFactor = Lights[i].factors.y;
switch (Lights[i].type)
{
case LIGHT_DIRECTIONAL:
{
vec3 lightDir = -Lights[i].parameters1.xyz;
// Ambient
lightAmbient += lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb);
float att = 1.0;
#if SHADOW_MAPPING
if (Lights[i].shadowMapping)
{
float shadowFactor = CalculateDirectionalShadowFactor(i);
if (shadowFactor == 0.0)
break;
att *= shadowFactor;
}
#endif
// Diffuse
float lambert = max(dot(normal, lightDir), 0.0);
lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor;
break;
}
case LIGHT_POINT:
{
vec3 lightPos = Lights[i].parameters1.xyz;
float lightAttenuation = Lights[i].parameters1.w;
float lightInvRadius = Lights[i].parameters2.w;
vec3 worldToLight = lightPos - vWorldPos;
float lightDirLength = length(worldToLight);
vec3 lightDir = worldToLight / lightDirLength; // Normalisation
float att = max(lightAttenuation - lightInvRadius * lightDirLength, 0.0);
// Ambient
lightAmbient += att * lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb);
#if SHADOW_MAPPING
if (Lights[i].shadowMapping)
{
float shadowFactor = CalculatePointShadowFactor(i, vWorldPos - lightPos, 0.1, 50.0);
if (shadowFactor == 0.0)
break;
att *= shadowFactor;
}
#endif
// Diffuse
float lambert = max(dot(normal, lightDir), 0.0);
lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor;
break;
}
case LIGHT_SPOT:
{
vec3 lightPos = Lights[i].parameters1.xyz;
vec3 lightDir = Lights[i].parameters2.xyz;
float lightAttenuation = Lights[i].parameters1.w;
float lightInvRadius = Lights[i].parameters2.w;
float lightInnerAngle = Lights[i].parameters3.x;
float lightOuterAngle = Lights[i].parameters3.y;
vec3 worldToLight = lightPos - vWorldPos;
float lightDistance = length(worldToLight);
worldToLight /= lightDistance; // Normalisation
float att = max(lightAttenuation - lightInvRadius * lightDistance, 0.0);
// Ambient
lightAmbient += att * lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb);
#if SHADOW_MAPPING
if (Lights[i].shadowMapping)
{
float shadowFactor = CalculateSpotShadowFactor(i);
if (shadowFactor == 0.0)
break;
att *= shadowFactor;
}
#endif
// Modification de l'atténuation pour gérer le spot
float curAngle = dot(lightDir, -worldToLight);
float innerMinusOuterAngle = lightInnerAngle - lightOuterAngle;
att *= max((curAngle - lightOuterAngle) / innerMinusOuterAngle, 0.0);
// Diffuse
float lambert = max(dot(normal, worldToLight), 0.0);
lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor;
}
default:
break;
}
}
}
lightSpecular *= MaterialSpecular.rgb;
#if SPECULAR_MAPPING
lightSpecular *= texture(MaterialSpecularMap, texCoord).rgb; // Utiliser l'alpha de MaterialSpecular n'aurait aucun sens
#endif
vec3 lightColor = (lightAmbient + lightDiffuse + lightSpecular);
#if REFLECTION_MAPPING
vec3 eyeVec = normalize(vWorldPos - EyePosition);
vec3 reflected = normalize(reflect(eyeVec, normal));
lightColor *= texture(ReflectionMap, reflected).rgb;
#endif
vec4 fragmentColor = vec4(lightColor, 1.0) * diffuseColor;
#if EMISSIVE_MAPPING
float lightIntensity = dot(lightColor, vec3(0.3, 0.59, 0.11));
vec3 emissionColor = MaterialDiffuse.rgb * texture(MaterialEmissiveMap, texCoord).rgb;
RenderTarget0 = vec4(mix(fragmentColor.rgb, emissionColor, clamp(1.0 - 3.0*lightIntensity, 0.0, 1.0)), fragmentColor.a);
#else
RenderTarget0 = fragmentColor;
#endif // EMISSIVE_MAPPING
#endif // FLAG_DEFERRED
}

File diff suppressed because one or more lines are too long

View File

@@ -1,148 +0,0 @@
/********************Entrant********************/
#if FLAG_BILLBOARD
in vec3 InstanceData0; // center
in vec4 InstanceData1; // size | sin cos
in vec4 InstanceData2; // color
#else
in mat4 InstanceData0;
#endif
in vec4 VertexColor;
in vec3 VertexPosition;
in vec3 VertexNormal;
in vec3 VertexTangent;
in vec2 VertexTexCoord;
in vec4 VertexUserdata0;
/********************Sortant********************/
out vec4 vColor;
out vec4 vLightSpacePos[3];
out mat3 vLightToWorld;
out vec3 vNormal;
out vec2 vTexCoord;
out vec3 vViewDir;
out vec3 vWorldPos;
/********************Uniformes********************/
uniform vec3 EyePosition;
uniform mat4 InvViewMatrix;
uniform mat4 LightViewProjMatrix[3];
uniform float VertexDepth;
uniform mat4 ViewMatrix;
uniform mat4 ViewProjMatrix;
uniform mat4 WorldMatrix;
uniform mat4 WorldViewProjMatrix;
/********************Fonctions********************/
void main()
{
#if FLAG_VERTEXCOLOR
vec4 color = VertexColor;
#else
vec4 color = vec4(1.0);
#endif
vec2 texCoords;
#if FLAG_BILLBOARD
#if FLAG_INSTANCING
vec3 billboardCenter = InstanceData0;
vec2 billboardSize = InstanceData1.xy;
vec2 billboardSinCos = InstanceData1.zw;
vec4 billboardColor = InstanceData2;
vec2 rotatedPosition;
rotatedPosition.x = VertexPosition.x*billboardSinCos.y - VertexPosition.y*billboardSinCos.x;
rotatedPosition.y = VertexPosition.y*billboardSinCos.y + VertexPosition.x*billboardSinCos.x;
rotatedPosition *= billboardSize;
vec3 cameraRight = vec3(ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]);
vec3 cameraUp = vec3(ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]);
vec3 vertexPos = billboardCenter + cameraRight*rotatedPosition.x + cameraUp*rotatedPosition.y;
gl_Position = ViewProjMatrix * vec4(vertexPos, 1.0);
color = billboardColor;
texCoords = VertexPosition.xy + 0.5;
#else
vec2 billboardCorner = VertexTexCoord - 0.5;
vec2 billboardSize = VertexUserdata0.xy;
vec2 billboardSinCos = VertexUserdata0.zw;
vec2 rotatedPosition;
rotatedPosition.x = billboardCorner.x*billboardSinCos.y - billboardCorner.y*billboardSinCos.x;
rotatedPosition.y = billboardCorner.y*billboardSinCos.y + billboardCorner.x*billboardSinCos.x;
rotatedPosition *= billboardSize;
vec3 cameraRight = vec3(ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]);
vec3 cameraUp = vec3(ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]);
vec3 vertexPos = VertexPosition + cameraRight*rotatedPosition.x + cameraUp*rotatedPosition.y;
gl_Position = ViewProjMatrix * vec4(vertexPos, 1.0);
texCoords = VertexTexCoord;
#endif
texCoords.y = 1.0 - texCoords.y;
#else
#if FLAG_INSTANCING
#if TRANSFORM
gl_Position = ViewProjMatrix * InstanceData0 * vec4(VertexPosition, 1.0);
#else
#if UNIFORM_VERTEX_DEPTH
gl_Position = InstanceData0 * vec4(VertexPosition.xy, VertexDepth, 1.0);
#else
gl_Position = InstanceData0 * vec4(VertexPosition, 1.0);
#endif
#endif
#else
#if TRANSFORM
gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);
#else
#if UNIFORM_VERTEX_DEPTH
gl_Position = vec4(VertexPosition.xy, VertexDepth, 1.0);
#else
gl_Position = vec4(VertexPosition, 1.0);
#endif
#endif
#endif
texCoords = VertexTexCoord;
#endif
vColor = color;
#if FLAG_INSTANCING
mat3 rotationMatrix = mat3(InstanceData0);
#else
mat3 rotationMatrix = mat3(WorldMatrix);
#endif
#if COMPUTE_TBNMATRIX
vec3 binormal = cross(VertexNormal, VertexTangent);
vLightToWorld[0] = normalize(rotationMatrix * VertexTangent);
vLightToWorld[1] = normalize(rotationMatrix * binormal);
vLightToWorld[2] = normalize(rotationMatrix * VertexNormal);
#else
vNormal = normalize(rotationMatrix * VertexNormal);
#endif
#if SHADOW_MAPPING
for (int i = 0; i < 3; ++i)
vLightSpacePos[i] = LightViewProjMatrix[i] * WorldMatrix * vec4(VertexPosition, 1.0);
#endif
#if TEXTURE_MAPPING
vTexCoord = VertexTexCoord;
#endif
#if PARALLAX_MAPPING
vViewDir = EyePosition - VertexPosition;
vViewDir *= vLightToWorld;
#endif
#if !FLAG_DEFERRED
#if FLAG_INSTANCING
vWorldPos = vec3(InstanceData0 * vec4(VertexPosition, 1.0));
#else
vWorldPos = vec3(WorldMatrix * vec4(VertexPosition, 1.0));
#endif
#endif
}

File diff suppressed because one or more lines are too long

View File

@@ -1,395 +0,0 @@
// Copyright (C) 2020 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/SkeletalModel.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/SkinningManager.hpp>
#include <Nazara/Utility/MeshData.hpp>
#include <Nazara/Utility/Sequence.hpp>
#include <Nazara/Utility/SkeletalMesh.hpp>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::SkeletalModel
* \brief Graphics class that represents a model with a skeleton
*/
/*!
* \brief Checks whether the parameters for the skeletal mesh are correct
* \return true If parameters are valid
*/
bool SkeletalModelParameters::IsValid() const
{
if (!ModelParameters::IsValid())
return false;
if (loadAnimation && !animation.IsValid())
return false;
return true;
}
/*!
* \brief Constructs a SkeletalModel object by default
*/
SkeletalModel::SkeletalModel() :
m_currentSequence(nullptr),
m_animationEnabled(true)
{
}
/*!
* \brief Adds the skeletal mesh to the rendering queue
*
* \param renderQueue Queue to be added
* \param instanceData Data for the instance
*/
void SkeletalModel::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const
{
if (!m_mesh)
return;
unsigned int submeshCount = m_mesh->GetSubMeshCount();
for (unsigned int i = 0; i < submeshCount; ++i)
{
const SkeletalMesh* mesh = static_cast<const SkeletalMesh*>(m_mesh->GetSubMesh(i));
const Material* material = GetMaterial(mesh->GetMaterialIndex());
MeshData meshData;
meshData.indexBuffer = mesh->GetIndexBuffer();
meshData.primitiveMode = mesh->GetPrimitiveMode();
meshData.vertexBuffer = SkinningManager::GetBuffer(mesh, &m_skeleton);
renderQueue->AddMesh(instanceData.renderOrder, material, meshData, m_skeleton.GetAABB(), instanceData.transformMatrix, scissorRect);
}
}
/*!
* \brief Updates the animation of the mesh
*
* \param elapsedTime Delta time between two frames
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if there is no animation
*/
void SkeletalModel::AdvanceAnimation(float elapsedTime)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
{
NazaraError("Model has no animation");
return;
}
#endif
m_interpolation += m_currentSequence->frameRate * elapsedTime;
while (m_interpolation > 1.f)
{
m_interpolation -= 1.f;
unsigned lastFrame = m_currentSequence->firstFrame + m_currentSequence->frameCount - 1;
if (m_nextFrame + 1 > lastFrame)
{
if (m_animation->IsLoopPointInterpolationEnabled())
{
m_currentFrame = m_nextFrame;
m_nextFrame = m_currentSequence->firstFrame;
}
else
{
m_currentFrame = m_currentSequence->firstFrame;
m_nextFrame = m_currentFrame + 1;
}
}
else
{
m_currentFrame = m_nextFrame;
m_nextFrame++;
}
}
m_animation->AnimateSkeleton(&m_skeleton, m_currentFrame, m_nextFrame, m_interpolation);
InvalidateBoundingVolume();
}
/*!
* \brief Clones this skeletal model
* \return Pointer to newly allocated SkeletalModel
*/
std::unique_ptr<InstancedRenderable> SkeletalModel::Clone() const
{
return std::make_unique<SkeletalModel>(*this);
}
/*!
* \brief Creates a default skeletal model
* \return Pointer to newly allocated SkeletalModel
*/
SkeletalModel* SkeletalModel::Create() const
{
return new SkeletalModel;
}
/*!
* \brief Enables the animation of the model
*
* \param animation Should the model be animated
*/
void SkeletalModel::EnableAnimation(bool animation)
{
m_animationEnabled = animation;
}
/*!
* \brief Gets the animation of the model
* \return Pointer to the animation
*/
Animation* SkeletalModel::GetAnimation() const
{
return m_animation;
}
/*!
* \brief Gets the skeleton of the model
* \return Pointer to the skeleton
*/
Skeleton* SkeletalModel::GetSkeleton()
{
InvalidateBoundingVolume();
return &m_skeleton;
}
/*!
* \brief Gets the skeleton of the model
* \return Constant pointer to the skeleton
*/
const Skeleton* SkeletalModel::GetSkeleton() const
{
return &m_skeleton;
}
/*!
* \brief Checks whether the skeleton has an animation
* \return true If it is the case
*
* \see IsAnimated, IsAnimationEnabled
*/
bool SkeletalModel::HasAnimation() const
{
return m_animation != nullptr;
}
/*!
* \brief Checks whether the skeleton is animated
* \return true
*
* \see HasAnimation, IsAnimationEnabled
*/
bool SkeletalModel::IsAnimated() const
{
return true;
}
/*!
* \brief Checks whether the skeleton is currently animated
* \return true If it is the case
*
* \see HasAnimation, IsAnimated
*/
bool SkeletalModel::IsAnimationEnabled() const
{
return m_animationEnabled;
}
/*!
* \brief Sets the animation for the model
* \return true If successful
*
* \param animation Animation for the model
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no mesh
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if animation is invalid
*/
bool SkeletalModel::SetAnimation(Animation* animation)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_mesh)
{
NazaraError("Model has no mesh");
return false;
}
if (animation)
{
if (!animation->IsValid())
{
NazaraError("Invalid animation");
return false;
}
if (animation->GetType() != m_mesh->GetAnimationType())
{
NazaraError("Animation type must match mesh animation type");
return false;
}
if (animation->GetJointCount() != m_mesh->GetJointCount())
{
NazaraError("Animation joint count must match mesh joint count");
return false;
}
}
#endif
m_animation = animation;
if (m_animation)
{
m_currentFrame = 0;
m_interpolation = 0.f;
SetSequence(0);
}
return true;
}
/*!
* \brief Sets the mesh for the model
*
* \param mesh Mesh for the model
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no mesh or if invalid
*/
void SkeletalModel::SetMesh(Mesh* mesh)
{
#if NAZARA_GRAPHICS_SAFE
if (mesh && mesh->GetAnimationType() != AnimationType_Skeletal)
{
NazaraError("Mesh animation type must be skeletal");
return;
}
#endif
Model::SetMesh(mesh);
if (m_mesh)
{
if (m_animation && m_animation->GetJointCount() != m_mesh->GetJointCount())
{
NazaraWarning("Animation joint count is not matching new mesh joint count, disabling animation...");
SetAnimation(nullptr);
}
m_skeleton = *m_mesh->GetSkeleton(); // Copy of skeleton template
}
}
/*!
* \brief Sets the sequence for the model
* \return true If successful
*
* \param sequenceName Name for the sequence animation
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no animation
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if sequence name does not exist for the current animation
*/
bool SkeletalModel::SetSequence(const String& sequenceName)
{
///TODO: Make this error "safe" with the new system of error handling (No-log)
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
{
NazaraError("Model has no animation");
return false;
}
#endif
const Sequence* currentSequence = m_animation->GetSequence(sequenceName);
#if NAZARA_GRAPHICS_SAFE
if (!currentSequence)
{
NazaraError("Sequence not found");
return false;
}
#endif
m_currentSequence = currentSequence;
m_nextFrame = m_currentSequence->firstFrame;
return true;
}
/*!
* \brief Sets the sequence for the model
*
* \param sequenceIndex Index for the sequence animation
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no animation
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if sequence name does not exist for the current animation
*/
void SkeletalModel::SetSequence(unsigned int sequenceIndex)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
{
NazaraError("Model has no animation");
return;
}
#endif
const Sequence* currentSequence = m_animation->GetSequence(sequenceIndex);
#if NAZARA_GRAPHICS_SAFE
if (!currentSequence)
{
NazaraError("Sequence not found");
return;
}
#endif
m_currentSequence = currentSequence;
m_nextFrame = m_currentSequence->firstFrame;
}
/*
* \brief Makes the bounding volume of this text
*/
void SkeletalModel::MakeBoundingVolume() const
{
m_boundingVolume.Set(m_skeleton.GetAABB());
}
/*!
* \brief Updates the model
*/
void SkeletalModel::Update()
{
///TODO
/*if (m_animationEnabled && m_animation)
AdvanceAnimation(m_scene->GetUpdateTime());*/
}
}

View File

@@ -1,262 +0,0 @@
// Copyright (C) 2020 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/SkinningManager.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/TaskScheduler.hpp>
#include <Nazara/Utility/Algorithm.hpp>
#include <Nazara/Utility/Joint.hpp>
#include <Nazara/Utility/Skeleton.hpp>
#include <Nazara/Utility/SkeletalMesh.hpp>
#include <Nazara/Utility/Skeleton.hpp>
#include <Nazara/Utility/VertexBuffer.hpp>
#include <unordered_map>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
struct BufferData
{
NazaraSlot(SkeletalMesh, OnSkeletalMeshDestroy, skeletalMeshDestroySlot);
VertexBufferRef buffer;
bool updated;
};
using MeshMap = std::unordered_map<const SkeletalMesh*, BufferData>;
struct MeshData
{
NazaraSlot(Skeleton, OnSkeletonDestroy, skeletonDestroySlot);
NazaraSlot(Skeleton, OnSkeletonJointsInvalidated, skeletonJointsInvalidatedSlot);
MeshMap meshMap;
};
struct QueueData
{
const SkeletalMesh* mesh;
const Skeleton* skeleton;
VertexBuffer* buffer;
};
using SkeletonMap = std::unordered_map<const Skeleton*, MeshData>;
SkeletonMap s_cache;
std::vector<QueueData> s_skinningQueue;
/*!
* \brief Skins the mesh for a single thread context
*
* \param mesh Skeletal mesh to get vertex buffer from
* \param skeleton Skeleton to consider for getting data
* \param buffer Vertex buffer symbolizing the transition
*/
void Skin_MonoCPU(const SkeletalMesh* mesh, const Skeleton* skeleton, VertexBuffer* buffer)
{
BufferMapper<VertexBuffer> inputMapper(mesh->GetVertexBuffer(), BufferAccess_ReadOnly);
BufferMapper<VertexBuffer> outputMapper(buffer, BufferAccess_DiscardAndWrite);
SkinningData skinningData;
skinningData.inputVertex = static_cast<SkeletalMeshVertex*>(inputMapper.GetPointer());
skinningData.outputVertex = static_cast<MeshVertex*>(outputMapper.GetPointer());
skinningData.joints = skeleton->GetJoints();
SkinPositionNormalTangent(skinningData, 0, mesh->GetVertexCount());
}
/*!
* \brief Skins the mesh for a multi-threaded context
*
* \param mesh Skeletal mesh to get vertex buffer from
* \param skeleton Skeleton to consider for getting data
* \param buffer Vertex buffer symbolizing the transition
*/
void Skin_MultiCPU(const SkeletalMesh* mesh, const Skeleton* skeleton, VertexBuffer* buffer)
{
BufferMapper<VertexBuffer> inputMapper(mesh->GetVertexBuffer(), BufferAccess_ReadOnly);
BufferMapper<VertexBuffer> outputMapper(buffer, BufferAccess_DiscardAndWrite);
SkinningData skinningData;
skinningData.inputVertex = static_cast<SkeletalMeshVertex*>(inputMapper.GetPointer());
skinningData.outputVertex = static_cast<MeshVertex*>(outputMapper.GetPointer());
skinningData.joints = skeleton->GetJoints();
// To avoid different threads to update the same matrix at the same time
// We try to update them before launching the tasks
unsigned int jointCount = skeleton->GetJointCount();
for (unsigned int i = 0; i < jointCount; ++i)
skinningData.joints[i].EnsureSkinningMatrixUpdate();
unsigned int workerCount = TaskScheduler::GetWorkerCount();
std::ldiv_t div = std::ldiv(mesh->GetVertexCount(), workerCount);
for (unsigned int i = 0; i < workerCount; ++i)
TaskScheduler::AddTask(SkinPositionNormalTangent, skinningData, i*div.quot, (i == workerCount-1) ? div.quot + div.rem : div.quot);
TaskScheduler::Run();
TaskScheduler::WaitForTasks();
}
}
/*!
* \ingroup graphics
* \class Nz::SkinningManager
* \brief Graphics class that represents the management of skinning
*/
/*!
* \brief Gets the vertex buffer from a skeletal mesh with its skeleton
* \return A pointer to the vertex buffer newly created
*
* \param mesh Skeletal mesh to get vertex buffer from
* \param skeleton Skeleton to consider for getting data
*
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if mesh is invalid
* \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if skeleton is invalid
*/
VertexBuffer* SkinningManager::GetBuffer(const SkeletalMesh* mesh, const Skeleton* skeleton)
{
#if NAZARA_GRAPHICS_SAFE
if (!mesh)
{
NazaraError("Invalid mesh");
return nullptr;
}
if (!skeleton)
{
NazaraError("Invalid skeleton");
return nullptr;
}
#endif
ErrorFlags flags(ErrorFlag_ThrowException);
SkeletonMap::iterator it = s_cache.find(skeleton);
if (it == s_cache.end())
{
MeshData meshData;
meshData.skeletonDestroySlot.Connect(skeleton->OnSkeletonDestroy, OnSkeletonRelease);
meshData.skeletonJointsInvalidatedSlot.Connect(skeleton->OnSkeletonJointsInvalidated, OnSkeletonInvalidated);
it = s_cache.insert(std::make_pair(skeleton, std::move(meshData))).first;
}
VertexBuffer* buffer;
MeshMap& meshMap = it->second.meshMap;
MeshMap::iterator it2 = meshMap.find(mesh);
if (it2 == meshMap.end())
{
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), mesh->GetVertexCount(), DataStorage_Hardware, BufferUsage_Dynamic);
BufferData data;
data.skeletalMeshDestroySlot.Connect(mesh->OnSkeletalMeshDestroy, OnSkeletalMeshDestroy);
data.buffer = vertexBuffer;
data.updated = true;
meshMap.insert(std::make_pair(mesh, std::move(data)));
s_skinningQueue.push_back(QueueData{mesh, skeleton, vertexBuffer});
buffer = vertexBuffer;
}
else
{
BufferData& data = it2->second;
if (!data.updated)
{
s_skinningQueue.push_back(QueueData{mesh, skeleton, data.buffer});
data.updated = true;
}
buffer = data.buffer;
}
return buffer;
}
/*!
* \brief Skins the skeletal mesh
*/
void SkinningManager::Skin()
{
for (QueueData& data : s_skinningQueue)
s_skinFunc(data.mesh, data.skeleton, data.buffer);
s_skinningQueue.clear();
}
/*!
* \brief Initializes the skinning librairies
* \return true
*/
bool SkinningManager::Initialize()
{
///TODO: GPU Skinning
if (TaskScheduler::Initialize())
s_skinFunc = Skin_MultiCPU;
else
s_skinFunc = Skin_MonoCPU;
return true; // Nothing particular to do
}
/*!
* \brief Handle the destruction of a skeletal mesh
*
* \param mesh SkeletalMesh being destroyed
*/
void SkinningManager::OnSkeletalMeshDestroy(const SkeletalMesh* mesh)
{
for (auto& pair : s_cache)
{
MeshMap& meshMap = pair.second.meshMap;
meshMap.erase(mesh);
}
}
/*!
* \brief Handle the invalidation of a skeletal mesh
*
* \param mesh SkeletalMesh being invalidated
*/
void SkinningManager::OnSkeletonInvalidated(const Skeleton* skeleton)
{
for (auto& pair : s_cache.at(skeleton).meshMap)
pair.second.updated = false;
}
/*!
* \brief Handle the release of a skeletal mesh
*
* \param skeleton SkeletalMesh being released
*/
void SkinningManager::OnSkeletonRelease(const Skeleton* skeleton)
{
s_cache.erase(skeleton);
}
/*!
* \brief Uninitializes the skinning librairies
*/
void SkinningManager::Uninitialize()
{
s_cache.clear();
s_skinningQueue.clear();
}
SkinningManager::SkinFunction SkinningManager::s_skinFunc = nullptr;
}

View File

@@ -1,220 +0,0 @@
// Copyright (C) 2020 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/SkyboxBackground.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderStates.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Renderer/Shader.hpp>
#include <Nazara/Utility/IndexBuffer.hpp>
#include <Nazara/Utility/VertexBuffer.hpp>
#include <Nazara/Utility/VertexDeclaration.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
static IndexBufferRef s_indexBuffer;
static RenderStates s_renderStates;
static ShaderRef s_shader;
static VertexBufferRef s_vertexBuffer;
}
/*!
* \ingroup graphics
* \class Nz::SkyboxBackground
* \brief Graphics class that represents a background with a cubemap texture
*/
/*!
* \brief Constructs a SkyboxBackground object with a cubemap texture
*
* \param cubemapTexture Cubemap texture
*/
SkyboxBackground::SkyboxBackground(TextureRef cubemapTexture) :
m_movementOffset(Vector3f::Zero()),
m_movementScale(0.f)
{
m_sampler.SetWrapMode(SamplerWrap_Clamp); // We don't want to see any beam
SetTexture(std::move(cubemapTexture));
}
/*!
* \brief Draws this relatively to the viewer
*
* \param viewer Viewer for the background
*/
void SkyboxBackground::Draw(const AbstractViewer* viewer) const
{
const Nz::RenderTarget* target = viewer->GetTarget();
Nz::Vector2ui targetSize = target->GetSize();
Matrix4f skyboxMatrix(viewer->GetViewMatrix());
skyboxMatrix.SetTranslation(Vector3f::Zero());
float zNear = viewer->GetZNear();
constexpr float movementLimit = 0.05f;
Vector3f offset = (viewer->GetEyePosition() - m_movementOffset) * -m_movementScale;
offset.x = Clamp(offset.x, -movementLimit, movementLimit);
offset.y = Clamp(offset.y, -movementLimit, movementLimit);
offset.z = Clamp(offset.z, -movementLimit, movementLimit);
offset *= zNear;
Matrix4f world;
world.MakeIdentity();
world.SetScale(Vector3f(zNear));
world.SetTranslation(offset);
Renderer::SetIndexBuffer(s_indexBuffer);
Renderer::SetMatrix(MatrixType_Projection, viewer->GetProjectionMatrix());
Renderer::SetMatrix(MatrixType_View, skyboxMatrix);
Renderer::SetMatrix(MatrixType_World, world);
Renderer::SetRenderStates(s_renderStates);
Renderer::SetShader(s_shader);
Renderer::SetTexture(0, m_texture);
Renderer::SetTextureSampler(0, m_sampler);
Renderer::SetVertexBuffer(s_vertexBuffer);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, 36);
Renderer::SetMatrix(MatrixType_Projection, viewer->GetProjectionMatrix());
Renderer::SetMatrix(MatrixType_View, viewer->GetViewMatrix());
}
/*!
* \brief Gets the background type
* \return Type of background
*/
BackgroundType SkyboxBackground::GetBackgroundType() const
{
return BackgroundType_Skybox;
}
/*!
* \brief Initializes the skybox
* \return true If successful
*
* \remark Produces a NazaraError if initialization failed
*/
bool SkyboxBackground::Initialize()
{
const UInt16 indices[6 * 6] =
{
0, 1, 2, 0, 2, 3,
3, 2, 6, 3, 6, 7,
7, 6, 5, 7, 5, 4,
4, 5, 1, 4, 1, 0,
0, 3, 7, 0, 7, 4,
1, 6, 2, 1, 5, 6
};
const float vertices[8 * 3 * sizeof(float)] =
{
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
};
///TODO: Replace by ShaderNode (probably after Vulkan)
const char* fragmentShaderSource =
"#version 140\n"
"in vec3 vTexCoord;\n"
"out vec4 RenderTarget0;\n"
"uniform samplerCube Skybox;\n"
"uniform float VertexDepth;\n"
"void main()\n"
"{\n"
" RenderTarget0 = texture(Skybox, vTexCoord);\n"
" gl_FragDepth = VertexDepth;\n"
"}\n";
const char* vertexShaderSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"out vec3 vTexCoord;\n"
"uniform mat4 WorldViewProjMatrix;\n"
"void main()\n"
"{\n"
" vec4 WVPVertex = WorldViewProjMatrix * vec4(VertexPosition, 1.0);\n"
" gl_Position = WVPVertex.xyww;\n"
" vTexCoord = VertexPosition;\n"
"}\n";
try
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
// Index buffer
IndexBufferRef indexBuffer = IndexBuffer::New(false, 36, DataStorage_Hardware, 0);
indexBuffer->Fill(indices, 0, 36);
// Vertex buffer
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ), 8, DataStorage_Hardware, 0);
vertexBuffer->Fill(vertices, 0, 8);
// Shader
ShaderRef shader = Shader::New();
shader->Create();
shader->AttachStageFromSource(ShaderStageType_Fragment, fragmentShaderSource);
shader->AttachStageFromSource(ShaderStageType_Vertex, vertexShaderSource);
shader->Link();
shader->SendInteger(shader->GetUniformLocation("Skybox"), 0);
shader->SendFloat(shader->GetUniformLocation("VertexDepth"), 1.f);
// Renderstates
s_renderStates.depthFunc = RendererComparison_Equal;
s_renderStates.cullingSide = FaceSide_Front;
s_renderStates.depthBuffer = true;
s_renderStates.depthWrite = false;
s_renderStates.faceCulling = true;
// Exception-free zone
s_indexBuffer = std::move(indexBuffer);
s_shader = std::move(shader);
s_vertexBuffer = std::move(vertexBuffer);
}
catch (const std::exception& e)
{
NazaraError("Failed to initialise: " + String(e.what()));
return false;
}
return true;
}
/*!
* \brief Uninitializes the skybox
*/
void SkyboxBackground::Uninitialize()
{
s_indexBuffer.Reset();
s_shader.Reset();
s_vertexBuffer.Reset();
}
}

View File

@@ -1,225 +0,0 @@
// Copyright (C) 2020 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/Sprite.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::Sprite
* \brief Graphics class that represents the rendering of a sprite
*/
/*!
* \brief Adds the sprite to the rendering queue
*
* \param renderQueue Queue to be added
* \param instanceData Data for the instance
*/
void Sprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const
{
const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<const VertexStruct_XYZ_Color_UV*>(instanceData.data.data());
renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(), vertices, 1, scissorRect);
}
/*!
* \brief Clones this sprite
*/
std::unique_ptr<InstancedRenderable> Sprite::Clone() const
{
return std::make_unique<Sprite>(*this);
}
/*!
* \brief Makes the bounding volume of this text
*/
void Sprite::MakeBoundingVolume() const
{
Vector3f origin(m_origin.x, -m_origin.y, m_origin.z);
m_boundingVolume.Set(-origin, m_size.x*Vector3f::Right() + m_size.y*Vector3f::Down() - origin);
}
/*!
* \brief Sets the material of the sprite from a name
*
* Tries to get a material from the MaterialLibrary and then the MaterialManager (which will treat the name as a path)
* Fails if the texture name is not a part of the MaterialLibrary nor the MaterialManager (which fails if it couldn't load the texture from its filepath)
*
* \param materialName Named texture for the material
* \param resizeSprite Should the sprite be resized to the material diffuse map size?
*
* \return True if the material was found or loaded from its name/path, false if it couldn't
*/
bool Sprite::SetMaterial(std::string materialName, bool resizeSprite)
{
MaterialRef material = MaterialLibrary::Query(materialName);
if (!material)
{
material = MaterialManager::Get(materialName);
if (!material)
{
NazaraError("Failed to get material \"" + materialName + "\"");
return false;
}
}
SetMaterial(std::move(material), resizeSprite);
return true;
}
/*!
* \brief Sets the material of the sprite from a name for a specific skin
*
* Tries to get a material from the MaterialLibrary and then the MaterialManager (which will treat the name as a path)
* Fails if the texture name is not a part of the MaterialLibrary nor the MaterialManager (which fails if it couldn't load the texture from its filepath)
*
* \param skinIndex Skin index to change
* \param materialName Named texture for the material
* \param resizeSprite Should the sprite be resized to the material diffuse map size?
*
* \return True if the material was found or loaded from its name/path, false if it couldn't
*/
bool Sprite::SetMaterial(std::size_t skinIndex, std::string materialName, bool resizeSprite)
{
MaterialRef material = MaterialLibrary::Query(materialName);
if (!material)
{
material = MaterialManager::Get(materialName);
if (!material)
{
NazaraError("Failed to get material \"" + materialName + "\"");
return false;
}
}
SetMaterial(skinIndex, std::move(material), resizeSprite);
return true;
}
/*!
* \brief Sets the texture of the sprite from a name for the current skin
* \return True if the texture was found or loaded from its name/path, false if it couldn't
*
* Tries to get a texture from the TextureLibrary and then the TextureManager (which will treat the name as a path)
* Fails if the texture name is not a part of the TextureLibrary nor the TextureManager (which fails if it couldn't load the texture from its filepath)
*
* \param textureName Named texture for the sprite
* \param resizeSprite Should the sprite be resized to the texture size?
*
* \remark The sprite material gets copied to prevent accidentally changing other drawable materials
*/
bool Sprite::SetTexture(std::string textureName, bool resizeSprite)
{
TextureRef texture = TextureLibrary::Query(textureName);
if (!texture)
{
texture = TextureManager::Get(textureName);
if (!texture)
{
NazaraError("Failed to get texture \"" + textureName + "\"");
return false;
}
}
SetTexture(std::move(texture), resizeSprite);
return true;
}
/*!
* \brief Sets the texture of the sprite from a name for a specific skin
* \return True if the texture was found or loaded from its name/path, false if it couldn't
*
* Tries to get a texture from the TextureLibrary and then the TextureManager (which will treat the name as a path)
* Fails if the texture name is not a part of the TextureLibrary nor the TextureManager (which fails if it couldn't load the texture from its filepath)
*
* \param skinIndex Named texture for the sprite
* \param textureName Named texture for the sprite
* \param resizeSprite Should the sprite be resized to the texture size?
*
* \remark The sprite material gets copied to prevent accidentally changing other drawable materials
*/
bool Sprite::SetTexture(std::size_t skinIndex, std::string textureName, bool resizeSprite)
{
TextureRef texture = TextureLibrary::Query(textureName);
if (!texture)
{
texture = TextureManager::Get(textureName);
if (!texture)
{
NazaraError("Failed to get texture \"" + textureName + "\"");
return false;
}
}
SetTexture(skinIndex, std::move(texture), resizeSprite);
return true;
}
/*!
* \brief Updates the data of the sprite
*
* \param instanceData Data of the instance
*/
void Sprite::UpdateData(InstanceData* instanceData) const
{
instanceData->data.resize(4 * sizeof(VertexStruct_XYZ_Color_UV));
VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<VertexStruct_XYZ_Color_UV*>(instanceData->data.data());
SparsePtr<Color> colorPtr(&vertices[0].color, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector3f> posPtr(&vertices[0].position, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector2f> texCoordPtr(&vertices[0].uv, sizeof(VertexStruct_XYZ_Color_UV));
Vector3f origin(m_origin.x, -m_origin.y, m_origin.z);
*colorPtr++ = m_color * m_cornerColor[RectCorner_LeftTop];
*posPtr++ = instanceData->transformMatrix.Transform(Vector3f(-origin));
*texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_LeftTop);
*colorPtr++ = m_color * m_cornerColor[RectCorner_RightTop];
*posPtr++ = instanceData->transformMatrix.Transform(m_size.x*Vector3f::Right() - origin);
*texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_RightTop);
*colorPtr++ = m_color * m_cornerColor[RectCorner_LeftBottom];
*posPtr++ = instanceData->transformMatrix.Transform(m_size.y*Vector3f::Down() - origin);
*texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_LeftBottom);
*colorPtr++ = m_color * m_cornerColor[RectCorner_RightBottom];
*posPtr++ = instanceData->transformMatrix.Transform(m_size.x*Vector3f::Right() + m_size.y*Vector3f::Down() - origin);
*texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_RightBottom);
}
/*!
* \brief Initializes the sprite library
* \return true If successful
*
* \remark Produces a NazaraError if the sprite library failed to be initialized
*/
bool Sprite::Initialize()
{
if (!SpriteLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
return true;
}
/*!
* \brief Uninitializes the sprite library
*/
void Sprite::Uninitialize()
{
SpriteLibrary::Uninitialize();
}
SpriteLibrary::LibraryMap Sprite::s_library;
}

View File

@@ -1,343 +0,0 @@
// Copyright (C) 2020 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/TextSprite.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/SparsePtr.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Utility/AbstractTextDrawer.hpp>
#include <Nazara/Utility/Font.hpp>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::TextSprite
* \brief Graphics class that represents the rendering of a sprite containing text
*/
/*!
* \brief Adds the text to the rendering queue
*
* \param renderQueue Queue to be added
* \param instanceData Data for the instance
*/
void TextSprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const
{
for (auto& pair : m_renderInfos)
{
const RenderKey& key = pair.first;
RenderIndices& indices = pair.second;
if (indices.count > 0)
{
const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<const VertexStruct_XYZ_Color_UV*>(instanceData.data.data());
renderQueue->AddSprites(instanceData.renderOrder + key.renderOrder, GetMaterial(), &vertices[indices.first * 4], indices.count, scissorRect, key.texture);
}
}
}
/*!
* \brief Clones this text sprite
*/
std::unique_ptr<InstancedRenderable> TextSprite::Clone() const
{
return std::make_unique<TextSprite>(*this);
}
/*!
* \brief Updates the text
*
* \param drawer Drawer used to compose the text
*
* \remark Produces a NazaraAssert if atlas does not use a hardware storage
*/
void TextSprite::Update(const AbstractTextDrawer& drawer)
{
CallOnExit clearOnFail([this]()
{
Clear();
});
// Mark every atlas as unused...
for (auto& pair : m_atlases)
pair.second.used = false;
// ... until they are marked as used by the drawer
std::size_t fontCount = drawer.GetFontCount();
for (std::size_t i = 0; i < fontCount; ++i)
{
Font* font = drawer.GetFont(i);
const AbstractAtlas* atlas = font->GetAtlas().get();
NazaraAssert(atlas->GetStorage() == DataStorage_Hardware, "Font uses a non-hardware atlas which cannot be used by text sprites");
auto it = m_atlases.find(atlas);
if (it == m_atlases.end())
{
it = m_atlases.insert(std::make_pair(atlas, AtlasSlots())).first;
AtlasSlots& atlasSlots = it->second;
atlasSlots.clearSlot.Connect(atlas->OnAtlasCleared, this, &TextSprite::OnAtlasInvalidated);
atlasSlots.layerChangeSlot.Connect(atlas->OnAtlasLayerChange, this, &TextSprite::OnAtlasLayerChange);
atlasSlots.releaseSlot.Connect(atlas->OnAtlasRelease, this, &TextSprite::OnAtlasInvalidated);
}
it->second.used = true;
}
// Remove unused atlas slots
auto atlasIt = m_atlases.begin();
while (atlasIt != m_atlases.end())
{
if (!atlasIt->second.used)
m_atlases.erase(atlasIt++);
else
++atlasIt;
}
std::size_t glyphCount = drawer.GetGlyphCount();
// Reset glyph count for every texture to zero
for (auto& pair : m_renderInfos)
pair.second.count = 0;
// Count glyph count for each texture
RenderKey lastRenderKey { nullptr, 0 };
unsigned int* count = nullptr;
std::size_t visibleGlyphCount = 0;
for (std::size_t i = 0; i < glyphCount; ++i)
{
const AbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i);
if (!glyph.atlas)
continue;
Texture* texture = static_cast<Texture*>(glyph.atlas);
RenderKey renderKey{ texture, glyph.renderOrder };
if (lastRenderKey != renderKey)
{
auto it = m_renderInfos.find(renderKey);
if (it == m_renderInfos.end())
it = m_renderInfos.insert(std::make_pair(renderKey, RenderIndices{0U, 0U})).first;
count = &it->second.count;
lastRenderKey = renderKey;
}
(*count)++;
visibleGlyphCount++;
}
m_localVertices.resize(visibleGlyphCount * 4);
// Attributes indices and reinitialize glyph count to zero to use it as a counter in the next loop
// This is because the 1st glyph can use texture A, the 2nd glyph can use texture B and the 3th glyph C can use texture A again
// so we need a counter to know where to write informations
// also remove unused render infos
unsigned int index = 0;
auto infoIt = m_renderInfos.begin();
while (infoIt != m_renderInfos.end())
{
RenderIndices& indices = infoIt->second;
if (indices.count == 0)
infoIt = m_renderInfos.erase(infoIt); //< No glyph uses this texture, remove from indices
else
{
indices.first = index;
index += indices.count;
indices.count = 0;
++infoIt;
}
}
lastRenderKey = { nullptr, 0 };
RenderIndices* indices = nullptr;
for (unsigned int i = 0; i < glyphCount; ++i)
{
const AbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i);
if (!glyph.atlas)
continue;
Texture* texture = static_cast<Texture*>(glyph.atlas);
RenderKey renderKey{ texture, glyph.renderOrder };
if (lastRenderKey != renderKey)
{
indices = &m_renderInfos[renderKey]; //< We changed texture, adjust the pointer
lastRenderKey = renderKey;
}
// First, compute the uv coordinates from our atlas rect
Vector2ui size(texture->GetSize());
float invWidth = 1.f / size.x;
float invHeight = 1.f / size.y;
Rectf uvRect(glyph.atlasRect);
uvRect.x *= invWidth;
uvRect.y *= invHeight;
uvRect.width *= invWidth;
uvRect.height *= invHeight;
// Our glyph may be flipped in the atlas, to render it correctly we need to change the uv coordinates accordingly
const RectCorner normalCorners[4] = {RectCorner_LeftTop, RectCorner_RightTop, RectCorner_LeftBottom, RectCorner_RightBottom};
const RectCorner flippedCorners[4] = {RectCorner_LeftBottom, RectCorner_LeftTop, RectCorner_RightBottom, RectCorner_RightTop};
// Set the position, color and UV of our vertices
for (unsigned int j = 0; j < 4; ++j)
{
// Remember that indices->count is a counter here, not a count value
std::size_t offset = (indices->first + indices->count) * 4 + j;
m_localVertices[offset].color = glyph.color;
m_localVertices[offset].position.Set(glyph.corners[j]);
m_localVertices[offset].uv.Set(uvRect.GetCorner((glyph.flipped) ? flippedCorners[j] : normalCorners[j]));
}
// Increment the counter, go to next glyph
indices->count++;
}
m_localBounds = drawer.GetBounds();
InvalidateBoundingVolume();
InvalidateInstanceData(0);
clearOnFail.Reset();
}
/*
* \brief Makes the bounding volume of this text
*/
void TextSprite::MakeBoundingVolume() const
{
Vector2f max = m_scale * m_localBounds.GetMaximum();
Vector2f min = m_scale * m_localBounds.GetMinimum();
m_boundingVolume.Set(min.x * Vector3f::Right() + min.y * Vector3f::Down(), max.x * Vector3f::Right() + max.y * Vector3f::Down());
}
/*!
* \brief Handle the invalidation of an atlas
*
* \param atlas Atlas being invalidated
*/
void TextSprite::OnAtlasInvalidated(const AbstractAtlas* atlas)
{
#ifdef NAZARA_DEBUG
if (m_atlases.find(atlas) == m_atlases.end())
{
NazaraInternalError("Not listening to " + String::Pointer(atlas));
return;
}
#endif
NazaraWarning("TextSprite " + String::Pointer(this) + " has been cleared because atlas " + String::Pointer(atlas) + " has been invalidated (cleared or released)");
Clear();
}
/*!
* \brief Handle the size change of an atlas layer
*
* \param atlas Atlas being invalidated
* \param oldLayer Pointer to the previous layer
* \param newLayer Pointer to the new layer
*/
void TextSprite::OnAtlasLayerChange(const AbstractAtlas* atlas, AbstractImage* oldLayer, AbstractImage* newLayer)
{
NazaraUnused(atlas);
#ifdef NAZARA_DEBUG
if (m_atlases.find(atlas) == m_atlases.end())
{
NazaraInternalError("Not listening to " + String::Pointer(atlas));
return;
}
#endif
if (!oldLayer)
return;
assert(newLayer);
// The texture of an atlas have just been recreated (size change)
// we have to adjust the coordinates of the texture and the rendering texture
Texture* oldTexture = static_cast<Texture*>(oldLayer);
Texture* newTexture = static_cast<Texture*>(newLayer);
Vector2ui oldSize(oldTexture->GetSize());
Vector2ui newSize(newTexture->GetSize());
Vector2f scale = Vector2f(oldSize) / Vector2f(newSize); // ratio of the old one to the new one
// It is possible we actually use that texture multiple times, check them all
for (auto it = m_renderInfos.begin(); it != m_renderInfos.end(); ++it)
{
const RenderKey& renderKey = it->first;
const RenderIndices& indices = it->second;
// Adjust texture coordinates by size ratio
SparsePtr<Vector2f> texCoordPtr(&m_localVertices[indices.first].uv, sizeof(VertexStruct_XY_Color_UV));
for (unsigned int i = 0; i < indices.count; ++i)
{
for (unsigned int j = 0; j < 4; ++j)
m_localVertices[i * 4 + j].uv *= scale;
}
// Erase and re-insert with the new texture handle
m_renderInfos.erase(it);
m_renderInfos.insert(std::make_pair(RenderKey{ newTexture, renderKey.renderOrder }, indices));
it = m_renderInfos.begin(); //< std::unordered_map::insert may invalidate all iterators, start from the beginning...
}
}
/*!
* \brief Updates the data of the sprite
*
* \param instanceData Data of the instance
*/
void TextSprite::UpdateData(InstanceData* instanceData) const
{
instanceData->data.resize(m_localVertices.size() * sizeof(VertexStruct_XYZ_Color_UV));
VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<VertexStruct_XYZ_Color_UV*>(instanceData->data.data());
SparsePtr<Color> colorPtr(&vertices[0].color, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector3f> posPtr(&vertices[0].position, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector2f> texCoordPtr(&vertices[0].uv, sizeof(VertexStruct_XYZ_Color_UV));
// We will not initialize the final vertices (those send to the RenderQueue)
// With the help of the coordinates axis, the matrix and our color attribute
for (auto& pair : m_renderInfos)
{
RenderIndices& indices = pair.second;
if (indices.count == 0)
continue; //< Ignore empty render indices
SparsePtr<Color> color = colorPtr + indices.first * 4;
SparsePtr<Vector3f> pos = posPtr + indices.first * 4;
SparsePtr<Vector2f> uv = texCoordPtr + indices.first * 4;
VertexStruct_XY_Color_UV* localVertex = &m_localVertices[indices.first * 4];
for (unsigned int i = 0; i < indices.count; ++i)
{
for (unsigned int j = 0; j < 4; ++j)
{
Vector3f localPos = localVertex->position.x*Vector3f::Right() + localVertex->position.y*Vector3f::Down();
localPos *= m_scale;
*pos++ = instanceData->transformMatrix.Transform(localPos);
*color++ = m_color * localVertex->color;
*uv++ = localVertex->uv;
localVertex++;
}
}
}
}
TextSpriteLibrary::LibraryMap TextSprite::s_library;
}

View File

@@ -1,98 +0,0 @@
// Copyright (C) 2020 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/TextureBackground.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderStates.hpp>
#include <Nazara/Renderer/UberShaderInstance.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
/*!
* \brief Defines render states
* \return RenderStates for the color background
*/
RenderStates BuildRenderStates()
{
RenderStates states;
states.depthFunc = RendererComparison_Equal;
states.cullingSide = FaceSide_Back;
states.depthBuffer = true;
states.depthWrite = false;
states.faceCulling = true;
return states;
}
}
/*!
* \ingroup graphics
* \class Nz::TextureBackground
* \brief Graphics class that represents a background with a texture
*/
/*!
* \brief Constructs a TextureBackground object with a texture
*
* \param texture Texture
*/
TextureBackground::TextureBackground(TextureRef texture)
{
m_uberShader = UberShaderLibrary::Get("Basic");
ParameterList list;
list.SetParameter("DIFFUSE_MAPPING", true);
list.SetParameter("TEXTURE_MAPPING", true);
list.SetParameter("UNIFORM_VERTEX_DEPTH", true);
m_uberShaderInstance = m_uberShader->Get(list);
const Shader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_materialDiffuseMapUniform = shader->GetUniformLocation("MaterialDiffuseMap");
m_vertexDepthUniform = shader->GetUniformLocation("VertexDepth");
SetTexture(std::move(texture));
}
/*!
* \brief Draws this relatively to the viewer
*
* \param viewer Viewer for the background
*/
void TextureBackground::Draw(const AbstractViewer* viewer) const
{
NazaraUnused(viewer);
static RenderStates states(BuildRenderStates());
Renderer::SetRenderStates(states);
Renderer::SetTexture(0, m_texture);
m_uberShaderInstance->Activate();
const Shader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, Color::White);
shader->SendFloat(m_vertexDepthUniform, 1.f);
shader->SendInteger(m_materialDiffuseMapUniform, 0);
Renderer::DrawFullscreenQuad();
}
/*!
* \brief Gets the background type
* \return Type of background
*/
BackgroundType TextureBackground::GetBackgroundType() const
{
return BackgroundType_Texture;
}
}

View File

@@ -1,135 +0,0 @@
// Copyright (C) 2020 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/TileMap.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Math/Rect.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
/*!
* \ingroup graphics
* \class Nz::TileMap
* \brief Graphics class that represent several tiles of the same size assembled into a grid
* This class is far more efficient than using a sprite for every tile
*/
/*!
* \brief Adds the TileMap to the rendering queue
*
* \param renderQueue Queue to be added
* \param instanceData Data for the instance
*/
void TileMap::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const
{
const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<const VertexStruct_XYZ_Color_UV*>(instanceData.data.data());
std::size_t spriteCount = 0;
for (std::size_t layerIndex = 0; layerIndex < m_layers.size(); ++layerIndex)
{
const auto& layer = m_layers[layerIndex];
if (layer.tiles.empty())
continue;
renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(layerIndex), &vertices[4 * spriteCount], layer.tiles.size(), scissorRect);
spriteCount += layer.tiles.size();
}
}
/*!
* \brief Clones this tilemap
*/
std::unique_ptr<InstancedRenderable> TileMap::Clone() const
{
return std::make_unique<TileMap>(*this);
}
void TileMap::MakeBoundingVolume() const
{
Nz::Vector2f size = GetSize();
m_boundingVolume.Set(Vector3f(0.f), size.x*Vector3f::Right() + size.y*Vector3f::Down());
}
void TileMap::UpdateData(InstanceData* instanceData) const
{
std::size_t spriteCount = 0;
for (const Layer& layer : m_layers)
spriteCount += layer.tiles.size();
instanceData->data.resize(4 * spriteCount * sizeof(VertexStruct_XYZ_Color_UV));
VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<VertexStruct_XYZ_Color_UV*>(instanceData->data.data());
spriteCount = 0;
for (const Layer& layer : m_layers)
{
SparsePtr<Color> colorPtr(&vertices[4 * spriteCount].color, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector3f> posPtr(&vertices[4 * spriteCount].position, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector2f> texCoordPtr(&vertices[4 * spriteCount].uv, sizeof(VertexStruct_XYZ_Color_UV));
for (std::size_t tileIndex : layer.tiles)
{
const Tile& tile = m_tiles[tileIndex];
NazaraAssert(tile.enabled, "Tile specified for rendering is not enabled");
std::size_t x = tileIndex % m_mapSize.x;
std::size_t y = tileIndex / m_mapSize.x;
Vector3f tileLeftCorner;
if (m_isometricModeEnabled)
tileLeftCorner.Set(x * m_tileSize.x + m_tileSize.x/2.f * (y % 2), y/2.f * -m_tileSize.y, 0.f);
else
tileLeftCorner.Set(x * m_tileSize.x, y * -m_tileSize.y, 0.f);
*colorPtr++ = tile.color;
*posPtr++ = instanceData->transformMatrix.Transform(tileLeftCorner);
*texCoordPtr++ = tile.textureCoords.GetCorner(RectCorner_LeftTop);
*colorPtr++ = tile.color;
*posPtr++ = instanceData->transformMatrix.Transform(tileLeftCorner + m_tileSize.x * Vector3f::Right());
*texCoordPtr++ = tile.textureCoords.GetCorner(RectCorner_RightTop);
*colorPtr++ = tile.color;
*posPtr++ = instanceData->transformMatrix.Transform(tileLeftCorner + m_tileSize.y * Vector3f::Down());
*texCoordPtr++ = tile.textureCoords.GetCorner(RectCorner_LeftBottom);
*colorPtr++ = tile.color;
*posPtr++ = instanceData->transformMatrix.Transform(tileLeftCorner + m_tileSize.x * Vector3f::Right() + m_tileSize.y * Vector3f::Down());
*texCoordPtr++ = tile.textureCoords.GetCorner(RectCorner_RightBottom);
}
spriteCount += layer.tiles.size();
}
}
/*!
* \brief Initializes the tilemap library
* \return true If successful
*
* \remark Produces a NazaraError if the tilemap library failed to be initialized
*/
bool TileMap::Initialize()
{
if (!TileMapLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
return true;
}
/*!
* \brief Uninitializes the tilemap library
*/
void TileMap::Uninitialize()
{
TileMapLibrary::Uninitialize();
}
TileMapLibrary::LibraryMap TileMap::s_library;
}

View File

@@ -7,12 +7,8 @@
#include <regex>
#ifndef NDK_SERVER
#include <Nazara/Graphics/ForwardRenderTechnique.hpp>
#include <Nazara/Utility/SimpleTextDrawer.hpp>
#include <NazaraSDK/Components/CameraComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Systems/RenderSystem.hpp>
#endif
namespace Ndk
@@ -63,14 +59,6 @@ namespace Ndk
else
NazaraWarning("Ignored command-line argument #" + Nz::String::Number(i) + " \"" + argument + '"');
}
#ifndef NDK_SERVER
if (HasOption("console"))
EnableConsole(true);
if (HasOption("fpscounter"))
EnableFPSCounter(true);
#endif
}
/*!
@@ -111,119 +99,8 @@ namespace Ndk
for (World& world : m_worlds)
world.Update(m_updateTime);
#ifndef NDK_SERVER
for (WindowInfo& info : m_windows)
{
if (!info.overlayWorld)
continue;
if (info.fpsCounter)
{
FPSCounterOverlay& fpsCounter = *info.fpsCounter;
fpsCounter.frameCount++;
fpsCounter.elapsedTime += m_updateTime;
if (fpsCounter.elapsedTime >= 1.f)
{
fpsCounter.sprite->Update(Nz::SimpleTextDrawer::Draw("FPS: " + Nz::String::Number(fpsCounter.frameCount), 36));
fpsCounter.frameCount = 0;
fpsCounter.elapsedTime = 0.f;
}
}
info.overlayWorld->Update(m_updateTime);
}
#endif
return true;
}
#ifndef NDK_SERVER
void Application::SetupConsole(WindowInfo& info)
{
std::unique_ptr<ConsoleOverlay> overlay = std::make_unique<ConsoleOverlay>();
Nz::Vector2ui windowDimensions;
if (info.window->IsValid())
windowDimensions = info.window->GetSize();
else
windowDimensions.MakeZero();
overlay->console = info.canvas->Add<Console>();
Console& consoleRef = *overlay->console;
consoleRef.Resize({float(windowDimensions.x), windowDimensions.y / 4.f});
consoleRef.Show(false);
// Redirect logs toward the console
overlay->logSlot.Connect(Nz::Log::OnLogWrite, [&consoleRef] (const Nz::String& str)
{
consoleRef.AddLine(str);
});
// Setup a few event callback to handle the console
Nz::EventHandler& eventHandler = info.window->GetEventHandler();
overlay->keyPressedSlot.Connect(eventHandler.OnKeyPressed, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent::KeyEvent& event)
{
if (event.virtualKey == Nz::Keyboard::VKey::F9)
{
// Toggle console visibility and focus
if (consoleRef.IsVisible())
{
consoleRef.ClearFocus();
consoleRef.Show(false);
}
else
{
consoleRef.Show(true);
consoleRef.SetFocus();
}
}
});
overlay->resizedSlot.Connect(info.renderTarget->OnRenderTargetSizeChange, [&consoleRef] (const Nz::RenderTarget* renderTarget)
{
Nz::Vector2ui size = renderTarget->GetSize();
consoleRef.Resize({float(size.x), size.y / 4.f});
});
info.console = std::move(overlay);
}
void Application::SetupFPSCounter(WindowInfo& info)
{
std::unique_ptr<FPSCounterOverlay> fpsCounter = std::make_unique<FPSCounterOverlay>();
fpsCounter->sprite = Nz::TextSprite::New();
fpsCounter->entity = info.overlayWorld->CreateEntity();
fpsCounter->entity->AddComponent<NodeComponent>();
fpsCounter->entity->AddComponent<GraphicsComponent>().Attach(fpsCounter->sprite);
info.fpsCounter = std::move(fpsCounter);
}
void Application::SetupOverlay(WindowInfo& info)
{
info.overlayWorld = std::make_unique<World>(false); //< No default system
if (info.window->IsValid())
info.canvas = std::make_unique<Canvas>(info.overlayWorld->CreateHandle(), info.window->GetEventHandler(), info.window->GetCursorController().CreateHandle());
RenderSystem& renderSystem = info.overlayWorld->AddSystem<RenderSystem>();
renderSystem.ChangeRenderTechnique<Nz::ForwardRenderTechnique>();
renderSystem.SetDefaultBackground(nullptr);
renderSystem.SetGlobalUp(Nz::Vector3f::Down());
EntityHandle viewer = info.overlayWorld->CreateEntity();
CameraComponent& camComponent = viewer->AddComponent<CameraComponent>();
viewer->AddComponent<NodeComponent>();
camComponent.SetProjectionType(Nz::ProjectionType_Orthogonal);
camComponent.SetTarget(info.renderTarget);
}
#endif
Application* Application::s_application = nullptr;
}

View File

@@ -1,381 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/BaseWidget.hpp>
#include <NazaraSDK/Canvas.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/World.hpp>
#include <algorithm>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::BaseWidget
* \brief Abstract class serving as a base class for all widgets
*/
/*!
* \brief Constructs a BaseWidget object using another widget as its parent
*
* \param parent Parent widget, must be valid and attached to a canvas
*
* Constructs a BaseWidget object using another widget as a base.
* This will also register the widget to the canvas owning the top-most widget.
*/
BaseWidget::BaseWidget(BaseWidget* parent) :
BaseWidget()
{
NazaraAssert(parent, "Invalid parent");
NazaraAssert(parent->GetCanvas(), "Parent has no canvas");
m_canvas = parent->GetCanvas();
m_widgetParent = parent;
m_world = m_canvas->GetWorld();
RegisterToCanvas();
}
/*!
* \brief Frees the widget, unregistering it from its canvas
*/
BaseWidget::~BaseWidget()
{
UnregisterFromCanvas();
}
/*!
* \brief Clears keyboard focus if and only if this widget owns it.
*/
void BaseWidget::ClearFocus()
{
if (IsRegisteredToCanvas())
m_canvas->ClearKeyboardOwner(m_canvasIndex);
}
/*!
* \brief Destroy the widget, deleting it in the process.
*
* Calling this function immediately destroys the widget, freeing its memory.
*/
void BaseWidget::Destroy()
{
NazaraAssert(this != m_canvas, "Canvas cannot be destroyed by calling Destroy()");
m_widgetParent->DestroyChild(this); //< This does delete us
}
/*!
* \brief Enable or disables the widget background.
*/
void BaseWidget::EnableBackground(bool enable)
{
if (m_backgroundEntity.IsValid() == enable)
return;
if (enable)
{
m_backgroundSprite = Nz::Sprite::New();
m_backgroundSprite->SetColor(m_backgroundColor);
m_backgroundSprite->SetMaterial(Nz::Material::New((m_backgroundColor.IsOpaque()) ? "Basic2D" : "Translucent2D")); //< TODO: Use a shared material instead of creating one everytime
m_backgroundEntity = CreateEntity();
m_backgroundEntity->AddComponent<GraphicsComponent>().Attach(m_backgroundSprite, -1);
m_backgroundEntity->AddComponent<NodeComponent>().SetParent(this);
BaseWidget::Layout(); // Only layout background
}
else
{
DestroyEntity(m_backgroundEntity);
m_backgroundEntity.Reset();
m_backgroundSprite.Reset();
}
}
/*!
* \brief Checks if this widget has keyboard focus
* \return true if widget has keyboard focus, false otherwise
*/
bool BaseWidget::HasFocus() const
{
if (!IsRegisteredToCanvas())
return false;
return m_canvas->IsKeyboardOwner(m_canvasIndex);
}
void BaseWidget::Resize(const Nz::Vector2f& size)
{
// Adjust new size
Nz::Vector2f newSize = size;
newSize.Maximize(m_minimumSize);
newSize.Minimize(m_maximumSize);
NotifyParentResized(newSize);
m_size = newSize;
Layout();
}
void BaseWidget::SetBackgroundColor(const Nz::Color& color)
{
m_backgroundColor = color;
if (m_backgroundSprite)
{
m_backgroundSprite->SetColor(color);
m_backgroundSprite->GetMaterial()->Configure((color.IsOpaque()) ? "Basic2D" : "Translucent2D"); //< Our sprite has its own material (see EnableBackground)
}
}
void BaseWidget::SetCursor(Nz::SystemCursor systemCursor)
{
m_cursor = systemCursor;
if (IsRegisteredToCanvas())
m_canvas->NotifyWidgetCursorUpdate(m_canvasIndex);
}
void BaseWidget::SetFocus()
{
if (IsRegisteredToCanvas())
m_canvas->SetKeyboardOwner(m_canvasIndex);
}
void BaseWidget::SetParent(BaseWidget* widget)
{
Canvas* oldCanvas = m_canvas;
Canvas* newCanvas = widget->GetCanvas();
// Changing a widget canvas is a problem because of the canvas entities
NazaraAssert(oldCanvas == newCanvas, "Transferring a widget between canvas is not yet supported");
Node::SetParent(widget);
m_widgetParent = widget;
Layout();
}
void BaseWidget::SetRenderingRect(const Nz::Rectf& renderingRect)
{
m_renderingRect = renderingRect;
UpdatePositionAndSize();
for (const auto& widgetPtr : m_children)
widgetPtr->UpdatePositionAndSize();
}
void BaseWidget::Show(bool show)
{
if (m_visible != show)
{
m_visible = show;
if (m_visible)
RegisterToCanvas();
else
UnregisterFromCanvas();
for (WidgetEntity& entity : m_entities)
{
if (entity.isEnabled)
{
entity.handle->Enable(show); //< This will override isEnabled
entity.isEnabled = true;
}
}
ShowChildren(show);
}
}
const EntityHandle& BaseWidget::CreateEntity()
{
const EntityHandle& newEntity = m_world->CreateEntity();
newEntity->Enable(m_visible);
m_entities.emplace_back();
WidgetEntity& newWidgetEntity = m_entities.back();
newWidgetEntity.handle = newEntity;
newWidgetEntity.onDisabledSlot.Connect(newEntity->OnEntityDisabled, [this](Entity* entity)
{
auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");
it->isEnabled = false;
});
newWidgetEntity.onEnabledSlot.Connect(newEntity->OnEntityEnabled, [this](Entity* entity)
{
auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");
if (!IsVisible())
entity->Disable(); // Next line will override isEnabled status
it->isEnabled = true;
});
return newEntity;
}
void BaseWidget::DestroyEntity(Entity* entity)
{
auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");
m_entities.erase(it);
}
void BaseWidget::Layout()
{
if (m_backgroundSprite)
m_backgroundSprite->SetSize(m_size.x, m_size.y);
UpdatePositionAndSize();
}
void BaseWidget::InvalidateNode()
{
Node::InvalidateNode();
UpdatePositionAndSize();
}
Nz::Rectf BaseWidget::GetScissorRect() const
{
Nz::Vector2f widgetPos = Nz::Vector2f(GetPosition(Nz::CoordSys_Global));
Nz::Vector2f widgetSize = GetSize();
Nz::Rectf widgetRect(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y);
Nz::Rectf widgetRenderingRect(widgetPos.x + m_renderingRect.x, widgetPos.y + m_renderingRect.y, m_renderingRect.width, m_renderingRect.height);
widgetRect.Intersect(widgetRenderingRect, &widgetRect);
return widgetRect;
}
bool BaseWidget::IsFocusable() const
{
return false;
}
void BaseWidget::OnFocusLost()
{
}
void BaseWidget::OnFocusReceived()
{
}
bool BaseWidget::OnKeyPressed(const Nz::WindowEvent::KeyEvent& key)
{
return false;
}
void BaseWidget::OnKeyReleased(const Nz::WindowEvent::KeyEvent& /*key*/)
{
}
void BaseWidget::OnMouseEnter()
{
}
void BaseWidget::OnMouseMoved(int /*x*/, int /*y*/, int /*deltaX*/, int /*deltaY*/)
{
}
void BaseWidget::OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button /*button*/)
{
}
void BaseWidget::OnMouseButtonRelease(int /*x*/, int /*y*/, Nz::Mouse::Button /*button*/)
{
}
void BaseWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float /*delta*/)
{
}
void BaseWidget::OnMouseExit()
{
}
void BaseWidget::OnParentResized(const Nz::Vector2f& /*newSize*/)
{
}
void BaseWidget::OnTextEntered(char32_t /*character*/, bool /*repeated*/)
{
}
void BaseWidget::OnTextEdited(const std::array<char, 32>& /*characters*/, int /*length*/)
{
}
void BaseWidget::ShowChildren(bool show)
{
for (const auto& widgetPtr : m_children)
widgetPtr->Show(show);
}
void BaseWidget::DestroyChild(BaseWidget* widget)
{
auto it = std::find_if(m_children.begin(), m_children.end(), [widget] (const std::unique_ptr<BaseWidget>& widgetPtr) -> bool
{
return widgetPtr.get() == widget;
});
NazaraAssert(it != m_children.end(), "Child widget not found in parent");
m_children.erase(it);
}
void BaseWidget::DestroyChildren()
{
m_children.clear();
}
void BaseWidget::RegisterToCanvas()
{
NazaraAssert(!IsRegisteredToCanvas(), "Widget is already registered to canvas");
m_canvasIndex = m_canvas->RegisterWidget(this);
}
void BaseWidget::UnregisterFromCanvas()
{
if (IsRegisteredToCanvas())
{
m_canvas->UnregisterWidget(m_canvasIndex);
m_canvasIndex = InvalidCanvasIndex;
}
}
void BaseWidget::UpdatePositionAndSize()
{
if (IsRegisteredToCanvas())
m_canvas->NotifyWidgetBoxUpdate(m_canvasIndex);
Nz::Rectf scissorRect = GetScissorRect();
if (m_widgetParent)
{
Nz::Rectf parentScissorRect = m_widgetParent->GetScissorRect();
if (!scissorRect.Intersect(parentScissorRect, &scissorRect))
scissorRect = parentScissorRect;
}
Nz::Recti fullBounds(scissorRect);
for (WidgetEntity& widgetEntity : m_entities)
{
const Ndk::EntityHandle& entity = widgetEntity.handle;
if (entity->HasComponent<GraphicsComponent>())
entity->GetComponent<GraphicsComponent>().SetScissorRect(fullBounds);
}
}
}

View File

@@ -1,230 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Canvas.hpp>
#include <limits>
namespace Ndk
{
std::size_t Canvas::RegisterWidget(BaseWidget* widget)
{
WidgetEntry box;
box.cursor = widget->GetCursor();
box.widget = widget;
std::size_t index = m_widgetEntries.size();
m_widgetEntries.emplace_back(box);
NotifyWidgetBoxUpdate(index);
return index;
}
void Canvas::UnregisterWidget(std::size_t index)
{
WidgetEntry& entry = m_widgetEntries[index];
if (m_hoveredWidget == index)
m_hoveredWidget = InvalidCanvasIndex;
if (m_keyboardOwner == index)
m_keyboardOwner = InvalidCanvasIndex;
if (m_widgetEntries.size() > 1U)
{
WidgetEntry& lastEntry = m_widgetEntries.back();
std::size_t lastEntryIndex = m_widgetEntries.size() - 1;
entry = std::move(lastEntry);
entry.widget->UpdateCanvasIndex(index);
if (m_hoveredWidget == lastEntryIndex)
m_hoveredWidget = index;
if (m_keyboardOwner == lastEntryIndex)
m_keyboardOwner = index;
}
m_widgetEntries.pop_back();
}
void Canvas::OnEventMouseButtonPressed(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent& event)
{
if (m_hoveredWidget != InvalidCanvasIndex)
{
WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget];
int x = static_cast<int>(std::round(event.x - hoveredWidget.box.x));
int y = static_cast<int>(std::round(event.y - hoveredWidget.box.y));
hoveredWidget.widget->OnMouseButtonPress(x, y, event.button);
}
}
void Canvas::OnEventMouseButtonRelease(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent& event)
{
if (m_hoveredWidget != InvalidCanvasIndex)
{
WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget];
int x = static_cast<int>(std::round(event.x - hoveredWidget.box.x));
int y = static_cast<int>(std::round(event.y - hoveredWidget.box.y));
hoveredWidget.widget->OnMouseButtonRelease(x, y, event.button);
}
}
void Canvas::OnEventMouseMoved(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseMoveEvent& event)
{
std::size_t bestEntry = InvalidCanvasIndex;
float bestEntryArea = std::numeric_limits<float>::infinity();
Nz::Vector3f mousePos(float(event.x), float(event.y), 0.f);
for (std::size_t i = 0; i < m_widgetEntries.size(); ++i)
{
const Nz::Boxf& box = m_widgetEntries[i].box;
if (box.Contains(mousePos))
{
float area = box.width * box.height;
if (area < bestEntryArea)
{
bestEntry = i;
bestEntryArea = area;
}
}
}
if (bestEntry != InvalidCanvasIndex)
{
if (m_hoveredWidget != bestEntry)
{
if (m_hoveredWidget != InvalidCanvasIndex)
{
WidgetEntry& previouslyHovered = m_widgetEntries[m_hoveredWidget];
previouslyHovered.widget->OnMouseExit();
}
m_hoveredWidget = bestEntry;
m_widgetEntries[m_hoveredWidget].widget->OnMouseEnter();
if (m_cursorController)
m_cursorController->UpdateCursor(Nz::Cursor::Get(m_widgetEntries[m_hoveredWidget].cursor));
}
WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget];
int x = static_cast<int>(std::round(event.x - hoveredWidget.box.x));
int y = static_cast<int>(std::round(event.y - hoveredWidget.box.y));
hoveredWidget.widget->OnMouseMoved(x, y, event.deltaX, event.deltaY);
}
else if (m_hoveredWidget != InvalidCanvasIndex)
{
m_widgetEntries[m_hoveredWidget].widget->OnMouseExit();
m_hoveredWidget = InvalidCanvasIndex;
if (m_cursorController)
m_cursorController->UpdateCursor(Nz::Cursor::Get(Nz::SystemCursor_Default));
}
}
void Canvas::OnEventMouseWheelMoved(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseWheelEvent& event)
{
if (m_hoveredWidget != InvalidCanvasIndex)
{
WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget];
int x = static_cast<int>(std::round(event.x - hoveredWidget.box.x));
int y = static_cast<int>(std::round(event.y - hoveredWidget.box.y));
hoveredWidget.widget->OnMouseWheelMoved(x, y, event.delta);
}
}
void Canvas::OnEventMouseLeft(const Nz::EventHandler* /*eventHandler*/)
{
if (m_hoveredWidget != InvalidCanvasIndex)
{
m_widgetEntries[m_hoveredWidget].widget->OnMouseExit();
m_hoveredWidget = InvalidCanvasIndex;
}
}
void Canvas::OnEventKeyPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event)
{
if (m_keyboardOwner != InvalidCanvasIndex)
{
if (m_widgetEntries[m_keyboardOwner].widget->OnKeyPressed(event))
return;
if (event.virtualKey == Nz::Keyboard::VKey::Tab)
{
if (!event.shift)
{
// Forward
for (std::size_t i = m_keyboardOwner + 1; i < m_widgetEntries.size(); ++i)
{
if (m_widgetEntries[i].widget->IsFocusable())
{
SetKeyboardOwner(i);
return;
}
}
for (std::size_t i = 0; i < m_keyboardOwner; ++i)
{
if (m_widgetEntries[i].widget->IsFocusable())
{
SetKeyboardOwner(i);
return;
}
}
}
else
{
// Backward
for (decltype(m_widgetEntries)::reverse_iterator rit{ m_widgetEntries.begin() + m_keyboardOwner }; rit != m_widgetEntries.rend(); ++rit)
{
if (rit->widget->IsFocusable())
{
SetKeyboardOwner(std::distance(m_widgetEntries.begin(), rit.base()) - 1);
return;
}
}
decltype(m_widgetEntries)::reverse_iterator rend { m_widgetEntries.begin() + m_keyboardOwner };
for (auto rit = m_widgetEntries.rbegin(); rit != rend; ++rit)
{
if (rit->widget->IsFocusable())
{
SetKeyboardOwner(std::distance(m_widgetEntries.begin(), rit.base()) - 1);
return;
}
}
}
}
}
OnUnhandledKeyPressed(eventHandler, event);
}
void Canvas::OnEventKeyReleased(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event)
{
if (m_keyboardOwner != InvalidCanvasIndex)
m_widgetEntries[m_keyboardOwner].widget->OnKeyReleased(event);
OnUnhandledKeyReleased(eventHandler, event);
}
void Canvas::OnEventTextEntered(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::TextEvent& event)
{
if (m_keyboardOwner != InvalidCanvasIndex)
m_widgetEntries[m_keyboardOwner].widget->OnTextEntered(event.character, event.repeated);
}
void Canvas::OnEventTextEdited(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::EditEvent& event)
{
if (m_keyboardOwner != InvalidCanvasIndex)
m_widgetEntries[m_keyboardOwner].widget->OnTextEdited(event.text, event.length);
}
}

View File

@@ -1,363 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Components/CameraComponent.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <NazaraSDK/Algorithm.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::CameraComponent
* \brief NDK class that represents the component for camera
*/
/*!
* \brief Applys the view of the camera
*
* \remark Produces a NazaraAssert if the camera has no target
*/
void CameraComponent::ApplyView() const
{
NazaraAssert(m_target, "CameraComponent has no target");
EnsureProjectionMatrixUpdate();
EnsureViewMatrixUpdate();
EnsureViewportUpdate();
Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, m_projectionMatrix);
Nz::Renderer::SetMatrix(Nz::MatrixType_View, m_viewMatrix);
Nz::Renderer::SetTarget(m_target);
Nz::Renderer::SetViewport(m_viewport);
}
/*!
* \brief Gets the aspect ratio of the camera
* \return Aspect ratio of the camera
*/
float CameraComponent::GetAspectRatio() const
{
EnsureViewportUpdate();
return m_aspectRatio;
}
/*!
* \brief Gets the eye position of the camera
*
* \remark Produces a NazaraAssert if entity is invalid or has no NodeComponent
*/
Nz::Vector3f CameraComponent::GetEyePosition() const
{
NazaraAssert(m_entity && m_entity->HasComponent<NodeComponent>(), "CameraComponent requires NodeComponent");
return m_entity->GetComponent<NodeComponent>().GetPosition();
}
/*!
* \brief Gets the forward direction of the camera
*
* \remark Produces a NazaraAssert if entity is invalid or has no NodeComponent
*/
Nz::Vector3f CameraComponent::GetForward() const
{
NazaraAssert(m_entity && m_entity->HasComponent<NodeComponent>(), "CameraComponent requires NodeComponent");
return m_entity->GetComponent<NodeComponent>().GetForward();
}
/*!
* \brief Gets the frutum of the camera
* \return A constant reference to the frustum of the camera
*/
const Nz::Frustumf& CameraComponent::GetFrustum() const
{
EnsureFrustumUpdate();
return m_frustum;
}
/*!
* \brief Gets the projection matrix of the camera
* \return A constant reference to the projection matrix of the camera
*/
const Nz::Matrix4f& CameraComponent::GetProjectionMatrix() const
{
EnsureProjectionMatrixUpdate();
return m_projectionMatrix;
}
/*!
* \brief Gets the projection type of the camera
* \return Projection type of the camera
*/
Nz::ProjectionType CameraComponent::GetProjectionType() const
{
return m_projectionType;
}
/*!
* \brief Gets the target of the camera
* \return A constant reference to the render target of the camera
*/
const Nz::RenderTarget* CameraComponent::GetTarget() const
{
return m_target;
}
/*!
* \brief Gets the view matrix of the camera
* \return A constant reference to the view matrix of the camera
*/
const Nz::Matrix4f& CameraComponent::GetViewMatrix() const
{
EnsureViewMatrixUpdate();
return m_viewMatrix;
}
/*!
* \brief Gets the view port of the camera
* \return A constant reference to the view port of the camera
*/
const Nz::Recti& CameraComponent::GetViewport() const
{
EnsureViewportUpdate();
return m_viewport;
}
/*!
* \brief Gets the Z far distance of the camera
* \return Z far distance of the camera
*/
float CameraComponent::GetZFar() const
{
return m_zFar;
}
/*!
* \brief Gets the Z near distance of the camera
* \return Z near distance of the camera
*/
float CameraComponent::GetZNear() const
{
return m_zNear;
}
/*!
* \brief Sets the layer of the camera in case of multiples layers
*
* \param layer Layer of the camera
*/
void CameraComponent::SetLayer(unsigned int layer)
{
m_layer = layer;
m_entity->Invalidate(); // Invalidate the entity to make it passes through RenderSystem validation
}
/*!
* \brief Operation to perform when component is attached to an entity
*/
void CameraComponent::OnAttached()
{
if (m_entity->HasComponent<NodeComponent>())
m_nodeInvalidationSlot.Connect(m_entity->GetComponent<NodeComponent>().OnNodeInvalidation, this, &CameraComponent::OnNodeInvalidated);
InvalidateViewMatrix();
}
/*!
* \brief Operation to perform when component is attached to this component
*
* \param component Component being attached
*/
void CameraComponent::OnComponentAttached(BaseComponent& component)
{
if (IsComponent<NodeComponent>(component))
{
NodeComponent& nodeComponent = static_cast<NodeComponent&>(component);
m_nodeInvalidationSlot.Connect(nodeComponent.OnNodeInvalidation, this, &CameraComponent::OnNodeInvalidated);
InvalidateViewMatrix();
}
}
/*!
* \brief Operation to perform when component is detached from this component
*
* \param component Component being detached
*/
void CameraComponent::OnComponentDetached(BaseComponent& component)
{
if (IsComponent<NodeComponent>(component))
{
m_nodeInvalidationSlot.Disconnect();
InvalidateViewMatrix();
}
}
/*!
* \brief Operation to perform when component is detached from an entity
*/
void CameraComponent::OnDetached()
{
m_nodeInvalidationSlot.Disconnect();
InvalidateViewMatrix();
}
/*!
* \brief Operation to perform when the node is invalidated
*
* \param node Pointer to the node
*/
void CameraComponent::OnNodeInvalidated(const Nz::Node* node)
{
NazaraUnused(node);
// Our view matrix depends on NodeComponent position/rotation
InvalidateViewMatrix();
}
/*!
* \brief Operation to perform when the render target is released
*
* \param renderTarget Pointer to the RenderTarget
*/
void CameraComponent::OnRenderTargetRelease(const Nz::RenderTarget* renderTarget)
{
if (renderTarget == m_target)
m_target = nullptr;
else
NazaraInternalError("Not listening to " + Nz::String::Pointer(renderTarget));
}
/*!
* \brief Operation to perform when the render target has its size changed
*
* \param renderTarget Pointer to the RenderTarget
*/
void CameraComponent::OnRenderTargetSizeChange(const Nz::RenderTarget* renderTarget)
{
if (renderTarget == m_target)
InvalidateViewport();
else
NazaraInternalError("Not listening to " + Nz::String::Pointer(renderTarget));
}
/*!
* \brief Updates the frustum of the camera
*/
void CameraComponent::UpdateFrustum() const
{
EnsureProjectionMatrixUpdate();
EnsureViewMatrixUpdate();
// Extract the frustum from the view and projection matrices
m_frustum.Extract(m_viewMatrix, m_projectionMatrix);
m_frustumUpdated = true;
}
/*!
* \brief Updates the project matrix of the camera
*/
void CameraComponent::UpdateProjectionMatrix() const
{
switch (m_projectionType)
{
case Nz::ProjectionType_Orthogonal:
if (m_size.x <= 0.f || m_size.y <= 0.f)
{
EnsureViewportUpdate();
m_projectionMatrix.MakeOrtho(0.f, static_cast<float>(m_viewport.width), 0.f, static_cast<float>(m_viewport.height), m_zNear, m_zFar);
}
else
m_projectionMatrix.MakeOrtho(0.f, m_size.x, 0.f, m_size.y, m_zNear, m_zFar);
break;
case Nz::ProjectionType_Perspective:
EnsureViewportUpdate(); // Can affect aspect ratio
m_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar);
break;
}
m_projectionMatrix *= Nz::Matrix4f::Scale(m_projectionScale);
m_projectionMatrixUpdated = true;
}
/*!
* \brief Updates the view matrix of the camera
*
* \remark Produces a NazaraAssert if entity is invalid or has no NodeComponent
*/
void CameraComponent::UpdateViewMatrix() const
{
NazaraAssert(m_entity && m_entity->HasComponent<NodeComponent>(), "CameraComponent requires NodeComponent");
NodeComponent& nodeComponent = m_entity->GetComponent<NodeComponent>();
// Build the view matrix using the NodeComponent position/rotation
m_viewMatrix.MakeViewMatrix(nodeComponent.GetPosition(Nz::CoordSys_Global), nodeComponent.GetRotation(Nz::CoordSys_Global));
m_viewMatrixUpdated = true;
}
/*!
* \brief Updates the view port of the camera
*
* \remark Produces a NazaraAssert if entity has no target
*/
void CameraComponent::UpdateViewport() const
{
NazaraAssert(m_target, "CameraComponent has no target");
Nz::Vector2ui targetSize = m_target->GetSize();
targetSize.y = std::max(targetSize.y, 1U); // Let's make sure we won't divide by zero
// Our target region is expressed as % of the viewport dimensions, let's compute it in pixels
Nz::Rectf fViewport(m_targetRegion);
fViewport.x *= targetSize.x;
fViewport.y *= targetSize.y;
fViewport.width *= targetSize.x;
fViewport.height *= targetSize.y;
// Compute the new aspect ratio, if it's different we need to invalidate the projection matrix
float aspectRatio = fViewport.width/fViewport.height;
if (!Nz::NumberEquals(m_aspectRatio, aspectRatio, 0.001f))
{
m_aspectRatio = aspectRatio;
if (m_projectionType == Nz::ProjectionType_Perspective)
InvalidateProjectionMatrix();
}
// Convert it back to int
m_viewport.Set(fViewport);
m_viewportUpdated = true;
}
ComponentIndex CameraComponent::componentIndex;
}

View File

@@ -1,35 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Components/DebugComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
namespace Ndk
{
void DebugComponent::DetachDebugRenderables(GraphicsComponent& gfxComponent)
{
for (auto& renderable : m_debugRenderables)
{
if (renderable)
{
gfxComponent.Detach(renderable);
renderable.Reset();
}
}
}
void DebugComponent::OnComponentDetached(BaseComponent& component)
{
if (IsComponent<GraphicsComponent>(component))
DetachDebugRenderables(static_cast<GraphicsComponent&>(component));
}
void DebugComponent::OnDetached()
{
if (m_entity->HasComponent<GraphicsComponent>())
DetachDebugRenderables(m_entity->GetComponent<GraphicsComponent>());
}
ComponentIndex DebugComponent::componentIndex;
}

View File

@@ -1,348 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/World.hpp>
#include <NazaraSDK/Systems/RenderSystem.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::GraphicsComponent
* \brief NDK class that represents the component for graphics
*/
/*!
* \brief Adds the renderable elements to the render queue
*
* \param renderQueue Queue to be added
*/
void GraphicsComponent::AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue) const
{
EnsureBoundingVolumesUpdate();
EnsureTransformMatrixUpdate();
RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem<RenderSystem>();
for (const Renderable& object : m_renderables)
{
if (!object.dataUpdated)
{
object.renderable->UpdateData(&object.data);
object.dataUpdated = true;
}
object.renderable->AddToRenderQueue(renderQueue, object.data, m_scissorRect);
}
}
/*!
* \brief Adds the renderable elements to the render queue if their bounding volume intersects with the frustum
*
* \param frustum Queue to be added
* \param renderQueue Queue to be added
*/
void GraphicsComponent::AddToRenderQueueByCulling(const Nz::Frustumf& frustum, Nz::AbstractRenderQueue* renderQueue) const
{
EnsureBoundingVolumesUpdate();
EnsureTransformMatrixUpdate();
RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem<RenderSystem>();
for (const Renderable& object : m_renderables)
{
if (frustum.Contains(object.boundingVolume))
{
if (!object.dataUpdated)
{
object.renderable->UpdateData(&object.data);
object.dataUpdated = true;
}
object.renderable->AddToRenderQueue(renderQueue, object.data, m_scissorRect);
}
}
}
/*!
* \brief Attaches a renderable to the entity with a specific matrix
*
* \param renderable Reference to a renderable element
* \param localMatrix Local matrix that will be applied to the instanced renderable
* \param renderOrder Render order of the element
*/
void GraphicsComponent::Attach(Nz::InstancedRenderableRef renderable, const Nz::Matrix4f& localMatrix, int renderOrder)
{
m_renderables.emplace_back(m_transformMatrix);
Renderable& entry = m_renderables.back();
entry.data.localMatrix = localMatrix;
entry.data.renderOrder = renderOrder;
entry.renderable = std::move(renderable);
ConnectInstancedRenderableSignals(entry);
std::size_t materialCount = entry.renderable->GetMaterialCount();
for (std::size_t i = 0; i < materialCount; ++i)
RegisterMaterial(entry.renderable->GetMaterial(i));
InvalidateAABB();
ForceCullingInvalidation();
}
void GraphicsComponent::ConnectInstancedRenderableSignals(Renderable& entry)
{
entry.renderableBoundingVolumeInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateBoundingVolume, [this](const Nz::InstancedRenderable*) { InvalidateAABB(); });
entry.renderableDataInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateData, std::bind(&GraphicsComponent::InvalidateRenderableData, this, std::placeholders::_1, std::placeholders::_2, m_renderables.size() - 1));
entry.renderableMaterialInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateMaterial, this, &GraphicsComponent::InvalidateRenderableMaterial);
entry.renderableReleaseSlot.Connect(entry.renderable->OnInstancedRenderableRelease, this, &GraphicsComponent::Detach);
entry.renderableResetMaterialsSlot.Connect(entry.renderable->OnInstancedRenderableResetMaterials, this, &GraphicsComponent::OnInstancedRenderableResetMaterials);
entry.renderableSkinChangeSlot.Connect(entry.renderable->OnInstancedRenderableSkinChange, this, &GraphicsComponent::OnInstancedRenderableSkinChange);
}
void GraphicsComponent::InvalidateRenderableData(const Nz::InstancedRenderable* renderable , Nz::UInt32 flags, std::size_t index)
{
NazaraAssert(index < m_renderables.size(), "Invalid renderable index");
NazaraUnused(renderable);
Renderable& r = m_renderables[index];
r.dataUpdated = false;
r.renderable->InvalidateData(&r.data, flags);
ForceCullingInvalidation();
}
void GraphicsComponent::InvalidateRenderableMaterial(const Nz::InstancedRenderable* renderable, std::size_t skinIndex, std::size_t matIndex, const Nz::MaterialRef& newMat)
{
// Don't listen to dormant materials
if (renderable->GetSkin() != skinIndex)
return;
RegisterMaterial(newMat);
const Nz::MaterialRef& oldMat = renderable->GetMaterial(skinIndex, matIndex);
UnregisterMaterial(oldMat);
ForceCullingInvalidation();
}
void GraphicsComponent::InvalidateReflectionMap()
{
m_entity->Invalidate();
if (m_reflectiveMaterialCount > 0)
{
if (!m_reflectionMap)
{
m_reflectionMap = Nz::Texture::New();
if (!m_reflectionMap->Create(Nz::ImageType_Cubemap, Nz::PixelFormat_RGB8, m_reflectionMapSize, m_reflectionMapSize))
{
NazaraWarning("Failed to create reflection map, reflections will be disabled for this entity");
return;
}
}
}
else
m_reflectionMap.Reset();
}
void GraphicsComponent::RegisterMaterial(Nz::Material* material, std::size_t count)
{
auto it = m_materialEntries.find(material);
if (it == m_materialEntries.end())
{
MaterialEntry matEntry;
matEntry.reflectionModelChangeSlot.Connect(material->OnMaterialReflectionModeChange, this, &GraphicsComponent::OnMaterialReflectionChange);
matEntry.renderableCounter = count;
if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime)
{
if (m_reflectiveMaterialCount++ == 0)
InvalidateReflectionMap();
}
m_materialEntries.emplace(material, std::move(matEntry));
}
else
it->second.renderableCounter += count;
}
/*!
* \brief Operation to perform when component is attached to an entity
*/
void GraphicsComponent::OnAttached()
{
if (m_entity->HasComponent<NodeComponent>())
m_nodeInvalidationSlot.Connect(m_entity->GetComponent<NodeComponent>().OnNodeInvalidation, this, &GraphicsComponent::OnNodeInvalidated);
InvalidateTransformMatrix();
}
/*!
* \brief Operation to perform when component is attached to this component
*
* \param component Component being attached
*/
void GraphicsComponent::OnComponentAttached(BaseComponent& component)
{
if (IsComponent<NodeComponent>(component))
{
NodeComponent& nodeComponent = static_cast<NodeComponent&>(component);
m_nodeInvalidationSlot.Connect(nodeComponent.OnNodeInvalidation, this, &GraphicsComponent::OnNodeInvalidated);
InvalidateTransformMatrix();
}
}
/*!
* \brief Operation to perform when component is detached from this component
*
* \param component Component being detached
*/
void GraphicsComponent::OnComponentDetached(BaseComponent& component)
{
if (IsComponent<NodeComponent>(component))
{
m_nodeInvalidationSlot.Disconnect();
InvalidateTransformMatrix();
}
}
/*!
* \brief Operation to perform when component is detached from an entity
*/
void GraphicsComponent::OnDetached()
{
m_nodeInvalidationSlot.Disconnect();
InvalidateTransformMatrix();
}
void GraphicsComponent::OnInstancedRenderableResetMaterials(const Nz::InstancedRenderable* renderable, std::size_t newMaterialCount)
{
RegisterMaterial(Nz::Material::GetDefault(), newMaterialCount);
std::size_t materialCount = renderable->GetMaterialCount();
for (std::size_t i = 0; i < materialCount; ++i)
UnregisterMaterial(renderable->GetMaterial(i));
ForceCullingInvalidation();
}
void GraphicsComponent::OnInstancedRenderableSkinChange(const Nz::InstancedRenderable* renderable, std::size_t newSkinIndex)
{
std::size_t materialCount = renderable->GetMaterialCount();
for (std::size_t i = 0; i < materialCount; ++i)
RegisterMaterial(renderable->GetMaterial(newSkinIndex, i));
for (std::size_t i = 0; i < materialCount; ++i)
UnregisterMaterial(renderable->GetMaterial(i));
ForceCullingInvalidation();
}
void GraphicsComponent::OnMaterialReflectionChange(const Nz::Material* material, Nz::ReflectionMode reflectionMode)
{
// Since this signal is only called when the new reflection mode is different from the current one, no need to compare both
if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime)
{
if (--m_reflectiveMaterialCount == 0)
InvalidateReflectionMap();
}
else if (reflectionMode == Nz::ReflectionMode_RealTime)
{
if (m_reflectiveMaterialCount++ == 0)
InvalidateReflectionMap();
}
}
void GraphicsComponent::OnNodeInvalidated(const Nz::Node* node)
{
NazaraUnused(node);
// Our view matrix depends on NodeComponent position/rotation
InvalidateAABB();
InvalidateTransformMatrix();
ForceCullingInvalidation(); //< Force invalidation on movement for now (FIXME)
}
void GraphicsComponent::UnregisterMaterial(Nz::Material* material)
{
auto it = m_materialEntries.find(material);
NazaraAssert(it != m_materialEntries.end(), "Material not registered");
MaterialEntry& matEntry = it->second;
if (--matEntry.renderableCounter == 0)
{
if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime)
{
if (--m_reflectiveMaterialCount == 0)
InvalidateReflectionMap();
}
m_materialEntries.erase(it);
}
}
/*!
* \brief Updates the bounding volume
*/
void GraphicsComponent::UpdateBoundingVolumes() const
{
EnsureTransformMatrixUpdate();
RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem<RenderSystem>();
m_aabb.Set(-1.f, -1.f, -1.f);
bool isAabbSet = false;
for (const Renderable& r : m_renderables)
{
r.boundingVolume = r.renderable->GetBoundingVolume();
r.data.transformMatrix = Nz::Matrix4f::ConcatenateAffine(renderSystem.GetCoordinateSystemMatrix(), Nz::Matrix4f::ConcatenateAffine(r.data.localMatrix, m_transformMatrix));
if (r.boundingVolume.IsFinite())
{
r.boundingVolume.Update(r.data.transformMatrix);
if (isAabbSet)
m_aabb.ExtendTo(r.boundingVolume.aabb);
else
{
m_aabb.Set(r.boundingVolume.aabb);
isAabbSet = true;
}
}
}
m_boundingVolumesUpdated = true;
for (CullingBoxEntry& entry : m_cullingBoxEntries)
entry.listEntry.UpdateBox(m_aabb);
}
/*!
* \brief Updates the transform matrix of the renderable
*
* \remark Produces a NazaraAssert if entity is invalid or has no NodeComponent
*/
void GraphicsComponent::UpdateTransformMatrix() const
{
NazaraAssert(m_entity && m_entity->HasComponent<NodeComponent>(), "GraphicsComponent requires NodeComponent");
m_transformMatrix = m_entity->GetComponent<NodeComponent>().GetTransformMatrix();
m_transformMatrixUpdated = true;
}
ComponentIndex GraphicsComponent::componentIndex;
}

View File

@@ -1,10 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Components/LightComponent.hpp>
namespace Ndk
{
ComponentIndex LightComponent::componentIndex;
}

View File

@@ -1,30 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Components/ParticleEmitterComponent.hpp>
#include <Nazara/Graphics/ParticleGroup.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::ParticleEmitterComponent
* \brief NDK class that represents the component emitter of particles
*/
/*!
* \brief Sets up the particles
*
* \param mapper Mapper containing layout information of each particle
* \param count Number of particles
*/
void ParticleEmitterComponent::SetupParticles(Nz::ParticleMapper& mapper, unsigned int count) const
{
if (m_isActive && m_setupFunc)
m_setupFunc(m_entity, mapper, count);
}
ComponentIndex ParticleEmitterComponent::componentIndex;
}

View File

@@ -1,10 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Components/ParticleGroupComponent.hpp>
namespace Ndk
{
ComponentIndex ParticleGroupComponent::componentIndex;
}

View File

@@ -1,232 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Console.hpp>
#include <Nazara/Core/Unicode.hpp>
#include <Nazara/Platform/Event.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Widgets.hpp>
#include <NazaraSDK/World.hpp>
namespace Ndk
{
namespace
{
const char s_inputPrefix[] = "> ";
constexpr std::size_t s_inputPrefixSize = Nz::CountOf(s_inputPrefix) - 1;
}
/*!
* \ingroup NDK
* \class Ndk::Console
* \brief NDK class that represents a console to help development with Lua scripting
*/
/*!
* \brief Constructs a Console object with a world to interact with
*
* \param world World to interact with
* \param size (Width, Height) of the console
* \param instance Lua instance that will interact with the world
*/
Console::Console(BaseWidget* parent) :
BaseWidget(parent),
m_historyPosition(0),
m_defaultFont(Nz::Font::GetDefault()),
m_characterSize(24),
m_maxHistoryLines(200)
{
// History
m_history = Add<RichTextAreaWidget>();
m_history->EnableBackground(true);
m_history->EnableLineWrap(true);
m_history->SetReadOnly(true);
m_history->SetBackgroundColor(Nz::Color(80, 80, 160, 128));
m_historyArea = Add<ScrollAreaWidget>(m_history);
// Input
m_input = Add<TextAreaWidget>();
m_input->EnableBackground(true);
m_input->SetText(s_inputPrefix);
m_input->SetTextColor(Nz::Color::Black);
m_input->OnTextAreaKeyReturn.Connect(this, &Console::ExecuteInput);
// Protect input prefix from erasure/selection
m_input->SetCursorPosition(s_inputPrefixSize);
m_input->OnTextAreaCursorMove.Connect([](const AbstractTextAreaWidget* textArea, Nz::Vector2ui* newCursorPos)
{
newCursorPos->x = std::max(newCursorPos->x, static_cast<unsigned int>(s_inputPrefixSize));
});
m_input->OnTextAreaSelection.Connect([](const AbstractTextAreaWidget* textArea, Nz::Vector2ui* start, Nz::Vector2ui* end)
{
start->x = std::max(start->x, static_cast<unsigned int>(s_inputPrefixSize));
end->x = std::max(end->x, static_cast<unsigned int>(s_inputPrefixSize));
});
m_input->OnTextAreaKeyBackspace.Connect([](const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
{
if (textArea->GetGlyphIndex() < s_inputPrefixSize)
*ignoreDefaultAction = true;
});
// Handle history
m_input->OnTextAreaKeyUp.Connect([&] (const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
{
*ignoreDefaultAction = true;
if (m_commandHistory.empty())
return;
if (m_historyPosition > 0)
m_historyPosition--;
m_input->SetText(s_inputPrefix + m_commandHistory[m_historyPosition]);
});
m_input->OnTextAreaKeyDown.Connect([&] (const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
{
*ignoreDefaultAction = true;
if (m_commandHistory.empty())
return;
if (++m_historyPosition >= m_commandHistory.size())
m_historyPosition = 0;
m_input->SetText(s_inputPrefix + m_commandHistory[m_historyPosition]);
});
}
/*!
* \brief Adds a line to the console
*
* \param text New line of text
* \param color Color for the text
*/
void Console::AddLine(const Nz::String& text, const Nz::Color& color)
{
if (m_historyLines.size() >= m_maxHistoryLines)
m_historyLines.erase(m_historyLines.begin());
m_historyLines.emplace_back(Line{ color, text });
m_history->SetTextColor(color);
m_history->AppendText(text + '\n');
m_history->Resize(m_history->GetPreferredSize());
m_historyArea->Resize(m_historyArea->GetSize());
m_historyArea->ScrollToRatio(1.f);
}
/*!
* \brief Clears the console
*
* Clears the console history and input
*/
void Console::Clear()
{
m_historyLines.clear();
m_history->Clear();
m_history->Resize(m_history->GetPreferredSize());
m_historyArea->Resize(m_historyArea->GetSize());
m_input->SetText(s_inputPrefix);
}
/*!
* \brief Clears the console focus
*
* Clear console input widget focus (if owned)
*/
void Console::ClearFocus()
{
m_input->ClearFocus();
}
/*!
* \brief Sets the character size
*
* \param size Size of the font
*/
void Console::SetCharacterSize(unsigned int size)
{
m_characterSize = size;
m_history->SetCharacterSize(size);
m_input->SetCharacterSize(size);
Layout();
}
/*!
* \brief Give the console input focus
*
*/
void Console::SetFocus()
{
m_input->SetFocus();
}
/*!
* \brief Sets the text font
*
* \param font Reference to a valid font
*
* \remark Produces a NazaraAssert if font is invalid or null
*/
void Console::SetTextFont(Nz::FontRef font)
{
NazaraAssert(font && font->IsValid(), "Invalid font");
m_defaultFont = std::move(font);
m_history->SetTextFont(m_defaultFont);
m_input->SetTextFont(m_defaultFont);
Layout();
}
/*!
* \brief Performs this action when an input is added to the console
*/
void Console::ExecuteInput(const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
{
NazaraAssert(textArea == m_input, "Unexpected signal from an other text area");
*ignoreDefaultAction = true;
Nz::String input = m_input->GetText();
Nz::String inputCmd = input.SubString(s_inputPrefixSize);
m_input->SetText(s_inputPrefix);
if (m_commandHistory.empty() || m_commandHistory.back() != inputCmd)
m_commandHistory.push_back(inputCmd);
m_historyPosition = m_commandHistory.size();
AddLine(input); //< With the input prefix
OnCommand(this, inputCmd);
}
/*!
* \brief Places the console according to its layout
*/
void Console::Layout()
{
Nz::Vector2f origin = Nz::Vector2f(GetPosition());
const Nz::Vector2f& size = GetSize();
unsigned int lineHeight = m_defaultFont->GetSizeInfo(m_characterSize).lineHeight;
float historyHeight = size.y - lineHeight;
m_historyArea->SetPosition(origin.x, origin.y);
m_historyArea->Resize({ size.x, historyHeight - 4.f });
m_input->Resize({size.x, size.y - historyHeight});
m_input->SetPosition(origin.x, origin.y + historyHeight);
}
}

View File

@@ -6,7 +6,6 @@
#include <Nazara/Audio/Audio.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Physics2D/Physics2D.hpp>
#include <Nazara/Physics3D/Physics3D.hpp>
#include <Nazara/Platform/Platform.hpp>
@@ -27,18 +26,8 @@
#include <NazaraSDK/Systems/VelocitySystem.hpp>
#ifndef NDK_SERVER
#include <NazaraSDK/Components/CameraComponent.hpp>
#include <NazaraSDK/Components/DebugComponent.hpp>
#include <NazaraSDK/Components/LightComponent.hpp>
#include <NazaraSDK/Components/ListenerComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/ParticleEmitterComponent.hpp>
#include <NazaraSDK/Components/ParticleGroupComponent.hpp>
#include <NazaraSDK/Systems/DebugSystem.hpp>
#include <NazaraSDK/Systems/ParticleSystem.hpp>
#include <NazaraSDK/Systems/ListenerSystem.hpp>
#include <NazaraSDK/Systems/RenderSystem.hpp>
#include <NazaraSDK/Widgets/CheckboxWidget.hpp>
#endif
namespace Ndk
@@ -75,7 +64,6 @@ namespace Ndk
#ifndef NDK_SERVER
// Client modules
Nz::Audio::Initialize();
Nz::Graphics::Initialize();
#endif
// SDK Initialization
@@ -95,13 +83,7 @@ namespace Ndk
#ifndef NDK_SERVER
// Client components
InitializeComponent<CameraComponent>("NdkCam");
InitializeComponent<DebugComponent>("NdkDebug");
InitializeComponent<LightComponent>("NdkLight");
InitializeComponent<ListenerComponent>("NdkList");
InitializeComponent<GraphicsComponent>("NdkGfx");
InitializeComponent<ParticleEmitterComponent>("NdkPaEmi");
InitializeComponent<ParticleGroupComponent>("NdkPaGrp");
#endif
// Systems
@@ -116,17 +98,7 @@ namespace Ndk
#ifndef NDK_SERVER
// Client systems
InitializeSystem<DebugSystem>();
InitializeSystem<ListenerSystem>();
InitializeSystem<ParticleSystem>();
InitializeSystem<RenderSystem>();
// Widgets
if (!CheckboxWidget::Initialize())
{
NazaraError("Failed to initialize Checkbox Widget");
return false;
}
#endif
NazaraNotice("Initialized: SDK");
@@ -170,7 +142,6 @@ namespace Ndk
#ifndef NDK_SERVER
// Client modules
Nz::Audio::Uninitialize();
Nz::Graphics::Uninitialize();
#endif
// Shared modules
@@ -179,8 +150,6 @@ namespace Ndk
Nz::Utility::Uninitialize();
#ifndef NDK_SERVER
// Widgets
CheckboxWidget::Uninitialize();
#endif
NazaraNotice("Uninitialized: SDK");

View File

@@ -1,526 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Systems/DebugSystem.hpp>
#include <Nazara/Core/Primitive.hpp>
#include <Nazara/Graphics/Model.hpp>
#include <Nazara/Utility/IndexIterator.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <NazaraSDK/Components/CollisionComponent2D.hpp>
#include <NazaraSDK/Components/CollisionComponent3D.hpp>
#include <NazaraSDK/Components/DebugComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Components/PhysicsComponent2D.hpp>
namespace Ndk
{
namespace
{
class DebugRenderable : public Nz::InstancedRenderable
{
public:
DebugRenderable(Ndk::Entity* owner, Nz::IndexBufferRef indexBuffer, Nz::VertexBufferRef vertexBuffer) :
m_entityOwner(owner),
m_indexBuffer(std::move(indexBuffer)),
m_vertexBuffer(std::move(vertexBuffer))
{
ResetMaterials(1);
m_meshData.indexBuffer = m_indexBuffer;
m_meshData.primitiveMode = Nz::PrimitiveMode_LineList;
m_meshData.vertexBuffer = m_vertexBuffer;
}
void UpdateBoundingVolume(InstanceData* /*instanceData*/) const override
{
}
void MakeBoundingVolume() const override
{
// We generate an infinite bounding volume so that we're always considered for rendering when culling does occurs
// (bounding volume culling happens only if GraphicsComponent AABB partially fail)
m_boundingVolume.MakeInfinite();
}
protected:
Ndk::EntityHandle m_entityOwner;
Nz::IndexBufferRef m_indexBuffer;
Nz::MeshData m_meshData;
Nz::VertexBufferRef m_vertexBuffer;
};
class AABBDebugRenderable : public DebugRenderable
{
public:
AABBDebugRenderable(Ndk::Entity* owner, Nz::MaterialRef globalMaterial, Nz::MaterialRef localMaterial, Nz::IndexBufferRef indexBuffer, Nz::VertexBufferRef vertexBuffer) :
DebugRenderable(owner, std::move(indexBuffer), std::move(vertexBuffer)),
m_globalMaterial(std::move(globalMaterial)),
m_localMaterial(std::move(localMaterial))
{
}
void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Nz::Recti& scissorRect) const override
{
NazaraAssert(m_entityOwner, "DebugRenderable has no owner");
const DebugComponent& entityDebug = m_entityOwner->GetComponent<DebugComponent>();
const GraphicsComponent& entityGfx = m_entityOwner->GetComponent<GraphicsComponent>();
auto DrawBox = [&](const Nz::Boxf& box, const Nz::MaterialRef& mat)
{
Nz::Matrix4f transformMatrix = Nz::Matrix4f::Identity();
transformMatrix.SetScale(box.GetLengths());
transformMatrix.SetTranslation(box.GetCenter());
renderQueue->AddMesh(0, mat, m_meshData, Nz::Boxf::Zero(), transformMatrix, scissorRect);
};
DrawBox(entityGfx.GetAABB(), m_globalMaterial);
for (std::size_t i = 0; i < entityGfx.GetAttachedRenderableCount(); ++i)
{
const Nz::BoundingVolumef& boundingVolume = entityGfx.GetBoundingVolume(i);
if (boundingVolume.IsFinite())
DrawBox(boundingVolume.aabb, m_localMaterial);
}
}
std::unique_ptr<InstancedRenderable> Clone() const override
{
return nullptr;
}
private:
Nz::MaterialRef m_globalMaterial;
Nz::MaterialRef m_localMaterial;
};
class OBBDebugRenderable : public DebugRenderable
{
public:
OBBDebugRenderable(Ndk::Entity* owner, Nz::MaterialRef material, Nz::IndexBufferRef indexBuffer, Nz::VertexBufferRef vertexBuffer) :
DebugRenderable(owner, std::move(indexBuffer), std::move(vertexBuffer)),
m_material(std::move(material))
{
}
void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Nz::Recti& scissorRect) const override
{
NazaraAssert(m_entityOwner, "DebugRenderable has no owner");
const DebugComponent& entityDebug = m_entityOwner->GetComponent<DebugComponent>();
const GraphicsComponent& entityGfx = m_entityOwner->GetComponent<GraphicsComponent>();
auto DrawBox = [&](const Nz::Boxf& box, const Nz::Matrix4f& transformMatrix)
{
Nz::Matrix4f boxMatrix = Nz::Matrix4f::Identity();
boxMatrix.SetScale(box.GetLengths());
boxMatrix.SetTranslation(box.GetCenter());
boxMatrix.ConcatenateAffine(transformMatrix);
renderQueue->AddMesh(0, m_material, m_meshData, Nz::Boxf::Zero(), boxMatrix, scissorRect);
};
for (std::size_t i = 0; i < entityGfx.GetAttachedRenderableCount(); ++i)
{
const Nz::BoundingVolumef& boundingVolume = entityGfx.GetBoundingVolume(i);
if (boundingVolume.IsFinite())
DrawBox(boundingVolume.obb.localBox, entityGfx.GetTransformMatrix(i));
}
}
std::unique_ptr<InstancedRenderable> Clone() const override
{
return nullptr;
}
private:
Nz::MaterialRef m_material;
};
}
/*!
* \ingroup NDK
* \class Ndk::DebugSystem
* \brief NDK class that represents the debug system
*
* \remark This system is enabled if the entity owns the trait: DebugComponent and GraphicsComponent
*/
/*!
* \brief Constructs an DebugSystem object by default
*/
DebugSystem::DebugSystem() :
m_isDepthBufferEnabled(true)
{
Requires<DebugComponent, GraphicsComponent, NodeComponent>();
SetUpdateOrder(1000); //< Update last
}
void DebugSystem::EnableDepthBuffer(bool enable)
{
m_isDepthBufferEnabled = enable;
if (m_collisionMaterial)
m_collisionMaterial->EnableDepthBuffer(enable);
if (m_globalAabbMaterial)
m_globalAabbMaterial->EnableDepthBuffer(enable);
if (m_localAabbMaterial)
m_localAabbMaterial->EnableDepthBuffer(enable);
if (m_obbMaterial)
m_obbMaterial->EnableDepthBuffer(enable);
}
Nz::InstancedRenderableRef DebugSystem::GenerateBox(Nz::Boxf box)
{
Nz::MeshRef mesh = Nz::Mesh::New();
mesh->CreateStatic();
mesh->BuildSubMesh(Nz::Primitive::Box(box.GetLengths()));
mesh->SetMaterialCount(1);
Nz::ModelRef model = Nz::Model::New();
model->SetMesh(mesh);
model->SetMaterial(0, GetOBBMaterial());
return model;
}
Nz::InstancedRenderableRef DebugSystem::GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* offset)
{
if (entity->HasComponent<CollisionComponent2D>())
{
CollisionComponent2D& entityCollision = entity->GetComponent<CollisionComponent2D>();
const Nz::Collider2DRef& geom = entityCollision.GetGeom();
std::vector<Nz::Vector3f> vertices;
std::vector<std::size_t> indices;
geom->ForEachPolygon([&](const Nz::Vector2f* polygonVertices, std::size_t vertexCount)
{
std::size_t firstIndex = vertices.size();
// Don't reserve and let the vector handle its own capacity
for (std::size_t i = 0; i < vertexCount; ++i)
vertices.emplace_back(*polygonVertices++);
for (std::size_t i = 0; i < vertexCount - 1; ++i)
{
indices.push_back(firstIndex + i);
indices.push_back(firstIndex + i + 1);
}
indices.push_back(firstIndex + vertexCount - 1);
indices.push_back(firstIndex);
});
Nz::IndexBufferRef indexBuffer = Nz::IndexBuffer::New(vertices.size() > 0xFFFF, Nz::UInt32(indices.size()), Nz::DataStorage_Hardware, 0);
Nz::IndexMapper indexMapper(indexBuffer, Nz::BufferAccess_WriteOnly);
Nz::IndexIterator indexPtr = indexMapper.begin();
for (std::size_t index : indices)
*indexPtr++ = static_cast<Nz::UInt32>(index);
indexMapper.Unmap();
Nz::VertexBufferRef vertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), Nz::UInt32(vertices.size()), Nz::DataStorage_Hardware, 0);
vertexBuffer->Fill(vertices.data(), 0, Nz::UInt32(vertices.size()));
Nz::MeshRef mesh = Nz::Mesh::New();
mesh->CreateStatic();
Nz::StaticMeshRef subMesh = Nz::StaticMesh::New(vertexBuffer, indexBuffer);
subMesh->SetPrimitiveMode(Nz::PrimitiveMode_LineList);
subMesh->SetMaterialIndex(0);
subMesh->GenerateAABB();
mesh->SetMaterialCount(1);
mesh->AddSubMesh(subMesh);
Nz::ModelRef model = Nz::Model::New();
model->SetMesh(mesh);
model->SetMaterial(0, GetCollisionMaterial());
// Find center of mass
if (entity->HasComponent<PhysicsComponent2D>())
{
const PhysicsComponent2D& entityPhys = entity->GetComponent<PhysicsComponent2D>();
*offset = entityPhys.GetMassCenter(Nz::CoordSys_Local) + entityCollision.GetGeomOffset();
}
else
*offset = entityCollision.GetGeomOffset();
return model;
}
else
return nullptr;
}
Nz::InstancedRenderableRef DebugSystem::GenerateCollision3DMesh(Entity* entity)
{
if (entity->HasComponent<CollisionComponent3D>())
{
CollisionComponent3D& entityCollision = entity->GetComponent<CollisionComponent3D>();
const Nz::Collider3DRef& geom = entityCollision.GetGeom();
std::vector<Nz::Vector3f> vertices;
std::vector<std::size_t> indices;
geom->ForEachPolygon([&](const Nz::Vector3f* polygonVertices, std::size_t vertexCount)
{
std::size_t firstIndex = vertices.size();
vertices.resize(firstIndex + vertexCount);
std::copy(polygonVertices, polygonVertices + vertexCount, &vertices[firstIndex]);
for (std::size_t i = 0; i < vertexCount - 1; ++i)
{
indices.push_back(firstIndex + i);
indices.push_back(firstIndex + i + 1);
}
indices.push_back(firstIndex + vertexCount - 1);
indices.push_back(firstIndex);
});
Nz::IndexBufferRef indexBuffer = Nz::IndexBuffer::New(vertices.size() > 0xFFFF, Nz::UInt32(indices.size()), Nz::DataStorage_Hardware, 0);
Nz::IndexMapper indexMapper(indexBuffer, Nz::BufferAccess_WriteOnly);
Nz::IndexIterator indexPtr = indexMapper.begin();
for (std::size_t index : indices)
*indexPtr++ = static_cast<Nz::UInt32>(index);
indexMapper.Unmap();
Nz::VertexBufferRef vertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), Nz::UInt32(vertices.size()), Nz::DataStorage_Hardware, 0);
vertexBuffer->Fill(vertices.data(), 0, Nz::UInt32(vertices.size()));
Nz::MeshRef mesh = Nz::Mesh::New();
mesh->CreateStatic();
Nz::StaticMeshRef subMesh = Nz::StaticMesh::New(vertexBuffer, indexBuffer);
subMesh->SetPrimitiveMode(Nz::PrimitiveMode_LineList);
subMesh->SetMaterialIndex(0);
subMesh->GenerateAABB();
mesh->SetMaterialCount(1);
mesh->AddSubMesh(subMesh);
Nz::ModelRef model = Nz::Model::New();
model->SetMesh(mesh);
model->SetMaterial(0, GetCollisionMaterial());
return model;
}
else
return nullptr;
}
std::pair<Nz::IndexBufferRef, Nz::VertexBufferRef> DebugSystem::GetBoxMesh()
{
if (!m_boxMeshIndexBuffer)
{
std::array<Nz::UInt16, 24> indices = {
{
0, 1,
1, 2,
2, 3,
3, 0,
4, 5,
5, 6,
6, 7,
7, 4,
0, 4,
1, 5,
2, 6,
3, 7
}
};
m_boxMeshIndexBuffer = Nz::IndexBuffer::New(false, Nz::UInt32(indices.size()), Nz::DataStorage_Hardware, 0);
m_boxMeshIndexBuffer->Fill(indices.data(), 0, Nz::UInt32(indices.size()));
}
if (!m_boxMeshVertexBuffer)
{
Nz::Boxf box(-0.5f, -0.5f, -0.5f, 1.f, 1.f, 1.f);
std::array<Nz::Vector3f, 8> positions = {
{
box.GetCorner(Nz::BoxCorner_FarLeftBottom),
box.GetCorner(Nz::BoxCorner_NearLeftBottom),
box.GetCorner(Nz::BoxCorner_NearRightBottom),
box.GetCorner(Nz::BoxCorner_FarRightBottom),
box.GetCorner(Nz::BoxCorner_FarLeftTop),
box.GetCorner(Nz::BoxCorner_NearLeftTop),
box.GetCorner(Nz::BoxCorner_NearRightTop),
box.GetCorner(Nz::BoxCorner_FarRightTop)
}
};
m_boxMeshVertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), Nz::UInt32(positions.size()), Nz::DataStorage_Hardware, 0);
m_boxMeshVertexBuffer->Fill(positions.data(), 0, Nz::UInt32(positions.size()));
}
return { m_boxMeshIndexBuffer, m_boxMeshVertexBuffer };
}
Nz::MaterialRef DebugSystem::GetGlobalAABBMaterial()
{
if (!m_globalAabbMaterial)
{
m_globalAabbMaterial = Nz::Material::New();
m_globalAabbMaterial->EnableFaceCulling(false);
m_globalAabbMaterial->EnableDepthBuffer(true);
m_globalAabbMaterial->SetDiffuseColor(Nz::Color::Orange);
m_globalAabbMaterial->SetFaceFilling(Nz::FaceFilling_Line);
//m_globalAabbMaterial->SetLineWidth(2.f);
}
return m_globalAabbMaterial;
}
Nz::MaterialRef DebugSystem::GetLocalAABBMaterial()
{
if (!m_localAabbMaterial)
{
m_localAabbMaterial = Nz::Material::New();
m_localAabbMaterial->EnableFaceCulling(false);
m_localAabbMaterial->EnableDepthBuffer(true);
m_localAabbMaterial->SetDiffuseColor(Nz::Color::Red);
m_localAabbMaterial->SetFaceFilling(Nz::FaceFilling_Line);
//m_localAabbMaterial->SetLineWidth(2.f);
}
return m_localAabbMaterial;
}
Nz::MaterialRef DebugSystem::GetCollisionMaterial()
{
if (!m_collisionMaterial)
{
m_collisionMaterial = Nz::Material::New();
m_collisionMaterial->EnableFaceCulling(false);
m_collisionMaterial->EnableDepthBuffer(true);
m_collisionMaterial->SetDiffuseColor(Nz::Color::Blue);
m_collisionMaterial->SetFaceFilling(Nz::FaceFilling_Line);
//m_collisionMaterial->SetLineWidth(2.f);
}
return m_collisionMaterial;
}
Nz::MaterialRef DebugSystem::GetOBBMaterial()
{
if (!m_obbMaterial)
{
m_obbMaterial = Nz::Material::New();
m_obbMaterial->EnableFaceCulling(false);
m_obbMaterial->EnableDepthBuffer(true);
m_obbMaterial->SetDiffuseColor(Nz::Color::Green);
m_obbMaterial->SetFaceFilling(Nz::FaceFilling_Line);
//m_obbMaterial->SetLineWidth(2.f);
}
return m_obbMaterial;
}
void DebugSystem::OnEntityValidation(Entity* entity, bool /*justAdded*/)
{
static constexpr int DebugDrawOrder = 1'000;
DebugComponent& entityDebug = entity->GetComponent<DebugComponent>();
GraphicsComponent& entityGfx = entity->GetComponent<GraphicsComponent>();
NodeComponent& entityNode = entity->GetComponent<NodeComponent>();
DebugDrawFlags enabledFlags = entityDebug.GetEnabledFlags();
DebugDrawFlags flags = entityDebug.GetFlags();
DebugDrawFlags flagsToEnable = flags & ~enabledFlags;
for (std::size_t i = 0; i <= static_cast<std::size_t>(DebugDraw::Max); ++i)
{
DebugDraw option = static_cast<DebugDraw>(i);
if (flagsToEnable & option)
{
switch (option)
{
case DebugDraw::Collider2D:
{
Nz::Vector3f offset;
Nz::InstancedRenderableRef renderable = GenerateCollision2DMesh(entity, &offset);
if (renderable)
entityGfx.Attach(renderable, Nz::Matrix4f::Translate(offset), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
}
case DebugDraw::Collider3D:
{
const Nz::Boxf& obb = entityGfx.GetAABB();
Nz::InstancedRenderableRef renderable = GenerateCollision3DMesh(entity);
if (renderable)
entityGfx.Attach(renderable, Nz::Matrix4f::Translate(obb.GetCenter() - entityNode.GetPosition()), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
}
case DebugDraw::GraphicsAABB:
{
auto indexVertexBuffers = GetBoxMesh();
Nz::InstancedRenderableRef renderable = new AABBDebugRenderable(entity, GetGlobalAABBMaterial(), GetLocalAABBMaterial(), indexVertexBuffers.first, indexVertexBuffers.second);
renderable->SetPersistent(false);
entityGfx.Attach(renderable, Nz::Matrix4f::Identity(), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
}
case DebugDraw::GraphicsOBB:
{
auto indexVertexBuffers = GetBoxMesh();
Nz::InstancedRenderableRef renderable = new OBBDebugRenderable(entity, GetOBBMaterial(), indexVertexBuffers.first, indexVertexBuffers.second);
renderable->SetPersistent(false);
entityGfx.Attach(renderable, Nz::Matrix4f::Identity(), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
}
default:
break;
}
}
}
DebugDrawFlags flagsToDisable = enabledFlags & ~flags;
for (std::size_t i = 0; i <= static_cast<std::size_t>(DebugDraw::Max); ++i)
{
DebugDraw option = static_cast<DebugDraw>(i);
if (flagsToDisable & option)
entityGfx.Detach(entityDebug.GetDebugRenderable(option));
}
entityDebug.UpdateEnabledFlags(flags);
}
void DebugSystem::OnUpdate(float elapsedTime)
{
// Nothing to do
}
SystemIndex DebugSystem::systemIndex;
}

View File

@@ -1,44 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Systems/ParticleSystem.hpp>
#include <NazaraSDK/Components/ParticleGroupComponent.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::ParticleSystem
* \brief NDK class that represents the particle system
*
* \remark This system is enabled if the entity has the trait: NodeComponent and any of these two: ParticleGroupComponent
*/
/*!
* \brief Constructs an ParticleSystem object by default
*/
ParticleSystem::ParticleSystem()
{
Requires<ParticleGroupComponent>();
}
/*!
* \brief Operation to perform when system is updated
*
* \param elapsedTime Delta time used for the update
*/
void ParticleSystem::OnUpdate(float elapsedTime)
{
for (const Ndk::EntityHandle& entity : GetEntities())
{
ParticleGroupComponent& group = entity->GetComponent<ParticleGroupComponent>();
group.Update(elapsedTime);
}
}
SystemIndex ParticleSystem::systemIndex;
}

View File

@@ -1,428 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Systems/RenderSystem.hpp>
#include <Nazara/Graphics/ColorBackground.hpp>
#include <Nazara/Graphics/ForwardRenderTechnique.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Graphics/SkinningManager.hpp>
#include <Nazara/Graphics/SkyboxBackground.hpp>
#include <Nazara/Math/Rect.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <NazaraSDK/Components/CameraComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/LightComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Components/ParticleGroupComponent.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::RenderSystem
* \brief NDK class that represents the rendering system
*
* \remark This system is enabled if the entity is a 'camera' with the trait: CameraComponent and NodeComponent
* or a drawable element with trait: GraphicsComponent and NodeComponent
* or a light element with trait: LightComponent and NodeComponent
* or a set of particles with trait: ParticleGroupComponent
*/
/*!
* \brief Constructs an RenderSystem object by default
*/
RenderSystem::RenderSystem() :
m_coordinateSystemMatrix(Nz::Matrix4f::Identity()),
m_coordinateSystemInvalidated(true),
m_forceRenderQueueInvalidation(false),
m_isCullingEnabled(true)
{
ChangeRenderTechnique<Nz::ForwardRenderTechnique>();
SetDefaultBackground(Nz::ColorBackground::New());
SetUpdateOrder(100); //< Render last, after every movement is done
SetMaximumUpdateRate(0.f); //< We don't want any rate limit
}
/*!
* \brief Operation to perform when an entity is removed
*
* \param entity Pointer to the entity
*/
void RenderSystem::OnEntityRemoved(Entity* entity)
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it)
{
if (it->GetObject() == entity)
{
m_cameras.erase(it);
break;
}
}
if (entity->HasComponent<GraphicsComponent>())
{
GraphicsComponent& gfxComponent = entity->GetComponent<GraphicsComponent>();
gfxComponent.RemoveFromCullingList(&m_drawableCulling);
}
}
/*!
* \brief Operation to perform when entity is validated for the system
*
* \param entity Pointer to the entity
* \param justAdded Is the entity newly added
*/
void RenderSystem::OnEntityValidation(Entity* entity, bool justAdded)
{
NazaraUnused(justAdded);
if (entity->HasComponent<CameraComponent>() && entity->HasComponent<NodeComponent>())
{
m_cameras.emplace_back(entity);
std::sort(m_cameras.begin(), m_cameras.end(), [](const EntityHandle& handle1, const EntityHandle& handle2)
{
return handle1->GetComponent<CameraComponent>().GetLayer() < handle2->GetComponent<CameraComponent>().GetLayer();
});
}
else
{
for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it)
{
if (it->GetObject() == entity)
{
m_cameras.erase(it);
break;
}
}
}
if (entity->HasComponent<GraphicsComponent>() && entity->HasComponent<NodeComponent>())
{
m_drawables.Insert(entity);
GraphicsComponent& gfxComponent = entity->GetComponent<GraphicsComponent>();
if (justAdded)
gfxComponent.AddToCullingList(&m_drawableCulling);
if (gfxComponent.DoesRequireRealTimeReflections())
m_realtimeReflected.Insert(entity);
else
m_realtimeReflected.Remove(entity);
}
else
{
m_drawables.Remove(entity);
m_realtimeReflected.Remove(entity);
if (entity->HasComponent<GraphicsComponent>())
{
GraphicsComponent& gfxComponent = entity->GetComponent<GraphicsComponent>();
gfxComponent.RemoveFromCullingList(&m_drawableCulling);
}
}
if (entity->HasComponent<LightComponent>() && entity->HasComponent<NodeComponent>())
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
LightComponent& lightComponent = entity->GetComponent<LightComponent>();
if (lightComponent.GetLightType() == Nz::LightType_Directional)
{
m_directionalLights.Insert(entity);
m_pointSpotLights.Remove(entity);
}
else
{
m_directionalLights.Remove(entity);
m_pointSpotLights.Insert(entity);
}
m_lights.Insert(entity);
}
else
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
m_directionalLights.Remove(entity);
m_lights.Remove(entity);
m_pointSpotLights.Remove(entity);
}
if (entity->HasComponent<ParticleGroupComponent>())
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
m_particleGroups.Insert(entity);
}
else
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
m_particleGroups.Remove(entity);
}
}
/*!
* \brief Operation to perform when system is updated
*
* \param elapsedTime Delta time used for the update
*/
void RenderSystem::OnUpdate(float /*elapsedTime*/)
{
// Invalidate every renderable if the coordinate system changed
if (m_coordinateSystemInvalidated)
{
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.InvalidateTransformMatrix();
}
m_coordinateSystemInvalidated = false;
}
Nz::SkinningManager::Skin();
UpdateDynamicReflections();
UpdatePointSpotShadowMaps();
for (const Ndk::EntityHandle& camera : m_cameras)
{
CameraComponent& camComponent = camera->GetComponent<CameraComponent>();
//UpdateDirectionalShadowMaps(camComponent);
Nz::AbstractRenderQueue* renderQueue = m_renderTechnique->GetRenderQueue();
// To make sure the bounding volumes used by the culling list is updated
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.EnsureBoundingVolumesUpdate();
}
bool forceInvalidation = false;
const Nz::Frustumf& frustum = camComponent.GetFrustum();
std::size_t visibilityHash;
if (m_isCullingEnabled)
visibilityHash = m_drawableCulling.Cull(frustum, &forceInvalidation);
else
visibilityHash = m_drawableCulling.FillWithAllEntries(&forceInvalidation);
// Always regenerate renderqueue if particle groups are present for now (FIXME)
if (!m_lights.empty() || !m_particleGroups.empty())
forceInvalidation = true;
if (camComponent.UpdateVisibility(visibilityHash) || m_forceRenderQueueInvalidation || forceInvalidation)
{
renderQueue->Clear();
for (const GraphicsComponent* gfxComponent : m_drawableCulling.GetFullyVisibleResults())
gfxComponent->AddToRenderQueue(renderQueue);
for (const GraphicsComponent* gfxComponent : m_drawableCulling.GetPartiallyVisibleResults())
gfxComponent->AddToRenderQueueByCulling(frustum, renderQueue);
for (const Ndk::EntityHandle& light : m_lights)
{
LightComponent& lightComponent = light->GetComponent<LightComponent>();
NodeComponent& lightNode = light->GetComponent<NodeComponent>();
///TODO: Cache somehow?
lightComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::ConcatenateAffine(m_coordinateSystemMatrix, lightNode.GetTransformMatrix()));
}
for (const Ndk::EntityHandle& particleGroup : m_particleGroups)
{
ParticleGroupComponent& groupComponent = particleGroup->GetComponent<ParticleGroupComponent>();
groupComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::Identity()); //< ParticleGroup doesn't use any transform matrix (yet)
}
m_forceRenderQueueInvalidation = false;
}
camComponent.ApplyView();
Nz::SceneData sceneData;
sceneData.ambientColor = Nz::Color(25, 25, 25);
sceneData.background = m_background;
sceneData.globalReflectionTexture = nullptr;
sceneData.viewer = &camComponent;
if (m_background && m_background->GetBackgroundType() == Nz::BackgroundType_Skybox)
sceneData.globalReflectionTexture = static_cast<Nz::SkyboxBackground*>(m_background.Get())->GetTexture();
m_renderTechnique->Clear(sceneData);
m_renderTechnique->Draw(sceneData);
}
}
/*!
* \brief Updates the directional shadow maps according to the position of the viewer
*
* \param viewer Viewer of the scene
*/
void RenderSystem::UpdateDynamicReflections()
{
Nz::SceneData dummySceneData;
dummySceneData.ambientColor = Nz::Color(0, 0, 0);
dummySceneData.background = nullptr;
dummySceneData.viewer = nullptr; //< Depth technique doesn't require any viewer
for (const Ndk::EntityHandle& handle : m_realtimeReflected)
{
//NazaraWarning("Realtime reflected: #" + handle->ToString());
}
}
void RenderSystem::UpdateDirectionalShadowMaps(const Nz::AbstractViewer& /*viewer*/)
{
if (!m_shadowRT.IsValid())
m_shadowRT.Create();
Nz::SceneData dummySceneData;
dummySceneData.ambientColor = Nz::Color(0, 0, 0);
dummySceneData.background = nullptr;
dummySceneData.viewer = nullptr; //< Depth technique doesn't require any viewer
for (const Ndk::EntityHandle& light : m_directionalLights)
{
LightComponent& lightComponent = light->GetComponent<LightComponent>();
NodeComponent& lightNode = light->GetComponent<NodeComponent>();
if (!lightComponent.IsShadowCastingEnabled())
continue;
Nz::Vector2ui shadowMapSize(lightComponent.GetShadowMap()->GetSize());
m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap());
Nz::Renderer::SetTarget(&m_shadowRT);
Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y));
Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue();
renderQueue->Clear();
///TODO: Culling
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.AddToRenderQueue(renderQueue);
}
///TODO: Cache the matrices in the light?
Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Ortho(0.f, 100.f, 100.f, 0.f, 1.f, 100.f));
Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetRotation() * Nz::Vector3f::Forward() * 100.f, lightNode.GetRotation()));
m_shadowTechnique.Clear(dummySceneData);
m_shadowTechnique.Draw(dummySceneData);
}
}
/*!
* \brief Updates the point spot shadow maps
*/
void RenderSystem::UpdatePointSpotShadowMaps()
{
if (!m_shadowRT.IsValid())
m_shadowRT.Create();
Nz::SceneData dummySceneData;
dummySceneData.ambientColor = Nz::Color(0, 0, 0);
dummySceneData.background = nullptr;
dummySceneData.viewer = nullptr; //< Depth technique doesn't require any viewer
for (const Ndk::EntityHandle& light : m_pointSpotLights)
{
LightComponent& lightComponent = light->GetComponent<LightComponent>();
NodeComponent& lightNode = light->GetComponent<NodeComponent>();
if (!lightComponent.IsShadowCastingEnabled())
continue;
Nz::Vector2ui shadowMapSize(lightComponent.GetShadowMap()->GetSize());
switch (lightComponent.GetLightType())
{
case Nz::LightType_Directional:
NazaraInternalError("Directional lights included in point/spot light list");
break;
case Nz::LightType_Point:
{
static Nz::Quaternionf rotations[6] =
{
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitX()), // CubemapFace_PositiveX
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitX()), // CubemapFace_NegativeX
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitY()), // CubemapFace_PositiveY
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitY()), // CubemapFace_NegativeY
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitZ()), // CubemapFace_PositiveZ
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitZ()) // CubemapFace_NegativeZ
};
for (unsigned int face = 0; face < 6; ++face)
{
m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap(), face);
Nz::Renderer::SetTarget(&m_shadowRT);
Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y));
///TODO: Cache the matrices in the light?
Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Perspective(Nz::FromDegrees(90.f), 1.f, 0.1f, lightComponent.GetRadius()));
Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetPosition(), rotations[face]));
Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue();
renderQueue->Clear();
///TODO: Culling
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.AddToRenderQueue(renderQueue);
}
m_shadowTechnique.Clear(dummySceneData);
m_shadowTechnique.Draw(dummySceneData);
}
break;
}
case Nz::LightType_Spot:
{
m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap());
Nz::Renderer::SetTarget(&m_shadowRT);
Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y));
///TODO: Cache the matrices in the light?
Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Perspective(lightComponent.GetOuterAngle()*2.f, 1.f, 0.1f, lightComponent.GetRadius()));
Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetPosition(), lightNode.GetRotation()));
Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue();
renderQueue->Clear();
///TODO: Culling
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.AddToRenderQueue(renderQueue);
}
m_shadowTechnique.Clear(dummySceneData);
m_shadowTechnique.Draw(dummySceneData);
break;
}
}
}
}
SystemIndex RenderSystem::systemIndex;
}

View File

@@ -1,489 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/AbstractTextAreaWidget.hpp>
#include <Nazara/Core/Unicode.hpp>
#include <Nazara/Utility/Font.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
namespace
{
constexpr float paddingWidth = 5.f;
constexpr float paddingHeight = 3.f;
}
AbstractTextAreaWidget::AbstractTextAreaWidget(BaseWidget* parent) :
BaseWidget(parent),
m_characterFilter(),
m_echoMode(EchoMode_Normal),
m_cursorPositionBegin(0U, 0U),
m_cursorPositionEnd(0U, 0U),
m_isLineWrapEnabled(false),
m_isMouseButtonDown(false),
m_multiLineEnabled(false),
m_readOnly(false),
m_tabEnabled(false)
{
m_textSprite = Nz::TextSprite::New();
m_textEntity = CreateEntity();
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
auto& textNode = m_textEntity->AddComponent<NodeComponent>();
textNode.SetParent(this);
textNode.SetPosition(paddingWidth, paddingHeight);
m_cursorEntity = CreateEntity();
m_cursorEntity->AddComponent<GraphicsComponent>();
m_cursorEntity->AddComponent<NodeComponent>().SetParent(m_textEntity);
m_cursorEntity->GetComponent<NodeComponent>();
m_cursorEntity->Enable(false);
SetCursor(Nz::SystemCursor_Text);
EnableBackground(true);
}
void AbstractTextAreaWidget::Clear()
{
Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
m_cursorPositionBegin.MakeZero();
m_cursorPositionEnd.MakeZero();
textDrawer.Clear();
m_textSprite->Update(textDrawer);
SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths()));
RefreshCursor();
}
void AbstractTextAreaWidget::EnableLineWrap(bool enable)
{
if (m_isLineWrapEnabled != enable)
{
m_isLineWrapEnabled = enable;
Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
if (enable)
textDrawer.SetMaxLineWidth(GetWidth());
else
textDrawer.SetMaxLineWidth(std::numeric_limits<float>::infinity());
UpdateTextSprite();
}
}
Nz::Vector2ui AbstractTextAreaWidget::GetHoveredGlyph(float x, float y) const
{
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
auto& textNode = m_textEntity->GetComponent<Ndk::NodeComponent>();
Nz::Vector2f textPosition = Nz::Vector2f(textNode.GetPosition(Nz::CoordSys_Local));
x -= textPosition.x;
y -= textPosition.y;
std::size_t glyphCount = textDrawer.GetGlyphCount();
if (glyphCount > 0)
{
std::size_t lineCount = textDrawer.GetLineCount();
std::size_t line = 0U;
for (; line < lineCount - 1; ++line)
{
Nz::Rectf lineBounds = textDrawer.GetLine(line).bounds;
if (lineBounds.GetMaximum().y > y)
break;
}
std::size_t upperLimit = (line != lineCount - 1) ? textDrawer.GetLine(line + 1).glyphIndex : glyphCount + 1;
std::size_t firstLineGlyph = textDrawer.GetLine(line).glyphIndex;
std::size_t i = firstLineGlyph;
for (; i < upperLimit - 1; ++i)
{
Nz::Rectf bounds = textDrawer.GetGlyph(i).bounds;
if (x < bounds.x + bounds.width * 0.75f)
break;
}
return Nz::Vector2ui(Nz::Vector2<std::size_t>(i - firstLineGlyph, line));
}
return Nz::Vector2ui::Zero();
}
void AbstractTextAreaWidget::Layout()
{
BaseWidget::Layout();
if (m_isLineWrapEnabled)
{
Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
textDrawer.SetMaxLineWidth(GetWidth());
UpdateTextSprite();
}
RefreshCursor();
}
bool AbstractTextAreaWidget::IsFocusable() const
{
return !m_readOnly;
}
void AbstractTextAreaWidget::OnFocusLost()
{
m_cursorEntity->Disable();
}
void AbstractTextAreaWidget::OnFocusReceived()
{
if (!m_readOnly)
m_cursorEntity->Enable(true);
}
bool AbstractTextAreaWidget::OnKeyPressed(const Nz::WindowEvent::KeyEvent& key)
{
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
switch (key.virtualKey)
{
case Nz::Keyboard::VKey::Backspace:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyBackspace(this, &ignoreDefaultAction);
std::size_t cursorGlyphEnd = GetGlyphIndex(m_cursorPositionEnd);
if (ignoreDefaultAction || cursorGlyphEnd == 0)
return true;
// When a text is selected, delete key does the same as delete and leave the character behind it
if (HasSelection())
EraseSelection();
else
{
MoveCursor(-1);
Erase(GetGlyphIndex(m_cursorPositionBegin));
}
return true;
}
case Nz::Keyboard::VKey::Delete:
{
if (HasSelection())
EraseSelection();
else
Erase(GetGlyphIndex(m_cursorPositionBegin));
return true;
}
case Nz::Keyboard::VKey::Down:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyDown(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (HasSelection())
SetCursorPosition(m_cursorPositionEnd);
MoveCursor({0, 1});
return true;
}
case Nz::Keyboard::VKey::End:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyEnd(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
std::size_t lineCount = textDrawer.GetLineCount();
if (key.control && lineCount > 0)
SetCursorPosition({ static_cast<unsigned int>(textDrawer.GetLineGlyphCount(lineCount - 1)), static_cast<unsigned int>(lineCount - 1) });
else
SetCursorPosition({ static_cast<unsigned int>(textDrawer.GetLineGlyphCount(m_cursorPositionEnd.y)), m_cursorPositionEnd.y });
return true;
}
case Nz::Keyboard::VKey::Home:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyHome(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
SetCursorPosition({ 0U, key.control ? 0U : m_cursorPositionEnd.y });
return true;
}
case Nz::Keyboard::VKey::Left:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyLeft(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (HasSelection())
SetCursorPosition(m_cursorPositionBegin);
else if (key.control)
HandleWordCursorMove(true);
else
MoveCursor(-1);
return true;
}
case Nz::Keyboard::VKey::Return:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyReturn(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (!m_multiLineEnabled)
break;
if (HasSelection())
EraseSelection();
Write(Nz::String('\n'));
return true;;
}
case Nz::Keyboard::VKey::Right:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyRight(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (HasSelection())
SetCursorPosition(m_cursorPositionEnd);
else if (key.control)
HandleWordCursorMove(false);
else
MoveCursor(1);
return true;
}
case Nz::Keyboard::VKey::Up:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyUp(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (HasSelection())
SetCursorPosition(m_cursorPositionBegin);
MoveCursor({0, -1});
return true;
}
case Nz::Keyboard::VKey::Tab:
{
if (!m_tabEnabled)
return false;
if (HasSelection())
HandleSelectionIndentation(!key.shift);
else
HandleIndentation(!key.shift);
return true;
}
default:
break;
}
return false;
}
void AbstractTextAreaWidget::OnKeyReleased(const Nz::WindowEvent::KeyEvent& /*key*/)
{
}
void AbstractTextAreaWidget::OnMouseButtonPress(int x, int y, Nz::Mouse::Button button)
{
if (button == Nz::Mouse::Left)
{
SetFocus();
Nz::Vector2ui hoveredGlyph = GetHoveredGlyph(float(x), float(y));
// Shift extends selection
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RShift))
SetSelection(hoveredGlyph, m_selectionCursor);
else
{
SetCursorPosition(hoveredGlyph);
m_selectionCursor = m_cursorPositionBegin;
}
m_isMouseButtonDown = true;
}
}
void AbstractTextAreaWidget::OnMouseButtonRelease(int, int, Nz::Mouse::Button button)
{
if (button == Nz::Mouse::Left)
m_isMouseButtonDown = false;
}
void AbstractTextAreaWidget::OnMouseEnter()
{
if (!Nz::Mouse::IsButtonPressed(Nz::Mouse::Left))
m_isMouseButtonDown = false;
}
void AbstractTextAreaWidget::OnMouseMoved(int x, int y, int deltaX, int deltaY)
{
if (m_isMouseButtonDown)
SetSelection(m_selectionCursor, GetHoveredGlyph(float(x), float(y)));
}
void AbstractTextAreaWidget::OnTextEntered(char32_t character, bool /*repeated*/)
{
if (m_readOnly)
return;
if (Nz::Unicode::GetCategory(character) == Nz::Unicode::Category_Other_Control || (m_characterFilter && !m_characterFilter(character)))
return;
if (HasSelection())
EraseSelection();
Write(Nz::String::Unicode(character));
}
void AbstractTextAreaWidget::RefreshCursor()
{
if (m_readOnly)
return;
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
auto GetGlyph = [&](const Nz::Vector2ui& glyphPosition, std::size_t* glyphIndex) -> const Nz::AbstractTextDrawer::Glyph*
{
if (glyphPosition.y >= textDrawer.GetLineCount())
return nullptr;
const auto& lineInfo = textDrawer.GetLine(glyphPosition.y);
std::size_t cursorGlyph = GetGlyphIndex({ glyphPosition.x, glyphPosition.y });
if (glyphIndex)
*glyphIndex = cursorGlyph;
std::size_t glyphCount = textDrawer.GetGlyphCount();
if (glyphCount > 0 && lineInfo.glyphIndex < cursorGlyph)
{
const auto& glyph = textDrawer.GetGlyph(std::min(cursorGlyph, glyphCount - 1));
return &glyph;
}
else
return nullptr;
};
// Move text so that cursor is always visible
const auto* lastGlyph = GetGlyph(m_cursorPositionEnd, nullptr);
float glyphPos = (lastGlyph) ? lastGlyph->bounds.x : 0.f;
float glyphWidth = (lastGlyph) ? lastGlyph->bounds.width : 0.f;
auto& node = m_textEntity->GetComponent<Ndk::NodeComponent>();
float textPosition = node.GetPosition(Nz::CoordSys_Local).x - paddingWidth;
float cursorPosition = glyphPos + textPosition;
float width = GetWidth();
if (width <= textDrawer.GetBounds().width)
{
if (cursorPosition + glyphWidth > width)
node.Move(width - cursorPosition - glyphWidth, 0.f);
else if (cursorPosition - glyphWidth < 0.f)
node.Move(-cursorPosition + glyphWidth, 0.f);
}
else
node.Move(-textPosition, 0.f); // Reset text position if we have enough room to show everything
// Show cursor/selection
std::size_t selectionLineCount = m_cursorPositionEnd.y - m_cursorPositionBegin.y + 1;
std::size_t oldSpriteCount = m_cursorSprites.size();
if (m_cursorSprites.size() != selectionLineCount)
{
m_cursorSprites.resize(m_cursorPositionEnd.y - m_cursorPositionBegin.y + 1);
for (std::size_t i = oldSpriteCount; i < m_cursorSprites.size(); ++i)
{
m_cursorSprites[i] = Nz::Sprite::New();
m_cursorSprites[i]->SetMaterial(Nz::Material::New("Translucent2D"));
}
}
GraphicsComponent& gfxComponent = m_cursorEntity->GetComponent<GraphicsComponent>();
gfxComponent.Clear();
for (unsigned int i = m_cursorPositionBegin.y; i <= m_cursorPositionEnd.y; ++i)
{
const auto& lineInfo = textDrawer.GetLine(i);
Nz::SpriteRef& cursorSprite = m_cursorSprites[i - m_cursorPositionBegin.y];
if (i == m_cursorPositionBegin.y || i == m_cursorPositionEnd.y)
{
auto GetGlyphPos = [&](const Nz::Vector2ui& glyphPosition)
{
std::size_t glyphIndex;
const auto* glyph = GetGlyph(glyphPosition, &glyphIndex);
if (glyph)
{
float position = glyph->bounds.x;
if (glyphIndex >= textDrawer.GetGlyphCount())
position += glyph->bounds.width;
return position;
}
else
return 0.f;
};
float beginX = (i == m_cursorPositionBegin.y) ? GetGlyphPos({ m_cursorPositionBegin.x, i }) : 0.f;
float endX = (i == m_cursorPositionEnd.y) ? GetGlyphPos({ m_cursorPositionEnd.x, i }) : lineInfo.bounds.width;
float spriteSize = std::max(endX - beginX, 1.f);
cursorSprite->SetColor((m_cursorPositionBegin == m_cursorPositionEnd) ? Nz::Color::Black : Nz::Color(0, 0, 0, 50));
cursorSprite->SetSize(spriteSize, lineInfo.bounds.height);
gfxComponent.Attach(cursorSprite, Nz::Matrix4f::Translate({ beginX, lineInfo.bounds.y, 0.f }));
}
else
{
cursorSprite->SetColor(Nz::Color(0, 0, 0, 50));
cursorSprite->SetSize(lineInfo.bounds.width, lineInfo.bounds.height);
gfxComponent.Attach(cursorSprite, Nz::Matrix4f::Translate({ 0.f, lineInfo.bounds.y, 0.f }));
}
}
}
void AbstractTextAreaWidget::UpdateTextSprite()
{
m_textSprite->Update(GetTextDrawer());
SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths()));
}
}

View File

@@ -1,122 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/BoxLayout.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/MemoryHelper.hpp>
#include <cassert>
namespace Ndk
{
void BoxLayout::Layout()
{
std::size_t axis1, axis2;
switch (m_orientation)
{
case BoxLayoutOrientation_Horizontal:
axis1 = 0; //< x
axis2 = 1; //< y
break;
case BoxLayoutOrientation_Vertical:
axis1 = 1; //< y
axis2 = 0; //< x
break;
default:
assert(false);
break;
}
m_childInfos.clear();
// Handle size
ForEachWidgetChild([&](BaseWidget* child)
{
if (!child->IsVisible())
return;
m_childInfos.emplace_back();
auto& info = m_childInfos.back();
info.isConstrained = false;
info.maximumSize = child->GetMaximumSize()[axis1];
info.minimumSize = child->GetMinimumSize()[axis1];
info.size = info.minimumSize;
info.widget = child;
});
Nz::Vector2f layoutSize = GetSize();
float availableSpace = layoutSize[axis1] - m_spacing * (m_childInfos.size() - 1);
float remainingSize = availableSpace;
for (auto& info : m_childInfos)
remainingSize -= info.minimumSize;
// Okay this algorithm is FAR from perfect but I couldn't figure a way other than this one
std::size_t unconstrainedChildCount = m_childInfos.size();
bool hasUnconstrainedChilds = false;
for (std::size_t i = 0; i < m_childInfos.size(); ++i)
{
if (remainingSize <= 0.0001f)
break;
float evenSize = remainingSize / unconstrainedChildCount;
for (auto& info : m_childInfos)
{
if (info.isConstrained)
continue;
float previousSize = info.size;
info.size += evenSize;
if (info.size > info.maximumSize)
{
unconstrainedChildCount--;
evenSize += (info.size - info.maximumSize) / unconstrainedChildCount;
info.isConstrained = true;
info.size = info.maximumSize;
}
else
hasUnconstrainedChilds = true;
remainingSize -= info.size - previousSize;
}
if (!hasUnconstrainedChilds)
break;
}
float spacing = m_spacing + remainingSize / (m_childInfos.size() - 1);
for (auto& info : m_childInfos)
{
Nz::Vector2f newSize = info.widget->GetSize();
newSize[axis1] = info.size;
info.widget->Resize(newSize);
}
// Handle position
float cursor = 0.f;
bool first = true;
for (auto& info : m_childInfos)
{
if (first)
first = false;
else
cursor += spacing;
Nz::Vector2f position = Nz::Vector2f(0.f, 0.f);
position[axis1] = cursor;
info.widget->SetPosition(position);
cursor += info.size;
};
}
}

View File

@@ -1,126 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/ButtonWidget.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
Nz::Color ButtonWidget::s_color { 74, 74, 74 };
Nz::Color ButtonWidget::s_cornerColor { 180, 180, 180 };
Nz::Color ButtonWidget::s_hoverColor { 128, 128, 128 };
Nz::Color ButtonWidget::s_hoverCornerColor { s_cornerColor };
Nz::Color ButtonWidget::s_pressColor { s_cornerColor };
Nz::Color ButtonWidget::s_pressCornerColor { s_color };
ButtonWidget::ButtonWidget(BaseWidget* parent) :
BaseWidget(parent),
m_color { s_color },
m_cornerColor { s_cornerColor },
m_hoverColor { s_hoverColor },
m_hoverCornerColor { s_hoverCornerColor },
m_pressColor { s_pressColor },
m_pressCornerColor { s_pressCornerColor }
{
m_gradientSprite = Nz::Sprite::New();
m_gradientSprite->SetColor(m_color);
m_gradientSprite->SetCornerColor(Nz::RectCorner_LeftBottom, m_cornerColor);
m_gradientSprite->SetCornerColor(Nz::RectCorner_RightBottom, m_cornerColor);
m_gradientSprite->SetMaterial(Nz::Material::New("Basic2D"));
m_gradientEntity = CreateEntity();
m_gradientEntity->AddComponent<NodeComponent>().SetParent(this);
m_gradientEntity->AddComponent<GraphicsComponent>().Attach(m_gradientSprite);
m_textSprite = Nz::TextSprite::New();
m_textEntity = CreateEntity();
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite, 1);
Layout();
}
const Nz::Color& ButtonWidget::GetDefaultColor()
{
return s_color;
}
const Nz::Color& ButtonWidget::GetDefaultCornerColor()
{
return s_cornerColor;
}
const Nz::Color& ButtonWidget::GetDefaultHoverColor()
{
return s_hoverColor;
}
const Nz::Color& ButtonWidget::GetDefaultHoverCornerColor()
{
return s_hoverCornerColor;
}
const Nz::Color& ButtonWidget::GetDefaultPressColor()
{
return s_pressColor;
}
const Nz::Color& ButtonWidget::GetDefaultPressCornerColor()
{
return s_pressCornerColor;
}
void ButtonWidget::Layout()
{
BaseWidget::Layout();
Nz::Vector2f size = GetSize();
m_gradientSprite->SetSize(size);
Nz::Boxf textBox = m_textEntity->GetComponent<GraphicsComponent>().GetAABB();
m_textEntity->GetComponent<NodeComponent>().SetPosition(size.x / 2.f - textBox.width / 2.f, size.y / 2.f - textBox.height / 2.f);
}
void ButtonWidget::OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button button)
{
if (button == Nz::Mouse::Left)
{
m_gradientSprite->SetColor(m_pressColor);
m_gradientSprite->SetCornerColor(Nz::RectCorner_LeftBottom, m_pressCornerColor);
m_gradientSprite->SetCornerColor(Nz::RectCorner_RightBottom, m_pressCornerColor);
m_gradientSprite->SetTexture(m_pressTexture, false);
}
}
void ButtonWidget::OnMouseButtonRelease(int /*x*/, int /*y*/, Nz::Mouse::Button button)
{
if (button == Nz::Mouse::Left)
{
m_gradientSprite->SetColor(m_hoverColor);
m_gradientSprite->SetCornerColor(Nz::RectCorner_LeftBottom, m_hoverCornerColor);
m_gradientSprite->SetCornerColor(Nz::RectCorner_RightBottom, m_hoverCornerColor);
m_gradientSprite->SetTexture(m_hoverTexture, false);
OnButtonTrigger(this);
}
}
void ButtonWidget::OnMouseEnter()
{
m_gradientSprite->SetColor(m_hoverColor);
m_gradientSprite->SetCornerColor(Nz::RectCorner_LeftBottom, m_hoverCornerColor);
m_gradientSprite->SetCornerColor(Nz::RectCorner_RightBottom, m_hoverCornerColor);
m_gradientSprite->SetTexture(m_hoverTexture, false);
}
void ButtonWidget::OnMouseExit()
{
m_gradientSprite->SetColor(m_color);
m_gradientSprite->SetCornerColor(Nz::RectCorner_LeftBottom, m_cornerColor);
m_gradientSprite->SetCornerColor(Nz::RectCorner_RightBottom, m_cornerColor);
m_gradientSprite->SetTexture(m_texture, false);
}
}

View File

@@ -1,178 +0,0 @@
// Copyright (C) 2017 Samy Bensaid
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/CheckboxWidget.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <algorithm>
namespace Ndk
{
Nz::Color CheckboxWidget::s_backgroundColor { Nz::Color::White };
Nz::Color CheckboxWidget::s_disabledBackgroundColor { 201, 201, 201 };
Nz::Color CheckboxWidget::s_disabledBorderColor { 62, 62, 62 };
Nz::Color CheckboxWidget::s_borderColor { Nz::Color::Black };
float CheckboxWidget::s_borderScale { 16.f };
CheckboxWidget::CheckboxWidget(BaseWidget* parent) :
BaseWidget(parent),
m_adaptativeMargin { true },
m_checkboxEnabled { true },
m_tristateEnabled { false },
m_textMargin { 16.f },
m_state { CheckboxState_Unchecked }
{
m_checkboxBorderSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_checkboxBackgroundSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_checkboxContentSprite = Nz::Sprite::New(Nz::Material::New("Translucent2D"));
m_textSprite = Nz::TextSprite::New();
m_checkboxBorderEntity = CreateEntity();
m_checkboxBorderEntity->AddComponent<NodeComponent>().SetParent(this);
m_checkboxBorderEntity->AddComponent<GraphicsComponent>().Attach(m_checkboxBorderSprite);
m_checkboxBackgroundEntity = CreateEntity();
m_checkboxBackgroundEntity->AddComponent<NodeComponent>().SetParent(this);
m_checkboxBackgroundEntity->AddComponent<GraphicsComponent>().Attach(m_checkboxBackgroundSprite, 1);
m_checkboxContentEntity = CreateEntity();
m_checkboxContentEntity->AddComponent<NodeComponent>().SetParent(this);
m_checkboxContentEntity->AddComponent<GraphicsComponent>().Attach(m_checkboxContentSprite, 2);
m_textEntity = CreateEntity();
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
m_checkMark = Nz::TextureLibrary::Get("Ndk::CheckboxWidget::checkmark");
SetCheckboxSize({ 32.f, 32.f });
UpdateCheckbox();
}
bool CheckboxWidget::Initialize()
{
const Nz::UInt8 r_checkmark[] =
{
#include <NazaraSDK/Resources/checkmark.png.h>
};
Nz::TextureRef checkmarkTexture = Nz::Texture::LoadFromMemory(r_checkmark, sizeof(r_checkmark) / sizeof(r_checkmark[0]));
if (!checkmarkTexture)
{
NazaraError("Failed to load embedded checkmark");
return false;
}
Nz::TextureLibrary::Register("Ndk::CheckboxWidget::checkmark", std::move(checkmarkTexture));
return true;
}
void CheckboxWidget::Uninitialize()
{
Nz::TextureLibrary::Unregister("Ndk::CheckboxWidget::checkmark");
}
void CheckboxWidget::SetState(CheckboxState state)
{
if (!m_checkboxEnabled)
return;
if (state == CheckboxState_Tristate)
m_tristateEnabled = true;
m_state = state;
UpdateCheckbox();
}
CheckboxState CheckboxWidget::SwitchToNextState()
{
if (!m_checkboxEnabled)
return m_state;
switch (m_state)
{
case CheckboxState_Unchecked:
SetState(CheckboxState_Checked);
break;
case CheckboxState_Checked:
SetState(m_tristateEnabled ? CheckboxState_Tristate : CheckboxState_Unchecked);
break;
case CheckboxState_Tristate:
SetState(CheckboxState_Unchecked);
break;
}
return m_state;
}
void CheckboxWidget::Layout()
{
BaseWidget::Layout();
Nz::Vector2f checkboxSize = GetCheckboxSize();
Nz::Vector2f borderSize = GetCheckboxBorderSize();
m_checkboxBackgroundEntity->GetComponent<NodeComponent>().SetPosition(borderSize);
Nz::Vector3f checkboxBox = m_checkboxContentSprite->GetBoundingVolume().obb.localBox.GetLengths();
m_checkboxContentEntity->GetComponent<NodeComponent>().SetPosition(checkboxSize.x / 2.f - checkboxBox.x / 2.f, checkboxSize.y / 2.f - checkboxBox.y / 2.f);
Nz::Vector3f textBox = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths();
m_textEntity->GetComponent<NodeComponent>().SetPosition(checkboxSize.x + (m_adaptativeMargin ? checkboxSize.x / 2.f : m_textMargin), checkboxSize.y / 2.f - textBox.y / 2.f);
}
void CheckboxWidget::OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button)
{
if (button == Nz::Mouse::Left && ContainsCheckbox(x, y) && IsCheckboxEnabled())
{
SwitchToNextState();
OnStateChanged(this);
}
}
void CheckboxWidget::UpdateCheckbox()
{
if (m_checkboxEnabled)
{
m_checkboxBorderSprite->SetColor(s_borderColor);
m_checkboxBackgroundSprite->SetColor(s_backgroundColor);
}
else
{
m_checkboxBorderSprite->SetColor(s_disabledBorderColor);
m_checkboxBackgroundSprite->SetColor(s_disabledBackgroundColor);
}
if (m_state == CheckboxState_Unchecked)
{
m_checkboxContentEntity->Enable(false);
return;
}
else if (m_state == CheckboxState_Checked)
{
m_checkboxContentEntity->Enable();
m_checkboxContentSprite->SetColor(Nz::Color::White);
m_checkboxContentSprite->SetTexture(m_checkMark, false);
}
else // Tristate
{
m_checkboxContentEntity->Enable();
m_checkboxContentSprite->SetColor(Nz::Color::Black);
m_checkboxContentSprite->SetTexture(Nz::TextureRef {});
}
}
void CheckboxWidget::UpdateSize()
{
Nz::Vector3f textSize = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths();
Nz::Vector2f checkboxSize = GetCheckboxSize();
Nz::Vector2f finalSize { checkboxSize.x + (m_adaptativeMargin ? checkboxSize.x / 2.f : m_textMargin) + textSize.x, std::max(textSize.y, checkboxSize.y) };
SetMinimumSize(finalSize);
SetPreferredSize(finalSize);
}
}

View File

@@ -1,28 +0,0 @@
// Copyright (C) 2017 Samy Bensaid
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/ImageWidget.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
namespace Ndk
{
ImageWidget::ImageWidget(BaseWidget* parent) :
BaseWidget(parent)
{
m_entity = CreateEntity();
m_entity->AddComponent<NodeComponent>().SetParent(this);
auto& gfx = m_entity->AddComponent<GraphicsComponent>();
m_sprite = Nz::Sprite::New();
gfx.Attach(m_sprite);
}
void ImageWidget::Layout()
{
BaseWidget::Layout();
m_sprite->SetSize(GetSize());
}
}

View File

@@ -1,22 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/LabelWidget.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
LabelWidget::LabelWidget(BaseWidget* parent) :
BaseWidget(parent)
{
m_textSprite = Nz::TextSprite::New();
m_textEntity = CreateEntity();
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
Layout();
}
}

View File

@@ -1,100 +0,0 @@
// Copyright (C) 2017 Samy Bensaid
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/ProgressBarWidget.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
namespace Ndk
{
float ProgressBarWidget::s_borderScale { 16.f };
Nz::Color ProgressBarWidget::s_borderColor { Nz::Color::Black };
Nz::Color ProgressBarWidget::s_barBackgroundColor { Nz::Color { 225, 225, 225 } };
Nz::Color ProgressBarWidget::s_barBackgroundCornerColor { Nz::Color { 255, 255, 255 } };
Nz::Color ProgressBarWidget::s_barColor { Nz::Color { 0, 225, 0 } };
Nz::Color ProgressBarWidget::s_barCornerColor { Nz::Color { 220, 255, 220 } };
ProgressBarWidget::ProgressBarWidget(BaseWidget* parent) :
BaseWidget(parent),
m_textColor { Nz::Color::Black },
m_textMargin { 16.f },
m_value { 0u }
{
m_borderSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_barBackgroundSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_barSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_borderSprite->SetColor(s_borderColor);
SetBarBackgroundColor(s_barBackgroundColor, s_barBackgroundCornerColor);
SetBarColor(s_barColor, s_barCornerColor);
m_borderEntity = CreateEntity();
m_borderEntity->AddComponent<NodeComponent>().SetParent(this);
m_borderEntity->AddComponent<GraphicsComponent>().Attach(m_borderSprite);
m_barEntity = CreateEntity();
m_barEntity->AddComponent<NodeComponent>().SetParent(this);
GraphicsComponent& graphics = m_barEntity->AddComponent<GraphicsComponent>();
graphics.Attach(m_barBackgroundSprite, 1);
graphics.Attach(m_barSprite, 2);
m_textSprite = Nz::TextSprite::New();
m_textEntity = CreateEntity();
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
UpdateText();
Layout();
}
const Nz::Color& ProgressBarWidget::GetDefaultBarColor()
{
return s_barColor;
}
const Nz::Color& ProgressBarWidget::GetDefaultBarCornerColor()
{
return s_barCornerColor;
}
const Nz::Color& ProgressBarWidget::GetDefaultBarBackgroundColor()
{
return s_barBackgroundColor;
}
const Nz::Color& ProgressBarWidget::GetDefaultBarBackgroundCornerColor()
{
return s_barBackgroundCornerColor;
}
void ProgressBarWidget::Layout()
{
Nz::Vector2f size = GetSize();
Nz::Vector2f progressBarSize = size;
if (IsTextEnabled())
{
UpdateText();
Nz::Vector3f textSize = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths();
m_textEntity->GetComponent<NodeComponent>().SetPosition(size.x - textSize.x, size.y / 2.f - textSize.y);
progressBarSize -= { textSize.x + m_textMargin, 0.f };
}
m_borderSprite->SetSize(progressBarSize);
Nz::Vector2f borderSize = GetProgressBarBorderSize();
m_barBackgroundSprite->SetSize(progressBarSize - (borderSize * 2.f));
m_barSprite->SetSize((progressBarSize.x - (borderSize.x * 2.f)) / 100.f * static_cast<float>(m_value), progressBarSize.y - (borderSize.y * 2.f));
m_barEntity->GetComponent<NodeComponent>().SetPosition(borderSize.x, borderSize.y);
}
}

View File

@@ -1,196 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/RichTextAreaWidget.hpp>
namespace Ndk
{
RichTextAreaWidget::RichTextAreaWidget(BaseWidget* parent) :
AbstractTextAreaWidget(parent)
{
Layout();
}
void RichTextAreaWidget::AppendText(const Nz::String& text)
{
//m_text += text;
switch (m_echoMode)
{
case EchoMode_Normal:
m_drawer.AppendText(text);
break;
case EchoMode_Password:
m_drawer.AppendText(Nz::String(text.GetLength(), '*'));
break;
case EchoMode_PasswordExceptLast:
{
/*m_drawer.Clear();
std::size_t textLength = m_text.GetLength();
if (textLength >= 2)
{
std::size_t lastCharacterPosition = m_text.GetCharacterPosition(textLength - 2);
if (lastCharacterPosition != Nz::String::npos)
m_drawer.AppendText(Nz::String(textLength - 1, '*'));
}
if (textLength >= 1)
m_drawer.AppendText(m_text.SubString(m_text.GetCharacterPosition(textLength - 1)));*/
break;
}
}
UpdateTextSprite();
//OnTextChanged(this, m_text);
}
void RichTextAreaWidget::Clear()
{
AbstractTextAreaWidget::Clear();
}
void RichTextAreaWidget::Erase(std::size_t firstGlyph, std::size_t lastGlyph)
{
if (firstGlyph > lastGlyph)
std::swap(firstGlyph, lastGlyph);
std::size_t textLength = m_drawer.GetGlyphCount();
if (firstGlyph > textLength)
return;
std::size_t firstBlock = m_drawer.FindBlock(firstGlyph);
std::size_t lastBlock = m_drawer.FindBlock((lastGlyph > 0) ? lastGlyph - 1 : lastGlyph);
if (firstBlock == lastBlock)
{
const Nz::String& blockText = m_drawer.GetBlockText(firstBlock);
std::size_t blockFirstGlyph = m_drawer.GetBlockFirstGlyphIndex(firstBlock);
Nz::String newText;
if (firstGlyph > blockFirstGlyph)
{
std::size_t characterPosition = blockText.GetCharacterPosition(firstGlyph - blockFirstGlyph);
NazaraAssert(characterPosition != Nz::String::npos, "Invalid character position");
newText.Append(blockText.SubString(0, characterPosition - 1));
}
if (lastGlyph < textLength)
newText.Append(blockText.SubString(blockText.GetCharacterPosition(lastGlyph - blockFirstGlyph)));
if (!newText.IsEmpty())
m_drawer.SetBlockText(firstBlock, std::move(newText));
else
m_drawer.RemoveBlock(firstBlock);
}
else
{
const Nz::String& lastBlockText = m_drawer.GetBlockText(lastBlock);
std::size_t lastBlockGlyphIndex = m_drawer.GetBlockFirstGlyphIndex(lastBlock);
// First, update/delete last block
std::size_t lastCharPos = lastBlockText.GetCharacterPosition(lastGlyph - lastBlockGlyphIndex);
if (lastCharPos != Nz::String::npos)
{
Nz::String newText = lastBlockText.SubString(lastCharPos);
if (!newText.IsEmpty())
m_drawer.SetBlockText(lastBlock, std::move(newText));
else
m_drawer.RemoveBlock(lastBlock);
}
// And then remove all middle blocks, remove in reverse order because of index shifting
assert(lastBlock > 0);
for (std::size_t i = lastBlock - 1; i > firstBlock; --i)
m_drawer.RemoveBlock(i);
const Nz::String& firstBlockText = m_drawer.GetBlockText(firstBlock);
std::size_t firstBlockGlyphIndex = m_drawer.GetBlockFirstGlyphIndex(firstBlock);
// And finally update/delete first block
if (firstGlyph > firstBlockGlyphIndex)
{
std::size_t firstCharPos = firstBlockText.GetCharacterPosition(firstGlyph - firstBlockGlyphIndex - 1);
if (firstCharPos != Nz::String::npos)
{
Nz::String newText = firstBlockText.SubString(0, firstCharPos);
if (!newText.IsEmpty())
m_drawer.SetBlockText(firstBlock, std::move(newText));
else
m_drawer.RemoveBlock(firstBlock);
}
}
else
m_drawer.RemoveBlock(firstBlock);
}
UpdateDisplayText();
}
void RichTextAreaWidget::Write(const Nz::String& text, std::size_t glyphPosition)
{
if (m_drawer.HasBlocks())
{
auto block = m_drawer.GetBlock(m_drawer.FindBlock((glyphPosition > 0) ? glyphPosition - 1 : glyphPosition));
std::size_t firstGlyph = block.GetFirstGlyphIndex();
assert(glyphPosition >= firstGlyph);
Nz::String blockText = block.GetText();
std::size_t characterPosition = blockText.GetCharacterPosition(glyphPosition - firstGlyph);
blockText.Insert(characterPosition, text);
block.SetText(blockText);
}
else
m_drawer.AppendText(text);
SetCursorPosition(glyphPosition + text.GetLength());
UpdateDisplayText();
}
Nz::AbstractTextDrawer& RichTextAreaWidget::GetTextDrawer()
{
return m_drawer;
}
const Nz::AbstractTextDrawer& RichTextAreaWidget::GetTextDrawer() const
{
return m_drawer;
}
void RichTextAreaWidget::HandleIndentation(bool add)
{
}
void RichTextAreaWidget::HandleSelectionIndentation(bool add)
{
}
void RichTextAreaWidget::HandleWordCursorMove(bool left)
{
}
void RichTextAreaWidget::UpdateDisplayText()
{
/*m_drawer.Clear();
switch (m_echoMode)
{
case EchoMode_Normal:
m_drawer.AppendText(m_text);
break;
case EchoMode_Password:
case EchoMode_PasswordExceptLast:
m_drawer.AppendText(Nz::String(m_text.GetLength(), '*'));
break;
}*/
UpdateTextSprite();
SetCursorPosition(m_cursorPositionBegin); //< Refresh cursor position (prevent it from being outside of the text)
}
}

View File

@@ -1,198 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/ScrollAreaWidget.hpp>
#include <Nazara/Math/Algorithm.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
namespace
{
constexpr float scrollbarPadding = 5.f;
}
ScrollAreaWidget::ScrollAreaWidget(BaseWidget* parent, BaseWidget* content) :
BaseWidget(parent),
m_content(content),
m_scrollbarStatus(ScrollBarStatus::None),
m_isScrollbarEnabled(true),
m_scrollRatio(0.f)
{
m_content->SetParent(this);
m_content->SetPosition(Nz::Vector3f::Zero());
m_scrollbarBackgroundSprite = Nz::Sprite::New();
m_scrollbarBackgroundSprite->SetColor(Nz::Color(62, 62, 62));
m_scrollbarBackgroundEntity = CreateEntity();
m_scrollbarBackgroundEntity->AddComponent<NodeComponent>().SetParent(this);
m_scrollbarBackgroundEntity->AddComponent<GraphicsComponent>().Attach(m_scrollbarBackgroundSprite, 1);
m_scrollbarSprite = Nz::Sprite::New();
m_scrollbarSprite->SetColor(Nz::Color(104, 104, 104));
m_scrollbarEntity = CreateEntity();
m_scrollbarEntity->AddComponent<NodeComponent>().SetParent(this);
m_scrollbarEntity->AddComponent<GraphicsComponent>().Attach(m_scrollbarSprite);
Resize(m_content->GetSize());
}
void ScrollAreaWidget::EnableScrollbar(bool enable)
{
if (m_isScrollbarEnabled != enable)
{
m_isScrollbarEnabled = enable;
bool isVisible = IsScrollbarVisible();
m_scrollbarEntity->Enable(isVisible);
m_scrollbarBackgroundEntity->Enable(isVisible);
}
}
void ScrollAreaWidget::ScrollToRatio(float ratio)
{
m_scrollRatio = Nz::Clamp(ratio, 0.f, 1.f);
float widgetHeight = GetHeight();
float maxHeight = widgetHeight - m_scrollbarSprite->GetSize().y - 2.f * scrollbarPadding;
auto& scrollbarNode = m_scrollbarEntity->GetComponent<Ndk::NodeComponent>();
scrollbarNode.SetPosition(Nz::Vector2f(scrollbarNode.GetPosition(Nz::CoordSys_Local).x, scrollbarPadding + m_scrollRatio * maxHeight));
float contentPosition = m_scrollRatio * (widgetHeight - m_content->GetHeight());
m_content->SetPosition(0.f, contentPosition);
m_content->SetRenderingRect(Nz::Rectf(-std::numeric_limits<float>::infinity(), -contentPosition, std::numeric_limits<float>::infinity(), widgetHeight));
}
Nz::Rectf ScrollAreaWidget::GetScrollbarRect() const
{
Nz::Vector2f scrollBarPosition = Nz::Vector2f(m_scrollbarEntity->GetComponent<Ndk::NodeComponent>().GetPosition(Nz::CoordSys_Local));
Nz::Vector2f scrollBarSize = m_scrollbarSprite->GetSize();
return Nz::Rectf(scrollBarPosition.x, scrollBarPosition.y, scrollBarSize.x, scrollBarSize.y);
}
void ScrollAreaWidget::Layout()
{
constexpr float scrollBarBackgroundWidth = 20.f;
constexpr float scrollBarWidth = scrollBarBackgroundWidth - 2.f * scrollbarPadding;
float areaHeight = GetHeight();
float contentHeight = m_content->GetHeight();
if (contentHeight > areaHeight)
{
m_hasScrollbar = true;
Nz::Vector2f contentSize(GetWidth() - scrollBarBackgroundWidth, contentHeight);
m_content->Resize(contentSize);
if (m_isScrollbarEnabled)
{
m_scrollbarEntity->Enable();
m_scrollbarBackgroundEntity->Enable();
}
float scrollBarHeight = std::max(std::floor(areaHeight * (areaHeight / contentHeight)), 20.f);
m_scrollbarBackgroundSprite->SetSize(scrollBarBackgroundWidth, areaHeight);
m_scrollbarSprite->SetSize(scrollBarWidth, scrollBarHeight);
m_scrollbarBackgroundEntity->GetComponent<Ndk::NodeComponent>().SetPosition(contentSize.x, 0.f);
m_scrollbarEntity->GetComponent<Ndk::NodeComponent>().SetPosition(contentSize.x + (scrollBarBackgroundWidth - scrollBarWidth) / 2.f, 0.f);
ScrollToRatio(m_scrollRatio);
}
else
{
m_hasScrollbar = false;
m_content->Resize(GetSize());
m_scrollbarEntity->Disable();
m_scrollbarBackgroundEntity->Disable();
ScrollToRatio(0.f);
}
BaseWidget::Layout();
}
void ScrollAreaWidget::OnMouseButtonPress(int x, int y, Nz::Mouse::Button button)
{
if (button != Nz::Mouse::Left)
return;
if (m_scrollbarStatus == ScrollBarStatus::Hovered)
{
UpdateScrollbarStatus(ScrollBarStatus::Grabbed);
auto& scrollbarNode = m_scrollbarEntity->GetComponent<Ndk::NodeComponent>();
m_grabbedDelta.Set(x, int(y - scrollbarNode.GetPosition(Nz::CoordSys_Local).y));
}
}
void ScrollAreaWidget::OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button)
{
if (button != Nz::Mouse::Left)
return;
if (m_scrollbarStatus == ScrollBarStatus::Grabbed)
{
Nz::Rectf scrollBarRect = GetScrollbarRect();
UpdateScrollbarStatus((scrollBarRect.Contains(Nz::Vector2f(float(x), float(y)))) ? ScrollBarStatus::Hovered : ScrollBarStatus::None);
}
}
void ScrollAreaWidget::OnMouseExit()
{
//if (m_scrollbarStatus == ScrollBarStatus::Hovered)
UpdateScrollbarStatus(ScrollBarStatus::None);
}
void ScrollAreaWidget::OnMouseMoved(int x, int y, int /*deltaX*/, int /*deltaY*/)
{
if (m_scrollbarStatus == ScrollBarStatus::Grabbed)
{
float height = GetHeight();
float maxHeight = height - m_scrollbarSprite->GetSize().y;
float newHeight = Nz::Clamp(float(y - m_grabbedDelta.y), 0.f, maxHeight);
ScrollToHeight(newHeight / maxHeight * m_content->GetHeight());
}
else
{
Nz::Rectf scrollBarRect = GetScrollbarRect();
UpdateScrollbarStatus((scrollBarRect.Contains(Nz::Vector2f(float(x), float(y)))) ? ScrollBarStatus::Hovered : ScrollBarStatus::None);
}
}
void ScrollAreaWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float delta)
{
constexpr float scrollStep = 100.f;
ScrollToHeight(GetScrollHeight() - scrollStep * delta);
}
void ScrollAreaWidget::UpdateScrollbarStatus(ScrollBarStatus status)
{
if (m_scrollbarStatus != status)
{
Nz::Color newColor;
switch (status)
{
case ScrollBarStatus::Grabbed: newColor = Nz::Color(235, 235, 235); break;
case ScrollBarStatus::Hovered: newColor = Nz::Color(152, 152, 152); break;
case ScrollBarStatus::None: newColor = Nz::Color(104, 104, 104); break;
}
m_scrollbarSprite->SetColor(newColor);
m_scrollbarStatus = status;
}
}
}

Some files were not shown because too many files have changed in this diff Show More