2248 lines
80 KiB
C++
2248 lines
80 KiB
C++
//
|
|
// 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 "graphics/irr_driver.hpp"
|
|
|
|
#include "config/user_config.hpp"
|
|
#include "graphics/callbacks.hpp"
|
|
#include "graphics/camera.hpp"
|
|
#include "graphics/glwrap.hpp"
|
|
#include "graphics/hardware_skinning.hpp"
|
|
#include "graphics/lens_flare.hpp"
|
|
#include "graphics/light.hpp"
|
|
#include "graphics/material_manager.hpp"
|
|
#include "graphics/particle_kind_manager.hpp"
|
|
#include "graphics/per_camera_node.hpp"
|
|
#include "graphics/post_processing.hpp"
|
|
#include "graphics/referee.hpp"
|
|
#include "graphics/shaders.hpp"
|
|
#include "graphics/shadow_importance.hpp"
|
|
#include "graphics/stkanimatedmesh.hpp"
|
|
#include "graphics/stkbillboard.hpp"
|
|
#include "graphics/stkmeshscenenode.hpp"
|
|
#include "graphics/sun.hpp"
|
|
#include "graphics/rtts.hpp"
|
|
#include "graphics/water.hpp"
|
|
#include "graphics/wind.hpp"
|
|
#include "guiengine/engine.hpp"
|
|
#include "guiengine/modaldialog.hpp"
|
|
#include "guiengine/scalable_font.hpp"
|
|
#include "guiengine/screen.hpp"
|
|
#include "io/file_manager.hpp"
|
|
#include "items/item_manager.hpp"
|
|
#include "items/powerup_manager.hpp"
|
|
#include "items/attachment_manager.hpp"
|
|
#include "items/projectile_manager.hpp"
|
|
#include "karts/abstract_kart.hpp"
|
|
#include "karts/kart_properties_manager.hpp"
|
|
#include "main_loop.hpp"
|
|
#include "modes/profile_world.hpp"
|
|
#include "modes/world.hpp"
|
|
#include "physics/physics.hpp"
|
|
#include "states_screens/dialogs/confirm_resolution_dialog.hpp"
|
|
#include "states_screens/state_manager.hpp"
|
|
#include "tracks/track_manager.hpp"
|
|
#include "utils/constants.hpp"
|
|
#include "utils/log.hpp"
|
|
#include "utils/profiler.hpp"
|
|
#include "utils/vs.hpp"
|
|
|
|
#include <irrlicht.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 || \
|
|
_IRR_MATERIAL_MAX_TEXTURES_ < 8 || !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;
|
|
|
|
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);
|
|
m_request_screenshot = false;
|
|
m_shaders = NULL;
|
|
m_rtts = NULL;
|
|
m_post_processing = NULL;
|
|
m_wind = new Wind();
|
|
m_mipviz = m_wireframe = m_normals = m_ssaoviz = \
|
|
m_lightviz = m_shadowviz = m_distortviz = 0;
|
|
} // IrrDriver
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Destructor - removes the irrlicht device.
|
|
*/
|
|
IrrDriver::~IrrDriver()
|
|
{
|
|
// Note that we can not simply delete m_post_processing here:
|
|
// m_post_processing uses a material that has a reference to
|
|
// m_post_processing (for a callback). So when the material is
|
|
// removed it will try to drop the ref count of its callback object,
|
|
// which is m_post_processing, and which was already deleted. So
|
|
// instead we just decrease the ref count here. When the material
|
|
// is deleted, it will trigger the actual deletion of
|
|
// PostProcessing when decreasing the refcount of its callback object.
|
|
if(m_post_processing)
|
|
{
|
|
// check if we createad the OpenGL device by calling initDevice()
|
|
m_post_processing->drop();
|
|
}
|
|
assert(m_device != NULL);
|
|
|
|
m_device->drop();
|
|
m_device = NULL;
|
|
m_modes.clear();
|
|
|
|
delete m_shaders;
|
|
delete m_wind;
|
|
} // ~IrrDriver
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Called before a race is started, after all cameras are set up.
|
|
*/
|
|
void IrrDriver::reset()
|
|
{
|
|
if (m_glsl) m_post_processing->reset();
|
|
} // reset
|
|
|
|
void IrrDriver::setPhase(STKRenderingPass p)
|
|
{
|
|
m_phase = p;
|
|
}
|
|
|
|
STKRenderingPass IrrDriver::getPhase() const
|
|
{
|
|
return m_phase;
|
|
}
|
|
|
|
void IrrDriver::IncreaseObjectCount()
|
|
{
|
|
object_count[m_phase]++;
|
|
}
|
|
|
|
core::array<video::IRenderTarget> &IrrDriver::getMainSetup()
|
|
{
|
|
return m_mrt;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#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
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
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;
|
|
user_config->saveConfig();
|
|
}
|
|
}
|
|
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;
|
|
user_config->saveConfig();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** This creates the actualy OpenGL device. This is called
|
|
*/
|
|
void IrrDriver::initDevice()
|
|
{
|
|
// If --no-graphics option was used, the null device can still be used.
|
|
if (!ProfileWorld::isNoGraphics())
|
|
{
|
|
// 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
|
|
|
|
const core::dimension2d<u32> ssize = m_device->getVideoModeList()
|
|
->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 = 800;
|
|
UserConfigParams::m_height = 600;
|
|
}
|
|
|
|
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();
|
|
// Clear the pointer stored in the file manager
|
|
file_manager->dropFileSystem();
|
|
m_device->drop();
|
|
m_device = NULL;
|
|
|
|
SIrrlichtCreationParameters params;
|
|
|
|
// 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 = bits;
|
|
params.EventReceiver = this;
|
|
params.Fullscreen = UserConfigParams::m_fullscreen;
|
|
params.Vsync = UserConfigParams::m_vsync;
|
|
params.WindowSize =
|
|
core::dimension2du(UserConfigParams::m_width,
|
|
UserConfigParams::m_height);
|
|
|
|
/*
|
|
switch ((int)UserConfigParams::m_antialiasing)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
params.AntiAlias = 2;
|
|
break;
|
|
case 2:
|
|
params.AntiAlias = 4;
|
|
break;
|
|
case 3:
|
|
params.AntiAlias = 8;
|
|
break;
|
|
default:
|
|
Log::error("irr_driver",
|
|
"[IrrDriver] WARNING: Invalid value for "
|
|
"anti-alias setting : %i\n",
|
|
(int)UserConfigParams::m_antialiasing);
|
|
}
|
|
*/
|
|
m_device = createDeviceEx(params);
|
|
if(m_device)
|
|
break;
|
|
} // for bits=32, 24, 16
|
|
|
|
|
|
// if still no device, try with a standard 800x600 window size, maybe
|
|
// size is the problem
|
|
if(!m_device)
|
|
{
|
|
UserConfigParams::m_width = 800;
|
|
UserConfigParams::m_height = 600;
|
|
|
|
m_device = createDevice(video::EDT_OPENGL,
|
|
core::dimension2du(UserConfigParams::m_width,
|
|
UserConfigParams::m_height ),
|
|
32, //bits per pixel
|
|
UserConfigParams::m_fullscreen,
|
|
false, // stencil buffers
|
|
false, // vsync
|
|
this // event receiver
|
|
);
|
|
if (m_device)
|
|
{
|
|
Log::verbose("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();
|
|
|
|
GLMajorVersion = 2;
|
|
GLMinorVersion = 1;
|
|
glGetIntegerv(GL_MAJOR_VERSION, &GLMajorVersion);
|
|
glGetIntegerv(GL_MINOR_VERSION, &GLMinorVersion);
|
|
Log::info("IrrDriver", "OPENGL VERSION IS %d.%d", GLMajorVersion, GLMinorVersion);
|
|
m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1));
|
|
|
|
// 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();
|
|
|
|
// Stores the new file system pointer.
|
|
file_manager->reInit();
|
|
|
|
|
|
if (m_glsl)
|
|
{
|
|
Log::info("irr_driver", "GLSL supported.");
|
|
|
|
// Order matters, create RTTs as soon as possible, as they are the largest blocks.
|
|
m_rtts = new RTT();
|
|
}
|
|
// m_glsl might be reset in rtt if an error occurs.
|
|
if(m_glsl)
|
|
{
|
|
m_shaders = new Shaders();
|
|
m_shadow_importance = new ShadowImportance();
|
|
|
|
m_mrt.clear();
|
|
m_mrt.reallocate(2);
|
|
|
|
irr::video::COpenGLDriver* gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver();
|
|
gl_driver->extGlGenQueries(1, &m_lensflare_query);
|
|
gl_driver->extGlGenQueries(Q_LAST, m_perf_query);
|
|
m_query_issued = false;
|
|
|
|
scene::IMesh * const sphere = m_scene_manager->getGeometryCreator()->createSphereMesh(1, 16, 16);
|
|
m_sun_interposer = new STKMeshSceneNode(sphere, m_scene_manager->getRootSceneNode(), NULL, -1);
|
|
m_sun_interposer->grab();
|
|
m_sun_interposer->setParent(NULL);
|
|
m_sun_interposer->setScale(core::vector3df(20));
|
|
|
|
m_sun_interposer->getMaterial(0).Lighting = false;
|
|
m_sun_interposer->getMaterial(0).ColorMask = video::ECP_NONE;
|
|
m_sun_interposer->getMaterial(0).ZWriteEnable = false;
|
|
m_sun_interposer->getMaterial(0).MaterialType = m_shaders->getShader(ES_OBJECTPASS);
|
|
|
|
sphere->drop();
|
|
|
|
m_lensflare = new scene::CLensFlareSceneNode(NULL, m_scene_manager, -1);
|
|
video::ITexture * const tex = getTexture(FileManager::TEXTURE,
|
|
"lensflare.png" );
|
|
if (!tex) Log::fatal("irr_driver", "Cannot find lens flare texture");
|
|
m_lensflare->setMaterialTexture(0, tex);
|
|
m_lensflare->setAutomaticCulling(scene::EAC_OFF);
|
|
|
|
m_suncam = m_scene_manager->addCameraSceneNode(0, vector3df(0), vector3df(0), -1, false);
|
|
m_suncam->grab();
|
|
m_suncam->setParent(NULL);
|
|
}
|
|
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 (!ProfileWorld::isNoGraphics())
|
|
{
|
|
#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*)"SuperTuxKart";
|
|
classhint->res_class = (char*)"SuperTuxKart";
|
|
XSetClassHint((Display*)videoData.OpenGLLinux.X11Display,
|
|
videoData.OpenGLLinux.X11Window,
|
|
classhint);
|
|
XFree(classhint);
|
|
#endif
|
|
m_device->setWindowCaption(L"SuperTuxKart");
|
|
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
|
|
m_post_processing = new PostProcessing(m_video_driver);
|
|
|
|
// 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::showPointer()
|
|
{
|
|
if (!m_pointer_shown)
|
|
{
|
|
m_pointer_shown = true;
|
|
this->getDevice()->getCursorControl()->setVisible(true);
|
|
}
|
|
} // showPointer
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void IrrDriver::hidePointer()
|
|
{
|
|
// always visible in artist debug mode, to be able to use the context menu
|
|
if (UserConfigParams::m_artist_debug_mode)
|
|
{
|
|
this->getDevice()->getCursorControl()->setVisible(true);
|
|
return;
|
|
}
|
|
|
|
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(const int x, const 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();
|
|
// TODO: Actually handle possible failure
|
|
XMoveWindow((Display*)videoData.OpenGLLinux.X11Display,
|
|
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,101,140));
|
|
GL32_draw2DRectangle( video::SColor(255, 0, 0, 0),
|
|
core::rect<s32>(0, 0,
|
|
UserConfigParams::m_prev_width,
|
|
UserConfigParams::m_prev_height) );
|
|
m_video_driver->endScene();
|
|
track_manager->removeAllCachedData();
|
|
attachment_manager->removeTextures();
|
|
projectile_manager->removeTextures();
|
|
ItemManager::removeTextures();
|
|
kart_properties_manager->unloadAllKarts();
|
|
powerup_manager->unloadPowerups();
|
|
Referee::cleanup();
|
|
ParticleKindManager::get()->cleanup();
|
|
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...)
|
|
|
|
// 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);
|
|
}
|
|
|
|
powerup_manager->loadAllPowerups ();
|
|
ItemManager::loadDefaultItemMeshes();
|
|
projectile_manager->loadData();
|
|
Referee::init();
|
|
GUIEngine::addLoadingIcon(
|
|
irr_driver->getTexture(file_manager->getAsset(FileManager::GUI,"gift.png")) );
|
|
|
|
file_manager->popTextureSearchPath();
|
|
|
|
kart_properties_manager->loadAllKarts();
|
|
|
|
attachment_manager->loadModels();
|
|
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();
|
|
|
|
} // 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
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Prints statistics about rendering, e.g. number of drawn and culled
|
|
* triangles etc. Note that printing this information will also slow
|
|
* down STK.
|
|
*/
|
|
void IrrDriver::printRenderStats()
|
|
{
|
|
io::IAttributes * attr = m_scene_manager->getParameters();
|
|
Log::verbose("irr_driver",
|
|
"[%ls], FPS:%3d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)\n",
|
|
m_video_driver->getName(),
|
|
m_video_driver->getFPS (),
|
|
(f32) m_video_driver->getPrimitiveCountDrawn( 0 ) * ( 1.f / 1000000.f ),
|
|
attr->getAttributeAsInt ( "culled" ),
|
|
attr->getAttributeAsInt ( "calls" ),
|
|
attr->getAttributeAsInt ( "drawn_solid" ),
|
|
attr->getAttributeAsInt ( "drawn_transparent" ),
|
|
attr->getAttributeAsInt ( "drawn_transparent_effect" )
|
|
);
|
|
|
|
} // printRenderStats
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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();
|
|
for(unsigned int j=0; j<video::MATERIAL_MAX_TEXTURES; j++)
|
|
{
|
|
video::ITexture* t=irr_material.getTexture(j);
|
|
if(t) material_manager->setAllMaterialFlags(t, mb);
|
|
|
|
} // for j<MATERIAL_MAX_TEXTURES
|
|
material_manager->setAllUntexturedMaterialFlags(mb);
|
|
} // for i<getMeshBufferCount()
|
|
} // setAllMaterialFlags
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Converts the mesh into a water scene node.
|
|
* \param mesh The mesh which is converted into a water scene node.
|
|
* \param wave_height Height of the water waves.
|
|
* \param wave_speed Speed of the water waves.
|
|
* \param wave_length Lenght of a water wave.
|
|
*/
|
|
scene::ISceneNode* IrrDriver::addWaterNode(scene::IMesh *mesh,
|
|
scene::IMesh **welded,
|
|
float wave_height,
|
|
float wave_speed,
|
|
float wave_length)
|
|
{
|
|
mesh->setMaterialFlag(video::EMF_GOURAUD_SHADING, true);
|
|
scene::IMesh* welded_mesh = m_scene_manager->getMeshManipulator()
|
|
->createMeshWelded(mesh);
|
|
scene::ISceneNode* out = NULL;
|
|
|
|
// TODO: using cand's new WaterNode would be better, but it does not
|
|
// support our material flags (like transparency, etc.)
|
|
//if (!m_glsl)
|
|
//{
|
|
out = m_scene_manager->addWaterSurfaceSceneNode(welded_mesh,
|
|
wave_height, wave_speed,
|
|
wave_length);
|
|
//} else
|
|
//{
|
|
// out = new WaterNode(m_scene_manager, welded_mesh, wave_height, wave_speed,
|
|
// wave_length);
|
|
//}
|
|
|
|
out->getMaterial(0).setFlag(video::EMF_GOURAUD_SHADING, true);
|
|
welded_mesh->drop(); // The scene node keeps a reference
|
|
|
|
*welded = welded_mesh;
|
|
|
|
return out;
|
|
} // addWaterNode
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Adds a mesh that will be optimised using an oct tree.
|
|
* \param mesh Mesh to add.
|
|
*/
|
|
scene::IMeshSceneNode *IrrDriver::addOctTree(scene::IMesh *mesh)
|
|
{
|
|
return m_scene_manager->addOctreeSceneNode(mesh);
|
|
} // addOctTree
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Adds a sphere with a given radius and color.
|
|
* \param radius The radius of the sphere.
|
|
* \param color The color to use (default (0,0,0,0)
|
|
*/
|
|
scene::IMeshSceneNode *IrrDriver::addSphere(float radius,
|
|
const video::SColor &color)
|
|
{
|
|
scene::IMeshSceneNode *node = m_scene_manager->addSphereSceneNode(radius);
|
|
node->setMaterialType(video::EMT_SOLID);
|
|
scene::IMesh *mesh = node->getMesh();
|
|
mesh->setMaterialFlag(video::EMF_COLOR_MATERIAL, true);
|
|
video::SMaterial m;
|
|
m.AmbientColor = color;
|
|
m.DiffuseColor = color;
|
|
m.EmissiveColor = color;
|
|
m.BackfaceCulling = false;
|
|
mesh->getMeshBuffer(0)->getMaterial() = m;
|
|
return node;
|
|
} // addSphere
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Adds a particle scene node.
|
|
*/
|
|
scene::IParticleSystemSceneNode *IrrDriver::addParticleNode(bool default_emitter)
|
|
{
|
|
return m_scene_manager->addParticleSystemSceneNode(default_emitter);
|
|
} // addParticleNode
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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,
|
|
scene::ISceneNode *parent)
|
|
{
|
|
if (!isGLSL())
|
|
return m_scene_manager->addMeshSceneNode(mesh, parent);
|
|
|
|
if (!parent)
|
|
parent = m_scene_manager->getRootSceneNode();
|
|
|
|
scene::IMeshSceneNode* node = new STKMeshSceneNode(mesh, parent, m_scene_manager, -1);
|
|
node->drop();
|
|
|
|
return node;
|
|
} // addMesh
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
PerCameraNode *IrrDriver::addPerCameraNode(scene::ISceneNode* node,
|
|
scene::ICameraSceneNode* camera,
|
|
scene::ISceneNode *parent)
|
|
{
|
|
return new PerCameraNode((parent ? parent
|
|
: m_scene_manager->getRootSceneNode()),
|
|
m_scene_manager, -1, camera, node);
|
|
} // addNode
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Adds a billboard node to scene.
|
|
*/
|
|
scene::ISceneNode *IrrDriver::addBillboard(const core::dimension2d< f32 > size,
|
|
video::ITexture *texture,
|
|
scene::ISceneNode* parent, bool alphaTesting)
|
|
{
|
|
scene::IBillboardSceneNode* node;
|
|
if (isGLSL())
|
|
{
|
|
if (!parent)
|
|
parent = m_scene_manager->getRootSceneNode();
|
|
|
|
node = new STKBillboard(parent, m_scene_manager, -1, vector3df(0., 0., 0.), size);
|
|
node->drop();
|
|
}
|
|
else
|
|
node = m_scene_manager->addBillboardSceneNode(parent, size);
|
|
assert(node->getMaterialCount() > 0);
|
|
node->setMaterialTexture(0, texture);
|
|
if(alphaTesting)
|
|
node->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
|
|
return node;
|
|
} // addMesh
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Creates a quad mesh with a given material.
|
|
* \param material The material to use (NULL if no material).
|
|
* \param create_one_quad If true creates one quad in the mesh.
|
|
*/
|
|
scene::IMesh *IrrDriver::createQuadMesh(const video::SMaterial *material,
|
|
bool create_one_quad)
|
|
{
|
|
scene::SMeshBuffer *buffer = new scene::SMeshBuffer();
|
|
if(create_one_quad)
|
|
{
|
|
video::S3DVertex v;
|
|
v.Pos = core::vector3df(0,0,0);
|
|
v.Normal = core::vector3df(1/sqrt(2.0f), 1/sqrt(2.0f), 0);
|
|
|
|
// Add the vertices
|
|
// ----------------
|
|
buffer->Vertices.push_back(v);
|
|
buffer->Vertices.push_back(v);
|
|
buffer->Vertices.push_back(v);
|
|
buffer->Vertices.push_back(v);
|
|
|
|
// Define the indices for the triangles
|
|
// ------------------------------------
|
|
buffer->Indices.push_back(0);
|
|
buffer->Indices.push_back(1);
|
|
buffer->Indices.push_back(2);
|
|
|
|
buffer->Indices.push_back(0);
|
|
buffer->Indices.push_back(2);
|
|
buffer->Indices.push_back(3);
|
|
}
|
|
if(material)
|
|
buffer->Material = *material;
|
|
scene::SMesh *mesh = new scene::SMesh();
|
|
mesh->addMeshBuffer(buffer);
|
|
mesh->recalculateBoundingBox();
|
|
buffer->drop();
|
|
return mesh;
|
|
} // createQuadMesh
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Creates a quad mesh buffer with a given width and height (z coordinate is
|
|
* 0).
|
|
* \param material The material to use for this quad.
|
|
* \param w Width of the quad.
|
|
* \param h Height of the quad.
|
|
*/
|
|
scene::IMesh *IrrDriver::createTexturedQuadMesh(const video::SMaterial *material,
|
|
const double w, const double h)
|
|
{
|
|
scene::SMeshBuffer *buffer = new scene::SMeshBuffer();
|
|
|
|
const float w_2 = (float)w/2.0f;
|
|
const float h_2 = (float)h/2.0f;
|
|
|
|
video::S3DVertex v1;
|
|
v1.Pos = core::vector3df(-w_2,-h_2,0);
|
|
v1.Normal = core::vector3df(0, 0, -1.0f);
|
|
v1.TCoords = core::vector2d<f32>(1,1);
|
|
|
|
video::S3DVertex v2;
|
|
v2.Pos = core::vector3df(w_2,-h_2,0);
|
|
v2.Normal = core::vector3df(0, 0, -1.0f);
|
|
v2.TCoords = core::vector2d<f32>(0,1);
|
|
|
|
video::S3DVertex v3;
|
|
v3.Pos = core::vector3df(w_2,h_2,0);
|
|
v3.Normal = core::vector3df(0, 0, -1.0f);
|
|
v3.TCoords = core::vector2d<f32>(0,0);
|
|
|
|
video::S3DVertex v4;
|
|
v4.Pos = core::vector3df(-w_2,h_2,0);
|
|
v4.Normal = core::vector3df(0, 0, -1.0f);
|
|
v4.TCoords = core::vector2d<f32>(1,0);
|
|
|
|
|
|
// Add the vertices
|
|
// ----------------
|
|
buffer->Vertices.push_back(v1);
|
|
buffer->Vertices.push_back(v2);
|
|
buffer->Vertices.push_back(v3);
|
|
buffer->Vertices.push_back(v4);
|
|
|
|
// Define the indices for the triangles
|
|
// ------------------------------------
|
|
buffer->Indices.push_back(0);
|
|
buffer->Indices.push_back(1);
|
|
buffer->Indices.push_back(2);
|
|
|
|
buffer->Indices.push_back(0);
|
|
buffer->Indices.push_back(2);
|
|
buffer->Indices.push_back(3);
|
|
|
|
if (material) buffer->Material = *material;
|
|
scene::SMesh *mesh = new scene::SMesh();
|
|
mesh->addMeshBuffer(buffer);
|
|
mesh->recalculateBoundingBox();
|
|
buffer->drop();
|
|
return mesh;
|
|
} // createQuadMesh
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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 an animated mesh to the scene.
|
|
* \param mesh The animated mesh to add.
|
|
*/
|
|
scene::IAnimatedMeshSceneNode *IrrDriver::addAnimatedMesh(scene::IAnimatedMesh *mesh, scene::ISceneNode* parent)
|
|
{
|
|
if (!isGLSL())
|
|
return m_scene_manager->addAnimatedMeshSceneNode(mesh, parent, -1,
|
|
core::vector3df(0,0,0),
|
|
core::vector3df(0,0,0),
|
|
core::vector3df(1,1,1),
|
|
/*addIfMeshIsZero*/true);
|
|
|
|
if (!parent)
|
|
parent = m_scene_manager->getRootSceneNode();
|
|
scene::IAnimatedMeshSceneNode* node =
|
|
new STKAnimatedMesh(mesh, parent, m_scene_manager, -1, core::vector3df(0,0,0), core::vector3df(0,0,0), core::vector3df(1,1,1));
|
|
node->drop();
|
|
return node;
|
|
} // addAnimatedMesh
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Adds a sky dome. Documentation from irrlicht:
|
|
* A skydome is a large (half-) sphere with a panoramic texture on the inside
|
|
* and is drawn around the camera position.
|
|
* \param texture: Texture for the dome.
|
|
* \param horiRes: Number of vertices of a horizontal layer of the sphere.
|
|
* \param vertRes: Number of vertices of a vertical layer of the sphere.
|
|
* \param texturePercentage: How much of the height of the texture is used.
|
|
* Should be between 0 and 1.
|
|
* \param spherePercentage: How much of the sphere is drawn. Value should be
|
|
* between 0 and 2, where 1 is an exact half-sphere and 2 is a full
|
|
* sphere.
|
|
*/
|
|
scene::ISceneNode *IrrDriver::addSkyDome(video::ITexture *texture,
|
|
int hori_res, int vert_res,
|
|
float texture_percent,
|
|
float sphere_percent)
|
|
{
|
|
Log::error("skybox", "Using deprecated SkyDome");
|
|
return m_scene_manager->addSkyDomeSceneNode(texture, hori_res, vert_res,
|
|
texture_percent,
|
|
sphere_percent);
|
|
} // addSkyDome
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Adds a skybox using. Irrlicht documentation:
|
|
* A skybox is a big cube with 6 textures on it and is drawn around the camera
|
|
* position.
|
|
* \param top: Texture for the top plane of the box.
|
|
* \param bottom: Texture for the bottom plane of the box.
|
|
* \param left: Texture for the left plane of the box.
|
|
* \param right: Texture for the right plane of the box.
|
|
* \param front: Texture for the front plane of the box.
|
|
* \param back: Texture for the back plane of the box.
|
|
*/
|
|
scene::ISceneNode *IrrDriver::addSkyBox(const std::vector<video::ITexture*> &texture,
|
|
const std::vector<video::ITexture*> &sphericalHarmonics)
|
|
{
|
|
assert(texture.size() == 6);
|
|
SkyboxTextures = texture;
|
|
SphericalHarmonicsTextures = sphericalHarmonics;
|
|
SkyboxCubeMap = 0;
|
|
return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1],
|
|
texture[2], texture[3],
|
|
texture[4], texture[5]);
|
|
} // addSkyBox
|
|
|
|
void IrrDriver::suppressSkyBox()
|
|
{
|
|
SkyboxTextures.clear();
|
|
SphericalHarmonicsTextures.clear();
|
|
if (SkyboxCubeMap)
|
|
glDeleteTextures(1, &SkyboxCubeMap);
|
|
SkyboxCubeMap = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Sets an error message to be displayed when a texture is not found. This
|
|
* error message is shown before the "Texture '%s' not found" message. It can
|
|
* be used to supply additional details like what kart is currently being
|
|
* loaded.
|
|
* \param error Error message, potentially with a '%' which will be replaced
|
|
* with detail.
|
|
* \param detail String to replace a '%' in the error message.
|
|
*/
|
|
void IrrDriver::setTextureErrorMessage(const std::string &error,
|
|
const std::string &detail)
|
|
{
|
|
if(detail=="")
|
|
m_texture_error_message = error;
|
|
else
|
|
m_texture_error_message = StringUtils::insertValues(error, detail);
|
|
} // setTextureErrorMessage
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Disables the texture error message again.
|
|
*/
|
|
void IrrDriver::unsetTextureErrorMessage()
|
|
{
|
|
m_texture_error_message = "";
|
|
} // unsetTextureErrorMessage
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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.
|
|
*/
|
|
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)
|
|
{
|
|
|
|
if(m_texture_error_message.size()>0)
|
|
{
|
|
Log::error("irr_driver", m_texture_error_message.c_str());
|
|
}
|
|
Log::error("irr_driver", "Texture '%s' not found.", filename.c_str());
|
|
}
|
|
|
|
return out;
|
|
} // getTexture
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Sets the ambient light.
|
|
* \param light The colour of the light to set.
|
|
*/
|
|
void IrrDriver::setAmbientLight(const video::SColor &light)
|
|
{
|
|
m_scene_manager->setAmbientLight(light);
|
|
} // setAmbientLight
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Displays the FPS on the screen.
|
|
*/
|
|
void IrrDriver::displayFPS()
|
|
{
|
|
gui::IGUIFont* font = GUIEngine::getSmallFont();
|
|
|
|
if(UserConfigParams::m_artist_debug_mode)
|
|
{
|
|
GL32_draw2DRectangle(video::SColor(150, 96, 74, 196),core::rect< s32 >(75,0,1100,40),NULL);
|
|
}
|
|
else
|
|
{
|
|
GL32_draw2DRectangle(video::SColor(150, 96, 74, 196),core::rect< s32 >(75,0,900,40),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, 0, 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;
|
|
|
|
static char buffer[128];
|
|
|
|
if (UserConfigParams::m_artist_debug_mode)
|
|
{
|
|
sprintf(buffer, "FPS: %i/%i/%i - Objects (P1:%d P2:%d T:%d) KTris - LightDst : ~%d",
|
|
min, fps, max, object_count[SOLID_NORMAL_AND_DEPTH_PASS], object_count[SOLID_NORMAL_AND_DEPTH_PASS], object_count[TRANSPARENT_PASS], m_last_light_bucket_distance);
|
|
object_count[SOLID_NORMAL_AND_DEPTH_PASS] = 0;
|
|
object_count[SOLID_NORMAL_AND_DEPTH_PASS] = 0;
|
|
object_count[TRANSPARENT_PASS] = 0;
|
|
}
|
|
else
|
|
{
|
|
sprintf(buffer, "FPS: %i/%i/%i - %i KTris", min, fps, max,
|
|
(int)roundf(kilotris));
|
|
}
|
|
|
|
core::stringw fpsString = buffer;
|
|
|
|
static video::SColor fpsColor = video::SColor(255, 0, 0, 0);
|
|
|
|
font->draw( fpsString.c_str(), core::rect< s32 >(100,0,400,50), fpsColor, false );
|
|
} // updateFPS
|
|
|
|
// ----------------------------------------------------------------------------
|
|
#ifdef DEBUG
|
|
void IrrDriver::drawDebugMeshes()
|
|
{
|
|
for (unsigned int n=0; n<m_debug_meshes.size(); n++)
|
|
{
|
|
scene::IMesh* mesh = m_debug_meshes[n]->getMesh();
|
|
scene::ISkinnedMesh* smesh = static_cast<scene::ISkinnedMesh*>(mesh);
|
|
const core::array< scene::ISkinnedMesh::SJoint * >& joints =
|
|
smesh->getAllJoints();
|
|
|
|
for (unsigned int j=0; j<joints.size(); j++)
|
|
{
|
|
drawJoint( false, true, joints[j], smesh, j);
|
|
}
|
|
}
|
|
|
|
video::SColor color(255,255,255,255);
|
|
video::SMaterial material;
|
|
material.Thickness = 2;
|
|
material.AmbientColor = color;
|
|
material.DiffuseColor = color;
|
|
material.EmissiveColor= color;
|
|
material.BackfaceCulling = false;
|
|
material.setFlag(video::EMF_LIGHTING, false);
|
|
getVideoDriver()->setMaterial(material);
|
|
getVideoDriver()->setTransform(video::ETS_WORLD, core::IdentityMatrix);
|
|
|
|
for (unsigned int n=0; n<m_debug_meshes.size(); n++)
|
|
{
|
|
scene::IMesh* mesh = m_debug_meshes[n]->getMesh();
|
|
|
|
|
|
scene::ISkinnedMesh* smesh = static_cast<scene::ISkinnedMesh*>(mesh);
|
|
const core::array< scene::ISkinnedMesh::SJoint * >& joints =
|
|
smesh->getAllJoints();
|
|
|
|
for (unsigned int j=0; j<joints.size(); j++)
|
|
{
|
|
scene::IMesh* mesh = m_debug_meshes[n]->getMesh();
|
|
scene::ISkinnedMesh* smesh = static_cast<scene::ISkinnedMesh*>(mesh);
|
|
|
|
drawJoint(true, false, joints[j], smesh, j);
|
|
}
|
|
}
|
|
} // drawDebugMeshes
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Draws a joing for debugging skeletons.
|
|
* \param drawline If true draw a line to the parent.
|
|
* \param drawname If true draw the name of the joint.
|
|
* \param joint The joint to draw.
|
|
* \param mesh The mesh whose skeleton is drawn (only used to get
|
|
* all joints to find the parent).
|
|
* \param id Index, which (%4) determines the color to use.
|
|
*/
|
|
void IrrDriver::drawJoint(bool drawline, bool drawname,
|
|
scene::ISkinnedMesh::SJoint* joint,
|
|
scene::ISkinnedMesh* mesh, int id)
|
|
{
|
|
scene::ISkinnedMesh::SJoint* parent = NULL;
|
|
const core::array< scene::ISkinnedMesh::SJoint * >& joints
|
|
= mesh->getAllJoints();
|
|
for (unsigned int j=0; j<joints.size(); j++)
|
|
{
|
|
if (joints[j]->Children.linear_search(joint) != -1)
|
|
{
|
|
parent = joints[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
core::vector3df jointpos = joint->GlobalMatrix.getTranslation();
|
|
|
|
video::SColor color(255, 255,255,255);
|
|
if (parent == NULL) color = video::SColor(255,0,255,0);
|
|
|
|
switch (id % 4)
|
|
{
|
|
case 0:
|
|
color = video::SColor(255,255,0,255);
|
|
break;
|
|
case 1:
|
|
color = video::SColor(255,255,0,0);
|
|
break;
|
|
case 2:
|
|
color = video::SColor(255,0,0,255);
|
|
break;
|
|
case 3:
|
|
color = video::SColor(255,0,255,255);
|
|
break;
|
|
}
|
|
|
|
|
|
if (parent)
|
|
{
|
|
core::vector3df parentpos = parent->GlobalMatrix.getTranslation();
|
|
|
|
jointpos = joint->GlobalMatrix.getTranslation();
|
|
|
|
if (drawline)
|
|
{
|
|
irr_driver->getVideoDriver()->draw3DLine(jointpos,
|
|
parentpos,
|
|
color);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
if (drawline)
|
|
{
|
|
irr_driver->getVideoDriver()->draw3DLine(jointpos,
|
|
core::vector3df(0,0,0),
|
|
color);
|
|
}
|
|
*/
|
|
}
|
|
|
|
if (joint->Children.size() == 0)
|
|
{
|
|
switch ((id + 1) % 4)
|
|
{
|
|
case 0:
|
|
color = video::SColor(255,255,0,255);
|
|
break;
|
|
case 1:
|
|
color = video::SColor(255,255,0,0);
|
|
break;
|
|
case 2:
|
|
color = video::SColor(255,0,0,255);
|
|
break;
|
|
case 3:
|
|
color = video::SColor(255,0,255,255);
|
|
break;
|
|
}
|
|
|
|
// This code doesn't quite work. 0.25 is used so that the bone is not
|
|
// way too long (not sure why I need to manually size it down)
|
|
// and the rotation of the bone is often rather off
|
|
core::vector3df v(0.0f, 0.25f, 0.0f);
|
|
//joint->GlobalMatrix.rotateVect(v);
|
|
joint->LocalMatrix.rotateVect(v);
|
|
v *= joint->LocalMatrix.getScale();
|
|
irr_driver->getVideoDriver()->draw3DLine(jointpos,
|
|
jointpos + v,
|
|
color);
|
|
}
|
|
|
|
switch ((id + 1) % 4)
|
|
{
|
|
case 0:
|
|
color = video::SColor(255,255,0,255);
|
|
break;
|
|
case 1:
|
|
color = video::SColor(255,255,0,0);
|
|
break;
|
|
case 2:
|
|
color = video::SColor(255,0,0,255);
|
|
break;
|
|
case 3:
|
|
color = video::SColor(255,0,255,255);
|
|
break;
|
|
}
|
|
|
|
if (drawname)
|
|
{
|
|
irr_driver->getVideoDriver()->setTransform(video::ETS_WORLD,
|
|
core::IdentityMatrix);
|
|
|
|
core::vector2di textpos =
|
|
irr_driver->getSceneManager()->getSceneCollisionManager()
|
|
->getScreenCoordinatesFrom3DPosition(jointpos);
|
|
|
|
GUIEngine::getSmallFont()->draw( stringw(joint->Name.c_str()),
|
|
core::rect<s32>(textpos,
|
|
core::dimension2d<s32>(500,50)),
|
|
color, false, false );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Requess a screenshot from irrlicht, and save it in a file.
|
|
*/
|
|
void IrrDriver::doScreenShot()
|
|
{
|
|
m_request_screenshot = false;
|
|
|
|
video::IImage* image = m_video_driver->createScreenShot();
|
|
if(!image)
|
|
{
|
|
Log::error("IrrDriver", "Could not create screen shot.");
|
|
return;
|
|
}
|
|
|
|
// Screenshot was successful.
|
|
time_t rawtime;
|
|
time ( &rawtime );
|
|
tm* timeInfo = localtime( &rawtime );
|
|
char time_buffer[256];
|
|
sprintf(time_buffer, "%i.%02i.%02i_%02i.%02i.%02i",
|
|
timeInfo->tm_year + 1900, timeInfo->tm_mon+1,
|
|
timeInfo->tm_mday, timeInfo->tm_hour,
|
|
timeInfo->tm_min, timeInfo->tm_sec);
|
|
|
|
std::string track_name = race_manager->getTrackName();
|
|
if (World::getWorld() == NULL) track_name = "menu";
|
|
std::string path = file_manager->getScreenshotDir()+track_name+"-"+time_buffer+".png";
|
|
|
|
if (irr_driver->getVideoDriver()->writeImageToFile(image, path.c_str(), 0))
|
|
{
|
|
RaceGUIBase* base = World::getWorld()
|
|
? World::getWorld()->getRaceGUI()
|
|
: NULL;
|
|
if (base)
|
|
{
|
|
base->addMessage(
|
|
core::stringw(("Screenshot saved to\n" + path).c_str()),
|
|
NULL, 2.0f, video::SColor(255,255,255,255), true, false);
|
|
} // if base
|
|
}
|
|
else
|
|
{
|
|
RaceGUIBase* base = World::getWorld()->getRaceGUI();
|
|
if (base)
|
|
{
|
|
base->addMessage(
|
|
core::stringw(("FAILED saving screenshot to\n" + path +
|
|
"\n:(").c_str()),
|
|
NULL, 2.0f, video::SColor(255,255,255,255),
|
|
true, false);
|
|
} // if base
|
|
} // if failed writing screenshot file
|
|
image->drop();
|
|
} // doScreenShot
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Update, called once per frame.
|
|
* \param dt Time since last update
|
|
*/
|
|
void IrrDriver::update(float dt)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
m_wind->update();
|
|
|
|
World *world = World::getWorld();
|
|
|
|
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(0,0,0,255));
|
|
//m_scene_manager->drawAll();
|
|
|
|
if (m_glsl)
|
|
renderGLSL(dt);
|
|
else
|
|
renderFixed(dt);
|
|
|
|
GUIEngine::render(dt);
|
|
//m_video_driver->endScene();
|
|
return;
|
|
}
|
|
else if (!world)
|
|
{
|
|
m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true,
|
|
video::SColor(255,100,101,140));
|
|
|
|
GUIEngine::render(dt);
|
|
|
|
m_video_driver->endScene();
|
|
return;
|
|
}
|
|
|
|
if (m_glsl)
|
|
renderGLSL(dt);
|
|
else
|
|
renderFixed(dt);
|
|
|
|
if (m_request_screenshot) doScreenShot();
|
|
|
|
// Enable this next print statement to get render information printed
|
|
// E.g. number of triangles rendered, culled etc. The stats is only
|
|
// printed while the race is running and not while the in-game menu
|
|
// is shown. This way the output can be studied by just opening the
|
|
// menu.
|
|
//if(World::getWorld() && World::getWorld()->isRacePhase())
|
|
// printRenderStats();
|
|
} // update
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void IrrDriver::requestScreenshot()
|
|
{
|
|
m_request_screenshot = true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool IrrDriver::supportsSplatting()
|
|
{
|
|
return m_glsl;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if 0
|
|
#pragma mark -
|
|
#pragma mark RTT
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** 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
|
|
|
|
// ----------------------------------------------------------------------------
|
|
IrrDriver::RTTProvider::~RTTProvider()
|
|
{
|
|
tearDownRTTScene();
|
|
|
|
if (!m_persistent_texture)
|
|
irr_driver->removeTexture(m_render_target_texture);
|
|
} // ~RTTProvider
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Sets up a given vector of meshes for render-to-texture. Ideal to embed a 3D
|
|
* object inside the GUI. If there are multiple meshes, the first mesh is
|
|
* considered to be the root, and all following meshes will have their
|
|
* locations relative to the location of the first mesh.
|
|
*/
|
|
void IrrDriver::RTTProvider::setupRTTScene(PtrVector<scene::IMesh, REF>& mesh,
|
|
AlignedArray<Vec3>& mesh_location,
|
|
AlignedArray<Vec3>& mesh_scale,
|
|
const std::vector<int>& model_frames)
|
|
{
|
|
if (model_frames[0] == -1)
|
|
{
|
|
scene::ISceneNode* node =
|
|
irr_driver->getSceneManager()->addMeshSceneNode(mesh.get(0), NULL);
|
|
node->setPosition( mesh_location[0].toIrrVector() );
|
|
node->setScale( mesh_scale[0].toIrrVector() );
|
|
node->setMaterialFlag(video::EMF_FOG_ENABLE, false);
|
|
m_rtt_main_node = node;
|
|
}
|
|
else
|
|
{
|
|
scene::IAnimatedMeshSceneNode* node =
|
|
irr_driver->getSceneManager()->addAnimatedMeshSceneNode(
|
|
(scene::IAnimatedMesh*)mesh.get(0), NULL );
|
|
node->setPosition( mesh_location[0].toIrrVector() );
|
|
node->setFrameLoop(model_frames[0], model_frames[0]);
|
|
node->setAnimationSpeed(0);
|
|
node->setScale( mesh_scale[0].toIrrVector() );
|
|
node->setMaterialFlag(video::EMF_FOG_ENABLE, false);
|
|
|
|
m_rtt_main_node = node;
|
|
}
|
|
|
|
assert(m_rtt_main_node != NULL);
|
|
assert(mesh.size() == mesh_location.size());
|
|
assert(mesh.size() == model_frames.size());
|
|
|
|
const int mesh_amount = mesh.size();
|
|
for (int n=1; n<mesh_amount; n++)
|
|
{
|
|
if (model_frames[n] == -1)
|
|
{
|
|
scene::ISceneNode* node =
|
|
irr_driver->getSceneManager()->addMeshSceneNode(mesh.get(n),
|
|
m_rtt_main_node);
|
|
node->setPosition( mesh_location[n].toIrrVector() );
|
|
node->updateAbsolutePosition();
|
|
node->setScale( mesh_scale[n].toIrrVector() );
|
|
}
|
|
else
|
|
{
|
|
scene::IAnimatedMeshSceneNode* node =
|
|
irr_driver->getSceneManager()
|
|
->addAnimatedMeshSceneNode((scene::IAnimatedMesh*)mesh.get(n),
|
|
m_rtt_main_node );
|
|
node->setPosition( mesh_location[n].toIrrVector() );
|
|
node->setFrameLoop(model_frames[n], model_frames[n]);
|
|
node->setAnimationSpeed(0);
|
|
node->updateAbsolutePosition();
|
|
node->setScale( mesh_scale[n].toIrrVector() );
|
|
//std::cout << "(((( set frame " << model_frames[n] << " ))))\n";
|
|
}
|
|
}
|
|
|
|
irr_driver->getSceneManager()->setAmbientLight(video::SColor(255, 35, 35, 35) );
|
|
|
|
const core::vector3df &spot_pos = core::vector3df(0, 30, 40);
|
|
m_light = irr_driver->getSceneManager()
|
|
->addLightSceneNode(NULL, spot_pos, video::SColorf(1.0f,1.0f,1.0f),
|
|
1600 /* radius */);
|
|
m_light->setLightType(video::ELT_SPOT);
|
|
m_light->setRotation((core::vector3df(0, 10, 0) - spot_pos).getHorizontalAngle());
|
|
m_light->updateAbsolutePosition();
|
|
|
|
m_rtt_main_node->setMaterialFlag(video::EMF_GOURAUD_SHADING , true);
|
|
m_rtt_main_node->setMaterialFlag(video::EMF_LIGHTING, true);
|
|
|
|
const int materials = m_rtt_main_node->getMaterialCount();
|
|
for (int n=0; n<materials; n++)
|
|
{
|
|
m_rtt_main_node->getMaterial(n).setFlag(video::EMF_LIGHTING, true);
|
|
|
|
// set size of specular highlights
|
|
m_rtt_main_node->getMaterial(n).Shininess = 100.0f;
|
|
m_rtt_main_node->getMaterial(n).SpecularColor.set(255,50,50,50);
|
|
m_rtt_main_node->getMaterial(n).DiffuseColor.set(255,150,150,150);
|
|
|
|
m_rtt_main_node->getMaterial(n).setFlag(video::EMF_GOURAUD_SHADING ,
|
|
true);
|
|
}
|
|
|
|
m_camera = irr_driver->getSceneManager()->addCameraSceneNode();
|
|
|
|
m_camera->setPosition( core::vector3df(0.0, 20.0f, 70.0f) );
|
|
if (irr_driver->isGLSL())
|
|
m_camera->setUpVector( core::vector3df(0.0, 1.0, 0.0) );
|
|
else
|
|
m_camera->setUpVector( core::vector3df(0.0, 1.0, 0.0) );
|
|
m_camera->setTarget( core::vector3df(0, 10, 0.0f) );
|
|
m_camera->setFOV( DEGREE_TO_RAD*50.0f );
|
|
m_camera->updateAbsolutePosition();
|
|
|
|
// Detach the note from the scene so we can render it independently
|
|
m_rtt_main_node->setVisible(false);
|
|
m_light->setVisible(false);
|
|
} // setupRTTScene
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void IrrDriver::RTTProvider::tearDownRTTScene()
|
|
{
|
|
//if (m_rtt_main_node != NULL) m_rtt_main_node->drop();
|
|
if (m_rtt_main_node != NULL) m_rtt_main_node->remove();
|
|
if (m_light != NULL) m_light->remove();
|
|
if (m_camera != NULL) m_camera->remove();
|
|
|
|
m_rtt_main_node = NULL;
|
|
m_camera = NULL;
|
|
m_light = NULL;
|
|
} // tearDownRTTScene
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/**
|
|
* 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 (!m_glsl)
|
|
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 = rimlit ? m_shaders->getShader(ES_OBJECTPASS_RIMLIT):
|
|
m_shaders->getShader(ES_OBJECTPASS_REF);
|
|
const video::E_MATERIAL_TYPE pass = rimlit ? m_shaders->getShader(ES_OBJECTPASS_RIMLIT):
|
|
m_shaders->getShader(ES_OBJECTPASS);
|
|
|
|
const video::E_MATERIAL_TYPE origref = m_shaders->getShader(ES_OBJECTPASS_REF);
|
|
const video::E_MATERIAL_TYPE origpass = m_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 (!m_glsl)
|
|
return;
|
|
|
|
applyObjectPassShader(m_scene_manager->getRootSceneNode());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos, float energy,
|
|
float r, float g, float b, bool sun, scene::ISceneNode* parent)
|
|
{
|
|
if (m_glsl)
|
|
{
|
|
if (parent == NULL) parent = m_scene_manager->getRootSceneNode();
|
|
LightNode *light = NULL;
|
|
|
|
if (!sun)
|
|
light = new LightNode(m_scene_manager, parent, energy, r, g, b);
|
|
else
|
|
light = new SunNode(m_scene_manager, parent, r, g, b);
|
|
|
|
light->grab();
|
|
|
|
light->setPosition(pos);
|
|
light->updateAbsolutePosition();
|
|
|
|
m_lights.push_back(light);
|
|
|
|
if (sun)
|
|
{
|
|
m_sun_interposer->setPosition(pos);
|
|
m_sun_interposer->updateAbsolutePosition();
|
|
|
|
m_lensflare->setPosition(pos);
|
|
m_lensflare->updateAbsolutePosition();
|
|
|
|
m_suncam->setPosition(pos);
|
|
m_suncam->updateAbsolutePosition();
|
|
|
|
((WaterShaderProvider *) m_shaders->m_callbacks[ES_WATER])->setSunPosition(pos);
|
|
((SkyboxProvider *) m_shaders->m_callbacks[ES_SKYBOX])->setSunPosition(pos);
|
|
}
|
|
|
|
return light;
|
|
}
|
|
else
|
|
{
|
|
return m_scene_manager->addLightSceneNode(m_scene_manager->getRootSceneNode(),
|
|
pos, video::SColorf(1.0f, r, g, b));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void IrrDriver::clearLights()
|
|
{
|
|
u32 i;
|
|
const u32 max = m_lights.size();
|
|
for (i = 0; i < max; i++)
|
|
{
|
|
m_lights[i]->drop();
|
|
}
|
|
|
|
m_lights.clear();
|
|
}
|