kyleleey
first commit
98a77e0
raw
history blame
12.4 kB
# Copyright (c) 2020-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
# property and proprietary rights in and to this material, related
# documentation and any modifications thereto. Any use, reproduction,
# disclosure or distribution of this material and related documentation
# without an express license agreement from NVIDIA CORPORATION or
# its affiliates is strictly prohibited.
import os
import torch
import xatlas
import trimesh
import numpy as np
import cv2
import nvdiffrast.torch as dr
from video3d.render.render import render_uv
from video3d.render.mesh import Mesh
from . import texture
from . import mesh
from . import material
######################################################################################
# Utility functions
######################################################################################
def _find_mat(materials, name):
for mat in materials:
if mat['name'] == name:
return mat
return materials[0] # Materials 0 is the default
######################################################################################
# Create mesh object from objfile
######################################################################################
def load_obj(filename, clear_ks=True, mtl_override=None):
obj_path = os.path.dirname(filename)
# Read entire file
with open(filename, 'r') as f:
lines = f.readlines()
# Load materials
all_materials = [
{
'name' : '_default_mat',
'bsdf' : 'pbr',
'kd' : texture.Texture2D(torch.tensor([0.5, 0.5, 0.5], dtype=torch.float32, device='cuda')),
'ks' : texture.Texture2D(torch.tensor([0.0, 0.0, 0.0], dtype=torch.float32, device='cuda'))
}
]
if mtl_override is None:
for line in lines:
if len(line.split()) == 0:
continue
if line.split()[0] == 'mtllib':
all_materials += material.load_mtl(os.path.join(obj_path, line.split()[1]), clear_ks) # Read in entire material library
else:
all_materials += material.load_mtl(mtl_override)
# load vertices
vertices, texcoords, normals = [], [], []
for line in lines:
if len(line.split()) == 0:
continue
prefix = line.split()[0].lower()
if prefix == 'v':
vertices.append([float(v) for v in line.split()[1:]])
elif prefix == 'vt':
val = [float(v) for v in line.split()[1:]]
texcoords.append([val[0], 1.0 - val[1]])
elif prefix == 'vn':
normals.append([float(v) for v in line.split()[1:]])
# load faces
activeMatIdx = None
used_materials = []
faces, tfaces, nfaces, mfaces = [], [], [], []
for line in lines:
if len(line.split()) == 0:
continue
prefix = line.split()[0].lower()
if prefix == 'usemtl': # Track used materials
mat = _find_mat(all_materials, line.split()[1])
if not mat in used_materials:
used_materials.append(mat)
activeMatIdx = used_materials.index(mat)
elif prefix == 'f': # Parse face
vs = line.split()[1:]
nv = len(vs)
vv = vs[0].split('/')
v0 = int(vv[0]) - 1
t0 = int(vv[1]) - 1 if vv[1] != "" else -1
n0 = int(vv[2]) - 1 if vv[2] != "" else -1
for i in range(nv - 2): # Triangulate polygons
vv = vs[i + 1].split('/')
v1 = int(vv[0]) - 1
t1 = int(vv[1]) - 1 if vv[1] != "" else -1
n1 = int(vv[2]) - 1 if vv[2] != "" else -1
vv = vs[i + 2].split('/')
v2 = int(vv[0]) - 1
t2 = int(vv[1]) - 1 if vv[1] != "" else -1
n2 = int(vv[2]) - 1 if vv[2] != "" else -1
mfaces.append(activeMatIdx)
faces.append([v0, v1, v2])
tfaces.append([t0, t1, t2])
nfaces.append([n0, n1, n2])
assert len(tfaces) == len(faces) and len(nfaces) == len (faces)
# Create an "uber" material by combining all textures into a larger texture
if len(used_materials) > 1:
uber_material, texcoords, tfaces = material.merge_materials(used_materials, texcoords, tfaces, mfaces)
else:
uber_material = used_materials[0]
vertices = torch.tensor(vertices, dtype=torch.float32, device='cuda')
texcoords = torch.tensor(texcoords, dtype=torch.float32, device='cuda') if len(texcoords) > 0 else None
normals = torch.tensor(normals, dtype=torch.float32, device='cuda') if len(normals) > 0 else None
faces = torch.tensor(faces, dtype=torch.int64, device='cuda')
tfaces = torch.tensor(tfaces, dtype=torch.int64, device='cuda') if texcoords is not None else None
nfaces = torch.tensor(nfaces, dtype=torch.int64, device='cuda') if normals is not None else None
return mesh.Mesh(vertices, faces, normals, nfaces, texcoords, tfaces, material=uber_material)
######################################################################################
# Save mesh object to objfile
######################################################################################
def write_obj(folder, fname, mesh, idx, save_material=True, feat=None, resolution=[256, 256]):
obj_file = os.path.join(folder, fname + '.obj')
print("Writing mesh: ", obj_file)
with open(obj_file, "w") as f:
f.write(f"mtllib {fname}.mtl\n")
f.write("g default\n")
v_pos = mesh.v_pos[idx].detach().cpu().numpy() if mesh.v_pos is not None else None
v_nrm = mesh.v_nrm[idx].detach().cpu().numpy() if mesh.v_nrm is not None else None
v_tex = mesh.v_tex[idx].detach().cpu().numpy() if mesh.v_tex is not None else None
t_pos_idx = mesh.t_pos_idx[0].detach().cpu().numpy() if mesh.t_pos_idx is not None else None
t_nrm_idx = mesh.t_nrm_idx[0].detach().cpu().numpy() if mesh.t_nrm_idx is not None else None
t_tex_idx = mesh.t_tex_idx[0].detach().cpu().numpy() if mesh.t_tex_idx is not None else None
print(" writing %d vertices" % len(v_pos))
for v in v_pos:
f.write('v {} {} {} \n'.format(v[0], v[1], v[2]))
if v_tex is not None and save_material:
print(" writing %d texcoords" % len(v_tex))
assert(len(t_pos_idx) == len(t_tex_idx))
for v in v_tex:
f.write('vt {} {} \n'.format(v[0], 1.0 - v[1]))
if v_nrm is not None:
print(" writing %d normals" % len(v_nrm))
assert(len(t_pos_idx) == len(t_nrm_idx))
for v in v_nrm:
f.write('vn {} {} {}\n'.format(v[0], v[1], v[2]))
# faces
f.write("s 1 \n")
f.write("g pMesh1\n")
f.write("usemtl defaultMat\n")
# Write faces
print(" writing %d faces" % len(t_pos_idx))
for i in range(len(t_pos_idx)):
f.write("f ")
for j in range(3):
f.write(' %s/%s/%s' % (str(t_pos_idx[i][j]+1), '' if v_tex is None else str(t_tex_idx[i][j]+1), '' if v_nrm is None else str(t_nrm_idx[i][j]+1)))
f.write("\n")
if save_material and mesh.material is not None:
mtl_file = os.path.join(folder, fname + '.mtl')
print("Writing material: ", mtl_file)
material.save_mtl(mtl_file, mesh.material, mesh=mesh.get_n(idx), feat=feat, resolution=resolution)
print("Done exporting mesh")
def write_textured_obj(folder, fname, mesh, idx, save_material=True, feat=None, resolution=[256, 256], prior_shape=None):
mesh = mesh.get_n(idx)
obj_file = os.path.join(folder, fname + '.obj')
print("Writing mesh: ", obj_file)
# Create uvs with xatlas
v_pos = mesh.v_pos.detach().cpu().numpy()
t_pos_idx = mesh.t_pos_idx.detach().cpu().numpy()
# v_color = torch.Tensor(v_pos)[None].to("cuda")
# v_color = mesh.material.sample(v_color, feat)
# v_color = v_color[0,0,:,:3].detach().cpu()
# v_color = torch.concat([v_color, torch.ones((v_color.shape[0], 1))], dim=-1)
# v_color = v_color.numpy() * 255
# v_color = v_color.astype(np.int32)
# tmp = trimesh.Trimesh(vertices=v_pos[0], faces=t_pos_idx[0], vertex_colors=v_color)
# _ = tmp.export("tmp.obj")
# from pdb import set_trace; set_trace()
atlas = xatlas.Atlas()
atlas.add_mesh(
v_pos[0],
t_pos_idx[0],
)
co = xatlas.ChartOptions()
po = xatlas.PackOptions()
# for k, v in xatlas_chart_options.items():
# setattr(co, k, v)
# for k, v in xatlas_pack_options.items():
# setattr(po, k, v)
atlas.generate(co, po)
vmapping, indices, uvs = atlas.get_mesh(0)
# vmapping, indices, uvs = xatlas.parametrize(v_pos[0], t_pos_idx[0])
# Convert to tensors
indices_int64 = indices.astype(np.uint64, casting='same_kind').view(np.int64)
uvs = torch.tensor(uvs, dtype=torch.float32, device='cuda')
faces = torch.tensor(indices_int64, dtype=torch.int64, device='cuda')
# new_mesh = Mesh(v_tex=uvs, t_tex_idx=faces, base=mesh)
new_mesh = Mesh(v_tex=uvs[None], t_tex_idx=faces[None], base=mesh)
# glctx = dr.RasterizeGLContext()
# mask, kd, ks, normal = render_uv(glctx, new_mesh, resolution, mesh.material, feat=feat)
# kd_min, kd_max = torch.tensor([ 0.0, 0.0, 0.0, 0.0], dtype=torch.float32, device='cuda'), torch.tensor([ 1.0, 1.0, 1.0, 1.0], dtype=torch.float32, device='cuda')
# ks_min, ks_max = torch.tensor([ 0.0, 0.0, 0.0] , dtype=torch.float32, device='cuda'), torch.tensor([ 0.0, 0.0, 0.0] , dtype=torch.float32, device='cuda')
# nrm_min, nrm_max = torch.tensor([-1.0, -1.0, 0.0], dtype=torch.float32, device='cuda'), torch.tensor([ 1.0, 1.0, 1.0], dtype=torch.float32, device='cuda')
new_mesh.material = material.Material({
'bsdf' : 'diffuse',
# 'kd' : texture.Texture2D(kd, min_max=[kd_min, kd_max]),
# 'ks' : texture.Texture2D(ks, min_max=[ks_min, ks_max]),
# 'normal' : texture.Texture2D(normal, min_max=[nrm_min, nrm_max]),
'kd_ks_normal': mesh.material
})
with open(obj_file, "w") as f:
f.write(f"mtllib {fname}.mtl\n")
f.write("g default\n")
v_pos = new_mesh.v_pos[idx].detach().cpu().numpy() if new_mesh.v_pos is not None else None
v_nrm = new_mesh.v_nrm[idx].detach().cpu().numpy() if new_mesh.v_nrm is not None else None
v_tex = new_mesh.v_tex[idx].detach().cpu().numpy() if new_mesh.v_tex is not None else None
t_pos_idx = new_mesh.t_pos_idx[0].detach().cpu().numpy() if new_mesh.t_pos_idx is not None else None
t_nrm_idx = new_mesh.t_nrm_idx[0].detach().cpu().numpy() if new_mesh.t_nrm_idx is not None else None
t_tex_idx = new_mesh.t_tex_idx[0].detach().cpu().numpy() if new_mesh.t_tex_idx is not None else None
print(" writing %d vertices" % len(v_pos))
for v in v_pos:
f.write('v {} {} {} \n'.format(v[0], v[1], v[2]))
if v_tex is not None and save_material:
print(" writing %d texcoords" % len(v_tex))
assert(len(t_pos_idx) == len(t_tex_idx))
for v in v_tex:
f.write('vt {} {} \n'.format(v[0], 1.0 - v[1]))
if v_nrm is not None:
print(" writing %d normals" % len(v_nrm))
assert(len(t_pos_idx) == len(t_nrm_idx))
for v in v_nrm:
f.write('vn {} {} {}\n'.format(v[0], v[1], v[2]))
# faces
f.write("s 1 \n")
f.write("g pMesh1\n")
f.write("usemtl defaultMat\n")
# Write faces
print(" writing %d faces" % len(t_pos_idx))
for i in range(len(t_pos_idx)):
f.write("f ")
for j in range(3):
f.write(' %s/%s/%s' % (str(t_pos_idx[i][j]+1), '' if v_tex is None else str(t_tex_idx[i][j]+1), '' if v_nrm is None else str(t_nrm_idx[i][j]+1)))
f.write("\n")
mtl_file = os.path.join(folder, fname + '.mtl')
print("Writing material: ", mtl_file)
material.save_mtl(mtl_file, new_mesh.material, mesh=new_mesh, feat=feat, resolution=resolution, prior_shape=prior_shape)
print("Done exporting mesh")