mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
W.I.P. Add Prim::add_child() and add make unique Prim name feature.
This commit is contained in:
@@ -24,6 +24,8 @@ void CreateScene(tinyusdz::Stage *stage) {
|
||||
// scene(Stage) in multi-threaded context, The app must take care of resource locks
|
||||
// in the app layer.
|
||||
|
||||
std::string err;
|
||||
|
||||
//
|
||||
// Create simple material with UsdPrevieSurface.
|
||||
//
|
||||
@@ -49,22 +51,27 @@ void CreateScene(tinyusdz::Stage *stage) {
|
||||
|
||||
surfaceShader.metallic = 0.3f;
|
||||
// TODO: UsdUVTexture, UsdPrimvarReader***, UsdTransform2d
|
||||
|
||||
|
||||
// Connect to UsdPreviewSurface's outputs:surface by setting targetPath.
|
||||
//
|
||||
//
|
||||
// token outputs:surface = </mat/defaultPBR.outputs:surface>
|
||||
mat.surface.set(tinyusdz::Path(/* prim path */"/mat/defaultPBR", /* prop path */"outputs:surface"));
|
||||
|
||||
//
|
||||
// Shaer::value is `value::Value` type, so can use '=' to assign Shader object.
|
||||
//
|
||||
shader.value = std::move(surfaceShader);
|
||||
shader.value = std::move(surfaceShader);
|
||||
}
|
||||
|
||||
tinyusdz::Prim shaderPrim(shader);
|
||||
tinyusdz::Prim matPrim(mat);
|
||||
matPrim.children().emplace_back(std::move(shaderPrim));
|
||||
|
||||
//matPrim.children().emplace_back(std::move(shaderPrim)); // no uniqueness check
|
||||
|
||||
// Use add_child() to ensure child Prim has unique name.
|
||||
if (!matPrim.add_child(std::move(shaderPrim), /* rename Prim name if_required */true, &err)) {
|
||||
std::cerr << "Failed to constrcut Scene: " << err << "\n";
|
||||
}
|
||||
|
||||
//
|
||||
// To construct Prim, first create concrete Prim object(e.g. Xform, GeomMesh),
|
||||
@@ -287,15 +294,20 @@ void CreateScene(tinyusdz::Stage *stage) {
|
||||
|
||||
}
|
||||
|
||||
tinyusdz::GeomSphere sphere;
|
||||
tinyusdz::GeomSphere sphere1;
|
||||
{
|
||||
sphere.name = "sphere0";
|
||||
|
||||
sphere.radius = 3.14;
|
||||
|
||||
sphere1.name = "sphere0";
|
||||
|
||||
sphere1.radius = 3.14;
|
||||
}
|
||||
|
||||
tinyusdz::GeomSphere sphere2;
|
||||
{
|
||||
sphere2.name = ""; // unique name will be assigned in add_child().
|
||||
sphere2.radius = 1.05;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create Scene(Stage) hierarchy.
|
||||
// You can add child Prim to parent Prim's `children()`.
|
||||
@@ -303,7 +315,8 @@ void CreateScene(tinyusdz::Stage *stage) {
|
||||
// [Xform]
|
||||
// |
|
||||
// +- [Mesh]
|
||||
// +- [Sphere]
|
||||
// +- [Sphere0]
|
||||
// +- [Sphere1]
|
||||
//
|
||||
// [Material]
|
||||
// |
|
||||
@@ -315,7 +328,7 @@ void CreateScene(tinyusdz::Stage *stage) {
|
||||
// Xform::name, ...)
|
||||
tinyusdz::Prim meshPrim(mesh);
|
||||
|
||||
tinyusdz::Prim spherePrim(sphere);
|
||||
tinyusdz::Prim spherePrim(sphere1);
|
||||
{
|
||||
// variantSet is maniuplated in Prim.
|
||||
// Currently we don't provide easy API for variantSet.
|
||||
@@ -328,7 +341,7 @@ void CreateScene(tinyusdz::Stage *stage) {
|
||||
|
||||
// key = variantSet name, value = default Variant selection
|
||||
vsmap.emplace("colorVariant", "red");
|
||||
|
||||
|
||||
spherePrim.metas().variants = vsmap;
|
||||
spherePrim.metas().variantSets = std::make_pair(tinyusdz::ListEditQual::Append, variantSetList);
|
||||
|
||||
@@ -357,23 +370,38 @@ void CreateScene(tinyusdz::Stage *stage) {
|
||||
|
||||
variantSet.name = "green";
|
||||
variantSet.variantSet.emplace("green", redVariant);
|
||||
|
||||
|
||||
spherePrim.variantSets().emplace("colorVariant", variantSet);
|
||||
}
|
||||
|
||||
|
||||
tinyusdz::Prim spherePrim2(sphere2);
|
||||
|
||||
tinyusdz::Prim xformPrim(xform);
|
||||
|
||||
xformPrim.children().emplace_back(std::move(meshPrim));
|
||||
xformPrim.children().emplace_back(std::move(spherePrim));
|
||||
//xformPrim.children().emplace_back(std::move(meshPrim));
|
||||
//xformPrim.children().emplace_back(std::move(spherePrim));
|
||||
|
||||
// Use add_child() to ensure child Prim has unique name.
|
||||
if (!xformPrim.add_child(std::move(meshPrim), /* rename Prim name if_required */true, &err)) {
|
||||
std::cerr << "Failed to constrcut Scene: " << err << "\n";
|
||||
}
|
||||
|
||||
if (!xformPrim.add_child(std::move(spherePrim), /* rename Prim name if_required */true, &err)) {
|
||||
std::cerr << "Failed to constrcut Scene: " << err << "\n";
|
||||
}
|
||||
|
||||
#if 0
|
||||
// set rename Prim `true`, otherwise `add_child` fails since spherePrim2 does not have valid & unique Prim name.
|
||||
if (!xformPrim.add_child(std::move(spherePrim2), /* rename Prim name if_required */true, &err)) {
|
||||
std::cerr << "Failed to constrcut Scene: " << err << "\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
// If you want to specify the appearance/traversal order of child Prim(e.g. Showing Prim tree in GUI, Ascii output), set "primChildren"(token[]) metadata
|
||||
// xfromPrim.metas().primChildren.size() must be identical to xformPrim.children().size()
|
||||
xformPrim.metas().primChildren.push_back(tinyusdz::value::token(spherePrim.element_name()));
|
||||
xformPrim.metas().primChildren.push_back(tinyusdz::value::token(meshPrim.element_name()));
|
||||
std::cout << "sphere.element_name = " << spherePrim.element_name() << "\n";
|
||||
std::cout << "mesh.element_name = " << meshPrim.element_name() << "\n";
|
||||
xformPrim.metas().primChildren.push_back(tinyusdz::value::token(xformPrim.children()[1].element_name()));
|
||||
xformPrim.metas().primChildren.push_back(tinyusdz::value::token(xformPrim.children()[0].element_name()));
|
||||
//xformPrim.metas().primChildren.push_back(tinyusdz::value::token(xformPrim.children()[2].element_name()));
|
||||
|
||||
stage->metas().defaultPrim = tinyusdz::value::token(xformPrim.element_name()); // token
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright 2021 - Present, Syoyo Fujita.
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
//
|
||||
@@ -30,6 +31,59 @@
|
||||
|
||||
namespace tinyusdz {
|
||||
|
||||
namespace {
|
||||
|
||||
///
|
||||
/// Simply add number suffix to make unique string.
|
||||
///
|
||||
/// - plane -> plane1
|
||||
/// - sphere1 -> sphere11
|
||||
/// - xform4 -> xform41
|
||||
///
|
||||
///
|
||||
bool makeUniqueName(std::multiset<std::string> &nameSet, const std::string &name, std::string *unique_name) {
|
||||
if (!unique_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nameSet.count(name) == 0) {
|
||||
(*unique_name) = name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Simply add number
|
||||
|
||||
const size_t kMaxLoop = 1024; // to avoid infinite loop.
|
||||
|
||||
std::string new_name = name;
|
||||
|
||||
size_t cnt = 0;
|
||||
while (cnt < kMaxLoop) {
|
||||
|
||||
size_t i = nameSet.count(new_name);
|
||||
if (i == 0) {
|
||||
// This should not happen though.
|
||||
return false;
|
||||
}
|
||||
|
||||
new_name += std::to_string(i);
|
||||
|
||||
if (nameSet.count(new_name) == 0) {
|
||||
|
||||
(*unique_name) = new_name;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
nonstd::optional<Interpolation> InterpolationFromString(const std::string &v) {
|
||||
if ("faceVarying" == v) {
|
||||
return Interpolation::FaceVarying;
|
||||
@@ -851,6 +905,7 @@ nonstd::optional<std::string> GetPrimElementName(const value::Value &v) {
|
||||
EXTRACT_NAME_AND_RETURN_PATH(RectLight)
|
||||
EXTRACT_NAME_AND_RETURN_PATH(Material)
|
||||
EXTRACT_NAME_AND_RETURN_PATH(Shader)
|
||||
// TODO: extract name must be handled in Shader class
|
||||
EXTRACT_NAME_AND_RETURN_PATH(UsdPreviewSurface)
|
||||
EXTRACT_NAME_AND_RETURN_PATH(UsdUVTexture)
|
||||
EXTRACT_NAME_AND_RETURN_PATH(UsdPrimvarReader_int)
|
||||
@@ -863,6 +918,7 @@ nonstd::optional<std::string> GetPrimElementName(const value::Value &v) {
|
||||
EXTRACT_NAME_AND_RETURN_PATH(UsdPrimvarReader_vector)
|
||||
EXTRACT_NAME_AND_RETURN_PATH(UsdPrimvarReader_point)
|
||||
EXTRACT_NAME_AND_RETURN_PATH(UsdPrimvarReader_matrix)
|
||||
//
|
||||
EXTRACT_NAME_AND_RETURN_PATH(SkelRoot)
|
||||
EXTRACT_NAME_AND_RETURN_PATH(Skeleton)
|
||||
EXTRACT_NAME_AND_RETURN_PATH(SkelAnimation)
|
||||
@@ -904,6 +960,7 @@ bool SetPrimElementName(value::Value &v, const std::string &elementName) {
|
||||
SET_ELEMENT_NAME(elementName, RectLight)
|
||||
SET_ELEMENT_NAME(elementName, Material)
|
||||
SET_ELEMENT_NAME(elementName, Shader)
|
||||
// TODO: set element name must be handled in Shader class
|
||||
SET_ELEMENT_NAME(elementName, UsdPreviewSurface)
|
||||
SET_ELEMENT_NAME(elementName, UsdUVTexture)
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_int)
|
||||
@@ -911,6 +968,12 @@ bool SetPrimElementName(value::Value &v, const std::string &elementName) {
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_float2)
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_float3)
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_float4)
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_string)
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_normal)
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_vector)
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_point)
|
||||
SET_ELEMENT_NAME(elementName, UsdPrimvarReader_matrix)
|
||||
//
|
||||
SET_ELEMENT_NAME(elementName, SkelRoot)
|
||||
SET_ELEMENT_NAME(elementName, Skeleton)
|
||||
SET_ELEMENT_NAME(elementName, SkelAnimation)
|
||||
@@ -980,6 +1043,78 @@ Prim::Prim(const std::string &elementPath, value::Value &&rhs) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Prim::add_child(Prim &&rhs, const bool rename_prim_name, std::string *err) {
|
||||
|
||||
#if defined(TINYUSD_ENABLE_THREAD)
|
||||
// TODO: Only take a lock when dirty.
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
#endif
|
||||
|
||||
|
||||
std::string elementName = rhs.element_name();
|
||||
|
||||
if (elementName.empty()) {
|
||||
if (err) {
|
||||
(*err) = "Prim has empty elementName.\n";
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (rename_prim_name) {
|
||||
// assign default name `default`
|
||||
elementName = "default";
|
||||
}
|
||||
}
|
||||
|
||||
if (_children.size() != _childrenNameSet.size()) {
|
||||
// Rebuild _childrenNames
|
||||
_childrenNameSet.clear();
|
||||
for (size_t i = 0; i < _children.size(); i++) {
|
||||
if (_childrenNameSet.count(_children[i].element_name())) {
|
||||
if (err) {
|
||||
(*err) = "Internal error: _children contains Prim with same elementName.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_childrenNameSet.insert(_children[i].element_name());
|
||||
}
|
||||
}
|
||||
|
||||
if (_childrenNameSet.count(elementName)) {
|
||||
if (rename_prim_name) {
|
||||
std::string unique_name;
|
||||
if (!makeUniqueName(_childrenNameSet, elementName, &unique_name)) {
|
||||
if (err) {
|
||||
(*err) = fmt::format("Internal error. cannot assign unique name for `{}`.\n", elementName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
elementName = unique_name;
|
||||
|
||||
if (!SetPrimElementName(rhs.get_data(), elementName)) {
|
||||
if (err) {
|
||||
(*err) = fmt::format("Internal error. cannot modify Prim's elementName.\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (err) {
|
||||
(*err) = fmt::format("Prim name(elementName) {} already exists in children.\n", rhs.element_name());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
_childrenNameSet.insert(elementName);
|
||||
_children.emplace_back(std::move(rhs));
|
||||
_child_dirty = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<int64_t> &Prim::get_child_indices_from_primChildren(bool force_update, bool *indices_is_valid) const {
|
||||
#if defined(TINYUSD_ENABLE_THREAD)
|
||||
// TODO: Only take a lock when dirty.
|
||||
|
||||
@@ -2656,17 +2656,47 @@ class Prim {
|
||||
_elementPath = Path(elementName, "");
|
||||
}
|
||||
|
||||
void add_child(Prim &&prim) {
|
||||
_children.emplace_back(std::move(prim));
|
||||
_child_dirty = true;
|
||||
}
|
||||
///
|
||||
/// Add Prim as a child.
|
||||
/// When `rename_element_name` is true, rename input Prims elementName to make it unique among children(since USD(Crate) spec doesn't allow same Prim elementName in the same Prim hierarchy.
|
||||
///
|
||||
/// Renaming rule is Maya-like:
|
||||
/// - No elementName given: `default`
|
||||
/// - Add or increment number suffix to the elementName:
|
||||
/// - `plane` => `plane1`
|
||||
/// - `plane1` => `plane2`
|
||||
///
|
||||
/// Note: This function is thread-safe.
|
||||
///
|
||||
/// @return true Upon success. false when failed(e.g. Prim with same Prim::element_name() already exists when `rename_element_name` is false) and fill `err` with error message
|
||||
///
|
||||
bool add_child(Prim &&prim, const bool rename_element_name = true, std::string *err = nullptr);
|
||||
|
||||
// TODO: Deprecate this API to disallow modification of children?.
|
||||
#if 0
|
||||
///
|
||||
/// Add Prim as a child.
|
||||
///
|
||||
///
|
||||
/// @return true Upon success. false when failed(e.g. Prim with same Prim::element_name() already exists) and fill `err` with error message
|
||||
///
|
||||
/// Note: This function is thread-safe.
|
||||
///
|
||||
bool add_child(Prim &&prim, const std::string &basename, std::string *err = nullptr);
|
||||
#endif
|
||||
|
||||
//{
|
||||
//
|
||||
// _children.emplace_back(std::move(prim));
|
||||
// _child_dirty = true;
|
||||
//}
|
||||
|
||||
// TODO: Deprecate this API to disallow direct modification of children.
|
||||
std::vector<Prim> &children() { return _children; }
|
||||
|
||||
const std::vector<Prim> &children() const { return _children; }
|
||||
|
||||
const value::Value &data() const { return _data; }
|
||||
value::Value &get_data() { return _data; }
|
||||
|
||||
Specifier &specifier() { return _specifier; }
|
||||
|
||||
@@ -2788,7 +2818,10 @@ class Prim {
|
||||
Specifier::Invalid}; // `def`, `over` or `class`. Usually `def`
|
||||
value::Value
|
||||
_data; // Generic container for concrete Prim object. GPrim, Xform, ...
|
||||
|
||||
std::vector<Prim> _children; // child Prim nodes
|
||||
//std::set<std::string> _childrenNames; // child Prim name(elementName).
|
||||
std::multiset<std::string> _childrenNameSet; // Stores input child Prim's elementName to assign unique elementName in `add_child`
|
||||
|
||||
mutable bool _child_dirty{false};
|
||||
mutable bool _primChildrenIndicesIsValid{
|
||||
|
||||
@@ -181,4 +181,16 @@ struct TokenKeyEqual {
|
||||
|
||||
#endif // TINYUSDZ_USE_STRING_ID_FOR_TOKEN_TYPE
|
||||
|
||||
inline bool operator==(const Token &lhs, const Token &rhs) {
|
||||
return TokenKeyEqual()(lhs, rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const Token &lhs, const Token &rhs) {
|
||||
return !TokenKeyEqual()(lhs, rhs);
|
||||
}
|
||||
|
||||
inline bool operator<(const Token &lhs, const Token &rhs) {
|
||||
return lhs.str() < rhs.str();
|
||||
}
|
||||
|
||||
} // namespace tinyusdz
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
TEST_LIST = {
|
||||
{ "prim_type_test", prim_type_test },
|
||||
{ "prim_add_test", prim_add_test },
|
||||
{ "primvar_test", primvar_test },
|
||||
{ "value_types_test", value_types_test },
|
||||
{ "xformOp_test", xformOp_test },
|
||||
|
||||
@@ -115,3 +115,23 @@ void prim_type_test(void) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void prim_add_test(void) {
|
||||
Model amodel;
|
||||
Model bmodel;
|
||||
Model cmodel;
|
||||
Model rootmodel;
|
||||
|
||||
Prim aprim("test01", amodel);
|
||||
Prim bprim("test02", bmodel);
|
||||
Prim cprim("test01", cmodel);
|
||||
Prim root("root", rootmodel);
|
||||
|
||||
TEST_CHECK(root.add_child(std::move(aprim)));
|
||||
|
||||
TEST_CHECK(root.add_child(std::move(bprim)));
|
||||
|
||||
// cannot add child Prim with same elementName
|
||||
TEST_CHECK(!root.add_child(std::move(bprim)));
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void prim_type_test(void);
|
||||
void prim_add_test(void);
|
||||
|
||||
@@ -12,6 +12,13 @@ using namespace tinyusdz;
|
||||
|
||||
void value_types_test(void) {
|
||||
|
||||
value::token tok1("bora");
|
||||
value::token tok2("muda");
|
||||
value::token tok3("bora");
|
||||
TEST_CHECK(tok1 == tok1);
|
||||
TEST_CHECK(tok1 != tok2);
|
||||
TEST_CHECK(tok1 == tok3);
|
||||
|
||||
TEST_CHECK(value::GetTypeName(value::TYPE_ID_TOKEN) == "token");
|
||||
TEST_CHECK(value::GetTypeName(value::TYPE_ID_TOKEN|value::TYPE_ID_1D_ARRAY_BIT) == "token[]");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user