File Coverage

blib/lib/OpenGL/RWX.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package OpenGL::RWX;
2              
3             =pod
4              
5             =head1 NAME
6              
7             OpenGL::RWX - Provides support for loading 3D models from RWX files
8              
9             =head1 SYNOPSIS
10              
11             # Create the object but don't load anything
12             my $model = OpenGL::RWX->new(
13             file => 'mymodel.rwx',
14             );
15            
16             # Load the model into OpenGL
17             $model->init;
18            
19             # Render the model into the current scene
20             $model->display;
21              
22             =head1 DESCRIPTION
23              
24             B provides a basic implementation of a RWX file parser.
25              
26             Given a file name, it will load the file and parse the contents directly
27             into a compiled OpenGL display list.
28              
29             The OpenGL display list can then be executed directly from the RWX object.
30              
31             The current implementation is extremely preliminary and functionality will
32             be gradually fleshed out over time.
33              
34             In this initial test implementation, the model will only render as a set of
35             points in space using the pre-existing material settings.
36              
37             =cut
38              
39 2     2   70362 use 5.008;
  2         8  
  2         89  
40 2     2   12 use strict;
  2         4  
  2         81  
41 2     2   21 use warnings;
  2         4  
  2         83  
42 2     2   3004 use IO::File 1.14 ();
  2         42725  
  2         58  
43 2     2   19 use File::Spec 3.31 ();
  2         38  
  2         48  
44 2     2   2382 use OpenGL 0.64 ':all';
  0            
  0            
45             use OpenGL::List 0.01 ();
46              
47             our $VERSION = '0.02';
48              
49              
50              
51              
52              
53             ######################################################################
54             # Constructor and Accessors
55              
56             sub new {
57             my $class = shift;
58             my $self = bless { @_ }, $class;
59              
60             # Check param
61             my $file = $self->file;
62             unless ( -f $file ) {
63             die "RWX model file '$file' does not exists";
64             }
65              
66             # Texture cache
67             $self->{textures} = { };
68              
69             return $self;
70             }
71              
72             sub file {
73             $_[0]->{file}
74             }
75              
76             sub list {
77             $_[0]->{list};
78             }
79              
80              
81              
82              
83              
84             ######################################################################
85             # Main Methods
86              
87             sub display {
88             glCallList( $_[0]->{list} );
89             }
90              
91             sub init {
92             my $self = shift;
93             my $handle = IO::File->new( $self->file, 'r' );
94             $self->parse( $handle );
95             $handle->close;
96             return 1;
97             }
98              
99              
100              
101              
102              
103             ######################################################################
104             # Parsing Methods
105              
106             sub parse {
107             my $self = shift;
108             my $handle = shift;
109              
110             # Set up the (Perl) vertex array.
111             # The vertex list starts from position 1, so prepad a null
112             my @color = ( 0, 0, 0 );
113             my $ambient = 0;
114             my $diffuse = 1;
115             my $opacity = 1;
116             my @vertex = ( undef );
117             my $begin = undef;
118              
119             # Start the list context
120             $self->{list} = OpenGL::List::glpList {
121             # Start without texture support and reset specularity
122             glEnable( GL_LIGHTING );
123             glDisable( GL_TEXTURE_2D );
124             OpenGL::glMaterialf( GL_FRONT, GL_SHININESS, 128 );
125             OpenGL::glMaterialfv_p( GL_FRONT, GL_SPECULAR, 1, 1, 1, 1 );
126             OpenGL::glMaterialfv_p(
127             GL_FRONT,
128             GL_AMBIENT,
129             ( map { $_ * $ambient } @color ),
130             $opacity,
131             );
132             OpenGL::glMaterialfv_p(
133             GL_FRONT,
134             GL_DIFFUSE,
135             ( map { $_ * $diffuse } @color ),
136             $opacity,
137             );
138              
139             while ( 1 ) {
140             my $line = $handle->getline;
141             last unless defined $line;
142              
143             # Remove blank lines, trailing whitespace and comments
144             $line =~ s/\s*(?:#.+)[\012\015]*\z//;
145             $line =~ m/\S/ or next;
146              
147             # Parse the dispatch the line
148             my @words = split /\s+/, $line;
149             my $command = lc shift @words;
150             if ( $command eq 'vertex' or $command eq 'vertexext' ) {
151             # Only take the first three values, ignore any uv stuff
152             push @vertex, [ @words[0..2] ];
153              
154             } elsif ( $command eq 'color' ) {
155             @color = @words;
156             OpenGL::glMaterialfv_p(
157             GL_FRONT,
158             GL_AMBIENT,
159             ( map { $_ * $ambient } @color ),
160             $opacity,
161             );
162             OpenGL::glMaterialfv_p(
163             GL_FRONT,
164             GL_DIFFUSE,
165             ( map { $_ * $diffuse } @color ),
166             $opacity,
167             );
168              
169             } elsif ( $command eq 'ambient' ) {
170             $ambient = $words[0];
171             OpenGL::glMaterialfv_p(
172             GL_FRONT,
173             GL_AMBIENT,
174             ( map { $_ * $ambient } @color ),
175             $opacity,
176             );
177             OpenGL::glMaterialfv_p(
178             GL_FRONT,
179             GL_DIFFUSE,
180             ( map { $_ * $diffuse } @color ),
181             $opacity,
182             );
183              
184             } elsif ( $command eq 'diffuse' ) {
185             $diffuse = $words[0];
186             OpenGL::glMaterialfv_p(
187             GL_FRONT,
188             GL_AMBIENT,
189             ( map { $_ * $ambient } @color ),
190             $opacity,
191             );
192             OpenGL::glMaterialfv_p(
193             GL_FRONT,
194             GL_DIFFUSE,
195             ( map { $_ * $diffuse } @color ),
196             $opacity,
197             );
198              
199             } elsif ( $command eq 'triangle' ) {
200             # Switch to triangle drawing mode if needed
201             glEnd() if defined $begin;
202             glBegin( GL_TRIANGLES );
203             $begin = 'triangle';
204              
205             # Set the surface normal
206             my @v0 = @{$vertex[$words[0]]};
207             my @v1 = @{$vertex[$words[1]]};
208             my @v2 = @{$vertex[$words[2]]};
209             glNormal9f( @v0, @v1, @v2 );
210              
211             # Draw the triangle polygon
212             glVertex3f( @v0 );
213             glVertex3f( @v1 );
214             glVertex3f( @v2 );
215              
216             } elsif ( $command eq 'quad' ) {
217             # Switch to quad drawing mode if needed
218             glEnd() if defined $begin;
219             glBegin( GL_QUADS );
220             $begin = 'quad';
221              
222             # Set the surface normal
223             my @v0 = @{$vertex[$words[0]]};
224             my @v1 = @{$vertex[$words[1]]};
225             my @v2 = @{$vertex[$words[2]]};
226             glNormal9f( @v0, @v1, @v2 );
227              
228             # Draw the quad polygon
229             glVertex3f( @v0 );
230             glVertex3f( @v1 );
231             glVertex3f( @v2 );
232             glVertex3f( @{$vertex[$words[3]]} );
233              
234             } elsif ( $command eq 'protoend' ) {
235             # End of the prototype, end drawing.
236             glEnd() if defined $begin;
237              
238             # Reset state
239             @color = ( 0, 0, 0 );
240             $ambient = 0;
241             $diffuse = 1;
242             $opacity = 1;
243             @vertex = ( undef );
244             $begin = undef;
245              
246             # Reset material
247             OpenGL::glMaterialfv_p(
248             GL_FRONT,
249             GL_AMBIENT,
250             ( map { $_ * $ambient } @color ),
251             $opacity,
252             );
253             OpenGL::glMaterialfv_p(
254             GL_FRONT,
255             GL_DIFFUSE,
256             ( map { $_ * $diffuse } @color ),
257             $opacity,
258             );
259              
260             } else {
261             # Unsupported command, silently ignore
262             }
263             }
264              
265             # Terminate drawing mode if we're still in it
266             glEnd() if defined $begin;
267             };
268              
269             return 1;
270             }
271              
272             # Calculate a surface normal
273             sub glNormal9f {
274             my ($x0, $y0, $z0, $x1, $y1, $z1, $x2, $y2, $z2) = @_;
275              
276             # Calculate vectors A and B
277             my $xa = $x0 - $x1;
278             my $ya = $y0 - $y1;
279             my $za = $z0 - $z1;
280             my $xb = $x1 - $x2;
281             my $yb = $y1 - $y2;
282             my $zb = $z1 - $z2;
283              
284             # Calculate the cross product
285             my $xn = ($ya * $zb) - ($za * $yb);
286             my $yn = ($za * $xb) - ($xa * $zb);
287             my $zn = ($xa * $yb) - ($ya * $xb);
288              
289             # Normalise the cross product
290             my $l = sqrt( ($xn * $xn) + ($yn * $yn) + ($zn * $zn) ) || 1;
291             glNormal3f( $xn / $l, $yn / $l, $zn / $l );
292             }
293              
294             1;
295              
296             =pod
297              
298             =head1 SUPPORT
299              
300             Bugs should be reported via the CPAN bug tracker at
301              
302             L
303              
304             =head1 AUTHOR
305              
306             Adam Kennedy Eadamk@cpan.orgE
307              
308             =head1 SEE ALSO
309              
310             L
311              
312             =head1 COPYRIGHT
313              
314             Copyright 2010 Adam Kennedy.
315              
316             This program is free software; you can redistribute
317             it and/or modify it under the same terms as Perl itself.
318              
319             The full text of the license can be found in the
320             LICENSE file included with this module.
321              
322             =cut