6b6ff0761ac8732685a6c965f156a515952749b4
[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", "maximum number of meshes to batch together and sort before issuing render calls (unused)"};
6 cvar_t r_meshqueue_immediaterender = {0, "r_meshqueue_immediaterender", "0", "immediately render non-transparent meshes rather than batching"};
7 cvar_t r_meshqueue_sort = {0, "r_meshqueue_sort", "0", "whether to sort meshes in a batch before issuing calls"};
8
9 typedef struct meshqueue_s
10 {
11         struct meshqueue_s *next;
12         void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfaceindices);
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         // this is only used by one piece of code in prvm_cmds, why is it used at all?
41         meshqueue_t *mq;
42         if (!mq_count)
43                 return;
44         for (mq = mq_listhead;mq;mq = mq->next)
45                 mq->callback(mq->ent, mq->rtlight, 1, &mq->surfacenumber);
46         mq_count = 0;
47         mq_listhead = NULL;
48 }
49
50 static void R_MeshQueue_EnlargeTransparentArray(int newtotal)
51 {
52         meshqueue_t *newarray;
53         newarray = (meshqueue_t *)Mem_Alloc(cls.permanentmempool, newtotal * sizeof(meshqueue_t));
54         if (mqt_array)
55         {
56                 memcpy(newarray, mqt_array, mqt_total * sizeof(meshqueue_t));
57                 Mem_Free(mqt_array);
58         }
59         mqt_array = newarray;
60         mqt_total = newtotal;
61 }
62
63 void R_MeshQueue_Add(void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist), const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
64 {
65         // this is only used by one piece of code in prvm_cmds, why is it used at all?
66         meshqueue_t *mq, **mqnext;
67         if (r_meshqueue_immediaterender.integer)
68         {
69                 callback(ent, rtlight, 1, &surfacenumber);
70                 return;
71         }
72         if (mq_count >= mq_total)
73                 R_MeshQueue_Render();
74         mq = &mq_array[mq_count++];
75         mq->callback = callback;
76         mq->ent = ent;
77         mq->surfacenumber = surfacenumber;
78         mq->rtlight = rtlight;
79
80         if (r_meshqueue_sort.integer)
81         {
82                 // bubble-insert sort into meshqueue
83                 for(mqnext = &mq_listhead;*mqnext;mqnext = &(*mqnext)->next)
84                 {
85                         if (mq->callback == (*mqnext)->callback)
86                         {
87                                 if (mq->ent == (*mqnext)->ent)
88                                 {
89                                         if (mq->surfacenumber == (*mqnext)->surfacenumber)
90                                         {
91                                                 if (mq->rtlight <= (*mqnext)->rtlight)
92                                                         break;
93                                         }
94                                         else if (mq->surfacenumber < (*mqnext)->surfacenumber)
95                                                 break;
96                                 }
97                                 else if (mq->ent < (*mqnext)->ent)
98                                         break;
99                         }
100                         else if (mq->callback < (*mqnext)->callback)
101                                 break;
102                 }
103         }
104         else
105         {
106                 // maintain the order
107                 for(mqnext = &mq_listhead;*mqnext;mqnext = &(*mqnext)->next);
108         }
109         mq->next = *mqnext;
110         *mqnext = mq;
111 }
112
113 void R_MeshQueue_AddTransparent(const vec3_t center, void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist), const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
114 {
115         meshqueue_t *mq;
116         if (mqt_count >= mqt_total)
117                 R_MeshQueue_EnlargeTransparentArray(mqt_total + 100);
118         mq = &mqt_array[mqt_count++];
119         mq->callback = callback;
120         mq->ent = ent;
121         mq->surfacenumber = surfacenumber;
122         mq->rtlight = rtlight;
123         mq->dist = DotProduct(center, r_viewforward) - mqt_viewplanedist;
124         mq->next = NULL;
125         mqt_viewmaxdist = max(mqt_viewmaxdist, mq->dist);
126 }
127
128 void R_MeshQueue_RenderTransparent(void)
129 {
130         int i;
131         int hashdist;
132         int batchnumsurfaces;
133         float distscale;
134         const entity_render_t *ent;
135         const rtlight_t *rtlight;
136         void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfaceindices);
137         meshqueue_t *mqt;
138         meshqueue_t *hash[4096], **hashpointer[4096];
139         int batchsurfaceindex[256];
140         if (mq_count)
141                 R_MeshQueue_Render();
142         if (!mqt_count)
143                 return;
144         memset(hash, 0, sizeof(hash));
145         for (i = 0;i < 4096;i++)
146                 hashpointer[i] = &hash[i];
147         distscale = 4095.0f / max(mqt_viewmaxdist, 4095);
148         for (i = 0, mqt = mqt_array;i < mqt_count;i++, mqt++)
149         {
150                 // generate index
151                 hashdist = (int) (mqt->dist * distscale);
152                 hashdist = bound(0, hashdist, 4095);
153                 // link to tail of hash chain (to preserve render order)
154                 mqt->next = NULL;
155                 *hashpointer[hashdist] = mqt;
156                 hashpointer[hashdist] = &mqt->next;
157         }
158         callback = NULL;
159         ent = NULL;
160         rtlight = NULL;
161         batchnumsurfaces = 0;
162         for (i = 4095;i >= 0;i--)
163         {
164                 if (hash[i])
165                 {
166                         for (mqt = hash[i];mqt;mqt = mqt->next)
167                         {
168                                 if (ent != mqt->ent || rtlight != mqt->rtlight || callback != mqt->callback || batchnumsurfaces >= 256)
169                                 {
170                                         if (batchnumsurfaces)
171                                                 callback(ent, rtlight, batchnumsurfaces, batchsurfaceindex);
172                                         batchnumsurfaces = 0;
173                                         ent = mqt->ent;
174                                         rtlight = mqt->rtlight;
175                                         callback = mqt->callback;
176                                 }
177                                 batchsurfaceindex[batchnumsurfaces++] = mqt->surfacenumber;
178                         }
179                 }
180         }
181         if (batchnumsurfaces)
182                 callback(ent, rtlight, batchnumsurfaces, batchsurfaceindex);
183         mqt_count = 0;
184 }
185
186 void R_MeshQueue_BeginScene(void)
187 {
188         if (r_meshqueue_entries.integer < 1)
189                 Cvar_SetValueQuick(&r_meshqueue_entries, 1);
190         if (r_meshqueue_entries.integer > 65536)
191                 Cvar_SetValueQuick(&r_meshqueue_entries, 65536);
192
193         if (mq_total != r_meshqueue_entries.integer || mq_array == NULL)
194         {
195                 mq_total = r_meshqueue_entries.integer;
196                 if (mq_array)
197                         Mem_Free(mq_array);
198                 mq_array = (meshqueue_t *)Mem_Alloc(cls.permanentmempool, mq_total * sizeof(meshqueue_t));
199         }
200
201         if (mqt_array == NULL)
202                 mqt_array = (meshqueue_t *)Mem_Alloc(cls.permanentmempool, mqt_total * sizeof(meshqueue_t));
203
204         mq_count = 0;
205         mqt_count = 0;
206         mq_listhead = NULL;
207         mqt_viewplanedist = DotProduct(r_vieworigin, r_viewforward);
208         mqt_viewmaxdist = 0;
209 }
210
211 void R_MeshQueue_EndScene(void)
212 {
213         if (mq_count)
214         {
215                 Con_Printf("R_MeshQueue_EndScene: main mesh queue still has %i items left, flushing\n", mq_count);
216                 R_MeshQueue_Render();
217         }
218         if (mqt_count)
219         {
220                 Con_Printf("R_MeshQueue_EndScene: transparent mesh queue still has %i items left, flushing\n", mqt_count);
221                 R_MeshQueue_RenderTransparent();
222         }
223 }
224