/* * NodeImageTexture.cpp * * Copyright (C) 1999 Stephen F. White * * Modified by Aaron Cram - Now uses DevIL to load textures * * 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 (see the file "COPYING" for details); if * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. */ #include #include "stdafx.h" #include "swt.h" #include "DuneApp.h" #include "resource.h" #include "NodeImageTexture.h" #include "Scene.h" #include "Proto.h" #include "FieldValue.h" #include "MFString.h" #include "SFBool.h" #include "SFInt32.h" #include "Field.h" #include "URL.h" #ifdef HAVE_LIBDEVIL # include #else # include "Image.h" #endif #include "Util.h" enum { IMG_STATUS_UNLOADED, IMG_STATUS_LOADED, IMG_STATUS_ERROR }; ProtoImageTexture::ProtoImageTexture(Scene *scene) : Proto(scene, "ImageTexture") { url.set( addExposedField(MFSTRING, "url", new MFString(), FF_URL, NULL)); repeatS.set( addField(SFBOOL, "repeatS", new SFBool(true))); repeatT.set( addField(SFBOOL, "repeatT", new SFBool(true))); #ifdef HAVE_TEXTUREIMAGE_MODE mode.set( addField(SFINT32, "mode", new SFInt32(0), new SFInt32(0), new SFInt32(3))); #endif } Node * ProtoImageTexture::create(Scene *scene) { return new NodeImageTexture(scene, this); } NodeImageTexture::NodeImageTexture(Scene *scene, Proto *def) : Node(scene, def) { _image = NULL; _textureWidth = 0; _textureHeight = 0; _imageStatus = IMG_STATUS_UNLOADED; _textureName = 0; _components = 0; _glColorMode = GL_RGB; // default: no transparency _scaleRequired = false; #ifdef HAVE_LIBDEVIL static bool il_needs_init = true; if (il_needs_init) { ilInit(); iluInit(); il_needs_init = false; ilEnable(IL_ORIGIN_SET); ilOriginFunc(IL_ORIGIN_LOWER_LEFT); } ilGenImages(1, &_imageName); ilBindImage(_imageName); #endif } NodeImageTexture::NodeImageTexture(const NodeImageTexture &node) : Node(node) { _image = NULL; _textureName = 0; // must load its own texture though _textureWidth = node._textureWidth; _textureHeight = node._textureHeight; _components=node._components; _glColorMode = node._glColorMode ; _imageStatus = node._imageStatus; _scaleRequired = node._scaleRequired; #ifdef HAVE_LIBDEVIL ilGenImages(1, &_imageName); ilBindImage(_imageName); // copy image data, if needed if (node._imageStatus == IMG_STATUS_LOADED) { if (ilCopyImage(node._imageName)) { _image = ilGetData(); if (_image) { _imageStatus = IMG_STATUS_LOADED; } else { _imageStatus = IMG_STATUS_ERROR; reportLoadError(NULL, NULL); } } else { _imageStatus = IMG_STATUS_ERROR; reportLoadError(NULL, NULL); } } #else int size = _textureWidth * _textureHeight * _components; // copy image data, if any if (node._image) { _image = new unsigned char[size]; memcpy(_image, node._image, size); } #endif } NodeImageTexture::~NodeImageTexture() { if (_textureName != 0) glDeleteTextures(1, &_textureName); #ifdef HAVE_LIBDEVIL ilDeleteImages(1, &_imageName); #else delete [] _image; #endif } void NodeImageTexture::setField(int field, FieldValue *value) { if (field == url_Index()) { _imageStatus = IMG_STATUS_UNLOADED; } Node::setField(field, value); } void NodeImageTexture::load() { MFString *urls = url(); int width, height; unsigned char *data = NULL; bool errorflag=true; char *lastCheckedPath = NULL; for (int i = 0; i < urls->getSize(); i++) { MyString path; URL url(_scene->getURL(), urls->getValue(i)); if (urls->getValue(i).length() == 0) continue; _scene->Download(url, &path); #ifdef HAVE_LIBDEVIL errorflag=true; if (ilLoadImage((char *)(const char *)path)) { width = ilGetInteger(IL_IMAGE_WIDTH); height = ilGetInteger(IL_IMAGE_HEIGHT); _components = ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL); if (_components == 1) { _glColorMode=GL_LUMINANCE; ilConvertImage(IL_LUMINANCE, IL_UNSIGNED_BYTE); } else if (_components == 2) { _glColorMode=GL_LUMINANCE_ALPHA; ilConvertImage(IL_LUMINANCE_ALPHA, IL_UNSIGNED_BYTE); } else if (_components == 3) { _glColorMode=GL_RGB; ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE); } else if (_components == 4) { _glColorMode=GL_RGBA; ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE); } else { reportLoadError((char *)(const char *)path, "Invalid image type: wrong number of components"); _imageStatus = IMG_STATUS_ERROR; return; } errorflag=false; break; } else lastCheckedPath = strdup(path); #else Image image; if (image.Open(path)) { width = image.GetWidth(); height = image.GetHeight(); _components = image.GetComponents(); if (_components==4) _glColorMode=GL_RGBA; else if (_components==3) _glColorMode = GL_RGB; else if (_components==2) _glColorMode = GL_LUMINANCE_ALPHA; else _glColorMode = GL_LUMINANCE; data = new unsigned char [width * height * _components]; image.Read(data); break; } #endif } #ifdef HAVE_LIBDEVIL if (errorflag) { _imageStatus = IMG_STATUS_ERROR; reportLoadError(lastCheckedPath, NULL); return; } #else if (data == NULL) { _imageStatus = IMG_STATUS_ERROR; return; } #endif _textureWidth = 1; _textureHeight = 1; while (_textureWidth < width) _textureWidth <<= 1; while (_textureHeight < height) _textureHeight <<= 1; #ifdef HAVE_LIBDEVIL if (width == _textureWidth && height == _textureHeight) { _scaleRequired = false; } else { _scaleRequired = true; iluImageParameter(ILU_FILTER, ILU_BILINEAR); if (!iluScale(_textureWidth, _textureHeight, _components)) { _imageStatus = IMG_STATUS_ERROR; reportLoadError(NULL, NULL); return; } } _image = ilGetData(); if (_image) { _imageStatus = IMG_STATUS_LOADED; } else { _imageStatus = IMG_STATUS_ERROR; reportLoadError(NULL, NULL); return; } #else delete [] _image; if (width == _textureWidth && height == _textureHeight) { _scaleRequired = false; _image = data; } else { _scaleRequired = true; _image = new unsigned char[_textureWidth * _textureHeight * _components]; glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1); gluScaleImage(_glColorMode, width, height, GL_UNSIGNED_BYTE, data, _textureWidth, _textureHeight, GL_UNSIGNED_BYTE, _image); delete [] data; } _imageStatus = IMG_STATUS_LOADED; #endif if (TheApp->isAnaglyphStereo()) { // change colors into grayscale values if ((_components == 3) || (_components == 4)) { int size = _textureWidth * _textureHeight * _components; for (int i = 0; i < size; i += _components) { int gray = (_image[i + 0] + _image[i + 1] + _image[i + 2]) / 3; _image[i + 0] = gray; _image[i + 1] = gray; _image[i + 2] = gray; } } } } #ifdef HAVE_LIBDEVIL // If a NULL pointer is passed in, this function will report any errors // that have happened inside the DevIL image library. Otherwise, it will // report whatever string is passed in. void NodeImageTexture::reportLoadError(char* filename, const char *error_msg) { // If the user hits the cancel button, all subsequent errors will be ignored. // This way, if the VRML file contains 100 broken images, the user won't have // to hit the OK button 100 times. // FIXME: This won't be reset until the program is restarted... static int mbReturn = 0; if (mbReturn == IDCANCEL) return; char errorstring[256]; swLoadString(IDS_IMAGE_TEXTURE_ERROR, errorstring, 255); char buf[4096]; if (error_msg) { if (filename) mysnprintf(buf, 4095, "%s: %s\n", filename, error_msg); else mysnprintf(buf, 4095, "%s\n", error_msg); } else { ILenum Error; while ((Error = ilGetError()) != IL_NO_ERROR) { if (filename) mysnprintf(buf, 4095, "%s: ilError #%d: %s\n", filename, Error, iluErrorString(Error)); else mysnprintf(buf, 4095, "ilError #%d: %s\n", Error, iluErrorString(Error)); mbReturn = swMessageBox(TheApp->mainWnd(), buf, errorstring, SW_MB_OKCANCEL, SW_MB_ERROR); } } } #endif void NodeImageTexture::bind() { if (_imageStatus == IMG_STATUS_UNLOADED) { load(); if (_textureName != 0) glDeleteTextures(1, &_textureName); _textureName = 0; } if (_imageStatus == IMG_STATUS_LOADED) { if (_textureName == 0) { glGenTextures(1, &_textureName); glBindTexture(GL_TEXTURE_2D, _textureName); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, _components, _textureWidth, _textureHeight, 0, _glColorMode, GL_UNSIGNED_BYTE, _image); GLenum error=glGetError(); if (error!=0) { /* what's wrong here with "invalid value" ? */ fprintf(stderr,"GL Error: %s\n",gluErrorString(error)); } } else { glBindTexture(GL_TEXTURE_2D, _textureName); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeatS()->getValue() ? GL_REPEAT : GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeatT()->getValue() ? GL_REPEAT : GL_CLAMP); glEnable(GL_TEXTURE_2D); #ifdef HAVE_TEXTUREIMAGE_MODE switch (mode()->getValue()) { case 1: glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); break; case 2: glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); break; case 3: glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); break; } #endif } } void NodeImageTexture::unbind() { glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); } int NodeImageTexture::isLoaded() { return _imageStatus == IMG_STATUS_LOADED; }