#!/usr/bin/perl

use strict;
use warnings;
use MD3;
use Params;

my %params = Params::get(qw/-i -o -t -s -v/);

my $infile = $params{-i};
my $scale = $params{-s};
my @shift = split / /, $params{-v};
my $modelname = $params{-o};
my $sprefix = $params{-t};

sub TranslateCoordinate(@)
{
	my ($x, $y, $z) = @_;
	return ($x * $scale + $shift[0], $z * $scale + $shift[1], $y * $scale + $shift[2]);
}

sub TranslateTexCoordinate(@)
{
	my ($x, $y) = @_;
	return ($x, $y);
}

sub TranslateFace(@)
{
	@_;
}

open my $inmodel, "<", $infile
	or die "$infile: $!";
open my $outmodel, ">", $modelname
	or die "$modelname: $!";

sub gets()
{
	return 0
		unless defined ($_ = <$inmodel>);
	y/\r//d;
	chomp;
	return 1;
}

sub FindNormal($$$)
{
	my ($a, $b, $c) = @_;

	my @b = map { $b->[$_] - $a->[$_] } 0..2;
	my @c = map { $c->[$_] - $a->[$_] } 0..2;

	my @norm =
	(
		$b[1] * $c[2] - $b[2] * $c[1],
		$b[2] * $c[0] - $b[0] * $c[2],
		$b[0] * $c[1] - $b[1] * $c[0]
	);

	my $norm = sqrt($norm[0]*$norm[0] + $norm[1]*$norm[1] + $norm[2]*$norm[2]);

	if($norm <= 0)
	{
		warn "@$a, @$b, @$c degenerate, discarding";
		return ();

		@norm = ( -$b[0]*$b[2], -$b[1]*$b[2], $b[0]*$b[0] + $b[1]*$b[1] );

		if($norm <= 0)
		{
			warn "@$a, @$b, @$c identical, cannot make a good normal; outputting a bad one";
			@norm = (0, 0, 1);
			$norm = 1;
		}
		else
		{
			warn "@$a, @$b, @$c collinear, cannot make a good normal; outputting a bad one";
			$norm = sqrt($norm[0]*$norm[0] + $norm[1]*$norm[1] + $norm[2]*$norm[2]);
		}
	}

	return map { $_ / $norm } @norm;
}

while(gets())
{
	if(/^\[ObjectBegin\]$/)
	{
		my $name = "UNNAMED";
		my @origin = (0, 0, 0);
		my @vertexes = ();

		my $md3 = MD3->new();
		my %surfaces; # shader -> { id => surfnumber, vertexes => { vertexstr -> num } }

		while(gets())
		{
			if(/^Name=\s*(.*)$/)
			{
				$name = $1;
				print STDERR "Processing $name...\n";
			}
			elsif(/^CentralPoint=\s*(.*)$/)
			{
				@origin = TranslateCoordinate split /\s+/, $1;
			}
			elsif(/^Verts=\s*(.*)$/)
			{
				my $nverts = $1;
				for(0..$nverts-1)
				{
					gets();
					my @vert = TranslateCoordinate split /\s+/, $_;
					$vert[0] -= $origin[0];
					$vert[1] -= $origin[1];
					$vert[2] -= $origin[2];
					push @vertexes, \@vert;
				}
			}
			elsif(/^Faces=\s*(.*)$/)
			{
				my $nfaces = $1;
				for(1..$nfaces)
				{
					gets();
					my @tokens = split /\s+/, $_;
					my $num = shift @tokens;
					die "Not a triangle"
						if $num != 3;
					my @face = ();
					for(0..$num-1)
					{
						push @face, [@{$vertexes[shift @tokens]}];
					}

					my $material = shift @tokens;
					for(0..$num-1)
					{
						my $s = shift @tokens;
						my $t = shift @tokens;
						($s, $t) = TranslateTexCoordinate($s, $t);
						push @{$face[$_]}, $s, $t;
					}
					@face = TranslateFace @face;

					# find the normal of the face
					my @norm = FindNormal $face[0], $face[1], $face[2];
					next
						if @norm == 0;

					my $surf = $surfaces{$material};
					if(not defined $surf)
					{
						$surf =
						{
							id => $md3->AddSurface($sprefix . $material),
							vertexes => {}
						};
						$surfaces{$material} = $surf;
					}

					my @vertexids = ();
					for(@face)
					{
						# allocate the vertices
						my @vert = (
							$_->[0], $_->[1], $_->[2],
							$norm[0], $norm[1], $norm[2],
							$_->[3], $_->[4]
						);
						my $vertstring = join ":", @vert;
						my $vertid = $surf->{vertexes}->{$vertstring};
						if(not defined $vertid)
						{
							$vertid = $md3->AddVertex($surf->{id}, @vert);
						}
						push @vertexids, $vertid;
					}

					$md3->AddTriangle($surf->{id}, @vertexids);
				}
			}
			elsif(/^\[ObjectEnd\]$/)
			{
				last;
			}
		}

		my $mscale = $md3->ModelScale();
		$md3->ApplyInverseScale($mscale);

		my $data = $md3->Write();

		$md3->{filename} = $name;
		open my $fh, ">", "$modelname"
			or die "$modelname: $!";
		print $fh $data;
		close $fh;

		print "{\n";
		print "\"classname\" \"misc_gamemodel\"\n";
		print "\"model\" \"$modelname\"\n";
		$origin[2] += 256; # shift up a little, for autodropping (spawnflags)
		print "\"origin\" \"@origin\"\n";
		print "\"modelscale\" \"$mscale\"\n";
		print "\"spawnflags\" \"3\"\n"; # drop down using the origin
		print "}\n";
	}
}
