<file_sep>/*
 * GameGlobals.h
 *
 *  Created on: 08-Oct-2014
 *      Author: woigames
 */

#ifndef GAMEGLOBALS_H_
#define GAMEGLOBALS_H_

#include "cocos2d.h"
#include "Box2D/Box2D.h"

using namespace cocos2d;
using namespace std;

#define SCREEN_SIZE GameGlobals::screen_size_
#define PTM_RATIO 128
#define SCREEN_TO_WORLD(value) (value) / PTM_RATIO
#define WORLD_TO_SCREEN(value) (value) * PTM_RATIO

#define MIN_SHAPE_SIZE 0.1f
#define MAX_SHAPE_SIZE 0.25f
#define NUM_TRAJECTORY_STEPS 60
#define IMPULSE_MAGNITUDE

typedef pair<b2Fixture*, b2Fixture*> fixture_pair;

enum EBodySize
{
	E_BODY_SMALL = 0,
	E_BODY_MEDIUM,
	E_BODY_LARGE,
};

class GameGlobals {
public:
	GameGlobals(){};
	~GameGlobals(){};

	static void Init();

	static b2Body* GetBodyForWorld(b2World* world, b2BodyType type, b2Shape::Type shape, EBodySize body_size = E_BODY_MEDIUM);
	static b2PolygonShape GetRandomBoxShape(EBodySize body_size = E_BODY_MEDIUM);
	static b2CircleShape GetRandomCircleShape();
	static void GetTrajectory(b2Vec2 initial_position, b2Vec2 initial_velocity, b2Vec2 gravity, vector<Vec2> &points);

	// buoyancy related
	static bool Inside(b2Vec2 cp1, b2Vec2 cp2, b2Vec2 p);
	static b2Vec2 Intersection(b2Vec2 cp1, b2Vec2 cp2, b2Vec2 s, b2Vec2 e);
	static bool FindIntersectionOfFixtures(b2Fixture* fA, b2Fixture* fB, vector<b2Vec2>& output_vertices);
	static b2Vec2 ComputeCentroid(vector<b2Vec2> vs, float& area);

	static Size screen_size_;
};

#endif /* GAMEGLOBALS_H_ */
<file_sep>#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "GameGlobals.h"

class HelloWorld : public Layer
{
public:
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();  
    
    // a selector callback
    void menuCloseCallback(Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);

    void CreateBoundary();
	void CreateBall();

    void GenerateTrajectory(b2Vec2 starting_velocity);

    virtual void update(float dt);

    virtual bool onTouchBegan(Touch* touch, Event* event);
	virtual void onTouchMoved(Touch* touch, Event* event);
	virtual void onTouchEnded(Touch* touch, Event* event);

protected:
	Rect boundary_rect_;
    Vec2 last_touch_;
	b2World* world_;
	b2Body* ball_body_;
    DrawNode* ball_node_;
    DrawNode* trajectory_;
    b2Vec2 impulse_;
};

#endif // __HELLOWORLD_SCENE_H__
<file_sep>#include "HelloWorldScene.h"

Scene* HelloWorld::createScene()
{
    auto scene = Scene::create();
    auto layer = HelloWorld::create();
    scene->addChild(layer);
    return scene;
}

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    
    boundary_rect_ = Rect(50, 50, SCREEN_SIZE.width - 100, SCREEN_SIZE.height - 100);
    last_touch_ = Vec2::ZERO;
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    auto closeItem = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
	closeItem->setPosition(Vec2(origin.x + SCREEN_SIZE.width - closeItem->getContentSize().width/2, origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, nullptr);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);
    
    b2Vec2 gravity;
	gravity.Set(0.0f, -10.0f);
	world_ = new b2World(gravity);

    trajectory_ = DrawNode::create();
    addChild(trajectory_);

    CreateBoundary();
    CreateBall();

//    setTouchEnabled(true);

    auto listener = EventListenerTouchOneByOne::create();
    listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
    listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
    listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

    scheduleUpdate();
    return true;
}

void HelloWorld::CreateBoundary()
{
	b2BodyDef groundBodyDef;
	groundBodyDef.position.Set(0, 0);

	b2Body* groundBody = world_->CreateBody(&groundBodyDef);

	// Define the ground box shape.
	b2EdgeShape groundBox;

	// bottom
	groundBox.Set(b2Vec2(boundary_rect_.getMinX()/PTM_RATIO, boundary_rect_.getMinY()/PTM_RATIO), b2Vec2(boundary_rect_.getMaxX()/PTM_RATIO, boundary_rect_.getMinY()/PTM_RATIO));
	groundBody->CreateFixture(&groundBox,0);
	groundBox.Set(b2Vec2(boundary_rect_.getMinX()/PTM_RATIO, boundary_rect_.getMinY()/PTM_RATIO), b2Vec2(boundary_rect_.getMinX()/PTM_RATIO, boundary_rect_.getMaxY()/PTM_RATIO));
	groundBody->CreateFixture(&groundBox,0);
	groundBox.Set(b2Vec2(boundary_rect_.getMaxX()/PTM_RATIO, boundary_rect_.getMinY()/PTM_RATIO), b2Vec2(boundary_rect_.getMaxX()/PTM_RATIO, boundary_rect_.getMaxY()/PTM_RATIO));
	groundBody->CreateFixture(&groundBox,0);
}

void HelloWorld::CreateBall()
{
    // create body
    b2BodyDef ball_body_def;
    ball_body_def.type = b2_dynamicBody;
    ball_body_ = world_->CreateBody(&ball_body_def);
    ball_body_->SetTransform(b2Vec2(SCREEN_SIZE.width*0.125f / PTM_RATIO, SCREEN_SIZE.height*0.125f / PTM_RATIO), 0.0f);

    b2CircleShape ball_shape;
    ball_shape.m_radius = 0.2f;

    // create fixture
    b2FixtureDef ball_fixture_def;
    ball_fixture_def.shape = &ball_shape;
    ball_fixture_def.restitution = 0.5f;

    ball_body_->CreateFixture(&ball_fixture_def);

    ball_node_ = DrawNode::create();
    ball_node_->drawDot(CCPointZero, 25, ccc4f(1, 1, 1, 0.5f));
    addChild(ball_node_);
}

void HelloWorld::GenerateTrajectory(b2Vec2 starting_velocity)
{
	b2Vec2 gravity(0.0f, -10.0f);

	vector<Vec2> points;
	GameGlobals::GetTrajectory(ball_body_->GetPosition(), starting_velocity, gravity, points);

	trajectory_->clear();
	for(int i = 0; i < NUM_TRAJECTORY_STEPS; ++i)
		trajectory_->drawDot(points[i], 1, ccc4f(1, 1, 1, 1.0f - (float)i/(float)NUM_TRAJECTORY_STEPS));
}

void HelloWorld::update(float dt)
{
	world_->Step(dt, 8, 3);
	ball_node_->setPosition(ball_body_->GetPosition().x * PTM_RATIO, ball_body_->GetPosition().y * PTM_RATIO);
}

bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{
	impulse_ = b2Vec2(5.0f, 5.0f);	//b2Vec2(7.5f, 7.5f);
	impulse_.x /= ball_body_->GetMass();
	impulse_.y /= ball_body_->GetMass();

    GenerateTrajectory(impulse_);

    last_touch_ = touch->getLocationInView();

    return true;
}

void HelloWorld::onTouchMoved(Touch* touch, Event* event)
{
	Vec2 curr_touch = touch->getLocationInView();

	float angle = CC_DEGREES_TO_RADIANS( (last_touch_.x - curr_touch.x) / 5 );
	b2Vec2 modified_impulse(impulse_.x * cosf(angle) - impulse_.y * sinf(angle), impulse_.x * sinf(angle) + impulse_.y * cosf(angle));
	impulse_ = modified_impulse;
	GenerateTrajectory(impulse_);

	last_touch_.set(curr_touch);
}

void HelloWorld::onTouchEnded(Touch* touch, Event* event)
{
	ball_body_->ApplyLinearImpulse(impulse_, ball_body_->GetWorldCenter(), true);
	impulse_ = b2Vec2_zero;
	last_touch_.set(Vec2::ZERO);
	trajectory_->clear();
}

void HelloWorld::menuCloseCallback(Ref* pSender)
{
	Director::getInstance()->replaceScene(HelloWorld::createScene());
}
<file_sep>/*
 * BuoyancyTest.h
 *
 *  Created on: 08-Oct-2014
 *      Author: woigames
 */

#ifndef BUOYANCYTEST_H_
#define BUOYANCYTEST_H_

#include "GameGlobals.h"

class GLESDebugDraw;

class BuoyancyTest : public Layer, public b2ContactListener {
public:
	virtual ~BuoyancyTest();

	static Scene* createScene();
	virtual bool init();
    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
    void onDraw(const Mat4 &transform, uint32_t flags);

	virtual void update(float dt);
	virtual void BeginContact(b2Contact* contact);
	virtual void EndContact(b2Contact* contact);

    virtual bool onTouchBegan(Touch* touch, Event* event);
	virtual void onTouchMoved(Touch* touch, Event* event);
	virtual void onTouchEnded(Touch* touch, Event* event);

	void CreateBuoyancyTest();
	void CreateFluid();
	void CreateTestBodies();
	void CreateBodyWithJoints();
//	void AddDynamicBody(Vec2 position, EBodySize size);
//	void AddStaticBody(Vec2 position, EBodySize size);

	void UpdateBuoyancy();
	void ApplyBuoyancyForce(b2Fixture* fixture_a, b2Fixture* fixture_b, float area, b2Vec2 centroid);
	void ApplyDrag(b2Fixture* fixture_a, b2Fixture* fixture_b, float area, b2Vec2 centroid);
	void UpdateTrajectory();

	CREATE_FUNC(BuoyancyTest);
    void menuCloseCallback(Ref* pSender);

private:
    b2Vec2 gravity_;
	b2World* world_;
	b2Body* base_;
	b2Body* fluid_;
	set<fixture_pair> fixture_pairs_;

	b2Body* player_body_;
    DrawNode* trajectory_;
    b2Vec2 impulse_;
    Vec2 last_touch_;

	GLESDebugDraw* debug_draw_;

    CustomCommand custom_command_;
};

#endif /* BUOYANCYTEST_H_ */
<file_sep>/*
 * BuoyancyTest.cpp
 *
 *  Created on: 08-Oct-2014
 *      Author: woigames
 */

#include "BuoyancyTest.h"
#include "GLES-Render.h"

BuoyancyTest::~BuoyancyTest() {
	// TODO Auto-generated destructor stub
}

Scene* BuoyancyTest::createScene()
{
	auto scene = Scene::create();
	auto layer = BuoyancyTest::create();
	scene->addChild(layer);
	return scene;
}

bool BuoyancyTest::init()
{
	if(!Layer::init())
		return false;

	gravity_ = b2Vec2_zero;
	world_ = nullptr;
	base_ = nullptr;
	fluid_ = nullptr;
	fixture_pairs_.clear();

	player_body_ = nullptr;
	trajectory_ = nullptr;
	impulse_ = b2Vec2_zero;
	last_touch_ = Vec2::ZERO;

	debug_draw_ = NULL;

    auto closeItem = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(BuoyancyTest::menuCloseCallback, this));
	closeItem->setPosition(Vec2(SCREEN_SIZE.width - closeItem->getContentSize().width/2, closeItem->getContentSize().height/2));

    auto menu = Menu::create(closeItem, nullptr);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

	CreateBuoyancyTest();

	auto listener = EventListenerTouchOneByOne::create();
	listener->onTouchBegan = CC_CALLBACK_2(BuoyancyTest::onTouchBegan, this);
	listener->onTouchMoved = CC_CALLBACK_2(BuoyancyTest::onTouchMoved, this);
	listener->onTouchEnded = CC_CALLBACK_2(BuoyancyTest::onTouchEnded, this);
	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

	scheduleUpdate();
	return true;
}

void BuoyancyTest::CreateBuoyancyTest()
{
	gravity_.Set(0.0f, -9.8f);
	world_ = new b2World(gravity_);
	world_->SetContactListener(this);

	CreateFluid();
	CreateTestBodies();
	trajectory_ = DrawNode::create();
	addChild(trajectory_);

	// create debug draw
	debug_draw_ = new GLESDebugDraw(PTM_RATIO);
	uint32 flags = 0;
	flags += b2Draw::e_shapeBit;
	flags += b2Draw::e_jointBit;
	debug_draw_->SetFlags(flags);
	world_->SetDebugDraw(debug_draw_);
}

void BuoyancyTest::CreateFluid()
{
	// create base
	b2BodyDef base_body_def;
	base_body_def.type = b2_staticBody;
	base_ = world_->CreateBody(&base_body_def);

	b2ChainShape base_shape;
	b2Vec2 vertices[4] = { b2Vec2(0, SCREEN_TO_WORLD(SCREEN_SIZE.height/3)), b2Vec2(0, 0), b2Vec2(SCREEN_TO_WORLD(SCREEN_SIZE.width), 0), b2Vec2(SCREEN_TO_WORLD(SCREEN_SIZE.width), SCREEN_TO_WORLD(SCREEN_SIZE.height/3)) };
	base_shape.CreateChain(vertices, 4);
	base_->CreateFixture(&base_shape, 0);

	// create fluid
	b2BodyDef fluid_body_def;
	fluid_body_def.type = b2_staticBody;
	fluid_ = world_->CreateBody(&fluid_body_def);

	b2PolygonShape fluid_shape;
	fluid_shape.SetAsBox( SCREEN_TO_WORLD(SCREEN_SIZE.width/2), SCREEN_TO_WORLD(SCREEN_SIZE.height/6), b2Vec2(SCREEN_TO_WORLD(SCREEN_SIZE.width/2), SCREEN_TO_WORLD(SCREEN_SIZE.height/6)), 0 );
	b2FixtureDef fluid_fixture_def;
	fluid_fixture_def.shape = &fluid_shape;
	fluid_fixture_def.isSensor = true;
	fluid_fixture_def.density = 1.5f;
	fluid_->CreateFixture(&fluid_fixture_def);
}

void BuoyancyTest::CreateTestBodies()
{
	// create a base for player
	b2Body* test_body = GameGlobals::GetBodyForWorld(world_, b2_staticBody, b2Shape::e_polygon, E_BODY_MEDIUM);
	test_body->SetTransform( b2Vec2( SCREEN_TO_WORLD(SCREEN_SIZE.width * 0.5f), SCREEN_TO_WORLD(SCREEN_SIZE.height * 0.3f) ), 0 );
	test_body->GetFixtureList()->SetFriction(1.0f);

	// create player
	b2BodyDef ball_body_def;
	ball_body_def.type = b2_dynamicBody;
	player_body_ = world_->CreateBody(&ball_body_def);
	player_body_->SetTransform(b2Vec2(SCREEN_SIZE.width * 0.5f / PTM_RATIO, SCREEN_SIZE.height * 0.5f / PTM_RATIO), 0.0f);
	player_body_->SetFixedRotation(false);

	b2CircleShape ball_shape;
	ball_shape.m_radius = 0.2f;
//	b2PolygonShape ball_shape;
//	ball_shape.SetAsBox(0.2f, 0.2f);
	b2FixtureDef ball_fixture_def;
	ball_fixture_def.friction = 1.0f;
	ball_fixture_def.shape = &ball_shape;

	player_body_->CreateFixture(&ball_fixture_def);

	CreateBodyWithJoints();
}

void BuoyancyTest::CreateBodyWithJoints()
{
	// create the floating body
	b2Vec2 position;
	position.Set(SCREEN_TO_WORLD(SCREEN_SIZE.width * 0.8f), SCREEN_TO_WORLD(SCREEN_SIZE.height * 0.3f));
	b2Body* test_body = GameGlobals::GetBodyForWorld(world_, b2_dynamicBody, b2Shape::e_polygon, E_BODY_LARGE);
	test_body->SetTransform(position, 0);
	test_body->ApplyTorque(100.0f, true);

	// create two revolute joints to anchor the floating body
	b2DistanceJointDef distance_joint_def;
	b2Vec2 base_anchor;
	b2Vec2 body_anchor;

	base_anchor.Set(SCREEN_TO_WORLD(SCREEN_SIZE.width * 0.65f), 0.0f);
	body_anchor.Set(SCREEN_TO_WORLD(SCREEN_SIZE.width * 0.75f), SCREEN_TO_WORLD(SCREEN_SIZE.height * 0.2f));
	distance_joint_def.Initialize(base_, test_body, base_anchor, body_anchor);
	world_->CreateJoint(&distance_joint_def);

	base_anchor.Set(SCREEN_TO_WORLD(SCREEN_SIZE.width * 0.95f), 0.0f);
	body_anchor.Set(SCREEN_TO_WORLD(SCREEN_SIZE.width * 0.85f), SCREEN_TO_WORLD(SCREEN_SIZE.height * 0.2f));
	distance_joint_def.Initialize(base_, test_body, base_anchor, body_anchor);
	world_->CreateJoint(&distance_joint_def);
}

void BuoyancyTest::update(float dt)
{
	world_->Step(dt, 8, 3);
	UpdateBuoyancy();
}

void BuoyancyTest::BeginContact(b2Contact* contact)
{
	b2Fixture* fixture_a = contact->GetFixtureA();
	b2Fixture* fixture_b = contact->GetFixtureB();

	if(fixture_a->IsSensor() && fixture_b->GetBody()->GetType() == b2_dynamicBody)
		fixture_pairs_.insert(make_pair(fixture_a, fixture_b));
	else if(fixture_b->IsSensor() && fixture_a->GetBody()->GetType() == b2_dynamicBody)
		fixture_pairs_.insert(make_pair(fixture_b, fixture_a));
	else
	{
		if( (fixture_a->GetBody() == player_body_ && fixture_b->GetBody()->GetType() == b2_dynamicBody) ||
				(fixture_b->GetBody() == player_body_ && fixture_a->GetBody()->GetType() == b2_dynamicBody) )
			player_body_->SetLinearVelocity(b2Vec2_zero);
	}
}

void BuoyancyTest::EndContact(b2Contact* contact)
{
	b2Fixture* fixture_a = contact->GetFixtureA();
	b2Fixture* fixture_b = contact->GetFixtureB();

	if(fixture_a->IsSensor() && fixture_b->GetBody()->GetType() == b2_dynamicBody)
		fixture_pairs_.erase(make_pair(fixture_a, fixture_b));
	else if(fixture_b->IsSensor() && fixture_a->GetBody()->GetType() == b2_dynamicBody)
		fixture_pairs_.erase(make_pair(fixture_b, fixture_a));
}

void BuoyancyTest::UpdateBuoyancy()
{
	set<fixture_pair>::iterator it = fixture_pairs_.begin();
	set<fixture_pair>::iterator end = fixture_pairs_.end();
	while (it != end) {

		//fixtureA is the fluid
		b2Fixture* fixture_a = it->first;
		b2Fixture* fixture_b = it->second;

		float density = fixture_a->GetDensity();

		vector<b2Vec2> intersectionPoints;
		if (GameGlobals::FindIntersectionOfFixtures(fixture_a, fixture_b, intersectionPoints)) {

			//find centroid
			float area = 0;
			b2Vec2 centroid = GameGlobals::ComputeCentroid(intersectionPoints, area);

			ApplyBuoyancyForce(fixture_a, fixture_b, area, centroid);
			ApplyDrag(fixture_a, fixture_b, area, centroid);
		}

		++it;
	}
}

void BuoyancyTest::ApplyBuoyancyForce(b2Fixture* fixture_a, b2Fixture* fixture_b, float area, b2Vec2 centroid)
{
	float displacedMass = fixture_a->GetDensity() * area;
	b2Vec2 buoyancyForce = displacedMass * -gravity_;
	fixture_b->GetBody()->ApplyForce(buoyancyForce, centroid, true);
}

void BuoyancyTest::ApplyDrag(b2Fixture* fixture_a, b2Fixture* fixture_b, float area, b2Vec2 centroid)
{
	b2Vec2 relative_velocity = fixture_b->GetBody()->GetLinearVelocityFromWorldPoint(centroid) - fixture_a->GetBody()->GetLinearVelocityFromWorldPoint(centroid);
	float relative_velocity_mag = relative_velocity.Normalize();

	// apply linear drag
	float drag_mag = fixture_a->GetDensity() * relative_velocity_mag * relative_velocity_mag;
	b2Vec2 drag_force = drag_mag * -relative_velocity;
	fixture_b->GetBody()->ApplyForce(drag_force, centroid, true);

	// apply angular drag
//	float angular_drag = area * -fixture_b->GetBody()->GetAngularVelocity();
//	fixture_b->GetBody()->ApplyTorque(angular_drag, true);
}

void BuoyancyTest::UpdateTrajectory()
{
	vector<Vec2> points;
	GameGlobals::GetTrajectory(player_body_->GetPosition(), impulse_, gravity_, points);
	trajectory_->clear();
	for(int i = 0; i < NUM_TRAJECTORY_STEPS; ++i)
		trajectory_->drawDot(points[i], 1, ccc4f(1, 1, 1, 1.0f - (float)i/(float)NUM_TRAJECTORY_STEPS));
}

bool BuoyancyTest::onTouchBegan(Touch* touch, Event* event)
{
	impulse_ = b2Vec2(5.0f, 5.0f);
	impulse_.x /= player_body_->GetMass();
	impulse_.y /= player_body_->GetMass();
	UpdateTrajectory();
	last_touch_ = touch->getLocation();
	return true;
}

void BuoyancyTest::onTouchMoved(Touch* touch, Event* event)
{
	Vec2 curr_touch = touch->getLocation();
	float angle = CC_DEGREES_TO_RADIANS( (last_touch_.x - curr_touch.x) / 5 );
	b2Vec2 modified_impulse(impulse_.x * cosf(angle) - impulse_.y * sinf(angle), impulse_.x * sinf(angle) + impulse_.y * cosf(angle));
	impulse_ = modified_impulse;
	UpdateTrajectory();
	last_touch_.set(curr_touch);
}

void BuoyancyTest::onTouchEnded(Touch* touch, Event* event)
{
	player_body_->ApplyLinearImpulse(impulse_, player_body_->GetWorldCenter(), true);
	impulse_ = b2Vec2_zero;
	last_touch_.set(Vec2::ZERO);
	trajectory_->clear();
}

void BuoyancyTest::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    Layer::draw(renderer, transform, flags);

    custom_command_.init(_globalZOrder);
    custom_command_.func = CC_CALLBACK_0(BuoyancyTest::onDraw, this, transform, flags);
    renderer->addCommand(&custom_command_);
}

void BuoyancyTest::onDraw(const Mat4 &transform, uint32_t flags)
{
    Director* director = Director::getInstance();
    CCASSERT(nullptr != director, "Director is null when seting 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 );
    world_->DrawDebugData();
    CHECK_GL_ERROR_DEBUG();

    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}

void BuoyancyTest::menuCloseCallback(Ref* pSender)
{
	Director::getInstance()->replaceScene(BuoyancyTest::createScene());
}
<file_sep>/*
 * GameGlobals.cpp
 *
 *  Created on: 08-Oct-2014
 *      Author: woigames
 */

#include "GameGlobals.h"

Size GameGlobals::screen_size_ = Size::ZERO;

void GameGlobals::Init()
{
	GameGlobals::screen_size_ = Director::getInstance()->getWinSize();
}

b2Body* GameGlobals::GetBodyForWorld(b2World* world, b2BodyType type, b2Shape::Type shape, EBodySize body_size)
{
	b2BodyDef body_def;
	body_def.type = type;
	b2Body* body = world->CreateBody(&body_def);

	b2PolygonShape box_shape = GameGlobals::GetRandomBoxShape(body_size);
	b2FixtureDef fixture_def;
	fixture_def.shape = &box_shape;
	fixture_def.density = 0.5f;
	body->CreateFixture(&fixture_def);
	return body;
}

b2PolygonShape GameGlobals::GetRandomBoxShape(EBodySize body_size)
{
	b2PolygonShape box_shape;
	float width = 0, height = 0;

	switch(body_size)
	{
	case E_BODY_SMALL:
		width = MIN_SHAPE_SIZE + CCRANDOM_0_1() * MAX_SHAPE_SIZE;
		width *= 1.25f;
		height = MIN_SHAPE_SIZE + CCRANDOM_0_1() * MAX_SHAPE_SIZE;
		break;
	case E_BODY_MEDIUM:
		width = 3.75f * MIN_SHAPE_SIZE + CCRANDOM_0_1() * MAX_SHAPE_SIZE;
		width *= 1.25f;
		height = 3.75f * MIN_SHAPE_SIZE + CCRANDOM_0_1() * MAX_SHAPE_SIZE;
		break;
	case E_BODY_LARGE:
		width = 7.5f * MIN_SHAPE_SIZE + CCRANDOM_0_1() * MAX_SHAPE_SIZE;
		width *= 1.25f;
		height = 7.5f * MIN_SHAPE_SIZE + CCRANDOM_0_1() * MAX_SHAPE_SIZE;
		break;
	}

	box_shape.SetAsBox(width, height);
	return box_shape;
}

b2CircleShape GameGlobals::GetRandomCircleShape()
{
	b2CircleShape circle_shape;
	circle_shape.m_radius = MIN_SHAPE_SIZE + CCRANDOM_0_1() * (MAX_SHAPE_SIZE - MIN_SHAPE_SIZE);
	return circle_shape;
}

void GameGlobals::GetTrajectory(b2Vec2 initial_position, b2Vec2 initial_velocity, b2Vec2 gravity, vector<Vec2> &points)
{
	points.clear();

	float t = 1 / 60.0f; 								// seconds per time step (at 60fps)
	b2Vec2 step_velocity = t * initial_velocity; 		// m/s
	b2Vec2 step_gravity = t * t * gravity; 				// m/s/s

	for(int i = 0; i < NUM_TRAJECTORY_STEPS; ++i)
	{
		b2Vec2 point_b2 = initial_position + i * step_velocity + 0.5f * (i*i+i) * step_gravity;
		Vec2 point(point_b2.x * PTM_RATIO, point_b2.y * PTM_RATIO);
		points.push_back(point);
	}
}

bool GameGlobals::Inside(b2Vec2 cp1, b2Vec2 cp2, b2Vec2 p)
{
	return (cp2.x-cp1.x)*(p.y-cp1.y) > (cp2.y-cp1.y)*(p.x-cp1.x);
}

b2Vec2 GameGlobals::Intersection(b2Vec2 cp1, b2Vec2 cp2, b2Vec2 s, b2Vec2 e)
{
	b2Vec2 dc(cp1.x - cp2.x, cp1.y - cp2.y);
	b2Vec2 dp(s.x - e.x, s.y - e.y);
	float n1 = cp1.x * cp2.y - cp1.y * cp2.x;
	float n2 = s.x * e.y - s.y * e.x;
	float n3 = 1.0 / (dc.x * dp.y - dc.y * dp.x);
	return b2Vec2((n1 * dp.x - n2 * dc.x) * n3, (n1 * dp.y - n2 * dc.y) * n3);
}

//http://rosettacode.org/wiki/Sutherland-Hodgman_polygon_clipping#JavaScript
//Note that this only works when fB is a convex polygon, but we know all
//fixtures in Box2D are convex, so that will not be a problem
bool GameGlobals::FindIntersectionOfFixtures(b2Fixture* fA, b2Fixture* fB, vector<b2Vec2>& output_vertices)
{
//currently this only handles polygon vs polygon
	if (fA->GetShape()->GetType() != b2Shape::e_polygon
			|| fB->GetShape()->GetType() != b2Shape::e_polygon)
		return false;

	b2PolygonShape* polyA = (b2PolygonShape*) fA->GetShape();
	b2PolygonShape* polyB = (b2PolygonShape*) fB->GetShape();

//fill subject polygon from fixtureA polygon
	for (int i = 0; i < polyA->GetVertexCount(); i++)
		output_vertices.push_back(fA->GetBody()->GetWorldPoint(polyA->GetVertex(i)));

//fill clip polygon from fixtureB polygon
	vector<b2Vec2> clip_polygon;
	for (int i = 0; i < polyB->GetVertexCount(); i++)
		clip_polygon.push_back(fB->GetBody()->GetWorldPoint(polyB->GetVertex(i)));

	b2Vec2 cp1 = clip_polygon[clip_polygon.size() - 1];
	for (int j = 0; j < clip_polygon.size(); j++)
	{
		b2Vec2 cp2 = clip_polygon[j];
		if (output_vertices.empty())
			return false;

		vector<b2Vec2> input_list = output_vertices;
		output_vertices.clear();
		b2Vec2 s = input_list[input_list.size() - 1]; //last on the input list
		for (int i = 0; i < input_list.size(); i++)
		{
			b2Vec2 e = input_list[i];
			if (GameGlobals::Inside(cp1, cp2, e))
			{
				if (!GameGlobals::Inside(cp1, cp2, s))
				{
					output_vertices.push_back(GameGlobals::Intersection(cp1, cp2, s, e));
				}
				output_vertices.push_back(e);
			}
			else if (GameGlobals::Inside(cp1, cp2, s))
			{
				output_vertices.push_back(GameGlobals::Intersection(cp1, cp2, s, e));
			}
			s = e;
		}
		cp1 = cp2;
	}

	return !output_vertices.empty();
}

//Taken from b2PolygonShape.cpp
b2Vec2 GameGlobals::ComputeCentroid(vector<b2Vec2> vs, float& area)
{
	int count = (int) vs.size();
	b2Assert(count >= 3);

	b2Vec2 c;
	c.Set(0.0f, 0.0f);
	area = 0.0f;

	// pRef is the reference point for forming triangles.
	// Its location doesnt change the result (except for rounding error).
	b2Vec2 pRef(0.0f, 0.0f);

	const float32 inv3 = 1.0f / 3.0f;

	for (int32 i = 0; i < count; ++i) {
		// Triangle vertices.
		b2Vec2 p1 = pRef;
		b2Vec2 p2 = vs[i];
		b2Vec2 p3 = i + 1 < count ? vs[i + 1] : vs[0];

		b2Vec2 e1 = p2 - p1;
		b2Vec2 e2 = p3 - p1;

		float32 D = b2Cross(e1, e2);

		float32 triangle_area = 0.5f * D;
		area += triangle_area;

		// Area weighted centroid
		c += triangle_area * inv3 * (p1 + p2 + p3);
	}

	// Centroid
	if (area > b2_epsilon)
		c *= 1.0f / area;
	else
		area = 0;
	return c;
}
<file_sep>BuoyancyTest
============
BuoyancyTest is a simple buoyancy simulation.
It has been built using Box2D in conjunction with cocos2d-x-3.2
