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 -> 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 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);
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
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
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.
95 #--------------------------------------------------------------------------------
96 # Copyright (C) 2009 Melchior FRANZ < mfranz # aon : at >
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.
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.
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 #--------------------------------------------------------------------------------
114 import Blender, BPyMessages, string, math, os
115 from Blender.Mathutils import *
116 from xml.sax import handler, make_parser
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)
125 DEG2RAD = math.pi / 180
126 RAD2DEG = 180 / math.pi
136 verbose = "verbose" in CONFIG
140 last_cursor = Vector(Blender.Window.GetCursorPos())
141 mirror = Blender.Draw.Create(0)
145 class Abort(Exception):
146 def __init__(self, msg):
158 print(("\033[31;1mError: %s\033[m" % msg))
159 Blender.Draw.PupMenu("Error%t|" + msg)
163 def getfloat(attrs, key, default):
164 if attrs.has_key(key):
165 return float(attrs[key])
170 def draw_dashed_line(mesh, start, end):
172 step = w * (end - start).normalize()
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:
179 mesh.verts.extend([a, b])
180 mesh.edges.extend([n + 2 * i, n + 2 * i + 1])
184 def draw_arrow(mesh, start, end):
186 m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start)
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]])
195 def draw_circle(mesh, numpoints, radius, matrix):
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]])
208 scene = Blender.Scene.GetCurrent()
210 def make_twosided(self, mesh):
213 f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
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
222 mesh = obj.getData(mesh = True)
223 mesh.materials += [mat]
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)
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)
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)
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)
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)
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)
291 class Launchbar(Item):
292 def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle):
293 mesh = Blender.Mesh.New()
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)
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)
315 def set_actionpt(self, p):
318 def set_dir(self, d):
319 self.thrustvector = d
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)
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)
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)
342 a = self.actionpt - self.center
343 matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
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())
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)
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")
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)
372 class Fuselage(Item):
373 def __init__(self, name, a, b, width, taper, midpoint):
376 for i in range(numvert):
377 alpha = i * 2 * math.pi / float(numvert)
378 angle.append([math.cos(alpha), math.sin(alpha)])
382 mesh = Blender.Mesh.New()
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]])
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])
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]
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
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)
427 def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral):
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]])
440 self.make_twosided(mesh)
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)
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)
455 obj.setMatrix(TranslationMatrix(root) * Global.matrix)
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()
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]])
469 self.make_twosided(mesh)
471 obj = self.scene.objects.new(mesh, name)
473 self.set_color(obj, name + "mat", [0.8, 0.8, 0, 0.9])
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
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"]
489 def error(self, exception):
490 raise Abort(str(exception))
492 def fatalError(self, exception):
493 raise Abort(str(exception))
495 def warning(self, exception):
496 print(("WARNING: " + str(exception)))
499 def setDocumentLocator(self, whatever):
502 def startDocument(self):
508 def endDocument(self):
509 for o in Item.scene.objects:
512 def characters(self, data):
515 def ignorableWhitespace(self, data, start, length):
518 def processingInstruction(self, target, data):
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")
525 self.tags.append(tag)
526 path = string.join(self.tags, '/')
528 parent = self.items[-1]
530 if self.counter.has_key(tag):
531 self.counter[tag] += 1
533 self.counter[tag] = 0
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]))
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)
551 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
552 compression = getfloat(attrs, "compression", 1)
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)
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)
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)
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)
578 elif tag == "actionpt":
579 if not isinstance(parent, Thrust):
580 raise Abort("%s is not part of a thruster/propeller/jet" % path)
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)
587 if not isinstance(parent, Thrust):
588 raise Abort("%s is not part of a thruster/propeller/jet" % path)
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]))
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
677 elif tag not in self.ignored:
678 log("\033[30;1m%s\033[m" % path)
680 self.items.append(item)
682 def endElement(self, tag):
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 }
692 for line in f.readlines():
693 line = string.strip(line)
694 if not line.startswith("<!--") or not line.endswith("-->"):
696 line = string.strip(line[4:-3])
697 if not string.lower(line).startswith("%s:" % tag):
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))
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']))
714 def load_yasim_config(path):
715 if BPyMessages.Error_NoFile(path):
718 Blender.Window.WaitCursor(1)
719 Blender.Window.EditMode(0)
721 print(("loading '%s'" % path))
723 for o in Item.scene.objects:
724 if o.name.startswith("YASim_"):
725 Item.scene.objects.unlink(o)
727 Global.matrix = YASIM_MATRIX
728 matrix = extract_matrix(path, "offsets")
730 Global.matrix *= matrix.invert()
732 yasim = make_parser()
733 yasim.setContentHandler(import_yasim())
734 yasim.setErrorHandler(import_yasim())
737 Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
741 print "Error:", e.msg, " -> aborting ...\n"
742 Blender.Draw.PupMenu("Error%t|" + e.msg)
744 Blender.Window.RedrawAll()
745 Blender.Window.WaitCursor(0)
750 from Blender import BGL, Draw
751 (width, height) = Blender.Window.GetAreaSize()
753 BGL.glClearColor(0.4, 0.4, 0.45, 1)
754 BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
756 BGL.glColor3f(1, 1, 1)
757 BGL.glRasterPos2f(5, 55)
758 Draw.Text("FlightGear YASim Import: '%s'" % Global.path)
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")
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))
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))
774 def gui_event(ev, value):
775 if ev == Blender.Draw.ESCKEY:
784 elif n == RELOAD_BUTTON:
785 load_yasim_config(Global.path)
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)))
794 elif n == MIRROR_BUTTON:
795 load_yasim_config(Global.path)
797 Blender.Draw.Redraw(1)
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"]
809 if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
810 Blender.Draw.Register(gui_draw, gui_event, gui_button)
812 Blender.Window.FileSelector(load_yasim_config, "Import YASim Configuration File", path)