diff --git a/source/blender/editors/io/io_usd.cc b/source/blender/editors/io/io_usd.cc index a7a2f58a1b4..221dc3b3185 100644 --- a/source/blender/editors/io/io_usd.cc +++ b/source/blender/editors/io/io_usd.cc @@ -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", + 0, + "Ignore", + "Subdivision scheme = None, export base mesh without subdivision"}, + {USD_SUBDIV_TESSELLATE, + "TESSELLATE", + 0, + "Tessellate", + "Subdivision scheme = None, export subdivided mesh"}, + {USD_SUBDIV_BEST_MATCH, + "BEST_MATCH", + 0, + "Best Match", + "Subdivision scheme = Catmull-Clark, when possible. " + "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, + "export_subdivision", + rna_enum_usd_export_subdiv_mode_items, + USD_SUBDIV_BEST_MATCH, + "Subdivision Scheme", + "Choose how subdivision modifiers will be mapped to the USD subdivision scheme " + "during export"); + RNA_def_boolean(ot->srna, "use_instancing", false, diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt index 9287fc546c9..3f7822dcb77 100644 --- a/source/blender/io/alembic/CMakeLists.txt +++ b/source/blender/io/alembic/CMakeLists.txt @@ -44,7 +44,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 @@ -72,7 +71,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 diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index 2922a606c05..f395cf86ee6 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -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" diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc deleted file mode 100644 index e74f1f26067..00000000000 --- a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc +++ /dev/null @@ -1,86 +0,0 @@ -/* SPDX-FileCopyrightText: 2020 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "abc_subdiv_disabler.h" - -#include - -#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(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(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 diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.h b/source/blender/io/alembic/exporter/abc_subdiv_disabler.h deleted file mode 100644 index cd568516102..00000000000 --- a/source/blender/io/alembic/exporter/abc_subdiv_disabler.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-FileCopyrightText: 2020 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ -#pragma once - -#include - -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 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 diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index 03365aa3c7e..10269e17d2e 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -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 ) diff --git a/source/blender/io/common/IO_subdiv_disabler.hh b/source/blender/io/common/IO_subdiv_disabler.hh new file mode 100644 index 00000000000..59ed65283dc --- /dev/null +++ b/source/blender/io/common/IO_subdiv_disabler.hh @@ -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 + +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 disabled_modifiers_; + std::set 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); + + /* 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 diff --git a/source/blender/io/common/intern/subdiv_disabler.cc b/source/blender/io/common/intern/subdiv_disabler.cc new file mode 100644 index 00000000000..4281185db44 --- /dev/null +++ b/source/blender/io/common/intern/subdiv_disabler.cc @@ -0,0 +1,118 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "IO_subdiv_disabler.hh" + +#include + +#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(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(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 diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc index 26d02fb162c..6fae3909cf2 100644 --- a/source/blender/io/usd/intern/usd_capi_export.cc +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -4,6 +4,7 @@ #include +#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 ¶ms, 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; diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index b1a04c48228..8acf6f15db0 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -56,6 +56,35 @@ bool USDGenericMeshWriter::is_supported(const HierarchyContext *context) const return true; } +/* Get the last subdiv modifier, regardless of enable/disable status */ +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. */ + + 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(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, + * 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 && + 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, + * 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 positions = mesh->vert_positions().cast(); diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h index e3c9a362242..e4f401f7532 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.h +++ b/source/blender/io/usd/intern/usd_writer_mesh.h @@ -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; - 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, diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 3b40d86cfb7..bb0521d5112 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -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;