]> git.mxchange.org Git - flightgear.git/blob - utils/Modeller/yasim_import.py
add mirror button (useful for asymmetric aircraft, like the BV-141)
[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_flap0#2 is the third
62 flap0 in the whole file, not necessarily in its parent XML group. A floating point part in the
63 object name (e.g. YASim_flap0#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 controls surfaces (flap0, flap1, slat, spoiler)
72   wing/mstab/hstab                    -> green with yellow control surfaces (which are 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, normal and forward vector,
78                                          one blade at phi0 with direction arrow near blade tip
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 The Mirror button complements symmetrical surfaces (wing/hstab/mstab) and control surfaces
89 (flap0/flap1/slat/spoiler). This is useful for asymmetrical aircraft, but has the disadvantage
90 that it moves the surfaces' object centers from their usual place, yasim's [x, y, z] value,
91 to [0, 0, 0]. Turning mirroring off restores the object center.
92 """
93
94
95 #--------------------------------------------------------------------------------
96 # Copyright (C) 2009  Melchior FRANZ  < mfranz # aon : at >
97 #
98 # This program is free software; you can redistribute it and/or
99 # modify it under the terms of the GNU General Public License as
100 # published by the Free Software Foundation; either version 2 of the
101 # License, or (at your option) any later version.
102 #
103 # This program is distributed in the hope that it will be useful, but
104 # WITHOUT ANY WARRANTY; without even the implied warranty of
105 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
106 # General Public License for more details.
107 #
108 # You should have received a copy of the GNU General Public License
109 # along with this program; if not, write to the Free Software
110 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
111 #--------------------------------------------------------------------------------
112
113
114 import Blender, BPyMessages, string, math, os
115 from Blender.Mathutils import *
116 from xml.sax import handler, make_parser
117
118
119 CONFIG = string.split(os.getenv("BLENDER_YASIM_IMPORT") or "")
120 YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])
121 ORIGIN = Vector(0, 0, 0)
122 X = Vector(1, 0, 0)
123 Y = Vector(0, 1, 0)
124 Z = Vector(0, 0, 1)
125 DEG2RAD = math.pi / 180
126 RAD2DEG = 180 / math.pi
127
128 NO_EVENT = 0
129 RELOAD_BUTTON = 1
130 CURSOR_BUTTON = 2
131 MIRROR_BUTTON = 3
132
133
134
135 class Global:
136         verbose = "verbose" in CONFIG
137         path = ""
138         matrix = None
139         cursor = ORIGIN
140         last_cursor = Vector(Blender.Window.GetCursorPos())
141         mirror = Blender.Draw.Create(0)
142
143
144
145 class Abort(Exception):
146         def __init__(self, msg):
147                 self.msg = msg
148
149
150
151 def log(msg):
152         if Global.verbose:
153                 print(msg)
154
155
156
157 def error(msg):
158         print(("\033[31;1mError: %s\033[m" % msg))
159         Blender.Draw.PupMenu("Error%t|" + msg)
160
161
162
163 def getfloat(attrs, key, default):
164         if attrs.has_key(key):
165                 return float(attrs[key])
166         return default
167
168
169
170 def draw_dashed_line(mesh, start, end):
171         w = 0.04
172         step = w * (end - start).normalize()
173         n = len(mesh.verts)
174         for i in range(int(1 + 0.5 * (end - start).length / w)):
175                 a = start + 2 * i * step
176                 b = start + (2 * i + 1) * step
177                 if (b - end).length < step.length:
178                         b = end
179                 mesh.verts.extend([a, b])
180                 mesh.edges.extend([n + 2 * i, n + 2 * i + 1])
181
182
183
184 def draw_arrow(mesh, start, end):
185         v = end - start
186         m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start)
187         v = v.length * X
188         n = len(mesh.verts)
189         mesh.verts.extend([ORIGIN * m , v * m, (v - 0.05 * X + 0.05 * Y) * m, (v - 0.05 * X - 0.05 * Y) * m]) # head
190         mesh.verts.extend([(ORIGIN + 0.05 * Y) * m, (ORIGIN - 0.05 * Y) * m]) # base
191         mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]])
192
193
194
195 def draw_circle(mesh, numpoints, radius, matrix):
196         n = len(mesh.verts)
197         for i in range(numpoints):
198                 angle = 2.0 * math.pi * i / numpoints
199                 v = Vector(radius * math.cos(angle), radius * math.sin(angle), 0)
200                 mesh.verts.extend([v * matrix])
201         for i in range(numpoints):
202                 i1 = (i + 1) % numpoints
203                 mesh.edges.extend([[n + i, n + i1]])
204
205
206
207 class Item:
208         scene = Blender.Scene.GetCurrent()
209
210         def make_twosided(self, mesh):
211                 mesh.faceUV = True
212                 for f in mesh.faces:
213                         f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
214
215         def set_color(self, obj, name, color):
216                 mat = Blender.Material.New(name)
217                 mat.setRGBCol(color[0], color[1], color[2])
218                 mat.setAlpha(color[3])
219                 mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW
220                 obj.transp = True
221
222                 mesh = obj.getData(mesh = True)
223                 mesh.materials += [mat]
224
225                 for f in mesh.faces:
226                         f.smooth = True
227                 mesh.calcNormals()
228
229
230
231 class Cockpit(Item):
232         def __init__(self, center):
233                 mesh = Blender.Mesh.Primitives.Monkey()
234                 mesh.transform(ScaleMatrix(0.13, 4) * Euler(90, 0, 90).toMatrix().resize4x4() * TranslationMatrix(Vector(-0.1, 0, -0.032)))
235                 obj = self.scene.objects.new(mesh, "YASim_cockpit")
236                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
237
238
239
240 class Tank(Item):
241         def __init__(self, name, center):
242                 mesh = Blender.Mesh.Primitives.Cube()
243                 mesh.transform(ScaleMatrix(0.05, 4))
244                 obj = self.scene.objects.new(mesh, name)
245                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
246
247
248
249 class Ballast(Item):
250         def __init__(self, name, center):
251                 mesh = Blender.Mesh.Primitives.Cylinder()
252                 mesh.transform(ScaleMatrix(0.05, 4))
253                 obj = self.scene.objects.new(mesh, name)
254                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
255
256
257
258 class Weight(Item):
259         def __init__(self, name, center):
260                 mesh = Blender.Mesh.Primitives.Cone()
261                 mesh.transform(ScaleMatrix(0.05, 4))
262                 obj = self.scene.objects.new(mesh, name)
263                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
264
265
266
267 class Gear(Item):
268         def __init__(self, name, center, compression):
269                 mesh = Blender.Mesh.New()
270                 mesh.verts.extend([ORIGIN, compression])
271                 mesh.edges.extend([0, 1])
272                 obj = self.scene.objects.new(mesh, name)
273                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
274
275
276
277 class Hook(Item):
278         def __init__(self, name, center, length, up_angle, dn_angle):
279                 mesh = Blender.Mesh.New()
280                 up = ORIGIN - length * math.cos(up_angle * DEG2RAD) * X - length * math.sin(up_angle * DEG2RAD) * Z
281                 dn = ORIGIN - length * math.cos(dn_angle * DEG2RAD) * X - length * math.sin(dn_angle * DEG2RAD) * Z
282                 mesh.verts.extend([ORIGIN, dn, dn + 0.05 * Y, dn - 0.05 * Y])
283                 mesh.edges.extend([[0, 1], [2, 3]])
284                 draw_dashed_line(mesh, ORIGIN, up)
285                 draw_dashed_line(mesh, ORIGIN, dn)
286                 obj = self.scene.objects.new(mesh, name)
287                 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
288
289
290
291 class Launchbar(Item):
292         def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle):
293                 mesh = Blender.Mesh.New()
294                 hb = hb - lb
295                 lb_tip = ORIGIN + lb_length * math.cos(dn_angle * DEG2RAD) * X - lb_length * math.sin(dn_angle * DEG2RAD) * Z
296                 hb_tip = hb - hb_length * math.cos(dn_angle * DEG2RAD) * X - hb_length * math.sin(dn_angle * DEG2RAD) * Z
297                 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])
298                 mesh.edges.extend([[0, 1], [1, 2], [2, 3], [4, 5], [6, 7]])
299                 draw_dashed_line(mesh, ORIGIN, lb_length * math.cos(up_angle * DEG2RAD) * X - lb_length * math.sin(up_angle * DEG2RAD) * Z)
300                 draw_dashed_line(mesh, hb, hb - hb_length * math.cos(up_angle * DEG2RAD) * X - hb_length * math.sin(up_angle * DEG2RAD) * Z)
301                 obj = self.scene.objects.new(mesh, name)
302                 obj.setMatrix(TranslationMatrix(lb) * Global.matrix)
303
304
305
306 class Hitch(Item):
307         def __init__(self, name, center):
308                 mesh = Blender.Mesh.Primitives.Circle(8, 0.1)
309                 obj = self.scene.objects.new(mesh, name)
310                 obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix)
311
312
313
314 class Thrust:
315         def set_actionpt(self, p):
316                 self.actionpt = p
317
318         def set_dir(self, d):
319                 self.thrustvector = d
320
321
322
323 class Thruster(Thrust, Item):
324         def __init__(self, name, center, thrustvector):
325                 (self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector)
326
327         def __del__(self):
328                 a = self.actionpt - self.center
329                 mesh = Blender.Mesh.New()
330                 draw_dashed_line(mesh, ORIGIN, a)
331                 draw_arrow(mesh, a, a + self.thrustvector.normalize())
332                 obj = self.scene.objects.new(mesh, self.name)
333                 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
334
335
336
337 class Propeller(Thrust, Item):
338         def __init__(self, name, center, radius):
339                 (self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X)
340
341         def __del__(self):
342                 a = self.actionpt - self.center
343                 matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
344
345                 mesh = Blender.Mesh.New()
346                 mesh.verts.extend([ORIGIN * matrix, (ORIGIN + self.radius * X) * matrix])
347                 mesh.edges.extend([[0, 1]])
348                 draw_dashed_line(mesh, ORIGIN, a)
349                 draw_arrow(mesh, a, a + self.thrustvector.normalize())
350
351                 draw_circle(mesh, 128, self.radius, matrix)
352                 obj = self.scene.objects.new(mesh, self.name)
353                 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
354
355
356
357 class Jet(Thrust, Item):
358         def __init__(self, name, center, rotate):
359                 (self.name, self.center, self.actionpt) = (name, center, center)
360                 self.thrustvector = -X * RotationMatrix(rotate, 4, "y")
361
362         def __del__(self):
363                 a = self.actionpt - self.center
364                 mesh = Blender.Mesh.New()
365                 draw_dashed_line(mesh, ORIGIN, a)
366                 draw_arrow(mesh, a, a + self.thrustvector.normalize())
367                 obj = self.scene.objects.new(mesh, self.name)
368                 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
369
370
371
372 class Fuselage(Item):
373         def __init__(self, name, a, b, width, taper, midpoint):
374                 numvert = 12
375                 angle = []
376                 for i in range(numvert):
377                         alpha = i * 2 * math.pi / float(numvert)
378                         angle.append([math.cos(alpha), math.sin(alpha)])
379
380                 axis = b - a
381                 length = axis.length
382                 mesh = Blender.Mesh.New()
383
384                 for i in range(numvert):
385                         mesh.verts.extend([[0, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
386                 for i in range(numvert):
387                         mesh.verts.extend([[midpoint * length, 0.5 * width * angle[i][0], 0.5 * width * angle[i][1]]])
388                 for i in range(numvert):
389                         mesh.verts.extend([[length, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
390                 for i in range(numvert):
391                         i1 = (i + 1) % numvert
392                         mesh.faces.extend([[i, i1, i1 + numvert, i + numvert]])
393                         mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]])
394
395                 mesh.verts.extend([ORIGIN, length * X])
396                 obj = self.scene.objects.new(mesh, name)
397                 obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix)
398                 self.set_color(obj, name + "mat", [0, 0, 0.5, 0.4])
399
400
401
402 class Rotor(Item):
403         def __init__(self, name, center, up, fwd, numblades, radius, chord, twist, taper, rel_len_blade_start, phi0, ccw):
404                 matrix = RotationMatrix(phi0, 4, "z") * up.toTrackQuat('z', 'x').toMatrix().resize4x4()
405                 invert = matrix.copy().invert()
406                 direction = [-1, 1][ccw]
407                 twist *= DEG2RAD
408                 a = ORIGIN + rel_len_blade_start * radius * X
409                 b = ORIGIN + radius * X
410                 tw = 0.5 * chord * taper * math.cos(twist) * Y + 0.5 * direction * chord * taper * math.sin(twist) * Z
411
412                 mesh = Blender.Mesh.New()
413                 mesh.verts.extend([ORIGIN, a, b, a + 0.5 * chord * Y, a - 0.5 * chord * Y, b + tw, b - tw])
414                 mesh.edges.extend([[0, 1], [1, 2], [1, 3], [1, 4], [3, 5], [4, 6], [5, 6]])
415                 draw_circle(mesh, 64, rel_len_blade_start * radius, Matrix())
416                 draw_circle(mesh, 128, radius, Matrix())
417                 draw_arrow(mesh, ORIGIN, up * invert)
418                 draw_arrow(mesh, ORIGIN, fwd * invert)
419                 b += 0.1 * X + direction * chord * Y
420                 draw_arrow(mesh, b, b + 0.5 * radius * direction * Y)
421                 obj = self.scene.objects.new(mesh, name)
422                 obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix)
423
424
425
426 class Wing(Item):
427         def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral):
428                 #  <1--0--2
429                 #   \  |  /
430                 #    4-3-5
431                 self.is_symmetric = not name.startswith("YASim_vstab#")
432                 mesh = Blender.Mesh.New()
433                 mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X])
434                 tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X
435                 tipfore = tip + 0.5 * taper * chord * math.cos(twist * DEG2RAD) * X + 0.5 * taper * chord * math.sin(twist * DEG2RAD) * Z
436                 tipaft = tip + tip - tipfore
437                 mesh.verts.extend([tip, tipfore, tipaft])
438                 mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]])
439
440                 self.make_twosided(mesh)
441
442                 obj = self.scene.objects.new(mesh, name)
443                 mesh.transform(Euler(dihedral, -incidence, 0).toMatrix().resize4x4())
444                 self.set_color(obj, name + "mat", [[0.5, 0.0, 0, 0.5], [0.0, 0.5, 0, 0.5]][self.is_symmetric])
445                 (self.obj, self.mesh) = (obj, mesh)
446
447                 if self.is_symmetric and Global.mirror.val:
448                         mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR)
449                         mod[Blender.Modifier.Settings.AXIS_X] = False
450                         mod[Blender.Modifier.Settings.AXIS_Y] = True
451                         mod[Blender.Modifier.Settings.AXIS_Z] = False
452                         mesh.transform(TranslationMatrix(root)) # must move object center to x axis
453                         obj.setMatrix(Global.matrix)
454                 else:
455                         obj.setMatrix(TranslationMatrix(root) * Global.matrix)
456
457         def add_flap(self, name, start, end):
458                 a = Vector(self.mesh.verts[2].co)
459                 b = Vector(self.mesh.verts[5].co)
460                 c = 0.2 * (Vector(self.mesh.verts[0].co - a)).normalize()
461                 m = self.obj.getMatrix()
462
463                 mesh = Blender.Mesh.New()
464                 i0 = a + start * (b - a)
465                 i1 = a + end * (b - a)
466                 mesh.verts.extend([i0, i1, i0 + c, i1 + c])
467                 mesh.faces.extend([[0, 1, 3, 2]])
468
469                 self.make_twosided(mesh)
470
471                 obj = self.scene.objects.new(mesh, name)
472                 obj.setMatrix(m)
473                 self.set_color(obj, name + "mat", [0.8, 0.8, 0, 0.9])
474
475                 if self.is_symmetric and Global.mirror.val:
476                         mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR)
477                         mod[Blender.Modifier.Settings.AXIS_X] = False
478                         mod[Blender.Modifier.Settings.AXIS_Y] = True
479                         mod[Blender.Modifier.Settings.AXIS_Z] = False
480
481
482
483 class import_yasim(handler.ContentHandler):
484         ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \
485                         "control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \
486                         "rotorgear", "tow", "winch", "solve-weight"]
487
488         # err_handler
489         def error(self, exception):
490                 raise Abort(str(exception))
491
492         def fatalError(self, exception):
493                 raise Abort(str(exception))
494
495         def warning(self, exception):
496                 print(("WARNING: " + str(exception)))
497
498         # doc_handler
499         def setDocumentLocator(self, whatever):
500                 pass
501
502         def startDocument(self):
503                 self.tags = []
504                 self.counter = {}
505                 self.items = [None]
506                 pass
507
508         def endDocument(self):
509                 for o in Item.scene.objects:
510                         o.sel = True
511
512         def characters(self, data):
513                 pass
514
515         def ignorableWhitespace(self, data, start, length):
516                 pass
517
518         def processingInstruction(self, target, data):
519                 pass
520
521         def startElement(self, tag, attrs):
522                 if len(self.tags) == 0 and tag != "airplane":
523                         raise Abort("this isn't a YASim config file")
524
525                 self.tags.append(tag)
526                 path = string.join(self.tags, '/')
527                 item = Item()
528                 parent = self.items[-1]
529
530                 if self.counter.has_key(tag):
531                         self.counter[tag] += 1
532                 else:
533                         self.counter[tag] = 0
534
535                 if tag == "cockpit":
536                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
537                         log("\033[31mcockpit x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
538                         item = Cockpit(c)
539
540                 elif tag == "fuselage":
541                         a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"]))
542                         b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"]))
543                         width = float(attrs["width"])
544                         taper = getfloat(attrs, "taper", 1)
545                         midpoint = getfloat(attrs, "midpoint", 0.5)
546                         log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \
547                                         (a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint))
548                         item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint)
549
550                 elif tag == "gear":
551                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
552                         compression = getfloat(attrs, "compression", 1)
553                         up = Z * compression
554                         if attrs.has_key("upx"):
555                                 up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression
556                         log("\033[35;1mgear x=%f y=%f z=%f compression=%f upx=%f upy=%f upz=%f\033[m" \
557                                         % (c[0], c[1], c[2], compression, up[0], up[1], up[2]))
558                         item = Gear("YASim_gear#%d" % self.counter[tag], c, up)
559
560                 elif tag == "jet":
561                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
562                         rotate = getfloat(attrs, "rotate", 0.0)
563                         log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate))
564                         item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate)
565
566                 elif tag == "propeller":
567                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
568                         radius = float(attrs["radius"])
569                         log("\033[36;1m%s x=%f y=%f z=%f radius=%f\033[m" % (tag, c[0], c[1], c[2], radius))
570                         item = Propeller("YASim_propeller#%d" % self.counter[tag], c, radius)
571
572                 elif tag == "thruster":
573                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
574                         v = Vector(float(attrs["vx"]), float(attrs["vy"]), float(attrs["vz"]))
575                         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]))
576                         item = Thruster("YASim_thruster#%d" % self.counter[tag], c, v)
577
578                 elif tag == "actionpt":
579                         if not isinstance(parent, Thrust):
580                                 raise Abort("%s is not part of a thruster/propeller/jet" % path)
581
582                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
583                         log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
584                         parent.set_actionpt(c)
585
586                 elif tag == "dir":
587                         if not isinstance(parent, Thrust):
588                                 raise Abort("%s is not part of a thruster/propeller/jet" % path)
589
590                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
591                         log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
592                         parent.set_dir(c)
593
594                 elif tag == "tank":
595                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
596                         log("\033[34;1m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
597                         item = Tank("YASim_tank#%d" % self.counter[tag], c)
598
599                 elif tag == "ballast":
600                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
601                         log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
602                         item = Ballast("YASim_ballast#%d" % self.counter[tag], c)
603
604                 elif tag == "weight":
605                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
606                         log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
607                         item = Weight("YASim_weight#%d" % self.counter[tag], c)
608
609                 elif tag == "hook":
610                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
611                         length = getfloat(attrs, "length", 1.0)
612                         up_angle = getfloat(attrs, "up-angle", 0.0)
613                         down_angle = getfloat(attrs, "down-angle", 70.0)
614                         log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \
615                                         % (tag, c[0], c[1], c[2], length, up_angle, down_angle))
616                         item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle)
617
618                 elif tag == "hitch":
619                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
620                         log("\033[35m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
621                         item = Hitch("YASim_hitch#%d" % self.counter[tag], c)
622
623                 elif tag == "launchbar":
624                         c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
625                         length = getfloat(attrs, "length", 1.0)
626                         up_angle = getfloat(attrs, "up-angle", -45.0)
627                         down_angle = getfloat(attrs, "down-angle", 45.0)
628                         holdback = Vector(getfloat(attrs, "holdback-x", c[0]), getfloat(attrs, "holdback-y", c[1]), getfloat(attrs, "holdback-z", c[2]))
629                         holdback_length = getfloat(attrs, "holdback-length", 2.0)
630                         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" \
631                                         % (tag, c[0], c[1], c[2], length, down_angle, up_angle, \
632                                         holdback[0], holdback[1], holdback[2], holdback_length))
633                         item = Launchbar("YASim_launchbar#%d" % self.counter[tag], c, length, holdback, holdback_length, up_angle, down_angle)
634
635                 elif tag == "wing" or tag == "hstab" or tag == "vstab" or tag == "mstab":
636                         root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
637                         length = float(attrs["length"])
638                         chord = float(attrs["chord"])
639                         incidence = getfloat(attrs, "incidence", 0.0)
640                         twist = getfloat(attrs, "twist", 0.0)
641                         taper = getfloat(attrs, "taper", 1.0)
642                         sweep = getfloat(attrs, "sweep", 0.0)
643                         dihedral = getfloat(attrs, "dihedral", [0.0, 90.0][tag == "vstab"])
644                         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" \
645                                         % (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral))
646                         item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral)
647
648                 elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler":
649                         if not isinstance(parent, Wing):
650                                 raise Abort("%s is not part of a wing or stab" % path)
651
652                         start = float(attrs["start"])
653                         end = float(attrs["end"])
654                         log("\t\033[33m%s start=%f end=%f\033[m" % (tag, start, end))
655                         parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end)
656
657                 elif tag == "rotor":
658                         c = Vector(getfloat(attrs, "x", 0.0), getfloat(attrs, "y", 0.0), getfloat(attrs, "z", 0.0))
659                         norm = Vector(getfloat(attrs, "nx", 0.0), getfloat(attrs, "ny", 0.0), getfloat(attrs, "nz", 1.0))
660                         fwd = Vector(getfloat(attrs, "fx", 1.0), getfloat(attrs, "fy", 0.0), getfloat(attrs, "fz", 0.0))
661                         diameter = getfloat(attrs, "diameter", 10.2)
662                         numblades = int(getfloat(attrs, "numblades", 4))
663                         chord = getfloat(attrs, "chord", 0.3)
664                         twist = getfloat(attrs, "twist", 0.0)
665                         taper = getfloat(attrs, "taper", 1.0)
666                         rel_len_blade_start = getfloat(attrs, "rel-len-blade-start", 0.0)
667                         phi0 = getfloat(attrs, "phi0", 0)
668                         ccw = not not getfloat(attrs, "ccw", 0)
669
670                         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 " \
671                                         + "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \
672                                         % (c[0], c[1], c[2], norm[0], norm[1], norm[2], fwd[0], fwd[1], fwd[2], numblades, \
673                                         diameter, chord, twist, taper, rel_len_blade_start, phi0, ccw))
674                         item = Rotor("YASim_rotor#%d" % self.counter[tag], c, norm, fwd, numblades, 0.5 * diameter, chord, \
675                                         twist, taper, rel_len_blade_start, phi0, ccw)
676
677                 elif tag not in self.ignored:
678                         log("\033[30;1m%s\033[m" % path)
679
680                 self.items.append(item)
681
682         def endElement(self, tag):
683                 self.tags.pop()
684                 self.items.pop()
685
686
687
688 def extract_matrix(path, tag):
689         v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 }
690         has_offsets = False
691         f = open(path)
692         for line in f.readlines():
693                 line = string.strip(line)
694                 if not line.startswith("<!--") or not line.endswith("-->"):
695                         continue
696                 line = string.strip(line[4:-3])
697                 if not string.lower(line).startswith("%s:" % tag):
698                         continue
699                 line = string.strip(line[len(tag) + 1:])
700                 for assignment in string.split(line):
701                         (key, value) = string.split(assignment, '=', 2)
702                         v[string.strip(key)] = float(string.strip(value))
703                         has_offsets = True
704         f.close()
705         matrix = None
706         if has_offsets:
707                 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'])))
708                 matrix = Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4()
709                 matrix *= TranslationMatrix(Vector(v['x'], v['y'], v['z']))
710         return matrix
711
712
713
714 def load_yasim_config(path):
715         if BPyMessages.Error_NoFile(path):
716                 return
717
718         Blender.Window.WaitCursor(1)
719         Blender.Window.EditMode(0)
720
721         print(("loading '%s'" % path))
722         try:
723                 for o in Item.scene.objects:
724                         if o.name.startswith("YASim_"):
725                                 Item.scene.objects.unlink(o)
726
727                 Global.matrix = YASIM_MATRIX
728                 matrix = extract_matrix(path, "offsets")
729                 if matrix:
730                         Global.matrix *= matrix.invert()
731
732                 yasim = make_parser()
733                 yasim.setContentHandler(import_yasim())
734                 yasim.setErrorHandler(import_yasim())
735                 yasim.parse(path)
736
737                 Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
738                 Global.path = path
739
740         except Abort, e:
741                 print "Error:", e.msg, "  -> aborting ...\n"
742                 Blender.Draw.PupMenu("Error%t|" + e.msg)
743
744         Blender.Window.RedrawAll()
745         Blender.Window.WaitCursor(0)
746
747
748
749 def gui_draw():
750         from Blender import BGL, Draw
751         (width, height) = Blender.Window.GetAreaSize()
752
753         BGL.glClearColor(0.4, 0.4, 0.45, 1)
754         BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
755
756         BGL.glColor3f(1, 1, 1)
757         BGL.glRasterPos2f(5, 55)
758         Draw.Text("FlightGear YASim Import:   '%s'" % Global.path)
759
760         Draw.PushButton("Reload", RELOAD_BUTTON, 5, 5, 80, 32, "reload YASim config file")
761         Global.mirror = Draw.Toggle("Mirror", MIRROR_BUTTON, 100, 5, 50, 16, Global.mirror.val, \
762                         "show symmetric surfaces on both sides (reloads config)")
763         Draw.PushButton("Update Cursor", CURSOR_BUTTON, width - 650, 5, 100, 32, "update cursor display")
764
765         BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Vector from last") - Blender.Draw.GetStringWidth("Current"), 24)
766         Draw.Text("Current cursor pos:    x = %+.3f    y = %+.3f    z = %+.3f" % tuple(Global.cursor))
767
768         c = Global.cursor - Global.last_cursor
769         BGL.glRasterPos2f(width - 530, 7)
770         Draw.Text("Vector from last cursor pos:    x = %+.3f    y = %+.3f    z = %+.3f    length = %.3f" % (c[0], c[1], c[2], c.length))
771
772
773
774 def gui_event(ev, value):
775         if ev == Blender.Draw.ESCKEY:
776                 Blender.Draw.Exit()
777
778
779
780 def gui_button(n):
781         if n == NO_EVENT:
782                 return
783
784         elif n == RELOAD_BUTTON:
785                 load_yasim_config(Global.path)
786
787         elif n == CURSOR_BUTTON:
788                 Global.last_cursor = Global.cursor
789                 Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert()
790                 d = Global.cursor - Global.last_cursor
791                 print(("cursor:   x=\"%f\" y=\"%f\" z=\"%f\"   dx=%f dy=%f dz=%f   length=%f" \
792                                 % (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length)))
793
794         elif n == MIRROR_BUTTON:
795                 load_yasim_config(Global.path)
796
797         Blender.Draw.Redraw(1)
798
799
800
801 def main():
802         log(6 * "\n")
803         registry = Blender.Registry.GetKey("FGYASimImportExport", False)
804         if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
805                 path = registry["path"]
806         else:
807                 path = ""
808
809         if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
810                 Blender.Draw.Register(gui_draw, gui_event, gui_button)
811
812         Blender.Window.FileSelector(load_yasim_config, "Import YASim Configuration File", path)
813
814
815
816 main()
817