update branch with latest head, most likely breaking water color in stereo mode experimental3D
authorkoda
Wed, 27 Oct 2010 14:02:20 +0200
branchexperimental3D
changeset 4004 b1c2c2f6fc5e
parent 3698 793386610068 (diff)
parent 4002 3e173ac63849 (current diff)
child 4006 45b63c2a694f
update branch with latest head, most likely breaking water color in stereo mode
QTfrontend/CMakeLists.txt
QTfrontend/game.cpp
QTfrontend/gameuiconfig.cpp
QTfrontend/gameuiconfig.h
QTfrontend/pages.cpp
QTfrontend/pages.h
hedgewars/ArgParsers.inc
hedgewars/hwengine.pas
hedgewars/uConsts.pas
hedgewars/uMisc.pas
hedgewars/uSHA.pas
hedgewars/uStore.pas
hedgewars/uWorld.pas
project_files/HedgewarsMobile/Classes/DetailViewController.h
project_files/HedgewarsMobile/Classes/DetailViewController.m
project_files/HedgewarsMobile/Resources/Frontend-iPad/background-lobby.png
project_files/HedgewarsMobile/Resources/Frontend-iPad/bluebox.png
project_files/HedgewarsMobile/Resources/Frontend-iPad/toolbarBackground.png
project_files/HedgewarsMobile/Resources/Overlay/joyPush.png
share/hedgewars/Data/Forts/BarrelhouseL.png
share/hedgewars/Data/Forts/BarrelhouseR.png
share/hedgewars/Data/Forts/IslandL.png
share/hedgewars/Data/Forts/IslandR.png
share/hedgewars/Data/Graphics/Flags/pirate.png
share/hedgewars/Data/Graphics/Hats/Pumpkin Hat.png
share/hedgewars/Data/Locale/hedgewars_bg.qm
share/hedgewars/Data/Locale/hedgewars_cs.qm
share/hedgewars/Data/Locale/hedgewars_de.qm
share/hedgewars/Data/Locale/hedgewars_en.qm
share/hedgewars/Data/Locale/hedgewars_es.qm
share/hedgewars/Data/Locale/hedgewars_fi.qm
share/hedgewars/Data/Locale/hedgewars_fr.qm
share/hedgewars/Data/Locale/hedgewars_it.qm
share/hedgewars/Data/Locale/hedgewars_ja.qm
share/hedgewars/Data/Locale/hedgewars_pl.qm
share/hedgewars/Data/Locale/hedgewars_pt_BR.qm
share/hedgewars/Data/Locale/hedgewars_pt_PT.qm
share/hedgewars/Data/Locale/hedgewars_ru.qm
share/hedgewars/Data/Locale/hedgewars_sk.qm
share/hedgewars/Data/Locale/hedgewars_sv.qm
share/hedgewars/Data/Locale/hedgewars_tr_TR.qm
share/hedgewars/Data/Locale/hedgewars_uk.qm
share/hedgewars/Data/Locale/hedgewars_zh_CN.qm
share/hedgewars/Data/Locale/hedgewars_zh_TW.qm
share/hedgewars/Data/Themes/Art/Thumbs.db
--- a/QTfrontend/game.cpp	Wed Oct 27 00:24:03 2010 -0400
+++ b/QTfrontend/game.cpp	Wed Oct 27 14:02:20 2010 +0200
@@ -299,6 +299,7 @@
     arguments << (config->isAltDamageEnabled() ? "1" : "0");
     arguments << config->netNick().toUtf8().toBase64();
     arguments << QString::number(config->translateQuality());
+    arguments << QString::number(config->stereoMode());
     arguments << tr("en.txt");
 
     return arguments;
--- a/QTfrontend/gameuiconfig.cpp	Wed Oct 27 00:24:03 2010 -0400
+++ b/QTfrontend/gameuiconfig.cpp	Wed Oct 27 14:02:20 2010 +0200
@@ -48,6 +48,7 @@
     Form->ui.pageOptions->CBFrontendFullscreen->setChecked(ffscr);
 
     Form->ui.pageOptions->SLQuality->setValue(value("video/quality", 5).toUInt());
+    Form->ui.pageOptions->CBStereoMode->setCurrentIndex(value("video/stereo", 0).toUInt());
     Form->ui.pageOptions->CBFrontendEffects->setChecked(frontendEffects);
     Form->ui.pageOptions->CBEnableSound->setChecked(value("audio/sound", true).toBool());
     Form->ui.pageOptions->CBEnableFrontendSound->setChecked(value("frontend/sound", true).toBool());
@@ -113,6 +114,7 @@
     setValue("video/fullscreen", vid_Fullscreen());
 
     setValue("video/quality", Form->ui.pageOptions->SLQuality->value());
+    setValue("video/stereo", stereoMode());
 
     setValue("frontend/effects", isFrontendEffects());
 
@@ -261,6 +263,11 @@
     return Form->ui.pageOptions->CBAltDamage->isChecked();
 }
 
+quint32 GameUIConfig::stereoMode() const
+{
+    return Form->ui.pageOptions->CBStereoMode->currentIndex();
+}
+
 bool GameUIConfig::appendDateTimeToRecordName()
 {
     return Form->ui.pageOptions->CBNameWithDate->isChecked();
--- a/QTfrontend/gameuiconfig.h	Wed Oct 27 00:24:03 2010 -0400
+++ b/QTfrontend/gameuiconfig.h	Wed Oct 27 14:02:20 2010 +0200
@@ -52,6 +52,7 @@
     bool isFrontendEffects() const;
     bool isFrontendFullscreen() const;
     void resizeToConfigValues();
+    quint32 stereoMode() const;
 
 #ifdef __APPLE__
 #ifdef SPARKLE_ENABLED
--- a/QTfrontend/pages.cpp	Wed Oct 27 00:24:03 2010 -0400
+++ b/QTfrontend/pages.cpp	Wed Oct 27 14:02:20 2010 +0200
@@ -629,6 +629,7 @@
 
             QVBoxLayout * GBAlayout = new QVBoxLayout(AGGroupBox);
             QHBoxLayout * GBAreslayout = new QHBoxLayout(0);
+            QHBoxLayout * GBAstereolayout = new QHBoxLayout(0);
             QHBoxLayout * GBAqualayout = new QHBoxLayout(0);
 
             CBFrontendFullscreen = new QCheckBox(AGGroupBox);
@@ -664,6 +665,7 @@
             CBFullscreen = new QCheckBox(AGGroupBox);
             CBFullscreen->setText(QCheckBox::tr("Fullscreen"));
             GBAlayout->addWidget(CBFullscreen);
+            connect(CBFullscreen, SIGNAL(stateChanged(int)), this, SLOT(setFullscreen(void)));
 
             QLabel * quality = new QLabel(AGGroupBox);
             quality->setText(QLabel::tr("Quality"));
@@ -677,6 +679,25 @@
             SLQuality->setFixedWidth(150);
             GBAqualayout->addWidget(SLQuality);
             GBAlayout->addLayout(GBAqualayout);
+            QLabel * stereo = new QLabel(AGGroupBox);
+            stereo->setText(QLabel::tr("Stereo rendering"));
+            GBAstereolayout->addWidget(stereo);
+
+            CBStereoMode = new QComboBox(AGGroupBox);
+            CBStereoMode->addItem(QComboBox::tr("Disabled"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Cyan"));
+            CBStereoMode->addItem(QComboBox::tr("Cyan/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Blue"));
+            CBStereoMode->addItem(QComboBox::tr("Blue/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Green"));
+            CBStereoMode->addItem(QComboBox::tr("Green/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Side-by-side"));
+            CBStereoMode->addItem(QComboBox::tr("Top-Bottom"));
+            CBStereoMode->addItem(QComboBox::tr("Frame Alternate"));
+            connect(CBStereoMode, SIGNAL(currentIndexChanged(int)), this, SLOT(forceFullscreen(int)));
+
+            GBAstereolayout->addWidget(CBStereoMode);
+            GBAlayout->addLayout(GBAstereolayout);
 
             hr = new QFrame(AGGroupBox);
             hr->setFrameStyle(QFrame::HLine);
@@ -726,7 +747,7 @@
             hr->setFixedHeight(10);
             GBAlayout->addWidget(hr);
 
-                QLabel *restartNote = new QLabel(this);
+            QLabel *restartNote = new QLabel(this);
             restartNote->setText(QString("* ") + QLabel::tr("Restart game to apply"));
             restartNote->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
             GBAlayout->addWidget(restartNote);
@@ -741,8 +762,23 @@
     BtnBack->setFixedHeight(BtnSaveOptions->height());
     BtnBack->setFixedWidth(BtnBack->width()+2);
     BtnBack->setStyleSheet("QPushButton{margin: 22px 0 9px 2px;}");
+}
 
-//    BtnAssociateFiles = addButton("");
+void PageOptions::forceFullscreen(int index)
+{
+    if (index != 0) {
+        previousFullscreenValue = this->CBFullscreen->isChecked();
+        this->CBFullscreen->setChecked(true);
+        this->CBFullscreen->setEnabled(false);
+    } else {
+        this->CBFullscreen->setChecked(previousFullscreenValue);
+        this->CBFullscreen->setEnabled(true);
+    }
+}
+
+void PageOptions::setFullscreen(void)
+{
+    this->CBResolution->setEnabled(!this->CBFullscreen->isChecked());
 }
 
 PageNet::PageNet(QWidget* parent) : AbstractPage(parent)
--- a/QTfrontend/pages.h	Wed Oct 27 00:24:03 2010 -0400
+++ b/QTfrontend/pages.h	Wed Oct 27 14:02:20 2010 +0200
@@ -222,6 +222,7 @@
     QComboBox *CBTeamName;
     IconedGroupBox *AGGroupBox;
     QComboBox *CBResolution;
+    QComboBox *CBStereoMode;
     QCheckBox *CBEnableSound;
     QCheckBox *CBEnableFrontendSound;
     QCheckBox *CBEnableMusic;
@@ -242,6 +243,11 @@
     QLineEdit *editNetNick;
     QSlider *SLQuality;
     QCheckBox *CBFrontendEffects;
+    bool previousFullscreenValue;
+
+private slots:
+    void forceFullscreen(int index);
+    void setFullscreen(void);
 };
 
 class PageNet : public AbstractPage
--- a/hedgewars/ArgParsers.inc	Wed Oct 27 00:24:03 2010 -0400
+++ b/hedgewars/ArgParsers.inc	Wed Oct 27 14:02:20 2010 +0200
@@ -8,6 +8,7 @@
 end;
 
 procedure internalStartGameWithParameters();
+var tmp: LongInt;
 begin
     val(ParamStr(2), cScreenWidth);
     val(ParamStr(3), cScreenHeight);
@@ -23,7 +24,9 @@
     cAltDamage:= ParamStr(13) = '1';
     UserNick:= DecodeBase64(ParamStr(14));
     val(ParamStr(15), cReducedQuality);
-    cLocaleFName:= ParamStr(16)
+    val(ParamStr(16), tmp);
+    cStereoMode:= TStereoMode(max(0, min(ord(high(TStereoMode)), tmp)));
+    cLocaleFName:= ParamStr(17);
 end;
 
 procedure setVideo(screenWidth: LongInt; screenHeight: LongInt; bitsStr: LongInt);
--- a/hedgewars/hwengine.pas	Wed Oct 27 00:24:03 2010 -0400
+++ b/hedgewars/hwengine.pas	Wed Oct 27 14:02:20 2010 +0200
@@ -227,10 +227,12 @@
     cAltDamage:= gameArgs[8] = '1';
     val(gameArgs[9], rotationQt);
     recordFileName:= gameArgs[10];
+    cStereoMode:= smNone; // TODO: Enable anaglyph rendering on iPhone?
 {$ENDIF}
 
     cLogfileBase:= 'game';
     initEverything(true);
+
     WriteLnToConsole('Hedgewars ' + cVersionString + ' engine (network protocol: ' + inttostr(cNetProtoVersion) + ')');
 {$IFDEF DEBUGFILE}
     AddFileLog('Prefix: "' + PathPrefix +'"');
--- a/hedgewars/uConsts.pas	Wed Oct 27 00:24:03 2010 -0400
+++ b/hedgewars/uConsts.pas	Wed Oct 27 14:02:20 2010 +0200
@@ -148,6 +148,9 @@
 
     TWave = (waveRollup, waveSad, waveWave, waveHurrah, waveLemonade, waveShrug, waveJuggle);
 
+    TRenderMode = (rmDefault, rmLeftEye, rmRightEye);
+    TStereoMode = (smNone, smRedCyan, smCyanRed, smRedBlue, smBlueRed, smRedGreen, smGreenRed, smHorizontal, smVertical, smAFR);
+
     THHFont = record
             Handle: PTTF_Font;
             Height: LongInt;
@@ -191,7 +194,7 @@
     TScreenFade = (sfNone, sfInit, sfToBlack, sfFromBlack, sfToWhite, sfFromWhite);
 const
     sfMax = 1000;
-    cDefaultParamNum = 16;
+    cDefaultParamNum = 17;
 
     // message constants
     errmsgCreateSurface   = 'Error creating SDL surface';
--- a/hedgewars/uMisc.pas	Wed Oct 27 00:24:03 2010 -0400
+++ b/hedgewars/uMisc.pas	Wed Oct 27 14:02:20 2010 +0200
@@ -55,6 +55,7 @@
     isSpeed         : boolean;
     isFirstFrame    : boolean;
 
+    cStereoMode     : TStereoMode;
     fastUntilLag    : boolean;
 
     GameState       : TGameState;
@@ -842,7 +843,6 @@
 {$ENDIF}
 {$I+}
 {$ENDIF}
-
 end;
 
 procedure freeModule;
--- a/hedgewars/uStore.pas	Wed Oct 27 00:24:03 2010 -0400
+++ b/hedgewars/uStore.pas	Wed Oct 27 14:02:20 2010 +0200
@@ -37,6 +37,10 @@
     MissionIcons: PSDL_Surface;
     ropeIconTex: PTexture;
     rotationQt: GLfloat;
+    wScreen: LongInt;
+    hScreen: LongInt;
+    framel, framer, depthl, depthr: GLuint;
+    texl, texr: GLuint;
 
 procedure initModule;
 procedure freeModule;
@@ -857,6 +861,15 @@
     SDL_FreeSurface(MissionIcons);
     FreeTexture(ropeIconTex);
     FreeTexture(HHTexture);
+    if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then
+    begin
+        glDeleteTextures(1, @texl);
+        glDeleteRenderbuffersEXT(1, @depthl);
+        glDeleteFramebuffersEXT(1, @framel);
+        glDeleteTextures(1, @texr);
+        glDeleteRenderbuffersEXT(1, @depthr);
+        glDeleteFramebuffersEXT(1, @framer)
+    end
 end;
 
 
@@ -1215,7 +1228,9 @@
 {$ENDIF}
     end;
 
-{$IFNDEF IPHONEOS}
+{$IFDEF IPHONEOS}
+    cGPUVendor:= gvApple;
+{$ELSE}
     if StrPos(Str2PChar(vendor), Str2PChar('nvidia')) <> nil then
         cGPUVendor:= gvNVIDIA
     else if StrPos(Str2PChar(vendor), Str2PChar('intel')) <> nil then
@@ -1223,8 +1238,43 @@
     else if StrPos(Str2PChar(vendor), Str2PChar('ati')) <> nil then
         cGPUVendor:= gvIntel;
 //SupportNPOTT:= glLoadExtension('GL_ARB_texture_non_power_of_two');
-{$ELSE}
-    cGPUVendor:= gvApple;
+
+    if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then
+    begin
+        // prepare left and right frame buffers and associated textures
+        glLoadExtension('GL_EXT_framebuffer_object');
+
+        // left
+        glGenFramebuffersEXT(1, @framel);
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framel);
+        glGenRenderbuffersEXT(1, @depthl);
+        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthl);
+        glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, cScreenWidth, cScreenHeight);
+        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthl);
+        glGenTextures(1, @texl);
+        glBindTexture(GL_TEXTURE_2D, texl);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  cScreenWidth, cScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texl, 0);
+
+        // right
+        glGenFramebuffersEXT(1, @framer);
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framer);
+        glGenRenderbuffersEXT(1, @depthr);
+        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthr);
+        glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, cScreenWidth, cScreenHeight);
+        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthr);
+        glGenTextures(1, @texr);
+        glBindTexture(GL_TEXTURE_2D, texr);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  cScreenWidth, cScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texr, 0);
+
+        // reset
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)
+    end;
 {$ENDIF}
 
 {$IFDEF DEBUGFILE}
--- a/hedgewars/uWorld.pas	Wed Oct 27 00:24:03 2010 -0400
+++ b/hedgewars/uWorld.pas	Wed Oct 27 14:02:20 2010 +0200
@@ -22,18 +22,19 @@
 interface
 uses SDLh, uGears, uConsts, uFloat, uRandom;
 
-
 var FollowGear: PGear;
     WindBarWidth: LongInt;
     bShowAmmoMenu: boolean;
     bSelected: boolean;
     bShowFinger: boolean;
     Frames: Longword;
+    AFRToggle: Boolean;
     WaterColor, DeepWaterColor: TSDL_Color;
     WorldDx: LongInt;
     WorldDy: LongInt;
     SkyOffset: LongInt;
     HorizontOffset: LongInt;
+    bAFRRight: Boolean;
 {$IFDEF COUNTTICKS}
     cntTicks: LongWord;
 {$ENDIF}
@@ -44,6 +45,7 @@
 
 procedure InitWorld;
 procedure DrawWorld(Lag: LongInt);
+procedure DrawWorldStereo(Lag: LongInt; RM: TRenderMode);
 procedure AddCaption(s: shortstring; Color: Longword; Group: TCapGroup);
 procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt);
 procedure HideMission;
@@ -71,6 +73,11 @@
     amSel: TAmmoType = amNothing;
     missionTex: PTexture;
     missionTimer: LongInt;
+    stereoDepth: GLfloat = 0;
+
+const cStereo_Sky     = 0.0750;
+      cStereo_Horizon = 0.0250;
+      cStereo_Water   = 0.0125;
 
 procedure InitWorld;
 var i, t: LongInt;
@@ -530,14 +537,6 @@
 
 
 procedure DrawWorld(Lag: LongInt);
-var i, t: LongInt;
-    r: TSDL_Rect;
-    tdx, tdy: Double;
-    grp: TCapGroup;
-    s: string[15];
-    highlight: Boolean;
-    offset, offsetX, offsetY, ScreenBottom: LongInt;
-    VertexBuffer: array [0..3] of TVertex2f;
 begin
     if not isPaused then
     begin
@@ -566,6 +565,150 @@
     if not isPaused then
         MoveCamera;
 
+    if cStereoMode = smNone then
+        begin
+        glClear(GL_COLOR_BUFFER_BIT);
+        DrawWorldStereo(Lag, rmDefault)
+        end
+    else if (cStereoMode = smAFR) then
+        begin
+        AFRToggle:= not AFRToggle;
+        glClear(GL_COLOR_BUFFER_BIT);
+        if AFRToggle then
+            DrawWorldStereo(Lag, rmLeftEye)
+        else
+            DrawWorldStereo(Lag, rmRightEye)
+        end
+    else if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) then
+        begin
+        // create left fb
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framel);
+        glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+        DrawWorldStereo(Lag, rmLeftEye);
+
+        // create right fb
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framer);
+        glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+        DrawWorldStereo(0, rmRightEye);
+
+        // detatch drawing from fbs
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+        glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+        SetScale(cDefaultZoomLevel);
+
+        // draw left frame
+        glBindTexture(GL_TEXTURE_2D, texl);
+        glBegin(GL_QUADS);
+            if cStereoMode = smHorizontal then
+                begin
+                glTexCoord2f(0.0, 0.0);
+                glVertex2d(cScreenWidth / -2, cScreenHeight);
+                glTexCoord2f(1.0, 0.0);
+                glVertex2d(0, cScreenHeight);
+                glTexCoord2f(1.0, 1.0);
+                glVertex2d(0, 0);
+                glTexCoord2f(0.0, 1.0);
+                glVertex2d(cScreenWidth / -2, 0);
+                end
+            else
+                begin
+                glTexCoord2f(0.0, 0.0);
+                glVertex2d(cScreenWidth / -2, cScreenHeight / 2);
+                glTexCoord2f(1.0, 0.0);
+                glVertex2d(cScreenWidth / 2, cScreenHeight / 2);
+                glTexCoord2f(1.0, 1.0);
+                glVertex2d(cScreenWidth / 2, 0);
+                glTexCoord2f(0.0, 1.0);
+                glVertex2d(cScreenWidth / -2, 0);
+                end;
+        glEnd();
+
+        // draw right frame
+        glBindTexture(GL_TEXTURE_2D, texr);
+        glBegin(GL_QUADS);
+            if cStereoMode = smHorizontal then
+                begin
+                glTexCoord2f(0.0, 0.0);
+                glVertex2d(0, cScreenHeight);
+                glTexCoord2f(1.0, 0.0);
+                glVertex2d(cScreenWidth / 2, cScreenHeight);
+                glTexCoord2f(1.0, 1.0);
+                glVertex2d(cScreenWidth / 2, 0);
+                glTexCoord2f(0.0, 1.0);
+                glVertex2d(0, 0);
+                end
+            else
+                begin
+                glTexCoord2f(0.0, 0.0);
+                glVertex2d(cScreenWidth / -2, cScreenHeight);
+                glTexCoord2f(1.0, 0.0);
+                glVertex2d(cScreenWidth / 2, cScreenHeight);
+                glTexCoord2f(1.0, 1.0);
+                glVertex2d(cScreenWidth / 2, cScreenHeight / 2);
+                glTexCoord2f(0.0, 1.0);
+                glVertex2d(cScreenWidth / -2, cScreenHeight / 2);
+                end;
+        glEnd();
+        SetScale(zoom);
+        end
+    else
+        begin
+        // clear scene
+        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+        glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+        // draw left eye in red channel only
+        if cStereoMode = smGreenRed then
+            glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE)
+        else if cStereoMode = smBlueRed then
+            glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE)
+        else if cStereoMode = smCyanRed then
+            glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE)
+        else
+            glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
+        DrawWorldStereo(Lag, rmLeftEye);
+        // draw right eye in selected channel(s) only
+        if cStereoMode = smRedGreen then
+            glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE)
+        else if cStereoMode = smRedBlue then
+            glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE)
+        else if cStereoMode = smRedCyan then
+            glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE)
+        else
+            glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
+        DrawWorldStereo(Lag, rmRightEye);
+        end
+end;
+
+procedure ChangeDepth(rm: TRenderMode; d: GLfloat);
+begin
+    d:= d / 5;
+    if rm = rmDefault then exit
+    else if rm = rmLeftEye then d:= -d;
+    stereoDepth:= stereoDepth + d;
+    glMatrixMode(GL_PROJECTION);
+    glTranslatef(d, 0, 0);
+    glMatrixMode(GL_MODELVIEW)
+end;
+ 
+procedure ResetDepth(rm: TRenderMode);
+begin
+    if rm = rmDefault then exit;
+    glMatrixMode(GL_PROJECTION);
+    glTranslatef(-stereoDepth, 0, 0);
+    glMatrixMode(GL_MODELVIEW);
+    stereoDepth:= 0;
+end;
+ 
+procedure DrawWorldStereo(Lag: LongInt; RM: TRenderMode);
+var i, t: LongInt;
+    r: TSDL_Rect;
+    tdx, tdy: Double;
+    grp: TCapGroup;
+    s: string[15];
+    highlight: Boolean;
+    offset, offsetX, offsetY, screenBottom: LongInt;
+    VertexBuffer: array [0..3] of TVertex2f;
+begin
     if (cReducedQuality and rqNoBackground) = 0 then
     begin
         // Offsets relative to camera - spare them to wimpier cpus, no bg or flakes for them anyway
@@ -577,7 +720,9 @@
             HorizontOffset:= HorizontOffset + ((ScreenBottom-SkyOffset) div 20);
 
         // background
+        ChangeDepth(RM, cStereo_Sky);
         DrawRepeated(sprSky, sprSkyL, sprSkyR, (WorldDx + LAND_WIDTH div 2) * 3 div 8, SkyOffset);
+        ChangeDepth(RM, -cStereo_Horizon);
         DrawRepeated(sprHorizont, sprHorizontL, sprHorizontR, (WorldDx + LAND_WIDTH div 2) * 3 div 5, HorizontOffset);
     end;
 
@@ -586,11 +731,16 @@
     if (cReducedQuality and rq2DWater) = 0 then
     begin
         // Waves
-        DrawWater(255, SkyOffset);
+        DrawWater(255, SkyOffset); 
+        ChangeDepth(RM, -cStereo_Water);
         DrawWaves( 1,  0 - WorldDx div 32, - cWaveHeight + offsetY div 35, 64);
+        ChangeDepth(RM, -cStereo_Water);
         DrawWaves( -1,  25 + WorldDx div 25, - cWaveHeight + offsetY div 38, 48);
+        ChangeDepth(RM, -cStereo_Water);
         DrawWaves( 1,  75 - WorldDx div 19, - cWaveHeight + offsetY div 45, 32);
+        ChangeDepth(RM, -cStereo_Water);
         DrawWaves(-1, 100 + WorldDx div 14, - cWaveHeight + offsetY div 70, 24);
+        ResetDepth(RM);
     end
     else
         DrawWaves(-1, 100, - (cWaveHeight + (cWaveHeight shr 1)), 0);
@@ -630,16 +780,21 @@
     DrawWater(cWaterOpacity, 0);
 
     // Waves
+    ChangeDepth(RM, cStereo_Water);
     DrawWaves( 1, 25 - WorldDx div 9, - cWaveHeight, 12);
 
     if (cReducedQuality and rq2DWater) = 0 then
     begin
         //DrawWater(cWaterOpacity, - offsetY div 40);
+        ChangeDepth(RM, cStereo_Water);
         DrawWaves(-1, 50 + WorldDx div 6, - cWaveHeight - offsetY div 40, 8);
         DrawWater(cWaterOpacity, - offsetY div 20);
+        ChangeDepth(RM, cStereo_Water);
         DrawWaves( 1, 75 - WorldDx div 4, - cWaveHeight - offsetY div 20, 2);
         DrawWater(cWaterOpacity, - offsetY div 10);
+        ChangeDepth(RM, cStereo_Water);
         DrawWaves( -1, 25 + WorldDx div 3, - cWaveHeight - offsetY div 10, 0);
+        ResetDepth(RM);
     end
     else
         DrawWaves(-1, 50, - (cWaveHeight shr 1), 0);
@@ -874,58 +1029,63 @@
 offsetX:= 10;
 {$ENDIF}
 offsetY:= cOffsetY;
-inc(Frames);
+if (RM = rmDefault) or (RM = rmRightEye) then
+begin
+    inc(Frames);
 
-if cShowFPS or (GameType = gmtDemo) then inc(CountTicks, Lag);
-if (GameType = gmtDemo) and (CountTicks >= 1000) then
-   begin
-   i:=GameTicks div 1000;
-   t:= i mod 60;
-   s:= inttostr(t);
-   if t < 10 then s:= '0' + s;
-   i:= i div 60;
-   t:= i mod 60;
-   s:= inttostr(t) + ':' + s;
-   if t < 10 then s:= '0' + s;
-   s:= inttostr(i div 60) + ':' + s;
-
-   if timeTexture <> nil then
-        FreeTexture(timeTexture);
-    timeTexture:= nil;
-
-   tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels);
-   tmpSurface:= doSurfaceConversion(tmpSurface);
-   timeTexture:= Surface2Tex(tmpSurface, false);
-   SDL_FreeSurface(tmpSurface)
-   end;
+    if cShowFPS or (GameType = gmtDemo) then
+        inc(CountTicks, Lag);
+    if (GameType = gmtDemo) and (CountTicks >= 1000) then
+    begin
+        i:=GameTicks div 1000;
+        t:= i mod 60;
+        s:= inttostr(t);
+        if t < 10 then s:= '0' + s;
+        i:= i div 60;
+        t:= i mod 60;
+        s:= inttostr(t) + ':' + s;
+        if t < 10 then s:= '0' + s;
+        s:= inttostr(i div 60) + ':' + s;
+   
+        if timeTexture <> nil then
+            FreeTexture(timeTexture);
+        timeTexture:= nil;
+    
+        tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels);
+        tmpSurface:= doSurfaceConversion(tmpSurface);
+        timeTexture:= Surface2Tex(tmpSurface, false);
+        SDL_FreeSurface(tmpSurface)
+    end;
 
-if timeTexture <> nil then
-   DrawTexture((cScreenWidth shr 1) - 20 - timeTexture^.w - offsetY, offsetX + timeTexture^.h+5, timeTexture);
+    if timeTexture <> nil then
+        DrawTexture((cScreenWidth shr 1) - 20 - timeTexture^.w - offsetY, offsetX + timeTexture^.h+5, timeTexture);
 
-if cShowFPS then
-   begin
-   if CountTicks >= 1000 then
-      begin
-      FPS:= Frames;
-      Frames:= 0;
-      CountTicks:= 0;
-      s:= inttostr(FPS) + ' fps';
-      if fpsTexture <> nil then
-        FreeTexture(fpsTexture);
-    fpsTexture:= nil;
-      tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels);
-      tmpSurface:= doSurfaceConversion(tmpSurface);
-      fpsTexture:= Surface2Tex(tmpSurface, false);
-      SDL_FreeSurface(tmpSurface)
-      end;
-   if fpsTexture <> nil then
-      DrawTexture((cScreenWidth shr 1) - 60 - offsetY, offsetX, fpsTexture);
-   end;
+    if cShowFPS then
+    begin
+        if CountTicks >= 1000 then
+        begin
+            FPS:= Frames;
+            Frames:= 0;
+            CountTicks:= 0;
+            s:= inttostr(FPS) + ' fps';
+            if fpsTexture <> nil then
+                FreeTexture(fpsTexture);
+            fpsTexture:= nil;
+            tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels);
+            tmpSurface:= doSurfaceConversion(tmpSurface);
+            fpsTexture:= Surface2Tex(tmpSurface, false);
+            SDL_FreeSurface(tmpSurface)
+        end;
+        if fpsTexture <> nil then
+            DrawTexture((cScreenWidth shr 1) - 60 - offsetY, offsetX, fpsTexture);
+    end;
 
-if CountTicks >= 1000 then CountTicks:= 0;
+    if CountTicks >= 1000 then CountTicks:= 0;
 
-// lag warning (?)
-inc(SoundTimerTicks, Lag);
+    // lag warning (?)
+    inc(SoundTimerTicks, Lag);
+end;
+
 if SoundTimerTicks >= 50 then
    begin
    SoundTimerTicks:= 0;