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/libactuator.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||||
cp lib/libelasticity.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
cp lib/libelasticity.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||||
cp lib/libobj_decoder.* ${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/libsensor.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||||
cp lib/libsdf_plugin.* ${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/actuator.dll ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
||||||
cp bin/Release/elasticity.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/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
|
cp bin/Release/sensor.dll ${TMPDIR}/mujoco_install/mujoco_plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ if(NOT EMSCRIPTEN)
|
|||||||
add_subdirectory(plugin/sdf)
|
add_subdirectory(plugin/sdf)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(plugin/obj_decoder)
|
add_subdirectory(plugin/obj_decoder)
|
||||||
|
add_subdirectory(plugin/stl_decoder)
|
||||||
add_subdirectory(src/engine)
|
add_subdirectory(src/engine)
|
||||||
add_subdirectory(src/user)
|
add_subdirectory(src/user)
|
||||||
add_subdirectory(src/xml)
|
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) {
|
bool mjCMesh::IsMSH(std::string_view filename, std::string_view ct) {
|
||||||
std::string asset_type = GetAssetContentType(filename, 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 {
|
bool mjCMesh::IsMSH() const {
|
||||||
return content_type_ == "model/vnd.mujoco.msh";
|
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_);
|
std::string asset_type = GetAssetContentType(resource->name, content_type_);
|
||||||
content_type_ = asset_type;
|
content_type_ = asset_type;
|
||||||
|
|
||||||
if (IsSTL()) {
|
if (IsMSH()) {
|
||||||
LoadSTL(resource);
|
|
||||||
} else if (IsMSH()) {
|
|
||||||
LoadMSH(resource, remove_repeated);
|
LoadMSH(resource, remove_repeated);
|
||||||
} else {
|
} else {
|
||||||
LoadFromDecoder(resource, remove_repeated);
|
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
|
// load MSH binary mesh
|
||||||
|
|||||||
+1
-1
@@ -60,6 +60,6 @@ set_target_properties(mujoco_wasm PROPERTIES
|
|||||||
OUTPUT_NAME "mujoco"
|
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})
|
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}")
|
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})
|
install(TARGETS mujoco_wasm_benchmark DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR})
|
||||||
|
|||||||
Reference in New Issue
Block a user