qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);CHECKGLERROR
}
+int R_Mesh_CreateStaticEBO(void *data, size_t size)
+{
+ GLuint bufferobject;
+ qglGenBuffersARB(1, &bufferobject);
+ GL_BindEBO(bufferobject);
+ qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, size, data, GL_STATIC_DRAW_ARB);
+ return (int)bufferobject;
+}
+
+void R_Mesh_DestroyEBO(int bufferobject)
+{
+ qglDeleteBuffersARB(1, (GLuint *)&bufferobject);
+}
+
+int R_Mesh_CreateStaticVBO(void *data, size_t size)
+{
+ GLuint bufferobject;
+ qglGenBuffersARB(1, &bufferobject);
+ GL_BindVBO(bufferobject);
+ qglBufferDataARB(GL_ARRAY_BUFFER_ARB, size, data, GL_STATIC_DRAW_ARB);
+ return (int)bufferobject;
+}
+
+void R_Mesh_DestroyVBO(int bufferobject)
+{
+ qglDeleteBuffersARB(1, (GLuint *)&bufferobject);
+}
+
void R_Mesh_Matrix(const matrix4x4_t *matrix)
{
if (memcmp(matrix, &backend_modelmatrix, sizeof(matrix4x4_t)))
// (only valid after R_Mesh_Start)
void R_Mesh_Finish(void);
+// allocates a static element array buffer object
+// (storing triangle data in video memory)
+int R_Mesh_CreateStaticEBO(void *data, size_t size);
+// frees an element array buffer object
+void R_Mesh_DestroyEBO(int bufferobject);
+// allocates a static vertex array buffer object
+// (storing vertex data in video memory)
+int R_Mesh_CreateStaticVBO(void *data, size_t size);
+// frees a vertex array buffer object
+void R_Mesh_DestroyVBO(int bufferobject);
+
// sets up the requested vertex transform matrix
void R_Mesh_Matrix(const matrix4x4_t *matrix);
// sets the vertex array pointer
R_Shadow_MarkVolumeFromBox(surface->num_firstshadowmeshtriangle, surface->num_triangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, relativelightorigin, relativelightdirection, r_shadow_compilingrtlight->cullmins, r_shadow_compilingrtlight->cullmaxs, surface->mins, surface->maxs);
}
R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, relativelightdirection, projectdistance, numshadowmark, shadowmarklist);
- r_shadow_compilingrtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, false, false);
+ r_shadow_compilingrtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, false, false, true);
}
void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativelightdirection, float lightradius, int modelnumsurfaces, const int *modelsurfacelist, const vec3_t lightmins, const vec3_t lightmaxs)
loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
- loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
+ loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
if (loadmodel->brush.numsubmodels)
for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
if (surface->num_triangles > 0)
Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
- loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
+ loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
loadmodel->brush.num_leafs = 0;
*/
static void Mod_Print(void);
static void Mod_Precache (void);
+static void Mod_BuildVBOs(void);
void Mod_Init (void)
{
Mod_BrushInit();
strlcpy(name, mod->name, sizeof(name));
isworldmodel = mod->isworldmodel;
used = mod->used;
+ if (mod->surfmesh.ebo)
+ R_Mesh_DestroyEBO(mod->surfmesh.ebo);
+ if (mod->surfmesh.vbo)
+ R_Mesh_DestroyVBO(mod->surfmesh.vbo);
// free textures/memory attached to the model
R_FreeTexturePool(&mod->texturepool);
Mem_FreePool(&mod->mempool);
else if (num == BSPVERSION || num == 30) Mod_Q1BSP_Load(mod, buf, bufend);
else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
Mem_Free(buf);
+
+ Mod_BuildVBOs();
+
// no fatal errors occurred, so this model is ready to use.
mod->loaded = true;
}
return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles, map_diffuse, map_specular, map_normal, light, neighbors, expandable);
}
-shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, int light, int neighbors)
+static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
+{
+ if (!gl_support_arb_vertex_buffer_object)
+ return;
+
+ // element buffer is easy because it's just one array
+ if (mesh->numtriangles)
+ mesh->ebo = R_Mesh_CreateStaticEBO(mesh->element3i, mesh->numtriangles * sizeof(int[3]));
+
+ // vertex buffer is several arrays and we put them in the same buffer
+ //
+ // is this wise? the texcoordtexture2f array is used with dynamic
+ // vertex/svector/tvector/normal when rendering animated models, on the
+ // other hand animated models don't use a lot of vertices anyway...
+ if (mesh->numverts)
+ {
+ size_t size;
+ unsigned char *data, *mem;
+ size = 0;
+ mesh->vbooffset_vertex3f = size;if (mesh->vertex3f ) size += mesh->numverts * sizeof(float[3]);
+ mesh->vbooffset_svector3f = size;if (mesh->svector3f ) size += mesh->numverts * sizeof(float[3]);
+ mesh->vbooffset_tvector3f = size;if (mesh->tvector3f ) size += mesh->numverts * sizeof(float[3]);
+ mesh->vbooffset_normal3f = size;if (mesh->normal3f ) size += mesh->numverts * sizeof(float[3]);
+ mesh->vbooffset_texcoord2f = size;if (mesh->texcoord2f ) size += mesh->numverts * sizeof(float[2]);
+ data = mem = (unsigned char *)Mem_Alloc(tempmempool, size);
+ if (mesh->vertex3f ) {memcpy(data, mesh->vertex3f , mesh->numverts * sizeof(float[3]));data += mesh->numverts * sizeof(float[3]);}
+ if (mesh->svector3f ) {memcpy(data, mesh->svector3f , mesh->numverts * sizeof(float[3]));data += mesh->numverts * sizeof(float[3]);}
+ if (mesh->tvector3f ) {memcpy(data, mesh->tvector3f , mesh->numverts * sizeof(float[3]));data += mesh->numverts * sizeof(float[3]);}
+ if (mesh->normal3f ) {memcpy(data, mesh->normal3f , mesh->numverts * sizeof(float[3]));data += mesh->numverts * sizeof(float[3]);}
+ if (mesh->texcoord2f ) {memcpy(data, mesh->texcoord2f , mesh->numverts * sizeof(float[2]));data += mesh->numverts * sizeof(float[2]);}
+ mesh->vbo = R_Mesh_CreateStaticVBO(mem, size);
+ Mem_Free(mem);
+ }
+}
+
+shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, qboolean light, qboolean neighbors, qboolean createvbo)
{
shadowmesh_t *mesh, *newmesh, *nextmesh;
// reallocate meshs to conserve space
newmesh = Mod_ShadowMesh_ReAlloc(mempool, mesh, light, neighbors);
newmesh->next = firstmesh;
firstmesh = newmesh;
+ if (createvbo)
+ Mod_ShadowMesh_CreateVBOs(newmesh);
}
Mem_Free(mesh);
}
shadowmesh_t *nextmesh;
for (;mesh;mesh = nextmesh)
{
+ if (mesh->ebo)
+ R_Mesh_DestroyEBO(mesh->ebo);
+ if (mesh->vbo)
+ R_Mesh_DestroyVBO(mesh->vbo);
nextmesh = mesh->next;
Mem_Free(mesh);
}
if (lastvertexpointer)
*lastvertexpointer = lastvertex;
}
+
+static void Mod_BuildVBOs(void)
+{
+ if (!gl_support_arb_vertex_buffer_object)
+ return;
+
+ // element buffer is easy because it's just one array
+ if (loadmodel->surfmesh.num_triangles)
+ loadmodel->surfmesh.ebo = R_Mesh_CreateStaticEBO(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+
+ // vertex buffer is several arrays and we put them in the same buffer
+ //
+ // is this wise? the texcoordtexture2f array is used with dynamic
+ // vertex/svector/tvector/normal when rendering animated models, on the
+ // other hand animated models don't use a lot of vertices anyway...
+ if (loadmodel->surfmesh.num_vertices)
+ {
+ size_t size;
+ unsigned char *data, *mem;
+ size = 0;
+ loadmodel->surfmesh.vbooffset_vertex3f = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+ loadmodel->surfmesh.vbooffset_svector3f = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+ loadmodel->surfmesh.vbooffset_tvector3f = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+ loadmodel->surfmesh.vbooffset_normal3f = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+ loadmodel->surfmesh.vbooffset_texcoordtexture2f = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+ loadmodel->surfmesh.vbooffset_texcoordlightmap2f = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+ loadmodel->surfmesh.vbooffset_lightmapcolor4f = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
+ data = mem = (unsigned char *)Mem_Alloc(tempmempool, size);
+ if (loadmodel->surfmesh.data_vertex3f ) {memcpy(data, loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);}
+ if (loadmodel->surfmesh.data_svector3f ) {memcpy(data, loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);}
+ if (loadmodel->surfmesh.data_tvector3f ) {memcpy(data, loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);}
+ if (loadmodel->surfmesh.data_normal3f ) {memcpy(data, loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);}
+ if (loadmodel->surfmesh.data_texcoordtexture2f ) {memcpy(data, loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);}
+ if (loadmodel->surfmesh.data_texcoordlightmap2f) {memcpy(data, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);}
+ if (loadmodel->surfmesh.data_lightmapcolor4f ) {memcpy(data, loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));data += loadmodel->surfmesh.num_vertices * sizeof(float[4]);}
+ loadmodel->surfmesh.vbo = R_Mesh_CreateStaticVBO(mem, size);
+ Mem_Free(mem);
+ }
+}
// (the surfaces reference portions of these meshes)
typedef struct surfmesh_s
{
+ // triangle data in system memory
int num_triangles; // number of triangles in the mesh
int *data_element3i; // int[tris*3] triangles of the mesh, 3 indices into vertex arrays for each
int *data_neighbor3i; // int[tris*3] neighboring triangle on each edge (-1 if none)
+ // element buffer object (stores triangles in video memory)
+ int ebo;
+ // vertex data in system memory
int num_vertices; // number of vertices in the mesh
float *data_vertex3f; // float[verts*3] vertex locations
float *data_svector3f; // float[verts*3] direction of 'S' (right) texture axis for each vertex
float *data_texcoordlightmap2f; // float[verts*2] texcoords for lightmap texture
float *data_lightmapcolor4f;
int *data_lightmapoffsets; // index into surface's lightmap samples for vertex lighting
+ // vertex buffer object (stores geometry in video memory)
+ int vbo;
+ size_t vbooffset_vertex3f;
+ size_t vbooffset_svector3f;
+ size_t vbooffset_tvector3f;
+ size_t vbooffset_normal3f;
+ size_t vbooffset_texcoordtexture2f;
+ size_t vbooffset_texcoordlightmap2f;
+ size_t vbooffset_lightmapcolor4f;
// morph blending, these are zero if model is skeletal or static
int num_morphframes;
struct md3vertex_s *data_morphmd3vertex;
// these are NULL after Mod_ShadowMesh_Finish is performed, only used
// while building meshes
shadowmeshvertexhash_t **vertexhashtable, *vertexhashentries;
+ // element buffer object (stores triangles in video memory)
+ // (created by Mod_ShadowMesh_Finish if possible)
+ int ebo;
+ // vertex buffer object (stores vertices in video memory)
+ // (created by Mod_ShadowMesh_Finish if possible)
+ int vbo;
+ size_t vbooffset_vertex3f;
+ size_t vbooffset_svector3f;
+ size_t vbooffset_tvector3f;
+ size_t vbooffset_normal3f;
+ size_t vbooffset_texcoord2f;
}
shadowmesh_t;
void Mod_ShadowMesh_AddTriangle(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, float *vertex14f);
void Mod_ShadowMesh_AddMesh(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, int numtris, const int *element3i);
shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, int light, int neighbors, int expandable);
-shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, int light, int neighbors);
+shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, qboolean light, qboolean neighbors, qboolean createvbo);
void Mod_ShadowMesh_CalcBBox(shadowmesh_t *firstmesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius);
void Mod_ShadowMesh_Free(shadowmesh_t *mesh);
- todo: difficulty ratings are: 0 = trivial, 1 = easy, 2 = easy-moderate, 3 = moderate, 4 = moderate-hard, 5 = hard, 6 = hard++, 7 = nightmare, d = done, -d = done but have not notified the people who asked for it, f = failed, -f = failed but have not notified the people who asked for it
+0 bug darkplaces memory: memstats doesn't account for memory used by VBO/EBO buffers in models
0 bug darkplaces client: can't move mouse around in nexuiz menu if vid_mouse is 0
0 bug darkplaces client: decals are not sticking to submodels
0 bug darkplaces client: if you press 1 during the demo loop when quake starts, escape doesn't do anything until you hit some other key (daemon)