428 lines
15 KiB
C++
Executable File
428 lines
15 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef _GTEXMANAGER_H_
|
|
#define _GTEXMANAGER_H_
|
|
|
|
#ifndef _PLATFORM_H_
|
|
#include "platform/platform.h"
|
|
#endif
|
|
|
|
// to be removed at some point, hopefully...
|
|
#ifndef _PLATFORMGL_H_
|
|
|
|
#include "platform/platformAssert.h"
|
|
#include "platform/platformGL.h"
|
|
|
|
#endif
|
|
|
|
//-------------------------------------- Forward Decls.
|
|
class GBitmap;
|
|
|
|
//------------------------------------------------------------------------------
|
|
//-------------------------------------- TextureHandle
|
|
//
|
|
|
|
/// Enumerated values for different types of textures
|
|
/// Generally speaking, all textures will have mipmaps extruded automatically
|
|
/// and use linear magnification and linear_mipmap_nearest minification filtering
|
|
/// unless trilinear filtering is enabled. Then, the minification is linear_maipmap_linear.
|
|
/// Most texture types have their bitmap data destroyed after texture creation.
|
|
/// Also, detail settings affect any of the textures except the ones noted.
|
|
/// Detail settings are based on the mip maps, so if the detail level is turned down,
|
|
/// the highest resolution detail map will NOT be the one that is in the actual
|
|
/// texture file, but one that has been averaged down appropriately.
|
|
///
|
|
/// @note "Small textures" are a different set of textures, dynamically created with
|
|
/// certain texture types whose first mip level is the same as the 4th mip level
|
|
/// of the first texture. So, the full res small texture will be the same as the
|
|
/// 4th averaged down version of the origional texture.
|
|
enum TextureHandleType
|
|
{
|
|
BitmapTexture = 0, ///< Regular bitmap - does not to be pow2, but will be converted to pow2, only 1 mip level
|
|
BitmapKeepTexture, ///< Same as BitmapTexture, but the data will be kept after creation
|
|
BitmapNoDownloadTexture, ///< Same as BitmapTexture except data will not be loaded to OpenGL and cannot be "bound"
|
|
RegisteredTexture, ///< INTERNAL USE ONLY - texture that has already been created and is just being reinstated
|
|
MeshTexture, ///< Has "small textures"
|
|
TerrainTexture, ///< OFF LIMITS - for terrain engine use only. You WILL cause problems if you use this type
|
|
SkyTexture, ///< Hooks into the sky texture detail level
|
|
InteriorTexture, ///< Has "small textures" and hooks into the interior texture detail level
|
|
BumpTexture, ///< Same as DetailTexture, except colors are halved
|
|
InvertedBumpTexture, ///< Same as BumpTexture, except colors are then inverted
|
|
|
|
DetailTexture, ///< If not palettized, will extrude mipmaps, only used for terrain detail maps
|
|
ZeroBorderTexture ///< Clears the border of the texture on all mip levels
|
|
};
|
|
|
|
|
|
class TextureObject
|
|
{
|
|
public:
|
|
TextureObject *next;
|
|
TextureObject *prev;
|
|
TextureObject *hashNext;
|
|
|
|
GLuint texGLName; ///< GL-Bindable texture index
|
|
GLuint smallTexGLName; ///< @see TextureHandleType
|
|
|
|
#ifdef TORQUE_GATHER_METRICS
|
|
U32 textureSpace;
|
|
#endif
|
|
|
|
StringTableEntry texFileName;
|
|
/// The actual bitmap data of the texture
|
|
/// @note this is usually destroyed once the texture has been loaded,
|
|
/// except in the case of BitmapKeepTexture, so any changes that might
|
|
/// need to be made to it need to be done within the loading code
|
|
GBitmap * bitmap;
|
|
|
|
U32 texWidth;
|
|
U32 texHeight;
|
|
|
|
U32 bitmapWidth;
|
|
U32 bitmapHeight;
|
|
|
|
U32 downloadedWidth;
|
|
U32 downloadedHeight;
|
|
|
|
TextureHandleType type;
|
|
bool filterNearest;
|
|
bool clamp;
|
|
bool holding;
|
|
S32 refCount;
|
|
};
|
|
|
|
typedef void (*TextureEventCallback)(const U32 eventCode, void *userData);
|
|
|
|
struct TextureManager
|
|
{
|
|
// additional functions for refreshing the textures, reloading larger
|
|
// mip levels, etc, will go in here, as well as delay-load functions.
|
|
friend class TextureHandle;
|
|
friend class InteriorLMManager;
|
|
friend struct TextureDictionary;
|
|
|
|
private:
|
|
|
|
static bool smTextureManagerActive;
|
|
|
|
/// Loads a texture from file, and returns the result of registerTexture
|
|
static TextureObject* loadTexture(const char *textureName, TextureHandleType type, bool clampToEdge, bool checkOnly = false);
|
|
|
|
/// Inserts a texture into the hash table, and gives the texture an OpenGL name
|
|
static TextureObject* registerTexture(const char *textureName, const GBitmap *data, bool clampToEdge);
|
|
|
|
/// Inserts a texture into the hash table, and gives the texture an OpenGL name
|
|
static TextureObject* registerTexture(const char *textureName, GBitmap *data, TextureHandleType type, bool clampToEdge);
|
|
|
|
/// Deletes the texture data and removes the texture from the texture dictionary hash table and OpenGL
|
|
static void freeTexture(TextureObject *to);
|
|
|
|
/// Creates the OpenGL texture and sets all related GL states.
|
|
static bool createGLName(GBitmap *pb, bool clampToEdge, U32 firstMip, TextureHandleType type, TextureObject* obj);
|
|
|
|
static void refresh(TextureObject *to);
|
|
static void refresh(TextureObject *to, GBitmap*);
|
|
|
|
static GBitmap* createMipBitmap(const GBitmap* pBitmap);
|
|
|
|
/// Just in case the texture specified does not have sides of power-of-2,
|
|
/// this function will copy the texture data into a new texture that IS power-of-2
|
|
/// and fill in the empty areas with the adjacent pixel
|
|
static GBitmap* createPaddedBitmap(GBitmap* pBitmap);
|
|
|
|
|
|
public:
|
|
static void create();
|
|
static void preDestroy();
|
|
static void destroy();
|
|
static bool isActive() { return smTextureManagerActive; }
|
|
|
|
/// @name Zombification
|
|
/// This pair of functions is a flush() equivalent. To flush
|
|
/// the cache, call:
|
|
/// makeZombie(); /* blah blah blah */ resurrect();
|
|
/// Note that NO drawing must take place until resurrect is
|
|
/// called. The manager is a stinking corpse at this point.
|
|
/// The split is necessary to support changing the OpenGL
|
|
/// device in the "right way". This way glDeleteTexture is
|
|
/// called on the original device rather than on the new
|
|
/// device, as a flush() call would necessitate.
|
|
/// @{
|
|
|
|
///
|
|
static void makeZombie();
|
|
static void resurrect();
|
|
/// @}
|
|
|
|
/// Added for convenience when you don't need to worry about the above problems. (Zombification)
|
|
static void flush();
|
|
static bool smIsZombie; ///< Is the texture manager a skulking undead brain-eating zombie from the great beyond? I sure hope not...
|
|
|
|
#ifdef TORQUE_GATHER_METRICS
|
|
static void dumpStats();
|
|
#endif
|
|
|
|
enum EventCodes
|
|
{
|
|
BeginZombification = 0,
|
|
CacheResurrected = 1
|
|
};
|
|
|
|
static U32 registerEventCallback(TextureEventCallback, void *userData);
|
|
static void unregisterEventCallback(const U32 callbackKey);
|
|
|
|
private:
|
|
static void postTextureEvent(const U32);
|
|
static bool smUseSmallTextures;
|
|
|
|
public:
|
|
static const char * csmTexturePrefix;
|
|
|
|
static void setSmallTexturesActive(const bool t) { smUseSmallTextures = t; }
|
|
static bool areSmallTexturesActive() { return smUseSmallTextures; }
|
|
static GBitmap *loadBitmapInstance(const char *textureName, bool recurse = true);
|
|
|
|
#ifdef TORQUE_GATHER_METRICS
|
|
static U32 smTextureSpaceLoaded;
|
|
static U32 smTextureCacheMisses;
|
|
|
|
static F32 getResidentFraction();
|
|
#endif
|
|
};
|
|
|
|
/// This is the main texture manager interface. Texturing can be
|
|
/// a bit complicated, but if you follow these easy steps, it is
|
|
/// really quite simple!
|
|
///
|
|
/// In order to use a texture on disk, first you must create a
|
|
/// TextureHandle data structure for it.
|
|
/// @code
|
|
/// TextureHandle handle = TextureHandle("pathToTexture", textureType);
|
|
/// @endcode
|
|
/// See the documentation on the different enumerated types for more info
|
|
/// on texture types.
|
|
///
|
|
/// Ok, now you have your texture loaded into video memory or ram,
|
|
/// whichever is chooses. In order to tell OpenGL to use your texture,
|
|
/// you have to bind it. GL_TEXTURE_2D is the type of texture you're
|
|
/// binding - a 2 dimisional texture. Also note that you only have
|
|
/// to do this if you are using direct OpenGL commands to draw rather
|
|
/// than dgl. Dgl manages the below on it's own so you don't have to worry about it.
|
|
/// @code
|
|
/// glBindTexture(GL_TEXTURE_2D, handle.getGLName());
|
|
/// @endcode
|
|
/// Now you can begin to draw you texture. If you havn't already,
|
|
/// make sure you make a call to glEnable(GL_TEXTURE_2D); before
|
|
/// you start drawing and a call to glDisable(GL_TEXTURE_2D); when
|
|
/// you're done. Failure to call glEnable will cause the texture not
|
|
/// to draw, and failure to call glDisable will probably case
|
|
/// an assert in debug mode and ugly artifacts in release.
|
|
///
|
|
/// If you are going through dgl, all you need is the TextureHandle and
|
|
/// some points. See the dgl documentation for more info on each
|
|
/// individual function in the dgl library. However, most dgl functions
|
|
/// will take a TextureObject data type. And, it just so happens that
|
|
/// a TextureHandle has a TextureObject! It also has an
|
|
/// operator TextureObject*(), which lets you cast a TextureHandle to
|
|
/// a TextureObject. That means that all you have to do is ignore
|
|
/// the TextureObject parameter and just give it a TextureHandle.
|
|
///
|
|
/// Some tips on texture performance:
|
|
///
|
|
/// Instead of using hard-coded paths, use a hook to a console variable.
|
|
/// You will probably change the directory structure for your game,
|
|
/// and that means that you will have to go back to all of the hardcoded
|
|
/// paths and change them by hand, then rebuild the engine. It is much
|
|
/// better to use script variables since they are all in one place and
|
|
/// easier to change.
|
|
///
|
|
/// Add the path string for your texture to the StringTable. Doing so
|
|
/// helps in string lookups and faster string performance.
|
|
///
|
|
/// Don't create the texture every frame if at all possible. Make it
|
|
/// a global variable if you have to - just don't load every frame.
|
|
/// Loading data off of the disk every frame WILL cause massive
|
|
/// performance loss and should be avoided at all costs. This is
|
|
/// not to mention the overhead of generating mip map levels
|
|
/// and uploading the texture into memory when the texture is created.
|
|
///
|
|
/// @note
|
|
/// Texture handles can be allocated in 2 ways - by name to be loaded
|
|
/// from disk, or by name to a dynamically generated texture
|
|
///
|
|
/// If you create a GBitmap and register it, the Texture manager
|
|
/// owns the pointer - so if you re-register a texture with the same
|
|
/// name, the texture manager will delete the second copy.
|
|
///
|
|
/// Also note the operator TextureObject*, as you can actually cast
|
|
/// a TextureHandle to a TextureObject* if necessary.
|
|
class TextureHandle
|
|
{
|
|
friend class DynamicTexture;
|
|
|
|
TextureObject *object;
|
|
void lock();
|
|
void unlock();
|
|
|
|
public:
|
|
TextureHandle() { object = NULL; }
|
|
|
|
TextureHandle(TextureObject *to)
|
|
{
|
|
object = to;
|
|
lock();
|
|
}
|
|
|
|
TextureHandle(const TextureHandle &th)
|
|
{
|
|
object = th.object;
|
|
lock();
|
|
}
|
|
|
|
TextureHandle(const char* textureName,
|
|
TextureHandleType type, // was =BitmapTexture - dc removed to eliminate overload confusion.
|
|
bool clampToEdge = false)
|
|
{
|
|
object = TextureManager::loadTexture(textureName, type, clampToEdge);
|
|
lock();
|
|
}
|
|
|
|
TextureHandle(const char* textureName,
|
|
const GBitmap* bmp,
|
|
bool clampToEdge = false)
|
|
{
|
|
object = TextureManager::registerTexture(textureName, bmp, clampToEdge);
|
|
lock();
|
|
}
|
|
|
|
TextureHandle(const char* textureName,
|
|
GBitmap* bmp,
|
|
TextureHandleType type,
|
|
bool clampToEdge = false)
|
|
{
|
|
object = TextureManager::registerTexture(textureName, bmp, type, clampToEdge);
|
|
lock();
|
|
}
|
|
|
|
~TextureHandle() { unlock(); }
|
|
|
|
TextureHandle& operator=(const TextureHandle &t)
|
|
{
|
|
unlock();
|
|
object = t.object;
|
|
lock();
|
|
return *this;
|
|
}
|
|
|
|
bool set(const char *textureName,
|
|
TextureHandleType type=BitmapTexture,
|
|
bool clampToEdge = false)
|
|
{
|
|
TextureObject* newObject = TextureManager::loadTexture(textureName, type, clampToEdge);
|
|
if (newObject != object)
|
|
{
|
|
unlock();
|
|
object = newObject;
|
|
lock();
|
|
}
|
|
return (object != NULL);
|
|
}
|
|
|
|
bool set(const char *textureName,
|
|
const GBitmap *data,
|
|
bool clampToEdge = false)
|
|
{
|
|
TextureObject* newObject = TextureManager::registerTexture(textureName, data, clampToEdge);
|
|
if (newObject != object)
|
|
{
|
|
unlock();
|
|
object = newObject;
|
|
lock();
|
|
}
|
|
return (object != NULL);
|
|
}
|
|
|
|
bool set(const char *textureName,
|
|
GBitmap *bmp,
|
|
TextureHandleType type,
|
|
bool clampToEdge = false)
|
|
{
|
|
TextureObject* newObject = TextureManager::registerTexture(textureName, bmp, type, clampToEdge);
|
|
if (newObject != object)
|
|
{
|
|
unlock();
|
|
object = newObject;
|
|
lock();
|
|
}
|
|
return (object != NULL);
|
|
}
|
|
|
|
bool operator==(const TextureHandle &t) const { return t.object == object; }
|
|
bool operator!=(const TextureHandle &t) const { return t.object != object; }
|
|
|
|
void setClamp(const bool);
|
|
void setFilterNearest();
|
|
|
|
void refresh();
|
|
void refresh(GBitmap*);
|
|
operator TextureObject*() { return object; }
|
|
/// Returns the texture's filename if it exists
|
|
const char* getName() const { return (object ? object->texFileName : NULL); }
|
|
U32 getWidth() const { return (object ? object->bitmapWidth : 0UL); }
|
|
U32 getHeight() const { return (object ? object->bitmapHeight : 0UL); }
|
|
U32 getDownloadedWidth() const { return (object ? object->downloadedWidth : 0UL); }
|
|
U32 getDownloadedHeight() const { return (object ? object->downloadedHeight : 0UL); }
|
|
GBitmap* getBitmap() { return (object ? object->bitmap : NULL); }
|
|
/// Gets the OpenGL index of the texture for use in glBindTexture().
|
|
U32 getGLName() const;
|
|
};
|
|
|
|
#if defined(TORQUE_GATHER_METRICS) && TORQUE_GATHER_METRICS > 1
|
|
#ifndef _PLATFORMGL_H_
|
|
#if defined(TORQUE_OS_MAC)
|
|
#include "PlatformMacCarb/platformGL.h"
|
|
#elif defined(TORQUE_OS_WIN32)
|
|
#include "PlatformWin32/platformGL.h"
|
|
#endif
|
|
#endif
|
|
|
|
inline U32 TextureHandle::getGLName() const
|
|
{
|
|
if (!object)
|
|
return 0;
|
|
|
|
U32 useName = object->texGLName;
|
|
if (TextureManager::areSmallTexturesActive() && object->smallTexGLName != 0)
|
|
useName = object->smallTexGLName;
|
|
|
|
if (useName != 0)
|
|
{
|
|
GLboolean res;
|
|
glAreTexturesResident(1, &useName, &res);
|
|
if (res == GL_FALSE)
|
|
TextureManager::smTextureCacheMisses++;
|
|
}
|
|
|
|
return useName;
|
|
}
|
|
|
|
#else
|
|
|
|
inline U32 TextureHandle::getGLName() const
|
|
{
|
|
if (!object)
|
|
return 0;
|
|
|
|
U32 useName = object->texGLName;
|
|
if (TextureManager::areSmallTexturesActive() && object->smallTexGLName != 0)
|
|
useName = object->smallTexGLName;
|
|
|
|
return useName;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif // _GTEXMANAGER_H_
|