7 # Tooltip: 'Loads and visualizes a YASim FDM geometry'
10 __author__ = "Melchior FRANZ < mfranz # aon : at >"
11 __url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/yasim_import.py"]
14 yasim_import.py loads and visualizes a YASim FDM geometry
15 =========================================================
17 It is recommended to load the model superimposed over a greyed out and immutable copy of the aircraft model:
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 in "Buttons Window" -> arrow button left of text entry "No Set Scene" -> "scene1")
24 (5) now load the YASim config file (menu -> "File" -> "Import" -> "YASim (.xml) ...")
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):
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
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:
43 <pitch-deg>5</pitch-deg>
48 <!-- offsets: x=3.45 z=-0.4 p=5 -->
50 Possible variables are:
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.
67 Elements are displayed as follows:
69 cockpit -> monkey head
70 fuselage -> blue "tube" (with only 12 sides for less clutter); center at "a"
71 vstab -> red with yellow control 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, unless
74 the "Mirror" button is active
75 thrusters (jet/propeller/thruster) -> dashed line from center to actionpt;
76 arrow from actionpt along thrust vector (always 1 m long);
78 rotor -> radius and rel_len_blade_start circle, normal and forward vector,
79 one blade at phi0 with direction arrow near blade tip
80 gear -> contact point and compression vector (no arrow head)
81 tank -> cube (10 cm side length)
82 weight -> inverted cone
84 hitch -> circle (10 cm diameter)
85 hook -> dashed line for up angle, T-line for down angle
86 launchbar -> dashed line for up angles, T-line for down angles
87 (launchbar and holdback each)
90 The Mirror button complements symmetrical surfaces (wing/hstab/mstab) and control surfaces
91 (flap0/flap1/slat/spoiler). This is useful for asymmetrical aircraft, but has the disadvantage
92 that it moves the surfaces' object centers from their usual place, yasim's [x, y, z] value,
93 to [0, 0, 0]. Turning mirroring off restores the object center.
97 Environment variable BLENDER_YASIM_IMPORT can be set to a space-separated list of options:
99 $ BLENDER_YASIM_IMPORT="mirror verbose" blender
103 verbose ... enables verbose logs
104 mirror ... enables mirroring of symmetric surfaces
108 #--------------------------------------------------------------------------------
109 # Copyright (C) 2009 Melchior FRANZ < mfranz # aon : at >
111 # This program is free software; you can redistribute it and/or
112 # modify it under the terms of the GNU General Public License as
113 # published by the Free Software Foundation; either version 2 of the
114 # License, or (at your option) any later version.
116 # This program is distributed in the hope that it will be useful, but
117 # WITHOUT ANY WARRANTY; without even the implied warranty of
118 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
119 # General Public License for more details.
121 # You should have received a copy of the GNU General Public License
122 # along with this program; if not, write to the Free Software
123 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
124 #--------------------------------------------------------------------------------
127 import Blender, BPyMessages, string, math, os
128 from Blender.Mathutils import *
129 from xml.sax import handler, make_parser
132 CONFIG = string.split(os.getenv("BLENDER_YASIM_IMPORT") or "")
133 YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])
134 ORIGIN = Vector(0, 0, 0)
138 DEG2RAD = math.pi / 180
139 RAD2DEG = 180 / math.pi
149 verbose = "verbose" in CONFIG
154 last_cursor = Vector(Blender.Window.GetCursorPos())
155 mirror_button = Blender.Draw.Create("mirror" in CONFIG)
159 class Abort(Exception):
160 def __init__(self, msg, term = None):
172 def draw_dashed_line(mesh, start, end):
174 step = w * (end - start).normalize()
176 for i in range(int(1 + 0.5 * (end - start).length / w)):
177 a = start + 2 * i * step
179 if (b - end).length < step.length:
181 mesh.verts.extend([a, b])
182 mesh.edges.extend([n + 2 * i, n + 2 * i + 1])
186 def draw_arrow(mesh, start, end):
188 m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start)
191 mesh.verts.extend([ORIGIN * m , v * m, (v - 0.05 * X + 0.05 * Y) * m, (v - 0.05 * X - 0.05 * Y) * m]) # head
192 mesh.verts.extend([(ORIGIN + 0.05 * Y) * m, (ORIGIN - 0.05 * Y) * m]) # base
193 mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]])
197 def draw_circle(mesh, numpoints, radius, matrix):
199 for i in range(numpoints):
200 angle = 2.0 * math.pi * i / numpoints
201 v = Vector(radius * math.cos(angle), radius * math.sin(angle), 0)
202 mesh.verts.extend([v * matrix])
203 for i in range(numpoints):
204 i1 = (i + 1) % numpoints
205 mesh.edges.extend([[n + i, n + i1]])
210 scene = Blender.Scene.GetCurrent()
212 def make_twosided(self, mesh):
215 f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
217 def set_color(self, obj, color):
218 mat = Blender.Material.New()
219 mat.setRGBCol(color[0], color[1], color[2])
220 mat.setAlpha(color[3])
221 mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW
224 mesh = obj.getData(mesh = True)
225 mesh.materials += [mat]
234 def __init__(self, center):
235 mesh = Blender.Mesh.Primitives.Monkey()
236 mesh.transform(ScaleMatrix(0.13, 4) * Euler(90, 0, 90).toMatrix().resize4x4() * TranslationMatrix(Vector(-0.1, 0, -0.032)))
237 obj = self.scene.objects.new(mesh, "YASim_cockpit")
238 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
243 def __init__(self, name, center):
244 mesh = Blender.Mesh.Primitives.Cube()
245 mesh.transform(ScaleMatrix(0.05, 4))
246 obj = self.scene.objects.new(mesh, name)
247 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
248 self.set_color(obj, [1, 0, 1, 0.5])
253 def __init__(self, name, center):
254 mesh = Blender.Mesh.Primitives.Cylinder()
255 mesh.transform(ScaleMatrix(0.05, 4))
256 obj = self.scene.objects.new(mesh, name)
257 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
258 self.set_color(obj, [1, 1, 0, 0.5])
263 def __init__(self, name, center):
264 mesh = Blender.Mesh.Primitives.Cone()
265 mesh.transform(ScaleMatrix(0.05, 4))
266 obj = self.scene.objects.new(mesh, name)
267 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
268 self.set_color(obj, [0, 1, 1, 0.5])
273 def __init__(self, name, center, compression):
274 mesh = Blender.Mesh.New()
275 mesh.verts.extend([ORIGIN, compression])
276 mesh.edges.extend([0, 1])
277 obj = self.scene.objects.new(mesh, name)
278 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
283 def __init__(self, name, center, length, up_angle, dn_angle):
284 mesh = Blender.Mesh.New()
285 up = ORIGIN - length * math.cos(up_angle * DEG2RAD) * X - length * math.sin(up_angle * DEG2RAD) * Z
286 dn = ORIGIN - length * math.cos(dn_angle * DEG2RAD) * X - length * math.sin(dn_angle * DEG2RAD) * Z
287 mesh.verts.extend([ORIGIN, dn, dn + 0.05 * Y, dn - 0.05 * Y])
288 mesh.edges.extend([[0, 1], [2, 3]])
289 draw_dashed_line(mesh, ORIGIN, up)
290 draw_dashed_line(mesh, ORIGIN, dn)
291 obj = self.scene.objects.new(mesh, name)
292 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
296 class Launchbar(Item):
297 def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle):
298 mesh = Blender.Mesh.New()
300 lb_tip = ORIGIN + lb_length * math.cos(dn_angle * DEG2RAD) * X - lb_length * math.sin(dn_angle * DEG2RAD) * Z
301 hb_tip = hb - hb_length * math.cos(dn_angle * DEG2RAD) * X - hb_length * math.sin(dn_angle * DEG2RAD) * Z
302 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])
303 mesh.edges.extend([[0, 1], [1, 2], [2, 3], [4, 5], [6, 7]])
304 draw_dashed_line(mesh, ORIGIN, lb_length * math.cos(up_angle * DEG2RAD) * X - lb_length * math.sin(up_angle * DEG2RAD) * Z)
305 draw_dashed_line(mesh, hb, hb - hb_length * math.cos(up_angle * DEG2RAD) * X - hb_length * math.sin(up_angle * DEG2RAD) * Z)
306 obj = self.scene.objects.new(mesh, name)
307 obj.setMatrix(TranslationMatrix(lb) * Global.matrix)
312 def __init__(self, name, center):
313 mesh = Blender.Mesh.Primitives.Circle(8, 0.1)
314 obj = self.scene.objects.new(mesh, name)
315 obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix)
320 def set_actionpt(self, p):
323 def set_dir(self, d):
324 self.thrustvector = d
328 class Thruster(Thrust, Item):
329 def __init__(self, name, center, thrustvector):
330 (self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector)
333 a = self.actionpt - self.center
334 mesh = Blender.Mesh.New()
335 draw_dashed_line(mesh, ORIGIN, a)
336 draw_arrow(mesh, a, a + self.thrustvector.normalize())
337 obj = self.scene.objects.new(mesh, self.name)
338 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
342 class Propeller(Thrust, Item):
343 def __init__(self, name, center, radius):
344 (self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X)
347 a = self.actionpt - self.center
348 matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
350 mesh = Blender.Mesh.New()
351 mesh.verts.extend([ORIGIN * matrix, (ORIGIN + self.radius * X) * matrix])
352 mesh.edges.extend([[0, 1]])
353 draw_dashed_line(mesh, ORIGIN, a)
354 draw_arrow(mesh, a, a + self.thrustvector.normalize())
356 draw_circle(mesh, 128, self.radius, matrix)
357 obj = self.scene.objects.new(mesh, self.name)
358 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
362 class Jet(Thrust, Item):
363 def __init__(self, name, center, rotate):
364 (self.name, self.center, self.actionpt) = (name, center, center)
365 self.thrustvector = -X * RotationMatrix(rotate, 4, "y")
368 a = self.actionpt - self.center
369 mesh = Blender.Mesh.New()
370 draw_dashed_line(mesh, ORIGIN, a)
371 draw_arrow(mesh, a, a + self.thrustvector.normalize())
372 obj = self.scene.objects.new(mesh, self.name)
373 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
377 class Fuselage(Item):
378 def __init__(self, name, a, b, width, taper, midpoint):
381 for i in range(numvert):
382 alpha = i * 2 * math.pi / float(numvert)
383 angle.append([math.cos(alpha), math.sin(alpha)])
387 mesh = Blender.Mesh.New()
389 for i in range(numvert):
390 mesh.verts.extend([[0, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
391 for i in range(numvert):
392 mesh.verts.extend([[midpoint * length, 0.5 * width * angle[i][0], 0.5 * width * angle[i][1]]])
393 for i in range(numvert):
394 mesh.verts.extend([[length, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
395 for i in range(numvert):
396 i1 = (i + 1) % numvert
397 mesh.faces.extend([[i, i1, i1 + numvert, i + numvert]])
398 mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]])
400 mesh.verts.extend([ORIGIN, length * X])
401 obj = self.scene.objects.new(mesh, name)
402 obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix)
403 self.set_color(obj, [0, 0, 0.5, 0.4])
408 def __init__(self, name, center, up, fwd, numblades, radius, chord, twist, taper, rel_len_blade_start, phi0, ccw):
409 matrix = RotationMatrix(phi0, 4, "z") * up.toTrackQuat('z', 'x').toMatrix().resize4x4()
410 invert = matrix.copy().invert()
411 direction = [-1, 1][ccw]
413 a = ORIGIN + rel_len_blade_start * radius * X
414 b = ORIGIN + radius * X
415 tw = 0.5 * chord * taper * math.cos(twist) * Y + 0.5 * direction * chord * taper * math.sin(twist) * Z
417 mesh = Blender.Mesh.New()
418 mesh.verts.extend([ORIGIN, a, b, a + 0.5 * chord * Y, a - 0.5 * chord * Y, b + tw, b - tw])
419 mesh.edges.extend([[0, 1], [1, 2], [1, 3], [1, 4], [3, 5], [4, 6], [5, 6]])
420 draw_circle(mesh, 64, rel_len_blade_start * radius, Matrix())
421 draw_circle(mesh, 128, radius, Matrix())
422 draw_arrow(mesh, ORIGIN, up * invert)
423 draw_arrow(mesh, ORIGIN, fwd * invert)
424 b += 0.1 * X + direction * chord * Y
425 draw_arrow(mesh, b, b + min(0.5 * radius, 1) * direction * Y)
426 obj = self.scene.objects.new(mesh, name)
427 obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix)
432 def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral):
436 self.is_symmetric = not name.startswith("YASim_vstab#")
437 mesh = Blender.Mesh.New()
438 mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X])
439 tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X
440 tipfore = tip + 0.5 * taper * chord * math.cos(twist * DEG2RAD) * X + 0.5 * taper * chord * math.sin(twist * DEG2RAD) * Z
441 tipaft = tip + tip - tipfore
442 mesh.verts.extend([tip, tipfore, tipaft])
443 mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]])
445 self.make_twosided(mesh)
447 obj = self.scene.objects.new(mesh, name)
448 mesh.transform(Euler(dihedral, -incidence, 0).toMatrix().resize4x4())
449 self.set_color(obj, [[0.5, 0.0, 0, 0.5], [0.0, 0.5, 0, 0.5]][self.is_symmetric])
450 (self.obj, self.mesh) = (obj, mesh)
452 if self.is_symmetric and Global.mirror_button.val:
453 mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR)
454 mod[Blender.Modifier.Settings.AXIS_X] = False
455 mod[Blender.Modifier.Settings.AXIS_Y] = True
456 mod[Blender.Modifier.Settings.AXIS_Z] = False
457 mesh.transform(TranslationMatrix(root)) # must move object center to x axis
458 obj.setMatrix(Global.matrix)
460 obj.setMatrix(TranslationMatrix(root) * Global.matrix)
462 def add_flap(self, name, start, end):
463 a = Vector(self.mesh.verts[2].co)
464 b = Vector(self.mesh.verts[5].co)
465 c = 0.2 * (Vector(self.mesh.verts[0].co - a)).normalize()
466 m = self.obj.getMatrix()
468 mesh = Blender.Mesh.New()
469 i0 = a + start * (b - a)
470 i1 = a + end * (b - a)
471 mesh.verts.extend([i0, i1, i0 + c, i1 + c])
472 mesh.faces.extend([[0, 1, 3, 2]])
474 self.make_twosided(mesh)
476 obj = self.scene.objects.new(mesh, name)
478 self.set_color(obj, [0.8, 0.8, 0, 0.9])
480 if self.is_symmetric and Global.mirror_button.val:
481 mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR)
482 mod[Blender.Modifier.Settings.AXIS_X] = False
483 mod[Blender.Modifier.Settings.AXIS_Y] = True
484 mod[Blender.Modifier.Settings.AXIS_Z] = False
488 class import_yasim(handler.ErrorHandler, handler.ContentHandler):
489 ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \
490 "control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \
491 "rotorgear", "tow", "winch", "solve-weight"]
495 def warning(self, exception):
496 print(self.error_string("WARNING", exception))
498 def error(self, exception):
499 print(self.error_string("ERROR", exception))
501 def fatalError(self, exception):
502 raise Abort(str(exception), self.error_string("FATAL", exception))
504 def error_string(self, tag, e):
505 (column, line, msg) = (e.getColumnNumber(), e.getLineNumber(), e.getMessage())
506 return "\n%s%s^--------%s: %s at line %d, column %d" \
507 % (Global.data[line - 1], (column) * ' ', tag, msg, line, column)
511 def setDocumentLocator(self, locator):
512 self.locator = locator
514 def startDocument(self):
519 def endDocument(self):
520 for o in Item.scene.objects:
523 def startElement(self, tag, attrs):
524 if len(self.tags) == 0 and tag != "airplane":
525 raise Abort("this isn't a YASim config file (bad root tag at line %d)" % self.locator.getLineNumber())
527 self.tags.append(tag)
528 path = string.join(self.tags, '/')
530 parent = self.items[-1]
532 if self.counter.has_key(tag):
533 self.counter[tag] += 1
535 self.counter[tag] = 0
538 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
539 log("\033[31mcockpit x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
542 elif tag == "fuselage":
543 a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"]))
544 b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"]))
545 width = float(attrs["width"])
546 taper = float(attrs.get("taper", 1))
547 midpoint = float(attrs.get("midpoint", 0.5))
548 log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \
549 (a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint))
550 item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint)
553 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
554 compression = float(attrs.get("compression", 1))
556 if attrs.has_key("upx"):
557 up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression
558 log("\033[35;1mgear x=%f y=%f z=%f compression=%f upx=%f upy=%f upz=%f\033[m" \
559 % (c[0], c[1], c[2], compression, up[0], up[1], up[2]))
560 item = Gear("YASim_gear#%d" % self.counter[tag], c, up)
563 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
564 rotate = float(attrs.get("rotate", 0))
565 log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate))
566 item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate)
568 elif tag == "propeller":
569 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
570 radius = float(attrs["radius"])
571 log("\033[36;1m%s x=%f y=%f z=%f radius=%f\033[m" % (tag, c[0], c[1], c[2], radius))
572 item = Propeller("YASim_propeller#%d" % self.counter[tag], c, radius)
574 elif tag == "thruster":
575 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
576 v = Vector(float(attrs["vx"]), float(attrs["vy"]), float(attrs["vz"]))
577 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]))
578 item = Thruster("YASim_thruster#%d" % self.counter[tag], c, v)
580 elif tag == "actionpt":
581 if not isinstance(parent, Thrust):
582 raise Abort("%s is not part of a thruster/propeller/jet at line %d" \
583 % (path, self.locator.getLineNumber()))
585 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
586 log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
587 parent.set_actionpt(c)
590 if not isinstance(parent, Thrust):
591 raise Abort("%s is not part of a thruster/propeller/jet at line %d" \
592 % (path, self.locator.getLineNumber()))
594 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
595 log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
599 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
600 log("\033[34;1m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
601 item = Tank("YASim_tank#%d" % self.counter[tag], c)
603 elif tag == "ballast":
604 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
605 log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
606 item = Ballast("YASim_ballast#%d" % self.counter[tag], c)
608 elif tag == "weight":
609 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
610 log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
611 item = Weight("YASim_weight#%d" % self.counter[tag], c)
614 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
615 length = float(attrs.get("length", 1))
616 up_angle = float(attrs.get("up-angle", 0))
617 down_angle = float(attrs.get("down-angle", 70))
618 log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \
619 % (tag, c[0], c[1], c[2], length, up_angle, down_angle))
620 item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle)
623 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
624 log("\033[35m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
625 item = Hitch("YASim_hitch#%d" % self.counter[tag], c)
627 elif tag == "launchbar":
628 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
629 length = float(attrs.get("length", 1))
630 up_angle = float(attrs.get("up-angle", -45))
631 down_angle = float(attrs.get("down-angle", 45))
632 holdback = Vector(float(attrs.get("holdback-x", c[0])), float(attrs.get("holdback-y", c[1])), float(attrs.get("holdback-z", c[2])))
633 holdback_length = float(attrs.get("holdback-length", 2))
634 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" \
635 % (tag, c[0], c[1], c[2], length, down_angle, up_angle, \
636 holdback[0], holdback[1], holdback[2], holdback_length))
637 item = Launchbar("YASim_launchbar#%d" % self.counter[tag], c, length, holdback, holdback_length, up_angle, down_angle)
639 elif tag == "wing" or tag == "hstab" or tag == "vstab" or tag == "mstab":
640 root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
641 length = float(attrs["length"])
642 chord = float(attrs["chord"])
643 incidence = float(attrs.get("incidence", 0))
644 twist = float(attrs.get("twist", 0))
645 taper = float(attrs.get("taper", 1))
646 sweep = float(attrs.get("sweep", 0))
647 dihedral = float(attrs.get("dihedral", [0, 90][tag == "vstab"]))
648 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" \
649 % (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral))
650 item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral)
652 elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler":
653 if not isinstance(parent, Wing):
654 raise Abort("%s is not part of a wing or stab at line %d" \
655 % (path, self.locator.getLineNumber()))
657 start = float(attrs["start"])
658 end = float(attrs["end"])
659 log("\t\033[33m%s start=%f end=%f\033[m" % (tag, start, end))
660 parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end)
663 c = Vector(float(attrs.get("x", 0)), float(attrs.get("y", 0)), float(attrs.get("z", 0)))
664 norm = Vector(float(attrs.get("nx", 0)), float(attrs.get("ny", 0)), float(attrs.get("nz", 1)))
665 fwd = Vector(float(attrs.get("fx", 1)), float(attrs.get("fy", 0)), float(attrs.get("fz", 0)))
666 diameter = float(attrs.get("diameter", 10.2))
667 numblades = int(attrs.get("numblades", 4))
668 chord = float(attrs.get("chord", 0.3))
669 twist = float(attrs.get("twist", 0))
670 taper = float(attrs.get("taper", 1))
671 rel_len_blade_start = float(attrs.get("rel-len-blade-start", 0))
672 phi0 = float(attrs.get("phi0", 0))
673 ccw = not not int(attrs.get("ccw", 0))
675 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 " \
676 + "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \
677 % (c[0], c[1], c[2], norm[0], norm[1], norm[2], fwd[0], fwd[1], fwd[2], numblades, \
678 diameter, chord, twist, taper, rel_len_blade_start, phi0, ccw))
679 item = Rotor("YASim_rotor#%d" % self.counter[tag], c, norm, fwd, numblades, 0.5 * diameter, chord, \
680 twist, taper, rel_len_blade_start, phi0, ccw)
682 elif tag not in self.ignored:
683 log("\033[30;1m%s\033[m" % path)
685 self.items.append(item)
687 def endElement(self, tag):
693 def extract_matrix(filedata, tag):
694 v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 }
696 for line in filedata:
697 line = string.strip(line)
698 if not line.startswith("<!--") or not line.endswith("-->"):
700 line = string.strip(line[4:-3])
701 if not string.lower(line).startswith("%s:" % tag):
703 line = string.strip(line[len(tag) + 1:])
704 for assignment in string.split(line):
705 (key, value) = string.split(assignment, '=', 2)
706 v[string.strip(key)] = float(string.strip(value))
712 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'])))
713 return Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4() * TranslationMatrix(Vector(v['x'], v['y'], v['z']))
717 def load_yasim_config(path):
718 if BPyMessages.Error_NoFile(path):
721 Blender.Window.WaitCursor(1)
722 Blender.Window.EditMode(0)
724 print(("loading '%s'" % path))
726 for o in Item.scene.objects:
727 if o.name.startswith("YASim_"):
728 Item.scene.objects.unlink(o)
731 Global.data = f.readlines()
735 Global.matrix = YASIM_MATRIX
736 matrix = extract_matrix(Global.data, "offsets")
738 Global.matrix *= matrix.invert()
740 Global.yasim.parse(path)
741 Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
745 print("%s\nAborting ..." % (e.term or e.msg))
746 Blender.Draw.PupMenu("Error%t|" + e.msg)
748 Blender.Window.RedrawAll()
749 Blender.Window.WaitCursor(0)
754 from Blender import BGL, Draw
755 (width, height) = Blender.Window.GetAreaSize()
757 BGL.glClearColor(0.4, 0.4, 0.45, 1)
758 BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
760 BGL.glColor3f(1, 1, 1)
761 BGL.glRasterPos2f(5, 55)
762 Draw.Text("FlightGear YASim Import: '%s'" % Global.path)
764 Draw.PushButton("Reload", RELOAD_BUTTON, 5, 5, 80, 32, "reload YASim config file")
765 Global.mirror_button = Draw.Toggle("Mirror", MIRROR_BUTTON, 100, 5, 50, 16, Global.mirror_button.val, \
766 "show symmetric surfaces on both sides (reloads config)")
767 Draw.PushButton("Update Cursor", CURSOR_BUTTON, width - 650, 5, 100, 32, "update cursor display (in YASim coordinate system)")
769 BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Vector from last") - Blender.Draw.GetStringWidth("Current"), 24)
770 Draw.Text("Current cursor pos: x = %+.3f y = %+.3f z = %+.3f" % tuple(Global.cursor))
772 c = Global.cursor - Global.last_cursor
773 BGL.glRasterPos2f(width - 530, 7)
774 Draw.Text("Vector from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f m" % (c[0], c[1], c[2], c.length))
778 def gui_event(ev, value):
779 if ev == Blender.Draw.ESCKEY:
788 elif n == RELOAD_BUTTON:
789 load_yasim_config(Global.path)
791 elif n == CURSOR_BUTTON:
792 Global.last_cursor = Global.cursor
793 Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert()
794 d = Global.cursor - Global.last_cursor
795 print(("cursor: x=\"%f\" y=\"%f\" z=\"%f\" dx=%f dy=%f dz=%f length=%f" \
796 % (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length)))
798 elif n == MIRROR_BUTTON:
799 load_yasim_config(Global.path)
801 Blender.Draw.Redraw(1)
807 registry = Blender.Registry.GetKey("FGYASimImportExport", False)
808 if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
809 path = registry["path"]
813 xml_handler = import_yasim()
814 Global.yasim = make_parser()
815 Global.yasim.setContentHandler(xml_handler)
816 Global.yasim.setErrorHandler(xml_handler)
818 if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
819 Blender.Draw.Register(gui_draw, gui_event, gui_button)
821 Blender.Window.FileSelector(load_yasim_config, "Import YASim Configuration File", path)