/** Run the actual main loop. */ void MainLoop::run() { IrrlichtDevice* device = irr_driver->getDevice(); m_curr_time = device->getTimer()->getRealTime(); while(!m_abort) { PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7); m_prev_time = m_curr_time; float dt = getLimitedDt(); network_manager->update(dt); if (World::getWorld()) // race is active if world exists { // Busy wait if race_manager is active (i.e. creating of world is done) // till all clients have reached this state. if (network_manager->getState()==NetworkManager::NS_READY_SET_GO_BARRIER) continue; updateRace(dt); } // if race is active // We need to check again because update_race may have requested // the main loop to abort; and it's not a good idea to continue // since the GUI engine is no more to be called then. // Also only do music, input, and graphics update if graphics are // enabled. if (!m_abort && !ProfileWorld::isNoGraphics()) { PROFILER_PUSH_CPU_MARKER("Music manager update", 0x7F, 0x00, 0x00); music_manager->update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("Input manager update", 0x00, 0x7F, 0x00); input_manager->update(dt); PROFILER_POP_CPU_MARKER(); #ifdef ENABLE_WIIUSE wiimote_manager->update(); #endif PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); irr_driver->update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_SYNC_FRAME(); } PROFILER_POP_CPU_MARKER(); } // while !m_exit } // run
/** Determines if a new state snapshot should be taken, and if so calls all * rewinder to do so. * \param ticks_not_used NUmber of physics time steps - should be 1. */ void RewindManager::update(int ticks_not_used) { // FIXME: rename ticks_not_used if (!m_enable_rewind_manager || m_all_rewinder.size() == 0 || m_is_rewinding) return; int ticks = World::getWorld()->getTicksSinceStart(); m_not_rewound_ticks.store(ticks, std::memory_order_relaxed); if (!shouldSaveState(ticks)) return; // Save state, remove expired rewinder first clearExpiredRewinder(); if (NetworkConfig::get()->isClient()) { auto& ret = m_local_state[ticks]; for (auto& p : m_all_rewinder) { if (auto r = p.second.lock()) ret.push_back(r->getLocalStateRestoreFunction()); } } else { saveState(); PROFILER_PUSH_CPU_MARKER("RewindManager - send state", 0x20, 0x7F, 0x40); if (auto gp = GameProtocol::lock()) gp->sendState(); } PROFILER_POP_CPU_MARKER(); } // update
/** Saves a state using the GameProtocol function to combine several * independent rewinders to write one state. */ void RewindManager::saveState() { PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20); auto gp = GameProtocol::lock(); if (!gp) return; gp->startNewState(); m_overall_state_size = 0; std::vector<std::string> rewinder_using; for (auto& p : m_all_rewinder) { // TODO: check if it's worth passing in a sufficiently large buffer from // GameProtocol - this would save the copy operation. BareNetworkString* buffer = NULL; if (auto r = p.second.lock()) buffer = r->saveState(&rewinder_using); if (buffer != NULL) { m_overall_state_size += buffer->size(); gp->addState(buffer); } delete buffer; // buffer can be freed } gp->finalizeState(rewinder_using); PROFILER_POP_CPU_MARKER(); } // saveState
/** Returns the current dt, which guarantees a limited frame rate. If dt is * too low (the frame rate too high), the process will sleep to reach the * maxium frame rate. */ float MainLoop::getLimitedDt() { IrrlichtDevice* device = irr_driver->getDevice(); m_prev_time = m_curr_time; float dt; // needed outside of the while loop while( 1 ) { m_curr_time = device->getTimer()->getRealTime(); dt = (float)(m_curr_time - m_prev_time); // don't allow the game to run slower than a certain amount. // when the computer can't keep it up, slow down the shown time instead static const float max_elapsed_time = 3.0f*1.0f/60.0f*1000.0f; /* time 3 internal substeps take */ if(dt > max_elapsed_time) dt=max_elapsed_time; // Throttle fps if more than maximum, which can reduce // the noise the fan on a graphics card makes. // When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus const int max_fps = (StateManager::get()->throttleFPS() ? 30 : UserConfigParams::m_max_fps); const int current_fps = (int)(1000.0f/dt); if (m_throttle_fps && current_fps > max_fps && !ProfileWorld::isProfileMode()) { int wait_time = 1000/max_fps - 1000/current_fps; if(wait_time < 1) wait_time = 1; PROFILER_PUSH_CPU_MARKER("Throttle framerate", 0, 0, 0); StkTime::sleep(wait_time); PROFILER_POP_CPU_MARKER(); } else break; } dt *= 0.001f; return dt; } // getLimitedDt
/** Determines if a new state snapshot should be taken, and if so calls all * rewinder to do so. * \param ticks_not_used NUmber of physics time steps - should be 1. */ void RewindManager::update(int ticks_not_used) { // FIXME: rename ticks_not_used if (!m_enable_rewind_manager || m_all_rewinder.size() == 0 || m_is_rewinding) return; float time = World::getWorld()->getTime(); int ticks = World::getWorld()->getTimeTicks(); m_not_rewound_ticks = ticks; // Clients don't save state, so they just exit. if (NetworkConfig::get()->isClient() || ticks - m_last_saved_state < m_state_frequency) { return; } // Save state saveState(/**allow_local_save*/false); PROFILER_PUSH_CPU_MARKER("RewindManager - send state", 0x20, 0x7F, 0x40); GameProtocol::lock()->sendState(); PROFILER_POP_CPU_MARKER(); m_last_saved_state = ticks; } // update
/** Replays all events from the last event played till the specified time. * \param world_ticks Up to (and inclusive) which time events will be replayed. * \param ticks Number of time steps - should be 1. */ void RewindManager::playEventsTill(int world_ticks, int *ticks) { bool needs_rewind; int rewind_ticks; // Merge in all network events that have happened at the current // time step. // merge and that have happened before the current time (which will // be getTime()+dt - world time has not been updated yet). m_rewind_queue.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks); if (needs_rewind) { Log::setPrefix("Rewind"); PROFILER_PUSH_CPU_MARKER("Rewind", 128, 128, 128); rewindTo(rewind_ticks, world_ticks); // This should replay everything up to 'now' assert(World::getWorld()->getTimeTicks() == world_ticks); PROFILER_POP_CPU_MARKER(); Log::setPrefix(""); } assert(!m_is_rewinding); if (m_rewind_queue.isEmpty()) return; // This is necessary to avoid that rewinding an event will store the // event again as a seemingly new event. m_is_rewinding = true; // Now play all events that happened at the current time stamp. m_rewind_queue.replayAllEvents(world_ticks); m_is_rewinding = false; } // playEventsTill
/** Returns the current dt, which guarantees a limited frame rate. If dt is * too low (the frame rate too high), the process will sleep to reach the * maxium frame rate. */ float MainLoop::getLimitedDt() { // In profile mode without graphics, run with a fixed dt of 1/60 if (ProfileWorld::isProfileMode() && ProfileWorld::isNoGraphics()) { return 1.0f/60.0f; } IrrlichtDevice* device = irr_driver->getDevice(); m_prev_time = m_curr_time; float dt; // needed outside of the while loop while( 1 ) { m_curr_time = device->getTimer()->getRealTime(); dt = (float)(m_curr_time - m_prev_time); const World* const world = World::getWorld(); if (UserConfigParams::m_fps_debug && world) { const LinearWorld *lw = dynamic_cast<const LinearWorld*>(world); if (lw) { Log::verbose("fps", "time %f distance %f dt %f fps %f", lw->getTime(), lw->getDistanceDownTrackForKart(0), dt*0.001f, 1000.0f / dt); } else { Log::verbose("fps", "time %f dt %f fps %f", world->getTime(), dt*0.001f, 1000.0f / dt); } } // don't allow the game to run slower than a certain amount. // when the computer can't keep it up, slow down the shown time instead static const float max_elapsed_time = 3.0f*1.0f/60.0f*1000.0f; /* time 3 internal substeps take */ if(dt > max_elapsed_time) dt=max_elapsed_time; // Throttle fps if more than maximum, which can reduce // the noise the fan on a graphics card makes. // When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus const int max_fps = (StateManager::get()->throttleFPS() ? 30 : UserConfigParams::m_max_fps); const int current_fps = (int)(1000.0f/dt); if (m_throttle_fps && current_fps > max_fps && !ProfileWorld::isProfileMode()) { int wait_time = 1000/max_fps - 1000/current_fps; if(wait_time < 1) wait_time = 1; PROFILER_PUSH_CPU_MARKER("Throttle framerate", 0, 0, 0); StkTime::sleep(wait_time); PROFILER_POP_CPU_MARKER(); } else break; } dt *= 0.001f; return dt; } // getLimitedDt
/** Prepare draw calls before scene rendering */ void DrawCalls::prepareDrawCalls(scene::ICameraSceneNode *camnode) { CPUParticleManager::getInstance()->reset(); TextBillboardDrawer::reset(); PROFILER_PUSH_CPU_MARKER("- culling", 0xFF, 0xFF, 0x0); SP::prepareDrawCalls(); parseSceneManager( irr_driver->getSceneManager()->getRootSceneNode()->getChildren(), camnode); SP::handleDynamicDrawCall(); SP::updateModelMatrix(); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- cpu particle generation", 0x2F, 0x1F, 0x11); CPUParticleManager::getInstance()->generateAll(); PROFILER_POP_CPU_MARKER(); // Add a 1 s timeout if (m_sync != 0) { PROFILER_PUSH_CPU_MARKER("- Sync Stall", 0xFF, 0x0, 0x0); GLenum reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0); if (reason != GL_ALREADY_SIGNALED) { do { reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000); } while (reason == GL_TIMEOUT_EXPIRED); } glDeleteSync(m_sync); m_sync = 0; PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- particle and text billboard upload", 0x3F, 0x03, 0x61); CPUParticleManager::getInstance()->uploadAll(); TextBillboardDrawer::updateAll(); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- SP::upload instance and skinning matrices", 0xFF, 0x0, 0xFF); SP::uploadAll(); PROFILER_POP_CPU_MARKER(); }
/** In network games this update function is called instead of * World::updateWorld(). * \param ticks Number of physics time steps - should be 1. * \param fast_forward If true, then only rewinders in network will be * updated, but not the physics. */ void RaceEventManager::update(int ticks, bool fast_forward) { // Replay all recorded events up to the current time // This might adjust dt - if a new state is being played, the dt is // determined from the last state till 'now' PROFILER_PUSH_CPU_MARKER("RaceEvent:play event", 100, 100, 100); RewindManager::get()->playEventsTill(World::getWorld()->getTicksSinceStart(), fast_forward); PROFILER_POP_CPU_MARKER(); if (!fast_forward) World::getWorld()->updateWorld(ticks); } // update
/** Updates the physics, all karts, the track, and projectile manager. * \param dt Time step size. */ void World::update(float dt) { #ifdef DEBUG assert(m_magic_number == 0xB01D6543); #endif PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00); #if MEASURE_FPS static float time = 0.0f; time += dt; if (time > 5.0f) { time -= 5.0f; printf("%i\n",irr_driver->getVideoDriver()->getFPS()); } #endif history->update(dt); if(ReplayRecorder::get()) ReplayRecorder::get()->update(dt); if(ReplayPlay::get()) ReplayPlay::get()->update(dt); if(history->replayHistory()) dt=history->getNextDelta(); WorldStatus::update(dt); if (!history->dontDoPhysics()) { m_physics->update(dt); } const int kart_amount = m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) { // Update all karts that are not eliminated if(!m_karts[i]->isEliminated()) m_karts[i]->update(dt) ; } for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera::getCamera(i)->update(dt); } projectile_manager->update(dt); PROFILER_POP_CPU_MARKER(); #ifdef DEBUG assert(m_magic_number == 0xB01D6543); #endif } // update
/** Saves a local state using the GameProtocol function to combine several * independent rewinders to write one state. Typically only the server needs * to save a state (which is then send to the clients), except that each * client needs one initial state (in case that it receives an event from * a client before a state from the serer). * \param allow_local_save Do a local save. */ void RewindManager::saveState(bool local_save) { PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20); GameProtocol::lock()->startNewState(local_save); AllRewinder::const_iterator rewinder; for (rewinder = m_all_rewinder.begin(); rewinder != m_all_rewinder.end(); ++rewinder) { // TODO: check if it's worth passing in a sufficiently large buffer from // GameProtocol - this would save the copy operation. BareNetworkString *buffer = (*rewinder)->saveState(); if (buffer && buffer->size() >= 0) { m_overall_state_size += buffer->size(); GameProtocol::lock()->addState(buffer); } // size >= 0 delete buffer; // buffer can be freed } PROFILER_POP_CPU_MARKER(); } // saveState
/** Updates the physics, all karts, the track, and projectile manager. * \param dt Time step size. */ void World::update(float dt) { PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00); #if MEASURE_FPS static float time = 0.0f; time += dt; if (time > 5.0f) { time -= 5.0f; printf("%i\n",irr_driver->getVideoDriver()->getFPS()); } #endif history->update(dt); if(ReplayRecorder::get()) ReplayRecorder::get()->update(dt); if(ReplayPlay::get()) ReplayPlay::get()->update(dt); if(history->replayHistory()) dt=history->getNextDelta(); WorldStatus::update(dt); // Clear race state so that new information can be stored race_state->clear(); if(network_manager->getMode()!=NetworkManager::NW_CLIENT && !history->dontDoPhysics()) { m_physics->update(dt); } const int kart_amount = m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) { // Update all karts that are not eliminated if(!m_karts[i]->isEliminated()) m_karts[i]->update(dt) ; } projectile_manager->update(dt); PROFILER_POP_CPU_MARKER(); } // update
void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned pointlightcount, std::vector<GlowData>& glows, float dt, bool hasShadow, bool forceRTT) { glBindBufferBase(GL_UNIFORM_BUFFER, 0, SharedObject::ViewProjectionMatrixesUBO); // Shadows { PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90); ScopedGPUTimer Timer(getGPUTimer(Q_SHADOWS)); // To avoid wrong culling, use the largest view possible m_scene_manager->setActiveCamera(m_suncam); if (!m_mipviz && !m_wireframe && UserConfigParams::m_dynamic_lights && UserConfigParams::m_shadows && !irr_driver->needUBOWorkaround() && hasShadow) renderShadows(); m_scene_manager->setActiveCamera(camnode); PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- Solid Pass 1", 0xFF, 0x00, 0x00); renderSolidFirstPass(); PROFILER_POP_CPU_MARKER(); // Lights { PROFILER_PUSH_CPU_MARKER("- Light", 0x00, 0xFF, 0x00); renderLights(pointlightcount); PROFILER_POP_CPU_MARKER(); } // Handle SSAO { PROFILER_PUSH_CPU_MARKER("- SSAO", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_SSAO)); if (UserConfigParams::m_ssao) renderSSAO(); PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- Solid Pass 2", 0x00, 0x00, 0xFF); if (!UserConfigParams::m_dynamic_lights && ! forceRTT) { glEnable(GL_FRAMEBUFFER_SRGB); glBindFramebuffer(GL_FRAMEBUFFER, 0); } else m_rtts->getFBO(FBO_COLORS).Bind(); renderSolidSecondPass(); PROFILER_POP_CPU_MARKER(); if (UserConfigParams::m_dynamic_lights && World::getWorld() != NULL && World::getWorld()->isFogEnabled()) { PROFILER_PUSH_CPU_MARKER("- Fog", 0xFF, 0x00, 0x00); m_post_processing->renderFog(); PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- Skybox", 0xFF, 0x00, 0xFF); renderSkybox(camnode); PROFILER_POP_CPU_MARKER(); if (getRH()) { glEnable(GL_PROGRAM_POINT_SIZE); m_rtts->getFBO(FBO_COLORS).Bind(); glUseProgram(FullScreenShader::RHDebug::Program); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_3D, m_rtts->getRH().getRTT()[0]); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_3D, m_rtts->getRH().getRTT()[1]); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_3D, m_rtts->getRH().getRTT()[2]); FullScreenShader::RHDebug::setUniforms(rh_matrix, rh_extend, 0, 1, 2); glDrawArrays(GL_POINTS, 0, 32 * 16 * 32); glDisable(GL_PROGRAM_POINT_SIZE); } if (getGI()) { m_rtts->getFBO(FBO_COLORS).Bind(); m_post_processing->renderGI(rh_matrix, rh_extend, m_rtts->getRH().getRTT()[0], m_rtts->getRH().getRTT()[1], m_rtts->getRH().getRTT()[2]); } PROFILER_PUSH_CPU_MARKER("- Glow", 0xFF, 0xFF, 0x00); // Render anything glowing. if (!m_mipviz && !m_wireframe && UserConfigParams::m_glow) { irr_driver->setPhase(GLOW_PASS); renderGlow(glows); } // end glow PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Lensflare/godray", 0x00, 0xFF, 0xFF); computeSunVisibility(); PROFILER_POP_CPU_MARKER(); // Render transparent { PROFILER_PUSH_CPU_MARKER("- Transparent Pass", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_TRANSPARENT)); renderTransparent(); PROFILER_POP_CPU_MARKER(); } // Render particles { PROFILER_PUSH_CPU_MARKER("- Particles", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_PARTICLES)); renderParticles(); PROFILER_POP_CPU_MARKER(); } if (!UserConfigParams::m_dynamic_lights && !forceRTT) { glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); return; } // Ensure that no object will be drawn after that by using invalid pass irr_driver->setPhase(PASS_COUNT); }
void IrrDriver::renderFixed(float dt) { World *world = World::getWorld(); // Never NULL. m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true, world->getClearColor()); irr_driver->getVideoDriver()->enableMaterial2D(); RaceGUIBase *rg = world->getRaceGUI(); if (rg) rg->update(dt); for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera *camera = Camera::getCamera(i); std::ostringstream oss; oss << "drawAll() for kart " << i; PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (i+1)*60, 0x00, 0x00); camera->activate(); rg->preRenderCallback(camera); // adjusts start referee m_renderpass = ~0; m_scene_manager->drawAll(); PROFILER_POP_CPU_MARKER(); // Note that drawAll must be called before rendering // the bullet debug view, since otherwise the camera // is not set up properly. This is only used for // the bullet debug view. if (UserConfigParams::m_artist_debug_mode) World::getWorld()->getPhysics()->draw(); } // for i<world->getNumKarts() // Set the viewport back to the full screen for race gui m_video_driver->setViewPort(core::recti(0, 0, UserConfigParams::m_width, UserConfigParams::m_height)); for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera *camera = Camera::getCamera(i); std::ostringstream oss; oss << "renderPlayerView() for kart " << i; PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), 0x00, 0x00, (i+1)*60); rg->renderPlayerView(camera, dt); PROFILER_POP_CPU_MARKER(); } // for i<getNumKarts // Either render the gui, or the global elements of the race gui. GUIEngine::render(dt); // Render the profiler if(UserConfigParams::m_profiler_enabled) { PROFILER_DRAW(); } #ifdef DEBUG drawDebugMeshes(); #endif m_video_driver->endScene(); }
void IrrDriver::renderGLSL(float dt) { BoundingBoxes.clear(); World *world = World::getWorld(); // Never NULL. Track *track = world->getTrack(); for (unsigned i = 0; i < PowerupManager::POWERUP_MAX; i++) { scene::IMesh *mesh = powerup_manager->m_all_meshes[i]; if (!mesh) continue; for (unsigned j = 0; j < mesh->getMeshBufferCount(); j++) { scene::IMeshBuffer *mb = mesh->getMeshBuffer(j); if (!mb) continue; for (unsigned k = 0; k < 4; k++) { video::ITexture *tex = mb->getMaterial().getTexture(k); if (!tex) continue; compressTexture(tex, true); } } } // Overrides video::SOverrideMaterial &overridemat = m_video_driver->getOverrideMaterial(); overridemat.EnablePasses = scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT; overridemat.EnableFlags = 0; if (m_wireframe) { overridemat.Material.Wireframe = 1; overridemat.EnableFlags |= video::EMF_WIREFRAME; } if (m_mipviz) { overridemat.Material.MaterialType = m_shaders->getShader(ES_MIPVIZ); overridemat.EnableFlags |= video::EMF_MATERIAL_TYPE; overridemat.EnablePasses = scene::ESNRP_SOLID; } // Get a list of all glowing things. The driver's list contains the static ones, // here we add items, as they may disappear each frame. std::vector<GlowData> glows = m_glowing; ItemManager * const items = ItemManager::get(); const u32 itemcount = items->getNumberOfItems(); u32 i; for (i = 0; i < itemcount; i++) { Item * const item = items->getItem(i); if (!item) continue; const Item::ItemType type = item->getType(); if (type != Item::ITEM_NITRO_BIG && type != Item::ITEM_NITRO_SMALL && type != Item::ITEM_BONUS_BOX && type != Item::ITEM_BANANA && type != Item::ITEM_BUBBLEGUM) continue; LODNode * const lod = (LODNode *) item->getSceneNode(); if (!lod->isVisible()) continue; const int level = lod->getLevel(); if (level < 0) continue; scene::ISceneNode * const node = lod->getAllNodes()[level]; node->updateAbsolutePosition(); GlowData dat; dat.node = node; dat.r = 1.0f; dat.g = 1.0f; dat.b = 1.0f; const video::SColorf &c = ItemManager::getGlowColor(type); dat.r = c.getRed(); dat.g = c.getGreen(); dat.b = c.getBlue(); glows.push_back(dat); } // Start the RTT for post-processing. // We do this before beginScene() because we want to capture the glClear() // because of tracks that do not have skyboxes (generally add-on tracks) m_post_processing->begin(); RaceGUIBase *rg = world->getRaceGUI(); if (rg) rg->update(dt); if (!CVS->isDefferedEnabled()) { SColor clearColor(0, 150, 150, 150); if (World::getWorld() != NULL) clearColor = World::getWorld()->getClearColor(); glClear(GL_COLOR_BUFFER_BIT); glDepthMask(GL_TRUE); glBindFramebuffer(GL_FRAMEBUFFER, 0); glClearColor(clearColor.getRed() / 255.f, clearColor.getGreen() / 255.f, clearColor.getBlue() / 255.f, clearColor.getAlpha() / 255.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } for(unsigned int cam = 0; cam < Camera::getNumCameras(); cam++) { Camera * const camera = Camera::getCamera(cam); scene::ICameraSceneNode * const camnode = camera->getCameraSceneNode(); std::ostringstream oss; oss << "drawAll() for kart " << cam; PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60, 0x00, 0x00); camera->activate(!CVS->isDefferedEnabled()); rg->preRenderCallback(camera); // adjusts start referee m_scene_manager->setActiveCamera(camnode); const core::recti &viewport = camera->getViewport(); if (World::getWorld() && World::getWorld()->getTrack()->hasShadows() && !SphericalHarmonicsTextures.empty()) irr_driver->getSceneManager()->setAmbientLight(SColor(0, 0, 0, 0)); // TODO: put this outside of the rendering loop if (!m_skybox_ready) { prepareSkybox(); m_skybox_ready = true; } if (!CVS->isDefferedEnabled()) glEnable(GL_FRAMEBUFFER_SRGB); PROFILER_PUSH_CPU_MARKER("Update Light Info", 0xFF, 0x0, 0x0); unsigned plc = UpdateLightsInfo(camnode, dt); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("UBO upload", 0x0, 0xFF, 0x0); computeMatrixesAndCameras(camnode, viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); uploadLightingData(); PROFILER_POP_CPU_MARKER(); renderScene(camnode, plc, glows, dt, track->hasShadows(), false); // Render bounding boxes if (irr_driver->getBoundingBoxesViz()) { glUseProgram(UtilShader::ColoredLine::getInstance()->Program); glBindVertexArray(UtilShader::ColoredLine::getInstance()->vao); glBindBuffer(GL_ARRAY_BUFFER, UtilShader::ColoredLine::getInstance()->vbo); UtilShader::ColoredLine::getInstance()->setUniforms(SColor(255, 255, 0, 0)); const float *tmp = BoundingBoxes.data(); for (unsigned int i = 0; i < BoundingBoxes.size(); i += 1024 * 6) { unsigned count = MIN2((int)BoundingBoxes.size() - i, 1024 * 6); glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(float), &tmp[i]); glDrawArrays(GL_LINES, 0, count / 3); } } // Debug physic // Note that drawAll must be called before rendering // the bullet debug view, since otherwise the camera // is not set up properly. This is only used for // the bullet debug view. if (UserConfigParams::m_artist_debug_mode) World::getWorld()->getPhysics()->draw(); if (world != NULL && world->getPhysics() != NULL) { IrrDebugDrawer* debug_drawer = world->getPhysics()->getDebugDrawer(); if (debug_drawer != NULL && debug_drawer->debugEnabled()) { const std::map<video::SColor, std::vector<float> >& lines = debug_drawer->getLines(); std::map<video::SColor, std::vector<float> >::const_iterator it; glUseProgram(UtilShader::ColoredLine::getInstance()->Program); glBindVertexArray(UtilShader::ColoredLine::getInstance()->vao); glBindBuffer(GL_ARRAY_BUFFER, UtilShader::ColoredLine::getInstance()->vbo); for (it = lines.begin(); it != lines.end(); it++) { UtilShader::ColoredLine::getInstance()->setUniforms(it->first); const std::vector<float> &vertex = it->second; const float *tmp = vertex.data(); for (unsigned int i = 0; i < vertex.size(); i += 1024 * 6) { unsigned count = MIN2((int)vertex.size() - i, 1024 * 6); glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(float), &tmp[i]); glDrawArrays(GL_LINES, 0, count / 3); } } glUseProgram(0); glBindVertexArray(0); } } // Render the post-processed scene if (CVS->isDefferedEnabled()) { bool isRace = StateManager::get()->getGameState() == GUIEngine::GAME; FrameBuffer *fbo = m_post_processing->render(camnode, isRace); if (irr_driver->getNormals()) irr_driver->getFBO(FBO_NORMAL_AND_DEPTHS).BlitToDefault(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); else if (irr_driver->getSSAOViz()) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); m_post_processing->renderPassThrough(m_rtts->getFBO(FBO_HALF1_R).getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); } else if (irr_driver->getRSM()) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); m_post_processing->renderPassThrough(m_rtts->getRSM().getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); } else if (irr_driver->getShadowViz()) { renderShadowsDebug(); } else { glEnable(GL_FRAMEBUFFER_SRGB); glBindFramebuffer(GL_FRAMEBUFFER, 0); if (CVS->isDefferedEnabled()) camera->activate(); m_post_processing->renderPassThrough(fbo->getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); glDisable(GL_FRAMEBUFFER_SRGB); } } // Save projection-view matrix for the next frame camera->setPreviousPVMatrix(m_ProjViewMatrix); PROFILER_POP_CPU_MARKER(); } // for i<world->getNumKarts() // Use full screen size float tmp[2]; tmp[0] = float(m_actual_screen_size.Width); tmp[1] = float(m_actual_screen_size.Height); glBindBuffer(GL_UNIFORM_BUFFER, SharedObject::ViewProjectionMatrixesUBO); glBufferSubData(GL_UNIFORM_BUFFER, (16 * 9) * sizeof(float), 2 * sizeof(float), tmp); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glUseProgram(0); // Set the viewport back to the full screen for race gui m_video_driver->setViewPort(core::recti(0, 0, irr_driver->getActualScreenSize().Width, irr_driver->getActualScreenSize().Height)); for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera *camera = Camera::getCamera(i); std::ostringstream oss; oss << "renderPlayerView() for kart " << i; PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), 0x00, 0x00, (i+1)*60); rg->renderPlayerView(camera, dt); PROFILER_POP_CPU_MARKER(); } // for i<getNumKarts { ScopedGPUTimer Timer(getGPUTimer(Q_GUI)); PROFILER_PUSH_CPU_MARKER("GUIEngine", 0x75, 0x75, 0x75); // Either render the gui, or the global elements of the race gui. GUIEngine::render(dt); PROFILER_POP_CPU_MARKER(); } // Render the profiler if(UserConfigParams::m_profiler_enabled) { PROFILER_DRAW(); } #ifdef DEBUG drawDebugMeshes(); #endif PROFILER_PUSH_CPU_MARKER("EndSccene", 0x45, 0x75, 0x45); m_video_driver->endScene(); PROFILER_POP_CPU_MARKER(); getPostProcessing()->update(dt); }
void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned pointlightcount, std::vector<GlowData>& glows, float dt, bool hasShadow, bool forceRTT) { glBindBufferBase(GL_UNIFORM_BUFFER, 0, SharedObject::ViewProjectionMatrixesUBO); glBindBufferBase(GL_UNIFORM_BUFFER, 1, SharedObject::LightingDataUBO); m_scene_manager->setActiveCamera(camnode); PROFILER_PUSH_CPU_MARKER("- Draw Call Generation", 0xFF, 0xFF, 0xFF); PrepareDrawCalls(camnode); PROFILER_POP_CPU_MARKER(); // Shadows { // To avoid wrong culling, use the largest view possible m_scene_manager->setActiveCamera(m_suncam); if (CVS->isDefferedEnabled() && CVS->isShadowEnabled() && hasShadow) { PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90); renderShadows(); PROFILER_POP_CPU_MARKER(); if (CVS->isGlobalIlluminationEnabled()) { PROFILER_PUSH_CPU_MARKER("- RSM", 0xFF, 0x0, 0xFF); renderRSM(); PROFILER_POP_CPU_MARKER(); } } m_scene_manager->setActiveCamera(camnode); } PROFILER_PUSH_CPU_MARKER("- Solid Pass 1", 0xFF, 0x00, 0x00); glDepthMask(GL_TRUE); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); glEnable(GL_CULL_FACE); if (CVS->isDefferedEnabled() || forceRTT) { m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); glClearColor(0., 0., 0., 0.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); renderSolidFirstPass(); } else { // We need a cleared depth buffer for some effect (eg particles depth blending) if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) glDisable(GL_FRAMEBUFFER_SRGB); m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); // Bind() modifies the viewport. In order not to affect anything else, // the viewport is just reset here and not removed in Bind(). const core::recti &vp = Camera::getActiveCamera()->getViewport(); glViewport(vp.UpperLeftCorner.X, irr_driver->getActualScreenSize().Height - vp.LowerRightCorner.Y, vp.LowerRightCorner.X - vp.UpperLeftCorner.X, vp.LowerRightCorner.Y - vp.UpperLeftCorner.Y); glClear(GL_DEPTH_BUFFER_BIT); if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) glEnable(GL_FRAMEBUFFER_SRGB); glBindFramebuffer(GL_FRAMEBUFFER, 0); } PROFILER_POP_CPU_MARKER(); // Lights { PROFILER_PUSH_CPU_MARKER("- Light", 0x00, 0xFF, 0x00); if (CVS->isDefferedEnabled()) renderLights(pointlightcount, hasShadow); PROFILER_POP_CPU_MARKER(); } // Handle SSAO { PROFILER_PUSH_CPU_MARKER("- SSAO", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_SSAO)); if (UserConfigParams::m_ssao) renderSSAO(); PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- Solid Pass 2", 0x00, 0x00, 0xFF); if (CVS->isDefferedEnabled() || forceRTT) { m_rtts->getFBO(FBO_COLORS).Bind(); SColor clearColor(0, 150, 150, 150); if (World::getWorld() != NULL) clearColor = World::getWorld()->getClearColor(); glClearColor(clearColor.getRed() / 255.f, clearColor.getGreen() / 255.f, clearColor.getBlue() / 255.f, clearColor.getAlpha() / 255.f); glClear(GL_COLOR_BUFFER_BIT); glDepthMask(GL_FALSE); } renderSolidSecondPass(); PROFILER_POP_CPU_MARKER(); if (getNormals()) { m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); renderNormalsVisualisation(); m_rtts->getFBO(FBO_COLORS).Bind(); } // Render ambient scattering if (CVS->isDefferedEnabled() && World::getWorld() != NULL && World::getWorld()->isFogEnabled()) { PROFILER_PUSH_CPU_MARKER("- Ambient scatter", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_FOG)); renderAmbientScatter(); PROFILER_POP_CPU_MARKER(); } { PROFILER_PUSH_CPU_MARKER("- Skybox", 0xFF, 0x00, 0xFF); ScopedGPUTimer Timer(getGPUTimer(Q_SKYBOX)); renderSkybox(camnode); PROFILER_POP_CPU_MARKER(); } // Render discrete lights scattering if (CVS->isDefferedEnabled() && World::getWorld() != NULL && World::getWorld()->isFogEnabled()) { PROFILER_PUSH_CPU_MARKER("- PointLight Scatter", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_FOG)); renderLightsScatter(pointlightcount); PROFILER_POP_CPU_MARKER(); } if (getRH()) { glDisable(GL_BLEND); m_rtts->getFBO(FBO_COLORS).Bind(); m_post_processing->renderRHDebug(m_rtts->getRH().getRTT()[0], m_rtts->getRH().getRTT()[1], m_rtts->getRH().getRTT()[2], rh_matrix, rh_extend); } if (getGI()) { glDisable(GL_BLEND); m_rtts->getFBO(FBO_COLORS).Bind(); m_post_processing->renderGI(rh_matrix, rh_extend, m_rtts->getRH().getRTT()[0], m_rtts->getRH().getRTT()[1], m_rtts->getRH().getRTT()[2]); } PROFILER_PUSH_CPU_MARKER("- Glow", 0xFF, 0xFF, 0x00); // Render anything glowing. if (!m_mipviz && !m_wireframe && UserConfigParams::m_glow) { ScopedGPUTimer Timer(getGPUTimer(Q_GLOW)); irr_driver->setPhase(GLOW_PASS); renderGlow(glows); } // end glow PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Lensflare/godray", 0x00, 0xFF, 0xFF); computeSunVisibility(); PROFILER_POP_CPU_MARKER(); // Render transparent { PROFILER_PUSH_CPU_MARKER("- Transparent Pass", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_TRANSPARENT)); renderTransparent(); PROFILER_POP_CPU_MARKER(); } m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); // Render particles { PROFILER_PUSH_CPU_MARKER("- Particles", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_PARTICLES)); renderParticles(); PROFILER_POP_CPU_MARKER(); } if (!CVS->isDefferedEnabled() && !forceRTT) { glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); return; } // Ensure that no object will be drawn after that by using invalid pass irr_driver->setPhase(PASS_COUNT); }
/** Prepare draw calls before scene rendering * \param[out] solid_poly_count Total number of polygons in objects * that will be rendered in this frame * \param[out] shadow_poly_count Total number of polygons for shadow * (rendered this frame) */ void DrawCalls::prepareDrawCalls( ShadowMatrices& shadow_matrices, scene::ICameraSceneNode *camnode, unsigned &solid_poly_count, unsigned &shadow_poly_count) { m_wind_dir = getWindDir(); clearLists(); for (unsigned Mat = 0; Mat < Material::SHADERTYPE_COUNT; ++Mat) { m_solid_pass_mesh[Mat].clear(); m_reflective_shadow_map_mesh[Mat].clear(); for (unsigned i = 0; i < 4; i++) m_shadow_pass_mesh[i * Material::SHADERTYPE_COUNT + Mat].clear(); } m_glow_pass_mesh.clear(); m_deferred_update.clear(); PROFILER_PUSH_CPU_MARKER("- culling", 0xFF, 0xFF, 0x0); parseSceneManager( irr_driver->getSceneManager()->getRootSceneNode()->getChildren(), &m_immediate_draw_list, camnode, shadow_matrices); PROFILER_POP_CPU_MARKER(); // Add a 1 s timeout if (!m_sync) m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); PROFILER_PUSH_CPU_MARKER("- Sync Stall", 0xFF, 0x0, 0x0); GLenum reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0); if (reason != GL_ALREADY_SIGNALED) { do { reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000); } while (reason == GL_TIMEOUT_EXPIRED); } glDeleteSync(m_sync); PROFILER_POP_CPU_MARKER(); /* switch (reason) { case GL_ALREADY_SIGNALED: printf("Already Signaled\n"); break; case GL_TIMEOUT_EXPIRED: printf("Timeout Expired\n"); break; case GL_CONDITION_SATISFIED: printf("Condition Satisfied\n"); break; case GL_WAIT_FAILED: printf("Wait Failed\n"); break; }*/ PROFILER_PUSH_CPU_MARKER("- Animations/Buffer upload", 0x0, 0x0, 0x0); for (unsigned i = 0; i < m_deferred_update.size(); i++) m_deferred_update[i]->updateGL(); PROFILER_POP_CPU_MARKER(); if (!CVS->supportsIndirectInstancingRendering()) return; #if !defined(USE_GLES2) int enableOpenMP = 0; if (CVS->supportsAsyncInstanceUpload()) enableOpenMP = 1; PROFILER_PUSH_CPU_MARKER("- Draw Command upload", 0xFF, 0x0, 0xFF); #pragma omp parallel sections if(enableOpenMP) { #pragma omp section { m_solid_cmd_buffer->fill(m_solid_pass_mesh); } #pragma omp section { m_glow_cmd_buffer->fill(&m_glow_pass_mesh); } #pragma omp section { irr_driver->setPhase(SHADOW_PASS); m_shadow_cmd_buffer->fill(m_shadow_pass_mesh); } #pragma omp section if (!shadow_matrices.isRSMMapAvail()) { m_reflective_shadow_map_cmd_buffer->fill(m_reflective_shadow_map_mesh); } } PROFILER_POP_CPU_MARKER(); solid_poly_count = m_solid_cmd_buffer->getPolyCount(); shadow_poly_count = m_shadow_cmd_buffer->getPolyCount(); if (CVS->supportsAsyncInstanceUpload()) glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); #endif // !defined(USE_GLES2) }
void IrrDriver::renderGLSL(float dt) { World *world = World::getWorld(); // Never NULL. // Overrides video::SOverrideMaterial &overridemat = m_video_driver->getOverrideMaterial(); overridemat.EnablePasses = scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT; overridemat.EnableFlags = 0; if (m_wireframe) { overridemat.Material.Wireframe = 1; overridemat.EnableFlags |= video::EMF_WIREFRAME; } if (m_mipviz) { overridemat.Material.MaterialType = m_shaders->getShader(ES_MIPVIZ); overridemat.EnableFlags |= video::EMF_MATERIAL_TYPE; overridemat.EnablePasses = scene::ESNRP_SOLID; } // Get a list of all glowing things. The driver's list contains the static ones, // here we add items, as they may disappear each frame. std::vector<GlowData> glows = m_glowing; std::vector<GlowNode *> transparent_glow_nodes; ItemManager * const items = ItemManager::get(); const u32 itemcount = items->getNumberOfItems(); u32 i; // For each static node, give it a glow representation const u32 staticglows = glows.size(); for (i = 0; i < staticglows; i++) { scene::ISceneNode * const node = glows[i].node; const float radius = (node->getBoundingBox().getExtent().getLength() / 2) * 2.0f; GlowNode * const repnode = new GlowNode(irr_driver->getSceneManager(), radius); repnode->setPosition(node->getTransformedBoundingBox().getCenter()); transparent_glow_nodes.push_back(repnode); } for (i = 0; i < itemcount; i++) { Item * const item = items->getItem(i); if (!item) continue; const Item::ItemType type = item->getType(); if (type != Item::ITEM_NITRO_BIG && type != Item::ITEM_NITRO_SMALL && type != Item::ITEM_BONUS_BOX && type != Item::ITEM_BANANA && type != Item::ITEM_BUBBLEGUM) continue; LODNode * const lod = (LODNode *) item->getSceneNode(); if (!lod->isVisible()) continue; const int level = lod->getLevel(); if (level < 0) continue; scene::ISceneNode * const node = lod->getAllNodes()[level]; node->updateAbsolutePosition(); GlowData dat; dat.node = node; dat.r = 1.0f; dat.g = 1.0f; dat.b = 1.0f; const video::SColorf &c = ItemManager::getGlowColor(type); dat.r = c.getRed(); dat.g = c.getGreen(); dat.b = c.getBlue(); glows.push_back(dat); // Push back its representation too const float radius = (node->getBoundingBox().getExtent().getLength() / 2) * 2.0f; GlowNode * const repnode = new GlowNode(irr_driver->getSceneManager(), radius); repnode->setPosition(node->getTransformedBoundingBox().getCenter()); transparent_glow_nodes.push_back(repnode); } // Start the RTT for post-processing. // We do this before beginScene() because we want to capture the glClear() // because of tracks that do not have skyboxes (generally add-on tracks) m_post_processing->begin(); m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true, world->getClearColor()); // Clear normal and depth to zero m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_NORMAL_AND_DEPTH), true, false, video::SColor(0,0,0,0)); // Clear specular map to zero m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_SPECULARMAP), true, false, video::SColor(0,0,0,0)); irr_driver->getVideoDriver()->enableMaterial2D(); RaceGUIBase *rg = world->getRaceGUI(); if (rg) rg->update(dt); for(unsigned int cam = 0; cam < Camera::getNumCameras(); cam++) { Camera * const camera = Camera::getCamera(cam); scene::ICameraSceneNode * const camnode = camera->getCameraSceneNode(); #ifdef ENABLE_PROFILER std::ostringstream oss; oss << "drawAll() for kart " << cam << std::flush; PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60, 0x00, 0x00); #endif camera->activate(); rg->preRenderCallback(camera); // adjusts start referee const u32 bgnodes = m_background.size(); /* if (bgnodes) { // If there are background nodes (3d skybox), draw them now. m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); m_renderpass = scene::ESNRP_SKY_BOX; m_scene_manager->drawAll(m_renderpass); const video::SOverrideMaterial prev = overridemat; overridemat.Enabled = 1; overridemat.EnableFlags = video::EMF_MATERIAL_TYPE; overridemat.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; for (i = 0; i < bgnodes; i++) { m_background[i]->setPosition(camnode->getPosition() * 0.97f); m_background[i]->updateAbsolutePosition(); m_background[i]->render(); } overridemat = prev; m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, true); }*/ // Fire up the MRT irr_driver->getVideoDriver()->setRenderTarget(irr_driver->getRTT(RTT_NORMAL_AND_DEPTH), false, false); PROFILER_PUSH_CPU_MARKER("- Solid Pass 1", 0xFF, 0x00, 0x00); m_renderpass = scene::ESNRP_CAMERA | scene::ESNRP_SOLID; glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glDisable(GL_ALPHA_TEST); glDepthMask(GL_TRUE); glDisable(GL_BLEND); irr_driver->setPhase(SOLID_NORMAL_AND_DEPTH_PASS); m_scene_manager->drawAll(m_renderpass); irr_driver->setProjMatrix(irr_driver->getVideoDriver()->getTransform(video::ETS_PROJECTION)); irr_driver->setViewMatrix(irr_driver->getVideoDriver()->getTransform(video::ETS_VIEW)); irr_driver->genProjViewMatrix(); PROFILER_POP_CPU_MARKER(); // Todo : reenable glow and shadows //ShadowImportanceProvider * const sicb = (ShadowImportanceProvider *) // irr_driver->getCallback(ES_SHADOW_IMPORTANCE); //sicb->updateIPVMatrix(); // Used to cull glowing items & lights const core::aabbox3df cambox = camnode->getViewFrustum()->getBoundingBox(); PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90); // Shadows if (!m_mipviz && !m_wireframe && UserConfigParams::m_shadows) //&& World::getWorld()->getTrack()->hasShadows()) { renderShadows(camnode, camera); } PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Light", 0x00, 0xFF, 0x00); // Lights renderLights(cambox, camnode, overridemat, cam, dt); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Solid Pass 2", 0x00, 0x00, 0xFF); irr_driver->setPhase(SOLID_LIT_PASS); glEnable(GL_DEPTH_TEST); glDisable(GL_ALPHA_TEST); glDepthMask(GL_FALSE); glDisable(GL_BLEND); m_renderpass = scene::ESNRP_CAMERA | scene::ESNRP_SOLID; m_scene_manager->drawAll(m_renderpass); PROFILER_POP_CPU_MARKER(); if (World::getWorld()->getTrack()->isFogEnabled()) { PROFILER_PUSH_CPU_MARKER("- Fog", 0xFF, 0x00, 0x00); m_post_processing->renderFog(irr_driver->getInvProjMatrix()); PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- Glow", 0xFF, 0xFF, 0x00); // Render anything glowing. if (!m_mipviz && !m_wireframe) { irr_driver->setPhase(GLOW_PASS); renderGlow(overridemat, glows, cambox, cam); } // end glow PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Skybox", 0xFF, 0x00, 0xFF); renderSkybox(); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Lensflare/godray", 0x00, 0xFF, 0xFF); // Is the lens flare enabled & visible? Check last frame's query. const bool hasflare = World::getWorld()->getTrack()->hasLensFlare(); const bool hasgodrays = World::getWorld()->getTrack()->hasGodRays(); if (true)//hasflare || hasgodrays) { irr::video::COpenGLDriver* gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver(); GLuint res = 0; if (m_query_issued) gl_driver->extGlGetQueryObjectuiv(m_lensflare_query, GL_QUERY_RESULT, &res); m_post_processing->setSunPixels(res); // Prepare the query for the next frame. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); gl_driver->extGlBeginQuery(GL_SAMPLES_PASSED_ARB, m_lensflare_query); m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); m_scene_manager->drawAll(scene::ESNRP_CAMERA); irr_driver->setPhase(GLOW_PASS); m_sun_interposer->render(); gl_driver->extGlEndQuery(GL_SAMPLES_PASSED_ARB); m_query_issued = true; m_lensflare->setStrength(res / 4000.0f); if (hasflare) m_lensflare->OnRegisterSceneNode(); // Make sure the color mask is reset glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } PROFILER_POP_CPU_MARKER(); // We need to re-render camera due to the per-cam-node hack. PROFILER_PUSH_CPU_MARKER("- Transparent Pass", 0xFF, 0x00, 0x00); irr_driver->setPhase(TRANSPARENT_PASS); m_renderpass = scene::ESNRP_CAMERA | scene::ESNRP_TRANSPARENT; glEnable(GL_DEPTH_TEST); glDisable(GL_ALPHA_TEST); glDepthMask(GL_FALSE); glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_CULL_FACE); m_scene_manager->drawAll(m_renderpass); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Particles", 0xFF, 0xFF, 0x00); m_renderpass = scene::ESNRP_CAMERA | scene::ESNRP_TRANSPARENT_EFFECT; glDepthMask(GL_FALSE); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); m_scene_manager->drawAll(m_renderpass); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Displacement", 0x00, 0x00, 0xFF); // Handle displacing nodes, if any const u32 displacingcount = m_displacing.size(); if (displacingcount) { renderDisplacement(overridemat, cam); } PROFILER_POP_CPU_MARKER(); // Drawing for this cam done, cleanup const u32 glowrepcount = transparent_glow_nodes.size(); for (i = 0; i < glowrepcount; i++) { transparent_glow_nodes[i]->remove(); transparent_glow_nodes[i]->drop(); } PROFILER_POP_CPU_MARKER(); // Note that drawAll must be called before rendering // the bullet debug view, since otherwise the camera // is not set up properly. This is only used for // the bullet debug view. if (UserConfigParams::m_artist_debug_mode) World::getWorld()->getPhysics()->draw(); } // for i<world->getNumKarts() PROFILER_PUSH_CPU_MARKER("Postprocessing", 0xFF, 0xFF, 0x00); // Render the post-processed scene m_post_processing->render(); PROFILER_POP_CPU_MARKER(); // Set the viewport back to the full screen for race gui m_video_driver->setViewPort(core::recti(0, 0, UserConfigParams::m_width, UserConfigParams::m_height)); for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera *camera = Camera::getCamera(i); char marker_name[100]; sprintf(marker_name, "renderPlayerView() for kart %d", i); PROFILER_PUSH_CPU_MARKER(marker_name, 0x00, 0x00, (i+1)*60); rg->renderPlayerView(camera, dt); PROFILER_POP_CPU_MARKER(); } // for i<getNumKarts PROFILER_PUSH_CPU_MARKER("GUIEngine", 0x75, 0x75, 0x75); // Either render the gui, or the global elements of the race gui. GUIEngine::render(dt); PROFILER_POP_CPU_MARKER(); // Render the profiler if(UserConfigParams::m_profiler_enabled) { PROFILER_DRAW(); } #ifdef DEBUG drawDebugMeshes(); #endif PROFILER_PUSH_CPU_MARKER("EndSccene", 0x45, 0x75, 0x45); m_video_driver->endScene(); PROFILER_POP_CPU_MARKER(); getPostProcessing()->update(dt); }
//----------------------------------------------------------------------------- /// Draw the markers void Profiler::draw() { PROFILER_PUSH_CPU_MARKER("ProfilerDraw", 0xFF, 0xFF, 0x00); video::IVideoDriver* driver = irr_driver->getVideoDriver(); std::stack<Marker> hovered_markers; drawBackground(); // Force to show the pointer irr_driver->showPointer(); int read_id = !m_write_id; // Compute some values for drawing (unit: pixels, but we keep floats for reducing errors accumulation) core::dimension2d<u32> screen_size = driver->getScreenSize(); const double profiler_width = (1.0 - 2.0*MARGIN_X) * screen_size.Width; const double x_offset = MARGIN_X*screen_size.Width; const double y_offset = (MARGIN_Y + LINE_HEIGHT)*screen_size.Height; const double line_height = LINE_HEIGHT*screen_size.Height; size_t nb_thread_infos = m_thread_infos.size(); double start = -1.0f; double end = -1.0f; for (size_t i = 0; i < nb_thread_infos; i++) { MarkerList& markers = m_thread_infos[i].markers_done[read_id]; MarkerList::const_iterator it_end = markers.end(); for (MarkerList::const_iterator it = markers.begin(); it != it_end; it++) { const Marker& m = *it; if (start < 0.0) start = m.start; else start = std::min(start, m.start); if (end < 0.0) end = m.end; else end = std::max(end, m.end); } } const double duration = end - start; const double factor = profiler_width / duration; // Get the mouse pos core::vector2di mouse_pos = GUIEngine::EventHandler::get()->getMousePos(); // For each thread: for (size_t i = 0; i < nb_thread_infos; i++) { // Draw all markers MarkerList& markers = m_thread_infos[i].markers_done[read_id]; if (markers.empty()) continue; if (m_capture_report) { if (m_first_capture_sweep) m_capture_report_buffer->getStdStream() << "\"Thread\";"; else m_capture_report_buffer->getStdStream() << i << ";"; } MarkerList::const_iterator it_end = markers.end(); for (MarkerList::const_iterator it = markers.begin(); it != it_end; it++) { const Marker& m = *it; assert(m.end >= 0.0); if (m_capture_report) { if (m_first_capture_sweep) m_capture_report_buffer->getStdStream() << "\"" << m.name << "\";"; else m_capture_report_buffer->getStdStream() << (int)round((m.end - m.start) * 1000) << ";"; } core::rect<s32> pos((s32)( x_offset + factor*m.start ), (s32)( y_offset + i*line_height ), (s32)( x_offset + factor*m.end ), (s32)( y_offset + (i+1)*line_height )); // Reduce vertically the size of the markers according to their layer pos.UpperLeftCorner.Y += m.layer*2; pos.LowerRightCorner.Y -= m.layer*2; GL32_draw2DRectangle(m.color, pos); // If the mouse cursor is over the marker, get its information if(pos.isPointInside(mouse_pos)) hovered_markers.push(m); } if (m_capture_report) { m_capture_report_buffer->getStdStream() << "\n"; m_first_capture_sweep = false; } } // GPU profiler QueryPerf hovered_gpu_marker = Q_LAST; long hovered_gpu_marker_elapsed = 0; int gpu_y = int(y_offset + nb_thread_infos*line_height + line_height/2); float total = 0; unsigned int gpu_timers[Q_LAST]; for (unsigned i = 0; i < Q_LAST; i++) { gpu_timers[i] = irr_driver->getGPUTimer(i).elapsedTimeus(); total += gpu_timers[i]; } static video::SColor colors[] = { video::SColor(255, 255, 0, 0), video::SColor(255, 0, 255, 0), video::SColor(255, 0, 0, 255), video::SColor(255, 255, 255, 0), video::SColor(255, 255, 0, 255), video::SColor(255, 0, 255, 255) }; if (hovered_markers.size() == 0) { float curr_val = 0; for (unsigned i = 0; i < Q_LAST; i++) { //Log::info("GPU Perf", "Phase %d : %d us\n", i, irr_driver->getGPUTimer(i).elapsedTimeus()); float elapsed = float(gpu_timers[i]); core::rect<s32> pos((s32)(x_offset + (curr_val / total)*profiler_width), (s32)(y_offset + gpu_y), (s32)(x_offset + ((curr_val + elapsed) / total)*profiler_width), (s32)(y_offset + gpu_y + line_height)); curr_val += elapsed; GL32_draw2DRectangle(colors[i % 6], pos); if (pos.isPointInside(mouse_pos)) { hovered_gpu_marker = (QueryPerf)i; hovered_gpu_marker_elapsed = gpu_timers[i]; } if (m_capture_report) { if (m_first_gpu_capture_sweep) m_gpu_capture_report_buffer->getStdStream() << GPU_Phase[i] << ";"; else m_gpu_capture_report_buffer->getStdStream() << elapsed << ";"; } } if (m_capture_report) { m_gpu_capture_report_buffer->getStdStream() << "\n"; m_first_gpu_capture_sweep = false; } } // Draw the end of the frame { s32 x_sync = (s32)(x_offset + factor*m_time_between_sync); s32 y_up_sync = (s32)(MARGIN_Y*screen_size.Height); s32 y_down_sync = (s32)( (MARGIN_Y + (2+nb_thread_infos)*LINE_HEIGHT)*screen_size.Height ); driver->draw2DLine(core::vector2di(x_sync, y_up_sync), core::vector2di(x_sync, y_down_sync), video::SColor(0xFF, 0x00, 0x00, 0x00)); } // Draw the hovered markers' names gui::ScalableFont* font = GUIEngine::getFont(); if (font) { core::stringw text; while(!hovered_markers.empty()) { Marker& m = hovered_markers.top(); std::ostringstream oss; oss.precision(4); oss << m.name << " [" << (m.end - m.start) << " ms / "; oss.precision(3); oss << (m.end - m.start)*100.0 / duration << "%]" << std::endl; text += oss.str().c_str(); hovered_markers.pop(); } font->draw(text, MARKERS_NAMES_POS, video::SColor(0xFF, 0xFF, 0x00, 0x00)); if (hovered_gpu_marker != Q_LAST) { std::ostringstream oss; oss << GPU_Phase[hovered_gpu_marker] << " : " << hovered_gpu_marker_elapsed << " us"; font->draw(oss.str().c_str(), GPU_MARKERS_NAMES_POS, video::SColor(0xFF, 0xFF, 0x00, 0x00)); } } if (m_capture_report) { font->draw("Capturing profiler report...", MARKERS_NAMES_POS, video::SColor(0xFF, 0x00, 0x90, 0x00)); } PROFILER_POP_CPU_MARKER(); }
void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned pointlightcount, std::vector<GlowData>& glows, float dt, bool hasShadow, bool forceRTT) { glBindBufferBase(GL_UNIFORM_BUFFER, 0, SharedObject::ViewProjectionMatrixesUBO); m_scene_manager->setActiveCamera(camnode); PROFILER_PUSH_CPU_MARKER("- Draw Call Generation", 0xFF, 0xFF, 0xFF); PrepareDrawCalls(camnode); PROFILER_POP_CPU_MARKER(); // Shadows { // To avoid wrong culling, use the largest view possible m_scene_manager->setActiveCamera(m_suncam); if (!m_mipviz && !m_wireframe && UserConfigParams::m_dynamic_lights && UserConfigParams::m_shadows && !irr_driver->needUBOWorkaround() && hasShadow) { PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90); renderShadows(); PROFILER_POP_CPU_MARKER(); if (UserConfigParams::m_gi) { PROFILER_PUSH_CPU_MARKER("- RSM", 0xFF, 0x0, 0xFF); renderRSM(); PROFILER_POP_CPU_MARKER(); } } m_scene_manager->setActiveCamera(camnode); } PROFILER_PUSH_CPU_MARKER("- Solid Pass 1", 0xFF, 0x00, 0x00); glDepthMask(GL_TRUE); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); glEnable(GL_CULL_FACE); if (UserConfigParams::m_dynamic_lights || forceRTT) { m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); glClearColor(0., 0., 0., 0.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); renderSolidFirstPass(); } PROFILER_POP_CPU_MARKER(); // Lights { PROFILER_PUSH_CPU_MARKER("- Light", 0x00, 0xFF, 0x00); if (UserConfigParams::m_dynamic_lights) renderLights(pointlightcount, hasShadow); PROFILER_POP_CPU_MARKER(); } // Handle SSAO { PROFILER_PUSH_CPU_MARKER("- SSAO", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_SSAO)); if (UserConfigParams::m_ssao) renderSSAO(); PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- Solid Pass 2", 0x00, 0x00, 0xFF); if (UserConfigParams::m_dynamic_lights || forceRTT) { m_rtts->getFBO(FBO_COLORS).Bind(); SColor clearColor(0, 150, 150, 150); if (World::getWorld() != NULL) clearColor = World::getWorld()->getClearColor(); glClearColor(clearColor.getRed() / 255.f, clearColor.getGreen() / 255.f, clearColor.getBlue() / 255.f, clearColor.getAlpha() / 255.f); glClear(GL_COLOR_BUFFER_BIT); glDepthMask(GL_FALSE); } renderSolidSecondPass(); PROFILER_POP_CPU_MARKER(); if (getNormals()) { m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); renderNormalsVisualisation(); m_rtts->getFBO(FBO_COLORS).Bind(); } if (UserConfigParams::m_dynamic_lights && World::getWorld() != NULL && World::getWorld()->isFogEnabled()) { PROFILER_PUSH_CPU_MARKER("- Fog", 0xFF, 0x00, 0x00); m_post_processing->renderFog(); PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- Skybox", 0xFF, 0x00, 0xFF); renderSkybox(camnode); PROFILER_POP_CPU_MARKER(); if (getRH()) { m_rtts->getFBO(FBO_COLORS).Bind(); m_post_processing->renderRHDebug(m_rtts->getRH().getRTT()[0], m_rtts->getRH().getRTT()[1], m_rtts->getRH().getRTT()[2], rh_matrix, rh_extend); } if (getGI()) { m_rtts->getFBO(FBO_COLORS).Bind(); m_post_processing->renderGI(rh_matrix, rh_extend, m_rtts->getRH().getRTT()[0], m_rtts->getRH().getRTT()[1], m_rtts->getRH().getRTT()[2]); } PROFILER_PUSH_CPU_MARKER("- Glow", 0xFF, 0xFF, 0x00); // Render anything glowing. if (!m_mipviz && !m_wireframe && UserConfigParams::m_glow) { irr_driver->setPhase(GLOW_PASS); renderGlow(glows); } // end glow PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Lensflare/godray", 0x00, 0xFF, 0xFF); computeSunVisibility(); PROFILER_POP_CPU_MARKER(); // Render transparent { PROFILER_PUSH_CPU_MARKER("- Transparent Pass", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_TRANSPARENT)); renderTransparent(); PROFILER_POP_CPU_MARKER(); } m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); // Render particles { PROFILER_PUSH_CPU_MARKER("- Particles", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_PARTICLES)); renderParticles(); PROFILER_POP_CPU_MARKER(); } if (!UserConfigParams::m_dynamic_lights && !forceRTT) { glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); return; } // Ensure that no object will be drawn after that by using invalid pass irr_driver->setPhase(PASS_COUNT); }
/** Updates the physics, all karts, the track, and projectile manager. * \param dt Time step size. */ void World::update(float dt) { #ifdef DEBUG assert(m_magic_number == 0xB01D6543); #endif PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00); #if MEASURE_FPS static float time = 0.0f; time += dt; if (time > 5.0f) { time -= 5.0f; printf("%i\n",irr_driver->getVideoDriver()->getFPS()); } #endif PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); WorldStatus::update(dt); RewindManager::get()->saveStates(); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00); // Update all the karts. This in turn will also update the controller, // which causes all AI steering commands set. So in the following // physics update the new steering is taken into account. const int kart_amount = (int)m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) { SpareTireAI* sta = dynamic_cast<SpareTireAI*>(m_karts[i]->getController()); // Update all karts that are not eliminated if(!m_karts[i]->isEliminated() || (sta && sta->isMoving())) m_karts[i]->update(dt); } PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (camera)", 0x60, 0x7F, 0x00); for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera::getCamera(i)->update(dt); } PROFILER_POP_CPU_MARKER(); if(race_manager->isRecordingRace()) ReplayRecorder::get()->update(dt); Scripting::ScriptEngine *script_engine = Scripting::ScriptEngine::getInstance(); if (script_engine) script_engine->update(dt); if (!history->dontDoPhysics()) { Physics::getInstance()->update(dt); } PROFILER_PUSH_CPU_MARKER("World::update (weather)", 0x80, 0x7F, 0x00); if (UserConfigParams::m_graphical_effects && Weather::getInstance()) { Weather::getInstance()->update(dt); } PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (projectiles)", 0xa0, 0x7F, 0x00); projectile_manager->update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_POP_CPU_MARKER(); #ifdef DEBUG assert(m_magic_number == 0xB01D6543); #endif } // update
/** Run the actual main loop. */ void MainLoop::run() { IrrlichtDevice* device = irr_driver->getDevice(); m_curr_time = device->getTimer()->getRealTime(); while(!m_abort) { PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7); m_prev_time = m_curr_time; float dt = getLimitedDt(); if (World::getWorld()) // race is active if world exists { PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255); updateRace(dt); PROFILER_POP_CPU_MARKER(); } // if race is active // We need to check again because update_race may have requested // the main loop to abort; and it's not a good idea to continue // since the GUI engine is no more to be called then. // Also only do music, input, and graphics update if graphics are // enabled. if (!m_abort && !ProfileWorld::isNoGraphics()) { PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00); music_manager->update(dt); input_manager->update(dt); #ifdef ENABLE_WIIUSE wiimote_manager->update(); #endif GUIEngine::update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); irr_driver->update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); ProtocolManager::getInstance()->update(); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); Online::RequestManager::get()->update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_SYNC_FRAME(); } else if (!m_abort && ProfileWorld::isNoGraphics()) { PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); ProtocolManager::getInstance()->update(); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); Online::RequestManager::get()->update(dt); PROFILER_POP_CPU_MARKER(); } PROFILER_SYNC_FRAME(); PROFILER_POP_CPU_MARKER(); } // while !m_exit } // run
/** Updates the physics simulation and handles all collisions. * \param ticks Number of physics steps to simulate. */ void Physics::update(int ticks) { PROFILER_PUSH_CPU_MARKER("Physics", 0, 0, 0); m_physics_loop_active = true; // Bullet can report the same collision more than once (up to 4 // contact points per collision). Additionally, more than one internal // substep might be taken, resulting in potentially even more // duplicates. To handle this, all collisions (i.e. pair of objects) // are stored in a vector, but only one entry per collision pair // of objects. m_all_collisions.clear(); // Since the world update (which calls physics update) is called at the // fixed frequency necessary for the physics update, we need to do exactly // one physic step only. double start; if(UserConfigParams::m_physics_debug) start = StkTime::getRealTime(); m_dynamics_world->stepSimulation(stk_config->ticks2Time(1), 1, stk_config->ticks2Time(1) ); if (UserConfigParams::m_physics_debug) { Log::verbose("Physics", "At %d physics duration %12.8f", World::getWorld()->getTicksSinceStart(), StkTime::getRealTime() - start); } // Now handle the actual collision. Note: flyables can not be removed // inside of this loop, since the same flyables might hit more than one // other object. So only a flag is set in the flyables, the actual // clean up is then done later in the projectile manager. std::vector<CollisionPair>::iterator p; for(p=m_all_collisions.begin(); p!=m_all_collisions.end(); ++p) { // Kart-kart collision // -------------------- if(p->getUserPointer(0)->is(UserPointer::UP_KART)) { KartKartCollision(p->getUserPointer(0)->getPointerKart(), p->getContactPointCS(0), p->getUserPointer(1)->getPointerKart(), p->getContactPointCS(1) ); Scripting::ScriptEngine* script_engine = Scripting::ScriptEngine::getInstance(); int kartid1 = p->getUserPointer(0)->getPointerKart()->getWorldKartId(); int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); script_engine->runFunction(false, "void onKartKartCollision(int, int)", [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartid1); ctx->SetArgDWord(1, kartid2); }); continue; } // if kart-kart collision if(p->getUserPointer(0)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Kart hits physical object // ------------------------- Scripting::ScriptEngine* script_engine = Scripting::ScriptEngine::getInstance(); AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); int kartId = kart->getWorldKartId(); PhysicalObject* obj = p->getUserPointer(0)->getPointerPhysicalObject(); std::string obj_id = obj->getID(); std::string scripting_function = obj->getOnKartCollisionFunction(); TrackObject* to = obj->getTrackObject(); TrackObject* library = to->getParentLibrary(); std::string lib_id; std::string* lib_id_ptr = NULL; if (library != NULL) lib_id = library->getID(); lib_id_ptr = &lib_id; if (scripting_function.size() > 0) { script_engine->runFunction(true, "void " + scripting_function + "(int, const string, const string)", [&](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartId); ctx->SetArgObject(1, lib_id_ptr); ctx->SetArgObject(2, &obj_id); }); } if (obj->isCrashReset()) { new RescueAnimation(kart); } else if (obj->isExplodeKartObject()) { ExplosionAnimation::create(kart); if (kart->getKartAnimation() != NULL) { World::getWorld()->kartHit(kart->getWorldKartId()); } } else if (obj->isFlattenKartObject()) { const KartProperties *kp = kart->getKartProperties(); // Count squash only once from original state bool was_squashed = kart->isSquashed(); if (kart->setSquash(kp->getSwatterSquashDuration(), kp->getSwatterSquashSlowdown()) && !was_squashed) { World::getWorld()->kartHit(kart->getWorldKartId()); } } else if(obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) { SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setBallHitter(kartId); } continue; } if (p->getUserPointer(0)->is(UserPointer::UP_ANIMATION)) { // Kart hits animation ThreeDAnimation *anim=p->getUserPointer(0)->getPointerAnimation(); if(anim->isCrashReset()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); new RescueAnimation(kart); } else if (anim->isExplodeKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); ExplosionAnimation::create(kart); if (kart->getKartAnimation() != NULL) { World::getWorld()->kartHit(kart->getWorldKartId()); } } else if (anim->isFlattenKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); const KartProperties *kp = kart->getKartProperties(); // Count squash only once from original state bool was_squashed = kart->isSquashed(); if (kart->setSquash(kp->getSwatterSquashDuration(), kp->getSwatterSquashSlowdown()) && !was_squashed) { World::getWorld()->kartHit(kart->getWorldKartId()); } } continue; } // now the first object must be a projectile // ========================================= if(p->getUserPointer(1)->is(UserPointer::UP_TRACK)) { // Projectile hits track // --------------------- p->getUserPointer(0)->getPointerFlyable()->hitTrack(); } else if(p->getUserPointer(1)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Projectile hits physical object // ------------------------------- Scripting::ScriptEngine* script_engine = Scripting::ScriptEngine::getInstance(); Flyable* flyable = p->getUserPointer(0)->getPointerFlyable(); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); std::string obj_id = obj->getID(); std::string scripting_function = obj->getOnItemCollisionFunction(); if (scripting_function.size() > 0) { script_engine->runFunction(true, "void " + scripting_function + "(int, int, const string)", [&](asIScriptContext* ctx) { ctx->SetArgDWord(0, (int)flyable->getType()); ctx->SetArgDWord(1, flyable->getOwnerId()); ctx->SetArgObject(2, &obj_id); }); } flyable->hit(NULL, obj); if (obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) { int kartId = p->getUserPointer(0)->getPointerFlyable()->getOwnerId(); SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setBallHitter(kartId); } } else if(p->getUserPointer(1)->is(UserPointer::UP_KART)) { // Projectile hits kart // -------------------- // Only explode a bowling ball if the target is // not invulnerable AbstractKart* target_kart = p->getUserPointer(1)->getPointerKart(); PowerupManager::PowerupType type = p->getUserPointer(0)->getPointerFlyable()->getType(); if(type != PowerupManager::POWERUP_BOWLING || !target_kart->isInvulnerable()) { Flyable *f = p->getUserPointer(0)->getPointerFlyable(); f->hit(target_kart); // Check for achievements AbstractKart * kart = World::getWorld()->getKart(f->getOwnerId()); LocalPlayerController *lpc = dynamic_cast<LocalPlayerController*>(kart->getController()); // Check that it's not a kart hitting itself (this can // happen at the time a flyable is shot - release too close // to the kart, and it's the current player. At this stage // only the current player can get achievements. if (target_kart != kart && lpc && lpc->canGetAchievements()) { if (type == PowerupManager::POWERUP_BOWLING) { PlayerManager::increaseAchievement(AchievementsStatus::BOWLING_HIT, 1); if (race_manager->isLinearRaceMode()) PlayerManager::increaseAchievement(AchievementsStatus::BOWLING_HIT_1RACE, 1); } // is bowling ball } // if target_kart != kart && is a player kart and is current player } } else { // Projectile hits projectile // -------------------------- p->getUserPointer(0)->getPointerFlyable()->hit(NULL); p->getUserPointer(1)->getPointerFlyable()->hit(NULL); } } // for all p in m_all_collisions m_physics_loop_active = false; // Now remove the karts that were removed while the above loop // was active. Now we can safely call removeKart, since the loop // is finished and m_physics_world_active is not set anymore. for(unsigned int i=0; i<m_karts_to_delete.size(); i++) removeKart(m_karts_to_delete[i]); m_karts_to_delete.clear(); PROFILER_POP_CPU_MARKER(); } // update
/** Updates the physics, all karts, the track, and projectile manager. * \param dt Time step size. */ void World::update(float dt) { #ifdef DEBUG assert(m_magic_number == 0xB01D6543); #endif PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00); #if MEASURE_FPS static float time = 0.0f; time += dt; if (time > 5.0f) { time -= 5.0f; printf("%i\n",irr_driver->getVideoDriver()->getFPS()); } #endif PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); history->update(dt); if(ReplayRecorder::get()) ReplayRecorder::get()->update(dt); if(ReplayPlay::get()) ReplayPlay::get()->update(dt); if(history->replayHistory()) dt=history->getNextDelta(); WorldStatus::update(dt); if (m_script_engine) m_script_engine->update(dt); PROFILER_POP_CPU_MARKER(); if (!history->dontDoPhysics()) { m_physics->update(dt); } PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00); const int kart_amount = (int)m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) { // Update all karts that are not eliminated if(!m_karts[i]->isEliminated()) m_karts[i]->update(dt) ; } PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (camera)", 0x60, 0x7F, 0x00); for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera::getCamera(i)->update(dt); } PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (weather)", 0x80, 0x7F, 0x00); if (UserConfigParams::m_graphical_effects && m_weather) { m_weather->update(dt); } PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (projectiles)", 0xa0, 0x7F, 0x00); projectile_manager->update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_POP_CPU_MARKER(); #ifdef DEBUG assert(m_magic_number == 0xB01D6543); #endif } // update
/** Updates the physics simulation and handles all collisions. * \param dt Time step. */ void Physics::update(float dt) { PROFILER_PUSH_CPU_MARKER("Physics", 0, 0, 0); m_physics_loop_active = true; // Bullet can report the same collision more than once (up to 4 // contact points per collision). Additionally, more than one internal // substep might be taken, resulting in potentially even more // duplicates. To handle this, all collisions (i.e. pair of objects) // are stored in a vector, but only one entry per collision pair // of objects. m_all_collisions.clear(); // Maximum of three substeps. This will work for framerate down to // 20 FPS (bullet default frequency is 60 HZ). m_dynamics_world->stepSimulation(dt, 3); // Now handle the actual collision. Note: flyables can not be removed // inside of this loop, since the same flyables might hit more than one // other object. So only a flag is set in the flyables, the actual // clean up is then done later in the projectile manager. std::vector<CollisionPair>::iterator p; for(p=m_all_collisions.begin(); p!=m_all_collisions.end(); ++p) { // Kart-kart collision // -------------------- if(p->getUserPointer(0)->is(UserPointer::UP_KART)) { KartKartCollision(p->getUserPointer(0)->getPointerKart(), p->getContactPointCS(0), p->getUserPointer(1)->getPointerKart(), p->getContactPointCS(1) ); continue; } // if kart-kart collision if(p->getUserPointer(0)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Kart hits physical object // ------------------------- PhysicalObject *obj = p->getUserPointer(0) ->getPointerPhysicalObject(); if(obj->isCrashReset()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); new RescueAnimation(kart); } else if (obj->isExplodeKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); ExplosionAnimation::create(kart); } else if (obj->isFlattenKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration(), kp->getSquashSlowdown()); } else if(obj->isSoccerBall()) { int kartId = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setLastKartTohitBall(kartId); } continue; } if(p->getUserPointer(0)->is(UserPointer::UP_ANIMATION)) { // Kart hits animation ThreeDAnimation *anim=p->getUserPointer(0)->getPointerAnimation(); if(anim->isCrashReset()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); new RescueAnimation(kart); } else if (anim->isExplodeKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); ExplosionAnimation::create(kart); } else if (anim->isFlattenKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration(), kp->getSquashSlowdown()); } continue; } // now the first object must be a projectile // ========================================= if(p->getUserPointer(1)->is(UserPointer::UP_TRACK)) { // Projectile hits track // --------------------- p->getUserPointer(0)->getPointerFlyable()->hitTrack(); } else if(p->getUserPointer(1)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Projectile hits physical object // ------------------------------- p->getUserPointer(0)->getPointerFlyable() ->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject()); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); if(obj->isSoccerBall()) { int kartId = p->getUserPointer(0)->getPointerFlyable()->getOwnerId(); SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setLastKartTohitBall(kartId); } } else if(p->getUserPointer(1)->is(UserPointer::UP_KART)) { // Projectile hits kart // -------------------- // Only explode a bowling ball if the target is // not invulnerable AbstractKart* target_kart = p->getUserPointer(1)->getPointerKart(); PowerupManager::PowerupType type = p->getUserPointer(0)->getPointerFlyable()->getType(); if(type != PowerupManager::POWERUP_BOWLING || !target_kart->isInvulnerable()) { p->getUserPointer(0)->getPointerFlyable()->hit(target_kart); if ( type ==PowerupManager::POWERUP_BOWLING ) ((SingleAchievement *) AchievementsManager::get()->getActive()->getAchievement(2))->increase(1); } } else { // Projectile hits projectile // -------------------------- p->getUserPointer(0)->getPointerFlyable()->hit(NULL); p->getUserPointer(1)->getPointerFlyable()->hit(NULL); } } // for all p in m_all_collisions m_physics_loop_active = false; // Now remove the karts that were removed while the above loop // was active. Now we can safely call removeKart, since the loop // is finished and m_physics_world_active is not set anymore. for(unsigned int i=0; i<m_karts_to_delete.size(); i++) removeKart(m_karts_to_delete[i]); m_karts_to_delete.clear(); PROFILER_POP_CPU_MARKER(); } // update
/** Run the actual main loop. * The sequnce in which various parts of STK are updated is: * - Determine next time step size (`getLimitedDt`). This takes maximum fps * into account (i.e. sleep if the fps would be too high), and will actually * slow down the in-game clock if the fps are too low (if more than 3/60 of * a second have passed, more than 3 physics time steps would be needed, * and physics do at most 3 time steps). * - if a race is taking place (i.e. not only a menu being shown), call * `updateRace()`, which is a thin wrapper around a call to * `World::updateWorld()`: * - Update history manager (which will either set the kart position and/or * controls when replaying, or store the current info for a replay). * This is mostly for debugging only (though available even in release * mode). * - Updates Replays - either storing data when not replaying, or * updating kart positions/control when replaying). * - Calls `WorldStatus::update()`, which updates the race state (e.g. * go from 'ready' to 'set' etc), and clock. * - Updates the physics (`Physics::update()`). This will simulate all * physical objects for the specified time with bullet. * - Updates all karts (`Kart::update()`). Obviously the update function * does a lot more than what is described here, this is only supposed to * be a _very_ high level overview: * - Updates its rewinder (to store potentially changed controls * as events) in `KartRewinder::update()`. * - Calls `Moveable::update()`, which takes the new position from * the physics and saves it (and computes dependent values, like * heading, local velocity). * - Updates its controller. This is either: * - an AI using `SkiddingController::update()` (which then will * compute the new controls), or * - a player controller using `PlayerController::update()`, which will * handle smooth steering (in case of digital input devices steering * is adjusted a bit over time to avoid an instant change from all * left to all right). Input events will be handled when updating * the irrlicht driver later at the end of the main loop. * - Updates kart animation (like rescue, ...) if one is shown atm. * - Update attachments. * - update physics, i.e. taking the current steering and updating * the bullet raycast vehicle with that data. The settings are actually * only used in the next frame when the physics are updated. * - Updates all cameras via `Camera::update()`. The camera position and * rotation is adjusted according to the position etc of the kart (and * special circumstances like rescue, falling). * - Updates all projectiles using the projectile manager. Some of the * projectiles are mostly handled by the physics (e.g. a cake will mainly * check if it's out of bounds), others (like basket ball) do all * their aiming and movement here. * - Updates the rewind manager to store rewind states. * - Updates the music manager. * - Updates the input manager (which only updates internal time, actual * input handling follows late) * - Updates the wiimote manager. This will read the data of all wiimotes * and feed the corresponding events to the irrlicht event system. * - Updates the STK internal gui engine. This updates all widgets, and * e.g. takes care of the rotation of the karts in the KartSelection * screen using the ModelViewWidget. * - Updates STK's irrlicht driver `IrrDriver::update()`: * - Calls Irrlicht's `beginScene()` . * - Renders the scene (several times with different viewport if * split screen is being used) * - Calls `GUIEngine::render()`, which renders all widgets with the * help of Irrlicht's GUIEnvironment (`drawAll()`). This will also * handle all events, i.e. all input is now handled (e.g. steering, * firing etc are all set in the corresponding karts depending on * user input). * - Calls Irrlicht's `endScene()` */ void MainLoop::run() { IrrlichtDevice* device = irr_driver->getDevice(); m_curr_time = device->getTimer()->getRealTime(); while(!m_abort) { PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7); m_prev_time = m_curr_time; float dt = getLimitedDt(); if (!m_abort && !ProfileWorld::isNoGraphics()) { // Render the previous frame, and also handle all user input. PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); irr_driver->update(dt); PROFILER_POP_CPU_MARKER(); } if (World::getWorld()) // race is active if world exists { PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255); updateRace(dt); PROFILER_POP_CPU_MARKER(); } // if race is active // We need to check again because update_race may have requested // the main loop to abort; and it's not a good idea to continue // since the GUI engine is no more to be called then. // Also only do music, input, and graphics update if graphics are // enabled. if (!m_abort && !ProfileWorld::isNoGraphics()) { PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00); input_manager->update(dt); #ifdef ENABLE_WIIUSE wiimote_manager->update(); #endif GUIEngine::update(dt); PROFILER_POP_CPU_MARKER(); // Update sfx and music after graphics, so that graphics code // can use as many threads as possible without interfering // with audio PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00); SFXManager::get()->update(); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); if (STKHost::existHost()) { if (STKHost::get()->requestedShutdown()) STKHost::get()->shutdown(); else ProtocolManager::getInstance()->update(dt); } PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); Online::RequestManager::get()->update(dt); PROFILER_POP_CPU_MARKER(); } else if (!m_abort && ProfileWorld::isNoGraphics()) { PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); if(NetworkConfig::get()->isNetworking()) ProtocolManager::getInstance()->update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); Online::RequestManager::get()->update(dt); PROFILER_POP_CPU_MARKER(); } if (World::getWorld() ) { World::getWorld()->updateTime(dt); } PROFILER_POP_CPU_MARKER(); PROFILER_SYNC_FRAME(); } // while !m_abort } // run
/** Updates the physics simulation and handles all collisions. * \param dt Time step. */ void Physics::update(float dt) { PROFILER_PUSH_CPU_MARKER("Physics", 0, 0, 0); m_physics_loop_active = true; // Bullet can report the same collision more than once (up to 4 // contact points per collision). Additionally, more than one internal // substep might be taken, resulting in potentially even more // duplicates. To handle this, all collisions (i.e. pair of objects) // are stored in a vector, but only one entry per collision pair // of objects. m_all_collisions.clear(); // Maximum of three substeps. This will work for framerate down to // 20 FPS (bullet default frequency is 60 HZ). m_dynamics_world->stepSimulation(dt, 3); // Now handle the actual collision. Note: flyables can not be removed // inside of this loop, since the same flyables might hit more than one // other object. So only a flag is set in the flyables, the actual // clean up is then done later in the projectile manager. std::vector<CollisionPair>::iterator p; for(p=m_all_collisions.begin(); p!=m_all_collisions.end(); ++p) { // Kart-kart collision // -------------------- if(p->getUserPointer(0)->is(UserPointer::UP_KART)) { KartKartCollision(p->getUserPointer(0)->getPointerKart(), p->getContactPointCS(0), p->getUserPointer(1)->getPointerKart(), p->getContactPointCS(1) ); Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); int kartid1 = p->getUserPointer(0)->getPointerKart()->getWorldKartId(); int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); script_engine->runFunction("void onKartKartCollision(int, int)", [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartid1); ctx->SetArgDWord(1, kartid2); }); continue; } // if kart-kart collision if(p->getUserPointer(0)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Kart hits physical object // ------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); int kartId = kart->getWorldKartId(); PhysicalObject* obj = p->getUserPointer(0)->getPointerPhysicalObject(); std::string obj_id = obj->getID(); std::string scripting_function = obj->getOnKartCollisionFunction(); if (scripting_function.size() > 0) { script_engine->runFunction("void " + scripting_function + "(int, const string)", [&](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartId); ctx->SetArgObject(1, &obj_id); }); } if (obj->isCrashReset()) { new RescueAnimation(kart); } else if (obj->isExplodeKartObject()) { ExplosionAnimation::create(kart); } else if (obj->isFlattenKartObject()) { const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration() * kart->getPlayerDifficulty()->getSquashDuration(), kp->getSquashSlowdown() * kart->getPlayerDifficulty()->getSquashSlowdown()); } else if(obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) { SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setLastKartTohitBall(kartId); } continue; } if (p->getUserPointer(0)->is(UserPointer::UP_ANIMATION)) { // Kart hits animation ThreeDAnimation *anim=p->getUserPointer(0)->getPointerAnimation(); if(anim->isCrashReset()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); new RescueAnimation(kart); } else if (anim->isExplodeKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); ExplosionAnimation::create(kart); } else if (anim->isFlattenKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration() * kart->getPlayerDifficulty()->getSquashDuration(), kp->getSquashSlowdown() * kart->getPlayerDifficulty()->getSquashSlowdown()); } continue; } // now the first object must be a projectile // ========================================= if(p->getUserPointer(1)->is(UserPointer::UP_TRACK)) { // Projectile hits track // --------------------- p->getUserPointer(0)->getPointerFlyable()->hitTrack(); } else if(p->getUserPointer(1)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Projectile hits physical object // ------------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); Flyable* flyable = p->getUserPointer(0)->getPointerFlyable(); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); std::string obj_id = obj->getID(); std::string scripting_function = obj->getOnItemCollisionFunction(); if (scripting_function.size() > 0) { script_engine->runFunction("void " + scripting_function + "(int, int, const string)", [&](asIScriptContext* ctx) { ctx->SetArgDWord(0, (int)flyable->getType()); ctx->SetArgDWord(1, flyable->getOwnerId()); ctx->SetArgObject(2, &obj_id); }); } flyable->hit(NULL, obj); if (obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) { int kartId = p->getUserPointer(0)->getPointerFlyable()->getOwnerId(); SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setLastKartTohitBall(kartId); } } else if(p->getUserPointer(1)->is(UserPointer::UP_KART)) { // Projectile hits kart // -------------------- // Only explode a bowling ball if the target is // not invulnerable AbstractKart* target_kart = p->getUserPointer(1)->getPointerKart(); PowerupManager::PowerupType type = p->getUserPointer(0)->getPointerFlyable()->getType(); if(type != PowerupManager::POWERUP_BOWLING || !target_kart->isInvulnerable()) { Flyable *f = p->getUserPointer(0)->getPointerFlyable(); f->hit(target_kart); // Check for achievements AbstractKart * kart = World::getWorld()->getKart(f->getOwnerId()); PlayerController *c = dynamic_cast<PlayerController*>(kart->getController()); // Check that it's not a kart hitting itself (this can // happen at the time a flyable is shot - release too close // to the kart, and it's the current player. At this stage // only the current player can get achievements. if (target_kart != kart && c && c->getPlayer()->getConstProfile() == PlayerManager::getCurrentPlayer()) { // Compare the current value of hits with the 'hit' goal value // (otherwise it would be compared with the kart id goal value, // which doesn't exist. PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_ARCH_ENEMY, target_kart->getIdent(), 1, "hit"); if (type == PowerupManager::POWERUP_BOWLING) { PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_STRIKE, "ball", 1); } // is bowling ball } // if target_kart != kart && is a player kart and is current player } } else { // Projectile hits projectile // -------------------------- p->getUserPointer(0)->getPointerFlyable()->hit(NULL); p->getUserPointer(1)->getPointerFlyable()->hit(NULL); } } // for all p in m_all_collisions m_physics_loop_active = false; // Now remove the karts that were removed while the above loop // was active. Now we can safely call removeKart, since the loop // is finished and m_physics_world_active is not set anymore. for(unsigned int i=0; i<m_karts_to_delete.size(); i++) removeKart(m_karts_to_delete[i]); m_karts_to_delete.clear(); PROFILER_POP_CPU_MARKER(); } // update