Exemple #1
0
/*
* RB_EndBatch
*/
void RB_EndBatch( void )
{
	int stream;
	vboSlice_t *batch;
	vboSlice_t *offset;

	if( rb.currentVBOId >= RB_VBO_NONE ) {
		return;
	}

	stream = -rb.currentVBOId - 1;
	offset = &rb.streamOffset[stream];
	batch = &rb.batches[stream];

	if( batch->numVerts ) {
		RB_UploadBatchMesh( batch );
	}

	if( !offset->numVerts || !offset->numElems ) {
		return;
	}

	RB_DrawElements( offset->firstVert, offset->numVerts, offset->firstElem, offset->numElems );

	offset->firstVert += offset->numVerts;
	offset->firstElem += offset->numElems;
	offset->numVerts = offset->numElems = 0;
}
Exemple #2
0
/*
* R_DrawBSPSurf
*/
qboolean R_DrawBSPSurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, drawSurfaceBSP_t *drawSurf )
{
	vboSlice_t *slice;

	slice = R_GetVBOSlice( drawSurf - rsh.worldBrushModel->drawSurfaces );
	assert( slice != NULL );

	RB_BindVBO( drawSurf->vbo->index, GL_TRIANGLES );

	if( drawSurf->dlightFrame == rsc.frameCount ) {
		RB_SetDlightBits( drawSurf->dlightBits & rn.dlightBits );
	}
	else {
		RB_SetDlightBits( 0 );
	}

	if( drawSurf->shadowFrame == rsc.frameCount ) {
		RB_SetShadowBits( drawSurf->shadowBits & rn.shadowBits );
	}
	else {
		RB_SetShadowBits( 0 );
	}

	RB_SetLightstyle( drawSurf->superLightStyle );

	if( drawSurf->numInstances ) {
		RB_DrawElementsInstanced( slice->firstVert, slice->numVerts, slice->firstElem, slice->numElems, 
			drawSurf->numInstances, drawSurf->instances );
	}
	else {
		RB_DrawElements( slice->firstVert, slice->numVerts, slice->firstElem, slice->numElems );
	}

	return qfalse;
}
Exemple #3
0
/*
* R_DrawBSPSurf
*/
bool R_DrawBSPSurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, const portalSurface_t *portalSurface, drawSurfaceBSP_t *drawSurf )
{
	const vboSlice_t *slice;
	const vboSlice_t *shadowSlice;
	static const vboSlice_t nullSlice = { 0 };
	int firstVert, firstElem;
	int firstShadowVert, firstShadowElem;

	slice = R_GetVBOSlice( drawSurf - rsh.worldBrushModel->drawSurfaces );
	shadowSlice = R_GetVBOSlice( rsh.worldBrushModel->numDrawSurfaces + ( drawSurf - rsh.worldBrushModel->drawSurfaces ) );
	if( !shadowSlice ) {
		shadowSlice = &nullSlice;
	}

	assert( slice != NULL );

	RB_BindVBO( drawSurf->vbo->index, GL_TRIANGLES );

	if( drawSurf->dlightFrame == rsc.frameCount ) {
		RB_SetDlightBits( drawSurf->dlightBits & rn.dlightBits );
	}
	else {
		RB_SetDlightBits( 0 );
	}

	if( drawSurf->shadowFrame == rsc.frameCount ) {
		RB_SetShadowBits( (drawSurf->shadowBits & rn.shadowBits) & rsc.renderedShadowBits );
	}
	else {
		RB_SetShadowBits( 0 );
	}

	RB_SetLightstyle( drawSurf->superLightStyle );

	firstVert = drawSurf->firstVboVert + slice->firstVert;
	firstElem = drawSurf->firstVboElem + slice->firstElem;
	firstShadowVert = drawSurf->firstVboVert + shadowSlice->firstVert;
	firstShadowElem = drawSurf->firstVboElem + shadowSlice->firstElem;

	if( drawSurf->numInstances ) {
		RB_DrawElementsInstanced( firstVert, slice->numVerts, firstElem, slice->numElems, 
			firstShadowVert, shadowSlice->numVerts, firstShadowElem, shadowSlice->numElems,
			drawSurf->numInstances, drawSurf->instances );
	}
	else {
		RB_DrawElements( firstVert, slice->numVerts,firstElem, slice->numElems,
			firstShadowVert, shadowSlice->numVerts, firstShadowElem, shadowSlice->numElems );
	}

	return false;
}
Exemple #4
0
/*
* R_DrawBlackBottom
*
* Draw dummy skybox side to prevent the HOM effect
*/
static void R_DrawBlackBottom( const skydome_t *skydome, const visSkySide_t *visSides, const mfog_t *fog, drawSurfaceSky_t *drawSurf ) {
	int side = 5;
	const visSkySide_t *visSide = visSides + side;

	if( drawSurf->skyMins[0][side] >= drawSurf->skyMaxs[0][side] ||
		drawSurf->skyMins[1][side] >= drawSurf->skyMaxs[1][side] ) {
		return;
	}

	RB_BindShader( rsc.skyent, rsh.envShader, fog );

	RB_BindVBO( skydome->linearVbos[side]->index, GL_TRIANGLES );

	RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems, 0, 0, 0, 0 );
}
Exemple #5
0
/*
* R_DrawBlackBottom
* 
* Draw dummy skybox side to prevent the HOM effect
*/
static void R_DrawBlackBottom( const skydome_t *skydome, const visSkySide_t *visSides )
{
	int side = 5;
	const visSkySide_t *visSide = visSides + side;

	if( rn.skyMins[0][side] >= rn.skyMaxs[0][side] ||
		rn.skyMins[1][side] >= rn.skyMaxs[1][side] )
		return;

	RB_BindShader( rsc.worldent, rf.envShader, rn.skyFog );

	RB_BindVBO( skydome->linearVbos[side]->index, GL_TRIANGLES );

	RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems );
}
Exemple #6
0
/*
* R_DrawSkyBoxSide
*/
static void R_DrawSkyBoxSide( const skydome_t *skydome, const visSkySide_t *visSide, const shader_t *skyShader,
							  const shader_t *skyboxShader, const mfog_t *fog, int imageIndex, drawSurfaceSky_t *drawSurf ) {
	int side = visSide->index;

	if( drawSurf->skyMins[0][side] >= drawSurf->skyMaxs[0][side] ||
		drawSurf->skyMins[1][side] >= drawSurf->skyMaxs[1][side] ) {
		return;
	}

	RB_BindShader( rsc.skyent, skyShader, fog );

	RB_BindVBO( skydome->linearVbos[side]->index, GL_TRIANGLES );

	RB_SetSkyboxShader( skyboxShader );

	RB_SetSkyboxSide( imageIndex );

	RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems, 0, 0, 0, 0 );
}
Exemple #7
0
/*
* R_DrawSkyBoxSide
*/
static void R_DrawSkyBoxSide( const skydome_t *skydome, const visSkySide_t *visSide, const shader_t *shader, 
	int imageIndex )
{
	int side = visSide->index;

	if( rn.skyMins[0][side] >= rn.skyMaxs[0][side] ||
		rn.skyMins[1][side] >= rn.skyMaxs[1][side] )
		return;

	RB_BindShader( rsc.worldent, rf.skyShader, rn.skyFog );

	RB_BindVBO( skydome->linearVbos[side]->index, GL_TRIANGLES );

	RB_SetSkyboxShader( shader );

	RB_SetSkyboxSide( imageIndex );

	RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems );
}
Exemple #8
0
/*
* RB_UploadMesh
*/
void RB_UploadMesh( const mesh_t *mesh )
{
	int stream;
	mesh_vbo_t *vbo;
	vboSlice_t *offset;
	vbo_hint_t vbo_hint = VBO_HINT_NONE;
	int numVerts = mesh->numVerts, numElems = mesh->numElems;

	assert( rb.currentVBOId < RB_VBO_NONE );
	if( rb.currentVBOId >= RB_VBO_NONE ) {
		return;
	}
	
	if( rb.currentVBOId == RB_VBO_STREAM_QUAD ) {
		numElems = numVerts/4*6;
	} else if( !numElems && rb.currentVBOId == RB_VBO_STREAM ) {
		numElems = (max(numVerts, 2) - 2) * 3;
	}

	if( !numVerts || !numElems ) {
		return;
	}

	vbo = rb.currentVBO;
	stream = -rb.currentVBOId - 1;
	offset = &rb.streamOffset[stream];

	if( offset->firstVert+offset->numVerts+numVerts > MAX_STREAM_VBO_VERTS || 
		offset->firstElem+offset->numVerts+numElems > MAX_STREAM_VBO_ELEMENTS ) {

		RB_DrawElements( offset->firstVert, offset->numVerts, 
			offset->firstElem, offset->numElems );

		R_DiscardVBOVertexData( vbo );
		if( rb.currentVBOId != RB_VBO_STREAM_QUAD ) {
			R_DiscardVBOElemData( vbo );
		}

		offset->firstVert = 0;
		offset->firstElem = 0;
		offset->numVerts = 0;
		offset->numElems = 0;
	}

	if( numVerts > MAX_STREAM_VBO_VERTS ||
		numElems > MAX_STREAM_VBO_ELEMENTS ) {
		// FIXME: do something about this?
		return;
	}

	if( rb.currentVBOId == RB_VBO_STREAM_QUAD ) {
		vbo_hint = VBO_HINT_ELEMS_QUAD;

		// quad indices are stored in a static vbo, don't call R_UploadVBOElemData
	} else {
		if( mesh->elems ) {
			vbo_hint = VBO_HINT_NONE;
		} else if( rb.currentVBOId == RB_VBO_STREAM ) {
			vbo_hint = VBO_HINT_ELEMS_TRIFAN;
		} else {
			assert( 0 );
		}
		R_UploadVBOElemData( vbo, offset->firstVert + offset->numVerts, 
			offset->firstElem + offset->numElems, mesh, vbo_hint );
	}

	R_UploadVBOVertexData( vbo, offset->firstVert + offset->numVerts, 
		rb.currentVAttribs, mesh, vbo_hint );

	offset->numElems += numElems;
	offset->numVerts += numVerts;
}
Exemple #9
0
/*
* R_DrawSkySurf
*/
void R_DrawSkySurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, const portalSurface_t *portalSurface, 
	                unsigned int shadowBits, drawSurfaceSky_t *drawSurf ) {
	int i;
	int numVisSides;
	visSkySide_t visSkySides[6];
	vec3_t mins, maxs;
	int umin, umax, vmin, vmax;
	bool skyportal = portalSurface != NULL && portalSurface->skyPortal;
	skydome_t *skydome = rsh.worldBrushModel->skydome;

	if( !skydome ) {
		return;
	}
	if( skyportal && !fog ) {
		return;
	}

	numVisSides = 0;
	ClearBounds( mins, maxs );

	memset( visSkySides, 0, sizeof( visSkySides ) );

	for( i = 0; i < 6; i++ ) {
		if( drawSurf->skyMins[0][i] >= drawSurf->skyMaxs[0][i] ||
			drawSurf->skyMins[1][i] >= drawSurf->skyMaxs[1][i] ) {
			continue;
		}

		// increase the visible sides counter
		numVisSides++;

		umin = (int)( ( drawSurf->skyMins[0][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE - 1 ) );
		umax = (int)( ( drawSurf->skyMaxs[0][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE - 1 ) ) + 1;
		vmin = (int)( ( drawSurf->skyMins[1][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE - 1 ) );
		vmax = (int)( ( drawSurf->skyMaxs[1][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE - 1 ) ) + 1;

		clamp( umin, 0, SIDE_SIZE - 1 );
		clamp( umax, 0, SIDE_SIZE - 1 );
		clamp( vmin, 0, SIDE_SIZE - 1 );
		clamp( vmax, 0, SIDE_SIZE - 1 );

		visSkySides[i].index = i;
		visSkySides[i].firstVert = vmin * SIDE_SIZE + umin;
		visSkySides[i].numVerts = ( vmax - vmin ) * SIDE_SIZE + ( umax - umin ) + 1;
		visSkySides[i].firstElem = ( vmin * ( SIDE_SIZE - 2 ) + umin ) * 6;
		visSkySides[i].numElems = ( ( vmax - vmin ) * ( SIDE_SIZE - 2 ) + ( umax - umin ) ) * 6;

		clamp( visSkySides[i].firstVert, 0, POINTS_LEN - 1 );
		clamp( visSkySides[i].numVerts, 0, POINTS_LEN );

		clamp( visSkySides[i].firstElem, 0, ELEM_LEN - 1 );
		clamp( visSkySides[i].numElems, 0, ELEM_LEN );

		skydome->meshes[i].numElems = visSkySides[i].numElems;
	}

	// no sides are truly visible, ignore
	if( !numVisSides ) {
		return;
	}

	// center skydome on camera to give the illusion of a larger space
	rsc.skyent->scale = shader->skyHeight;
	VectorCopy( rn.viewOrigin, rsc.skyent->origin );
	R_TransformForEntity( rsc.skyent );

	if( skyportal ) {
		// render fake fogged skybox
		R_DrawSkyBox( skydome, visSkySides, rsh.emptyFogShader, shader, fog, drawSurf );
	} else {
		if( shader->skyboxImages[0] ) {
			R_DrawSkyBox( skydome, visSkySides, rsh.skyShader, shader, fog, drawSurf );
		} else {
			R_DrawBlackBottom( skydome, visSkySides, fog, drawSurf );
		}

		if( shader->numpasses ) {
			for( i = 0; i < 5; i++ ) {
				const visSkySide_t *visSide = visSkySides + i;

				if( drawSurf->skyMins[0][i] >= drawSurf->skyMaxs[0][i] ||
					drawSurf->skyMins[1][i] >= drawSurf->skyMaxs[1][i] ) {
					continue;
				}

				RB_BindShader( rsc.skyent, shader, NULL ); // must be called for every side to reset backend state

				RB_BindVBO( skydome->sphereVbos[i]->index, GL_TRIANGLES );

				RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems, 0, 0, 0, 0 );
			}
		}
	}

	R_TransformForEntity( e );
}
Exemple #10
0
void RB_DrawSkyDome(int tiles, float rows, int height,
                    int radius, float offset, float topoffs,
                    unsigned int c1, unsigned int c2)
{
    fixed_t x, y, z;
    fixed_t lx, ly;
    int i;
    angle_t an;
    float tu1, tu2;
    int r;
    vtx_t *vtx;
    int count;

    lx = ly = count = 0;

#define NUM_SKY_DOME_FACES  32

    //
    // front faces are drawn here, so cull the back faces
    //
    RB_SetState(GLSTATE_CULL, true);
    RB_SetCull(GLCULL_BACK);
    RB_SetState(GLSTATE_DEPTHTEST, true);
    RB_SetState(GLSTATE_BLEND, true);
    RB_SetState(GLSTATE_ALPHATEST, false);

    r = radius / (NUM_SKY_DOME_FACES / 4);

    //
    // set pointer for the main vertex list
    //
    RB_BindDrawPointers(drawVertex);
    vtx = drawVertex;

    // excuse the mess......
#define SKYDOME_VERTEX() vtx->x = FIXED2FLOAT(x); vtx->y = FIXED2FLOAT(y); vtx->z = FIXED2FLOAT(z)
#define SKYDOME_UV(u, v) vtx->tu = u; vtx->tv = v
#define SKYDOME_LEFT(v, h)                      \
    x = lx;                                     \
    y = ly;                                     \
    z = (h<<FRACBITS);                          \
    SKYDOME_UV(tu1, v);                        \
    SKYDOME_VERTEX();                           \
    vtx++

#define SKYDOME_RIGHT(v, h)                     \
    x = lx + FixedMul((r<<FRACBITS), finecosine[angle >> ANGLETOFINESHIFT]);    \
    y = ly + FixedMul((r<<FRACBITS), finesine[angle >> ANGLETOFINESHIFT]);      \
    z = (h<<FRACBITS);                          \
    SKYDOME_UV((tu2 * (i + 1)), v);            \
    SKYDOME_VERTEX();                           \
    vtx++

    tu1 = 0;
    tu2 = ((float)tiles / (float)NUM_SKY_DOME_FACES) * 0.5f;
    an = (ANG_MAX / NUM_SKY_DOME_FACES);

    //
    // setup vertex data
    //
    for(i = 0; i < NUM_SKY_DOME_FACES; ++i)
    {
        angle_t angle = an * i;

        *(unsigned int*)&vtx[0].r = c2;
        *(unsigned int*)&vtx[1].r = c1;
        *(unsigned int*)&vtx[2].r = c1;
        *(unsigned int*)&vtx[3].r = c2;

        SKYDOME_LEFT(rows, -height);
        SKYDOME_LEFT(topoffs, height);
        SKYDOME_RIGHT(topoffs, height);
        SKYDOME_RIGHT(rows, -height);

        lx = x;
        ly = y;

        RB_AddTriangle(0+count, 1+count, 2+count);
        RB_AddTriangle(3+count, 0+count, 2+count);
        count += 4;

        tu1 += tu2;
    }

    for(i = 0; i < NUM_SKY_DOME_FACES * 4; i++)
    {
        drawVertex[i].x += -((float)radius / ((float)NUM_SKY_DOME_FACES / 2.0f));
        drawVertex[i].y += -((float)radius / (M_PI / 2));
        drawVertex[i].z += -offset;
    }

    //
    // draw sky dome
    //
    RB_DrawElements();
    RB_ResetElements();

    RB_SetCull(GLCULL_FRONT);

#undef SKYDOME_RIGHT
#undef SKYDOME_LEFT
#undef SKYDOME_UV
#undef SKYDOME_VERTEX
#undef NUM_SKY_DOME_FACES
}
Exemple #11
0
/*
* R_DrawSkeletalSurf
*/
qboolean R_DrawSkeletalSurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, drawSurfaceSkeletal_t *drawSurf )
{
	unsigned int i, j;
	int framenum = e->frame;
	int oldframenum = e->oldframe;
	float backlerp = e->backlerp;
	float frontlerp = 1.0 - backlerp;
	bonepose_t tempbonepose[256];
	const bonepose_t *bp, *oldbp, *bonepose, *oldbonepose, *lerpedbonepose;
	bonepose_t *out, tp;
	mskbone_t *bone;
	mat4_t *bonePoseRelativeMat;
	dualquat_t *bonePoseRelativeDQ;
	size_t bonePoseRelativeMatSize, bonePoseRelativeDQSize;
	const model_t *mod = drawSurf->model;
	const mskmodel_t *skmodel = ( const mskmodel_t * )mod->extradata;
	const mskmesh_t *skmesh = drawSurf->mesh;
	qboolean hardwareTransform = skmesh->vbo != NULL && glConfig.maxGLSLBones > 0 ? qtrue : qfalse;
	vattribmask_t vattribs;

	bonePoseRelativeMat = NULL;
	bonePoseRelativeDQ = NULL;

	bp = e->boneposes;
	oldbp = e->oldboneposes;

	// not sure if it's really needed
	if( bp == skmodel->frames[0].boneposes )
	{
		bp = NULL;
		framenum = oldframenum = 0;
	}

	// choose boneposes for lerping
	if( bp )
	{
		if( !oldbp )
			oldbp = bp;
	}
	else
	{
		if( ( framenum >= (int)skmodel->numframes ) || ( framenum < 0 ) )
		{
#ifndef PUBLIC_BUILD
			ri.Com_DPrintf( "R_DrawBonesFrameLerp %s: no such frame %d\n", mod->name, framenum );
#endif
			framenum = 0;
		}
		if( ( oldframenum >= (int)skmodel->numframes ) || ( oldframenum < 0 ) )
		{
#ifndef PUBLIC_BUILD
			ri.Com_DPrintf( "R_DrawBonesFrameLerp %s: no such oldframe %d\n", mod->name, oldframenum );
#endif
			oldframenum = 0;
		}

		bp = skmodel->frames[framenum].boneposes;
		oldbp = skmodel->frames[oldframenum].boneposes;
	}

	if( bp == oldbp && !framenum && skmesh->vbo != NULL ) {
		// fastpath: render static frame 0 as is
		RB_BindVBO( skmesh->vbo->index, GL_TRIANGLES );

		RB_DrawElements( 0, skmesh->numverts, 0, skmesh->numtris * 3 );

		return qfalse;
	}

	// see what vertex attribs backend needs
	vattribs = RB_GetVertexAttribs();

	// cache size
	bonePoseRelativeMatSize = sizeof( mat4_t ) * (skmodel->numbones + skmodel->numblends);
	bonePoseRelativeDQSize = sizeof( dualquat_t ) * skmodel->numbones;

	// fetch bones tranforms from cache (both matrices and dual quaternions)
	bonePoseRelativeDQ = ( dualquat_t * )R_GetSketalCache( R_ENT2NUM( e ), mod->lodnum );
	if( bonePoseRelativeDQ ) {
		bonePoseRelativeMat = ( mat4_t * )(( qbyte * )bonePoseRelativeDQ + bonePoseRelativeDQSize);
	}
	else {
		// lerp boneposes and store results in cache

		lerpedbonepose = tempbonepose;
		if( bp == oldbp || frontlerp == 1 )
		{
			if( e->boneposes )
			{
				// assume that parent transforms have already been applied
				lerpedbonepose = bp;
			}
			else
			{
				for( i = 0; i < skmodel->numbones; i++ )
				{
					j = i;
					out = tempbonepose + j;
					bonepose = bp + j;
					bone = skmodel->bones + j;

					if( bone->parent >= 0 ) {
						DualQuat_Multiply( tempbonepose[bone->parent].dualquat, bonepose->dualquat, out->dualquat );
					}
					else {
						DualQuat_Copy( bonepose->dualquat, out->dualquat );
					}
				}
			}
		}
		else
		{
			if( e->boneposes )
			{
				// lerp, assume that parent transforms have already been applied
				for( i = 0, out = tempbonepose, bonepose = bp, oldbonepose = oldbp, bone = skmodel->bones; i < skmodel->numbones; i++, out++, bonepose++, oldbonepose++, bone++ )
				{
					DualQuat_Lerp( oldbonepose->dualquat, bonepose->dualquat, frontlerp, out->dualquat );
				}
			}
			else
			{
				// lerp and transform
				for( i = 0; i < skmodel->numbones; i++ )
				{
					j = i;
					out = tempbonepose + j;
					bonepose = bp + j;
					oldbonepose = oldbp + j;
					bone = skmodel->bones + j;

					DualQuat_Lerp( oldbonepose->dualquat, bonepose->dualquat, frontlerp, out->dualquat );

					if( bone->parent >= 0 ) {
						DualQuat_Copy( out->dualquat, tp.dualquat );
						DualQuat_Multiply( tempbonepose[bone->parent].dualquat, tp.dualquat, out->dualquat );
					}
				}
			}
		}

		bonePoseRelativeDQ = ( dualquat_t * )R_AllocSkeletalDataCache( R_ENT2NUM( e ), mod->lodnum, 
			bonePoseRelativeDQSize + bonePoseRelativeMatSize );

		// generate dual quaternions for all bones
		for( i = 0; i < skmodel->numbones; i++ ) {
			DualQuat_Multiply( lerpedbonepose[i].dualquat, skmodel->invbaseposes[i].dualquat, bonePoseRelativeDQ[i] );
			DualQuat_Normalize( bonePoseRelativeDQ[i] );
		}

		// CPU transforms
		if( !hardwareTransform ) {
			bonePoseRelativeMat = ( mat4_t * )(( qbyte * )bonePoseRelativeDQ + bonePoseRelativeDQSize);

			// generate matrices for all bones
			for( i = 0; i < skmodel->numbones; i++ ) {
				Matrix4_FromDualQuaternion( bonePoseRelativeDQ[i], bonePoseRelativeMat[i] );
			}

			// generate matrices for all blend combinations
			R_SkeletalBlendPoses( skmodel->numblends, skmodel->blends, skmodel->numbones, bonePoseRelativeMat );
		}
	}

	if( hardwareTransform )
	{
		RB_BindVBO( skmesh->vbo->index, GL_TRIANGLES );
		RB_SetBonesData( skmodel->numbones, bonePoseRelativeDQ, skmesh->maxWeights );
		RB_DrawElements( 0, skmesh->numverts, 0, skmesh->numtris * 3 );
	}
	else
	{
		mesh_t *rb_mesh;

		RB_BindVBO( RB_VBO_STREAM, GL_TRIANGLES );

		rb_mesh = RB_MapBatchMesh( skmesh->numverts, skmesh->numtris * 3 );
		if( !rb_mesh ) {
			ri.Com_DPrintf( S_COLOR_YELLOW "R_DrawAliasSurf: RB_MapBatchMesh returned NULL for (%s)(%s)", 
				drawSurf->model->name, skmesh->name );
			return qfalse;
		}

		R_SkeletalTransformVerts( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat,
			( vec_t * )skmesh->xyzArray[0], ( vec_t * )rb_mesh->xyzArray );

		if( vattribs & VATTRIB_SVECTOR_BIT ) {
			R_SkeletalTransformNormalsAndSVecs( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat,
			( vec_t * )skmesh->normalsArray[0], ( vec_t * )rb_mesh->normalsArray,
			( vec_t * )skmesh->sVectorsArray[0], ( vec_t * )rb_mesh->sVectorsArray );
		} else if( vattribs & VATTRIB_NORMAL_BIT ) {
			R_SkeletalTransformNormals( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat,
			( vec_t * )skmesh->normalsArray[0], ( vec_t * )rb_mesh->normalsArray );
		}

		rb_mesh->elems = skmesh->elems;
		rb_mesh->numElems = skmesh->numtris * 3;
		rb_mesh->numVerts = skmesh->numverts;
		rb_mesh->stArray = skmesh->stArray;

		RB_UploadMesh( rb_mesh );
		RB_EndBatch();
	}

	return qfalse;
}
Exemple #12
0
/*
* R_DrawSkySurf
*/
qboolean R_DrawSkySurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, drawSurfaceBSP_t *drawSurf )
{
	int i;
	int numVisSides;
	visSkySide_t visSkySides[6];
	vec3_t mins, maxs;
	int umin, umax, vmin, vmax;
	entity_t skyent;
	refdef_t *rd = &rn.refdef;
	skydome_t *skydome = r_worldbrushmodel->skydome;

	if( !skydome )
		return qfalse;

	numVisSides = 0;
	ClearBounds( mins, maxs );

	memset( visSkySides, 0, sizeof( visSkySides ) );

	for( i = 0; i < 6; i++ )
	{
		if( rn.skyMins[0][i] >= rn.skyMaxs[0][i] ||
			rn.skyMins[1][i] >= rn.skyMaxs[1][i] )
			continue;

		// increase the visible sides counter
		numVisSides++;

		umin = (int)( ( rn.skyMins[0][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) );
		umax = (int)( ( rn.skyMaxs[0][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) ) + 1;
		vmin = (int)( ( rn.skyMins[1][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) );
		vmax = (int)( ( rn.skyMaxs[1][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) ) + 1;

		clamp( umin, 0, SIDE_SIZE-1 );
		clamp( umax, 0, SIDE_SIZE-1 );
		clamp( vmin, 0, SIDE_SIZE-1 );
		clamp( vmax, 0, SIDE_SIZE-1 );

		visSkySides[i].index = i;
		visSkySides[i].firstVert = vmin * SIDE_SIZE + umin;
		visSkySides[i].numVerts = (vmax - vmin) * SIDE_SIZE + (umax - umin);
		visSkySides[i].firstElem = (vmin * (SIDE_SIZE-1) + umin) * 6;
		visSkySides[i].numElems = ((vmax - vmin) * (SIDE_SIZE-1) + (umax - umin)) * 6;

		AddPointToBounds( skydome->meshes[i].xyzArray[vmin*SIDE_SIZE+umin], mins, maxs );
		AddPointToBounds( skydome->meshes[i].xyzArray[vmax*SIDE_SIZE+umax], mins, maxs );

		skydome->meshes[i].numElems = visSkySides[i].numElems;
	}

	// no sides are truly visible, ignore
	if( !numVisSides )
		return qfalse;

	VectorAdd( mins, rn.viewOrigin, mins );
	VectorAdd( maxs, rn.viewOrigin, maxs );

	if( rd->rdflags & RDF_SKYPORTALINVIEW ) {
		R_DrawSkyPortal( e, &rd->skyportal, mins, maxs );
		return qfalse;
	}

	// center skydome on camera to give the illusion of a larger space
	skyent = *rsc.worldent;
	skyent.scale = shader->skyHeight;
	VectorCopy( rn.viewOrigin, skyent.origin );
	R_TransformForEntity( &skyent );

	if( shader->skyboxImages[0] )
		R_DrawSkyBox( skydome, visSkySides, shader );
	else
		R_DrawBlackBottom( skydome, visSkySides );

	if( shader->numpasses )
	{
		RB_BindShader( rsc.worldent, shader, rn.skyFog );

		for( i = 0; i < 5; i++ )
		{
			const visSkySide_t *visSide = visSkySides + i;

			if( rn.skyMins[0][i] >= rn.skyMaxs[0][i] ||
				rn.skyMins[1][i] >= rn.skyMaxs[1][i] )
				continue;

			RB_BindVBO( skydome->sphereVbos[i]->index, GL_TRIANGLES );

			RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems );
		}
	}

	R_TransformForEntity( e );

	return qfalse;
}
Exemple #13
0
/*
* R_DrawAliasSurf
* 
* Interpolates between two frames and origins
*/
qboolean R_DrawAliasSurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, drawSurfaceAlias_t *drawSurf )
{
	int i;
	int framenum, oldframenum;
	float backv[3], frontv[3];
	vec3_t normal, oldnormal;
	qboolean calcVerts, calcNormals, calcSTVectors;
	vec3_t move;
	const maliasframe_t *frame, *oldframe;
	const maliasvertex_t *v, *ov;
	float backlerp = e->backlerp;
	const maliasmodel_t *model = ( const maliasmodel_t * )drawSurf->model->extradata;
	const maliasmesh_t *aliasmesh = drawSurf->mesh;
	vattribmask_t vattribs;

	// see what vertex attribs backend needs
	vattribs = RB_GetVertexAttribs();

	framenum = bound( e->frame, 0, model->numframes );
	oldframenum = bound( e->oldframe, 0, model->numframes );

	frame = model->frames + framenum;
	oldframe = model->frames + oldframenum;
	for( i = 0; i < 3; i++ )
		move[i] = frame->translate[i] + ( oldframe->translate[i] - frame->translate[i] ) * backlerp;

	// based on backend's needs
	calcNormals = calcSTVectors = qfalse;
	calcNormals = ( ( vattribs & VATTRIB_NORMAL_BIT ) != 0 ) && ( ( framenum != 0 ) || ( oldframenum != 0 ) );
	calcSTVectors = ( ( vattribs & VATTRIB_SVECTOR_BIT ) != 0 ) && calcNormals;

	if( aliasmesh->vbo != NULL && !framenum && !oldframenum )
	{
		RB_BindVBO( aliasmesh->vbo->index, GL_TRIANGLES );

		RB_DrawElements( 0, aliasmesh->numverts, 0, aliasmesh->numtris * 3 );
	}
	else
	{
		mesh_t *rb_mesh;
		vec3_t *inVertsArray;
		vec3_t *inNormalsArray;
		vec4_t *inSVectorsArray;

		RB_BindVBO( RB_VBO_STREAM, GL_TRIANGLES );

		rb_mesh = RB_MapBatchMesh( aliasmesh->numverts, aliasmesh->numtris * 3 );
		if( !rb_mesh ) {
			ri.Com_DPrintf( S_COLOR_YELLOW "R_DrawAliasSurf: RB_MapBatchMesh returned NULL for (%s)(%s)", 
				drawSurf->model->name, aliasmesh->name );
			return qfalse;
		}

		inVertsArray = rb_mesh->xyzArray;
		inNormalsArray = rb_mesh->normalsArray;
		inSVectorsArray = rb_mesh->sVectorsArray;

		if( !framenum && !oldframenum )
		{
			calcVerts = qfalse;

			if( calcNormals )
			{
				v = aliasmesh->vertexes;
				for( i = 0; i < aliasmesh->numverts; i++, v++ )
					R_LatLongToNorm( v->latlong, inNormalsArray[i] );
			}
		}
		else if( framenum == oldframenum )
		{
			calcVerts = qtrue;

			for( i = 0; i < 3; i++ )
				frontv[i] = frame->scale[i];

			v = aliasmesh->vertexes + framenum * aliasmesh->numverts;
			for( i = 0; i < aliasmesh->numverts; i++, v++ )
			{
				VectorSet( inVertsArray[i],
					move[0] + v->point[0]*frontv[0],
					move[1] + v->point[1]*frontv[1],
					move[2] + v->point[2]*frontv[2] );

				if( calcNormals )
					R_LatLongToNorm( v->latlong, inNormalsArray[i] );
			}
		}
		else
		{
			calcVerts = qtrue;

			for( i = 0; i < 3; i++ )
			{
				backv[i] = backlerp * oldframe->scale[i];
				frontv[i] = ( 1.0f - backlerp ) * frame->scale[i];
			}

			v = aliasmesh->vertexes + framenum * aliasmesh->numverts;
			ov = aliasmesh->vertexes + oldframenum * aliasmesh->numverts;
			for( i = 0; i < aliasmesh->numverts; i++, v++, ov++ )
			{
				VectorSet( inVertsArray[i],
					move[0] + v->point[0]*frontv[0] + ov->point[0]*backv[0],
					move[1] + v->point[1]*frontv[1] + ov->point[1]*backv[1],
					move[2] + v->point[2]*frontv[2] + ov->point[2]*backv[2] );

				if( calcNormals )
				{
					R_LatLongToNorm( v->latlong, normal );
					R_LatLongToNorm( ov->latlong, oldnormal );

					VectorSet( inNormalsArray[i],
						normal[0] + ( oldnormal[0] - normal[0] ) * backlerp,
						normal[1] + ( oldnormal[1] - normal[1] ) * backlerp,
						normal[2] + ( oldnormal[2] - normal[2] ) * backlerp );
				}
			}
		}

		if( calcSTVectors )
			R_BuildTangentVectors( aliasmesh->numverts, inVertsArray, inNormalsArray, aliasmesh->stArray, aliasmesh->numtris, aliasmesh->elems, inSVectorsArray );

		if( !calcVerts ) {
			rb_mesh->xyzArray = aliasmesh->xyzArray;
		}
		rb_mesh->elems = aliasmesh->elems;
		rb_mesh->numElems = aliasmesh->numtris * 3;
		rb_mesh->numVerts = aliasmesh->numverts;

		rb_mesh->stArray = aliasmesh->stArray;
		if( !calcNormals ) {
			rb_mesh->normalsArray = aliasmesh->normalsArray;
		}
		if( !calcSTVectors ) {
			rb_mesh->sVectorsArray = aliasmesh->sVectorsArray;
		}

		RB_UploadMesh( rb_mesh );

		RB_EndBatch();
	}

	return qfalse;
}
Exemple #14
0
/*
* R_DrawBSPSurf
*/
void R_DrawBSPSurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, const portalSurface_t *portalSurface, unsigned int entShadowBits, drawSurfaceBSP_t *drawSurf )
{
	const vboSlice_t *slice;
	const vboSlice_t *shadowSlice;
	static const vboSlice_t nullSlice = { 0 };
	int firstVert, firstElem;
	int numVerts, numElems;
	int firstShadowVert, firstShadowElem;
	int numShadowVerts, numShadowElems;
	unsigned shadowBits, dlightBits;

	slice = R_GetVBOSlice( drawSurf - rsh.worldBrushModel->drawSurfaces );
	shadowSlice = R_GetVBOSlice( rsh.worldBrushModel->numDrawSurfaces + ( drawSurf - rsh.worldBrushModel->drawSurfaces ) );
	if( !shadowSlice ) {
		shadowSlice = &nullSlice;
	}

	assert( slice != NULL );

	if( drawSurf->dlightFrame == rsc.frameCount ) {
		dlightBits = drawSurf->dlightBits & rn.dlightBits;
	}
	else {
		dlightBits = 0;
	}

	if( drawSurf->shadowFrame == rsc.frameCount ) {
		shadowBits = (drawSurf->shadowBits & rn.shadowBits) & rsc.renderedShadowBits;
	}
	else {
		shadowBits = 0;
	}

	// shadowBits are shared for all rendering instances (normal view, portals, etc)
	// if either shadow slice is empty or shadowBits is 0, then we must pass the surface unshadowed
	numVerts = slice->numVerts;
	numElems = slice->numElems;
	firstVert = drawSurf->firstVboVert + slice->firstVert;
	firstElem = drawSurf->firstVboElem + slice->firstElem;
	if( shadowBits && shadowSlice->numElems ) {
		numShadowVerts = shadowSlice->numVerts;
		numShadowElems = shadowSlice->numElems;
		firstShadowVert = drawSurf->firstVboVert + shadowSlice->firstVert;
		firstShadowElem = drawSurf->firstVboElem + shadowSlice->firstElem;
	}
	else {
		shadowBits = 0;
		numShadowVerts = 0;
		numShadowElems = 0;
		firstShadowVert = 0;
		firstShadowElem = 0;
	}

	RB_BindVBO( drawSurf->vbo->index, GL_TRIANGLES );

	RB_SetDlightBits( dlightBits );

	RB_SetShadowBits( shadowBits );

	RB_SetLightstyle( drawSurf->superLightStyle );

	if( drawSurf->numInstances ) {
		RB_DrawElementsInstanced( firstVert, numVerts, firstElem, numElems, 
			firstShadowVert, numShadowVerts, firstShadowElem, numShadowElems,
			drawSurf->numInstances, drawSurf->instances );
	}
	else {
		RB_DrawElements( firstVert, numVerts, firstElem, numElems, 
			firstShadowVert, numShadowVerts, firstShadowElem, numShadowElems );
	}
}
Exemple #15
0
/*
* RB_FlushDynamicMeshes
*/
void RB_FlushDynamicMeshes( void )
{
    int i, numDraws = rb.numDynamicDraws;
    rbDynamicStream_t *stream;
    rbDynamicDraw_t *draw;
    int sx, sy, sw, sh;
    float offsetx = 0.0f, offsety = 0.0f, transx, transy;
    mat4_t m;

    if( !numDraws ) {
        return;
    }

    for( i = 0; i < RB_VBO_NUM_STREAMS; i++ ) {
        stream = &rb.dynamicStreams[i];

        // R_UploadVBO* are going to rebind buffer arrays for upload
        // so update our local VBO state cache by calling RB_BindVBO
        RB_BindVBO( -i - 1, GL_TRIANGLES ); // dummy value for primitive here

        // because of firstVert, upload elems first
        if( stream->drawElements.numElems ) {
            mesh_t elemMesh;
            memset( &elemMesh, 0, sizeof( elemMesh ) );
            elemMesh.elems = dynamicStreamElems[i] + stream->drawElements.firstElem;
            elemMesh.numElems = stream->drawElements.numElems;
            R_UploadVBOElemData( stream->vbo, 0, stream->drawElements.firstElem, &elemMesh );
            stream->drawElements.firstElem += stream->drawElements.numElems;
            stream->drawElements.numElems = 0;
        }

        if( stream->drawElements.numVerts ) {
            R_UploadVBOVertexRawData( stream->vbo, stream->drawElements.firstVert, stream->drawElements.numVerts,
                                      stream->vertexData + stream->drawElements.firstVert * stream->vbo->vertexSize );
            stream->drawElements.firstVert += stream->drawElements.numVerts;
            stream->drawElements.numVerts = 0;
        }
    }

    RB_GetScissor( &sx, &sy, &sw, &sh );

    Matrix4_Copy( rb.objectMatrix, m );
    transx = m[12];
    transy = m[13];

    for( i = 0, draw = rb.dynamicDraws; i < numDraws; i++, draw++ ) {
        RB_BindShader( draw->entity, draw->shader, draw->fog );
        RB_BindVBO( draw->streamId, draw->primitive );
        RB_SetPortalSurface( draw->portalSurface );
        RB_SetShadowBits( draw->shadowBits );
        RB_Scissor( draw->scissor[0], draw->scissor[1], draw->scissor[2], draw->scissor[3] );

        // translate the mesh in 2D
        if( ( offsetx != draw->offset[0] ) || ( offsety != draw->offset[1] ) ) {
            offsetx = draw->offset[0];
            offsety = draw->offset[1];
            m[12] = transx + offsetx;
            m[13] = transy + offsety;
            RB_LoadObjectMatrix( m );
        }

        RB_DrawElements(
            draw->drawElements.firstVert, draw->drawElements.numVerts,
            draw->drawElements.firstElem, draw->drawElements.numElems,
            draw->drawElements.firstVert, draw->drawElements.numVerts,
            draw->drawElements.firstElem, draw->drawElements.numElems );
    }

    rb.numDynamicDraws = 0;

    RB_Scissor( sx, sy, sw, sh );

    // restore the original translation in the object matrix if it has been changed
    if( offsetx || offsety ) {
        m[12] = transx;
        m[13] = transy;
        RB_LoadObjectMatrix( m );
    }
}
Exemple #16
0
/*
* R_BlitTextureToScrFbo
*/
static void R_BlitTextureToScrFbo( const refdef_t *fd, image_t *image, int dstFbo, 
	int program_type, const vec4_t color, int blendMask, int numShaderImages, image_t **shaderImages )
{
	int x, y;
	int w, h, fw, fh;
	static char s_name[] = "$builtinpostprocessing";
	static shaderpass_t p;
	static shader_t s;
	int i;
	static tcmod_t tcmod;
	mat4_t m;

	assert( rsh.postProcessingVBO );

	// blit + flip using a static mesh to avoid redundant buffer uploads
	// (also using custom PP effects like FXAA with the stream VBO causes
	// Adreno to mark the VBO as "slow" (due to some weird bug)
	// for the rest of the frame and drop FPS to 10-20).
	RB_FlushDynamicMeshes();

	RB_BindFrameBufferObject( dstFbo );

	if( !dstFbo ) {
		// default framebuffer
		// set the viewport to full resolution
		// but keep the scissoring region
		x = fd->x;
		y = fd->y;
		w = fw = fd->width;
		h = fh = fd->height;
		RB_Viewport( 0, 0, glConfig.width, glConfig.height );
		RB_Scissor( rn.scissor[0], rn.scissor[1], rn.scissor[2], rn.scissor[3] );
	}
	else {
		// aux framebuffer
		// set the viewport to full resolution of the framebuffer (without the NPOT padding if there's one)
		// draw quad on the whole framebuffer texture
		// set scissor to default framebuffer resolution
		image_t *cb = RFB_GetObjectTextureAttachment( dstFbo, false );
		x = 0;
		y = 0;
		w = fw = rf.frameBufferWidth;
		h = fh = rf.frameBufferHeight;
		if( cb ) {
			fw = cb->upload_width;
			fh = cb->upload_height;
		}
		RB_Viewport( 0, 0, w, h );
		RB_Scissor( 0, 0, glConfig.width, glConfig.height );
	}

	s.vattribs = VATTRIB_POSITION_BIT|VATTRIB_TEXCOORDS_BIT;
	s.sort = SHADER_SORT_NEAREST;
	s.numpasses = 1;
	s.name = s_name;
	s.passes = &p;

	p.rgbgen.type = RGB_GEN_IDENTITY;
	p.alphagen.type = ALPHA_GEN_IDENTITY;
	p.tcgen = TC_GEN_NONE;
	p.images[0] = image;
	for( i = 0; i < numShaderImages; i++ )
		p.images[i + 1] = shaderImages[i];
	p.flags = blendMask;
	p.program_type = program_type;

	if( !dstFbo ) {
		tcmod.type = TC_MOD_TRANSFORM;
		tcmod.args[0] = ( float )( w ) / ( float )( image->upload_width );
		tcmod.args[1] = ( float )( h ) / ( float )( image->upload_height );
		tcmod.args[4] = ( float )( x ) / ( float )( image->upload_width );
		tcmod.args[5] = ( float )( image->upload_height - h - y ) / ( float )( image->upload_height );
		p.numtcmods = 1;
		p.tcmods = &tcmod;
	}
	else {
		p.numtcmods = 0;
	}

	Matrix4_Identity( m );
	Matrix4_Scale2D( m, fw, fh );
	Matrix4_Translate2D( m, x, y );
	RB_LoadObjectMatrix( m );

	RB_BindShader( NULL, &s, NULL );
	RB_BindVBO( rsh.postProcessingVBO->index, GL_TRIANGLES );
	RB_DrawElements( 0, 4, 0, 6, 0, 0, 0, 0 );

	RB_LoadObjectMatrix( mat4x4_identity );

	// restore 2D viewport and scissor
	RB_Viewport( 0, 0, rf.frameBufferWidth, rf.frameBufferHeight );
	RB_Scissor( 0, 0, rf.frameBufferWidth, rf.frameBufferHeight );
}
Exemple #17
0
void RB_DrawClouds(const int lump)
{
    vtx_t *v;
    int i;
    rbTexture_t *texture;

    RB_SetCull(GLCULL_BACK);

    texture = RB_GetTexture(RDT_PATCH, lump, 0);
    RB_BindTexture(texture);
    RB_ChangeTexParameters(texture, TC_REPEAT, TEXFILTER);

    RB_BindDrawPointers(drawVertex);
    v = drawVertex;

    /*
    --------------------------
    |\                     / |
    |  \                 /   |
    |    \             /     |
    |      \ _______ /       |
    |       |       |        |
    |       |       |        |
    |       |       |        |
    |       |_______|        |
    |      /         \       |
    |    /             \     |
    |  /                 \   |
    |/                     \ |
    --------------------------
    */

    v[0].x = -CLOUD_OUTER_SIZE; v[0].y =  CLOUD_OUTER_SIZE; v[0].z = CLOUD_HEIGHT;
    v[1].x =  CLOUD_OUTER_SIZE; v[1].y =  CLOUD_OUTER_SIZE; v[1].z = CLOUD_HEIGHT;
    v[2].x =  CLOUD_OUTER_SIZE; v[2].y = -CLOUD_OUTER_SIZE; v[2].z = CLOUD_HEIGHT;
    v[3].x = -CLOUD_SIZE;       v[3].y = -CLOUD_SIZE;       v[3].z = CLOUD_HEIGHT;
    v[4].x = -CLOUD_SIZE;       v[4].y =  CLOUD_SIZE;       v[4].z = CLOUD_HEIGHT;
    v[5].x =  CLOUD_SIZE;       v[5].y = -CLOUD_SIZE;       v[5].z = CLOUD_HEIGHT;
    v[6].x = -CLOUD_OUTER_SIZE; v[6].y = -CLOUD_OUTER_SIZE; v[6].z = CLOUD_HEIGHT;
    v[7].x =  CLOUD_SIZE;       v[7].y =  CLOUD_SIZE;       v[7].z = CLOUD_HEIGHT;

    v[0].tu = CLOUD_TILE_INNER; v[0].tv = CLOUD_TILE_OUTER;
    v[1].tu = CLOUD_TILE_INNER; v[1].tv = CLOUD_TILE_INNER;
    v[2].tu = CLOUD_TILE_OUTER; v[2].tv = CLOUD_TILE_INNER;
    v[3].tu = CLOUD_TILE;       v[3].tv = CLOUD_TILE;
    v[4].tu = 0.0f;             v[4].tv = CLOUD_TILE;
    v[5].tu = CLOUD_TILE;       v[5].tv = 0.0f;
    v[6].tu = CLOUD_TILE_OUTER; v[6].tv = CLOUD_TILE_OUTER;
    v[7].tu = 0.0f;             v[7].tv = 0.0f;

    for(i = 0; i < 8; ++i)
    {
        v[i].r = 255;
        v[i].g = 255;
        v[i].b = 255;
        v[i].a = 100;
        v[i].tu += sky_cloudpan1;
        v[i].tv += sky_cloudpan2;
    }

    // zero out the outer portion of the geometry so it blends into the sky dome
    v[3].a = v[4].a = v[5].a = v[7].a = 0;
    v[3].r = v[4].r = v[5].r = v[7].r = 0;
    v[3].g = v[4].g = v[5].g = v[7].g = 0;
    v[3].b = v[4].b = v[5].b = v[7].b = 0;

    RB_SetBlend(GLSRC_ONE, GLDST_ONE_MINUS_SRC_ALPHA);
    RB_AddTriangle(0, 1, 2);
    RB_AddTriangle(4, 0, 3);
    RB_AddTriangle(5, 3, 6);
    RB_AddTriangle(7, 5, 2);
    RB_AddTriangle(4, 7, 1);
    RB_AddTriangle(6, 0, 2);
    RB_AddTriangle(3, 0, 6);
    RB_AddTriangle(2, 5, 6);
    RB_AddTriangle(1, 7, 2);
    RB_AddTriangle(0, 4, 1);
    RB_DrawElements();
    RB_ResetElements();
}