ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC.pm
Revision: 1.59
Committed: Tue May 23 17:30:18 2006 UTC (17 years, 11 months ago) by elmex
Branch: MAIN
Changes since 1.58: +1 -0 lines
Log Message:
ATI gfx card workaround for NPOT

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     package CFClient::PodToPango;
137    
138     use base Pod::POM::View::Text;
139    
140     our $indent = 0;
141    
142     *view_seq_code =
143     *view_seq_bold = sub { "<b>$_[1]</b>" };
144     *view_seq_italic = sub { "<i>$_[1]</i>" };
145     *view_seq_space =
146     *view_seq_link =
147     *view_seq_index = sub { CFClient::UI::Label::escape ($_[1]) };
148    
149     sub view_seq_text {
150     my $text = $_[1];
151     $text =~ s/\s+/ /g;
152     CFClient::UI::Label::escape ($text)
153     }
154    
155     sub view_item {
156     ("\t" x ($indent / 4))
157     . $_[1]->title->present ($_[0])
158     . "\n"
159     . $_[1]->content->present ($_[0])
160     }
161    
162 root 1.53 sub view_verbatim {
163     (join "",
164     map +("\t" x ($indent / 2)) . "$_\n",
165     split /\n/, CFClient::UI::Label::escape ($_[1]))
166     . "\n"
167     }
168    
169 root 1.52 sub view_textblock {
170     ("\t" x ($indent / 2)) . "$_[1]\n\n"
171     }
172    
173     sub view_head2 {
174     "<big>" . $_[1]->title->present ($_[0]) . "</big>\n\n"
175     . $_[1]->content->present ($_[0])
176     };
177    
178     sub view_over {
179     local $indent = $indent + $_[1]->indent;
180     $_[1]->content->present ($_[0])
181     }
182    
183 root 1.34 package CFClient::Database;
184    
185     our @ISA = BerkeleyDB::Btree::;
186    
187     sub get($$) {
188     my $data;
189    
190     $_[0]->db_get ($_[1], $data) == 0
191     ? $data
192 root 1.37 : ()
193 root 1.34 }
194    
195     my %DB_SYNC;
196    
197     sub put($$$) {
198     my ($db, $key, $data) = @_;
199    
200 root 1.37 $DB_SYNC{$db} = AnyEvent->timer (after => 5, cb => sub { $db->db_sync });
201 root 1.34
202 root 1.37 $db->db_put ($key => $data)
203 root 1.34 }
204    
205 root 1.22 package CFClient::Texture;
206 root 1.3
207 root 1.25 use strict;
208    
209 root 1.3 use Scalar::Util;
210    
211 root 1.41 use CFClient::OpenGL;
212 root 1.3
213 root 1.35 my %TEXTURES;
214 root 1.3
215 root 1.14 sub new {
216 root 1.4 my ($class, %data) = @_;
217    
218 root 1.14 my $self = bless {
219 root 1.15 internalformat => GL_RGBA,
220     format => GL_RGBA,
221 root 1.29 type => GL_UNSIGNED_BYTE,
222 root 1.14 %data,
223     }, $class;
224 root 1.4
225 root 1.35 Scalar::Util::weaken ($TEXTURES{$self+0} = $self);
226 root 1.4
227     $self->upload;
228    
229     $self
230     }
231    
232     sub new_from_image {
233 root 1.33 my ($class, $image, %arg) = @_;
234 root 1.4
235 root 1.33 $class->new (image => $image, %arg)
236 root 1.4 }
237    
238 root 1.3 sub new_from_file {
239 root 1.42 my ($class, $path, %arg) = @_;
240 root 1.3
241     open my $fh, "<:raw", $path
242     or die "$path: $!";
243    
244     local $/;
245 root 1.42 $class->new_from_image (<$fh>, %arg)
246 root 1.3 }
247    
248 root 1.14 #sub new_from_surface {
249     # my ($class, $surface) = @_;
250     #
251     # $surface->rgba;
252     #
253     # $class->new (
254     # data => $surface->pixels,
255 root 1.24 # w => $surface->width,
256     # h => $surface->height,
257 root 1.14 # )
258     #}
259    
260 root 1.21 sub new_from_layout {
261 root 1.42 my ($class, $layout, %arg) = @_;
262 root 1.14
263 root 1.50 my ($w, $h, $data, $format, $internalformat) = $layout->render;
264 root 1.14
265     $class->new (
266 root 1.24 w => $w,
267     h => $h,
268 root 1.14 data => $data,
269 root 1.50 format => $format,
270     internalformat => $format,
271 root 1.29 type => GL_UNSIGNED_BYTE,
272 root 1.42 %arg,
273 root 1.4 )
274 root 1.3 }
275    
276 root 1.8 sub new_from_opengl {
277     my ($class, $w, $h, $cb) = @_;
278    
279 root 1.48 $class->new (w => $w || 1, h => $h || 1, render_cb => $cb)
280 root 1.8 }
281    
282 root 1.19 sub topot {
283     (grep $_ >= $_[0], 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768)[0]
284     }
285    
286 root 1.3 sub upload {
287     my ($self) = @_;
288    
289 root 1.27 return unless $GL_VERSION;
290 root 1.3
291 root 1.6 my $data;
292 root 1.3
293     if (exists $self->{data}) {
294 root 1.6 $data = $self->{data};
295 root 1.25
296 root 1.24 } elsif (exists $self->{render_cb}) {
297     glViewport 0, 0, $self->{w}, $self->{h};
298 root 1.12 glMatrixMode GL_PROJECTION;
299     glLoadIdentity;
300 root 1.31 glOrtho 0, $self->{w}, 0, $self->{h}, -10000, 10000;
301 root 1.12 glMatrixMode GL_MODELVIEW;
302     glLoadIdentity;
303 root 1.25 $self->{render_cb}->($self, $self->{w}, $self->{h});
304 root 1.8
305 root 1.3 } else {
306 root 1.29 ($self->{w}, $self->{h}, $data, $self->{internalformat}, $self->{format}, $self->{type})
307     = CFClient::load_image_inline $self->{image};
308 root 1.3 }
309    
310 root 1.24 my ($tw, $th) = @$self{qw(w h)};
311 root 1.19
312 root 1.47 unless ($tw > 0 && $th > 0) {
313 root 1.24 $tw = $th = 1;
314     $data = "\x00" x 64;
315     }
316    
317 root 1.32 $self->{minified} = [CFClient::average $tw, $th, $data]
318 root 1.33 if $self->{minify};
319 root 1.32
320 root 1.24 unless ($GL_NPOT) {
321     # TODO: does not work for zero-sized textures
322 root 1.19 $tw = topot $tw;
323     $th = topot $th;
324    
325 root 1.43 if (($tw != $self->{w} || $th != $self->{h}) && defined $data) {
326 root 1.24 my $bpp = (length $data) / ($self->{w} * $self->{h});
327 root 1.20 $data = pack "(a" . ($tw * $bpp) . ")*",
328 root 1.24 unpack "(a" . ($self->{w} * $bpp) . ")*", $data;
329     $data .= ("\x00" x ($tw * $bpp)) x ($th - $self->{h});
330 root 1.19 }
331     }
332    
333 root 1.24 $self->{s} = $self->{w} / $tw;
334     $self->{t} = $self->{h} / $th;
335 root 1.19
336 root 1.41 $self->{name} ||= glGenTexture;
337 root 1.3
338     glBindTexture GL_TEXTURE_2D, $self->{name};
339    
340     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP;
341     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP;
342 root 1.42
343     if ($::FAST) {
344     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST;
345     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST;
346 root 1.45 } elsif ($self->{mipmap} && $GL_VERSION >= 1.4) {
347     # alternatively check for 0x8191
348     glTexParameter GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1;
349     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR;
350     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR;
351 root 1.42 } else {
352     glTexParameter GL_TEXTURE_2D, GL_GENERATE_MIPMAP, $self->{mipmap};
353     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR;
354 root 1.45 glTexParameter GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR;
355 root 1.42 }
356 root 1.3
357 root 1.45 glGetError;
358    
359 root 1.8 if (defined $data) {
360     glTexImage2D GL_TEXTURE_2D, 0,
361 root 1.15 $self->{internalformat},
362 root 1.54 $tw, $th,
363 root 1.8 0,
364 root 1.14 $self->{format},
365 root 1.29 $self->{type},
366 root 1.8 $data;
367 root 1.49 CFClient::gl_check "uploading texture %dx%d if=%x f=%x t=%x",
368     $tw, $th, $self->{internalformat}, $self->{format}, $self->{type};
369 root 1.8 } else {
370     glCopyTexImage2D GL_TEXTURE_2D, 0,
371 root 1.15 $self->{internalformat},
372 root 1.8 0, 0,
373 root 1.19 $tw, $th,
374 root 1.8 0;
375 root 1.49 CFClient::gl_check "copying to texture %dx%d if=%x",
376     $tw, $th, $self->{internalformat};
377 root 1.8 }
378 root 1.55
379     glBindTexture GL_TEXTURE_2D, 0; # just to be on the safe side
380 root 1.3 }
381    
382     sub DESTROY {
383     my ($self) = @_;
384    
385 root 1.35 delete $TEXTURES{$self+0};
386    
387 root 1.41 glDeleteTexture delete $self->{name}
388 root 1.36 if $self->{name};
389 root 1.3 }
390    
391 root 1.54 sub restore_state {
392 root 1.3 $_->upload
393 root 1.36 for values %TEXTURES;
394 root 1.54 }
395 root 1.3
396 root 1.1 1;
397    
398     =back
399    
400     =head1 AUTHOR
401    
402     Marc Lehmann <schmorp@schmorp.de>
403     http://home.schmorp.de/
404    
405     =cut
406