USD: optionally author subdivision schema on export #113267

Merged
Jesse Yurkovich merged 9 commits from wave/blender_wave_Apple:dev/author_subdivs_v2 into main 2023-12-15 21:11:50 +01:00
12 changed files with 362 additions and 135 deletions

View File

@ -91,6 +91,26 @@ const EnumPropertyItem rna_enum_usd_tex_name_collision_mode_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
const EnumPropertyItem rna_enum_usd_export_subdiv_mode_items[] = {
{USD_SUBDIV_IGNORE,
"IGNORE",
brecht marked this conversation as resolved Outdated

No need for SUBDIV_ prefix in the identifier.

No need for `SUBDIV_` prefix in the identifier.

Thanks, how about this?

const EnumPropertyItem rna_enum_usd_export_subdiv_mode_items[] = {
    {USD_SUBDIV_IGNORE,
     "IGNORE",
     0,
     "Ignore",
     "SubD scheme = None, export base mesh without subdivision"},
    {USD_SUBDIV_TESSELLATE,
     "TESSELATE",
     0,
     "Tessellate",
     "SubD scheme = None, export subdivided mesh"},
    {USD_SUBDIV_BESTMATCH,
     "BESTMATCH",
     0,
     "Best Match",
     "Export base mesh with Catmull-Clark subdivision scheme when possible.  "
     "Reverts to exporting the subdivided mesh for the Simple subdivision type. "},
    {0, nullptr, 0, nullptr, nullptr},
};
Thanks, how about this? ``` const EnumPropertyItem rna_enum_usd_export_subdiv_mode_items[] = { {USD_SUBDIV_IGNORE, "IGNORE", 0, "Ignore", "SubD scheme = None, export base mesh without subdivision"}, {USD_SUBDIV_TESSELLATE, "TESSELATE", 0, "Tessellate", "SubD scheme = None, export subdivided mesh"}, {USD_SUBDIV_BESTMATCH, "BESTMATCH", 0, "Best Match", "Export base mesh with Catmull-Clark subdivision scheme when possible. " "Reverts to exporting the subdivided mesh for the Simple subdivision type. "}, {0, nullptr, 0, nullptr, nullptr}, }; ```

That looks fine. Just change BESTMATCH -> BEST_MATCH.

That looks fine. Just change BESTMATCH -> BEST_MATCH.
0,
"Ignore",
"Subdivision scheme = None, export base mesh without subdivision"},
Matt-McLin marked this conversation as resolved Outdated

"SubD" -> "Subdivision" here and the other enum below since these are user-facing strings.

"SubD" -> "Subdivision" here and the other enum below since these are user-facing strings.

Thanks for catching, I thought I had already changed this

Thanks for catching, I thought I had already changed this
{USD_SUBDIV_TESSELLATE,
"TESSELLATE",
brecht marked this conversation as resolved Outdated

TESSELATE -> TESSELLATE

TESSELATE -> TESSELLATE
0,
"Tessellate",
"Subdivision scheme = None, export subdivided mesh"},
{USD_SUBDIV_BEST_MATCH,
"BEST_MATCH",
0,
"Best Match",
"Subdivision scheme = Catmull-Clark, when possible. "
Matt-McLin marked this conversation as resolved Outdated

To match the format of the text above. How about:

Subdivision scheme = Catmull-Clark, when possible. Reverts to exporting the subdivided mesh for the Simple subdivision type

Also note that, due to a quirk of how these strings get processed. The last sentence should not end with a period(.)

To match the format of the text above. How about: ``` Subdivision scheme = Catmull-Clark, when possible. Reverts to exporting the subdivided mesh for the Simple subdivision type ``` Also note that, due to a quirk of how these strings get processed. The last sentence should _not_ end with a period(`.`)
"Reverts to exporting the subdivided mesh for the Simple subdivision type"},
{0, nullptr, 0, nullptr, nullptr},
};
/* Stored in the wmOperator's customdata field to indicate it should run as a background job.
* This is set when the operator is invoked, and not set when it is only executed. */
enum { AS_BACKGROUND_JOB = 1 };
@ -155,6 +175,8 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
const bool export_mesh_colors = RNA_boolean_get(op->ptr, "export_mesh_colors");
const bool export_normals = RNA_boolean_get(op->ptr, "export_normals");
const bool export_materials = RNA_boolean_get(op->ptr, "export_materials");
const eSubdivExportMode export_subdiv = eSubdivExportMode(
RNA_enum_get(op->ptr, "export_subdivision"));
const bool use_instancing = RNA_boolean_get(op->ptr, "use_instancing");
const bool evaluation_mode = RNA_enum_get(op->ptr, "evaluation_mode");
@ -174,6 +196,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
export_normals,
export_mesh_colors,
export_materials,
export_subdiv,
selected_objects_only,
visible_objects_only,
use_instancing,
@ -211,6 +234,7 @@ static void wm_usd_export_draw(bContext * /*C*/, wmOperator *op)
uiItemR(col, ptr, "export_uvmaps", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "export_normals", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "export_materials", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "export_subdivision", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "root_prim_path", UI_ITEM_NONE, nullptr, ICON_NONE);
col = uiLayoutColumn(box, true);
@ -335,6 +359,14 @@ void WM_OT_usd_export(wmOperatorType *ot)
"Export viewport settings of materials as USD preview materials, and export "
"material assignments as geometry subsets");
RNA_def_enum(ot->srna,
brecht marked this conversation as resolved Outdated

Don't abbreviate: export_subdivision

Don't abbreviate: `export_subdivision`
"export_subdivision",
rna_enum_usd_export_subdiv_mode_items,
USD_SUBDIV_BEST_MATCH,
brecht marked this conversation as resolved Outdated

SubD Scheme -> Subdivision

We don't use the term "SubD" in the Blender interface,.

SubD Scheme -> Subdivision We don't use the term "SubD" in the Blender interface,.
"Subdivision Scheme",
"Choose how subdivision modifiers will be mapped to the USD subdivision scheme "
"during export");
RNA_def_boolean(ot->srna,
"use_instancing",
false,

View File

@ -43,7 +43,6 @@ set(SRC
exporter/abc_custom_props.cc
exporter/abc_export_capi.cc
exporter/abc_hierarchy_iterator.cc
exporter/abc_subdiv_disabler.cc
exporter/abc_writer_abstract.cc
exporter/abc_writer_camera.cc
exporter/abc_writer_curves.cc
@ -71,7 +70,6 @@ set(SRC
exporter/abc_archive.h
exporter/abc_custom_props.h
exporter/abc_hierarchy_iterator.h
exporter/abc_subdiv_disabler.h
exporter/abc_writer_abstract.h
exporter/abc_writer_camera.h
exporter/abc_writer_curves.h

View File

@ -3,9 +3,9 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "ABC_alembic.h"
#include "IO_subdiv_disabler.hh"
#include "abc_archive.h"
#include "abc_hierarchy_iterator.h"
#include "abc_subdiv_disabler.h"
#include "MEM_guardedalloc.h"

View File

@ -1,86 +0,0 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "abc_subdiv_disabler.h"
#include <cstdio>
#include "BLI_listbase.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_layer_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BKE_layer.h"
#include "BKE_modifier.hh"
namespace blender::io::alembic {
SubdivModifierDisabler::SubdivModifierDisabler(Depsgraph *depsgraph) : depsgraph_(depsgraph) {}
SubdivModifierDisabler::~SubdivModifierDisabler()
{
for (ModifierData *modifier : disabled_modifiers_) {
modifier->mode &= ~eModifierMode_DisableTemporary;
}
}
void SubdivModifierDisabler::disable_modifiers()
{
Scene *scene = DEG_get_input_scene(depsgraph_);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph_);
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *object = base->object;
if (object->type != OB_MESH) {
continue;
}
ModifierData *subdiv = get_subdiv_modifier(scene, object);
if (subdiv == nullptr) {
continue;
}
/* This disables more modifiers than necessary, as it doesn't take restrictions like
* "export selected objects only" into account. However, with the subsurfs disabled,
* moving to a different frame is also going to be faster, so in the end this is probably
* a good thing to do. */
subdiv->mode |= eModifierMode_DisableTemporary;
disabled_modifiers_.insert(subdiv);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
}
}
ModifierData *SubdivModifierDisabler::get_subdiv_modifier(Scene *scene, Object *ob)
{
ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
for (; md; md = md->prev) {
if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) {
continue;
}
if (md->type == eModifierType_Subsurf) {
SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>(md);
if (smd->subdivType == ME_CC_SUBSURF) {
return md;
}
}
/* mesh is not a subsurf. break */
if (!ELEM(md->type, eModifierType_Displace, eModifierType_ParticleSystem)) {
return nullptr;
}
}
return nullptr;
}
} // namespace blender::io::alembic

View File

@ -1,40 +0,0 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <set>
struct Depsgraph;
struct ModifierData;
struct Object;
struct Scene;
namespace blender::io::alembic {
/**
* Temporarily all subdivision modifiers on mesh objects.
* The destructor restores all disabled modifiers.
*
* This is used to export unsubdivided meshes to Alembic. It is done in a separate step before the
* exporter starts iterating over all the frames, so that it only has to happen once per export.
*/
class SubdivModifierDisabler final {
private:
Depsgraph *depsgraph_;
std::set<ModifierData *> disabled_modifiers_;
public:
explicit SubdivModifierDisabler(Depsgraph *depsgraph);
~SubdivModifierDisabler();
void disable_modifiers();
/**
* Check if the mesh is a subsurf, ignoring disabled modifiers and
* displace if it's after subsurf.
*/
static ModifierData *get_subdiv_modifier(Scene *scene, Object *ob);
};
} // namespace blender::io::alembic

View File

@ -18,12 +18,14 @@ set(SRC
intern/object_identifier.cc
intern/orientation.cc
intern/path_util.cc
intern/subdiv_disabler.cc
IO_abstract_hierarchy_iterator.h
IO_dupli_persistent_id.hh
IO_orientation.hh
IO_path_util.hh
IO_path_util_types.hh
IO_subdiv_disabler.hh
IO_types.hh
intern/dupli_parent_finder.hh
)

View File

@ -0,0 +1,62 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_modifier_types.h"
#include <set>
struct Depsgraph;
struct ModifierData;
struct Object;
struct Scene;
namespace blender::io {
/**
* This code is shared between the Alembic and USD exporters.
* Temporarily disable the subdiv modifier on mesh objects,
* if the subdiv modifier is last on the modifier stack.
*
* The destructor restores all disabled modifiers.
*
* Currently, this class is used to disable Catmull-Clark subdivision modifiers.
* It is done in a separate step before the exporter starts iterating over all
* the frames, so that it only has to happen once per export.
*/
class SubdivModifierDisabler final {
private:
Depsgraph *depsgraph_;
std::set<ModifierData *> disabled_modifiers_;
std::set<Object *> modified_objects_;
public:
explicit SubdivModifierDisabler(Depsgraph *depsgraph);
~SubdivModifierDisabler();
/**
* Disable subdiv modifiers on all mesh objects.
*/
void disable_modifiers();
/**
* Return the Catmull-Clark subdiv modifier on the mesh, if it's the last modifier
* in the list or if it's the last modifier preceding any particle system modifiers.
* This function ignores Simple subdiv modifiers.
*/
static ModifierData *get_subdiv_modifier(Scene *scene, const Object *ob, ModifierMode mode);
Matt-McLin marked this conversation as resolved Outdated

Let's continue to use subdiv in the api names/comments as much as possible. the subsurf term is legacy. Use get_subdiv_modifier and subdiv in comments rather than subsurf.

Let's continue to use subdiv in the api names/comments as much as possible. the subsurf term is legacy. Use `get_subdiv_modifier` and `subdiv` in comments rather than `subsurf`.
/* Disallow copying. */
SubdivModifierDisabler(const SubdivModifierDisabler &) = delete;
SubdivModifierDisabler &operator=(const SubdivModifierDisabler &) = delete;
private:
/**
* Disable the given modifier and add it to the disabled
* modifiers list.
*/
void disable_modifier(ModifierData *mod);
};
} // namespace blender::io

View File

@ -0,0 +1,118 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "IO_subdiv_disabler.hh"
#include <cstdio>
#include "BLI_listbase.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_layer_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BKE_layer.h"
#include "BKE_modifier.hh"
namespace blender::io {
/* Returns the last subdiv modifier associated with an object,
* if that modifier should be disabled.
* We do not disable the subdiv modifier if other modifiers are
* applied after it, with the sole exception of particle modifiers,
* which are allowed.
* Returns nullptr if there is not any subdiv modifier to disable.
*/
ModifierData *SubdivModifierDisabler::get_subdiv_modifier(Scene *scene,
const Object *ob,
ModifierMode mode)
{
ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
for (; md; md = md->prev) {
/* Ignore disabled modifiers. */
if (!BKE_modifier_is_enabled(scene, md, mode)) {
continue;
}
if (md->type == eModifierType_Subsurf) {
SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>(md);
if (smd->subdivType == ME_CC_SUBSURF) {
/* This is a Catmull-Clark modifier. */
return md;
}
/* Not Catmull-Clark, so ignore it. */
return nullptr;
}
/* If any modifier other than a particle system exists after the
* subdiv modifier, then abort. */
if (md->type != eModifierType_ParticleSystem) {
return nullptr;
}
}
return nullptr;
}
SubdivModifierDisabler::SubdivModifierDisabler(Depsgraph *depsgraph) : depsgraph_(depsgraph) {}
SubdivModifierDisabler::~SubdivModifierDisabler()
{
/* Enable previously disabled modifiers. */
for (ModifierData *modifier : disabled_modifiers_) {
modifier->mode &= ~eModifierMode_DisableTemporary;
}
/* Update object to render with restored modifiers in the viewport. */
for (Object *object : modified_objects_) {
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
}
}
void SubdivModifierDisabler::disable_modifiers()
{
eEvaluationMode eval_mode = DEG_get_mode(depsgraph_);
const ModifierMode mode = eval_mode == DAG_EVAL_VIEWPORT ? eModifierMode_Realtime :
eModifierMode_Render;
Scene *scene = DEG_get_input_scene(depsgraph_);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph_);
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *object = base->object;
if (object->type != OB_MESH) {
continue;
}
/* Check if a subdiv modifier exists, and should be disabled. */
ModifierData *mod = get_subdiv_modifier(scene, object, mode);
if (!mod) {
continue;
}
/* This might disable more modifiers than necessary, as it doesn't take restrictions like
* "export selected objects only" into account. However, with the subdivs disabled,
* moving to a different frame is also going to be faster, so in the end this is probably
* a good thing to do. */
disable_modifier(mod);
modified_objects_.insert(object);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
}
}
void SubdivModifierDisabler::disable_modifier(ModifierData *mod)
{
mod->mode |= eModifierMode_DisableTemporary;
disabled_modifiers_.insert(mod);
}
} // namespace blender::io

View File

@ -4,6 +4,7 @@
#include <iostream>
#include "IO_subdiv_disabler.hh"
#include "usd.h"
#include "usd.hh"
#include "usd_hierarchy_iterator.h"
@ -226,6 +227,16 @@ pxr::UsdStageRefPtr export_to_stage(const USDExportParams &params,
Scene *scene = DEG_get_input_scene(depsgraph);
Main *bmain = DEG_get_bmain(depsgraph);
SubdivModifierDisabler mod_disabler(depsgraph);
/* If we want to set the subdiv scheme, then we need to the export the mesh
* without the subdiv modifier applied. */
if ((params.export_subdiv == USD_SUBDIV_BEST_MATCH) ||
(params.export_subdiv == USD_SUBDIV_IGNORE)) {
mod_disabler.disable_modifiers();
BKE_scene_graph_update_tagged(depsgraph, bmain);
}
/* This whole `export_to_stage` function is assumed to cover about 80% of the whole export
* process, from 0.1f to 0.9f. */
worker_status->progress = 0.10f;

View File

@ -56,6 +56,35 @@ bool USDGenericMeshWriter::is_supported(const HierarchyContext *context) const
return true;
}
/* Get the last subdiv modifier, regardless of enable/disable status */
Matt-McLin marked this conversation as resolved Outdated

Subsurf to subdiv in api names

Subsurf to subdiv in api names
static const SubsurfModifierData *get_last_subdiv_modifier(eEvaluationMode eval_mode, Object *obj)
{
BLI_assert(obj);
/* Return the subdiv modifier if it is the last modifier and has
* the required mode enabled. */
brecht marked this conversation as resolved

This seems inconsistent with SubdivModifierDisabler::get_subsurf_modifier, which skips displacement and particle system modifiers.

Can we unify this code into a single function?

Ideally code would also be unified with Alembic export and moved into io/common

This seems inconsistent with `SubdivModifierDisabler::get_subsurf_modifier`, which skips displacement and particle system modifiers. Can we unify this code into a single function? Ideally code would also be unified with Alembic export and moved into `io/common`
ModifierData *md = (ModifierData *)(obj->modifiers.last);
if (!md) {
return nullptr;
}
/* Determine if the modifier is enabled for the current evaluation mode. */
ModifierMode mod_mode = (eval_mode == DAG_EVAL_RENDER) ? eModifierMode_Render :
eModifierMode_Realtime;
if ((md->mode & mod_mode) != mod_mode) {
return nullptr;
}
if (md->type == eModifierType_Subsurf) {
return reinterpret_cast<SubsurfModifierData *>(md);
}
return nullptr;
}
void USDGenericMeshWriter::do_write(HierarchyContext &context)
{
Object *object_eval = context.object;
@ -67,7 +96,11 @@ void USDGenericMeshWriter::do_write(HierarchyContext &context)
}
try {
write_mesh(context, mesh);
/* Fetch the subdiv modifier, if one exists and it is the last modifier. */
const SubsurfModifierData *subsurfData = get_last_subdiv_modifier(
usd_export_context_.export_params.evaluation_mode, object_eval);
write_mesh(context, mesh, subsurfData);
if (needsfree) {
free_export_mesh(mesh);
@ -376,7 +409,9 @@ struct USDMeshData {
pxr::VtFloatArray corner_sharpnesses;
};
void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
void USDGenericMeshWriter::write_mesh(HierarchyContext &context,
Mesh *mesh,
const SubsurfModifierData *subsurfData)
{
pxr::UsdTimeCode timecode = get_export_time_code();
pxr::UsdStageRefPtr stage = usd_export_context_.stage;
@ -465,18 +500,26 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
}
write_custom_data(mesh, usd_mesh);
write_surface_velocity(mesh, usd_mesh);
if (usd_export_context_.export_params.export_normals) {
const pxr::TfToken subdiv_scheme = get_subdiv_scheme(subsurfData);
/* Normals can be animated, so ensure these are written for each frame,
Matt-McLin marked this conversation as resolved Outdated

I wonder... do we need to keep the subdiv+normals writing before this frame_has_been_written_ condition? There's a few usdview warnings if I attempt an animation while keyframing SubD modifier enable: Invalid Hydra prim '/root/Plane_002/Plane_002': # of facevaryings mismatch (64 != 24) for primvar normals

These go away if subdiv+normals are written before the condition here. All that said, keyframing the enable buttons on viewport/render don't really do what I thought they'd do. Maybe look at that after the initial patch lands. Or maybe my understanding of what this condition protects is wrong.

I wonder... do we need to keep the subdiv+normals writing before this `frame_has_been_written_` condition? There's a few `usdview` warnings if I attempt an animation while keyframing SubD modifier enable: `Invalid Hydra prim '/root/Plane_002/Plane_002': # of facevaryings mismatch (64 != 24) for primvar normals` These go away if subdiv+normals are written before the condition here. All that said, keyframing the enable buttons on viewport/render don't really do what I thought they'd do. Maybe look at that after the initial patch lands. Or maybe my understanding of what this condition protects is wrong.

Hi @deadpin , our assumption here is that the Subdivision scheme will never be animated, as we are not aware of any use cases where this would be desirable. I suggest we take this as a later update if such a use case arises.

Hi @deadpin , our assumption here is that the Subdivision scheme will never be animated, as we are not aware of any use cases where this would be desirable. I suggest we take this as a later update if such a use case arises.
* unless a subdiv modifier is used, in which case normals are computed,
* not stored with the mesh. */
if (usd_export_context_.export_params.export_normals &&
Matt-McLin marked this conversation as resolved Outdated

We should take this large block and factor it out to a write_subdiv_and_normals private method to help keep things organized.

We should take this large block and factor it out to a `write_subdiv_and_normals` private method to help keep things organized.
subdiv_scheme == pxr::UsdGeomTokens->none) {
write_normals(mesh, usd_mesh);
}
write_surface_velocity(mesh, usd_mesh);
/* TODO(Sybren): figure out what happens when the face groups change. */
if (frame_has_been_written_) {
return;
}
usd_mesh.CreateSubdivisionSchemeAttr().Set(pxr::UsdGeomTokens->none);
/* The subdivision scheme is a uniform according to spec,
brecht marked this conversation as resolved Outdated

#113883 recently eliminated all the usage of WM_report in this code, this will need to be changed as well.

#113883 recently eliminated all the usage of `WM_report` in this code, this will need to be changed as well.
* so this value cannot be animated. */
write_subdiv(subdiv_scheme, usd_mesh, subsurfData);
if (usd_export_context_.export_params.export_materials) {
assign_materials(context, usd_mesh, usd_mesh_data.face_groups);
@ -491,6 +534,80 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
}
}
pxr::TfToken USDGenericMeshWriter::get_subdiv_scheme(const SubsurfModifierData *subsurfData)
{
/* Default to setting the subdivision scheme to None. */
pxr::TfToken subdiv_scheme = pxr::UsdGeomTokens->none;
if (subsurfData) {
if (subsurfData->subdivType == SUBSURF_TYPE_CATMULL_CLARK) {
if (usd_export_context_.export_params.export_subdiv == USD_SUBDIV_BEST_MATCH) {
/* If a subdivision modifier exists, and it uses Catmull-Clark, then apply Catmull-Clark
* SubD scheme. */
subdiv_scheme = pxr::UsdGeomTokens->catmullClark;
}
}
else {
/* "Simple" is currently the only other subdivision type provided by Blender, */
/* and we do not yet provide a corresponding representation for USD export. */
BKE_reportf(reports(),
RPT_WARNING,
"USD export: Simple subdivision not supported, exporting subdivided mesh");
}
}
return subdiv_scheme;
}
void USDGenericMeshWriter::write_subdiv(const pxr::TfToken &subdiv_scheme,
pxr::UsdGeomMesh &usd_mesh,
const SubsurfModifierData *subsurfData)
{
usd_mesh.CreateSubdivisionSchemeAttr().Set(subdiv_scheme);
if (subdiv_scheme == pxr::UsdGeomTokens->catmullClark) {
/* For Catmull-Clark, also consider the various interpolation modes. */
/* For reference, see
* https://graphics.pixar.com/opensubdiv/docs/subdivision_surfaces.html#face-varying-interpolation-rules
*/
switch (subsurfData->uv_smooth) {
case SUBSURF_UV_SMOOTH_NONE:
usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->all);
break;
case SUBSURF_UV_SMOOTH_PRESERVE_CORNERS:
usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->cornersOnly);
break;
case SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS:
usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->cornersPlus1);
break;
case SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE:
usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->cornersPlus2);
break;
case SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES:
usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->boundaries);
break;
case SUBSURF_UV_SMOOTH_ALL:
usd_mesh.CreateFaceVaryingLinearInterpolationAttr().Set(pxr::UsdGeomTokens->none);
break;
default:
BLI_assert_msg(0, "Unsupported UV smoothing mode.");
}
/* For reference, see
* https://graphics.pixar.com/opensubdiv/docs/subdivision_surfaces.html#boundary-interpolation-rules
*/
switch (subsurfData->boundary_smooth) {
case SUBSURF_BOUNDARY_SMOOTH_ALL:
usd_mesh.CreateInterpolateBoundaryAttr().Set(pxr::UsdGeomTokens->edgeOnly);
break;
case SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS:
usd_mesh.CreateInterpolateBoundaryAttr().Set(pxr::UsdGeomTokens->edgeAndCorner);
break;
default:
BLI_assert_msg(0, "Unsupported boundary smoothing mode.");
}
}
}
static void get_positions(const Mesh *mesh, USDMeshData &usd_mesh_data)
{
const Span<pxr::GfVec3f> positions = mesh->vert_positions().cast<pxr::GfVec3f>();

View File

@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_modifier_types.h"
#include "usd_writer_abstract.h"
#include "BLI_map.hh"
@ -31,7 +32,11 @@ class USDGenericMeshWriter : public USDAbstractWriter {
/* 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);
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,

View File

@ -40,6 +40,13 @@ typedef enum eUSDTexNameCollisionMode {
USD_TEX_NAME_COLLISION_OVERWRITE = 1,
} eUSDTexNameCollisionMode;
typedef enum eSubdivExportMode {
USD_SUBDIV_IGNORE = 0, /* Subdivision scheme = None, export base mesh without subdivision. */
USD_SUBDIV_TESSELLATE = 1, /* Subdivision scheme = None, export subdivided mesh. */
USD_SUBDIV_BEST_MATCH = 2, /* Apply the USD subdivision scheme that is the closest match to Blender. */
/* Reverts to USD_SUBDIV_TESSELLATE if the subdivision method is not supported. */
} eSubdivExportMode;
struct USDExportParams {
bool export_animation = false;
bool export_hair = true;
@ -47,6 +54,7 @@ struct USDExportParams {
bool export_normals = true;
bool export_mesh_colors = true;
bool export_materials = true;
eSubdivExportMode export_subdiv = USD_SUBDIV_BEST_MATCH;
bool selected_objects_only = false;
bool visible_objects_only = true;
bool use_instancing = false;