Refactor user cache to just support void pointers so user objects can be cached whole.
PiperOrigin-RevId: 651307384 Change-Id: I92c058a900727070235d00b6d2165c29fd12e7b0
This commit is contained in:
committed by
Copybara-Service
parent
c145b79f6e
commit
f9ab89b205
+28
-190
@@ -14,137 +14,16 @@
|
||||
|
||||
#include "user/user_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// copies a block of data into the asset and returns number of bytes stored
|
||||
// loading data into an asset should happen in a single thread
|
||||
template<typename T> std::size_t mjCAsset::Add(const std::string& name,
|
||||
const T* data, std::size_t n) {
|
||||
auto [it, inserted] = blocks_.insert({name, mjCAssetData()});
|
||||
if (!inserted) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t nbytes = n * sizeof(T);
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
|
||||
mjCAssetData& block = it->second;
|
||||
|
||||
block.bytes = std::shared_ptr<uint8_t>(new uint8_t[nbytes],
|
||||
[](uint8_t *p) {delete [] p;});
|
||||
std::copy(ptr, ptr + nbytes, block.bytes.get());
|
||||
block.nbytes = nbytes;
|
||||
nbytes_ += nbytes;
|
||||
return nbytes;
|
||||
}
|
||||
template std::size_t mjCAsset::Add(const std::string& name,
|
||||
const uint8_t* data, std::size_t n);
|
||||
template std::size_t mjCAsset::Add(const std::string& name,
|
||||
const int* data, std::size_t n);
|
||||
template std::size_t mjCAsset::Add(const std::string& name,
|
||||
const unsigned int* data, std::size_t n);
|
||||
template std::size_t mjCAsset::Add(const std::string& name,
|
||||
const float* data, std::size_t n);
|
||||
template std::size_t mjCAsset::Add(const std::string& name,
|
||||
const double* data, std::size_t n);
|
||||
|
||||
|
||||
|
||||
// copies a vector into the asset and returns number of bytes stored
|
||||
// loading data into an asset should happen in a single thread
|
||||
template<typename T> std::size_t mjCAsset::AddVector(const std::string& name,
|
||||
const std::vector<T>& v) {
|
||||
return Add<T>(name, v.data(), v.size());
|
||||
}
|
||||
template std::size_t mjCAsset::AddVector(const std::string& name,
|
||||
const std::vector<uint8_t>& v);
|
||||
template std::size_t mjCAsset::AddVector(const std::string& name,
|
||||
const std::vector<int>& v);
|
||||
template std::size_t mjCAsset::AddVector(const std::string& name,
|
||||
const std::vector<unsigned int>& v);
|
||||
template std::size_t mjCAsset::AddVector(const std::string& name,
|
||||
const std::vector<float>& v);
|
||||
template std::size_t mjCAsset::AddVector(const std::string& name,
|
||||
const std::vector<double>& v);
|
||||
|
||||
|
||||
|
||||
// returns a pointer to a block of data, sets n to size of data
|
||||
template<typename T>
|
||||
const T* mjCAsset::Get(const std::string& name, std::size_t* n) const {
|
||||
auto it = blocks_.find(name);
|
||||
if (it == blocks_.end()) {
|
||||
*n = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const mjCAssetData& data = it->second;
|
||||
|
||||
// TODO(kylebayes): This is probably caused by a user bug and needs an
|
||||
// assertion for debugging purposes
|
||||
if (data.nbytes % sizeof(T)) {
|
||||
*n = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*n = data.nbytes / sizeof(T);
|
||||
return reinterpret_cast<T*>(data.bytes.get());
|
||||
}
|
||||
|
||||
template
|
||||
const uint8_t* mjCAsset::Get(const std::string& name, std::size_t* n) const;
|
||||
template
|
||||
const int* mjCAsset::Get(const std::string& name, std::size_t* n) const;
|
||||
template
|
||||
const unsigned int* mjCAsset::Get(const std::string& name, std::size_t* n) const;
|
||||
template
|
||||
const float* mjCAsset::Get(const std::string& name, std::size_t* n) const;
|
||||
template
|
||||
const double* mjCAsset::Get(const std::string& name, std::size_t* n) const;
|
||||
|
||||
|
||||
|
||||
// copies a block of data into a vector
|
||||
template<typename T> std::optional<std::vector<T>>
|
||||
mjCAsset::GetVector(const std::string& name) const {
|
||||
std::size_t n;
|
||||
const T* ptr = Get<T>(name, &n);
|
||||
if (ptr == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::vector<T>(ptr, ptr + n);
|
||||
}
|
||||
|
||||
template std::optional<std::vector<uint8_t>>
|
||||
mjCAsset::GetVector(const std::string& name) const;
|
||||
template std::optional<std::vector<int>>
|
||||
mjCAsset::GetVector(const std::string& name) const;
|
||||
template std::optional<std::vector<unsigned int>>
|
||||
mjCAsset::GetVector(const std::string& name) const;
|
||||
template std::optional<std::vector<float>>
|
||||
mjCAsset::GetVector(const std::string& name) const;
|
||||
template std::optional<std::vector<double>>
|
||||
mjCAsset::GetVector(const std::string& name) const;
|
||||
|
||||
|
||||
|
||||
// replaces blocks data in asset
|
||||
void mjCAsset::ReplaceBlocks(
|
||||
const std::unordered_map<std::string, mjCAssetData>& blocks,
|
||||
std::size_t nbytes) {
|
||||
blocks_ = blocks;
|
||||
nbytes_ = nbytes;
|
||||
}
|
||||
|
||||
#include <mujoco/mjplugin.h>
|
||||
#include "user/user_resource.h"
|
||||
|
||||
|
||||
// makes a copy for user (strip unnecessary items)
|
||||
@@ -152,8 +31,8 @@ mjCAsset mjCAsset::Copy(const mjCAsset& other) {
|
||||
mjCAsset asset;
|
||||
asset.id_ = other.Id();
|
||||
asset.timestamp_ = other.Timestamp();
|
||||
asset.blocks_ = other.blocks_;
|
||||
asset.nbytes_ = other.nbytes_;
|
||||
asset.data_ = other.data_;
|
||||
asset.size_ = other.size_;
|
||||
return asset;
|
||||
}
|
||||
|
||||
@@ -185,97 +64,54 @@ const std::string* mjCCache::HasAsset(const std::string& id) {
|
||||
|
||||
// inserts an asset into the cache, if asset is already in the cache, its data
|
||||
// is updated only if the timestamps disagree
|
||||
bool mjCCache::Insert(const mjCAsset& asset) {
|
||||
bool mjCCache::Insert(const std::string& modelname, const mjResource *resource,
|
||||
std::shared_ptr<const void> data, std::size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
// check if asset is too large to fit in the cache
|
||||
std::size_t nbytes = asset.BytesCount();
|
||||
const std::string& id = asset.Id();
|
||||
if ((size_ + nbytes > max_size_) && lookup_.find(id) == lookup_.end()) {
|
||||
if ((size_ + size > max_size_) &&
|
||||
lookup_.find(resource->name) == lookup_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (asset.References().size() != 1) {
|
||||
return false;
|
||||
}
|
||||
const std::string& filename = *(asset.References().begin());
|
||||
auto [it, inserted] = lookup_.insert({id, asset});
|
||||
mjCAsset asset(modelname, resource, data, size);
|
||||
auto [it, inserted] = lookup_.insert({resource->name, asset});
|
||||
mjCAsset* asset_ptr = &(it->second);
|
||||
|
||||
if (!inserted) {
|
||||
if (size_ - asset_ptr->BytesCount() + nbytes > max_size_) {
|
||||
if (size_ - asset_ptr->BytesCount() + size > max_size_) {
|
||||
return false;
|
||||
}
|
||||
models_[filename].insert(asset_ptr); // add it for the model
|
||||
asset_ptr->AddReference(filename);
|
||||
models_[modelname].insert(asset_ptr); // add it for the model
|
||||
asset_ptr->AddReference(modelname);
|
||||
if (it->second.Timestamp() == asset.Timestamp()) {
|
||||
return true;
|
||||
}
|
||||
asset_ptr->SetTimestamp(asset.Timestamp());
|
||||
size_ = size_ - asset_ptr->BytesCount() + nbytes;
|
||||
asset_ptr->ReplaceBlocks(asset.Blocks(), nbytes);
|
||||
size_ = size_ - asset_ptr->BytesCount() + size;
|
||||
asset_ptr->ReplaceData(asset);
|
||||
return true;
|
||||
}
|
||||
|
||||
// new asset
|
||||
asset_ptr->SetInsertNum(insert_num_++);
|
||||
entries_.insert(asset_ptr);
|
||||
models_[filename].insert(asset_ptr);
|
||||
size_ += nbytes;
|
||||
models_[modelname].insert(asset_ptr);
|
||||
size_ += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool mjCCache::Insert(mjCAsset&& asset) {
|
||||
// populate data from the cache into the given function
|
||||
bool mjCCache::PopulateData(const mjResource* resource, mjCDataFunc fn) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
// check if asset is too large to fit in the cache
|
||||
std::size_t nbytes = asset.BytesCount();
|
||||
const std::string& id = asset.Id();
|
||||
if ((size_ + nbytes > max_size_) && lookup_.find(id) == lookup_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (asset.References().size() != 1) {
|
||||
return false;
|
||||
}
|
||||
const std::string& filename = *(asset.References().begin());
|
||||
auto [it, inserted] = lookup_.try_emplace(id, std::move(asset));
|
||||
mjCAsset* asset_ptr = &(it->second);
|
||||
|
||||
if (!inserted) {
|
||||
if (size_ - asset_ptr->BytesCount() + nbytes > max_size_) {
|
||||
return false;
|
||||
}
|
||||
models_[filename].insert(asset_ptr); // add it for the model
|
||||
asset_ptr->AddReference(std::move(filename));
|
||||
if (it->second.Timestamp() == asset.Timestamp()) {
|
||||
return true;
|
||||
}
|
||||
// move data and timestamp over
|
||||
asset_ptr->SetTimestamp(std::move(asset.timestamp_));
|
||||
size_ = size_ - asset_ptr->BytesCount() + nbytes;
|
||||
asset_ptr->ReplaceBlocks(std::move(asset.blocks_), asset.nbytes_);
|
||||
return true;
|
||||
}
|
||||
|
||||
// new asset
|
||||
asset_ptr->SetInsertNum(insert_num_++);
|
||||
entries_.insert(asset_ptr);
|
||||
models_[filename].insert(asset_ptr);
|
||||
size_ += nbytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// returns the asset with the given id, if it exists in the cache
|
||||
std::optional<mjCAsset> mjCCache::Get(const std::string& id) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto it = lookup_.find(id);
|
||||
auto it = lookup_.find(resource->name);
|
||||
if (it == lookup_.end()) {
|
||||
return std::nullopt;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mju_isModifiedResource(resource, it->second.Timestamp().c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mjCAsset* asset = &(it->second);
|
||||
@@ -284,7 +120,9 @@ std::optional<mjCAsset> mjCCache::Get(const std::string& id) {
|
||||
// update priority queue
|
||||
entries_.erase(asset);
|
||||
entries_.insert(asset);
|
||||
return asset->Copy(*asset);
|
||||
|
||||
asset->PopulateData(fn);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+31
-55
@@ -16,38 +16,34 @@
|
||||
#define MUJOCO_SRC_USER_CACHE_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// data associated with an asset
|
||||
struct mjCAssetData {
|
||||
std::shared_ptr<uint8_t> bytes; // raw serialized bytes of cached data
|
||||
std::size_t nbytes; // number of bytes stored
|
||||
};
|
||||
#include <mujoco/mjplugin.h>
|
||||
|
||||
typedef std::function<void(const void*)> mjCDataFunc;
|
||||
typedef void (*mjCDeallocFunc)(const void*);
|
||||
|
||||
// A class container for a thread-safe asset cache
|
||||
//
|
||||
// Each mjCAsset is used to store raw and/or processed data loaded from a
|
||||
// resource and is defined by a unique ID (usually the full filename of the
|
||||
// asset). The asset's data can be segregated into blocks for ease of use of
|
||||
// mix and matching different types of data. Each block is given a unique name
|
||||
// within the asset for readability. For example, mjCAsset for a mesh may
|
||||
// include all vertex positions, edges, and the computed volume.
|
||||
// asset).
|
||||
class mjCAsset {
|
||||
friend class mjCCache;
|
||||
public:
|
||||
mjCAsset(std::string filename, std::string id, std::string timestamp)
|
||||
: id_(std::move(id)), timestamp_(std::move(timestamp)) {
|
||||
AddReference(filename);
|
||||
mjCAsset(std::string modelname, const mjResource* resource,
|
||||
std::shared_ptr<const void> data, std::size_t size) :
|
||||
id_(resource->name), timestamp_(resource->timestamp),
|
||||
size_(size), data_(std::move(data)) {
|
||||
AddReference(modelname);
|
||||
}
|
||||
|
||||
// move and copy constructors
|
||||
@@ -56,35 +52,16 @@ class mjCAsset {
|
||||
mjCAsset(const mjCAsset& other) = default;
|
||||
mjCAsset& operator=(const mjCAsset& other) = default;
|
||||
|
||||
// copies a block of data into the asset and returns number of bytes stored
|
||||
// loading data into an asset should happen in a single thread
|
||||
template<typename T> std::size_t Add(const std::string& name,
|
||||
const T* data, std::size_t n);
|
||||
|
||||
|
||||
// copies a vector into the asset and returns number of bytes stored
|
||||
// loading data into an asset should happen in a single thread
|
||||
template<typename T> std::size_t AddVector(const std::string& name,
|
||||
const std::vector<T>& v);
|
||||
|
||||
// returns a pointer to a block of data, sets n to size of data
|
||||
template<typename T>
|
||||
const T* Get(const std::string& name, std::size_t* n) const;
|
||||
|
||||
// copies a block of data into a vector
|
||||
template<typename T>
|
||||
std::optional<std::vector<T>> GetVector(const std::string& name) const;
|
||||
|
||||
// returns true if a block of data by the given name is stored in the asset
|
||||
bool HasData(const std::string& name) const {
|
||||
return blocks_.find(name) != blocks_.end();
|
||||
}
|
||||
|
||||
const std::string& Timestamp() const { return timestamp_; }
|
||||
const std::string& Id() const { return id_; }
|
||||
std::size_t InsertNum() const { return insert_num_; }
|
||||
std::size_t AccessCount() const { return access_count_; }
|
||||
|
||||
// pass data in the cache to the given function
|
||||
void PopulateData(mjCDataFunc fn) const {
|
||||
fn(data_.get());
|
||||
}
|
||||
|
||||
private:
|
||||
mjCAsset() = default;
|
||||
|
||||
@@ -93,12 +70,13 @@ class mjCAsset {
|
||||
void RemoveReference(const std::string& xml_file) {
|
||||
references_.erase(xml_file);
|
||||
}
|
||||
bool HasReferences() const { return !references_.empty(); }
|
||||
|
||||
// replaces data blocks in asset
|
||||
void ReplaceBlocks(
|
||||
const std::unordered_map<std::string, mjCAssetData>& blocks,
|
||||
std::size_t nbytes);
|
||||
void ReplaceData(const mjCAsset& other) {
|
||||
data_ = other.data_;
|
||||
size_ = other.size_;
|
||||
}
|
||||
|
||||
bool HasReferences() const { return !references_.empty(); }
|
||||
|
||||
void IncrementAccess() { access_count_++; }
|
||||
|
||||
@@ -110,9 +88,9 @@ class mjCAsset {
|
||||
void SetTimestamp(std::string timestamp) { timestamp_ = timestamp; }
|
||||
|
||||
// accessors
|
||||
std::size_t BytesCount() const { return nbytes_; }
|
||||
const std::unordered_map<std::string, mjCAssetData>& Blocks() const {
|
||||
return blocks_;
|
||||
std::size_t BytesCount() const { return size_; }
|
||||
const void* Data() const {
|
||||
return data_.get();
|
||||
}
|
||||
const std::set<std::string>& References() const { return references_; }
|
||||
|
||||
@@ -120,10 +98,8 @@ class mjCAsset {
|
||||
std::string timestamp_; // opaque timestamp of asset
|
||||
std::size_t insert_num_; // number when asset was inserted
|
||||
std::size_t access_count_ = 0; // incremented when getting 0th block
|
||||
std::size_t nbytes_ = 0; // how many bytes taken up by the asset
|
||||
|
||||
// the actually data of the asset
|
||||
std::unordered_map<std::string, mjCAssetData> blocks_;
|
||||
std::size_t size_ = 0; // how many bytes taken up by the asset
|
||||
std::shared_ptr<const void> data_; // actual data of the asset
|
||||
|
||||
// list of models referencing this asset
|
||||
std::set<std::string> references_;
|
||||
@@ -160,11 +136,11 @@ class mjCCache {
|
||||
|
||||
// inserts an asset into the cache, if asset is already in the cache, its data
|
||||
// is updated only if the timestamps disagree
|
||||
bool Insert(const mjCAsset& asset);
|
||||
bool Insert(mjCAsset&& asset);
|
||||
bool Insert(const std::string& modelname, const mjResource *resource,
|
||||
std::shared_ptr<const void> data, std::size_t size);
|
||||
|
||||
// returns the asset with the given id, if it exists in the cache
|
||||
std::optional<mjCAsset> Get(const std::string& id);
|
||||
// populate data from the cache into the given function
|
||||
bool PopulateData(const mjResource* resource, mjCDataFunc fn);
|
||||
|
||||
// deletes the asset from the cache with the given id
|
||||
void DeleteAsset(const std::string& id);
|
||||
|
||||
+101
-102
@@ -21,13 +21,13 @@
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <mujoco/mjspec.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "user/user_api.h"
|
||||
|
||||
#ifdef MUJOCO_TINYOBJLOADER_IMPL
|
||||
@@ -69,9 +69,6 @@ extern "C" {
|
||||
#include "qhull_ra.h"
|
||||
}
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// compute triangle area, surface normal, center
|
||||
static double _triangle(double* normal, double* center,
|
||||
const float* v1, const float* v2, const float* v3) {
|
||||
@@ -337,7 +334,41 @@ void mjCMesh::LoadSDF() {
|
||||
delete[] field;
|
||||
}
|
||||
|
||||
void mjCMesh::CacheOBJ(mjCCache* cache, const mjResource* resource) {
|
||||
if (cache == nullptr) return;
|
||||
|
||||
// cache mesh data into new mesh object
|
||||
mjCMesh *mesh = new mjCMesh();
|
||||
mesh->vert_ = vert_;
|
||||
mesh->normal_ = normal_;
|
||||
mesh->texcoord_ = texcoord_;
|
||||
mesh->face_ = face_;
|
||||
mesh->facetexcoord_ = facetexcoord_;
|
||||
mesh->facenormal_ = facenormal_;
|
||||
mesh->vertex_index_ = vertex_index_;
|
||||
mesh->normal_index_ = normal_index_;
|
||||
mesh->texcoord_index_ = texcoord_index_;
|
||||
mesh->num_face_vertices_ = num_face_vertices_;
|
||||
|
||||
// calculate estimated size of mesh
|
||||
std::size_t size = sizeof(mjCMesh)
|
||||
+ (sizeof(float) * vert_.size())
|
||||
+ (sizeof(float) * normal_.size())
|
||||
+ (sizeof(float) * texcoord_.size())
|
||||
+ (sizeof(int) * face_.size())
|
||||
+ (sizeof(int) * facetexcoord_.size())
|
||||
+ (sizeof(int) * facenormal_.size())
|
||||
+ (sizeof(int) * vertex_index_.size())
|
||||
+ (sizeof(int) * normal_index_.size())
|
||||
+ (sizeof(int) * texcoord_index_.size())
|
||||
+ (sizeof(unsigned char) * num_face_vertices_.size());
|
||||
|
||||
std::shared_ptr<const void> cached_data(mesh, +[](const void* data) {
|
||||
const mjCMesh* mesh = static_cast<const mjCMesh*>(data);
|
||||
delete mesh;
|
||||
});
|
||||
cache->Insert("", resource, cached_data, size);
|
||||
}
|
||||
|
||||
// compiler
|
||||
void mjCMesh::Compile(const mjVFS* vfs) {
|
||||
@@ -368,14 +399,19 @@ void mjCMesh::Compile(const mjVFS* vfs) {
|
||||
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
||||
}
|
||||
|
||||
string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
||||
mjResource* resource = LoadResource(filename, vfs);
|
||||
|
||||
try {
|
||||
if (asset_type == "model/stl") {
|
||||
LoadSTL(resource);
|
||||
} else if (asset_type == "model/obj") {
|
||||
LoadOBJ(resource);
|
||||
// try loading from cache
|
||||
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_globalCache());
|
||||
if (!cache || !LoadCachedOBJ(cache, resource)) {
|
||||
LoadOBJ(resource);
|
||||
CacheOBJ(cache, resource);
|
||||
}
|
||||
} else {
|
||||
LoadMSH(resource);
|
||||
}
|
||||
@@ -855,18 +891,6 @@ void mjCMesh::LoadOBJ(mjResource* resource) {
|
||||
tinyobj::ObjReader objReader;
|
||||
const void* bytes = nullptr;
|
||||
|
||||
// try loading from cache
|
||||
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_globalCache());
|
||||
if (cache) {
|
||||
auto asset = cache->Get(resource->name);
|
||||
if (asset.has_value() &&
|
||||
!mju_isModifiedResource(resource, asset->Timestamp().c_str())) {
|
||||
if (LoadCachedOBJ(asset.value())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int buffer_sz = mju_readResource(resource, &bytes);
|
||||
if (buffer_sz < 0) {
|
||||
throw mjCError(this, "could not read OBJ file '%s'", resource->name);
|
||||
@@ -933,108 +957,83 @@ void mjCMesh::LoadOBJ(mjResource* resource) {
|
||||
texcoord_[2*i+1] = 1-texcoord_[2*i+1];
|
||||
}
|
||||
|
||||
// try caching asset
|
||||
if (cache) {
|
||||
mjCAsset asset("", resource->name, resource->timestamp);
|
||||
// save some partial data for caching
|
||||
if (!objReader.GetShapes().empty()) {
|
||||
const auto& mesh = objReader.GetShapes()[0].mesh;
|
||||
num_face_vertices_ = mesh.num_face_vertices;
|
||||
|
||||
asset.AddVector("vert_", vert_);
|
||||
asset.AddVector("normal_", normal_);
|
||||
asset.AddVector("texcoord_", texcoord_);
|
||||
vertex_index_.reserve(mesh.indices.size());
|
||||
normal_index_.reserve(mesh.indices.size());
|
||||
texcoord_index_.reserve(mesh.indices.size());
|
||||
|
||||
if (!objReader.GetShapes().empty()) {
|
||||
const auto& mesh = objReader.GetShapes()[0].mesh;
|
||||
std::vector<int> vertex_index;
|
||||
vertex_index.reserve(mesh.indices.size());
|
||||
|
||||
std::vector<int> normal_index;
|
||||
normal_index.reserve(mesh.indices.size());
|
||||
|
||||
std::vector<int> texcoord_index;
|
||||
texcoord_index.reserve(mesh.indices.size());
|
||||
|
||||
for (tinyobj::index_t index : mesh.indices) {
|
||||
vertex_index.push_back(index.vertex_index);
|
||||
normal_index.push_back(index.normal_index);
|
||||
texcoord_index.push_back(index.texcoord_index);
|
||||
}
|
||||
|
||||
asset.AddVector("num_face_vertices", mesh.num_face_vertices);
|
||||
asset.AddVector("vertex_index", vertex_index);
|
||||
asset.AddVector("normal_index", normal_index);
|
||||
asset.AddVector("texcoord_index", texcoord_index);
|
||||
} else {
|
||||
asset.AddVector("num_face_vertices", std::vector<unsigned char>());
|
||||
asset.AddVector("vertex_index", std::vector<int>());
|
||||
asset.AddVector("normal_index", std::vector<int>());
|
||||
asset.AddVector("texcoord_index", std::vector<int>());
|
||||
for (tinyobj::index_t index : mesh.indices) {
|
||||
vertex_index_.push_back(index.vertex_index);
|
||||
normal_index_.push_back(index.normal_index);
|
||||
texcoord_index_.push_back(index.texcoord_index);
|
||||
}
|
||||
cache->Insert(std::move(asset));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// load OBJ from cached asset, return true on success
|
||||
bool mjCMesh::LoadCachedOBJ(const mjCAsset& asset) {
|
||||
bool mjCMesh::LoadCachedOBJ(mjCCache *cache, const mjResource* resource) {
|
||||
// check that asset has all data
|
||||
if (!asset.HasData("vert_") || !asset.HasData("normal_")
|
||||
|| !asset.HasData("texcoord_") || !asset.HasData("num_face_vertices")
|
||||
|| !asset.HasData("vertex_index") || !asset.HasData("normal_index")
|
||||
|| !asset.HasData("texcoord_index")) {
|
||||
if (!cache->PopulateData(resource, [&](const void* data) {
|
||||
const mjCMesh* mesh = static_cast<const mjCMesh*>(data);
|
||||
vert_ = mesh->vert_;
|
||||
normal_ = mesh->normal_;
|
||||
texcoord_ = mesh->texcoord_;
|
||||
vertex_index_ = mesh->vertex_index_;
|
||||
normal_index_ = mesh->normal_index_;
|
||||
texcoord_index_ = mesh->texcoord_index_;
|
||||
num_face_vertices_ = mesh->num_face_vertices_;
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
vert_ = asset.GetVector<float>("vert_").value();
|
||||
normal_ = asset.GetVector<float>("normal_").value();
|
||||
texcoord_ = asset.GetVector<float>("texcoord_").value();
|
||||
|
||||
vector<int> vertex_index = asset.GetVector<int>("vertex_index").value();
|
||||
vector<int> normal_index = asset.GetVector<int>("normal_index").value();
|
||||
vector<int> texcoord_index = asset.GetVector<int>("texcoord_index").value();
|
||||
vector<unsigned char> num_face_vertices =
|
||||
asset.GetVector<unsigned char>("num_face_vertices").value();
|
||||
|
||||
bool righthand = (scale[0] * scale[1] * scale[2]) > 0;
|
||||
|
||||
|
||||
for (int face = 0, i = 0; i < vertex_index.size();) {
|
||||
int nfacevert = num_face_vertices[face];
|
||||
for (int face = 0, i = 0; i < vertex_index_.size();) {
|
||||
int nfacevert = num_face_vertices_[face];
|
||||
if (nfacevert < 3 || nfacevert > 4) {
|
||||
throw mjCError(
|
||||
this, "only tri or quad meshes are supported for OBJ (file '%s')",
|
||||
asset.Id().c_str());
|
||||
resource->name);
|
||||
}
|
||||
|
||||
face_.push_back(vertex_index[i]);
|
||||
face_.push_back(vertex_index[i + (righthand == 1 ? 1 : 2)]);
|
||||
face_.push_back(vertex_index[i + (righthand == 1 ? 2 : 1)]);
|
||||
face_.push_back(vertex_index_[i]);
|
||||
face_.push_back(vertex_index_[i + (righthand == 1 ? 1 : 2)]);
|
||||
face_.push_back(vertex_index_[i + (righthand == 1 ? 2 : 1)]);
|
||||
|
||||
if (!normal_.empty()) {
|
||||
facenormal_.push_back(normal_index[i]);
|
||||
facenormal_.push_back(normal_index[i + (righthand == 1 ? 1 : 2)]);
|
||||
facenormal_.push_back(normal_index[i + (righthand == 1 ? 2 : 1)]);
|
||||
facenormal_.push_back(normal_index_[i]);
|
||||
facenormal_.push_back(normal_index_[i + (righthand == 1 ? 1 : 2)]);
|
||||
facenormal_.push_back(normal_index_[i + (righthand == 1 ? 2 : 1)]);
|
||||
}
|
||||
|
||||
if (!texcoord_.empty()) {
|
||||
facetexcoord_.push_back(texcoord_index[i]);
|
||||
facetexcoord_.push_back(texcoord_index[i + (righthand == 1 ? 1 : 2)]);
|
||||
facetexcoord_.push_back(texcoord_index[i + (righthand == 1 ? 2 : 1)]);
|
||||
facetexcoord_.push_back(texcoord_index_[i]);
|
||||
facetexcoord_.push_back(texcoord_index_[i + (righthand == 1 ? 1 : 2)]);
|
||||
facetexcoord_.push_back(texcoord_index_[i + (righthand == 1 ? 2 : 1)]);
|
||||
}
|
||||
|
||||
if (nfacevert == 4) {
|
||||
face_.push_back(vertex_index[i]);
|
||||
face_.push_back(vertex_index[i + (righthand == 1 ? 2 : 3)]);
|
||||
face_.push_back(vertex_index[i + (righthand == 1 ? 3 : 2)]);
|
||||
face_.push_back(vertex_index_[i]);
|
||||
face_.push_back(vertex_index_[i + (righthand == 1 ? 2 : 3)]);
|
||||
face_.push_back(vertex_index_[i + (righthand == 1 ? 3 : 2)]);
|
||||
|
||||
if (!normal_.empty()) {
|
||||
facenormal_.push_back(normal_index[i]);
|
||||
facenormal_.push_back(normal_index[i + (righthand == 1 ? 1 : 2)]);
|
||||
facenormal_.push_back(normal_index[i + (righthand == 1 ? 2 : 1)]);
|
||||
facenormal_.push_back(normal_index_[i]);
|
||||
facenormal_.push_back(normal_index_[i + (righthand == 1 ? 1 : 2)]);
|
||||
facenormal_.push_back(normal_index_[i + (righthand == 1 ? 2 : 1)]);
|
||||
}
|
||||
|
||||
if (!texcoord_.empty()) {
|
||||
facetexcoord_.push_back(texcoord_index[i]);
|
||||
facetexcoord_.push_back(texcoord_index[i + (righthand == 1 ? 1 : 2)]);
|
||||
facetexcoord_.push_back(texcoord_index[i + (righthand == 1 ? 2 : 1)]);
|
||||
facetexcoord_.push_back(texcoord_index_[i]);
|
||||
facetexcoord_.push_back(texcoord_index_[i + (righthand == 1 ? 1 : 2)]);
|
||||
facetexcoord_.push_back(texcoord_index_[i + (righthand == 1 ? 2 : 1)]);
|
||||
}
|
||||
}
|
||||
i += nfacevert;
|
||||
@@ -2086,12 +2085,12 @@ void mjCSkin::Compile(const mjVFS* vfs) {
|
||||
}
|
||||
|
||||
// load SKN
|
||||
string ext = mjuu_getext(file_);
|
||||
std::string ext = mjuu_getext(file_);
|
||||
if (strcasecmp(ext.c_str(), ".skn")) {
|
||||
throw mjCError(this, "Unknown skin file type: %s", file_.c_str());
|
||||
}
|
||||
|
||||
string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
||||
mjResource* resource = LoadResource(filename, vfs);
|
||||
|
||||
try {
|
||||
@@ -2152,7 +2151,7 @@ void mjCSkin::Compile(const mjVFS* vfs) {
|
||||
}
|
||||
|
||||
// set total vertex weights to 0
|
||||
vector<float> vw;
|
||||
std::vector<float> vw;
|
||||
size_t nvert = vert_.size()/3;
|
||||
vw.resize(nvert);
|
||||
fill(vw.begin(), vw.end(), 0.0f);
|
||||
@@ -2520,7 +2519,7 @@ void mjCFlex::Compile(const mjVFS* vfs) {
|
||||
// process elements
|
||||
for (int e=0; e<(int)elem_.size()/(dim+1); e++) {
|
||||
// make sorted copy of element
|
||||
vector<int> el;
|
||||
std::vector<int> el;
|
||||
el.assign(elem_.begin()+e*(dim+1), elem_.begin()+(e+1)*(dim+1));
|
||||
std::sort(el.begin(), el.end());
|
||||
|
||||
@@ -2555,7 +2554,7 @@ void mjCFlex::Compile(const mjVFS* vfs) {
|
||||
}
|
||||
|
||||
// compute global vertex positions
|
||||
vertxpos = vector<double> (3*nvert);
|
||||
vertxpos = std::vector<double> (3*nvert);
|
||||
for (int i=0; i < nvert; i++) {
|
||||
// get body id, set vertxpos = body.xpos0
|
||||
int b = rigid ? vertbodyid[0] : vertbodyid[i];
|
||||
@@ -2702,17 +2701,17 @@ void mjCFlex::CreateBVH(void) {
|
||||
|
||||
// create shells and element-vertex collision pairs
|
||||
void mjCFlex::CreateShellPair(void) {
|
||||
vector<vector<int>> fragspec(nelem*(dim+1)); // [sorted frag vertices, elem, original frag vertices]
|
||||
vector<vector<int>> connectspec; // [elem1, elem2, common sorted frag vertices]
|
||||
vector<bool> border(nelem, false); // is element on the border
|
||||
vector<bool> borderfrag(nelem*(dim+1), false); // is fragment on the border
|
||||
std::vector<std::vector<int>> fragspec(nelem*(dim+1)); // [sorted frag vertices, elem, original frag vertices]
|
||||
std::vector<std::vector<int>> connectspec; // [elem1, elem2, common sorted frag vertices]
|
||||
std::vector<bool> border(nelem, false); // is element on the border
|
||||
std::vector<bool> borderfrag(nelem*(dim+1), false); // is fragment on the border
|
||||
|
||||
// make fragspec
|
||||
for (int e=0; e<nelem; e++) {
|
||||
int n = e*(dim+1);
|
||||
|
||||
// element vertices in original (unsorted) order
|
||||
vector<int> el;
|
||||
std::vector<int> el;
|
||||
el.assign(elem_.begin()+n, elem_.begin()+n+dim+1);
|
||||
|
||||
// line: 2 vertex fragments
|
||||
@@ -2797,13 +2796,13 @@ void mjCFlex::CreateShellPair(void) {
|
||||
int cnt = 1;
|
||||
for (int n=1; n<nelem*(dim+1); n++) {
|
||||
// extract frag vertices, without elem
|
||||
vector<int> previous = {fragspec[n-1].begin(), fragspec[n-1].begin()+dim};
|
||||
vector<int> current = {fragspec[n].begin(), fragspec[n].begin()+dim};
|
||||
std::vector<int> previous = {fragspec[n-1].begin(), fragspec[n-1].begin()+dim};
|
||||
std::vector<int> current = {fragspec[n].begin(), fragspec[n].begin()+dim};
|
||||
|
||||
// same sequential fragments
|
||||
if (previous==current) {
|
||||
// found pair of elements connected by common fragment
|
||||
vector<int> connect;
|
||||
std::vector<int> connect;
|
||||
connect.insert(connect.end(), fragspec[n-1][dim]);
|
||||
connect.insert(connect.end(), fragspec[n][dim]);
|
||||
connect.insert(connect.end(), fragspec[n].begin(), fragspec[n].begin()+dim);
|
||||
@@ -2843,10 +2842,10 @@ void mjCFlex::CreateShellPair(void) {
|
||||
|
||||
// compute elemlayer (distance from border) via value iteration in 3D
|
||||
if (dim<3) {
|
||||
elemlayer = vector<int> (nelem, 0);
|
||||
elemlayer = std::vector<int> (nelem, 0);
|
||||
}
|
||||
else {
|
||||
elemlayer = vector<int> (nelem, nelem+1); // init with greater than max value
|
||||
elemlayer = std::vector<int> (nelem, nelem+1); // init with greater than max value
|
||||
for (int e=0; e<nelem; e++) {
|
||||
if (border[e]) {
|
||||
elemlayer[e] = 0; // set border elements to 0
|
||||
@@ -2878,7 +2877,7 @@ void mjCFlex::CreateShellPair(void) {
|
||||
for (const auto& connect : connectspec) {
|
||||
if (border[connect[0]] || border[connect[1]]) {
|
||||
// extract common fragment
|
||||
vector<int> frag = {connect.begin()+2, connect.end()};
|
||||
std::vector<int> frag = {connect.begin()+2, connect.end()};
|
||||
|
||||
// process both elements
|
||||
for (int ei=0; ei < 2; ei++) {
|
||||
|
||||
+108
-108
@@ -18,10 +18,12 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
@@ -47,10 +49,92 @@
|
||||
|
||||
namespace {
|
||||
namespace mju = ::mujoco::util;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
class PNGImage {
|
||||
public:
|
||||
static PNGImage Load(const mjCBase* obj, mjResource* resource,
|
||||
LodePNGColorType color_type);
|
||||
int Width() const { return width_; }
|
||||
int Height() const { return height_; }
|
||||
uint8_t operator[] (int i) const { return data_[i]; }
|
||||
std::vector<unsigned char>& MoveData() { return data_; }
|
||||
|
||||
private:
|
||||
std::size_t Size() const {
|
||||
return data_.size() + (3 * sizeof(int));
|
||||
}
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
LodePNGColorType color_type_;
|
||||
std::vector<uint8_t> data_;
|
||||
};
|
||||
|
||||
PNGImage PNGImage::Load(const mjCBase* obj, mjResource* resource,
|
||||
LodePNGColorType color_type) {
|
||||
PNGImage image;
|
||||
image.color_type_ = color_type;
|
||||
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_globalCache());
|
||||
|
||||
// try loading from cache
|
||||
if (cache && cache->PopulateData(resource, [&image](const void* data) {
|
||||
const PNGImage *cached_image = static_cast<const PNGImage*>(data);
|
||||
if (cached_image->color_type_ == image.color_type_) {
|
||||
image = *cached_image;
|
||||
}
|
||||
})) {
|
||||
if (!image.data_.empty()) return image;
|
||||
}
|
||||
|
||||
// open PNG resource
|
||||
const unsigned char* buffer;
|
||||
int nbuffer = mju_readResource(resource, (const void**) &buffer);
|
||||
|
||||
if (nbuffer < 0) {
|
||||
throw mjCError(obj, "could not read PNG file '%s'", resource->name);
|
||||
}
|
||||
|
||||
if (!nbuffer) {
|
||||
throw mjCError(obj, "empty PNG file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// decode PNG from buffer
|
||||
unsigned int w, h;
|
||||
unsigned err = lodepng::decode(image.data_, w, h,
|
||||
buffer, nbuffer, image.color_type_, 8);
|
||||
|
||||
// check for errors
|
||||
if (err) {
|
||||
std::stringstream ss;
|
||||
ss << "error decoding PNG file '" << resource->name << "': " << lodepng_error_text(err);
|
||||
throw mjCError(obj, "%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
image.width_ = w;
|
||||
image.height_ = h;
|
||||
|
||||
if (image.width_ <= 0 || image.height_ < 0) {
|
||||
std::stringstream ss;
|
||||
ss << "error decoding PNG file '" << resource->name << "': " << "dimensions are invalid";
|
||||
throw mjCError(obj, "%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
// insert raw image data into cache
|
||||
if (cache) {
|
||||
PNGImage *cached_image = new PNGImage(image);;
|
||||
std::size_t size = image.Size();
|
||||
std::shared_ptr<const void> cached_data(cached_image, +[](const void* data) {
|
||||
delete static_cast<const PNGImage*>(data);
|
||||
});
|
||||
cache->Insert("", resource, cached_data, size);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// utiility function for checking size parameters
|
||||
static void checksize(double* size, mjtGeom type, mjCBase* object, const char* name, int id) {
|
||||
// plane: handle infinite
|
||||
@@ -91,92 +175,6 @@ static bool islimited(int limited, const double range[2]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// fetches cached image from PNG asset, returns nullopt if not available
|
||||
static std::optional<std::vector<unsigned char>>
|
||||
LoadCachedPNG(mjCAsset& asset, unsigned& w, unsigned& h, LodePNGColorType color_type) {
|
||||
if (!asset.HasData("dims") || !asset.HasData("image")) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<unsigned int> dims
|
||||
= asset.GetVector<unsigned int>("dims").value();
|
||||
auto maybe_cached_image = asset.GetVector<unsigned char>("image");
|
||||
|
||||
if (dims.size() != 3) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if ((maybe_cached_image->size() != dims[0] * dims[1]) || (color_type != dims[2])) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
w = dims[0];
|
||||
h = dims[1];
|
||||
|
||||
return maybe_cached_image;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// decodes PNG images from the given resource
|
||||
std::vector<unsigned char> LoadPNG(const mjCBase* obj, mjResource* resource,
|
||||
unsigned& w, unsigned& h, LodePNGColorType color_type) {
|
||||
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_globalCache());
|
||||
|
||||
// try loading from cache
|
||||
if (cache) {
|
||||
auto asset = cache->Get(resource->name);
|
||||
if (asset.has_value() &&
|
||||
!mju_isModifiedResource(resource, asset->Timestamp().c_str())) {
|
||||
auto maybe_cached_image = LoadCachedPNG(asset.value(), w, h, color_type);
|
||||
if (maybe_cached_image.has_value()) {
|
||||
return maybe_cached_image.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// open PNG resource
|
||||
const unsigned char* buffer;
|
||||
int buffer_sz = mju_readResource(resource, (const void**) &buffer);
|
||||
|
||||
if (buffer_sz < 0) {
|
||||
throw mjCError(obj, "could not read PNG file '%s'", resource->name);
|
||||
}
|
||||
|
||||
if (!buffer_sz) {
|
||||
throw mjCError(obj, "empty PNG file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// decode PNG from buffer
|
||||
std::vector<unsigned char> image;
|
||||
unsigned err = lodepng::decode(image, w, h, buffer, buffer_sz, color_type, 8);
|
||||
|
||||
// check for errors
|
||||
if (err) {
|
||||
std::stringstream ss;
|
||||
ss << "error decoding PNG file '" << resource->name << "': " << lodepng_error_text(err);
|
||||
throw mjCError(obj, "%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
if (!w || !h) {
|
||||
throw mjCError(obj, "error decoding PNG file '%s': zero dimension", resource->name);
|
||||
}
|
||||
|
||||
// insert raw image data into cache
|
||||
if (cache) {
|
||||
mjCAsset asset("", resource->name, resource->timestamp);
|
||||
asset.AddVector("dims", std::vector<unsigned>{w, h, static_cast<unsigned int>(color_type)});
|
||||
asset.AddVector("image", image);
|
||||
cache->Insert(std::move(asset));
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------- class mjCError implementation ------------------------------------------
|
||||
|
||||
// constructor
|
||||
@@ -705,7 +703,7 @@ void mjCBase::NameSpace(const mjCModel* m) {
|
||||
|
||||
|
||||
// load resource if found (fallback to OS filesystem)
|
||||
mjResource* mjCBase::LoadResource(string filename, const mjVFS* vfs) {
|
||||
mjResource* mjCBase::LoadResource(std::string filename, const mjVFS* vfs) {
|
||||
// try reading from provided VFS
|
||||
mjResource* r = mju_openVfsResource(filename.c_str(), vfs);
|
||||
|
||||
@@ -1200,7 +1198,7 @@ mjCBase* mjCBody::GetObject(mjtObj type, int id) {
|
||||
|
||||
// find object by name in given list
|
||||
template <class T>
|
||||
static T* findobject(string name, vector<T*>& list) {
|
||||
static T* findobject(std::string name, std::vector<T*>& list) {
|
||||
for (unsigned int i=0; i<list.size(); i++) {
|
||||
if (list[i]->name == name) {
|
||||
return list[i];
|
||||
@@ -1213,7 +1211,7 @@ static T* findobject(string name, vector<T*>& list) {
|
||||
|
||||
|
||||
// recursive find by name
|
||||
mjCBase* mjCBody::FindObject(mjtObj type, string _name, bool recursive) {
|
||||
mjCBase* mjCBody::FindObject(mjtObj type, std::string _name, bool recursive) {
|
||||
mjCBase* res = 0;
|
||||
|
||||
// check self: just in case
|
||||
@@ -1308,7 +1306,7 @@ void mjCBody::GeomFrame(void) {
|
||||
int sz;
|
||||
double com[3] = {0, 0, 0};
|
||||
double toti[6] = {0, 0, 0, 0, 0, 0};
|
||||
vector<mjCGeom*> sel;
|
||||
std::vector<mjCGeom*> sel;
|
||||
|
||||
// select geoms based on group
|
||||
sel.clear();
|
||||
@@ -3103,11 +3101,10 @@ void mjCHField::LoadCustom(mjResource* resource) {
|
||||
|
||||
// load elevation data from PNG format
|
||||
void mjCHField::LoadPNG(mjResource* resource) {
|
||||
unsigned w, h;
|
||||
std::vector<unsigned char> image = ::LoadPNG(this, resource, w, h, LCT_GREY);
|
||||
PNGImage image = PNGImage::Load(this, resource, LCT_GREY);
|
||||
|
||||
ncol = w;
|
||||
nrow = h;
|
||||
ncol = image.Width();
|
||||
nrow = image.Height();
|
||||
|
||||
// copy image data over with rows reversed
|
||||
data.reserve(nrow * ncol);
|
||||
@@ -3161,7 +3158,7 @@ void mjCHField::Compile(const mjVFS* vfs) {
|
||||
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
||||
}
|
||||
|
||||
string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
||||
mjResource* resource = LoadResource(filename, vfs);
|
||||
|
||||
try {
|
||||
@@ -3525,7 +3522,10 @@ void mjCTexture::BuiltinCube(void) {
|
||||
void mjCTexture::LoadPNG(mjResource* resource,
|
||||
std::vector<unsigned char>& image,
|
||||
unsigned int& w, unsigned int& h) {
|
||||
image = ::LoadPNG(this, resource, w, h, LCT_RGB);
|
||||
PNGImage png_image = PNGImage::Load(this, resource, LCT_RGB);
|
||||
w = png_image.Width();
|
||||
h = png_image.Height();
|
||||
image = png_image.MoveData();
|
||||
}
|
||||
|
||||
|
||||
@@ -3570,7 +3570,7 @@ void mjCTexture::LoadCustom(mjResource* resource,
|
||||
|
||||
|
||||
// load from PNG or custom file, flip if specified
|
||||
void mjCTexture::LoadFlip(string filename, const mjVFS* vfs,
|
||||
void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs,
|
||||
std::vector<unsigned char>& image,
|
||||
unsigned int& w, unsigned int& h) {
|
||||
std::string asset_type = GetAssetContentType(filename, content_type_);
|
||||
@@ -3646,7 +3646,7 @@ void mjCTexture::LoadFlip(string filename, const mjVFS* vfs,
|
||||
|
||||
|
||||
// load 2D
|
||||
void mjCTexture::Load2D(string filename, const mjVFS* vfs) {
|
||||
void mjCTexture::Load2D(std::string filename, const mjVFS* vfs) {
|
||||
// load PNG or custom
|
||||
unsigned int w, h;
|
||||
std::vector<unsigned char> image;
|
||||
@@ -3669,7 +3669,7 @@ void mjCTexture::Load2D(string filename, const mjVFS* vfs) {
|
||||
|
||||
|
||||
// load cube or skybox from single file (repeated or grid)
|
||||
void mjCTexture::LoadCubeSingle(string filename, const mjVFS* vfs) {
|
||||
void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
|
||||
// check gridsize
|
||||
if (gridsize[0]<1 || gridsize[1]<1 || gridsize[0]*gridsize[1]>12) {
|
||||
throw mjCError(this, "gridsize must be non-zero and no more than 12 squares in texture");
|
||||
@@ -3779,7 +3779,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
||||
}
|
||||
|
||||
// make filename
|
||||
string filename = mjuu_combinePaths(model->modelfiledir_, model->texturedir_, cubefiles_[i]);
|
||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->texturedir_, cubefiles_[i]);
|
||||
|
||||
// load PNG or custom
|
||||
unsigned int w, h;
|
||||
@@ -3877,7 +3877,7 @@ void mjCTexture::Compile(const mjVFS* vfs) {
|
||||
}
|
||||
|
||||
// make filename
|
||||
string filename = mjuu_combinePaths(model->modelfiledir_, model->texturedir_, file_);
|
||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->texturedir_, file_);
|
||||
|
||||
// dispatch
|
||||
if (type==mjTEXTURE_2D) {
|
||||
@@ -4113,7 +4113,7 @@ void mjCPair::ResolveReferences(const mjCModel* m) {
|
||||
|
||||
// swap if body1 > body2
|
||||
if (geom1->body->id > geom2->body->id) {
|
||||
string nametmp = geomname1_;
|
||||
std::string nametmp = geomname1_;
|
||||
geomname1_ = geomname2_;
|
||||
geomname2_ = nametmp;
|
||||
|
||||
@@ -4337,7 +4337,7 @@ void mjCBodyPair::ResolveReferences(const mjCModel* m) {
|
||||
|
||||
// swap if body1 > body2
|
||||
if (pb1->id > pb2->id) {
|
||||
string nametmp = bodyname1_;
|
||||
std::string nametmp = bodyname1_;
|
||||
bodyname1_ = bodyname2_;
|
||||
bodyname2_ = nametmp;
|
||||
|
||||
@@ -4639,7 +4639,7 @@ void mjCTendon::SetModel(mjCModel* _model) {
|
||||
|
||||
|
||||
// add site as wrap object
|
||||
void mjCTendon::WrapSite(string name, std::string_view info) {
|
||||
void mjCTendon::WrapSite(std::string name, std::string_view info) {
|
||||
// create wrap object
|
||||
mjCWrap* wrap = new mjCWrap(model, this);
|
||||
wrap->info = info;
|
||||
@@ -4654,7 +4654,7 @@ void mjCTendon::WrapSite(string name, std::string_view info) {
|
||||
|
||||
|
||||
// add geom (with side site) as wrap object
|
||||
void mjCTendon::WrapGeom(string name, string sidesite, std::string_view info) {
|
||||
void mjCTendon::WrapGeom(std::string name, std::string sidesite, std::string_view info) {
|
||||
// create wrap object
|
||||
mjCWrap* wrap = new mjCWrap(model, this);
|
||||
wrap->info = info;
|
||||
@@ -4670,7 +4670,7 @@ void mjCTendon::WrapGeom(string name, string sidesite, std::string_view info) {
|
||||
|
||||
|
||||
// add joint as wrap object
|
||||
void mjCTendon::WrapJoint(string name, double coef, std::string_view info) {
|
||||
void mjCTendon::WrapJoint(std::string name, double coef, std::string_view info) {
|
||||
// create wrap object
|
||||
mjCWrap* wrap = new mjCWrap(model, this);
|
||||
wrap->info = info;
|
||||
|
||||
+10
-1
@@ -846,7 +846,10 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
||||
|
||||
private:
|
||||
void LoadOBJ(mjResource* resource); // load mesh in wavefront OBJ format
|
||||
bool LoadCachedOBJ(const mjCAsset& asset); // load OBJ from cache asset, return true on success
|
||||
// load OBJ from cache asset, return true on success
|
||||
bool LoadCachedOBJ(mjCCache *cache, const mjResource* resource);
|
||||
// put OBJ into asset cache
|
||||
void CacheOBJ(mjCCache *cache, const mjResource* resource);
|
||||
void LoadSTL(mjResource* resource); // load mesh in STL BIN format
|
||||
void LoadMSH(mjResource* resource); // load mesh in MSH BIN format
|
||||
void LoadSDF(); // generate mesh using marching cubes
|
||||
@@ -864,6 +867,12 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
||||
double* center_; // face circumcenter data (3*nface)
|
||||
int* graph_; // convex graph data
|
||||
|
||||
// for caching purposes
|
||||
std::vector<int> vertex_index_;
|
||||
std::vector<int> normal_index_;
|
||||
std::vector<int> texcoord_index_;
|
||||
std::vector<unsigned char> num_face_vertices_;
|
||||
|
||||
// compute the volume and center-of-mass of the mesh given the face center
|
||||
void ComputeVolume(double CoM[3], mjtGeomInertia type, const double facecen[3],
|
||||
bool exactmeshinertia);
|
||||
|
||||
+94
-208
@@ -14,19 +14,21 @@
|
||||
|
||||
// Tests for user/user_cache.cc
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "test/fixture.h"
|
||||
#include "src/user/user_cache.h"
|
||||
#include "src/user/user_vfs.h"
|
||||
#include "src/user/user_resource.h"
|
||||
|
||||
namespace mujoco {
|
||||
|
||||
using ::testing::ElementsAreArray;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::StrEq;
|
||||
@@ -36,165 +38,82 @@ using CacheTest = MujocoTest;
|
||||
namespace {
|
||||
|
||||
constexpr int kMaxSize = 100; // in bytes
|
||||
constexpr std::string kText = "Hello World";
|
||||
constexpr std::string kModel = "myModel";
|
||||
constexpr std::string kFile = "hello.txt";
|
||||
|
||||
void CacheText(mjCCache& cache, const std::string& model,
|
||||
const std::string& name, const std::string& text) {
|
||||
mjVFS vfs;
|
||||
mj_defaultVFS(&vfs);
|
||||
mj_addBufferVFS(&vfs, name.c_str(), text.data(), text.size());
|
||||
mjResource* resource = mju_openVfsResource(name.c_str(), &vfs);
|
||||
std::shared_ptr<const void> data(&text, +[](const void* data) {});
|
||||
cache.Insert(model, resource, data, text.size());
|
||||
mju_closeResource(resource);
|
||||
mj_deleteVFS(&vfs);
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
GetCachedText(mjCCache& cache, const std::string& model,
|
||||
const std::string& name, const std::string& text) {
|
||||
std::string cached_text;
|
||||
mjVFS vfs;
|
||||
mj_defaultVFS(&vfs);
|
||||
mj_addBufferVFS(&vfs, name.c_str(), text.data(), std::strlen(text.c_str()));
|
||||
mjResource* resource = mju_openVfsResource(name.c_str(), &vfs);
|
||||
bool inserted = cache.PopulateData(resource,
|
||||
[&cached_text](const void* data) {
|
||||
cached_text = *(static_cast<const std::string*>(data));
|
||||
});
|
||||
mju_closeResource(resource);
|
||||
mj_deleteVFS(&vfs);
|
||||
return inserted ? std::optional<std::string>(cached_text) : std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
TEST(CacheTest, SizeTest) {
|
||||
mjCCache cache(kMaxSize);
|
||||
EXPECT_EQ(cache.Size(), 0);
|
||||
}
|
||||
|
||||
TEST(CacheTest, HasAssetSuccessTest) {
|
||||
TEST(CacheTest, InsertSuccess) {
|
||||
mjCCache cache(kMaxSize);
|
||||
CacheText(cache, kModel, kFile, kText);
|
||||
auto cached_text = GetCachedText(cache, kModel, kFile, kText);
|
||||
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
cache.Insert(asset);
|
||||
|
||||
EXPECT_THAT(*(cache.HasAsset("foo.obj")), StrEq("now"));
|
||||
EXPECT_THAT(cached_text.value(), StrEq(kText));
|
||||
}
|
||||
|
||||
TEST(CacheTest, HasAssetFailureTest) {
|
||||
TEST(CacheTest, InsertFailure) {
|
||||
mjCCache cache(kMaxSize);
|
||||
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
cache.Insert(asset);
|
||||
|
||||
EXPECT_THAT(cache.HasAsset("file2.xml"), nullptr);
|
||||
CacheText(cache, kModel, kFile, kText);
|
||||
auto cached_text = GetCachedText(cache, kModel, "hello2.txt", kText);
|
||||
EXPECT_EQ(cached_text, std::nullopt);
|
||||
}
|
||||
|
||||
TEST(CacheTest, AddSuccessTest) {
|
||||
TEST(CacheTest, InsertReplace) {
|
||||
mjCCache cache(kMaxSize);
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<double> v2 = {1.0, 2.0, 3.0};
|
||||
const std::string kUpdatedText = "Goodbye World";
|
||||
CacheText(cache, kModel, kFile, kText);
|
||||
CacheText(cache, kModel, kFile, kUpdatedText);
|
||||
auto cached_text = GetCachedText(cache, kModel, kFile, kUpdatedText);
|
||||
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
std::size_t nbytes1 = asset.AddVector("v1", v1);
|
||||
std::size_t nbytes2 = asset.AddVector("v2", v2);
|
||||
cache.Insert(asset);
|
||||
|
||||
ASSERT_EQ(nbytes1, 12);
|
||||
ASSERT_EQ(nbytes2, 24);
|
||||
ASSERT_EQ(cache.Size(), 36);
|
||||
}
|
||||
|
||||
TEST(CacheTest, AddFailureTest) {
|
||||
mjCCache cache(kMaxSize);
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<double> v2 = {1.0, 2.0, 3.0};
|
||||
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
asset.AddVector("v1", v1);
|
||||
std::size_t nbytes = asset.AddVector("v1", v2);
|
||||
cache.Insert(asset);
|
||||
|
||||
ASSERT_EQ(nbytes, 0);
|
||||
ASSERT_EQ(cache.Size(), 12);
|
||||
}
|
||||
|
||||
TEST(CacheTest, InsertReplaceTest) {
|
||||
mjCCache cache(kMaxSize);
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<double> v2 = {1.0, 2.0, 3.0};
|
||||
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
asset.AddVector("v", v1);
|
||||
cache.Insert(asset);
|
||||
|
||||
mjCAsset asset2("file.xml", "foo.obj", "nower");
|
||||
asset2.AddVector("v", v2);
|
||||
bool inserted = cache.Insert(asset2);
|
||||
EXPECT_TRUE(inserted);
|
||||
|
||||
mjCAsset asset3 = *(cache.Get("foo.obj"));
|
||||
std::vector<double> v3 = asset3.GetVector<double>("v").value();
|
||||
EXPECT_THAT(v3, ElementsAreArray(v2));
|
||||
|
||||
ASSERT_EQ(cache.Size(), 24);
|
||||
}
|
||||
|
||||
TEST(CacheTest, MoveInsertNewTest) {
|
||||
mjCCache cache(kMaxSize);
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<double> v2 = {1.0, 2.0, 3.0};
|
||||
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
std::size_t nbytes1 = asset.AddVector("v1", v1);
|
||||
std::size_t nbytes2 = asset.AddVector("v2", v2);
|
||||
cache.Insert(std::move(asset));
|
||||
|
||||
ASSERT_EQ(nbytes1, 12);
|
||||
ASSERT_EQ(nbytes2, 24);
|
||||
ASSERT_EQ(cache.Size(), 36);
|
||||
}
|
||||
|
||||
TEST(CacheTest, MoveInsertReplaceTest) {
|
||||
mjCCache cache(kMaxSize);
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<double> v2 = {1.0, 2.0, 3.0};
|
||||
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
asset.AddVector("v", v1);
|
||||
cache.Insert(std::move(asset));
|
||||
|
||||
mjCAsset asset2("file.xml", "foo.obj", "nower");
|
||||
asset2.AddVector("v", v2);
|
||||
bool inserted = cache.Insert(std::move(asset2));
|
||||
EXPECT_TRUE(inserted);
|
||||
|
||||
mjCAsset asset3 = *(cache.Get("foo.obj"));
|
||||
std::vector<double> v3 = asset3.GetVector<double>("v").value();
|
||||
EXPECT_THAT(v3, ElementsAreArray(v2));
|
||||
|
||||
ASSERT_EQ(cache.Size(), 24);
|
||||
}
|
||||
|
||||
TEST(CacheTest, GetSuccessTest) {
|
||||
mjCCache cache(kMaxSize);
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<double> v2 = {4.0, 5.0, 6.0};
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
asset.AddVector("v1", v1);
|
||||
asset.AddVector("v2", v2);
|
||||
cache.Insert(asset);
|
||||
|
||||
mjCAsset asset2 = *(cache.Get("foo.obj"));
|
||||
|
||||
std::vector<int> v3 = asset2.GetVector<int>("v1").value();
|
||||
EXPECT_THAT(v3, ElementsAreArray(v1));
|
||||
|
||||
std::vector<double> v4 = asset2.GetVector<double>("v2").value();
|
||||
EXPECT_THAT(v4, ElementsAreArray(v2));
|
||||
}
|
||||
|
||||
TEST(CacheTest, GetFailueTest) {
|
||||
mjCCache cache(kMaxSize);
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
asset.AddVector("v", v);
|
||||
cache.Insert(asset);
|
||||
|
||||
mjCAsset asset2 = *(cache.Get("foo.obj"));
|
||||
EXPECT_EQ(cache.Get("bar.obj").has_value(), false);
|
||||
|
||||
auto v2 = asset2.GetVector<int>("v2");
|
||||
EXPECT_EQ(v2.has_value(), false);
|
||||
EXPECT_THAT(cached_text.value(), StrEq(kUpdatedText));
|
||||
}
|
||||
|
||||
// Trim cache based off of access count
|
||||
TEST(CacheTest, LimitTest1) {
|
||||
TEST(CacheTest, Limit1) {
|
||||
mjCCache cache(kMaxSize);
|
||||
EXPECT_THAT(cache.MaxSize(), kMaxSize);
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
|
||||
mjCAsset asset1 = mjCAsset("file.xml", "foo.obj", "now");
|
||||
mjCAsset asset2 = mjCAsset("file.xml", "bar.obj", "now");
|
||||
asset1.AddVector("foo.obj", v);
|
||||
asset2.AddVector("bar.obj", v);
|
||||
cache.Insert(asset1);
|
||||
cache.Insert(asset2);
|
||||
CacheText(cache, "fil.xml", "foo.obj", kText);
|
||||
CacheText(cache, "fil.xml", "bar.obj", kText);
|
||||
|
||||
// access asset foo twice, bar one
|
||||
cache.Get("foo.obj");
|
||||
cache.Get("foo.obj");
|
||||
cache.Get("bar.obj");
|
||||
GetCachedText(cache, "file.xml", "foo.obj", kText);
|
||||
GetCachedText(cache, "file.xml", "foo.obj", kText);
|
||||
GetCachedText(cache, "file.xml", "bar.obj", kText);
|
||||
|
||||
// make max size so cache can hold only one asset
|
||||
cache.SetMaxSize(12);
|
||||
@@ -206,21 +125,16 @@ TEST(CacheTest, LimitTest1) {
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), IsNull());
|
||||
}
|
||||
|
||||
// Trim cache based off of insert order
|
||||
TEST(CacheTest, LimitTest2) {
|
||||
mjCCache cache(kMaxSize);
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
mjCAsset asset1("file.xml", "foo.obj", "now");
|
||||
mjCAsset asset2("file.xml", "bar.obj", "now");
|
||||
asset1.AddVector("v", v);
|
||||
asset2.AddVector("v", v);
|
||||
|
||||
cache.Insert(asset1);
|
||||
cache.Insert(asset2);
|
||||
// Trim cache based off of insert order
|
||||
TEST(CacheTest, Limit2) {
|
||||
mjCCache cache(kMaxSize);
|
||||
CacheText(cache, "file.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file.xml", "bar.obj", kText);
|
||||
|
||||
// get each asset once
|
||||
mjCAsset asset3 = *(cache.Get("foo.obj"));
|
||||
mjCAsset asset4 = *(cache.Get("bar.obj"));
|
||||
GetCachedText(cache, "file.xml", "foo.obj", kText);
|
||||
GetCachedText(cache, "file.xml", "bar.obj", kText);
|
||||
|
||||
// make max size so cache can hold only one asset
|
||||
cache.SetMaxSize(12);
|
||||
@@ -233,17 +147,10 @@ TEST(CacheTest, LimitTest2) {
|
||||
}
|
||||
|
||||
// stress test with large asset
|
||||
TEST(CacheTest, LimitTest3) {
|
||||
TEST(CacheTest, Limit3) {
|
||||
mjCCache cache(12);
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<int> v2 = {1, 2, 3, 4, 5};
|
||||
mjCAsset asset1("file.xml", "foo.obj", "now");
|
||||
mjCAsset asset2("file.xml", "bar.obj", "now");
|
||||
asset1.AddVector("v", v1);
|
||||
asset2.AddVector("v", v2);
|
||||
|
||||
cache.Insert(std::move(asset1));
|
||||
cache.Insert(std::move(asset2));
|
||||
CacheText(cache, "file.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file.xml", "bar.obj", kText);
|
||||
|
||||
// foo should still be in cache
|
||||
EXPECT_THAT(cache.HasAsset("foo.obj"), NotNull());
|
||||
@@ -252,16 +159,10 @@ TEST(CacheTest, LimitTest3) {
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), IsNull());
|
||||
}
|
||||
|
||||
TEST(CacheTest, LimitTest4) {
|
||||
TEST(CacheTest, Limit4) {
|
||||
mjCCache cache(12);
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
mjCAsset asset1("file.xml", "foo.obj", "now");
|
||||
mjCAsset asset2("file.xml", "bar.obj", "now");
|
||||
asset1.AddVector("v", v);
|
||||
asset2.AddVector("v", v);
|
||||
|
||||
cache.Insert(std::move(asset1));
|
||||
cache.Insert(std::move(asset2));
|
||||
CacheText(cache, "file.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file.xml", "bar.obj", kText);
|
||||
|
||||
EXPECT_THAT(cache.HasAsset("foo.obj"), NotNull());
|
||||
|
||||
@@ -269,12 +170,10 @@ TEST(CacheTest, LimitTest4) {
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), IsNull());
|
||||
}
|
||||
|
||||
TEST(CacheTest, ResetAllTest) {
|
||||
TEST(CacheTest, ResetAll) {
|
||||
mjCCache cache(kMaxSize);
|
||||
mjCAsset asset1("file1.xml", "foo.obj", "now");
|
||||
mjCAsset asset2("file2.xml", "bar.obj", "now");
|
||||
cache.Insert(asset1);
|
||||
cache.Insert(asset2);
|
||||
CacheText(cache, "file1.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file2.xml", "bar.obj", kText);
|
||||
|
||||
EXPECT_THAT(cache.HasAsset("foo.obj"), NotNull());
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), NotNull());
|
||||
@@ -285,14 +184,11 @@ TEST(CacheTest, ResetAllTest) {
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), IsNull());
|
||||
}
|
||||
|
||||
TEST(CacheTest, ResetModelTest1) {
|
||||
TEST(CacheTest, ResetModel1) {
|
||||
mjCCache cache(kMaxSize);
|
||||
mjCAsset asset("file1.xml", "foo.obj", "now");
|
||||
mjCAsset asset2("file1.xml", "bar.obj", "now");
|
||||
mjCAsset asset3("file2.xml", "bar.obj", "now");
|
||||
cache.Insert(asset);
|
||||
cache.Insert(asset2);
|
||||
cache.Insert(asset3);
|
||||
CacheText(cache, "file1.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file1.xml", "bar.obj", kText);
|
||||
CacheText(cache, "file2.xml", "bar.obj", kText);
|
||||
|
||||
cache.Reset("file2.xml");
|
||||
|
||||
@@ -300,14 +196,11 @@ TEST(CacheTest, ResetModelTest1) {
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), IsNull());
|
||||
}
|
||||
|
||||
TEST(CacheTest, ResetModelTest2) {
|
||||
TEST(CacheTest, ResetModel2) {
|
||||
mjCCache cache(kMaxSize);
|
||||
mjCAsset asset("file1.xml", "foo.obj", "now");
|
||||
mjCAsset asset2("file2.xml", "foo.obj", "now");
|
||||
mjCAsset asset3("file2.xml", "bar.obj", "now");
|
||||
cache.Insert(asset);
|
||||
cache.Insert(asset2);
|
||||
cache.Insert(asset3);
|
||||
CacheText(cache, "file1.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file2.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file2.xml", "bar.obj", kText);
|
||||
|
||||
cache.Reset("file2.xml");
|
||||
|
||||
@@ -315,14 +208,11 @@ TEST(CacheTest, ResetModelTest2) {
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), IsNull());
|
||||
}
|
||||
|
||||
TEST(CacheTest, RemoveModelTest1) {
|
||||
TEST(CacheTest, RemoveModel1) {
|
||||
mjCCache cache(kMaxSize);
|
||||
mjCAsset asset("file1.xml", "foo.obj", "now");
|
||||
mjCAsset asset2("file1.xml", "bar.obj", "now");
|
||||
mjCAsset asset3("file2.xml", "bar.obj", "now");
|
||||
cache.Insert(asset);
|
||||
cache.Insert(asset2);
|
||||
cache.Insert(asset3);
|
||||
CacheText(cache, "file1.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file1.xml", "bar.obj", kText);
|
||||
CacheText(cache, "file2.xml", "bar.obj", kText);
|
||||
|
||||
cache.RemoveModel("file2.xml");
|
||||
|
||||
@@ -330,12 +220,10 @@ TEST(CacheTest, RemoveModelTest1) {
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), NotNull());
|
||||
}
|
||||
|
||||
TEST(CacheTest, RemoveModelTest2) {
|
||||
TEST(CacheTest, RemoveModel2) {
|
||||
mjCCache cache(kMaxSize);
|
||||
mjCAsset asset("file1.xml", "foo.obj", "now");
|
||||
mjCAsset asset2("file2.xml", "bar.obj", "now");
|
||||
cache.Insert(asset);
|
||||
cache.Insert(asset2);
|
||||
CacheText(cache, "file1.xml", "foo.obj", kText);
|
||||
CacheText(cache, "file2.xml", "bar.obj", kText);
|
||||
|
||||
cache.Reset("file2.xml");
|
||||
|
||||
@@ -343,20 +231,18 @@ TEST(CacheTest, RemoveModelTest2) {
|
||||
EXPECT_THAT(cache.HasAsset("bar.obj"), IsNull());
|
||||
}
|
||||
|
||||
TEST(CacheTest, DeleteAssetSuccessTest) {
|
||||
TEST(CacheTest, DeleteAssetSuccess) {
|
||||
mjCCache cache(kMaxSize);
|
||||
mjCAsset asset("file1.xml", "foo.obj", "now");
|
||||
cache.Insert(asset);
|
||||
CacheText(cache, "file.xml", "foo.obj", kText);
|
||||
|
||||
cache.DeleteAsset("foo.obj");
|
||||
|
||||
EXPECT_THAT(cache.HasAsset("foo.obj"), IsNull());
|
||||
}
|
||||
|
||||
TEST(CacheTest, DeleteAssetFailureTest) {
|
||||
TEST(CacheTest, DeleteAssetFailure) {
|
||||
mjCCache cache(kMaxSize);
|
||||
mjCAsset asset("file.xml", "foo.obj", "now");
|
||||
cache.Insert(asset);
|
||||
CacheText(cache, "file.xml", "foo.obj", kText);
|
||||
|
||||
cache.DeleteAsset("bar.obj");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user