Ejemplo n.º 1
0
/*
========================
idSoundVoice_OpenAL::Start
========================
*/
void idSoundVoice_OpenAL::Start( int offsetMS, int ssFlags )
{
	if( s_debugHardware.GetBool() )
	{
		idLib::Printf( "%dms: %i starting %s @ %dms\n", Sys_Milliseconds(), openalSource, leadinSample ? leadinSample->GetName() : "<null>", offsetMS );
	}
	
	if( !leadinSample )
	{
		return;
	}
	
	if( !alIsSource( openalSource ) )
	{
		return;
	}
	
	if( leadinSample->IsDefault() )
	{
		idLib::Warning( "Starting defaulted sound sample %s", leadinSample->GetName() );
	}
	
	bool flicker = ( ssFlags & SSF_NO_FLICKER ) == 0;
	
	if( flicker != hasVUMeter )
	{
		hasVUMeter = flicker;
		
		/*
		if( flicker )
		{
			IUnknown* vuMeter = NULL;
		
			if( XAudio2CreateVolumeMeter( &vuMeter, 0 ) == S_OK )
			{
		
				XAUDIO2_EFFECT_DESCRIPTOR descriptor;
				descriptor.InitialState = true;
				descriptor.OutputChannels = leadinSample->NumChannels();
				descriptor.pEffect = vuMeter;
		
				XAUDIO2_EFFECT_CHAIN chain;
				chain.EffectCount = 1;
				chain.pEffectDescriptors = &descriptor;
		
				pSourceVoice->SetEffectChain( &chain );
		
				vuMeter->Release();
			}
		}
		else
		{
			pSourceVoice->SetEffectChain( NULL );
		}
		*/
	}
	
	assert( offsetMS >= 0 );
	int offsetSamples = MsecToSamples( offsetMS, leadinSample->SampleRate() );
	if( loopingSample == NULL && offsetSamples >= leadinSample->playLength )
	{
		return;
	}
	
	RestartAt( offsetSamples );
	Update();
	UnPause();
}
Ejemplo n.º 2
0
/*
========================
ConvertCG2GLSL
========================
*/
idStr ConvertCG2GLSL( const idStr& in, const char* name, bool isVertexProgram, idStr& uniforms )
{
	idStr program;
	program.ReAllocate( in.Length() * 2, false );
	
	idList< inOutVariable_t, TAG_RENDERPROG > varsIn;
	idList< inOutVariable_t, TAG_RENDERPROG > varsOut;
	idList< idStr > uniformList;
	
	idLexer src( LEXFL_NOFATALERRORS );
	src.LoadMemory( in.c_str(), in.Length(), name );
	
	bool inMain = false;
	const char* uniformArrayName = isVertexProgram ? VERTEX_UNIFORM_ARRAY_NAME : FRAGMENT_UNIFORM_ARRAY_NAME;
	char newline[128] = { "\n" };
	// GLES3 has tighter type safety, so we track whether we force all numerics to float
	bool numericsAsFloats =
#if defined( USE_GLES3 )
			true;
#else
			false;
#endif
	bool backupNumericsAsFloats = numericsAsFloats;
	bool inUniform = false;

	idToken token;
	while( src.ReadToken( &token ) )
	{
	
		// check for uniforms
		if( token == "uniform" && src.CheckTokenString( "float4" ) )
		{
			src.ReadToken( &token );
			uniformList.Append( token );
			
			// strip ': register()' from uniforms
			if( src.CheckTokenString( ":" ) )
			{
				if( src.CheckTokenString( "register" ) )
				{
					src.SkipUntilString( ";" );
				}
			}
			
			continue;
		}
		if( token == "uniform" ) {
			inUniform = true;
		}
		
		// convert the in/out structs
		if( token == "struct" )
		{
			if( src.CheckTokenString( "VS_IN" ) )
			{
				ParseInOutStruct( src, AT_VS_IN, varsIn );
				program += "\n\n";
				for( int i = 0; i < varsIn.Num(); i++ )
				{
					if( varsIn[i].declareInOut )
					{
						program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n";
					}
				}
				continue;
			}
			else if( src.CheckTokenString( "VS_OUT" ) )
			{
				ParseInOutStruct( src, AT_VS_OUT, varsOut );
				program += "\n";
				for( int i = 0; i < varsOut.Num(); i++ )
				{
					if( varsOut[i].declareInOut )
					{
						program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n";
					}
				}
				continue;
			}
			else if( src.CheckTokenString( "PS_IN" ) )
			{
				ParseInOutStruct( src, AT_PS_IN, varsIn );
				program += "\n\n";
				for( int i = 0; i < varsIn.Num(); i++ )
				{
					if( varsIn[i].declareInOut )
					{
						program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n";
					}
				}
				inOutVariable_t var;
				var.type = "vec4";
				var.nameCg = "position";
				var.nameGLSL = "gl_FragCoord";
				varsIn.Append( var );
				continue;
			}
			else if( src.CheckTokenString( "PS_OUT" ) )
			{
				ParseInOutStruct( src, AT_PS_OUT, varsOut );
				program += "\n";
				for( int i = 0; i < varsOut.Num(); i++ )
				{
					if( varsOut[i].declareInOut )
					{
						program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n";
					}
				}
				continue;
			}
		}
		
		// strip 'static'
		if( token == "static" )
		{
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			src.SkipWhiteSpace( true ); // remove white space between 'static' and the next token
			continue;
		}
		
		// strip ': register()' from uniforms
		if( token == ":" )
		{
			if( src.CheckTokenString( "register" ) )
			{
				src.SkipUntilString( ";" );
				program += ";";
				continue;
			}
		}
		
		// strip in/program parameters from main
		if( token == "void" && src.CheckTokenString( "main" ) )
		{
			src.ExpectTokenString( "(" );
			while( src.ReadToken( &token ) )
			{
				if( token == ")" )
				{
					break;
				}
			}
			
			program += "\nvoid main()";
			inMain = true;
			continue;
		}
		
		// strip 'const' from local variables in main()
		if( token == "const" && inMain )
		{
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			src.SkipWhiteSpace( true ); // remove white space between 'const' and the next token
			continue;
		}
		
		// maintain indentation
		if( token == "{" )
		{
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			program += "{";
			
			int len = Min( idStr::Length( newline ) + 1, ( int )sizeof( newline ) - 1 );
			newline[len - 1] = '\t';
			newline[len - 0] = '\0';
			continue;
		}
		if( token == "}" )
		{
			int len = Max( idStr::Length( newline ) - 1, 0 );
			newline[len] = '\0';
			
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			program += "}";
			continue;
		}
		// if we force numerics to floats we have to special case things inside []
		if( numericsAsFloats && inUniform ) {
			if( token == "[" ) {
				backupNumericsAsFloats = numericsAsFloats;
				numericsAsFloats = false;
			}
		} else {
			if( token == "]" ) {
				numericsAsFloats = backupNumericsAsFloats;
			}
		}
		
		// check for a type conversion
		bool foundType = false;
		for( int i = 0; typeConversion[i].typeCG != NULL; i++ )
		{
			if( token.Cmp( typeConversion[i].typeCG ) == 0 )
			{
				program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
				program += typeConversion[i].typeGLSL;
				foundType = true;
				break;
			}
		}
		if( foundType )
		{
			continue;
		}

		if( r_useUniformArrays.GetBool() )
		{
			// check for uniforms that need to be converted to the array
			bool isUniform = false;
			for( int i = 0; i < uniformList.Num(); i++ )
			{
				if( token == uniformList[i] )
				{
					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
					program += va( "%s[%d /* %s */]", uniformArrayName, i, uniformList[i].c_str() );
					isUniform = true;
					break;
				}
			}
			if( isUniform )
			{
				continue;
			}
		}
		// check for input/output parameters
		if( src.CheckTokenString( "." ) )
		{
		
			if( token == "vertex" || token == "fragment" )
			{
				idToken member;
				src.ReadToken( &member );
				
				bool foundInOut = false;
				for( int i = 0; i < varsIn.Num(); i++ )
				{
					if( member.Cmp( varsIn[i].nameCg ) == 0 )
					{
						program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
						program += varsIn[i].nameGLSL;
						foundInOut = true;
						break;
					}
				}
				if( !foundInOut )
				{
					src.Error( "invalid input parameter %s.%s", token.c_str(), member.c_str() );
					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
					program += token;
					program += ".";
					program += member;
				}
				continue;
			}
			
			if( token == "result" )
			{
				idToken member;
				src.ReadToken( &member );
				
				bool foundInOut = false;
				for( int i = 0; i < varsOut.Num(); i++ )
				{
					if( member.Cmp( varsOut[i].nameCg ) == 0 )
					{
						program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
						program += varsOut[i].nameGLSL;
						foundInOut = true;
						break;
					}
				}
				if( !foundInOut )
				{
					src.Error( "invalid output parameter %s.%s", token.c_str(), member.c_str() );
					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
					program += token;
					program += ".";
					program += member;
				}
				continue;
			}
			
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			program += token;
			program += ".";
			continue;
		}
		
		// check for a function conversion
		bool foundFunction = false;
		for( int i = 0; builtinConversion[i].nameCG != NULL; i++ )
		{
			if( token.Cmp( builtinConversion[i].nameCG ) == 0 )
			{
				program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
				program += builtinConversion[i].nameGLSL;
				foundFunction = true;
				break;
			}
		}
		if( foundFunction )
		{
			continue;
		}

		// Deano GLES3 promote all numbers to floats
		if( token.IsNumeric() ) {
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			if (numericsAsFloats) {
				program += token.GetFloatValue();
			} else {
				program += token.GetIntValue();
			}
		} else {
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			program += token;
		}
		if( token == ";" && inUniform) {
			inUniform = false;
		}
	}
	
	idStr out;
	
	if( isVertexProgram )
	{
		out.ReAllocate( idStr::Length( vertexInsert ) + in.Length() * 2, false );
		out += vertexInsert;
	}
	else
	{
		out.ReAllocate( idStr::Length( fragmentInsert ) + in.Length() * 2, false );
		out += fragmentInsert;
	}
	
	if( uniformList.Num() > 0 )
	{
		if( r_useUniformArrays.GetBool() )
		{
			out += va( "\nuniform vec4 %s[%d];\n", uniformArrayName, uniformList.Num() );
		}
		else
		{
			out += "\n";
			for( int i = 0; i < uniformList.Num(); i++ )
			{
				out += "uniform vec4 ";
				out += uniformList[i];
				out += ";\n";
			}
		}
	}
	
	out += program;
	
	for( int i = 0; i < uniformList.Num(); i++ )
	{
		uniforms += uniformList[i];
		uniforms += "\n";
	}
	uniforms += "\n";
	
	return out;
}
Ejemplo n.º 3
0
/*
=============
R_CheckCvars

See if some cvars that we watch have changed
=============
*/
static void R_CheckCvars()
{

	// gamma stuff
	if( r_gamma.IsModified() || r_brightness.IsModified() )
	{
		r_gamma.ClearModified();
		r_brightness.ClearModified();
		R_SetColorMappings();
	}
	
	// filtering
	if( r_maxAnisotropicFiltering.IsModified() || r_useTrilinearFiltering.IsModified() || r_lodBias.IsModified() )
	{
		idLib::Printf( "Updating texture filter parameters.\n" );
		r_maxAnisotropicFiltering.ClearModified();
		r_useTrilinearFiltering.ClearModified();
		r_lodBias.ClearModified();
		for( int i = 0 ; i < globalImages->images.Num() ; i++ )
		{
			if( globalImages->images[i] )
			{
				globalImages->images[i]->Bind();
				globalImages->images[i]->SetTexParameters();
			}
		}
	}
	
	extern idCVar r_useSeamlessCubeMap;
	if( r_useSeamlessCubeMap.IsModified() )
	{
		r_useSeamlessCubeMap.ClearModified();
		if( glConfig.seamlessCubeMapAvailable )
		{
			if( r_useSeamlessCubeMap.GetBool() )
			{
				qglEnable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
			}
			else
			{
				qglDisable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
			}
		}
	}
	
	extern idCVar r_useSRGB;
	if( r_useSRGB.IsModified() )
	{
		r_useSRGB.ClearModified();
		if( glConfig.sRGBFramebufferAvailable )
		{
			if( r_useSRGB.GetBool() )
			{
				qglEnable( GL_FRAMEBUFFER_SRGB );
			}
			else
			{
				qglDisable( GL_FRAMEBUFFER_SRGB );
			}
		}
	}
	
	
	if( r_multiSamples.IsModified() )
	{
		if( r_multiSamples.GetInteger() > 0 )
		{
			qglEnable( GL_MULTISAMPLE_ARB );
		}
		else
		{
			qglDisable( GL_MULTISAMPLE_ARB );
		}
	}
	
	// check for changes to logging state
	GLimp_EnableLogging( r_logFile.GetInteger() != 0 );
}
Ejemplo n.º 4
0
/*
==========================
Posix_PollInput
==========================
*/
void Posix_PollInput() {
	static char buf[16];
	static XEvent event;
	static XKeyEvent *key_event = (XKeyEvent*)&event;
	int lookupRet;
	int b, dx, dy;
	KeySym keysym;

	if ( !dpy ) {
		return;
	}

	// NOTE: Sys_GetEvent only calls when there are no events left
	// but here we pump all X events that have accumulated
	// pump one by one? or use threaded input?
	while ( XPending( dpy ) ) {
		XNextEvent( dpy, &event );
		switch (event.type) {
			case KeyPress:
				#ifdef XEVT_DBG
				if (key_event->keycode > 0x7F)
					common->DPrintf("WARNING: KeyPress keycode > 0x7F");
				#endif
				key_event->keycode &= 0x7F;
				#ifdef XEVT_DBG2
					printf("SE_KEY press %d\n", key_event->keycode);
				#endif
				Posix_QueEvent( SE_KEY, s_scantokey[key_event->keycode], true, 0, NULL);
				lookupRet = XLookupString(key_event, buf, sizeof(buf), &keysym, NULL);
				if (lookupRet > 0) {
					char s = buf[0];
					#ifdef XEVT_DBG
						if (buf[1]!=0)
							common->DPrintf("WARNING: got XLookupString buffer '%s' (%d)\n", buf, strlen(buf));
					#endif
					#ifdef XEVT_DBG2
						printf("SE_CHAR %s\n", buf);
					#endif
					Posix_QueEvent( SE_CHAR, s, 0, 0, NULL);
				}
				if (!Posix_AddKeyboardPollEvent( s_scantokey[key_event->keycode], true ))
					return;
			break;

			case KeyRelease:
				if (Sys_XRepeatPress(&event)) {
					#ifdef XEVT_DBG2
						printf("RepeatPress\n");
					#endif
					continue;
				}
				#ifdef XEVT_DBG
				if (key_event->keycode > 0x7F)
					common->DPrintf("WARNING: KeyRelease keycode > 0x7F");
				#endif
				key_event->keycode &= 0x7F;
				#ifdef XEVT_DBG2
					printf("SE_KEY release %d\n", key_event->keycode);
				#endif
				Posix_QueEvent( SE_KEY, s_scantokey[key_event->keycode], false, 0, NULL);
				if (!Posix_AddKeyboardPollEvent( s_scantokey[key_event->keycode], false ))
					return;
			break;

			case ButtonPress:
				if (event.xbutton.button == 4) {
					Posix_QueEvent( SE_KEY, K_MWHEELUP, true, 0, NULL);
					if (!Posix_AddMousePollEvent( M_DELTAZ, 1 ))
						return;
				} else if (event.xbutton.button == 5) {
					Posix_QueEvent( SE_KEY, K_MWHEELDOWN, true, 0, NULL);
					if (!Posix_AddMousePollEvent( M_DELTAZ, -1 ))
						return;
				} else {
					b = -1;
					if (event.xbutton.button == 1) {
						b = 0;		// K_MOUSE1
					} else if (event.xbutton.button == 2) {
						b = 2;		// K_MOUSE3
					} else if (event.xbutton.button == 3) {
						b = 1;		// K_MOUSE2
					} else if (event.xbutton.button == 6) {
						b = 3;		// K_MOUSE4
					} else if (event.xbutton.button == 7) {
						b = 4;		// K_MOUSE5
					}
					if (b == -1 || b > 4) {
						common->DPrintf("X ButtonPress %d not supported\n", event.xbutton.button);
					} else {
						Posix_QueEvent( SE_KEY, K_MOUSE1 + b, true, 0, NULL);
						if (!Posix_AddMousePollEvent( M_ACTION1 + b, true ))
							return;
					}
				}
			break;

			case ButtonRelease:
				if (event.xbutton.button == 4) {
					Posix_QueEvent( SE_KEY, K_MWHEELUP, false, 0, NULL);
				} else if (event.xbutton.button == 5) {
					Posix_QueEvent( SE_KEY, K_MWHEELDOWN, false, 0, NULL);
				} else {
					b = -1;
					if (event.xbutton.button == 1) {
						b = 0;
					} else if (event.xbutton.button == 2) {
						b = 2;
					} else if (event.xbutton.button == 3) {
						b = 1;
					} else if (event.xbutton.button == 6) {
						b = 3;		// K_MOUSE4
					} else if (event.xbutton.button == 7) {
						b = 4;		// K_MOUSE5
					}
					if (b == -1 || b > 4) {
						common->DPrintf("X ButtonRelease %d not supported\n", event.xbutton.button);
					} else {
						Posix_QueEvent( SE_KEY, K_MOUSE1 + b, false, 0, NULL);
						if (!Posix_AddMousePollEvent( M_ACTION1 + b, false ))
							return;
					}
				}
			break;

			case MotionNotify:
				if (!mouse_active)
					break;
				if (in_dgamouse.GetBool()) {
					dx = event.xmotion.x_root;
					dy = event.xmotion.y_root;

					Posix_QueEvent( SE_MOUSE, dx, dy, 0, NULL);

					// if we overflow here, we'll get a warning, but the delta will be completely processed anyway
					Posix_AddMousePollEvent( M_DELTAX, dx );
					if (!Posix_AddMousePollEvent( M_DELTAY, dy ))
						return;
				} else {
					// if it's a center motion, we've just returned from our warp
					// FIXME: we generate mouse delta on wrap return, but that lags us quite a bit from the initial event..
					if (event.xmotion.x == glConfig.vidWidth / 2 &&
						event.xmotion.y == glConfig.vidHeight / 2) {
						mwx = glConfig.vidWidth / 2;
						mwy = glConfig.vidHeight / 2;

						Posix_QueEvent( SE_MOUSE, mx, my, 0, NULL);

						Posix_AddMousePollEvent( M_DELTAX, mx );
						if (!Posix_AddMousePollEvent( M_DELTAY, my ))
							return;
						mx = my = 0;
						break;
					}

					dx = ((int) event.xmotion.x - mwx);
					dy = ((int) event.xmotion.y - mwy);
					mx += dx;
					my += dy;

					mwx = event.xmotion.x;
					mwy = event.xmotion.y;
					XWarpPointer(dpy,None,win,0,0,0,0, (glConfig.vidWidth/2),(glConfig.vidHeight/2));
				}
			break;
		}
	}
}
Ejemplo n.º 5
0
/*
================================================================================================
idRenderProgManager::LoadGLSLProgram
================================================================================================
*/
void idRenderProgManager::LoadGLSLProgram( const int programIndex, const int vertexShaderIndex, const int fragmentShaderIndex )
{
	glslProgram_t& prog = glslPrograms[programIndex];
	
	if( prog.progId != INVALID_PROGID )
	{
		return; // Already loaded
	}
	
	GLuint vertexProgID = ( vertexShaderIndex != -1 ) ? vertexShaders[ vertexShaderIndex ].progId : INVALID_PROGID;
	GLuint fragmentProgID = ( fragmentShaderIndex != -1 ) ? fragmentShaders[ fragmentShaderIndex ].progId : INVALID_PROGID;
	
	const GLuint program = qglCreateProgram();
	GL_DBG_CHK
	if( program )
	{
	
		if( vertexProgID != INVALID_PROGID )
		{
			qglAttachShader( program, vertexProgID );
		}
		
		if( fragmentProgID != INVALID_PROGID )
		{
			qglAttachShader( program, fragmentProgID );
		}
		
		// bind vertex attribute locations
		for( int i = 0; attribsPC[i].glsl != NULL; i++ )
		{
			if( ( attribsPC[i].flags & AT_VS_IN ) != 0 )
			{
				qglBindAttribLocation( program, attribsPC[i].bind, attribsPC[i].glsl );
			}
		}
		
		qglLinkProgram( program );
		
		int infologLength = 0;
		qglGetProgramiv( program, GL_INFO_LOG_LENGTH, &infologLength );
		if( infologLength > 1 )
		{
			char* infoLog = ( char* )malloc( infologLength );
			int charsWritten = 0;
			qglGetProgramInfoLog( program, infologLength, &charsWritten, infoLog );
			
			// catch the strings the ATI and Intel drivers output on success
			if( strstr( infoLog, "Vertex shader(s) linked, fragment shader(s) linked." ) != NULL || strstr( infoLog, "No errors." ) != NULL )
			{
				//idLib::Printf( "render prog %s from %s linked\n", GetName(), GetFileName() );
			}
			else
			{
				idLib::Printf( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n",
							   programIndex,
							   ( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>",
							   ( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" );
				idLib::Printf( "%s\n", infoLog );
			}
			
			free( infoLog );
		}
	}
	
	int linked = GL_FALSE;
	qglGetProgramiv( program, GL_LINK_STATUS, &linked );
	if( linked == GL_FALSE )
	{
		qglDeleteProgram( program );
		idLib::Error( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n",
					  programIndex,
					  ( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>",
					  ( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" );
		return;
	}
	
	if( r_useUniformArrays.GetBool() )
	{
		prog.vertexUniformArray = qglGetUniformLocation( program, VERTEX_UNIFORM_ARRAY_NAME );
		prog.fragmentUniformArray = qglGetUniformLocation( program, FRAGMENT_UNIFORM_ARRAY_NAME );
		
		assert( prog.vertexUniformArray != -1 || vertexShaderIndex < 0 || vertexShaders[vertexShaderIndex].uniforms.Num() == 0 );
		assert( prog.fragmentUniformArray != -1 || fragmentShaderIndex < 0 || fragmentShaders[fragmentShaderIndex].uniforms.Num() == 0 );
	}
	else
	{
		// store the uniform locations after we have linked the GLSL program
		prog.uniformLocations.Clear();
		for( int i = 0; i < RENDERPARM_TOTAL; i++ )
		{
			const char* parmName = GetGLSLParmName( i );
			GLint loc = qglGetUniformLocation( program, parmName );
			if( loc != -1 )
			{
				glslUniformLocation_t uniformLocation;
				uniformLocation.parmIndex = i;
				uniformLocation.uniformIndex = loc;
				prog.uniformLocations.Append( uniformLocation );
			}
		}
		
		// store the USER uniform locations
		for( int i = 0; i < MAX_GLSL_USER_PARMS; i++ )
		{
			const char* parmName = GetGLSLParmName( RENDERPARM_USER + i );
			GLint loc = qglGetUniformLocation( program, parmName );
			if( loc != -1 )
			{
				glslUniformLocation_t uniformLocation;
				uniformLocation.parmIndex = RENDERPARM_USER + i;
				uniformLocation.uniformIndex = loc;
				prog.uniformLocations.Append( uniformLocation );
			}
		}
		
		// sort the uniforms based on index
		prog.uniformLocations.SortWithTemplate( idSort_QuickUniforms() );
	}
	
	// get the uniform buffer binding for skinning joint matrices
	GLint blockIndex = qglGetUniformBlockIndex( program, "matrices_ubo" );
	if( blockIndex != -1 )
	{
		qglUniformBlockBinding( program, blockIndex, 0 );
	}
	
	// set the texture unit locations once for the render program. We only need to do this once since we only link the program once
	qglUseProgram( program );
	for( int i = 0; i < MAX_PROG_TEXTURE_PARMS; ++i )
	{
		GLint loc = qglGetUniformLocation( program, va( "samp%d", i ) );
		if( loc != -1 )
		{
			qglUniform1i( loc, i );
		}
	}
	
	idStr programName = vertexShaders[ vertexShaderIndex ].name;
	programName.StripFileExtension();
	prog.name = programName;
	prog.progId = program;
	prog.fragmentShaderIndex = fragmentShaderIndex;
	prog.vertexShaderIndex = vertexShaderIndex;
}
/*
========================
idMenuScreen_Shell_Bindings::UpdateBindingDisplay
========================
*/
void idMenuScreen_Shell_Bindings::UpdateBindingDisplay()
{

	idList< idList< idStr, TAG_IDLIB_LIST_MENU >, TAG_IDLIB_LIST_MENU > bindList;
	
	for( int i = 0; i < numBinds; ++i )
	{
		idList< idStr > option;
		
		option.Append( keyboardBinds[i].display );
		
		if( ( idStr::Icmp( keyboardBinds[i].bind, "" ) != 0 ) )
		{
			keyBindings_t bind = idKeyInput::KeyBindingsFromBinding( keyboardBinds[i].bind, false, true );
			
			idStr bindings;
			
			if( !bind.gamepad.IsEmpty() && in_useJoystick.GetBool() )
			{
				idStrList joyBinds;
				int start = 0;
				while( start < bind.gamepad.Length() )
				{
					int end = bind.gamepad.Find( ", ", true, start );
					if( end < 0 )
					{
						end = bind.gamepad.Length();
					}
					joyBinds.Alloc().CopyRange( bind.gamepad, start, end );
					start = end + 2;
				}
				const char* buttonsWithImages[] =
				{
					"JOY1", "JOY2", "JOY3", "JOY4", "JOY5", "JOY6",
					"JOY_TRIGGER1", "JOY_TRIGGER2", 0
				};
				for( int i = 0; i < joyBinds.Num(); i++ )
				{
					if( joyBinds[i].Icmpn( "JOY_STICK", 9 ) == 0 )
					{
						continue; // Can't rebind the sticks, so don't even show them
					}
					bool hasImage = false;
					for( const char** b = buttonsWithImages; *b != 0; b++ )
					{
						if( joyBinds[i].Icmp( *b ) == 0 )
						{
							hasImage = true;
							break;
						}
					}
					if( !bindings.IsEmpty() )
					{
						bindings.Append( ", " );
					}
					if( hasImage )
					{
						bindings.Append( '<' );
						bindings.Append( joyBinds[i] );
						bindings.Append( '>' );
					}
					else
					{
						bindings.Append( joyBinds[i] );
					}
				}
				bindings.Replace( "JOY_DPAD", "DPAD" );
			}
			
			if( !bind.keyboard.IsEmpty() )
			{
				if( !bindings.IsEmpty() )
				{
					bindings.Append( ", " );
				}
				bindings.Append( bind.keyboard );
			}
			
			if( !bind.mouse.IsEmpty() )
			{
				if( !bindings.IsEmpty() )
				{
					bindings.Append( ", " );
				}
				bindings.Append( bind.mouse );
			}
			
			bindings.ToUpper();
			option.Append( bindings );
			
		}
		else
		{
			option.Append( "" );
		}
		
		bindList.Append( option );
	}
	
	options->SetListData( bindList );
	
}
Ejemplo n.º 7
0
/*
========================
idCommonLocal::ProcessSnapshot
========================
*/
void idCommonLocal::ProcessSnapshot( idSnapShot& ss )
{
	int time = Sys_Milliseconds();
	
	snapTime = time;
	snapPrevious			= snapCurrent;
	snapCurrent.serverTime	= ss.GetTime();
	snapRate = snapCurrent.serverTime - snapPrevious.serverTime;
	
	
	static int lastReceivedLocalTime = 0;
	int timeSinceLastSnap = ( time - lastReceivedLocalTime );
	if( net_debug_snapShotTime.GetBool() )
	{
		idLib::Printf( "^2ProcessSnapshot. delta serverTime: %d  delta localTime: %d \n", ( snapCurrent.serverTime - snapPrevious.serverTime ), timeSinceLastSnap );
	}
	lastReceivedLocalTime = time;
	
	/* JAF ?
	for ( int i = 0; i < MAX_PLAYERS; i++ ) {
		idBitMsg msg;
		if ( ss.GetObjectMsgByID( idSession::SS_PLAYER + i, msg ) ) {
			if ( msg.GetSize() == 0 ) {
				snapCurrent.players[ i ].valid = false;
				continue;
			}
	
			idSerializer ser( msg, false );
			SerializePlayer( ser, snapCurrent.players[ i ] );
			snapCurrent.players[ i ].valid = true;
	
			extern idCVar com_drawSnapshots;
			if ( com_drawSnapshots.GetInteger() == 3 ) {
				console->AddSnapObject( "players", msg.GetSize(), ss.CompareObject( &oldss, idSession::SS_PLAYER + i ) );
			}
		}
	}
	*/
	
	// Read usercmds from other players
	for( int p = 0; p < MAX_PLAYERS; p++ )
	{
		if( p == game->GetLocalClientNum() )
		{
			continue;
		}
		idBitMsg msg;
		if( ss.GetObjectMsgByID( SNAP_USERCMDS + p, msg ) )
		{
			NetReadUsercmds( p, msg );
		}
	}
	
	
	
	
	// Set server game time here so that it accurately reflects the time when this frame was saved out, in case any serialize function needs it.
	int oldTime = Game()->GetServerGameTimeMs();
	Game()->SetServerGameTimeMs( snapCurrent.serverTime );
	
	Game()->ClientReadSnapshot( ss ); //, &oldss );
	
	// Restore server game time
	Game()->SetServerGameTimeMs( oldTime );
	
	snapTimeDelta = ss.GetRecvTime() - oldss.GetRecvTime();
	oldss = ss;
}
Ejemplo n.º 8
0
/*
========================
idSessionLocalCallbacks::HandlePeerMatchParamUpdate
========================
*/
void idSessionLocalCallbacks::HandlePeerMatchParamUpdate( int peer, int msg ) {
	if ( net_headlessServer.GetBool() ) {
		sessionLocal->storedPeer = peer;
		sessionLocal->storedMsgType = msg;
	}
}
Ejemplo n.º 9
0
/*
=================
idUsercmdGenLocal::MouseMove
=================
*/
void idUsercmdGenLocal::MouseMove( void ) {
	float		mx, my, strafeMx, strafeMy;
	static int	history[8][2];
	static int	historyCounter;
	int			i;

	history[historyCounter&7][0] = mouseDx;
	history[historyCounter&7][1] = mouseDy;
	
	// allow mouse movement to be smoothed together
	int smooth = m_smooth.GetInteger();
	if ( smooth < 1 ) {
		smooth = 1;
	}
	if ( smooth > 8 ) {
		smooth = 8;
	}
	mx = 0;
	my = 0;
	for ( i = 0 ; i < smooth ; i++ ) {
		mx += history[ ( historyCounter - i + 8 ) & 7 ][0];
		my += history[ ( historyCounter - i + 8 ) & 7 ][1];
	}
	mx /= smooth;
	my /= smooth;

	// use a larger smoothing for strafing
	smooth = m_strafeSmooth.GetInteger();
	if ( smooth < 1 ) {
		smooth = 1;
	}
	if ( smooth > 8 ) {
		smooth = 8;
	}
	strafeMx = 0;
	strafeMy = 0;
	for ( i = 0 ; i < smooth ; i++ ) {
		strafeMx += history[ ( historyCounter - i + 8 ) & 7 ][0];
		strafeMy += history[ ( historyCounter - i + 8 ) & 7 ][1];
	}
	strafeMx /= smooth;
	strafeMy /= smooth;

	historyCounter++;

	if ( idMath::Fabs( mx ) > 1000 || idMath::Fabs( my ) > 1000 ) {
		Sys_DebugPrintf( "idUsercmdGenLocal::MouseMove: Ignoring ridiculous mouse delta.\n" );
		mx = my = 0;
	}

	mx *= sensitivity.GetFloat();
	my *= sensitivity.GetFloat();

	if ( m_showMouseRate.GetBool() ) {
		Sys_DebugPrintf( "[%3i %3i  = %5.1f %5.1f = %5.1f %5.1f]\n", mouseDx, mouseDy, mx, my, strafeMx, strafeMy );
	}

	mouseDx = 0;
	mouseDy = 0;

	if ( !strafeMx && !strafeMy ) {
		return;
	}

	if ( ButtonState( UB_STRAFE ) || !( cmd.buttons & BUTTON_MLOOK ) ) {
		// add mouse X/Y movement to cmd
		strafeMx *= m_strafeScale.GetFloat();
		strafeMy *= m_strafeScale.GetFloat();
		// clamp as a vector, instead of separate floats
		float len = sqrt( strafeMx * strafeMx + strafeMy * strafeMy );
		if ( len > 127 ) {
			strafeMx = strafeMx * 127 / len;
			strafeMy = strafeMy * 127 / len;
		}
	}

	if ( !ButtonState( UB_STRAFE ) ) {
		viewangles[YAW] -= m_yaw.GetFloat() * mx;
	} else {
		cmd.rightmove = idMath::ClampChar( (int)(cmd.rightmove + strafeMx) );
	}

	if ( !ButtonState( UB_STRAFE ) && ( cmd.buttons & BUTTON_MLOOK ) ) {
		viewangles[PITCH] += m_pitch.GetFloat() * my;
	} else {
		cmd.forwardmove = idMath::ClampChar( (int)(cmd.forwardmove - strafeMy) );
	}
}
Ejemplo n.º 10
0
/*
============
idLCP_Square::Solve
============
*/
bool idLCP_Square::Solve(const idMatX &o_m, idVecX &o_x, const idVecX &o_b, const idVecX &o_lo, const idVecX &o_hi, const int *o_boxIndex)
{
	int i, j, n, limit, limitSide, boxStartIndex;
	float dir, maxStep, dot, s;
	char *failed;

	// true when the matrix rows are 16 byte padded
	padded = ((o_m.GetNumRows()+3)&~3) == o_m.GetNumColumns();

	assert(padded || o_m.GetNumRows() == o_m.GetNumColumns());
	assert(o_x.GetSize() == o_m.GetNumRows());
	assert(o_b.GetSize() == o_m.GetNumRows());
	assert(o_lo.GetSize() == o_m.GetNumRows());
	assert(o_hi.GetSize() == o_m.GetNumRows());

	// allocate memory for permuted input
	f.SetData(o_m.GetNumRows(), VECX_ALLOCA(o_m.GetNumRows()));
	a.SetData(o_b.GetSize(), VECX_ALLOCA(o_b.GetSize()));
	b.SetData(o_b.GetSize(), VECX_ALLOCA(o_b.GetSize()));
	lo.SetData(o_lo.GetSize(), VECX_ALLOCA(o_lo.GetSize()));
	hi.SetData(o_hi.GetSize(), VECX_ALLOCA(o_hi.GetSize()));

	if (o_boxIndex) {
		boxIndex = (int *)_alloca16(o_x.GetSize() * sizeof(int));
		memcpy(boxIndex, o_boxIndex, o_x.GetSize() * sizeof(int));
	} else {
		boxIndex = NULL;
	}

	// we override the const on o_m here but on exit the matrix is unchanged
	m.SetData(o_m.GetNumRows(), o_m.GetNumColumns(), const_cast<float *>(o_m[0]));
	f.Zero();
	a.Zero();
	b = o_b;
	lo = o_lo;
	hi = o_hi;

	// pointers to the rows of m
	rowPtrs = (float **) _alloca16(m.GetNumRows() * sizeof(float *));

	for (i = 0; i < m.GetNumRows(); i++) {
		rowPtrs[i] = m[i];
	}

	// tells if a variable is at the low boundary, high boundary or inbetween
	side = (int *) _alloca16(m.GetNumRows() * sizeof(int));

	// index to keep track of the permutation
	permuted = (int *) _alloca16(m.GetNumRows() * sizeof(int));

	for (i = 0; i < m.GetNumRows(); i++) {
		permuted[i] = i;
	}

	// permute input so all unbounded variables come first
	numUnbounded = 0;

	for (i = 0; i < m.GetNumRows(); i++) {
		if (lo[i] == -idMath::INFINITY && hi[i] == idMath::INFINITY) {
			if (numUnbounded != i) {
				Swap(numUnbounded, i);
			}

			numUnbounded++;
		}
	}

	// permute input so all variables using the boxIndex come last
	boxStartIndex = m.GetNumRows();

	if (boxIndex) {
		for (i = m.GetNumRows() - 1; i >= numUnbounded; i--) {
			if (boxIndex[i] >= 0 && (lo[i] != -idMath::INFINITY || hi[i] != idMath::INFINITY)) {
				boxStartIndex--;

				if (boxStartIndex != i) {
					Swap(boxStartIndex, i);
				}
			}
		}
	}

	// sub matrix for factorization
	clamped.SetData(m.GetNumRows(), m.GetNumColumns(), MATX_ALLOCA(m.GetNumRows() * m.GetNumColumns()));
	diagonal.SetData(m.GetNumRows(), VECX_ALLOCA(m.GetNumRows()));

	// all unbounded variables are clamped
	numClamped = numUnbounded;

	// if there are unbounded variables
	if (numUnbounded) {

		// factor and solve for unbounded variables
		if (!FactorClamped()) {
			idLib::common->Printf("idLCP_Square::Solve: unbounded factorization failed\n");
			return false;
		}

		SolveClamped(f, b.ToFloatPtr());

		// if there are no bounded variables we are done
		if (numUnbounded == m.GetNumRows()) {
			o_x = f;	// the vector is not permuted
			return true;
		}
	}

#ifdef IGNORE_UNSATISFIABLE_VARIABLES
	int numIgnored = 0;
#endif

	// allocate for delta force and delta acceleration
	delta_f.SetData(m.GetNumRows(), VECX_ALLOCA(m.GetNumRows()));
	delta_a.SetData(m.GetNumRows(), VECX_ALLOCA(m.GetNumRows()));

	// solve for bounded variables
	failed = NULL;

	for (i = numUnbounded; i < m.GetNumRows(); i++) {

		// once we hit the box start index we can initialize the low and high boundaries of the variables using the box index
		if (i == boxStartIndex) {
			for (j = 0; j < boxStartIndex; j++) {
				o_x[permuted[j]] = f[j];
			}

			for (j = boxStartIndex; j < m.GetNumRows(); j++) {
				s = o_x[boxIndex[j]];

				if (lo[j] != -idMath::INFINITY) {
					lo[j] = - idMath::Fabs(lo[j] * s);
				}

				if (hi[j] != idMath::INFINITY) {
					hi[j] = idMath::Fabs(hi[j] * s);
				}
			}
		}

		// calculate acceleration for current variable
		SIMDProcessor->Dot(dot, rowPtrs[i], f.ToFloatPtr(), i);
		a[i] = dot - b[i];

		// if already at the low boundary
		if (lo[i] >= -LCP_BOUND_EPSILON && a[i] >= -LCP_ACCEL_EPSILON) {
			side[i] = -1;
			continue;
		}

		// if already at the high boundary
		if (hi[i] <= LCP_BOUND_EPSILON && a[i] <= LCP_ACCEL_EPSILON) {
			side[i] = 1;
			continue;
		}

		// if inside the clamped region
		if (idMath::Fabs(a[i]) <= LCP_ACCEL_EPSILON) {
			side[i] = 0;
			AddClamped(i);
			continue;
		}

		// drive the current variable into a valid region
		for (n = 0; n < maxIterations; n++) {

			// direction to move
			if (a[i] <= 0.0f) {
				dir = 1.0f;
			} else {
				dir = -1.0f;
			}

			// calculate force delta
			CalcForceDelta(i, dir);

			// calculate acceleration delta: delta_a = m * delta_f;
			CalcAccelDelta(i);

			// maximum step we can take
			GetMaxStep(i, dir, maxStep, limit, limitSide);

			if (maxStep <= 0.0f) {
#ifdef IGNORE_UNSATISFIABLE_VARIABLES
				// ignore the current variable completely
				lo[i] = hi[i] = 0.0f;
				f[i] = 0.0f;
				side[i] = -1;
				numIgnored++;
#else
				failed = va("invalid step size %.4f", maxStep);
#endif
				break;
			}

			// change force
			ChangeForce(i, maxStep);

			// change acceleration
			ChangeAccel(i, maxStep);

			// clamp/unclamp the variable that limited this step
			side[limit] = limitSide;

			switch (limitSide) {
				case 0: {
					a[limit] = 0.0f;
					AddClamped(limit);
					break;
				}
				case -1: {
					f[limit] = lo[limit];

					if (limit != i) {
						RemoveClamped(limit);
					}

					break;
				}
				case 1: {
					f[limit] = hi[limit];

					if (limit != i) {
						RemoveClamped(limit);
					}

					break;
				}
			}

			// if the current variable limited the step we can continue with the next variable
			if (limit == i) {
				break;
			}
		}

		if (n >= maxIterations) {
			failed = va("max iterations %d", maxIterations);
			break;
		}

		if (failed) {
			break;
		}
	}

#ifdef IGNORE_UNSATISFIABLE_VARIABLES

	if (numIgnored) {
		if (lcp_showFailures.GetBool()) {
			idLib::common->Printf("idLCP_Symmetric::Solve: %d of %d bounded variables ignored\n", numIgnored, m.GetNumRows() - numUnbounded);
		}
	}

#endif

	// if failed clear remaining forces
	if (failed) {
		if (lcp_showFailures.GetBool()) {
			idLib::common->Printf("idLCP_Square::Solve: %s (%d of %d bounded variables ignored)\n", failed, m.GetNumRows() - i, m.GetNumRows() - numUnbounded);
		}

		for (j = i; j < m.GetNumRows(); j++) {
			f[j] = 0.0f;
		}
	}

#if defined(_DEBUG) && 0

	if (!failed) {
		// test whether or not the solution satisfies the complementarity conditions
		for (i = 0; i < m.GetNumRows(); i++) {
			a[i] = -b[i];

			for (j = 0; j < m.GetNumRows(); j++) {
				a[i] += rowPtrs[i][j] * f[j];
			}

			if (f[i] == lo[i]) {
				if (lo[i] != hi[i] && a[i] < -LCP_ACCEL_EPSILON) {
					int bah1 = 1;
				}
			} else if (f[i] == hi[i]) {
				if (lo[i] != hi[i] && a[i] > LCP_ACCEL_EPSILON) {
					int bah2 = 1;
				}
			} else if (f[i] < lo[i] || f[i] > hi[i] || idMath::Fabs(a[i]) > 1.0f) {
				int bah3 = 1;
			}
		}
	}

#endif

	// unpermute result
	for (i = 0; i < f.GetSize(); i++) {
		o_x[permuted[i]] = f[i];
	}

	// unpermute original matrix
	for (i = 0; i < m.GetNumRows(); i++) {
		for (j = 0; j < m.GetNumRows(); j++) {
			if (permuted[j] == i) {
				break;
			}
		}

		if (i != j) {
			m.SwapColumns(i, j);
			idSwap(permuted[i], permuted[j]);
		}
	}

	return true;
}
/*
========================
idSoundHardware_XAudio2::Update
========================
*/
void idSoundHardware_XAudio2::Update()
{
	if( pXAudio2 == NULL )
	{
		int nowTime = Sys_Milliseconds();
		if( lastResetTime + 1000 < nowTime )
		{
			lastResetTime = nowTime;
			Init();
		}
		return;
	}
	if( soundSystem->IsMuted() )
	{
		pMasterVoice->SetVolume( 0.0f, OPERATION_SET );
	}
	else
	{
		pMasterVoice->SetVolume( DBtoLinear( s_volume_dB.GetFloat() ), OPERATION_SET );
	}
	
	pXAudio2->CommitChanges( XAUDIO2_COMMIT_ALL );
	
	// IXAudio2SourceVoice::Stop() has been called for every sound on the
	// zombie list, but it is documented as asyncronous, so we have to wait
	// until it actually reports that it is no longer playing.
	for( int i = 0; i < zombieVoices.Num(); i++ )
	{
		zombieVoices[i]->FlushSourceBuffers();
		if( !zombieVoices[i]->IsPlaying() )
		{
			freeVoices.Append( zombieVoices[i] );
			zombieVoices.RemoveIndexFast( i );
			i--;
		}
		else
		{
			static int playingZombies;
			playingZombies++;
		}
	}
	
	if( s_showPerfData.GetBool() )
	{
		XAUDIO2_PERFORMANCE_DATA perfData;
		pXAudio2->GetPerformanceData( &perfData );
		idLib::Printf( "Voices: %d/%d CPU: %.2f%% Mem: %dkb\n", perfData.ActiveSourceVoiceCount, perfData.TotalSourceVoiceCount, perfData.AudioCyclesSinceLastQuery / ( float )perfData.TotalCyclesSinceLastQuery, perfData.MemoryUsageInBytes / 1024 );
	}
	
	if( vuMeterRMS == NULL )
	{
		// Init probably hasn't been called yet
		return;
	}
	
	vuMeterRMS->Enable( s_showLevelMeter.GetBool() );
	vuMeterPeak->Enable( s_showLevelMeter.GetBool() );
	
	if( !s_showLevelMeter.GetBool() )
	{
		pMasterVoice->DisableEffect( 0 );
		return;
	}
	else
	{
		pMasterVoice->EnableEffect( 0 );
	}
	
	float peakLevels[ 8 ];
	float rmsLevels[ 8 ];
	
	XAUDIO2FX_VOLUMEMETER_LEVELS levels;
	levels.ChannelCount = outputChannels;
	levels.pPeakLevels = peakLevels;
	levels.pRMSLevels = rmsLevels;
	
	if( levels.ChannelCount > 8 )
	{
		levels.ChannelCount = 8;
	}
	
	pMasterVoice->GetEffectParameters( 0, &levels, sizeof( levels ) );
	
	int currentTime = Sys_Milliseconds();
	for( int i = 0; i < outputChannels; i++ )
	{
		if( vuMeterPeakTimes[i] < currentTime )
		{
			vuMeterPeak->SetValue( i, vuMeterPeak->GetValue( i ) * 0.9f, colorRed );
		}
	}
	
	float width = 20.0f;
	float height = 200.0f;
	float left = 100.0f;
	float top = 100.0f;
	
	sscanf( s_meterPosition.GetString(), "%f %f %f %f", &left, &top, &width, &height );
	
	vuMeterRMS->SetPosition( left, top, width * levels.ChannelCount, height );
	vuMeterPeak->SetPosition( left, top, width * levels.ChannelCount, height );
	
	for( uint32 i = 0; i < levels.ChannelCount; i++ )
	{
		vuMeterRMS->SetValue( i, rmsLevels[ i ], idVec4( 0.5f, 1.0f, 0.0f, 1.00f ) );
		if( peakLevels[ i ] >= vuMeterPeak->GetValue( i ) )
		{
			vuMeterPeak->SetValue( i, peakLevels[ i ], colorRed );
			vuMeterPeakTimes[i] = currentTime + s_meterTopTime.GetInteger();
		}
	}
}
Ejemplo n.º 12
0
/*
===============
Posix_InitConsoleInput
===============
*/
void Posix_InitConsoleInput()
{
	struct termios tc;
	
	if( in_tty.GetBool() )
	{
		if( isatty( STDIN_FILENO ) != 1 )
		{
			Sys_Printf( "terminal support disabled: stdin is not a tty\n" );
			in_tty.SetBool( false );
			return;
		}
		if( tcgetattr( 0, &tty_tc ) == -1 )
		{
			Sys_Printf( "tcgetattr failed. disabling terminal support: %s\n", strerror( errno ) );
			in_tty.SetBool( false );
			return;
		}
		// make the input non blocking
		if( fcntl( STDIN_FILENO, F_SETFL, fcntl( STDIN_FILENO, F_GETFL, 0 ) | O_NONBLOCK ) == -1 )
		{
			Sys_Printf( "fcntl STDIN non blocking failed.  disabling terminal support: %s\n", strerror( errno ) );
			in_tty.SetBool( false );
			return;
		}
		tc = tty_tc;
		/*
		  ECHO: don't echo input characters
		  ICANON: enable canonical mode.  This  enables  the  special
		  	characters  EOF,  EOL,  EOL2, ERASE, KILL, REPRINT,
		  	STATUS, and WERASE, and buffers by lines.
		  ISIG: when any of the characters  INTR,  QUIT,  SUSP,  or
		  	DSUSP are received, generate the corresponding signal
		*/
		tc.c_lflag &= ~( ECHO | ICANON );
		/*
		  ISTRIP strip off bit 8
		  INPCK enable input parity checking
		*/
		tc.c_iflag &= ~( ISTRIP | INPCK );
		tc.c_cc[VMIN] = 1;
		tc.c_cc[VTIME] = 0;
		if( tcsetattr( 0, TCSADRAIN, &tc ) == -1 )
		{
			Sys_Printf( "tcsetattr failed: %s\n", strerror( errno ) );
			Sys_Printf( "terminal support may not work correctly. Use +set in_tty 0 to disable it\n" );
		}
#if 0
		// make the output non blocking
		if( fcntl( STDOUT_FILENO, F_SETFL, fcntl( STDOUT_FILENO, F_GETFL, 0 ) | O_NONBLOCK ) == -1 )
		{
			Sys_Printf( "fcntl STDOUT non blocking failed: %s\n", strerror( errno ) );
		}
#endif
		tty_enabled = true;
		// check the terminal type for the supported ones
		char* term = getenv( "TERM" );
		if( term )
		{
			if( strcmp( term, "linux" ) && strcmp( term, "xterm" ) && strcmp( term, "xterm-color" ) && strcmp( term, "screen" ) )
			{
				Sys_Printf( "WARNING: terminal type '%s' is unknown. terminal support may not work correctly\n", term );
			}
		}
		Sys_Printf( "terminal support enabled ( use +set in_tty 0 to disabled )\n" );
	}
	else
	{
		Sys_Printf( "terminal support disabled\n" );
	}
}
/*
========================
idMenuScreen_Shell_PressStart::ShowScreen
========================
*/
void idMenuScreen_Shell_PressStart::ShowScreen( const mainMenuTransition_t transitionType )
{

	idSWFScriptObject& root = GetSWFObject()->GetRootObject();
	if( BindSprite( root ) )
	{
		if( g_demoMode.GetBool() )
		{
		
			idList<const idMaterial*> coverIcons;
			if( itemList != NULL )
			{
				itemList->SetListImages( coverIcons );
			}
			
			if( startButton != NULL )
			{
				startButton->BindSprite( root );
				startButton->SetLabel( idLocalization::GetString( "#str_swf_press_start" ) );
			}
			
			idSWFSpriteInstance* backing = GetSprite()->GetScriptObject()->GetNestedSprite( "backing" );
			if( backing != NULL )
			{
				backing->SetVisible( false );
			}
			
		}
		else
		{
		
			idList<const idMaterial*> coverIcons;
			
			coverIcons.Append( doomCover );
			coverIcons.Append( doom3Cover );
			coverIcons.Append( doom2Cover );
			
			if( itemList != NULL )
			{
				itemList->SetListImages( coverIcons );
				itemList->SetFocusIndex( 1, true );
				itemList->SetViewIndex( 1 );
				itemList->SetMoveToIndex( 1 );
			}
			
			if( startButton != NULL )
			{
				startButton->BindSprite( root );
				startButton->SetLabel( "" );
			}
			
			idSWFSpriteInstance* backing = GetSprite()->GetScriptObject()->GetNestedSprite( "backing" );
			if( backing != NULL )
			{
				backing->SetVisible( true );
			}
			
		}
	}
	
	idMenuScreen::ShowScreen( transitionType );
}
Ejemplo n.º 14
0
/*
========================
idLangDict::Load
========================
*/
bool idLangDict::Load( const byte* buffer, const int bufferLen, const char* name )
{

	if( buffer == NULL || bufferLen <= 0 )
	{
		// let whoever called us deal with the failure (so sys_lang can be reset)
		return false;
	}
	
	idLib::Printf( "Reading %s", name );
	
	bool utf8 = false;
	
	// in all but retail builds, ensure that the byte-order mark is NOT MISSING so that
	// we can avoid debugging UTF-8 code
#ifndef ID_RETAIL
	utf8Encoding_t encoding = idLocalization::VerifyUTF8( buffer, bufferLen, name );
	if( encoding == UTF8_ENCODED_BOM )
	{
		utf8 = true;
	}
	else if( encoding == UTF8_PURE_ASCII )
	{
		utf8 = false;
	}
	else
	{
		assert( false );	// this should have been handled in VerifyUTF8 with a FatalError
		return false;
	}
#else
	// in release we just check the BOM so we're not scanning the lang file twice on startup
	if( bufferLen > 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF )
	{
		utf8 = true;
	}
#endif
	
	if( utf8 )
	{
		idLib::Printf( " as UTF-8\n" );
	}
	else
	{
		idLib::Printf( " as ASCII\n" );
	}
	
	idStr tempKey;
	idStr tempVal;
	
	int line = 0;
	int numStrings = 0;
	
	int i = 0;
	while( i < bufferLen )
	{
		uint32 c = buffer[i++];
		if( c == '/' )    // comment, read until new line
		{
			while( i < bufferLen )
			{
				c = buffer[i++];
				if( c == '\n' )
				{
					line++;
					break;
				}
			}
		}
		else if( c == '}' )
		{
			break;
		}
		else if( c == '\n' )
		{
			line++;
		}
		else if( c == '\"' )
		{
			int keyStart = i;
			int keyEnd = -1;
			while( i < bufferLen )
			{
				c = buffer[i++];
				if( c == '\"' )
				{
					keyEnd = i - 1;
					break;
				}
			}
			if( keyEnd < keyStart )
			{
				idLib::FatalError( "%s File ended while reading key at line %d", name, line );
			}
			tempKey.CopyRange( ( char* )buffer, keyStart, keyEnd );
			
			int valStart = -1;
			while( i < bufferLen )
			{
				c = buffer[i++];
				if( c == '\"' )
				{
					valStart = i;
					break;
				}
			}
			if( valStart < 0 )
			{
				idLib::FatalError( "%s File ended while reading value at line %d", name, line );
			}
			int valEnd = -1;
			tempVal.CapLength( 0 );
			while( i < bufferLen )
			{
				c = utf8 ? idStr::UTF8Char( buffer, i ) : buffer[i++];
				if( !utf8 && c >= 0x80 )
				{
					// this is a serious error and we must check this to avoid accidentally shipping a file where someone squased UTF-8 encodings
					idLib::FatalError( "Language file %s is supposed to be plain ASCII, but has byte values > 127!", name );
				}
				if( c == '\"' )
				{
					valEnd = i - 1;
					continue;
				}
				if( c == '\n' )
				{
					line++;
					break;
				}
				if( c == '\r' )
				{
					continue;
				}
				if( c == '\\' )
				{
					c = utf8 ? idStr::UTF8Char( buffer, i ) : buffer[i++];
					if( c == 'n' )
					{
						c = '\n';
					}
					else if( c == 't' )
					{
						c = '\t';
					}
					else if( c == '\"' )
					{
						c = '\"';
					}
					else if( c == '\\' )
					{
						c = '\\';
					}
					else
					{
						idLib::Warning( "Unknown escape sequence %x at line %d", c, line );
					}
				}
				tempVal.AppendUTF8Char( c );
			}
			if( valEnd < valStart )
			{
				idLib::FatalError( "%s File ended while reading value at line %d", name, line );
			}
			if( lang_maskLocalizedStrings.GetBool() && tempVal.Length() > 0 && tempKey.Find( "#font_" ) == -1 )
			{
				int len = tempVal.Length();
				if( len > 0 )
				{
					tempVal.Fill( 'W', len - 1 );
				}
				else
				{
					tempVal.Empty();
				}
				tempVal.Append( 'X' );
			}
			AddKeyVal( tempKey, tempVal );
			numStrings++;
		}
	}
	
	idLib::Printf( "%i strings read\n", numStrings );
	
	// get rid of any waste due to geometric list growth
	//mem.PushHeap();
	keyVals.Condense();
	//mem.PopHeap();
	
	return true;
}
Ejemplo n.º 15
0
/*
==================
FullscreenFXManager::Process
==================
*/
void FullscreenFXManager::Process( const renderView_t* view )
{
	bool allpass = false;
	bool atLeastOneFX = false;
	
	if( g_testFullscreenFX.GetInteger() == -2 )
	{
		allpass = true;
	}
	
	// do the first render
	gameRenderWorld->RenderScene( view );
	
	// we should consider these on a case-by-case basis for stereo rendering
	// double vision could be implemented "for real" by shifting the
	// eye views
	if( IsGameStereoRendered() && !player_allowScreenFXInStereo.GetBool() )
	{
		return;
	}
	
	// do the process
	for( int i = 0; i < fx.Num(); i++ )
	{
		FullscreenFX* pfx = fx[i];
		bool drawIt = false;
		
		// determine if we need to draw
		if( pfx->Active() || g_testFullscreenFX.GetInteger() == i || allpass )
		{
			drawIt = pfx->SetTriggerState( true );
		}
		else
		{
			drawIt = pfx->SetTriggerState( false );
		}
		
		// do the actual drawing
		if( drawIt )
		{
			atLeastOneFX = true;
			
			// we need to dump to _currentRender
			renderSystem->CaptureRenderToImage( "_currentRender" );
			
			// handle the accum pass if we have one
			if( pfx->HasAccum() )
			{
				// we need to crop the accum pass
				renderSystem->CropRenderSize( 512, 512 );
				pfx->AccumPass( view );
				renderSystem->CaptureRenderToImage( "_accum" );
				renderSystem->UnCrop();
			}
			
			// do the high quality pass
			pfx->HighQuality();
			
			// do the blendback
			Blendback( pfx->GetFadeAlpha() );
		}
	}
}
Ejemplo n.º 16
0
/*
=================
idRenderModelManagerLocal::GetModel
=================
*/
idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool createIfNotFound )
{

	if( !_modelName || !_modelName[0] )
	{
		return NULL;
	}
	
	idStrStatic< MAX_OSPATH > canonical = _modelName;
	canonical.ToLower();
	
	idStrStatic< MAX_OSPATH > extension;
	canonical.ExtractFileExtension( extension );
	
	// see if it is already present
	int key = hash.GenerateKey( canonical, false );
	for( int i = hash.First( key ); i != -1; i = hash.Next( i ) )
	{
		idRenderModel* model = models[i];
		
		if( canonical.Icmp( model->Name() ) == 0 )
		{
			if( !model->IsLoaded() )
			{
				// reload it if it was purged
				idStr generatedFileName = "generated/rendermodels/";
				generatedFileName.AppendPath( canonical );
				generatedFileName.SetFileExtension( va( "b%s", extension.c_str() ) );
				if( model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() )
				{
					idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
					model->PurgeModel();
					if( !model->LoadBinaryModel( file, 0 ) )
					{
						model->LoadModel();
					}
				}
				else
				{
					model->LoadModel();
				}
			}
			else if( insideLevelLoad && !model->IsLevelLoadReferenced() )
			{
				// we are reusing a model already in memory, but
				// touch all the materials to make sure they stay
				// in memory as well
				model->TouchData();
			}
			model->SetLevelLoadReferenced( true );
			return model;
		}
	}
	
	// see if we can load it
	
	// determine which subclass of idRenderModel to initialize
	
	idRenderModel* model = NULL;
	
	if( ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) || ( extension.Icmp( "flt" ) == 0 ) || ( extension.Icmp( "ma" ) == 0 ) )
	{
		model = new( TAG_MODEL ) idRenderModelStatic;
	}
	else if( extension.Icmp( MD5_MESH_EXT ) == 0 )
	{
		model = new( TAG_MODEL ) idRenderModelMD5;
	}
	else if( extension.Icmp( "md3" ) == 0 )
	{
		model = new( TAG_MODEL ) idRenderModelMD3;
	}
	else if( extension.Icmp( "prt" ) == 0 )
	{
		model = new( TAG_MODEL ) idRenderModelPrt;
	}
	else if( extension.Icmp( "liquid" ) == 0 )
	{
		model = new( TAG_MODEL ) idRenderModelLiquid;
	}
	
	idStrStatic< MAX_OSPATH > generatedFileName;
	
	if( model != NULL )
	{
	
		generatedFileName = "generated/rendermodels/";
		generatedFileName.AppendPath( canonical );
		generatedFileName.SetFileExtension( va( "b%s", extension.c_str() ) );
		
		// Get the timestamp on the original file, if it's newer than what is stored in binary model, regenerate it
		ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( canonical );
		
		idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
		
		if( !model->SupportsBinaryModel() || !r_binaryLoadRenderModels.GetBool() )
		{
			model->InitFromFile( canonical );
		}
		else
		{
			if( !model->LoadBinaryModel( file, sourceTimeStamp ) )
			{
				model->InitFromFile( canonical );
				
				idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
				idLib::Printf( "Writing %s\n", generatedFileName.c_str() );
				model->WriteBinaryModel( outputFile );
			} /* else {
				idLib::Printf( "loaded binary model %s from file %s\n", model->Name(), generatedFileName.c_str() );
			} */
		}
	}
	
	// Not one of the known formats
	if( model == NULL )
	{
	
		if( extension.Length() )
		{
			common->Warning( "unknown model type '%s'", canonical.c_str() );
		}
		
		if( !createIfNotFound )
		{
			return NULL;
		}
		
		idRenderModelStatic*	smodel = new( TAG_MODEL ) idRenderModelStatic;
		smodel->InitEmpty( canonical );
		smodel->MakeDefaultModel();
		
		model = smodel;
	}
	
	if( cvarSystem->GetCVarBool( "fs_buildresources" ) )
	{
		fileSystem->AddModelPreload( canonical );
	}
	model->SetLevelLoadReferenced( true );
	
	if( !createIfNotFound && model->IsDefaultModel() )
	{
		delete model;
		model = NULL;
		
		return NULL;
	}
	
	if( cvarSystem->GetCVarBool( "fs_buildgame" ) )
	{
		fileSystem->AddModelPreload( model->Name() );
	}
	
	AddModel( model );
	
	return model;
}
/*
========================
idMenuScreen_Shell_Root::ShowScreen
========================
*/
void idMenuScreen_Shell_Root::ShowScreen( const mainMenuTransition_t transitionType )
{

	if( menuData != NULL && menuData->GetPlatform() != 2 )
	{
		idList< idList< idStr, TAG_IDLIB_LIST_MENU >, TAG_IDLIB_LIST_MENU > menuOptions;
		idList< idStr > option;
		
		int index = 0;
		
		if( g_demoMode.GetBool() )
		{
			idMenuWidget_Button* buttonWidget = NULL;
			
			option.Append( "START DEMO" );	// START DEMO
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_START_DEMO );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "Launch the demo" );
			}
			index++;
			
			if( g_demoMode.GetInteger() == 2 )
			{
				option.Clear();
				option.Append( "START PRESS DEMO" );	// START DEMO
				menuOptions.Append( option );
				options->GetChildByIndex( index ).ClearEventActions();
				options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_START_DEMO2 );
				buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
				if( buttonWidget != NULL )
				{
					buttonWidget->SetDescription( "Launch the press demo" );
				}
				index++;
			}
			
			option.Clear();
			option.Append( "#str_swf_settings" );	// settings
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_SETTINGS );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "#str_02206" );
			}
			index++;
			
			option.Clear();
			option.Append( "#str_swf_quit" );	// quit
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_QUIT );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "#str_01976" );
			}
			index++;
			
		}
		else
		{
		
			idMenuWidget_Button* buttonWidget = NULL;
			
#if !defined ( ID_RETAIL )
			option.Append( "DEV" );	// DEV
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_DEV );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "View a list of maps available for play" );
			}
			index++;
#endif
			
			option.Clear();
			option.Append( "#str_swf_campaign" );	// singleplayer
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_CAMPAIGN );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "#str_swf_campaign_desc" );
			}
			index++;
			
			option.Clear();
			option.Append( "#str_swf_multiplayer" );	// multiplayer
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_MULTIPLAYER );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "#str_02215" );
			}
			index++;
			
			option.Clear();
			option.Append( "#str_swf_settings" );	// settings
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_SETTINGS );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "#str_02206" );
			}
			index++;
			
			
			option.Clear();
			option.Append( "#str_swf_credits" );	// credits
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_CREDITS );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "#str_02219" );
			}
			index++;
			
			// only add quit option for PC
			option.Clear();
			option.Append( "#str_swf_quit" );	// quit
			menuOptions.Append( option );
			options->GetChildByIndex( index ).ClearEventActions();
			options->GetChildByIndex( index ).AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, ROOT_CMD_QUIT );
			buttonWidget = dynamic_cast< idMenuWidget_Button* >( &options->GetChildByIndex( index ) );
			if( buttonWidget != NULL )
			{
				buttonWidget->SetDescription( "#str_01976" );
			}
			index++;
		}
		options->SetListData( menuOptions );
	}
	else
	{
		idList< idList< idStr, TAG_IDLIB_LIST_MENU >, TAG_IDLIB_LIST_MENU > menuOptions;
		options->SetListData( menuOptions );
	}
	
	idMenuScreen::ShowScreen( transitionType );
	
	if( menuData != NULL && menuData->GetPlatform() == 2 )
	{
		idMenuHandler_Shell* shell = dynamic_cast< idMenuHandler_Shell* >( menuData );
		if( shell != NULL )
		{
			idMenuWidget_MenuBar* menuBar = shell->GetMenuBar();
			if( menuBar != NULL )
			{
				menuBar->SetFocusIndex( GetRootIndex() );
			}
		}
	}
}
Ejemplo n.º 18
0
/*
=================
idRenderModelManagerLocal::Preload
=================
*/
void idRenderModelManagerLocal::Preload( const idPreloadManifest& manifest )
{
	if( preload_MapModels.GetBool() )
	{
		// preload this levels images
		int	start = Sys_Milliseconds();
		int numLoaded = 0;
		idList< preloadSort_t > preloadSort;
		preloadSort.Resize( manifest.NumResources() );
		for( int i = 0; i < manifest.NumResources(); i++ )
		{
			const preloadEntry_s& p = manifest.GetPreloadByIndex( i );
			idResourceCacheEntry rc;
			idStrStatic< MAX_OSPATH > filename;
			if( p.resType == PRELOAD_MODEL )
			{
				filename = "generated/rendermodels/";
				filename += p.resourceName;
				idStrStatic< 16 > ext;
				filename.ExtractFileExtension( ext );
				filename.SetFileExtension( va( "b%s", ext.c_str() ) );
			}
			if( p.resType == PRELOAD_PARTICLE )
			{
				filename = "generated/particles/";
				filename += p.resourceName;
				filename += ".bprt";
			}
			if( !filename.IsEmpty() )
			{
				if( fileSystem->GetResourceCacheEntry( filename, rc ) )
				{
					preloadSort_t ps = {};
					ps.idx = i;
					ps.ofs = rc.offset;
					preloadSort.Append( ps );
				}
			}
		}
		
		preloadSort.SortWithTemplate( idSort_Preload() );
		
		for( int i = 0; i < preloadSort.Num(); i++ )
		{
			const preloadSort_t& ps = preloadSort[ i ];
			const preloadEntry_s& p = manifest.GetPreloadByIndex( ps.idx );
			if( p.resType == PRELOAD_MODEL )
			{
				idRenderModel* model = FindModel( p.resourceName );
				if( model != NULL )
				{
					model->SetLevelLoadReferenced( true );
				}
			}
			else if( p.resType == PRELOAD_PARTICLE )
			{
				declManager->FindType( DECL_PARTICLE, p.resourceName );
			}
			numLoaded++;
		}
		
		int	end = Sys_Milliseconds();
		common->Printf( "%05d models preloaded ( or were already loaded ) in %5.1f seconds\n", numLoaded, ( end - start ) * 0.001 );
		common->Printf( "----------------------------------------\n" );
	}
}
Ejemplo n.º 19
0
/*
====================
idMD5Anim::LoadAnim
====================
*/
bool idMD5Anim::LoadAnim( const char* filename )
{

	idLexer	parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT );
	idToken	token;
	
	idStr generatedFileName = "generated/anim/";
	generatedFileName.AppendPath( filename );
	generatedFileName.SetFileExtension( ".bMD5anim" );
	
	// Get the timestamp on the original file, if it's newer than what is stored in binary model, regenerate it
	ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( filename );
	
	idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
	if( binaryLoadAnim.GetBool() && LoadBinary( file, sourceTimeStamp ) )
	{
		name = filename;
		if( cvarSystem->GetCVarBool( "fs_buildresources" ) )
		{
			// for resource gathering write this anim to the preload file for this map
			fileSystem->AddAnimPreload( name );
		}
		return true;
	}
	
	if( !parser.LoadFile( filename ) )
	{
		return false;
	}
	
	name = filename;
	
	Free();
	
	parser.ExpectTokenString( MD5_VERSION_STRING );
	int version = parser.ParseInt();
	if( version != MD5_VERSION )
	{
		parser.Error( "Invalid version %d.  Should be version %d\n", version, MD5_VERSION );
	}
	
	// skip the commandline
	parser.ExpectTokenString( "commandline" );
	parser.ReadToken( &token );
	
	// parse num frames
	parser.ExpectTokenString( "numFrames" );
	numFrames = parser.ParseInt();
	if( numFrames <= 0 )
	{
		parser.Error( "Invalid number of frames: %d", numFrames );
	}
	
	// parse num joints
	parser.ExpectTokenString( "numJoints" );
	numJoints = parser.ParseInt();
	if( numJoints <= 0 )
	{
		parser.Error( "Invalid number of joints: %d", numJoints );
	}
	
	// parse frame rate
	parser.ExpectTokenString( "frameRate" );
	frameRate = parser.ParseInt();
	if( frameRate < 0 )
	{
		parser.Error( "Invalid frame rate: %d", frameRate );
	}
	
	// parse number of animated components
	parser.ExpectTokenString( "numAnimatedComponents" );
	numAnimatedComponents = parser.ParseInt();
	if( ( numAnimatedComponents < 0 ) || ( numAnimatedComponents > numJoints * 6 ) )
	{
		parser.Error( "Invalid number of animated components: %d", numAnimatedComponents );
	}
	
	// parse the hierarchy
	jointInfo.SetGranularity( 1 );
	jointInfo.SetNum( numJoints );
	parser.ExpectTokenString( "hierarchy" );
	parser.ExpectTokenString( "{" );
	for( int i = 0; i < numJoints; i++ )
	{
		parser.ReadToken( &token );
		jointInfo[ i ].nameIndex = animationLib.JointIndex( token );
		
		// parse parent num
		jointInfo[ i ].parentNum = parser.ParseInt();
		if( jointInfo[ i ].parentNum >= i )
		{
			parser.Error( "Invalid parent num: %d", jointInfo[ i ].parentNum );
		}
		
		if( ( i != 0 ) && ( jointInfo[ i ].parentNum < 0 ) )
		{
			parser.Error( "Animations may have only one root joint" );
		}
		
		// parse anim bits
		jointInfo[ i ].animBits = parser.ParseInt();
		if( jointInfo[ i ].animBits & ~63 )
		{
			parser.Error( "Invalid anim bits: %d", jointInfo[ i ].animBits );
		}
		
		// parse first component
		jointInfo[ i ].firstComponent = parser.ParseInt();
		if( ( numAnimatedComponents > 0 ) && ( ( jointInfo[ i ].firstComponent < 0 ) || ( jointInfo[ i ].firstComponent >= numAnimatedComponents ) ) )
		{
			parser.Error( "Invalid first component: %d", jointInfo[ i ].firstComponent );
		}
	}
	
	parser.ExpectTokenString( "}" );
	
	// parse bounds
	parser.ExpectTokenString( "bounds" );
	parser.ExpectTokenString( "{" );
	bounds.SetGranularity( 1 );
	bounds.SetNum( numFrames );
	for( int i = 0; i < numFrames; i++ )
	{
		parser.Parse1DMatrix( 3, bounds[ i ][ 0 ].ToFloatPtr() );
		parser.Parse1DMatrix( 3, bounds[ i ][ 1 ].ToFloatPtr() );
	}
	parser.ExpectTokenString( "}" );
	
	// parse base frame
	baseFrame.SetGranularity( 1 );
	baseFrame.SetNum( numJoints );
	parser.ExpectTokenString( "baseframe" );
	parser.ExpectTokenString( "{" );
	for( int i = 0; i < numJoints; i++ )
	{
		idCQuat q;
		parser.Parse1DMatrix( 3, baseFrame[ i ].t.ToFloatPtr() );
		parser.Parse1DMatrix( 3, q.ToFloatPtr() );//baseFrame[ i ].q.ToFloatPtr() );
		baseFrame[ i ].q = q.ToQuat();//.w = baseFrame[ i ].q.CalcW();
		baseFrame[ i ].w = 0.0f;
	}
	parser.ExpectTokenString( "}" );
	
	// parse frames
	componentFrames.SetGranularity( 1 );
	componentFrames.SetNum( numAnimatedComponents * numFrames + JOINT_FRAME_PAD );
	componentFrames[numAnimatedComponents * numFrames + JOINT_FRAME_PAD - 1] = 0.0f;
	
	float* componentPtr = componentFrames.Ptr();
	for( int i = 0; i < numFrames; i++ )
	{
		parser.ExpectTokenString( "frame" );
		int num = parser.ParseInt();
		if( num != i )
		{
			parser.Error( "Expected frame number %d", i );
		}
		parser.ExpectTokenString( "{" );
		
		for( int j = 0; j < numAnimatedComponents; j++, componentPtr++ )
		{
			*componentPtr = parser.ParseFloat();
		}
		
		parser.ExpectTokenString( "}" );
	}
	
	// get total move delta
	if( !numAnimatedComponents )
	{
		totaldelta.Zero();
	}
	else
	{
		componentPtr = &componentFrames[ jointInfo[ 0 ].firstComponent ];
		if( jointInfo[ 0 ].animBits & ANIM_TX )
		{
			for( int i = 0; i < numFrames; i++ )
			{
				componentPtr[ numAnimatedComponents * i ] -= baseFrame[ 0 ].t.x;
			}
			totaldelta.x = componentPtr[ numAnimatedComponents * ( numFrames - 1 ) ];
			componentPtr++;
		}
		else
		{
			totaldelta.x = 0.0f;
		}
		if( jointInfo[ 0 ].animBits & ANIM_TY )
		{
			for( int i = 0; i < numFrames; i++ )
			{
				componentPtr[ numAnimatedComponents * i ] -= baseFrame[ 0 ].t.y;
			}
			totaldelta.y = componentPtr[ numAnimatedComponents * ( numFrames - 1 ) ];
			componentPtr++;
		}
		else
		{
			totaldelta.y = 0.0f;
		}
		if( jointInfo[ 0 ].animBits & ANIM_TZ )
		{
			for( int i = 0; i < numFrames; i++ )
			{
				componentPtr[ numAnimatedComponents * i ] -= baseFrame[ 0 ].t.z;
			}
			totaldelta.z = componentPtr[ numAnimatedComponents * ( numFrames - 1 ) ];
		}
		else
		{
			totaldelta.z = 0.0f;
		}
	}
	baseFrame[ 0 ].t.Zero();
	
	// we don't count last frame because it would cause a 1 frame pause at the end
	animLength = ( ( numFrames - 1 ) * 1000 + frameRate - 1 ) / frameRate;
	
	if( binaryLoadAnim.GetBool() )
	{
		idLib::Printf( "Writing %s\n", generatedFileName.c_str() );
		idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
		WriteBinary( outputFile, sourceTimeStamp );
	}
	
	// done
	return true;
}
Ejemplo n.º 20
0
/*
===================
R_AddModels

The end result of running this is the addition of drawSurf_t to the
tr.viewDef->drawSurfs[] array and light link chains, along with
frameData and vertexCache allocations to support the drawSurfs.
===================
*/
void R_AddModels()
{
	SCOPED_PROFILE_EVENT( "R_AddModels" );
	
	tr.viewDef->viewEntitys = R_SortViewEntities( tr.viewDef->viewEntitys );
	
	//-------------------------------------------------
	// Go through each view entity that is either visible to the view, or to
	// any light that intersects the view (for shadows).
	//-------------------------------------------------
	
	if( r_useParallelAddModels.GetBool() )
	{
		for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next )
		{
			tr.frontEndJobList->AddJob( ( jobRun_t )R_AddSingleModel, vEntity );
		}
		tr.frontEndJobList->Submit();
		tr.frontEndJobList->Wait();
	}
	else
	{
		for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next )
		{
			R_AddSingleModel( vEntity );
		}
	}
	
	//-------------------------------------------------
	// Kick off jobs to setup static and dynamic shadow volumes.
	//-------------------------------------------------
	
	if( r_useParallelAddShadows.GetInteger() == 1 )
	{
		for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next )
		{
			for( staticShadowVolumeParms_t* shadowParms = vEntity->staticShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next )
			{
				tr.frontEndJobList->AddJob( ( jobRun_t )StaticShadowVolumeJob, shadowParms );
			}
			for( dynamicShadowVolumeParms_t* shadowParms = vEntity->dynamicShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next )
			{
				tr.frontEndJobList->AddJob( ( jobRun_t )DynamicShadowVolumeJob, shadowParms );
			}
			vEntity->staticShadowVolumes = NULL;
			vEntity->dynamicShadowVolumes = NULL;
		}
		tr.frontEndJobList->Submit();
		// wait here otherwise the shadow volume index buffer may be unmapped before all shadow volumes have been constructed
		tr.frontEndJobList->Wait();
	}
	else
	{
		int start = Sys_Microseconds();
		
		for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next )
		{
			for( staticShadowVolumeParms_t* shadowParms = vEntity->staticShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next )
			{
				StaticShadowVolumeJob( shadowParms );
			}
			for( dynamicShadowVolumeParms_t* shadowParms = vEntity->dynamicShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next )
			{
				DynamicShadowVolumeJob( shadowParms );
			}
			vEntity->staticShadowVolumes = NULL;
			vEntity->dynamicShadowVolumes = NULL;
		}
		
		int end = Sys_Microseconds();
		backEnd.pc.shadowMicroSec += end - start;
	}
	
	//-------------------------------------------------
	// Move the draw surfs to the view.
	//-------------------------------------------------
	
	tr.viewDef->numDrawSurfs = 0;	// clear the ambient surface list
	tr.viewDef->maxDrawSurfs = 0;	// will be set to INITIAL_DRAWSURFS on R_LinkDrawSurfToView
	
	for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next )
	{
		for( drawSurf_t* ds = vEntity->drawSurfs; ds != NULL; )
		{
			drawSurf_t* next = ds->nextOnLight;
			if( ds->linkChain == NULL )
			{
				R_LinkDrawSurfToView( ds, tr.viewDef );
			}
			else
			{
				ds->nextOnLight = *ds->linkChain;
				*ds->linkChain = ds;
			}
			ds = next;
		}
		vEntity->drawSurfs = NULL;
	}
}
Ejemplo n.º 21
0
/*
========================
idCommonLocal::RunNetworkSnapshotFrame
========================
*/
void idCommonLocal::RunNetworkSnapshotFrame()
{

	// Process any reliable messages we've received
	for( int i = 0; i < reliableQueue.Num(); i++ )
	{
		game->ProcessReliableMessage( reliableQueue[i].client, reliableQueue[i].type, idBitMsg( ( const byte* )reliableQueue[i].data, reliableQueue[i].dataSize ) );
		Mem_Free( reliableQueue[i].data );
	}
	reliableQueue.Clear();
	
	// abuse the game timing to time presentable thinking on clients
	time_gameFrame = Sys_Microseconds();
	time_maxGameFrame = 0;
	count_numGameFrames = 0;
	
	if( snapPrevious.serverTime >= 0 )
	{
	
		int	msec_interval = 1 + idMath::Ftoi( ( float )initialBaseTicksPerSec );
		
		static int clientTimeResidual = 0;
		static int lastTime = Sys_Milliseconds();
		int currentTime = Sys_Milliseconds();
		int deltaFrameTime = idMath::ClampInt( 1, 33, currentTime - lastTime );
		
		clientTimeResidual += idMath::ClampInt( 0, 50, currentTime - lastTime );
		lastTime = currentTime;
		
		extern idCVar com_fixedTic;
		if( com_fixedTic.GetBool() )
		{
			clientTimeResidual = 0;
		}
		
		do
		{
			// If we are extrapolating and have fresher snapshots, then use the freshest one
			while( ( snapCurrentTime >= snapRate || com_forceLatestSnap.GetBool() ) && readSnapshotIndex < writeSnapshotIndex )
			{
				snapCurrentTime -= snapRate;
				ProcessNextSnapshot();
			}
			
			// this only matters when running < 60 fps
			// JAF Game()->GetRenderWorld()->UpdateDeferredPositions();
			
			// Clamp the current time so that it doesn't fall outside of our extrapolation bounds
			snapCurrentTime = idMath::ClampInt( 0, snapRate + Min( ( int )snapRate, ( int )net_maxExtrapolationInMS.GetInteger() ), snapCurrentTime );
			
			if( snapRate <= 0 )
			{
				idLib::Warning( "snapRate <= 0. Resetting to 100" );
				snapRate = 100;
			}
			
			float fraction = ( float )snapCurrentTime / ( float )snapRate;
			if( !IsValid( fraction ) )
			{
				idLib::Warning( "Interpolation Fraction invalid: snapCurrentTime %d / snapRate %d", ( int )snapCurrentTime, ( int )snapRate );
				fraction = 0.0f;
			}
			
			InterpolateSnapshot( snapPrevious, snapCurrent, fraction, true );
			
			// Default to a snap scale of 1
			float snapRateScale = net_interpolationBaseRate.GetFloat();
			
			snapTimeBuffered = CalcSnapTimeBuffered( totalBufferedTime, totalRecvTime );
			effectiveSnapRate = static_cast< float >( totalBufferedTime ) / static_cast< float >( totalRecvTime );
			
			if( net_minBufferedSnapPCT_Static.GetFloat() > 0.0f )
			{
				optimalPCTBuffer = session->GetTitleStorageFloat( "net_minBufferedSnapPCT_Static", net_minBufferedSnapPCT_Static.GetFloat() );
			}
			
			// Calculate optimal amount of buffered time we want
			if( net_optimalDynamic.GetBool() )
			{
				optimalTimeBuffered = idMath::ClampInt( 0, net_maxBufferedSnapMS.GetInteger(), snapRate * optimalPCTBuffer );
				optimalTimeBufferedWindow = snapRate * net_minBufferedSnapWinPCT_Static.GetFloat();
			}
			else
			{
				optimalTimeBuffered = net_optimalSnapTime.GetFloat();
				optimalTimeBufferedWindow = net_optimalSnapWindow.GetFloat();
			}
			
			// Scale snapRate based on where we are in the buffer
			if( snapTimeBuffered <= optimalTimeBuffered )
			{
				if( snapTimeBuffered <= idMath::FLT_SMALLEST_NON_DENORMAL )
				{
					snapRateScale = 0;
				}
				else
				{
					snapRateScale = net_interpolationFallbackRate.GetFloat();
					// When we interpolate past our cushion of buffered snapshot, we want to slow smoothly slow the
					// rate of interpolation. frac will go from 1.0 to 0.0 (if snapshots stop coming in).
					float startSlowdown = ( net_interpolationSlowdownStart.GetFloat() * optimalTimeBuffered );
					if( startSlowdown > 0 && snapTimeBuffered < startSlowdown )
					{
						float frac = idMath::ClampFloat( 0.0f, 1.0f, snapTimeBuffered / startSlowdown );
						if( !IsValid( frac ) )
						{
							frac = 0.0f;
						}
						snapRateScale = Square( frac ) * snapRateScale;
						if( !IsValid( snapRateScale ) )
						{
							snapRateScale = 0.0f;
						}
					}
				}
				
				
			}
			else if( snapTimeBuffered > optimalTimeBuffered + optimalTimeBufferedWindow )
			{
				// Go faster
				snapRateScale = net_interpolationCatchupRate.GetFloat();
				
			}
			
			float delta_interpolate = ( float )initialBaseTicksPerSec * snapRateScale;
			if( net_effectiveSnapRateEnable.GetBool() )
			{
			
				float deltaFrameGameMS = static_cast<float>( initialBaseTicksPerSec ) * static_cast<float>( deltaFrameTime / 1000.0f );
				delta_interpolate = ( deltaFrameGameMS * snapRateScale * effectiveSnapRate ) + snapCurrentResidual;
				if( !IsValid( delta_interpolate ) )
				{
					delta_interpolate = 0.0f;
				}
				
				snapCurrentResidual = idMath::Frac( delta_interpolate ); // fixme: snapCurrentTime should just be a float, but would require changes in d4 too
				if( !IsValid( snapCurrentResidual ) )
				{
					snapCurrentResidual = 0.0f;
				}
				
				if( net_effectiveSnapRateDebug.GetBool() )
				{
					idLib::Printf( "%d/%.2f snapRateScale: %.2f effectiveSR: %.2f d.interp: %.2f snapTimeBuffered: %.2f res: %.2f\n", deltaFrameTime, deltaFrameGameMS, snapRateScale, effectiveSnapRate, delta_interpolate, snapTimeBuffered, snapCurrentResidual );
				}
			}
			
			assert( IsValid( delta_interpolate ) );
			int interpolate_interval = idMath::Ftoi( delta_interpolate );
			
			snapCurrentTime += interpolate_interval;	// advance interpolation time by the scaled interpolate_interval
			clientTimeResidual -= msec_interval;		// advance local client residual time (fixed step)
			
		}
		while( clientTimeResidual >= msec_interval );
		
		if( clientTimeResidual < 0 )
		{
			clientTimeResidual = 0;
		}
	}
	
	time_gameFrame = Sys_Microseconds() - time_gameFrame;
}
Ejemplo n.º 22
0
/*
===================
R_AddSingleModel

May be run in parallel.

Here is where dynamic models actually get instantiated, and necessary
interaction surfaces get created. This is all done on a sort-by-model
basis to keep source data in cache (most likely L2) as any interactions
and shadows are generated, since dynamic models will typically be lit by
two or more lights.
===================
*/
void R_AddSingleModel( viewEntity_t* vEntity )
{
	// we will add all interaction surfs here, to be chained to the lights in later serial code
	vEntity->drawSurfs = NULL;
	vEntity->staticShadowVolumes = NULL;
	vEntity->dynamicShadowVolumes = NULL;
	
	// globals we really should pass in...
	const viewDef_t* viewDef = tr.viewDef;
	
	idRenderEntityLocal* entityDef = vEntity->entityDef;
	const renderEntity_t* renderEntity = &entityDef->parms;
	const idRenderWorldLocal* world = entityDef->world;
	
	if( viewDef->isXraySubview && entityDef->parms.xrayIndex == 1 )
	{
		return;
	}
	else if( !viewDef->isXraySubview && entityDef->parms.xrayIndex == 2 )
	{
		return;
	}
	
	SCOPED_PROFILE_EVENT( renderEntity->hModel == NULL ? "Unknown Model" : renderEntity->hModel->Name() );
	
	// calculate the znear for testing whether or not the view is inside a shadow projection
	const float znear = ( viewDef->renderView.cramZNear ) ? ( r_znear.GetFloat() * 0.25f ) : r_znear.GetFloat();
	
	// if the entity wasn't seen through a portal chain, it was added just for light shadows
	const bool modelIsVisible = !vEntity->scissorRect.IsEmpty();
	const bool addInteractions = modelIsVisible && ( !viewDef->isXraySubview || entityDef->parms.xrayIndex == 2 );
	const int entityIndex = entityDef->index;
	
	//---------------------------
	// Find which of the visible lights contact this entity
	//
	// If the entity doesn't accept light or cast shadows from any surface,
	// this can be skipped.
	//
	// OPTIMIZE: world areas can assume all referenced lights are used
	//---------------------------
	int	numContactedLights = 0;
	static const int MAX_CONTACTED_LIGHTS = 128;
	viewLight_t* contactedLights[MAX_CONTACTED_LIGHTS];
	idInteraction* staticInteractions[MAX_CONTACTED_LIGHTS];
	
	if( renderEntity->hModel == NULL ||
			renderEntity->hModel->ModelHasInteractingSurfaces() ||
			renderEntity->hModel->ModelHasShadowCastingSurfaces() )
	{
		SCOPED_PROFILE_EVENT( "Find lights" );
		for( viewLight_t* vLight = viewDef->viewLights; vLight != NULL; vLight = vLight->next )
		{
			if( vLight->scissorRect.IsEmpty() )
			{
				continue;
			}
			if( vLight->entityInteractionState != NULL )
			{
				// new code path, everything was done in AddLight
				if( vLight->entityInteractionState[entityIndex] == viewLight_t::INTERACTION_YES )
				{
					contactedLights[numContactedLights] = vLight;
					staticInteractions[numContactedLights] = world->interactionTable[vLight->lightDef->index * world->interactionTableWidth + entityIndex];
					if( ++numContactedLights == MAX_CONTACTED_LIGHTS )
					{
						break;
					}
				}
				continue;
			}
			
			const idRenderLightLocal* lightDef = vLight->lightDef;
			
			if( !lightDef->globalLightBounds.IntersectsBounds( entityDef->globalReferenceBounds ) )
			{
				continue;
			}
			
			if( R_CullModelBoundsToLight( lightDef, entityDef->localReferenceBounds, entityDef->modelRenderMatrix ) )
			{
				continue;
			}
			
			if( !modelIsVisible )
			{
				// some lights have their center of projection outside the world
				if( lightDef->areaNum != -1 )
				{
					// if no part of the model is in an area that is connected to
					// the light center (it is behind a solid, closed door), we can ignore it
					bool areasConnected = false;
					for( areaReference_t* ref = entityDef->entityRefs; ref != NULL; ref = ref->ownerNext )
					{
						if( world->AreasAreConnected( lightDef->areaNum, ref->area->areaNum, PS_BLOCK_VIEW ) )
						{
							areasConnected = true;
							break;
						}
					}
					if( areasConnected == false )
					{
						// can't possibly be seen or shadowed
						continue;
					}
				}
				
				// check more precisely for shadow visibility
				idBounds shadowBounds;
				R_ShadowBounds( entityDef->globalReferenceBounds, lightDef->globalLightBounds, lightDef->globalLightOrigin, shadowBounds );
				
				// this doesn't say that the shadow can't effect anything, only that it can't
				// effect anything in the view
				if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, shadowBounds ) )
				{
					continue;
				}
			}
			contactedLights[numContactedLights] = vLight;
			staticInteractions[numContactedLights] = world->interactionTable[vLight->lightDef->index * world->interactionTableWidth + entityIndex];
			if( ++numContactedLights == MAX_CONTACTED_LIGHTS )
			{
				break;
			}
		}
	}
	
	// if we aren't visible and none of the shadows stretch into the view,
	// we don't need to do anything else
	if( !modelIsVisible && numContactedLights == 0 )
	{
		return;
	}
	
	//---------------------------
	// create a dynamic model if the geometry isn't static
	//---------------------------
	idRenderModel* model = R_EntityDefDynamicModel( entityDef );
	if( model == NULL || model->NumSurfaces() <= 0 )
	{
		return;
	}
	
	// add the lightweight blood decal surfaces if the model is directly visible
	if( modelIsVisible )
	{
		assert( !vEntity->scissorRect.IsEmpty() );
		
		if( entityDef->decals != NULL && !r_skipDecals.GetBool() )
		{
			entityDef->decals->CreateDeferredDecals( model );
			
			unsigned int numDrawSurfs = entityDef->decals->GetNumDecalDrawSurfs();
			for( unsigned int i = 0; i < numDrawSurfs; i++ )
			{
				drawSurf_t* decalDrawSurf = entityDef->decals->CreateDecalDrawSurf( vEntity, i );
				if( decalDrawSurf != NULL )
				{
					decalDrawSurf->linkChain = NULL;
					decalDrawSurf->nextOnLight = vEntity->drawSurfs;
					vEntity->drawSurfs = decalDrawSurf;
				}
			}
		}
		
		if( entityDef->overlays != NULL && !r_skipOverlays.GetBool() )
		{
			entityDef->overlays->CreateDeferredOverlays( model );
			
			unsigned int numDrawSurfs = entityDef->overlays->GetNumOverlayDrawSurfs();
			for( unsigned int i = 0; i < numDrawSurfs; i++ )
			{
				drawSurf_t* overlayDrawSurf = entityDef->overlays->CreateOverlayDrawSurf( vEntity, model, i );
				if( overlayDrawSurf != NULL )
				{
					overlayDrawSurf->linkChain = NULL;
					overlayDrawSurf->nextOnLight = vEntity->drawSurfs;
					vEntity->drawSurfs = overlayDrawSurf;
				}
			}
		}
	}
	
	//---------------------------
	// copy matrix related stuff for back-end use
	// and setup a render matrix for faster culling
	//---------------------------
	vEntity->modelDepthHack = renderEntity->modelDepthHack;
	vEntity->weaponDepthHack = renderEntity->weaponDepthHack;
	vEntity->skipMotionBlur = renderEntity->skipMotionBlur;
	
	memcpy( vEntity->modelMatrix, entityDef->modelMatrix, sizeof( vEntity->modelMatrix ) );
	R_MatrixMultiply( entityDef->modelMatrix, viewDef->worldSpace.modelViewMatrix, vEntity->modelViewMatrix );
	
	idRenderMatrix viewMat;
	idRenderMatrix::Transpose( *( idRenderMatrix* )vEntity->modelViewMatrix, viewMat );
	idRenderMatrix::Multiply( viewDef->projectionRenderMatrix, viewMat, vEntity->mvp );
	if( renderEntity->weaponDepthHack )
	{
		idRenderMatrix::ApplyDepthHack( vEntity->mvp );
	}
	if( renderEntity->modelDepthHack != 0.0f )
	{
		idRenderMatrix::ApplyModelDepthHack( vEntity->mvp, renderEntity->modelDepthHack );
	}
	
	// local light and view origins are used to determine if the view is definitely outside
	// an extruded shadow volume, which means we can skip drawing the end caps
	idVec3 localViewOrigin;
	R_GlobalPointToLocal( vEntity->modelMatrix, viewDef->renderView.vieworg, localViewOrigin );
	
	//---------------------------
	// add all the model surfaces
	//---------------------------
	for( int surfaceNum = 0; surfaceNum < model->NumSurfaces(); surfaceNum++ )
	{
		const modelSurface_t* surf = model->Surface( surfaceNum );
		
		// for debugging, only show a single surface at a time
		if( r_singleSurface.GetInteger() >= 0 && surfaceNum != r_singleSurface.GetInteger() )
		{
			continue;
		}
		
		srfTriangles_t* tri = surf->geometry;
		if( tri == NULL )
		{
			continue;
		}
		if( tri->numIndexes == 0 )
		{
			continue;		// happens for particles
		}
		const idMaterial* shader = surf->shader;
		if( shader == NULL )
		{
			continue;
		}
		if( !shader->IsDrawn() )
		{
			continue;		// collision hulls, etc
		}
		
		// RemapShaderBySkin
		if( entityDef->parms.customShader != NULL )
		{
			// this is sort of a hack, but causes deformed surfaces to map to empty surfaces,
			// so the item highlight overlay doesn't highlight the autosprite surface
			if( shader->Deform() )
			{
				continue;
			}
			shader = entityDef->parms.customShader;
		}
		else if( entityDef->parms.customSkin )
		{
			shader = entityDef->parms.customSkin->RemapShaderBySkin( shader );
			if( shader == NULL )
			{
				continue;
			}
			if( !shader->IsDrawn() )
			{
				continue;
			}
		}
		
		// optionally override with the renderView->globalMaterial
		if( tr.primaryRenderView.globalMaterial != NULL )
		{
			shader = tr.primaryRenderView.globalMaterial;
		}
		
		SCOPED_PROFILE_EVENT( shader->GetName() );
		
		// debugging tool to make sure we have the correct pre-calculated bounds
		if( r_checkBounds.GetBool() )
		{
			for( int j = 0; j < tri->numVerts; j++ )
			{
				int k;
				for( k = 0; k < 3; k++ )
				{
					if( tri->verts[j].xyz[k] > tri->bounds[1][k] + CHECK_BOUNDS_EPSILON
							|| tri->verts[j].xyz[k] < tri->bounds[0][k] - CHECK_BOUNDS_EPSILON )
					{
						common->Printf( "bad tri->bounds on %s:%s\n", entityDef->parms.hModel->Name(), shader->GetName() );
						break;
					}
					if( tri->verts[j].xyz[k] > entityDef->localReferenceBounds[1][k] + CHECK_BOUNDS_EPSILON
							|| tri->verts[j].xyz[k] < entityDef->localReferenceBounds[0][k] - CHECK_BOUNDS_EPSILON )
					{
						common->Printf( "bad referenceBounds on %s:%s\n", entityDef->parms.hModel->Name(), shader->GetName() );
						break;
					}
				}
				if( k != 3 )
				{
					break;
				}
			}
		}
		
		// view frustum culling for the precise surface bounds, which is tighter
		// than the entire entity reference bounds
		// If the entire model wasn't visible, there is no need to check the
		// individual surfaces.
		const bool surfaceDirectlyVisible = modelIsVisible && !idRenderMatrix::CullBoundsToMVP( vEntity->mvp, tri->bounds );
		
		// RB: added check wether GPU skinning is available at all
		const bool gpuSkinned = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() && glConfig.gpuSkinningAvailable );
		// RB end
		
		//--------------------------
		// base drawing surface
		//--------------------------
		drawSurf_t* baseDrawSurf = NULL;
		if( surfaceDirectlyVisible )
		{
			// make sure we have an ambient cache and all necessary normals / tangents
			if( !vertexCache.CacheIsCurrent( tri->indexCache ) )
			{
				tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
			}
			
			if( !vertexCache.CacheIsCurrent( tri->ambientCache ) )
			{
				// we are going to use it for drawing, so make sure we have the tangents and normals
				if( shader->ReceivesLighting() && !tri->tangentsCalculated )
				{
					assert( tri->staticModelWithJoints == NULL );
					R_DeriveTangents( tri );
					
					// RB: this was hit by parametric particle models ..
					//assert( false );	// this should no longer be hit
					// RB end
				}
				tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
			}
			
			// add the surface for drawing
			// we can re-use some of the values for light interaction surfaces
			baseDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *baseDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
			baseDrawSurf->frontEndGeo = tri;
			baseDrawSurf->space = vEntity;
			baseDrawSurf->scissorRect = vEntity->scissorRect;
			baseDrawSurf->extraGLState = 0;
			baseDrawSurf->renderZFail = 0;
			
			R_SetupDrawSurfShader( baseDrawSurf, shader, renderEntity );
			
			// Check for deformations (eyeballs, flares, etc)
			const deform_t shaderDeform = shader->Deform();
			if( shaderDeform != DFRM_NONE )
			{
				drawSurf_t* deformDrawSurf = R_DeformDrawSurf( baseDrawSurf );
				if( deformDrawSurf != NULL )
				{
					// any deforms may have created multiple draw surfaces
					for( drawSurf_t* surf = deformDrawSurf, * next = NULL; surf != NULL; surf = next )
					{
						next = surf->nextOnLight;
						
						surf->linkChain = NULL;
						surf->nextOnLight = vEntity->drawSurfs;
						vEntity->drawSurfs = surf;
					}
				}
			}
			
			// Most deform source surfaces do not need to be rendered.
			// However, particles are rendered in conjunction with the source surface.
			if( shaderDeform == DFRM_NONE || shaderDeform == DFRM_PARTICLE || shaderDeform == DFRM_PARTICLE2 )
			{
				// copy verts and indexes to this frame's hardware memory if they aren't already there
				if( !vertexCache.CacheIsCurrent( tri->ambientCache ) )
				{
					tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( tri->verts[0] ), VERTEX_CACHE_ALIGN ) );
				}
				if( !vertexCache.CacheIsCurrent( tri->indexCache ) )
				{
					tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( tri->indexes[0] ), INDEX_CACHE_ALIGN ) );
				}
				
				R_SetupDrawSurfJoints( baseDrawSurf, tri, shader );
				
				baseDrawSurf->numIndexes = tri->numIndexes;
				baseDrawSurf->ambientCache = tri->ambientCache;
				baseDrawSurf->indexCache = tri->indexCache;
				baseDrawSurf->shadowCache = 0;
				
				baseDrawSurf->linkChain = NULL;		// link to the view
				baseDrawSurf->nextOnLight = vEntity->drawSurfs;
				vEntity->drawSurfs = baseDrawSurf;
			}
		}
		
		//----------------------------------------
		// add all light interactions
		//----------------------------------------
		for( int contactedLight = 0; contactedLight < numContactedLights; contactedLight++ )
		{
			viewLight_t* vLight = contactedLights[contactedLight];
			const idRenderLightLocal* lightDef = vLight->lightDef;
			const idInteraction* interaction = staticInteractions[contactedLight];
			
			// check for a static interaction
			surfaceInteraction_t* surfInter = NULL;
			if( interaction > INTERACTION_EMPTY && interaction->staticInteraction )
			{
				// we have a static interaction that was calculated accurately
				assert( model->NumSurfaces() == interaction->numSurfaces );
				surfInter = &interaction->surfaces[surfaceNum];
			}
			else
			{
				// try to do a more precise cull of this model surface to the light
				if( R_CullModelBoundsToLight( lightDef, tri->bounds, entityDef->modelRenderMatrix ) )
				{
					continue;
				}
			}
			
			// "invisible ink" lights and shaders (imp spawn drawing on walls, etc)
			if( shader->Spectrum() != lightDef->lightShader->Spectrum() )
			{
				continue;
			}
			
			// Calculate the local light origin to determine if the view is inside the shadow
			// projection and to calculate the triangle facing for dynamic shadow volumes.
			idVec3 localLightOrigin;
			R_GlobalPointToLocal( vEntity->modelMatrix, lightDef->globalLightOrigin, localLightOrigin );
			
			//--------------------------
			// surface light interactions
			//--------------------------
			
			dynamicShadowVolumeParms_t* dynamicShadowParms = NULL;
			
			if( addInteractions && surfaceDirectlyVisible && shader->ReceivesLighting() )
			{
				// static interactions can commonly find that no triangles from a surface
				// contact the light, even when the total model does
				if( surfInter == NULL || surfInter->lightTrisIndexCache > 0 )
				{
					// create a drawSurf for this interaction
					drawSurf_t* lightDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *lightDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
					
					if( surfInter != NULL )
					{
						// optimized static interaction
						lightDrawSurf->numIndexes = surfInter->numLightTrisIndexes;
						lightDrawSurf->indexCache = surfInter->lightTrisIndexCache;
					}
					else
					{
						// throw the entire source surface at it without any per-triangle culling
						lightDrawSurf->numIndexes = tri->numIndexes;
						lightDrawSurf->indexCache = tri->indexCache;
						
						// optionally cull the triangles to the light volume
						if( r_cullDynamicLightTriangles.GetBool() )
						{
						
							vertCacheHandle_t lightIndexCache = vertexCache.AllocIndex( NULL, ALIGN( lightDrawSurf->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
							if( vertexCache.CacheIsCurrent( lightIndexCache ) )
							{
								lightDrawSurf->indexCache = lightIndexCache;
								
								dynamicShadowParms = ( dynamicShadowVolumeParms_t* )R_FrameAlloc( sizeof( dynamicShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS );
								
								dynamicShadowParms->verts = tri->verts;
								dynamicShadowParms->numVerts = tri->numVerts;
								dynamicShadowParms->indexes = tri->indexes;
								dynamicShadowParms->numIndexes = tri->numIndexes;
								dynamicShadowParms->silEdges = tri->silEdges;
								dynamicShadowParms->numSilEdges = tri->numSilEdges;
								dynamicShadowParms->joints = gpuSkinned ? tri->staticModelWithJoints->jointsInverted : NULL;
								dynamicShadowParms->numJoints = gpuSkinned ? tri->staticModelWithJoints->numInvertedJoints : 0;
								dynamicShadowParms->triangleBounds = tri->bounds;
								dynamicShadowParms->triangleMVP = vEntity->mvp;
								dynamicShadowParms->localLightOrigin = localLightOrigin;
								dynamicShadowParms->localViewOrigin = localViewOrigin;
								idRenderMatrix::Multiply( vLight->lightDef->baseLightProject, entityDef->modelRenderMatrix, dynamicShadowParms->localLightProject );
								dynamicShadowParms->zNear = znear;
								dynamicShadowParms->lightZMin = vLight->scissorRect.zmin;
								dynamicShadowParms->lightZMax = vLight->scissorRect.zmax;
								dynamicShadowParms->cullShadowTrianglesToLight = false;
								dynamicShadowParms->forceShadowCaps = false;
								dynamicShadowParms->useShadowPreciseInsideTest = false;
								dynamicShadowParms->useShadowDepthBounds = false;
								dynamicShadowParms->tempFacing = NULL;
								dynamicShadowParms->tempCulled = NULL;
								dynamicShadowParms->tempVerts = NULL;
								dynamicShadowParms->indexBuffer = NULL;
								dynamicShadowParms->shadowIndices = NULL;
								dynamicShadowParms->maxShadowIndices = 0;
								dynamicShadowParms->numShadowIndices = NULL;
								dynamicShadowParms->lightIndices = ( triIndex_t* )vertexCache.MappedIndexBuffer( lightIndexCache );
								dynamicShadowParms->maxLightIndices = lightDrawSurf->numIndexes;
								dynamicShadowParms->numLightIndices = &lightDrawSurf->numIndexes;
								dynamicShadowParms->renderZFail = NULL;
								dynamicShadowParms->shadowZMin = NULL;
								dynamicShadowParms->shadowZMax = NULL;
								dynamicShadowParms->shadowVolumeState = & lightDrawSurf->shadowVolumeState;
								
								lightDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED;
								
								dynamicShadowParms->next = vEntity->dynamicShadowVolumes;
								vEntity->dynamicShadowVolumes = dynamicShadowParms;
							}
						}
					}
					lightDrawSurf->ambientCache = tri->ambientCache;
					lightDrawSurf->shadowCache = 0;
					lightDrawSurf->frontEndGeo = tri;
					lightDrawSurf->space = vEntity;
					lightDrawSurf->material = shader;
					lightDrawSurf->extraGLState = 0;
					lightDrawSurf->scissorRect = vLight->scissorRect; // interactionScissor;
					lightDrawSurf->sort = 0.0f;
					lightDrawSurf->renderZFail = 0;
					lightDrawSurf->shaderRegisters = baseDrawSurf->shaderRegisters;
					
					R_SetupDrawSurfJoints( lightDrawSurf, tri, shader );
					
					// Determine which linked list to add the light surface to.
					// There will only be localSurfaces if the light casts shadows and
					// there are surfaces with NOSELFSHADOW.
					if( shader->Coverage() == MC_TRANSLUCENT )
					{
						lightDrawSurf->linkChain = &vLight->translucentInteractions;
					}
					else if( !lightDef->parms.noShadows && shader->TestMaterialFlag( MF_NOSELFSHADOW ) )
					{
						lightDrawSurf->linkChain = &vLight->localInteractions;
					}
					else
					{
						lightDrawSurf->linkChain = &vLight->globalInteractions;
					}
					lightDrawSurf->nextOnLight = vEntity->drawSurfs;
					vEntity->drawSurfs = lightDrawSurf;
				}
			}
			
			//--------------------------
			// surface shadows
			//--------------------------
			
			if( !shader->SurfaceCastsShadow() )
			{
				continue;
			}
			if( !lightDef->LightCastsShadows() )
			{
				continue;
			}
			if( tri->silEdges == NULL )
			{
				continue;		// can happen for beam models (shouldn't use a shadow casting material, though...)
			}
			
			// if the static shadow does not have any shadows
			if( surfInter != NULL && surfInter->numShadowIndexes == 0 && !r_useShadowMapping.GetBool() )
			{
				continue;
			}
			
			// some entities, like view weapons, don't cast any shadows
			if( entityDef->parms.noShadow )
			{
				continue;
			}
			
			// No shadow if it's suppressed for this light.
			if( entityDef->parms.suppressShadowInLightID && entityDef->parms.suppressShadowInLightID == lightDef->parms.lightId )
			{
				continue;
			}
			
			// RB begin
			if( r_useShadowMapping.GetBool() )
			{
				//if( addInteractions && surfaceDirectlyVisible && shader->ReceivesLighting() )
				{
					// static interactions can commonly find that no triangles from a surface
					// contact the light, even when the total model does
					if( surfInter == NULL || surfInter->lightTrisIndexCache > 0 )
					{
						// create a drawSurf for this interaction
						drawSurf_t* shadowDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *shadowDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
						
						if( surfInter != NULL )
						{
							// optimized static interaction
							shadowDrawSurf->numIndexes = surfInter->numLightTrisIndexes;
							shadowDrawSurf->indexCache = surfInter->lightTrisIndexCache;
						}
						else
						{
							// make sure we have an ambient cache and all necessary normals / tangents
							if( !vertexCache.CacheIsCurrent( tri->indexCache ) )
							{
								tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
							}
							
							// throw the entire source surface at it without any per-triangle culling
							shadowDrawSurf->numIndexes = tri->numIndexes;
							shadowDrawSurf->indexCache = tri->indexCache;
						}
						
						if( !vertexCache.CacheIsCurrent( tri->ambientCache ) )
						{
							// we are going to use it for drawing, so make sure we have the tangents and normals
							if( shader->ReceivesLighting() && !tri->tangentsCalculated )
							{
								assert( tri->staticModelWithJoints == NULL );
								R_DeriveTangents( tri );
								
								// RB: this was hit by parametric particle models ..
								//assert( false );	// this should no longer be hit
								// RB end
							}
							tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
						}
						
						shadowDrawSurf->ambientCache = tri->ambientCache;
						shadowDrawSurf->shadowCache = 0;
						shadowDrawSurf->frontEndGeo = tri;
						shadowDrawSurf->space = vEntity;
						shadowDrawSurf->material = shader;
						shadowDrawSurf->extraGLState = 0;
						shadowDrawSurf->scissorRect = vLight->scissorRect; // interactionScissor;
						shadowDrawSurf->sort = 0.0f;
						shadowDrawSurf->renderZFail = 0;
						//shadowDrawSurf->shaderRegisters = baseDrawSurf->shaderRegisters;
						
						R_SetupDrawSurfJoints( shadowDrawSurf, tri, shader );
						
						// determine which linked list to add the shadow surface to
						
						//shadowDrawSurf->linkChain = shader->TestMaterialFlag( MF_NOSELFSHADOW ) ? &vLight->localShadows : &vLight->globalShadows;
						
						shadowDrawSurf->linkChain = &vLight->globalShadows;
						shadowDrawSurf->nextOnLight = vEntity->drawSurfs;
						
						vEntity->drawSurfs = shadowDrawSurf;
						
					}
				}
				
				
				continue;
			}
			// RB end
			
			if( lightDef->parms.prelightModel && lightDef->lightHasMoved == false &&
					entityDef->parms.hModel->IsStaticWorldModel() && !r_skipPrelightShadows.GetBool() )
			{
				// static light / world model shadow interacitons
				// are always captured in the prelight shadow volume
				continue;
			}
			
			// If the shadow is drawn (or translucent), but the model isn't, we must include the shadow caps
			// because we may be able to see into the shadow volume even though the view is outside it.
			// This happens for the player world weapon and possibly some animations in multiplayer.
			const bool forceShadowCaps = !addInteractions || r_forceShadowCaps.GetBool();
			
			drawSurf_t* shadowDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *shadowDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
			
			if( surfInter != NULL )
			{
				shadowDrawSurf->numIndexes = 0;
				shadowDrawSurf->indexCache = surfInter->shadowIndexCache;
				shadowDrawSurf->shadowCache = tri->shadowCache;
				shadowDrawSurf->scissorRect = vLight->scissorRect;		// default to the light scissor and light depth bounds
				shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE;	// assume the shadow volume is done in case r_skipStaticShadows is set
				
				if( !r_skipStaticShadows.GetBool() )
				{
					staticShadowVolumeParms_t* staticShadowParms = ( staticShadowVolumeParms_t* )R_FrameAlloc( sizeof( staticShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS );
					
					staticShadowParms->verts = tri->staticShadowVertexes;
					staticShadowParms->numVerts = tri->numVerts * 2;
					staticShadowParms->indexes = surfInter->shadowIndexes;
					staticShadowParms->numIndexes = surfInter->numShadowIndexes;
					staticShadowParms->numShadowIndicesWithCaps = surfInter->numShadowIndexes;
					staticShadowParms->numShadowIndicesNoCaps = surfInter->numShadowIndexesNoCaps;
					staticShadowParms->triangleBounds = tri->bounds;
					staticShadowParms->triangleMVP = vEntity->mvp;
					staticShadowParms->localLightOrigin = localLightOrigin;
					staticShadowParms->localViewOrigin = localViewOrigin;
					staticShadowParms->zNear = znear;
					staticShadowParms->lightZMin = vLight->scissorRect.zmin;
					staticShadowParms->lightZMax = vLight->scissorRect.zmax;
					staticShadowParms->forceShadowCaps = forceShadowCaps;
					staticShadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool();
					staticShadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool();
					staticShadowParms->numShadowIndices = & shadowDrawSurf->numIndexes;
					staticShadowParms->renderZFail = & shadowDrawSurf->renderZFail;
					staticShadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin;
					staticShadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax;
					staticShadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState;
					
					shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED;
					
					staticShadowParms->next = vEntity->staticShadowVolumes;
					vEntity->staticShadowVolumes = staticShadowParms;
				}
				
			}
			else
			{
				// When CPU skinning the dynamic shadow verts of a dynamic model may not have been copied to buffer memory yet.
				if( !vertexCache.CacheIsCurrent( tri->shadowCache ) )
				{
					assert( !gpuSkinned );	// the shadow cache should be static when using GPU skinning
					// Extracts just the xyz values from a set of full size drawverts, and
					// duplicates them with w set to 0 and 1 for the vertex program to project.
					// This is constant for any number of lights, the vertex program takes care
					// of projecting the verts to infinity for a particular light.
					tri->shadowCache = vertexCache.AllocVertex( NULL, ALIGN( tri->numVerts * 2 * sizeof( idShadowVert ), VERTEX_CACHE_ALIGN ) );
					idShadowVert* shadowVerts = ( idShadowVert* )vertexCache.MappedVertexBuffer( tri->shadowCache );
					idShadowVert::CreateShadowCache( shadowVerts, tri->verts, tri->numVerts );
				}
				
				const int maxShadowVolumeIndexes = tri->numSilEdges * 6 + tri->numIndexes * 2;
				
				shadowDrawSurf->numIndexes = 0;
				shadowDrawSurf->indexCache = vertexCache.AllocIndex( NULL, ALIGN( maxShadowVolumeIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
				shadowDrawSurf->shadowCache = tri->shadowCache;
				shadowDrawSurf->scissorRect = vLight->scissorRect;		// default to the light scissor and light depth bounds
				shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE;	// assume the shadow volume is done in case the index cache allocation failed
				
				// if the index cache was successfully allocated then setup the parms to create a shadow volume in parallel
				if( vertexCache.CacheIsCurrent( shadowDrawSurf->indexCache ) && !r_skipDynamicShadows.GetBool() )
				{
				
					// if the parms were not already allocated for culling interaction triangles to the light frustum
					if( dynamicShadowParms == NULL )
					{
						dynamicShadowParms = ( dynamicShadowVolumeParms_t* )R_FrameAlloc( sizeof( dynamicShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS );
					}
					else
					{
						// the shadow volume will be rendered first so when the interaction surface is drawn the triangles have been culled for sure
						*dynamicShadowParms->shadowVolumeState = SHADOWVOLUME_DONE;
					}
					
					dynamicShadowParms->verts = tri->verts;
					dynamicShadowParms->numVerts = tri->numVerts;
					dynamicShadowParms->indexes = tri->indexes;
					dynamicShadowParms->numIndexes = tri->numIndexes;
					dynamicShadowParms->silEdges = tri->silEdges;
					dynamicShadowParms->numSilEdges = tri->numSilEdges;
					dynamicShadowParms->joints = gpuSkinned ? tri->staticModelWithJoints->jointsInverted : NULL;
					dynamicShadowParms->numJoints = gpuSkinned ? tri->staticModelWithJoints->numInvertedJoints : 0;
					dynamicShadowParms->triangleBounds = tri->bounds;
					dynamicShadowParms->triangleMVP = vEntity->mvp;
					dynamicShadowParms->localLightOrigin = localLightOrigin;
					dynamicShadowParms->localViewOrigin = localViewOrigin;
					idRenderMatrix::Multiply( vLight->lightDef->baseLightProject, entityDef->modelRenderMatrix, dynamicShadowParms->localLightProject );
					dynamicShadowParms->zNear = znear;
					dynamicShadowParms->lightZMin = vLight->scissorRect.zmin;
					dynamicShadowParms->lightZMax = vLight->scissorRect.zmax;
					dynamicShadowParms->cullShadowTrianglesToLight = r_cullDynamicShadowTriangles.GetBool();
					dynamicShadowParms->forceShadowCaps = forceShadowCaps;
					dynamicShadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool();
					dynamicShadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool();
					dynamicShadowParms->tempFacing = NULL;
					dynamicShadowParms->tempCulled = NULL;
					dynamicShadowParms->tempVerts = NULL;
					dynamicShadowParms->indexBuffer = NULL;
					dynamicShadowParms->shadowIndices = ( triIndex_t* )vertexCache.MappedIndexBuffer( shadowDrawSurf->indexCache );
					dynamicShadowParms->maxShadowIndices = maxShadowVolumeIndexes;
					dynamicShadowParms->numShadowIndices = & shadowDrawSurf->numIndexes;
					// dynamicShadowParms->lightIndices may have already been set for the interaction surface
					// dynamicShadowParms->maxLightIndices may have already been set for the interaction surface
					// dynamicShadowParms->numLightIndices may have already been set for the interaction surface
					dynamicShadowParms->renderZFail = & shadowDrawSurf->renderZFail;
					dynamicShadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin;
					dynamicShadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax;
					dynamicShadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState;
					
					shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED;
					
					// if the parms we not already linked for culling interaction triangles to the light frustum
					if( dynamicShadowParms->lightIndices == NULL )
					{
						dynamicShadowParms->next = vEntity->dynamicShadowVolumes;
						vEntity->dynamicShadowVolumes = dynamicShadowParms;
					}
					
					tr.pc.c_createShadowVolumes++;
				}
			}
			
			assert( vertexCache.CacheIsCurrent( shadowDrawSurf->shadowCache ) );
			assert( vertexCache.CacheIsCurrent( shadowDrawSurf->indexCache ) );
			
			shadowDrawSurf->ambientCache = 0;
			shadowDrawSurf->frontEndGeo = NULL;
			shadowDrawSurf->space = vEntity;
			shadowDrawSurf->material = NULL;
			shadowDrawSurf->extraGLState = 0;
			shadowDrawSurf->sort = 0.0f;
			shadowDrawSurf->shaderRegisters = NULL;
			
			R_SetupDrawSurfJoints( shadowDrawSurf, tri, NULL );
			
			// determine which linked list to add the shadow surface to
			shadowDrawSurf->linkChain = shader->TestMaterialFlag( MF_NOSELFSHADOW ) ? &vLight->localShadows : &vLight->globalShadows;
			shadowDrawSurf->nextOnLight = vEntity->drawSurfs;
			vEntity->drawSurfs = shadowDrawSurf;
		}
	}
}
Ejemplo n.º 23
0
/*
================================================================================================
idRenderProgManager::LoadGLSLShader
================================================================================================
*/
GLuint idRenderProgManager::LoadGLSLShader( GLenum target, const char* name, idList<int>& uniforms )
{

	idStr inFile;
	idStr outFileHLSL;
	idStr outFileGLSL;
	idStr outFileUniforms;
	
	// RB: replaced backslashes
	inFile.Format( "renderprogs/%s", name );
	inFile.StripFileExtension();
	outFileHLSL.Format( "renderprogs/hlsl/%s", name );
	outFileHLSL.StripFileExtension();
	outFileGLSL.Format( "renderprogs/glsl/%s", name );
	outFileGLSL.StripFileExtension();
	outFileUniforms.Format( "renderprogs/glsl/%s", name );
	outFileUniforms.StripFileExtension();
	// RB end
	
	if( target == GL_FRAGMENT_SHADER )
	{
		inFile += ".pixel";
		outFileHLSL += "_fragment.hlsl";
		outFileGLSL += "_fragment.glsl";
		outFileUniforms += "_fragment.uniforms";
	}
	else
	{
		inFile += ".vertex";
		outFileHLSL += "_vertex.hlsl";
		outFileGLSL += "_vertex.glsl";
		outFileUniforms += "_vertex.uniforms";
	}
	
	// first check whether we already have a valid GLSL file and compare it to the hlsl timestamp;
	ID_TIME_T hlslTimeStamp;
	int hlslFileLength = fileSystem->ReadFile( inFile.c_str(), NULL, &hlslTimeStamp );
	
	ID_TIME_T glslTimeStamp;
	int glslFileLength = fileSystem->ReadFile( outFileGLSL.c_str(), NULL, &glslTimeStamp );
	
	// if the glsl file doesn't exist or we have a newer HLSL file we need to recreate the glsl file.
	idStr programGLSL;
	idStr programUniforms;
	if( ( glslFileLength <= 0 ) || ( hlslTimeStamp > glslTimeStamp ) )
	{
		if( hlslFileLength <= 0 )
		{
			// hlsl file doesn't even exist bail out
			return false;
		}
		
		void* hlslFileBuffer = NULL;
		int len = fileSystem->ReadFile( inFile.c_str(), &hlslFileBuffer );
		if( len <= 0 )
		{
			return false;
		}
		idStr hlslCode( ( const char* ) hlslFileBuffer );
		idStr programHLSL = StripDeadCode( hlslCode, inFile );
		programGLSL = ConvertCG2GLSL( programHLSL, inFile, target == GL_VERTEX_SHADER, programUniforms );
		
		fileSystem->WriteFile( outFileHLSL, programHLSL.c_str(), programHLSL.Length(), "fs_basepath" );
		fileSystem->WriteFile( outFileGLSL, programGLSL.c_str(), programGLSL.Length(), "fs_basepath" );
		if( r_useUniformArrays.GetBool() )
		{
			fileSystem->WriteFile( outFileUniforms, programUniforms.c_str(), programUniforms.Length(), "fs_basepath" );
		}
	}
	else
	{
		// read in the glsl file
		void* fileBufferGLSL = NULL;
		int lengthGLSL = fileSystem->ReadFile( outFileGLSL.c_str(), &fileBufferGLSL );
		if( lengthGLSL <= 0 )
		{
			idLib::Error( "GLSL file %s could not be loaded and may be corrupt", outFileGLSL.c_str() );
		}
		programGLSL = ( const char* ) fileBufferGLSL;
		Mem_Free( fileBufferGLSL );
		
		if( r_useUniformArrays.GetBool() )
		{
			// read in the uniform file
			void* fileBufferUniforms = NULL;
			int lengthUniforms = fileSystem->ReadFile( outFileUniforms.c_str(), &fileBufferUniforms );
			if( lengthUniforms <= 0 )
			{
				idLib::Error( "uniform file %s could not be loaded and may be corrupt", outFileUniforms.c_str() );
			}
			programUniforms = ( const char* ) fileBufferUniforms;
			Mem_Free( fileBufferUniforms );
		}
	}
	
	// find the uniforms locations in either the vertex or fragment uniform array
	if( r_useUniformArrays.GetBool() )
	{
		uniforms.Clear();
		
		idLexer src( programUniforms, programUniforms.Length(), "uniforms" );
		idToken token;
		while( src.ReadToken( &token ) )
		{
			int index = -1;
			for( int i = 0; i < RENDERPARM_TOTAL && index == -1; i++ )
			{
				const char* parmName = GetGLSLParmName( i );
				if( token == parmName )
				{
					index = i;
				}
			}
			for( int i = 0; i < MAX_GLSL_USER_PARMS && index == -1; i++ )
			{
				const char* parmName = GetGLSLParmName( RENDERPARM_USER + i );
				if( token == parmName )
				{
					index = RENDERPARM_USER + i;
				}
			}
			if( index == -1 )
			{
				idLib::Error( "couldn't find uniform %s for %s", token.c_str(), outFileGLSL.c_str() );
			}
			uniforms.Append( index );
		}
	}
	
	// create and compile the shader
	const GLuint shader = qglCreateShader( target );
	if( shader )
	{
		const char* source[1] = { programGLSL.c_str() };
		
		qglShaderSource( shader, 1, source, NULL );
		qglCompileShader( shader );
		
		int infologLength = 0;
		qglGetShaderiv( shader, GL_INFO_LOG_LENGTH, &infologLength );
		if( infologLength > 1 )
		{
			idTempArray<char> infoLog( infologLength );
			int charsWritten = 0;
			qglGetShaderInfoLog( shader, infologLength, &charsWritten, infoLog.Ptr() );
			
			// catch the strings the ATI and Intel drivers output on success
			if( strstr( infoLog.Ptr(), "successfully compiled to run on hardware" ) != NULL ||
					strstr( infoLog.Ptr(), "No errors." ) != NULL )
			{
				//idLib::Printf( "%s program %s from %s compiled to run on hardware\n", typeName, GetName(), GetFileName() );
			}
			else if( r_displayGLSLCompilerMessages.GetBool() ) // DG:  check for the CVar I added above
			{
				idLib::Printf( "While compiling %s program %s\n", ( target == GL_FRAGMENT_SHADER ) ? "fragment" : "vertex" , inFile.c_str() );
				
				const char separator = '\n';
				idList<idStr> lines;
				lines.Clear();
				idStr source( programGLSL );
				lines.Append( source );
				for( int index = 0, ofs = lines[index].Find( separator ); ofs != -1; index++, ofs = lines[index].Find( separator ) )
				{
					lines.Append( lines[index].c_str() + ofs + 1 );
					lines[index].CapLength( ofs );
				}
				
				idLib::Printf( "-----------------\n" );
				for( int i = 0; i < lines.Num(); i++ )
				{
					idLib::Printf( "%3d: %s\n", i + 1, lines[i].c_str() );
				}
				idLib::Printf( "-----------------\n" );
				
				idLib::Printf( "%s\n", infoLog.Ptr() );
			}
		}
		
		GLint compiled = GL_FALSE;
		qglGetShaderiv( shader, GL_COMPILE_STATUS, &compiled );
		if( compiled == GL_FALSE )
		{
			qglDeleteShader( shader );
			return INVALID_PROGID;
		}
	}
	
	return shader;
}
/*
===================
R_AddSingleLight

May be run in parallel.

Sets vLight->removeFromList to true if the light should be removed from the list.
Builds a chain of entities that need to be added for shadows only off vLight->shadowOnlyViewEntities.
Allocates and fills in vLight->entityInteractionState.

Calc the light shader values, removing any light from the viewLight list
if it is determined to not have any visible effect due to being flashed off or turned off.

Add any precomputed shadow volumes.
===================
*/
static void R_AddSingleLight( viewLight_t* vLight )
{
	// until proven otherwise
	vLight->removeFromList = true;
	vLight->shadowOnlyViewEntities = NULL;
	vLight->preLightShadowVolumes = NULL;
	
	// globals we really should pass in...
	const viewDef_t* viewDef = tr.viewDef;
	
	const idRenderLightLocal* light = vLight->lightDef;
	const idMaterial* lightShader = light->lightShader;
	if( lightShader == NULL )
	{
		common->Error( "R_AddSingleLight: NULL lightShader" );
		return;
	}
	
	SCOPED_PROFILE_EVENT( lightShader->GetName() );
	
	// see if we are suppressing the light in this view
	if( !r_skipSuppress.GetBool() )
	{
		if( light->parms.suppressLightInViewID && light->parms.suppressLightInViewID == viewDef->renderView.viewID )
		{
			return;
		}
		if( light->parms.allowLightInViewID && light->parms.allowLightInViewID != viewDef->renderView.viewID )
		{
			return;
		}
	}
	
	// evaluate the light shader registers
	float* lightRegs = ( float* )R_FrameAlloc( lightShader->GetNumRegisters() * sizeof( float ), FRAME_ALLOC_SHADER_REGISTER );
	lightShader->EvaluateRegisters( lightRegs, light->parms.shaderParms, viewDef->renderView.shaderParms,
									tr.viewDef->renderView.time[0] * 0.001f, light->parms.referenceSound );
									
	// if this is a purely additive light and no stage in the light shader evaluates
	// to a positive light value, we can completely skip the light
	if( !lightShader->IsFogLight() && !lightShader->IsBlendLight() )
	{
		int lightStageNum;
		for( lightStageNum = 0; lightStageNum < lightShader->GetNumStages(); lightStageNum++ )
		{
			const shaderStage_t*	lightStage = lightShader->GetStage( lightStageNum );
			
			// ignore stages that fail the condition
			if( !lightRegs[ lightStage->conditionRegister ] )
			{
				continue;
			}
			
			const int* registers = lightStage->color.registers;
			
			// snap tiny values to zero
			if( lightRegs[ registers[0] ] < 0.001f )
			{
				lightRegs[ registers[0] ] = 0.0f;
			}
			if( lightRegs[ registers[1] ] < 0.001f )
			{
				lightRegs[ registers[1] ] = 0.0f;
			}
			if( lightRegs[ registers[2] ] < 0.001f )
			{
				lightRegs[ registers[2] ] = 0.0f;
			}
			
			if( lightRegs[ registers[0] ] > 0.0f ||
					lightRegs[ registers[1] ] > 0.0f ||
					lightRegs[ registers[2] ] > 0.0f )
			{
				break;
			}
		}
		if( lightStageNum == lightShader->GetNumStages() )
		{
			// we went through all the stages and didn't find one that adds anything
			// remove the light from the viewLights list, and change its frame marker
			// so interaction generation doesn't think the light is visible and
			// create a shadow for it
			return;
		}
	}
	
	//--------------------------------------------
	// copy data used by backend
	//--------------------------------------------
	vLight->globalLightOrigin = light->globalLightOrigin;
	vLight->lightProject[0] = light->lightProject[0];
	vLight->lightProject[1] = light->lightProject[1];
	vLight->lightProject[2] = light->lightProject[2];
	vLight->lightProject[3] = light->lightProject[3];
	
	// the fog plane is the light far clip plane
	idPlane fogPlane(	light->baseLightProject[2][0] - light->baseLightProject[3][0],
						light->baseLightProject[2][1] - light->baseLightProject[3][1],
						light->baseLightProject[2][2] - light->baseLightProject[3][2],
						light->baseLightProject[2][3] - light->baseLightProject[3][3] );
	const float planeScale = idMath::InvSqrt( fogPlane.Normal().LengthSqr() );
	vLight->fogPlane[0] = fogPlane[0] * planeScale;
	vLight->fogPlane[1] = fogPlane[1] * planeScale;
	vLight->fogPlane[2] = fogPlane[2] * planeScale;
	vLight->fogPlane[3] = fogPlane[3] * planeScale;
	
	// copy the matrix for deforming the 'zeroOneCubeModel' to exactly cover the light volume in world space
	vLight->inverseBaseLightProject = light->inverseBaseLightProject;
	
	vLight->falloffImage = light->falloffImage;
	vLight->lightShader = light->lightShader;
	vLight->shaderRegisters = lightRegs;
	
	if( r_useLightScissors.GetInteger() != 0 )
	{
		// Calculate the matrix that projects the zero-to-one cube to exactly cover the
		// light frustum in clip space.
		idRenderMatrix invProjectMVPMatrix;
		idRenderMatrix::Multiply( viewDef->worldSpace.mvp, light->inverseBaseLightProject, invProjectMVPMatrix );
		
		// Calculate the projected bounds, either not clipped at all, near clipped, or fully clipped.
		idBounds projected;
		if( r_useLightScissors.GetInteger() == 1 )
		{
			idRenderMatrix::ProjectedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube );
		}
		else if( r_useLightScissors.GetInteger() == 2 )
		{
			idRenderMatrix::ProjectedNearClippedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube );
		}
		else
		{
			idRenderMatrix::ProjectedFullyClippedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube );
		}
		
		if( projected[0][2] >= projected[1][2] )
		{
			// the light was culled to the view frustum
			return;
		}
		
		float screenWidth = ( float )viewDef->viewport.x2 - ( float )viewDef->viewport.x1;
		float screenHeight = ( float )viewDef->viewport.y2 - ( float )viewDef->viewport.y1;
		
		idScreenRect lightScissorRect;
		lightScissorRect.x1 = idMath::Ftoi( projected[0][0] * screenWidth );
		lightScissorRect.x2 = idMath::Ftoi( projected[1][0] * screenWidth );
		lightScissorRect.y1 = idMath::Ftoi( projected[0][1] * screenHeight );
		lightScissorRect.y2 = idMath::Ftoi( projected[1][1] * screenHeight );
		lightScissorRect.Expand();
		
		vLight->scissorRect.Intersect( lightScissorRect );
		vLight->scissorRect.zmin = projected[0][2];
		vLight->scissorRect.zmax = projected[1][2];
	}
	
	// this one stays on the list
	vLight->removeFromList = false;
	
	//--------------------------------------------
	// create interactions with all entities the light may touch, and add viewEntities
	// that may cast shadows, even if they aren't directly visible.  Any real work
	// will be deferred until we walk through the viewEntities
	//--------------------------------------------
	const int renderViewID = viewDef->renderView.viewID;
	
	// this bool array will be set true whenever the entity will visibly interact with the light
	vLight->entityInteractionState = ( byte* )R_ClearedFrameAlloc( light->world->entityDefs.Num() * sizeof( vLight->entityInteractionState[0] ), FRAME_ALLOC_INTERACTION_STATE );
	
	const bool lightCastsShadows = light->LightCastsShadows();
	idInteraction * * const interactionTableRow = light->world->interactionTable + light->index * light->world->interactionTableWidth;
	
	for( areaReference_t* lref = light->references; lref != NULL; lref = lref->ownerNext )
	{
		portalArea_t* area = lref->area;
		
		// some lights have their center of projection outside the world, but otherwise
		// we want to ignore areas that are not connected to the light center due to a closed door
		if( light->areaNum != -1 && r_useAreasConnectedForShadowCulling.GetInteger() == 2 )
		{
			if( !light->world->AreasAreConnected( light->areaNum, area->areaNum, PS_BLOCK_VIEW ) )
			{
				// can't possibly be seen or shadowed
				continue;
			}
		}
		
		// check all the models in this area
		for( areaReference_t* eref = area->entityRefs.areaNext; eref != &area->entityRefs; eref = eref->areaNext )
		{
			idRenderEntityLocal* edef = eref->entity;
			
			if( vLight->entityInteractionState[ edef->index ] != viewLight_t::INTERACTION_UNCHECKED )
			{
				continue;
			}
			// until proven otherwise
			vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_NO;
			
			// The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree()
			const idInteraction* inter = interactionTableRow[ edef->index ];
			
			const renderEntity_t& eParms = edef->parms;
			const idRenderModel* eModel = eParms.hModel;
			
			// a large fraction of static entity / light pairs will still have no interactions even though
			// they are both present in the same area(s)
			if( eModel != NULL && !eModel->IsDynamicModel() && inter == INTERACTION_EMPTY )
			{
				// the interaction was statically checked, and it didn't generate any surfaces,
				// so there is no need to force the entity onto the view list if it isn't
				// already there
				continue;
			}
			
			// We don't want the lights on weapons to illuminate anything else.
			// There are two assumptions here -- that allowLightInViewID is only
			// used for weapon lights, and that all weapons will have weaponDepthHack.
			// A more general solution would be to have an allowLightOnEntityID field.
			// HACK: the armor-mounted flashlight is a private spot light, which is probably
			// wrong -- you would expect to see them in multiplayer.
			if( light->parms.allowLightInViewID && light->parms.pointLight && !eParms.weaponDepthHack )
			{
				continue;
			}
			
			// non-shadow casting entities don't need to be added if they aren't
			// directly visible
			if( ( eParms.noShadow || ( eModel && !eModel->ModelHasShadowCastingSurfaces() ) ) && !edef->IsDirectlyVisible() )
			{
				continue;
			}
			
			// if the model doesn't accept lighting or cast shadows, it doesn't need to be added
			if( eModel && !eModel->ModelHasInteractingSurfaces() && !eModel->ModelHasShadowCastingSurfaces() )
			{
				continue;
			}
			
			// no interaction present, so either the light or entity has moved
			// assert( lightHasMoved || edef->entityHasMoved );
			if( inter == NULL )
			{
				// some big outdoor meshes are flagged to not create any dynamic interactions
				// when the level designer knows that nearby moving lights shouldn't actually hit them
				if( eParms.noDynamicInteractions )
				{
					continue;
				}
				
				// do a check of the entity reference bounds against the light frustum to see if they can't
				// possibly interact, despite sharing one or more world areas
				if( R_CullModelBoundsToLight( light, edef->localReferenceBounds, edef->modelRenderMatrix ) )
				{
					continue;
				}
			}
			
			// we now know that the entity and light do overlap
			
			if( edef->IsDirectlyVisible() )
			{
				// entity is directly visible, so the interaction is definitely needed
				vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_YES;
				continue;
			}
			
			// the entity is not directly visible, but if we can tell that it may cast
			// shadows onto visible surfaces, we must make a viewEntity for it
			if( !lightCastsShadows )
			{
				// surfaces are never shadowed in this light
				continue;
			}
			// if we are suppressing its shadow in this view (player shadows, etc), skip
			if( !r_skipSuppress.GetBool() )
			{
				if( eParms.suppressShadowInViewID && eParms.suppressShadowInViewID == renderViewID )
				{
					continue;
				}
				if( eParms.suppressShadowInLightID && eParms.suppressShadowInLightID == light->parms.lightId )
				{
					continue;
				}
			}
			
			// should we use the shadow bounds from pre-calculated interactions?
			idBounds shadowBounds;
			R_ShadowBounds( edef->globalReferenceBounds, light->globalLightBounds, light->globalLightOrigin, shadowBounds );
			
			// this test is pointless if we knew the light was completely contained
			// in the view frustum, but the entity would also be directly visible in most
			// of those cases.
			
			// this doesn't say that the shadow can't effect anything, only that it can't
			// effect anything in the view, so we shouldn't set up a view entity
			if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, shadowBounds ) )
			{
				continue;
			}
			
			// debug tool to allow viewing of only one entity at a time
			if( r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != edef->index )
			{
				continue;
			}
			
			// we do need it for shadows
			vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_YES;
			
			// we will need to create a viewEntity_t for it in the serial code section
			shadowOnlyEntity_t* shadEnt = ( shadowOnlyEntity_t* )R_FrameAlloc( sizeof( shadowOnlyEntity_t ), FRAME_ALLOC_SHADOW_ONLY_ENTITY );
			shadEnt->next = vLight->shadowOnlyViewEntities;
			shadEnt->edef = edef;
			vLight->shadowOnlyViewEntities = shadEnt;
		}
	}
	
	//--------------------------------------------
	// add the prelight shadows for the static world geometry
	//--------------------------------------------
	if( light->parms.prelightModel != NULL )
	{
		srfTriangles_t* tri = light->parms.prelightModel->Surface( 0 )->geometry;
		
		// these shadows will have valid bounds, and can be culled normally,
		// but they will typically cover most of the light's bounds
		if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, tri->bounds ) )
		{
			return;
		}
		
		// prelight models should always have static data that never gets purged
		assert( vertexCache.CacheIsCurrent( tri->shadowCache ) );
		assert( vertexCache.CacheIsCurrent( tri->indexCache ) );
		
		drawSurf_t* shadowDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *shadowDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
		
		shadowDrawSurf->frontEndGeo = tri;
		shadowDrawSurf->ambientCache = 0;
		shadowDrawSurf->indexCache = tri->indexCache;
		shadowDrawSurf->shadowCache = tri->shadowCache;
		shadowDrawSurf->jointCache = 0;
		shadowDrawSurf->numIndexes = 0;
		shadowDrawSurf->space = &viewDef->worldSpace;
		shadowDrawSurf->material = NULL;
		shadowDrawSurf->extraGLState = 0;
		shadowDrawSurf->shaderRegisters = NULL;
		shadowDrawSurf->scissorRect = vLight->scissorRect;		// default to the light scissor and light depth bounds
		shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE;	// assume the shadow volume is done in case r_skipPrelightShadows is set
		
		if( !r_skipPrelightShadows.GetBool() )
		{
			preLightShadowVolumeParms_t* shadowParms = ( preLightShadowVolumeParms_t* )R_FrameAlloc( sizeof( shadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS );
			
			shadowParms->verts = tri->preLightShadowVertexes;
			shadowParms->numVerts = tri->numVerts * 2;
			shadowParms->indexes = tri->indexes;
			shadowParms->numIndexes = tri->numIndexes;
			shadowParms->triangleBounds = tri->bounds;
			shadowParms->triangleMVP = viewDef->worldSpace.mvp;
			shadowParms->localLightOrigin = vLight->globalLightOrigin;
			shadowParms->localViewOrigin = viewDef->renderView.vieworg;
			shadowParms->zNear = r_znear.GetFloat();
			shadowParms->lightZMin = vLight->scissorRect.zmin;
			shadowParms->lightZMax = vLight->scissorRect.zmax;
			shadowParms->forceShadowCaps = r_forceShadowCaps.GetBool();
			shadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool();
			shadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool();
			shadowParms->numShadowIndices = & shadowDrawSurf->numIndexes;
			shadowParms->renderZFail = & shadowDrawSurf->renderZFail;
			shadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin;
			shadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax;
			shadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState;
			
			// the pre-light shadow volume "_prelight_light_3297" in "d3xpdm2" is malformed in that it contains the light origin so the precise inside test always fails
			if( tr.primaryWorld->mapName.IcmpPath( "maps/game/mp/d3xpdm2.map" ) == 0 && idStr::Icmp( light->parms.prelightModel->Name(), "_prelight_light_3297" ) == 0 )
			{
				shadowParms->useShadowPreciseInsideTest = false;
			}
			
			shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED;
			
			shadowParms->next = vLight->preLightShadowVolumes;
			vLight->preLightShadowVolumes = shadowParms;
		}
		
		// actually link it in
		shadowDrawSurf->nextOnLight = vLight->globalShadows;
		vLight->globalShadows = shadowDrawSurf;
	}
}
Ejemplo n.º 25
0
/*
========================
StripDeadCode
========================
*/
idStr StripDeadCode( const idStr& in, const char* name )
{
	if( r_skipStripDeadCode.GetBool() )
	{
		return in;
	}
	
	//idLexer src( LEXFL_NOFATALERRORS );
	idParser src( LEXFL_NOFATALERRORS );
	src.LoadMemory( in.c_str(), in.Length(), name );
	src.AddDefine( "PC" );
	
	idList< idCGBlock, TAG_RENDERPROG > blocks;
	
	blocks.SetNum( 100 );
	
	idToken token;
	while( !src.EndOfFile() )
	{
		idCGBlock& block = blocks.Alloc();
		// read prefix
		while( src.ReadToken( &token ) )
		{
			bool found = false;
			for( int i = 0; i < numPrefixes; i++ )
			{
				if( token == prefixes[i] )
				{
					found = true;
					break;
				}
			}
			if( !found )
			{
				for( int i = 0; i < numTypes; i++ )
				{
					if( token == types[i] )
					{
						found = true;
						break;
					}
					int typeLen = idStr::Length( types[i] );
					if( token.Cmpn( types[i], typeLen ) == 0 )
					{
						for( int j = 0; j < numTypePosts; j++ )
						{
							if( idStr::Cmp( token.c_str() + typeLen, typePosts[j] ) == 0 )
							{
								found = true;
								break;
							}
						}
						if( found )
						{
							break;
						}
					}
				}
			}
			if( found )
			{
				if( block.prefix.Length() > 0 && token.WhiteSpaceBeforeToken() )
				{
					block.prefix += ' ';
				}
				block.prefix += token;
			}
			else
			{
				src.UnreadToken( &token );
				break;
			}
		}
		if( !src.ReadToken( &token ) )
		{
			blocks.SetNum( blocks.Num() - 1 );
			break;
		}
		block.name = token;
		
		if( src.PeekTokenString( "=" ) || src.PeekTokenString( ":" ) || src.PeekTokenString( "[" ) )
		{
			src.ReadToken( &token );
			block.postfix = token;
			while( src.ReadToken( &token ) )
			{
				if( token == ";" )
				{
					block.postfix += ';';
					break;
				}
				else
				{
					if( token.WhiteSpaceBeforeToken() )
					{
						block.postfix += ' ';
					}
					block.postfix += token;
				}
			}
		}
		else if( src.PeekTokenString( "(" ) )
		{
			idStr parms, body;
			src.ParseBracedSection( parms, -1, true, '(', ')' );
			if( src.CheckTokenString( ";" ) )
			{
				block.postfix = parms + ";";
			}
			else
			{
				src.ParseBracedSection( body, -1, true, '{', '}' );
				block.postfix = parms + " " + body;
			}
		}
		else if( src.PeekTokenString( "{" ) )
		{
			src.ParseBracedSection( block.postfix, -1, true, '{', '}' );
			if( src.CheckTokenString( ";" ) )
			{
				block.postfix += ';';
			}
		}
		else if( src.CheckTokenString( ";" ) )
		{
			block.postfix = idStr( ';' );
		}
		else
		{
			src.Warning( "Could not strip dead code -- unknown token %s\n", token.c_str() );
			return in;
		}
	}
	
	idList<int, TAG_RENDERPROG> stack;
	for( int i = 0; i < blocks.Num(); i++ )
	{
		blocks[i].used = ( ( blocks[i].name == "main" )
						   || blocks[i].name.Right( 4 ) == "_ubo"
						 );
						 
		if( blocks[i].name == "include" )
		{
			blocks[i].used = true;
			blocks[i].name = ""; // clear out the include tag
		}
		
		if( blocks[i].used )
		{
			stack.Append( i );
		}
	}
	
	while( stack.Num() > 0 )
	{
		int i = stack[stack.Num() - 1];
		stack.SetNum( stack.Num() - 1 );
		
		idLexer src( LEXFL_NOFATALERRORS );
		src.LoadMemory( blocks[i].postfix.c_str(), blocks[i].postfix.Length(), name );
		while( src.ReadToken( &token ) )
		{
			for( int j = 0; j < blocks.Num(); j++ )
			{
				if( !blocks[j].used )
				{
					if( token == blocks[j].name )
					{
						blocks[j].used = true;
						stack.Append( j );
					}
				}
			}
		}
	}
	
	idStr out;
	
	for( int i = 0; i < blocks.Num(); i++ )
	{
		if( blocks[i].used )
		{
			out += blocks[i].prefix;
			out += ' ';
			out += blocks[i].name;
			out += ' ';
			out += blocks[i].postfix;
			out += '\n';
		}
	}
	
	return out;
}
/*
=================
R_AddLights
=================
*/
void R_AddLights()
{
	SCOPED_PROFILE_EVENT( "R_AddLights" );
	
	//-------------------------------------------------
	// check each light individually, possibly in parallel
	//-------------------------------------------------
	
	if( r_useParallelAddLights.GetBool() )
	{
		for( viewLight_t* vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next )
		{
			tr.frontEndJobList->AddJob( ( jobRun_t )R_AddSingleLight, vLight );
		}
		tr.frontEndJobList->Submit();
		tr.frontEndJobList->Wait();
	}
	else
	{
		for( viewLight_t* vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next )
		{
			R_AddSingleLight( vLight );
		}
	}
	
	//-------------------------------------------------
	// cull lights from the list if they turned out to not be needed
	//-------------------------------------------------
	
	tr.pc.c_viewLights = 0;
	viewLight_t** ptr = &tr.viewDef->viewLights;
	while( *ptr != NULL )
	{
		viewLight_t* vLight = *ptr;
		
		if( vLight->removeFromList )
		{
			vLight->lightDef->viewCount = -1;	// this probably doesn't matter with current code
			*ptr = vLight->next;
			continue;
		}
		
		ptr = &vLight->next;
		
		// serial work
		tr.pc.c_viewLights++;
		
		for( shadowOnlyEntity_t* shadEnt = vLight->shadowOnlyViewEntities; shadEnt != NULL; shadEnt = shadEnt->next )
		{
			// this will add it to the viewEntities list, but with an empty scissor rect
			R_SetEntityDefViewEntity( shadEnt->edef );
		}
		
		if( r_showLightScissors.GetBool() )
		{
			R_ShowColoredScreenRect( vLight->scissorRect, vLight->lightDef->index );
		}
	}
	
	//-------------------------------------------------
	// Add jobs to setup pre-light shadow volumes.
	//-------------------------------------------------
	
	if( r_useParallelAddShadows.GetInteger() == 1 )
	{
		for( viewLight_t* vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next )
		{
			for( preLightShadowVolumeParms_t* shadowParms = vLight->preLightShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next )
			{
				tr.frontEndJobList->AddJob( ( jobRun_t )PreLightShadowVolumeJob, shadowParms );
			}
			vLight->preLightShadowVolumes = NULL;
		}
	}
	else
	{
		int start = Sys_Microseconds();
		
		for( viewLight_t* vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next )
		{
			for( preLightShadowVolumeParms_t* shadowParms = vLight->preLightShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next )
			{
				PreLightShadowVolumeJob( shadowParms );
			}
			vLight->preLightShadowVolumes = NULL;
		}
		
		int end = Sys_Microseconds();
		backEnd.pc.shadowMicroSec += end - start;
	}
}
Ejemplo n.º 27
0
/*
========================
idMenuScreen_HUD::UpdateCursorState
========================
*/
void idMenuScreen_HUD::UpdateCursorState()
{

	if( !cursorTalking && !cursorInCombat && !cursorGrabber && !cursorItem )
	{
	
		cursorNone = true;
		cursorState = CURSOR_NONE;
		
		// hide all cursors
		if( combatCursor )
		{
			combatCursor->StopFrame( 1 );
			combatCursor->SetVisible( false );
		}
		
		if( talkCursor )
		{
			talkCursor->StopFrame( 1 );
			talkCursor->SetVisible( false );
		}
		
		if( grabberCursor )
		{
			grabberCursor->StopFrame( 1 );
			grabberCursor->SetVisible( false );
		}
		
	}
	else
	{
	
		if( cursorTalking )
		{
		
			if( cursorTalking == 1 )  	// ready to talk
			{
			
			}
			else if( cursorTalking == 2 )      // already talking / busy
			{
			
			}
			
			if( cursorState != CURSOR_TALK )
			{
			
				if( combatCursor )
				{
					combatCursor->StopFrame( 1 );
					combatCursor->SetVisible( false );
				}
				
				if( grabberCursor )
				{
					grabberCursor->StopFrame( 1 );
					grabberCursor->SetVisible( false );
				}
				
				// play roll on
				if( talkCursor )
				{
					talkCursor->SetVisible( true );
					talkCursor->PlayFrame( 2 );
					
					idSWFSpriteInstance* topBacking = talkCursor->GetScriptObject()->GetNestedSprite( "backing", "topBar" );
					idSWFSpriteInstance* bottomBacking = talkCursor->GetScriptObject()->GetNestedSprite( "backing", "botBar" );
					
					idSWFTextInstance* txtAction = talkCursor->GetScriptObject()->GetNestedText( "info", "txtAction" );
					idSWFTextInstance* txtFocus = talkCursor->GetScriptObject()->GetNestedText( "info", "txtFocus" );
					
					idSWFTextInstance* txtPrompt = talkCursor->GetScriptObject()->GetNestedText( "talkPrompt", "txtPrompt" );
					
					if( txtAction )
					{
					
						if( !in_useJoystick.GetBool() )
						{
							txtAction->tooltip = true;
							keyBindings_t bind = idKeyInput::KeyBindingsFromBinding( "_use", true );
							idStr actionText = idLocalization::GetString( cursorAction );
							if( !bind.mouse.IsEmpty() )
							{
								actionText.Append( " [" );
								actionText.Append( bind.mouse );
								actionText.Append( "]" );
							}
							else if( !bind.keyboard.IsEmpty() )
							{
								actionText.Append( " [" );
								actionText.Append( bind.keyboard );
								actionText.Append( "]" );
							}
							
							txtAction->SetText( actionText );
						}
						else
						{
							txtAction->tooltip = false;
							txtAction->SetText( cursorAction );
						}
						txtAction->SetStrokeInfo( true, 0.75f, 1.5f );
						float actionLength = txtAction->GetTextLength();
						
						if( topBacking )
						{
							if( !cursorAction.IsEmpty() )
							{
								topBacking->SetXPos( actionLength );
							}
							else
							{
								topBacking->SetXPos( -75.0f );
							}
						}
					}
					
					if( txtFocus )
					{
						txtFocus->SetText( cursorFocus );
						txtFocus->SetStrokeInfo( true, 0.75f, 1.5f );
						float focusLength = txtFocus->GetTextLength();
						
						if( bottomBacking )
						{
							if( !cursorFocus.IsEmpty() )
							{
								bottomBacking->SetXPos( focusLength );
							}
							else
							{
								bottomBacking->SetXPos( -75.0f );
							}
						}
					}
					
					if( txtPrompt )
					{
						if( in_useJoystick.GetBool() )
						{
							txtPrompt->tooltip = true;
							txtPrompt->SetText( "_use" );
						}
						else
						{
							txtPrompt->tooltip = false;
							txtPrompt->SetText( "" );
						}
					}
				}
				cursorState = CURSOR_TALK;
			}
			
		}
		else if( cursorGrabber )
		{
		
			if( talkCursor )
			{
				talkCursor->StopFrame( 1 );
				talkCursor->SetVisible( false );
			}
			
			if( combatCursor )
			{
				combatCursor->StopFrame( 1 );
				combatCursor->SetVisible( false );
			}
			
			if( cursorState != CURSOR_GRABBER )
			{
				if( grabberCursor )
				{
					grabberCursor->SetVisible( true );
					grabberCursor->PlayFrame( "loop" );
				}
			}
			
			cursorState = CURSOR_GRABBER;
			
		}
		else if( cursorItem )
		{
		
			cursorState = CURSOR_ITEM;
			
		}
		else if( cursorInCombat )
		{
		
			if( cursorState == CURSOR_TALK )
			{
				if( talkCursor )
				{
					talkCursor->StopFrame( 1 );
					talkCursor->SetVisible( false );
				}
				
				if( combatCursor )
				{
					combatCursor->SetVisible( true );
					combatCursor->PlayFrame( "rollOn" );
				}
				
				// play cursor roll on
			}
			else if( cursorState != CURSOR_IN_COMBAT )
			{
			
				if( grabberCursor )
				{
					grabberCursor->StopFrame( 1 );
					grabberCursor->SetVisible( false );
				}
				
				// set cursor visible
				if( combatCursor )
				{
					combatCursor->SetVisible( true );
					combatCursor->StopFrame( 2 );
				}
				
			}
			
			cursorState = CURSOR_IN_COMBAT;
			
		}
	}
}
Ejemplo n.º 28
0
/*
========================
idSoundWorldLocal::Update
========================
*/
void idSoundWorldLocal::Update()
{

	if( s_noSound.GetBool() )
	{
		return;
	}
	
	// ------------------
	// Update emitters
	//
	// Only loop through the list once to avoid extra cache misses
	// ------------------
	
	// The naming convention is weird here because we reuse the name "channel"
	// An idSoundChannel is a channel on an emitter, which may have an explicit channel assignment or SND_CHANNEL_ANY
	// A hardware channel is a channel from the sound file itself (IE: left, right, LFE)
	// We only allow MAX_HARDWARE_CHANNELS channels, which may wind up being a smaller number of idSoundChannels
	idStaticList< idActiveChannel, MAX_HARDWARE_VOICES > activeEmitterChannels;
	int	maxEmitterChannels = s_maxEmitterChannels.GetInteger() + 1;	// +1 to leave room for insert-before-sort
	if( maxEmitterChannels > MAX_HARDWARE_VOICES )
	{
		maxEmitterChannels = MAX_HARDWARE_VOICES;
	}
	
	int activeHardwareChannels = 0;
	int	totalHardwareChannels = 0;
	int	totalEmitterChannels = 0;
	
	int currentTime = GetSoundTime();
	for( int e = emitters.Num() - 1; e >= 0; e-- )
	{
		// check for freeing a one-shot emitter that is finished playing
		if( emitters[e]->CheckForCompletion( currentTime ) )
		{
			// do a fast list collapse by swapping the last element into
			// the slot we are deleting
			emitters[e]->Reset();
			emitterAllocator.Free( emitters[e] );
			int lastEmitter = emitters.Num() - 1;
			if( e != lastEmitter )
			{
				emitters[e] = emitters[lastEmitter];
				emitters[e]->index = e;
			}
			emitters.SetNum( lastEmitter );
			continue;
		}
		
		emitters[e]->Update( currentTime );
		
		totalEmitterChannels += emitters[e]->channels.Num();
		
		// sort the active channels into the hardware list
		for( int i = 0; i < emitters[e]->channels.Num(); i++ )
		{
			idSoundChannel* channel = emitters[e]->channels[i];
			
			// check if this channel contributes at all
			const bool canMute = channel->CanMute();
			if( canMute && channel->volumeDB <= DB_SILENCE )
			{
#if !defined(USE_OPENAL)
				channel->Mute();
				continue;
#endif
			}
			
			// Calculate the sort key.
			// VO can't be stopped and restarted accurately, so always keep VO channels by adding a large value to the sort key.
			const int sortKey = idMath::Ftoi( channel->volumeDB * 100.0f + ( canMute ? 0.0f : 100000.0f ) );
			
			// Keep track of the total number of hardware channels.
			// This is done after calculating the sort key to avoid a load-hit-store that
			// would occur when using the sort key in the loop below after the Ftoi above.
			const int sampleChannels = channel->leadinSample->NumChannels();
			totalHardwareChannels += sampleChannels;
			
			// Find the location to insert this channel based on the sort key.
			int insertIndex = 0;
			for( insertIndex = 0; insertIndex < activeEmitterChannels.Num(); insertIndex++ )
			{
				if( sortKey > activeEmitterChannels[insertIndex].sortKey )
				{
					break;
				}
			}
			
			// Only insert at the end if there is room.
			if( insertIndex == activeEmitterChannels.Num() )
			{
				// Always leave one spot free in the 'activeEmitterChannels' so there is room to insert sort a potentially louder sound later.
				if( activeEmitterChannels.Num() + 1 >= activeEmitterChannels.Max() || activeHardwareChannels + sampleChannels > MAX_HARDWARE_CHANNELS )
				{
					// We don't have enough voices to play this, so mute it if it was playing.
					channel->Mute();
					continue;
				}
			}
			
			// We want to insert the sound at this point.
			activeEmitterChannels.Insert( idActiveChannel( channel, sortKey ), insertIndex );
			activeHardwareChannels += sampleChannels;
			
			// If we are over our voice limit or at our channel limit, mute sounds until it fits.
			// If activeEmitterChannels is full, always remove the last one so there is room to insert sort a potentially louder sound later.
			while( activeEmitterChannels.Num() == maxEmitterChannels || activeHardwareChannels > MAX_HARDWARE_CHANNELS )
			{
				const int indexToRemove = activeEmitterChannels.Num() - 1;
				idSoundChannel* const channelToMute = activeEmitterChannels[ indexToRemove ].channel;
				channelToMute->Mute();
				activeHardwareChannels -= channelToMute->leadinSample->NumChannels();
				activeEmitterChannels.RemoveIndex( indexToRemove );
			}
		}
	}
	
	const float secondsPerFrame = 1.0f / com_engineHz_latched;
	
	// ------------------
	// In the very common case of having more sounds that would contribute to the
	// mix than there are available hardware voices, it can be an audible discontinuity
	// when a channel initially gets a voice or loses a voice.
	// To avoid this, make sure that the last few hardware voices are mixed with a volume
	// of zero, so they won't make a difference as they come and go.
	// It isn't obvious what the exact best volume ramping method should be, just that
	// it smoothly change frame to frame.
	// ------------------
	const int uncushionedChannels = maxEmitterChannels - s_cushionFadeChannels.GetInteger();
	currentCushionDB = AdjustForCushionChannels( activeEmitterChannels, uncushionedChannels,
					   currentCushionDB, s_cushionFadeRate.GetFloat() * secondsPerFrame );
					   
	// ------------------
	// Update Hardware
	// ------------------
	shakeAmp = 0.0f;
	
	idStr showVoiceTable;
	bool showVoices = s_showVoices.GetBool();
	if( showVoices )
	{
		showVoiceTable.Format( "currentCushionDB: %5.1f  freeVoices: %i zombieVoices: %i buffers:%i/%i\n", currentCushionDB,
							   soundSystemLocal.hardware.GetNumFreeVoices(), soundSystemLocal.hardware.GetNumZombieVoices(),
							   soundSystemLocal.activeStreamBufferContexts.Num(), soundSystemLocal.freeStreamBufferContexts.Num() );
	}
	for( int i = 0; i < activeEmitterChannels.Num(); i++ )
	{
		idSoundChannel* chan = activeEmitterChannels[i].channel;
		chan->UpdateHardware( 0.0f, currentTime );
		
		if( showVoices )
		{
			idStr voiceLine;
			voiceLine.Format( "%5.1f db [%3i:%2i] %s", chan->volumeDB, chan->emitter->index, chan->logicalChannel, chan->CanMute() ? "" : " <CANT MUTE>\n" );
			idSoundSample* leadinSample = chan->leadinSample;
			idSoundSample* loopingSample = chan->loopingSample;
			if( loopingSample == NULL )
			{
				voiceLine.Append( va( "%ikhz*%i %s\n", leadinSample->SampleRate() / 1000, leadinSample->NumChannels(), leadinSample->GetName() ) );
			}
			else if( loopingSample == leadinSample )
			{
				voiceLine.Append( va( "%ikhz*%i <LOOPING> %s\n", leadinSample->SampleRate() / 1000, leadinSample->NumChannels(), leadinSample->GetName() ) );
			}
			else
			{
				voiceLine.Append( va( "%ikhz*%i %s | %ikhz*%i %s\n", leadinSample->SampleRate() / 1000, leadinSample->NumChannels(), leadinSample->GetName(), loopingSample->SampleRate() / 1000, loopingSample->NumChannels(), loopingSample->GetName() ) );
			}
			showVoiceTable += voiceLine;
		}
		
		// Calculate shakes
		if( chan->hardwareVoice == NULL )
		{
			continue;
		}
		
		shakeAmp += chan->parms.shakes * chan->hardwareVoice->GetGain() * chan->currentAmplitude;
	}
	if( showVoices )
	{
		static idOverlayHandle handle;
		console->PrintOverlay( handle, JUSTIFY_LEFT, showVoiceTable.c_str() );
	}
	
	if( s_drawSounds.GetBool() && renderWorld != NULL )
	{
		for( int e = 0; e < emitters.Num(); e++ )
		{
			idSoundEmitterLocal* emitter = emitters[e];
			bool audible = false;
			float maxGain = 0.0f;
			for( int c = 0; c < emitter->channels.Num(); c++ )
			{
				if( emitter->channels[c]->hardwareVoice != NULL )
				{
					audible = true;
					maxGain = Max( maxGain, emitter->channels[c]->hardwareVoice->GetGain() );
				}
			}
			if( !audible )
			{
				continue;
			}
			
			static const int lifetime = 20;
			
			idBounds ref;
			ref.Clear();
			ref.AddPoint( idVec3( -10.0f ) );
			ref.AddPoint( idVec3( 10.0f ) );
			
			// draw a box
			renderWorld->DebugBounds( idVec4( maxGain, maxGain, 1.0f, 1.0f ), ref, emitter->origin, lifetime );
			if( emitter->origin != emitter->spatializedOrigin )
			{
				renderWorld->DebugLine( idVec4( 1.0f, 0.0f, 0.0f, 1.0f ), emitter->origin, emitter->spatializedOrigin, lifetime );
			}
			
			// draw the index
			idVec3 textPos = emitter->origin;
			textPos.z -= 8;
			renderWorld->DrawText( va( "%i", e ), textPos, 0.1f, idVec4( 1, 0, 0, 1 ), listener.axis, 1, lifetime );
			textPos.z += 8;
			
			// run through all the channels
			for( int k = 0; k < emitter->channels.Num(); k++ )
			{
				idSoundChannel* chan = emitter->channels[k];
				float	min = chan->parms.minDistance;
				float	max = chan->parms.maxDistance;
				const char* defaulted = chan->leadinSample->IsDefault() ? " *DEFAULTED*" : "";
				idStr text;
				text.Format( "%s (%i %i/%i)%s", chan->soundShader->GetName(), idMath::Ftoi( emitter->spatializedDistance ), idMath::Ftoi( min ), idMath::Ftoi( max ), defaulted );
				renderWorld->DrawText( text, textPos, 0.1f, idVec4( 1, 0, 0, 1 ), listener.axis, 1, lifetime );
				textPos.z += 8;
			}
		}
	}
}
Ejemplo n.º 29
0
/*
========================
idJobThread::Run
========================
*/
int idJobThread::Run()
{
	threadJobListState_t threadJobListState[MAX_JOBLISTS];
	int numJobLists = 0;
	int lastStalledJobList = -1;
	
	while( !IsTerminating() )
	{
	
		// fetch any new job lists and add them to the local list
		if( numJobLists < MAX_JOBLISTS && firstJobList < lastJobList )
		{
			threadJobListState[numJobLists].jobList = jobLists[firstJobList & ( MAX_JOBLISTS - 1 )].jobList;
			threadJobListState[numJobLists].version = jobLists[firstJobList & ( MAX_JOBLISTS - 1 )].version;
			threadJobListState[numJobLists].signalIndex = 0;
			threadJobListState[numJobLists].lastJobIndex = 0;
			threadJobListState[numJobLists].nextJobIndex = -1;
			numJobLists++;
			firstJobList++;
		}
		if( numJobLists == 0 )
		{
			break;
		}
		
		int currentJobList = 0;
		jobListPriority_t priority = JOBLIST_PRIORITY_NONE;
		if( lastStalledJobList < 0 )
		{
			// find the job list with the highest priority
			for( int i = 0; i < numJobLists; i++ )
			{
				if( threadJobListState[i].jobList->GetPriority() > priority && !threadJobListState[i].jobList->WaitForOtherJobList() )
				{
					priority = threadJobListState[i].jobList->GetPriority();
					currentJobList = i;
				}
			}
		}
		else
		{
			// try to hide the stall with a job from a list that has equal or higher priority
			currentJobList = lastStalledJobList;
			priority = threadJobListState[lastStalledJobList].jobList->GetPriority();
			for( int i = 0; i < numJobLists; i++ )
			{
				if( i != lastStalledJobList && threadJobListState[i].jobList->GetPriority() >= priority && !threadJobListState[i].jobList->WaitForOtherJobList() )
				{
					priority = threadJobListState[i].jobList->GetPriority();
					currentJobList = i;
				}
			}
		}
		
		// if the priority is high then try to run through the whole list to reduce the overhead
		// otherwise run a single job and re-evaluate priorities for the next job
		bool singleJob = ( priority == JOBLIST_PRIORITY_HIGH ) ? false : jobs_prioritize.GetBool();
		
		// try running one or more jobs from the current job list
		int result = threadJobListState[currentJobList].jobList->RunJobs( threadNum, threadJobListState[currentJobList], singleJob );
		
		if( ( result & idParallelJobList_Threads::RUN_DONE ) != 0 )
		{
			// done with this job list so remove it from the local list
			for( int i = currentJobList; i < numJobLists - 1; i++ )
			{
				threadJobListState[i] = threadJobListState[i + 1];
			}
			numJobLists--;
			lastStalledJobList = -1;
		}
		else if( ( result & idParallelJobList_Threads::RUN_STALLED ) != 0 )
		{
			// yield when stalled on the same job list again without making any progress
			if( currentJobList == lastStalledJobList )
			{
				if( ( result & idParallelJobList_Threads::RUN_PROGRESS ) == 0 )
				{
					Sys_Yield();
				}
			}
			lastStalledJobList = currentJobList;
		}
		else
		{
			lastStalledJobList = -1;
		}
	}
	return 0;
}
Ejemplo n.º 30
0
/*
========================
idSoundVoice_OpenAL::Create
========================
*/
void idSoundVoice_OpenAL::Create( const idSoundSample* leadinSample_, const idSoundSample* loopingSample_ )
{
	if( IsPlaying() )
	{
		// This should never hit
		Stop();
		return;
	}
	
	triggered = true;
	
	leadinSample = ( idSoundSample_OpenAL* )leadinSample_;
	loopingSample = ( idSoundSample_OpenAL* )loopingSample_;
	
	if( alIsSource( openalSource ) && CompatibleFormat( leadinSample ) )
	{
		sampleRate = leadinSample->format.basic.samplesPerSec;
	}
	else
	{
		DestroyInternal();
		formatTag = leadinSample->format.basic.formatTag;
		numChannels = leadinSample->format.basic.numChannels;
		sampleRate = leadinSample->format.basic.samplesPerSec;
		
		//soundSystemLocal.hardware.pXAudio2->CreateSourceVoice( &pSourceVoice, ( const WAVEFORMATEX* )&leadinSample->format, XAUDIO2_VOICE_USEFILTER, 4.0f, &streamContext );
		
		CheckALErrors();
		
		alGenSources( 1, &openalSource );
		if( CheckALErrors() != AL_NO_ERROR )
			//if( pSourceVoice == NULL )
		{
			// If this hits, then we are most likely passing an invalid sample format, which should have been caught by the loader (and the sample defaulted)
			return;
		}
		
		alSourcef( openalSource, AL_ROLLOFF_FACTOR, 0.0f );
		
		//if( ( loopingSample == NULL && leadinSample->openalBuffer != 0 ) || ( loopingSample != NULL && soundShader->entries[0]->hardwareBuffer ) )
		if( leadinSample->openalBuffer != 0 )
		{
			alSourcei( openalSource, AL_BUFFER, 0 );
			
			// handle uncompressed (non streaming) single shot and looping sounds
			/*
			if( triggered )
			{
				alSourcei( openalSource, AL_BUFFER, looping ? chan->soundShader->entries[0]->openalBuffer : leadinSample->openalBuffer );
			}
			*/
		}
		else
		{
			//if( triggered )
			
			// handle streaming sounds (decode on the fly) both single shot AND looping
			
			alSourcei( openalSource, AL_BUFFER, 0 );
			alDeleteBuffers( 3, &lastopenalStreamingBuffer[0] );
			lastopenalStreamingBuffer[0] = openalStreamingBuffer[0];
			lastopenalStreamingBuffer[1] = openalStreamingBuffer[1];
			lastopenalStreamingBuffer[2] = openalStreamingBuffer[2];
			
			alGenBuffers( 3, &openalStreamingBuffer[0] );
			/*
			if( soundSystemLocal.alEAXSetBufferMode )
			{
				soundSystemLocal.alEAXSetBufferMode( 3, &chan->openalStreamingBuffer[0], alGetEnumValue( ID_ALCHAR "AL_STORAGE_ACCESSIBLE" ) );
			}
			*/
			openalStreamingBuffer[0];
			openalStreamingBuffer[1];
			openalStreamingBuffer[2];
		}
		
		if( s_debugHardware.GetBool() )
		{
			if( loopingSample == NULL || loopingSample == leadinSample )
			{
				idLib::Printf( "%dms: %i created for %s\n", Sys_Milliseconds(), openalSource, leadinSample ? leadinSample->GetName() : "<null>" );
			}
			else
			{
				idLib::Printf( "%dms: %i created for %s and %s\n", Sys_Milliseconds(), openalSource, leadinSample ? leadinSample->GetName() : "<null>", loopingSample ? loopingSample->GetName() : "<null>" );
			}
		}
	}
	
	sourceVoiceRate = sampleRate;
	//pSourceVoice->SetSourceSampleRate( sampleRate );
	//pSourceVoice->SetVolume( 0.0f );
	
	alSourcei( openalSource, AL_SOURCE_RELATIVE, AL_TRUE );
	alSource3f( openalSource, AL_POSITION, 0.0f, 0.0f, 0.0f );
	
	// RB: FIXME 0.0f ?
	alSourcef( openalSource, AL_GAIN, 1.0f );
	
	//OnBufferStart( leadinSample, 0 );
}