#!perl -s
# -*- perl -*-
# Takes Stanford ply ascii file, and converts it to a mesh

use strict;

@ARGV=("-") if @ARGV==0;
die unless @ARGV==1;

open(FILE,"<@ARGV");
binmode(FILE);
binmode(STDOUT);

my $binary=0; my $little_endian=0;
my $nvproperties=0; my $nvpropbytes=0;
my $ucharintensity=0; my $ucharlist=0; my $rgb_byte_offset=0;
my $nv=0; my $nf=0; my $nstrips;

while (<FILE>) {
  s/\cM\cJ$/\cJ/;		# dos to unix
  print "# $_";
  if (/^element vertex/) { die "? '$_'" unless ($nv)=/vertex\s+(\d+)$/; }
  if (/^element face/) { die "?" unless ($nf)=/face\s+(\d+)$/; }
  if (/^element tristrips/) { die "?" unless ($nstrips)=/tristrips\s+(\d+)$/; die "?" unless $nstrips==1; }
  if (/^property /) {
    if ($nf) {
      if (/^property uchar intensity/) {
	$ucharintensity=1;
      } elsif (/^property list.*indices$/) {
      } else {
	die "?";
      }
      if (/list uchar int/) { $ucharlist=1; }
    } elsif ($nstrips) {
      if (/^property list int int vertex_indices$/) {
      } else {
	die "?";
      }
    } else {
      $nvproperties++;
      if (/ red$/) { $rgb_byte_offset=$nvpropbytes; }
      if (/ uchar /) {
	$nvpropbytes+=1;
      } elsif (/ float /) {
	$nvpropbytes+=4;
      } else {
	die "?";
      }
    }
  }
  $little_endian=1 if /^format.*binary_little_endian/;
  $binary=1 if /binary_/;
  if (/^end_header$/) { last; }
}

warn "binary=$binary nvproperties=$nvproperties nvpropbytes=$nvpropbytes" .
  " ucharlist=$ucharlist nv=$nv nf=$nf\n";

warn "processing vertices\n";
for (my $i=1;$i<=$nv;$i++) {
  my $attrib=""; my @F;
  if (!$binary) {
    $_=<FILE>;
    @F=split(' ');
    die "?" unless @F==$nvproperties;
    if ($rgb_byte_offset && $nvproperties==6) {
      $attrib=sprintf(" {rgb=(%g %g %g)}",
		      $F[3]/255., $F[4]/255., $F[5]/255.);
    }
  } else {
    read(FILE,$_,$nvpropbytes)==$nvpropbytes or die "?";
    @F=unpack("fff", pack("LLL",unpack($little_endian?"VVV":"NNN",substr($_,0,12))));
    die "?" unless @F==3;
    if ($rgb_byte_offset) {
      my $rgb=substr($_,$rgb_byte_offset,3);
      # die "?" unless length($_)==3;
      $attrib=sprintf(" {rgb=(%g %g %g)}",
		      unpack("C",substr($rgb,0,1))/255.,
		      unpack("C",substr($rgb,1,1))/255.,
		      unpack("C",substr($rgb,2,1))/255.);
    }
  }
  # print "Vertex $i  @F[0..2]$attrib\n";
  printf "Vertex $i  %g %g %g%s\n",@F[0..2],$attrib;
}

warn "processing faces\n";
if ($nstrips) {
  die "?" unless $binary;
  read(FILE,$_,4)==4 or die "?";
  my $numi=unpack($little_endian?"V":"N",$_);
  my $ii=0; my $nstrips=0; my ($vi0, $vi1, $vi2);
  for (my $i=0;$i<$numi;$i++) {
    read(FILE,$_,4)==4 or die "?";
    my $vi=unpack($little_endian?"V":"N",$_);
    if ($vi==4294967295) { $nstrips++; $ii=0; next; }
    $vi+=1; $ii++; $vi2=$vi1; $vi1=$vi0; $vi0=$vi;
    next if $ii<3;
    $nf++;
    if ($ii&1) {
      print "Face $nf  $vi2 $vi1 $vi0\n";
    } else {
      print "Face $nf  $vi1 $vi2 $vi0\n";
    }
  }
  die "?" unless $ii==0;
  warn "nstrips=$nstrips nfaces=$nf\n";
} else {
  for (my $i=1;$i<=$nf;$i++) {
    my @F;
    if (!$binary) {
      $_=<FILE>;
      @F=split(' ');
    } else {
      if ($ucharintensity) { read(FILE,$_,1); }
      if (!$ucharlist) {
	read(FILE,$_,16)==16 or die "?";
	@F=unpack($little_endian?"VVVV":"NNNN",$_);
      } else {
	read(FILE,$_,13)==13 or die "?";
	@F=unpack($little_endian?"CVVV":"CNNN",$_);
      }
    }
    die "?" unless @F==4;
    if ($F[0]!=3) { warn "$F[0]\n"; }
    die "?" unless $F[0]==3;
    shift @F;
    for my $a (@F) {
      $a+=1;
      if ($a<1 || $a>$nv) {
	warn "references to $a outside 1..$nv\n";
      }
    }
    print "Face $i  @F[0..2]\n";
  }
}
