ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC.pm
Revision: 1.56
Committed: Fri May 19 23:18:40 2006 UTC (17 years, 11 months ago) by root
Branch: MAIN
Changes since 1.55: +1 -1 lines
Log Message:
implement face animation, fix inventory layout

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