Move STL handling to a mjpDecoder.
PiperOrigin-RevId: 880820344 Change-Id: Ib3a714dd9698335433e518ed6af17d7b8407a8fb
This commit is contained in:
committed by
Copybara-Service
parent
330c36c99b
commit
ed0d7ba29f
@@ -102,6 +102,7 @@ copy_plugins_posix() {
|
||||
cp lib/libactuator.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp lib/libelasticity.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp lib/libobj_decoder.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp lib/libstl_decoder.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp lib/libsensor.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp lib/libsdf_plugin.* ${TMPDIR}/mujoco_install/mujoco_plugin
|
||||
}
|
||||
@@ -113,6 +114,7 @@ copy_plugins_window() {
|
||||
cp bin/Release/actuator.dll ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp bin/Release/elasticity.dll ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp bin/Release/obj_decoder.dll ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp bin/Release/stl_decoder.dll ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||
cp bin/Release/sensor.dll ${TMPDIR}/mujoco_install/mujoco_plugin
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ if(NOT EMSCRIPTEN)
|
||||
add_subdirectory(plugin/sdf)
|
||||
endif()
|
||||
add_subdirectory(plugin/obj_decoder)
|
||||
add_subdirectory(plugin/stl_decoder)
|
||||
add_subdirectory(src/engine)
|
||||
add_subdirectory(src/user)
|
||||
add_subdirectory(src/xml)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright 2026 DeepMind Technologies Limited
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
add_library(stl_decoder OBJECT stl_decoder.cc)
|
||||
else()
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}")
|
||||
|
||||
add_library(stl_decoder SHARED stl_decoder.cc)
|
||||
endif()
|
||||
|
||||
target_include_directories(stl_decoder PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../include
|
||||
)
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
target_link_libraries(stl_decoder PRIVATE)
|
||||
else()
|
||||
target_link_libraries(stl_decoder PRIVATE
|
||||
mujoco
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_options(stl_decoder PRIVATE
|
||||
${AVX_COMPILE_OPTIONS}
|
||||
${MUJOCO_MACOS_COMPILE_OPTIONS}
|
||||
${EXTRA_COMPILE_OPTIONS}
|
||||
${MUJOCO_CXX_FLAGS}
|
||||
)
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
target_link_options(stl_decoder PRIVATE
|
||||
${MUJOCO_MACOS_LINK_OPTIONS}
|
||||
${EXTRA_LINK_OPTIONS}
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS stl_decoder
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/mujoco_plugin"
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright 2026 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <mujoco/mjplugin.h>
|
||||
#include <mujoco/mjspec.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
void ReadFromBuffer(T* dst, const char* src) {
|
||||
std::memcpy(dst, src, sizeof(T));
|
||||
}
|
||||
|
||||
struct Vec3Key {
|
||||
int x, y, z;
|
||||
bool operator<(const Vec3Key& o) const {
|
||||
if (x != o.x) return x < o.x;
|
||||
if (y != o.y) return y < o.y;
|
||||
return z < o.z;
|
||||
}
|
||||
};
|
||||
|
||||
Vec3Key FloatToKey(const float v[3]) {
|
||||
int x, y, z;
|
||||
std::memcpy(&x, &v[0], sizeof(int));
|
||||
std::memcpy(&y, &v[1], sizeof(int));
|
||||
std::memcpy(&z, &v[2], sizeof(int));
|
||||
return {x, y, z};
|
||||
}
|
||||
|
||||
mjSpec* Decode(mjResource* resource, const mjVFS* vfs) {
|
||||
const void* bytes = nullptr;
|
||||
int buffer_sz = mju_readResource(resource, &bytes);
|
||||
if (buffer_sz < 0) {
|
||||
mju_warning("stl_decoder: could not read STL file '%s'", resource->name);
|
||||
return nullptr;
|
||||
}
|
||||
if (!buffer_sz) {
|
||||
mju_warning("stl_decoder: STL file '%s' is empty", resource->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* buffer = static_cast<const char*>(bytes);
|
||||
|
||||
if (buffer_sz < 84) {
|
||||
mju_warning("stl_decoder: invalid header in STL file '%s'", resource->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int nfaces = 0;
|
||||
ReadFromBuffer(&nfaces, buffer + 80);
|
||||
if (nfaces < 1 || nfaces > 200000) {
|
||||
mju_warning(
|
||||
"stl_decoder: number of faces should be between 1 and 200000 in STL "
|
||||
"file '%s'; perhaps this is an ASCII file?",
|
||||
resource->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (nfaces * 50 != buffer_sz - 84) {
|
||||
mju_warning(
|
||||
"stl_decoder: STL file '%s' has wrong size; perhaps this is an ASCII "
|
||||
"file?",
|
||||
resource->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* stl = buffer + 84;
|
||||
|
||||
std::vector<float> uservert;
|
||||
std::vector<int> userface(3 * nfaces, 0);
|
||||
std::map<Vec3Key, int> vertmap;
|
||||
|
||||
for (int i = 0; i < nfaces; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
float v[3];
|
||||
ReadFromBuffer(&v, stl + 50 * i + 12 * (j + 1));
|
||||
|
||||
if (std::fabs(v[0]) > std::pow(2, 30) ||
|
||||
std::fabs(v[1]) > std::pow(2, 30) ||
|
||||
std::fabs(v[2]) > std::pow(2, 30)) {
|
||||
mju_warning(
|
||||
"stl_decoder: vertex in STL file '%s' "
|
||||
"exceeds maximum bounds",
|
||||
resource->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vec3Key key = FloatToKey(v);
|
||||
auto [it, inserted] = vertmap.emplace(key, uservert.size() / 3);
|
||||
if (inserted) {
|
||||
uservert.push_back(v[0]);
|
||||
uservert.push_back(v[1]);
|
||||
uservert.push_back(v[2]);
|
||||
}
|
||||
userface[3 * i + j] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
mjSpec* spec = mj_makeSpec();
|
||||
mjsMesh* mesh = mjs_addMesh(spec, nullptr);
|
||||
|
||||
mjs_setString(mesh->file, resource->name);
|
||||
mjs_setFloat(mesh->uservert, uservert.data(), uservert.size());
|
||||
mjs_setInt(mesh->userface, userface.data(), userface.size());
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
int CanDecode(const mjResource* resource) {
|
||||
std::string_view name(resource->name);
|
||||
return name.ends_with(".stl") || name.ends_with(".STL");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
mjPLUGIN_LIB_INIT {
|
||||
mjpDecoder decoder;
|
||||
mjp_defaultDecoder(&decoder);
|
||||
decoder.content_type = "model/stl";
|
||||
decoder.extension = ".stl";
|
||||
decoder.decode = Decode;
|
||||
decoder.can_decode = CanDecode;
|
||||
mjp_registerDecoder(&decoder);
|
||||
}
|
||||
+1
-81
@@ -614,10 +614,6 @@ void mjCMesh::ProcessVertices(const std::vector<float>& vert, bool remove_repeat
|
||||
|
||||
|
||||
|
||||
bool mjCMesh::IsSTL(std::string_view filename, std::string_view ct) {
|
||||
std::string asset_type = GetAssetContentType(filename, ct);
|
||||
return asset_type == "model/stl";
|
||||
}
|
||||
|
||||
bool mjCMesh::IsMSH(std::string_view filename, std::string_view ct) {
|
||||
std::string asset_type = GetAssetContentType(filename, ct);
|
||||
@@ -626,9 +622,6 @@ bool mjCMesh::IsMSH(std::string_view filename, std::string_view ct) {
|
||||
|
||||
|
||||
|
||||
bool mjCMesh::IsSTL() const {
|
||||
return content_type_ == "model/stl";
|
||||
}
|
||||
|
||||
bool mjCMesh::IsMSH() const {
|
||||
return content_type_ == "model/vnd.mujoco.msh";
|
||||
@@ -684,9 +677,7 @@ void mjCMesh::LoadFromResource(mjResource* resource, bool remove_repeated) {
|
||||
std::string asset_type = GetAssetContentType(resource->name, content_type_);
|
||||
content_type_ = asset_type;
|
||||
|
||||
if (IsSTL()) {
|
||||
LoadSTL(resource);
|
||||
} else if (IsMSH()) {
|
||||
if (IsMSH()) {
|
||||
LoadMSH(resource, remove_repeated);
|
||||
} else {
|
||||
LoadFromDecoder(resource, remove_repeated);
|
||||
@@ -1115,77 +1106,6 @@ bool mjCMesh::LoadCachedMesh(mjCCache *cache, const mjResource* resource) {
|
||||
|
||||
|
||||
|
||||
// load STL binary mesh
|
||||
void mjCMesh::LoadSTL(mjResource* resource) {
|
||||
bool righthand = scale[0] * scale[1] * scale[2] > 0;
|
||||
|
||||
// get file data in buffer
|
||||
char* buffer = 0;
|
||||
int buffer_sz = mju_readResource(resource, (const void**)&buffer);
|
||||
|
||||
// still not found
|
||||
if (buffer_sz < 0) {
|
||||
throw mjCError(this, "could not read STL file '%s'", resource->name);
|
||||
} else if (!buffer_sz) {
|
||||
throw mjCError(this, "STL file '%s' is empty", resource->name);
|
||||
}
|
||||
|
||||
// make sure there is enough data for header
|
||||
if (buffer_sz < 84) {
|
||||
throw mjCError(this, "invalid header in STL file '%s'", resource->name);
|
||||
}
|
||||
|
||||
// get number of triangles, check bounds
|
||||
int nfaces = 0;
|
||||
ReadFromBuffer(&nfaces, buffer + 80);
|
||||
if (nfaces < 1 || nfaces > 200000) {
|
||||
throw mjCError(this,
|
||||
"number of faces should be between 1 and 200000 in STL file '%s';"
|
||||
" perhaps this is an ASCII file?", resource->name);
|
||||
}
|
||||
|
||||
// check remaining buffer size
|
||||
if (nfaces*50 != buffer_sz-84) {
|
||||
throw mjCError(this,
|
||||
"STL file '%s' has wrong size; perhaps this is an ASCII file?",
|
||||
resource->name);
|
||||
}
|
||||
|
||||
// assign stl data pointer
|
||||
const char* stl = buffer + 84;
|
||||
|
||||
// allocate face and vertex data
|
||||
face_.assign(3*nfaces, 0);
|
||||
std::vector<float> vert;
|
||||
|
||||
// add vertices and faces, including repeated for now
|
||||
for (int i=0; i < nfaces; i++) {
|
||||
for (int j=0; j < 3; j++) {
|
||||
// read vertex coordinates
|
||||
float v[3];
|
||||
ReadFromBuffer(&v, stl + 50*i + 12*(j + 1));
|
||||
|
||||
// check if vertex can be cast to an int safely
|
||||
if (fabs(v[0]) > pow(2, 30) || fabs(v[1]) > pow(2, 30) || fabs(v[2]) > pow(2, 30)) {
|
||||
throw mjCError(this, "vertex in STL file '%s' exceed maximum bounds", resource->name);
|
||||
}
|
||||
|
||||
// add vertex address in face; change order if scale makes it lefthanded
|
||||
if (righthand || j == 0) {
|
||||
face_[3*i + j] = vert.size() / 3;
|
||||
} else {
|
||||
face_[3*i + 3 - j] = vert.size() / 3;
|
||||
}
|
||||
|
||||
// add vertex data
|
||||
vert.push_back(v[0]);
|
||||
vert.push_back(v[1]);
|
||||
vert.push_back(v[2]);
|
||||
}
|
||||
}
|
||||
ProcessVertices(vert, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// load MSH binary mesh
|
||||
|
||||
+1
-1
@@ -60,6 +60,6 @@ set_target_properties(mujoco_wasm PROPERTIES
|
||||
OUTPUT_NAME "mujoco"
|
||||
)
|
||||
|
||||
target_link_libraries(mujoco_wasm ccd lodepng mujoco tinyxml2 qhullstatic_r obj_decoder)
|
||||
target_link_libraries(mujoco_wasm ccd lodepng mujoco tinyxml2 qhullstatic_r obj_decoder stl_decoder)
|
||||
|
||||
install(TARGETS mujoco_wasm DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR})
|
||||
|
||||
@@ -53,6 +53,6 @@ add_executable(mujoco_wasm_benchmark ${MUJOCO_WASM_FILES})
|
||||
|
||||
set_target_properties(mujoco_wasm_benchmark PROPERTIES LINK_FLAGS "${EMCC_LINKER_FLAGS_STR}")
|
||||
|
||||
target_link_libraries(mujoco_wasm_benchmark ccd lodepng mujoco tinyxml2 qhullstatic_r obj_decoder)
|
||||
target_link_libraries(mujoco_wasm_benchmark ccd lodepng mujoco tinyxml2 qhullstatic_r obj_decoder stl_decoder)
|
||||
|
||||
install(TARGETS mujoco_wasm_benchmark DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR})
|
||||
|
||||
Reference in New Issue
Block a user