Fix bug in cached meshes where convex hull was missing. Previously if a mesh was copied from the cache it would not have its convex hull if the original mesh that was cached didn't previously compute it.

Fixes #2609

PiperOrigin-RevId: 756223337
Change-Id: I54705456636c270607a17340b249047e0df75d0b
This commit is contained in:
Kyle Bayes
2025-05-08 02:41:06 -07:00
committed by Copybara-Service
parent 6cfea71985
commit f4774a5449
6 changed files with 88 additions and 57 deletions
+3 -3
View File
@@ -102,7 +102,8 @@ bool mjCCache::Insert(const std::string& modelname, const mjResource *resource,
// populate data from the cache into the given function
// populate data from the cache into the given function, return true if data was
// copied
bool mjCCache::PopulateData(const mjResource* resource, mjCDataFunc fn) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = lookup_.find(resource->name);
@@ -121,8 +122,7 @@ bool mjCCache::PopulateData(const mjResource* resource, mjCDataFunc fn) {
entries_.erase(asset);
entries_.insert(asset);
asset->PopulateData(fn);
return true;
return asset->PopulateData(fn);
}
+4 -4
View File
@@ -28,7 +28,7 @@
#include <mujoco/mjplugin.h>
typedef std::function<void(const void*)> mjCDataFunc;
typedef std::function<bool(const void*)> mjCDataFunc;
typedef void (*mjCDeallocFunc)(const void*);
// A class container for a thread-safe asset cache
@@ -57,9 +57,9 @@ class mjCAsset {
std::size_t InsertNum() const { return insert_num_; }
std::size_t AccessCount() const { return access_count_; }
// pass data in the cache to the given function
void PopulateData(mjCDataFunc fn) const {
fn(data_.get());
// pass data in the cache to the given function, return true if data was copied
bool PopulateData(mjCDataFunc fn) const {
return fn(data_.get());
}
private:
+24 -41
View File
@@ -1012,30 +1012,28 @@ void mjCMesh::LoadOBJ(mjResource* resource, bool remove_repeated) {
// load mesh from cached asset, return true on success
bool mjCMesh::LoadCachedMesh(mjCCache *cache, const mjResource* resource) {
// save previous mesh properties (in case different from cached mesh)
int maxhullvert = maxhullvert_;
mjtMeshInertia old_inertia = inertia;
double old_scale[3] = {scale[0], scale[1], scale[2]};
auto process_mesh = [&](const void* data) {
const mjCMesh* mesh = static_cast<const mjCMesh*>(data);
// check if maxhullvert is different
maxhullvert_ = mesh->maxhullvert_;
if (maxhullvert != mesh->maxhullvert_) {
return;
if (maxhullvert_ != mesh->maxhullvert_) {
return false;
}
// check if inertia is different
inertia = mesh->inertia;
if (old_inertia != mesh->inertia) {
return;
if (inertia != mesh->inertia) {
return false;
}
// check if scale is different
memcpy(scale, mesh->scale, 3*sizeof(double));
if (old_scale[0] != mesh->scale[0] || old_scale[1] != mesh->scale[1] ||
old_scale[2] != mesh->scale[2]) {
return;
if (scale[0] != mesh->scale[0] ||
scale[1] != mesh->scale[1] ||
scale[2] != mesh->scale[2]) {
return false;
}
// check if need hull
if (needhull_ && !mesh->szgraph_) {
return false;
}
processed_ = mesh->processed_;
@@ -1047,11 +1045,14 @@ bool mjCMesh::LoadCachedMesh(mjCCache *cache, const mjResource* resource) {
facetexcoord_ = mesh->facetexcoord_;
halfedge_ = mesh->halfedge_;
szgraph_ = mesh->szgraph_;
graph_ = nullptr;
if (szgraph_) {
graph_ = (int*)mju_malloc(szgraph_*sizeof(int));
std::copy(mesh->graph_, mesh->graph_ + szgraph_, graph_);
// only copy graph if needed
if (needhull_ || mesh->face_.empty()) {
szgraph_ = mesh->szgraph_;
graph_ = nullptr;
if (szgraph_) {
graph_ = (int*)mju_malloc(szgraph_*sizeof(int));
std::copy(mesh->graph_, mesh->graph_ + szgraph_, graph_);
}
}
polygons_ = mesh->polygons_;
@@ -1072,29 +1073,11 @@ bool mjCMesh::LoadCachedMesh(mjCCache *cache, const mjResource* resource) {
}
tree_ = mesh->tree_;
face_aabb_ = mesh->face_aabb_;
return true;
};
// check that cached asset has all data, make sure no metadata has changed
if (!cache->PopulateData(resource, process_mesh)) {
return false;
}
if (maxhullvert != maxhullvert_) {
maxhullvert_ = maxhullvert;
return false;
}
if (inertia != old_inertia) {
inertia = old_inertia;
return false;
}
if (scale[0] != old_scale[0] || scale[1] != old_scale[1] ||
scale[2] != old_scale[2]) {
scale[0] = old_scale[0];
scale[1] = old_scale[1];
scale[2] = old_scale[2];
return false;
}
return true;
// check that cached asset has all data
return cache->PopulateData(resource, process_mesh);
}
// load STL binary mesh
+12 -7
View File
@@ -75,14 +75,19 @@ PNGImage PNGImage::Load(const mjCBase* obj, mjResource* resource,
image.color_type_ = color_type;
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_globalCache());
// cache callback
auto callback = [&image](const void* data) {
const PNGImage *cached_image = static_cast<const PNGImage*>(data);
if (cached_image->color_type_ == image.color_type_) {
image = *cached_image;
return true;
}
return false;
};
// try loading from cache
if (cache && cache->PopulateData(resource, [&image](const void* data) {
const PNGImage *cached_image = static_cast<const PNGImage*>(data);
if (cached_image->color_type_ == image.color_type_) {
image = *cached_image;
}
})) {
if (!image.data_.empty()) return image;
if (cache && cache->PopulateData(resource, callback)) {
return image;
}
// open PNG resource