#!/usr/bin/perl -w # NAME: chkdae.pl # AIM: Load and review a dae file - COLLADA - http://www.collada.org # COLLADA defines an XML-based schema to make it easy to transport 3D assets between applications # 16/05/2013 geoff mclane http://geoffair.net/mperl use strict; use warnings; use File::Basename; # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] ) use XML::Simple; use Data::Dumper; 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"; # 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.1 2013-05-16"; my $load_log = 0; my $in_file = ''; my $verbosity = 0; my $out_file = ''; my $json_out = $temp_dir.$PATH_SEP."temp.dae.json"; my $ac3d_out = $temp_dir.$PATH_SEP."temp.dae2ac3d.ac"; # ### DEBUG ### my $debug_on = 1; my $def_file = 'C:\FG\18\blendac3d\shb\shb3.dae'; ### program variables my @warnings = (); my $cwd = cwd(); 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); } #========================= #$VAR1 = { # 'xmlns' => 'http://www.collada.org/2005/11/COLLADASchema', # 'library_effects' => { # 'effect' => { # 'ID8773' => { # ... # 'library_visual_scenes' => { # 'visual_scene' => { # 'id' => 'ID1', # 'node' => { # 'name' => 'SketchUp', # 'node' => { # 'name' => 'instance_0', # 'id' => 'ID2', # 'instance_node' => { # 'url' => '#ID3' # }, # 'matrix' => '1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1' # ... # 'library_materials' => { # 'material' => { # '__auto_72' => { # 'instance_effect' => { # 'url' => '#ID9357' # }, # 'id' => 'ID9356' # }, # ... # 'library_nodes' => { # 'node' => { # 'Component' => { # 'id' => 'ID5', # 'instance_geometry' => [ # { # 'url' => '#ID6', # 'bind_material' => { # 'technique_common' => { # 'instance_material' => { # 'bind_vertex_input' => { # 'input_set' => '0', # 'input_semantic' => 'TEXCOORD', # 'semantic' => 'UVSET0' # }, # 'target' => '#ID7', # 'symbol' => 'Material2' # } # } # } # }, # # .... # 'version' => '1.4.1', # 'scene' => { # 'instance_visual_scene' => { # 'url' => '#ID1' # } # }, # 'library_images' => { # 'image' => { # 'ID9230' => { # 'init_from' => 'shb3/__auto_9.jpg' # }, # ... # 'library_geometries' => { # 'geometry' => { # 'ID4971' => { # 'mesh' => { # 'vertices' => { # 'input' => [ # { # 'source' => '#ID4972', # 'semantic' => 'POSITION' # }, # { # 'source' => '#ID4973', # 'semantic' => 'NORMAL' # } # ], # 'id' => 'ID4974' # }, # 'source' => { # 'ID4973' => { # 'technique_common' => { # 'accessor' => { # 'stride' => '3', # 'source' => '#ID4976', # 'count' => '8', # 'param' => { # 'Z' => { # 'type' => 'float' # }, # 'X' => { # 'type' => 'float' # }, # 'Y' => { # 'type' => 'float' # } # } # } # }, # 'float_array' => { # 'count' => '24', # 'content' => '-1 0 0 -1 0 0 -1 0 0 -1 0 0 1 -0 -0 1 -0 -0 1 -0 -0 1 -0 -0', # 'id' => 'ID4976' # } # }, # 'triangles' => { # 'p' => '0 1 2 1 0 3 3 0 4 4 0 5 5 0 6 5 6 7 8 9 10 9 11 10 10 11 12 12 11 13 13 11 14 15 14 11', # 'count' => '12', # 'input' => { # 'source' => '#ID5244', # 'semantic' => 'VERTEX', # 'offset' => '0' # }, # 'material' => 'Material2' # } # } # } # } # }, # ... # 'asset' => { # 'created' => '2013-05-16T09:44:18Z', # 'unit' => { # 'name' => 'inch', # 'meter' => '0.02539999969303608' # }, # 'up_axis' => 'Z_UP', # 'modified' => '2013-05-16T09:44:18Z', # 'contributor' => { # 'authoring_tool' => 'SketchUp 8.0.16846' # } # } my %asset_hash = ( 'created' => 1, 'unit' => 2, # { 'name' => 'inch', 'meter' => '0.02539999969303608' }, 'up_axis' => 1, # 'Z_UP', 'modified' => 1, # '2013-05-16T09:44:18Z', 'contributor' => 2 # { 'authoring_tool' => 'SketchUp 8.0.16846' } ); sub write_floats($) { my $rh = shift; # \%floats my @arr = sort keys( %{$rh} ); my $kcnt = scalar @arr; my $json = "{\"success\":true,\"source\":\"$pgmname\",\"last_updated\":\""; $json .= lu_get_YYYYMMDD_hhmmss_UTC(time())." UTC\","; $json .= "\"count\":$kcnt,\"nodes\":[\n"; my ($key,$rfh,$stride,$count,$rfa,$cnt,$rta,$rhidt); $cnt = 0; foreach $key (@arr) { $rfh = ${$rh}{$key}; #prt(Dumper($rfh)); #pgm_exit(1,""); $rhidt = ${$rfh}{'idtyp'}; $rfa = ${$rfh}{'verts'}; $rta = ${$rfh}{'tris'}; $stride = ${$rfh}{'stride'}; $count = ${$rfh}{'count'}; $json .= " {\"id\":\"$key\","; $json .= "\"tris\":["; $json .= join(",",@{$rta}); $json .= "],"; $json .= "\"count\":$count,"; $json .= "\"stride\":$stride,\n"; if (defined ${$rhidt}{$key}) { $json .= " \"".${$rhidt}{$key}."\":["; } else { $json .= " \"vertices\":["; } $json .= join(",",@{$rfa}); $json .= "]}"; $cnt++; $json .= ',' if ($cnt < $kcnt); $json .= "\n"; } $json .= "]}\n"; write2file($json,$json_out); prt("json written to [$json_out]\n"); } sub mycmp_id_sort { my $id1 = substr($a,2); my $id2 = substr($b,2); return -1 if ($id1 < $id2); return 1 if ($id1 > $id2); return 0; } # $mesh_hash{$k2}->{idtyp} = {%hidt}; # sep POSITION and NORMAL # $mesh_hash{$k2}->{tris} = [@art]; # $mesh_hash{$k2}->{verts} = [@ar3]; # $mesh_hash{$k2}->{stride} = $stride; # $mesh_hash{$k2}->{count} = $count; # $mesh_hash{$k2}->{order} = $kcnt; # simple numeric order sub write_meshes($) { my $rmh = shift; # \%master_hash # each 'mesh' is stored by a 'key' like ID10933 my ($mkey,$rh); my ($k2,$v2,$typ,$cnt); my @marr = sort mycmp_id_sort keys(%{$rmh}); $cnt = scalar @marr; my ($key,$rfh,$rhidt,$rfa,$rta,$stride,$count); my ($msg,@ar2,@arr,$itm,$wrap,$v); my $ac3d = "AC3Db\n"; $ac3d .= "MATERIAL \"DefaultWhite\" rgb 1.0000 1.0000 1.0000 amb 0.2000 0.2000 0.2000 emis 0.0000 0.0000 0.0000 spec 0.5000 0.5000 0.5000 shi 10 trans 0.0000\n"; $ac3d .= "MATERIAL \"Material\" rgb 0.8000 0.8000 0.8000 amb 1.0000 1.0000 1.0000 emis 0.0000 0.0000 0.0000 spec 0.5000 0.5000 0.5000 shi 50 trans 0.0000\n"; $ac3d .= "OBJECT world\n"; $ac3d .= "name \"".$pgmname."_AC3D_gen\"\n"; $ac3d .= "kids $cnt\n"; $itm = 0; foreach $mkey (@marr) { $itm++; $msg = "$itm: $mkey: "; $ac3d .= "OBJECT poly\n"; $ac3d .= "name \"$mkey\"\n"; $ac3d .= "loc 0.0 0.0 0.0\n"; #$ac3d .= "crease 45.0\n"; # $master_hash{$key}->{mesh} = {%mesh_hash}; $rh = ${$rmh}{$mkey}{'mesh'}; # get the mesh hash @arr = sort mycmp_id_sort keys(%{$rh}); # got through the keys for this mesh foreach $key (@arr) { $msg .= "$key "; $rfh = ${$rh}{$key}; #prt(Dumper($rfh)); #pgm_exit(1,""); $rhidt = ${$rfh}{'idtyp'}; $rfa = ${$rfh}{'verts'}; $rta = ${$rfh}{'tris'}; $stride = ${$rfh}{'stride'}; $count = ${$rfh}{'count'}; $typ = ${$rhidt}{$key}; if ((defined $typ) && ($typ eq 'POSITION')) { $cnt = scalar @{$rfa}; $msg .= "POS ".int($cnt / 3)." "; $ac3d .= "numvert ".int($cnt / 3)."\n"; $wrap = 0; foreach $v (@{$rfa}) { $ac3d .= $v; $wrap++; if ($wrap == 3) { $ac3d .= "\n"; $wrap = 0; } else { $ac3d .= ' '; } } if ($wrap) { prtw("WARNING: Appears cnt $cnt not divis by 3!\n". "Filling in ".(3 - $wrap)." zero vertice(s) - UGH\n"); while ($wrap < 3) { $ac3d .= " 0.0"; $wrap++; } $ac3d .= "\n"; } $cnt = scalar @{$rta}; $ac3d .= "numsurf ".int($cnt / 3)."\n"; $wrap = 0; $msg .= "TRI ".int($cnt / 3)." "; foreach $v (@{$rta}) { if ($wrap == 0) { $ac3d .= "SURF 0x10\n"; $ac3d .= "mat 1\n"; $ac3d .= "refs 3\n"; } $ac3d .= "$v 0 0\n"; $wrap++; $wrap = 0 if ($wrap == 3); } if ($wrap) { prtw("WARNING: Appears cnt $cnt not divis by 3!\n". "Filling in ".(3 - $wrap)." zero tris - UGH\n"); while ($wrap < 3) { $ac3d .= "0 0 0\n"; $wrap++; } } $ac3d .= "kids 0\n"; } elsif (defined $typ) { $msg .= "Skip $typ "; } else { $msg .= "NO TYPE! "; } } prt("$msg\n"); } write2file($ac3d,$ac3d_out); prt("ac3d written to [$ac3d_out]\n"); } sub process_in_file($) { my ($inf) = @_; if (! -f $inf) { pgm_exit(1,"ERROR: Unable to 'stat' file [$inf]\n"); } my $xml = new XML::Simple; # (ForceArray => 0); my $data = $xml->XMLin($inf); #prt(Dumper($data)); #$load_log = 2; my ($tmp,$key,$val,$k2,$v2,@arr,@ar2,$cnt,$kcnt,@ar3,$msg,@art); my ($stride,$count,$source,$semantic,%hidt,$rt); if (defined ${$data}{'asset'}) { my $rah = ${$data}{'asset'}; #prt(Dumper($rah)); foreach $key (keys %asset_hash) { $val = $asset_hash{$key}; if (defined ${$rah}{$key}) { $tmp = ${$rah}{$key}; if ($val == 1) { prt("$key: $tmp\n"); } elsif ($val == 2) { foreach $k2 (keys %{$tmp}) { $v2 = ${$tmp}{$k2}; prt("$key -> $k2 = $v2\n"); } } } } } my %floats = (); my %idtotype = (); my %master_hash = (); my %mesh_hash = (); if (defined ${$data}{'library_geometries'}{'geometry'}) { my $rgh = ${$data}{'library_geometries'}{'geometry'}; #prt(Dumper($rgh)); @arr = keys %{$rgh}; $v2 = scalar @arr; prt("Found $v2 geometries\n"); $kcnt = 0; foreach $key (@arr) { # ok, each 'key' here, like 'ID10933' should have a 'mesh' # That 'mesh' is made up of - # 'vertices' which will be ID1=NORMAL, ID2=POSITION # 'source' where ID1, ID2 will be given as an array # 'triangles' sets of 3 giving indexs into NORMAL and POSITIONS # $val = ${$rgh}{$key}; # extract a geometry # prt(Dumper($val)) if ($kcnt == 0); $kcnt++; # simple numeric order... $msg = "$kcnt: "; %mesh_hash = (); # restart the 'mesh' hash if (defined ${$val}{'mesh'}) { my $rmh = ${$val}{'mesh'}; $msg .= "mesh "; @art = (); %hidt = (); if (defined ${$rmh}{'vertices'}{'input'}) { my $rvina = ${$rmh}{'vertices'}{'input'}; $msg .= 'I'; foreach $k2 (@{$rvina}) { if ((defined ${$k2}{'source'})&&(defined ${$k2}{'semantic'})) { $source = ${$k2}{'source'}; $source =~ s/^\#//; $semantic = ${$k2}{'semantic'}; $idtotype{$source} = $semantic; $hidt{$source} = $semantic; $msg .= "$source:".substr($semantic,0,1); } else { $msg .= 'NSrc:Sem?'; } } } if (defined ${$rmh}{'triangles'}) { my $rth = ${$rmh}{'triangles'}; # get ref hash if (defined ${$rth}{'p'}) { $v2 = ${$rth}{'p'}; @art = split(/\s+/,$v2); $cnt = scalar @art; $msg .= " T$cnt "; } if (defined ${$rth}{'input'}) { my $rtria = ${$rth}{'input'}; # get array $rt = ref($rtria); if ($rt eq 'ARRAY') { foreach $k2 (@{$rtria}) { if ((defined ${$k2}{'source'})&&(defined ${$k2}{'semantic'})) { $source = ${$k2}{'source'}; $source =~ s/^\#//; $semantic = ${$k2}{'semantic'}; $idtotype{$source} = $semantic; $hidt{$source} = $semantic; } } } elsif ($rt eq 'HASH') { $k2 = $rtria; if ((defined ${$k2}{'source'})&&(defined ${$k2}{'semantic'})) { $source = ${$k2}{'source'}; $source =~ s/^\#//; $semantic = ${$k2}{'semantic'}; $idtotype{$source} = $semantic; $hidt{$source} = $semantic; } } } } if (defined ${$rmh}{'source'}) { my $rsh = ${$rmh}{'source'}; @ar2 = keys %{$rsh}; $cnt = scalar @ar2; $msg .= "-> source cnt $cnt "; foreach $k2 (@ar2) { my $rfah = ${$rsh}{$k2}; if (defined ${$rfah}{'technique_common'}{'accessor'}) { my $racch = ${$rfah}{'technique_common'}{'accessor'}; if ((defined ${$racch}{'stride'}) && (defined ${$racch}{'count'})) { $count = ${$racch}{'count'}; $stride = ${$racch}{'stride'}; $msg .= $count.'x'.$stride.'='; } } if (defined ${$rfah}{'float_array'}) { my $rfa = ${$rfah}{'float_array'}; if (defined ${$rfa}{'count'}) { $cnt = ${$rfa}{'count'}; if (defined $hidt{$k2}) { $msg .= "$cnt=".$hidt{$k2}." "; } else { $msg .= "$cnt $k2 NO DEF? "; } } if (defined ${$rfa}{'content'}) { $v2 = ${$rfa}{'content'}; # get the floats @ar3 = split(/\s+/,$v2); $floats{$k2}->{idtyp} = {%hidt}; # sep POSITION and NORMAL $floats{$k2}->{tris} = [@art]; $floats{$k2}->{verts} = [@ar3]; $floats{$k2}->{stride} = $stride; $floats{$k2}->{count} = $count; $floats{$k2}->{order} = $kcnt; $mesh_hash{$k2}->{idtyp} = {%hidt}; # sep POSITION and NORMAL $mesh_hash{$k2}->{tris} = [@art]; $mesh_hash{$k2}->{verts} = [@ar3]; $mesh_hash{$k2}->{stride} = $stride; $mesh_hash{$k2}->{count} = $count; $mesh_hash{$k2}->{order} = $kcnt; # simple numeric order } } else { $msg .= "NFA "; } } } } else { $msg .= "NO 'mesh'!"; } $master_hash{$key}->{mesh} = {%mesh_hash}; prt("$msg\n"); } #$load_log = 2; } write_floats(\%floats); write_meshes(\%master_hash); } ######################################### ### MAIN ### parse_args(@ARGV); process_in_file($in_file); 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"); } } 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 (-o) = Write output to this file.\n"); } # for an AC3D format reference see ac3dfile.pl # eof - chkdae.pl