first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/compiler.h>
#include <iostream>
#include <GL/glut.h>
#include <plib/ssg.h>
using std::cerr;
using std::endl;
int
main (int ac, char ** av)
{
if (ac != 3) {
cerr << "Usage: " << av[0] << " <file_in> <file_out>" << endl;
return 1;
}
int fakeac = 1;
char * fakeav[] = { "3dconvert",
"Convert a 3D Model",
0 };
glutInit(&fakeac, fakeav);
glutCreateWindow(fakeav[1]);
ssgInit();
ssgEntity * object = ssgLoad(av[1]);
if (object == 0) {
cerr << "Failed to load " << av[1] << endl;
return 2;
}
ssgSave(av[2], object);
}

193
utils/Modeller/ac3d-despeckle Executable file
View File

@@ -0,0 +1,193 @@
#!/usr/bin/perl -w
# $Id$
# Melchior FRANZ < mfranz # aon : at > Public Domain
#
# Older nVidia cards like the GF4 MX440 don't like UV faces
# where all coordinates are equal, and thus don't have an area.
# This leads to grey spots that are constantly going on and off.
# The script fixes UV faces with 3 and 4 references by assigning
# a one (half)pixel sized area, and moves coordinates into the
# texture area if necessary.
#
use strict;
my $SELF = $0;
$SELF =~ s,.*\/,,;
my $VERBOSE = 0;
my $NOOP = 0;
my $TEXPATH = ".";
my $help = <<EOF;
Usage:
$SELF [-v] [-n] [-t <path>] <infile.ac >outfile.ac
Options:
-h this help
-v verbose output (also shows line numbers if used twice: -v -v)
-n dry run
-t <path> texture directory (default $TEXPATH)
Output:
# BAD (face with identical UV coords; will be fixed)
? CHECK (only two UV coordinates or more than 4; could be a problem)
. OK (wrong coordinates, but no texture assigned; ignore)
EOF
my $OBJECTNAME;
my $TEXTURE;
my $PIXW = 0;
my $PIXH = 0;
sub fatal($) {
die shift;
}
sub report($) {
print STDERR shift;
print STDERR "[$.] " if $VERBOSE > 1;
}
sub parse_args() {
while (@ARGV) {
my $arg = shift @ARGV;
if ($arg eq '-h' or $arg eq '--help') {
print STDERR $help;
exit 0;
} elsif ($arg eq '-v' or $arg eq '--verbose') {
$VERBOSE++;
} elsif ($arg eq '-n' or $arg eq '--dry-run') {
$NOOP = 1;
} elsif ($arg eq '-t') {
$arg = shift @ARGV;
defined $arg or fatal('-t option without <text> argument');
$TEXPATH = $arg;
}
}
}
sub sgisize($) {
my $name = shift;
my $i;
-f $name or fatal("cannot find texture '$name'");
-s $name > 512 or fatal("'$name' cannot be an SGI image (too small)");
open(IMG, "<$name") || fatal("cannot open file '$name': $!");
read(IMG, $i, 2);
unpack("n", $i) == 474 or fatal("'$name' cannot be an SGI image (wrong magic number)");
my ($x, $y);
seek(IMG, 6, 0);
read(IMG, $x, 2);
read(IMG, $y, 2);
close IMG or fatal("cannot close file '$name': $!");
return (unpack("n", $x), unpack("n", $y));
}
# main()
parse_args();
print STDERR "\033[35mTEXTURE PATH: $TEXPATH\033[m" if $VERBOSE;
while (<>) {
if (/^OBJECT /) {
undef $TEXTURE;
} elsif (/^name\s+(.*)/) {
$OBJECTNAME = $1;
$OBJECTNAME =~ /"(.*)"/ and $OBJECTNAME = $1;
print STDERR "\033[35;1m\nNAME: $OBJECTNAME\033[m " if $VERBOSE;
} elsif (/^texture\s+(.*)/) {
$TEXTURE = $1;
$TEXTURE =~ /"(.*)"/ and $TEXTURE = $1;
my ($WIDTH, $HEIGHT) = sgisize($TEXPATH . '/' . $TEXTURE);
print STDERR "\033[33;1m\nTEXTURE: $TEXTURE ($WIDTH x $HEIGHT)\033[m " if $VERBOSE;
$PIXW = 1.0 / $WIDTH;
$PIXH = 1.0 / $HEIGHT;
} elsif (/^SURF /) {
print $_ unless $NOOP;
my $mat = <>;
$mat =~ /^mat / or fatal("line $.: mat entry missing");
print $mat unless $NOOP;
my $refs = <>;
$refs =~ /^refs\s+(\d+)/ or fatal("line $.: refs entry missing");
print $refs unless $NOOP;
my $n = $1;
if ($n < 3 or $n > 4) {
report("?") if $VERBOSE;
unless ($NOOP) {
print scalar(<>) foreach (1 .. $n);
}
next;
}
my (@ref0, @ref1, @ref2, @ref3);
@ref0 = split /\s+/, <>;
@ref1 = split /\s+/, <>;
@ref2 = split /\s+/, <>;
@ref3 = split /\s+/, <> if $n == 4;
goto writeuv if $ref0[1] != $ref1[1] or $ref0[2] != $ref1[2] or $ref1[1] != $ref2[1] or $ref1[2] != $ref2[2];
goto writeuv if $n == 4 and ($ref2[1] != $ref3[1] or $ref2[2] != $ref3[2]);
if (defined $TEXTURE) {
report("#") if $VERBOSE;
my ($x, $y) = @ref0[1, 2];
$x = 0.0 if $x < 0.0;
$x = 1.0 - $PIXW if $x > 1.0 - $PIXW;
$y = 0.0 if $y < 0.0;
$y = 1.0 - $PIXH if $y > 1.0 - $PIXH;
$x = int($x / $PIXW) * $PIXW;
$y = int($y / $PIXH) * $PIXH;
$ref0[1] = $x, $ref0[2] = $y;
$ref1[1] = $x + $PIXW;
$ref2[1] = $x + $PIXW, $ref2[2] = $y + $PIXH;
$ref3[2] = $y + $PIXH if $n == 4;
} else {
report(".") if $VERBOSE;
}
writeuv:
unless ($NOOP) {
print join " ", @ref0;
print "\n";
print join " ", @ref1;
print "\n";
print join " ", @ref2;
print "\n";
if ($n == 4) {
print join " ", @ref3;
print "\n";
}
}
next;
}
print unless $NOOP;
}
print STDERR "\n" if $VERBOSE;
exit 0;

View File

@@ -0,0 +1,79 @@
/*
// animassist.c
//
// Jim Wilson 5/8/2003
//
// Copyright (C) 2003 Jim Wilson - jimw@kelcomaine.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int main( int argc, char **argv ) {
float x1,y1,z1,x2,y2,z2 = 0;
float center_x,center_y,center_z,axis_x,axis_y,axis_z;
float vector_length;
if (argc < 7) {
printf("animassist - utility to calculate axis and center values for SimGear model animation\n\n");
printf("Usage: animassist x1 y1 z1 x2 y2 z2\n\n");
printf("Defining two vectors in SimGear coords (x1,y1,z1) and (x2,y2,z2)\n\n");
printf("Conversion from model to SimGear:\n");
printf("SimGear Coord AC3D Coordinate\n");
printf(" X = X\n");
printf(" Y = Z * -1\n");
printf(" Z = Y\n\n");
printf("Note: no normalization done, so please make the 2nd vector the furthest out\n");
} else {
x1 = atof(argv[1]);
y1 = atof(argv[2]);
z1 = atof(argv[3]);
x2 = atof(argv[4]);
y2 = atof(argv[5]);
z2 = atof(argv[6]);
center_x = (x1+x2)/2;
center_y = (y1+y2)/2;
center_z = (z1+z2)/2;
vector_length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1));
/* arbitrary axis defined by cos(theta) where theta is angle from x,y or z axis */
axis_x = (x2-x1)/vector_length;
axis_y = (y2-y1)/vector_length;
axis_z = (z2-z1)/vector_length;
printf("Flightgear Model XML for:\n");
printf("Axis from (%f,%f,%f) to (%f,%f,%f)\n", x1,y1,z1,x2,y2,z2);
printf("Assuming units in meters!\n\n");
printf("<center>\n");
printf(" <x-m>%f</x-m>\n",center_x);
printf(" <y-m>%f</y-m>\n",center_y);
printf(" <z-m>%f</z-m>\n",center_z);
printf("</center>\n\n");
printf("<axis>\n");
printf(" <x>%f</x>\n",axis_x);
printf(" <y>%f</y>\n",axis_y);
printf(" <z>%f</z>\n",axis_z);
printf("</axis>\n\n");
}
return 0;
}

68
utils/Modeller/animcheck.pl Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/perl -w
# Quick & dirty script to find booboos in FlightGear animation files
# May 2004 Joshua Babcock jbabcock@atlantech.net
use strict;
if ( $#ARGV != 1 ) {
print "Usage: $0 <xml file> <ac3d file>\n";
exit;
}
my %Objects;
my %References;
my $Key;
my $Num;
my $First;
my $ObjCount=0;
my $CheckForm=1;
# Put whatever you want in here to check for poorly formatted object names.
sub CheckForm {
my $Bad=0;
$_[0] !~ /^[A-Z].*/ && ($Bad=1);
$_[0] =~ /\W/ && ($Bad=1);
print "$_[0] poorly formatted\n" if $Bad;
}
# Make a hash of all the object names in the AC3D file.
open (AC3D, $ARGV[1]) or die "Could not open $ARGV[1]";
while (<AC3D>) {
/^name \"(.*)\"/ && ($Objects{$1}+=1);
}
close AC3D;
# Check for duplicates and proper format.
foreach $Key (keys %Objects) {
print "$Objects{$Key} instances of $1\n" if ($Objects{$Key}>1);
&CheckForm($Key) if $CheckForm;
++$ObjCount;
}
print "$ObjCount objects found.\n\n";
# Make a hash of objects in the XML file that do not reference an object in the AC3D file.
open (XML, $ARGV[0]) or die "Could not open $ARGV[0]";
while (<XML>) {
if (m|<object-name>(.*)</object-name>| && ! exists($Objects{$1})) {
# voodoo, "Perl Cookbook", p140
push( @{$References{$1}}, $.);
}
}
close XML;
# List all the bad referencees.
foreach $Key (keys %References) {
$First=1;
print "Non-existant object $Key at line";
print "s" if (scalar( @{$References{$Key}}) > 1);
print ":";
foreach $Num (@{$References{$Key}}) {
$First ? ($First=0) : (print ",");
print " $Num"
}
print "\n";
}
exit 0;

View File

@@ -0,0 +1,585 @@
#!BPY
# """
# Name: 'FlightGear Animation (.xml)'
# Blender: 243
# Group: 'Export'
# Submenu: 'Generate textured lights' LIGHTS
# Submenu: 'Rotation' ROTATE
# Submenu: 'Range (LOD)' RANGE
# Submenu: 'Translation from origin' TRANS0
# Submenu: 'Translation from cursor' TRANSC
# Tooltip: 'FlightGear Animation'
# """
# BLENDER PLUGIN
#
# Put this file into ~/.blender/scripts/. You'll then find
# it in Blender under "File->Export->FlightGear Animation (*.xml)"
#
# For the script to work properly, make sure a directory
# ~/.blender/scripts/bpydata/config/ exists. To change the
# script parameters, edit file fgfs_animation.cfg in that
# dir, or in blender, switch to "Scripts Window" view, select
# "Scripts->System->Scripts Config Editor" and there select
# "Export->FlightGear Animation (*.xml)". Don't forget to
# "apply" the changes.
#
# This file is Public Domain.
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = "http://members.aon.at/mfranz/flightgear/"
__version__ = "$Revision$ -- $Date$"
__bpydoc__ = """\
== Generate textured lights ==
Adds linked "light objects" (square consisting of two triangles at
origin) for each selected vertex, and creates FlightGear animation
code that scales all faces according to view distance, moves them to
the vertex location, turns the face towards the viewer and blends
it with other (semi)transparent objects. All lights are turned off
at daylight.
== Translation from origin ==
Generates "translate" animation for each selected vertex and for the
cursor, if it is not at origin. This mode is thought for moving
objects to their proper location after scaling them at origin, a
technique that is used for lights.
== Translation from cursor ==
Same as above, except that movements from the cursor location are
calculated.
== Rotation ==
Generates one "rotate" animation from two selected vertices
(rotation axis), and the cursor (rotation center).
== Range ==
Generates "range" animation skeleton with list of all selected objects.
"""
#==================================================================================================
import sys
import Blender
from math import sqrt
from Blender import Mathutils
from Blender.Mathutils import Vector, VecMultMat
REG_KEY = 'fgfs_animation'
MODE = __script__['arg']
ORIGIN = [0, 0, 0]
FILENAME = Blender.Get("filename")
(BASENAME, EXTNAME) = Blender.sys.splitext(FILENAME)
DIRNAME = Blender.sys.dirname(BASENAME)
FILENAME = Blender.sys.basename(BASENAME) + "-export.xml"
TOOLTIPS = {
'PATHNAME': "where exported data should be saved (current dir if empty)",
'INDENT': "number of spaces per indentation level, or 0 for tabs",
}
reg = Blender.Registry.GetKey(REG_KEY, True)
if not reg:
print "WRITING REGISTRY"
Blender.Registry.SetKey(REG_KEY, {
'INDENT': 0,
'PATHNAME': "",
'tooltips': TOOLTIPS,
}, True)
#==================================================================================================
class Error(Exception):
pass
class BlenderSetup:
def __init__(self):
#Blender.Window.WaitCursor(1)
self.is_editmode = Blender.Window.EditMode()
if self.is_editmode:
Blender.Window.EditMode(0)
def __del__(self):
#Blender.Window.WaitCursor(0)
if self.is_editmode:
Blender.Window.EditMode(1)
class XMLExporter:
def __init__(self, filename):
cursor = Blender.Window.GetCursorPos()
self.file = open(filename, "w")
self.write('<?xml version="1.0"?>\n')
self.write("<!-- project: %s -->\n" % Blender.Get("filename"))
self.write("<!-- cursor: %0.12f %0.12f %0.12f -->\n\n" % (cursor[0], cursor[1], cursor[2]))
self.write("<PropertyList>\n")
self.write("\t<path>%s.ac</path>\n\n" % BASENAME)
def __del__(self):
try:
self.write("</PropertyList>\n")
self.file.close()
except AttributeError: # opening in __init__ failed
pass
def write(self, s):
global INDENT
if INDENT <= 0:
self.file.write(s)
else:
self.file.write(s.expandtabs(INDENT))
def comment(self, s, e = "", a = ""):
self.write(a + "<!-- " + s + " -->\n" + e)
def translate(self, name, va, vb):
x = vb[0] - va[0]
y = vb[1] - va[1]
z = vb[2] - va[2]
length = sqrt(x * x + y * y + z * z)
s = """\
<animation>
<type>translate</type>
<object-name>%s</object-name>
<offset-m>%s</offset-m>
<axis>
<x>%s</x>
<y>%s</y>
<z>%s</z>
</axis>
</animation>\n\n"""
self.write(s % (name, Round(length), Round(x), Round(y), Round(z)))
def rotate(self, name, center, va, vb):
x = Round(vb[0] - va[0])
y = Round(vb[1] - va[1])
z = Round(vb[2] - va[2])
self.write("\t<!-- Vertex 0: %f %f %f -->\n" % (va[0], va[1], va[2]))
self.write("\t<!-- Vertex 1: %f %f %f -->\n" % (vb[0], vb[1], vb[2]))
s = """\
<animation>
<type>rotate</type>
<object-name>%s</object-name>
<property>null</property>
<!--min-deg>0</min-deg-->
<!--max-deg>360</max-deg-->
<!--factor>1</factor-->
<center>
<x-m>%s</x-m>
<y-m>%s</y-m>
<z-m>%s</z-m>
</center>
<axis>
<x>%s</x>
<y>%s</y>
<z>%s</z>
</axis>
</animation>\n\n"""
self.write(s % (name, Round(center[0]), Round(center[1]), Round(center[2]), x, y, z))
def billboard(self, name, spherical = "true"):
s = """\
<animation>
<type>billboard</type>
<object-name>%s</object-name>
<spherical type="bool">%s</spherical>
</animation>\n\n"""
self.write(s % (name, spherical))
def group(self, name, objects):
self.write("\t<animation>\n");
self.write("\t\t<name>%s</name>\n" % name);
for o in objects:
self.write("\t\t<object-name>%s</object-name>\n" % o.name)
self.write("\t</animation>\n\n");
def alphatest(self, name, factor = 0.001):
s = """\
<animation>
<type>alpha-test</type>
<object-name>%s</object-name>
<alpha-factor>%s</alpha-factor>
</animation>\n\n"""
self.write(s % (name, factor))
def sunselect(self, name, angle = 1.57):
s = """\
<animation>
<type>select</type>
<name>%s</name>
<object-name>%s</object-name>
<condition>
<greater-than>
<property>/sim/time/sun-angle-rad</property>
<value>%s</value>
</greater-than>
</condition>
</animation>\n\n"""
self.write(s % (name + "Night", name, angle))
def distscale(self, name, base):
s = """\
<animation>
<type>dist-scale</type>
<object-name>%s</object-name>
<interpolation>
<entry>
<ind>0</ind>
<dep alias="../../../../%sparams/light-near"/>
</entry>
<entry>
<ind>500</ind>
<dep alias="../../../../%sparams/light-med"/>
</entry>
<entry>
<ind>16000</ind>
<dep alias="../../../../%sparams/light-far"/>
</entry>
</interpolation>
</animation>\n\n"""
self.write(s % (name, base, base, base))
def range(self, objects):
self.write("\t<animation>\n")
self.write("\t\t<type>range</type>\n")
for o in objects:
self.write("\t\t<object-name>%s</object-name>\n" % o.name)
self.write("""\
<min-m>0</min-m>
<!--
<max-property>/sim/rendering/static-lod/detailed</max-property>
<min-property>/sim/rendering/static-lod/detailed</min-property>
<max-property>/sim/rendering/static-lod/rough</max-property>
<min-property>/sim/rendering/static-lod/rough</min-property>
-->
<max-property>/sim/rendering/static-lod/bare</max-property>\n""")
self.write("\t</animation>\n\n")
def lightrange(self, dist = 25000):
s = """\
<animation>
<type>range</type>
<min-m>0</min-m>
<max-m>%s</max-m>
</animation>\n\n"""
self.write(s % dist)
#==================================================================================================
def Round(f, digits = 6):
r = round(f, digits)
if r == int(r):
return str(int(r))
else:
return str(r)
def serial(i, max = 0):
if max == 1:
return ""
if max < 10:
return "%d" % i
if max < 100:
return "%02d" % i
if max < 1000:
return "%03d" % i
if max < 10000:
return "%04d" % i
return "%d" % i
def needsObjects(objects, n):
def error(e):
raise Error("wrong number of selected mesh objects: please select " + e)
if n < 0 and len(objects) < -n:
if n == -1:
error("at least one object")
else:
error("at least %d objects" % -n)
elif n > 0 and len(objects) != n:
if n == 1:
error("exactly one object")
else:
error("exactly %d objects" % n)
def checkName(name):
""" check if name is already in use """
try:
Blender.Object.Get(name)
raise Error("can't generate object '" + name + "'; name already in use")
except AttributeError:
pass
except ValueError:
pass
def selectedVertices(object):
verts = []
mat = object.getMatrix('worldspace')
for v in object.getData().verts:
if not v.sel:
continue
vec = Vector([v[0], v[1], v[2]])
vec.resize4D()
vec *= mat
v[0], v[1], v[2] = vec[0], vec[1], vec[2]
verts.append(v)
return verts
def createLightMesh(name, size = 1):
mesh = Blender.NMesh.New(name + "mesh")
mesh.mode = Blender.NMesh.Modes.NOVNORMALSFLIP
mesh.verts.append(Blender.NMesh.Vert(-size, 0, -size))
mesh.verts.append(Blender.NMesh.Vert(size, 0, -size))
mesh.verts.append(Blender.NMesh.Vert(size, 0, size))
mesh.verts.append(Blender.NMesh.Vert(-size, 0, size))
face1 = Blender.NMesh.Face()
face1.v.append(mesh.verts[0])
face1.v.append(mesh.verts[1])
face1.v.append(mesh.verts[2])
mesh.faces.append(face1)
face2 = Blender.NMesh.Face()
face2.v.append(mesh.verts[0])
face2.v.append(mesh.verts[2])
face2.v.append(mesh.verts[3])
mesh.faces.append(face2)
mat = Blender.Material.New(name + "mat")
mat.setRGBCol(1, 1, 1)
mat.setMirCol(1, 1, 1)
mat.setAlpha(1)
mat.setEmit(1)
mat.setSpecCol(0, 0, 0)
mat.setSpec(0)
mat.setAmb(0)
mesh.setMaterials([mat])
return mesh
def createLight(mesh, name):
object = Blender.Object.New("Mesh", name)
object.link(mesh)
Blender.Scene.getCurrent().link(object)
return object
# modes ===========================================================================================
class mode:
def __init__(self, xml = None):
self.cursor = Blender.Window.GetCursorPos()
self.objects = [o for o in Blender.Object.GetSelected() if o.getType() == 'Mesh']
if xml is not None:
BlenderSetup()
self.test()
self.execute(xml)
def test(self):
pass
class translationFromOrigin(mode):
def execute(self, xml):
if self.cursor != ORIGIN:
xml.translate("BlenderCursor", ORIGIN, self.cursor)
needsObjects(self.objects, 1)
object = self.objects[0]
verts = selectedVertices(object)
if len(verts):
xml.comment('[%s] translate from origin: "%s"' % (BASENAME, object.name), '\n')
for i, v in enumerate(verts):
xml.translate("X%d" % i, ORIGIN, v)
class translationFromCursor(mode):
def test(self):
needsObjects(self.objects, 1)
self.object = self.objects[0]
self.verts = selectedVertices(self.object)
if not len(self.verts):
raise Error("no vertex selected")
def execute(self, xml):
xml.comment('[%s] translate from cursor: "%s"' % (BASENAME, self.object.name), '\n')
for i, v in enumerate(self.verts):
xml.translate("X%d" % i, self.cursor, v)
class rotation(mode):
def test(self):
needsObjects(self.objects, 1)
self.object = self.objects[0]
self.verts = selectedVertices(self.object)
if len(self.verts) != 2:
raise Error("you have to select two vertices that define the rotation axis!")
def execute(self, xml):
xml.comment('[%s] rotate "%s"' % (BASENAME, self.object.name), '\n')
if self.cursor == ORIGIN:
Blender.Draw.PupMenu("The cursor is still at origin!%t|"\
"But nevertheless, I pretend it is the rotation center.")
xml.rotate(self.object.name, self.cursor, self.verts[0], self.verts[1])
class levelOfDetail(mode):
def test(self):
needsObjects(self.objects, -1)
def execute(self, xml):
xml.comment('[%s] level of detail' % BASENAME, '\n')
xml.range(self.objects)
class interpolation(mode):
def test(self):
needsObjects(self.objects, -2)
def execute(self, xml):
print
for i, o in enumerate(self.objects):
print "%d: %s" % (i, o.name)
raise Error("this mode doesn't do anything useful yet")
class texturedLights(mode):
def test(self):
needsObjects(self.objects, 1)
self.object = self.objects[0]
self.verts = selectedVertices(self.object)
if not len(self.verts):
raise Error("there are no vertices to put lights at")
self.lightname = self.object.name + "X"
checkName(self.lightname)
for i, v in enumerate(self.verts):
checkName(self.lightname + serial(i, len(self.verts)))
def execute(self, xml):
lightname = self.lightname
verts = self.verts
object = self.object
lightmesh = createLightMesh(lightname)
lights = []
for i, v in enumerate(verts):
lights.append(createLight(lightmesh, lightname + serial(i, len(verts))))
parent = object.getParent()
if parent is not None:
parent.makeParent(lights)
for l in lights:
l.Layer = object.Layer
xml.lightrange()
xml.write("\t<%sparams>\n" % lightname)
xml.write("\t\t<light-near>%s</light-near>\n" % "0.4")
xml.write("\t\t<light-med>%s</light-med>\n" % "0.8")
xml.write("\t\t<light-far>%s</light-far>\n" % "10")
xml.write("\t</%sparams>\n\n" % lightname)
if len(lights) == 1:
xml.sunselect(lightname)
xml.alphatest(lightname)
else:
xml.group(lightname + "Group", lights)
xml.sunselect(lightname + "Group")
xml.alphatest(lightname + "Group")
for i, l in enumerate(lights):
xml.translate(l.name, ORIGIN, verts[i])
for l in lights:
xml.billboard(l.name)
for l in lights:
xml.distscale(l.name, lightname)
Blender.Redraw(-1)
execute = {
'LIGHTS' : texturedLights,
'TRANS0' : translationFromOrigin,
'TRANSC' : translationFromCursor,
'ROTATE' : rotation,
'RANGE' : levelOfDetail,
'INTERPOL' : interpolation
}
# main() ==========================================================================================
def dofile(filename):
try:
xml = XMLExporter(filename)
execute[MODE](xml)
except Error, e:
xml.comment("ERROR: " + e.args[0], '\n')
raise Error(e.args[0])
except IOError, (errno, strerror):
raise Error(strerror)
def main():
try:
global FILENAME, INDENT
reg = Blender.Registry.GetKey(REG_KEY, True)
if reg:
PATHNAME = reg['PATHNAME'] or ""
INDENT = reg['INDENT'] or 0
else:
PATHNAME = ""
INDENT = 0
if PATHNAME:
FILENAME = Blender.sys.join(PATHNAME, FILENAME)
print 'writing to "' + FILENAME + '"'
dofile(FILENAME)
except Error, e:
print "ERROR: " + e.args[0]
Blender.Draw.PupMenu("ERROR%t|" + e.args[0])
if MODE:
main()

View File

@@ -0,0 +1,134 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h> // exit()
#include <simgear/bucket/newbucket.hxx>
#include <simgear/math/sg_geodesy.hxx>
int make_model( const char *texture,
double ll_lon, double ll_lat,
double lr_lon, double lr_lat,
double ur_lon, double ur_lat,
double ul_lon, double ul_lat,
const char *model )
{
// open the output file
FILE *out = fopen( model, "w" );
if ( out == NULL ) {
printf("Error: cannot open %s for writing\n", model);
return 0;
}
// compute polygon boundaries in local XY projection
double cen_lon = (ll_lon + lr_lon + ur_lon + ul_lon) / 4.0;
double cen_lat = (ll_lat + lr_lat + ur_lat + ul_lat) / 4.0;
double az1, az2, dist;
double angle;
geo_inverse_wgs_84( cen_lat, cen_lon, ll_lat, ll_lon, &az1, &az2, &dist );
angle = az1 * SGD_DEGREES_TO_RADIANS;
double llx = sin(angle) * dist;
double lly = cos(angle) * dist;
printf( "az1 = %.3f dx = %.3f dy = %.3f\n",
az1, sin(angle) * dist, cos(angle) * dist );
geo_inverse_wgs_84( cen_lat, cen_lon, lr_lat, lr_lon, &az1, &az2, &dist );
angle = az1 * SGD_DEGREES_TO_RADIANS;
double lrx = sin(angle) * dist;
double lry = cos(angle) * dist;
printf( "az1 = %.3f dx = %.3f dy = %.3f\n",
az1, sin(angle) * dist, cos(angle) * dist );
geo_inverse_wgs_84( cen_lat, cen_lon, ur_lat, ur_lon, &az1, &az2, &dist );
angle = az1 * SGD_DEGREES_TO_RADIANS;
double urx = sin(angle) * dist;
double ury = cos(angle) * dist;
printf( "az1 = %.3f dx = %.3f dy = %.3f\n",
az1, sin(angle) * dist, cos(angle) * dist );
geo_inverse_wgs_84( cen_lat, cen_lon, ul_lat, ul_lon, &az1, &az2, &dist );
angle = az1 * SGD_DEGREES_TO_RADIANS;
double ulx = sin(angle) * dist;
double uly = cos(angle) * dist;
printf( "az1 = %.3f dx = %.3f dy = %.3f\n",
az1, sin(angle) * dist, cos(angle) * dist );
fprintf( out, "AC3Db\n" );
fprintf( out, "MATERIAL \"DefaultWhite\" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0\n" );
fprintf( out, "OBJECT world\n" );
fprintf( out, "kids 1\n" );
fprintf( out, "OBJECT poly\n" );
fprintf( out, "name \"terrain\"\n" );
fprintf( out, "data 9\n" );
fprintf( out, "Plane.073\n" );
fprintf( out, "texture \"%s\"\n", texture );
fprintf( out, "texrep 1 1\n" );
fprintf( out, "crease 30.000000\n" );
fprintf( out, "numvert 4\n" );
fprintf( out, "%.3f 0 %.3f\n", lry, lrx );
fprintf( out, "%.3f 0 %.3f\n", lly, llx );
fprintf( out, "%.3f 0 %.3f\n", uly, ulx );
fprintf( out, "%.3f 0 %.3f\n", ury, urx );
fprintf( out, "numsurf 1\n" );
fprintf( out, "SURF 0x00\n" );
fprintf( out, "mat 0\n" );
fprintf( out, "refs 4\n" );
fprintf( out, "0 0.0 1.0\n" );
fprintf( out, "3 0.0 0.0\n" );
fprintf( out, "2 1.0 0.0\n" );
fprintf( out, "1 1.0 1.0\n" );
fprintf( out, "kids 0\n" );
fclose( out );
SGBucket b( cen_lon, cen_lat );
printf("\n");
printf("Model is created as %s\n", model);
printf("\n");
printf("Now copy the .ac and .rgb file to your Model directory\n");
printf("and add the following line to your .stg file:\n");
printf("\n");
printf(" %s/%s.stg\n",
b.gen_base_path().c_str(), b.gen_index_str().c_str());
printf("\n");
printf("OBJECT_SHARED Models/MNUAV/%s %.6f %.6f ALT_IN_METERS 0.00\n",
model, cen_lon, cen_lat);
return 1;
}
void usage( char *prog ) {
printf("Usage: %s photo.rgb ll_lon ll_lat lr_lon lr_lat ur_lon ur_lat ul_lon ul_lat model.ac\n", prog);
printf("Usage: %s photo.rgb left_lon right_lon lower_lat upper_lat model.ac\n", prog);
exit( 0 );
}
int main( int argc, char **argv ) {
if ( argc == 11 ) {
make_model( argv[1],
atof(argv[2]), atof(argv[3]), atof(argv[4]), atof(argv[5]),
atof(argv[6]), atof(argv[7]), atof(argv[8]), atof(argv[9]),
argv[10] );
} else if ( argc == 7 ) {
make_model( argv[1],
atof(argv[2]), atof(argv[4]), atof(argv[3]), atof(argv[4]),
atof(argv[3]), atof(argv[5]), atof(argv[2]), atof(argv[5]),
argv[6] );
} else {
usage( argv[0] );
}
return 0;
}

View File

@@ -0,0 +1,174 @@
#!BPY
# """
# Name: 'UV: Export to SVG'
# Blender: 245
# Group: 'Image'
# Tooltip: 'Export selected objects to SVG file'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/uv_export_svg.py"]
__version__ = "0.1"
__bpydoc__ = """\
Saves the UV mappings of all selected objects to an SVG file. The uv_import_svg.py
script can be used to re-import such a file. Each object and each group of adjacent
faces therein will be made a separate SVG group.
"""
#--------------------------------------------------------------------------------
# Copyright (C) 2008 Melchior FRANZ < mfranz # aon : at >
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#--------------------------------------------------------------------------------
FILL_COLOR = None # 'yellow' or '#ffa000' e.g. for uni-color, None for random color
ID_SEPARATOR = '_.:._'
import Blender, BPyMessages, sys, random
class Abort(Exception):
def __init__(self, msg):
self.msg = msg
class UVFaceGroups:
def __init__(self, mesh):
faces = dict([(f.index, f) for f in mesh.faces])
self.groups = []
while faces:
self.groups.append(self.adjacent(faces))
def __len__(self):
return len(self.groups)
def __iter__(self):
return self.groups.__iter__()
def adjacent(self, faces):
uvcoords = {}
face = faces.popitem()[1]
group = [face]
for c in face.uv:
uvcoords[tuple(c)] = True
while True:
found = []
for face in faces.itervalues():
for c in face.uv:
if tuple(c) in uvcoords:
for c in face.uv:
uvcoords[tuple(c)] = True
found.append(face)
break
if not found:
return group
for face in found:
group.append(face)
del faces[face.index]
def stringcolor(string):
random.seed(hash(string))
c = [random.randint(220, 255), random.randint(120, 240), random.randint(120, 240)]
random.shuffle(c)
return "#%02x%02x%02x" % tuple(c)
def write_svg(path):
size = Blender.Draw.PupMenu("Image size%t|128|256|512|1024|2048|4096|8192")
if size < 0:
raise Abort('no image size chosen')
size = 1 << (size + 6)
svg = open(path, "w")
svg.write('<?xml version="1.0" standalone="no"?>\n')
svg.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n\n')
svg.write('<svg width="%spx" height="%spx" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg"' \
' version="1.1" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">\n'
% (size, size, size, size))
svg.write("\t<desc>uv_export_svg.py: %s</desc>\n" % path);
svg.write('\t<rect x="0" y="0" width="%d" height="%d" fill="none" stroke="blue" stroke-width="%f"/>\n'
% (size, size, 1.0))
objects = {}
for o in Blender.Scene.GetCurrent().objects.selected:
if o.type != "Mesh":
continue
mesh = o.getData(mesh = 1)
if mesh.faceUV:
objects[mesh.name] = (o.name, mesh)
for meshname, v in objects.iteritems():
objname, mesh = v
color = FILL_COLOR or stringcolor(meshname)
svg.write('\t<g style="fill:%s; stroke:black stroke-width:1px" inkscape:label="%s" ' \
'id="%s">\n' % (color, objname, objname))
facegroups = UVFaceGroups(mesh)
for faces in facegroups:
indent = '\t\t'
if len(facegroups) > 1:
svg.write('\t\t<g>\n')
indent = '\t\t\t'
for f in faces:
svg.write('%s<polygon id="%s%s%d" points="' % (indent, meshname, ID_SEPARATOR, f.index))
for p in f.uv:
svg.write('%.8f,%.8f ' % (p[0] * size, size - p[1] * size))
svg.write('"/>\n')
if len(facegroups) > 1:
svg.write('\t\t</g>\n')
svg.write("\t</g>\n")
svg.write('</svg>\n')
svg.close()
def export(path):
if not BPyMessages.Warning_SaveOver(path):
return
editmode = Blender.Window.EditMode()
if editmode:
Blender.Window.EditMode(0)
try:
write_svg(path)
Blender.Registry.SetKey("UVImportExportSVG", { "path" : path }, False)
except Abort, e:
print "Error:", e.msg, " -> aborting ...\n"
Blender.Draw.PupMenu("Error%t|" + e.msg)
if editmode:
Blender.Window.EditMode(1)
registry = Blender.Registry.GetKey("UVImportExportSVG", False)
if registry and "path" in registry:
path = registry["path"]
else:
active = Blender.Scene.GetCurrent().objects.active
basename = Blender.sys.basename(Blender.sys.splitext(Blender.Get("filename"))[0])
path = basename + "-" + active.name + ".svg"
Blender.Window.FileSelector(export, "Export to SVG", path)

308
utils/Modeller/uv_import_svg.py Executable file
View File

@@ -0,0 +1,308 @@
#!BPY
# """
# Name: 'UV: (Re)Import UV from SVG'
# Blender: 245
# Group: 'Image'
# Tooltip: 'Re-import UV layout from SVG file'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/uv_import_svg.py"]
__version__ = "0.2"
__bpydoc__ = """\
Imports an SVG file containing UV maps, which has been saved by the
uv_export.svg script. This allows to move, scale, and rotate object
mappings in SVG editors like Inkscape. Note that all contained UV maps
will be set, no matter which objects are actually selected at the moment.
The choice has been made when the file was saved!
"""
#--------------------------------------------------------------------------------
# Copyright (C) 2008 Melchior FRANZ < mfranz # aon : at >
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#--------------------------------------------------------------------------------
ID_SEPARATOR = '_.:._'
import Blender, BPyMessages, sys, math, re, xml.sax
numwsp = re.compile('(?<=[\d.])\s+(?=[-+.\d])')
commawsp = re.compile('\s+|\s*,\s*')
istrans = re.compile('^\s*(skewX|skewY|scale|translate|rotate|matrix)\s*\(([^\)]*)\)\s*')
isnumber = re.compile('^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$')
class Abort(Exception):
def __init__(self, msg):
self.msg = msg
class Matrix:
def __init__(self, a = 1, b = 0, c = 0, d = 1, e = 0, f = 0):
self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
def __str__(self):
return "[Matrix %f %f %f %f %f %f]" % (self.a, self.b, self.c, self.d, self.e, self.f)
def multiply(self, mat):
a = mat.a * self.a + mat.c * self.b
b = mat.b * self.a + mat.d * self.b
c = mat.a * self.c + mat.c * self.d
d = mat.b * self.c + mat.d * self.d
e = mat.a * self.e + mat.c * self.f + mat.e
f = mat.b * self.e + mat.d * self.f + mat.f
self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
def transform(self, u, v):
return u * self.a + v * self.c + self.e, u * self.b + v * self.d + self.f
def translate(self, dx, dy):
self.multiply(Matrix(1, 0, 0, 1, dx, dy))
def scale(self, sx, sy):
self.multiply(Matrix(sx, 0, 0, sy, 0, 0))
def rotate(self, a):
a *= math.pi / 180
self.multiply(Matrix(math.cos(a), math.sin(a), -math.sin(a), math.cos(a), 0, 0))
def skewX(self, a):
a *= math.pi / 180
self.multiply(Matrix(1, 0, math.tan(a), 1, 0, 0))
def skewY(self, a):
a *= math.pi / 180
self.multiply(Matrix(1, math.tan(a), 0, 1, 0, 0))
def parse_transform(s):
matrix = Matrix()
while True:
match = istrans.match(s)
if not match:
break
cmd = match.group(1)
values = commawsp.split(match.group(2).strip())
s = s[len(match.group(0)):]
arg = []
for value in values:
match = isnumber.match(value)
if not match:
raise Abort("bad transform value")
arg.append(float(match.group(0)))
num = len(arg)
if cmd == "skewX":
if num == 1:
matrix.skewX(arg[0])
continue
elif cmd == "skewY":
if num == 1:
matrix.skewY(arg[0])
continue
elif cmd == "scale":
if num == 1:
matrix.scale(arg[0], arg[0])
continue
if num == 2:
matrix.scale(arg[0], arg[1])
continue
elif cmd == "translate":
if num == 1:
matrix.translate(arg[0], 0)
continue
if num == 2:
matrix.translate(arg[0], arg[1])
continue
elif cmd == "rotate":
if num == 1:
matrix.rotate(arg[0])
continue
if num == 3:
matrix.translate(-arg[1], -arg[2])
matrix.rotate(arg[0])
matrix.translate(arg[1], arg[2])
continue
elif cmd == "matrix":
if num == 6:
matrix.multiply(Matrix(*arg))
continue
else:
print "ERROR: unknown transform", cmd
continue
print "ERROR: '%s' with wrong argument number (%d)" % (cmd, num)
if len(s):
print "ERROR: transform with trailing garbage (%s)" % s
return matrix
class import_svg(xml.sax.handler.ContentHandler):
# err_handler
def error(self, exception):
raise Abort(str(exception))
def fatalError(self, exception):
raise Abort(str(exception))
def warning(self, exception):
print "WARNING: " + str(exception)
# doc_handler
def setDocumentLocator(self, whatever):
pass
def startDocument(self):
self.verified = False
self.scandesc = False
self.matrices = [None]
self.meshes = {}
for o in Blender.Scene.GetCurrent().objects:
if o.type != "Mesh":
continue
mesh = o.getData(mesh = 1)
if not mesh.faceUV:
continue
if mesh.name in self.meshes:
continue
self.meshes[mesh.name] = mesh
def endDocument(self):
pass
def characters(self, data):
if not self.scandesc:
return
if data.startswith("uv_export_svg.py"):
self.verified = True
def ignorableWhitespace(self, data, start, length):
pass
def processingInstruction(self, target, data):
pass
def startElement(self, name, attrs):
currmat = self.matrices[-1]
if "transform" in attrs:
m = parse_transform(attrs["transform"])
if currmat is not None:
m.multiply(currmat)
self.matrices.append(m)
else:
self.matrices.append(currmat)
if name == "polygon":
self.handlePolygon(attrs)
elif name == "svg":
if "viewBox" in attrs:
x, y, w, h = commawsp.split(attrs["viewBox"], 4)
if int(x) or int(y):
raise Abort("bad viewBox")
self.width = int(w)
self.height = int(h)
if self.width != self.height:
raise Abort("viewBox isn't a square")
else:
raise Abort("no viewBox")
elif name == "desc" and not self.verified:
self.scandesc = True
def endElement(self, name):
self.scandesc = False
self.matrices.pop()
def handlePolygon(self, attrs):
if not self.verified:
raise Abort("this file wasn't written by uv_export_svg.py")
ident = attrs.get("id", None)
points = attrs.get("points", None)
if not ident or not points:
print('bad polygon "%s"' % ident)
return
try:
meshname, num = ident.strip().split(ID_SEPARATOR, 2)
except:
print('broken id "%s"' % ident)
return
if not meshname in self.meshes:
print('unknown mesh "%s"' % meshname)
return
#print 'mesh %s face %d: ' % (meshname, num)
matrix = self.matrices[-1]
transuv = []
for p in numwsp.split(points.strip()):
u, v = commawsp.split(p.strip(), 2)
u = float(u)
v = float(v)
if matrix:
u, v = matrix.transform(u, v)
transuv.append((u / self.width, 1 - v / self.height))
for i, uv in enumerate(self.meshes[meshname].faces[int(num)].uv):
uv[0] = transuv[i][0]
uv[1] = transuv[i][1]
def run_parser(path):
if BPyMessages.Error_NoFile(path):
return
editmode = Blender.Window.EditMode()
if editmode:
Blender.Window.EditMode(0)
Blender.Window.WaitCursor(1)
try:
xml.sax.parse(path, import_svg(), import_svg())
Blender.Registry.SetKey("UVImportExportSVG", { "path" : path }, False)
except Abort, e:
print "Error:", e.msg, " -> aborting ...\n"
Blender.Draw.PupMenu("Error%t|" + e.msg)
Blender.Window.RedrawAll()
Blender.Window.WaitCursor(0)
if editmode:
Blender.Window.EditMode(1)
registry = Blender.Registry.GetKey("UVImportExportSVG", False)
if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
path = registry["path"]
else:
path = ""
Blender.Window.FileSelector(run_parser, "Import SVG", path)

172
utils/Modeller/uv_pack.py Normal file
View File

@@ -0,0 +1,172 @@
#!BPY
# """
# Name: 'Pack selected objects on a square'
# Blender: 245
# Group: 'UV'
# Tooltip: 'Pack UV maps of all selected objects onto an empty square texture'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = "http://members.aon.at/mfranz/flightgear/"
__version__ = "0.1"
__bpydoc__ = """\
Script for mapping multiple objects onto one square texture.
Usage:
(1) create new square texture in the UV editor
(2) map all objects individually, choosing the most appropriate technique
(3) scale each of the mappings to the appropriate size, relative to the
other object mappings
(4) select all objects and switch to edit mode (consider to use
Select->Linked->Material or similar methods)
(5) start this script with UVs->Scripts->Pack objects on a square
[now the texture image will first be erased, then colored rectangles
will appear for each object]
(6) rescale and/or remap objects that you aren't happy with (the
relative size of a mapping will be kept)
[continue with (5) until you like the result]
(7) export UV layout to SVG (UVs->Scripts->Save UV Face Layout)
"""
#--------------------------------------------------------------------------------
# Copyright (C) 2008 Melchior FRANZ < mfranz # aon : at >
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#--------------------------------------------------------------------------------
MARGIN = 10 # px
GAP = 10 # px
import Blender
from random import randint as rand
class Abort(Exception):
def __init__(self, msg):
self.msg = msg
def pack():
image = Blender.Image.GetCurrent()
if not image:
raise Abort('No texture image selected')
imgwidth, imgheight = image.getSize()
if imgwidth != imgheight:
Blender.Draw.PupMenu("Warning%t|Image isn't a square!")
gap = (float(GAP) / imgwidth, float(GAP) / imgheight)
margin = (float(MARGIN) / imgwidth - gap[0] * 0.5, float(MARGIN) / imgheight - gap[1] * 0.5)
def drawrect(x0, y0, x1, y1, color = (255, 255, 255, 255)):
x0 *= imgwidth
x1 *= imgwidth
y0 *= imgheight
y1 *= imgheight
for u in range(int(x0 + 0.5), int(x1 - 0.5)):
for v in range(int(y0 + 0.5), int(y1 - 0.5)):
image.setPixelI(u, v, color)
boxes = []
unique_meshes = {}
BIG = 1<<30
Blender.Window.DrawProgressBar(0.0, "packing")
for o in Blender.Scene.GetCurrent().objects.selected:
if o.type != "Mesh":
continue
mesh = o.getData(mesh = 1)
if not mesh.faceUV:
continue
if mesh.name in unique_meshes:
#print "dropping duplicate mesh", mesh.name, "of object", o.name
continue
unique_meshes[mesh.name] = True
print "\tobject '%s'" % o.name
xmin = ymin = BIG
xmax = ymax = -BIG
for f in mesh.faces:
for p in f.uv:
xmin = min(xmin, p[0])
xmax = max(xmax, p[0])
ymin = min(ymin, p[1])
ymax = max(ymax, p[1])
width = xmax - xmin
height = ymax - ymin
boxes.append([0, 0, width + gap[0], height + gap[1], xmin, ymin, mesh])
if not boxes:
raise Abort('No mesh objects selected')
boxwidth, boxheight = Blender.Geometry.BoxPack2D(boxes)
boxmax = max(boxwidth, boxheight)
xscale = (1.0 - 2.0 * margin[0]) / boxmax
yscale = (1.0 - 2.0 * margin[1]) / boxmax
image.reload()
#drawrect(0, 0, 1, 1) # erase texture
for i, box in enumerate(boxes):
Blender.Window.DrawProgressBar(float(i) * len(boxes), "Drawing")
xmin = ymin = BIG
xmax = ymax = -BIG
for f in box[6].faces:
for p in f.uv:
p[0] = (p[0] - box[4] + box[0] + gap[0] * 0.5 + margin[0]) * xscale
p[1] = (p[1] - box[5] + box[1] + gap[1] * 0.5 + margin[1]) * yscale
xmin = min(xmin, p[0])
xmax = max(xmax, p[0])
ymin = min(ymin, p[1])
ymax = max(ymax, p[1])
drawrect(xmin, ymin, xmax, ymax, (rand(128, 255), rand(128, 255), rand(128, 255), 255))
box[6].update()
Blender.Window.RedrawAll()
Blender.Window.DrawProgressBar(1.0, "Finished")
editmode = Blender.Window.EditMode()
if editmode:
Blender.Window.EditMode(0)
Blender.Window.WaitCursor(1)
try:
print "box packing ..."
pack()
print "done\n"
except Abort, e:
print "Error:", e.msg, " -> aborting ...\n"
Blender.Draw.PupMenu("Error%t|" + e.msg)
Blender.Window.WaitCursor(0)
if editmode:
Blender.Window.EditMode(1)

View File

@@ -0,0 +1,827 @@
#!BPY
# """
# Name: 'YASim (.xml)'
# Blender: 245
# Group: 'Import'
# Tooltip: 'Loads and visualizes a YASim FDM geometry'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/yasim_import.py"]
__version__ = "0.2"
__bpydoc__ = """\
yasim_import.py loads and visualizes a YASim FDM geometry
=========================================================
It is recommended to load the model superimposed over a greyed out and immutable copy of the aircraft model:
(0) put this script into ~/.blender/scripts/
(1) load or import aircraft model (menu -> "File" -> "Import" -> "AC3D (.ac) ...")
(2) create new *empty* scene (menu -> arrow button left of "SCE:scene1" combobox -> "ADD NEW" -> "empty")
(3) rename scene to yasim (not required)
(4) link to scene1 (F10 -> "Output" tab in "Buttons Window" -> arrow button left of text entry "No Set Scene" -> "scene1")
(5) now load the YASim config file (menu -> "File" -> "Import" -> "YASim (.xml) ...")
This is good enough for simple checks. But if you are working on the YASim configuration, then you need a
quick and convenient way to reload the file. In that case continue after (4):
(5) switch the button area at the bottom of the blender screen to "Scripts Window" mode (green python snake icon)
(6) load the YASim config file (menu -> "Scripts" -> "Import" -> "YASim (.xml) ...")
(7) make the "Scripts Window" area as small as possible by dragging the area separator down
(8) optionally split the "3D View" area and switch the right part to the "Outliner"
(9) press the "Reload YASim" button in the script area to reload the file
If the 3D model is displaced with respect to the FDM model, then the <offsets> values from the
model animation XML file should be added as comment to the YASim config file, as a line all by
itself, with no spaces surrounding the equal signs. Spaces elsewhere are allowed. For example:
<offsets>
<x-m>3.45</x-m>
<z-m>-0.4</z-m>
<pitch-deg>5</pitch-deg>
</offsets>
becomes:
<!-- offsets: x=3.45 z=-0.4 p=5 -->
Possible variables are:
x ... <x-m>
y ... <y-m>
z ... <z-m>
h ... <heading-deg>
p ... <pitch-deg>
r ... <roll-deg>
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_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.
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 control 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, unless
the "Mirror" button is active
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, normal and forward vector,
one blade at phi0 with direction arrow near blade tip
gear -> contact point and compression vector (no arrow head)
tank -> magenta cube (10 cm side length)
weight -> inverted cyan cone
ballast -> yellow cylinder
hitch -> hexagon (10 cm diameter)
hook -> dashed line for up angle, T-line for down angle
launchbar -> dashed line for up angles, T-line for down angles
(launchbar and holdback each)
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.
Environment variable BLENDER_YASIM_IMPORT can be set to a space-separated list of options:
$ BLENDER_YASIM_IMPORT="mirror verbose" blender
whereby:
verbose ... enables verbose logs
mirror ... enables mirroring of symmetric surfaces
"""
#--------------------------------------------------------------------------------
# Copyright (C) 2009 Melchior FRANZ < mfranz # aon : at >
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#--------------------------------------------------------------------------------
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)
Y = Vector(0, 1, 0)
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
data = None
cursor = ORIGIN
last_cursor = Vector(Blender.Window.GetCursorPos())
mirror_button = Blender.Draw.Create("mirror" in CONFIG)
class Abort(Exception):
def __init__(self, msg, term = None):
self.msg = msg
self.term = term
def log(msg):
if Global.verbose:
print(msg)
def draw_dashed_line(mesh, start, end):
w = 0.04
step = w * (end - start).normalize()
n = len(mesh.verts)
for i in range(int(1 + 0.5 * (end - start).length / w)):
a = start + 2 * i * step
b = a + step
if (b - end).length < step.length:
b = end
mesh.verts.extend([a, b])
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)
v = v.length * X
n = len(mesh.verts)
mesh.verts.extend([ORIGIN * m , v * m, (v - 0.05 * X + 0.05 * Y) * m, (v - 0.05 * X - 0.05 * Y) * m]) # head
mesh.verts.extend([(ORIGIN + 0.05 * Y) * m, (ORIGIN - 0.05 * Y) * m]) # base
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):
angle = 2.0 * math.pi * i / numpoints
v = Vector(radius * math.cos(angle), radius * math.sin(angle), 0)
mesh.verts.extend([v * matrix])
for i in range(numpoints):
i1 = (i + 1) % numpoints
mesh.edges.extend([[n + i, n + i1]])
class Item:
scene = Blender.Scene.GetCurrent()
def make_twosided(self, mesh):
mesh.faceUV = True
for f in mesh.faces:
f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
def set_color(self, obj, color):
mat = Blender.Material.New()
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):
mesh = Blender.Mesh.Primitives.Monkey()
mesh.transform(ScaleMatrix(0.13, 4) * Euler(90, 0, 90).toMatrix().resize4x4() * TranslationMatrix(Vector(-0.1, 0, -0.032)))
obj = self.scene.objects.new(mesh, "YASim_cockpit")
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
class Tank(Item):
def __init__(self, name, center):
mesh = Blender.Mesh.Primitives.Cube()
mesh.transform(ScaleMatrix(0.05, 4))
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
self.set_color(obj, [1, 0, 1, 0.5])
class Ballast(Item):
def __init__(self, name, center):
mesh = Blender.Mesh.Primitives.Cylinder()
mesh.transform(ScaleMatrix(0.05, 4))
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
self.set_color(obj, [1, 1, 0, 0.5])
class Weight(Item):
def __init__(self, name, center):
mesh = Blender.Mesh.Primitives.Cone()
mesh.transform(ScaleMatrix(0.05, 4))
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
self.set_color(obj, [0, 1, 1, 0.5])
class Gear(Item):
def __init__(self, name, center, compression):
mesh = Blender.Mesh.New()
mesh.verts.extend([ORIGIN, compression])
mesh.edges.extend([0, 1])
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
class Hook(Item):
def __init__(self, name, center, length, up_angle, dn_angle):
mesh = Blender.Mesh.New()
up = ORIGIN - length * math.cos(up_angle * DEG2RAD) * X - length * math.sin(up_angle * DEG2RAD) * Z
dn = ORIGIN - length * math.cos(dn_angle * DEG2RAD) * X - length * math.sin(dn_angle * DEG2RAD) * Z
mesh.verts.extend([ORIGIN, dn, dn + 0.05 * Y, dn - 0.05 * Y])
mesh.edges.extend([[0, 1], [2, 3]])
draw_dashed_line(mesh, ORIGIN, up)
draw_dashed_line(mesh, ORIGIN, dn)
obj = self.scene.objects.new(mesh, name)
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()
hb = hb - lb
lb_tip = ORIGIN + lb_length * math.cos(dn_angle * DEG2RAD) * X - lb_length * math.sin(dn_angle * DEG2RAD) * Z
hb_tip = hb - hb_length * math.cos(dn_angle * DEG2RAD) * X - hb_length * math.sin(dn_angle * DEG2RAD) * Z
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])
mesh.edges.extend([[0, 1], [1, 2], [2, 3], [4, 5], [6, 7]])
draw_dashed_line(mesh, ORIGIN, lb_length * math.cos(up_angle * DEG2RAD) * X - lb_length * math.sin(up_angle * DEG2RAD) * Z)
draw_dashed_line(mesh, hb, hb - hb_length * math.cos(up_angle * DEG2RAD) * X - hb_length * math.sin(up_angle * DEG2RAD) * Z)
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(lb) * Global.matrix)
class Hitch(Item):
def __init__(self, name, center):
mesh = Blender.Mesh.Primitives.Circle(6, 0.1)
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix)
class Thrust:
def set_actionpt(self, p):
self.actionpt = p
def set_dir(self, d):
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)
def __del__(self):
a = self.actionpt - self.center
mesh = Blender.Mesh.New()
draw_dashed_line(mesh, ORIGIN, a)
draw_arrow(mesh, a, a + self.thrustvector.normalize())
obj = self.scene.objects.new(mesh, self.name)
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)
def __del__(self):
a = self.actionpt - self.center
matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
mesh = Blender.Mesh.New()
mesh.verts.extend([ORIGIN * matrix, (ORIGIN + self.radius * X) * matrix])
mesh.edges.extend([[0, 1]])
draw_dashed_line(mesh, ORIGIN, a)
draw_arrow(mesh, a, a + self.thrustvector.normalize())
draw_circle(mesh, 128, self.radius, matrix)
obj = self.scene.objects.new(mesh, self.name)
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)
self.thrustvector = -X * RotationMatrix(rotate, 4, "y")
def __del__(self):
a = self.actionpt - self.center
mesh = Blender.Mesh.New()
draw_dashed_line(mesh, ORIGIN, a)
draw_arrow(mesh, a, a + self.thrustvector.normalize())
obj = self.scene.objects.new(mesh, self.name)
obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
class Fuselage(Item):
def __init__(self, name, a, b, width, taper, midpoint):
numvert = 12
angle = []
for i in range(numvert):
alpha = i * 2 * math.pi / float(numvert)
angle.append([math.cos(alpha), math.sin(alpha)])
axis = b - a
length = axis.length
mesh = Blender.Mesh.New()
for i in range(numvert):
mesh.verts.extend([[0, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
for i in range(numvert):
mesh.verts.extend([[midpoint * length, 0.5 * width * angle[i][0], 0.5 * width * angle[i][1]]])
for i in range(numvert):
mesh.verts.extend([[length, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
for i in range(numvert):
i1 = (i + 1) % numvert
mesh.faces.extend([[i, i1, i1 + numvert, i + numvert]])
mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]])
mesh.verts.extend([ORIGIN, length * X])
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix)
self.set_color(obj, [0, 0, 0.5, 0.4])
class Rotor(Item):
def __init__(self, name, center, up, fwd, numblades, radius, chord, twist, taper, rel_len_blade_start, phi0, ccw):
matrix = RotationMatrix(phi0, 4, "z") * up.toTrackQuat('z', 'x').toMatrix().resize4x4()
invert = matrix.copy().invert()
direction = [-1, 1][ccw]
twist *= DEG2RAD
a = ORIGIN + rel_len_blade_start * radius * X
b = ORIGIN + radius * X
tw = 0.5 * chord * taper * math.cos(twist) * Y + 0.5 * direction * chord * taper * math.sin(twist) * Z
mesh = Blender.Mesh.New()
mesh.verts.extend([ORIGIN, a, b, a + 0.5 * chord * Y, a - 0.5 * chord * Y, b + tw, b - tw])
mesh.edges.extend([[0, 1], [1, 2], [1, 3], [1, 4], [3, 5], [4, 6], [5, 6]])
draw_circle(mesh, 64, rel_len_blade_start * radius, Matrix())
draw_circle(mesh, 128, radius, Matrix())
draw_arrow(mesh, ORIGIN, up * invert)
draw_arrow(mesh, ORIGIN, fwd * invert)
b += 0.1 * X + direction * chord * Y
draw_arrow(mesh, b, b + min(0.5 * radius, 1) * direction * Y)
obj = self.scene.objects.new(mesh, name)
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
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
tipfore = tip + 0.5 * taper * chord * math.cos(twist * DEG2RAD) * X + 0.5 * taper * chord * math.sin(twist * DEG2RAD) * Z
tipaft = tip + tip - tipfore
mesh.verts.extend([tip, tipfore, tipaft])
mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]])
self.make_twosided(mesh)
obj = self.scene.objects.new(mesh, name)
mesh.transform(Euler(dihedral, -incidence, 0).toMatrix().resize4x4())
self.set_color(obj, [[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_button.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)
c = 0.2 * (Vector(self.mesh.verts[0].co - a)).normalize()
m = self.obj.getMatrix()
mesh = Blender.Mesh.New()
i0 = a + start * (b - a)
i1 = a + end * (b - a)
mesh.verts.extend([i0, i1, i0 + c, i1 + c])
mesh.faces.extend([[0, 1, 3, 2]])
self.make_twosided(mesh)
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(m)
self.set_color(obj, [0.8, 0.8, 0, 0.9])
if self.is_symmetric and Global.mirror_button.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.ErrorHandler, handler.ContentHandler):
ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \
"control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \
"rotorgear", "tow", "winch", "solve-weight"]
# err_handler
def warning(self, exception):
print((self.error_string("Warning", exception)))
def error(self, exception):
print((self.error_string("Error", exception)))
def fatalError(self, exception):
raise Abort(str(exception), self.error_string("Fatal", exception))
def error_string(self, tag, e):
(column, line) = (e.getColumnNumber(), e.getLineNumber())
return "%s: %s\n%s%s^" % (tag, str(e), Global.data[line - 1], column * ' ')
# doc_handler
def setDocumentLocator(self, locator):
self.locator = locator
def startDocument(self):
self.tags = []
self.counter = {}
self.items = [None]
def endDocument(self):
for o in Item.scene.objects:
o.sel = True
def startElement(self, tag, attrs):
if len(self.tags) == 0 and tag != "airplane":
raise Abort("this isn't a YASim config file (bad root tag at line %d)" % self.locator.getLineNumber())
self.tags.append(tag)
path = string.join(self.tags, '/')
item = Item()
parent = self.items[-1]
if self.counter.has_key(tag):
self.counter[tag] += 1
else:
self.counter[tag] = 0
if tag == "cockpit":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[31mcockpit x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
item = Cockpit(c)
elif tag == "fuselage":
a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"]))
b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"]))
width = float(attrs["width"])
taper = float(attrs.get("taper", 1))
midpoint = float(attrs.get("midpoint", 0.5))
log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \
(a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint))
item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint)
elif tag == "gear":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
compression = float(attrs.get("compression", 1))
up = Z * compression
if attrs.has_key("upx"):
up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression
log("\033[35;1mgear x=%f y=%f z=%f compression=%f upx=%f upy=%f upz=%f\033[m" \
% (c[0], c[1], c[2], compression, up[0], up[1], up[2]))
item = Gear("YASim_gear#%d" % self.counter[tag], c, up)
elif tag == "jet":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
rotate = float(attrs.get("rotate", 0))
log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate))
item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate)
elif tag == "propeller":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
radius = float(attrs["radius"])
log("\033[36;1m%s x=%f y=%f z=%f radius=%f\033[m" % (tag, c[0], c[1], c[2], radius))
item = Propeller("YASim_propeller#%d" % self.counter[tag], c, radius)
elif tag == "thruster":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
v = Vector(float(attrs["vx"]), float(attrs["vy"]), float(attrs["vz"]))
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]))
item = Thruster("YASim_thruster#%d" % self.counter[tag], c, v)
elif tag == "actionpt":
if not isinstance(parent, Thrust):
raise Abort("%s is not part of a thruster/propeller/jet at line %d" \
% (path, self.locator.getLineNumber()))
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
parent.set_actionpt(c)
elif tag == "dir":
if not isinstance(parent, Thrust):
raise Abort("%s is not part of a thruster/propeller/jet at line %d" \
% (path, self.locator.getLineNumber()))
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
parent.set_dir(c)
elif tag == "tank":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[34;1m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
item = Tank("YASim_tank#%d" % self.counter[tag], c)
elif tag == "ballast":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
item = Ballast("YASim_ballast#%d" % self.counter[tag], c)
elif tag == "weight":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
item = Weight("YASim_weight#%d" % self.counter[tag], c)
elif tag == "hook":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
length = float(attrs.get("length", 1))
up_angle = float(attrs.get("up-angle", 0))
down_angle = float(attrs.get("down-angle", 70))
log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \
% (tag, c[0], c[1], c[2], length, up_angle, down_angle))
item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle)
elif tag == "hitch":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[35m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
item = Hitch("YASim_hitch#%d" % self.counter[tag], c)
elif tag == "launchbar":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
length = float(attrs.get("length", 1))
up_angle = float(attrs.get("up-angle", -45))
down_angle = float(attrs.get("down-angle", 45))
holdback = Vector(float(attrs.get("holdback-x", c[0])), float(attrs.get("holdback-y", c[1])), float(attrs.get("holdback-z", c[2])))
holdback_length = float(attrs.get("holdback-length", 2))
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" \
% (tag, c[0], c[1], c[2], length, down_angle, up_angle, \
holdback[0], holdback[1], holdback[2], holdback_length))
item = Launchbar("YASim_launchbar#%d" % self.counter[tag], c, length, holdback, holdback_length, up_angle, down_angle)
elif tag == "wing" or tag == "hstab" or tag == "vstab" or tag == "mstab":
root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
length = float(attrs["length"])
chord = float(attrs["chord"])
incidence = float(attrs.get("incidence", 0))
twist = float(attrs.get("twist", 0))
taper = float(attrs.get("taper", 1))
sweep = float(attrs.get("sweep", 0))
dihedral = float(attrs.get("dihedral", [0, 90][tag == "vstab"]))
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" \
% (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral))
item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral)
elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler":
if not isinstance(parent, Wing):
raise Abort("%s is not part of a wing or stab at line %d" \
% (path, self.locator.getLineNumber()))
start = float(attrs["start"])
end = float(attrs["end"])
log("\t\033[33m%s start=%f end=%f\033[m" % (tag, start, end))
parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end)
elif tag == "rotor":
c = Vector(float(attrs.get("x", 0)), float(attrs.get("y", 0)), float(attrs.get("z", 0)))
norm = Vector(float(attrs.get("nx", 0)), float(attrs.get("ny", 0)), float(attrs.get("nz", 1)))
fwd = Vector(float(attrs.get("fx", 1)), float(attrs.get("fy", 0)), float(attrs.get("fz", 0)))
diameter = float(attrs.get("diameter", 10.2))
numblades = int(attrs.get("numblades", 4))
chord = float(attrs.get("chord", 0.3))
twist = float(attrs.get("twist", 0))
taper = float(attrs.get("taper", 1))
rel_len_blade_start = float(attrs.get("rel-len-blade-start", 0))
phi0 = float(attrs.get("phi0", 0))
ccw = not not int(attrs.get("ccw", 0))
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 " \
+ "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \
% (c[0], c[1], c[2], norm[0], norm[1], norm[2], fwd[0], fwd[1], fwd[2], numblades, \
diameter, chord, twist, taper, rel_len_blade_start, phi0, ccw))
item = Rotor("YASim_rotor#%d" % self.counter[tag], c, norm, fwd, numblades, 0.5 * diameter, chord, \
twist, taper, rel_len_blade_start, phi0, ccw)
elif tag not in self.ignored:
log("\033[30;1m%s\033[m" % path)
self.items.append(item)
def endElement(self, tag):
self.tags.pop()
self.items.pop()
def extract_matrix(filedata, tag):
v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 }
has_offsets = False
for line in filedata:
line = string.strip(line)
if not line.startswith("<!--") or not line.endswith("-->"):
continue
line = string.strip(line[4:-3])
if not string.lower(line).startswith("%s:" % tag):
continue
line = string.strip(line[len(tag) + 1:])
for assignment in string.split(line):
(key, value) = string.split(assignment, '=', 2)
v[string.strip(key)] = float(string.strip(value))
has_offsets = True
if not has_offsets:
return None
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'])))
return Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4() * TranslationMatrix(Vector(v['x'], v['y'], v['z']))
def load_yasim_config(path):
if BPyMessages.Error_NoFile(path):
return
Blender.Window.WaitCursor(1)
Blender.Window.EditMode(0)
print(("loading '%s'" % path))
try:
for o in Item.scene.objects:
if o.name.startswith("YASim_"):
Item.scene.objects.unlink(o)
try:
f = open(path)
Global.data = f.readlines()
finally:
f.close()
Global.path = path
Global.matrix = YASIM_MATRIX
matrix = extract_matrix(Global.data, "offsets")
if matrix:
Global.matrix *= matrix.invert()
Global.yasim.parse(path)
Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
Global.data = None
except Abort, e:
print(("%s\nAborting ..." % (e.term or e.msg)))
Blender.Draw.PupMenu("Error%t|" + e.msg)
Blender.Window.RedrawAll()
Blender.Window.WaitCursor(0)
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)
BGL.glColor3f(1, 1, 1)
BGL.glRasterPos2f(5, 55)
Draw.Text("FlightGear YASim Import: '%s'" % Global.path)
Draw.PushButton("Reload", RELOAD_BUTTON, 5, 5, 80, 32, "reload YASim config file")
Global.mirror_button = Draw.Toggle("Mirror", MIRROR_BUTTON, 100, 5, 50, 16, Global.mirror_button.val, \
"show symmetric surfaces on both sides (reloads config)")
Draw.PushButton("Update Cursor", CURSOR_BUTTON, width - 650, 5, 100, 32, "update cursor display (in YASim coordinate system)")
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("Vector from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f m" % (c[0], c[1], c[2], c.length))
def gui_event(ev, value):
if ev == Blender.Draw.ESCKEY:
Blender.Draw.Exit()
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 = ""
xml_handler = import_yasim()
Global.yasim = make_parser()
Global.yasim.setContentHandler(xml_handler)
Global.yasim.setErrorHandler(xml_handler)
if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
Blender.Draw.Register(gui_draw, gui_event, gui_button)
Blender.Window.FileSelector(load_yasim_config, "Import YASim Configuration File", path)
main()