~etc-pgh-launchpad/wildpockets/trunk

« back to all changes in this revision

Viewing changes to resourceserver/libs/modelexporter3/model.py

  • Committer: etc-pgh-launchpad at cmu
  • Date: 2010-11-30 20:56:30 UTC
  • Revision ID: etc-pgh-launchpad@lists.andrew.cmu.edu-20101130205630-0blbkcz28ovjl8wj
Committing the Wild Pockets code base to Launchpad.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
"""
 
3
Wild Pockets
 
4
 
 
5
Copyright (c) 2010 Carnegie Mellon University
 
6
 
 
7
Permission is hereby granted, free of charge, to any person obtaining a copy
 
8
of this software and associated documentation files (the "Software"), to deal
 
9
in the Software without restriction, including without limitation the rights
 
10
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
11
copies of the Software, and to permit persons to whom the Software is
 
12
furnished to do so, subject to the following conditions:
 
13
 
 
14
The above copyright notice and this permission notice shall be included in
 
15
all copies or substantial portions of the Software.
 
16
 
 
17
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
18
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
19
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
20
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
21
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
22
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
23
THE SOFTWARE.
 
24
 
 
25
"""
 
26
 
 
27
"""
 
28
Data structure constructor for models
 
29
"""
 
30
 
 
31
 
 
32
import solids,importer
 
33
 
 
34
modes={ "standard":0, "wireframe":1, "alphatest":2, }
 
35
 
 
36
class Model(importer.Importer):
 
37
    
 
38
    
 
39
    def __init__(self):
 
40
        self.description=None
 
41
        self.collisions=[]
 
42
        self.bones=[]
 
43
        self.meshes={}
 
44
        self.textures={}
 
45
        self.mass=None
 
46
        self.currentBone=None
 
47
        self.currentMesh=None
 
48
        self.currentMeshName=None
 
49
        self.currentVertex=None
 
50
        self.currentCollisionSphere=None
 
51
        self.currentCollisionBox=None
 
52
        self.physicsFlag=0
 
53
        self.centerOfMass=(0,0,0)
 
54
        self.inertiaRotation=(1,0,0,0)
 
55
        self.inertiaScale=(1,1,1)
 
56
        self.scriptFilename=None
 
57
        self.scriptClassname=None
 
58
 
 
59
    def _checkCurrentBone(self,cmdname):
 
60
        """
 
61
        Raises an error if a command is attempted that isn't valid without
 
62
        a bone
 
63
        """
 
64
        if self.currentBone==None:
 
65
            raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current bone.")
 
66
 
 
67
    def _checkCurrentMesh(self,cmdname):
 
68
        """
 
69
        Raises an error if a command is attempted that isn't valid without
 
70
        a mesh
 
71
        """
 
72
        if self.currentMesh==None:
 
73
            raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current mesh.")
 
74
 
 
75
    def _checkCurrentVertex(self,cmdname):
 
76
        """
 
77
        Raises an error if a command is attempted that isn't valid without a vertex
 
78
        """
 
79
        if self.currentVertex==None:
 
80
            raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current vertex.")
 
81
 
 
82
    def _checkCurrentCSphere(self,cmdname):
 
83
        if self.currentCollisionSphere==None:
 
84
            raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current collision sphere.")
 
85
 
 
86
    def _checkCurrentCBox(self,cmdname):
 
87
        if self.currentCollisionBox==None:
 
88
            raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current collision box.")
 
89
 
 
90
    def _checkCurrentWeight(self,cmdname):
 
91
        if self.currentWeight==None:
 
92
            raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current weight.")
 
93
 
 
94
 
 
95
    def _addCollisionSphere(self,name,position=(0,0,0),radius=1,bone=0):
 
96
        self.currentCollisionSphere={
 
97
            "name":name,
 
98
            "type":"sphere",
 
99
            "position":position,
 
100
            "radius":radius,
 
101
            "bone":bone
 
102
        }
 
103
        self.collisions.append(self.currentCollisionSphere)
 
104
 
 
105
    def _addCollisionBox(self,name,position=(0,0,0),size=(0,0,0),rotation=(1,0,0,0),bone=0):
 
106
        self.currentCollisionBox={
 
107
            "name":name,
 
108
            "type":"box",
 
109
            "position":position,
 
110
            "size":size,
 
111
            "rotation":rotation,
 
112
            "bone":bone
 
113
        }
 
114
        self.collisions.append(self.currentCollisionBox)
 
115
 
 
116
    def _addWeight(self,name,position=(0,0,0),magnitude=1,bone=0):
 
117
        self.currentWeight={
 
118
            "name":name,
 
119
            "position":position,
 
120
            "magnitude":magnitude,
 
121
            "bone":bone
 
122
        }
 
123
        self.m_weights.append(self.currentWeight)
 
124
 
 
125
    def setdescription(self,description):
 
126
        self.description=description
 
127
 
 
128
    def setmass(self,mass):
 
129
        self.mass=float(mass)
 
130
 
 
131
    def addbone(self,boneName,boneParent):
 
132
        """
 
133
        Adds a bone to the model
 
134
        """
 
135
        if boneName in self.bones:
 
136
            raise importer.ImporterError("Attempted to add a bone named '"+boneName+"', but a bone by the same name already exists.")
 
137
        self.currentBone={
 
138
            "parentName":boneParent,
 
139
            "position":(0,0,0),
 
140
            "rotation":(1,0,0,0),
 
141
            "scale":(1,1,1)
 
142
        }
 
143
        self.bones.append((boneName,self.currentBone))
 
144
 
 
145
    def bonepos(self,x,y,z):
 
146
        self._checkCurrentBone("bonepos")
 
147
        self.currentBone["position"]=(float(x),float(y),float(z))
 
148
 
 
149
    def bonequat(self,w,x,y,z):
 
150
        self._checkCurrentBone("bonequat")
 
151
        self.currentBone["rotation"]=tuple(map(float,(w,x,y,z)))
 
152
 
 
153
    def bonescale(self,sx,sy,sz):
 
154
        self._checkCurrentBone("bonescale")
 
155
        self.currentBone["scale"]=tuple(map(float,(sx,sy,sz)))
 
156
 
 
157
    
 
158
    def addmesh(self,meshname):
 
159
        if meshname in self.meshes:
 
160
            raise importer.ImporterError("Attempted to add a mesh with name '"+meshname+"', but that name was already used.")
 
161
        self.currentMesh={
 
162
            "vertices":[],
 
163
            "feedOrder":[],
 
164
            "texture":None,
 
165
            "bumpmap":None,
 
166
            "glossmap":None,
 
167
            "bumptype":None,
 
168
            "meshmode":(modes["standard"]),
 
169
            "color":None,
 
170
        }
 
171
        self.meshes[meshname]=self.currentMesh
 
172
        self.currentMeshName=meshname
 
173
        
 
174
    def meshmode(self, modename):
 
175
        self._checkCurrentMesh("meshmode")
 
176
        self.currentMesh["meshmode"] = modes[modename]
 
177
 
 
178
    def texture(self,texturePath):
 
179
        """
 
180
        Sets the texture image of the current mesh
 
181
 
 
182
        texturePath: Path to the texture
 
183
        """
 
184
        self._checkCurrentMesh("texture")
 
185
        self.textures[texturePath]="exists"
 
186
        self.currentMesh["texture"]=texturePath
 
187
 
 
188
    def bumpmap(self,texturePath):
 
189
        """
 
190
        Sets the bump map of the current path (mutually exclusive with normal map)
 
191
 
 
192
        texturePath: path to the texture
 
193
        """
 
194
        self._checkCurrentMesh("bumpmap")
 
195
        if(self.currentMesh["bumpmap"] != None):
 
196
            raise importer.ImporterError("Attempted to add a bumpmap to the current mesh, but a bump or normal map was already added.")
 
197
        self.textures[texturePath]="exists"
 
198
        self.currentMesh["bumpmap"]=texturePath
 
199
        self.currentMesh["bumptype"]="bump"
 
200
    
 
201
    def normalmap(self,texturePath):
 
202
        """
 
203
        Sets the bump map of the current path (mutually exclusive with normal map)
 
204
 
 
205
        texturePath: path to the texture
 
206
        """
 
207
        self._checkCurrentMesh("normalmap")
 
208
        if(self.currentMesh["bumpmap"] != None):
 
209
            raise importer.ImporterError("Attempted to add a normal map to the current mesh, but a bump or normal map was already added.")
 
210
        self.textures[texturePath]="exists"
 
211
        self.currentMesh["bumpmap"]=texturePath
 
212
        self.currentMesh["bumptype"]="normal"
 
213
 
 
214
    def glossmap(self,texturePath):
 
215
        """
 
216
        Sets the gloss map of the current path
 
217
 
 
218
        texturePath: path to the texture
 
219
        """
 
220
        self._checkCurrentMesh("glossmap")
 
221
        if(self.currentMesh["glossmap"] != None):
 
222
            raise importer.ImporterError("Attempted to add a gloss map to the current mesh, but a gloss map was already added.")
 
223
        self.textures[texturePath]="exists"
 
224
        self.currentMesh["glossmap"]=texturePath
 
225
 
 
226
    def addvertex(self,x,y,z):
 
227
        self._checkCurrentMesh("addvertex")
 
228
        try:
 
229
            x=float(x)
 
230
            y=float(y)
 
231
            z=float(z)
 
232
        except Exception,e:
 
233
            raise importer.ImportError("Vertex coordinates were not convertible to finite decimal values.")
 
234
        self.currentVertex={
 
235
            "coords":(x,y,z),
 
236
            "normal":(0,0,0),
 
237
            "texture":(0,0),
 
238
            "weights":None
 
239
        }
 
240
        self.currentMesh["vertices"].append(self.currentVertex)
 
241
 
 
242
    def normal(self,x,y,z):
 
243
        #note: we've decided to temporarily account for bad float values by simply smashing them to 0. Not the
 
244
        #      most robust solution, but we need the script to not crash until we can fix the loader
 
245
        try:
 
246
            x=float(x)
 
247
            y=float(y)
 
248
            z=float(z)
 
249
        except Exception,e:
 
250
            x=0
 
251
            y=0
 
252
            z=0
 
253
            
 
254
        self._checkCurrentVertex("normal")
 
255
        self.currentVertex["normal"]=(x,y,z)
 
256
 
 
257
    def texcoord(self,x,y,z):
 
258
        self._checkCurrentVertex("texcoord")
 
259
        self.currentVertex["texture"]=(x,y,z)
 
260
 
 
261
    def weights(self,i1,w1,i2,w2,i3,w3,i4,w4):
 
262
        self._checkCurrentVertex("weights")
 
263
        self.currentVertex["weights"]=((i1,w1),(i2,w2),(i3,w3),(i4,w4))
 
264
 
 
265
    def addtri(self,t1,t2,t3):
 
266
        self._checkCurrentMesh("addtri")
 
267
        self.currentMesh["feedOrder"].append(t1)
 
268
        self.currentMesh["feedOrder"].append(t2)
 
269
        self.currentMesh["feedOrder"].append(t3)
 
270
 
 
271
    def meshtoAABoundingBox(self):
 
272
        self._checkCurrentMesh("meshtoAABoundingBox")
 
273
        vertices=[]
 
274
        for v in self.currentMesh["vertices"]:
 
275
            vertices.append(v["coords"])
 
276
        center,extent=solids.pointsToAABB(vertices)
 
277
        self._addCollisionBox(
 
278
            self.currentMeshName,
 
279
            center,extent
 
280
        )
 
281
 
 
282
        del(self.meshes[self.currentMeshName])
 
283
        self.currentMeshName=None
 
284
        self.currentMesh=None
 
285
 
 
286
    def meshtoSphere(self):
 
287
        self._checkCurrentMesh("meshtoSphere")
 
288
        vertices=[]
 
289
        for v in self.currentMesh["vertices"]:
 
290
            vertices.append(v["coords"])
 
291
        center,radius=solids.pointsToSphere(vertices)
 
292
        self._addCollisionSphere(
 
293
            self.currentMeshName,
 
294
            center,radius
 
295
        )
 
296
 
 
297
        del(self.meshes[self.currentMeshName])
 
298
        self.currentMeshName=None
 
299
        self.currentMesh=None
 
300
 
 
301
    def meshtoBox(self):
 
302
        self._checkCurrentMesh("meshtoBox")
 
303
        vertices=[]
 
304
        for v in self.currentMesh["vertices"]:
 
305
            vertices.append(v["coords"])
 
306
        center,rotation,extents=solids.pointsToBox(vertices)
 
307
        self._addCollisionBox(
 
308
            self.currentMeshName,
 
309
            center,extents,rotation
 
310
        )
 
311
 
 
312
        del(self.meshes[self.currentMeshName])
 
313
        self.currentMeshName=None
 
314
        self.currentMesh=None
 
315
 
 
316
    def cbox(self,name):
 
317
        self._addCollisionBox(name)
 
318
 
 
319
    def cbcenter(self,cx,cy,cz):
 
320
        self._checkCurrentCBox("cbcenter")
 
321
        self.currentCollisionBox["position"]=tuple(map(float,(cx,cy,cz)))
 
322
 
 
323
    def cbsize(self,bx,by,bz):
 
324
        self._checkCurrentCBox("cbsize")
 
325
        self.currentCollisionBox["size"]=tuple(map(float,(bx,by,bz)))
 
326
 
 
327
    def cbquat(self,w,x,y,z):
 
328
        self._checkCurrentCBox("cbquat")
 
329
        self.currentCollisionBox["rotation"]=tuple(map(float,(w,x,y,z)))
 
330
 
 
331
    def cbbone(self,boneID):
 
332
        self._checkCurrentCBox("cbbone")
 
333
        self.currentCollisionBox["bone"]=int(boneID)
 
334
 
 
335
    def csphere(self,name):
 
336
        self._addCollisionSphere(name)
 
337
 
 
338
    def cscenter(self,cx,cy,cz):
 
339
        self._checkCurrentCSphere("cscenter")
 
340
        self.currentCollisionSphere["position"]=tuple(map(float,(cx,cy,cz)))
 
341
 
 
342
    def csradius(self,r):
 
343
        self._checkCurrentCSphere("csradius")
 
344
        self.currentCollisionSphere["radius"]=float(r)
 
345
 
 
346
    def csbone(self,boneID):
 
347
        self._checkCurrentCSphere("csbone")
 
348
        self.currentCollisionSphere["bone"]=int(boneID)
 
349
 
 
350
    def compos(self,x,y,z):
 
351
        if self.physicsFlag==2:
 
352
            raise importer.ImporterError("Cannot specify 'compos' command when any of the 'moi' commands have been specified.")
 
353
        self.physicsFlag=1
 
354
        self.centerOfMass=(float(x),float(y),float(z))
 
355
 
 
356
    def moipos(self,x,y,z):
 
357
        if self.physicsFlag==1:
 
358
            raise importer.ImporterError("Cannot specify 'moipos' command when 'compos' has already been specified.")
 
359
        self.physicsFlag=2
 
360
        self.centerOfMass=(float(x),float(y),float(z))
 
361
 
 
362
    def moiquat(self,w,x,y,z):
 
363
        if self.physicsFlag==1:
 
364
            raise importer.ImporterError("Cannot specify 'moiquat' command when 'compos' has already been specified.")
 
365
        self.physicsFlag=2
 
366
        self.inertiaRotation=(float(w),float(x),float(y),float(z))
 
367
 
 
368
    def moiscale(self,x,y,z):
 
369
        if self.physicsFlag==1:
 
370
            raise importer.ImporterError("Cannot specify 'moiscale' command when 'compos' has already been specified.")
 
371
        self.physicsFlag=2
 
372
        self.inertiaScale=(float(x),float(y),float(z))
 
373
 
 
374
    def luafilename(self,fname):
 
375
        self.scriptFilename=str(fname)
 
376
 
 
377
    def luaclassname(self,cname):
 
378
        self.scriptClassname=str(cname)
 
379
    
 
380
    def __str__(self):
 
381
        nl="\n"
 
382
        result=""
 
383
        result += "description: "+str(self.description)+nl
 
384
        result += "collisions: "+nl
 
385
        for c in self.collisions:
 
386
            result += "  "+str(c)+nl
 
387
        result += "bones:" +nl
 
388
        boneCount=0
 
389
        for (boneName,boneData) in self.bones:
 
390
            result += "  bone "+str(boneCount)+": "+boneName+" -> "+boneData["parentName"]+nl
 
391
            result += "    position: "+str(boneData["position"])+nl
 
392
            result += "    rotation: "+str(boneData["rotation"])+nl
 
393
            result += "    scale: "+str(boneData["scale"])+nl+nl
 
394
            boneCount += 1
 
395
        return result
 
396
 
 
397
    
 
398
 
 
399
 
 
400
    
 
401
 
 
402
        
 
403
 
 
404
        
 
405
        
 
406
    
 
407