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_hstab#2 is the third
62 hstab in the whole file, not necessarily in its parent XML group. A floating point part in the
63 object name (e.g. YASim_hstab#2.004) only means that the geometry has been reloaded that often.
64 It's an unavoidable consequence of how Blender deals with meshes.
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 flaps
72 wing/mstab/hstab -> green with yellow flaps/spoilers/slats (always 20 cm deep);
73 symmetric surfaces are only displayed on the left side
74 thrusters (jet/propeller/thruster) -> dashed line from center to actionpt;
75 arrow from actionpt along thrust vector (always 1 m long);
77 rotor -> radius and rel_len_blade_start circle, direction arrow,
78 normal and forward vector, one blade at phi0
79 gear -> contact point and compression vector (no arrow head)
80 tank -> cube (10 cm side length)
81 weight -> inverted cone
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
90 #--------------------------------------------------------------------------------
91 # Copyright (C) 2009 Melchior FRANZ < mfranz # aon : at >
93 # This program is free software; you can redistribute it and/or
94 # modify it under the terms of the GNU General Public License as
95 # published by the Free Software Foundation; either version 2 of the
96 # License, or (at your option) any later version.
98 # This program is distributed in the hope that it will be useful, but
99 # WITHOUT ANY WARRANTY; without even the implied warranty of
100 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
101 # General Public License for more details.
103 # You should have received a copy of the GNU General Public License
104 # along with this program; if not, write to the Free Software
105 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
106 #--------------------------------------------------------------------------------
109 import Blender, BPyMessages, string, math
110 from Blender.Mathutils import *
111 from xml.sax import handler, make_parser
114 YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])
115 ORIGIN = Vector(0, 0, 0)
119 DEG2RAD = math.pi / 180
120 RAD2DEG = 180 / math.pi
127 last_cursor = Vector(Blender.Window.GetCursorPos())
130 class Abort(Exception):
131 def __init__(self, msg):
136 #print(msg) # uncomment to get verbose log messages
141 print(("\033[31;1mError: %s\033[m" % msg))
142 Blender.Draw.PupMenu("Error%t|" + msg)
145 def getfloat(attrs, key, default):
146 if attrs.has_key(key):
147 return float(attrs[key])
151 def draw_dashed_line(mesh, start, end):
153 step = w * (end - start).normalize()
155 for i in range(int(1 + 0.5 * (end - start).length / w)):
156 a = start + 2 * i * step
157 b = start + (2 * i + 1) * step
158 if (b - end).length < step.length:
160 mesh.verts.extend([a, b])
161 mesh.edges.extend([n + 2 * i, n + 2 * i + 1])
164 def draw_arrow(mesh, start, end):
166 m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start)
169 mesh.verts.extend([ORIGIN * m , v * m, (v - 0.05 * X + 0.05 * Y) * m, (v - 0.05 * X - 0.05 * Y) * m]) # head
170 mesh.verts.extend([(ORIGIN + 0.05 * Y) * m, (ORIGIN - 0.05 * Y) * m]) # base
171 mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]])
174 def draw_circle(mesh, numpoints, radius, matrix):
176 for i in range(numpoints):
177 angle = 2.0 * math.pi * i / numpoints
178 v = Vector(radius * math.cos(angle), radius * math.sin(angle), 0)
179 mesh.verts.extend([v * matrix])
180 for i in range(numpoints):
181 i1 = (i + 1) % numpoints
182 mesh.edges.extend([[n + i, n + i1]])
186 scene = Blender.Scene.GetCurrent()
188 def make_twosided(self, mesh):
191 f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
193 def set_color(self, mesh, name, color):
194 mat = Blender.Material.New(name)
195 mat.setRGBCol(color[0], color[1], color[2])
196 mat.setAlpha(color[3])
197 mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW
198 mesh.materials += [mat]
202 def __init__(self, center):
203 mesh = Blender.Mesh.Primitives.Monkey()
204 mesh.transform(ScaleMatrix(0.13, 4) * Euler(90, 0, 90).toMatrix().resize4x4() * TranslationMatrix(Vector(-0.1, 0, -0.032)))
205 obj = self.scene.objects.new(mesh, "YASim_cockpit")
206 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
210 def __init__(self, name, center):
211 mesh = Blender.Mesh.Primitives.Cube()
212 mesh.transform(ScaleMatrix(0.05, 4))
213 obj = self.scene.objects.new(mesh, name)
214 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
218 def __init__(self, name, center):
219 mesh = Blender.Mesh.Primitives.Cylinder()
220 mesh.transform(ScaleMatrix(0.05, 4))
221 obj = self.scene.objects.new(mesh, name)
222 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
226 def __init__(self, name, center):
227 mesh = Blender.Mesh.Primitives.Cone()
228 mesh.transform(ScaleMatrix(0.05, 4))
229 obj = self.scene.objects.new(mesh, name)
230 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
234 def __init__(self, name, center, compression):
235 mesh = Blender.Mesh.New()
236 mesh.verts.extend([ORIGIN, compression])
237 mesh.edges.extend([0, 1])
238 obj = self.scene.objects.new(mesh, name)
239 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
243 def __init__(self, name, center, length, up_angle, dn_angle):
244 mesh = Blender.Mesh.New()
245 up = ORIGIN - length * math.cos(up_angle * DEG2RAD) * X - length * math.sin(up_angle * DEG2RAD) * Z
246 dn = ORIGIN - length * math.cos(dn_angle * DEG2RAD) * X - length * math.sin(dn_angle * DEG2RAD) * Z
247 mesh.verts.extend([ORIGIN, dn, dn + 0.05 * Y, dn - 0.05 * Y])
248 mesh.edges.extend([[0, 1], [2, 3]])
249 draw_dashed_line(mesh, ORIGIN, up)
250 draw_dashed_line(mesh, ORIGIN, dn)
251 obj = self.scene.objects.new(mesh, name)
252 obj.setMatrix(TranslationMatrix(center) * Global.matrix)
255 class Launchbar(Item):
256 def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle):
257 mesh = Blender.Mesh.New()
259 lb_tip = ORIGIN + lb_length * math.cos(dn_angle * DEG2RAD) * X - lb_length * math.sin(dn_angle * DEG2RAD) * Z
260 hb_tip = hb - hb_length * math.cos(dn_angle * DEG2RAD) * X - hb_length * math.sin(dn_angle * DEG2RAD) * Z
261 mesh.verts.extend([lb_tip, ORIGIN, hb, hb_tip, lb_tip + 0.05 * Y, lb_tip - 0.05 * Y, hb_tip + 0.05 * Y, hb_tip - 0.05 * Y])
262 mesh.edges.extend([[0, 1], [1, 2], [2, 3], [4, 5], [6, 7]])
263 draw_dashed_line(mesh, ORIGIN, lb_length * math.cos(up_angle * DEG2RAD) * X - lb_length * math.sin(up_angle * DEG2RAD) * Z)
264 draw_dashed_line(mesh, hb, hb - hb_length * math.cos(up_angle * DEG2RAD) * X - hb_length * math.sin(up_angle * DEG2RAD) * Z)
265 obj = self.scene.objects.new(mesh, name)
266 obj.setMatrix(TranslationMatrix(lb) * Global.matrix)
270 def __init__(self, name, center):
271 mesh = Blender.Mesh.Primitives.Circle(8, 0.1)
272 obj = self.scene.objects.new(mesh, name)
273 obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix)
277 def set_actionpt(self, p):
280 def set_dir(self, d):
281 self.thrustvector = d
284 class Thruster(Thrust, Item):
285 def __init__(self, name, center, thrustvector):
286 (self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector)
289 a = self.actionpt - self.center
290 mesh = Blender.Mesh.New()
291 draw_dashed_line(mesh, ORIGIN, a)
292 draw_arrow(mesh, a, a + self.thrustvector.normalize())
293 obj = self.scene.objects.new(mesh, self.name)
294 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
297 class Propeller(Thrust, Item):
298 def __init__(self, name, center, radius):
299 (self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X)
302 a = self.actionpt - self.center
303 matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
305 mesh = Blender.Mesh.New()
306 mesh.verts.extend([ORIGIN * matrix, (ORIGIN + self.radius * X) * matrix])
307 mesh.edges.extend([[0, 1]])
308 draw_dashed_line(mesh, ORIGIN, a)
309 draw_arrow(mesh, a, a + self.thrustvector.normalize())
311 draw_circle(mesh, 128, self.radius, matrix)
312 obj = self.scene.objects.new(mesh, self.name)
313 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
316 class Jet(Thrust, Item):
317 def __init__(self, name, center, rotate):
318 (self.name, self.center, self.actionpt) = (name, center, center)
319 self.thrustvector = -X * RotationMatrix(rotate, 4, "y")
322 a = self.actionpt - self.center
323 mesh = Blender.Mesh.New()
324 draw_dashed_line(mesh, ORIGIN, a)
325 draw_arrow(mesh, a, a + self.thrustvector.normalize())
326 obj = self.scene.objects.new(mesh, self.name)
327 obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
330 class Fuselage(Item):
331 def __init__(self, name, a, b, width, taper, midpoint):
334 for i in range(numvert):
335 alpha = i * 2 * math.pi / float(numvert)
336 angle.append([math.cos(alpha), math.sin(alpha)])
340 mesh = Blender.Mesh.New()
342 for i in range(numvert):
343 mesh.verts.extend([[0, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
344 for i in range(numvert):
345 mesh.verts.extend([[midpoint * length, 0.5 * width * angle[i][0], 0.5 * width * angle[i][1]]])
346 for i in range(numvert):
347 mesh.verts.extend([[length, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
348 for i in range(numvert):
349 i1 = (i + 1) % numvert
350 mesh.faces.extend([[i, i1, i1 + numvert, i + numvert]])
351 mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]])
353 mesh.verts.extend([ORIGIN, length * X])
354 self.set_color(mesh, name + "mat", [0, 0, 0.5, 0.4])
355 obj = self.scene.objects.new(mesh, name)
357 obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix)
361 def __init__(self, name, center, up, fwd, numblades, radius, chord, twist, taper, rel_len_blade_start, phi0, ccw):
362 matrix = RotationMatrix(phi0, 4, "z") * up.toTrackQuat('z', 'x').toMatrix().resize4x4()
363 invert = matrix.copy().invert()
364 direction = [-1, 1][ccw]
366 a = ORIGIN + rel_len_blade_start * radius * X
367 b = ORIGIN + radius * X
368 tw = 0.5 * chord * taper * math.cos(twist) * Y + 0.5 * direction * chord * taper * math.sin(twist) * Z
370 mesh = Blender.Mesh.New()
371 mesh.verts.extend([ORIGIN, a, b, a + 0.5 * chord * Y, a - 0.5 * chord * Y, b + tw, b - tw])
372 mesh.edges.extend([[0, 1], [1, 2], [1, 3], [1, 4], [3, 5], [4, 6], [5, 6]])
373 draw_circle(mesh, 64, rel_len_blade_start * radius, Matrix())
374 draw_circle(mesh, 128, radius, Matrix())
375 draw_arrow(mesh, ORIGIN, up * invert)
376 draw_arrow(mesh, ORIGIN, fwd * invert)
377 b += 0.1 * X + direction * chord * Y
378 draw_arrow(mesh, b, b + 0.5 * radius * direction * Y)
379 obj = self.scene.objects.new(mesh, name)
380 obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix)
384 def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral):
388 is_vstab = name.startswith("YASim_vstab")
389 mesh = Blender.Mesh.New()
390 mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X])
391 tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X
392 tipfore = tip + 0.5 * taper * chord * math.cos(twist * DEG2RAD) * X + 0.5 * taper * chord * math.sin(twist * DEG2RAD) * Z
393 tipaft = tip + tip - tipfore
394 mesh.verts.extend([tip, tipfore, tipaft])
395 mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]])
397 self.set_color(mesh, name + "mat", [[0, 0.5, 0, 0.5], [0.5, 0, 0, 0.5]][is_vstab])
398 self.make_twosided(mesh)
400 obj = self.scene.objects.new(mesh, name)
402 m = Euler(dihedral, -incidence, 0).toMatrix().resize4x4()
403 m *= TranslationMatrix(root)
404 obj.setMatrix(m * Global.matrix)
405 (self.obj, self.mesh) = (obj, mesh)
407 def add_flap(self, name, start, end):
408 a = Vector(self.mesh.verts[2].co)
409 b = Vector(self.mesh.verts[5].co)
410 c = 0.2 * (Vector(self.mesh.verts[0].co - a)).normalize()
411 m = self.obj.getMatrix()
413 mesh = Blender.Mesh.New()
414 i0 = a + start * (b - a)
415 i1 = a + end * (b - a)
416 mesh.verts.extend([i0, i1, i0 + c, i1 + c])
417 mesh.faces.extend([[0, 1, 3, 2]])
419 self.set_color(mesh, name + "mat", [0.8, 0.8, 0, 0.9])
420 self.make_twosided(mesh)
422 obj = self.scene.objects.new(mesh, name)
427 class import_yasim(handler.ContentHandler):
428 ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \
429 "control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \
430 "rotorgear", "tow", "winch", "solve-weight"]
433 def error(self, exception):
434 raise Abort(str(exception))
436 def fatalError(self, exception):
437 raise Abort(str(exception))
439 def warning(self, exception):
440 print(("WARNING: " + str(exception)))
443 def setDocumentLocator(self, whatever):
446 def startDocument(self):
452 def endDocument(self):
453 for o in Item.scene.objects:
456 def characters(self, data):
459 def ignorableWhitespace(self, data, start, length):
462 def processingInstruction(self, target, data):
465 def startElement(self, tag, attrs):
466 if len(self.tags) == 0 and tag != "airplane":
467 raise Abort("this isn't a YASim config file")
469 self.tags.append(tag)
470 path = string.join(self.tags, '/')
472 parent = self.items[-1]
474 if self.counter.has_key(tag):
475 self.counter[tag] += 1
477 self.counter[tag] = 0
480 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
481 log("\033[31mcockpit x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
484 elif tag == "fuselage":
485 a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"]))
486 b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"]))
487 width = float(attrs["width"])
488 taper = getfloat(attrs, "taper", 1)
489 midpoint = getfloat(attrs, "midpoint", 0.5)
490 log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \
491 (a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint))
492 item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint)
495 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
496 compression = getfloat(attrs, "compression", 1)
498 if attrs.has_key("upx"):
499 up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression
500 log("\033[35;1mgear x=%f y=%f z=%f compression=%f upx=%f upy=%f upz=%f\033[m" \
501 % (c[0], c[1], c[2], compression, up[0], up[1], up[2]))
502 item = Gear("YASim_gear#%d" % self.counter[tag], c, up)
505 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
506 rotate = getfloat(attrs, "rotate", 0.0)
507 log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate))
508 item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate)
510 elif tag == "propeller":
511 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
512 radius = float(attrs["radius"])
513 log("\033[36;1m%s x=%f y=%f z=%f radius=%f\033[m" % (tag, c[0], c[1], c[2], radius))
514 item = Propeller("YASim_propeller#%d" % self.counter[tag], c, radius)
516 elif tag == "thruster":
517 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
518 v = Vector(float(attrs["vx"]), float(attrs["vy"]), float(attrs["vz"]))
519 log("\033[36;1m%s x=%f y=%f z=%f vx=%f vy=%f vz=%f\033[m" % (tag, c[0], c[1], c[2], v[0], v[1], v[2]))
520 item = Thruster("YASim_thruster#%d" % self.counter[tag], c, v)
522 elif tag == "actionpt":
523 if not isinstance(parent, Thrust):
524 raise Abort("%s is not part of a thruster/propeller/jet" % path)
526 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
527 log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
528 parent.set_actionpt(c)
531 if not isinstance(parent, Thrust):
532 raise Abort("%s is not part of a thruster/propeller/jet" % path)
534 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
535 log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
539 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
540 log("\033[34;1m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
541 item = Tank("YASim_tank#%d" % self.counter[tag], c)
543 elif tag == "ballast":
544 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
545 log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
546 item = Ballast("YASim_ballast#%d" % self.counter[tag], c)
548 elif tag == "weight":
549 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
550 log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
551 item = Weight("YASim_weight#%d" % self.counter[tag], c)
554 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
555 length = getfloat(attrs, "length", 1.0)
556 up_angle = getfloat(attrs, "up-angle", 0.0)
557 down_angle = getfloat(attrs, "down-angle", 70.0)
558 log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \
559 % (tag, c[0], c[1], c[2], length, up_angle, down_angle))
560 item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle)
563 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
564 log("\033[35m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
565 item = Hitch("YASim_hitch#%d" % self.counter[tag], c)
567 elif tag == "launchbar":
568 c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
569 length = getfloat(attrs, "length", 1.0)
570 up_angle = getfloat(attrs, "up-angle", -45.0)
571 down_angle = getfloat(attrs, "down-angle", 45.0)
572 holdback = Vector(getfloat(attrs, "holdback-x", c[0]), getfloat(attrs, "holdback-y", c[1]), getfloat(attrs, "holdback-z", c[2]))
573 holdback_length = getfloat(attrs, "holdback-length", 2.0)
574 log("\033[35m%s x=%f y=%f z=%f length=%f down-angle=%f up-angle=%f holdback-x=%f holdback-y=%f holdback-z+%f holdback-length=%f\033[m" \
575 % (tag, c[0], c[1], c[2], length, down_angle, up_angle, \
576 holdback[0], holdback[1], holdback[2], holdback_length))
577 item = Launchbar("YASim_launchbar#%d" % self.counter[tag], c, length, holdback, holdback_length, up_angle, down_angle)
579 elif tag == "wing" or tag == "hstab" or tag == "vstab" or tag == "mstab":
580 root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
581 length = float(attrs["length"])
582 chord = float(attrs["chord"])
583 incidence = getfloat(attrs, "incidence", 0.0)
584 twist = getfloat(attrs, "twist", 0.0)
585 taper = getfloat(attrs, "taper", 1.0)
586 sweep = getfloat(attrs, "sweep", 0.0)
587 dihedral = getfloat(attrs, "dihedral", [0.0, 90.0][tag == "vstab"])
588 log("\033[33;1m%s x=%f y=%f z=%f length=%f chord=%f incidence=%f twist=%f taper=%f sweep=%f dihedral=%f\033[m" \
589 % (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral))
590 item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral)
592 elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler":
593 if not isinstance(parent, Wing):
594 raise Abort("%s is not part of a wing or stab" % path)
596 start = float(attrs["start"])
597 end = float(attrs["end"])
598 log("\t\033[33m%s start=%f end=%f\033[m" % (tag, start, end))
599 parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end)
602 c = Vector(getfloat(attrs, "x", 0.0), getfloat(attrs, "y", 0.0), getfloat(attrs, "z", 0.0))
603 norm = Vector(getfloat(attrs, "nx", 0.0), getfloat(attrs, "ny", 0.0), getfloat(attrs, "nz", 1.0))
604 fwd = Vector(getfloat(attrs, "fx", 1.0), getfloat(attrs, "fy", 0.0), getfloat(attrs, "fz", 0.0))
605 diameter = getfloat(attrs, "diameter", 10.2)
606 numblades = int(getfloat(attrs, "numblades", 4))
607 chord = getfloat(attrs, "chord", 0.3)
608 twist = getfloat(attrs, "twist", 0.0)
609 taper = getfloat(attrs, "taper", 1.0)
610 rel_len_blade_start = getfloat(attrs, "rel-len-blade-start", 0.0)
611 phi0 = getfloat(attrs, "phi0", 0)
612 ccw = not not getfloat(attrs, "ccw", 0)
614 log(("\033[36;1mrotor x=%f y=%f z=%f nx=%f ny=%f nz=%f fx=%f fy=%f fz=%f numblades=%d diameter=%f " \
615 + "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \
616 % (c[0], c[1], c[2], norm[0], norm[1], norm[2], fwd[0], fwd[1], fwd[2], numblades, \
617 diameter, chord, twist, taper, rel_len_blade_start, phi0, ccw))
618 item = Rotor("YASim_rotor#%d" % self.counter[tag], c, norm, fwd, numblades, 0.5 * diameter, chord, \
619 twist, taper, rel_len_blade_start, phi0, ccw)
621 elif tag not in self.ignored:
622 log("\033[30;1m%s\033[m" % path)
624 self.items.append(item)
626 def endElement(self, tag):
631 def extract_matrix(path, tag):
632 v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 }
635 for line in f.readlines():
636 line = string.strip(line)
637 if not line.startswith("<!--") or not line.endswith("-->"):
639 line = string.strip(line[4:-3])
640 if not string.lower(line).startswith("%s:" % tag):
642 line = string.strip(line[len(tag) + 1:])
643 for assignment in string.split(line):
644 (key, value) = string.split(assignment, '=', 2)
645 v[string.strip(key)] = float(string.strip(value))
650 print(("using offsets: x=%f y=%f z=%f h=%f p=%f r=%f" % (v['x'], v['y'], v['z'], v['h'], v['p'], v['r'])))
651 matrix = Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4()
652 matrix *= TranslationMatrix(Vector(v['x'], v['y'], v['z']))
656 def run_parser(path):
657 if BPyMessages.Error_NoFile(path):
660 editmode = Blender.Window.EditMode()
662 Blender.Window.EditMode(0)
663 Blender.Window.WaitCursor(1)
665 print(("loading '%s'" % path))
667 for o in Item.scene.objects:
668 if o.name.startswith("YASim_"):
669 Item.scene.objects.unlink(o)
671 Global.matrix = YASIM_MATRIX
672 matrix = extract_matrix(path, "offsets")
674 Global.matrix *= matrix.invert()
676 yasim = make_parser()
677 yasim.setContentHandler(import_yasim())
678 yasim.setErrorHandler(import_yasim())
681 Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
685 print "Error:", e.msg, " -> aborting ...\n"
686 Blender.Draw.PupMenu("Error%t|" + e.msg)
688 Blender.Window.RedrawAll()
689 Blender.Window.WaitCursor(0)
691 Blender.Window.EditMode(1)
695 from Blender import BGL, Draw
696 (width, height) = Blender.Window.GetAreaSize()
698 BGL.glClearColor(0.4, 0.4, 0.45, 1)
699 BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
700 Draw.PushButton("Reload YASim", 0, 5, 5, 100, 28)
701 Draw.PushButton("Update Cursor", 1, width - 650, 5, 100, 28)
702 BGL.glColor3f(1, 1, 1)
704 BGL.glRasterPos2f(120, 15)
705 Draw.Text(Global.path)
707 BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Distance from last") - Blender.Draw.GetStringWidth("Current"), 24)
708 Draw.Text("Current cursor pos: x = %+.3f y = %+.3f z = %+.3f" % tuple(Global.cursor))
710 c = Global.cursor - Global.last_cursor
711 BGL.glRasterPos2f(width - 530, 7)
712 Draw.Text("Distance from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f" % (c[0], c[1], c[2], c.length))
715 def event(ev, value):
716 if ev == Blender.Draw.ESCKEY:
722 run_parser(Global.path)
724 Global.last_cursor = Global.cursor
725 Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert()
726 d = Global.cursor - Global.last_cursor
727 print(("cursor: x=\"%f\" y=\"%f\" z=\"%f\" dx=%f dy=%f dz=%f length=%f" \
728 % (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length)))
729 Blender.Draw.Redraw(1)
733 registry = Blender.Registry.GetKey("FGYASimImportExport", False)
734 if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
735 path = registry["path"]
741 if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
742 Blender.Draw.Register(draw, event, button)
744 Blender.Window.FileSelector(run_parser, "Import YASim Configuration File", path)