Merge pull request #419 from aismann/Box2D-TestBed

* Starting "Box2D - TextBed" adaption (most demos working)

* fix CMakeList.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update Box2dTest.cpp

* Update tests.h

* Update CCPhysicsDebugNodeBox2D.cpp

* Update controller.cpp
This commit is contained in:
aismann 2021-07-06 15:02:47 +02:00 committed by GitHub
parent 46a3ec8d6b
commit 76b5bf8038
68 changed files with 10978 additions and 5 deletions

View File

@ -358,13 +358,87 @@ if(WINDOWS OR MACOSX OR LINUX)
endif()
list(APPEND GAME_HEADER
Classes/Box2DTest/Box2dTest.h
Classes/Box2DTest/Box2dTest.h
)
if(WINDOWS)
list(APPEND GAME_HEADER
Classes/Box2DTestBed/Test.h
Classes/Box2DTestBed/Box2DTestBed.h
Classes/Box2DTestBed/CCPhysicsDebugNodeBox2D.h
)
endif()
list(APPEND GAME_SOURCE
Classes/Box2DTest/Box2dTest.cpp
)
Classes/Box2DTest/Box2dTest.cpp
)
if(WINDOWS)
list(APPEND GAME_HEADER
Classes/Box2DTestBed/Box2DTestBed.cpp
Classes/Box2DTestBed/CCPhysicsDebugNodeBox2D.cpp
Classes/Box2DTestBed/Test.cpp
Classes/Box2DTestBed/tests/add_pair.cpp
Classes/Box2DTestBed/tests/apply_force.cpp
Classes/Box2DTestBed/tests/body_types.cpp
Classes/Box2DTestBed/tests/box_stack.cpp
Classes/Box2DTestBed/tests/breakable.cpp
Classes/Box2DTestBed/tests/bridge.cpp
Classes/Box2DTestBed/tests/bullet_test.cpp
Classes/Box2DTestBed/tests/cantilever.cpp
Classes/Box2DTestBed/tests/car.cpp
Classes/Box2DTestBed/tests/chain.cpp
Classes/Box2DTestBed/tests/chain_problem.cpp
Classes/Box2DTestBed/tests/character_collision.cpp
Classes/Box2DTestBed/tests/circle_stack.cpp
Classes/Box2DTestBed/tests/collision_filtering.cpp
Classes/Box2DTestBed/tests/collision_processing.cpp
Classes/Box2DTestBed/tests/compound_shapes.cpp
Classes/Box2DTestBed/tests/confined.cpp
Classes/Box2DTestBed/tests/continuous_test.cpp
Classes/Box2DTestBed/tests/convex_hull.cpp
Classes/Box2DTestBed/tests/conveyor_belt.cpp
Classes/Box2DTestBed/tests/distance_joint.cpp
Classes/Box2DTestBed/tests/distance_test.cpp
Classes/Box2DTestBed/tests/dominos.cpp
Classes/Box2DTestBed/tests/dump_loader.cpp
Classes/Box2DTestBed/tests/dynamic_tree.cpp
Classes/Box2DTestBed/tests/edge_shapes.cpp
Classes/Box2DTestBed/tests/edge_test.cpp
Classes/Box2DTestBed/tests/friction.cpp
Classes/Box2DTestBed/tests/gear_joint.cpp
Classes/Box2DTestBed/tests/heavy1.cpp
Classes/Box2DTestBed/tests/heavy2.cpp
Classes/Box2DTestBed/tests/mobile_balanced.cpp
Classes/Box2DTestBed/tests/mobile_unbalanced.cpp
Classes/Box2DTestBed/tests/motor_joint.cpp
Classes/Box2DTestBed/tests/pinball.cpp
Classes/Box2DTestBed/tests/platformer.cpp
Classes/Box2DTestBed/tests/polygon_collision.cpp
Classes/Box2DTestBed/tests/polygon_shapes.cpp
Classes/Box2DTestBed/tests/prismatic_joint.cpp
Classes/Box2DTestBed/tests/pulley_joint.cpp
Classes/Box2DTestBed/tests/pyramid.cpp
Classes/Box2DTestBed/tests/ray_cast.cpp
Classes/Box2DTestBed/tests/restitution.cpp
Classes/Box2DTestBed/tests/revolute_joint.cpp
Classes/Box2DTestBed/tests/rope.cpp
Classes/Box2DTestBed/tests/sensor.cpp
Classes/Box2DTestBed/tests/shape_cast.cpp
Classes/Box2DTestBed/tests/shape_editing.cpp
Classes/Box2DTestBed/tests/skier.cpp
Classes/Box2DTestBed/tests/slider_crank_1.cpp
Classes/Box2DTestBed/tests/slider_crank_2.cpp
Classes/Box2DTestBed/tests/theo_jansen.cpp
Classes/Box2DTestBed/tests/tiles.cpp
Classes/Box2DTestBed/tests/time_of_impact.cpp
Classes/Box2DTestBed/tests/tumbler.cpp
Classes/Box2DTestBed/tests/web.cpp
Classes/Box2DTestBed/tests/wheel_joint.cpp
Classes/Box2DTestBed/tests/wrecking_ball.cpp
)
endif()
list(APPEND GAME_HEADER
Classes/Physics3DTest/Physics3DTest.h
Classes/NavMeshTest/NavMeshTest.h

View File

@ -0,0 +1,280 @@
/****************************************************************************
* Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft
http://www.cocos2d-x.org
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 "Box2DTestBed.h"
#include "CCPhysicsDebugNodeBox2D.h"
#include "test.h"
//#include "renderer/CCRenderer.h"
USING_NS_CC;
#define kAccelerometerFrequency 30
#define FRAMES_BETWEEN_PRESSES_FOR_DOUBLE_CLICK 10
extern int g_testCount;
Settings settings;
enum
{
kTagBox2DNode,
};
extern cocos2d::DrawNode* drawBox2D;
Box2DTestBedTests::Box2DTestBedTests()
{
for (int entryId = 0; entryId < g_testCount; ++entryId)
{
addTestCase(g_testEntries[entryId].name, [entryId]() {
return Box2DTestBed::createWithEntryID(entryId);
});
}
}
//------------------------------------------------------------------
//
// Box2dTestBed
//
//------------------------------------------------------------------
Box2DTestBed::Box2DTestBed()
{
}
Box2DTestBed::~Box2DTestBed()
{
_eventDispatcher->removeEventListener(_touchListener);
}
Box2DTestBed* Box2DTestBed::createWithEntryID(int entryId)
{
auto layer = new (std::nothrow) Box2DTestBed();
layer->initWithEntryID(entryId);
layer->autorelease();
return layer;
}
bool Box2DTestBed::initWithEntryID(int entryId)
{
if (!TestCase::init())
{
return false;
}
auto director = Director::getInstance();
Vec2 visibleOrigin = director->getVisibleOrigin();
Size visibleSize = director->getVisibleSize();
m_entryID = entryId;
Box2DView* view = Box2DView::viewWithEntryID(entryId);
addChild(view, 0, kTagBox2DNode);
view->setScale(15);
view->setAnchorPoint(Vec2(0, 0));
view->setPosition(visibleOrigin.x + visibleSize.width / 2, visibleOrigin.y + visibleSize.height / 3);
auto label = Label::createWithTTF(view->title().c_str(), "fonts/arial.ttf", 28);
addChild(label, 1);
label->setPosition(visibleOrigin.x + visibleSize.width / 2, visibleOrigin.y + visibleSize.height - 50);
// Adds touch event listener
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(Box2DTestBed::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(Box2DTestBed::onTouchMoved, this);
_eventDispatcher->addEventListenerWithFixedPriority(listener, 1);
_touchListener = listener;
addChild(drawBox2D, 100);
//this->createResetButton();
return true;
}
bool Box2DTestBed::onTouchBegan(Touch* touch, Event* event)
{
return true;
}
void Box2DTestBed::onTouchMoved(Touch* touch, Event* event)
{
auto diff = touch->getDelta();
auto node = getChildByTag(kTagBox2DNode);
auto currentPos = node->getPosition();
node->setPosition(currentPos + diff);
}
//void Box2DTestBed::createResetButton() {
// auto reset = MenuItemImage::create("Images/r1.png", "Images/r2.png", CC_CALLBACK_1(Box2DTestBed::reset, this));
// auto menu = Menu::create(reset, nullptr);
// menu->setPosition(VisibleRect::center().x, VisibleRect::bottom().y + 40);
// this->addChild(menu, -1);
//}
//
//void Box2DTestBed::reset(Ref* sender) {
// getTestSuite()->restartCurrTest();
//}
//
//void Box2DTestBed::onEnter() {
// TestCase::onEnter();
// // physicsDebugNodeOffset = VisibleRect::center();
// //physicsDebugNodeOffset.y += 20;
// //ChipmunkDemoMessageString = "";
// //label->setString("");
//}
//------------------------------------------------------------------
//
// Box2DView
//
//------------------------------------------------------------------
Box2DView::Box2DView(void)
{
}
Box2DView* Box2DView::viewWithEntryID(int entryId)
{
Box2DView* pView = new (std::nothrow) Box2DView();
pView->initWithEntryID(entryId);
pView->autorelease();
return pView;
}
bool Box2DView::initWithEntryID(int entryId)
{
m_entry = g_testEntries + entryId;
m_test = m_entry->createFcn();
// Adds Touch Event Listener
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(Box2DView::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(Box2DView::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(Box2DView::onTouchEnded, this);
_eventDispatcher->addEventListenerWithFixedPriority(listener, -10);
_touchListener = listener;
auto keyboardListener = EventListenerKeyboard::create();
keyboardListener->onKeyPressed = CC_CALLBACK_2(Box2DView::onKeyPressed, this);
keyboardListener->onKeyReleased = CC_CALLBACK_2(Box2DView::onKeyReleased, this);
_eventDispatcher->addEventListenerWithFixedPriority(keyboardListener, -11);
_keyboardListener = keyboardListener;
return true;
}
std::string Box2DView::title() const
{
std::string title = std::string(m_entry->category) + std::string(":") + std::string(m_entry->name);
return title;
}
void Box2DView::draw(Renderer* renderer, const Mat4& transform, uint32_t flags)
{
Layer::draw(renderer, transform, flags);
_customCmd.init(_globalZOrder, transform, flags);
_customCmd.func = CC_CALLBACK_0(Box2DView::onDraw, this, transform, flags);
renderer->addCommand(&_customCmd);
Director* director = Director::getInstance();
}
void Box2DView::onDraw(const Mat4& transform, uint32_t flags)
{
Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when setting matrix stack");
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);
// GL::enableVertexAttribs( cocos2d::GL::VERTEX_ATTRIB_FLAG_POSITION );
drawBox2D->clear();
m_test->Step(&settings);
m_test->m_world->DebugDraw();
CHECK_GL_ERROR_DEBUG();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
Box2DView::~Box2DView()
{
// Removes Touch Event Listener
_eventDispatcher->removeEventListener(_touchListener);
_eventDispatcher->removeEventListener(_keyboardListener);
delete m_test;
}
bool Box2DView::onTouchBegan(Touch* touch, Event* event)
{
auto touchLocation = touch->getLocation();
auto nodePosition = convertToNodeSpace(touchLocation);
log("Box2DView::onTouchBegan, pos: %f,%f -> %f,%f", touchLocation.x, touchLocation.y, nodePosition.x, nodePosition.y);
return m_test->MouseDown(b2Vec2(nodePosition.x, nodePosition.y));
}
void Box2DView::onTouchMoved(Touch* touch, Event* event)
{
auto touchLocation = touch->getLocation();
auto nodePosition = convertToNodeSpace(touchLocation);
log("Box2DView::onTouchMoved, pos: %f,%f -> %f,%f", touchLocation.x, touchLocation.y, nodePosition.x, nodePosition.y);
m_test->MouseMove(b2Vec2(nodePosition.x, nodePosition.y));
}
void Box2DView::onTouchEnded(Touch* touch, Event* event)
{
auto touchLocation = touch->getLocation();
auto nodePosition = convertToNodeSpace(touchLocation);
log("Box2DView::onTouchEnded, pos: %f,%f -> %f,%f", touchLocation.x, touchLocation.y, nodePosition.x, nodePosition.y);
m_test->MouseUp(b2Vec2(nodePosition.x, nodePosition.y));
}
void Box2DView::onKeyPressed(EventKeyboard::KeyCode code, Event* event)
{
log("Box2dView:onKeyPressed, keycode: %d", static_cast<int>(code));
m_test->Keyboard(static_cast<unsigned char>(code));
}
void Box2DView::onKeyReleased(EventKeyboard::KeyCode code, Event* event)
{
log("onKeyReleased, keycode: %d", static_cast<int>(code));
m_test->KeyboardUp(static_cast<unsigned char>(code));
}

View File

@ -0,0 +1,93 @@
/****************************************************************************
* Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft
http://www.cocos2d-x.org
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.
****************************************************************************/
#ifndef _BOX2D_VIEW_H_
#define _BOX2D_VIEW_H_
#include "../BaseTest.h"
#include "renderer/CCCustomCommand.h"
DEFINE_TEST_SUITE(Box2DTestBedTests);
class Box2DTestBed : public TestCase
{
public:
static Box2DTestBed* createWithEntryID(int entryId);
Box2DTestBed();
virtual ~Box2DTestBed();
//void onEnter() override;
//void createResetButton();
//void reset(cocos2d::Ref* sender);
bool initWithEntryID(int entryId);
bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
void onTouchMoved(cocos2d::Touch* touch, cocos2d::Event* event);
cocos2d::DrawNode* draw = NULL;
private:
int m_entryID;
cocos2d::EventListenerTouchOneByOne* _touchListener;
};
struct TestEntry;
class Test;
class Box2DView : public cocos2d::Layer
{
cocos2d::EventListenerTouchOneByOne* _touchListener;
cocos2d::EventListenerKeyboard* _keyboardListener;
TestEntry* m_entry;
Test* m_test;
int m_entryID;
public:
Box2DView(void);
virtual ~Box2DView(void);
bool initWithEntryID(int entryId);
std::string title() const;
virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags) override;
// virtual void registerWithTouchDispatcher();
bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event)override;
void onTouchMoved(cocos2d::Touch* touch, cocos2d::Event* event)override;
void onTouchEnded(cocos2d::Touch* touch, cocos2d::Event* event)override;
void onKeyPressed(cocos2d::EventKeyboard::KeyCode code, cocos2d::Event* event)override;
void onKeyReleased(cocos2d::EventKeyboard::KeyCode code, cocos2d::Event* event)override;
//virtual void accelerometer(UIAccelerometer* accelerometer, cocos2d::Acceleration* acceleration);
static Box2DView* viewWithEntryID(int entryId);
protected:
void onDraw(const cocos2d::Mat4& transform, uint32_t flags);
cocos2d::CallbackCommand _customCmd;
};
#endif

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft
*
*
*
* 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 "CCPhysicsDebugNodeBox2D.h"
#include "cocos2d.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "extensions/cocos-ext.h"
USING_NS_CC;
cocos2d::DrawNode* drawBox2D;
//
//// halx99: since adxe init scene default camera at 'initWithXXX' function, only change design size at scene
//// construct is ok see also: https://github.com/adxeproject/adxe/commit/581a7921554c09746616759d5a5ca6ce9d3eaa22
//auto director = Director::getInstance();
//auto glview = director->getOpenGLView();
//Size designSize(960 * 0.85, 640 * 0.85);
//glview->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::NO_BORDER);
cocos2d::Vec2 physicsDebugNodeOffset = { 260, 70 };
GLESDebugDraw::GLESDebugDraw()
: mRatio( 10.0f )
{
this->initShader();
drawBP = DrawNode::create();
drawBox2D = drawBP;
}
GLESDebugDraw::GLESDebugDraw(float ratio )
: mRatio( ratio )
{
this->initShader();
}
void GLESDebugDraw::initShader( void )
{
//mShaderProgram = GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_U_COLOR);
//mColorLocation = glGetUniformLocation( mShaderProgram->getProgram(), "u_color");
}
void GLESDebugDraw::DrawPolygon(const b2Vec2* verts, int vertexCount, const b2Color& color)
{
if (!drawBP) return;
Vec2* vec = new (std::nothrow) Vec2[vertexCount];
for (size_t i = 0; i < vertexCount; i++) {
vec[i] = Vec2(verts[i].x * mRatio, verts[i].y * mRatio) + physicsDebugNodeOffset;
}
drawBP->drawPolygon(vec, vertexCount, Color4F(color.r, color.g, color.b, color.a), 1, Color4F(color.r, color.g, color.b, color.a));
}
void GLESDebugDraw::DrawSolidPolygon(const b2Vec2* verts, int vertexCount, const b2Color& color)
{
if (!drawBP) return;
Vec2* vec = new (std::nothrow) Vec2[vertexCount];
for (size_t i = 0; i < vertexCount; i++) {
vec[i] = Vec2(verts[i].x * mRatio, verts[i].y * mRatio) + physicsDebugNodeOffset;
}
drawBP->drawPolygon(vec, vertexCount, Color4F(color.r / 2, color.g / 2, color.b / 2, color.a), 0.2f, Color4F(color.r, color.g, color.b, color.a));
//drawBP->drawSolidPoly(vec, vertexCount, Color4F(color.r, color.g, color.b, color.a));
}
void GLESDebugDraw::DrawCircle(const b2Vec2& center, float radius, const b2Color& color)
{
if (!drawBP) return;
drawBP->drawCircle(Vec2(center.x * mRatio, center.y * mRatio) + physicsDebugNodeOffset, radius* mRatio, CC_DEGREES_TO_RADIANS(0), 30, true, 1.0f,
1.0f, Color4F(color.r, color.g, color.b, color.a));
}
void GLESDebugDraw::DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color)
{
if (!drawBP) return;
// DrawSolidCircle Maybe have to fix later
drawBP->drawCircle(Vec2(center.x * mRatio, center.y * mRatio) + physicsDebugNodeOffset, radius* mRatio, CC_DEGREES_TO_RADIANS(0), 20, true, 1.0f, 1.0f, Color4F(color.r, color.g, color.b, color.a));
}
void GLESDebugDraw::DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color)
{
if (!drawBP) return;
drawBP->drawLine(Vec2(p1.x * mRatio, p1.y * mRatio) + physicsDebugNodeOffset, Vec2(p2.x * mRatio, p2.y * mRatio) + physicsDebugNodeOffset, Color4F(color.r, color.g, color.b, color.a));
}
void GLESDebugDraw::DrawTransform(const b2Transform& xf)
{
CCLOG("======== DrawTransform ===========");
b2Vec2 p1 = xf.p, p2;
const float k_axisScale = 0.4f;
p2 = p1 + k_axisScale * xf.q.GetXAxis();
DrawSegment(p1, p2, b2Color(1,0,0));
p2 = p1 + k_axisScale * xf.q.GetYAxis();
DrawSegment(p1,p2,b2Color(0,1,0));
}
void GLESDebugDraw::DrawPoint(const b2Vec2& p, float size, const b2Color& color)
{
if (!drawBP) return;
drawBP->drawPoint(Vec2(p.x * mRatio, p.y * mRatio) + physicsDebugNodeOffset, size, Color4F(color.r, color.g, color.b, color.a));
}
void GLESDebugDraw::DrawString(int x, int y, const char *string, ...)
{
// NSLog(@"DrawString: unsupported: %s", string);
//printf(string);
/* Unsupported as yet. Could replace with bitmap font renderer at a later date */
}
void GLESDebugDraw::DrawAABB(b2AABB* aabb, const b2Color& color)
{
CCLOG("======== DrawAABB ===========");
if (!drawBP) return;
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 * mRatio, p1.y * mRatio) + physicsDebugNodeOffset ,
Vec2(p2.x * mRatio, p2.y * mRatio) + physicsDebugNodeOffset ,
Vec2(p3.x * mRatio, p3.y * mRatio) + physicsDebugNodeOffset ,
Vec2(p4.x * mRatio, p4.y * mRatio) + physicsDebugNodeOffset ,
};
drawBP->drawSolidPoly(verts, sizeof(verts) / sizeof(verts[0]), Color4F(color.r, color.g, color.b, color.a));
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft
*
* 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.
*/
#ifndef __PHYSICSNODES_DEBUGNODE_BOX2D_H__
#define __PHYSICSNODES_DEBUGNODE_BOX2D_H__
#include "Box2D/Box2D.h"
#include "cocos2d.h"
struct b2AABB;
extern cocos2d::DrawNode* drawBox2D;
// This class implements debug drawing callbacks that are invoked
// inside b2World::Step.
class GLESDebugDraw : public b2Draw
{
float mRatio;
// cocos2d::g* mShaderProgram;
GLint mColorLocation;
cocos2d::DrawNode* drawBP = NULL;
void initShader( void );
public:
GLESDebugDraw();
GLESDebugDraw(float ratio );
virtual void DrawPolygon(const b2Vec2* vertices, int vertexCount, const b2Color& color);
virtual void DrawSolidPolygon(const b2Vec2* vertices, int vertexCount, const b2Color& color);
virtual void DrawCircle(const b2Vec2& center, float radius, const b2Color& color);
virtual void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color);
virtual void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color);
virtual void DrawTransform(const b2Transform& xf);
virtual void DrawPoint(const b2Vec2& p, float size, const b2Color& color);
virtual void DrawString(int x, int y, const char* string, ...);
virtual void DrawAABB(b2AABB* aabb, const b2Color& color);
};
#endif

View File

@ -0,0 +1,478 @@
/*
* 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 "test.h"
#include "CCPhysicsDebugNodeBox2D.h"
#include <stdio.h>
void DestructionListener::SayGoodbye(b2Joint* joint)
{
if (test->m_mouseJoint == joint)
{
test->m_mouseJoint = nullptr;
}
else
{
test->JointDestroyed(joint);
}
}
Test::Test()
{
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
m_world = new b2World(gravity);
m_bomb = nullptr;
m_textLine = 30;
m_mouseJoint = nullptr;
m_pointCount = 0;
m_destructionListener.test = this;
m_world->SetDestructionListener(&m_destructionListener);
m_world->SetContactListener(this);
m_world->SetDebugDraw(&m_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 = nullptr;
}
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)
{
m_debugDraw.DrawString(5, DRAW_STRING_NEW_LINE, string);
m_textLine = 2 * DRAW_STRING_NEW_LINE;
}
class QueryCallback : public b2QueryCallback
{
public:
QueryCallback(const b2Vec2& point)
{
m_point = point;
m_fixture = nullptr;
}
bool ReportFixture(b2Fixture* fixture)
{
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 != nullptr)
{
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)
{
b2Body* body = callback.m_fixture->GetBody();
b2MouseJointDef md;
md.bodyA = m_groundBody;
md.bodyB = body;
md.target = p;
md.maxForce = 1000.0f * body->GetMass();
m_mouseJoint = (b2MouseJoint*)m_world->CreateJoint(&md);
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 != nullptr)
{
return;
}
SpawnBomb(p);
}
void Test::MouseUp(const b2Vec2& p)
{
if (m_mouseJoint)
{
m_world->DestroyJoint(m_mouseJoint);
m_mouseJoint = nullptr;
}
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 = nullptr;
}
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->hz > 0.0f ? 1.0f / settings->hz : float(0.0f);
if (settings->pause)
{
if (settings->singleStep)
{
settings->singleStep = 0;
}
else
{
timeStep = 0.0f;
}
m_debugDraw.DrawString(5, m_textLine, "****PAUSED****");
m_textLine += DRAW_STRING_NEW_LINE;
}
uint32 flags = 0;
flags += settings->drawShapes * b2Draw::e_shapeBit;
flags += settings->drawJoints * b2Draw::e_jointBit;
flags += settings->drawAABBs * b2Draw::e_aabbBit;
flags += settings->drawCOMs * b2Draw::e_centerOfMassBit;
m_debugDraw.SetFlags(flags);
m_world->SetAllowSleeping(settings->enableSleep > 0);
m_world->SetWarmStarting(settings->enableWarmStarting > 0);
m_world->SetContinuousPhysics(settings->enableContinuous > 0);
m_world->SetSubStepping(settings->enableSubStepping > 0);
m_pointCount = 0;
m_world->Step(timeStep, settings->velocityIterations, settings->positionIterations);
m_world->DebugDraw();
if (timeStep > 0.0f)
{
++m_stepCount;
}
if (settings->drawStats)
{
int32 bodyCount = m_world->GetBodyCount();
int32 contactCount = m_world->GetContactCount();
int32 jointCount = m_world->GetJointCount();
m_debugDraw.DrawString(5, m_textLine, "bodies/contacts/joints = %d/%d/%d", bodyCount, contactCount, jointCount);
m_textLine += DRAW_STRING_NEW_LINE;
int32 proxyCount = m_world->GetProxyCount();
int32 height = m_world->GetTreeHeight();
int32 balance = m_world->GetTreeBalance();
float quality = m_world->GetTreeQuality();
m_debugDraw.DrawString(5, m_textLine, "proxies/height/balance/quality = %d/%d/%d/%g", proxyCount, height, balance, quality);
m_textLine += DRAW_STRING_NEW_LINE;
}
// 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->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;
}
m_debugDraw.DrawString(5, m_textLine, "step [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.step, aveProfile.step, m_maxProfile.step);
m_textLine += DRAW_STRING_NEW_LINE;
m_debugDraw.DrawString(5, m_textLine, "collide [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.collide, aveProfile.collide, m_maxProfile.collide);
m_textLine += DRAW_STRING_NEW_LINE;
m_debugDraw.DrawString(5, m_textLine, "solve [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solve, aveProfile.solve, m_maxProfile.solve);
m_textLine += DRAW_STRING_NEW_LINE;
m_debugDraw.DrawString(5, m_textLine, "solve init [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveInit, aveProfile.solveInit, m_maxProfile.solveInit);
m_textLine += DRAW_STRING_NEW_LINE;
m_debugDraw.DrawString(5, m_textLine, "solve velocity [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveVelocity, aveProfile.solveVelocity, m_maxProfile.solveVelocity);
m_textLine += DRAW_STRING_NEW_LINE;
m_debugDraw.DrawString(5, m_textLine, "solve position [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solvePosition, aveProfile.solvePosition, m_maxProfile.solvePosition);
m_textLine += DRAW_STRING_NEW_LINE;
m_debugDraw.DrawString(5, m_textLine, "solveTOI [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveTOI, aveProfile.solveTOI, m_maxProfile.solveTOI);
m_textLine += DRAW_STRING_NEW_LINE;
m_debugDraw.DrawString(5, m_textLine, "broad-phase [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.broadphase, aveProfile.broadphase, m_maxProfile.broadphase);
m_textLine += DRAW_STRING_NEW_LINE;
}
if (m_mouseJoint)
{
b2Vec2 p1 = m_mouseJoint->GetAnchorB();
b2Vec2 p2 = m_mouseJoint->GetTarget();
b2Color c;
c.Set(0.0f, 1.0f, 0.0f);
m_debugDraw.DrawPoint(p1, 4.0f, c);
m_debugDraw.DrawPoint(p2, 4.0f, c);
c.Set(0.8f, 0.8f, 0.8f);
m_debugDraw.DrawSegment(p1, p2, c);
}
if (m_bombSpawning)
{
b2Color c;
c.Set(0.0f, 0.0f, 1.0f);
m_debugDraw.DrawPoint(m_bombSpawnPoint, 4.0f, c);
c.Set(0.8f, 0.8f, 0.8f);
m_debugDraw.DrawSegment(m_mouseWorld, m_bombSpawnPoint, c);
}
if (settings->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
m_debugDraw.DrawPoint(point->position, 10.0f, b2Color(0.3f, 0.95f, 0.3f));
}
else if (point->state == b2_persistState)
{
// Persist
m_debugDraw.DrawPoint(point->position, 5.0f, b2Color(0.3f, 0.3f, 0.95f));
}
if (settings->drawContactNormals == 1)
{
b2Vec2 p1 = point->position;
b2Vec2 p2 = p1 + k_axisScale * point->normal;
m_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.9f));
}
else if (settings->drawContactImpulse == 1)
{
b2Vec2 p1 = point->position;
b2Vec2 p2 = p1 + k_impulseScale * point->normalImpulse * point->normal;
m_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
}
if (settings->drawFrictionImpulse == 1)
{
b2Vec2 tangent = b2Cross(point->normal, 1.0f);
b2Vec2 p1 = point->position;
b2Vec2 p2 = p1 + k_impulseScale * point->tangentImpulse * tangent;
m_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
}
}
}
}
void Test::ShiftOrigin(const b2Vec2& newOrigin)
{
m_world->ShiftOrigin(newOrigin);
}
TestEntry g_testEntries[MAX_TESTS] = { {nullptr} };
int g_testCount = 0;
int RegisterTest(const char* category, const char* name, TestCreateFcn* fcn)
{
int index = g_testCount;
if (index < MAX_TESTS)
{
g_testEntries[index] = { category, name, fcn };
++g_testCount;
return index;
}
return -1;
}

View File

@ -0,0 +1,206 @@
/*
* 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.
*/
#ifndef TEST_H
#define TEST_H
#include "Box2D/Box2D.h"
#include "CCPhysicsDebugNodeBox2D.h"
#include <cstdlib>
class Test;
struct Settings;
#define RAND_LIMIT 32767
#define DRAW_STRING_NEW_LINE 25
/// Random number in range [-1,1]
inline float RandomFloat()
{
float r = (float)(std::rand() & (RAND_LIMIT));
r /= RAND_LIMIT;
r = 2.0f * r - 1.0f;
return r;
}
/// Random floating point number in range [lo, hi]
inline float RandomFloat(float lo, float hi)
{
float r = (float)(std::rand() & (RAND_LIMIT));
r /= RAND_LIMIT;
r = (hi - lo) * r + lo;
return r;
}
/// Test settings. Some can be controlled in the GUI.
struct Settings
{
Settings()
{
viewCenter.Set(0.0f, 20.0f);
hz = 60.0f;
velocityIterations = 8;
positionIterations = 3;
drawShapes = 1;
drawJoints = 1;
drawAABBs = 0;
drawContactPoints = 0;
drawContactNormals = 0;
drawContactImpulse = 0;
drawFrictionImpulse = 0;
drawCOMs = 0;
drawStats = 0;
drawProfile = 0;
enableWarmStarting = 1;
enableContinuous = 1;
enableSubStepping = 0;
enableSleep = 1;
pause = 0;
singleStep = 0;
}
b2Vec2 viewCenter;
float hz;
int32 velocityIterations;
int32 positionIterations;
int32 drawShapes;
int32 drawJoints;
int32 drawAABBs;
int32 drawContactPoints;
int32 drawContactNormals;
int32 drawContactImpulse;
int32 drawFrictionImpulse;
int32 drawCOMs;
int32 drawStats;
int32 drawProfile;
int32 enableWarmStarting;
int32 enableContinuous;
int32 enableSubStepping;
int32 enableSleep;
int32 pause;
int32 singleStep;
};
// This is called when a joint in the world is implicitly destroyed
// because an attached body is destroyed. This gives us a chance to
// nullify the mouse joint.
class DestructionListener : public b2DestructionListener
{
public:
void SayGoodbye(b2Fixture* fixture) { B2_NOT_USED(fixture); }
void SayGoodbye(b2Joint* joint);
Test* test;
};
const int32 k_maxContactPoints = 2048;
struct ContactPoint
{
b2Fixture* fixtureA;
b2Fixture* fixtureB;
b2Vec2 normal;
b2Vec2 position;
b2PointState state;
float normalImpulse;
float tangentImpulse;
float separation;
};
class Test : public b2ContactListener
{
public:
Test();
virtual ~Test();
void DrawTitle(const char *string);
virtual void Step(Settings* settings);
virtual void Keyboard(unsigned char key) { B2_NOT_USED(key); }
virtual void KeyboardUp(unsigned char key) { B2_NOT_USED(key); }
void ShiftMouseDown(const b2Vec2& p);
virtual bool MouseDown(const b2Vec2& p);
virtual void MouseUp(const b2Vec2& p);
void MouseMove(const b2Vec2& p);
void LaunchBomb();
void LaunchBomb(const b2Vec2& position, const b2Vec2& velocity);
void SpawnBomb(const b2Vec2& worldPt);
void CompleteBombSpawn(const b2Vec2& p);
// Let derived tests know that a joint was destroyed.
virtual void JointDestroyed(b2Joint* joint) { B2_NOT_USED(joint); }
// Callbacks for derived classes.
virtual void BeginContact(b2Contact* contact) { B2_NOT_USED(contact); }
virtual void EndContact(b2Contact* contact) { B2_NOT_USED(contact); }
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{
B2_NOT_USED(contact);
B2_NOT_USED(impulse);
}
void ShiftOrigin(const b2Vec2& newOrigin);
protected:
friend class DestructionListener;
friend class BoundaryListener;
friend class ContactListener;
friend class Box2DView;
b2Body* m_groundBody;
b2AABB m_worldAABB;
ContactPoint m_points[k_maxContactPoints];
int32 m_pointCount;
DestructionListener m_destructionListener;
GLESDebugDraw m_debugDraw;
int32 m_textLine;
b2World* m_world;
b2Body* m_bomb;
b2MouseJoint* m_mouseJoint;
b2Vec2 m_bombSpawnPoint;
bool m_bombSpawning;
b2Vec2 m_mouseWorld;
int32 m_stepCount;
b2Profile m_maxProfile;
b2Profile m_totalProfile;
};
typedef Test* TestCreateFcn();
int RegisterTest(const char* category, const char* name, TestCreateFcn* fcn);
//
struct TestEntry
{
const char* category;
const char* name;
TestCreateFcn* createFcn;
};
#define MAX_TESTS 256
extern TestEntry g_testEntries[MAX_TESTS];
extern int g_testCount;
#endif

View File

@ -0,0 +1,73 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class AddPair : public Test
{
public:
AddPair()
{
m_world->SetGravity(b2Vec2(0.0f,0.0f));
{
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.1f;
float minX = -6.0f;
float maxX = 0.0f;
float minY = 4.0f;
float maxY = 6.0f;
for (int32 i = 0; i < 400; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(RandomFloat(minX,maxX),RandomFloat(minY,maxY));
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 0.01f);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f,5.0f);
bd.bullet = true;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(10.0f, 0.0f));
}
}
static Test* Create()
{
return new AddPair;
}
};
static int testIndex = RegisterTest("Benchmark", "Add Pair", AddPair::Create);

View File

@ -0,0 +1,203 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// This test shows how to apply forces and torques to a body.
// It also shows how to use the friction joint that can be useful
// for overhead games.
class ApplyForce : public Test
{
public:
ApplyForce()
{
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
const float k_restitution = 0.4f;
b2Body* ground;
{
b2BodyDef bd;
bd.position.Set(0.0f, 20.0f);
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef sd;
sd.shape = &shape;
sd.density = 0.0f;
sd.restitution = k_restitution;
// Left vertical
shape.SetTwoSided(b2Vec2(-20.0f, -20.0f), b2Vec2(-20.0f, 20.0f));
ground->CreateFixture(&sd);
// Right vertical
shape.SetTwoSided(b2Vec2(20.0f, -20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Top horizontal
shape.SetTwoSided(b2Vec2(-20.0f, 20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Bottom horizontal
shape.SetTwoSided(b2Vec2(-20.0f, -20.0f), b2Vec2(20.0f, -20.0f));
ground->CreateFixture(&sd);
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly1;
poly1.Set(vertices, 3);
b2FixtureDef sd1;
sd1.shape = &poly1;
sd1.density = 2.0f;
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly2;
poly2.Set(vertices, 3);
b2FixtureDef sd2;
sd2.shape = &poly2;
sd2.density = 2.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 3.0);
bd.angle = b2_pi;
bd.allowSleep = false;
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&sd1);
m_body->CreateFixture(&sd2);
float gravity = 10.0f;
float I = m_body->GetInertia();
float mass = m_body->GetMass();
// Compute an effective radius that can be used to
// set the max torque for a friction joint
// For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float radius = b2Sqrt(2.0f * I / mass);
b2FrictionJointDef jd;
jd.bodyA = ground;
jd.bodyB = m_body;
jd.localAnchorA.SetZero();
jd.localAnchorB = m_body->GetLocalCenter();
jd.collideConnected = true;
jd.maxForce = 0.5f * mass * gravity;
jd.maxTorque = 0.2f * mass * radius * gravity;
m_world->CreateJoint(&jd);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 7.0f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
float gravity = 10.0f;
float I = body->GetInertia();
float mass = body->GetMass();
// For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float radius = b2Sqrt(2.0f * I / mass);
b2FrictionJointDef jd;
jd.localAnchorA.SetZero();
jd.localAnchorB.SetZero();
jd.bodyA = ground;
jd.bodyB = body;
jd.collideConnected = true;
jd.maxForce = mass * gravity;
jd.maxTorque = 0.1f * mass * radius * gravity;
m_world->CreateJoint(&jd);
}
}
}
void Step(Settings* settings) override
{
//g_debugDraw.DrawString(5, m_textLine, "Forward (W), Turn (A) and (D)");
//m_textLine += m_textIncrement;
//if (glfwGetKey(g_mainWindow, GLFW_KEY_W) == GLFW_PRESS)
//{
// b2Vec2 f = m_body->GetWorldVector(b2Vec2(0.0f, -50.0f));
// b2Vec2 p = m_body->GetWorldPoint(b2Vec2(0.0f, 3.0f));
// m_body->ApplyForce(f, p, true);
//}
//if (glfwGetKey(g_mainWindow, GLFW_KEY_A) == GLFW_PRESS)
//{
// m_body->ApplyTorque(10.0f, true);
//}
//if (glfwGetKey(g_mainWindow, GLFW_KEY_D) == GLFW_PRESS)
//{
// m_body->ApplyTorque(-10.0f, true);
//}
Test::Step(settings);
}
static Test* Create()
{
return new ApplyForce;
}
b2Body* m_body;
};
static int testIndex = RegisterTest("Forces", "Apply Force", ApplyForce::Create);

View File

@ -0,0 +1,163 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class BodyTypes : public Test
{
public:
BodyTypes()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
ground->CreateFixture(&fd);
}
// Define attachment
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 3.0f);
m_attachment = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
m_attachment->CreateFixture(&shape, 2.0f);
}
// Define platform
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.0f, 5.0f);
m_platform = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f, b2Vec2(4.0f, 0.0f), 0.5f * b2_pi);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
m_platform->CreateFixture(&fd);
b2RevoluteJointDef rjd;
rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));
rjd.maxMotorTorque = 50.0f;
rjd.enableMotor = true;
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, m_platform, b2Vec2(0.0f, 5.0f), b2Vec2(1.0f, 0.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = -10.0f;
pjd.upperTranslation = 10.0f;
pjd.enableLimit = true;
m_world->CreateJoint(&pjd);
m_speed = 3.0f;
}
// Create a payload
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 8.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.75f, 0.75f);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
body->CreateFixture(&fd);
}
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_D:
// m_platform->SetType(b2_dynamicBody);
// break;
// case GLFW_KEY_S:
// m_platform->SetType(b2_staticBody);
// break;
// case GLFW_KEY_K:
// m_platform->SetType(b2_kinematicBody);
// m_platform->SetLinearVelocity(b2Vec2(-m_speed, 0.0f));
// m_platform->SetAngularVelocity(0.0f);
// break;
// }
//}
void Step(Settings* settings) override
{
// Drive the kinematic body.
if (m_platform->GetType() == b2_kinematicBody)
{
b2Vec2 p = m_platform->GetTransform().p;
b2Vec2 v = m_platform->GetLinearVelocity();
if ((p.x < -10.0f && v.x < 0.0f) ||
(p.x > 10.0f && v.x > 0.0f))
{
v.x = -v.x;
m_platform->SetLinearVelocity(v);
}
}
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Keys: (d) dynamic, (s) static, (k) kinematic");
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new BodyTypes;
}
b2Body* m_attachment;
b2Body* m_platform;
float m_speed;
};
static int testIndex = RegisterTest("Examples", "Body Types", BodyTypes::Create);

View File

@ -0,0 +1,174 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
extern B2_API bool g_blockSolve;
class BoxStack : public Test
{
public:
enum
{
e_columnCount = 1,
e_rowCount = 15
//e_columnCount = 1,
//e_rowCount = 1
};
BoxStack()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(b2Vec2(20.0f, 0.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float xs[5] = {0.0f, -10.0f, -5.0f, 5.0f, 10.0f};
for (int32 j = 0; j < e_columnCount; ++j)
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
int32 n = j * e_rowCount + i;
b2Assert(n < e_rowCount * e_columnCount);
m_indices[n] = n;
bd.userData.pointer = n;
float x = 0.0f;
//float x = RandomFloat(-0.02f, 0.02f);
//float x = i % 2 == 0 ? -0.01f : 0.01f;
bd.position.Set(xs[j] + x, 0.55f + 1.1f * i);
b2Body* body = m_world->CreateBody(&bd);
m_bodies[n] = body;
body->CreateFixture(&fd);
}
}
m_bullet = NULL;
}
//void Keyboard(int key) override
//{
// //switch (key)
// //{
// //case GLFW_KEY_COMMA:
// // if (m_bullet != NULL)
// // {
// // m_world->DestroyBody(m_bullet);
// // m_bullet = NULL;
// // }
// // {
// // b2CircleShape shape;
// // shape.m_radius = 0.25f;
// // b2FixtureDef fd;
// // fd.shape = &shape;
// // fd.density = 20.0f;
// // fd.restitution = 0.05f;
// // b2BodyDef bd;
// // bd.type = b2_dynamicBody;
// // bd.bullet = true;
// // bd.position.Set(-31.0f, 5.0f);
// // m_bullet = m_world->CreateBody(&bd);
// // m_bullet->CreateFixture(&fd);
// // m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
// // }
// // break;
// //
// // case GLFW_KEY_B:
// // g_blockSolve = !g_blockSolve;
// // break;
// //}
//}
void Step(Settings* settings) override
{
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Press: (,) to launch a bullet.");
//m_textLine += m_textIncrement;
//g_debugDraw.DrawString(5, m_textLine, "Blocksolve = %d", g_blockSolve);
if (m_stepCount == 300)
{
if (m_bullet != NULL)
{
m_world->DestroyBody(m_bullet);
m_bullet = NULL;
}
{
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.restitution = 0.05f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.bullet = true;
bd.position.Set(-31.0f, 5.0f);
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&fd);
m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
}
}
}
static Test* Create()
{
return new BoxStack;
}
b2Body* m_bullet;
b2Body* m_bodies[e_rowCount * e_columnCount];
int32 m_indices[e_rowCount * e_columnCount];
};
static int testIndex = RegisterTest("Stacking", "Boxes", BoxStack::Create);

View File

@ -0,0 +1,158 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// This is used to test sensor shapes.
class Breakable : public Test
{
public:
enum
{
e_count = 7
};
Breakable()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Breakable dynamic body
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 40.0f);
bd.angle = 0.25f * b2_pi;
m_body1 = m_world->CreateBody(&bd);
m_shape1.SetAsBox(0.5f, 0.5f, b2Vec2(-0.5f, 0.0f), 0.0f);
m_piece1 = m_body1->CreateFixture(&m_shape1, 1.0f);
m_shape2.SetAsBox(0.5f, 0.5f, b2Vec2(0.5f, 0.0f), 0.0f);
m_piece2 = m_body1->CreateFixture(&m_shape2, 1.0f);
}
m_break = false;
m_broke = false;
}
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override
{
if (m_broke)
{
// The body already broke.
return;
}
// Should the body break?
int32 count = contact->GetManifold()->pointCount;
float maxImpulse = 0.0f;
for (int32 i = 0; i < count; ++i)
{
maxImpulse = b2Max(maxImpulse, impulse->normalImpulses[i]);
}
if (maxImpulse > 40.0f)
{
// Flag the body for breaking.
m_break = true;
}
}
void Break()
{
// Create two bodies from one.
b2Body* body1 = m_piece1->GetBody();
b2Vec2 center = body1->GetWorldCenter();
body1->DestroyFixture(m_piece2);
m_piece2 = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = body1->GetPosition();
bd.angle = body1->GetAngle();
b2Body* body2 = m_world->CreateBody(&bd);
m_piece2 = body2->CreateFixture(&m_shape2, 1.0f);
// Compute consistent velocities for new bodies based on
// cached velocity.
b2Vec2 center1 = body1->GetWorldCenter();
b2Vec2 center2 = body2->GetWorldCenter();
b2Vec2 velocity1 = m_velocity + b2Cross(m_angularVelocity, center1 - center);
b2Vec2 velocity2 = m_velocity + b2Cross(m_angularVelocity, center2 - center);
body1->SetAngularVelocity(m_angularVelocity);
body1->SetLinearVelocity(velocity1);
body2->SetAngularVelocity(m_angularVelocity);
body2->SetLinearVelocity(velocity2);
}
void Step(Settings* settings) override
{
if (m_break)
{
Break();
m_broke = true;
m_break = false;
}
// Cache velocities to improve movement on breakage.
if (m_broke == false)
{
m_velocity = m_body1->GetLinearVelocity();
m_angularVelocity = m_body1->GetAngularVelocity();
}
Test::Step(settings);
}
static Test* Create()
{
return new Breakable;
}
b2Body* m_body1;
b2Vec2 m_velocity;
float m_angularVelocity;
b2PolygonShape m_shape1;
b2PolygonShape m_shape2;
b2Fixture* m_piece1;
b2Fixture* m_piece2;
bool m_broke;
bool m_break;
};
static int testIndex = RegisterTest("Examples", "Breakable", Breakable::Create);

View File

@ -0,0 +1,128 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Bridge : public Test
{
public:
enum
{
e_count = 30
};
Bridge()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
if (i == (e_count >> 1))
{
m_middle = body;
}
prevBody = body;
}
b2Vec2 anchor(-15.0f + 1.0f * e_count, 5.0f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 3; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Bridge;
}
b2Body* m_middle;
};
static int testIndex = RegisterTest("Joints", "Bridge", Bridge::Create);

View File

@ -0,0 +1,139 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class BulletTest : public Test
{
public:
BulletTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 4.0f);
b2PolygonShape box;
box.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&box, 1.0f);
box.SetAsBox(0.25f, 0.25f);
//m_x = RandomFloat(-1.0f, 1.0f);
m_x = 0.20352793f;
bd.position.Set(m_x, 10.0f);
bd.bullet = true;
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&box, 100.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 4.0f), 0.0f);
m_body->SetLinearVelocity(b2Vec2_zero);
m_body->SetAngularVelocity(0.0f);
m_x = RandomFloat(-1.0f, 1.0f);
m_bullet->SetTransform(b2Vec2(m_x, 10.0f), 0.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_bullet->SetAngularVelocity(0.0f);
extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern B2_API int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters;
extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
b2_gjkCalls = 0;
b2_gjkIters = 0;
b2_gjkMaxIters = 0;
b2_toiCalls = 0;
b2_toiIters = 0;
b2_toiMaxIters = 0;
b2_toiRootIters = 0;
b2_toiMaxRootIters = 0;
}
void Step(Settings* settings) override
{
Test::Step(settings);
extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern B2_API int32 b2_toiCalls, b2_toiIters;
extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
//if (b2_gjkCalls > 0)
//{
// g_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
// b2_gjkCalls, b2_gjkIters / float(b2_gjkCalls), b2_gjkMaxIters);
// m_textLine += m_textIncrement;
//}
//if (b2_toiCalls > 0)
//{
// g_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
// b2_toiCalls, b2_toiIters / float(b2_toiCalls), b2_toiMaxRootIters);
// m_textLine += m_textIncrement;
// g_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
// b2_toiRootIters / float(b2_toiCalls), b2_toiMaxRootIters);
// m_textLine += m_textIncrement;
//}
if (m_stepCount % 60 == 0)
{
Launch();
}
}
static Test* Create()
{
return new BulletTest;
}
b2Body* m_body;
b2Body* m_bullet;
float m_x;
};
static int testIndex = RegisterTest("Continuous", "Bullet Test", BulletTest::Create);

View File

@ -0,0 +1,218 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// It is difficult to make a cantilever made of links completely rigid with weld joints.
// You will have to use a high number of iterations to make them stiff.
// So why not go ahead and use soft weld joints? They behave like a revolute
// joint with a rotational spring.
class Cantilever : public Test
{
public:
enum
{
e_count = 8
};
Cantilever()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
float frequencyHz = 5.0f;
float dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < 3; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.0f + 2.0f * i, 15.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 2.0f * i, 15.0f);
jd.Initialize(prevBody, body, anchor);
b2AngularStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(-5.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
float frequencyHz = 8.0f;
float dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.5f + 1.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(5.0f + 1.0f * i, 10.0f);
jd.Initialize(prevBody, body, anchor);
b2AngularStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, prevBody, body);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 2; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Cantilever;
}
b2Body* m_middle;
};
static int testIndex = RegisterTest("Joints", "Cantilever", Cantilever::Create);

View File

@ -0,0 +1,284 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// This is a fun demo that shows off the wheel joint
class Car : public Test
{
public:
Car()
{
m_speed = 50.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 0.0f;
fd.friction = 0.6f;
shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&fd);
float hs[10] = {0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f};
float x = 20.0f, y1 = 0.0f, dx = 5.0f;
for (int32 i = 0; i < 10; ++i)
{
float y2 = hs[i];
shape.SetTwoSided(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
for (int32 i = 0; i < 10; ++i)
{
float y2 = hs[i];
shape.SetTwoSided(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 80.0f;
shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 10.0f, 5.0f));
ground->CreateFixture(&fd);
x += 20.0f;
shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x, 20.0f));
ground->CreateFixture(&fd);
}
// Teeter
{
b2BodyDef bd;
bd.position.Set(140.0f, 1.0f);
bd.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(10.0f, 0.25f);
body->CreateFixture(&box, 1.0f);
b2RevoluteJointDef jd;
jd.Initialize(ground, body, body->GetPosition());
jd.lowerAngle = -8.0f * b2_pi / 180.0f;
jd.upperAngle = 8.0f * b2_pi / 180.0f;
jd.enableLimit = true;
m_world->CreateJoint(&jd);
body->ApplyAngularImpulse(100.0f, true);
}
// Bridge
{
int32 N = 20;
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.6f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(161.0f + 2.0f * i, -0.125f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(160.0f + 2.0f * i, -0.125f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
b2Vec2 anchor(160.0f + 2.0f * N, -0.125f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
// Boxes
{
b2PolygonShape box;
box.SetAsBox(0.5f, 0.5f);
b2Body* body = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(230.0f, 0.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 1.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 2.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 3.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 4.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
}
// Car
{
b2PolygonShape chassis;
b2Vec2 vertices[8];
vertices[0].Set(-1.5f, -0.5f);
vertices[1].Set(1.5f, -0.5f);
vertices[2].Set(1.5f, 0.0f);
vertices[3].Set(0.0f, 0.9f);
vertices[4].Set(-1.15f, 0.9f);
vertices[5].Set(-1.5f, 0.2f);
chassis.Set(vertices, 6);
b2CircleShape circle;
circle.m_radius = 0.4f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 1.0f);
m_car = m_world->CreateBody(&bd);
m_car->CreateFixture(&chassis, 1.0f);
b2FixtureDef fd;
fd.shape = &circle;
fd.density = 1.0f;
fd.friction = 0.9f;
bd.position.Set(-1.0f, 0.35f);
m_wheel1 = m_world->CreateBody(&bd);
m_wheel1->CreateFixture(&fd);
bd.position.Set(1.0f, 0.4f);
m_wheel2 = m_world->CreateBody(&bd);
m_wheel2->CreateFixture(&fd);
b2WheelJointDef jd;
b2Vec2 axis(0.0f, 1.0f);
float mass1 = m_wheel1->GetMass();
float mass2 = m_wheel2->GetMass();
float hertz = 4.0f;
float dampingRatio = 0.7f;
float omega = 2.0f * b2_pi * hertz;
jd.Initialize(m_car, m_wheel1, m_wheel1->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 20.0f;
jd.enableMotor = true;
jd.stiffness = mass1 * omega * omega;
jd.damping = 2.0f * mass1 * dampingRatio * omega;
jd.lowerTranslation = -0.25f;
jd.upperTranslation = 0.25f;
jd.enableLimit = true;
m_spring1 = (b2WheelJoint*)m_world->CreateJoint(&jd);
jd.Initialize(m_car, m_wheel2, m_wheel2->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 10.0f;
jd.enableMotor = false;
jd.stiffness = mass2 * omega * omega;
jd.damping = 2.0f * mass2 * dampingRatio * omega;
jd.lowerTranslation = -0.25f;
jd.upperTranslation = 0.25f;
jd.enableLimit = true;
m_spring2 = (b2WheelJoint*)m_world->CreateJoint(&jd);
}
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_A:
// m_spring1->SetMotorSpeed(m_speed);
// break;
// case GLFW_KEY_S:
// m_spring1->SetMotorSpeed(0.0f);
// break;
// case GLFW_KEY_D:
// m_spring1->SetMotorSpeed(-m_speed);
// break;
// }
//}
void Step(Settings* settings) override
{
//g_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, hz down = q, hz up = e");
//m_textLine += m_textIncrement;
//g_camera.m_center.x = m_car->GetPosition().x;
Test::Step(settings);
}
static Test* Create()
{
return new Car;
}
b2Body* m_car;
b2Body* m_wheel1;
b2Body* m_wheel2;
float m_speed;
b2WheelJoint* m_spring1;
b2WheelJoint* m_spring2;
};
static int testIndex = RegisterTest("Examples", "Car", Car::Create);

View File

@ -0,0 +1,92 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
#define TEST_BAD_BODY 0
class Chain : public Test
{
public:
Chain()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.6f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const float y = 25.0f;
b2Body* prevBody = ground;
for (int32 i = 0; i < 30; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + i, y);
b2Body* body = m_world->CreateBody(&bd);
#if TEST_BAD_BODY == 1
if (i == 10)
{
// Test zero density dynamic body
fd.density = 0.0f;
}
else
{
fd.density = 20.0f;
}
#endif
body->CreateFixture(&fd);
b2Vec2 anchor(float(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
}
static Test* Create()
{
return new Chain;
}
};
static int testIndex = RegisterTest("Joints", "Chain", Chain::Create);

View File

@ -0,0 +1,94 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class ChainProblem : public Test
{
public:
ChainProblem()
{
{
b2Vec2 g(0.0f, -10.0f);
m_world->SetGravity(g);
b2Body** bodies = (b2Body**)b2Alloc(2 * sizeof(b2Body*));
b2Joint** joints = (b2Joint**)b2Alloc(0 * sizeof(b2Joint*));
{
b2BodyDef bd;
bd.type = b2BodyType(0);
bodies[0] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
b2Vec2 v1(0.0f, 1.0f);
b2Vec2 v2(0.0f, 0.0f);
b2Vec2 v3(4.0f, 0.0f);
b2EdgeShape shape;
shape.SetTwoSided(v1, v2);
bodies[0]->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v2, v3);
bodies[0]->CreateFixture(&shape, 0.0f);
}
}
{
b2BodyDef bd;
bd.type = b2BodyType(2);
//bd.position.Set(6.033980250358582e-01f, 3.028350114822388e+00f);
bd.position.Set(1.0f, 3.0f);
bodies[1] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 0.2f;
fd.density = 10.0f;
b2PolygonShape shape;
b2Vec2 vs[8];
vs[0].Set(0.5f, -3.0f);
vs[1].Set(0.5f, 3.0f);
vs[2].Set(-0.5f, 3.0f);
vs[3].Set(-0.5f, -3.0f);
shape.Set(vs, 4);
fd.shape = &shape;
bodies[1]->CreateFixture(&fd);
}
}
b2Free(joints);
b2Free(bodies);
joints = NULL;
bodies = NULL;
}
}
static Test* Create()
{
return new ChainProblem;
}
};
static int testIndex = RegisterTest("Bugs", "Chain Problem", ChainProblem::Create);

View File

@ -0,0 +1,256 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
/// This is a test of typical character collision scenarios. This does not
/// show how you should implement a character in your application.
/// Instead this is used to test smooth collision on edge chains.
class CharacterCollision : public Test
{
public:
CharacterCollision()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Collinear edges with no adjacency information.
// This shows the problematic case where a box shape can hit
// an internal vertex.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-8.0f, 1.0f), b2Vec2(-6.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(b2Vec2(-6.0f, 1.0f), b2Vec2(-4.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(b2Vec2(-4.0f, 1.0f), b2Vec2(-2.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Chain shape
{
b2BodyDef bd;
bd.angle = 0.25f * b2_pi;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(5.0f, 7.0f);
vs[1].Set(6.0f, 8.0f);
vs[2].Set(7.0f, 8.0f);
vs[3].Set(8.0f, 7.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Square tiles. This shows that adjacency shapes may
// have non-smooth collision. There is no solution
// to this problem.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(1.0f, 1.0f, b2Vec2(4.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(6.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(8.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
}
// Square made from an edge loop. Collision should be smooth.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(-1.0f, 3.0f);
vs[1].Set(1.0f, 3.0f);
vs[2].Set(1.0f, 5.0f);
vs[3].Set(-1.0f, 5.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Edge loop. Collision should be smooth.
{
b2BodyDef bd;
bd.position.Set(-10.0f, 4.0f);
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[10];
vs[0].Set(0.0f, 0.0f);
vs[1].Set(6.0f, 0.0f);
vs[2].Set(6.0f, 2.0f);
vs[3].Set(4.0f, 1.0f);
vs[4].Set(2.0f, 2.0f);
vs[5].Set(0.0f, 2.0f);
vs[6].Set(-2.0f, 2.0f);
vs[7].Set(-4.0f, 3.0f);
vs[8].Set(-6.0f, 2.0f);
vs[9].Set(-6.0f, 0.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 10);
ground->CreateFixture(&shape, 0.0f);
}
// Square character 1
{
b2BodyDef bd;
bd.position.Set(-3.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Square character 2
{
b2BodyDef bd;
bd.position.Set(-5.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Hexagon character
{
b2BodyDef bd;
bd.position.Set(-5.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
float angle = 0.0f;
float delta = b2_pi / 3.0f;
b2Vec2 vertices[6];
for (int32 i = 0; i < 6; ++i)
{
vertices[i].Set(0.5f * cosf(angle), 0.5f * sinf(angle));
angle += delta;
}
b2PolygonShape shape;
shape.Set(vertices, 6);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(3.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(-7.0f, 6.0f);
bd.type = b2_dynamicBody;
bd.allowSleep = false;
m_character = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 1.0f;
m_character->CreateFixture(&fd);
}
}
void Step(Settings* settings) override
{
b2Vec2 v = m_character->GetLinearVelocity();
v.x = -5.0f;
m_character->SetLinearVelocity(v);
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "This tests various character collision shapes.");
//m_textLine += m_textIncrement;
//g_debugDraw.DrawString(5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes.");
//m_textLine += m_textIncrement;
//g_debugDraw.DrawString(5, m_textLine, "Feature: edge chains have smooth collision inside and out.");
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new CharacterCollision;
}
b2Body* m_character;
};
static int testIndex = RegisterTest("Examples", "Character Collision", CharacterCollision::Create);

View File

@ -0,0 +1,89 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class CircleStack : public Test
{
public:
enum
{
e_count = 10
};
CircleStack()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0, 4.0f + 3.0f * i);
m_bodies[i] = m_world->CreateBody(&bd);
m_bodies[i]->CreateFixture(&shape, 1.0f);
m_bodies[i]->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
}
void Step(Settings* settings) override
{
Test::Step(settings);
//for (int32 i = 0; i < e_count; ++i)
//{
// printf("%g ", m_bodies[i]->GetWorldCenter().y);
//}
//for (int32 i = 0; i < e_count; ++i)
//{
// printf("%g ", m_bodies[i]->GetLinearVelocity().y);
//}
//printf("\n");
}
static Test* Create()
{
return new CircleStack;
}
b2Body* m_bodies[e_count];
};
static int testIndex = RegisterTest("Stacking", "Circles", CircleStack::Create);

View File

@ -0,0 +1,179 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// This is a test of collision filtering.
// There is a triangle, a box, and a circle.
// There are 6 shapes. 3 large and 3 small.
// The 3 small ones always collide.
// The 3 large ones never collide.
// The boxes don't collide with triangles (except if both are small).
const int16 k_smallGroup = 1;
const int16 k_largeGroup = -1;
const uint16 k_triangleCategory = 0x0002;
const uint16 k_boxCategory = 0x0004;
const uint16 k_circleCategory = 0x0008;
const uint16 k_triangleMask = 0xFFFF;
const uint16 k_boxMask = 0xFFFF ^ k_triangleCategory;
const uint16 k_circleMask = 0xFFFF;
class CollisionFiltering : public Test
{
public:
CollisionFiltering()
{
// Ground body
{
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;
sd.friction = 0.3f;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
triangleShapeDef.filter.groupIndex = k_smallGroup;
triangleShapeDef.filter.categoryBits = k_triangleCategory;
triangleShapeDef.filter.maskBits = k_triangleMask;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(-5.0f, 2.0f);
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleShapeDef.filter.groupIndex = k_largeGroup;
triangleBodyDef.position.Set(-5.0f, 6.0f);
triangleBodyDef.fixedRotation = true; // look at me!
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape p;
p.SetAsBox(0.5f, 1.0f);
body->CreateFixture(&p, 1.0f);
b2PrismaticJointDef jd;
jd.bodyA = body2;
jd.bodyB = body;
jd.enableLimit = true;
jd.localAnchorA.Set(0.0f, 4.0f);
jd.localAnchorB.SetZero();
jd.localAxisA.Set(0.0f, 1.0f);
jd.lowerTranslation = -1.0f;
jd.upperTranslation = 1.0f;
m_world->CreateJoint(&jd);
}
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
boxShapeDef.restitution = 0.1f;
boxShapeDef.filter.groupIndex = k_smallGroup;
boxShapeDef.filter.categoryBits = k_boxCategory;
boxShapeDef.filter.maskBits = k_boxMask;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(0.0f, 2.0f);
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxShapeDef.filter.groupIndex = k_largeGroup;
boxBodyDef.position.Set(0.0f, 6.0f);
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
circleShapeDef.filter.groupIndex = k_smallGroup;
circleShapeDef.filter.categoryBits = k_circleCategory;
circleShapeDef.filter.maskBits = k_circleMask;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(5.0f, 2.0f);
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleShapeDef.filter.groupIndex = k_largeGroup;
circleBodyDef.position.Set(5.0f, 6.0f);
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
static Test* Create()
{
return new CollisionFiltering;
}
};
static int testIndex = RegisterTest("Examples", "Collision Filtering", CollisionFiltering::Create);

View File

@ -0,0 +1,191 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
#include <algorithm>
// This test shows collision processing and tests
// deferred body destruction.
class CollisionProcessing : public Test
{
public:
CollisionProcessing()
{
// Ground body
{
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
float xLo = -5.0f, xHi = 5.0f;
float yLo = 2.0f, yHi = 35.0f;
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
void Step(Settings* settings) override
{
Test::Step(settings);
// We are going to destroy some bodies according to contact
// points. We must buffer the bodies that should be destroyed
// because they may belong to multiple contact points.
const int32 k_maxNuke = 6;
b2Body* nuke[k_maxNuke];
int32 nukeCount = 0;
// Traverse the contact results. Destroy bodies that
// are touching heavier bodies.
for (int32 i = 0; i < m_pointCount; ++i)
{
ContactPoint* point = m_points + i;
b2Body* body1 = point->fixtureA->GetBody();
b2Body* body2 = point->fixtureB->GetBody();
float mass1 = body1->GetMass();
float mass2 = body2->GetMass();
if (mass1 > 0.0f && mass2 > 0.0f)
{
if (mass2 > mass1)
{
nuke[nukeCount++] = body1;
}
else
{
nuke[nukeCount++] = body2;
}
if (nukeCount == k_maxNuke)
{
break;
}
}
}
// Sort the nuke array to group duplicates.
std::sort(nuke, nuke + nukeCount);
// Destroy the bodies, skipping duplicates.
int32 i = 0;
while (i < nukeCount)
{
b2Body* b = nuke[i++];
while (i < nukeCount && nuke[i] == b)
{
++i;
}
if (b != m_bomb)
{
m_world->DestroyBody(b);
}
}
}
static Test* Create()
{
return new CollisionProcessing;
}
};
static int testIndex = RegisterTest("Examples", "Collision Processing", CollisionProcessing::Create);

View File

@ -0,0 +1,227 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
//#include "imgui/imgui.h"
class CompoundShapes : public Test
{
public:
CompoundShapes()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
body->CreateFixture(&shape, 0.0f);
}
// Table 1
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-15.0f, 1.0f);
m_table1 = m_world->CreateBody(&bd);
b2PolygonShape top;
top.SetAsBox(3.0f, 0.5f, b2Vec2(0.0f, 3.5f), 0.0f);
b2PolygonShape leftLeg;
leftLeg.SetAsBox(0.5f, 1.5f, b2Vec2(-2.5f, 1.5f), 0.0f);
b2PolygonShape rightLeg;
rightLeg.SetAsBox(0.5f, 1.5f, b2Vec2(2.5f, 1.5f), 0.0f);
m_table1->CreateFixture(&top, 2.0f);
m_table1->CreateFixture(&leftLeg, 2.0f);
m_table1->CreateFixture(&rightLeg, 2.0f);
}
// Table 2
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 1.0f);
m_table2 = m_world->CreateBody(&bd);
b2PolygonShape top;
top.SetAsBox(3.0f, 0.5f, b2Vec2(0.0f, 3.5f), 0.0f);
b2PolygonShape leftLeg;
leftLeg.SetAsBox(0.5f, 2.0f, b2Vec2(-2.5f, 2.0f), 0.0f);
b2PolygonShape rightLeg;
rightLeg.SetAsBox(0.5f, 2.0f, b2Vec2(2.5f, 2.0f), 0.0f);
m_table2->CreateFixture(&top, 2.0f);
m_table2->CreateFixture(&leftLeg, 2.0f);
m_table2->CreateFixture(&rightLeg, 2.0f);
}
// Spaceship 1
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.0f, 1.0f);
m_ship1 = m_world->CreateBody(&bd);
b2Vec2 vertices[3];
b2PolygonShape left;
vertices[0].Set(-2.0f, 0.0f);
vertices[1].Set(0.0f, 4.0f / 3.0f);
vertices[2].Set(0.0f, 4.0f);
left.Set(vertices, 3);
b2PolygonShape right;
vertices[0].Set(2.0f, 0.0f);
vertices[1].Set(0.0f, 4.0f / 3.0f);
vertices[2].Set(0.0f, 4.0f);
right.Set(vertices, 3);
m_ship1->CreateFixture(&left, 2.0f);
m_ship1->CreateFixture(&right, 2.0f);
}
// Spaceship 2
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(15.0f, 1.0f);
m_ship2 = m_world->CreateBody(&bd);
b2Vec2 vertices[3];
b2PolygonShape left;
vertices[0].Set(-2.0f, 0.0f);
vertices[1].Set(1.0f, 2.0f);
vertices[2].Set(0.0f, 4.0f);
left.Set(vertices, 3);
b2PolygonShape right;
vertices[0].Set(2.0f, 0.0f);
vertices[1].Set(-1.0f, 2.0f);
vertices[2].Set(0.0f, 4.0f);
right.Set(vertices, 3);
m_ship2->CreateFixture(&left, 2.0f);
m_ship2->CreateFixture(&right, 2.0f);
}
}
void Spawn()
{
// Table 1 obstruction
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = m_table1->GetPosition();
bd.angle = m_table1->GetAngle();
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(4.0f, 0.1f, b2Vec2(0.0f, 3.0f), 0.0f);
body->CreateFixture(&box, 2.0f);
}
// Table 2 obstruction
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = m_table2->GetPosition();
bd.angle = m_table2->GetAngle();
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(4.0f, 0.1f, b2Vec2(0.0f, 3.0f), 0.0f);
body->CreateFixture(&box, 2.0f);
}
// Ship 1 obstruction
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = m_ship1->GetPosition();
bd.angle = m_ship1->GetAngle();
bd.gravityScale = 0.0f;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape circle;
circle.m_radius = 0.5f;
circle.m_p.Set(0.0f, 2.0f);
body->CreateFixture(&circle, 2.0f);
}
// Ship 2 obstruction
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = m_ship2->GetPosition();
bd.angle = m_ship2->GetAngle();
bd.gravityScale = 0.0f;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape circle;
circle.m_radius = 0.5f;
circle.m_p.Set(0.0f, 2.0f);
body->CreateFixture(&circle, 2.0f);
}
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
// ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// if (ImGui::Button("Spawn"))
// {
// Spawn();
// }
// ImGui::End();
//}
static Test* Create()
{
return new CompoundShapes;
}
b2Body* m_table1;
b2Body* m_table2;
b2Body* m_ship1;
b2Body* m_ship2;
};
static int testIndex = RegisterTest("Examples", "Compound Shapes", CompoundShapes::Create);

View File

@ -0,0 +1,170 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Confined : public Test
{
public:
enum
{
e_columnCount = 0,
e_rowCount = 0
};
Confined()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
// Floor
shape.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
// Left wall
shape.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(-10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Right wall
shape.SetTwoSided(b2Vec2(10.0f, 0.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Roof
shape.SetTwoSided(b2Vec2(-10.0f, 20.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float radius = 0.5f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.1f;
for (int32 j = 0; j < e_columnCount; ++j)
{
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + (2.1f * j + 1.0f + 0.01f * i) * radius, (2.0f * i + 1.0f) * radius);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
}
void CreateCircle()
{
float radius = 2.0f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.0f;
b2Vec2 p(RandomFloat(), 3.0f + RandomFloat());
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p;
//bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_C:
// CreateCircle();
// break;
// }
//}
void Step(Settings* settings) override
{
bool sleeping = true;
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
if (b->IsAwake())
{
sleeping = false;
}
}
if (m_stepCount == 180)
{
m_stepCount += 0;
}
//if (sleeping)
//{
// CreateCircle();
//}
Test::Step(settings);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
b2Vec2 p = b->GetPosition();
if (p.x <= -10.0f || 10.0f <= p.x || p.y <= 0.0f || 20.0f <= p.y)
{
p.x += 0.0f;
}
}
//g_debugDraw.DrawString(5, m_textLine, "Press 'c' to create a circle.");
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new Confined;
}
};
static int testIndex = RegisterTest("Solver", "Confined", Confined::Create);

View File

@ -0,0 +1,160 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class ContinuousTest : public Test
{
public:
ContinuousTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
#if 1
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 20.0f);
//bd.angle = 0.1f;
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&shape, 1.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
//m_angularVelocity = 46.661274f;
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
#else
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 2.0f);
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
bd.bullet = true;
bd.position.Set(0.0f, 10.0f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
}
#endif
extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern B2_API int32 b2_toiCalls, b2_toiIters;
extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
extern B2_API float b2_toiTime, b2_toiMaxTime;
b2_gjkCalls = 0; b2_gjkIters = 0; b2_gjkMaxIters = 0;
b2_toiCalls = 0; b2_toiIters = 0;
b2_toiRootIters = 0; b2_toiMaxRootIters = 0;
b2_toiTime = 0.0f; b2_toiMaxTime = 0.0f;
}
void Launch()
{
extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern B2_API int32 b2_toiCalls, b2_toiIters;
extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
extern B2_API float b2_toiTime, b2_toiMaxTime;
b2_gjkCalls = 0; b2_gjkIters = 0; b2_gjkMaxIters = 0;
b2_toiCalls = 0; b2_toiIters = 0;
b2_toiRootIters = 0; b2_toiMaxRootIters = 0;
b2_toiTime = 0.0f; b2_toiMaxTime = 0.0f;
m_body->SetTransform(b2Vec2(0.0f, 20.0f), 0.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
void Step(Settings* settings) override
{
Test::Step(settings);
extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
if (b2_gjkCalls > 0)
{
//g_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
// b2_gjkCalls, b2_gjkIters / float(b2_gjkCalls), b2_gjkMaxIters);
//m_textLine += m_textIncrement;
}
extern B2_API int32 b2_toiCalls, b2_toiIters;
extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
extern B2_API float b2_toiTime, b2_toiMaxTime;
if (b2_toiCalls > 0)
{
//g_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave [max] toi iters = %3.1f [%d]",
// b2_toiCalls, b2_toiIters / float(b2_toiCalls), b2_toiMaxRootIters);
//m_textLine += m_textIncrement;
//
//g_debugDraw.DrawString(5, m_textLine, "ave [max] toi root iters = %3.1f [%d]",
// b2_toiRootIters / float(b2_toiCalls), b2_toiMaxRootIters);
//m_textLine += m_textIncrement;
//g_debugDraw.DrawString(5, m_textLine, "ave [max] toi time = %.1f [%.1f] (microseconds)",
// 1000.0f * b2_toiTime / float(b2_toiCalls), 1000.0f * b2_toiMaxTime);
//m_textLine += m_textIncrement;
}
if (m_stepCount % 60 == 0)
{
//Launch();
}
}
static Test* Create()
{
return new ContinuousTest;
}
b2Body* m_body;
float m_angularVelocity;
};
static int testIndex = RegisterTest("Continuous", "Continuous Test", ContinuousTest::Create);

View File

@ -0,0 +1,112 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class ConvexHull : public Test
{
public:
enum
{
e_count = b2_maxPolygonVertices
};
ConvexHull()
{
Generate();
m_auto = false;
}
void Generate()
{
b2Vec2 lowerBound(-8.0f, -8.0f);
b2Vec2 upperBound(8.0f, 8.0f);
for (int32 i = 0; i < e_count; ++i)
{
float x = 10.0f * RandomFloat();
float y = 10.0f * RandomFloat();
// Clamp onto a square to help create collinearities.
// This will stress the convex hull algorithm.
b2Vec2 v(x, y);
v = b2Clamp(v, lowerBound, upperBound);
m_points[i] = v;
}
m_count = e_count;
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_A:
// m_auto = !m_auto;
// break;
// case GLFW_KEY_G:
// Generate();
// break;
// }
//}
void Step(Settings* settings) override
{
Test::Step(settings);
b2PolygonShape shape;
shape.Set(m_points, m_count);
//g_debugDraw.DrawString(5, m_textLine, "Press g to generate a new random convex hull");
//m_textLine += m_textIncrement;
// g_debugDraw.DrawPolygon(shape.m_vertices, shape.m_count, b2Color(0.9f, 0.9f, 0.9f));
for (int32 i = 0; i < m_count; ++i)
{
//g_debugDraw.DrawPoint(m_points[i], 3.0f, b2Color(0.3f, 0.9f, 0.3f));
//g_debugDraw.DrawString(m_points[i] + b2Vec2(0.05f, 0.05f), "%d", i);
}
if (shape.Validate() == false)
{
m_textLine += 0;
}
if (m_auto)
{
Generate();
}
}
static Test* Create()
{
return new ConvexHull;
}
b2Vec2 m_points[b2_maxPolygonVertices];
int32 m_count;
bool m_auto;
};
static int testIndex = RegisterTest("Geometry", "Convex Hull", ConvexHull::Create);

View File

@ -0,0 +1,101 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class ConveyorBelt : public Test
{
public:
ConveyorBelt()
{
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Platform
{
b2BodyDef bd;
bd.position.Set(-5.0f, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(10.0f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.8f;
m_platform = body->CreateFixture(&fd);
}
// Boxes
for (int32 i = 0; i < 5; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 2.0f * i, 7.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
body->CreateFixture(&shape, 20.0f);
}
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override
{
Test::PreSolve(contact, oldManifold);
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_platform)
{
contact->SetTangentSpeed(5.0f);
}
if (fixtureB == m_platform)
{
contact->SetTangentSpeed(-5.0f);
}
}
void Step(Settings* settings) override
{
Test::Step(settings);
}
static Test* Create()
{
return new ConveyorBelt;
}
b2Fixture* m_platform;
};
static int testIndex = RegisterTest("Examples", "Conveyor Belt", ConveyorBelt::Create);

View File

@ -0,0 +1,123 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
//#include "imgui/imgui.h"
// This tests distance joints, body destruction, and joint destruction.
class DistanceJoint : public Test
{
public:
DistanceJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.angularDamping = 0.1f;
bd.position.Set(0.0f, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
body->CreateFixture(&shape, 5.0f);
m_hertz = 1.0f;
m_dampingRatio = 0.7f;
b2DistanceJointDef jd;
jd.Initialize(ground, body, b2Vec2(0.0f, 15.0f), bd.position);
jd.collideConnected = true;
m_length = jd.length;
m_minLength = m_length;
m_maxLength = m_length;
b2LinearStiffness(jd.stiffness, jd.damping, m_hertz, m_dampingRatio, jd.bodyA, jd.bodyB);
m_joint = (b2DistanceJoint*)m_world->CreateJoint(&jd);
}
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(260.0f, 150.0f));
// ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// if (ImGui::SliderFloat("Length", &m_length, 0.0f, 20.0f, "%.0f"))
// {
// m_length = m_joint->SetLength(m_length);
// }
// if (ImGui::SliderFloat("Min Length", &m_minLength, 0.0f, 20.0f, "%.0f"))
// {
// m_minLength = m_joint->SetMinLength(m_minLength);
// }
// if (ImGui::SliderFloat("Max Length", &m_maxLength, 0.0f, 20.0f, "%.0f"))
// {
// m_maxLength = m_joint->SetMaxLength(m_maxLength);
// }
// if (ImGui::SliderFloat("Hertz", &m_hertz, 0.0f, 10.0f, "%.1f"))
// {
// float stiffness;
// float damping;
// b2LinearStiffness(stiffness, damping, m_hertz, m_dampingRatio, m_joint->GetBodyA(), m_joint->GetBodyB());
// m_joint->SetStiffness(stiffness);
// m_joint->SetDamping(damping);
// }
// if (ImGui::SliderFloat("Damping Ratio", &m_dampingRatio, 0.0f, 2.0f, "%.1f"))
// {
// float stiffness;
// float damping;
// b2LinearStiffness(stiffness, damping, m_hertz, m_dampingRatio, m_joint->GetBodyA(), m_joint->GetBodyB());
// m_joint->SetStiffness(stiffness);
// m_joint->SetDamping(damping);
// }
// ImGui::End();
//}
static Test* Create()
{
return new DistanceJoint;
}
b2DistanceJoint* m_joint;
float m_length;
float m_minLength;
float m_maxLength;
float m_hertz;
float m_dampingRatio;
};
static int testIndex = RegisterTest("Joints", "Distance Joint", DistanceJoint::Create);

View File

@ -0,0 +1,139 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
#include "box2d/b2_distance.h"
class DistanceTest : public Test
{
public:
DistanceTest()
{
{
m_transformA.SetIdentity();
m_transformA.p.Set(0.0f, -0.2f);
m_polygonA.SetAsBox(10.0f, 0.2f);
}
{
m_positionB.Set(12.017401f, 0.13678508f);
m_angleB = -0.0109265f;
m_transformB.Set(m_positionB, m_angleB);
m_polygonB.SetAsBox(2.0f, 0.1f);
}
}
static Test* Create()
{
return new DistanceTest;
}
void Step(Settings* settings) override
{
Test::Step(settings);
b2DistanceInput input;
input.proxyA.Set(&m_polygonA, 0);
input.proxyB.Set(&m_polygonB, 0);
input.transformA = m_transformA;
input.transformB = m_transformB;
input.useRadii = true;
b2SimplexCache cache;
cache.count = 0;
b2DistanceOutput output;
b2Distance(&output, &cache, &input);
//g_debugDraw.DrawString(5, m_textLine, "distance = %g", output.distance);
//m_textLine += m_textIncrement;
//g_debugDraw.DrawString(5, m_textLine, "iterations = %d", output.iterations);
//m_textLine += m_textIncrement;
{
b2Color color(0.9f, 0.9f, 0.9f);
b2Vec2 v[b2_maxPolygonVertices];
for (int32 i = 0; i < m_polygonA.m_count; ++i)
{
v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
}
// g_debugDraw.DrawPolygon(v, m_polygonA.m_count, color);
for (int32 i = 0; i < m_polygonB.m_count; ++i)
{
v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
}
// g_debugDraw.DrawPolygon(v, m_polygonB.m_count, color);
}
b2Vec2 x1 = output.pointA;
b2Vec2 x2 = output.pointB;
//b2Color c1(1.0f, 0.0f, 0.0f);
//g_debugDraw.DrawPoint(x1, 4.0f, c1);
//b2Color c2(1.0f, 1.0f, 0.0f);
//g_debugDraw.DrawPoint(x2, 4.0f, c2);
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_A:
// m_positionB.x -= 0.1f;
// break;
// case GLFW_KEY_D:
// m_positionB.x += 0.1f;
// break;
// case GLFW_KEY_S:
// m_positionB.y -= 0.1f;
// break;
// case GLFW_KEY_W:
// m_positionB.y += 0.1f;
// break;
// case GLFW_KEY_Q:
// m_angleB += 0.1f * b2_pi;
// break;
// case GLFW_KEY_E:
// m_angleB -= 0.1f * b2_pi;
// break;
// }
// m_transformB.Set(m_positionB, m_angleB);
//}
b2Vec2 m_positionB;
float m_angleB;
b2Transform m_transformA;
b2Transform m_transformB;
b2PolygonShape m_polygonA;
b2PolygonShape m_polygonB;
};
static int testIndex = RegisterTest("Geometry", "Distance Test", DistanceTest::Create);

View File

@ -0,0 +1,220 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Dominos : public Test
{
public:
Dominos()
{
b2Body* b1;
{
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2BodyDef bd;
b1 = m_world->CreateBody(&bd);
b1->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(6.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-1.5f, 10.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.1f, 1.0f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.1f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 1.0f * i, 11.25f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(7.0f, 0.25f, b2Vec2_zero, 0.3f);
b2BodyDef bd;
bd.position.Set(1.0f, 6.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
b2Body* b2;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.5f);
b2BodyDef bd;
bd.position.Set(-7.0f, 4.0f);
b2 = m_world->CreateBody(&bd);
b2->CreateFixture(&shape, 0.0f);
}
b2Body* b3;
{
b2PolygonShape shape;
shape.SetAsBox(6.0f, 0.125f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-0.9f, 1.0f);
bd.angle = -0.15f;
b3 = m_world->CreateBody(&bd);
b3->CreateFixture(&shape, 10.0f);
}
b2RevoluteJointDef jd;
b2Vec2 anchor;
anchor.Set(-2.0f, 1.0f);
jd.Initialize(b1, b3, anchor);
jd.collideConnected = true;
m_world->CreateJoint(&jd);
b2Body* b4;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f, 15.0f);
b4 = m_world->CreateBody(&bd);
b4->CreateFixture(&shape, 10.0f);
}
anchor.Set(-7.0f, 15.0f);
jd.Initialize(b2, b4, anchor);
m_world->CreateJoint(&jd);
b2Body* b5;
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(6.5f, 3.0f);
b5 = m_world->CreateBody(&bd);
b2PolygonShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 10.0f;
fd.friction = 0.1f;
shape.SetAsBox(1.0f, 0.1f, b2Vec2(0.0f, -0.9f), 0.0f);
b5->CreateFixture(&fd);
shape.SetAsBox(0.1f, 1.0f, b2Vec2(-0.9f, 0.0f), 0.0f);
b5->CreateFixture(&fd);
shape.SetAsBox(0.1f, 1.0f, b2Vec2(0.9f, 0.0f), 0.0f);
b5->CreateFixture(&fd);
}
anchor.Set(6.0f, 2.0f);
jd.Initialize(b1, b5, anchor);
m_world->CreateJoint(&jd);
b2Body* b6;
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(6.5f, 4.1f);
b6 = m_world->CreateBody(&bd);
b6->CreateFixture(&shape, 30.0f);
}
anchor.Set(7.5f, 4.0f);
jd.Initialize(b5, b6, anchor);
m_world->CreateJoint(&jd);
b2Body* b7;
{
b2PolygonShape shape;
shape.SetAsBox(0.1f, 1.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(7.4f, 1.0f);
b7 = m_world->CreateBody(&bd);
b7->CreateFixture(&shape, 10.0f);
}
b2DistanceJointDef djd;
djd.bodyA = b3;
djd.bodyB = b7;
djd.localAnchorA.Set(6.0f, 0.0f);
djd.localAnchorB.Set(0.0f, -1.0f);
b2Vec2 d = djd.bodyB->GetWorldPoint(djd.localAnchorB) - djd.bodyA->GetWorldPoint(djd.localAnchorA);
djd.length = d.Length();
b2LinearStiffness(djd.stiffness, djd.damping, 1.0f, 1.0f, djd.bodyA, djd.bodyB);
m_world->CreateJoint(&djd);
{
float radius = 0.2f;
b2CircleShape shape;
shape.m_radius = radius;
for (int32 i = 0; i < 4; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.9f + 2.0f * radius * i, 2.4f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 10.0f);
}
}
}
static Test* Create()
{
return new Dominos;
}
};
static int testIndex = RegisterTest("Examples", "Dominos", Dominos::Create);

View File

@ -0,0 +1,88 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// This test holds worlds dumped using b2World::Dump.
class DumpLoader : public Test
{
public:
DumpLoader()
{
b2ChainShape chainShape;
b2Vec2 vertices[] = {b2Vec2(-5,0), b2Vec2(5,0), b2Vec2(5,5), b2Vec2(4,1), b2Vec2(-4,1), b2Vec2(-5,5)};
chainShape.CreateLoop(vertices, 6);
b2FixtureDef groundFixtureDef;
groundFixtureDef.density = 0;
groundFixtureDef.shape = &chainShape;
b2BodyDef groundBodyDef;
groundBodyDef.type = b2_staticBody;
b2Body *groundBody = m_world->CreateBody(&groundBodyDef);
b2Fixture *groundBodyFixture = groundBody->CreateFixture(&groundFixtureDef);
b2CircleShape ballShape;
ballShape.m_radius = 1;
b2FixtureDef ballFixtureDef;
ballFixtureDef.restitution = 0.75f;
ballFixtureDef.density = 1;
ballFixtureDef.shape = &ballShape;
b2BodyDef ballBodyDef;
ballBodyDef.type = b2BodyType::b2_dynamicBody;
ballBodyDef.position = b2Vec2(0, 10);
// ballBodyDef.angularDamping = 0.2f;
m_ball = m_world->CreateBody(&ballBodyDef);
b2Fixture *ballFixture = m_ball->CreateFixture(&ballFixtureDef);
m_ball->ApplyForceToCenter(b2Vec2(-1000, -400), true);
}
void Step(Settings* settings) override
{
b2Vec2 v = m_ball->GetLinearVelocity();
float omega = m_ball->GetAngularVelocity();
b2MassData massData;
m_ball->GetMassData(&massData);
float ke = 0.5f * massData.mass * b2Dot(v, v) + 0.5f * massData.I * omega * omega;
//g_debugDraw.DrawString(5, m_textLine, "kinetic energy = %.6f", ke);
//m_textLine += m_textIncrement;
Test::Step(settings);
}
static Test* Create()
{
return new DumpLoader;
}
b2Body* m_ball;
};
static int testIndex = RegisterTest("Bugs", "Dump Loader", DumpLoader::Create);

View File

@ -0,0 +1,360 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class DynamicTree : public Test
{
public:
enum
{
e_actorCount = 128
};
DynamicTree()
{
m_worldExtent = 15.0f;
m_proxyExtent = 0.5f;
srand(888);
for (int32 i = 0; i < e_actorCount; ++i)
{
Actor* actor = m_actors + i;
GetRandomAABB(&actor->aabb);
actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
}
m_stepCount = 0;
float h = m_worldExtent;
m_queryAABB.lowerBound.Set(-3.0f, -4.0f + h);
m_queryAABB.upperBound.Set(5.0f, 6.0f + h);
m_rayCastInput.p1.Set(-5.0, 5.0f + h);
m_rayCastInput.p2.Set(7.0f, -4.0f + h);
//m_rayCastInput.p1.Set(0.0f, 2.0f + h);
//m_rayCastInput.p2.Set(0.0f, -2.0f + h);
m_rayCastInput.maxFraction = 1.0f;
m_automated = false;
}
static Test* Create()
{
return new DynamicTree;
}
void Step(Settings* settings) override
{
B2_NOT_USED(settings);
m_rayActor = NULL;
for (int32 i = 0; i < e_actorCount; ++i)
{
m_actors[i].fraction = 1.0f;
m_actors[i].overlap = false;
}
if (m_automated == true)
{
int32 actionCount = b2Max(1, e_actorCount >> 2);
for (int32 i = 0; i < actionCount; ++i)
{
Action();
}
}
Query();
RayCast();
for (int32 i = 0; i < e_actorCount; ++i)
{
Actor* actor = m_actors + i;
if (actor->proxyId == b2_nullNode)
continue;
b2Color c(0.9f, 0.9f, 0.9f);
if (actor == m_rayActor && actor->overlap)
{
c.Set(0.9f, 0.6f, 0.6f);
}
else if (actor == m_rayActor)
{
c.Set(0.6f, 0.9f, 0.6f);
}
else if (actor->overlap)
{
c.Set(0.6f, 0.6f, 0.9f);
}
//g_debugDraw.DrawAABB(&actor->aabb, c);
}
//b2Color c(0.7f, 0.7f, 0.7f);
//g_debugDraw.DrawAABB(&m_queryAABB, c);
//g_debugDraw.DrawSegment(m_rayCastInput.p1, m_rayCastInput.p2, c);
//b2Color c1(0.2f, 0.9f, 0.2f);
//b2Color c2(0.9f, 0.2f, 0.2f);
//g_debugDraw.DrawPoint(m_rayCastInput.p1, 6.0f, c1);
//g_debugDraw.DrawPoint(m_rayCastInput.p2, 6.0f, c2);
if (m_rayActor)
{
//b2Color cr(0.2f, 0.2f, 0.9f);
//b2Vec2 p = m_rayCastInput.p1 + m_rayActor->fraction * (m_rayCastInput.p2 - m_rayCastInput.p1);
//g_debugDraw.DrawPoint(p, 6.0f, cr);
}
{
//int32 height = m_tree.GetHeight();
//g_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d", height);
//m_textLine += m_textIncrement;
}
++m_stepCount;
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_A:
// m_automated = !m_automated;
// break;
// case GLFW_KEY_C:
// CreateProxy();
// break;
// case GLFW_KEY_D:
// DestroyProxy();
// break;
// case GLFW_KEY_M:
// MoveProxy();
// break;
// }
//}
bool QueryCallback(int32 proxyId)
{
Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
actor->overlap = b2TestOverlap(m_queryAABB, actor->aabb);
return true;
}
float RayCastCallback(const b2RayCastInput& input, int32 proxyId)
{
Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
b2RayCastOutput output;
bool hit = actor->aabb.RayCast(&output, input);
if (hit)
{
m_rayCastOutput = output;
m_rayActor = actor;
m_rayActor->fraction = output.fraction;
return output.fraction;
}
return input.maxFraction;
}
private:
struct Actor
{
b2AABB aabb;
float fraction;
bool overlap;
int32 proxyId;
};
void GetRandomAABB(b2AABB* aabb)
{
b2Vec2 w; w.Set(2.0f * m_proxyExtent, 2.0f * m_proxyExtent);
//aabb->lowerBound.x = -m_proxyExtent;
//aabb->lowerBound.y = -m_proxyExtent + m_worldExtent;
aabb->lowerBound.x = RandomFloat(-m_worldExtent, m_worldExtent);
aabb->lowerBound.y = RandomFloat(0.0f, 2.0f * m_worldExtent);
aabb->upperBound = aabb->lowerBound + w;
}
void MoveAABB(b2AABB* aabb)
{
b2Vec2 d;
d.x = RandomFloat(-0.5f, 0.5f);
d.y = RandomFloat(-0.5f, 0.5f);
//d.x = 2.0f;
//d.y = 0.0f;
aabb->lowerBound += d;
aabb->upperBound += d;
b2Vec2 c0 = 0.5f * (aabb->lowerBound + aabb->upperBound);
b2Vec2 min; min.Set(-m_worldExtent, 0.0f);
b2Vec2 max; max.Set(m_worldExtent, 2.0f * m_worldExtent);
b2Vec2 c = b2Clamp(c0, min, max);
aabb->lowerBound += c - c0;
aabb->upperBound += c - c0;
}
void CreateProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId == b2_nullNode)
{
GetRandomAABB(&actor->aabb);
actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
return;
}
}
}
void DestroyProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId != b2_nullNode)
{
m_tree.DestroyProxy(actor->proxyId);
actor->proxyId = b2_nullNode;
return;
}
}
}
void MoveProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId == b2_nullNode)
{
continue;
}
b2AABB aabb0 = actor->aabb;
MoveAABB(&actor->aabb);
b2Vec2 displacement = actor->aabb.GetCenter() - aabb0.GetCenter();
m_tree.MoveProxy(actor->proxyId, actor->aabb, displacement);
return;
}
}
void Action()
{
int32 choice = rand() % 20;
switch (choice)
{
case 0:
CreateProxy();
break;
case 1:
DestroyProxy();
break;
default:
MoveProxy();
}
}
void Query()
{
m_tree.Query(this, m_queryAABB);
for (int32 i = 0; i < e_actorCount; ++i)
{
if (m_actors[i].proxyId == b2_nullNode)
{
continue;
}
bool overlap = b2TestOverlap(m_queryAABB, m_actors[i].aabb);
B2_NOT_USED(overlap);
b2Assert(overlap == m_actors[i].overlap);
}
}
void RayCast()
{
m_rayActor = NULL;
b2RayCastInput input = m_rayCastInput;
// Ray cast against the dynamic tree.
m_tree.RayCast(this, input);
// Brute force ray cast.
Actor* bruteActor = NULL;
b2RayCastOutput bruteOutput;
for (int32 i = 0; i < e_actorCount; ++i)
{
if (m_actors[i].proxyId == b2_nullNode)
{
continue;
}
b2RayCastOutput output;
bool hit = m_actors[i].aabb.RayCast(&output, input);
if (hit)
{
bruteActor = m_actors + i;
bruteOutput = output;
input.maxFraction = output.fraction;
}
}
if (bruteActor != NULL)
{
b2Assert(bruteOutput.fraction == m_rayCastOutput.fraction);
}
}
float m_worldExtent;
float m_proxyExtent;
b2DynamicTree m_tree;
b2AABB m_queryAABB;
b2RayCastInput m_rayCastInput;
b2RayCastOutput m_rayCastOutput;
Actor* m_rayActor;
Actor m_actors[e_actorCount];
int32 m_stepCount;
bool m_automated;
};
static int testIndex = RegisterTest("Collision", "Dynamic Tree", DynamicTree::Create);

View File

@ -0,0 +1,253 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "settings.h"
#include "../test.h"
class EdgeShapesCallback : public b2RayCastCallback
{
public:
EdgeShapesCallback()
{
m_fixture = NULL;
}
float ReportFixture(b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float fraction) override
{
m_fixture = fixture;
m_point = point;
m_normal = normal;
return fraction;
}
b2Fixture* m_fixture;
b2Vec2 m_point;
b2Vec2 m_normal;
};
class EdgeShapes : public Test
{
public:
enum
{
e_maxBodies = 256
};
EdgeShapes()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
float x1 = -20.0f;
float y1 = 2.0f * cosf(x1 / 10.0f * b2_pi);
for (int32 i = 0; i < 80; ++i)
{
float x2 = x1 + 0.5f;
float y2 = 2.0f * cosf(x2 / 10.0f * b2_pi);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(x1, y1), b2Vec2(x2, y2));
ground->CreateFixture(&shape, 0.0f);
x1 = x2;
y1 = y2;
}
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float w = 1.0f;
float b = w / (2.0f + b2Sqrt(2.0f));
float s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
m_angle = 0.0f;
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
float x = RandomFloat(-10.0f, 10.0f);
float y = RandomFloat(10.0f, 20.0f);
bd.position.Set(x, y);
bd.angle = RandomFloat(-b2_pi, b2_pi);
bd.type = b2_dynamicBody;
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.friction = 0.3f;
fd.density = 20.0f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.friction = 0.3f;
fd.density = 20.0f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_1:
// case GLFW_KEY_2:
// case GLFW_KEY_3:
// case GLFW_KEY_4:
// case GLFW_KEY_5:
// Create(key - GLFW_KEY_1);
// break;
// case GLFW_KEY_D:
// DestroyBody();
// break;
// }
//}
void Step(Settings* settings) override
{
bool advanceRay = 0.1;// settings.m_pause == 0 || settings.m_singleStep;
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
//m_textLine += m_textIncrement;
float L = 25.0f;
b2Vec2 point1(0.0f, 10.0f);
b2Vec2 d(L * cosf(m_angle), -L * b2Abs(sinf(m_angle)));
b2Vec2 point2 = point1 + d;
EdgeShapesCallback callback;
m_world->RayCast(&callback, point1, point2);
//if (callback.m_fixture)
//{
// g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
// g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
// b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
// g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
//}
//else
//{
// g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
//}
if (advanceRay)
{
m_angle += 0.25f * b2_pi / 180.0f;
}
}
static Test* Create()
{
return new EdgeShapes;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
float m_angle;
};
static int testIndex = RegisterTest("Geometry", "Edge Shapes", EdgeShapes::Create);

View File

@ -0,0 +1,282 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
//#include "imgui/imgui.h"
class EdgeTest : public Test
{
public:
EdgeTest()
{
b2Vec2 vertices[10] =
{
{10.0f, -4.0f},
{10.0f, 0.0f},
{6.0f, 0.0f},
{4.0f, 2.0f},
{2.0f, 0.0f},
{-2.0f, 0.0f},
{-6.0f, 0.0f},
{-8.0f, -3.0f},
{-10.0f, 0.0f},
{-10.0f, -4.0f}
};
m_offset1.Set(0.0f, 8.0f);
m_offset2.Set(0.0f, 16.0f);
{
b2Vec2 v1 = vertices[0] + m_offset1;
b2Vec2 v2 = vertices[1] + m_offset1;
b2Vec2 v3 = vertices[2] + m_offset1;
b2Vec2 v4 = vertices[3] + m_offset1;
b2Vec2 v5 = vertices[4] + m_offset1;
b2Vec2 v6 = vertices[5] + m_offset1;
b2Vec2 v7 = vertices[6] + m_offset1;
b2Vec2 v8 = vertices[7] + m_offset1;
b2Vec2 v9 = vertices[8] + m_offset1;
b2Vec2 v10 = vertices[9] + m_offset1;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetOneSided(v10, v1, v2, v3);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v1, v2, v3, v4);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v2, v3, v4, v5);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v3, v4, v5, v6);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v4, v5, v6, v7);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v5, v6, v7, v8);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v6, v7, v8, v9);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v7, v8, v9, v10);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v8, v9, v10, v1);
ground->CreateFixture(&shape, 0.0f);
shape.SetOneSided(v9, v10, v1, v2);
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 v1 = vertices[0] + m_offset2;
b2Vec2 v2 = vertices[1] + m_offset2;
b2Vec2 v3 = vertices[2] + m_offset2;
b2Vec2 v4 = vertices[3] + m_offset2;
b2Vec2 v5 = vertices[4] + m_offset2;
b2Vec2 v6 = vertices[5] + m_offset2;
b2Vec2 v7 = vertices[6] + m_offset2;
b2Vec2 v8 = vertices[7] + m_offset2;
b2Vec2 v9 = vertices[8] + m_offset2;
b2Vec2 v10 = vertices[9] + m_offset2;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(v1, v2);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v2, v3);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v3, v4);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v4, v5);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v5, v6);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v6, v7);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v7, v8);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v8, v9);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v9, v10);
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(v10, v1);
ground->CreateFixture(&shape, 0.0f);
}
m_body1 = nullptr;
m_body2 = nullptr;
CreateBoxes();
m_boxes = true;
}
void CreateBoxes()
{
if (m_body1)
{
m_world->DestroyBody(m_body1);
m_body1 = nullptr;
}
if (m_body2)
{
m_world->DestroyBody(m_body2);
m_body2 = nullptr;
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(8.0f, 2.6f) + m_offset1;
bd.allowSleep = false;
m_body1 = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 1.0f);
m_body1->CreateFixture(&shape, 1.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(8.0f, 2.6f) + m_offset2;
bd.allowSleep = false;
m_body2 = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 1.0f);
m_body2->CreateFixture(&shape, 1.0f);
}
}
void CreateCircles()
{
if (m_body1)
{
m_world->DestroyBody(m_body1);
m_body1 = nullptr;
}
if (m_body2)
{
m_world->DestroyBody(m_body2);
m_body2 = nullptr;
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(-0.5f, 0.6f) + m_offset1;
bd.allowSleep = false;
m_body1 = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
m_body1->CreateFixture(&shape, 1.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(-0.5f, 0.6f) + m_offset2;
bd.allowSleep = false;
m_body2 = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
m_body2->CreateFixture(&shape, 1.0f);
}
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
// ImGui::Begin("Custom Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// if (ImGui::RadioButton("Boxes", m_boxes == true))
// {
// CreateBoxes();
// m_boxes = true;
// }
// if (ImGui::RadioButton("Circles", m_boxes == false))
// {
// CreateCircles();
// m_boxes = false;
// }
// ImGui::End();
//}
void Step(Settings* settings) override
{
//if (glfwGetKey(g_mainWindow, GLFW_KEY_A) == GLFW_PRESS)
//{
// m_body1->ApplyForceToCenter(b2Vec2(-10.0f, 0.0f), true);
// m_body2->ApplyForceToCenter(b2Vec2(-10.0f, 0.0f), true);
//}
//if (glfwGetKey(g_mainWindow, GLFW_KEY_D) == GLFW_PRESS)
//{
// m_body1->ApplyForceToCenter(b2Vec2(10.0f, 0.0f), true);
// m_body2->ApplyForceToCenter(b2Vec2(10.0f, 0.0f), true);
//}
Test::Step(settings);
}
static Test* Create()
{
return new EdgeTest;
}
b2Vec2 m_offset1, m_offset2;
b2Body* m_body1;
b2Body* m_body2;
bool m_boxes;
};
static int testIndex = RegisterTest("Geometry", "Edge Test", EdgeTest::Create);

View File

@ -0,0 +1,127 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Friction : public Test
{
public:
Friction()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-4.0f, 22.0f);
bd.angle = -0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.0f);
b2BodyDef bd;
bd.position.Set(10.5f, 19.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(4.0f, 14.0f);
bd.angle = 0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.0f);
b2BodyDef bd;
bd.position.Set(-10.5f, 11.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-4.0f, 6.0f);
bd.angle = -0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 25.0f;
float friction[5] = {0.75f, 0.5f, 0.35f, 0.1f, 0.0f};
for (int i = 0; i < 5; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-15.0f + 4.0f * i, 28.0f);
b2Body* body = m_world->CreateBody(&bd);
fd.friction = friction[i];
body->CreateFixture(&fd);
}
}
}
static Test* Create()
{
return new Friction;
}
};
static int testIndex = RegisterTest("Forces", "Friction", Friction::Create);

View File

@ -0,0 +1,180 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class GearJoint : public Test
{
public:
GearJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape circle1;
circle1.m_radius = 1.0f;
b2PolygonShape box;
box.SetAsBox(0.5f, 5.0f);
b2CircleShape circle2;
circle2.m_radius = 2.0f;
b2BodyDef bd1;
bd1.type = b2_staticBody;
bd1.position.Set(10.0f, 9.0f);
b2Body* body1 = m_world->CreateBody(&bd1);
body1->CreateFixture(&circle1, 5.0f);
b2BodyDef bd2;
bd2.type = b2_dynamicBody;
bd2.position.Set(10.0f, 8.0f);
b2Body* body2 = m_world->CreateBody(&bd2);
body2->CreateFixture(&box, 5.0f);
b2BodyDef bd3;
bd3.type = b2_dynamicBody;
bd3.position.Set(10.0f, 6.0f);
b2Body* body3 = m_world->CreateBody(&bd3);
body3->CreateFixture(&circle2, 5.0f);
b2RevoluteJointDef jd1;
jd1.Initialize(body1, body2, bd1.position);
b2Joint* joint1 = m_world->CreateJoint(&jd1);
b2RevoluteJointDef jd2;
jd2.Initialize(body2, body3, bd3.position);
b2Joint* joint2 = m_world->CreateJoint(&jd2);
b2GearJointDef jd4;
jd4.bodyA = body1;
jd4.bodyB = body3;
jd4.joint1 = joint1;
jd4.joint2 = joint2;
jd4.ratio = circle2.m_radius / circle1.m_radius;
m_world->CreateJoint(&jd4);
}
{
b2CircleShape circle1;
circle1.m_radius = 1.0f;
b2CircleShape circle2;
circle2.m_radius = 2.0f;
b2PolygonShape box;
box.SetAsBox(0.5f, 5.0f);
b2BodyDef bd1;
bd1.type = b2_dynamicBody;
bd1.position.Set(-3.0f, 12.0f);
b2Body* body1 = m_world->CreateBody(&bd1);
body1->CreateFixture(&circle1, 5.0f);
b2RevoluteJointDef jd1;
jd1.bodyA = ground;
jd1.bodyB = body1;
jd1.localAnchorA = ground->GetLocalPoint(bd1.position);
jd1.localAnchorB = body1->GetLocalPoint(bd1.position);
jd1.referenceAngle = body1->GetAngle() - ground->GetAngle();
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd1);
b2BodyDef bd2;
bd2.type = b2_dynamicBody;
bd2.position.Set(0.0f, 12.0f);
b2Body* body2 = m_world->CreateBody(&bd2);
body2->CreateFixture(&circle2, 5.0f);
b2RevoluteJointDef jd2;
jd2.Initialize(ground, body2, bd2.position);
m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd2);
b2BodyDef bd3;
bd3.type = b2_dynamicBody;
bd3.position.Set(2.5f, 12.0f);
b2Body* body3 = m_world->CreateBody(&bd3);
body3->CreateFixture(&box, 5.0f);
b2PrismaticJointDef jd3;
jd3.Initialize(ground, body3, bd3.position, b2Vec2(0.0f, 1.0f));
jd3.lowerTranslation = -5.0f;
jd3.upperTranslation = 5.0f;
jd3.enableLimit = true;
m_joint3 = (b2PrismaticJoint*)m_world->CreateJoint(&jd3);
b2GearJointDef jd4;
jd4.bodyA = body1;
jd4.bodyB = body2;
jd4.joint1 = m_joint1;
jd4.joint2 = m_joint2;
jd4.ratio = circle2.m_radius / circle1.m_radius;
m_joint4 = (b2GearJoint*)m_world->CreateJoint(&jd4);
b2GearJointDef jd5;
jd5.bodyA = body2;
jd5.bodyB = body3;
jd5.joint1 = m_joint2;
jd5.joint2 = m_joint3;
jd5.ratio = -1.0f / circle2.m_radius;
m_joint5 = (b2GearJoint*)m_world->CreateJoint(&jd5);
}
}
void Step(Settings* settings) override
{
Test::Step(settings);
float ratio, value;
ratio = m_joint4->GetRatio();
value = m_joint1->GetJointAngle() + ratio * m_joint2->GetJointAngle();
//g_debugDraw.DrawString(5, m_textLine, "theta1 + %4.2f * theta2 = %4.2f", (float) ratio, (float) value);
//m_textLine += m_textIncrement;
ratio = m_joint5->GetRatio();
value = m_joint2->GetJointAngle() + ratio * m_joint3->GetJointTranslation();
//g_debugDraw.DrawString(5, m_textLine, "theta2 + %4.2f * delta = %4.2f", (float) ratio, (float) value);
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new GearJoint;
}
b2RevoluteJoint* m_joint1;
b2RevoluteJoint* m_joint2;
b2PrismaticJoint* m_joint3;
b2GearJoint* m_joint4;
b2GearJoint* m_joint5;
};
static int testIndex = RegisterTest("Joints", "Gear", GearJoint::Create);

View File

@ -0,0 +1,61 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Heavy1 : public Test
{
public:
Heavy1()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 0.5f);
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 10.0f);
bd.position.Set(0.0f, 6.0f);
body = m_world->CreateBody(&bd);
shape.m_radius = 5.0f;
body->CreateFixture(&shape, 10.0f);
}
static Test* Create()
{
return new Heavy1;
}
};
static int testIndex = RegisterTest("Solver", "Heavy 1", Heavy1::Create);

View File

@ -0,0 +1,94 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Heavy2 : public Test
{
public:
Heavy2()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 2.5f);
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 10.0f);
bd.position.Set(0.0f, 3.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 10.0f);
m_heavy = NULL;
}
void ToggleHeavy()
{
if (m_heavy)
{
m_world->DestroyBody(m_heavy);
m_heavy = NULL;
}
else
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 9.0f);
m_heavy = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 5.0f;
m_heavy->CreateFixture(&shape, 10.0f);
}
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_H:
// ToggleHeavy();
// break;
// }
//}
static Test* Create()
{
return new Heavy2;
}
b2Body* m_heavy;
};
static int testIndex = RegisterTest("Solver", "Heavy 2", Heavy2::Create);

View File

@ -0,0 +1,108 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class MobileBalanced : public Test
{
public:
enum
{
e_depth = 4
};
MobileBalanced()
{
b2Body* ground;
// Create ground body.
{
b2BodyDef bodyDef;
bodyDef.position.Set(0.0f, 20.0f);
ground = m_world->CreateBody(&bodyDef);
}
float a = 0.5f;
b2Vec2 h(0.0f, a);
b2Body* root = AddNode(ground, b2Vec2_zero, 0, 3.0f, a);
b2RevoluteJointDef jointDef;
jointDef.bodyA = ground;
jointDef.bodyB = root;
jointDef.localAnchorA.SetZero();
jointDef.localAnchorB = h;
m_world->CreateJoint(&jointDef);
}
b2Body* AddNode(b2Body* parent, const b2Vec2& localAnchor, int32 depth, float offset, float a)
{
float density = 20.0f;
b2Vec2 h(0.0f, a);
b2Vec2 p = parent->GetPosition() + localAnchor - h;
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = p;
b2Body* body = m_world->CreateBody(&bodyDef);
b2PolygonShape shape;
shape.SetAsBox(0.25f * a, a);
body->CreateFixture(&shape, density);
if (depth == e_depth)
{
return body;
}
shape.SetAsBox(offset, 0.25f * a, b2Vec2(0, -a), 0.0f);
body->CreateFixture(&shape, density);
b2Vec2 a1 = b2Vec2(offset, -a);
b2Vec2 a2 = b2Vec2(-offset, -a);
b2Body* body1 = AddNode(body, a1, depth + 1, 0.5f * offset, a);
b2Body* body2 = AddNode(body, a2, depth + 1, 0.5f * offset, a);
b2RevoluteJointDef jointDef;
jointDef.bodyA = body;
jointDef.localAnchorB = h;
jointDef.localAnchorA = a1;
jointDef.bodyB = body1;
m_world->CreateJoint(&jointDef);
jointDef.localAnchorA = a2;
jointDef.bodyB = body2;
m_world->CreateJoint(&jointDef);
return body;
}
static Test* Create()
{
return new MobileBalanced;
}
};
static int testIndex = RegisterTest("Solver", "Mobile Balanced", MobileBalanced::Create);

View File

@ -0,0 +1,105 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class MobileUnbalanced : public Test
{
public:
enum
{
e_depth = 4
};
MobileUnbalanced()
{
b2Body* ground;
// Create ground body.
{
b2BodyDef bodyDef;
bodyDef.position.Set(0.0f, 20.0f);
ground = m_world->CreateBody(&bodyDef);
}
float a = 0.5f;
b2Vec2 h(0.0f, a);
b2Body* root = AddNode(ground, b2Vec2_zero, 0, 3.0f, a);
b2RevoluteJointDef jointDef;
jointDef.bodyA = ground;
jointDef.bodyB = root;
jointDef.localAnchorA.SetZero();
jointDef.localAnchorB = h;
m_world->CreateJoint(&jointDef);
}
b2Body* AddNode(b2Body* parent, const b2Vec2& localAnchor, int32 depth, float offset, float a)
{
float density = 20.0f;
b2Vec2 h(0.0f, a);
b2Vec2 p = parent->GetPosition() + localAnchor - h;
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = p;
b2Body* body = m_world->CreateBody(&bodyDef);
b2PolygonShape shape;
shape.SetAsBox(0.25f * a, a);
body->CreateFixture(&shape, density);
if (depth == e_depth)
{
return body;
}
b2Vec2 a1 = b2Vec2(offset, -a);
b2Vec2 a2 = b2Vec2(-offset, -a);
b2Body* body1 = AddNode(body, a1, depth + 1, 0.5f * offset, a);
b2Body* body2 = AddNode(body, a2, depth + 1, 0.5f * offset, a);
b2RevoluteJointDef jointDef;
jointDef.bodyA = body;
jointDef.localAnchorB = h;
jointDef.localAnchorA = a1;
jointDef.bodyB = body1;
m_world->CreateJoint(&jointDef);
jointDef.localAnchorA = a2;
jointDef.bodyB = body2;
m_world->CreateJoint(&jointDef);
return body;
}
static Test* Create()
{
return new MobileUnbalanced;
}
};
static int testIndex = RegisterTest("Solver", "Mobile Unbalanced", MobileUnbalanced::Create);

View File

@ -0,0 +1,118 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "settings.h"
#include "../test.h"
/// This test shows how to use a motor joint. A motor joint
/// can be used to animate a dynamic body. With finite motor forces
/// the body can be blocked by collision with other bodies.
class MotorJoint : public Test
{
public:
MotorJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
ground->CreateFixture(&fd);
}
// Define motorized body
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 8.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
body->CreateFixture(&fd);
b2MotorJointDef mjd;
mjd.Initialize(ground, body);
mjd.maxForce = 1000.0f;
mjd.maxTorque = 1000.0f;
m_joint = (b2MotorJoint*)m_world->CreateJoint(&mjd);
}
m_go = false;
m_time = 0.0f;
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_S:
// m_go = !m_go;
// break;
// }
//}
void Step(Settings* settings) override
{
//if (m_go && settings.m_hertz > 0.0f)
//{
// m_time += 1.0f / settings.m_hertz;
//}
b2Vec2 linearOffset;
linearOffset.x = 6.0f * sinf(2.0f * m_time);
linearOffset.y = 8.0f + 4.0f * sinf(1.0f * m_time);
float angularOffset = 4.0f * m_time;
m_joint->SetLinearOffset(linearOffset);
m_joint->SetAngularOffset(angularOffset);
//g_debugDraw.DrawPoint(linearOffset, 4.0f, b2Color(0.9f, 0.9f, 0.9f));
//Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Keys: (s) pause");
//m_textLine += 15;
}
static Test* Create()
{
return new MotorJoint;
}
b2MotorJoint* m_joint;
float m_time;
bool m_go;
};
static int testIndex = RegisterTest("Joints", "Motor Joint", MotorJoint::Create);

View File

@ -0,0 +1,170 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
/// This tests bullet collision and provides an example of a gameplay scenario.
/// This also uses a loop shape.
class Pinball : public Test
{
public:
Pinball()
{
// Ground body
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2Vec2 vs[5];
vs[0].Set(-8.0f, 6.0f);
vs[1].Set(-8.0f, 20.0f);
vs[2].Set(8.0f, 20.0f);
vs[3].Set(8.0f, 6.0f);
vs[4].Set(0.0f, -2.0f);
b2ChainShape loop;
loop.CreateLoop(vs, 5);
b2FixtureDef fd;
fd.shape = &loop;
fd.density = 0.0f;
ground->CreateFixture(&fd);
}
// Flippers
{
b2Vec2 p1(-2.0f, 0.0f), p2(2.0f, 0.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p1;
b2Body* leftFlipper = m_world->CreateBody(&bd);
bd.position = p2;
b2Body* rightFlipper = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(1.75f, 0.1f);
b2FixtureDef fd;
fd.shape = &box;
fd.density = 1.0f;
leftFlipper->CreateFixture(&fd);
rightFlipper->CreateFixture(&fd);
b2RevoluteJointDef jd;
jd.bodyA = ground;
jd.localAnchorB.SetZero();
jd.enableMotor = true;
jd.maxMotorTorque = 1000.0f;
jd.enableLimit = true;
jd.motorSpeed = 0.0f;
jd.localAnchorA = p1;
jd.bodyB = leftFlipper;
jd.lowerAngle = -30.0f * b2_pi / 180.0f;
jd.upperAngle = 5.0f * b2_pi / 180.0f;
m_leftJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
jd.motorSpeed = 0.0f;
jd.localAnchorA = p2;
jd.bodyB = rightFlipper;
jd.lowerAngle = -5.0f * b2_pi / 180.0f;
jd.upperAngle = 30.0f * b2_pi / 180.0f;
m_rightJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(1.0f, 15.0f);
bd.type = b2_dynamicBody;
bd.bullet = true;
m_ball = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.2f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
m_ball->CreateFixture(&fd);
}
m_button = false;
}
void Step(Settings* settings) override
{
if (m_button)
{
m_leftJoint->SetMotorSpeed(20.0f);
m_rightJoint->SetMotorSpeed(-20.0f);
}
else
{
m_leftJoint->SetMotorSpeed(-10.0f);
m_rightJoint->SetMotorSpeed(10.0f);
}
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Press 'a' to control the flippers");
//m_textLine += m_textIncrement;
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_A:
// m_button = true;
// break;
// }
//}
//void KeyboardUp(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_A:
// m_button = false;
// break;
// }
//}
static Test* Create()
{
return new Pinball;
}
b2RevoluteJoint* m_leftJoint;
b2RevoluteJoint* m_rightJoint;
b2Body* m_ball;
bool m_button;
};
static int testIndex = RegisterTest("Examples", "Pinball", Pinball::Create);

View File

@ -0,0 +1,133 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Platformer : public Test
{
public:
enum State
{
e_unknown,
e_above,
e_below
};
Platformer()
{
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Platform
{
b2BodyDef bd;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(3.0f, 0.5f);
m_platform = body->CreateFixture(&shape, 0.0f);
m_bottom = 10.0f - 0.5f;
m_top = 10.0f + 0.5f;
}
// Actor
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
m_radius = 0.5f;
b2CircleShape shape;
shape.m_radius = m_radius;
m_character = body->CreateFixture(&shape, 20.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_state = e_unknown;
}
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override
{
Test::PreSolve(contact, oldManifold);
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA != m_platform && fixtureA != m_character)
{
return;
}
if (fixtureB != m_platform && fixtureB != m_character)
{
return;
}
#if 1
b2Vec2 position = m_character->GetBody()->GetPosition();
if (position.y < m_top + m_radius - 3.0f * b2_linearSlop)
{
contact->SetEnabled(false);
}
#else
b2Vec2 v = m_character->GetBody()->GetLinearVelocity();
if (v.y > 0.0f)
{
contact->SetEnabled(false);
}
#endif
}
void Step(Settings* settings) override
{
Test::Step(settings);
b2Vec2 v = m_character->GetBody()->GetLinearVelocity();
// g_debugDraw.DrawString(5, m_textLine, "Character Linear Velocity: %f", v.y);
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new Platformer;
}
float m_radius, m_top, m_bottom;
State m_state;
b2Fixture* m_platform;
b2Fixture* m_character;
};
static int testIndex = RegisterTest("Examples", "Platformer", Platformer::Create);

View File

@ -0,0 +1,127 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class PolygonCollision : public Test
{
public:
PolygonCollision()
{
//{
// m_polygonA.SetAsBox(0.2f, 0.4f);
// m_transformA.Set(b2Vec2(0.0f, 0.0f), 0.0f);
//}
//{
// m_polygonB.SetAsBox(0.5f, 0.5f);
// m_positionB.Set(19.345284f, 1.5632932f);
// m_angleB = 1.9160721f;
// m_transformB.Set(m_positionB, m_angleB);
//}
}
static Test* Create()
{
return new PolygonCollision;
}
void Step(Settings* settings) override
{
B2_NOT_USED(settings);
b2Manifold manifold;
// b2CollidePolygons(&manifold, &m_polygonA, m_transformA, &m_polygonB, m_transformB);
b2WorldManifold worldManifold;
// worldManifold.Initialize(&manifold, m_transformA, m_polygonA.m_radius, m_transformB, m_polygonB.m_radius);
//g_debugDraw.DrawString(5, m_textLine, "point count = %d", manifold.pointCount);
//m_textLine += m_textIncrement;
{
b2Color color(0.9f, 0.9f, 0.9f);
b2Vec2 v[b2_maxPolygonVertices];
//for (int32 i = 0; i < m_polygonA.m_count; ++i)
//{
// v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
//}
// g_debugDraw.DrawPolygon(v, m_polygonA.m_count, color);
//for (int32 i = 0; i < m_polygonB.m_count; ++i)
//{
// v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
//}
// g_debugDraw.DrawPolygon(v, m_polygonB.m_count, color);
}
for (int32 i = 0; i < manifold.pointCount; ++i)
{
// g_debugDraw.DrawPoint(worldManifold.points[i], 4.0f, b2Color(0.9f, 0.3f, 0.3f));
}
Test::Step(settings);
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_A:
// m_positionB.x -= 0.1f;
// break;
// case GLFW_KEY_D:
// m_positionB.x += 0.1f;
// break;
// case GLFW_KEY_S:
// m_positionB.y -= 0.1f;
// break;
// case GLFW_KEY_W:
// m_positionB.y += 0.1f;
// break;
// case GLFW_KEY_Q:
// m_angleB += 0.1f * b2_pi;
// break;
// case GLFW_KEY_E:
// m_angleB -= 0.1f * b2_pi;
// break;
// }
// m_transformB.Set(m_positionB, m_angleB);
//}
//b2PolygonShape m_polygonA;
//b2PolygonShape m_polygonB;
//b2Transform m_transformA;
//b2Transform m_transformB;
// b2Vec2 m_positionB;
// float m_angleB;
};
static int testIndex = RegisterTest("Geometry", "Polygon Collision", PolygonCollision::Create);

View File

@ -0,0 +1,265 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
/// This tests stacking. It also shows how to use b2World::Query
/// and b2TestOverlap.
/// This callback is called by b2World::QueryAABB. We find all the fixtures
/// that overlap an AABB. Of those, we use b2TestOverlap to determine which fixtures
/// overlap a circle. Up to 4 overlapped fixtures will be highlighted with a yellow border.
class PolygonShapesCallback : public b2QueryCallback
{
public:
enum
{
e_maxCount = 4
};
PolygonShapesCallback()
{
m_count = 0;
}
/// Called for each fixture found in the query AABB.
/// @return false to terminate the query.
bool ReportFixture(b2Fixture* fixture) override
{
if (m_count == e_maxCount)
{
return false;
}
b2Body* body = fixture->GetBody();
b2Shape* shape = fixture->GetShape();
bool overlap = b2TestOverlap(shape, 0, &m_circle, 0, body->GetTransform(), m_transform);
if (overlap)
{
b2Color color(0.95f, 0.95f, 0.6f);
b2Vec2 center = body->GetWorldCenter();
// g_debugDraw->DrawPoint(center, 5.0f, color);
++m_count;
}
return true;
}
b2CircleShape m_circle;
b2Transform m_transform;
b2Draw* g_debugDraw;
int32 m_count;
};
class PolygonShapes : public Test
{
public:
enum
{
e_maxBodies = 256
};
PolygonShapes()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float w = 1.0f;
float b = w / (2.0f + b2Sqrt(2.0f));
float s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
float x = RandomFloat(-2.0f, 2.0f);
bd.position.Set(x, 10.0f);
bd.angle = RandomFloat(-b2_pi, b2_pi);
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.density = 1.0f;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.density = 1.0f;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_1:
// case GLFW_KEY_2:
// case GLFW_KEY_3:
// case GLFW_KEY_4:
// case GLFW_KEY_5:
// Create(key - GLFW_KEY_1);
// break;
// case GLFW_KEY_A:
// for (int32 i = 0; i < e_maxBodies; i += 2)
// {
// if (m_bodies[i])
// {
// bool enabled = m_bodies[i]->IsEnabled();
// m_bodies[i]->SetEnabled(!enabled);
// }
// }
// break;
// case GLFW_KEY_D:
// DestroyBody();
// break;
// }
//}
void Step(Settings* settings) override
{
Test::Step(settings);
PolygonShapesCallback callback;
callback.m_circle.m_radius = 2.0f;
callback.m_circle.m_p.Set(0.0f, 1.1f);
callback.m_transform.SetIdentity();
// callback.g_debugDraw = &g_debugDraw;
b2AABB aabb;
callback.m_circle.ComputeAABB(&aabb, callback.m_transform, 0);
m_world->QueryAABB(&callback, aabb);
b2Color color(0.4f, 0.7f, 0.8f);
//g_debugDraw.DrawCircle(callback.m_circle.m_p, callback.m_circle.m_radius, color);
//g_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff, maximum of %d overlaps detected", PolygonShapesCallback::e_maxCount);
//m_textLine += m_textIncrement;
//g_debugDraw.DrawString(5, m_textLine, "Press 'a' to enable/disable some bodies");
//m_textLine += m_textIncrement;
//g_debugDraw.DrawString(5, m_textLine, "Press 'd' to destroy a body");
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new PolygonShapes;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
};
static int testIndex = RegisterTest("Geometry", "Polygon Shapes", PolygonShapes::Create);

View File

@ -0,0 +1,118 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "settings.h"
#include "../test.h"
//#include "imgui/imgui.h"
// Test the prismatic joint with limits and motor options.
class PrismaticJoint : public Test
{
public:
PrismaticJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
m_enableLimit = true;
m_enableMotor = false;
m_motorSpeed = 10.0f;
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 1.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
bd.angle = 0.5f * b2_pi;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
b2PrismaticJointDef pjd;
// Horizontal
pjd.Initialize(ground, body, bd.position, b2Vec2(1.0f, 0.0f));
pjd.motorSpeed = m_motorSpeed;
pjd.maxMotorForce = 10000.0f;
pjd.enableMotor = m_enableMotor;
pjd.lowerTranslation = -10.0f;
pjd.upperTranslation = 10.0f;
pjd.enableLimit = m_enableLimit;
m_joint = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
}
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
// ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// if (ImGui::Checkbox("Limit", &m_enableLimit))
// {
// m_joint->EnableLimit(m_enableLimit);
// }
// if (ImGui::Checkbox("Motor", &m_enableMotor))
// {
// m_joint->EnableMotor(m_enableMotor);
// }
// if (ImGui::SliderFloat("Speed", &m_motorSpeed, -100.0f, 100.0f, "%.0f"))
// {
// m_joint->SetMotorSpeed(m_motorSpeed);
// }
// ImGui::End();
//}
void Step(Settings* settings) override
{
Test::Step(settings);
// float force = m_joint->GetMotorForce(settings.m_hertz);
//g_debugDraw.DrawString(5, m_textLine, "Motor Force = %4.0f", force);
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new PrismaticJoint;
}
b2PrismaticJoint* m_joint;
float m_motorSpeed;
bool m_enableMotor;
bool m_enableLimit;
};
static int testIndex = RegisterTest("Joints", "Prismatic", PrismaticJoint::Create);

View File

@ -0,0 +1,96 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class PulleyJoint : public Test
{
public:
PulleyJoint()
{
float y = 16.0f;
float L = 12.0f;
float a = 1.0f;
float b = 2.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2CircleShape circle;
circle.m_radius = 2.0f;
circle.m_p.Set(-10.0f, y + b + L);
ground->CreateFixture(&circle, 0.0f);
circle.m_p.Set(10.0f, y + b + L);
ground->CreateFixture(&circle, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(a, b);
b2BodyDef bd;
bd.type = b2_dynamicBody;
//bd.fixedRotation = true;
bd.position.Set(-10.0f, y);
b2Body* body1 = m_world->CreateBody(&bd);
body1->CreateFixture(&shape, 5.0f);
bd.position.Set(10.0f, y);
b2Body* body2 = m_world->CreateBody(&bd);
body2->CreateFixture(&shape, 5.0f);
b2PulleyJointDef pulleyDef;
b2Vec2 anchor1(-10.0f, y + b);
b2Vec2 anchor2(10.0f, y + b);
b2Vec2 groundAnchor1(-10.0f, y + b + L);
b2Vec2 groundAnchor2(10.0f, y + b + L);
pulleyDef.Initialize(body1, body2, groundAnchor1, groundAnchor2, anchor1, anchor2, 1.5f);
m_joint1 = (b2PulleyJoint*)m_world->CreateJoint(&pulleyDef);
}
}
void Step(Settings* settings) override
{
Test::Step(settings);
float ratio = m_joint1->GetRatio();
float L = m_joint1->GetCurrentLengthA() + ratio * m_joint1->GetCurrentLengthB();
//g_debugDraw.DrawString(5, m_textLine, "L1 + %4.2f * L2 = %4.2f", (float) ratio, (float) L);
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new PulleyJoint;
}
b2PulleyJoint* m_joint1;
};
static int testIndex = RegisterTest("Joints", "Pulley", PulleyJoint::Create);

View File

@ -0,0 +1,92 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Pyramid : public Test
{
public:
enum
{
e_count = 20
};
Pyramid()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
float a = 0.5f;
b2PolygonShape shape;
shape.SetAsBox(a, a);
b2Vec2 x(-7.0f, 0.75f);
b2Vec2 y;
b2Vec2 deltaX(0.5625f, 1.25f);
b2Vec2 deltaY(1.125f, 0.0f);
for (int32 i = 0; i < e_count; ++i)
{
y = x;
for (int32 j = i; j < e_count; ++j)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
y += deltaY;
}
x += deltaX;
}
}
}
void Step(Settings* settings) override
{
Test::Step(settings);
//b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
//if (m_stepCount == 400)
//{
// tree->RebuildBottomUp();
//}
}
static Test* Create()
{
return new Pyramid;
}
};
static int testIndex = RegisterTest("Stacking", "Pyramid", Pyramid::Create);

View File

@ -0,0 +1,479 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "settings.h"
#include "../test.h"
//#include "imgui/imgui.h"
enum
{
e_maxBodies = 256
};
// This test demonstrates how to use the world ray-cast feature.
// NOTE: we are intentionally filtering one of the polygons, therefore
// the ray will always miss one type of polygon.
// This callback finds the closest hit. Polygon 0 is filtered.
class RayCastClosestCallback : public b2RayCastCallback
{
public:
RayCastClosestCallback()
{
m_hit = false;
}
float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float fraction) override
{
uintptr_t index = fixture->GetUserData().pointer;
if (index == 1)
{
// By returning -1, we instruct the calling code to ignore this fixture and
// continue the ray-cast to the next fixture.
return -1.0f;
}
m_hit = true;
m_point = point;
m_normal = normal;
// By returning the current fraction, we instruct the calling code to clip the ray and
// continue the ray-cast to the next fixture. WARNING: do not assume that fixtures
// are reported in order. However, by clipping, we can always get the closest fixture.
return fraction;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
// This callback finds any hit. Polygon 0 is filtered. For this type of query we are usually
// just checking for obstruction, so the actual fixture and hit point are irrelevant.
class RayCastAnyCallback : public b2RayCastCallback
{
public:
RayCastAnyCallback()
{
m_hit = false;
}
float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float) override
{
uintptr_t index = fixture->GetUserData().pointer;
if (index == 1)
{
// By returning -1, we instruct the calling code to ignore this fixture and
// continue the ray-cast to the next fixture.
return -1.0f;
}
m_hit = true;
m_point = point;
m_normal = normal;
// At this point we have a hit, so we know the ray is obstructed.
// By returning 0, we instruct the calling code to terminate the ray-cast.
return 0.0f;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
// This ray cast collects multiple hits along the ray. Polygon 0 is filtered.
// The fixtures are not necessary reported in order, so we might not capture
// the closest fixture.
class RayCastMultipleCallback : public b2RayCastCallback
{
public:
enum
{
e_maxCount = 3
};
RayCastMultipleCallback()
{
m_count = 0;
}
float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float) override
{
uintptr_t index = fixture->GetUserData().pointer;
if (index == 1)
{
// By returning -1, we instruct the calling code to ignore this fixture and
// continue the ray-cast to the next fixture.
return -1.0f;
}
b2Assert(m_count < e_maxCount);
m_points[m_count] = point;
m_normals[m_count] = normal;
++m_count;
if (m_count == e_maxCount)
{
// At this point the buffer is full.
// By returning 0, we instruct the calling code to terminate the ray-cast.
return 0.0f;
}
// By returning 1, we instruct the caller to continue without clipping the ray.
return 1.0f;
}
b2Vec2 m_points[e_maxCount];
b2Vec2 m_normals[e_maxCount];
int32 m_count;
};
class RayCast : public Test
{
public:
enum Mode
{
e_any = 0,
e_closest = 1,
e_multiple = 2
};
RayCast()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float w = 1.0f;
float b = w / (2.0f + b2Sqrt(2.0f));
float s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
{
m_edge.SetTwoSided(b2Vec2(-1.0f, 0.0f), b2Vec2(1.0f, 0.0f));
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
m_degrees = 0.0f;
m_mode = e_closest;
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
float x = RandomFloat(-10.0f, 10.0f);
float y = RandomFloat(0.0f, 20.0f);
bd.position.Set(x, y);
bd.angle = RandomFloat(-b2_pi, b2_pi);
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.friction = 0.3f;
fd.userData.pointer = index + 1;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else if (index < 5)
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.friction = 0.3f;
fd.userData.pointer = index + 1;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_edge;
fd.friction = 0.3f;
fd.userData.pointer = index + 1;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(210.0f, 285.0f));
// ImGui::Begin("Ray-cast Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// if (ImGui::Button("Shape 1"))
// {
// Create(0);
// }
// if (ImGui::Button("Shape 2"))
// {
// Create(1);
// }
// if (ImGui::Button("Shape 3"))
// {
// Create(2);
// }
// if (ImGui::Button("Shape 4"))
// {
// Create(3);
// }
// if (ImGui::Button("Shape 5"))
// {
// Create(4);
// }
// if (ImGui::Button("Shape 6"))
// {
// Create(5);
// }
// if (ImGui::Button("Destroy Shape"))
// {
// DestroyBody();
// }
// ImGui::RadioButton("Any", &m_mode, e_any);
// ImGui::RadioButton("Closest", &m_mode, e_closest);
// ImGui::RadioButton("Multiple", &m_mode, e_multiple);
// ImGui::SliderFloat("Angle", &m_degrees, 0.0f, 360.0f, "%.0f");
// ImGui::End();
//}
void Step(Settings* settings) override
{
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Shape 1 is intentionally ignored by the ray");
//m_textLine += m_textIncrement;
switch (m_mode)
{
case e_closest:
// g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: closest - find closest fixture along the ray");
break;
case e_any:
// g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: any - check for obstruction");
break;
case e_multiple:
// g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: multiple - gather multiple fixtures");
break;
}
// m_textLine += m_textIncrement;
float angle = b2_pi * m_degrees / 180.0f;
float L = 11.0f;
b2Vec2 point1(0.0f, 10.0f);
b2Vec2 d(L * cosf(angle), L * sinf(angle));
b2Vec2 point2 = point1 + d;
if (m_mode == e_closest)
{
RayCastClosestCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_hit)
{
// g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
// g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
// g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
// g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
}
else if (m_mode == e_any)
{
RayCastAnyCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_hit)
{
// g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
// g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
// g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
// g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
}
else if (m_mode == e_multiple)
{
RayCastMultipleCallback callback;
m_world->RayCast(&callback, point1, point2);
// g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
for (int32 i = 0; i < callback.m_count; ++i)
{
b2Vec2 p = callback.m_points[i];
b2Vec2 n = callback.m_normals[i];
// g_debugDraw.DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
// g_debugDraw.DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = p + 0.5f * n;
// g_debugDraw.DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f));
}
}
#if 0
// This case was failing.
{
b2Vec2 vertices[4];
//vertices[0].Set(-22.875f, -3.0f);
//vertices[1].Set(22.875f, -3.0f);
//vertices[2].Set(22.875f, 3.0f);
//vertices[3].Set(-22.875f, 3.0f);
b2PolygonShape shape;
//shape.Set(vertices, 4);
shape.SetAsBox(22.875f, 3.0f);
b2RayCastInput input;
input.p1.Set(10.2725f,1.71372f);
input.p2.Set(10.2353f,2.21807f);
//input.maxFraction = 0.567623f;
input.maxFraction = 0.56762173f;
b2Transform xf;
xf.SetIdentity();
xf.position.Set(23.0f, 5.0f);
b2RayCastOutput output;
bool hit;
hit = shape.RayCast(&output, input, xf);
hit = false;
b2Color color(1.0f, 1.0f, 1.0f);
b2Vec2 vs[4];
for (int32 i = 0; i < 4; ++i)
{
vs[i] = b2Mul(xf, shape.m_vertices[i]);
}
g_debugDraw.DrawPolygon(vs, 4, color);
g_debugDraw.DrawSegment(input.p1, input.p2, color);
}
#endif
}
static Test* Create()
{
return new RayCast;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
b2EdgeShape m_edge;
float m_degrees;
int32 m_mode;
};
static int testIndex = RegisterTest("Collision", "Ray Cast", RayCast::Create);

View File

@ -0,0 +1,79 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// Note: even with a restitution of 1.0, there is some energy change
// due to position correction.
class Restitution : public Test
{
public:
Restitution()
{
const float threshold = 10.0f;
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
fd.restitutionThreshold = threshold;
ground->CreateFixture(&fd);
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
float restitution[7] = { 0.0f, 0.1f, 0.3f, 0.5f, 0.75f, 0.9f, 1.0f };
for (int32 i = 0; i < 7; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 3.0f * i, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
fd.restitution = restitution[i];
fd.restitutionThreshold = threshold;
body->CreateFixture(&fd);
}
}
}
static Test* Create()
{
return new Restitution;
}
};
static int testIndex = RegisterTest("Forces", "Restitution", Restitution::Create);

View File

@ -0,0 +1,162 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "settings.h"
#include "../test.h"
//#include "imgui/imgui.h"
class RevoluteJoint : public Test
{
public:
RevoluteJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
//fd.filter.categoryBits = 2;
ground->CreateFixture(&fd);
}
m_enableLimit = true;
m_enableMotor = false;
m_motorSpeed = 1.0f;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 3.0f, b2Vec2(0.0f, 3.0f), 0.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
b2RevoluteJointDef jd;
jd.Initialize(ground, body, b2Vec2(-10.0f, 20.5f));
jd.motorSpeed = m_motorSpeed;
jd.maxMotorTorque = 10000.0f;
jd.enableMotor = m_enableMotor;
jd.lowerAngle = -0.25f * b2_pi;
jd.upperAngle = 0.5f * b2_pi;
jd.enableLimit = m_enableLimit;
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
{
b2CircleShape circle_shape;
circle_shape.m_radius = 2.0f;
b2BodyDef circle_bd;
circle_bd.type = b2_dynamicBody;
circle_bd.position.Set(5.0f, 30.0f);
b2FixtureDef fd;
fd.density = 5.0f;
fd.filter.maskBits = 1;
fd.shape = &circle_shape;
m_ball = m_world->CreateBody(&circle_bd);
m_ball->CreateFixture(&fd);
b2PolygonShape polygon_shape;
polygon_shape.SetAsBox(10.0f, 0.5f, b2Vec2 (-10.0f, 0.0f), 0.0f);
b2BodyDef polygon_bd;
polygon_bd.position.Set(20.0f, 10.0f);
polygon_bd.type = b2_dynamicBody;
polygon_bd.bullet = true;
b2Body* polygon_body = m_world->CreateBody(&polygon_bd);
polygon_body->CreateFixture(&polygon_shape, 2.0f);
b2RevoluteJointDef jd;
jd.Initialize(ground, polygon_body, b2Vec2(19.0f, 10.0f));
jd.lowerAngle = -0.25f * b2_pi;
jd.upperAngle = 0.0f * b2_pi;
jd.enableLimit = true;
jd.enableMotor = true;
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 10000.0f;
m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
// ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// if (ImGui::Checkbox("Limit", &m_enableLimit))
// {
// m_joint1->EnableLimit(m_enableLimit);
// }
// if (ImGui::Checkbox("Motor", &m_enableMotor))
// {
// m_joint1->EnableMotor(m_enableMotor);
// }
// if (ImGui::SliderFloat("Speed", &m_motorSpeed, -20.0f, 20.0f, "%.0f"))
// {
// m_joint1->SetMotorSpeed(m_motorSpeed);
// }
// ImGui::End();
//}
void Step(Settings* settings) override
{
Test::Step(settings);
// float torque1 = m_joint1->GetMotorTorque(settings->hz);
//g_debugDraw.DrawString(5, m_textLine, "Motor Torque 1= %4.0f", torque1);
//m_textLine += m_textIncrement;
// float torque2 = m_joint2->GetMotorTorque(settings.hz);
//g_debugDraw.DrawString(5, m_textLine, "Motor Torque 2= %4.0f", torque2);
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new RevoluteJoint;
}
b2Body* m_ball;
b2RevoluteJoint* m_joint1;
b2RevoluteJoint* m_joint2;
float m_motorSpeed;
bool m_enableMotor;
bool m_enableLimit;
};
static int testIndex = RegisterTest("Joints", "Revolute", RevoluteJoint::Create);

View File

@ -0,0 +1,286 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "settings.h"
#include "../test.h"
#include "box2d/b2_rope.h"
//#include "imgui/imgui.h"
///
class Rope : public Test
{
public:
Rope()
{
const int32 N = 20;
const float L = 0.5f;
b2Vec2 vertices[N];
float masses[N];
for (int32 i = 0; i < N; ++i)
{
vertices[i].Set(0.0f, L * (N - i));
masses[i] = 1.0f;
}
masses[0] = 0.0f;
masses[1] = 0.0f;
m_tuning1.bendHertz = 30.0f;
m_tuning1.bendDamping = 4.0f;
m_tuning1.bendStiffness = 1.0f;
m_tuning1.bendingModel = b2_pbdTriangleBendingModel;
m_tuning1.isometric = true;
m_tuning1.stretchHertz = 30.0f;
m_tuning1.stretchDamping = 4.0f;
m_tuning1.stretchStiffness = 1.0f;
m_tuning1.stretchingModel = b2_pbdStretchingModel;
m_tuning2.bendHertz = 30.0f;
m_tuning2.bendDamping = 0.7f;
m_tuning2.bendStiffness = 1.0f;
m_tuning2.bendingModel = b2_pbdHeightBendingModel;
m_tuning2.isometric = true;
m_tuning2.stretchHertz = 30.0f;
m_tuning2.stretchDamping = 1.0f;
m_tuning2.stretchStiffness = 1.0f;
m_tuning2.stretchingModel = b2_pbdStretchingModel;
m_position1.Set(-5.0f, 15.0f);
m_position2.Set(5.0f, 15.0f);
b2RopeDef def;
def.vertices = vertices;
def.count = N;
def.gravity.Set(0.0f, -10.0f);
def.masses = masses;
def.position = m_position1;
def.tuning = m_tuning1;
m_rope1.Create(def);
def.position = m_position2;
def.tuning = m_tuning2;
m_rope2.Create(def);
m_iterations1 = 8;
m_iterations2 = 8;
m_speed = 10.0f;
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(200.0f, 700.0f));
// ImGui::Begin("Tuning", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// ImGui::Separator();
// ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f);
//
// const ImGuiComboFlags comboFlags = 0;
// const char* bendModels[] = { "Spring", "PBD Ang", "XPBD Ang", "PBD Dist", "PBD Height", "PBD Triangle" };
// const char* stretchModels[] = { "PBD", "XPBD" };
// ImGui::Text("Rope 1");
// static int bendModel1 = m_tuning1.bendingModel;
// if (ImGui::BeginCombo("Bend Model##1", bendModels[bendModel1], comboFlags))
// {
// for (int i = 0; i < IM_ARRAYSIZE(bendModels); ++i)
// {
// bool isSelected = (bendModel1 == i);
// if (ImGui::Selectable(bendModels[i], isSelected))
// {
// bendModel1 = i;
// m_tuning1.bendingModel = b2BendingModel(i);
// }
// if (isSelected)
// {
// ImGui::SetItemDefaultFocus();
// }
// }
// ImGui::EndCombo();
// }
// ImGui::SliderFloat("Damping##B1", &m_tuning1.bendDamping, 0.0f, 4.0f, "%.1f");
// ImGui::SliderFloat("Hertz##B1", &m_tuning1.bendHertz, 0.0f, 60.0f, "%.0f");
// ImGui::SliderFloat("Stiffness##B1", &m_tuning1.bendStiffness, 0.0f, 1.0f, "%.1f");
// ImGui::Checkbox("Isometric##1", &m_tuning1.isometric);
// ImGui::Checkbox("Fixed Mass##1", &m_tuning1.fixedEffectiveMass);
// ImGui::Checkbox("Warm Start##1", &m_tuning1.warmStart);
// static int stretchModel1 = m_tuning1.stretchingModel;
// if (ImGui::BeginCombo("Stretch Model##1", stretchModels[stretchModel1], comboFlags))
// {
// for (int i = 0; i < IM_ARRAYSIZE(stretchModels); ++i)
// {
// bool isSelected = (stretchModel1 == i);
// if (ImGui::Selectable(stretchModels[i], isSelected))
// {
// stretchModel1 = i;
// m_tuning1.stretchingModel = b2StretchingModel(i);
// }
// if (isSelected)
// {
// ImGui::SetItemDefaultFocus();
// }
// }
// ImGui::EndCombo();
// }
// ImGui::SliderFloat("Damping##S1", &m_tuning1.stretchDamping, 0.0f, 4.0f, "%.1f");
// ImGui::SliderFloat("Hertz##S1", &m_tuning1.stretchHertz, 0.0f, 60.0f, "%.0f");
// ImGui::SliderFloat("Stiffness##S1", &m_tuning1.stretchStiffness, 0.0f, 1.0f, "%.1f");
// ImGui::SliderInt("Iterations##1", &m_iterations1, 1, 100, "%d");
// ImGui::Separator();
// ImGui::Text("Rope 2");
// static int bendModel2 = m_tuning2.bendingModel;
// if (ImGui::BeginCombo("Bend Model##2", bendModels[bendModel2], comboFlags))
// {
// for (int i = 0; i < IM_ARRAYSIZE(bendModels); ++i)
// {
// bool isSelected = (bendModel2 == i);
// if (ImGui::Selectable(bendModels[i], isSelected))
// {
// bendModel2 = i;
// m_tuning2.bendingModel = b2BendingModel(i);
// }
// if (isSelected)
// {
// ImGui::SetItemDefaultFocus();
// }
// }
// ImGui::EndCombo();
// }
// ImGui::SliderFloat("Damping##B2", &m_tuning2.bendDamping, 0.0f, 4.0f, "%.1f");
// ImGui::SliderFloat("Hertz##B2", &m_tuning2.bendHertz, 0.0f, 60.0f, "%.0f");
// ImGui::SliderFloat("Stiffness##B2", &m_tuning2.bendStiffness, 0.0f, 1.0f, "%.1f");
// ImGui::Checkbox("Isometric##2", &m_tuning2.isometric);
// ImGui::Checkbox("Fixed Mass##2", &m_tuning2.fixedEffectiveMass);
// ImGui::Checkbox("Warm Start##2", &m_tuning2.warmStart);
// static int stretchModel2 = m_tuning2.stretchingModel;
// if (ImGui::BeginCombo("Stretch Model##2", stretchModels[stretchModel2], comboFlags))
// {
// for (int i = 0; i < IM_ARRAYSIZE(stretchModels); ++i)
// {
// bool isSelected = (stretchModel2 == i);
// if (ImGui::Selectable(stretchModels[i], isSelected))
// {
// stretchModel2 = i;
// m_tuning2.stretchingModel = b2StretchingModel(i);
// }
// if (isSelected)
// {
// ImGui::SetItemDefaultFocus();
// }
// }
// ImGui::EndCombo();
// }
// ImGui::SliderFloat("Damping##S2", &m_tuning2.stretchDamping, 0.0f, 4.0f, "%.1f");
// ImGui::SliderFloat("Hertz##S2", &m_tuning2.stretchHertz, 0.0f, 60.0f, "%.0f");
// ImGui::SliderFloat("Stiffness##S2", &m_tuning2.stretchStiffness, 0.0f, 1.0f, "%.1f");
// ImGui::SliderInt("Iterations##2", &m_iterations2, 1, 100, "%d");
// ImGui::Separator();
// ImGui::SliderFloat("Speed", &m_speed, 10.0f, 100.0f, "%.0f");
// if (ImGui::Button("Reset"))
// {
// m_position1.Set(-5.0f, 15.0f);
// m_position2.Set(5.0f, 15.0f);
// m_rope1.Reset(m_position1);
// m_rope2.Reset(m_position2);
// }
// ImGui::PopItemWidth();
// ImGui::End();
//}
void Step(Settings* settings) override
{
float dt = 0.5; // settings.hz > 0.0f ? 1.0f / settings.hz : 0.0f;
if (settings->pause == 1 && settings->singleStep == 0)
{
dt = 0.0f;
}
//if (glfwGetKey(g_mainWindow, GLFW_KEY_COMMA) == GLFW_PRESS)
//{
// m_position1.x -= m_speed * dt;
// m_position2.x -= m_speed * dt;
//}
//if (glfwGetKey(g_mainWindow, GLFW_KEY_PERIOD) == GLFW_PRESS)
//{
// m_position1.x += m_speed * dt;
// m_position2.x += m_speed * dt;
//}
m_rope1.SetTuning(m_tuning1);
m_rope2.SetTuning(m_tuning2);
m_rope1.Step(dt, m_iterations1, m_position1);
m_rope2.Step(dt, m_iterations2, m_position2);
Test::Step(settings);
//m_rope1.Draw(&g_debugDraw);
//m_rope2.Draw(&g_debugDraw);
//g_debugDraw.DrawString(5, m_textLine, "Press comma and period to move left and right");
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new Rope;
}
b2Rope m_rope1;
b2Rope m_rope2;
b2RopeTuning m_tuning1;
b2RopeTuning m_tuning2;
int32 m_iterations1;
int32 m_iterations2;
b2Vec2 m_position1;
b2Vec2 m_position2;
float m_speed;
};
static int testIndex = RegisterTest("Rope", "Bending", Rope::Create);

View File

@ -0,0 +1,195 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
//#include "imgui/imgui.h"
// This shows how to use sensor shapes. Sensors don't have collision, but report overlap events.
class Sensors : public Test
{
public:
enum
{
e_count = 7
};
Sensors()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
{
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
#if 0
{
b2FixtureDef sd;
sd.SetAsBox(10.0f, 2.0f, b2Vec2(0.0f, 20.0f), 0.0f);
sd.isSensor = true;
m_sensor = ground->CreateFixture(&sd);
}
#else
{
b2CircleShape shape;
shape.m_radius = 5.0f;
shape.m_p.Set(0.0f, 10.0f);
b2FixtureDef fd;
fd.shape = &shape;
fd.isSensor = true;
m_sensor = ground->CreateFixture(&fd);
}
#endif
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 3.0f * i, 20.0f);
bd.userData.pointer = i;
m_touching[i] = false;
m_bodies[i] = m_world->CreateBody(&bd);
m_bodies[i]->CreateFixture(&shape, 1.0f);
}
}
m_force = 100.0f;
}
// Implement contact listener.
void BeginContact(b2Contact* contact) override
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_sensor)
{
uintptr_t index = fixtureB->GetBody()->GetUserData().pointer;
if (index < e_count)
{
m_touching[index] = true;
}
}
if (fixtureB == m_sensor)
{
uintptr_t index = fixtureA->GetBody()->GetUserData().pointer;
if (index < e_count)
{
m_touching[index] = true;
}
}
}
// Implement contact listener.
void EndContact(b2Contact* contact) override
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_sensor)
{
uintptr_t index = fixtureB->GetBody()->GetUserData().pointer;
if (index < e_count)
{
m_touching[index] = false;
}
}
if (fixtureB == m_sensor)
{
uintptr_t index = fixtureA->GetBody()->GetUserData().pointer;
if (index < e_count)
{
m_touching[index] = false;
}
}
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(200.0f, 60.0f));
// ImGui::Begin("Sensor Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// ImGui::SliderFloat("Force", &m_force, 0.0f, 2000.0f, "%.0f");
// ImGui::End();
//}
void Step(Settings* settings) override
{
Test::Step(settings);
// Traverse the contact results. Apply a force on shapes
// that overlap the sensor.
for (int32 i = 0; i < e_count; ++i)
{
if (m_touching[i] == false)
{
continue;
}
b2Body* body = m_bodies[i];
b2Body* ground = m_sensor->GetBody();
b2CircleShape* circle = (b2CircleShape*)m_sensor->GetShape();
b2Vec2 center = ground->GetWorldPoint(circle->m_p);
b2Vec2 position = body->GetPosition();
b2Vec2 d = center - position;
if (d.LengthSquared() < FLT_EPSILON * FLT_EPSILON)
{
continue;
}
d.Normalize();
b2Vec2 F = m_force * d;
body->ApplyForce(F, position, false);
}
}
static Test* Create()
{
return new Sensors;
}
b2Fixture* m_sensor;
b2Body* m_bodies[e_count];
float m_force;
bool m_touching[e_count];
};
static int testIndex = RegisterTest("Collision", "Sensors", Sensors::Create);

View File

@ -0,0 +1,193 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
#include "box2d/b2_distance.h"
class ShapeCast : public Test
{
public:
enum
{
e_vertexCount = 8
};
ShapeCast()
{
#if 1
m_vAs[0].Set(-0.5f, 1.0f);
m_vAs[1].Set(0.5f, 1.0f);
m_vAs[2].Set(0.0f, 0.0f);
m_countA = 3;
m_radiusA = b2_polygonRadius;
m_vBs[0].Set(-0.5f, -0.5f);
m_vBs[1].Set(0.5f, -0.5f);
m_vBs[2].Set(0.5f, 0.5f);
m_vBs[3].Set(-0.5f, 0.5f);
m_countB = 4;
m_radiusB = b2_polygonRadius;
m_transformA.p.Set(0.0f, 0.25f);
m_transformA.q.SetIdentity();
m_transformB.p.Set(-4.0f, 0.0f);
m_transformB.q.SetIdentity();
m_translationB.Set(8.0f, 0.0f);
#elif 0
m_vAs[0].Set(0.0f, 0.0f);
m_countA = 1;
m_radiusA = 0.5f;
m_vBs[0].Set(0.0f, 0.0f);
m_countB = 1;
m_radiusB = 0.5f;
m_transformA.p.Set(0.0f, 0.25f);
m_transformA.q.SetIdentity();
m_transformB.p.Set(-4.0f, 0.0f);
m_transformB.q.SetIdentity();
m_translationB.Set(8.0f, 0.0f);
#else
m_vAs[0].Set(0.0f, 0.0f);
m_vAs[1].Set(2.0f, 0.0f);
m_countA = 2;
m_radiusA = b2_polygonRadius;
m_vBs[0].Set(0.0f, 0.0f);
m_countB = 1;
m_radiusB = 0.25f;
// Initial overlap
m_transformA.p.Set(0.0f, 0.0f);
m_transformA.q.SetIdentity();
m_transformB.p.Set(-0.244360745f, 0.05999358f);
m_transformB.q.SetIdentity();
m_translationB.Set(0.0f, 0.0399999991f);
#endif
}
static Test* Create()
{
return new ShapeCast;
}
void Step(Settings* settings) override
{
Test::Step(settings);
b2ShapeCastInput input;
input.proxyA.Set(m_vAs, m_countA, m_radiusA);
input.proxyB.Set(m_vBs, m_countB, m_radiusB);
input.transformA = m_transformA;
input.transformB = m_transformB;
input.translationB = m_translationB;
b2ShapeCastOutput output;
bool hit = b2ShapeCast(&output, &input);
b2Transform transformB2;
transformB2.q = m_transformB.q;
transformB2.p = m_transformB.p + output.lambda * input.translationB;
b2DistanceInput distanceInput;
distanceInput.proxyA.Set(m_vAs, m_countA, m_radiusA);
distanceInput.proxyB.Set(m_vBs, m_countB, m_radiusB);
distanceInput.transformA = m_transformA;
distanceInput.transformB = transformB2;
distanceInput.useRadii = false;
b2SimplexCache simplexCache;
simplexCache.count = 0;
b2DistanceOutput distanceOutput;
b2Distance(&distanceOutput, &simplexCache, &distanceInput);
//g_debugDraw.DrawString(5, m_textLine, "hit = %s, iters = %d, lambda = %g, distance = %g",
// hit ? "true" : "false", output.iterations, output.lambda, distanceOutput.distance);
//m_textLine += m_textIncrement;
b2Vec2 vertices[b2_maxPolygonVertices];
for (int32 i = 0; i < m_countA; ++i)
{
vertices[i] = b2Mul(m_transformA, m_vAs[i]);
}
if (m_countA == 1)
{
// g_debugDraw.DrawCircle(vertices[0], m_radiusA, b2Color(0.9f, 0.9f, 0.9f));
}
else
{
// g_debugDraw.DrawPolygon(vertices, m_countA, b2Color(0.9f, 0.9f, 0.9f));
}
for (int32 i = 0; i < m_countB; ++i)
{
vertices[i] = b2Mul(m_transformB, m_vBs[i]);
}
if (m_countB == 1)
{
// g_debugDraw.DrawCircle(vertices[0], m_radiusB, b2Color(0.5f, 0.9f, 0.5f));
}
else
{
// g_debugDraw.DrawPolygon(vertices, m_countB, b2Color(0.5f, 0.9f, 0.5f));
}
for (int32 i = 0; i < m_countB; ++i)
{
vertices[i] = b2Mul(transformB2, m_vBs[i]);
}
if (m_countB == 1)
{
// g_debugDraw.DrawCircle(vertices[0], m_radiusB, b2Color(0.5f, 0.7f, 0.9f));
}
else
{
// g_debugDraw.DrawPolygon(vertices, m_countB, b2Color(0.5f, 0.7f, 0.9f));
}
if (hit)
{
b2Vec2 p1 = output.point;
// g_debugDraw.DrawPoint(p1, 10.0f, b2Color(0.9f, 0.3f, 0.3f));
b2Vec2 p2 = p1 + output.normal;
// g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.3f, 0.3f));
}
}
b2Vec2 m_vAs[b2_maxPolygonVertices];
int32 m_countA;
float m_radiusA;
b2Vec2 m_vBs[b2_maxPolygonVertices];
int32 m_countB;
float m_radiusB;
b2Transform m_transformA;
b2Transform m_transformB;
b2Vec2 m_translationB;
};
static int testIndex = RegisterTest("Collision", "Shape Cast", ShapeCast::Create);

View File

@ -0,0 +1,108 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class ShapeEditing : public Test
{
public:
ShapeEditing()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
m_body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(4.0f, 4.0f, b2Vec2(0.0f, 0.0f), 0.0f);
m_fixture1 = m_body->CreateFixture(&shape, 10.0f);
m_fixture2 = NULL;
m_sensor = false;
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_C:
// if (m_fixture2 == NULL)
// {
// b2CircleShape shape;
// shape.m_radius = 3.0f;
// shape.m_p.Set(0.5f, -4.0f);
// m_fixture2 = m_body->CreateFixture(&shape, 10.0f);
// m_body->SetAwake(true);
// }
// break;
// case GLFW_KEY_D:
// if (m_fixture2 != NULL)
// {
// m_body->DestroyFixture(m_fixture2);
// m_fixture2 = NULL;
// m_body->SetAwake(true);
// }
// break;
// case GLFW_KEY_S:
// if (m_fixture2 != NULL)
// {
// m_sensor = !m_sensor;
// m_fixture2->SetSensor(m_sensor);
// }
// break;
// }
//}
void Step(Settings* settings) override
{
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape.");
//m_textLine += m_textIncrement;
//g_debugDraw.DrawString(5, m_textLine, "sensor = %d", m_sensor);
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new ShapeEditing;
}
b2Body* m_body;
b2Fixture* m_fixture1;
b2Fixture* m_fixture2;
bool m_sensor;
};
static int testIndex = RegisterTest("Examples", "Shape Editing", ShapeEditing::Create);

View File

@ -0,0 +1,150 @@
/*
Test case for collision/jerking issue.
*/
#include "../test.h"
#include <vector>
#include <iostream>
class Skier : public Test
{
public:
Skier()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
float const PlatformWidth = 8.0f;
/*
First angle is from the horizontal and should be negative for a downward slope.
Second angle is relative to the preceding slope, and should be positive, creating a kind of
loose 'Z'-shape from the 3 edges.
If A1 = -10, then A2 <= ~1.5 will result in the collision glitch.
If A1 = -30, then A2 <= ~10.0 will result in the glitch.
*/
float const Angle1Degrees = -30.0f;
float const Angle2Degrees = 10.0f;
/*
The larger the value of SlopeLength, the less likely the glitch will show up.
*/
float const SlopeLength = 2.0f;
float const SurfaceFriction = 0.2f;
// Convert to radians
float const Slope1Incline = -Angle1Degrees * b2_pi / 180.0f;
float const Slope2Incline = Slope1Incline - Angle2Degrees * b2_pi / 180.0f;
//
m_platform_width = PlatformWidth;
// Horizontal platform
b2Vec2 v1(-PlatformWidth, 0.0f);
b2Vec2 v2(0.0f, 0.0f);
b2Vec2 v3(SlopeLength * cosf(Slope1Incline), -SlopeLength * sinf(Slope1Incline));
b2Vec2 v4(v3.x + SlopeLength * cosf(Slope2Incline), v3.y - SlopeLength * sinf(Slope2Incline));
b2Vec2 v5(v4.x, v4.y - 1.0f);
b2Vec2 vertices[5] = { v5, v4, v3, v2, v1 };
b2ChainShape shape;
shape.CreateLoop(vertices, 5);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 0.0f;
fd.friction = SurfaceFriction;
ground->CreateFixture(&fd);
}
{
float const BodyWidth = 1.0f;
float const BodyHeight = 2.5f;
float const SkiLength = 3.0f;
/*
Larger values for this seem to alleviate the issue to some extent.
*/
float const SkiThickness = 0.3f;
float const SkiFriction = 0.0f;
float const SkiRestitution = 0.15f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
float initial_y = BodyHeight / 2 + SkiThickness;
bd.position.Set(-m_platform_width / 2, initial_y);
b2Body* skier = m_world->CreateBody(&bd);
b2PolygonShape ski;
b2Vec2 verts[4];
verts[0].Set(-SkiLength / 2 - SkiThickness, -BodyHeight / 2);
verts[1].Set(-SkiLength / 2, -BodyHeight / 2 - SkiThickness);
verts[2].Set(SkiLength / 2, -BodyHeight / 2 - SkiThickness);
verts[3].Set(SkiLength / 2 + SkiThickness, -BodyHeight / 2);
ski.Set(verts, 4);
b2FixtureDef fd;
fd.density = 1.0f;
fd.friction = SkiFriction;
fd.restitution = SkiRestitution;
fd.shape = &ski;
skier->CreateFixture(&fd);
skier->SetLinearVelocity(b2Vec2(0.5f, 0.0f));
m_skier = skier;
}
//g_camera.m_center = b2Vec2(m_platform_width / 2.0f, 0.0f);
//g_camera.m_zoom = 0.4f;
//m_fixed_camera = true;
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_C:
// m_fixed_camera = !m_fixed_camera;
// if(m_fixed_camera)
// {
// g_camera.m_center = b2Vec2(m_platform_width / 2.0f, 0.0f);
// }
// break;
// }
//}
void Step(Settings* settings) override
{
//g_debugDraw.DrawString(5, m_textLine, "Keys: c = Camera fixed/tracking");
//m_textLine += m_textIncrement;
if(!m_fixed_camera)
{
// g_camera.m_center = m_skier->GetPosition();
}
Test::Step(settings);
}
static Test* Create()
{
return new Skier;
}
b2Body* m_skier;
float m_platform_width;
bool m_fixed_camera;
};
static int testIndex = RegisterTest("Bugs", "Skier", Skier::Create);

View File

@ -0,0 +1,106 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// A basic slider crank created for GDC tutorial: Understanding Constraints
class SliderCrank1 : public Test
{
public:
SliderCrank1()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
bd.position.Set(0.0f, 17.0f);
ground = m_world->CreateBody(&bd);
}
{
b2Body* prevBody = ground;
// Define crank.
{
b2PolygonShape shape;
shape.SetAsBox(4.0f, 1.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(-12.0f, 20.0f));
m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define connecting rod
{
b2PolygonShape shape;
shape.SetAsBox(8.0f, 1.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(4.0f, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(-4.0f, 20.0f));
m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define piston
{
b2PolygonShape shape;
shape.SetAsBox(3.0f, 3.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.position.Set(12.0f, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(12.0f, 20.0f));
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, body, b2Vec2(12.0f, 17.0f), b2Vec2(1.0f, 0.0f));
m_world->CreateJoint(&pjd);
}
}
}
static Test* Create()
{
return new SliderCrank1;
}
};
static int testIndex = RegisterTest("Examples", "Slider Crank 1", SliderCrank1::Create);

View File

@ -0,0 +1,160 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "settings.h"
#include "../test.h"
// A motor driven slider crank with joint friction.
class SliderCrank2 : public Test
{
public:
SliderCrank2()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Body* prevBody = ground;
// Define crank.
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 7.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 5.0f));
rjd.motorSpeed = 1.0f * b2_pi;
rjd.maxMotorTorque = 10000.0f;
rjd.enableMotor = true;
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define follower.
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 13.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 9.0f));
rjd.enableMotor = false;
m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define piston
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.position.Set(0.0f, 17.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 17.0f));
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, body, b2Vec2(0.0f, 17.0f), b2Vec2(0.0f, 1.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
m_joint2 = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
}
// Create a payload
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 23.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
}
}
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_F:
// m_joint2->EnableMotor(!m_joint2->IsMotorEnabled());
// m_joint2->GetBodyB()->SetAwake(true);
// break;
// case GLFW_KEY_M:
// m_joint1->EnableMotor(!m_joint1->IsMotorEnabled());
// m_joint1->GetBodyB()->SetAwake(true);
// break;
// }
//}
void Step(Settings* settings) override
{
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Keys: (f) toggle friction, (m) toggle motor");
//m_textLine += m_textIncrement;
//float torque = m_joint1->GetMotorTorque(settings.m_hertz);
//g_debugDraw.DrawString(5, m_textLine, "Motor Torque = %5.0f", (float) torque);
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new SliderCrank2;
}
b2RevoluteJoint* m_joint1;
b2PrismaticJoint* m_joint2;
};
static int testIndex = RegisterTest("Examples", "Slider Crank 2", SliderCrank2::Create);

View File

@ -0,0 +1,266 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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.
// Inspired by a contribution from roman_m
// Dimensions scooped from APE (http://www.cove.org/ape/index.htm)
#include "../test.h"
class TheoJansen : public Test
{
public:
void CreateLeg(float s, const b2Vec2& wheelAnchor)
{
b2Vec2 p1(5.4f * s, -6.1f);
b2Vec2 p2(7.2f * s, -1.2f);
b2Vec2 p3(4.3f * s, -1.9f);
b2Vec2 p4(3.1f * s, 0.8f);
b2Vec2 p5(6.0f * s, 1.5f);
b2Vec2 p6(2.5f * s, 3.7f);
b2FixtureDef fd1, fd2;
fd1.filter.groupIndex = -1;
fd2.filter.groupIndex = -1;
fd1.density = 1.0f;
fd2.density = 1.0f;
b2PolygonShape poly1, poly2;
if (s > 0.0f)
{
b2Vec2 vertices[3];
vertices[0] = p1;
vertices[1] = p2;
vertices[2] = p3;
poly1.Set(vertices, 3);
vertices[0] = b2Vec2_zero;
vertices[1] = p5 - p4;
vertices[2] = p6 - p4;
poly2.Set(vertices, 3);
}
else
{
b2Vec2 vertices[3];
vertices[0] = p1;
vertices[1] = p3;
vertices[2] = p2;
poly1.Set(vertices, 3);
vertices[0] = b2Vec2_zero;
vertices[1] = p6 - p4;
vertices[2] = p5 - p4;
poly2.Set(vertices, 3);
}
fd1.shape = &poly1;
fd2.shape = &poly2;
b2BodyDef bd1, bd2;
bd1.type = b2_dynamicBody;
bd2.type = b2_dynamicBody;
bd1.position = m_offset;
bd2.position = p4 + m_offset;
bd1.angularDamping = 10.0f;
bd2.angularDamping = 10.0f;
b2Body* body1 = m_world->CreateBody(&bd1);
b2Body* body2 = m_world->CreateBody(&bd2);
body1->CreateFixture(&fd1);
body2->CreateFixture(&fd2);
{
b2DistanceJointDef jd;
// Using a soft distance constraint can reduce some jitter.
// It also makes the structure seem a bit more fluid by
// acting like a suspension system.
float dampingRatio = 0.5f;
float frequencyHz = 10.0f;
jd.Initialize(body1, body2, p2 + m_offset, p5 + m_offset);
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_world->CreateJoint(&jd);
jd.Initialize(body1, body2, p3 + m_offset, p4 + m_offset);
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_world->CreateJoint(&jd);
jd.Initialize(body1, m_wheel, p3 + m_offset, wheelAnchor + m_offset);
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_world->CreateJoint(&jd);
jd.Initialize(body2, m_wheel, p6 + m_offset, wheelAnchor + m_offset);
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_world->CreateJoint(&jd);
}
{
b2RevoluteJointDef jd;
jd.Initialize(body2, m_chassis, p4 + m_offset);
m_world->CreateJoint(&jd);
}
}
TheoJansen()
{
m_offset.Set(0.0f, 8.0f);
m_motorSpeed = 2.0f;
m_motorOn = true;
b2Vec2 pivot(0.0f, 0.8f);
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(-50.0f, 10.0f));
ground->CreateFixture(&shape, 0.0f);
shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(50.0f, 10.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Balls
for (int32 i = 0; i < 40; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.25f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f + 2.0f * i, 0.5f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
}
// Chassis
{
b2PolygonShape shape;
shape.SetAsBox(2.5f, 1.0f);
b2FixtureDef sd;
sd.density = 1.0f;
sd.shape = &shape;
sd.filter.groupIndex = -1;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = pivot + m_offset;
m_chassis = m_world->CreateBody(&bd);
m_chassis->CreateFixture(&sd);
}
{
b2CircleShape shape;
shape.m_radius = 1.6f;
b2FixtureDef sd;
sd.density = 1.0f;
sd.shape = &shape;
sd.filter.groupIndex = -1;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = pivot + m_offset;
m_wheel = m_world->CreateBody(&bd);
m_wheel->CreateFixture(&sd);
}
{
b2RevoluteJointDef jd;
jd.Initialize(m_wheel, m_chassis, pivot + m_offset);
jd.collideConnected = false;
jd.motorSpeed = m_motorSpeed;
jd.maxMotorTorque = 400.0f;
jd.enableMotor = m_motorOn;
m_motorJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
b2Vec2 wheelAnchor;
wheelAnchor = pivot + b2Vec2(0.0f, -0.8f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
m_wheel->SetTransform(m_wheel->GetPosition(), 120.0f * b2_pi / 180.0f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
m_wheel->SetTransform(m_wheel->GetPosition(), -120.0f * b2_pi / 180.0f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
}
void Step(Settings* settings) override
{
/* g_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, toggle motor = m");
m_textLine += m_textIncrement;*/
Test::Step(settings);
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_A:
// m_motorJoint->SetMotorSpeed(-m_motorSpeed);
// break;
// case GLFW_KEY_S:
// m_motorJoint->SetMotorSpeed(0.0f);
// break;
// case GLFW_KEY_D:
// m_motorJoint->SetMotorSpeed(m_motorSpeed);
// break;
// case GLFW_KEY_M:
// m_motorJoint->EnableMotor(!m_motorJoint->IsMotorEnabled());
// break;
// }
//}
static Test* Create()
{
return new TheoJansen;
}
b2Vec2 m_offset;
b2Body* m_chassis;
b2Body* m_wheel;
b2RevoluteJoint* m_motorJoint;
bool m_motorOn;
float m_motorSpeed;
};
static int testIndex = RegisterTest("Examples", "Theo Jansen", TheoJansen::Create);

View File

@ -0,0 +1,159 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
/// This stress tests the dynamic tree broad-phase. This also shows that tile
/// based collision is _not_ smooth due to Box2D not knowing about adjacency.
class Tiles : public Test
{
public:
enum
{
e_count = 20
};
Tiles()
{
m_fixtureCount = 0;
b2Timer timer;
{
float a = 0.5f;
b2BodyDef bd;
bd.position.y = -a;
b2Body* ground = m_world->CreateBody(&bd);
#if 1
int32 N = 200;
int32 M = 10;
b2Vec2 position;
position.y = 0.0f;
for (int32 j = 0; j < M; ++j)
{
position.x = -N * a;
for (int32 i = 0; i < N; ++i)
{
b2PolygonShape shape;
shape.SetAsBox(a, a, position, 0.0f);
ground->CreateFixture(&shape, 0.0f);
++m_fixtureCount;
position.x += 2.0f * a;
}
position.y -= 2.0f * a;
}
#else
int32 N = 200;
int32 M = 10;
b2Vec2 position;
position.x = -N * a;
for (int32 i = 0; i < N; ++i)
{
position.y = 0.0f;
for (int32 j = 0; j < M; ++j)
{
b2PolygonShape shape;
shape.SetAsBox(a, a, position, 0.0f);
ground->CreateFixture(&shape, 0.0f);
position.y -= 2.0f * a;
}
position.x += 2.0f * a;
}
#endif
}
{
float a = 0.5f;
b2PolygonShape shape;
shape.SetAsBox(a, a);
b2Vec2 x(-7.0f, 0.75f);
b2Vec2 y;
b2Vec2 deltaX(0.5625f, 1.25f);
b2Vec2 deltaY(1.125f, 0.0f);
for (int32 i = 0; i < e_count; ++i)
{
y = x;
for (int32 j = i; j < e_count; ++j)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
//if (i == 0 && j == 0)
//{
// bd.allowSleep = false;
//}
//else
//{
// bd.allowSleep = true;
//}
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
++m_fixtureCount;
y += deltaY;
}
x += deltaX;
}
}
m_createTime = timer.GetMilliseconds();
}
void Step(Settings* settings) override
{
const b2ContactManager& cm = m_world->GetContactManager();
int32 height = cm.m_broadPhase.GetTreeHeight();
int32 leafCount = cm.m_broadPhase.GetProxyCount();
int32 minimumNodeCount = 2 * leafCount - 1;
float minimumHeight = ceilf(logf(float(minimumNodeCount)) / logf(2.0f));
//g_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", height, int32(minimumHeight));
//m_textLine += m_textIncrement;
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "create time = %6.2f ms, fixture count = %d",
// m_createTime, m_fixtureCount);
//m_textLine += m_textIncrement;
//b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
//if (m_stepCount == 400)
//{
// tree->RebuildBottomUp();
//}
}
static Test* Create()
{
return new Tiles;
}
int32 m_fixtureCount;
float m_createTime;
};
static int testIndex = RegisterTest("Benchmark", "Tiles", Tiles::Create);

View File

@ -0,0 +1,131 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
#include "box2d/b2_time_of_impact.h"
class TimeOfImpact : public Test
{
public:
TimeOfImpact()
{
m_shapeA.SetAsBox(25.0f, 5.0f);
m_shapeB.SetAsBox(2.5f, 2.5f);
}
static Test* Create()
{
return new TimeOfImpact;
}
void Step(Settings* settings) override
{
Test::Step(settings);
b2Sweep sweepA;
sweepA.c0.Set(24.0f, -60.0f);
sweepA.a0 = 2.95f;
sweepA.c = sweepA.c0;
sweepA.a = sweepA.a0;
sweepA.localCenter.SetZero();
b2Sweep sweepB;
sweepB.c0.Set(53.474274f, -50.252514f);
sweepB.a0 = 513.36676f; // - 162.0f * b2_pi;
sweepB.c.Set(54.595478f, -51.083473f);
sweepB.a = 513.62781f; // - 162.0f * b2_pi;
sweepB.localCenter.SetZero();
//sweepB.a0 -= 300.0f * b2_pi;
//sweepB.a -= 300.0f * b2_pi;
b2TOIInput input;
input.proxyA.Set(&m_shapeA, 0);
input.proxyB.Set(&m_shapeB, 0);
input.sweepA = sweepA;
input.sweepB = sweepB;
input.tMax = 1.0f;
b2TOIOutput output;
b2TimeOfImpact(&output, &input);
//g_debugDraw.DrawString(5, m_textLine, "toi = %g", output.t);
//m_textLine += m_textIncrement;
extern B2_API int32 b2_toiMaxIters, b2_toiMaxRootIters;
//g_debugDraw.DrawString(5, m_textLine, "max toi iters = %d, max root iters = %d", b2_toiMaxIters, b2_toiMaxRootIters);
//m_textLine += m_textIncrement;
b2Vec2 vertices[b2_maxPolygonVertices];
b2Transform transformA;
sweepA.GetTransform(&transformA, 0.0f);
for (int32 i = 0; i < m_shapeA.m_count; ++i)
{
vertices[i] = b2Mul(transformA, m_shapeA.m_vertices[i]);
}
// g_debugDraw.DrawPolygon(vertices, m_shapeA.m_count, b2Color(0.9f, 0.9f, 0.9f));
b2Transform transformB;
sweepB.GetTransform(&transformB, 0.0f);
//b2Vec2 localPoint(2.0f, -0.1f);
for (int32 i = 0; i < m_shapeB.m_count; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
// g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.5f, 0.9f, 0.5f));
sweepB.GetTransform(&transformB, output.t);
for (int32 i = 0; i < m_shapeB.m_count; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
// g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.5f, 0.7f, 0.9f));
sweepB.GetTransform(&transformB, 1.0f);
for (int32 i = 0; i < m_shapeB.m_count; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
// g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.9f, 0.5f, 0.5f));
#if 0
for (float t = 0.0f; t < 1.0f; t += 0.1f)
{
sweepB.GetTransform(&transformB, t);
for (int32 i = 0; i < m_shapeB.m_count; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.9f, 0.5f, 0.5f));
}
#endif
}
b2PolygonShape m_shapeA;
b2PolygonShape m_shapeB;
};
static int testIndex = RegisterTest("Collision", "Time of Impact", TimeOfImpact::Create);

View File

@ -0,0 +1,102 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
class Tumbler : public Test
{
public:
enum
{
e_count = 800
};
Tumbler()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.allowSleep = false;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 10.0f, b2Vec2( 10.0f, 0.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(0.5f, 10.0f, b2Vec2(-10.0f, 0.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, 10.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, -10.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
b2RevoluteJointDef jd;
jd.bodyA = ground;
jd.bodyB = body;
jd.localAnchorA.Set(0.0f, 10.0f);
jd.localAnchorB.Set(0.0f, 0.0f);
jd.referenceAngle = 0.0f;
jd.motorSpeed = 0.05f * b2_pi;
jd.maxMotorTorque = 1e8f;
jd.enableMotor = true;
m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
m_count = 0;
}
void Step(Settings* settings) override
{
Test::Step(settings);
if (m_count < e_count)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.125f, 0.125f);
body->CreateFixture(&shape, 1.0f);
++m_count;
}
}
static Test* Create()
{
return new Tumbler;
}
b2RevoluteJoint* m_joint;
int32 m_count;
};
static int testIndex = RegisterTest("Benchmark", "Tumbler", Tumbler::Create);

View File

@ -0,0 +1,218 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
// Test distance joints, body destruction, and joint destruction.
class Web : public Test
{
public:
Web()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 5.0f);
m_bodies[0] = m_world->CreateBody(&bd);
m_bodies[0]->CreateFixture(&shape, 5.0f);
bd.position.Set(5.0f, 5.0f);
m_bodies[1] = m_world->CreateBody(&bd);
m_bodies[1]->CreateFixture(&shape, 5.0f);
bd.position.Set(5.0f, 15.0f);
m_bodies[2] = m_world->CreateBody(&bd);
m_bodies[2]->CreateFixture(&shape, 5.0f);
bd.position.Set(-5.0f, 15.0f);
m_bodies[3] = m_world->CreateBody(&bd);
m_bodies[3]->CreateFixture(&shape, 5.0f);
b2DistanceJointDef jd;
b2Vec2 p1, p2, d;
float frequencyHz = 2.0f;
float dampingRatio = 0.0f;
jd.bodyA = ground;
jd.bodyB = m_bodies[0];
jd.localAnchorA.Set(-10.0f, 0.0f);
jd.localAnchorB.Set(-0.5f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_joints[0] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[1];
jd.localAnchorA.Set(10.0f, 0.0f);
jd.localAnchorB.Set(0.5f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_joints[1] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[2];
jd.localAnchorA.Set(10.0f, 20.0f);
jd.localAnchorB.Set(0.5f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_joints[2] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[3];
jd.localAnchorA.Set(-10.0f, 20.0f);
jd.localAnchorB.Set(-0.5f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_joints[3] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[0];
jd.bodyB = m_bodies[1];
jd.localAnchorA.Set(0.5f, 0.0f);
jd.localAnchorB.Set(-0.5f, 0.0f);;
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_joints[4] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[1];
jd.bodyB = m_bodies[2];
jd.localAnchorA.Set(0.0f, 0.5f);
jd.localAnchorB.Set(0.0f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_joints[5] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[2];
jd.bodyB = m_bodies[3];
jd.localAnchorA.Set(-0.5f, 0.0f);
jd.localAnchorB.Set(0.5f, 0.0f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_joints[6] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[3];
jd.bodyB = m_bodies[0];
jd.localAnchorA.Set(0.0f, -0.5f);
jd.localAnchorB.Set(0.0f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
m_joints[7] = m_world->CreateJoint(&jd);
}
}
//void Keyboard(int key) override
//{
// switch (key)
// {
// case GLFW_KEY_B:
// for (int32 i = 0; i < 4; ++i)
// {
// if (m_bodies[i])
// {
// m_world->DestroyBody(m_bodies[i]);
// m_bodies[i] = NULL;
// break;
// }
// }
// break;
// case GLFW_KEY_J:
// for (int32 i = 0; i < 8; ++i)
// {
// if (m_joints[i])
// {
// m_world->DestroyJoint(m_joints[i]);
// m_joints[i] = NULL;
// break;
// }
// }
// break;
// }
//}
void Step(Settings* settings) override
{
Test::Step(settings);
//g_debugDraw.DrawString(5, m_textLine, "Press: (b) to delete a body, (j) to delete a joint");
//m_textLine += m_textIncrement;
}
void JointDestroyed(b2Joint* joint) override
{
for (int32 i = 0; i < 8; ++i)
{
if (m_joints[i] == joint)
{
m_joints[i] = NULL;
break;
}
}
}
static Test* Create()
{
return new Web;
}
b2Body* m_bodies[4];
b2Joint* m_joints[8];
};
static int testIndex = RegisterTest("Examples", "Web", Web::Create);

View File

@ -0,0 +1,126 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "settings.h"
#include "../test.h"
//#include "imgui/imgui.h"
// Test the wheel joint with motor, spring, and limit options.
class WheelJoint : public Test
{
public:
WheelJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
m_enableLimit = true;
m_enableMotor = false;
m_motorSpeed = 10.0f;
{
b2CircleShape shape;
shape.m_radius = 2.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
b2WheelJointDef jd;
// Horizontal
jd.Initialize(ground, body, bd.position, b2Vec2(0.0f, 1.0f));
jd.motorSpeed = m_motorSpeed;
jd.maxMotorTorque = 10000.0f;
jd.enableMotor = m_enableMotor;
jd.lowerTranslation = -3.0f;
jd.upperTranslation = 3.0f;
jd.enableLimit = m_enableLimit;
float hertz = 1.0f;
float dampingRatio = 0.7f;
b2LinearStiffness(jd.stiffness, jd.damping, hertz, dampingRatio, ground, body);
m_joint = (b2WheelJoint*)m_world->CreateJoint(&jd);
}
}
void Step(Settings* settings) override
{
Test::Step(settings);
// float torque = m_joint->GetMotorTorque(settings.hz);
//g_debugDraw.DrawString(5, m_textLine, "Motor Torque = %4.0f", torque);
//m_textLine += m_textIncrement;
// b2Vec2 F = m_joint->GetReactionForce(settings.hz);
//g_debugDraw.DrawString(5, m_textLine, "Reaction Force = (%4.1f, %4.1f)", F.x, F.y);
//m_textLine += m_textIncrement;
}
/*void UpdateUI() override
{
ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
if (ImGui::Checkbox("Limit", &m_enableLimit))
{
m_joint->EnableLimit(m_enableLimit);
}
if (ImGui::Checkbox("Motor", &m_enableMotor))
{
m_joint->EnableMotor(m_enableMotor);
}
if (ImGui::SliderFloat("Speed", &m_motorSpeed, -100.0f, 100.0f, "%.0f"))
{
m_joint->SetMotorSpeed(m_motorSpeed);
}
ImGui::End();*/
//}
static Test* Create()
{
return new WheelJoint;
}
b2WheelJoint* m_joint;
float m_motorSpeed;
bool m_enableMotor;
bool m_enableLimit;
};
static int testIndex = RegisterTest("Joints", "Wheel", WheelJoint::Create);

View File

@ -0,0 +1,165 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// 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 "../test.h"
//#include "imgui/imgui.h"
/// This test shows how a distance joint can be used to stabilize a chain of
/// bodies with a heavy payload. Notice that the distance joint just prevents
/// excessive stretching and has no other effect.
/// By disabling the distance joint you can see that the Box2D solver has trouble
/// supporting heavy bodies with light bodies. Try playing around with the
/// densities, time step, and iterations to see how they affect stability.
/// This test also shows how to use contact filtering. Filtering is configured
/// so that the payload does not collide with the chain.
class WreckingBall : public Test
{
public:
WreckingBall()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
fd.filter.categoryBits = 0x0001;
fd.filter.maskBits = 0xFFFF & ~0x0002;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const int32 N = 10;
const float y = 15.0f;
m_distanceJointDef.localAnchorA.Set(0.0f, y);
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + 1.0f * i, y);
if (i == N - 1)
{
bd.position.Set(1.0f * i, y);
bd.angularDamping = 0.4f;
}
b2Body* body = m_world->CreateBody(&bd);
if (i == N - 1)
{
b2CircleShape circleShape;
circleShape.m_radius = 1.5f;
b2FixtureDef sfd;
sfd.shape = &circleShape;
sfd.density = 100.0f;
sfd.filter.categoryBits = 0x0002;
body->CreateFixture(&sfd);
}
else
{
body->CreateFixture(&fd);
}
b2Vec2 anchor(float(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
m_distanceJointDef.localAnchorB.SetZero();
float extraLength = 0.01f;
m_distanceJointDef.minLength = 0.0f;
m_distanceJointDef.maxLength = N - 1.0f + extraLength;
m_distanceJointDef.bodyB = prevBody;
}
{
m_distanceJointDef.bodyA = ground;
m_distanceJoint = m_world->CreateJoint(&m_distanceJointDef);
m_stabilize = true;
}
}
//void UpdateUI() override
//{
// ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
// ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
// ImGui::Begin("Wrecking Ball Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
// if (ImGui::Checkbox("Stabilize", &m_stabilize))
// {
// if (m_stabilize == true && m_distanceJoint == nullptr)
// {
// m_distanceJoint = m_world->CreateJoint(&m_distanceJointDef);
// }
// else if (m_stabilize == false && m_distanceJoint != nullptr)
// {
// m_world->DestroyJoint(m_distanceJoint);
// m_distanceJoint = nullptr;
// }
// }
// ImGui::End();
//}
void Step(Settings* settings) override
{
Test::Step(settings);
//if (m_distanceJoint)
//{
// g_debugDraw.DrawString(5, m_textLine, "Distance Joint ON");
//}
//else
//{
// g_debugDraw.DrawString(5, m_textLine, "Distance Joint OFF");
//}
//m_textLine += m_textIncrement;
}
static Test* Create()
{
return new WreckingBall;
}
b2DistanceJointDef m_distanceJointDef;
b2Joint* m_distanceJoint;
bool m_stabilize;
};
static int testIndex = RegisterTest("Examples", "Wrecking Ball", WreckingBall::Create);

View File

@ -0,0 +1,68 @@
/* Copyright (c) 2007 Scott Lembcke
*
* 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 "ChipmunkDebugDraw.h"
typedef struct ChipmunkDemo ChipmunkDemo;
typedef cpSpace *(*ChipmunkDemoInitFunc)(void);
typedef void (*ChipmunkDemoUpdateFunc)(cpSpace *space, double dt);
typedef void (*ChipmunkDemoDrawFunc)(cpSpace *space);
typedef void (*ChipmunkDemoDestroyFunc)(cpSpace *space);
struct ChipmunkDemo {
const char *name;
double timestep;
ChipmunkDemoInitFunc initFunc;
ChipmunkDemoUpdateFunc updateFunc;
ChipmunkDemoDrawFunc drawFunc;
ChipmunkDemoDestroyFunc destroyFunc;
};
static inline cpFloat
frand(void)
{
return (cpFloat)rand()/(cpFloat)RAND_MAX;
}
static inline cpVect
frand_unit_circle(){
cpVect v = cpv(frand()*2.0f - 1.0f, frand()*2.0f - 1.0f);
return (cpvlengthsq(v) < 1.0f ? v : frand_unit_circle());
}
extern int ChipmunkDemoTicks;
extern double ChipmunkDemoTime;
extern cpVect ChipmunkDemoKeyboard;
extern cpVect ChipmunkDemoMouse;
extern cpBool ChipmunkDemoRightClick;
extern cpBool ChipmunkDemoRightDown;
extern char const *ChipmunkDemoMessageString;
void ChipmunkDemoPrintString(char const *fmt, ...);
extern cpShapeFilter GRAB_FILTER;
extern cpShapeFilter NOT_GRABBABLE_FILTER;
void ChipmunkDemoDefaultDrawImpl(cpSpace *space);
void ChipmunkDemoFreeSpaceChildren(cpSpace *space);

View File

@ -56,7 +56,9 @@ public:
addTest("Box2D - Basic", []() { return new (std::nothrow) Box2DTests(); });
// addTest("Box2D - TestBed", []() { return new (std::nothrow) Box2dTestBedSuite(); });
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
addTest("Box2D - TestBed", []() { return new (std::nothrow) Box2DTestBedTests(); });
#endif
addTest("Chipmunk2D - Basic", []() { return new ChipmunkTests(); });
#if defined(CC_PLATFORM_PC)
addTest("Chipmunk2D - TestBed", []() { return new ChipmunkTestBedTests(); });

View File

@ -27,7 +27,7 @@
#include "Box2DTest/Box2dTest.h"
//#include "Box2DTestBed/Box2dView.h"
#include "Box2DTestBed/Box2DTestBed.h"
#include "ChipmunkTest/ChipmunkTest.h"