/* ======================== 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(); }
/* ======================== 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; }
/* ============= 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 ); }
/* ========================== 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; } } }
/* ================================================================================================ 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 ); }
/* ======================== 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; }
/* ======================== idSessionLocalCallbacks::HandlePeerMatchParamUpdate ======================== */ void idSessionLocalCallbacks::HandlePeerMatchParamUpdate( int peer, int msg ) { if ( net_headlessServer.GetBool() ) { sessionLocal->storedPeer = peer; sessionLocal->storedMsgType = msg; } }
/* ================= 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) ); } }
/* ============ 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(); } } }
/* =============== 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 ); }
/* ======================== 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; }
/* ================== 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() ); } } }
/* ================= 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() ); } } } }
/* ================= 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" ); } }
/* ==================== 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; }
/* =================== 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; } }
/* ======================== 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; }
/* =================== 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; } } }
/* ================================================================================================ 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; } }
/* ======================== 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; } }
/* ======================== 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; } } }
/* ======================== 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; } } } }
/* ======================== 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; }
/* ======================== 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 ); }