00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "platform/texture.hpp"
00035
00036 #include "data/shared.hpp"
00037 #include "data/array.hpp"
00038 #include "data/dictionary.hpp"
00039 #include "data/file.hpp"
00040 #include "data/fstream.hpp"
00041 #include "data/cache.hpp"
00042
00043 #include "platform/mapped_file.hpp"
00044 #include "platform/lowlevel.hpp"
00045
00046 #include <cstdlib>
00047 #include <cstring>
00048
00049
00050 namespace gsgl
00051 {
00052
00053 using namespace data;
00054 using namespace io;
00055
00056
00057 namespace platform
00058 {
00059
00060 rgba_buffer::rgba_buffer(const string & fname)
00061 : width(0), height(0), buffer(0), mf(0)
00062 {
00063 string full_path = io::file::get_full_path(fname);
00064
00065 if (!file::exists(full_path))
00066 throw runtime_exception(L"Unable to map %ls: file does not exist!", full_path.w_string());
00067
00068 mf = new mapped_file(fname, io::FILE_OPEN_READ | io::FILE_OPEN_WRITE);
00069 int *iptr = static_cast<int *>( mf->get_pointer() );
00070
00071 width = *iptr++;
00072 height = *iptr++;
00073
00074 buffer = reinterpret_cast<unsigned char *>( iptr );
00075 }
00076
00077
00078 rgba_buffer::rgba_buffer(SDL_Surface *surface)
00079 : width(surface->w), height(surface->h), buffer(0), mf(0)
00080 {
00081 buffer = static_cast<unsigned char *>( ::malloc(surface->w * surface->h * 4) );
00082
00083 if (!buffer)
00084 throw runtime_exception(L"Out of memory for texture.");
00085
00086 SDL_LockSurface(surface);
00087
00088 unsigned char *ptr = static_cast<unsigned char *>(surface->pixels);
00089 unsigned int temp, pixel;
00090
00091 for (int v = 0; v < surface->h; ++v)
00092 {
00093 for (int u = 0; u < surface->w; ++u)
00094 {
00095 int index = u + v*surface->w;
00096
00097 pixel = * reinterpret_cast<unsigned int *>(ptr);
00098
00099
00100
00101
00102 temp = pixel & surface->format->Rmask;
00103 temp = temp >> surface->format->Rshift;
00104 temp = temp << surface->format->Rloss;
00105 buffer[index*4+0] = static_cast<unsigned char>(temp);
00106
00107 temp = pixel & surface->format->Gmask;
00108 temp = temp >> surface->format->Gshift;
00109 temp = temp << surface->format->Gloss;
00110 buffer[index*4+1] = static_cast<unsigned char>(temp);
00111
00112 temp = pixel & surface->format->Bmask;
00113 temp = temp >> surface->format->Bshift;
00114 temp = temp << surface->format->Bloss;
00115 buffer[index*4+2] = static_cast<unsigned char>(temp);
00116
00117 temp = pixel & surface->format->Amask;
00118 temp = temp >> surface->format->Ashift;
00119 temp = temp << surface->format->Aloss;
00120 buffer[index*4+3] = static_cast<unsigned char>(temp);
00121
00122 ptr += surface->format->BytesPerPixel;
00123 }
00124 }
00125
00126 SDL_UnlockSurface(surface);
00127
00128 flip_vertical();
00129 }
00130
00131
00132 rgba_buffer::rgba_buffer(int width, int height, const color & c)
00133 : width(width), height(height), buffer(0), mf(0)
00134 {
00135 buffer = static_cast<unsigned char *>( ::malloc(width * height * 4) );
00136
00137 clear(c);
00138 }
00139
00140
00141 rgba_buffer::~rgba_buffer()
00142 {
00143 if (mf)
00144 {
00145 delete mf;
00146 }
00147 else
00148 {
00149 ::free(buffer);
00150 }
00151 }
00152
00153
00154 void rgba_buffer::save(const string & fname)
00155 {
00156 fd_stream s(fname, io::FILE_OPEN_WRITE | io::FILE_OPEN_BINARY);
00157
00158 s.write(reinterpret_cast<unsigned char *>(&width), sizeof(int));
00159 s.write(reinterpret_cast<unsigned char *>(&height), sizeof(int));
00160
00161 unsigned char *p = buffer;
00162 for (int i = 0; i < height; ++i, p += width*4)
00163 {
00164 s.write(p, width*4);
00165 }
00166 }
00167
00168
00169 void rgba_buffer::clear(const color & c)
00170 {
00171 unsigned char r = static_cast<unsigned char>( c[color::COMPONENT_RED] * 255.0f );
00172 unsigned char g = static_cast<unsigned char>( c[color::COMPONENT_GREEN] * 255.0f );
00173 unsigned char b = static_cast<unsigned char>( c[color::COMPONENT_BLUE] * 255.0f );
00174 unsigned char a = static_cast<unsigned char>( c[color::COMPONENT_ALPHA] * 255.0f );
00175
00176 unsigned char *p = buffer;
00177 for (int i = 0; i < width*height; ++i)
00178 {
00179 *p++ = r;
00180 *p++ = g;
00181 *p++ = b;
00182 *p++ = a;
00183 }
00184 }
00185
00186
00187 void rgba_buffer::flip_vertical()
00188 {
00189 int a = 0, b = height - 1;
00190 unsigned char *temp = static_cast<unsigned char *>( ::malloc(width*4) );
00191 unsigned char *ptr_a, *ptr_b, *ptr_t = temp;
00192
00193 while (b > a)
00194 {
00195 ptr_a = buffer + a++*width*4;
00196 ptr_b = buffer + b--*width*4;
00197
00198 ::memcpy(ptr_t, ptr_a, width*4);
00199 ::memcpy(ptr_a, ptr_b, width*4);
00200 ::memcpy(ptr_b, ptr_t, width*4);
00201 }
00202
00203 ::free(temp);
00204 }
00205
00206
00207
00208
00209
00210 class texture_impl
00211 : public shared_object
00212 {
00213 string name;
00214 texture_format format;
00215 gsgl::flags_t flags;
00216
00217 const GLenum opengl_texture_unit;
00218 GLuint opengl_id;
00219
00220 rgba_buffer *buffer;
00221
00222 public:
00223 texture_impl(const string & fname, const texture_format & format, const GLenum opengl_texture_unit, const gsgl::flags_t flags);
00224 texture_impl(SDL_Surface *surface, const string & name, const texture_format & format, const GLenum opengl_texture_unit, const gsgl::flags_t flags);
00225 virtual ~texture_impl();
00226
00227 const string & get_name() const { return name; }
00228 const int get_texture_unit() { return static_cast<int>(opengl_texture_unit); }
00229
00230 void bind();
00231 void unbind();
00232 void update();
00233 void unload();
00234 };
00235
00236
00237 texture_impl::texture_impl(const string & fname, const texture_format & format, const GLenum opengl_texture_unit, const gsgl::flags_t flags)
00238 : shared_object(), name(fname), format(format), opengl_texture_unit(opengl_texture_unit), flags(flags), opengl_id(0), buffer(0)
00239 {
00240 if (!file::exists(fname))
00241 throw io_exception(L"Texture file %ls not found.", fname.w_string());
00242
00243 SDL_Surface *surface = IMG_Load(fname.c_string());
00244 if (!surface)
00245 throw runtime_exception(L"Unable to load texture file %ls: %hs", fname.w_string(), IMG_GetError());
00246
00247 buffer = new rgba_buffer(surface);
00248
00249 SDL_FreeSurface(surface);
00250 }
00251
00252
00253 texture_impl::texture_impl(SDL_Surface *surface, const string & name, const texture_format & format, const GLenum opengl_texture_unit, const gsgl::flags_t flags)
00254 : shared_object(), name(name), format(format), opengl_texture_unit(opengl_texture_unit), flags(flags), opengl_id(0), buffer(0)
00255 {
00256 buffer = new rgba_buffer(surface);
00257 }
00258
00259
00260 texture_impl::~texture_impl()
00261 {
00262 unload();
00263 delete buffer;
00264 }
00265
00266
00267 void texture_impl::bind()
00268 {
00269 GLint max_texture_units;
00270 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &max_texture_units);
00271
00272 if (opengl_texture_unit >= static_cast<GLenum>(max_texture_units))
00273 throw runtime_exception(L"Invalid texture unit %d; there are only %d texture units available.", opengl_texture_unit, max_texture_units);
00274
00275 glActiveTexture(GL_TEXTURE0 + opengl_texture_unit); CHECK_GL_ERRORS();
00276
00277 if (opengl_id)
00278 {
00279 glBindTexture(GL_TEXTURE_2D, opengl_id); CHECK_GL_ERRORS();
00280 }
00281 else
00282 {
00283 glGenTextures(1, &opengl_id); CHECK_GL_ERRORS();
00284 if (opengl_id == 0)
00285 throw runtime_exception(L"Unable to generate an OpenGL texture ID for %ls", name.w_string());
00286
00287 glBindTexture(GL_TEXTURE_2D, opengl_id); CHECK_GL_ERRORS();
00288
00289 GLint ff = 0;
00290 switch (format)
00291 {
00292 case TEXTURE_HEIGHTMAP:
00293 ff = (flags & TEXTURE_LOAD_UNCOMPRESSED) ? GL_LUMINANCE : GL_COMPRESSED_LUMINANCE;
00294 break;
00295 case TEXTURE_COLORMAP:
00296 default:
00297 ff = (flags & TEXTURE_LOAD_UNCOMPRESSED) ? GL_RGBA : GL_COMPRESSED_RGBA;
00298 break;
00299 }
00300
00301 glTexImage2D(GL_TEXTURE_2D, 0, ff, buffer->get_width(), buffer->get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer->get_pointer()); CHECK_GL_ERRORS();
00302 }
00303 }
00304
00305
00306 void texture_impl::unbind()
00307 {
00308 glBindTexture(GL_TEXTURE_2D, 0); CHECK_GL_ERRORS();
00309 }
00310
00311
00312 void texture_impl::update()
00313 {
00314 bind();
00315 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, buffer->get_width(), buffer->get_height(), GL_RGBA, GL_UNSIGNED_BYTE, buffer->get_pointer());
00316 unbind();
00317 }
00318
00319
00320 void texture_impl::unload()
00321 {
00322 if (opengl_id)
00323 {
00324 glDeleteTextures(1, &opengl_id); CHECK_GL_ERRORS();
00325 opengl_id = 0;
00326 }
00327 }
00328
00329
00330
00331
00332 typedef data::cache<texture_impl> texture_cache;
00333
00334
00335 texture::texture(const texture & tex)
00336 : impl(tex.impl), flags(tex.flags),
00337 gl_env(tex.gl_env), gl_wrap(tex.gl_wrap), gl_filter(tex.gl_filter)
00338 {
00339 impl->attach();
00340 }
00341
00342
00343 texture::texture(const string & fname, const gsgl::flags_t flags, const texture_format & format, const int & texture_unit)
00344 : impl(0), flags(flags),
00345 gl_env(GL_REPLACE), gl_wrap(GL_REPEAT), gl_filter(GL_LINEAR)
00346 {
00347 texture_cache & global_textures = *texture_cache::global_instance();
00348
00349 assign_gl_modes();
00350
00351 string full_path = io::file::get_full_path(fname);
00352 string full_name = string::format(L"%ls: %08d; %d", full_path.w_string(), flags, texture_unit);
00353
00354 if (global_textures.contains_index(full_name))
00355 {
00356 impl = global_textures[full_name];
00357 }
00358 else
00359 {
00360 impl = new texture_impl(full_path, format, static_cast<GLenum>(texture_unit), flags);
00361 global_textures[full_name] = impl;
00362 impl->attach();
00363 }
00364
00365 impl->attach();
00366 }
00367
00368
00369 static int TEX_COUNT = 0;
00370
00371
00372 texture::texture(SDL_Surface *surface,
00373 const gsgl::flags_t flags,
00374 const texture_format & format,
00375 const string & identifier,
00376 const int & texture_unit)
00377 : impl(0), flags(flags),
00378 gl_env(GL_REPLACE), gl_wrap(GL_REPEAT), gl_filter(GL_LINEAR)
00379 {
00380 texture_cache & global_textures = *texture_cache::global_instance();
00381
00382 assign_gl_modes();
00383
00384
00385 string ptr_name = string::format(L"SDL surface (%ls): %08p %08d %08d; %d", identifier.w_string(), surface, ++TEX_COUNT, flags, texture_unit);
00386
00387 if (global_textures.contains_index(ptr_name))
00388 {
00389 impl = global_textures[ptr_name];
00390 }
00391 else
00392 {
00393 impl = new texture_impl(surface, ptr_name, format, static_cast<GLenum>(texture_unit), flags);
00394 global_textures[ptr_name] = impl;
00395 impl->attach();
00396 }
00397
00398 impl->attach();
00399 }
00400
00401
00402 texture::~texture()
00403 {
00404 impl->detach();
00405 }
00406
00407
00408 const int texture::get_texture_unit() const
00409 {
00410 return impl->get_texture_unit();
00411 }
00412
00413
00414 void texture::bind(gsgl::flags_t render_flags)
00415 {
00416 assert(impl);
00417 impl->bind();
00418
00419 if (!(flags & TEXTURE_LOAD_NO_PARAMS))
00420 {
00421 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, gl_env); CHECK_GL_ERRORS();
00422
00423 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gl_wrap); CHECK_GL_ERRORS();
00424 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gl_wrap); CHECK_GL_ERRORS();
00425 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter); CHECK_GL_ERRORS();
00426 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter); CHECK_GL_ERRORS();
00427
00428 if (GL_EXT_texture_filter_anisotropic && (render_flags & TEXTURE_RENDER_ANISOTROPIC))
00429 {
00430 GLfloat max_aniso;
00431 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_aniso); CHECK_GL_ERRORS();
00432 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); CHECK_GL_ERRORS();
00433 }
00434 }
00435 }
00436
00437
00438 void texture::unbind()
00439 {
00440 assert(impl);
00441 impl->unbind();
00442 }
00443
00444
00445 void texture::update()
00446 {
00447 assert(impl);
00448 impl->update();
00449 }
00450
00451
00452 void texture::unload()
00453 {
00454 assert(impl);
00455 impl->unload();
00456 }
00457
00458
00459 void texture::assign_gl_modes()
00460 {
00461
00462 if (flags & TEXTURE_ENV_REPLACE)
00463 gl_env = GL_REPLACE;
00464 else if (flags & TEXTURE_ENV_MODULATE)
00465 gl_env = GL_MODULATE;
00466 else
00467 gl_env = GL_REPLACE;
00468
00469
00470 if (flags & TEXTURE_WRAP_REPEAT)
00471 gl_wrap = GL_REPEAT;
00472 else if (flags & TEXTURE_WRAP_CLAMP)
00473 gl_wrap = GL_CLAMP;
00474 else
00475 gl_wrap = GL_REPEAT;
00476
00477
00478 if (flags & TEXTURE_FILTER_LINEAR)
00479 gl_filter = GL_LINEAR;
00480 else
00481 gl_filter = GL_LINEAR;
00482 }
00483
00484
00485 gsgl::data_object *texture::create_global_texture_cache()
00486 {
00487 return new texture_cache(L"texture cache");
00488 }
00489
00490
00491 }
00492
00493
00494
00495 platform::texture_cache *platform::texture_cache::instance = 0;
00496
00497 }