from __future__ import absolute_import, division, print_function, unicode_literals
import sys
import os
from pi3d.constants import GL_TRIANGLES
from pi3d.loader.parse_mtl import parse_mtl
from pi3d.Texture import Texture
from pi3d.Buffer import Buffer
import logging
LOGGER = logging.getLogger(__name__)
#########################################################################################
#
# this block added by paddy gaunt 22 August 2012
# Copyright (c) Paddy Gaunt, 2012
# Chunks of this code are based on https://github.com/mrdoob/three.js/ by
# AlteredQualia http://alteredqualia.com
#
#########################################################################################
#########################################################################################
[docs]def parse_vertex(text):
"""Parse text chunk specifying single vertex.
Possible formats:
* vertex index
* vertex index / texture index
* vertex index / texture index / normal index
* vertex index / / normal index
"""
v = 0
t = 0
n = 0
chunks = text.split("/")
v = int(chunks[0])
if len(chunks) > 1:
if chunks[1]:
t = int(chunks[1])
if len(chunks) > 2:
if chunks[2]:
n = int(chunks[2])
return { 'v':v, 't':t, 'n':n }
#########################################################################################
[docs]def loadFileOBJ(model, fileName):
"""Loads an obj file with associated mtl file to produce Buffer object
as part of a Shape. Arguments:
*model*
Model object to add to.
*fileName*
Path and name of obj file relative to program file.
"""
model.coordinateSystem = "Y-up"
model.parent = None
#model.childModel = [] # don't really need parent and child pointers but will speed up traversing tree
model.vNormal = False
model.vGroup = {} # holds the information for each vertex group
# read in the file and parse into some arrays, name='teapot', z=4
#import os
if fileName[0] != '/':
for p in sys.path:
if os.path.isfile(p + '/' + fileName): # this could theoretically get different files with same name
fileName = p + '/' + fileName
break
filePath = os.path.split(os.path.abspath(fileName))[0]
LOGGER.debug(filePath)
f = open(fileName, 'r')
vertices = []
normals = []
uvs = []
faces = {}
materials = {}
material = ""
mcounter = 0
mcurrent = 0
mtllib = ""
# current face state
group = 0
objct = 0
smooth = 0
for l in f:
chunks = l.split()
if len(chunks) > 0:
# Vertices as (x,y,z) coordinates
# v 0.123 0.234 0.345
if chunks[0] == "v" and len(chunks) >= 4:
x = float(chunks[1])
y = float(chunks[2])
z = -float(chunks[3]) # z direction away in gl es 2.0 shaders
vertices.append((x, y, z))
# Normals in (x, y, z) form; normals might not be unit
# vn 0.707 0.000 0.707
if chunks[0] == "vn" and len(chunks) >= 4:
x = float(chunks[1])
y = float(chunks[2])
z = -float(chunks[3]) # z direction away in gl es 2.0 shaders
normals.append((x, y, z))
# Texture coordinates in (u,v)
# vt 0.500 -1.352
if chunks[0] == "vt" and len(chunks) >= 3:
u = float(chunks[1])
v = float(chunks[2])
uvs.append((u, v))
# Face
if chunks[0] == "f" and len(chunks) >= 4:
vertex_index = []
uv_index = []
normal_index = []
# Precompute vert / normal / uv lists
# for negative index lookup
vertlen = len(vertices) + 1
normlen = len(normals) + 1
uvlen = len(uvs) + 1
for v in chunks[1:]:
vertex = parse_vertex(v)
if vertex['v']:
if vertex['v'] < 0:
vertex['v'] += vertlen
vertex_index.append(vertex['v'])
if vertex['t']:
if vertex['t'] < 0:
vertex['t'] += uvlen
uv_index.append(vertex['t'])
if vertex['n']:
if vertex['n'] < 0:
vertex['n'] += normlen
normal_index.append(vertex['n'])
if not mcurrent in faces:
faces[mcurrent] = []
faces[mcurrent].append({
'vertex':vertex_index,
'uv':uv_index,
'normal':normal_index,
'group':group,
'object':objct,
'smooth':smooth,
})
# Group
if chunks[0] == "g" and len(chunks) == 2:
group = chunks[1]
# Object
if chunks[0] == "o" and len(chunks) == 2:
objct = chunks[1]
# Materials definition
if chunks[0] == "mtllib" and len(chunks) == 2:
mtllib = chunks[1]
# Material
if chunks[0] == "usemtl":
if len(chunks) > 1:
material = chunks[1]
else:
material = ""
if not material in materials:
mcurrent = mcounter
materials[material] = mcounter
mcounter += 1
else:
mcurrent = materials[material]
# Smooth shading
if chunks[0] == "s" and len(chunks) == 2:
smooth = chunks[1]
for g in faces: # make each of these into an array_buffer with its own material
g_vertices = []
g_normals = []
g_tex_coords = []
g_indices = []
i = 0 # vertex counter in this material
LOGGER.debug("len uv={}".format(len(vertices)))
vec_dict = {} # hold unique combinations of v/u/n
for f in faces[g]:
vec_list = [] # hold index vals for each array_buffer entry for this face
length = len(f['vertex'])
length_n = len(f['normal'])
length_uv = len(f['uv'])
for v in range(length):
vec_tuple = (f['vertex'][v],
f['uv'][v] if length_uv > 0 else -1,
f['normal'][v] if length_n == length else -1)
if vec_tuple in vec_dict: #already exists don't duplicate
vec_list.append(vec_dict[vec_tuple])
else:
g_vertices.append(vertices[vec_tuple[0] - 1])
if length_n == length: #only use normals if there is one for each vertex
g_normals.append(normals[vec_tuple[2] - 1])
if (length_uv > 0 and len(uvs[vec_tuple[1] - 1]) == 2):
g_tex_coords.append(uvs[vec_tuple[1] - 1])
vec_dict[vec_tuple] = i
vec_list.append(i)
i += 1
for t in range(len(vec_list) - 2):
g_indices.append((vec_list[0], vec_list[t + 2], vec_list[t + 1]))
if len(g_normals) != len(g_vertices):
g_normals = None # force Buffer.__init__() to generate normals
model.buf.append(Buffer(model, g_vertices, g_tex_coords, g_indices, g_normals))
n = len(model.buf) - 1
model.vGroup[g] = n
model.buf[n].indicesLen = len(model.buf[n].element_array_buffer)
model.buf[n].material = (0.0, 0.0, 0.0, 0.0)
model.buf[n].draw_method = GL_TRIANGLES
LOGGER.debug("indices=%s\nvertices=%s", len(model.buf[n].element_array_buffer),
len(model.buf[n].array_buffer))
try:
material_lib = parse_mtl(open(os.path.join(filePath, mtllib), 'r'))
for m in materials:
LOGGER.debug(m)
if 'mapDiffuse' in material_lib[m]:
tfileName = material_lib[m]['mapDiffuse']
model.buf[model.vGroup[materials[m]]].texFile = tfileName
model.buf[model.vGroup[materials[m]]].textures = [Texture(filePath + '/' + tfileName, blend=False, flip=True)] # load from file
else:
model.buf[model.vGroup[materials[m]]].texFile = None
model.buf[model.vGroup[materials[m]]].textures = []
if 'colorDiffuse' in material_lib[m]:#TODO don't create this array if texture being used though not exclusive.
#TODO check this works with appropriate mtl file
redVal = material_lib[m]['colorDiffuse'][0]
grnVal = material_lib[m]['colorDiffuse'][1]
bluVal = material_lib[m]['colorDiffuse'][2]
model.buf[model.vGroup[materials[m]]].material = (redVal, grnVal, bluVal, 1.0)
model.buf[model.vGroup[materials[m]]].unib[3:6] = [redVal, grnVal, bluVal]
except:
LOGGER.warning('no material specified')