pengc02's picture
all
ec9a6bc
import torch
import torch.nn.functional as F
import numpy as np
from AnimatableGaussians.utils.knn import knn_gather
def barycentric_coordinate(pts, face_vertices):
"""
:param pts: (B, N, 3)
:param face_vertices: (B, N, 3, 3)
:return bc_coords: (B, N, 3)
"""
vec0 = face_vertices[:, :, 0] - pts
vec1 = face_vertices[:, :, 1] - pts
vec2 = face_vertices[:, :, 2] - pts
area0 = torch.linalg.norm(torch.cross(vec1, vec2), dim = -1)
area1 = torch.linalg.norm(torch.cross(vec2, vec0), dim = -1)
area2 = torch.linalg.norm(torch.cross(vec0, vec1), dim = -1)
bc_coord = torch.stack([area0, area1, area2], -1)
bc_coord = F.normalize(bc_coord, p = 1, dim = -1, eps = 1e-16)
return bc_coord
def barycentric_interpolate(vert_attris, faces, face_ids, bc_coords):
"""
:param vert_attris: (B, V, C)
:param faces: (B, F, 3)
:param face_ids: (B, N)
:param bc_coords: (B, N, 3)
:return inter_attris: (B, N, C)
"""
selected_faces = torch.gather(faces, 1, face_ids.unsqueeze(-1).expand(-1, -1, 3)) # (B, N, 3)
face_attris = knn_gather(vert_attris, selected_faces) # (B, N, 3, C)
inter_attris = (face_attris * bc_coords.unsqueeze(-1)).sum(-2) # (B, N, C)
return inter_attris
def sample_surface_pts(mesh, count, mask = None, w_color = False):
"""
Modified from Scanimate code
Sample the surface of a mesh, returning the specified
number of points
For individual triangle sampling uses this method:
http://mathworld.wolfram.com/TrianglePointPicking.html
Parameters
---------
mesh : trimesh.Trimesh
Geometry to sample the surface of
count : int
Number of points to return
Returns
---------
samples : (count, 3) float
Points in space on the surface of mesh
face_index : (count,) int
Indices of faces for each sampled point
"""
valid_faces = mesh.faces[mask]
face_index = np.random.choice(a = valid_faces.shape[0], size = count, replace = True)
selected_faces = valid_faces[face_index]
# pull triangles into the form of an origin + 2 vectors
tri_origins = mesh.vertices[selected_faces[:, 0]]
tri_vectors = mesh.vertices[selected_faces[:, 1:]].copy()
tri_vectors -= np.tile(tri_origins, (1, 2)).reshape((-1, 2, 3))
# randomly generate two 0-1 scalar components to multiply edge vectors by
random_lengths = np.random.random((len(tri_vectors), 2, 1))
# points will be distributed on a quadrilateral if we use 2 0-1 samples
# if the two scalar components sum less than 1.0 the point will be
# inside the triangle, so we find vectors longer than 1.0 and
# transform them to be inside the triangle
random_test = random_lengths.sum(axis = 1).reshape(-1) > 1.0
random_lengths[random_test] -= 1.0
random_lengths = np.abs(random_lengths)
# multiply triangle edge vectors by the random lengths and sum
sample_vector = (tri_vectors * random_lengths).sum(axis = 1)
# finally, offset by the origin to generate
# (n,3) points in space on the triangle
samples = sample_vector + tri_origins
colors = None
normals = None
if w_color:
colors = mesh.visual.vertex_colors[:, :3].astype(np.float32)
colors = colors / 255.0
colors = colors.view(np.ndarray)[selected_faces]
clr_origins = colors[:, 0]
clr_vectors = colors[:, 1:]
clr_vectors -= np.tile(clr_origins, (1, 2)).reshape((-1, 2, 3))
sample_color = (clr_vectors * random_lengths).sum(axis=1)
colors = sample_color + clr_origins
normals = mesh.face_normals[face_index]
return samples, colors, normals
def normalize_vert_bbox(verts, attris = None, dim=-1, per_axis=False):
bbox_min = torch.min(verts, dim=dim, keepdim=True)[0]
bbox_max = torch.max(verts, dim=dim, keepdim=True)[0]
if attris is not None:
verts = attris
verts = verts - 0.5 * (bbox_max + bbox_min)
if per_axis:
verts = 2 * verts / (bbox_max - bbox_min)
else:
verts = 2 * verts / torch.max(bbox_max-bbox_min, dim=dim, keepdim=True)[0]
return verts