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.BindVertexBuffer(0, *cubeMeshGfx->GetVertexBuffer(0));
builder.BindPipeline(*skyboxPipeline); builder.BindPipeline(*skyboxPipeline);
builder.DrawIndexed(static_cast<Nz::UInt32>(cubeMeshGfx->GetIndexCount(0))); builder.DrawIndexed(Nz::SafeCast<Nz::UInt32>(cubeMeshGfx->GetIndexCount(0)));
}); });
forwardPass.SetExecutionCallback([&] 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> void HashCombine(std::size_t& seed, const T& v);
template<typename T> bool IsPowerOfTwo(T value); template<typename T> bool IsPowerOfTwo(T value);
template<typename T> T ReverseBits(T integer); 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> constexpr auto UnderlyingCast(T value) -> std::underlying_type_t<T>;
template<typename T> template<typename T>

View File

@ -13,6 +13,7 @@
#include <Nazara/Core/Stream.hpp> #include <Nazara/Core/Stream.hpp>
#include <cassert> #include <cassert>
#include <climits> #include <climits>
#include <limits>
#include <utility> #include <utility>
#include <Nazara/Core/Debug.hpp> #include <Nazara/Core/Debug.hpp>
@ -291,6 +292,75 @@ namespace Nz
return reversed; 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> template<typename T>
constexpr auto UnderlyingCast(T value) -> std::underlying_type_t<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 // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/RenderElement.hpp> #include <Nazara/Graphics/RenderElement.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/Error.hpp> #include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Debug.hpp>
namespace Nz namespace Nz
{ {
inline RenderElement::RenderElement(BasicRenderElement elementType) : inline RenderElement::RenderElement(BasicRenderElement elementType) :
RenderElement(static_cast<UInt8>(elementType)) RenderElement(SafeCast<UInt8>(elementType))
{ {
} }
@ -22,3 +24,5 @@ namespace Nz
return m_elementType; return m_elementType;
} }
} }
#include <Nazara/Core/DebugOff.hpp>

View File

@ -71,7 +71,7 @@ namespace Nz
std::array<Vector2f, circleVerticesCount> vertices; std::array<Vector2f, circleVerticesCount> vertices;
Vector2f origin = FromChipmunk(pos); Vector2f origin = FromChipmunk(pos);
float r = static_cast<float>(radius); float r = SafeCast<float>(radius);
RadianAnglef angleBetweenVertices = 2.f * Pi<float> / vertices.size(); RadianAnglef angleBetweenVertices = 2.f * Pi<float> / vertices.size();
for (std::size_t i = 0; i < vertices.size(); ++i) for (std::size_t i = 0; i < vertices.size(); ++i)
@ -91,7 +91,7 @@ namespace Nz
{ {
const auto& callback = *static_cast<const CallbackType*>(userdata); 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 from = FromChipmunk(a);
Vector2f to = FromChipmunk(b); Vector2f to = FromChipmunk(b);
@ -100,7 +100,7 @@ namespace Nz
Vector2f thicknessNormal(sincos.second * normal.x - sincos.first * normal.y, Vector2f thicknessNormal(sincos.second * normal.x - sincos.first * normal.y,
sincos.first * normal.x + sincos.second * 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; std::array<Vector2f, 4> vertices;
vertices[0] = from + thickness * thicknessNormal; vertices[0] = from + thickness * thicknessNormal;
@ -166,14 +166,14 @@ namespace Nz
{ {
} }
Nz::Vector2f BoxCollider2D::ComputeCenterOfMass() const Vector2f BoxCollider2D::ComputeCenterOfMass() const
{ {
return m_rect.GetCenter(); return m_rect.GetCenter();
} }
float BoxCollider2D::ComputeMomentOfInertia(float mass) const 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 ColliderType2D BoxCollider2D::GetType() const
@ -195,14 +195,14 @@ namespace Nz
{ {
} }
Nz::Vector2f CircleCollider2D::ComputeCenterOfMass() const Vector2f CircleCollider2D::ComputeCenterOfMass() const
{ {
return m_offset; return m_offset;
} }
float CircleCollider2D::ComputeMomentOfInertia(float mass) const 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 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) for (const auto& geom : m_geoms)
centerOfMass += geom->ComputeCenterOfMass(); centerOfMass += geom->ComputeCenterOfMass();
@ -285,20 +285,20 @@ namespace Nz
m_vertices[i].Set(*vertices++); 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"); 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())); 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 float ConvexCollider2D::ComputeMomentOfInertia(float mass) const
{ {
static_assert(sizeof(cpVect) == sizeof(Vector2d), "Chipmunk vector is not equivalent to Vector2d"); 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 ColliderType2D ConvexCollider2D::GetType() const
@ -319,9 +319,9 @@ namespace Nz
return ColliderType2D::Null; 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 float NullCollider2D::ComputeMomentOfInertia(float mass) const
@ -336,14 +336,14 @@ namespace Nz
/******************************** SegmentCollider2D *********************************/ /******************************** SegmentCollider2D *********************************/
Nz::Vector2f SegmentCollider2D::ComputeCenterOfMass() const Vector2f SegmentCollider2D::ComputeCenterOfMass() const
{ {
return (m_first + m_second) / 2.f; return (m_first + m_second) / 2.f;
} }
float SegmentCollider2D::ComputeMomentOfInertia(float mass) const 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 ColliderType2D SegmentCollider2D::GetType() const

View File

@ -145,38 +145,38 @@ namespace Nz
std::shared_ptr<StaticMesh> Collider3D::GenerateMesh() const std::shared_ptr<StaticMesh> Collider3D::GenerateMesh() const
{ {
std::vector<Nz::Vector3f> colliderVertices; std::vector<Vector3f> colliderVertices;
std::vector<Nz::UInt16> colliderIndices; std::vector<UInt16> colliderIndices;
// Generate a line list // 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) for (std::size_t i = 0; i < vertexCount; ++i)
colliderVertices.push_back(vertices[i]); colliderVertices.push_back(vertices[i]);
for (std::size_t i = 1; i < vertexCount; ++i) for (std::size_t i = 1; i < vertexCount; ++i)
{ {
colliderIndices.push_back(firstIndex + i - 1); colliderIndices.push_back(SafeCast<UInt16>(firstIndex + i - 1));
colliderIndices.push_back(firstIndex + i); colliderIndices.push_back(SafeCast<UInt16>(firstIndex + i));
} }
if (vertexCount > 2) if (vertexCount > 2)
{ {
colliderIndices.push_back(firstIndex + vertexCount - 1); colliderIndices.push_back(SafeCast<UInt16>(firstIndex + vertexCount - 1));
colliderIndices.push_back(firstIndex); 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()); 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()); 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->GenerateAABB();
colliderSubMesh->SetPrimitiveMode(Nz::PrimitiveMode::LineList); colliderSubMesh->SetPrimitiveMode(PrimitiveMode::LineList);
return colliderSubMesh; return colliderSubMesh;
} }