Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
94f32a9
Refactor MeshShape to use TriMesh internally, decouple from Assimp
jeongseok-meta Oct 25, 2025
3d120c8
Fix critical MeshShape bugs in constructor and setMesh
jeongseok-meta Nov 2, 2025
3b3101a
Add backward compatibility for external libraries using mMesh
jeongseok-meta Nov 2, 2025
d51c554
Enable running gazebo tests
jeongseok-meta Nov 2, 2025
960b04f
Fix build/tests after mesh_loader merge
jslee02 Dec 13, 2025
2cd16d8
Refactor mesh loading and keep Gazebo compatibility
jslee02 Dec 13, 2025
1f7b4b9
Merge remote-tracking branch 'origin/main' into refactor/mesh_loader
jslee02 Dec 13, 2025
ce115ec
CMake: find clang-format binary on macOS
jslee02 Dec 13, 2025
143ff19
CMake: chunk clang-format for format target
jslee02 Dec 13, 2025
9b180be
Gazebo: fix gz-physics common test gtest headers
jslee02 Dec 13, 2025
99ea569
Docs: normalize markdown emphasis
jslee02 Dec 13, 2025
203a9bc
Fix dynamics forward header collision
jslee02 Dec 14, 2025
4cfb31a
Merge origin/main into refactor/mesh_loader
jslee02 Dec 14, 2025
f56e099
Fix Windows/macOS build regressions
jslee02 Dec 14, 2025
30ec9ea
Merge remote-tracking branch 'origin/main' into refactor/mesh_loader
jslee02 Dec 14, 2025
9c5e017
MeshShape: fix cached scene adoption
jslee02 Dec 14, 2025
b669fbd
MeshLoader: merge multiple meshes
jslee02 Dec 15, 2025
20f5ede
Merge origin/main into refactor/mesh_loader
jslee02 Dec 15, 2025
eeb760a
MeshShape: fix ArrowShape rendering and tests
jslee02 Dec 17, 2025
64dd080
Merge remote-tracking branch 'origin/main' into refactor/mesh_loader
jslee02 Dec 17, 2025
27ceab2
Publish dartpy: stabilize linux Py314 wheels
jslee02 Dec 17, 2025
2b2754b
MeshShape: preserve materials for TriMesh URIs
jslee02 Dec 18, 2025
07d7e53
test-all: build examples and fix builds
jslee02 Dec 18, 2025
805fea5
examples: avoid deprecated math constants
jslee02 Dec 18, 2025
8788d2f
pixi: skip sccache when GHA cache disabled
jslee02 Dec 18, 2025
6348ee6
Merge origin/main
jslee02 Dec 18, 2025
a233f73
pixi: disable compiler cache without GHA
jslee02 Dec 18, 2025
fbb31f4
gui: fix ImGuiViewer setUpViewInWindow overload
jslee02 Dec 18, 2025
eecff5f
pixi: disable compiler cache in install config
jslee02 Dec 18, 2025
d0256d9
scripts: keep patch_gz_physics unchanged
jslee02 Dec 18, 2025
94fedae
wheel-build: disable sccache when GHA cache disabled
jslee02 Dec 18, 2025
a6bd99d
Merge origin/main into refactor/mesh_loader
jslee02 Dec 19, 2025
633884d
Add python binding for computeVertexNormals
jslee02 Dec 19, 2025
fb27303
Pass normals only when a full per-vertex buffer exists
jslee02 Dec 19, 2025
82417d8
Deprecate Assimp mesh APIs ahead of DART 8
jslee02 Dec 19, 2025
7bf0d12
Preserve submesh materials in TriMesh assimp conversion
jslee02 Dec 19, 2025
6b48151
Make submesh range helper a MeshShape member
jslee02 Dec 19, 2025
eca78d9
Suppress deprecated MeshShape API in tests
jslee02 Dec 19, 2025
4ad950f
Adjust MeshShape material index test for default material
jslee02 Dec 19, 2025
bf52b1c
ci: skip lint in macOS test-all
jslee02 Dec 19, 2025
df3b65d
ci: restore macOS lint in test-all
jslee02 Dec 19, 2025
662e043
math: avoid lazy Eigen lambdas in Random uniform
jslee02 Dec 19, 2025
4462ac7
Preserve texture URIs for mesh materials
jslee02 Dec 20, 2025
579b9df
Format code
jslee02 Dec 20, 2025
cd5c6f2
Preserve UVs when rebuilding mesh scenes
jslee02 Dec 20, 2025
7f809cb
Format code
jslee02 Dec 20, 2025
d7f3d0e
Adapt mesh parsers to shared TriMesh ownership
jslee02 Dec 20, 2025
b2b9d17
Merge branch 'main' into refactor/mesh_loader
jslee02 Dec 20, 2025
c243d39
Update MeshShapeNode material refresh
jslee02 Dec 20, 2025
dd5e3c2
Ensure ImGui import library on Windows
jslee02 Dec 20, 2025
110bf67
Skip GUI material update test in ASAN ImGui builds
jslee02 Dec 20, 2025
03d1c0f
Restore Bullet convex mesh detection for TriMesh
jslee02 Dec 20, 2025
181ad65
Fix mesh loading without retriever
jslee02 Dec 20, 2025
0d41e22
Update docs
jslee02 Dec 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/publish_dartpy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,16 @@ jobs:
uses: mozilla-actions/[email protected]
with:
disable_annotations: true
# Enable only when GitHub cache backend is available; otherwise skip.
use-gha-cache: ${{ env.ACTIONS_CACHE_URL != '' }}

- name: Configure environment for compiler cache
if: (matrix.skip-on-commit != true) || github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/v')
uses: ./.github/actions/configure-compiler-cache

- name: Build wheel
if: (matrix.skip-on-commit != true) || github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/v')
env:
# Work around occasional sccache spawn failures on ubuntu-latest + Python 3.14 wheel builds.
DART_DISABLE_COMPILER_CACHE: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == '314') && 'ON' || 'OFF' }}
run: pixi run -e py${{ matrix.python-version }}-wheel wheel-build

- name: Repair wheel
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This file is a pointer board for agents working in this repository. Keep it conc
## Read First

- Architectural, build, and workflow expectations live in `docs/onboarding` (start with `docs/onboarding/ci-cd.md` and `docs/onboarding/build-system.md`).
- The day-to-day pixi workflow (install, config, build, test) is documented in `docs/onboarding/building.md`.
- The day-to-day pixi workflow (install, config, build, lint, test) is documented in `docs/onboarding/building.md` and `docs/onboarding/testing.md`.
- Coding standards, formatting, and contribution flow are in `CONTRIBUTING.md`.
- Feature‑specific notes belong beside the code (e.g., README in the component directory) or in `docs/`.
- Unified model loading API (`dart::io`) is documented in `docs/onboarding/io-parsing.md`.
Expand Down
9 changes: 9 additions & 0 deletions cmake/DARTFindDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,15 @@ if(DART_BUILD_GUI)
${IMGUI_BACKEND_HEADERS}
)

if(WIN32 AND BUILD_SHARED_LIBS)
# ImGui does not export symbols by default; ensure an import library is
# generated so dart-gui can link against dart-imgui-lib on Windows.
set_target_properties(
${imgui_library_name}
PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON
)
endif()

# Configure include directories
# Build tree: use fetched source directory
# Install tree: use standard include paths (like system-installed imgui)
Expand Down
127 changes: 59 additions & 68 deletions dart/collision/bullet/BulletCollisionDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
#include <algorithm>
#include <vector>

#include <cmath>

namespace dart {
namespace collision {

Expand Down Expand Up @@ -91,17 +93,15 @@ std::unique_ptr<btCollisionShape> createBulletEllipsoidMesh(
std::unique_ptr<btCollisionShape> createBulletEllipsoidMultiSphere(
const Eigen::Vector3d& radii);

std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromAssimpScene(
const Eigen::Vector3d& scale, const aiScene* scene);

std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromAssimpMesh(
const aiMesh* mesh);
std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromTriMesh(
const Eigen::Vector3d& scale,
const std::shared_ptr<math::TriMesh<double>>& mesh);

template <typename HeightmapShapeT>
std::unique_ptr<BulletCollisionShape> createBulletCollisionShapeFromHeightmap(
const HeightmapShapeT* heightMap);

bool isConvex(const aiMesh* mesh, float threshold = 0.001);
bool isConvex(const math::TriMesh<double>& mesh, float threshold = 0.001f);

} // anonymous namespace

Expand Down Expand Up @@ -649,17 +649,19 @@ BulletCollisionDetector::createBulletCollisionShape(
std::move(bulletCollisionShape));
} else if (const auto shapeMesh = shape->as<MeshShape>()) {
const auto scale = shapeMesh->getScale();
const auto mesh = shapeMesh->getMesh();
const auto triMesh = shapeMesh->getTriMesh();

auto bulletCollisionShape
= createBulletCollisionShapeFromAssimpScene(scale, mesh);
= createBulletCollisionShapeFromTriMesh(scale, triMesh);

return std::make_unique<BulletCollisionShape>(
std::move(bulletCollisionShape));
} else if (const auto softMeshShape = shape->as<SoftMeshShape>()) {
const auto mesh = softMeshShape->getAssimpMesh();
const auto triMesh = softMeshShape->getTriMesh();
const Eigen::Vector3d scale = Eigen::Vector3d::Ones();

auto bulletCollisionShape = createBulletCollisionShapeFromAssimpMesh(mesh);
auto bulletCollisionShape
= createBulletCollisionShapeFromTriMesh(scale, triMesh);

return std::make_unique<BulletCollisionShape>(
std::move(bulletCollisionShape));
Expand Down Expand Up @@ -1015,62 +1017,40 @@ std::unique_ptr<btCollisionShape> createBulletEllipsoidMultiSphere(
}

//==============================================================================
std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromAssimpScene(
const Eigen::Vector3d& scale, const aiScene* scene)
std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromTriMesh(
const Eigen::Vector3d& scale,
const std::shared_ptr<math::TriMesh<double>>& mesh)
{
auto triMesh = new btTriangleMesh();

for (auto i = 0u; i < scene->mNumMeshes; ++i) {
for (auto j = 0u; j < scene->mMeshes[i]->mNumFaces; ++j) {
btVector3 vertices[3];
for (auto k = 0u; k < 3; ++k) {
const aiVector3D& vertex
= scene->mMeshes[i]
->mVertices[scene->mMeshes[i]->mFaces[j].mIndices[k]];
vertices[k] = btVector3(
vertex.x * scale[0], vertex.y * scale[1], vertex.z * scale[2]);
}
triMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
}
}
const bool makeConvexMesh
= scene->mNumMeshes == 1 && isConvex(scene->mMeshes[0]);
if (makeConvexMesh) {
auto convexMeshShape = std::make_unique<btConvexTriangleMeshShape>(triMesh);
convexMeshShape->setMargin(0.0f);
convexMeshShape->setUserPointer(triMesh);
return convexMeshShape;
} else {
auto gimpactMeshShape = std::make_unique<btGImpactMeshShape>(triMesh);
gimpactMeshShape->updateBound();
gimpactMeshShape->setUserPointer(triMesh);
return gimpactMeshShape;
if (!mesh) {
return nullptr;
}
}

//==============================================================================
std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromAssimpMesh(
const aiMesh* mesh)
{
auto triMesh = new btTriangleMesh();

for (auto i = 0u; i < mesh->mNumFaces; ++i) {
btVector3 vertices[3];
for (auto j = 0u; j < 3; ++j) {
const aiVector3D& vertex = mesh->mVertices[mesh->mFaces[i].mIndices[j]];
vertices[j] = btVector3(vertex.x, vertex.y, vertex.z);
const auto& vertices = mesh->getVertices();
const auto& triangles = mesh->getTriangles();

for (const auto& triangle : triangles) {
btVector3 btVertices[3];
for (auto k = 0u; k < 3; ++k) {
const auto& vertex = vertices[triangle[k]];
btVertices[k] = btVector3(
vertex.x() * scale[0], vertex.y() * scale[1], vertex.z() * scale[2]);
}
triMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
triMesh->addTriangle(btVertices[0], btVertices[1], btVertices[2]);
}

const bool makeConvexMesh = isConvex(mesh);
const bool makeConvexMesh = isConvex(*mesh);

if (makeConvexMesh) {
auto convexMeshShape = std::make_unique<btConvexTriangleMeshShape>(triMesh);
convexMeshShape->setMargin(0.0f);
convexMeshShape->setUserPointer(triMesh);
return convexMeshShape;
} else {
auto gimpactMeshShape = std::make_unique<btGImpactMeshShape>(triMesh);
gimpactMeshShape->updateBound();
gimpactMeshShape->setUserPointer(triMesh);
return gimpactMeshShape;
}
}
Expand Down Expand Up @@ -1144,43 +1124,54 @@ std::unique_ptr<BulletCollisionShape> createBulletCollisionShapeFromHeightmap(
}

//==============================================================================
bool isConvex(const aiMesh* mesh, float threshold)
bool isConvex(const math::TriMesh<double>& mesh, float threshold)
{
// Check whether all the other vertices on the mesh is on the internal side of
// the face, assuming that the direction of the normal of the face is pointing
// external side.
// Check whether all the other vertices on the mesh are on the internal side
// of the face, assuming that the direction of the normal of the face is
// pointing to the external side.
//
// Reference: https://stackoverflow.com/a/40056279/3122234
const auto& points = mesh.getVertices();
const auto& triangles = mesh.getTriangles();

if (points.empty() || triangles.empty()) {
return false;
}

const auto points = mesh->mVertices;
btVector3 vertices[3];
for (auto i = 0u; i < mesh->mNumFaces; ++i) {
for (const auto& triangle : triangles) {
for (auto j = 0u; j < 3; ++j) {
const aiVector3D& vertex = mesh->mVertices[mesh->mFaces[i].mIndices[j]];
vertices[j] = btVector3(vertex.x, vertex.y, vertex.z);
const auto& vertex = points[triangle[j]];
vertices[j] = btVector3(vertex.x(), vertex.y(), vertex.z());
}
const btVector3& A = vertices[0];
const btVector3 B = vertices[1] - A;
const btVector3 C = vertices[2] - A;

const btVector3 BCNorm = B.cross(C).normalized();
const btVector3 BCNormRaw = B.cross(C);
const btScalar normSquared = BCNormRaw.length2();
if (normSquared <= btScalar(0.0)) {
continue;
}
const btVector3 BCNorm = BCNormRaw / btSqrt(normSquared);

const float checkPoint
= btVector3(
points[0].x - A.x(), points[0].y - A.y(), points[0].z - A.z())
.dot(BCNorm);
const float checkPoint = btVector3(
points[0].x() - A.x(),
points[0].y() - A.y(),
points[0].z() - A.z())
.dot(BCNorm);

for (auto j = 0u; j < mesh->mNumVertices; ++j) {
float dist
= btVector3(
points[j].x - A.x(), points[j].y - A.y(), points[j].z - A.z())
for (const auto& point : points) {
const float dist
= btVector3(point.x() - A.x(), point.y() - A.y(), point.z() - A.z())
.dot(BCNorm);
if ((std::abs(checkPoint) > threshold) && (std::abs(dist) > threshold)
&& (checkPoint * dist < 0.0f)) {
return false;
}
}
}

return true;
}

Expand Down
2 changes: 0 additions & 2 deletions dart/collision/bullet/BulletCollisionDetector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
#include <dart/collision/bullet/BulletCollisionShape.hpp>
#include <dart/collision/bullet/BulletInclude.hpp>

#include <assimp/scene.h>

#include <unordered_map>
#include <vector>

Expand Down
34 changes: 2 additions & 32 deletions dart/collision/fcl/CollisionShapes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,43 +39,13 @@

#include <dart/math/Constants.hpp>

#include <assimp/scene.h>

#include <cmath>

namespace dart {
namespace collision {

template <class BV>
::fcl::BVHModel<BV>* createMesh(
double _scaleX,
double _scaleY,
double _scaleZ,
const aiScene* _mesh,
const dart::collision::fcl::Transform3& _transform)
{
DART_ASSERT(_mesh);
::fcl::BVHModel<BV>* model = new ::fcl::BVHModel<BV>;
model->beginModel();

for (unsigned int i = 0; i < _mesh->mNumMeshes; i++) {
for (unsigned int j = 0; j < _mesh->mMeshes[i]->mNumFaces; j++) {
dart::collision::fcl::Vector3 vertices[3];
for (unsigned int k = 0; k < 3; k++) {
const aiVector3D& vertex
= _mesh->mMeshes[i]
->mVertices[_mesh->mMeshes[i]->mFaces[j].mIndices[k]];
vertices[k] = dart::collision::fcl::Vector3(
vertex.x * _scaleX, vertex.y * _scaleY, vertex.z * _scaleZ);
vertices[k] = dart::collision::fcl::transform(_transform, vertices[k]);
}
model->addTriangle(vertices[0], vertices[1], vertices[2]);
}
}

model->endModel();
return model;
}
// Deprecated aiScene-based createMesh removed - use TriMesh-based alternatives
// in FCLCollisionDetector instead

template <class BV>
::fcl::BVHModel<BV>* createEllipsoid(
Expand Down
Loading
Loading