USD: optionally author subdivision schema on export #113267
@ -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
|
||||
0,
|
||||
"Ignore",
|
||||
"Subdivision scheme = None, export base mesh without subdivision"},
|
||||
Matt-McLin marked this conversation as resolved
Outdated
Jesse Yurkovich
commented
"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.
Matt McLin
commented
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
Brecht Van Lommel
commented
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
Jesse Yurkovich
commented
To match the format of the text above. How about:
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
Brecht Van Lommel
commented
Don't abbreviate: 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
Brecht Van Lommel
commented
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,
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
)
|
||||
|
62
source/blender/io/common/IO_subdiv_disabler.hh
Normal 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
Jesse Yurkovich
commented
Let's continue to use subdiv in the api names/comments as much as possible. the subsurf term is legacy. Use 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
|
118
source/blender/io/common/intern/subdiv_disabler.cc
Normal 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
|
@ -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 ¶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;
|
||||
|
@ -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
Jesse Yurkovich
commented
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
Brecht Van Lommel
commented
This seems inconsistent with Can we unify this code into a single function? Ideally code would also be unified with Alembic export and moved into 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
Jesse Yurkovich
commented
I wonder... do we need to keep the subdiv+normals writing before this 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.
Matt McLin
commented
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
Jesse Yurkovich
commented
We should take this large block and factor it out to a 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
Brecht Van Lommel
commented
#113883 recently eliminated all the usage of #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>();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
No need for
SUBDIV_
prefix in the identifier.Thanks, how about this?
That looks fine. Just change BESTMATCH -> BEST_MATCH.