mirror of https://github.com/axmolengine/axmol.git
507 lines
15 KiB
C++
507 lines
15 KiB
C++
/*
|
|
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
|
|
#include "tests/test.h"
|
|
#include "tests/settings.h"
|
|
|
|
#include "extensions/cocos-ext.h"
|
|
#include "axmol.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
USING_NS_AX;
|
|
USING_NS_AX_EXT;
|
|
|
|
#if defined(AX_PLATFORM_PC)
|
|
extern ax::Label* labelDebugDraw;
|
|
#endif
|
|
|
|
void DestructionListener::SayGoodbye(b2Joint* joint)
|
|
{
|
|
if (test->m_mouseJoint == joint)
|
|
{
|
|
test->m_mouseJoint = NULL;
|
|
}
|
|
else
|
|
{
|
|
test->JointDestroyed(joint);
|
|
}
|
|
}
|
|
|
|
Test::Test()
|
|
{
|
|
b2Vec2 gravity;
|
|
gravity.Set(0.0f, -10.0f);
|
|
m_world = new b2World(gravity);
|
|
m_bomb = NULL;
|
|
m_textLine = 30;
|
|
m_textIncrement = 13;
|
|
m_mouseJoint = NULL;
|
|
m_pointCount = 0;
|
|
|
|
m_destructionListener.test = this;
|
|
m_world->SetDestructionListener(&m_destructionListener);
|
|
m_world->SetContactListener(this);
|
|
m_world->SetDebugDraw(&g_debugDraw);
|
|
|
|
m_bombSpawning = false;
|
|
|
|
m_stepCount = 0;
|
|
|
|
b2BodyDef bodyDef;
|
|
m_groundBody = m_world->CreateBody(&bodyDef);
|
|
|
|
memset(&m_maxProfile, 0, sizeof(b2Profile));
|
|
memset(&m_totalProfile, 0, sizeof(b2Profile));
|
|
}
|
|
|
|
Test::~Test()
|
|
{
|
|
// By deleting the world, we delete the bomb, mouse joint, etc.
|
|
delete m_world;
|
|
m_world = NULL;
|
|
}
|
|
|
|
void Test::PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
|
|
{
|
|
const b2Manifold* manifold = contact->GetManifold();
|
|
|
|
if (manifold->pointCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
b2Fixture* fixtureA = contact->GetFixtureA();
|
|
b2Fixture* fixtureB = contact->GetFixtureB();
|
|
|
|
b2PointState state1[b2_maxManifoldPoints], state2[b2_maxManifoldPoints];
|
|
b2GetPointStates(state1, state2, oldManifold, manifold);
|
|
|
|
b2WorldManifold worldManifold;
|
|
contact->GetWorldManifold(&worldManifold);
|
|
|
|
for (int32 i = 0; i < manifold->pointCount && m_pointCount < k_maxContactPoints; ++i)
|
|
{
|
|
ContactPoint* cp = m_points + m_pointCount;
|
|
cp->fixtureA = fixtureA;
|
|
cp->fixtureB = fixtureB;
|
|
cp->position = worldManifold.points[i];
|
|
cp->normal = worldManifold.normal;
|
|
cp->state = state2[i];
|
|
cp->normalImpulse = manifold->points[i].normalImpulse;
|
|
cp->tangentImpulse = manifold->points[i].tangentImpulse;
|
|
cp->separation = worldManifold.separations[i];
|
|
++m_pointCount;
|
|
}
|
|
}
|
|
|
|
void Test::DrawTitle(const char* string)
|
|
{
|
|
DrawString(5, 5, string);
|
|
m_textLine = int32(26.0f);
|
|
}
|
|
|
|
class QueryCallback : public b2QueryCallback
|
|
{
|
|
public:
|
|
QueryCallback(const b2Vec2& point)
|
|
{
|
|
m_point = point;
|
|
m_fixture = NULL;
|
|
}
|
|
|
|
bool ReportFixture(b2Fixture* fixture) override
|
|
{
|
|
b2Body* body = fixture->GetBody();
|
|
if (body->GetType() == b2_dynamicBody)
|
|
{
|
|
bool inside = fixture->TestPoint(m_point);
|
|
if (inside)
|
|
{
|
|
m_fixture = fixture;
|
|
|
|
// We are done, terminate the query.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Continue the query.
|
|
return true;
|
|
}
|
|
|
|
b2Vec2 m_point;
|
|
b2Fixture* m_fixture;
|
|
};
|
|
|
|
bool Test::MouseDown(const b2Vec2& p)
|
|
{
|
|
m_mouseWorld = p;
|
|
|
|
if (m_mouseJoint != NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Make a small box.
|
|
b2AABB aabb;
|
|
b2Vec2 d;
|
|
d.Set(0.001f, 0.001f);
|
|
aabb.lowerBound = p - d;
|
|
aabb.upperBound = p + d;
|
|
|
|
// Query the world for overlapping shapes.
|
|
QueryCallback callback(p);
|
|
m_world->QueryAABB(&callback, aabb);
|
|
|
|
if (callback.m_fixture)
|
|
{
|
|
float frequencyHz = 5.0f;
|
|
float dampingRatio = 0.7f;
|
|
|
|
b2Body* body = callback.m_fixture->GetBody();
|
|
b2MouseJointDef jd;
|
|
jd.bodyA = m_groundBody;
|
|
jd.bodyB = body;
|
|
jd.target = p;
|
|
jd.maxForce = 1000.0f * body->GetMass();
|
|
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
|
|
|
|
m_mouseJoint = (b2MouseJoint*)m_world->CreateJoint(&jd);
|
|
body->SetAwake(true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Test::SpawnBomb(const b2Vec2& worldPt)
|
|
{
|
|
m_bombSpawnPoint = worldPt;
|
|
m_bombSpawning = true;
|
|
}
|
|
|
|
void Test::CompleteBombSpawn(const b2Vec2& p)
|
|
{
|
|
if (m_bombSpawning == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const float multiplier = 30.0f;
|
|
b2Vec2 vel = m_bombSpawnPoint - p;
|
|
vel *= multiplier;
|
|
LaunchBomb(m_bombSpawnPoint, vel);
|
|
m_bombSpawning = false;
|
|
}
|
|
|
|
void Test::ShiftMouseDown(const b2Vec2& p)
|
|
{
|
|
m_mouseWorld = p;
|
|
|
|
if (m_mouseJoint != NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SpawnBomb(p);
|
|
}
|
|
|
|
void Test::MouseUp(const b2Vec2& p)
|
|
{
|
|
if (m_mouseJoint)
|
|
{
|
|
m_world->DestroyJoint(m_mouseJoint);
|
|
m_mouseJoint = NULL;
|
|
}
|
|
|
|
if (m_bombSpawning)
|
|
{
|
|
CompleteBombSpawn(p);
|
|
}
|
|
}
|
|
|
|
void Test::MouseMove(const b2Vec2& p)
|
|
{
|
|
m_mouseWorld = p;
|
|
|
|
if (m_mouseJoint)
|
|
{
|
|
m_mouseJoint->SetTarget(p);
|
|
}
|
|
}
|
|
|
|
void Test::LaunchBomb()
|
|
{
|
|
b2Vec2 p(RandomFloat(-15.0f, 15.0f), 30.0f);
|
|
b2Vec2 v = -5.0f * p;
|
|
LaunchBomb(p, v);
|
|
}
|
|
|
|
void Test::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity)
|
|
{
|
|
if (m_bomb)
|
|
{
|
|
m_world->DestroyBody(m_bomb);
|
|
m_bomb = NULL;
|
|
}
|
|
|
|
b2BodyDef bd;
|
|
bd.type = b2_dynamicBody;
|
|
bd.position = position;
|
|
bd.bullet = true;
|
|
m_bomb = m_world->CreateBody(&bd);
|
|
m_bomb->SetLinearVelocity(velocity);
|
|
|
|
b2CircleShape circle;
|
|
circle.m_radius = 0.3f;
|
|
|
|
b2FixtureDef fd;
|
|
fd.shape = &circle;
|
|
fd.density = 20.0f;
|
|
fd.restitution = 0.0f;
|
|
|
|
b2Vec2 minV = position - b2Vec2(0.3f, 0.3f);
|
|
b2Vec2 maxV = position + b2Vec2(0.3f, 0.3f);
|
|
|
|
b2AABB aabb;
|
|
aabb.lowerBound = minV;
|
|
aabb.upperBound = maxV;
|
|
|
|
m_bomb->CreateFixture(&fd);
|
|
}
|
|
|
|
void Test::Step(Settings& settings)
|
|
{
|
|
float timeStep = settings.m_hertz > 0.0f ? 1.0f / settings.m_hertz : float(0.0f);
|
|
|
|
if (settings.m_pause)
|
|
{
|
|
if (settings.m_singleStep)
|
|
{
|
|
settings.m_singleStep = 0;
|
|
}
|
|
else
|
|
{
|
|
timeStep = 0.0f;
|
|
}
|
|
|
|
DrawString(5, m_textLine, "****PAUSED****");
|
|
}
|
|
|
|
uint32 flags = 0;
|
|
flags += settings.m_drawShapes * b2Draw::e_shapeBit;
|
|
flags += settings.m_drawJoints * b2Draw::e_jointBit;
|
|
flags += settings.m_drawAABBs * b2Draw::e_aabbBit;
|
|
flags += settings.m_drawCOMs * b2Draw::e_centerOfMassBit;
|
|
g_debugDraw.SetFlags(flags);
|
|
|
|
m_world->SetAllowSleeping(settings.m_enableSleep);
|
|
m_world->SetWarmStarting(settings.m_enableWarmStarting);
|
|
m_world->SetContinuousPhysics(settings.m_enableContinuous);
|
|
m_world->SetSubStepping(settings.m_enableSubStepping);
|
|
|
|
m_pointCount = 0;
|
|
|
|
m_world->Step(timeStep, settings.m_velocityIterations, settings.m_positionIterations);
|
|
|
|
m_world->DebugDraw();
|
|
|
|
if (timeStep > 0.0f)
|
|
{
|
|
++m_stepCount;
|
|
}
|
|
|
|
if (settings.m_drawStats)
|
|
{
|
|
int32 bodyCount = m_world->GetBodyCount();
|
|
int32 contactCount = m_world->GetContactCount();
|
|
int32 jointCount = m_world->GetJointCount();
|
|
DrawString(5, m_textLine, "bodies/contacts/joints = %d/%d/%d", bodyCount, contactCount, jointCount);
|
|
|
|
int32 proxyCount = m_world->GetProxyCount();
|
|
int32 height = m_world->GetTreeHeight();
|
|
int32 balance = m_world->GetTreeBalance();
|
|
float quality = m_world->GetTreeQuality();
|
|
DrawString(5, m_textLine, "proxies/height/balance/quality = %d/%d/%d/%g", proxyCount, height, balance, quality);
|
|
}
|
|
|
|
// Track maximum profile times
|
|
{
|
|
const b2Profile& p = m_world->GetProfile();
|
|
m_maxProfile.step = b2Max(m_maxProfile.step, p.step);
|
|
m_maxProfile.collide = b2Max(m_maxProfile.collide, p.collide);
|
|
m_maxProfile.solve = b2Max(m_maxProfile.solve, p.solve);
|
|
m_maxProfile.solveInit = b2Max(m_maxProfile.solveInit, p.solveInit);
|
|
m_maxProfile.solveVelocity = b2Max(m_maxProfile.solveVelocity, p.solveVelocity);
|
|
m_maxProfile.solvePosition = b2Max(m_maxProfile.solvePosition, p.solvePosition);
|
|
m_maxProfile.solveTOI = b2Max(m_maxProfile.solveTOI, p.solveTOI);
|
|
m_maxProfile.broadphase = b2Max(m_maxProfile.broadphase, p.broadphase);
|
|
|
|
m_totalProfile.step += p.step;
|
|
m_totalProfile.collide += p.collide;
|
|
m_totalProfile.solve += p.solve;
|
|
m_totalProfile.solveInit += p.solveInit;
|
|
m_totalProfile.solveVelocity += p.solveVelocity;
|
|
m_totalProfile.solvePosition += p.solvePosition;
|
|
m_totalProfile.solveTOI += p.solveTOI;
|
|
m_totalProfile.broadphase += p.broadphase;
|
|
}
|
|
|
|
if (settings.m_drawProfile)
|
|
{
|
|
const b2Profile& p = m_world->GetProfile();
|
|
|
|
b2Profile aveProfile;
|
|
memset(&aveProfile, 0, sizeof(b2Profile));
|
|
if (m_stepCount > 0)
|
|
{
|
|
float scale = 1.0f / m_stepCount;
|
|
aveProfile.step = scale * m_totalProfile.step;
|
|
aveProfile.collide = scale * m_totalProfile.collide;
|
|
aveProfile.solve = scale * m_totalProfile.solve;
|
|
aveProfile.solveInit = scale * m_totalProfile.solveInit;
|
|
aveProfile.solveVelocity = scale * m_totalProfile.solveVelocity;
|
|
aveProfile.solvePosition = scale * m_totalProfile.solvePosition;
|
|
aveProfile.solveTOI = scale * m_totalProfile.solveTOI;
|
|
aveProfile.broadphase = scale * m_totalProfile.broadphase;
|
|
}
|
|
|
|
DrawString(5, m_textLine, "step [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.step, aveProfile.step,
|
|
m_maxProfile.step);
|
|
DrawString(5, m_textLine, "collide [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.collide, aveProfile.collide,
|
|
m_maxProfile.collide);
|
|
DrawString(5, m_textLine, "solve [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solve, aveProfile.solve,
|
|
m_maxProfile.solve);
|
|
DrawString(5, m_textLine, "solve init [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveInit, aveProfile.solveInit,
|
|
m_maxProfile.solveInit);
|
|
DrawString(5, m_textLine, "solve velocity [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveVelocity,
|
|
aveProfile.solveVelocity, m_maxProfile.solveVelocity);
|
|
DrawString(5, m_textLine, "solve position [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solvePosition,
|
|
aveProfile.solvePosition, m_maxProfile.solvePosition);
|
|
DrawString(5, m_textLine, "solveTOI [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveTOI, aveProfile.solveTOI,
|
|
m_maxProfile.solveTOI);
|
|
DrawString(5, m_textLine, "broad-phase [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.broadphase,
|
|
aveProfile.broadphase, m_maxProfile.broadphase);
|
|
}
|
|
|
|
if (m_bombSpawning)
|
|
{
|
|
b2Color c;
|
|
c.Set(0.0f, 0.0f, 1.0f);
|
|
g_debugDraw.DrawPoint(m_bombSpawnPoint, 4.0f, c);
|
|
|
|
c.Set(0.8f, 0.8f, 0.8f);
|
|
g_debugDraw.DrawSegment(m_mouseWorld, m_bombSpawnPoint, c);
|
|
}
|
|
|
|
if (settings.m_drawContactPoints)
|
|
{
|
|
const float k_impulseScale = 0.1f;
|
|
const float k_axisScale = 0.3f;
|
|
|
|
for (int32 i = 0; i < m_pointCount; ++i)
|
|
{
|
|
ContactPoint* point = m_points + i;
|
|
|
|
if (point->state == b2_addState)
|
|
{
|
|
// Add
|
|
g_debugDraw.DrawPoint(point->position, 10.0f, b2Color(0.3f, 0.95f, 0.3f));
|
|
}
|
|
else if (point->state == b2_persistState)
|
|
{
|
|
// Persist
|
|
g_debugDraw.DrawPoint(point->position, 5.0f, b2Color(0.3f, 0.3f, 0.95f));
|
|
}
|
|
|
|
if (settings.m_drawContactNormals == 1)
|
|
{
|
|
b2Vec2 p1 = point->position;
|
|
b2Vec2 p2 = p1 + k_axisScale * point->normal;
|
|
g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.9f));
|
|
}
|
|
else if (settings.m_drawContactImpulse == 1)
|
|
{
|
|
b2Vec2 p1 = point->position;
|
|
b2Vec2 p2 = p1 + k_impulseScale * point->normalImpulse * point->normal;
|
|
g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
|
|
}
|
|
|
|
if (settings.m_drawFrictionImpulse == 1)
|
|
{
|
|
b2Vec2 tangent = b2Cross(point->normal, 1.0f);
|
|
b2Vec2 p1 = point->position;
|
|
b2Vec2 p2 = p1 + k_impulseScale * point->tangentImpulse * tangent;
|
|
g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Test::ShiftOrigin(const b2Vec2& newOrigin)
|
|
{
|
|
m_world->ShiftOrigin(newOrigin);
|
|
}
|
|
|
|
void Test::initShader(void)
|
|
{
|
|
// initShader is unsupported
|
|
}
|
|
|
|
void Test::DrawString(int x, int y, const char* fmt, ...)
|
|
{
|
|
#if defined(AX_PLATFORM_PC)
|
|
debugString.append(std::string(fmt));
|
|
debugString.append("\n");
|
|
labelDebugDraw->setString(debugString);
|
|
// labelDebugDraw->setPosition(x, y);
|
|
#endif
|
|
}
|
|
|
|
void Test::DrawString(const b2Vec2& pw, const char* fmt, ...)
|
|
{
|
|
#if defined(AX_PLATFORM_PC)
|
|
debugString.append(std::string(fmt));
|
|
debugString.append("\n");
|
|
labelDebugDraw->setString(debugString);
|
|
// labelDebugDraw->setPosition(pw.x, pw.y);
|
|
#endif
|
|
}
|
|
|
|
void Test::DrawAABB(b2AABB* aabb, const b2Color& color)
|
|
{
|
|
b2Vec2 p1 = aabb->lowerBound;
|
|
b2Vec2 p2 = b2Vec2(aabb->upperBound.x, aabb->lowerBound.y);
|
|
b2Vec2 p3 = aabb->upperBound;
|
|
b2Vec2 p4 = b2Vec2(aabb->lowerBound.x, aabb->upperBound.y);
|
|
|
|
Vec2 verts[] = {
|
|
Vec2(p1.x * g_debugDraw.mRatio, p1.y * g_debugDraw.mRatio) + g_debugDraw.debugNodeOffset,
|
|
Vec2(p2.x * g_debugDraw.mRatio, p2.y * g_debugDraw.mRatio) + g_debugDraw.debugNodeOffset,
|
|
Vec2(p3.x * g_debugDraw.mRatio, p3.y * g_debugDraw.mRatio) + g_debugDraw.debugNodeOffset,
|
|
Vec2(p4.x * g_debugDraw.mRatio, p4.y * g_debugDraw.mRatio) + g_debugDraw.debugNodeOffset,
|
|
};
|
|
debugDrawNode->drawPolygon(verts, sizeof(verts) / sizeof(verts[0]),
|
|
Color4F(color.r / 2, color.g / 2, color.b / 2, 0), 0.4f,
|
|
Color4F(color.r, color.g, color.b, color.a));
|
|
}
|
|
|
|
void Test::Flush()
|
|
{
|
|
// Flush is unsupported
|
|
}
|