/* * R_UploadVBOVertexData * * Uploads required vertex data to the buffer. * * Vertex attributes masked by halfFloatVattribs will use half-precision floats * to save memory, if GL_ARB_half_float_vertex is available. Note that if * VATTRIB_POSITION_BIT is not set, it will also reset bits for other positional * attributes such as autosprite pos and instance pos. */ vattribmask_t R_UploadVBOVertexData( mesh_vbo_t *vbo, int vertsOffset, vattribmask_t vattribs, const mesh_t *mesh, vbo_hint_t hint ) { int i, j; unsigned numVerts; size_t vertSize; vattribmask_t errMask; vattribmask_t hfa; qbyte *data; assert( vbo != NULL ); assert( mesh != NULL ); if( !vbo || !vbo->vertexId ) { return 0; } errMask = 0; numVerts = mesh->numVerts; vertSize = vbo->vertexSize; hfa = vbo->halfFloatAttribs; data = R_VBOVertBuffer( numVerts, vertSize ); RB_BindArrayBuffer( vbo->vertexId ); // upload vertex xyz data if( vattribs & VATTRIB_POSITION_BIT ) { if( !mesh->xyzArray ) { errMask |= VATTRIB_POSITION_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_POSITION_BIT, hfa ), mesh->xyzArray[0], 4, vertSize, numVerts, data + 0 ); } } // upload normals data if( vbo->normalsOffset && (vattribs & VATTRIB_NORMAL_BIT) ) { if( !mesh->normalsArray ) { errMask |= VATTRIB_NORMAL_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_NORMAL_BIT, hfa ), mesh->normalsArray[0], 4, vertSize, numVerts, data + vbo->normalsOffset ); } } // upload tangent vectors if( vbo->sVectorsOffset && ( ( vattribs & (VATTRIB_SVECTOR_BIT|VATTRIB_AUTOSPRITE2_BIT) ) == VATTRIB_SVECTOR_BIT ) ) { if( !mesh->sVectorsArray ) { errMask |= VATTRIB_SVECTOR_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_SVECTOR_BIT, hfa ), mesh->sVectorsArray[0], 4, vertSize, numVerts, data + vbo->sVectorsOffset ); } } // upload texture coordinates if( vbo->stOffset && (vattribs & VATTRIB_TEXCOORDS_BIT) ) { if( !mesh->stArray ) { errMask |= VATTRIB_TEXCOORDS_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_TEXCOORDS_BIT, hfa ), mesh->stArray[0], 2, vertSize, numVerts, data + vbo->stOffset ); } } // upload lightmap texture coordinates if( vbo->lmstOffset[0] && ( vattribs & VATTRIB_LMCOORDS0_BIT ) ) { int i; vattribbit_t lmattrbit; lmattrbit = VATTRIB_LMCOORDS0_BIT; for( i = 0; i < MAX_LIGHTMAPS/2; i++ ) { if( !(vattribs & lmattrbit) ) { break; } if( !mesh->lmstArray[i*2+0] ) { errMask |= lmattrbit; break; } R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_LMCOORDS0_BIT, hfa ), mesh->lmstArray[i*2+0][0], 2, vertSize, numVerts, data + vbo->lmstOffset[i] ); if( vattribs & (lmattrbit<<1) ) { if( !mesh->lmstArray[i*2+1] ) { errMask |= lmattrbit<<1; break; } R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_LMCOORDS0_BIT, hfa ), mesh->lmstArray[i*2+1][0], 2, vertSize, numVerts, data + vbo->lmstOffset[i] + 2 * sizeof( float ) ); } lmattrbit <<= 2; } } // upload vertex colors (although indices > 0 are never used) if( vbo->colorsOffset[0] && (vattribs & VATTRIB_COLOR0_BIT) ) { if( !mesh->colorsArray[0] ) { errMask |= VATTRIB_COLOR0_BIT; } else { R_FillVertexBuffer( int, int, (int *)&mesh->colorsArray[0][0], 1, vertSize, numVerts, data + vbo->colorsOffset[0] ); } } // upload centre and radius for autosprites // this code assumes that the mesh has been properly pretransformed if( vbo->spritePointsOffset && ( (vattribs & VATTRIB_AUTOSPRITE2_BIT) == VATTRIB_AUTOSPRITE2_BIT ) ) { // for autosprite2 also upload vertices that form the longest axis // the remaining vertex can be trivially computed in vertex shader vec3_t vd[3]; float d[3]; int longest_edge = -1, longer_edge = -1, short_edge; float longest_dist = 0, longer_dist = 0; const int edges[3][2] = { { 1, 0 }, { 2, 0 }, { 2, 1 } }; vec4_t centre[4]; vec4_t axes[4]; vec4_t *verts = mesh->xyzArray; elem_t *elems, temp_elems[6]; int numQuads; size_t bufferOffset0 = vbo->spritePointsOffset; size_t bufferOffset1 = vbo->sVectorsOffset; if( hint == VBO_HINT_ELEMS_QUAD ) { numQuads = numVerts / 4; } else { assert( mesh->elems != NULL ); if( !mesh->elems ) { numQuads = 0; } else { numQuads = mesh->numElems / 6; } } for( i = 0, elems = mesh->elems; i < numQuads; i++, elems += 6 ) { if( hint == VBO_HINT_ELEMS_QUAD ) { elem_t firstV = i * 4; temp_elems[0] = firstV; temp_elems[1] = firstV + 2 - 1; temp_elems[2] = firstV + 2; temp_elems[3] = firstV; temp_elems[4] = firstV + 3 - 1; temp_elems[5] = firstV + 3; elems = temp_elems; } // find the longest edge, the long edge and the short edge longest_edge = longer_edge = -1; longest_dist = longer_dist = 0; for( j = 0; j < 3; j++ ) { float len; VectorSubtract( verts[elems[edges[j][0]]], verts[elems[edges[j][1]]], vd[j] ); len = VectorLength( vd[j] ); if( !len ) { len = 1; } d[j] = len; if( longest_edge == -1 || longest_dist < len ) { longer_dist = longest_dist; longer_edge = longest_edge; longest_dist = len; longest_edge = j; } else if( longer_dist < len ) { longer_dist = len; longer_edge = j; } } short_edge = 3 - (longest_edge + longer_edge); if( short_edge > 2 ) { continue; } // centre VectorAdd( verts[elems[edges[longest_edge][0]]], verts[elems[edges[longest_edge][1]]], centre[0] ); VectorScale( centre[0], 0.5, centre[0] ); // radius centre[0][3] = d[longest_edge] * 0.5; // unused // right axis, normalized VectorScale( vd[short_edge], 1.0 / d[short_edge], vd[short_edge] ); // up axis, normalized VectorScale( vd[longer_edge], 1.0 / d[longer_edge], vd[longer_edge] ); NormToLatLong( vd[short_edge], &axes[0][0] ); NormToLatLong( vd[longer_edge], &axes[0][2] ); for( j = 1; j < 4; j++ ) { Vector4Copy( centre[0], centre[j] ); Vector4Copy( axes[0], axes[j] ); } R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_AUTOSPRITE_BIT, hfa ), centre[0], 4, vertSize, 4, data + bufferOffset0 ); R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_SVECTOR_BIT, hfa ), axes[0], 4, vertSize, 4, data + bufferOffset1 ); bufferOffset0 += 4 * vertSize; bufferOffset1 += 4 * vertSize; } } else if( vbo->spritePointsOffset && ( (vattribs & VATTRIB_AUTOSPRITE_BIT) == VATTRIB_AUTOSPRITE_BIT ) ) { vec4_t *verts; vec4_t centre[4]; int numQuads = numVerts / 4; size_t bufferOffset = vbo->spritePointsOffset; for( i = 0, verts = mesh->xyzArray; i < numQuads; i++, verts += 4 ) { // centre for( j = 0; j < 3; j++ ) { centre[0][j] = (verts[0][j] + verts[1][j] + verts[2][j] + verts[3][j]) * 0.25; } // radius centre[0][3] = Distance( verts[0], centre[0] ) * 0.707106f; // 1.0f / sqrt(2) for( j = 1; j < 4; j++ ) { Vector4Copy( centre[0], centre[j] ); } R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_AUTOSPRITE_BIT, hfa ), centre[0], 4, vertSize, 4, data + bufferOffset ); bufferOffset += 4 * vertSize; } } if( vattribs & VATTRIB_BONES_BITS ) { if( vbo->bonesIndicesOffset ) { if( !mesh->blendIndices ) { errMask |= VATTRIB_BONESINDICES_BIT; } else { R_FillVertexBuffer( int, int, (int *)&mesh->blendIndices[0], 1, vertSize, numVerts, data + vbo->bonesIndicesOffset ); } } if( vbo->bonesWeightsOffset ) { if( !mesh->blendWeights ) { errMask |= VATTRIB_BONESWEIGHTS_BIT; } else { R_FillVertexBuffer( int, int, (int *)&mesh->blendWeights[0], 1, vertSize, numVerts, data + vbo->bonesWeightsOffset ); } } } qglBufferSubDataARB( GL_ARRAY_BUFFER_ARB, vertsOffset * vertSize, numVerts * vertSize, data ); return errMask; }
/* * R_FillVBOVertexDataBuffer * * Generates required vertex data to be uploaded to the buffer. * * Vertex attributes masked by halfFloatVattribs will use half-precision floats * to save memory, if GL_ARB_half_float_vertex is available. Note that if * VATTRIB_POSITION_BIT is not set, it will also reset bits for other positional * attributes such as autosprite pos and instance pos. */ vattribmask_t R_FillVBOVertexDataBuffer( mesh_vbo_t *vbo, vattribmask_t vattribs, const mesh_t *mesh, void *outData ) { int i, j; unsigned numVerts; size_t vertSize; vattribmask_t errMask; vattribmask_t hfa; uint8_t *data = outData; assert( vbo != NULL ); assert( mesh != NULL ); if( !vbo ) { return 0; } errMask = 0; numVerts = mesh->numVerts; vertSize = vbo->vertexSize; hfa = vbo->halfFloatAttribs; // upload vertex xyz data if( vattribs & VATTRIB_POSITION_BIT ) { if( !mesh->xyzArray ) { errMask |= VATTRIB_POSITION_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_POSITION_BIT, hfa ), mesh->xyzArray[0], 4, vertSize, numVerts, data + 0 ); } } // upload normals data if( vbo->normalsOffset && (vattribs & VATTRIB_NORMAL_BIT) ) { if( !mesh->normalsArray ) { errMask |= VATTRIB_NORMAL_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_NORMAL_BIT, hfa ), mesh->normalsArray[0], 4, vertSize, numVerts, data + vbo->normalsOffset ); } } // upload tangent vectors if( vbo->sVectorsOffset && ( ( vattribs & (VATTRIB_SVECTOR_BIT|VATTRIB_AUTOSPRITE2_BIT) ) == VATTRIB_SVECTOR_BIT ) ) { if( !mesh->sVectorsArray ) { errMask |= VATTRIB_SVECTOR_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_SVECTOR_BIT, hfa ), mesh->sVectorsArray[0], 4, vertSize, numVerts, data + vbo->sVectorsOffset ); } } // upload texture coordinates if( vbo->stOffset && (vattribs & VATTRIB_TEXCOORDS_BIT) ) { if( !mesh->stArray ) { errMask |= VATTRIB_TEXCOORDS_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_TEXCOORDS_BIT, hfa ), mesh->stArray[0], 2, vertSize, numVerts, data + vbo->stOffset ); } } // upload lightmap texture coordinates if( vbo->lmstOffset[0] && ( vattribs & VATTRIB_LMCOORDS0_BIT ) ) { vattribbit_t lmattrbit; int type = FLOAT_VATTRIB_GL_TYPE( VATTRIB_LMCOORDS0_BIT, hfa ); int lmstSize = ( ( type == GL_HALF_FLOAT ) ? 2 * sizeof( GLhalfARB ) : 2 * sizeof( float ) ); lmattrbit = VATTRIB_LMCOORDS0_BIT; for( i = 0; i < ( MAX_LIGHTMAPS + 1 ) / 2; i++ ) { if( !(vattribs & lmattrbit) ) { break; } if( !mesh->lmstArray[i*2+0] ) { errMask |= lmattrbit; break; } R_FillVertexBuffer_float_or_half( type, mesh->lmstArray[i*2+0][0], 2, vertSize, numVerts, data + vbo->lmstOffset[i] ); if( vattribs & (lmattrbit<<1) ) { if( !mesh->lmstArray[i*2+1] ) { errMask |= lmattrbit<<1; break; } R_FillVertexBuffer_float_or_half( type, mesh->lmstArray[i*2+1][0], 2, vertSize, numVerts, data + vbo->lmstOffset[i] + lmstSize ); } lmattrbit <<= 2; } } // upload lightmap array texture layers if( vbo->lmlayersOffset[0] && ( vattribs & VATTRIB_LMLAYERS0123_BIT ) ) { vattribbit_t lmattrbit; lmattrbit = VATTRIB_LMLAYERS0123_BIT; for( i = 0; i < ( MAX_LIGHTMAPS + 3 ) / 4; i++ ) { if( !( vattribs & lmattrbit ) ) { break; } if( !mesh->lmlayersArray[i] ) { errMask |= lmattrbit; break; } R_FillVertexBuffer( int, int, ( int * )&mesh->lmlayersArray[i][0], 1, vertSize, numVerts, data + vbo->lmlayersOffset[i] ); lmattrbit <<= 1; } }