Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
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
Loading
Loading