ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC.pm
Revision: 1.60
Committed: Tue May 23 23:14:44 2006 UTC (17 years, 11 months ago) by root
Branch: MAIN
Changes since 1.59: +15 -1 lines
Log Message:
rig up primitive and still buggy pod/help viewer, add intro document

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3 root 1.22 CFClient - undocumented utility garbage for our crossfire client
4 root 1.1
5     =head1 SYNOPSIS
6    
7 root 1.22 use CFClient;
8 root 1.1
9     =head1 DESCRIPTION
10    
11     =over 4
12    
13     =cut
14    
15 root 1.22 package CFClient;
16 root 1.1
17     BEGIN {
18     $VERSION = '0.1';
19    
20 root 1.2 use XSLoader;
21 root 1.22 XSLoader::load "CFClient", $VERSION;
22 root 1.1 }
23    
24 root 1.43 use Carp ();
25 root 1.52 use AnyEvent ();
26 root 1.34 use BerkeleyDB;
27 root 1.52
28 root 1.41 use CFClient::OpenGL;
29 root 1.26
30 root 1.19 our %GL_EXT;
31     our $GL_VERSION;
32    
33     our $GL_NPOT;
34 root 1.49 our $GL_DEBUG = 1;
35 root 1.19
36     sub gl_init {
37     $GL_VERSION = gl_version * 1;
38     %GL_EXT = map +($_ => 1), split /\s+/, gl_extensions;
39    
40     $GL_NPOT = $GL_EXT{GL_ARB_texture_non_power_of_two} || $GL_VERSION >= 2;
41 elmex 1.59 $GL_NPOT = 0 if gl_vendor =~ /ATI Technologies/; # ATI doesn't get it right...
42 root 1.19
43 root 1.49 glDisable GL_COLOR_MATERIAL;
44 root 1.26 glShadeModel GL_FLAT;
45 root 1.49 glDisable GL_DITHER;
46 root 1.26 glDisable GL_DEPTH_TEST;
47 root 1.57 glDepthMask 0;
48 root 1.26 glHint GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST;
49    
50 root 1.22 CFClient::Texture::restore_state ();
51 root 1.19 }
52    
53 root 1.49 sub gl_check {
54     return unless $GL_DEBUG;
55    
56     if (my $error = glGetError) {
57 root 1.56 Carp::cluck sprintf "opengl error %x while %s", $error, &sprintf(@_);
58 root 1.49 }
59     }
60    
61 root 1.5 sub find_rcfile($) {
62     my $path;
63    
64 root 1.46 for (grep !ref, @INC) {
65 root 1.22 $path = "$_/CFClient/resources/$_[0]";
66 root 1.5 return $path if -r $path;
67     }
68    
69     die "FATAL: can't find required file $_[0]\n";
70     }
71    
72     sub read_cfg {
73     my ($file) = @_;
74    
75     open CFG, $file
76     or return;
77    
78     my $CFG;
79    
80     local $/;
81     $CFG = eval <CFG>;
82    
83     $::CFG = $CFG;
84    
85     close CFG;
86     }
87    
88     sub write_cfg {
89     my ($file) = @_;
90    
91     open CFG, ">$file"
92     or return;
93    
94     {
95 elmex 1.9 require Data::Dumper;
96 root 1.5 local $Data::Dumper::Purity = 1;
97     $::CFG->{VERSION} = $::VERSION;
98     print CFG Data::Dumper->Dump ([$::CFG], [qw/CFG/]);
99     }
100    
101     close CFG;
102     }
103    
104 root 1.34 mkdir "$Crossfire::VARDIR/pclient", 0777;
105    
106     our $DB_ENV = new BerkeleyDB::Env
107     -Home => "$Crossfire::VARDIR/pclient",
108     -Cachesize => 1_000_000,
109 root 1.39 -ErrFile => "$Crossfire::VARDIR/pclient/errorlog.txt",
110     # -ErrPrefix => "DATABASE",
111 root 1.34 -Verbose => 1,
112 root 1.58 -Flags => DB_CREATE | DB_RECOVER | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN,
113 root 1.34 or die "unable to create/open database home $Crossfire::VARDIR/pclient: $BerkeleyDB::Error";
114    
115     sub db_table($) {
116 root 1.38 my ($table) = @_;
117    
118     $table =~ s/([^a-zA-Z0-9_\-])/sprintf "=%x=", ord $1/ge;
119    
120 root 1.34 new CFClient::Database
121     -Env => $DB_ENV,
122 root 1.38 -Filename => $table,
123     # -Filename => "database",
124     # -Subname => $table,
125 root 1.51 -Property => DB_CHKSUM,
126 root 1.34 -Flags => DB_CREATE | DB_UPGRADE,
127     or die "unable to create/open database table $_[0]: $BerkeleyDB::Error";
128     }
129    
130 root 1.52 sub pod_to_pango($) {
131     my ($pom) = @_;
132    
133     $pom->present ("CFClient::PodToPango")
134     }
135    
136 root 1.60 sub pod_to_pango_list($) {
137     my ($pom) = @_;
138    
139     [
140     map s/^(\s*)// && [40 * length $1, length $_ ? $_ : " "],
141     split /\n/, $pom->present ("CFClient::PodToPango")
142     ]
143     }
144    
145 root 1.52 package CFClient::PodToPango;
146    
147     use base Pod::POM::View::Text;
148    
149     our $indent = 0;
150    
151     *view_seq_code =
152     *view_seq_bold = sub { "<b>$_[1]</b>" };
153     *view_seq_italic = sub { "<i>$_[1]</i>" };
154     *view_seq_space =
155     *view_seq_link =
156     *view_seq_index = sub { CFClient::UI::Label::escape ($_[1]) };
157    
158     sub view_seq_text {
159     my $text = $_[1];
160     $text =~ s/\s+/ /g;
161     CFClient::UI::Label::escape ($text)
162     }
163    
164     sub view_item {
165     ("\t" x ($indent / 4))
166     . $_[1]->title->present ($_[0])
167     . "\n"
168     . $_[1]->content->present ($_[0])
169     }
170    
171 root 1.53 sub view_verbatim {
172     (join "",
173     map +("\t" x ($indent / 2)) . "$_\n",
174     split /\n/, CFClient::UI::Label::escape ($_[1]))
175     . "\n"
176     }
177    
178 root 1.52 sub view_textblock {
179     ("\t" x ($indent / 2)) . "$_[1]\n\n"
180     }
181    
182 root 1.60 sub view_head1 {
183     "<span foreground='#ffff00' size='x-large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
184     . $_[1]->content->present ($_[0])
185     };
186    
187 root 1.52 sub view_head2 {
188 root 1.60 "<span foreground='#ccccff' size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
189 root 1.52 . $_[1]->content->present ($_[0])
190     };
191    
192     sub view_over {
193     local $indent = $indent + $_[1]->indent;
194     $_[1]->content->present ($_[0])
195     }
196    
197 root 1.34 package CFClient::Database;
198    
199     our @ISA = BerkeleyDB::Btree::;
200    
201     sub get($$) {
202     my $data;
203    
204     $_[0]->db_get ($_[1], $data) == 0
205     ? $data
206 root 1.37 : ()
207 root 1.34 }
208    
209     my %DB_SYNC;
210    
211     sub put($$$) {
212     my ($db, $key, $data) = @_;
213    
214 root 1.37 $DB_SYNC{$db} = AnyEvent->timer (after => 5, cb => sub { $db->db_sync });
215 root 1.34
216 root 1.37 $db->db_put ($key => $data)
217 root 1.34 }
218    
219 root 1.22 package CFClient::Texture;
220 root 1.3
221 root 1.25 use strict;
222    
223 root 1.3 use Scalar::Util;
224    
225 root 1.41 use CFClient::OpenGL;
226 root 1.3
227 root 1.35 my %TEXTURES;
228 root 1.3
229 root 1.14 sub new {
230 root 1.4 my ($class, %data) = @_;
231    
232 root 1.14 my $self = bless {
233 root 1.15 internalformat => GL_RGBA,
234     format => GL_RGBA,
235 root 1.29 type => GL_UNSIGNED_BYTE,
236 root 1.14 %data,
237     }, $class;
238 root 1.4
239 root 1.35 Scalar::Util::weaken ($TEXTURES{$self+0} = $self);
240 root 1.4
241     $self->upload;
242    
243     $self
244     }
245    
246     sub new_from_image {
247 root 1.33 my ($class, $image, %arg) = @_;
248 root 1.4
249 root 1.33 $class->new (image => $image, %arg)
250 root 1.4 }
251    
252 root 1.3 sub new_from_file {
253 root 1.42 my ($class, $path, %arg) = @_;
254 root 1.3
255     open my $fh, "<:raw", $path
256     or die "$path: $!";
257    
258     local $/;
259 root 1.42 $class->new_from_image (<$fh>, %arg)
260 root 1.3 }
261    
262 root 1.14 #sub new_from_surface {
263     # my ($class, $surface) = @_;
264     #
265     # $surface->rgba;
266     #
267     # $class->new (
268     # data => $surface->pixels,
269 root 1.24 # w => $surface->width,
270     # h => $surface->height,
271 root 1.14 # )
272     #}
273    
274 root 1.21 sub new_from_layout {
275 root 1.42 my ($class, $layout, %arg) = @_;
276 root 1.14
277 root 1.50 my ($w, $h, $data, $format, $internalformat) = $layout->render;
278 root 1.14
279     $class->new (
280 root 1.24 w => $w,
281     h => $h,
282 root 1.14 data => $data,
283 root 1.50 format => $format,
284     internalformat => $format,
285 root 1.29 type => GL_UNSIGNED_BYTE,
286 root 1.42 %arg,
287 root 1.4 )
288 root 1.3 }
289    
290 root 1.8 sub new_from_opengl {
291     my ($class, $w, $h, $cb) = @_;
292    
293 root 1.48 $class->new (w => $w || 1, h => $h || 1, render_cb => $cb)
294 root 1.8 }
295    
296 root 1.19 sub topot {
297     (grep $_ >= $_[0], 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768)[0]
298     }
299    
300 root 1.3 sub upload {
301     my ($self) = @_;
302    
303 root 1.27 return unless $GL_VERSION;
304 root 1.3
305 root 1.6 my $data;
306 root 1.3
307     if (exists $self->{data}) {
308 root 1.6 $data = $self->{data};
309 root 1.25
310 root 1.24 } elsif (exists $self->{render_cb}) {
311     glViewport 0, 0, $self->{w}, $self->{h};
312 root 1.12 glMatrixMode GL_PROJECTION;
313     glLoadIdentity;
314 root 1.31 glOrtho 0, $self->{w}, 0, $self->{h}, -10000, 10000;
315 root 1.12 glMatrixMode GL_MODELVIEW;
316     glLoadIdentity;
317 root 1.25 $self->{render_cb}->($self, $self->{w}, $self->{h});
318 root 1.8
319 root 1.3 } else {
320 root 1.29 ($self->{w}, $self->{h}, $data, $self->{internalformat}, $self->{format}, $self->{type})
321     = CFClient::load_image_inline $self->{image};
322 root 1.3 }
323    
324 root 1.24 my ($tw, $th) = @$self{qw(w h)};
325 root 1.19
326 root 1.47 unless ($tw > 0 && $th > 0) {
327 root 1.24 $tw = $th = 1;
328     $data = "\x00" x 64;
329     }
330    
331 root 1.32 $self->{minified} = [CFClient::average $tw, $th, $data]
332 root 1.33 if $self->{minify};
333 root 1.32
334 root 1.24 unless ($GL_NPOT) {
335     # TODO: does not work for zero-sized textures
336 root 1.19 $tw = topot $tw;
337     $th = topot $th;
338    
339 root 1.43 if (($tw != $self->{w} || $th != $self->{h}) && defined $data) {
340 root 1.24 my $bpp = (length $data) / ($self->{w} * $self->{h});
341 root 1.20 $data = pack "(a" . ($tw * $bpp) . ")*",
342 root 1.24 unpack "(a" . ($self->{w} * $bpp) . ")*", $data;
343     $data .= ("\x00" x ($tw * $bpp)) x ($th - $self->{h});
344 root 1.19 }
345     }
346    
347 root 1.24 $self->{s} = $self->{w} / $tw;
348     $self->{t} = $self->{h} / $th;
349 root 1.19
350 root 1.41 $self->{name} ||= glGenTexture;
351 root 1.3
352     glBindTexture GL_TEXTURE_2D, $self->{name};
353    
354     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP;
355     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP;
356 root 1.42
357     if ($::FAST) {
358     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST;
359     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST;
360 root 1.45 } elsif ($self->{mipmap} && $GL_VERSION >= 1.4) {
361     # alternatively check for 0x8191
362     glTexParameter GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1;
363     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR;
364     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR;
365 root 1.42 } else {
366     glTexParameter GL_TEXTURE_2D, GL_GENERATE_MIPMAP, $self->{mipmap};
367     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR;
368 root 1.45 glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR;
369 root 1.42 }
370 root 1.3
371 root 1.45 glGetError;
372    
373 root 1.8 if (defined $data) {
374     glTexImage2D GL_TEXTURE_2D, 0,
375 root 1.15 $self->{internalformat},
376 root 1.54 $tw, $th,
377 root 1.8 0,
378 root 1.14 $self->{format},
379 root 1.29 $self->{type},
380 root 1.8 $data;
381 root 1.49 CFClient::gl_check "uploading texture %dx%d if=%x f=%x t=%x",
382     $tw, $th, $self->{internalformat}, $self->{format}, $self->{type};
383 root 1.8 } else {
384     glCopyTexImage2D GL_TEXTURE_2D, 0,
385 root 1.15 $self->{internalformat},
386 root 1.8 0, 0,
387 root 1.19 $tw, $th,
388 root 1.8 0;
389 root 1.49 CFClient::gl_check "copying to texture %dx%d if=%x",
390     $tw, $th, $self->{internalformat};
391 root 1.8 }
392 root 1.55
393     glBindTexture GL_TEXTURE_2D, 0; # just to be on the safe side
394 root 1.3 }
395    
396     sub DESTROY {
397     my ($self) = @_;
398    
399 root 1.35 delete $TEXTURES{$self+0};
400    
401 root 1.41 glDeleteTexture delete $self->{name}
402 root 1.36 if $self->{name};
403 root 1.3 }
404    
405 root 1.54 sub restore_state {
406 root 1.3 $_->upload
407 root 1.36 for values %TEXTURES;
408 root 1.54 }
409 root 1.3
410 root 1.1 1;
411    
412     =back
413    
414     =head1 AUTHOR
415    
416     Marc Lehmann <schmorp@schmorp.de>
417     http://home.schmorp.de/
418    
419     =cut
420