New grow and skid sounds from remaxim
[supertux.git] / src / video / gl_renderer.cpp
index 7683774..c861595 100644 (file)
 #include "obstack/obstackpp.hpp"
 #define LIGHTMAP_DIV 5
 
+#ifdef GL_VERSION_ES_CM_1_0
+#  define glOrtho glOrthof
+#endif
+
 namespace 
 {
+
 inline void intern_draw(float left, float top, float right, float bottom,
                         float uv_left, float uv_top,
                         float uv_right, float uv_bottom,
@@ -60,64 +65,67 @@ inline void intern_draw(float left, float top, float right, float bottom,
  
   if(effect & VERTICAL_FLIP) 
     std::swap(uv_top, uv_bottom);
+
+  // unrotated blit
+  glBlendFunc(blend.sfactor, blend.dfactor);
+  glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
  
-  if (angle == 0.0f)
-    { // unrotated blit
-      glBlendFunc(blend.sfactor, blend.dfactor);
-      glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
-      glBegin(GL_QUADS);
-      glTexCoord2f(uv_left, uv_top);
-      glVertex2f(left, top);
-
-      glTexCoord2f(uv_right, uv_top);
-      glVertex2f(right, top);
-
-      glTexCoord2f(uv_right, uv_bottom);
-      glVertex2f(right, bottom);
-
-      glTexCoord2f(uv_left, uv_bottom);
-      glVertex2f(left, bottom);
-      glEnd();
-    }
-  else
-    { // rotated blit
-      float center_x = (left + right) / 2;
-      float center_y = (top + bottom) / 2;
-
-      float sa = sinf(angle/180.0f*M_PI);
-      float ca = cosf(angle/180.0f*M_PI);
-
-      left  -= center_x;
-      right -= center_x;
-
-      top    -= center_y;
-      bottom -= center_y;
-
-      glBlendFunc(blend.sfactor, blend.dfactor);
-      glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
-      glBegin(GL_QUADS);
-      glTexCoord2f(uv_left, uv_top);
-      glVertex2f(left*ca - top*sa + center_x,
-                 left*sa + top*ca + center_y);
-
-      glTexCoord2f(uv_right, uv_top);
-      glVertex2f(right*ca - top*sa + center_x,
-                 right*sa + top*ca + center_y);
-
-      glTexCoord2f(uv_right, uv_bottom);
-      glVertex2f(right*ca - bottom*sa + center_x,
-                 right*sa + bottom*ca + center_y);
-
-      glTexCoord2f(uv_left, uv_bottom);
-      glVertex2f(left*ca - bottom*sa + center_x,
-                 left*sa + bottom*ca + center_y);
-      glEnd();
-    }
+  if (angle == 0.0f) {
+    float vertices[] = {
+      left, top,
+      right, top,
+      right, bottom,
+      left, bottom,
+    };
+    glVertexPointer(2, GL_FLOAT, 0, vertices);
+
+    float uvs[] = {
+      uv_left, uv_top,
+      uv_right, uv_top,
+      uv_right, uv_bottom,
+      uv_left, uv_bottom,
+    };
+    glTexCoordPointer(2, GL_FLOAT, 0, uvs);
+
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+  } else {
+    // rotated blit
+    float center_x = (left + right) / 2;
+    float center_y = (top + bottom) / 2;
+
+    float sa = sinf(angle/180.0f*M_PI);
+    float ca = cosf(angle/180.0f*M_PI);
+
+    left  -= center_x;
+    right -= center_x;
+
+    top    -= center_y;
+    bottom -= center_y;
+
+    float vertices[] = {
+        left*ca - top*sa + center_x, left*sa + top*ca + center_y,
+        right*ca - top*sa + center_x, right*sa + top*ca + center_y,
+        right*ca - bottom*sa + center_x, right*sa + bottom*ca + center_y,
+        left*ca - bottom*sa + center_x, left*sa + bottom*ca + center_y
+    };
+    glVertexPointer(2, GL_FLOAT, 0, vertices);
+
+    float uvs[] = {
+      uv_left, uv_top,
+      uv_right, uv_top,
+      uv_right, uv_bottom,
+      uv_left, uv_bottom,
+    };
+    glTexCoordPointer(2, GL_FLOAT, 0, uvs);
+
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+  }
 
   // FIXME: find a better way to restore the blend mode
   glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 }
+
 }
 
 namespace GL {
@@ -143,14 +151,16 @@ Renderer::Renderer()
   if(texture_manager != 0)
     texture_manager->save_textures();
 
+#ifdef SDL_GL_SWAP_CONTROL
   if(config->try_vsync) {
     /* we want vsync for smooth scrolling */
     SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
   }
+#endif
 
   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 
-  // Hu? 16bit rendering?
+  // FIXME: Hu? 16bit rendering?
   SDL_GL_SetAttribute(SDL_GL_RED_SIZE,   5);
   SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
   SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,  5);
@@ -158,6 +168,7 @@ Renderer::Renderer()
   int flags = SDL_OPENGL;
   int width;
   int height;
+
   if(config->use_fullscreen)
     {
       flags |= SDL_FULLSCREEN;
@@ -166,13 +177,14 @@ Renderer::Renderer()
     }
   else
     {
-      flags |= SDL_RESIZABLE;
+//      flags |= SDL_RESIZABLE;
       width  = config->window_width;
       height = config->window_height;
     }
-  int bpp = 0;
 
+  int bpp = 0;
   SDL_Surface *screen = SDL_SetVideoMode(width, height, bpp, flags);
+
   if(screen == 0) {
     std::stringstream msg;
     msg << "Couldn't set video mode (" << width << "x" << height
@@ -185,22 +197,13 @@ Renderer::Renderer()
   glDisable(GL_CULL_FACE);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_BLEND);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
-  glViewport(0, 0, screen->w, screen->h);
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-
-  // logical resolution here not real monitor resolution
-  glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
-
-  glMatrixMode(GL_MODELVIEW);
-  glLoadIdentity();
-  glTranslatef(0, 0, 0);
-
-  check_gl_error("Setting up view matrices");
-
-
+  // Init the projection matrix, viewport and stuff
+  apply_config();
+  
   if(texture_manager == 0)
     texture_manager = new TextureManager();
   else
@@ -260,7 +263,7 @@ Renderer::draw_surface_part(const DrawingRequest& request)
               uv_bottom,
               0.0,
               request.alpha,
-              Color(1.0, 1.0, 1.0),
+              request.color,
               Blend(),
               request.drawing_effect);
 }
@@ -274,14 +277,30 @@ Renderer::draw_gradient(const DrawingRequest& request)
   const Color& bottom = gradientrequest->bottom;
 
   glDisable(GL_TEXTURE_2D);
-  glBegin(GL_QUADS);
-  glColor4f(top.red, top.green, top.blue, top.alpha);
-  glVertex2f(0, 0);
-  glVertex2f(SCREEN_WIDTH, 0);
-  glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
-  glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
-  glVertex2f(0, SCREEN_HEIGHT);
-  glEnd();
+  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+  glEnableClientState(GL_COLOR_ARRAY);
+
+  float vertices[] = {
+    0, 0,
+    SCREEN_WIDTH, 0,
+    SCREEN_WIDTH, SCREEN_HEIGHT,
+    0, SCREEN_HEIGHT
+  };
+  glVertexPointer(2, GL_FLOAT, 0, vertices);
+
+  float colors[] = {
+    top.red, top.green, top.blue, top.alpha,
+    top.red, top.green, top.blue, top.alpha,
+    bottom.red, bottom.green, bottom.blue, bottom.alpha,
+    bottom.red, bottom.green, bottom.blue, bottom.alpha,
+  };
+  glColorPointer(4, GL_FLOAT, 0, colors);
+
+  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+  glDisableClientState(GL_COLOR_ARRAY);
+  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
   glEnable(GL_TEXTURE_2D);
   glColor4f(1, 1, 1, 1);
 }
@@ -292,6 +311,11 @@ Renderer::draw_filled_rect(const DrawingRequest& request)
   const FillRectRequest* fillrectrequest
     = (FillRectRequest*) request.request_data;
 
+  glDisable(GL_TEXTURE_2D);
+  glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
+            fillrectrequest->color.blue, fillrectrequest->color.alpha);
+  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+  
   if (fillrectrequest->radius != 0.0f)
     {
       // draw round rect
@@ -307,32 +331,37 @@ Renderer::draw_filled_rect(const DrawingRequest& request)
                  request.pos.x + fillrectrequest->size.x - radius,
                  request.pos.y + fillrectrequest->size.y - radius);
 
-      glDisable(GL_TEXTURE_2D);
-      glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
-                fillrectrequest->color.blue, fillrectrequest->color.alpha);
-
 
       int n = 8;
-      glBegin(GL_QUAD_STRIP);
+      int p = 0;
+      float vertices[(n+1) * 4 * 2];
+
       for(int i = 0; i <= n; ++i)
         {
           float x = sinf(i * (M_PI/2) / n) * radius;
           float y = cosf(i * (M_PI/2) / n) * radius;
 
-          glVertex2f(irect.get_left()  - x, irect.get_top() - y);
-          glVertex2f(irect.get_right() + x, irect.get_top() - y);
+          vertices[p++] = irect.get_left() - x;
+          vertices[p++] = irect.get_top()  - y;
+
+          vertices[p++] = irect.get_right() + x;
+          vertices[p++] = irect.get_top()   - y;
         }
+
       for(int i = 0; i <= n; ++i)
         {
           float x = cosf(i * (M_PI/2) / n) * radius;
           float y = sinf(i * (M_PI/2) / n) * radius;
 
-          glVertex2f(irect.get_left()  - x, irect.get_bottom() + y);
-          glVertex2f(irect.get_right() + x, irect.get_bottom() + y);
+          vertices[p++] = irect.get_left()   - x;
+          vertices[p++] = irect.get_bottom() + y;
+
+          vertices[p++] = irect.get_right()  + x;
+          vertices[p++] = irect.get_bottom() + y;
         }
-      glEnd();
-      glEnable(GL_TEXTURE_2D);
-      glColor4f(1, 1, 1, 1);
+
+      glVertexPointer(2, GL_FLOAT, 0, vertices);
+      glDrawArrays(GL_TRIANGLE_STRIP, 0,  sizeof(vertices)/sizeof(float)/2);
     }
   else
     {
@@ -341,19 +370,20 @@ Renderer::draw_filled_rect(const DrawingRequest& request)
       float w = fillrectrequest->size.x;
       float h = fillrectrequest->size.y;
 
-      glDisable(GL_TEXTURE_2D);
-      glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
-                fillrectrequest->color.blue, fillrectrequest->color.alpha);
-
-      glBegin(GL_QUADS);
-      glVertex2f(x, y);
-      glVertex2f(x+w, y);
-      glVertex2f(x+w, y+h);
-      glVertex2f(x, y+h);
-      glEnd();
-      glEnable(GL_TEXTURE_2D);
-      glColor4f(1, 1, 1, 1);
+      float vertices[] = {
+        x,   y,
+        x+w, y,
+        x+w, y+h,
+        x,   y+h
+      };
+      glVertexPointer(2, GL_FLOAT, 0, vertices);
+
+      glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
     }
+
+  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+  glEnable(GL_TEXTURE_2D);
+  glColor4f(1, 1, 1, 1);
 }
 
 void
@@ -370,31 +400,32 @@ Renderer::draw_inverse_ellipse(const DrawingRequest& request)
   float w = ellipse->size.x/2.0f;
   float h = ellipse->size.y/2.0f;
 
-  glBegin(GL_TRIANGLES);
-    
+  static const int slices = 16;
+  static const int points = (slices+1) * 12;
+
+  float vertices[points * 2];
+  int   p = 0;
+
   // Bottom
-  glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
-  glVertex2f(0, SCREEN_HEIGHT);
-  glVertex2f(x, y+h);
+  vertices[p++] = SCREEN_WIDTH; vertices[p++] = SCREEN_HEIGHT;
+  vertices[p++] = 0;            vertices[p++] = SCREEN_HEIGHT;
+  vertices[p++] = x;            vertices[p++] = y+h;
 
   // Top
-  glVertex2f(SCREEN_WIDTH, 0);
-  glVertex2f(0, 0);
-  glVertex2f(x, y-h);
+  vertices[p++] = SCREEN_WIDTH; vertices[p++] = 0;
+  vertices[p++] = 0;            vertices[p++] = 0;
+  vertices[p++] = x;            vertices[p++] = y-h;
 
   // Left
-  glVertex2f(SCREEN_WIDTH, 0);
-  glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
-  glVertex2f(x+w, y);
+  vertices[p++] = SCREEN_WIDTH; vertices[p++] = 0;
+  vertices[p++] = SCREEN_WIDTH; vertices[p++] = SCREEN_HEIGHT;
+  vertices[p++] = x+w;          vertices[p++] = y;
 
   // Right
-  glVertex2f(0, 0);
-  glVertex2f(0, SCREEN_HEIGHT);
-  glVertex2f(x-w, y);
+  vertices[p++] = 0;            vertices[p++] = 0;
+  vertices[p++] = 0;            vertices[p++] = SCREEN_HEIGHT;
+  vertices[p++] = x-w;          vertices[p++] = y;
 
-  glEnd();
-        
-  int slices = 16;
   for(int i = 0; i < slices; ++i)
     {
       float ex1 = sinf(M_PI/2 / slices * i) * w;
@@ -403,30 +434,34 @@ Renderer::draw_inverse_ellipse(const DrawingRequest& request)
       float ex2 = sinf(M_PI/2 / slices * (i+1)) * w;
       float ey2 = cosf(M_PI/2 / slices * (i+1)) * h;
 
-      glBegin(GL_TRIANGLES);
-
       // Bottom/Right
-      glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
-      glVertex2f(x + ex1, y + ey1);
-      glVertex2f(x + ex2, y + ey2);
+      vertices[p++] = SCREEN_WIDTH; vertices[p++] = SCREEN_HEIGHT;
+      vertices[p++] = x + ex1;      vertices[p++] = y + ey1;
+      vertices[p++] = x + ex2;      vertices[p++] = y + ey2;
 
       // Top/Left
-      glVertex2f(0, 0);
-      glVertex2f(x - ex1, y - ey1);
-      glVertex2f(x - ex2, y - ey2);
+      vertices[p++] = 0;            vertices[p++] = 0;
+      vertices[p++] = x - ex1;      vertices[p++] = y - ey1;
+      vertices[p++] = x - ex2;      vertices[p++] = y - ey2;
 
       // Top/Right
-      glVertex2f(SCREEN_WIDTH, 0);
-      glVertex2f(x + ex1, y - ey1);
-      glVertex2f(x + ex2, y - ey2);
+      vertices[p++] = SCREEN_WIDTH; vertices[p++] = 0;
+      vertices[p++] = x + ex1;      vertices[p++] = y - ey1;
+      vertices[p++] = x + ex2;      vertices[p++] = y - ey2;
 
       // Bottom/Left
-      glVertex2f(0, SCREEN_HEIGHT);
-      glVertex2f(x - ex1, y + ey1);
-      glVertex2f(x - ex2, y + ey2);
-
-      glEnd();
+      vertices[p++] = 0;            vertices[p++] = SCREEN_HEIGHT;
+      vertices[p++] = x - ex1;      vertices[p++] = y + ey1;
+      vertices[p++] = x - ex2;      vertices[p++] = y + ey2;
     }
+
+  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+  glVertexPointer(2, GL_FLOAT, 0, vertices);
+
+  glDrawArrays(GL_TRIANGLES, 0, points);
+
+  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
   glEnable(GL_TEXTURE_2D);
   glColor4f(1, 1, 1, 1);    
 }
@@ -509,7 +544,9 @@ Renderer::flip()
 void
 Renderer::resize(int w, int h)
 {
-  SDL_SetVideoMode(w, h, 0, SDL_OPENGL | SDL_RESIZABLE);
+  // This causes the screen to go black, which is annoying, but seems
+  // unavoidable with SDL at the moment
+  SDL_SetVideoMode(w, h, 0, SDL_OPENGL /*| SDL_RESIZABLE*/);
 
   config->window_width  = w;
   config->window_height = h;
@@ -520,16 +557,23 @@ Renderer::resize(int w, int h)
 void
 Renderer::apply_config()
 {    
-  std::cout << "Applying Config:" 
-            << "\n  Desktop: " << desktop_width << "x" << desktop_height
-            << "\n  Window:  " << config->window_width << "x" << config->window_height
-            << "\n  FullRes: " << config->fullscreen_width << "x" << config->fullscreen_height
-            << "\n  Aspect:  " << config->aspect_width << ":" << config->aspect_height
-            << "\n  Magnif:  " << config->magnification
-            << std::endl;
+  if (0)
+    {
+      std::cout << "Applying Config:" 
+                << "\n  Desktop: " << desktop_width << "x" << desktop_height
+                << "\n  Window:  " << config->window_width << "x" << config->window_height
+                << "\n  FullRes: " << config->fullscreen_width << "x" << config->fullscreen_height
+                << "\n  Aspect:  " << config->aspect_width << ":" << config->aspect_height
+                << "\n  Magnif:  " << config->magnification
+                << std::endl;
+    }
 
   int w,h;
-  float target_aspect  = float(config->aspect_width) / float(config->aspect_height);
+  float target_aspect = float(desktop_width) / desktop_height;
+  
+  if (config->aspect_width != 0 && config->aspect_height != 0)
+    target_aspect = float(config->aspect_width) / float(config->aspect_height);
+
   float desktop_aspect = 4.0f / 3.0f; // random default fallback guess
   
   if (desktop_width != -1 && desktop_height != -1)
@@ -537,6 +581,7 @@ Renderer::apply_config()
       desktop_aspect = float(desktop_width) / float(desktop_height);
     }
 
+  // Get the screen width
   if (config->use_fullscreen)
     {
       w = config->fullscreen_width;
@@ -551,42 +596,84 @@ Renderer::apply_config()
 
   if (target_aspect > 1.0f)
     {
-      SCREEN_WIDTH  = w * (target_aspect / desktop_aspect);
-      SCREEN_HEIGHT = h;
+      SCREEN_WIDTH  = static_cast<int>(w * (target_aspect / desktop_aspect));
+      SCREEN_HEIGHT = static_cast<int>(h);
     }
   else
     {
-      SCREEN_WIDTH  = w;
-      SCREEN_HEIGHT = h  * (target_aspect / desktop_aspect);
+      SCREEN_WIDTH  = static_cast<int>(w);
+      SCREEN_HEIGHT = static_cast<int>(h  * (target_aspect / desktop_aspect));
     }
 
-  SCREEN_WIDTH  *= config->magnification;  
-  SCREEN_HEIGHT *= config->magnification;  
-
-  int max_width  = 1600;
+  int max_width  = 1600; // FIXME: Maybe 1920 is ok too
   int max_height = 1200;
-  
-  // A little wonky
-  if (SCREEN_WIDTH > max_width)
+
+  if (config->magnification == 0.0f) // Magic value that means 'minfill'
     {
-      float scale = float(max_width)/SCREEN_WIDTH;
-      SCREEN_WIDTH  *= scale;
-      SCREEN_HEIGHT *= scale;
+      // This scales SCREEN_WIDTH/SCREEN_HEIGHT so that they never excede
+      // max_width/max_height
+      if (SCREEN_WIDTH > max_width || SCREEN_HEIGHT > max_height)
+        {
+          float scale1  = float(max_width)/SCREEN_WIDTH;
+          float scale2  = float(max_height)/SCREEN_HEIGHT;
+          float scale   = (scale1 < scale2) ? scale1 : scale2;
+          SCREEN_WIDTH  = static_cast<int>(SCREEN_WIDTH  * scale);
+          SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT * scale);
+        }
+
+      glViewport(0, 0, w, h);
     }
-  else if (SCREEN_HEIGHT > max_height)
+  else
     {
-      float scale = float(max_height)/SCREEN_HEIGHT;
-      SCREEN_WIDTH  *= scale;
-      SCREEN_HEIGHT *= scale;
+      SCREEN_WIDTH  = static_cast<int>(SCREEN_WIDTH  / config->magnification);
+      SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT / config->magnification);
+
+      // This works by adding black borders around the screen to limit
+      // SCREEN_WIDTH/SCREEN_HEIGHT to max_width/max_height
+      int nw = w;
+      int nh = h;
+
+      if (SCREEN_WIDTH > max_width)
+        {
+          nw = static_cast<int>((float) nw * float(max_width)/SCREEN_WIDTH);
+          SCREEN_WIDTH = static_cast<int>(max_width);
+        }
+
+      if (SCREEN_HEIGHT > max_height)
+        {
+          nh = static_cast<int>((float) nh * float(max_height)/SCREEN_HEIGHT);
+          SCREEN_HEIGHT = static_cast<int>(max_height);
+        }
+
+      // Clear both buffers so that we get a clean black border without junk
+      glClear(GL_COLOR_BUFFER_BIT);
+      SDL_GL_SwapBuffers();
+      glClear(GL_COLOR_BUFFER_BIT);
+      SDL_GL_SwapBuffers();
+
+      if (0)
+        std::cout << (w-nw)/2 << " "
+                  << (h-nh)/2 << " "
+                  << nw << "x" << nh << std::endl;
+
+      glViewport(std::max(0, (w-nw)/2), 
+                 std::max(0, (h-nh)/2), 
+                 std::min(nw, w),
+                 std::min(nh, h));
     }
-  
-  std::cout << " -> " << SCREEN_WIDTH << "x" << SCREEN_HEIGHT << std::endl;
 
-  glViewport(0, 0, w, h);
+  if (0)
+    std::cout << "  -> " << SCREEN_WIDTH << "x" << SCREEN_HEIGHT << std::endl;
 
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
-  glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);    
+
+  glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1, 1);
+
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  glTranslatef(0, 0, 0);
+  check_gl_error("Setting up view matrices");
 }
 
 } // namespace GL