2 This file is part of 3dengfx, realtime visualization system.
3 Copyright (C) 2004, 2005, 2006 John Tsiombikas <nuclear@siggraph.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * Author: John Tsiombikas 2004
24 #include "3dengfx_config.h"
29 #include "common/hashtable.hpp"
30 #include "gfx/image.h"
31 #include "gfx/color.hpp"
32 #include "n3dmath2/n3dmath2.hpp"
33 #include "common/string_hash.hpp"
34 #include "common/err_msg.h"
38 static void create_normal_cube_map();
40 static HashTable<string, Texture*> *textures;
41 static Texture *normal_cubemap;
43 static void delete_texture(Texture *tex) {
44 glDeleteTextures(1, &tex->tex_id);
49 static void init_tex_man() {
51 textures = new HashTable<string, Texture*>;
52 textures->set_hash_function(string_hash);
53 textures->set_data_destructor(delete_texture);
56 void add_texture(Texture *texture, const char *fname) {
58 if(!textures) init_tex_man();
60 if(!fname) { // enter a randomly named texture
61 textures->insert(tmpnam(0), texture);
63 textures->insert(fname, texture);
67 // TODO: implement this one, after making HashTable remove by value
68 void remove_texture(Texture *texture) {
71 Texture *find_texture(const char *fname) {
73 if(!textures) init_tex_man();
75 Pair<string, Texture*> *res = textures->find(fname);
76 return res ? res->val : 0;
80 /* ----- get_texture() function -----
81 * first looks in the texture database in constant time (hash table)
82 * if the texture is already there it just returns the pointer. If the
83 * texture is not there it tries to load the image data, create the texture
84 * and return it, and if it fails it returns a NULL pointer
86 Texture *get_texture(const char *fname) {
90 if((tex = find_texture(fname))) return tex;
92 // first check to see if it's a custom file (cubemap).
93 if(is_cubemap(fname)) {
94 tex = load_cubemap(fname);
95 add_texture(tex, fname);
101 void *img_buf = load_image(fname, &pbuf.width, &pbuf.height);
102 if(!img_buf) return 0;
104 pbuf.buffer = new Pixel[pbuf.width * pbuf.height];
105 memcpy(pbuf.buffer, img_buf, pbuf.width * pbuf.height * sizeof(Pixel));
110 tex->set_pixel_data(pbuf);
111 add_texture(tex, fname);
116 void destroy_textures() {
117 static bool called_again = false;
120 warning("Multiple destroy_textures() calls");
126 info("Shutting down texture manager, destroying all textures...");
132 Texture *make_cube_map(Texture **tex_array) {
133 int size = tex_array[0]->width;
135 Texture *cube = new Texture(size, size, TEX_CUBE);
137 cube->lock(CUBE_MAP_PX);
138 tex_array[CUBE_MAP_INDEX_PX]->lock();
139 memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PX]->buffer, size * size * sizeof(Pixel));
140 tex_array[CUBE_MAP_INDEX_PX]->unlock();
141 cube->unlock(CUBE_MAP_PX);
143 cube->lock(CUBE_MAP_NX);
144 tex_array[CUBE_MAP_INDEX_NX]->lock();
145 memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NX]->buffer, size * size * sizeof(Pixel));
146 tex_array[CUBE_MAP_INDEX_NX]->unlock();
147 cube->unlock(CUBE_MAP_NX);
149 cube->lock(CUBE_MAP_PY);
150 tex_array[CUBE_MAP_INDEX_PY]->lock();
151 memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PY]->buffer, size * size * sizeof(Pixel));
152 tex_array[CUBE_MAP_INDEX_PY]->unlock();
153 cube->unlock(CUBE_MAP_PY);
155 cube->lock(CUBE_MAP_NY);
156 tex_array[CUBE_MAP_INDEX_NY]->lock();
157 memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NY]->buffer, size * size * sizeof(Pixel));
158 tex_array[CUBE_MAP_INDEX_NY]->unlock();
159 cube->unlock(CUBE_MAP_NY);
161 cube->lock(CUBE_MAP_PZ);
162 tex_array[CUBE_MAP_INDEX_PZ]->lock();
163 memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PZ]->buffer, size * size * sizeof(Pixel));
164 tex_array[CUBE_MAP_INDEX_PZ]->unlock();
165 cube->unlock(CUBE_MAP_PZ);
167 cube->lock(CUBE_MAP_NZ);
168 tex_array[CUBE_MAP_INDEX_NZ]->lock();
169 memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NZ]->buffer, size * size * sizeof(Pixel));
170 tex_array[CUBE_MAP_INDEX_NZ]->unlock();
171 cube->unlock(CUBE_MAP_NZ);
177 Texture *get_normal_cube() {
178 if(!normal_cubemap) create_normal_cube_map();
179 return normal_cubemap;
182 static Color vec_to_color(const Vector3 &v) {
183 return Color(v.x * 0.5 + 0.5, v.y * 0.5 + 0.5, v.z * 0.5 + 0.5);
186 static void create_normal_cube_map() {
187 static const int size = 32;
188 static const scalar_t fsize = (scalar_t)size;
189 static const scalar_t half_size = fsize / 2.0;
192 delete normal_cubemap;
193 normal_cubemap = new Texture(size, size, TEX_CUBE);
196 normal_cubemap->lock(CUBE_MAP_PX);
197 ptr = normal_cubemap->buffer;
198 for(int j=0; j<size; j++) {
199 for(int i=0; i<size; i++) {
200 Vector3 normal(half_size, -((scalar_t)j + 0.5 - half_size), -((scalar_t)i + 0.5 - half_size));
201 *ptr++ = pack_color32(vec_to_color(normal.normalized()));
204 normal_cubemap->unlock(CUBE_MAP_PX);
207 normal_cubemap->lock(CUBE_MAP_NX);
208 ptr = normal_cubemap->buffer;
209 for(int j=0; j<size; j++) {
210 for(int i=0; i<size; i++) {
211 Vector3 normal(-half_size, -((scalar_t)j + 0.5 - half_size), (scalar_t)i + 0.5 - half_size);
212 *ptr++ = pack_color32(vec_to_color(normal.normalized()));
215 normal_cubemap->unlock(CUBE_MAP_NX);
218 normal_cubemap->lock(CUBE_MAP_PY);
219 ptr = normal_cubemap->buffer;
220 for(int j=0; j<size; j++) {
221 for(int i=0; i<size; i++) {
222 Vector3 normal((scalar_t)i + 0.5 - half_size, half_size, (scalar_t)j + 0.5 - half_size);
223 *ptr++ = pack_color32(vec_to_color(normal.normalized()));
226 normal_cubemap->unlock(CUBE_MAP_PY);
229 normal_cubemap->lock(CUBE_MAP_NY);
230 ptr = normal_cubemap->buffer;
231 for(int j=0; j<size; j++) {
232 for(int i=0; i<size; i++) {
233 Vector3 normal((scalar_t)i + 0.5 - half_size, -half_size, -((scalar_t)j + 0.5 - half_size));
234 *ptr++ = pack_color32(vec_to_color(normal.normalized()));
237 normal_cubemap->unlock(CUBE_MAP_NY);
240 normal_cubemap->lock(CUBE_MAP_PZ);
241 ptr = normal_cubemap->buffer;
242 for(int j=0; j<size; j++) {
243 for(int i=0; i<size; i++) {
244 Vector3 normal((scalar_t)i + 0.5 - half_size, -((scalar_t)j + 0.5 - half_size), half_size);
245 *ptr++ = pack_color32(vec_to_color(normal.normalized()));
248 normal_cubemap->unlock(CUBE_MAP_PZ);
251 normal_cubemap->lock(CUBE_MAP_NZ);
252 ptr = normal_cubemap->buffer;
253 for(int j=0; j<size; j++) {
254 for(int i=0; i<size; i++) {
255 Vector3 normal(-((scalar_t)i + 0.5 - half_size), -((scalar_t)j + 0.5 - half_size), -half_size);
256 *ptr++ = pack_color32(vec_to_color(normal.normalized()));
259 normal_cubemap->unlock(CUBE_MAP_NZ);
263 bool is_cubemap(const char *fname) {
264 FILE *fp = fopen(fname, "r");
270 fread(idstr, 1, 4, fp);
274 return strcmp(idstr, "CUBE") == 0;
277 Texture *load_cubemap(const char *fname) {
278 FILE *fp = fopen(fname, "r");
280 error("could not open %s", fname);
284 if(!is_cubemap(fname)) {
285 error("%s is not a cubemap", fname);
291 unsigned int cube_size = 0;
292 unsigned long xsz = 0, ysz = 0;
295 fgets(line, 512, fp); // skip file id & text description
297 if(fgets(line, 512, fp) && isdigit(*line)) {
298 cube_size = atoi(line);
301 for(int i=0; i<6; i++) {
302 if(!fgets(line, 512, fp)) {
303 error("%s is not a complete cubemap file, EOF encountered", fname);
307 if(line[strlen(line)-1] == '\n') {
308 line[strlen(line)-1] = 0;
312 if(!(img[i] = load_image(line, &x, &y))) {
313 error("cubemap %s requires %s, which cannot be opened", fname, line);
317 if(i > 0 && (x != xsz || y != ysz)) {
318 error("inconsistent cubemap %s, image sizes differ", fname);
325 error("cubemap %s contains non-square textures", fname);
333 for(int i=0; i<6; i++) {
334 if(img[i]) free_image(img[i]);
339 if(xsz != cube_size) {
340 warning("cubemap %s loaded correctly, but wrong size in the header", fname);
343 Texture *cube = new Texture(cube_size, cube_size, TEX_CUBE);
345 CubeMapFace faces[] = {
346 CUBE_MAP_PX, CUBE_MAP_NX,
347 CUBE_MAP_PY, CUBE_MAP_NY,
348 CUBE_MAP_PZ, CUBE_MAP_NZ
351 for(int i=0; i<6; i++) {
352 cube->lock(faces[i]);
353 memcpy(cube->buffer, img[i], cube_size * cube_size * sizeof(Pixel));
354 cube->unlock(faces[i]);