/* * RB_BindVBO */ void RB_BindVBO( int id, int primitive ) { mesh_vbo_t *vbo; rb.primitive = primitive; if( id < RB_VBO_NONE ) { vbo = rb.dynamicStreams[-id - 1].vbo; } else if( id == RB_VBO_NONE ) { vbo = NULL; } else { vbo = R_GetVBOByIndex( id ); } rb.currentVBOId = id; rb.currentVBO = vbo; if( !vbo ) { RB_BindArrayBuffer( 0 ); RB_BindElementArrayBuffer( 0 ); return; } RB_BindArrayBuffer( vbo->vertexId ); RB_BindElementArrayBuffer( vbo->elemId ); }
/* * RB_BindVBO */ void RB_BindVBO( int id, int primitive ) { mesh_vbo_t *vbo; vboSlice_t *batch; if( rb.currentVBOId == id ) { return; } if( id < RB_VBO_NONE ) { vbo = rb.streamVBOs[-id - 1]; batch = &rb.batches[-id - 1]; } else if( id == RB_VBO_NONE ) { vbo = NULL; batch = NULL; } else { vbo = R_GetVBOByIndex( id ); batch = NULL; } rb.primitive = primitive; rb.currentVBOId = id; rb.currentVBO = vbo; rb.currentBatch = batch; if( !vbo ) { RB_BindArrayBuffer( 0 ); RB_BindElementArrayBuffer( 0 ); return; } RB_BindArrayBuffer( vbo->vertexId ); RB_BindElementArrayBuffer( vbo->elemId ); }
/* * R_UploadVBOElemData * * Upload elements into the buffer, properly offsetting them (batching) */ void R_UploadVBOElemData( mesh_vbo_t *vbo, int vertsOffset, int elemsOffset, const mesh_t *mesh, vbo_hint_t hint ) { int i; elem_t *ielems; assert( vbo != NULL ); if( !vbo->elemId ) return; if( hint == VBO_HINT_ELEMS_QUAD ) { R_UploadVBOElemQuadData( vbo, vertsOffset, elemsOffset, mesh->numVerts ); return; } if( hint == VBO_HINT_ELEMS_TRIFAN ) { R_UploadVBOElemTrifanData( vbo, vertsOffset, elemsOffset, mesh->numVerts ); return; } ielems = R_VBOElemBuffer( mesh->numElems ); for( i = 0; i < mesh->numElems; i++ ) { ielems[i] = vertsOffset + mesh->elems[i]; } RB_BindElementArrayBuffer( vbo->elemId ); qglBufferSubDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, elemsOffset * sizeof( elem_t ), mesh->numElems * sizeof( elem_t ), ielems ); }
/* * R_DiscardVBOElemData */ void R_DiscardVBOElemData( mesh_vbo_t *vbo ) { GLenum elem_usage = VBO_ELEM_USAGE_FOR_TAG(vbo->tag); if( vbo->elemId ) { RB_BindElementArrayBuffer( vbo->elemId ); qglBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, vbo->elemBufferSize, NULL, elem_usage ); } }
/* * R_UploadVBOElemTrifanData * * Builds and uploads indexes in trifan order, properly offsetting them for batching */ static int R_UploadVBOElemTrifanData( mesh_vbo_t *vbo, int vertsOffset, int elemsOffset, int numVerts ) { int numElems; elem_t *ielems; assert( vbo != NULL ); if( !vbo->elemId ) return 0; numElems = (numVerts - 2) * 3; ielems = R_VBOElemBuffer( numElems ); R_BuildTrifanElements( vertsOffset, numVerts, ielems ); RB_BindElementArrayBuffer( vbo->elemId ); qglBufferSubDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, elemsOffset * sizeof( elem_t ), numElems * sizeof( elem_t ), ielems ); return numElems; }
/* * R_CreateMeshVBO * * Create two static buffer objects: vertex buffer and elements buffer, the real * data is uploaded by calling R_UploadVBOVertexData and R_UploadVBOElemData. * * Tag allows vertex buffer objects to be grouped and released simultaneously. */ mesh_vbo_t *R_CreateMeshVBO( void *owner, int numVerts, int numElems, int numInstances, vattribmask_t vattribs, vbo_tag_t tag, vattribmask_t halfFloatVattribs ) { int i; size_t size; GLuint vbo_id; vbohandle_t *vboh = NULL; mesh_vbo_t *vbo = NULL; GLenum array_usage = VBO_ARRAY_USAGE_FOR_TAG(tag); GLenum elem_usage = VBO_ELEM_USAGE_FOR_TAG(tag); size_t vertexSize; vattribbit_t lmattrbit; if( !glConfig.ext.vertex_buffer_object ) return NULL; if( !r_free_vbohandles ) return NULL; if( !glConfig.ext.half_float_vertex ) { halfFloatVattribs = 0; } else { if( !(halfFloatVattribs & VATTRIB_POSITION_BIT) ) { halfFloatVattribs &= ~(VATTRIB_AUTOSPRITE_BIT); } halfFloatVattribs &= ~VATTRIB_COLORS_BITS; halfFloatVattribs &= ~VATTRIB_BONES_BITS; // TODO: convert quaternion component of instance_t to half-float // when uploading instances data halfFloatVattribs &= ~VATTRIB_INSTANCES_BITS; } vboh = r_free_vbohandles; vbo = &r_mesh_vbo[vboh->index]; memset( vbo, 0, sizeof( *vbo ) ); // vertex data vertexSize = 0; vertexSize += FLOAT_VATTRIB_SIZE(VATTRIB_POSITION_BIT, halfFloatVattribs) * 4; // normals data if( vattribs & VATTRIB_NORMAL_BIT ) { assert( !(vertexSize & 3) ); vbo->normalsOffset = vertexSize; vertexSize += FLOAT_VATTRIB_SIZE(VATTRIB_NORMAL_BIT, halfFloatVattribs) * 4; } // s-vectors (tangent vectors) if( vattribs & VATTRIB_SVECTOR_BIT ) { assert( !(vertexSize & 3) ); vbo->sVectorsOffset = vertexSize; vertexSize += FLOAT_VATTRIB_SIZE(VATTRIB_SVECTOR_BIT, halfFloatVattribs) * 4; } // texture coordinates if( vattribs & VATTRIB_TEXCOORDS_BIT ) { assert( !(vertexSize & 3) ); vbo->stOffset = vertexSize; vertexSize += FLOAT_VATTRIB_SIZE(VATTRIB_TEXCOORDS_BIT, halfFloatVattribs) * 2; } // lightmap texture coordinates lmattrbit = VATTRIB_LMCOORDS0_BIT; for( i = 0; i < MAX_LIGHTMAPS/2; i++ ) { if( !(vattribs & lmattrbit) ) { break; } assert( !(vertexSize & 3) ); vbo->lmstOffset[i] = vertexSize; vbo->lmstSize[i] = vattribs & VATTRIB_LMCOORDS0_BIT<<1 ? 4 : 2; vertexSize += FLOAT_VATTRIB_SIZE(VATTRIB_LMCOORDS0_BIT, halfFloatVattribs) * vbo->lmstSize[i]; lmattrbit = ( vattribbit_t )( ( vattribmask_t )lmattrbit << 2 ); } // vertex colors if( vattribs & VATTRIB_COLOR0_BIT ) { assert( !(vertexSize & 3) ); vbo->colorsOffset[0] = vertexSize; vertexSize += sizeof( int ); } // bones data for skeletal animation if( (vattribs & VATTRIB_BONES_BITS) == VATTRIB_BONES_BITS ) { assert( SKM_MAX_WEIGHTS == 4 ); assert( !(vertexSize & 3) ); vbo->bonesIndicesOffset = vertexSize; vertexSize += sizeof( int ); assert( !(vertexSize & 3) ); vbo->bonesWeightsOffset = vertexSize; vertexSize += sizeof( int ); } // autosprites // FIXME: autosprite2 requires waaaay too much data for such a trivial // transformation.. if( (vattribs & VATTRIB_AUTOSPRITE_BIT) == VATTRIB_AUTOSPRITE_BIT ) { assert( !(vertexSize & 3) ); vbo->spritePointsOffset = vertexSize; vertexSize += FLOAT_VATTRIB_SIZE(VATTRIB_AUTOSPRITE_BIT, halfFloatVattribs) * 4; } size = vertexSize * numVerts; // instances data if( ( (vattribs & VATTRIB_INSTANCES_BITS) == VATTRIB_INSTANCES_BITS ) && numInstances && glConfig.ext.instanced_arrays ) { assert( !(vertexSize & 3) ); vbo->instancesOffset = size; size += numInstances * sizeof( GLfloat ) * 8; } // pre-allocate vertex buffer vbo_id = 0; qglGenBuffersARB( 1, &vbo_id ); if( !vbo_id ) goto error; vbo->vertexId = vbo_id; RB_BindArrayBuffer( vbo->vertexId ); qglBufferDataARB( GL_ARRAY_BUFFER_ARB, size, NULL, array_usage ); if( qglGetError () == GL_OUT_OF_MEMORY ) goto error; vbo->arrayBufferSize = size; // pre-allocate elements buffer vbo_id = 0; qglGenBuffersARB( 1, &vbo_id ); if( !vbo_id ) goto error; vbo->elemId = vbo_id; size = numElems * sizeof( elem_t ); RB_BindElementArrayBuffer( vbo->elemId ); qglBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, size, NULL, elem_usage ); if( qglGetError () == GL_OUT_OF_MEMORY ) goto error; vbo->elemBufferSize = size; r_free_vbohandles = vboh->next; // link to the list of active vbo handles vboh->prev = &r_vbohandles_headnode; vboh->next = r_vbohandles_headnode.next; vboh->next->prev = vboh; vboh->prev->next = vboh; r_num_active_vbos++; vbo->registrationSequence = rsh.registrationSequence; vbo->vertexSize = vertexSize; vbo->numVerts = numVerts; vbo->numElems = numElems; vbo->owner = owner; vbo->index = vboh->index + 1; vbo->tag = tag; vbo->halfFloatAttribs = halfFloatVattribs; return vbo; error: if( vbo ) R_ReleaseMeshVBO( vbo ); RB_BindArrayBuffer( 0 ); RB_BindElementArrayBuffer( 0 ); return NULL; }