]> git.mxchange.org Git - flightgear.git/blob - utils/Modeller/yasim_import.py
YASim importer plugin for Blender
[flightgear.git] / utils / Modeller / yasim_import.py
1 #!BPY
2
3 # """
4 # Name: 'YASim (.xml)'
5 # Blender: 245
6 # Group: 'Import'
7 # Tooltip: 'Loads and visualizes a YASim FDM geometry'
8 # """
9
10 __author__ = "Melchior FRANZ < mfranz # aon : at >"
11 __url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/yasim_import.py"]
12 __version__ = "0.1"
13 __bpydoc__ = """\
14 yasim_import.py loads and visualizes a YASim FDM geometry
15 =========================================================
16
17 It is recommended to load the model superimposed over a greyed out and immutable copy of the aircraft model:
18
19   (1) load or import aircraft model (menu -> "File" -> "Import" -> "AC3D (.ac) ...")
20   (2) create new *empty* scene (menu -> arrow button left of "SCE:scene1" combobox -> "ADD NEW" -> "empty")
21   (3) rename scene to yasim (not required)
22   (4) link to scene1 (F10 -> "Output" tab -> arrow button left of text entry "No Set Scene" -> "scene1")
23   (5) now load the YASim config file (menu -> "File" -> "Import" -> "YASim (.xml) ...")
24
25 This is good enough for simple checks. But if you are working on the YASim configuration, then you need a
26 quick and convenient way to reload the file. In that case continue after (4):
27
28   (5) switch the button area on the bottom of the blender screen to "Scripts Window" mode (green python snake icon)
29   (6) load the YASim config file (menu -> "Scripts" -> "Import" -> "YASim (.xml) ...")
30   (7) make the "Scripts Window" area as small as possible by dragging the area separator down
31   (8) optionally split the "3D View" area and switch the right part to the "Outliner"
32   (9) press the "Reload YASim" button in the script area to reload the file
33
34
35 If the 3D model is displaced with respect to the FDM model, then the <offsets> values from the
36 model animation XML file should be added as comment to the YASim config file, as a line all by
37 itself, with no spaces surrounding the equal signs. Spaces elsewhere are allowed. For example:
38
39   <!-- offsets: x=3.45 y=0.4 p=5 -->
40
41 Possible variables are:
42
43   x ... <x-m>
44   y ... <y-m>
45   z ... <z-m>
46   h ... <heading-deg>
47   p ... <pitch-deg>
48   r ... <roll-deg>
49
50 Of course, absolute FDM coordinates can then no longer directly be read from Blender's 3D view.
51 The cursor coordinates display in the script area, however, shows the coordinates in YASim space.
52 Note that object names don't contain XML indices but element numbers. YASim_hstab#2 is the third
53 hstab in the whole file, not necessarily in its parent XML group. A floating point part in the
54 object name (e.g. YASim_hstab#2.004) only means that the geometry has been reloaded that often.
55 It's an unavoidable consequence of how Blender deals with meshes.
56
57
58 Elements are displayed as follows:
59
60   cockpit                             -> monkey head
61   fuselage                            -> blue "tube" (with only 12 sides for less clutter)
62   vstab                               -> red with yellow flaps
63   wing/mstab/hstab                    -> green with yellow flaps/spoilers/slats (always 20 cm deep);
64                                          symmetric surfaces are only displayed on the left side
65   thrusters (jet/propeller/thruster)  -> dashed line from center to actionpt;
66                                          arrow from actionpt along thrust vector (always 1 m long);
67                                          propeller circle
68   rotor                               -> radius and rel_len_blade_start circly, direction arrow,
69                                          normal and forward vector, one blade at phi0
70   gear                                -> contact point and compression vector (no arrow head)
71   tank                                -> cube (10 cm side length)
72   weight                              -> inverted cone
73   ballast                             -> cylinder
74   hitch                               -> circle (10 cm diameter)
75   hook                                -> dashed line for up angle, T-line for down angle
76   launchbar                           -> dashed line for up angles, T-line for down angles
77
78
79 Cursor coordinates displayed in GUI and terminal are in YASim coordinates and consider an
80 XML embedded displacement matrix as described above.
81 """
82
83
84 #--------------------------------------------------------------------------------
85 # Copyright (C) 2009  Melchior FRANZ  < mfranz # aon : at >
86 #
87 # This program is free software; you can redistribute it and/or
88 # modify it under the terms of the GNU General Public License as
89 # published by the Free Software Foundation; either version 2 of the
90 # License, or (at your option) any later version.
91 #
92 # This program is distributed in the hope that it will be useful, but
93 # WITHOUT ANY WARRANTY; without even the implied warranty of
94 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
95 # General Public License for more details.
96 #
97 # You should have received a copy of the GNU General Public License
98 # along with this program; if not, write to the Free Software
99 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
100 #--------------------------------------------------------------------------------
101
102
103 import Blender, BPyMessages, string, math
104 from Blender.Mathutils import *
105 from xml.sax import handler, make_parser
106
107
108 YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])
109 ORIGIN = Vector(0, 0, 0)
110 X = Vector(1, 0, 0)
111 Y = Vector(0, 1, 0)
112 Z = Vector(0, 0, 1)
113 DEG2RAD = math.pi / 180
114 RAD2DEG = 180 / math.pi
115
116
117 class Global:
118         path = ""
119         matrix = None
120         cursor = ORIGIN
121         last_cursor = Vector(Blender.Window.GetCursorPos())
122
123
124 class Abort(Exception):
125         def __init__(self, msg):
126                 self.msg = msg
127
128
129 def log(msg):
130         #print(msg)     # uncomment to get verbose log messages
131         pass
132
133
134 def error(msg):
135         print("\033[31;1mError: %s\033[m" % msg)
136         Blender.Draw.PupMenu("Error%t|" + msg)
137
138
139 def getfloat(attrs, key, default):
140         if attrs.has_key(key):
141                 return float(attrs[key])
142         return default
143
144
145 def draw_dashed_line(mesh, start, end):
146         w = 0.04
147         step = w * (end - start).normalize()
148         n = len(mesh.verts)
149         for i in range(int(1 + 0.5 * (end - start).length / w)):
150                 a = start + 2 * i * step
151                 b = start + (2 * i + 1) * step
152                 if (b - end).length < step.length:
153                         b = end
154                 mesh.verts.extend([a, b])
155                 mesh.edges.extend([n + 2 * i, n + 2 * i + 1])
156
157
158 def draw_arrow(mesh, start, end):
159         v = end - start
160         m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start)
161         v = v.length * X
162         n = len(mesh.verts)
163         mesh.verts.extend([ORIGIN * m , v * m, (v - 0.05 * X + 0.05 * Y) * m, (v - 0.05 * X - 0.05 * Y) * m]) # head
164         mesh.verts.extend([(ORIGIN + 0.05 * Y) * m, (ORIGIN - 0.05 * Y) * m]) # base
165         mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]])
166
167
168 def draw_circle(mesh, numpoints, radius, matrix):
169         n = len(mesh.verts)
170         for i in range(numpoints):
171                 angle = 2.0 * math.pi * i / numpoints
172                 v = Vector(radius * math.cos(angle), radius * math.sin(angle), 0)
173                 mesh.verts.extend([v * matrix])
174         for i in range(numpoints):
175                 i1 = (i + 1) % numpoints
176                 mesh.edges.extend([[n + i, n + i1]])
177
178
179 class Item:
180         scene = Blender.Scene.GetCurrent()
181
182         def make_twosided(self, mesh):
183                 mesh.faceUV = True
184                 for f in mesh.faces:
185                         f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
186
187         def set_color(self, mesh, name, color):
188                 mat = Blender.Material.New(name)
189                 mat.setRGBCol(color[0], color[1], color[2])
190                 mat.setAlpha(color[3])
191                 mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW
192                 mesh.materials += [mat]
193
194
195 class Cockpit(Item):
196         def __init__(self, center):
197                 mesh = Blender.Mesh.Primitives.Monkey()
198                 mesh.transform(ScaleMatrix(0.13, 4) * Euler(90, 0, 90).toMatrix().resize4x4() * TranslationMatrix(Vector(-0.1, 0, -0.032)))
199                 obj = self.scene.objects.new(mesh, "YASim_cockpit")
200                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
201
202
203 class Tank(Item):
204         def __init__(self, name, center):
205                 mesh = Blender.Mesh.Primitives.Cube()
206                 mesh.transform(ScaleMatrix(0.05, 4))
207                 obj = self.scene.objects.new(mesh, name)
208                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
209
210
211 class Ballast(Item):
212         def __init__(self, name, center):
213                 mesh = Blender.Mesh.Primitives.Cylinder()
214                 mesh.transform(ScaleMatrix(0.05, 4))
215                 obj = self.scene.objects.new(mesh, name)
216                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
217
218
219 class Weight(Item):
220         def __init__(self, name, center):
221                 mesh = Blender.Mesh.Primitives.Cone()
222                 mesh.transform(ScaleMatrix(0.05, 4))
223                 obj = self.scene.objects.new(mesh, name)
224                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
225
226
227 class Gear(Item):
228         def __init__(self, name, center, compression):
229                 mesh = Blender.Mesh.New()
230                 mesh.verts.extend([ORIGIN, compression])
231                 mesh.edges.extend([0, 1])
232                 obj = self.scene.objects.new(mesh, name)
233                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
234
235
236 class Hook(Item):
237         def __init__(self, name, center, length, up_angle, dn_angle):
238                 mesh = Blender.Mesh.New()
239                 up = ORIGIN - length * math.cos(up_angle * DEG2RAD) * X - length * math.sin(up_angle * DEG2RAD) * Z
240                 dn = ORIGIN - length * math.cos(dn_angle * DEG2RAD) * X - length * math.sin(dn_angle * DEG2RAD) * Z
241                 mesh.verts.extend([ORIGIN, dn, dn + 0.05 * Y, dn - 0.05 * Y])
242                 mesh.edges.extend([[0, 1], [2, 3]])
243                 draw_dashed_line(mesh, ORIGIN, up)
244                 draw_dashed_line(mesh, ORIGIN, dn)
245                 obj = self.scene.objects.new(mesh, name)
246                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
247
248
249 class Launchbar(Item):
250         def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle):
251                 mesh = Blender.Mesh.New()
252                 hb = hb - lb
253                 lb_tip = ORIGIN + lb_length * math.cos(dn_angle * DEG2RAD) * X - lb_length * math.sin(dn_angle * DEG2RAD) * Z
254                 hb_tip = hb - hb_length * math.cos(dn_angle * DEG2RAD) * X - hb_length * math.sin(dn_angle * DEG2RAD) * Z
255                 mesh.verts.extend([lb_tip, ORIGIN, hb, hb_tip, lb_tip + 0.05 * Y, lb_tip - 0.05 * Y, hb_tip + 0.05 * Y, hb_tip - 0.05 * Y])
256                 mesh.edges.extend([[0, 1], [1, 2], [2, 3], [4, 5], [6, 7]])
257                 draw_dashed_line(mesh, ORIGIN, lb_length * math.cos(up_angle * DEG2RAD) * X - lb_length * math.sin(up_angle * DEG2RAD) * Z)
258                 draw_dashed_line(mesh, hb, hb - hb_length * math.cos(up_angle * DEG2RAD) * X - hb_length * math.sin(up_angle * DEG2RAD) * Z)
259                 obj = self.scene.objects.new(mesh, name)
260                 obj.setMatrix(TranslationMatrix(lb) * Global.matrix)
261
262
263 class Hitch(Item):
264         def __init__(self, name, center):
265                 mesh = Blender.Mesh.Primitives.Circle(8, 0.1)
266                 obj = self.scene.objects.new(mesh, name)
267                 obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix)
268
269
270 class Thrust:
271         def set_actionpt(self, p):
272                 self.actionpt = p
273
274         def set_dir(self, d):
275                 self.thrustvector = d
276
277
278 class Thruster(Thrust, Item):
279         def __init__(self, name, center, thrustvector):
280                 (self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector)
281
282         def __del__(self):
283                 a = self.actionpt - self.center
284                 mesh = Blender.Mesh.New()
285                 draw_dashed_line(mesh, ORIGIN, a)
286                 draw_arrow(mesh, a, a + self.thrustvector.normalize())
287                 obj = self.scene.objects.new(mesh, self.name)
288                 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
289
290
291 class Propeller(Thrust, Item):
292         def __init__(self, name, center, radius):
293                 (self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X)
294
295         def __del__(self):
296                 a = self.actionpt - self.center
297                 cross = -X.cross(self.thrustvector)
298                 angle = AngleBetweenVecs(-X, self.thrustvector)
299                 matrix = RotationMatrix(angle, 4, "r", cross) * TranslationMatrix(a)
300                 matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
301
302                 mesh = Blender.Mesh.New()
303                 mesh.verts.extend([ORIGIN * matrix, (ORIGIN + self.radius * X) * matrix])
304                 mesh.edges.extend([[0, 1]])
305                 draw_dashed_line(mesh, ORIGIN, a)
306                 draw_arrow(mesh, a, a + self.thrustvector.normalize())
307
308                 draw_circle(mesh, 128, self.radius, matrix)
309                 obj = self.scene.objects.new(mesh, self.name)
310                 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
311
312
313 class Jet(Thrust, Item):
314         def __init__(self, name, center, rotate):
315                 (self.name, self.center, self.actionpt) = (name, center, center)
316                 self.thrustvector = -X * RotationMatrix(rotate, 4, "y")
317
318         def __del__(self):
319                 a = self.actionpt - self.center
320                 mesh = Blender.Mesh.New()
321                 draw_dashed_line(mesh, ORIGIN, a)
322                 draw_arrow(mesh, a, a + self.thrustvector.normalize())
323                 obj = self.scene.objects.new(mesh, self.name)
324                 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
325
326
327 class Fuselage(Item):
328         def __init__(self, name, a, b, width, taper, midpoint):
329                 numvert = 12
330                 angle = []
331                 for i in range(numvert):
332                         alpha = i * 2 * math.pi / float(numvert)
333                         angle.append([math.cos(alpha), math.sin(alpha)])
334
335                 axis = b - a
336                 length = axis.length
337                 mesh = Blender.Mesh.New()
338
339                 for i in range(numvert):
340                         mesh.verts.extend([[0, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
341                 for i in range(numvert):
342                         mesh.verts.extend([[midpoint * length, 0.5 * width * angle[i][0], 0.5 * width * angle[i][1]]])
343                 for i in range(numvert):
344                         mesh.verts.extend([[length, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
345                 for i in range(numvert):
346                         i1 = (i + 1) % numvert
347                         mesh.faces.extend([[i, i1, i1 + numvert, i + numvert]])
348                         mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]])
349
350                 mesh.verts.extend([ORIGIN, length * X])
351                 self.set_color(mesh, name + "mat", [0, 0, 0.5, 0.4])
352                 obj = self.scene.objects.new(mesh, name)
353                 obj.transp = True
354                 obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix)
355
356
357 class Rotor(Item):
358         def __init__(self, name, center, up, fwd, numblades, radius, chord, twist, taper, rel_len_blade_start, phi0, ccw):
359                 matrix = RotationMatrix(phi0, 4, "z") * up.toTrackQuat('z', 'x').toMatrix().resize4x4()
360                 invert = matrix.copy().invert()
361                 direction = [-1, 1][ccw]
362                 twist *= DEG2RAD
363                 a = ORIGIN + rel_len_blade_start * radius * X
364                 b = ORIGIN + radius * X
365                 tw = 0.5 * chord * taper * math.cos(twist) * Y + 0.5 * direction * chord * taper * math.sin(twist) * Z
366
367                 mesh = Blender.Mesh.New()
368                 mesh.verts.extend([ORIGIN, a, b, a + 0.5 * chord * Y, a - 0.5 * chord * Y, b + tw, b - tw])
369                 mesh.edges.extend([[0, 1], [1, 2], [1, 3], [1, 4], [3, 5], [4, 6], [5, 6]])
370                 draw_circle(mesh, 64, rel_len_blade_start * radius, Matrix())
371                 draw_circle(mesh, 128, radius, Matrix())
372                 draw_arrow(mesh, ORIGIN, up * invert)
373                 draw_arrow(mesh, ORIGIN, fwd * invert)
374                 b += 0.1 * X + direction * chord * Y
375                 draw_arrow(mesh, b, b + 0.5 * radius * direction * Y)
376                 obj = self.scene.objects.new(mesh, name)
377                 obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix)
378
379
380 class Wing(Item):
381         def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral):
382                 #  <1--0--2
383                 #   \  |  /
384                 #    4-3-5
385
386                 mesh = Blender.Mesh.New()
387                 mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X])
388                 tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X
389                 tipfore = tip + 0.5 * taper * chord * math.cos(twist * DEG2RAD) * X + 0.5 * taper * chord * math.sin(twist * DEG2RAD) * Z
390                 tipaft = tip + tip - tipfore
391                 mesh.verts.extend([tip, tipfore, tipaft])
392                 mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]])
393
394                 self.set_color(mesh, name + "mat", [[0, 0.5, 0, 0.5], [0.5, 0, 0, 0.5]][name.startswith("YASim_vstab")])
395                 self.make_twosided(mesh)
396
397                 obj = self.scene.objects.new(mesh, name)
398                 obj.transp = True
399                 m = Euler(dihedral, -incidence, 0).toMatrix().resize4x4()
400                 m *= TranslationMatrix(root)
401                 obj.setMatrix(m * Global.matrix)
402                 (self.obj, self.mesh) = (obj, mesh)
403
404         def add_flap(self, name, start, end):
405                 a = Vector(self.mesh.verts[2].co)
406                 b = Vector(self.mesh.verts[5].co)
407                 c = 0.2 * (Vector(self.mesh.verts[0].co - a)).normalize()
408                 m = self.obj.getMatrix()
409
410                 mesh = Blender.Mesh.New()
411                 i0 = a + start * (b - a)
412                 i1 = a + end * (b - a)
413                 mesh.verts.extend([i0, i1, i0 + c, i1 + c])
414                 mesh.faces.extend([[0, 1, 3, 2]])
415
416                 self.set_color(mesh, name + "mat", [0.8, 0.8, 0, 0.9])
417                 self.make_twosided(mesh)
418
419                 obj = self.scene.objects.new(mesh, name)
420                 obj.transp = True
421                 obj.setMatrix(m)
422
423
424 class import_yasim(handler.ContentHandler):
425         ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \
426                         "control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \
427                         "rotorgear", "tow", "winch", "solve-weight"]
428
429         # err_handler
430         def error(self, exception):
431                 raise Abort(str(exception))
432
433         def fatalError(self, exception):
434                 raise Abort(str(exception))
435
436         def warning(self, exception):
437                 print "WARNING: " + str(exception)
438
439         # doc_handler
440         def setDocumentLocator(self, whatever):
441                 pass
442
443         def startDocument(self):
444                 self.tags = []
445                 self.counter = {}
446                 self.items = [None]
447                 pass
448
449         def endDocument(self):
450                 for o in Item.scene.objects:
451                         o.sel = True
452
453         def characters(self, data):
454                 pass
455
456         def ignorableWhitespace(self, data, start, length):
457                 pass
458
459         def processingInstruction(self, target, data):
460                 pass
461
462         def startElement(self, tag, attrs):
463                 if len(self.tags) == 0 and tag != "airplane":
464                         raise Abort("this isn't a YASim config file")
465
466                 self.tags.append(tag)
467                 path = string.join(self.tags, '/')
468                 item = Item()
469                 parent = self.items[-1]
470
471                 if self.counter.has_key(tag):
472                         self.counter[tag] += 1
473                 else:
474                         self.counter[tag] = 0
475
476                 if tag == "cockpit":
477                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
478                         log("\033[31mcockpit x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
479                         item = Cockpit(c)
480
481                 elif tag == "fuselage":
482                         a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"]))
483                         b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"]))
484                         width = float(attrs["width"])
485                         taper = getfloat(attrs, "taper", 1)
486                         midpoint = getfloat(attrs, "midpoint", 0.5)
487                         log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \
488                                         (a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint))
489                         item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint)
490
491                 elif tag == "gear":
492                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
493                         compression = getfloat(attrs, "compression", 1)
494                         up = Z * compression
495                         if attrs.has_key("upx"):
496                                 up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression
497                         log("\033[35;1mgear x=%f y=%f z=%f compression=%f upx=%f upy=%f upz=%f\033[m" \
498                                         % (c[0], c[1], c[2], compression, up[0], up[1], up[2]))
499                         item = Gear("YASim_gear#%d" % self.counter[tag], c, up)
500
501                 elif tag == "jet":
502                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
503                         rotate = getfloat(attrs, "rotate", 0.0)
504                         log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate))
505                         item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate)
506
507                 elif tag == "propeller":
508                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
509                         radius = float(attrs["radius"])
510                         log("\033[36;1m%s x=%f y=%f z=%f radius=%f\033[m" % (tag, c[0], c[1], c[2], radius))
511                         item = Propeller("YASim_propeller#%d" % self.counter[tag], c, radius)
512
513                 elif tag == "thruster":
514                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
515                         v = Vector(float(attrs["vx"]), float(attrs["vy"]), float(attrs["vz"]))
516                         log("\033[36;1m%s x=%f y=%f z=%f vx=%f vy=%f vz=%f\033[m" % (tag, c[0], c[1], c[2], v[0], v[1], v[2]))
517                         item = Thruster("YASim_thruster#%d" % self.counter[tag], c, v)
518
519                 elif tag == "actionpt":
520                         if not isinstance(parent, Thrust):
521                                 raise Abort("%s is not part of a propeller or jet" % path)
522
523                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
524                         log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
525                         parent.set_actionpt(c)
526
527                 elif tag == "dir":
528                         if not isinstance(parent, Thrust):
529                                 raise Abort("%s is not part of a propeller or jet" % path)
530
531                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
532                         log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
533                         parent.set_dir(c)
534
535                 elif tag == "tank":
536                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
537                         log("\033[34;1m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
538                         item = Tank("YASim_tank#%d" % self.counter[tag], c)
539
540                 elif tag == "ballast":
541                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
542                         log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
543                         item = Ballast("YASim_ballast#%d" % self.counter[tag], c)
544
545                 elif tag == "weight":
546                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
547                         log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
548                         item = Weight("YASim_weight#%d" % self.counter[tag], c)
549
550                 elif tag == "hook":
551                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
552                         length = getfloat(attrs, "length", 1.0)
553                         up_angle = getfloat(attrs, "up-angle", 0.0)
554                         down_angle = getfloat(attrs, "down-angle", 70.0)
555                         log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \
556                                         % (tag, c[0], c[1], c[2], length, up_angle, down_angle))
557                         item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle)
558
559                 elif tag == "hitch":
560                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
561                         log("\033[35m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
562                         item = Hitch("YASim_hitch#%d" % self.counter[tag], c)
563
564                 elif tag == "launchbar":
565                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
566                         length = getfloat(attrs, "length", 1.0)
567                         up_angle = getfloat(attrs, "up-angle", -45.0)
568                         down_angle = getfloat(attrs, "down-angle", 45.0)
569                         holdback = Vector(getfloat(attrs, "holdback-x", c[0]), getfloat(attrs, "holdback-y", c[1]), getfloat(attrs, "holdback-z", c[2]))
570                         holdback_length = getfloat(attrs, "holdback-length", 2.0)
571                         log("\033[35m%s x=%f y=%f z=%f length=%f down-angle=%f up-angle=%f holdback-x=%f holdback-y=%f holdback-z+%f holdback-length=%f\033[m" \
572                                         % (tag, c[0], c[1], c[2], length, down_angle, up_angle, \
573                                         holdback[0], holdback[1], holdback[2], holdback_length))
574                         item = Launchbar("YASim_launchbar#%d" % self.counter[tag], c, length, holdback, holdback_length, up_angle, down_angle)
575
576                 elif tag == "wing" or tag == "hstab" or tag == "vstab" or tag == "mstab":
577                         root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
578                         length = float(attrs["length"])
579                         chord = float(attrs["chord"])
580                         incidence = getfloat(attrs, "incidence", 0.0)
581                         twist = getfloat(attrs, "twist", 0.0)
582                         taper = getfloat(attrs, "taper", 1.0)
583                         sweep = getfloat(attrs, "sweep", 0.0)
584                         dihedral = getfloat(attrs, "dihedral", [0.0, 90.0][tag == "vstab"])
585                         log("\033[33;1m%s x=%f y=%f z=%f length=%f chord=%f incidence=%f twist=%f taper=%f sweep=%f dihedral=%f\033[m" \
586                                         % (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral))
587                         item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral)
588
589                 elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler":
590                         if not isinstance(parent, Wing):
591                                 raise Abort("%s is not part of a wing or stab" % path)
592
593                         start = float(attrs["start"])
594                         end = float(attrs["end"])
595                         log("\t\033[33m%s start=%f end=%f\033[m" % (tag, start, end))
596                         parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end)
597
598                 elif tag == "rotor":
599                         c = Vector(getfloat(attrs, "x", 0.0), getfloat(attrs, "y", 0.0), getfloat(attrs, "z", 0.0))
600                         norm = Vector(getfloat(attrs, "nx", 0.0), getfloat(attrs, "ny", 0.0), getfloat(attrs, "nz", 1.0))
601                         fwd = Vector(getfloat(attrs, "fx", 1.0), getfloat(attrs, "fy", 0.0), getfloat(attrs, "fz", 0.0))
602                         diameter = getfloat(attrs, "diameter", 10.2)
603                         numblades = int(getfloat(attrs, "numblades", 4))
604                         chord = getfloat(attrs, "chord", 0.3)
605                         twist = getfloat(attrs, "twist", 0.0)
606                         taper = getfloat(attrs, "taper", 1.0)
607                         rel_len_blade_start = getfloat(attrs, "rel-len-blade-start", 0.0)
608                         phi0 = getfloat(attrs, "phi0", 0)
609                         ccw = not not getfloat(attrs, "ccw", 0)
610
611                         log(("\033[36;1mrotor x=%f y=%f z=%f nx=%f ny=%f nz=%f fx=%f fy=%f fz=%f numblades=%d diameter=%f " \
612                                         + "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \
613                                         % (c[0], c[1], c[2], norm[0], norm[1], norm[2], fwd[0], fwd[1], fwd[2], numblades, \
614                                         diameter, chord, twist, taper, rel_len_blade_start, phi0, ccw))
615                         item = Rotor("YASim_rotor#%d" % self.counter[tag], c, norm, fwd, numblades, 0.5 * diameter, chord, \
616                                         twist, taper, rel_len_blade_start, phi0, ccw)
617
618                 elif tag not in self.ignored:
619                         log("\033[30;1m%s\033[m" % path)
620
621                 self.items.append(item)
622
623         def endElement(self, tag):
624                 self.tags.pop()
625                 self.items.pop()
626
627
628 def extract_matrix(path, tag):
629         v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 }
630         hasmatrix = False
631         f = open(path)
632         for line in f.readlines():
633                 line = string.strip(line)
634                 if not line.startswith("<!--") or not line.endswith("-->"):
635                         continue
636                 line = string.strip(line[4:-3])
637                 if not string.lower(line).startswith("%s:" % tag):
638                         continue
639                 line = string.strip(line[8:])
640                 for assignment in string.split(line):
641                         (key, value) = string.split(assignment, '=', 2)
642                         v[string.strip(key)] = float(string.strip(value))
643                         hasmatrix = True
644         f.close()
645         matrix = None
646         if hasmatrix:
647                 matrix = Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4()
648                 matrix *= TranslationMatrix(Vector(v['x'], v['y'], v['z']))
649         return matrix
650
651
652 def run_parser(path):
653         if BPyMessages.Error_NoFile(path):
654                 return
655
656         editmode = Blender.Window.EditMode()
657         if editmode:
658                 Blender.Window.EditMode(0)
659         Blender.Window.WaitCursor(1)
660
661         try:
662                 for o in Item.scene.objects:
663                         if o.name.startswith("YASim_"):
664                                 Item.scene.objects.unlink(o)
665
666                 print("\033[1mloading '%s'\033[m" % path)
667
668                 Global.matrix = YASIM_MATRIX
669                 matrix = extract_matrix(path, "offsets")
670                 if matrix:
671                         Global.matrix *= matrix.invert()
672
673                 yasim = make_parser()
674                 yasim.setContentHandler(import_yasim())
675                 yasim.setErrorHandler(import_yasim())
676                 yasim.parse(path)
677
678                 Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
679                 Global.path = path
680
681         except Abort, e:
682                 print "Error:", e.msg, "  -> aborting ...\n"
683                 Blender.Draw.PupMenu("Error%t|" + e.msg)
684
685         Blender.Window.RedrawAll()
686         Blender.Window.WaitCursor(0)
687         if editmode:
688                 Blender.Window.EditMode(1)
689
690
691 def draw():
692         from Blender import BGL, Draw
693         (width, height) = Blender.Window.GetAreaSize()
694
695         BGL.glClearColor(0.4, 0.4, 0.45, 1)
696         BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
697         Draw.PushButton("Reload YASim", 0, 5, 5, 100, 28)
698         Draw.PushButton("Update Cursor", 1, width - 650, 5, 100, 28)
699         BGL.glColor3f(1, 1, 1)
700
701         BGL.glRasterPos2f(120, 15)
702         Draw.Text(Global.path)
703
704         BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Distance from last") - Blender.Draw.GetStringWidth("Current"), 24)
705         Draw.Text("Current cursor pos:    x = %+.3f    y = %+.3f    z = %+.3f" % tuple(Global.cursor))
706
707         c = Global.cursor - Global.last_cursor
708         BGL.glRasterPos2f(width - 530, 7)
709         Draw.Text("Distance from last cursor pos:    x = %+.3f    y = %+.3f    z = %+.3f    length = %.3f" % (c[0], c[1], c[2], c.length))
710
711
712 def event(ev, value):
713         if ev == Blender.Draw.ESCKEY:
714                 Blender.Draw.Exit()
715
716
717 def button(n):
718         if n == 0:
719                 run_parser(Global.path)
720         elif n == 1:
721                 Global.last_cursor = Global.cursor
722                 Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert()
723                 d = Global.cursor - Global.last_cursor
724                 print("cursor:   x=\"%f\" y=\"%f\" z=\"%f\"   dx=%f dy=%f dz=%f   length=%f" \
725                                 % (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length))
726         Blender.Draw.Redraw(1)
727
728
729 def main():
730         registry = Blender.Registry.GetKey("FGYASimImportExport", False)
731         if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
732                 path = registry["path"]
733         else:
734                 path = ""
735
736
737         log(6 * "\n")
738         if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
739                 Blender.Draw.Register(draw, event, button)
740
741         Blender.Window.FileSelector(run_parser, "Import YASim Configuration File", path)
742
743
744 main()
745