Refactor VFS logic out of ResourceProvider plugin code.
PiperOrigin-RevId: 561302536 Change-Id: I952f9e76e85a7e3a111e0338f307378a6ff58197
This commit is contained in:
committed by
Copybara-Service
parent
215433f5f7
commit
f86e8b449f
+19
-28
@@ -655,28 +655,13 @@ struct mjLROpt_ { // options for mj_setLengthRange()
|
|||||||
mjtNum tolrange; // convergence tolerance (relative to range)
|
mjtNum tolrange; // convergence tolerance (relative to range)
|
||||||
};
|
};
|
||||||
typedef struct mjLROpt_ mjLROpt;
|
typedef struct mjLROpt_ mjLROpt;
|
||||||
struct mjVFS_ { // virtual file system for loading from memory
|
struct mjVFS_ { // virtual file system for loading from memory
|
||||||
int nfile; // number of files present
|
int nfile; // number of files present
|
||||||
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
|
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
|
||||||
int filesize[mjMAXVFS]; // file size in bytes
|
size_t filesize[mjMAXVFS]; // file size in bytes
|
||||||
void* filedata[mjMAXVFS]; // buffer with file data
|
void* filedata[mjMAXVFS]; // buffer with file data
|
||||||
};
|
};
|
||||||
typedef struct mjVFS_ mjVFS;
|
typedef struct mjVFS_ mjVFS;
|
||||||
struct mjResource_ {
|
|
||||||
char* name; // name of resource (filename, etc)
|
|
||||||
void* data; // opaque data pointer
|
|
||||||
const void* provider_data; // opaque resource provider data
|
|
||||||
|
|
||||||
// reading callback from resource provider
|
|
||||||
int (*read)(struct mjResource_* resource, const void** buffer);
|
|
||||||
|
|
||||||
// closing callback from resource provider
|
|
||||||
void (*close)(struct mjResource_* resource);
|
|
||||||
|
|
||||||
// getdir callback from resource provider
|
|
||||||
void (*getdir)(struct mjResource_* resource, const char** dir, int* ndir);
|
|
||||||
};
|
|
||||||
typedef struct mjResource_ mjResource;
|
|
||||||
struct mjOption_ { // physics options
|
struct mjOption_ { // physics options
|
||||||
// timing parameters
|
// timing parameters
|
||||||
mjtNum timestep; // timestep
|
mjtNum timestep; // timestep
|
||||||
@@ -1262,15 +1247,21 @@ struct mjModel_ {
|
|||||||
int* names_map; // internal hash map of names (nnames_map x 1)
|
int* names_map; // internal hash map of names (nnames_map x 1)
|
||||||
};
|
};
|
||||||
typedef struct mjModel_ mjModel;
|
typedef struct mjModel_ mjModel;
|
||||||
struct mjpResourceProvider_ {
|
struct mjResource_ {
|
||||||
const char* prefix; // prefix for match against a resource name
|
char* name; // name of resource (filename, etc)
|
||||||
mjfOpenResource open; // opening callback
|
void* data; // opaque data pointer
|
||||||
mjfReadResource read; // reading callback
|
const struct mjpResourceProvider* provider; // pointer to the provider
|
||||||
mjfCloseResource close; // closing callback
|
|
||||||
mjfGetResourceDir getdir; // getdir callback (optional)
|
|
||||||
void* data; // opaque data pointer (resource invariant)
|
|
||||||
};
|
};
|
||||||
typedef struct mjpResourceProvider_ mjpResourceProvider;
|
typedef struct mjResource_ mjResource;
|
||||||
|
struct mjpResourceProvider {
|
||||||
|
const char* prefix; // prefix for match against a resource name
|
||||||
|
mjfOpenResource open; // opening callback
|
||||||
|
mjfReadResource read; // reading callback
|
||||||
|
mjfCloseResource close; // closing callback
|
||||||
|
mjfGetResourceDir getdir; // get directory callback (optional)
|
||||||
|
void* data; // opaque data pointer (resource invariant)
|
||||||
|
};
|
||||||
|
typedef struct mjpResourceProvider mjpResourceProvider;
|
||||||
typedef enum mjtPluginCapabilityBit_ {
|
typedef enum mjtPluginCapabilityBit_ {
|
||||||
mjPLUGIN_ACTUATOR = 1<<0, // actuator forces
|
mjPLUGIN_ACTUATOR = 1<<0, // actuator forces
|
||||||
mjPLUGIN_SENSOR = 1<<1, // sensor measurements
|
mjPLUGIN_SENSOR = 1<<1, // sensor measurements
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
#include <mujoco/mjtnum.h>
|
#include <mujoco/mjtnum.h>
|
||||||
|
|
||||||
// global constants
|
// global constants
|
||||||
@@ -378,34 +379,14 @@ typedef struct mjLROpt_ mjLROpt;
|
|||||||
|
|
||||||
//---------------------------------- mjVFS ---------------------------------------------------------
|
//---------------------------------- mjVFS ---------------------------------------------------------
|
||||||
|
|
||||||
struct mjVFS_ { // virtual file system for loading from memory
|
struct mjVFS_ { // virtual file system for loading from memory
|
||||||
int nfile; // number of files present
|
int nfile; // number of files present
|
||||||
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
|
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
|
||||||
int filesize[mjMAXVFS]; // file size in bytes
|
size_t filesize[mjMAXVFS]; // file size in bytes
|
||||||
void* filedata[mjMAXVFS]; // buffer with file data
|
void* filedata[mjMAXVFS]; // buffer with file data
|
||||||
};
|
};
|
||||||
typedef struct mjVFS_ mjVFS;
|
typedef struct mjVFS_ mjVFS;
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------- mjResource ----------------------------------------------------
|
|
||||||
|
|
||||||
struct mjResource_ {
|
|
||||||
char* name; // name of resource (filename, etc)
|
|
||||||
void* data; // opaque data pointer
|
|
||||||
const void* provider_data; // opaque resource provider data
|
|
||||||
|
|
||||||
// reading callback from resource provider
|
|
||||||
int (*read)(struct mjResource_* resource, const void** buffer);
|
|
||||||
|
|
||||||
// closing callback from resource provider
|
|
||||||
void (*close)(struct mjResource_* resource);
|
|
||||||
|
|
||||||
// getdir callback from resource provider
|
|
||||||
void (*getdir)(struct mjResource_* resource, const char** dir, int* ndir);
|
|
||||||
};
|
|
||||||
typedef struct mjResource_ mjResource;
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------- mjOption ------------------------------------------------------
|
//---------------------------------- mjOption ------------------------------------------------------
|
||||||
|
|
||||||
struct mjOption_ { // physics options
|
struct mjOption_ { // physics options
|
||||||
|
|||||||
@@ -22,7 +22,12 @@
|
|||||||
|
|
||||||
//---------------------------------- Resource Provider ---------------------------------------------
|
//---------------------------------- Resource Provider ---------------------------------------------
|
||||||
|
|
||||||
#define mjVFS_PREFIX "vfs" // prefix for VFS providers
|
struct mjResource_ {
|
||||||
|
char* name; // name of resource (filename, etc)
|
||||||
|
void* data; // opaque data pointer
|
||||||
|
const struct mjpResourceProvider* provider; // pointer to the provider
|
||||||
|
};
|
||||||
|
typedef struct mjResource_ mjResource;
|
||||||
|
|
||||||
// callback for opeing a resource, returns zero on failure
|
// callback for opeing a resource, returns zero on failure
|
||||||
typedef int (*mjfOpenResource)(mjResource* resource);
|
typedef int (*mjfOpenResource)(mjResource* resource);
|
||||||
@@ -39,15 +44,15 @@ typedef void (*mjfCloseResource)(mjResource* resource);
|
|||||||
typedef void (*mjfGetResourceDir)(mjResource* resource, const char** dir, int* ndir);
|
typedef void (*mjfGetResourceDir)(mjResource* resource, const char** dir, int* ndir);
|
||||||
|
|
||||||
// struct describing a single resource provider
|
// struct describing a single resource provider
|
||||||
struct mjpResourceProvider_ {
|
struct mjpResourceProvider {
|
||||||
const char* prefix; // prefix for match against a resource name
|
const char* prefix; // prefix for match against a resource name
|
||||||
mjfOpenResource open; // opening callback
|
mjfOpenResource open; // opening callback
|
||||||
mjfReadResource read; // reading callback
|
mjfReadResource read; // reading callback
|
||||||
mjfCloseResource close; // closing callback
|
mjfCloseResource close; // closing callback
|
||||||
mjfGetResourceDir getdir; // getdir callback (optional)
|
mjfGetResourceDir getdir; // get directory callback (optional)
|
||||||
void* data; // opaque data pointer (resource invariant)
|
void* data; // opaque data pointer (resource invariant)
|
||||||
};
|
};
|
||||||
typedef struct mjpResourceProvider_ mjpResourceProvider;
|
typedef struct mjpResourceProvider mjpResourceProvider;
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------- Plugins -------------------------------------------------------
|
//---------------------------------- Plugins -------------------------------------------------------
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
|||||||
StructFieldDecl(
|
StructFieldDecl(
|
||||||
name='filesize',
|
name='filesize',
|
||||||
type=ArrayType(
|
type=ArrayType(
|
||||||
inner_type=ValueType(name='int'),
|
inner_type=ValueType(name='size_t'),
|
||||||
extents=(2000,),
|
extents=(2000,),
|
||||||
),
|
),
|
||||||
doc='file size in bytes',
|
doc='file size in bytes',
|
||||||
|
|||||||
+6
-24
@@ -661,7 +661,7 @@ void mj_saveModel(const mjModel* m, const char* filename, void* buffer, int buff
|
|||||||
|
|
||||||
|
|
||||||
// load model from binary MJB resource
|
// load model from binary MJB resource
|
||||||
static mjModel* _mj_loadModel(const char* filename, int vfs_provider) {
|
mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) {
|
||||||
int header[NHEADER] = {0};
|
int header[NHEADER] = {0};
|
||||||
int expected_header[NHEADER] = {ID, sizeof(mjtNum), getnint(), getnsize(), getnptr()};
|
int expected_header[NHEADER] = {ID, sizeof(mjtNum), getnint(), getnsize(), getnptr()};
|
||||||
int ints[256];
|
int ints[256];
|
||||||
@@ -670,8 +670,11 @@ static mjModel* _mj_loadModel(const char* filename, int vfs_provider) {
|
|||||||
mjModel *m = 0;
|
mjModel *m = 0;
|
||||||
mjResource* r = NULL;
|
mjResource* r = NULL;
|
||||||
|
|
||||||
if((r = mju_openResource(filename, vfs_provider)) == NULL) {
|
// first try vfs, otherwise try a provider or OS filesystem
|
||||||
return NULL;
|
if ((r = mju_openVfsResource(filename, vfs)) == NULL) {
|
||||||
|
if ((r = mju_openResource(filename)) == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* buffer = NULL;
|
const void* buffer = NULL;
|
||||||
@@ -785,27 +788,6 @@ static mjModel* _mj_loadModel(const char* filename, int vfs_provider) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// load model from binary MJB file
|
|
||||||
// if vfs is not NULL, look up file in vfs before reading from disk
|
|
||||||
mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) {
|
|
||||||
if (vfs == NULL) {
|
|
||||||
return _mj_loadModel(filename, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = mj_registerVfsProvider(vfs);
|
|
||||||
if (index < 1) {
|
|
||||||
mjERROR("could not allocate memory");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mjModel* model = _mj_loadModel(filename, index);
|
|
||||||
|
|
||||||
mjp_unregisterResourceProvider(index);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// de-allocate mjModel
|
// de-allocate mjModel
|
||||||
void mj_deleteModel(mjModel* m) {
|
void mj_deleteModel(mjModel* m) {
|
||||||
if (m) {
|
if (m) {
|
||||||
|
|||||||
+16
-102
@@ -24,6 +24,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -57,9 +58,6 @@ constexpr int kMaxAttributes = 255;
|
|||||||
|
|
||||||
constexpr int kCacheLine = 256;
|
constexpr int kCacheLine = 256;
|
||||||
|
|
||||||
// vfs prefix
|
|
||||||
constexpr const char* kVfsPrefix = mjVFS_PREFIX;
|
|
||||||
|
|
||||||
// A table of registered plugins, implemented as a linked list of array "blocks".
|
// A table of registered plugins, implemented as a linked list of array "blocks".
|
||||||
// This is a compromise that maintains a good degree of memory locality while not invalidating
|
// This is a compromise that maintains a good degree of memory locality while not invalidating
|
||||||
// existing pointers when growing the table. It is expected that for most users, the number of
|
// existing pointers when growing the table. It is expected that for most users, the number of
|
||||||
@@ -270,6 +268,7 @@ bool ResourceProvidersAreIdentical(const mjpResourceProvider* p1, const mjpResou
|
|||||||
p1->open == p2->open &&
|
p1->open == p2->open &&
|
||||||
p1->read == p2->read &&
|
p1->read == p2->read &&
|
||||||
p1->close == p2->close &&
|
p1->close == p2->close &&
|
||||||
|
p1->getdir == p2->getdir &&
|
||||||
p1->data == p2->data);
|
p1->data == p2->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,18 +531,7 @@ void mjp_defaultResourceProvider(mjpResourceProvider* provider) {
|
|||||||
|
|
||||||
// globally register a resource provider (thread-safe), return new slot id
|
// globally register a resource provider (thread-safe), return new slot id
|
||||||
int mjp_registerResourceProvider(const mjpResourceProvider* provider) {
|
int mjp_registerResourceProvider(const mjpResourceProvider* provider) {
|
||||||
// check against reserved prefixes
|
// check if prefix is valid URI scheme format
|
||||||
if (PrefixesAreIdentical(kVfsPrefix, provider->prefix)) {
|
|
||||||
mju_warning("provider->prefix is '%s' which is reserved", provider->prefix);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mjp_registerResourceProviderInternal(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal version of mjp_registerResourceProvider without prechecks on reserved prefixes
|
|
||||||
int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
|
||||||
// check if prefix is valid URI scheme format
|
|
||||||
if (!IsValidURISchemeFormat(provider->prefix)) {
|
if (!IsValidURISchemeFormat(provider->prefix)) {
|
||||||
mju_warning("provider->prefix is '%s' which is not a valid URI scheme format",
|
mju_warning("provider->prefix is '%s' which is not a valid URI scheme format",
|
||||||
provider->prefix);
|
provider->prefix);
|
||||||
@@ -562,35 +550,25 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
|||||||
// Do not handle objects with nontrivial destructors outside of this lambda.
|
// Do not handle objects with nontrivial destructors outside of this lambda.
|
||||||
// Do not call mju_error inside this lambda.
|
// Do not call mju_error inside this lambda.
|
||||||
int slot = [&]() -> int {
|
int slot = [&]() -> int {
|
||||||
bool vfs_provider = false;
|
|
||||||
std::unique_ptr<char[]> prefix;
|
std::unique_ptr<char[]> prefix;
|
||||||
|
|
||||||
// check if this is a VFS provider
|
|
||||||
if (PrefixesAreIdentical(mjVFS_PREFIX, provider->prefix)) {
|
|
||||||
vfs_provider = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy prefix
|
// copy prefix
|
||||||
if (!vfs_provider) {
|
prefix = CopyName(provider->prefix);
|
||||||
prefix = CopyName(provider->prefix);
|
if (!prefix) {
|
||||||
if (!prefix) {
|
if (strklen(provider->prefix) == -1) {
|
||||||
if (strklen(provider->prefix) == -1) {
|
std::snprintf(err, sizeof(err),
|
||||||
std::snprintf(err, sizeof(err),
|
"provider->prefix length exceeds the maximum limit of %d", kMaxNameLength);
|
||||||
"provider->prefix length exceeds the maximum limit of %d", kMaxNameLength);
|
} else {
|
||||||
} else {
|
std::snprintf(err, sizeof(err), "failed to allocate memory for resource provider prefix");
|
||||||
std::snprintf(err, sizeof(err), "failed to allocate memory for resource provider prefix");
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
|
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
|
||||||
auto lock = global.lock_mutex_exclusively();
|
auto lock = global.lock_mutex_exclusively();
|
||||||
int count = global.count().load(std::memory_order_acquire);
|
int count = global.count().load(std::memory_order_acquire);
|
||||||
int local_idx = 0;
|
int local_idx = 0;
|
||||||
int free_idx = -1, free_local_idx = -1;
|
|
||||||
PluginTable<mjpResourceProvider>* table = &global.table();
|
PluginTable<mjpResourceProvider>* table = &global.table();
|
||||||
PluginTable<mjpResourceProvider>* free_table = nullptr;
|
|
||||||
|
|
||||||
// check if a non-identical provider with the same name has already been registered
|
// check if a non-identical provider with the same name has already been registered
|
||||||
for (int i = 0; i < count; ++i, ++local_idx) {
|
for (int i = 0; i < count; ++i, ++local_idx) {
|
||||||
@@ -600,17 +578,7 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
|||||||
}
|
}
|
||||||
mjpResourceProvider& existing = table->plugins[local_idx];
|
mjpResourceProvider& existing = table->plugins[local_idx];
|
||||||
|
|
||||||
// VFS providers can safely go in open slots
|
if (existing.prefix != nullptr) {
|
||||||
if (vfs_provider && existing.prefix == nullptr && free_idx == -1) {
|
|
||||||
free_table = table;
|
|
||||||
free_idx = i;
|
|
||||||
free_local_idx = local_idx;
|
|
||||||
|
|
||||||
// can skip the rest
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!vfs_provider && existing.prefix != nullptr) {
|
|
||||||
// if identical then return slot number
|
// if identical then return slot number
|
||||||
if (PrefixesAreIdentical(provider->prefix, existing.prefix)) {
|
if (PrefixesAreIdentical(provider->prefix, existing.prefix)) {
|
||||||
if (ResourceProvidersAreIdentical(provider, &existing)) {
|
if (ResourceProvidersAreIdentical(provider, &existing)) {
|
||||||
@@ -626,7 +594,7 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allocate a new block of PluginTable if the last allocated block is full
|
// allocate a new block of PluginTable if the last allocated block is full
|
||||||
if (free_local_idx == -1 && local_idx == PluginTable<mjpResourceProvider>::kBlockSize) {
|
if (local_idx == PluginTable<mjpResourceProvider>::kBlockSize) {
|
||||||
local_idx = 0;
|
local_idx = 0;
|
||||||
table = AddNewTableBlock<mjpResourceProvider>(table);
|
table = AddNewTableBlock<mjpResourceProvider>(table);
|
||||||
if (!table) {
|
if (!table) {
|
||||||
@@ -636,19 +604,12 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
|||||||
|
|
||||||
// all checks passed, register the plugin into the global table
|
// all checks passed, register the plugin into the global table
|
||||||
mjpResourceProvider& registered_provider = table->plugins[local_idx];
|
mjpResourceProvider& registered_provider = table->plugins[local_idx];
|
||||||
if (free_local_idx != -1) {
|
|
||||||
registered_provider = free_table->plugins[free_local_idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
registered_provider = *provider;
|
registered_provider = *provider;
|
||||||
registered_provider.prefix = (!vfs_provider) ? prefix.release() : kVfsPrefix;
|
registered_provider.prefix = prefix.release();
|
||||||
|
|
||||||
// increment the global plugin count with a release memory barrier
|
// increment the global plugin count with a release memory barrier
|
||||||
if (free_idx == -1) {
|
global.count().store(count + 1, std::memory_order_release);
|
||||||
free_idx = count;
|
return count;
|
||||||
global.count().store(count + 1, std::memory_order_release);
|
|
||||||
}
|
|
||||||
return free_idx;
|
|
||||||
}();
|
}();
|
||||||
|
|
||||||
// ========= ATTENTION! ==========================================================================
|
// ========= ATTENTION! ==========================================================================
|
||||||
@@ -663,46 +624,6 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
|||||||
return slot+1;
|
return slot+1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// globally unregister resource provider (thread-safe)
|
|
||||||
// only used for VFS resource providers
|
|
||||||
void mjp_unregisterResourceProvider(int slot) {
|
|
||||||
// shift slot to zero-index
|
|
||||||
slot--;
|
|
||||||
|
|
||||||
if (slot < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get global table, acquire lock
|
|
||||||
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
|
|
||||||
auto lock = global.lock_mutex_exclusively();
|
|
||||||
int count = global.count().load(std::memory_order_acquire);
|
|
||||||
|
|
||||||
if (slot >= count) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginTable<mjpResourceProvider>* table = &global.table();
|
|
||||||
|
|
||||||
// iterate over blocks in the global table until the local index is less than the block size
|
|
||||||
int local_idx = slot;
|
|
||||||
while (local_idx >= PluginTable<mjpResourceProvider>::kBlockSize) {
|
|
||||||
local_idx -= PluginTable<mjpResourceProvider>::kBlockSize;
|
|
||||||
table = table->next;
|
|
||||||
if (!table) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// local_idx is now a valid index into the current block
|
|
||||||
mjpResourceProvider& provider = table->plugins[local_idx];
|
|
||||||
|
|
||||||
// no-op for anything other than VFS resource providers
|
|
||||||
if (provider.prefix == kVfsPrefix) {
|
|
||||||
provider.prefix = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the number of globally registered resource providers
|
// return the number of globally registered resource providers
|
||||||
int mjp_resourceProviderCount() {
|
int mjp_resourceProviderCount() {
|
||||||
return GetGlobal<mjpResourceProvider>().count().load(std::memory_order_acquire);
|
return GetGlobal<mjpResourceProvider>().count().load(std::memory_order_acquire);
|
||||||
@@ -728,12 +649,6 @@ const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// since multiple VFS resource providers can be registered with the same
|
|
||||||
// prefix, it doesn't make sense to try to match against them
|
|
||||||
if (PrefixesAreIdentical(kVfsPrefix, file_prefix.c_str())) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
|
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
|
||||||
auto lock = global.lock_mutex_exclusively();
|
auto lock = global.lock_mutex_exclusively();
|
||||||
PluginTable<mjpResourceProvider>* table = &global.table();
|
PluginTable<mjpResourceProvider>* table = &global.table();
|
||||||
@@ -743,7 +658,6 @@ const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name) {
|
|||||||
for (int i = 0;
|
for (int i = 0;
|
||||||
i < PluginTable<mjpPlugin>::kBlockSize && found_slot < count;
|
i < PluginTable<mjpPlugin>::kBlockSize && found_slot < count;
|
||||||
++i, ++found_slot) {
|
++i, ++found_slot) {
|
||||||
|
|
||||||
const mjpResourceProvider& provider = table->plugins[i];
|
const mjpResourceProvider& provider = table->plugins[i];
|
||||||
const char *prefix = provider.prefix;
|
const char *prefix = provider.prefix;
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ MJAPI int mjp_registerPlugin(const mjpPlugin* plugin);
|
|||||||
// globally register a resource provider (thread-safe), return new slot id
|
// globally register a resource provider (thread-safe), return new slot id
|
||||||
MJAPI int mjp_registerResourceProvider(const mjpResourceProvider* provider);
|
MJAPI int mjp_registerResourceProvider(const mjpResourceProvider* provider);
|
||||||
|
|
||||||
// globally unregister a resource provider (thread-safe)
|
|
||||||
MJAPI void mjp_unregisterResourceProvider(int slot);
|
|
||||||
|
|
||||||
// return the number of globally registered plugins
|
// return the number of globally registered plugins
|
||||||
MJAPI int mjp_pluginCount();
|
MJAPI int mjp_pluginCount();
|
||||||
|
|
||||||
|
|||||||
@@ -16,40 +16,23 @@
|
|||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <mujoco/mjmodel.h>
|
|
||||||
#include <mujoco/mjplugin.h>
|
#include <mujoco/mjplugin.h>
|
||||||
#include "engine/engine_plugin.h"
|
#include "engine/engine_plugin.h"
|
||||||
#include "engine/engine_util_errmem.h"
|
#include "engine/engine_util_errmem.h"
|
||||||
|
|
||||||
// file buffer used internally for the OS filesystem
|
// file buffer used internally for the OS filesystem
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* buffer;
|
uint8_t* buffer; // raw bytes from file
|
||||||
int nbuffer;
|
size_t nbuffer; // size of buffer in bytes
|
||||||
} file_buffer;
|
} file_buffer;
|
||||||
|
|
||||||
// helper function to fill data from resource provider into provider
|
|
||||||
static void fillResource(const mjpResourceProvider* provider, mjResource* resource) {
|
|
||||||
if (provider == NULL) {
|
|
||||||
resource->read = NULL;
|
|
||||||
resource->close = NULL;
|
|
||||||
resource->getdir = NULL;
|
|
||||||
resource->provider_data = NULL;
|
|
||||||
} else {
|
|
||||||
resource->read = provider->read;
|
|
||||||
resource->close = provider->close;
|
|
||||||
resource->getdir = provider->getdir;
|
|
||||||
resource->provider_data = provider->data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// open the given resource; if the name doesn't have a prefix matching with a
|
// open the given resource; if the name doesn't have a prefix matching with a
|
||||||
// resource provider, then the default_provider is used
|
// resource provider, then the OS filesystem is used
|
||||||
// if default_provider non-positive, then the OS filesystem is used
|
mjResource* mju_openResource(const char* name) {
|
||||||
mjResource* mju_openResource(const char* name, int default_provider) {
|
|
||||||
mjResource* resource = (mjResource*) mju_malloc(sizeof(mjResource));
|
mjResource* resource = (mjResource*) mju_malloc(sizeof(mjResource));
|
||||||
const mjpResourceProvider* provider = NULL;
|
const mjpResourceProvider* provider = NULL;
|
||||||
if (resource == NULL) {
|
if (resource == NULL) {
|
||||||
@@ -57,19 +40,22 @@ mjResource* mju_openResource(const char* name, int default_provider) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear out resource
|
||||||
|
memset(resource, 0, sizeof(mjResource));
|
||||||
|
|
||||||
// copy name
|
// copy name
|
||||||
resource->name = mju_malloc(sizeof(char) * (strlen(name) + 1));
|
resource->name = mju_malloc(sizeof(char) * (strlen(name) + 1));
|
||||||
if (resource->name == NULL) {
|
if (resource->name == NULL) {
|
||||||
mju_free(resource);
|
mju_closeResource(resource);
|
||||||
mjERROR("could not allocate memory");
|
mjERROR("could not allocate memory");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
strcpy(resource->name, name);
|
memcpy(resource->name, name, sizeof(char) * (strlen(name) + 1));
|
||||||
|
|
||||||
// find provider based off prefix of name
|
// find provider based off prefix of name
|
||||||
provider = mjp_getResourceProvider(name);
|
provider = mjp_getResourceProvider(name);
|
||||||
if (provider != NULL) {
|
if (provider != NULL) {
|
||||||
fillResource(provider, resource);
|
resource->provider = provider;
|
||||||
if (provider->open(resource)) {
|
if (provider->open(resource)) {
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
@@ -77,48 +63,19 @@ mjResource* mju_openResource(const char* name, int default_provider) {
|
|||||||
mju_warning("mju_openResource: could not open resource '%s' "
|
mju_warning("mju_openResource: could not open resource '%s' "
|
||||||
"using a resource provider matching prefix '%s'",
|
"using a resource provider matching prefix '%s'",
|
||||||
name, provider->prefix);
|
name, provider->prefix);
|
||||||
mju_free(resource->name);
|
mju_closeResource(resource);
|
||||||
mju_free(resource);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback to default provider
|
|
||||||
if (default_provider > 0) {
|
|
||||||
provider = mjp_getResourceProviderAtSlot(default_provider);
|
|
||||||
if (provider == NULL) {
|
|
||||||
mju_warning("mju_openResource: unknown resource provider at slot %d",
|
|
||||||
default_provider);
|
|
||||||
mju_free(resource->name);
|
|
||||||
mju_free(resource);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fillResource(provider, resource);
|
|
||||||
if (provider->open(resource)) {
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// lastly fallback to OS filesystem
|
// lastly fallback to OS filesystem
|
||||||
else {
|
resource->provider = NULL;
|
||||||
fillResource(NULL, resource);
|
resource->data = mju_malloc(sizeof(file_buffer));
|
||||||
resource->data = mju_malloc(sizeof(file_buffer));
|
file_buffer* fb = (file_buffer*) resource->data;
|
||||||
file_buffer* fb = (file_buffer*) resource->data;
|
fb->buffer = mju_fileToMemory(name, &(fb->nbuffer));
|
||||||
fb->buffer = mju_fileToMemory(name, &(fb->nbuffer));
|
if (fb->buffer == NULL) {
|
||||||
if (fb->buffer == NULL) {
|
mju_warning("mju_openResource: unknown file '%s'", name);
|
||||||
mju_warning("mju_openResource: unknown file '%s'", name);
|
mju_closeResource(resource);
|
||||||
mju_free(fb);
|
return NULL;
|
||||||
mju_free(resource->name);
|
|
||||||
mju_free(resource);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
@@ -131,20 +88,20 @@ void mju_closeResource(mjResource* resource) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// use the resource provider to close resource
|
// use the resource provider close callback
|
||||||
if (resource->close) {
|
if (resource->provider && resource->provider->close) {
|
||||||
resource->close(resource);
|
resource->provider->close(resource);
|
||||||
}
|
} else {
|
||||||
|
// clear OS filesystem if present
|
||||||
// if provider is NULL, then OS filesystem is used
|
|
||||||
else {
|
|
||||||
file_buffer* fb = (file_buffer*) resource->data;
|
file_buffer* fb = (file_buffer*) resource->data;
|
||||||
mju_free(fb->buffer);
|
if (fb) {
|
||||||
mju_free(fb);
|
if (fb->buffer) mju_free(fb->buffer);
|
||||||
|
mju_free(fb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// free name and resource
|
// free resource
|
||||||
mju_free(resource->name);
|
if (resource->name) mju_free(resource->name);
|
||||||
mju_free(resource);
|
mju_free(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,12 +114,12 @@ int mju_readResource(mjResource* resource, const void** buffer) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource->read) {
|
if (resource->provider) {
|
||||||
return resource->read(resource, buffer);
|
return resource->provider->read(resource, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// if provider read callback is NULL, then OS filesystem is used
|
// if provider is NULL, then OS filesystem is used
|
||||||
const file_buffer* fb = (file_buffer*) resource->data;
|
const file_buffer* fb = (file_buffer*) resource->data;
|
||||||
*buffer = fb->buffer;
|
*buffer = fb->buffer;
|
||||||
return fb->nbuffer;
|
return fb->nbuffer;
|
||||||
@@ -175,14 +132,14 @@ void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir) {
|
|||||||
*dir = NULL;
|
*dir = NULL;
|
||||||
*ndir = 0;
|
*ndir = 0;
|
||||||
|
|
||||||
if (!resource) {
|
if (resource == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// provider is not OS filesystem
|
// provider is not OS filesystem
|
||||||
if (resource->read) {
|
if (resource->provider) {
|
||||||
if (resource->getdir) {
|
if (resource->provider->getdir) {
|
||||||
resource->getdir(resource, dir, ndir);
|
resource->provider->getdir(resource, dir, ndir);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*dir = resource->name;
|
*dir = resource->name;
|
||||||
@@ -211,7 +168,7 @@ int mju_dirnamelen(const char* path) {
|
|||||||
|
|
||||||
|
|
||||||
// read file into memory buffer (allocated here with mju_malloc)
|
// read file into memory buffer (allocated here with mju_malloc)
|
||||||
void* mju_fileToMemory(const char* filename, int* filesize) {
|
void* mju_fileToMemory(const char* filename, size_t* filesize) {
|
||||||
// open file
|
// open file
|
||||||
*filesize = 0;
|
*filesize = 0;
|
||||||
FILE* fp = fopen(filename, "rb");
|
FILE* fp = fopen(filename, "rb");
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#ifndef MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_
|
#ifndef MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_
|
||||||
#define MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_
|
#define MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <mujoco/mjexport.h>
|
#include <mujoco/mjexport.h>
|
||||||
#include "engine/engine_plugin.h"
|
#include "engine/engine_plugin.h"
|
||||||
|
|
||||||
@@ -23,9 +25,8 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// open the given resource; if the name doesn't have a prefix matching with a
|
// open the given resource; if the name doesn't have a prefix matching with a
|
||||||
// resource provider, then the default_provider is used
|
// resource provider, then the OS filesystem is used
|
||||||
// if default_provider non-positive, then the OS filesystem is used
|
MJAPI mjResource* mju_openResource(const char* name);
|
||||||
MJAPI mjResource* mju_openResource(const char* name, int default_provider);
|
|
||||||
|
|
||||||
// close the given resource; no-op if resource is NULL
|
// close the given resource; no-op if resource is NULL
|
||||||
MJAPI void mju_closeResource(mjResource* resource);
|
MJAPI void mju_closeResource(mjResource* resource);
|
||||||
@@ -37,11 +38,15 @@ MJAPI int mju_readResource(mjResource* resource, const void** buffer);
|
|||||||
// sets for a resource with a name partitioned as {dir}{filename}, the dir and ndir pointers
|
// sets for a resource with a name partitioned as {dir}{filename}, the dir and ndir pointers
|
||||||
MJAPI void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir);
|
MJAPI void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir);
|
||||||
|
|
||||||
|
// Returns > 0 if resource has been modified since last read, 0 if not, and < 0
|
||||||
|
// if inconclusive
|
||||||
|
MJAPI int mju_isModifiedResource(const mjResource* resource);
|
||||||
|
|
||||||
// get the length of the dirname portion of a given path
|
// get the length of the dirname portion of a given path
|
||||||
int mju_dirnamelen(const char* path);
|
int mju_dirnamelen(const char* path);
|
||||||
|
|
||||||
// read file into memory buffer (allocated here with mju_malloc)
|
// read file into memory buffer (allocated here with mju_malloc)
|
||||||
void* mju_fileToMemory(const char* filename, int* filesize);
|
void* mju_fileToMemory(const char* filename, size_t* filesize);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-16
@@ -14,10 +14,10 @@
|
|||||||
|
|
||||||
#include "engine/engine_vfs.h"
|
#include "engine/engine_vfs.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "engine/engine_array_safety.h"
|
#include "engine/engine_array_safety.h"
|
||||||
#include "engine/engine_plugin.h"
|
|
||||||
#include "engine/engine_resource.h"
|
#include "engine/engine_resource.h"
|
||||||
#include "engine/engine_util_errmem.h"
|
#include "engine/engine_util_errmem.h"
|
||||||
#include "engine/engine_util_misc.h"
|
#include "engine/engine_util_misc.h"
|
||||||
@@ -90,7 +90,7 @@ int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename) {
|
|||||||
mjSTRNCPY(vfs->filename[vfs->nfile], newname);
|
mjSTRNCPY(vfs->filename[vfs->nfile], newname);
|
||||||
|
|
||||||
// allocate and read
|
// allocate and read
|
||||||
int filesize = 0;
|
size_t filesize = 0;
|
||||||
vfs->filedata[vfs->nfile] = mju_fileToMemory(fullname, &filesize);
|
vfs->filedata[vfs->nfile] = mju_fileToMemory(fullname, &filesize);
|
||||||
if (!vfs->filedata[vfs->nfile]) {
|
if (!vfs->filedata[vfs->nfile]) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -211,11 +211,11 @@ void mj_deleteVFS(mjVFS* vfs) {
|
|||||||
|
|
||||||
// open callback for the VFS resource provider
|
// open callback for the VFS resource provider
|
||||||
static int vfs_open_callback(mjResource* resource) {
|
static int vfs_open_callback(mjResource* resource) {
|
||||||
if (!resource || !resource->provider_data || !resource->name) {
|
if (!resource || !resource->name || !resource->data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mjVFS* vfs = (const mjVFS*) resource->provider_data;
|
const mjVFS* vfs = (const mjVFS*) resource->data;
|
||||||
return mj_findFileVFS(vfs, resource->name) >= 0;
|
return mj_findFileVFS(vfs, resource->name) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,12 +223,12 @@ static int vfs_open_callback(mjResource* resource) {
|
|||||||
|
|
||||||
// read callback for the VFS resource provider
|
// read callback for the VFS resource provider
|
||||||
static int vfs_read_callback(mjResource* resource, const void** buffer) {
|
static int vfs_read_callback(mjResource* resource, const void** buffer) {
|
||||||
if (!resource || !resource->provider_data) {
|
if (!resource || !resource->name || !resource->data) {
|
||||||
*buffer = NULL;
|
*buffer = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mjVFS* vfs = (const mjVFS*) resource->provider_data;
|
const mjVFS* vfs = (const mjVFS*) resource->data;
|
||||||
int i = mj_findFileVFS(vfs, resource->name);
|
int i = mj_findFileVFS(vfs, resource->name);
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
*buffer = NULL;
|
*buffer = NULL;
|
||||||
@@ -260,16 +260,49 @@ static void vfs_getdir_callback(mjResource* resource, const char** dir, int* ndi
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// registers a VFS resource provider; returns the index of the provider
|
// open VFS resource
|
||||||
int mj_registerVfsProvider(const mjVFS* vfs) {
|
mjResource* mju_openVfsResource(const char* name, const mjVFS* vfs) {
|
||||||
mjpResourceProvider provider = {
|
if (vfs == NULL) {
|
||||||
.prefix = mjVFS_PREFIX,
|
return NULL;
|
||||||
.open = &vfs_open_callback,
|
}
|
||||||
.read = &vfs_read_callback,
|
|
||||||
.close = &vfs_close_callback,
|
// VFS provider
|
||||||
.getdir = &vfs_getdir_callback,
|
static struct mjpResourceProvider provider = {
|
||||||
.data = (void*) vfs
|
.prefix = NULL,
|
||||||
|
.data = NULL,
|
||||||
|
.open = &vfs_open_callback,
|
||||||
|
.read = &vfs_read_callback,
|
||||||
|
.close = &vfs_close_callback,
|
||||||
|
.getdir = &vfs_getdir_callback,
|
||||||
};
|
};
|
||||||
|
|
||||||
return mjp_registerResourceProviderInternal(&provider);
|
// create resource
|
||||||
|
mjResource* resource = (mjResource*) mju_malloc(sizeof(mjResource));
|
||||||
|
if (resource == NULL) {
|
||||||
|
mjERROR("could not allocate memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out resource
|
||||||
|
memset(resource, 0, sizeof(mjResource));
|
||||||
|
|
||||||
|
// copy name
|
||||||
|
resource->name = mju_malloc(sizeof(char) * (strlen(name) + 1));
|
||||||
|
if (resource->name == NULL) {
|
||||||
|
mju_closeResource(resource);
|
||||||
|
mjERROR("could not allocate memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(resource->name, name, sizeof(char) * (strlen(name) + 1));
|
||||||
|
resource->data = (void*) vfs;
|
||||||
|
|
||||||
|
// open resource
|
||||||
|
resource->provider = &provider;
|
||||||
|
if (provider.open(resource)) {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found in VFS
|
||||||
|
mju_closeResource(resource);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <mujoco/mjexport.h>
|
#include <mujoco/mjexport.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
|
#include <mujoco/mjplugin.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -40,8 +41,8 @@ MJAPI int mj_deleteFileVFS(mjVFS* vfs, const char* filename);
|
|||||||
// delete all files from VFS
|
// delete all files from VFS
|
||||||
MJAPI void mj_deleteVFS(mjVFS* vfs);
|
MJAPI void mj_deleteVFS(mjVFS* vfs);
|
||||||
|
|
||||||
// registers a VFS resource provider; returns the index of the provider
|
// open VFS resource
|
||||||
MJAPI int mj_registerVfsProvider(const mjVFS* vfs);
|
MJAPI mjResource* mju_openVfsResource(const char* name, const mjVFS* vfs);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <MC.h>
|
#include <MC.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
|
#include <mujoco/mjplugin.h>
|
||||||
#include "cc/array_safety.h"
|
#include "cc/array_safety.h"
|
||||||
#include "engine/engine_crossplatform.h"
|
#include "engine/engine_crossplatform.h"
|
||||||
#include "engine/engine_resource.h"
|
#include "engine/engine_resource.h"
|
||||||
@@ -329,7 +330,7 @@ void mjCMesh::LoadSDF() {
|
|||||||
|
|
||||||
|
|
||||||
// compiler
|
// compiler
|
||||||
void mjCMesh::Compile(int vfs_provider) {
|
void mjCMesh::Compile(const mjVFS* vfs) {
|
||||||
// load file
|
// load file
|
||||||
if (!file_.empty()) {
|
if (!file_.empty()) {
|
||||||
// remove path from file if necessary
|
// remove path from file if necessary
|
||||||
@@ -348,7 +349,7 @@ void mjCMesh::Compile(int vfs_provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file_);
|
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file_);
|
||||||
mjResource* resource = LoadResource(filename, vfs_provider);
|
mjResource* resource = LoadResource(filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (asset_type == "model/stl") {
|
if (asset_type == "model/stl") {
|
||||||
@@ -1836,7 +1837,7 @@ mjCSkin::~mjCSkin() {
|
|||||||
|
|
||||||
|
|
||||||
// compiler
|
// compiler
|
||||||
void mjCSkin::Compile(int vfs_provider) {
|
void mjCSkin::Compile(const mjVFS* vfs) {
|
||||||
|
|
||||||
// load file
|
// load file
|
||||||
if (!file.empty()) {
|
if (!file.empty()) {
|
||||||
@@ -1865,7 +1866,7 @@ void mjCSkin::Compile(int vfs_provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
||||||
mjResource* resource = LoadResource(filename, vfs_provider);
|
mjResource* resource = LoadResource(filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LoadSKN(resource);
|
LoadSKN(resource);
|
||||||
|
|||||||
@@ -2423,7 +2423,7 @@ static void warninghandler(const char* msg) {
|
|||||||
|
|
||||||
|
|
||||||
// compiler
|
// compiler
|
||||||
mjModel* mjCModel::Compile(int vfs_provider) {
|
mjModel* mjCModel::Compile(const mjVFS* vfs) {
|
||||||
// The volatile keyword is necessary to prevent a possible memory leak due to
|
// The volatile keyword is necessary to prevent a possible memory leak due to
|
||||||
// an interaction between longjmp and compiler optimization. Specifically, at
|
// an interaction between longjmp and compiler optimization. Specifically, at
|
||||||
// the point where the setjmp takes places, these pointers have never been
|
// the point where the setjmp takes places, these pointers have never been
|
||||||
@@ -2454,7 +2454,7 @@ mjModel* mjCModel::Compile(int vfs_provider) {
|
|||||||
// TryCompile resulted in an mju_error which was converted to a longjmp.
|
// TryCompile resulted in an mju_error which was converted to a longjmp.
|
||||||
throw mjCError(0, "engine error: %s", errortext);
|
throw mjCError(0, "engine error: %s", errortext);
|
||||||
}
|
}
|
||||||
TryCompile(*const_cast<mjModel**>(&m), *const_cast<mjData**>(&data), vfs_provider);
|
TryCompile(*const_cast<mjModel**>(&m), *const_cast<mjData**>(&data), vfs);
|
||||||
} catch (mjCError err) {
|
} catch (mjCError err) {
|
||||||
// deallocate everything allocated in Compile
|
// deallocate everything allocated in Compile
|
||||||
mj_deleteModel(m);
|
mj_deleteModel(m);
|
||||||
@@ -2480,7 +2480,7 @@ mjModel* mjCModel::Compile(int vfs_provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void mjCModel::TryCompile(mjModel*& m, mjData*& d, int vfs_provider) {
|
void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
||||||
// check if nan test works
|
// check if nan test works
|
||||||
double test = mjNAN;
|
double test = mjNAN;
|
||||||
if (mjuu_defined(test)) {
|
if (mjuu_defined(test)) {
|
||||||
@@ -2557,7 +2557,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, int vfs_provider) {
|
|||||||
|
|
||||||
// compile meshes (needed for geom compilation)
|
// compile meshes (needed for geom compilation)
|
||||||
for (int i=0; i<meshes.size(); i++) {
|
for (int i=0; i<meshes.size(); i++) {
|
||||||
meshes[i]->Compile(vfs_provider);
|
meshes[i]->Compile(vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// automatically set nuser fields
|
// automatically set nuser fields
|
||||||
@@ -2616,9 +2616,9 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, int vfs_provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compile all other objects except for keyframes
|
// compile all other objects except for keyframes
|
||||||
for (int i=0; i<skins.size(); i++) skins[i]->Compile(vfs_provider);
|
for (int i=0; i<skins.size(); i++) skins[i]->Compile(vfs);
|
||||||
for (int i=0; i<hfields.size(); i++) hfields[i]->Compile(vfs_provider);
|
for (int i=0; i<hfields.size(); i++) hfields[i]->Compile(vfs);
|
||||||
for (int i=0; i<textures.size(); i++) textures[i]->Compile(vfs_provider);
|
for (int i=0; i<textures.size(); i++) textures[i]->Compile(vfs);
|
||||||
for (int i=0; i<materials.size(); i++) materials[i]->Compile();
|
for (int i=0; i<materials.size(); i++) materials[i]->Compile();
|
||||||
for (int i=0; i<pairs.size(); i++) pairs[i]->Compile();
|
for (int i=0; i<pairs.size(); i++) pairs[i]->Compile();
|
||||||
for (int i=0; i<excludes.size(); i++) excludes[i]->Compile();
|
for (int i=0; i<excludes.size(); i++) excludes[i]->Compile();
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class mjCModel {
|
|||||||
mjCModel(); // constructor
|
mjCModel(); // constructor
|
||||||
~mjCModel(); // destructor
|
~mjCModel(); // destructor
|
||||||
|
|
||||||
mjModel* Compile(int vfs_provider = 0); // COMPILER: construct mjModel
|
mjModel* Compile(const mjVFS* vfs = 0); // COMPILER: construct mjModel
|
||||||
bool CopyBack(const mjModel*); // DECOMPILER: copy numeric back
|
bool CopyBack(const mjModel*); // DECOMPILER: copy numeric back
|
||||||
void FuseStatic(void); // fuse static bodies with parent
|
void FuseStatic(void); // fuse static bodies with parent
|
||||||
void FuseReindex(mjCBody* body); // reindex elements during fuse
|
void FuseReindex(mjCBody* body); // reindex elements during fuse
|
||||||
@@ -163,8 +163,8 @@ class mjCModel {
|
|||||||
int nuser_sensor; // number of mjtNums in sensor_user
|
int nuser_sensor; // number of mjtNums in sensor_user
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void TryCompile(mjModel*& m, mjData*& d, int vfs_provider);
|
void TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs);
|
||||||
mjModel* _Compile(int vfs_provider);
|
mjModel* _Compile(const mjVFS* vfs);
|
||||||
|
|
||||||
void Clear(void); // clear objects allocated by Compile
|
void Clear(void); // clear objects allocated by Compile
|
||||||
|
|
||||||
|
|||||||
+20
-22
@@ -39,6 +39,7 @@
|
|||||||
#include "engine/engine_util_misc.h"
|
#include "engine/engine_util_misc.h"
|
||||||
#include "engine/engine_util_solve.h"
|
#include "engine/engine_util_solve.h"
|
||||||
#include "engine/engine_util_spatial.h"
|
#include "engine/engine_util_spatial.h"
|
||||||
|
#include "engine/engine_vfs.h"
|
||||||
#include "user/user_model.h"
|
#include "user/user_model.h"
|
||||||
#include "user/user_util.h"
|
#include "user/user_util.h"
|
||||||
|
|
||||||
@@ -495,17 +496,14 @@ mjCBase::mjCBase() {
|
|||||||
|
|
||||||
|
|
||||||
// load resource if found (fallback to OS filesystem)
|
// load resource if found (fallback to OS filesystem)
|
||||||
mjResource* mjCBase::LoadResource(string filename, int provider) {
|
mjResource* mjCBase::LoadResource(string filename, const mjVFS* vfs) {
|
||||||
mjResource* r = nullptr;
|
mjResource* r = nullptr;
|
||||||
const char* cname = filename.c_str();
|
const char* cname = filename.c_str();
|
||||||
|
|
||||||
// try reading from given provider
|
// try reading from provided VFS
|
||||||
if ((r = mju_openResource(cname, provider)) == nullptr) {
|
if ((r = mju_openVfsResource(cname, vfs)) == nullptr) {
|
||||||
if (!provider) {
|
// not in vfs try a provider or fallback to OS filesystem
|
||||||
throw mjCError(0, "file not found: '%s'", cname);
|
if ((r = mju_openResource(filename.c_str())) == nullptr) {
|
||||||
}
|
|
||||||
// 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);
|
throw mjCError(this, "resource not found via provider or OS filesystem: '%s'", cname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2194,7 +2192,7 @@ void mjCHField::LoadPNG(mjResource* resource) {
|
|||||||
|
|
||||||
|
|
||||||
// compiler
|
// compiler
|
||||||
void mjCHField::Compile(int vfs_provider) {
|
void mjCHField::Compile(const mjVFS* vfs) {
|
||||||
// check size parameters
|
// check size parameters
|
||||||
for (int i=0; i<4; i++)
|
for (int i=0; i<4; i++)
|
||||||
if (size[i]<=0)
|
if (size[i]<=0)
|
||||||
@@ -2226,7 +2224,7 @@ void mjCHField::Compile(int vfs_provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
||||||
mjResource* resource = LoadResource(filename, vfs_provider);
|
mjResource* resource = LoadResource(filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (asset_type == "image/png") {
|
if (asset_type == "image/png") {
|
||||||
@@ -2617,7 +2615,7 @@ void mjCTexture::LoadCustom(mjResource* resource,
|
|||||||
|
|
||||||
|
|
||||||
// load from PNG or custom file, flip if specified
|
// load from PNG or custom file, flip if specified
|
||||||
void mjCTexture::LoadFlip(string filename, int vfs_provider,
|
void mjCTexture::LoadFlip(string filename, const mjVFS* vfs,
|
||||||
std::vector<unsigned char>& image,
|
std::vector<unsigned char>& image,
|
||||||
unsigned int& w, unsigned int& h) {
|
unsigned int& w, unsigned int& h) {
|
||||||
std::string asset_type = GetAssetContentType(filename, content_type);
|
std::string asset_type = GetAssetContentType(filename, content_type);
|
||||||
@@ -2631,7 +2629,7 @@ void mjCTexture::LoadFlip(string filename, int vfs_provider,
|
|||||||
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
mjResource* resource = LoadResource(filename, vfs_provider);
|
mjResource* resource = LoadResource(filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (asset_type == "image/png") {
|
if (asset_type == "image/png") {
|
||||||
@@ -2693,11 +2691,11 @@ void mjCTexture::LoadFlip(string filename, int vfs_provider,
|
|||||||
|
|
||||||
|
|
||||||
// load 2D
|
// load 2D
|
||||||
void mjCTexture::Load2D(string filename, int vfs_provider) {
|
void mjCTexture::Load2D(string filename, const mjVFS* vfs) {
|
||||||
// load PNG or custom
|
// load PNG or custom
|
||||||
unsigned int w, h;
|
unsigned int w, h;
|
||||||
std::vector<unsigned char> image;
|
std::vector<unsigned char> image;
|
||||||
LoadFlip(filename, vfs_provider, image, w, h);
|
LoadFlip(filename, vfs, image, w, h);
|
||||||
|
|
||||||
// assign size
|
// assign size
|
||||||
width = w;
|
width = w;
|
||||||
@@ -2716,7 +2714,7 @@ void mjCTexture::Load2D(string filename, int vfs_provider) {
|
|||||||
|
|
||||||
|
|
||||||
// load cube or skybox from single file (repeated or grid)
|
// load cube or skybox from single file (repeated or grid)
|
||||||
void mjCTexture::LoadCubeSingle(string filename, int vfs_provider) {
|
void mjCTexture::LoadCubeSingle(string filename, const mjVFS* vfs) {
|
||||||
// check gridsize
|
// check gridsize
|
||||||
if (gridsize[0]<1 || gridsize[1]<1 || gridsize[0]*gridsize[1]>12) {
|
if (gridsize[0]<1 || gridsize[1]<1 || gridsize[0]*gridsize[1]>12) {
|
||||||
throw mjCError(this,
|
throw mjCError(this,
|
||||||
@@ -2727,7 +2725,7 @@ void mjCTexture::LoadCubeSingle(string filename, int vfs_provider) {
|
|||||||
// load PNG or custom
|
// load PNG or custom
|
||||||
unsigned int w, h;
|
unsigned int w, h;
|
||||||
std::vector<unsigned char> image;
|
std::vector<unsigned char> image;
|
||||||
LoadFlip(filename, vfs_provider, image, w, h);
|
LoadFlip(filename, vfs, image, w, h);
|
||||||
|
|
||||||
// check gridsize for compatibility
|
// check gridsize for compatibility
|
||||||
if (w/gridsize[1]!=h/gridsize[0] || (w%gridsize[1]) || (h%gridsize[0])) {
|
if (w/gridsize[1]!=h/gridsize[0] || (w%gridsize[1]) || (h%gridsize[0])) {
|
||||||
@@ -2816,7 +2814,7 @@ void mjCTexture::LoadCubeSingle(string filename, int vfs_provider) {
|
|||||||
|
|
||||||
|
|
||||||
// load cube or skybox from separate file
|
// load cube or skybox from separate file
|
||||||
void mjCTexture::LoadCubeSeparate(int vfs_provider) {
|
void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
||||||
// keep track of which faces were defined
|
// keep track of which faces were defined
|
||||||
int loaded[6] = {0, 0, 0, 0, 0, 0};
|
int loaded[6] = {0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
@@ -2834,7 +2832,7 @@ void mjCTexture::LoadCubeSeparate(int vfs_provider) {
|
|||||||
// load PNG or custom
|
// load PNG or custom
|
||||||
unsigned int w, h;
|
unsigned int w, h;
|
||||||
std::vector<unsigned char> image;
|
std::vector<unsigned char> image;
|
||||||
LoadFlip(filename, vfs_provider, image, w, h);
|
LoadFlip(filename, vfs, image, w, h);
|
||||||
|
|
||||||
// PNG must be square
|
// PNG must be square
|
||||||
if (w!=h) {
|
if (w!=h) {
|
||||||
@@ -2888,7 +2886,7 @@ void mjCTexture::LoadCubeSeparate(int vfs_provider) {
|
|||||||
|
|
||||||
|
|
||||||
// compiler
|
// compiler
|
||||||
void mjCTexture::Compile(int vfs_provider) {
|
void mjCTexture::Compile(const mjVFS* vfs) {
|
||||||
// builtin
|
// builtin
|
||||||
if (builtin!=mjBUILTIN_NONE) {
|
if (builtin!=mjBUILTIN_NONE) {
|
||||||
// check size
|
// check size
|
||||||
@@ -2931,9 +2929,9 @@ void mjCTexture::Compile(int vfs_provider) {
|
|||||||
|
|
||||||
// dispatch
|
// dispatch
|
||||||
if (type==mjTEXTURE_2D) {
|
if (type==mjTEXTURE_2D) {
|
||||||
Load2D(filename, vfs_provider);
|
Load2D(filename, vfs);
|
||||||
} else {
|
} else {
|
||||||
LoadCubeSingle(filename, vfs_provider);
|
LoadCubeSingle(filename, vfs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2961,7 +2959,7 @@ void mjCTexture::Compile(int vfs_provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only cube and skybox
|
// only cube and skybox
|
||||||
LoadCubeSeparate(vfs_provider);
|
LoadCubeSeparate(vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure someone allocated data; SHOULD NOT OCCUR
|
// make sure someone allocated data; SHOULD NOT OCCUR
|
||||||
|
|||||||
+10
-9
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "lodepng.h"
|
#include "lodepng.h"
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
|
#include <mujoco/mjplugin.h>
|
||||||
|
|
||||||
// forward declarations of all mjC/X classes
|
// forward declarations of all mjC/X classes
|
||||||
class mjCError;
|
class mjCError;
|
||||||
@@ -173,7 +174,7 @@ class mjCBase {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// load resource if found (fallback to OS filesystem)
|
// load resource if found (fallback to OS filesystem)
|
||||||
mjResource* LoadResource(std::string filename, int provider);
|
mjResource* LoadResource(std::string filename, const mjVFS* vfs);
|
||||||
|
|
||||||
// Get and sanitize content type from raw_text if not empty, otherwise parse
|
// Get and sanitize content type from raw_text if not empty, otherwise parse
|
||||||
// content type from resource_name; throw on failure
|
// content type from resource_name; throw on failure
|
||||||
@@ -572,7 +573,7 @@ class mjCMesh: public mjCBase {
|
|||||||
void set_usertexcoord(std::optional<std::vector<float>>&& usertexcoord);
|
void set_usertexcoord(std::optional<std::vector<float>>&& usertexcoord);
|
||||||
void set_userface(std::optional<std::vector<int>>&& userface);
|
void set_userface(std::optional<std::vector<int>>&& userface);
|
||||||
|
|
||||||
void Compile(int vfs_provider); // compiler
|
void Compile(const mjVFS* vfs); // compiler
|
||||||
double* GetPosPtr(mjtMeshType type); // get position
|
double* GetPosPtr(mjtMeshType type); // get position
|
||||||
double* GetQuatPtr(mjtMeshType type); // get orientation
|
double* GetQuatPtr(mjtMeshType type); // get orientation
|
||||||
double* GetInertiaBoxPtr(mjtMeshType type); // get inertia box
|
double* GetInertiaBoxPtr(mjtMeshType type); // get inertia box
|
||||||
@@ -696,7 +697,7 @@ class mjCSkin: public mjCBase {
|
|||||||
private:
|
private:
|
||||||
mjCSkin(mjCModel* = 0); // constructor
|
mjCSkin(mjCModel* = 0); // constructor
|
||||||
~mjCSkin(); // destructor
|
~mjCSkin(); // destructor
|
||||||
void Compile(int vfs_provider); // compiler
|
void Compile(const mjVFS* vfs); // compiler
|
||||||
void LoadSKN(mjResource* resource); // load skin in SKN BIN format
|
void LoadSKN(mjResource* resource); // load skin in SKN BIN format
|
||||||
|
|
||||||
int matid; // material id
|
int matid; // material id
|
||||||
@@ -723,7 +724,7 @@ class mjCHField : public mjCBase {
|
|||||||
private:
|
private:
|
||||||
mjCHField(mjCModel* model); // constructor
|
mjCHField(mjCModel* model); // constructor
|
||||||
~mjCHField(); // destructor
|
~mjCHField(); // destructor
|
||||||
void Compile(int vfs_provider); // compiler
|
void Compile(const mjVFS* vfs); // compiler
|
||||||
|
|
||||||
void LoadCustom(mjResource* resource); // load from custom format
|
void LoadCustom(mjResource* resource); // load from custom format
|
||||||
void LoadPNG(mjResource* resource); // load from PNG format
|
void LoadPNG(mjResource* resource); // load from PNG format
|
||||||
@@ -768,15 +769,15 @@ class mjCTexture : public mjCBase {
|
|||||||
private:
|
private:
|
||||||
mjCTexture(mjCModel*); // constructor
|
mjCTexture(mjCModel*); // constructor
|
||||||
~mjCTexture(); // destructior
|
~mjCTexture(); // destructior
|
||||||
void Compile(int vfs_provider); // compiler
|
void Compile(const mjVFS* vfs); // compiler
|
||||||
|
|
||||||
void Builtin2D(void); // make builtin 2D
|
void Builtin2D(void); // make builtin 2D
|
||||||
void BuiltinCube(void); // make builtin cube
|
void BuiltinCube(void); // make builtin cube
|
||||||
void Load2D(std::string filename, int vfs_provider); // load 2D from file
|
void Load2D(std::string filename, const mjVFS* vfs); // load 2D from file
|
||||||
void LoadCubeSingle(std::string filename, int vfs_provider); // load cube from single file
|
void LoadCubeSingle(std::string filename, const mjVFS* vfs); // load cube from single file
|
||||||
void LoadCubeSeparate(int vfs_provider); // load cube from separate files
|
void LoadCubeSeparate(const mjVFS* vfs); // load cube from separate files
|
||||||
|
|
||||||
void LoadFlip(std::string filename, int vfs_provider, // load and flip
|
void LoadFlip(std::string filename, const mjVFS* vfs, // load and flip
|
||||||
std::vector<unsigned char>& image,
|
std::vector<unsigned char>& image,
|
||||||
unsigned int& w, unsigned int& h);
|
unsigned int& w, unsigned int& h);
|
||||||
|
|
||||||
|
|||||||
+13
-11
@@ -22,9 +22,11 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <mujoco/mjmodel.h>
|
||||||
#include "cc/array_safety.h"
|
#include "cc/array_safety.h"
|
||||||
#include "engine/engine_crossplatform.h"
|
#include "engine/engine_crossplatform.h"
|
||||||
#include "engine/engine_resource.h"
|
#include "engine/engine_resource.h"
|
||||||
|
#include "engine/engine_vfs.h"
|
||||||
#include "user/user_model.h"
|
#include "user/user_model.h"
|
||||||
#include "user/user_util.h"
|
#include "user/user_util.h"
|
||||||
#include "xml/xml_native_reader.h"
|
#include "xml/xml_native_reader.h"
|
||||||
@@ -110,7 +112,7 @@ string mjWriteXML(mjCModel* model, char* error, int error_sz) {
|
|||||||
|
|
||||||
// find include elements recursively, replace them with subtree from xml file
|
// find include elements recursively, replace them with subtree from xml file
|
||||||
static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
|
static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
|
||||||
int vfs_provider, vector<string>& included) {
|
const mjVFS* vfs, vector<string>& included) {
|
||||||
// include element: process
|
// include element: process
|
||||||
if (!strcasecmp(elem->Value(), "include")) {
|
if (!strcasecmp(elem->Value(), "include")) {
|
||||||
// make sure include has no children
|
// make sure include has no children
|
||||||
@@ -133,9 +135,9 @@ static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
|
|||||||
// get data source
|
// get data source
|
||||||
mjResource *resource = nullptr;
|
mjResource *resource = nullptr;
|
||||||
const char* xmlstring = nullptr;
|
const char* xmlstring = nullptr;
|
||||||
if ((resource = mju_openResource(filename.c_str(), vfs_provider)) == nullptr) {
|
if ((resource = mju_openVfsResource(filename.c_str(), vfs)) == nullptr) {
|
||||||
// load from OS filesystem
|
// load from provider or OS filesystem
|
||||||
if (!vfs_provider || (resource = mju_openResource(filename.c_str(), 0)) == nullptr) {
|
if ((resource = mju_openResource(filename.c_str())) == nullptr) {
|
||||||
throw mjXError(elem, "Could not open file '%s'", filename.c_str());
|
throw mjXError(elem, "Could not open file '%s'", filename.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,14 +198,14 @@ static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// run XMLInclude on first new child
|
// run XMLInclude on first new child
|
||||||
return mjIncludeXML(first->ToElement(), dir, vfs_provider, included);
|
return mjIncludeXML(first->ToElement(), dir, vfs, included);
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise check all child elements, return self
|
// otherwise check all child elements, return self
|
||||||
else {
|
else {
|
||||||
XMLElement* child = elem->FirstChildElement();
|
XMLElement* child = elem->FirstChildElement();
|
||||||
while (child) {
|
while (child) {
|
||||||
child = mjIncludeXML(child, dir, vfs_provider, included);
|
child = mjIncludeXML(child, dir, vfs, included);
|
||||||
if (child) {
|
if (child) {
|
||||||
child = child->NextSiblingElement();
|
child = child->NextSiblingElement();
|
||||||
}
|
}
|
||||||
@@ -215,7 +217,7 @@ static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
|
|||||||
|
|
||||||
|
|
||||||
// Main parser function
|
// Main parser function
|
||||||
mjCModel* mjParseXML(const char* filename, int vfs_provider, char* error, int error_sz) {
|
mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz) {
|
||||||
LocaleOverride locale_override;
|
LocaleOverride locale_override;
|
||||||
|
|
||||||
// check arguments
|
// check arguments
|
||||||
@@ -235,9 +237,9 @@ mjCModel* mjParseXML(const char* filename, int vfs_provider, char* error, int er
|
|||||||
// get data source
|
// get data source
|
||||||
mjResource* resource = nullptr;
|
mjResource* resource = nullptr;
|
||||||
const char* xmlstring = nullptr;
|
const char* xmlstring = nullptr;
|
||||||
if ((resource = mju_openResource(filename, vfs_provider)) == nullptr) {
|
if ((resource = mju_openVfsResource(filename, vfs)) == nullptr) {
|
||||||
// load from OS filesystem
|
// load from provider or fallback to OS filesystem
|
||||||
if (!vfs_provider || (resource = mju_openResource(filename, 0)) == nullptr) {
|
if ((resource = mju_openResource(filename)) == nullptr) {
|
||||||
if (error) {
|
if (error) {
|
||||||
snprintf(error, error_sz, "mjParseXML: could not open file '%s'", filename);
|
snprintf(error, error_sz, "mjParseXML: could not open file '%s'", filename);
|
||||||
}
|
}
|
||||||
@@ -303,7 +305,7 @@ mjCModel* mjParseXML(const char* filename, int vfs_provider, char* error, int er
|
|||||||
// find include elements, replace them with subtree from xml file
|
// find include elements, replace them with subtree from xml file
|
||||||
vector<string> included;
|
vector<string> included;
|
||||||
included.push_back(filename);
|
included.push_back(filename);
|
||||||
mjIncludeXML(root, model->modelfiledir, vfs_provider, included);
|
mjIncludeXML(root, model->modelfiledir, vfs, included);
|
||||||
|
|
||||||
// parse MuJoCo model
|
// parse MuJoCo model
|
||||||
mjXReader parser;
|
mjXReader parser;
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
std::string mjWriteXML(mjCModel* model, char* error, int error_sz);
|
std::string mjWriteXML(mjCModel* model, char* error, int error_sz);
|
||||||
|
|
||||||
// Main parser function
|
// Main parser function
|
||||||
mjCModel* mjParseXML(const char* filename, int vfs_provider, char* error, int error_sz);
|
mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz);
|
||||||
|
|
||||||
|
|
||||||
#endif // MUJOCO_SRC_XML_XML_H_
|
#endif // MUJOCO_SRC_XML_XML_H_
|
||||||
|
|||||||
+8
-30
@@ -70,20 +70,23 @@ static std::mutex themutex;
|
|||||||
|
|
||||||
//---------------------------------- Functions -----------------------------------------------------
|
//---------------------------------- Functions -----------------------------------------------------
|
||||||
|
|
||||||
// mj_loadXML helper function
|
// parse XML file in MJCF or URDF format, compile it, return low-level model
|
||||||
mjModel* _loadXML(const char* filename, int vfs_provider,
|
// if vfs is not NULL, look up files in vfs before reading from disk
|
||||||
char* error, int error_sz) {
|
// error can be NULL; otherwise assumed to have size error_sz
|
||||||
|
mjModel* mj_loadXML(const char* filename, const mjVFS* vfs,
|
||||||
|
char* error, int error_sz) {
|
||||||
|
|
||||||
// serialize access to themodel
|
// serialize access to themodel
|
||||||
std::lock_guard<std::mutex> lock(themutex);
|
std::lock_guard<std::mutex> lock(themutex);
|
||||||
|
|
||||||
// parse new model
|
// parse new model
|
||||||
mjCModel* newmodel = mjParseXML(filename, vfs_provider, error, error_sz);
|
mjCModel* newmodel = mjParseXML(filename, vfs, error, error_sz);
|
||||||
if (!newmodel) {
|
if (!newmodel) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// compile new model
|
// compile new model
|
||||||
mjModel* m = newmodel->Compile(vfs_provider);
|
mjModel* m = newmodel->Compile(vfs);
|
||||||
if (!m) {
|
if (!m) {
|
||||||
mjCopyError(error, newmodel->GetError().message, error_sz);
|
mjCopyError(error, newmodel->GetError().message, error_sz);
|
||||||
delete newmodel;
|
delete newmodel;
|
||||||
@@ -106,31 +109,6 @@ mjModel* _loadXML(const char* filename, int vfs_provider,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// parse XML file in MJCF or URDF format, compile it, return low-level model
|
|
||||||
// if vfs is not NULL, look up files in vfs before reading from disk
|
|
||||||
// error can be NULL; otherwise assumed to have size error_sz
|
|
||||||
mjModel* mj_loadXML(const char* filename, const mjVFS* vfs,
|
|
||||||
char* error, int error_sz) {
|
|
||||||
|
|
||||||
if (vfs == nullptr) {
|
|
||||||
return _loadXML(filename, 0, error, error_sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = mj_registerVfsProvider(vfs);
|
|
||||||
if (index < 1) {
|
|
||||||
if (error) {
|
|
||||||
snprintf(error, error_sz, "mj_loadXML: could not register VFS");
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mjModel* model = _loadXML(filename, index, error, error_sz);
|
|
||||||
mjp_unregisterResourceProvider(index);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// update XML data structures with info from low-level model, save as MJCF
|
// update XML data structures with info from low-level model, save as MJCF
|
||||||
// returns 1 if successful, 0 otherwise
|
// returns 1 if successful, 0 otherwise
|
||||||
// error can be NULL; otherwise assumed to have size error_sz
|
// error can be NULL; otherwise assumed to have size error_sz
|
||||||
|
|||||||
@@ -19,12 +19,10 @@
|
|||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include "src/cc/array_safety.h"
|
#include "src/cc/array_safety.h"
|
||||||
#include <mujoco/mjmodel.h>
|
|
||||||
#include <mujoco/mjplugin.h>
|
#include <mujoco/mjplugin.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "src/engine/engine_plugin.h"
|
#include "src/engine/engine_plugin.h"
|
||||||
#include "src/engine/engine_resource.h"
|
#include "src/engine/engine_resource.h"
|
||||||
#include "src/engine/engine_vfs.h"
|
|
||||||
#include "test/fixture.h"
|
#include "test/fixture.h"
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
@@ -68,20 +66,48 @@ void close_str(mjResource* resource) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, RegisterProviderSuccess) {
|
TEST_F(ResourceTest, RegisterProviderSuccess) {
|
||||||
mjpResourceProvider provider = {"my-prefix.123+45", open_nop, read_nop, close_nop,
|
mjpResourceProvider provider = {
|
||||||
nullptr};
|
"my-prefix.123+45", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
int count1 = mjp_resourceProviderCount();
|
int count1 = mjp_resourceProviderCount();
|
||||||
int i = mjp_registerResourceProvider(&provider);
|
int i = mjp_registerResourceProvider(&provider);
|
||||||
int count2 = mjp_resourceProviderCount();
|
int count2 = mjp_resourceProviderCount();
|
||||||
|
|
||||||
EXPECT_GT(i, 0);
|
EXPECT_GT(i, 0);
|
||||||
EXPECT_EQ(count1+1, count2);
|
EXPECT_EQ(count2 - count1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResourceTest, RegisterProviderMultipleSuccess) {
|
||||||
|
mjpResourceProvider provider = {
|
||||||
|
"my-prefix.123+44", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
|
mjpResourceProvider provider2 = {
|
||||||
|
"my-prefix.123+46", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
mjpResourceProvider provider3 = {
|
||||||
|
"my-prefix.123+41", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
|
int count1 = mjp_resourceProviderCount();
|
||||||
|
int i = mjp_registerResourceProvider(&provider);
|
||||||
|
int i2 = mjp_registerResourceProvider(&provider2);
|
||||||
|
int i3 = mjp_registerResourceProvider(&provider3);
|
||||||
|
int count2 = mjp_resourceProviderCount();
|
||||||
|
|
||||||
|
EXPECT_GT(i, 0);
|
||||||
|
EXPECT_GT(i2, 0);
|
||||||
|
EXPECT_GT(i3, 0);
|
||||||
|
EXPECT_EQ(count2 - count1, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, RegisterProviderMissingCallbacks) {
|
TEST_F(ResourceTest, RegisterProviderMissingCallbacks) {
|
||||||
mjpResourceProvider provider = {"myprefix", nullptr, nullptr, nullptr,
|
mjpResourceProvider provider = {
|
||||||
nullptr};
|
"myprefix"
|
||||||
|
};
|
||||||
|
|
||||||
// install warning handler
|
// install warning handler
|
||||||
static char warning[1024];
|
static char warning[1024];
|
||||||
@@ -98,7 +124,9 @@ TEST_F(ResourceTest, RegisterProviderMissingCallbacks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, RegisterProviderMissingPrefix) {
|
TEST_F(ResourceTest, RegisterProviderMissingPrefix) {
|
||||||
mjpResourceProvider provider = {"", open_nop, read_nop, close_nop, nullptr, nullptr};
|
mjpResourceProvider provider = {
|
||||||
|
"", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
// install warning handler
|
// install warning handler
|
||||||
static char warning[1024];
|
static char warning[1024];
|
||||||
@@ -115,7 +143,9 @@ TEST_F(ResourceTest, RegisterProviderMissingPrefix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, RegisterProviderInvalidPrefix1) {
|
TEST_F(ResourceTest, RegisterProviderInvalidPrefix1) {
|
||||||
mjpResourceProvider provider = {"1invalid", open_nop, read_nop, close_nop, nullptr, nullptr};
|
mjpResourceProvider provider = {
|
||||||
|
"1invalid", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
// install warning handler
|
// install warning handler
|
||||||
static char warning[1024];
|
static char warning[1024];
|
||||||
@@ -132,7 +162,9 @@ TEST_F(ResourceTest, RegisterProviderInvalidPrefix1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, RegisterProviderInvalidPrefix2) {
|
TEST_F(ResourceTest, RegisterProviderInvalidPrefix2) {
|
||||||
mjpResourceProvider provider = {"invalid:", open_nop, read_nop, close_nop, nullptr, nullptr};
|
mjpResourceProvider provider = {
|
||||||
|
"invalid:", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
// install warning handler
|
// install warning handler
|
||||||
static char warning[1024];
|
static char warning[1024];
|
||||||
@@ -149,11 +181,13 @@ TEST_F(ResourceTest, RegisterProviderInvalidPrefix2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, RegisterProviderSame) {
|
TEST_F(ResourceTest, RegisterProviderSame) {
|
||||||
mjpResourceProvider provider = {"prefix", open_nop, read_nop, close_nop,
|
mjpResourceProvider provider = {
|
||||||
nullptr, nullptr};
|
"prefix", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
mjpResourceProvider provider2 = {"prefix", open_nop, read_nop, close_nop,
|
mjpResourceProvider provider2 = {
|
||||||
nullptr, nullptr};
|
"prefix", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
int i1 = mjp_registerResourceProvider(&provider);
|
int i1 = mjp_registerResourceProvider(&provider);
|
||||||
int count1 = mjp_resourceProviderCount();
|
int count1 = mjp_resourceProviderCount();
|
||||||
@@ -166,11 +200,13 @@ TEST_F(ResourceTest, RegisterProviderSame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, RegisterProviderSameCase) {
|
TEST_F(ResourceTest, RegisterProviderSameCase) {
|
||||||
mjpResourceProvider provider = {"prefix", open_nop, read_nop, close_nop,
|
mjpResourceProvider provider = {
|
||||||
nullptr, nullptr};
|
"prefix", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
mjpResourceProvider provider2 = {"PREFIX", open_nop, read_nop, close_nop,
|
mjpResourceProvider provider2 = {
|
||||||
nullptr, nullptr};
|
"PREFIX", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
int i1 = mjp_registerResourceProvider(&provider);
|
int i1 = mjp_registerResourceProvider(&provider);
|
||||||
int count1 = mjp_resourceProviderCount();
|
int count1 = mjp_resourceProviderCount();
|
||||||
@@ -183,15 +219,16 @@ TEST_F(ResourceTest, RegisterProviderSameCase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, GeneralTest) {
|
TEST_F(ResourceTest, GeneralTest) {
|
||||||
mjpResourceProvider provider = {"str", open_str, read_str, close_str,
|
mjpResourceProvider provider = {
|
||||||
nullptr, nullptr};
|
"str", open_str, read_str, close_str
|
||||||
|
};
|
||||||
|
|
||||||
// register resource provider
|
// register resource provider
|
||||||
int i = mjp_registerResourceProvider(&provider);
|
int i = mjp_registerResourceProvider(&provider);
|
||||||
EXPECT_GT(i, 0);
|
EXPECT_GT(i, 0);
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("str:file", 0);
|
mjResource* resource = mju_openResource("str:file");
|
||||||
ASSERT_THAT(resource, NotNull());
|
ASSERT_THAT(resource, NotNull());
|
||||||
|
|
||||||
const char* buffer = NULL;
|
const char* buffer = NULL;
|
||||||
@@ -203,8 +240,9 @@ TEST_F(ResourceTest, GeneralTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, GeneralTestFailure) {
|
TEST_F(ResourceTest, GeneralTestFailure) {
|
||||||
mjpResourceProvider provider = {"str", open_str, read_str, close_str,
|
mjpResourceProvider provider = {
|
||||||
nullptr, nullptr};
|
"str", open_str, read_str, close_str
|
||||||
|
};
|
||||||
|
|
||||||
// register resource provider
|
// register resource provider
|
||||||
int i = mjp_registerResourceProvider(&provider);
|
int i = mjp_registerResourceProvider(&provider);
|
||||||
@@ -219,15 +257,16 @@ TEST_F(ResourceTest, GeneralTestFailure) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("str:notfound", 0);
|
mjResource* resource = mju_openResource("str:notfound");
|
||||||
ASSERT_THAT(resource, IsNull());
|
ASSERT_THAT(resource, IsNull());
|
||||||
|
|
||||||
EXPECT_THAT(warning, HasSubstr("could not open"));
|
EXPECT_THAT(warning, HasSubstr("could not open"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, NameWithValidPrefix) {
|
TEST_F(ResourceTest, NameWithValidPrefix) {
|
||||||
mjpResourceProvider provider = {"nop", open_nop, read_nop, close_nop,
|
mjpResourceProvider provider = {
|
||||||
nullptr, nullptr};
|
"nop", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
// register resource provider
|
// register resource provider
|
||||||
int i = mjp_registerResourceProvider(&provider);
|
int i = mjp_registerResourceProvider(&provider);
|
||||||
@@ -242,14 +281,15 @@ TEST_F(ResourceTest, NameWithValidPrefix) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("nop:found", 0);
|
mjResource* resource = mju_openResource("nop:found");
|
||||||
ASSERT_THAT(resource, NotNull());
|
ASSERT_THAT(resource, NotNull());
|
||||||
mju_closeResource(resource);
|
mju_closeResource(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, NameWithUpperCasePrefix) {
|
TEST_F(ResourceTest, NameWithUpperCasePrefix) {
|
||||||
mjpResourceProvider provider = {"nop", open_nop, read_nop, close_nop,
|
mjpResourceProvider provider = {
|
||||||
nullptr, nullptr};
|
"nop", open_nop, read_nop, close_nop,
|
||||||
|
};
|
||||||
|
|
||||||
// register resource provider
|
// register resource provider
|
||||||
int i = mjp_registerResourceProvider(&provider);
|
int i = mjp_registerResourceProvider(&provider);
|
||||||
@@ -264,14 +304,15 @@ TEST_F(ResourceTest, NameWithUpperCasePrefix) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("NOP:found", 0);
|
mjResource* resource = mju_openResource("NOP:found");
|
||||||
ASSERT_THAT(resource, NotNull());
|
ASSERT_THAT(resource, NotNull());
|
||||||
mju_closeResource(resource);
|
mju_closeResource(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, NameWithInvalidPrefix) {
|
TEST_F(ResourceTest, NameWithInvalidPrefix) {
|
||||||
mjpResourceProvider provider = {"nop", open_nop, read_nop, close_nop,
|
mjpResourceProvider provider = {
|
||||||
nullptr, nullptr};
|
"nop", open_nop, read_nop, close_nop
|
||||||
|
};
|
||||||
|
|
||||||
// register resource provider
|
// register resource provider
|
||||||
int i = mjp_registerResourceProvider(&provider);
|
int i = mjp_registerResourceProvider(&provider);
|
||||||
@@ -286,33 +327,9 @@ TEST_F(ResourceTest, NameWithInvalidPrefix) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("nopfound", 0);
|
mjResource* resource = mju_openResource("nopfound");
|
||||||
ASSERT_THAT(resource, IsNull());
|
ASSERT_THAT(resource, IsNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResourceTest, VFSProvider) {
|
|
||||||
// load VFS on the heap
|
|
||||||
auto vfs = std::make_unique<mjVFS>();
|
|
||||||
mj_defaultVFS(vfs.get());
|
|
||||||
mj_makeEmptyFileVFS(vfs.get(), "file", 1);
|
|
||||||
|
|
||||||
// register resource provider
|
|
||||||
int i = mj_registerVfsProvider(vfs.get());
|
|
||||||
EXPECT_GT(i, 0);
|
|
||||||
|
|
||||||
|
|
||||||
// install warning handler
|
|
||||||
static char warning[1024];
|
|
||||||
warning[0] = '\0';
|
|
||||||
mju_user_warning = [](const char* msg) {
|
|
||||||
util::strcpy_arr(warning, msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
// open resource
|
|
||||||
mjResource* resource = mju_openResource("vfs:file", 0);
|
|
||||||
ASSERT_THAT(resource, IsNull());
|
|
||||||
mj_deleteFileVFS(vfs.get(), "file");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
+6
-4
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "test/fixture.h"
|
#include "test/fixture.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -79,10 +80,11 @@ mjModel* LoadModelFromString(std::string_view xml, char* error,
|
|||||||
resource->data = &(resource->name[strlen("LoadModelFromString:")]);
|
resource->data = &(resource->name[strlen("LoadModelFromString:")]);
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
resourceProvider.read = +[](mjResource* resource, const void** buffer) {
|
resourceProvider.read =
|
||||||
*buffer = resource->data;
|
+[](mjResource* resource, const void** buffer) {
|
||||||
return (int) strlen((const char*) resource->data);
|
*buffer = resource->data;
|
||||||
};
|
return (int) strlen((const char*) resource->data);
|
||||||
|
};
|
||||||
resourceProvider.close = +[](mjResource* resource) {};
|
resourceProvider.close = +[](mjResource* resource) {};
|
||||||
mjp_registerResourceProvider(&resourceProvider);
|
mjp_registerResourceProvider(&resourceProvider);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1756,7 +1756,7 @@ public unsafe struct _mjVFS
|
|||||||
{
|
{
|
||||||
public int nfile;
|
public int nfile;
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000 * 1000)] public char[] filename;
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000 * 1000)] public char[] filename;
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000)] public int[] filesize;
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000)] public UIntPtr[] filesize;
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000)] public IntPtr[] filedata;
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000)] public IntPtr[] filedata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user