jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/objloader.py (153 lines of code) (raw):
"""
The MIT License (MIT)
Copyright (c) 2013 Niko Skrypnik
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.
"""
"""
Loaders for Wavefront format .obj files
=============
"""
from pdb import set_trace as T
import os
from .loader import BaseLoader
from kivy.core.image import Image
from kivy3 import Object3D, Mesh, Material, Vector2
from kivy3.core.geometry import Geometry
from kivy3.core.face3 import Face3
class WaveObject(object):
""" This class contains top level mesh object information like vertices,
normals, texcoords and faces
"""
_mtl_map = {"Ka": "color", "Kd": "diffuse", "Ks": "specular",
"Ns": "shininess", "Tr": "transparency",
"d": "transparency", "map_Kd": "map"
}
def __init__(self, loader, name=''):
self.name = name
self.faces = []
self.loader = loader
self.mtl_name = None
def convert_to_mesh(self, vertex_format=None):
"""Converts data gotten from the .obj definition
file and create Kivy3 Mesh object which may be used
for drawing object in the scene
"""
geometry = Geometry()
material = Material()
mtl_dirname = os.path.abspath(os.path.dirname(self.loader.mtl_source))
v_idx = 0
# create geometry for mesh
for f in self.faces:
verts = f[0]
norms = f[1]
tcs = f[2]
face3 = Face3(0, 0, 0)
for i, e in enumerate(['a', 'b', 'c']):
#get normal components
n = (0.0, 0.0, 0.0)
if norms[i] != -1:
n = self.loader.normals[norms[i] - 1]
face3.vertex_normals.append(n)
#get vertex components
v = self.loader.vertices[verts[i] - 1]
geometry.vertices.append(v)
setattr(face3, e, v_idx)
v_idx += 1
#get texture coordinate components
t = (0.0, 0.0)
if tcs[i] != -1:
t = self.loader.texcoords[tcs[i] - 1]
tc = Vector2(t[0], 1. - t[1])
geometry.face_vertex_uvs[0].append(tc)
geometry.faces.append(face3)
# apply material for object
if self.mtl_name in self.loader.mtl_contents:
raw_material = self.loader.mtl_contents[self.mtl_name]
for k, v in raw_material.items():
_k = self._mtl_map.get(k, None)
if k in ["map_Kd", ]:
map_path = os.path.join(mtl_dirname, v[0])
tex = Image(map_path).texture
material.map = tex
continue
if _k:
if len(v) == 1:
v = float(v[0])
if k == 'Tr':
v = 1. - v
setattr(material, _k, v)
else:
v = list(map(lambda x: float(x), v))
setattr(material, _k, v)
mesh = Mesh(geometry, material)
return mesh
class OBJLoader(BaseLoader):
def __init__(self, **kw):
super(OBJLoader, self).__init__(**kw)
self.mtl_source = None # source of MTL
self.mtl_contents = {} # should be filled in load_mtl
def load_mtl(self):
if not os.path.exists(self.mtl_source):
#TODO show warning about materials file is not found
return
for line in open(self.mtl_source, "r"):
if line.startswith('#'):
continue
values = line.split()
if not values:
continue
if values[0] == 'newmtl':
mtl = self.mtl_contents[values[1]] = {}
continue
elif mtl is None:
raise ValueError("mtl doesn't start with newmtl statement")
mtl[values[0]] = values[1:]
def _load_meshes(self):
wvobj = WaveObject(self)
self.vertices = []
self.normals = []
self.texcoords = []
faces_section = False
for line in open(self.source, "r"):
if line.startswith('#'):
continue
if line.startswith('s'):
continue
values = line.split()
if not values:
continue
if values[0] == 'o' or values[0] == 'g':
wvobj.name = values[1]
elif values[0] == 'mtllib':
if not self.mtl_source:
_obj_dir = os.path.abspath(os.path.dirname(self.source))
self.mtl_source = os.path.join(_obj_dir, values[1])
self.load_mtl()
elif values[0] == 'usemtl':
wvobj.mtl_name = values[1]
elif values[0] == 'v':
if faces_section:
# here we yield new mesh object
faces_section = False
yield wvobj
wvobj = WaveObject(self)
v = list(map(float, values[1:4]))
if self.swapyz:
v = v[0], v[2], v[1]
self.vertices.append(v)
elif values[0] == 'vn':
v = list(map(float, values[1:4]))
if self.swapyz:
v = v[0], v[2], v[1]
self.normals.append(v)
elif values[0] == 'vt':
self.texcoords.append(list(map(float, values[1:3])))
elif values[0] == 'f':
if not faces_section:
faces_section = True
face = []
texcoords = []
norms = []
for v in values[1:]:
w = v.split('/')
face.append(int(w[0]))
if len(w) >= 2 and len(w[1]) > 0:
texcoords.append(int(w[1]))
else:
texcoords.append(-1)
if len(w) >= 3 and len(w[2]) > 0:
norms.append(int(w[2]))
else:
norms.append(-1)
wvobj.faces.append((face, norms, texcoords))
yield wvobj
def load(self, source, **kw):
self.swapyz = kw.pop("swapyz", False)
return super(OBJLoader, self).load(source, **kw)
def parse(self):
obj = Object3D()
for wvobj in self._load_meshes():
obj.add(wvobj.convert_to_mesh())
return obj
class OBJMTLLoader(OBJLoader):
""" This subclass of Wafefront format files loader
which allows to use custom MTL file, but not the one is
defined in .obj file
"""
def load(self, source, mtl_source, **kw):
self.mtl_source = mtl_source
self.load_mtl()
return super(OBJMTLLoader, self).load(source, **kw)