a9b7ac0b925ca6d128d55332bda4ec24f0f020c7
[xonotic/darkplaces.git] / meshqueue.c
1
2 #include "quakedef.h"
3 #include "meshqueue.h"
4
5 cvar_t r_meshqueue_entries = {CVAR_SAVE, "r_meshqueue_entries", "16"};
6 cvar_t r_meshqueue_immediaterender = {0, "r_meshqueue_immediaterender", "0"};
7 cvar_t r_meshqueue_sort = {0, "r_meshqueue_sort", "0"};
8
9 typedef struct meshqueue_s
10 {
11         struct meshqueue_s *next;
12         void (*callback)(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight);
13         const entity_render_t *ent;
14         int surfacenumber;
15         const rtlight_t *rtlight;
16         float dist;
17 }
18 meshqueue_t;
19
20 float mqt_viewplanedist;
21 float mqt_viewmaxdist;
22 meshqueue_t *mq_array, *mqt_array, *mq_listhead;
23 int mq_count, mqt_count;
24 int mq_total, mqt_total;
25
26 void R_MeshQueue_Init(void)
27 {
28         Cvar_RegisterVariable(&r_meshqueue_entries);
29         Cvar_RegisterVariable(&r_meshqueue_immediaterender);
30         Cvar_RegisterVariable(&r_meshqueue_sort);
31
32         mq_total = 0;
33         mqt_total = 0;
34         mq_array = NULL;
35         mqt_array = NULL;
36 }
37
38 void R_MeshQueue_Render(void)
39 {
40         meshqueue_t *mq;
41         if (!mq_count)
42                 return;
43         for (mq = mq_listhead;mq;mq = mq->next)
44                 mq->callback(mq->ent, mq->surfacenumber, mq->rtlight);
45         mq_count = 0;
46         mq_listhead = NULL;
47 }
48
49 static void R_MeshQueue_EnlargeTransparentArray(int newtotal)
50 {
51         meshqueue_t *newarray;
52         newarray = (meshqueue_t *)Mem_Alloc(cl_mempool, newtotal * sizeof(meshqueue_t));
53         if (mqt_array)
54         {
55                 memcpy(newarray, mqt_array, mqt_total * sizeof(meshqueue_t));
56                 Mem_Free(mqt_array);
57         }
58         mqt_array = newarray;
59         mqt_total = newtotal;
60 }
61
62 void R_MeshQueue_Add(void (*callback)(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight), const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
63 {
64         meshqueue_t *mq, **mqnext;
65         if (r_meshqueue_immediaterender.integer)
66         {
67                 callback(ent, surfacenumber, rtlight);
68                 return;
69         }
70         if (mq_count >= mq_total)
71                 R_MeshQueue_Render();
72         mq = &mq_array[mq_count++];
73         mq->callback = callback;
74         mq->ent = ent;
75         mq->surfacenumber = surfacenumber;
76         mq->rtlight = rtlight;
77
78         if (r_meshqueue_sort.integer)
79         {
80                 // bubble-insert sort into meshqueue
81                 for(mqnext = &mq_listhead;*mqnext;mqnext = &(*mqnext)->next)
82                 {
83                         if (mq->callback == (*mqnext)->callback)
84                         {
85                                 if (mq->ent == (*mqnext)->ent)
86                                 {
87                                         if (mq->surfacenumber == (*mqnext)->surfacenumber)
88                                         {
89                                                 if (mq->rtlight <= (*mqnext)->rtlight)
90                                                         break;
91                                         }
92                                         else if (mq->surfacenumber < (*mqnext)->surfacenumber)
93                                                 break;
94                                 }
95                                 else if (mq->ent < (*mqnext)->ent)
96                                         break;
97                         }
98                         else if (mq->callback < (*mqnext)->callback)
99                                 break;
100                 }
101         }
102         else
103         {
104                 // maintain the order
105                 for(mqnext = &mq_listhead;*mqnext;mqnext = &(*mqnext)->next);
106         }
107         mq->next = *mqnext;
108         *mqnext = mq;
109 }
110
111 void R_MeshQueue_AddTransparent(const vec3_t center, void (*callback)(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight), const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
112 {
113         meshqueue_t *mq;
114         if (mqt_count >= mqt_total)
115                 R_MeshQueue_EnlargeTransparentArray(mqt_total + 100);
116         mq = &mqt_array[mqt_count++];
117         mq->callback = callback;
118         mq->ent = ent;
119         mq->surfacenumber = surfacenumber;
120         mq->rtlight = rtlight;
121         mq->dist = DotProduct(center, r_viewforward) - mqt_viewplanedist;
122         mq->next = NULL;
123         mqt_viewmaxdist = max(mqt_viewmaxdist, mq->dist);
124 }
125
126 void R_MeshQueue_RenderTransparent(void)
127 {
128         int i;
129         int hashdist;
130         float distscale;
131         meshqueue_t *mqt;
132         meshqueue_t *hash[4096], **hashpointer[4096];
133         if (mq_count)
134                 R_MeshQueue_Render();
135         if (!mqt_count)
136                 return;
137         memset(hash, 0, sizeof(hash));
138         for (i = 0;i < 4096;i++)
139                 hashpointer[i] = &hash[i];
140         distscale = 4095.0f / max(mqt_viewmaxdist, 4095);
141         for (i = 0, mqt = mqt_array;i < mqt_count;i++, mqt++)
142         {
143                 // generate index
144                 hashdist = (int) (mqt->dist * distscale);
145                 hashdist = bound(0, hashdist, 4095);
146                 // link to tail of hash chain (to preserve render order)
147                 mqt->next = NULL;
148                 *hashpointer[hashdist] = mqt;
149                 hashpointer[hashdist] = &mqt->next;
150         }
151         for (i = 4095;i >= 0;i--)
152                 if (hash[i])
153                         for (mqt = hash[i];mqt;mqt = mqt->next)
154                                 mqt->callback(mqt->ent, mqt->surfacenumber, mqt->rtlight);
155         mqt_count = 0;
156 }
157
158 void R_MeshQueue_BeginScene(void)
159 {
160         if (r_meshqueue_entries.integer < 1)
161                 Cvar_SetValueQuick(&r_meshqueue_entries, 1);
162         if (r_meshqueue_entries.integer > 65536)
163                 Cvar_SetValueQuick(&r_meshqueue_entries, 65536);
164
165         if (mq_total != r_meshqueue_entries.integer || mq_array == NULL)
166         {
167                 mq_total = r_meshqueue_entries.integer;
168                 if (mq_array)
169                         Mem_Free(mq_array);
170                 mq_array = (meshqueue_t *)Mem_Alloc(cl_mempool, mq_total * sizeof(meshqueue_t));
171         }
172
173         if (mqt_array == NULL)
174                 mqt_array = (meshqueue_t *)Mem_Alloc(cl_mempool, mqt_total * sizeof(meshqueue_t));
175
176         mq_count = 0;
177         mqt_count = 0;
178         mq_listhead = NULL;
179         mqt_viewplanedist = DotProduct(r_vieworigin, r_viewforward);
180         mqt_viewmaxdist = 0;
181 }
182
183 void R_MeshQueue_EndScene(void)
184 {
185         if (mq_count)
186         {
187                 Con_Printf("R_MeshQueue_EndScene: main mesh queue still has %i items left, flushing\n", mq_count);
188                 R_MeshQueue_Render();
189         }
190         if (mqt_count)
191         {
192                 Con_Printf("R_MeshQueue_EndScene: transparent mesh queue still has %i items left, flushing\n", mqt_count);
193                 R_MeshQueue_RenderTransparent();
194         }
195 }
196