Construct PrimSpec tree from USD(for Composition. W.I.P.)

This commit is contained in:
Syoyo Fujita
2022-11-19 04:59:59 +09:00
parent cf7a978504
commit fc0619c115
11 changed files with 220 additions and 195 deletions

View File

@@ -5,4 +5,3 @@ TabWidth: 2
UseTab: Never
BreakBeforeBraces: Attach
Standard: Cpp11
IndentPPDirective: BeforeHash

View File

@@ -8,12 +8,21 @@
int main(int argc, char **argv) {
if (argc < 2) {
std::cout << "Need input.usda\n";
std::cout << "Need input.usda (--flatten)\n";
exit(-1);
}
std::string filename = argv[1];
bool do_compose =false;
if (argc > 2) {
std::string arg = argv[2];
if (arg == "--flatten") {
do_compose = true;
}
}
std::string base_dir;
base_dir = tinyusdz::io::GetBaseDir(filename);
@@ -41,8 +50,15 @@ int main(int argc, char **argv) {
#endif
reader.SetBaseDir(base_dir);
tinyusdz::LoadState state = tinyusdz::LoadState::Toplevel;
if (do_compose) {
// FIXME
state = tinyusdz::LoadState::Sublayer;
std::cout << "Enable composition.\n";
}
{
bool ret = reader.Read();
bool ret = reader.Read(state);
if (!ret) {
std::cerr << "Failed to parse .usda: \n";

View File

@@ -4295,29 +4295,49 @@ bool AsciiParser::ParseBlock(const Specifier spec, const int64_t primIdx,
std::string pTy = prim_type;
if (prim_type.empty()) {
// No Prim type specified. Treat it as Model
if (IsToplevel()) {
pTy = "Model";
}
if (prim_type.empty()) {
// No Prim type specified. Treat it as Model
if (_prim_construct_fun_map.count(pTy)) {
auto construct_fun = _prim_construct_fun_map[pTy];
pTy = "Model";
}
Path fullpath(GetCurrentPath(), "");
Path pname(prim_name, "");
nonstd::expected<bool, std::string> ret = construct_fun(
fullpath, spec, pname, primIdx, parentPrimIdx, props, in_metas);
if (_prim_construct_fun_map.count(pTy)) {
auto construct_fun = _prim_construct_fun_map[pTy];
if (!ret) {
// construction failed.
PUSH_ERROR_AND_RETURN("Constructing Prim type `" + pTy +
"` failed: " + ret.error());
Path fullpath(GetCurrentPath(), "");
Path pname(prim_name, "");
nonstd::expected<bool, std::string> ret = construct_fun(
fullpath, spec, pname, primIdx, parentPrimIdx, props, in_metas);
if (!ret) {
// construction failed.
PUSH_ERROR_AND_RETURN("Constructing Prim type `" + pTy +
"` failed: " + ret.error());
}
} else {
PUSH_WARN(fmt::format(
"TODO: Unsupported/Unimplemented Prim type: `{}`. Skipping parsing.",
pTy));
}
} else {
PUSH_WARN(fmt::format(
"TODO: Unsupported/Unimplemented Prim type: `{}`. Skipping parsing.",
pTy));
// Load scene as PrimSpec tree
if (_primspec_fun) {
Path fullpath(GetCurrentPath(), "");
Path pname(prim_name, "");
// pass prim_type as is(empty = empty string)
nonstd::expected<bool, std::string> ret = _primspec_fun(
fullpath, spec, prim_type, pname, primIdx, parentPrimIdx, props, in_metas);
if (!ret) {
// construction failed.
PUSH_ERROR_AND_RETURN(fmt::format("Constructing PrimSpec typeName `{}`, elementName `{}` failed: {}", prim_type, prim_name, ret.error()));
}
} else {
PUSH_ERROR_AND_RETURN_TAG(kAscii, "[Internal Error] PrimSpec handler is not found.");
}
}
PopPath();
@@ -4327,12 +4347,12 @@ bool AsciiParser::ParseBlock(const Specifier spec, const int64_t primIdx,
///
/// Parser entry point
/// TODO: Refactor
/// TODO: Refactor and use unified code path regardless of LoadState.
///
bool AsciiParser::Parse(LoadState state) {
_sub_layered = (state == LoadState::SUBLAYER);
_referenced = (state == LoadState::REFERENCE);
_payloaded = (state == LoadState::PAYLOAD);
_sub_layered = (state == LoadState::Sublayer);
_referenced = (state == LoadState::Reference);
_payloaded = (state == LoadState::Payload);
bool header_ok = ParseMagicHeader();
if (!header_ok) {
@@ -4355,13 +4375,14 @@ bool AsciiParser::Parse(LoadState state) {
if (c == '(') {
// stage meta.
// TODO: We could skip parsing stage meta in flatten(composition) mode.
if (!ParseStageMetas()) {
PUSH_ERROR_AND_RETURN("Failed to parse Stage metas.");
}
}
}
if (_stage_meta_process_fun) {
if (IsToplevel() && _stage_meta_process_fun) {
DCOUT("StageMeta callback.");
bool ret = _stage_meta_process_fun(_stage_metas);
if (!ret) {

View File

@@ -5,11 +5,13 @@
#pragma once
#include <cerrno>
#include <functional>
//#include <functional>
#include <stdio.h>
#include <stack>
//#include "external/better-enums/enum.h"
#include "composition.hh"
#include "prim-types.hh"
#include "stream-reader.hh"
#include "tinyusdz.hh"
@@ -49,38 +51,6 @@ struct PathIdentifier : std::string {
// using std::string;
};
enum class LoadState {
TOPLEVEL, // toplevel .usda input
SUBLAYER, // .usda is read by 'subLayers'
REFERENCE, // .usda is read by `references`
PAYLOAD, // .usda is read by `payload`
};
// Prim Kind
// https://graphics.pixar.com/usd/release/glossary.html#usdglossary-kind
#if 1
enum class Kind {
Model, // "model"
Group, // "group"
Assembly, // "assembly"
Component, // "component"
Subcomponent, // "subcomponent"
};
#else
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
BETTER_ENUM(Kind, int, model, group, assembly, component, subcomponent);
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif
///
/// Test if input file is USDA ascii format.
///
@@ -93,8 +63,9 @@ class AsciiParser {
// Frequently used prim metas
nonstd::optional<Kind> kind;
value::dict customData; // `customData`
std::vector<value::StringData> strings; // String only unregistered metadata.
value::dict customData; // `customData`
std::vector<value::StringData>
strings; // String only unregistered metadata.
};
// TODO: Unifity class with StageMetas in prim-types.hh
@@ -104,7 +75,7 @@ class AsciiParser {
///
std::vector<value::AssetPath> subLayers; // 'subLayers'
value::token defaultPrim; // 'defaultPrim'
value::StringData doc; // 'doc' or 'documentation'
value::StringData doc; // 'doc' or 'documentation'
nonstd::optional<Axis> upAxis; // not specified = nullopt
nonstd::optional<double> metersPerUnit;
nonstd::optional<double> timeCodesPerSecond;
@@ -211,11 +182,6 @@ class AsciiParser {
~AsciiParser();
///
/// Callback functions which is called from a class outside of
/// AsciiParser(i.e. USDAReader)
///
///
/// Assign index to primitive for index-based prim scene graph representation.
/// -1 = root
@@ -289,6 +255,18 @@ class AsciiParser {
_post_prim_construct_fun_map[prim_type] = fun;
}
///
/// For composition(Treat Prim as generic container).
/// AsciiParser(i.e. USDAReader)
///
using PrimSpecFunction = std::function<nonstd::expected<bool, std::string>(
const Path &full_path, const Specifier spec, const std::string &primTypeName,
const Path &prim_name, const int64_t primIdx, const int64_t parentPrimIdx,
const std::map<std::string, Property> &properties,
const PrimMetaMap &in_meta)>;
void RegisterPrimSpecFunction(PrimSpecFunction fun) { _primspec_fun = fun; }
///
/// Base filesystem directory to search asset files.
///
@@ -307,30 +285,35 @@ class AsciiParser {
///
/// Parser entry point
///
bool Parse(LoadState state = LoadState::TOPLEVEL);
bool Parse(LoadState state = LoadState::Toplevel);
///
/// Parse TimeSample value with specified array type of `type_id`(value::TypeId)
/// (You can obrain type_id from string using value::GetTypeId())
/// Parse TimeSample value with specified array type of
/// `type_id`(value::TypeId) (You can obrain type_id from string using
/// value::GetTypeId())
///
bool ParseTimeSampleValue(const uint32_t type_id, value::Value *result);
///
/// Parse TimeSample value with specified `type_name`(Appears in USDA. .e.g. "float", "matrix2d")
/// Parse TimeSample value with specified `type_name`(Appears in USDA. .e.g.
/// "float", "matrix2d")
///
bool ParseTimeSampleValue(const std::string &type_name, value::Value *result);
///
/// Parse TimeSample value with specified base type of `type_id`(value::TypeId)
/// (You can obrain type_id from string using value::GetTypeId())
/// Parse TimeSample value with specified base type of
/// `type_id`(value::TypeId) (You can obrain type_id from string using
/// value::GetTypeId())
///
bool ParseTimeSampleValueOfArrayType(const uint32_t base_type_id, value::Value *result);
bool ParseTimeSampleValueOfArrayType(const uint32_t base_type_id,
value::Value *result);
///
/// Parse TimeSample value with specified array type of `type_name`("[]" omiotted. .e.g. "float" for "float[]")
/// Parse TimeSample value with specified array type of `type_name`("[]"
/// omiotted. .e.g. "float" for "float[]")
///
bool ParseTimeSampleValueOfArrayType(const std::string &type_name, value::Value *result);
bool ParseTimeSampleValueOfArrayType(const std::string &type_name,
value::Value *result);
// TODO: ParseBasicType?
bool ParsePurpose(Purpose *result);
@@ -740,8 +723,8 @@ class AsciiParser {
// -- [TimeSamples] -------------------
//template <typename T>
//using TimeSampleData = std::vector<std::pair<double, nonstd::optional<T>>>;
// template <typename T>
// using TimeSampleData = std::vector<std::pair<double, nonstd::optional<T>>>;
// template <typename T>
// using TimeSampleDataArray = std::vector<std::pair<double,
@@ -751,19 +734,20 @@ class AsciiParser {
/// Convert TimeSampleData<T> to TimeSamples(type-erased TimeSample Sdata
/// struct)
///
//template <typename T>
//value::TimeSamples ConvertToTimeSamples(const TimeSampleData<T> &in);
// template <typename T>
// value::TimeSamples ConvertToTimeSamples(const TimeSampleData<T> &in);
//template <typename T>
//value::TimeSamples ConvertToTimeSamples(
// const TimeSampleData<std::vector<T>> &in);
// template <typename T>
// value::TimeSamples ConvertToTimeSamples(
// const TimeSampleData<std::vector<T>> &in);
//// T = scalar(e.g. `float`)
//template <typename T>
//nonstd::optional<TimeSampleData<T>> TryParseTimeSamples();
// template <typename T>
// nonstd::optional<TimeSampleData<T>> TryParseTimeSamples();
//template <typename T>
//nonstd::optional<TimeSampleData<std::vector<T>>> TryParseTimeSamplesOfArray();
// template <typename T>
// nonstd::optional<TimeSampleData<std::vector<T>>>
// TryParseTimeSamplesOfArray();
// ---------------------------------------
@@ -847,8 +831,8 @@ class AsciiParser {
std::map<std::string, PrimConstructFunction> _prim_construct_fun_map;
std::map<std::string, PostPrimConstructFunction> _post_prim_construct_fun_map;
// class Impl;
// Impl *_impl;
// For composition. PrimSpec is typeless so single callback function only.
PrimSpecFunction _primspec_fun{nullptr};
};
} // namespace ascii

21
src/composition.hh Normal file
View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022-Present Light Transport Entertainment Inc.
//
// Layer and Prim composition features.
//
#pragma once
#include "prim-types.hh"
namespace tinyusdz {
// USD asset loading state.
enum class LoadState
{
Toplevel, // load initial .usd(default)
Sublayer, // load USD from Stage meta sublayer
Reference, // load USD from Prim meta reference
Payload // load USD from Prim meta payload
};
} // namespace tinyusdz

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022-Present Light Transport Entertainment Inc.
#include "prim-composition.hh"
#include "composition.hh"
namespace tinyusdz {
namespace prim {

View File

@@ -1,19 +0,0 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022-Present Light Transport Entertainment Inc.
//
// Prim composition features.
//
#pragma once
#include "prim-types.hh"
namespace tinyusdz {
namespace prim {
class LayerStack
{
std::vector<Layer> layers;
};
} // namespace prim
} // namespace tinyusdz

View File

@@ -293,15 +293,17 @@ bool Stage::find_prim_from_relative_path(const Prim &root,
}
}
bool Stage::LoadLayerFromMemory(const uint8_t *addr, const size_t nbytes, const std::string &asset_name, Layer *layer) {
bool Stage::LoadLayerFromMemory(const uint8_t *addr, const size_t nbytes, const std::string &asset_name, const LoadState load_state, Layer *layer) {
// TODO: USDC/USDZ support.
tinyusdz::StreamReader sr(addr, nbytes, /* swap endian */ false);
tinyusdz::usda::USDAReader reader(&sr);
// TODO: Uase AssetResolver
//reader.SetBaseDir(base_dir);
if (!reader.Read(ascii::LoadState::REFERENCE)) {
if (!reader.Read(load_state)) {
return false;
}
@@ -312,7 +314,7 @@ bool Stage::LoadLayerFromMemory(const uint8_t *addr, const size_t nbytes, const
return false;
}
bool Stage::LoadLayerFromFile(const std::string &_filename, Layer *layer) {
bool Stage::LoadLayerFromFile(const std::string &_filename, const LoadState load_state, Layer *layer) {
// TODO: Setup AssetResolver.
std::string filepath = io::ExpandFilePath(_filename, /* userdata */ nullptr);
@@ -328,7 +330,7 @@ bool Stage::LoadLayerFromFile(const std::string &_filename, Layer *layer) {
PUSH_ERROR_AND_RETURN("Read file failed: " + err);
}
return LoadLayerFromMemory(data.data(), data.size(), filepath, layer);
return LoadLayerFromMemory(data.data(), data.size(), filepath, load_state, layer);
}
bool Stage::LoadSubLayers(std::vector<Layer> *sublayers) {

View File

@@ -5,6 +5,7 @@
#pragma once
#include "prim-types.hh"
#include "composition.hh"
namespace tinyusdz {
@@ -129,11 +130,11 @@ class Stage {
///
/// Loads USD from and return it as Layer
///
bool LoadLayerFromFile(const std::string &filename, Layer *layer);
bool LoadLayerFromFile(const std::string &filename, const LoadState load_state, Layer *layer);
///
/// Loads USD asset from memory and return it as Layer
bool LoadLayerFromMemory(const uint8_t *addr, const size_t nbytes, const std::string &asset_name, Layer *layer);
bool LoadLayerFromMemory(const uint8_t *addr, const size_t nbytes, const std::string &asset_name, const LoadState load_state, Layer *dest);
///
/// Loads `reference` USD asset and return it as Layer

View File

@@ -518,6 +518,76 @@ class USDAReader::Impl {
return true;
}
void RegisterPrimSpecHandler() {
_parser.RegisterPrimSpecFunction(
[&](const Path &full_path, const Specifier spec, const std::string &typeName, const Path &prim_name, const int64_t primIdx,
const int64_t parentPrimIdx,
const prim::PropertyMap &properties,
const ascii::AsciiParser::PrimMetaMap &in_meta)
-> nonstd::expected<bool, std::string> {
if (!prim_name.is_valid()) {
return nonstd::make_unexpected("Invalid Prim name: " +
prim_name.full_path_name());
}
if (prim_name.is_absolute_path() || prim_name.is_root_path()) {
return nonstd::make_unexpected(
"Prim name should not starts with '/' or contain `/`: Prim "
"name = " +
prim_name.full_path_name());
}
if (!prim_name.prop_part().empty()) {
return nonstd::make_unexpected(
"Prim path should not contain property part(`.`): Prim name "
"= " +
prim_name.full_path_name());
}
if (primIdx < 0) {
return nonstd::make_unexpected(
"Unexpected primIdx value. primIdx must be positive.");
}
if (prim_name.prim_part().empty()) {
return nonstd::make_unexpected("Prim's name should not be empty ");
}
PrimSpec primspec;
primspec.name() = prim_name.prim_part();
primspec.specifier() = spec;
primspec.typeName() = typeName;
DCOUT("primspec name, primType = " << prim_name.prim_part() << ", " << typeName);
// Assign index for PrimSpec
// TODO: Use sample id table(= _prim_nodes)
if (size_t(primIdx) >= _primspec_nodes.size()) {
_primspec_nodes.resize(size_t(primIdx) + 1);
}
DCOUT("sz " << std::to_string(_primspec_nodes.size())
<< ", primIdx = " << primIdx);
_primspec_nodes[size_t(primIdx)].primSpec = std::move(primspec);
DCOUT("primspec[" << primIdx << "].ty = "
<< _primspec_nodes[size_t(primIdx)].primSpec.typeName());
_primspec_nodes[size_t(primIdx)].parent = parentPrimIdx;
if (parentPrimIdx == -1) {
_toplevel_primspecs.push_back(size_t(primIdx));
} else {
_primspec_nodes[size_t(parentPrimIdx)].children.push_back(
size_t(primIdx));
return true;
}
return true;
}
);
}
void ImportScene(tinyusdz::Stage &scene) { _imported_scene = scene; }
bool HasPath(const std::string &path) {
@@ -937,7 +1007,7 @@ class USDAReader::Impl {
/// TODO: Use callback function(visitor) so that Reconstruct**** function is
/// invoked in the Parser context.
///
bool Read(ascii::LoadState state = ascii::LoadState::TOPLEVEL);
bool Read(LoadState state = LoadState::Toplevel);
// std::vector<GPrim> GetGPrims() { return _gprims; }
@@ -1551,7 +1621,7 @@ bool USDAReader::Impl::ReconstructPrim(
/// -- Impl Read
///
bool USDAReader::Impl::Read(ascii::LoadState state) {
bool USDAReader::Impl::Read(LoadState state) {
///
/// Setup callbacks.
///
@@ -1559,6 +1629,10 @@ bool USDAReader::Impl::Read(ascii::LoadState state) {
RegisterPrimIdxAssignCallback();
// For composition(load state = !Toplevel)
RegisterPrimSpecHandler();
// For direct Prim reconstruction(load state = Toplevel)
RegisterReconstructCallback<Model>(); // Generic prim.
RegisterReconstructCallback<GPrim>(); // Geometric prim
@@ -1600,80 +1674,6 @@ bool USDAReader::Impl::Read(ascii::LoadState state) {
PUSH_ERROR_AND_RETURN("Parse failed:" + _parser.GetError());
}
#if 0
DCOUT("# of toplevel prims = " << std::to_string(PrimSize()));
{
size_t i = 0;
for (auto it = PrimBegin(); it != PrimEnd(); ++it, i++) {
DCOUT("Prim[" << std::to_string(i) << "].type = " << (*it).type_name());
}
}
#endif
#if 0
//_sub_layered = (state == LOAD_STATE_SUBLAYER);
//_referenced = (state == LOAD_STATE_REFERENCE);
//_payloaded = (state == LOAD_STATE_PAYLOAD);
// Stage meta.
if (!_parser.ParseStageMetas()) {
PUSH_PARSER_ERROR_AND_RETURN();
return false;
}
DCOUT("Done parsing Stage metas");
// parse blocks
while (!_parser.Eof()) {
if (!_parser.SkipCommentAndWhitespaceAndNewline()) {
PUSH_PARSER_ERROR_AND_RETURN();
}
if (_parser.Eof()) {
// Whitespaces in the end of line.
break;
}
// Look ahead token
auto curr_loc = _parser.CurrLoc();
DCOUT("loc = " << curr_loc);
std::string tok;
if (!_parser.ReadIdentifier(&tok)) {
DCOUT("Failed to read identifier");
PUSH_PARSER_ERROR_AND_RETURN();
}
DCOUT("tok = " << tok);
// Rewind
if (!_parser.SeekTo(curr_loc)) {
PUSH_PARSER_ERROR_AND_RETURN();
}
if (tok == "def") {
DCOUT("`def` block");
bool block_ok = _parser.ParseDefBlock();
if (!block_ok) {
PUSH_PARSER_ERROR_AND_RETURN();
}
} else if (tok == "over") {
DCOUT("`over` block");
bool block_ok = _parser.ParseOverBlock();
if (!block_ok) {
PUSH_PARSER_ERROR_AND_RETURN();
}
} else if (tok == "class") {
DCOUT("`class` block");
bool block_ok = _parser.ParseClassBlock();
if (!block_ok) {
PUSH_PARSER_ERROR_AND_RETURN();
}
} else {
PUSH_ERROR_AND_RETURN("Unknown identifier '" + tok + "' for Prim block statement.");
}
}
#endif
return true;
}
@@ -1703,7 +1703,7 @@ USDAReader::USDAReader(StreamReader *sr) { _impl = new Impl(sr); }
USDAReader::~USDAReader() { delete _impl; }
bool USDAReader::Read(ascii::LoadState state) { return _impl->Read(state); }
bool USDAReader::Read(const LoadState state) { return _impl->Read(state); }
void USDAReader::SetBaseDir(const std::string &dir) {
return _impl->SetBaseDir(dir);
@@ -1744,7 +1744,7 @@ USDAReader::~USDAReader() {
bool USDAReader::CheckHeader() { return false; }
bool USDAReader::Read(ascii::LoadState state) {
bool USDAReader::Read(const LoadState state) {
(void)state;
return false;
}

View File

@@ -44,7 +44,7 @@ class USDAReader {
///
/// Reader entry point
///
bool Read(ascii::LoadState state = ascii::LoadState::TOPLEVEL);
bool Read(LoadState state = LoadState::Toplevel);
///
///