+ mem = (r_bufferdata_buffer_t *)Mem_Alloc(r_main_mempool, sizeof(*mem));
+ mem->size = size;
+ mem->current = 0;
+ if (type == R_BUFFERDATA_VERTEX)
+ mem->buffer = R_Mesh_CreateMeshBuffer(NULL, mem->size, "dynamicbuffervertex", false, false, true, false);
+ else if (type == R_BUFFERDATA_INDEX16)
+ mem->buffer = R_Mesh_CreateMeshBuffer(NULL, mem->size, "dynamicbufferindex16", true, false, true, true);
+ else if (type == R_BUFFERDATA_INDEX32)
+ mem->buffer = R_Mesh_CreateMeshBuffer(NULL, mem->size, "dynamicbufferindex32", true, false, true, false);
+ else if (type == R_BUFFERDATA_UNIFORM)
+ mem->buffer = R_Mesh_CreateMeshBuffer(NULL, mem->size, "dynamicbufferuniform", false, true, true, false);
+ mem->purge = r_bufferdata_buffer[r_bufferdata_cycle][type];
+ r_bufferdata_buffer[r_bufferdata_cycle][type] = mem;
+ }
+}
+
+void R_BufferData_NewFrame(void)
+{
+ int type;
+ r_bufferdata_buffer_t **p, *mem;
+ // cycle to the next frame's buffers
+ r_bufferdata_cycle = (r_bufferdata_cycle + 1) % R_BUFFERDATA_CYCLE;
+ // if we ran out of space on the last time we used these buffers, free the old memory now
+ for (type = 0;type < R_BUFFERDATA_COUNT;type++)
+ {
+ if (r_bufferdata_buffer[r_bufferdata_cycle][type])
+ {
+ R_BufferData_Resize((r_bufferdata_type_t)type, false, 131072);
+ // free all but the head buffer, this is how we recycle obsolete
+ // buffers after they are no longer in use
+ p = &r_bufferdata_buffer[r_bufferdata_cycle][type]->purge;
+ while (*p)
+ {
+ mem = *p;
+ *p = (*p)->purge;
+ if (mem->buffer)
+ R_Mesh_DestroyMeshBuffer(mem->buffer);
+ Mem_Free(mem);
+ }
+ // reset the current offset
+ r_bufferdata_buffer[r_bufferdata_cycle][type]->current = 0;
+ }
+ }
+}
+
+r_meshbuffer_t *R_BufferData_Store(size_t datasize, const void *data, r_bufferdata_type_t type, int *returnbufferoffset)
+{
+ r_bufferdata_buffer_t *mem;
+ int offset = 0;
+ int padsize;
+
+ *returnbufferoffset = 0;
+
+ // align size to a byte boundary appropriate for the buffer type, this
+ // makes all allocations have aligned start offsets
+ if (type == R_BUFFERDATA_UNIFORM)
+ padsize = (datasize + r_uniformbufferalignment - 1) & ~(r_uniformbufferalignment - 1);
+ else
+ padsize = (datasize + 15) & ~15;
+
+ // if we ran out of space in this buffer we must allocate a new one
+ if (!r_bufferdata_buffer[r_bufferdata_cycle][type] || r_bufferdata_buffer[r_bufferdata_cycle][type]->current + padsize > r_bufferdata_buffer[r_bufferdata_cycle][type]->size)
+ R_BufferData_Resize(type, true, padsize);
+
+ // if the resize did not give us enough memory, fail
+ if (!r_bufferdata_buffer[r_bufferdata_cycle][type] || r_bufferdata_buffer[r_bufferdata_cycle][type]->current + padsize > r_bufferdata_buffer[r_bufferdata_cycle][type]->size)
+ Sys_Error("R_BufferData_Store: failed to create a new buffer of sufficient size\n");
+
+ mem = r_bufferdata_buffer[r_bufferdata_cycle][type];
+ offset = mem->current;
+ mem->current += padsize;
+
+ // upload the data to the buffer at the chosen offset
+ if (offset == 0)
+ R_Mesh_UpdateMeshBuffer(mem->buffer, NULL, mem->size, false, 0);
+ R_Mesh_UpdateMeshBuffer(mem->buffer, data, datasize, true, offset);
+
+ // count the usage for stats
+ r_refdef.stats[r_stat_bufferdatacurrent_vertex + type] = max(r_refdef.stats[r_stat_bufferdatacurrent_vertex + type], (int)mem->current);
+ r_refdef.stats[r_stat_bufferdatasize_vertex + type] = max(r_refdef.stats[r_stat_bufferdatasize_vertex + type], (int)mem->size);
+
+ // return the buffer offset
+ *returnbufferoffset = offset;
+
+ return mem->buffer;
+}
+
+//==================================================================================
+
+// LordHavoc: animcache originally written by Echon, rewritten since then
+
+/**
+ * Animation cache prevents re-generating mesh data for an animated model
+ * multiple times in one frame for lighting, shadowing, reflections, etc.
+ */
+
+void R_AnimCache_Free(void)
+{
+}
+
+void R_AnimCache_ClearCache(void)
+{
+ int i;
+ entity_render_t *ent;
+
+ for (i = 0;i < r_refdef.scene.numentities;i++)
+ {
+ ent = r_refdef.scene.entities[i];
+ ent->animcache_vertex3f = NULL;
+ ent->animcache_vertex3f_vertexbuffer = NULL;
+ ent->animcache_vertex3f_bufferoffset = 0;
+ ent->animcache_normal3f = NULL;
+ ent->animcache_normal3f_vertexbuffer = NULL;
+ ent->animcache_normal3f_bufferoffset = 0;
+ ent->animcache_svector3f = NULL;
+ ent->animcache_svector3f_vertexbuffer = NULL;
+ ent->animcache_svector3f_bufferoffset = 0;
+ ent->animcache_tvector3f = NULL;
+ ent->animcache_tvector3f_vertexbuffer = NULL;
+ ent->animcache_tvector3f_bufferoffset = 0;
+ ent->animcache_vertexmesh = NULL;
+ ent->animcache_vertexmesh_vertexbuffer = NULL;
+ ent->animcache_vertexmesh_bufferoffset = 0;
+ ent->animcache_skeletaltransform3x4 = NULL;
+ ent->animcache_skeletaltransform3x4buffer = NULL;
+ ent->animcache_skeletaltransform3x4offset = 0;
+ ent->animcache_skeletaltransform3x4size = 0;
+ }
+}
+
+static void R_AnimCache_UpdateEntityMeshBuffers(entity_render_t *ent, int numvertices)
+{
+ int i;
+
+ // check if we need the meshbuffers
+ if (!vid.useinterleavedarrays)
+ return;
+
+ if (!ent->animcache_vertexmesh && ent->animcache_normal3f)
+ ent->animcache_vertexmesh = (r_vertexmesh_t *)R_FrameData_Alloc(sizeof(r_vertexmesh_t)*numvertices);
+ // TODO: upload vertexbuffer?
+ if (ent->animcache_vertexmesh)
+ {
+ r_refdef.stats[r_stat_animcache_vertexmesh_count] += 1;
+ r_refdef.stats[r_stat_animcache_vertexmesh_vertices] += numvertices;
+ r_refdef.stats[r_stat_animcache_vertexmesh_maxvertices] = max(r_refdef.stats[r_stat_animcache_vertexmesh_maxvertices], numvertices);
+ memcpy(ent->animcache_vertexmesh, ent->model->surfmesh.data_vertexmesh, sizeof(r_vertexmesh_t)*numvertices);
+ for (i = 0;i < numvertices;i++)
+ memcpy(ent->animcache_vertexmesh[i].vertex3f, ent->animcache_vertex3f + 3*i, sizeof(float[3]));
+ if (ent->animcache_svector3f)
+ for (i = 0;i < numvertices;i++)