From 24561860f0d3ea47474f7094b7c2d6596c7c18c6 Mon Sep 17 00:00:00 2001 From: boyu0 Date: Tue, 22 Oct 2013 18:00:24 +0800 Subject: [PATCH] issue #2771: add ray cast test --- cocos/physics/CCPhysicsShape.h | 2 +- cocos/physics/CCPhysicsWorld.cpp | 58 ++-- cocos/physics/CCPhysicsWorld.h | 14 +- .../Classes/PhysicsTest/PhysicsTest.cpp | 291 +++++++++++++++++- .../TestCpp/Classes/PhysicsTest/PhysicsTest.h | 32 +- 5 files changed, 349 insertions(+), 48 deletions(-) diff --git a/cocos/physics/CCPhysicsShape.h b/cocos/physics/CCPhysicsShape.h index 4191c7e6b5..d1b5e0db04 100644 --- a/cocos/physics/CCPhysicsShape.h +++ b/cocos/physics/CCPhysicsShape.h @@ -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: diff --git a/cocos/physics/CCPhysicsWorld.cpp b/cocos/physics/CCPhysicsWorld.cpp index 6ca256bc67..cab183ba7a 100644 --- a/cocos/physics/CCPhysicsWorld.cpp +++ b/cocos/physics/CCPhysicsWorld.cpp @@ -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 diff --git a/cocos/physics/CCPhysicsWorld.h b/cocos/physics/CCPhysicsWorld.h index fa6a509dfc..e61cde2763 100644 --- a/cocos/physics/CCPhysicsWorld.h +++ b/cocos/physics/CCPhysicsWorld.h @@ -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 report; + std::function report; }; class PhysicsRectQueryCallback { public: - std::function report; + PhysicsRectQueryCallback() + : report(nullptr) + {} + virtual ~PhysicsRectQueryCallback(){} + +public: + std::function report; }; /** diff --git a/samples/Cpp/TestCpp/Classes/PhysicsTest/PhysicsTest.cpp b/samples/Cpp/TestCpp/Classes/PhysicsTest/PhysicsTest.cpp index 91c67da54d..0c67ddc204 100644 --- a/samples/Cpp/TestCpp/Classes/PhysicsTest/PhysicsTest.cpp +++ b/samples/Cpp/TestCpp/Classes/PhysicsTest/PhysicsTest.cpp @@ -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& 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"; } \ No newline at end of file diff --git a/samples/Cpp/TestCpp/Classes/PhysicsTest/PhysicsTest.h b/samples/Cpp/TestCpp/Classes/PhysicsTest/PhysicsTest.h index 34f8053b37..65c1c6bad9 100644 --- a/samples/Cpp/TestCpp/Classes/PhysicsTest/PhysicsTest.h +++ b/samples/Cpp/TestCpp/Classes/PhysicsTest/PhysicsTest.h @@ -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& 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