1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "../Precompiled.h"
24 
25 #include "../Core/Context.h"
26 #include "../IO/Log.h"
27 #include "../Scene/Node.h"
28 #include "../Scene/Scene.h"
29 #include "../Urho2D/CollisionShape2D.h"
30 #include "../Urho2D/PhysicsUtils2D.h"
31 #include "../Urho2D/RigidBody2D.h"
32 
33 #include "../DebugNew.h"
34 
35 namespace Urho3D
36 {
37 
CollisionShape2D(Context * context)38 CollisionShape2D::CollisionShape2D(Context* context) :
39     Component(context),
40     fixture_(0),
41     cachedWorldScale_(Vector3::ONE)
42 {
43 
44 }
45 
~CollisionShape2D()46 CollisionShape2D::~CollisionShape2D()
47 {
48     if (rigidBody_)
49         rigidBody_->RemoveCollisionShape2D(this);
50 
51     ReleaseFixture();
52 }
53 
RegisterObject(Context * context)54 void CollisionShape2D::RegisterObject(Context* context)
55 {
56     URHO3D_ACCESSOR_ATTRIBUTE("Trigger", IsTrigger, SetTrigger, bool, false, AM_DEFAULT);
57     URHO3D_ACCESSOR_ATTRIBUTE("Category Bits", GetCategoryBits, SetCategoryBits, int, 0, AM_DEFAULT);
58     URHO3D_ACCESSOR_ATTRIBUTE("Mask Bits", GetMaskBits, SetMaskBits, int, 0, AM_DEFAULT);
59     URHO3D_ACCESSOR_ATTRIBUTE("Group Index", GetGroupIndex, SetGroupIndex, int, 0, AM_DEFAULT);
60     URHO3D_ACCESSOR_ATTRIBUTE("Density", GetDensity, SetDensity, float, 0.0f, AM_DEFAULT);
61     URHO3D_ACCESSOR_ATTRIBUTE("Friction", GetFriction, SetFriction, float, 0.2f, AM_DEFAULT);
62     URHO3D_ACCESSOR_ATTRIBUTE("Restitution", GetRestitution, SetRestitution, float, 0.0f, AM_DEFAULT);
63 }
64 
OnSetEnabled()65 void CollisionShape2D::OnSetEnabled()
66 {
67     if (IsEnabledEffective())
68     {
69         CreateFixture();
70         if (rigidBody_)
71             rigidBody_->AddCollisionShape2D(this);
72     }
73     else
74     {
75         if (rigidBody_)
76             rigidBody_->RemoveCollisionShape2D(this);
77         ReleaseFixture();
78     }
79 }
80 
SetTrigger(bool trigger)81 void CollisionShape2D::SetTrigger(bool trigger)
82 {
83     if (fixtureDef_.isSensor == trigger)
84         return;
85 
86     fixtureDef_.isSensor = trigger;
87 
88     if (fixture_)
89         fixture_->SetSensor(trigger);
90 
91     MarkNetworkUpdate();
92 }
93 
SetCategoryBits(int categoryBits)94 void CollisionShape2D::SetCategoryBits(int categoryBits)
95 {
96     if (fixtureDef_.filter.categoryBits == categoryBits)
97         return;
98 
99     fixtureDef_.filter.categoryBits = (uint16)categoryBits;
100 
101     if (fixture_)
102         fixture_->SetFilterData(fixtureDef_.filter);
103 
104     MarkNetworkUpdate();
105 }
106 
SetMaskBits(int maskBits)107 void CollisionShape2D::SetMaskBits(int maskBits)
108 {
109     if (fixtureDef_.filter.maskBits == maskBits)
110         return;
111 
112     fixtureDef_.filter.maskBits = (uint16)maskBits;
113 
114     if (fixture_)
115         fixture_->SetFilterData(fixtureDef_.filter);
116 
117     MarkNetworkUpdate();
118 }
119 
SetGroupIndex(int groupIndex)120 void CollisionShape2D::SetGroupIndex(int groupIndex)
121 {
122     if (fixtureDef_.filter.groupIndex == groupIndex)
123         return;
124 
125     fixtureDef_.filter.groupIndex = (int16)groupIndex;
126 
127     if (fixture_)
128         fixture_->SetFilterData(fixtureDef_.filter);
129 
130     MarkNetworkUpdate();
131 }
132 
SetDensity(float density)133 void CollisionShape2D::SetDensity(float density)
134 {
135     if (fixtureDef_.density == density)
136         return;
137 
138     fixtureDef_.density = density;
139 
140     if (fixture_)
141     {
142         // This will not automatically adjust the mass of the body
143         fixture_->SetDensity(density);
144 
145         if (rigidBody_->GetUseFixtureMass())
146             rigidBody_->GetBody()->ResetMassData();
147     }
148 
149     MarkNetworkUpdate();
150 }
151 
SetFriction(float friction)152 void CollisionShape2D::SetFriction(float friction)
153 {
154     if (fixtureDef_.friction == friction)
155         return;
156 
157     fixtureDef_.friction = friction;
158 
159     if (fixture_)
160     {
161         // This will not change the friction of existing contacts
162         fixture_->SetFriction(friction);
163 
164         b2ContactEdge* contractEdge = rigidBody_->GetBody()->GetContactList();
165         while (contractEdge)
166         {
167             b2Contact* contact = contractEdge->contact;
168             if (contact->GetFixtureA() == fixture_ || contact->GetFixtureB() == fixture_)
169                 contractEdge->contact->ResetFriction();
170 
171             contractEdge = contractEdge->next;
172         }
173     }
174 
175     MarkNetworkUpdate();
176 }
177 
SetRestitution(float restitution)178 void CollisionShape2D::SetRestitution(float restitution)
179 {
180     if (fixtureDef_.restitution == restitution)
181         return;
182 
183     fixtureDef_.restitution = restitution;
184 
185     if (fixture_)
186     {
187         // This will not change the restitution of existing contacts
188         fixture_->SetRestitution(restitution);
189 
190         b2ContactEdge* contractEdge = rigidBody_->GetBody()->GetContactList();
191         while (contractEdge)
192         {
193             b2Contact* contact = contractEdge->contact;
194             if (contact->GetFixtureA() == fixture_ || contact->GetFixtureB() == fixture_)
195                 contractEdge->contact->ResetRestitution();
196 
197             contractEdge = contractEdge->next;
198         }
199     }
200 
201     MarkNetworkUpdate();
202 }
203 
CreateFixture()204 void CollisionShape2D::CreateFixture()
205 {
206     if (fixture_)
207         return;
208 
209     if (!fixtureDef_.shape)
210         return;
211 
212     if (!rigidBody_)
213     {
214         rigidBody_ = node_->GetComponent<RigidBody2D>(); // RigidBody2D can be created after CollisionShape2D
215         if (!rigidBody_)
216             return;
217     }
218 
219     b2Body* body = rigidBody_->GetBody();
220     if (!body)
221         return;
222 
223     // Chain shape must have atleast two vertices before creating fixture
224     if (fixtureDef_.shape->m_type != b2Shape::e_chain || static_cast<const b2ChainShape*>(fixtureDef_.shape)->m_count >= 2)
225     {
226         b2MassData massData;
227         body->GetMassData(&massData);
228         fixture_ = body->CreateFixture(&fixtureDef_);
229         if (!rigidBody_->GetUseFixtureMass()) // Workaround for resetting mass in CreateFixture().
230             body->SetMassData(&massData);
231         fixture_->SetUserData(this);
232     }
233 }
234 
ReleaseFixture()235 void CollisionShape2D::ReleaseFixture()
236 {
237     if (!fixture_)
238         return;
239 
240     if (!rigidBody_)
241         return;
242 
243     b2Body* body = rigidBody_->GetBody();
244     if (!body)
245         return;
246 
247     b2MassData massData;
248     body->GetMassData(&massData);
249     body->DestroyFixture(fixture_);
250     if (!rigidBody_->GetUseFixtureMass()) // Workaround for resetting mass in DestroyFixture().
251         body->SetMassData(&massData);
252     fixture_ = 0;
253 }
254 
GetMass() const255 float CollisionShape2D::GetMass() const
256 {
257     if (!fixture_)
258         return 0.0f;
259 
260     b2MassData massData;
261     fixture_->GetMassData(&massData);
262 
263     return massData.mass;
264 }
265 
GetInertia() const266 float CollisionShape2D::GetInertia() const
267 {
268     if (!fixture_)
269         return 0.0f;
270 
271     b2MassData massData;
272     fixture_->GetMassData(&massData);
273 
274     return massData.I;
275 }
276 
GetMassCenter() const277 Vector2 CollisionShape2D::GetMassCenter() const
278 {
279     if (!fixture_)
280         return Vector2::ZERO;
281 
282     b2MassData massData;
283     fixture_->GetMassData(&massData);
284 
285     return ToVector2(massData.center);
286 }
287 
OnNodeSet(Node * node)288 void CollisionShape2D::OnNodeSet(Node* node)
289 {
290     Component::OnNodeSet(node);
291 
292     if (node)
293     {
294         node->AddListener(this);
295         rigidBody_ = node->GetComponent<RigidBody2D>();
296         if (rigidBody_)
297         {
298             CreateFixture();
299             rigidBody_->AddCollisionShape2D(this);
300         }
301     }
302 }
303 
OnMarkedDirty(Node * node)304 void CollisionShape2D::OnMarkedDirty(Node* node)
305 {
306     // Use signed world scale to allow flipping of sprites by negative scale to work properly in regard to the collision shape
307     Vector3 newWorldScale = node_->GetSignedWorldScale();
308 
309     Vector3 delta = newWorldScale - cachedWorldScale_;
310     if (delta.DotProduct(delta) < 0.01f)
311         return;
312 
313     // Physics operations are not safe from worker threads
314     Scene* scene = GetScene();
315     if (scene && scene->IsThreadedUpdate())
316     {
317         scene->DelayedMarkedDirty(this);
318         return;
319     }
320 
321     cachedWorldScale_ = newWorldScale;
322 
323     if (fixture_)
324         ApplyNodeWorldScale();
325 }
326 
327 }
328