/* * CG_AddBlobShadow * * Ok, to not use decals space we need these arrays to store the * polygons info. We do not need the linked list nor registration */ static void CG_AddBlobShadow( vec3_t origin, vec3_t dir, float orient, float radius, float r, float g, float b, float a, cgshadebox_t *shadeBox ) { int i, j, c, nverts; vec3_t axis[3]; byte_vec4_t color; fragment_t *fr, fragments[MAX_BLOBSHADOW_FRAGMENTS]; int numfragments; poly_t poly; vec4_t verts[MAX_BLOBSHADOW_VERTS]; if( radius <= 0 || VectorCompare( dir, vec3_origin ) ) return; // invalid // calculate orientation matrix VectorNormalize2( dir, axis[0] ); PerpendicularVector( axis[1], axis[0] ); RotatePointAroundVector( axis[2], axis[0], axis[1], orient ); CrossProduct( axis[0], axis[2], axis[1] ); numfragments = trap_R_GetClippedFragments( origin, radius, axis, // clip it MAX_BLOBSHADOW_VERTS, verts, MAX_BLOBSHADOW_FRAGMENTS, fragments ); // no valid fragments if( !numfragments ) return; // clamp and scale colors if( r < 0 ) r = 0;else if( r > 1 ) r = 255;else r *= 255; if( g < 0 ) g = 0;else if( g > 1 ) g = 255;else g *= 255; if( b < 0 ) b = 0;else if( b > 1 ) b = 255;else b *= 255; if( a < 0 ) a = 0;else if( a > 1 ) a = 255;else a *= 255; color[0] = ( qbyte )( r ); color[1] = ( qbyte )( g ); color[2] = ( qbyte )( b ); color[3] = ( qbyte )( a ); c = *( int * )color; radius = 0.5f / radius; VectorScale( axis[1], radius, axis[1] ); VectorScale( axis[2], radius, axis[2] ); memset( &poly, 0, sizeof( poly ) ); for( i = 0, nverts = 0, fr = fragments; i < numfragments; i++, fr++ ) { if( nverts+fr->numverts > MAX_BLOBSHADOW_VERTS ) return; if( fr->numverts <= 0 ) continue; poly.shader = shadeBox->shader; poly.verts = &shadeBox->verts[nverts]; poly.normals = &shadeBox->norms[nverts]; poly.stcoords = &shadeBox->stcoords[nverts]; poly.colors = &shadeBox->colors[nverts]; poly.numverts = fr->numverts; poly.fognum = fr->fognum; nverts += fr->numverts; for( j = 0; j < fr->numverts; j++ ) { vec3_t v; Vector4Copy( verts[fr->firstvert+j], poly.verts[j] ); VectorCopy( axis[0], poly.normals[j] ); poly.normals[j][3] = 0; VectorSubtract( poly.verts[j], origin, v ); poly.stcoords[j][0] = DotProduct( v, axis[1] ) + 0.5f; poly.stcoords[j][1] = DotProduct( v, axis[2] ) + 0.5f; *( int * )poly.colors[j] = c; } trap_R_AddPolyToScene( &poly ); } }
/* ================== CG_BuildableStatusDisplay ================== */ static void CG_BuildableStatusDisplay( centity_t *cent ) { entityState_t *es = ¢->currentState; vec3_t origin; float healthScale; int health; float x, y; vec4_t color; qboolean powered, marked; trace_t tr; float d; buildStat_t *bs; int i, j; int entNum; vec3_t trOrigin; vec3_t right; qboolean visible = qfalse; vec3_t mins, maxs; entityState_t *hit; if( BG_FindTeamForBuildable( es->modelindex ) == BIT_ALIENS ) bs = &cgs.alienBuildStat; else bs = &cgs.humanBuildStat; if( !bs->loaded ) return; d = Distance( cent->lerpOrigin, cg.refdef.vieworg ); if( d > STATUS_MAX_VIEW_DIST ) return; Vector4Copy( bs->foreColor, color ); // trace for center point BG_FindBBoxForBuildable( es->modelindex, mins, maxs ); VectorCopy( cent->lerpOrigin, origin ); // center point origin[ 2 ] += mins[ 2 ]; origin[ 2 ] += ( abs( mins[ 2 ] ) + abs( maxs[ 2 ] ) ) / 2; entNum = cg.predictedPlayerState.clientNum; // if first try fails, step left, step right for( j = 0; j < 3; j++ ) { VectorCopy( cg.refdef.vieworg, trOrigin ); switch( j ) { case 1: // step right AngleVectors( cg.refdefViewAngles, NULL, right, NULL ); VectorMA( trOrigin, STATUS_PEEK_DIST, right, trOrigin ); break; case 2: // step left AngleVectors( cg.refdefViewAngles, NULL, right, NULL ); VectorMA( trOrigin, -STATUS_PEEK_DIST, right, trOrigin ); break; default: break; } // look through up to 3 players and/or transparent buildables for( i = 0; i < 3; i++ ) { CG_Trace( &tr, trOrigin, NULL, NULL, origin, entNum, MASK_SHOT ); if( tr.entityNum == cent->currentState.number ) { visible = qtrue; break; } if( tr.entityNum == ENTITYNUM_WORLD ) break; hit = &cg_entities[ tr.entityNum ].currentState; if( tr.entityNum < MAX_CLIENTS || ( hit->eType == ET_BUILDABLE && ( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) || BG_FindTransparentTestForBuildable( hit->modelindex ) ) ) ) { entNum = tr.entityNum; VectorCopy( tr.endpos, trOrigin ); } else break; } } // hack to make the kit obscure view if( cg_drawGun.integer && visible && cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS && CG_WorldToScreen( origin, &x, &y ) ) { if( x > 450 && y > 290 ) visible = qfalse; } if( !visible && cent->buildableStatus.visible ) { cent->buildableStatus.visible = qfalse; cent->buildableStatus.lastTime = cg.time; } else if( visible && !cent->buildableStatus.visible ) { cent->buildableStatus.visible = qtrue; cent->buildableStatus.lastTime = cg.time; } // Fade up if( cent->buildableStatus.visible ) { if( cent->buildableStatus.lastTime + STATUS_FADE_TIME > cg.time ) color[ 3 ] = (float)( cg.time - cent->buildableStatus.lastTime ) / STATUS_FADE_TIME; } // Fade down if( !cent->buildableStatus.visible ) { if( cent->buildableStatus.lastTime + STATUS_FADE_TIME > cg.time ) color[ 3 ] = 1.0f - (float)( cg.time - cent->buildableStatus.lastTime ) / STATUS_FADE_TIME; else return; } health = es->generic1 & B_HEALTH_MASK; healthScale = (float)health / B_HEALTH_MASK; if( health > 0 && healthScale < 0.01f ) healthScale = 0.01f; else if( healthScale < 0.0f ) healthScale = 0.0f; else if( healthScale > 1.0f ) healthScale = 1.0f; if( CG_WorldToScreen( origin, &x, &y ) ) { float picH = bs->frameHeight; float picW = bs->frameWidth; float picX = x; float picY = y; float scale; float subH, subY; vec4_t frameColor; // this is fudged to get the width/height in the cfg to be more realistic scale = ( picH / d ) * 3; powered = es->generic1 & B_POWERED_TOGGLEBIT; marked = es->generic1 & B_MARKED_TOGGLEBIT; picH *= scale; picW *= scale; picX -= ( picW * 0.5f ); picY -= ( picH * 0.5f ); // sub-elements such as icons and number subH = picH - ( picH * bs->verticalMargin ); subY = picY + ( picH * 0.5f ) - ( subH * 0.5f ); if( bs->frameShader ) { Vector4Copy( bs->backColor, frameColor ); frameColor[ 3 ] = color[ 3 ]; trap_R_SetColor( frameColor ); CG_DrawPic( picX, picY, picW, picH, bs->frameShader ); trap_R_SetColor( NULL ); } if( health > 0 ) { float hX, hY, hW, hH; vec4_t healthColor; hX = picX + ( bs->healthPadding * scale ); hY = picY + ( bs->healthPadding * scale ); hH = picH - ( bs->healthPadding * 2.0f * scale ); hW = picW * healthScale - ( bs->healthPadding * 2.0f * scale ); if( healthScale == 1.0f ) Vector4Copy( bs->healthLowColor, healthColor ); else if( healthScale >= 0.75f ) Vector4Copy( bs->healthGuardedColor, healthColor ); else if( healthScale >= 0.50f ) Vector4Copy( bs->healthElevatedColor, healthColor ); else if( healthScale >= 0.25f ) Vector4Copy( bs->healthHighColor, healthColor ); else Vector4Copy( bs->healthSevereColor, healthColor ); healthColor[ 3 ] = color[ 3 ]; trap_R_SetColor( healthColor ); CG_DrawPic( hX, hY, hW, hH, cgs.media.whiteShader ); trap_R_SetColor( NULL ); } if( bs->overlayShader ) { float oW = bs->overlayWidth; float oH = bs->overlayHeight; float oX = x; float oY = y; oH *= scale; oW *= scale; oX -= ( oW * 0.5f ); oY -= ( oH * 0.5f ); trap_R_SetColor( frameColor ); CG_DrawPic( oX, oY, oW, oH, bs->overlayShader ); trap_R_SetColor( NULL ); } trap_R_SetColor( color ); if( !powered ) { float pX; pX = picX + ( subH * bs->horizontalMargin ); CG_DrawPic( pX, subY, subH, subH, bs->noPowerShader ); } if( marked ) { float mX; mX = picX + picW - ( subH * bs->horizontalMargin ) - subH; CG_DrawPic( mX, subY, subH, subH, bs->markedShader ); } { float nX; int healthMax; int healthPoints; healthMax = BG_FindHealthForBuildable( es->modelindex ); healthPoints = (int)( healthScale * healthMax ); if( health > 0 && healthPoints < 1 ) healthPoints = 1; nX = picX + ( picW * 0.5f ) - 2.0f - ( ( subH * 4 ) * 0.5f ); if( healthPoints > 999 ) nX -= 0.0f; else if( healthPoints > 99 ) nX -= subH * 0.5f; else if( healthPoints > 9 ) nX -= subH * 1.0f; else nX -= subH * 1.5f; CG_DrawField( nX, subY, 4, subH, subH, healthPoints ); } trap_R_SetColor( NULL ); } }
static void UI_SPLevelMenu_MenuDraw( void ) { int n, i; int x, y; vec4_t color; int level; // int fraglimit; int pad; char buf[MAX_INFO_VALUE]; char string[64]; if( levelMenuInfo.reinit ) { UI_PopMenu(); UI_SPLevelMenu(); return; } // draw player name trap_Cvar_VariableStringBuffer( "name", string, 32 ); Q_CleanStr( string ); UI_DrawProportionalString( 320, PLAYER_Y, string, UI_CENTER|UI_SMALLFONT, color_orange ); // check for model changes trap_Cvar_VariableStringBuffer( "model", buf, sizeof(buf) ); if( Q_stricmp( buf, levelMenuInfo.playerModel ) != 0 ) { Q_strncpyz( levelMenuInfo.playerModel, buf, sizeof(levelMenuInfo.playerModel) ); PlayerIcon( levelMenuInfo.playerModel, levelMenuInfo.playerPicName, sizeof(levelMenuInfo.playerPicName) ); levelMenuInfo.item_player.shader = 0; } // standard menu drawing Menu_Draw( &levelMenuInfo.menu ); // draw player award levels y = AWARDS_Y; i = 0; for( n = 0; n < 6; n++ ) { level = levelMenuInfo.awardLevels[n]; if( level > 0 ) { if( i & 1 ) { x = 224 - (i - 1 ) / 2 * (48 + 16); } else { x = 368 + i / 2 * (48 + 16); } i++; if( level == 1 ) { continue; } if( level >= 1000000 ) { Com_sprintf( string, sizeof(string), "%im", level / 1000000 ); } else if( level >= 1000 ) { Com_sprintf( string, sizeof(string), "%ik", level / 1000 ); } else { Com_sprintf( string, sizeof(string), "%i", level ); } UI_DrawString( x + 24, y + 48, string, UI_CENTER, color_yellow ); } } UI_DrawProportionalString( 18, 38, va( "Tier %i", selectedArenaSet + 1 ), UI_LEFT|UI_SMALLFONT, color_orange ); for ( n = 0; n < levelMenuInfo.numMaps; n++ ) { x = levelMenuInfo.item_maps[n].generic.x; y = levelMenuInfo.item_maps[n].generic.y; UI_FillRect( x, y + 96, 128, 18, color_black ); } if ( selectedArenaSet > currentSet ) { UI_DrawProportionalString( 320, 216, "ACCESS DENIED", UI_CENTER|UI_BIGFONT, color_red ); return; } // show levelshots for levels of current tier Vector4Copy( color_white, color ); color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR); for ( n = 0; n < levelMenuInfo.numMaps; n++ ) { x = levelMenuInfo.item_maps[n].generic.x; y = levelMenuInfo.item_maps[n].generic.y; UI_DrawString( x + 64, y + 96, levelMenuInfo.levelNames[n], UI_CENTER|UI_SMALLFONT, color_orange ); if( levelMenuInfo.levelScores[n] == 1 ) { UI_DrawHandlePic( x, y, 128, 96, levelMenuInfo.levelCompletePic[levelMenuInfo.levelScoresSkill[n] - 1] ); } if ( n == selectedArena ) { if( Menu_ItemAtCursor( &levelMenuInfo.menu ) == &levelMenuInfo.item_maps[n] ) { trap_R_SetColor( color ); } UI_DrawHandlePic( x-1, y-1, 130, 130 - 14, levelMenuInfo.levelSelectedPic ); trap_R_SetColor( NULL ); } else if( Menu_ItemAtCursor( &levelMenuInfo.menu ) == &levelMenuInfo.item_maps[n] ) { trap_R_SetColor( color ); UI_DrawHandlePic( x-31, y-30, 256, 256-27, levelMenuInfo.levelFocusPic); trap_R_SetColor( NULL ); } } // show map name and long name of selected level y = 192; Q_strncpyz( buf, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "map" ), 20 ); Q_strupr( buf ); Com_sprintf( string, sizeof(string), "%s: %s", buf, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "longname" ) ); UI_DrawProportionalString( 320, y, string, UI_CENTER|UI_SMALLFONT, color_orange ); // fraglimit = atoi( Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "fraglimit" ) ); // UI_DrawString( 18, 212, va("Frags %i", fraglimit) , UI_LEFT|UI_SMALLFONT, color_orange ); // draw bot opponents y += 24; pad = (7 - levelMenuInfo.numBots) * (64 + 26) / 2; for( n = 0; n < levelMenuInfo.numBots; n++ ) { x = 18 + pad + (64 + 26) * n; if( levelMenuInfo.botPics[n] ) { UI_DrawHandlePic( x, y, 64, 64, levelMenuInfo.botPics[n]); } else { UI_FillRect( x, y, 64, 64, color_black ); UI_DrawProportionalString( x+22, y+18, "?", UI_BIGFONT, color_orange ); } UI_DrawString( x, y + 64, levelMenuInfo.botNames[n], UI_SMALLFONT|UI_LEFT, color_orange ); } }
/* @@@@@@@@@@@@@@@@@@@@@ RE_RenderScene Draw a 3D view into a part of the window, then return to 2D drawing. Rendering a scene may require multiple views to be rendered to handle mirrors, @@@@@@@@@@@@@@@@@@@@@ */ void RE_RenderScene( const refdef_t *fd ) { viewParms_t parms; int startTime; if ( !tr.registered ) { return; } GLimp_LogComment( "====== RE_RenderScene =====\n" ); if ( r_norefresh->integer ) { return; } startTime = ri.Milliseconds(); if ( !tr.world && !( fd->rdflags & RDF_NOWORLDMODEL ) ) { ri.Error(errorParm_t::ERR_DROP, "R_RenderScene: NULL worldmodel" ); } tr.refdef.x = fd->x; tr.refdef.y = fd->y; tr.refdef.width = fd->width; tr.refdef.height = fd->height; tr.refdef.fov_x = fd->fov_x; tr.refdef.fov_y = fd->fov_y; VectorCopy( fd->vieworg, tr.refdef.vieworg ); VectorCopy( fd->viewaxis[ 0 ], tr.refdef.viewaxis[ 0 ] ); VectorCopy( fd->viewaxis[ 1 ], tr.refdef.viewaxis[ 1 ] ); VectorCopy( fd->viewaxis[ 2 ], tr.refdef.viewaxis[ 2 ] ); VectorCopy( fd->blurVec, tr.refdef.blurVec ); tr.refdef.time = fd->time; tr.refdef.rdflags = fd->rdflags; // copy the areamask data over and note if it has changed, which // will force a reset of the visible leafs even if the view hasn't moved tr.refdef.areamaskModified = false; if ( !( tr.refdef.rdflags & RDF_NOWORLDMODEL ) && !( ( tr.refdef.rdflags & RDF_SKYBOXPORTAL ) && tr.world->numSkyNodes > 0 ) ) { int areaDiff; int i; // compare the area bits areaDiff = 0; for ( i = 0; i < MAX_MAP_AREA_BYTES / 4; i++ ) { areaDiff |= ( ( int * ) tr.refdef.areamask ) [ i ] ^ ( ( int * ) fd->areamask ) [ i ]; ( ( int * ) tr.refdef.areamask ) [ i ] = ( ( int * ) fd->areamask ) [ i ]; } if ( areaDiff ) { // a door just opened or something tr.refdef.areamaskModified = true; } } R_AddWorldLightsToScene(); // derived info tr.refdef.floatTime = float(double(tr.refdef.time) * 0.001); tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; tr.refdef.drawSurfs = backEndData[ tr.smpFrame ]->drawSurfs; tr.refdef.numInteractions = r_firstSceneInteraction; tr.refdef.interactions = backEndData[ tr.smpFrame ]->interactions; tr.refdef.numEntities = r_numEntities - r_firstSceneEntity; tr.refdef.entities = &backEndData[ tr.smpFrame ]->entities[ r_firstSceneEntity ]; tr.refdef.numLights = r_numLights - r_firstSceneLight; tr.refdef.lights = &backEndData[ tr.smpFrame ]->lights[ r_firstSceneLight ]; tr.refdef.numPolys = r_numPolys - r_firstScenePoly; tr.refdef.polys = &backEndData[ tr.smpFrame ]->polys[ r_firstScenePoly ]; tr.refdef.numPolybuffers = r_numPolybuffers - r_firstScenePolybuffer; tr.refdef.polybuffers = &backEndData[ tr.smpFrame ]->polybuffers[ r_firstScenePolybuffer ]; tr.refdef.numDecalProjectors = r_numDecalProjectors - r_firstSceneDecalProjector; tr.refdef.decalProjectors = &backEndData[ tr.smpFrame ]->decalProjectors[ r_firstSceneDecalProjector ]; tr.refdef.numDecals = r_numDecals - r_firstSceneDecal; tr.refdef.decals = &backEndData[ tr.smpFrame ]->decals[ r_firstSceneDecal ]; tr.refdef.numVisTests = r_numVisTests - r_firstSceneVisTest; tr.refdef.visTests = &backEndData[ tr.smpFrame ]->visTests[ r_firstSceneVisTest ]; // a single frame may have multiple scenes draw inside it -- // a 3D game view, 3D status bar renderings, 3D menus, etc. // They need to be distinguished by the light flare code, because // the visibility state for a given surface may be different in // each scene / view. tr.frameSceneNum++; tr.sceneCount++; // Tr3B: a scene can have multiple views caused by mirrors or portals // the number of views is restricted so we can use hardware occlusion queries // and put them into the BSP nodes for each view tr.viewCount = -1; // setup view parms for the initial view // // set up viewport // The refdef takes 0-at-the-top y coordinates, so // convert to GL's 0-at-the-bottom space // Com_Memset( &parms, 0, sizeof( parms ) ); if ( tr.refdef.pixelTarget == nullptr ) { parms.viewportX = tr.refdef.x; parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height ); } else { //Driver bug, if we try and do pixel target work along the top edge of a window //we can end up capturing part of the status bar. (see screenshot corruption..) //Soooo.. use the middle. parms.viewportX = glConfig.vidWidth / 2; parms.viewportY = glConfig.vidHeight / 2; } parms.viewportWidth = tr.refdef.width; parms.viewportHeight = tr.refdef.height; parms.scissorX = parms.viewportX; parms.scissorY = parms.viewportY; parms.scissorWidth = parms.viewportWidth; parms.scissorHeight = parms.viewportHeight; Vector4Set( parms.viewportVerts[ 0 ], parms.viewportX, parms.viewportY, 0, 1 ); Vector4Set( parms.viewportVerts[ 1 ], parms.viewportX + parms.viewportWidth, parms.viewportY, 0, 1 ); Vector4Set( parms.viewportVerts[ 2 ], parms.viewportX + parms.viewportWidth, parms.viewportY + parms.viewportHeight, 0, 1 ); Vector4Set( parms.viewportVerts[ 3 ], parms.viewportX, parms.viewportY + parms.viewportHeight, 0, 1 ); parms.isPortal = false; parms.fovX = tr.refdef.fov_x; parms.fovY = tr.refdef.fov_y; VectorCopy( fd->vieworg, parms.orientation.origin ); VectorCopy( fd->viewaxis[ 0 ], parms.orientation.axis[ 0 ] ); VectorCopy( fd->viewaxis[ 1 ], parms.orientation.axis[ 1 ] ); VectorCopy( fd->viewaxis[ 2 ], parms.orientation.axis[ 2 ] ); VectorCopy( fd->vieworg, parms.pvsOrigin ); Vector4Copy( fd->gradingWeights, parms.gradingWeights ); R_RenderView( &parms ); R_RenderPostProcess(); // the next scene rendered in this frame will tack on after this one r_firstSceneDrawSurf = tr.refdef.numDrawSurfs; r_firstSceneInteraction = tr.refdef.numInteractions; r_firstSceneEntity = r_numEntities; r_firstSceneLight = r_numLights; r_firstScenePoly = r_numPolys; r_firstScenePolybuffer = r_numPolybuffers; r_firstSceneVisTest = r_numVisTests; tr.frontEndMsec += ri.Milliseconds() - startTime; }
/* * R_RenderMeshGLSL_Shadowmap */ static void R_RenderMeshGLSL_Shadowmap( r_glslfeat_t programFeatures ) { int i; int state; int scissor[4], old_scissor[4]; int program, object; vec3_t tdir, lightDir; shaderpass_t *pass = r_back.accumPasses[0]; if( r_shadows_pcf->integer ) programFeatures |= GLSL_SHADOWMAP_APPLY_PCF; if( r_shadows_dither->integer ) programFeatures |= GLSL_SHADOWMAP_APPLY_DITHER; // update uniforms program = R_RegisterGLSLProgram( pass->program_type, pass->program, NULL, NULL, NULL, 0, programFeatures ); object = R_GetProgramObject( program ); if( !object ) return; Vector4Copy( ri.scissor, old_scissor ); for( i = 0, r_back.currentCastGroup = r_shadowGroups; i < r_numShadowGroups; i++, r_back.currentCastGroup++ ) { if( !( r_back.currentShadowBits & r_back.currentCastGroup->bit ) ) continue; // project the bounding box on to screen then use scissor test // so that fragment shader isn't run for unshadowed regions if( !R_ScissorForBounds( r_back.currentCastGroup->visCorners, &scissor[0], &scissor[1], &scissor[2], &scissor[3] ) ) continue; GL_Scissor( ri.refdef.x + scissor[0], ri.refdef.y + scissor[1], scissor[2], scissor[3] ); R_BindShaderpass( pass, r_back.currentCastGroup->depthTexture, 0, NULL ); GL_TexEnv( GL_MODULATE ); qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB ); qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL ); // calculate the fragment color R_ModifyColor( pass, qfalse, qfalse ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ) | GLSTATE_BLEND_MTEX; GL_SetState( state ); qglUseProgramObjectARB( object ); VectorCopy( r_back.currentCastGroup->direction, tdir ); Matrix_TransformVector( ri.currententity->axis, tdir, lightDir ); R_UpdateProgramUniforms( program, ri.viewOrigin, vec3_origin, lightDir, r_back.currentCastGroup->lightAmbient, NULL, NULL, qtrue, r_back.currentCastGroup->depthTexture->upload_width, r_back.currentCastGroup->depthTexture->upload_height, r_back.currentCastGroup->projDist, 0, 0, colorArrayCopy[0], r_back.overBrightBits, r_back.currentShaderTime, r_back.entityColor ); R_FlushArrays(); qglUseProgramObjectARB( 0 ); qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE ); } GL_Scissor( old_scissor[0], old_scissor[1], old_scissor[2], old_scissor[3] ); }
/* * FTLIB_DrawRawString - Doesn't care about aligning. Returns drawn len. * It can stop when reaching maximum width when a value has been parsed. */ size_t FTLIB_DrawRawString( int x, int y, const char *str, size_t maxwidth, int *width, qfontface_t *font, vec4_t color, int flags ) { unsigned int xoffset = 0; vec4_t scolor; const char *s, *olds; int gc, colorindex; wchar_t num, prev_num = 0; qglyph_t *glyph, *prev_glyph = NULL; renderString_f renderString; getKerning_f getKerning; bool hasKerning; if( !str || !font ) return 0; Vector4Copy( color, scolor ); renderString = font->f->renderString; getKerning = font->f->getKerning; hasKerning = ( flags & TEXTDRAWFLAG_KERNING ) && font->hasKerning; for( s = str; s; ) { olds = s; gc = FTLIB_GrabChar( &s, &num, &colorindex, flags ); if( gc == GRABCHAR_CHAR ) { if( num == '\n' ) break; if( num < ' ' ) continue; glyph = FTLIB_GetGlyph( font, num ); if( !glyph ) { num = FTLIB_REPLACEMENT_GLYPH; glyph = FTLIB_GetGlyph( font, num ); } if( !glyph->shader ) renderString( font, olds ); // ignore kerning at this point so the full width of the previous character will always be returned if( maxwidth && ( ( xoffset + glyph->x_advance ) > maxwidth ) ) { s = olds; break; } if( hasKerning && prev_num ) xoffset += getKerning( font, prev_glyph, glyph ); FTLIB_DrawRawChar( x + xoffset, y, num, font, scolor ); xoffset += glyph->x_advance; prev_num = num; prev_glyph = glyph; } else if( gc == GRABCHAR_COLOR ) { assert( ( unsigned )colorindex < MAX_S_COLORS ); VectorCopy( color_table[colorindex], scolor ); } else if( gc == GRABCHAR_END ) break; else assert( 0 ); } if( width ) *width = xoffset; return ( s - str ); }
void CM_AddFacetBevels( facet_t *facet ) { int i, j, k, l; int axis, dir, order, flipped; float plane[4], d, minBack, newplane[4]; winding_t *w, *w2; vec3_t mins, maxs, vec, vec2; #ifndef ADDBEVELS return; #endif Vector4Copy( planes[ facet->surfacePlane ].plane, plane ); w = BaseWindingForPlane( plane, plane[3] ); for ( j = 0 ; j < facet->numBorders && w ; j++ ) { if ( facet->borderPlanes[j] == facet->surfacePlane ) { continue; } Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane ); if ( !facet->borderInward[j] ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } ChopWindingInPlace( &w, plane, plane[3], 0.1f ); } if ( !w ) { return; } WindingBounds( w, mins, maxs ); // add the axial planes order = 0; for ( axis = 0; axis < 3; axis++ ) { for ( dir = -1; dir <= 1; dir += 2, order++ ) { VectorClear( plane ); plane[axis] = dir; if ( dir == 1 ) { plane[3] = maxs[axis]; } else { plane[3] = -mins[axis]; } //if it's the surface plane if ( CM_PlaneEqual( &planes[facet->surfacePlane], plane, &flipped ) ) { continue; } // see if the plane is allready present for ( i = 0; i < facet->numBorders; i++ ) { if ( dir > 0 ) { if ( planes[facet->borderPlanes[i]].plane[axis] >= 0.9999f ) { break; } } else { if ( planes[facet->borderPlanes[i]].plane[axis] <= -0.9999f ) { break; } } } if ( i == facet->numBorders ) { if ( facet->numBorders > 4 + 6 + 16 ) { Com_Printf( "ERROR: too many bevels\n" ); } facet->borderPlanes[facet->numBorders] = CM_FindPlane2( plane, &flipped ); facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = flipped; facet->numBorders++; } } } // // add the edge bevels // // test the non-axial plane edges for ( j = 0; j < w->numpoints; j++ ) { k = ( j + 1 ) % w->numpoints; VectorSubtract( w->p[j], w->p[k], vec ); //if it's a degenerate edge if ( VectorNormalize( vec ) < 0.5f ) { continue; } CM_SnapVector( vec ); for ( k = 0; k < 3; k++ ) { if ( vec[k] == -1.0f || vec[k] == 1.0f || ( vec[k] == 0.0f && vec[( k + 1 ) % 3] == 0.0f ) ) { break; // axial } } if ( k < 3 ) { continue; // only test non-axial edges } // try the six possible slanted axials from this edge for ( axis = 0 ; axis < 3 ; axis++ ) { for ( dir = -1 ; dir <= 1 ; dir += 2 ) { // construct a plane VectorClear( vec2 ); vec2[axis] = dir; CrossProduct( vec, vec2, plane ); if ( VectorNormalize( plane ) < 0.5f ) { continue; } plane[3] = DotProduct( w->p[j], plane ); // if all the points of the facet winding are // behind this plane, it is a proper edge bevel minBack = 0.0f; for ( l = 0; l < w->numpoints; l++ ) { d = DotProduct( w->p[l], plane ) - plane[3]; if ( d > 0.1f ) { break; // point in front } if ( d < minBack ) { minBack = d; } } // if some point was at the front if ( l < w->numpoints ) { continue; } // if no points at the back then the winding is on the bevel plane if ( minBack > -0.1f ) { break; } //if it's the surface plane if ( CM_PlaneEqual( &planes[facet->surfacePlane], plane, &flipped ) ) { continue; } // see if the plane is allready present for ( i = 0; i < facet->numBorders; i++ ) { if ( CM_PlaneEqual( &planes[facet->borderPlanes[i]], plane, &flipped ) ) { break; } } if ( i == facet->numBorders ) { if ( facet->numBorders > 4 + 6 + 16 ) { Com_Printf( "ERROR: too many bevels\n" ); } facet->borderPlanes[facet->numBorders] = CM_FindPlane2( plane, &flipped ); for ( k = 0; k < facet->numBorders; k++ ) { if ( facet->borderPlanes[facet->numBorders] == facet->borderPlanes[k] ) { Com_Printf( "WARNING: bevel plane already used\n" ); } } facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = flipped; // w2 = CopyWinding( w ); Vector4Copy( planes[facet->borderPlanes[facet->numBorders]].plane, newplane ); if ( !facet->borderInward[facet->numBorders] ) { VectorNegate( newplane, newplane ); newplane[3] = -newplane[3]; } //end if ChopWindingInPlace( &w2, newplane, newplane[3], 0.1f ); if ( !w2 ) { // TTimo: any map load spams with this error .. don't print it anymore //Com_DPrintf("WARNING: CM_AddFacetBevels... invalid bevel\n"); continue; } else { FreeWinding( w2 ); } // facet->numBorders++; //already got a bevel // break; } } } } FreeWinding( w ); #ifndef BSPC //add opposite plane facet->borderPlanes[facet->numBorders] = facet->surfacePlane; facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = qtrue; facet->numBorders++; #endif //BSPC }
/* ================== CM_ValidateFacet If the facet isn't bounded by its borders, we screwed up. ================== */ static qboolean CM_ValidateFacet( cFacet_t *facet ) { float plane[ 4 ]; int j; winding_t *w; vec3_t bounds[ 2 ]; if ( facet->surfacePlane == -1 ) { return qfalse; } Vector4Copy( planes[ facet->surfacePlane ].plane, plane ); w = BaseWindingForPlane( plane, plane[ 3 ] ); for ( j = 0; j < facet->numBorders && w; j++ ) { if ( facet->borderPlanes[ j ] == -1 ) { FreeWinding( w ); return qfalse; } Vector4Copy( planes[ facet->borderPlanes[ j ] ].plane, plane ); if ( !facet->borderInward[ j ] ) { VectorSubtract( vec3_origin, plane, plane ); plane[ 3 ] = -plane[ 3 ]; } ChopWindingInPlace( &w, plane, plane[ 3 ], 0.1f ); } if ( !w ) { return qfalse; // winding was completely chopped away } // see if the facet is unreasonably large WindingBounds( w, bounds[ 0 ], bounds[ 1 ] ); FreeWinding( w ); for ( j = 0; j < 3; j++ ) { if ( bounds[ 1 ][ j ] - bounds[ 0 ][ j ] > MAX_WORLD_COORD ) { return qfalse; // we must be missing a plane } if ( bounds[ 0 ][ j ] >= MAX_WORLD_COORD ) { return qfalse; } if ( bounds[ 1 ][ j ] <= MIN_WORLD_COORD ) { return qfalse; } } return qtrue; // winding is fine }
static void FilterTraceWindingIntoNodes_r(traceWinding_t * tw, int nodeNum) { int num; vec4_t plane1, plane2, reverse; traceNode_t *node; traceWinding_t front, back; /* don't filter if passed a bogus node (solid, etc) */ if(nodeNum < 0 || nodeNum >= numTraceNodes) return; /* get node */ node = &traceNodes[nodeNum]; /* is this a decision node? */ if(node->type >= 0) { /* create winding plane if necessary, filtering out bogus windings as well */ if(nodeNum == headNodeNum) { if(!PlaneFromPoints(tw->plane, tw->v[0].xyz, tw->v[1].xyz, tw->v[2].xyz, qtrue)) return; } /* validate the node */ if(node->children[0] == 0 || node->children[1] == 0) Error("Invalid tracenode: %d", nodeNum); /* get node plane */ Vector4Copy(node->plane, plane1); /* get winding plane */ Vector4Copy(tw->plane, plane2); /* invert surface plane */ VectorSubtract(vec3_origin, plane2, reverse); reverse[3] = -plane2[3]; /* front only */ if(DotProduct(plane1, plane2) > 0.999f && fabs(plane1[3] - plane2[3]) < 0.001f) { FilterTraceWindingIntoNodes_r(tw, node->children[0]); return; } /* back only */ if(DotProduct(plane1, reverse) > 0.999f && fabs(plane1[3] - reverse[3]) < 0.001f) { FilterTraceWindingIntoNodes_r(tw, node->children[1]); return; } /* clip the winding by node plane */ ClipTraceWinding(tw, plane1, &front, &back); /* filter by node plane */ if(front.numVerts >= 3) FilterTraceWindingIntoNodes_r(&front, node->children[0]); if(back.numVerts >= 3) FilterTraceWindingIntoNodes_r(&back, node->children[1]); /* return to caller */ return; } /* add winding to leaf node */ num = AddTraceWinding(tw); AddItemToTraceNode(node, num); }
/* ============== Tess_AddQuadStampExt2 ============== */ void Tess_AddQuadStampExt2( vec4_t quadVerts[ 4 ], const vec4_t color, float s1, float t1, float s2, float t2 ) { int i; vec3_t normal, tangent, binormal; int ndx; GLimp_LogComment( "--- Tess_AddQuadStampExt2 ---\n" ); Tess_CheckOverflow( 4, 6 ); ndx = tess.numVertexes; // triangle indexes for a simple quad tess.indexes[ tess.numIndexes ] = ndx; tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; VectorCopy( quadVerts[ 0 ], tess.verts[ ndx + 0 ].xyz ); VectorCopy( quadVerts[ 1 ], tess.verts[ ndx + 1 ].xyz ); VectorCopy( quadVerts[ 2 ], tess.verts[ ndx + 2 ].xyz ); VectorCopy( quadVerts[ 3 ], tess.verts[ ndx + 3 ].xyz ); tess.attribsSet |= ATTR_POSITION | ATTR_COLOR | ATTR_TEXCOORD | ATTR_QTANGENT; // constant normal all the way around vec2_t st[ 3 ] = { { s1, t1 }, { s2, t1 }, { s2, t2 } }; R_CalcFaceNormal( normal, quadVerts[ 0 ], quadVerts[ 1 ], quadVerts[ 2 ] ); R_CalcTangents( tangent, binormal, quadVerts[ 0 ], quadVerts[ 1 ], quadVerts[ 2 ], st[ 0 ], st[ 1 ], st[ 2 ] ); //if ( !calcNormals ) //{ // VectorNegate( backEnd.viewParms.orientation.axis[ 0 ], normal ); //} R_TBNtoQtangents( tangent, binormal, normal, tess.verts[ ndx ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 1 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 2 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 3 ].qtangents ); // standard square texture coordinates tess.verts[ ndx ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 1 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 1 ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 2 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 2 ].texCoords[ 1 ] = floatToHalf( t2 ); tess.verts[ ndx + 3 ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx + 3 ].texCoords[ 1 ] = floatToHalf( t2 ); // constant color all the way around // should this be identity and let the shader specify from entity? u8vec4_t iColor; floatToUnorm8( color, iColor ); for ( i = 0; i < 4; i++ ) { Vector4Copy( iColor, tess.verts[ ndx + i ].color ); } tess.numVertexes += 4; tess.numIndexes += 6; }
/* ============= Tess_SurfacePolychain ============= */ static void Tess_SurfacePolychain( srfPoly_t *p ) { int i, j; int numVertexes; int numIndexes; GLimp_LogComment( "--- Tess_SurfacePolychain ---\n" ); Tess_CheckOverflow( p->numVerts, 3 * ( p->numVerts - 2 ) ); // fan triangles into the tess array numVertexes = 0; for ( i = 0; i < p->numVerts; i++ ) { VectorCopy( p->verts[ i ].xyz, tess.verts[ tess.numVertexes + i ].xyz ); tess.verts[ tess.numVertexes + i ].texCoords[ 0 ] = floatToHalf( p->verts[ i ].st[ 0 ] ); tess.verts[ tess.numVertexes + i ].texCoords[ 1 ] = floatToHalf( p->verts[ i ].st[ 1 ] ); Vector4Copy( p->verts[ i ].modulate, tess.verts[ tess.numVertexes + i ].color ); numVertexes++; } // generate fan indexes into the tess array numIndexes = 0; for ( i = 0; i < p->numVerts - 2; i++ ) { tess.indexes[ tess.numIndexes + i * 3 + 0 ] = tess.numVertexes; tess.indexes[ tess.numIndexes + i * 3 + 1 ] = tess.numVertexes + i + 1; tess.indexes[ tess.numIndexes + i * 3 + 2 ] = tess.numVertexes + i + 2; numIndexes += 3; } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR; // calc tangent spaces if ( tess.surfaceShader->interactLight && !tess.skipTangentSpaces ) { int i; float *v; const float *v0, *v1, *v2; const int16_t *t0, *t1, *t2; vec3_t tangent, *tangents; vec3_t binormal, *binormals; vec3_t normal, *normals; glIndex_t *indices; tangents = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); binormals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); normals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); for ( i = 0; i < numVertexes; i++ ) { VectorClear( tangents[ i ] ); VectorClear( binormals[ i ] ); VectorClear( normals[ i ] ); } for ( i = 0, indices = tess.indexes + tess.numIndexes; i < numIndexes; i += 3, indices += 3 ) { v0 = tess.verts[ indices[ 0 ] ].xyz; v1 = tess.verts[ indices[ 1 ] ].xyz; v2 = tess.verts[ indices[ 2 ] ].xyz; t0 = tess.verts[ indices[ 0 ] ].texCoords; t1 = tess.verts[ indices[ 1 ] ].texCoords; t2 = tess.verts[ indices[ 2 ] ].texCoords; R_CalcFaceNormal( normal, v0, v1, v2 ); R_CalcTangents( tangent, binormal, v0, v1, v2, t0, t1, t2 ); for ( j = 0; j < 3; j++ ) { v = tangents[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, tangent, v ); v = binormals[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, binormal, v ); v = normals[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, normal, v ); } } for ( i = 0; i < numVertexes; i++ ) { VectorNormalizeFast( normals[ i ] ); R_TBNtoQtangents( tangents[ i ], binormals[ i ], normals[ i ], tess.verts[ tess.numVertexes + i ].qtangents ); } ri.Hunk_FreeTempMemory( normals ); ri.Hunk_FreeTempMemory( binormals ); ri.Hunk_FreeTempMemory( tangents ); tess.attribsSet |= ATTR_QTANGENT; } tess.numIndexes += numIndexes; tess.numVertexes += numVertexes; }
/* ============== Tess_AddQuadStampExt ============== */ void Tess_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, const vec4_t color, float s1, float t1, float s2, float t2 ) { int i; vec3_t normal; int ndx; GLimp_LogComment( "--- Tess_AddQuadStampExt ---\n" ); Tess_CheckOverflow( 4, 6 ); ndx = tess.numVertexes; // triangle indexes for a simple quad tess.indexes[ tess.numIndexes ] = ndx; tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; tess.verts[ ndx ].xyz[ 0 ] = origin[ 0 ] + left[ 0 ] + up[ 0 ]; tess.verts[ ndx ].xyz[ 1 ] = origin[ 1 ] + left[ 1 ] + up[ 1 ]; tess.verts[ ndx ].xyz[ 2 ] = origin[ 2 ] + left[ 2 ] + up[ 2 ]; tess.verts[ ndx + 1 ].xyz[ 0 ] = origin[ 0 ] - left[ 0 ] + up[ 0 ]; tess.verts[ ndx + 1 ].xyz[ 1 ] = origin[ 1 ] - left[ 1 ] + up[ 1 ]; tess.verts[ ndx + 1 ].xyz[ 2 ] = origin[ 2 ] - left[ 2 ] + up[ 2 ]; tess.verts[ ndx + 2 ].xyz[ 0 ] = origin[ 0 ] - left[ 0 ] - up[ 0 ]; tess.verts[ ndx + 2 ].xyz[ 1 ] = origin[ 1 ] - left[ 1 ] - up[ 1 ]; tess.verts[ ndx + 2 ].xyz[ 2 ] = origin[ 2 ] - left[ 2 ] - up[ 2 ]; tess.verts[ ndx + 3 ].xyz[ 0 ] = origin[ 0 ] + left[ 0 ] - up[ 0 ]; tess.verts[ ndx + 3 ].xyz[ 1 ] = origin[ 1 ] + left[ 1 ] - up[ 1 ]; tess.verts[ ndx + 3 ].xyz[ 2 ] = origin[ 2 ] + left[ 2 ] - up[ 2 ]; // constant normal all the way around VectorSubtract( vec3_origin, backEnd.viewParms.orientation.axis[ 0 ], normal ); R_TBNtoQtangents( left, up, normal, tess.verts[ ndx ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 1 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 2 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 3 ].qtangents ); // standard square texture coordinates tess.verts[ ndx ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 1 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 1 ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 2 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 2 ].texCoords[ 1 ] = floatToHalf( t2 ); tess.verts[ ndx + 3 ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx + 3 ].texCoords[ 1 ] = floatToHalf( t2 ); // constant color all the way around // should this be identity and let the shader specify from entity? u8vec4_t iColor; floatToUnorm8( color, iColor ); for ( i = 0; i < 4; i++ ) { Vector4Copy( iColor, tess.verts[ ndx + i ].color ); } tess.numVertexes += 4; tess.numIndexes += 6; tess.attribsSet |= ATTR_POSITION | ATTR_QTANGENT | ATTR_COLOR | ATTR_TEXCOORD; }
/* ================== CM_AddFacetBevels ================== */ static void CM_AddFacetBevels( cfacet_t *facet ) { int i, j, k, l; int axis, dir, order, flipped; float plane[4], d, newplane[4]; vec3_t mins, maxs, vec, vec2; cwinding_t *w, *w2; Vector4Copy( planes[facet->surfacePlane].plane, plane ); w = CM_BaseWindingForPlane( plane, plane[3] ); for( j = 0; j < facet->numBorders && w; j++ ) { if( facet->borderPlanes[j] == facet->surfacePlane ) continue; Vector4Copy( planes[facet->borderPlanes[j]].plane, plane ); if( !facet->borderInward[j] ) { VectorNegate( plane, plane ); plane[3] = -plane[3]; } CM_ChopWindingInPlace( &w, plane, plane[3], ON_EPSILON ); } if( !w ) return; CM_WindingBounds( w, mins, maxs ); // // add the axial planes // order = 0; for( axis = 0; axis < 3; axis++ ) { for( dir = -1; dir <= 1; dir += 2, order++ ) { VectorClear( plane ); plane[axis] = dir; if( dir == 1 ) plane[3] = maxs[axis]; else plane[3] = -mins[axis]; // if it's the surface plane if( CM_PlaneEqual( &planes[facet->surfacePlane], plane, &flipped )) continue; // see if the plane is allready present for( i = 0; i < facet->numBorders; i++ ) { if( CM_PlaneEqual( &planes[facet->borderPlanes[i]], plane, &flipped )) break; } if( i == facet->numBorders ) { if( facet->numBorders > MAX_FACET_BEVELS ) MsgDev( D_ERROR, "CM_AddFacetBevels: too many bevels\n" ); facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped); facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = flipped; facet->numBorders++; } } } // // add the edge bevels // // test the non-axial plane edges for( j = 0; j < w->numpoints; j++ ) { k = (j + 1) % w->numpoints; VectorSubtract( w->p[j], w->p[k], vec ); // if it's a degenerate edge if( VectorNormalizeLength( vec ) < 0.5f ) continue; CM_SnapVector( vec ); for( k = 0; k < 3; k++ ) { if( vec[k] == -1 || vec[k] == 1 ) break; // axial } if( k < 3 ) continue; // only test non-axial edges // try the six possible slanted axials from this edge for( axis = 0; axis < 3; axis++ ) { for( dir = -1; dir <= 1; dir += 2 ) { // construct a plane VectorClear( vec2 ); vec2[axis] = dir; CrossProduct( vec, vec2, plane ); if( VectorNormalizeLength( plane ) < 0.5f ) continue; plane[3] = DotProduct( w->p[j], plane ); // if all the points of the facet winding are // behind this plane, it is a proper edge bevel for( l = 0; l < w->numpoints; l++ ) { d = DotProduct( w->p[l], plane ) - plane[3]; if( d > ON_EPSILON ) break; // point in front } if( l < w->numpoints ) continue; // if it's the surface plane if( CM_PlaneEqual( &planes[facet->surfacePlane], plane, &flipped )) continue; // see if the plane is allready present for( i = 0; i < facet->numBorders; i++ ) { if( CM_PlaneEqual( &planes[facet->borderPlanes[i]], plane, &flipped )) break; } if( i == facet->numBorders ) { if( facet->numBorders > MAX_FACET_BEVELS ) MsgDev( D_ERROR, "CM_AddFacetBevels: too many bevels\n" ); facet->borderPlanes[facet->numBorders] = CM_FindPlane2( plane, &flipped ); for( k = 0; k < facet->numBorders; k++ ) { if( facet->borderPlanes[facet->numBorders] == facet->borderPlanes[k] ) MsgDev( D_WARN, "CM_AddFacetBevels: bevel plane already used\n" ); } facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = flipped; w2 = CM_CopyWinding( w ); Vector4Copy( planes[facet->borderPlanes[facet->numBorders]].plane, newplane ); if( !facet->borderInward[facet->numBorders] ) { VectorNegate( newplane, newplane ); newplane[3] = -newplane[3]; } CM_ChopWindingInPlace( &w2, newplane, newplane[3], ON_EPSILON ); if( !w2 ) { cm.numInvalidBevels++; continue; } else CM_FreeWinding( w2 ); facet->numBorders++; // already got a bevel } } } } CM_FreeWinding( w ); // add opposite plane facet->borderPlanes[facet->numBorders] = facet->surfacePlane; facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = true; facet->numBorders++; }
/** origin should be a point within a unit of the plane dir should be the plane normal temporary marks will not be stored or randomly oriented, but immediately passed to the renderer. */ void CG_ImpactMark(qhandle_t markShader, const vec3_t origin, const vec3_t dir, float orientation, vec4_t color, qboolean alphaFade, float radius, qboolean temporary) { vec3_t axis[3]; float texCoordScale; vec3_t originalPoints[4]; int i, j; int numFragments; markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf; vec3_t markPoints[MAX_MARK_POINTS]; vec3_t projection; if (!cg_marks.integer) { return; } if (radius <= 0) { CG_Error("CG_ImpactMark called with <= 0 radius"); } // create the texture axis VectorNormalize2(dir, axis[0]); PerpendicularVector(axis[1], axis[0]); RotatePointAroundVector(axis[2], axis[0], axis[1], orientation); CrossProduct(axis[0], axis[2], axis[1]); texCoordScale = 0.5 * 1.0 / radius; // create the full polygon for (i = 0; i < 3; i++) { originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i]; originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i]; originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i]; originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i]; } // get the fragments VectorScale(dir, -20, projection); numFragments = trap_CM_MarkFragments(4, (void *)originalPoints, projection, MAX_MARK_POINTS, markPoints[0], MAX_MARK_FRAGMENTS, markFragments); for (i = 0, mf = markFragments; i < numFragments; i++, mf++) { polyVert_t *v; polyVert_t verts[MAX_VERTS_ON_POLY]; markPoly_t *mark; // we have an upper limit on the complexity of polygons // that we store persistantly if (mf->numPoints > MAX_VERTS_ON_POLY) { mf->numPoints = MAX_VERTS_ON_POLY; } for (j = 0, v = verts; j < mf->numPoints; j++, v++) { vec3_t delta; VectorCopy(markPoints[mf->firstPoint + j], v->xyz); VectorSubtract(v->xyz, origin, delta); v->st[0] = 0.5 + DotProduct(delta, axis[1]) * texCoordScale; v->st[1] = 0.5 + DotProduct(delta, axis[2]) * texCoordScale; SCR_SetRGBA(v->modulate, color); } // if it is a temporary (shadow) mark, add it immediately and forget about it if (temporary) { trap_R_AddPolyToScene(markShader, mf->numPoints, verts); continue; } // otherwise save it persistantly mark = CG_AllocMark(); mark->time = cg.time; mark->alphaFade = alphaFade; mark->markShader = markShader; mark->poly.numVerts = mf->numPoints; Vector4Copy(color, mark->color); memcpy(mark->verts, verts, mf->numPoints * sizeof(verts[0])); markTotal++; } }
/* * CG_SpawnPolyBeam * Spawns a polygon from start to end points length and given width. * shaderlenght makes reference to size of the texture it will draw, so it can be tiled. */ static cpoly_t *CG_SpawnPolyBeam( const vec3_t start, const vec3_t end, const vec4_t color, int width, unsigned int dietime, unsigned int fadetime, struct shader_s *shader, int shaderlength, int tag ) { cpoly_t *cgpoly; poly_t *poly; vec3_t angles, dir; int i; float xmin, ymin, xmax, ymax; float stx = 1.0f, sty = 1.0f; // find out beam polygon sizes VectorSubtract( end, start, dir ); VecToAngles( dir, angles ); xmin = 0; xmax = VectorNormalize( dir ); ymin = -( width*0.5 ); ymax = width*0.5; if( shaderlength && xmax > shaderlength ) stx = xmax / (float)shaderlength; if( xmax - xmin < ymax - ymin ) { // do not render polybeams which have width longer than their length return NULL; } cgpoly = CG_SpawnPolygon( 1.0, 1.0, 1.0, 1.0, dietime ? dietime : cgs.snapFrameTime, fadetime, shader, tag ); VectorCopy( angles, cgpoly->angles ); VectorCopy( start, cgpoly->origin ); if( color ) Vector4Copy( color, cgpoly->color ); // create the polygon inside the cgpolygon poly = cgpoly->poly; poly->shader = cgpoly->shader; poly->numverts = 0; // Vic: I think it's safe to assume there should be no fog applied to beams... poly->fognum = 0; // A Vector4Set( poly->verts[poly->numverts], xmin, 0, ymin, 1 ); poly->stcoords[poly->numverts][0] = 0; poly->stcoords[poly->numverts][1] = 0; poly->colors[poly->numverts][0] = ( uint8_t )( cgpoly->color[0] * 255 ); poly->colors[poly->numverts][1] = ( uint8_t )( cgpoly->color[1] * 255 ); poly->colors[poly->numverts][2] = ( uint8_t )( cgpoly->color[2] * 255 ); poly->colors[poly->numverts][3] = ( uint8_t )( cgpoly->color[3] * 255 ); poly->numverts++; // B Vector4Set( poly->verts[poly->numverts], xmin, 0, ymax, 1 ); poly->stcoords[poly->numverts][0] = 0; poly->stcoords[poly->numverts][1] = sty; poly->colors[poly->numverts][0] = ( uint8_t )( cgpoly->color[0] * 255 ); poly->colors[poly->numverts][1] = ( uint8_t )( cgpoly->color[1] * 255 ); poly->colors[poly->numverts][2] = ( uint8_t )( cgpoly->color[2] * 255 ); poly->colors[poly->numverts][3] = ( uint8_t )( cgpoly->color[3] * 255 ); poly->numverts++; // C Vector4Set( poly->verts[poly->numverts], xmax, 0, ymax, 1 ); poly->stcoords[poly->numverts][0] = stx; poly->stcoords[poly->numverts][1] = sty; poly->colors[poly->numverts][0] = ( uint8_t )( cgpoly->color[0] * 255 ); poly->colors[poly->numverts][1] = ( uint8_t )( cgpoly->color[1] * 255 ); poly->colors[poly->numverts][2] = ( uint8_t )( cgpoly->color[2] * 255 ); poly->colors[poly->numverts][3] = ( uint8_t )( cgpoly->color[3] * 255 ); poly->numverts++; // D Vector4Set( poly->verts[poly->numverts], xmax, 0, ymin, 1 ); poly->stcoords[poly->numverts][0] = stx; poly->stcoords[poly->numverts][1] = 0; poly->colors[poly->numverts][0] = ( uint8_t )( cgpoly->color[0] * 255 ); poly->colors[poly->numverts][1] = ( uint8_t )( cgpoly->color[1] * 255 ); poly->colors[poly->numverts][2] = ( uint8_t )( cgpoly->color[2] * 255 ); poly->colors[poly->numverts][3] = ( uint8_t )( cgpoly->color[3] * 255 ); poly->numverts++; // the verts data is stored inside cgpoly, cause it can be moved later for( i = 0; i < poly->numverts; i++ ) Vector4Copy( poly->verts[i], cgpoly->verts[i] ); return cgpoly; }
void R_DecalSurface( msurface_t *surf, decalinfo_t *decalinfo ) { // get the texture associated with this surface mtexinfo_t *tex = surf->texinfo; vec4_t textureU, textureV; float *sAxis = NULL; float s, t, w, h; decal_t *decal = surf->pdecals; // we in restore mode if( cls.state == ca_connected ) { // NOTE: we may have the decal on this surface that come from another level. // check duplicate with same position and texture while( decal != NULL ) { if( VectorCompare( decal->position, decalinfo->m_Position ) && decal->texture == decalinfo->m_iTexture ) return; // decal already exists, don't place it again decal = decal->pnext; } } Vector4Copy( tex->vecs[0], textureU ); Vector4Copy( tex->vecs[1], textureV ); // project decal center into the texture space of the surface s = DotProduct( decalinfo->m_Position, textureU ) + textureU[3] - surf->texturemins[0]; t = DotProduct( decalinfo->m_Position, textureV ) + textureV[3] - surf->texturemins[1]; // Determine the decal basis (measured in world space) // Note that the decal basis vectors 0 and 1 will always lie in the same // plane as the texture space basis vectorstextureVecsTexelsPerWorldUnits. if( decalinfo->m_Flags & FDECAL_USESAXIS ) sAxis = decalinfo->m_SAxis; R_DecalComputeBasis( surf, sAxis, decalinfo->m_Basis ); // Compute an effective width and height (axis aligned) in the parent texture space // How does this work? decalBasis[0] represents the u-direction (width) // of the decal measured in world space, decalBasis[1] represents the // v-direction (height) measured in world space. // textureVecsTexelsPerWorldUnits[0] represents the u direction of // the surface's texture space measured in world space (with the appropriate // scale factor folded in), and textureVecsTexelsPerWorldUnits[1] // represents the texture space v direction. We want to find the dimensions (w,h) // of a square measured in texture space, axis aligned to that coordinate system. // All we need to do is to find the components of the decal edge vectors // (decalWidth * decalBasis[0], decalHeight * decalBasis[1]) // in texture coordinates: w = fabs( decalinfo->m_decalWidth * DotProduct( textureU, decalinfo->m_Basis[0] )) + fabs( decalinfo->m_decalHeight * DotProduct( textureU, decalinfo->m_Basis[1] )); h = fabs( decalinfo->m_decalWidth * DotProduct( textureV, decalinfo->m_Basis[0] )) + fabs( decalinfo->m_decalHeight * DotProduct( textureV, decalinfo->m_Basis[1] )); // move s,t to upper left corner s -= ( w * 0.5f ); t -= ( h * 0.5f ); // Is this rect within the surface? -- tex width & height are unsigned if( s <= -w || t <= -h || s > (surf->extents[0] + w) || t > (surf->extents[1] + h)) { return; // nope } // stamp it R_DecalCreate( decalinfo, surf, s, t ); }
/* * FTLIB_DrawClampString */ void FTLIB_DrawClampString( int x, int y, const char *str, int xmin, int ymin, int xmax, int ymax, qfontface_t *font, vec4_t color, int flags ) { int xoffset = 0; vec4_t scolor; int colorindex; wchar_t num, prev_num = 0; const char *s = str, *olds; int gc; qglyph_t *glyph, *prev_glyph = NULL; renderString_f renderString; getKerning_f getKerning; bool hasKerning; if( !str || !font ) return; if( ( xmax <= xmin ) || ( ymax <= ymin ) || ( x > xmax ) || ( y > ymax ) ) return; Vector4Copy( color, scolor ); renderString = font->f->renderString; getKerning = font->f->getKerning; hasKerning = ( flags & TEXTDRAWFLAG_KERNING ) && font->hasKerning; while( 1 ) { olds = s; gc = FTLIB_GrabChar( &s, &num, &colorindex, flags ); if( gc == GRABCHAR_CHAR ) { if( num == '\n' ) break; if( num < ' ' ) continue; glyph = FTLIB_GetGlyph( font, num ); if( !glyph ) { num = FTLIB_REPLACEMENT_GLYPH; glyph = FTLIB_GetGlyph( font, num ); } if( !glyph->shader ) renderString( font, olds ); if( prev_num ) { xoffset += prev_glyph->x_advance; if( hasKerning ) xoffset += getKerning( font, prev_glyph, glyph ); } if( x + xoffset > xmax ) break; FTLIB_DrawClampChar( x + xoffset, y, num, xmin, ymin, xmax, ymax, font, scolor ); prev_num = num; prev_glyph = glyph; } else if( gc == GRABCHAR_COLOR ) { assert( ( unsigned )colorindex < MAX_S_COLORS ); VectorCopy( color_table[colorindex], scolor ); } else if( gc == GRABCHAR_END ) break; else assert( 0 ); } }
/** * @brief Calculates a per-vertex tangentspace basis and stores it in GL arrays attached to the mesh * @param mesh The mesh to calculate normals for * @param framenum The animation frame to calculate normals for * @param translate The frame translation for the given animation frame * @param backlerp Whether to store the results in the GL arrays for the previous keyframe or the next keyframe * @sa R_ModCalcUniqueNormalsAndTangents */ static void R_ModCalcNormalsAndTangents (mAliasMesh_t *mesh, int framenum, const vec3_t translate, qboolean backlerp) { int i, j; mAliasVertex_t *vertexes = &mesh->vertexes[framenum * mesh->num_verts]; mAliasCoord_t *stcoords = mesh->stcoords; const int numIndexes = mesh->num_tris * 3; const int32_t *indexArray = mesh->indexes; vec3_t triangleNormals[MAX_ALIAS_TRIS]; vec3_t triangleTangents[MAX_ALIAS_TRIS]; vec3_t triangleBitangents[MAX_ALIAS_TRIS]; float *texcoords, *verts, *normals, *tangents; /* set up array pointers for either the previous keyframe or the next keyframe */ texcoords = mesh->texcoords; if (backlerp) { verts = mesh->verts; normals = mesh->normals; tangents = mesh->tangents; } else { verts = mesh->next_verts; normals = mesh->next_normals; tangents = mesh->next_tangents; } /* calculate per-triangle surface normals and tangents*/ for (i = 0, j = 0; i < numIndexes; i += 3, j++) { vec3_t dir1, dir2; vec2_t dir1uv, dir2uv; /* calculate two mostly perpendicular edge directions */ VectorSubtract(vertexes[indexArray[i + 0]].point, vertexes[indexArray[i + 1]].point, dir1); VectorSubtract(vertexes[indexArray[i + 2]].point, vertexes[indexArray[i + 1]].point, dir2); Vector2Subtract(stcoords[indexArray[i + 0]], stcoords[indexArray[i + 1]], dir1uv); Vector2Subtract(stcoords[indexArray[i + 2]], stcoords[indexArray[i + 1]], dir2uv); /* we have two edge directions, we can calculate a third vector from * them, which is the direction of the surface normal */ CrossProduct(dir1, dir2, triangleNormals[j]); /* normalize */ VectorNormalizeFast(triangleNormals[j]); /* then we use the texture coordinates to calculate a tangent space */ if ((dir1uv[1] * dir2uv[0] - dir1uv[0] * dir2uv[1]) != 0.0) { const float frac = 1.0 / (dir1uv[1] * dir2uv[0] - dir1uv[0] * dir2uv[1]); vec3_t tmp1, tmp2; /* calculate tangent */ VectorMul(-1.0 * dir2uv[1] * frac, dir1, tmp1); VectorMul(dir1uv[1] * frac, dir2, tmp2); VectorAdd(tmp1, tmp2, triangleTangents[j]); /* calculate bitangent */ VectorMul(-1.0 * dir2uv[0] * frac, dir1, tmp1); VectorMul(dir1uv[0] * frac, dir2, tmp2); VectorAdd(tmp1, tmp2, triangleBitangents[j]); /* normalize */ VectorNormalizeFast(triangleTangents[j]); VectorNormalizeFast(triangleBitangents[j]); } else { VectorClear(triangleTangents[j]); VectorClear(triangleBitangents[j]); } } /* for each vertex */ for (i = 0; i < mesh->num_verts; i++) { vec3_t n, b, v; vec4_t t; const int len = mesh->revIndexes[i].length; const int32_t *list = mesh->revIndexes[i].list; VectorClear(n); VectorClear(t); VectorClear(b); /* for each vertex that got mapped to this one (ie. for each triangle this vertex is a part of) */ for (j = 0; j < len; j++) { const int32_t idx = list[j] / 3; VectorAdd(n, triangleNormals[idx], n); VectorAdd(t, triangleTangents[idx], t); VectorAdd(b, triangleBitangents[idx], b); } /* normalization here does shared-vertex smoothing */ VectorNormalizeFast(n); VectorNormalizeFast(t); VectorNormalizeFast(b); /* Grahm-Schmidt orthogonalization */ Orthogonalize(t, n); /* calculate handedness */ CrossProduct(n, t, v); t[3] = (DotProduct(v, b) < 0.0) ? -1.0 : 1.0; /* copy this vertex's info to all the right places in the arrays */ for (j = 0; j < len; j++) { const int32_t idx = list[j]; const int meshIndex = mesh->indexes[list[j]]; Vector2Copy(stcoords[meshIndex], (texcoords + (2 * idx))); VectorAdd(vertexes[meshIndex].point, translate, (verts + (3 * idx))); VectorCopy(n, (normals + (3 * idx))); Vector4Copy(t, (tangents + (4 * idx))); } } }
void CM_DrawDebugSurface( void ( *drawPoly )( int color, int numPoints, float *points ) ) { static cvar_t *cv; #ifndef BSPC static cvar_t *cv2; #endif const patchCollide_t *pc; facet_t *facet; winding_t *w; int i, j, k, n; int curplanenum, planenum, curinward, inward; float plane[4]; vec3_t mins = {-15, -15, -28}, maxs = {15, 15, 28}; //vec3_t mins = {0, 0, 0}, maxs = {0, 0, 0}; vec3_t v1, v2; #ifndef BSPC if ( !cv2 ) { cv2 = Cvar_Get( "r_debugSurface", "0", 0 ); } if ( cv2->integer != 1 ) { BotDrawDebugPolygons( drawPoly, cv2->integer ); return; } #endif if ( !debugPatchCollide ) { return; } #ifndef BSPC if ( !cv ) { cv = Cvar_Get( "cm_debugSize", "2", 0 ); } #endif pc = debugPatchCollide; for ( i = 0, facet = pc->facets ; i < pc->numFacets ; i++, facet++ ) { for ( k = 0 ; k < facet->numBorders + 1; k++ ) { // if ( k < facet->numBorders ) { planenum = facet->borderPlanes[k]; inward = facet->borderInward[k]; } else { planenum = facet->surfacePlane; inward = qfalse; //continue; } Vector4Copy( pc->planes[ planenum ].plane, plane ); //planenum = facet->surfacePlane; if ( inward ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } plane[3] += cv->value; //* for ( n = 0; n < 3; n++ ) { if ( plane[n] > 0 ) { v1[n] = maxs[n]; } else { v1[n] = mins[n];} } //end for VectorNegate( plane, v2 ); plane[3] += Q_fabs( DotProduct( v1, v2 ) ); //*/ w = BaseWindingForPlane( plane, plane[3] ); for ( j = 0 ; j < facet->numBorders + 1 && w; j++ ) { // if ( j < facet->numBorders ) { curplanenum = facet->borderPlanes[j]; curinward = facet->borderInward[j]; } else { curplanenum = facet->surfacePlane; curinward = qfalse; //continue; } // if ( curplanenum == planenum ) { continue; } Vector4Copy( pc->planes[ curplanenum ].plane, plane ); if ( !curinward ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } // if ( !facet->borderNoAdjust[j] ) { plane[3] -= cv->value; // } for ( n = 0; n < 3; n++ ) { if ( plane[n] > 0 ) { v1[n] = maxs[n]; } else { v1[n] = mins[n];} } //end for VectorNegate( plane, v2 ); plane[3] -= Q_fabs( DotProduct( v1, v2 ) ); ChopWindingInPlace( &w, plane, plane[3], 0.1f ); } if ( w ) { if ( facet == debugFacet ) { drawPoly( 4, w->numpoints, w->p[0] ); //Com_Printf("blue facet has %d border planes\n", facet->numBorders); } else { drawPoly( 1, w->numpoints, w->p[0] ); } FreeWinding( w ); } else { Com_Printf( "winding chopped away by border planes\n" ); } } } // draw the debug block { vec3_t v[3]; VectorCopy( debugBlockPoints[0], v[0] ); VectorCopy( debugBlockPoints[1], v[1] ); VectorCopy( debugBlockPoints[2], v[2] ); drawPoly( 2, 3, v[0] ); VectorCopy( debugBlockPoints[2], v[0] ); VectorCopy( debugBlockPoints[3], v[1] ); VectorCopy( debugBlockPoints[0], v[2] ); drawPoly( 2, 3, v[0] ); } #if 0 vec3_t v[4]; v[0][0] = pc->bounds[1][0]; v[0][1] = pc->bounds[1][1]; v[0][2] = pc->bounds[1][2]; v[1][0] = pc->bounds[1][0]; v[1][1] = pc->bounds[0][1]; v[1][2] = pc->bounds[1][2]; v[2][0] = pc->bounds[0][0]; v[2][1] = pc->bounds[0][1]; v[2][2] = pc->bounds[1][2]; v[3][0] = pc->bounds[0][0]; v[3][1] = pc->bounds[1][1]; v[3][2] = pc->bounds[1][2]; drawPoly( 4, v[0] ); #endif }
void CG_FilledBar(float x, float y, float w, float h, float *startColor, float *endColor, const float *bgColor, float frac, int flags) { vec4_t backgroundcolor = {1, 1, 1, 0.25f}, colorAtPos; // colorAtPos is the lerped color if necessary int indent = BAR_BORDERSIZE; if( frac > 1 ) { frac = 1.f; } if( frac < 0 ) { frac = 0; } if((flags&BAR_BG) && bgColor) { // BAR_BG set, and color specified, use specified bg color Vector4Copy(bgColor, backgroundcolor); } if(flags&BAR_LERP_COLOR) { Vector4Average(startColor, endColor, frac, colorAtPos); } // background if((flags&BAR_BG)) { // draw background at full size and shrink the remaining box to fit inside with a border. (alternate border may be specified by a BAR_BGSPACING_xx) CG_FillRect ( x, y, w, h, backgroundcolor ); if(flags&BAR_BGSPACING_X0Y0) { // fill the whole box (no border) } else if(flags&BAR_BGSPACING_X0Y5) { // spacing created for weapon heat indent*=3; y+=indent; h-=(2*indent); } else { // default spacing of 2 units on each side x+=indent; y+=indent; w-=(2*indent); h-=(2*indent); } } // adjust for horiz/vertical and draw the fractional box if(flags&BAR_VERT) { if(flags&BAR_LEFT) { // TODO: remember to swap colors on the ends here y+=(h*(1-frac)); } else if (flags&BAR_CENTER) { y+=(h*(1-frac)/2); } if(flags&BAR_LERP_COLOR) { CG_FillRect ( x, y, w, h * frac, colorAtPos ); } else { // CG_FillRectGradient ( x, y, w, h * frac, startColor, endColor, 0 ); CG_FillRect ( x, y, w, h * frac, startColor ); } } else { if(flags&BAR_LEFT) { // TODO: remember to swap colors on the ends here x+=(w*(1-frac)); } else if (flags&BAR_CENTER) { x+=(w*(1-frac)/2); } if(flags&BAR_LERP_COLOR) { CG_FillRect ( x, y, w*frac, h, colorAtPos ); } else { // CG_FillRectGradient ( x, y, w * frac, h, startColor, endColor, 0 ); CG_FillRect ( x, y, w*frac, h, startColor ); } } }
/* ============= CG_Scanner ============= */ void CG_Scanner( rectDef_t *rect, qhandle_t shader, vec4_t color ) { int i; vec3_t origin; vec3_t relOrigin; vec4_t hIabove; vec4_t hIbelow; vec4_t aIabove = { 1.0f, 0.0f, 0.0f, 0.75f }; vec4_t aIbelow = { 1.0f, 0.0f, 0.0f, 0.5f }; Vector4Copy( color, hIabove ); hIabove[ 3 ] *= 1.5f; Vector4Copy( color, hIbelow ); VectorCopy( entityPositions.origin, origin ); //draw human buildables below scanner plane for( i = 0; i < entityPositions.numHumanBuildables; i++ ) { VectorClear( relOrigin ); VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin ); if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) ) CG_DrawBlips( rect, relOrigin, hIbelow ); } //draw alien buildables below scanner plane for( i = 0; i < entityPositions.numAlienBuildables; i++ ) { VectorClear( relOrigin ); VectorSubtract( entityPositions.alienBuildablePos[ i ], origin, relOrigin ); if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) ) CG_DrawBlips( rect, relOrigin, aIbelow ); } //draw human clients below scanner plane for( i = 0; i < entityPositions.numHumanClients; i++ ) { VectorClear( relOrigin ); VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin ); if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) ) CG_DrawBlips( rect, relOrigin, hIbelow ); } //draw alien buildables below scanner plane for( i = 0; i < entityPositions.numAlienClients; i++ ) { VectorClear( relOrigin ); VectorSubtract( entityPositions.alienClientPos[ i ], origin, relOrigin ); if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) ) CG_DrawBlips( rect, relOrigin, aIbelow ); } if( !cg_disableScannerPlane.integer ) { trap_R_SetColor( color ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); trap_R_SetColor( NULL ); } //draw human buildables above scanner plane for( i = 0; i < entityPositions.numHumanBuildables; i++ ) { VectorClear( relOrigin ); VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin ); if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) ) CG_DrawBlips( rect, relOrigin, hIabove ); } //draw alien buildables above scanner plane for( i = 0; i < entityPositions.numAlienBuildables; i++ ) { VectorClear( relOrigin ); VectorSubtract( entityPositions.alienBuildablePos[ i ], origin, relOrigin ); if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) ) CG_DrawBlips( rect, relOrigin, aIabove ); } //draw human clients above scanner plane for( i = 0; i < entityPositions.numHumanClients; i++ ) { VectorClear( relOrigin ); VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin ); if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) ) CG_DrawBlips( rect, relOrigin, hIabove ); } //draw alien clients above scanner plane for( i = 0; i < entityPositions.numAlienClients; i++ ) { VectorClear( relOrigin ); VectorSubtract( entityPositions.alienClientPos[ i ], origin, relOrigin ); if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) ) CG_DrawBlips( rect, relOrigin, aIabove ); } }
/* * CG_AddParticles */ void CG_AddParticles( void ) { int i, j, k; float alpha; float time, time2; vec3_t org; vec3_t corner; byte_vec4_t color; int maxparticle, activeparticles; float alphaValues[MAX_PARTICLES]; cparticle_t *p, *free_particles[MAX_PARTICLES]; if( !cg_numparticles ) { return; } j = 0; maxparticle = -1; activeparticles = 0; for( i = 0, p = particles; i < cg_numparticles; i++, p++ ) { time = ( cg.time - p->time ) * 0.001f; alpha = alphaValues[i] = p->alpha + time * p->alphavel; if( alpha <= 0 ) { // faded out free_particles[j++] = p; continue; } maxparticle = i; activeparticles++; time2 = time * time * 0.5f; org[0] = p->org[0] + p->vel[0] * time + p->accel[0] * time2; org[1] = p->org[1] + p->vel[1] * time + p->accel[1] * time2; org[2] = p->org[2] + p->vel[2] * time + p->accel[2] * time2; color[0] = (uint8_t)( bound( 0, p->color[0], 1.0f ) * 255 ); color[1] = (uint8_t)( bound( 0, p->color[1], 1.0f ) * 255 ); color[2] = (uint8_t)( bound( 0, p->color[2], 1.0f ) * 255 ); color[3] = (uint8_t)( bound( 0, alpha, 1.0f ) * 255 ); corner[0] = org[0]; corner[1] = org[1] - 0.5f * p->scale; corner[2] = org[2] - 0.5f * p->scale; Vector4Set( p->pVerts[0], corner[0], corner[1] + p->scale, corner[2] + p->scale, 1 ); Vector4Set( p->pVerts[1], corner[0], corner[1], corner[2] + p->scale, 1 ); Vector4Set( p->pVerts[2], corner[0], corner[1], corner[2], 1 ); Vector4Set( p->pVerts[3], corner[0], corner[1] + p->scale, corner[2], 1 ); for( k = 0; k < 4; k++ ) { Vector4Copy( color, p->pColor[k] ); } p->poly.numverts = 4; p->poly.verts = p->pVerts; p->poly.stcoords = p->pStcoords; p->poly.colors = p->pColor; p->poly.fognum = p->fog ? 0 : -1; p->poly.shader = ( p->shader == NULL ) ? CG_MediaShader( cgs.media.shaderParticle ) : p->shader; trap_R_AddPolyToScene( &p->poly ); } i = 0; while( maxparticle >= activeparticles ) { *free_particles[i++] = particles[maxparticle--]; while( maxparticle >= activeparticles ) { if( alphaValues[maxparticle] <= 0 ) { maxparticle--; } else { break; } } } cg_numparticles = activeparticles; }
/* ** CG_DrawChat */ void CG_DrawChat( cg_gamechat_t *chat, int x, int y, char *fontName, struct qfontface_s *font, int fontSize, int width, int height, int padding_x, int padding_y, vec4_t backColor, struct shader_s *backShader ) { int i, j; int s, e, w; int utf_len; int l, total_lines, lines; int x_offset, y_offset; int font_height; int pass; int lastcolor; int message_mode; int wait_time, fade_time; const cg_gamemessage_t *msg; const char *text; char tstr[GAMECHAT_STRING_SIZE]; vec4_t fontColor; bool chat_active = false; bool background_drawn = false; int corner_radius = 12 * cgs.vidHeight / 600; int background_y; int first_candidate; font_height = trap_SCR_FontHeight( font ); message_mode = (int)trap_Cvar_Value( "con_messageMode" ); chat_active = ( chat->lastMsgTime + GAMECHAT_WAIT_IN_TIME + GAMECHAT_FADE_IN_TIME > cg.realTime || message_mode ); lines = 0; total_lines = /*!message_mode ? 0 : */ 1; if( chat_active ) { wait_time = GAMECHAT_WAIT_IN_TIME; fade_time = GAMECHAT_FADE_IN_TIME; } else { wait_time = GAMECHAT_WAIT_OUT_TIME; fade_time = GAMECHAT_FADE_OUT_TIME; } if( chat_active != chat->lastActive ) { // smooth fade ins and fade outs chat->lastActiveChangeTime = cg.realTime - ( 1.0 - chat->activeFrac ) * ( wait_time + fade_time ); } if( cg.realTime >= chat->lastActiveChangeTime + wait_time ) { int time_diff, time_interval; time_diff = cg.realTime - ( chat->lastActiveChangeTime + wait_time ); time_interval = fade_time; if( time_diff <= time_interval ) { chat->activeFrac = (float)time_diff / time_interval; } else { chat->activeFrac = 1; } } else { chat->activeFrac = 0; } if( chat_active ) { backColor[3] *= chat->activeFrac; } else { backColor[3] *= ( 1.0 - chat->activeFrac ); } for( i = 0; i < GAMECHAT_STACK_SIZE; i++ ) { bool old_msg; l = chat->nextMsg - 1 - i; if( l < 0 ) { l = GAMECHAT_STACK_SIZE + l; } msg = &chat->messages[l]; text = msg->text; old_msg = !message_mode && ( cg.realTime > msg->time + GAMECHAT_NOTIFY_TIME ); if( !background_drawn && backColor[3] ) { if( old_msg ) { // keep the box being drawn for a while to prevent it from flickering // upon arrival of the possibly entered chat message if( !( !chat_active && cg.realTime <= chat->lastActiveChangeTime + 200 ) ) { break; } } background_y = y; trap_R_DrawStretchPic( x, background_y, width, height - corner_radius, 0.0f, 0.0f, 1.0f, 0.5f, backColor, backShader ); background_y += height - corner_radius; if( trap_IN_IME_GetCandidates( NULL, 0, 10, NULL, &first_candidate ) ) { int candidates_height = ( first_candidate ? 3 : 5 ) * font_height; trap_R_DrawStretchPic( x, background_y, width, candidates_height, 0.0f, 0.5f, 1.0f, 0.5f, backColor, backShader ); background_y += candidates_height; } trap_R_DrawStretchPic( x, background_y, corner_radius, corner_radius, 0.0f, 0.5f, 0.5f, 1.0f, backColor, backShader ); trap_R_DrawStretchPic( x + corner_radius, background_y, width - corner_radius * 2, corner_radius, 0.5f, 0.5f, 0.5f, 1.0f, backColor, backShader ); trap_R_DrawStretchPic( x + width - corner_radius, background_y, corner_radius, corner_radius, 0.5f, 0.5f, 1.0f, 1.0f, backColor, backShader ); background_drawn = true; } // unless user is typing something, only display recent messages if( old_msg ) { break; } pass = 0; lines = 0; lastcolor = ColorIndex( COLOR_WHITE ); parse_string: l = 1; s = e = 0; while( 1 ) { int len; memset( tstr, 0, sizeof( tstr ) ); // skip whitespaces at start for( ; text[s] == '\n' || Q_IsBreakingSpace( text + s ); s = Q_Utf8SyncPos( text, s + 1, UTF8SYNC_RIGHT ) ) ; // empty string if( !text[s] ) { break; } w = -1; len = trap_SCR_StrlenForWidth( text + s, font, width - padding_x * 2 ); clamp_low( len, 1 ); for( j = s; ( j < ( s + len ) ) && text[j] != '\0'; j += utf_len ) { utf_len = Q_Utf8SyncPos( text + j, 1, UTF8SYNC_RIGHT ); memcpy( tstr + j - s, text + j, utf_len ); if( text[j] == '\n' || Q_IsBreakingSpace( text + j ) ) { w = j; // last whitespace } if( text[j] == '\n' ) { break; } } e = j; // end // try to word avoid splitting words, unless no other options if( text[j] != '\0' && w > 0 ) { // stop at the last encountered whitespace j = w; } tstr[j - s] = '\0'; Vector4Copy( color_table[lastcolor], fontColor ); fontColor[3] = chat_active ? chat->activeFrac : 1.0 - chat->activeFrac; if( pass ) { // now actually render the line x_offset = padding_x; y_offset = height - padding_y - font_height - ( total_lines + lines - l ) * ( font_height + 2 ); if( y_offset < padding_y ) { break; } trap_SCR_DrawClampString( x + x_offset, y + y_offset, tstr, x + padding_x, y + padding_y, x - padding_x + width, y - padding_y + height, font, fontColor ); l++; } else { // increase the lines counter lines++; } if( !text[j] ) { // fast path: we don't need two passes in case of one-liners.. if( lines == 1 ) { x_offset = padding_x; y_offset = height - font_height - total_lines * ( font_height + 2 ); if( y_offset < padding_y ) { break; } trap_SCR_DrawClampString( x + x_offset, y + y_offset, tstr, x + padding_x, y + padding_y, x - padding_x + width, y - padding_y + height, font, fontColor ); total_lines++; pass++; } break; } if( pass ) { // grab the last color token to carry it over to the next line lastcolor = Q_ColorStrLastColor( lastcolor, tstr, j - s ); } s = j; } if( !pass ) { pass++; goto parse_string; } else { total_lines += lines; } } // let the engine know where the input line should be drawn trap_SCR_DrawChat( x + padding_x, y + height - padding_y - font_height, width - padding_x, font ); chat->lastActive = chat_active; }
/* * CG_AddFragmentedDecal */ void CG_AddFragmentedDecal( vec3_t origin, vec3_t dir, float orient, float radius, float r, float g, float b, float a, struct shader_s *shader ) { int i, j, c; vec3_t axis[3]; byte_vec4_t color; fragment_t *fr, fragments[MAX_TEMPDECAL_FRAGMENTS]; int numfragments; poly_t poly; vec4_t verts[MAX_BLOBSHADOW_VERTS]; static vec4_t t_verts[MAX_TEMPDECAL_VERTS * MAX_TEMPDECALS]; static vec4_t t_norms[MAX_TEMPDECAL_VERTS * MAX_TEMPDECALS]; static vec2_t t_stcoords[MAX_TEMPDECAL_VERTS * MAX_TEMPDECALS]; static byte_vec4_t t_colors[MAX_TEMPDECAL_VERTS * MAX_TEMPDECALS]; if( radius <= 0 || VectorCompare( dir, vec3_origin ) ) { return; // invalid } // calculate orientation matrix VectorNormalize2( dir, axis[0] ); PerpendicularVector( axis[1], axis[0] ); RotatePointAroundVector( axis[2], axis[0], axis[1], orient ); CrossProduct( axis[0], axis[2], axis[1] ); numfragments = trap_R_GetClippedFragments( origin, radius, axis, // clip it MAX_BLOBSHADOW_VERTS, verts, MAX_TEMPDECAL_FRAGMENTS, fragments ); // no valid fragments if( !numfragments ) { return; } // clamp and scale colors if( r < 0 ) { r = 0; } else if( r > 1 ) { r = 255; } else { r *= 255; } if( g < 0 ) { g = 0; } else if( g > 1 ) { g = 255; } else { g *= 255; } if( b < 0 ) { b = 0; } else if( b > 1 ) { b = 255; } else { b *= 255; } if( a < 0 ) { a = 0; } else if( a > 1 ) { a = 255; } else { a *= 255; } color[0] = ( uint8_t )( r ); color[1] = ( uint8_t )( g ); color[2] = ( uint8_t )( b ); color[3] = ( uint8_t )( a ); c = *( int * )color; radius = 0.5f / radius; VectorScale( axis[1], radius, axis[1] ); VectorScale( axis[2], radius, axis[2] ); memset( &poly, 0, sizeof( poly ) ); for( i = 0, fr = fragments; i < numfragments; i++, fr++ ) { if( fr->numverts <= 0 ) { continue; } if( cg_numDecalVerts + (unsigned)fr->numverts > sizeof( t_verts ) / sizeof( t_verts[0] ) ) { return; } poly.shader = shader; poly.verts = &t_verts[cg_numDecalVerts]; poly.normals = &t_norms[cg_numDecalVerts]; poly.stcoords = &t_stcoords[cg_numDecalVerts]; poly.colors = &t_colors[cg_numDecalVerts]; poly.numverts = fr->numverts; poly.fognum = fr->fognum; cg_numDecalVerts += (unsigned)fr->numverts; for( j = 0; j < fr->numverts; j++ ) { vec3_t v; Vector4Copy( verts[fr->firstvert + j], poly.verts[j] ); VectorCopy( axis[0], poly.normals[j] ); poly.normals[j][3] = 0; VectorSubtract( poly.verts[j], origin, v ); poly.stcoords[j][0] = DotProduct( v, axis[1] ) + 0.5f; poly.stcoords[j][1] = DotProduct( v, axis[2] ) + 0.5f; *( int * )poly.colors[j] = c; } trap_R_AddPolyToScene( &poly ); } }
void CG_BuildableStatusParse( const char *filename, buildStat_t *bs ) { pc_token_t token; int handle; const char *s; int i; float f; vec4_t c; handle = trap_Parse_LoadSource( filename ); if( !handle ) return; while( 1 ) { if( !trap_Parse_ReadToken( handle, &token ) ) break; if( !Q_stricmp( token.string, "frameShader" ) ) { if( PC_String_Parse( handle, &s ) ) bs->frameShader = trap_R_RegisterShader( s ); continue; } else if( !Q_stricmp( token.string, "overlayShader" ) ) { if( PC_String_Parse( handle, &s ) ) bs->overlayShader = trap_R_RegisterShader( s ); continue; } else if( !Q_stricmp( token.string, "noPowerShader" ) ) { if( PC_String_Parse( handle, &s ) ) bs->noPowerShader = trap_R_RegisterShader( s ); continue; } else if( !Q_stricmp( token.string, "markedShader" ) ) { if( PC_String_Parse( handle, &s ) ) bs->markedShader = trap_R_RegisterShader( s ); continue; } else if( !Q_stricmp( token.string, "healthSevereColor" ) ) { if( PC_Color_Parse( handle, &c ) ) Vector4Copy( c, bs->healthSevereColor ); continue; } else if( !Q_stricmp( token.string, "healthHighColor" ) ) { if( PC_Color_Parse( handle, &c ) ) Vector4Copy( c, bs->healthHighColor ); continue; } else if( !Q_stricmp( token.string, "healthElevatedColor" ) ) { if( PC_Color_Parse( handle, &c ) ) Vector4Copy( c, bs->healthElevatedColor ); continue; } else if( !Q_stricmp( token.string, "healthGuardedColor" ) ) { if( PC_Color_Parse( handle, &c ) ) Vector4Copy( c, bs->healthGuardedColor ); continue; } else if( !Q_stricmp( token.string, "healthLowColor" ) ) { if( PC_Color_Parse( handle, &c ) ) Vector4Copy( c, bs->healthLowColor ); continue; } else if( !Q_stricmp( token.string, "foreColor" ) ) { if( PC_Color_Parse( handle, &c ) ) Vector4Copy( c, bs->foreColor ); continue; } else if( !Q_stricmp( token.string, "backColor" ) ) { if( PC_Color_Parse( handle, &c ) ) Vector4Copy( c, bs->backColor ); continue; } else if( !Q_stricmp( token.string, "frameHeight" ) ) { if( PC_Int_Parse( handle, &i ) ) bs->frameHeight = i; continue; } else if( !Q_stricmp( token.string, "frameWidth" ) ) { if( PC_Int_Parse( handle, &i ) ) bs->frameWidth = i; continue; } else if( !Q_stricmp( token.string, "healthPadding" ) ) { if( PC_Int_Parse( handle, &i ) ) bs->healthPadding = i; continue; } else if( !Q_stricmp( token.string, "overlayHeight" ) ) { if( PC_Int_Parse( handle, &i ) ) bs->overlayHeight = i; continue; } else if( !Q_stricmp( token.string, "overlayWidth" ) ) { if( PC_Int_Parse( handle, &i ) ) bs->overlayWidth = i; continue; } else if( !Q_stricmp( token.string, "verticalMargin" ) ) { if( PC_Float_Parse( handle, &f ) ) bs->verticalMargin = f; continue; } else if( !Q_stricmp( token.string, "horizontalMargin" ) ) { if( PC_Float_Parse( handle, &f ) ) bs->horizontalMargin = f; continue; } else { Com_Printf("CG_BuildableStatusParse: unknown token %s in %s\n", token.string, filename ); bs->loaded = qfalse; return; } } bs->loaded = qtrue; }
/* ================== CM_AddFacetBevels ================== */ static void CM_AddFacetBevels( facet_t *facet ) { int i, j, k, l; int axis, dir, order; float plane[4], d, newplane[4]; winding_t *w, *w2; vec3_t mins, maxs, vec, vec2; Vector4Copy( planes[ facet->surfacePlane ].plane, plane ); w = BaseWindingForPlane( plane, plane[3] ); for ( j = 0 ; j < facet->numBorders && w ; j++ ) { if (facet->borderPlanes[j] == facet->surfacePlane) continue; Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane ); if ( !facet->borderInward[j] ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } ChopWindingInPlace( &w, plane, plane[3], 0.1f ); } if ( !w ) { return; } WindingBounds(w, mins, maxs); // add the axial planes order = 0; qbool flipped; for ( axis = 0 ; axis < 3 ; axis++ ) { for ( dir = -1 ; dir <= 1 ; dir += 2, order++ ) { VectorClear(plane); plane[axis] = dir; plane[3] = (dir == 1) ? maxs[axis] : -mins[axis]; //if it's the surface plane if (CM_PlaneEqual(&planes[facet->surfacePlane], plane, &flipped)) { continue; } // see if the plane is allready present for ( i = 0 ; i < facet->numBorders ; i++ ) { if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) break; } if ( i == facet->numBorders ) { if (facet->numBorders > 4 + 6 + 16) Com_Printf("ERROR: too many bevels\n"); facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped); facet->borderNoAdjust[facet->numBorders] = qfalse; facet->borderInward[facet->numBorders] = flipped; facet->numBorders++; } } } // // add the edge bevels // // test the non-axial plane edges for ( j = 0 ; j < w->numpoints ; j++ ) { k = (j+1)%w->numpoints; VectorSubtract (w->p[j], w->p[k], vec); //if it's a degenerate edge if (VectorNormalize (vec) < 0.5) continue; CM_SnapVector(vec); for ( k = 0; k < 3 ; k++ ) if ( vec[k] == -1 || vec[k] == 1 ) break; // axial if ( k < 3 ) continue; // only test non-axial edges // try the six possible slanted axials from this edge for ( axis = 0 ; axis < 3 ; axis++ ) { for ( dir = -1 ; dir <= 1 ; dir += 2 ) { // construct a plane VectorClear (vec2); vec2[axis] = dir; CrossProduct (vec, vec2, plane); if (VectorNormalize (plane) < 0.5) continue; plane[3] = DotProduct (w->p[j], plane); // if all the points of the facet winding are // behind this plane, it is a proper edge bevel for ( l = 0 ; l < w->numpoints ; l++ ) { d = DotProduct (w->p[l], plane) - plane[3]; if (d > 0.1) break; // point in front } if ( l < w->numpoints ) continue; //if it's the surface plane if (CM_PlaneEqual(&planes[facet->surfacePlane], plane, &flipped)) { continue; } // see if the plane is allready present for ( i = 0 ; i < facet->numBorders ; i++ ) { if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) { break; } } if ( i == facet->numBorders ) { if (facet->numBorders > 4 + 6 + 16) Com_Printf("ERROR: too many bevels\n"); facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped); for ( k = 0 ; k < facet->numBorders ; k++ ) { if (facet->borderPlanes[facet->numBorders] == facet->borderPlanes[k]) Com_Printf("WARNING: bevel plane already used\n"); } facet->borderNoAdjust[facet->numBorders] = qfalse; facet->borderInward[facet->numBorders] = flipped; w2 = CopyWinding(w); Vector4Copy(planes[facet->borderPlanes[facet->numBorders]].plane, newplane); if (!facet->borderInward[facet->numBorders]) { VectorNegate(newplane, newplane); newplane[3] = -newplane[3]; } ChopWindingInPlace( &w2, newplane, newplane[3], 0.1f ); if (!w2) { Com_DPrintf("WARNING: CM_AddFacetBevels... invalid bevel\n"); continue; } else { FreeWinding(w2); } facet->numBorders++; //already got a bevel // break; } } } } FreeWinding( w ); #ifndef BSPC //add opposite plane facet->borderPlanes[facet->numBorders] = facet->surfacePlane; facet->borderNoAdjust[facet->numBorders] = qfalse; facet->borderInward[facet->numBorders] = qtrue; facet->numBorders++; #endif //BSPC }
/* * SCR_DrawPlayerTab */ static int SCR_DrawPlayerTab( const char **ptrptr, int team, int x, int y, int panelWidth, struct qfontface_s *font, int pass ) { int dir, align, i, columncount; char type, string[MAX_STRING_CHARS]; const char *token, *layout; int height, width, xoffset, yoffset; vec4_t teamcolor = { 0.0f, 0.0f, 0.0f, 1.0f }, color; int iconnum; struct shader_s *icon; bool highlight = false, trans = false; if( GS_TeamBasedGametype() ) { dir = ( team == TEAM_ALPHA ) ? -1 : 1; align = ( team == TEAM_ALPHA ) ? ALIGN_RIGHT_TOP : ALIGN_LEFT_TOP; } else { dir = 0; align = ALIGN_CENTER_TOP; } xoffset = 0; yoffset = 0; height = trap_SCR_FontHeight( font ); // start from the center again xoffset = CG_HorizontalAlignForWidth( 0, align, panelWidth ); xoffset += ( SCB_CENTERMARGIN * dir ); // draw the background columncount = 0; if( ( team == TEAM_ALPHA ) || ( team == TEAM_BETA ) ) CG_TeamColor( team, teamcolor ); // draw the player tab column titles layout = cgs.configStrings[CS_SCB_PLAYERTAB_LAYOUT]; while( SCR_GetNextColumnLayout( &layout, NULL, &type, &width, font ) != NULL ) { // grab the actual scoreboard data if( !SCR_ParseToken( ptrptr, &token ) ) break; if( SCR_SkipColumn( type ) ) continue; Vector4Copy( colorWhite, color ); // reset to white after each column icon = NULL; string[0] = 0; // interpret the data based on the type defined in the layout switch( type ) { default: CG_Error( "SCR_DrawPlayerTab: Invalid player tab layout\n" ); break; case 's': // is a string { char l10n[MAX_STRING_CHARS]; Q_strncpyz( string, CG_TranslateColoredString( token, l10n, sizeof( l10n ) ), sizeof( string ) ); } break; case 'n': // is a player name indicated by player number i = atoi( token ); if( i < 0 ) // negative numbers toggle transparency on { trans = true; i = -1 - i; } if( i < 0 || i >= gs.maxclients ) Q_strncpyz( string, "invalid", sizeof( string ) ); else Q_strncpyz( string, cgs.clientInfo[i].name, sizeof( string ) ); if( ISVIEWERENTITY( i + 1 ) ) // highlight if it's our own player highlight = true; break; case 'i': // is a integer (negatives are colored in red) i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%i", i ); VectorCopy( i >= 0 ? colorWhite : colorRed, color ); break; case 'f': // is a float Q_snprintfz( string, sizeof( string ), "%.2f", atof( token ) ); break; case 'l': // p is an integer colored in latency style i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%i", i ); CG_PingColor( i, color ); break; case 'b': // is a Y/N boolean i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%s", CG_TranslateString( ( i != 0 ) ? "Yes" : "No" ) ); VectorCopy( i ? colorGreen : colorRed, color ); break; case 'p': // is a picture. It uses height for width to get a square iconnum = atoi( token ); if( ( iconnum > 0 ) && ( iconnum < MAX_IMAGES ) ) icon = cgs.imagePrecache[iconnum]; break; case 't': // is a race time. Convert time into MM:SS:mm { unsigned int milli, min, sec; milli = (unsigned int)( atoi( token ) ); if( !milli ) Q_snprintfz( string, sizeof( string ), CG_TranslateString( "no time" ) ); else { min = milli / 60000; milli -= min * 60000; sec = milli / 1000; milli -= sec * 1000; Q_snprintfz( string, sizeof( string ), va( "%02i:%02i.%03i", min, sec, milli ) ); } } break; case 'r': // is a ready state tick that is hidden when not in warmup if( atoi( token ) ) icon = CG_MediaShader( cgs.media.shaderVSayIcon[VSAY_YES] ); break; } if( !width ) continue; // draw the column background teamcolor[3] = SCB_BACKGROUND_ALPHA; if( columncount & 1 ) teamcolor[3] -= 0.15; if( highlight ) teamcolor[3] += 0.3; if( trans ) color[3] = 0.3; if( !pass ) { trap_R_DrawStretchPic( x + xoffset, y + yoffset, width, height, 0, 0, 1, 1, teamcolor, cgs.shaderWhite ); if( icon ) SCR_AddPlayerIcon( icon, x + xoffset, y + yoffset, color[3], font ); } // draw the column value if( pass && string[0] ) { trap_SCR_DrawClampString( x + xoffset, y + yoffset, string, x + xoffset, y + yoffset, x + xoffset + width, y + yoffset + height, font, color ); } columncount++; xoffset += width; } yoffset += height; return yoffset; }
//================ //CG_DrawMiniMap //================ void CG_DrawMiniMap( int x, int y, int iw, int ih, qboolean draw_playernames, qboolean draw_itemnames, int align, vec4_t color ) { int i, entnum; centity_t *cent; vec3_t coords; vec4_t tmp_col, tmp_white_alpha, tmp_yellow_alpha; // background color of the map vec3_t mins, maxs, extend; int map_w, map_h, map_z; int x_lefttop, y_lefttop, z_lefttop; // the negative y coordinate (bottom of the map) float map_div_w, map_div_h; qboolean isSelf; if( !cg_showminimap->integer ) return; // if inside a team if( cg.predictedPlayerState.stats[STAT_REALTEAM] >= TEAM_PLAYERS && cg.predictedPlayerState.stats[STAT_REALTEAM] < GS_MAX_TEAMS ) { if( !GS_CanShowMinimap() || !( cg_showminimap->integer & 1 ) ) return; } else if( !( cg_showminimap->integer & 2 ) ) { // allow only when chasing a player and the player is allowed to see it if( !GS_CanShowMinimap() || !( cg_showminimap->integer & 1 ) || cg.predictedPlayerState.stats[STAT_REALTEAM] == cg.predictedPlayerState.stats[STAT_TEAM] ) return; } if( !cgs.shaderMiniMap ) return; x = CG_HorizontalAlignForWidth( x, align, iw ); y = CG_VerticalAlignForHeight( y, align, ih ); Vector4Copy( color, tmp_col ); Vector4Copy( colorWhite, tmp_white_alpha ); Vector4Copy( colorYellow, tmp_yellow_alpha ); tmp_white_alpha[3] = color[3]; tmp_yellow_alpha[3] = color[3]; // Get Worldmodel bounds... trap_R_ModelBounds( NULL, mins, maxs ); // NULL for world model... // make it a square bounding box VectorSubtract( maxs, mins, extend ); if( extend[1] > extend[0] ) { mins[0] -= ( extend[1] - extend[0] ) * 0.5; maxs[0] += ( extend[1] - extend[0] ) * 0.5; } else { mins[1] -= ( extend[0] - extend[1] ) * 0.5; maxs[1] += ( extend[0] - extend[1] ) * 0.5; } map_w = maxs[0] - mins[0]; // map width (in game units) map_h = maxs[1] - mins[1]; map_z = maxs[2] - mins[2]; x_lefttop = -mins[0]; // the negative x coordinate ( left of the map ) y_lefttop = -mins[1]; // the negative y coordinate (bottom of the map) z_lefttop = -mins[2]; // the negative y coordinate (bottom of the map) map_div_w = (float)map_w / (float)iw; map_div_h = (float)map_h / (float)ih; CG_DrawHUDRect( x, y, ALIGN_LEFT_TOP, iw, ih, 1, 1, tmp_col, cgs.shaderMiniMap ); //alignment test, to display green dot at 0,0 //CG_DrawHUDRect( x + x_lefttop/map_div_w -1, y + y_lefttop/map_div_h -1,ALIGN_LEFT_TOP,3,3,1,1, colorGreen, CG_MediaShader( cgs.media.shaderMiniMap ) ); for( i = 0; i < cg.frame.numEntities; i++ ) { entnum = cg.frame.parsedEntities[i&( MAX_PARSE_ENTITIES-1 )].number; // filter invalid ents if( entnum < 1 || entnum >= MAX_EDICTS ) continue; // retrieve the centity_t cent = &cg_entities[entnum]; isSelf = ( (unsigned)entnum == cg.predictedPlayerState.POVnum ); if( ( cent->current.type != ET_PLAYER ) && ( cent->current.type != ET_MINIMAP_ICON ) && !( cent->item ) ) continue; if( isSelf ) VectorCopy( cg.predictedPlayerState.pmove.origin, coords ); else VectorCopy( cent->current.origin, coords ); coords[0] = ( coords[0] + x_lefttop ) / map_div_w; coords[1] = ih - ( coords[1] + y_lefttop ) / map_div_h; coords[2] = ( coords[2] + (float)z_lefttop ) / (float)map_z; // is it a player? if( ( cent->current.type == ET_PLAYER ) ) { int box_size = (int)( 3.0 + coords[2] * 10.0 ); // check if we're allowed to see team members only (coaches, CA) if( cg.predictedPlayerState.stats[STAT_LAYOUTS] & STAT_LAYOUT_SPECTEAMONLY || (cg.predictedPlayerState.stats[STAT_REALTEAM] != TEAM_SPECTATOR && GS_TeamOnlyMinimap()) ) { if( cg.predictedPlayerState.stats[STAT_REALTEAM] != cent->current.team ) continue; } if( cent->current.team == TEAM_SPECTATOR ) { if( entnum != cg.view.POVent ) continue; VectorSet( tmp_col, 1, 1, 1 ); } else { CG_TeamColor( cent->current.team, tmp_col ); } // get color tmp_col[3] = bound( 0, color[3] + 0.3f, 1 ); CG_DrawHUDRect( x + (int)coords[0] -box_size/2, y + (int)coords[1] -box_size/2, ALIGN_LEFT_TOP, box_size, box_size, box_size, box_size, tmp_col, NULL ); // differentiate ourselves with an arrow if( isSelf ) { int thisX, thisY, thisSize; thisSize = max( box_size, 8 ); thisX = CG_VerticalAlignForHeight( x + (int)coords[0], ALIGN_CENTER_MIDDLE, thisSize ); thisY = CG_VerticalAlignForHeight( y + (int)coords[1] - thisSize, ALIGN_CENTER_MIDDLE, thisSize ); trap_R_DrawStretchPic( thisX, thisY, thisSize, thisSize, 0, 0, 1, 1, tmp_yellow_alpha, CG_MediaShader( cgs.media.shaderDownArrow ) ); } // do we want names too? if( draw_playernames == qtrue ) trap_SCR_DrawString( x + (int)coords[0] + 8, y + (int)coords[1] - 4, ALIGN_LEFT_TOP, COM_RemoveColorTokensExt( cgs.clientInfo[cent->current.number-1].name, qtrue ), cgs.fontSystemSmall, tmp_yellow_alpha ); } else if( cent->current.type == ET_MINIMAP_ICON ) { if( cent->ent.customShader ) { vec4_t tmp_this_color; int thisX, thisY, thisSize; thisSize = (float)cent->prev.frame + (float)( cent->current.frame - cent->prev.frame ) * cg.lerpfrac; if( thisSize <= 0 ) thisSize = 18; tmp_this_color[0] = (float)cent->ent.shaderRGBA[0] / 255.0f; tmp_this_color[1] = (float)cent->ent.shaderRGBA[1] / 255.0f; tmp_this_color[2] = (float)cent->ent.shaderRGBA[2] / 255.0f; tmp_this_color[3] = 1.0f; thisX = CG_VerticalAlignForHeight( x + coords[0], ALIGN_CENTER_MIDDLE, thisSize ); thisY = CG_VerticalAlignForHeight( y + coords[1], ALIGN_CENTER_MIDDLE, thisSize ); trap_R_DrawStretchPic( thisX, thisY, thisSize, thisSize, 0, 0, 1, 1, tmp_this_color, cent->ent.customShader ); } } else if( cent->item && cent->item->icon ) { // if ALIGN_CENTER_MIDDLE or something is used, images are f****d // so thats why they are set manually at the correct pos with -n CG_DrawHUDRect( x+(int)coords[0]-8, y+(int)coords[1]-8, ALIGN_LEFT_TOP, 15, 15, 1, 1, tmp_white_alpha, trap_R_RegisterPic( cent->item->icon ) ); if( draw_itemnames == qtrue ) trap_SCR_DrawString( x + (int)coords[0] + 16, y + (int)coords[1] - 8, ALIGN_LEFT_TOP, cent->item->shortname, cgs.fontSystemSmall, tmp_yellow_alpha ); } } }
/* ==================== CM_TraceThroughPatchCollide ==================== */ void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ) { int i, j, hit, hitnum; float offset, enterFrac, leaveFrac, t; patchPlane_t *planes; facet_t *facet; float plane[4] = {0, 0, 0, 0}, bestplane[4] = {0, 0, 0, 0}; vec3_t startp, endp; #ifndef BSPC static cvar_t *cv; #endif //BSPC if ( !CM_BoundsIntersect( tw->bounds[0], tw->bounds[1], pc->bounds[0], pc->bounds[1] ) ) { return; } if ( tw->isPoint ) { CM_TracePointThroughPatchCollide( tw, pc ); return; } facet = pc->facets; for ( i = 0 ; i < pc->numFacets ; i++, facet++ ) { enterFrac = -1.0; leaveFrac = 1.0; hitnum = -1; // planes = &pc->planes[ facet->surfacePlane ]; VectorCopy( planes->plane, plane ); plane[3] = planes->plane[3]; if ( tw->sphere.use ) { // adjust the plane distance apropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane, tw->sphere.offset ); if ( t > 0.0f ) { VectorSubtract( tw->start, tw->sphere.offset, startp ); VectorSubtract( tw->end, tw->sphere.offset, endp ); } else { VectorAdd( tw->start, tw->sphere.offset, startp ); VectorAdd( tw->end, tw->sphere.offset, endp ); } } else { offset = DotProduct( tw->offsets[ planes->signbits ], plane ); plane[3] -= offset; VectorCopy( tw->start, startp ); VectorCopy( tw->end, endp ); } if ( !CM_CheckFacetPlane( plane, startp, endp, &enterFrac, &leaveFrac, &hit ) ) { continue; } if ( hit ) { Vector4Copy( plane, bestplane ); } for ( j = 0; j < facet->numBorders; j++ ) { planes = &pc->planes[ facet->borderPlanes[j] ]; if ( facet->borderInward[j] ) { VectorNegate( planes->plane, plane ); plane[3] = -planes->plane[3]; } else { VectorCopy( planes->plane, plane ); plane[3] = planes->plane[3]; } if ( tw->sphere.use ) { // adjust the plane distance apropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane, tw->sphere.offset ); if ( t > 0.0f ) { VectorSubtract( tw->start, tw->sphere.offset, startp ); VectorSubtract( tw->end, tw->sphere.offset, endp ); } else { VectorAdd( tw->start, tw->sphere.offset, startp ); VectorAdd( tw->end, tw->sphere.offset, endp ); } } else { // NOTE: this works even though the plane might be flipped because the bbox is centered offset = DotProduct( tw->offsets[ planes->signbits ], plane ); plane[3] += fabs( offset ); VectorCopy( tw->start, startp ); VectorCopy( tw->end, endp ); } if ( !CM_CheckFacetPlane( plane, startp, endp, &enterFrac, &leaveFrac, &hit ) ) { break; } if ( hit ) { hitnum = j; Vector4Copy( plane, bestplane ); } } if ( j < facet->numBorders ) { continue; } //never clip against the back side if ( hitnum == facet->numBorders - 1 ) { continue; } if ( enterFrac < leaveFrac && enterFrac >= 0 ) { if ( enterFrac < tw->trace.fraction ) { if ( enterFrac < 0 ) { enterFrac = 0; } #ifndef BSPC if ( !cv ) { cv = Cvar_Get( "r_debugSurfaceUpdate", "1", 0 ); } if ( cv && cv->integer ) { debugPatchCollide = pc; debugFacet = facet; } #endif //BSPC tw->trace.fraction = enterFrac; VectorCopy( bestplane, tw->trace.plane.normal ); tw->trace.plane.dist = bestplane[3]; } } } }
qbyte *_ColorForEntity( int entNum, byte_vec4_t color, qboolean player ) { centity_t *cent; int team; centity_t *owner; cvar_t *teamForceColor = NULL; int rgbcolor; int *forceColor; if( entNum < 1 || entNum >= MAX_EDICTS ) { Vector4Set( color, 255, 255, 255, 255 ); return color; } owner = cent = &cg_entities[entNum]; if( cent->current.type == ET_CORPSE && cent->current.bodyOwner ) // it's a body owner = &cg_entities[cent->current.bodyOwner]; team = CG_ForceTeam( owner->current.number, owner->current.team ); switch( team ) { case TEAM_ALPHA: { teamForceColor = cg_teamALPHAcolor; forceColor = &cgs.teamColor[TEAM_ALPHA]; } break; case TEAM_BETA: { teamForceColor = cg_teamBETAcolor; forceColor = &cgs.teamColor[TEAM_BETA]; } break; case TEAM_PLAYERS: default: { teamForceColor = cg_teamPLAYERScolor; forceColor = &cgs.teamColor[TEAM_PLAYERS]; } break; } if( teamForceColor->modified ) { CG_RegisterTeamColor( team ); } //if forced models is enabled or it is color forced team we do, if( (teamForceColor->string[0] || team >= TEAM_ALPHA) && cent->current.type != ET_CORPSE ) { // skin color to team color rgbcolor = *forceColor; Vector4Set( color, COLOR_R( rgbcolor ), COLOR_G( rgbcolor ), COLOR_B( rgbcolor ), 255 ); } // user defined colors if it's a player else if( ( player && ( owner->current.number - 1 < gs.maxclients ) ) && cent->current.type != ET_CORPSE ) { Vector4Copy( cgs.clientInfo[owner->current.number - 1].color, color ); } // Make corpses grey else if ( cent->current.type == ET_CORPSE && cent->current.bodyOwner ) { Vector4Set( color, 60, 60, 60, 60 ); } else // white for everything else { Vector4Set( color, 255, 255, 255, 255 ); } return color; }