|
''' |
|
MIT License |
|
|
|
Copyright (c) 2019 Shunsuke Saito, Zeng Huang, and Ryota Natsume |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
|
of this software and associated documentation files (the "Software"), to deal |
|
in the Software without restriction, including without limitation the rights |
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
copies of the Software, and to permit persons to whom the Software is |
|
furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included in all |
|
copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
SOFTWARE. |
|
''' |
|
import numpy as np |
|
|
|
|
|
def save_obj_mesh(mesh_path, verts, faces): |
|
file = open(mesh_path, 'w') |
|
for v in verts: |
|
file.write('v %.4f %.4f %.4f\n' % (v[0], v[1], v[2])) |
|
for f in faces: |
|
f_plus = f + 1 |
|
file.write('f %d %d %d\n' % (f_plus[0], f_plus[1], f_plus[2])) |
|
file.close() |
|
|
|
|
|
def read_mtlfile(fname): |
|
materials = {} |
|
with open(fname) as f: |
|
lines = f.read().splitlines() |
|
|
|
for line in lines: |
|
if line: |
|
split_line = line.strip().split(' ', 1) |
|
if len(split_line) < 2: |
|
continue |
|
|
|
prefix, data = split_line[0], split_line[1] |
|
if 'newmtl' in prefix: |
|
material = {} |
|
materials[data] = material |
|
elif materials: |
|
if data: |
|
split_data = data.strip().split(' ') |
|
|
|
|
|
|
|
if 'map' in prefix: |
|
material[prefix] = split_data[-1].split('\\')[-1] |
|
elif len(split_data) > 1: |
|
material[prefix] = tuple(float(d) for d in split_data) |
|
else: |
|
try: |
|
material[prefix] = int(data) |
|
except ValueError: |
|
material[prefix] = float(data) |
|
|
|
return materials |
|
|
|
|
|
def load_obj_mesh_mtl(mesh_file): |
|
vertex_data = [] |
|
norm_data = [] |
|
uv_data = [] |
|
|
|
face_data = [] |
|
face_norm_data = [] |
|
face_uv_data = [] |
|
|
|
|
|
face_data_mat = {} |
|
face_norm_data_mat = {} |
|
face_uv_data_mat = {} |
|
|
|
|
|
mtl_data = None |
|
cur_mat = None |
|
|
|
if isinstance(mesh_file, str): |
|
f = open(mesh_file, "r") |
|
else: |
|
f = mesh_file |
|
for line in f: |
|
if isinstance(line, bytes): |
|
line = line.decode("utf-8") |
|
if line.startswith('#'): |
|
continue |
|
values = line.split() |
|
if not values: |
|
continue |
|
|
|
if values[0] == 'v': |
|
v = list(map(float, values[1:4])) |
|
vertex_data.append(v) |
|
elif values[0] == 'vn': |
|
vn = list(map(float, values[1:4])) |
|
norm_data.append(vn) |
|
elif values[0] == 'vt': |
|
vt = list(map(float, values[1:3])) |
|
uv_data.append(vt) |
|
elif values[0] == 'mtllib': |
|
mtl_data = read_mtlfile(mesh_file.replace(mesh_file.split('/')[-1],values[1])) |
|
elif values[0] == 'usemtl': |
|
cur_mat = values[1] |
|
elif values[0] == 'f': |
|
|
|
l_face_data = [] |
|
l_face_uv_data = [] |
|
l_face_norm_data = [] |
|
|
|
|
|
if len(values) > 4: |
|
f = list(map(lambda x: int(x.split('/')[0]) if int(x.split('/')[0]) < 0 else int(x.split('/')[0])-1, values[1:4])) |
|
l_face_data.append(f) |
|
f = list(map(lambda x: int(x.split('/')[0]) if int(x.split('/')[0]) < 0 else int(x.split('/')[0])-1, [values[3], values[4], values[1]])) |
|
l_face_data.append(f) |
|
|
|
else: |
|
f = list(map(lambda x: int(x.split('/')[0]) if int(x.split('/')[0]) < 0 else int(x.split('/')[0])-1, values[1:4])) |
|
l_face_data.append(f) |
|
|
|
if len(values[1].split('/')) >= 2: |
|
|
|
if len(values) > 4: |
|
f = list(map(lambda x: int(x.split('/')[1]) if int(x.split('/')[1]) < 0 else int(x.split('/')[1])-1, values[1:4])) |
|
l_face_uv_data.append(f) |
|
f = list(map(lambda x: int(x.split('/')[1]) if int(x.split('/')[1]) < 0 else int(x.split('/')[1])-1, [values[3], values[4], values[1]])) |
|
l_face_uv_data.append(f) |
|
|
|
elif len(values[1].split('/')[1]) != 0: |
|
f = list(map(lambda x: int(x.split('/')[1]) if int(x.split('/')[1]) < 0 else int(x.split('/')[1])-1, values[1:4])) |
|
l_face_uv_data.append(f) |
|
|
|
if len(values[1].split('/')) == 3: |
|
|
|
if len(values) > 4: |
|
f = list(map(lambda x: int(x.split('/')[2]) if int(x.split('/')[2]) < 0 else int(x.split('/')[2])-1, values[1:4])) |
|
l_face_norm_data.append(f) |
|
f = list(map(lambda x: int(x.split('/')[2]) if int(x.split('/')[2]) < 0 else int(x.split('/')[2])-1, [values[3], values[4], values[1]])) |
|
l_face_norm_data.append(f) |
|
|
|
elif len(values[1].split('/')[2]) != 0: |
|
f = list(map(lambda x: int(x.split('/')[2]) if int(x.split('/')[2]) < 0 else int(x.split('/')[2])-1, values[1:4])) |
|
l_face_norm_data.append(f) |
|
|
|
face_data += l_face_data |
|
face_uv_data += l_face_uv_data |
|
face_norm_data += l_face_norm_data |
|
|
|
if cur_mat is not None: |
|
if cur_mat not in face_data_mat.keys(): |
|
face_data_mat[cur_mat] = [] |
|
if cur_mat not in face_uv_data_mat.keys(): |
|
face_uv_data_mat[cur_mat] = [] |
|
if cur_mat not in face_norm_data_mat.keys(): |
|
face_norm_data_mat[cur_mat] = [] |
|
face_data_mat[cur_mat] += l_face_data |
|
face_uv_data_mat[cur_mat] += l_face_uv_data |
|
face_norm_data_mat[cur_mat] += l_face_norm_data |
|
|
|
vertices = np.array(vertex_data) |
|
faces = np.array(face_data) |
|
|
|
norms = np.array(norm_data) |
|
norms = normalize_v3(norms) |
|
face_normals = np.array(face_norm_data) |
|
|
|
uvs = np.array(uv_data) |
|
face_uvs = np.array(face_uv_data) |
|
|
|
out_tuple = (vertices, faces, norms, face_normals, uvs, face_uvs) |
|
|
|
if cur_mat is not None and mtl_data is not None: |
|
for key in face_data_mat: |
|
face_data_mat[key] = np.array(face_data_mat[key]) |
|
face_uv_data_mat[key] = np.array(face_uv_data_mat[key]) |
|
face_norm_data_mat[key] = np.array(face_norm_data_mat[key]) |
|
|
|
out_tuple += (face_data_mat, face_norm_data_mat, face_uv_data_mat, mtl_data) |
|
|
|
return out_tuple |
|
|
|
|
|
def load_obj_mesh(mesh_file, with_normal=False, with_texture=False): |
|
vertex_data = [] |
|
norm_data = [] |
|
uv_data = [] |
|
|
|
face_data = [] |
|
face_norm_data = [] |
|
face_uv_data = [] |
|
|
|
if isinstance(mesh_file, str): |
|
f = open(mesh_file, "r") |
|
else: |
|
f = mesh_file |
|
for line in f: |
|
if isinstance(line, bytes): |
|
line = line.decode("utf-8") |
|
if line.startswith('#'): |
|
continue |
|
values = line.split() |
|
if not values: |
|
continue |
|
|
|
if values[0] == 'v': |
|
v = list(map(float, values[1:4])) |
|
vertex_data.append(v) |
|
elif values[0] == 'vn': |
|
vn = list(map(float, values[1:4])) |
|
norm_data.append(vn) |
|
elif values[0] == 'vt': |
|
vt = list(map(float, values[1:3])) |
|
uv_data.append(vt) |
|
|
|
elif values[0] == 'f': |
|
|
|
if len(values) > 4: |
|
f = list(map(lambda x: int(x.split('/')[0]), values[1:4])) |
|
face_data.append(f) |
|
f = list(map(lambda x: int(x.split('/')[0]), [values[3], values[4], values[1]])) |
|
face_data.append(f) |
|
|
|
else: |
|
f = list(map(lambda x: int(x.split('/')[0]), values[1:4])) |
|
face_data.append(f) |
|
|
|
|
|
if len(values[1].split('/')) >= 2: |
|
|
|
if len(values) > 4: |
|
f = list(map(lambda x: int(x.split('/')[1]), values[1:4])) |
|
face_uv_data.append(f) |
|
f = list(map(lambda x: int(x.split('/')[1]), [values[3], values[4], values[1]])) |
|
face_uv_data.append(f) |
|
|
|
elif len(values[1].split('/')[1]) != 0: |
|
f = list(map(lambda x: int(x.split('/')[1]), values[1:4])) |
|
face_uv_data.append(f) |
|
|
|
if len(values[1].split('/')) == 3: |
|
|
|
if len(values) > 4: |
|
f = list(map(lambda x: int(x.split('/')[2]), values[1:4])) |
|
face_norm_data.append(f) |
|
f = list(map(lambda x: int(x.split('/')[2]), [values[3], values[4], values[1]])) |
|
face_norm_data.append(f) |
|
|
|
elif len(values[1].split('/')[2]) != 0: |
|
f = list(map(lambda x: int(x.split('/')[2]), values[1:4])) |
|
face_norm_data.append(f) |
|
|
|
vertices = np.array(vertex_data) |
|
faces = np.array(face_data) - 1 |
|
|
|
if with_texture and with_normal: |
|
uvs = np.array(uv_data) |
|
face_uvs = np.array(face_uv_data) - 1 |
|
norms = np.array(norm_data) |
|
if norms.shape[0] == 0: |
|
norms = compute_normal(vertices, faces) |
|
face_normals = faces |
|
else: |
|
norms = normalize_v3(norms) |
|
face_normals = np.array(face_norm_data) - 1 |
|
return vertices, faces, norms, face_normals, uvs, face_uvs |
|
|
|
if with_texture: |
|
uvs = np.array(uv_data) |
|
face_uvs = np.array(face_uv_data) - 1 |
|
return vertices, faces, uvs, face_uvs |
|
|
|
if with_normal: |
|
norms = np.array(norm_data) |
|
norms = normalize_v3(norms) |
|
face_normals = np.array(face_norm_data) - 1 |
|
return vertices, faces, norms, face_normals |
|
|
|
return vertices, faces |
|
|
|
|
|
def normalize_v3(arr): |
|
''' Normalize a numpy array of 3 component vectors shape=(n,3) ''' |
|
lens = np.sqrt(arr[:, 0] ** 2 + arr[:, 1] ** 2 + arr[:, 2] ** 2) |
|
eps = 0.00000001 |
|
lens[lens < eps] = eps |
|
arr[:, 0] /= lens |
|
arr[:, 1] /= lens |
|
arr[:, 2] /= lens |
|
return arr |
|
|
|
|
|
def compute_normal(vertices, faces): |
|
|
|
norm = np.zeros(vertices.shape, dtype=vertices.dtype) |
|
|
|
tris = vertices[faces] |
|
|
|
n = np.cross(tris[::, 1] - tris[::, 0], tris[::, 2] - tris[::, 0]) |
|
|
|
|
|
normalize_v3(n) |
|
|
|
|
|
|
|
|
|
norm[faces[:, 0]] += n |
|
norm[faces[:, 1]] += n |
|
norm[faces[:, 2]] += n |
|
normalize_v3(norm) |
|
|
|
return norm |
|
|
|
|
|
def compute_tangent(vertices, faces, normals, uvs, faceuvs): |
|
|
|
|
|
c1 = np.cross(normals, np.array([0,1,0.0])) |
|
tan = c1 |
|
normalize_v3(tan) |
|
btan = np.cross(normals, tan) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return tan, btan |
|
|
|
if __name__ == '__main__': |
|
pts, tri, nml, trin, uvs, triuv = load_obj_mesh('/home/ICT2000/ssaito/Documents/Body/tmp/Baseball_Pitching/0012.obj', True, True) |
|
compute_tangent(pts, tri, uvs, triuv) |