#include "quakedef.h" #include "meshqueue.h" typedef struct meshqueue_s { struct meshqueue_s *next; void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfaceindices); const entity_render_t *ent; int surfacenumber; const rtlight_t *rtlight; float dist; dptransparentsortcategory_t category; } meshqueue_t; int trans_sortarraysize; meshqueue_t **trans_hash = NULL; meshqueue_t ***trans_hashpointer = NULL; float mqt_viewplanedist; float mqt_viewmaxdist; meshqueue_t *mqt_array; int mqt_count; int mqt_total; void R_MeshQueue_BeginScene(void) { mqt_count = 0; mqt_viewplanedist = DotProduct(r_refdef.view.origin, r_refdef.view.forward); mqt_viewmaxdist = 0; } void R_MeshQueue_AddTransparent(dptransparentsortcategory_t category, 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) { meshqueue_t *mq; if (mqt_count >= mqt_total || !mqt_array) { int newtotal = max(1024, mqt_total * 2); meshqueue_t *newarray = (meshqueue_t *)Mem_Alloc(cls.permanentmempool, 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; } mq = &mqt_array[mqt_count++]; mq->callback = callback; mq->ent = ent; mq->surfacenumber = surfacenumber; mq->rtlight = rtlight; mq->category = category; if (r_transparent_useplanardistance.integer) mq->dist = DotProduct(center, r_refdef.view.forward) - mqt_viewplanedist; else mq->dist = VectorDistance(center, r_refdef.view.origin); mq->next = NULL; mqt_viewmaxdist = max(mqt_viewmaxdist, mq->dist); } void R_MeshQueue_RenderTransparent(void) { int i, hashindex, maxhashindex, batchnumsurfaces; float distscale; const entity_render_t *ent; const rtlight_t *rtlight; void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfaceindices); int batchsurfaceindex[MESHQUEUE_TRANSPARENT_BATCHSIZE]; meshqueue_t *mqt; if (!mqt_count) return; // check for bad cvars if (r_transparent_sortarraysize.integer < 1 || r_transparent_sortarraysize.integer > 32768) Cvar_SetValueQuick(&r_transparent_sortarraysize, bound(1, r_transparent_sortarraysize.integer, 32768)); if (r_transparent_sortmindist.integer < 1 || r_transparent_sortmindist.integer >= r_transparent_sortmaxdist.integer) Cvar_SetValueQuick(&r_transparent_sortmindist, 0); if (r_transparent_sortmaxdist.integer < r_transparent_sortmindist.integer || r_transparent_sortmaxdist.integer > 32768) Cvar_SetValueQuick(&r_transparent_sortmaxdist, bound(r_transparent_sortmindist.integer, r_transparent_sortmaxdist.integer, 32768)); // update hash array if (trans_sortarraysize != r_transparent_sortarraysize.integer) { trans_sortarraysize = r_transparent_sortarraysize.integer; if (trans_hash) Mem_Free(trans_hash); trans_hash = (meshqueue_t **)Mem_Alloc(cls.permanentmempool, sizeof(meshqueue_t *) * trans_sortarraysize); if (trans_hashpointer) Mem_Free(trans_hashpointer); trans_hashpointer = (meshqueue_t ***)Mem_Alloc(cls.permanentmempool, sizeof(meshqueue_t **) * trans_sortarraysize); } // build index memset(trans_hash, 0, sizeof(meshqueue_t *) * trans_sortarraysize); for (i = 0; i < trans_sortarraysize; i++) trans_hashpointer[i] = &trans_hash[i]; distscale = (trans_sortarraysize - 1) / min(mqt_viewmaxdist, r_transparent_sortmaxdist.integer); maxhashindex = trans_sortarraysize - 1; for (i = 0, mqt = mqt_array; i < mqt_count; i++, mqt++) { switch(mqt->category) { default: case TRANSPARENTSORT_HUD: hashindex = 0; break; case TRANSPARENTSORT_DISTANCE: // this could use a reduced range if we need more categories hashindex = bound(0, (int)(bound(0, mqt->dist - r_transparent_sortmindist.integer, r_transparent_sortmaxdist.integer) * distscale), maxhashindex); break; case TRANSPARENTSORT_SKY: hashindex = maxhashindex; break; } // link to tail of hash chain (to preserve render order) mqt->next = NULL; *trans_hashpointer[hashindex] = mqt; trans_hashpointer[hashindex] = &mqt->next; } callback = NULL; ent = NULL; rtlight = NULL; batchnumsurfaces = 0; // draw for (i = maxhashindex; i >= 0; i--) { if (trans_hash[i]) { for (mqt = trans_hash[i]; mqt; mqt = mqt->next) { if (ent != mqt->ent || rtlight != mqt->rtlight || callback != mqt->callback || batchnumsurfaces >= MESHQUEUE_TRANSPARENT_BATCHSIZE) { if (batchnumsurfaces) callback(ent, rtlight, batchnumsurfaces, batchsurfaceindex); batchnumsurfaces = 0; ent = mqt->ent; rtlight = mqt->rtlight; callback = mqt->callback; } batchsurfaceindex[batchnumsurfaces++] = mqt->surfacenumber; } } } if (batchnumsurfaces) callback(ent, rtlight, batchnumsurfaces, batchsurfaceindex); mqt_count = 0; }