2 # Copyright © 2003-2011 Jamie Zawinski <jwz@jwz.org>
4 # Permission to use, copy, modify, distribute, and sell this software and its
5 # documentation for any purpose is hereby granted without fee, provided that
6 # the above copyright notice appear in all copies and that both that
7 # copyright notice and this permission notice appear in supporting
8 # documentation. No representations are made about the suitability of this
9 # software for any purpose. It is provided "as is" without express or
12 # Reads a VRML WRL file, and emits C data suitable for use with OpenGL's
13 # glInterleavedArrays() and glDrawArrays() routines.
15 # Face normals are computed.
19 # --normalize Compute the bounding box of the object, and scale all
20 # coordinates so that the object fits inside a unit cube.
22 # Created: 8-Mar-2003 for Wavefront OBJ, converted to VRML 27-Sep-2011.
28 my $progname = $0; $progname =~ s@.*/@@g;
29 my $version = q{ $Revision: 1.1 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
34 # convert a vector to a unit vector
37 my $L = sqrt (($x * $x) + ($y * $y) + ($z * $z));
49 # Calculate the unit normal at p0 given two other points p1,p2 on the
50 # surface. The normal points in the direction of p1 crossproduct p2.
52 sub face_normal($$$$$$$$$) {
55 $p2x, $p2y, $p2z) = @_;
58 my ($pax, $pay, $paz);
59 my ($pbx, $pby, $pbz);
67 $nx = $pay * $pbz - $paz * $pby;
68 $ny = $paz * $pbx - $pax * $pbz;
69 $nz = $pax * $pby - $pay * $pbx;
71 return (normalize ($nx, $ny, $nz));
75 sub parse_vrml_1($$$) {
76 my ($filename, $body, $normalize_p) = @_;
78 my @verts = (); # list of refs of coords, [x, y, z]
79 my @faces = (); # list of refs of [ point, point, point, ... ]
80 # where 'point' is a list of indexes into 'verts'.
82 $body =~ s% \b point \s* \[ (.*?) \] %{
83 foreach my $point (split (/,/, $1)) {
84 $point =~ s/^\s+|\s+$//gsi;
86 my @p = split(/\s+/, $point);
91 $body =~ s% \b coordIndex \s* \[ (.*?) \] %{
92 foreach my $face (split (/\s*,-1,?\s*/, $1)) {
93 $face =~ s/^\s+|\s+$//gsi;
95 my @p = split(/\s*,\s*/, $face);
100 return () if ($#verts < 0);
102 # generate interleaved list of triangle coordinates and normals
105 my $nfaces = $#faces+1;
107 foreach my $f (@faces) {
108 # $f is [ p1, p2, p3, ... ]
112 error ("too few points in face") if ($#f < 2);
115 # If there are more than 3 points, do a triangle fan from the first one:
116 # [1 2 3] [1 3 4] [1 4 5] etc. Doesn't always work with convex shapes.
122 my ($pp1, $pp2, $pp3) = ($p1, $p2, $p3);
123 # Reverse the winding order.
124 # ($pp1, $pp2, $pp3) = ($pp3, $pp2, $pp1);
126 my $x1 = $verts[$pp1]->[0];
127 my $y1 = $verts[$pp1]->[1];
128 my $z1 = $verts[$pp1]->[2];
130 my $x2 = $verts[$pp2]->[0];
131 my $y2 = $verts[$pp2]->[1];
132 my $z2 = $verts[$pp2]->[2];
134 my $x3 = $verts[$pp3]->[0];
135 my $y3 = $verts[$pp3]->[1];
136 my $z3 = $verts[$pp3]->[2];
138 error ("missing points in face") unless defined($z3);
140 my ($nx, $ny, $nz) = face_normal ($x1, $y1, $z1,
144 push @triangles, [$nx, $ny, $nz, $x1, $y1, $z1,
145 $nx, $ny, $nz, $x2, $y2, $z2,
146 $nx, $ny, $nz, $x3, $y3, $z3];
154 sub parse_vrml($$$) {
155 my ($filename, $body, $normalize_p) = @_;
159 $body =~ s/\s*\#.*$//gmi; # comments
162 $body =~ s/\bIndexedLineSet \s* { \s* coordIndex \s* \[ .*? \] \s* }//gsix;
164 $body =~ s/(\bSeparator\b)/\001$1/g;
166 foreach my $sec (split (m/\001/, $body)) {
167 push @triangles, parse_vrml_1 ($filename, $sec, $normalize_p);
171 # find bounding box, and normalize
173 if ($normalize_p || $verbose) {
174 my $minx = 999999999;
175 my $miny = 999999999;
176 my $minz = 999999999;
177 my $maxx = -999999999;
178 my $maxy = -999999999;
179 my $maxz = -999999999;
182 foreach my $t (@triangles) {
183 my ($nx1, $ny1, $nz1, $x1, $y1, $z1,
184 $nx2, $ny2, $nz2, $x2, $y2, $z2,
185 $nx3, $ny3, $nz3, $x3, $y3, $z3) = @$t;
187 foreach my $x ($x1, $x2, $x3) {
188 $minx = $x if ($x < $minx);
189 $maxx = $x if ($x > $maxx);
191 foreach my $y ($y1, $y2, $y3) {
192 $miny = $y if ($y < $miny);
193 $maxy = $y if ($y > $maxy);
195 foreach my $z ($z1, $z2, $z3) {
196 $minz = $z if ($z < $minz);
197 $maxz = $z if ($z > $maxz);
201 my $w = ($maxx - $minx);
202 my $h = ($maxy - $miny);
203 my $d = ($maxz - $minz);
204 my $sizea = ($w > $h ? $w : $h);
205 my $sizeb = ($w > $d ? $w : $d);
206 my $size = ($sizea > $sizeb ? $sizea : $sizeb);
208 print STDERR "$progname: bbox is " .
209 sprintf("%.2f x %.2f x %.2f\n", $w, $h, $d)
216 print STDERR "$progname: dividing by $size for bbox of " .
217 sprintf("%.2f x %.2f x %.2f\n", $w, $h, $d)
220 foreach my $t (@triangles) {
222 $t[3] /= $size; $t[4] /= $size; $t[5] /= $size;
223 $t[9] /= $size; $t[10] /= $size; $t[11] /= $size;
224 $t[15] /= $size; $t[16] /= $size; $t[17] /= $size;
235 my ($filename, @triangles) = @_;
239 $code .= "#include \"gllist.h\"\n";
240 $code .= "static const float data[]={\n";
242 my $nfaces = $#triangles + 1;
243 my $npoints = $nfaces * 3;
245 foreach my $t (@triangles) {
246 my ($nx1, $ny1, $nz1, $x1, $y1, $z1,
247 $nx2, $ny2, $nz2, $x2, $y2, $z2,
248 $nx3, $ny3, $nz3, $x3, $y3, $z3) = @$t;
249 my $lines = sprintf("\t" . "%.6f,%.6f,%.6f," . "%.6f,%.6f,%.6f,\n" .
250 "\t" . "%.6f,%.6f,%.6f," . "%.6f,%.6f,%.6f,\n" .
251 "\t" . "%.6f,%.6f,%.6f," . "%.6f,%.6f,%.6f,\n",
252 $nx1, $ny1, $nz1, $x1, $y1, $z1,
253 $nx2, $ny2, $nz2, $x2, $y2, $z2,
254 $nx3, $ny3, $nz3, $x3, $y3, $z3);
255 $lines =~ s/([.\d])0+,/$1,/g; # lose trailing insignificant zeroes
257 $lines =~ s/-0,/0,/g;
262 my $token = $filename; # guess at a C token from the filename
263 $token =~ s/\<[^<>]*\>//;
265 $token =~ s/\.[^.]*$//;
266 $token =~ s/[^a-z\d]/_/gi;
270 $token =~ tr [A-Z] [a-z];
271 $token = 'foo' if ($token eq '');
273 my $format = 'GL_N3F_V3F';
274 my $primitive = 'GL_TRIANGLES';
278 $code .= "static const struct gllist frame={";
279 $code .= "$format,$primitive,$npoints,data,NULL};\n";
280 $code .= "const struct gllist *$token=&frame;\n";
282 print STDERR "$filename: " .
283 (($#triangles+1)*3) . " points, " .
284 (($#triangles+1)) . " faces.\n"
291 sub vrml_to_gl($$$) {
292 my ($infile, $outfile, $normalize_p) = @_;
296 if ($infile eq '-') {
299 open ($in, '<', $infile) || error ("$infile: $!");
301 my $filename = ($infile eq '-' ? "<stdin>" : $infile);
302 print STDERR "$progname: reading $filename...\n"
304 while (<$in>) { $body .= $_; }
307 $body =~ s/\r\n/\n/g; # CRLF -> LF
308 $body =~ s/\r/\n/g; # CR -> LF
310 my @triangles = parse_vrml ($filename, $body, $normalize_p);
312 $filename = ($outfile eq '-' ? "<stdout>" : $outfile);
313 my $code = generate_c ($filename, @triangles);
316 if ($outfile eq '-') {
319 open ($out, '>', $outfile) || error ("$outfile: $!");
321 (print $out $code) || error ("$filename: $!");
322 (close $out) || error ("$filename: $!");
324 print STDERR "$progname: wrote $filename\n"
325 if ($verbose || $outfile ne '-');
331 print STDERR "$progname: $_\n";
336 print STDERR "usage: $progname [--verbose] [infile [outfile]]\n";
341 my ($infile, $outfile);
343 while ($_ = $ARGV[0]) {
345 if ($_ eq "--verbose") { $verbose++; }
346 elsif (m/^-v+$/) { $verbose += length($_)-1; }
347 elsif ($_ eq "--normalize") { $normalize_p = 1; }
348 elsif (m/^-./) { usage; }
349 elsif (!defined($infile)) { $infile = $_; }
350 elsif (!defined($outfile)) { $outfile = $_; }
354 $infile = "-" unless defined ($infile);
355 $outfile = "-" unless defined ($outfile);
357 vrml_to_gl ($infile, $outfile, $normalize_p);