+// LordHavoc: animcache written by Echon, refactored and reformatted by me
+
+/**
+ * Animation cache helps save re-animating a player mesh if it's re-rendered again in a given frame
+ * (reflections, lighting, etc). All animation cache becomes invalid on the next frame and is flushed
+ * (well, over-wrote). The memory for each cache is kept around to save on allocation thrashing.
+ */
+
+typedef struct r_animcache_entity_s
+{
+ float *vertex3f;
+ float *normal3f;
+ float *svector3f;
+ float *tvector3f;
+ int maxvertices;
+ qboolean wantnormals;
+ qboolean wanttangents;
+}
+r_animcache_entity_t;
+
+typedef struct r_animcache_s
+{
+ r_animcache_entity_t entity[MAX_EDICTS*2];
+ int maxindex;
+ int currentindex;
+}
+r_animcache_t;
+
+static r_animcache_t r_animcachestate;
+
+void R_AnimCache_Free(void)
+{
+ int idx;
+ for (idx=0 ; idx<r_animcachestate.maxindex ; idx++)
+ {
+ r_animcachestate.entity[idx].maxvertices = 0;
+ Mem_Free(r_animcachestate.entity[idx].vertex3f);
+ r_animcachestate.entity[idx].vertex3f = NULL;
+ r_animcachestate.entity[idx].normal3f = NULL;
+ r_animcachestate.entity[idx].svector3f = NULL;
+ r_animcachestate.entity[idx].tvector3f = NULL;
+ }
+ r_animcachestate.currentindex = 0;
+ r_animcachestate.maxindex = 0;
+}
+
+void R_AnimCache_ResizeEntityCache(const int cacheIdx, const int numvertices)
+{
+ int arraySize;
+ float *base;
+ r_animcache_entity_t *cache = &r_animcachestate.entity[cacheIdx];
+
+ if (cache->maxvertices >= numvertices)
+ return;
+
+ // Release existing memory
+ if (cache->vertex3f)
+ Mem_Free(cache->vertex3f);
+
+ // Pad by 1024 verts
+ cache->maxvertices = (numvertices + 1023) & ~1023;
+ arraySize = cache->maxvertices * 3;
+
+ // Allocate, even if we don't need this memory in this instance it will get ignored and potentially used later
+ base = (float *)Mem_Alloc(r_main_mempool, arraySize * sizeof(float) * 4);
+ r_animcachestate.entity[cacheIdx].vertex3f = base;
+ r_animcachestate.entity[cacheIdx].normal3f = base + arraySize;
+ r_animcachestate.entity[cacheIdx].svector3f = base + arraySize*2;
+ r_animcachestate.entity[cacheIdx].tvector3f = base + arraySize*3;
+
+// Con_Printf("allocated cache for %i (%f KB)\n", cacheIdx, (arraySize*sizeof(float)*4)/1024.0f);
+}
+
+void R_AnimCache_NewFrame(void)
+{
+ int i;
+
+ if (r_animcache.integer && r_drawentities.integer)
+ r_animcachestate.maxindex = sizeof(r_animcachestate.entity) / sizeof(r_animcachestate.entity[0]);
+ else if (r_animcachestate.maxindex)
+ R_AnimCache_Free();
+
+ r_animcachestate.currentindex = 0;
+
+ for (i = 0;i < r_refdef.scene.numentities;i++)
+ r_refdef.scene.entities[i]->animcacheindex = -1;
+}
+
+qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qboolean wanttangents)
+{
+ dp_model_t *model = ent->model;
+ r_animcache_entity_t *c;
+ // see if it's already cached this frame
+ if (ent->animcacheindex >= 0)
+ {
+ // add normals/tangents if needed
+ c = r_animcachestate.entity + ent->animcacheindex;
+ if (c->wantnormals)
+ wantnormals = false;
+ if (c->wanttangents)
+ wanttangents = false;
+ if (wantnormals || wanttangents)
+ model->AnimateVertices(model, ent->frameblend, NULL, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+ }
+ else
+ {
+ // see if this ent is worth caching
+ if (r_animcachestate.maxindex <= r_animcachestate.currentindex)
+ return false;
+ if (!model || !model->Draw || !model->surfmesh.isanimated || !model->AnimateVertices || (ent->frameblend[0].lerp == 1 && ent->frameblend[0].subframe == 0))
+ return false;
+ // assign it a cache entry and make sure the arrays are big enough
+ R_AnimCache_ResizeEntityCache(r_animcachestate.currentindex, model->surfmesh.num_vertices);
+ ent->animcacheindex = r_animcachestate.currentindex++;
+ c = r_animcachestate.entity + ent->animcacheindex;
+ c->wantnormals = wantnormals;
+ c->wanttangents = wanttangents;
+ model->AnimateVertices(model, ent->frameblend, c->vertex3f, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+ }
+ return true;
+}
+
+void R_AnimCache_CacheVisibleEntities(void)
+{
+ int i;
+ qboolean wantnormals;
+ qboolean wanttangents;
+
+ if (!r_animcachestate.maxindex)
+ return;
+
+ wantnormals = !r_showsurfaces.integer;
+ wanttangents = !r_showsurfaces.integer && (r_glsl.integer || r_refdef.scene.rtworld || r_refdef.scene.rtdlight);
+
+ // TODO: thread this?
+
+ for (i = 0;i < r_refdef.scene.numentities;i++)
+ {
+ if (!r_refdef.viewcache.entityvisible[i])
+ continue;
+ R_AnimCache_GetEntity(r_refdef.scene.entities[i], wantnormals, wanttangents);
+ }
+}
+
+//==================================================================================
+
+static void R_View_UpdateEntityLighting (void)
+{
+ int i;
+ entity_render_t *ent;
+ vec3_t tempdiffusenormal;
+
+ for (i = 0;i < r_refdef.scene.numentities;i++)
+ {
+ ent = r_refdef.scene.entities[i];
+
+ // skip unseen models
+ if (!r_refdef.viewcache.entityvisible[i] && r_shadows.integer != 1)
+ continue;
+
+ // skip bsp models
+ if (ent->model && ent->model->brush.num_leafs)
+ {
+ // TODO: use modellight for r_ambient settings on world?
+ VectorSet(ent->modellight_ambient, 0, 0, 0);
+ VectorSet(ent->modellight_diffuse, 0, 0, 0);
+ VectorSet(ent->modellight_lightdir, 0, 0, 1);
+ continue;
+ }
+
+ // fetch the lighting from the worldmodel data
+ VectorSet(ent->modellight_ambient, r_refdef.scene.ambient * (2.0f / 128.0f), r_refdef.scene.ambient * (2.0f / 128.0f), r_refdef.scene.ambient * (2.0f / 128.0f));
+ VectorClear(ent->modellight_diffuse);
+ VectorClear(tempdiffusenormal);
+ if ((ent->flags & RENDER_LIGHT) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
+ {
+ vec3_t org;
+ Matrix4x4_OriginFromMatrix(&ent->matrix, org);
+ r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, org, ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal);
+ }
+ else // highly rare
+ VectorSet(ent->modellight_ambient, 1, 1, 1);
+
+ // move the light direction into modelspace coordinates for lighting code
+ Matrix4x4_Transform3x3(&ent->inversematrix, tempdiffusenormal, ent->modellight_lightdir);
+ if(VectorLength2(ent->modellight_lightdir) == 0)
+ VectorSet(ent->modellight_lightdir, 0, 0, 1); // have to set SOME valid vector here
+ VectorNormalize(ent->modellight_lightdir);
+ }
+}
+