Update UsdWriter to write any parts of MjSpec that are not from USD to the EditTarget.
This enables two workflows: 1) Users that parsed some other format into MjSpec, or built it programmatically can export animated USD easily with as minimal as possible interaction with USD. 2) Creating a spec from USD, then modifying the spec, should not cause a duplication of data when writing to USD. PiperOrigin-RevId: 827627520 Change-Id: I02c8c14a18569547710e54f3704d52ac0bf70ce6
This commit is contained in:
committed by
Copybara-Service
parent
abc02b736e
commit
7d7648f7ed
@@ -38,6 +38,7 @@ class Writer {
|
|||||||
void AddSink(PoseSinkFn sink_fn);
|
void AddSink(PoseSinkFn sink_fn);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void WriteSpecsWithoutUSDOriginToLayer();
|
||||||
void BuildMjUsdMapping();
|
void BuildMjUsdMapping();
|
||||||
|
|
||||||
pxr::UsdStageRefPtr stage_;
|
pxr::UsdStageRefPtr stage_;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <mujoco/experimental/usd/mjcPhysics/tokens.h>
|
#include <mujoco/experimental/usd/mjcPhysics/tokens.h>
|
||||||
|
#include <mujoco/experimental/usd/utils.h>
|
||||||
#include <mujoco/mjspec.h>
|
#include <mujoco/mjspec.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "mjcf/utils.h"
|
#include "mjcf/utils.h"
|
||||||
@@ -150,13 +151,12 @@ using mujoco::usd::CreateClassSpec;
|
|||||||
using mujoco::usd::CreatePrimSpec;
|
using mujoco::usd::CreatePrimSpec;
|
||||||
using mujoco::usd::CreateRelationshipSpec;
|
using mujoco::usd::CreateRelationshipSpec;
|
||||||
using mujoco::usd::SetAttributeDefault;
|
using mujoco::usd::SetAttributeDefault;
|
||||||
using mujoco::usd::SetAttributeMetadata;
|
|
||||||
using mujoco::usd::SetAttributeTimeSample;
|
using mujoco::usd::SetAttributeTimeSample;
|
||||||
using mujoco::usd::SetLayerMetadata;
|
using mujoco::usd::SetLayerMetadata;
|
||||||
using mujoco::usd::SetPrimKind;
|
using mujoco::usd::SetPrimKind;
|
||||||
using mujoco::usd::SetPrimPurpose;
|
using mujoco::usd::SetPrimPurpose;
|
||||||
|
|
||||||
pxr::GfMatrix4d MujocoPosQuatToTransform(double *pos, double *quat) {
|
pxr::GfMatrix4d MujocoPosQuatToTransform(mjtNum *pos, mjtNum *quat) {
|
||||||
pxr::GfQuatd quaternion = pxr::GfQuatd::GetIdentity();
|
pxr::GfQuatd quaternion = pxr::GfQuatd::GetIdentity();
|
||||||
quaternion.SetReal(quat[0]);
|
quaternion.SetReal(quat[0]);
|
||||||
quaternion.SetImaginary(quat[1], quat[2], quat[3]);
|
quaternion.SetImaginary(quat[1], quat[2], quat[3]);
|
||||||
@@ -174,14 +174,26 @@ pxr::GfMatrix4d MujocoPosQuatToTransform(double *pos, double *quat) {
|
|||||||
|
|
||||||
class ModelWriter {
|
class ModelWriter {
|
||||||
public:
|
public:
|
||||||
ModelWriter(mjSpec *spec, mjModel *model, pxr::SdfLayerRefPtr layer)
|
ModelWriter(mjSpec* spec, mjModel* model, pxr::SdfLayerRefPtr layer,
|
||||||
: spec_(spec), model_(model), layer_(layer), class_path_("/Bad_Path") {
|
bool skip_elems_from_usd)
|
||||||
|
: spec_(spec),
|
||||||
|
model_(model),
|
||||||
|
layer_(layer),
|
||||||
|
skip_elems_from_usd_(skip_elems_from_usd),
|
||||||
|
class_path_("/Bad_Path") {
|
||||||
body_paths_ = std::vector<pxr::SdfPath>(model->nbody);
|
body_paths_ = std::vector<pxr::SdfPath>(model->nbody);
|
||||||
site_paths_ = std::vector<pxr::SdfPath>(model->nsite);
|
site_paths_ = std::vector<pxr::SdfPath>(model->nsite);
|
||||||
joint_paths_ = std::vector<pxr::SdfPath>(model->njnt);
|
joint_paths_ = std::vector<pxr::SdfPath>(model->njnt);
|
||||||
}
|
}
|
||||||
~ModelWriter() { mj_deleteModel(model_); }
|
~ModelWriter() { mj_deleteModel(model_); }
|
||||||
|
|
||||||
|
bool ShouldWrite(mjsElement* element) {
|
||||||
|
// If we've been told to skip over spec elements that originated from USD,
|
||||||
|
// check the user value and if it has a primpath then it came from decoding
|
||||||
|
// a USD stage into an MjSpec.
|
||||||
|
return !skip_elems_from_usd_ || mujoco::usd::GetUsdPrimPathUserValue(element).IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
void Write() {
|
void Write() {
|
||||||
// Create top level class holder.
|
// Create top level class holder.
|
||||||
class_path_ = CreateClassSpec(layer_, pxr::SdfPath::AbsoluteRootPath(),
|
class_path_ = CreateClassSpec(layer_, pxr::SdfPath::AbsoluteRootPath(),
|
||||||
@@ -214,6 +226,7 @@ class ModelWriter {
|
|||||||
mjModel *model_;
|
mjModel *model_;
|
||||||
pxr::SdfLayerRefPtr layer_;
|
pxr::SdfLayerRefPtr layer_;
|
||||||
|
|
||||||
|
bool skip_elems_from_usd_ = false;
|
||||||
// Path to top level class spec that all classes should be children of.
|
// Path to top level class spec that all classes should be children of.
|
||||||
pxr::SdfPath class_path_;
|
pxr::SdfPath class_path_;
|
||||||
// Mapping from Mujoco body id to SdfPath.
|
// Mapping from Mujoco body id to SdfPath.
|
||||||
@@ -343,7 +356,6 @@ class ModelWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto existing_order =
|
auto existing_order =
|
||||||
prim_spec->GetField(pxr::UsdGeomTokens->xformOpOrder).UncheckedGet<pxr::VtArray<pxr::TfToken>>();
|
|
||||||
prim_spec->GetField(pxr::UsdGeomTokens->xformOpOrder).Get<pxr::VtArray<pxr::TfToken>>();
|
prim_spec->GetField(pxr::UsdGeomTokens->xformOpOrder).Get<pxr::VtArray<pxr::TfToken>>();
|
||||||
|
|
||||||
pxr::VtArray<pxr::TfToken> new_order(order.size() + existing_order.size());
|
pxr::VtArray<pxr::TfToken> new_order(order.size() + existing_order.size());
|
||||||
@@ -746,7 +758,9 @@ class ModelWriter {
|
|||||||
|
|
||||||
mjsMesh *mesh = mjs_asMesh(mjs_firstElement(spec_, mjOBJ_MESH));
|
mjsMesh *mesh = mjs_asMesh(mjs_firstElement(spec_, mjOBJ_MESH));
|
||||||
while (mesh) {
|
while (mesh) {
|
||||||
WriteMesh(mesh, scope->GetPath());
|
if (ShouldWrite(mesh->element)) {
|
||||||
|
WriteMesh(mesh, scope->GetPath());
|
||||||
|
}
|
||||||
mesh = mjs_asMesh(mjs_nextElement(spec_, mesh->element));
|
mesh = mjs_asMesh(mjs_nextElement(spec_, mesh->element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1161,10 +1175,12 @@ class ModelWriter {
|
|||||||
std::unordered_map<std::string, std::vector<mjsKey *>> keyframes_map;
|
std::unordered_map<std::string, std::vector<mjsKey *>> keyframes_map;
|
||||||
mjsKey *keyframe = mjs_asKey(mjs_firstElement(spec_, mjOBJ_KEY));
|
mjsKey *keyframe = mjs_asKey(mjs_firstElement(spec_, mjOBJ_KEY));
|
||||||
while (keyframe) {
|
while (keyframe) {
|
||||||
std::string keyframe_name = mjs_getName(keyframe->element)->empty()
|
if (ShouldWrite(keyframe->element)) {
|
||||||
? kTokens->keyframe
|
std::string keyframe_name = mjs_getName(keyframe->element)->empty()
|
||||||
: *mjs_getName(keyframe->element);
|
? kTokens->keyframe
|
||||||
keyframes_map[keyframe_name].push_back(keyframe);
|
: *mjs_getName(keyframe->element);
|
||||||
|
keyframes_map[keyframe_name].push_back(keyframe);
|
||||||
|
}
|
||||||
keyframe = mjs_asKey(mjs_nextElement(spec_, keyframe->element));
|
keyframe = mjs_asKey(mjs_nextElement(spec_, keyframe->element));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1338,7 +1354,9 @@ class ModelWriter {
|
|||||||
mjsActuator *actuator =
|
mjsActuator *actuator =
|
||||||
mjs_asActuator(mjs_firstElement(spec_, mjOBJ_ACTUATOR));
|
mjs_asActuator(mjs_firstElement(spec_, mjOBJ_ACTUATOR));
|
||||||
while (actuator) {
|
while (actuator) {
|
||||||
WriteActuator(actuator, scope->GetPath());
|
if (ShouldWrite(actuator->element)) {
|
||||||
|
WriteActuator(actuator, scope->GetPath());
|
||||||
|
}
|
||||||
actuator = mjs_asActuator(mjs_nextElement(spec_, actuator->element));
|
actuator = mjs_asActuator(mjs_nextElement(spec_, actuator->element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1800,7 +1818,9 @@ class ModelWriter {
|
|||||||
void WriteSites(mjsBody *body) {
|
void WriteSites(mjsBody *body) {
|
||||||
mjsSite *site = mjs_asSite(mjs_firstChild(body, mjOBJ_SITE, false));
|
mjsSite *site = mjs_asSite(mjs_firstChild(body, mjOBJ_SITE, false));
|
||||||
while (site) {
|
while (site) {
|
||||||
WriteSite(site, body);
|
if (ShouldWrite(site->element)) {
|
||||||
|
WriteSite(site, body);
|
||||||
|
}
|
||||||
site = mjs_asSite(mjs_nextChild(body, site->element, false));
|
site = mjs_asSite(mjs_nextChild(body, site->element, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1808,7 +1828,9 @@ class ModelWriter {
|
|||||||
void WriteGeoms(mjsBody *body) {
|
void WriteGeoms(mjsBody *body) {
|
||||||
mjsGeom *geom = mjs_asGeom(mjs_firstChild(body, mjOBJ_GEOM, false));
|
mjsGeom *geom = mjs_asGeom(mjs_firstChild(body, mjOBJ_GEOM, false));
|
||||||
while (geom) {
|
while (geom) {
|
||||||
WriteGeom(geom, body);
|
if (ShouldWrite(geom->element)) {
|
||||||
|
WriteGeom(geom, body);
|
||||||
|
}
|
||||||
geom = mjs_asGeom(mjs_nextChild(body, geom->element, false));
|
geom = mjs_asGeom(mjs_nextChild(body, geom->element, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1824,7 +1846,9 @@ class ModelWriter {
|
|||||||
// WriteJoint properly handles the case where the parent is the worldbody.
|
// WriteJoint properly handles the case where the parent is the worldbody.
|
||||||
WriteJoint(nullptr, body);
|
WriteJoint(nullptr, body);
|
||||||
} else {
|
} else {
|
||||||
WriteJoint(joint, body);
|
if (ShouldWrite(joint->element)) {
|
||||||
|
WriteJoint(joint, body);
|
||||||
|
}
|
||||||
if (mjs_asJoint(mjs_nextChild(body, joint->element, false))) {
|
if (mjs_asJoint(mjs_nextChild(body, joint->element, false))) {
|
||||||
TF_WARN(
|
TF_WARN(
|
||||||
"Multiple joints found for body %d. Only writing the first one.",
|
"Multiple joints found for body %d. Only writing the first one.",
|
||||||
@@ -2153,7 +2177,9 @@ class ModelWriter {
|
|||||||
void WriteCameras(mjsBody *body) {
|
void WriteCameras(mjsBody *body) {
|
||||||
mjsCamera *cam = mjs_asCamera(mjs_firstChild(body, mjOBJ_CAMERA, false));
|
mjsCamera *cam = mjs_asCamera(mjs_firstChild(body, mjOBJ_CAMERA, false));
|
||||||
while (cam) {
|
while (cam) {
|
||||||
WriteCamera(cam, body);
|
if (ShouldWrite(cam->element)) {
|
||||||
|
WriteCamera(cam, body);
|
||||||
|
}
|
||||||
cam = mjs_asCamera(mjs_nextChild(body, cam->element, false));
|
cam = mjs_asCamera(mjs_nextChild(body, cam->element, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2178,7 +2204,9 @@ class ModelWriter {
|
|||||||
void WriteLights(mjsBody *body) {
|
void WriteLights(mjsBody *body) {
|
||||||
mjsLight *light = mjs_asLight(mjs_firstChild(body, mjOBJ_LIGHT, false));
|
mjsLight *light = mjs_asLight(mjs_firstChild(body, mjOBJ_LIGHT, false));
|
||||||
while (light) {
|
while (light) {
|
||||||
WriteLight(light, body);
|
if (ShouldWrite(light->element)) {
|
||||||
|
WriteLight(light, body);
|
||||||
|
}
|
||||||
light = mjs_asLight(mjs_nextChild(body, light->element, false));
|
light = mjs_asLight(mjs_nextChild(body, light->element, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2286,22 +2314,27 @@ class ModelWriter {
|
|||||||
body_spec->SetField(pxr::SdfFieldKeys->CustomData, customData);
|
body_spec->SetField(pxr::SdfFieldKeys->CustomData, customData);
|
||||||
|
|
||||||
body_paths_[body_id] = body_spec->GetPath();
|
body_paths_[body_id] = body_spec->GetPath();
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(body->element, body_spec->GetPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteBodies() {
|
void WriteBodies() {
|
||||||
mjsBody *body = mjs_asBody(mjs_firstElement(spec_, mjOBJ_BODY));
|
mjsBody *body = mjs_asBody(mjs_firstElement(spec_, mjOBJ_BODY));
|
||||||
while (body) {
|
while (body) {
|
||||||
// Only write a rigidbody if we are not the world body.
|
// If this body originally came from USD, don't try and write it
|
||||||
// We fall through since the world body might have static
|
// again.
|
||||||
// geom children.
|
if (ShouldWrite(body->element)) {
|
||||||
if (mjs_getId(body->element) != kWorldIndex) {
|
// Only write a rigidbody if we are not the world body.
|
||||||
WriteBody(body);
|
// We fall through since the world body might have static
|
||||||
|
// geom children.
|
||||||
|
if (mjs_getId(body->element) != kWorldIndex) {
|
||||||
|
WriteBody(body);
|
||||||
|
}
|
||||||
|
WriteSites(body);
|
||||||
|
WriteGeoms(body);
|
||||||
|
WriteJoints(body);
|
||||||
|
WriteCameras(body);
|
||||||
|
WriteLights(body);
|
||||||
}
|
}
|
||||||
WriteSites(body);
|
|
||||||
WriteGeoms(body);
|
|
||||||
WriteJoints(body);
|
|
||||||
WriteCameras(body);
|
|
||||||
WriteLights(body);
|
|
||||||
body = mjs_asBody(mjs_nextElement(spec_, body->element));
|
body = mjs_asBody(mjs_nextElement(spec_, body->element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2323,14 +2356,14 @@ class ModelWriter {
|
|||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
namespace usd {
|
namespace usd {
|
||||||
|
|
||||||
bool WriteSpecToData(mjSpec *spec, pxr::SdfLayerRefPtr layer) {
|
bool WriteSpecToData(mjSpec *spec, pxr::SdfLayerRefPtr layer, bool skip_elems_from_usd) {
|
||||||
mjModel *model = mj_compile(spec, nullptr);
|
mjModel *model = mj_compile(spec, nullptr);
|
||||||
if (model == nullptr) {
|
if (model == nullptr) {
|
||||||
TF_ERROR(MujocoCompilationError, "%s", mjs_getError(spec));
|
TF_ERROR(MujocoCompilationError, "%s", mjs_getError(spec));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelWriter(spec, model, layer).Write();
|
ModelWriter(spec, model, layer, skip_elems_from_usd).Write();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ namespace usd {
|
|||||||
// Args:
|
// Args:
|
||||||
// spec: mjSpec built programmatically or via parsed XML.
|
// spec: mjSpec built programmatically or via parsed XML.
|
||||||
// layer: SdfLayerRefPtr that will be written to.
|
// layer: SdfLayerRefPtr that will be written to.
|
||||||
bool WriteSpecToData(mjSpec* spec, pxr::SdfLayerRefPtr layer);
|
// skip_elems_from_usd: If true, skips over elements in the spec that have a usd_prim_path
|
||||||
|
// custom attribute set.
|
||||||
|
bool WriteSpecToData(mjSpec* spec, pxr::SdfLayerRefPtr layer, bool skip_elems_from_usd = false);
|
||||||
} // namespace usd
|
} // namespace usd
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|
||||||
|
|||||||
@@ -278,6 +278,8 @@ mjsMesh* ParseUsdMesh(mjSpec* spec, const pxr::UsdPrim& prim, mjsGeom* geom,
|
|||||||
}
|
}
|
||||||
mjsMesh* mesh = mjs_addMesh(spec, nullptr);
|
mjsMesh* mesh = mjs_addMesh(spec, nullptr);
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(mesh->element, prim.GetPath());
|
||||||
|
|
||||||
geom->type = mjGEOM_MESH;
|
geom->type = mjGEOM_MESH;
|
||||||
pxr::UsdGeomMesh usd_mesh(prim);
|
pxr::UsdGeomMesh usd_mesh(prim);
|
||||||
std::vector<float> uservert;
|
std::vector<float> uservert;
|
||||||
@@ -928,6 +930,8 @@ void ParseMjcPhysicsTendon(mjSpec* spec,
|
|||||||
mjsTendon* mj_tendon = mjs_addTendon(spec, nullptr);
|
mjsTendon* mj_tendon = mjs_addTendon(spec, nullptr);
|
||||||
mjs_setName(mj_tendon->element, prim.GetPath().GetAsString().c_str());
|
mjs_setName(mj_tendon->element, prim.GetPath().GetAsString().c_str());
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(mj_tendon->element, prim.GetPath());
|
||||||
|
|
||||||
pxr::TfToken type;
|
pxr::TfToken type;
|
||||||
tendon.GetTypeAttr().Get(&type);
|
tendon.GetTypeAttr().Get(&type);
|
||||||
|
|
||||||
@@ -1221,6 +1225,8 @@ void ParseMjcPhysicsActuator(mjSpec* spec,
|
|||||||
mjsActuator* mj_act = mjs_addActuator(spec, nullptr);
|
mjsActuator* mj_act = mjs_addActuator(spec, nullptr);
|
||||||
mjs_setName(mj_act->element, prim.GetPath().GetAsString().c_str());
|
mjs_setName(mj_act->element, prim.GetPath().GetAsString().c_str());
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(mj_act->element, prim.GetPath());
|
||||||
|
|
||||||
auto group_attr = tran.GetGroupAttr();
|
auto group_attr = tran.GetGroupAttr();
|
||||||
if (group_attr.HasAuthoredValue()) {
|
if (group_attr.HasAuthoredValue()) {
|
||||||
group_attr.Get(&mj_act->group);
|
group_attr.Get(&mj_act->group);
|
||||||
@@ -1664,6 +1670,8 @@ void ParseUsdGeomGprim(mjSpec* spec, const pxr::UsdPrim& gprim,
|
|||||||
geom->contype = 0;
|
geom->contype = 0;
|
||||||
geom->conaffinity = 0;
|
geom->conaffinity = 0;
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(geom->element, gprim.GetPath());
|
||||||
|
|
||||||
ParseDisplayColorAndOpacity(gprim, geom);
|
ParseDisplayColorAndOpacity(gprim, geom);
|
||||||
SetLocalPoseFromPrim(gprim, body_prim, geom, caches.xform_cache);
|
SetLocalPoseFromPrim(gprim, body_prim, geom, caches.xform_cache);
|
||||||
if (!MaybeParseGeomPrimitive(gprim, geom, caches.xform_cache)) {
|
if (!MaybeParseGeomPrimitive(gprim, geom, caches.xform_cache)) {
|
||||||
@@ -1719,6 +1727,8 @@ void ParseUsdPhysicsCollider(mjSpec* spec,
|
|||||||
geom->contype = 1;
|
geom->contype = 1;
|
||||||
geom->conaffinity = 1;
|
geom->conaffinity = 1;
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(geom->element, prim.GetPath());
|
||||||
|
|
||||||
if (prim.HasAPI<pxr::MjcPhysicsCollisionAPI>()) {
|
if (prim.HasAPI<pxr::MjcPhysicsCollisionAPI>()) {
|
||||||
ParseMjcPhysicsCollisionAPI(geom, pxr::MjcPhysicsCollisionAPI(prim));
|
ParseMjcPhysicsCollisionAPI(geom, pxr::MjcPhysicsCollisionAPI(prim));
|
||||||
}
|
}
|
||||||
@@ -1799,6 +1809,8 @@ void ParseUsdPhysicsJoint(mjSpec* spec, const pxr::UsdPrim& prim, mjsBody* body,
|
|||||||
mj_joint->type = type;
|
mj_joint->type = type;
|
||||||
mjs_setName(mj_joint->element, prim.GetPath().GetAsString().c_str());
|
mjs_setName(mj_joint->element, prim.GetPath().GetAsString().c_str());
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(mj_joint->element, prim.GetPath());
|
||||||
|
|
||||||
if (prim.IsA<pxr::UsdPhysicsRevoluteJoint>()) {
|
if (prim.IsA<pxr::UsdPhysicsRevoluteJoint>()) {
|
||||||
pxr::UsdPhysicsRevoluteJoint revolute(prim);
|
pxr::UsdPhysicsRevoluteJoint revolute(prim);
|
||||||
TfToken axis;
|
TfToken axis;
|
||||||
@@ -1888,6 +1900,8 @@ void ParseMjcPhysicsSite(mjSpec* spec, const pxr::MjcPhysicsSiteAPI& site_api,
|
|||||||
site_api.GetPrim().GetPath().GetAsString().c_str());
|
site_api.GetPrim().GetPath().GetAsString().c_str());
|
||||||
SetLocalPoseFromPrim(site_api.GetPrim(), parent_prim, site, xform_cache);
|
SetLocalPoseFromPrim(site_api.GetPrim(), parent_prim, site, xform_cache);
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(site->element, prim.GetPath());
|
||||||
|
|
||||||
auto group_attr = site_api.GetGroupAttr();
|
auto group_attr = site_api.GetGroupAttr();
|
||||||
if (group_attr.HasAuthoredValue()) {
|
if (group_attr.HasAuthoredValue()) {
|
||||||
group_attr.Get(&site->group);
|
group_attr.Get(&site->group);
|
||||||
@@ -1933,6 +1947,9 @@ void ParseMjcPhysicsKeyframe(mjSpec* spec,
|
|||||||
if (n_time_samples == 0) {
|
if (n_time_samples == 0) {
|
||||||
// If no time samples, we create a single keyframe.
|
// If no time samples, we create a single keyframe.
|
||||||
mjsKey* key = mjs_addKey(spec);
|
mjsKey* key = mjs_addKey(spec);
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(key->element, prim.GetPath());
|
||||||
|
|
||||||
mjs_setName(key->element, prim.GetName().GetString().c_str());
|
mjs_setName(key->element, prim.GetName().GetString().c_str());
|
||||||
setKeyframeData(key, qpos_attr, &key->qpos);
|
setKeyframeData(key, qpos_attr, &key->qpos);
|
||||||
setKeyframeData(key, qvel_attr, &key->qvel);
|
setKeyframeData(key, qvel_attr, &key->qvel);
|
||||||
@@ -1947,6 +1964,9 @@ void ParseMjcPhysicsKeyframe(mjSpec* spec,
|
|||||||
int keyframe_id = 0;
|
int keyframe_id = 0;
|
||||||
for (double time : times) {
|
for (double time : times) {
|
||||||
mjsKey* key = mjs_addKey(spec);
|
mjsKey* key = mjs_addKey(spec);
|
||||||
|
|
||||||
|
mujoco::usd::SetUsdPrimPathUserValue(key->element, prim.GetPath());
|
||||||
|
|
||||||
std::string key_name =
|
std::string key_name =
|
||||||
prim.GetName().GetString() + "_" + std::to_string(keyframe_id++);
|
prim.GetName().GetString() + "_" + std::to_string(keyframe_id++);
|
||||||
mjs_setName(key->element, key_name.c_str());
|
mjs_setName(key->element, key_name.c_str());
|
||||||
|
|||||||
@@ -18,9 +18,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <mujoco/experimental/usd/utils.h>
|
#include <mujoco/experimental/usd/utils.h>
|
||||||
|
#include "experimental/usd/plugins/mjcf/mujoco_to_usd.h"
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include <pxr/usd/sdf/path.h>
|
#include <pxr/usd/sdf/path.h>
|
||||||
#include <pxr/usd/usd/common.h>
|
#include <pxr/usd/usd/common.h>
|
||||||
|
#include <pxr/usd/usd/stage.h>
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
namespace usd {
|
namespace usd {
|
||||||
@@ -30,11 +32,20 @@ namespace usd {
|
|||||||
// API.
|
// API.
|
||||||
Writer::Writer(pxr::UsdStageRefPtr stage, mjSpec* spec, mjModel_* model)
|
Writer::Writer(pxr::UsdStageRefPtr stage, mjSpec* spec, mjModel_* model)
|
||||||
: stage_(stage), spec_(spec), model_(model) {
|
: stage_(stage), spec_(spec), model_(model) {
|
||||||
|
WriteSpecsWithoutUSDOriginToLayer();
|
||||||
BuildMjUsdMapping();
|
BuildMjUsdMapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
Writer::~Writer() = default;
|
Writer::~Writer() = default;
|
||||||
|
|
||||||
|
void Writer::WriteSpecsWithoutUSDOriginToLayer() {
|
||||||
|
// The user may have constructed the spec from sources other than USD.
|
||||||
|
// In this case, we will write write all those spec elements to a new layer in
|
||||||
|
// the stage. This allows us to keep the same writing API for a simulation regardless
|
||||||
|
// of how a spec was produced.
|
||||||
|
mujoco::usd::WriteSpecToData(spec_, stage_->GetEditTarget().GetLayer(), true);
|
||||||
|
}
|
||||||
|
|
||||||
void Writer::BuildMjUsdMapping() {
|
void Writer::BuildMjUsdMapping() {
|
||||||
body_id_to_path_.assign(model_->nbody, pxr::SdfPath());
|
body_id_to_path_.assign(model_->nbody, pxr::SdfPath());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user