Files
goo-engine/source/blender/io/usd/intern/usd_writer_mesh.h
T
Michael Kowalski c6d61e8586 USD: Support armature and shape key export
New functionality to export armatures and shape keys as USD
skeletons and blend shapes.

Added "Armature", "Only Deform Bones" and "Shape Key" USD export options.

Added USDArmatureWriter class.

Extended USDMeshWriter to write skinned meshes for binding
with skeletons and 'neutral' meshes with blend shape targets.
Specifically, when exporting an armature, a skinned mesh is written
in its pre-modified rest position.  When exporting to blend shapes,
a mesh with shape keys is saved with its vertices in the shape key
basis shape position.

Added USDHierarchyIterator::process_usd_skel() function to
finish processing skeleton and blend shape export after the
writer instances completed writing.  This is necessary because
some of the export operations require processing multiple prims
at once.

Extended USDTransformWriter::do_write() to write transforms
sparsely, to avoid saving redundant transform values when exporting
armatures.

Added a create_skel_roots() function, called on the stage at the
end of the export.  This function attempts to ensure that skinned
prims and skeletons are encapsulated under SkelRoot primitives,
which is required in USD for correct skinning behavior.

When exporting blend shape animations for multiple meshes bound
to a single skeleton, we need to merge the blend shape time samples
of the different meshes into a single animation primitive at the end
of the export.  This requires some tricky book keeping, where the weight
time samples for a given mesh are initially saved by the mesh writer to a
temporary attribute on the mesh and are later copied to the animation
primitive as one of the final steps.

When writing blend shapes and skinned meshes, the pre-modified mesh
is exported.  This is to ensure that the number of blend shape offsets
matches the number of points, and so that the skinned mesh is saved in
its rest position.

Because the pre-modified mesh must be exported, modifiers in addition
to Armature modifiers will not be applied.  This still allows the round trip
UsdSkel -> Blender -> UsdSkel, but some additional setup might be
required to export to UsdSkel when there are multiple modifiers (for
example, applying mirroring modifiers that precede the armature
modifier).

Exporting bendy bones or absolute shape keys isn't currently
supported.

Co-authored-by: Charles Wardlaw <charleswardlaw@noreply.localhost>
Pull Request: https://projects.blender.org/blender/blender/pulls/111931
2024-01-02 15:51:39 +01:00

97 lines
3.4 KiB
C++

/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_modifier_types.h"
#include "usd_writer_abstract.h"
#include "BLI_map.hh"
#include <pxr/usd/usdGeom/mesh.h>
struct Key;
namespace blender::bke {
class AttributeIDRef;
struct AttributeMetaData;
} // namespace blender::bke
namespace blender::io::usd {
struct USDMeshData;
/* Writer for USD geometry. Does not assume the object is a mesh object. */
class USDGenericMeshWriter : public USDAbstractWriter {
public:
USDGenericMeshWriter(const USDExporterContext &ctx);
protected:
virtual bool is_supported(const HierarchyContext *context) const override;
virtual void do_write(HierarchyContext &context) override;
virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) = 0;
virtual void free_export_mesh(Mesh *mesh);
private:
/* Mapping from material slot number to array of face indices with that material. */
using MaterialFaceGroups = Map<short, pxr::VtIntArray>;
void write_mesh(HierarchyContext &context, Mesh *mesh, const SubsurfModifierData *subsurfData);
pxr::TfToken get_subdiv_scheme(const SubsurfModifierData *subsurfData);
void write_subdiv(const pxr::TfToken &subdiv_scheme,
pxr::UsdGeomMesh &usd_mesh,
const SubsurfModifierData *subsurfData);
void get_geometry_data(const Mesh *mesh, struct USDMeshData &usd_mesh_data);
void assign_materials(const HierarchyContext &context,
pxr::UsdGeomMesh usd_mesh,
const MaterialFaceGroups &usd_face_groups);
void write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh);
void write_surface_velocity(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh);
void write_custom_data(const Object *obj, const Mesh *mesh, pxr::UsdGeomMesh usd_mesh);
void write_generic_data(const Mesh *mesh,
pxr::UsdGeomMesh usd_mesh,
const bke::AttributeIDRef &attribute_id,
const bke::AttributeMetaData &meta_data);
void write_uv_data(const Mesh *mesh,
pxr::UsdGeomMesh usd_mesh,
const bke::AttributeIDRef &attribute_id,
const char *active_set_name);
void write_color_data(const Mesh *mesh,
pxr::UsdGeomMesh usd_mesh,
const bke::AttributeIDRef &attribute_id,
const bke::AttributeMetaData &meta_data);
template<typename BlenderT, typename USDT>
void copy_blender_buffer_to_prim(const Span<BlenderT> buffer,
const pxr::UsdTimeCode timecode,
pxr::UsdGeomPrimvar attribute_pv);
};
class USDMeshWriter : public USDGenericMeshWriter {
bool write_skinned_mesh_;
bool write_blend_shapes_;
public:
USDMeshWriter(const USDExporterContext &ctx);
protected:
virtual void do_write(HierarchyContext &context) override;
virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
/**
* Determine whether we should write skinned mesh or blend shape data
* based on the export parameters and the modifiers enabled on the object.
*/
void set_skel_export_flags(const HierarchyContext &context);
void init_skinned_mesh(const HierarchyContext &context);
void init_blend_shapes(const HierarchyContext &context);
void add_shape_key_weights_sample(const Object *obj);
};
} // namespace blender::io::usd