from __future__ import absolute_import, division, print_function, unicode_literals
import sys
import re, os
import pi3d
from random import randint
from six_mod.moves import xrange
from six_mod import advance_iterator
from pi3d.Texture import Texture
from pi3d.Buffer import Buffer
import logging
LOGGER = logging.getLogger(__name__)
[docs]class vertex():
def __init__(self, coords_in, UVcoords_in, normal_in):
self.coords = coords_in
self.UVcoords = UVcoords_in
# self.UVtangent = UVtangent_in
# self.UVbinormal = UVbinormal_in
self.normal = normal_in
[docs]class polygon():
def __init__(self, normal_in, rgba_in, MRef_in, TRef_in, vertexRef_in, vpkey_in):
self.normal = [] #should always be three
for nVal in normal_in:
self.normal.append(nVal)
self.rgba = [] #should always be four
for rgbVal in rgba_in:
self.rgba.append(rgbVal)
self.MRef = MRef_in
self.TRef = TRef_in
self.vref = [] # variable number of indices
for v in vertexRef_in:
self.vref.append(v)
self.vpKey = vpkey_in
[docs]def loadFileEGG(model, fileName):
"""Loads an panda3d egg file to produce Buffer object
as part of a Shape.
Arguments:
*model*
Model object to add to.
*fileName*
Path and name of egg file relative to program file.
"""
model.coordinateSystem = "Y-up"
model.materialList = {}
model.textureList = {}
model.vertexGroupList = {}
model.vertexList = []
model.polygonList = []
model.childModelList = []
model.parentModel = 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
if fileName[0] != '/':
import os
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')
l = f.read() # whole thing as a string in memory this will only work for reasonably small files!!!
############### function to parse file as one off process to avoid re-traversing string #########
# convertes the '<a> b { c <d> e {f} <g> h {i} }' structure
# into nested arrays ['a', 'b', 'c',[['d','e','',['','','f',[]]],['g','h','',['','','i',[]]]]]
def pRec(x, bReg, l, i):
while 1:
try:
nxtFind = advance_iterator(bReg)
j = nxtFind.start()
except:
return i+1
c = l[j]
if c == "<": # add entry to array at this level
if len(x[3]) == 0: x[2] = l[i:j].strip() # text after "{" and before "<Tabxyz>"
i = j+1 # save marker for start of descriptor
x[3].append(["", "", "", []])
elif c == "{":
xn = x[3][len(x[3])-1]
tx = l[i-1:j].strip().split()
xn[0] = tx[0] #x[0] & x[1] is the "<Tabxyz>" & "123" prior to "{"
xn[1] = tx[1] if len(tx) > 1 else ""
i = pRec(xn, bReg, l, j+1)
else: #i.e. c="}" # go up one level of recursion
if len(x[3]) == 0: x[2] = l[i:j].strip()
return j+1
################### end of pRec #################
####### go through all the nested <Groups> ####################
def groupDrill(gp, np):
structVList = {}
offsetVList = {}
structPList = []
offset = 0
#numv = 0
#numi = 0
for x in gp:
if len(x) == 0: continue
if ("<Group>" in x[0]):
if len(x[1]) > 0:
nextnp = np+x[1]
else:
nextnp = np+str(randint(10000, 99999))
groupDrill(x[3], nextnp)
else:
#build vertex, polygon, normal, triangles, UVs etc etc
if "<VertexPool>" in x[0]:
vp = x[1]
structVList[vp] = []
offsetVList[vp] = offset
for v in x[3]:
#if "<Vertex>" in v[0]: #try with this test first!
coords = [float(n) for n in v[2].strip().split()] # before first < error if no coords!
# texture mapping
UVcoords = []
normal = []
for u in v[3]:
if "<UV>" in u[0]: UVcoords = [float(n) for n in u[2].strip().split()]
#TODO get UVtangent and UVbinormal out of UVset (and use them!)
# if ("<Tangent>" in vList[v]): UVtangent = [float(n) for n in (extBracket("<Tangent>", vList[v]))[0].split()] # not sure how to use this at this stage
#else: UVtangent = []
# if ("<Binormal>" in vList[v]): UVbinormal = [float(n) for n in (extBracket("<Binormal>", vList[v]))[0].split()] # not sure how to use this at this stage
#else: UVbinormal = []
# normals, used in 'smoothing' edges between polygons
if "<Normal>" in u[0]: normal = [float(n) for n in u[2].strip().split()]
vInt = int(v[1])
while (len(structVList[vp]) < (vInt+1)): structVList[vp].append("")
structVList[vp][vInt] = (vertex(coords, UVcoords, normal))
offset += 1
#
# now go through splitting out the Polygons from this Group same level as vertexGroup
if "<Polygon>" in x[0]:
normal = []
rgba = []
MRef = ""
TRef = ""
for p in x[3]:
if ("<Normal>" in p[0]): normal = [float(n) for n in p[2].strip().split()]
if ("<RGBA>" in p[0]): rgba = [float(n) for n in p[2].strip().split()]
if ("<MRef>" in p[0]): MRef = p[2].strip()
if ("<TRef>" in p[0]): TRef = p[2].strip()
if ("<VertexRef>" in p[0]):
vref = []
for n in p[2].strip().split():
vref.append(int(n))
#numv += 1
#numi += 3
#numi -= 6 # number of corners of triangle = (n-2)*3 where n is the number of corners of face
vpKey = p[3][0][2].strip() # ought to do a for r in p[3]; if "Ref in...
# add to list
#while (len(structPList) < (p+1)): structPList.append("")
#
structPList.append(polygon(normal, rgba, MRef, TRef, vref, vpKey))
# now go through the polygons in order of vertexPool+id, trying to ensure that the polygon arrays in each group are built in the order of vertexPool names
# only cope with one material and one texture per group
#numv -= 1
#numi -= 1
g_vertices = []
g_normals = []
g_tex_coords = []
g_indices = []
nv = 0 # vertex counter in this material
#ni = 0 # triangle vertex count in this material
gMRef = ""
gTRef = ""
nP = len(structPList)
for p in xrange(nP):
if (len(structPList[p].MRef) > 0): gMRef = structPList[p].MRef
else: gMRef = ""
if (len(structPList[p].TRef) > 0): gTRef = structPList[p].TRef
else: gTRef = ""
vpKey = structPList[p].vpKey
vref = structPList[p].vref
startV = nv
for j in vref:
if (len(structVList[vpKey][j].normal) > 0): model.vNormal = True
else: model.vNormal = False
if model.coordinateSystem == "z-up":
thisV = [structVList[vpKey][j].coords[1], structVList[vpKey][j].coords[2], -structVList[vpKey][j].coords[0]]
if model.vNormal:
thisN = [structVList[vpKey][j].normal[1], structVList[vpKey][j].normal[2], -structVList[vpKey][j].normal[0]]
else:
thisV = [structVList[vpKey][j].coords[0], structVList[vpKey][j].coords[1], -structVList[vpKey][j].coords[2]]
if model.vNormal:
thisN = [structVList[vpKey][j].normal[0], structVList[vpKey][j].normal[1], -structVList[vpKey][j].normal[2]]
g_vertices.append(thisV)
if model.vNormal: nml = thisN
else: nml = structPList[p].normal
g_normals.append(nml)
uvc = structVList[vpKey][j].UVcoords
if (len(uvc) == 2):
g_tex_coords.append(uvc)
else:
g_tex_coords.append([0.0, 0.0])
nv += 1
n = nv - startV - 1
for j in range(1, n):
g_indices.append((startV, startV + j + 1, startV + j))
ilen = len(g_vertices)
if ilen > 0:
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[np] = n
model.buf[n].indicesLen = ilen
model.buf[n].material = (0.0, 0.0, 0.0, 0.0)
model.buf[n].ttype = pi3d.constants.GL_TRIANGLES
# load the texture file TODO check if same as previously loaded files (for other loadModel()s)
if (gTRef in model.textureList):
model.buf[model.vGroup[np]].textures = [model.textureList[gTRef]["texID"]]
model.buf[model.vGroup[np]].texFile = model.textureList[gTRef]["filename"]
else:
model.buf[model.vGroup[np]].textures = []
model.buf[model.vGroup[np]].texFile = None
#TODO don't create this array if texture being used but should be able to combine
if (gMRef in model.materialList):
redVal = float(model.materialList[gMRef]["diffr"])
grnVal = float(model.materialList[gMRef]["diffg"])
bluVal = float(model.materialList[gMRef]["diffb"])
model.buf[model.vGroup[np]].material = (redVal, grnVal, bluVal, 1.0)
model.buf[model.vGroup[np]].unib[3:6] = [redVal, grnVal, bluVal]
else: model.buf[model.vGroup[np]].material = (0.0, 0.0, 0.0, 0.0)
####### end of groupDrill function #####################
bReg = re.finditer('[{}<]', l)
xx = ["", "", "", []]
pRec(xx, bReg, l, 0)
l = None #in case it's running out of memory?
f.close()
for x in xx[3]:
if "<Texture>" in x[0]:
model.textureList[x[1]] = {}
for i in xrange(len(x[3])): model.textureList[x[1]][x[3][i][1]] = x[3][i][2]
model.textureList[x[1]]["filename"] = x[2].strip("\"")
LOGGER.debug('path: %s, filename:%s', filePath, model.textureList[x[1]]["filename"])
model.textureList[x[1]]["texID"] = Texture(os.path.join(filePath, model.textureList[x[1]]["filename"]), False, True) # load from file
if "<CoordinateSystem>" in x[0]:
model.coordinateSystem = x[2].lower()
if "<Material>" in x[0]:
model.materialList[x[1]] = {}
for i in xrange(len(x[3])): model.materialList[x[1]][x[3][i][1]] = x[3][i][2]
if "<Group>" in x[0]:
groupDrill(x[3], x[1])