From b1e8917266635a6a5ee32d663dab0c38f68ffefe Mon Sep 17 00:00:00 2001 From: mfranz Date: Sun, 13 Dec 2009 20:23:59 +0000 Subject: [PATCH] add mirror button (useful for asymmetric aircraft, like the BV-141) --- utils/Modeller/yasim_import.py | 155 ++++++++++++++++++++++++--------- 1 file changed, 112 insertions(+), 43 deletions(-) diff --git a/utils/Modeller/yasim_import.py b/utils/Modeller/yasim_import.py index 69b370dd6..f8541edba 100644 --- a/utils/Modeller/yasim_import.py +++ b/utils/Modeller/yasim_import.py @@ -58,9 +58,9 @@ Possible variables are: Of course, absolute FDM coordinates can then no longer directly be read from Blender's 3D view. The cursor coordinates display in the script area, however, shows the coordinates in YASim space. -Note that object names don't contain XML indices but element numbers. YASim_hstab#2 is the third -hstab in the whole file, not necessarily in its parent XML group. A floating point part in the -object name (e.g. YASim_hstab#2.004) only means that the geometry has been reloaded that often. +Note that object names don't contain XML indices but element numbers. YASim_flap0#2 is the third +flap0 in the whole file, not necessarily in its parent XML group. A floating point part in the +object name (e.g. YASim_flap0#2.004) only means that the geometry has been reloaded that often. It's an unavoidable consequence of how Blender deals with meshes. @@ -68,14 +68,14 @@ Elements are displayed as follows: cockpit -> monkey head fuselage -> blue "tube" (with only 12 sides for less clutter); center at "a" - vstab -> red with yellow flaps - wing/mstab/hstab -> green with yellow flaps/spoilers/slats (always 20 cm deep); + vstab -> red with yellow controls surfaces (flap0, flap1, slat, spoiler) + wing/mstab/hstab -> green with yellow control surfaces (which are always 20 cm deep); symmetric surfaces are only displayed on the left side thrusters (jet/propeller/thruster) -> dashed line from center to actionpt; arrow from actionpt along thrust vector (always 1 m long); propeller circle - rotor -> radius and rel_len_blade_start circle, direction arrow, - normal and forward vector, one blade at phi0 + rotor -> radius and rel_len_blade_start circle, normal and forward vector, + one blade at phi0 with direction arrow near blade tip gear -> contact point and compression vector (no arrow head) tank -> cube (10 cm side length) weight -> inverted cone @@ -84,6 +84,11 @@ Elements are displayed as follows: hook -> dashed line for up angle, T-line for down angle launchbar -> dashed line for up angles, T-line for down angles + +The Mirror button complements symmetrical surfaces (wing/hstab/mstab) and control surfaces +(flap0/flap1/slat/spoiler). This is useful for asymmetrical aircraft, but has the disadvantage +that it moves the surfaces' object centers from their usual place, yasim's [x, y, z] value, +to [0, 0, 0]. Turning mirroring off restores the object center. """ @@ -106,11 +111,12 @@ Elements are displayed as follows: #-------------------------------------------------------------------------------- -import Blender, BPyMessages, string, math +import Blender, BPyMessages, string, math, os from Blender.Mathutils import * from xml.sax import handler, make_parser +CONFIG = string.split(os.getenv("BLENDER_YASIM_IMPORT") or "") YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]) ORIGIN = Vector(0, 0, 0) X = Vector(1, 0, 0) @@ -119,12 +125,21 @@ Z = Vector(0, 0, 1) DEG2RAD = math.pi / 180 RAD2DEG = 180 / math.pi +NO_EVENT = 0 +RELOAD_BUTTON = 1 +CURSOR_BUTTON = 2 +MIRROR_BUTTON = 3 + + class Global: + verbose = "verbose" in CONFIG path = "" matrix = None cursor = ORIGIN last_cursor = Vector(Blender.Window.GetCursorPos()) + mirror = Blender.Draw.Create(0) + class Abort(Exception): @@ -132,9 +147,11 @@ class Abort(Exception): self.msg = msg + def log(msg): - #print(msg) # uncomment to get verbose log messages - pass + if Global.verbose: + print(msg) + def error(msg): @@ -142,12 +159,14 @@ def error(msg): Blender.Draw.PupMenu("Error%t|" + msg) + def getfloat(attrs, key, default): if attrs.has_key(key): return float(attrs[key]) return default + def draw_dashed_line(mesh, start, end): w = 0.04 step = w * (end - start).normalize() @@ -161,6 +180,7 @@ def draw_dashed_line(mesh, start, end): mesh.edges.extend([n + 2 * i, n + 2 * i + 1]) + def draw_arrow(mesh, start, end): v = end - start m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start) @@ -171,6 +191,7 @@ def draw_arrow(mesh, start, end): mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]]) + def draw_circle(mesh, numpoints, radius, matrix): n = len(mesh.verts) for i in range(numpoints): @@ -182,6 +203,7 @@ def draw_circle(mesh, numpoints, radius, matrix): mesh.edges.extend([[n + i, n + i1]]) + class Item: scene = Blender.Scene.GetCurrent() @@ -190,13 +212,21 @@ class Item: for f in mesh.faces: f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL - def set_color(self, mesh, name, color): + def set_color(self, obj, name, color): mat = Blender.Material.New(name) mat.setRGBCol(color[0], color[1], color[2]) mat.setAlpha(color[3]) mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW + obj.transp = True + + mesh = obj.getData(mesh = True) mesh.materials += [mat] + for f in mesh.faces: + f.smooth = True + mesh.calcNormals() + + class Cockpit(Item): def __init__(self, center): @@ -206,6 +236,7 @@ class Cockpit(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Tank(Item): def __init__(self, name, center): mesh = Blender.Mesh.Primitives.Cube() @@ -214,6 +245,7 @@ class Tank(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Ballast(Item): def __init__(self, name, center): mesh = Blender.Mesh.Primitives.Cylinder() @@ -222,6 +254,7 @@ class Ballast(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Weight(Item): def __init__(self, name, center): mesh = Blender.Mesh.Primitives.Cone() @@ -230,6 +263,7 @@ class Weight(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Gear(Item): def __init__(self, name, center, compression): mesh = Blender.Mesh.New() @@ -239,6 +273,7 @@ class Gear(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Hook(Item): def __init__(self, name, center, length, up_angle, dn_angle): mesh = Blender.Mesh.New() @@ -252,6 +287,7 @@ class Hook(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Launchbar(Item): def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle): mesh = Blender.Mesh.New() @@ -266,6 +302,7 @@ class Launchbar(Item): obj.setMatrix(TranslationMatrix(lb) * Global.matrix) + class Hitch(Item): def __init__(self, name, center): mesh = Blender.Mesh.Primitives.Circle(8, 0.1) @@ -273,6 +310,7 @@ class Hitch(Item): obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix) + class Thrust: def set_actionpt(self, p): self.actionpt = p @@ -281,6 +319,7 @@ class Thrust: self.thrustvector = d + class Thruster(Thrust, Item): def __init__(self, name, center, thrustvector): (self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector) @@ -294,6 +333,7 @@ class Thruster(Thrust, Item): obj.setMatrix(TranslationMatrix(self.center) * Global.matrix) + class Propeller(Thrust, Item): def __init__(self, name, center, radius): (self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X) @@ -313,6 +353,7 @@ class Propeller(Thrust, Item): obj.setMatrix(TranslationMatrix(self.center) * Global.matrix) + class Jet(Thrust, Item): def __init__(self, name, center, rotate): (self.name, self.center, self.actionpt) = (name, center, center) @@ -327,6 +368,7 @@ class Jet(Thrust, Item): obj.setMatrix(TranslationMatrix(self.center) * Global.matrix) + class Fuselage(Item): def __init__(self, name, a, b, width, taper, midpoint): numvert = 12 @@ -351,10 +393,10 @@ class Fuselage(Item): mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]]) mesh.verts.extend([ORIGIN, length * X]) - self.set_color(mesh, name + "mat", [0, 0, 0.5, 0.4]) obj = self.scene.objects.new(mesh, name) - obj.transp = True obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix) + self.set_color(obj, name + "mat", [0, 0, 0.5, 0.4]) + class Rotor(Item): @@ -380,12 +422,13 @@ class Rotor(Item): obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix) + class Wing(Item): def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral): # <1--0--2 # \ | / # 4-3-5 - is_vstab = name.startswith("YASim_vstab") + self.is_symmetric = not name.startswith("YASim_vstab#") mesh = Blender.Mesh.New() mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X]) tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X @@ -394,16 +437,23 @@ class Wing(Item): mesh.verts.extend([tip, tipfore, tipaft]) mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]]) - self.set_color(mesh, name + "mat", [[0, 0.5, 0, 0.5], [0.5, 0, 0, 0.5]][is_vstab]) self.make_twosided(mesh) obj = self.scene.objects.new(mesh, name) - obj.transp = True - m = Euler(dihedral, -incidence, 0).toMatrix().resize4x4() - m *= TranslationMatrix(root) - obj.setMatrix(m * Global.matrix) + mesh.transform(Euler(dihedral, -incidence, 0).toMatrix().resize4x4()) + self.set_color(obj, name + "mat", [[0.5, 0.0, 0, 0.5], [0.0, 0.5, 0, 0.5]][self.is_symmetric]) (self.obj, self.mesh) = (obj, mesh) + if self.is_symmetric and Global.mirror.val: + mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR) + mod[Blender.Modifier.Settings.AXIS_X] = False + mod[Blender.Modifier.Settings.AXIS_Y] = True + mod[Blender.Modifier.Settings.AXIS_Z] = False + mesh.transform(TranslationMatrix(root)) # must move object center to x axis + obj.setMatrix(Global.matrix) + else: + obj.setMatrix(TranslationMatrix(root) * Global.matrix) + def add_flap(self, name, start, end): a = Vector(self.mesh.verts[2].co) b = Vector(self.mesh.verts[5].co) @@ -416,12 +466,18 @@ class Wing(Item): mesh.verts.extend([i0, i1, i0 + c, i1 + c]) mesh.faces.extend([[0, 1, 3, 2]]) - self.set_color(mesh, name + "mat", [0.8, 0.8, 0, 0.9]) self.make_twosided(mesh) obj = self.scene.objects.new(mesh, name) - obj.transp = True obj.setMatrix(m) + self.set_color(obj, name + "mat", [0.8, 0.8, 0, 0.9]) + + if self.is_symmetric and Global.mirror.val: + mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR) + mod[Blender.Modifier.Settings.AXIS_X] = False + mod[Blender.Modifier.Settings.AXIS_Y] = True + mod[Blender.Modifier.Settings.AXIS_Z] = False + class import_yasim(handler.ContentHandler): @@ -628,6 +684,7 @@ class import_yasim(handler.ContentHandler): self.items.pop() + def extract_matrix(path, tag): v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 } has_offsets = False @@ -653,14 +710,13 @@ def extract_matrix(path, tag): return matrix -def run_parser(path): + +def load_yasim_config(path): if BPyMessages.Error_NoFile(path): return - editmode = Blender.Window.EditMode() - if editmode: - Blender.Window.EditMode(0) Blender.Window.WaitCursor(1) + Blender.Window.EditMode(0) print(("loading '%s'" % path)) try: @@ -687,61 +743,74 @@ def run_parser(path): Blender.Window.RedrawAll() Blender.Window.WaitCursor(0) - if editmode: - Blender.Window.EditMode(1) -def draw(): + +def gui_draw(): from Blender import BGL, Draw (width, height) = Blender.Window.GetAreaSize() BGL.glClearColor(0.4, 0.4, 0.45, 1) BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) - Draw.PushButton("Reload YASim", 0, 5, 5, 100, 28) - Draw.PushButton("Update Cursor", 1, width - 650, 5, 100, 28) + BGL.glColor3f(1, 1, 1) + BGL.glRasterPos2f(5, 55) + Draw.Text("FlightGear YASim Import: '%s'" % Global.path) - BGL.glRasterPos2f(120, 15) - Draw.Text(Global.path) + Draw.PushButton("Reload", RELOAD_BUTTON, 5, 5, 80, 32, "reload YASim config file") + Global.mirror = Draw.Toggle("Mirror", MIRROR_BUTTON, 100, 5, 50, 16, Global.mirror.val, \ + "show symmetric surfaces on both sides (reloads config)") + Draw.PushButton("Update Cursor", CURSOR_BUTTON, width - 650, 5, 100, 32, "update cursor display") - BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Distance from last") - Blender.Draw.GetStringWidth("Current"), 24) + BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Vector from last") - Blender.Draw.GetStringWidth("Current"), 24) Draw.Text("Current cursor pos: x = %+.3f y = %+.3f z = %+.3f" % tuple(Global.cursor)) c = Global.cursor - Global.last_cursor BGL.glRasterPos2f(width - 530, 7) - Draw.Text("Distance from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f" % (c[0], c[1], c[2], c.length)) + Draw.Text("Vector from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f" % (c[0], c[1], c[2], c.length)) -def event(ev, value): + +def gui_event(ev, value): if ev == Blender.Draw.ESCKEY: Blender.Draw.Exit() -def button(n): - if n == 0: - run_parser(Global.path) - elif n == 1: + +def gui_button(n): + if n == NO_EVENT: + return + + elif n == RELOAD_BUTTON: + load_yasim_config(Global.path) + + elif n == CURSOR_BUTTON: Global.last_cursor = Global.cursor Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert() d = Global.cursor - Global.last_cursor print(("cursor: x=\"%f\" y=\"%f\" z=\"%f\" dx=%f dy=%f dz=%f length=%f" \ % (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length))) + + elif n == MIRROR_BUTTON: + load_yasim_config(Global.path) + Blender.Draw.Redraw(1) + def main(): + log(6 * "\n") registry = Blender.Registry.GetKey("FGYASimImportExport", False) if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])): path = registry["path"] else: path = "" - - log(6 * "\n") if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT): - Blender.Draw.Register(draw, event, button) + Blender.Draw.Register(gui_draw, gui_event, gui_button) + + Blender.Window.FileSelector(load_yasim_config, "Import YASim Configuration File", path) - Blender.Window.FileSelector(run_parser, "Import YASim Configuration File", path) main()