void Display() // Display function. { // Turn on z-buffering. glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); // Set up the view matrix. glMatrixMode(GL_MODELVIEW); glLoadIdentity(); matrix ViewMatrix; ViewMatrix.View(ViewerDir, ViewerUp, ViewerLoc); OGLViewMatrix(ViewMatrix); // Transform the frustum planes from view coords into world coords. int i; for (i = 0; i < 6; i++) { // Rotate the plane from view coords into world coords. I'm pretty sure this is not a slick // way to do this :) PlaneInfo& tp = TransformedFrustumPlane[i]; PlaneInfo& p = FrustumPlane[i]; ViewMatrix.ApplyInverseRotation(&tp.Normal, p.Normal); vector v; ViewMatrix.ApplyInverse(&v, p.Normal * p.D); tp.D = v * tp.Normal; } // Clear buffers. glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Draw the quadtree. if (root) { root->Update(RootCornerData, (const float*) ViewerLoc, Detail); TriangleCounter += root->Render(RootCornerData, Textured); } // Show frame. glutSwapBuffers(); }
void Update() // Runs one iteration of the game loop. { CurrentTicks = Timer::GetTicks() - StartTicks; int DeltaTicks = CurrentTicks - LastTicks; // Force DeltaTicks to a fixed value if we're making a movie. if (Config::GetValue("RecordMoviePath") /*&& Config::GetBool("RecordMoviePause") == false*/ ) { DeltaTicks = 33; if (MovieFrameNumber % 3 == 0) DeltaTicks++; // Exactly 30 frames/sec. // Monkey with the time base so that CurrentTicks // increments at 30 frames/sec in sync with DeltaTicks. // This is so code which samples CurrentTicks will behave // properly during movie recording. CurrentTicks = LastTicks + DeltaTicks; // StartTicks = (Timer::GetTicks() - CurrentTicks); } if (MovieMode == true) { LastTicks = CurrentTicks; if (DeltaTicks > 200) DeltaTicks = 200; // // Movie mode. // // Don't do world update and rendering. Just show the movie, until // it's done, or until the user escapes past the movie. // Check for user escape. int UpdateCount = 0; // For protection against update functions taking longer than the sampling period. UpdateState u; while (UpdatesPending() && UpdateCount++ < 30) { GetNextUpdateState(&u); } if (Input::CheckForEventDown(Input::BUTTON0) || Input::CheckForEventDown(Input::ENTER) || Input::CheckForEventDown(Input::ESCAPE) || Input::CheckForEventDown(Input::BUTTON2) || Config::GetBoolValue("SkipIntro")) { // Cut the movie short. MovieDuration = 0; } Input::EndFrameNotify(); // Show a new frame. glViewport(0, 0, Render::GetWindowWidth(), Render::GetWindowHeight()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (MoviePlayer) MoviePlayer->play(DeltaTicks); if (Config::GetValue("RecordMoviePath")) SaveMovieFrame(); Render::ShowFrame(); MovieDuration -= DeltaTicks; if (MovieDuration <= 0) { // Done with the movie, now exit movie mode. MovieDuration = 0; if (MoviePlayer) { MoviePlayer->unRef(); MoviePlayer = NULL; } MovieMode = false; } // Sleep a little. int SleepTime = 13 - DeltaTicks; if (SleepTime > 0) { Timer::Sleep(SleepTime); } return; } // // Update the game logic & physics. // UpdateState u; if (Config::GetValue("RecordMoviePath") != NULL) { // Special case -- simulate a fixed update rate. // Consume input. do { GetNextUpdateState(&u); } while (UpdatesPending()); u.DeltaTicks = DeltaTicks; // Force frame time. u.Ticks = CurrentTicks; DoUpdates(u); } else { // Normal case -- consume input info and update in real-time. int UpdateCount = 0; // For protection against update functions taking longer than the sampling period. while (UpdatesPending() && UpdateCount++ < 30) { GetNextUpdateState(&u); DoUpdates(u); } if (!Config::GetBoolValue("LimitUpdateRate")) { UpdateCount++; GetNextUpdateState(&u); //xxxxxx Config::SetBoolValue("LastUpdateBeforeRender", true); //xxxxx DoUpdates(u); } else { if (UpdateCount) { Config::SetBoolValue("LastUpdateBeforeRender", true); } } if (UpdateCount == 0) { return; } } LastTicks = CurrentTicks; // Don't try to render if we've entered movie mode during the updates. if (MovieMode) return; // Don't render if we're about to quit. if (Main::GetQuit()) return; // Render visuals. int number_of_players = MultiPlayer::NumberOfLocalPlayers(); int window_corner_x[4]; int window_corner_y[4]; int window_size_x[4]; int window_size_y[4]; SetWindowCoordinates(number_of_players, window_corner_x, window_corner_y, window_size_x, window_size_y); // For each player, draw graphics for (int player_index = 0; player_index < number_of_players; player_index++){ MultiPlayer::SetCurrentPlayerIndex(player_index); ViewState s; // Increment the frame number. //if (player_index == 0) FrameNumber++; s.FrameNumber = FrameNumber; // Set the time value. s.Ticks = u.Ticks; float ViewAngle = Config::GetFloatValue("ViewAngle") * (PI / 180); // Get viewer orientation. vec3 ViewerDir = XAxis; vec3 ViewerUp = YAxis; vec3 ViewerLoc = ZeroVector; if (Viewer) { ViewerDir = Viewer->GetDirection(); ViewerUp = Viewer->GetUp(); ViewerLoc = Viewer->GetLocation(); if (ViewerDir.checknan() || ViewerUp.checknan()) { // Trouble. Fall back to a default orientation. ViewerDir = XAxis; ViewerUp = YAxis; } if (ViewerLoc.checknan()) { // Trouble. Fall back to a default location. ViewerLoc = ZeroVector; } } // Establish transformation from world coordinates to view coordinates. s.CameraMatrix.View(ViewerDir, ViewerUp, ViewerLoc); s.ViewMatrix = s.CameraMatrix; s.Viewpoint = ViewerLoc; s.MinZ = 1; s.MaxZ = Z_MAX; s.OneOverMaxZMinusMinZ = 1.0f / (s.MaxZ - s.MinZ); // Set the clipping planes. // s.ClipPlaneCount = 0; // Near. s.ClipPlane[0].Normal = ZAxis; s.ClipPlane[0].D = s.MinZ; // s.ClipPlaneCount++; // Left. s.ClipPlane[1].Normal = vec3(-cosf(ViewAngle/2), 0, sinf(ViewAngle/2)); s.ClipPlane[1].D = 0; // s.ClipPlaneCount++; // Right. s.ClipPlane[2].Normal = vec3(cosf(ViewAngle/2), 0, sinf(ViewAngle/2)); s.ClipPlane[2].D = 0; // s.ClipPlaneCount++; // float AspectRatio = float(Render::GetWindowHeight()) / float(Render::GetWindowWidth() * 2.0); // Bjorn // float VerticalAngle2 = atanf(tanf(ViewAngle/2) * AspectRatio); float AspectRatio = float(window_size_y[player_index]) / float(window_size_x[player_index]); // Bjorn float VerticalAngle2 = atanf(tanf(ViewAngle/2) * AspectRatio); // Top. s.ClipPlane[3].Normal = vec3(0, -cosf(VerticalAngle2), sinf(VerticalAngle2)); s.ClipPlane[3].D = 0; // s.ClipPlaneCount++; // Bottom. s.ClipPlane[4].Normal = vec3(0, cosf(VerticalAngle2), sinf(VerticalAngle2)); s.ClipPlane[4].D = 0; // s.ClipPlaneCount++; // Far. s.ClipPlane[5].Normal = -ZAxis; s.ClipPlane[5].D = -s.MaxZ; // s.ClipPlaneCount++; // Set the projection factors so the view volume fills the screen. s.XProjectionFactor = (Render::GetWindowWidth() - 0.6f) / 2 / tanf(ViewAngle/2); s.YProjectionFactor = s.XProjectionFactor; // Set the x/y offsets so that 0,0 appears in the middle of the screen. s.XOffset = (Render::GetWindowWidth() + 1) * 0.5f; s.YOffset = (Render::GetWindowHeight() + 1) * 0.5f; // Only clear the screen the first time each frame if (player_index == 0){ Render::BeginFrame(); Render::ClearFrame(); } glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); // Set the viewport (split the screen) glViewport(window_corner_x[player_index], window_corner_y[player_index], window_size_x[player_index], window_size_y[player_index]); // Set up OpenGL matrices. glMatrixMode(GL_PROJECTION); glLoadIdentity(); float w = 2 * s.MinZ * tanf(ViewAngle/2); float h = w * AspectRatio; OGLFrustum(w, h, s.MinZ, s.MaxZ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); OGLViewMatrix(ViewerDir, ViewerUp, ViewerLoc); // Update the terrain. TerrainMesh::Update(s); // Set up fog parameters. if (Config::GetBoolValue("Fog")) { glEnable(GL_FOG); glFogi(GL_FOG_MODE, GL_LINEAR /* GL_EXP */); glFogf(GL_FOG_START, 0); glFogf(GL_FOG_END, Weather::GetFadeDistance()); // For GL_LINEAR mode. // glFogf(GL_FOG_DENSITY, 4.852 / Weather::GetFadeDistance()); // For GL_EXP mode; meaningless for GL_LINEAR mode. glHint(GL_FOG_HINT, GL_NICEST); glFogfv(GL_FOG_COLOR, Weather::GetFadeColor()); } // Determine far clipping plane, and decide whether or not to do two passes. float MaxZ = fmin(Z_MAX, Weather::GetFadeDistance()); bool TwoPass = true; // Should set to false if we have a 24- or 32-bit z-buffer... if (MaxZ < TWO_PASS_CUTOFF) TwoPass = false; if (TwoPass && Config::GetBoolValue("ZSinglePass")) TwoPass = false; // Manual override, force single pass. float MidZ = MaxZ; if (TwoPass) { // // Draw the distant part of the world. // // Pick a z value to separate the near and far passes, in order to optimize z-buffer usage. MidZ = sqrtf(Z_MIN * Z_MAX); s.MaxZ = MaxZ; s.MinZ = MidZ * 0.9f; s.OneOverMaxZMinusMinZ = 1.0f / (s.MaxZ - s.MinZ); // Near. s.ClipPlane[0].Normal = ZAxis; s.ClipPlane[0].D = s.MinZ; // Far. s.ClipPlane[5].Normal = -ZAxis; s.ClipPlane[5].D = -s.MaxZ; // Set up OpenGL matrices. glMatrixMode(GL_PROJECTION); glLoadIdentity(); w = 2 * s.MinZ * tanf(ViewAngle/2); h = w * AspectRatio; OGLFrustum(w, h, s.MinZ, s.MaxZ); glMatrixMode(GL_MODELVIEW); // glLoadIdentity(); // OGLViewMatrix(Viewer->GetDirection(), Viewer->GetUp(), Viewer->GetLocation()); // Bjorn : moved this for sky to work when having two players // Draw a backdrop. glDisable(GL_FOG); Weather::RenderBackdrop(s); TerrainMesh::Render(s); Model::Render(s); } // // Draw closer part of terrain. // Render::ClearZBuffer(); s.MaxZ = MidZ; s.MinZ = Z_MIN; s.OneOverMaxZMinusMinZ = 1.0f / (s.MaxZ - s.MinZ); // Near. s.ClipPlane[0].Normal = ZAxis; s.ClipPlane[0].D = s.MinZ; // Far. s.ClipPlane[5].Normal = -ZAxis; s.ClipPlane[5].D = -s.MaxZ; // Set up OpenGL matrices. glMatrixMode(GL_PROJECTION); glLoadIdentity(); w = 2 * s.MinZ * tanf(ViewAngle/2); h = w * AspectRatio; OGLFrustum(w, h, s.MinZ, s.MaxZ); glMatrixMode(GL_MODELVIEW); // glLoadIdentity(); // OGLViewMatrix(Viewer->GetDirection(), Viewer->GetUp(), Viewer->GetLocation()); TerrainMesh::Render(s); // Draw the objects. Model::Render(s); // // Overlay effects. // // Turn off the fog for overlay stuff. glDisable(GL_FOG); if (Config::GetBoolValue("BoarderShadow")) { // Draw boarder shadow. Boarder::RenderShadow(s); } // Draw a trail in the snow. // Trail::Render(s); // Draw ballistic particles. Particle::Render(s); // Draw weather overlays (falling snow, etc). if (player_index == 0) Weather::RenderOverlay(s, true); else Weather::RenderOverlay(s, false); // End of 3D drawing. Render::EndFrame(); // Draw any UI stuff. UI::Render(s, player_index); // Music captions. Music::Render(s); // Console. Console::Render(s); // Remember the frame time, for computing frame rate stats. FrameTimeQueue[NextFTSample].Timestamp = CurrentTicks; FrameTimeQueue[NextFTSample].FrameTime = DeltaTicks; NextFTSample += 1; if (NextFTSample >= FT_SAMPLES) NextFTSample = 0; // Show the frame rate. if (Config::GetBoolValue("ShowFrameRate")) { // Compute min, max, average frame times over the past one second. int min = 1000, max = 1, sum = 0, SampleCount = 0; int i; for (i = 0; i < FT_SAMPLES; i++) { FrameTimeSample& s = FrameTimeQueue[i]; if (CurrentTicks - s.Timestamp < 1000) { // Sample is within the last second. if (s.FrameTime < min) min = s.FrameTime; if (s.FrameTime > max) max = s.FrameTime; sum += s.FrameTime; SampleCount++; } } // Compute corresponding frame rates. float MinFR, MaxFR, AvgFR; MinFR = 1000.0f / max; MaxFR = 1000.0f / min; if (SampleCount) { AvgFR = (SampleCount * 1000.0f) / sum; } // Show the stats. char buf[80]; strcpy(buf, "fps: "); Text::FormatNumber(buf + strlen(buf), 1000.0f / DeltaTicks, 2, 1); // last strcat(buf, "/"); Text::FormatNumber(buf + strlen(buf), MinFR, 2, 1); // min strcat(buf, "/"); Text::FormatNumber(buf + strlen(buf), MaxFR, 2, 1); // max strcat(buf, "/"); Text::FormatNumber(buf + strlen(buf), AvgFR, 2, 1); // avg Text::DrawString(5, 12, Text::FIXEDSYS, Text::ALIGN_LEFT, buf); // Show a scrolling bar graph of frame rate. GUIBegin();//xxxx for (i = 0; i < FT_SAMPLES; i++) { FrameTimeSample& s = FrameTimeQueue[(NextFTSample + i) % FT_SAMPLES]; float height = 0; if (s.FrameTime) height = (1000.0f / s.FrameTime) / 240.0f; glColor3f(1, 0.25f, 0.25f); glBegin(GL_QUADS); glVertex2f((i-320)/320.0f, 180/240.0f + 0); glVertex2f((i-320+1)/320.0f, 180/240.0f + 0); glVertex2f((i-320+1)/320.0f, 180/240.0f + height); glVertex2f((i-320)/320.0f, 180/240.0f + height); glEnd(); } GUIEnd();//xxxx } // Show some other miscellaneous info, if desired. if (Config::GetBoolValue("ShowRenderStats")) { char buf[1000]; sprintf(buf, "Model:\n texels: %dK\n tris: %d\n" "Terrain:\n tris: %d\n active nodes: %d\n total nodes: %d\n nudge: %g\n" " cache:\n texels: %dK\n active nodes = %d\n nodes built = %d\n thrash ct = %d" , Model::GetTexelCount() / 1024, Model::GetRenderedTriangleCount(), TerrainMesh::GetRenderedTriangleCount(), TerrainMesh::GetNodesActiveCount(), TerrainMesh::GetNodesTotalCount(), TerrainMesh::GetDetailNudge(), Surface::GetTexelCount() / 1024, Surface::GetActiveNodeCount(), Surface::GetNodesBuilt(), Surface::GetThrashCount() ); Text::DrawMultiLineString(5, 24, Text::FIXEDSYS, Text::ALIGN_LEFT, 640, buf); } // Show viewer location. if (Config::GetBoolValue("ShowViewerLocation") && Viewer) { char buf[80]; Text::FontID f = Text::FIXEDSYS; vec3 v = ViewerLoc; int y = 400; int dy = Text::GetFontHeight(f); Text::FormatNumber(buf, v.X() + 32768, 4, 1); Text::DrawString(20, y, f, Text::ALIGN_LEFT, buf, 0xFF000000); y += dy; Text::FormatNumber(buf, v.Y(), 4, 1); Text::DrawString(20, y, f, Text::ALIGN_LEFT, buf, 0xFF000000); y += dy; Text::FormatNumber(buf, v.Z() + 32768, 4, 1); Text::DrawString(20, y, f, Text::ALIGN_LEFT, buf, 0xFF000000); y += dy; } // // Log user speed xxxxxx // { // char buf[80]; // float speed = 0; // MDynamic* d = Game::GetUser(); // if (d) speed = d->GetVelocity().magnitude(); // Text::FormatNumber(buf, speed * 2.2369, 3, 1); // Text::DrawString(40, 400, Text::DEFAULT, Text::ALIGN_LEFT, buf); // } // // xxxxxxxx Overlay::Render(); if (Config::GetValue("RecordMoviePath")) SaveMovieFrame(); const char* fn = Config::GetValue("SaveFramePPM"); if (fn) { Render::WriteScreenshotFilePPM(fn); Config::SetValue("SaveFramePPM", NULL); } } // end of two player display Render::ShowFrame(); //xxxxxxx Config::SetBoolValue("F4Pressed", false); }