'''
threeDS.py - Python Module for loading and displaying 3DStudio format modules
Copyright (C) 2000-2001 Jason Petrone <jp@demonseed.net>
The 3DS format is very robust, and much of it is not implemented in this
module. Currently, 3DS geometry, materials(including textures), and undirected
lights are supported. Keyframing, directed lighting, and everything else is
currently unimplemented.
'''
__version__ = '0.3.1'
__author__ = 'Jason Petrone <jp@demonseed.net>'
DEBUG = 0
from struct import *
import os, cStringIO, math, copy
from OpenGL.GL import *
from Numeric import *
dispListCnt = 0
# generic chunks used throughout
RGB1 = 0x0010 # 3 floats of RGB
RGB2 = 0x0011 # 3 bytes of RGB
IPERCENT = 0x0030 # integer percentage
FPERCENT = 0x0031 # float percentage
OBJMESH = 0x3D3D
SMOOTH_GROUP = 0x4150
# geometry chunks
OBJBLOCK = 0x4000
TRIMESH = 0x4100
VERTLIST = 0x4110
FACELIST = 0x4120
FACEMAT = 0x4130
MAPLIST = 0x4140
TRMATRIX = 0x4160
LIGHT = 0x4600
SPOTLIGHT = 0x4610
CAMERA = 0x4700
MAIN = 0x4D4D
# material chunks
AMBIENT = 0xA010
DIFFUSE = 0xA020
SPECULAR = 0xA030
SHININESS = 0xA040
TRANSPARENCY = 0xA050
DOUBLESIDED = 0xA081
TEXTURE = 0xA200
MATNAME = 0xA000
MAT_MAPNAME = 0xA300
MATMAPTILING = 0xA351
TEXBLUR = 0xA353
MAT_MAP_USCALE = 0xA354
MAT_MAP_VSCALE = 0xA356
MATERIAL = 0xAFFF
KEYFRAMER = 0xB000
# global textures(dont want to load them more than once!)
tex_data = {}
def readStr(f):
txt = ""
[c] = unpack('c', f.read(1) )
while c != '\0':
txt = txt + c
[c] = unpack('c', f.read(1) )
return txt
def readHdr(f):
[hdr] = unpack( '<H', f.read(2) )
[size] = unpack( '<I', f.read(4) )
return (hdr, size)
class TexImage:
data = None
sizex = 0
sizey = 0
class Texture:
def __init__(self, f, myLen):
self.imgfile = None
self.texId = -1
while f.tell() < myLen:
[hdr, size] = readHdr(f)
if hdr == MAT_MAPNAME:
global tex_data
self.imgfile = readStr(f)
if not tex_data.has_key(self.imgfile):
try:
from PIL import Image
img = Image.open(self.imgfile)
except IOError, e:
print 'Cannot load '+self.imgfile+': '+ str(e)
self.imgfile = None
continue
img = img.convert('RGBA', colors=32)
# texture size must be aligned by 2^n
w = 0; h = 0
[w, h] = img.size
power = 2
while w > power:
power = power * 2
w = power
power = 2
while h > power:
power = power * 2
h = power
img = img.resize((w, h))
# now create storage class
teximg = TexImage()
teximg.sizex, teximg.sizey = w, h
teximg.data = img.tostring('raw', 'RGBA', 0, -1)
# outfile = self.imgfile + '.png'
# img.save(outfile, "PNG")
tex_data[self.imgfile] = teximg
elif hdr == IPERCENT:
[self.percent] = unpack( '<h', f.read(2) )
elif hdr == FPERCENT:
[self.percent] = unpack( '<f', f.read(4) )
elif hdr == MATMAPTILING:
# not sure what to do with this
[self.tiling] = unpack( '<h', f.read(2) )
elif hdr == TEXBLUR:
[self.blur] = unpack( '<f', f.read(4) )
else:
if DEBUG: print 'tex hdr: 0x'+hex(hdr)[2:].upper()
f.read(size - 6)
return None
def load(self):
global tex_data
self.texId = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.texId)
if self.imgfile is None: return
teximg = tex_data[self.imgfile]
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
# glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
# glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
# glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
# glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
# glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
# glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
# glTexImage2D(GL_TEXTURE_2D, 0, 4, teximg.sizex, teximg.sizey, 0,
# GL_RGBA, GL_UNSIGNED_BYTE, teximg.data)
# glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
# glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, 3, teximg.sizex, teximg.sizey, 0,
GL_RGBA, GL_UNSIGNED_BYTE, teximg.data)
def set(self):
if DEBUG: print 'setting texture ' + self.imgfile
if self.texId == -1: self.load()
glBindTexture(GL_TEXTURE_2D, self.texId)
class Material:
"All shading values are stored as floats"
def __init__(self, f = None, myLen = None):
self.texture = None
self.doublesided = None
if f == None:
# set defaults
self.ambient = [1.0, 1.0, 1.0, 1.0]
self.diffuse = [1.0, 1.0, 1.0, 1.0]
self.specular = [1.0, 1.0, 1.0, 1.0]
return
self.ambient = []
self.diffuse = []
self.specular = []
self.shininess = 0
self.transparency = 0
self.name = ""
while f.tell() < myLen:
[hdr, size] = readHdr(f)
if hdr == MATNAME:
self.name = readStr(f)
elif hdr == AMBIENT:
self.ambient = self.readShading(f, f.tell() + size - 6)
self.ambient.append(0.6)
elif hdr == DIFFUSE:
self.diffuse = self.readShading(f, f.tell() + size - 6)
self.diffuse.append(0.8)
elif hdr == SPECULAR:
self.specular = self.readShading(f, f.tell() + size - 6)
self.specular.append(0.5)
elif hdr == SHININESS:
[hdr, size] = readHdr(f)
[self.shininess] = unpack('<H', f.read(2))
elif hdr == TRANSPARENCY:
[hdr, size] = readHdr(f)
[self.transparency] = unpack('<H', f.read(2))
elif hdr == TEXTURE:
self.texture = Texture(f, f.tell() + size - 6)
if DEBUG: print self.name + ': ' + self.texture.imgfile
elif hdr == DOUBLESIDED:
if DEBUG: print 'doublesided material: '+self.name
self.doublesided = 1
else:
# print 'mat hdr: 0x'+hex(hdr)[2:].upper()
f.read(size - 6)
def readShading(self, f, myLen):
while f.tell() < myLen:
[hdr, size] = readHdr(f)
if hdr == RGB1:
return list(unpack('<3f', f.read(12)))
elif hdr == RGB2:
[r, g, b] = unpack( '3B', f.read(3) )
return [ float(r)/256.0, float(g)/256.0, float(b)/256.0 ]
else:
f.read(size - 6)
def set(self):
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, self.ambient)
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, self.diffuse)
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, self.specular)
# glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, self.shininess)
# glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, (self.shininess/100.0)*128)
if self.texture: self.texture.set()
class Light:
color = [0, 0, 0]
pos = [0, 0, 0]
target = None
def __init__(self, name, f, myLen):
self.pos = unpack('<3f', f.read(12))
while f.tell() < myLen:
[hdr, size] = readHdr(f)
if hdr == RGB1:
self.color = unpack( '<3f', f.read(12) )
elif hdr == RGB2:
[r, g, b] = unpack( '3B', f.read(3) )
self.color = [ float(r)/256.0, float(g)/256.0, float(b)/256.0 ]
elif hdr == SPOTLIGHT:
self.target = [0, 0, 0]
self.target = unpack('<3f', f.read(12))
self.hotspot = unpack('<1f', f.read(4))
self.falloff = unpack('<1f', f.re