Difficult-Rocket/libs/pyglet/model/codecs/obj.py

239 lines
7.5 KiB
Python
Raw Normal View History

2021-04-16 23:21:06 +08:00
import os
import pyglet
from pyglet.gl import GL_TRIANGLES
2022-06-04 11:08:30 +08:00
from pyglet.util import asstr
2021-04-16 23:21:06 +08:00
from .. import Model, Material, MaterialGroup, TexturedMaterialGroup
from . import ModelDecodeException, ModelDecoder
class Mesh:
def __init__(self, name):
self.name = name
self.material = None
self.indices = []
self.vertices = []
self.normals = []
self.tex_coords = []
self.colors = []
def load_material_library(filename):
file = open(filename, 'r')
name = None
diffuse = [1.0, 1.0, 1.0]
ambient = [1.0, 1.0, 1.0]
specular = [1.0, 1.0, 1.0]
emission = [0.0, 0.0, 0.0]
shininess = 100.0
opacity = 1.0
texture_name = None
2021-04-17 01:14:38 +08:00
matlib = {}
2021-04-16 23:21:06 +08:00
for line in file:
if line.startswith('#'):
continue
values = line.split()
if not values:
continue
if values[0] == 'newmtl':
2021-04-17 01:14:38 +08:00
if name is not None:
# save previous material
for item in (diffuse, ambient, specular, emission):
item.append(opacity)
matlib[name] = Material(name, diffuse, ambient, specular, emission, shininess, texture_name)
2021-04-16 23:21:06 +08:00
name = values[1]
2021-04-17 01:14:38 +08:00
2021-04-16 23:21:06 +08:00
elif name is None:
2022-07-16 20:20:23 +08:00
raise ModelDecodeException(f'Expected "newmtl" in {filename}')
2021-04-16 23:21:06 +08:00
try:
if values[0] == 'Kd':
diffuse = list(map(float, values[1:]))
elif values[0] == 'Ka':
ambient = list(map(float, values[1:]))
elif values[0] == 'Ks':
specular = list(map(float, values[1:]))
elif values[0] == 'Ke':
emission = list(map(float, values[1:]))
elif values[0] == 'Ns':
2022-07-16 20:20:23 +08:00
shininess = float(values[1]) # Blender exports 1~1000
shininess = (shininess * 128) / 1000 # Normalize to 1~128 for OpenGL
2021-04-16 23:21:06 +08:00
elif values[0] == 'd':
opacity = float(values[1])
elif values[0] == 'map_Kd':
texture_name = values[1]
except BaseException as ex:
raise ModelDecodeException('Parsing error in {0}.'.format((filename, ex)))
file.close()
for item in (diffuse, ambient, specular, emission):
item.append(opacity)
2021-04-17 01:14:38 +08:00
matlib[name] = Material(name, diffuse, ambient, specular, emission, shininess, texture_name)
return matlib
2021-04-16 23:21:06 +08:00
def parse_obj_file(filename, file=None):
materials = {}
mesh_list = []
location = os.path.dirname(filename)
2022-06-04 11:08:30 +08:00
try:
if file is None:
with open(filename, 'r') as f:
file_contents = f.read()
else:
file_contents = asstr(file.read())
except (UnicodeDecodeError, OSError):
raise ModelDecodeException
2021-04-16 23:21:06 +08:00
material = None
mesh = None
vertices = [[0., 0., 0.]]
normals = [[0., 0., 0.]]
tex_coords = [[0., 0.]]
diffuse = [1.0, 1.0, 1.0, 1.0]
ambient = [1.0, 1.0, 1.0, 1.0]
specular = [1.0, 1.0, 1.0, 1.0]
emission = [0.0, 0.0, 0.0, 1.0]
shininess = 100.0
default_material = Material("Default", diffuse, ambient, specular, emission, shininess)
for line in file_contents.splitlines():
if line.startswith('#'):
continue
values = line.split()
if not values:
continue
if values[0] == 'v':
vertices.append(list(map(float, values[1:4])))
elif values[0] == 'vn':
normals.append(list(map(float, values[1:4])))
elif values[0] == 'vt':
tex_coords.append(list(map(float, values[1:3])))
elif values[0] == 'mtllib':
material_abspath = os.path.join(location, values[1])
2021-04-17 01:14:38 +08:00
materials = load_material_library(filename=material_abspath)
2021-04-16 23:21:06 +08:00
elif values[0] in ('usemtl', 'usemat'):
material = materials.get(values[1])
if mesh is not None:
mesh.material = material
elif values[0] == 'o':
mesh = Mesh(name=values[1])
mesh_list.append(mesh)
elif values[0] == 'f':
if mesh is None:
mesh = Mesh(name='')
mesh_list.append(mesh)
if material is None:
material = default_material
if mesh.material is None:
mesh.material = material
# For fan triangulation, remember first and latest vertices
n1 = None
nlast = None
t1 = None
tlast = None
v1 = None
vlast = None
for i, v in enumerate(values[1:]):
v_i, t_i, n_i = (list(map(int, [j or 0 for j in v.split('/')])) + [0, 0])[:3]
if v_i < 0:
v_i += len(vertices) - 1
if t_i < 0:
t_i += len(tex_coords) - 1
if n_i < 0:
n_i += len(normals) - 1
mesh.normals += normals[n_i]
mesh.tex_coords += tex_coords[t_i]
mesh.vertices += vertices[v_i]
if i >= 3:
# Triangulate
mesh.normals += n1 + nlast
mesh.tex_coords += t1 + tlast
mesh.vertices += v1 + vlast
if i == 0:
n1 = normals[n_i]
t1 = tex_coords[t_i]
v1 = vertices[v_i]
nlast = normals[n_i]
tlast = tex_coords[t_i]
vlast = vertices[v_i]
return mesh_list
###################################################
# Decoder definitions start here:
###################################################
class OBJModelDecoder(ModelDecoder):
def get_file_extensions(self):
return ['.obj']
2022-04-08 23:07:41 +08:00
def decode(self, filename, file, batch, group=None):
2021-04-16 23:21:06 +08:00
if not batch:
batch = pyglet.graphics.Batch()
mesh_list = parse_obj_file(filename=filename, file=file)
vertex_lists = []
groups = []
for mesh in mesh_list:
material = mesh.material
2022-03-05 23:10:18 +08:00
count = len(mesh.vertices) // 3
2021-04-16 23:21:06 +08:00
if material.texture_name:
2022-03-05 23:10:18 +08:00
program = pyglet.model.get_default_textured_shader()
2022-03-22 23:20:07 +08:00
texture = pyglet.resource.texture(material.texture_name)
2022-04-08 23:07:41 +08:00
matgroup = TexturedMaterialGroup(material, program, texture, parent=group)
vertex_lists.append(program.vertex_list(count, GL_TRIANGLES, batch, matgroup,
2022-03-05 23:10:18 +08:00
vertices=('f', mesh.vertices),
normals=('f', mesh.normals),
tex_coords=('f', mesh.tex_coords),
colors=('f', material.diffuse * count)))
2021-04-16 23:21:06 +08:00
else:
2022-03-05 23:10:18 +08:00
program = pyglet.model.get_default_shader()
2022-04-08 23:07:41 +08:00
matgroup = MaterialGroup(material, program, parent=group)
vertex_lists.append(program.vertex_list(count, GL_TRIANGLES, batch, matgroup,
2022-03-05 23:10:18 +08:00
vertices=('f', mesh.vertices),
normals=('f', mesh.normals),
colors=('f', material.diffuse * count)))
2022-04-08 23:07:41 +08:00
groups.append(matgroup)
2021-04-16 23:21:06 +08:00
return Model(vertex_lists=vertex_lists, groups=groups, batch=batch)
def get_decoders():
return [OBJModelDecoder()]
def get_encoders():
return []