e0e1e8c7091c7765783b311010f8da3399a1e3c2
[xonotic/darkplaces.git] / meshqueue.c
1
2 #include "quakedef.h"
3 #include "meshqueue.h"
4
5 typedef struct meshqueue_s
6 {
7         struct meshqueue_s *next;
8         void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfaceindices);
9         const entity_render_t *ent;
10         int surfacenumber;
11         const rtlight_t *rtlight;
12         float dist;
13 }
14 meshqueue_t;
15
16 int trans_sortarraysize;
17 meshqueue_t **trans_hash = NULL;
18 meshqueue_t ***trans_hashpointer = NULL;
19 extern cvar_t r_transparent_sortarraysize;
20 extern cvar_t r_transparent_sortmindist;
21 extern cvar_t r_transparent_sortmaxdist;
22
23 float mqt_viewplanedist;
24 float mqt_viewmaxdist;
25 meshqueue_t *mqt_array;
26 int mqt_count;
27 int mqt_total;
28
29 void R_MeshQueue_BeginScene(void)
30 {
31         mqt_count = 0;
32         mqt_viewplanedist = DotProduct(r_refdef.view.origin, r_refdef.view.forward);
33         mqt_viewmaxdist = 0;
34 }
35
36 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)
37 {
38         meshqueue_t *mq;
39         if (mqt_count >= mqt_total || !mqt_array)
40         {
41                 int newtotal = max(1024, mqt_total * 2);
42                 meshqueue_t *newarray = (meshqueue_t *)Mem_Alloc(cls.permanentmempool, newtotal * sizeof(meshqueue_t));
43                 if (mqt_array)
44                 {
45                         memcpy(newarray, mqt_array, mqt_total * sizeof(meshqueue_t));
46                         Mem_Free(mqt_array);
47                 }
48                 mqt_array = newarray;
49                 mqt_total = newtotal;
50         }
51         mq = &mqt_array[mqt_count++];
52         mq->callback = callback;
53         mq->ent = ent;
54         mq->surfacenumber = surfacenumber;
55         mq->rtlight = rtlight;
56         if (ent && (ent->flags & RENDER_WORLDOBJECT))
57                 mq->dist = mqt_viewmaxdist;
58         else
59                 mq->dist = DotProduct(center, r_refdef.view.forward) - mqt_viewplanedist;
60         mq->next = NULL;
61         mqt_viewmaxdist = max(mqt_viewmaxdist, mq->dist);
62 }
63
64 void R_MeshQueue_RenderTransparent(void)
65 {
66         int i, hashindex, maxhashindex, batchnumsurfaces;
67         float distscale;
68         const entity_render_t *ent;
69         const rtlight_t *rtlight;
70         void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfaceindices);
71         int batchsurfaceindex[MESHQUEUE_TRANSPARENT_BATCHSIZE];
72         meshqueue_t *mqt;
73
74         if (!mqt_count)
75                 return;
76
77         // check for bad cvars
78         if (r_transparent_sortarraysize.integer < 1 || r_transparent_sortarraysize.integer > 32768)
79                 Cvar_SetValueQuick(&r_transparent_sortarraysize, bound(1, r_transparent_sortarraysize.integer, 32768));
80         if (r_transparent_sortmindist.integer < 1 || r_transparent_sortmindist.integer >= r_transparent_sortmaxdist.integer)
81                 Cvar_SetValueQuick(&r_transparent_sortmindist, 0);
82         if (r_transparent_sortmaxdist.integer < r_transparent_sortmindist.integer || r_transparent_sortmaxdist.integer > 32768)
83                 Cvar_SetValueQuick(&r_transparent_sortmaxdist, bound(r_transparent_sortmindist.integer, r_transparent_sortmaxdist.integer, 32768));
84
85         // update hash array
86         if (trans_sortarraysize != r_transparent_sortarraysize.integer)
87         {
88                 trans_sortarraysize = r_transparent_sortarraysize.integer;
89                 if (trans_hash)
90                         Mem_Free(trans_hash);
91                 trans_hash = (meshqueue_t **)Mem_Alloc(cls.permanentmempool, sizeof(trans_hash) * trans_sortarraysize); 
92                 if (trans_hashpointer)
93                         Mem_Free(trans_hashpointer);
94                 trans_hashpointer = (meshqueue_t ***)Mem_Alloc(cls.permanentmempool, sizeof(trans_hashpointer) * trans_sortarraysize); 
95         }
96
97         // build index
98         memset(trans_hash, 0, sizeof(trans_hash) * trans_sortarraysize);
99         for (i = 0; i < trans_sortarraysize; i++)
100                 trans_hashpointer[i] = &trans_hash[i];
101         distscale = (trans_sortarraysize - 1) / min(mqt_viewmaxdist, r_transparent_sortmaxdist.integer);
102         maxhashindex = trans_sortarraysize - 1;
103         for (i = 0, mqt = mqt_array; i < mqt_count; i++, mqt++)
104         {
105                 hashindex = bound(0, (int)(bound(0, mqt->dist - r_transparent_sortmindist.integer, r_transparent_sortmaxdist.integer) * distscale), maxhashindex);
106                 // link to tail of hash chain (to preserve render order)
107                 mqt->next = NULL;
108                 *trans_hashpointer[hashindex] = mqt;
109                 trans_hashpointer[hashindex] = &mqt->next;
110         }
111         callback = NULL;
112         ent = NULL;
113         rtlight = NULL;
114         batchnumsurfaces = 0;
115
116         // draw
117         for (i = maxhashindex; i >= 0; i--)
118         {
119                 if (trans_hash[i])
120                 {
121                         for (mqt = trans_hash[i]; mqt; mqt = mqt->next)
122                         {
123                                 if (ent != mqt->ent || rtlight != mqt->rtlight || callback != mqt->callback || batchnumsurfaces >= MESHQUEUE_TRANSPARENT_BATCHSIZE)
124                                 {
125                                         if (batchnumsurfaces)
126                                                 callback(ent, rtlight, batchnumsurfaces, batchsurfaceindex);
127                                         batchnumsurfaces = 0;
128                                         ent = mqt->ent;
129                                         rtlight = mqt->rtlight;
130                                         callback = mqt->callback;
131                                 }
132                                 batchsurfaceindex[batchnumsurfaces++] = mqt->surfacenumber;
133                         }
134                 }
135         }
136         if (batchnumsurfaces)
137                 callback(ent, rtlight, batchnumsurfaces, batchsurfaceindex);
138         mqt_count = 0;
139 }