int polygonelements[768];
+static void R_Mesh_CacheArray_Startup(void);
+static void R_Mesh_CacheArray_Shutdown(void);
void GL_Backend_AllocArrays(void)
{
int i, size;
CHECKGLERROR
}
#endif
+
+ R_Mesh_CacheArray_Startup();
}
void GL_Backend_FreeArrays(void)
{
int i;
+ R_Mesh_CacheArray_Shutdown();
+
#ifdef MESH_VAR
if (mesh_var)
{
qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);CHECKGLERROR
qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);CHECKGLERROR
qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);CHECKGLERROR
- qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_CONSTANT_ARB);CHECKGLERROR
+ qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);CHECKGLERROR // for GL_INTERPOLATE_ARB mode
qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);CHECKGLERROR
qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);CHECKGLERROR
qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);CHECKGLERROR
}
}
-/*
-#ifdef MESH_BATCH
- if (gl_mesh_batching.integer)
- {
- if (gl_batchvertexcount == 0)
- gl_batchvertexfirst = varray_offset;
- gl_batchvertexcount += numverts;
- }
-#endif
-*/
varray_vertex3f = varray_buf_vertex3f + varray_offset * 3;
varray_color4f = varray_buf_color4f + varray_offset * 4;
for (i = 0;i < backendunits;i++)
}
}
+// renders triangles using vertices from the most recent GetSpace call
+// (can be multiple Draw calls per GetSpace)
+void R_Mesh_Draw_NoBatching(int numverts, int numtriangles, const int *elements)
+{
+ int numelements = numtriangles * 3;
+ if (numtriangles == 0 || numverts == 0)
+ {
+ Con_Printf("R_Mesh_Draw_NoBatching(%d, %d, %08p);\n", numverts, numtriangles, elements);
+ return;
+ }
+ c_meshs++;
+ c_meshelements += numelements;
+ CHECKGLERROR
+ if (gl_state.pointervertexcount)
+ {
+ if (r_render.integer)
+ {
+ GL_LockArrays(0, gl_state.pointervertexcount);
+ if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
+ {
+ qglDrawRangeElements(GL_TRIANGLES, 0, gl_state.pointervertexcount, numelements, GL_UNSIGNED_INT, elements);CHECKGLERROR
+ }
+ else
+ {
+ qglDrawElements(GL_TRIANGLES, numelements, GL_UNSIGNED_INT, elements);CHECKGLERROR
+ }
+ GL_LockArrays(0, 0);
+ }
+ }
+ else
+ {
+ GL_Backend_RenumberElements(varray_buf_elements3i, numelements, elements, varray_offset);
+ if (r_render.integer)
+ {
+ GL_LockArrays(varray_offset, numverts);
+ if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
+ {
+ qglDrawRangeElements(GL_TRIANGLES, varray_offset, varray_offset + numverts, numelements, GL_UNSIGNED_INT, varray_buf_elements3i);CHECKGLERROR
+ }
+ else
+ {
+ qglDrawElements(GL_TRIANGLES, numelements, GL_UNSIGNED_INT, varray_buf_elements3i);CHECKGLERROR
+ }
+ GL_LockArrays(0, 0);
+ }
+ }
+}
+
// restores backend state, used when done with 3D rendering
void R_Mesh_Finish(void)
{
qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), p ? p : varray_buf_vertex3f);CHECKGLERROR
}
- if (gl_state.colorarray)
+ p = gl_state.pointervertexcount ? m->pointer_color : NULL;
+ if (gl_state.pointer_color != p)
{
- p = gl_state.pointervertexcount ? m->pointer_color : NULL;
- if (gl_state.pointer_color != p)
- {
#ifdef MESH_BATCH
- if (gl_batchelementcount)
- R_Mesh_EndBatch();
+ if (gl_batchelementcount)
+ R_Mesh_EndBatch();
#endif
- gl_state.pointer_color = p;
- if (p != varray_buf_color4f || gl_mesh_floatcolors.integer)
- qglColorPointer(4, GL_FLOAT, sizeof(float[4]), p ? p : varray_buf_color4f);
- else
- qglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GLubyte[4]), p ? p : varray_buf_color4b);
- CHECKGLERROR
- }
+ gl_state.pointer_color = p;
+ if (p || gl_mesh_floatcolors.integer)
+ qglColorPointer(4, GL_FLOAT, sizeof(float[4]), p ? p : varray_buf_color4f);
+ else
+ qglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GLubyte[4]), p ? p : varray_buf_color4b);
+ CHECKGLERROR
}
}
if (mesh_var)
{
float *out = varray_texcoord2f[tmu];
- while (--numverts)
+ while (numverts--)
{
*out++ = *texcoord2f++;
*out++ = *texcoord2f++;
if (mesh_var)
{
float *out = varray_color4f;
- while (--numverts)
+ while (numverts--)
{
*out++ = *color4f++;
*out++ = *color4f++;
memcpy(varray_color4f, color4f, numverts * sizeof(float[4]));
}
+void R_ScrollTexCoord2f (float *out2f, const float *in2f, int numverts, float s, float t)
+{
+ while (numverts--)
+ {
+ *out2f++ = *in2f++ + s;
+ *out2f++ = *in2f++ + t;
+ }
+}
+
+//===========================================================================
+// vertex array caching subsystem
+//===========================================================================
+
+typedef struct rcachearraylink_s
+{
+ struct rcachearraylink_s *next, *prev;
+ struct rcachearrayitem_s *data;
+}
+rcachearraylink_t;
+
+typedef struct rcachearrayitem_s
+{
+ // the original request structure
+ rcachearrayrequest_t request;
+ // active
+ int active;
+ // offset into r_mesh_rcachedata
+ int offset;
+ // for linking this into the sequential list
+ rcachearraylink_t sequentiallink;
+ // for linking this into the lookup list
+ rcachearraylink_t hashlink;
+}
+rcachearrayitem_t;
+
+#define RCACHEARRAY_HASHSIZE 65536
+#define RCACHEARRAY_ITEMS 4096
+#define RCACHEARRAY_DEFAULTSIZE (4 << 20)
+
+// all active items are linked into this chain in sorted order
+static rcachearraylink_t r_mesh_rcachesequentialchain;
+// all inactive items are linked into this chain in unknown order
+static rcachearraylink_t r_mesh_rcachefreechain;
+// all active items are also linked into these chains (using their hashlink)
+static rcachearraylink_t r_mesh_rcachechain[RCACHEARRAY_HASHSIZE];
+
+// all items are stored here, whether active or inactive
+static rcachearrayitem_t r_mesh_rcacheitems[RCACHEARRAY_ITEMS];
+
+// size of data buffer
+static int r_mesh_rcachedata_size = RCACHEARRAY_DEFAULTSIZE;
+// data buffer
+static qbyte r_mesh_rcachedata[RCACHEARRAY_DEFAULTSIZE];
+
+// current state
+static int r_mesh_rcachedata_offset;
+static rcachearraylink_t *r_mesh_rcachesequentialchain_current;
+
+static void R_Mesh_CacheArray_Startup(void)
+{
+ int i;
+ rcachearraylink_t *l;
+ // prepare all the linked lists
+ l = &r_mesh_rcachesequentialchain;l->next = l->prev = l;l->data = NULL;
+ l = &r_mesh_rcachefreechain;l->next = l->prev = l;l->data = NULL;
+ memset(&r_mesh_rcachechain, 0, sizeof(r_mesh_rcachechain));
+ for (i = 0;i < RCACHEARRAY_HASHSIZE;i++)
+ {
+ l = &r_mesh_rcachechain[i];
+ l->next = l->prev = l;
+ l->data = NULL;
+ }
+ memset(&r_mesh_rcacheitems, 0, sizeof(r_mesh_rcacheitems));
+ for (i = 0;i < RCACHEARRAY_ITEMS;i++)
+ {
+ r_mesh_rcacheitems[i].hashlink.data = r_mesh_rcacheitems[i].sequentiallink.data = &r_mesh_rcacheitems[i];
+ l = &r_mesh_rcacheitems[i].sequentiallink;
+ l->next = &r_mesh_rcachefreechain;
+ l->prev = l->next->prev;
+ l->next->prev = l->prev->next = l;
+ }
+ // clear other state
+ r_mesh_rcachedata_offset = 0;
+ r_mesh_rcachesequentialchain_current = &r_mesh_rcachesequentialchain;
+}
+
+static void R_Mesh_CacheArray_Shutdown(void)
+{
+}
+
+/*
+static void R_Mesh_CacheArray_ValidateState(int num)
+{
+ rcachearraylink_t *l, *lhead;
+ lhead = &r_mesh_rcachesequentialchain;
+ if (r_mesh_rcachesequentialchain_current == lhead)
+ return;
+ for (l = lhead->next;l != lhead;l = l->next)
+ if (r_mesh_rcachesequentialchain_current == l)
+ return;
+ Sys_Error("%i", num);
+}
+*/
+
+int R_Mesh_CacheArray(rcachearrayrequest_t *r)
+{
+ rcachearraylink_t *l, *lhead, *lnext;
+ rcachearrayitem_t *d;
+ int hashindex, offset, offsetend;
+
+ //R_Mesh_CacheArray_ValidateState(3);
+ // calculate a hashindex to choose a cache chain
+ r->data = NULL;
+ hashindex = CRC_Block((void *)r, sizeof(*r)) % RCACHEARRAY_HASHSIZE;
+
+ // is it already cached?
+ for (lhead = &r_mesh_rcachechain[hashindex], l = lhead->next;l != lhead;l = l->next)
+ {
+ if (!memcmp(&l->data->request, r, sizeof(l->data->request)))
+ {
+ // we have it cached already
+ r->data = r_mesh_rcachedata + l->data->offset;
+ return false;
+ }
+ }
+
+ // we need to add a new cache item, this means finding a place for the new
+ // data and making sure we have a free item available, lots of work...
+
+ // check if buffer needs to wrap
+ if (r_mesh_rcachedata_offset + r->data_size > r_mesh_rcachedata_size)
+ {
+ /*
+ if (r->data_size * 10 > r_mesh_rcachedata_size)
+ {
+ // realloc whole cache
+ }
+ */
+ // reset back to start
+ r_mesh_rcachedata_offset = 0;
+ r_mesh_rcachesequentialchain_current = &r_mesh_rcachesequentialchain;
+ }
+ offset = r_mesh_rcachedata_offset;
+ r_mesh_rcachedata_offset += r->data_size;
+ offsetend = r_mesh_rcachedata_offset;
+ //R_Mesh_CacheArray_ValidateState(4);
+
+ /*
+ {
+ int n;
+ for (lhead = &r_mesh_rcachesequentialchain, l = lhead->next, n = 0;l != lhead;l = l->next, n++);
+ Con_Printf("R_Mesh_CacheArray: new data range %i:%i, %i items are already linked\n", offset, offsetend, n);
+ }
+ */
+
+ // make room for the new data (remove old items)
+ lhead = &r_mesh_rcachesequentialchain;
+ l = r_mesh_rcachesequentialchain_current;
+ if (l == lhead)
+ l = l->next;
+ while (l != lhead && l->data->offset < offsetend && l->data->offset + l->data->request.data_size > offset)
+ {
+ //r_mesh_rcachesequentialchain_current = l;
+ //R_Mesh_CacheArray_ValidateState(8);
+ lnext = l->next;
+ // if at the end of the chain, wrap around
+ if (lnext == lhead)
+ lnext = lnext->next;
+ //r_mesh_rcachesequentialchain_current = lnext;
+ //R_Mesh_CacheArray_ValidateState(10);
+
+ // unlink from sequential chain
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+ //R_Mesh_CacheArray_ValidateState(11);
+ // link into free chain
+ l->next = &r_mesh_rcachefreechain;
+ l->prev = l->next->prev;
+ l->next->prev = l->prev->next = l;
+ //R_Mesh_CacheArray_ValidateState(12);
+
+ l = &l->data->hashlink;
+ // unlink from hash chain
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+
+ l = lnext;
+ //r_mesh_rcachesequentialchain_current = l;
+ //R_Mesh_CacheArray_ValidateState(9);
+ }
+ //r_mesh_rcachesequentialchain_current = l;
+ //R_Mesh_CacheArray_ValidateState(5);
+ // gobble an extra item if we have no free items available
+ if (r_mesh_rcachefreechain.next == &r_mesh_rcachefreechain)
+ {
+ lnext = l->next;
+
+ // unlink from sequential chain
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+ // link into free chain
+ l->next = &r_mesh_rcachefreechain;
+ l->prev = l->next->prev;
+ l->next->prev = l->prev->next = l;
+
+ l = &l->data->hashlink;
+ // unlink from hash chain
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+
+ l = lnext;
+ }
+ r_mesh_rcachesequentialchain_current = l;
+ //R_Mesh_CacheArray_ValidateState(6);
+
+ // now take an item from the free chain
+ l = r_mesh_rcachefreechain.next;
+ // set it up
+ d = l->data;
+ d->request = *r;
+ d->offset = offset;
+ // unlink
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+ // relink to sequential
+ l->next = r_mesh_rcachesequentialchain_current->prev;
+ l->prev = l->next->prev;
+ while (l->next->data && l->data && l->next->data->offset <= d->offset)
+ {
+ //Con_Printf(">\n");
+ l->next = l->next->next;
+ l->prev = l->prev->next;
+ }
+ while (l->prev->data && l->data && l->prev->data->offset >= d->offset)
+ {
+ //Con_Printf("<\n");
+ l->prev = l->prev->prev;
+ l->next = l->next->prev;
+ }
+ l->next->prev = l->prev->next = l;
+ // also link into hash chain
+ l = &l->data->hashlink;
+ l->next = &r_mesh_rcachechain[hashindex];
+ l->prev = l->next->prev;
+ l->prev->next = l;
+ l->next->prev = l->prev->next = l;
+
+
+ //r_mesh_rcachesequentialchain_current = d->sequentiallink.next;
+
+ //R_Mesh_CacheArray_ValidateState(7);
+ // and finally set the data pointer
+ r->data = r_mesh_rcachedata + d->offset;
+ // and tell the caller to fill the array
+ return true;
+}
+