root/trunk/RBRapier/RBRapier/Formats/Lightwave/Loader.py

Revision 379, 9.9 kB (checked in by sholloway, 5 years ago)

*** empty log message ***

Line 
1 #!/usr/bin/env python
2 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 ##~ License
4 ##~
5 ##- The RuneBlade Foundation library is intended to ease some
6 ##- aspects of writing intricate Jabber, XML, and User Interface (wxPython, etc.)
7 ##- applications, while providing the flexibility to modularly change the
8 ##- architecture. Enjoy.
9 ##~
10 ##~ Copyright (C) 2002  TechGame Networks, LLC.
11 ##~
12 ##~ This library is free software; you can redistribute it and/or
13 ##~ modify it under the terms of the BSD style License as found in the
14 ##~ LICENSE file included with this distribution.
15 ##~
16 ##~ TechGame Networks, LLC can be reached at:
17 ##~ 3578 E. Hartsel Drive #211
18 ##~ Colorado Springs, Colorado, USA, 80920
19 ##~
20 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
22 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 #~ Imports
24 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25
26 import os
27 import struct
28 import chunk
29
30 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 #~ Definitions
32 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33
34 class _LWOSubChunk(chunk.Chunk):
35     """Adapt python's chunk.Chunk class to be able to read LWO sub-chunks..."""
36     def __init__(self, file, align = 1, bigendian = 1, inclheader = 0):
37         import struct
38         self.closed = 0
39         self.align = align      # whether to align to word (2-byte) boundaries
40         if bigendian:
41             strflag = '>'
42         else:
43             strflag = '<'
44         self.file = file
45         self.chunkname = file.read(4)
46         if len(self.chunkname) < 4:
47             raise EOFError
48         try:
49             self.chunksize = struct.unpack(strflag+'h', file.read(2))[0]
50         except struct.error:
51             raise EOFError
52         if inclheader:
53             self.chunksize = self.chunksize - 6 # subtract header
54         self.size_read = 0
55         try:
56             self.offset = self.file.tell()
57         except (AttributeError, IOError):
58             self.seekable = 0
59         else:
60             self.seekable = 1
61
62 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
63
64 def _ReadChunkString(chunk, readstep=2):
65     result = ''
66     while result[-1:] != '\0':
67         result += chunk.read(readstep)
68     result = result[:result.find('\0')]
69     return result
70
71 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72
73 class _LWOSurface(object):
74     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75     #~ Constants / Variables / Etc.
76     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
77
78     #BaseColor = (255, 255, 255, 0)
79     #Flag = 0
80
81     #iLuminous = 0
82     #iDiffuse = 0
83     #iSpecular = 0
84     #iReflection = 0
85     #iTransparency = 0
86
87     #Luminous = 0.
88     #Diffuse = 0.
89     #Specular = 0.
90     #Reflection = 0.
91     #Transparency = 0.
92
93     #Shininess = 1
94
95     #ReflectiveMode = 3
96     #ReflectiveImage = ''
97     #ReflectiveSeamAngle = 0.
98     #ReflectiveIndex = 1.
99
100     #EdgeTransparency = 1.
101     #SmoothingAngle = math.pi/2.
102
103     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104     #~ Public Methods
105     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106
107     def __init__(self, Name):
108         self.Name = Name
109         self.Faces = []
110
111     def ReadFormat(self, SurfaceDefChunk, TaskProgress=None):
112         FormatMap = {
113             'COLR': ('BaseColor', '>BBBB'),
114             'FLAG': ('Flag', '>H'),
115
116             'LUMI': ('iLuminous', '>h'),
117             'DIFF': ('iDiffuse', '>h'),
118             'SPEC': ('iSpecular', '>h'),
119             'REFL': ('iReflection', '>h'),
120             'TRAN': ('iTransparency', '>h'),
121
122             'VLUM': ('Luminous', '>f'),
123             'VDIF': ('Diffuse', '>f'),
124             'VSPC': ('Specular', '>f'),
125             'VRFL': ('Reflection', '>f'),
126             'VTRN': ('Transparency', '>f'),
127
128             'GLOS': ('Shininess', '>h'),
129
130             'RFLT': ('ReflectiveMode', '>h'),
131             'RIMG': ('ReflectiveImage', _ReadChunkString),
132             'RSAN': ('ReflectiveSeamAngle', '>f'),
133             'RIND': ('ReflectiveIndex', '>f'),
134
135             'EDGE': ('EdgeTransparency', '>f'),
136             'SMAN': ('SmoothingAngle', '>f'),
137             }
138         try:
139             while 1:
140                 SubChunk = _LWOSubChunk(SurfaceDefChunk)
141                 name, fmt = FormatMap.get(SubChunk.getname(), (None, None))
142                 if isinstance(fmt, str):
143                     value = struct.unpack(fmt, SubChunk.read(SubChunk.getsize()))
144                     if len(value) == 1: value = value[0]
145                 elif callable(fmt):
146                     value = fmt(SubChunk)
147                 else:
148                     print "[%s] UNKNOWN: %s %r" % (self.Name, SubChunk.getname(), SubChunk.read())
149                     SubChunk.skip()
150                     continue
151                 if name:
152                     setattr(self, name, value)
153                     #print name, value
154         except EOFError:
155             pass # end of chunk
156
157         Flag = getattr(self, 'Flag', 0)
158         self.Luminous = Flag & 1; Flag >>= 1
159         self.Outline = Flag & 1; Flag >>= 1
160         self.Smoothing = Flag & 1; Flag >>= 1
161         self.ColorHighlights = Flag & 1; Flag >>= 1
162         self.ColorFilter = Flag & 1; Flag >>= 1
163         self.OpaqueEdge = Flag & 1; Flag >>= 1
164         self.TransparentEdge = Flag & 1; Flag >>= 1
165         self.SharpTerminator = Flag & 1; Flag >>= 1
166         self.DoubleSided = Flag & 1; Flag >>= 1
167         self.Additive = Flag & 1; Flag >>= 1
168         self.ShadowAlpha = Flag & 1; Flag >>= 1
169
170 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
171
172 class _LWOFace(object):
173     def ReadFormat(self, FacesChunk, TaskProgress=None):
174         NumIndices = struct.unpack(">H", FacesChunk.read(2))[0]
175         self.IndexList = [struct.unpack(">H", FacesChunk.read(2))[0] for each in xrange(NumIndices)]
176         self.SurfaceIndex = struct.unpack(">h", FacesChunk.read(2))[0]
177         ByteLen = len(self.IndexList)*2 + 4
178         if self.SurfaceIndex < 0:
179             self.SurfaceIndex = -self.SurfaceIndex
180             # This polygon has "detail polygons", but the reference says to ignore them
181             # as they are mostly obsolete
182             NumDetailPolys = struct.unpack(">H", FacesChunk.read(2))[0]
183             ByteLen += 2
184             # we exit at this point so the detail polys are handled as normal polys... call me lazy!
185         self.SurfaceIndex -= 1 # Make index zero based
186         return ByteLen
187
188 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
189
190 class LightwaveLWOBLoader(object):
191     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
192     #~ Constants / Variables / Etc.
193     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
194
195     RaiseMissingElements = 0
196     WarnMissingElements = 0
197
198     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
199     #~ Public Methods
200     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
201
202     def ReadFormat(self, LightwaveLWOFile, TaskProgress=None):
203         self.Name = None
204         self.Vertices = []
205         self.Surfaces = {}
206
207         ChunkTypeTable = {
208             'SRFS': self._ReadSurfaces,
209             'PNTS': self._ReadPoints,
210             'POLS': self._ReadFaces,
211             'SURF': self._ReadSurfaceDefinition,
212             }
213
214         self._FilePath = os.path.split(LightwaveLWOFile.name)[0]
215         FORMChunk = chunk.Chunk(LightwaveLWOFile)
216         if FORMChunk.getname() != 'FORM':
217             raise ValueError, "File does not contain a FORM chunk to contain a Lightwave3D Object"
218
219         if TaskProgress:
220             TaskProgress = TaskProgress.NewSubtask('Load Lightwave Object', FORMChunk.tell(), FORMChunk.getsize())
221         else: TaskProgress = None
222
223         FormType = FORMChunk.read(4)
224         if FormType == 'LWOB':
225             pass # This is what we're looking for
226         elif FormType != 'LWO2':
227             raise NotImplemented, "File contains a Lightwave3D 2 Object (LWO2), which is not (yet) supported."
228         else: raise ValueError, "File does not contain a Lightwave3D Object"
229
230         try:
231             while 1:
232                 NextChunk = chunk.Chunk(FORMChunk)
233                 try:
234                     ex = ChunkTypeTable[NextChunk.getname()]
235                 except KeyError:
236                     if self.RaiseMissingElements:
237                         raise NotImplemented, "Unknown LightwaveOBJ chunk type '%s'" % NextChunk.getname()
238                     elif self.WarnMissingElements:
239                         print "Unknown LightwaveOBJ chunk type '%s'" % NextChunk.getname()
240                     NextChunk.skip()
241                 else:
242                     ex(NextChunk)
243                 if TaskProgress:
244                     TaskProgress.Progress = FORMChunk.tell()
245         except EOFError:
246             pass # we're done
247
248     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
249     #~ Protected Methods
250     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
251
252     def _ReadSurfaces(self, SurfacesChunk):
253         self.SurfaceNames = filter(None, SurfacesChunk.read().split('\0'))
254         self.Surfaces = dict([(name, _LWOSurface(name)) for name in self.SurfaceNames])
255
256     def _ReadPoints(self, PointsChunk):
257         NumPoints = PointsChunk.getsize() / 12
258         self.Vertices = [struct.unpack(">3f", PointsChunk.read(12)) for idx in xrange(NumPoints)]
259
260     def _ReadFaces(self, FacesChunk):
261         BytesLeft = FacesChunk.getsize()
262         while BytesLeft > 0:
263             face = _LWOFace()
264             BytesConsumed = face.ReadFormat(FacesChunk)
265             FaceSurfaceName = self.SurfaceNames[face.SurfaceIndex-1]
266             self.Surfaces[FaceSurfaceName].Faces.append(face)
267             BytesLeft -= BytesConsumed
268         assert BytesLeft == 0
269
270     def _ReadSurfaceDefinition(self, SurfaceDefChunk):
271         SurfaceName = _ReadChunkString(SurfaceDefChunk)
272         self.Surfaces[SurfaceName].ReadFormat(SurfaceDefChunk)
273
274 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
275 #~ Testing
276 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
277
278 if __name__=='__main__':
279     print "Testing..."
280     test = LightwaveLWOLoader()
281     #test.ReadFormat(open('data/ki162a.lwo', 'rb'))
282     test.ReadFormat(open('data/fi110a.lwo', 'rb'))
283     print "Test complete."
284
285
Note: See TracBrowser for help on using the browser.