NazaraEngine/src/Nazara/Graphics/BasicRenderQueue.cpp

958 lines
26 KiB
C++

// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/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;
});
}
}
}