diff --git a/Makefiles/Makefile.SDL.OSX.GCC b/Makefiles/Makefile.SDL.OSX.GCC
index 1a28daf..056ba83 100644
--- a/Makefiles/Makefile.SDL.OSX.GCC
+++ b/Makefiles/Makefile.SDL.OSX.GCC
@@ -90,7 +90,7 @@ endif
HEADERS = Src/Supermodel.h Src/Games.h Src/OSD/SDL/Types.h
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
- $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
+ $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
$(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \
$(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \
$(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \
@@ -216,4 +216,4 @@ $(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp Src/Sound/MPEG/%.h
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
$(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp
- $(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
\ No newline at end of file
+ $(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
diff --git a/Makefiles/Makefile.SDL.UNIX.GCC b/Makefiles/Makefile.SDL.UNIX.GCC
index d4571bd..ff80255 100644
--- a/Makefiles/Makefile.SDL.UNIX.GCC
+++ b/Makefiles/Makefile.SDL.UNIX.GCC
@@ -92,7 +92,7 @@ endif
HEADERS = Src/Supermodel.h Src/Games.h Src/OSD/SDL/Types.h
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
- $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
+ $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
$(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \
$(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \
$(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \
diff --git a/Makefiles/Makefile.SDL.Win32.GCC b/Makefiles/Makefile.SDL.Win32.GCC
index 3cf451c..d347ebb 100644
--- a/Makefiles/Makefile.SDL.Win32.GCC
+++ b/Makefiles/Makefile.SDL.Win32.GCC
@@ -115,7 +115,7 @@ endif
#
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
- $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
+ $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
$(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \
$(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \
$(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \
@@ -233,4 +233,4 @@ $(OBJ_DIR)/%.o: Src/Pkgs/%.c
# To eliminate name conflicts, object files have the prefix "amp_" attached.
#
$(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp
- $(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
\ No newline at end of file
+ $(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
diff --git a/Makefiles/Makefile.SDL.Win32.MSVC b/Makefiles/Makefile.SDL.Win32.MSVC
index 15c4ac9..3c82a8b 100644
--- a/Makefiles/Makefile.SDL.Win32.MSVC
+++ b/Makefiles/Makefile.SDL.Win32.MSVC
@@ -146,7 +146,7 @@ endif
HEADERS = Src/Supermodel.h Src/OSD/SDL/Types.h
OBJ = $(OBJ_DIR)/PPCDisasm.obj $(OBJ_DIR)/Games.obj $(OBJ_DIR)/Config.obj $(OBJ_DIR)/INIFile.obj $(OBJ_DIR)/BlockFile.obj $(OBJ_DIR)/93C46.obj \
$(OBJ_DIR)/ROMLoad.obj $(OBJ_DIR)/unzip.obj $(OBJ_DIR)/ioapi.obj $(OBJ_DIR)/Error.obj $(OBJ_DIR)/glew.obj $(OBJ_DIR)/Shader.obj \
- $(OBJ_DIR)/Real3D.obj $(OBJ_DIR)/Render3D.obj $(OBJ_DIR)/Models.obj $(OBJ_DIR)/Render2D.obj $(OBJ_DIR)/TileGen.obj \
+ $(OBJ_DIR)/Real3D.obj $(OBJ_DIR)/Render3D.obj $(OBJ_DIR)/Models.obj $(OBJ_DIR)/TextureRefs.obj $(OBJ_DIR)/Render2D.obj $(OBJ_DIR)/TileGen.obj \
$(OBJ_DIR)/Model3.obj $(OBJ_DIR)/ppc.obj $(OBJ_DIR)/Main.obj $(OBJ_DIR)/Audio.obj $(OBJ_DIR)/Thread.obj $(OBJ_DIR)/SoundBoard.obj \
$(OBJ_DIR)/SCSP.obj $(OBJ_DIR)/SCSPDSP.obj $(OBJ_DIR)/68K.obj $(OBJ_DIR)/m68kcpu.obj $(OBJ_DIR)/m68kopnz.obj $(OBJ_DIR)/m68kopdm.obj \
$(OBJ_DIR)/m68kopac.obj $(OBJ_DIR)/m68kops.obj $(OBJ_DIR)/DSB.obj $(OBJ_DIR)/Z80.obj \
@@ -287,4 +287,4 @@ $(OBJ_DIR)/amp_%.obj: Src/Sound/MPEG/%.cpp Src/Sound/MPEG/%.h
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj
$(OBJ_DIR)/amp_%.obj: Src/Sound/MPEG/%.cpp
- $(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj
\ No newline at end of file
+ $(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj
diff --git a/Src/Graphics/Models.cpp b/Src/Graphics/Models.cpp
index d4b3bb5..e490710 100644
--- a/Src/Graphics/Models.cpp
+++ b/Src/Graphics/Models.cpp
@@ -611,39 +611,40 @@ bool CRender3D::InsertPolygon(ModelCache *Cache, const Poly *P)
}
// Begins caching a new model by resetting to the start of the local vertex buffer
-bool CRender3D::BeginModel(ModelCache *Cache)
+struct VBORef *CRender3D::BeginModel(ModelCache *Cache)
{
- int m;
+ struct VBORef *Model;
+
+ unsigned m = Cache->numModels;
// Determine whether we've exceeded the model cache limits (caller will have to recache)
- if (Cache->numModels >= Cache->maxModels)
- return FAIL; // too many models
- //return ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static");
+ if (m >= Cache->maxModels)
+ {
+ //ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static");
+ return NULL;
+ }
- m = Cache->numModels;
+ Model = &(Cache->Models[m]);
// Reset to the beginning of the local vertex buffer
for (int i = 0; i < 2; i++)
Cache->curVertIdx[i] = 0;
- // Clear the VBO reference to 0
- memset(&(Cache->Models[m]), 0, sizeof(VBORef));
+ // Clear the VBO reference to 0 and clear texture references
+ memset(Model, 0, sizeof(VBORef) - sizeof(CTextureRefs));
+ Model->texRefs.Clear();
// Record starting index of first opaque polygon in VBO (alpha poly index will be re-set in EndModel())
- Cache->Models[m].index[POLY_STATE_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat));
- Cache->Models[m].index[POLY_STATE_ALPHA] = Cache->Models[m].index[POLY_STATE_NORMAL];
+ Model->index[POLY_STATE_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat));
+ Model->index[POLY_STATE_ALPHA] = Model->index[POLY_STATE_NORMAL];
- return OKAY;
+ return Model;
}
// Uploads all vertices from the local vertex buffer to the VBO, sets up the VBO reference, updates the LUT
-struct VBORef *CRender3D::EndModel(ModelCache *Cache, int lutIdx, UINT16 texOffset)
+void CRender3D::EndModel(ModelCache *Cache, struct VBORef *Model, int lutIdx, UINT16 texOffset)
{
- struct VBORef *Model;
- int m;
-
- m = Cache->numModels++;
- Model = &(Cache->Models[m]);
+ int m = Cache->numModels++;
// Record the number of vertices, completing the VBORef
for (int i = 0; i < 2; i++)
@@ -669,9 +670,6 @@ struct VBORef *CRender3D::EndModel(ModelCache *Cache, int lutIdx, UINT16 texOffs
if (Cache->lut[lutIdx] >= 0) // another texture offset state already cached
Model->nextTexOffset = &(Cache->Models[Cache->lut[lutIdx]]);
Cache->lut[lutIdx] = m;
-
- // Return a pointer to the cached model's VBO reference
- return Model;
}
/*
@@ -699,7 +697,8 @@ struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOf
return NULL;
// Start constructing a new model
- if (FAIL == BeginModel(Cache))
+ struct VBORef *Model = BeginModel(Cache);
+ if (NULL == Model)
return NULL; // too many models!
// Cache all polygons
@@ -758,7 +757,13 @@ struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOf
// Decode the texture
if (texEnable)
- DecodeTexture(texFormat, texBaseX, texBaseY, texWidth, texHeight);
+ {
+ // If model cache is static, record texture reference in model cache entry for later decoding.
+ // If cache is dynamic, or if it's not possible to record the texture reference (due to lack of
+ // memory) then decode the texture now.
+ if (Cache->dynamic || !Model->texRefs.AddRef(texFormat, texBaseX, texBaseY, texWidth, texHeight))
+ DecodeTexture(texFormat, texBaseX, texBaseY, texWidth, texHeight);
+ }
// Polygon normal is in upper 24 bits: sign + 1.22 fixed point
P.n[0] = (GLfloat) (((INT32)P.header[1])>>8) * (1.0f/4194304.0f);
@@ -826,7 +831,8 @@ struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOf
}
// Finish model and enter it into the LUT
- return EndModel(Cache,lutIdx,texOffset);
+ EndModel(Cache,Model,lutIdx,texOffset);
+ return Model;
}
diff --git a/Src/Graphics/Render3D.cpp b/Src/Graphics/Render3D.cpp
index 02fe420..3649cad 100644
--- a/Src/Graphics/Render3D.cpp
+++ b/Src/Graphics/Render3D.cpp
@@ -160,7 +160,6 @@
#define ISINF(x) (std::isinf(x))
#endif
-
/******************************************************************************
Definitions and Constants
******************************************************************************/
@@ -427,12 +426,8 @@ void CRender3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned
}
}
}
-
- ClearModelCache(&VROMCache);
- ClearModelCache(&PolyCache);
}
-
/******************************************************************************
Real3D Address Translation
@@ -625,6 +620,11 @@ bool CRender3D::DrawModel(UINT32 modelAddr)
}
}
+ // If cache is static then decode all the texture references contained in the cached model
+ // before rendering (models in dynamic cache will have been decoded already in CacheModel)
+ if (!Cache->dynamic)
+ ModelRef->texRefs.DecodeAllTextures(this);
+
// Add to display list
return AppendDisplayList(Cache, false, ModelRef);
}
@@ -957,10 +957,6 @@ void CRender3D::RenderViewport(UINT32 addr, int pri)
stackDepth = 0;
listDepth = 0;
- // Descend down the node link: Use stack machine to traverse display list
- //ClearStack();
- //StackMachine(nodeAddr);
-
// Descend down the node link: Use recursive traversal
DescendNodePtr(nodeAddr);
}
diff --git a/Src/Graphics/Render3D.h b/Src/Graphics/Render3D.h
index f79160b..3ce82e5 100644
--- a/Src/Graphics/Render3D.h
+++ b/Src/Graphics/Render3D.h
@@ -67,13 +67,17 @@ struct Poly
* vertices: normal and alpha. Copies of the model with different texture
* offsets applied are searchable via the linked list of texture offset states.
*/
+
struct VBORef
{
- unsigned index[2]; // index of model polygons in VBO
- unsigned numVerts[2];// number of vertices
- unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
+ unsigned index[2]; // index of model polygons in VBO
+ unsigned numVerts[2]; // number of vertices
+ unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
+
struct VBORef *nextTexOffset; // linked list of models with different texture offset states
- UINT16 texOffset; // texture offset data for this model
+ UINT16 texOffset; // texture offset data for this model
+
+ CTextureRefs texRefs; // unique texture references contained in this model
};
// Display list items: model instances and viewport settings
@@ -196,6 +200,8 @@ public:
*/
class CRender3D
{
+ friend class CTextureRefs;
+
public:
/*
* RenderFrame(void):
@@ -299,7 +305,7 @@ public:
*/
CRender3D(void);
~CRender3D(void);
-
+
private:
/*
* Private Members
@@ -315,8 +321,8 @@ private:
void ClearDisplayList(ModelCache *Cache);
bool InsertPolygon(ModelCache *cache, const Poly *p);
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
- bool BeginModel(ModelCache *cache);
- struct VBORef *EndModel(ModelCache *cache, int lutIdx, UINT16 texOffset);
+ struct VBORef *BeginModel(ModelCache *cache);
+ void EndModel(ModelCache *cache, struct VBORef *Model, int lutIdx, UINT16 texOffset);
struct VBORef *CacheModel(ModelCache *cache, int lutIdx, UINT16 texOffset, const UINT32 *data);
struct VBORef *LookUpModel(ModelCache *cache, int lutIdx, UINT16 texOffset);
void ClearModelCache(ModelCache *cache);
diff --git a/Src/Graphics/TextureRefs.cpp b/Src/Graphics/TextureRefs.cpp
new file mode 100755
index 0000000..072d594
--- /dev/null
+++ b/Src/Graphics/TextureRefs.cpp
@@ -0,0 +1,329 @@
+/**
+ ** Supermodel
+ ** A Sega Model 3 Arcade Emulator.
+ ** Copyright 2011 Bart Trzynadlowski, Nik Henson
+ **
+ ** This file is part of Supermodel.
+ **
+ ** Supermodel 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 3 of the License, or (at your option)
+ ** any later version.
+ **
+ ** Supermodel 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 Supermodel. If not, see .
+ **/
+
+/*
+ * CTextureRefs.cpp
+ *
+ * Class that tracks unique texture references, eg in a cached model.
+ *
+ * Texture references are stored internally as a 27-bit field (3 bits for format, 6 bits each for x, y, width & height) to save space.
+ *
+ * A pre-allocated array is used for storing up to TEXREFS_ARRAY_SIZE texture references. When that limit is exceeded, it switches
+ * to using a hashset to store the texture references, but this requires extra memory allocation.
+ */
+
+#include "Supermodel.h"
+
+CTextureRefs::CTextureRefs() : m_size(0), m_hashCapacity(0), m_hashEntries(NULL)
+{
+ //
+}
+
+CTextureRefs::~CTextureRefs()
+{
+ DeleteAllHashEntries();
+}
+
+unsigned CTextureRefs::GetSize() const
+{
+ return m_size;
+}
+
+void CTextureRefs::Clear()
+{
+ // Delete all hash entries
+ DeleteAllHashEntries();
+ m_size = 0;
+ m_hashCapacity = 0;
+ m_hashEntries = NULL;
+}
+
+bool CTextureRefs::ContainsRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
+{
+ // Pack texture reference into bitfield
+ unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
+
+ // Check if using array or hashset
+ if (m_size <= TEXREFS_ARRAY_SIZE)
+ {
+ // See if texture reference held in array
+ for (unsigned i = 0; i < m_size; i++)
+ {
+ if (texRef == m_array[i])
+ return true;
+ }
+ return false;
+ }
+ else
+ // See if texture reference held in hashset
+ return HashContains(texRef);
+}
+
+bool CTextureRefs::AddRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
+{
+ // Pack texture reference into bitfield
+ unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
+
+ // Check if using array or hashset
+ if (m_size <= TEXREFS_ARRAY_SIZE)
+ {
+ // See if already held in array, if so nothing to do
+ for (unsigned i = 0; i < m_size; i++)
+ {
+ if (texRef == m_array[i])
+ return true;
+ }
+ // If not, check if array is full
+ if (m_size == TEXREFS_ARRAY_SIZE)
+ {
+ // If so, set initial hashset capacity to 47 to initialize it
+ UpdateHashCapacity(47);
+ // Copy array into hashset
+ for (unsigned i = 0; i < TEXREFS_ARRAY_SIZE; i++)
+ AddToHash(m_array[i]);
+ // Add texture reference to hashset
+ AddToHash(texRef);
+ }
+ else
+ {
+ // Add texture reference to array
+ m_array[m_size] = texRef;
+ m_size++;
+ }
+ return true;
+ }
+ else
+ // Add texture reference to hashset
+ return AddToHash(texRef);
+}
+
+bool CTextureRefs::RemoveRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
+{
+ // Pack texture reference into bitfield
+ unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
+
+ // Check if using array or hashset
+ if (m_size <= TEXREFS_ARRAY_SIZE)
+ {
+ for (unsigned i = 0; i < m_size; i++)
+ {
+ if (texRef == m_array[i])
+ {
+ for (unsigned j = i + 1; j < m_size; j++)
+ m_array[j - 1] = m_array[j];
+ m_size--;
+ return true;
+ }
+ }
+ return false;
+ }
+ else
+ {
+ // Remove texture reference from hashset
+ bool removed = RemoveFromHash(texRef);
+
+ // See if should switch back to array
+ if (m_size == TEXREFS_ARRAY_SIZE)
+ {
+ // Loop through all hash entries and copy texture references into array
+ unsigned j = 0;
+ for (unsigned i = 0; i < m_hashCapacity; i++)
+ {
+ for (HashEntry *entry = m_hashEntries[i]; entry; entry = entry->nextEntry)
+ m_array[j++] = entry->texRef;
+ }
+ // Delete all hash entries
+ DeleteAllHashEntries();
+ }
+ return removed;
+ }
+}
+
+void CTextureRefs::DecodeAllTextures(CRender3D *Render3D)
+{
+ // Check if using array or hashset
+ if (m_size <= TEXREFS_ARRAY_SIZE)
+ {
+ // Loop through elements in array and call CRender3D::DecodeTexture
+ for (unsigned i = 0; i < m_size; i++)
+ {
+ // Unpack texture reference from bitfield
+ unsigned texRef = m_array[i];
+ unsigned fmt = texRef>>24;
+ unsigned x = (texRef>>13)&0x7E0;
+ unsigned y = (texRef>>7)&0x7E0;
+ unsigned width = (texRef>>1)&0x7E0;
+ unsigned height = (texRef<<5)&0x7E0;
+ Render3D->DecodeTexture(fmt, x, y, width, height);
+ }
+ }
+ else
+ {
+ // Loop through all hash entriesa and call CRender3D::DecodeTexture
+ for (unsigned i = 0; i < m_hashCapacity; i++)
+ {
+ for (HashEntry *entry = m_hashEntries[i]; entry; entry = entry->nextEntry)
+ {
+ // Unpack texture reference from bitfield
+ unsigned texRef = entry->texRef;
+ unsigned fmt = texRef>>24;
+ unsigned x = (texRef>>13)&0x7E0;
+ unsigned y = (texRef>>7)&0x7E0;
+ unsigned width = (texRef>>1)&0x7E0;
+ unsigned height = (texRef<<5)&0x7E0;
+ Render3D->DecodeTexture(fmt, x, y, width, height);
+ }
+ }
+ }
+}
+
+bool CTextureRefs::UpdateHashCapacity(unsigned capacity)
+{
+ unsigned oldCapacity = m_hashCapacity;
+ HashEntry **oldEntries = m_hashEntries;
+ // Update capacity and create new empty entries array
+ m_hashCapacity = capacity;
+ m_hashEntries = new(std::nothrow) HashEntry*[capacity];
+ if (!m_hashEntries)
+ return false;
+ memset(m_hashEntries, NULL, capacity * sizeof(HashEntry*));
+ if (oldEntries)
+ {
+ // Redistribute entries into new entries array
+ for (unsigned i = 0; i < oldCapacity; i++)
+ {
+ HashEntry *entry = oldEntries[i];
+ while (entry)
+ {
+ HashEntry *nextEntry = entry->nextEntry;
+ unsigned hash = entry->texRef % capacity;
+ entry->nextEntry = m_hashEntries[hash];
+ m_hashEntries[hash] = entry;
+ entry = nextEntry;
+ }
+ }
+ // Delete old entries array
+ delete[] oldEntries;
+ }
+ return true;
+}
+
+HashEntry *CTextureRefs::CreateHashEntry(unsigned texRef, bool &hashCapacityUpdated)
+{
+ // Update size and increase hash capacity if required
+ m_size++;
+ hashCapacityUpdated = m_size >= m_hashCapacity;
+ if (hashCapacityUpdated)
+ {
+ if (m_hashCapacity < 89)
+ UpdateHashCapacity(89); // Capacity of 89 gives good sequence of mostly prime capacities (89, 179, 359, 719, 1439, 2879 etc)
+ else
+ UpdateHashCapacity(2 * m_hashCapacity + 1);
+ }
+ return new(std::nothrow) HashEntry(texRef);
+}
+
+void CTextureRefs::DeleteHashEntry(HashEntry *entry)
+{
+ // Update size and delete hash entry
+ m_size--;
+ delete entry;
+}
+
+void CTextureRefs::DeleteAllHashEntries()
+{
+ if (!m_hashEntries)
+ return;
+ // Delete all hash entries and their storage
+ for (unsigned i = 0; i < m_hashCapacity; i++)
+ {
+ HashEntry *entry = m_hashEntries[i];
+ if (entry)
+ delete entry;
+ }
+ delete[] m_hashEntries;
+}
+
+bool CTextureRefs::AddToHash(unsigned texRef)
+{
+ // Convert texture reference to hash value
+ unsigned hash = texRef % m_hashCapacity;
+ // Loop through linked list for hash value and see if have texture reference already
+ HashEntry *headEntry = m_hashEntries[hash];
+ HashEntry *entry = headEntry;
+ while (entry && texRef != entry->texRef)
+ entry = entry->nextEntry;
+ // If found, nothing to do
+ if (entry)
+ return true;
+ // Otherwise, create new hash entry for texture reference
+ bool hashCapacityUpdated;
+ entry = CreateHashEntry(texRef, hashCapacityUpdated);
+ // If couldn't create entry (ie out of memory), let caller know
+ if (!entry)
+ return false;
+ if (hashCapacityUpdated)
+ {
+ // If hash capacity was increased recalculate hash value
+ hash = texRef % m_hashCapacity;
+ headEntry = m_hashEntries[hash];
+ }
+ // Store hash entry in linked list for hash value
+ entry->nextEntry = headEntry;
+ m_hashEntries[hash] = entry;
+ return true;
+}
+
+bool CTextureRefs::RemoveFromHash(unsigned texRef)
+{
+ // Convert texture reference to hash value
+ unsigned hash = texRef % m_hashCapacity;
+ // Loop through linked list for hash value and see if have texture reference
+ HashEntry *entry = m_hashEntries[hash];
+ HashEntry *prevEntry = NULL;
+ while (entry && texRef != entry->texRef)
+ {
+ prevEntry = entry;
+ entry = entry->nextEntry;
+ }
+ // If not found, nothing to do
+ if (!entry)
+ return false;
+ // Otherwise, remove entry from linked list for hash value
+ if (prevEntry)
+ prevEntry->nextEntry = entry->nextEntry;
+ else
+ m_hashEntries[hash] = entry->nextEntry;
+ // Delete hash entry storage
+ DeleteHashEntry(entry);
+ return true;
+}
+
+bool CTextureRefs::HashContains(unsigned texRef) const
+{
+ // Convert texture reference to hash value
+ unsigned hash = texRef % m_hashCapacity;
+ // Loop through linked list for hash value and see if have texture reference
+ HashEntry *entry = m_hashEntries[hash];
+ while (entry && texRef != entry->texRef)
+ entry = entry->nextEntry;
+ return !!entry;
+}
diff --git a/Src/Graphics/TextureRefs.h b/Src/Graphics/TextureRefs.h
new file mode 100755
index 0000000..9f65106
--- /dev/null
+++ b/Src/Graphics/TextureRefs.h
@@ -0,0 +1,164 @@
+/**
+ ** Supermodel
+ ** A Sega Model 3 Arcade Emulator.
+ ** Copyright 2011 Bart Trzynadlowski, Nik Henson
+ **
+ ** This file is part of Supermodel.
+ **
+ ** Supermodel 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 3 of the License, or (at your option)
+ ** any later version.
+ **
+ ** Supermodel 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 Supermodel. If not, see .
+ **/
+
+/*
+ * CTextureRefs.h
+ *
+ * Class that tracks unique texture references, eg in a cached model.
+ */
+
+#ifndef INCLUDED_TEXTUREREFS_H
+#define INCLUDED_TEXTUREREFS_H
+
+#define TEXREFS_ARRAY_SIZE 12
+
+// Hash entry that holds a texture reference in the hashset
+struct HashEntry
+{
+ const unsigned texRef; // Texture reference as a bitfield
+ HashEntry *nextEntry; // Next entry with the same hash
+
+ HashEntry(unsigned theTexRef) : texRef(theTexRef), nextEntry(NULL) { }
+};
+
+class CRender3D;
+
+class CTextureRefs
+{
+public:
+ /*
+ * CTextureRefs():
+ *
+ * Constructor.
+ */
+ CTextureRefs();
+
+ /*
+ * ~CTextureRefs():
+ *
+ * Destructor.
+ */
+ ~CTextureRefs();
+
+ /*
+ * GetSize():
+ *
+ * Returns number of unique texture references held.
+ */
+ unsigned GetSize() const;
+
+ /*
+ * Clear():
+ *
+ * Removes all texture references.
+ */
+ void Clear();
+
+ /*
+ * ContainsRef(fmt, x, y, width, height):
+ *
+ * Returns true if holds the given texture reference.
+ */
+ bool ContainsRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
+
+ /*
+ * AddRef(fmt, x, y, width, height):
+ *
+ * Adds the given texture reference. Returns false if it was not possible to add the reference (ie out of memory).
+ */
+ bool AddRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
+
+ /*
+ * RemoveRef(fmt, x, y, width, height):
+ *
+ * Removes the given texture reference. Return true if the reference was found.
+ */
+ bool RemoveRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
+
+ /*
+ * RemoveRef(fmt, x, y, width, height):
+ *
+ * Decodes all texture references held, calling CRender3D::DecodeTexture for each one.
+ */
+ void DecodeAllTextures(CRender3D *Render3D);
+
+private:
+ // Number of texture references held.
+ unsigned m_size;
+
+ // Pre-allocated array used to hold first TEXREFS_ARRAY_SIZE texture references.
+ unsigned m_array[TEXREFS_ARRAY_SIZE];
+
+ // Dynamically allocated hashset used to hold texture references when there are more than TEXREFS_ARRAY_SIZE.
+ unsigned m_hashCapacity;
+ HashEntry **m_hashEntries;
+
+ /*
+ * UpdateHashCapacity(hashCapacity)
+ *
+ * Increases capacity of the hashset to given size.
+ */
+ bool UpdateHashCapacity(unsigned hashCapacity);
+
+ /*
+ * CreateHashEntry(texRef, hashCapacityUpdated)
+ *
+ * Creates and returns a new hash entry, updating the capacity if required (hashCapacityUpdated is set to true).
+ */
+ HashEntry *CreateHashEntry(unsigned texRef, bool &hashCapacityUpdated);
+
+ /*
+ * DeleteHashEntry(entry)
+ *
+ * Deletes the given hash entry and its storage.
+ */
+ void DeleteHashEntry(HashEntry *entry);
+
+ /*
+ * DeleteAllHashEntries()
+ *
+ * Deletes all hash entries and their storage.
+ */
+ void DeleteAllHashEntries();
+
+ /*
+ * AddToHash(texRef)
+ *
+ * Adds the given texture reference (as a bitfield) to the hashset.
+ */
+ bool AddToHash(unsigned texRef);
+
+ /*
+ * RemoveFromHash(texRef)
+ *
+ * Removes the given texture reference (as a bitfield) from the hashset.
+ */
+ bool RemoveFromHash(unsigned texRef);
+
+ /*
+ * HashContains(texRef)
+ *
+ * Returns true if given texture reference (as a bitfield) is held in the hashset.
+ */
+ bool HashContains(unsigned texRef) const;
+};
+
+#endif // INCLUDED_TEXTUREREFS_H
diff --git a/Src/Supermodel.h b/Src/Supermodel.h
index d6a8bed..01906ff 100644
--- a/Src/Supermodel.h
+++ b/Src/Supermodel.h
@@ -118,6 +118,7 @@
#include "INIFile.h"
#include "BlockFile.h"
#include "Graphics/Render2D.h"
+#include "Graphics/TextureRefs.h"
#include "Graphics/Render3D.h"
#include "Graphics/Shader.h"
#ifdef SUPERMODEL_DEBUGGER
diff --git a/VS2008/Supermodel.vcproj b/VS2008/Supermodel.vcproj
index f3bb427..1b504f8 100755
--- a/VS2008/Supermodel.vcproj
+++ b/VS2008/Supermodel.vcproj
@@ -690,6 +690,10 @@
RelativePath="..\Src\Graphics\Shader.cpp"
>
+
+
@@ -1824,6 +1828,10 @@
RelativePath="..\Src\Graphics\Shaders3D.h"
>
+
+