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