Physics2D: Add DebugDraw method

This commit is contained in:
Jérôme Leclercq 2017-10-13 15:14:37 +02:00
parent b14a9f219a
commit 101783126c
4 changed files with 148 additions and 0 deletions

View File

@ -8,6 +8,7 @@
#define NAZARA_PHYSWORLD2D_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Color.hpp>
#include <Nazara/Core/Signal.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Physics2D/Config.hpp>
@ -30,8 +31,16 @@ namespace Nz
using ContactPostSolveCallback = std::function<void(PhysWorld2D& world, RigidBody2D& bodyA, RigidBody2D& bodyB, void* userdata)>;
using ContactStartCallback = std::function<bool(PhysWorld2D& world, RigidBody2D& bodyA, RigidBody2D& bodyB, void* userdata)>;
using DebugDrawCircleCallback = std::function<void(const Vector2f& origin, float rotation, float radius, Color outlineColor, Color fillColor, void* userdata)>;
using DebugDrawDotCallback = std::function<void(const Vector2f& origin, float radius, Color color, void* userdata)>;
using DebugDrawPolygonCallback = std::function<void(const Vector2f* vertices, std::size_t vertexCount, float radius, Color outlineColor, Color fillColor, void* userdata)>;
using DebugDrawSegmentCallback = std::function<void(const Vector2f& first, const Vector2f& second, Color color, void* userdata)>;
using DebugDrawTickSegmentCallback = std::function<void(const Vector2f& first, const Vector2f& second, float thickness, Color outlineColor, Color fillColor, void* userdata)>;
using DebugDrawGetColorCallback = std::function<Color(RigidBody2D& body, std::size_t shapeIndex, void* userdata)>;
public:
struct Callback;
struct DebugDrawOptions;
struct NearestQueryResult;
struct RaycastHit;
@ -40,6 +49,8 @@ namespace Nz
PhysWorld2D(PhysWorld2D&&) = delete; ///TODO
~PhysWorld2D();
void DebugDraw(const DebugDrawOptions& options, bool drawShapes = true, bool drawConstraints = true, bool drawCollisions = true);
float GetDamping() const;
Vector2f GetGravity() const;
cpSpace* GetHandle() const;
@ -74,6 +85,22 @@ namespace Nz
void* userdata;
};
struct DebugDrawOptions
{
Color constraintColor;
Color collisionPointColor;
Color shapeOutlineColor;
DebugDrawCircleCallback circleCallback;
DebugDrawGetColorCallback colorCallback;
DebugDrawDotCallback dotCallback;
DebugDrawPolygonCallback polygonCallback;
DebugDrawSegmentCallback segmentCallback;
DebugDrawTickSegmentCallback thickSegmentCallback;
void* userdata;
};
struct NearestQueryResult
{
Nz::RigidBody2D* nearestBody;

View File

@ -13,6 +13,7 @@
#include <Nazara/Math/Rect.hpp>
#include <Nazara/Physics2D/Config.hpp>
#include <Nazara/Physics2D/Collider2D.hpp>
#include <limits>
struct cpBody;
@ -44,6 +45,7 @@ namespace Nz
float GetMomentOfInertia() const;
Vector2f GetPosition() const;
float GetRotation() const;
std::size_t GetShapeIndex(cpShape* shape) const;
void* GetUserdata() const;
Vector2f GetVelocity() const;
PhysWorld2D* GetWorld() const;
@ -67,6 +69,8 @@ namespace Nz
NazaraSignal(OnRigidBody2DMove, RigidBody2D* /*oldPointer*/, RigidBody2D* /*newPointer*/);
NazaraSignal(OnRigidBody2DRelease, RigidBody2D* /*rigidBody*/);
static constexpr std::size_t InvalidShapeIndex = std::numeric_limits<std::size_t>::max();
private:
void CopyBodyData(cpBody* body);
cpBody* Create(float mass = 1.f, float moment = 1.f);

View File

@ -3,11 +3,83 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Physics2D/PhysWorld2D.hpp>
#include <Nazara/Core/MemoryHelper.hpp>
#include <chipmunk/chipmunk.h>
#include <Nazara/Physics2D/Debug.hpp>
namespace Nz
{
namespace
{
Color CpDebugColorToColor(cpSpaceDebugColor c)
{
return Color{ static_cast<Nz::UInt8>(c.r * 255.f), static_cast<Nz::UInt8>(c.g * 255.f), static_cast<Nz::UInt8>(c.b * 255.f), static_cast<Nz::UInt8>(c.a * 255.f) };
}
cpSpaceDebugColor ColorToCpDebugColor(Color c)
{
return cpSpaceDebugColor{ c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f };
}
void DrawCircle(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer userdata)
{
auto drawOptions = static_cast<PhysWorld2D::DebugDrawOptions*>(userdata);
if (drawOptions->circleCallback)
drawOptions->circleCallback(Vector2f(float(pos.x), float(pos.y)), float(angle), float(radius), CpDebugColorToColor(outlineColor), CpDebugColorToColor(fillColor), drawOptions->userdata);
};
void DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer userdata)
{
auto drawOptions = static_cast<PhysWorld2D::DebugDrawOptions*>(userdata);
if (drawOptions->dotCallback)
drawOptions->dotCallback(Vector2f(float(pos.x), float(pos.y)), float(size), CpDebugColorToColor(color), drawOptions->userdata);
};
using DebugDrawPolygonCallback = std::function<void(const Vector2f* vertices, std::size_t vertexCount, float radius, Color outlineColor, Color fillColor, void* userdata)>;
void DrawPolygon(int vertexCount, const cpVect* vertices, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer userdata)
{
auto drawOptions = static_cast<PhysWorld2D::DebugDrawOptions*>(userdata);
if (drawOptions->polygonCallback)
{
//TODO: constexpr if to prevent copy/cast if sizeof(cpVect) == sizeof(Vector2f)
StackAllocation stackAlloc = NazaraStackAllocation(vertexCount * sizeof(Vector2f));
Vector2f* vec = static_cast<Vector2f*>(stackAlloc.GetPtr());
for (int i = 0; i < vertexCount; ++i)
vec[i].Set(float(vertices[i].x), float(vertices[i].y));
drawOptions->polygonCallback(vec, vertexCount, float(radius), CpDebugColorToColor(outlineColor), CpDebugColorToColor(fillColor), drawOptions->userdata);
}
};
void DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer userdata)
{
auto drawOptions = static_cast<PhysWorld2D::DebugDrawOptions*>(userdata);
if (drawOptions->segmentCallback)
drawOptions->segmentCallback(Vector2f(float(a.x), float(a.y)), Vector2f(float(b.x), float(b.y)), CpDebugColorToColor(color), drawOptions->userdata);
};
void DrawThickSegment(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer userdata)
{
auto drawOptions = static_cast<PhysWorld2D::DebugDrawOptions*>(userdata);
if (drawOptions->thickSegmentCallback)
drawOptions->thickSegmentCallback(Vector2f(float(a.x), float(a.y)), Vector2f(float(b.x), float(b.y)), float(radius), CpDebugColorToColor(outlineColor), CpDebugColorToColor(fillColor), drawOptions->userdata);
};
cpSpaceDebugColor GetColorForShape(cpShape* shape, cpDataPointer userdata)
{
auto drawOptions = static_cast<PhysWorld2D::DebugDrawOptions*>(userdata);
if (drawOptions->colorCallback)
{
RigidBody2D& rigidBody = *static_cast<RigidBody2D*>(cpShapeGetUserData(shape));
return ColorToCpDebugColor(drawOptions->colorCallback(rigidBody, rigidBody.GetShapeIndex(shape), drawOptions->userdata));
}
else
return cpSpaceDebugColor{255.f, 0.f, 0.f, 255.f};
};
}
PhysWorld2D::PhysWorld2D() :
m_stepSize(0.005f),
m_timestepAccumulator(0.f)
@ -21,6 +93,42 @@ namespace Nz
cpSpaceFree(m_handle);
}
void PhysWorld2D::DebugDraw(const DebugDrawOptions& options, bool drawShapes, bool drawConstraints, bool drawCollisions)
{
auto ColorToCpDebugColor = [](Color c) -> cpSpaceDebugColor
{
return cpSpaceDebugColor{ c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f };
};
cpSpaceDebugDrawOptions drawOptions;
drawOptions.collisionPointColor = ColorToCpDebugColor(options.collisionPointColor);
drawOptions.constraintColor = ColorToCpDebugColor(options.constraintColor);
drawOptions.shapeOutlineColor = ColorToCpDebugColor(options.shapeOutlineColor);
drawOptions.data = const_cast<DebugDrawOptions*>(&options); //< Yeah, I know, shame :bell: but it won't be used for writing anyway
std::underlying_type_t<cpSpaceDebugDrawFlags> drawFlags = 0;
if (drawCollisions)
drawFlags |= CP_SPACE_DEBUG_DRAW_COLLISION_POINTS;
if (drawConstraints)
drawFlags |= CP_SPACE_DEBUG_DRAW_CONSTRAINTS;
if (drawShapes)
drawFlags |= CP_SPACE_DEBUG_DRAW_SHAPES;
drawOptions.flags = static_cast<cpSpaceDebugDrawFlags>(drawFlags);
// Callback trampoline
drawOptions.colorForShape = GetColorForShape;
drawOptions.drawCircle = DrawCircle;
drawOptions.drawDot = DrawDot;
drawOptions.drawFatSegment = DrawThickSegment;
drawOptions.drawPolygon = DrawPolygon;
drawOptions.drawSegment = DrawSegment;
cpSpaceDebugDraw(m_handle, &drawOptions);
}
float PhysWorld2D::GetDamping() const
{
return float(cpSpaceGetDamping(m_handle));

View File

@ -184,6 +184,15 @@ namespace Nz
return FromRadians(static_cast<float>(cpBodyGetAngle(m_handle)));
}
std::size_t RigidBody2D::GetShapeIndex(cpShape* shape) const
{
auto it = std::find(m_shapes.begin(), m_shapes.end(), shape);
if (it == m_shapes.end())
return InvalidShapeIndex;
return std::distance(m_shapes.begin(), it);
}
void* RigidBody2D::GetUserdata() const
{
return m_userData;