#!/usr/bin/perl use strict; use warnings; use FindBin; use lib "$FindBin::Bin/../lib"; use LDraw::Parser; use Getopt::Long; my $opts = {}; GetOptions( $opts, 'help', 'scale=s', 'ldrawdir=s', 'file=s', 'debug', 'json', 'nomodel', 'invert', 'cache', ); if (!keys %{$opts}) { print_usage(); exit 0; } if ($opts->{help}) { print_usage(); exit 0; } if (!$opts->{file}) { print "ERROR: --file is required! (try --help)\n"; exit 1; } sub print_usage { print < [--scale= --ldrawdir=/usr/share/ldraw] Takes an ldraw part .dat file as input and converts it into an STL file. --file The full path to the input .dat part file. Regardless of where this file is, sub-parts of the file will be searched in --ldrawdir. --scale Also scale the STL by N. This is separate from the LDU scaling that is used to convert internally from LDU (LDraw Unit) to mm (STL). --ldrawdir The location of the ldraw parts library package. Note: it is expected that this contains the directories "p", "parts" and "models". The Debian non-free package "ldraw-parts" installs to /usr/share/ldraw and that is the default value for this tool. --debug Print debugging messages to STDERR --json Dump the model as a json object in the form: {"normals":[],"vertexes":[]} Note: the surface normal of the triangle is duplicated for each of the 3 vertexes in the triangle, so these can be loaded into GL buffers and rendered with glDrawArrays. --nomodel Do not print the stl output. I am using this to run the script over all parts to try to detect issues. --invert Invert the part. Used for debugging. --cache Use a cache to avoid repeated geneneration of the same geometry. Many geometric primitives are repeated (eg: a stud), and are simply translated into different locations in the model. A combination of the sub-part name and the invert flag is used to build a cache key to store the generated triangles for the sub-part. When this is stable I will change this to --nocache so it's on by default. END } my $parser_opts = { file => $opts->{file}, }; if ($opts->{cache}) { $parser_opts->{cache} = LDraw::Parser::Cache->new; } if ($opts->{scale}) { $parser_opts->{scale} = $opts->{scale}; } if ($opts->{ldrawdir}) { $parser_opts->{ldraw_path} = $opts->{ldrawdir}; } if ($opts->{debug}) { $parser_opts->{debug} = 1; } if ($opts->{invert}) { $parser_opts->{invert} = 1; } my $parser = LDraw::Parser->new($parser_opts); $parser->parse; if ($opts->{json}) { my $data = $parser->gl_buffer; print '{"normals":['; print join(',', @{$data->{normals}}); print '],"vertexes":['; print join(',', @{$data->{vertexes}}); print ']}'; print "\n"; exit 0; } if ($opts->{nomodel}) { exit 0; } my $facets = $parser->stl_buffer; printf("solid GiantLegoRocks\n"); for my $facet (@{$facets}) { printf("facet normal %0.4f %0.4f %0.4f\n", @{$facet->{normal}}); printf(" outer loop\n"); for my $vertex (@{$facet->{vertexes}}) { printf(" vertex %0.4f %0.4f %0.4f\n", @{$vertex}); } printf(" endloop\n"); printf("endfacet\n"); } printf("endsolid GiantLegoRocks\n");