Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
PhysicsProcessor.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 
7 using SiliconStudio.Core;
8 using SiliconStudio.Core.Collections;
9 using SiliconStudio.Core.Mathematics;
10 using SiliconStudio.Paradox.Effects;
11 using SiliconStudio.Paradox.Effects.Modules;
12 using SiliconStudio.Paradox.Engine;
13 using SiliconStudio.Paradox.EntityModel;
14 using SiliconStudio.Paradox.Games;
15 using SiliconStudio.Paradox.Graphics;
16 using SiliconStudio.Paradox.Threading;
17 
18 namespace SiliconStudio.Paradox.Physics
19 {
20  public class PhysicsProcessor : EntityProcessor<PhysicsProcessor.AssociatedData>
21  {
22  public class AssociatedData
23  {
26  public ModelComponent ModelComponent; //not mandatory, could be null e.g. invisible triggers
27  }
28 
29  readonly FastList<PhysicsElement> elements = new FastList<PhysicsElement>();
30  readonly FastList<PhysicsElement> characters = new FastList<PhysicsElement>();
31 
32  Bullet2PhysicsSystem physicsSystem;
33  RenderSystem renderSystem;
34 
36  : base(new PropertyKey[] { PhysicsComponent.Key, TransformationComponent.Key })
37  {
38  }
39 
40  protected override AssociatedData GenerateAssociatedData(Entity entity)
41  {
42  return new AssociatedData
43  {
44  PhysicsComponent = entity.Get(PhysicsComponent.Key),
46  ModelComponent = entity.Get(ModelComponent.Key),
47  };
48  }
49 
50  //This is called by the physics engine to update the transformation of Dynamic rigidbodies
51  static void RigidBodySetWorldTransform(PhysicsElement element, Matrix physicsTransform)
52  {
53  element.UpdateTransformationComponent(physicsTransform);
54  }
55 
56  //This is valid for Dynamic rigidbodies (called once at initialization)
57  //and Kinematic rigidbodies (called every simulation tick (if body not sleeping) to let the physics engine know where the kinematic body is)
58  static void RigidBodyGetWorldTransform(PhysicsElement element, out Matrix physicsTransform)
59  {
60  physicsTransform = element.DerivePhysicsTransformation();
61  }
62 
63  void NewElement(PhysicsElement element, AssociatedData data, Entity entity)
64  {
65  if (element.Shape == null) return; //no shape no purpose
66 
67  var shape = element.Shape.Shape;
68 
69  element.Data = data;
70  element.BoneIndex = -1;
71 
72  if (!element.Sprite && element.LinkedBoneName != null && data.ModelComponent != null)
73  {
74  //find the linked bone, if can't be found we just skip this element
75  for (var index = 0; index < data.ModelComponent.ModelViewHierarchy.Nodes.Length; index++)
76  {
77  var node = data.ModelComponent.ModelViewHierarchy.Nodes[index];
78  if (node.Name != element.LinkedBoneName) continue;
79  element.BoneIndex = index;
80  break;
81  }
82 
83  if (element.BoneIndex == -1)
84  {
85  throw new Exception("The specified LinkedBoneName doesn't exist in the model hierarchy.");
86  }
87  }
88 
89  //complex hierarchy models not implemented yet
90  if (element.BoneIndex != -1)
91  {
92  throw new NotImplementedException("Physics on complex hierarchy model's bones is not implemented yet.");
93  }
94 
95  var defaultGroups = element.CanCollideWith == 0 || element.CollisionGroup == 0;
96 
97  switch (element.Type)
98  {
99  case PhysicsElement.Types.PhantomCollider:
100  {
101  var c = physicsSystem.PhysicsEngine.CreateCollider(shape);
102 
103  element.Collider = c; //required by the next call
104  element.Collider.EntityObject = entity; //required by the next call
105  element.UpdatePhysicsTransformation(); //this will set position and rotation of the collider
106 
107  c.IsTrigger = true;
108 
109  if (defaultGroups)
110  {
111  physicsSystem.PhysicsEngine.AddCollider(c);
112  }
113  else
114  {
115  physicsSystem.PhysicsEngine.AddCollider(c, (CollisionFilterGroups)element.CollisionGroup, element.CanCollideWith);
116  }
117  }
118  break;
119  case PhysicsElement.Types.StaticCollider:
120  {
121  var c = physicsSystem.PhysicsEngine.CreateCollider(shape);
122 
123  element.Collider = c; //required by the next call
124  element.Collider.EntityObject = entity; //required by the next call
125  element.UpdatePhysicsTransformation(); //this will set position and rotation of the collider
126 
127  c.IsTrigger = false;
128 
129  if (defaultGroups)
130  {
131  physicsSystem.PhysicsEngine.AddCollider(c);
132  }
133  else
134  {
135  physicsSystem.PhysicsEngine.AddCollider(c, (CollisionFilterGroups)element.CollisionGroup, element.CanCollideWith);
136  }
137  }
138  break;
139  case PhysicsElement.Types.StaticRigidBody:
140  {
141  var rb = physicsSystem.PhysicsEngine.CreateRigidBody(shape);
142 
143  rb.EntityObject = entity;
144  rb.GetWorldTransformCallback = (out Matrix transform) => RigidBodyGetWorldTransform(element, out transform);
145  rb.SetWorldTransformCallback = transform => RigidBodySetWorldTransform(element, transform);
146  element.Collider = rb;
147  element.UpdatePhysicsTransformation(); //this will set position and rotation of the collider
148 
149  rb.Type = RigidBodyTypes.Static;
150 
151  if (defaultGroups)
152  {
153  physicsSystem.PhysicsEngine.AddRigidBody(rb);
154  }
155  else
156  {
157  physicsSystem.PhysicsEngine.AddRigidBody(rb, (CollisionFilterGroups)element.CollisionGroup, element.CanCollideWith);
158  }
159  }
160  break;
161  case PhysicsElement.Types.DynamicRigidBody:
162  {
163  var rb = physicsSystem.PhysicsEngine.CreateRigidBody(shape);
164 
165  rb.EntityObject = entity;
166  rb.GetWorldTransformCallback = (out Matrix transform) => RigidBodyGetWorldTransform(element, out transform);
167  rb.SetWorldTransformCallback = transform => RigidBodySetWorldTransform(element, transform);
168  element.Collider = rb;
169  element.UpdatePhysicsTransformation(); //this will set position and rotation of the collider
170 
171  rb.Type = RigidBodyTypes.Dynamic;
172  rb.Mass = 1.0f;
173 
174  if (defaultGroups)
175  {
176  physicsSystem.PhysicsEngine.AddRigidBody(rb);
177  }
178  else
179  {
180  physicsSystem.PhysicsEngine.AddRigidBody(rb, (CollisionFilterGroups)element.CollisionGroup, element.CanCollideWith);
181  }
182  }
183  break;
184  case PhysicsElement.Types.KinematicRigidBody:
185  {
186  var rb = physicsSystem.PhysicsEngine.CreateRigidBody(shape);
187 
188  rb.EntityObject = entity;
189  rb.GetWorldTransformCallback = (out Matrix transform) => RigidBodyGetWorldTransform(element, out transform);
190  rb.SetWorldTransformCallback = transform => RigidBodySetWorldTransform(element, transform);
191  element.Collider = rb;
192  element.UpdatePhysicsTransformation(); //this will set position and rotation of the collider
193 
194  rb.Type = RigidBodyTypes.Kinematic;
195  rb.Mass = 0.0f;
196 
197  if (defaultGroups)
198  {
199  physicsSystem.PhysicsEngine.AddRigidBody(rb);
200  }
201  else
202  {
203  physicsSystem.PhysicsEngine.AddRigidBody(rb, (CollisionFilterGroups)element.CollisionGroup, element.CanCollideWith);
204  }
205  }
206  break;
207  case PhysicsElement.Types.CharacterController:
208  {
209  var ch = physicsSystem.PhysicsEngine.CreateCharacter(shape, element.StepHeight);
210 
211  element.Collider = ch;
212  element.Collider.EntityObject = entity;
213  element.UpdatePhysicsTransformation(); //this will set position and rotation of the collider
214 
215  if (defaultGroups)
216  {
217  physicsSystem.PhysicsEngine.AddCharacter(ch);
218  }
219  else
220  {
221  physicsSystem.PhysicsEngine.AddCharacter(ch, (CollisionFilterGroups)element.CollisionGroup, element.CanCollideWith);
222  }
223 
224  characters.Add(element);
225  }
226  break;
227  }
228 
229  elements.Add(element);
230  }
231 
232  void DeleteElement(PhysicsElement element, bool now = false)
233  {
234  //might be possible that this element was not valid during creation so it would be already null
235  if (element.Collider == null) return;
236 
237  var toDispose = new List<IDisposable>();
238 
239  elements.Remove(element);
240 
241  switch (element.Type)
242  {
243  case PhysicsElement.Types.PhantomCollider:
244  case PhysicsElement.Types.StaticCollider:
245  {
246  physicsSystem.PhysicsEngine.RemoveCollider(element.Collider);
247  }
248  break;
249  case PhysicsElement.Types.StaticRigidBody:
250  case PhysicsElement.Types.DynamicRigidBody:
251  case PhysicsElement.Types.KinematicRigidBody:
252  {
253  var rb = (RigidBody)element.Collider;
254  var constraints = rb.LinkedConstraints.ToArray();
255  foreach (var c in constraints)
256  {
257  physicsSystem.PhysicsEngine.RemoveConstraint(c);
258  toDispose.Add(c);
259  }
260 
261  physicsSystem.PhysicsEngine.RemoveRigidBody(rb);
262  }
263  break;
264  case PhysicsElement.Types.CharacterController:
265  {
266  characters.Remove(element);
267  physicsSystem.PhysicsEngine.RemoveCharacter((Character) element.Collider);
268  }
269  break;
270  }
271 
272  toDispose.Add(element.Collider);
273  element.Collider = null;
274 
275  //dispose in another thread for better performance
276  if (!now)
277  {
278  TaskList.Dispatch(toDispose, 4, 128, (i, disposable) => disposable.Dispose());
279  }
280  else
281  {
282  foreach (var d in toDispose)
283  {
284  d.Dispose();
285  }
286  }
287  }
288 
289  protected override void OnEntityAdding(Entity entity, AssociatedData data)
290  {
291  if (!physicsSystem.PhysicsEngine.Initialized) return;
292 
293  foreach (var element in data.PhysicsComponent.Elements)
294  {
295  NewElement(element, data, entity);
296  }
297  }
298 
299  protected override void OnEntityRemoved(Entity entity, AssociatedData data)
300  {
301  if (!physicsSystem.PhysicsEngine.Initialized) return;
302 
303  foreach (var element in data.PhysicsComponent.Elements)
304  {
305  DeleteElement(element, true);
306  }
307  }
308 
309  protected override void OnEnabledChanged(Entity entity, bool enabled)
310  {
311  if (!physicsSystem.PhysicsEngine.Initialized) return;
312 
313  var elements = entity.Get(PhysicsComponent.Key).Elements;
314 
315  foreach (var element in elements.Where(element => element.Collider != null))
316  {
317  element.Collider.Enabled = enabled;
318  }
319  }
320 
321  protected override void OnSystemAdd()
322  {
323  physicsSystem = (Bullet2PhysicsSystem)Services.GetSafeServiceAs<IPhysicsSystem>();
324  renderSystem = Services.GetSafeServiceAs<RenderSystem>();
325 
326  //setup debug device and debug shader
327  var gfxDevice = Services.GetSafeServiceAs<IGraphicsDeviceService>();
328  physicsSystem.PhysicsEngine.DebugGraphicsDevice = gfxDevice.GraphicsDevice;
329 
330  //Debug primitives render, should happen about the last steps of the pipeline
331  renderSystem.Pipeline.EndPass += DebugShapesDraw;
332  }
333 
334  protected override void OnSystemRemove()
335  {
336  //remove all elements from the engine
337  foreach (var element in elements)
338  {
339  DeleteElement(element);
340  }
341  }
342 
343  private void DrawDebugCompound(ref Matrix viewProj, CompoundColliderShape compound, PhysicsElement element)
344  {
345  for (var i = 0; i < compound.Count; i++)
346  {
347  var subShape = compound[i];
348  switch (subShape.Type)
349  {
350  case ColliderShapeTypes.StaticPlane:
351  continue;
352  case ColliderShapeTypes.Compound:
353  DrawDebugCompound(ref viewProj, (CompoundColliderShape)compound[i], element);
354  break;
355  default:
356  {
357  var physTrans = element.BoneIndex == -1 ? element.Collider.PhysicsWorldTransform : element.BoneWorldMatrix;
358  physTrans = Matrix.Multiply(subShape.PositiveCenterMatrix, physTrans);
359 
360  //must account collider shape scaling
361  Matrix worldTrans;
362  Matrix.Multiply(ref subShape.DebugPrimitiveScaling, ref physTrans, out worldTrans);
363 
364  physicsSystem.PhysicsEngine.DebugEffect.WorldViewProj = worldTrans * viewProj;
365  physicsSystem.PhysicsEngine.DebugEffect.Color = element.Collider.IsActive ? Color.Green : Color.Red;
366  physicsSystem.PhysicsEngine.DebugEffect.UseUv = subShape.Type != ColliderShapeTypes.ConvexHull;
367 
368  physicsSystem.PhysicsEngine.DebugEffect.Apply();
369 
370  subShape.DebugPrimitive.Draw();
371  }
372  break;
373  }
374  }
375  }
376 
377  private void DebugShapesDraw(RenderContext context)
378  {
379  if (!physicsSystem.PhysicsEngine.CreateDebugPrimitives ||
380  !physicsSystem.PhysicsEngine.RenderDebugPrimitives ||
381  !physicsSystem.PhysicsEngine.Initialized ||
382  physicsSystem.PhysicsEngine.DebugGraphicsDevice == null ||
383  physicsSystem.PhysicsEngine.DebugEffect == null)
384  return;
385 
386  Matrix viewProj;
387  if (renderSystem.Pipeline.Parameters.ContainsKey(TransformationKeys.View) && renderSystem.Pipeline.Parameters.ContainsKey(TransformationKeys.Projection))
388  {
389  viewProj = renderSystem.Pipeline.Parameters.Get(TransformationKeys.View) * renderSystem.Pipeline.Parameters.Get(TransformationKeys.Projection);
390  }
391  else
392  {
393  return;
394  }
395 
396  var rasterizers = physicsSystem.PhysicsEngine.DebugGraphicsDevice.RasterizerStates;
397  physicsSystem.PhysicsEngine.DebugGraphicsDevice.SetRasterizerState(rasterizers.CullNone);
398 
399  foreach (var element in elements)
400  {
401  var shape = element.Shape.Shape;
402 
403  if (shape.Type == ColliderShapeTypes.Compound) //multiple shapes
404  {
405  DrawDebugCompound(ref viewProj, (CompoundColliderShape)shape, element);
406  }
407  else if (shape.Type != ColliderShapeTypes.StaticPlane) //a single shape
408  {
409  var physTrans = element.BoneIndex == -1 ? element.Collider.PhysicsWorldTransform : element.BoneWorldMatrix;
410 
411  //must account collider shape scaling
412  Matrix worldTrans;
413  Matrix.Multiply(ref element.Shape.Shape.DebugPrimitiveScaling, ref physTrans, out worldTrans);
414 
415  physicsSystem.PhysicsEngine.DebugEffect.WorldViewProj = worldTrans * viewProj;
416  physicsSystem.PhysicsEngine.DebugEffect.Color = element.Collider.IsActive ? Color.Green : Color.Red;
417  physicsSystem.PhysicsEngine.DebugEffect.UseUv = shape.Type != ColliderShapeTypes.ConvexHull;
418 
419  physicsSystem.PhysicsEngine.DebugEffect.Apply();
420 
421  shape.DebugPrimitive.Draw();
422  }
423  }
424 
425  physicsSystem.PhysicsEngine.DebugGraphicsDevice.SetRasterizerState(rasterizers.CullBack);
426  }
427 
428  public override void Update(GameTime time)
429  {
430  if (!physicsSystem.PhysicsEngine.Initialized) return;
431 
432  //Simulation processing is from here
433  physicsSystem.PhysicsEngine.Update((float)time.Elapsed.TotalSeconds);
434 
435  //characters need manual updating
436  foreach (var element in characters.Where(element => element.Collider.Enabled))
437  {
438  element.UpdateTransformationComponent(element.Collider.PhysicsWorldTransform);
439  }
440  }
441 
442  //public override void Draw(GameTime time)
443  //{
444  // if (!mPhysicsSystem.PhysicsEngine.Initialized) return;
445 
446  // //process all enabled elements
447  // foreach (var e in mElementsToUpdateDraw)
448  // {
449  // var collider = e.Collider;
450 
451  // var mesh = e.Data.ModelComponent;
452  // if (mesh == null) continue;
453 
454  // var nodeTransform = mesh.ModelViewHierarchy.NodeTransformations[e.BoneIndex];
455 
456  // Vector3 translation;
457  // Vector3 scale;
458  // Quaternion rotation;
459  // nodeTransform.WorldMatrix.Decompose(out scale, out rotation, out translation); //derive rot and translation, scale is ignored for now
460  // if (collider.UpdateTransformation(ref rotation, ref translation))
461  // {
462  // //true, Phys is the authority so we need to update the transformation
463  // TransformationComponent.CreateMatrixTRS(ref translation, ref rotation, ref scale, out nodeTransform.WorldMatrix);
464  // if (nodeTransform.ParentIndex != -1) //assuming -1 is root node
465  // {
466  // var parentWorld = mesh.ModelViewHierarchy.NodeTransformations[nodeTransform.ParentIndex];
467  // var inverseParent = parentWorld.WorldMatrix;
468  // inverseParent.Invert();
469  // nodeTransform.LocalMatrix = Matrix.Multiply(nodeTransform.WorldMatrix, inverseParent);
470  // }
471  // else
472  // {
473  // nodeTransform.LocalMatrix = nodeTransform.WorldMatrix;
474  // }
475  // }
476 
477  // e.BoneWorldMatrix = Matrix.AffineTransformation(1.0f, rotation, translation);
478 
479  // //update TRS
480  // nodeTransform.LocalMatrix.Decompose(out nodeTransform.Transform.Scaling, out nodeTransform.Transform.Rotation, out nodeTransform.Transform.Translation);
481 
482  // mesh.ModelViewHierarchy.NodeTransformations[e.BoneIndex] = nodeTransform; //its a struct so we need to copy back
483  // }
484  //}
485  }
486 }
Service providing method to access GraphicsDevice life-cycle.
List< PhysicsElement > Elements
Elements describing the physical colliders/rigidbodies/character of this entity Any runtime dynamic c...
Game entity. It usually aggregates multiple EntityComponent
Definition: Entity.cs:28
string LinkedBoneName
Gets or sets the link (usually a bone).
Defines Position, Rotation and Scale of its Entity.
override void OnEntityRemoved(Entity entity, AssociatedData data)
Entity processor, triggered on various EntitySystem events such as Entity and Component additions and...
Current timing used for variable-step (real time) or fixed-step (game time) games.
Definition: GameTime.cs:31
Thread-local storage context used during rendering.
Renders its RenderSystem.Pipeline, which will usually result in drawing all meshes, UI, etc...
Definition: RenderSystem.cs:18
CollisionFilterGroups1 CollisionGroup
Gets or sets the collision group.
CollisionFilterGroups CanCollideWith
Gets or sets the can collide with.
override AssociatedData GenerateAssociatedData(Entity entity)
Add a Model to an Entity, that will be used during rendering.
override void OnEntityAdding(Entity entity, AssociatedData data)
using SiliconStudio.Paradox. Physics
override void OnEnabledChanged(Entity entity, bool enabled)
TimeSpan Elapsed
Gets the elapsed game time since the last update
Definition: GameTime.cs:80
static readonly ParameterKey< Matrix > View
bool Sprite
Gets or sets a value indicating whether this PhysicsElement is representing a sprite.
static readonly ParameterKey< Matrix > Projection
static PropertyKey< TransformationComponent > Key
A class that represents a tag propety.
Definition: PropertyKey.cs:17
Represents a 4x4 mathematical matrix.
Definition: Matrix.cs:47