5
Copyright (c) 2010 Carnegie Mellon University
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:
14
The above copyright notice and this permission notice shall be included in
15
all copies or substantial portions of the Software.
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
28
Data structure constructor for models
32
import solids,importer
34
modes={ "standard":0, "wireframe":1, "alphatest":2, }
36
class Model(importer.Importer):
48
self.currentMeshName=None
49
self.currentVertex=None
50
self.currentCollisionSphere=None
51
self.currentCollisionBox=None
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
59
def _checkCurrentBone(self,cmdname):
61
Raises an error if a command is attempted that isn't valid without
64
if self.currentBone==None:
65
raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current bone.")
67
def _checkCurrentMesh(self,cmdname):
69
Raises an error if a command is attempted that isn't valid without
72
if self.currentMesh==None:
73
raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current mesh.")
75
def _checkCurrentVertex(self,cmdname):
77
Raises an error if a command is attempted that isn't valid without a vertex
79
if self.currentVertex==None:
80
raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current vertex.")
82
def _checkCurrentCSphere(self,cmdname):
83
if self.currentCollisionSphere==None:
84
raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current collision sphere.")
86
def _checkCurrentCBox(self,cmdname):
87
if self.currentCollisionBox==None:
88
raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current collision box.")
90
def _checkCurrentWeight(self,cmdname):
91
if self.currentWeight==None:
92
raise importer.ImporterError("Cannot do '"+cmdname+"' when there is no current weight.")
95
def _addCollisionSphere(self,name,position=(0,0,0),radius=1,bone=0):
96
self.currentCollisionSphere={
103
self.collisions.append(self.currentCollisionSphere)
105
def _addCollisionBox(self,name,position=(0,0,0),size=(0,0,0),rotation=(1,0,0,0),bone=0):
106
self.currentCollisionBox={
114
self.collisions.append(self.currentCollisionBox)
116
def _addWeight(self,name,position=(0,0,0),magnitude=1,bone=0):
120
"magnitude":magnitude,
123
self.m_weights.append(self.currentWeight)
125
def setdescription(self,description):
126
self.description=description
128
def setmass(self,mass):
129
self.mass=float(mass)
131
def addbone(self,boneName,boneParent):
133
Adds a bone to the model
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.")
138
"parentName":boneParent,
140
"rotation":(1,0,0,0),
143
self.bones.append((boneName,self.currentBone))
145
def bonepos(self,x,y,z):
146
self._checkCurrentBone("bonepos")
147
self.currentBone["position"]=(float(x),float(y),float(z))
149
def bonequat(self,w,x,y,z):
150
self._checkCurrentBone("bonequat")
151
self.currentBone["rotation"]=tuple(map(float,(w,x,y,z)))
153
def bonescale(self,sx,sy,sz):
154
self._checkCurrentBone("bonescale")
155
self.currentBone["scale"]=tuple(map(float,(sx,sy,sz)))
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.")
168
"meshmode":(modes["standard"]),
171
self.meshes[meshname]=self.currentMesh
172
self.currentMeshName=meshname
174
def meshmode(self, modename):
175
self._checkCurrentMesh("meshmode")
176
self.currentMesh["meshmode"] = modes[modename]
178
def texture(self,texturePath):
180
Sets the texture image of the current mesh
182
texturePath: Path to the texture
184
self._checkCurrentMesh("texture")
185
self.textures[texturePath]="exists"
186
self.currentMesh["texture"]=texturePath
188
def bumpmap(self,texturePath):
190
Sets the bump map of the current path (mutually exclusive with normal map)
192
texturePath: path to the texture
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"
201
def normalmap(self,texturePath):
203
Sets the bump map of the current path (mutually exclusive with normal map)
205
texturePath: path to the texture
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"
214
def glossmap(self,texturePath):
216
Sets the gloss map of the current path
218
texturePath: path to the texture
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
226
def addvertex(self,x,y,z):
227
self._checkCurrentMesh("addvertex")
233
raise importer.ImportError("Vertex coordinates were not convertible to finite decimal values.")
240
self.currentMesh["vertices"].append(self.currentVertex)
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
254
self._checkCurrentVertex("normal")
255
self.currentVertex["normal"]=(x,y,z)
257
def texcoord(self,x,y,z):
258
self._checkCurrentVertex("texcoord")
259
self.currentVertex["texture"]=(x,y,z)
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))
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)
271
def meshtoAABoundingBox(self):
272
self._checkCurrentMesh("meshtoAABoundingBox")
274
for v in self.currentMesh["vertices"]:
275
vertices.append(v["coords"])
276
center,extent=solids.pointsToAABB(vertices)
277
self._addCollisionBox(
278
self.currentMeshName,
282
del(self.meshes[self.currentMeshName])
283
self.currentMeshName=None
284
self.currentMesh=None
286
def meshtoSphere(self):
287
self._checkCurrentMesh("meshtoSphere")
289
for v in self.currentMesh["vertices"]:
290
vertices.append(v["coords"])
291
center,radius=solids.pointsToSphere(vertices)
292
self._addCollisionSphere(
293
self.currentMeshName,
297
del(self.meshes[self.currentMeshName])
298
self.currentMeshName=None
299
self.currentMesh=None
302
self._checkCurrentMesh("meshtoBox")
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
312
del(self.meshes[self.currentMeshName])
313
self.currentMeshName=None
314
self.currentMesh=None
317
self._addCollisionBox(name)
319
def cbcenter(self,cx,cy,cz):
320
self._checkCurrentCBox("cbcenter")
321
self.currentCollisionBox["position"]=tuple(map(float,(cx,cy,cz)))
323
def cbsize(self,bx,by,bz):
324
self._checkCurrentCBox("cbsize")
325
self.currentCollisionBox["size"]=tuple(map(float,(bx,by,bz)))
327
def cbquat(self,w,x,y,z):
328
self._checkCurrentCBox("cbquat")
329
self.currentCollisionBox["rotation"]=tuple(map(float,(w,x,y,z)))
331
def cbbone(self,boneID):
332
self._checkCurrentCBox("cbbone")
333
self.currentCollisionBox["bone"]=int(boneID)
335
def csphere(self,name):
336
self._addCollisionSphere(name)
338
def cscenter(self,cx,cy,cz):
339
self._checkCurrentCSphere("cscenter")
340
self.currentCollisionSphere["position"]=tuple(map(float,(cx,cy,cz)))
342
def csradius(self,r):
343
self._checkCurrentCSphere("csradius")
344
self.currentCollisionSphere["radius"]=float(r)
346
def csbone(self,boneID):
347
self._checkCurrentCSphere("csbone")
348
self.currentCollisionSphere["bone"]=int(boneID)
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.")
354
self.centerOfMass=(float(x),float(y),float(z))
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.")
360
self.centerOfMass=(float(x),float(y),float(z))
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.")
366
self.inertiaRotation=(float(w),float(x),float(y),float(z))
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.")
372
self.inertiaScale=(float(x),float(y),float(z))
374
def luafilename(self,fname):
375
self.scriptFilename=str(fname)
377
def luaclassname(self,cname):
378
self.scriptClassname=str(cname)
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
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