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