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 (1) load or import aircraft model (menu -> "File" -> "Import" -> "AC3D (.ac) ...")
20 (2) create new *empty* scene (menu -> arrow button left of "SCE:scene1" combobox -> "ADD NEW" -> "empty")
21 (3) rename scene to yasim (not required)
22 (4) link to scene1 (F10 -> "Output" tab -> arrow button left of text entry "No Set Scene" -> "scene1")
23 (5) now load the YASim config file (menu -> "File" -> "Import" -> "YASim (.xml) ...")
25 This is good enough for simple checks. But if you are working on the YASim configuration, then you need a
26 quick and convenient way to reload the file. In that case continue after (4):
28 (5) switch the button area on the bottom of the blender screen to "Scripts Window" mode (green python snake icon)
29 (6) load the YASim config file (menu -> "Scripts" -> "Import" -> "YASim (.xml) ...")
30 (7) make the "Scripts Window" area as small as possible by dragging the area separator down
31 (8) optionally split the "3D View" area and switch the right part to the "Outliner"
32 (9) press the "Reload YASim" button in the script area to reload the file
35 If the 3D model is displaced with respect to the FDM model, then the <offsets> values from the
36 model animation XML file should be added as comment to the YASim config file, as a line all by
37 itself, with no spaces surrounding the equal signs. Spaces elsewhere are allowed. For example:
39 <!-- offsets: x=3.45 y=0.4 p=5 -->
41 Possible variables are:
50 Of course, absolute FDM coordinates can then no longer directly be read from Blender's 3D view.
51 The cursor coordinates display in the script area, however, shows the coordinates in YASim space.
52 Note that object names don't contain XML indices but element numbers. YASim_hstab#2 is the third
53 hstab in the whole file, not necessarily in its parent XML group. A floating point part in the
54 object name (e.g. YASim_hstab#2.004) only means that the geometry has been reloaded that often.
55 It's an unavoidable consequence of how Blender deals with meshes.
58 Elements are displayed as follows:
60 cockpit -> monkey head
61 fuselage -> blue "tube" (with only 12 sides for less clutter)
62 vstab -> red with yellow flaps
63 wing/mstab/hstab -> green with yellow flaps/spoilers/slats (always 20 cm deep);
64 symmetric surfaces are only displayed on the left side
65 thrusters (jet/propeller/thruster) -> dashed line from center to actionpt;
66 arrow from actionpt along thrust vector (always 1 m long);
68 rotor -> radius and rel_len_blade_start circly, direction arrow,
69 normal and forward vector, one blade at phi0
70 gear -> contact point and compression vector (no arrow head)
71 tank -> cube (10 cm side length)
72 weight -> inverted cone
74 hitch -> circle (10 cm diameter)
75 hook -> dashed line for up angle, T-line for down angle
76 launchbar -> dashed line for up angles, T-line for down angles
79 Cursor coordinates displayed in GUI and terminal are in YASim coordinates and consider an
80 XML embedded displacement matrix as described above.
84 #--------------------------------------------------------------------------------
85 # Copyright (C) 2009 Melchior FRANZ < mfranz # aon : at >
87 # This program is free software; you can redistribute it and/or
88 # modify it under the terms of the GNU General Public License as
89 # published by the Free Software Foundation; either version 2 of the
90 # License, or (at your option) any later version.
92 # This program is distributed in the hope that it will be useful, but
93 # WITHOUT ANY WARRANTY; without even the implied warranty of
94 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
95 # General Public License for more details.
97 # You should have received a copy of the GNU General Public License
98 # along with this program; if not, write to the Free Software
99 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
100 #--------------------------------------------------------------------------------
103 import Blender, BPyMessages, string, math
104 from Blender.Mathutils import *
105 from xml.sax import handler, make_parser
108 YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])
109 ORIGIN = Vector(0, 0, 0)
113 DEG2RAD = math.pi / 180
114 RAD2DEG = 180 / math.pi
121 last_cursor = Vector(Blender.Window.GetCursorPos())
124 class Abort(Exception):
125 def __init__(self, msg):
130 #print(msg) # uncomment to get verbose log messages
135 print("\033[31;1mError: %s\033[m" % msg)
136 Blender.Draw.PupMenu("Error%t|" + msg)
139 def getfloat(attrs, key, default):
140 if attrs.has_key(key):
141 return float(attrs[key])
145 def draw_dashed_line(mesh, start, end):
147 step = w * (end - start).normalize()
149 for i in range(int(1 + 0.5 * (end - start).length / w)):
150 a = start + 2 * i * step
151 b = start + (2 * i + 1) * step
152 if (b - end).length < step.length:
154 mesh.verts.extend([a, b])
155 mesh.edges.extend([n + 2 * i, n + 2 * i + 1])
158 def draw_arrow(mesh, start, end):
160 m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start)
163 mesh.verts.extend([ORIGIN * m , v * m, (v - 0.05 * X + 0.05 * Y) * m, (v - 0.05 * X - 0.05 * Y) * m]) # head
164 mesh.verts.extend([(ORIGIN + 0.05 * Y) * m, (ORIGIN - 0.05 * Y) * m]) # base
165 mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]])
168 def draw_circle(mesh, numpoints, radius, matrix):
170 for i in range(numpoints):
171 angle = 2.0 * math.pi * i / numpoints
172 v = Vector(radius * math.cos(angle), radius * math.sin(angle), 0)
173 mesh.verts.extend([v * matrix])
174 for i in range(numpoints):
175 i1 = (i + 1) % numpoints
176 mesh.edges.extend([[n + i, n + i1]])
180 scene = Blender.Scene.GetCurrent()
182 def make_twosided(self, mesh):
185 f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
187 def set_color(self, mesh, name, color):
188 mat = Blender.Material.New(name)
189 mat.setRGBCol(color[0], color[1], color[2])
190 mat.setAlpha(color[3])
191 mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW
192 mesh.materials += [mat]
196 def __init__(self, center):
197 mesh = Blender.Mesh.Primitives.Monkey()
198 mesh.transform(ScaleMatrix(0.13, 4) * Euler(90, 0, 90).toMatrix().resize4x4() * TranslationMatrix(Vector(-0.1, 0, -0.032)))
199 obj = self.scene.objects.new(mesh, "YASim_cockpit")
200 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
204 def __init__(self, name, center):
205 mesh = Blender.Mesh.Primitives.Cube()
206 mesh.transform(ScaleMatrix(0.05, 4))
207 obj = self.scene.objects.new(mesh, name)
208 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
212 def __init__(self, name, center):
213 mesh = Blender.Mesh.Primitives.Cylinder()
214 mesh.transform(ScaleMatrix(0.05, 4))
215 obj = self.scene.objects.new(mesh, name)
216 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
220 def __init__(self, name, center):
221 mesh = Blender.Mesh.Primitives.Cone()
222 mesh.transform(ScaleMatrix(0.05, 4))
223 obj = self.scene.objects.new(mesh, name)
224 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
228 def __init__(self, name, center, compression):
229 mesh = Blender.Mesh.New()
230 mesh.verts.extend([ORIGIN, compression])
231 mesh.edges.extend([0, 1])
232 obj = self.scene.objects.new(mesh, name)
233 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
237 def __init__(self, name, center, length, up_angle, dn_angle):
238 mesh = Blender.Mesh.New()
239 up = ORIGIN - length * math.cos(up_angle * DEG2RAD) * X - length * math.sin(up_angle * DEG2RAD) * Z
240 dn = ORIGIN - length * math.cos(dn_angle * DEG2RAD) * X - length * math.sin(dn_angle * DEG2RAD) * Z
241 mesh.verts.extend([ORIGIN, dn, dn + 0.05 * Y, dn - 0.05 * Y])
242 mesh.edges.extend([[0, 1], [2, 3]])
243 draw_dashed_line(mesh, ORIGIN, up)
244 draw_dashed_line(mesh, ORIGIN, dn)
245 obj = self.scene.objects.new(mesh, name)
246 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
249 class Launchbar(Item):
250 def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle):
251 mesh = Blender.Mesh.New()
253 lb_tip = ORIGIN + lb_length * math.cos(dn_angle * DEG2RAD) * X - lb_length * math.sin(dn_angle * DEG2RAD) * Z
254 hb_tip = hb - hb_length * math.cos(dn_angle * DEG2RAD) * X - hb_length * math.sin(dn_angle * DEG2RAD) * Z
255 mesh.verts.extend([lb_tip, ORIGIN, hb, hb_tip, lb_tip + 0.05 * Y, lb_tip - 0.05 * Y, hb_tip + 0.05 * Y, hb_tip - 0.05 * Y])
256 mesh.edges.extend([[0, 1], [1, 2], [2, 3], [4, 5], [6, 7]])
257 draw_dashed_line(mesh, ORIGIN, lb_length * math.cos(up_angle * DEG2RAD) * X - lb_length * math.sin(up_angle * DEG2RAD) * Z)
258 draw_dashed_line(mesh, hb, hb - hb_length * math.cos(up_angle * DEG2RAD) * X - hb_length * math.sin(up_angle * DEG2RAD) * Z)
259 obj = self.scene.objects.new(mesh, name)
260 obj.setMatrix(TranslationMatrix(lb) * Global.matrix)
264 def __init__(self, name, center):
265 mesh = Blender.Mesh.Primitives.Circle(8, 0.1)
266 obj = self.scene.objects.new(mesh, name)
267 obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix)
271 def set_actionpt(self, p):
274 def set_dir(self, d):
275 self.thrustvector = d
278 class Thruster(Thrust, Item):
279 def __init__(self, name, center, thrustvector):
280 (self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector)
283 a = self.actionpt - self.center
284 mesh = Blender.Mesh.New()
285 draw_dashed_line(mesh, ORIGIN, a)
286 draw_arrow(mesh, a, a + self.thrustvector.normalize())
287 obj = self.scene.objects.new(mesh, self.name)
288 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
291 class Propeller(Thrust, Item):
292 def __init__(self, name, center, radius):
293 (self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X)
296 a = self.actionpt - self.center
297 cross = -X.cross(self.thrustvector)
298 angle = AngleBetweenVecs(-X, self.thrustvector)
299 matrix = RotationMatrix(angle, 4, "r", cross) * TranslationMatrix(a)
300 matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
302 mesh = Blender.Mesh.New()
303 mesh.verts.extend([ORIGIN * matrix, (ORIGIN + self.radius * X) * matrix])
304 mesh.edges.extend([[0, 1]])
305 draw_dashed_line(mesh, ORIGIN, a)
306 draw_arrow(mesh, a, a + self.thrustvector.normalize())
308 draw_circle(mesh, 128, self.radius, matrix)
309 obj = self.scene.objects.new(mesh, self.name)
310 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
313 class Jet(Thrust, Item):
314 def __init__(self, name, center, rotate):
315 (self.name, self.center, self.actionpt) = (name, center, center)
316 self.thrustvector = -X * RotationMatrix(rotate, 4, "y")
319 a = self.actionpt - self.center
320 mesh = Blender.Mesh.New()
321 draw_dashed_line(mesh, ORIGIN, a)
322 draw_arrow(mesh, a, a + self.thrustvector.normalize())
323 obj = self.scene.objects.new(mesh, self.name)
324 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
327 class Fuselage(Item):
328 def __init__(self, name, a, b, width, taper, midpoint):
331 for i in range(numvert):
332 alpha = i * 2 * math.pi / float(numvert)
333 angle.append([math.cos(alpha), math.sin(alpha)])
337 mesh = Blender.Mesh.New()
339 for i in range(numvert):
340 mesh.verts.extend([[0, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
341 for i in range(numvert):
342 mesh.verts.extend([[midpoint * length, 0.5 * width * angle[i][0], 0.5 * width * angle[i][1]]])
343 for i in range(numvert):
344 mesh.verts.extend([[length, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
345 for i in range(numvert):
346 i1 = (i + 1) % numvert
347 mesh.faces.extend([[i, i1, i1 + numvert, i + numvert]])
348 mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]])
350 mesh.verts.extend([ORIGIN, length * X])
351 self.set_color(mesh, name + "mat", [0, 0, 0.5, 0.4])
352 obj = self.scene.objects.new(mesh, name)
354 obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix)
358 def __init__(self, name, center, up, fwd, numblades, radius, chord, twist, taper, rel_len_blade_start, phi0, ccw):
359 matrix = RotationMatrix(phi0, 4, "z") * up.toTrackQuat('z', 'x').toMatrix().resize4x4()
360 invert = matrix.copy().invert()
361 direction = [-1, 1][ccw]
363 a = ORIGIN + rel_len_blade_start * radius * X
364 b = ORIGIN + radius * X
365 tw = 0.5 * chord * taper * math.cos(twist) * Y + 0.5 * direction * chord * taper * math.sin(twist) * Z
367 mesh = Blender.Mesh.New()
368 mesh.verts.extend([ORIGIN, a, b, a + 0.5 * chord * Y, a - 0.5 * chord * Y, b + tw, b - tw])
369 mesh.edges.extend([[0, 1], [1, 2], [1, 3], [1, 4], [3, 5], [4, 6], [5, 6]])
370 draw_circle(mesh, 64, rel_len_blade_start * radius, Matrix())
371 draw_circle(mesh, 128, radius, Matrix())
372 draw_arrow(mesh, ORIGIN, up * invert)
373 draw_arrow(mesh, ORIGIN, fwd * invert)
374 b += 0.1 * X + direction * chord * Y
375 draw_arrow(mesh, b, b + 0.5 * radius * direction * Y)
376 obj = self.scene.objects.new(mesh, name)
377 obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix)
381 def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral):
386 mesh = Blender.Mesh.New()
387 mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X])
388 tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X
389 tipfore = tip + 0.5 * taper * chord * math.cos(twist * DEG2RAD) * X + 0.5 * taper * chord * math.sin(twist * DEG2RAD) * Z
390 tipaft = tip + tip - tipfore
391 mesh.verts.extend([tip, tipfore, tipaft])
392 mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]])
394 self.set_color(mesh, name + "mat", [[0, 0.5, 0, 0.5], [0.5, 0, 0, 0.5]][name.startswith("YASim_vstab")])
395 self.make_twosided(mesh)
397 obj = self.scene.objects.new(mesh, name)
399 m = Euler(dihedral, -incidence, 0).toMatrix().resize4x4()
400 m *= TranslationMatrix(root)
401 obj.setMatrix(m * Global.matrix)
402 (self.obj, self.mesh) = (obj, mesh)
404 def add_flap(self, name, start, end):
405 a = Vector(self.mesh.verts[2].co)
406 b = Vector(self.mesh.verts[5].co)
407 c = 0.2 * (Vector(self.mesh.verts[0].co - a)).normalize()
408 m = self.obj.getMatrix()
410 mesh = Blender.Mesh.New()
411 i0 = a + start * (b - a)
412 i1 = a + end * (b - a)
413 mesh.verts.extend([i0, i1, i0 + c, i1 + c])
414 mesh.faces.extend([[0, 1, 3, 2]])
416 self.set_color(mesh, name + "mat", [0.8, 0.8, 0, 0.9])
417 self.make_twosided(mesh)
419 obj = self.scene.objects.new(mesh, name)
424 class import_yasim(handler.ContentHandler):
425 ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \
426 "control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \
427 "rotorgear", "tow", "winch", "solve-weight"]
430 def error(self, exception):
431 raise Abort(str(exception))
433 def fatalError(self, exception):
434 raise Abort(str(exception))
436 def warning(self, exception):
437 print "WARNING: " + str(exception)
440 def setDocumentLocator(self, whatever):
443 def startDocument(self):
449 def endDocument(self):
450 for o in Item.scene.objects:
453 def characters(self, data):
456 def ignorableWhitespace(self, data, start, length):
459 def processingInstruction(self, target, data):
462 def startElement(self, tag, attrs):
463 if len(self.tags) == 0 and tag != "airplane":
464 raise Abort("this isn't a YASim config file")
466 self.tags.append(tag)
467 path = string.join(self.tags, '/')
469 parent = self.items[-1]
471 if self.counter.has_key(tag):
472 self.counter[tag] += 1
474 self.counter[tag] = 0
477 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
478 log("\033[31mcockpit x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
481 elif tag == "fuselage":
482 a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"]))
483 b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"]))
484 width = float(attrs["width"])
485 taper = getfloat(attrs, "taper", 1)
486 midpoint = getfloat(attrs, "midpoint", 0.5)
487 log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \
488 (a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint))
489 item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint)
492 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
493 compression = getfloat(attrs, "compression", 1)
495 if attrs.has_key("upx"):
496 up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression
497 log("\033[35;1mgear x=%f y=%f z=%f compression=%f upx=%f upy=%f upz=%f\033[m" \
498 % (c[0], c[1], c[2], compression, up[0], up[1], up[2]))
499 item = Gear("YASim_gear#%d" % self.counter[tag], c, up)
502 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
503 rotate = getfloat(attrs, "rotate", 0.0)
504 log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate))
505 item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate)
507 elif tag == "propeller":
508 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
509 radius = float(attrs["radius"])
510 log("\033[36;1m%s x=%f y=%f z=%f radius=%f\033[m" % (tag, c[0], c[1], c[2], radius))
511 item = Propeller("YASim_propeller#%d" % self.counter[tag], c, radius)
513 elif tag == "thruster":
514 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
515 v = Vector(float(attrs["vx"]), float(attrs["vy"]), float(attrs["vz"]))
516 log("\033[36;1m%s x=%f y=%f z=%f vx=%f vy=%f vz=%f\033[m" % (tag, c[0], c[1], c[2], v[0], v[1], v[2]))
517 item = Thruster("YASim_thruster#%d" % self.counter[tag], c, v)
519 elif tag == "actionpt":
520 if not isinstance(parent, Thrust):
521 raise Abort("%s is not part of a propeller or jet" % path)
523 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
524 log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
525 parent.set_actionpt(c)
528 if not isinstance(parent, Thrust):
529 raise Abort("%s is not part of a propeller or jet" % path)
531 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
532 log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
536 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
537 log("\033[34;1m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
538 item = Tank("YASim_tank#%d" % self.counter[tag], c)
540 elif tag == "ballast":
541 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
542 log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
543 item = Ballast("YASim_ballast#%d" % self.counter[tag], c)
545 elif tag == "weight":
546 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
547 log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
548 item = Weight("YASim_weight#%d" % self.counter[tag], c)
551 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
552 length = getfloat(attrs, "length", 1.0)
553 up_angle = getfloat(attrs, "up-angle", 0.0)
554 down_angle = getfloat(attrs, "down-angle", 70.0)
555 log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \
556 % (tag, c[0], c[1], c[2], length, up_angle, down_angle))
557 item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle)
560 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
561 log("\033[35m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
562 item = Hitch("YASim_hitch#%d" % self.counter[tag], c)
564 elif tag == "launchbar":
565 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
566 length = getfloat(attrs, "length", 1.0)
567 up_angle = getfloat(attrs, "up-angle", -45.0)
568 down_angle = getfloat(attrs, "down-angle", 45.0)
569 holdback = Vector(getfloat(attrs, "holdback-x", c[0]), getfloat(attrs, "holdback-y", c[1]), getfloat(attrs, "holdback-z", c[2]))
570 holdback_length = getfloat(attrs, "holdback-length", 2.0)
571 log("\033[35m%s x=%f y=%f z=%f length=%f down-angle=%f up-angle=%f holdback-x=%f holdback-y=%f holdback-z+%f holdback-length=%f\033[m" \
572 % (tag, c[0], c[1], c[2], length, down_angle, up_angle, \
573 holdback[0], holdback[1], holdback[2], holdback_length))
574 item = Launchbar("YASim_launchbar#%d" % self.counter[tag], c, length, holdback, holdback_length, up_angle, down_angle)
576 elif tag == "wing" or tag == "hstab" or tag == "vstab" or tag == "mstab":
577 root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
578 length = float(attrs["length"])
579 chord = float(attrs["chord"])
580 incidence = getfloat(attrs, "incidence", 0.0)
581 twist = getfloat(attrs, "twist", 0.0)
582 taper = getfloat(attrs, "taper", 1.0)
583 sweep = getfloat(attrs, "sweep", 0.0)
584 dihedral = getfloat(attrs, "dihedral", [0.0, 90.0][tag == "vstab"])
585 log("\033[33;1m%s x=%f y=%f z=%f length=%f chord=%f incidence=%f twist=%f taper=%f sweep=%f dihedral=%f\033[m" \
586 % (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral))
587 item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral)
589 elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler":
590 if not isinstance(parent, Wing):
591 raise Abort("%s is not part of a wing or stab" % path)
593 start = float(attrs["start"])
594 end = float(attrs["end"])
595 log("\t\033[33m%s start=%f end=%f\033[m" % (tag, start, end))
596 parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end)
599 c = Vector(getfloat(attrs, "x", 0.0), getfloat(attrs, "y", 0.0), getfloat(attrs, "z", 0.0))
600 norm = Vector(getfloat(attrs, "nx", 0.0), getfloat(attrs, "ny", 0.0), getfloat(attrs, "nz", 1.0))
601 fwd = Vector(getfloat(attrs, "fx", 1.0), getfloat(attrs, "fy", 0.0), getfloat(attrs, "fz", 0.0))
602 diameter = getfloat(attrs, "diameter", 10.2)
603 numblades = int(getfloat(attrs, "numblades", 4))
604 chord = getfloat(attrs, "chord", 0.3)
605 twist = getfloat(attrs, "twist", 0.0)
606 taper = getfloat(attrs, "taper", 1.0)
607 rel_len_blade_start = getfloat(attrs, "rel-len-blade-start", 0.0)
608 phi0 = getfloat(attrs, "phi0", 0)
609 ccw = not not getfloat(attrs, "ccw", 0)
611 log(("\033[36;1mrotor x=%f y=%f z=%f nx=%f ny=%f nz=%f fx=%f fy=%f fz=%f numblades=%d diameter=%f " \
612 + "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \
613 % (c[0], c[1], c[2], norm[0], norm[1], norm[2], fwd[0], fwd[1], fwd[2], numblades, \
614 diameter, chord, twist, taper, rel_len_blade_start, phi0, ccw))
615 item = Rotor("YASim_rotor#%d" % self.counter[tag], c, norm, fwd, numblades, 0.5 * diameter, chord, \
616 twist, taper, rel_len_blade_start, phi0, ccw)
618 elif tag not in self.ignored:
619 log("\033[30;1m%s\033[m" % path)
621 self.items.append(item)
623 def endElement(self, tag):
628 def extract_matrix(path, tag):
629 v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 }
632 for line in f.readlines():
633 line = string.strip(line)
634 if not line.startswith("<!--") or not line.endswith("-->"):
636 line = string.strip(line[4:-3])
637 if not string.lower(line).startswith("%s:" % tag):
639 line = string.strip(line[8:])
640 for assignment in string.split(line):
641 (key, value) = string.split(assignment, '=', 2)
642 v[string.strip(key)] = float(string.strip(value))
647 matrix = Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4()
648 matrix *= TranslationMatrix(Vector(v['x'], v['y'], v['z']))
652 def run_parser(path):
653 if BPyMessages.Error_NoFile(path):
656 editmode = Blender.Window.EditMode()
658 Blender.Window.EditMode(0)
659 Blender.Window.WaitCursor(1)
662 for o in Item.scene.objects:
663 if o.name.startswith("YASim_"):
664 Item.scene.objects.unlink(o)
666 print("\033[1mloading '%s'\033[m" % path)
668 Global.matrix = YASIM_MATRIX
669 matrix = extract_matrix(path, "offsets")
671 Global.matrix *= matrix.invert()
673 yasim = make_parser()
674 yasim.setContentHandler(import_yasim())
675 yasim.setErrorHandler(import_yasim())
678 Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
682 print "Error:", e.msg, " -> aborting ...\n"
683 Blender.Draw.PupMenu("Error%t|" + e.msg)
685 Blender.Window.RedrawAll()
686 Blender.Window.WaitCursor(0)
688 Blender.Window.EditMode(1)
692 from Blender import BGL, Draw
693 (width, height) = Blender.Window.GetAreaSize()
695 BGL.glClearColor(0.4, 0.4, 0.45, 1)
696 BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
697 Draw.PushButton("Reload YASim", 0, 5, 5, 100, 28)
698 Draw.PushButton("Update Cursor", 1, width - 650, 5, 100, 28)
699 BGL.glColor3f(1, 1, 1)
701 BGL.glRasterPos2f(120, 15)
702 Draw.Text(Global.path)
704 BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Distance from last") - Blender.Draw.GetStringWidth("Current"), 24)
705 Draw.Text("Current cursor pos: x = %+.3f y = %+.3f z = %+.3f" % tuple(Global.cursor))
707 c = Global.cursor - Global.last_cursor
708 BGL.glRasterPos2f(width - 530, 7)
709 Draw.Text("Distance from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f" % (c[0], c[1], c[2], c.length))
712 def event(ev, value):
713 if ev == Blender.Draw.ESCKEY:
719 run_parser(Global.path)
721 Global.last_cursor = Global.cursor
722 Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert()
723 d = Global.cursor - Global.last_cursor
724 print("cursor: x=\"%f\" y=\"%f\" z=\"%f\" dx=%f dy=%f dz=%f length=%f" \
725 % (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length))
726 Blender.Draw.Redraw(1)
730 registry = Blender.Registry.GetKey("FGYASimImportExport", False)
731 if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
732 path = registry["path"]
738 if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
739 Blender.Draw.Register(draw, event, button)
741 Blender.Window.FileSelector(run_parser, "Import YASim Configuration File", path)