# How to Create Custom Shapes and Collision Geometries in DART: A Complete Guide

> Learn to create custom shapes and collision geometries in DART. This guide covers deriving from dart dynamics Shape and attaching to BodyNode for physics simulation.

- Repository: [DART: Dynamic Animation and Robotics Toolkit/dart](https://github.com/dartsim/dart)
- Tags: how-to-guide
- Published: 2026-02-28

---

**To create custom shapes and collision geometries in DART, derive a class from `dart::dynamics::Shape`, implement the pure virtual interface including `computeInertia()`, `clone()`, and bounding box updates, then attach it to a `BodyNode` via a `ShapeNode` with `CollisionAspect` for physics simulation.**

DART (Dynamic Animation and Robotics Toolkit) provides a flexible geometry system through the `dart::dynamics` namespace that allows developers to extend the simulator with arbitrary collision geometries. Whether you need a custom convex hull for collision detection or a novel visual primitive, understanding how to create custom shapes and collision geometries in DART is essential for advanced robotics simulation. The architecture separates visual representation from collision representation, enabling you to mix and match aspects as needed.

## Understanding DART's Shape Architecture

DART represents all geometry through the **`dart::dynamics::Shape`** class hierarchy defined in [`dart/dynamics/shape.hpp`](https://github.com/dartsim/dart/blob/main/dart/dynamics/shape.hpp). This abstract base class declares the contract that every geometry must satisfy, including type identification, inertia calculation, and spatial bounds.

The key components work together as follows:

- **`dart::dynamics::Shape`** – The abstract base in [`shape.hpp`](https://github.com/dartsim/dart/blob/main/shape.hpp) that defines pure virtual methods for `getType()`, `computeInertia()`, `clone()`, `updateBoundingBox()`, and `updateVolume()`.
- **Concrete implementations** – Classes like `SphereShape` and `BoxShape` in `dart/dynamics/` provide reference implementations of the interface.
- **`dart::dynamics::ShapeNode`** – Defined in [`shape_node.cpp`](https://github.com/dartsim/dart/blob/main/shape_node.cpp), this class binds a `Shape` to a `BodyNode` and manages **aspects**: `VisualAspect` for rendering, `CollisionAspect` for physics, and `DynamicsAspect` for mass properties.
- **FCL integration** – The FCL collision detector converts shapes into BVH models using utilities in [`dart/collision/fcl/collision_shapes.hpp`](https://github.com/dartsim/dart/blob/main/dart/collision/fcl/collision_shapes.hpp).

## Implementing a Custom Shape Class

To create custom shapes and collision geometries in DART, you must subclass `Shape` and implement five critical virtual methods. These methods enable DART to compute physical properties, perform broad-phase collision culling, and clone skeletons.

### Required Virtual Methods

Every custom shape must override:

- **`getType()`** – Returns a `std::string_view` identifier for debugging and serialization.
- **`computeInertia(double mass)`** – Returns an `Eigen::Matrix3d` representing the inertia tensor for the given mass.
- **`clone()`** – Returns a `std::shared_ptr<Shape>` deep copy for DART's skeleton cloning system.
- **`updateBoundingBox()`** – Populates `mBoundingBox` with axis-aligned bounds in the local frame.
- **`updateVolume()`** – Computes and caches the volume in `mVolume`.

### Complete Capped Cylinder Example

Below is a minimal implementation of a capped cylinder, demonstrating how to satisfy the `Shape` contract:

```cpp
// MyCappedCylinder.hpp
#pragma once

#include <dart/dynamics/Shape.hpp>
#include <Eigen/Dense>

class MyCappedCylinder : public dart::dynamics::Shape
{
public:
  MyCappedCylinder(double radius, double height)
    : Shape(SHAPE_TYPE::CYLINDER),  // reuse existing type enum
      mRadius(radius), mHeight(height) {}

  std::string_view getType() const override { 
    return "MyCappedCylinder"; 
  }

  Eigen::Matrix3d computeInertia(double mass) const override
  {
    // Solid cylinder inertia (simplified, caps omitted)
    double Ixx = 0.25 * mass * mRadius * mRadius
               + (1.0 / 12.0) * mass * mHeight * mHeight;
    double Iyy = Ixx;
    double Izz = 0.5 * mass * mRadius * mRadius;
    Eigen::Matrix3d I = Eigen::Matrix3d::Zero();
    I(0,0) = Ixx; I(1,1) = Iyy; I(2,2) = Izz;
    return I;
  }

  dart::dynamics::ShapePtr clone() const override
  {
    return std::make_shared<MyCappedCylinder>(mRadius, mHeight);
  }

protected:
  void updateBoundingBox() const override
  {
    mBoundingBox.setMin(Eigen::Vector3d(-mRadius, -mRadius, -mHeight/2));
    mBoundingBox.setMax(Eigen::Vector3d( mRadius,  mRadius,  mHeight/2));
    mIsBoundingBoxDirty = false;
  }

  void updateVolume() const override
  {
    mVolume = M_PI * mRadius * mRadius * mHeight;
    mIsVolumeDirty = false;
  }

private:
  double mRadius, mHeight;
};

```

The constructor initializes the base class with a `ShapeType` enum value. You can reuse an existing type like `CYLINDER` or add a new entry to [`shape.hpp`](https://github.com/dartsim/dart/blob/main/shape.hpp) if your geometry requires distinct handling.

## Attaching Custom Shapes to Body Nodes

Once implemented, attach your custom shape to a skeleton using **`BodyNode::createShapeNodeWith<Aspects...>`**. This template method instantiates a `ShapeNode` and attaches the specified aspects in a single call.

```cpp
#include <dart/dart.hpp>
#include "MyCappedCylinder.hpp"

int main()
{
  // Create skeleton and body
  auto skel = dart::dynamics::Skeleton::create("robot");
  auto body = skel->createBodyNode();

  // Instantiate custom shape
  auto myShape = std::make_shared<MyCappedCylinder>(0.3, 1.2);

  // Create ShapeNode with visual, collision, and dynamics aspects
  auto shapeNode = body->createShapeNodeWith<
      dart::dynamics::VisualAspect,
      dart::dynamics::CollisionAspect,
      dart::dynamics::DynamicsAspect>(myShape);

  // Configure visual properties
  shapeNode->getVisualAspect()->setColor({0.8, 0.2, 0.2, 1.0});
  
  return 0;
}

```

By including `CollisionAspect`, you enable physics simulation. DART automatically converts the shape for the FCL collision detector using the generic mesh pathway defined in [`dart/collision/fcl/collision_shapes.hpp`](https://github.com/dartsim/dart/blob/main/dart/collision/fcl/collision_shapes.hpp).

## Providing Custom Collision Geometries for FCL

When your visual geometry does not map cleanly to primitives, you can supply a custom FCL BVH model directly. This is necessary for complex convex hulls or procedurally generated collision meshes.

### Building FCL BVH Models

The utilities in [`collision_shapes.hpp`](https://github.com/dartsim/dart/blob/main/collision_shapes.hpp) demonstrate how to construct `fcl::BVHModel` instances programmatically. Adapt this pattern for custom convex hulls:

```cpp
#include <dart/collision/fcl/collision_shapes.hpp>
#include <fcl/geometry/bvh/bvh_model.h>

std::shared_ptr<::fcl::CollisionGeometryd> createCustomConvexHull(
    const std::vector<Eigen::Vector3d>& points,
    const dart::math::Isometry3d& transform)
{
  using BV = ::fcl::OBBRSSd;  // Bounding volume type
  auto model = std::make_shared<::fcl::BVHModel<BV>>();
  model->beginModel();

  // Convert points and add triangles
  for (size_t i = 0; i + 2 < points.size(); i += 3)
  {
    auto fclTransform = dart::collision::fcl::toFclTransform(transform);
    ::fcl::Vector3d p1 = fclTransform * ::fcl::Vector3d(points[i][0], points[i][1], points[i][2]);
    ::fcl::Vector3d p2 = fclTransform * ::fcl::Vector3d(points[i+1][0], points[i+1][1], points[i+1][2]);
    ::fcl::Vector3d p3 = fclTransform * ::fcl::Vector3d(points[i+2][0], points[i+2][1], points[i+2][2]);
    
    model->addTriangle(p1, p2, p3);
  }

  model->endModel();
  return model;
}

```

This follows the same implementation pattern as `createCube()` and `createEllipsoid()` in the DART source.

### Integrating with CollisionAspect

To use your custom FCL geometry, wrap it in a collision object and attach it to a body:

```cpp
auto customCollision = std::make_shared<dart::collision::FCLCollisionObject>(
    createCustomConvexHull(myPoints, dart::math::Isometry3d::Identity()));

body->createShapeNodeWith<dart::dynamics::CollisionAspect>(customCollision);

```

This approach bypasses the automatic shape-to-mesh conversion, giving you full control over the collision representation while maintaining compatibility with DART's simulation pipeline.

## Summary

- **Subclass `dart::dynamics::Shape`** and implement `getType()`, `computeInertia()`, `clone()`, `updateBoundingBox()`, and `updateVolume()` to define custom geometry.
- **Use `BodyNode::createShapeNodeWith<...>`** to attach shapes with `VisualAspect`, `CollisionAspect`, and `DynamicsAspect` as needed.
- **Leverage [`dart/collision/fcl/collision_shapes.hpp`](https://github.com/dartsim/dart/blob/main/dart/collision/fcl/collision_shapes.hpp)** for reference implementations of FCL BVH model creation when building custom collision geometries.
- **Provide `clone()` implementations** that return `std::shared_ptr<Shape>` to ensure skeleton copying works correctly throughout the framework.

## Frequently Asked Questions

### Do I need to modify DART's source code to add a custom shape?

No. You can subclass `dart::dynamics::Shape` in your own project headers without modifying `dartsim/dart`. Simply include `<dart/dynamics/Shape.hpp>` and link against DART. The dynamic nature of the `Shape` hierarchy allows runtime registration of your custom types.

### How does DART convert shapes for collision detection?

By default, DART uses the FCL collision detector. When you add a `CollisionAspect` to a `ShapeNode`, DART attempts to convert the shape into an FCL BVH model using converters in [`dart/collision/fcl/collision_shapes.hpp`](https://github.com/dartsim/dart/blob/main/dart/collision/fcl/collision_shapes.hpp). For standard types like spheres and boxes, it uses optimized FCL primitives. For meshes, it builds a BVH tree from the triangle data.

### Can I use a custom collision geometry without a visual representation?

Yes. Create a `ShapeNode` with only `CollisionAspect` specified in the template parameters: `body->createShapeNodeWith<dart::dynamics::CollisionAspect>(myCollisionShape)`. This creates an invisible collision geometry that participates in physics simulation but renders nothing, useful for simplified collision proxies.

### What is the difference between Shape and ShapeNode in DART?

A **`Shape`** represents the geometric data itself—vertices, inertia, and bounding volume. A **`ShapeNode`** is the scene graph attachment point that associates a `Shape` with a specific `BodyNode` and manages **aspects** (visual, collision, dynamics). Multiple `ShapeNode` instances can share the same `Shape` instance, allowing efficient memory use when identical geometries appear multiple times in a skeleton.