r5139 - trunk/misc
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Fri Nov 28 08:27:29 EST 2008
Author: div0
Date: 2008-11-28 08:27:29 -0500 (Fri, 28 Nov 2008)
New Revision: 5139
Modified:
trunk/misc/bsptool.pl
Log:
bsptool: -ljpg = externalize lightmaps, save as jpg
Modified: trunk/misc/bsptool.pl
===================================================================
--- trunk/misc/bsptool.pl 2008-11-28 09:52:25 UTC (rev 5138)
+++ trunk/misc/bsptool.pl 2008-11-28 13:27:29 UTC (rev 5139)
@@ -2,10 +2,20 @@
use strict;
use warnings;
+use Image::Magick;
+use POSIX qw/floor ceil/;
+my @lumpname = qw/entities textures planes nodes leafs leaffaces leafbrushes models brushes brushsides vertices triangles effects faces lightmaps lightgrid pvs advertisements/;
+my %lumpid = map { $lumpname[$_] => $_ } 0.. at lumpname-1;
my $msg = "";
+my @bsp;
+# READ THE BSP
+
my $fn = shift @ARGV;
+$fn =~ /(.*)\.bsp$/
+ or die "invalid input file name (must be a .bsp): $fn";
+my $basename = $1;
open my $fh, "<", $fn
or die "$fn: $!";
@@ -14,17 +24,12 @@
die "Invalid BSP format"
if $header ne "IBSP\x2e\x00\x00\x00";
-my @lumpname = qw/entities textures planes nodes leafs leaffaces leafbrushes models brushes brushsides vertices triangles effects faces lightmaps lightgrid pvs advertisements/;
-my %lumpid = map { $lumpname[$_] => $_ } 0.. at lumpname-1;
-
-my @bsp;
-
for(0..16)
{
read $fh, my $lump, 8;
my ($offset, $length) = unpack "VV", $lump;
- print "BSP lump $_ ($lumpname[$_]): offset $offset length $length\n";
+ print STDERR "BSP lump $_ ($lumpname[$_]): offset $offset length $length\n";
push @bsp, [$offset, $length, undef];
}
@@ -38,30 +43,157 @@
$_->[2] = $data;
}
+close $fh;
+
+# STRUCT DECODING
+
+sub DecodeLump($@)
+{
+ my ($lump, @fields) = @_;
+ my @decoded;
+
+ my $spec = "";
+ my @decoders;
+
+ my $item;
+ my @data;
+ my $idx;
+
+ for(@fields)
+ {
+ if(/^(\w*)=(.*?)(\d*)$/)
+ {
+ $spec .= "$2$3 ";
+ my $f = $1;
+ my $n = $3;
+ if($n eq '')
+ {
+ push @decoders, sub { $item->{$f} = $data[$idx++]; };
+ }
+ else
+ {
+ push @decoders, sub { $item->{$f} = [ map { $data[$idx++] } 1..$n ]; };
+ }
+ }
+ }
+
+ my $itemlen = length pack $spec, ();
+ my $len = length $lump;
+
+ die "Invalid lump size: $len not divisible by $itemlen"
+ if $len % $itemlen;
+
+ my $items = $len / $itemlen;
+ for(0..$items - 1)
+ {
+ @data = unpack $spec, substr $lump, $_ * $itemlen, $itemlen;
+ $item = {};
+ $idx = 0;
+ $_->() for @decoders;
+ push @decoded, $item;
+ }
+ @decoded;
+}
+
+# OPTIONS
+
for(@ARGV)
{
- if(/^-x(.*)$/)
+ if(/^-d(.+)$/) # delete a lump
{
my $id = $lumpid{$1};
die "invalid lump $1 to remove"
unless defined $id;
$bsp[$id]->[2] = "";
}
- elsif(/^-m(.*)$/)
+ elsif(/^-m(.*)$/) # change the message
{
$msg = $1;
}
- elsif(/^-e(.*)$/) # extract lump
+ elsif(/^-l(jpg|png|tga)(\d+)?$/) # externalize lightmaps (deleting the internal ones)
{
+ my $ext = $1;
+ my $quality = $2;
+ my %lightmaps = ();
+ my $faces = $bsp[$lumpid{faces}]->[2];
+ my $lightmaps = $bsp[$lumpid{lightmaps}]->[2];
+ my @values = DecodeLump $faces,
+ qw/texture=V effect=V type=V vertex=V n_vertexes=V meshvert=V n_meshverts=V lm_index=V lm_start=f2 lm_size=f2 lm_origin=f3 lm_vec_0=f3 lm_vec_1=f3 normal=f3 size=V2/;
+ my $oddfound = 0;
+ for(@values)
+ {
+ my $l = $_->{lm_index};
+ next if $l >= 2**31; # signed
+ $oddfound = 1
+ if $l % 2;
+ ++$lightmaps{$l};
+ }
+ if(!$oddfound)
+ {
+ $lightmaps{$_+1} = $lightmaps{$_} for keys %lightmaps;
+ }
+ for(sort { $a <=> $b } keys %lightmaps)
+ {
+ print STDERR "Lightmap $_ was used $lightmaps{$_} times\n";
+
+ # export that lightmap
+ my $lmsize = 128 * 128 * 3;
+ next if length $lightmaps < ($_ + 1) * $lmsize;
+ my $lmdata = substr $lightmaps, $_ * $lmsize, $lmsize;
+ my $img = Image::Magick->new(size => '128x128', depth => 8, magick => 'RGB');
+ $img->BlobToImage($lmdata);
+ my $outfn = sprintf "%s/lm_%04d.$ext", $basename, $_;
+ mkdir $basename;
+ $img->Set(quality => $quality)
+ if defined $quality;
+ my $err = $img->Write($outfn);
+ die $err
+ if $err;
+ print STDERR "Wrote $outfn\n";
+ }
+
+ # nullify the lightmap lump
+ $bsp[$lumpid{lightmaps}]->[2] = "";
+ }
+ elsif(/^-g$/) # decimate light grid
+ {
+ my @models = DecodeLump $bsp[$lumpid{models}]->[2],
+ qw/mins=f3 maxs=f3 face=V n_faces=V brush=V n_brushes=V/;
+ my $entities = $bsp[$lumpid{entities}]->[2];
+ my @entitylines = split /\r?\n/, $entities;
+ my $gridsize = "64 64 128";
+ for(@entitylines)
+ {
+ last if $_ eq '}';
+ /^\s*"gridsize"\s+"(.*)"$/
+ and $gridsize = $1;
+ }
+ my @scale = map { 1 / $_ } split / /, $gridsize;
+ my @imins = map { ceil($models[0]{mins}[$_] * $scale[$_]) } 0..2;
+ my @imaxs = map { floor($models[0]{maxs}[$_] * $scale[$_]) } 0..2;
+ my @isize = map { $imaxs[$_] - $imins[$_] + 1 } 0..2;
+ my $isize = $isize[0] * $isize[1] * $isize[2];
+ my @gridcells = DecodeLump $bsp[$lumpid{lightgrid}]->[2],
+ qw/ambient=C3 directional=C3 dir=C2/;
+ die "Cannot decode light grid"
+ unless $isize == @gridcells;
+
+ # TODO now decimate it and reinsert the lump (and the changed entity lump for the new size)
+ }
+ elsif(/^-x(.+)$/) # extract lump to stdout
+ {
my $id = $lumpid{$1};
die "invalid lump $1 to extract"
unless defined $id;
print $bsp[$id]->[2];
}
- elsif(/^-o(.*)$/)
+ elsif(/^-o(.+)?$/) # write the final BSP file
{
- open my $fh, ">", $1
- or die "$1: $!";
+ my $outfile = $1;
+ $outfile = $fn
+ if not defined $outfile;
+ open my $fh, ">", $outfile
+ or die "$outfile: $!";
print $fh $header;
my $pos = 17 * 8 + tell($fh) + length $msg;
for(@bsp)
@@ -76,11 +208,16 @@
{
print $fh $_->[2];
}
+ close $fh;
+ print "Wrote $outfile\n";
}
+ else
+ {
+ die "Invalid option: $_";
+ }
}
# TODO:
# features like:
-# externalize lightmaps
# decimate light grid
# edit lightmaps/grid
More information about the nexuiz-commits
mailing list