/*
==============
R_IQMBuildSkeleton
==============
*/
static int IQMBuildSkeleton( refSkeleton_t *skel, skelAnimation_t *skelAnim,
			     int startFrame, int endFrame, float frac )
{
	int            i;
	IQAnim_t       *anim;
	transform_t    *newPose, *oldPose;
	vec3_t         mins, maxs;

	anim = skelAnim->iqm;

	// Validate the frames so there is no chance of a crash.
	// This will write directly into the entity structure, so
	// when the surfaces are rendered, they don't need to be
	// range checked again.
	if( anim->flags & IQM_LOOP ) {
		startFrame %= anim->num_frames;
		endFrame %= anim->num_frames;
	} else {
		Q_clamp( startFrame, 0, anim->num_frames - 1 );
		Q_clamp( endFrame, 0, anim->num_frames - 1 );
	}

	// compute frame pointers
	oldPose = &anim->poses[ startFrame * anim->num_joints ];
	newPose = &anim->poses[ endFrame * anim->num_joints ];

	// calculate a bounding box in the current coordinate system
	if( anim->bounds ) {
		float *bounds = &anim->bounds[ 6 * startFrame ];
		VectorCopy( bounds, mins );
		VectorCopy( bounds + 3, maxs );

		bounds = &anim->bounds[ 6 * endFrame ];
		BoundsAdd( mins, maxs, bounds, bounds + 3 );
	}

	for ( i = 0; i < anim->num_joints; i++ )
	{
		TransStartLerp( &skel->bones[ i ].t );
		TransAddWeight( 1.0f - frac, &oldPose[ i ], &skel->bones[ i ].t );
		TransAddWeight( frac, &newPose[ i ], &skel->bones[ i ].t );
		TransEndLerp( &skel->bones[ i ].t );

#if defined( REFBONE_NAMES )
		Q_strncpyz( skel->bones[ i ].name, anim->name, sizeof( skel->bones[ i ].name ) );
#endif

		skel->bones[ i ].parentIndex = anim->jointParents[ i ];
	}

	skel->numBones = anim->num_joints;
	skel->type = refSkeletonType_t::SK_RELATIVE;
	VectorCopy( mins, skel->bounds[ 0 ] );
	VectorCopy( maxs, skel->bounds[ 1 ] );
	return true;
}
Exemple #2
0
static qboolean G_VoteTimelimit( gentity_t *ent, int numArgs, const char *arg1, const char *arg2 ) {
	float tl = Q_clamp( 0.0f, (float)atof( arg2 ), 35790.0f );
	Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %.3f", arg1, tl );
	Q_strncpyz( level.voteDisplayString, level.voteString, sizeof( level.voteDisplayString ) );
	Q_strncpyz( level.voteStringClean, level.voteString, sizeof( level.voteStringClean ) );
	return qtrue;
}
/*
==============
RE_BuildSkeleton
==============
*/
int RE_BuildSkeleton(refSkeleton_t * skel, qhandle_t hAnim, int startFrame, int endFrame, float frac, qboolean clearOrigin)
{
	skelAnimation_t *skelAnim;

	skelAnim = R_GetAnimationByHandle(hAnim);

	if(skelAnim->type == AT_MD5 && skelAnim->md5)
	{
		int             i;
		md5Animation_t *anim;
		md5Channel_t   *channel;
		md5Frame_t     *newFrame, *oldFrame;
		vec3_t          newOrigin, oldOrigin, lerpedOrigin;
		quat_t          newQuat, oldQuat, lerpedQuat;
		int             componentsApplied;

		anim = skelAnim->md5;

		// Validate the frames so there is no chance of a crash.
		// This will write directly into the entity structure, so
		// when the surfaces are rendered, they don't need to be
		// range checked again.
		/*
		   if((startFrame >= anim->numFrames) || (startFrame < 0) || (endFrame >= anim->numFrames) || (endFrame < 0))
		   {
		   ri.Printf(PRINT_DEVELOPER, "RE_BuildSkeleton: no such frame %d to %d for '%s'\n", startFrame, endFrame, anim->name);
		   //startFrame = 0;
		   //endFrame = 0;
		   }
		 */

		Q_clamp(startFrame, 0, anim->numFrames - 1);
		Q_clamp(endFrame, 0, anim->numFrames - 1);

		// compute frame pointers
		oldFrame = &anim->frames[startFrame];
		newFrame = &anim->frames[endFrame];

		// calculate a bounding box in the current coordinate system
		for(i = 0; i < 3; i++)
		{
			skel->bounds[0][i] =
				oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
			skel->bounds[1][i] =
				oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
		}

		for(i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++)
		{
			// set baseframe values
			VectorCopy(channel->baseOrigin, newOrigin);
			VectorCopy(channel->baseOrigin, oldOrigin);

			QuatCopy(channel->baseQuat, newQuat);
			QuatCopy(channel->baseQuat, oldQuat);

			componentsApplied = 0;

			// update tranlation bits
			if(channel->componentsBits & COMPONENT_BIT_TX)
			{
				oldOrigin[0] = oldFrame->components[channel->componentsOffset + componentsApplied];
				newOrigin[0] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			if(channel->componentsBits & COMPONENT_BIT_TY)
			{
				oldOrigin[1] = oldFrame->components[channel->componentsOffset + componentsApplied];
				newOrigin[1] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			if(channel->componentsBits & COMPONENT_BIT_TZ)
			{
				oldOrigin[2] = oldFrame->components[channel->componentsOffset + componentsApplied];
				newOrigin[2] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			// update quaternion rotation bits
			if(channel->componentsBits & COMPONENT_BIT_QX)
			{
				((vec_t *) oldQuat)[0] = oldFrame->components[channel->componentsOffset + componentsApplied];
				((vec_t *) newQuat)[0] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			if(channel->componentsBits & COMPONENT_BIT_QY)
			{
				((vec_t *) oldQuat)[1] = oldFrame->components[channel->componentsOffset + componentsApplied];
				((vec_t *) newQuat)[1] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			if(channel->componentsBits & COMPONENT_BIT_QZ)
			{
				((vec_t *) oldQuat)[2] = oldFrame->components[channel->componentsOffset + componentsApplied];
				((vec_t *) newQuat)[2] = newFrame->components[channel->componentsOffset + componentsApplied];
			}

			QuatCalcW(oldQuat);
			QuatNormalize(oldQuat);

			QuatCalcW(newQuat);
			QuatNormalize(newQuat);

#if 1
			VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin);
			QuatSlerp(oldQuat, newQuat, frac, lerpedQuat);
#else
			VectorCopy(newOrigin, lerpedOrigin);
			QuatCopy(newQuat, lerpedQuat);
#endif

			// copy lerped information to the bone + extra data
			skel->bones[i].parentIndex = channel->parentIndex;

			if(channel->parentIndex < 0 && clearOrigin)
			{
				VectorClear(skel->bones[i].origin);
				QuatClear(skel->bones[i].rotation);

				// move bounding box back
				VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]);
				VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]);
			}
			else
			{
				VectorCopy(lerpedOrigin, skel->bones[i].origin);
			}

			QuatCopy(lerpedQuat, skel->bones[i].rotation);

#if defined(REFBONE_NAMES)
			Q_strncpyz(skel->bones[i].name, channel->name, sizeof(skel->bones[i].name));
#endif
		}

		skel->numBones = anim->numChannels;
		skel->type = SK_RELATIVE;
		return qtrue;
	}
	else if(skelAnim->type == AT_PSA && skelAnim->psa)
	{
		int             i;
		psaAnimation_t *anim;
		axAnimationKey_t *newKey, *oldKey;
		axReferenceBone_t *refBone;
		vec3_t          newOrigin, oldOrigin, lerpedOrigin;
		quat_t          newQuat, oldQuat, lerpedQuat;
		refSkeleton_t   skeleton;

		anim = skelAnim->psa;

		Q_clamp(startFrame, 0, anim->info.numRawFrames - 1);
		Q_clamp(endFrame, 0, anim->info.numRawFrames - 1);

		ClearBounds(skel->bounds[0], skel->bounds[1]);

		skel->numBones = anim->info.numBones;
		for(i = 0, refBone = anim->bones; i < anim->info.numBones; i++, refBone++)
		{
			oldKey = &anim->keys[startFrame * anim->info.numBones + i];
			newKey = &anim->keys[endFrame * anim->info.numBones + i];

			VectorCopy(newKey->position, newOrigin);
			VectorCopy(oldKey->position, oldOrigin);

			QuatCopy(newKey->quat, newQuat);
			QuatCopy(oldKey->quat, oldQuat);

			//QuatCalcW(oldQuat);
			//QuatNormalize(oldQuat);

			//QuatCalcW(newQuat);
			//QuatNormalize(newQuat);

			VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin);
			QuatSlerp(oldQuat, newQuat, frac, lerpedQuat);

			// copy lerped information to the bone + extra data
			skel->bones[i].parentIndex = refBone->parentIndex;

			if(refBone->parentIndex < 0 && clearOrigin)
			{
				VectorClear(skel->bones[i].origin);
				QuatClear(skel->bones[i].rotation);

				// move bounding box back
				VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]);
				VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]);
			}
			else
			{
				VectorCopy(lerpedOrigin, skel->bones[i].origin);
			}

			QuatCopy(lerpedQuat, skel->bones[i].rotation);

#if defined(REFBONE_NAMES)
			Q_strncpyz(skel->bones[i].name, refBone->name, sizeof(skel->bones[i].name));
#endif

			// calculate absolute values for the bounding box approximation
			VectorCopy(skel->bones[i].origin, skeleton.bones[i].origin);
			QuatCopy(skel->bones[i].rotation, skeleton.bones[i].rotation);

			if(refBone->parentIndex >= 0)
			{
				vec3_t          rotated;
				quat_t          quat;
				refBone_t      *parent;
				refBone_t      *bone;

				bone = &skeleton.bones[i];
				parent = &skeleton.bones[refBone->parentIndex];

				QuatTransformVector(parent->rotation, bone->origin, rotated);

				VectorAdd(parent->origin, rotated, bone->origin);

				QuatMultiply1(parent->rotation, bone->rotation, quat);
				QuatCopy(quat, bone->rotation);

				AddPointToBounds(bone->origin, skel->bounds[0], skel->bounds[1]);
			}
		}

		skel->numBones = anim->info.numBones;
		skel->type = SK_RELATIVE;
		return qtrue;
	}

	//ri.Printf(PRINT_WARNING, "RE_BuildSkeleton: bad animation '%s' with handle %i\n", anim->name, hAnim);

	// FIXME: clear existing bones and bounds?
	return qfalse;
}
/*
===============
CG_AddReverbEffects
===============
*/
static void CG_AddReverbEffects( vec3_t loc )
{
	int   i, j;
	float dist, weight;
	int   selectedIdx[3] = { 0, 0, 0 };
	float selectedWeight[3] = { 0.0f, 0.0f, 0.0f };
	float totalWeight = 0.0f;
	qboolean haveGlobal = qfalse;

	// the first allocated reverb is special in that it may be global
	i = 0;

	if ( cgs.gameReverbEffects[0][0] && cgs.gameGradingModels[0] == -1 )
	{
		selectedIdx[0] = 0;
		selectedWeight[0] = 2.0f; // won't be sorted down
		haveGlobal = qtrue;
		i = 1;
	}

	for(; i < MAX_REVERB_EFFECTS; i++ )
	{
		if( !cgs.gameReverbEffects[i][0] )
		{
			continue;
		}

		dist = trap_CM_DistanceToModel( loc, cgs.gameReverbModels[i] );
		weight = 1.0f - dist / cgs.gameReverbDistances[i];
		weight = Q_clamp( weight, 0.0f, 1.0f ); // Maths::clampFraction( weight )

		// search 3 greatest weights
		if( weight <= selectedWeight[2] )
		{
			continue;
		}

		for( j = 1; j >= 0; j-- )
		{
			if( weight <= selectedWeight[j] )
			{
				break;
			}

			selectedIdx[j+1] = selectedIdx[j];
			selectedWeight[j+1] = selectedWeight[j];
		}

		selectedIdx[j+1] = i;
		selectedWeight[j+1] = weight;
	}

	i = haveGlobal ? 1 : 0;

	for(; i < 3; i++ )
	{
        totalWeight += selectedWeight[i];
	}

    if (haveGlobal)
    {
        if (totalWeight > 1.0f)
        {
            selectedWeight[0] = 0;
        }
        else
        {
            selectedWeight[0] = 1.0f - totalWeight;
            totalWeight = 1.0f;
        }
    }

    if (totalWeight == 0.0f)
    {
        for(i = 0; i < 3; i++)
        {
            selectedWeight[i] = 0.0f;
        }
    }
    else
    {
        for(i = 0; i < 3; i++)
        {
            selectedWeight[i] /= totalWeight;
        }
    }

    for (i = 0; i < 3; i++)
    {
        // The mapper defined intensity is between 0 and 2 to have saner defaults (the presets are very strong)
        trap_S_SetReverb(i, cgs.gameReverbEffects[selectedIdx[i]], selectedWeight[i] / 2.0f * cgs.gameReverbIntensities[selectedIdx[i]]);
    }
}
/*
===============
CG_CalcColorGradingForPoint

Sets cg.refdef.gradingWeights
===============
*/
static void CG_CalcColorGradingForPoint( vec3_t loc )
{
	int   i, j;
	float dist, weight;
	int   selectedIdx[3] = { 0, 0, 0 };
	float selectedWeight[3] = { 0.0f, 0.0f, 0.0f };
	float totalWeight = 0.0f;
	int freeSlot = -1;
	qboolean haveGlobal = qfalse;

	// the first allocated grading is special in that it may be global
	i = 0;

	if ( cgs.gameGradingTextures[0] && cgs.gameGradingModels[0] == -1 )
	{
		selectedIdx[0] = 0; // shouldn't be needed
		selectedWeight[0] = 2.0f; // won't be sorted down
		haveGlobal = qtrue;
		i = 1;
	}

	for(; i < MAX_GRADING_TEXTURES; i++ )
	{
		if( !cgs.gameGradingTextures[i] )
		{
			continue;
		}

		dist = trap_CM_DistanceToModel( loc, cgs.gameGradingModels[i] );
		weight = 1.0f - dist / cgs.gameGradingDistances[i];
		weight = Q_clamp( weight, 0.0f, 1.0f ); // Maths::clampFraction( weight )

		// search 3 greatest weights
		if( weight <= selectedWeight[2] )
		{
			continue;
		}

		for( j = 1; j >= 0; j-- )
		{
			if( weight <= selectedWeight[j] )
			{
				break;
			}

			selectedIdx[j+1] = selectedIdx[j];
			selectedWeight[j+1] = selectedWeight[j];
		}

		selectedIdx[j+1] = i;
		selectedWeight[j+1] = weight;
	}

	i = 0;

	if( haveGlobal )
	{
		trap_SetColorGrading( 1, cgs.gameGradingTextures[0] );
		i = 1;
	}

	for(; i < 3; i++ )
	{
		if( selectedWeight[i] > 0.0f )
		{
			trap_SetColorGrading( i + 1, cgs.gameGradingTextures[selectedIdx[i]] );
			totalWeight += selectedWeight[i];
		}
		else
		{
			freeSlot = i;
		}
	}

	if( !haveGlobal && totalWeight < 1.0f )
	{
		if(freeSlot >= 0)
		{
			//If there is a free slot, use it with the neutral cgrade
			//to make sure that using only the 3 map grade will always be ok
			trap_SetColorGrading( freeSlot + 1, cgs.media.neutralCgrade);
			selectedWeight[freeSlot] = 1.0f - totalWeight;
			totalWeight = 1.0f;
		}
	}

	cg.refdef.gradingWeights[0] = 0.0f;
	cg.refdef.gradingWeights[1] = haveGlobal ? ( 1.0f - totalWeight ) : ( selectedWeight[0] / totalWeight );
	cg.refdef.gradingWeights[2] = totalWeight == 0.0f ? 0.0f : selectedWeight[1] / totalWeight;
	cg.refdef.gradingWeights[3] = totalWeight == 0.0f ? 0.0f : selectedWeight[2] / totalWeight;
}
float RB_EvalExpression(const expression_t * exp, float defaultValue)
{
#if 1
	int             i;
	expOperation_t  op;
	expOperation_t  ops[MAX_EXPRESSION_OPS];
	int             numOps;
	float           value;
	float           value1;
	float           value2;
	extern const opstring_t opStrings[];

	numOps = 0;
	value = 0;
	value1 = 0;
	value2 = 0;

	if(!exp || !exp->active)
	{
		return defaultValue;
	}

	// http://www.qiksearch.com/articles/cs/postfix-evaluation/
	// http://www.kyz.uklinux.net/evaluate/

	for(i = 0; i < exp->numOps; i++)
	{
		op = exp->ops[i];

		switch (op.type)
		{
			case OP_BAD:
				return defaultValue;

			case OP_NEG:
			{
				if(numOps < 1)
				{
					ri.Printf(PRINT_ALL, "WARNING: shader %s has numOps < 1 for unary - operator\n", tess.surfaceShader->name);
					return defaultValue;
				}

				value1 = GetOpValue(&ops[numOps - 1]);
				numOps--;

				value = -value1;

				// push result
				op.type = OP_NUM;
				op.value = value;
				ops[numOps++] = op;
				break;
			}

			case OP_NUM:
			case OP_TIME:
			case OP_PARM0:
			case OP_PARM1:
			case OP_PARM2:
			case OP_PARM3:
			case OP_PARM4:
			case OP_PARM5:
			case OP_PARM6:
			case OP_PARM7:
			case OP_PARM8:
			case OP_PARM9:
			case OP_PARM10:
			case OP_PARM11:
			case OP_GLOBAL0:
			case OP_GLOBAL1:
			case OP_GLOBAL2:
			case OP_GLOBAL3:
			case OP_GLOBAL4:
			case OP_GLOBAL5:
			case OP_GLOBAL6:
			case OP_GLOBAL7:
			case OP_FRAGMENTSHADERS:
			case OP_FRAMEBUFFEROBJECTS:
			case OP_SOUND:
			case OP_DISTANCE:
				ops[numOps++] = op;
				break;

			case OP_TABLE:
			{
				shaderTable_t  *table;
				int             numValues;
				float           index;
				float           lerp;
				int             oldIndex;
				int             newIndex;

				if(numOps < 1)
				{
					ri.Printf(PRINT_ALL, "WARNING: shader %s has numOps < 1 for table operator\n", tess.surfaceShader->name);
					return defaultValue;
				}

				value1 = GetOpValue(&ops[numOps - 1]);
				numOps--;

				table = tr.shaderTables[(int)op.value];

				numValues = table->numValues;

				index = value1 * numValues;	// float index into the table?s elements
				lerp = index - floor(index);	// being inbetween two elements of the table

				oldIndex = (int)index;
				newIndex = (int)index + 1;

				if(table->clamp)
				{
					// clamp indices to table-range
					Q_clamp(oldIndex, 0, numValues - 1);
					Q_clamp(newIndex, 0, numValues - 1);
				}
				else
				{
					// wrap around indices
					oldIndex %= numValues;
					newIndex %= numValues;
				}

				if(table->snap)
				{
					// use fixed value
					value = table->values[oldIndex];
				}
				else
				{
					// lerp value
					value = table->values[oldIndex] + ((table->values[newIndex] - table->values[oldIndex]) * lerp);
				}

				//ri.Printf(PRINT_ALL, "%s: %i %i %f\n", table->name, oldIndex, newIndex, value);

				// push result
				op.type = OP_NUM;
				op.value = value;
				ops[numOps++] = op;
				break;
			}

			default:
			{
				if(numOps < 2)
				{
					ri.Printf(PRINT_ALL, "WARNING: shader %s has numOps < 2 for binary operator %s\n", tess.surfaceShader->name,
							  opStrings[op.type].s);
					return defaultValue;
				}

				value2 = GetOpValue(&ops[numOps - 1]);
				numOps--;

				value1 = GetOpValue(&ops[numOps - 1]);
				numOps--;

				switch (op.type)
				{
					case OP_LAND:
						value = value1 && value2;
						break;

					case OP_LOR:
						value = value1 || value2;
						break;

					case OP_GE:
						value = value1 >= value2;
						break;

					case OP_LE:
						value = value1 <= value2;
						break;

					case OP_LEQ:
						value = value1 == value2;
						break;

					case OP_LNE:
						value = value1 != value2;
						break;

					case OP_ADD:
						value = value1 + value2;
						break;

					case OP_SUB:
						value = value1 - value2;
						break;

					case OP_DIV:
						if(value2 == 0)
						{
							// don't divide by zero
							value = value1;
						}
						else
						{
							value = value1 / value2;
						}
						break;

					case OP_MOD:
						value = (float)((int)value1 % (int)value2);
						break;

					case OP_MUL:
						value = value1 * value2;
						break;

					case OP_LT:
						value = value1 < value2;
						break;

					case OP_GT:
						value = value1 > value2;
						break;

					default:
						value = value1 = value2 = 0;
						break;
				}

				//ri.Printf(PRINT_ALL, "%s: %f %f %f\n", opStrings[op.type].s, value, value1, value2);

				// push result
				op.type = OP_NUM;
				op.value = value;
				ops[numOps++] = op;
				break;
			}
		}
	}

	return GetOpValue(&ops[0]);
#else
	return defaultValue;
#endif
}
Exemple #7
0
/*
 * UI_SPSkillMenu_Init
 */
static void
UI_SPSkillMenu_Init(void)
{
	int skill;

	memset(&skillMenuInfo, 0, sizeof(skillMenuInfo));
	skillMenuInfo.menu.fullscreen = qtrue;
	skillMenuInfo.menu.key = UI_SPSkillMenu_Key;

	UI_SPSkillMenu_Cache();

	skillMenuInfo.art_frame.generic.type = MTYPE_BITMAP;
	skillMenuInfo.art_frame.generic.name = ART_FRAME;
	skillMenuInfo.art_frame.generic.flags	= QMF_LEFT_JUSTIFY|QMF_INACTIVE;
	skillMenuInfo.art_frame.generic.x	= 142;
	skillMenuInfo.art_frame.generic.y	= 118;
	skillMenuInfo.art_frame.width	= 359;
	skillMenuInfo.art_frame.height	= 256;

	skillMenuInfo.art_banner.generic.type	= MTYPE_BTEXT;
	skillMenuInfo.art_banner.generic.flags	= QMF_CENTER_JUSTIFY;
	skillMenuInfo.art_banner.generic.x	= 320;
	skillMenuInfo.art_banner.generic.y	= 16;
	skillMenuInfo.art_banner.string = "DIFFICULTY";
	skillMenuInfo.art_banner.color	= color_white;
	skillMenuInfo.art_banner.style	= UI_CENTER;

	skillMenuInfo.item_baby.generic.type = MTYPE_PTEXT;
	skillMenuInfo.item_baby.generic.flags = QMF_CENTER_JUSTIFY|
						QMF_PULSEIFFOCUS;
	skillMenuInfo.item_baby.generic.x = 320;
	skillMenuInfo.item_baby.generic.y = 170;
	skillMenuInfo.item_baby.generic.callback = UI_SPSkillMenu_SkillEvent;
	skillMenuInfo.item_baby.generic.id = ID_BABY;
	skillMenuInfo.item_baby.string	= "I Can Win";
	skillMenuInfo.item_baby.color	= color_red;
	skillMenuInfo.item_baby.style	= UI_CENTER;

	skillMenuInfo.item_easy.generic.type = MTYPE_PTEXT;
	skillMenuInfo.item_easy.generic.flags = QMF_CENTER_JUSTIFY|
						QMF_PULSEIFFOCUS;
	skillMenuInfo.item_easy.generic.x = 320;
	skillMenuInfo.item_easy.generic.y = 198;
	skillMenuInfo.item_easy.generic.callback = UI_SPSkillMenu_SkillEvent;
	skillMenuInfo.item_easy.generic.id = ID_EASY;
	skillMenuInfo.item_easy.string	= "Bring It On";
	skillMenuInfo.item_easy.color	= color_red;
	skillMenuInfo.item_easy.style	= UI_CENTER;

	skillMenuInfo.item_medium.generic.type	= MTYPE_PTEXT;
	skillMenuInfo.item_medium.generic.flags = QMF_CENTER_JUSTIFY|
						  QMF_PULSEIFFOCUS;
	skillMenuInfo.item_medium.generic.x = 320;
	skillMenuInfo.item_medium.generic.y = 227;
	skillMenuInfo.item_medium.generic.callback = UI_SPSkillMenu_SkillEvent;
	skillMenuInfo.item_medium.generic.id = ID_MEDIUM;
	skillMenuInfo.item_medium.string	= "Hurt Me Plenty";
	skillMenuInfo.item_medium.color		= color_red;
	skillMenuInfo.item_medium.style		= UI_CENTER;

	skillMenuInfo.item_hard.generic.type = MTYPE_PTEXT;
	skillMenuInfo.item_hard.generic.flags = QMF_CENTER_JUSTIFY|
						QMF_PULSEIFFOCUS;
	skillMenuInfo.item_hard.generic.x = 320;
	skillMenuInfo.item_hard.generic.y = 255;
	skillMenuInfo.item_hard.generic.callback = UI_SPSkillMenu_SkillEvent;
	skillMenuInfo.item_hard.generic.id = ID_HARD;
	skillMenuInfo.item_hard.string	= "Hardcore";
	skillMenuInfo.item_hard.color	= color_red;
	skillMenuInfo.item_hard.style	= UI_CENTER;

	skillMenuInfo.item_nightmare.generic.type = MTYPE_PTEXT;
	skillMenuInfo.item_nightmare.generic.flags = QMF_CENTER_JUSTIFY|
						     QMF_PULSEIFFOCUS;
	skillMenuInfo.item_nightmare.generic.x	= 320;
	skillMenuInfo.item_nightmare.generic.y	= 283;
	skillMenuInfo.item_nightmare.generic.callback =
		UI_SPSkillMenu_SkillEvent;
	skillMenuInfo.item_nightmare.generic.id = ID_NIGHTMARE;
	skillMenuInfo.item_nightmare.string	= "NIGHTMARE!";
	skillMenuInfo.item_nightmare.color	= color_red;
	skillMenuInfo.item_nightmare.style	= UI_CENTER;

	skillMenuInfo.item_back.generic.type = MTYPE_BITMAP;
	skillMenuInfo.item_back.generic.name = ART_BACK;
	skillMenuInfo.item_back.generic.flags = QMF_LEFT_JUSTIFY|
						QMF_PULSEIFFOCUS;
	skillMenuInfo.item_back.generic.x = 0;
	skillMenuInfo.item_back.generic.y = 480-64;
	skillMenuInfo.item_back.generic.callback = UI_SPSkillMenu_BackEvent;
	skillMenuInfo.item_back.generic.id = ID_BACK;
	skillMenuInfo.item_back.width = 128;
	skillMenuInfo.item_back.height = 64;
	skillMenuInfo.item_back.focuspic = ART_BACK_FOCUS;

	skillMenuInfo.art_skillPic.generic.type = MTYPE_BITMAP;
	skillMenuInfo.art_skillPic.generic.flags = QMF_LEFT_JUSTIFY|
						   QMF_INACTIVE;
	skillMenuInfo.art_skillPic.generic.x = 320-64;
	skillMenuInfo.art_skillPic.generic.y = 368;
	skillMenuInfo.art_skillPic.width = 128;
	skillMenuInfo.art_skillPic.height = 96;

	skillMenuInfo.item_fight.generic.type	= MTYPE_BITMAP;
	skillMenuInfo.item_fight.generic.name	= ART_FIGHT;
	skillMenuInfo.item_fight.generic.flags	= QMF_RIGHT_JUSTIFY|
						  QMF_PULSEIFFOCUS;
	skillMenuInfo.item_fight.generic.callback =
		UI_SPSkillMenu_FightEvent;
	skillMenuInfo.item_fight.generic.id	= ID_FIGHT;
	skillMenuInfo.item_fight.generic.x	= 640;
	skillMenuInfo.item_fight.generic.y	= 480-64;
	skillMenuInfo.item_fight.width		= 128;
	skillMenuInfo.item_fight.height		= 64;
	skillMenuInfo.item_fight.focuspic	= ART_FIGHT_FOCUS;

	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.art_frame);
	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.art_banner);
	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.item_baby);
	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.item_easy);
	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.item_medium);
	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.item_hard);
	Menu_AddItem(&skillMenuInfo.menu,
		( void* )&skillMenuInfo.item_nightmare);
	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.art_skillPic);
	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.item_back);
	Menu_AddItem(&skillMenuInfo.menu, ( void* )&skillMenuInfo.item_fight);

	skill = (int)Q_clamp(1, 5, trap_cvargetf("g_spSkill"));
	SetSkillColor(skill, color_white);
	skillMenuInfo.art_skillPic.shader = skillMenuInfo.skillpics[skill - 1];
	if(skill == 5)
		trap_sndstartlocalsound(skillMenuInfo.nightmareSound,
			CHAN_ANNOUNCER);
}