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