File Coverage

blib/lib/OpenGL/QEng/Thing.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             ### $Id: Thing.pm 427 2008-08-19 18:40:46Z duncan $
2             ####------------------------------------------
3             ## @file Thing.pm
4             # Define Thing Class
5              
6             ## @class Thing
7             # Base class for all things in this universe
8             #
9             # Everything that can be seen is a thing
10              
11             package OpenGL::QEng::Thing;
12              
13 3     3   1358 use strict;
  3         5  
  3         92  
14 3     3   12 use warnings;
  3         4  
  3         77  
15 3     3   1426 use OpenGL qw/:all/;
  0            
  0            
16             use File::ShareDir;
17             use OpenGL::QEng::Event;
18             use OpenGL::QEng::TextureList;
19              
20             use base qw/OpenGL::QEng::OUtil/;
21              
22             use constant PI => 4*atan2(1,1); # 3.14159;
23             use constant RADIANS => PI/180.0;
24              
25             #------------------------------------------
26             # @cmethod % new()
27             # Create a Thing
28             #
29             sub new {
30             my ($class,@props) = @_;
31              
32             my $props = (scalar(@props) == 1) ? $props[0] : {@props};
33              
34             my $self =
35             {event => OpenGL::QEng::Event->new,
36             xsize => undef,
37             ysize => undef,
38             zsize => undef,
39             x => 0, # Thing current location x
40             y => 0, # Thing current location y
41             z => 0, # Thing current location z
42             roll => 0, # rotation about z axis
43             pitch => 0, # rotation about x axis
44             yaw => 0, # rotation about y axis
45             is_at => undef, # container of this Object
46             seen => 0, # this thing been seen by the team? y/n XXX?
47             texture => undef, # Texture image for this thing
48             GLid => undef,
49             state => undef,
50             event_script => undef,
51             event_code => {},
52             near_script => undef,
53             near_code => undef,
54             range => undef,
55             wrap_class => undef,
56             target => {},
57             eye_magnet => 0,
58             holder => 0,
59             visible => 1,
60             store_at => {x=>0, y=>0.01, z=>0, roll=>0, pitch=>0, yaw=>0},
61             holds => undef,
62             parts => undef,
63             name => undef,
64             no_events => undef,
65             };
66             bless($self,$class);
67              
68             $self->passedArgs($props) if keys %$props;
69             $self->create_accessors;
70             $self->claim_GLid;
71             $self->register_events;
72             $self;
73             }
74              
75             #--------------------------------------------------
76             sub register_events {
77             my ($self) = @_;
78              
79             return if $self->no_events;
80             if (defined $self->event_script) {
81             for my $event (keys %{$self->event_script}) {
82             unless (ref($self->event_code->{$event})) {
83             my $cmdTxt = '$self->{event_code}{$event} = '
84             .$self->event_script->{$event};
85             eval $cmdTxt;
86             if ($@) {
87             print STDERR "EVAL ($cmdTxt) FAILED: $@\n";
88             next;
89             }
90             }
91             $self->event->callback($self,$event,$self->event_code->{$event});
92             }
93             }
94             if (defined $self->near_script) {
95             unless (ref $self->near_code) {
96             my $cmdTxt = '$self->{near_code} = ' .$self->near_script;
97             eval $cmdTxt;
98             if ($@) {
99             print STDERR "EVAL ($cmdTxt) FAILED: $@\n";
100             }
101             }
102             $self->{event}->callback($self,'team_at',\&handle_near);
103             }
104             if (defined $self->name) {
105             $self->{event}->callback($self,'who_is',
106             sub {
107             my ($self,$stash,$obj,$ev,$name,@args) = @_;
108             $self->send_event('i_am',$name)
109             if $self->name eq $name;
110             });
111             }
112             }
113              
114             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
115             {my $GLid_serial = 1000; # GLid starting number
116             my %GLid2objref;
117              
118             #--------------------
119             sub find_thing_by_GLid {
120             my ($class, $GLid) = @_;
121             $GLid2objref{$GLid};
122             }
123              
124             #--------------------
125             sub claim_GLid {
126             my ($self) = @_;
127             die "$self -> sub claim_GLid(): no GLid method" unless $self->can('GLid');
128             if (defined $self->GLid) {
129             warn "$self already has GLid $self->{GLid}";
130             } else {
131             $self->{GLid} = $GLid_serial++;
132             }
133             $GLid2objref{$self->GLid} = $self;
134             }
135             } #end closure - - - - - - - - - - - - - - - - - - - - - - -
136              
137             #-----------------------------------------------------------------
138             sub make_me_nod {
139             my ($self) = @_;
140             $self->eye_magnet || 0;
141             }
142              
143             #-----------------------------------------------------------------
144             sub can_hold {
145             my ($self,$thing) = @_;
146             $self->holder || 0;
147             }
148              
149             #-----------------------------------------------------------------
150             sub special_parts {} #no parts are special by default
151              
152             #------------------------------------------
153             ## @method draw($mode)
154             # Draw this object in its current state at its current location
155             # or set up for testing for a touch
156             sub draw {
157             die join(':', caller),' called Thing::draw()';
158             }
159              
160             #---------------------------------------------------------
161             ##@method contains(@things)
162             #
163             #With array arg, add all to things contained by the current thing
164             #Always return things contained by current thing
165             #
166             sub contains {
167             my ($self,@things) = @_;
168              
169             die "contains($self,@things) from ",join(':',caller)," " if @things;
170              
171             my ($p,$h) = ($self->parts || [],$self->holds || []);
172             return [@$p,@$h];
173             }
174              
175             #--------------------------
176             ## @method assimilate($thing)
177             # make $thing a part of $self
178             #
179             sub assimilate {
180             my ($self,$thing) = @_;
181              
182             return unless defined($thing);
183             push @{$self->{parts}}, $thing;
184             $thing->is_at($self);
185             $thing->invalidate_map_view;
186             }
187              
188             #--------------------------
189             ## @method put_thing($thing)
190             #put arg (a thing instance) into the current thing
191             sub put_thing {
192             my ($self,$thing,$store) = @_;
193              
194             return unless defined($thing);
195             die "put_thing($self,$thing) from ",join(':',caller)," " unless $store;
196              
197             push @{$self->{holds}}, $thing;
198             $thing->is_at($self);
199             $thing->invalidate_map_view;
200             $self->invalidate_map_view($thing); #XXX ??? test
201              
202             if (defined(my $store_location = $self->store_at)) {
203             $thing->x ($store_location->{x}) if defined $store_location->{x};
204             $thing->y ($store_location->{y}) if defined $store_location->{y};
205             $thing->z ($store_location->{z}) if defined $store_location->{z};
206             $thing->roll ($store_location->{roll}) if defined $store_location->{roll};
207             $thing->pitch($store_location->{pitch})if defined $store_location->{pitch};
208             $thing->yaw ($store_location->{yaw}) if defined $store_location->{yaw};
209             }
210             }
211              
212             #--------------------------
213             ## @method take_thing($desired_thing)
214             #remove arg (a thing instance) from the current thing and return it
215             sub take_thing {
216             my ($self,$desired_thing) = @_;
217              
218             return unless defined $desired_thing;
219             my $returned_thing;
220             my @things;
221             while (my $thing = shift(@{$self->{holds}})) {
222             if ($thing eq $desired_thing) {
223             $returned_thing = $thing;
224             } else {
225             push(@things,$thing);
226             }
227             }
228             $self->{holds} = \@things;
229             unless (defined $returned_thing) {
230             print STDERR "$desired_thing not found in $self\n";
231             return;
232             }
233             $returned_thing->is_at->invalidate_map_view($returned_thing);
234             $returned_thing;
235             }
236              
237             #--------------------------
238             sub excise {
239             my ($self,$thing) = @_;
240              
241             return unless defined $thing;
242             my $returned_thing;
243             my @things = @{$self->{parts}};
244             $self->{parts} = [];
245             while (my $t = shift @things) {
246             if ($t == $thing) {
247             $returned_thing = $t;
248             } else {
249             push @{$self->{parts}}, $t;
250             }
251             }
252             unless (defined $returned_thing) {
253             print STDERR "$thing not found in $self\n";
254             return;
255             }
256             $returned_thing->is_at->invalidate_map_view($returned_thing)
257             if defined $returned_thing->is_at;
258             $returned_thing;
259             }
260              
261             #-------------------------------------
262             ## @method send_event(%event)
263             #signal an event
264             sub send_event {
265             $_[0]->{event}->yell(@_)
266             }
267              
268             #------------------------------------------
269             sub printArray {
270             my ($aref) = @_;
271              
272             print STDOUT "[";
273             for my $i (@$aref) {
274             if (ref($i) eq '') {
275             printScalar($i);
276             }
277             elsif (ref($i) eq 'ARRAY') {
278             printArray($i);
279             }
280             elsif (ref($i) eq 'HASH') {
281             printHash($i);
282             }
283             else {
284             warn "printArray won't do '$i' because it is a ", ref $i,
285             " caller ",join(':',caller);
286             }
287             }
288             print STDOUT "],";
289             }
290              
291             #------------------------------------------
292             sub printHash {
293             my ($href) = @_;
294              
295             print STDOUT "{";
296             for my $k (keys %$href) {
297             print STDOUT "$k=>";
298             if (ref($href->{$k}) eq '') {
299             printScalar($href->{$k});
300             }
301             elsif (ref($href->{$k}) eq 'ARRAY') {
302             printArray($href->{$k});
303             }
304             elsif (ref($href->{$k}) eq 'HASH') {
305             printHash($href->{$k});
306             }
307             else {
308             warn "printHash won't do '$href->{$k}' because it is a ",
309             ref $href->{$k};
310             }
311             }
312             print STDOUT "},";
313             }
314              
315             #------------------------------------------
316             sub printScalar {
317             my ($s) = @_;
318              
319             #return unless defined $s;
320             if (! defined $s) {
321             print STDOUT 'undef,';
322             }
323             elsif (ref($s) eq '') {
324             no warnings 'numeric';
325             if ($s eq 0+$s) { #num
326             print STDOUT "$s,";
327             }
328             elsif ((index($s,"\n") == -1) and (index($s,"'") == -1)) { #q
329             print STDOUT "'$s',";
330             }
331             else { #qq
332             $s =~ s/\n/\\n/g;
333             $s =~ s/"/\\"/g;
334             print STDOUT "\"$s\",";
335             }
336             }
337             else {
338             warn "printScalar won't do '$s' because it is a ", ref $s;
339             }
340             }
341              
342             #------------------------------------------
343             {;
344             my %deflt_cache;
345              
346             sub not_default {
347             my ($self) = @_;
348              
349             unless (exists $deflt_cache{ref $self}) {
350             $deflt_cache{ref $self} = ref($self)->new(no_events=>1);
351             }
352             my $dflt = $deflt_cache{ref $self};
353              
354             my $href = {};
355             for my $key (keys %{$self}) {
356             if (!defined($dflt->{$key})) {
357             $href->{$key} = $self->{$key};
358             }
359             elsif (defined $self->{$key}) {
360             $href->{$key} = $self->{$key}
361             if (ref2str($self->{$key}) ne ref2str($dflt->{$key}));
362             }
363             }
364             $href;
365             }
366             } # end closure
367              
368             #------------------------------------------
369             sub boring_stuff {
370             {x => 1,
371             z => 1,
372             yaw => 1,
373             GLid => 1,
374             event => 1,
375             is_at => 1,
376             holds => 1,
377             parts => 1,
378             map_view => 1,
379             range_2 => 1,
380             near_code => 1,
381             event_code=> 1,
382             wrap_class=> 1,
383             objects => 1,
384             tlines => 1,
385             }
386             }
387              
388             #------------------------------------------
389             sub printMe {
390             my ($self,$depth) = @_;
391              
392             $depth ||= 0;
393             my $started = 0;
394             for my $sp ($self->special_parts) {
395             next unless defined $self->{$sp};
396             unless ($started) {
397             print STDOUT ' 'x$depth,"partof_next;\n" unless $started;
398             $started = 1;
399             }
400             $self->{$sp}->{name} =
401             'XX'.$self->{$sp} unless defined($self->{$sp}->{name});
402             $self->{$sp}->printMe($depth+1);
403             }
404             print STDOUT ' 'x$depth,"done;\n" if $started;
405              
406             (my $map_ref = ref $self) =~ s/OpenGL::QEng:://;
407             print STDOUT ' 'x$depth,"$map_ref $self->{x} $self->{z} $self->{yaw}";
408             my $spec = $self->not_default;
409             my $boring = $self->boring_stuff;
410             for my $key (keys %{$spec}) {
411             next unless defined $spec->{$key};
412             next if defined $boring->{$key};
413              
414             if (ref($spec->{$key}) eq '') {
415             print STDOUT " $key=>";
416             printScalar($spec->{$key});
417             }
418             elsif (ref($spec->{$key}) eq 'ARRAY') {
419             next unless @{$spec->{$key}};
420             print STDOUT " $key=>";
421             printArray($spec->{$key});
422             }
423             elsif (ref($spec->{$key}) eq 'HASH') {
424             next unless keys %{$spec->{$key}};
425             print STDOUT " $key=>";
426             printHash($spec->{$key});
427             }
428             else {
429             warn "$self ->printMe won't do '$key' because it is a ",
430             ref $spec->{$key};
431             }
432             }
433             for my $sp ($self->special_parts) {
434             print STDOUT " $sp=>{named=>'$self->{$sp}->{name}'},"
435             if defined($self->{$sp});
436             }
437             print STDOUT ";\n";
438              
439             my @parts = @{$self->parts} if $self->parts;
440             $started = 0;
441             PART:
442             for my $thing (@parts) {
443             next if exists $thing->{i_am_a_wall_chunk};
444             for my $sp ($self->special_parts) {
445             next PART if ((!$self->{$sp}) || $thing eq $self->{$sp});
446             }
447             unless ($started) {
448             print STDOUT ' 'x$depth,"partof_last;\n" unless $started;
449             $started = 1;
450             }
451             $thing->printMe($depth+1);
452             }
453             print STDOUT ' 'x$depth,"done;\n" if $started;
454              
455             my @holds = @{$self->holds} if $self->holds;
456             $started = 0;
457             for my $thing (@holds) {
458             unless ($started) {
459             print STDOUT ' 'x$depth,"in_last;\n" unless $started;
460             $started = 1;
461             }
462             $thing->printMe($depth+1);
463             }
464             print STDOUT ' 'x$depth,"done;\n" if $started;
465             }
466              
467             #-----------------------------------------------------------
468             ## @method handle_touch()
469             #default touch handler method for Things
470             #
471             sub handle_touch {
472             return unless defined($ENV{WIZARD});
473             my $where = $_[0]->is_at || 'undef';
474             print STDERR "Thing::handle_touch(",join(',',@_),")\n";
475             print STDERR "--\t$_[0] is_at: $where\n";
476             }
477              
478             #--------------------------------------------------
479             sub handle_near {
480             my ($self,$stash,$obj,$ev,$tx,$tz,$currmap,@args) = @_;
481             warn 'handle_near: undefined currmap' unless defined $currmap;
482              
483             return unless defined $self->range;
484             my $range = $self->range;
485             $self->range(undef); # poor man's exclusion lock
486              
487             if (defined($self->near_code) && $self->is_at eq $currmap) {
488             my $distSq = ($self->x-$tx)*($self->x-$tx)+($self->z-$tz)*($self->z-$tz);
489             $self->{range_2} ||= $range*$range;
490             $self->near_code->($distSq) if ($distSq <= $self->{range_2});
491             }
492              
493             $self->range($range); # unlock me
494             }
495              
496             #------------------------------------------------------------
497             sub tractable { # tractability - 'solid', 'seethru', 'passable'
498             return 'passable';
499             }
500              
501             #-------------------------------------
502             sub color_me_gone {
503             my $self = shift;
504              
505             my $where_am_i = $self->is_at();
506             $where_am_i->take_thing($self);
507             }
508              
509             #-----------------------------------------------------------
510             ## @method unlock($self,$team)
511             #Attempt to unlock this Thing. Test that the team is using the matching key.
512             #Provide helpful feed back to the game player.
513             #
514             sub unlock {
515             my ($self,$team_holds) = @_;
516              
517             my ($unlocker) = ($self->key or $self->opener or '(undef)');
518             my $try_key =
519             (ref($team_holds) eq 'OpenGL::QEng::Key') ? $team_holds->type : ref($team_holds);
520             $try_key =~ s/OpenGL::QEng:://;
521              
522             if ($try_key eq $unlocker) {# success
523             $self->state('closed');
524             #$self->send_event('state');
525             if (defined $self->opener) {
526             $self->send_event('msg',"'Using the $try_key, frees the door'\n");
527             } else {
528             $self->send_event('msg',"The $try_key key turns in the lock\n",
529             "'Click'\n");
530             }
531             return $team_holds;
532             } else { # failure
533             print "Locked/Stuck tryed: $try_key, need: $unlocker\n"
534             if ($ENV{WIZARD});
535             if (defined $self->opener) { # need opener and have nothing
536             # or need opener and have wrong thing
537             $self->send_event('msg',"Stuck\n");
538             }
539             else {
540             if (ref($team_holds) eq 'OpenGL::QEng::Key') { # need key and have wrong key
541             $self->send_event('msg',"The $try_key key doesn't fit.\n");
542             }
543             elsif ($try_key) { # need key and have something else
544             $self->send_event('msg',"A $try_key won't unlock it.\n");
545             }
546             else { # need key and don't have anything
547             $self->send_event('msg',"Locked\n");
548             }
549             }
550             return 0;
551             }
552             }
553              
554             #--------------------------------------------------
555             sub model {
556             die "bad arg @_ from ",join(':',caller) if defined $_[1] && !ref $_[1];
557             die "$_[0] has no model in hash ",join(':',caller)
558             unless exists $_[0]->{model};
559             return unless exists $_[0]->{model};
560             $_[0]->{model} = $_[1] if defined $_[1];
561             $_[0]->{model};
562             }
563              
564             #---------------------------------------------------------
565             sub get_corners {
566             my ($self) = @_;
567              
568             my $corners = [];
569             return $corners unless $self->visible;
570              
571             my $color = ($self->seen) ? $self->color || 'black' : undef;
572             $color = $color->[0] if ref $color eq 'ARRAY';
573             my $tract = $self->tractable;
574             die "oops: $self" unless $tract;
575              
576             die 'bad model' unless (ref $self->{model} eq 'HASH');
577             my ($minx, $maxx) = ($self->{model}{minx}, $self->{model}{maxx});
578             my ($miny, $maxy) = ($self->{model}{miny}, $self->{model}{maxy});
579             my ($minz, $maxz) = ($self->{model}{minz}, $self->{model}{maxz});
580              
581             if (defined($minx) && defined($maxx) && defined($minz) && defined($maxz)) {
582             if (($self->y+$miny) < 1 && ($self->y+$maxy) > 0) {
583             push @$corners,
584             [$minx, $minz, $minx, $maxz, $color, $tract, $self],
585             [$minx, $maxz, $maxx, $maxz, $color, $tract, $self],
586             [$maxx, $maxz, $maxx, $minz, $color, $tract, $self],
587             [$maxx, $minz, $minx, $minz, $color, $tract, $self];
588             }
589             #XXX later this test should be modified by the team current y
590             }
591             $corners;
592             }
593              
594             #-----------------------------------------------------------
595             sub find_objects {
596             my ($self) = @_;
597              
598             unless (defined $self->{objects}) {
599             my $yaw = $self->yaw;
600             my ($selfX,$selfY,$selfZ) = ($self->x,$self->y,$self->z);
601             die "$self missing prereqs: $yaw,$selfX,$selfZ"
602             unless defined($yaw) && defined($selfX) && defined($selfZ);
603              
604             my $objects;
605             for my $obj (@{$self->contains}) {
606             for my $list ($obj->find_objects) {
607             die "poor map view from $obj: list=$list\n"
608             unless defined $list && ref($list) eq 'ARRAY';
609             push @$objects, @$list;
610             }
611             }
612             push @{$self->{objects}}, [$self->x,$self->y,$self->z,$self];
613             for my $line (@$objects) {
614             push @{$self->{objects}},
615             [$selfX+cos($yaw*RADIANS)*$line->[0]+sin($yaw*RADIANS)*$line->[2],
616             $selfY+$line->[1],
617             $selfZ-sin($yaw*RADIANS)*$line->[0]+cos($yaw*RADIANS)*$line->[2],
618             $line->[3]];
619             }
620             }
621             return $self->{objects};
622             }
623              
624             #-----------------------------------------------------------
625             ## @method @ get_map_view()
626             # Get the location of this object and
627             # a color reflecting if the object has been seen yet
628             sub get_map_view {
629             my $self = shift;
630              
631             unless (defined $self->{map_view}) {
632             my $yaw = $self->yaw;
633             my ($selfX,$selfY,$selfZ) = ($self->x,$self->y,$self->z);
634             die "$self missing prereqs: $yaw,$selfX,$selfZ"
635             unless defined($yaw) && defined($selfX) && defined($selfZ);
636              
637             my @corners = @{$self->get_corners};
638              
639             for my $obj (@{$self->contains}) {
640             my %parts;
641             for my $line ($obj->get_map_view) {
642             die "poor map view from $obj:"
643             unless defined $line && ref($line) eq 'ARRAY' && @$line >= 4;
644             push @corners,[$line->[0],$line->[1],$line->[2],$line->[3],
645             $line->[4],$line->[5],$line->[6]];
646             $parts{$line->[6]} = $line->[6];
647             }
648             for my $p (keys %parts) { delete($parts{$p}->{tlines}) }
649             }
650              
651             my @view;
652             for my $line (@corners) {
653             my $tline =
654             [$selfX+cos($yaw*RADIANS)*$line->[0]+sin($yaw*RADIANS)*$line->[1],
655             $selfZ-sin($yaw*RADIANS)*$line->[0]+cos($yaw*RADIANS)*$line->[1],
656             $selfX+cos($yaw*RADIANS)*$line->[2]+sin($yaw*RADIANS)*$line->[3],
657             $selfZ-sin($yaw*RADIANS)*$line->[2]+cos($yaw*RADIANS)*$line->[3],
658             $line->[4],$line->[5],$line->[6]];
659             push @view, $tline;
660             push @{$line->[6]->{tlines}}, $tline;
661             }
662             $self->{map_view} = \@view;
663             }
664             return @{$self->{map_view}};
665             }
666              
667             #----------------------------------------------------------------------
668             sub invalidate_map_view {
669             my ($self,$thing) = @_;
670              
671             undef $self->{map_view};
672             undef $self->{objects} if ref $thing;
673              
674             $self->is_at->invalidate_map_view($thing)
675             if defined $self->is_at && $self->is_at->can('invalidate_map_view');
676             (print STDERR "$self has no home\n",return) unless defined $self->is_at;
677             print STDERR "${self}'s home can't invalidate\n" unless $self->is_at->can('invalidate_map_view');
678             }
679              
680             #------------------------------------------
681             ## @method move()
682             # Step the animation
683             sub move {
684             my ($self) = @_;
685              
686             my $need_redraw = 0;
687             if (values %{$self->target}) {
688             my %quantum = (x=>.2, y=>.2, z=>.2,
689             roll=>2, pitch=>2, yaw=>2,
690             opening=>2, levang=>2);
691             for my $attr (keys %quantum) {
692             if (defined $self->{target}{$attr}) {
693             die "oops: $attr" unless defined $self->{$attr};
694             if ($self->{$attr} == $self->{target}{$attr}) {
695             undef $self->{target}{$attr};
696             }
697             else {
698             my $delta = abs($self->{$attr} - $self->{target}{$attr});
699             if ($delta <= $quantum{$attr}) {
700             $self->{$attr} = $self->{target}{$attr};
701             undef $self->{target}{$attr};
702             } elsif ($self->{target}{$attr} > $self->{$attr}) {
703             $self->{$attr} += $quantum{$attr};
704             } else {
705             $self->{$attr} -= $quantum{$attr};
706             }
707             $need_redraw++;
708             $self->invalidate_map_view($self);
709             }
710             }
711             }
712             }
713             if ($self->isa('OpenGL::QEng::Team')) {
714             $self->send_event('team_at',$self->x,$self->z,$self->is_at)
715             if $need_redraw;
716             }
717             elsif ($self->contains) {
718             foreach my $o (@{$self->contains}) {
719             $o->move;
720             }
721             }
722             $self->send_event('need_redraw') if $need_redraw;
723             }
724              
725             #----------------------------------------------------
726             {;
727             my $textList;
728              
729             ## @method $ pickTexture($key)
730             # Set the texture from a texture name string
731             sub pickTexture {
732             my ($self,$key) = @_;
733              
734             unless (defined $textList) {
735             my $idir = File::ShareDir::dist_dir('Games-Quest3D');
736             $idir .= '/images';
737             $textList = OpenGL::QEng::TextureList->new($idir);
738             }
739             $textList->pickTexture($key);
740             }
741             }
742              
743             #-------------------------------------
744             ## select a color by name
745              
746             {;# @map_item Current colors are:
747             my %colors;
748              
749             sub make_color_map {
750             %colors = ('blue' =>[0.0,0.0,1.0],
751             'purple' =>[160.0/255.0, 23.0/255.0, 240.0/255.0],
752             'pink' =>[1.0,192.0/255.0,203.0/255.0],
753             'red' =>[1.0,0.0,0.0],
754             'magenta' =>[1.0,0.0,1.0],
755             'yellow' =>[1.0,1.0,0.0],
756             'white' =>[1.0,1.0,1.0],
757             'cyan' =>[0.0,1.0,1.0],
758             'green' =>[0.0,1.0,0.0],
759             'beige' =>[245.0/255.0,245.0/255.0,135.0/255.0],
760             'brown' =>[141.0/255.0, 76.0/255.0, 47.0/255.0],
761             'orange' =>[255.0/255.0,165.0/255.0,0.0/255.0],
762             'gold' =>[255.0/255.0,215.0/255.0,0.0/255.0],
763             'gray' =>[64.0/255.0,64.0/255.0,64.0/255.0],
764             'gray75' =>[191.0/255.0,191.0/255.0,191.0/255.0],
765             'slate gray'=>[112.0/255.0,128.0/255.0,144.0/255.0],
766             'darkgray' =>[47.0/255.0,79.0/255.0,79.0/255.0],
767             'medgray' =>[192.0/255.0,192.0/255.0,192.0/255.0],
768             'lightgray'=>[211.0/255.0,211.0/255.0,211.0/255.0],
769             'black' =>[0.0,0.0,0.0],
770             'cream' =>[250.0/255.0,240.0/255.0,230.0/255.0],
771             'light green' =>[144.0/255.0,238.0/255.0,144.0/255.0],
772             'light blue' =>[173.0/255.0,216.0/255.0,230.0/255.0],
773             );
774             my $path = 'rgb.txt';
775             for my $p ('/etc/X11/rgb.txt',
776             '/usr/share/X11/rgb.txt',
777             '/usr/X11R6/lib/X11/rgb.txt',
778             '/usr/openwin/lib/X11/rgb.txt',
779             ) {
780             ($path=$p, last) if -f $p;
781             }
782             if (open my $rgb,'<',$path) {
783             while (my $line = <$rgb>) {
784             my ($r,$g,$b,$name);
785             next unless ($r,$g,$b,$name) =
786             $line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\w.*\w)\s*$/;
787             $colors{lc $name} = [$r/255.0,$g/255.0,$b/255.0,];
788             }
789             close $rgb;
790             }
791             }
792              
793             #-------------------------------------
794             ## @method setColor($color)
795             # set the color from a text name
796             sub setColor {
797             my ($self,$color) = @_;
798              
799             die "setColor($self,) c.f. ",join(':',caller),"\n" unless $color;
800             make_color_map() unless $colors{red};
801             $color = lc $color;
802             if ($color eq 'clear'){
803             glColor4f(0.0,0.0,0.0,1.0);
804             } elsif (defined($colors{$color})) {
805             glColor4f($colors{$color}[0],$colors{$color}[1],$colors{$color}[2],1.0);
806             } else {
807             print "unknown color $color\n";
808             }
809             }
810              
811             #-------------------------------------
812             ## @method @ getColor($color)
813             # get the color value triplet from a text name
814             sub getColor {
815             my ($self,$color) = @_;
816              
817             die "getColor($self,) c.f. ",join(':',caller),"\n" unless $color;
818             make_color_map() unless $colors{red};
819             $color = lc $color;
820             if (defined $colors{$color}) {
821             return @{$colors{$color}};
822             }
823             print "unknown color $color\n";
824             }
825             } # end closure
826              
827             #------------------------------------------
828             ## @method $ tErr
829             # print any pending OpenGL error
830             sub tErr { return; #XXX for timing
831             my ($self,$w) = @_;
832              
833             while (my $e = glGetError()) {
834             print "$e, ",gluErrorString($e)," \@:$w\n";
835             }
836             }
837              
838             #-------------------------------------
839             {my $dlRoot = 1;
840             sub getDLname {
841             $dlRoot++;
842             }
843             }
844              
845             #------------------------------------------
846             sub ref2str {
847             my ($ref) = @_;
848              
849             if (ref($ref) eq 'ARRAY') {
850             return aref2str($ref);
851             }
852             elsif (ref($ref) eq 'HASH') {
853             return href2str($ref);
854             }
855             elsif (! defined $ref) {
856             return 'undef';
857             }
858             else {
859             return $ref;
860             }
861             }
862              
863             #------------------------------------------
864             sub aref2str {
865             my ($aref) = @_;
866              
867             my $str = '[';
868             for my $i (@$aref) {
869             #next unless defined $i;
870             if (ref($i) eq 'ARRAY') {
871             $str .= aref2str($i);
872             }
873             elsif (ref($i) eq 'HASH') {
874             $str .= href2str($i);
875             }
876             elsif (! defined $i) {
877             $str .= 'undef,';
878             }
879             else {
880             $str .= $i.',';
881             }
882             }
883             $str .= '],';
884             }
885              
886             #------------------------------------------
887             sub href2str {
888             my ($href) = @_;
889              
890             my $str = '{';
891             for my $k (keys %$href) {
892             #next unless defined $href->{$k};
893             $str .= "$k=>";
894             if (ref($href->{$k}) eq 'ARRAY') {
895             $str .= aref2str($href->{$k});
896             }
897             elsif (ref($href->{$k}) eq 'HASH') {
898             $str .= href2str($href->{$k});
899             }
900             elsif (! defined $href->{$k}) {
901             $str .= 'undef,';
902             }
903             else {
904             $str .= $href->{$k}.',';
905             }
906             }
907             $str .= '},';
908             }
909              
910             #==================================================================
911             ###
912             ### Test Driver for Thing Objects
913             ###
914             if (not defined caller()) {
915             package main;
916             #require Data::Dumper;
917              
918             my $v = OpenGL::QEng::Thing->new;
919             warn $v;
920             $v->printMe;
921             #print '$v is',Dumper($v),"\n";
922             }
923              
924             1;
925              
926             __END__