Refactor user cache to just support void pointers so user objects can be cached whole.

PiperOrigin-RevId: 651307384
Change-Id: I92c058a900727070235d00b6d2165c29fd12e7b0
This commit is contained in:
Kyle Bayes
2024-07-11 01:00:26 -07:00
committed by Copybara-Service
parent c145b79f6e
commit f9ab89b205
6 changed files with 372 additions and 664 deletions
+101 -102
View File
@@ -21,13 +21,13 @@
#include <cstring>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <mujoco/mjspec.h>
#include <mujoco/mujoco.h>
#include "user/user_api.h"
#ifdef MUJOCO_TINYOBJLOADER_IMPL
@@ -69,9 +69,6 @@ extern "C" {
#include "qhull_ra.h"
}
using std::string;
using std::vector;
// compute triangle area, surface normal, center
static double _triangle(double* normal, double* center,
const float* v1, const float* v2, const float* v3) {
@@ -337,7 +334,41 @@ void mjCMesh::LoadSDF() {
delete[] field;
}
void mjCMesh::CacheOBJ(mjCCache* cache, const mjResource* resource) {
if (cache == nullptr) return;
// cache mesh data into new mesh object
mjCMesh *mesh = new mjCMesh();
mesh->vert_ = vert_;
mesh->normal_ = normal_;
mesh->texcoord_ = texcoord_;
mesh->face_ = face_;
mesh->facetexcoord_ = facetexcoord_;
mesh->facenormal_ = facenormal_;
mesh->vertex_index_ = vertex_index_;
mesh->normal_index_ = normal_index_;
mesh->texcoord_index_ = texcoord_index_;
mesh->num_face_vertices_ = num_face_vertices_;
// calculate estimated size of mesh
std::size_t size = sizeof(mjCMesh)
+ (sizeof(float) * vert_.size())
+ (sizeof(float) * normal_.size())
+ (sizeof(float) * texcoord_.size())
+ (sizeof(int) * face_.size())
+ (sizeof(int) * facetexcoord_.size())
+ (sizeof(int) * facenormal_.size())
+ (sizeof(int) * vertex_index_.size())
+ (sizeof(int) * normal_index_.size())
+ (sizeof(int) * texcoord_index_.size())
+ (sizeof(unsigned char) * num_face_vertices_.size());
std::shared_ptr<const void> cached_data(mesh, +[](const void* data) {
const mjCMesh* mesh = static_cast<const mjCMesh*>(data);
delete mesh;
});
cache->Insert("", resource, cached_data, size);
}
// compiler
void mjCMesh::Compile(const mjVFS* vfs) {
@@ -368,14 +399,19 @@ void mjCMesh::Compile(const mjVFS* vfs) {
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
}
string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
mjResource* resource = LoadResource(filename, vfs);
try {
if (asset_type == "model/stl") {
LoadSTL(resource);
} else if (asset_type == "model/obj") {
LoadOBJ(resource);
// try loading from cache
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_globalCache());
if (!cache || !LoadCachedOBJ(cache, resource)) {
LoadOBJ(resource);
CacheOBJ(cache, resource);
}
} else {
LoadMSH(resource);
}
@@ -855,18 +891,6 @@ void mjCMesh::LoadOBJ(mjResource* resource) {
tinyobj::ObjReader objReader;
const void* bytes = nullptr;
// try loading from cache
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_globalCache());
if (cache) {
auto asset = cache->Get(resource->name);
if (asset.has_value() &&
!mju_isModifiedResource(resource, asset->Timestamp().c_str())) {
if (LoadCachedOBJ(asset.value())) {
return;
}
}
}
int buffer_sz = mju_readResource(resource, &bytes);
if (buffer_sz < 0) {
throw mjCError(this, "could not read OBJ file '%s'", resource->name);
@@ -933,108 +957,83 @@ void mjCMesh::LoadOBJ(mjResource* resource) {
texcoord_[2*i+1] = 1-texcoord_[2*i+1];
}
// try caching asset
if (cache) {
mjCAsset asset("", resource->name, resource->timestamp);
// save some partial data for caching
if (!objReader.GetShapes().empty()) {
const auto& mesh = objReader.GetShapes()[0].mesh;
num_face_vertices_ = mesh.num_face_vertices;
asset.AddVector("vert_", vert_);
asset.AddVector("normal_", normal_);
asset.AddVector("texcoord_", texcoord_);
vertex_index_.reserve(mesh.indices.size());
normal_index_.reserve(mesh.indices.size());
texcoord_index_.reserve(mesh.indices.size());
if (!objReader.GetShapes().empty()) {
const auto& mesh = objReader.GetShapes()[0].mesh;
std::vector<int> vertex_index;
vertex_index.reserve(mesh.indices.size());
std::vector<int> normal_index;
normal_index.reserve(mesh.indices.size());
std::vector<int> texcoord_index;
texcoord_index.reserve(mesh.indices.size());
for (tinyobj::index_t index : mesh.indices) {
vertex_index.push_back(index.vertex_index);
normal_index.push_back(index.normal_index);
texcoord_index.push_back(index.texcoord_index);
}
asset.AddVector("num_face_vertices", mesh.num_face_vertices);
asset.AddVector("vertex_index", vertex_index);
asset.AddVector("normal_index", normal_index);
asset.AddVector("texcoord_index", texcoord_index);
} else {
asset.AddVector("num_face_vertices", std::vector<unsigned char>());
asset.AddVector("vertex_index", std::vector<int>());
asset.AddVector("normal_index", std::vector<int>());
asset.AddVector("texcoord_index", std::vector<int>());
for (tinyobj::index_t index : mesh.indices) {
vertex_index_.push_back(index.vertex_index);
normal_index_.push_back(index.normal_index);
texcoord_index_.push_back(index.texcoord_index);
}
cache->Insert(std::move(asset));
}
}
// load OBJ from cached asset, return true on success
bool mjCMesh::LoadCachedOBJ(const mjCAsset& asset) {
bool mjCMesh::LoadCachedOBJ(mjCCache *cache, const mjResource* resource) {
// check that asset has all data
if (!asset.HasData("vert_") || !asset.HasData("normal_")
|| !asset.HasData("texcoord_") || !asset.HasData("num_face_vertices")
|| !asset.HasData("vertex_index") || !asset.HasData("normal_index")
|| !asset.HasData("texcoord_index")) {
if (!cache->PopulateData(resource, [&](const void* data) {
const mjCMesh* mesh = static_cast<const mjCMesh*>(data);
vert_ = mesh->vert_;
normal_ = mesh->normal_;
texcoord_ = mesh->texcoord_;
vertex_index_ = mesh->vertex_index_;
normal_index_ = mesh->normal_index_;
texcoord_index_ = mesh->texcoord_index_;
num_face_vertices_ = mesh->num_face_vertices_;
})) {
return false;
}
vert_ = asset.GetVector<float>("vert_").value();
normal_ = asset.GetVector<float>("normal_").value();
texcoord_ = asset.GetVector<float>("texcoord_").value();
vector<int> vertex_index = asset.GetVector<int>("vertex_index").value();
vector<int> normal_index = asset.GetVector<int>("normal_index").value();
vector<int> texcoord_index = asset.GetVector<int>("texcoord_index").value();
vector<unsigned char> num_face_vertices =
asset.GetVector<unsigned char>("num_face_vertices").value();
bool righthand = (scale[0] * scale[1] * scale[2]) > 0;
for (int face = 0, i = 0; i < vertex_index.size();) {
int nfacevert = num_face_vertices[face];
for (int face = 0, i = 0; i < vertex_index_.size();) {
int nfacevert = num_face_vertices_[face];
if (nfacevert < 3 || nfacevert > 4) {
throw mjCError(
this, "only tri or quad meshes are supported for OBJ (file '%s')",
asset.Id().c_str());
resource->name);
}
face_.push_back(vertex_index[i]);
face_.push_back(vertex_index[i + (righthand == 1 ? 1 : 2)]);
face_.push_back(vertex_index[i + (righthand == 1 ? 2 : 1)]);
face_.push_back(vertex_index_[i]);
face_.push_back(vertex_index_[i + (righthand == 1 ? 1 : 2)]);
face_.push_back(vertex_index_[i + (righthand == 1 ? 2 : 1)]);
if (!normal_.empty()) {
facenormal_.push_back(normal_index[i]);
facenormal_.push_back(normal_index[i + (righthand == 1 ? 1 : 2)]);
facenormal_.push_back(normal_index[i + (righthand == 1 ? 2 : 1)]);
facenormal_.push_back(normal_index_[i]);
facenormal_.push_back(normal_index_[i + (righthand == 1 ? 1 : 2)]);
facenormal_.push_back(normal_index_[i + (righthand == 1 ? 2 : 1)]);
}
if (!texcoord_.empty()) {
facetexcoord_.push_back(texcoord_index[i]);
facetexcoord_.push_back(texcoord_index[i + (righthand == 1 ? 1 : 2)]);
facetexcoord_.push_back(texcoord_index[i + (righthand == 1 ? 2 : 1)]);
facetexcoord_.push_back(texcoord_index_[i]);
facetexcoord_.push_back(texcoord_index_[i + (righthand == 1 ? 1 : 2)]);
facetexcoord_.push_back(texcoord_index_[i + (righthand == 1 ? 2 : 1)]);
}
if (nfacevert == 4) {
face_.push_back(vertex_index[i]);
face_.push_back(vertex_index[i + (righthand == 1 ? 2 : 3)]);
face_.push_back(vertex_index[i + (righthand == 1 ? 3 : 2)]);
face_.push_back(vertex_index_[i]);
face_.push_back(vertex_index_[i + (righthand == 1 ? 2 : 3)]);
face_.push_back(vertex_index_[i + (righthand == 1 ? 3 : 2)]);
if (!normal_.empty()) {
facenormal_.push_back(normal_index[i]);
facenormal_.push_back(normal_index[i + (righthand == 1 ? 1 : 2)]);
facenormal_.push_back(normal_index[i + (righthand == 1 ? 2 : 1)]);
facenormal_.push_back(normal_index_[i]);
facenormal_.push_back(normal_index_[i + (righthand == 1 ? 1 : 2)]);
facenormal_.push_back(normal_index_[i + (righthand == 1 ? 2 : 1)]);
}
if (!texcoord_.empty()) {
facetexcoord_.push_back(texcoord_index[i]);
facetexcoord_.push_back(texcoord_index[i + (righthand == 1 ? 1 : 2)]);
facetexcoord_.push_back(texcoord_index[i + (righthand == 1 ? 2 : 1)]);
facetexcoord_.push_back(texcoord_index_[i]);
facetexcoord_.push_back(texcoord_index_[i + (righthand == 1 ? 1 : 2)]);
facetexcoord_.push_back(texcoord_index_[i + (righthand == 1 ? 2 : 1)]);
}
}
i += nfacevert;
@@ -2086,12 +2085,12 @@ void mjCSkin::Compile(const mjVFS* vfs) {
}
// load SKN
string ext = mjuu_getext(file_);
std::string ext = mjuu_getext(file_);
if (strcasecmp(ext.c_str(), ".skn")) {
throw mjCError(this, "Unknown skin file type: %s", file_.c_str());
}
string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
mjResource* resource = LoadResource(filename, vfs);
try {
@@ -2152,7 +2151,7 @@ void mjCSkin::Compile(const mjVFS* vfs) {
}
// set total vertex weights to 0
vector<float> vw;
std::vector<float> vw;
size_t nvert = vert_.size()/3;
vw.resize(nvert);
fill(vw.begin(), vw.end(), 0.0f);
@@ -2520,7 +2519,7 @@ void mjCFlex::Compile(const mjVFS* vfs) {
// process elements
for (int e=0; e<(int)elem_.size()/(dim+1); e++) {
// make sorted copy of element
vector<int> el;
std::vector<int> el;
el.assign(elem_.begin()+e*(dim+1), elem_.begin()+(e+1)*(dim+1));
std::sort(el.begin(), el.end());
@@ -2555,7 +2554,7 @@ void mjCFlex::Compile(const mjVFS* vfs) {
}
// compute global vertex positions
vertxpos = vector<double> (3*nvert);
vertxpos = std::vector<double> (3*nvert);
for (int i=0; i < nvert; i++) {
// get body id, set vertxpos = body.xpos0
int b = rigid ? vertbodyid[0] : vertbodyid[i];
@@ -2702,17 +2701,17 @@ void mjCFlex::CreateBVH(void) {
// create shells and element-vertex collision pairs
void mjCFlex::CreateShellPair(void) {
vector<vector<int>> fragspec(nelem*(dim+1)); // [sorted frag vertices, elem, original frag vertices]
vector<vector<int>> connectspec; // [elem1, elem2, common sorted frag vertices]
vector<bool> border(nelem, false); // is element on the border
vector<bool> borderfrag(nelem*(dim+1), false); // is fragment on the border
std::vector<std::vector<int>> fragspec(nelem*(dim+1)); // [sorted frag vertices, elem, original frag vertices]
std::vector<std::vector<int>> connectspec; // [elem1, elem2, common sorted frag vertices]
std::vector<bool> border(nelem, false); // is element on the border
std::vector<bool> borderfrag(nelem*(dim+1), false); // is fragment on the border
// make fragspec
for (int e=0; e<nelem; e++) {
int n = e*(dim+1);
// element vertices in original (unsorted) order
vector<int> el;
std::vector<int> el;
el.assign(elem_.begin()+n, elem_.begin()+n+dim+1);
// line: 2 vertex fragments
@@ -2797,13 +2796,13 @@ void mjCFlex::CreateShellPair(void) {
int cnt = 1;
for (int n=1; n<nelem*(dim+1); n++) {
// extract frag vertices, without elem
vector<int> previous = {fragspec[n-1].begin(), fragspec[n-1].begin()+dim};
vector<int> current = {fragspec[n].begin(), fragspec[n].begin()+dim};
std::vector<int> previous = {fragspec[n-1].begin(), fragspec[n-1].begin()+dim};
std::vector<int> current = {fragspec[n].begin(), fragspec[n].begin()+dim};
// same sequential fragments
if (previous==current) {
// found pair of elements connected by common fragment
vector<int> connect;
std::vector<int> connect;
connect.insert(connect.end(), fragspec[n-1][dim]);
connect.insert(connect.end(), fragspec[n][dim]);
connect.insert(connect.end(), fragspec[n].begin(), fragspec[n].begin()+dim);
@@ -2843,10 +2842,10 @@ void mjCFlex::CreateShellPair(void) {
// compute elemlayer (distance from border) via value iteration in 3D
if (dim<3) {
elemlayer = vector<int> (nelem, 0);
elemlayer = std::vector<int> (nelem, 0);
}
else {
elemlayer = vector<int> (nelem, nelem+1); // init with greater than max value
elemlayer = std::vector<int> (nelem, nelem+1); // init with greater than max value
for (int e=0; e<nelem; e++) {
if (border[e]) {
elemlayer[e] = 0; // set border elements to 0
@@ -2878,7 +2877,7 @@ void mjCFlex::CreateShellPair(void) {
for (const auto& connect : connectspec) {
if (border[connect[0]] || border[connect[1]]) {
// extract common fragment
vector<int> frag = {connect.begin()+2, connect.end()};
std::vector<int> frag = {connect.begin()+2, connect.end()};
// process both elements
for (int ei=0; ei < 2; ei++) {