hikerstk fdd4739d94 Added addSphere to irr_driver to make debugging code a bit more flexible.
git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11655 178a84e3-b1eb-0310-8ba1-8eac791a3b58
2012-10-01 21:53:40 +00:00

1961 lines
71 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2009 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/camera.hpp"
#include "graphics/hardware_skinning.hpp"
#include "graphics/material_manager.hpp"
#include "graphics/particle_kind_manager.hpp"
#include "graphics/per_camera_node.hpp"
#include "graphics/referee.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 "utils/constants.hpp"
#include "utils/profiler.hpp"
#include <irrlicht.h>
using namespace irr::core;
using namespace irr::scene;
using namespace irr::video;
#ifndef round
# define round(x) (floor(x+0.5f))
#endif
#ifdef WIN32
#include <Windows.h>
#endif
#if defined(__linux__) && !defined(ANDROID)
namespace X11
{
#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_device = createDevice(video::EDT_NULL);
m_request_screenshot = false;
} // IrrDriver
// ----------------------------------------------------------------------------
/** Destructor - removes the irrlicht device.
*/
IrrDriver::~IrrDriver()
{
m_post_processing.shut();
assert(m_device != NULL);
m_device->drop();
m_device = NULL;
m_modes.clear();
} // ~IrrDriver
// ----------------------------------------------------------------------------
#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.
*/
X11::Window get_toplevel_parent(X11::Display* display, X11::Window window)
{
X11::Window parent;
X11::Window root;
X11::Window * children;
unsigned int num_children;
while (true)
{
if (0 == X11::XQueryTree(display, window, &root,
&parent, &children, &num_children))
{
fprintf(stderr, "XQueryTree error\n");
abort(); //change to whatever error handling you prefer
}
if (children) { //must test for null
X11::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 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;
printf("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
{
printf("Could not retrieve window location\n");
}
#elif defined(__linux__) && !defined(ANDROID)
using namespace X11;
const 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;
printf("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())
{
printf("!!!!! Performance warning: Irrlicht compiled with debug mode.!!!!!\n");
printf("!!!!! 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)
{
fprintf(stderr, "[IrrDriver] 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;
int num_drivers = 5;
// Test if user has chosen a driver or if we should try all to find
// a working one.
if( UserConfigParams::m_renderer != 0 ) num_drivers = 1;
// ---- open device
// Try different drivers: start with opengl, then DirectX
for(int driver_type=0; driver_type<num_drivers; driver_type++)
{
video::E_DRIVER_TYPE type;
// Test if user has chosen a driver or if we should try all to find a
// woring one.
type = getEngineDriverType(
UserConfigParams::m_renderer ? UserConfigParams::m_renderer
: driver_type );
// Try 32 and, upon failure, 24 then 16 bit per pixels
for (int bits=32; bits>15; bits -=8)
{
if(UserConfigParams::logMisc())
printf("[IrrDriver] Trying to create device with "
"%i bits\n", bits);
SIrrlichtCreationParameters params;
params.DriverType = type;
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:
fprintf(stderr, "[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(m_device) break;
} // for edt_types
// 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)
{
fprintf(stderr, "An invalid resolution was set in the config file, reverting to saner values\n");
}
}
}
if(!m_device)
{
fprintf(stderr, "Couldn't initialise irrlicht device. Quitting.\n");
exit(-1);
}
m_scene_manager = m_device->getSceneManager();
m_gui_env = m_device->getGUIEnvironment();
m_video_driver = m_device->getVideoDriver();
// 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.
using namespace X11;
const 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->setResizable(false);
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);
if (!UserConfigParams::m_fbo)
{
m_device->getVideoDriver()->disableFeature(EVDF_FRAMEBUFFER_OBJECT);
}
// 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
// Stores the new file system pointer.
file_manager->reInit();
// Initialize material2D
video::SMaterial& material2D = m_video_driver->getMaterial2D();
material2D.setFlag(video::EMF_ANTI_ALIASING, true);
for (unsigned int n=0; n<MATERIAL_MAX_TEXTURES; n++)
{
material2D.TextureLayer[n].BilinearFilter = false;
material2D.TextureLayer[n].TrilinearFilter = true;
//material2D.TextureLayer[n].TextureWrap = ETC_CLAMP_TO_EDGE;
material2D.TextureLayer[n].TextureWrapU = ETC_CLAMP_TO_EDGE;
material2D.TextureLayer[n].TextureWrapV = 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.init(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
//-----------------------------------------------------------------------------
video::E_DRIVER_TYPE IrrDriver::getEngineDriverType( int index )
{
video::E_DRIVER_TYPE type;
std::string rendererName = "";
// Choose the driver type.
switch(index)
{
// TODO Change default renderer dependen on operating system?
// Direct3D9 for Windows and OpenGL for Unix like systems?
case 0:
type = video::EDT_OPENGL;
rendererName = "OpenGL";
break;
case 1:
type = video::EDT_OPENGL;
rendererName = "OpenGL";
break;
case 2:
type = video::EDT_DIRECT3D9;
rendererName = "Direct3D9";
break;
case 3:
type = video::EDT_DIRECT3D8;
rendererName = "Direct3D8";
break;
case 4:
type = video::EDT_SOFTWARE;
rendererName = "Software";
break;
case 5:
type = video::EDT_BURNINGSVIDEO;
rendererName = "Burning's Video Software";
break;
default:
type = video::EDT_NULL;
rendererName = "Null Device";
}
// Ouput which render will be tried.
printf("[IrrDriver] Trying %s rendering.\n", rendererName.c_str());
return type;
}
//-----------------------------------------------------------------------------
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(const int x, const int y)
{
const SExposedVideoData& videoData = m_video_driver->getExposedVideoData();
#ifdef WIN32
// 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
{
printf("[IrrDriver] WARNING: Could not set window location\n");
return false;
}
#elif defined(__linux__) && !defined(ANDROID)
using namespace X11;
// 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));
m_video_driver->draw2DRectangle( SColor(255, 0, 0, 0),
core::rect<s32>(0, 0,
UserConfigParams::m_prev_width,
UserConfigParams::m_prev_height) );
m_video_driver->endScene();
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->getGUIDir()+"/options_video.png")
);
file_manager->pushTextureSearchPath(file_manager->getModelFile(""));
const std::string materials_file =
file_manager->getModelFile("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->getGUIDir() + "/gift.png") );
file_manager->popTextureSearchPath();
KartPropertiesManager::addKartSearchDir(file_manager->getAddonsFile("karts"));
kart_properties_manager -> loadAllKarts();
attachment_manager -> loadModels();
GUIEngine::addLoadingIcon( irr_driver->getTexture(file_manager->getGUIDir() + "/banana.png") );
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();
printf("[%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))
{
fprintf(stderr, "[IrrDriver::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)
{
IAnimatedMesh* am = getAnimatedMesh(filename);
if (am == NULL)
{
fprintf(stderr, "ERROR: 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,
float wave_height,
float wave_speed,
float wave_length)
{
mesh->setMaterialFlag(EMF_GOURAUD_SHADING, true);
scene::IMesh* welded_mesh = m_scene_manager->getMeshManipulator()->createMeshWelded(mesh);
scene::ISceneNode* out = m_scene_manager->addWaterSurfaceSceneNode(welded_mesh,
wave_height, wave_speed,
wave_length);
out->getMaterial(0).setFlag(EMF_GOURAUD_SHADING, true);
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)
{
return m_scene_manager->addMeshSceneNode(mesh, parent);
} // addMesh
// ----------------------------------------------------------------------------
PerCameraNode *IrrDriver::addPerCameraMesh(scene::IMesh* mesh,
scene::ICameraSceneNode* camera,
scene::ISceneNode *parent)
{
return new PerCameraNode((parent ? parent
: m_scene_manager->getRootSceneNode()),
m_scene_manager, -1, camera, mesh);
} // addMesh
// ----------------------------------------------------------------------------
/** Adds a billboard node to scene.
*/
scene::ISceneNode *IrrDriver::addBillboard(const core::dimension2d< f32 > size,
video::ITexture *texture,
scene::ISceneNode* parent)
{
scene::IBillboardSceneNode* node =
m_scene_manager->addBillboardSceneNode(parent, size);
assert(node->getMaterialCount() > 0);
node->setMaterialTexture(0, texture);
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;
SMesh *mesh = new 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;
SMesh *mesh = new 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)
{
return m_scene_manager->addAnimatedMeshSceneNode(mesh, NULL, -1,
core::vector3df(0,0,0),
core::vector3df(0,0,0),
core::vector3df(1,1,1),
/*addIfMeshIsZero*/true);
} // 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)
{
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)
{
return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1],
texture[2], texture[3],
texture[4], texture[5]);
} // addSkyBox
// ----------------------------------------------------------------------------
/** 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.
* \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(is_premul &&
StringUtils::hasSuffix(filename.c_str(), ".png") &&
(img->getColorFormat() == irr::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(is_prediv &&
(img->getColorFormat() == irr::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)
{
printf("[IrrDriver] Texture '%s' not found; Put a breakpoint at line %s:%i to debug!\n",
filename.c_str(), __FILE__, __LINE__);
}
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
// ----------------------------------------------------------------------------
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;
}
//printf("* Adding '%s'\n", texture->getName().getPath().c_str());
ITexture *t = m_video_driver->addTexture(StringUtils::getBasename(texture->getName().getPath().c_str()).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::getFont();
// 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;
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);
// 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 (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[64];
if (UserConfigParams::m_artist_debug_mode)
{
sprintf(buffer, "FPS: %i/%i/%i - %.2f/%.2f/%.2f KTris", min, fps, max, low, kilotris, high);
}
else
{
sprintf(buffer, "FPS: %i/%i/%i - %i KTris", min, fps, max, (int)round(kilotris));
}
core::stringw fpsString = buffer;
static video::SColor fpsColor = video::SColor(255, 255, 0, 0);
font->draw( fpsString.c_str(), core::rect< s32 >(100,0,400,50), fpsColor, false );
} // updateFPS
#ifdef DEBUG
void drawJoint(bool drawline, bool drawname, irr::scene::ISkinnedMesh::SJoint* joint,
ISkinnedMesh* mesh, int id)
{
//if (joint->PositionKeys.size() == 0) return;
irr::scene::ISkinnedMesh::SJoint* parent = NULL;
const core::array< irr::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();
SColor color(255, 255,255,255);
if (parent == NULL) color = SColor(255,0,255,0);
switch (id % 4)
{
case 0:
color = SColor(255,255,0,255);
break;
case 1:
color = SColor(255,255,0,0);
break;
case 2:
color = SColor(255,0,0,255);
break;
case 3:
color = 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 = SColor(255,255,0,255);
break;
case 1:
color = SColor(255,255,0,0);
break;
case 2:
color = SColor(255,0,0,255);
break;
case 3:
color = 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 = SColor(255,255,0,255);
break;
case 1:
color = SColor(255,255,0,0);
break;
case 2:
color = SColor(255,0,0,255);
break;
case 3:
color = 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
// ----------------------------------------------------------------------------
/** Update, called once per frame.
* \param dt Time since last update
*/
void IrrDriver::update(float dt)
{
// 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;
}
if (!m_device->run())
{
main_loop->abort();
}
if (UserConfigParams::m_gamepad_debug)
{
// Print a dividing line so that it's easier to see which events
// get received in which order in the one frame.
printf("-------------------------------------\n");
}
World *world = World::getWorld();
const bool inRace = world!=NULL;
// With bullet debug view we have to clear the back buffer, but
// that's not necessary for non-debug
bool back_buffer_clear = inRace && (world->getPhysics()->isDebug() ||
world->clearBackBuffer() );
if (inRace)
{
// Start the RTT for post-processing.
// We do this before beginScene() because we want to capture the glClear()
// because of tracks that do not have skyboxes (generally add-on tracks)
m_post_processing.beginCapture();
m_video_driver->beginScene(back_buffer_clear,
true, world->getClearColor());
}
else
{
m_video_driver->beginScene(back_buffer_clear,
true, video::SColor(255,100,101,140));
}
{
PROFILER_PUSH_CPU_MARKER("Update GUI widgets", 0x7F, 0x7F, 0x00);
// Just to mark the begin/end scene block
GUIEngine::GameState state = StateManager::get()->getGameState();
if (state != GUIEngine::GAME)
{
// This code needs to go outside beginScene() / endScene() since
// the model view widget will do off-screen rendering there
GUIEngine::Widget* widget;
for_in (widget, GUIEngine::needsUpdate)
{
widget->update(dt);
}
}
PROFILER_POP_CPU_MARKER();
if (inRace)
{
irr_driver->getVideoDriver()->enableMaterial2D();
RaceGUIBase *rg = world->getRaceGUI();
if (rg) rg->update(dt);
// No kart, this must be a cutscene
if (world->getNumKarts() == 0)
{
m_scene_manager->drawAll();
}
for(unsigned int i=0; i<world->getNumKarts(); i++)
{
AbstractKart *kart=world->getKart(i);
if(kart->getCamera())
{
#ifdef ENABLE_PROFILER
{
std::ostringstream oss;
oss << "drawAll() for kart " << i << std::flush;
PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (i+1)*60, 0x00, 0x00);
}
#endif
kart->getCamera()->activate();
rg->preRenderCallback(*kart);
m_scene_manager->drawAll();
PROFILER_POP_CPU_MARKER();
// Note that drawAll must be called before rendering
// the bullet debug view, since otherwise the camera
// is not set up properly. This is only used for
// the bullet debug view.
if (UserConfigParams::m_artist_debug_mode)
World::getWorld()->getPhysics()->draw();
} // if kart->Camera
} // for i<world->getNumKarts()
// Stop capturing for the post-processing
m_post_processing.endCapture();
// Render the post-processed scene
m_post_processing.render();
// To draw the race gui we set the viewport back to the full
// screen.
m_video_driver->setViewPort(core::recti(0, 0,
UserConfigParams::m_width,
UserConfigParams::m_height));
for(unsigned int i=0; i<world->getNumKarts(); i++)
{
AbstractKart *kart = world->getKart(i);
if(kart->getCamera())
{
{
char marker_name[100];
sprintf(marker_name, "renderPlayerView() for kart %d", i);
PROFILER_PUSH_CPU_MARKER(marker_name, 0x00, 0x00, (i+1)*60);
}
rg->renderPlayerView(kart);
PROFILER_POP_CPU_MARKER();
}
} // for i<getNumKarts
}
else
{
// render 3D stuff in cutscenes too
m_scene_manager->drawAll();
}
// The render and displayFPS calls interfere with bullet debug
// rendering, so they can not be called.
//FIXME if(!inRace || !UserConfigParams::m_bullet_debug)
{
// Either render the gui, or the global elements of the race gui.
GUIEngine::render(dt);
}
// Render the profiler
if(UserConfigParams::m_profiler_enabled)
{
PROFILER_DRAW();
}
} // just to mark the begin/end scene block
#ifdef DEBUG
for (unsigned int n=0; n<debug_meshes.size(); n++)
{
IMesh* mesh = debug_meshes[n]->getMesh();
ISkinnedMesh* smesh = static_cast<ISkinnedMesh*>(mesh);
const core::array< irr::scene::ISkinnedMesh::SJoint * >& joints = smesh->getAllJoints();
for (unsigned int j=0; j<joints.size(); j++)
{
//drawJoint(debug_meshes[n]->getFrameNr(), joints[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<debug_meshes.size(); n++)
{
IMesh* mesh = debug_meshes[n]->getMesh();
ISkinnedMesh* smesh = static_cast<ISkinnedMesh*>(mesh);
const core::array< irr::scene::ISkinnedMesh::SJoint * >& joints = smesh->getAllJoints();
for (unsigned int j=0; j<joints.size(); j++)
{
IMesh* mesh = debug_meshes[n]->getMesh();
ISkinnedMesh* smesh = static_cast<ISkinnedMesh*>(mesh);
drawJoint(true, false, joints[j], smesh, j);
}
}
#endif
m_video_driver->endScene();
if (m_request_screenshot)
{
m_request_screenshot = false;
video::IImage* image = m_video_driver->createScreenShot();
if (image)
{
time_t rawtime;
time ( &rawtime );
tm* timeInfo = localtime( &rawtime );
char timeBuffer[256];
sprintf(timeBuffer, "%i.%02i.%02i %02i.%02i.%02i",
timeInfo->tm_year + 1900, timeInfo->tm_mon, timeInfo->tm_mday,
timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
std::string trackName = race_manager->getTrackName();
if (World::getWorld() == NULL) trackName = "menu";
#if defined(WIN32)
std::string path = StringUtils::insertValues("C:\\Temp\\supertuxkart %s %s.png",
trackName, timeBuffer);
#else
std::string path = StringUtils::insertValues("/tmp/supertuxkart %s %s.png",
trackName, timeBuffer);
#endif
if (irr_driver->getVideoDriver()->writeImageToFile(image, io::path(path.c_str()), 0))
{
RaceGUIBase* base = (World::getWorld() == NULL ? NULL : World::getWorld()->getRaceGUI());
if (base != NULL)
{
base->addMessage(core::stringw(("Screenshot saved to\n" + path).c_str()), NULL,
2.0f, video::SColor(255,255,255,255), true, false);
}
}
else
{
RaceGUIBase* base = World::getWorld()->getRaceGUI();
if (base != NULL)
{
base->addMessage(core::stringw(("FAILED saving screenshot to\n" + path + "\n:(").c_str()),
NULL, 2.0f, video::SColor(255,255,255,255), true, false);
}
}
image->drop();
}
}
getPostProcessing()->update(dt);
// 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;
}
// ----------------------------------------------------------------------------
// Irrlicht Event handler.
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)
{
printf("[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 UserConfigParams::m_pixel_shaders &&
m_video_driver->queryFeature(video::EVDF_ARB_GLSL) &&
m_video_driver->queryFeature(video::EVDF_PIXEL_SHADER_2_0) &&
m_video_driver->queryFeature(video::EVDF_MULTITEXTURE );
}
// ----------------------------------------------------------------------------
#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() );
m_rtt_main_node = node;
}
else
{
scene::IAnimatedMeshSceneNode* node =
irr_driver->getSceneManager()->addAnimatedMeshSceneNode(
(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() );
m_rtt_main_node = node;
}
assert(m_rtt_main_node != NULL);
assert(mesh.size() == (int)mesh_location.size());
assert(mesh.size() == (int)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((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, 120,
120, 120) );
const core::vector3df &sun_pos = core::vector3df( 0, 200, 100.0f );
m_light = irr_driver->getSceneManager()
->addLightSceneNode(NULL, sun_pos, video::SColorf(1.0f,1.0f,1.0f),
10000.0f /* radius */);
m_light->getLightData().DiffuseColor
= irr::video::SColorf(0.5f, 0.5f, 0.5f, 0.5f);
m_light->getLightData().SpecularColor
= irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);
m_rtt_main_node->setMaterialFlag(EMF_GOURAUD_SHADING , true);
m_rtt_main_node->setMaterialFlag(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(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(EMF_GOURAUD_SHADING , true);
}
m_camera = irr_driver->getSceneManager()->addCameraSceneNode();
m_camera->setPosition( core::vector3df(0.0, 20.0f, 70.0f) );
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
*/
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) );
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);
}
m_video_driver->setRenderTarget(0, false, false);
return m_render_target_texture;
}