added 3dengfx into the repo, probably not the correct version for this
[summerhack] / src / 3dengfx / src / 3dengfx / texman.cpp
diff --git a/src/3dengfx/src/3dengfx/texman.cpp b/src/3dengfx/src/3dengfx/texman.cpp
new file mode 100644 (file)
index 0000000..66ef695
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+This file is part of 3dengfx, realtime visualization system.
+Copyright (C) 2004, 2005, 2006 John Tsiombikas <nuclear@siggraph.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/* texture manager
+ *
+ * Author: John Tsiombikas 2004
+ */
+
+#include "3dengfx_config.h"
+
+#include <string>
+#include <cstring>
+#include "texman.hpp"
+#include "common/hashtable.hpp"
+#include "gfx/image.h"
+#include "gfx/color.hpp"
+#include "n3dmath2/n3dmath2.hpp"
+#include "common/string_hash.hpp"
+#include "common/err_msg.h"
+
+using std::string;
+
+static void create_normal_cube_map();
+
+static HashTable<string, Texture*> *textures;
+static Texture *normal_cubemap;
+
+static void delete_texture(Texture *tex) {
+       glDeleteTextures(1, &tex->tex_id);
+       glGetError();
+       //delete tex;
+}
+
+static void init_tex_man() {
+       if(textures) return;
+       textures = new HashTable<string, Texture*>;
+       textures->set_hash_function(string_hash);
+       textures->set_data_destructor(delete_texture);
+}
+
+void add_texture(Texture *texture, const char *fname) {
+       
+       if(!textures) init_tex_man();
+       
+       if(!fname) {    // enter a randomly named texture
+               textures->insert(tmpnam(0), texture);
+       } else {
+               textures->insert(fname, texture);
+       }
+}
+
+// TODO: implement this one, after making HashTable remove by value
+void remove_texture(Texture *texture) {
+}
+
+Texture *find_texture(const char *fname) {
+       
+       if(!textures) init_tex_man();
+       
+       Pair<string, Texture*> *res = textures->find(fname);
+       return res ? res->val : 0;
+}
+
+
+/* ----- get_texture() function -----
+ * first looks in the texture database in constant time (hash table)
+ * if the texture is already there it just returns the pointer. If the
+ * texture is not there it tries to load the image data, create the texture
+ * and return it, and if it fails it returns a NULL pointer
+ */
+Texture *get_texture(const char *fname) {
+       if(!fname) return 0;
+       
+       Texture *tex;
+       if((tex = find_texture(fname))) return tex;
+
+       // first check to see if it's a custom file (cubemap).
+       if(is_cubemap(fname)) {
+               tex = load_cubemap(fname);
+               add_texture(tex, fname);
+               return tex;
+       }
+       
+       PixelBuffer pbuf;
+       
+       void *img_buf = load_image(fname, &pbuf.width, &pbuf.height);
+       if(!img_buf) return 0;
+
+       pbuf.buffer = new Pixel[pbuf.width * pbuf.height];
+       memcpy(pbuf.buffer, img_buf, pbuf.width * pbuf.height * sizeof(Pixel));
+
+       free_image(img_buf);
+       
+       tex = new Texture;
+       tex->set_pixel_data(pbuf);
+       add_texture(tex, fname);
+       return tex;
+}
+
+
+void destroy_textures() {
+       static bool called_again = false;
+
+       if(called_again) {
+               warning("Multiple destroy_textures() calls");
+               return;
+       } else {
+               called_again = true;
+       }
+
+       info("Shutting down texture manager, destroying all textures...");
+       delete textures;
+       textures = 0;
+}
+
+
+Texture *make_cube_map(Texture **tex_array) {
+       int size = tex_array[0]->width;
+
+       Texture *cube = new Texture(size, size, TEX_CUBE);
+
+       cube->lock(CUBE_MAP_PX);
+       tex_array[CUBE_MAP_INDEX_PX]->lock();
+       memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PX]->buffer, size * size * sizeof(Pixel));
+       tex_array[CUBE_MAP_INDEX_PX]->unlock();
+       cube->unlock(CUBE_MAP_PX);
+
+       cube->lock(CUBE_MAP_NX);
+       tex_array[CUBE_MAP_INDEX_NX]->lock();
+       memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NX]->buffer, size * size * sizeof(Pixel));
+       tex_array[CUBE_MAP_INDEX_NX]->unlock();
+       cube->unlock(CUBE_MAP_NX);
+       
+       cube->lock(CUBE_MAP_PY);
+       tex_array[CUBE_MAP_INDEX_PY]->lock();
+       memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PY]->buffer, size * size * sizeof(Pixel));
+       tex_array[CUBE_MAP_INDEX_PY]->unlock();
+       cube->unlock(CUBE_MAP_PY);
+       
+       cube->lock(CUBE_MAP_NY);
+       tex_array[CUBE_MAP_INDEX_NY]->lock();
+       memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NY]->buffer, size * size * sizeof(Pixel));
+       tex_array[CUBE_MAP_INDEX_NY]->unlock();
+       cube->unlock(CUBE_MAP_NY);
+
+       cube->lock(CUBE_MAP_PZ);
+       tex_array[CUBE_MAP_INDEX_PZ]->lock();
+       memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PZ]->buffer, size * size * sizeof(Pixel));
+       tex_array[CUBE_MAP_INDEX_PZ]->unlock();
+       cube->unlock(CUBE_MAP_PZ);
+
+       cube->lock(CUBE_MAP_NZ);
+       tex_array[CUBE_MAP_INDEX_NZ]->lock();
+       memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NZ]->buffer, size * size * sizeof(Pixel));
+       tex_array[CUBE_MAP_INDEX_NZ]->unlock();
+       cube->unlock(CUBE_MAP_NZ);
+
+       return cube;
+}
+
+
+Texture *get_normal_cube() {
+       if(!normal_cubemap) create_normal_cube_map();
+       return normal_cubemap;
+}
+
+static Color vec_to_color(const Vector3 &v) {
+       return Color(v.x * 0.5 + 0.5, v.y * 0.5 + 0.5, v.z * 0.5 + 0.5);
+}
+
+static void create_normal_cube_map() {
+       static const int size = 32;
+       static const scalar_t fsize = (scalar_t)size;
+       static const scalar_t half_size = fsize / 2.0;
+       Pixel *ptr;
+       
+       delete normal_cubemap;
+       normal_cubemap = new Texture(size, size, TEX_CUBE);
+
+       // +X
+       normal_cubemap->lock(CUBE_MAP_PX);
+       ptr = normal_cubemap->buffer;
+       for(int j=0; j<size; j++) {
+               for(int i=0; i<size; i++) {
+                       Vector3 normal(half_size, -((scalar_t)j + 0.5 - half_size), -((scalar_t)i + 0.5 - half_size));
+                       *ptr++ = pack_color32(vec_to_color(normal.normalized()));
+               }
+       }
+       normal_cubemap->unlock(CUBE_MAP_PX);
+       
+       // -X
+       normal_cubemap->lock(CUBE_MAP_NX);
+       ptr = normal_cubemap->buffer;
+       for(int j=0; j<size; j++) {
+               for(int i=0; i<size; i++) {
+                       Vector3 normal(-half_size, -((scalar_t)j + 0.5 - half_size), (scalar_t)i + 0.5 - half_size);
+                       *ptr++ = pack_color32(vec_to_color(normal.normalized()));
+               }
+       }
+       normal_cubemap->unlock(CUBE_MAP_NX);
+       
+       // +Y
+       normal_cubemap->lock(CUBE_MAP_PY);
+       ptr = normal_cubemap->buffer;
+       for(int j=0; j<size; j++) {
+               for(int i=0; i<size; i++) {
+                       Vector3 normal((scalar_t)i + 0.5 - half_size, half_size, (scalar_t)j + 0.5 - half_size); 
+                       *ptr++ = pack_color32(vec_to_color(normal.normalized()));
+               }
+       }
+       normal_cubemap->unlock(CUBE_MAP_PY);
+       
+       // -Y
+       normal_cubemap->lock(CUBE_MAP_NY);
+       ptr = normal_cubemap->buffer;
+       for(int j=0; j<size; j++) {
+               for(int i=0; i<size; i++) {
+                       Vector3 normal((scalar_t)i + 0.5 - half_size, -half_size, -((scalar_t)j + 0.5 - half_size)); 
+                       *ptr++ = pack_color32(vec_to_color(normal.normalized()));
+               }
+       }
+       normal_cubemap->unlock(CUBE_MAP_NY);
+
+       // +Z
+       normal_cubemap->lock(CUBE_MAP_PZ);
+       ptr = normal_cubemap->buffer;
+       for(int j=0; j<size; j++) {
+               for(int i=0; i<size; i++) {
+                       Vector3 normal((scalar_t)i + 0.5 - half_size, -((scalar_t)j + 0.5 - half_size), half_size);
+                       *ptr++ = pack_color32(vec_to_color(normal.normalized()));
+               }
+       }
+       normal_cubemap->unlock(CUBE_MAP_PZ);
+
+       // -Z
+       normal_cubemap->lock(CUBE_MAP_NZ);
+       ptr = normal_cubemap->buffer;
+       for(int j=0; j<size; j++) {
+               for(int i=0; i<size; i++) {
+                       Vector3 normal(-((scalar_t)i + 0.5 - half_size), -((scalar_t)j + 0.5 - half_size), -half_size);
+                       *ptr++ = pack_color32(vec_to_color(normal.normalized()));
+               }
+       }
+       normal_cubemap->unlock(CUBE_MAP_NZ);
+}
+
+
+bool is_cubemap(const char *fname) {
+       FILE *fp = fopen(fname, "r");
+       if(!fp) {
+               return false;
+       }
+
+       char idstr[5];
+       fread(idstr, 1, 4, fp);
+       idstr[4] = 0;
+
+       fclose(fp);
+       return strcmp(idstr, "CUBE") == 0;
+}
+
+Texture *load_cubemap(const char *fname) {
+       FILE *fp = fopen(fname, "r");
+       if(!fp) {
+               error("could not open %s", fname);
+               return 0;
+       }
+
+       if(!is_cubemap(fname)) {
+               error("%s is not a cubemap", fname);
+               fclose(fp);
+               return 0;
+       }
+
+       char line[512];
+       unsigned int cube_size = 0;
+       unsigned long xsz = 0, ysz = 0;
+       void *img[6] = {0};
+
+       fgets(line, 512, fp);   // skip file id & text description
+       
+       if(fgets(line, 512, fp) && isdigit(*line)) {
+               cube_size = atoi(line);
+       }
+       
+       for(int i=0; i<6; i++) {
+               if(!fgets(line, 512, fp)) {
+                       error("%s is not a complete cubemap file, EOF encountered", fname);
+                       break;
+               }
+
+               if(line[strlen(line)-1] == '\n') {
+                       line[strlen(line)-1] = 0;
+               }
+               
+               unsigned long x, y;
+               if(!(img[i] = load_image(line, &x, &y))) {
+                       error("cubemap %s requires %s, which cannot be opened", fname, line);
+                       break;
+               }
+
+               if(i > 0 && (x != xsz || y != ysz)) {
+                       error("inconsistent cubemap %s, image sizes differ", fname);
+                       break;
+               }
+               xsz = x;
+               ysz = y;
+
+               if(xsz != ysz) {
+                       error("cubemap %s contains non-square textures", fname);
+                       break;
+               }
+       }
+
+       fclose(fp);
+       
+       if(!img[5]) {
+               for(int i=0; i<6; i++) {
+                       if(img[i]) free_image(img[i]);
+               }
+               return 0;
+       }
+       
+       if(xsz != cube_size) {
+               warning("cubemap %s loaded correctly, but wrong size in the header", fname);
+       }
+
+       Texture *cube = new Texture(cube_size, cube_size, TEX_CUBE);
+
+       CubeMapFace faces[] = {
+               CUBE_MAP_PX, CUBE_MAP_NX,
+               CUBE_MAP_PY, CUBE_MAP_NY,
+               CUBE_MAP_PZ, CUBE_MAP_NZ
+       };
+
+       for(int i=0; i<6; i++) {
+               cube->lock(faces[i]);
+               memcpy(cube->buffer, img[i], cube_size * cube_size * sizeof(Pixel));
+               cube->unlock(faces[i]);
+
+               free_image(img[i]);
+       }
+
+       return cube;
+}