Core/Algorithm: Add SafeCast

This commit is contained in:
Jérôme Leclercq 2021-10-26 20:23:31 +02:00
parent 73838f5f08
commit 66bbf63e87
6 changed files with 114 additions and 30 deletions

View File

@ -715,7 +715,7 @@ int main()
builder.BindVertexBuffer(0, *cubeMeshGfx->GetVertexBuffer(0));
builder.BindPipeline(*skyboxPipeline);
builder.DrawIndexed(static_cast<Nz::UInt32>(cubeMeshGfx->GetIndexCount(0)));
builder.DrawIndexed(Nz::SafeCast<Nz::UInt32>(cubeMeshGfx->GetIndexCount(0)));
});
forwardPass.SetExecutionCallback([&]
{

View File

@ -36,6 +36,16 @@ namespace Nz
template<typename T> void HashCombine(std::size_t& seed, const T& v);
template<typename T> bool IsPowerOfTwo(T value);
template<typename T> T ReverseBits(T integer);
#ifdef NAZARA_DEBUG
template<typename To, typename From, std::enable_if_t<std::is_integral_v<To> && std::is_integral_v<From>, int> = 0> To SafeCast(From value);
template<typename To, typename From, std::enable_if_t<std::is_floating_point_v<To> && std::is_floating_point_v<From>, int> = 0> To SafeCast(From value);
template<typename To, typename From, std::enable_if_t<std::is_integral_v<To> && std::is_floating_point_v<From>, int> = 0> To SafeCast(From value);
template<typename To, typename From, std::enable_if_t<std::is_floating_point_v<To> && std::is_integral_v<From>, int> = 0> To SafeCast(From value);
template<typename To, typename From, std::enable_if_t<std::is_enum_v<To>&& std::is_integral_v<From>, int> = 0> To SafeCast(From value);
template<typename To, typename From, std::enable_if_t<std::is_integral_v<To>&& std::is_enum_v<From>, int> = 0> To SafeCast(From value);
#else
template<typename To, typename From> To SafeCast(From value);
#endif
template<typename T> constexpr auto UnderlyingCast(T value) -> std::underlying_type_t<T>;
template<typename T>

View File

@ -13,6 +13,7 @@
#include <Nazara/Core/Stream.hpp>
#include <cassert>
#include <climits>
#include <limits>
#include <utility>
#include <Nazara/Core/Debug.hpp>
@ -291,6 +292,75 @@ namespace Nz
return reversed;
}
#ifdef NAZARA_DEBUG
template<typename To, typename From, std::enable_if_t<std::is_integral_v<To> && std::is_integral_v<From>, int>>
To SafeCast(From value)
{
// Type capable of storing the biggest value between the two types
using MaxValueType = std::conditional_t<(sizeof(From) > sizeof(To) || (sizeof(From) == sizeof(To) && std::is_signed_v<To>)), From, To>;
// Type capable of storing the smallest value between the two types
using MinValueType = std::conditional_t<(sizeof(From) > sizeof(To) || (sizeof(From) == sizeof(To) && std::is_signed_v<From>)), From, To>;
if constexpr (!std::is_signed_v<To>)
assert(value >= 0);
assert(static_cast<MaxValueType>(value) <= static_cast<MaxValueType>(std::numeric_limits<To>::max()));
assert(static_cast<MinValueType>(value) >= static_cast<MinValueType>(std::numeric_limits<To>::lowest()));
return static_cast<To>(value);
}
template<typename To, typename From, std::enable_if_t<std::is_floating_point_v<To> && std::is_floating_point_v<From>, int>>
To SafeCast(From value)
{
// Type capable of storing the biggest value between the two types
using MaxValueType = std::conditional_t<(sizeof(From) > sizeof(To)), From, To>;
// Type capable of storing the smallest value between the two types
using MinValueType = std::conditional_t<(sizeof(From) > sizeof(To)), From, To>;
assert(static_cast<MaxValueType>(value) <= static_cast<MaxValueType>(std::numeric_limits<To>::max()));
assert(static_cast<MinValueType>(value) >= static_cast<MinValueType>(std::numeric_limits<To>::lowest()));
return static_cast<To>(value);
}
template<typename To, typename From, std::enable_if_t<std::is_integral_v<To> && std::is_floating_point_v<From>, int>>
To SafeCast(From value)
{
assert(floor(value) == value);
assert(value <= static_cast<From>(std::numeric_limits<To>::max()));
assert(value >= static_cast<From>(std::numeric_limits<To>::lowest()));
return static_cast<To>(value);
}
template<typename To, typename From, std::enable_if_t<std::is_floating_point_v<To> && std::is_integral_v<From>, int>>
To SafeCast(From value)
{
return static_cast<To>(value);
}
template<typename To, typename From, std::enable_if_t<std::is_enum_v<To> && std::is_integral_v<From>, int>>
To SafeCast(From value)
{
return static_cast<To>(SafeCast<std::underlying_type_t<To>>(value));
}
template<typename To, typename From, std::enable_if_t<std::is_integral_v<To> && std::is_enum_v<From>, int>>
To SafeCast(From value)
{
return SafeCast<To>(static_cast<std::underlying_type_t<From>>(value));
}
#else
template<typename To, typename From>
To SafeCast(From value)
{
return static_cast<To>(value);
}
#endif
template<typename T>
constexpr auto UnderlyingCast(T value) -> std::underlying_type_t<T>
{

View File

@ -3,12 +3,14 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/RenderElement.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Debug.hpp>
namespace Nz
{
inline RenderElement::RenderElement(BasicRenderElement elementType) :
RenderElement(static_cast<UInt8>(elementType))
RenderElement(SafeCast<UInt8>(elementType))
{
}
@ -22,3 +24,5 @@ namespace Nz
return m_elementType;
}
}
#include <Nazara/Core/DebugOff.hpp>

View File

@ -71,7 +71,7 @@ namespace Nz
std::array<Vector2f, circleVerticesCount> vertices;
Vector2f origin = FromChipmunk(pos);
float r = static_cast<float>(radius);
float r = SafeCast<float>(radius);
RadianAnglef angleBetweenVertices = 2.f * Pi<float> / vertices.size();
for (std::size_t i = 0; i < vertices.size(); ++i)
@ -91,7 +91,7 @@ namespace Nz
{
const auto& callback = *static_cast<const CallbackType*>(userdata);
static std::pair<float, float> sincos = Nz::DegreeAnglef(90.f).GetSinCos();
static std::pair<float, float> sincos = DegreeAnglef(90.f).GetSinCos();
Vector2f from = FromChipmunk(a);
Vector2f to = FromChipmunk(b);
@ -100,7 +100,7 @@ namespace Nz
Vector2f thicknessNormal(sincos.second * normal.x - sincos.first * normal.y,
sincos.first * normal.x + sincos.second * normal.y);
float thickness = static_cast<float>(radius);
float thickness = SafeCast<float>(radius);
std::array<Vector2f, 4> vertices;
vertices[0] = from + thickness * thicknessNormal;
@ -166,14 +166,14 @@ namespace Nz
{
}
Nz::Vector2f BoxCollider2D::ComputeCenterOfMass() const
Vector2f BoxCollider2D::ComputeCenterOfMass() const
{
return m_rect.GetCenter();
}
float BoxCollider2D::ComputeMomentOfInertia(float mass) const
{
return static_cast<float>(cpMomentForBox2(mass, cpBBNew(m_rect.x, m_rect.y, m_rect.x + m_rect.width, m_rect.y + m_rect.height)));
return SafeCast<float>(cpMomentForBox2(mass, cpBBNew(m_rect.x, m_rect.y, m_rect.x + m_rect.width, m_rect.y + m_rect.height)));
}
ColliderType2D BoxCollider2D::GetType() const
@ -195,14 +195,14 @@ namespace Nz
{
}
Nz::Vector2f CircleCollider2D::ComputeCenterOfMass() const
Vector2f CircleCollider2D::ComputeCenterOfMass() const
{
return m_offset;
}
float CircleCollider2D::ComputeMomentOfInertia(float mass) const
{
return static_cast<float>(cpMomentForCircle(mass, 0.f, m_radius, cpv(m_offset.x, m_offset.y)));
return SafeCast<float>(cpMomentForCircle(mass, 0.f, m_radius, cpv(m_offset.x, m_offset.y)));
}
ColliderType2D CircleCollider2D::GetType() const
@ -224,9 +224,9 @@ namespace Nz
{
}
Nz::Vector2f CompoundCollider2D::ComputeCenterOfMass() const
Vector2f CompoundCollider2D::ComputeCenterOfMass() const
{
Nz::Vector2f centerOfMass = Nz::Vector2f::Zero();
Vector2f centerOfMass = Vector2f::Zero();
for (const auto& geom : m_geoms)
centerOfMass += geom->ComputeCenterOfMass();
@ -285,20 +285,20 @@ namespace Nz
m_vertices[i].Set(*vertices++);
}
Nz::Vector2f ConvexCollider2D::ComputeCenterOfMass() const
Vector2f ConvexCollider2D::ComputeCenterOfMass() const
{
static_assert(sizeof(cpVect) == sizeof(Vector2d), "Chipmunk vector is not equivalent to Vector2d");
cpVect center = cpCentroidForPoly(int(m_vertices.size()), reinterpret_cast<const cpVect*>(m_vertices.data()));
return Nz::Vector2f(float(center.x), float(center.y));
return Vector2f(float(center.x), float(center.y));
}
float ConvexCollider2D::ComputeMomentOfInertia(float mass) const
{
static_assert(sizeof(cpVect) == sizeof(Vector2d), "Chipmunk vector is not equivalent to Vector2d");
return static_cast<float>(cpMomentForPoly(mass, int(m_vertices.size()), reinterpret_cast<const cpVect*>(m_vertices.data()), cpv(0.0, 0.0), m_radius));
return SafeCast<float>(cpMomentForPoly(mass, int(m_vertices.size()), reinterpret_cast<const cpVect*>(m_vertices.data()), cpv(0.0, 0.0), m_radius));
}
ColliderType2D ConvexCollider2D::GetType() const
@ -319,9 +319,9 @@ namespace Nz
return ColliderType2D::Null;
}
Nz::Vector2f NullCollider2D::ComputeCenterOfMass() const
Vector2f NullCollider2D::ComputeCenterOfMass() const
{
return Nz::Vector2f::Zero();
return Vector2f::Zero();
}
float NullCollider2D::ComputeMomentOfInertia(float mass) const
@ -336,14 +336,14 @@ namespace Nz
/******************************** SegmentCollider2D *********************************/
Nz::Vector2f SegmentCollider2D::ComputeCenterOfMass() const
Vector2f SegmentCollider2D::ComputeCenterOfMass() const
{
return (m_first + m_second) / 2.f;
}
float SegmentCollider2D::ComputeMomentOfInertia(float mass) const
{
return static_cast<float>(cpMomentForSegment(mass, cpv(m_first.x, m_first.y), cpv(m_second.x, m_second.y), m_thickness));
return SafeCast<float>(cpMomentForSegment(mass, cpv(m_first.x, m_first.y), cpv(m_second.x, m_second.y), m_thickness));
}
ColliderType2D SegmentCollider2D::GetType() const

View File

@ -145,38 +145,38 @@ namespace Nz
std::shared_ptr<StaticMesh> Collider3D::GenerateMesh() const
{
std::vector<Nz::Vector3f> colliderVertices;
std::vector<Nz::UInt16> colliderIndices;
std::vector<Vector3f> colliderVertices;
std::vector<UInt16> colliderIndices;
// Generate a line list
ForEachPolygon([&](const Nz::Vector3f* vertices, std::size_t vertexCount)
ForEachPolygon([&](const Vector3f* vertices, std::size_t vertexCount)
{
Nz::UInt16 firstIndex = colliderVertices.size();
UInt16 firstIndex = SafeCast<UInt16>(colliderVertices.size());
for (std::size_t i = 0; i < vertexCount; ++i)
colliderVertices.push_back(vertices[i]);
for (std::size_t i = 1; i < vertexCount; ++i)
{
colliderIndices.push_back(firstIndex + i - 1);
colliderIndices.push_back(firstIndex + i);
colliderIndices.push_back(SafeCast<UInt16>(firstIndex + i - 1));
colliderIndices.push_back(SafeCast<UInt16>(firstIndex + i));
}
if (vertexCount > 2)
{
colliderIndices.push_back(firstIndex + vertexCount - 1);
colliderIndices.push_back(firstIndex);
colliderIndices.push_back(SafeCast<UInt16>(firstIndex + vertexCount - 1));
colliderIndices.push_back(SafeCast<UInt16>(firstIndex));
}
});
std::shared_ptr<Nz::VertexBuffer> colliderVB = std::make_shared<Nz::VertexBuffer>(Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ), colliderVertices.size(), Nz::DataStorage::Software, 0);
std::shared_ptr<VertexBuffer> colliderVB = std::make_shared<VertexBuffer>(VertexDeclaration::Get(VertexLayout::XYZ), colliderVertices.size(), DataStorage::Software, 0);
colliderVB->Fill(colliderVertices.data(), 0, colliderVertices.size());
std::shared_ptr<Nz::IndexBuffer> colliderIB = std::make_shared<Nz::IndexBuffer>(false, colliderIndices.size(), Nz::DataStorage::Software, 0);
std::shared_ptr<IndexBuffer> colliderIB = std::make_shared<IndexBuffer>(false, colliderIndices.size(), DataStorage::Software, 0);
colliderIB->Fill(colliderIndices.data(), 0, colliderIndices.size());
std::shared_ptr<Nz::StaticMesh> colliderSubMesh = std::make_shared<Nz::StaticMesh>(std::move(colliderVB), std::move(colliderIB));
std::shared_ptr<StaticMesh> colliderSubMesh = std::make_shared<StaticMesh>(std::move(colliderVB), std::move(colliderIB));
colliderSubMesh->GenerateAABB();
colliderSubMesh->SetPrimitiveMode(Nz::PrimitiveMode::LineList);
colliderSubMesh->SetPrimitiveMode(PrimitiveMode::LineList);
return colliderSubMesh;
}