issue #2771: add ray cast test

This commit is contained in:
boyu0 2013-10-22 18:00:24 +08:00
parent e99b80b304
commit 24561860f0
5 changed files with 349 additions and 48 deletions

View File

@ -97,7 +97,7 @@ public:
virtual Point getOffset() { return Point::ZERO; }
virtual Point getCenter() { return getOffset(); }
static Point* recenterPoints(Point* points, int count, Point center);
static Point* recenterPoints(Point* points, int count, Point center = Point::ZERO);
static Point getPolyonCenter(Point* points, int count);
protected:

View File

@ -89,7 +89,7 @@ public:
static void rayCastCallbackFunc(cpShape *shape, cpFloat t, cpVect n, RayCastCallbackInfo *info);
static void rectQueryCallbackFunc(cpShape *shape, RectQueryCallbackInfo *info);
private:
public:
static bool continues;
};
@ -391,12 +391,16 @@ void PhysicsWorld::drawWithShape(DrawNode* node, PhysicsShape* shape)
Point centre = PhysicsHelper::cpv2point(cpBodyGetPos(cpShapeGetBody(shape)))
+ PhysicsHelper::cpv2point(cpCircleShapeGetOffset(shape));
Point seg[4] = {};
seg[0] = Point(centre.x - radius, centre.y - radius);
seg[1] = Point(centre.x - radius, centre.y + radius);
seg[2] = Point(centre.x + radius, centre.y + radius);
seg[3] = Point(centre.x + radius, centre.y - radius);
node->drawPolygon(seg, 4, Color4F(), 1, Color4F(1, 0, 0, 1));
static const int CIRCLE_SEG_NUM = 12;
Point seg[CIRCLE_SEG_NUM] = {};
for (int i = 0; i < CIRCLE_SEG_NUM; ++i)
{
float angle = (float)i * M_PI / (float)CIRCLE_SEG_NUM * 2.0f;
Point d(radius * cosf(angle), radius * sinf(angle));
seg[i] = centre + d;
}
node->drawPolygon(seg, CIRCLE_SEG_NUM, Color4F(1.0f, 0.0f, 0.0f, 0.3f), 1, Color4F(1, 0, 0, 1));
break;
}
case CP_SEGMENT_SHAPE:
@ -525,26 +529,36 @@ void PhysicsWorld::setGravity(Point gravity)
void PhysicsWorld::rayCast(PhysicsRayCastCallback& callback, Point point1, Point point2, void* data)
{
RayCastCallbackInfo info = {this, &callback, point1, point2, data};
cpSpaceSegmentQuery(this->_info->space,
PhysicsHelper::point2cpv(point1),
PhysicsHelper::point2cpv(point2),
CP_ALL_LAYERS,
CP_NO_GROUP,
(cpSpaceSegmentQueryFunc)PhysicsWorldCallback::rayCastCallbackFunc,
&info);
if (callback.report != nullptr)
{
RayCastCallbackInfo info = {this, &callback, point1, point2, data};
PhysicsWorldCallback::continues = true;
cpSpaceSegmentQuery(this->_info->space,
PhysicsHelper::point2cpv(point1),
PhysicsHelper::point2cpv(point2),
CP_ALL_LAYERS,
CP_NO_GROUP,
(cpSpaceSegmentQueryFunc)PhysicsWorldCallback::rayCastCallbackFunc,
&info);
}
}
void PhysicsWorld::rectQuery(PhysicsRectQueryCallback& callback, Rect rect, void* data)
{
RectQueryCallbackInfo info = {this, &callback, data};
cpSpaceBBQuery(this->_info->space,
PhysicsHelper::rect2cpbb(rect),
CP_ALL_LAYERS,
CP_NO_GROUP,
(cpSpaceBBQueryFunc)PhysicsWorldCallback::rectQueryCallbackFunc,
&info);
if (callback.report != nullptr)
{
RectQueryCallbackInfo info = {this, &callback, data};
PhysicsWorldCallback::continues = true;
cpSpaceBBQuery(this->_info->space,
PhysicsHelper::rect2cpbb(rect),
CP_ALL_LAYERS,
CP_NO_GROUP,
(cpSpaceBBQueryFunc)PhysicsWorldCallback::rectQueryCallbackFunc,
&info);
}
}
Array* PhysicsWorld::getAllBody() const

View File

@ -53,6 +53,10 @@ class PhysicsWorld;
class PhysicsRayCastCallback
{
public:
PhysicsRayCastCallback()
: report(nullptr)
{}
virtual ~PhysicsRayCastCallback(){}
/**
* @brief Called for each fixture found in the query. You control how the ray cast
* proceeds by returning a float:
@ -63,13 +67,19 @@ public:
* @param normal the normal vector at the point of intersection
* @return true to continue, false to terminate
*/
std::function<bool(PhysicsWorld&, PhysicsShape&, Point point, Point normal, float fraction, void* data)> report;
std::function<bool(PhysicsWorld&, PhysicsShape&, Point, Point, float, void*)> report;
};
class PhysicsRectQueryCallback
{
public:
std::function<bool(PhysicsWorld&, PhysicsShape&, void* data)> report;
PhysicsRectQueryCallback()
: report(nullptr)
{}
virtual ~PhysicsRectQueryCallback(){}
public:
std::function<bool(PhysicsWorld&, PhysicsShape&, void*)> report;
};
/**

View File

@ -9,6 +9,7 @@ namespace
CL(PhysicsDemoPyramidStack),
CL(PhysicsDemoPlink),
CL(PhysicsDemoClickAdd),
CL(PhysicsDemoRayCast),
};
static int sceneIdx=-1;
@ -48,6 +49,8 @@ namespace
return layer;
}
static const Color4F STATIC_COLOR = {1.0f, 0.0f, 0.0f, 1.0f};
}
@ -87,6 +90,8 @@ void PhysicsTestScene::toggleDebug()
PhysicsDemo::PhysicsDemo()
: _scene(nullptr)
, _ball(nullptr)
, _spriteTexture(nullptr)
{
}
@ -304,22 +309,62 @@ namespace
}
}
Node* PhysicsDemoLogoSmash::makeBall(float x, float y)
Sprite* PhysicsDemo::makeBall(float x, float y, float radius, PhysicsMaterial material)
{
auto ball = Sprite::createWithTexture(_ball->getTexture());
ball->setScale(0.1);
Sprite* ball = nullptr;
if (_ball != nullptr)
{
ball = Sprite::createWithTexture(_ball->getTexture());
}else
{
ball = Sprite::create("Images/ball.png");
}
auto body = PhysicsBody::createCircle(0.95, PhysicsMaterial(1, 0, 0));
body->setMass(1.0);
body->setMoment(PHYSICS_INFINITY);
ball->setScale(0.13f * radius);
auto body = PhysicsBody::createCircle(radius, material);
ball->setPhysicsBody(body);
ball->setPosition(Point(x, y));
return ball;
}
Sprite* PhysicsDemo::makeBox(float x, float y, Size size, PhysicsMaterial material)
{
auto box = CCRANDOM_0_1() > 0.5f ? Sprite::create("Images/YellowSquare.png") : Sprite::create("Images/CyanSquare.png");
box->setScaleX(size.width/100.0f);
box->setScaleY(size.height/100.0f);
auto body = PhysicsBody::createBox(size);
box->setPhysicsBody(body);
box->setPosition(Point(x, y));
return box;
}
Sprite* PhysicsDemo::makeTriangle(float x, float y, Size size, PhysicsMaterial material)
{
auto triangle = CCRANDOM_0_1() > 0.5f ? Sprite::create("Images/YellowTriangle.png") : Sprite::create("Images/CyanTriangle.png");
if(size.height == 0)
{
triangle->setScale(size.width/100.0f);
}else
{
triangle->setScaleX(size.width/50.0f);
triangle->setScaleY(size.height/43.5f);
}
Point vers[] = { Point(0, size.height/2), Point(size.width/2, -size.height/2), Point(-size.width/2, -size.height/2)};
auto body = PhysicsBody::createPolygon(vers, 3);
triangle->setPhysicsBody(body);
triangle->setPosition(Point(x, y));
return triangle;
}
void PhysicsDemoLogoSmash::onEnter()
{
PhysicsDemo::onEnter();
@ -337,19 +382,22 @@ void PhysicsDemoLogoSmash::onEnter()
float x_jitter = 0.05*frand();
float y_jitter = 0.05*frand();
_ball->addChild(makeBall(2*(x - logo_width/2 + x_jitter) + VisibleRect::getVisibleRect().size.width/2,
2*(logo_height-y + y_jitter) + VisibleRect::getVisibleRect().size.height/2 - logo_height/2));
Node* ball = makeBall(2*(x - logo_width/2 + x_jitter) + VisibleRect::getVisibleRect().size.width/2,
2*(logo_height-y + y_jitter) + VisibleRect::getVisibleRect().size.height/2 - logo_height/2,
0.95f, PhysicsMaterial(1.0f, 0.0f, 0.0f));
ball->getPhysicsBody()->setMass(1.0);
ball->getPhysicsBody()->setMoment(PHYSICS_INFINITY);
_ball->addChild(ball);
}
}
}
auto bullet = Sprite::createWithTexture(_ball->getTexture());
bullet->setScale(0.5);
auto body = PhysicsBody::createCircle(8, PhysicsMaterial(PHYSICS_INFINITY, 0, 0));
body->setVelocity(Point(400, 0));
bullet->setPhysicsBody(body);
auto bullet = makeBall(400, 0, 10, PhysicsMaterial(PHYSICS_INFINITY, 0, 0));
bullet->getPhysicsBody()->setVelocity(Point(400, 0));
bullet->setPosition(Point(-1000, VisibleRect::getVisibleRect().size.height/2));
@ -393,7 +441,7 @@ void PhysicsDemoPlink::onEnter()
{
PhysicsDemo::onEnter();
auto node = Node::create();
auto node = DrawNode::create();
auto body = PhysicsBody::create();
body->setDynamic(false);
node->setPhysicsBody(body);
@ -405,7 +453,11 @@ void PhysicsDemoPlink::onEnter()
{
for (int j = 0; j < 4; ++j)
{
body->addShape(PhysicsShapePolygon::create(tris, 3, PHYSICSSHAPE_MATERIAL_DEFAULT, Point(rect.origin.x + rect.size.width/9*i + (j%2)*40 - 20, rect.origin.y + j*70)));
Point offset(rect.origin.x + rect.size.width/9*i + (j%2)*40 - 20, rect.origin.y + j*70);
body->addShape(PhysicsShapePolygon::create(tris, 3, PHYSICSSHAPE_MATERIAL_DEFAULT, offset));
Point drawVec[] = {tris[0] + offset, tris[1] + offset, tris[2] + offset};
node->drawPolygon(drawVec, 3, STATIC_COLOR, 1, STATIC_COLOR);
}
}
@ -416,4 +468,209 @@ void PhysicsDemoPlink::onEnter()
std::string PhysicsDemoPlink::title()
{
return "Plink";
}
PhysicsDemoRayCast::PhysicsDemoRayCast()
: _angle(0.0f)
, _node(nullptr)
, _mode(0)
{}
void PhysicsDemoRayCast::onEnter()
{
PhysicsDemo::onEnter();
setTouchEnabled(true);
_scene->getPhysicsWorld()->setGravity(Point::ZERO);
auto node = DrawNode::create();
node->setPhysicsBody(PhysicsBody::createEdgeSegment(VisibleRect::leftBottom() + Point(0, 50), VisibleRect::rightBottom() + Point(0, 50)));
node->drawSegment(VisibleRect::leftBottom() + Point(0, 50), VisibleRect::rightBottom() + Point(0, 50), 1, STATIC_COLOR);
this->addChild(node);
MenuItemFont::setFontSize(18);
auto item = MenuItemFont::create("Change Mode(any)", CC_CALLBACK_1(PhysicsDemoRayCast::changeModeCallback, this));
auto menu = Menu::create(item, NULL);
this->addChild(menu);
menu->setPosition(Point(VisibleRect::left().x+100, VisibleRect::top().y-10));
scheduleUpdate();
}
void PhysicsDemoRayCast::changeModeCallback(Object* sender)
{
_mode = (_mode + 1) % 3;
switch (_mode)
{
case 0:
((MenuItemFont*)sender)->setString("Change Mode(any)");
break;
case 1:
((MenuItemFont*)sender)->setString("Change Mode(nearest)");
break;
case 2:
((MenuItemFont*)sender)->setString("Change Mode(multiple)");
break;
default:
break;
}
}
bool PhysicsDemoRayCast::anyRay(PhysicsWorld& world, PhysicsShape& shape, Point point, Point normal, float fraction, void* data)
{
*((Point*)data) = point;
return false;
}
class PhysicsDemoNearestRayCastCallback : public PhysicsRayCastCallback
{
public:
PhysicsDemoNearestRayCastCallback();
private:
float _friction;
};
PhysicsDemoNearestRayCastCallback::PhysicsDemoNearestRayCastCallback()
: _friction(1.0f)
{
report = [this](PhysicsWorld& world, PhysicsShape& shape, Point point, Point normal, float fraction, void* data)->bool
{
if (_friction > fraction)
{
*((Point*)data) = point;
_friction = fraction;
}
return true;
};
}
namespace
{
static const int MAX_MULTI_RAYCAST_NUM = 5;
}
class PhysicsDemoMultiRayCastCallback : public PhysicsRayCastCallback
{
public:
PhysicsDemoMultiRayCastCallback();
public:
Point points[MAX_MULTI_RAYCAST_NUM];
int num;
};
PhysicsDemoMultiRayCastCallback::PhysicsDemoMultiRayCastCallback()
: num(0)
{
report = [this](PhysicsWorld& world, PhysicsShape& shape, Point point, Point normal, float fraction, void* data)->bool
{
if (num < MAX_MULTI_RAYCAST_NUM)
{
points[num++] = point;
}
return true;
};
}
void PhysicsDemoRayCast::update(float delta)
{
float L = 150.0f;
Point point1 = VisibleRect::center();
Point d(L * cosf(_angle), L * sinf(_angle));
Point point2 = point1 + d;
removeChild(_node);
_node = DrawNode::create();
switch (_mode)
{
case 0:
{
PhysicsRayCastCallback callback;
Point point3 = point2;
callback.report = anyRay;
_scene->getPhysicsWorld()->rayCast(callback, point1, point2, &point3);
_node->drawSegment(point1, point3, 1, STATIC_COLOR);
if (point2 != point3)
{
_node->drawDot(point3, 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f));
}
addChild(_node);
break;
}
case 1:
{
PhysicsDemoNearestRayCastCallback callback;
Point point3 = point2;
_scene->getPhysicsWorld()->rayCast(callback, point1, point2, &point3);
_node->drawSegment(point1, point3, 1, STATIC_COLOR);
if (point2 != point3)
{
_node->drawDot(point3, 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f));
}
addChild(_node);
break;
}
case 2:
{
PhysicsDemoMultiRayCastCallback callback;
_scene->getPhysicsWorld()->rayCast(callback, point1, point2, nullptr);
_node->drawSegment(point1, point2, 1, STATIC_COLOR);
for (int i = 0; i < callback.num; ++i)
{
_node->drawDot(callback.points[i], 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f));
}
addChild(_node);
break;
}
default:
break;
}
_angle += 0.25f * M_PI / 180.0f;
}
void PhysicsDemoRayCast::onTouchesEnded(const std::vector<Touch*>& touches, Event* event)
{
//Add a new body/atlas sprite at the touched location
for( auto &touch: touches)
{
auto location = touch->getLocation();
float r = CCRANDOM_0_1();
if (r < 1.0f/3.0f)
{
addChild(makeBall(location.x, location.y, 5 + CCRANDOM_0_1()*10));
}else if(r < 2.0f/3.0f)
{
addChild(makeBox(location.x, location.y, Size(10 + CCRANDOM_0_1()*15, 10 + CCRANDOM_0_1()*15)));
}else
{
addChild(makeTriangle(location.x, location.y, Size(10 + CCRANDOM_0_1()*20, 10 + CCRANDOM_0_1()*20)));
}
}
}
std::string PhysicsDemoRayCast::title()
{
return "Ray Cast";
}

View File

@ -37,9 +37,13 @@ public:
void toggleDebugCallback(Object* sender);
void addGrossiniAtPosition(Point p, float scale = 1.0);
Sprite* makeBall(float x, float y, float radius, PhysicsMaterial material = {1.0f, 1.0f, 1.0f});
Sprite* makeBox(float x, float y, Size size, PhysicsMaterial material = {1.0f, 1.0f, 1.0f});
Sprite* makeTriangle(float x, float y, Size size, PhysicsMaterial material = {1.0f, 1.0f, 1.0f});
private:
protected:
Texture2D* _spriteTexture; // weak ref
SpriteBatchNode* _ball;
};
class PhysicsDemoClickAdd : public PhysicsDemo
@ -57,11 +61,6 @@ class PhysicsDemoLogoSmash : public PhysicsDemo
public:
void onEnter() override;
std::string title() override;
Node* makeBall(float x, float y);
private:
SpriteBatchNode* _ball;
};
class PhysicsDemoPyramidStack : public PhysicsDemo
@ -78,4 +77,25 @@ public:
std::string title() override;
};
class PhysicsDemoRayCast : public PhysicsDemo
{
public:
PhysicsDemoRayCast();
public:
void onEnter() override;
std::string title() override;
void update(float delta) override;
void onTouchesEnded(const std::vector<Touch*>& touches, Event* event) override;
void changeModeCallback(Object* sender);
static bool anyRay(PhysicsWorld& world, PhysicsShape& shape, Point point, Point normal, float fraction, void* data);
private:
float _angle;
DrawNode* _node;
int _mode;
};
#endif