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;
|
||||
}
|
||||
Reference in New Issue
Block a user