12#include <pxr/usd/usdGeom/mesh.h>
13#include <pxr/usd/usdGeom/primvarsAPI.h>
14#include <pxr/usd/usdShade/material.h>
15#include <pxr/usd/usdShade/materialBindingAPI.h>
16#include <pxr/usd/usdSkel/bindingAPI.h>
43static const pxr::TfToken
Anim(
"Anim", pxr::TfToken::Immortal);
78 if ((md->
mode & mod_mode) != mod_mode) {
91 Object *object_eval = context.object;
92 bool needsfree =
false;
95 if (mesh ==
nullptr) {
100 const bool tag_only =
false;
107 bmesh_from_mesh_params.calc_vert_normal =
true;
118 mesh = triangulated_mesh;
127 write_mesh(context, mesh, subsurfData);
130 if (prim.IsValid() && object_eval) {
147void USDGenericMeshWriter::write_custom_data(
const Object *obj,
149 const pxr::UsdGeomMesh &usd_mesh)
161 ELEM(iter.
name,
"position",
"material_index",
"velocity",
"crease_vert"))
189 if (usd_export_context_.export_params.export_uvmaps) {
190 this->write_uv_data(usd_mesh, iter, active_uvmap_name);
195 this->write_generic_data(mesh, usd_mesh, iter);
203 switch (blender_domain) {
205 return pxr::UsdGeomTokens->faceVarying;
207 return pxr::UsdGeomTokens->vertex;
209 return pxr::UsdGeomTokens->uniform;
217void USDGenericMeshWriter::write_generic_data(
const Mesh *mesh,
218 const pxr::UsdGeomMesh &usd_mesh,
221 const pxr::TfToken pv_name(
222 make_safe_name(attr.
name, usd_export_context_.export_params.allow_unicode));
224 const std::optional<pxr::TfToken> pv_interp = convert_blender_domain_to_usd(attr.
domain);
225 const std::optional<pxr::SdfValueTypeName> pv_type = convert_blender_type_to_usd(
228 if (!pv_interp || !pv_type) {
231 "Mesh '%s', Attribute '%s' (domain %d, type %d) cannot be converted to USD",
240 if (attribute.is_empty()) {
244 const pxr::UsdTimeCode timecode = get_export_time_code();
245 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_mesh);
247 pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type, *pv_interp);
249 copy_blender_attribute_to_primvar(
250 attribute, attr.
data_type, timecode, pv_attr, usd_value_writer_);
253void USDGenericMeshWriter::write_uv_data(
const pxr::UsdGeomMesh &usd_mesh,
254 const bke::AttributeIter &attr,
255 const StringRef active_uvmap_name)
258 if (buffer.is_empty()) {
264 const StringRef name = usd_export_context_.export_params.rename_uvmaps &&
265 active_uvmap_name == attr.name ?
269 const pxr::UsdTimeCode timecode = get_export_time_code();
270 const pxr::TfToken pv_name(
271 make_safe_name(name, usd_export_context_.export_params.allow_unicode));
272 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_mesh);
274 pxr::UsdGeomPrimvar pv_uv = pv_api.CreatePrimvar(
275 pv_name, pxr::SdfValueTypeNames->TexCoord2fArray, pxr::UsdGeomTokens->faceVarying);
277 copy_blender_buffer_to_primvar<float2, pxr::GfVec2f>(buffer, timecode, pv_uv, usd_value_writer_);
280void USDGenericMeshWriter::free_export_mesh(
Mesh *mesh)
318 pxr::UsdTimeCode timecode = get_export_time_code();
319 pxr::UsdStageRefPtr
stage = usd_export_context_.stage;
320 const pxr::SdfPath &usd_path = usd_export_context_.usd_path;
322 pxr::UsdGeomMesh usd_mesh = pxr::UsdGeomMesh::Define(
stage, usd_path);
323 write_visibility(context, timecode, usd_mesh);
328 get_geometry_data(mesh, usd_mesh_data);
330 if (usd_export_context_.export_params.use_instancing && context.is_instance()) {
331 if (!mark_as_instance(context, usd_mesh.GetPrim())) {
339 if (usd_export_context_.export_params.export_materials) {
340 assign_materials(context, usd_mesh, usd_mesh_data.
face_groups);
346 pxr::UsdAttribute attr_points = usd_mesh.CreatePointsAttr(pxr::VtValue(),
true);
347 pxr::UsdAttribute attr_face_vertex_counts = usd_mesh.CreateFaceVertexCountsAttr(pxr::VtValue(),
349 pxr::UsdAttribute attr_face_vertex_indices = usd_mesh.CreateFaceVertexIndicesAttr(pxr::VtValue(),
352 if (!attr_points.HasValue()) {
355 attr_points.Set(usd_mesh_data.
points, pxr::UsdTimeCode::Default());
356 attr_face_vertex_counts.Set(usd_mesh_data.
face_vertex_counts, pxr::UsdTimeCode::Default());
357 attr_face_vertex_indices.Set(usd_mesh_data.
face_indices, pxr::UsdTimeCode::Default());
360 usd_value_writer_.SetAttribute(attr_points, pxr::VtValue(usd_mesh_data.
points), timecode);
361 usd_value_writer_.SetAttribute(
363 usd_value_writer_.SetAttribute(
364 attr_face_vertex_indices, pxr::VtValue(usd_mesh_data.
face_indices), timecode);
367 pxr::UsdAttribute attr_crease_lengths = usd_mesh.CreateCreaseLengthsAttr(pxr::VtValue(),
true);
368 pxr::UsdAttribute attr_crease_indices = usd_mesh.CreateCreaseIndicesAttr(pxr::VtValue(),
true);
369 pxr::UsdAttribute attr_crease_sharpness = usd_mesh.CreateCreaseSharpnessesAttr(pxr::VtValue(),
372 if (!attr_crease_lengths.HasValue()) {
373 attr_crease_lengths.Set(usd_mesh_data.
crease_lengths, pxr::UsdTimeCode::Default());
375 attr_crease_sharpness.Set(usd_mesh_data.
crease_sharpnesses, pxr::UsdTimeCode::Default());
378 usd_value_writer_.SetAttribute(
379 attr_crease_lengths, pxr::VtValue(usd_mesh_data.
crease_lengths), timecode);
380 usd_value_writer_.SetAttribute(
382 usd_value_writer_.SetAttribute(
389 pxr::UsdAttribute attr_corner_indices = usd_mesh.CreateCornerIndicesAttr(pxr::VtValue(),
true);
390 pxr::UsdAttribute attr_corner_sharpnesses = usd_mesh.CreateCornerSharpnessesAttr(
391 pxr::VtValue(),
true);
393 if (!attr_corner_indices.HasValue()) {
394 attr_corner_indices.Set(usd_mesh_data.
corner_indices, pxr::UsdTimeCode::Default());
395 attr_corner_sharpnesses.Set(usd_mesh_data.
corner_sharpnesses, pxr::UsdTimeCode::Default());
398 usd_value_writer_.SetAttribute(
399 attr_corner_indices, pxr::VtValue(usd_mesh_data.
corner_indices), timecode);
400 usd_value_writer_.SetAttribute(
405 write_surface_velocity(mesh, usd_mesh);
407 const pxr::TfToken subdiv_scheme = get_subdiv_scheme(subsurfData);
412 if (usd_export_context_.export_params.export_normals &&
413 subdiv_scheme == pxr::UsdGeomTokens->none)
415 write_normals(mesh, usd_mesh);
419 if (frame_has_been_written_) {
425 write_subdiv(subdiv_scheme, usd_mesh, subsurfData);
427 if (usd_export_context_.export_params.export_materials) {
428 assign_materials(context, usd_mesh, usd_mesh_data.
face_groups);
432 if (
const std::optional<Bounds<float3>> bounds = mesh->bounds_min_max()) {
433 pxr::VtArray<pxr::GfVec3f> extent{
434 pxr::GfVec3f{bounds->min[0], bounds->min[1], bounds->min[2]},
435 pxr::GfVec3f{bounds->max[0], bounds->max[1], bounds->max[2]}};
436 usd_mesh.CreateExtentAttr().Set(extent);
440pxr::TfToken USDGenericMeshWriter::get_subdiv_scheme(
const SubsurfModifierData *subsurfData)
443 pxr::TfToken subdiv_scheme = pxr::UsdGeomTokens->none;
447 if (usd_export_context_.export_params.export_subdiv == USD_SUBDIV_BEST_MATCH) {
450 subdiv_scheme = pxr::UsdGeomTokens->catmullClark;
458 "USD export: Simple subdivision not supported, exporting subdivided mesh");
462 return subdiv_scheme;
465void USDGenericMeshWriter::write_subdiv(
const pxr::TfToken &subdiv_scheme,
466 const pxr::UsdGeomMesh &usd_mesh,
469 usd_mesh.CreateSubdivisionSchemeAttr().Set(subdiv_scheme);
470 if (subdiv_scheme == pxr::UsdGeomTokens->catmullClark) {
477 usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->all);
480 usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->cornersOnly);
483 usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->cornersPlus1);
486 usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->cornersPlus2);
489 usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->boundaries);
492 usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->none);
503 usd_mesh.CreateInterpolateBoundaryAttr().Set(pxr::UsdGeomTokens->edgeOnly);
506 usd_mesh.CreateInterpolateBoundaryAttr().Set(pxr::UsdGeomTokens->edgeAndCorner);
517 usd_mesh_data.
points = pxr::VtArray<pxr::GfVec3f>(positions.begin(), positions.end());
525 const VArray<int> material_indices = *attributes.lookup_or_default<
int>(
527 if (!material_indices.
is_single() && mesh->totcol > 1) {
541 const Span<int> corner_verts = mesh->corner_verts();
555 for (
const int i : edges.index_range()) {
556 const float crease = creases[i];
557 if (crease == 0.0f) {
561 const float sharpness = crease >= 1.0f ? pxr::UsdGeomMesh::SHARPNESS_INFINITE : crease;
580 const float crease = creases[i];
583 const float sharpness = crease >= 1.0f ? pxr::UsdGeomMesh::SHARPNESS_INFINITE : crease;
590void USDGenericMeshWriter::get_geometry_data(
const Mesh *mesh, USDMeshData &usd_mesh_data)
592 get_positions(mesh, usd_mesh_data);
593 get_loops_polys(mesh, usd_mesh_data);
594 get_edge_creases(mesh, usd_mesh_data);
595 get_vert_creases(mesh, usd_mesh_data);
598void USDGenericMeshWriter::assign_materials(
const HierarchyContext &context,
599 const pxr::UsdGeomMesh &usd_mesh,
600 const MaterialFaceGroups &usd_face_groups)
602 if (context.object->totcol == 0) {
609 bool mesh_material_bound =
false;
610 auto mesh_prim = usd_mesh.GetPrim();
611 pxr::UsdShadeMaterialBindingAPI material_binding_api(mesh_prim);
612 for (
int mat_num = 0; mat_num < context.object->totcol; mat_num++) {
614 if (material ==
nullptr) {
618 pxr::UsdShadeMaterial usd_material = ensure_usd_material(context, material);
619 material_binding_api.Bind(usd_material);
623 usd_mesh.CreateDoubleSidedAttr(
626 mesh_material_bound =
true;
630 if (mesh_material_bound) {
634 pxr::UsdShadeMaterialBindingAPI::Apply(mesh_prim);
638 usd_mesh.CreateDoubleSidedAttr(pxr::VtValue(
true));
641 if (!mesh_material_bound || usd_face_groups.size() < 2) {
649 for (
const MaterialFaceGroups::Item &face_group : usd_face_groups.items()) {
650 short material_number = face_group.key;
651 const pxr::VtIntArray &face_indices = face_group.value;
654 if (material ==
nullptr) {
658 pxr::UsdShadeMaterial usd_material = ensure_usd_material(context, material);
659 pxr::TfToken material_name = usd_material.GetPath().GetNameToken();
661 pxr::UsdGeomSubset usd_face_subset = material_binding_api.CreateMaterialBindSubset(
662 material_name, face_indices);
663 auto subset_prim = usd_face_subset.GetPrim();
664 auto subset_material_api = pxr::UsdShadeMaterialBindingAPI(subset_prim);
665 subset_material_api.Bind(usd_material);
667 subset_material_api.Apply(subset_prim);
671void USDGenericMeshWriter::write_normals(
const Mesh *mesh, pxr::UsdGeomMesh &usd_mesh)
673 pxr::UsdTimeCode timecode = get_export_time_code();
675 pxr::VtVec3fArray loop_normals;
676 loop_normals.resize(mesh->corners_num);
678 MutableSpan dst_normals(
reinterpret_cast<float3 *
>(loop_normals.data()), loop_normals.size());
680 switch (mesh->normals_domain()) {
686 const OffsetIndices faces = mesh->faces();
687 const Span<float3> face_normals = mesh->face_normals();
688 for (
const int i : faces.index_range()) {
689 dst_normals.slice(faces[i]).fill(face_normals[i]);
699 pxr::UsdAttribute attr_normals = usd_mesh.CreateNormalsAttr(pxr::VtValue(),
true);
700 if (!attr_normals.HasValue()) {
701 attr_normals.Set(loop_normals, pxr::UsdTimeCode::Default());
703 usd_value_writer_.SetAttribute(attr_normals, pxr::VtValue(loop_normals), timecode);
704 usd_mesh.SetNormalsInterpolation(pxr::UsdGeomTokens->faceVarying);
707void USDGenericMeshWriter::write_surface_velocity(
const Mesh *mesh,
708 const pxr::UsdGeomMesh &usd_mesh)
712 const VArraySpan velocity = *mesh->attributes().lookup<
float3>(
"velocity",
714 if (velocity.is_empty()) {
719 Span<pxr::GfVec3f> data = velocity.cast<pxr::GfVec3f>();
720 pxr::VtVec3fArray usd_velocities;
721 usd_velocities.assign(data.begin(), data.end());
723 pxr::UsdTimeCode timecode = get_export_time_code();
724 pxr::UsdAttribute attr_vel = usd_mesh.CreateVelocitiesAttr(pxr::VtValue(),
true);
725 if (!attr_vel.HasValue()) {
726 attr_vel.Set(usd_velocities, pxr::UsdTimeCode::Default());
729 usd_value_writer_.SetAttribute(attr_vel, usd_velocities, timecode);
739 write_skinned_mesh_ =
false;
740 write_blend_shapes_ =
false;
746 write_skinned_mesh_ =
params.export_armatures &&
759 if (!mesh_prim.IsValid()) {
761 "%s: couldn't get valid mesh prim for mesh %s",
767 pxr::UsdSkelBindingAPI skel_api = pxr::UsdSkelBindingAPI::Apply(mesh_prim);
771 "Couldn't apply UsdSkelBindingAPI to mesh prim %s",
781 "Couldn't get armature modifier object for skinned mesh %s",
792 "No armature bones for skinned mesh %s",
797 bool needsfree =
false;
800 if (mesh ==
nullptr) {
825 if (!mesh_prim.IsValid()) {
827 "Couldn't get valid mesh prim for mesh %s",
828 mesh_prim.GetPath().GetAsString().c_str());
846 if (write_blend_shapes_) {
854 if (write_skinned_mesh_) {
858 if (write_blend_shapes_) {
866 if (write_blend_shapes_) {
873 if (write_skinned_mesh_) {
901 if (!mesh_prim.IsValid()) {
903 "Couldn't get valid mesh prim for mesh %s",
913 pxr::UsdAttribute temp_weights_attr = pxr::UsdGeomPrimvarsAPI(mesh_prim).CreatePrimvar(
916 if (!temp_weights_attr) {
918 "Couldn't create primvar %s on prim %s",
920 mesh_prim.GetPath().GetAsString().c_str());
924 temp_weights_attr.Set(weights, timecode);
CustomData interface, see also DNA_customdata_types.h.
const char * CustomData_get_render_layer_name(const CustomData *data, eCustomDataType type)
void BKE_id_free(Main *bmain, void *idv)
const char * BKE_id_name(const ID &id)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
Mesh * BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings)
BMesh * BKE_mesh_to_bmesh_ex(const Mesh *mesh, const BMeshCreateParams *create_params, const BMeshFromMeshParams *convert_params)
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
Mesh * BKE_object_get_pre_modified_mesh(const Object *object)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_assert_msg(a, msg)
#define CLOG_WARN(clg_ref,...)
@ SUBSURF_TYPE_CATMULL_CLARK
@ SUBSURF_BOUNDARY_SMOOTH_ALL
@ SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS
@ SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS
@ SUBSURF_UV_SMOOTH_PRESERVE_CORNERS
@ SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES
@ SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_triangulate(BMesh *bm, const int quad_method, const int ngon_method, const int min_vertices, const bool tag_only, BMOperator *op, BMOpSlot *slot_facemap_out, BMOpSlot *slot_facemap_double_out)
Value & lookup_or_add_default(const Key &key)
Span< NewT > constexpr cast() const
constexpr const T * end() const
constexpr IndexRange index_range() const
constexpr const T * begin() const
constexpr int64_t rfind(char c, int64_t pos=INT64_MAX) const
constexpr const char * c_str() const
eCustomDataType data_type
GAttributeReader get() const
bool frame_has_been_written_
pxr::UsdTimeCode get_export_time_code() const
void write_id_properties(const pxr::UsdPrim &prim, const ID &id, pxr::UsdTimeCode=pxr::UsdTimeCode::Default()) const
const USDExporterContext usd_export_context_
USDGenericMeshWriter(const USDExporterContext &ctx)
virtual void do_write(HierarchyContext &context) override
virtual bool is_supported(const HierarchyContext *context) const override
virtual Mesh * get_export_mesh(Object *object_eval, bool &r_needsfree)=0
virtual void free_export_mesh(Mesh *mesh)
virtual void do_write(HierarchyContext &context) override
void add_shape_key_weights_sample(const Object *obj)
void init_skinned_mesh(const HierarchyContext &context)
void init_blend_shapes(const HierarchyContext &context)
virtual Mesh * get_export_mesh(Object *object_eval, bool &r_needsfree) override
void set_skel_export_flags(const HierarchyContext &context)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
bool attribute_name_is_anonymous(const StringRef name)
void write_custom_data(const OCompoundProperty &prop, CDStreamConfig &config, CustomData *data, int data_type)
static void get_loops_polys(const Mesh *mesh, USDMeshData &usd_mesh_data)
static const SubsurfModifierData * get_last_subdiv_modifier(eEvaluationMode eval_mode, Object *obj)
static void get_vert_creases(const Mesh *mesh, USDMeshData &usd_mesh_data)
static void get_positions(const Mesh *mesh, USDMeshData &usd_mesh_data)
std::string make_safe_name(const std::string &name, bool allow_unicode)
bool is_armature_modifier_bone_name(const Object &obj, const StringRefNull name, const Depsgraph *depsgraph)
void create_blend_shapes(pxr::UsdStageRefPtr stage, const Object *obj, const pxr::UsdPrim &mesh_prim, bool allow_unicode)
void export_deform_verts(const Mesh *mesh, const pxr::UsdSkelBindingAPI &skel_api, const Span< std::string > bone_names)
const Key * get_mesh_shape_key(const Object *obj)
static void get_edge_creases(const Mesh *mesh, USDMeshData &usd_mesh_data)
static std::optional< pxr::TfToken > convert_blender_domain_to_usd(const bke::AttrDomain blender_domain)
pxr::TfToken TempBlendShapeWeightsPrimvarName
void get_armature_bone_names(const Object *ob_arm, const bool use_deform, Vector< std::string > &r_names)
const Object * get_armature_modifier_obj(const Object &obj, const Depsgraph *depsgraph)
pxr::VtFloatArray get_blendshape_weights(const Key *key)
bool can_export_skinned_mesh(const Object &obj, const Depsgraph *depsgraph)
Mesh * get_shape_key_basis_mesh(Object *obj)
bool is_mesh_with_shape_keys(const Object *obj)
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal)
static const pxr::TfToken Anim("Anim", pxr::TfToken::Immortal)
char duplicator_visibility_flag
enum eEvaluationMode evaluation_mode
bool visible_objects_only
const USDExportParams & export_params
const pxr::SdfPath usd_path
const pxr::UsdStageRefPtr stage
pxr::VtIntArray face_vertex_counts
pxr::VtIntArray crease_vertex_indices
Map< short, pxr::VtIntArray > face_groups
pxr::VtIntArray face_indices
pxr::VtIntArray crease_lengths
pxr::VtFloatArray corner_sharpnesses
pxr::VtFloatArray crease_sharpnesses
pxr::VtIntArray corner_indices
pxr::VtArray< pxr::GfVec3f > points