Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Handle polygon fields in mesh subset operations (#1068)
Summary:

reduceMeshInternal (used by reduceMeshByFaces, reduceMeshByVertices, and
reduceMeshComponents) didn't handle the polyFaces/polyFaceSizes/polyTexcoordFaces
fields. After a mesh subset operation, the polygon data would be stale.

Add remapPolyFaces helper to filter and remap polygon data during mesh reduction,
using the same active-vertex logic as triangulated faces. Also add verticesToPolys,
polysToVertices conversion functions and reduceMeshByPolys for selecting mesh
subsets by polygon.

Reviewed By: fbogo

Differential Revision: D94579400
  • Loading branch information
cdtwigg authored and facebook-github-bot committed Mar 4, 2026
commit fbbb3cc5cbd5f871f4de6acd5fb7aaadc54cb0dd
1 change: 1 addition & 0 deletions cmake/build_variables.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ character_test_sources = [
"test/character/linear_skinning_test.cpp",
"test/character/locator_test.cpp",
"test/character/locator_state_test.cpp",
"test/character/mesh_reduction_test.cpp",
"test/character/parameter_limits_test.cpp",
"test/character/parameter_transform_test.cpp",
"test/character/pose_shape_test.cpp",
Expand Down
193 changes: 191 additions & 2 deletions momentum/character/character_utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,53 @@ std::vector<Eigen::Vector3i> remapFaces(
return remappedFaces;
}

template <typename T>
void remapPolyFaces(
MeshT<T>& newMesh,
const MeshT<T>& mesh,
const std::vector<bool>& activeVertices,
const std::vector<size_t>& reverseVertexMapping,
const std::vector<size_t>& reverseTexcoordMapping) {
if (mesh.polyFaces.empty()) {
return;
}

newMesh.polyFaceSizes.reserve(mesh.polyFaceSizes.size());
newMesh.polyFaces.reserve(mesh.polyFaces.size());
if (!mesh.polyTexcoordFaces.empty()) {
newMesh.polyTexcoordFaces.reserve(mesh.polyTexcoordFaces.size());
}

uint32_t offset = 0;
for (const auto polySize : mesh.polyFaceSizes) {
// Check if all vertices of this polygon are active
bool allActive = true;
for (uint32_t i = 0; i < polySize; ++i) {
const auto vtx = mesh.polyFaces[offset + i];
if (vtx >= activeVertices.size() || !activeVertices[vtx]) {
allActive = false;
break;
}
}

if (allActive) {
newMesh.polyFaceSizes.push_back(polySize);
for (uint32_t i = 0; i < polySize; ++i) {
newMesh.polyFaces.push_back(
static_cast<uint32_t>(reverseVertexMapping[mesh.polyFaces[offset + i]]));
}
if (!mesh.polyTexcoordFaces.empty()) {
for (uint32_t i = 0; i < polySize; ++i) {
newMesh.polyTexcoordFaces.push_back(
static_cast<uint32_t>(reverseTexcoordMapping[mesh.polyTexcoordFaces[offset + i]]));
}
}
}

offset += polySize;
}
}

std::vector<std::vector<int32_t>> remapLines(
const std::vector<std::vector<int32_t>>& lines,
const std::vector<size_t>& mapping) {
Expand Down Expand Up @@ -923,6 +970,57 @@ std::vector<bool> facesToVertices(
return activeVertices;
}

std::vector<bool> verticesToPolys(
const std::vector<uint32_t>& polyFaces,
const std::vector<uint32_t>& polyFaceSizes,
const std::vector<bool>& activeVertices) {
if (polyFaceSizes.empty()) {
return {};
}
MT_CHECK(!polyFaceSizes.empty());
std::vector<bool> activePolys(polyFaceSizes.size(), false);
uint32_t offset = 0;
for (size_t polyIdx = 0; polyIdx < polyFaceSizes.size(); ++polyIdx) {
const auto polySize = polyFaceSizes[polyIdx];
bool allActive = true;
for (uint32_t i = 0; i < polySize; ++i) {
const auto vtx = polyFaces[offset + i];
if (vtx >= activeVertices.size() || !activeVertices[vtx]) {
allActive = false;
break;
}
}
activePolys[polyIdx] = allActive;
offset += polySize;
}
return activePolys;
}

std::vector<bool> polysToVertices(
const std::vector<uint32_t>& polyFaces,
const std::vector<uint32_t>& polyFaceSizes,
const std::vector<bool>& activePolys,
size_t vertexCount) {
std::vector<bool> activeVertices(vertexCount, false);
if (activePolys.empty()) {
return activeVertices;
}
uint32_t offset = 0;
for (size_t polyIdx = 0; polyIdx < polyFaceSizes.size(); ++polyIdx) {
const auto polySize = polyFaceSizes[polyIdx];
if (activePolys[polyIdx]) {
for (uint32_t i = 0; i < polySize; ++i) {
const auto vtx = polyFaces[offset + i];
if (vtx < vertexCount) {
activeVertices[vtx] = true;
}
}
}
offset += polySize;
}
return activeVertices;
}

// Internal mesh reduction function used by both reduceMeshComponents and
// the public reduceMeshByFaces/reduceMeshByVertices for Mesh.
template <typename T>
Expand Down Expand Up @@ -954,22 +1052,26 @@ MeshT<T> reduceMeshInternal(
newMesh.lines = remapLines(mesh.lines, reverseVertexMapping);
}

std::vector<size_t> reverseTextureVertexMapping;
if (!mesh.texcoord_faces.empty() && !mesh.texcoords.empty()) {
std::vector<bool> activeTextureTriangles = activeFaces;
activeTextureTriangles.resize(mesh.texcoord_faces.size(), false);

const std::vector<bool> activeTextureVertices =
facesToVertices(mesh.texcoord_faces, activeTextureTriangles, mesh.texcoords.size());

auto [forwardTextureVertexMapping, reverseTextureVertexMapping] =
createIndexMapping(activeTextureVertices);
auto [forwardTextureVertexMapping, revTexMapping] = createIndexMapping(activeTextureVertices);
reverseTextureVertexMapping = std::move(revTexMapping);

newMesh.texcoords = selectVertices(mesh.texcoords, forwardTextureVertexMapping);
newMesh.texcoord_faces =
remapFaces(mesh.texcoord_faces, reverseTextureVertexMapping, activeTextureTriangles);
newMesh.texcoord_lines = remapLines(mesh.texcoord_lines, reverseTextureVertexMapping);
}

// Remap polygon face data
remapPolyFaces(newMesh, mesh, activeVertices, reverseVertexMapping, reverseTextureVertexMapping);

return newMesh;
}

Expand Down Expand Up @@ -1185,6 +1287,61 @@ std::vector<bool> facesToVertices(const MeshT<T>& mesh, const std::vector<bool>&
return facesToVertices(mesh.faces, activeFaces, mesh.vertices.size());
}

template <typename T>
std::vector<bool> verticesToPolys(const MeshT<T>& mesh, const std::vector<bool>& activeVertices) {
MT_CHECK(
activeVertices.size() == mesh.vertices.size(),
"Active vertices size ({}) does not match mesh vertex count ({})",
activeVertices.size(),
mesh.vertices.size());

return verticesToPolys(mesh.polyFaces, mesh.polyFaceSizes, activeVertices);
}

template <typename T>
std::vector<bool> polysToVertices(const MeshT<T>& mesh, const std::vector<bool>& activePolys) {
MT_CHECK(
activePolys.size() == mesh.polyFaceSizes.size(),
"Active polys size ({}) does not match polygon count ({})",
activePolys.size(),
mesh.polyFaceSizes.size());

return polysToVertices(mesh.polyFaces, mesh.polyFaceSizes, activePolys, mesh.vertices.size());
}

template <typename T>
MeshT<T> reduceMeshByPolys(const MeshT<T>& mesh, const std::vector<bool>& activePolys) {
MT_CHECK(
activePolys.size() == mesh.polyFaceSizes.size(),
"Active polys size ({}) does not match polygon count ({})",
activePolys.size(),
mesh.polyFaceSizes.size());

// Convert polygon selection to vertex selection, then to face selection
const auto activeVertices = polysToVertices(mesh, activePolys);
const auto activeFaces = verticesToFaces(mesh, activeVertices);

return reduceMeshInternal(mesh, activeVertices, activeFaces);
}

template <typename T>
CharacterT<T> reduceMeshByPolys(
const CharacterT<T>& character,
const std::vector<bool>& activePolys) {
MT_CHECK(character.mesh, "Cannot reduce mesh: character has no mesh");
MT_CHECK(
activePolys.size() == character.mesh->polyFaceSizes.size(),
"Active polys size ({}) does not match polygon count ({})",
activePolys.size(),
character.mesh->polyFaceSizes.size());

// Convert polygon selection to vertex selection, then to face selection
const auto activeVertices = polysToVertices(*character.mesh, activePolys);
const auto activeFaces = verticesToFaces(*character.mesh, activeVertices);

return reduceMeshComponents(character, activeVertices, activeFaces);
}

// Explicit instantiations for commonly used types
template CharacterT<float> reduceMeshByVertices<float>(
const CharacterT<float>& character,
Expand Down Expand Up @@ -1234,4 +1391,36 @@ template std::vector<bool> facesToVertices<double>(
const MeshT<double>& mesh,
const std::vector<bool>& activeFaces);

template std::vector<bool> verticesToPolys<float>(
const MeshT<float>& mesh,
const std::vector<bool>& activeVertices);

template std::vector<bool> verticesToPolys<double>(
const MeshT<double>& mesh,
const std::vector<bool>& activeVertices);

template std::vector<bool> polysToVertices<float>(
const MeshT<float>& mesh,
const std::vector<bool>& activePolys);

template std::vector<bool> polysToVertices<double>(
const MeshT<double>& mesh,
const std::vector<bool>& activePolys);

template MeshT<float> reduceMeshByPolys<float>(
const MeshT<float>& mesh,
const std::vector<bool>& activePolys);

template MeshT<double> reduceMeshByPolys<double>(
const MeshT<double>& mesh,
const std::vector<bool>& activePolys);

template CharacterT<float> reduceMeshByPolys<float>(
const CharacterT<float>& character,
const std::vector<bool>& activePolys);

template CharacterT<double> reduceMeshByPolys<double>(
const CharacterT<double>& character,
const std::vector<bool>& activePolys);

} // namespace momentum
50 changes: 49 additions & 1 deletion momentum/character/character_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,60 @@ template <typename T>

/// Converts face selection to vertex selection
///
/// @param[in] character Character containing the mesh
/// @param[in] mesh The mesh containing the faces
/// @param[in] activeFaces Boolean vector indicating which faces are active
/// @return Boolean vector indicating which vertices are used by active faces
template <typename T>
[[nodiscard]] std::vector<bool> facesToVertices(
const MeshT<T>& mesh,
const std::vector<bool>& activeFaces);

/// Converts vertex selection to polygon selection
///
/// A polygon is active if all its vertices are active.
///
/// @param[in] mesh The mesh containing the polygon data
/// @param[in] activeVertices Boolean vector indicating which vertices are active
/// @return Boolean vector indicating which polygons only contain active vertices
template <typename T>
[[nodiscard]] std::vector<bool> verticesToPolys(
const MeshT<T>& mesh,
const std::vector<bool>& activeVertices);

/// Converts polygon selection to vertex selection
///
/// @param[in] mesh The mesh containing the polygon data
/// @param[in] activePolys Boolean vector indicating which polygons are active
/// @return Boolean vector indicating which vertices are used by active polygons
template <typename T>
[[nodiscard]] std::vector<bool> polysToVertices(
const MeshT<T>& mesh,
const std::vector<bool>& activePolys);

/// Reduces a standalone mesh to only include the specified polygons and associated vertices.
///
/// Converts polygon selection to vertex selection and face selection, then reduces the mesh.
/// Both triangulated faces and polygon data are filtered and remapped.
///
/// @param[in] mesh The mesh to reduce
/// @param[in] activePolys Boolean vector indicating which polygons to keep
/// @return A new mesh containing only the specified polygons and their referenced vertices
template <typename T>
[[nodiscard]] MeshT<T> reduceMeshByPolys(
const MeshT<T>& mesh,
const std::vector<bool>& activePolys);

/// Reduces the mesh to only include the specified polygons and associated vertices
///
/// Converts polygon selection to vertex selection and face selection, then reduces the mesh.
/// Both triangulated faces and polygon data are filtered and remapped.
///
/// @param[in] character Character to be reduced
/// @param[in] activePolys Boolean vector indicating which polygons to keep
/// @return A new character with mesh reduced to the specified polygons
template <typename T>
[[nodiscard]] CharacterT<T> reduceMeshByPolys(
const CharacterT<T>& character,
const std::vector<bool>& activePolys);

} // namespace momentum
2 changes: 0 additions & 2 deletions momentum/test/character/character_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
#include "momentum/character/blend_shape.h"
#include "momentum/character/blend_shape_skinning.h"
#include "momentum/character/character_state.h"
#include "momentum/character/character_utility.h"
#include "momentum/character/collision_geometry.h"
#include "momentum/character/linear_skinning.h"
#include "momentum/character/locator.h"
#include "momentum/character/parameter_transform.h"
#include "momentum/character/skeleton.h"
Expand Down
Loading
Loading