From 39896f8064f1ed58d4d0f59e8c819f9885953dec Mon Sep 17 00:00:00 2001 From: Kyle Bayes Date: Tue, 13 Aug 2024 06:39:49 -0700 Subject: [PATCH] Restructure mjCMesh for caching. PiperOrigin-RevId: 662496475 Change-Id: I4c3a58a63ef2c3b1196b3e6b30eb556b5528ff7f --- src/user/user_flexcomp.cc | 10 ++--- src/user/user_mesh.cc | 65 ++++++++++++++++---------------- src/user/user_model.cc | 14 +++---- src/user/user_objects.h | 72 +++++++++++++++++++----------------- src/xml/xml_native_writer.cc | 64 ++++++++++++++++---------------- 5 files changed, 114 insertions(+), 111 deletions(-) diff --git a/src/user/user_flexcomp.cc b/src/user/user_flexcomp.cc index 18f9dbdd..2243c1f5 100644 --- a/src/user/user_flexcomp.cc +++ b/src/user/user_flexcomp.cc @@ -932,13 +932,13 @@ bool mjCFlexcomp::MakeMesh(mjCModel* model, char* error, int error_sz) { // LoadOBJ uses userXXX, extra processing needed if (isobj) { // check sizes - if (mesh.vert_.empty() || mesh.face_.empty()) { + if (mesh.Vert().empty() || mesh.Face().empty()) { return comperr(error, "Vertex and face data required", error_sz); } - if (mesh.vert_.size()%3) { + if (mesh.Vert().size()%3) { return comperr(error, "Vertex data must be multiple of 3", error_sz); } - if (mesh.face_.size()%3) { + if (mesh.Face().size()%3) { return comperr(error, "Face data must be multiple of 3", error_sz); } @@ -947,12 +947,12 @@ bool mjCFlexcomp::MakeMesh(mjCModel* model, char* error, int error_sz) { } // copy faces - element = mesh.face_; + element = mesh.Face(); // copy vertices, convert from float to double point = vector (mesh.nvert()*3); for (int i=0; i < mesh.nvert()*3; i++) { - point[i] = (double) mesh.vert_[i]; + point[i] = (double) mesh.Vert(i); } return true; diff --git a/src/user/user_mesh.cc b/src/user/user_mesh.cc index b5e8e6f9..6a637f40 100644 --- a/src/user/user_mesh.cc +++ b/src/user/user_mesh.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -249,12 +250,6 @@ mjCMesh::~mjCMesh() { -void mjCMesh::set_needhull(bool needhull) { - needhull_ = needhull; -} - - - // generate mesh using marching cubes void mjCMesh::LoadSDF() { if (plugin_name.empty() && plugin_instance_name.empty()) { @@ -338,8 +333,10 @@ void mjCMesh::LoadSDF() { delete[] field; } -void mjCMesh::CacheOBJ(mjCCache* cache, const mjResource* resource) { +void mjCMesh::CacheMesh(mjCCache* cache, const mjResource* resource, + std::string_view asset_type) { if (cache == nullptr) return; + if (asset_type != "model/obj") return; // only OBJ files are cached // cache mesh data into new mesh object mjCMesh *mesh = new mjCMesh(); @@ -379,6 +376,8 @@ void mjCMesh::Compile(const mjVFS* vfs) { CopyFromSpec(); visual_ = true; std::string asset_type = GetAssetContentType(file_, content_type_); + mjResource* resource = nullptr; + mjCCache *cache = reinterpret_cast(mj_globalCache()); // load file if (!file_.empty()) { @@ -404,25 +403,30 @@ void mjCMesh::Compile(const mjVFS* vfs) { } std::string filename = mjuu_combinePaths(model->meshdir_, file_); - mjResource* resource = LoadResource(model->modelfiledir_, filename, vfs); + resource = LoadResource(model->modelfiledir_, filename, vfs); - try { - if (asset_type == "model/stl") { - LoadSTL(resource); - } else if (asset_type == "model/obj") { - // try loading from cache - mjCCache *cache = reinterpret_cast(mj_globalCache()); - if (!cache || !LoadCachedOBJ(cache, resource)) { + // try loading from cache + if (cache != nullptr && LoadCachedMesh(cache, resource)) { + mju_closeResource(resource); + resource = nullptr; + } + + if (resource != nullptr) { + try { + if (asset_type == "model/stl") { + LoadSTL(resource); + } else if (asset_type == "model/obj"){ LoadOBJ(resource); - CacheOBJ(cache, resource); + } else { + LoadMSH(resource); } - } else { - LoadMSH(resource); + } catch (mjCError err) { + mju_closeResource(resource); + throw err; } + + CacheMesh(cache, resource, asset_type); mju_closeResource(resource); - } catch (mjCError err) { - mju_closeResource(resource); - throw err; } // check repeated mesh data @@ -456,19 +460,16 @@ void mjCMesh::Compile(const mjVFS* vfs) { } else if (facetexcoord_.empty()) { facetexcoord_ = spec_facetexcoord_; } - } - - // create using marching cubes - else if (plugin.active) { - LoadSDF(); + } else if (plugin.active) { + LoadSDF(); // create using marching cubes } // check sizes - if (vert_.size()<12) throw mjCError(this, "at least 4 vertices required"); - if (vert_.size()%3) throw mjCError(this, "vertex data must be a multiple of 3"); - if (normal_.size()%3) throw mjCError(this, "normal data must be a multiple of 3"); - if (texcoord_.size()%2) throw mjCError(this, "texcoord must be a multiple of 2"); - if (face_.size()%3) throw mjCError(this, "face data must be a multiple of 3"); + if (vert_.size() < 12) throw mjCError(this, "at least 4 vertices required"); + if (vert_.size() % 3) throw mjCError(this, "vertex data must be a multiple of 3"); + if (normal_.size() % 3) throw mjCError(this, "normal data must be a multiple of 3"); + if (texcoord_.size() % 2) throw mjCError(this, "texcoord must be a multiple of 2"); + if (face_.size() % 3) throw mjCError(this, "face data must be a multiple of 3"); // check texcoord size if no face texcoord indices are given if (!texcoord_.empty() && texcoord_.size() != 2 * nvert() && @@ -981,7 +982,7 @@ void mjCMesh::LoadOBJ(mjResource* resource) { // load OBJ from cached asset, return true on success -bool mjCMesh::LoadCachedOBJ(mjCCache *cache, const mjResource* resource) { +bool mjCMesh::LoadCachedMesh(mjCCache *cache, const mjResource* resource) { // check that asset has all data if (!cache->PopulateData(resource, [&](const void* data) { const mjCMesh* mesh = static_cast(data); diff --git a/src/user/user_model.cc b/src/user/user_model.cc index e39e432b..f0d2810e 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -1310,7 +1310,7 @@ void mjCModel::SetDefaultNames(std::vector& assets) { for (int i=0; iCopyFromSpec(); if (assets[i]->name.empty()) { - stripped = mjuu_strippath(assets[i]->get_file()); + stripped = mjuu_strippath(assets[i]->File()); assets[i]->name = mjuu_stripext(stripped); names[assets[i]->name].push_back(i); } @@ -1365,8 +1365,8 @@ template static size_t getpathslength(std::vector list) { size_t result = 0; for (const auto& element : list) { - if (!element->get_file().empty()) { - result += element->get_file().length() + 1; + if (!element->File().empty()) { + result += element->File().length() + 1; } } @@ -1827,10 +1827,10 @@ template static int pathlist(vector& list, int adr, int* path_adr, char* paths) { for (unsigned int i = 0; i < list.size(); ++i) { path_adr[i] = -1; - if (!list[i] || list[i]->get_file().empty()) { + if (!list[i] || list[i]->File().empty()) { continue; } - adr = addtolist(list[i]->get_file(), adr, &path_adr[i], paths); + adr = addtolist(list[i]->File(), adr, &path_adr[i], paths); } return adr; @@ -2310,7 +2310,7 @@ void mjCModel::CopyObjects(mjModel* m) { m->mesh_graphadr[i] = (pme->szgraph() ? graph_adr : -1); m->mesh_bvhnum[i] = pme->tree().Nbvh(); m->mesh_bvhadr[i] = pme->tree().Nbvh() ? bvh_adr : -1; - mjuu_copyvec(&m->mesh_scale[3 * i], pme->get_scale(), 3); + mjuu_copyvec(&m->mesh_scale[3 * i], pme->Scale(), 3); mjuu_copyvec(&m->mesh_pos[3 * i], pme->GetOffsetPosPtr(), 3); mjuu_copyvec(&m->mesh_quat[4 * i], pme->GetOffsetQuatPtr(), 4); @@ -3466,7 +3466,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) { if (geoms_[i]->mesh && (geoms_[i]->spec.type==mjGEOM_MESH || geoms_[i]->spec.type==mjGEOM_SDF) && (geoms_[i]->spec.contype || geoms_[i]->spec.conaffinity)) { - geoms_[i]->mesh->set_needhull(true); + geoms_[i]->mesh->SetNeedHull(true); } } diff --git a/src/user/user_objects.h b/src/user/user_objects.h index b14b1b2a..42dff904 100644 --- a/src/user/user_objects.h +++ b/src/user/user_objects.h @@ -788,9 +788,6 @@ class mjCMesh_ : public mjCBase { }; class mjCMesh: public mjCMesh_, private mjsMesh { - friend class mjCModel; - friend class mjCFlexcomp; - friend class mjXWriter; public: mjCMesh(mjCModel* = nullptr, mjCDef* = nullptr); mjCMesh(const mjCMesh& other); @@ -804,20 +801,24 @@ class mjCMesh: public mjCMesh_, private mjsMesh { void CopyFromSpec(void); void PointToLocal(void); - // public getters and setters - const std::string& get_content_type() const { return content_type_; } - const std::string& get_file() const { return file_; } - const double* get_refpos() const { return refpos; } - const double* get_refquat() const { return refquat; } - const double* get_scale() const { return scale; } - bool get_smoothnormal() const { return smoothnormal; } - void set_needhull(bool needhull); + // accessors + const mjsPlugin& Plugin() const { return plugin; } + const std::string& ContentType() const { return content_type_; } + const std::string& File() const { return file_; } + const double* Refpos() const { return refpos; } + const double* Refquat() const { return refquat; } + const double* Scale() const { return scale; } + bool SmoothNormal() const { return smoothnormal; } + const std::vector& Vert() const { return vert_; } + float Vert(int i) const { return vert_[i]; } + const std::vector& UserVert() const { return spec_vert_; } + const std::vector& UserNormal() const { return spec_normal_; } + const std::vector& UserTexcoord() const { return spec_texcoord_; } + const std::vector& Face() const { return face_; } + const std::vector& UserFace() const { return spec_face_; } - // public getters for user data - const std::vector& get_uservert() const { return spec_vert_; } - const std::vector& get_usernormal() const { return spec_normal_; } - const std::vector& get_usertexcoord() const { return spec_texcoord_; } - const std::vector& get_userface() const { return spec_face_; } + // setters + void SetNeedHull(bool needhull) { needhull_ = needhull; } // mesh properties computed by Compile const double* aamm() const { return aamm_; } @@ -858,23 +859,27 @@ class mjCMesh: public mjCMesh_, private mjsMesh { // sets properties of a bounding volume given a face id void SetBoundingVolume(int faceid); + void LoadOBJ(mjResource* resource); // load mesh in wavefront OBJ format + void LoadSTL(mjResource* resource); // load mesh in STL BIN format + void LoadMSH(mjResource* resource); // load mesh in MSH BIN format + + void RemoveRepeated(); // remove repeated vertices + private: - void LoadOBJ(mjResource* resource); // load mesh in wavefront OBJ format - // load OBJ from cache asset, return true on success - bool LoadCachedOBJ(mjCCache *cache, const mjResource* resource); - // put OBJ into asset cache - void CacheOBJ(mjCCache *cache, const mjResource* resource); - void LoadSTL(mjResource* resource); // load mesh in STL BIN format - void LoadMSH(mjResource* resource); // load mesh in MSH BIN format + // load mesh from cache asset, return true on success (OBJ files are only supported) + bool LoadCachedMesh(mjCCache *cache, const mjResource* resource); + // store mesh into asset cache (OBJ files are only supported) + void CacheMesh(mjCCache *cache, const mjResource* resource, + std::string_view asset_type); + void LoadSDF(); // generate mesh using marching cubes - void MakeGraph(void); // make graph of convex hull - void CopyGraph(void); // copy graph into face data - void MakeNormal(void); // compute vertex normals - void MakeCenter(void); // compute face circumcircle data + void MakeGraph(); // make graph of convex hull + void CopyGraph(); // copy graph into face data + void MakeNormal(); // compute vertex normals + void MakeCenter(); // compute face circumcircle data void Process(); // compute inertial properties void ApplyTransformations(); // apply user transformations void ComputeFaceCentroid(double[3]); // compute centroid of all faces - void RemoveRepeated(void); // remove repeated vertices void CheckMesh(mjtGeomInertia type); // check if the mesh is valid // mesh data to be copied into mjModel @@ -940,7 +945,7 @@ class mjCSkin: public mjCSkin_, private mjsSkin { using mjCBase::name; using mjCBase::info; - const std::string& get_file() const { return file_; } + const std::string& File() const { return file_; } const std::string& get_material() const { return material_; } const std::vector& get_vert() const { return vert_; } const std::vector& get_texcoord() const { return texcoord_; } @@ -997,7 +1002,7 @@ class mjCHField : public mjCHField_, private mjsHField { void CopyFromSpec(void); void PointToLocal(void); - std::string get_file() const { return file_; } + std::string File() const { return file_; } // getter for user data std::vector& get_userdata() { return userdata_; } @@ -1044,7 +1049,7 @@ class mjCTexture : public mjCTexture_, private mjsTexture { void CopyFromSpec(void); void PointToLocal(void); - std::string get_file() const { return file_; } + std::string File() const { return file_; } std::string get_content_type() const { return content_type_; } std::vector get_cubefiles() const { return cubefiles_; } @@ -1194,13 +1199,12 @@ class mjCBodyPair : public mjCBodyPair_, private mjsExclude { std::string get_bodyname1() const { return bodyname1_; } std::string get_bodyname2() const { return bodyname2_; } - int GetSignature(void) { + int GetSignature() { return signature; } private: - - void Compile(void); // compiler + void Compile(); // compiler }; diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index 48274dfb..23c77c63 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -39,8 +39,6 @@ namespace { -using std::size_t; -using std::string; using tinyxml2::XMLComment; using tinyxml2::XMLDocument; using tinyxml2::XMLElement; @@ -64,7 +62,7 @@ class mj_XMLPrinter : public tinyxml2::XMLPrinter { // save XML file using custom 2-space indentation -static string WriteDoc(XMLDocument& doc, char *error, size_t error_sz) { +static std::string WriteDoc(XMLDocument& doc, char *error, size_t error_sz) { doc.ClearError(); mj_XMLPrinter stream(nullptr, /*compact=*/false); doc.Print(&stream); @@ -72,7 +70,7 @@ static string WriteDoc(XMLDocument& doc, char *error, size_t error_sz) { mjCopyError(error, doc.ErrorStr(), error_sz); return ""; } - std::string str = string(stream.CStr()); + std::string str = std::string(stream.CStr()); // top level sections std::array sections = { @@ -86,10 +84,10 @@ static string WriteDoc(XMLDocument& doc, char *error, size_t error_sz) { // insert newlines before section headers for (const std::string& section : sections) { - size_t pos = 0; + std::size_t pos = 0; while ((pos = str.find(section, pos)) != std::string::npos) { // find newline before this section - size_t line_pos = str.rfind('\n', pos); + std::size_t line_pos = str.rfind('\n', pos); // save position of first section if (line_pos < first_pos) first_pos = line_pos; @@ -127,7 +125,7 @@ XMLElement* mjXWriter::InsertEnd(XMLElement* parent, const char* name) { // write flex void mjXWriter::OneFlex(XMLElement* elem, const mjCFlex* pflex) { - string text; + std::string text; mjCFlex defflex; // common attributes @@ -195,7 +193,7 @@ void mjXWriter::OneFlex(XMLElement* elem, const mjCFlex* pflex) { // write mesh void mjXWriter::OneMesh(XMLElement* elem, const mjCMesh* pmesh, mjCDef* def) { - string text; + std::string text; // regular if (!writingdefaults) { @@ -203,60 +201,60 @@ void mjXWriter::OneMesh(XMLElement* elem, const mjCMesh* pmesh, mjCDef* def) { if (pmesh->classname != "main") { WriteAttrTxt(elem, "class", pmesh->classname); } - WriteAttrTxt(elem, "content_type", pmesh->get_content_type()); - WriteAttrTxt(elem, "file", pmesh->get_file()); + WriteAttrTxt(elem, "content_type", pmesh->ContentType()); + WriteAttrTxt(elem, "file", pmesh->File()); // write vertex data - if (!pmesh->get_uservert().empty()) { - text = VectorToString(pmesh->get_uservert()); + if (!pmesh->UserVert().empty()) { + text = VectorToString(pmesh->UserVert()); WriteAttrTxt(elem, "vertex", text); } // write normal data - if (!pmesh->get_usernormal().empty()) { - text = VectorToString(pmesh->get_usernormal()); + if (!pmesh->UserNormal().empty()) { + text = VectorToString(pmesh->UserNormal()); WriteAttrTxt(elem, "normal", text); } // write texcoord data - if (!pmesh->get_usertexcoord().empty()) { - text = VectorToString(pmesh->get_usertexcoord()); + if (!pmesh->UserTexcoord().empty()) { + text = VectorToString(pmesh->UserTexcoord()); WriteAttrTxt(elem, "texcoord", text); } // write face data - if (!pmesh->get_userface().empty()) { - text = VectorToString(pmesh->get_userface()); + if (!pmesh->UserFace().empty()) { + text = VectorToString(pmesh->UserFace()); WriteAttrTxt(elem, "face", text); } } // defaults and regular - WriteAttr(elem, "refpos", 3, pmesh->refpos, def->Mesh().refpos); - WriteAttr(elem, "refquat", 4, pmesh->refquat, def->Mesh().refquat); - WriteAttr(elem, "scale", 3, pmesh->scale, def->Mesh().scale); - WriteAttrKey(elem, "smoothnormal", bool_map, 2, pmesh->get_smoothnormal(), - def->Mesh().get_smoothnormal()); + WriteAttr(elem, "refpos", 3, pmesh->Refpos(), def->Mesh().Refpos()); + WriteAttr(elem, "refquat", 4, pmesh->Refquat(), def->Mesh().Refquat()); + WriteAttr(elem, "scale", 3, pmesh->Scale(), def->Mesh().Scale()); + WriteAttrKey(elem, "smoothnormal", bool_map, 2, pmesh->SmoothNormal(), + def->Mesh().SmoothNormal()); } // write skin void mjXWriter::OneSkin(XMLElement* elem, const mjCSkin* pskin) { - string text; + std::string text; mjCDef mydef; float zero = 0; // write attributes WriteAttrTxt(elem, "name", pskin->name); - WriteAttrTxt(elem, "file", pskin->get_file()); + WriteAttrTxt(elem, "file", pskin->File()); WriteAttrTxt(elem, "material", pskin->get_material()); WriteAttrInt(elem, "group", pskin->group, 0); WriteAttr(elem, "rgba", 4, pskin->rgba, mydef.Geom().rgba); WriteAttr(elem, "inflate", 1, &pskin->inflate, &zero); // write data if no file - if (pskin->get_file().empty()) { + if (pskin->File().empty()) { // mesh vert text = VectorToString(pskin->get_vert()); WriteAttrTxt(elem, "vertex", text); @@ -846,7 +844,7 @@ void mjXWriter::SetModel(const mjSpec* spec) { // save existing model in MJCF canonical format, must be compiled -string mjXWriter::Write(char *error, size_t error_sz) { +std::string mjXWriter::Write(char *error, size_t error_sz) { // check model if (!model || !model->IsCompiled()) { mjCopyError(error, "XML Write error: Only compiled model can be written", error_sz); @@ -862,7 +860,7 @@ string mjXWriter::Write(char *error, size_t error_sz) { doc.InsertFirstChild(root); // write comment if present - string text = mjs_getString(model->comment); + std::string text = mjs_getString(model->comment); if (!text.empty()) { XMLComment* comment = doc.NewComment(text.c_str()); root->LinkEndChild(comment); @@ -1456,7 +1454,7 @@ void mjXWriter::Asset(XMLElement* root) { else if (ptex->get_cubefiles()[0].empty() && ptex->get_cubefiles()[1].empty() && ptex->get_cubefiles()[2].empty() && ptex->get_cubefiles()[3].empty() && ptex->get_cubefiles()[4].empty() && ptex->get_cubefiles()[5].empty() && - ptex->get_file().empty() && ptex->gridsize[0] == 1 && ptex->gridsize[1] == 1) { + ptex->File().empty() && ptex->gridsize[0] == 1 && ptex->gridsize[1] == 1) { throw mjXError(0, "no support for buffer textures."); } @@ -1464,7 +1462,7 @@ void mjXWriter::Asset(XMLElement* root) { else { // write single file WriteAttrTxt(elem, "content_type", ptex->get_content_type()); - WriteAttrTxt(elem, "file", ptex->get_file()); + WriteAttrTxt(elem, "file", ptex->File()); // write separate files WriteAttrTxt(elem, "fileright", ptex->get_cubefiles()[0]); @@ -1501,10 +1499,10 @@ void mjXWriter::Asset(XMLElement* root) { for (int i=0; iGetObject(mjOBJ_MESH, i); - if (pmesh->plugin.active) { + if (pmesh->Plugin().active) { elem = InsertEnd(section, "mesh"); WriteAttrTxt(elem, "name", pmesh->name); - OnePlugin(InsertEnd(elem, "plugin"), &pmesh->plugin); + OnePlugin(InsertEnd(elem, "plugin"), &pmesh->Plugin()); } else{ elem = InsertEnd(section, "mesh"); OneMesh(elem, pmesh, model->def_map[pmesh->classname]); @@ -1527,7 +1525,7 @@ void mjXWriter::Asset(XMLElement* root) { WriteAttrInt(elem, "nrow", phf->nrow); WriteAttrInt(elem, "ncol", phf->ncol); if (!phf->get_userdata().empty()) { - string text; + std::string text; Vector2String(text, phf->get_userdata(), phf->ncol); WriteAttrTxt(elem, "elevation", text); }