Remove Graphics module and fix compilation
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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
@@ -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
@@ -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
@@ -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());*/
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user