#!/usr/bin/perl -w
# NAME: ac3dfile.pl
# AIM: Read an parse an AC3D file
# 11/05/2013 - some UI improvements
# 05/05/2013 geoff mclane http://geoffair.net/mperl
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Cwd;
my $os = $^O;
my $perl_dir = '/home/geoff/bin';
my $PATH_SEP = '/';
my $temp_dir = '/tmp';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $temp_dir = $perl_dir;
    $PATH_SEP = "\\";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n";
require 'lib_vec3.pm' or die "Unable to load 'lib_vec3.pm' Check paths in \@INC...\n";
# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt";
open_log($outfile);

# user variables
my $VERS = "0.0.2 2013-05-11";
#my $VERS = "0.0.1 2013-03-17";
my $load_log = 0;
my $in_file = '';
my $verbosity = 0;
my $out_file = '';

# ### DEBUG ###
my $debug_on = 1;
my $def_file = 'C:\FG\18\blendac3d\f16.ac';

#8: ac_to_gl: Unrecognised token '<<<<<<< HEAD:Models/Weather/cirrus_alt1.ac'
#my $def_file = 'C:\FG\fgdata\Models\Weather\cirrus_alt1.ac';
#ac_to_gl: Illegal vertex record.
#my $def_file = 'C:\FG\fgdata\Aircraft\victor\Models\refuel-hose.ac';
#ac_to_gl: Mismatched double-quote ('"') in ' <greater-than>'
#3222: ac_to_gl: Unrecognised token '<property>/systems/electrical/battery-bus/u-volts</property>'
#name " <greater-than>
#        <property>/systems/electrical/battery-bus/u-volts</property>
#        <value type=\"double\">10.0</value>
#      </greater-than>
#Case"
#my $def_file = 'C:\FG\fgdata\Aircraft\SenecaII\Models\AI\AI.ac';
#615: ac_to_gl: Unrecognised token 'SURF 0x10'
#my $def_file = 'C:\FG\fgdata\Aircraft\PC-6\Models\porter.ac';
#83579: ac_to_gl: Unrecognised token 'texure "brushed1.png"'
#my $def_file = 'C:\FG\fgdata\Aircraft\harrier\Models\harrier.ac';
#ac_to_gl: Illegal ref record.
#my $def_file = 'C:\FG\fgdata\Aircraft\Dromader\Models\DropSystem.ac';
#14398: ac_to_gl: Unrecognised token 'light'
#my $def_file = 'C:\FG\fgdata\Aircraft\dhc8\Models\dhc8.ac';
#17645: ac_to_gl: Unrecognised token '00522410077974'
#my $def_file = 'C:\FG\fgdata\Aircraft\dc8-63\Models\Instruments\Yokes\yokes.ac';
#1271: ac_to_gl: Unrecognised token 'texoff -0.5 0.5'
#my $def_file = 'C:\FG\fgdata\Aircraft\a4\Models\attitude-mod1.ac';
# 1300: ac_to_gl: Unrecognised token 'subdiv 1'
#my $def_file = 'C:\FG\fgdata\Aircraft\A380\Models\FlightDeck\Glareshield\efis_c.ac';
#my $def_file = 'C:\FG\fgdata\AI\Aircraft\Dromader\Models\M18B_Dromader.ac';
#my $def_file = 'C:\FG\fgdata\AI\Aircraft\c310u3a\Models\c310u3a.ac'; # texoff record ???
#my $def_file = 'C:\FG\fgdata\AI\Aircraft\beech99\Models\beech99.ac';
#my $def_file = 'C:\FG\fgdata\Aircraft\c172p\Models\c172p.ac';

my $show_objs = 1;
my $show_names = 1;


### program variables
my @warnings = ();
my $cwd = cwd();
my %ac_objects = ();

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        prt( "\nNo warnings issued.\n\n" ) if (VERB9());
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    close_log($outfile,$load_log);
    exit($val);
}


sub prtw($) {
   my ($tx) = shift;
   $tx =~ s/\n$//;
   prt("$tx\n");
   push(@warnings,$tx);
}

sub vec3 { vec3->new(@_) }

my $max_mat_len = 0;
my @mat_index = ();
my %materials = ();
my %mats_used = ();
my %mat_fields = (
    'rgb' => 1,
    'amb' => 5,
    'emis' => 9,
    'spec' => 13,
    'shi' => 17,
    'trans' => 19
    );

my @mat_order = qw( rgb amb emis spec shi trans );

sub show_materials() {
    my @arr = keys(%materials);
    my $cnt = scalar @arr;
    prt("Collected $cnt materials...\n");
    my ($rh,$key,$ra,$k,$msg,$va);
    foreach $key (@mat_index) {
    ###foreach $key (@arr) {
        $msg = $key;
        $msg .= ' ' while (length($msg) < $max_mat_len);
        $rh = $materials{$key};
        foreach $k (@mat_order) {
        ##### foreach $k (keys %{$rh}) {
            $va = ${$rh}{$k};
            if (($k eq 'shi')||($k eq 'trans')) {
                $msg .= " $k ".${$va}[0];
            } else {
                $msg .= " $k ".${$va}[0]." ".${$va}[1]." ".${$va}[2];
            }
        }
        if (defined $mats_used{$key}) {
            $msg .= " Used ".$mats_used{$key};
        } else {
            $msg .= " NOT USED";
        }

        prt("$msg\n");

    }
}

my $curr_line = 0;

#          0  1   2  3  4   5   6  7  8   9    10 11 12  13   14 15 16  17  18  19    20
# MATERIAL %s rgb %f %f %f  amb %f %f %f  emis %f %f %f  spec %f %f %f  shi %d  trans %f
sub compare_material_hash($$$) {
    my ($mat,$rh1,$rh2) = @_;
    my @k1 = sort keys(%{$rh1});
    my @k2 = sort keys(%{$rh2});
    my $c1 = scalar @k1;
    my $c2 = scalar @k2;
    return 1 if ($c1 != $c2);
    my ($i,$key,$v1,$v2,$msg);
    for ($i = 0; $i < $c1; $i++) {
        return 1 if ($k1[$i] ne $k2[$i]);
    }
    $msg = '';
    foreach $key (@k1) {
        $v1 = ${$rh1}{$key};
        $v2 = ${$rh2}{$key};
        if (${$v1}[0] != ${$v2}[0]) {
            $msg .= "item $key NOT same! ".${$v1}[0]." vs ".${$v2}[0]." ";
            #prt("Material $mat, item $key NOT same! ".${$v1}[0]." vs ".${$v2}[0]."\n");
            #return 1;
        }
    }
    if (length($msg)) {
        prt("Material $mat: $msg\n");
        return 1;
    }
    return 0;
}

sub get_material($) {
    my $line = shift;
    my @arr = space_split($line);
    my ($cnt,$key,$val,$rh);
    $cnt = scalar @arr;
    my %h = ();
    if ($cnt > 20) {
        foreach $key (keys %mat_fields) {
            $val = $mat_fields{$key};
            if ($arr[$val] eq $key) {
                # ok
                if (($key eq 'shi')||($key eq 'trans')) {
                    $h{$key} = [$arr[$val+1],0,0];
                } else {
                    $h{$key} = [ $arr[$val+1], $arr[$val+2], $arr[$val+3] ];
                }
            } else {
                prtw("WARNING: $curr_line: arr[$val] is ".$arr[$val].", NOT $key?\n");
                return;
            }
        }
        $key = strip_quotes($arr[0]);
        if (defined $materials{$key}) {
            $rh = $materials{$key};
            if (compare_material_hash($key,$rh,\%h)) {
                prtw("WARNING: MATERIAL $key is DUPLICATED with different values... Using 2nd\n");
                $materials{$key} = \%h; # store this 'material'
            }
        } else {
            $materials{$key} = \%h; # store this 'material'
            $val = length($key);
            $max_mat_len = $val if ($val > $max_mat_len);
            push(@mat_index,$key);
        }
    } else {
        prtw("WARNING: $curr_line: BAD MATERIAL $line\n");
    }

}

sub get_mat_name($) {
    my $ind = shift;
    my $cnt = scalar @mat_index;
    my $mn = "INDEX $ind out of range $cnt";
    if ($ind < $cnt) {
        $mn = $mat_index[$ind];
        $mats_used{$mn}++;
    }
    return $mn;
}

sub process_in_file($) {
    my ($inf) = @_;
    if (! open INF, "<$inf") {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    my @lines = <INF>;
    close INF;
    my $lncnt = scalar @lines;
    prt("Processing $lncnt lines, from [$inf]...\n");
    my ($line,$i,$obj,$nam,$verts,$refs,$nserf,$serfn,$serfcnt);
    my ($stype,$shdg,$dec_nbr,$surf_type,$surf_shdg,$imat,$matnam);
    my ($j,@arr,$cnt,$ind,$ra,$len);
    $curr_line = 0;
    $nserf = 0;
    $serfcnt = 0;
    my @vertexes = ();
    my @verts = ();
    for ($i = 0; $i < $lncnt; $i++) {
        $line = $lines[$i];
        chomp $line;
        $line = trim_all($line);
        $curr_line = $i + 1;
        $len = length($line);
        next if ($len == 0); # not really in the spec - a BLANK line, but here skip it
        if ($i == 0) {
            if ($line =~ /^AC3D/) {
                $line = substr($line,4);
                prt("AC3D Version $line\n");
            } else {
                prt("Not an AC3D file...\n");
                return;
            }
        } else {
            if ($line =~ /^MATERIAL\s*/) {
                get_material(trim_all(substr($line,9)));
                #prt("$lnn: $line\n");
            } elsif ($line =~ /^OBJECT\s+/) {
                # OBJECT \%s
                $obj = strip_quotes(trim_all(substr($line,7)));
                prt("$curr_line: OBJECT $obj\n") if (VERB9() || $show_objs);
                @vertexes = ();
                $nam = '';
                $matnam = '';
                @verts = ();
            } elsif ($line =~ /^name\s*/) {
                # *name \%s
                $nam = strip_quotes(trim_all(substr($line,5)));
                prt("name $nam\n") if (VERB9() || $show_names);
                # MAYBE can span several lines, but not per the spec
            } elsif ($line =~ /^data\s+/) {
                # *data \%d
                $i++; # skip next line
            } elsif ($line =~ /^texture\s+/) {
                # *texture \%s
            } elsif ($line =~ /^texrep\s+/) {
                # *texrep \%f \%f
            } elsif ($line =~ /^rot\s+/) {
                # *rot \%f \%f \%f  \%f \%f \%f  \%f \%f \%f
            } elsif ($line =~ /^loc\s+/) {
                # *loc \%f \%f \%f
            } elsif ($line =~ /^url\s+/) {
                # *url \%s
            } elsif ($line =~ /^numvert\s+/) {
                # *numvert \%d
                # lines of \%f \%f \%f
                $verts = trim_all(substr($line,8));
                prt("List of $verts vertexes... obj $obj, name $nam\n") if (VERB9());
                for ($j = 0; $j < $verts; $j++) {
                    $curr_line = $i + $j + 1;
                    $line = $lines[$curr_line];
                    chomp $line;
                    $line = trim_all($line);
                    @arr = split(/\s+/,$line);
                    $cnt = scalar @arr;
                    if ($cnt == 3) {
                        prt("vertex ".$arr[0]." ".$arr[1]." ".$arr[2]."\n") if (VERB5());
                        push(@vertexes, [$arr[0],$arr[1],$arr[2]]);
                        push(@verts, vec3($arr[0],$arr[1],$arr[2]));
                    } else {
                        prtw("WARNING: $curr_line: Did not split to 3 vertexes!\n");
                    }
                }
                $i += $verts;
            } elsif ($line =~ /^numsurf\s+/) {
                if ($nserf) {
                    prtw("WARNING: $curr_line: Remain $nserf SURF beyond $serfcnt count!\n");
                }
                # *numsurf \%d
                $nserf = trim_all(substr($line,8));
                $serfcnt = $nserf;
            } elsif ($line =~ /^SURF\s+/) {
                # *SURF \%d
                if ($nserf) {
                    $nserf--;
                } else {
                    prtw("WARNING: $curr_line: A SURF beyond $serfcnt count!\n");
                }
                $serfn = trim_all(substr($line,5));
                $dec_nbr = hex($serfn);
                $stype = $dec_nbr & 0x0f;
                $shdg  = $dec_nbr >> 4;
                if ($stype == 0) {
                    $surf_type = 'polygon';
                } elsif ($stype == 1) {
                    $surf_type = 'closeline';
                } elsif ($stype == 2) {
                    $surf_type = 'line';
                } else {
                    $surf_type = 'UNKNONW';
                }
                if ($shdg == 0) {
                    $surf_shdg = 'none';
                } elsif ($shdg == 1) {
                    $surf_shdg = 'shaded';
                } elsif ($shdg == 1) {
                    $surf_shdg = 'twosided';
                } else {
                    $surf_shdg = 'UNKNONW';
                }
                prt("SURF $serfn $stype=$surf_type $shdg=$surf_shdg\n") if (VERB9());
            } elsif ($line =~ /^mat\s+/) {
                # *mat \%d
                $imat = trim_all(substr($line,4));
                $matnam = get_mat_name($imat);
                prt("mat $imat $matnam\n") if (VERB9());
            } elsif ($line =~ /^refs\s+/) {
                # refs \%d
                # refs lines of \%d \%f \%f
                # Each line contains an index to the vertex and 
                # the texture coordinates for this surface vertex.
                $refs = trim_all(substr($line,5));
                prt("List of $refs reference for $matnam\n") if (VERB9());
                for ($j = 0; $j < $refs; $j++) {
                    $line = $lines[$i + $j + 1];
                    chomp $line;
                    $line = trim_all($line);
                    @arr = split(/\s+/,$line);
                    $cnt = scalar @arr;
                    if ($cnt == 3) {
                        $ind = $arr[0];
                        $cnt = scalar @vertexes;
                        if ($ind < $cnt) {
                            $ra = $vertexes[$ind];
                            prt("index $ind coord ".$arr[1]." ".$arr[2]." on ".${$ra}[0]." ".${$ra}[1]." ".${$ra}[2]."\n") if (VERB9());
                        } else {
                            prt("index $ind coord ".$arr[1]." ".$arr[2]." OUT OF RANGE\n");
                        }
                    } else {
                        prtw("WARNING: $curr_line: Did not split index + 2 coords!\nline [$line]\n");
                    }
                }
                $i += $refs;
            } elsif ($line =~ /^kids\s+/) {
                # kids \%d
            } elsif ($line =~ /^crease\s+/) {
            #} elsif ($line =~ /^Mesh\.(\d+)$/i) {
            #} elsif ($line =~ /^Plane\.(\d+)$/i) {
            #} elsif ($line =~ /^Plane/i) {
            } else {
                pgm_exit(1,"$curr_line: WHAT IS THIS? [$line] FIX ME!\n");
            }
        }
    }
}

sub show_vertices() {

}

#########################################
### MAIN ###
parse_args(@ARGV);
process_in_file($in_file);
show_materials() if (VERB5());
show_vertices();
pgm_exit(0,"");
########################################

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av);
}

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg);
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Verbosity = $verbosity\n") if (VERB1());
            } elsif ($sarg =~ /^l/) {
                if ($sarg =~ /^ll/) {
                    $load_log = 2;
                } else {
                    $load_log = 1;
                }
                prt("Set to load log at end. ($load_log)\n") if (VERB1());
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $out_file = $sarg;
                prt("Set out file to [$out_file].\n") if (VERB1());
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = $arg;
            prt("Set input to [$in_file]\n") if (VERB1());
        }
        shift @av;
    }

    if ($debug_on) {
        prtw("WARNING: DEBUG is ON!\n");
        if ((length($in_file) ==  0) && $debug_on) {
            $in_file = $def_file;
            prt("Set DEFAULT input to [$in_file]\n");
        }
        $load_log = 1;
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n");
    }
}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --out <file>  (-o) = Write output to this file.\n");
}

sub get_ref() {
    my $txt = <<EOF;
THE AC3D FILE FORMAT (This document last updated 27th March 2003)

The AC3D file format is ascii text and is very simple to parse.  It's also very easy to generate AC3D files from your own data.  This document describes the structure of AC3D files.  Some simple examples are included at the end.
AC3D filenames usually have a '.ac' suffix.  For more information about AC3D, see http://www.ac3d.org.

an AC3D file has this overall structure:

(HEADER)
(Materials - the palette)
(objects)

An AC3D file always starts with a header line e.g.:

AC3Db

The first four characters are always 'AC3D' the next hex number following
immediately defines the internal version of the file (0xb == 11).  
If the version is later than the program knows about then it will probably
may refuse to load it.  As new versions of the AC3D file format evolve, this number 
is used to make the loading code backwards compatible.

To parse an AC3D file you need something like this:

read the header (AC3Dx)
while not end of file 
    {
    read a line
    check first token on line 
    handle this tokens values (this might involve reading more lines)
    }

This is the basic structure.  \%f indicates a floating point value, \%d an
integer and \%s a string (must be surrounded by quotes " if it contains spaces).
Lines beginning with a '*' indicate an optional section.  The tokens are
case-sensitive.  The indentation is used to show the structure of the file - a
real AC3D file has no indentation.

AC3Db
MATERIAL \%s rgb \%f \%f \%f  amb \%f \%f \%f  emis \%f \%f \%f  spec \%f \%f \%f  shi \%d  trans \%f
OBJECT \%s
	*name \%s
    *data \%d
	*texture \%s
	*texrep \%f \%f
	*rot \%f \%f \%f  \%f \%f \%f  \%f \%f \%f
	*loc \%f \%f \%f
	*url \%s
	*numvert \%d
		numvert lines of \%f \%f \%f
	*numsurf \%d
		*SURF \%d
		*mat \%d
		refs \%d
                refs lines of \%d \%f \%f
	kids \%d

MATERIAL (name) rgb \%f \%f \%f  amb \%f \%f \%f  emis \%f \%f \%f  spec \%f \%f \%f  shi \%d  trans \%f

single line describing a material.  These are referenced by the "mat" 
token of a surface.  The first "MATERIAL" in the file will be indexed as
zero.  The materials are usually all specified at the start of the file,
imediately after the header.  

OBJECT \%s

Specifies the start of an object. The end of the object section must
be a 'kids' line which indicates how many children objects (may be zero)
follow.  The parameter is the object type - one of:  world, poly,
group.

*name \%s

Optional - a name for the object

*data \%d

Optional - object data.  Usually the object-data string for an object.
The parameter is an interger which specifies the number of characters
(starting on the next line) to be read.

*texture \%s

Optional - default is no texture. the path of the texture bitmap file for the texture of the current object.

*texrep \%f \%f

Optional - default 1.0,1.0 .  The texture repeat values for the tiling of a texture
on an object's surfaces.


*rot \%f \%f \%f  \%f \%f \%f  \%f \%f \%f

The 3x3 rotation matrix for this objects vertices.  Note that the rotation is relative
to the object's parent i.e. it is not a global rotation matrix.  If this token
is not specified then the default rotation matrix is 1 0 0, 0 1 0, 0 0 1

*loc \%f \%f \%f

The translation of the object.  Effectively the definition of the centre of the object.  This is
relative to the parent - i.e. not a global position.  If this is not found then
the default centre of the object will be 0, 0, 0.

*url \%s

The url of an object - default is blank.

*numvert \%d
	numvert lines of \%f \%f \%f

The number of vertices in an object.  Parameter specifies the number of 
lins that follow.  If this token is read then you MUST read
that many lines of (\%f \%f \%f) - specifying each vertex point as a local 
coordinate.  Some objects (e.g. groups) may not have a numvert token.

*numsurf \%d

The number of surfaces that this object contains.  The parameter specifies
the number of subsections that follow - each one being a different surface

SURF \%d
The start of a surface.  The parameter specifies the surface type and flags.
The first 4 bits (flags & 0xF) is the type (0 = polygon, 1 = closedline,
 2 = line).  
The next four bits (flags >> 4) specify the shading and 
 backface.  bit1 = shaded surface bit2 = twosided.

*mat \%d
The index to the material that this surface has.

refs \%d
    refs lines of \%d \%f \%f

The number of vertices in the surface.  This number indicates the number of lines
following.  Each line contains an index to the vertex and the texture coordinates
for this surface vertex.

kids \%d

This is the final token of an object section and it must exist.  If the parameter
is a number > 0 then more objects are recursively loaded as children of the 
current object.

Here is an example file - a simple rectangle (white)

AC3Db
MATERIAL "" rgb 1 1 1  amb 0.2 0.2 0.2  emis 0 0 0  spec 0.5 0.5 0.5  shi 10  trans 0
OBJECT world
kids 1
OBJECT poly
name "rect"
loc 1 0.5 0
numvert 4
-1 0.5 0
1 0.5 0
1 -0.5 0
-1 -0.5 0
numsurf 1
SURF 0x20
mat 0
refs 4
3 0 0
2 1 0
1 1 1
0 0 1
kids 0

Another example  - an object with 7 vertices (no surfaces, no materials defined)  This is a good way of getting point data into AC3D.  The Vertex->create convex-surface/object can be used on these vertices to 'wrap' a 3d shape around them

AC3Db
OBJECT poly
numvert 7
-0.83 -0.235 -0.04
-0.63 0.185 -0.04
-0.55 0.235 -0.25
-0.33 0.235 0.29
0.09 0.235 -0.29
0.33 -0.195 -0.04
0.83 0.005 -0.04
kids 0

If you write a loader then you least you need is code to handle the object 
token, and the objects numvert/vertice and numsurf/sufaces - esentially
the geometry of the model.

You can ignore any line that starts with a token other than these e,g textures,
rotation, location etc.

Last revision 27/03/03 ac - removed old email address

EOF
    return $txt;
}

sub get_osg_txt() {
    my $txt = <<EOF;
Review of the tokens handled by C:/FG/17/OSGtrunk/src/osgPlugins/ac/ac3d.cpp,
in osg::Node *readObject()...
if (token == "MATERIAL") {
} else if (token == "OBJECT") {
} else if (token == "crease") {
} else if (token == "data") {
} else if (token == "name") {
} else if (token == "texture") {
} else if (token == "texrep") {
} else if (token == "texoff") {
} else if (token == "rot") {
} else if (token == "loc") {
} else if (token == "url") {
} else if (token == "numvert") {
} else if (token == "numsurf") {
    stream >> num;
    // list of materials required- generate one geode per material
    for (unsigned n = 0; n < num; ++n) {
        stream >> token;
        if (token != "SURF") OSG_FATAL << "osgDB ac3d reader: expected SURF line while reading object
        stream >> token;
        if (token != "mat") OSG_FATAL << "osgDB ac3d reader: expected mat line while reading object
        stream >> matIdx;
        // read the refs
        stream >> token;
        if (token != "refs") OSG_FATAL << "osgDB ac3d reader: expected refs line while reading object
        stream >> nRefs;
        for (unsigned i = 0; i < nRefs; ++i) {
        // Read the vertex index
        stream >> index;
        if (vertexSet->size() <= index) OSG_FATAL << "osgDB ac3d reader: invalid ref vertex index while reading object
        // Read the texture corrdinates
       stream >> texCoord[0] >> texCoord[1];
    }
} else if (token == "kids") {
   stream >> num;
   for (unsigned n = 0; n < num; n++) {
   osg::Node *k = readObject(stream, fileData, transform*parentTransform, textureData);
   if (k == 0) OSG_FATAL << "osgDB ac3d reader: error reading child object" << std::endl;
}

EOF
    return $txt;
}

# eof - ac3dfile.pl
