WIP: Brush assets project #106303
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "BKE_bake_data_block_map.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_volume_grid_fwd.hh"
|
||||
|
||||
namespace blender::bke::bake {
|
||||
|
||||
|
@ -81,6 +82,17 @@ class AttributeBakeItem : public BakeItem {
|
|||
}
|
||||
};
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
class VolumeGridBakeItem : public BakeItem {
|
||||
public:
|
||||
/** Using #unique_ptr so that `BKE_volume_grid_fwd.hh` can be used. */
|
||||
std::unique_ptr<GVolumeGrid> grid;
|
||||
|
||||
VolumeGridBakeItem(std::unique_ptr<GVolumeGrid> grid);
|
||||
~VolumeGridBakeItem();
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Storage for a single value of a trivial type like `float`, `int`, etc. */
|
||||
class PrimitiveBakeItem : public BakeItem {
|
||||
private:
|
||||
|
|
|
@ -121,6 +121,11 @@ class SocketValueVariant {
|
|||
*/
|
||||
bool is_context_dependent_field() const;
|
||||
|
||||
/**
|
||||
* The stored value is a volume grid.
|
||||
*/
|
||||
bool is_volume_grid() const;
|
||||
|
||||
/**
|
||||
* Convert the stored value into a single value. For simple value access, this is not necessary,
|
||||
* because #get` does the conversion implicitly. However, it is necessary if one wants to use
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "BKE_mesh.hh"
|
||||
#include "BKE_pointcloud.hh"
|
||||
#include "BKE_volume.hh"
|
||||
#include "BKE_volume_grid.hh"
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
||||
|
@ -134,6 +135,14 @@ void GeometryBakeItem::try_restore_data_blocks(GeometrySet &main_geometry,
|
|||
});
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
VolumeGridBakeItem::VolumeGridBakeItem(std::unique_ptr<GVolumeGrid> grid) : grid(std::move(grid))
|
||||
{
|
||||
}
|
||||
|
||||
VolumeGridBakeItem::~VolumeGridBakeItem() = default;
|
||||
#endif
|
||||
|
||||
PrimitiveBakeItem::PrimitiveBakeItem(const CPPType &type, const void *value) : type_(type)
|
||||
{
|
||||
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
|
||||
|
|
|
@ -1197,6 +1197,23 @@ static void serialize_bake_item(const BakeItem &item,
|
|||
r_io_item.append_str("type", "ATTRIBUTE");
|
||||
r_io_item.append_str("name", attribute_state_item->name());
|
||||
}
|
||||
#ifdef WITH_OPENVDB
|
||||
else if (const auto *grid_state_item = dynamic_cast<const VolumeGridBakeItem *>(&item)) {
|
||||
r_io_item.append_str("type", "GRID");
|
||||
const GVolumeGrid &grid = *grid_state_item->grid;
|
||||
auto io_vdb = blob_writer
|
||||
.write_as_stream(".vdb",
|
||||
[&](std::ostream &stream) {
|
||||
openvdb::GridCPtrVec vdb_grids;
|
||||
bke::VolumeTreeAccessToken tree_token;
|
||||
vdb_grids.push_back(grid->grid_ptr(tree_token));
|
||||
openvdb::io::Stream vdb_stream(stream);
|
||||
vdb_stream.write(vdb_grids);
|
||||
})
|
||||
.serialize();
|
||||
r_io_item.append("vdb", std::move(io_vdb));
|
||||
}
|
||||
#endif
|
||||
else if (const auto *string_state_item = dynamic_cast<const StringBakeItem *>(&item)) {
|
||||
r_io_item.append_str("type", "STRING");
|
||||
const StringRefNull str = string_state_item->value();
|
||||
|
@ -1246,6 +1263,39 @@ static std::unique_ptr<BakeItem> deserialize_bake_item(const DictionaryValue &io
|
|||
}
|
||||
return std::make_unique<AttributeBakeItem>(std::move(*name));
|
||||
}
|
||||
#ifdef WITH_OPENVDB
|
||||
if (*state_item_type == StringRef("GRID")) {
|
||||
const DictionaryValue &io_grid = io_item;
|
||||
const auto *io_vdb = io_grid.lookup_dict("vdb");
|
||||
if (!io_vdb) {
|
||||
return {};
|
||||
}
|
||||
std::optional<BlobSlice> vdb_slice = BlobSlice::deserialize(*io_vdb);
|
||||
if (!vdb_slice) {
|
||||
return {};
|
||||
}
|
||||
openvdb::GridPtrVecPtr vdb_grids;
|
||||
if (!blob_reader.read_as_stream(*vdb_slice, [&](std::istream &stream) {
|
||||
try {
|
||||
openvdb::io::Stream vdb_stream{stream};
|
||||
vdb_grids = vdb_stream.getGrids();
|
||||
return true;
|
||||
}
|
||||
catch (...) {
|
||||
return false;
|
||||
}
|
||||
}))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
if (vdb_grids->size() != 1) {
|
||||
return {};
|
||||
}
|
||||
std::shared_ptr<openvdb::GridBase> vdb_grid = std::move((*vdb_grids)[0]);
|
||||
GVolumeGrid grid{std::move(vdb_grid)};
|
||||
return std::make_unique<VolumeGridBakeItem>(std::make_unique<GVolumeGrid>(grid));
|
||||
}
|
||||
#endif
|
||||
if (*state_item_type == StringRef("STRING")) {
|
||||
const std::shared_ptr<io::serialize::Value> *io_data = io_item.lookup("data");
|
||||
if (!io_data) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "BKE_geometry_fields.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_socket_value.hh"
|
||||
#include "BKE_volume_grid.hh"
|
||||
|
||||
namespace blender::bke::bake {
|
||||
|
||||
|
@ -80,6 +81,14 @@ Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(const Span<voi
|
|||
}
|
||||
bake_items[i] = std::make_unique<AttributeBakeItem>(attribute_name);
|
||||
}
|
||||
#ifdef WITH_OPENVDB
|
||||
else if (value_variant.is_volume_grid()) {
|
||||
bke::GVolumeGrid grid = value_variant.get<bke::GVolumeGrid>();
|
||||
grid.get_for_write().set_name(config.names[i]);
|
||||
bake_items[i] = std::make_unique<VolumeGridBakeItem>(
|
||||
std::make_unique<bke::GVolumeGrid>(std::move(grid)));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
value_variant.convert_to_single();
|
||||
GPointer value = value_variant.get_single_ptr();
|
||||
|
@ -152,6 +161,22 @@ Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(const Span<voi
|
|||
r_attribute_map.add(item->name(), attribute_id);
|
||||
return true;
|
||||
}
|
||||
#ifdef WITH_OPENVDB
|
||||
if (const auto *item = dynamic_cast<const VolumeGridBakeItem *>(&bake_item)) {
|
||||
const GVolumeGrid &grid = *item->grid;
|
||||
const VolumeGridType grid_type = grid->grid_type();
|
||||
const std::optional<eNodeSocketDatatype> grid_socket_type = grid_type_to_socket_type(
|
||||
grid_type);
|
||||
if (!grid_socket_type) {
|
||||
return false;
|
||||
}
|
||||
if (grid_socket_type == socket_type) {
|
||||
new (r_value) SocketValueVariant(*item->grid);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
|
|
|
@ -229,6 +229,11 @@ bool SocketValueVariant::is_context_dependent_field() const
|
|||
return field.node().depends_on_input();
|
||||
}
|
||||
|
||||
bool SocketValueVariant::is_volume_grid() const
|
||||
{
|
||||
return kind_ == Kind::Grid;
|
||||
}
|
||||
|
||||
void SocketValueVariant::convert_to_single()
|
||||
{
|
||||
switch (kind_) {
|
||||
|
|
|
@ -74,6 +74,24 @@ const EnumPropertyItem rna_enum_usd_mtl_name_collision_mode_items[] = {
|
|||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
const EnumPropertyItem rna_enum_usd_attr_import_mode_items[] = {
|
||||
{USD_ATTR_IMPORT_NONE, "NONE", 0, "None", "Do not import attributes"},
|
||||
{USD_ATTR_IMPORT_USER,
|
||||
"USER",
|
||||
0,
|
||||
"User",
|
||||
"Import attributes in the 'userProperties' namespace as "
|
||||
"Blender custom properties. The namespace will "
|
||||
"be stripped from the property names"},
|
||||
{USD_ATTR_IMPORT_ALL,
|
||||
"ALL",
|
||||
0,
|
||||
"All Custom",
|
||||
"Import all USD custom attributes as Blender custom properties. "
|
||||
"Namespaces will be retained in the property names"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
const EnumPropertyItem rna_enum_usd_tex_import_mode_items[] = {
|
||||
{USD_TEX_IMPORT_NONE, "IMPORT_NONE", 0, "None", "Don't import textures"},
|
||||
{USD_TEX_IMPORT_PACK, "IMPORT_PACK", 0, "Packed", "Import textures as packed data"},
|
||||
|
@ -189,6 +207,9 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
|||
const bool export_shapekeys = RNA_boolean_get(op->ptr, "export_shapekeys");
|
||||
const bool only_deform_bones = RNA_boolean_get(op->ptr, "only_deform_bones");
|
||||
|
||||
const bool export_custom_properties = RNA_boolean_get(op->ptr, "export_custom_properties");
|
||||
const bool author_blender_name = RNA_boolean_get(op->ptr, "author_blender_name");
|
||||
|
||||
char root_prim_path[FILE_MAX];
|
||||
RNA_string_get(op->ptr, "root_prim_path", root_prim_path);
|
||||
process_prim_path(root_prim_path);
|
||||
|
@ -212,6 +233,8 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
|||
export_textures,
|
||||
overwrite_textures,
|
||||
relative_paths,
|
||||
export_custom_properties,
|
||||
author_blender_name,
|
||||
};
|
||||
|
||||
STRNCPY(params.root_prim_path, root_prim_path);
|
||||
|
@ -238,6 +261,13 @@ static void wm_usd_export_draw(bContext *C, wmOperator *op)
|
|||
uiItemR(col, ptr, "visible_objects_only", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
col = uiLayoutColumn(box, true);
|
||||
uiItemR(col, ptr, "export_custom_properties", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
col = uiLayoutColumn(box, true);
|
||||
uiItemR(col, ptr, "author_blender_name", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "export_custom_properties"));
|
||||
|
||||
col = uiLayoutColumn(box, true);
|
||||
uiItemR(col, ptr, "export_animation", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(col, ptr, "export_hair", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
@ -280,8 +310,8 @@ static void wm_usd_export_draw(bContext *C, wmOperator *op)
|
|||
uiItemR(col, ptr, "relative_paths", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
box = uiLayoutBox(layout);
|
||||
uiItemL(box, IFACE_("Experimental"), ICON_NONE);
|
||||
uiItemR(box, ptr, "use_instancing", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
col = uiLayoutColumnWithHeading(box, true, IFACE_("Experimental"));
|
||||
uiItemR(col, ptr, "use_instancing", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
static void free_operator_customdata(wmOperator *op)
|
||||
|
@ -453,6 +483,18 @@ void WM_OT_usd_export(wmOperatorType *ot)
|
|||
"Root Prim",
|
||||
"If set, add a transform primitive with the given path to the stage "
|
||||
"as the parent of all exported data");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"export_custom_properties",
|
||||
true,
|
||||
"Export Custom Properties",
|
||||
"When checked, custom properties will be exported as USD User Properties");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"author_blender_name",
|
||||
true,
|
||||
"Author Blender Name",
|
||||
"When checked, custom userProperties will be authored to allow a round trip");
|
||||
}
|
||||
|
||||
/* ====== USD Import ====== */
|
||||
|
@ -533,6 +575,9 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
|
|||
const eUSDMtlNameCollisionMode mtl_name_collision_mode = eUSDMtlNameCollisionMode(
|
||||
RNA_enum_get(op->ptr, "mtl_name_collision_mode"));
|
||||
|
||||
const eUSDAttrImportMode attr_import_mode = eUSDAttrImportMode(
|
||||
RNA_enum_get(op->ptr, "attr_import_mode"));
|
||||
|
||||
/* TODO(makowalski): Add support for sequences. */
|
||||
const bool is_sequence = false;
|
||||
int offset = 0;
|
||||
|
@ -589,6 +634,7 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
|
|||
params.import_textures_mode = import_textures_mode;
|
||||
params.tex_name_collision_mode = tex_name_collision_mode;
|
||||
params.import_all_materials = import_all_materials;
|
||||
params.attr_import_mode = attr_import_mode;
|
||||
|
||||
STRNCPY(params.import_textures_dir, import_textures_dir);
|
||||
|
||||
|
@ -641,6 +687,7 @@ static void wm_usd_import_draw(bContext * /*C*/, wmOperator *op)
|
|||
uiItemR(col, ptr, "relative_path", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(col, ptr, "create_collection", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(box, ptr, "light_intensity_scale", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(col, ptr, "attr_import_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
box = uiLayoutBox(layout);
|
||||
col = uiLayoutColumnWithHeading(box, true, IFACE_("Materials"));
|
||||
|
@ -830,6 +877,13 @@ void WM_OT_usd_import(wmOperatorType *ot)
|
|||
USD_TEX_NAME_COLLISION_USE_EXISTING,
|
||||
"File Name Collision",
|
||||
"Behavior when the name of an imported texture file conflicts with an existing file");
|
||||
|
||||
RNA_def_enum(ot->srna,
|
||||
"attr_import_mode",
|
||||
rna_enum_usd_attr_import_mode_items,
|
||||
USD_ATTR_IMPORT_ALL,
|
||||
"Import Custom Properties",
|
||||
"Behavior when importing USD attributes as Blender custom properties");
|
||||
}
|
||||
|
||||
namespace blender::ed::io {
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <openvdb_fwd.hh>
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
#include "BKE_volume_grid_fwd.hh"
|
||||
# include <openvdb_fwd.hh>
|
||||
|
||||
# include "BKE_volume_grid_fwd.hh"
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
|
@ -16,3 +18,5 @@ openvdb::FloatGrid &resample_sdf_grid_if_necessary(bke::VolumeGrid<float> &volum
|
|||
std::shared_ptr<openvdb::FloatGrid> &storage);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
||||
#endif
|
||||
|
|
|
@ -120,6 +120,7 @@ set(SRC
|
|||
intern/usd_reader_stage.cc
|
||||
intern/usd_reader_volume.cc
|
||||
intern/usd_reader_xform.cc
|
||||
intern/usd_reader_utils.cc
|
||||
intern/usd_skel_convert.cc
|
||||
intern/usd_skel_root_utils.cc
|
||||
|
||||
|
@ -160,6 +161,7 @@ set(SRC
|
|||
intern/usd_reader_stage.hh
|
||||
intern/usd_reader_volume.hh
|
||||
intern/usd_reader_xform.hh
|
||||
intern/usd_reader_utils.hh
|
||||
intern/usd_skel_convert.hh
|
||||
intern/usd_skel_root_utils.hh
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "usd_reader_material.hh"
|
||||
#include "usd_reader_utils.hh"
|
||||
|
||||
#include "usd_asset_utils.hh"
|
||||
|
||||
|
@ -493,6 +494,9 @@ Material *USDMaterialReader::add_material(const pxr::UsdShadeMaterial &usd_mater
|
|||
}
|
||||
}
|
||||
|
||||
/* Load custom properties directly from the Material's prim. */
|
||||
set_id_props_from_prim(&mtl->id, usd_material.GetPrim());
|
||||
|
||||
return mtl;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,52 @@
|
|||
* Adapted from the Blender Alembic importer implementation. */
|
||||
|
||||
#include "usd_reader_prim.hh"
|
||||
#include "usd_reader_utils.hh"
|
||||
|
||||
#include "usd.hh"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include <pxr/usd/usd/prim.h>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
void USDPrimReader::set_props(const bool merge_with_parent,
|
||||
const pxr::UsdTimeCode motionSampleTime)
|
||||
{
|
||||
if (!prim_ || !object_) {
|
||||
return;
|
||||
}
|
||||
|
||||
eUSDAttrImportMode attr_import_mode = this->import_params_.attr_import_mode;
|
||||
|
||||
if (attr_import_mode == USD_ATTR_IMPORT_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (merge_with_parent) {
|
||||
/* This object represents a parent Xform merged with its child prim.
|
||||
* Set the parent prim's custom properties on the Object ID. */
|
||||
if (const pxr::UsdPrim parent_prim = prim_.GetParent()) {
|
||||
set_id_props_from_prim(&object_->id, parent_prim, attr_import_mode, motionSampleTime);
|
||||
}
|
||||
}
|
||||
if (!object_->data) {
|
||||
/* If the object has no data, set the prim's custom properties on the object.
|
||||
* This applies to Xforms that have been converted to Empty objects. */
|
||||
set_id_props_from_prim(&object_->id, prim_, attr_import_mode, motionSampleTime);
|
||||
}
|
||||
|
||||
if (object_->data) {
|
||||
/* If the object has data, the data represents the USD prim, so set the prim's custom
|
||||
* properties on the data directly. */
|
||||
set_id_props_from_prim(
|
||||
static_cast<ID *>(object_->data), prim_, attr_import_mode, motionSampleTime);
|
||||
}
|
||||
}
|
||||
|
||||
USDPrimReader::USDPrimReader(const pxr::UsdPrim &prim,
|
||||
const USDImportParams &import_params,
|
||||
const ImportSettings &settings)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/usd/prim.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
struct CacheFile;
|
||||
|
@ -161,6 +162,28 @@ class USDPrimReader {
|
|||
}
|
||||
|
||||
bool is_in_proto() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Convert custom attributes on the encapsulated USD prim (or on its parent)
|
||||
* to custom properties on the generated object and/or data. This function
|
||||
* assumes create_object() and read_object_data() have been called.
|
||||
*
|
||||
* If the generated object has instantiated data, it's assumed that the data
|
||||
* represents the USD prim, and the prim properties will be set on the data ID.
|
||||
* If the object data is null (which would be the case when a USD Xform is
|
||||
* converted to an Empty object), then the prim properties will be set on the
|
||||
* object ID. Finally, a true value for the 'merge_with_parent' argument indicates
|
||||
* that the object represents a USD Xform and its child prim that were merged
|
||||
* on import, and the properties of the prim's parent will be set on the object
|
||||
* ID.
|
||||
*
|
||||
* \param merge_with_parent: If true, set the properties of the prim's parent
|
||||
* on the object ID
|
||||
* \param motionSampleTime: The time code for sampling tha USD attributes
|
||||
*/
|
||||
void set_props(bool merge_with_parent = false,
|
||||
pxr::UsdTimeCode motionSampleTime = pxr::UsdTimeCode::Default());
|
||||
};
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
/* SPDX-FileCopyrightText: 2024 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "usd_reader_utils.hh"
|
||||
|
||||
#include <pxr/usd/usd/attribute.h>
|
||||
|
||||
#include "CLG_log.h"
|
||||
static CLG_LogRef LOG = {"io.usd"};
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename VECT>
|
||||
void set_array_prop(IDProperty *idgroup,
|
||||
const char *prop_name,
|
||||
const pxr::UsdAttribute &attr,
|
||||
const pxr::UsdTimeCode motionSampleTime)
|
||||
{
|
||||
if (!idgroup || !attr) {
|
||||
return;
|
||||
}
|
||||
|
||||
VECT vec;
|
||||
if (!attr.Get<VECT>(&vec, motionSampleTime)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDPropertyTemplate val = {0};
|
||||
val.array.len = static_cast<int>(vec.dimension);
|
||||
|
||||
if (val.array.len <= 0) {
|
||||
CLOG_WARN(&LOG, "Invalid array length for prop %s", prop_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::is_same<float, typename VECT::ScalarType>()) {
|
||||
val.array.type = IDP_FLOAT;
|
||||
}
|
||||
else if (std::is_same<pxr::GfHalf, typename VECT::ScalarType>()) {
|
||||
val.array.type = IDP_FLOAT;
|
||||
}
|
||||
else if (std::is_same<double, typename VECT::ScalarType>()) {
|
||||
val.array.type = IDP_DOUBLE;
|
||||
}
|
||||
else if (std::is_same<int, typename VECT::ScalarType>()) {
|
||||
val.array.type = IDP_INT;
|
||||
}
|
||||
else {
|
||||
CLOG_WARN(&LOG, "Couldn't determine array type for prop %s", prop_name);
|
||||
return;
|
||||
}
|
||||
|
||||
IDProperty *prop = IDP_New(IDP_ARRAY, &val, prop_name);
|
||||
|
||||
if (!prop) {
|
||||
CLOG_WARN(&LOG, "Couldn't create array prop %s", prop_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::is_same<pxr::GfHalf, typename VECT::ScalarType>()) {
|
||||
float *prop_data = static_cast<float *>(prop->data.pointer);
|
||||
for (int i = 0; i < val.array.len; ++i) {
|
||||
prop_data[i] = vec[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::memcpy(prop->data.pointer, vec.data(), prop->len * sizeof(typename VECT::ScalarType));
|
||||
}
|
||||
|
||||
IDP_AddToGroup(idgroup, prop);
|
||||
}
|
||||
|
||||
bool equivalent(const pxr::SdfValueTypeName &type_name1, const pxr::SdfValueTypeName &type_name2)
|
||||
{
|
||||
return type_name1.GetType().IsA(type_name2.GetType());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
/* TfToken objects are not cheap to construct, so we do it once. */
|
||||
namespace usdtokens {
|
||||
static const pxr::TfToken userProperties("userProperties", pxr::TfToken::Immortal);
|
||||
} // namespace usdtokens
|
||||
|
||||
static void set_string_prop(IDProperty *idgroup, const char *prop_name, const char *str_val)
|
||||
{
|
||||
if (!idgroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDPropertyTemplate val = {0};
|
||||
val.string.str = str_val;
|
||||
/* Note length includes null terminator. */
|
||||
val.string.len = strlen(str_val) + 1;
|
||||
val.string.subtype = IDP_STRING_SUB_UTF8;
|
||||
|
||||
IDProperty *prop = IDP_New(IDP_STRING, &val, prop_name);
|
||||
|
||||
IDP_AddToGroup(idgroup, prop);
|
||||
}
|
||||
|
||||
static void set_int_prop(IDProperty *idgroup, const char *prop_name, const int ival)
|
||||
{
|
||||
if (!idgroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDPropertyTemplate val = {0};
|
||||
val.i = ival;
|
||||
IDProperty *prop = IDP_New(IDP_INT, &val, prop_name);
|
||||
|
||||
IDP_AddToGroup(idgroup, prop);
|
||||
}
|
||||
|
||||
static void set_bool_prop(IDProperty *idgroup, const char *prop_name, const bool bval)
|
||||
{
|
||||
if (!idgroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDPropertyTemplate val = {0};
|
||||
val.i = bval;
|
||||
IDProperty *prop = IDP_New(IDP_BOOLEAN, &val, prop_name);
|
||||
|
||||
IDP_AddToGroup(idgroup, prop);
|
||||
}
|
||||
|
||||
static void set_float_prop(IDProperty *idgroup, const char *prop_name, const float fval)
|
||||
{
|
||||
if (!idgroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDPropertyTemplate val = {0};
|
||||
val.f = fval;
|
||||
IDProperty *prop = IDP_New(IDP_FLOAT, &val, prop_name);
|
||||
|
||||
IDP_AddToGroup(idgroup, prop);
|
||||
}
|
||||
|
||||
static void set_double_prop(IDProperty *idgroup, const char *prop_name, const double dval)
|
||||
{
|
||||
if (!idgroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDPropertyTemplate val = {0};
|
||||
val.d = dval;
|
||||
IDProperty *prop = IDP_New(IDP_DOUBLE, &val, prop_name);
|
||||
|
||||
IDP_AddToGroup(idgroup, prop);
|
||||
}
|
||||
|
||||
void set_id_props_from_prim(ID *id,
|
||||
const pxr::UsdPrim &prim,
|
||||
const eUSDAttrImportMode attr_import_mode,
|
||||
const pxr::UsdTimeCode time_code)
|
||||
{
|
||||
pxr::UsdAttributeVector attribs = prim.GetAuthoredAttributes();
|
||||
if (attribs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool all_custom_attrs = (attr_import_mode == USD_ATTR_IMPORT_ALL);
|
||||
|
||||
for (const pxr::UsdAttribute &attr : attribs) {
|
||||
if (!attr.IsCustom()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> attr_names = attr.SplitName();
|
||||
|
||||
bool is_user_prop = attr_names[0] == "userProperties";
|
||||
|
||||
if (attr_names.size() > 2 && is_user_prop && attr_names[1] == "blender") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!all_custom_attrs && !is_user_prop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IDProperty *idgroup = IDP_EnsureProperties(id);
|
||||
|
||||
/* When importing user properties, strip the namespace. */
|
||||
pxr::TfToken attr_name;
|
||||
if (is_user_prop) {
|
||||
/* We strip the userProperties namespace, but leave others in case
|
||||
* someone's custom attribute namespace is important in their pipeline. */
|
||||
const std::string token = "userProperties:";
|
||||
const std::string name = attr.GetName().GetString();
|
||||
attr_name = pxr::TfToken(name.substr(token.size(), name.size() - token.size()));
|
||||
}
|
||||
else {
|
||||
attr_name = attr.GetName();
|
||||
}
|
||||
|
||||
pxr::SdfValueTypeName type_name = attr.GetTypeName();
|
||||
|
||||
if (type_name == pxr::SdfValueTypeNames->Int) {
|
||||
int ival = 0;
|
||||
if (attr.Get<int>(&ival, time_code)) {
|
||||
set_int_prop(idgroup, attr_name.GetString().c_str(), ival);
|
||||
}
|
||||
}
|
||||
else if (type_name == pxr::SdfValueTypeNames->Float) {
|
||||
float fval = 0.0f;
|
||||
if (attr.Get<float>(&fval, time_code)) {
|
||||
set_float_prop(idgroup, attr_name.GetString().c_str(), fval);
|
||||
}
|
||||
}
|
||||
else if (type_name == pxr::SdfValueTypeNames->Double) {
|
||||
double dval = 0.0;
|
||||
if (attr.Get<double>(&dval, time_code)) {
|
||||
set_double_prop(idgroup, attr_name.GetString().c_str(), dval);
|
||||
}
|
||||
}
|
||||
else if (type_name == pxr::SdfValueTypeNames->Half) {
|
||||
pxr::GfHalf hval = 0.0f;
|
||||
if (attr.Get<pxr::GfHalf>(&hval, time_code)) {
|
||||
set_float_prop(idgroup, attr_name.GetString().c_str(), hval);
|
||||
}
|
||||
}
|
||||
else if (type_name == pxr::SdfValueTypeNames->String) {
|
||||
std::string sval;
|
||||
if (attr.Get<std::string>(&sval, time_code)) {
|
||||
set_string_prop(idgroup, attr_name.GetString().c_str(), sval.c_str());
|
||||
}
|
||||
}
|
||||
else if (type_name == pxr::SdfValueTypeNames->Token) {
|
||||
pxr::TfToken tval;
|
||||
if (attr.Get<pxr::TfToken>(&tval, time_code)) {
|
||||
set_string_prop(idgroup, attr_name.GetString().c_str(), tval.GetString().c_str());
|
||||
}
|
||||
}
|
||||
else if (type_name == pxr::SdfValueTypeNames->Asset) {
|
||||
pxr::SdfAssetPath aval;
|
||||
if (attr.Get<pxr::SdfAssetPath>(&aval, time_code)) {
|
||||
set_string_prop(idgroup, attr_name.GetString().c_str(), aval.GetAssetPath().c_str());
|
||||
}
|
||||
}
|
||||
else if (type_name == pxr::SdfValueTypeNames->Bool) {
|
||||
bool bval = false;
|
||||
if (attr.Get<bool>(&bval, time_code)) {
|
||||
set_bool_prop(idgroup, attr_name.GetString().c_str(), bval);
|
||||
}
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Float2)) {
|
||||
set_array_prop<pxr::GfVec2f>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Float3)) {
|
||||
set_array_prop<pxr::GfVec3f>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Float4)) {
|
||||
set_array_prop<pxr::GfVec4f>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Double2)) {
|
||||
set_array_prop<pxr::GfVec2d>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Double3)) {
|
||||
set_array_prop<pxr::GfVec3d>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Double4)) {
|
||||
set_array_prop<pxr::GfVec4d>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Int2)) {
|
||||
set_array_prop<pxr::GfVec2i>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Int3)) {
|
||||
set_array_prop<pxr::GfVec3i>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Int4)) {
|
||||
set_array_prop<pxr::GfVec4i>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Half2)) {
|
||||
set_array_prop<pxr::GfVec2h>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Half3)) {
|
||||
set_array_prop<pxr::GfVec3h>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
else if (equivalent(type_name, pxr::SdfValueTypeNames->Half4)) {
|
||||
set_array_prop<pxr::GfVec4h>(idgroup, attr_name.GetString().c_str(), attr, time_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-FileCopyrightText: 2024 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "usd.hh"
|
||||
|
||||
#include <pxr/usd/usd/prim.h>
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
void set_id_props_from_prim(ID *id,
|
||||
const pxr::UsdPrim &prim,
|
||||
eUSDAttrImportMode attr_import_mode = USD_ATTR_IMPORT_ALL,
|
||||
pxr::UsdTimeCode time_code = pxr::UsdTimeCode::Default());
|
||||
|
||||
} // namespace blender::io::usd
|
|
@ -54,6 +54,9 @@ void USDXformReader::read_object_data(Main * /*bmain*/, const double motionSampl
|
|||
}
|
||||
|
||||
BKE_object_apply_mat4(object_, transform_from_usd, true, false);
|
||||
|
||||
/* Make sure to collect custom attributes */
|
||||
set_props(use_parent_xform(), motionSampleTime);
|
||||
}
|
||||
|
||||
void USDXformReader::read_matrix(float r_mat[4][4] /* local matrix */,
|
||||
|
|
|
@ -27,6 +27,7 @@ static const pxr::TfToken preview_shader("previewShader", pxr::TfToken::Immortal
|
|||
static const pxr::TfToken preview_surface("UsdPreviewSurface", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken surface("surface", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken blender_ns("userProperties:blender", pxr::TfToken::Immortal);
|
||||
} // namespace usdtokens
|
||||
|
||||
static std::string get_mesh_active_uvlayer_name(const Object *ob)
|
||||
|
@ -42,8 +43,109 @@ static std::string get_mesh_active_uvlayer_name(const Object *ob)
|
|||
return name ? name : "";
|
||||
}
|
||||
|
||||
template<typename USDT>
|
||||
bool set_vec_attrib(const pxr::UsdPrim &prim,
|
||||
const IDProperty *prop,
|
||||
const pxr::TfToken &prop_token,
|
||||
const pxr::SdfValueTypeName &type_name,
|
||||
const pxr::UsdTimeCode &timecode)
|
||||
{
|
||||
if (!prim || !prop || !prop->data.pointer || prop_token.IsEmpty() || !type_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pxr::UsdAttribute vec_attr = prim.CreateAttribute(prop_token, type_name, true);
|
||||
|
||||
if (!vec_attr) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Couldn't create USD attribute for array property %s",
|
||||
prop_token.GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
USDT vec_value(static_cast<typename USDT::ScalarType *>(prop->data.pointer));
|
||||
|
||||
return vec_attr.Set(vec_value, timecode);
|
||||
}
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
static void create_vector_attrib(const pxr::UsdPrim &prim,
|
||||
const IDProperty *prop,
|
||||
const pxr::TfToken &prop_token,
|
||||
const pxr::UsdTimeCode &timecode)
|
||||
{
|
||||
if (!prim || !prop || prop_token.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop->type != IDP_ARRAY) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Property %s is not an array type and can't be converted to a vector attribute",
|
||||
prop->name);
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::SdfValueTypeName type_name;
|
||||
bool success = false;
|
||||
|
||||
if (prop->subtype == IDP_FLOAT) {
|
||||
if (prop->len == 2) {
|
||||
type_name = pxr::SdfValueTypeNames->Float2;
|
||||
success = set_vec_attrib<pxr::GfVec2f>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
else if (prop->len == 3) {
|
||||
type_name = pxr::SdfValueTypeNames->Float3;
|
||||
success = set_vec_attrib<pxr::GfVec3f>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
else if (prop->len == 4) {
|
||||
type_name = pxr::SdfValueTypeNames->Float4;
|
||||
success = set_vec_attrib<pxr::GfVec4f>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
}
|
||||
else if (prop->subtype == IDP_DOUBLE) {
|
||||
if (prop->len == 2) {
|
||||
type_name = pxr::SdfValueTypeNames->Double2;
|
||||
success = set_vec_attrib<pxr::GfVec2d>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
else if (prop->len == 3) {
|
||||
type_name = pxr::SdfValueTypeNames->Double3;
|
||||
success = set_vec_attrib<pxr::GfVec3d>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
else if (prop->len == 4) {
|
||||
type_name = pxr::SdfValueTypeNames->Double4;
|
||||
success = set_vec_attrib<pxr::GfVec4d>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
}
|
||||
else if (prop->subtype == IDP_INT) {
|
||||
if (prop->len == 2) {
|
||||
type_name = pxr::SdfValueTypeNames->Int2;
|
||||
success = set_vec_attrib<pxr::GfVec2i>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
else if (prop->len == 3) {
|
||||
type_name = pxr::SdfValueTypeNames->Int3;
|
||||
success = set_vec_attrib<pxr::GfVec3i>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
else if (prop->len == 4) {
|
||||
type_name = pxr::SdfValueTypeNames->Int4;
|
||||
success = set_vec_attrib<pxr::GfVec4i>(prim, prop, prop_token, type_name, timecode);
|
||||
}
|
||||
}
|
||||
|
||||
if (!type_name) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Couldn't determine USD type name for array property %s",
|
||||
prop_token.GetString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
CLOG_WARN(
|
||||
&LOG, "Couldn't set USD attribute from array property %s", prop_token.GetString().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_context)
|
||||
: usd_export_context_(usd_export_context), frame_has_been_written_(false), is_animated_(false)
|
||||
{
|
||||
|
@ -121,7 +223,14 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(const HierarchyCont
|
|||
}
|
||||
|
||||
std::string active_uv = get_mesh_active_uvlayer_name(context.object);
|
||||
return create_usd_material(usd_export_context_, usd_path, material, active_uv, reports());
|
||||
|
||||
usd_material = create_usd_material(
|
||||
usd_export_context_, usd_path, material, active_uv, reports());
|
||||
|
||||
auto prim = usd_material.GetPrim();
|
||||
write_id_properties(prim, material->id, get_export_time_code());
|
||||
|
||||
return usd_material;
|
||||
}
|
||||
|
||||
void USDAbstractWriter::write_visibility(const HierarchyContext &context,
|
||||
|
@ -165,6 +274,111 @@ bool USDAbstractWriter::mark_as_instance(const HierarchyContext &context, const
|
|||
return true;
|
||||
}
|
||||
|
||||
void USDAbstractWriter::write_id_properties(const pxr::UsdPrim &prim,
|
||||
const ID &id,
|
||||
pxr::UsdTimeCode timecode) const
|
||||
{
|
||||
if (!usd_export_context_.export_params.export_custom_properties) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (usd_export_context_.export_params.author_blender_name) {
|
||||
if (GS(id.name) == ID_OB) {
|
||||
/* Author property of original blender Object name. */
|
||||
prim.CreateAttribute(pxr::TfToken(usdtokens::blender_ns.GetString() + ":object_name"),
|
||||
pxr::SdfValueTypeNames->String,
|
||||
true)
|
||||
.Set<std::string>(std::string(id.name + 2));
|
||||
}
|
||||
else {
|
||||
prim.CreateAttribute(pxr::TfToken(usdtokens::blender_ns.GetString() + ":data_name"),
|
||||
pxr::SdfValueTypeNames->String,
|
||||
true)
|
||||
.Set<std::string>(std::string(id.name + 2));
|
||||
}
|
||||
}
|
||||
|
||||
if (id.properties) {
|
||||
write_user_properties(prim, id.properties, timecode);
|
||||
}
|
||||
}
|
||||
|
||||
void USDAbstractWriter::write_user_properties(const pxr::UsdPrim &prim,
|
||||
IDProperty *properties,
|
||||
pxr::UsdTimeCode timecode) const
|
||||
{
|
||||
if (properties == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (properties->type != IDP_GROUP) {
|
||||
return;
|
||||
}
|
||||
|
||||
const StringRef displayName_identifier = "displayName";
|
||||
|
||||
for (IDProperty *prop = (IDProperty *)properties->data.group.first; prop; prop = prop->next) {
|
||||
if (displayName_identifier == prop->name) {
|
||||
if (prop->type == IDP_STRING && prop->data.pointer) {
|
||||
prim.SetDisplayName(static_cast<char *>(prop->data.pointer));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string prop_name = pxr::TfMakeValidIdentifier(prop->name);
|
||||
std::string full_prop_name = "userProperties:" + prop_name;
|
||||
|
||||
pxr::TfToken prop_token = pxr::TfToken(full_prop_name);
|
||||
|
||||
if (prim.HasAttribute(prop_token)) {
|
||||
/* Don't overwrite existing attributes, as these may have been
|
||||
* created by the exporter logic and shouldn't be changed. */
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (prop->type) {
|
||||
case IDP_INT:
|
||||
if (pxr::UsdAttribute int_attr = prim.CreateAttribute(
|
||||
prop_token, pxr::SdfValueTypeNames->Int, true))
|
||||
{
|
||||
int_attr.Set<int>(prop->data.val, timecode);
|
||||
}
|
||||
break;
|
||||
case IDP_FLOAT:
|
||||
if (pxr::UsdAttribute float_attr = prim.CreateAttribute(
|
||||
prop_token, pxr::SdfValueTypeNames->Float, true))
|
||||
{
|
||||
float_attr.Set<float>(*reinterpret_cast<float *>(&prop->data.val), timecode);
|
||||
}
|
||||
break;
|
||||
case IDP_DOUBLE:
|
||||
if (pxr::UsdAttribute double_attr = prim.CreateAttribute(
|
||||
prop_token, pxr::SdfValueTypeNames->Double, true))
|
||||
{
|
||||
double_attr.Set<double>(*reinterpret_cast<double *>(&prop->data.val), timecode);
|
||||
}
|
||||
break;
|
||||
case IDP_STRING:
|
||||
if (pxr::UsdAttribute str_attr = prim.CreateAttribute(
|
||||
prop_token, pxr::SdfValueTypeNames->String, true))
|
||||
{
|
||||
str_attr.Set<std::string>(static_cast<const char *>(prop->data.pointer), timecode);
|
||||
}
|
||||
break;
|
||||
case IDP_BOOLEAN:
|
||||
if (pxr::UsdAttribute bool_attr = prim.CreateAttribute(
|
||||
prop_token, pxr::SdfValueTypeNames->Bool, true))
|
||||
{
|
||||
bool_attr.Set<bool>(prop->data.val, timecode);
|
||||
}
|
||||
break;
|
||||
case IDP_ARRAY:
|
||||
create_vector_attrib(prim, prop, prop_token, timecode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void USDAbstractWriter::author_extent(const pxr::UsdTimeCode timecode, pxr::UsdGeomBoundable &prim)
|
||||
{
|
||||
/* Do not use any existing `extentsHint` that may be authored, instead recompute the extent when
|
||||
|
|
|
@ -67,6 +67,13 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
|
|||
pxr::SdfPath get_material_library_path() const;
|
||||
pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material);
|
||||
|
||||
void write_id_properties(const pxr::UsdPrim &prim,
|
||||
const ID &id,
|
||||
pxr::UsdTimeCode = pxr::UsdTimeCode::Default()) const;
|
||||
void write_user_properties(const pxr::UsdPrim &prim,
|
||||
IDProperty *properties,
|
||||
pxr::UsdTimeCode = pxr::UsdTimeCode::Default()) const;
|
||||
|
||||
void write_visibility(const HierarchyContext &context,
|
||||
const pxr::UsdTimeCode timecode,
|
||||
pxr::UsdGeomImageable &usd_geometry);
|
||||
|
|
|
@ -97,6 +97,9 @@ void USDCameraWriter::do_write(HierarchyContext &context)
|
|||
float focus_distance = BKE_camera_object_dof_distance(context.object);
|
||||
usd_camera.CreateFocusDistanceAttr().Set(focus_distance, timecode);
|
||||
}
|
||||
|
||||
auto prim = usd_camera.GetPrim();
|
||||
write_id_properties(prim, camera->id, timecode);
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
|
|
@ -505,6 +505,9 @@ void USDCurvesWriter::do_write(HierarchyContext &context)
|
|||
set_writer_attributes(usd_curves, verts, control_point_counts, widths, timecode, interpolation);
|
||||
|
||||
assign_materials(context, usd_curves);
|
||||
|
||||
auto prim = usd_curves.GetPrim();
|
||||
write_id_properties(prim, curves->id, timecode);
|
||||
}
|
||||
|
||||
void USDCurvesWriter::assign_materials(const HierarchyContext &context,
|
||||
|
|
|
@ -62,6 +62,11 @@ void USDHairWriter::do_write(HierarchyContext &context)
|
|||
curves.CreateDisplayColorAttr(pxr::VtValue(colors));
|
||||
}
|
||||
|
||||
if (psys->part) {
|
||||
auto prim = curves.GetPrim();
|
||||
write_id_properties(prim, psys->part->id, timecode);
|
||||
}
|
||||
|
||||
this->author_extent(timecode, curves);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,6 +126,9 @@ void USDLightWriter::do_write(HierarchyContext &context)
|
|||
usd_light_api.CreateSpecularAttr().Set(light->spec_fac, timecode);
|
||||
usd_light_api.CreateNormalizeAttr().Set(true, timecode);
|
||||
|
||||
auto prim = usd_light_api.GetPrim();
|
||||
write_id_properties(prim, light->id, timecode);
|
||||
|
||||
set_light_extents(usd_light_api.GetPrim(), timecode);
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,12 @@ void USDGenericMeshWriter::do_write(HierarchyContext &context)
|
|||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
auto prim = usd_export_context_.stage->GetPrimAtPath(usd_export_context_.usd_path);
|
||||
if (prim.IsValid() && object_eval) {
|
||||
prim.SetActive((object_eval->duplicator_visibility_flag & OB_DUPLI_FLAG_RENDER) != 0);
|
||||
write_id_properties(prim, mesh->id, get_export_time_code());
|
||||
}
|
||||
}
|
||||
|
||||
void USDGenericMeshWriter::write_custom_data(const Object *obj,
|
||||
|
|
|
@ -30,6 +30,11 @@ void USDTransformWriter::do_write(HierarchyContext &context)
|
|||
|
||||
pxr::GfMatrix4d mat_val(parent_relative_matrix);
|
||||
usd_value_writer_.SetAttribute(xformOp_.GetAttr(), mat_val, get_export_time_code());
|
||||
|
||||
if (context.object) {
|
||||
auto prim = xform.GetPrim();
|
||||
write_id_properties(prim, context.object->id, get_export_time_code());
|
||||
}
|
||||
}
|
||||
|
||||
bool USDTransformWriter::check_is_animated(const HierarchyContext &context) const
|
||||
|
|
|
@ -34,6 +34,17 @@ enum eUSDMtlNameCollisionMode {
|
|||
USD_MTL_NAME_COLLISION_REFERENCE_EXISTING = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Behavior for importing of custom
|
||||
* attributes / properties outside
|
||||
* a prim's regular schema.
|
||||
*/
|
||||
typedef enum eUSDAttrImportMode {
|
||||
USD_ATTR_IMPORT_NONE = 0,
|
||||
USD_ATTR_IMPORT_USER = 1,
|
||||
USD_ATTR_IMPORT_ALL = 2,
|
||||
} eUSDAttrImportMode;
|
||||
|
||||
/**
|
||||
* Behavior when importing textures from a package
|
||||
* (e.g., USDZ archive) or from a URI path.
|
||||
|
@ -84,6 +95,8 @@ struct USDExportParams {
|
|||
bool export_textures = true;
|
||||
bool overwrite_textures = true;
|
||||
bool relative_paths = true;
|
||||
bool export_custom_properties = true;
|
||||
bool author_blender_name = true;
|
||||
char root_prim_path[1024] = ""; /* FILE_MAX */
|
||||
char collection[MAX_IDPROP_NAME] = "";
|
||||
|
||||
|
@ -126,6 +139,7 @@ struct USDImportParams {
|
|||
char import_textures_dir[768]; /* FILE_MAXDIR */
|
||||
eUSDTexNameCollisionMode tex_name_collision_mode;
|
||||
bool import_all_materials;
|
||||
eUSDAttrImportMode attr_import_mode;
|
||||
|
||||
/**
|
||||
* Communication structure between the wmJob management code and the worker code. Currently used
|
||||
|
|
Loading…
Reference in New Issue