Refactor user codebase to use LoadResource helper method.
* Fix small bug when registering resource providers. * Add test cases for OS filesystem fallback with VFS. * Rewrite LoadModelFromString using a resource provider (now can pass custom VFSs). PiperOrigin-RevId: 526959705 Change-Id: Iee1773e5584535d868aab6b206d1bf362e600c8a
This commit is contained in:
committed by
Copybara-Service
parent
c2115a1f51
commit
23961e24b7
@@ -588,7 +588,7 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!vfs_provider) {
|
||||
if (!vfs_provider && existing.prefix != nullptr) {
|
||||
int n = std::strlen(provider->prefix);
|
||||
int m = std::strlen(existing.prefix);
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mjplugin.h>
|
||||
#include "engine/engine_plugin.h"
|
||||
#include "engine/engine_util_errmem.h"
|
||||
|
||||
@@ -59,7 +61,9 @@ mjResource* mju_openResource(const char* name, int default_provider) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
mju_warning("mju_openResource: could not open resource '%s", name);
|
||||
mju_warning("mju_openResource: could not open resource '%s' "
|
||||
"using a resource provider matching prefix '%s'",
|
||||
name, provider->prefix);
|
||||
mju_free(resource->name);
|
||||
mju_free(resource);
|
||||
return NULL;
|
||||
@@ -69,7 +73,8 @@ mjResource* mju_openResource(const char* name, int default_provider) {
|
||||
if (default_provider > 0) {
|
||||
provider = mjp_getResourceProviderAtSlot(default_provider);
|
||||
if (provider == NULL) {
|
||||
mju_warning("mju_openResource: unknown resource provider");
|
||||
mju_warning("mju_openResource: unknown resource provider at slot %d",
|
||||
default_provider);
|
||||
mju_free(resource->name);
|
||||
mju_free(resource);
|
||||
return NULL;
|
||||
@@ -81,7 +86,9 @@ mjResource* mju_openResource(const char* name, int default_provider) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
mju_warning("mju_openResource: could not open resource '%s", name);
|
||||
mju_warning("mju_openResource: could not open resource '%s' "
|
||||
"with default provider at slot %d",
|
||||
name, default_provider);
|
||||
mju_free(resource->name);
|
||||
mju_free(resource);
|
||||
return NULL;
|
||||
|
||||
+64
-118
@@ -13,15 +13,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <csetjmp>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -189,15 +186,28 @@ void mjCMesh::Compile(int default_provider) {
|
||||
|
||||
// load STL, OBJ or MSH
|
||||
string ext = mjuu_getext(file);
|
||||
if (!strcasecmp(ext.c_str(), ".stl")) {
|
||||
LoadSTL(default_provider);
|
||||
} else if (!strcasecmp(ext.c_str(), ".obj")) {
|
||||
LoadOBJ(default_provider);
|
||||
} else if (!strcasecmp(ext.c_str(), ".msh")) {
|
||||
LoadMSH(default_provider);
|
||||
} else {
|
||||
if (strcasecmp(ext.c_str(), ".stl") &&
|
||||
strcasecmp(ext.c_str(), ".obj") &&
|
||||
strcasecmp(ext.c_str(), ".msh")) {
|
||||
throw mjCError(this, "Unknown mesh file type: %s", file.c_str());
|
||||
}
|
||||
|
||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
||||
mjResource* resource = LoadResource(filename, default_provider);
|
||||
|
||||
try {
|
||||
if (!strcasecmp(ext.c_str(), ".stl")) {
|
||||
LoadSTL(resource);
|
||||
} else if (!strcasecmp(ext.c_str(), ".obj")) {
|
||||
LoadOBJ(resource);
|
||||
} else {
|
||||
LoadMSH(resource);
|
||||
}
|
||||
mju_closeResource(resource);
|
||||
} catch (mjCError err) {
|
||||
mju_closeResource(resource);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// copy user vertex
|
||||
@@ -583,36 +593,20 @@ void mjCMesh::RemoveRepeated() {
|
||||
|
||||
|
||||
// load OBJ mesh
|
||||
void mjCMesh::LoadOBJ(int default_provider) {
|
||||
|
||||
// make filename
|
||||
string filename = mjuu_makefullname(
|
||||
model->modelfiledir, model->meshdir, file);
|
||||
mjResource* r = nullptr;
|
||||
|
||||
// try reading from default provider
|
||||
if ((r = mju_openResource(filename.c_str(), default_provider)) == nullptr) {
|
||||
// try reading from filesystem
|
||||
if (!default_provider || (r = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "could not open OBJ file '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void mjCMesh::LoadOBJ(mjResource* resource) {
|
||||
tinyobj::ObjReader objReader;
|
||||
const void* bytes = nullptr;
|
||||
int buffer_sz = mju_readResource(r, &bytes);
|
||||
int buffer_sz = mju_readResource(resource, &bytes);
|
||||
if (buffer_sz < 0) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "could not read OBJ file '%s'", filename.c_str());
|
||||
throw mjCError(this, "could not read OBJ file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// TODO(etom): support .mtl files?
|
||||
const char* buffer = (const char*) bytes;
|
||||
objReader.ParseFromString(std::string(buffer, buffer_sz), std::string());
|
||||
mju_closeResource(r);
|
||||
|
||||
if (!objReader.Valid()) {
|
||||
throw mjCError(this, "could not parse OBJ file '%s'", filename.c_str());
|
||||
throw mjCError(this, "could not parse OBJ file '%s'", resource->name);
|
||||
}
|
||||
|
||||
const auto& attrib = objReader.GetAttrib();
|
||||
@@ -631,7 +625,7 @@ void mjCMesh::LoadOBJ(int default_provider) {
|
||||
if (nfacevert < 3 || nfacevert > 4) {
|
||||
throw mjCError(
|
||||
this, "only tri or quad meshes are supported for OBJ (file '%s')",
|
||||
filename.c_str());
|
||||
resource->name);
|
||||
}
|
||||
|
||||
face_indices.push_back(mesh.indices[idx]);
|
||||
@@ -686,53 +680,38 @@ void mjCMesh::LoadOBJ(int default_provider) {
|
||||
|
||||
|
||||
// load STL binary mesh
|
||||
void mjCMesh::LoadSTL(int default_provider) {
|
||||
void mjCMesh::LoadSTL(mjResource* resource) {
|
||||
bool righthand = (scale[0]*scale[1]*scale[2]>0);
|
||||
|
||||
// make filename
|
||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
||||
mjResource* r = nullptr;
|
||||
if((r = mju_openResource(filename.c_str(), default_provider)) == nullptr) {
|
||||
if(!default_provider || (r = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "could not open STL file '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get file data in buffer
|
||||
char* buffer = 0;
|
||||
int buffer_sz = mju_readResource(r, (const void**) &buffer);
|
||||
int buffer_sz = mju_readResource(resource, (const void**) &buffer);
|
||||
|
||||
// still not found
|
||||
if (buffer_sz < 0) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "could not read STL file '%s'", filename.c_str());
|
||||
throw mjCError(this, "could not read STL file '%s'", resource->name);
|
||||
} else if (!buffer_sz) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "STL file '%s' is empty", filename.c_str());
|
||||
throw mjCError(this, "STL file '%s' is empty", resource->name);
|
||||
}
|
||||
|
||||
// make sure there is enough data for header
|
||||
if (buffer_sz<84) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "invalid header in STL file '%s'", filename.c_str());
|
||||
throw mjCError(this, "invalid header in STL file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// get number of triangles, check bounds
|
||||
nface = *(unsigned int*)(buffer+80);
|
||||
if (nface<1 || nface>200000) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this,
|
||||
"number of faces should be between 1 and 200000 in STL file '%s';"
|
||||
" perhaps this is an ASCII file?", filename.c_str());
|
||||
" perhaps this is an ASCII file?", resource->name);
|
||||
}
|
||||
|
||||
// check remaining buffer size
|
||||
if (nface*50 != buffer_sz-84) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this,
|
||||
"STL file '%s' has wrong size; perhaps this is an ASCII file?",
|
||||
filename.c_str());
|
||||
resource->name);
|
||||
}
|
||||
|
||||
// assign stl data pointer
|
||||
@@ -749,16 +728,14 @@ void mjCMesh::LoadSTL(int default_provider) {
|
||||
float* v = (float*)(stl+50*i+12*(j+1));
|
||||
for (int k=0; k < 3; k++) {
|
||||
if (std::isnan(v[k]) || std::isinf(v[k])) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "STL file '%s' contains invalid vertices.",
|
||||
filename.c_str());
|
||||
resource->name);
|
||||
}
|
||||
// check if vertex coordinates can be cast to an int safely
|
||||
if (fabs(v[k])>pow(2, 30)) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this,
|
||||
"vertex coordinates in STL file '%s' exceed maximum bounds",
|
||||
filename.c_str());
|
||||
resource->name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,44 +752,29 @@ void mjCMesh::LoadSTL(int default_provider) {
|
||||
}
|
||||
}
|
||||
|
||||
mju_closeResource(r);
|
||||
RemoveRepeated();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// load MSH binary mesh
|
||||
void mjCMesh::LoadMSH(int default_provider) {
|
||||
void mjCMesh::LoadMSH(mjResource* resource) {
|
||||
bool righthand = (scale[0]*scale[1]*scale[2]>0);
|
||||
|
||||
// make filename
|
||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
||||
|
||||
mjResource* r = nullptr;
|
||||
if((r = mju_openResource(filename.c_str(), default_provider)) == nullptr) {
|
||||
// fall back to OS filesystem
|
||||
if(!default_provider || (r = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "could not open MSH file '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// get file data in buffer
|
||||
char* buffer = 0;
|
||||
int buffer_sz = mju_readResource(r, (const void**) &buffer);
|
||||
int buffer_sz = mju_readResource(resource, (const void**) &buffer);
|
||||
|
||||
// still not found
|
||||
if (buffer_sz < 0) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "could not read MSH file '%s'", filename.c_str());
|
||||
throw mjCError(this, "could not read MSH file '%s'", resource->name);
|
||||
} else if (!buffer_sz) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "MSH file '%s' is empty", filename.c_str());
|
||||
throw mjCError(this, "MSH file '%s' is empty", resource->name);
|
||||
}
|
||||
|
||||
// make sure header is present
|
||||
if (buffer_sz<4*sizeof(int)) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "missing header in MSH file '%s'", filename.c_str());
|
||||
throw mjCError(this, "missing header in MSH file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// get sizes from header
|
||||
@@ -825,15 +787,13 @@ void mjCMesh::LoadMSH(int default_provider) {
|
||||
if (nvert<4 || nface<0 || nnormal<0 || ntexcoord<0 ||
|
||||
(nnormal>0 && nnormal!=nvert) ||
|
||||
(ntexcoord>0 && ntexcoord!=nvert)) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "invalid sizes in MSH file '%s'", filename.c_str());
|
||||
throw mjCError(this, "invalid sizes in MSH file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// check file size
|
||||
if (buffer_sz != 4*sizeof(int) + 3*nvert*sizeof(float) + 3*nnormal*sizeof(float) +
|
||||
2*ntexcoord*sizeof(float) + 3*nface*sizeof(int)) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "unexpected file size in MSH file '%s'", filename.c_str());
|
||||
throw mjCError(this, "unexpected file size in MSH file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// allocate and copy
|
||||
@@ -872,8 +832,6 @@ void mjCMesh::LoadMSH(int default_provider) {
|
||||
face[3*i+2] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
mju_closeResource(r);
|
||||
}
|
||||
|
||||
|
||||
@@ -1601,11 +1559,20 @@ void mjCSkin::Compile(int default_provider) {
|
||||
|
||||
// load SKN
|
||||
string ext = mjuu_getext(file);
|
||||
if (!strcasecmp(ext.c_str(), ".skn")) {
|
||||
LoadSKN(default_provider);
|
||||
} else {
|
||||
if (strcasecmp(ext.c_str(), ".skn")) {
|
||||
throw mjCError(this, "Unknown skin file type: %s", file.c_str());
|
||||
}
|
||||
|
||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
||||
mjResource* resource = LoadResource(filename, default_provider);
|
||||
|
||||
try {
|
||||
LoadSKN(resource);
|
||||
mju_closeResource(resource);
|
||||
} catch(mjCError err) {
|
||||
mju_closeResource(resource);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all data is present
|
||||
@@ -1724,33 +1691,19 @@ void mjCSkin::Compile(int default_provider) {
|
||||
|
||||
|
||||
// load skin in SKN BIN format
|
||||
void mjCSkin::LoadSKN(int default_provider) {
|
||||
// make filename
|
||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
||||
|
||||
// get file data in buffer
|
||||
mjResource* r = nullptr;
|
||||
if((r = mju_openResource(filename.c_str(), default_provider)) == nullptr) {
|
||||
if(!default_provider || (r = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "could not open SKN file '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void mjCSkin::LoadSKN(mjResource* resource) {
|
||||
char* buffer = 0;
|
||||
int buffer_sz = mju_readResource(r, (const void**) &buffer);
|
||||
int buffer_sz = mju_readResource(resource, (const void**) &buffer);
|
||||
|
||||
if (buffer_sz < 0) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "could not read SKN file '%s'", filename.c_str());
|
||||
throw mjCError(this, "could not read SKN file '%s'", resource->name);
|
||||
} else if (!buffer_sz) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "SKN file '%s' is empty", filename.c_str());
|
||||
throw mjCError(this, "SKN file '%s' is empty", resource->name);
|
||||
}
|
||||
|
||||
// make sure header is present
|
||||
if (buffer_sz<16) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "missing header in SKN file '%s'", filename.c_str());
|
||||
throw mjCError(this, "missing header in SKN file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// get sizes from header
|
||||
@@ -1761,14 +1714,12 @@ void mjCSkin::LoadSKN(int default_provider) {
|
||||
|
||||
// negative sizes not allowed
|
||||
if (nvert<0 || ntexcoord<0 || nface<0 || nbone<0) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "negative size in header of SKN file '%s'", filename.c_str());
|
||||
throw mjCError(this, "negative size in header of SKN file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// make sure we have data for vert, texcoord, face
|
||||
if (buffer_sz < 16 + 12*nvert + 8*ntexcoord + 12*nface) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "insufficient data in SKN file '%s'", filename.c_str());
|
||||
throw mjCError(this, "insufficient data in SKN file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// data pointer and counter
|
||||
@@ -1807,8 +1758,7 @@ void mjCSkin::LoadSKN(int default_provider) {
|
||||
for (int i=0; i<nbone; i++) {
|
||||
// check size
|
||||
if (buffer_sz/4-4-cnt < 18) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "insufficient data in SKN file '%s', bone %d", filename.c_str(), i);
|
||||
throw mjCError(this, "insufficient data in SKN file '%s', bone %d", resource->name, i);
|
||||
}
|
||||
|
||||
// read name
|
||||
@@ -1832,16 +1782,14 @@ void mjCSkin::LoadSKN(int default_provider) {
|
||||
|
||||
// check for negative
|
||||
if (vcount<1) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "vertex count must be positive in SKN file '%s', bone %d",
|
||||
filename.c_str(), i);
|
||||
resource->name, i);
|
||||
}
|
||||
|
||||
// check size
|
||||
if (buffer_sz/4-4-cnt < 2*vcount) {
|
||||
mju_closeResource(r);
|
||||
throw mjCError(this, "insufficient vertex data in SKN file '%s', bone %d",
|
||||
filename.c_str(), i);
|
||||
resource->name, i);
|
||||
}
|
||||
|
||||
// read vertid
|
||||
@@ -1855,10 +1803,8 @@ void mjCSkin::LoadSKN(int default_provider) {
|
||||
cnt += vcount;
|
||||
}
|
||||
|
||||
mju_closeResource(r);
|
||||
|
||||
// check final size
|
||||
if (buffer_sz != 16+4*cnt) {
|
||||
throw mjCError(this, "unexpected buffer size in SKN file '%s'", filename.c_str());
|
||||
throw mjCError(this, "unexpected buffer size in SKN file '%s'", resource->name);
|
||||
}
|
||||
}
|
||||
|
||||
+75
-84
@@ -466,6 +466,25 @@ mjCBase::mjCBase() {
|
||||
|
||||
|
||||
|
||||
// load resource if found (fallback to OS filesystem)
|
||||
mjResource* mjCBase::LoadResource(string filename, int provider) {
|
||||
mjResource* r = nullptr;
|
||||
const char* cname = filename.c_str();
|
||||
|
||||
// try reading from given provider
|
||||
if ((r = mju_openResource(cname, provider)) == nullptr) {
|
||||
if (!provider) {
|
||||
throw mjCError(0, "file not found: '%s'", cname);
|
||||
}
|
||||
// if provider wasn't the OS filesystem try to fallback to OS filesystem
|
||||
if ((r = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "resource not found via provider or OS filesystem: '%s'", cname);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
//------------------ class mjCBody implementation --------------------------------------------------
|
||||
|
||||
// constructor
|
||||
@@ -1978,28 +1997,20 @@ mjCHField::~mjCHField() {
|
||||
|
||||
|
||||
// load elevation data from custom format
|
||||
void mjCHField::LoadCustom(string filename, int default_provider) {
|
||||
void mjCHField::LoadCustom(mjResource* resource) {
|
||||
// get file data in buffer
|
||||
const void* buffer = 0;
|
||||
mjResource* resource = nullptr;
|
||||
|
||||
if ((resource = mju_openResource(filename.c_str(), default_provider)) == nullptr) {
|
||||
// default to OS filesystem
|
||||
if (!default_provider || (resource = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "could not open hfield file '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int buffer_sz = mju_readResource(resource, &buffer);
|
||||
|
||||
// still not found
|
||||
if (!buffer || buffer_sz < 1) {
|
||||
throw mjCError(this, "could not read hfield file '%s'", filename.c_str());
|
||||
if (buffer_sz < 1) {
|
||||
throw mjCError(this, "could not read hfield file '%s'", resource->name);
|
||||
} else if (!buffer_sz) {
|
||||
throw mjCError(this, "empty hfield file '%s'", resource->name);
|
||||
}
|
||||
|
||||
|
||||
if (buffer_sz < 2*sizeof(int)) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "hfield missing header '%s'", filename.c_str());
|
||||
throw mjCError(this, "hfield missing header '%s'", resource->name);
|
||||
}
|
||||
|
||||
// read dimensions
|
||||
@@ -2009,51 +2020,38 @@ void mjCHField::LoadCustom(string filename, int default_provider) {
|
||||
|
||||
// check dimensions
|
||||
if (nrow<1 || ncol<1) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "non-positive hfield dimensions in file '%s'", filename.c_str());
|
||||
throw mjCError(this, "non-positive hfield dimensions in file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// check buffer size
|
||||
if (buffer_sz != nrow*ncol*sizeof(float)+8) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "unexpected file size in file '%s'", filename.c_str());
|
||||
throw mjCError(this, "unexpected file size in file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// allocate
|
||||
data = (float*) mju_malloc(nrow*ncol*sizeof(float));
|
||||
if (!data) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "could not allocate buffers in hfield");
|
||||
}
|
||||
|
||||
// copy data
|
||||
memcpy(data, (void*)(pint+2), nrow*ncol*sizeof(float));
|
||||
|
||||
// close file
|
||||
mju_closeResource(resource);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// load elevation data from PNG format
|
||||
void mjCHField::LoadPNG(string filename, int default_provider) {
|
||||
void mjCHField::LoadPNG(mjResource* resource) {
|
||||
// determine data source
|
||||
const void* inbuffer = 0;
|
||||
mjResource* resource = nullptr;
|
||||
|
||||
if ((resource = mju_openResource(filename.c_str(), default_provider)) == nullptr) {
|
||||
// try reading from filesystem
|
||||
if (!default_provider || (resource = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "could not open PNG file '%s' %d", filename.c_str(), default_provider);
|
||||
}
|
||||
}
|
||||
|
||||
int inbuffer_sz = mju_readResource(resource, &inbuffer);
|
||||
|
||||
// still not found
|
||||
if (!inbuffer || inbuffer_sz < 1) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "could not read PNG file '%s'", filename.c_str());
|
||||
if (inbuffer_sz < 1) {
|
||||
throw mjCError(this, "could not read hfield PNG file '%s'", resource->name);
|
||||
}
|
||||
|
||||
if (!inbuffer_sz) {
|
||||
throw mjCError(this, "empty hfield PNG file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// load PNG from file or memory
|
||||
@@ -2063,18 +2061,15 @@ void mjCHField::LoadPNG(string filename, int default_provider) {
|
||||
|
||||
// check
|
||||
if (err) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "PNG load error '%s' in hfield id = %d", lodepng_error_text(err), id);
|
||||
}
|
||||
if (!w || !h) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "Zero dimension in PNG hfield '%s' (id = %d)", name.c_str(), id);
|
||||
throw mjCError(this, "Zero dimension in PNG hfield '%s' (id = %d)", resource->name, id);
|
||||
}
|
||||
|
||||
// allocate
|
||||
data = (float*) mju_malloc(w*h*sizeof(float));
|
||||
if (!data) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "could not allocate buffers in hfield");
|
||||
}
|
||||
|
||||
@@ -2086,7 +2081,6 @@ void mjCHField::LoadPNG(string filename, int default_provider) {
|
||||
data[c+(nrow-1-r)*ncol] = (float)image[c+r*ncol];
|
||||
}
|
||||
image.clear();
|
||||
mju_closeResource(resource);
|
||||
}
|
||||
|
||||
|
||||
@@ -2114,13 +2108,21 @@ void mjCHField::Compile(int default_provider) {
|
||||
|
||||
// make filename
|
||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
||||
mjResource* resource = LoadResource(filename, default_provider);
|
||||
|
||||
// load depending on format
|
||||
string ext = mjuu_getext(filename);
|
||||
if (!strcasecmp(ext.c_str(), ".png")) {
|
||||
LoadPNG(filename, default_provider);
|
||||
} else {
|
||||
LoadCustom(filename, default_provider);
|
||||
|
||||
try {
|
||||
if (!strcasecmp(ext.c_str(), ".png")) {
|
||||
LoadPNG(resource);
|
||||
} else {
|
||||
LoadCustom(resource);
|
||||
}
|
||||
mju_closeResource(resource);
|
||||
} catch(mjCError err) {
|
||||
mju_closeResource(resource);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2431,30 +2433,22 @@ void mjCTexture::BuiltinCube(void) {
|
||||
|
||||
|
||||
// load PNG file
|
||||
void mjCTexture::LoadPNG(string filename, int default_provider,
|
||||
void mjCTexture::LoadPNG(mjResource* resource,
|
||||
std::vector<unsigned char>& image,
|
||||
unsigned int& w, unsigned int& h) {
|
||||
const void* inbuffer = 0;
|
||||
mjResource* resource = nullptr;
|
||||
|
||||
if ((resource = mju_openResource(filename.c_str(), default_provider)) == nullptr) {
|
||||
// try reading from filesystem
|
||||
if (!default_provider || (resource = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "could not open PNG file '%s' %d", filename.c_str(), default_provider);
|
||||
}
|
||||
}
|
||||
|
||||
int inbuffer_sz = mju_readResource(resource, &inbuffer);
|
||||
|
||||
// still not found
|
||||
if (!inbuffer || inbuffer_sz < 1) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "could not read PNG file '%s'", filename.c_str());
|
||||
if (inbuffer_sz < 1) {
|
||||
throw mjCError(this, "could not read PNG texture file '%s'", resource->name);
|
||||
} else if (!inbuffer_sz) {
|
||||
throw mjCError(this, "PNG texture file '%s' is empty", resource->name);
|
||||
}
|
||||
|
||||
|
||||
// load PNG from file or memory
|
||||
unsigned int err = lodepng::decode(image, w, h, (const unsigned char*) inbuffer, inbuffer_sz, LCT_RGB, 8);
|
||||
mju_closeResource(resource);
|
||||
|
||||
// check
|
||||
if (err) {
|
||||
@@ -2462,34 +2456,27 @@ void mjCTexture::LoadPNG(string filename, int default_provider,
|
||||
"PNG file load error '%s' in texture id = %d", lodepng_error_text(err), id);
|
||||
}
|
||||
if (w<1 || h<1) {
|
||||
throw mjCError(this, "Empty PNG file in texture '%s' (id %d)", (const char*)file.c_str(), id);
|
||||
throw mjCError(this, "Empty PNG file in texture '%s' (id %d)", resource->name, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// load custom file
|
||||
void mjCTexture::LoadCustom(string filename, int default_provider,
|
||||
void mjCTexture::LoadCustom(mjResource* resource,
|
||||
std::vector<unsigned char>& image,
|
||||
unsigned int& w, unsigned int& h) {
|
||||
const void* buffer = 0;
|
||||
mjResource* resource = nullptr;
|
||||
|
||||
if ((resource = mju_openResource(filename.c_str(), default_provider)) == nullptr) {
|
||||
// default to OS filesystem
|
||||
if (!default_provider || (resource = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
||||
throw mjCError(this, "could not open texture file '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int buffer_sz = mju_readResource(resource, &buffer);
|
||||
|
||||
// still not found
|
||||
if (!buffer || buffer_sz < 0) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "could not read texture file '%s'", filename.c_str());
|
||||
if (buffer_sz < 0) {
|
||||
throw mjCError(this, "could not read texture file '%s'", resource->name);
|
||||
} else if (!buffer_sz) {
|
||||
throw mjCError(this, "texture file is empty: '%s'", resource->name);
|
||||
}
|
||||
|
||||
|
||||
// read dimensions
|
||||
int* pint = (int*)buffer;
|
||||
w = pint[0];
|
||||
@@ -2497,23 +2484,19 @@ void mjCTexture::LoadCustom(string filename, int default_provider,
|
||||
|
||||
// check dimensions
|
||||
if (w<1 || h<1) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "Non-PNG texture, assuming custom binary file format,\n"
|
||||
"non-positive texture dimensions in file '%s'", filename.c_str());
|
||||
"non-positive texture dimensions in file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// check buffer size
|
||||
if (buffer_sz != 2*sizeof(int) + w*h*3*sizeof(char)) {
|
||||
mju_closeResource(resource);
|
||||
throw mjCError(this, "Non-PNG texture, assuming custom binary file format,\n"
|
||||
"unexpected file size in file '%s'", filename.c_str());
|
||||
"unexpected file size in file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// allocate and copy
|
||||
image.resize(w*h*3);
|
||||
memcpy(image.data(), (void*)(pint+2), w*h*3*sizeof(char));
|
||||
|
||||
mju_closeResource(resource);
|
||||
}
|
||||
|
||||
|
||||
@@ -2524,10 +2507,18 @@ void mjCTexture::LoadFlip(string filename, int default_provider,
|
||||
unsigned int& w, unsigned int& h) {
|
||||
// dispatch to PNG or Custom loaded
|
||||
string ext = mjuu_getext(filename);
|
||||
if (!strcasecmp(ext.c_str(), ".png")) {
|
||||
LoadPNG(filename, default_provider, image, w, h);
|
||||
} else {
|
||||
LoadCustom(filename, default_provider, image, w, h);
|
||||
mjResource* resource = LoadResource(filename, default_provider);
|
||||
|
||||
try {
|
||||
if (!strcasecmp(ext.c_str(), ".png")) {
|
||||
LoadPNG(resource, image, w, h);
|
||||
} else {
|
||||
LoadCustom(resource, image, w, h);
|
||||
}
|
||||
mju_closeResource(resource);
|
||||
} catch(mjCError err) {
|
||||
mju_closeResource(resource);
|
||||
throw err;
|
||||
}
|
||||
|
||||
// horizontal flip
|
||||
|
||||
+43
-40
@@ -144,6 +144,9 @@ class mjCBase {
|
||||
friend class mjCDef;
|
||||
|
||||
public:
|
||||
// load resource if found (fallback to OS filesystem)
|
||||
mjResource* LoadResource(std::string filename, int provider);
|
||||
|
||||
std::string name; // object name
|
||||
std::string classname; // defaults class name
|
||||
int id; // object id
|
||||
@@ -513,9 +516,9 @@ class mjCMesh: public mjCBase {
|
||||
mjCMesh(mjCModel* = 0, mjCDef* = 0); // constructor
|
||||
~mjCMesh(); // destructor
|
||||
void Compile(int default_provider); // compiler
|
||||
void LoadOBJ(int default_provider); // load mesh in wavefront OBJ format
|
||||
void LoadSTL(int default_provider); // load mesh in STL BIN format
|
||||
void LoadMSH(int default_provider); // load mesh in MSH BIN format
|
||||
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 MakeGraph(void); // make graph of convex hull
|
||||
void CopyGraph(void); // copy graph into face data
|
||||
void MakeNormal(void); // compute vertex normals
|
||||
@@ -595,7 +598,7 @@ class mjCSkin: public mjCBase {
|
||||
mjCSkin(mjCModel* = 0); // constructor
|
||||
~mjCSkin(); // destructor
|
||||
void Compile(int default_provider); // compiler
|
||||
void LoadSKN(int default_provider); // load skin in SKN BIN format
|
||||
void LoadSKN(mjResource* resource); // load skin in SKN BIN format
|
||||
|
||||
int matid; // material id
|
||||
std::vector<int> bodyid; // body ids
|
||||
@@ -622,8 +625,8 @@ class mjCHField : public mjCBase {
|
||||
~mjCHField(); // destructor
|
||||
void Compile(int default_provider); // compiler
|
||||
|
||||
void LoadCustom(std::string filename, int default_provider); // load from custom format
|
||||
void LoadPNG(std::string filename, int default_provider); // load from PNG format
|
||||
void LoadCustom(mjResource* resource); // load from custom format
|
||||
void LoadPNG(mjResource* resource); // load from PNG format
|
||||
};
|
||||
|
||||
|
||||
@@ -676,10 +679,10 @@ class mjCTexture : public mjCBase {
|
||||
std::vector<unsigned char>& image,
|
||||
unsigned int& w, unsigned int& h);
|
||||
|
||||
void LoadPNG(std::string filename, int default_provider,
|
||||
void LoadPNG(mjResource* resource,
|
||||
std::vector<unsigned char>& image,
|
||||
unsigned int& w, unsigned int& h);
|
||||
void LoadCustom(std::string filename, int default_provider,
|
||||
void LoadCustom(mjResource* resource,
|
||||
std::vector<unsigned char>& image,
|
||||
unsigned int& w, unsigned int& h);
|
||||
|
||||
@@ -708,8 +711,8 @@ class mjCMaterial : public mjCBase {
|
||||
float rgba[4]; // rgba
|
||||
|
||||
private:
|
||||
mjCMaterial(mjCModel* = 0, mjCDef* = 0);// constructor
|
||||
void Compile(void); // compiler
|
||||
mjCMaterial(mjCModel* = 0, mjCDef* = 0); // constructor
|
||||
void Compile(void); // compiler
|
||||
|
||||
int texid; // id of material
|
||||
};
|
||||
@@ -742,8 +745,8 @@ class mjCPair : public mjCBase {
|
||||
}
|
||||
|
||||
private:
|
||||
mjCPair(mjCModel* = 0, mjCDef* = 0);// constructor
|
||||
void Compile(void); // compiler
|
||||
mjCPair(mjCModel* = 0, mjCDef* = 0); // constructor
|
||||
void Compile(void); // compiler
|
||||
|
||||
int geom1; // id of geom1
|
||||
int geom2; // id of geom2
|
||||
@@ -828,22 +831,22 @@ class mjCTendon : public mjCBase {
|
||||
mjCWrap* GetWrap(int); // pointer to wrap
|
||||
|
||||
// variables set by user
|
||||
int group; // group for visualization
|
||||
std::string material; // name of material for rendering
|
||||
int limited; // does tendon have limits: 0 false, 1 true, 2 auto
|
||||
double width; // width for rendering
|
||||
mjtNum solref_limit[mjNREF]; // solver reference: tendon limits
|
||||
mjtNum solimp_limit[mjNIMP]; // solver impedance: tendon limits
|
||||
mjtNum solref_friction[mjNREF]; // solver reference: tendon friction
|
||||
mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction
|
||||
double range[2]; // length limits
|
||||
double margin; // margin value for tendon limit detection
|
||||
double stiffness; // stiffness coefficient
|
||||
double damping; // damping coefficient
|
||||
double frictionloss; // friction loss
|
||||
double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring
|
||||
std::vector<double> userdata; // user data
|
||||
float rgba[4]; // rgba when material is omitted
|
||||
int group; // group for visualization
|
||||
std::string material; // name of material for rendering
|
||||
int limited; // does tendon have limits: 0 false, 1 true, 2 auto
|
||||
double width; // width for rendering
|
||||
mjtNum solref_limit[mjNREF]; // solver reference: tendon limits
|
||||
mjtNum solimp_limit[mjNIMP]; // solver impedance: tendon limits
|
||||
mjtNum solref_friction[mjNREF]; // solver reference: tendon friction
|
||||
mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction
|
||||
double range[2]; // length limits
|
||||
double margin; // margin value for tendon limit detection
|
||||
double stiffness; // stiffness coefficient
|
||||
double damping; // damping coefficient
|
||||
double frictionloss; // friction loss
|
||||
double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring
|
||||
std::vector<double> userdata; // user data
|
||||
float rgba[4]; // rgba when material is omitted
|
||||
|
||||
private:
|
||||
mjCTendon(mjCModel* = 0, mjCDef* = 0); // constructor
|
||||
@@ -934,8 +937,8 @@ class mjCActuator : public mjCBase {
|
||||
std::string refsite; // reference site, for site transmission only
|
||||
|
||||
private:
|
||||
mjCActuator(mjCModel* = 0, mjCDef* = 0);// constructor
|
||||
void Compile(void); // compiler
|
||||
mjCActuator(mjCModel* = 0, mjCDef* = 0); // constructor
|
||||
void Compile(void); // compiler
|
||||
|
||||
int trnid[2]; // id of transmission target
|
||||
};
|
||||
@@ -1046,18 +1049,18 @@ class mjCKey : public mjCBase {
|
||||
friend class mjXWriter;
|
||||
|
||||
public:
|
||||
double time; // time
|
||||
std::vector<double> qpos; // qpos
|
||||
std::vector<double> qvel; // qvel
|
||||
std::vector<double> act; // act
|
||||
std::vector<double> mpos; // mocap pos
|
||||
std::vector<double> mquat; // mocap quat
|
||||
std::vector<double> ctrl; // ctrl
|
||||
double time; // time
|
||||
std::vector<double> qpos; // qpos
|
||||
std::vector<double> qvel; // qvel
|
||||
std::vector<double> act; // act
|
||||
std::vector<double> mpos; // mocap pos
|
||||
std::vector<double> mquat; // mocap quat
|
||||
std::vector<double> ctrl; // ctrl
|
||||
|
||||
private:
|
||||
mjCKey(mjCModel*); // constructor
|
||||
~mjCKey(); // destructor
|
||||
void Compile(const mjModel* m); // compiler
|
||||
mjCKey(mjCModel*); // constructor
|
||||
~mjCKey(); // destructor
|
||||
void Compile(const mjModel* m); // compiler
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user