--- /dev/null
+
+#include "quakedef.h"
+#include "meshqueue.h"
+
+cvar_t r_meshqueue_entries = {CVAR_SAVE, "r_meshqueue_entries", "16"};
+cvar_t r_meshqueue_immediaterender = {0, "r_meshqueue_immediaterender", "0"};
+cvar_t r_meshqueue_sort = {0, "r_meshqueue_sort", "0"};
+
+typedef struct meshqueue_s
+{
+ struct meshqueue_s *next;
+ void (*callback)(void *data1, int data2);
+ void *data1;
+ int data2;
+ float dist;
+}
+meshqueue_t;
+
+float mqt_viewplanedist;
+meshqueue_t *mq_array, *mqt_array, *mq_listhead;
+int mq_count, mqt_count;
+int mq_total, mqt_total;
+
+mempool_t *meshqueuemempool;
+
+void R_MeshQueue_Init(void)
+{
+ Cvar_RegisterVariable(&r_meshqueue_entries);
+ Cvar_RegisterVariable(&r_meshqueue_immediaterender);
+ Cvar_RegisterVariable(&r_meshqueue_sort);
+
+ meshqueuemempool = Mem_AllocPool("R_MeshQueue");
+ mq_total = 0;
+ mqt_total = 1000;
+ mq_array = NULL;
+ mqt_array = NULL;
+}
+
+static void R_MeshQueue_Render(void)
+{
+ meshqueue_t *mq;
+ for (mq = mq_listhead;mq;mq = mq->next)
+ mq->callback(mq->data1, mq->data2);
+ mq_count = 0;
+ mq_listhead = NULL;
+}
+
+static void R_MeshQueue_EnlargeTransparentArray(int newtotal)
+{
+ meshqueue_t *newarray;
+ newarray = Mem_Alloc(meshqueuemempool, newtotal * sizeof(meshqueue_t));
+ if (mqt_array)
+ {
+ memcpy(newarray, mqt_array, mqt_total * sizeof(meshqueue_t));
+ Mem_Free(mqt_array);
+ }
+ mqt_array = newarray;
+ mqt_total = newtotal;
+}
+
+void R_MeshQueue_Add(void (*callback)(void *data1, int data2), void *data1, int data2)
+{
+ meshqueue_t *mq, **mqnext;
+ if (r_meshqueue_immediaterender.integer)
+ {
+ callback(data1, data2);
+ return;
+ }
+ if (mq_count >= mq_total)
+ R_MeshQueue_Render();
+ mq = &mq_array[mq_count++];
+ mq->callback = callback;
+ mq->data1 = data1;
+ mq->data2 = data2;
+
+ // bubble-insert sort into meshqueue
+ mqnext = &mq_listhead;
+ if (r_meshqueue_sort.integer)
+ {
+ for(;;)
+ {
+ if (*mqnext)
+ {
+ if (mq->callback == (*mqnext)->callback)
+ {
+ if (mq->data1 == (*mqnext)->data1)
+ {
+ if (mq->data2 <= (*mqnext)->data2)
+ break;
+ }
+ else if (mq->data1 < (*mqnext)->data1)
+ break;
+ }
+ else if (mq->callback < (*mqnext)->callback)
+ break;
+ }
+ else
+ break;
+ mqnext = &(*mqnext)->next;
+ }
+ }
+ mq->next = *mqnext;
+ *mqnext = mq;
+}
+
+void R_MeshQueue_AddTransparent(vec3_t center, void (*callback)(void *data1, int data2), void *data1, int data2)
+{
+ meshqueue_t *mq;
+ if (mqt_count >= mqt_total)
+ R_MeshQueue_EnlargeTransparentArray(mqt_total + 100);
+ mq = &mqt_array[mqt_count++];
+ mq->callback = callback;
+ mq->data1 = data1;
+ mq->data2 = data2;
+ mq->dist = DotProduct(center, vpn) - mqt_viewplanedist;
+ mq->next = NULL;
+}
+
+static void R_MeshQueue_RenderTransparent(void)
+{
+ int i;
+ int hashdist;
+ meshqueue_t *mqt;
+ meshqueue_t *hash[4096];
+ memset(hash, 0, 4096 * sizeof(meshqueue_t *));
+ for (i = 0, mqt = mqt_array;i < mqt_count;i++, mqt++)
+ {
+ // generate index
+ hashdist = (int) (mqt->dist);
+ hashdist = bound(0, hashdist, 4095);
+ // reversed to simplify render loop
+ hashdist = 4095 - hashdist;
+ // link into hash chain
+ mqt->next = hash[hashdist];
+ hash[hashdist] = mqt;
+ }
+ for (i = 0;i < 4096;i++)
+ if (hash[i])
+ for (mqt = hash[i];mqt;mqt = mqt->next)
+ R_MeshQueue_Add(mqt->callback, mqt->data1, mqt->data2);
+ mqt_count = 0;
+}
+
+void R_MeshQueue_BeginScene(void)
+{
+ if (r_meshqueue_entries.integer < 1)
+ Cvar_SetValueQuick(&r_meshqueue_entries, 1);
+ if (r_meshqueue_entries.integer > 65536)
+ Cvar_SetValueQuick(&r_meshqueue_entries, 65536);
+
+ if (mq_total != r_meshqueue_entries.integer || mq_array == NULL)
+ {
+ mq_total = r_meshqueue_entries.integer;
+ if (mq_array)
+ Mem_Free(mq_array);
+ mq_array = Mem_Alloc(meshqueuemempool, mq_total * sizeof(meshqueue_t));
+ }
+
+ if (mqt_array == NULL)
+ mqt_array = Mem_Alloc(meshqueuemempool, mqt_total * sizeof(meshqueue_t));
+
+ mq_count = 0;
+ mqt_count = 0;
+ mq_listhead = NULL;
+ mqt_viewplanedist = DotProduct(r_origin, vpn);
+}
+
+void R_MeshQueue_EndScene(void)
+{
+ if (mqt_count)
+ R_MeshQueue_RenderTransparent();
+ if (mq_count)
+ R_MeshQueue_Render();
+}
+