Click or drag to resize
DigitalRuneFAQ
How can I define collision shapes for 3D models?

We are working on an editor to simplify and automate this process. Without an editor you can use one of following approaches.

  • The very lazy approach: Just use the triangle mesh. Of course, this is not so good for performance and simulation stability (convex shapes behave better).
  • Convex Hulls: Compute simplified convex hulls for the meshes at runtime or preferably in the XNA content pipeline.
  • Manually placing collision shapes in a 3d modeling tool of your choice. You can create a separate model that defines only the collision shapes. Or create the collision shapes in the same model and use a naming convention to identify collision shapes. Or use a custom export script. Etc.
  • Automatic Approximate Convex Decomposition of meshes using the ConvexDecomposition class.
  • Creating bounding shapes using methods of the GeometryHelper (e.g. CreateBoundingShape, ComputeBoundingBox, etc.).

The DigitalRune samples demonstrate several different ways to create shapes for models.

How can I get ALL ray hits of a ray and a triangle mesh?

Usually, the collision detection reports only a single contact between a RayShape and a TriangleMeshShape. This is by design. If the ray hits several features (triangles) of a single object, only the first hit is returned. (Note: RayShapeStopsAtFirstHit is only relevant if the ray hits several different objects.)

If you want to find all hit triangles, check out How can I efficiently perform a lot of ray casts against a single mesh?.

How can I efficiently perform a lot of ray casts against a single mesh?

Use a TriangleMeshShape with an AabbTreeT or a CompressedAabbTree for the static mesh. (You have to experiment which tree type is faster. The CompressedAabbTree can be slower than the AabbTreeT because it has to do a bit more computations, but it could also be faster because it uses less memory and generates less cache misses.)

Create a Ray in the local space of the mesh. Call TriangleMeshShape.Partition.GetOverlaps(Ray) to find all triangles which potentially overlap the ray. Use GeometryHelperGetContact to test the returned triangles vs. ray. The returned contacts are in the local space of the triangle mesh object. Don't forget to convert them back to world space if necessary.

Cannot convert TriangleMesh to DcelMesh

The winding order is relevant for the DCEL mesh. The DcelMeshFromTriangleMesh method currently assumes that all triangles in the mesh are connected. Unconnected submeshes are not allowed. Two triangles are connected if they share an edge and if the winding order matches. - In other words, the triangle (A, B, C) is connected to the triangle (C, B, D) but not to the triangle (B, C, D).

How can I convert an XNA Model to a TriangleMesh?

You can use the method TriangleMeshFromModel. This method works very similar to this code snippet:

C#
public static TriangleMesh FromModel(Model model)
{
  var triangleMesh = new TriangleMesh();

  foreach (var modelMesh in model.Meshes)
  {
    // Get bone transformation.
    Matrix transform = GetAbsoluteTransform(modelMesh.ParentBone);

    foreach (var modelMeshPart in modelMesh.MeshParts)
    {
      // Get vertex element info.
      var vertexDeclaration = modelMeshPart.VertexBuffer.VertexDeclaration;
      var vertexElements = vertexDeclaration.GetVertexElements();

      // Get the vertex positions.
      var positionElement = vertexElements.First(e => e.VertexElementUsage == VertexElementUsage.Position);
      if (positionElement.VertexElementFormat != VertexElementFormat.Vector3)
        throw new NotSupportedException("For vertex positions only VertexElementFormat.Vector3 is supported.");
      var positions = new Vector3[modelMeshPart.NumVertices];
      modelMeshPart.VertexBuffer.GetData(
        modelMeshPart.VertexOffset * vertexDeclaration.VertexStride + positionElement.Offset,
        positions,
        0,
        modelMeshPart.NumVertices,
        vertexDeclaration.VertexStride);

      // Apply bone transformation.
      for (int i = 0; i < positions.Length; i++)
        positions[i] = Vector3.Transform(positions[i], transform);

      // Get indices.
      var indexElementSize = (modelMeshPart.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) ? 2 : 4;
      if (indexElementSize != 2)
        throw new NotSupportedException("Only 16 bit indices are supported.");

      var indices = new short[modelMeshPart.PrimitiveCount * 3];
      modelMeshPart.IndexBuffer.GetData(
        modelMeshPart.StartIndex * 2,
        indices,
        0,
        modelMeshPart.PrimitiveCount * 3);

      // Remember the number of vertices already in the mesh.
      int vertexCount = triangleMesh.Vertices.Count;

      // Add the vertices of the current modelMeshPart.
      foreach (var p in positions)
        triangleMesh.Vertices.Add((Vector3F)p);

      // Add indices to triangle mesh.
      for (int i = 0; i < modelMeshPart.PrimitiveCount; i++)
      {
        // The three indices of the next triangle.
        // We add 'vertexCount' because the triangleMesh already contains other mesh parts.
        var i0 = indices[i * 3 + 0] + vertexCount;
        var i1 = indices[i * 3 + 1] + vertexCount;
        var i2 = indices[i * 3 + 2] + vertexCount;

        triangleMesh.Indices.Add(i0);
        triangleMesh.Indices.Add(i2);     // DigitalRune Geometry uses other winding order!
        triangleMesh.Indices.Add(i1);
      }
    }
  }

  return triangleMesh;
}


private static Matrix GetAbsoluteTransform(ModelBone bone)
{
  if (bone == null)
    return Matrix.Identity;
  return bone.Transform * GetAbsoluteTransform(bone.Parent);
}
Which spatial partition should I use?

Our spatial partitions are used in several places:

Recommendations:

For the collision detection broad phase use the SweepAndPruneSpaceT. Only if you make a lot of ray queries and/or use frustum culling use a DualPartitionT.

Start using these partitions. Once you have a representative level running in your game, you can test other partition types and see whether they perform better.

Note: Our library is very flexible. You can, for example, use an AdaptiveAabbTreeT as the collision detection broad phase, or the DualPartitionT for a triangle mesh. Though in a typical game these combinations will perform worse. For example:

C#
// This is the default:
_simulation.CollisionDomain.BroadPhase = new SweepAndPruneSpace<CollisionObject>();

// Here are other examples:
_simulation.CollisionDomain.BroadPhase = new DualPartition<CollisionObject>();
_simulation.CollisionDomain.BroadPhase = new DynamicAabbTree<CollisionObject>
{ EnableMotionPrediction = true, OptimizationPerFrame = 0.01f };
_simulation.CollisionDomain.BroadPhase = new AdaptiveAabbTree<CollisionObject>();
How can I manage dynamic objects in a spatial partition?

Use a DynamicAabbTreeT. If the AABB of an item in tree is changed at runtime, you must invalidate the tree:

C#
tree.Invalidate(indexOfChangedAabb);

In each frame call:

C#
tree.Update(false);

This rebuilds or incrementally optimizes the tree when needed. (It does nothing if no AABBs have changed and no objects were added or removed.) If you do not make this call, then this will be done automatically in GetOverlaps. But it is better to control manually when this happens.