Add option to mjspec for creating a texture directly from a buffer.
PiperOrigin-RevId: 662186527 Change-Id: I469c876681d52964ac971c0bb2ec3517224d3a3d
This commit is contained in:
committed by
Copybara-Service
parent
20cae556ee
commit
851bb6eef4
@@ -4417,6 +4417,15 @@ Safely cast an element as mjsMaterial, or return NULL if the element is not an m
|
|||||||
|
|
||||||
Attribute setters
|
Attribute setters
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
|
.. _mjs_setBuffer:
|
||||||
|
|
||||||
|
mjs_setBuffer
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. mujoco-include:: mjs_setBuffer
|
||||||
|
|
||||||
|
Copy buffer.
|
||||||
|
|
||||||
.. _mjs_setString:
|
.. _mjs_setString:
|
||||||
|
|
||||||
mjs_setString
|
mjs_setString
|
||||||
|
|||||||
+8
-1
@@ -5,6 +5,13 @@ Changelog
|
|||||||
Upcoming version (not yet released)
|
Upcoming version (not yet released)
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
General
|
||||||
|
^^^^^^^
|
||||||
|
1. Add :ref:`mjSpec` option for creating a texture from a buffer.
|
||||||
|
|
||||||
|
Upcoming version (not yet released)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
General
|
General
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
- :ref:`shellinertia <body-geom-shellinertia>` is now supported by all geom types.
|
- :ref:`shellinertia <body-geom-shellinertia>` is now supported by all geom types.
|
||||||
@@ -22,7 +29,7 @@ Version 3.2.1 (Aug 5, 2024)
|
|||||||
|
|
||||||
General
|
General
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
1. Renamed ``mjModel.tex_rbg`` to ``mjModel.tex_data``.
|
1. Renamed ``mjModel.tex_rgb`` to ``mjModel.tex_data``.
|
||||||
2. Added a new :ref:`autoreset<option-flag-autoreset>` flag to disable automatic reset when NaNs or infinities are
|
2. Added a new :ref:`autoreset<option-flag-autoreset>` flag to disable automatic reset when NaNs or infinities are
|
||||||
detected.
|
detected.
|
||||||
3. Added sub-elements to the MJCF :ref:`material<asset-material>` element, to allow specification of multiple textures
|
3. Added sub-elements to the MJCF :ref:`material<asset-material>` element, to allow specification of multiple textures
|
||||||
|
|||||||
@@ -2026,6 +2026,9 @@ typedef struct mjsTexture_ { // texture specification
|
|||||||
// method 3: separate files
|
// method 3: separate files
|
||||||
mjStringVec* cubefiles; // different file for each side of the cube
|
mjStringVec* cubefiles; // different file for each side of the cube
|
||||||
|
|
||||||
|
// method 4: from buffer read by user
|
||||||
|
mjBuffer* data; // texture data
|
||||||
|
|
||||||
// flip options
|
// flip options
|
||||||
mjtByte hflip; // horizontal flip
|
mjtByte hflip; // horizontal flip
|
||||||
mjtByte vflip; // vertical flip
|
mjtByte vflip; // vertical flip
|
||||||
@@ -3584,6 +3587,7 @@ mjsHField* mjs_asHField(mjsElement* element);
|
|||||||
mjsSkin* mjs_asSkin(mjsElement* element);
|
mjsSkin* mjs_asSkin(mjsElement* element);
|
||||||
mjsTexture* mjs_asTexture(mjsElement* element);
|
mjsTexture* mjs_asTexture(mjsElement* element);
|
||||||
mjsMaterial* mjs_asMaterial(mjsElement* element);
|
mjsMaterial* mjs_asMaterial(mjsElement* element);
|
||||||
|
void mjs_setBuffer(mjBuffer* dest, const void* array, int size);
|
||||||
void mjs_setString(mjString* dest, const char* text);
|
void mjs_setString(mjString* dest, const char* text);
|
||||||
void mjs_setStringVec(mjStringVec* dest, const char* text);
|
void mjs_setStringVec(mjStringVec* dest, const char* text);
|
||||||
mjtByte mjs_setInStringVec(mjStringVec* dest, int i, const char* text);
|
mjtByte mjs_setInStringVec(mjStringVec* dest, int i, const char* text);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
// this is a C-API
|
// this is a C-API
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ extern "C" {
|
|||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
// C++: defined to be compatible with corresponding std types
|
// C++: defined to be compatible with corresponding std types
|
||||||
|
using mjBuffer = std::vector<std::byte>;
|
||||||
using mjString = std::string;
|
using mjString = std::string;
|
||||||
using mjStringVec = std::vector<std::string>;
|
using mjStringVec = std::vector<std::string>;
|
||||||
using mjIntVec = std::vector<int>;
|
using mjIntVec = std::vector<int>;
|
||||||
@@ -41,6 +43,7 @@ extern "C" {
|
|||||||
using mjDoubleVec = std::vector<double>;
|
using mjDoubleVec = std::vector<double>;
|
||||||
#else
|
#else
|
||||||
// C: opaque types
|
// C: opaque types
|
||||||
|
typedef void mjBuffer;
|
||||||
typedef void mjString;
|
typedef void mjString;
|
||||||
typedef void mjStringVec;
|
typedef void mjStringVec;
|
||||||
typedef void mjIntVec;
|
typedef void mjIntVec;
|
||||||
@@ -511,6 +514,9 @@ typedef struct mjsTexture_ { // texture specification
|
|||||||
// method 3: separate files
|
// method 3: separate files
|
||||||
mjStringVec* cubefiles; // different file for each side of the cube
|
mjStringVec* cubefiles; // different file for each side of the cube
|
||||||
|
|
||||||
|
// method 4: from buffer read by user
|
||||||
|
mjBuffer* data; // texture data
|
||||||
|
|
||||||
// flip options
|
// flip options
|
||||||
mjtByte hflip; // horizontal flip
|
mjtByte hflip; // horizontal flip
|
||||||
mjtByte vflip; // vertical flip
|
mjtByte vflip; // vertical flip
|
||||||
|
|||||||
@@ -1629,6 +1629,9 @@ MJAPI mjsMaterial* mjs_asMaterial(mjsElement* element);
|
|||||||
|
|
||||||
//---------------------------------- Attribute setters ---------------------------------------------
|
//---------------------------------- Attribute setters ---------------------------------------------
|
||||||
|
|
||||||
|
// Copy buffer.
|
||||||
|
MJAPI void mjs_setBuffer(mjBuffer* dest, const void* array, int size);
|
||||||
|
|
||||||
// Copy text to string.
|
// Copy text to string.
|
||||||
MJAPI void mjs_setString(mjString* dest, const char* text);
|
MJAPI void mjs_setString(mjString* dest, const char* text);
|
||||||
|
|
||||||
|
|||||||
@@ -10245,6 +10245,30 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
|
|||||||
),
|
),
|
||||||
doc='Safely cast an element as mjsMaterial, or return NULL if the element is not an mjsMaterial.', # pylint: disable=line-too-long
|
doc='Safely cast an element as mjsMaterial, or return NULL if the element is not an mjsMaterial.', # pylint: disable=line-too-long
|
||||||
)),
|
)),
|
||||||
|
('mjs_setBuffer',
|
||||||
|
FunctionDecl(
|
||||||
|
name='mjs_setBuffer',
|
||||||
|
return_type=ValueType(name='void'),
|
||||||
|
parameters=(
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='dest',
|
||||||
|
type=PointerType(
|
||||||
|
inner_type=ValueType(name='mjBuffer'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='array',
|
||||||
|
type=PointerType(
|
||||||
|
inner_type=ValueType(name='void', is_const=True),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='size',
|
||||||
|
type=ValueType(name='int'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
doc='Copy buffer.',
|
||||||
|
)),
|
||||||
('mjs_setString',
|
('mjs_setString',
|
||||||
FunctionDecl(
|
FunctionDecl(
|
||||||
name='mjs_setString',
|
name='mjs_setString',
|
||||||
|
|||||||
@@ -10011,6 +10011,13 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
|||||||
),
|
),
|
||||||
doc='different file for each side of the cube',
|
doc='different file for each side of the cube',
|
||||||
),
|
),
|
||||||
|
StructFieldDecl(
|
||||||
|
name='data',
|
||||||
|
type=PointerType(
|
||||||
|
inner_type=ValueType(name='mjBuffer'),
|
||||||
|
),
|
||||||
|
doc='texture data',
|
||||||
|
),
|
||||||
StructFieldDecl(
|
StructFieldDecl(
|
||||||
name='hflip',
|
name='hflip',
|
||||||
type=ValueType(name='mjtByte'),
|
type=ValueType(name='mjtByte'),
|
||||||
|
|||||||
@@ -146,6 +146,24 @@ def _ptr_binding_code(
|
|||||||
self.{fullvarname}->push_back(py::cast<{vartype}>(val));
|
self.{fullvarname}->push_back(py::cast<{vartype}>(val));
|
||||||
}}
|
}}
|
||||||
}}, py::return_value_policy::reference_internal);"""
|
}}, py::return_value_policy::reference_internal);"""
|
||||||
|
elif vartype == 'mjBuffer': # C++ buffer -> Python list
|
||||||
|
return f"""\
|
||||||
|
{classname}.def_property(
|
||||||
|
"{varname}",
|
||||||
|
[]({rawclassname}& self) -> py::list {{
|
||||||
|
py::list list;
|
||||||
|
for (auto val : *self.{fullvarname}) {{
|
||||||
|
list.append(val);
|
||||||
|
}}
|
||||||
|
return list;
|
||||||
|
}},
|
||||||
|
[]({rawclassname}& self, py::object rhs) {{
|
||||||
|
self.{fullvarname}->clear();
|
||||||
|
self.{fullvarname}->reserve(py::len(rhs));
|
||||||
|
for (auto val : rhs) {{
|
||||||
|
self.{fullvarname}->push_back(py::cast<const std::byte>(val));
|
||||||
|
}}
|
||||||
|
}}, py::return_value_policy::reference_internal);"""
|
||||||
elif vartype == 'mjStringVec': # C++ vector of strings -> Python list
|
elif vartype == 'mjStringVec': # C++ vector of strings -> Python list
|
||||||
return f"""\
|
return f"""\
|
||||||
{classname}.def_property(
|
{classname}.def_property(
|
||||||
|
|||||||
@@ -14,8 +14,10 @@
|
|||||||
|
|
||||||
#include "user/user_api.h"
|
#include "user/user_api.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -877,6 +879,16 @@ mjsMaterial* mjs_asMaterial(mjsElement* element) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// copy buffer to destination buffer
|
||||||
|
void mjs_setBuffer(mjBuffer* dest, const void* array, int size) {
|
||||||
|
const std::byte* buffer = static_cast<const std::byte*>(array);
|
||||||
|
dest->clear();
|
||||||
|
dest->reserve(size);
|
||||||
|
std::copy_n(buffer, size, std::back_inserter(*dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// set string
|
// set string
|
||||||
void mjs_setString(mjString* dest, const char* text) {
|
void mjs_setString(mjString* dest, const char* text) {
|
||||||
std::string* str = static_cast<std::string*>(dest);
|
std::string* str = static_cast<std::string*>(dest);
|
||||||
|
|||||||
@@ -302,6 +302,9 @@ MJAPI mjsMaterial* mjs_asMaterial(mjsElement* element);
|
|||||||
|
|
||||||
//---------------------------------- Attribute setters ---------------------------------------------
|
//---------------------------------- Attribute setters ---------------------------------------------
|
||||||
|
|
||||||
|
// Copy buffer.
|
||||||
|
MJAPI void mjs_setBuffer(mjBuffer* dest, const void* array, int size);
|
||||||
|
|
||||||
// Copy text to string.
|
// Copy text to string.
|
||||||
MJAPI void mjs_setString(mjString* dest, const char* text);
|
MJAPI void mjs_setString(mjString* dest, const char* text);
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <csetjmp>
|
#include <csetjmp>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
@@ -2573,7 +2574,7 @@ void mjCModel::CopyObjects(mjModel* m) {
|
|||||||
m->tex_adr[i] = data_adr;
|
m->tex_adr[i] = data_adr;
|
||||||
|
|
||||||
// copy rgb data
|
// copy rgb data
|
||||||
memcpy(m->tex_data + data_adr, ptex->data.data(),
|
memcpy(m->tex_data + data_adr, ptex->data_.data(),
|
||||||
ptex->nchannel * ptex->width * ptex->height);
|
ptex->nchannel * ptex->width * ptex->height);
|
||||||
|
|
||||||
// advance counter
|
// advance counter
|
||||||
|
|||||||
+82
-65
@@ -3459,7 +3459,8 @@ mjCTexture::mjCTexture(mjCModel* _model) {
|
|||||||
spec_cubefiles_.assign(6, "");
|
spec_cubefiles_.assign(6, "");
|
||||||
|
|
||||||
// clear internal variables
|
// clear internal variables
|
||||||
data.clear();
|
data_.clear();
|
||||||
|
clear_data_ = false;
|
||||||
|
|
||||||
// point to local
|
// point to local
|
||||||
PointToLocal();
|
PointToLocal();
|
||||||
@@ -3480,6 +3481,7 @@ mjCTexture& mjCTexture::operator=(const mjCTexture& other) {
|
|||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
this->spec = other.spec;
|
this->spec = other.spec;
|
||||||
*static_cast<mjCTexture_*>(this) = static_cast<const mjCTexture_&>(other);
|
*static_cast<mjCTexture_*>(this) = static_cast<const mjCTexture_&>(other);
|
||||||
|
clear_data_ = other.clear_data_;
|
||||||
}
|
}
|
||||||
PointToLocal();
|
PointToLocal();
|
||||||
return *this;
|
return *this;
|
||||||
@@ -3491,6 +3493,7 @@ void mjCTexture::PointToLocal() {
|
|||||||
spec.element = static_cast<mjsElement*>(this);
|
spec.element = static_cast<mjsElement*>(this);
|
||||||
spec.name = &name;
|
spec.name = &name;
|
||||||
spec.file = &spec_file_;
|
spec.file = &spec_file_;
|
||||||
|
spec.data = &data_;
|
||||||
spec.content_type = &spec_content_type_;
|
spec.content_type = &spec_content_type_;
|
||||||
spec.cubefiles = &spec_cubefiles_;
|
spec.cubefiles = &spec_cubefiles_;
|
||||||
spec.info = &info;
|
spec.info = &info;
|
||||||
@@ -3507,21 +3510,23 @@ void mjCTexture::CopyFromSpec() {
|
|||||||
content_type_ = spec_content_type_;
|
content_type_ = spec_content_type_;
|
||||||
cubefiles_ = spec_cubefiles_;
|
cubefiles_ = spec_cubefiles_;
|
||||||
|
|
||||||
// clear precompiled asset. TODO: use asset cache
|
if (clear_data_) {
|
||||||
data.clear();
|
// clear precompiled asset. TODO: use asset cache
|
||||||
|
data_.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// free data storage allocated by lodepng
|
// free data storage allocated by lodepng
|
||||||
mjCTexture::~mjCTexture() {
|
mjCTexture::~mjCTexture() {
|
||||||
data.clear();
|
data_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// insert random dots
|
// insert random dots
|
||||||
static void randomdot(unsigned char* rgb, const double* markrgb,
|
static void randomdot(std::byte* rgb, const double* markrgb,
|
||||||
int width, int height, double probability) {
|
int width, int height, double probability) {
|
||||||
// make distribution using fixed seed
|
// make distribution using fixed seed
|
||||||
std::mt19937_64 rng;
|
std::mt19937_64 rng;
|
||||||
@@ -3533,7 +3538,7 @@ static void randomdot(unsigned char* rgb, const double* markrgb,
|
|||||||
for (int c=0; c<width; c++) {
|
for (int c=0; c<width; c++) {
|
||||||
if (dist(rng)<probability) {
|
if (dist(rng)<probability) {
|
||||||
for (int j=0; j<3; j++) {
|
for (int j=0; j<3; j++) {
|
||||||
rgb[3*(r*width+c)+j] = (mjtByte)(255*markrgb[j]);
|
rgb[3*(r*width+c)+j] = (std::byte)(255*markrgb[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3543,7 +3548,7 @@ static void randomdot(unsigned char* rgb, const double* markrgb,
|
|||||||
|
|
||||||
|
|
||||||
// interpolate between colors based on value in (-1, +1)
|
// interpolate between colors based on value in (-1, +1)
|
||||||
static void interp(unsigned char* rgb, const double* rgb1, const double* rgb2, double pos) {
|
static void interp(std::byte* rgb, const double* rgb1, const double* rgb2, double pos) {
|
||||||
const double correction = 1.0/sqrt(2);
|
const double correction = 1.0/sqrt(2);
|
||||||
double alpha = 0.5*(1 + pos/sqrt(1+pos*pos)/correction);
|
double alpha = 0.5*(1 + pos/sqrt(1+pos*pos)/correction);
|
||||||
if (alpha<0) {
|
if (alpha<0) {
|
||||||
@@ -3553,14 +3558,14 @@ static void interp(unsigned char* rgb, const double* rgb1, const double* rgb2, d
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int j=0; j<3; j++) {
|
for (int j=0; j<3; j++) {
|
||||||
rgb[j] = (mjtByte)(255*(alpha*rgb1[j] + (1-alpha)*rgb2[j]));
|
rgb[j] = (std::byte)(255*(alpha*rgb1[j] + (1-alpha)*rgb2[j]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// make checker pattern for one side
|
// make checker pattern for one side
|
||||||
static void checker(unsigned char* rgb, const unsigned char* RGB1, const unsigned char* RGB2,
|
static void checker(std::byte* rgb, const std::byte* RGB1, const std::byte* RGB2,
|
||||||
int width, int height) {
|
int width, int height) {
|
||||||
for (int r=0; r<height/2; r++) {
|
for (int r=0; r<height/2; r++) {
|
||||||
for (int c=0; c<width/2; c++) {
|
for (int c=0; c<width/2; c++) {
|
||||||
@@ -3588,12 +3593,12 @@ static void checker(unsigned char* rgb, const unsigned char* RGB1, const unsigne
|
|||||||
|
|
||||||
// make builtin: 2D
|
// make builtin: 2D
|
||||||
void mjCTexture::Builtin2D(void) {
|
void mjCTexture::Builtin2D(void) {
|
||||||
unsigned char RGB1[3], RGB2[3], RGBm[3];
|
std::byte RGB1[3], RGB2[3], RGBm[3];
|
||||||
// convert fixed colors
|
// convert fixed colors
|
||||||
for (int j=0; j<3; j++) {
|
for (int j=0; j<3; j++) {
|
||||||
RGB1[j] = (mjtByte)(255*rgb1[j]);
|
RGB1[j] = (std::byte)(255*rgb1[j]);
|
||||||
RGB2[j] = (mjtByte)(255*rgb2[j]);
|
RGB2[j] = (std::byte)(255*rgb2[j]);
|
||||||
RGBm[j] = (mjtByte)(255*markrgb[j]);
|
RGBm[j] = (std::byte)(255*markrgb[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------ face
|
//------------------ face
|
||||||
@@ -3608,21 +3613,21 @@ void mjCTexture::Builtin2D(void) {
|
|||||||
double pos = 2*sqrt(x*x+y*y) - 1;
|
double pos = 2*sqrt(x*x+y*y) - 1;
|
||||||
|
|
||||||
// interpolate through sigmoid
|
// interpolate through sigmoid
|
||||||
interp(data.data() + 3*(r*width+c), rgb2, rgb1, pos);
|
interp(data_.data() + 3*(r*width+c), rgb2, rgb1, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checker
|
// checker
|
||||||
else if (builtin==mjBUILTIN_CHECKER) {
|
else if (builtin==mjBUILTIN_CHECKER) {
|
||||||
checker(data.data(), RGB1, RGB2, width, height);
|
checker(data_.data(), RGB1, RGB2, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// flat
|
// flat
|
||||||
else if (builtin==mjBUILTIN_FLAT) {
|
else if (builtin==mjBUILTIN_FLAT) {
|
||||||
for (int r=0; r<height; r++) {
|
for (int r=0; r<height; r++) {
|
||||||
for (int c=0; c<width; c++) {
|
for (int c=0; c<width; c++) {
|
||||||
memcpy(data.data()+3*(r*width+c), RGB1, 3);
|
memcpy(data_.data()+3*(r*width+c), RGB1, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3632,28 +3637,28 @@ void mjCTexture::Builtin2D(void) {
|
|||||||
// edge
|
// edge
|
||||||
if (mark==mjMARK_EDGE) {
|
if (mark==mjMARK_EDGE) {
|
||||||
for (int r=0; r<height; r++) {
|
for (int r=0; r<height; r++) {
|
||||||
memcpy(data.data()+3*(r*width+0), RGBm, 3);
|
memcpy(data_.data()+3*(r*width+0), RGBm, 3);
|
||||||
memcpy(data.data()+3*(r*width+width-1), RGBm, 3);
|
memcpy(data_.data()+3*(r*width+width-1), RGBm, 3);
|
||||||
}
|
}
|
||||||
for (int c=0; c<width; c++) {
|
for (int c=0; c<width; c++) {
|
||||||
memcpy(data.data()+3*(0*width+c), RGBm, 3);
|
memcpy(data_.data()+3*(0*width+c), RGBm, 3);
|
||||||
memcpy(data.data()+3*((height-1)*width+c), RGBm, 3);
|
memcpy(data_.data()+3*((height-1)*width+c), RGBm, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cross
|
// cross
|
||||||
else if (mark==mjMARK_CROSS) {
|
else if (mark==mjMARK_CROSS) {
|
||||||
for (int r=0; r<height; r++) {
|
for (int r=0; r<height; r++) {
|
||||||
memcpy(data.data()+3*(r*width+width/2), RGBm, 3);
|
memcpy(data_.data()+3*(r*width+width/2), RGBm, 3);
|
||||||
}
|
}
|
||||||
for (int c=0; c<width; c++) {
|
for (int c=0; c<width; c++) {
|
||||||
memcpy(data.data()+3*(height/2*width+c), RGBm, 3);
|
memcpy(data_.data()+3*(height/2*width+c), RGBm, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// random dots
|
// random dots
|
||||||
else if (mark==mjMARK_RANDOM && random>0) {
|
else if (mark==mjMARK_RANDOM && random>0) {
|
||||||
randomdot(data.data(), markrgb, width, height, random);
|
randomdot(data_.data(), markrgb, width, height, random);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3661,7 +3666,7 @@ void mjCTexture::Builtin2D(void) {
|
|||||||
|
|
||||||
// make builtin: Cube
|
// make builtin: Cube
|
||||||
void mjCTexture::BuiltinCube(void) {
|
void mjCTexture::BuiltinCube(void) {
|
||||||
unsigned char RGB1[3], RGB2[3], RGBm[3], RGBi[3];
|
std::byte RGB1[3], RGB2[3], RGBm[3], RGBi[3];
|
||||||
int w = width;
|
int w = width;
|
||||||
if (w > std::numeric_limits<int>::max() / w) {
|
if (w > std::numeric_limits<int>::max() / w) {
|
||||||
throw mjCError(this, "Cube texture width is too large.");
|
throw mjCError(this, "Cube texture width is too large.");
|
||||||
@@ -3670,9 +3675,9 @@ void mjCTexture::BuiltinCube(void) {
|
|||||||
|
|
||||||
// convert fixed colors
|
// convert fixed colors
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
RGB1[j] = (mjtByte)(255 * rgb1[j]);
|
RGB1[j] = (std::byte)(255 * rgb1[j]);
|
||||||
RGB2[j] = (mjtByte)(255 * rgb2[j]);
|
RGB2[j] = (std::byte)(255 * rgb2[j]);
|
||||||
RGBm[j] = (mjtByte)(255 * markrgb[j]);
|
RGBm[j] = (std::byte)(255 * markrgb[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------ faces
|
//------------------ faces
|
||||||
@@ -3694,26 +3699,26 @@ void mjCTexture::BuiltinCube(void) {
|
|||||||
|
|
||||||
// set sides
|
// set sides
|
||||||
interp(RGBi, rgb1, rgb2, elside);
|
interp(RGBi, rgb1, rgb2, elside);
|
||||||
memcpy(data.data() + 0 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 0: right
|
memcpy(data_.data() + 0 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 0: right
|
||||||
memcpy(data.data() + 1 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 1: left
|
memcpy(data_.data() + 1 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 1: left
|
||||||
memcpy(data.data() + 4 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 4: front
|
memcpy(data_.data() + 4 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 4: front
|
||||||
memcpy(data.data() + 5 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 5: back
|
memcpy(data_.data() + 5 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 5: back
|
||||||
|
|
||||||
// set up and down
|
// set up and down
|
||||||
interp(data.data() + 2 * 3 * ww + 3 * (r * w + c), rgb1, rgb2, elup); // 2: up
|
interp(data_.data() + 2 * 3 * ww + 3 * (r * w + c), rgb1, rgb2, elup); // 2: up
|
||||||
interp(data.data() + 3 * 3 * ww + 3 * (r * w + c), rgb1, rgb2, -elup); // 3: down
|
interp(data_.data() + 3 * 3 * ww + 3 * (r * w + c), rgb1, rgb2, -elup); // 3: down
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checker
|
// checker
|
||||||
else if (builtin == mjBUILTIN_CHECKER) {
|
else if (builtin == mjBUILTIN_CHECKER) {
|
||||||
checker(data.data() + 0 * 3 * ww, RGB1, RGB2, w, w);
|
checker(data_.data() + 0 * 3 * ww, RGB1, RGB2, w, w);
|
||||||
checker(data.data() + 1 * 3 * ww, RGB1, RGB2, w, w);
|
checker(data_.data() + 1 * 3 * ww, RGB1, RGB2, w, w);
|
||||||
checker(data.data() + 2 * 3 * ww, RGB1, RGB2, w, w);
|
checker(data_.data() + 2 * 3 * ww, RGB1, RGB2, w, w);
|
||||||
checker(data.data() + 3 * 3 * ww, RGB1, RGB2, w, w);
|
checker(data_.data() + 3 * 3 * ww, RGB1, RGB2, w, w);
|
||||||
checker(data.data() + 4 * 3 * ww, RGB2, RGB1, w, w);
|
checker(data_.data() + 4 * 3 * ww, RGB2, RGB1, w, w);
|
||||||
checker(data.data() + 5 * 3 * ww, RGB2, RGB1, w, w);
|
checker(data_.data() + 5 * 3 * ww, RGB2, RGB1, w, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
// flat
|
// flat
|
||||||
@@ -3721,14 +3726,14 @@ void mjCTexture::BuiltinCube(void) {
|
|||||||
for (int r = 0; r < w; r++) {
|
for (int r = 0; r < w; r++) {
|
||||||
for (int c = 0; c < w; c++) {
|
for (int c = 0; c < w; c++) {
|
||||||
// set sides and up
|
// set sides and up
|
||||||
memcpy(data.data() + 0 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
memcpy(data_.data() + 0 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
||||||
memcpy(data.data() + 1 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
memcpy(data_.data() + 1 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
||||||
memcpy(data.data() + 2 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
memcpy(data_.data() + 2 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
||||||
memcpy(data.data() + 4 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
memcpy(data_.data() + 4 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
||||||
memcpy(data.data() + 5 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
memcpy(data_.data() + 5 * 3 * ww + 3 * (r * w + c), RGB1, 3);
|
||||||
|
|
||||||
// set down
|
// set down
|
||||||
memcpy(data.data() + 3 * 3 * ww + 3 * (r * w + c), RGB2, 3);
|
memcpy(data_.data() + 3 * 3 * ww + 3 * (r * w + c), RGB2, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3739,12 +3744,12 @@ void mjCTexture::BuiltinCube(void) {
|
|||||||
if (mark == mjMARK_EDGE) {
|
if (mark == mjMARK_EDGE) {
|
||||||
for (int j = 0; j < 6; j++) {
|
for (int j = 0; j < 6; j++) {
|
||||||
for (int r = 0; r < w; r++) {
|
for (int r = 0; r < w; r++) {
|
||||||
memcpy(data.data() + j * 3 * ww + 3 * (r * w + 0), RGBm, 3);
|
memcpy(data_.data() + j * 3 * ww + 3 * (r * w + 0), RGBm, 3);
|
||||||
memcpy(data.data() + j * 3 * ww + 3 * (r * w + w - 1), RGBm, 3);
|
memcpy(data_.data() + j * 3 * ww + 3 * (r * w + w - 1), RGBm, 3);
|
||||||
}
|
}
|
||||||
for (int c = 0; c < w; c++) {
|
for (int c = 0; c < w; c++) {
|
||||||
memcpy(data.data() + j * 3 * ww + 3 * (0 * w + c), RGBm, 3);
|
memcpy(data_.data() + j * 3 * ww + 3 * (0 * w + c), RGBm, 3);
|
||||||
memcpy(data.data() + j * 3 * ww + 3 * ((w - 1) * w + c), RGBm, 3);
|
memcpy(data_.data() + j * 3 * ww + 3 * ((w - 1) * w + c), RGBm, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3753,17 +3758,17 @@ void mjCTexture::BuiltinCube(void) {
|
|||||||
else if (mark == mjMARK_CROSS) {
|
else if (mark == mjMARK_CROSS) {
|
||||||
for (int j = 0; j < 6; j++) {
|
for (int j = 0; j < 6; j++) {
|
||||||
for (int r = 0; r < w; r++) {
|
for (int r = 0; r < w; r++) {
|
||||||
memcpy(data.data() + j * 3 * ww + 3 * (r * w + w / 2), RGBm, 3);
|
memcpy(data_.data() + j * 3 * ww + 3 * (r * w + w / 2), RGBm, 3);
|
||||||
}
|
}
|
||||||
for (int c = 0; c < w; c++) {
|
for (int c = 0; c < w; c++) {
|
||||||
memcpy(data.data() + j * 3 * ww + 3 * (w / 2 * w + c), RGBm, 3);
|
memcpy(data_.data() + j * 3 * ww + 3 * (w / 2 * w + c), RGBm, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// random dots
|
// random dots
|
||||||
else if (mark == mjMARK_RANDOM && random > 0) {
|
else if (mark == mjMARK_RANDOM && random > 0) {
|
||||||
randomdot(data.data(), markrgb, w, height, random);
|
randomdot(data_.data(), markrgb, w, height, random);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3928,12 +3933,12 @@ void mjCTexture::Load2D(std::string filename, const mjVFS* vfs) {
|
|||||||
throw mjCError(this, "Texture too large");
|
throw mjCError(this, "Texture too large");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
data.assign(nchannel*size, 0);
|
data_.assign(nchannel*size, std::byte(0));
|
||||||
} catch (const std::bad_alloc& e) {
|
} catch (const std::bad_alloc& e) {
|
||||||
throw mjCError(this, "Could not allocate memory for texture '%s' (id %d)",
|
throw mjCError(this, "Could not allocate memory for texture '%s' (id %d)",
|
||||||
(const char*)file_.c_str(), id);
|
(const char*)file_.c_str(), id);
|
||||||
}
|
}
|
||||||
memcpy(data.data(), image.data(), nchannel*size);
|
memcpy(data_.data(), image.data(), nchannel*size);
|
||||||
image.clear();
|
image.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3975,7 +3980,7 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
|
|||||||
throw mjCError(this, "Cube texture too large");
|
throw mjCError(this, "Cube texture too large");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
data.assign(3*size, 0);
|
data_.assign(3*size, std::byte(0));
|
||||||
} catch (const std::bad_alloc& e) {
|
} catch (const std::bad_alloc& e) {
|
||||||
throw mjCError(this,
|
throw mjCError(this,
|
||||||
"Could not allocate memory for texture '%s' (id %d)",
|
"Could not allocate memory for texture '%s' (id %d)",
|
||||||
@@ -3984,7 +3989,7 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
|
|||||||
|
|
||||||
// copy: repeated
|
// copy: repeated
|
||||||
if (gridsize[0]==1 && gridsize[1]==1) {
|
if (gridsize[0]==1 && gridsize[1]==1) {
|
||||||
memcpy(data.data(), image.data(), 3*width*width);
|
memcpy(data_.data(), image.data(), 3*width*width);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy: grid
|
// copy: grid
|
||||||
@@ -4017,7 +4022,7 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
|
|||||||
int rstart = width*(k/gridsize[1]);
|
int rstart = width*(k/gridsize[1]);
|
||||||
int cstart = width*(k%gridsize[1]);
|
int cstart = width*(k%gridsize[1]);
|
||||||
for (int j=0; j<width; j++) {
|
for (int j=0; j<width; j++) {
|
||||||
memcpy(data.data()+i*3*width*width+j*3*width, image.data()+(j+rstart)*3*w+3*cstart, 3*width);
|
memcpy(data_.data()+i*3*width*width+j*3*width, image.data()+(j+rstart)*3*w+3*cstart, 3*width);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark as defined
|
// mark as defined
|
||||||
@@ -4031,7 +4036,7 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
|
|||||||
for (int k=0; k<width; k++) {
|
for (int k=0; k<width; k++) {
|
||||||
for (int s=0; s<width; s++) {
|
for (int s=0; s<width; s++) {
|
||||||
for (int j=0; j<3; j++) {
|
for (int j=0; j<3; j++) {
|
||||||
data[i*3*width*width + 3*(k*width+s) + j] = (mjtByte)(255*rgb1[j]);
|
data_[i*3*width*width + 3*(k*width+s) + j] = (std::byte)(255*rgb1[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4073,7 +4078,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// first file: set size and allocate data
|
// first file: set size and allocate data
|
||||||
if (data.empty()) {
|
if (data_.empty()) {
|
||||||
width = w;
|
width = w;
|
||||||
if (width >= std::numeric_limits<int>::max()/6) {
|
if (width >= std::numeric_limits<int>::max()/6) {
|
||||||
throw mjCError(this, "Invalid width of builtin texture");
|
throw mjCError(this, "Invalid width of builtin texture");
|
||||||
@@ -4084,7 +4089,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
|||||||
throw mjCError(this, "PNG texture too large");
|
throw mjCError(this, "PNG texture too large");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
data.assign(3*size, 0);
|
data_.assign(3*size, std::byte(0));
|
||||||
} catch (const std::bad_alloc& e) {
|
} catch (const std::bad_alloc& e) {
|
||||||
throw mjCError(this, "Could not allocate memory for texture");
|
throw mjCError(this, "Could not allocate memory for texture");
|
||||||
}
|
}
|
||||||
@@ -4098,7 +4103,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy data
|
// copy data
|
||||||
memcpy(data.data()+i*3*width*width, image.data(), 3*width*width);
|
memcpy(data_.data()+i*3*width*width, image.data(), 3*width*width);
|
||||||
image.clear();
|
image.clear();
|
||||||
|
|
||||||
// mark as defined
|
// mark as defined
|
||||||
@@ -4112,7 +4117,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
|||||||
for (int k=0; k<width; k++) {
|
for (int k=0; k<width; k++) {
|
||||||
for (int s=0; s<width; s++) {
|
for (int s=0; s<width; s++) {
|
||||||
for (int j=0; j<3; j++) {
|
for (int j=0; j<3; j++) {
|
||||||
data[i*3*width*width + 3*(k*width+s) + j] = (mjtByte)(255*rgb1[j]);
|
data_[i*3*width*width + 3*(k*width+s) + j] = (std::byte)(255*rgb1[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4126,8 +4131,17 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
|||||||
void mjCTexture::Compile(const mjVFS* vfs) {
|
void mjCTexture::Compile(const mjVFS* vfs) {
|
||||||
CopyFromSpec();
|
CopyFromSpec();
|
||||||
|
|
||||||
|
// buffer from user
|
||||||
|
if (!data_.empty()) {
|
||||||
|
if (data_.size() != nchannel*width*height) {
|
||||||
|
throw mjCError(this, "Texture buffer has incorrect size, given %d expected %d", nullptr,
|
||||||
|
data_.size(), nchannel * width * height);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// builtin
|
// builtin
|
||||||
if (builtin != mjBUILTIN_NONE) {
|
else if (builtin != mjBUILTIN_NONE) {
|
||||||
// check width
|
// check width
|
||||||
if (width<1) {
|
if (width<1) {
|
||||||
throw mjCError(this, "Invalid width of builtin texture");
|
throw mjCError(this, "Invalid width of builtin texture");
|
||||||
@@ -4151,7 +4165,7 @@ void mjCTexture::Compile(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
// allocate data
|
// allocate data
|
||||||
try {
|
try {
|
||||||
data.assign(nchannel*size, 0);
|
data_.assign(nchannel*size, std::byte(0));
|
||||||
} catch (const std::bad_alloc& e) {
|
} catch (const std::bad_alloc& e) {
|
||||||
throw mjCError(this, "Could not allocate memory for texture");
|
throw mjCError(this, "Could not allocate memory for texture");
|
||||||
}
|
}
|
||||||
@@ -4208,9 +4222,12 @@ void mjCTexture::Compile(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make sure someone allocated data; SHOULD NOT OCCUR
|
// make sure someone allocated data; SHOULD NOT OCCUR
|
||||||
if (data.empty()) {
|
if (data_.empty()) {
|
||||||
throw mjCError(this, "texture '%s' (id %d) was not specified", name.c_str(), id);
|
throw mjCError(this, "texture '%s' (id %d) was not specified", name.c_str(), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if recompiled is called, clear data_ first
|
||||||
|
clear_data_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#ifndef MUJOCO_SRC_USER_USER_OBJECTS_H_
|
#ifndef MUJOCO_SRC_USER_USER_OBJECTS_H_
|
||||||
#define MUJOCO_SRC_USER_USER_OBJECTS_H_
|
#define MUJOCO_SRC_USER_USER_OBJECTS_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -1015,7 +1016,7 @@ class mjCHField : public mjCHField_, private mjsHField {
|
|||||||
|
|
||||||
class mjCTexture_ : public mjCBase {
|
class mjCTexture_ : public mjCBase {
|
||||||
protected:
|
protected:
|
||||||
std::vector<mjtByte> data; // texture data (rgb, roughness, etc.)
|
std::vector<std::byte> data_; // texture data (rgb, roughness, etc.)
|
||||||
|
|
||||||
std::string file_;
|
std::string file_;
|
||||||
std::string content_type_;
|
std::string content_type_;
|
||||||
@@ -1066,6 +1067,8 @@ class mjCTexture : public mjCTexture_, private mjsTexture {
|
|||||||
void LoadCustom(mjResource* resource,
|
void LoadCustom(mjResource* resource,
|
||||||
std::vector<unsigned char>& image,
|
std::vector<unsigned char>& image,
|
||||||
unsigned int& w, unsigned int& h);
|
unsigned int& w, unsigned int& h);
|
||||||
|
|
||||||
|
bool clear_data_; // if true, data_ is empty and should be filled by Compile
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -406,5 +406,11 @@ std::string WriteXML(const mjSpec* spec, char* error, int nerror) {
|
|||||||
|
|
||||||
mjXWriter writer;
|
mjXWriter writer;
|
||||||
writer.SetModel(spec);
|
writer.SetModel(spec);
|
||||||
return writer.Write(error, nerror);
|
|
||||||
|
try {
|
||||||
|
return writer.Write(error, nerror);
|
||||||
|
} catch (mjXError err) {
|
||||||
|
mjCopyError(error, err.message, nerror);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ void mjXWriter::OneMaterial(XMLElement* elem, const mjCMaterial* pmat, mjCDef* d
|
|||||||
}
|
}
|
||||||
if (has_non_rgb) {
|
if (has_non_rgb) {
|
||||||
// // TODO elem = InsertEnd(section, "role");
|
// // TODO elem = InsertEnd(section, "role");
|
||||||
mju_error("mjXWriter: no support for non-RGB textures.");
|
throw mjXError(0, "no support for non-RGB textures.");
|
||||||
}
|
}
|
||||||
WriteAttrKey(elem, "texuniform", bool_map, 2, pmat->texuniform, def->Material().texuniform);
|
WriteAttrKey(elem, "texuniform", bool_map, 2, pmat->texuniform, def->Material().texuniform);
|
||||||
WriteAttr(elem, "texrepeat", 2, pmat->texrepeat, def->Material().texrepeat);
|
WriteAttr(elem, "texrepeat", 2, pmat->texrepeat, def->Material().texrepeat);
|
||||||
@@ -1452,6 +1452,14 @@ void mjXWriter::Asset(XMLElement* root) {
|
|||||||
WriteAttrInt(elem, "height", ptex->height);
|
WriteAttrInt(elem, "height", ptex->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write buffer
|
||||||
|
else if (ptex->get_cubefiles()[0].empty() && ptex->get_cubefiles()[1].empty() &&
|
||||||
|
ptex->get_cubefiles()[2].empty() && ptex->get_cubefiles()[3].empty() &&
|
||||||
|
ptex->get_cubefiles()[4].empty() && ptex->get_cubefiles()[5].empty() &&
|
||||||
|
ptex->get_file().empty() && ptex->gridsize[0] == 1 && ptex->gridsize[1] == 1) {
|
||||||
|
throw mjXError(0, "no support for buffer textures.");
|
||||||
|
}
|
||||||
|
|
||||||
// write textures loaded from files
|
// write textures loaded from files
|
||||||
else {
|
else {
|
||||||
// write single file
|
// write single file
|
||||||
|
|||||||
+69
-25
@@ -15,6 +15,8 @@
|
|||||||
// Tests for user/user_api.cc.
|
// Tests for user/user_api.cc.
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -272,6 +274,30 @@ TEST_F(PluginTest, RecompileCompareObjCache) {
|
|||||||
mj_deleteVFS(vfs.get());
|
mj_deleteVFS(vfs.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tiny RGB 2 x 3 PNG file
|
||||||
|
static constexpr uint8_t tex1[] = {
|
||||||
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02,
|
||||||
|
0x08, 0x02, 0x00, 0x00, 0x00, 0x12, 0x16, 0xf1, 0x4d, 0x00, 0x00, 0x00,
|
||||||
|
0x1c, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x78, 0xc1, 0xc0, 0xc0,
|
||||||
|
0xc0, 0xf0, 0xbf, 0xb8, 0xb8, 0x98, 0x81, 0xe1, 0x3f, 0xc3, 0xff, 0xff,
|
||||||
|
0xff, 0xc5, 0xc4, 0xc4, 0x00, 0x46, 0xd7, 0x07, 0x7f, 0xd2, 0x52, 0xa1,
|
||||||
|
0x41, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
|
||||||
|
0x82
|
||||||
|
};
|
||||||
|
|
||||||
|
// previous PNG file, but rotated by 180 degrees
|
||||||
|
static constexpr uint8_t tex2[] = {
|
||||||
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02,
|
||||||
|
0x08, 0x02, 0x00, 0x00, 0x00, 0x12, 0x16, 0xf1, 0x4d, 0x00, 0x00, 0x00,
|
||||||
|
0x1c, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x10, 0x13, 0x13, 0xfb,
|
||||||
|
0xff, 0xff, 0x3f, 0xc3, 0x7f, 0x06, 0x96, 0xd8, 0xd8, 0x58, 0x46, 0x46,
|
||||||
|
0x86, 0x17, 0x0c, 0x0c, 0x00, 0x49, 0x22, 0x06, 0x44, 0xe4, 0x91, 0xb8,
|
||||||
|
0x83, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
|
||||||
|
0x82
|
||||||
|
};
|
||||||
|
|
||||||
TEST_F(PluginTest, RecompileComparePngCache) {
|
TEST_F(PluginTest, RecompileComparePngCache) {
|
||||||
static constexpr char xml[] = R"(
|
static constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
@@ -286,31 +312,6 @@ TEST_F(PluginTest, RecompileComparePngCache) {
|
|||||||
</mujoco>
|
</mujoco>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
// tiny RGB 2 x 3 PNG file
|
|
||||||
static constexpr unsigned char tex1[] = {
|
|
||||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
|
||||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02,
|
|
||||||
0x08, 0x02, 0x00, 0x00, 0x00, 0x12, 0x16, 0xf1, 0x4d, 0x00, 0x00, 0x00,
|
|
||||||
0x1c, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x78, 0xc1, 0xc0, 0xc0,
|
|
||||||
0xc0, 0xf0, 0xbf, 0xb8, 0xb8, 0x98, 0x81, 0xe1, 0x3f, 0xc3, 0xff, 0xff,
|
|
||||||
0xff, 0xc5, 0xc4, 0xc4, 0x00, 0x46, 0xd7, 0x07, 0x7f, 0xd2, 0x52, 0xa1,
|
|
||||||
0x41, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
|
|
||||||
0x82
|
|
||||||
};
|
|
||||||
|
|
||||||
// previous PNG file, but rotated by 180 degrees
|
|
||||||
static constexpr unsigned char tex2[] = {
|
|
||||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
|
||||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02,
|
|
||||||
0x08, 0x02, 0x00, 0x00, 0x00, 0x12, 0x16, 0xf1, 0x4d, 0x00, 0x00, 0x00,
|
|
||||||
0x1c, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x10, 0x13, 0x13, 0xfb,
|
|
||||||
0xff, 0xff, 0x3f, 0xc3, 0x7f, 0x06, 0x96, 0xd8, 0xd8, 0x58, 0x46, 0x46,
|
|
||||||
0x86, 0x17, 0x0c, 0x0c, 0x00, 0x49, 0x22, 0x06, 0x44, 0xe4, 0x91, 0xb8,
|
|
||||||
0x83, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
|
|
||||||
0x82
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
auto vfs = std::make_unique<mjVFS>();
|
auto vfs = std::make_unique<mjVFS>();
|
||||||
mj_defaultVFS(vfs.get());
|
mj_defaultVFS(vfs.get());
|
||||||
mj_addBufferVFS(vfs.get(), "tex.png", tex1, sizeof(tex1));
|
mj_addBufferVFS(vfs.get(), "tex.png", tex1, sizeof(tex1));
|
||||||
@@ -334,6 +335,49 @@ TEST_F(PluginTest, RecompileComparePngCache) {
|
|||||||
mj_deleteVFS(vfs.get());
|
mj_deleteVFS(vfs.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------- test textures ------------------------------
|
||||||
|
|
||||||
|
TEST_F(PluginTest, TextureFromBuffer) {
|
||||||
|
mjSpec* spec = mj_makeSpec();
|
||||||
|
|
||||||
|
mjsTexture* t1 = mjs_addTexture(spec);
|
||||||
|
mjs_setString(t1->name, "tex1");
|
||||||
|
t1->type = mjTEXTURE_2D;
|
||||||
|
t1->width = 3;
|
||||||
|
t1->height = 2;
|
||||||
|
t1->nchannel = 3;
|
||||||
|
mjs_setBuffer(t1->data, (std::byte*)tex1, 18);
|
||||||
|
|
||||||
|
mjsTexture* t2 = mjs_addTexture(spec);
|
||||||
|
mjs_setString(t2->name, "tex2");
|
||||||
|
t2->type = mjTEXTURE_2D;
|
||||||
|
t2->width = 3;
|
||||||
|
t2->height = 2;
|
||||||
|
t2->nchannel = 3;
|
||||||
|
mjs_setBuffer(t2->data, (std::byte*)tex2, 18);
|
||||||
|
|
||||||
|
mjsMaterial* mat = mjs_addMaterial(spec, nullptr);
|
||||||
|
mjs_setString(mat->name, "mat");
|
||||||
|
mjs_setInStringVec(mat->textures, mjTEXROLE_RGB, "tex1");
|
||||||
|
mjs_setInStringVec(mat->textures, mjTEXROLE_ORM, "tex2");
|
||||||
|
|
||||||
|
mjsGeom* geom = mjs_addGeom(mjs_findBody(spec, "world"), nullptr);
|
||||||
|
mjs_setString(geom->material, "mat");
|
||||||
|
geom->size[0] = 1;
|
||||||
|
|
||||||
|
mjModel* m = mj_compile(spec, nullptr);
|
||||||
|
EXPECT_THAT(m, NotNull());
|
||||||
|
EXPECT_EQ(m->ntex, 2);
|
||||||
|
|
||||||
|
std::array<char, 1024> err;
|
||||||
|
std::array<char, 1024> str;
|
||||||
|
mj_saveXMLString(spec, str.data(), str.size(), err.data(), err.size());
|
||||||
|
EXPECT_STREQ(err.data(), "XML Error: no support for buffer textures.");
|
||||||
|
|
||||||
|
mj_deleteModel(m);
|
||||||
|
mj_deleteSpec(spec);
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------- test attach --------------------------------
|
// -------------------------------- test attach --------------------------------
|
||||||
|
|
||||||
static constexpr char xml_child[] = R"(
|
static constexpr char xml_child[] = R"(
|
||||||
|
|||||||
Reference in New Issue
Block a user