2020-02-19 11:50:05 +08:00
|
|
|
// Copyright (c) 2018-2019 Kiwano - Nomango
|
|
|
|
|
//
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
|
//
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
|
//
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
|
// THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
#include <kiwano-physics/PhysicWorld.h>
|
|
|
|
|
#include <kiwano-physics/ContactEvent.h>
|
|
|
|
|
|
|
|
|
|
namespace kiwano
|
|
|
|
|
{
|
|
|
|
|
namespace physics
|
|
|
|
|
{
|
|
|
|
|
|
2020-02-23 14:56:14 +08:00
|
|
|
class PhysicWorld::DebugDrawer : public b2Draw
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DebugDrawer(const Size& size)
|
|
|
|
|
{
|
|
|
|
|
canvas_ = Canvas::Create(size);
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_ = canvas_->GetContext2D();
|
2020-02-23 14:56:14 +08:00
|
|
|
|
|
|
|
|
fill_brush_ = Brush::Create(Color::White);
|
|
|
|
|
line_brush_ = Brush::Create(Color::White);
|
|
|
|
|
|
|
|
|
|
b2Draw::SetFlags(b2Draw::e_shapeBit | b2Draw::e_jointBit | b2Draw::e_jointBit | b2Draw::e_centerOfMassBit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BeginDraw()
|
|
|
|
|
{
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->BeginDraw();
|
|
|
|
|
ctx_->Clear();
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EndDraw()
|
|
|
|
|
{
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->EndDraw();
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Render(RenderContext& ctx)
|
|
|
|
|
{
|
|
|
|
|
canvas_->OnRender(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetFillColor(const b2Color& color)
|
|
|
|
|
{
|
|
|
|
|
fill_brush_->SetColor(reinterpret_cast<const Color&>(color));
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->SetCurrentBrush(fill_brush_);
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetLineColor(const b2Color& color)
|
|
|
|
|
{
|
|
|
|
|
line_brush_->SetColor(reinterpret_cast<const Color&>(color));
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->SetCurrentBrush(line_brush_);
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override
|
|
|
|
|
{
|
|
|
|
|
SetLineColor(color);
|
|
|
|
|
|
|
|
|
|
b2Vec2 p1 = vertices[vertexCount - 1];
|
|
|
|
|
for (int32 i = 0; i < vertexCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
b2Vec2 p2 = vertices[i];
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->DrawLine(global::WorldToLocal(p1), global::WorldToLocal(p2));
|
2020-02-23 14:56:14 +08:00
|
|
|
p1 = p2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override
|
|
|
|
|
{
|
|
|
|
|
ShapeMaker maker;
|
|
|
|
|
maker.BeginPath(global::WorldToLocal(vertices[0]));
|
|
|
|
|
for (int32 i = 1; i < vertexCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
maker.AddLine(global::WorldToLocal(vertices[i]));
|
|
|
|
|
}
|
|
|
|
|
maker.EndPath(true);
|
|
|
|
|
|
|
|
|
|
SetFillColor(color);
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->FillShape(*maker.GetShape());
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color) override
|
|
|
|
|
{
|
|
|
|
|
SetLineColor(color);
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->DrawCircle(global::WorldToLocal(center), global::WorldToLocal(radius));
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color) override
|
|
|
|
|
{
|
|
|
|
|
SetFillColor(color);
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->FillCircle(global::WorldToLocal(center), global::WorldToLocal(radius));
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) override
|
|
|
|
|
{
|
|
|
|
|
SetLineColor(color);
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->DrawLine(global::WorldToLocal(p1), global::WorldToLocal(p2));
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawTransform(const b2Transform& xf) override
|
|
|
|
|
{
|
|
|
|
|
const float k_axisScale = 0.4f;
|
|
|
|
|
|
|
|
|
|
b2Color red(1.0f, 0.0f, 0.0f);
|
|
|
|
|
b2Color green(0.0f, 1.0f, 0.0f);
|
|
|
|
|
b2Vec2 p1 = xf.p, p2;
|
|
|
|
|
|
|
|
|
|
p2 = p1 + k_axisScale * xf.q.GetXAxis();
|
|
|
|
|
|
|
|
|
|
SetLineColor(red);
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->DrawLine(global::WorldToLocal(p1), global::WorldToLocal(p2));
|
2020-02-23 14:56:14 +08:00
|
|
|
|
|
|
|
|
p2 = p1 + k_axisScale * xf.q.GetYAxis();
|
|
|
|
|
|
|
|
|
|
SetLineColor(green);
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->DrawLine(global::WorldToLocal(p1), global::WorldToLocal(p2));
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawPoint(const b2Vec2& p, float32 size, const b2Color& color) override
|
|
|
|
|
{
|
|
|
|
|
SetFillColor(color);
|
2020-04-26 21:20:58 +08:00
|
|
|
ctx_->FillCircle(global::WorldToLocal(p), global::WorldToLocal(size));
|
2020-02-23 14:56:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2020-04-26 21:20:58 +08:00
|
|
|
CanvasPtr canvas_;
|
|
|
|
|
RenderContextPtr ctx_;
|
|
|
|
|
BrushPtr fill_brush_;
|
|
|
|
|
BrushPtr line_brush_;
|
2020-02-23 14:56:14 +08:00
|
|
|
};
|
|
|
|
|
|
2020-02-19 11:50:05 +08:00
|
|
|
class DestructionListener : public b2DestructionListener
|
|
|
|
|
{
|
|
|
|
|
Function<void(b2Joint*)> joint_destruction_callback_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
DestructionListener(Function<void(b2Joint*)> callback)
|
|
|
|
|
: joint_destruction_callback_(callback)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SayGoodbye(b2Joint* joint) override
|
|
|
|
|
{
|
|
|
|
|
joint_destruction_callback_(joint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SayGoodbye(b2Fixture* fixture) override {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ContactListener : public b2ContactListener
|
|
|
|
|
{
|
|
|
|
|
Function<void(Event*)> dispatcher_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
ContactListener(Function<void(Event*)> dispatcher)
|
|
|
|
|
: dispatcher_(dispatcher)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BeginContact(b2Contact* b2contact) override
|
|
|
|
|
{
|
|
|
|
|
Contact contact;
|
|
|
|
|
contact.SetB2Contact(b2contact);
|
|
|
|
|
|
|
|
|
|
ContactBeginEventPtr evt = new ContactBeginEvent(contact);
|
|
|
|
|
dispatcher_(evt.Get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EndContact(b2Contact* b2contact) override
|
|
|
|
|
{
|
|
|
|
|
PhysicBody* body_a = static_cast<PhysicBody*>(b2contact->GetFixtureA()->GetBody()->GetUserData());
|
|
|
|
|
PhysicBody* body_b = static_cast<PhysicBody*>(b2contact->GetFixtureB()->GetBody()->GetUserData());
|
|
|
|
|
if (!body_a || !body_b || !body_a->GetBoundActor() || !body_b->GetBoundActor())
|
|
|
|
|
{
|
|
|
|
|
// Don't dispatch contact event after the body has been detached
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Contact contact;
|
|
|
|
|
contact.SetB2Contact(b2contact);
|
|
|
|
|
|
|
|
|
|
ContactEndEventPtr evt = new ContactEndEvent(contact);
|
|
|
|
|
dispatcher_(evt.Get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override
|
|
|
|
|
{
|
|
|
|
|
KGE_NOT_USED(contact);
|
|
|
|
|
KGE_NOT_USED(oldManifold);
|
|
|
|
|
}
|
|
|
|
|
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override
|
|
|
|
|
{
|
|
|
|
|
KGE_NOT_USED(contact);
|
|
|
|
|
KGE_NOT_USED(impulse);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PhysicWorldPtr PhysicWorld::Create()
|
|
|
|
|
{
|
|
|
|
|
PhysicWorldPtr ptr = new (std::nothrow) PhysicWorld;
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PhysicWorldPtr PhysicWorld::Create(const Vec2& gravity)
|
|
|
|
|
{
|
|
|
|
|
PhysicWorldPtr ptr = new (std::nothrow) PhysicWorld;
|
|
|
|
|
if (ptr)
|
|
|
|
|
{
|
|
|
|
|
ptr->SetGravity(gravity);
|
|
|
|
|
}
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PhysicWorld::PhysicWorld()
|
|
|
|
|
: world_(b2Vec2(0, 10.0f))
|
|
|
|
|
, vel_iter_(6)
|
|
|
|
|
, pos_iter_(2)
|
|
|
|
|
{
|
2020-02-22 17:24:31 +08:00
|
|
|
SetName("KGE_PHYSIC_WORLD");
|
|
|
|
|
|
2020-02-19 11:50:05 +08:00
|
|
|
destroy_listener_ = std::make_unique<DestructionListener>(Closure(this, &PhysicWorld::JointRemoved));
|
|
|
|
|
world_.SetDestructionListener(destroy_listener_.get());
|
|
|
|
|
|
|
|
|
|
contact_listener_ = std::make_unique<ContactListener>(Closure(this, &PhysicWorld::DispatchEvent));
|
|
|
|
|
world_.SetContactListener(contact_listener_.get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PhysicWorld::~PhysicWorld()
|
|
|
|
|
{
|
|
|
|
|
world_.SetDestructionListener(nullptr);
|
|
|
|
|
world_.SetContactListener(nullptr);
|
|
|
|
|
|
|
|
|
|
// Make sure b2World was destroyed after b2Body
|
|
|
|
|
RemoveAllJoints();
|
|
|
|
|
RemoveAllBodies();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::AddBody(PhysicBodyPtr body)
|
|
|
|
|
{
|
|
|
|
|
if (body)
|
|
|
|
|
{
|
|
|
|
|
bodies_.push_back(body);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::RemoveBody(PhysicBodyPtr body)
|
|
|
|
|
{
|
|
|
|
|
if (body)
|
|
|
|
|
{
|
|
|
|
|
auto iter = std::find(bodies_.begin(), bodies_.end(), body);
|
|
|
|
|
if (iter != bodies_.end())
|
|
|
|
|
{
|
|
|
|
|
body->Destroy();
|
|
|
|
|
bodies_.erase(iter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::RemoveAllBodies()
|
|
|
|
|
{
|
|
|
|
|
for (auto body : bodies_)
|
|
|
|
|
{
|
|
|
|
|
body->Destroy();
|
|
|
|
|
}
|
|
|
|
|
bodies_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const List<PhysicBodyPtr>& PhysicWorld::GetAllBodies() const
|
|
|
|
|
{
|
|
|
|
|
return bodies_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::AddJoint(JointPtr joint)
|
|
|
|
|
{
|
|
|
|
|
if (joint)
|
|
|
|
|
{
|
|
|
|
|
bool success = joint->Init(this);
|
|
|
|
|
KGE_ASSERT(success);
|
|
|
|
|
|
|
|
|
|
joints_.push_back(joint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::RemoveJoint(JointPtr joint)
|
|
|
|
|
{
|
|
|
|
|
if (joint)
|
|
|
|
|
{
|
|
|
|
|
auto iter = std::find(joints_.begin(), joints_.end(), joint);
|
|
|
|
|
if (iter != joints_.end())
|
|
|
|
|
{
|
|
|
|
|
joint->Destroy();
|
|
|
|
|
joints_.erase(iter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::RemoveAllJoints()
|
|
|
|
|
{
|
|
|
|
|
for (auto joint : joints_)
|
|
|
|
|
{
|
|
|
|
|
joint->Destroy();
|
|
|
|
|
}
|
|
|
|
|
joints_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const List<JointPtr>& PhysicWorld::GetAllJoints() const
|
|
|
|
|
{
|
|
|
|
|
return joints_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b2World* PhysicWorld::GetB2World()
|
|
|
|
|
{
|
|
|
|
|
return &world_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const b2World* PhysicWorld::GetB2World() const
|
|
|
|
|
{
|
|
|
|
|
return &world_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vec2 PhysicWorld::GetGravity() const
|
|
|
|
|
{
|
|
|
|
|
b2Vec2 g = world_.GetGravity();
|
|
|
|
|
return Vec2(g.x, g.y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::SetGravity(Vec2 gravity)
|
|
|
|
|
{
|
|
|
|
|
world_.SetGravity(b2Vec2(gravity.x, gravity.y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ContactList PhysicWorld::GetContactList()
|
|
|
|
|
{
|
|
|
|
|
Contact contact;
|
|
|
|
|
contact.SetB2Contact(world_.GetContactList());
|
|
|
|
|
return ContactList(contact);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 17:24:31 +08:00
|
|
|
void PhysicWorld::InitComponent(Actor* actor)
|
|
|
|
|
{
|
|
|
|
|
Component::InitComponent(actor);
|
|
|
|
|
|
|
|
|
|
// Update body status
|
|
|
|
|
Actor* world_actor = GetBoundActor();
|
|
|
|
|
BeforeSimulation(world_actor, Matrix3x2(), 0.0f);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 11:50:05 +08:00
|
|
|
void PhysicWorld::OnUpdate(Duration dt)
|
|
|
|
|
{
|
2020-02-22 17:24:31 +08:00
|
|
|
Actor* world_actor = GetBoundActor();
|
|
|
|
|
|
|
|
|
|
BeforeSimulation(world_actor, Matrix3x2(), 0.0f);
|
2020-02-19 11:50:05 +08:00
|
|
|
|
2020-02-22 17:24:31 +08:00
|
|
|
// Update physic world
|
2020-02-19 11:50:05 +08:00
|
|
|
world_.Step(dt.Seconds(), vel_iter_, pos_iter_);
|
|
|
|
|
|
2020-02-22 17:24:31 +08:00
|
|
|
AfterSimulation(world_actor, Matrix3x2(), 0.0f);
|
2020-02-19 11:50:05 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-23 14:56:14 +08:00
|
|
|
void PhysicWorld::OnRender(RenderContext& ctx)
|
|
|
|
|
{
|
|
|
|
|
if (drawer_)
|
|
|
|
|
{
|
|
|
|
|
drawer_->BeginDraw();
|
|
|
|
|
world_.DrawDebugData();
|
|
|
|
|
drawer_->EndDraw();
|
|
|
|
|
|
|
|
|
|
drawer_->Render(ctx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 11:50:05 +08:00
|
|
|
void PhysicWorld::DispatchEvent(Event* evt)
|
|
|
|
|
{
|
|
|
|
|
Actor* actor = GetBoundActor();
|
|
|
|
|
if (actor)
|
|
|
|
|
{
|
|
|
|
|
actor->DispatchEvent(evt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::JointRemoved(b2Joint* b2joint)
|
|
|
|
|
{
|
|
|
|
|
Joint* joint = static_cast<Joint*>(b2joint->GetUserData());
|
|
|
|
|
if (joint)
|
|
|
|
|
{
|
|
|
|
|
auto iter = std::find(joints_.begin(), joints_.end(), joint);
|
|
|
|
|
if (iter != joints_.end())
|
|
|
|
|
{
|
|
|
|
|
joints_.erase(iter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 17:24:31 +08:00
|
|
|
void PhysicWorld::BeforeSimulation(Actor* parent, const Matrix3x2& parent_to_world, float parent_rotation)
|
|
|
|
|
{
|
|
|
|
|
for (auto child : parent->GetAllChildren())
|
|
|
|
|
{
|
|
|
|
|
Matrix3x2 child_to_world = child->GetTransformMatrixToParent() * parent_to_world;
|
|
|
|
|
|
2020-06-22 00:57:43 +08:00
|
|
|
PhysicBody* body = child->GetPhysicBody();
|
2020-02-22 17:24:31 +08:00
|
|
|
if (body)
|
|
|
|
|
{
|
|
|
|
|
body->BeforeSimulation(child.Get(), parent_to_world, child_to_world, parent_rotation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float rotation = parent_rotation + child->GetRotation();
|
|
|
|
|
BeforeSimulation(child.Get(), child_to_world, rotation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PhysicWorld::AfterSimulation(Actor* parent, const Matrix3x2& parent_to_world, float parent_rotation)
|
|
|
|
|
{
|
|
|
|
|
for (auto child : parent->GetAllChildren())
|
|
|
|
|
{
|
2020-06-22 00:57:43 +08:00
|
|
|
PhysicBody* body = child->GetPhysicBody();
|
2020-02-22 17:24:31 +08:00
|
|
|
if (body)
|
|
|
|
|
{
|
|
|
|
|
body->AfterSimulation(child.Get(), parent_to_world, parent_rotation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Matrix3x2 child_to_world = child->GetTransformMatrixToParent() * parent_to_world;
|
|
|
|
|
float rotation = parent_rotation + child->GetRotation();
|
|
|
|
|
AfterSimulation(child.Get(), child_to_world, rotation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-23 14:56:14 +08:00
|
|
|
void PhysicWorld::ShowDebugInfo(bool show)
|
|
|
|
|
{
|
|
|
|
|
if (show)
|
|
|
|
|
{
|
|
|
|
|
if (!drawer_)
|
|
|
|
|
{
|
|
|
|
|
Size size = Renderer::GetInstance().GetOutputSize();
|
|
|
|
|
drawer_ = std::unique_ptr<DebugDrawer>(new DebugDrawer(size));
|
|
|
|
|
|
|
|
|
|
world_.SetDebugDraw(drawer_.get());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
drawer_.reset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 11:50:05 +08:00
|
|
|
} // namespace physics
|
|
|
|
|
} // namespace kiwano
|