//
//  SuperTuxKart - a fun racing game with go-kart
//  Copyright (C) 2009-2013 Joerg Henrichs
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 3
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#include "irr_driver.h"
#include "../config/user_config.h"



#include "material_manager.h"
#include "texture_manager.h"

#include "glwrap.h"
#include "2dutils.h"
#include "central_settings.h"
#include "camera.h"
#include "rtts.h"
#include "shaders.h"
#include "gl_headers.h"
#include "ls_animated_mesh.h"
#include "ls_mesh_scene_node.h"
#include "ls_scene_manager.h"

#include "../guiengine/engine.h"
#include "../guiengine/message_queue.h"
#include "../guiengine/modaldialog.h"
#include "../guiengine/scalable_font.h"
#include "../guiengine/screen.h"
#include "../states_screens/state_manager.h"
#include "../maps/map_manager.h"


#include "../io/file_manager.h"
#include "../main_loop.h"
#include "../modes/world.h"

#include "../utils/constants.h"
#include "../utils/log.h"
#include "../utils/profiler.h"
#include "../utils/cpp2011.h"
#include "../utils/string_utils.h"

#include <irrlicht.h>
#include "../../lib/irrlicht/source/Irrlicht/CSkinnedMesh.h"





/* Build-time check that the Irrlicht we're building against works for us.
 * Should help prevent distros building against an incompatible library.
 */
#if IRRLICHT_VERSION_MAJOR < 1 || IRRLICHT_VERSION_MINOR < 7 || \
    !defined(_IRR_COMPILE_WITH_OPENGL_) || \
    !defined(_IRR_COMPILE_WITH_B3D_LOADER_)
#error "Building against an incompatible Irrlicht. Distros, please use the included version."
#endif


using namespace irr;

#ifdef WIN32
#include <Windows.h>
#endif
#if defined(__linux__) && !defined(ANDROID)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif


/** singleton */
IrrDriver *irr_driver = NULL;

GPUTimer          m_perf_query[Q_LAST];

const int MIN_SUPPORTED_HEIGHT = 600;
const int MIN_SUPPORTED_WIDTH  = 800;

// ----------------------------------------------------------------------------
/** The constructor creates the irrlicht device. It first creates a NULL
 *  device. This is necessary to handle the Chicken/egg problem with irrlicht:
 *  access to the file system is given from the device, but we can't create the
 *  device before reading the user_config file (for resolution, fullscreen).
 *  So we create a dummy device here to begin with, which is then later (once
 *  the real device exists) changed in initDevice().
 */
IrrDriver::IrrDriver()
{

    m_resolution_changing = RES_CHANGE_NONE;
    m_phase               = SOLID_NORMAL_AND_DEPTH_PASS;
    m_device              = createDevice(video::EDT_NULL,
                                         irr::core::dimension2d<u32>(640, 480),
                                         /*bits*/16U, /**fullscreen*/ false,
                                         /*stencilBuffer*/ false,
                                         /*vsync*/false,
                                         /*event receiver*/ NULL,
                                         file_manager->getFileSystem());
    m_shaders             = NULL;
    m_rtts                = NULL;
    memset(object_count, 0, sizeof(object_count));

}   // IrrDriver

// ----------------------------------------------------------------------------
/** Destructor - removes the irrlicht device.
 */
IrrDriver::~IrrDriver()
{


    assert(m_device != NULL);

    m_device->drop();
    m_device = NULL;
    m_modes.clear();

    Shaders::destroy();


}   // ~IrrDriver

void IrrDriver::test_Render()
{
    m_video_driver->beginScene(true, true, video::SColor(55,5,215,5));
    m_scene_manager->drawAll();
    m_video_driver->endScene();

}


/*
void IrrDriver::test_CreateMap()
{
    const std::string& dirname = "/home/sintel/IrrlichtProjects/laundredsheep/ls-assets/maps/maptest_map/";
    std::string s_root = StringUtils::getPath(StringUtils::removeExtension(dirname));
    s_root                 += "/";
    std::string path = "/home/sintel/IrrlichtProjects/laundredsheep/ls-assets/maps/maptest_map/scene.xml";
    XMLNode *root    = file_manager->createXMLTree(path);

    Log::info("irrdriver","ok");
    std::string model_name;
    const XMLNode *map_node = root->getNode("map");
    map_node->get("model", &model_name);
    std::string full_path = s_root + model_name;


    scene::IMesh *mesh;

    mesh = irr_driver->getMesh(full_path.c_str());

if(!mesh)
    {
        //Log::fatal("map",
        //           "Main map model '%s' in '%s' not found, aborting.\n",
        //           root_node->getName().c_str(),  model_name.c_str());
        Log::fatal("no mesh", "error!!!");

    }
    Log::info("...","meshes are loaded!!");
    irr::scene::IMeshSceneNode* scene_node = 0;
    scene_node = m_scene_manager->addMeshSceneNode(mesh);
    scene_node->setMaterialFlag(irr::video::EMF_LIGHTING, false);
    scene::ICameraSceneNode * cam = m_scene_manager->addCameraSceneNode(0, irr::core::vector3df(0,15,-20), irr::core::vector3df(0,5,0));

    scene::IMesh * mesh2 = m_scene_manager->getMesh("/home/sintel/IrrlichtProjects/laundredsheep/ls-assets/maps/maptest_map/maptest_map.b3d");

    irr::scene::IMeshSceneNode* scene_node2 = m_scene_manager->addMeshSceneNode(mesh2);
    scene_node2->setMaterialFlag(irr::video::EMF_LIGHTING, false);


}
*/

void IrrDriver::setPhase(LSRenderingPass p)
{
    m_phase = p;
}

LSRenderingPass IrrDriver::getPhase() const
{
  return m_phase;
}

void IrrDriver::IncreaseObjectCount()
{
    object_count[m_phase]++;
}

void IrrDriver::IncreasePolyCount(unsigned Polys)
{
    poly_count[m_phase] += Polys;
}

core::array<video::IRenderTarget> &IrrDriver::getMainSetup()
{
  return m_mrt;
}

GPUTimer &IrrDriver::getGPUTimer(unsigned i)
{
    return m_perf_query[i];
}



// ----------------------------------------------------------------------------

#if defined(__linux__) && !defined(ANDROID)
/*
Returns the parent window of "window" (i.e. the ancestor of window
that is a direct child of the root, or window itself if it is a direct child).
If window is the root window, returns window.
*/
Window get_toplevel_parent(Display* display, Window window)
{
     Window parent;
     Window root;
     Window * children;
     unsigned int num_children;

     while (true)
     {
         if (0 == XQueryTree(display, window, &root,
                   &parent, &children, &num_children))
         {
             Log::fatal("irr_driver", "XQueryTree error\n");
         }

         if (children) { //must test for null
            XFree(children);

         }
         if (window == root || parent == root) {
             return window;
         }
         else {
             window = parent;
         }
     }
}
#endif

// ----------------------------------------------------------------------------
/** If the position of the window should be remembered, store it in the config
 *  file.
 *  \post The user config file must still be saved!
 */

// ----------------------------------------------------------------------------
void IrrDriver::computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode,
                                          size_t width, size_t height)
{
    m_current_screen_size = core::vector2df(float(width), float(height));
    Log::error("Irr Driver", "computeMatrixesAndCameras");
    //m_shadow_matrices->computeMatrixesAndCameras(camnode, width, height);
}   // computeMatrixesAndCameras

// ----------------------------------------------------------------------------

void IrrDriver::updateConfigIfRelevant()
{
        if (!UserConfigParams::m_fullscreen &&
             UserConfigParams::m_remember_window_location)
    {
#ifdef WIN32
        const video::SExposedVideoData& videoData = m_device->getVideoDriver()
                                                     ->getExposedVideoData();
        // this should work even if using DirectX in theory because the HWnd is
        // always third pointer in the struct, no matter which union is used
        HWND window = (HWND)videoData.OpenGLWin32.HWnd;
        WINDOWPLACEMENT placement;
        placement.length = sizeof(WINDOWPLACEMENT);
        if (GetWindowPlacement(window, &placement))
        {
            int x = (int)placement.rcNormalPosition.left;
            int y = (int)placement.rcNormalPosition.top;
            // If the windows position is saved, it must be a non-negative
            // number. So if the window is partly off screen, move it to the
            // corresponding edge.
            if(x<0) x = 0;
            if(y<0) y = 0;
            Log::verbose("irr_driver",
                       "Retrieved window location for config : %i %i\n", x, y);

            if (UserConfigParams::m_window_x != x || UserConfigParams::m_window_y != y)
            {
                UserConfigParams::m_window_x = x;
                UserConfigParams::m_window_y = y;
            }
        }
        else
        {
            Log::warn("irr_driver", "Could not retrieve window location\n");
        }

#elif defined(__linux__) && !defined(ANDROID)
        const video::SExposedVideoData& videoData =
            m_device->getVideoDriver()->getExposedVideoData();
        Display* display = (Display*)videoData.OpenGLLinux.X11Display;
        XWindowAttributes xwa;
        XGetWindowAttributes(display, get_toplevel_parent(display,
                                       videoData.OpenGLLinux.X11Window), &xwa);
        int wx = xwa.x;
        int wy = xwa.y;
        Log::verbose("irr_driver",
                     "Retrieved window location for config : %i %i\n", wx, wy);


        if (UserConfigParams::m_window_x != wx || UserConfigParams::m_window_y != wy)
        {
            UserConfigParams::m_window_x = wx;
            UserConfigParams::m_window_y = wy;
        }

#endif
    }
}   // updateConfigIfRelevant

// ----------------------------------------------------------------------------
/** Gets a list of supported video modes from the irrlicht device. This data
 *  is stored in m_modes.
 */
void IrrDriver::createListOfVideoModes()
{
    // Note that this is actually reported by valgrind as a leak, but it is
    // a leak in irrlicht: this list is dynamically created the first time
    // it is used, but then not cleaned on exit.
    video::IVideoModeList* modes = m_device->getVideoModeList();
    const int count = modes->getVideoModeCount();

    for(int i=0; i<count; i++)
    {
        // only consider 32-bit resolutions for now
        if (modes->getVideoModeDepth(i) >= 24)
        {
            const int w = modes->getVideoModeResolution(i).Width;
            const int h = modes->getVideoModeResolution(i).Height;
            if (h < MIN_SUPPORTED_HEIGHT || w < MIN_SUPPORTED_WIDTH)
                continue;

            VideoMode mode(w, h);
            m_modes.push_back( mode );
        }   // if depth >=24
    }   // for i < video modes count
}   // createListOfVideoModes

void IrrDriver::initDevice()
{
    Log::info("IrrDriver", "initDevice");
    // If --no-graphics option was used, the null device can still be used.

        // This code is only executed once. No need to reload the video
        // modes every time the resolution changes.
        if(m_modes.size()==0)
        {
            createListOfVideoModes();
            // The debug name is only set if irrlicht is compiled in debug
            // mode. So we use this to print a warning to the user.
            if(m_device->getDebugName())
            {
                Log::warn("irr_driver",
                          "!!!!! Performance warning: Irrlicht compiled with "
                          "debug mode.!!!!!\n");
                Log::warn("irr_driver",
                          "!!!!! This can have a significant performance "
                          "impact         !!!!!\n");
            }

        } // end if firstTime

        video::IVideoModeList* modes = m_device->getVideoModeList();
        const core::dimension2d<u32> ssize = modes->getDesktopResolution();
        if (UserConfigParams::m_width > (int)ssize.Width ||
            UserConfigParams::m_height > (int)ssize.Height)
        {
            Log::warn("irr_driver", "The window size specified in "
                      "user config is larger than your screen!");
            UserConfigParams::m_width = (int)ssize.Width;
            UserConfigParams::m_height = (int)ssize.Height;
        }

        core::dimension2d<u32> res = core::dimension2du(UserConfigParams::m_width,
                                                    UserConfigParams::m_height);


        if (UserConfigParams::m_fullscreen)
        {
            if (modes->getVideoModeCount() > 0)
            {
                res = modes->getVideoModeResolution(res, res);

                UserConfigParams::m_width = res.Width;
                UserConfigParams::m_height = res.Height;
            }
            else
            {
                Log::verbose("irr_driver", "Cannot get information about "
                             "resolutions. Try to use the default one.");
                UserConfigParams::m_width = MIN_SUPPORTED_WIDTH;
                UserConfigParams::m_height = MIN_SUPPORTED_HEIGHT;
            }
        }

        m_device->closeDevice();
        m_video_driver  = NULL;
        m_gui_env       = NULL;
        m_scene_manager = NULL;
        // In some circumstances it would happen that a WM_QUIT message
        // (apparently sent for this NULL device) is later received by
        // the actual window, causing it to immediately quit.
        // Following advise on the irrlicht forums I added the following
        // two calles - the first one didn't make a difference (but
        // certainly can't hurt), but the second one apparenlty solved
        // the problem for now.
        m_device->clearSystemMessages();
        m_device->run();
        m_device->drop();
        m_device  = NULL;


        SIrrlichtCreationParameters params;
        params.ForceLegacyDevice = false; //test

        // Try 32 and, upon failure, 24 then 16 bit per pixels



        for (int bits=32; bits>15; bits -=8)
        {
            if(UserConfigParams::logMisc())
                Log::verbose("irr_driver", "Trying to create device with "
                             "%i bits\n", bits);

            params.DriverType    = video::EDT_OPENGL;
            params.Stencilbuffer = false;
            params.Bits          = 32;
            params.EventReceiver = this;
            params.Fullscreen    = false; // test
            params.Vsync         = false; // test

            params.FileSystem    = file_manager->getFileSystem();

            params.WindowSize    =
                core::dimension2du(UserConfigParams::m_width,
                                   UserConfigParams::m_height);



            m_device = createDeviceEx(params); // error here

            if(m_device)
                break;

        }   // for bits=32, 24, 16



        // if still no device, try with a default screen size, maybe
        // size is the problem
        if(!m_device)
        {
            //UserConfigParams::m_width  = MIN_SUPPORTED_WIDTH;
            //UserConfigParams::m_height = MIN_SUPPORTED_HEIGHT;

            m_device = createDevice(video::EDT_OPENGL,
                        core::dimension2du(UserConfigParams::m_width,
                                           UserConfigParams::m_height ),
                                    32, //bits per pixel
                                    false,  // fullscreen
                                    false,  // stencil buffers
                                    false,  // vsync
                                    this,   // event receiver
                                    file_manager->getFileSystem()
                                    );
            if (m_device)
            {
                Log::info("irr_driver", "An invalid resolution was set in "
                             "the config file, reverting to saner values\n");
            }

        }

    if(!m_device)
    {
        Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n");
    }

    m_scene_manager = m_device->getSceneManager();
    m_gui_env       = m_device->getGUIEnvironment();
    m_video_driver  = m_device->getVideoDriver();



    m_sync = 0;


    m_actual_screen_size = m_video_driver->getCurrentRenderTargetSize();

    CVS->init();



    // This remaps the window, so it has to be done before the clear to avoid flicker
    m_device->setResizable(false);

    // Immediate clear to black for a nicer user loading experience
    m_video_driver->beginScene(/*backBuffer clear*/true, /* Z */ false);
    m_video_driver->endScene();

    if (CVS->isGLSL())
    {
        Log::info("irr_driver", "GLSL supported.");
    }

/*    if (!supportGeometryShader())
    {
        // these options require geometry shaders
        UserConfigParams::m_shadows = 0;
        UserConfigParams::m_gi = false;
    }*/

    // m_glsl might be reset in rtt if an error occurs.
    if (CVS->isGLSL())
    {
        //m_shaders = new Shaders();
        Shaders::init();

        m_mrt.clear();
        m_mrt.reallocate(2);

    }
    else
    {
        Log::warn("irr_driver", "Using the fixed pipeline (old GPU, or shaders disabled in options)");
    }

    // Only change video driver settings if we are showing graphics

#if defined(__linux__) && !defined(ANDROID)
        // Set class hints on Linux, used by Window Managers.
        const video::SExposedVideoData& videoData = m_video_driver
                                                ->getExposedVideoData();
        XClassHint* classhint = XAllocClassHint();
        classhint->res_name = (char*)"LaundredSheep";
        classhint->res_class = (char*)"LaundredSheep";
        XSetClassHint((Display*)videoData.OpenGLLinux.X11Display,
                           videoData.OpenGLLinux.X11Window,
                           classhint);
        XFree(classhint);
#endif
        m_device->setWindowCaption(L"LaundredSheep");
        m_device->getVideoDriver()
            ->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
        m_device->getVideoDriver()
            ->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_QUALITY, true);

        // Force creation of mipmaps even if the mipmaps flag in a b3d file
        // does not set the 'enable mipmap' flag.

        //m_scene_manager->getParameters()
        //    ->setAttribute(scene::B3D_LOADER_IGNORE_MIPMAP_FLAG, true);


        // Set window to remembered position
        if (  !UserConfigParams::m_fullscreen
            && UserConfigParams::m_remember_window_location
            && UserConfigParams::m_window_x >= 0
            && UserConfigParams::m_window_y >= 0            )
        {
            moveWindow(UserConfigParams::m_window_x,
                       UserConfigParams::m_window_y);
        } // If reinstating window location
     // If showing graphics

    // Initialize material2D
    video::SMaterial& material2D = m_video_driver->getMaterial2D();
    material2D.setFlag(video::EMF_ANTI_ALIASING, true);
    for (unsigned int n=0; n<video::MATERIAL_MAX_TEXTURES; n++)
    {
        material2D.TextureLayer[n].BilinearFilter = false;
        material2D.TextureLayer[n].TrilinearFilter = true;
        material2D.TextureLayer[n].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
        material2D.TextureLayer[n].TextureWrapV = video::ETC_CLAMP_TO_EDGE;

        //material2D.TextureLayer[n].LODBias = 16;
        material2D.UseMipMaps = true;
    }
    material2D.AntiAliasing=video::EAAM_FULL_BASIC;
    //m_video_driver->enableMaterial2D();

    // Initialize post-processing if supported

    // set cursor visible by default (what's the default is not too clearly documented,
    // so let's decide ourselves...)
    m_device->getCursorControl()->setVisible(true);
    m_pointer_shown = true;
}   // initDevice

// ----------------------------------------------------------------------------
void IrrDriver::setMaxTextureSize()
{
    if( (UserConfigParams::m_high_definition_textures & 0x01) == 0)
    {
        //io::IAttributes &att = m_video_driver->getNonConstDriverAttributes();
        //att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(512, 512));
    }
}   // setMaxTextureSize

//-----------------------------------------------------------------------------
void IrrDriver::getOpenGLData(std::string *vendor, std::string *renderer,
                              std::string *version)
{
    *vendor   = (char*)glGetString(GL_VENDOR  );
    *renderer = (char*)glGetString(GL_RENDERER);
    *version  = (char*)glGetString(GL_VERSION );
}   // getOpenGLData

//-----------------------------------------------------------------------------
void IrrDriver::showPointer()
{
    if (!m_pointer_shown)
    {
        m_pointer_shown = true;
        this->getDevice()->getCursorControl()->setVisible(true);
    }
}   // showPointer

//-----------------------------------------------------------------------------
void IrrDriver::hidePointer()
{


    if (m_pointer_shown)
    {
        m_pointer_shown = false;
        this->getDevice()->getCursorControl()->setVisible(false);
    }
}   // hidePointer

//-----------------------------------------------------------------------------

core::position2di IrrDriver::getMouseLocation()
{
    return this->getDevice()->getCursorControl()->getPosition();
}

//-----------------------------------------------------------------------------
/** Moves the STK main window to coordinates (x,y)
 *  \return true on success, false on failure
 *          (always true on Linux at the moment)
 */
bool IrrDriver::moveWindow(int x, int y)
{
#ifdef WIN32
    const video::SExposedVideoData& videoData =
                    m_video_driver->getExposedVideoData();
    // this should work even if using DirectX in theory,
    // because the HWnd is always third pointer in the struct,
    // no matter which union is used
    HWND window = (HWND)videoData.OpenGLWin32.HWnd;
    if (SetWindowPos(window, HWND_TOP, x, y, -1, -1,
                     SWP_NOOWNERZORDER | SWP_NOSIZE))
    {
        // everything OK
        return true;
    }
    else
    {
        Log::warn("irr_driver", "Could not set window location\n");
        return false;
    }
#elif defined(__linux__) && !defined(ANDROID)
    const video::SExposedVideoData& videoData = m_video_driver->getExposedVideoData();

    Display* display = (Display*)videoData.OpenGLLinux.X11Display;
    int screen = DefaultScreen(display);
    int screen_w = DisplayWidth(display, screen);
    int screen_h = DisplayHeight(display, screen);

    if (x + UserConfigParams::m_width > screen_w)
    {
        x = screen_w - UserConfigParams::m_width;
    }

    if (y + UserConfigParams::m_height > screen_h)
    {
        y = screen_h - UserConfigParams::m_height;
    }

    // TODO: Actually handle possible failure
    //XMoveWindow(display, videoData.OpenGLLinux.X11Window, x, y);
#endif
    return true;
}
//-----------------------------------------------------------------------------

void IrrDriver::changeResolution(const int w, const int h,
                                 const bool fullscreen)
{
    // update user config values
    UserConfigParams::m_prev_width = UserConfigParams::m_width;
    UserConfigParams::m_prev_height = UserConfigParams::m_height;
    UserConfigParams::m_prev_fullscreen = UserConfigParams::m_fullscreen;

    UserConfigParams::m_width = w;
    UserConfigParams::m_height = h;
    UserConfigParams::m_fullscreen = fullscreen;

    // Setting this flag will trigger a call to applyResolutionSetting()
    // in the next update call. This avoids the problem that changeResolution
    // is actually called from the gui, i.e. the event loop, i.e. while the
    // old device is active - so we can't delete this device (which we must
    // do in applyResolutionSettings
    m_resolution_changing = RES_CHANGE_YES;
}   // changeResolution

//-----------------------------------------------------------------------------

void IrrDriver::applyResolutionSettings()
{
    // show black before resolution switch so we don't see OpenGL's buffer
    // garbage during switch
    m_video_driver->beginScene(true, true, video::SColor(255,100,251,140));


    m_video_driver->endScene();



    delete input_manager;
    GUIEngine::clear();
    GUIEngine::cleanUp();

    m_device->closeDevice();
    m_device->clearSystemMessages();
    m_device->run();

    delete material_manager;
    material_manager = NULL;

    // ---- Reinit
    // FIXME: this load sequence is (mostly) duplicated from main.cpp!!
    // That's just error prone
    // (we're sure to update main.cpp at some point and forget this one...)
    //m_shaders->killShaders();

    resetTextureTable();
    // initDevice will drop the current device.
    initDevice();

    // Re-init GUI engine
    GUIEngine::init(m_device, m_video_driver, StateManager::get());

    //material_manager->reInit();
    material_manager = new MaterialManager();
    material_manager->loadMaterial();
    input_manager = new InputManager ();
    input_manager->setMode(InputManager::MENU);

    GUIEngine::addLoadingIcon(
        irr_driver->getTexture(file_manager->getAsset(FileManager::GUI,"options_video.png"))
                             );

    file_manager->pushTextureSearchPath(file_manager->getAsset(FileManager::MODEL,""));
    const std::string materials_file =
        file_manager->getAssetChecked(FileManager::MODEL, "materials.xml");
    if (materials_file != "")
    {
        material_manager->addSharedMaterial(materials_file);
    }
    GUIEngine::addLoadingIcon(
        irr_driver->getTexture(file_manager->getAsset(FileManager::GUI,"gift.png")) );

    file_manager->popTextureSearchPath();


    std::string banana = file_manager->getAsset(FileManager::GUI, "banana.png");
    GUIEngine::addLoadingIcon(irr_driver->getTexture(banana) );
    // No need to reload cached track data (track_manager->cleanAllCachedData
    // above) - this happens dynamically when the tracks are loaded.
    GUIEngine::reshowCurrentScreen();
    MessageQueue::updatePosition();
}   // applyResolutionSettings


// ----------------------------------------------------------------------------

void IrrDriver::cancelResChange()
{
    UserConfigParams::m_width = UserConfigParams::m_prev_width;
    UserConfigParams::m_height = UserConfigParams::m_prev_height;
    UserConfigParams::m_fullscreen = UserConfigParams::m_prev_fullscreen;

    // This will trigger calling applyResolutionSettings in update(). This is
    // necessary to avoid that the old screen is deleted, while it is
    // still active (i.e. sending out events which triggered the change
    // of resolution
    // Setting this flag will trigger a call to applyResolutionSetting()
    // in the next update call. This avoids the problem that changeResolution
    // is actually called from the gui, i.e. the event loop, i.e. while the
    // old device is active - so we can't delete this device (which we must
    // do in applyResolutionSettings)
    m_resolution_changing=RES_CHANGE_CANCEL;

}   // cancelResChange



// ----------------------------------------------------------------------------
/** Loads an animated mesh and returns a pointer to it.
 *  \param filename File to load.
 */
scene::IAnimatedMesh *IrrDriver::getAnimatedMesh(const std::string &filename)
{
    scene::IAnimatedMesh *m  = NULL;

    if (StringUtils::getExtension(filename) == "b3dz")
    {
        // compressed file
        io::IFileSystem* file_system = getDevice()->getFileSystem();
        if (!file_system->addFileArchive(filename.c_str(),
                                         /*ignoreCase*/false,
                                         /*ignorePath*/true, io::EFAT_ZIP))
        {
            Log::error("irr_driver",
                       "getMesh: Failed to open zip file <%s>\n",
                       filename.c_str());
            return NULL;
        }

        // Get the recently added archive
        io::IFileArchive* zip_archive =
        file_system->getFileArchive(file_system->getFileArchiveCount()-1);
        io::IReadFile* content = zip_archive->createAndOpenFile(0);
        m = m_scene_manager->getMesh(content);
        content->drop();

        file_system->removeFileArchive(file_system->getFileArchiveCount()-1);
    }
    else
    {
        m = m_scene_manager->getMesh(filename.c_str());
    }

    if(!m) return NULL;

    setAllMaterialFlags(m);

    return m;
}   // getAnimatedMesh

// ----------------------------------------------------------------------------

/** Loads a non-animated mesh and returns a pointer to it.
 *  \param filename  File to load.
 */
scene::IMesh *IrrDriver::getMesh(const std::string &filename)
{
    scene::IAnimatedMesh* am = getAnimatedMesh(filename);
    if (am == NULL)
    {
        Log::error("irr_driver", "Cannot load mesh <%s>\n",
                   filename.c_str());
        return NULL;
    }
    return am->getMesh(0);
}   // getMesh

// ----------------------------------------------------------------------------
/** Sets the material flags in this mesh depending on the settings in
 *  material_manager.
 *  \param mesh  The mesh to change the settings in.
 */
void IrrDriver::setAllMaterialFlags(scene::IMesh *mesh) const
{

    unsigned int n=mesh->getMeshBufferCount();
    for(unsigned int i=0; i<n; i++)
    {
        scene::IMeshBuffer *mb = mesh->getMeshBuffer(i);
        video::SMaterial &irr_material = mb->getMaterial();

        // special case : for splatting, the main material is on layer 1.
        // it was done this way to provide a fallback for computers
        // where shaders are not supported
        //video::ITexture* t2 = irr_material.getTexture(1);
        bool is_splatting = false;
        /*
        if (t2)
        {
            Material* mat = material_manager->getMaterialFor(t2, mb);
            if (mat != NULL && mat->getShaderType() == Material::SHADERTYPE_SPLATTING)
            {
                material_manager->setAllMaterialFlags(t2, mb);
                is_splatting = true;
            }
        }
        */

        if (!is_splatting)
        {
            video::ITexture* t = irr_material.getTexture(0);
            if (t)
            {
                material_manager->setAllMaterialFlags(t, mb);
            }
            else
            {
                material_manager->setAllUntexturedMaterialFlags(mb);
            }
        }

    }  // for i<getMeshBufferCount()

}   // setAllMaterialFlags




// ----------------------------------------------------------------------------
/** Adds a static mesh to scene. This should be used for smaller objects,
 *  since the node is not optimised.
 *  \param mesh The mesh to add.
 */
scene::IMeshSceneNode *IrrDriver::addMesh(scene::IMesh *mesh,
                                          const std::string& debug_name,
                                          scene::ISceneNode *parent)
{
    Log::info("IrrDriver", "add a mesh");
    //return m_scene_manager->addMeshSceneNode(mesh, parent); // test

    if (!CVS->isGLSL())
        return m_scene_manager->addMeshSceneNode(mesh, parent);

    if (!parent)
      parent = m_scene_manager->getRootSceneNode();
    //scene::IMeshSceneNode* node = 0;
    scene::IMeshSceneNode* node = new STKMeshSceneNode(mesh, parent, m_scene_manager, -1, debug_name);
    node->drop();

    return node;
}   // addMesh

/*
scene::IMeshSceneNode *IrrDriver::addOctTree(scene::IMesh *mesh)
{
    return m_scene_manager->addOctreeSceneNode(mesh);
}   // addOctTree
*/



// ----------------------------------------------------------------------------
/** Removes a scene node from the scene.
 *  \param node The scene node to remove.
 */
void IrrDriver::removeNode(scene::ISceneNode *node)
{
    node->remove();
}   // removeNode

// ----------------------------------------------------------------------------
/** Removes a mesh from the mesh cache, freeing the memory.
 *  \param mesh The mesh to remove.
 */
void IrrDriver::removeMeshFromCache(scene::IMesh *mesh)
{
    m_scene_manager->getMeshCache()->removeMesh(mesh);
}   // removeMeshFromCache

// ----------------------------------------------------------------------------
/** Removes a texture from irrlicht's texture cache.
 *  \param t The texture to remove.
 */
void IrrDriver::removeTexture(video::ITexture *t)
{
    m_video_driver->removeTexture(t);
}   // removeTexture




// ----------------------------------------------------------------------------
/** Adds a camera to the scene.
 */
scene::ICameraSceneNode *IrrDriver::addCameraSceneNode()
{
    return m_scene_manager->addCameraSceneNode();
}   // addCameraSceneNode

// ----------------------------------------------------------------------------
/** Removes a camera. This can't be done with removeNode() since the camera
 *  can be marked as active, meaning a drop will not delete it. While this
 *  doesn't really cause a memory leak (the camera is removed the next time
 *  a camera is added), it's a bit cleaner and easier to check for memory
 *  leaks, since the scene root should now always be empty.
 */
void IrrDriver::removeCameraSceneNode(scene::ICameraSceneNode *camera)
{
    if(camera==m_scene_manager->getActiveCamera())
        m_scene_manager->setActiveCamera(NULL);    // basically causes a drop
    camera->remove();
}   // removeCameraSceneNode


// ----------------------------------------------------------------------------
/** Loads a texture from a file and returns the texture object. This is just
 *  a convenient wrapper which loads the texture from a STK asset directory.
 *  It calls the file manager to get the full path, then calls the normal
 *  getTexture() function.s
 *  \param type The FileManager::AssetType of the texture.
 *  \param filename File name of the texture to load.
 *  \param is_premul If the alpha values needd to be multiplied for
 *         all pixels.
 *  \param is_prediv If the alpha value needs to be divided into
 *         each pixel.
 */

 // ----------------------------------------------------------------------------
/** Disables the texture error message again.
 */
void IrrDriver::unsetTextureErrorMessage()
{
    m_texture_error_message = "";
}   // unsetTextureErrorMessage

video::ITexture *IrrDriver::getTexture(FileManager::AssetType type,
                                       const std::string &filename,
                                       bool is_premul,
                                       bool is_prediv,
                                       bool complain_if_not_found)
{
    const std::string path = file_manager->getAsset(type, filename);
    return getTexture(path, is_premul, is_prediv, complain_if_not_found);
}   // getTexture


// ----------------------------------------------------------------------------
/** Loads a texture from a file and returns the texture object.
 *  \param filename File name of the texture to load.
 *  \param is_premul If the alpha values needd to be multiplied for
 *         all pixels.
 *  \param is_prediv If the alpha value needs to be divided into
 *         each pixel.
 */
video::ITexture *IrrDriver::getTexture(const std::string &filename,
                                       bool is_premul,
                                       bool is_prediv,
                                       bool complain_if_not_found)
{
    video::ITexture* out;
    if(!is_premul && !is_prediv)
    {
        if (!complain_if_not_found) m_device->getLogger()->setLogLevel(ELL_NONE);
        out = m_video_driver->getTexture(filename.c_str());
        if (!complain_if_not_found) m_device->getLogger()->setLogLevel(ELL_WARNING);
    }
    else
    {
        // FIXME: can't we just do this externally, and just use the
        // modified textures??
        video::IImage* img =
            m_video_driver->createImageFromFile(filename.c_str());
        // PNGs are non premul, but some are used for premul tasks, so convert
        // http://home.comcast.net/~tom_forsyth/blog.wiki.html#[[Premultiplied%20alpha]]
        // FIXME check param, not name
        if(img && is_premul &&
            StringUtils::hasSuffix(filename.c_str(), ".png") &&
            (img->getColorFormat() == video::ECF_A8R8G8B8) &&
            img->lock())
        {
            core::dimension2d<u32> dim = img->getDimension();
            for(unsigned int x = 0; x < dim.Width; x++)
            {
                for(unsigned int y = 0; y < dim.Height; y++)
                {
                    video::SColor col = img->getPixel(x, y);
                    unsigned int alpha = col.getAlpha();
                    unsigned int red   = alpha * col.getRed()   / 255;
                    unsigned int blue  = alpha * col.getBlue()  / 255;
                    unsigned int green = alpha * col.getGreen() / 255;
                    col.set(alpha, red, green, blue);
                    img->setPixel(x, y, col, false);
                }   // for y
            }   // for x
            img->unlock();
        }   // if png and ColorFOrmat and lock
        // Other formats can be premul, but the tasks can be non premul
        // So divide to get the separate RGBA (only possible if alpha!=0)
        else if(img && is_prediv &&
            (img->getColorFormat() == video::ECF_A8R8G8B8) &&
            img->lock())
        {
            core::dimension2d<u32> dim = img->getDimension();
            for(unsigned int  x = 0; x < dim.Width; x++)
            {
                for(unsigned int y = 0; y < dim.Height; y++)
                {
                    video::SColor col = img->getPixel(x, y);
                    unsigned int alpha = col.getAlpha();
                    // Avoid divide by zero
                    if (alpha) {
                        unsigned int red   = 255 * col.getRed() / alpha ;
                        unsigned int blue  = 255 * col.getBlue() / alpha;
                        unsigned int green = 255 * col.getGreen() / alpha;
                        col.set(alpha, red, green, blue);
                        img->setPixel(x, y, col, false);
                    }
                }   // for y
            }   // for x
            img->unlock();
        }   // if premul && color format && lock
        out = m_video_driver->addTexture(filename.c_str(), img, NULL);
    }   // if is_premul or is_prediv


    if (complain_if_not_found && out == NULL)
    {

    }

    m_texturesFileName[out] = filename;

    return out;
}   // getTexture

// ----------------------------------------------------------------------------
/** Clear the texture-filename reminder.
*/
void IrrDriver::clearTexturesFileName()
{
    m_texturesFileName.clear();
} // clearTexturesFileName

// ----------------------------------------------------------------------------
/** Get the texture file name using a texture pointer.
*   \param tex Pointer on the texture for which we want to find the file name.
*   \return Filename of the texture if found, or an empty string otherwise.
*/
std::string IrrDriver::getTextureName(video::ITexture* tex)
{
    std::map<video::ITexture*, std::string>::iterator it;
    it = m_texturesFileName.find(tex);
    if (it != m_texturesFileName.end())
        return it->second;
    else
        return "";
} // getTextureName

// ----------------------------------------------------------------------------
/** Appends a pointer to each texture used in this mesh to the vector.
 *  \param mesh The mesh from which the textures are being determined.
 *  \param texture_list The list to which to attach the pointer to.
 */
void IrrDriver::grabAllTextures(const scene::IMesh *mesh)
{
    const unsigned int n = mesh->getMeshBufferCount();

    for(unsigned int i=0; i<n; i++)
    {
        scene::IMeshBuffer *b = mesh->getMeshBuffer(i);
        video::SMaterial   &m = b->getMaterial();
        for(unsigned int j=0; j<video::MATERIAL_MAX_TEXTURES; j++)
        {
            video::ITexture *t = m.getTexture(j);
            if(t)
                t->grab();
        }   // for j < MATERIAL_MAX_TEXTURE
    }   // for i <getMeshBufferCount
}   // grabAllTextures

// ----------------------------------------------------------------------------
/** Appends a pointer to each texture used in this mesh to the vector.
 *  \param mesh The mesh from which the textures are being determined.
 *  \param texture_list The list to which to attach the pointer to.
 */
void IrrDriver::dropAllTextures(const scene::IMesh *mesh)
{
    const unsigned int n = mesh->getMeshBufferCount();

    for(unsigned int i=0; i<n; i++)
    {
        scene::IMeshBuffer *b = mesh->getMeshBuffer(i);
        video::SMaterial   &m = b->getMaterial();
        for(unsigned int j=0; j<video::MATERIAL_MAX_TEXTURES; j++)
        {
            video::ITexture *t = m.getTexture(j);
            if(t)
            {
                t->drop();
                if(t->getReferenceCount()==1)
                    removeTexture(t);
            }   // if t
        }   // for j < MATERIAL_MAX_TEXTURE
    }   // for i <getMeshBufferCount
}   // dropAllTextures

// ----------------------------------------------------------------------------
video::ITexture* IrrDriver::applyMask(video::ITexture* texture,
                                      const std::string& mask_path)
{
    video::IImage* img =
        m_video_driver->createImage(texture, core::position2d<s32>(0,0),
                                    texture->getSize());

    video::IImage* mask =
        m_video_driver->createImageFromFile(mask_path.c_str());

    if (img == NULL || mask == NULL) return NULL;

    if (img->lock() && mask->lock())
    {
        core::dimension2d<u32> dim = img->getDimension();
        for (unsigned int x = 0; x < dim.Width; x++)
        {
            for (unsigned int y = 0; y < dim.Height; y++)
            {
                video::SColor col = img->getPixel(x, y);
                video::SColor alpha = mask->getPixel(x, y);
                col.setAlpha( alpha.getRed() );
                img->setPixel(x, y, col, false);
            }   // for y
        }   // for x

        mask->unlock();
        img->unlock();
    }
    else
    {
        return NULL;
    }

    std::string base =
        StringUtils::getBasename(texture->getName().getPath().c_str());
    video::ITexture *t = m_video_driver->addTexture(base.c_str(),img, NULL);
    img->drop();
    mask->drop();
    return t;
}   // applyMask

// ----------------------------------------------------------------------------
void IrrDriver::setRTT(RTT* rtt)
{

//    m_shadow_matrices->resetShadowCamNodes();
    m_rtts = rtt;
}
// ----------------------------------------------------------------------------
void IrrDriver::onLoadWorld()
{
    if (CVS->isGLSL())
    {
        const core::recti &viewport = Camera::getCamera(0)->getViewport();
        size_t width = viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X;
        size_t height = viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y;
        m_rtts = new RTT(width, height);
    }
}
// ----------------------------------------------------------------------------
void IrrDriver::onUnloadWorld()
{
    delete m_rtts;
    m_rtts = NULL;

    //suppressSkyBox();
}


// ----------------------------------------------------------------------------
/** Displays the FPS on the screen.
 */
void IrrDriver::displayFPS()
{
    gui::IGUIFont* font = GUIEngine::getSmallFont();
    core::rect<s32> position;


    position = core::rect<s32>(75, 0, 900, 40);
    GL32_draw2DRectangle(video::SColor(150, 96, 74, 196), position, NULL);
    // We will let pass some time to let things settle before trusting FPS counter
    // even if we also ignore fps = 1, which tends to happen in first checks
    const int NO_TRUST_COUNT = 200;
    static int no_trust = NO_TRUST_COUNT;

    // Min and max info tracking, per mode, so user can check game vs menus
    bool current_state     = StateManager::get()->getGameState()
                               == GUIEngine::GAME;
    static bool prev_state = false;
    static int min         = 999; // Absurd values for start will print first time
    static int max         = 0;   // but no big issue, maybe even "invisible"
    static float low       = 1000000.0f; // These two are for polycount stats
    static float high      = 0.0f;       // just like FPS, but in KTris

    // Reset limits if state changes
    if (prev_state != current_state)
    {
        min = 999;
        max = 0;
        low = 1000000.0f;
        high = 0.0f;
        no_trust = NO_TRUST_COUNT;
        prev_state = current_state;
    }

    if (no_trust)
    {
        no_trust--;

        static video::SColor fpsColor = video::SColor(255, 255, 0, 0);
        font->draw( L"FPS: ...", core::rect< s32 >(100,0,400,50), fpsColor,
                    false );

        return;
    }

    // Ask for current frames per second and last number of triangles
    // processed (trimed to thousands)
    const int fps         = m_video_driver->getFPS();
    const float kilotris  = m_video_driver->getPrimitiveCountDrawn(0)
                                * (1.f / 1000.f);

    if (min > fps && fps > 1) min = fps; // Start moments sometimes give useless 1
    if (max < fps) max = fps;
    if (low > kilotris) low = kilotris;
    if (high < kilotris) high = kilotris;

    core::stringw fpsString;


    fpsString = "FPS: %d/%d/%d - %d KTris", min, fps, max, (int)roundf(kilotris);

    static video::SColor fpsColor = video::SColor(255, 55, 255, 0);

    font->draw( fpsString.c_str(), position, fpsColor, false );
}   // updateFPS


// ----------------------------------------------------------------------------
/** Update, called once per frame.
 *  \param dt Time since last update
 */
void IrrDriver::update(float dt)
{
    Log::info("IrrDriver","update");
    // User aborted (e.g. closed window)
    // =================================
    if (!m_device->run())
    {

        main_loop->abort();
        return;
    }

    // If we quit via the menu the m_device->run() does not return true.
    // To avoid any other calls, we return here.
    if(main_loop->isAborted())
        return;

    // If the resolution should be switched, do it now. This will delete the
    // old device and create a new one.
    /*
    if (m_resolution_changing!=RES_CHANGE_NONE)
    {
        applyResolutionSettings();
        if(m_resolution_changing==RES_CHANGE_YES)
        new ConfirmResolutionDialog();
        m_resolution_changing = RES_CHANGE_NONE;
    }
    */

    //test_Render();



    World *world = World::getWorld();

    if (GUIEngine::getCurrentScreen() == NULL)
    {
    //Log::warn("irrDriver", "no screen");
    }

    if (GUIEngine::getCurrentScreen() != NULL &&
        GUIEngine::getCurrentScreen()->needs3D() &&
        world != NULL)
    {
        //printf("Screen that needs 3D\n");
        //m_video_driver->beginScene(/*backBuffer clear*/true, /*zBuffer*/true,
        //                           video::SColor(100,0,255,255));
        //m_scene_manager->drawAll();

        if (CVS->isGLSL())
            renderGLSL(dt);
            //renderFixed(dt);
        else
            renderFixed(dt);

        //GUIEngine::render(dt);
        //m_video_driver->endScene();
        return;
    }

    else if (!world)
    {
        Log::error("IrrDriver","no world whao!!!");
        m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true,
                                   video::SColor(55,255,101,140));

        GUIEngine::render(dt);

        m_video_driver->endScene();
        return;
    }
    //Log::warn("irrDriver", "???");
    if (CVS->isGLSL())
        renderGLSL(dt);
        //renderFixed(dt);

    else
        renderFixed(dt);
}   // update



// ----------------------------------------------------------------------------
/** This is not really used to process events, it's only used to shut down
 *  irrLicht's chatty logging until the event handler is ready to take
 *  the task.
 */
bool IrrDriver::OnEvent(const irr::SEvent &event)
{
    //TODO: ideally we wouldn't use this object to STFU irrlicht's chatty
    //      debugging, we'd just create the EventHandler earlier so it
    //      can act upon it
    switch (event.EventType)
    {
    case irr::EET_LOG_TEXT_EVENT:
    {
        // Ignore 'normal' messages
        if (event.LogEvent.Level > 1)
        {
            Log::warn("[IrrDriver Temp Logger]", "Level %d: %s\n",
                   event.LogEvent.Level,event.LogEvent.Text);
        }
        return true;
    }
    default:
        return false;
    }   // switch

    return false;
}   // OnEvent

// ----------------------------------------------------------------------------

#if 0
#pragma mark -
#pragma mark RTT
#endif

// ----------------------------------------------------------------------------
/**
 * THIS IS THE OLD OPENGL 1 RTT PROVIDER, USE THE SHADER-BASED
 * RTT FOR NEW DEVELOPMENT
 * Begins a rendering to a texture.
 *  \param dimension The size of the texture.
 *  \param name Name of the texture.
 *  \param persistent_texture Whether the created RTT texture should persist in
 *                            memory after the RTTProvider is deleted
 */
IrrDriver::RTTProvider::RTTProvider(const core::dimension2du &dimension,
                                    const std::string &name, bool persistent_texture)
{
    m_persistent_texture = persistent_texture;
    m_video_driver = irr_driver->getVideoDriver();
    m_render_target_texture =
        m_video_driver->addRenderTargetTexture(dimension,
                                               name.c_str(),
                                               video::ECF_A8R8G8B8);
    if (m_render_target_texture != NULL)
    {
        m_video_driver->setRenderTarget(m_render_target_texture);
    }

    m_rtt_main_node = NULL;
    m_camera        = NULL;
    m_light         = NULL;
}   // RTTProvider

// ----------------------------------------------------------------------------
/**
 * Performs the actual render-to-texture
 *  \param target The texture to render the meshes to.
 *  \param angle (Optional) heading for all meshes.
 *  \return the texture that was rendered to, or NULL if RTT does not work on
 *          this computer
 */
video::ITexture* IrrDriver::RTTProvider::renderToTexture(float angle,
                                                         bool is_2d_render)
{
    // m_render_target_texture will be NULL if RTT doesn't work on this computer
    if (m_render_target_texture == NULL) return NULL;

    // Rendering a 2d only model (using direct opengl rendering)
    // does not work if setRenderTarget is called here again.
    // And rendering 3d only works if it is called here :(
    if(!is_2d_render)
        m_video_driver->setRenderTarget(m_render_target_texture);

    if (angle != -1 && m_rtt_main_node != NULL)
        m_rtt_main_node->setRotation( core::vector3df(0, angle, 0) );

    video::SOverrideMaterial &overridemat = m_video_driver->getOverrideMaterial();
    overridemat.EnablePasses = scene::ESNRP_SOLID;
    //overridemat.EnableFlags = video::EMF_MATERIAL_TYPE;
    //overridemat.Material.MaterialType = video::EMT_SOLID;

    if (m_rtt_main_node == NULL)
    {
        irr_driver->getSceneManager()->drawAll();
    }
    else
    {
        m_rtt_main_node->setVisible(true);
        m_light->setVisible(true);
        irr_driver->getSceneManager()->drawAll();
        m_rtt_main_node->setVisible(false);
        m_light->setVisible(false);
    }

    overridemat.EnablePasses = 0;

    m_video_driver->setRenderTarget(0, false, false);
    return m_render_target_texture;
}

// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------

void IrrDriver::applyObjectPassShader(scene::ISceneNode * const node, bool rimlit)
{
    if (!CVS->isGLSL())
        return;

    // Don't override sky
    if (node->getType() == scene::ESNT_SKY_DOME ||
        node->getType() == scene::ESNT_SKY_BOX)
        return;

    const u32 mcount = node->getMaterialCount();
    u32 i;
    const video::E_MATERIAL_TYPE ref = Shaders::getShader(rimlit ? ES_OBJECTPASS_RIMLIT
                                                                 : ES_OBJECTPASS_REF);
    const video::E_MATERIAL_TYPE pass = Shaders::getShader(rimlit ? ES_OBJECTPASS_RIMLIT
                                                                  : ES_OBJECTPASS);

    const video::E_MATERIAL_TYPE origref = Shaders::getShader(ES_OBJECTPASS_REF);
    const video::E_MATERIAL_TYPE origpass = Shaders::getShader(ES_OBJECTPASS);

    bool viamb = false;
    scene::IMesh *mesh = NULL;
    if (node->getType() == scene::ESNT_ANIMATED_MESH)
    {
        viamb = ((scene::IAnimatedMeshSceneNode *) node)->isReadOnlyMaterials();
        mesh = ((scene::IAnimatedMeshSceneNode *) node)->getMesh();
    }
    else if (node->getType() == scene::ESNT_MESH)
    {
        viamb = ((scene::IMeshSceneNode *) node)->isReadOnlyMaterials();
        mesh = ((scene::IMeshSceneNode *) node)->getMesh();
    }
    //else if (node->getType() == scene::ESNT_WATER_SURFACE)
    //{
    //    viamb = (dynamic_cast<scene::IMeshSceneNode*>(node))->isReadOnlyMaterials();
    //    mesh = (dynamic_cast<scene::IMeshSceneNode*>(node))->getMesh();
    //}

    for (i = 0; i < mcount; i++)
    {
        video::SMaterial &nodemat = node->getMaterial(i);
        video::SMaterial &mbmat = mesh ? mesh->getMeshBuffer(i)->getMaterial() : nodemat;
        video::SMaterial *mat = &nodemat;

        if (viamb)
            mat = &mbmat;

        if (mat->MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF ||
            mat->MaterialType == origref)
            mat->MaterialType = ref;
        else if (mat->MaterialType == video::EMT_SOLID ||
                 mat->MaterialType == origpass ||
                 (mat->MaterialType >= video::EMT_LIGHTMAP &&
                 mat->MaterialType <= video::EMT_LIGHTMAP_LIGHTING_M4))
            mat->MaterialType = pass;
    }


    core::list<scene::ISceneNode*> kids = node->getChildren();
    scene::ISceneNodeList::Iterator it = kids.begin();
    for (; it != kids.end(); ++it)
    {
        applyObjectPassShader(*it, rimlit);
    }
}

// ----------------------------------------------------------------------------

void IrrDriver::applyObjectPassShader()
{
    if (!CVS->isGLSL())
        return;

    applyObjectPassShader(m_scene_manager->getRootSceneNode());
}

GLuint IrrDriver::getRenderTargetTexture(TypeRTT which)
{
    return m_rtts->getRenderTarget(which);
}
