diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e62316f2138973c6dd31e4aaaf36f5e6678fae21..c4b6729b9999bea48df5b6e70880214b858f2cdf 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -339,6 +339,7 @@ if(${SRB2_CONFIG_HWRENDER})
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_trick.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_vertarray.c
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_vertbuckets.c
 	)
 
 	set (SRB2_HWRENDER_HEADERS
@@ -352,6 +353,7 @@ if(${SRB2_CONFIG_HWRENDER})
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_main.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_vertarray.h
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_vertbuckets.h
 	)
 
 	set(SRB2_R_OPENGL_SOURCES
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 78fc31afccceb1397b08bc40bc3a17e247ff0d89..7b41ee42f30161e0d53d8b6760d22f7a9943d8cf 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -1105,4 +1105,9 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum)
 	Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
 }
 
+int HWR_GetNumCacheTextures(void)
+{
+	return gr_numtextures;
+}
+
 #endif //HWRENDER
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 88786bc112ae459eb8826fc14ab551d8f479ce98..2da80c64bf80e70869dabb2b04188febac1ff620 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -108,6 +108,9 @@ GLPatch_t *HWR_GetCachedGLPatchPwad(UINT16 wad, UINT16 lump);
 GLPatch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum);
 void HWR_GetFadeMask(lumpnum_t fademasklumpnum);
 
+/** */
+int HWR_GetNumCacheTextures(void);
+
 // --------
 // hw_draw.c
 // --------
diff --git a/src/hardware/hw_vertbuckets.c b/src/hardware/hw_vertbuckets.c
new file mode 100644
index 0000000000000000000000000000000000000000..10876bb76a86a9c368efcbb4ed46e77b23445145
--- /dev/null
+++ b/src/hardware/hw_vertbuckets.c
@@ -0,0 +1,94 @@
+// Emacs style mode select   -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) 2015 by Sonic Team Jr.
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+/// \file
+/// \brief Vertex buckets mapping based on texture binding, implementation
+
+#include "hw_vertbuckets.h"
+
+#include "hw_glob.h"
+#include "hw_drv.h"
+
+#include "../z_zone.h"
+
+// 6 because we assume at least a quad per texture.
+#define INITIAL_BUCKET_SIZE 6
+
+static FVertexArray * buckets = NULL;
+static int numBuckets = 0;
+
+static int bucketsHaveBeenInitialized = 0;
+
+void HWR_ResetVertexBuckets(void)
+{
+	// Free all arrays if they've been initialized before.
+	if (bucketsHaveBeenInitialized)
+	{
+		int i = 0;
+		for (i = 0; i < numBuckets; i++)
+		{
+			HWR_FreeVertexArray(&buckets[i]);
+		}
+		Z_Free(buckets);
+	}
+
+	// We will only have as many buckets as gr_numtextures
+	numBuckets = HWR_GetNumCacheTextures();
+	buckets = Z_Malloc(numBuckets * sizeof(FVertexArray), PU_HWRCACHE, NULL);
+	
+	{
+		int i = 0;
+		for (i = 0; i < numBuckets; i++)
+		{
+			HWR_InitVertexArray(&buckets[i], INITIAL_BUCKET_SIZE);
+		}
+	}
+	bucketsHaveBeenInitialized = 1;
+}
+
+FVertexArray * HWR_GetVertexArrayForTexture(int texNum)
+{
+	if (texNum >= numBuckets || texNum < 0)
+	{
+		I_Error("HWR_GetVertexArrayForTexture: texNum >= numBuckets");
+		//return NULL;
+	}
+	return &buckets[texNum];
+}
+
+void HWR_DrawVertexBuckets(void)
+{
+	int i = 0;
+	for (i = 0; i < numBuckets; i++)
+	{
+		FSurfaceInfo surface;
+		FVertexArray * bucket = &buckets[i];
+		
+		if (bucket->used == 0)
+		{
+			// We did not add any vertices with this texture, so we can skip binding and drawing.
+			continue;
+		}
+		
+		// Bind the texture.
+		
+		HWR_GetTexture(i);
+		// Draw the triangles.
+		// TODO handle polyflags and modulation for lighting/translucency?
+		HWD.pfnDrawPolygon(&surface, bucket->buffer, bucket->used, 0);
+		CONS_Printf("%d: %d tris\n", i, bucket->used);
+	}
+}
diff --git a/src/hardware/hw_vertbuckets.h b/src/hardware/hw_vertbuckets.h
new file mode 100644
index 0000000000000000000000000000000000000000..c93f19d30172e283f562274cd6e3c5b06f22d463
--- /dev/null
+++ b/src/hardware/hw_vertbuckets.h
@@ -0,0 +1,42 @@
+// Emacs style mode select   -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) 2015 by Sonic Team Jr.
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+/// \file
+/// \brief Vertex buckets mapping based on texture binding
+
+#ifndef __HW_VERTBUCKETS_H__
+#define __HW_VERTBUCKETS_H__
+
+#include "hw_vertarray.h"
+
+/* The vertex buckets are a mapping of runtime texture number to FVertexArray.
+ * BSP traversal, instead of sending DrawPolygon calls to the driver directly,
+ * will add vertices to these buckets based on the texture used. After BSP
+ * traversal, you should call HWR_DrawVertexBuckets in order to send DrawPolygon
+ * calls for each texture in the buckets.
+ */
+
+/** Empty the vertex buckets to prepare for another BSP traversal. This function
+  * should be called before rendering any BSP node. */
+void HWR_ResetVertexBuckets(void);
+
+/** Get a pointer to the vertex array used for a given texture number. */
+FVertexArray * HWR_GetVertexArrayForTexture(int texNum);
+
+/** Send draw commands to the hardware driver to draw each of the buckets. */
+void HWR_DrawVertexBuckets(void);
+
+#endif //__HW_VERTBUCKETS_H__