void RWGame::render(float alpha, float time) { lastDraws = getRenderer()->getRenderer()->getDrawCount(); getRenderer()->getRenderer()->swap(); auto size = getWindow().getSize(); renderer->setViewport(size.x, size.y); ViewCamera viewCam; viewCam.frustum.fov = glm::radians(90.f); if( state->currentCutscene != nullptr && state->cutsceneStartTime >= 0.f ) { auto cutscene = state->currentCutscene; float cutsceneTime = std::min(world->getGameTime() - state->cutsceneStartTime, cutscene->tracks.duration); cutsceneTime += GAME_TIMESTEP * alpha; glm::vec3 cameraPos = cutscene->tracks.getPositionAt(cutsceneTime), targetPos = cutscene->tracks.getTargetAt(cutsceneTime); float zoom = cutscene->tracks.getZoomAt(cutsceneTime); viewCam.frustum.fov = glm::radians(zoom); float tilt = cutscene->tracks.getRotationAt(cutsceneTime); auto direction = glm::normalize(targetPos - cameraPos); auto right = glm::normalize(glm::cross(glm::vec3(0.f, 0.f, 1.f), direction)); auto up = glm::normalize(glm::cross(direction, right)); glm::mat3 m; m[0][0] = direction.x; m[0][1] = right.x; m[0][2] = up.x; m[1][0] = direction.y; m[1][1] = right.y; m[1][2] = up.y; m[2][0] = direction.z; m[2][1] = right.z; m[2][2] = up.z; auto qtilt = glm::angleAxis(glm::radians(tilt), direction); cameraPos += cutscene->meta.sceneOffset; targetPos += cutscene->meta.sceneOffset; viewCam.position = cameraPos; viewCam.rotation = glm::inverse(glm::quat_cast(m)) * qtilt; } else if( state->cameraFixed ) { viewCam.position = state->cameraPosition; viewCam.rotation = state->cameraRotation; } else { // There's no cutscene playing - use the camera returned by the State. viewCam.position = glm::mix(lastCam.position, nextCam.position, alpha); viewCam.rotation = glm::slerp(lastCam.rotation, nextCam.rotation, alpha); } viewCam.frustum.aspectRatio = window.getSize().x / (float) window.getSize().y; if ( state->isCinematic ) { viewCam.frustum.fov *= viewCam.frustum.aspectRatio; } glEnable(GL_DEPTH_TEST); glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); renderer->getRenderer()->pushDebugGroup("World"); RW_PROFILE_BEGIN("world"); renderer->renderWorld(world, viewCam, alpha); RW_PROFILE_END(); auto rendertime = renderer->getRenderer()->popDebugGroup(); RW_PROFILE_BEGIN("debug"); if( showDebugPaths ) { renderDebugPaths(time); } if ( showDebugStats ) { renderDebugStats(time, rendertime); } if( showDebugPhysics ) { if( world ) { world->dynamicsWorld->debugDrawWorld(); debug->flush(renderer); } } RW_PROFILE_END(); drawOnScreenText(world, renderer); }
int RWGame::run() { last_clock_time = clock.now(); // Loop until the window is closed or we run out of state. while (window.isOpen() && StateManager::get().states.size()) { State* state = StateManager::get().states.back(); RW_PROFILE_FRAME_BOUNDARY(); RW_PROFILE_BEGIN("Input"); SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: window.close(); break; case SDL_WINDOWEVENT: switch (event.window.event) { case SDL_WINDOWEVENT_FOCUS_GAINED: inFocus = true; break; case SDL_WINDOWEVENT_FOCUS_LOST: inFocus = false; break; } break; case SDL_KEYDOWN: globalKeyEvent(event); break; case SDL_MOUSEMOTION: event.motion.xrel *= MOUSE_SENSITIVITY_SCALE; event.motion.yrel *= MOUSE_SENSITIVITY_SCALE; break; } RW_PROFILE_BEGIN("State"); state->handleEvent(event); RW_PROFILE_END() } RW_PROFILE_END(); auto now = clock.now(); float timer = std::chrono::duration<float>(now - last_clock_time).count(); last_clock_time = now; accum += timer * timescale; RW_PROFILE_BEGIN("Update"); if ( accum >= GAME_TIMESTEP ) { RW_PROFILE_BEGIN("state"); StateManager::get().tick(GAME_TIMESTEP); RW_PROFILE_END(); if (StateManager::get().states.size() == 0) { break; } RW_PROFILE_BEGIN("engine"); tick(GAME_TIMESTEP); RW_PROFILE_END(); accum -= GAME_TIMESTEP; // Throw away time if the accumulator reaches too high. if ( accum > GAME_TIMESTEP * 5.f ) { accum = 0.f; } } RW_PROFILE_END(); float alpha = fmod(accum, GAME_TIMESTEP) / GAME_TIMESTEP; if( ! state->shouldWorldUpdate() ) { alpha = 1.f; } RW_PROFILE_BEGIN("Render"); RW_PROFILE_BEGIN("engine"); render(alpha, timer); RW_PROFILE_END(); RW_PROFILE_BEGIN("state"); if (StateManager::get().states.size() > 0) { StateManager::get().draw(renderer); } RW_PROFILE_END(); RW_PROFILE_END(); renderProfile(); window.swap(); } if( httpserver_thread ) { httpserver_thread->join(); } return 0; }
int RWGame::run() { clock.restart(); // Loop until the window is closed or we run out of state. while (window.isOpen() && StateManager::get().states.size()) { State* state = StateManager::get().states.back(); RW_PROFILE_FRAME_BOUNDARY(); RW_PROFILE_BEGIN("Input"); sf::Event event; while (window.pollEvent(event)) { switch (event.type) { case sf::Event::GainedFocus: inFocus = true; break; case sf::Event::LostFocus: inFocus = false; break; case sf::Event::KeyPressed: globalKeyEvent(event); break; case sf::Event::Closed: return 0; default: break; } RW_PROFILE_BEGIN("State"); state->handleEvent(event); RW_PROFILE_END() } RW_PROFILE_END(); if(! window.isOpen() ) { break; } float timer = clock.restart().asSeconds(); accum += timer * timescale; RW_PROFILE_BEGIN("Update"); if ( accum >= GAME_TIMESTEP ) { RW_PROFILE_BEGIN("state"); StateManager::get().tick(GAME_TIMESTEP); RW_PROFILE_END(); if (StateManager::get().states.size() == 0) { break; } RW_PROFILE_BEGIN("engine"); tick(GAME_TIMESTEP); RW_PROFILE_END(); accum -= GAME_TIMESTEP; // Throw away time if the accumulator reaches too high. if ( accum > GAME_TIMESTEP * 5.f ) { accum = 0.f; } } RW_PROFILE_END(); float alpha = fmod(accum, GAME_TIMESTEP) / GAME_TIMESTEP; if( ! state->shouldWorldUpdate() ) { alpha = 1.f; } RW_PROFILE_BEGIN("Render"); RW_PROFILE_BEGIN("engine"); render(alpha, timer); RW_PROFILE_END(); RW_PROFILE_BEGIN("state"); if (StateManager::get().states.size() > 0) { StateManager::get().draw(renderer); } RW_PROFILE_END(); RW_PROFILE_END(); renderProfile(); window.display(); } if( httpserver_thread ) { httpserver_thread->join(); } return 0; }
void GameRenderer::renderWorld(GameWorld* world, const ViewCamera &camera, float alpha) { _renderAlpha = alpha; _renderWorld = world; // Store the input camera, _camera = camera; setupRender(); glBindVertexArray( vao ); float tod = world->getHour() + world->getMinute()/60.f; // Requires a float 0-24 auto weatherID = static_cast<WeatherLoader::WeatherCondition>(world->state->basic.nextWeather * 24); auto weather = world->data->weatherLoader.getWeatherData(weatherID, tod); glm::vec3 skyTop = weather.skyTopColor; glm::vec3 skyBottom = weather.skyBottomColor; glm::vec3 ambient = weather.ambientColor; glm::vec3 dynamic = weather.directLightColor; float theta = (tod/(60.f * 24.f) - 0.5f) * 2 * 3.14159265; glm::vec3 sunDirection{ sin(theta), 0.0, cos(theta), }; sunDirection = glm::normalize(sunDirection); _camera.frustum.near = world->state->cameraNear; _camera.frustum.far = weather.farClipping; auto view = _camera.getView(); auto proj = _camera.frustum.projection(); Renderer::SceneUniformData sceneParams { proj, view, glm::vec4{ambient, 0.0f}, glm::vec4{dynamic, 0.0f}, glm::vec4(skyBottom, 1.f), glm::vec4(camera.position, 0.f), weather.fogStart, camera.frustum.far }; renderer->setSceneParameters(sceneParams); renderer->clear(glm::vec4(skyBottom, 1.f)); _camera.frustum.update(proj * view); if (cullOverride) { cullingCamera.frustum.update( cullingCamera.frustum.projection() * cullingCamera.getView()); } culled = 0; renderer->useProgram(worldProg); //=============================================================== // Render List Construction //--------------------------------------------------------------- RW_PROFILE_BEGIN("RenderList"); // This is sequential at the moment, it should be easy to make it // run in parallel with a good threading system. RenderList renderList; // Naive optimisation, assume 10% hitrate renderList.reserve(world->allObjects.size() * 0.5f); RW_PROFILE_BEGIN("Build"); ObjectRenderer objectRenderer(_renderWorld, (cullOverride ? cullingCamera : _camera), _renderAlpha, getMissingTexture()); // World Objects for (auto object : world->allObjects) { objectRenderer.buildRenderList(object, renderList); } RW_PROFILE_END(); renderer->pushDebugGroup("Objects"); renderer->pushDebugGroup("RenderList"); // Also parallelizable RW_PROFILE_BEGIN("Sort"); std::sort(renderList.begin(), renderList.end(), [](const Renderer::RenderInstruction& a, const Renderer::RenderInstruction&b) { return a.sortKey < b.sortKey; }); RW_PROFILE_END(); RW_PROFILE_BEGIN("Draw"); renderer->drawBatched(renderList); RW_PROFILE_END(); renderer->popDebugGroup(); profObjects = renderer->popDebugGroup(); RW_PROFILE_END(); // Render arrows above anything that isn't radar only (or hidden) ModelRef& arrowModel = world->data->models["arrow"]; if( arrowModel && arrowModel->resource ) { auto arrowTex = world->data->textures[{"copblue",""}]; auto arrowFrame = arrowModel->resource->findFrame( "arrow" ); for( auto& blip : world->state->radarBlips ) { if( blip.second.display == BlipData::Show ) { glm::mat4 model; if( blip.second.target > 0 ) { // TODO restore arrows /*auto& pool = world->getTypeObjectPool(blip.second.target); auto object = pool.find(blip.second.target); if( object ) { model = object->getTimeAdjustedTransform( _renderAlpha ); }*/ } else { model = glm::translate( model, blip.second.coord ); } float a = world->getGameTime() * glm::pi<float>(); model = glm::translate( model, glm::vec3(0.f, 0.f, 2.5f + glm::sin( a ) * 0.5f) ); model = glm::rotate( model, a, glm::vec3(0.f, 0.f, 1.f) ); model = glm::scale( model, glm::vec3(1.5f, 1.5f, 1.5f) ); Renderer::DrawParameters dp; dp.textures = {arrowTex->getName()}; dp.ambient = 1.f; dp.colour = glm::u8vec4(255, 255, 255, 255); auto geom = arrowModel->resource->geometries[arrowFrame->getGeometries()[0]]; Model::SubGeometry& sg = geom->subgeom[0]; dp.start = sg.start; dp.count = sg.numIndices; dp.diffuse = 1.f; renderer->draw( model, &geom->dbuff, dp ); } } } // Draw goal indicators glDepthMask(GL_FALSE); renderer->useProgram( particleProg ); for(auto& i : world->getAreaIndicators()) { renderAreaIndicator( &i ); } glDepthMask(GL_TRUE); renderer->pushDebugGroup("Water"); water.render(this, world); profWater = renderer->popDebugGroup(); renderer->pushDebugGroup("Sky"); glBindVertexArray( vao ); Renderer::DrawParameters dp; dp.start = 0; dp.count = skydomeSegments * skydomeRows * 6; renderer->useProgram(skyProg); renderer->setUniform(skyProg, "TopColor", glm::vec4(skyTop, 1.f)); renderer->setUniform(skyProg, "BottomColor", glm::vec4(skyBottom, 1.f)); renderer->draw(glm::mat4(), &skyDbuff, dp); profSky = renderer->popDebugGroup(); renderer->pushDebugGroup("Effects"); renderEffects(world); profEffects = renderer->popDebugGroup(); glDisable(GL_DEPTH_TEST); GLuint splashTexName = 0; auto fc = world->state->fadeColour; if((fc.r + fc.g + fc.b) == 0 && world->state->currentSplash.size() > 0) { auto splash = world->data->findTexture(world->state->currentSplash); if ( splash ) { splashTexName = splash->getName(); } } if( (world->state->isCinematic || world->state->currentCutscene ) && splashTexName != 0 ) { renderLetterbox(); } float fadeTimer = world->getGameTime() - world->state->fadeStart; if( fadeTimer < world->state->fadeTime || !world->state->fadeOut ) { glUseProgram(ssRectProgram); glUniform2f(ssRectOffset, 0.f, 0.f); glUniform2f(ssRectSize, 1.f, 1.f); glUniform1i(ssRectTexture, 0); if(splashTexName != 0) { glBindTexture(GL_TEXTURE_2D, splashTexName); fc = glm::u16vec3(0, 0, 0); } else { glBindTexture(GL_TEXTURE_2D, 0); } float fadeFrac = 0.f; if( world->state->fadeTime > 0.f ) { fadeFrac = std::min(fadeTimer / world->state->fadeTime, 1.f); } float a = world->state->fadeOut ? 1.f - fadeFrac : fadeFrac; glm::vec4 fadeNormed(fc.r / 255.f, fc.g/ 255.f, fc.b/ 255.f, a); glUniform4fv(ssRectColour, 1, glm::value_ptr(fadeNormed)); glBindVertexArray( ssRectDraw.getVAOName() ); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } if( (world->state->isCinematic || world->state->currentCutscene ) && splashTexName == 0 ) { renderLetterbox(); } renderPostProcess(); glUseProgram(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray( 0 ); }