tge/engine/dgl/gTexManager.h
2017-04-17 06:17:10 -06:00

469 lines
17 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;
// we have a slightly more complicated versions of lock() and unlock() for debug, so they are in gTexManager.cc
#if defined(TORQUE_DEBUG)
void lock();
void unlock();
#else
inline void lock()
{
if ( object )
object->refCount++;
}
inline void unlock()
{
// Do nothing if the manager isn't active or we do not have an object
if(!TextureManager::isActive() || (object == NULL))
return;
object->refCount--;
if (object->holding == false)
{
if(!object->refCount)
TextureManager::freeTexture(object);
}
else
{
AssertISV(object->refCount >= 0, avar("Texture holding out of balance: %d (0x%x)", object->refCount, object->refCount));
}
object = NULL;
}
#endif
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()
{
TextureManager::refresh(object);
}
void refresh(GBitmap* bmp)
{
AssertFatal(object->type == TerrainTexture, "Error, only terrain textures may be refreshed in this manner!");
TextureManager::refresh(object, bmp);
}
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); }
const GBitmap* getBitmap() const { return (object ? object->bitmap : NULL); }
bool isValid() const { return (object ? true : false); }
/// 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_