From 2061a3862f264fe0bfff9ea8a298bdb207ad4c0a Mon Sep 17 00:00:00 2001 From: hybrid Date: Mon, 2 Nov 2009 17:20:21 +0000 Subject: [PATCH] Merged revisions 2695:2780 from trunk. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@2781 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 33 +- examples/04.Movement/main.cpp | 2 +- examples/09.Meshviewer/main.cpp | 8 +- .../20.ManagedLights/ManagedLights.vcproj | 191 ++++-------- examples/20.ManagedLights/main.cpp | 20 +- .../Quake3Explorer_vc7.vcproj | 9 + examples/21.Quake3Explorer/main.cpp | 125 ++++---- examples/BuildAllExamples_v7.sln | 16 +- examples/BuildAllExamples_v9.sln | 3 + include/IAnimatedMeshSceneNode.h | 6 +- include/IEventReceiver.h | 4 +- include/IFileArchive.h | 12 +- include/IFileList.h | 21 +- include/IFileSystem.h | 13 +- include/IGUIContextMenu.h | 55 +++- include/IGUIElement.h | 8 +- include/IGUIEnvironment.h | 11 +- include/IGUIFont.h | 2 +- include/IGUISkin.h | 26 +- include/IGUITreeView.h | 184 +++++------ include/IImageLoader.h | 2 +- include/IImageWriter.h | 2 +- include/ILightManager.h | 40 +-- include/IMeshLoader.h | 2 +- include/IMeshManipulator.h | 6 +- include/ISceneCollisionManager.h | 3 +- include/ISceneManager.h | 70 +++-- include/IVideoDriver.h | 93 ++++-- include/IrrCompileConfig.h | 15 +- include/SColor.h | 10 +- include/SViewFrustum.h | 2 +- include/irrArray.h | 2 +- include/irrMath.h | 5 +- include/irrString.h | 96 +++++- include/irrTypes.h | 13 +- include/irrXML.h | 6 +- include/irrlicht.h | 11 +- include/matrix4.h | 9 +- include/path.h | 2 + include/position2d.h | 2 + include/quaternion.h | 11 +- media/020shot.jpg | Bin 0 -> 11126 bytes media/021shot.jpg | Bin 97819 -> 25032 bytes media/ninja animation ranges.txt | 2 + scripts/doc/irrlicht/makedocumentation.bat | 6 +- scripts/doc/irrlicht/makedocumentation.sh | 2 +- source/Irrlicht/CAnimatedMeshMD2.h | 1 - source/Irrlicht/CAnimatedMeshSceneNode.cpp | 13 +- source/Irrlicht/CAnimatedMeshSceneNode.h | 5 +- source/Irrlicht/CAttributeImpl.h | 3 + source/Irrlicht/CCameraSceneNode.cpp | 40 ++- source/Irrlicht/CCameraSceneNode.h | 3 + source/Irrlicht/CD3D8Driver.cpp | 22 +- source/Irrlicht/CD3D8Driver.h | 3 + source/Irrlicht/CD3D8MaterialRenderer.h | 4 +- source/Irrlicht/CD3D9Driver.cpp | 22 +- source/Irrlicht/CD3D9Driver.h | 3 + source/Irrlicht/CD3D9MaterialRenderer.h | 6 +- .../Irrlicht/CD3D9ShaderMaterialRenderer.cpp | 12 +- source/Irrlicht/CFileList.cpp | 8 +- source/Irrlicht/CFileList.h | 9 +- source/Irrlicht/CFileSystem.cpp | 30 +- source/Irrlicht/CFileSystem.h | 3 + source/Irrlicht/CGUIColorSelectDialog.cpp | 4 +- source/Irrlicht/CGUIContextMenu.cpp | 130 ++++++-- source/Irrlicht/CGUIContextMenu.h | 27 +- source/Irrlicht/CGUIEnvironment.cpp | 8 +- source/Irrlicht/CGUIEnvironment.h | 2 +- source/Irrlicht/CGUIFileOpenDialog.cpp | 4 +- source/Irrlicht/CGUIMessageBox.cpp | 290 +++++++++--------- source/Irrlicht/CGUIMessageBox.h | 6 +- source/Irrlicht/CGUISkin.cpp | 6 + source/Irrlicht/CGUIToolBar.cpp | 16 +- source/Irrlicht/CIrrDeviceConsole.cpp | 15 +- source/Irrlicht/CIrrDeviceConsole.h | 4 +- source/Irrlicht/CIrrDeviceLinux.cpp | 290 ++++++++++-------- source/Irrlicht/CIrrDeviceLinux.h | 3 +- source/Irrlicht/CIrrDeviceSDL.cpp | 5 +- source/Irrlicht/CIrrDeviceSDL.h | 1 - source/Irrlicht/CIrrDeviceStub.cpp | 3 +- source/Irrlicht/CIrrDeviceStub.h | 5 +- source/Irrlicht/CIrrDeviceWin32.cpp | 76 +++-- source/Irrlicht/CIrrDeviceWin32.h | 21 +- source/Irrlicht/CIrrDeviceWinCE.cpp | 9 +- source/Irrlicht/CIrrDeviceWinCE.h | 14 +- source/Irrlicht/CLWOMeshFileLoader.cpp | 30 +- source/Irrlicht/CMeshManipulator.cpp | 13 +- source/Irrlicht/CMeshSceneNode.cpp | 1 + source/Irrlicht/CNullDriver.cpp | 89 ++---- source/Irrlicht/CNullDriver.h | 33 +- source/Irrlicht/COBJMeshFileLoader.cpp | 10 +- source/Irrlicht/COSOperator.cpp | 4 +- source/Irrlicht/COpenGLDriver.cpp | 21 +- source/Irrlicht/COpenGLDriver.h | 3 + source/Irrlicht/COpenGLMaterialRenderer.h | 6 +- source/Irrlicht/COpenGLNormalMapRenderer.cpp | 47 +-- .../Irrlicht/COpenGLParallaxMapRenderer.cpp | 75 ++--- source/Irrlicht/CPLYMeshFileLoader.cpp | 6 +- source/Irrlicht/CPakReader.cpp | 92 +++--- source/Irrlicht/CPakReader.h | 19 +- source/Irrlicht/CSTLMeshFileLoader.cpp | 4 +- source/Irrlicht/CSceneManager.cpp | 110 ++++--- source/Irrlicht/CSceneManager.h | 12 +- .../Irrlicht/CSceneNodeAnimatorCameraMaya.cpp | 6 + .../Irrlicht/CSceneNodeAnimatorCameraMaya.h | 1 + .../Irrlicht/CSceneNodeAnimatorRotation.cpp | 6 +- source/Irrlicht/CSkinnedMesh.cpp | 22 +- source/Irrlicht/CSoftwareDriver2.cpp | 7 + source/Irrlicht/CSoftwareDriver2.h | 3 + source/Irrlicht/CTRGouraud2.cpp | 6 +- source/Irrlicht/CTRGouraudAlpha2.cpp | 6 +- source/Irrlicht/CTRGouraudAlphaNoZ2.cpp | 6 +- source/Irrlicht/CTRTextureBlend.cpp | 6 +- source/Irrlicht/CTRTextureDetailMap2.cpp | 6 +- source/Irrlicht/CTRTextureGouraud2.cpp | 6 +- source/Irrlicht/CTRTextureGouraudAdd2.cpp | 6 +- source/Irrlicht/CTRTextureGouraudAddNoZ2.cpp | 6 +- source/Irrlicht/CTRTextureGouraudAlpha.cpp | 6 +- source/Irrlicht/CTRTextureGouraudAlphaNoZ.cpp | 6 +- source/Irrlicht/CTRTextureGouraudNoZ2.cpp | 6 +- .../CTRTextureGouraudVertexAlpha2.cpp | 6 +- source/Irrlicht/CTRTextureLightMap2_Add.cpp | 6 +- source/Irrlicht/CTRTextureLightMap2_M1.cpp | 6 +- source/Irrlicht/CTRTextureLightMap2_M2.cpp | 6 +- source/Irrlicht/CTRTextureLightMap2_M4.cpp | 13 +- .../CTRTextureLightMapGouraud2_M4.cpp | 6 +- source/Irrlicht/CTarReader.cpp | 4 + source/Irrlicht/CTerrainSceneNode.cpp | 8 +- source/Irrlicht/Irrlicht7.1.vcproj | 30 +- source/Irrlicht/Irrlicht8.0.vcproj | 48 +-- source/Irrlicht/Makefile | 32 +- source/Irrlicht/SoftwareDriver2_helper.h | 2 +- source/Irrlicht/os.cpp | 4 +- tests/b3dAnimation.cpp | 4 +- tests/lightMaps.cpp | 65 ++++ tests/main.cpp | 3 + tests/media/Burning's Video-b3dAnimation.png | Bin 5982 -> 6245 bytes tests/media/Burning's Video-lightmaps.png | Bin 0 -> 33502 bytes tests/media/Direct3D 9.0-lightmaps.png | Bin 0 -> 33502 bytes tests/media/OpenGL-lightmaps.png | Bin 0 -> 33502 bytes tests/media/sample_pakfile.pak | Bin 0 -> 88 bytes tests/media/test.xml | 5 + tests/pakReader.cpp | 52 ++++ tests/terrainSceneNode.cpp | 4 +- tests/testUtils.cpp | 9 +- tests/testVector2d.cpp | 12 +- tests/testVector3d.cpp | 11 + tests/testXML.cpp | 43 +++ tests/tests-last-passed-at.txt | 2 +- tests/tests.cbp | 7 +- tests/tests_vc8.vcproj | 4 + tests/textureRenderStates.cpp | 2 +- tools/GUIEditor/CGUIEditWindow.cpp | 2 +- tools/GUIEditor/CGUIPanel.cpp | 2 + tools/GUIEditor/main.cpp | 2 - 155 files changed, 2068 insertions(+), 1305 deletions(-) create mode 100644 media/020shot.jpg create mode 100644 tests/lightMaps.cpp create mode 100644 tests/media/Burning's Video-lightmaps.png create mode 100644 tests/media/Direct3D 9.0-lightmaps.png create mode 100644 tests/media/OpenGL-lightmaps.png create mode 100644 tests/media/sample_pakfile.pak create mode 100644 tests/media/test.xml create mode 100644 tests/pakReader.cpp create mode 100644 tests/testXML.cpp diff --git a/changes.txt b/changes.txt index 47c4fad9..0a346657 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,16 @@ -Changes in 1.6 (??.??.2009) +Changes in 1.6.1 + + - Fix another (OldValue == NewValue) before drop()/grap(), this time in CTextureAttribute::setTexture. + + - Bugfix: CGUIToolBar::addButton does no longer mess up when no image is set and does now actually work with the text. + + - Fix ninja animation range which got messed up a little when b3d animations got fixed (thx gbox for finding) + +Changes in 1.6 (23.09.2009) + + - Added IFileSystem::createEmptyFileList, exposed IFileList::sort, addItem and added getID + + - Fix MAKE_IRR_ID so it can be used from outside the irr namespace (Micha's patch) - Renamed some methods in ISkinnedMesh to match the official Irrlicht naming scheme according to createXXX() @@ -645,10 +657,27 @@ Changes in 1.6 (??.??.2009) - Add a hitPosition out parameter to ISceneCollisionManager::getCollisionResultPosition() - this is a (small) API breaking change. ------------------------------------- -Changes in version 1.5.1 (??.?? 2009) +Changes in version 1.5.2 (??.??.2009) + + - Fix terrain smoothing, bug found by loverlinfish + + - SOLARIS recognition removed. Please specify the platform define manually. This allows for compilation under sparc/Linux and sparc/Solaris + + - Some uninitialized variables fixed + + - FreeBSD joystick support added (for Debian package) - Fix cursor problems found by buffer and by rvl2 as described in http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=34823&highlight= + - OSX/XCode updates + + - MS3D loader bug fixed + + - Float parse bug fixed + +------------------------------------- +Changes in version 1.5.1 (05.08.2009) + - Make sure a missing font does not corrupt the skin. - Fix getAngle in vector2d as suggested by xray. This has only a minor impact on s32 vectors. diff --git a/examples/04.Movement/main.cpp b/examples/04.Movement/main.cpp index 78fb1f51..8584bb0b 100644 --- a/examples/04.Movement/main.cpp +++ b/examples/04.Movement/main.cpp @@ -176,7 +176,7 @@ int main() */ anms->setMaterialFlag(video::EMF_LIGHTING, false); - anms->setFrameLoop(0, 14); + anms->setFrameLoop(0, 13); anms->setAnimationSpeed(15); // anms->setMD2Animation(scene::EMAT_RUN); diff --git a/examples/09.Meshviewer/main.cpp b/examples/09.Meshviewer/main.cpp index f0edc768..efae647d 100644 --- a/examples/09.Meshviewer/main.cpp +++ b/examples/09.Meshviewer/main.cpp @@ -556,11 +556,9 @@ public: Most of the hard work is done. We only need to create the Irrlicht Engine device and all the buttons, menus and toolbars. We start up the engine as usual, using createDevice(). To make our application catch events, we set our -eventreceiver as parameter. The #ifdef WIN32 preprocessor commands are not -necessary, but I included them to make the tutorial use DirectX on Windows and -OpenGL on all other platforms like Linux. As you can see, there is also a -unusual call to IrrlichtDevice::setResizeAble(). This makes the render window -resizeable, which is quite useful for a mesh viewer. +eventreceiver as parameter. As you can see, there is also a call to +IrrlichtDevice::setResizeable(). This makes the render window resizeable, which +is quite useful for a mesh viewer. */ int main(int argc, char* argv[]) { diff --git a/examples/20.ManagedLights/ManagedLights.vcproj b/examples/20.ManagedLights/ManagedLights.vcproj index 794946fa..586c49e6 100644 --- a/examples/20.ManagedLights/ManagedLights.vcproj +++ b/examples/20.ManagedLights/ManagedLights.vcproj @@ -1,227 +1,160 @@ + SccProjectName="" + SccLocalPath=""> + Name="Win32"/> - - - - - - - + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + CompileAs="0"/> - - + Name="VCCustomBuildTool"/> + TargetMachine="1"/> + Name="VCMIDLTool" + TypeLibraryName=".\Debug/ManagedLights.tlb" + HeaderFileName=""/> + Name="VCPostBuildEventTool"/> + Name="VCPreBuildEventTool"/> + Name="VCPreLinkEventTool"/> + Name="VCResourceCompilerTool" + PreprocessorDefinitions="_DEBUG" + Culture="3079"/> + Name="VCWebServiceProxyGeneratorTool"/> + Name="VCXMLDataGeneratorTool"/> + Name="VCWebDeploymentTool"/> + + - - - - - + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + SuppressStartupBanner="TRUE" + CompileAs="0"/> - - + Name="VCCustomBuildTool"/> + TargetMachine="1"/> + Name="VCMIDLTool" + TypeLibraryName=".\Release/ManagedLights.tlb" + HeaderFileName=""/> + Name="VCPostBuildEventTool"/> + Name="VCPreBuildEventTool"/> + Name="VCPreLinkEventTool"/> + Name="VCResourceCompilerTool" + PreprocessorDefinitions="NDEBUG" + Culture="3079"/> + Name="VCWebServiceProxyGeneratorTool"/> + Name="VCXMLDataGeneratorTool"/> + Name="VCWebDeploymentTool"/> + + + RelativePath="main.cpp"> + Name="Debug|Win32"> + BasicRuntimeChecks="3"/> + Name="Release|Win32"> + PreprocessorDefinitions=""/> diff --git a/examples/20.ManagedLights/main.cpp b/examples/20.ManagedLights/main.cpp index 4a5757b4..7b2d696c 100644 --- a/examples/20.ManagedLights/main.cpp +++ b/examples/20.ManagedLights/main.cpp @@ -1,8 +1,10 @@ +/** Example 020 Managed Lights -// Written by Colin MacDonald -// Copyright (C) 2002-2009 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h +Written by Colin MacDonald. This tutorial explains the use of the Light Manager +of Irrlicht. It enables the use of more dynamic light sources than the actual +hardware supports. Further applications of the Light Manager, such as per scene +node callbacks, are left out for simplicity of the example. +*/ #include #include @@ -273,7 +275,7 @@ private: }; -/*! +/* */ int main(int argumentCount, char * argumentValues[]) { @@ -328,7 +330,9 @@ int main(int argumentCount, char * argumentValues[]) guienv->addStaticText(L"2 - Closest 3 lights", core::rect(10,30,200,50)); guienv->addStaticText(L"3 - Lights in zone", core::rect(10,50,200,70)); - // Add several "zones". You could use this technique to light individual rooms, for example. +/* +Add several "zones". You could use this technique to light individual rooms, for example. +*/ for(f32 zoneX = -100.f; zoneX <= 100.f; zoneX += 50.f) for(f32 zoneY = -60.f; zoneY <= 60.f; zoneY += 60.f) { @@ -403,3 +407,7 @@ int main(int argumentCount, char * argumentValues[]) device->drop(); return 0; } + +/* +**/ + diff --git a/examples/21.Quake3Explorer/Quake3Explorer_vc7.vcproj b/examples/21.Quake3Explorer/Quake3Explorer_vc7.vcproj index 12a7b656..eefd7bb9 100644 --- a/examples/21.Quake3Explorer/Quake3Explorer_vc7.vcproj +++ b/examples/21.Quake3Explorer/Quake3Explorer_vc7.vcproj @@ -126,6 +126,15 @@ + + + + + + diff --git a/examples/21.Quake3Explorer/main.cpp b/examples/21.Quake3Explorer/main.cpp index c7110a70..12f6e042 100644 --- a/examples/21.Quake3Explorer/main.cpp +++ b/examples/21.Quake3Explorer/main.cpp @@ -23,12 +23,17 @@ Copyright 2006-2009 Burningwater, Thomas Alten #include "sound.h" #include -/*! +/* Game Data is used to hold Data which is needed to drive the game */ struct GameData { - GameData ( const path &startupDir); + GameData ( const path &startupDir) : + retVal(0), createExDevice(0), Device(0), StartupDir(startupDir) + { + setDefault (); + } + void setDefault (); s32 save ( const path &filename ); s32 load ( const path &filename ); @@ -58,18 +63,7 @@ struct GameData IrrlichtDevice *Device; }; -/*! -*/ -GameData::GameData ( const path &startupDir) -{ - retVal = 0; - createExDevice = 0; - Device = 0; - StartupDir = startupDir; - setDefault (); -} - -/*! +/* set default settings */ void GameData::setDefault () @@ -92,7 +86,7 @@ void GameData::setDefault () deviceParam.WindowSize.Height = 600; deviceParam.Fullscreen = false; deviceParam.Bits = 32; - deviceParam.ZBufferBits = 32; + deviceParam.ZBufferBits = 16; deviceParam.Vsync = false; deviceParam.AntiAlias = false; @@ -112,19 +106,17 @@ void GameData::setDefault () CurrentMapName = ""; CurrentArchiveList.clear (); - //! Explorer Media directory + // Explorer Media directory CurrentArchiveList.push_back ( StartupDir + "../../media/" ); - //! Add the original quake3 files before you load your custom map - //! Most mods are using the original shaders, models&items&weapons - CurrentArchiveList.push_back ( "/q/baseq3/" ); - - - CurrentArchiveList.push_back ( StartupDir + "../../media/map-20kdm2.pk3" ); + // Add the original quake3 files before you load your custom map + // Most mods are using the original shaders, models&items&weapons + CurrentArchiveList.push_back("/q/baseq3/"); + CurrentArchiveList.push_back(StartupDir + "../../media/map-20kdm2.pk3"); } -/*! +/* Load the current game State from a typical quake3 cfg file */ s32 GameData::load ( const path &filename ) @@ -132,7 +124,7 @@ s32 GameData::load ( const path &filename ) if (!Device) return 0; - //! the quake3 mesh loader can also handle *.shader and *.cfg file + // the quake3 mesh loader can also handle *.shader and *.cfg file IQ3LevelMesh* mesh = (IQ3LevelMesh*) Device->getSceneManager()->getMesh ( filename ); if (!mesh) return 0; @@ -171,7 +163,7 @@ s32 GameData::load ( const path &filename ) return 1; } -/*! +/* Store the current game State in a quake3 configuration file */ s32 GameData::save ( const path &filename ) @@ -217,7 +209,7 @@ s32 GameData::save ( const path &filename ) return 1; } -/*! +/* Representing a player */ struct Q3Player : public IAnimationEndCallBack @@ -254,7 +246,7 @@ struct Q3Player : public IAnimationEndCallBack }; -/*! +/* End player */ void Q3Player::shutdown () { @@ -274,13 +266,15 @@ void Q3Player::shutdown () } -/*! +/* create a new player */ void Q3Player::create ( IrrlichtDevice *device, IQ3LevelMesh* mesh, ISceneNode *mapNode, IMetaTriangleSelector *meta ) { setTimeFire ( Anim + 0, 200, FIRED ); setTimeFire ( Anim + 1, 5000 ); + if (!device) + return; // load FPS weapon to Camera Device = device; Mesh = mesh; @@ -371,12 +365,14 @@ void Q3Player::create ( IrrlichtDevice *device, IQ3LevelMesh* mesh, ISceneNode * } -/*! +/* so we need a good starting Position in the level. we can ask the Quake3 Loader for all entities with class_name "info_player_deathmatch" */ void Q3Player::respawn () { + if (!Device) + return; ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera(); Device->getLogger()->log( "respawn" ); @@ -395,6 +391,8 @@ void Q3Player::respawn () */ void Q3Player::setpos ( const vector3df &pos, const vector3df &rotation ) { + if (!Device) + return; Device->getLogger()->log( "setpos" ); ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera(); @@ -407,7 +405,7 @@ void Q3Player::setpos ( const vector3df &pos, const vector3df &rotation ) } } -/*! +/* set the Animation of the player and weapon */ void Q3Player::setAnim ( const c8 *name ) { @@ -431,8 +429,7 @@ void Q3Player::setAnim ( const c8 *name ) } -/*! -*/ +// Callback void Q3Player::OnAnimationEnd(IAnimatedMeshSceneNode* node) { setAnim ( 0 ); @@ -440,7 +437,8 @@ void Q3Player::OnAnimationEnd(IAnimatedMeshSceneNode* node) -//! GUIElements +/* GUI Elements +*/ struct GUI { GUI () @@ -483,11 +481,10 @@ struct GUI IGUIStaticText* StatusLine; IGUIImage* Logo; IGUIWindow* Window; - }; -/*! +/* CQuake3EventHandler controls the game */ class CQuake3EventHandler : public IEventReceiver @@ -547,14 +544,14 @@ private: void dropMap (); }; -/*! +/* Constructor */ CQuake3EventHandler::CQuake3EventHandler( GameData *game ) : Game(game), Mesh(0), MapParent(0), ShaderParent(0), ItemParent(0), UnresolvedParent(0), BulletParent(0), FogParent(0), SkyNode(0), Meta(0) { buf[0]=0; - //! Also use 16 Bit Textures for 16 Bit RenderDevice + // Also use 16 Bit Textures for 16 Bit RenderDevice if ( Game->deviceParam.Bits == 16 ) { game->Device->getVideoDriver()->setTextureCreationFlag(ETCF_ALWAYS_16_BIT, true); @@ -572,6 +569,7 @@ CQuake3EventHandler::CQuake3EventHandler( GameData *game ) } +// destructor CQuake3EventHandler::~CQuake3EventHandler () { Player[0].shutdown (); @@ -583,7 +581,7 @@ CQuake3EventHandler::~CQuake3EventHandler () } -//! create runtime textures smog, fog +// create runtime textures smog, fog void CQuake3EventHandler::createTextures () { IVideoDriver * driver = Game->Device->getVideoDriver(); @@ -636,7 +634,7 @@ void CQuake3EventHandler::createTextures () } -/*! +/* create the GUI */ void CQuake3EventHandler::CreateGUI() @@ -829,8 +827,7 @@ void CQuake3EventHandler::CreateGUI() } - -/*! +/* Add an Archive to the FileSystems und updates the GUI */ void CQuake3EventHandler::AddArchive ( const path& archiveName ) @@ -916,7 +913,7 @@ void CQuake3EventHandler::AddArchive ( const path& archiveName ) u32 g = 0; core::stringw s; - //! browse the attached file system + // browse the attached file system fs->setFileListSystem ( FILESYSTEM_VIRTUAL ); fs->changeWorkingDirectoryTo ( "/maps/" ); IFileList *fileList = fs->createFileList (); @@ -994,7 +991,7 @@ void CQuake3EventHandler::AddArchive ( const path& archiveName ) } -/*! +/* clears the Map in Memory */ void CQuake3EventHandler::dropMap () @@ -1031,7 +1028,7 @@ void CQuake3EventHandler::dropMap () Mesh = 0; } -/*! +/* Load new map */ void CQuake3EventHandler::LoadMap ( const stringw &mapName, s32 collision ) { @@ -1128,9 +1125,6 @@ void CQuake3EventHandler::LoadMap ( const stringw &mapName, s32 collision ) } /* -**/ - -/*! Adds a SceneNode with an icon to the Scene Tree */ void CQuake3EventHandler::addSceneTreeItem( ISceneNode * parent, IGUITreeViewNode* nodeParent) @@ -1203,13 +1197,13 @@ void CQuake3EventHandler::addSceneTreeItem( ISceneNode * parent, IGUITreeViewNod } -//! Adds life! +// Adds life! void CQuake3EventHandler::CreatePlayers() { Player[0].create ( Game->Device, Mesh, MapParent, Meta ); } -//! Adds a skydome to the scene +// Adds a skydome to the scene void CQuake3EventHandler::AddSky( u32 dome, const c8 *texture) { ISceneManager *smgr = Game->Device->getSceneManager (); @@ -1229,10 +1223,13 @@ void CQuake3EventHandler::AddSky( u32 dome, const c8 *texture) snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] ); SkyNode = smgr->addSkyBoxSceneNode( driver->getTexture ( buf ), 0, 0, 0, 0, 0 ); - for ( i = 0; i < 6; ++i ) + if (SkyNode) { - snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] ); - SkyNode->getMaterial(i).setTexture ( 0, driver->getTexture ( buf ) ); + for ( i = 0; i < 6; ++i ) + { + snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] ); + SkyNode->getMaterial(i).setTexture ( 0, driver->getTexture ( buf ) ); + } } } else @@ -1264,7 +1261,8 @@ void CQuake3EventHandler::AddSky( u32 dome, const c8 *texture) ); } - SkyNode->setName ( "Skydome" ); + if (SkyNode) + SkyNode->setName("Skydome"); //SkyNode->getMaterial(0).ZBuffer = video::EMDF_DEPTH_LESS_EQUAL; driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState); @@ -1272,8 +1270,7 @@ void CQuake3EventHandler::AddSky( u32 dome, const c8 *texture) } -/*! -*/ +// enable GUI elements void CQuake3EventHandler::SetGUIActive( s32 command) { bool inputState = false; @@ -1315,7 +1312,7 @@ void CQuake3EventHandler::SetGUIActive( s32 command) -/*! +/* Handle game input */ bool CQuake3EventHandler::OnEvent(const SEvent& eve) @@ -1525,7 +1522,7 @@ bool CQuake3EventHandler::OnEvent(const SEvent& eve) else if (eve.KeyInput.Key == KEY_F11) { - //! screenshot are taken without gamma! + // screenshot are taken without gamma! IImage* image = Game->Device->getVideoDriver()->createScreenShot(); if (image) { @@ -1824,8 +1821,7 @@ void CQuake3EventHandler::useItem( Q3Player * player) // play sound } -/*! -*/ +// rendered when bullets hit something void CQuake3EventHandler::createParticleImpacts( u32 now ) { ISceneManager* sm = Game->Device->getSceneManager(); @@ -1969,7 +1965,7 @@ void CQuake3EventHandler::Animate() IAttributes * attr = smgr->getParameters(); swprintf ( msg, 128, - L"Q3 %s [%s], FPS:%03d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)", + L"Q3 %s [%ls], FPS:%03d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)", Game->CurrentMapName.c_str(), driver->getName(), driver->getFPS (), @@ -2006,7 +2002,7 @@ void CQuake3EventHandler::Animate() } -/*! +/* The main game states */ void runGame ( GameData *game ) { @@ -2024,10 +2020,10 @@ void runGame ( GameData *game ) // create an event receiver based on current game data CQuake3EventHandler *eventHandler = new CQuake3EventHandler( game ); - //! load stored config + // load stored config game->load ( "explorer.cfg" ); - //! add our media directory and archive to the file system + // add our media directory and archive to the file system for ( u32 i = 0; i < game->CurrentArchiveList.size(); ++i ) { eventHandler->AddArchive ( game->CurrentArchiveList[i] ); @@ -2078,7 +2074,7 @@ void runGame ( GameData *game ) #endif -/*! +/* The main routine, doing all setup */ int IRRCALLCONV main(int argc, char* argv[]) { @@ -2130,4 +2126,3 @@ int IRRCALLCONV main(int argc, char* argv[]) /* **/ - diff --git a/examples/BuildAllExamples_v7.sln b/examples/BuildAllExamples_v7.sln index 8cacce69..839aaa70 100644 --- a/examples/BuildAllExamples_v7.sln +++ b/examples/BuildAllExamples_v7.sln @@ -78,15 +78,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "19.MouseAndJoystick", "19.M ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "20.ManagedLights", "20.ManagedLights\ManagedLights_vc7.vcproj", "{16007FE2-142B-47F8-93E1-519BA3F39E71}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "21.Quake3Explorer", "21.Quake3Explorer\Quake3Explorer_vc7.vcproj", "{CDC4AAA9-72E1-4FFA-A04D-7EF59D8B97CD}" ProjectSection(ProjectDependencies) = postProject {E08E042A-6C45-411B-92BE-3CC31331019F} = {E08E042A-6C45-411B-92BE-3CC31331019F} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "20.ManagedLights", "20.ManagedLights\ManagedLights.vcproj", "{1AB9413E-4F53-42A3-8CB2-CB4BE22336D0}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug @@ -167,14 +167,14 @@ Global {EB3B38EA-5CE7-4983-845B-880661E69D09}.Release.Build.0 = Release|Win32 {1AB9413E-4F53-42A3-8CB2-CB4BE22336D0}.Debug.ActiveCfg = Debug|Win32 {1AB9413E-4F53-42A3-8CB2-CB4BE22336D0}.Release.ActiveCfg = Release|Win32 - {16007FE2-142B-47F8-93E1-519BA3F39E71}.Debug.ActiveCfg = Debug|Win32 - {16007FE2-142B-47F8-93E1-519BA3F39E71}.Debug.Build.0 = Debug|Win32 - {16007FE2-142B-47F8-93E1-519BA3F39E71}.Release.ActiveCfg = Release|Win32 - {16007FE2-142B-47F8-93E1-519BA3F39E71}.Release.Build.0 = Release|Win32 {CDC4AAA9-72E1-4FFA-A04D-7EF59D8B97CD}.Debug.ActiveCfg = Debug|Win32 {CDC4AAA9-72E1-4FFA-A04D-7EF59D8B97CD}.Debug.Build.0 = Debug|Win32 {CDC4AAA9-72E1-4FFA-A04D-7EF59D8B97CD}.Release.ActiveCfg = Release|Win32 {CDC4AAA9-72E1-4FFA-A04D-7EF59D8B97CD}.Release.Build.0 = Release|Win32 + {1AB9413E-4F53-42A3-8CB2-CB4BE22336D0}.Debug.ActiveCfg = Debug|Win32 + {1AB9413E-4F53-42A3-8CB2-CB4BE22336D0}.Debug.Build.0 = Debug|Win32 + {1AB9413E-4F53-42A3-8CB2-CB4BE22336D0}.Release.ActiveCfg = Release|Win32 + {1AB9413E-4F53-42A3-8CB2-CB4BE22336D0}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection diff --git a/examples/BuildAllExamples_v9.sln b/examples/BuildAllExamples_v9.sln index 472dbece..5d966b40 100644 --- a/examples/BuildAllExamples_v9.sln +++ b/examples/BuildAllExamples_v9.sln @@ -181,8 +181,11 @@ Global {FE853A36-E0D1-4AC5-A792-B643E70D2953}.Debug|Win32.ActiveCfg = Debug|Win32 {FE853A36-E0D1-4AC5-A792-B643E70D2953}.Debug|Win32.Build.0 = Debug|Win32 {FE853A36-E0D1-4AC5-A792-B643E70D2953}.Release|Win32.ActiveCfg = Release|Win32 + {FE853A36-E0D1-4AC5-A792-B643E70D2953}.Release|Win32.Build.0 = Release|Win32 {16007FE2-142B-47F8-93E1-519BA3F39E71}.Debug|Win32.ActiveCfg = Debug|Win32 + {16007FE2-142B-47F8-93E1-519BA3F39E71}.Debug|Win32.Build.0 = Debug|Win32 {16007FE2-142B-47F8-93E1-519BA3F39E71}.Release|Win32.ActiveCfg = Release|Win32 + {16007FE2-142B-47F8-93E1-519BA3F39E71}.Release|Win32.Build.0 = Release|Win32 {CDC4AAA9-72E1-4FFA-A04D-7EF59D8B97CD}.Debug|Win32.ActiveCfg = Debug|Win32 {CDC4AAA9-72E1-4FFA-A04D-7EF59D8B97CD}.Debug|Win32.Build.0 = Debug|Win32 {CDC4AAA9-72E1-4FFA-A04D-7EF59D8B97CD}.Release|Win32.ActiveCfg = Release|Win32 diff --git a/include/IAnimatedMeshSceneNode.h b/include/IAnimatedMeshSceneNode.h index a1e024ad..d942e035 100644 --- a/include/IAnimatedMeshSceneNode.h +++ b/include/IAnimatedMeshSceneNode.h @@ -81,10 +81,14 @@ namespace scene \return True if successful, false if not. */ virtual bool setFrameLoop(s32 begin, s32 end) = 0; - //! Sets the speed with witch the animation is played. + //! Sets the speed with which the animation is played. /** \param framesPerSecond: Frames per second played. */ virtual void setAnimationSpeed(f32 framesPerSecond) = 0; + //! Gets the speed with which the animation is played. + /** \return Frames per second played. */ + virtual f32 getAnimationSpeed() const =0; + //! Creates shadow volume scene node as child of this node. /** The shadow can be rendered using the ZPass or the zfail method. ZPass is a little bit faster because the shadow volume diff --git a/include/IEventReceiver.h b/include/IEventReceiver.h index 89dd0206..7c9e139f 100644 --- a/include/IEventReceiver.h +++ b/include/IEventReceiver.h @@ -313,7 +313,7 @@ struct SEvent * each connected joystick once per run() of the device. Joystick events will * not be generated by default. If joystick support is available for the * active device, _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ is defined, and - * @ref IrrlichtDevice::activateJoysticks() has been called, an event of + * @ref irr::IrrlichtDevice::activateJoysticks() has been called, an event of * this type will be generated once per joystick per @ref IrrlichtDevice::run() * regardless of whether the state of the joystick has actually changed. */ struct SJoystickEvent @@ -420,7 +420,7 @@ public: }; -//! Information on a joystick, returned from @ref IrrlichtDevice::activateJoysticks() +//! Information on a joystick, returned from @ref irr::IrrlichtDevice::activateJoysticks() struct SJoystickInfo { //! The ID of the joystick diff --git a/include/IFileArchive.h b/include/IFileArchive.h index be5f45b3..7f0f4b2c 100644 --- a/include/IFileArchive.h +++ b/include/IFileArchive.h @@ -55,7 +55,7 @@ public: or 0 on failure. */ virtual IReadFile* createAndOpenFile(const path& filename) =0; - //! Opens a file based on its position. + //! Opens a file based on its position in the file list. /** Creates and returns \param index The zero based index of the file. \return Returns a pointer to the created file on success, or 0 on failure. */ @@ -80,7 +80,7 @@ class IArchiveLoader : public virtual IReferenceCounted public: //! Check if the file might be loaded by this class /** Check based on the file extension (e.g. ".zip") - \param fileName Name of file to check. + \param filename Name of file to check. \return True if file seems to be loadable. */ virtual bool isALoadableFileFormat(const path& filename) const =0; @@ -97,12 +97,16 @@ public: virtual bool isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const =0; //! Creates an archive from the filename - /** \param file File handle to check. + /** \param filename File to use. + \param ignoreCase Searching is performed without regarding the case + \param ignorePaths Files are searched for without checking for the directories \return Pointer to newly created archive, or 0 upon error. */ virtual IFileArchive* createArchive(const path& filename, bool ignoreCase, bool ignorePaths) const =0; //! Creates an archive from the file - /** \param file File handle to check. + /** \param file File handle to use. + \param ignoreCase Searching is performed without regarding the case + \param ignorePaths Files are searched for without checking for the directories \return Pointer to newly created archive, or 0 upon error. */ virtual IFileArchive* createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const =0; }; diff --git a/include/IFileList.h b/include/IFileList.h index c6a1bf8c..376d2e62 100644 --- a/include/IFileList.h +++ b/include/IFileList.h @@ -33,7 +33,7 @@ public: //! Gets the full name of a file in the list including the path, based on an index. /** \param index is the zero based index of the file which name should be returned. The index must be less than the amount getFileCount() returns. - \return File name of the file. Returns 0, if an error occured. */ + \return File name of the file. Returns 0 if an error occured. */ virtual const io::path& getFullFileName(u32 index) const = 0; //! Returns the size of a file in the file list, based on an index. @@ -42,6 +42,15 @@ public: \return The size of the file in bytes. */ virtual u32 getFileSize(u32 index) const = 0; + //! Returns the ID of a file in the file list, based on an index. + /** This optional ID can be used to link the file list entry to information held + elsewhere. For example this could be an index in an IFileArchive, linking the entry + to its data offset, uncompressed size and CRC. + \param index is the zero based index of the file which should be returned. + The index must be less than the amount getFileCount() returns. + \return The ID of the file. */ + virtual u32 getID(u32 index) const = 0; + //! Check if the file is a directory /** \param index The zero based index which will be checked. The index must be less than the amount getFileCount() returns. @@ -58,6 +67,16 @@ public: //! Returns the base path of the file list virtual const io::path& getPath() const = 0; + + //! Add as a file or folder to the list + /** \param fullPath The file name including path, from the root of the file list. + \param isDirectory True if this is a directory rather than a file. + \param size The size of the file in bytes. + \param id The ID of the file in the archive which owns it */ + virtual u32 addItem(const io::path& fullPath, u32 size, bool isDirectory, u32 id=0) = 0; + + //! Sorts the file list. You should call this after adding any items to the file list + virtual void sort() = 0; }; } // end namespace irr diff --git a/include/IFileSystem.h b/include/IFileSystem.h index 36a2ba47..8af22568 100644 --- a/include/IFileSystem.h +++ b/include/IFileSystem.h @@ -125,9 +125,10 @@ public: virtual bool removeFileArchive(u32 index) =0; //! Removes an archive from the file system. - /** This will close the archive and free any file handles, but will not close resources which have already - been loaded and are now cached, for example textures and meshes. - \param index: The index of the archive to remove + /** This will close the archive and free any file handles, but will not + close resources which have already been loaded and are now cached, for + example textures and meshes. + \param filename The archive of the given name will be removed \return Returns true on success, false on failure */ virtual bool removeFileArchive(const path& filename) =0; @@ -226,6 +227,12 @@ public: See IReferenceCounted::drop() for more information. */ virtual IFileList* createFileList() =0; + //! Creates an empty filelist + /** \return a Pointer to the created IFileList is returned. After the list has been used + it has to be deleted using its IFileList::drop() method. + See IReferenceCounted::drop() for more information. */ + virtual IFileList* createEmptyFileList(const io::path& path, bool ignoreCase, bool ignorePaths) =0; + //! Set the active type of file system. virtual EFileSystemType setFileListSystem(EFileSystemType listType) =0; diff --git a/include/IGUIContextMenu.h b/include/IGUIContextMenu.h index 5d8cb3c2..4dbc3dca 100644 --- a/include/IGUIContextMenu.h +++ b/include/IGUIContextMenu.h @@ -11,6 +11,21 @@ namespace irr { namespace gui { + //! Close behaviour. + //! Default is ECMC_REMOVE + enum ECONTEXT_MENU_CLOSE + { + //! do nothing - menu stays open + ECMC_IGNORE = 0, + + //! remove the gui element + ECMC_REMOVE = 1, + + //! call setVisible(false) + ECMC_HIDE = 2, + + // note to implementors - this is planned as bitset, so continue with 4 if you need to add further flags. + }; //! GUI Context menu interface. class IGUIContextMenu : public IGUIElement @@ -21,6 +36,12 @@ namespace gui IGUIContextMenu(IGUIEnvironment* environment, IGUIElement* parent, s32 id, core::rect rectangle) : IGUIElement(EGUIET_CONTEXT_MENU, environment, parent, id, rectangle) {} + //! set behaviour when menus are closed + virtual void setCloseHandling(ECONTEXT_MENU_CLOSE onClose) = 0; + + //! get current behaviour when the menue will be closed + virtual ECONTEXT_MENU_CLOSE getCloseHandling() const = 0; + //! Get amount of menu items virtual u32 getItemCount() const = 0; @@ -36,7 +57,30 @@ namespace gui \param checked: Specifies if the menu item should be initially checked. \return Returns the index of the new item */ virtual u32 addItem(const wchar_t* text, s32 commandId=-1, bool enabled=true, - bool hasSubMenu=false, bool checked=false) = 0; + bool hasSubMenu=false, bool checked=false, bool autoChecking=false) = 0; + + //! Insert a menu item at specified position. + /** \param idx: Position to insert the new element, + should be smaller than itemcount otherwise the item is added to the end. + \param text: Text of menu item. Set this to 0 to create + an separator instead of a real item, which is the same like + calling addSeparator(); + \param commandId: Command id of menu item, a simple id you may + set to whatever you want. + \param enabled: Specifies if the menu item should be enabled. + \param hasSubMenu: Set this to true if there should be a submenu + at this item. You can acess this submenu via getSubMenu(). + \param checked: Specifies if the menu item should be initially checked. + \return Returns the index of the new item */ + virtual u32 insertItem(u32 idx, const wchar_t* text, s32 commandId=-1, bool enabled=true, + bool hasSubMenu=false, bool checked=false, bool autoChecking=false) = 0; + + //! Find an item by it's CommandID + /** + \param commandId: We are looking for the first item which has this commandID + \param idxStartSearch: Start searching from this index. + \return Returns the index of the item when found or otherwise -1. */ + virtual s32 findItemWithCommandId(s32 commandId, u32 idxStartSearch=0) const = 0; //! Adds a separator item to the menu virtual void addSeparator() = 0; @@ -94,6 +138,15 @@ namespace gui \param idx: Zero based index of the menu item \return Returns a pointer to the submenu of an item. */ virtual IGUIContextMenu* getSubMenu(u32 idx) const = 0; + + //! should the element change the checked status on clicking + virtual void setItemAutoChecking(u32 idx, bool autoChecking) = 0; + + //! does the element change the checked status on clicking + virtual bool getItemAutoChecking(u32 idx) const = 0; + + //! When an eventparent is set it receives events instead of the usual parent element + virtual void setEventParent(IGUIElement *parent) = 0; }; } // end namespace gui diff --git a/include/IGUIElement.h b/include/IGUIElement.h index 818e3afb..b560fdc2 100644 --- a/include/IGUIElement.h +++ b/include/IGUIElement.h @@ -252,7 +252,7 @@ public: DesiredRect.UpperLeftCorner.X += diffx/2; break; case EGUIA_SCALE: - DesiredRect.UpperLeftCorner.X = (s32)(ScaleRect.UpperLeftCorner.X * fw); + DesiredRect.UpperLeftCorner.X = core::round32(ScaleRect.UpperLeftCorner.X * fw); break; } @@ -267,7 +267,7 @@ public: DesiredRect.LowerRightCorner.X += diffx/2; break; case EGUIA_SCALE: - DesiredRect.LowerRightCorner.X = (s32)(ScaleRect.LowerRightCorner.X * fw); + DesiredRect.LowerRightCorner.X = core::round32(ScaleRect.LowerRightCorner.X * fw); break; } @@ -282,7 +282,7 @@ public: DesiredRect.UpperLeftCorner.Y += diffy/2; break; case EGUIA_SCALE: - DesiredRect.UpperLeftCorner.Y = (s32)(ScaleRect.UpperLeftCorner.Y * fh); + DesiredRect.UpperLeftCorner.Y = core::round32(ScaleRect.UpperLeftCorner.Y * fh); break; } @@ -297,7 +297,7 @@ public: DesiredRect.LowerRightCorner.Y += diffy/2; break; case EGUIA_SCALE: - DesiredRect.LowerRightCorner.Y = (s32)(ScaleRect.LowerRightCorner.Y * fh); + DesiredRect.LowerRightCorner.Y = core::round32(ScaleRect.LowerRightCorner.Y * fh); break; } diff --git a/include/IGUIEnvironment.h b/include/IGUIEnvironment.h index 50acb29a..80838913 100644 --- a/include/IGUIEnvironment.h +++ b/include/IGUIEnvironment.h @@ -149,8 +149,9 @@ public: //! Creates the image list from the given texture. - /** Loads the font if it was not loaded before. - \param filename Filename of the Font. + /** \param texture Texture to split into images + \param imageSize Dimension of each image + \param useAlphaChannel Flag whether alpha channel of the texture should be honored. \return Pointer to the font. Returns 0 if the font could not be loaded. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ @@ -240,11 +241,12 @@ public: to (EMBF_OK | EMBF_CANCEL). \param parent Parent gui element of the message box. \param id Id with which the gui element can be identified. + \param image Optional texture which will be displayed beside the text as an image \return Pointer to the created message box. Returns 0 if an error occured. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual IGUIWindow* addMessageBox(const wchar_t* caption, const wchar_t* text=0, - bool modal = true, s32 flags = EMBF_OK, IGUIElement* parent=0, s32 id=-1) = 0; + bool modal = true, s32 flags = EMBF_OK, IGUIElement* parent=0, s32 id=-1, video::ITexture* image=0) = 0; //! Adds a scrollbar. /** \param horizontal Specifies if the scroll bar is drawn horizontal @@ -313,6 +315,8 @@ public: \param parent Parent gui element of the list box. \param id Id to identify the gui element. \param drawBackground Flag whether the background should be drawn. + \param scrollBarVertical Flag whether a vertical scrollbar should be used + \param scrollBarHorizontal Flag whether a horizontal scrollbar should be used \return Pointer to the created list box. Returns 0 if an error occured. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ @@ -395,6 +399,7 @@ public: /** An edit box with up and down buttons \param text Text to be displayed. Can be altered after creation by setText(). \param rectangle Rectangle specifying the borders of the spin box. + \param border Set to true if the spin box should have a 3d border. \param parent Parent item of the element, e.g. a window. Set it to 0 to place the spin box directly in the environment. \param id The ID of the element. diff --git a/include/IGUIFont.h b/include/IGUIFont.h index 35c94ab3..e601bcd5 100644 --- a/include/IGUIFont.h +++ b/include/IGUIFont.h @@ -92,7 +92,7 @@ public: //! Define which characters should not be drawn by the font. /** For example " " would not draw any space which is usually blank in most fonts. - \param invisible: string of symbols, which are not send down to the videodriver + \param s String of symbols which are not send down to the videodriver */ virtual void setInvisibleCharacters( const wchar_t *s ) = 0; }; diff --git a/include/IGUISkin.h b/include/IGUISkin.h index d0ce8658..83c4baeb 100644 --- a/include/IGUISkin.h +++ b/include/IGUISkin.h @@ -139,9 +139,9 @@ namespace gui EGDS_WINDOW_BUTTON_WIDTH, //! width of a checkbox check EGDS_CHECK_BOX_WIDTH, - //! width of a messagebox + //! deprecated EGDS_MESSAGE_BOX_WIDTH, - //! height of a messagebox + //! deprecated EGDS_MESSAGE_BOX_HEIGHT, //! width of a default button EGDS_BUTTON_WIDTH, @@ -155,6 +155,17 @@ namespace gui EGDS_TITLEBARTEXT_DISTANCE_X, //! distance for text in the title bar, from the top of the window rect EGDS_TITLEBARTEXT_DISTANCE_Y, + //! free space in a messagebox between borders and contents on all sides + EGDS_MESSAGE_BOX_GAP_SPACE, + //! minimal space to reserve for messagebox text-width + EGDS_MESSAGE_BOX_MIN_TEXT_WIDTH, + //! maximal space to reserve for messagebox text-width + EGDS_MESSAGE_BOX_MAX_TEST_WIDTH, + //! minimal space to reserve for messagebox text-height + EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT, + //! maximal space to reserve for messagebox text-height + EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT, + //! this value is not used, it only specifies the amount of default sizes //! available. EGDS_COUNT @@ -174,7 +185,14 @@ namespace gui "ButtonHeight", "TextDistanceX", "TextDistanceY", - 0, + "TitleBarTextX", + "TitleBarTextY", + "MessageBoxGapSpace", + "MessageBoxMinTextWidth", + "MessageBoxMaxTextWidth", + "MessageBoxMinTextHeight", + "MessageBoxMaxTextHeight", + 0 }; @@ -212,7 +230,7 @@ namespace gui "WindowButtonMaximize", "WindowButtonMinimize", "WindowButtonRestore", - 0, + 0 }; //! Customizable symbols for GUI diff --git a/include/IGUITreeView.h b/include/IGUITreeView.h index 2d2cace2..bd8554d3 100644 --- a/include/IGUITreeView.h +++ b/include/IGUITreeView.h @@ -30,7 +30,7 @@ namespace gui virtual IGUITreeView* getOwner() const = 0; //! Returns the parent node of this node. - //! For the root node this will return 0. + /** For the root node this will return 0. */ virtual IGUITreeViewNode* getParent() const = 0; //! returns the text of the node @@ -79,118 +79,114 @@ namespace gui virtual bool hasChilds() const = 0; //! Adds a new node behind the last child node. - //! \param text text of the new node - //! \param icon icon text of the new node - //! \param imageIndex index of the image for the new node (-1 = none) - //! \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) - //! \param data user data (void*) of the new node - //! \param data2 user data2 (IReferenceCounted*) of the new node - //! \return - //! returns the new node + /** \param text text of the new node + \param icon icon text of the new node + \param imageIndex index of the image for the new node (-1 = none) + \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) + \param data user data (void*) of the new node + \param data2 user data2 (IReferenceCounted*) of the new node + \return The new node + */ virtual IGUITreeViewNode* addChildBack( - const wchar_t* text, - const wchar_t* icon = 0, - s32 imageIndex = -1, - s32 selectedImageIndex = -1, - void* data = 0, - IReferenceCounted* data2 = 0 ) = 0; + const wchar_t* text, + const wchar_t* icon = 0, + s32 imageIndex = -1, + s32 selectedImageIndex = -1, + void* data = 0, + IReferenceCounted* data2 = 0 ) = 0; //! Adds a new node before the first child node. - //! \param text text of the new node - //! \param icon icon text of the new node - //! \param imageIndex index of the image for the new node (-1 = none) - //! \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) - //! \param data user data (void*) of the new node - //! \param data2 user data2 (IReferenceCounted*) of the new node - //! \return - //! returns the new node + /** \param text text of the new node + \param icon icon text of the new node + \param imageIndex index of the image for the new node (-1 = none) + \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) + \param data user data (void*) of the new node + \param data2 user data2 (IReferenceCounted*) of the new node + \return The new node + */ virtual IGUITreeViewNode* addChildFront( - const wchar_t* text, - const wchar_t* icon = 0, - s32 imageIndex = -1, - s32 selectedImageIndex = -1, - void* data = 0, - IReferenceCounted* data2 = 0 ) = 0; + const wchar_t* text, + const wchar_t* icon = 0, + s32 imageIndex = -1, + s32 selectedImageIndex = -1, + void* data = 0, + IReferenceCounted* data2 = 0 ) = 0; //! Adds a new node behind the other node. - //! The other node has also te be a child node from this node. - //! \param text text of the new node - //! \param icon icon text of the new node - //! \param imageIndex index of the image for the new node (-1 = none) - //! \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) - //! \param data user data (void*) of the new node - //! \param data2 user data2 (IReferenceCounted*) of the new node - //! \return - //! returns the new node or 0 if other is no child node from this + /** The other node has also te be a child node from this node. + \param other Node to insert after + \param text text of the new node + \param icon icon text of the new node + \param imageIndex index of the image for the new node (-1 = none) + \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) + \param data user data (void*) of the new node + \param data2 user data2 (IReferenceCounted*) of the new node + \return The new node or 0 if other is no child node from this + */ virtual IGUITreeViewNode* insertChildAfter( - IGUITreeViewNode* other, - const wchar_t* text, - const wchar_t* icon = 0, - s32 imageIndex = -1, - s32 selectedImageIndex = -1, - void* data = 0, - IReferenceCounted* data2 = 0 ) = 0; + IGUITreeViewNode* other, + const wchar_t* text, + const wchar_t* icon = 0, + s32 imageIndex = -1, + s32 selectedImageIndex = -1, + void* data = 0, + IReferenceCounted* data2 = 0 ) = 0; //! Adds a new node before the other node. - //! The other node has also te be a child node from this node. - //! \param text text of the new node - //! \param icon icon text of the new node - //! \param imageIndex index of the image for the new node (-1 = none) - //! \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) - //! \param data user data (void*) of the new node - //! \param data2 user data2 (IReferenceCounted*) of the new node - //! \return - //! returns the new node or 0 if other is no child node from this + /** The other node has also te be a child node from this node. + \param other Node to insert before + \param text text of the new node + \param icon icon text of the new node + \param imageIndex index of the image for the new node (-1 = none) + \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) + \param data user data (void*) of the new node + \param data2 user data2 (IReferenceCounted*) of the new node + \return The new node or 0 if other is no child node from this + */ virtual IGUITreeViewNode* insertChildBefore( - IGUITreeViewNode* other, - const wchar_t* text, - const wchar_t* icon = 0, - s32 imageIndex = -1, - s32 selectedImageIndex = -1, - void* data = 0, - IReferenceCounted* data2 = 0 ) = 0; + IGUITreeViewNode* other, + const wchar_t* text, + const wchar_t* icon = 0, + s32 imageIndex = -1, + s32 selectedImageIndex = -1, + void* data = 0, + IReferenceCounted* data2 = 0 ) = 0; //! Return the first child node from this node. - //! \return - //! Returns the first child node or 0 if this node has no childs. + /** \return The first child node or 0 if this node has no childs. */ virtual IGUITreeViewNode* getFirstChild() const = 0; //! Return the last child node from this node. - //! \return - //! Returns the last child node or 0 if this node has no childs. + /** \return The last child node or 0 if this node has no childs. */ virtual IGUITreeViewNode* getLastChild() const = 0; - //! Returns the preverse sibling node from this node. - //! \return - //! Returns the preverse sibling node from this node or 0 if this is - //! the first node from the parent node. + //! Returns the previous sibling node from this node. + /** \return The previous sibling node from this node or 0 if this is + the first node from the parent node. + */ virtual IGUITreeViewNode* getPrevSibling() const = 0; //! Returns the next sibling node from this node. - //! \return - //! Returns the next sibling node from this node or 0 if this is - //! the last node from the parent node. + /** \return The next sibling node from this node or 0 if this is + the last node from the parent node. + */ virtual IGUITreeViewNode* getNextSibling() const = 0; //! Returns the next visible (expanded, may be out of scrolling) node from this node. - //! \return - //! Returns the next visible node from this node or 0 if this is - //! the last visible node. + /** \return The next visible node from this node or 0 if this is + the last visible node. */ virtual IGUITreeViewNode* getNextVisible() const = 0; //! Deletes a child node. - // \return - //! Returns true if the node was found as a child and is deleted. + /** \return Returns true if the node was found as a child and is deleted. */ virtual bool deleteChild( IGUITreeViewNode* child ) = 0; //! Moves a child node one position up. - //! \return - //! Returns true if the node was found as achild node and was not already the first child. + /** \return True if the node was found as achild node and was not already the first child. */ virtual bool moveChildUp( IGUITreeViewNode* child ) = 0; //! Moves a child node one position down. - //! \return - //! Returns true if the node was found as achild node and was not already the last child. + /** \return True if the node was found as achild node and was not already the last child. */ virtual bool moveChildDown( IGUITreeViewNode* child ) = 0; //! Returns true if the node is expanded (childs are visible). @@ -209,7 +205,7 @@ namespace gui virtual bool isRoot() const = 0; //! Returns the level of this node. - //! The root node has level 0. Direct childs of the root has level 1 ... + /** The root node has level 0. Direct childs of the root has level 1 ... */ virtual s32 getLevel() const = 0; //! Returns true if this node is visible (all parents are expanded). @@ -218,9 +214,10 @@ namespace gui //! Default tree view GUI element. - //! Displays a windows like tree buttons to expand/collaps the child nodes of an node - //! and optional tree lines. - //! Each node consits of an text, an icon text and a void pointer for user data. + /** Displays a windows like tree buttons to expand/collaps the child nodes of an node + and optional tree lines. + Each node consits of an text, an icon text and a void pointer for user data. + */ class IGUITreeView : public IGUIElement { public: @@ -242,17 +239,20 @@ namespace gui virtual bool getLinesVisible() const = 0; //! sets if the tree lines are visible - //! \param visible true for visible, false for invisible + /** \param visible true for visible, false for invisible */ virtual void setLinesVisible( bool visible ) = 0; - //! Sets the font which should be used as icon font. This font is set to the Irrlicht engine - //! built-in-font by default. Icons can be displayed in front of every list item. - //! An icon is a string, displayed with the icon font. When using the build-in-font of the - //! Irrlicht engine as icon font, the icon strings defined in GUIIcons.h can be used. + //! Sets the font which should be used as icon font. + /** This font is set to the Irrlicht engine built-in-font by + default. Icons can be displayed in front of every list item. + An icon is a string, displayed with the icon font. When using + the build-in-font of the Irrlicht engine as icon font, the icon + strings defined in GUIIcons.h can be used. + */ virtual void setIconFont( IGUIFont* font ) = 0; //! Sets the image list which should be used for the image and selected image of every node. - //! The default is 0 (no images). + /** The default is 0 (no images). */ virtual void setImageList( IGUIImageList* imageList ) = 0; //! Returns the image list which is used for the nodes. @@ -265,7 +265,7 @@ namespace gui virtual bool getImageLeftOfIcon() const = 0; //! Returns the node which is associated to the last event. - //! This pointer is only valid inside the OnEvent call! + /** This pointer is only valid inside the OnEvent call! */ virtual IGUITreeViewNode* getLastEventNode() const = 0; }; diff --git a/include/IImageLoader.h b/include/IImageLoader.h index 09258b3f..a9163582 100644 --- a/include/IImageLoader.h +++ b/include/IImageLoader.h @@ -29,7 +29,7 @@ public: //! Check if the file might be loaded by this class /** Check is based on the file extension (e.g. ".tga") - \param fileName Name of file to check. + \param filename Name of file to check. \return True if file seems to be loadable. */ virtual bool isALoadableFileExtension(const io::path& filename) const = 0; diff --git a/include/IImageWriter.h b/include/IImageWriter.h index 1c49f3c8..bbdaafd3 100644 --- a/include/IImageWriter.h +++ b/include/IImageWriter.h @@ -26,7 +26,7 @@ class IImageWriter : public IReferenceCounted { public: //! Check if this writer can write a file with the given extension - /** \param fileName Name of the file to check. + /** \param filename Name of the file to check. \return True if file extension specifies a writable type. */ virtual bool isAWriteableFileExtension(const io::path& filename) const = 0; diff --git a/include/ILightManager.h b/include/ILightManager.h index 274c698b..cb31e6f5 100644 --- a/include/ILightManager.h +++ b/include/ILightManager.h @@ -1,5 +1,5 @@ // Written by Colin MacDonald - all rights assigned to Nikolaus Gebhardt -// Copyright (C) 2008 Nikolaus Gebhardt +// Copyright (C) 2008-2009 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h @@ -15,43 +15,45 @@ namespace scene { class ILightSceneNode; - //! ILightManager provides an interface for user applications to manipulate the list - //! of lights in the scene. The light list can be trimmed or re-ordered before device/ - //! hardware lights are created, and/or individual lights can be switched on and off - //! before or after each scene node is rendered. - //! It is assumed that the ILightManager implementation will store any data that it wishes - //! to retain, i.e. the ISceneManager to which it is assigned, the lightList, the current - //! render pass, and the current scene node. + //! ILightManager provides an interface for user applications to manipulate the list of lights in the scene. + /** The light list can be trimmed or re-ordered before device/ hardware + lights are created, and/or individual lights can be switched on and off + before or after each scene node is rendered. It is assumed that the + ILightManager implementation will store any data that it wishes to + retain, i.e. the ISceneManager to which it is assigned, the lightList, + the current render pass, and the current scene node. */ class ILightManager : public IReferenceCounted { public: //! Called after the scene's light list has been built, but before rendering has begun. - //! As actual device/hardware lights are not created until the ESNRP_LIGHT render pass, - //! this provides an opportunity for the light manager to trim or re-order the light - //! list, before any device/hardware lights have actually been created. - //! \param[in] smgr: the Scene Manager - //! \param[inout] lightLight: the Scene Manager's light list, which the light manager may - //! modify. This reference will remain valid until OnPostRender(). + /** As actual device/hardware lights are not created until the + ESNRP_LIGHT render pass, this provides an opportunity for the + light manager to trim or re-order the light list, before any + device/hardware lights have actually been created. + \param lightList: the Scene Manager's light list, which + the light manager may modify. This reference will remain valid + until OnPostRender(). + */ virtual void OnPreRender(core::array & lightList) = 0; //! Called after the last scene node is rendered. - //! After this call returns, the lightList passed to OnPreRender() becomes invalid. + /** After this call returns, the lightList passed to OnPreRender() becomes invalid. */ virtual void OnPostRender(void) = 0; //! Called before a render pass begins - //! \param[in] renderPass: the render pass that's about to begin + /** \param renderPass: the render pass that's about to begin */ virtual void OnRenderPassPreRender(E_SCENE_NODE_RENDER_PASS renderPass) = 0; //! Called after the render pass specified in OnRenderPassPreRender() ends - //! \param[in] renderPass: the render pass that has finished + /** \param[in] renderPass: the render pass that has finished */ virtual void OnRenderPassPostRender(E_SCENE_NODE_RENDER_PASS renderPass) = 0; //! Called before the given scene node is rendered - //! \param[in] node: the scene node that's about to be rendered + /** \param[in] node: the scene node that's about to be rendered */ virtual void OnNodePreRender(ISceneNode* node) = 0; //! Called after the the node specified in OnNodePreRender() has been rendered - //! \param[in] node: the scene node that has just been rendered + /** \param[in] node: the scene node that has just been rendered */ virtual void OnNodePostRender(ISceneNode* node) = 0; }; } // end namespace scene diff --git a/include/IMeshLoader.h b/include/IMeshLoader.h index 323c2740..34964578 100644 --- a/include/IMeshLoader.h +++ b/include/IMeshLoader.h @@ -33,7 +33,7 @@ public: //! Returns true if the file might be loaded by this class. /** This decision should be based on the file extension (e.g. ".cob") only. - \param fileName Name of the file to test. + \param filename Name of the file to test. \return True if the file might be loaded by this class. */ virtual bool isALoadableFileExtension(const io::path& filename) const = 0; diff --git a/include/IMeshManipulator.h b/include/IMeshManipulator.h index 3185407f..0532afee 100644 --- a/include/IMeshManipulator.h +++ b/include/IMeshManipulator.h @@ -70,7 +70,7 @@ namespace scene virtual void scale(IMeshBuffer* buffer, const core::vector3df& factor) const = 0; //! Scales the actual mesh, not a scene node. - /** \deprecated + /** \deprecated Use scale() instead \param mesh Mesh on which the operation is performed. \param factor Scale factor for each axis. */ virtual void scaleMesh(IMesh* mesh, const core::vector3df& factor) const {return scale(mesh,factor);} @@ -98,7 +98,7 @@ namespace scene virtual void transform(IMeshBuffer* buffer, const core::matrix4& m) const = 0; //! Applies a transformation to a mesh - /** \deprecated + /** \deprecated Use transform() instead \param mesh Mesh on which the operation is performed. \param m transformation matrix. */ virtual void transformMesh(IMesh* mesh, const core::matrix4& m) const {return transform(mesh,m);} @@ -128,7 +128,7 @@ namespace scene //! Creates a planar texture mapping on the meshbuffer /** This method is currently implemented towards the LWO planar mapping. A more general biasing might be required. - \param meshbuffer Buffer on which the operation is performed. + \param buffer Buffer on which the operation is performed. \param resolutionS Resolution of the planar mapping in horizontal direction. This is the ratio between object space and texture space. \param resolutionT Resolution of the planar mapping in vertical direction. This is the ratio between object space and texture space. \param axis The axis along which the texture is projected. The allowed values are 0 (X), 1(Y), and 2(Z). diff --git a/include/ISceneCollisionManager.h b/include/ISceneCollisionManager.h index a0324ae8..e8093b0d 100644 --- a/include/ISceneCollisionManager.h +++ b/include/ISceneCollisionManager.h @@ -26,7 +26,7 @@ namespace scene public: //! Finds the collision point of a line and lots of triangles, if there is one. - /** \param ray: Line with witch collisions are tested. + /** \param ray: Line with which collisions are tested. \param selector: TriangleSelector containing the triangles. It can be created for example using ISceneManager::createTriangleSelector() or @@ -58,6 +58,7 @@ namespace scene movement of the ellipsoid. \param triout: Optional parameter where the last triangle causing a collision is stored, if there is a collision. + \param hitPosition: Return value for the position of the collision \param outFalling: Is set to true if the ellipsoid is falling down, caused by gravity. \param outNode: the node with which the ellipoid collided (if any) diff --git a/include/ISceneManager.h b/include/ISceneManager.h index ed2e3786..630e602a 100644 --- a/include/ISceneManager.h +++ b/include/ISceneManager.h @@ -209,7 +209,7 @@ namespace scene * a dummy object in this mode. Meshes included in the * scene will be added into the scene manager with the * following naming scheme: - * path/to/file/file.dea#meshname. The loading of such + * "path/to/file/file.dea#meshname". The loading of such * meshes is logged. Currently, this loader is able to * create meshes (made of only polygons), lights, and * cameras. Materials and animations are currently not @@ -398,13 +398,12 @@ namespace scene const core::vector3df& rotation = core::vector3df(0,0,0), const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f)) = 0; - //! Adds a test scene node for test purposes to the scene. - /** It is a simple cube of (1,1,1) size. - \param size: Size of the cube. - \param parent: Parent of the scene node. Can be NULL if no parent. + //! Adds a cube scene node + /** \param size: Size of the cube, uniformly in each dimension. + \param parent: Parent of the scene node. Can be 0 if no parent. \param id: Id of the node. This id can be used to identify the scene node. - \param position: Position of the space relative to its parent where the - scene node will be placed. + \param position: Position of the space relative to its parent + where the scene node will be placed. \param rotation: Initital rotation of the scene node. \param scale: Initial scale of the scene node. \return Pointer to the created test scene node. This @@ -415,14 +414,14 @@ namespace scene const core::vector3df& rotation = core::vector3df(0,0,0), const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f)) = 0; - //! Adds a sphere scene node for test purposes to the scene. - /** It is a simple sphere. - \param radius: Radius of the sphere. - \param polyCount: Polycount of the sphere. - \param parent: Parent of the scene node. Can be NULL if no parent. + //! Adds a sphere scene node of the given radius and detail + /** \param radius: Radius of the sphere. + \param polyCount: Polycount of the sphere, i.e. subdivision in + horizontal and vertical direction. + \param parent: Parent of the scene node. Can be 0 if no parent. \param id: Id of the node. This id can be used to identify the scene node. - \param position: Position of the space relative to its parent where the - scene node will be placed. + \param position: Position of the space relative to its parent + where the scene node will be placed. \param rotation: Initital rotation of the scene node. \param scale: Initial scale of the scene node. \return Pointer to the created test scene node. This @@ -537,25 +536,31 @@ namespace scene \param parent: Parent scene node of the camera. Can be null. If the parent moves, the camera will move too. \param id: id of the camera. This id can be used to identify the camera. + \param makeActive Flag whether this camera should become the active one. + Make sure you always have one active camera. \return Pointer to interface to camera if successful, otherwise 0. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ICameraSceneNode* addCameraSceneNode(ISceneNode* parent = 0, const core::vector3df& position = core::vector3df(0,0,0), - const core::vector3df& lookat = core::vector3df(0,0,100), s32 id=-1) = 0; + const core::vector3df& lookat = core::vector3df(0,0,100), + s32 id=-1, bool makeActive=true) = 0; //! Adds a maya style user controlled camera scene node to the scene graph. /** This is a standard camera with an animator that provides mouse control similar - to camera in the 3D Software Maya by Alias Wavefront. - \param parent: Parent scene node of the camera. Can be null. - \param rotateSpeed: Rotation speed of the camera. - \param zoomSpeed: Zoom speed of the camera. - \param translationSpeed: TranslationSpeed of the camera. - \param id: id of the camera. This id can be used to identify the camera. - \return Returns a pointer to the interface of the camera if successful, otherwise 0. - This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ + to camera in the 3D Software Maya by Alias Wavefront. + \param parent: Parent scene node of the camera. Can be null. + \param rotateSpeed: Rotation speed of the camera. + \param zoomSpeed: Zoom speed of the camera. + \param translationSpeed: TranslationSpeed of the camera. + \param id: id of the camera. This id can be used to identify the camera. + \param makeActive Flag whether this camera should become the active one. + Make sure you always have one active camera. + \return Returns a pointer to the interface of the camera if successful, otherwise 0. + This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ICameraSceneNode* addCameraSceneNodeMaya(ISceneNode* parent = 0, f32 rotateSpeed = -1500.0f, f32 zoomSpeed = 200.0f, - f32 translationSpeed = 1500.0f, s32 id=-1) = 0; + f32 translationSpeed = 1500.0f, s32 id=-1, + bool makeActive=true) = 0; //! Adds a camera scene node with an animator which provides mouse and keyboard control appropriate for first person shooters (FPS). /** This FPS camera is intended to provide a demonstration of a @@ -616,13 +621,16 @@ namespace scene up when the mouse is moved down and down when the mouse is moved up, the default is 'false' which means it will follow the movement of the mouse cursor. + \param makeActive Flag whether this camera should become the active one. + Make sure you always have one active camera. \return Pointer to the interface of the camera if successful, otherwise 0. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ICameraSceneNode* addCameraSceneNodeFPS(ISceneNode* parent = 0, f32 rotateSpeed = 100.0f, f32 moveSpeed = 0.5f, s32 id=-1, SKeyMap* keyMapArray=0, s32 keyMapSize=0, bool noVerticalMovement=false, - f32 jumpSpeed = 0.f, bool invertMouse=false) = 0; + f32 jumpSpeed = 0.f, bool invertMouse=false, + bool makeActive=true) = 0; //! Adds a dynamic light scene node to the scene graph. /** The light will cast dynamic light on all @@ -692,6 +700,7 @@ namespace scene \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. + \param radius The Radius of the sphere \param parent: Parent scene node of the dome. A dome usually has no parent, so this should be null. Note: If a parent is set, the dome will not change how it is drawn. @@ -1098,6 +1107,8 @@ namespace scene move from the start point to the end point. \param loop: If set to false, the node stops when the end point is reached. If loop is true, the node begins again at the start. + \param pingpong Flag to set whether the animator should fly + back from end to start again. \return The animator. Attach it to a scene node with ISceneNode::addAnimator() and the animator will animate it. If you no longer need the animator, you should call ISceneNodeAnimator::drop(). @@ -1199,10 +1210,11 @@ namespace scene virtual ITriangleSelector* createTriangleSelector(IMesh* mesh, ISceneNode* node) = 0; //! Creates a simple ITriangleSelector, based on an animated mesh scene node. - //! Details of the mesh associated with the node will be extracted internally. - //! Call ITriangleSelector::update() to have the triangle selector updated based - //! on the current frame of the animated mesh scene node. - //! \param: The animated mesh scene node from which to build the selector + /** Details of the mesh associated with the node will be extracted internally. + Call ITriangleSelector::update() to have the triangle selector updated based + on the current frame of the animated mesh scene node. + \param node The animated mesh scene node from which to build the selector + */ virtual ITriangleSelector* createTriangleSelector(IAnimatedMeshSceneNode* node) = 0; diff --git a/include/IVideoDriver.h b/include/IVideoDriver.h index bde254e9..464def92 100644 --- a/include/IVideoDriver.h +++ b/include/IVideoDriver.h @@ -132,6 +132,14 @@ namespace video EFT_FOG_EXP2 }; + const c8* const FogTypeNames[] = + { + "FogExp", + "FogLinear", + "FogExp2", + 0 + }; + struct SOverrideMaterial { //! The Material values @@ -396,7 +404,7 @@ namespace video example in picture edit programs. To avoid this problem, you could use the makeColorKeyTexture method, which takes the position of a pixel instead a color value. - \param \deprecated zeroTexels If set to true, then any texels that match + \param zeroTexels \deprecated If set to true, then any texels that match the color key will have their color, as well as their alpha, set to zero (i.e. black). This behaviour matches the legacy (buggy) behaviour prior to release 1.5 and is provided for backwards compatibility only.*/ @@ -412,7 +420,7 @@ namespace video \param colorKeyPixelPos Position of a pixel with the color key color. Every texel with this color will become fully transparent as described above. - \param \deprecated zeroTexels If set to true, then any texels that match + \param zeroTexels \deprecated If set to true, then any texels that match the color key will have their color, as well as their alpha, set to zero (i.e. black). This behaviour matches the legacy (buggy) behaviour prior to release 1.5 and is provided for backwards compatibility only.*/ @@ -548,8 +556,11 @@ namespace video \param vertexCount Amount of vertices in the array. \param indexList Pointer to array of indices. \param triangleCount Amount of Triangles. Usually amount of indices / 3. */ - virtual void drawIndexedTriangleList(const S3DVertex* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) =0; + void drawIndexedTriangleList(const S3DVertex* vertices, + u32 vertexCount, const u16* indexList, u32 triangleCount) + { + drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_STANDARD, scene::EPT_TRIANGLES, EIT_16BIT); + } //! Draws an indexed triangle list. /** Note that there may be at maximum 65536 vertices, because @@ -560,8 +571,11 @@ namespace video \param vertexCount Amount of vertices in the array. \param indexList Pointer to array of indices. \param triangleCount Amount of Triangles. Usually amount of indices / 3. */ - virtual void drawIndexedTriangleList(const S3DVertex2TCoords* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) =0; + void drawIndexedTriangleList(const S3DVertex2TCoords* vertices, + u32 vertexCount, const u16* indexList, u32 triangleCount) + { + drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_2TCOORDS, scene::EPT_TRIANGLES, EIT_16BIT); + } //! Draws an indexed triangle list. /** Note that there may be at maximum 65536 vertices, because @@ -572,8 +586,11 @@ namespace video \param vertexCount Amount of vertices in the array. \param indexList Pointer to array of indices. \param triangleCount Amount of Triangles. Usually amount of indices / 3. */ - virtual void drawIndexedTriangleList(const S3DVertexTangents* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) =0; + void drawIndexedTriangleList(const S3DVertexTangents* vertices, + u32 vertexCount, const u16* indexList, u32 triangleCount) + { + drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_TANGENTS, scene::EPT_TRIANGLES, EIT_16BIT); + } //! Draws an indexed triangle fan. /** Note that there may be at maximum 65536 vertices, because @@ -584,8 +601,11 @@ namespace video \param vertexCount Amount of vertices in the array. \param indexList Pointer to array of indices. \param triangleCount Amount of Triangles. Usually amount of indices - 2. */ - virtual void drawIndexedTriangleFan(const S3DVertex* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) =0; + void drawIndexedTriangleFan(const S3DVertex* vertices, + u32 vertexCount, const u16* indexList, u32 triangleCount) + { + drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT); + } //! Draws an indexed triangle fan. /** Note that there may be at maximum 65536 vertices, because @@ -596,18 +616,36 @@ namespace video \param vertexCount Amount of vertices in the array. \param indexList Pointer to array of indices. \param triangleCount Amount of Triangles. Usually amount of indices - 2. */ - virtual void drawIndexedTriangleFan(const S3DVertex2TCoords* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) =0; + void drawIndexedTriangleFan(const S3DVertex2TCoords* vertices, + u32 vertexCount, const u16* indexList, u32 triangleCount) + { + drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_2TCOORDS, scene::EPT_TRIANGLE_FAN, EIT_16BIT); + } + + //! Draws an indexed triangle fan. + /** Note that there may be at maximum 65536 vertices, because + the index list is an array of 16 bit values each with a maximum + value of 65536. If there are more than 65536 vertices in the + list, results of this operation are not defined. + \param vertices Pointer to array of vertices. + \param vertexCount Amount of vertices in the array. + \param indexList Pointer to array of indices. + \param triangleCount Amount of Triangles. Usually amount of indices - 2. */ + void drawIndexedTriangleFan(const S3DVertexTangents* vertices, + u32 vertexCount, const u16* indexList, u32 triangleCount) + { + drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_TANGENTS, scene::EPT_TRIANGLE_FAN, EIT_16BIT); + } //! Draws a 3d line. /** For some implementations, this method simply calls - drawIndexedTriangles for some triangles. + drawVertexPrimitiveList for some triangles. Note that the line is drawn using the current transformation matrix and material. So if you need to draw the 3D line independently of the current transformation, use \code - driver->setMaterial(unlitMaterial); - driver->setTransform(video::ETS_WORLD, core::matrix4()); + driver->setMaterial(someMaterial); + driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); \endcode for some properly set up material before drawing the line. Some drivers support line thickness set in the material. @@ -618,15 +656,15 @@ namespace video const core::vector3df& end, SColor color = SColor(255,255,255,255)) =0; //! Draws a 3d triangle. - /** This method calls drawIndexedTriangles for some triangles. + /** This method calls drawVertexPrimitiveList for some triangles. This method works with all drivers because it simply calls - drawIndexedTriangleList but it is hence not very fast. + drawVertexPrimitiveList, but it is hence not very fast. Note that the triangle is drawn using the current transformation matrix and material. So if you need to draw it independently of the current transformation, use \code - driver->setMaterial(unlitMaterial); - driver->setTransform(video::ETS_WORLD, core::matrix4()); + driver->setMaterial(someMaterial); + driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); \endcode for some properly set up material before drawing the triangle. \param triangle The triangle to draw. @@ -640,8 +678,8 @@ namespace video matrix and material. So if you need to draw it independently of the current transformation, use \code - driver->setMaterial(unlitMaterial); - driver->setTransform(video::ETS_WORLD, core::matrix4()); + driver->setMaterial(someMaterial); + driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); \endcode for some properly set up material before drawing the box. \param box The axis aligned box to draw @@ -864,8 +902,7 @@ namespace video /** These are global values attached to each 3d object rendered, which has the fog flag enabled in its material. \param color Color of the fog - \param linearFog Set this to true for linear fog, otherwise - exponential fog is applied. + \param fogType Type of fog used \param start Only used in linear fog mode (linearFog=true). Specifies where fog starts. \param end Only used in linear fog mode (linearFog=true). @@ -883,6 +920,11 @@ namespace video f32 start=50.0f, f32 end=100.0f, f32 density=0.01f, bool pixelFog=false, bool rangeFog=false) =0; + //! Gets the fog mode. + virtual void getFog(SColor& color, E_FOG_TYPE& fogType, + f32& start, f32& end, f32& density, + bool& pixelFog, bool& rangeFog) = 0; + //! Get the current color format of the color buffer /** \return Color format of the color buffer. */ virtual ECOLOR_FORMAT getColorFormat() const =0; @@ -965,7 +1007,7 @@ namespace video //! Returns the maximum amount of primitives /** (mostly vertices) which the device is able to render with - one drawIndexedTriangleList call. + one drawVertexPrimitiveList call. \return Maximum amount of primitives. */ virtual u32 getMaximalPrimitiveCount() const =0; @@ -1253,6 +1295,9 @@ namespace video Use the SceneManager attribute to set this value from your app. \param flag Default behavior is to disable ZWrite, i.e. false. */ virtual void setAllowZWriteOnTransparent(bool flag) =0; + + //! Returns the maximum texture size supported. + virtual core::dimension2du getMaxTextureSize() const =0; }; } // end namespace video diff --git a/include/IrrCompileConfig.h b/include/IrrCompileConfig.h index f18ac63a..301ee609 100644 --- a/include/IrrCompileConfig.h +++ b/include/IrrCompileConfig.h @@ -11,8 +11,8 @@ #define IRRLICHT_VERSION_REVISION 0 // This flag will be defined only in SVN, the official release code will have // it undefined -#define IRRLICHT_VERSION_SVN -#define IRRLICHT_SDK_VERSION "1.6-SVN" +//#define IRRLICHT_VERSION_SVN +#define IRRLICHT_SDK_VERSION "1.6" #include // TODO: Although included elsewhere this is required at least for mingw @@ -92,10 +92,7 @@ #endif #if !defined(_IRR_WINDOWS_API_) && !defined(_IRR_OSX_PLATFORM_) -#if defined(__sparc__) || defined(__sun__) -#define __BIG_ENDIAN__ -#define _IRR_SOLARIS_PLATFORM_ -#else +#ifndef _IRR_SOLARIS_PLATFORM_ #define _IRR_LINUX_PLATFORM_ #endif #define _IRR_POSIX_API_ @@ -126,7 +123,7 @@ headers, e.g. Summer 2004. This is a Microsoft issue, not an Irrlicht one. #if defined(_IRR_WINDOWS_API_) && (!defined(__GNUC__) || defined(IRR_COMPILE_WITH_DX9_DEV_PACK)) //! Only define _IRR_COMPILE_WITH_DIRECT3D_8_ if you have an appropriate DXSDK, e.g. Summer 2004 -//#define _IRR_COMPILE_WITH_DIRECT3D_8_ +#define _IRR_COMPILE_WITH_DIRECT3D_8_ #define _IRR_COMPILE_WITH_DIRECT3D_9_ #endif @@ -509,6 +506,10 @@ precision will be lower but speed higher. currently X86 only #undef _IRR_WCHAR_FILESYSTEM #endif +#if defined(__sparc__) || defined(__sun__) +#define __BIG_ENDIAN__ +#endif + #if defined(_IRR_SOLARIS_PLATFORM_) #undef _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ #endif diff --git a/include/SColor.h b/include/SColor.h index 6fe2ae89..400a88a3 100644 --- a/include/SColor.h +++ b/include/SColor.h @@ -191,13 +191,19 @@ namespace video 0 means no blue, 255 means full blue. */ u32 getBlue() const { return color & 0xff; } - //! Returns the luminance of the color. + //! Get lightness of the color in the range [0,255] + f32 getLightness() const + { + return 0.5f*(core::max_(core::max_(getRed(),getGreen()),getBlue())+core::min_(core::min_(getRed(),getGreen()),getBlue())); + } + + //! Get luminance of the color in the range [0,255]. f32 getLuminance() const { return 0.3f*getRed() + 0.59f*getGreen() + 0.11f*getBlue(); } - //! Returns the average intensity of the color. + //! Get average intensity of the color in the range [0,255]. u32 getAverage() const { return ( getRed() + getGreen() + getBlue() ) / 3; diff --git a/include/SViewFrustum.h b/include/SViewFrustum.h index dc5b330c..08eae824 100644 --- a/include/SViewFrustum.h +++ b/include/SViewFrustum.h @@ -85,7 +85,7 @@ namespace scene const core::matrix4& getTransform( video::E_TRANSFORMATION_STATE state) const; //! clips a line to the view frustum. - //! \Return: Returns true if the line was clipped, false if not + /** \return True if the line was clipped, false if not */ bool clipLine(core::line3d& line) const; //! the position of the camera diff --git a/include/irrArray.h b/include/irrArray.h index aae8195f..3d21f641 100644 --- a/include/irrArray.h +++ b/include/irrArray.h @@ -96,7 +96,7 @@ public: //! set a new allocation strategy /** if the maximum size of the array is unknown, you can define how big the allocation should happen. - \param element: newStratgey to applay to this array. */ + \param newStrategy New strategy to apply to this array. */ void setAllocStrategy ( eAllocStrategy newStrategy = ALLOC_STRATEGY_DOUBLE ) { strategy = newStrategy; diff --git a/include/irrMath.h b/include/irrMath.h index aeef0e1d..5c40c70b 100644 --- a/include/irrMath.h +++ b/include/irrMath.h @@ -38,7 +38,7 @@ namespace core //! Rounding error constant often used when comparing f32 values. - const s32 ROUNDING_ERROR_S32 = 1; + const s32 ROUNDING_ERROR_S32 = 0; const f32 ROUNDING_ERROR_f32 = 0.000001f; const f64 ROUNDING_ERROR_f64 = 0.00000001; @@ -450,7 +450,6 @@ namespace core // calculate: 1 / x REALINLINE f32 reciprocal( const f32 f ) { - _IRR_DEBUG_BREAK_IF(f==0.f); //divide by zero #if defined (IRRLICHT_FAST_MATH) // SSE Newton-Raphson reciprocal estimate, accurate to 23 significant @@ -484,7 +483,6 @@ namespace core // calculate: 1 / x REALINLINE f64 reciprocal ( const f64 f ) { - _IRR_DEBUG_BREAK_IF(f==0.); //divide by zero return 1.0 / f; } @@ -492,7 +490,6 @@ namespace core // calculate: 1 / x, low precision allowed REALINLINE f32 reciprocal_approxim ( const f32 f ) { - _IRR_DEBUG_BREAK_IF(f==0.f); //divide by zero #if defined( IRRLICHT_FAST_MATH) // SSE Newton-Raphson reciprocal estimate, accurate to 23 significant diff --git a/include/irrString.h b/include/irrString.h index bf0f3dab..1d0f2160 100644 --- a/include/irrString.h +++ b/include/irrString.h @@ -124,8 +124,7 @@ public: // temporary buffer for 16 numbers - c8 tmpbuf[16]; - tmpbuf[15] = 0; + c8 tmpbuf[16]={0}; u32 idx = 15; // special case '0' @@ -164,8 +163,7 @@ public: { // temporary buffer for 16 numbers - c8 tmpbuf[16]; - tmpbuf[15] = 0; + c8 tmpbuf[16]={0}; u32 idx = 15; // special case '0' @@ -222,7 +220,7 @@ public: //! Destructor - virtual ~string() + ~string() { allocator.deallocate(array); // delete [] array; } @@ -737,10 +735,11 @@ public: //! finds another string in this string /** \param str: Another string + \param start: Start position of the search \return Positions where the string has been found, or -1 if not found. */ template - s32 find(const B* const str) const + s32 find(const B* const str, const u32 start = 0) const { if (str && *str) { @@ -752,7 +751,7 @@ public: if (len > used-1) return -1; - for (u32 i=0; i toRemove) + { + u32 size = toRemove.size(); + u32 pos = 0; + u32 found = 0; + for (u32 i=0; i & characters) + { + u32 pos = 0; + u32 found = 0; + for (u32 i=0; i 1 ? array[used-2] : 0; } diff --git a/include/irrTypes.h b/include/irrTypes.h index fb565f4d..f0e3f725 100644 --- a/include/irrTypes.h +++ b/include/irrTypes.h @@ -122,12 +122,15 @@ typedef unsigned short wchar_t; namespace irr { -//! Should the wide character version of the FileSystem be used +//! Type name for character type used by the file system. +/** Should the wide character version of the FileSystem be used it is a +16 bit character variable. Used for unicode Filesystem and unicode strings. +Else it is a 8 bit character variable. Used for ansi Filesystem and non-unicode +strings +*/ #if defined(_IRR_WCHAR_FILESYSTEM) - //! 16 bit character variable. Used for unicode Filesystem and unicode strings typedef wchar_t fschar_t; #else - //! 8 bit character variable. Used for ansi Filesystem and non-unicode strings typedef char fschar_t; #endif @@ -211,8 +214,8 @@ Compiler version defines: VC6.0 : 1200, VC7.0 : 1300, VC7.1 : 1310, VC8.0 : 1400 /** some compilers can create those by directly writing the code like 'code', but some generate warnings so we use this macro here */ #define MAKE_IRR_ID(c0, c1, c2, c3) \ - ((u32)(u8)(c0) | ((u32)(u8)(c1) << 8) | \ - ((u32)(u8)(c2) << 16) | ((u32)(u8)(c3) << 24 )) + ((irr::u32)(irr::u8)(c0) | ((irr::u32)(irr::u8)(c1) << 8) | \ + ((irr::u32)(irr::u8)(c2) << 16) | ((irr::u32)(irr::u8)(c3) << 24 )) #endif // __IRR_TYPES_H_INCLUDED__ diff --git a/include/irrXML.h b/include/irrXML.h index 45738d9d..76e6f88c 100644 --- a/include/irrXML.h +++ b/include/irrXML.h @@ -17,7 +17,7 @@ Here you'll find any information you'll need to develop applications with irrXML. If you look for a tutorial on how to start, take a look at the \ref irrxmlexample, at the homepage of irrXML at www.ambiera.com/irrxml/ - or into the SDK in the directory \example. + or into the SDK in the directory example. irrXML is intended to be a high speed and easy-to-use XML Parser for C++, and this documentation is an important part of it. If you have any questions or @@ -522,7 +522,7 @@ namespace io bool deleteCallback = false); - /*! \file irrxml.h + /*! \file irrXML.h \brief Header file of the irrXML, the Irrlicht XML parser. This file includes everything needed for using irrXML, @@ -534,7 +534,7 @@ namespace io \endcode It is also common to use the two namespaces in which irrXML is included, - directly after #including irrXML.h: + directly after including irrXML.h: \code #include diff --git a/include/irrlicht.h b/include/irrlicht.h index cf4850d7..389d379b 100644 --- a/include/irrlicht.h +++ b/include/irrlicht.h @@ -184,7 +184,7 @@ #include "vector2d.h" #include "vector3d.h" -/*! \mainpage Irrlicht Engine 1.5 API documentation +/*! \mainpage Irrlicht Engine 1.6 API documentation * *
* @@ -357,7 +357,7 @@ namespace irr // THE FOLLOWING IS AN EMPTY LIST OF ALL SUB NAMESPACES // EXISTING ONLY FOR THE DOCUMENTATION SOFTWARE DOXYGEN. - //! In this namespace can be found basic classes like vectors, planes, arrays, lists and so on. + //! Basic classes such as vectors, planes, arrays, lists, and so on can be found in this namespace. namespace core { } @@ -367,14 +367,12 @@ namespace irr { } - //! This namespace provides interfaces for input/output: Reading and - //! writing files, accessing zip archives, xml files, ... + //! This namespace provides interfaces for input/output: Reading and writing files, accessing zip archives, xml files, ... namespace io { } - //! All scene management can be found in this namespace: Mesh loading, - //! special scene nodes like octrees and billboards, ... + //! All scene management can be found in this namespace: Mesh loading, special scene nodes like octrees and billboards, ... namespace scene { } @@ -391,4 +389,3 @@ namespace irr #endif - diff --git a/include/matrix4.h b/include/matrix4.h index d50366b0..a4c306dd 100644 --- a/include/matrix4.h +++ b/include/matrix4.h @@ -310,9 +310,9 @@ namespace core */ CMatrix4& buildRotateFromTo(const core::vector3df& from, const core::vector3df& to); - //! Builds a combined matrix which translate to a center before rotation and translate afterwards - /** \param from: vector to rotate from - \param to: vector to rotate to + //! Builds a combined matrix which translates to a center before rotation and translates from origin afterwards + /** \param center Position to rotate around + \param translate Translation applied after the rotation */ void setRotationCenter(const core::vector3df& center, const core::vector3df& translate); @@ -1922,9 +1922,6 @@ namespace core //! Builds a combined matrix which translate to a center before rotation and translate afterwards - /** \param from: vector to rotate from - \param to: vector to rotate to - */ template inline void CMatrix4::setRotationCenter(const core::vector3df& center, const core::vector3df& translation) { diff --git a/include/path.h b/include/path.h index 8cb7e4fa..56259d66 100644 --- a/include/path.h +++ b/include/path.h @@ -12,6 +12,8 @@ namespace irr namespace io { +//! Type used for all file system related strings. +/** This type will transparently handle different file system encodings. */ typedef core::string path; } // io diff --git a/include/position2d.h b/include/position2d.h index d2854c26..ea0850b7 100644 --- a/include/position2d.h +++ b/include/position2d.h @@ -14,6 +14,7 @@ namespace irr { namespace core { + // Use typedefs where possible as they are more explicit... //! \deprecated position2d is now a synonym for vector2d, but vector2d should be used directly. @@ -28,3 +29,4 @@ typedef vector2d position2di; #define position2d vector2d #endif // __IRR_POSITION_H_INCLUDED__ + diff --git a/include/quaternion.h b/include/quaternion.h index 568db401..fe60d949 100644 --- a/include/quaternion.h +++ b/include/quaternion.h @@ -83,7 +83,7 @@ class quaternion matrix4 getMatrix() const; //! Creates a matrix from this quaternion - void getMatrix( matrix4 &dest, const vector3df &translation ) const; + void getMatrix( matrix4 &dest, const core::vector3df &translation ) const; /*! Creates a matrix from this quaternion @@ -102,7 +102,7 @@ class quaternion lookat *= m3; */ - void getMatrixCenter( matrix4 &dest, const vector3df ¢er, const vector3df &translation ) const; + void getMatrixCenter( matrix4 &dest, const core::vector3df ¢er, const core::vector3df &translation ) const; //! Creates a matrix from this quaternion inline void getMatrix_transposed( matrix4 &dest ) const; @@ -339,10 +339,9 @@ inline void quaternion::getMatrix( matrix4 &dest, const core::vector3df ¢er m2.setInverseTranslation ( center ); lookat *= m2; */ -inline void quaternion::getMatrixCenter( matrix4 &dest, - const core::vector3df ¢er, - const core::vector3df &translation - ) const +inline void quaternion::getMatrixCenter(matrix4 &dest, + const core::vector3df ¢er, + const core::vector3df &translation) const { f32 * m = dest.pointer(); diff --git a/media/020shot.jpg b/media/020shot.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da5d98b88671008dd325d481ada43728a1c2e3c7 GIT binary patch literal 11126 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 U1@Efn+as+6m|tbQ{4@8z04QBM)Bpeg literal 0 HcmV?d00001 diff --git a/media/021shot.jpg b/media/021shot.jpg index db25117df2b6dae7af7f67043315d576810df891..48b28d7f4e8eb76ee8513913bf91243acb0efb98 100644 GIT binary patch literal 25032 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 literal 97819 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.txt ..\sed.exe -f tutorials.sed ..\..\..\examples\15.LoadIrrFile\main.cpp >>tut.txt ..\sed.exe -f tutorials.sed ..\..\..\examples\16.Quake3MapShader\main.cpp >>tut.txt -..\sed.exe -f tutorials.sed ..\..\..\examples\17.SplitScreen\main.cpp >>tut.txt +..\sed.exe -f tutorials.sed ..\..\..\examples\17.HelloWorld_Mobile\main.cpp >>tut.txt +..\sed.exe -f tutorials.sed ..\..\..\examples\18.SplitScreen\main.cpp >>tut.txt +..\sed.exe -f tutorials.sed ..\..\..\examples\19.MouseAndJoystick\main.cpp >>tut.txt +..\sed.exe -f tutorials.sed ..\..\..\examples\20.ManagedLights\main.cpp >>tut.txt +..\sed.exe -f tutorials.sed ..\..\..\examples\21.Quake3Explorer\main.cpp >>tut.txt ..\doxygen.exe doxygen.cfg diff --git a/scripts/doc/irrlicht/makedocumentation.sh b/scripts/doc/irrlicht/makedocumentation.sh index 1239855b..c430b837 100755 --- a/scripts/doc/irrlicht/makedocumentation.sh +++ b/scripts/doc/irrlicht/makedocumentation.sh @@ -1,5 +1,5 @@ rm tut.txt || true; -for i in ../../../examples/[01]*/main.cpp; do +for i in ../../../examples/[012]*/main.cpp; do sed -f tutorials.sed $i >>tut.txt; done diff --git a/source/Irrlicht/CAnimatedMeshMD2.h b/source/Irrlicht/CAnimatedMeshMD2.h index 8b552b51..663eef18 100644 --- a/source/Irrlicht/CAnimatedMeshMD2.h +++ b/source/Irrlicht/CAnimatedMeshMD2.h @@ -126,7 +126,6 @@ namespace scene virtual void calculateBoundingBox(); u32 FrameCount; - s32 TriangleCount; private: diff --git a/source/Irrlicht/CAnimatedMeshSceneNode.cpp b/source/Irrlicht/CAnimatedMeshSceneNode.cpp index 35c9ecb0..75f9aefa 100644 --- a/source/Irrlicht/CAnimatedMeshSceneNode.cpp +++ b/source/Irrlicht/CAnimatedMeshSceneNode.cpp @@ -349,6 +349,7 @@ void CAnimatedMeshSceneNode::render() { video::SMaterial debug_mat; debug_mat.Lighting = false; + debug_mat.AntiAliasing=0; driver->setMaterial(debug_mat); // show normals if (DebugDataVisible & scene::EDS_NORMALS) @@ -523,6 +524,12 @@ void CAnimatedMeshSceneNode::setAnimationSpeed(f32 framesPerSecond) } +f32 CAnimatedMeshSceneNode::getAnimationSpeed() const +{ + return FramesPerSecond * 1000.f; +} + + //! returns the axis aligned bounding box of this node const core::aabbox3d& CAnimatedMeshSceneNode::getBoundingBox() const { @@ -767,8 +774,8 @@ void CAnimatedMeshSceneNode::serializeAttributes(io::IAttributes* out, io::SAttr out->addBool("Looping", Looping); out->addBool("ReadOnlyMaterials", ReadOnlyMaterials); out->addFloat("FramesPerSecond", FramesPerSecond); - - // TODO: write animation names instead of frame begin and ends + out->addInt("StartFrame", StartFrame); + out->addInt("EndFrame", EndFrame); } @@ -783,6 +790,8 @@ void CAnimatedMeshSceneNode::deserializeAttributes(io::IAttributes* in, io::SAtt Looping = in->getAttributeAsBool("Looping"); ReadOnlyMaterials = in->getAttributeAsBool("ReadOnlyMaterials"); FramesPerSecond = in->getAttributeAsFloat("FramesPerSecond"); + StartFrame = in->getAttributeAsInt("StartFrame"); + EndFrame = in->getAttributeAsInt("EndFrame"); if (newMeshStr != "" && oldMeshStr != newMeshStr) { diff --git a/source/Irrlicht/CAnimatedMeshSceneNode.h b/source/Irrlicht/CAnimatedMeshSceneNode.h index ec167207..05874e7f 100644 --- a/source/Irrlicht/CAnimatedMeshSceneNode.h +++ b/source/Irrlicht/CAnimatedMeshSceneNode.h @@ -57,9 +57,12 @@ namespace scene //! playback has ended. Set this to 0 to disable the callback again. virtual void setAnimationEndCallback(IAnimationEndCallBack* callback=0); - //! sets the speed with witch the animation is played + //! sets the speed with which the animation is played virtual void setAnimationSpeed(f32 framesPerSecond); + //! gets the speed with which the animation is played + virtual f32 getAnimationSpeed() const; + //! returns the material based on the zero based index i. To get the amount //! of materials used by this scene node, use getMaterialCount(). //! This function is needed for inserting the node into the scene hirachy on a diff --git a/source/Irrlicht/CAttributeImpl.h b/source/Irrlicht/CAttributeImpl.h index 53409b30..4586e9e4 100644 --- a/source/Irrlicht/CAttributeImpl.h +++ b/source/Irrlicht/CAttributeImpl.h @@ -1850,6 +1850,9 @@ public: virtual void setTexture(video::ITexture* value) { + if ( value == Value ) + return; + if (Value) Value->drop(); diff --git a/source/Irrlicht/CCameraSceneNode.cpp b/source/Irrlicht/CCameraSceneNode.cpp index 5dbf11c7..9be28aa4 100644 --- a/source/Irrlicht/CCameraSceneNode.cpp +++ b/source/Irrlicht/CCameraSceneNode.cpp @@ -234,6 +234,16 @@ void CCameraSceneNode::recalculateProjectionMatrix() //! prerender void CCameraSceneNode::OnRegisterSceneNode() { + if ( SceneManager->getActiveCamera () == this ) + SceneManager->registerNodeForRendering(this, ESNRP_CAMERA); + + ISceneNode::OnRegisterSceneNode(); +} + + +//! render +void CCameraSceneNode::render() +{ core::vector3df pos = getAbsolutePosition(); core::vector3df tgtv = Target - pos; tgtv.normalize(); @@ -245,7 +255,7 @@ void CCameraSceneNode::OnRegisterSceneNode() f32 dp = tgtv.dotProduct(up); - if ( core::equals(fabsf(dp), 1.f) ) + if ( core::equals(core::abs_(dp), 1.f) ) { up.X += 0.5f; } @@ -254,16 +264,6 @@ void CCameraSceneNode::OnRegisterSceneNode() ViewArea.getTransform(video::ETS_VIEW) *= Affector; recalculateViewArea(); - if ( SceneManager->getActiveCamera () == this ) - SceneManager->registerNodeForRendering(this, ESNRP_CAMERA); - - ISceneNode::OnRegisterSceneNode(); -} - - -//! render -void CCameraSceneNode::render() -{ video::IVideoDriver* driver = SceneManager->getVideoDriver(); if ( driver) { @@ -345,6 +345,24 @@ bool CCameraSceneNode::getTargetAndRotationBinding(void) const } +//! Creates a clone of this scene node and its children. +ISceneNode* CCameraSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager) +{ + if (!newParent) + newParent = Parent; + if (!newManager) + newManager = SceneManager; + + CCameraSceneNode* nb = new CCameraSceneNode(newParent, + newManager, ID, RelativeTranslation, Target); + + nb->cloneMembers(this, newManager); + + nb->drop(); + return nb; +} + + } // end namespace } // end namespace diff --git a/source/Irrlicht/CCameraSceneNode.h b/source/Irrlicht/CCameraSceneNode.h index 0071bba7..6beee2dc 100644 --- a/source/Irrlicht/CCameraSceneNode.h +++ b/source/Irrlicht/CCameraSceneNode.h @@ -142,6 +142,9 @@ namespace scene //! Queries if the camera scene node's rotation and its target position are bound together. virtual bool getTargetAndRotationBinding(void) const; + //! Creates a clone of this scene node and its children. + virtual ISceneNode* clone(ISceneNode* newParent=0, ISceneManager* newManager=0); + protected: void recalculateProjectionMatrix(); diff --git a/source/Irrlicht/CD3D8Driver.cpp b/source/Irrlicht/CD3D8Driver.cpp index e3dd6a28..e36627cb 100644 --- a/source/Irrlicht/CD3D8Driver.cpp +++ b/source/Irrlicht/CD3D8Driver.cpp @@ -151,7 +151,7 @@ bool CD3D8Driver::initDriver(const core::dimension2d& screenSize, #if defined( _IRR_XBOX_PLATFORM_) D3DCREATETYPE d3dCreate = (D3DCREATETYPE) &Direct3DCreate8; #else - D3DLibrary = LoadLibrary( "d3d8.dll" ); + D3DLibrary = LoadLibrary( __TEXT("d3d8.dll") ); if (!D3DLibrary) { @@ -904,7 +904,19 @@ void CD3D8Driver::draw2D3DVertexPrimitiveList(const void* vertices, return; } else - setRenderStates2DMode(true, (Material.getTexture(0) != 0), true); + { + if (Material.MaterialType==EMT_ONETEXTURE_BLEND) + { + E_BLEND_FACTOR srcFact; + E_BLEND_FACTOR dstFact; + E_MODULATE_FUNC modulo; + u32 alphaSource; + unpack_texureBlendFunc ( srcFact, dstFact, modulo, alphaSource, Material.MaterialTypeParam); + setRenderStates2DMode(alphaSource&video::EAS_VERTEX_COLOR, (Material.getTexture(0) != 0), alphaSource&video::EAS_TEXTURE); + } + else + setRenderStates2DMode(Material.MaterialType==EMT_TRANSPARENT_VERTEX_ALPHA, (Material.getTexture(0) != 0), Material.MaterialType==EMT_TRANSPARENT_ALPHA_CHANNEL); + } switch (pType) { @@ -2296,6 +2308,12 @@ void CD3D8Driver::enableClipPlane(u32 index, bool enable) } +core::dimension2du CD3D8Driver::getMaxTextureSize() const +{ + return core::dimension2du(Caps.MaxTextureWidth, Caps.MaxTextureHeight); +} + + } // end namespace video } // end namespace irr diff --git a/source/Irrlicht/CD3D8Driver.h b/source/Irrlicht/CD3D8Driver.h index 04a9e1ae..38b9edd1 100644 --- a/source/Irrlicht/CD3D8Driver.h +++ b/source/Irrlicht/CD3D8Driver.h @@ -217,6 +217,9 @@ namespace video //! \param enable: If true, enable the clipping plane else disable it. virtual void enableClipPlane(u32 index, bool enable); + //! Returns the maximum texture size supported. + virtual core::dimension2du getMaxTextureSize() const; + virtual bool checkDriverReset() {return DriverWasReset;} private: diff --git a/source/Irrlicht/CD3D8MaterialRenderer.h b/source/Irrlicht/CD3D8MaterialRenderer.h index 66ae92e3..24798012 100644 --- a/source/Irrlicht/CD3D8MaterialRenderer.h +++ b/source/Irrlicht/CD3D8MaterialRenderer.h @@ -401,10 +401,10 @@ public: if (material.MaterialType == EMT_LIGHTMAP_ADD) pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD); else - if (material.MaterialType == EMT_LIGHTMAP_M4) + if (material.MaterialType == EMT_LIGHTMAP_M4 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M4) pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE4X); else - if (material.MaterialType == EMT_LIGHTMAP_M2) + if (material.MaterialType == EMT_LIGHTMAP_M2 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M2) pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE2X); else pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); diff --git a/source/Irrlicht/CD3D9Driver.cpp b/source/Irrlicht/CD3D9Driver.cpp index 090a85ea..069ac461 100644 --- a/source/Irrlicht/CD3D9Driver.cpp +++ b/source/Irrlicht/CD3D9Driver.cpp @@ -163,7 +163,7 @@ bool CD3D9Driver::initDriver(const core::dimension2d& screenSize, if (!pID3D) { - D3DLibrary = LoadLibrary( "d3d9.dll" ); + D3DLibrary = LoadLibrary( __TEXT("d3d9.dll") ); if (!D3DLibrary) { @@ -1173,7 +1173,19 @@ void CD3D9Driver::draw2D3DVertexPrimitiveList(const void* vertices, return; } else - setRenderStates2DMode(true, (Material.getTexture(0) != 0), true); + { + if (Material.MaterialType==EMT_ONETEXTURE_BLEND) + { + E_BLEND_FACTOR srcFact; + E_BLEND_FACTOR dstFact; + E_MODULATE_FUNC modulo; + u32 alphaSource; + unpack_texureBlendFunc ( srcFact, dstFact, modulo, alphaSource, Material.MaterialTypeParam); + setRenderStates2DMode(alphaSource&video::EAS_VERTEX_COLOR, (Material.getTexture(0) != 0), (alphaSource&video::EAS_TEXTURE) != 0); + } + else + setRenderStates2DMode(Material.MaterialType==EMT_TRANSPARENT_VERTEX_ALPHA, (Material.getTexture(0) != 0), Material.MaterialType==EMT_TRANSPARENT_ALPHA_CHANNEL); + } switch (pType) { @@ -3111,6 +3123,12 @@ void CD3D9Driver::removeDepthSurface(SDepthSurface* depth) } +core::dimension2du CD3D9Driver::getMaxTextureSize() const +{ + return core::dimension2du(Caps.MaxTextureWidth, Caps.MaxTextureHeight); +} + + } // end namespace video } // end namespace irr diff --git a/source/Irrlicht/CD3D9Driver.h b/source/Irrlicht/CD3D9Driver.h index 4ff672e1..c5a664d1 100644 --- a/source/Irrlicht/CD3D9Driver.h +++ b/source/Irrlicht/CD3D9Driver.h @@ -272,6 +272,9 @@ namespace video /** \return Color format of the color buffer. */ virtual ECOLOR_FORMAT getColorFormat() const; + //! Returns the maximum texture size supported. + virtual core::dimension2du getMaxTextureSize() const; + //! Get the current color format of the color buffer /** \return Color format of the color buffer as D3D color value. */ D3DFORMAT getD3DColorFormat() const; diff --git a/source/Irrlicht/CD3D9MaterialRenderer.h b/source/Irrlicht/CD3D9MaterialRenderer.h index f38e9919..782e167a 100644 --- a/source/Irrlicht/CD3D9MaterialRenderer.h +++ b/source/Irrlicht/CD3D9MaterialRenderer.h @@ -377,7 +377,7 @@ public: pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); // 127 is required by EMT_TRANSPARENT_ALPHA_CHANNEL_REF - pID3DDevice->SetRenderState(D3DRS_ALPHAREF, 127); + pID3DDevice->SetRenderState(D3DRS_ALPHAREF, 127); pID3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); pID3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); } @@ -431,10 +431,10 @@ public: if (material.MaterialType == EMT_LIGHTMAP_ADD) pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD); else - if (material.MaterialType == EMT_LIGHTMAP_M4) + if (material.MaterialType == EMT_LIGHTMAP_M4 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M4) pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE4X); else - if (material.MaterialType == EMT_LIGHTMAP_M2) + if (material.MaterialType == EMT_LIGHTMAP_M2 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M2) pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE2X); else pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); diff --git a/source/Irrlicht/CD3D9ShaderMaterialRenderer.cpp b/source/Irrlicht/CD3D9ShaderMaterialRenderer.cpp index f0cf45ec..4647c894 100644 --- a/source/Irrlicht/CD3D9ShaderMaterialRenderer.cpp +++ b/source/Irrlicht/CD3D9ShaderMaterialRenderer.cpp @@ -325,7 +325,7 @@ HRESULT CD3D9ShaderMaterialRenderer::stubD3DXAssembleShader(LPCSTR pSrcData, if (!pFn && !LoadFailed) { // try to load dll - core::stringc strDllName = "d3dx9_"; + io::path strDllName = "d3dx9_"; strDllName += (int)D3DX_SDK_VERSION; strDllName += ".dll"; @@ -368,7 +368,7 @@ HRESULT CD3D9ShaderMaterialRenderer::stubD3DXAssembleShaderFromFile(LPCSTR pSrcF #endif // invoke static linked function - return D3DXAssembleShaderFromFile(pSrcFile, pDefines, pInclude, Flags, + return D3DXAssembleShaderFromFileA(pSrcFile, pDefines, pInclude, Flags, ppShader, ppErrorMsgs); #else { @@ -385,7 +385,7 @@ HRESULT CD3D9ShaderMaterialRenderer::stubD3DXAssembleShaderFromFile(LPCSTR pSrcF if (!pFn && !LoadFailed) { // try to load dll - core::stringc strDllName = "d3dx9_"; + io::path strDllName = "d3dx9_"; strDllName += (int)D3DX_SDK_VERSION; strDllName += ".dll"; @@ -446,7 +446,7 @@ HRESULT CD3D9ShaderMaterialRenderer::stubD3DXCompileShader(LPCSTR pSrcData, UINT if (!pFn && !LoadFailed) { // try to load dll - core::stringc strDllName = "d3dx9_"; + io::path strDllName = "d3dx9_"; strDllName += (int)D3DX_SDK_VERSION; strDllName += ".dll"; @@ -489,7 +489,7 @@ HRESULT CD3D9ShaderMaterialRenderer::stubD3DXCompileShaderFromFile(LPCSTR pSrcFi #endif // invoke static linked function - return D3DXCompileShaderFromFile(pSrcFile, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs, ppConstantTable); + return D3DXCompileShaderFromFileA(pSrcFile, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs, ppConstantTable); #else { // try to load shader functions from the dll and print error if failed. @@ -506,7 +506,7 @@ HRESULT CD3D9ShaderMaterialRenderer::stubD3DXCompileShaderFromFile(LPCSTR pSrcFi if (!pFn && !LoadFailed) { // try to load dll - core::stringc strDllName = "d3dx9_"; + io::path strDllName = "d3dx9_"; strDllName += (int)D3DX_SDK_VERSION; strDllName += ".dll"; diff --git a/source/Irrlicht/CFileList.cpp b/source/Irrlicht/CFileList.cpp index 4a041cec..4f6be729 100644 --- a/source/Irrlicht/CFileList.cpp +++ b/source/Irrlicht/CFileList.cpp @@ -94,6 +94,12 @@ u32 CFileList::addItem(const io::path& fullPath, u32 size, bool isDirectory, u32 return Files.size() - 1; } +//! Returns the ID of a file in the file list, based on an index. +u32 CFileList::getID(u32 index) const +{ + return index < Files.size() ? Files[index].ID : 0; +} + bool CFileList::isDirectory(u32 index) const { bool ret = false; @@ -107,7 +113,7 @@ bool CFileList::isDirectory(u32 index) const //! Returns the size of a file u32 CFileList::getFileSize(u32 index) const { - return index < Files.size() ? Files[index].IsDirectory : 0; + return index < Files.size() ? Files[index].Size : 0; } diff --git a/source/Irrlicht/CFileList.h b/source/Irrlicht/CFileList.h index 72007b46..97467de0 100644 --- a/source/Irrlicht/CFileList.h +++ b/source/Irrlicht/CFileList.h @@ -80,10 +80,8 @@ public: \param id The ID of the file in the archive which owns it */ virtual u32 addItem(const io::path& fullPath, u32 size, bool isDirectory, u32 id=0); - //! Sorts the file list - void sort(); - - // IFileList methods + //! Sorts the file list. You should call this after adding any items to the file list + virtual void sort(); //! Returns the amount of files in the filelist. virtual u32 getFileCount() const; @@ -94,6 +92,9 @@ public: //! Gets the full name of a file in the list, path included, based on an index. virtual const io::path& getFullFileName(u32 index) const; + //! Returns the ID of a file in the file list, based on an index. + virtual u32 getID(u32 index) const; + //! Returns true if the file is a directory virtual bool isDirectory(u32 index) const; diff --git a/source/Irrlicht/CFileSystem.cpp b/source/Irrlicht/CFileSystem.cpp index 04d24636..e2e3e2dd 100644 --- a/source/Irrlicht/CFileSystem.cpp +++ b/source/Irrlicht/CFileSystem.cpp @@ -24,8 +24,8 @@ #if defined (_IRR_WINDOWS_API_) #if !defined ( _WIN32_WCE ) #include // for _chdir + #include // for _access #endif - #include // for _access #else #if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_)) #include @@ -438,7 +438,7 @@ io::path CFileSystem::getAbsolutePath(const io::path& filename) const #elif defined(_IRR_WINDOWS_API_) #if defined(_IRR_WCHAR_FILESYSTEM ) - c16 fpath[_MAX_PATH]; + wchar_t fpath[_MAX_PATH]; p = _wfullpath(fpath, filename.c_str(), _MAX_PATH); #else c8 fpath[_MAX_PATH]; @@ -689,6 +689,13 @@ IFileList* CFileSystem::createFileList() return r; } +//! Creates an empty filelist +IFileList* CFileSystem::createEmptyFileList(const io::path& path, bool ignoreCase, bool ignorePaths) +{ + return new CFileList(path, ignoreCase, ignorePaths); +} + + //! determines if a file exists and would be able to be opened. bool CFileSystem::existFile(const io::path& filename) const { @@ -696,14 +703,31 @@ bool CFileSystem::existFile(const io::path& filename) const if (FileArchives[i]->getFileList()->findFile(filename)!=-1) return true; +#if defined(_IRR_WINDOWS_CE_PLATFORM_) +#if defined(_IRR_WCHAR_FILESYSTEM) + HANDLE hFile = CreateFileW(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); +#else + HANDLE hFile = CreateFileA(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); +#endif + if (hFile == INVALID_HANDLE_VALUE) + return false; + else + { + CloseHandle(hFile); + return true; + } +#else _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; +#if defined(_MSC_VER) #if defined(_IRR_WCHAR_FILESYSTEM) return (_waccess(filename.c_str(), 0) != -1); -#elif defined(_MSC_VER) +#else return (_access(filename.c_str(), 0) != -1); +#endif #else return (access(filename.c_str(), F_OK) != -1); #endif +#endif } diff --git a/source/Irrlicht/CFileSystem.h b/source/Irrlicht/CFileSystem.h index 48f8e15f..79e25809 100644 --- a/source/Irrlicht/CFileSystem.h +++ b/source/Irrlicht/CFileSystem.h @@ -96,6 +96,9 @@ public: //! and returns it. virtual IFileList* createFileList(); + //! Creates an empty filelist + virtual IFileList* createEmptyFileList(const io::path& path, bool ignoreCase, bool ignorePaths); + //! determines if a file exists and would be able to be opened. virtual bool existFile(const io::path& filename) const; diff --git a/source/Irrlicht/CGUIColorSelectDialog.cpp b/source/Irrlicht/CGUIColorSelectDialog.cpp index 96904fa7..e8cb0f46 100644 --- a/source/Irrlicht/CGUIColorSelectDialog.cpp +++ b/source/Irrlicht/CGUIColorSelectDialog.cpp @@ -131,6 +131,8 @@ CGUIColorSelectDialog::CGUIColorSelectDialog(const wchar_t* title, IGUIEnvironme } SBatteryItem item; + item.Incoming=0.f; + item.Outgoing=0.f; r.UpperLeftCorner.X = Template[i].x + 15; r.UpperLeftCorner.Y = Template[i].y; @@ -218,7 +220,7 @@ void CGUIColorSelectDialog::buildColorRing( const core::dimension2d & dim, const f32 r = sqrtf((f32) r2); // normalize, dotproduct = xnorm - const f32 xn = -p.X * core::reciprocal(r); + const f32 xn = r == 0.f ? 0.f : -p.X * core::reciprocal(r); hsl.Hue = acosf(xn); if ( p.Y > 0 ) diff --git a/source/Irrlicht/CGUIContextMenu.cpp b/source/Irrlicht/CGUIContextMenu.cpp index 36323c08..37812452 100644 --- a/source/Irrlicht/CGUIContextMenu.cpp +++ b/source/Irrlicht/CGUIContextMenu.cpp @@ -24,7 +24,7 @@ CGUIContextMenu::CGUIContextMenu(IGUIEnvironment* environment, IGUIElement* parent, s32 id, core::rect rectangle, bool getFocus, bool allowFocus) : IGUIContextMenu(environment, parent, id, rectangle), EventParent(0), LastFont(0), - HighLighted(-1), ChangeTime(0), AllowFocus(allowFocus) + CloseHandling(ECMC_REMOVE), HighLighted(-1), ChangeTime(0), AllowFocus(allowFocus) { #ifdef _DEBUG setDebugName("CGUIContextMenu"); @@ -51,6 +51,17 @@ CGUIContextMenu::~CGUIContextMenu() LastFont->drop(); } +//! set behaviour when menus are closed +void CGUIContextMenu::setCloseHandling(ECONTEXT_MENU_CLOSE onClose) +{ + CloseHandling = onClose; +} + +//! get current behaviour when the menue will be closed +ECONTEXT_MENU_CLOSE CGUIContextMenu::getCloseHandling() const +{ + return CloseHandling; +} //! Returns amount of menu items u32 CGUIContextMenu::getItemCount() const @@ -60,29 +71,57 @@ u32 CGUIContextMenu::getItemCount() const //! Adds a menu item. -u32 CGUIContextMenu::addItem(const wchar_t* text, s32 id, bool enabled, bool hasSubMenu, bool checked) +u32 CGUIContextMenu::addItem(const wchar_t* text, s32 commandId, bool enabled, bool hasSubMenu, bool checked, bool autoChecking) +{ + return insertItem(Items.size(), text, commandId, enabled, hasSubMenu, checked, autoChecking); +} + +//! Insert a menu item at specified position. +u32 CGUIContextMenu::insertItem(u32 idx, const wchar_t* text, s32 commandId, bool enabled, + bool hasSubMenu, bool checked, bool autoChecking) { SItem s; s.Enabled = enabled; s.Checked = checked; + s.AutoChecking = autoChecking; s.Text = text; s.IsSeparator = (text == 0); s.SubMenu = 0; - s.CommandId = id; + s.CommandId = commandId; if (hasSubMenu) { - s.SubMenu = new CGUIContextMenu(Environment, this, id, + s.SubMenu = new CGUIContextMenu(Environment, this, commandId, core::rect(0,0,100,100), false, false); s.SubMenu->setVisible(false); } - Items.push_back(s); + u32 result = idx; + if ( idx < Items.size() ) + { + Items.insert(s, idx); + } + else + { + Items.push_back(s); + result = Items.size() - 1; + } recalculateSize(); - return Items.size() - 1; + return result; } +s32 CGUIContextMenu::findItemWithCommandId(s32 commandId, u32 idxStartSearch) const +{ + for ( u32 i=idxStartSearch; i= Items.size()) + return; + + Items[idx].AutoChecking = autoChecking; +} + +//! does the element change the checked status on clicking +bool CGUIContextMenu::getItemAutoChecking(u32 idx) const +{ + if (idx >= Items.size()) + return false; + + return Items[idx].AutoChecking; +} + //! Returns if a menu item is enabled bool CGUIContextMenu::isItemEnabled(u32 idx) const @@ -231,8 +288,17 @@ bool CGUIContextMenu::OnEvent(const SEvent& event) if (event.GUIEvent.Caller == this && !isMyChild(event.GUIEvent.Element) && AllowFocus) { // set event parent of submenus - setEventParent(Parent); - remove(); + setEventParent(EventParent ? EventParent : Parent); + + if ( CloseHandling & ECMC_HIDE ) + { + setVisible(false); + } + if ( CloseHandling & ECMC_REMOVE ) + { + remove(); + } + return false; } break; @@ -326,16 +392,20 @@ u32 CGUIContextMenu::sendClick(const core::position2d& p) Items[HighLighted].SubMenu) return 2; + if ( Items[HighLighted].AutoChecking ) + { + Items[HighLighted].Checked = Items[HighLighted].Checked ? false : true; + } + SEvent event; event.EventType = EET_GUI_EVENT; event.GUIEvent.Caller = this; event.GUIEvent.Element = 0; event.GUIEvent.EventType = EGET_MENU_ITEM_SELECTED; - if (Parent) - Parent->OnEvent(event); - else if (EventParent) - EventParent->OnEvent(event); + EventParent->OnEvent(event); + else if (Parent) + Parent->OnEvent(event); return 1; } @@ -644,6 +714,8 @@ void CGUIContextMenu::serializeAttributes(io::IAttributes* out, io::SAttributeRe out->addInt("ParentItem", i); } + out->addInt("CloseHandling", (s32)CloseHandling); + // write out the item list out->addInt("ItemCount", Items.size()); @@ -662,6 +734,10 @@ void CGUIContextMenu::serializeAttributes(io::IAttributes* out, io::SAttributeRe out->addInt(tmp.c_str(), Items[i].CommandId); tmp = "Enabled"; tmp += i; out->addBool(tmp.c_str(), Items[i].Enabled); + tmp = "Checked"; tmp += i; + out->addBool(tmp.c_str(), Items[i].Checked); + tmp = "AutoChecking"; tmp += i; + out->addBool(tmp.c_str(), Items[i].AutoChecking); } } } @@ -678,6 +754,7 @@ void CGUIContextMenu::deserializeAttributes(io::IAttributes* in, io::SAttributeR if (Parent && ( Parent->getType() == EGUIET_CONTEXT_MENU || Parent->getType() == EGUIET_MENU ) ) ((CGUIContextMenu*)Parent)->setSubMenu(in->getAttributeAsInt("ParentItem"),this); + CloseHandling = (ECONTEXT_MENU_CLOSE)in->getAttributeAsInt("CloseHandling"); removeAllItems(); @@ -688,28 +765,37 @@ void CGUIContextMenu::deserializeAttributes(io::IAttributes* in, io::SAttributeR { core::stringc tmp; core::stringw txt; - s32 commandid; - bool enabled; - bool checked; + s32 commandid=-1; + bool enabled=true; + bool checked=false; + bool autochecking=false; tmp = "IsSeparator"; tmp += i; - if ( in->getAttributeAsBool(tmp.c_str()) ) + if ( in->existsAttribute(tmp.c_str()) && in->getAttributeAsBool(tmp.c_str()) ) addSeparator(); else { tmp = "Text"; tmp += i; - txt = in->getAttributeAsStringW(tmp.c_str()); + if ( in->existsAttribute(tmp.c_str()) ) + txt = in->getAttributeAsStringW(tmp.c_str()); tmp = "CommandID"; tmp += i; - commandid = in->getAttributeAsInt(tmp.c_str()); + if ( in->existsAttribute(tmp.c_str()) ) + commandid = in->getAttributeAsInt(tmp.c_str()); tmp = "Enabled"; tmp += i; - enabled = in->getAttributeAsBool(tmp.c_str()); + if ( in->existsAttribute(tmp.c_str()) ) + enabled = in->getAttributeAsBool(tmp.c_str()); tmp = "Checked"; tmp += i; - checked = in->getAttributeAsBool(tmp.c_str()); + if ( in->existsAttribute(tmp.c_str()) ) + checked = in->getAttributeAsBool(tmp.c_str()); - addItem(core::stringw(txt.c_str()).c_str(), commandid, enabled, false, checked); + tmp = "AutoChecking"; tmp += i; + if ( in->existsAttribute(tmp.c_str()) ) + autochecking = in->getAttributeAsBool(tmp.c_str()); + + addItem(core::stringw(txt.c_str()).c_str(), commandid, enabled, false, checked, autochecking); } } diff --git a/source/Irrlicht/CGUIContextMenu.h b/source/Irrlicht/CGUIContextMenu.h index a71dc12d..e78e64d0 100644 --- a/source/Irrlicht/CGUIContextMenu.h +++ b/source/Irrlicht/CGUIContextMenu.h @@ -31,12 +31,25 @@ namespace gui //! destructor virtual ~CGUIContextMenu(); + //! set behaviour when menus are closed + virtual void setCloseHandling(ECONTEXT_MENU_CLOSE onClose); + + //! get current behaviour when the menue will be closed + virtual ECONTEXT_MENU_CLOSE getCloseHandling() const; + //! Returns amount of menu items virtual u32 getItemCount() const; //! Adds a menu item. virtual u32 addItem(const wchar_t* text, s32 commandid, - bool enabled, bool hasSubMenu, bool checked); + bool enabled, bool hasSubMenu, bool checked, bool autoChecking); + + //! Insert a menu item at specified position. + virtual u32 insertItem(u32 idx, const wchar_t* text, s32 commandId, bool enabled, + bool hasSubMenu, bool checked, bool autoChecking); + + //! Find a item which has the given CommandId starting from given index + virtual s32 findItemWithCommandId(s32 commandId, u32 idxStartSearch) const; //! Adds a separator item to the menu virtual void addSeparator(); @@ -81,6 +94,12 @@ namespace gui //! Sets the visible state of this element. virtual void setVisible(bool visible); + //! should the element change the checked status on clicking + virtual void setItemAutoChecking(u32 idx, bool autoChecking); + + //! does the element change the checked status on clicking + virtual bool getItemAutoChecking(u32 idx) const; + //! Returns command id of a menu item virtual s32 getItemCommandId(u32 idx) const; @@ -90,6 +109,9 @@ namespace gui //! Adds a sub menu from an element that already exists. virtual void setSubMenu(u32 index, CGUIContextMenu* menu); + //! When an eventparent is set it receives events instead of the usual parent element + virtual void setEventParent(IGUIElement *parent); + //! Writes attributes of the element. virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const; @@ -107,6 +129,7 @@ namespace gui bool IsSeparator; bool Enabled; bool Checked; + bool AutoChecking; core::dimension2d Dim; s32 PosY; CGUIContextMenu* SubMenu; @@ -130,12 +153,12 @@ namespace gui //! Gets drawing rect of Item virtual core::rect getRect(const SItem& i, const core::rect& absolute) const; - void setEventParent(IGUIElement *parent); core::array Items; core::position2d Pos; IGUIElement* EventParent; IGUIFont *LastFont; + ECONTEXT_MENU_CLOSE CloseHandling; s32 HighLighted; u32 ChangeTime; bool AllowFocus; diff --git a/source/Irrlicht/CGUIEnvironment.cpp b/source/Irrlicht/CGUIEnvironment.cpp index 1d61e4a0..97e43b92 100644 --- a/source/Irrlicht/CGUIEnvironment.cpp +++ b/source/Irrlicht/CGUIEnvironment.cpp @@ -978,7 +978,7 @@ IGUIElement* CGUIEnvironment::addModalScreen(IGUIElement* parent) //! Adds a message box. IGUIWindow* CGUIEnvironment::addMessageBox(const wchar_t* caption, const wchar_t* text, - bool modal, s32 flag, IGUIElement* parent, s32 id) + bool modal, s32 flag, IGUIElement* parent, s32 id, video::ITexture* image) { if (!CurrentSkin) return 0; @@ -990,8 +990,8 @@ IGUIWindow* CGUIEnvironment::addMessageBox(const wchar_t* caption, const wchar_t screenDim.Width = parent->getAbsolutePosition().getWidth(); screenDim.Height = parent->getAbsolutePosition().getHeight(); - msgBoxDim.Width = CurrentSkin->getSize(gui::EGDS_MESSAGE_BOX_WIDTH); - msgBoxDim.Height = CurrentSkin->getSize(gui::EGDS_MESSAGE_BOX_HEIGHT); + msgBoxDim.Width = 2; + msgBoxDim.Height = 2; rect.UpperLeftCorner.X = (screenDim.Width - msgBoxDim.Width) / 2; rect.UpperLeftCorner.Y = (screenDim.Height - msgBoxDim.Height) / 2; @@ -1005,7 +1005,7 @@ IGUIWindow* CGUIEnvironment::addMessageBox(const wchar_t* caption, const wchar_t } IGUIWindow* win = new CGUIMessageBox(this, caption, text, flag, - parent, id, rect); + parent, id, rect, image); win->drop(); return win; diff --git a/source/Irrlicht/CGUIEnvironment.h b/source/Irrlicht/CGUIEnvironment.h index ba0deab4..c307ac98 100644 --- a/source/Irrlicht/CGUIEnvironment.h +++ b/source/Irrlicht/CGUIEnvironment.h @@ -96,7 +96,7 @@ public: //! Adds a message box. virtual IGUIWindow* addMessageBox(const wchar_t* caption, const wchar_t* text=0, - bool modal = true, s32 flag = EMBF_OK, IGUIElement* parent=0, s32 id=-1); + bool modal = true, s32 flag = EMBF_OK, IGUIElement* parent=0, s32 id=-1, video::ITexture* image=0); //! adds a scrollbar. The returned pointer must not be dropped. virtual IGUIScrollBar* addScrollBar(bool horizontal, const core::rect& rectangle, diff --git a/source/Irrlicht/CGUIFileOpenDialog.cpp b/source/Irrlicht/CGUIFileOpenDialog.cpp index 34bbb19c..38f33ff3 100644 --- a/source/Irrlicht/CGUIFileOpenDialog.cpp +++ b/source/Irrlicht/CGUIFileOpenDialog.cpp @@ -327,7 +327,9 @@ void CGUIFileOpenDialog::fillListBox() FileList = FileSystem->createFileList(); core::stringw s; +#if !defined(_IRR_WINDOWS_CE_PLATFORM_) setlocale(LC_ALL,""); +#endif if (FileList) { @@ -339,7 +341,7 @@ void CGUIFileOpenDialog::fillListBox() int len = mbstowcs(ws,cs,strlen(cs)); ws[len] = 0; s = ws; - delete ws; + delete [] ws; #else s = FileList->getFileName(i).c_str(); #endif diff --git a/source/Irrlicht/CGUIMessageBox.cpp b/source/Irrlicht/CGUIMessageBox.cpp index 4f76a26a..d9ec6940 100644 --- a/source/Irrlicht/CGUIMessageBox.cpp +++ b/source/Irrlicht/CGUIMessageBox.cpp @@ -9,6 +9,7 @@ #include "IGUIEnvironment.h" #include "IGUIButton.h" #include "IGUIFont.h" +#include "ITexture.h" namespace irr { @@ -18,9 +19,10 @@ namespace gui //! constructor CGUIMessageBox::CGUIMessageBox(IGUIEnvironment* environment, const wchar_t* caption, const wchar_t* text, s32 flags, - IGUIElement* parent, s32 id, core::rect rectangle) + IGUIElement* parent, s32 id, core::rect rectangle, video::ITexture* image) : CGUIWindow(environment, parent, id, rectangle), OkButton(0), CancelButton(0), YesButton(0), NoButton(0), StaticText(0), + Icon(0), IconTexture(image), Flags(flags), MessageText(text), Pressed(false) { #ifdef _DEBUG @@ -43,6 +45,9 @@ CGUIMessageBox::CGUIMessageBox(IGUIEnvironment* environment, const wchar_t* capt Environment->setFocus(this); + if ( IconTexture ) + IconTexture->grab(); + refreshControls(); } @@ -64,62 +69,134 @@ CGUIMessageBox::~CGUIMessageBox() if (NoButton) NoButton->drop(); + + if (Icon) + Icon->drop(); + + if ( IconTexture ) + IconTexture->drop(); } +void CGUIMessageBox::setButton(IGUIButton*& button, bool isAvailable, const core::rect & btnRect, const wchar_t * text, IGUIElement*& focusMe) +{ + // add/remove ok button + if (isAvailable) + { + if (!button) + { + button = Environment->addButton(btnRect, this); + button->setSubElement(true); + button->grab(); + } + else + button->setRelativePosition(btnRect); + + button->setText(text); + + focusMe = button; + } + else if (button) + { + button->drop(); + button->remove(); + button =0; + } +} void CGUIMessageBox::refreshControls() { + // Layout can be seen as 4 boxes (a layoutmanager would be nice) + // One box at top over the whole width for title + // Two boxes with same height at the middle beside each other for icon and for text + // One box at the bottom for the buttons + const IGUISkin* skin = Environment->getSkin(); - IGUIElement* focusMe = 0; const s32 buttonHeight = skin->getSize(EGDS_BUTTON_HEIGHT); const s32 buttonWidth = skin->getSize(EGDS_BUTTON_WIDTH); - const s32 titleHeight = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH)+2; + const s32 titleHeight = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH)+2; // titlebar has no own constant const s32 buttonDistance = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH); + const s32 borderWidth = skin->getSize(EGDS_MESSAGE_BOX_GAP_SPACE); - // add static multiline text - - core::dimension2d dim(AbsoluteClippingRect.getWidth() - buttonWidth, - AbsoluteClippingRect.getHeight() - (buttonHeight * 2)); - const core::position2d pos((AbsoluteClippingRect.getWidth() - dim.Width) / 2, - buttonHeight / 2 + titleHeight); - + // add the static text for the message + core::rect staticRect; + staticRect.UpperLeftCorner.X = borderWidth; + staticRect.UpperLeftCorner.Y = titleHeight + borderWidth; + staticRect.LowerRightCorner.X = staticRect.UpperLeftCorner.X + skin->getSize(EGDS_MESSAGE_BOX_MAX_TEST_WIDTH); + staticRect.LowerRightCorner.Y = staticRect.UpperLeftCorner.Y + skin->getSize(EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT); if (!StaticText) { - StaticText = Environment->addStaticText(MessageText.c_str(), - core::rect(pos, dim), false, false, this); + StaticText = Environment->addStaticText(MessageText.c_str(), staticRect, false, false, this); + StaticText->setWordWrap(true); StaticText->setSubElement(true); StaticText->grab(); } else { - StaticText->setRelativePosition(core::rect(pos, dim)); + StaticText->setRelativePosition(staticRect); StaticText->setText(MessageText.c_str()); } - // adjust static text height + s32 textHeight = StaticText->getTextHeight(); + s32 textWidth = StaticText->getTextWidth() + 6; // +6 because the static itself needs that + const s32 iconHeight = IconTexture ? IconTexture->getOriginalSize().Height : 0; - const s32 textHeight = StaticText->getTextHeight(); - core::rect tmp = StaticText->getRelativePosition(); - tmp.LowerRightCorner.Y = tmp.UpperLeftCorner.Y + textHeight; - StaticText->setRelativePosition(tmp); - dim.Height = dim.Height - buttonHeight < tmp.getHeight() ? tmp.getHeight() : dim.Height - buttonHeight; + if ( textWidth < skin->getSize(EGDS_MESSAGE_BOX_MIN_TEXT_WIDTH) ) + textWidth = skin->getSize(EGDS_MESSAGE_BOX_MIN_TEXT_WIDTH) + 6; + // no neeed to check for max because it couldn't get larger due to statictextbox. + if ( textHeight < skin->getSize(EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT) ) + textHeight = skin->getSize(EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT); + if ( textHeight > skin->getSize(EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT) ) + textHeight = skin->getSize(EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT); - // adjust message box height if required + // content is text + icons + borders (but not titlebar) + s32 contentHeight = textHeight > iconHeight ? textHeight : iconHeight; + contentHeight += borderWidth; + s32 contentWidth = 0; - tmp = getRelativePosition(); - s32 msgBoxHeight = textHeight + core::floor32(2.5f * buttonHeight) + titleHeight; - msgBoxHeight = tmp.getHeight() < msgBoxHeight ? msgBoxHeight : tmp.getHeight(); + // add icon + if ( IconTexture ) + { + core::position2d iconPos; + iconPos.Y = titleHeight + borderWidth; + if ( iconHeight < textHeight ) + iconPos.Y += (textHeight-iconHeight) / 2; + iconPos.X = borderWidth; - // adjust message box position + if (!Icon) + { + Icon = Environment->addImage(IconTexture, iconPos, true, this); + Icon->setSubElement(true); + Icon->grab(); + } + else + { + core::rect iconRect( iconPos.X, iconPos.Y, iconPos.X + IconTexture->getOriginalSize().Width, iconPos.Y + IconTexture->getOriginalSize().Height ); + Icon->setRelativePosition(iconRect); + } - tmp.UpperLeftCorner.Y = (Parent->getAbsolutePosition().getHeight() - msgBoxHeight) / 2; - tmp.LowerRightCorner.Y = tmp.UpperLeftCorner.Y + msgBoxHeight; - setRelativePosition(tmp); + contentWidth += borderWidth + IconTexture->getOriginalSize().Width; + } + else if ( Icon ) + { + Icon->drop(); + Icon->remove(); + Icon = 0; + } - // add buttons + // position text + core::rect textRect; + textRect.UpperLeftCorner.X = contentWidth + borderWidth; + textRect.UpperLeftCorner.Y = titleHeight + borderWidth; + if ( textHeight < iconHeight ) + textRect.UpperLeftCorner.Y += (iconHeight-textHeight) / 2; + textRect.LowerRightCorner.X = textRect.UpperLeftCorner.X + textWidth; + textRect.LowerRightCorner.Y = textRect.UpperLeftCorner.Y + textHeight; + contentWidth += 2*borderWidth + textWidth; + StaticText->setRelativePosition( textRect ); + // find out button size needs s32 countButtons = 0; if (Flags & EMBF_OK) ++countButtons; @@ -130,123 +207,44 @@ void CGUIMessageBox::refreshControls() if (Flags & EMBF_NO) ++countButtons; + s32 buttonBoxWidth = countButtons * buttonWidth + 2 * borderWidth; + if ( countButtons > 1 ) + buttonBoxWidth += (countButtons-1) * buttonDistance; + s32 buttonBoxHeight = buttonHeight + 2 * borderWidth; + + // calc new message box sizes + core::rect tmp = getRelativePosition(); + s32 msgBoxHeight = titleHeight + contentHeight + buttonBoxHeight; + s32 msgBoxWidth = contentWidth > buttonBoxWidth ? contentWidth : buttonBoxWidth; + + // adjust message box position + tmp.UpperLeftCorner.Y = (Parent->getAbsolutePosition().getHeight() - msgBoxHeight) / 2; + tmp.LowerRightCorner.Y = tmp.UpperLeftCorner.Y + msgBoxHeight; + tmp.UpperLeftCorner.X = (Parent->getAbsolutePosition().getWidth() - msgBoxWidth) / 2; + tmp.LowerRightCorner.X = tmp.UpperLeftCorner.X + msgBoxWidth; + setRelativePosition(tmp); + + // add buttons + core::rect btnRect; - btnRect.UpperLeftCorner.Y = pos.Y + dim.Height + buttonHeight / 2; + btnRect.UpperLeftCorner.Y = titleHeight + contentHeight + borderWidth; btnRect.LowerRightCorner.Y = btnRect.UpperLeftCorner.Y + buttonHeight; - btnRect.UpperLeftCorner.X = (AbsoluteClippingRect.getWidth() - - (buttonWidth*countButtons + (buttonDistance*countButtons+1))) / 2 + - buttonDistance / 2; + btnRect.UpperLeftCorner.X = borderWidth; + if ( contentWidth > buttonBoxWidth ) + btnRect.UpperLeftCorner.X += (contentWidth - buttonBoxWidth) / 2; // center buttons btnRect.LowerRightCorner.X = btnRect.UpperLeftCorner.X + buttonWidth; - // add/remove ok button - if (Flags & EMBF_OK) - { - if (!OkButton) - { - OkButton = Environment->addButton(btnRect, this); - OkButton->setSubElement(true); - OkButton->grab(); - } - else - OkButton->setRelativePosition(btnRect); - - OkButton->setText(skin->getDefaultText(EGDT_MSG_BOX_OK)); - - btnRect.LowerRightCorner.X += buttonWidth + buttonDistance; - btnRect.UpperLeftCorner.X += buttonWidth + buttonDistance; - - focusMe = OkButton; - } - else if (OkButton) - { - OkButton->drop(); - OkButton->remove(); - OkButton = 0; - } - - // add cancel button - if (Flags & EMBF_CANCEL) - { - if (!CancelButton) - { - CancelButton = Environment->addButton(btnRect, this); - CancelButton->setSubElement(true); - CancelButton->grab(); - } - else - CancelButton->setRelativePosition(btnRect); - - CancelButton->setText(skin->getDefaultText(EGDT_MSG_BOX_CANCEL)); - - btnRect.LowerRightCorner.X += buttonWidth + buttonDistance; - btnRect.UpperLeftCorner.X += buttonWidth + buttonDistance; - - if (!focusMe) - focusMe = CancelButton; - - } - else if (CancelButton) - { - CancelButton->drop(); - CancelButton->remove(); - CancelButton = 0; - } - - - // add/remove yes button - if (Flags & EMBF_YES) - { - if (!YesButton) - { - YesButton = Environment->addButton(btnRect, this); - YesButton->setSubElement(true); - YesButton->grab(); - } - else - YesButton->setRelativePosition(btnRect); - - YesButton->setText(skin->getDefaultText(EGDT_MSG_BOX_YES)); - - btnRect.LowerRightCorner.X += buttonWidth + buttonDistance; - btnRect.UpperLeftCorner.X += buttonWidth + buttonDistance; - - if (!focusMe) - focusMe = YesButton; - } - else if (YesButton) - { - YesButton->drop(); - YesButton->remove(); - YesButton = 0; - } - - // add no button - if (Flags & EMBF_NO) - { - if (!NoButton) - { - NoButton = Environment->addButton(btnRect, this); - NoButton->setSubElement(true); - NoButton->grab(); - } - else - NoButton->setRelativePosition(btnRect); - - NoButton->setText(skin->getDefaultText(EGDT_MSG_BOX_NO)); - - btnRect.LowerRightCorner.X += buttonWidth + buttonDistance; - btnRect.UpperLeftCorner.X += buttonWidth + buttonDistance; - - if (!focusMe) - focusMe = NoButton; - - } - else if (NoButton) - { - NoButton->drop(); - NoButton->remove(); - NoButton = 0; - } + IGUIElement* focusMe = 0; + setButton(OkButton, (Flags & EMBF_OK) != 0, btnRect, skin->getDefaultText(EGDT_MSG_BOX_OK), focusMe); + if ( Flags & EMBF_OK ) + btnRect += core::position2d(buttonWidth + buttonDistance, 0); + setButton(CancelButton, (Flags & EMBF_CANCEL) != 0, btnRect, skin->getDefaultText(EGDT_MSG_BOX_CANCEL), focusMe); + if ( Flags & EMBF_CANCEL ) + btnRect += core::position2d(buttonWidth + buttonDistance, 0); + setButton(YesButton, (Flags & EMBF_YES) != 0, btnRect, skin->getDefaultText(EGDT_MSG_BOX_YES), focusMe); + if ( Flags & EMBF_YES ) + btnRect += core::position2d(buttonWidth + buttonDistance, 0); + setButton(NoButton, (Flags & EMBF_NO) != 0, btnRect, skin->getDefaultText(EGDT_MSG_BOX_NO), focusMe); if (Environment->hasFocus(this) && focusMe) Environment->setFocus(focusMe); @@ -409,6 +407,7 @@ void CGUIMessageBox::serializeAttributes(io::IAttributes* out, io::SAttributeRea out->addBool ("CancelButton", (Flags & EMBF_CANCEL) != 0 ); out->addBool ("YesButton", (Flags & EMBF_YES) != 0 ); out->addBool ("NoButton", (Flags & EMBF_NO) != 0 ); + out->addTexture ("Texture", IconTexture); out->addString ("MessageText", MessageText.c_str()); } @@ -424,6 +423,15 @@ void CGUIMessageBox::deserializeAttributes(io::IAttributes* in, io::SAttributeRe Flags |= in->getAttributeAsBool("YesButton") ? EMBF_YES : 0; Flags |= in->getAttributeAsBool("NoButton") ? EMBF_NO : 0; + if ( IconTexture ) + { + IconTexture->drop(); + IconTexture = NULL; + } + IconTexture = in->getAttributeAsTexture("Texture"); + if ( IconTexture ) + IconTexture->grab(); + MessageText = in->getAttributeAsStringW("MessageText").c_str(); CGUIWindow::deserializeAttributes(in,options); diff --git a/source/Irrlicht/CGUIMessageBox.h b/source/Irrlicht/CGUIMessageBox.h index 5686e028..3885c8c8 100644 --- a/source/Irrlicht/CGUIMessageBox.h +++ b/source/Irrlicht/CGUIMessageBox.h @@ -10,6 +10,7 @@ #include "CGUIWindow.h" #include "IGUIStaticText.h" +#include "IGUIImage.h" #include "irrArray.h" namespace irr @@ -23,7 +24,7 @@ namespace gui //! constructor CGUIMessageBox(IGUIEnvironment* environment, const wchar_t* caption, const wchar_t* text, s32 flag, - IGUIElement* parent, s32 id, core::rect rectangle); + IGUIElement* parent, s32 id, core::rect rectangle, video::ITexture* image=0); //! destructor virtual ~CGUIMessageBox(); @@ -40,12 +41,15 @@ namespace gui private: void refreshControls(); + void setButton(IGUIButton*& button, bool isAvailable, const core::rect & btnRect, const wchar_t * text, IGUIElement*& focusMe); IGUIButton* OkButton; IGUIButton* CancelButton; IGUIButton* YesButton; IGUIButton* NoButton; IGUIStaticText* StaticText; + IGUIImage * Icon; + video::ITexture * IconTexture; s32 Flags; core::stringw MessageText; diff --git a/source/Irrlicht/CGUISkin.cpp b/source/Irrlicht/CGUISkin.cpp index 0a700db4..024b472c 100644 --- a/source/Irrlicht/CGUISkin.cpp +++ b/source/Irrlicht/CGUISkin.cpp @@ -104,6 +104,12 @@ CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver) Sizes[EGDS_TITLEBARTEXT_DISTANCE_Y] = 2; } + Sizes[EGDS_MESSAGE_BOX_GAP_SPACE] = 15; + Sizes[EGDS_MESSAGE_BOX_MIN_TEXT_WIDTH] = 0; + Sizes[EGDS_MESSAGE_BOX_MAX_TEST_WIDTH] = 500; + Sizes[EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT] = 0; + Sizes[EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT] = 99999; + Texts[EGDT_MSG_BOX_OK] = L"OK"; Texts[EGDT_MSG_BOX_CANCEL] = L"Cancel"; Texts[EGDT_MSG_BOX_YES] = L"Yes"; diff --git a/source/Irrlicht/CGUIToolBar.cpp b/source/Irrlicht/CGUIToolBar.cpp index 9c7c4e1a..d2b9a54a 100644 --- a/source/Irrlicht/CGUIToolBar.cpp +++ b/source/Irrlicht/CGUIToolBar.cpp @@ -121,13 +121,27 @@ IGUIButton* CGUIToolBar::addButton(s32 id, const wchar_t* text,const wchar_t* to { ButtonX += 3; - core::rect rectangle(ButtonX,2,0,0); + core::rect rectangle(ButtonX,2,ButtonX+1,3); if ( img ) { const core::dimension2du &size = img->getOriginalSize(); rectangle.LowerRightCorner.X = rectangle.UpperLeftCorner.X + size.Width + 8; rectangle.LowerRightCorner.Y = rectangle.UpperLeftCorner.Y + size.Height + 6; } + + if ( text ) + { + IGUISkin* skin = Environment->getSkin(); + IGUIFont * font = skin->getFont(EGDF_BUTTON); + if ( font ) + { + core::dimension2d dim = font->getDimension(text); + if ( (s32)dim.Width > rectangle.getWidth() ) + rectangle.LowerRightCorner.X = rectangle.UpperLeftCorner.X + dim.Width + 8; + if ( (s32)dim.Height > rectangle.getHeight() ) + rectangle.LowerRightCorner.Y = rectangle.UpperLeftCorner.Y + dim.Height + 6; + } + } ButtonX += rectangle.getWidth(); diff --git a/source/Irrlicht/CIrrDeviceConsole.cpp b/source/Irrlicht/CIrrDeviceConsole.cpp index 4e817fa1..984fa802 100644 --- a/source/Irrlicht/CIrrDeviceConsole.cpp +++ b/source/Irrlicht/CIrrDeviceConsole.cpp @@ -38,7 +38,7 @@ BOOL WINAPI ConsoleHandler(DWORD CEvent) DeviceToClose->closeDevice(); return TRUE; } -#else +#elif defined(_IRR_POSIX_API_) // sigterm handler #include @@ -64,7 +64,7 @@ const u16 ASCIIArtCharsCount = 32; //! constructor CIrrDeviceConsole::CIrrDeviceConsole(const SIrrlichtCreationParameters& params) - : CIrrDeviceStub(params), IsDeviceRunning(true), IsWindowFocused(true), ConsoleFont(0), OutFile(stdout) + : CIrrDeviceStub(params), IsWindowFocused(true), ConsoleFont(0), OutFile(stdout) { DeviceToClose = this; @@ -94,9 +94,9 @@ CIrrDeviceConsole::CIrrDeviceConsole(const SIrrlichtCreationParameters& params) // catch windows close/break signals SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler, TRUE); -#else +#elif defined(_IRR_POSIX_API_) // catch other signals - signal(SIGABRT, &sighandler); + signal(SIGABRT, &sighandler); signal(SIGTERM, &sighandler); signal(SIGINT, &sighandler); @@ -305,7 +305,7 @@ bool CIrrDeviceConsole::run() // todo: keyboard input from terminal in raw mode #endif - return IsDeviceRunning; + return !Close; } //! Cause the device to temporarily pause execution and let other processes to run @@ -346,8 +346,7 @@ void CIrrDeviceConsole::sleep(u32 timeMs, bool pauseTimer) void CIrrDeviceConsole::setWindowCaption(const wchar_t* text) { #ifdef _IRR_WINDOWS_NT_CONSOLE_ - core::stringc txt(text); - SetConsoleTitle(txt.c_str()); + SetConsoleTitleW(text); #endif } @@ -413,7 +412,7 @@ bool CIrrDeviceConsole::present(video::IImage* surface, void* windowId, core::re void CIrrDeviceConsole::closeDevice() { // return false next time we run() - IsDeviceRunning = false; + Close = true; } diff --git a/source/Irrlicht/CIrrDeviceConsole.h b/source/Irrlicht/CIrrDeviceConsole.h index 557800ce..6177e7c8 100644 --- a/source/Irrlicht/CIrrDeviceConsole.h +++ b/source/Irrlicht/CIrrDeviceConsole.h @@ -223,8 +223,7 @@ namespace irr core::stringc Text; }; - bool IsDeviceRunning, - IsWindowFocused; + bool IsWindowFocused; core::array OutputBuffer; gui::IGUIFont *ConsoleFont; @@ -328,3 +327,4 @@ namespace gui #endif // _IRR_COMPILE_WITH_CONSOLE_DEVICE_ #endif // __C_IRR_DEVICE_CONSOLE_H_INCLUDED__ + diff --git a/source/Irrlicht/CIrrDeviceLinux.cpp b/source/Irrlicht/CIrrDeviceLinux.cpp index 4e67c8a8..bcf66ba0 100644 --- a/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/source/Irrlicht/CIrrDeviceLinux.cpp @@ -26,6 +26,10 @@ #include #include +#ifdef __FREE_BSD_ +#include +#else + // linux/joystick.h includes linux/input.h, which #defines values for various KEY_FOO keys. // These override the irr::KEY_FOO equivalents, which stops key handling from working. // As a workaround, defining _INPUT_H stops linux/input.h from being included; it @@ -34,6 +38,8 @@ #include // Would normally be included in linux/input.h #include #undef _INPUT_H +#endif + #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ namespace irr @@ -73,7 +79,7 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param) #endif #endif Width(param.WindowSize.Width), Height(param.WindowSize.Height), - Close(false), WindowHasFocus(false), WindowMinimized(false), + WindowHasFocus(false), WindowMinimized(false), UseXVidMode(false), UseXRandR(false), UseGLXWindow(false), ExternalWindow(false), AutorepeatSupport(0) { @@ -128,6 +134,8 @@ CIrrDeviceLinux::~CIrrDeviceLinux() #ifdef _IRR_COMPILE_WITH_X11_ if (StdHints) XFree(StdHints); + // Disable cursor and free it later on + CursorControl->setVisible(false); if (display) { #ifdef _IRR_COMPILE_WITH_OPENGL_ @@ -149,21 +157,8 @@ CIrrDeviceLinux::~CIrrDeviceLinux() } #endif // #ifdef _IRR_COMPILE_WITH_OPENGL_ - #ifdef _IRR_LINUX_X11_VIDMODE_ - if (UseXVidMode && CreationParams.Fullscreen) - { - XF86VidModeSwitchToMode(display, screennr, &oldVideoMode); - XF86VidModeSetViewPort(display, screennr, 0, 0); - } - #endif - #ifdef _IRR_LINUX_X11_RANDR_ - if (UseXRandR && CreationParams.Fullscreen) - { - XRRScreenConfiguration *config=XRRGetScreenInfo(display,DefaultRootWindow(display)); - XRRSetScreenConfig(display,config,DefaultRootWindow(display),oldRandrMode,oldRandrRotation,CurrentTime); - XRRFreeScreenConfigInfo(config); - } - #endif + // Reset fullscreen resolution change + switchToFullscreen(true); if (SoftwareImage) XDestroyImage(SoftwareImage); @@ -207,6 +202,109 @@ int IrrPrintXError(Display *display, XErrorEvent *event) #endif +bool CIrrDeviceLinux::switchToFullscreen(bool reset) +{ + if (!CreationParams.Fullscreen) + return true; + if (reset) + { +#ifdef _IRR_LINUX_X11_VIDMODE_ + if (UseXVidMode && CreationParams.Fullscreen) + { + XF86VidModeSwitchToMode(display, screennr, &oldVideoMode); + XF86VidModeSetViewPort(display, screennr, 0, 0); + } + #endif + #ifdef _IRR_LINUX_X11_RANDR_ + if (UseXRandR && CreationParams.Fullscreen) + { + XRRScreenConfiguration *config=XRRGetScreenInfo(display,DefaultRootWindow(display)); + XRRSetScreenConfig(display,config,DefaultRootWindow(display),oldRandrMode,oldRandrRotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + } + #endif + return true; + } + + getVideoModeList(); + #if defined(_IRR_LINUX_X11_VIDMODE_) || defined(_IRR_LINUX_X11_RANDR_) + s32 eventbase, errorbase; + s32 bestMode = -1; + #endif + + #ifdef _IRR_LINUX_X11_VIDMODE_ + if (XF86VidModeQueryExtension(display, &eventbase, &errorbase)) + { + // enumerate video modes + s32 modeCount; + XF86VidModeModeInfo** modes; + + XF86VidModeGetAllModeLines(display, screennr, &modeCount, &modes); + + // find fitting mode + for (s32 i = 0; ihdisplay >= Width && modes[i]->vdisplay >= Height) + bestMode = i; + else if (bestMode!=-1 && + modes[i]->hdisplay >= Width && + modes[i]->vdisplay >= Height && + modes[i]->hdisplay < modes[bestMode]->hdisplay && + modes[i]->vdisplay < modes[bestMode]->vdisplay) + bestMode = i; + } + if (bestMode != -1) + { + os::Printer::log("Starting fullscreen mode...", ELL_INFORMATION); + XF86VidModeSwitchToMode(display, screennr, modes[bestMode]); + XF86VidModeSetViewPort(display, screennr, 0, 0); + UseXVidMode=true; + } + else + { + os::Printer::log("Could not find specified video mode, running windowed.", ELL_WARNING); + CreationParams.Fullscreen = false; + } + + XFree(modes); + } + else + #endif + #ifdef _IRR_LINUX_X11_RANDR_ + if (XRRQueryExtension(display, &eventbase, &errorbase)) + { + s32 modeCount; + XRRScreenConfiguration *config=XRRGetScreenInfo(display,DefaultRootWindow(display)); + XRRScreenSize *modes=XRRConfigSizes(config,&modeCount); + for (s32 i = 0; i= Width && (u32)modes[i].height >= Height) + bestMode = i; + else if (bestMode!=-1 && + (u32)modes[i].width >= Width && + (u32)modes[i].height >= Height && + modes[i].width < modes[bestMode].width && + modes[i].height < modes[bestMode].height) + bestMode = i; + } + if (bestMode != -1) + { + XRRSetScreenConfig(display,config,DefaultRootWindow(display),bestMode,oldRandrRotation,CurrentTime); + UseXRandR=true; + } + XRRFreeScreenConfigInfo(config); + } + else + #endif + { + os::Printer::log("VidMode or RandR extension must be installed to allow Irrlicht " + "to switch to fullscreen mode. Running in windowed mode instead.", ELL_WARNING); + CreationParams.Fullscreen = false; + } + return CreationParams.Fullscreen; +} + + bool CIrrDeviceLinux::createWindow() { #ifdef _IRR_COMPILE_WITH_X11_ @@ -228,86 +326,7 @@ bool CIrrDeviceLinux::createWindow() screennr = DefaultScreen(display); - // query extension - - if (CreationParams.Fullscreen) - { - getVideoModeList(); - #if defined(_IRR_LINUX_X11_VIDMODE_) || defined(_IRR_LINUX_X11_RANDR_) - s32 eventbase, errorbase; - s32 bestMode = -1; - #endif - - #ifdef _IRR_LINUX_X11_VIDMODE_ - if (XF86VidModeQueryExtension(display, &eventbase, &errorbase)) - { - // enumerate video modes - s32 modeCount; - XF86VidModeModeInfo** modes; - - XF86VidModeGetAllModeLines(display, screennr, &modeCount, &modes); - - // find fitting mode - for (s32 i = 0; ihdisplay >= Width && modes[i]->vdisplay >= Height) - bestMode = i; - else if (bestMode!=-1 && - modes[i]->hdisplay >= Width && - modes[i]->vdisplay >= Height && - modes[i]->hdisplay < modes[bestMode]->hdisplay && - modes[i]->vdisplay < modes[bestMode]->vdisplay) - bestMode = i; - } - if (bestMode != -1) - { - os::Printer::log("Starting fullscreen mode...", ELL_INFORMATION); - XF86VidModeSwitchToMode(display, screennr, modes[bestMode]); - XF86VidModeSetViewPort(display, screennr, 0, 0); - UseXVidMode=true; - } - else - { - os::Printer::log("Could not find specified video mode, running windowed.", ELL_WARNING); - CreationParams.Fullscreen = false; - } - - XFree(modes); - } - else - #endif - #ifdef _IRR_LINUX_X11_RANDR_ - if (XRRQueryExtension(display, &eventbase, &errorbase)) - { - s32 modeCount; - XRRScreenConfiguration *config=XRRGetScreenInfo(display,DefaultRootWindow(display)); - XRRScreenSize *modes=XRRConfigSizes(config,&modeCount); - for (s32 i = 0; i= Width && (u32)modes[i].height >= Height) - bestMode = i; - else if (bestMode!=-1 && - (u32)modes[i].width >= Width && - (u32)modes[i].height >= Height && - modes[i].width < modes[bestMode].width && - modes[i].height < modes[bestMode].height) - bestMode = i; - } - if (bestMode != -1) - { - XRRSetScreenConfig(display,config,DefaultRootWindow(display),bestMode,oldRandrRotation,CurrentTime); - UseXRandR=true; - } - XRRFreeScreenConfigInfo(config); - } - else - #endif - { - os::Printer::log("VidMode or RandR extension must be installed to allow Irrlicht " - "to switch to fullscreen mode. Running in windowed mode instead.", ELL_WARNING); - CreationParams.Fullscreen = false; - } - } + switchToFullscreen(); #ifdef _IRR_COMPILE_WITH_OPENGL_ @@ -563,7 +582,7 @@ bool CIrrDeviceLinux::createWindow() attributes.colormap = colormap; attributes.border_pixel = 0; - attributes.event_mask = StructureNotifyMask | FocusChangeMask; + attributes.event_mask = StructureNotifyMask | FocusChangeMask | ExposureMask; if (!CreationParams.IgnoreInput) attributes.event_mask |= PointerMotionMask | ButtonPressMask | KeyPressMask | @@ -571,44 +590,27 @@ bool CIrrDeviceLinux::createWindow() if (!CreationParams.WindowId) { - // create Window, either for Fullscreen or windowed mode + // create new Window + // Remove window manager decoration in fullscreen + attributes.override_redirect = CreationParams.Fullscreen; + window = XCreateWindow(display, + RootWindow(display, visual->screen), + 0, 0, Width, Height, 0, visual->depth, + InputOutput, visual->visual, + CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, + &attributes); + XMapRaised(display, window); + CreationParams.WindowId = (void*)window; + Atom wmDelete; + wmDelete = XInternAtom(display, wmDeleteWindow, True); + XSetWMProtocols(display, window, &wmDelete, 1); if (CreationParams.Fullscreen) { - attributes.override_redirect = True; - - window = XCreateWindow(display, - RootWindow(display, visual->screen), - 0, 0, Width, Height, 0, visual->depth, - InputOutput, visual->visual, - CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, - &attributes); - CreationParams.WindowId = (void*)window; - - XWarpPointer(display, None, window, 0, 0, 0, 0, 0, 0); - XMapRaised(display, window); XGrabKeyboard(display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime); XGrabPointer(display, window, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, window, None, CurrentTime); - } - else - { // we want windowed mode - attributes.event_mask |= ExposureMask; - attributes.event_mask |= FocusChangeMask; - - window = XCreateWindow(display, - RootWindow(display, visual->screen), - 0, 0, Width, Height, 0, visual->depth, - InputOutput, visual->visual, - CWBorderPixel | CWColormap | CWEventMask, - &attributes); - - CreationParams.WindowId = (void*)window; - - Atom wmDelete; - wmDelete = XInternAtom(display, wmDeleteWindow, True); - XSetWMProtocols(display, window, &wmDelete, 1); - XMapRaised(display, window); + XWarpPointer(display, None, window, 0, 0, 0, 0, 0, 0); } } else @@ -1558,15 +1560,26 @@ bool CIrrDeviceLinux::activateJoysticks(core::array & joystickInf devName = "/dev/input/js"; devName += joystick; info.fd = open(devName.c_str(), O_RDONLY); + if(-1 == info.fd) + { + // and BSD here + devName = "/dev/joy"; + devName += joystick; + info.fd = open(devName.c_str(), O_RDONLY); + } } if(-1 == info.fd) continue; +#ifdef __FREE_BSD_ + info.axes=2; + info.buttons=2; +#else ioctl( info.fd, JSIOCGAXES, &(info.axes) ); ioctl( info.fd, JSIOCGBUTTONS, &(info.buttons) ); - fcntl( info.fd, F_SETFL, O_NONBLOCK ); +#endif (void)memset(&info.persistentData, 0, sizeof(info.persistentData)); info.persistentData.EventType = irr::EET_JOYSTICK_INPUT_EVENT; @@ -1583,9 +1596,11 @@ bool CIrrDeviceLinux::activateJoysticks(core::array & joystickInf returnInfo.Axes = info.axes; returnInfo.Buttons = info.buttons; +#ifndef __FREE_BSD_ char name[80]; ioctl( info.fd, JSIOCGNAME(80), name); returnInfo.Name = name; +#endif joystickInfo.push_back(returnInfo); } @@ -1612,11 +1627,19 @@ void CIrrDeviceLinux::pollJoysticks() if(0 == ActiveJoysticks.size()) return; - u32 joystick; - for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + u32 j; + for(j= 0; j< ActiveJoysticks.size(); ++j) { - JoystickInfo & info = ActiveJoysticks[joystick]; + JoystickInfo & info = ActiveJoysticks[j]; +#ifdef __FREE_BSD_ + struct joystick js; + if( read( info.fd, &js, JS_RETURN ) == JS_RETURN ) + { + info.persistentData.JoystickEvent.ButtonStates = js.buttons; /* should be a two-bit field */ + info.persistentData.JoystickEvent.Axis[0] = js.x; /* X axis */ + info.persistentData.JoystickEvent.Axis[1] = js.y; /* Y axis */ +#else struct js_event event; while(sizeof(event) == read(info.fd, &event, sizeof(event))) { @@ -1637,6 +1660,7 @@ void CIrrDeviceLinux::pollJoysticks() break; } } +#endif // Send an irrlicht joystick event once per ::run() even if no new data were received. (void)postEventFromUser(info.persistentData); diff --git a/source/Irrlicht/CIrrDeviceLinux.h b/source/Irrlicht/CIrrDeviceLinux.h index c1973ae7..8fe330df 100644 --- a/source/Irrlicht/CIrrDeviceLinux.h +++ b/source/Irrlicht/CIrrDeviceLinux.h @@ -135,6 +135,8 @@ namespace irr void initXAtoms(); + bool switchToFullscreen(bool reset=false); + //! Implementation of the linux cursor control class CCursorControl : public gui::ICursorControl { @@ -354,7 +356,6 @@ namespace irr #endif #endif u32 Width, Height; - bool Close; bool WindowHasFocus; bool WindowMinimized; bool UseXVidMode; diff --git a/source/Irrlicht/CIrrDeviceSDL.cpp b/source/Irrlicht/CIrrDeviceSDL.cpp index 14690f0b..2ffab63f 100644 --- a/source/Irrlicht/CIrrDeviceSDL.cpp +++ b/source/Irrlicht/CIrrDeviceSDL.cpp @@ -59,8 +59,7 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters& param) Screen((SDL_Surface*)param.WindowId), SDL_Flags(SDL_ANYFORMAT), MouseX(0), MouseY(0), MouseButtonStates(0), Width(param.WindowSize.Width), Height(param.WindowSize.Height), - Close(0), Resizable(false), - WindowHasFocus(false), WindowMinimized(false) + Resizable(false), WindowHasFocus(false), WindowMinimized(false) { #ifdef _DEBUG setDebugName("CIrrDeviceSDL"); @@ -75,7 +74,7 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters& param) SDL_INIT_NOPARACHUTE ) < 0) { os::Printer::log( "Unable to initialize SDL!", SDL_GetError()); - Close = 1; + Close = true; } #if defined(_IRR_WINDOWS_) diff --git a/source/Irrlicht/CIrrDeviceSDL.h b/source/Irrlicht/CIrrDeviceSDL.h index 94dd5dca..ac34e0f0 100644 --- a/source/Irrlicht/CIrrDeviceSDL.h +++ b/source/Irrlicht/CIrrDeviceSDL.h @@ -203,7 +203,6 @@ namespace irr u32 Width, Height; - bool Close; bool Resizable; bool WindowHasFocus; bool WindowMinimized; diff --git a/source/Irrlicht/CIrrDeviceStub.cpp b/source/Irrlicht/CIrrDeviceStub.cpp index f79b34bd..89630f6e 100644 --- a/source/Irrlicht/CIrrDeviceStub.cpp +++ b/source/Irrlicht/CIrrDeviceStub.cpp @@ -19,7 +19,8 @@ namespace irr CIrrDeviceStub::CIrrDeviceStub(const SIrrlichtCreationParameters& params) : IrrlichtDevice(), VideoDriver(0), GUIEnvironment(0), SceneManager(0), Timer(0), CursorControl(0), UserReceiver(params.EventReceiver), Logger(0), Operator(0), - FileSystem(0), InputReceivingSceneManager(0), CreationParams(params) + FileSystem(0), InputReceivingSceneManager(0), CreationParams(params), + Close(false) { Timer = new CTimer(); if (os::Printer::Logger) diff --git a/source/Irrlicht/CIrrDeviceStub.h b/source/Irrlicht/CIrrDeviceStub.h index 26109727..47a54161 100644 --- a/source/Irrlicht/CIrrDeviceStub.h +++ b/source/Irrlicht/CIrrDeviceStub.h @@ -147,8 +147,6 @@ namespace irr IOSOperator* Operator; io::IFileSystem* FileSystem; scene::ISceneManager* InputReceivingSceneManager; - video::CVideoModeList VideoModeList; - SIrrlichtCreationParameters CreationParams; struct SMouseMultiClicks { @@ -162,6 +160,9 @@ namespace irr core::position2di LastClick; }; SMouseMultiClicks MouseMultiClicks; + video::CVideoModeList VideoModeList; + SIrrlichtCreationParameters CreationParams; + bool Close; }; } // end namespace irr diff --git a/source/Irrlicht/CIrrDeviceWin32.cpp b/source/Irrlicht/CIrrDeviceWin32.cpp index f096ce6d..faa45229 100644 --- a/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/source/Irrlicht/CIrrDeviceWin32.cpp @@ -258,6 +258,18 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return 0; break; + case WM_ACTIVATE: + // we need to take care for screen changes, e.g. Alt-Tab + dev = getDeviceFromHWnd(hWnd); + if (dev) + { + if ((wParam&0xFF)==WA_INACTIVE) + dev->switchToFullScreen(true); + else + dev->switchToFullScreen(); + } + break; + case WM_USER: event.EventType = irr::EET_USER_EVENT; event.UserEvent.UserData1 = (irr::s32)wParam; @@ -298,17 +310,17 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) // create the window if we need to and we do not use the null device if (!CreationParams.WindowId && CreationParams.DriverType != video::EDT_NULL) { - const c8* ClassName = "CIrrDeviceWin32"; + const fschar_t* ClassName = __TEXT("CIrrDeviceWin32"); // Register Class WNDCLASSEX wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; - wcex.hIcon = NULL; + wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = 0; @@ -316,7 +328,7 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) wcex.hIconSm = 0; // if there is an icon, load it - wcex.hIcon = (HICON)LoadImage(hInstance, "irrlicht.ico", IMAGE_ICON, 0,0, LR_LOADFROMFILE); + wcex.hIcon = (HICON)LoadImage(hInstance, __TEXT("irrlicht.ico"), IMAGE_ICON, 0,0, LR_LOADFROMFILE); RegisterClassEx(&wcex); @@ -349,7 +361,7 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) // create window - HWnd = CreateWindow( ClassName, "", style, windowLeft, windowTop, + HWnd = CreateWindow( ClassName, __TEXT(""), style, windowLeft, windowTop, realWidth, realHeight, NULL, NULL, hInstance, NULL); CreationParams.WindowId = HWnd; @@ -414,8 +426,7 @@ CIrrDeviceWin32::~CIrrDeviceWin32() } } - if (ChangedToFullScreen) - ChangeDisplaySettings(NULL,0); + switchToFullScreen(true); } @@ -463,9 +474,7 @@ void CIrrDeviceWin32::createDriver() case video::EDT_OPENGL: #ifdef _IRR_COMPILE_WITH_OPENGL_ - - if (CreationParams.Fullscreen) - switchToFullScreen(CreationParams.WindowSize.Width, CreationParams.WindowSize.Height, CreationParams.Bits); + switchToFullScreen(); VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, this); if (!VideoDriver) @@ -501,8 +510,7 @@ void CIrrDeviceWin32::createDriver() case video::EDT_SOFTWARE: #ifdef _IRR_COMPILE_WITH_SOFTWARE_ - if (CreationParams.Fullscreen) - switchToFullScreen(CreationParams.WindowSize.Width, CreationParams.WindowSize.Height, CreationParams.Bits); + switchToFullScreen(); VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this); #else @@ -513,8 +521,7 @@ void CIrrDeviceWin32::createDriver() case video::EDT_BURNINGSVIDEO: #ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_ - if (CreationParams.Fullscreen) - switchToFullScreen(CreationParams.WindowSize.Width, CreationParams.WindowSize.Height, CreationParams.Bits); + switchToFullScreen(); VideoDriver = video::createSoftwareDriver2(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this); #else @@ -541,8 +548,6 @@ bool CIrrDeviceWin32::run() MSG msg; - bool quit = false; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); @@ -553,17 +558,17 @@ bool CIrrDeviceWin32::run() DispatchMessage(&msg); if (msg.message == WM_QUIT) - quit = true; + Close = true; } - if (!quit) + if (!Close) resizeIfNecessary(); - if(!quit) + if(!Close) pollJoysticks(); _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return !quit; + return !Close; } @@ -701,6 +706,7 @@ void CIrrDeviceWin32::closeDevice() PostQuitMessage(0); PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); DestroyWindow(HWnd); + Close=true; } @@ -735,16 +741,26 @@ bool CIrrDeviceWin32::isWindowMinimized() const //! switches to fullscreen -bool CIrrDeviceWin32::switchToFullScreen(s32 width, s32 height, s32 bits) +bool CIrrDeviceWin32::switchToFullScreen(bool reset) { + if (!CreationParams.Fullscreen) + return true; + if (reset) + { + if (ChangedToFullScreen) + return (ChangeDisplaySettings(NULL,0)==DISP_CHANGE_SUCCESSFUL); + else + return true; + } + DEVMODE dm; memset(&dm, 0, sizeof(dm)); dm.dmSize = sizeof(dm); // use default values from current setting EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm); - dm.dmPelsWidth = width; - dm.dmPelsHeight = height; - dm.dmBitsPerPel = bits; + dm.dmPelsWidth = CreationParams.WindowSize.Width; + dm.dmPelsHeight = CreationParams.WindowSize.Height; + dm.dmBitsPerPel = CreationParams.Bits; dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; LONG res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); @@ -881,17 +897,17 @@ void CIrrDeviceWin32::getWindowsVersion(core::stringc& out) DWORD dwBufLen; RegOpenKeyEx( HKEY_LOCAL_MACHINE, - "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", + __TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"), 0, KEY_QUERY_VALUE, &hKey ); - RegQueryValueEx( hKey, "ProductType", NULL, NULL, + RegQueryValueEx( hKey, __TEXT("ProductType"), NULL, NULL, (LPBYTE) szProductType, &dwBufLen); RegCloseKey( hKey ); - if (lstrcmpi( "WINNT", szProductType) == 0 ) + if (_strcmpi( "WINNT", szProductType) == 0 ) out.append("Professional "); - if (lstrcmpi( "LANMANNT", szProductType) == 0) + if (_strcmpi( "LANMANNT", szProductType) == 0) out.append("Server "); - if (lstrcmpi( "SERVERNT", szProductType) == 0) + if (_strcmpi( "SERVERNT", szProductType) == 0) out.append("Advanced Server "); } diff --git a/source/Irrlicht/CIrrDeviceWin32.h b/source/Irrlicht/CIrrDeviceWin32.h index faf39adb..6913c34d 100644 --- a/source/Irrlicht/CIrrDeviceWin32.h +++ b/source/Irrlicht/CIrrDeviceWin32.h @@ -101,14 +101,18 @@ namespace irr return CIrrDeviceStub::checkSuccessiveClicks(mouseX, mouseY); } + //! switchs to fullscreen + bool switchToFullScreen(bool reset=false); + //! Implementation of the win32 cursor control class CCursorControl : public gui::ICursorControl { public: CCursorControl(const core::dimension2d& wsize, HWND hwnd, bool fullscreen) - : WindowSize(wsize), InvWindowSize(0.0f, 0.0f), IsVisible(true), - HWnd(hwnd), BorderX(0), BorderY(0), UseReferenceRect(false) + : WindowSize(wsize), InvWindowSize(0.0f, 0.0f), + HWnd(hwnd), BorderX(0), BorderY(0), + UseReferenceRect(false), IsVisible(true) { if (WindowSize.Width!=0) InvWindowSize.Width = 1.0f / WindowSize.Width; @@ -172,9 +176,9 @@ namespace irr virtual void setPosition(f32 x, f32 y) { if (!UseReferenceRect) - setPosition((s32)(x*WindowSize.Width), (s32)(y*WindowSize.Height)); + setPosition(core::round32(x*WindowSize.Width), core::round32(y*WindowSize.Height)); else - setPosition((s32)(x*ReferenceRect.getWidth()), (s32)(y*ReferenceRect.getHeight())); + setPosition(core::round32(x*ReferenceRect.getWidth()), core::round32(y*ReferenceRect.getHeight())); } //! Sets the new position of the cursor. @@ -247,6 +251,7 @@ namespace irr /** Used to notify the cursor that the window was resized. */ virtual void OnResize(const core::dimension2d& size) { + WindowSize = size; if (size.Width!=0) InvWindowSize.Width = 1.0f / size.Width; else @@ -297,12 +302,12 @@ namespace irr core::position2d CursorPos; core::dimension2d WindowSize; core::dimension2d InvWindowSize; - bool IsVisible; HWND HWnd; s32 BorderX, BorderY; - bool UseReferenceRect; core::rect ReferenceRect; + bool UseReferenceRect; + bool IsVisible; }; //! returns the win32 cursor control @@ -313,9 +318,6 @@ namespace irr //! create the driver void createDriver(); - //! switchs to fullscreen - bool switchToFullScreen(s32 width, s32 height, s32 bits); - void getWindowsVersion(core::stringc& version); void resizeIfNecessary(); @@ -344,4 +346,3 @@ namespace irr #endif // _IRR_COMPILE_WITH_WINDOWS_DEVICE_ #endif // __C_IRR_DEVICE_WIN32_H_INCLUDED__ - diff --git a/source/Irrlicht/CIrrDeviceWinCE.cpp b/source/Irrlicht/CIrrDeviceWinCE.cpp index dbfb7a49..f71f2203 100644 --- a/source/Irrlicht/CIrrDeviceWinCE.cpp +++ b/source/Irrlicht/CIrrDeviceWinCE.cpp @@ -552,8 +552,6 @@ bool CIrrDeviceWinCE::run() MSG msg; - bool quit = false; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); @@ -564,14 +562,14 @@ bool CIrrDeviceWinCE::run() DispatchMessage(&msg); if (msg.message == WM_QUIT) - quit = true; + Close = true; } - if (!quit) + if (!Close) resizeIfNecessary(); _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return !quit; + return !Close; } @@ -713,6 +711,7 @@ void CIrrDeviceWinCE::closeDevice() PostQuitMessage(0); PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); DestroyWindow(HWnd); + Close=true; } diff --git a/source/Irrlicht/CIrrDeviceWinCE.h b/source/Irrlicht/CIrrDeviceWinCE.h index fcb349e8..83c43ae3 100644 --- a/source/Irrlicht/CIrrDeviceWinCE.h +++ b/source/Irrlicht/CIrrDeviceWinCE.h @@ -90,8 +90,9 @@ namespace irr public: CCursorControl(const core::dimension2d& wsize, HWND hwnd, bool fullscreen) - : WindowSize(wsize), InvWindowSize(0.0f, 0.0f), IsVisible(true), - HWnd(hwnd), BorderX(0), BorderY(0), UseReferenceRect(false) + : WindowSize(wsize), InvWindowSize(0.0f, 0.0f), + HWnd(hwnd), BorderX(0), BorderY(0), + UseReferenceRect(false), IsVisible(true) { if (WindowSize.Width!=0) InvWindowSize.Width = 1.0f / WindowSize.Width; @@ -129,9 +130,9 @@ namespace irr virtual void setPosition(f32 x, f32 y) { if (!UseReferenceRect) - setPosition((s32)(x*WindowSize.Width), (s32)(y*WindowSize.Height)); + setPosition(core::round32(x*WindowSize.Width), core::round32(y*WindowSize.Height)); else - setPosition((s32)(x*ReferenceRect.getWidth()), (s32)(y*ReferenceRect.getHeight())); + setPosition(core::round32(x*ReferenceRect.getWidth()), core::round32(y*ReferenceRect.getHeight())); } //! Sets the new position of the cursor. @@ -205,6 +206,7 @@ namespace irr /** Used to notify the cursor that the window was resized. */ virtual void OnResize(const core::dimension2d& size) { + WindowSize = size; if (size.Width!=0) InvWindowSize.Width = 1.0f / size.Width; else @@ -255,12 +257,12 @@ namespace irr core::position2d CursorPos; core::dimension2d WindowSize; core::dimension2d InvWindowSize; - bool IsVisible; HWND HWnd; s32 BorderX, BorderY; - bool UseReferenceRect; core::rect ReferenceRect; + bool UseReferenceRect; + bool IsVisible; }; diff --git a/source/Irrlicht/CLWOMeshFileLoader.cpp b/source/Irrlicht/CLWOMeshFileLoader.cpp index 6576f824..8a8dbe75 100644 --- a/source/Irrlicht/CLWOMeshFileLoader.cpp +++ b/source/Irrlicht/CLWOMeshFileLoader.cpp @@ -217,6 +217,7 @@ IAnimatedMesh* CLWOMeshFileLoader::createMesh(io::IReadFile* file) const u16 uvTag = Materials[tag]->Texture[0].UVTag; const u16 duvTag = Materials[tag]->Texture[0].DUVTag; video::S3DVertex vertex; + vertex.Color=0xffffffff; const u32 vertCount=mb->Vertices.size(); for (u32 i=0; iMeshbuffer->Vertices.size())); #endif if (!Materials[i]->Meshbuffer->Vertices.size()) + { + Materials[i]->Meshbuffer->drop(); + delete Materials[i]; continue; + } for (u32 j=0; jMeshbuffer->Vertices.size(); ++j) Materials[i]->Meshbuffer->Vertices[j].Color=Materials[i]->Meshbuffer->Material.DiffuseColor; Materials[i]->Meshbuffer->recalculateBoundingBox(); @@ -343,17 +348,17 @@ IAnimatedMesh* CLWOMeshFileLoader::createMesh(io::IReadFile* file) } } // get the resolution for this axis - f32 resolutionS = 1.f/Materials[i]->Texture[0].Size.Z; - f32 resolutionT = 1.f/Materials[i]->Texture[0].Size.Y; + f32 resolutionS = core::reciprocal(Materials[i]->Texture[0].Size.Z); + f32 resolutionT = core::reciprocal(Materials[i]->Texture[0].Size.Y); if (Materials[i]->Texture[0].Axis==1) { - resolutionS = 1.f/Materials[i]->Texture[0].Size.X; - resolutionT = 1.f/Materials[i]->Texture[0].Size.Z; + resolutionS = core::reciprocal(Materials[i]->Texture[0].Size.X); + resolutionT = core::reciprocal(Materials[i]->Texture[0].Size.Z); } else if (Materials[i]->Texture[0].Axis==2) { - resolutionS = 1.f/Materials[i]->Texture[0].Size.X; - resolutionT = 1.f/Materials[i]->Texture[0].Size.Y; + resolutionS = core::reciprocal(Materials[i]->Texture[0].Size.X); + resolutionT = core::reciprocal(Materials[i]->Texture[0].Size.Y); } // use the two-way planar mapping SceneManager->getMeshManipulator()->makePlanarTextureMapping(Materials[i]->Meshbuffer, resolutionS, resolutionT, Materials[i]->Texture[0].Axis, Materials[i]->Texture[0].Center); @@ -362,17 +367,19 @@ IAnimatedMesh* CLWOMeshFileLoader::createMesh(io::IReadFile* file) // add bump maps if (Materials[i]->Meshbuffer->Material.MaterialType==video::EMT_NORMAL_MAP_SOLID) { - SMesh tmpmesh; - tmpmesh.addMeshBuffer(Materials[i]->Meshbuffer); - SceneManager->getMeshManipulator()->createMeshWithTangents(&tmpmesh, true, true); - Mesh->addMeshBuffer(tmpmesh.getMeshBuffer(0)); + SMesh* tmpmesh = new SMesh(); + tmpmesh->addMeshBuffer(Materials[i]->Meshbuffer); + SceneManager->getMeshManipulator()->createMeshWithTangents(tmpmesh, true, true); + Mesh->addMeshBuffer(tmpmesh->getMeshBuffer(0)); + tmpmesh->getMeshBuffer(0)->drop(); + tmpmesh->drop(); } else { SceneManager->getMeshManipulator()->recalculateNormals(Materials[i]->Meshbuffer); Mesh->addMeshBuffer(Materials[i]->Meshbuffer); } - Mesh->getMeshBuffer(Mesh->getMeshBufferCount()-1)->drop(); + Materials[i]->Meshbuffer->drop(); // clear the material array elements delete Materials[i]; } @@ -607,6 +614,7 @@ void CLWOMeshFileLoader::readObj1(u32 size) u16 numVerts, vertIndex; s16 material; video::S3DVertex vertex; + vertex.Color=0xffffffff; while (size!=0) { diff --git a/source/Irrlicht/CMeshManipulator.cpp b/source/Irrlicht/CMeshManipulator.cpp index e5e2794c..115b1b96 100644 --- a/source/Irrlicht/CMeshManipulator.cpp +++ b/source/Irrlicht/CMeshManipulator.cpp @@ -909,9 +909,9 @@ IMesh* CMeshManipulator::createMeshWithTangents(IMesh* mesh, bool recalculateNor v[idx[i+2]].TCoords); if (recalculateNormals) - v[idx[i+0]].Tangent += localTangent * weight.X; + v[idx[i+0]].Normal += localNormal * weight.X; + v[idx[i+0]].Tangent += localTangent * weight.X; v[idx[i+0]].Binormal += localBinormal * weight.X; - v[idx[i+0]].Normal += localNormal * weight.X; calculateTangents( localNormal, @@ -925,9 +925,9 @@ IMesh* CMeshManipulator::createMeshWithTangents(IMesh* mesh, bool recalculateNor v[idx[i+0]].TCoords); if (recalculateNormals) - v[idx[i+1]].Tangent += localTangent * weight.Y; + v[idx[i+1]].Normal += localNormal * weight.Y; + v[idx[i+1]].Tangent += localTangent * weight.Y; v[idx[i+1]].Binormal += localBinormal * weight.Y; - v[idx[i+1]].Normal += localNormal * weight.Y; calculateTangents( localNormal, @@ -941,9 +941,9 @@ IMesh* CMeshManipulator::createMeshWithTangents(IMesh* mesh, bool recalculateNor v[idx[i+1]].TCoords); if (recalculateNormals) - v[idx[i+2]].Tangent += localTangent * weight.Z; + v[idx[i+2]].Normal += localNormal * weight.Z; + v[idx[i+2]].Tangent += localTangent * weight.Z; v[idx[i+2]].Binormal += localBinormal * weight.Z; - v[idx[i+2]].Normal += localNormal * weight.Z; } // Normalize the tangents and binormals @@ -1104,7 +1104,6 @@ IMesh* CMeshManipulator::createMeshWith1TCoords(IMesh* mesh) const SMeshBuffer* buffer = new SMeshBuffer(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); - buffer->Material.MaterialType = video::EMT_SOLID; // copy vertices diff --git a/source/Irrlicht/CMeshSceneNode.cpp b/source/Irrlicht/CMeshSceneNode.cpp index 841beb10..45bcbee5 100644 --- a/source/Irrlicht/CMeshSceneNode.cpp +++ b/source/Irrlicht/CMeshSceneNode.cpp @@ -178,6 +178,7 @@ void CMeshSceneNode::render() { video::SMaterial m; m.Lighting = false; + m.AntiAliasing=0; driver->setMaterial(m); if (DebugDataVisible & scene::EDS_BBOX) diff --git a/source/Irrlicht/CNullDriver.cpp b/source/Irrlicht/CNullDriver.cpp index 7987e265..fcfb03eb 100644 --- a/source/Irrlicht/CNullDriver.cpp +++ b/source/Irrlicht/CNullDriver.cpp @@ -585,56 +585,6 @@ void CNullDriver::draw2DVertexPrimitiveList(const void* vertices, u32 vertexCoun } -//! draws an indexed triangle list -void CNullDriver::drawIndexedTriangleList(const S3DVertex* vertices, u32 vertexCount, const u16* indexList, u32 triangleCount) -{ - drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_STANDARD, scene::EPT_TRIANGLES, EIT_16BIT); -} - - -//! draws an indexed triangle list -void CNullDriver::drawIndexedTriangleList(const S3DVertex2TCoords* vertices, u32 vertexCount, const u16* indexList, u32 triangleCount) -{ - drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_2TCOORDS, scene::EPT_TRIANGLES, EIT_16BIT); -} - - -//! Draws an indexed triangle list. -void CNullDriver::drawIndexedTriangleList(const S3DVertexTangents* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) -{ - drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_TANGENTS, scene::EPT_TRIANGLES, EIT_16BIT); -} - - - -//! Draws an indexed triangle fan. -void CNullDriver::drawIndexedTriangleFan(const S3DVertex* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) -{ - drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT); -} - - - -//! Draws an indexed triangle fan. -void CNullDriver::drawIndexedTriangleFan(const S3DVertex2TCoords* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) -{ - drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_2TCOORDS, scene::EPT_TRIANGLE_FAN, EIT_16BIT); -} - - - -//! Draws an indexed triangle fan. -void CNullDriver::drawIndexedTriangleFan(const S3DVertexTangents* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount) -{ - drawVertexPrimitiveList(vertices, vertexCount, indexList, triangleCount, EVT_TANGENTS, scene::EPT_TRIANGLE_FAN, EIT_16BIT); -} - - - //! Draws a 3d line. void CNullDriver::draw3DLine(const core::vector3df& start, const core::vector3df& end, SColor color) @@ -642,17 +592,27 @@ void CNullDriver::draw3DLine(const core::vector3df& start, } - //! Draws a 3d triangle. void CNullDriver::draw3DTriangle(const core::triangle3df& triangle, SColor color) { - draw3DLine(triangle.pointA, triangle.pointB, color); - draw3DLine(triangle.pointB, triangle.pointC, color); - draw3DLine(triangle.pointC, triangle.pointA, color); + S3DVertex vertices[3]; + vertices[0].Pos=triangle.pointA; + vertices[0].Color=color; + vertices[0].Normal=triangle.getNormal().normalize(); + vertices[0].TCoords.set(0.f,0.f); + vertices[1].Pos=triangle.pointB; + vertices[1].Color=color; + vertices[1].Normal=vertices[0].Normal; + vertices[1].TCoords.set(0.5f,1.f); + vertices[2].Pos=triangle.pointC; + vertices[2].Color=color; + vertices[2].Normal=vertices[0].Normal; + vertices[2].TCoords.set(1.f,0.f); + const u16 indexList[] = {0,1,2}; + drawVertexPrimitiveList(vertices, 3, indexList, 1, EVT_STANDARD, scene::EPT_TRIANGLES, EIT_16BIT); } - //! Draws a 3d axis aligned box. void CNullDriver::draw3DBox(const core::aabbox3d& box, SColor color) { @@ -1442,6 +1402,18 @@ void CNullDriver::setFog(SColor color, E_FOG_TYPE fogType, f32 start, f32 end, RangeFog = rangeFog; } +//! Gets the fog mode. +void CNullDriver::getFog(SColor& color, E_FOG_TYPE& fogType, f32& start, f32& end, + f32& density, bool& pixelFog, bool& rangeFog) +{ + color = FogColor; + fogType = FogType; + start = FogStart; + end = FogEnd; + density = FogDensity; + pixelFog = PixelFog; + rangeFog = RangeFog; +} //! Draws a mesh buffer void CNullDriver::drawMeshBuffer(const scene::IMeshBuffer* mb) @@ -2101,5 +2073,12 @@ SOverrideMaterial& CNullDriver::getOverrideMaterial() return OverrideMaterial; } + +core::dimension2du CNullDriver::getMaxTextureSize() const +{ + return core::dimension2du(0x10000,0x10000); // maybe large enough +} + + } // end namespace } // end namespace diff --git a/source/Irrlicht/CNullDriver.h b/source/Irrlicht/CNullDriver.h index 3d9c198f..b11ec158 100644 --- a/source/Irrlicht/CNullDriver.h +++ b/source/Irrlicht/CNullDriver.h @@ -107,34 +107,12 @@ namespace video //! draws a vertex primitive list virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount, const void* indexList, u32 primitiveCount, - E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType); + E_VERTEX_TYPE vType=EVT_STANDARD, scene::E_PRIMITIVE_TYPE pType=scene::EPT_TRIANGLES, E_INDEX_TYPE iType=EIT_16BIT); //! draws a vertex primitive list in 2d virtual void draw2DVertexPrimitiveList(const void* vertices, u32 vertexCount, const void* indexList, u32 primitiveCount, - E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType); - - //! draws an indexed triangle list - virtual void drawIndexedTriangleList(const S3DVertex* vertices, u32 vertexCount, const u16* indexList, u32 triangleCount); - - //! draws an indexed triangle list - virtual void drawIndexedTriangleList(const S3DVertex2TCoords* vertices, u32 vertexCount, const u16* indexList, u32 triangleCount); - - //! Draws an indexed triangle list. - virtual void drawIndexedTriangleList(const S3DVertexTangents* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount); - - //! Draws an indexed triangle fan. - virtual void drawIndexedTriangleFan(const S3DVertex* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount); - - //! Draws an indexed triangle list. - virtual void drawIndexedTriangleFan(const S3DVertex2TCoords* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount); - - //! Draws an indexed triangle fan. - virtual void drawIndexedTriangleFan(const S3DVertexTangents* vertices, - u32 vertexCount, const u16* indexList, u32 triangleCount); + E_VERTEX_TYPE vType=EVT_STANDARD, scene::E_PRIMITIVE_TYPE pType=scene::EPT_TRIANGLES, E_INDEX_TYPE iType=EIT_16BIT); //! Draws a 3d line. virtual void draw3DLine(const core::vector3df& start, @@ -238,6 +216,10 @@ namespace video f32 start=50.0f, f32 end=100.0f, f32 density=0.01f, bool pixelFog=false, bool rangeFog=false); + virtual void getFog(SColor& color, E_FOG_TYPE& fogType, + f32& start, f32& end, f32& density, + bool& pixelFog, bool& rangeFog); + //! get color format of the current color buffer virtual ECOLOR_FORMAT getColorFormat() const; @@ -579,6 +561,9 @@ namespace video virtual void setAllowZWriteOnTransparent(bool flag) { AllowZWriteOnTransparent=flag; } + //! Returns the maximum texture size supported. + virtual core::dimension2du getMaxTextureSize() const; + //! deprecated method virtual ITexture* createRenderTargetTexture(const core::dimension2d& size, const c8* name=0); diff --git a/source/Irrlicht/COBJMeshFileLoader.cpp b/source/Irrlicht/COBJMeshFileLoader.cpp index 033b2ee0..b98aaf30 100644 --- a/source/Irrlicht/COBJMeshFileLoader.cpp +++ b/source/Irrlicht/COBJMeshFileLoader.cpp @@ -423,13 +423,20 @@ const c8* COBJMeshFileLoader::readTextures(const c8* bufPtr, const c8* const buf texname.replace('\\', '/'); video::ITexture * texture = 0; + bool newTexture=false; if (texname.size()) { if (FileSystem->existFile(texname)) + { + newTexture = SceneManager->getVideoDriver()->findTexture(texname) == 0; texture = SceneManager->getVideoDriver()->getTexture(texname); + } else + { + newTexture = SceneManager->getVideoDriver()->findTexture(relPath + texname) == 0; // try to read in the relative path, the .obj is loaded from texture = SceneManager->getVideoDriver()->getTexture( relPath + texname ); + } } if ( texture ) { @@ -437,7 +444,8 @@ const c8* COBJMeshFileLoader::readTextures(const c8* bufPtr, const c8* const buf currMaterial->Meshbuffer->Material.setTexture(0, texture); else if (type==1) { - SceneManager->getVideoDriver()->makeNormalMapTexture(texture, bumpiness); + if (newTexture) + SceneManager->getVideoDriver()->makeNormalMapTexture(texture, bumpiness); currMaterial->Meshbuffer->Material.setTexture(1, texture); currMaterial->Meshbuffer->Material.MaterialType=video::EMT_PARALLAX_MAP_SOLID; currMaterial->Meshbuffer->Material.MaterialTypeParam=0.035f; diff --git a/source/Irrlicht/COSOperator.cpp b/source/Irrlicht/COSOperator.cpp index 5c14a26a..9fb1d4d0 100644 --- a/source/Irrlicht/COSOperator.cpp +++ b/source/Irrlicht/COSOperator.cpp @@ -132,7 +132,7 @@ bool COSOperator::getProcessorSpeedMHz(u32* MHz) const HKEY Key; Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, - "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + __TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), 0, KEY_READ, &Key); if(Error != ERROR_SUCCESS) @@ -140,7 +140,7 @@ bool COSOperator::getProcessorSpeedMHz(u32* MHz) const DWORD Speed = 0; DWORD Size = sizeof(Speed); - Error = RegQueryValueEx(Key, "~MHz", NULL, NULL, (LPBYTE)&Speed, &Size); + Error = RegQueryValueEx(Key, __TEXT("~MHz"), NULL, NULL, (LPBYTE)&Speed, &Size); RegCloseKey(Key); diff --git a/source/Irrlicht/COpenGLDriver.cpp b/source/Irrlicht/COpenGLDriver.cpp index 71c47964..5a21112c 100644 --- a/source/Irrlicht/COpenGLDriver.cpp +++ b/source/Irrlicht/COpenGLDriver.cpp @@ -77,7 +77,7 @@ bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDevi if (AntiAlias > 1) { // Create a window to test antialiasing support - const c8* ClassName = "GLCIrrDeviceWin32"; + const fschar_t* ClassName = __TEXT("GLCIrrDeviceWin32"); HINSTANCE lhInstance = GetModuleHandle(0); // Register Class @@ -116,7 +116,7 @@ bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDevi const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2; const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2; - HWND temporary_wnd=CreateWindow(ClassName, "", style, windowLeft, windowTop, + HWND temporary_wnd=CreateWindow(ClassName, __TEXT(""), style, windowLeft, windowTop, realWidth, realHeight, NULL, NULL, lhInstance, NULL); if (!temporary_wnd) @@ -1435,7 +1435,17 @@ void COpenGLDriver::draw2DVertexPrimitiveList(const void* vertices, u32 vertexCo // draw everything this->setActiveTexture(0, Material.getTexture(0)); - setRenderStates2DMode(false, (Material.getTexture(0) != 0), false); + if (Material.MaterialType==EMT_ONETEXTURE_BLEND) + { + E_BLEND_FACTOR srcFact; + E_BLEND_FACTOR dstFact; + E_MODULATE_FUNC modulo; + u32 alphaSource; + unpack_texureBlendFunc ( srcFact, dstFact, modulo, alphaSource, Material.MaterialTypeParam); + setRenderStates2DMode(alphaSource&video::EAS_VERTEX_COLOR, (Material.getTexture(0) != 0), alphaSource&video::EAS_TEXTURE); + } + else + setRenderStates2DMode(Material.MaterialType==EMT_TRANSPARENT_VERTEX_ALPHA, (Material.getTexture(0) != 0), Material.MaterialType==EMT_TRANSPARENT_ALPHA_CHANNEL); if (MultiTextureExtension) extGlClientActiveTexture(GL_TEXTURE0_ARB); @@ -3500,6 +3510,11 @@ void COpenGLDriver::enableClipPlane(u32 index, bool enable) } +core::dimension2du COpenGLDriver::getMaxTextureSize() const +{ + return core::dimension2du(MaxTextureSize, MaxTextureSize); +} + } // end namespace } // end namespace diff --git a/source/Irrlicht/COpenGLDriver.h b/source/Irrlicht/COpenGLDriver.h index e2d4022a..a2da0cfc 100644 --- a/source/Irrlicht/COpenGLDriver.h +++ b/source/Irrlicht/COpenGLDriver.h @@ -315,6 +315,9 @@ namespace video //! Returns the graphics card vendor name. virtual core::stringc getVendorInfo() {return vendorName;} + //! Returns the maximum texture size supported. + virtual core::dimension2du getMaxTextureSize() const; + ITexture* createDepthTexture(ITexture* texture, bool shared=true); void removeDepthTexture(ITexture* texture); diff --git a/source/Irrlicht/COpenGLMaterialRenderer.h b/source/Irrlicht/COpenGLMaterialRenderer.h index 5851d825..197f007f 100644 --- a/source/Irrlicht/COpenGLMaterialRenderer.h +++ b/source/Irrlicht/COpenGLMaterialRenderer.h @@ -177,7 +177,7 @@ public: { return true; } - + private: u32 getGLBlend ( E_BLEND_FACTOR factor ) const @@ -405,7 +405,7 @@ public: { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } } @@ -466,7 +466,7 @@ public: glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); if (material.MaterialType == EMT_LIGHTMAP_ADD) - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD_SIGNED_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD); else glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); diff --git a/source/Irrlicht/COpenGLNormalMapRenderer.cpp b/source/Irrlicht/COpenGLNormalMapRenderer.cpp index 6135ee45..c4201255 100644 --- a/source/Irrlicht/COpenGLNormalMapRenderer.cpp +++ b/source/Irrlicht/COpenGLNormalMapRenderer.cpp @@ -47,9 +47,6 @@ const char OPENGL_NORMAL_MAP_VSH[] = "PARAM MVP[4] = { state.matrix.mvp }; # modelViewProjection matrix.\n"\ "TEMP Temp;\n"\ "TEMP TempColor;\n"\ - "TEMP TempNormal;\n"\ - "TEMP TempTangent;\n"\ - "TEMP TempBinormal;\n"\ "TEMP TempLightVector1;\n"\ "TEMP TempLightVector2;\n"\ "TEMP TempTransLightV1;\n"\ @@ -63,40 +60,19 @@ const char OPENGL_NORMAL_MAP_VSH[] = "MOV OutPos.z, Temp.z;\n"\ "MOV result.fogcoord.x, Temp.z;\n"\ "\n"\ - "# transform normal \n"\ - "DP3 TempNormal.x, InNormal.x, program.local[0];\n"\ - "DP3 TempNormal.y, InNormal.y, program.local[1]; \n"\ - "DP3 TempNormal.z, InNormal.z, program.local[2];\n"\ - "\n"\ - "# transform tangent \n"\ - "DP3 TempTangent.x, InTangent.x, program.local[0];\n"\ - "DP3 TempTangent.y, InTangent.y, program.local[1]; \n"\ - "DP3 TempTangent.z, InTangent.z, program.local[2];\n"\ - "\n"\ - "# transform binormal \n"\ - "DP3 TempBinormal.x, InBinormal.x, program.local[0];\n"\ - "DP3 TempBinormal.y, InBinormal.y, program.local[1]; \n"\ - "DP3 TempBinormal.z, InBinormal.z, program.local[2];\n"\ - "\n"\ - "# vertex into world position \n"\ - "DP4 Temp.x, InPos, program.local[0];\n"\ - "DP4 Temp.y, InPos, program.local[1];\n"\ - "DP4 Temp.z, InPos, program.local[2];\n"\ - "DP4 Temp.w, InPos, program.local[3];\n"\ - "\n"\ "# vertex - lightpositions \n"\ - "SUB TempLightVector1, program.local[12], Temp; \n"\ - "SUB TempLightVector2, program.local[14], Temp; \n"\ + "SUB TempLightVector1, program.local[12], InPos; \n"\ + "SUB TempLightVector2, program.local[14], InPos; \n"\ "\n"\ "# transform the light vector 1 with U, V, W \n"\ - "DP3 TempTransLightV1.x, TempTangent, TempLightVector1; \n"\ - "DP3 TempTransLightV1.y, TempBinormal, TempLightVector1; \n"\ - "DP3 TempTransLightV1.z, TempNormal, TempLightVector1; \n"\ + "DP3 TempTransLightV1.x, InTangent, TempLightVector1; \n"\ + "DP3 TempTransLightV1.y, InBinormal, TempLightVector1; \n"\ + "DP3 TempTransLightV1.z, InNormal, TempLightVector1; \n"\ "\n"\ "# transform the light vector 2 with U, V, W \n"\ - "DP3 TempTransLightV2.x, TempTangent, TempLightVector2; \n"\ - "DP3 TempTransLightV2.y, TempBinormal, TempLightVector2; \n"\ - "DP3 TempTransLightV2.z, TempNormal, TempLightVector2; \n"\ + "DP3 TempTransLightV2.x, InTangent, TempLightVector2; \n"\ + "DP3 TempTransLightV2.y, InBinormal, TempLightVector2; \n"\ + "DP3 TempTransLightV2.z, InNormal, TempLightVector2; \n"\ "\n"\ "# normalize light vector 1 \n"\ "DP3 TempTransLightV1.w, TempTransLightV1, TempTransLightV1; \n"\ @@ -277,6 +253,10 @@ void COpenGLNormalMapRenderer::OnSetConstants(IMaterialRendererServices* service u32 cnt = driver->getDynamicLightCount(); + // Load the inverse world matrix. + core::matrix4 invWorldMat; + driver->getTransform(video::ETS_WORLD).getInverse(invWorldMat); + for (u32 i=0; i<2; ++i) { video::SLight light; @@ -291,6 +271,9 @@ void COpenGLNormalMapRenderer::OnSetConstants(IMaterialRendererServices* service light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation + // Transform the light by the inverse world matrix to get it into object space. + invWorldMat.transformVect(light.Position); + services->setVertexShaderConstant( reinterpret_cast(&light.Position), 12+(i*2), 1); diff --git a/source/Irrlicht/COpenGLParallaxMapRenderer.cpp b/source/Irrlicht/COpenGLParallaxMapRenderer.cpp index 70be541c..3c3b0e71 100644 --- a/source/Irrlicht/COpenGLParallaxMapRenderer.cpp +++ b/source/Irrlicht/COpenGLParallaxMapRenderer.cpp @@ -49,9 +49,6 @@ const char OPENGL_PARALLAX_MAP_VSH[] = "PARAM MVP[4] = { state.matrix.mvp }; # modelViewProjection matrix.\n"\ "TEMP Temp;\n"\ "TEMP TempColor;\n"\ - "TEMP TempNormal;\n"\ - "TEMP TempTangent;\n"\ - "TEMP TempBinormal;\n"\ "TEMP TempLightVector1;\n"\ "TEMP TempLightVector2;\n"\ "TEMP TempEyeVector;\n"\ @@ -66,48 +63,27 @@ const char OPENGL_PARALLAX_MAP_VSH[] = "MOV OutPos.z, Temp.z;\n"\ "MOV result.fogcoord.x, Temp.z;\n"\ "\n"\ - "# transform normal \n"\ - "DP3 TempNormal.x, InNormal.x, program.local[0];\n"\ - "DP3 TempNormal.y, InNormal.y, program.local[1]; \n"\ - "DP3 TempNormal.z, InNormal.z, program.local[2];\n"\ - "\n"\ - "# transform tangent \n"\ - "DP3 TempTangent.x, InTangent.x, program.local[0];\n"\ - "DP3 TempTangent.y, InTangent.y, program.local[1]; \n"\ - "DP3 TempTangent.z, InTangent.z, program.local[2];\n"\ - "\n"\ - "# transform binormal \n"\ - "DP3 TempBinormal.x, InBinormal.x, program.local[0];\n"\ - "DP3 TempBinormal.y, InBinormal.y, program.local[1]; \n"\ - "DP3 TempBinormal.z, InBinormal.z, program.local[2];\n"\ - "\n"\ - "# vertex into world position \n"\ - "DP4 Temp.x, InPos, program.local[0];\n"\ - "DP4 Temp.y, InPos, program.local[1];\n"\ - "DP4 Temp.z, InPos, program.local[2];\n"\ - "DP4 Temp.w, InPos, program.local[3];\n"\ - "\n"\ "# vertex - lightpositions \n"\ - "SUB TempLightVector1, program.local[12], Temp; \n"\ - "SUB TempLightVector2, program.local[14], Temp; \n"\ + "SUB TempLightVector1, program.local[12], InPos; \n"\ + "SUB TempLightVector2, program.local[14], InPos; \n"\ "\n"\ "# eye vector \n"\ - "SUB Temp, program.local[16], Temp; \n"\ + "SUB Temp, program.local[16], InPos; \n"\ "\n"\ "# transform the light vector 1 with U, V, W \n"\ - "DP3 TempTransLightV1.x, TempTangent, TempLightVector1; \n"\ - "DP3 TempTransLightV1.y, TempBinormal, TempLightVector1; \n"\ - "DP3 TempTransLightV1.z, TempNormal, TempLightVector1; \n"\ + "DP3 TempTransLightV1.x, InTangent, TempLightVector1; \n"\ + "DP3 TempTransLightV1.y, InBinormal, TempLightVector1; \n"\ + "DP3 TempTransLightV1.z, InNormal, TempLightVector1; \n"\ "\n"\ "# transform the light vector 2 with U, V, W \n"\ - "DP3 TempTransLightV2.x, TempTangent, TempLightVector2; \n"\ - "DP3 TempTransLightV2.y, TempBinormal, TempLightVector2; \n"\ - "DP3 TempTransLightV2.z, TempNormal, TempLightVector2; \n"\ + "DP3 TempTransLightV2.x, InTangent, TempLightVector2; \n"\ + "DP3 TempTransLightV2.y, InBinormal, TempLightVector2; \n"\ + "DP3 TempTransLightV2.z, InNormal, TempLightVector2; \n"\ "\n"\ "# transform the eye vector with U, V, W \n"\ - "DP3 TempEyeVector.x, TempTangent, Temp; \n"\ - "DP3 TempEyeVector.y, TempBinormal, Temp; \n"\ - "DP3 TempEyeVector.z, TempNormal, Temp; \n"\ + "DP3 TempEyeVector.x, InTangent, Temp; \n"\ + "DP3 TempEyeVector.y, InBinormal, Temp; \n"\ + "DP3 TempEyeVector.z, InNormal, Temp; \n"\ "\n"\ "# normalize light vector 1 \n"\ "DP3 TempTransLightV1.w, TempTransLightV1, TempTransLightV1; \n"\ @@ -311,17 +287,6 @@ void COpenGLParallaxMapRenderer::OnSetConstants(IMaterialRendererServices* servi const core::matrix4& tWorld = driver->getTransform(video::ETS_WORLD).getTransposed(); services->setVertexShaderConstant(tWorld.pointer(), 0, 4); - // The viewpoint is at (0., 0., 0.) in eye space. - // Turning this into a vector [0 0 0 1] and multiply it by - // the inverse of the view matrix, the resulting vector is the - // object space location of the camera. - - f32 floats[4] = {0.0f,0.0f,0.0f,1.0f}; - core::matrix4 minv(driver->getTransform(video::ETS_VIEW)); - minv.makeInverse(); - minv.multiplyWith1x4Matrix(floats); - services->setVertexShaderConstant(floats, 16, 1); - // set transposed worldViewProj matrix core::matrix4 worldViewProj(driver->getTransform(video::ETS_PROJECTION)); worldViewProj *= driver->getTransform(video::ETS_VIEW); @@ -334,6 +299,10 @@ void COpenGLParallaxMapRenderer::OnSetConstants(IMaterialRendererServices* servi u32 cnt = driver->getDynamicLightCount(); + // Load the inverse world matrix. + core::matrix4 invWorldMat; + driver->getTransform(video::ETS_WORLD).getInverse(invWorldMat); + for (u32 i=0; i<2; ++i) { video::SLight light; @@ -348,6 +317,9 @@ void COpenGLParallaxMapRenderer::OnSetConstants(IMaterialRendererServices* servi light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation + // Transform the light by the inverse world matrix to get it into object space. + invWorldMat.transformVect(light.Position); + services->setVertexShaderConstant( reinterpret_cast(&light.Position), 12+(i*2), 1); @@ -355,6 +327,15 @@ void COpenGLParallaxMapRenderer::OnSetConstants(IMaterialRendererServices* servi reinterpret_cast(&light.DiffuseColor), 13+(i*2), 1); } + // Obtain the view position by transforming 0,0,0 by the inverse view matrix + // and then multiply this by the inverse world matrix. + core::vector3df viewPos(0.0f, 0.0f, 0.0f); + core::matrix4 inverseView; + driver->getTransform(video::ETS_VIEW).getInverse(inverseView); + inverseView.transformVect(viewPos); + invWorldMat.transformVect(viewPos); + services->setVertexShaderConstant(reinterpret_cast(&viewPos.X), 16, 1); + // set scale factor f32 factor = 0.02f; // default value if (CurrentScale != 0.0f) diff --git a/source/Irrlicht/CPLYMeshFileLoader.cpp b/source/Irrlicht/CPLYMeshFileLoader.cpp index f6a3354f..9cff36b3 100644 --- a/source/Irrlicht/CPLYMeshFileLoader.cpp +++ b/source/Irrlicht/CPLYMeshFileLoader.cpp @@ -34,7 +34,7 @@ CPLYMeshFileLoader::~CPLYMeshFileLoader() // (we do, but this could be disabled to increase the speed of loading hundreds of meshes) if (Buffer) { - delete Buffer; + delete [] Buffer; Buffer = 0; } @@ -270,7 +270,7 @@ IAnimatedMesh* CPLYMeshFileLoader::createMesh(io::IReadFile* file) // free the buffer - delete Buffer; + delete [] Buffer; Buffer = 0; File->drop(); File = 0; @@ -557,7 +557,7 @@ c8* CPLYMeshFileLoader::getNextLine() // begin at the start of the next line c8* pos = StartPointer; - while (*pos && pos < EndPointer && *pos != '\r' && *pos != '\n') + while (pos < EndPointer && *pos && *pos != '\r' && *pos != '\n') ++pos; if ( pos < EndPointer && ( *(pos+1) == '\r' || *(pos+1) == '\n') ) diff --git a/source/Irrlicht/CPakReader.cpp b/source/Irrlicht/CPakReader.cpp index 00388a43..34477bdc 100644 --- a/source/Irrlicht/CPakReader.cpp +++ b/source/Irrlicht/CPakReader.cpp @@ -15,20 +15,34 @@ namespace irr namespace io { +namespace +{ + +inline bool isHeaderValid(const SPAKFileHeader& header) +{ + const c8* tag = header.tag; + return tag[0] == 'P' && + tag[1] == 'A' && + tag[2] == 'C' && + tag[3] == 'K'; +} + +} // end namespace + //! Constructor CArchiveLoaderPAK::CArchiveLoaderPAK( io::IFileSystem* fs) : FileSystem(fs) { - #ifdef _DEBUG +#ifdef _DEBUG setDebugName("CArchiveLoaderPAK"); - #endif +#endif } //! returns true if the file maybe is able to be loaded by this class bool CArchiveLoaderPAK::isALoadableFileFormat(const io::path& filename) const { - return core::hasFileExtension ( filename, "pak" ); + return core::hasFileExtension(filename, "pak"); } //! Check to see if the loader can create archives of this type. @@ -47,7 +61,7 @@ IFileArchive* CArchiveLoaderPAK::createArchive(const io::path& filename, bool ig if (file) { - archive = createArchive ( file, ignoreCase, ignorePaths ); + archive = createArchive(file, ignoreCase, ignorePaths); file->drop (); } @@ -76,9 +90,9 @@ bool CArchiveLoaderPAK::isALoadableFileFormat(io::IReadFile* file) const { SPAKFileHeader header; - file->read( &header.tag, 4 ); + file->read(&header, sizeof(header)); - return header.tag[0] == 'P' && header.tag[1] == 'A'; + return isHeaderValid(header); } @@ -88,17 +102,14 @@ bool CArchiveLoaderPAK::isALoadableFileFormat(io::IReadFile* file) const CPakReader::CPakReader(IReadFile* file, bool ignoreCase, bool ignorePaths) : CFileList(file ? file->getFileName() : "", ignoreCase, ignorePaths), File(file) { - #ifdef _DEBUG +#ifdef _DEBUG setDebugName("CPakReader"); - #endif +#endif if (File) { File->grab(); - - // scan local headers scanLocalHeader(); - sort(); } } @@ -116,48 +127,43 @@ const IFileList* CPakReader::getFileList() const return this; } -//! scans for a local header, returns false if there is no more local file header. bool CPakReader::scanLocalHeader() { + SPAKFileHeader header; + + // Read and validate the header + File->read(&header, sizeof(header)); + if (!isHeaderValid(header)) + return false; - c8 tmp[1024]; - io::path PakFileName; - - memset(&header, 0, sizeof(SPAKFileHeader)); - File->read(&header, sizeof(SPAKFileHeader)); - - if (header.tag[0] != 'P' && header.tag[1] != 'A') - return false; // local file headers end here. - + // Seek to the table of contents +#ifdef __BIG_ENDIAN__ + header.offset = os::Byteswap::byteswap(header.offset); + header.length = os::Byteswap::byteswap(header.length); +#endif File->seek(header.offset); - const int count = header.length / ((sizeof(u32) * 2) + 56); + const int numberOfFiles = header.length / sizeof(SPAKFileEntry); - for(int i = 0; i < count; i++) + Offsets.reallocate(numberOfFiles); + // Loop through each entry in the table of contents + for(int i = 0; i < numberOfFiles; i++) { - // read filename - PakFileName.reserve(56+2); - File->read(tmp, 56); - tmp[56] = 0x0; - PakFileName = tmp; + // read an entry + SPAKFileEntry entry; + File->read(&entry, sizeof(entry)); - #ifdef _DEBUG - os::Printer::log(PakFileName.c_str()); - #endif - - s32 offset; - s32 size; - - File->read(&offset, sizeof(u32)); - File->read(&size, sizeof(u32)); - -#ifdef __BIG_ENDIAN__ - os::Byteswap::byteswap(offset); - os::Byteswap::byteswap(size); +#ifdef _DEBUG + os::Printer::log(entry.name); #endif - addItem(PakFileName, size, false, Offsets.size()); - Offsets.push_back(offset); +#ifdef __BIG_ENDIAN__ + entry.offset = os::Byteswap::byteswap(entry.offset); + entry.length = os::Byteswap::byteswap(entry.length); +#endif + + addItem(io::path(entry.name), entry.length, false, Offsets.size()); + Offsets.push_back(entry.offset); } return true; } diff --git a/source/Irrlicht/CPakReader.h b/source/Irrlicht/CPakReader.h index 22a81586..621afe90 100644 --- a/source/Irrlicht/CPakReader.h +++ b/source/Irrlicht/CPakReader.h @@ -20,13 +20,24 @@ namespace irr { namespace io { + //! File header containing location and size of the table of contents struct SPAKFileHeader { + // Don't change the order of these fields! They must match the order stored on disk. c8 tag[4]; u32 offset; u32 length; }; + //! An entry in the PAK file's table of contents. + struct SPAKFileEntry + { + // Don't change the order of these fields! They must match the order stored on disk. + c8 name[56]; + u32 offset; + u32 length; + }; + //! Archiveloader capable of loading PAK Archives class CArchiveLoaderPAK : public IArchiveLoader { @@ -79,7 +90,6 @@ namespace io // file archive methods //! return the id of the file Archive - virtual const io::path& getArchiveName() const { return File->getFileName(); @@ -99,16 +109,11 @@ namespace io private: - //! scans for a local header, returns false if there is no more local file header. + //! scans for a local header, returns false if the header is invalid bool scanLocalHeader(); - //! splits filename from zip file into useful filenames and paths - //void extractFilename(SPakFileEntry* entry); - IReadFile* File; - SPAKFileHeader header; - //! Contains offsets of the files from the start of the archive file core::array Offsets; }; diff --git a/source/Irrlicht/CSTLMeshFileLoader.cpp b/source/Irrlicht/CSTLMeshFileLoader.cpp index 28382934..720d6812 100644 --- a/source/Irrlicht/CSTLMeshFileLoader.cpp +++ b/source/Irrlicht/CSTLMeshFileLoader.cpp @@ -43,7 +43,9 @@ IAnimatedMesh* CSTLMeshFileLoader::createMesh(io::IReadFile* file) const u32 WORD_BUFFER_LENGTH = 512; SMesh* mesh = new SMesh(); - mesh->addMeshBuffer( new SMeshBuffer() ); + SMeshBuffer* meshBuffer = new SMeshBuffer(); + mesh->addMeshBuffer(meshBuffer); + meshBuffer->drop(); core::vector3df vertex[3]; core::vector3df normal; diff --git a/source/Irrlicht/CSceneManager.cpp b/source/Irrlicht/CSceneManager.cpp index 58ec488a..f5b8501f 100644 --- a/source/Irrlicht/CSceneManager.cpp +++ b/source/Irrlicht/CSceneManager.cpp @@ -662,65 +662,64 @@ IMeshSceneNode* CSceneManager::addOctTreeSceneNode(IMesh* mesh, ISceneNode* pare //! the camera will move too. //! \return Returns pointer to interface to camera ICameraSceneNode* CSceneManager::addCameraSceneNode(ISceneNode* parent, - const core::vector3df& position, const core::vector3df& lookat, s32 id) + const core::vector3df& position, const core::vector3df& lookat, s32 id, + bool makeActive) { if (!parent) parent = this; ICameraSceneNode* node = new CCameraSceneNode(parent, this, id, position, lookat); - node->drop(); - setActiveCamera(node); + if (makeActive) + setActiveCamera(node); + node->drop(); return node; } -//! Adds a camera scene node which is able to be controlld with the mouse similar +//! Adds a camera scene node which is able to be controlled with the mouse similar //! to in the 3D Software Maya by Alias Wavefront. //! The returned pointer must not be dropped. ICameraSceneNode* CSceneManager::addCameraSceneNodeMaya(ISceneNode* parent, - f32 rotateSpeed, f32 zoomSpeed, f32 translationSpeed, s32 id) + f32 rotateSpeed, f32 zoomSpeed, f32 translationSpeed, s32 id, + bool makeActive) { - if (!parent) - parent = this; + ICameraSceneNode* node = addCameraSceneNode(parent, core::vector3df(), + core::vector3df(0,0,100), id, makeActive); + if (node) + { + ISceneNodeAnimator* anm = new CSceneNodeAnimatorCameraMaya(CursorControl, + rotateSpeed, zoomSpeed, translationSpeed); - ICameraSceneNode* node = new CCameraSceneNode(parent, this, id); - ISceneNodeAnimator* anm = new CSceneNodeAnimatorCameraMaya(CursorControl, - rotateSpeed, zoomSpeed, translationSpeed); - - node->addAnimator(anm); - setActiveCamera(node); - - anm->drop(); - node->drop(); + node->addAnimator(anm); + anm->drop(); + } return node; } -//! Adds a camera scene node which is able to be controled with the mouse and keys +//! Adds a camera scene node which is able to be controlled with the mouse and keys //! like in most first person shooters (FPS): ICameraSceneNode* CSceneManager::addCameraSceneNodeFPS(ISceneNode* parent, - f32 rotateSpeed, f32 moveSpeed, s32 id, - SKeyMap* keyMapArray, s32 keyMapSize, bool noVerticalMovement, f32 jumpSpeed, bool invertMouseY) + f32 rotateSpeed, f32 moveSpeed, s32 id, SKeyMap* keyMapArray, + s32 keyMapSize, bool noVerticalMovement, f32 jumpSpeed, + bool invertMouseY, bool makeActive) { - if (!parent) - parent = this; + ICameraSceneNode* node = addCameraSceneNode(parent, core::vector3df(), + core::vector3df(0,0,100), id, makeActive); + if (node) + { + ISceneNodeAnimator* anm = new CSceneNodeAnimatorCameraFPS(CursorControl, + rotateSpeed, moveSpeed, jumpSpeed, + keyMapArray, keyMapSize, noVerticalMovement, invertMouseY); - ICameraSceneNode* node = new CCameraSceneNode(parent, this, id); - ISceneNodeAnimator* anm = new CSceneNodeAnimatorCameraFPS(CursorControl, - rotateSpeed, moveSpeed, jumpSpeed, - keyMapArray, keyMapSize, noVerticalMovement, invertMouseY); - - // Bind the node's rotation to its target. This is consistent with 1.4.2 and below. - node->bindTargetAndRotation(true); - - node->addAnimator(anm); - setActiveCamera(node); - - anm->drop(); - node->drop(); + // Bind the node's rotation to its target. This is consistent with 1.4.2 and below. + node->bindTargetAndRotation(true); + node->addAnimator(anm); + anm->drop(); + } return node; } @@ -1099,8 +1098,8 @@ ICameraSceneNode* CSceneManager::getActiveCamera() const //! \param camera: The new camera which should be active. void CSceneManager::setActiveCamera(ICameraSceneNode* camera) { - if (camera) - camera->grab(); + if (camera) + camera->grab(); if (ActiveCamera) ActiveCamera->drop(); @@ -1346,7 +1345,7 @@ void CSceneManager::drawAll() camWorldPos.set(0,0,0); if ( ActiveCamera ) { - ActiveCamera->OnRegisterSceneNode(); + ActiveCamera->render(); camWorldPos = ActiveCamera->getAbsolutePosition(); } @@ -2488,6 +2487,22 @@ void CSceneManager::serializeAttributes(io::IAttributes* out, io::SAttributeRead out->addString ("Name", Name.c_str()); out->addInt ("Id", ID ); out->addColorf ("AmbientLight", AmbientLight); + + // fog attributes from video driver + video::SColor color; + video::E_FOG_TYPE fogType; + f32 start, end, density; + bool pixelFog, rangeFog; + + Driver->getFog(color, fogType, start, end, density, pixelFog, rangeFog); + + out->addEnum("FogType", fogType, video::FogTypeNames); + out->addColorf("FogColor", color); + out->addFloat("FogStart", start); + out->addFloat("FogEnd", end); + out->addFloat("FogDensity", density); + out->addBool("FogPixel", pixelFog); + out->addBool("FogRange", rangeFog); } //! Reads attributes of the scene node. @@ -2497,6 +2512,23 @@ void CSceneManager::deserializeAttributes(io::IAttributes* in, io::SAttributeRea ID = in->getAttributeAsInt("Id"); AmbientLight = in->getAttributeAsColorf("AmbientLight"); + // fog attributes + video::SColor color; + video::E_FOG_TYPE fogType; + f32 start, end, density; + bool pixelFog, rangeFog; + if (in->existsAttribute("FogType")) + { + fogType = (video::E_FOG_TYPE) in->getAttributeAsEnumeration("FogType", video::FogTypeNames); + color = in->getAttributeAsColorf("FogColor").toSColor(); + start = in->getAttributeAsFloat("FogStart"); + end = in->getAttributeAsFloat("FogEnd"); + density = in->getAttributeAsFloat("FogDensity"); + pixelFog = in->getAttributeAsBool("FogPixel"); + rangeFog = in->getAttributeAsBool("FogRange"); + Driver->setFog(color, fogType, start, end, density, pixelFog, rangeFog); + } + RelativeTranslation.set(0,0,0); RelativeRotation.set(0,0,0); RelativeScale.set(1,1,1); @@ -2526,7 +2558,11 @@ const video::SColorf& CSceneManager::getAmbientLight() const //! Get a skinned mesh, which is not available as header-only code ISkinnedMesh* CSceneManager::createSkinnedMesh() { +#ifdef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ return new CSkinnedMesh(); +#else + return 0; +#endif } //! Returns a mesh writer implementation if available diff --git a/source/Irrlicht/CSceneManager.h b/source/Irrlicht/CSceneManager.h index 3082fa82..171ed2e2 100644 --- a/source/Irrlicht/CSceneManager.h +++ b/source/Irrlicht/CSceneManager.h @@ -133,20 +133,24 @@ namespace scene //! \return Pointer to interface to camera virtual ICameraSceneNode* addCameraSceneNode(ISceneNode* parent = 0, const core::vector3df& position = core::vector3df(0,0,0), - const core::vector3df& lookat = core::vector3df(0,0,100), s32 id=-1); + const core::vector3df& lookat = core::vector3df(0,0,100), + s32 id=-1, bool makeActive=true); //! Adds a camera scene node which is able to be controlle with the mouse similar //! like in the 3D Software Maya by Alias Wavefront. //! The returned pointer must not be dropped. virtual ICameraSceneNode* addCameraSceneNodeMaya(ISceneNode* parent = 0, - f32 rotateSpeed = -1500.0f, f32 zoomSpeed = 200.0f, f32 translationSpeed = 1500.0f, s32 id=-1); + f32 rotateSpeed = -1500.0f, f32 zoomSpeed = 200.0f, + f32 translationSpeed = 1500.0f, s32 id=-1, + bool makeActive=true); //! Adds a camera scene node which is able to be controled with the mouse and keys //! like in most first person shooters (FPS): virtual ICameraSceneNode* addCameraSceneNodeFPS(ISceneNode* parent = 0, f32 rotateSpeed = 100.0f, f32 moveSpeed = .5f, s32 id=-1, - SKeyMap* keyMapArray=0, s32 keyMapSize=0, bool noVerticalMovement=false, - f32 jumpSpeed = 0.f, bool invertMouseY=false); + SKeyMap* keyMapArray=0, s32 keyMapSize=0, + bool noVerticalMovement=false, f32 jumpSpeed = 0.f, + bool invertMouseY=false, bool makeActive=true); //! Adds a dynamic light scene node. The light will cast dynamic light on all //! other scene nodes in the scene, which have the material flag video::MTF_LIGHTING diff --git a/source/Irrlicht/CSceneNodeAnimatorCameraMaya.cpp b/source/Irrlicht/CSceneNodeAnimatorCameraMaya.cpp index 1842c08e..a2d34e16 100644 --- a/source/Irrlicht/CSceneNodeAnimatorCameraMaya.cpp +++ b/source/Irrlicht/CSceneNodeAnimatorCameraMaya.cpp @@ -110,6 +110,11 @@ void CSceneNodeAnimatorCameraMaya::animateNode(ISceneNode *node, u32 timeMs) { OldTarget = camera->getTarget(); OldCamera = camera; + LastCameraTarget = OldTarget; + } + else + { + OldTarget += camera->getTarget() - LastCameraTarget; } core::vector3df target = camera->getTarget(); @@ -227,6 +232,7 @@ void CSceneNodeAnimatorCameraMaya::animateNode(ISceneNode *node, u32 timeMs) camera->setPosition(Pos); camera->setTarget(target); camera->setUpVector(upVector); + LastCameraTarget = camera->getTarget(); } diff --git a/source/Irrlicht/CSceneNodeAnimatorCameraMaya.h b/source/Irrlicht/CSceneNodeAnimatorCameraMaya.h index 4350770f..e91c5fdf 100644 --- a/source/Irrlicht/CSceneNodeAnimatorCameraMaya.h +++ b/source/Irrlicht/CSceneNodeAnimatorCameraMaya.h @@ -99,6 +99,7 @@ namespace scene f32 CurrentZoom; f32 RotX, RotY; core::vector3df OldTarget; + core::vector3df LastCameraTarget; // to find out if the camera target was moved outside this animator scene::ICameraSceneNode* OldCamera; core::position2df MousePos; diff --git a/source/Irrlicht/CSceneNodeAnimatorRotation.cpp b/source/Irrlicht/CSceneNodeAnimatorRotation.cpp index 785a241d..3f5592b1 100644 --- a/source/Irrlicht/CSceneNodeAnimatorRotation.cpp +++ b/source/Irrlicht/CSceneNodeAnimatorRotation.cpp @@ -33,11 +33,11 @@ void CSceneNodeAnimatorRotation::animateNode(ISceneNode* node, u32 timeMs) // precision problems with huge floats. core::vector3df rot = node->getRotation() + Rotation*(diffTime*0.1f); if (rot.X>360.f) - fmodf(rot.X, 360.f); + rot.X=fmodf(rot.X, 360.f); if (rot.Y>360.f) - fmodf(rot.Y, 360.f); + rot.Y=fmodf(rot.Y, 360.f); if (rot.Z>360.f) - fmodf(rot.Z, 360.f); + rot.Z=fmodf(rot.Z, 360.f); node->setRotation(rot); StartTime=timeMs; } diff --git a/source/Irrlicht/CSkinnedMesh.cpp b/source/Irrlicht/CSkinnedMesh.cpp index 1ee487aa..0a8f152b 100644 --- a/source/Irrlicht/CSkinnedMesh.cpp +++ b/source/Irrlicht/CSkinnedMesh.cpp @@ -62,7 +62,6 @@ IMesh* CSkinnedMesh::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 return this; animateMesh((f32)frame, 1.0f); - buildAll_LocalAnimatedMatrices(); skinMesh(); return this; } @@ -117,18 +116,18 @@ void CSkinnedMesh::animateMesh(f32 frame, f32 blend) joint->Animatedscale = core::lerp(oldScale, scale, blend); joint->Animatedrotation.slerp(oldRotation, rotation, blend); } - - //Note: - //_LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for - //one render (to play two animations at the same time) _LocalAnimatedMatrix only needs to be built once. - //a call to buildAllLocalAnimatedMatrices is needed before skinning the mesh, and before the user gets the joints to move - - //---------------- - // Temp! - buildAll_LocalAnimatedMatrices(); - //----------------- } + //Note: + //_LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for + //one render (to play two animations at the same time) _LocalAnimatedMatrix only needs to be built once. + //a call to buildAllLocalAnimatedMatrices is needed before skinning the mesh, and before the user gets the joints to move + + //---------------- + // Temp! + buildAll_LocalAnimatedMatrices(); + //----------------- + updateBoundingBox(); } @@ -462,6 +461,7 @@ void CSkinnedMesh::skinMesh() for (i=0; isize(); ++i) (*SkinningBuffers)[i]->setDirty(EBT_VERTEX); } + updateBoundingBox(); } diff --git a/source/Irrlicht/CSoftwareDriver2.cpp b/source/Irrlicht/CSoftwareDriver2.cpp index f3f79086..6d9a97cc 100644 --- a/source/Irrlicht/CSoftwareDriver2.cpp +++ b/source/Irrlicht/CSoftwareDriver2.cpp @@ -2125,6 +2125,12 @@ void CBurningVideoDriver::drawStencilShadow(bool clearStencilBuffer, video::SCol } +core::dimension2du CBurningVideoDriver::getMaxTextureSize() const +{ + return core::dimension2du(SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE, SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE); +} + + } // end namespace video } // end namespace irr @@ -2149,3 +2155,4 @@ IVideoDriver* createSoftwareDriver2(const core::dimension2d& windowSize, bo } // end namespace video } // end namespace irr + diff --git a/source/Irrlicht/CSoftwareDriver2.h b/source/Irrlicht/CSoftwareDriver2.h index c5df9b9d..09679b01 100644 --- a/source/Irrlicht/CSoftwareDriver2.h +++ b/source/Irrlicht/CSoftwareDriver2.h @@ -154,6 +154,9 @@ namespace video //! Returns the graphics card vendor name. virtual core::stringc getVendorInfo(); + //! Returns the maximum texture size supported. + virtual core::dimension2du getMaxTextureSize() const; + protected: diff --git a/source/Irrlicht/CTRGouraud2.cpp b/source/Irrlicht/CTRGouraud2.cpp index aad5e9b6..61702585 100644 --- a/source/Irrlicht/CTRGouraud2.cpp +++ b/source/Irrlicht/CTRGouraud2.cpp @@ -265,9 +265,9 @@ void CTRGouraud2::drawTriangle ( const s4DVertex *a,const s4DVertex *b,const s4D const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRGouraudAlpha2.cpp b/source/Irrlicht/CTRGouraudAlpha2.cpp index 95c72098..dab272b4 100644 --- a/source/Irrlicht/CTRGouraudAlpha2.cpp +++ b/source/Irrlicht/CTRGouraudAlpha2.cpp @@ -276,9 +276,9 @@ void CTRGouraudAlpha2::drawTriangle ( const s4DVertex *a,const s4DVertex *b,cons const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRGouraudAlphaNoZ2.cpp b/source/Irrlicht/CTRGouraudAlphaNoZ2.cpp index a17302e4..50c52ef1 100644 --- a/source/Irrlicht/CTRGouraudAlphaNoZ2.cpp +++ b/source/Irrlicht/CTRGouraudAlphaNoZ2.cpp @@ -278,9 +278,9 @@ void CTRGouraudAlphaNoZ2::drawTriangle ( const s4DVertex *a,const s4DVertex *b,c const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureBlend.cpp b/source/Irrlicht/CTRTextureBlend.cpp index 19412bcc..736796e6 100644 --- a/source/Irrlicht/CTRTextureBlend.cpp +++ b/source/Irrlicht/CTRTextureBlend.cpp @@ -2004,9 +2004,9 @@ void CTRTextureBlend::drawTriangle ( const s4DVertex *a,const s4DVertex *b,const const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureDetailMap2.cpp b/source/Irrlicht/CTRTextureDetailMap2.cpp index a9131651..0f334588 100644 --- a/source/Irrlicht/CTRTextureDetailMap2.cpp +++ b/source/Irrlicht/CTRTextureDetailMap2.cpp @@ -282,9 +282,9 @@ void CTRTextureDetailMap2::drawTriangle ( const s4DVertex *a,const s4DVertex *b, const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureGouraud2.cpp b/source/Irrlicht/CTRTextureGouraud2.cpp index 42707489..5bf84556 100644 --- a/source/Irrlicht/CTRTextureGouraud2.cpp +++ b/source/Irrlicht/CTRTextureGouraud2.cpp @@ -294,9 +294,9 @@ void CTRTextureGouraud2::drawTriangle ( const s4DVertex *a,const s4DVertex *b,co const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureGouraudAdd2.cpp b/source/Irrlicht/CTRTextureGouraudAdd2.cpp index a42c7879..0ec81e8b 100644 --- a/source/Irrlicht/CTRTextureGouraudAdd2.cpp +++ b/source/Irrlicht/CTRTextureGouraudAdd2.cpp @@ -302,9 +302,9 @@ void CTRTextureGouraudAdd2::drawTriangle ( const s4DVertex *a,const s4DVertex *b const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); // find if the major edge is left or right aligned f32 temp[4]; diff --git a/source/Irrlicht/CTRTextureGouraudAddNoZ2.cpp b/source/Irrlicht/CTRTextureGouraudAddNoZ2.cpp index a7639609..02770668 100644 --- a/source/Irrlicht/CTRTextureGouraudAddNoZ2.cpp +++ b/source/Irrlicht/CTRTextureGouraudAddNoZ2.cpp @@ -270,9 +270,9 @@ void CTRTextureGouraudAddNoZ2::drawTriangle ( const s4DVertex *a,const s4DVertex const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureGouraudAlpha.cpp b/source/Irrlicht/CTRTextureGouraudAlpha.cpp index deb2c5a2..1e651f53 100644 --- a/source/Irrlicht/CTRTextureGouraudAlpha.cpp +++ b/source/Irrlicht/CTRTextureGouraudAlpha.cpp @@ -363,9 +363,9 @@ void CTRTextureGouraudAlpha2::drawTriangle ( const s4DVertex *a,const s4DVertex const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureGouraudAlphaNoZ.cpp b/source/Irrlicht/CTRTextureGouraudAlphaNoZ.cpp index 6538ec4d..394f41ed 100644 --- a/source/Irrlicht/CTRTextureGouraudAlphaNoZ.cpp +++ b/source/Irrlicht/CTRTextureGouraudAlphaNoZ.cpp @@ -363,9 +363,9 @@ void CTRTextureGouraudAlphaNoZ::drawTriangle ( const s4DVertex *a,const s4DVerte const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureGouraudNoZ2.cpp b/source/Irrlicht/CTRTextureGouraudNoZ2.cpp index 01c83c79..a05af5a2 100644 --- a/source/Irrlicht/CTRTextureGouraudNoZ2.cpp +++ b/source/Irrlicht/CTRTextureGouraudNoZ2.cpp @@ -266,9 +266,9 @@ void CTRTextureGouraudNoZ2::drawTriangle ( const s4DVertex *a,const s4DVertex *b const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureGouraudVertexAlpha2.cpp b/source/Irrlicht/CTRTextureGouraudVertexAlpha2.cpp index 92cda9e8..02a953ca 100644 --- a/source/Irrlicht/CTRTextureGouraudVertexAlpha2.cpp +++ b/source/Irrlicht/CTRTextureGouraudVertexAlpha2.cpp @@ -309,9 +309,9 @@ void CTRTextureVertexAlpha2::drawTriangle ( const s4DVertex *a,const s4DVertex * const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureLightMap2_Add.cpp b/source/Irrlicht/CTRTextureLightMap2_Add.cpp index 89360c0a..b7b7244c 100644 --- a/source/Irrlicht/CTRTextureLightMap2_Add.cpp +++ b/source/Irrlicht/CTRTextureLightMap2_Add.cpp @@ -290,9 +290,9 @@ void CTRTextureLightMap2_Add::drawTriangle ( const s4DVertex *a,const s4DVertex const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureLightMap2_M1.cpp b/source/Irrlicht/CTRTextureLightMap2_M1.cpp index 7fc0a73f..f1b262b9 100644 --- a/source/Irrlicht/CTRTextureLightMap2_M1.cpp +++ b/source/Irrlicht/CTRTextureLightMap2_M1.cpp @@ -260,9 +260,9 @@ void CTRTextureLightMap2_M1::drawTriangle ( const s4DVertex *a,const s4DVertex * const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureLightMap2_M2.cpp b/source/Irrlicht/CTRTextureLightMap2_M2.cpp index 4d4fc350..2591502b 100644 --- a/source/Irrlicht/CTRTextureLightMap2_M2.cpp +++ b/source/Irrlicht/CTRTextureLightMap2_M2.cpp @@ -260,9 +260,9 @@ void CTRTextureLightMap2_M2::drawTriangle ( const s4DVertex *a,const s4DVertex * const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureLightMap2_M4.cpp b/source/Irrlicht/CTRTextureLightMap2_M4.cpp index 8cbf465f..80a245e7 100644 --- a/source/Irrlicht/CTRTextureLightMap2_M4.cpp +++ b/source/Irrlicht/CTRTextureLightMap2_M4.cpp @@ -588,14 +588,13 @@ void CTRTextureLightMap2_M4::drawTriangle_Min ( const s4DVertex *a,const s4DVert const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; - // find if the major edge is left or right aligned f32 temp[4]; @@ -962,9 +961,9 @@ void CTRTextureLightMap2_M4::drawTriangle ( const s4DVertex *a,const s4DVertex * const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_EQUAL_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTRTextureLightMapGouraud2_M4.cpp b/source/Irrlicht/CTRTextureLightMapGouraud2_M4.cpp index 2da69b99..4097d9de 100644 --- a/source/Irrlicht/CTRTextureLightMapGouraud2_M4.cpp +++ b/source/Irrlicht/CTRTextureLightMapGouraud2_M4.cpp @@ -304,9 +304,9 @@ void CTRGTextureLightMap2_M4::drawTriangle ( const s4DVertex *a,const s4DVertex const f32 ba = b->Pos.y - a->Pos.y; const f32 cb = c->Pos.y - b->Pos.y; // calculate delta y of the edges - scan.invDeltaY[0] = (ca != 0.f)?core::reciprocal ( ca ):0.f; - scan.invDeltaY[1] = (ba != 0.f)?core::reciprocal ( ba ):0.f; - scan.invDeltaY[2] = (cb != 0.f)?core::reciprocal ( cb ):0.f; + scan.invDeltaY[0] = core::reciprocal( ca ); + scan.invDeltaY[1] = core::reciprocal( ba ); + scan.invDeltaY[2] = core::reciprocal( cb ); if ( F32_LOWER_0 ( scan.invDeltaY[0] ) ) return; diff --git a/source/Irrlicht/CTarReader.cpp b/source/Irrlicht/CTarReader.cpp index 04d92222..13604dce 100644 --- a/source/Irrlicht/CTarReader.cpp +++ b/source/Irrlicht/CTarReader.cpp @@ -10,7 +10,9 @@ #include "CLimitReadFile.h" #include "os.h" #include "coreutil.h" +#if !defined(_IRR_WINDOWS_CE_PLATFORM_) #include "errno.h" +#endif namespace irr { @@ -202,8 +204,10 @@ u32 CTarReader::populateFileList() } u32 size = strtoul(sSize.c_str(), NULL, 8); +#if !defined(_IRR_WINDOWS_CE_PLATFORM_) if (errno == ERANGE) os::Printer::log("File too large", fullPath, ELL_WARNING); +#endif // save start position u32 offset = pos + 512; diff --git a/source/Irrlicht/CTerrainSceneNode.cpp b/source/Irrlicht/CTerrainSceneNode.cpp index ca12be07..a7f77089 100644 --- a/source/Irrlicht/CTerrainSceneNode.cpp +++ b/source/Irrlicht/CTerrainSceneNode.cpp @@ -1101,10 +1101,10 @@ namespace scene for (s32 x = 1; x < TerrainData.Size - 1; ++x) { mb->getVertexBuffer()[x + yd].Pos.Y = - (mb->getVertexBuffer()[x-1 + yd].Pos.Y + - mb->getVertexBuffer()[x+1 + yd].Pos.Y + - mb->getVertexBuffer()[x + yd - TerrainData.Size].Pos.Y + - mb->getVertexBuffer()[x + yd - TerrainData.Size].Pos.Y) * 0.25f; + (mb->getVertexBuffer()[x-1 + yd].Pos.Y + //left + mb->getVertexBuffer()[x+1 + yd].Pos.Y + //right + mb->getVertexBuffer()[x + yd - TerrainData.Size].Pos.Y + //above + mb->getVertexBuffer()[x + yd + TerrainData.Size].Pos.Y) * 0.25f; //below } yd += TerrainData.Size; } diff --git a/source/Irrlicht/Irrlicht7.1.vcproj b/source/Irrlicht/Irrlicht7.1.vcproj index 8c25d151..b6c203fc 100644 --- a/source/Irrlicht/Irrlicht7.1.vcproj +++ b/source/Irrlicht/Irrlicht7.1.vcproj @@ -1667,12 +1667,6 @@ - - - - @@ -1972,30 +1966,30 @@ - - - - - - - - + + + + + + + + diff --git a/source/Irrlicht/Irrlicht8.0.vcproj b/source/Irrlicht/Irrlicht8.0.vcproj index a9766994..775216b4 100644 --- a/source/Irrlicht/Irrlicht8.0.vcproj +++ b/source/Irrlicht/Irrlicht8.0.vcproj @@ -1613,6 +1613,10 @@ RelativePath=".\glext.h" > + + + + @@ -2229,10 +2237,6 @@ RelativePath=".\CPLYMeshFileLoader.h" > - - @@ -2738,22 +2742,6 @@ RelativePath=".\CMemoryFile.h" > - - - - - - - - @@ -2762,6 +2750,18 @@ RelativePath="CMountPointReader.h" > + + + + + + @@ -2770,6 +2770,14 @@ RelativePath="CReadFile.h" > + + + + diff --git a/source/Irrlicht/Makefile b/source/Irrlicht/Makefile index 5cc7fb9c..d8d61e4d 100644 --- a/source/Irrlicht/Makefile +++ b/source/Irrlicht/Makefile @@ -54,7 +54,7 @@ OGLESINCLUDES = -I$(HOME)/irrlicht/SDKPackage-ogles1/Builds/OGLES/Include -I$(HO OGLESLINK = -L$(HOME)/irrlicht/SDKPackage-ogles1/Builds/OGLES/LinuxPC/Lib -lGLES_CM CXXINCS = -I../../include -Izlib -Ijpeglib -Ilibpng CPPFLAGS = $(CXXINCS) $(OGLESINCLUDES) -DIRRLICHT_EXPORTS=1 -CXXFLAGS = -Wall -pipe -fno-exceptions -fno-rtti -fstrict-aliasing +CXXFLAGS += -Wall -pipe -fno-exceptions -fno-rtti -fstrict-aliasing ifndef NDEBUG CXXFLAGS += -g -D_DEBUG else @@ -65,8 +65,8 @@ CXXFLAGS += -pg endif CFLAGS := -fexpensive-optimizations -O3 -DPNG_THREAD_UNSAFE_OK -DPNG_NO_MMX_CODE -DPNG_NO_MNG_FEATURES -sharedlib sharedlib_osx: CXXFLAGS += -fpic -sharedlib sharedlib_osx: CFLAGS += -fpic +sharedlib sharedlib_osx: CXXFLAGS += -fPIC +sharedlib sharedlib_osx: CFLAGS += -fPIC #multilib handling ifeq ($(HOSTTYPE), x86_64) @@ -76,11 +76,10 @@ endif #Linux specific options staticlib sharedlib install: SYSTEM = Linux STATIC_LIB = libIrrlicht.a -IRRLICHT_DLL := ../../bin/Win32-gcc/Irrlicht.dll LIB_PATH = ../../lib/$(SYSTEM) INSTALL_DIR = /usr/local/lib sharedlib install: SHARED_LIB = libIrrlicht.so -staticlib sharedlib: LDFLAGS = --no-export-all-symbols --add-stdcall-alias +staticlib sharedlib: LDFLAGS += --no-export-all-symbols --add-stdcall-alias sharedlib: LDFLAGS += -L/usr/X11R6/lib$(LIBSELECT) -lGL -lXxf86vm staticlib sharedlib: CXXINCS += -I/usr/X11R6/include @@ -89,12 +88,13 @@ staticlib_osx sharedlib_osx install_osx: SYSTEM = MacOSX staticlib_osx sharedlib_osx: IRROTHEROBJ += MacOSX/CIrrDeviceMacOSX.o MacOSX/OSXClipboard.o MacOSX/AppDelegate.o staticlib_osx sharedlib_osx: CXXINCS += -IMacOSX -I/usr/X11R6/include sharedlib_osx install_osx: SHARED_LIB = libIrrlicht.dylib -staticlib_osx sharedlib_osx: LDFLAGS = --no-export-all-symbols --add-stdcall-alias +staticlib_osx sharedlib_osx: LDFLAGS += --no-export-all-symbols --add-stdcall-alias sharedlib_osx: LDFLAGS += -L/usr/X11R6/lib$(LIBSELECT) -lGL -lXxf86vm #Windows specific options +IRRLICHT_DLL := ../../bin/Win32-gcc/Irrlicht.dll sharedlib_win32 staticlib_win32: SYSTEM = Win32-gcc -sharedlib_win32: LDFLAGS = -lgdi32 -lopengl32 -ld3dx9d -lwinmm +sharedlib_win32: LDFLAGS += -lgdi32 -lopengl32 -ld3dx9d -lwinmm sharedlib_win32 staticlib_win32: CPPFLAGS += -DIRR_COMPILE_WITH_DX9_DEV_PACK -D__GNUWIN32__ -D_WIN32 -DWIN32 -D_WINDOWS -D_MBCS -D_USRDLL staticlib_win32: CPPFLAGS += -D_IRR_STATIC_LIB_ @@ -104,7 +104,7 @@ all linux: staticlib # Builds Irrlicht as shared lib (libIrrlicht.so.versionNumber) and copies it into /lib/Linux sharedlib: $(LINKOBJ) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -shared -Wl,-soname,$(SHARED_LIB).$(VERSION_MAJOR).$(VERSION_MINOR) -fPIC -o $(SHARED_LIB).$(VERSION) $^ $(LDFLAGS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -shared -Wl,-soname,$(SHARED_LIB).$(VERSION_MAJOR).$(VERSION_MINOR) -o $(SHARED_LIB).$(VERSION) $^ $(LDFLAGS) cp $(SHARED_LIB).$(VERSION) $(LIB_PATH) # Builds Irrlicht as static lib (libIrrlicht.a) @@ -126,25 +126,17 @@ staticlib_win32: $(STATIC_LIB) # Builds Irrlicht as shared lib (libIrrlicht.so.versionNumber) and copies it into /lib/MacOSX sharedlib_osx: $(LINKOBJ) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -dynamiclib -Wl,-install_name,$(SHARED_LIB).$(VERSION_MAJOR).$(VERSION_MINOR) -fPIC -o $(SHARED_LIB).$(VERSION) $^ $(LDFLAGS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -dynamiclib -Wl,-install_name,$(SHARED_LIB).$(VERSION_MAJOR).$(VERSION_MINOR) -o $(SHARED_LIB).$(VERSION) $^ $(LDFLAGS) cp $(SHARED_LIB).$(VERSION) $(LIB_PATH) # Installs Irrlicht if it was created as shared lib -install: +install install_osx: $(RM) -r $(INSTALL_DIR)/../include/irrlicht mkdir -p $(INSTALL_DIR)/../include/irrlicht cp ../../include/*.h $(INSTALL_DIR)/../include/irrlicht/ cp $(LIB_PATH)/$(SHARED_LIB).$(VERSION) $(INSTALL_DIR) - cd $(INSTALL_DIR) && ln -s -f libIrrlicht.so.$(VERSION) $(SHARED_LIB) - ldconfig -n $(INSTALL_DIR) - -install_osx: - $(RM) -r $(INSTALL_DIR)/../include/irrlicht - mkdir -p $(INSTALL_DIR)/../include/irrlicht - cp ../../include/*.h $(INSTALL_DIR)/../include/irrlicht - cp $(LIB_PATH)/$(SHARED_LIB).$(VERSION) $(INSTALL_DIR) - cd $(INSTALL_DIR) && ln -s libIrrlicht.dylib.$(VERSION) $(SHARED_LIB) - ldconfig -n $(INSTALL_DIR) + cd $(INSTALL_DIR) && ln -s -f $(SHARED_LIB).$(VERSION) $(SHARED_LIB) +# ldconfig -n $(INSTALL_DIR) TAGS: ctags *.cpp ../../include/*.h *.h diff --git a/source/Irrlicht/SoftwareDriver2_helper.h b/source/Irrlicht/SoftwareDriver2_helper.h index 16ec3461..770648af 100644 --- a/source/Irrlicht/SoftwareDriver2_helper.h +++ b/source/Irrlicht/SoftwareDriver2_helper.h @@ -931,7 +931,7 @@ inline s32 intervall_intersect_test( const sIntervall& a, const sIntervall& b) } -} // namespace +} // end namespace irr #endif diff --git a/source/Irrlicht/os.cpp b/source/Irrlicht/os.cpp index 7fdf6fd5..deeb6ffb 100644 --- a/source/Irrlicht/os.cpp +++ b/source/Irrlicht/os.cpp @@ -69,8 +69,8 @@ namespace os tmp += L"\n"; OutputDebugStringW(tmp.c_str()); #else - OutputDebugString(message); - OutputDebugString("\n"); + OutputDebugStringA(message); + OutputDebugStringA("\n"); printf("%s\n", message); #endif } diff --git a/tests/b3dAnimation.cpp b/tests/b3dAnimation.cpp index 27a53872..272059fa 100644 --- a/tests/b3dAnimation.cpp +++ b/tests/b3dAnimation.cpp @@ -40,6 +40,7 @@ bool b3dAnimation(void) node1->setMaterialFlag(EMF_LIGHTING, false); node1->setAnimationSpeed(0.f); node1->setCurrentFrame(10.f); + node1->setDebugDataVisible(irr::scene::EDS_BBOX_BUFFERS); } node2 = smgr->addAnimatedMeshSceneNode(mesh); @@ -50,13 +51,14 @@ bool b3dAnimation(void) node2->setMaterialFlag(EMF_LIGHTING, false); node2->setAnimationSpeed(0.f); node2->setCurrentFrame(62.f); + node2->setDebugDataVisible(irr::scene::EDS_BBOX_BUFFERS); } (void)smgr->addCameraSceneNode(); // Just jump to the last frame since that's all we're interested in. device->run(); - driver->beginScene(true, true, SColor(255, 255, 255, 0)); + driver->beginScene(true, true, SColor(255, 60, 60, 60)); smgr->drawAll(); driver->endScene(); diff --git a/tests/lightMaps.cpp b/tests/lightMaps.cpp new file mode 100644 index 00000000..3a0d1f5f --- /dev/null +++ b/tests/lightMaps.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2008-2009 Christian Stehno, Colin MacDonald +// No rights reserved: this software is in the public domain. + +#include "testUtils.h" + +using namespace irr; +using namespace core; +using namespace scene; +using namespace video; +using namespace io; +using namespace gui; + +//! Tests lightmaps under all drivers that support them +static bool runTestWithDriver(E_DRIVER_TYPE driverType) +{ + IrrlichtDevice *device = createDevice( driverType, dimension2d(160, 120), 32); + if (!device) + return true; // Treat a failure to create a driver as benign; this saves a lot of #ifdefs + + IVideoDriver* driver = device->getVideoDriver(); + ISceneManager * smgr = device->getSceneManager(); + + bool result = true; + bool added = device->getFileSystem()->addZipFileArchive("../media/map-20kdm2.pk3"); + assert(added); + + if(added) + { + ISceneNode * node = smgr->addOctTreeSceneNode(smgr->getMesh("20kdm2.bsp")->getMesh(0), 0, -1, 1024); + assert(node); + + if (node) + { + node->setMaterialFlag(EMF_LIGHTING, false); + node->setPosition(core::vector3df(-1300,-820,-1249)); + node->setScale(core::vector3df(1, 5, 1)); + + (void)smgr->addCameraSceneNode(0, core::vector3df(0,0,0), core::vector3df(40,100,30)); + + driver->beginScene(true, true, video::SColor(255,255,255,0)); + smgr->drawAll(); + driver->endScene(); + + result = takeScreenshotAndCompareAgainstReference(driver, "-lightmaps.png", 96); + } + } + + device->drop(); + + return result; +} + + +bool lightMaps(void) +{ + bool passed = true; + + passed &= runTestWithDriver(EDT_BURNINGSVIDEO); + passed &= runTestWithDriver(EDT_DIRECT3D9); + passed &= runTestWithDriver(EDT_DIRECT3D8); + passed &= runTestWithDriver(EDT_OPENGL); + + return passed; +} + diff --git a/tests/main.cpp b/tests/main.cpp index 78e72986..4540abfe 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -58,6 +58,7 @@ int main(int argumentCount, char * arguments[]) TEST(disambiguateTextures); // Normally you should run this first, since it validates the working directory. TEST(filesystem); TEST(zipReader); + TEST(pakReader); TEST(exports); TEST(sceneCollisionManager); TEST(testVector3d); @@ -91,6 +92,8 @@ int main(int argumentCount, char * arguments[]) TEST(enumerateImageManipulators); TEST(testGeometryCreator); TEST(makeColorKeyTexture); + TEST(lightMaps); + TEST(testXML); const unsigned int numberOfTests = tests.size(); diff --git a/tests/media/Burning's Video-b3dAnimation.png b/tests/media/Burning's Video-b3dAnimation.png index 100d2bba5fb85c442179272c3a303501f37deb32..52feb695c0b60c6faf67e252e1ae50bd48a048ac 100644 GIT binary patch literal 6245 zcmcgx=QrGs)BWf~iB%Kb5)rG0sGAVcYeb^==tPeatj_8YRtZ)_536@VL|MHhtX{ut zkX53y_<8<`=f$0wGq3KPIcLtDnb?;)s?>K_?f?Klt*)l@>ehd`g+Dprt-bi+CJg}h z1l5(EzxL0;<(j&(8Gc^AS(bS-H6>LW@X$_=&d$7|%{AFIxrmBNB=x@4!`TC^PbFOB zOaspzD4aaHOF#<$2z{U+tWf>@Q;hcGHX~v+(Y9>rwVtF>|*pb*%`9lm&hu`5%7_BEePuTRNghpNZs_Ate#F%(lr`l$TX-433+DaclE+K zc_p!jh$$x8!boteNeoHmn7z_~>Ty}(g`y|cwgYi}D1`j{yfnJLj?_Ig`hwtHqDmc9 z{%ZdaR~tXV_W>m>BYP2;2Wzd~hxfH4`cC+EbxtGvd(H5cI}drKsPGloXAbn4~eRM+d}LhU&8Z(v8McPV>?-)h|>8J>tL`^$NgyI#IreJzCF z-oEtSgkU<91j?cFrm+X zg7V8JDn9emo=wMPs(j@+1T~IJp{$XCZfc@|leonNedci+2kfN?!FfrG%K5dGocz{9zijzHVTBdqR*q!EX$icXx z!JKW0=KhRNY&r-`2&xZHghN3V0jvasdIrhboAY$ofr*I_dC6%%zmrw0z8&)x2)|`} zdU|s>w&0plPpJ?_+(F?me&?IMLz`c)`$n!(&rc=t0{gz>X~_gMYh3omZSxCmPHsFJ zXR_SIm|^>=x(xrw$aWsp$ETS^po=w0xofy58It|Gqy9O_zgv)5%=xbLRlralsDQ^V zSCJ13g88TbvrNGX)6yEn#>UtC+|!>w!Xxb#3H=S`EF(m;n3TZu(}--wnqk^-ueRqN z@p5?NyVc1C6`2I4&NO^=jLh^R5D%ZGsUzT$H0$;^NN(WZ@XKXm55Ba->tT~pTqX69 zru)z_y>GLnxolT1A&1GMgioIroD4Qjv-BNOJ9Sz~a@XktTA_|%{}zJ6Q17%X zf5}?r8G9TaqD1@O1@Z|0X-0}Mg>E$bbw+W#k#GO?2N!aT)~T_LaMm}DNeeg0?3fjA zrUn#%`l>PA|B~Ixs;t$PBN@o;Ulbw=Wa2u{s3xO@W=-DX={w)5qWf7tY8fzZ3Si=1 z%t!qYjC(OHoI(I_TjjE-dqLOjgj_}CQXZX zs#qw1k>_^{Z2NFY)cG4XZ&AAOhHS$r7LWw$r(;R|Vc;ruX2QwW0!#?Pc)NQs(3Glr zHYMViD6<;AiH}I6BxQ#O#z9}-g6XB8N|$GS-I4$7t`TU}B( z5}svzOSQ{N%z|&oHTL7qqH^%1;)C5EAaE_=Lk@yvx#awYI{-aUTqyh_qgXTLHFTTn z)k|9;Oy!Nd=bc=+q&nqycx*gIo+}qOchVXLC(r!KPg`TsMiF&XQEx0tp_WFj2BMgW zRMb#{F5itvFE?Kv6Bma^aa9wuQ<5emCJfpVaA|zwV10$0Dx`?ZG^Q78YuN=QFSM|R z2&C;Tz9-bOc4fA@2NtqPcGDhGD;uF_Poa3t2XO_Obw*WE;L=G%_BGtJQa(x^#)5+M zDL+hH2zrMPS|fO;Bc|Cv@Y&325+*K9`Mo(kF)s1Xrp_G_?WKfr{Yo?v*#N@iJI2 zNP-EB=|!xk&|^`BY8y{WIP4yy26S6}J5U+A`J;@NB(bLyNQ9)+#8>iYRKwf>0Jp2L znRP}rtgE0t`yR%@vv}y8V;P`?J&jZ@ASQbt$0~~3%jdUi>p4y6qa20;rC7UNLEJjs zeUK0#7}U6L!LB}3ahRT25w?DyKm?;jvOQ=zK{^BXul)-t49Ed%ebDkw3QDXhLHg}4 zjUE9Umvh$$VW^QZ(&z;tD)v61an8fvW3(w-NAUQ0{I@zxi(a&y zqiO)hBMj+AQdqF86nToHXE>By_f1@4CNFoF(UK)VALSvz|9UXOiBPMsLW$<_a6a8z zde)>6`nkmc8+sBUur@J?(97iyVnkow4vv(W^7gOSX6aW^g>R z9s}fDc+e<#e^BA>sm!Gy*#b!kK}1vLou}bue9d%Gd~2e2UXO6cD6gGY)K6?$Pr9^; zuP%IA-w1C}dF#w4$`SfY>Ve_*`^PdQ$!QScqK3&hwezUm$rU$0t1+*0MKXZ4c|61H zW8ej(`XDEZ(SX<>@{5Jj6jL^(rN&co=Qm6;Go5K>RAbtAI(%jogYB7+F|O(3_cAQZ`sn6WiT*f3~-W*>N4zkCnXRY3Ye z+2DEi;b`j#8v~8|Uet{olSbKuLJBy2Je<3CN_gvF{&oBx5szMvxbZOzX zXT~v6v1Z&T#598l8;BJG<^>maDJq)Cv5^8ln1Dxj1!lQ5g4dioZXOuEXL(`Gf81&} z6l&LKP!a1oW0p*^h5d5%ZRGzm0xHAPLLOo~!`C8LWEsXszLPq#icO z%24)AhcII7{kXOyqMvM!RjX5+o@k9%Cz8Z616{MYa2t-$+_tCY?S48XX*nTzuW=-`{_x?s8wuie)ef0XO9`BobNJjLf=UG z-0=+-uh`W532Dw&t{%TaFBh)w!1Ta*HHz6J+T5__fuj&HI5P1A#I-f4AWhR&Cc)@$ z-t~#8=j`dtbxmQnOVrfl_%*-tb5QtaYdAmVFhLBKpFv>f6x>Mt6ygM;0v3b0FkS$o za>|Q4Hc@JT5o9(@C|1>(#&ygMAe$|z{6aIi>@dW^(rnl z2xGT7M9F^HhW!-3dakDY&?QThn3AAU|LCy{W~2qV*KCX)`G9oShCH@rX!t4WO449Z z5FTi1cC&AJEu=&tT%Fh9mgd#41F9#l-x0vkK7!LthT4E8vmSDmvKOjX0~-{Wf!p!s z#VA8;m{QQMXX1kU;MR8-gc)hB-1~5?(XxE?@CLtR->&p3vaSWpc4{%n@SIjxU*YUky8FXjAmnKyyZAM1?QA z&bogsv)ZmJC06VAKj`uVa+>8+fVHSB*lPauvJ&SzmNM6m2 zZ6rF$Aol_T&_lNE;n?%abte>JT@lougnBEy^XsEIQ=m$>tS1%G`hZ#tC8X6S-`Ut5{Y?bx%G z0IXH90F#C3odJbx>TU%mb*Z%$5+0poHIS3#MSImwWX65_;z`N zzL$ZF0*E1HTCZH*78CO>Ow$`=am-YExf-vCi;f4CWx0iQ z7ztv@oU{%a*~t%6^5?djgro!@LXt|69Nx>nk9p0*XGw_(setbMNiJ~dMqOX}`hX%6 z50^`#M)OzN6tSTI2qaP2cDIVC-u^orBe($7v?KB5`e~`c=}cJnU7mTKCU7k^H?y7vwVor_OGuEl{vAJ$9B%b$XIc z_x4x3NNl#KN7_zn z)`#zG(-~|c1&D$iUq0&FWsey#`n~hubU~}J+}#7;XLLG_)YZM(Ll|P+EX$uT1-4#h zC-xT2W*L+-2|sAVm<|1$Joe6&Te3-F%XJFAI=RTZUR;;Ih)CA9WtqyV_A(Ds<1)Y$ zJ9u|5>qzvYQ-!J>lIFXaMMXV}-L@dxt}foqixM$6} zPx48OVBLrvV0B>G=uH~E9>gc)T@6aS>?^qL3cI>IrI!-Py*gClDKlUu5ELBNS!`*1 zx1+gn{x5atBlBy>u|`8lQ&qdfn{q*Ay`I=bzm-So87l*^rm*7jc4=dwWuf=)XI8Gi zYBwBCx#U^oG`~OIX3nT(FY4~jja7NvY)2ew`j>si*UES8TYoBK?e}k72m2+HKwXwu z*~`DV*({tabKJ*KO6x)3ExLt8DK_Pi(hg~(I%#0GlqQTe71z zWwp{HI+6tlB96{~2ZNe0;d$|(KZbkPr$fvlY%{feoxKO5EL2>MbIt)?p4S7u1T!9G zzh{|A5psT&M@Lj=Eu*mq5Q>;{+sUbcR_~pl>AhXt9`mZ}^xmKL5KR2QK(yGQ`_;bR zXz-Enoq@(N;F~5TAVBFI?=4>C9{6qImiRidIxs~IPBo9;bm4q_< zWz|?fQCN~q%zTt+DHgBQ9+00{SRT713$5T^Xu}o_WR>CM8qvDK!?2duG`mS-c~4J0 zRj)**AAYLbr3*a}k$-Ur>YTe)`*6{CX3_Y2-V&MZYyd(0!~bs3RPQ@`U8uRgFg%=r zsSIeTDA2Po+70PA!0~Vf?Z+<$HgeuT&~ZBr>&+b!cUSr$%S(FxbIZC}dAWZBR+VF< zGfw}_ChToVC4Wz|P0&?tC6@Ype7%}0^{nk^tv_DGS9t92&A;C*!Ivknqt)}Wf%Dra zucL!QpDbSU2~a0K_Gw&*R+7G{L^nPPucp%f{gJxsX!3&v3s z6e2R3d-^x{e+ZLzxv*sT3~+Du+5VBTFK$s>-t*KHowWEpSUmf9K|o66&8`Z?HWQ_mq_4_{f`fxY0_!1L_k?xMw2z>wT^;$T zbxc*eFJA6flY2+I+-gx}36FUDwu3_5%Dm-*QUh`c9qzT(7^qi^^U4E zA0YAY@;PejJavFM(2OA+hM#|*uBh{plZ#VR-qaW*wrt;VQiY#gjAV6-EQBmY7FTsv znF(VLXBXQ}bVeUG>toS--?3QPeidNpJi?Nlr4(w{b$e8Ezo`q!*xQmh9r7A?>}YK| z9kBcK&^$apc&HDYW^nl5pQ~l6>ynTY%T27qDJS%7w`~u0+O{lr{;$b^*`zz@TAKR! z@&-31QwsQhX!D#U>O7x^xojR%mS~Lwk|fJKl1EoJ7oedGC&TpfySX08g*W_^19lu|YtkV=s11TN}H>_A!97#`JhHQWPPXy#dZZ*e`jjBkTtO9cd;TnaGQge)IXGn3a3%jG`+l2VWIb6M z3rwBxPWs+~tw66g#THAaL@Zj*M&y({rL`~p8?X7E-qEFEZzP6YhESe8!Z``>*O^o?=(Fa`~X>?_vRgyEtQ6}&d+x;xI z^F`dx-gPs4;})^ffJgx+Q2&hE*%&w^2P9JXiy{kWGu<(*tF3onw!P-5-IeEJc?EQx zwfdoJOHYezI7-HPkB_gnwAVP|b-QILbkyE`4N7eTUnic-1QGkJ_0UUtS5d^mLPEZG xNbL;EQY;mk7rND0r#9H>?~^+GKLdM1lqI6CE9!#~ynP=4>dHDwm5Ns3{{sX$NMQf~ literal 5982 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 literal 0 HcmV?d00001 diff --git a/tests/media/Direct3D 9.0-lightmaps.png b/tests/media/Direct3D 9.0-lightmaps.png new file mode 100644 index 0000000000000000000000000000000000000000..f53a7e36f1bbb442ecfb5d27f3f7e9190478ed00 GIT binary patch literal 33502 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 literal 0 HcmV?d00001 diff --git a/tests/media/OpenGL-lightmaps.png b/tests/media/OpenGL-lightmaps.png new file mode 100644 index 0000000000000000000000000000000000000000..f53a7e36f1bbb442ecfb5d27f3f7e9190478ed00 GIT binary patch literal 33502 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 literal 0 HcmV?d00001 diff --git a/tests/media/sample_pakfile.pak b/tests/media/sample_pakfile.pak new file mode 100644 index 0000000000000000000000000000000000000000..68e489a08f59e72581378623d9518564fad6ac24 GIT binary patch literal 88 zcmWG=boQ2DU|?_nVvp3EoP34y{Gyx`#gf$G5`7S%S5i^J;L4-|ly3my3?5e|h4&H+ bFJ^j_GcafZMHm>q0@;n?i++LB@IWyD=QtGw literal 0 HcmV?d00001 diff --git a/tests/media/test.xml b/tests/media/test.xml new file mode 100644 index 00000000..d311c1aa --- /dev/null +++ b/tests/media/test.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/pakReader.cpp b/tests/pakReader.cpp new file mode 100644 index 00000000..6de6e85b --- /dev/null +++ b/tests/pakReader.cpp @@ -0,0 +1,52 @@ +#include "testUtils.h" + +using namespace irr; +using namespace core; +using namespace io; + +bool pakReader(void) +{ + IrrlichtDevice * device = irr::createDevice(video::EDT_NULL, dimension2d(1, 1)); + assert(device); + if(!device) + return false; + + io::IFileSystem * fs = device->getFileSystem (); + if ( !fs ) + return false; + + if ( !fs->addFileArchive(io::path("media/sample_pakfile.pak"), /*bool ignoreCase=*/true, /*bool ignorePaths=*/false) ) + return false; + + // log what we got + io::IFileArchive* archive = fs->getFileArchive(fs->getFileArchiveCount()-1); + const io::IFileList* fileList = archive->getFileList(); + for ( u32 f=0; f < fileList->getFileCount(); ++f) + { + logTestString("File name: %s\n", fileList->getFileName(f).c_str()); + logTestString("Full path: %s\n", fileList->getFullFileName(f).c_str()); + } + + bool result = true; + io::path filename("test/test.txt"); + result &= fs->existFile(filename); + if (!result ) + { + logTestString("existFile failed"); + } + IReadFile* readFile = fs->createAndOpenFile(filename); + if ( !readFile ) + { + result = false; + logTestString("createAndOpenFilefailed"); + } + + char tmp[123] = {'\0'}; + readFile->read(tmp, sizeof(tmp)); + if (strncmp(tmp, "Hello world!", sizeof(tmp))) + { + result = false; + logTestString("Read bad data from pak file.\n"); + } + return result; +} diff --git a/tests/terrainSceneNode.cpp b/tests/terrainSceneNode.cpp index 1319e013..c34bfec7 100644 --- a/tests/terrainSceneNode.cpp +++ b/tests/terrainSceneNode.cpp @@ -45,7 +45,7 @@ bool terrainSceneNode(void) driver->endScene(); // Note that this has to be a slightly fuzzier than usual compare to satisfy multiple OpenGL environments - bool result = takeScreenshotAndCompareAgainstReference(driver, "-terrainSceneNode-1.png", 98.24f); + bool result = takeScreenshotAndCompareAgainstReference(driver, "-terrainSceneNode-1.png", 98.22f); if(!result) { logTestString("Small camera up rotation caused bad recalc.\n"); @@ -60,7 +60,7 @@ bool terrainSceneNode(void) smgr->drawAll(); driver->endScene(); - result &= takeScreenshotAndCompareAgainstReference(driver, "-terrainSceneNode-2.png", 98.84f); + result &= takeScreenshotAndCompareAgainstReference(driver, "-terrainSceneNode-2.png", 98.83f); if(!result) { logTestString("Large camera up rotation caused bad recalc.\n"); diff --git a/tests/testUtils.cpp b/tests/testUtils.cpp index a816e32b..8ddce54e 100644 --- a/tests/testUtils.cpp +++ b/tests/testUtils.cpp @@ -142,14 +142,14 @@ static float fuzzyCompareImages(irr::video::IImage * image1, image1->unlock(); image2->unlock(); - const u32 totalColours = pixels * 775; + const u32 totalColours = pixels * 255*3; return 100.f * (totalColours - mismatchedColours) / totalColours; } bool takeScreenshotAndCompareAgainstReference(irr::video::IVideoDriver * driver, - const char * fileName, - irr::f32 requiredMatch) + const char * fileName, + irr::f32 requiredMatch) { irr::video::IImage * screenshot = driver->createScreenShot(); if(!screenshot) @@ -206,7 +206,7 @@ bool takeScreenshotAndCompareAgainstReference(irr::video::IVideoDriver * driver, irr::core::stringc mismatchFilename = "results/"; mismatchFilename += driverName; mismatchFilename += fileName; - logTestString("Writing mismatched image to '%s\n", mismatchFilename.c_str()); + logTestString("Writing mismatched image to '%s'\n", mismatchFilename.c_str()); (void)driver->writeImageToFile(screenshot, mismatchFilename.c_str()); } @@ -265,3 +265,4 @@ void logTestString(const char * format, ...) OutputDebugStringA(logString); #endif // #if defined(TESTING_ON_WINDOWS) } + diff --git a/tests/testVector2d.cpp b/tests/testVector2d.cpp index 458bd9c8..0aa5fab4 100644 --- a/tests/testVector2d.cpp +++ b/tests/testVector2d.cpp @@ -52,7 +52,7 @@ static bool doTests() vec.set(5, 5); vec.normalize(); - compareVectors(vec, vector2d((T)0.70710681378841400, (T)0.70710681378841400)); + COMPARE_VECTORS(vec, vector2d((T)0.70710681378841400, (T)0.70710681378841400)); vec.set(5, 5); otherVec.set(10, 20); @@ -162,6 +162,16 @@ static bool doTests() return false; } + core::vector2d zeroZero(0, 0); + core::vector2d oneOne(1, 1); + // Check if comparing (0.0, 0.0) with (1.0, 1.0) returns false. + if(zeroZero == oneOne) + { + logTestString("\nERROR: vector2d %.16f, %.16f == vector2d %.16f, %.16f\n", + (f64)zeroZero.X, (f64)zeroZero.Y, (f64)oneOne.X, (f64)oneOne.Y); + return false; + } + return true; } diff --git a/tests/testVector3d.cpp b/tests/testVector3d.cpp index a4ef608c..1206f8dc 100644 --- a/tests/testVector3d.cpp +++ b/tests/testVector3d.cpp @@ -126,6 +126,17 @@ static bool doTests() if ( is_nan(vec) ) return false; + core::vector3d zeroZero(0, 0, 0); + core::vector3d oneOne(1, 1, 1); + // Check if comparing (0.0, 0.0, 0.0) with (1.0, 1.0, 1.0) returns false. + if(zeroZero == oneOne) + { + logTestString("\nERROR: vector3d %.16f, %.16f, %.16f == vector3d %.16f, %.16f, %.16f\n", + (f64)zeroZero.X, (f64)zeroZero.Y, (f64)zeroZero.Z, + (f64)oneOne.X, (f64)oneOne.Y, (f64)oneOne.Z); + return false; + } + return true; } diff --git a/tests/testXML.cpp b/tests/testXML.cpp new file mode 100644 index 00000000..19a15c4d --- /dev/null +++ b/tests/testXML.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2009 Christian Stehno +// No rights reserved: this software is in the public domain. + +#include "testUtils.h" + +using namespace irr; +using namespace core; + +/** Tests for XML handling */ +bool testXML(void) +{ + IrrlichtDevice *device = createDevice(video::EDT_NULL, dimension2du(400, 200)); + + io::IXMLReaderUTF8* reader = device->getFileSystem()->createXMLReaderUTF8("media/test.xml"); + if (!reader) + { + logTestString("Could not create XML reader.\n"); + return false; + } + + const core::stringc expected[] = { + "a", "b", "c" + }; + + bool retVal = true; + u32 i=0; + while(reader->read()) + { + if (reader->getNodeType() == io::EXN_ELEMENT) + { + if (expected[i++] != reader->getNodeName()) + { + logTestString("Did not find expected string in XML element name.\n"); + retVal = false; + break; + } + } + } + + reader->drop(); + return retVal; +} + diff --git a/tests/tests-last-passed-at.txt b/tests/tests-last-passed-at.txt index 44fe6c6e..8d848c37 100644 --- a/tests/tests-last-passed-at.txt +++ b/tests/tests-last-passed-at.txt @@ -1,2 +1,2 @@ -Test suite pass at GMT Wed Sep 9 14:11:32 2009 +Test suite pass at GMT Wed Oct 28 15:55:33 2009 diff --git a/tests/tests.cbp b/tests/tests.cbp index 171a70e1..b633a42c 100644 --- a/tests/tests.cbp +++ b/tests/tests.cbp @@ -14,6 +14,9 @@ + + +