diff --git a/projects/kiwano-physics/kiwano-physics.vcxproj b/projects/kiwano-physics/kiwano-physics.vcxproj index 7814b99d..dac0da27 100644 --- a/projects/kiwano-physics/kiwano-physics.vcxproj +++ b/projects/kiwano-physics/kiwano-physics.vcxproj @@ -12,6 +12,8 @@ + + @@ -21,6 +23,8 @@ + + diff --git a/projects/kiwano-physics/kiwano-physics.vcxproj.filters b/projects/kiwano-physics/kiwano-physics.vcxproj.filters index 72b37123..2a808fb3 100644 --- a/projects/kiwano-physics/kiwano-physics.vcxproj.filters +++ b/projects/kiwano-physics/kiwano-physics.vcxproj.filters @@ -8,6 +8,8 @@ + + @@ -15,5 +17,7 @@ + + \ No newline at end of file diff --git a/src/kiwano-network/HttpClient.cpp b/src/kiwano-network/HttpClient.cpp index 169d9825..4822be25 100644 --- a/src/kiwano-network/HttpClient.cpp +++ b/src/kiwano-network/HttpClient.cpp @@ -36,7 +36,7 @@ namespace uint32_t write_data(void* buffer, uint32_t size, uint32_t nmemb, void* userp) { - kiwano::string* recv_buffer = (kiwano::string*)userp; + core::string* recv_buffer = (core::string*)userp; uint32_t total = size * nmemb; // add data to the end of recv_buffer @@ -46,10 +46,10 @@ namespace return total; } - kiwano::string convert_to_utf8(kiwano::wstring const& str) + core::string convert_to_utf8(core::wstring const& str) { std::wstring_convert> utf8_conv; - kiwano::string result; + core::string result; try { @@ -63,10 +63,10 @@ namespace return result; } - kiwano::wstring convert_from_utf8(kiwano::string const& str) + core::wstring convert_from_utf8(core::string const& str) { kiwano::string_convert> utf8_conv; - kiwano::wstring result; + core::wstring result; try { @@ -104,7 +104,7 @@ namespace } } - bool Init(HttpClient* client, Vector const& headers, kiwano::string const& url, kiwano::string* response_data, kiwano::string* response_header, char* error_buffer) + bool Init(HttpClient* client, Vector const& headers, core::string const& url, core::string* response_data, core::string* response_header, char* error_buffer) { if (!SetOption(CURLOPT_ERRORBUFFER, error_buffer)) return false; @@ -170,11 +170,11 @@ namespace public: static inline bool GetRequest( HttpClient* client, - Vector const& headers, - kiwano::string const& url, + Vector const& headers, + core::string const& url, long* response_code, - kiwano::string* response_data, - kiwano::string* response_header, + core::string* response_data, + core::string* response_header, char* error_buffer) { Curl curl; @@ -185,12 +185,12 @@ namespace static inline bool PostRequest( HttpClient* client, - Vector const& headers, - kiwano::string const& url, - kiwano::string const& request_data, + Vector const& headers, + core::string const& url, + core::string const& request_data, long* response_code, - kiwano::string* response_data, - kiwano::string* response_header, + core::string* response_data, + core::string* response_header, char* error_buffer) { Curl curl; @@ -203,12 +203,12 @@ namespace static inline bool PutRequest( HttpClient* client, - Vector const& headers, - kiwano::string const& url, - kiwano::string const& request_data, + Vector const& headers, + core::string const& url, + core::string const& request_data, long* response_code, - kiwano::string* response_data, - kiwano::string* response_header, + core::string* response_data, + core::string* response_header, char* error_buffer) { Curl curl; @@ -221,11 +221,11 @@ namespace static inline bool DeleteRequest( HttpClient* client, - Vector const& headers, - kiwano::string const& url, + Vector const& headers, + core::string const& url, long* response_code, - kiwano::string* response_data, - kiwano::string* response_header, + core::string* response_data, + core::string* response_header, char* error_buffer) { Curl curl; @@ -307,13 +307,13 @@ namespace kiwano bool ok = false; long response_code = 0; char error_message[256] = { 0 }; - kiwano::string response_header; - kiwano::string response_data; + core::string response_header; + core::string response_data; - kiwano::string url = convert_to_utf8(request->GetUrl()); - kiwano::string data = convert_to_utf8(request->GetData()); + core::string url = convert_to_utf8(request->GetUrl()); + core::string data = convert_to_utf8(request->GetData()); - Vector headers; + Vector headers; headers.reserve(request->GetHeaders().size()); for (const auto& pair : request->GetHeaders()) { diff --git a/src/kiwano-physics/Body.cpp b/src/kiwano-physics/Body.cpp index 44dd0783..2d4f4f89 100644 --- a/src/kiwano-physics/Body.cpp +++ b/src/kiwano-physics/Body.cpp @@ -30,6 +30,9 @@ namespace kiwano : body_(nullptr) , actor_(nullptr) , world_(nullptr) + , category_bits_(0x0001) + , mask_bits_(0xFFFF) + , group_index_(0) { } @@ -111,6 +114,57 @@ namespace kiwano } } + void PhysicBody::SetCategoryBits(uint16_t category_bits) + { + KGE_ASSERT(body_); + + if (category_bits != category_bits_) + { + category_bits_ = category_bits; + + b2Fixture* fixture = body_->GetFixtureList(); + while (fixture) + { + UpdateFixtureFilter(fixture); + fixture = fixture->GetNext(); + } + } + } + + void PhysicBody::SetMaskBits(uint16_t mask_bits) + { + KGE_ASSERT(body_); + + if (mask_bits != mask_bits_) + { + mask_bits_ = mask_bits; + + b2Fixture* fixture = body_->GetFixtureList(); + while (fixture) + { + UpdateFixtureFilter(fixture); + fixture = fixture->GetNext(); + } + } + } + + void PhysicBody::SetGroupIndex(int16_t index) + { + KGE_ASSERT(body_); + + if (index != group_index_) + { + group_index_ = index; + + b2Fixture* fixture = body_->GetFixtureList(); + while (fixture) + { + UpdateFixtureFilter(fixture); + fixture = fixture->GetNext(); + } + } + } + void PhysicBody::GetMassData(float* mass, Point* center, float* inertia) { KGE_ASSERT(body_ && world_); @@ -252,5 +306,14 @@ namespace kiwano } } + void PhysicBody::UpdateFixtureFilter(b2Fixture* fixture) + { + b2Filter filter; + filter.categoryBits = category_bits_; + filter.maskBits = mask_bits_; + filter.groupIndex = group_index_; + fixture->SetFilterData(filter); + } + } } diff --git a/src/kiwano-physics/Body.h b/src/kiwano-physics/Body.h index 079b900c..2b64e8b1 100644 --- a/src/kiwano-physics/Body.h +++ b/src/kiwano-physics/Body.h @@ -22,6 +22,7 @@ #include #include #include +#include namespace kiwano { @@ -60,11 +61,26 @@ namespace kiwano PhysicFixture AddChainShape(Vector const& vertexs, bool loop, float density = 0.f); // 获取夹具 - PhysicFixture GetFixture() const { KGE_ASSERT(body_); PhysicFixture(body_->GetFixtureList()); } + PhysicFixture GetFixtureList() const { KGE_ASSERT(body_); PhysicFixture(body_->GetFixtureList()); } // 移除夹具 void RemoveFixture(PhysicFixture const& fixture); + // 获取接触边 + PhysicContactEdge GetContactList() const { KGE_ASSERT(body_); PhysicContactEdge(body_->GetContactList()); } + + // 类别码 + uint16_t GetCategoryBits() const { return category_bits_; } + void SetCategoryBits(uint16_t category_bits); + + // 碰撞掩码 + uint16_t GetMaskBits() const { return mask_bits_; } + void SetMaskBits(uint16_t mask_bits); + + // 组索引 + int16_t GetGroupIndex() const { return group_index_; } + void SetGroupIndex(int16_t index); + // 旋转角度 float GetBodyRotation() const { KGE_ASSERT(body_); return math::Radian2Degree(body_->GetAngle()); } void SetBodyRotation(float angle) { SetBodyTransform(GetBodyPosition(), angle); } @@ -143,10 +159,17 @@ namespace kiwano void UpdateActor(); void UpdateFromActor(); + protected: + void UpdateFixtureFilter(b2Fixture* fixture); + protected: Actor* actor_; PhysicWorld* world_; b2Body* body_; + + uint16_t category_bits_; + uint16_t mask_bits_; + int16_t group_index_; }; } } diff --git a/src/kiwano-physics/Contact.cpp b/src/kiwano-physics/Contact.cpp new file mode 100644 index 00000000..8b5bcf41 --- /dev/null +++ b/src/kiwano-physics/Contact.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2018-2019 Kiwano - Nomango +// +// 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 +#include +#include + +namespace kiwano +{ + namespace physics + { + + PhysicContact::PhysicContact() + : contact_(nullptr) + { + } + + PhysicContact::PhysicContact(b2Contact* contact) + : PhysicContact() + { + SetB2Contact(contact); + } + + PhysicContact PhysicContact::GetNext() + { + KGE_ASSERT(contact_); + return PhysicContact(contact_->GetNext()); + } + + const PhysicContact PhysicContact::GetNext() const + { + KGE_ASSERT(contact_); + return PhysicContact(contact_->GetNext()); + } + + PhysicFixture PhysicContact::GetFixtureA() + { + KGE_ASSERT(contact_); + return PhysicFixture(contact_->GetFixtureA()); + } + + const PhysicFixture PhysicContact::GetFixtureA() const + { + KGE_ASSERT(contact_); + return PhysicFixture(contact_->GetFixtureA()); + } + + PhysicFixture PhysicContact::GetFixtureB() + { + KGE_ASSERT(contact_); + return PhysicFixture(contact_->GetFixtureB()); + } + + const PhysicFixture PhysicContact::GetFixtureB() const + { + KGE_ASSERT(contact_); + return PhysicFixture(contact_->GetFixtureB()); + } + + void PhysicContact::SetTangentSpeed(float speed) + { + KGE_ASSERT(contact_); + + PhysicBody* body = GetFixtureA().GetBody(); + KGE_ASSERT(body); + + PhysicWorld* world = body->GetWorld(); + KGE_ASSERT(world); + + contact_->SetTangentSpeed(world->Stage2World(speed)); + } + + float PhysicContact::GetTangentSpeed() const + { + KGE_ASSERT(contact_); + + const PhysicBody* body = GetFixtureA().GetBody(); + KGE_ASSERT(body); + + const PhysicWorld* world = body->GetWorld(); + KGE_ASSERT(world); + + return world->World2Stage(contact_->GetTangentSpeed()); + } + + + PhysicContactEdge::PhysicContactEdge() + : edge_(nullptr) + { + } + + PhysicContactEdge::PhysicContactEdge(b2ContactEdge* edge) + : PhysicContactEdge() + { + SetB2ContactEdge(edge); + } + + } +} diff --git a/src/kiwano-physics/Contact.h b/src/kiwano-physics/Contact.h new file mode 100644 index 00000000..b32b8db4 --- /dev/null +++ b/src/kiwano-physics/Contact.h @@ -0,0 +1,112 @@ +// Copyright (c) 2018-2019 Kiwano - Nomango +// +// 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. + +#pragma once +#include +#include + +namespace kiwano +{ + namespace physics + { + class PhysicBody; + + // 接触 + class KGE_API PhysicContact + { + public: + PhysicContact(); + PhysicContact(b2Contact* contact); + + // 是否是接触 + bool IsTouching() const { KGE_ASSERT(contact_); return contact_->IsTouching(); } + + // 启用或禁用 (仅作用于一个时间步) + void SetEnabled(bool flag) { KGE_ASSERT(contact_); contact_->SetEnabled(flag); } + bool IsEnabled() const { KGE_ASSERT(contact_); return contact_->IsEnabled(); } + + // 获取下一接触 + PhysicContact GetNext(); + const PhysicContact GetNext() const; + + // 夹具 A + PhysicFixture GetFixtureA(); + const PhysicFixture GetFixtureA() const; + + // 夹具 B + PhysicFixture GetFixtureB(); + const PhysicFixture GetFixtureB() const; + + // 摩擦 + void SetFriction(float friction) { KGE_ASSERT(contact_); contact_->SetFriction(friction); } + float GetFriction() const { KGE_ASSERT(contact_); return contact_->GetFriction(); } + void ResetFriction() { KGE_ASSERT(contact_); contact_->ResetFriction(); } + + // 弹性恢复 + void SetRestitution(float restitution) { KGE_ASSERT(contact_); contact_->SetRestitution(restitution); } + float GetRestitution() const { KGE_ASSERT(contact_); return contact_->GetRestitution(); } + void ResetRestitution() { KGE_ASSERT(contact_); contact_->ResetRestitution(); } + + // 切线速度 + void SetTangentSpeed(float speed); + float GetTangentSpeed() const; + + b2Contact* GetB2Contact() { return contact_; } + const b2Contact* GetB2Contact() const { return contact_; } + void SetB2Contact(b2Contact* contact) { contact_ = contact; } + + protected: + b2Contact* contact_; + }; + + + // 接触边 + class KGE_API PhysicContactEdge + { + public: + PhysicContactEdge(); + PhysicContactEdge(b2ContactEdge* edge); + + // 获取接触物体 + PhysicBody* GetOtherBody() { KGE_ASSERT(edge_); return static_cast(edge_->other->GetUserData()); } + const PhysicBody* GetOtherBody() const { KGE_ASSERT(edge_); return static_cast(edge_->other->GetUserData()); } + + // 获取接触 + PhysicContact GetContact() { KGE_ASSERT(edge_); return PhysicContact(edge_->contact); } + const PhysicContact GetContact() const { KGE_ASSERT(edge_); return PhysicContact(edge_->contact); } + + // 获取上一接触边 + PhysicContactEdge GetPrev() { KGE_ASSERT(edge_); return PhysicContactEdge(edge_->prev); } + const PhysicContactEdge GetPrev() const { KGE_ASSERT(edge_); return PhysicContactEdge(edge_->prev); } + + // 获取下一接触边 + PhysicContactEdge GetNext() { KGE_ASSERT(edge_); return PhysicContactEdge(edge_->next); } + const PhysicContactEdge GetNext() const { KGE_ASSERT(edge_); return PhysicContactEdge(edge_->next); } + + b2ContactEdge* GetB2ContactEdge() { return edge_; } + const b2ContactEdge* GetB2ContactEdge() const { return edge_; } + void SetB2ContactEdge(b2ContactEdge* edge) { edge_ = edge; } + + protected: + b2ContactEdge* edge_; + }; + + } +} diff --git a/src/kiwano-physics/ContactListener.cpp b/src/kiwano-physics/ContactListener.cpp new file mode 100644 index 00000000..cd8974fe --- /dev/null +++ b/src/kiwano-physics/ContactListener.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2018-2019 Kiwano - Nomango +// +// 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 +#include +#include + +namespace kiwano +{ + namespace physics + { + PhysicContactListener::PhysicContactListener() + : running_(true) + { + } + + PhysicContactListener::~PhysicContactListener() + { + } + + + PhysicContactCallbackListener::PhysicContactCallbackListener() + { + } + + PhysicContactCallbackListener::~PhysicContactCallbackListener() + { + } + + + PhysicContactListener* PhysicContactDispatcher::AddContactListener(PhysicContactListenerPtr listener) + { + return AddContactListener(listener.get()); + } + + PhysicContactListener* PhysicContactDispatcher::AddContactListener(PhysicContactListener* listener) + { + KGE_ASSERT(listener && "AddListener failed, NULL pointer exception"); + + if (listener) + { + listeners_.push_back(listener); + } + return listener; + } + + void PhysicContactDispatcher::StartContactListeners(String const& listener_name) + { + for (auto listener = listeners_.first_item(); listener; listener = listener->next_item()) + { + if (listener->IsName(listener_name)) + { + listener->Start(); + } + } + } + + void PhysicContactDispatcher::StopContactListeners(String const& listener_name) + { + for (auto listener = listeners_.first_item(); listener; listener = listener->next_item()) + { + if (listener->IsName(listener_name)) + { + listener->Stop(); + } + } + } + + void PhysicContactDispatcher::RemoveContactListeners(String const& listener_name) + { + PhysicContactListenerPtr next; + for (auto listener = listeners_.first_item(); listener; listener = next) + { + next = listener->next_item(); + + if (listener->IsName(listener_name)) + { + listeners_.remove(listener); + } + } + } + + void PhysicContactDispatcher::OnContactBegin(b2Contact* b2contact) + { + if (listeners_.empty()) + return; + + PhysicContact contact(b2contact); + PhysicContactListenerPtr next; + for (auto listener = listeners_.first_item(); listener; listener = next) + { + next = listener->next_item(); + + if (listener->IsRunning()) + { + listener->OnContactBegin(contact); + } + } + } + + void PhysicContactDispatcher::OnContactEnd(b2Contact* b2contact) + { + if (listeners_.empty()) + return; + + PhysicContact contact(b2contact); + PhysicContactListenerPtr next; + for (auto listener = listeners_.first_item(); listener; listener = next) + { + next = listener->next_item(); + + if (listener->IsRunning()) + { + listener->OnContactEnd(contact); + } + } + } + + } +} diff --git a/src/kiwano-physics/ContactListener.h b/src/kiwano-physics/ContactListener.h new file mode 100644 index 00000000..4b67398d --- /dev/null +++ b/src/kiwano-physics/ContactListener.h @@ -0,0 +1,132 @@ +// Copyright (c) 2018-2019 Kiwano - Nomango +// +// 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. + +#pragma once +#include + +namespace kiwano +{ + namespace physics + { + class PhysicContactDispatcher; + + KGE_DECLARE_SMART_PTR(PhysicContactListener); + + // 接触监听器 + class KGE_API PhysicContactListener + : public ObjectBase + , protected IntrusiveListItem + { + friend IntrusiveList; + friend class PhysicContactDispatcher; + + public: + PhysicContactListener(); + virtual ~PhysicContactListener(); + + // 接触开始 + virtual void OnContactBegin(PhysicContact contact) { KGE_NOT_USED(contact); } + + // 接触结束 + virtual void OnContactEnd(PhysicContact contact) { KGE_NOT_USED(contact); } + + inline void Start() { running_ = true; } + inline void Stop() { running_ = true; } + inline bool IsRunning() const { return running_; } + + protected: + bool running_; + }; + + + KGE_DECLARE_SMART_PTR(PhysicContactCallbackListener); + + // 接触回调监听器 + class KGE_API PhysicContactCallbackListener + : public PhysicContactListener + { + friend IntrusiveList; + friend class PhysicContactDispatcher; + + public: + using ContactBeginCallback = Function; + using ContactEndCallback = Function; + + PhysicContactCallbackListener(); + virtual ~PhysicContactCallbackListener(); + + // 接触开始回调 + void SetCallbackOnContactBegin(ContactBeginCallback const& cb) { begin_ = cb; } + ContactBeginCallback GetCallbackOnContactBegin() const { return begin_; } + + // 接触结束回调 + void SetCallbackOnContactEnd(ContactEndCallback const& cb) { end_ = cb; } + ContactEndCallback GetCallbackOnContactEnd() const { return end_; } + + void OnContactBegin(PhysicContact contact) override { if (begin_) begin_(contact); } + void OnContactEnd(PhysicContact contact) override { if (end_) end_(contact); } + + protected: + ContactBeginCallback begin_; + ContactEndCallback end_; + }; + + + // 接触分发器 + class KGE_API PhysicContactDispatcher + { + public: + using Listeners = IntrusiveList; + + // 添加监听器 + PhysicContactListener* AddContactListener( + PhysicContactListenerPtr listener + ); + + // 添加监听器 + PhysicContactListener* AddContactListener( + PhysicContactListener* listener + ); + + // 启动监听器 + void StartContactListeners( + String const& listener_name + ); + + // 停止监听器 + void StopContactListeners( + String const& listener_name + ); + + // 移除监听器 + void RemoveContactListeners( + String const& listener_name + ); + + protected: + void OnContactBegin(b2Contact* contact); + + void OnContactEnd(b2Contact* contact); + + private: + Listeners listeners_; + }; + } +} diff --git a/src/kiwano-physics/Fixture.cpp b/src/kiwano-physics/Fixture.cpp index c238b5ff..864d6caa 100644 --- a/src/kiwano-physics/Fixture.cpp +++ b/src/kiwano-physics/Fixture.cpp @@ -58,6 +58,18 @@ namespace kiwano return PhysicFixture(); } + PhysicBody* PhysicFixture::GetBody() + { + KGE_ASSERT(fixture_); + return static_cast(fixture_->GetBody()->GetUserData()); + } + + const PhysicBody* PhysicFixture::GetBody() const + { + KGE_ASSERT(fixture_); + return static_cast(fixture_->GetBody()->GetUserData()); + } + PhysicShape PhysicFixture::GetShape() const { KGE_ASSERT(fixture_); diff --git a/src/kiwano-physics/Fixture.h b/src/kiwano-physics/Fixture.h index 7e5e5911..0d498045 100644 --- a/src/kiwano-physics/Fixture.h +++ b/src/kiwano-physics/Fixture.h @@ -37,7 +37,14 @@ namespace kiwano static PhysicFixture Create(PhysicBody* body, PhysicShape* shape, float density = 0.f, float friction = 0.2f, float restitution = 0.f); + // 物体 + PhysicBody* GetBody(); + const PhysicBody* GetBody() const; + + // 形状 PhysicShape GetShape() const; + + // 下一夹具 (同一物体上) PhysicFixture GetNext() const; bool IsValid() const { return !!fixture_; } diff --git a/src/kiwano-physics/World.cpp b/src/kiwano-physics/World.cpp index b786b69a..afb6baf5 100644 --- a/src/kiwano-physics/World.cpp +++ b/src/kiwano-physics/World.cpp @@ -53,16 +53,37 @@ namespace kiwano } }; + class PhysicWorld::ContactListener + : public b2ContactListener + { + PhysicWorld* world_; + + public: + ContactListener(PhysicWorld* world) + : world_(world) + { + } + + void BeginContact(b2Contact* contact) override { world_->OnContactBegin(contact); } + void EndContact(b2Contact* contact) override { world_->OnContactEnd(contact); } + void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override { KGE_NOT_USED(contact); KGE_NOT_USED(oldManifold); } + void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override { KGE_NOT_USED(contact); KGE_NOT_USED(impulse); } + }; + PhysicWorld::PhysicWorld() : world_(b2Vec2(0, 10.0f)) , vel_iter_(6) , pos_iter_(2) , global_scale_(DefaultGlobalScale) , destruction_listener_(nullptr) + , contact_listener_(nullptr) , removing_joint_(false) { destruction_listener_ = new DestructionListener(this); world_.SetDestructionListener(destruction_listener_); + + contact_listener_ = new ContactListener(this); + world_.SetContactListener(contact_listener_); } PhysicWorld::~PhysicWorld() @@ -74,6 +95,13 @@ namespace kiwano destruction_listener_ = nullptr; } + world_.SetContactListener(nullptr); + if (contact_listener_) + { + delete contact_listener_; + contact_listener_ = nullptr; + } + // Make sure b2World was destroyed after b2Body RemoveAllChildren(); RemoveAllBodies(); diff --git a/src/kiwano-physics/World.h b/src/kiwano-physics/World.h index a0b98ec8..5cebada3 100644 --- a/src/kiwano-physics/World.h +++ b/src/kiwano-physics/World.h @@ -21,6 +21,7 @@ #pragma once #include #include +#include namespace kiwano { @@ -29,6 +30,7 @@ namespace kiwano // 物理世界 class KGE_API PhysicWorld : public Stage + , public PhysicContactDispatcher { friend class PhysicBody; friend class PhysicJoint; @@ -45,32 +47,29 @@ namespace kiwano void SetGravity(Vec2 gravity); // 获取全局缩放比例 - inline float GetGlobalScale() const { return global_scale_; } + inline float GetGlobalScale() const { return global_scale_; } // 设置全局缩放比例 - inline void SetGlobalScale(float scale) { global_scale_ = scale; } + inline void SetGlobalScale(float scale) { global_scale_ = scale; } // 游戏世界单位转换为物理世界单位 - inline float World2Stage(float value) { return value * GetGlobalScale(); } - inline Point World2Stage(const b2Vec2& pos) { return Point(World2Stage(pos.x), World2Stage(pos.y)); } + inline float World2Stage(float value) const { return value * GetGlobalScale(); } + inline Point World2Stage(const b2Vec2& pos) const { return Point(World2Stage(pos.x), World2Stage(pos.y)); } // 物理世界单位转换为游戏世界单位 - inline float Stage2World(float value) { return value / GetGlobalScale(); } - inline b2Vec2 Stage2World(const Point& pos) { return b2Vec2(Stage2World(pos.x), Stage2World(pos.y)); } + inline float Stage2World(float value) const { return value / GetGlobalScale(); } + inline b2Vec2 Stage2World(const Point& pos) const { return b2Vec2(Stage2World(pos.x), Stage2World(pos.y)); } // 设置速度迭代次数, 默认为 6 - inline void SetVelocityIterations(int vel_iter) { vel_iter_ = vel_iter; } + inline void SetVelocityIterations(int vel_iter) { vel_iter_ = vel_iter; } // 设置位置迭代次数, 默认为 2 - inline void SetPositionIterations(int pos_iter) { pos_iter_ = pos_iter; } + inline void SetPositionIterations(int pos_iter) { pos_iter_ = pos_iter; } - // 获取 Box2D 世界 b2World* GetB2World(); - - // 获取 Box2D 世界 const b2World* GetB2World() const; - protected: + private: // 移除物体 void RemoveBody(PhysicBody* body); @@ -89,19 +88,22 @@ namespace kiwano // 关节被移除 void JointRemoved(b2Joint* joint); - protected: void Update(Duration dt) override; - class DestructionListener; - friend DestructionListener; - - protected: + private: b2World world_; int vel_iter_; int pos_iter_; float global_scale_; + + class DestructionListener; + friend DestructionListener; DestructionListener* destruction_listener_; + class ContactListener; + friend ContactListener; + ContactListener* contact_listener_; + bool removing_joint_; Vector joints_; }; diff --git a/src/kiwano/2d/Actor.cpp b/src/kiwano/2d/Actor.cpp index 78fb509e..100520c7 100644 --- a/src/kiwano/2d/Actor.cpp +++ b/src/kiwano/2d/Actor.cpp @@ -283,7 +283,7 @@ namespace kiwano void Actor::SetStage(Stage* stage) { - if (stage && stage_ != stage) + if (stage_ != stage) { stage_ = stage; for (Actor* child = children_.first_item().get(); child; child = child->next_item().get()) @@ -539,6 +539,11 @@ namespace kiwano return nullptr; } + Actor::Children& Actor::GetAllChildren() + { + return children_; + } + Actor::Children const & Actor::GetAllChildren() const { return children_; diff --git a/src/kiwano/2d/Actor.h b/src/kiwano/2d/Actor.h index b0727211..6d805471 100644 --- a/src/kiwano/2d/Actor.h +++ b/src/kiwano/2d/Actor.h @@ -49,10 +49,10 @@ namespace kiwano Actor(); // 更新角色 - virtual void OnUpdate(Duration dt) { KGE_UNUSED(dt); } + virtual void OnUpdate(Duration dt) { KGE_NOT_USED(dt); } // 渲染角色 - virtual void OnRender(RenderTarget* rt) { KGE_UNUSED(rt); } + virtual void OnRender(RenderTarget* rt) { KGE_NOT_USED(rt); } // 获取显示状态 bool IsVisible() const { return visible_; } @@ -334,6 +334,9 @@ namespace kiwano String const& name ) const; + // 获取全部子角色 + Children& GetAllChildren(); + // 获取全部子角色 Children const& GetAllChildren() const; @@ -407,7 +410,7 @@ namespace kiwano void SetStage(Stage* stage); - protected: + private: bool visible_; bool update_pausing_; bool cascade_opacity_; diff --git a/src/kiwano/2d/DebugActor.cpp b/src/kiwano/2d/DebugActor.cpp index 295cfed0..65dc2900 100644 --- a/src/kiwano/2d/DebugActor.cpp +++ b/src/kiwano/2d/DebugActor.cpp @@ -84,7 +84,7 @@ namespace kiwano void DebugActor::OnUpdate(Duration dt) { - KGE_UNUSED(dt); + KGE_NOT_USED(dt); frame_time_.push_back(Time::Now()); while (frame_time_.back() - frame_time_.front() >= time::Sec) diff --git a/src/kiwano/2d/Layer.cpp b/src/kiwano/2d/Layer.cpp index 4c651eae..8a01bc63 100644 --- a/src/kiwano/2d/Layer.cpp +++ b/src/kiwano/2d/Layer.cpp @@ -72,7 +72,7 @@ namespace kiwano if (!swallow_) { ActorPtr prev; - for (auto child = children_.last_item(); child; child = prev) + for (auto child = GetAllChildren().last_item(); child; child = prev) { prev = child->prev_item(); child->Dispatch(evt); diff --git a/src/kiwano/2d/ShapeActor.cpp b/src/kiwano/2d/ShapeActor.cpp index a34e0a4c..759e12e7 100644 --- a/src/kiwano/2d/ShapeActor.cpp +++ b/src/kiwano/2d/ShapeActor.cpp @@ -189,7 +189,7 @@ namespace kiwano void RoundRectActor::SetRadius(Vec2 const& radius) { - SetRoundedRect(size_, radius); + SetRoundedRect(GetSize(), radius); } void RoundRectActor::SetRectSize(Size const& size) diff --git a/src/kiwano/2d/ShapeActor.h b/src/kiwano/2d/ShapeActor.h index d69623a6..15667da7 100644 --- a/src/kiwano/2d/ShapeActor.h +++ b/src/kiwano/2d/ShapeActor.h @@ -166,7 +166,7 @@ namespace kiwano inline Vec2 GetRadius() const { return radius_; } - inline Size GetRectSize() const { return size_; } + inline Size GetRectSize() const { return GetSize(); } void SetRadius( Vec2 const& radius diff --git a/src/kiwano/2d/Stage.cpp b/src/kiwano/2d/Stage.cpp index 59cfc9f8..d3f12fbd 100644 --- a/src/kiwano/2d/Stage.cpp +++ b/src/kiwano/2d/Stage.cpp @@ -26,7 +26,7 @@ namespace kiwano { Stage::Stage() { - stage_ = this; + SetStage(this); SetAnchor(Vec2{ 0, 0 }); SetSize(Renderer::GetInstance()->GetOutputSize()); diff --git a/src/kiwano/2d/Text.cpp b/src/kiwano/2d/Text.cpp index 508598a5..47d60b9a 100644 --- a/src/kiwano/2d/Text.cpp +++ b/src/kiwano/2d/Text.cpp @@ -42,10 +42,10 @@ namespace kiwano Text::Text() : font_(text_default_font) - , style_(text_default_style) , layout_dirty_(false) , format_dirty_(false) { + text_layout_.SetTextStyle(text_default_style); } Text::Text(String const& text) @@ -65,11 +65,11 @@ namespace kiwano Text::Text(String const& text, const Font & font, const TextStyle & style) : font_(font) - , style_(style) , text_(text) , layout_dirty_(true) , format_dirty_(true) { + text_layout_.SetTextStyle(style); } Text::~Text() @@ -84,7 +84,7 @@ namespace kiwano void Text::SetStyle(const TextStyle& style) { - style_ = style; + text_layout_.SetTextStyle(style); layout_dirty_ = true; } @@ -121,11 +121,6 @@ namespace kiwano } } - void Text::SetColor(Color const& color) - { - style_.color = color; - } - void Text::SetItalic(bool italic) { if (font_.italic != italic) @@ -137,67 +132,73 @@ namespace kiwano void Text::SetWrapWidth(float wrap_width) { - if (style_.wrap_width != wrap_width) + if (text_layout_.GetTextStyle().wrap_width != wrap_width) { - style_.wrap_width = wrap_width; + text_layout_.GetTextStyle().wrap_width = wrap_width; layout_dirty_ = true; } } void Text::SetLineSpacing(float line_spacing) { - if (style_.line_spacing != line_spacing) + if (text_layout_.GetTextStyle().line_spacing != line_spacing) { - style_.line_spacing = line_spacing; + text_layout_.GetTextStyle().line_spacing = line_spacing; layout_dirty_ = true; } } void Text::SetAlignment(TextAlign align) { - if (style_.alignment != align) + if (text_layout_.GetTextStyle().alignment != align) { - style_.alignment = align; + text_layout_.GetTextStyle().alignment = align; layout_dirty_ = true; } } void Text::SetUnderline(bool underline) { - if (style_.underline != underline) + if (text_layout_.GetTextStyle().underline != underline) { - style_.underline = underline; + text_layout_.GetTextStyle().underline = underline; layout_dirty_ = true; } } void Text::SetStrikethrough(bool strikethrough) { - if (style_.strikethrough != strikethrough) + if (text_layout_.GetTextStyle().strikethrough != strikethrough) { - style_.strikethrough = strikethrough; + text_layout_.GetTextStyle().strikethrough = strikethrough; layout_dirty_ = true; } } + void Text::SetColor(Color const& color) + { + text_layout_.GetTextStyle().color = color; + text_layout_.GetTextStyle().color = color; + } + void Text::SetOutline(bool outline) { - style_.outline = outline; + text_layout_.GetTextStyle().outline = outline; } void Text::SetOutlineColor(Color const&outline_color) { - style_.outline_color = outline_color; + text_layout_.GetTextStyle().outline_color = outline_color; } void Text::SetOutlineWidth(float outline_width) { - style_.outline_width = outline_width; + text_layout_.GetTextStyle().outline_width = outline_width; } void Text::SetOutlineStroke(StrokeStyle outline_stroke) { - style_.outline_stroke = outline_stroke; + text_layout_.GetTextStyle().outline_stroke = outline_stroke; } void Text::OnRender(RenderTarget* rt) @@ -216,13 +217,13 @@ namespace kiwano if (format_dirty_) { format_dirty_ = false; - text_layout_.Update(font_); + text_layout_.UpdateFont(font_); } if (layout_dirty_) { layout_dirty_ = false; - text_layout_.Update(text_, style_); + text_layout_.UpdateLayout(text_); SetSize(text_layout_.GetLayoutSize()); } } diff --git a/src/kiwano/2d/Text.h b/src/kiwano/2d/Text.h index c4cc6586..46623d79 100644 --- a/src/kiwano/2d/Text.h +++ b/src/kiwano/2d/Text.h @@ -60,7 +60,7 @@ namespace kiwano inline Font GetFont() const { return font_; } // 获取文本样式 - inline TextStyle GetStyle() const { return style_; } + inline TextStyle GetStyle() const { return text_layout_.GetTextStyle(); } // 获取文本布局 inline TextLayout GetLayout() const { return text_layout_; } @@ -171,6 +171,5 @@ namespace kiwano TextLayout text_layout_; String text_; Font font_; - TextStyle style_; }; } diff --git a/src/kiwano/base/EventDispatcher.cpp b/src/kiwano/base/EventDispatcher.cpp index 91c715fd..ee5d6673 100644 --- a/src/kiwano/base/EventDispatcher.cpp +++ b/src/kiwano/base/EventDispatcher.cpp @@ -33,7 +33,7 @@ namespace kiwano { next = listener->next_item(); - if (listener->type_ == evt.type) + if (listener->IsRunning() && listener->type_ == evt.type) { listener->callback_(evt); } diff --git a/src/kiwano/macros.h b/src/kiwano/macros.h index eede99d6..23940124 100644 --- a/src/kiwano/macros.h +++ b/src/kiwano/macros.h @@ -112,6 +112,6 @@ # pragma warning (disable: 4251) #endif -#define KGE_UNUSED(VAR) ((void)VAR) +#define KGE_NOT_USED(VAR) ((void)VAR) #define KGE_DEPRECATED(...) __declspec(deprecated(__VA_ARGS__)) diff --git a/src/kiwano/renderer/TextLayout.cpp b/src/kiwano/renderer/TextLayout.cpp index 41495bfd..21c230ae 100644 --- a/src/kiwano/renderer/TextLayout.cpp +++ b/src/kiwano/renderer/TextLayout.cpp @@ -53,19 +53,18 @@ namespace kiwano TextLayout::TextLayout(String const& text, Font const& font, TextStyle const& style) { - Update(font); - Update(text, style); + UpdateFont(font); + SetTextStyle(style); + UpdateLayout(text); } - void TextLayout::Update(Font const& font) + void TextLayout::UpdateFont(Font const& font) { text_format_.Update(font); } - void TextLayout::Update(String const& text, TextStyle const& style) + void TextLayout::UpdateLayout(String const& text) { - style_ = style; - if (text.empty()) { text_format_.SetTextFormat(nullptr); @@ -83,7 +82,7 @@ namespace kiwano if (SUCCEEDED(hr)) { - if (style.line_spacing == 0.f) + if (style_.line_spacing == 0.f) { hr = text_layout_->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT, 0, 0); } @@ -91,25 +90,25 @@ namespace kiwano { hr = text_layout_->SetLineSpacing( DWRITE_LINE_SPACING_METHOD_UNIFORM, - style.line_spacing, - style.line_spacing * 0.8f + style_.line_spacing, + style_.line_spacing * 0.8f ); } } if (SUCCEEDED(hr)) { - hr = text_layout_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(style.alignment)); + hr = text_layout_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(style_.alignment)); } if (SUCCEEDED(hr)) { - hr = text_layout_->SetWordWrapping((style.wrap_width > 0) ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP); + hr = text_layout_->SetWordWrapping((style_.wrap_width > 0) ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP); } if (SUCCEEDED(hr)) { - if (style.underline) + if (style_.underline) { hr = text_layout_->SetUnderline(true, { 0, UINT32(text.length()) }); } @@ -117,7 +116,7 @@ namespace kiwano if (SUCCEEDED(hr)) { - if (style.strikethrough) + if (style_.strikethrough) { text_layout_->SetStrikethrough(true, { 0, UINT32(text.length()) }); } @@ -125,9 +124,9 @@ namespace kiwano if (SUCCEEDED(hr)) { - if (style.wrap_width > 0) + if (style_.wrap_width > 0) { - hr = text_layout_->SetMaxWidth(style.wrap_width); + hr = text_layout_->SetMaxWidth(style_.wrap_width); } else { diff --git a/src/kiwano/renderer/TextLayout.h b/src/kiwano/renderer/TextLayout.h index 2a5e6d83..cb5ad358 100644 --- a/src/kiwano/renderer/TextLayout.h +++ b/src/kiwano/renderer/TextLayout.h @@ -52,15 +52,17 @@ namespace kiwano TextLayout(String const& text, Font const& font, TextStyle const& style); - void Update(Font const& font); + void UpdateFont(Font const& font); - void Update(String const& text, TextStyle const& style); + void UpdateLayout(String const& text); uint32_t GetLineCount(); Size GetLayoutSize() const; + inline TextStyle& GetTextStyle() { return style_; } inline TextStyle const& GetTextStyle() const { return style_; } + inline void SetTextStyle(TextStyle const& style) { style_ = style; } public: inline TextFormat GetTextFormat() const { return text_format_; } diff --git a/src/kiwano/renderer/win32/TextRenderer.cpp b/src/kiwano/renderer/win32/TextRenderer.cpp index e5af503a..c1fad6e3 100644 --- a/src/kiwano/renderer/win32/TextRenderer.cpp +++ b/src/kiwano/renderer/win32/TextRenderer.cpp @@ -213,10 +213,10 @@ namespace kiwano __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, IUnknown* clientDrawingEffect) { - KGE_UNUSED(clientDrawingContext); - KGE_UNUSED(measuringMode); - KGE_UNUSED(glyphRunDescription); - KGE_UNUSED(clientDrawingEffect); + KGE_NOT_USED(clientDrawingContext); + KGE_NOT_USED(measuringMode); + KGE_NOT_USED(glyphRunDescription); + KGE_NOT_USED(clientDrawingEffect); HRESULT hr = S_OK; @@ -311,8 +311,8 @@ namespace kiwano __in DWRITE_UNDERLINE const* underline, IUnknown* clientDrawingEffect) { - KGE_UNUSED(clientDrawingContext); - KGE_UNUSED(clientDrawingEffect); + KGE_NOT_USED(clientDrawingContext); + KGE_NOT_USED(clientDrawingEffect); HRESULT hr; @@ -380,8 +380,8 @@ namespace kiwano __in DWRITE_STRIKETHROUGH const* strikethrough, IUnknown* clientDrawingEffect) { - KGE_UNUSED(clientDrawingContext); - KGE_UNUSED(clientDrawingEffect); + KGE_NOT_USED(clientDrawingContext); + KGE_NOT_USED(clientDrawingEffect); HRESULT hr; @@ -451,13 +451,13 @@ namespace kiwano BOOL IsRightToLeft, IUnknown* clientDrawingEffect) { - KGE_UNUSED(clientDrawingContext); - KGE_UNUSED(originX); - KGE_UNUSED(originY); - KGE_UNUSED(inlineObject); - KGE_UNUSED(IsSideways); - KGE_UNUSED(IsRightToLeft); - KGE_UNUSED(clientDrawingEffect); + KGE_NOT_USED(clientDrawingContext); + KGE_NOT_USED(originX); + KGE_NOT_USED(originY); + KGE_NOT_USED(inlineObject); + KGE_NOT_USED(IsSideways); + KGE_NOT_USED(IsRightToLeft); + KGE_NOT_USED(clientDrawingEffect); return E_NOTIMPL; } @@ -465,7 +465,7 @@ namespace kiwano __maybenull void* clientDrawingContext, __out BOOL* isDisabled) { - KGE_UNUSED(clientDrawingContext); + KGE_NOT_USED(clientDrawingContext); *isDisabled = FALSE; return S_OK; @@ -475,7 +475,7 @@ namespace kiwano __maybenull void* clientDrawingContext, __out DWRITE_MATRIX* transform) { - KGE_UNUSED(clientDrawingContext); + KGE_NOT_USED(clientDrawingContext); pRT_->GetTransform(reinterpret_cast(transform)); return S_OK; @@ -485,7 +485,7 @@ namespace kiwano __maybenull void* clientDrawingContext, __out float* pixelsPerDip) { - KGE_UNUSED(clientDrawingContext); + KGE_NOT_USED(clientDrawingContext); float x, yUnused;