From e379658827e0810b12a8907e1bd2c57372dcc0b9 Mon Sep 17 00:00:00 2001 From: hybrid Date: Wed, 18 May 2011 22:21:50 +0000 Subject: [PATCH] Merge from trunk, revisions up to 3726. Another major update to latest 1.8 changes. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@3727 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 28 + examples/01.HelloWorld/HelloWorld.cbp | 2 +- examples/02.Quake3Map/Quake3Map.cbp | 2 +- examples/02.Quake3Map/main.cpp | 2 +- .../03.CustomSceneNode/CustomSceneNode.cbp | 2 +- examples/04.Movement/Movement.cbp | 15 +- examples/05.UserInterface/UserInterface.cbp | 2 +- examples/06.2DGraphics/2DGraphics.cbp | 2 +- examples/07.Collision/Collision.cbp | 2 +- examples/07.Collision/main.cpp | 2 +- examples/08.SpecialFX/SpecialFX.cbp | 2 +- examples/09.Meshviewer/Meshviewer.cbp | 2 +- examples/09.Meshviewer/main.cpp | 3 +- examples/10.Shaders/Shaders.cbp | 2 +- .../11.PerPixelLighting/PerPixelLighting.cbp | 2 +- examples/11.PerPixelLighting/main.cpp | 6 +- .../12.TerrainRendering/TerrainRendering.cbp | 2 +- examples/12.TerrainRendering/main.cpp | 12 +- .../13.RenderToTexture/RenderToTexture.cbp | 2 +- examples/15.LoadIrrFile/LoadIrrFile.cbp | 2 +- .../16.Quake3MapShader/Quake3MapShader.cbp | 2 +- examples/16.Quake3MapShader/main.cpp | 4 +- examples/18.SplitScreen/SplitScreen.cbp | 2 +- examples/18.SplitScreen/main.cpp | 2 +- .../19.MouseAndJoystick/MouseAndJoystick.cbp | 2 +- examples/20.ManagedLights/ManagedLights.cbp | 2 +- examples/20.ManagedLights/main.cpp | 38 +- examples/21.Quake3Explorer/Quake3Explorer.cbp | 2 +- examples/21.Quake3Explorer/main.cpp | 2 +- examples/22.MaterialViewer/MaterialViewer.cbp | 2 +- .../22.MaterialViewer/MaterialViewer.vcproj | 2 +- .../MaterialViewer_vc9.vcproj | 2 +- examples/22.MaterialViewer/main.cpp | 36 +- examples/24.CursorControl/CursorControl.cbp | 2 +- examples/25.XmlHandling/XmlHandling.cbp | 2 +- .../25.XmlHandling/XmlHandling_vc10.vcxproj | 102 ++ examples/25.XmlHandling/main.cpp | 155 ++- examples/26.OcclusionQuery/Makefile | 39 + examples/26.OcclusionQuery/OcclusionQuery.cbp | 59 + examples/26.OcclusionQuery/OcclusionQuery.dev | 59 + .../26.OcclusionQuery/OcclusionQuery.vcproj | 163 +++ .../OcclusionQuery_vc10.vcxproj | 128 ++ .../OcclusionQuery_vc8.vcproj | 231 ++++ .../OcclusionQuery_vc9.vcproj | 230 ++++ examples/26.OcclusionQuery/main.cpp | 213 +++ examples/BuildAllExamples.workspace | 9 +- examples/BuildAllExamples_v7.sln | 24 +- examples/BuildAllExamples_vc10.sln | 18 + examples/Demo/CDemo.cpp | 5 +- examples/Demo/CMainMenu.cpp | 4 +- examples/Demo/Demo_vc10.vcxproj | 3 +- examples/Demo/demo.cbp | 2 +- include/EDriverFeatures.h | 6 + include/EMaterialFlags.h | 8 +- include/IAnimatedMesh.h | 91 -- include/IAnimatedMeshMD3.h | 30 +- include/IAnimatedMeshSceneNode.h | 10 +- include/IAttributes.h | 7 +- include/IBoneSceneNode.h | 4 +- include/IEventReceiver.h | 9 +- include/IFileSystem.h | 55 +- include/IGUIListBox.h | 4 + include/IGUISkin.h | 9 +- include/IGUIStaticText.h | 11 + include/IGUITable.h | 3 + include/IGUITreeView.h | 6 +- include/ILightManager.h | 2 +- include/IMeshCache.h | 15 +- include/IMeshManipulator.h | 8 +- include/IParticleSystemSceneNode.h | 2 +- include/IQ3Shader.h | 13 +- include/ISceneLoader.h | 58 + include/ISceneManager.h | 172 ++- include/ISceneNode.h | 6 +- include/ISceneNodeAnimatorFactory.h | 2 - include/ITriangleSelector.h | 53 +- include/IVideoDriver.h | 44 +- include/IrrCompileConfig.h | 22 +- include/SColor.h | 85 +- include/SMaterial.h | 91 +- include/fast_atof.h | 331 +++-- include/irrMap.h | 2 +- include/irrlicht.h | 1 + include/quaternion.h | 83 +- include/vector2d.h | 4 + readme.txt | 1 - source/Irrlicht/C3DSMeshFileLoader.cpp | 42 +- source/Irrlicht/CAnimatedMeshHalfLife.cpp | 474 +++---- source/Irrlicht/CAnimatedMeshHalfLife.h | 788 ++++++----- source/Irrlicht/CAnimatedMeshMD3.cpp | 24 +- source/Irrlicht/CAnimatedMeshSceneNode.cpp | 10 +- source/Irrlicht/CAnimatedMeshSceneNode.h | 4 +- source/Irrlicht/CAttributeImpl.h | 14 +- source/Irrlicht/CAttributes.cpp | 14 +- source/Irrlicht/CAttributes.h | 6 +- source/Irrlicht/CBlit.h | 470 +++++-- source/Irrlicht/CD3D8Driver.cpp | 78 +- source/Irrlicht/CD3D8Texture.cpp | 2 +- source/Irrlicht/CD3D9Driver.cpp | 150 +- source/Irrlicht/CD3D9Driver.h | 2 +- source/Irrlicht/CD3D9MaterialRenderer.h | 175 ++- source/Irrlicht/CD3D9ShaderMaterialRenderer.h | 1 - .../CDefaultSceneNodeAnimatorFactory.cpp | 2 + source/Irrlicht/CFileSystem.cpp | 163 ++- source/Irrlicht/CFileSystem.h | 15 + source/Irrlicht/CGUIColorSelectDialog.cpp | 278 ++-- source/Irrlicht/CGUIColorSelectDialog.h | 16 +- source/Irrlicht/CGUIEnvironment.cpp | 5 +- source/Irrlicht/CGUIFont.cpp | 22 +- source/Irrlicht/CGUIListBox.cpp | 27 +- source/Irrlicht/CGUIListBox.h | 3 + source/Irrlicht/CGUIScrollBar.cpp | 4 + source/Irrlicht/CGUISpinBox.cpp | 4 +- source/Irrlicht/CGUIStaticText.cpp | 269 +++- source/Irrlicht/CGUIStaticText.h | 12 + source/Irrlicht/CGUITabControl.cpp | 8 +- source/Irrlicht/CGUITable.cpp | 30 +- source/Irrlicht/CGUITable.h | 3 + source/Irrlicht/CImageLoaderJPG.cpp | 1 + source/Irrlicht/CImageLoaderPCX.cpp | 18 +- source/Irrlicht/CImageLoaderPNG.cpp | 29 +- source/Irrlicht/CImageLoaderPPM.cpp | 14 +- source/Irrlicht/CIrrDeviceConsole.cpp | 3 + source/Irrlicht/CIrrDeviceSDL.cpp | 22 +- source/Irrlicht/CIrrDeviceWin32.cpp | 562 ++++++-- source/Irrlicht/CIrrDeviceWin32.h | 20 +- source/Irrlicht/CIrrDeviceWinCE.cpp | 18 +- source/Irrlicht/CMD2MeshFileLoader.cpp | 1 - source/Irrlicht/CMeshManipulator.cpp | 104 +- source/Irrlicht/CMeshSceneNode.cpp | 11 +- source/Irrlicht/CMetaTriangleSelector.cpp | 31 + source/Irrlicht/CMetaTriangleSelector.h | 13 +- source/Irrlicht/CMountPointReader.cpp | 2 + source/Irrlicht/CNullDriver.cpp | 96 +- source/Irrlicht/CNullDriver.h | 9 +- source/Irrlicht/COBJMeshFileLoader.cpp | 14 +- source/Irrlicht/COgreMeshFileLoader.cpp | 42 +- source/Irrlicht/COgreMeshFileLoader.h | 4 +- source/Irrlicht/COpenGLDriver.cpp | 356 +++-- source/Irrlicht/COpenGLDriver.h | 16 +- source/Irrlicht/COpenGLExtensionHandler.cpp | 786 +++++------ source/Irrlicht/COpenGLExtensionHandler.h | 112 +- source/Irrlicht/COpenGLMaterialRenderer.h | 80 +- source/Irrlicht/COpenGLTexture.cpp | 2 +- source/Irrlicht/CParticleSphereEmitter.cpp | 2 +- source/Irrlicht/CSMFMeshFileLoader.cpp | 232 ++++ source/Irrlicht/CSMFMeshFileLoader.h | 67 + source/Irrlicht/CSceneLoaderIrr.cpp | 280 ++++ source/Irrlicht/CSceneLoaderIrr.h | 78 ++ source/Irrlicht/CSceneManager.cpp | 546 +++----- source/Irrlicht/CSceneManager.h | 41 +- source/Irrlicht/CSoftwareDriver2.cpp | 23 + source/Irrlicht/CSoftwareDriver2.h | 5 + source/Irrlicht/CTerrainSceneNode.cpp | 52 +- source/Irrlicht/CTerrainSceneNode.h | 35 +- source/Irrlicht/CTerrainTriangleSelector.cpp | 66 +- source/Irrlicht/CTerrainTriangleSelector.h | 36 +- source/Irrlicht/CTextSceneNode.cpp | 7 +- source/Irrlicht/CTextSceneNode.h | 12 +- source/Irrlicht/CTriangleSelector.cpp | 32 + source/Irrlicht/CTriangleSelector.h | 9 + source/Irrlicht/CXMeshFileLoader.cpp | 8 +- source/Irrlicht/IAttribute.h | 2 +- source/Irrlicht/Irrlicht-gcc.cbp | 5 + source/Irrlicht/Irrlicht10.0.vcxproj | 25 +- source/Irrlicht/Irrlicht10.0.vcxproj.filters | 20 +- source/Irrlicht/Irrlicht7.1.vcproj | 15 + source/Irrlicht/Irrlicht8.0.vcproj | 23 +- source/Irrlicht/Irrlicht9.0.vcproj | 22 +- source/Irrlicht/Irrlicht_mobile6.vcproj | 9 + source/Irrlicht/Irrlicht_xbox.vcproj | 6 + source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm | 80 +- .../MacOSX/MacOSX.xcodeproj/project.pbxproj | 1212 ++++++++++++++++- source/Irrlicht/Makefile | 14 +- source/Irrlicht/dmfsupport.h | 16 +- source/Irrlicht/glext.h | 46 +- source/Irrlicht/os.cpp | 2 +- source/Irrlicht/wglext.h | 32 +- source/source.txt | 4 +- tests/2dmaterial.cpp | 737 +++++++++- tests/anti-aliasing.cpp | 2 +- tests/draw2DImage.cpp | 42 +- tests/drawPixel.cpp | 75 +- tests/fast_atof.cpp | 163 ++- tests/filesystem.cpp | 2 +- tests/ioScene.cpp | 23 +- tests/lightMaps.cpp | 2 +- tests/loadTextures.cpp | 2 +- tests/main.cpp | 1 + tests/makeColorKeyTexture.cpp | 17 +- tests/material.cpp | 67 + tests/matrixOps.cpp | 14 +- tests/media/Burning's Video-2dmatFilter.png | Bin 3073 -> 3043 bytes .../Burning's Video-draw2DImage4cFilter.png | Bin 0 -> 20835 bytes tests/media/Burning's Video-loadScene.png | Bin 29997 -> 29530 bytes tests/media/Burning's Video-pixelAccuracy.png | Bin 0 -> 728 bytes tests/media/Burning's Video-rttAndText.png | Bin 1908 -> 6789 bytes .../Direct3D 9.0-draw2DImage4cFilter.png | Bin 0 -> 32938 bytes tests/media/Direct3D 9.0-draw2DImagePNG.png | Bin 0 -> 23799 bytes tests/media/Direct3D 9.0-pixelAccuracy.png | Bin 0 -> 728 bytes tests/media/Direct3D 9.0-polygonBack.png | Bin 0 -> 8592 bytes tests/media/Direct3D 9.0-polygonFront.png | Bin 0 -> 8592 bytes ...icht Software Driver 1.0-pixelAccuracy.png | Bin 0 -> 728 bytes tests/media/OpenGL-draw2DImage4cFilter.png | Bin 0 -> 36266 bytes tests/media/OpenGL-draw2DImagePNG.png | Bin 0 -> 23838 bytes tests/media/OpenGL-pixelAccuracy.png | Bin 0 -> 728 bytes tests/media/OpenGL-polygonBack.png | Bin 0 -> 8166 bytes tests/media/OpenGL-polygonFront.png | Bin 0 -> 8181 bytes tests/media/RedbrushAlpha-0.25.png | Bin 0 -> 35945 bytes tests/media/licenses.txt | 7 + tests/media/title_font.png | Bin 0 -> 190372 bytes tests/media/title_font.xml | Bin 0 -> 21356 bytes tests/media/title_font_2.png | Bin 0 -> 157760 bytes tests/mrt.cpp | 4 + tests/planeMatrix.cpp | 2 +- tests/renderTargetTexture.cpp | 365 +++-- tests/skinnedMesh.cpp | 1 - tests/terrainSceneNode.cpp | 5 +- tests/testQuaternion.cpp | 245 +++- tests/tests-last-passed-at.txt | 2 +- tests/tests.cbp | 1 + tests/tests_vc10.vcxproj | 3 +- tests/tests_vc8.vcproj | 4 + tests/tests_vc9.vcproj | 4 + tests/transparentMaterials.cpp | 12 +- tests/videoDriver.cpp | 1 + tools/FileToHeader/FileToHeader.cbp | 51 + tools/FileToHeader/FileToHeader.layout | 7 + tools/FileToHeader/Makefile | 35 + tools/FileToHeader/main.cpp | 175 +++ tools/GUIEditor/CGUIAttributeEditor.cpp | 6 +- tools/GUIEditor/CGUIEditWorkspace.cpp | 20 +- tools/GUIEditor/GUI Editor_v7.vcproj | 2 +- tools/GUIEditor/GUI Editor_v8.vcproj | 2 +- tools/GUIEditor/GUI Editor_v9.vcproj | 2 +- tools/GUIEditor/GUI Editor_vc10.vcxproj | 1 - tools/GUIEditor/GUIEditor_gcc.cbp | 2 +- tools/MeshConverter/MeshConverter.cbp | 2 +- 238 files changed, 10084 insertions(+), 3457 deletions(-) create mode 100644 examples/25.XmlHandling/XmlHandling_vc10.vcxproj create mode 100644 examples/26.OcclusionQuery/Makefile create mode 100644 examples/26.OcclusionQuery/OcclusionQuery.cbp create mode 100644 examples/26.OcclusionQuery/OcclusionQuery.dev create mode 100644 examples/26.OcclusionQuery/OcclusionQuery.vcproj create mode 100644 examples/26.OcclusionQuery/OcclusionQuery_vc10.vcxproj create mode 100644 examples/26.OcclusionQuery/OcclusionQuery_vc8.vcproj create mode 100644 examples/26.OcclusionQuery/OcclusionQuery_vc9.vcproj create mode 100644 examples/26.OcclusionQuery/main.cpp create mode 100644 include/ISceneLoader.h create mode 100644 source/Irrlicht/CSMFMeshFileLoader.cpp create mode 100644 source/Irrlicht/CSMFMeshFileLoader.h create mode 100644 source/Irrlicht/CSceneLoaderIrr.cpp create mode 100644 source/Irrlicht/CSceneLoaderIrr.h create mode 100644 tests/material.cpp create mode 100644 tests/media/Burning's Video-draw2DImage4cFilter.png create mode 100644 tests/media/Burning's Video-pixelAccuracy.png create mode 100644 tests/media/Direct3D 9.0-draw2DImage4cFilter.png create mode 100644 tests/media/Direct3D 9.0-draw2DImagePNG.png create mode 100644 tests/media/Direct3D 9.0-pixelAccuracy.png create mode 100644 tests/media/Direct3D 9.0-polygonBack.png create mode 100644 tests/media/Direct3D 9.0-polygonFront.png create mode 100644 tests/media/Irrlicht Software Driver 1.0-pixelAccuracy.png create mode 100644 tests/media/OpenGL-draw2DImage4cFilter.png create mode 100644 tests/media/OpenGL-draw2DImagePNG.png create mode 100644 tests/media/OpenGL-pixelAccuracy.png create mode 100644 tests/media/OpenGL-polygonBack.png create mode 100644 tests/media/OpenGL-polygonFront.png create mode 100644 tests/media/RedbrushAlpha-0.25.png create mode 100644 tests/media/licenses.txt create mode 100644 tests/media/title_font.png create mode 100644 tests/media/title_font.xml create mode 100644 tests/media/title_font_2.png create mode 100644 tools/FileToHeader/FileToHeader.cbp create mode 100644 tools/FileToHeader/FileToHeader.layout create mode 100644 tools/FileToHeader/Makefile create mode 100644 tools/FileToHeader/main.cpp diff --git a/changes.txt b/changes.txt index 09823396..479d4f38 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,23 @@ Changes in 1.8 (??.??.2011) + - Added IGUIListBox::getItemAt + + - Added IGUITable::getColumnWidth + + - Added the ability to open an archive from an IReadFile*, added a FileToHeader tool with instructions of how to make a portable app that consists of a single executable file. + + - Added ISceneManager::createSceneNodeAnimator to create animators by name + + - The Makefile now creates a symlink from the soname to the binary name during install. Binary compatibility is only confirmed between minor releases, so the only useful symlink is from libIrrlicht.so.1.8 to libIrrlicht.so.1.8.0; others should rightly fail. + + - Added SMF mesh loader, loads meshes from 3D World Studio. Originally written by Joseph Ellis + + - The loader selection process now consistently checks loader lists in reverse order, so new loaders added to Irrlicht override the internal ones. This applies when adding external mesh, image, scene and archive loaders, image writers, plus node, animator and GUI element factories. + + - Added getters to retrieve mesh, scene and archive loaders. + + - Added ISceneLoader interface and .irr loader. Users can now add their own scene loaders to the scene manager and use them by calling loadScene. + - Renamed IGUIElement::bringToBack to sendToBack, like in Windows - Send EGET_ELEMENT_CLOSED event when context menues should be closed (thx @ Mloren for reporting). @@ -245,6 +263,12 @@ The following names can be queried for the given types: ----------------------------- Changes in 1.7.3 (??.??.2011) + - CGUIScrollBar passes unused mousemove-events now to parent element. Fixes focus-bug in ComboBox reported by REDDemon here: http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=43474&highlight= + + - Fix clipping in CGUITabControl + + - Fix clipping in CGUITable, reported by ceyron + - Skip bone weights and additional information in ms3d file if no joint was defined before. - Fix mem leak in CImage, found by mloren. @@ -876,6 +900,7 @@ Changes in 1.6 (23.09.2009) wrong size of Matrices copy. renamed E_TRANSFORMATION_STATE_2 to E_TRANSFORMATION_STATE_FRUSTUM therefore also changed SViewFrustum::setTransformState to not tap + in the pitfall again of wrong memory... - moved @@ -1819,6 +1844,7 @@ Changes in version 1.4.1 (04 Jun 2008) - Avoid a crash when passing setSkin the current skin + - Fixed current frame calculation for non-looped animations. Bug reported by greenya. - Fixed bug in CBillboardSceneNode::setColor, reported by rogerborg @@ -2508,6 +2534,7 @@ Font improvements: of Animation name... - changed spelling "frustrum" to "frustum" + -> changed also SViewFrustrum.h to SViewFrustum.h - changed Parameter AutomaticCulling from bool to enum E_CULLING_TYPE @@ -4127,6 +4154,7 @@ Changes in version 0.4.2: (13 Dec 2003) ------------------------------------------------------------------------------------- Changes in version 0.4.1: (18 Sep 2003) + - Input events are now processed faster than repaint and window events. This means that input commands now effect things directly, in earlier versions there could be a delay when there were low frames per second. Some people diff --git a/examples/01.HelloWorld/HelloWorld.cbp b/examples/01.HelloWorld/HelloWorld.cbp index 829e1894..ae1b983c 100644 --- a/examples/01.HelloWorld/HelloWorld.cbp +++ b/examples/01.HelloWorld/HelloWorld.cbp @@ -40,7 +40,7 @@ - + diff --git a/examples/02.Quake3Map/Quake3Map.cbp b/examples/02.Quake3Map/Quake3Map.cbp index 172ead1f..b435a5fc 100644 --- a/examples/02.Quake3Map/Quake3Map.cbp +++ b/examples/02.Quake3Map/Quake3Map.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/02.Quake3Map/main.cpp b/examples/02.Quake3Map/main.cpp index f37b95dd..6cfcea27 100644 --- a/examples/02.Quake3Map/main.cpp +++ b/examples/02.Quake3Map/main.cpp @@ -101,7 +101,7 @@ int main() we are able to read from the files in that archive as if they are directly stored on the disk. */ - device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3"); + device->getFileSystem()->addFileArchive("../../media/map-20kdm2.pk3"); /* Now we can load the mesh by calling diff --git a/examples/03.CustomSceneNode/CustomSceneNode.cbp b/examples/03.CustomSceneNode/CustomSceneNode.cbp index fa71ac1e..38f51a1d 100644 --- a/examples/03.CustomSceneNode/CustomSceneNode.cbp +++ b/examples/03.CustomSceneNode/CustomSceneNode.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/04.Movement/Movement.cbp b/examples/04.Movement/Movement.cbp index 7f5a2ebf..c3b8bbcb 100644 --- a/examples/04.Movement/Movement.cbp +++ b/examples/04.Movement/Movement.cbp @@ -7,7 +7,7 @@ - + - + - + diff --git a/examples/05.UserInterface/UserInterface.cbp b/examples/05.UserInterface/UserInterface.cbp index 8c998310..2aeb3ff4 100644 --- a/examples/05.UserInterface/UserInterface.cbp +++ b/examples/05.UserInterface/UserInterface.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/06.2DGraphics/2DGraphics.cbp b/examples/06.2DGraphics/2DGraphics.cbp index 336b89ca..6bc997cf 100644 --- a/examples/06.2DGraphics/2DGraphics.cbp +++ b/examples/06.2DGraphics/2DGraphics.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/07.Collision/Collision.cbp b/examples/07.Collision/Collision.cbp index ac70e400..4095d006 100644 --- a/examples/07.Collision/Collision.cbp +++ b/examples/07.Collision/Collision.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/07.Collision/main.cpp b/examples/07.Collision/main.cpp index e13a8e84..8387c499 100644 --- a/examples/07.Collision/main.cpp +++ b/examples/07.Collision/main.cpp @@ -53,7 +53,7 @@ int main() video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager(); - device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3"); + device->getFileSystem()->addFileArchive("../../media/map-20kdm2.pk3"); scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp"); scene::IMeshSceneNode* q3node = 0; diff --git a/examples/08.SpecialFX/SpecialFX.cbp b/examples/08.SpecialFX/SpecialFX.cbp index 89c0157d..49d040fc 100644 --- a/examples/08.SpecialFX/SpecialFX.cbp +++ b/examples/08.SpecialFX/SpecialFX.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/09.Meshviewer/Meshviewer.cbp b/examples/09.Meshviewer/Meshviewer.cbp index 9587130a..4d3bfbab 100644 --- a/examples/09.Meshviewer/Meshviewer.cbp +++ b/examples/09.Meshviewer/Meshviewer.cbp @@ -38,7 +38,7 @@ - + diff --git a/examples/09.Meshviewer/main.cpp b/examples/09.Meshviewer/main.cpp index 9578484a..74eaf977 100644 --- a/examples/09.Meshviewer/main.cpp +++ b/examples/09.Meshviewer/main.cpp @@ -312,6 +312,7 @@ void createToolBox() env->addStaticText(L":", core::rect(10,240,150,265), true, false, t1); env->addStaticText(L"Framerate:", core::rect(12,240,75,265), false, false, t1); + // current frame info env->addStaticText(L"", core::rect(75,240,200,265), false, false, t1, GUI_ID_ANIMATION_INFO); scrollbar = env->addScrollBar(true, @@ -703,7 +704,7 @@ int main(int argc, char* argv[]) video::SColorf(1.0f,1.0f,1.0f),2000); smgr->setAmbientLight(video::SColorf(0.3f,0.3f,0.3f)); // add our media directory as "search path" - Device->getFileSystem()->addFolderFileArchive("../../media/"); + Device->getFileSystem()->addFileArchive("../../media/"); /* The next step is to read the configuration file. It is stored in the xml diff --git a/examples/10.Shaders/Shaders.cbp b/examples/10.Shaders/Shaders.cbp index e8da8d87..ebbd3825 100644 --- a/examples/10.Shaders/Shaders.cbp +++ b/examples/10.Shaders/Shaders.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/11.PerPixelLighting/PerPixelLighting.cbp b/examples/11.PerPixelLighting/PerPixelLighting.cbp index ca1338a2..8f9ce7f0 100644 --- a/examples/11.PerPixelLighting/PerPixelLighting.cbp +++ b/examples/11.PerPixelLighting/PerPixelLighting.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/11.PerPixelLighting/main.cpp b/examples/11.PerPixelLighting/main.cpp index 5b8a0f9c..b308aa60 100644 --- a/examples/11.PerPixelLighting/main.cpp +++ b/examples/11.PerPixelLighting/main.cpp @@ -328,7 +328,7 @@ int main() // scale the mesh by factor 50 core::matrix4 m; m.setScale ( core::vector3df(50,50,50) ); - manipulator->transformMesh( tangentSphereMesh, m ); + manipulator->transform( tangentSphereMesh, m ); earth = smgr->addMeshSceneNode(tangentSphereMesh); @@ -427,7 +427,7 @@ int main() core::aabbox3d(-3,0,-3,3,1,3), core::vector3df(0.0f,0.03f,0.0f), 80,100, - video::SColor(0,255,255,255), video::SColor(0,255,255,255), + video::SColor(10,255,255,255), video::SColor(10,255,255,255), 400,1100); em->setMinStartSize(core::dimension2d(30.0f, 40.0f)); em->setMaxStartSize(core::dimension2d(30.0f, 40.0f)); @@ -444,7 +444,7 @@ int main() ps->setMaterialFlag(video::EMF_LIGHTING, false); ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false); ps->setMaterialTexture(0, driver->getTexture("../../media/fireball.bmp")); - ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA); + ps->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); MyEventReceiver receiver(room, earth, env, driver); device->setEventReceiver(&receiver); diff --git a/examples/12.TerrainRendering/TerrainRendering.cbp b/examples/12.TerrainRendering/TerrainRendering.cbp index 823f67ef..761948f8 100644 --- a/examples/12.TerrainRendering/TerrainRendering.cbp +++ b/examples/12.TerrainRendering/TerrainRendering.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/12.TerrainRendering/main.cpp b/examples/12.TerrainRendering/main.cpp index a23a2d0f..b049e084 100644 --- a/examples/12.TerrainRendering/main.cpp +++ b/examples/12.TerrainRendering/main.cpp @@ -29,10 +29,10 @@ class MyEventReceiver : public IEventReceiver public: MyEventReceiver(scene::ISceneNode* terrain, scene::ISceneNode* skybox, scene::ISceneNode* skydome) : - Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true) + Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true), showDebug(false) { - Skybox->setVisible(true); - Skydome->setVisible(false); + Skybox->setVisible(showBox); + Skydome->setVisible(!showBox); } bool OnEvent(const SEvent& event) @@ -62,6 +62,10 @@ public: Skybox->setVisible(showBox); Skydome->setVisible(!showBox); return true; + case irr::KEY_KEY_X: // toggle debug information + showDebug=!showDebug; + Terrain->setDebugDataVisible(showDebug?scene::EDS_BBOX_ALL:scene::EDS_OFF); + return true; default: break; } @@ -75,6 +79,7 @@ private: scene::ISceneNode* Skybox; scene::ISceneNode* Skydome; bool showBox; + bool showDebug; }; @@ -177,7 +182,6 @@ int main() terrain->setMaterialType(video::EMT_DETAIL_MAP); terrain->scaleTexture(1.0f, 20.0f); - //terrain->setDebugDataVisible ( true ); /* To be able to do collision with the terrain, we create a triangle selector. diff --git a/examples/13.RenderToTexture/RenderToTexture.cbp b/examples/13.RenderToTexture/RenderToTexture.cbp index 84032711..1b1fb318 100644 --- a/examples/13.RenderToTexture/RenderToTexture.cbp +++ b/examples/13.RenderToTexture/RenderToTexture.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/15.LoadIrrFile/LoadIrrFile.cbp b/examples/15.LoadIrrFile/LoadIrrFile.cbp index c5156dd2..b26bad6d 100644 --- a/examples/15.LoadIrrFile/LoadIrrFile.cbp +++ b/examples/15.LoadIrrFile/LoadIrrFile.cbp @@ -40,7 +40,7 @@ - + diff --git a/examples/16.Quake3MapShader/Quake3MapShader.cbp b/examples/16.Quake3MapShader/Quake3MapShader.cbp index 20c7b068..62a6ce80 100644 --- a/examples/16.Quake3MapShader/Quake3MapShader.cbp +++ b/examples/16.Quake3MapShader/Quake3MapShader.cbp @@ -38,7 +38,7 @@ - + diff --git a/examples/16.Quake3MapShader/main.cpp b/examples/16.Quake3MapShader/main.cpp index aa1993ec..2581896b 100644 --- a/examples/16.Quake3MapShader/main.cpp +++ b/examples/16.Quake3MapShader/main.cpp @@ -31,7 +31,7 @@ to ask the user for a driver type using the console. #endif #ifdef IRRLICHT_QUAKE3_ARENA - #define QUAKE3_STORAGE_FORMAT addZipFileArchive + #define QUAKE3_STORAGE_FORMAT addFileArchive #define QUAKE3_STORAGE_1 "../../media/map-20kdm2.pk3" #define QUAKE3_MAP_NAME "maps/20kdm2.bsp" #endif @@ -145,7 +145,7 @@ int IRRCALLCONV main(int argc, char* argv[]) gui::IGUIEnvironment* gui = device->getGUIEnvironment(); //! add our private media directory to the file system - device->getFileSystem()->addFolderFileArchive("../../media/"); + device->getFileSystem()->addFileArchive("../../media/"); /* To display the Quake 3 map, we first need to load it. Quake 3 maps diff --git a/examples/18.SplitScreen/SplitScreen.cbp b/examples/18.SplitScreen/SplitScreen.cbp index b84d8de0..f26ba77a 100644 --- a/examples/18.SplitScreen/SplitScreen.cbp +++ b/examples/18.SplitScreen/SplitScreen.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/18.SplitScreen/main.cpp b/examples/18.SplitScreen/main.cpp index c1b59e7f..7f649737 100644 --- a/examples/18.SplitScreen/main.cpp +++ b/examples/18.SplitScreen/main.cpp @@ -109,7 +109,7 @@ int main() } //Load map - device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3"); + device->getFileSystem()->addFileArchive("../../media/map-20kdm2.pk3"); IAnimatedMesh *map = smgr->getMesh("20kdm2.bsp"); if (map) { diff --git a/examples/19.MouseAndJoystick/MouseAndJoystick.cbp b/examples/19.MouseAndJoystick/MouseAndJoystick.cbp index ef9742d3..2e170747 100644 --- a/examples/19.MouseAndJoystick/MouseAndJoystick.cbp +++ b/examples/19.MouseAndJoystick/MouseAndJoystick.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/20.ManagedLights/ManagedLights.cbp b/examples/20.ManagedLights/ManagedLights.cbp index 8b8083b8..f0b20d32 100644 --- a/examples/20.ManagedLights/ManagedLights.cbp +++ b/examples/20.ManagedLights/ManagedLights.cbp @@ -40,7 +40,7 @@ - + diff --git a/examples/20.ManagedLights/main.cpp b/examples/20.ManagedLights/main.cpp index 1421b5ec..ee664c1c 100644 --- a/examples/20.ManagedLights/main.cpp +++ b/examples/20.ManagedLights/main.cpp @@ -63,19 +63,17 @@ class CMyLightManager : public scene::ILightManager, public IEventReceiver // These data represent the state information that this light manager // is interested in. scene::ISceneManager * SceneManager; - core::array * SceneLightList; + core::array * SceneLightList; scene::E_SCENE_NODE_RENDER_PASS CurrentRenderPass; scene::ISceneNode * CurrentSceneNode; public: CMyLightManager(scene::ISceneManager* sceneManager) : Mode(NO_MANAGEMENT), RequestedMode(NO_MANAGEMENT), - SceneManager(sceneManager), SceneLightList(0), + SceneManager(sceneManager), SceneLightList(0), CurrentRenderPass(scene::ESNRP_NONE), CurrentSceneNode(0) { } - virtual ~CMyLightManager(void) { } - // The input receiver interface, which just switches light management strategy bool OnEvent(const SEvent & event) { @@ -111,7 +109,7 @@ public: // This is called before the first scene node is rendered. - virtual void OnPreRender(core::array & lightList) + virtual void OnPreRender(core::array & lightList) { // Update the mode; changing it here ensures that it's consistent throughout a render Mode = RequestedMode; @@ -127,7 +125,7 @@ public: // lights on to ensure that they are in a consistent state. You wouldn't normally have // to do this when using a light manager, since you'd continue to do light management // yourself. - for(u32 i = 0; i < SceneLightList->size(); i++) + for (u32 i = 0; i < SceneLightList->size(); i++) (*SceneLightList)[i]->setVisible(true); } @@ -140,9 +138,9 @@ public: virtual void OnRenderPassPostRender(scene::E_SCENE_NODE_RENDER_PASS renderPass) { // I only want solid nodes to be lit, so after the solid pass, turn all lights off. - if(scene::ESNRP_SOLID == renderPass) + if (scene::ESNRP_SOLID == renderPass) { - for(u32 i = 0; i < SceneLightList->size(); ++i) + for (u32 i = 0; i < SceneLightList->size(); ++i) (*SceneLightList)[i]->setVisible(false); } } @@ -178,8 +176,8 @@ public: u32 i; for(i = 0; i < SceneLightList->size(); ++i) { - scene::ILightSceneNode* lightNode = (*SceneLightList)[i]; - f64 distance = lightNode->getAbsolutePosition().getDistanceFromSQ(nodePosition); + scene::ISceneNode* lightNode = (*SceneLightList)[i]; + const f64 distance = lightNode->getAbsolutePosition().getDistanceFromSQ(nodePosition); sortingArray.push_back(LightDistanceElement(lightNode, distance)); } @@ -189,7 +187,6 @@ public: // Turn on the three nearest lights, and turn the others off. for(i = 0; i < sortingArray.size(); ++i) sortingArray[i].node->setVisible(i < 3); - } else if(LIGHTS_IN_ZONE == Mode) { @@ -200,7 +197,9 @@ public: // knowledge of how this particular scene graph is organised. for (u32 i = 0; i < SceneLightList->size(); ++i) { - scene::ILightSceneNode* lightNode = (*SceneLightList)[i]; + if ((*SceneLightList)[i]->getType() != scene::ESNT_LIGHT) + continue; + scene::ILightSceneNode* lightNode = static_cast((*SceneLightList)[i]); video::SLight & lightData = lightNode->getLightData(); if (video::ELT_DIRECTIONAL != lightData.Type) @@ -224,10 +223,10 @@ private: // Find the empty scene node that is the parent of the specified node scene::ISceneNode * findZone(scene::ISceneNode * node) { - if(!node) + if (!node) return 0; - if(node->getType() == scene::ESNT_EMPTY) + if (node->getType() == scene::ESNT_EMPTY) return node; return findZone(node->getParent()); @@ -239,11 +238,10 @@ private: { core::list const & children = node->getChildren(); for (core::list::ConstIterator child = children.begin(); - child != children.end(); - ++child) + child != children.end(); ++child) { - if((*child)->getType() == scene::ESNT_LIGHT) - static_cast(*child)->setVisible(true); + if ((*child)->getType() == scene::ESNT_LIGHT) + (*child)->setVisible(true); else // Assume that lights don't have any children that are also lights turnOnZoneLights(*child); } @@ -256,10 +254,10 @@ private: public: LightDistanceElement() {}; - LightDistanceElement(scene::ILightSceneNode* n, f64 d) + LightDistanceElement(scene::ISceneNode* n, f64 d) : node(n), distance(d) { } - scene::ILightSceneNode* node; + scene::ISceneNode* node; f64 distance; // Lower distance elements are sorted to the start of the array diff --git a/examples/21.Quake3Explorer/Quake3Explorer.cbp b/examples/21.Quake3Explorer/Quake3Explorer.cbp index 34ef5960..df690297 100644 --- a/examples/21.Quake3Explorer/Quake3Explorer.cbp +++ b/examples/21.Quake3Explorer/Quake3Explorer.cbp @@ -40,7 +40,7 @@ - + diff --git a/examples/21.Quake3Explorer/main.cpp b/examples/21.Quake3Explorer/main.cpp index 99912a94..2f5eaf62 100644 --- a/examples/21.Quake3Explorer/main.cpp +++ b/examples/21.Quake3Explorer/main.cpp @@ -1888,7 +1888,7 @@ void CQuake3EventHandler::createParticleImpacts( u32 now ) pas->setMaterialFlag(video::EMF_LIGHTING, false); pas->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false); - pas->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA ); + pas->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR ); pas->setMaterialTexture(0, Game->Device->getVideoDriver()->getTexture( smoke[g].texture )); } diff --git a/examples/22.MaterialViewer/MaterialViewer.cbp b/examples/22.MaterialViewer/MaterialViewer.cbp index 13deb9bd..ef4a5454 100644 --- a/examples/22.MaterialViewer/MaterialViewer.cbp +++ b/examples/22.MaterialViewer/MaterialViewer.cbp @@ -39,7 +39,7 @@ - + diff --git a/examples/22.MaterialViewer/MaterialViewer.vcproj b/examples/22.MaterialViewer/MaterialViewer.vcproj index 8373d8d2..7419359f 100644 --- a/examples/22.MaterialViewer/MaterialViewer.vcproj +++ b/examples/22.MaterialViewer/MaterialViewer.vcproj @@ -1,7 +1,7 @@ diff --git a/examples/22.MaterialViewer/main.cpp b/examples/22.MaterialViewer/main.cpp index 482557b8..785ef727 100755 --- a/examples/22.MaterialViewer/main.cpp +++ b/examples/22.MaterialViewer/main.cpp @@ -227,41 +227,37 @@ protected: { video::SColor col; - u32 alpha=col.getAlpha(); - if ( EditAlpha ) + if (EditAlpha) { - alpha = (u32)core::strtol10( core::stringc( EditAlpha->getText() ).c_str(), 0); - if ( alpha > 255 ) + u32 alpha = core::strtoul10(core::stringc(EditAlpha->getText()).c_str()); + if (alpha > 255) alpha = 255; + col.setAlpha(alpha); } - col.setAlpha(alpha); - u32 red=col.getRed(); - if ( EditRed ) + if (EditRed) { - red = (u32)core::strtol10( core::stringc( EditRed->getText() ).c_str(), 0); - if ( red > 255 ) + u32 red = core::strtoul10(core::stringc(EditRed->getText()).c_str()); + if (red > 255) red = 255; + col.setRed(red); } - col.setRed(red); - u32 green=col.getGreen(); - if ( EditGreen ) + if (EditGreen) { - green = (u32)core::strtol10( core::stringc( EditGreen->getText() ).c_str(), 0); - if ( green > 255 ) + u32 green = core::strtoul10(core::stringc(EditGreen->getText()).c_str()); + if (green > 255) green = 255; + col.setGreen(green); } - col.setGreen(green); - u32 blue=col.getBlue(); - if ( EditBlue ) + if (EditBlue) { - blue = (u32)core::strtol10( core::stringc( EditBlue->getText() ).c_str(), 0); - if ( blue > 255 ) + u32 blue = core::strtoul10(core::stringc(EditBlue->getText()).c_str()); + if (blue > 255) blue = 255; + col.setBlue(blue); } - col.setBlue(blue); return col; } diff --git a/examples/24.CursorControl/CursorControl.cbp b/examples/24.CursorControl/CursorControl.cbp index 3b754a29..86679e72 100644 --- a/examples/24.CursorControl/CursorControl.cbp +++ b/examples/24.CursorControl/CursorControl.cbp @@ -40,7 +40,7 @@ - + diff --git a/examples/25.XmlHandling/XmlHandling.cbp b/examples/25.XmlHandling/XmlHandling.cbp index eff8e20f..30d759e8 100644 --- a/examples/25.XmlHandling/XmlHandling.cbp +++ b/examples/25.XmlHandling/XmlHandling.cbp @@ -40,7 +40,7 @@ - + diff --git a/examples/25.XmlHandling/XmlHandling_vc10.vcxproj b/examples/25.XmlHandling/XmlHandling_vc10.vcxproj new file mode 100644 index 00000000..4abd25b2 --- /dev/null +++ b/examples/25.XmlHandling/XmlHandling_vc10.vcxproj @@ -0,0 +1,102 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + 25.XmlHandling + {8FDA260E-EF27-4F8C-8720-7AF707DD0D9E} + 25.XmlHandling + Win32Proj + + + + Application + MultiByte + + + Application + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win32-VisualStudio\ + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + true + Speed + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + false + + + Level3 + + + Cdecl + + + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + false + Console + true + true + + + + + + + + + + + diff --git a/examples/25.XmlHandling/main.cpp b/examples/25.XmlHandling/main.cpp index 6a11ac6e..faa06e34 100644 --- a/examples/25.XmlHandling/main.cpp +++ b/examples/25.XmlHandling/main.cpp @@ -24,7 +24,7 @@ using namespace gui; * and manages the options. * * - * The class makes use of irrMap which is a an associative arrays using a red-black tree + * The class makes use of irrMap which is a an associative arrays using a red-black tree * it allows easy mapping of a key to a value, along the way there is some information on how to use it * */ @@ -39,26 +39,24 @@ public: SettingManager(const stringw& settings_file): SettingsFile(settings_file), NullDevice(0) { // Irrlicht null device, we want to load settings before we actually created our device, therefore, nulldevice - NullDevice = irr::createDevice(irr::video::EDT_NULL); - + NullDevice = irr::createDevice(irr::video::EDT_NULL); //DriverOptions is an irrlicht map, //we can insert values in the map in two ways by calling insert(key,value) or by using the [key] operator //the [] operator overrides values if they already exist - DriverOptions.insert(L"Software", EDT_SOFTWARE ); - DriverOptions.insert(L"OpenGL", EDT_OPENGL ); - DriverOptions.insert(L"Direct3D9", EDT_DIRECT3D9 ); + DriverOptions.insert(L"Software", EDT_SOFTWARE); + DriverOptions.insert(L"OpenGL", EDT_OPENGL); + DriverOptions.insert(L"Direct3D9", EDT_DIRECT3D9); //some resolution options - ResolutionOptions.insert(L"640x480", dimension2du(640,480) ); - ResolutionOptions.insert(L"800x600", dimension2du(800,600) ); - ResolutionOptions.insert(L"1024x768", dimension2du(1024,768) ); + ResolutionOptions.insert(L"640x480", dimension2du(640,480)); + ResolutionOptions.insert(L"800x600", dimension2du(800,600)); + ResolutionOptions.insert(L"1024x768", dimension2du(1024,768)); //our preferred defaults - SettingMap.insert(L"driver", L"Direct3D9"); //0 is software - SettingMap.insert(L"resolution", L"640x480"); //0 is 640x480 - SettingMap.insert(L"fullscreen", L"0"); //0 is false - + SettingMap.insert(L"driver", L"Direct3D9"); + SettingMap.insert(L"resolution", L"640x480"); + SettingMap.insert(L"fullscreen", L"0"); //0 is false } /** @@ -67,7 +65,7 @@ public: */ ~SettingManager() { - if(NullDevice) + if (NullDevice) { NullDevice->closeDevice(); NullDevice->drop(); @@ -91,28 +89,28 @@ public: bool load() { //if not able to create device dont attempt to load - if(!NullDevice) + if (!NullDevice) return false; irr::io::IXMLReader* xml = NullDevice->getFileSystem()->createXMLReader(SettingsFile); //create xml reader - if ( !xml ) + if (!xml) return false; - const stringw settingTag(L"setting"); //we'll be looking for this tag in the xml - stringw currentSection; //keep track of our currentsection - const stringw videoTag(L"video"); //constant for videotag + const stringw settingTag(L"setting"); //we'll be looking for this tag in the xml + stringw currentSection; //keep track of our currentsection + const stringw videoTag(L"video"); //constant for videotag //while there is more to read - while(xml->read()) + while (xml->read()) { //check the node type - switch(xml->getNodeType()) + switch (xml->getNodeType()) { //we found a new element case irr::io::EXN_ELEMENT: { //we currently are in the empty or mygame section and find the video tag so we set our current section to video - if(currentSection.empty() && videoTag.equals_ignore_case(xml->getNodeName())) + if (currentSection.empty() && videoTag.equals_ignore_case(xml->getNodeName())) { currentSection = videoTag; } @@ -120,21 +118,20 @@ public: else if (currentSection.equals_ignore_case(videoTag) && settingTag.equals_ignore_case(xml->getNodeName() )) { //read in the key - stringw key = xml->getAttributeValueSafe(L"name"); + stringw key = xml->getAttributeValueSafe(L"name"); //if there actually is a key to set - if( !key.empty()) + if (!key.empty()) { //set the setting in the map to the value, //the [] operator overrides values if they already exist or inserts a new key value //pair into the settings map if it was not defined yet - SettingMap[ key ] = xml->getAttributeValueSafe(L"value"); + SettingMap[key] = xml->getAttributeValueSafe(L"value"); } } //.. // You can add your own sections and tags to read in here //.. - } break; @@ -160,12 +157,12 @@ public: { //if not able to create device don't attempt to save - if(!NullDevice) + if (!NullDevice) return false; //create xml writer irr::io::IXMLWriter* xwriter = NullDevice->getFileSystem()->createXMLWriter( SettingsFile ); - if(!xwriter) + if (!xwriter) return false; //write out the obligatory xml header. Each xml-file needs to have exactly one of those. @@ -227,8 +224,8 @@ public: /** * Get setting as string - * @param key name of setting - * @return empty string if the settings is not found, else value of the setting + * @param key Name of setting + * @return Empty string if the settings is not found, else value of the setting */ stringw getSetting(const stringw& key) const { @@ -240,18 +237,17 @@ public: return n->getValue(); else return L""; - } /** * Get setting as bool - * @param key name of setting - * @return false if the key cannot be found, else true if the setting == 1 + * @param key Name of setting + * @return False if the key cannot be found, else true if the setting == 1 */ bool getSettingAsBoolean(const stringw& key ) const { stringw s = getSetting(key); - if(s.empty()) + if (s.empty()) return false; return s.equals_ignore_case(L"1"); } @@ -259,33 +255,29 @@ public: /** * Get setting as integer NOTE: function is not used in example but provided for completeness * @param key name of setting - * @return 0 if the key cannot be found, else the setting converted to an integer + * @return 0 if the key cannot be found, else the setting converted to an integer */ - s32 getSettingAsInteger(const stringw& key ) const + s32 getSettingAsInteger(const stringw& key) const { //we implicitly cast to string instead of stringw because strtol10 does not accept wide strings - stringc s = getSetting(key); - if(s.empty()) + const stringc s = getSetting(key); + if (s.empty()) return 0; return strtol10(s.c_str()); - - } public: - map DriverOptions; //available options for driver config - map ResolutionOptions; //available options for resolution config + map DriverOptions; //available options for driver config + map ResolutionOptions; //available options for resolution config private: - - SettingManager(const SettingManager& other); // defined but not implemented + SettingManager(const SettingManager& other); // defined but not implemented SettingManager& operator=(const SettingManager& other); // defined but not implemented - map SettingMap; //current config - - stringw SettingsFile; // location of the xml, usually the - irr::IrrlichtDevice* NullDevice; + map SettingMap; //current config + stringw SettingsFile; // location of the xml, usually the + irr::IrrlichtDevice* NullDevice; }; /** @@ -295,35 +287,35 @@ struct SAppContext { SAppContext() : Device(0),Gui(0), Driver(0), Settings(0), ShouldQuit(false), - ButtonSave(0), ButtonExit(0), ListboxDriver(0), + ButtonSave(0), ButtonExit(0), ListboxDriver(0), ListboxResolution(0), CheckboxFullscreen(0) { } ~SAppContext() { - if(Settings) + if (Settings) delete Settings; - if(Device) + if (Device) { Device->closeDevice(); Device->drop(); } } - IrrlichtDevice * Device; - IGUIEnvironment* Gui; - IVideoDriver* Driver; - SettingManager* Settings; - bool ShouldQuit; + IrrlichtDevice* Device; + IGUIEnvironment* Gui; + IVideoDriver* Driver; + SettingManager* Settings; + bool ShouldQuit; //settings dialog - IGUIButton* ButtonSave; - IGUIButton* ButtonExit; - IGUIListBox* ListboxDriver; - IGUIListBox* ListboxResolution; - IGUICheckBox* CheckboxFullscreen; + IGUIButton* ButtonSave; + IGUIButton* ButtonExit; + IGUIListBox* ListboxDriver; + IGUIListBox* ListboxResolution; + IGUICheckBox* CheckboxFullscreen; }; /* @@ -347,23 +339,23 @@ public: if ( event.GUIEvent.Caller == App.ButtonSave ) { //if there is a selection write it - if( App.ListboxDriver->getSelected() != -1) + if ( App.ListboxDriver->getSelected() != -1) App.Settings->setSetting(L"driver", App.ListboxDriver->getListItem(App.ListboxDriver->getSelected())); //if there is a selection write it - if( App.ListboxResolution->getSelected() != -1) + if ( App.ListboxResolution->getSelected() != -1) App.Settings->setSetting(L"resolution", App.ListboxResolution->getListItem(App.ListboxResolution->getSelected())); App.Settings->setSetting(L"fullscreen", App.CheckboxFullscreen->isChecked()); - if(App.Settings->save()) + if (App.Settings->save()) { App.Gui->addMessageBox(L"settings save",L"settings saved, please restart for settings to change effect","",true); } } // cancel/exit button clicked, tell the application to exit - else if( event.GUIEvent.Caller == App.ButtonExit) + else if ( event.GUIEvent.Caller == App.ButtonExit) { App.ShouldQuit = true; } @@ -412,7 +404,7 @@ void createSettingsDialog(SAppContext& app) // add listbox for resolution choice app.Gui->addStaticText (L"Resolution", rect< s32 >(10,130, 200, 140), false, true, windowSettings); - app.ListboxResolution = app.Gui->addListBox(rect(10,140,220,200), windowSettings, 1,true); + app.ListboxResolution = app.Gui->addListBox(rect(10,140,220,200), windowSettings, 1,true); //add all available options tothe resolution listbox map::Iterator ri = app.Settings->ResolutionOptions.getIterator(); @@ -423,16 +415,20 @@ void createSettingsDialog(SAppContext& app) app.ListboxResolution->setSelected(app.Settings->getSetting("resolution").c_str()); //add checkbox to toggle fullscreen, initially set to loaded setting - app.CheckboxFullscreen = app.Gui->addCheckBox(app.Settings->getSettingAsBoolean("fullscreen"), - rect(10,220,220,240), - windowSettings, -1, - L"Fullscreen"); + app.CheckboxFullscreen = app.Gui->addCheckBox( + app.Settings->getSettingAsBoolean("fullscreen"), + rect(10,220,220,240), windowSettings, -1, + L"Fullscreen"); //last but not least add save button - app.ButtonSave = app.Gui->addButton(rect(80,250,150,270), windowSettings, 2, L"Save video settings"); + app.ButtonSave = app.Gui->addButton( + rect(80,250,150,270), windowSettings, 2, + L"Save video settings"); //exit/cancel button - app.ButtonExit = app.Gui->addButton(rect(160,250,240,270), windowSettings, 2, L"Cancel and exit"); + app.ButtonExit = app.Gui->addButton( + rect(160,250,240,270), windowSettings, 2, + L"Cancel and exit"); } int main() @@ -446,12 +442,12 @@ int main() param.WindowSize.set(640,480); /** - * Try to load config. + * Try to load config. * I leave it as an exercise of the reader to store the configuration in the local application data folder, * the only logical place to store config data for games. For all other operating systems I redirect to your manuals */ app.Settings = new SettingManager("../../media/settings.xml"); - if( !app.Settings->load() ) + if ( !app.Settings->load() ) { // ... // Here add your own exception handling, for now we continue because there are defaults set in SettingManager constructor @@ -467,9 +463,9 @@ int main() //see DriverOptions in the settingmanager class for details map::Node* driver = app.Settings->DriverOptions.find( app.Settings->getSetting("driver") ); - if(driver) + if (driver) { - if( irr::IrrlichtDevice::isDriverSupported( static_cast( driver->getValue() ))) + if ( irr::IrrlichtDevice::isDriverSupported( static_cast( driver->getValue() ))) { // selected driver is supported, so we use it. param.DriverType = static_cast( driver->getValue()); @@ -478,7 +474,7 @@ int main() //map resolution setting to dimension in a similar way as demonstrated above map::Node* res = app.Settings->ResolutionOptions.find( app.Settings->getSetting("resolution") ); - if(res) + if (res) { param.WindowSize = res->getValue(); } @@ -506,9 +502,9 @@ int main() app.Device->setEventReceiver(&receiver); //enter main loop - while(!app.ShouldQuit && app.Device->run()) + while (!app.ShouldQuit && app.Device->run()) { - if(app.Device->isWindowActive()) + if (app.Device->isWindowActive()) { app.Driver->beginScene(true, true, SColor(0,200,200,200)); app.Gui->drawAll(); @@ -521,3 +517,4 @@ int main() return 0; } + diff --git a/examples/26.OcclusionQuery/Makefile b/examples/26.OcclusionQuery/Makefile new file mode 100644 index 00000000..87e37e40 --- /dev/null +++ b/examples/26.OcclusionQuery/Makefile @@ -0,0 +1,39 @@ +# Makefile for Irrlicht Examples +# It's usually sufficient to change just the target name and source file list +# and be sure that CXX is set to a valid compiler +Target = 26.OcclusionQuery +Sources = main.cpp + +# general compiler settings +CPPFLAGS = -I../../include -I/usr/X11R6/include +CXXFLAGS = -O3 -ffast-math +#CXXFLAGS = -g -Wall + +#default target is Linux +all: all_linux + +ifeq ($(HOSTTYPE), x86_64) +LIBSELECT=64 +endif + +# target specific settings +all_linux: LDFLAGS = -L/usr/X11R6/lib$(LIBSELECT) -L../../lib/Linux -lIrrlicht -lGL -lXxf86vm -lXext -lX11 -lXcursor +all_linux clean_linux: SYSTEM=Linux +all_win32: LDFLAGS = -L../../lib/Win32-gcc -lIrrlicht -lopengl32 -lm +all_win32: CPPFLAGS += -D__GNUWIN32__ -D_WIN32 -DWIN32 -D_WINDOWS -D_MBCS -D_USRDLL +all_win32 clean_win32: SYSTEM=Win32-gcc +all_win32 clean_win32: SUF=.exe +# name of the binary - only valid for targets which set SYSTEM +DESTPATH = ../../bin/$(SYSTEM)/$(Target)$(SUF) + +all_linux all_win32: + $(warning Building...) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(Sources) -o $(DESTPATH) $(LDFLAGS) + +clean: clean_linux clean_win32 + $(warning Cleaning...) + +clean_linux clean_win32: + @$(RM) $(DESTPATH) + +.PHONY: all all_win32 clean clean_linux clean_win32 diff --git a/examples/26.OcclusionQuery/OcclusionQuery.cbp b/examples/26.OcclusionQuery/OcclusionQuery.cbp new file mode 100644 index 00000000..d4107d19 --- /dev/null +++ b/examples/26.OcclusionQuery/OcclusionQuery.cbp @@ -0,0 +1,59 @@ + + + + + + diff --git a/examples/26.OcclusionQuery/OcclusionQuery.dev b/examples/26.OcclusionQuery/OcclusionQuery.dev new file mode 100644 index 00000000..e9d7c04b --- /dev/null +++ b/examples/26.OcclusionQuery/OcclusionQuery.dev @@ -0,0 +1,59 @@ +[Project] +FileName=example.dev +Name=Irrlicht Example 26 OcclusionQuery +UnitCount=1 +Type=1 +Ver=1 +ObjFiles= +Includes=..\..\include +Libs= +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler= +Linker=../../lib/Win32-gcc/libIrrlicht.a_@@_ +IsCpp=1 +Icon= +ExeOutput=../../bin/Win32-gcc +ObjectOutput=obj +OverrideOutput=1 +OverrideOutputName=26.OcclusionQuery.exe +HostApplication= +Folders= +CommandLine= +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=0000000000000000000000 +UseCustomMakefile=0 +CustomMakefile= + +[Unit1] +FileName=main.cpp +CompileCpp=1 +Folder=Projekt1 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[VersionInfo] +Major=0 +Minor=1 +Release=1 +Build=1 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Irrlicht Engine example compiled using DevCpp and gcc +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 + diff --git a/examples/26.OcclusionQuery/OcclusionQuery.vcproj b/examples/26.OcclusionQuery/OcclusionQuery.vcproj new file mode 100644 index 00000000..602f2d3d --- /dev/null +++ b/examples/26.OcclusionQuery/OcclusionQuery.vcproj @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/26.OcclusionQuery/OcclusionQuery_vc10.vcxproj b/examples/26.OcclusionQuery/OcclusionQuery_vc10.vcxproj new file mode 100644 index 00000000..0ec112cc --- /dev/null +++ b/examples/26.OcclusionQuery/OcclusionQuery_vc10.vcxproj @@ -0,0 +1,128 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + 26.OcclusionQuery + {5CE0E2E7-879D-4152-B61D-24E7D0707B45} + OcclusionQuery + + + + Application + false + MultiByte + + + Application + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\bin\Win32-VisualStudio\ + + ..\..\bin\Win32-VisualStudio\ + + AllRules.ruleset + + + AllRules.ruleset + + + + + + .\Debug\OcclusionQuery.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\26.OcclusionQuery.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Release\OcclusionQuery.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\26.OcclusionQuery.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + Disabled + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + EnableFastChecks + MaxSpeed + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + \ No newline at end of file diff --git a/examples/26.OcclusionQuery/OcclusionQuery_vc8.vcproj b/examples/26.OcclusionQuery/OcclusionQuery_vc8.vcproj new file mode 100644 index 00000000..faa137fc --- /dev/null +++ b/examples/26.OcclusionQuery/OcclusionQuery_vc8.vcproj @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/26.OcclusionQuery/OcclusionQuery_vc9.vcproj b/examples/26.OcclusionQuery/OcclusionQuery_vc9.vcproj new file mode 100644 index 00000000..9697b571 --- /dev/null +++ b/examples/26.OcclusionQuery/OcclusionQuery_vc9.vcproj @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/26.OcclusionQuery/main.cpp b/examples/26.OcclusionQuery/main.cpp new file mode 100644 index 00000000..5dd13c2a --- /dev/null +++ b/examples/26.OcclusionQuery/main.cpp @@ -0,0 +1,213 @@ +/** Example 026 OcclusionQuery + +This Tutorial shows how to speed up rendering by use of the +OcclusionQuery feature. The usual rendering tries to avoid rendering of +scene nodes by culling those nodes which are outside the visible area, the +view frustum. However, this technique does not cope with occluded objects +which are still in the line of sight, but occluded by some larger object +between the object and the eye (camera). Occlusion queries check exactly that. +The queries basically measure the number of pixels that a previous render +left on the screen. +Since those pixels cannot be recognized at the end of a rendering anymore, +the pixel count is measured directly when rendering. Thus, one needs to render +the occluder (the object in front) first. This object needs to write to the +z-buffer in order to become a real occluder. Then the node is rendered and in +case a z-pass happens, i.e. the pixel is written to the framebuffer, the pixel +is counted in the query. +The result of a query is the number of pixels which got through. One can, based +on this number, judge if the scene node is visible enough to be rendered, or if +the node should be removed in the next round. Also note that the number of +pixels is a safe over approximation in general. The pixels might be overdrawn +later on, and the GPU tries to avoid inaccuracies which could lead to false +negatives in the queries. + +As you might have recognized already, we had to render the node to get the +numbers. So where's the benefit, you might say. There are several ways where +occlusion queries can help. It is often a good idea to just render the bbox +of the node instead of the actual mesh. This is really fast and is a safe over +approximation. If you need a more exact render with the actual geometry, it's +a good idea to render with just basic solid material. Avoid complex shaders +and state changes through textures. There's no need while just doing the +occlusion query. At least if the render is not used for the actual scene. This +is the third way to optimize occlusion queries. Just check the queries every +5th or 10th frane, or even less frequent. This depends on the movement speed +of the objects and camera. +*/ + +#ifdef _MSC_VER +// We'll also define this to stop MSVC complaining about sprintf(). +#define _CRT_SECURE_NO_WARNINGS +#pragma comment(lib, "Irrlicht.lib") +#endif + +#include +#include "driverChoice.h" + +using namespace irr; + +/* +We need keyboard input events to switch some parameters +*/ +class MyEventReceiver : public IEventReceiver +{ +public: + // This is the one method that we have to implement + virtual bool OnEvent(const SEvent& event) + { + // Remember whether each key is down or up + if (event.EventType == irr::EET_KEY_INPUT_EVENT) + KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown; + + return false; + } + + // This is used to check whether a key is being held down + virtual bool IsKeyDown(EKEY_CODE keyCode) const + { + return KeyIsDown[keyCode]; + } + + MyEventReceiver() + { + for (u32 i=0; i(640, 480), 16, false, false, false, &receiver); + + if (device == 0) + return 1; // could not create selected driver. + + video::IVideoDriver* driver = device->getVideoDriver(); + scene::ISceneManager* smgr = device->getSceneManager(); + + smgr->getGUIEnvironment()->addStaticText(L"Press Space to hide occluder.", core::recti(10,10, 200,50)); + + /* + Create the node to be occluded. We create a sphere node with high poly count. + */ + scene::ISceneNode * node = smgr->addSphereSceneNode(10, 64); + if (node) + { + node->setPosition(core::vector3df(0,0,60)); + node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp")); + node->setMaterialFlag(video::EMF_LIGHTING, false); + } + + /* + Now we create another node, the occluder. It's a simple plane. + */ + scene::ISceneNode* plane = smgr->addMeshSceneNode(smgr->addHillPlaneMesh( + "plane", core::dimension2df(10,10), core::dimension2du(2,2)), 0, -1, + core::vector3df(0,0,20), core::vector3df(270,0,0)); + + if (plane) + { + plane->setMaterialTexture(0, driver->getTexture("../../media/t351sml.jpg")); + plane->setMaterialFlag(video::EMF_LIGHTING, false); + plane->setMaterialFlag(video::EMF_BACK_FACE_CULLING, true); + } + + /* + Here we create the occlusion query. Because we don't have a plain mesh scene node + (ESNT_MESH or ESNT_ANIMATED_MESH), we pass the base geometry as well. Instead, + we could also pass a simpler mesh or the bounding box. But we will use a time + based method, where the occlusion query renders to the frame buffer and in case + of success (occlusion), the mesh is not drawn for several frames. + */ + driver->addOcclusionQuery(node, ((scene::IMeshSceneNode*)node)->getMesh()); + + /* + We have done everything, just a camera and draw it. We also write the + current frames per second and the name of the driver to the caption of the + window to examine the render speedup. + We also store the time for measuring the time since the last occlusion query ran + and store whether the node should be visible in the next frames. + */ + smgr->addCameraSceneNode(); + int lastFPS = -1; + u32 timeNow = device->getTimer()->getTime(); + bool nodeVisible=true; + + while(device->run()) + { + plane->setVisible(!receiver.IsKeyDown(irr::KEY_SPACE)); + + driver->beginScene(true, true, video::SColor(255,113,113,133)); + /* + First, we draw the scene, possibly without the occluded element. This is necessary + because we need the occluder to be drawn first. You can also use several scene + managers to collect a number of possible occluders in a separately rendered + scene. + */ + node->setVisible(nodeVisible); + smgr->drawAll(); + smgr->getGUIEnvironment()->drawAll(); + + /* + Once in a while, here every 100 ms, we check the visibility. We run the queries, + update the pixel value, and query the result. Since we already rendered the node + we render the query invisible. The update is made blocking, as we need the result + immediately. If you don't need the result immediately, e.g. because oyu have other + things to render, you can call the update non-blocking. This gives the GPU more + time to pass back the results without flushing the render pipeline. + If the update was called non-blocking, the result from getOcclusionQueryResult is + either the previous value, or 0xffffffff if no value has been generated at all, yet. + The result is taken immediately as visibility flag for the node. + */ + if (device->getTimer()->getTime()-timeNow>100) + { + driver->runAllOcclusionQueries(false); + driver->updateAllOcclusionQueries(); + nodeVisible=driver->getOcclusionQueryResult(node)>0; + timeNow=device->getTimer()->getTime(); + } + + driver->endScene(); + + int fps = driver->getFPS(); + + if (lastFPS != fps) + { + core::stringw tmp(L"OcclusionQuery Example ["); + tmp += driver->getName(); + tmp += L"] fps: "; + tmp += fps; + + device->setWindowCaption(tmp.c_str()); + lastFPS = fps; + } + } + + /* + In the end, delete the Irrlicht device. + */ + device->drop(); + + return 0; +} + +/* +That's it. Compile and play around with the program. +**/ diff --git a/examples/BuildAllExamples.workspace b/examples/BuildAllExamples.workspace index 86c4a905..d2cc08cf 100644 --- a/examples/BuildAllExamples.workspace +++ b/examples/BuildAllExamples.workspace @@ -2,7 +2,7 @@ - + @@ -22,9 +22,14 @@ + + + + - + + diff --git a/examples/BuildAllExamples_v7.sln b/examples/BuildAllExamples_v7.sln index 1896e593..d07be5f6 100644 --- a/examples/BuildAllExamples_v7.sln +++ b/examples/BuildAllExamples_v7.sln @@ -111,10 +111,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "23.SMeshHandling", "23.SMes ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "22.MaterialViewer", "22.MaterialViewer\MaterialViewer.vcproj", "{4E6C2F8D-BA92-4C5B-96FD-72D4FE8BD7FA}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GUI Editor", "..\tools\GUIEditor\GUI Editor_v7.vcproj", "{853A396E-C031-4C26-A716-5B4E176BE11D}" ProjectSection(ProjectDependencies) = postProject EndProjectSection @@ -123,6 +119,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Font Tool", "..\tools\IrrFo ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "22.MaterialViewer", "22.MaterialViewer\MaterialViewer.vcproj", "{4E6C2F8D-BA92-4C5B-96FD-72D4FE8BD7FA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug @@ -219,18 +219,18 @@ Global {6AEC2AA2-C9FF-4B7D-B07A-94A9D34B41D7}.Debug.Build.0 = Debug|Win32 {6AEC2AA2-C9FF-4B7D-B07A-94A9D34B41D7}.Release.ActiveCfg = Release|Win32 {6AEC2AA2-C9FF-4B7D-B07A-94A9D34B41D7}.Release.Build.0 = Release|Win32 + {853A396E-C031-4C26-A716-5B4E176BE11D}.Debug.ActiveCfg = Debug|Win32 + {853A396E-C031-4C26-A716-5B4E176BE11D}.Debug.Build.0 = Debug|Win32 + {853A396E-C031-4C26-A716-5B4E176BE11D}.Release.ActiveCfg = Release|Win32 + {853A396E-C031-4C26-A716-5B4E176BE11D}.Release.Build.0 = Release|Win32 + {853A396E-C031-4C26-A716-5B4E176BE11D}.Debug.ActiveCfg = Debug|Win32 + {853A396E-C031-4C26-A716-5B4E176BE11D}.Debug.Build.0 = Debug|Win32 + {853A396E-C031-4C26-A716-5B4E176BE11D}.Release.ActiveCfg = Release|Win32 + {853A396E-C031-4C26-A716-5B4E176BE11D}.Release.Build.0 = Release|Win32 {4E6C2F8D-BA92-4C5B-96FD-72D4FE8BD7FA}.Debug.ActiveCfg = Debug|Win32 {4E6C2F8D-BA92-4C5B-96FD-72D4FE8BD7FA}.Debug.Build.0 = Debug|Win32 {4E6C2F8D-BA92-4C5B-96FD-72D4FE8BD7FA}.Release.ActiveCfg = Release|Win32 {4E6C2F8D-BA92-4C5B-96FD-72D4FE8BD7FA}.Release.Build.0 = Release|Win32 - {853A396E-C031-4C26-A716-5B4E176BE11D}.Debug.ActiveCfg = Debug|Win32 - {853A396E-C031-4C26-A716-5B4E176BE11D}.Debug.Build.0 = Debug|Win32 - {853A396E-C031-4C26-A716-5B4E176BE11D}.Release.ActiveCfg = Release|Win32 - {853A396E-C031-4C26-A716-5B4E176BE11D}.Release.Build.0 = Release|Win32 - {853A396E-C031-4C26-A716-5B4E176BE11D}.Debug.ActiveCfg = Debug|Win32 - {853A396E-C031-4C26-A716-5B4E176BE11D}.Debug.Build.0 = Debug|Win32 - {853A396E-C031-4C26-A716-5B4E176BE11D}.Release.ActiveCfg = Release|Win32 - {853A396E-C031-4C26-A716-5B4E176BE11D}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection diff --git a/examples/BuildAllExamples_vc10.sln b/examples/BuildAllExamples_vc10.sln index d67073e8..b1ed3523 100644 --- a/examples/BuildAllExamples_vc10.sln +++ b/examples/BuildAllExamples_vc10.sln @@ -134,6 +134,16 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FontTool", "..\tools\IrrFon EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MeshConverter", "..\tools\MeshConverter\MeshConverter_vc10.vcxproj", "{E72B637E-4AA6-46F3-885F-AC67B4B470ED}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "26.OcclusionQuery", "26.OcclusionQuery\OcclusionQuery_vc10.vcxproj", "{5CE0E2E7-879D-4152-B61D-24E7D0707B45}" + ProjectSection(ProjectDependencies) = postProject + {E08E042A-6C45-411B-92BE-3CC31331019F} = {E08E042A-6C45-411B-92BE-3CC31331019F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "25.XmlHandling", "25.XmlHandling\XmlHandling_vc10.vcxproj", "{8FDA260E-EF27-4F8C-8720-7AF707DD0D9E}" + ProjectSection(ProjectDependencies) = postProject + {E08E042A-6C45-411B-92BE-3CC31331019F} = {E08E042A-6C45-411B-92BE-3CC31331019F} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -256,6 +266,14 @@ Global {E72B637E-4AA6-46F3-885F-AC67B4B470ED}.Debug|Win32.Build.0 = Debug|Win32 {E72B637E-4AA6-46F3-885F-AC67B4B470ED}.Release|Win32.ActiveCfg = Release|Win32 {E72B637E-4AA6-46F3-885F-AC67B4B470ED}.Release|Win32.Build.0 = Release|Win32 + {5CE0E2E7-879D-4152-B61D-24E7D0707B45}.Debug|Win32.ActiveCfg = Debug|Win32 + {5CE0E2E7-879D-4152-B61D-24E7D0707B45}.Debug|Win32.Build.0 = Debug|Win32 + {5CE0E2E7-879D-4152-B61D-24E7D0707B45}.Release|Win32.ActiveCfg = Release|Win32 + {5CE0E2E7-879D-4152-B61D-24E7D0707B45}.Release|Win32.Build.0 = Release|Win32 + {8FDA260E-EF27-4F8C-8720-7AF707DD0D9E}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FDA260E-EF27-4F8C-8720-7AF707DD0D9E}.Debug|Win32.Build.0 = Debug|Win32 + {8FDA260E-EF27-4F8C-8720-7AF707DD0D9E}.Release|Win32.ActiveCfg = Release|Win32 + {8FDA260E-EF27-4F8C-8720-7AF707DD0D9E}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/Demo/CDemo.cpp b/examples/Demo/CDemo.cpp index 3e726b06..93087b2b 100644 --- a/examples/Demo/CDemo.cpp +++ b/examples/Demo/CDemo.cpp @@ -525,7 +525,7 @@ void CDemo::loadSceneData() scene::IParticleEmitter* em = campFire->createBoxEmitter( core::aabbox3d(-7,0,-7,7,1,7), core::vector3df(0.0f,0.06f,0.0f), - 80,100, video::SColor(0,255,255,255),video::SColor(0,255,255,255), 800,2000); + 80,100, video::SColor(1,255,255,255),video::SColor(1,255,255,255), 800,2000); em->setMinStartSize(core::dimension2d(20.0f, 10.0f)); em->setMaxStartSize(core::dimension2d(20.0f, 10.0f)); @@ -539,7 +539,8 @@ void CDemo::loadSceneData() campFire->setMaterialFlag(video::EMF_LIGHTING, false); campFire->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false); campFire->setMaterialTexture(0, driver->getTexture("../../media/fireball.bmp")); - campFire->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA); + campFire->setMaterialType(video::EMT_ONETEXTURE_BLEND); + campFire->getMaterial(0).MaterialTypeParam=video::pack_texureBlendFunc(video::EBF_ONE, video::EBF_ONE_MINUS_SRC_ALPHA, video::EMFN_MODULATE_1X, video::EAS_VERTEX_COLOR); // load music diff --git a/examples/Demo/CMainMenu.cpp b/examples/Demo/CMainMenu.cpp index 0c700928..1122c9d7 100644 --- a/examples/Demo/CMainMenu.cpp +++ b/examples/Demo/CMainMenu.cpp @@ -25,9 +25,9 @@ bool CMainMenu::run(bool& outFullscreen, bool& outMusic, bool& outShadows, core::dimension2d(512, 384), 16, false, false, false, this); if (MenuDevice->getFileSystem()->existFile("irrlicht.dat")) - MenuDevice->getFileSystem()->addZipFileArchive("irrlicht.dat"); + MenuDevice->getFileSystem()->addFileArchive("irrlicht.dat"); else - MenuDevice->getFileSystem()->addZipFileArchive("../../media/irrlicht.dat"); + MenuDevice->getFileSystem()->addFileArchive("../../media/irrlicht.dat"); video::IVideoDriver* driver = MenuDevice->getVideoDriver(); scene::ISceneManager* smgr = MenuDevice->getSceneManager(); diff --git a/examples/Demo/Demo_vc10.vcxproj b/examples/Demo/Demo_vc10.vcxproj index 8ce4fc77..ebb5591a 100644 --- a/examples/Demo/Demo_vc10.vcxproj +++ b/examples/Demo/Demo_vc10.vcxproj @@ -109,7 +109,6 @@ 0x0c07 - ..\..\bin\Win32-VisualStudio\Demo.exe ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) true Windows @@ -156,4 +155,4 @@ - + \ No newline at end of file diff --git a/examples/Demo/demo.cbp b/examples/Demo/demo.cbp index 91e25df3..dd8487ef 100644 --- a/examples/Demo/demo.cbp +++ b/examples/Demo/demo.cbp @@ -41,7 +41,7 @@ - + diff --git a/include/EDriverFeatures.h b/include/EDriverFeatures.h index bf1b3d07..31216e24 100644 --- a/include/EDriverFeatures.h +++ b/include/EDriverFeatures.h @@ -109,6 +109,12 @@ namespace video //! Supports occlusion queries EVDF_OCCLUSION_QUERY, + //! Supports polygon offset/depth bias for avoiding z-fighting + EVDF_POLYGON_OFFSET, + + //! Support for different blend functions. Without, only ADD is available + EVDF_BLEND_OPERATIONS, + //! Only used for counting the elements of this enum EVDF_COUNT }; diff --git a/include/EMaterialFlags.h b/include/EMaterialFlags.h index 85fedb59..8aecaafd 100644 --- a/include/EMaterialFlags.h +++ b/include/EMaterialFlags.h @@ -78,7 +78,13 @@ namespace video EMF_COLOR_MATERIAL = 0x10000, //! Flag for enabling/disabling mipmap usage - EMF_USE_MIP_MAPS = 0x20000 + EMF_USE_MIP_MAPS = 0x20000, + + //! Flag for blend operation + EMF_BLEND_OPERATION = 0x40000, + + //! Flag for polygon offset + EMF_POLYGON_OFFSET = 0x80000 }; } // end namespace video diff --git a/include/IAnimatedMesh.h b/include/IAnimatedMesh.h index bea77a8e..41de1df4 100644 --- a/include/IAnimatedMesh.h +++ b/include/IAnimatedMesh.h @@ -54,97 +54,6 @@ namespace scene EAMT_SKINNED }; - - //! Possible types of Animation Type - enum E_ANIMATION_TYPE - { - //! No Animation - EAMT_STILL, - //! From Start to End, then Stop ( Limited Line ) - EAMT_WAYPOINT, - //! Linear Cycling Animation ( Sawtooth ) - EAMT_LOOPING, - //! Linear bobbing ( Triangle ) - EAMT_PINGPONG - }; - - //! Names for Animation Type - const c8* const MeshAnimationTypeNames[] = - { - "still", - "waypoint", - "looping", - "pingpong", - 0 - }; - - - //! Data for holding named Animation Info - struct KeyFrameInterpolation - { - core::stringc Name; // Name of the current Animation/Bone - E_ANIMATION_TYPE AnimationType; // Type of Animation ( looping, usw..) - - f32 CurrentFrame; // Current Frame - s32 NextFrame; // Frame which will be used next. For blending - - s32 StartFrame; // Absolute Frame where the current animation start - s32 Frames; // Relative Frames how much Frames this animation have - s32 LoopingFrames; // How much of Frames sould be looped - s32 EndFrame; // Absolute Frame where the current animation ends End = start + frames - 1 - - f32 FramesPerSecond; // Speed in Frames/Seconds the animation is played - f32 RelativeSpeed; // Factor Original fps is modified - - u32 BeginTime; // Animation started at this thime - u32 EndTime; // Animation end at this time - u32 LastTime; // Last Keyframe was done at this time - - KeyFrameInterpolation ( const c8 * name = "", s32 start = 0, s32 frames = 0, s32 loopingframes = 0, - f32 fps = 0.f, f32 relativefps = 1.f ) - : Name ( name ), AnimationType ( loopingframes ? EAMT_LOOPING : EAMT_WAYPOINT), - CurrentFrame ( (f32) start ), NextFrame ( start ), StartFrame ( start ), - Frames ( frames ), LoopingFrames ( loopingframes ), EndFrame ( start + frames - 1 ), - FramesPerSecond ( fps ), RelativeSpeed ( relativefps ), - BeginTime ( 0 ), EndTime ( 0 ), LastTime ( 0 ) - { - } - - // linear search - bool operator == ( const KeyFrameInterpolation & other ) const - { - return Name.equals_ignore_case ( other.Name ); - } - - }; - - - //! a List holding named Animations - typedef core::array < KeyFrameInterpolation > IAnimationList; - - //! a List holding named Skins - typedef core::array < core::stringc > ISkinList; - - - // Current Model per Body - struct SubModel - { - core::stringc name; - u32 startBuffer; - u32 endBuffer; - u32 state; - }; - - struct BodyPart - { - core::stringc name; - u32 defaultModel; - core::array < SubModel > model; - }; - //! a List holding named Models and SubModels - typedef core::array < BodyPart > IBodyList; - - //! Interface for an animated mesh. /** There are already simple implementations of this interface available so you don't have to implement this interface on your own if you need to: diff --git a/include/IAnimatedMeshMD3.h b/include/IAnimatedMeshMD3.h index 2be5d905..6ee0b56f 100644 --- a/include/IAnimatedMeshMD3.h +++ b/include/IAnimatedMeshMD3.h @@ -213,20 +213,20 @@ namespace scene //! holds a associative list of named quaternions struct SMD3QuaternionTagList { - SMD3QuaternionTagList () + SMD3QuaternionTagList() { - Container.setAllocStrategy ( core::ALLOC_STRATEGY_SAFE ); + Container.setAllocStrategy(core::ALLOC_STRATEGY_SAFE); } // construct copy constructor - SMD3QuaternionTagList( const SMD3QuaternionTagList & copyMe ) + SMD3QuaternionTagList(const SMD3QuaternionTagList& copyMe) { *this = copyMe; } - virtual ~SMD3QuaternionTagList () {} + virtual ~SMD3QuaternionTagList() {} - SMD3QuaternionTag* get ( const core::stringc& name ) + SMD3QuaternionTag* get(const core::stringc& name) { SMD3QuaternionTag search ( name ); s32 index = Container.linear_search ( search ); @@ -240,14 +240,14 @@ namespace scene return Container.size(); } - void set_used ( u32 new_size) + void set_used(u32 new_size) { - s32 diff = (s32) new_size - (s32) Container.allocated_size (); + s32 diff = (s32) new_size - (s32) Container.allocated_size(); if ( diff > 0 ) { - SMD3QuaternionTag e ( "" ); + SMD3QuaternionTag e(""); for ( s32 i = 0; i < diff; ++i ) - Container.push_back ( e ); + Container.push_back(e); } } @@ -261,9 +261,9 @@ namespace scene return Container[index]; } - void push_back ( const SMD3QuaternionTag& other ) + void push_back(const SMD3QuaternionTag& other) { - Container.push_back ( other ); + Container.push_back(other); } SMD3QuaternionTagList& operator = (const SMD3QuaternionTagList & copyMe) @@ -292,7 +292,7 @@ namespace scene } core::stringc Name; - core::array < SMD3MeshBuffer * > Buffer; + core::array Buffer; SMD3QuaternionTagList TagList; SMD3Header MD3Header; }; @@ -304,13 +304,13 @@ namespace scene public: //! tune how many frames you want to render inbetween. - virtual void setInterpolationShift ( u32 shift, u32 loopMode ) = 0; + virtual void setInterpolationShift(u32 shift, u32 loopMode) =0; //! get the tag list of the mesh. - virtual SMD3QuaternionTagList *getTagList(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) = 0; + virtual SMD3QuaternionTagList* getTagList(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) =0; //! get the original md3 mesh. - virtual SMD3Mesh * getOriginalMesh () = 0; + virtual SMD3Mesh* getOriginalMesh() =0; }; } // end namespace scene diff --git a/include/IAnimatedMeshSceneNode.h b/include/IAnimatedMeshSceneNode.h index d9d5ad7b..ababa419 100644 --- a/include/IAnimatedMeshSceneNode.h +++ b/include/IAnimatedMeshSceneNode.h @@ -130,12 +130,6 @@ namespace scene /** \return Amount of joints in the mesh. */ virtual u32 getJointCount() const = 0; - //! Deprecated command, please use getJointNode - virtual ISceneNode* getMS3DJointNode(const c8* jointName) = 0; - - //! Deprecated command, please use getJointNode - virtual ISceneNode* getXJointNode(const c8* jointName) = 0; - //! Starts a default MD2 animation. /** With this method it is easily possible to start a Run, Attack, Die or whatever animation, if the mesh contained in @@ -161,7 +155,7 @@ namespace scene animation with this name could be found. */ virtual bool setMD2Animation(const c8* animationName) = 0; - //! Returns the current displayed frame number. + //! Returns the currently displayed frame number. virtual f32 getFrameNr() const = 0; //! Returns the current start frame number. virtual s32 getStartFrame() const = 0; @@ -179,7 +173,7 @@ namespace scene virtual void setAnimationEndCallback(IAnimationEndCallBack* callback=0) = 0; //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style. - /* In this way it is possible to change the materials a mesh + /** In this way it is possible to change the materials a mesh causing all mesh scene nodes referencing this mesh to change too. */ virtual void setReadOnlyMaterials(bool readonly) = 0; diff --git a/include/IAttributes.h b/include/IAttributes.h index 1f594100..26e18ce7 100644 --- a/include/IAttributes.h +++ b/include/IAttributes.h @@ -24,6 +24,7 @@ #include "irrArray.h" #include "IXMLReader.h" #include "EAttributes.h" +#include "path.h" namespace irr { @@ -636,10 +637,10 @@ public: */ //! Adds an attribute as texture reference - virtual void addTexture(const c8* attributeName, video::ITexture* texture) = 0; + virtual void addTexture(const c8* attributeName, video::ITexture* texture, const io::path& filename = "") = 0; //! Sets an attribute as texture reference - virtual void setAttribute(const c8* attributeName, video::ITexture* texture ) = 0; + virtual void setAttribute(const c8* attributeName, video::ITexture* texture, const io::path& filename = "") = 0; //! Gets an attribute as texture reference //! \param attributeName: Name of the attribute to get. @@ -650,7 +651,7 @@ public: virtual video::ITexture* getAttributeAsTexture(s32 index) = 0; //! Sets an attribute as texture reference - virtual void setAttribute(s32 index, video::ITexture* texture) = 0; + virtual void setAttribute(s32 index, video::ITexture* texture, const io::path& filename = "") = 0; /* diff --git a/include/IBoneSceneNode.h b/include/IBoneSceneNode.h index 7ce769ec..d19f75af 100644 --- a/include/IBoneSceneNode.h +++ b/include/IBoneSceneNode.h @@ -60,8 +60,8 @@ namespace scene ISceneNode(parent, mgr, id),positionHint(-1),scaleHint(-1),rotationHint(-1) { } //! Get the name of the bone - /** \deprecated Use getName instead. */ - virtual const c8* getBoneName() const { return getName(); } + /** \deprecated Use getName instead. This method may be removed by Irrlicht 1.9 */ + _IRR_DEPRECATED_ virtual const c8* getBoneName() const { return getName(); } //! Get the index of the bone virtual u32 getBoneIndex() const = 0; diff --git a/include/IEventReceiver.h b/include/IEventReceiver.h index 500470ee..26e5a8cf 100644 --- a/include/IEventReceiver.h +++ b/include/IEventReceiver.h @@ -245,11 +245,12 @@ namespace irr //! A tree view node was expanded. See IGUITreeView::getLastEventNode(). EGET_TREEVIEW_NODE_EXPAND, - //! deprecated - use EGET_TREEVIEW_NODE_COLLAPSE instead - EGET_TREEVIEW_NODE_COLLAPS, - //! A tree view node was collapsed. See IGUITreeView::getLastEventNode(). - EGET_TREEVIEW_NODE_COLLAPSE = EGET_TREEVIEW_NODE_COLLAPS, + EGET_TREEVIEW_NODE_COLLAPSE, + + //! deprecated - use EGET_TREEVIEW_NODE_COLLAPSE instead. This + //! may be removed by Irrlicht 1.9 + EGET_TREEVIEW_NODE_COLLAPS = EGET_TREEVIEW_NODE_COLLAPSE, //! No real event. Just for convenience to get number of events EGET_COUNT diff --git a/include/IFileSystem.h b/include/IFileSystem.h index 4a526d59..95ead62a 100644 --- a/include/IFileSystem.h +++ b/include/IFileSystem.h @@ -117,10 +117,31 @@ public: E_FILE_ARCHIVE_TYPE archiveType=EFAT_UNKNOWN, const core::stringc& password="") =0; - //! Adds an external archive loader to the engine. - /** Use this function to add support for new archive types to the - engine, for example proprietary or encrypted file storage. */ - virtual void addArchiveLoader(IArchiveLoader* loader) =0; + //! Adds an archive to the file system. + /** After calling this, the Irrlicht Engine will also search and open + files directly from this archive. This is useful for hiding data from + the end user, speeding up file access and making it possible to access + for example Quake3 .pk3 files, which are just renamed .zip files. By + default Irrlicht supports ZIP, PAK, TAR, PNK, and directories as + archives. You can provide your own archive types by implementing + IArchiveLoader and passing an instance to addArchiveLoader. + Irrlicht supports AES-encrypted zip files, and the advanced compression + techniques lzma and bzip2. + \param file: Archive to add to the file system. + \param ignoreCase: If set to true, files in the archive can be accessed without + writing all letters in the right case. + \param ignorePaths: If set to true, files in the added archive can be accessed + without its complete path. + \param archiveType: If no specific E_FILE_ARCHIVE_TYPE is selected then + the type of archive will depend on the extension of the file name. If + you use a different extension then you can use this parameter to force + a specific type of archive. + \param password An optional password, which is used in case of encrypted archives. + \return True if the archive was added successfully, false if not. */ + virtual bool addFileArchive(IReadFile* file, bool ignoreCase=true, + bool ignorePaths=true, + E_FILE_ARCHIVE_TYPE archiveType=EFAT_UNKNOWN, + const core::stringc& password="") =0; //! Get the number of archives currently attached to the file system virtual u32 getFileArchiveCount() const =0; @@ -149,9 +170,23 @@ public: //! Get the archive at a given index. virtual IFileArchive* getFileArchive(u32 index) =0; + //! Adds an external archive loader to the engine. + /** Use this function to add support for new archive types to the + engine, for example proprietary or encrypted file storage. */ + virtual void addArchiveLoader(IArchiveLoader* loader) =0; + + //! Gets the number of archive loaders currently added + virtual u32 getArchiveLoaderCount() const = 0; + + //! Retrieve the given archive loader + /** \param index The index of the loader to retrieve. This parameter is an 0-based + array index. + \return A pointer to the specified loader, 0 if the index is incorrect. */ + virtual IArchiveLoader* getArchiveLoader(u32 index) const = 0; + //! Adds a zip archive to the file system. /** \deprecated This function is provided for compatibility - with older versions of Irrlicht and may be removed in future versions, + with older versions of Irrlicht and may be removed in Irrlicht 1.9, you should use addFileArchive instead. After calling this, the Irrlicht Engine will search and open files directly from this archive too. This is useful for hiding data from the end user, speeding up file access and making it possible to @@ -162,14 +197,14 @@ public: \param ignorePaths: If set to true, files in the added archive can be accessed without its complete path. \return True if the archive was added successfully, false if not. */ - virtual bool addZipFileArchive(const c8* filename, bool ignoreCase=true, bool ignorePaths=true) + _IRR_DEPRECATED_ virtual bool addZipFileArchive(const c8* filename, bool ignoreCase=true, bool ignorePaths=true) { return addFileArchive(filename, ignoreCase, ignorePaths, EFAT_ZIP); } //! Adds an unzipped archive (or basedirectory with subdirectories..) to the file system. /** \deprecated This function is provided for compatibility - with older versions of Irrlicht and may be removed in future versions, + with older versions of Irrlicht and may be removed in Irrlicht 1.9, you should use addFileArchive instead. Useful for handling data which will be in a zip file \param filename: Filename of the unzipped zip archive base directory to add to the file system. @@ -178,14 +213,14 @@ public: \param ignorePaths: If set to true, files in the added archive can be accessed without its complete path. \return True if the archive was added successful, false if not. */ - virtual bool addFolderFileArchive(const c8* filename, bool ignoreCase=true, bool ignorePaths=true) + _IRR_DEPRECATED_ virtual bool addFolderFileArchive(const c8* filename, bool ignoreCase=true, bool ignorePaths=true) { return addFileArchive(filename, ignoreCase, ignorePaths, EFAT_FOLDER); } //! Adds a pak archive to the file system. /** \deprecated This function is provided for compatibility - with older versions of Irrlicht and may be removed in future versions, + with older versions of Irrlicht and may be removed in Irrlicht 1.9, you should use addFileArchive instead. After calling this, the Irrlicht Engine will search and open files directly from this archive too. This is useful for hiding data from the end user, speeding up file access and making it possible to @@ -196,7 +231,7 @@ public: \param ignorePaths: If set to true, files in the added archive can be accessed without its complete path.(should not use with Quake2 paks \return True if the archive was added successful, false if not. */ - virtual bool addPakFileArchive(const c8* filename, bool ignoreCase=true, bool ignorePaths=true) + _IRR_DEPRECATED_ virtual bool addPakFileArchive(const c8* filename, bool ignoreCase=true, bool ignorePaths=true) { return addFileArchive(filename, ignoreCase, ignorePaths, EFAT_PAK); } diff --git a/include/IGUIListBox.h b/include/IGUIListBox.h index 4217fd7d..76c293a2 100644 --- a/include/IGUIListBox.h +++ b/include/IGUIListBox.h @@ -56,6 +56,10 @@ namespace gui //! Removes an item from the list virtual void removeItem(u32 index) = 0; + //! get the the id of the item at the given absolute coordinates + /** \return The id of the listitem or -1 when no item is at those coordinates*/ + virtual s32 getItemAt(s32 xpos, s32 ypos) const = 0; + //! Returns the icon of an item virtual s32 getIcon(u32 index) const = 0; diff --git a/include/IGUISkin.h b/include/IGUISkin.h index 49bae3db..9f5064e3 100644 --- a/include/IGUISkin.h +++ b/include/IGUISkin.h @@ -31,8 +31,10 @@ namespace gui { //! Default windows look and feel EGST_WINDOWS_CLASSIC=0, + //! Like EGST_WINDOWS_CLASSIC, but with metallic shaded windows and buttons EGST_WINDOWS_METALLIC, + //! Burning's skin EGST_BURNING_SKIN, @@ -155,9 +157,9 @@ namespace gui EGDS_WINDOW_BUTTON_WIDTH, //! width of a checkbox check EGDS_CHECK_BOX_WIDTH, - //! deprecated + //! \deprecated This may be removed by Irrlicht 1.9 EGDS_MESSAGE_BOX_WIDTH, - //! deprecated + //! \deprecated This may be removed by Irrlicht 1.9 EGDS_MESSAGE_BOX_HEIGHT, //! width of a default button EGDS_BUTTON_WIDTH, @@ -177,8 +179,6 @@ namespace gui EGDS_MESSAGE_BOX_MIN_TEXT_WIDTH, //! maximal space to reserve for messagebox text-width EGDS_MESSAGE_BOX_MAX_TEXT_WIDTH, - //! deprecated - this was a typo. Should be removed for 1.8 - EGDS_MESSAGE_BOX_MAX_TEST_WIDTH = EGDS_MESSAGE_BOX_MAX_TEXT_WIDTH, //! minimal space to reserve for messagebox text-height EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT, //! maximal space to reserve for messagebox text-height @@ -294,6 +294,7 @@ namespace gui EGDI_MORE_DOWN, //! plus icon for trees EGDI_EXPAND, + //! minus icon for trees EGDI_COLLAPSE, //! file icon for file selection diff --git a/include/IGUIStaticText.h b/include/IGUIStaticText.h index b9ab1ff0..221bfa21 100644 --- a/include/IGUIStaticText.h +++ b/include/IGUIStaticText.h @@ -97,6 +97,17 @@ namespace gui //! Checks if the text in this label should be clipped if it goes outside bounds virtual bool isTextRestrainedInside() const = 0; + + //! Set whether the string should be interpreted as right-to-left (RTL) text + /** \note This component does not implement the Unicode bidi standard, the + text of the component should be already RTL if you call this. The + main difference when RTL is enabled is that the linebreaks for multiline + elements are performed starting from the end. + */ + virtual void setRightToLeft(bool rtl) = 0; + + //! Checks whether the text in this element should be interpreted as right-to-left + virtual bool isRightToLeft() const = 0; }; diff --git a/include/IGUITable.h b/include/IGUITable.h index ff9f000a..96fa5f11 100644 --- a/include/IGUITable.h +++ b/include/IGUITable.h @@ -113,6 +113,9 @@ namespace gui //! Set the width of a column virtual void setColumnWidth(u32 columnIndex, u32 width) = 0; + //! Get the width of a column + virtual u32 getColumnWidth(u32 columnIndex) const = 0; + //! columns can be resized by drag 'n drop virtual void setResizableColumns(bool resizable) = 0; diff --git a/include/IGUITreeView.h b/include/IGUITreeView.h index 7e7192f4..b4959e6c 100644 --- a/include/IGUITreeView.h +++ b/include/IGUITreeView.h @@ -70,7 +70,8 @@ namespace gui virtual void clearChildren() = 0; //! removes all children (recursive) from this node - /** \deprecated Deprecated in 1.8, use clearChildren() instead. */ + /** \deprecated Deprecated in 1.8, use clearChildren() instead. + This method may be removed by Irrlicht 1.9 */ _IRR_DEPRECATED_ void clearChilds() { return clearChildren(); @@ -80,7 +81,8 @@ namespace gui virtual bool hasChildren() const = 0; //! returns true if this node has child nodes - /** \deprecated Deprecated in 1.8, use hasChildren() instead. */ + /** \deprecated Deprecated in 1.8, use hasChildren() instead. + This method may be removed by Irrlicht 1.9 */ _IRR_DEPRECATED_ bool hasChilds() const { return hasChildren(); diff --git a/include/ILightManager.h b/include/ILightManager.h index eaa4151c..e9ea87f3 100644 --- a/include/ILightManager.h +++ b/include/ILightManager.h @@ -34,7 +34,7 @@ namespace scene the light manager may modify. This reference will remain valid until OnPostRender(). */ - virtual void OnPreRender(core::array & lightList) = 0; + 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. */ diff --git a/include/IMeshCache.h b/include/IMeshCache.h index 3f8ed878..5f1f5252 100644 --- a/include/IMeshCache.h +++ b/include/IMeshCache.h @@ -79,35 +79,40 @@ namespace scene virtual IAnimatedMesh* getMeshByIndex(u32 index) = 0; //! Returns a mesh based on its name (often a filename). - /** \deprecated Use getMeshByName() instead. */ + /** \deprecated Use getMeshByName() instead. This method may be removed by + Irrlicht 1.9 */ _IRR_DEPRECATED_ IAnimatedMesh* getMeshByFilename(const io::path& filename) { return getMeshByName(filename); } //! Get the name of a loaded mesh, based on its index. (Name is often identical to the filename). - /** \deprecated Use getMeshName() instead. */ + /** \deprecated Use getMeshName() instead. This method may be removed by + Irrlicht 1.9 */ _IRR_DEPRECATED_ const io::path& getMeshFilename(u32 index) const { return getMeshName(index).getInternalName(); } //! Get the name of a loaded mesh, if there is any. (Name is often identical to the filename). - /** \deprecated Use getMeshName() instead. */ + /** \deprecated Use getMeshName() instead. This method may be removed by + Irrlicht 1.9 */ _IRR_DEPRECATED_ const io::path& getMeshFilename(const IMesh* const mesh) const { return getMeshName(mesh).getInternalName(); } //! Renames a loaded mesh. - /** \deprecated Use renameMesh() instead. */ + /** \deprecated Use renameMesh() instead. This method may be removed by + Irrlicht 1.9 */ _IRR_DEPRECATED_ bool setMeshFilename(u32 index, const io::path& filename) { return renameMesh(index, filename); } //! Renames a loaded mesh. - /** \deprecated Use renameMesh() instead. */ + /** \deprecated Use renameMesh() instead. This method may be removed by + Irrlicht 1.9 */ _IRR_DEPRECATED_ bool setMeshFilename(const IMesh* const mesh, const io::path& filename) { return renameMesh(mesh, filename); diff --git a/include/IMeshManipulator.h b/include/IMeshManipulator.h index 97d85fb5..88fd6c09 100644 --- a/include/IMeshManipulator.h +++ b/include/IMeshManipulator.h @@ -91,10 +91,10 @@ namespace scene } //! Scales the actual mesh, not a scene node. - /** \deprecated Use scale() instead + /** \deprecated Use scale() instead. This method may be removed by Irrlicht 1.9 \param mesh Mesh on which the operation is performed. \param factor Scale factor for each axis. */ - void scaleMesh(IMesh* mesh, const core::vector3df& factor) const {return scale(mesh,factor);} + _IRR_DEPRECATED_ void scaleMesh(IMesh* mesh, const core::vector3df& factor) const {return scale(mesh,factor);} //! Scale the texture coords of a mesh. /** \param mesh Mesh on which the operation is performed. @@ -131,10 +131,10 @@ namespace scene } //! Applies a transformation to a mesh - /** \deprecated Use transform() instead + /** \deprecated Use transform() instead. This method may be removed by Irrlicht 1.9 \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);} + _IRR_DEPRECATED_ virtual void transformMesh(IMesh* mesh, const core::matrix4& m) const {return transform(mesh,m);} //! Clones a static IMesh into a modifiable SMesh. /** All meshbuffers in the returned SMesh diff --git a/include/IParticleSystemSceneNode.h b/include/IParticleSystemSceneNode.h index 92ee0b58..f0693c4d 100644 --- a/include/IParticleSystemSceneNode.h +++ b/include/IParticleSystemSceneNode.h @@ -454,7 +454,7 @@ public: //! Creates a fade out particle affector. /** This affector modifies the color of every particle and and reaches the final color when the particle dies. This affector looks really - good, if the EMT_TRANSPARENT_VERTEX_ALPHA material is used and the + good, if the EMT_TRANSPARENT_ADD_COLOR material is used and the targetColor is video::SColor(0,0,0,0): Particles are fading out into void with this setting. \param targetColor: Color whereto the color of the particle is changed. diff --git a/include/IQ3Shader.h b/include/IQ3Shader.h index e00be85e..378516a9 100644 --- a/include/IQ3Shader.h +++ b/include/IQ3Shader.h @@ -771,14 +771,19 @@ namespace quake3 io::IFileSystem *fileSystem, video::IVideoDriver* driver) { - static const char * extension[2] = + static const char* extension[] = { ".jpg", - ".tga" + ".jpeg", + ".png", + ".dds", + ".tga", + ".bmp", + ".pcx" }; tStringList stringList; - getAsStringList ( stringList, -1, name, startPos ); + getAsStringList(stringList, -1, name, startPos); textures.clear(); @@ -786,7 +791,7 @@ namespace quake3 for ( u32 i = 0; i!= stringList.size (); ++i ) { video::ITexture* texture = 0; - for ( u32 g = 0; g != 2 ; ++g ) + for (u32 g = 0; g != 7 ; ++g) { core::cutFilenameExtension ( loadFile, stringList[i] ); diff --git a/include/ISceneLoader.h b/include/ISceneLoader.h new file mode 100644 index 00000000..04e076d5 --- /dev/null +++ b/include/ISceneLoader.h @@ -0,0 +1,58 @@ +#ifndef __I_SCENE_LOADER_H_INCLUDED__ +#define __I_SCENE_LOADER_H_INCLUDED__ + +#include "IReferenceCounted.h" +#include "path.h" + +namespace irr +{ +namespace io +{ + class IReadFile; +} // end namespace io +namespace scene +{ + class ISceneNode; + class ISceneUserDataSerializer; + +//! Class which can load a scene into the scene manager. +/** If you want Irrlicht to be able to load currently unsupported +scene file formats (e.g. .vrml), then implement this and add your +new Sceneloader to the engine with ISceneManager::addExternalSceneLoader(). */ +class ISceneLoader : public virtual IReferenceCounted +{ +public: + + //! Returns true if the class might be able to load this file. + /** This decision should be based on the file extension (e.g. ".vrml") + only. + \param fileName Name of the file to test. + \return True if the extension is a recognised type. */ + virtual bool isALoadableFileExtension(const io::path& filename) const = 0; + + //! Returns true if the class might be able to load this file. + /** This decision will be based on a quick look at the contents of the file. + \param file The file to test. + \return True if the extension is a recognised type. */ + virtual bool isALoadableFileFormat(io::IReadFile* file) const = 0; + + //! Loads the scene into the scene manager. + /** \param file File which contains the scene. + \param userDataSerializer: If you want to load user data which may be attached + to some some scene nodes in the file, implement the ISceneUserDataSerializer + interface and provide it as parameter here. Otherwise, simply specify 0 as this + parameter. + \param rootNode The node to load the scene into, if none is provided then the + scene will be loaded into the root node. + \return Returns true on success, false on failure. Returns 0 if loading failed. */ + virtual bool loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer=0, + ISceneNode* rootNode=0) = 0; + +}; + + +} // end namespace scene +} // end namespace irr + +#endif + diff --git a/include/ISceneManager.h b/include/ISceneManager.h index e00db57f..6edf0947 100644 --- a/include/ISceneManager.h +++ b/include/ISceneManager.h @@ -49,8 +49,6 @@ namespace video namespace scene { - class IMeshWriter; - //! Enumeration for render passes. /** A parameter passed to the registerNodeForRendering() method of the ISceneManager, specifying when the node wants to be drawn in relation to the other nodes. */ @@ -96,33 +94,35 @@ namespace scene ESNRP_SHADOW =64 }; + class IAnimatedMesh; + class IAnimatedMeshSceneNode; + class IBillboardSceneNode; + class IBillboardTextSceneNode; + class ICameraSceneNode; + class IDummyTransformationSceneNode; + class ILightManager; + class ILightSceneNode; class IMesh; class IMeshBuffer; - class IAnimatedMesh; class IMeshCache; + class IMeshLoader; + class IMeshManipulator; + class IMeshSceneNode; + class IMeshWriter; + class IMetaTriangleSelector; + class IParticleSystemSceneNode; + class ISceneCollisionManager; + class ISceneLoader; class ISceneNode; - class ICameraSceneNode; - class IAnimatedMeshSceneNode; class ISceneNodeAnimator; class ISceneNodeAnimatorCollisionResponse; - class ILightSceneNode; - class IBillboardSceneNode; - class ITerrainSceneNode; - class IMeshSceneNode; - class IMeshLoader; - class ISceneCollisionManager; - class IParticleSystemSceneNode; - class IDummyTransformationSceneNode; - class ITriangleSelector; - class IMetaTriangleSelector; - class IMeshManipulator; - class ITextSceneNode; - class IBillboardTextSceneNode; - class IVolumeLightSceneNode; - class ISceneNodeFactory; class ISceneNodeAnimatorFactory; + class ISceneNodeFactory; class ISceneUserDataSerializer; - class ILightManager; + class ITerrainSceneNode; + class ITextSceneNode; + class ITriangleSelector; + class IVolumeLightSceneNode; namespace quake3 { @@ -144,16 +144,13 @@ namespace scene character on a moving platform on a moving ship. The SceneManager is also able to load 3d mesh files of different formats. Take a look at getMesh() to find out what formats are - supported. And if these formats are not enough use + supported. If these formats are not enough, use addExternalMeshLoader() to add new formats to the engine. */ class ISceneManager : public virtual IReferenceCounted { public: - //! Destructor - virtual ~ISceneManager() {} - //! Get pointer to an animateable mesh. Loads the file if not loaded already. /** * If you want to remove a loaded mesh from the cache again, use removeMesh(). @@ -167,12 +164,22 @@ namespace scene * 3D Studio (.3ds) * Loader for 3D-Studio files which lots of 3D packages * are able to export. Only static meshes are currently - * supported by this importer. + * supported by this importer. + * + * + * 3D World Studio (.smf) + * Loader for Leadwerks SMF mesh files, a simple mesh format + * containing static geometry for games. The proprietary .STF texture format + * is not supported yet. This loader was originally written by Joseph Ellis. * * * Bliz Basic B3D (.b3d) * Loader for blitz basic files, developed by Mark - * Sibly, also supports animations. + * Sibly. This is the ideal animated mesh format for game + * characters as it is both rigidly defined and widely + * supported by modeling and animation software. + * As this format supports skeletal animations, an + * ISkinnedMesh will be returned by this importer. * * * Cartography shop 4 (.csm) @@ -203,7 +210,7 @@ namespace scene * behaves like the other loaders and does not create * instances, but it can be switched into this mode by * using - * SceneManager->getParameters()->setAttribute(COLLADA_CREATE_SCENE_INSTANCES, true); + * SceneManager->getParameters()->setAttribute(COLLADA_CREATE_SCENE_INSTANCES, true); * Created scene nodes will be named as the names of the * nodes in the COLLADA file. The returned mesh is just * a dummy object in this mode. Meshes included in the @@ -211,6 +218,8 @@ namespace scene * following naming scheme: * "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 * supported but this will change with future releases. @@ -244,8 +253,28 @@ namespace scene * and there are several tools for them available, e.g. * the Maya exporter included in the DX SDK. * .x files can include skeletal animations and Irrlicht - * is able to play and display them. Currently, Irrlicht - * only supports uncompressed .x files. + * is able to play and display them, users can manipulate + * the joints via the ISkinnedMesh interface. Currently, + * Irrlicht only supports uncompressed .x files. + * + * + * Half-Life model (.mdl) + * This loader opens Half-life 1 models, it was contributed + * by Fabio Concas and adapted by Thomas Alten. + * + * + * Irrlicht Mesh (.irrMesh) + * This is a static mesh format written in XML, native + * to Irrlicht and written by the irr mesh writer. + * This format is exported by the CopperCube engine's + * lightmapper. + * + * + * LightWave (.lwo) + * Native to NewTek's LightWave 3D, the LWO format is well + * known and supported by many exporters. This loader will + * import LWO2 models including lightmaps, bumpmaps and + * reflection textures. * * * Maya (.obj) @@ -258,8 +287,8 @@ namespace scene * Milkshape (.ms3d) * .MS3D files contain models and sometimes skeletal * animations from the Milkshape 3D modeling and animation - * software. This importer for Irrlicht can display and/or - * animate these files. + * software. Like the other skeletal mesh loaders, oints + * are exposed via the ISkinnedMesh animated mesh type. * * * My3D (.my3d) @@ -331,6 +360,29 @@ namespace scene * animation. Irrlicht can read, display and animate * them directly with this importer. * + * + * Quake 3 models (.md3) + * Quake 3 models are characters with morph target + * animation, they contain mount points for weapons and body + * parts and are typically made of several sections which are + * manually joined together. + * + * + * Stanford Triangle (.ply) + * Invented by Stanford University and known as the native + * format of the infamous "Stanford Bunny" model, this is a + * popular static mesh format used by 3D scanning hardware + * and software. This loader supports extremely large models + * in both ASCII and binary format, but only has rudimentary + * material support in the form of vertex colors and texture + * coordinates. + * + * + * Stereolithography (.stl) + * The STL format is used for rapid prototyping and + * computer-aided manufacturing, thus has no support for + * materials. + * * * * To load and display a mesh quickly, just do this: @@ -510,7 +562,7 @@ namespace scene s32 id=-1, s32 minimalPolysPerNode=512, bool alsoAddIfMeshPointerZero=false) = 0; //! Adds a scene node for rendering using a octree to the scene graph. - /** \deprecated Use addOctreeSceneNode instead. */ + /** \deprecated Use addOctreeSceneNode instead. This method may be removed by Irrlicht 1.9. */ _IRR_DEPRECATED_ IMeshSceneNode* addOctTreeSceneNode(IAnimatedMesh* mesh, ISceneNode* parent=0, s32 id=-1, s32 minimalPolysPerNode=512, bool alsoAddIfMeshPointerZero=false) { @@ -534,7 +586,7 @@ namespace scene s32 id=-1, s32 minimalPolysPerNode=256, bool alsoAddIfMeshPointerZero=false) = 0; //! Adds a scene node for rendering using a octree to the scene graph. - /** \deprecated Use addOctreeSceneNode instead. */ + /** \deprecated Use addOctreeSceneNode instead. This method may be removed by Irrlicht 1.9. */ _IRR_DEPRECATED_ IMeshSceneNode* addOctTreeSceneNode(IMesh* mesh, ISceneNode* parent=0, s32 id=-1, s32 minimalPolysPerNode=256, bool alsoAddIfMeshPointerZero=false) { @@ -1278,7 +1330,7 @@ namespace scene ISceneNode* node, s32 minimalPolysPerNode=32) = 0; //! //! Creates a Triangle Selector, optimized by an octree. - /** \deprecated Use createOctreeTriangleSelector instead. */ + /** \deprecated Use createOctreeTriangleSelector instead. This method may be removed by Irrlicht 1.9. */ _IRR_DEPRECATED_ ITriangleSelector* createOctTreeTriangleSelector(IMesh* mesh, ISceneNode* node, s32 minimalPolysPerNode=32) { @@ -1309,10 +1361,37 @@ namespace scene file formats it currently is not able to load (e.g. .cob), just implement the IMeshLoader interface in your loading class and add it with this method. Using this method it is also possible to override built-in mesh loaders with - newer or updated versions without the need of recompiling the engine. + newer or updated versions without the need to recompile the engine. \param externalLoader: Implementation of a new mesh loader. */ virtual void addExternalMeshLoader(IMeshLoader* externalLoader) = 0; + //! Returns the number of mesh loaders supported by Irrlicht at this time + virtual u32 getMeshLoaderCount() const = 0; + + //! Retrieve the given mesh loader + /** \param index The index of the loader to retrieve. This parameter is an 0-based + array index. + \return A pointer to the specified loader, 0 if the index is incorrect. */ + virtual IMeshLoader* getMeshLoader(u32 index) const = 0; + + //! Adds an external scene loader for extending the engine with new file formats. + /** If you want the engine to be extended with + file formats it currently is not able to load (e.g. .vrml), just implement + the ISceneLoader interface in your loading class and add it with this method. + Using this method it is also possible to override the built-in scene loaders + with newer or updated versions without the need to recompile the engine. + \param externalLoader: Implementation of a new mesh loader. */ + virtual void addExternalSceneLoader(ISceneLoader* externalLoader) = 0; + + //! Returns the number of scene loaders supported by Irrlicht at this time + virtual u32 getSceneLoaderCount() const = 0; + + //! Retrieve the given scene loader + /** \param index The index of the loader to retrieve. This parameter is an 0-based + array index. + \return A pointer to the specified loader, 0 if the index is incorrect. */ + virtual ISceneLoader* getSceneLoader(u32 index) const = 0; + //! Get pointer to the scene collision manager. /** \return Pointer to the collision manager This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ @@ -1407,6 +1486,13 @@ namespace scene This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ISceneNode* addSceneNode(const char* sceneNodeTypeName, ISceneNode* parent=0) = 0; + //! creates a scene node animator based on its type name + /** \param typeName: Type of the scene node animator to add. + \param target: Target scene node of the new animator. + \return Returns pointer to the new scene node animator or null if not successful. You need to + drop this pointer after calling this, see IReferenceCounted::drop() for details. */ + virtual ISceneNodeAnimator* createSceneNodeAnimator(const char* typeName, ISceneNode* target=0) = 0; + //! Creates a new scene manager. /** This can be used to easily draw and/or store two independent scenes at the same time. The mesh cache will be @@ -1459,7 +1545,8 @@ namespace scene virtual bool saveScene(io::IWriteFile* file, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* node=0) = 0; //! Loads a scene. Note that the current scene is not cleared before. - /** The scene is usually load from an .irr file, an xml based format. .irr files can + /** The scene is usually loaded from an .irr file, an xml based format, but other scene formats + can be added to the engine via ISceneManager::addExternalSceneLoader. .irr files can Be edited with the Irrlicht Engine Editor, irrEdit (http://irredit.irrlicht3d.org) or saved directly by the engine using ISceneManager::saveScene(). \param filename: Name of the file. @@ -1468,13 +1555,14 @@ namespace scene implement the ISceneUserDataSerializer interface and provide it as parameter here. Otherwise, simply specify 0 as this parameter. - \param node Node which is taken as the root node of the scene. Pass 0 to add the scene + \param rootNode Node which is taken as the root node of the scene. Pass 0 to add the scene directly to the scene manager (which is also the default). \return True if successful. */ - virtual bool loadScene(const io::path& filename, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* node=0) = 0; + virtual bool loadScene(const io::path& filename, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* rootNode=0) = 0; //! Loads a scene. Note that the current scene is not cleared before. - /** The scene is usually load from an .irr file, an xml based format. .irr files can + /** The scene is usually loaded from an .irr file, an xml based format, but other scene formats + can be added to the engine via ISceneManager::addExternalSceneLoader. .irr files can Be edited with the Irrlicht Engine Editor, irrEdit (http://irredit.irrlicht3d.org) or saved directly by the engine using ISceneManager::saveScene(). \param file: File where the scene is going to be saved into. @@ -1483,10 +1571,10 @@ namespace scene implement the ISceneUserDataSerializer interface and provide it as parameter here. Otherwise, simply specify 0 as this parameter. - \param node Node which is taken as the root node of the scene. Pass 0 to add the scene + \param rootNode Node which is taken as the root node of the scene. Pass 0 to add the scene directly to the scene manager (which is also the default). \return True if successful. */ - virtual bool loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* node=0) = 0; + virtual bool loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* rootNode=0) = 0; //! Get a mesh writer implementation if available /** Note: You need to drop() the pointer after use again, see IReferenceCounted::drop() diff --git a/include/ISceneNode.h b/include/ISceneNode.h index 1db3cf5a..a593189c 100644 --- a/include/ISceneNode.h +++ b/include/ISceneNode.h @@ -539,7 +539,7 @@ namespace scene /** A bitwise OR of the types from @ref irr::scene::E_DEBUG_SCENE_TYPE. Please note that not all scene nodes support all debug data types. \param state The debug data visibility state to be used. */ - virtual void setDebugDataVisible(s32 state) + virtual void setDebugDataVisible(u32 state) { DebugDataVisible = state; } @@ -547,7 +547,7 @@ namespace scene //! Returns if debug data like bounding boxes are drawn. /** \return A bitwise OR of the debug data values from @ref irr::scene::E_DEBUG_SCENE_TYPE that are currently visible. */ - s32 isDebugDataVisible() const + u32 isDebugDataVisible() const { return DebugDataVisible; } @@ -831,7 +831,7 @@ namespace scene u32 AutomaticCullingState; //! Flag if debug data should be drawn, such as Bounding Boxes. - s32 DebugDataVisible; + u32 DebugDataVisible; //! Is the node visible? bool IsVisible; diff --git a/include/ISceneNodeAnimatorFactory.h b/include/ISceneNodeAnimatorFactory.h index f6600e72..8441ca23 100644 --- a/include/ISceneNodeAnimatorFactory.h +++ b/include/ISceneNodeAnimatorFactory.h @@ -28,8 +28,6 @@ namespace scene { public: - virtual ~ISceneNodeAnimatorFactory() {} - //! creates a scene node animator based on its type id /** \param type: Type of the scene node animator to add. \param target: Target scene node of the new animator. diff --git a/include/ITriangleSelector.h b/include/ITriangleSelector.h index e3ba5b78..f74109d0 100644 --- a/include/ITriangleSelector.h +++ b/include/ITriangleSelector.h @@ -29,10 +29,7 @@ class ITriangleSelector : public virtual IReferenceCounted { public: - //! Destructor - virtual ~ITriangleSelector() {} - - //! Returns amount of all available triangles in this selector + //! Get amount of all available triangles in this selector virtual s32 getTriangleCount() const = 0; //! Gets the triangles for one associated node. @@ -41,19 +38,19 @@ public: selector. If there is more than one scene node associated (e.g. for an IMetaTriangleSelector) this this function may be called multiple times to retrieve all triangles. - \param triangles: Array where the resulting triangles will be + \param triangles Array where the resulting triangles will be written to. - \param arraySize: Size of the target array. + \param arraySize Size of the target array. \param outTriangleCount: Amount of triangles which have been written into the array. - \param transform: Pointer to matrix for transforming the triangles + \param transform Pointer to matrix for transforming the triangles before they are returned. Useful for example to scale all triangles down into an ellipsoid space. If this pointer is null, no transformation will be done. */ virtual void getTriangles(core::triangle3df* triangles, s32 arraySize, s32& outTriangleCount, const core::matrix4* transform=0) const = 0; - //! Gets the triangles for one associated node which lie or may lie within a specific bounding box. + //! Gets the triangles for one associated node which may lie within a specific bounding box. /** This returns all triangles for one scene node associated with this selector. If there is more than one scene node associated (e.g. for @@ -62,14 +59,14 @@ public: This method will return at least the triangles that intersect the box, but may return other triangles as well. - \param triangles: Array where the resulting triangles will be written + \param triangles Array where the resulting triangles will be written to. - \param arraySize: Size of the target array. - \param outTriangleCount: Amount of triangles which have been written + \param arraySize Size of the target array. + \param outTriangleCount Amount of triangles which have been written into the array. - \param box: Only triangles which are in this axis aligned bounding box + \param box Only triangles which are in this axis aligned bounding box will be written into the array. - \param transform: Pointer to matrix for transforming the triangles + \param transform Pointer to matrix for transforming the triangles before they are returned. Useful for example to scale all triangles down into an ellipsoid space. If this pointer is null, no transformation will be done. */ @@ -86,14 +83,14 @@ public: Please note that unoptimized triangle selectors also may return triangles which are not in contact at all with the 3d line. - \param triangles: Array where the resulting triangles will be written + \param triangles Array where the resulting triangles will be written to. - \param arraySize: Size of the target array. - \param outTriangleCount: Amount of triangles which have been written + \param arraySize Size of the target array. + \param outTriangleCount Amount of triangles which have been written into the array. - \param line: Only triangles which may be in contact with this 3d line + \param line Only triangles which may be in contact with this 3d line will be written into the array. - \param transform: Pointer to matrix for transforming the triangles + \param transform Pointer to matrix for transforming the triangles before they are returned. Useful for example to scale all triangles down into an ellipsoid space. If this pointer is null, no transformation will be done. */ @@ -101,9 +98,9 @@ public: s32& outTriangleCount, const core::line3d& line, const core::matrix4* transform=0) const = 0; - //! Return the scene node associated with a given triangle. + //! Get scene node associated with a given triangle. /** - This allows you to find which scene node (potentially of several) is + This allows to find which scene node (potentially of several) is associated with a specific triangle. \param triangleIndex: the index of the triangle for which you want to find @@ -112,11 +109,23 @@ public: */ virtual ISceneNode* getSceneNodeForTriangle(u32 triangleIndex) const = 0; + //! Get number of TriangleSelectors that are part of this one + /** Only useful for MetaTriangleSelector, others return 1 + */ + virtual u32 getSelectorCount() const = 0; + + //! Get TriangleSelector based on index based on getSelectorCount + /** Only useful for MetaTriangleSelector, others return 'this' or 0 + */ + virtual ITriangleSelector* getSelector(u32 index) = 0; + + //! Get TriangleSelector based on index based on getSelectorCount + /** Only useful for MetaTriangleSelector, others return 'this' or 0 + */ + virtual const ITriangleSelector* getSelector(u32 index) const = 0; }; } // end namespace scene } // end namespace irr - #endif - diff --git a/include/IVideoDriver.h b/include/IVideoDriver.h index 4bef5ac4..07dc317a 100644 --- a/include/IVideoDriver.h +++ b/include/IVideoDriver.h @@ -25,6 +25,7 @@ namespace irr namespace io { class IAttributes; + struct SAttributeReadWriteOptions; class IReadFile; class IWriteFile; } // end namespace io @@ -84,11 +85,11 @@ namespace video ETS_COUNT }; - //! enumeration for signalling ressources which were lost after the last render cycle - /** These values can be signalled by the driver, telling the app that some ressources + //! enumeration for signaling resources which were lost after the last render cycle + /** These values can be signaled by the driver, telling the app that some resources were lost and need to be recreated. Irrlicht will sometimes recreate the actual objects, but the content needs to be recreated by the application. */ - enum E_LOST_RESSOURCE + enum E_LOST_RESOURCE { //! The whole device/driver is lost ELR_DEVICE = 1, @@ -208,20 +209,20 @@ namespace video E_COLOR_PLANE colorMask=ECP_ALL, E_BLEND_FACTOR blendFuncSrc=EBF_ONE, E_BLEND_FACTOR blendFuncDst=EBF_ONE_MINUS_SRC_ALPHA, - bool blendEnable=false) : + E_BLEND_OPERATION blendOp=EBO_NONE) : RenderTexture(texture), TargetType(ERT_RENDER_TEXTURE), ColorMask(colorMask), BlendFuncSrc(blendFuncSrc), BlendFuncDst(blendFuncDst), - BlendEnable(blendEnable) {} + BlendOp(blendOp) {} IRenderTarget(E_RENDER_TARGET target, E_COLOR_PLANE colorMask=ECP_ALL, E_BLEND_FACTOR blendFuncSrc=EBF_ONE, E_BLEND_FACTOR blendFuncDst=EBF_ONE_MINUS_SRC_ALPHA, - bool blendEnable=false) : + E_BLEND_OPERATION blendOp=EBO_NONE) : RenderTexture(0), TargetType(target), ColorMask(colorMask), BlendFuncSrc(blendFuncSrc), BlendFuncDst(blendFuncDst), - BlendEnable(blendEnable) {} + BlendOp(blendOp) {} bool operator!=(const IRenderTarget& other) const { return ((RenderTexture != other.RenderTexture) || @@ -229,14 +230,14 @@ namespace video (ColorMask != other.ColorMask) || (BlendFuncSrc != other.BlendFuncSrc) || (BlendFuncDst != other.BlendFuncDst) || - (BlendEnable != other.BlendEnable)); + (BlendOp != other.BlendOp)); } ITexture* RenderTexture; E_RENDER_TARGET TargetType:8; E_COLOR_PLANE ColorMask:8; E_BLEND_FACTOR BlendFuncSrc:4; E_BLEND_FACTOR BlendFuncDst:4; - bool BlendEnable; + E_BLEND_OPERATION BlendOp:4; }; //! Interface to driver which is able to perform 2d and 3d graphics functions. @@ -336,7 +337,7 @@ namespace video //! Retrieve the given image loader /** \param n The index of the loader to retrieve. This parameter is an 0-based array index. - \return A pointer to the specified loader, 0 if the index is uncorrect. */ + \return A pointer to the specified loader, 0 if the index is incorrect. */ virtual IImageLoader* getImageLoader(u32 n) = 0; //! Retrieve the number of image writers @@ -346,7 +347,7 @@ namespace video //! Retrieve the given image writer /** \param n The index of the writer to retrieve. This parameter is an 0-based array index. - \return A pointer to the specified writer, 0 if the index is uncorrect. */ + \return A pointer to the specified writer, 0 if the index is incorrect. */ virtual IImageWriter* getImageWriter(u32 n) = 0; //! Sets a material. @@ -462,7 +463,7 @@ namespace video //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ - virtual void createOcclusionQuery(scene::ISceneNode* node, + virtual void addOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh=0) =0; //! Remove occlusion query. @@ -512,7 +513,8 @@ namespace video \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.*/ + to release 1.5 and is provided for backwards compatibility only. + This parameter may be removed by Irrlicht 1.9. */ virtual void makeColorKeyTexture(video::ITexture* texture, video::SColor color, bool zeroTexels = false) const =0; @@ -528,7 +530,8 @@ namespace video \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.*/ + to release 1.5 and is provided for backwards compatibility only. + This parameter may be removed by Irrlicht 1.9. */ virtual void makeColorKeyTexture(video::ITexture* texture, core::position2d colorKeyPixelPos, bool zeroTexels = false) const =0; @@ -1211,23 +1214,23 @@ namespace video virtual IImage* createImage(ECOLOR_FORMAT format, const core::dimension2d& size) =0; //! Creates a software image by converting it to given format from another image. - /** \deprecated Create an empty image and use copyTo() + /** \deprecated Create an empty image and use copyTo(). This method may be removed by Irrlicht 1.9. \param format Desired color format of the image. \param imageToCopy Image to copy to the new image. \return The created image. If you no longer need the image, you should call IImage::drop(). See IReferenceCounted::drop() for more information. */ - virtual IImage* createImage(ECOLOR_FORMAT format, IImage *imageToCopy) =0; + _IRR_DEPRECATED_ virtual IImage* createImage(ECOLOR_FORMAT format, IImage *imageToCopy) =0; //! Creates a software image from a part of another image. - /** \deprecated Create an empty image and use copyTo() + /** \deprecated Create an empty image and use copyTo(). This method may be removed by Irrlicht 1.9. \param imageToCopy Image to copy to the new image in part. \param pos Position of rectangle to copy. \param size Extents of rectangle to copy. \return The created image. If you no longer need the image, you should call IImage::drop(). See IReferenceCounted::drop() for more information. */ - virtual IImage* createImage(IImage* imageToCopy, + _IRR_DEPRECATED_ virtual IImage* createImage(IImage* imageToCopy, const core::position2d& pos, const core::dimension2d& size) =0; @@ -1308,9 +1311,12 @@ namespace video renderer names from getMaterialRendererName() to write out the material type name, so they should be set before. \param material The material to serialize. + \param options Additional options which might influence the + serialization. \return The io::IAttributes container holding the material properties. */ - virtual io::IAttributes* createAttributesFromMaterial(const video::SMaterial& material) =0; + virtual io::IAttributes* createAttributesFromMaterial(const video::SMaterial& material, + io::SAttributeReadWriteOptions* options=0) =0; //! Fills an SMaterial structure from attributes. /** Please note that for setting material types of the diff --git a/include/IrrCompileConfig.h b/include/IrrCompileConfig.h index dc054502..9534870d 100644 --- a/include/IrrCompileConfig.h +++ b/include/IrrCompileConfig.h @@ -11,7 +11,7 @@ #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 -beta +#define IRRLICHT_VERSION_SVN -alpha #define IRRLICHT_SDK_VERSION "1.8.0-alpha" #include // TODO: Although included elsewhere this is required at least for mingw @@ -122,6 +122,14 @@ #undef _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ #endif +//! Define _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ if you want to use DirectInput for joystick handling. +/** This only applies to Windows devices, currently only supported under Win32 device. +If not defined, Windows Multimedia library is used, which offers also broad support for joystick devices. */ +#define _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ +#ifdef NO_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ +#undef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ +#endif + //! Maximum number of texture an SMaterial can have, up to 8 are supported by Irrlicht. #define _IRR_MATERIAL_MAX_TEXTURES_ 4 @@ -363,6 +371,13 @@ tool . */ //! Uncomment the following line if you want to ignore the deprecated warnings //#define IGNORE_DEPRECATED_WARNING +//! Define _IRR_COMPILE_WITH_IRR_SCENE_LOADER_ if you want to be able to load +/** .irr scenes using ISceneManager::loadScene */ +#define _IRR_COMPILE_WITH_IRR_SCENE_LOADER_ +#ifdef NO_IRR_COMPILE_WITH_IRR_SCENE_LOADER_ +#undef _IRR_COMPILE_WITH_IRR_SCENE_LOADER_ +#endif + //! Define _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ if you want to use bone based /** animated meshes. If you compile without this, you will be unable to load B3D, MS3D or X meshes */ @@ -474,6 +489,11 @@ B3D, MS3D or X meshes */ #ifdef NO_IRR_COMPILE_WITH_PLY_LOADER_ #undef _IRR_COMPILE_WITH_PLY_LOADER_ #endif +//! Define _IRR_COMPILE_WITH_SMF_LOADER_ if you want to load 3D World Studio mesh files +#define _IRR_COMPILE_WITH_SMF_LOADER_ +#ifdef NO_IRR_COMPILE_WITH_SMF_LOADER_ +#undef _IRR_COMPILE_WITH_SMF_LOADER_ +#endif //! Define _IRR_COMPILE_WITH_IRR_WRITER_ if you want to write static .irrMesh files #define _IRR_COMPILE_WITH_IRR_WRITER_ diff --git a/include/SColor.h b/include/SColor.h index 61cc1596..0291cc34 100644 --- a/include/SColor.h +++ b/include/SColor.h @@ -583,9 +583,10 @@ namespace video }; - //! Class representing a color in HSV format - /** The color values for hue, saturation, value - are stored in a 32 bit floating point variable. + //! Class representing a color in HSL format + /** The color values for hue, saturation, luminance + are stored in 32bit floating point variables. Hue is in range [0,360], + Luminance and Saturation are in percent [0,100] */ class SColorHSL { @@ -593,24 +594,23 @@ namespace video SColorHSL ( f32 h = 0.f, f32 s = 0.f, f32 l = 0.f ) : Hue ( h ), Saturation ( s ), Luminance ( l ) {} - void fromRGB(const SColor &color); - void toRGB(SColor &color) const; + void fromRGB(const SColorf &color); + void toRGB(SColorf &color) const; f32 Hue; f32 Saturation; f32 Luminance; private: - inline u32 toRGB1(f32 rm1, f32 rm2, f32 rh) const; + inline f32 toRGB1(f32 rm1, f32 rm2, f32 rh) const; }; - inline void SColorHSL::fromRGB(const SColor &color) + inline void SColorHSL::fromRGB(const SColorf &color) { - const u32 maxValInt = core::max_(color.getRed(), color.getGreen(), color.getBlue()); - const f32 maxVal = (f32)maxValInt; + const f32 maxVal = core::max_(color.getRed(), color.getGreen(), color.getBlue()); const f32 minVal = (f32)core::min_(color.getRed(), color.getGreen(), color.getBlue()); - Luminance = (maxVal/minVal)*0.5f; + Luminance = (maxVal+minVal)*50; if (core::equals(maxVal, minVal)) { Hue=0.f; @@ -619,7 +619,7 @@ namespace video } const f32 delta = maxVal-minVal; - if ( Luminance <= 0.5f ) + if ( Luminance <= 50 ) { Saturation = (delta)/(maxVal+minVal); } @@ -627,71 +627,70 @@ namespace video { Saturation = (delta)/(2-maxVal-minVal); } + Saturation *= 100; - if (maxValInt == color.getRed()) + if (core::equals(maxVal, color.getRed())) Hue = (color.getGreen()-color.getBlue())/delta; - else if (maxValInt == color.getGreen()) - Hue = 2+(color.getBlue()-color.getRed())/delta; + else if (core::equals(maxVal, color.getGreen())) + Hue = 2+((color.getBlue()-color.getRed())/delta); else // blue is max - Hue = 4+(color.getRed()-color.getGreen())/delta; + Hue = 4+((color.getRed()-color.getGreen())/delta); - Hue *= (60.0f * core::DEGTORAD); + Hue *= 60.0f; while ( Hue < 0.f ) - Hue += 2.f * core::PI; + Hue += 360; } - inline void SColorHSL::toRGB(SColor &color) const + inline void SColorHSL::toRGB(SColorf &color) const { + const f32 l = Luminance/100; if (core::iszero(Saturation)) // grey { - u8 c = (u8) ( Luminance * 255.0 ); - color.setRed(c); - color.setGreen(c); - color.setBlue(c); + color.set(l, l, l); return; } f32 rm2; - if ( Luminance <= 0.5f ) + if ( Luminance <= 50 ) { - rm2 = Luminance + Luminance * Saturation; + rm2 = l + l * (Saturation/100); } else { - rm2 = Luminance + Saturation - Luminance * Saturation; + rm2 = l + (1 - l) * (Saturation/100); } - const f32 rm1 = 2.0f * Luminance - rm2; + const f32 rm1 = 2.0f * l - rm2; - color.setRed ( toRGB1(rm1, rm2, Hue + (120.0f * core::DEGTORAD )) ); - color.setGreen ( toRGB1(rm1, rm2, Hue) ); - color.setBlue ( toRGB1(rm1, rm2, Hue - (120.0f * core::DEGTORAD) ) ); + const f32 h = Hue / 360.0f; + color.set( toRGB1(rm1, rm2, h + 1.f/3.f), + toRGB1(rm1, rm2, h), + toRGB1(rm1, rm2, h - 1.f/3.f) + ); } - inline u32 SColorHSL::toRGB1(f32 rm1, f32 rm2, f32 rh) const + // algorithm from Foley/Van-Dam + inline f32 SColorHSL::toRGB1(f32 rm1, f32 rm2, f32 rh) const { - while ( rh > 2.f * core::PI ) - rh -= 2.f * core::PI; + if (rh<0) + rh += 1; + if (rh>1) + rh -= 1; - while ( rh < 0.f ) - rh += 2.f * core::PI; - - if (rh < 60.0f * core::DEGTORAD ) - rm1 = rm1 + (rm2 - rm1) * rh / (60.0f * core::DEGTORAD); - else if (rh < 180.0f * core::DEGTORAD ) + if (rh < 1.f/6.f) + rm1 = rm1 + (rm2 - rm1) * rh*6.f; + else if (rh < 0.5f) rm1 = rm2; - else if (rh < 240.0f * core::DEGTORAD ) - rm1 = rm1 + (rm2 - rm1) * ( ( 240.0f * core::DEGTORAD ) - rh) / - (60.0f * core::DEGTORAD); + else if (rh < 2.f/3.f) + rm1 = rm1 + (rm2 - rm1) * ((2.f/3.f)-rh)*6.f; - return (u32) core::round32(rm1 * 255.f); + return rm1; } } // end namespace video } // end namespace irr #endif - diff --git a/include/SMaterial.h b/include/SMaterial.h index d1b52ab6..56479c54 100644 --- a/include/SMaterial.h +++ b/include/SMaterial.h @@ -22,19 +22,34 @@ namespace video //! Flag for EMT_ONETEXTURE_BLEND, ( BlendFactor ) BlendFunc = source * sourceFactor + dest * destFactor enum E_BLEND_FACTOR { - EBF_ZERO = 0, //!< src & dest (0, 0, 0, 0) - EBF_ONE, //!< src & dest (1, 1, 1, 1) - EBF_DST_COLOR, //!< src (destR, destG, destB, destA) - EBF_ONE_MINUS_DST_COLOR, //!< src (1-destR, 1-destG, 1-destB, 1-destA) - EBF_SRC_COLOR, //!< dest (srcR, srcG, srcB, srcA) + EBF_ZERO = 0, //!< src & dest (0, 0, 0, 0) + EBF_ONE, //!< src & dest (1, 1, 1, 1) + EBF_DST_COLOR, //!< src (destR, destG, destB, destA) + EBF_ONE_MINUS_DST_COLOR, //!< src (1-destR, 1-destG, 1-destB, 1-destA) + EBF_SRC_COLOR, //!< dest (srcR, srcG, srcB, srcA) EBF_ONE_MINUS_SRC_COLOR, //!< dest (1-srcR, 1-srcG, 1-srcB, 1-srcA) - EBF_SRC_ALPHA, //!< src & dest (srcA, srcA, srcA, srcA) + EBF_SRC_ALPHA, //!< src & dest (srcA, srcA, srcA, srcA) EBF_ONE_MINUS_SRC_ALPHA, //!< src & dest (1-srcA, 1-srcA, 1-srcA, 1-srcA) - EBF_DST_ALPHA, //!< src & dest (destA, destA, destA, destA) + EBF_DST_ALPHA, //!< src & dest (destA, destA, destA, destA) EBF_ONE_MINUS_DST_ALPHA, //!< src & dest (1-destA, 1-destA, 1-destA, 1-destA) EBF_SRC_ALPHA_SATURATE //!< src (min(srcA, 1-destA), idem, ...) }; + //! Values defining the blend operation used when blend is enabled + enum E_BLEND_OPERATION + { + EBO_NONE = 0, //!< No blending happens + EBO_ADD, //!< Default blending adds the color values + EBO_SUBTRACT, //!< This mode subtracts the color values + EBO_REVSUBTRACT,//!< This modes subtracts destination from source + EBO_MIN, //!< Choose minimum value of each color channel + EBO_MAX, //!< Choose maximum value of each color channel + EBO_MIN_FACTOR, //!< Choose minimum value of each color channel after applying blend factors, not widely supported + EBO_MAX_FACTOR, //!< Choose maximum value of each color channel after applying blend factors, not widely supported + EBO_MIN_ALPHA, //!< Choose minimum value of each color channel based on alpha value, not widely supported + EBO_MAX_ALPHA //!< Choose maximum value of each color channel based on alpha value, not widely supported + }; + //! MaterialTypeParam: e.g. DirectX: D3DTOP_MODULATE, D3DTOP_MODULATE2X, D3DTOP_MODULATE4X enum E_MODULATE_FUNC { @@ -182,6 +197,19 @@ namespace video ECM_DIFFUSE_AND_AMBIENT }; + //! Flags for the definition of the polygon offset feature + /** These flags define whether the offset should be into the screen, or towards the eye. */ + enum E_POLYGON_OFFSET + { + //! Push pixel towards the far plane, away from the eye + /** This is typically used for rendering inner areas. */ + EPO_BACK=0, + //! Pull pixels towards the camera. + /** This is typically used for polygons which should appear on top + of other elements, such as decals. */ + EPO_FRONT=1 + }; + //! Maximum number of texture an SMaterial can have. const u32 MATERIAL_MAX_TEXTURES = _IRR_MATERIAL_MAX_TEXTURES_; @@ -195,9 +223,11 @@ namespace video EmissiveColor(0,0,0,0), SpecularColor(255,255,255,255), Shininess(0.0f), MaterialTypeParam(0.0f), MaterialTypeParam2(0.0f), Thickness(1.0f), ZBuffer(ECFN_LESSEQUAL), AntiAliasing(EAAM_SIMPLE), ColorMask(ECP_ALL), - ColorMaterial(ECM_DIFFUSE), - Wireframe(false), PointCloud(false), GouraudShading(true), Lighting(true), ZWriteEnable(true), - BackfaceCulling(true), FrontfaceCulling(false), FogEnable(false), NormalizeNormals(false), UseMipMaps(true) + ColorMaterial(ECM_DIFFUSE), BlendOperation(EBO_NONE), + PolygonOffsetFactor(0), PolygonOffsetDirection(EPO_FRONT), + Wireframe(false), PointCloud(false), GouraudShading(true), + Lighting(true), ZWriteEnable(true), BackfaceCulling(true), FrontfaceCulling(false), + FogEnable(false), NormalizeNormals(false), UseMipMaps(true) { } //! Copy constructor @@ -246,6 +276,9 @@ namespace video AntiAliasing = other.AntiAliasing; ColorMask = other.ColorMask; ColorMaterial = other.ColorMaterial; + BlendOperation = other.BlendOperation; + PolygonOffsetFactor = other.PolygonOffsetFactor; + PolygonOffsetDirection = other.PolygonOffsetDirection; UseMipMaps = other.UseMipMaps; return *this; @@ -344,6 +377,20 @@ namespace video a very similar rendering as with lighting turned off, just with light shading. */ u8 ColorMaterial:3; + //! Store the blend operation of choice + /** Values to be chosen from E_BLEND_OPERATION. The actual way to use this value + is not yet determined, so ignore it for now. */ + E_BLEND_OPERATION BlendOperation:4; + + //! Factor specifying how far the polygon offset should be made + /** Specifying 0 disables the polygon offset. The direction is specified spearately. + The factor can be from 0 to 7.*/ + u8 PolygonOffsetFactor:3; + + //! Flag defining the direction the polygon offset is applied to. + /** Can be to front or to back, specififed by values from E_POLYGON_OFFSET. */ + E_POLYGON_OFFSET PolygonOffsetDirection:1; + //! Draw as wireframe or filled triangles? Default: false /** The user can access a material flag using \code material.Wireframe=true \endcode @@ -489,16 +536,19 @@ namespace video } break; case EMF_ANTI_ALIASING: - AntiAliasing = value?EAAM_SIMPLE:EAAM_OFF; - break; + AntiAliasing = value?EAAM_SIMPLE:EAAM_OFF; break; case EMF_COLOR_MASK: - ColorMask = value?ECP_ALL:ECP_NONE; - break; + ColorMask = value?ECP_ALL:ECP_NONE; break; case EMF_COLOR_MATERIAL: - ColorMaterial = value?ECM_DIFFUSE:ECM_NONE; - break; + ColorMaterial = value?ECM_DIFFUSE:ECM_NONE; break; case EMF_USE_MIP_MAPS: - UseMipMaps = value; + UseMipMaps = value; break; + case EMF_BLEND_OPERATION: + BlendOperation = value?EBO_ADD:EBO_NONE; break; + case EMF_POLYGON_OFFSET: + PolygonOffsetFactor = value?1:0; + PolygonOffsetDirection = EPO_BACK; + break; default: break; } @@ -554,6 +604,10 @@ namespace video return (ColorMaterial != ECM_NONE); case EMF_USE_MIP_MAPS: return UseMipMaps; + case EMF_BLEND_OPERATION: + return BlendOperation != EBO_NONE; + case EMF_POLYGON_OFFSET: + return PolygonOffsetFactor != 0; } return false; @@ -587,6 +641,9 @@ namespace video AntiAliasing != b.AntiAliasing || ColorMask != b.ColorMask || ColorMaterial != b.ColorMaterial || + BlendOperation != b.BlendOperation || + PolygonOffsetFactor != b.PolygonOffsetFactor || + PolygonOffsetDirection != b.PolygonOffsetDirection || UseMipMaps != b.UseMipMaps; for (u32 i=0; (i= '0') && ( *in <= '9' )) { - unsignedValue = ( unsignedValue * 10 ) + ( *in - '0' ); - ++in; - - if(unsignedValue > (u32)INT_MAX) + const u32 tmp = ( unsignedValue * 10 ) + ( *in - '0' ); + if (tmp (u32)INT_MAX) + { + if (negative) + return (s32)INT_MIN; + else + return (s32)INT_MAX; + } else - return (s32)unsignedValue; + { + if (negative) + return -((s32)unsignedValue); + else + return (s32)unsignedValue; + } +} + +//! Convert a hex-encoded character to an unsigned integer. +/** \param[in] in The digit to convert. Only digits 0 to 9 and chars A-F,a-f + will be considered. + \return The unsigned integer value of the digit. 0xffffffff if the input is + not hex +*/ +inline u32 ctoul16(char in) +{ + if (in >= '0' && in <= '9') + return in - '0'; + else if (in >= 'a' && in <= 'f') + return 10u + in - 'a'; + else if (in >= 'A' && in <= 'F') + return 10u + in - 'A'; + else + return 0xffffffff; +} + +//! Convert a simple string of base 16 digits into an unsigned 32 bit integer. +/** \param[in] in: The string of digits to convert. No leading chars are + allowed, only digits 0 to 9 and chars A-F,a-f are allowed. Parsing stops + at the first illegal char. + \param[out] out: (optional) If provided, it will be set to point at the + first character not used in the calculation. + \return The unsigned integer value of the digits. If the string specifies + too many digits to encode in an u32 then INT_MAX will be returned. +*/ +inline u32 strtoul16(const char* in, const char** out=0) +{ + if (!in) + { + if (out) + *out = in; + return 0; + } + + bool overflow=false; + u32 unsignedValue = 0; + while (true) + { + u32 tmp = 0; + if ((*in >= '0') && (*in <= '9')) + tmp = (unsignedValue << 4u) + (*in - '0'); + else if ((*in >= 'A') && (*in <= 'F')) + tmp = (unsignedValue << 4u) + (*in - 'A') + 10; + else if ((*in >= 'a') && (*in <= 'f')) + tmp = (unsignedValue << 4u) + (*in - 'a') + 10; + else + break; + if (tmp= '0') && (*in <= '7')) + tmp = (unsignedValue << 3u) + (*in - '0'); + else + break; + if (tmp= MAX_SAFE_U32_VALUE) + if (intValue >= MAX_SAFE_U32_VALUE) break; - intValue = ( intValue * 10) + ( *in - '0' ); + intValue = (intValue * 10) + (*in - '0'); ++in; } - floatValue = (f32)intValue; + f32 floatValue = (f32)intValue; - // If there are any digits left to parse, then we need to use + // If there are any digits left to parse, then we need to use // floating point arithmetic from here. while ( ( *in >= '0') && ( *in <= '9' ) ) { - floatValue = ( floatValue * 10.f ) + (f32)( *in - '0' ); + floatValue = (floatValue * 10.f) + (f32)(*in - '0'); ++in; - if(floatValue > FLT_MAX) // Just give up. + if (floatValue > FLT_MAX) // Just give up. break; } - if(out) + if (out) *out = in; return floatValue; } //! Provides a fast function for converting a string into a float. -//! This is not guaranteed to be as accurate as atof(), but is -//! approximately 6 to 8 times as fast. -//! \param[in] in: The string to convert. -//! \param[out] out: The resultant float will be written here. -//! \return A pointer to the first character in the string that wasn't -//! use to create the float value. -inline const char* fast_atof_move( const char * in, f32 & out) +/** This is not guaranteed to be as accurate as atof(), but is + approximately 6 to 8 times as fast. + \param[in] in The string to convert. + \param[out] result The resultant float will be written here. + \return Pointer to the first character in the string that wasn't used + to create the float value. +*/ +inline const char* fast_atof_move(const char* in, f32& result) { // Please run this regression test when making any modifications to this function: // https://sourceforge.net/tracker/download.php?group_id=74339&atid=540676&file_id=298968&aid=1865300 - out = 0.f; - if(!in) + result = 0.f; + if (!in) return 0; - bool negative = false; - if(*in == '-') - { - negative = true; - ++in; - } - - f32 value = strtof10 ( in, &in ); - - if (*in == '.') - { + const bool negative = ('-' == *in); + if (negative || ('+'==*in)) ++in; - const char * afterDecimal = in; - f32 decimal = strtof10 ( in, &afterDecimal ); - decimal *= fast_atof_table[afterDecimal - in]; - - value += decimal; + f32 value = strtof10(in, &in); + if ('.' == *in) + { + const char* afterDecimal = ++in; + const f32 decimal = strtof10(in, &afterDecimal); + value += decimal * fast_atof_table[afterDecimal - in]; in = afterDecimal; } @@ -169,24 +329,27 @@ inline const char* fast_atof_move( const char * in, f32 & out) ++in; // Assume that the exponent is a whole number. // strtol10() will deal with both + and - signs, - // but cast to (f32) to prevent overflow at FLT_MAX - value *= (f32)pow(10.0f, (f32)strtol10(in, &in)); + // but calculate as f32 to prevent overflow at FLT_MAX + value *= powf(10.f, (f32)strtol10(in, &in)); } - if(negative) - out = -value; - else - out = value; - + result = negative?-value:value; return in; } //! Convert a string to a floating point number -//! \param floatAsString: The string to convert. -inline float fast_atof(const char* floatAsString) +/** \param floatAsString The string to convert. + \param out Optional pointer to the first character in the string that + wasn't used to create the float value. + \result Float value parsed from the input string +*/ +inline float fast_atof(const char* floatAsString, const char** out=0) { float ret; - fast_atof_move(floatAsString, ret); + if (out) + *out=fast_atof_move(floatAsString, ret); + else + fast_atof_move(floatAsString, ret); return ret; } diff --git a/include/irrMap.h b/include/irrMap.h index e09f316c..d523c7ef 100644 --- a/include/irrMap.h +++ b/include/irrMap.h @@ -737,7 +737,7 @@ class map return Root == 0; } - //! \deprecated Use empty() instead. + //! \deprecated Use empty() instead. This method may be removed by Irrlicht 1.9 _IRR_DEPRECATED_ bool isEmpty() const { return empty(); diff --git a/include/irrlicht.h b/include/irrlicht.h index 9303a930..feaca631 100644 --- a/include/irrlicht.h +++ b/include/irrlicht.h @@ -129,6 +129,7 @@ #include "path.h" #include "irrXML.h" #include "ISceneCollisionManager.h" +#include "ISceneLoader.h" #include "ISceneManager.h" #include "ISceneNode.h" #include "ISceneNodeAnimator.h" diff --git a/include/quaternion.h b/include/quaternion.h index 100c8413..e56b7329 100644 --- a/include/quaternion.h +++ b/include/quaternion.h @@ -120,7 +120,10 @@ class quaternion //! Inverts this quaternion quaternion& makeInverse(); - //! Set this quaternion to the result of the interpolation between two quaternions + //! Set this quaternion to the result of the linear interpolation between two quaternions + quaternion& lerp(quaternion q1, quaternion q2, f32 time); + + //! Set this quaternion to the result of the spherical interpolation between two quaternions quaternion& slerp( quaternion q1, quaternion q2, f32 interpolate ); //! Create quaternion from rotation angle and rotation axis. @@ -490,43 +493,37 @@ inline quaternion& quaternion::normalize() } +// set this quaternion to the result of the linear interpolation between two quaternions +inline quaternion& quaternion::lerp(quaternion q1, quaternion q2, f32 time) +{ + const f32 scale = 1.0f - time; + const f32 invscale = time; + return (*this = (q1*scale) + (q2*invscale)); +} + + // set this quaternion to the result of the interpolation between two quaternions inline quaternion& quaternion::slerp(quaternion q1, quaternion q2, f32 time) { f32 angle = q1.dotProduct(q2); + // make sure we use the short rotation if (angle < 0.0f) { q1 *= -1.0f; angle *= -1.0f; } - f32 scale; - f32 invscale; - - if ((angle + 1.0f) > 0.05f) + if (angle <= 0.95f) // spherical interpolation { - if ((1.0f - angle) >= 0.05f) // spherical interpolation - { - const f32 theta = acosf(angle); - const f32 invsintheta = reciprocal(sinf(theta)); - scale = sinf(theta * (1.0f-time)) * invsintheta; - invscale = sinf(theta * time) * invsintheta; - } - else // linear interploation - { - scale = 1.0f - time; - invscale = time; - } + const f32 theta = acosf(angle); + const f32 invsintheta = reciprocal(sinf(theta)); + const f32 scale = sinf(theta * (1.0f-time)) * invsintheta; + const f32 invscale = sinf(theta * time) * invsintheta; + return (*this = (q1*scale) + (q2*invscale)); } - else - { - q2.set(-q1.Y, q1.X, -q1.W, q1.Z); - scale = sinf(PI * (0.5f - time)); - invscale = sinf(PI * time); - } - - return (*this = (q1*scale) + (q2*invscale)); + else // linear interploation + return lerp(q1,q2,time); } @@ -578,15 +575,35 @@ inline void quaternion::toEuler(vector3df& euler) const const f64 sqx = X*X; const f64 sqy = Y*Y; const f64 sqz = Z*Z; + const f64 test = 2.0 * (Y*W - X*Z); - // heading = rotation about z-axis - euler.Z = (f32) (atan2(2.0 * (X*Y +Z*W),(sqx - sqy - sqz + sqw))); - - // bank = rotation about x-axis - euler.X = (f32) (atan2(2.0 * (Y*Z +X*W),(-sqx - sqy + sqz + sqw))); - - // attitude = rotation about y-axis - euler.Y = asinf( clamp(-2.0f * (X*Z - Y*W), -1.0f, 1.0f) ); + if (core::equals(test, 1.0, 0.000001)) + { + // heading = rotation about z-axis + euler.Z = (f32) (-2.0*atan2(X, W)); + // bank = rotation about x-axis + euler.X = 0; + // attitude = rotation about y-axis + euler.Y = (f32) (core::PI64/2.0); + } + else if (core::equals(test, -1.0, 0.000001)) + { + // heading = rotation about z-axis + euler.Z = (f32) (2.0*atan2(X, W)); + // bank = rotation about x-axis + euler.X = 0; + // attitude = rotation about y-axis + euler.Y = (f32) (core::PI64/-2.0); + } + else + { + // heading = rotation about z-axis + euler.Z = (f32) atan2(2.0 * (X*Y +Z*W),(sqx - sqy - sqz + sqw)); + // bank = rotation about x-axis + euler.X = (f32) atan2(2.0 * (Y*Z +X*W),(-sqx - sqy + sqz + sqw)); + // attitude = rotation about y-axis + euler.Y = (f32) asin( clamp(test, -1.0, 1.0) ); + } } diff --git a/include/vector2d.h b/include/vector2d.h index 8e0b1815..9367e867 100644 --- a/include/vector2d.h +++ b/include/vector2d.h @@ -214,6 +214,8 @@ public: // don't use getLength here to avoid precision loss with s32 vectors f64 tmp = Y / sqrt((f64)(X*X + Y*Y)); + if ( tmp > 1.0 ) // avoid floating-point trouble as sqrt(y*y) is occasionally larger y + tmp = 1.0; tmp = atan( core::squareroot(1 - tmp*tmp) / tmp) * RADTODEG64; if (X>0 && Y>0) @@ -244,6 +246,8 @@ public: tmp = tmp / core::squareroot((f64)((X*X + Y*Y) * (b.X*b.X + b.Y*b.Y))); if (tmp < 0.0) tmp = -tmp; + if ( tmp > 1.0 ) // avoid floating-point trouble + tmp = 1.0; return atan(sqrt(1 - tmp*tmp) / tmp) * RADTODEG64; } diff --git a/readme.txt b/readme.txt index ce42760f..5b830f9e 100644 --- a/readme.txt +++ b/readme.txt @@ -14,7 +14,6 @@ The Irrlicht Engine SDK version 1.8 6. Contact - ========================================================================== 1. Directory Structure Overview ========================================================================== diff --git a/source/Irrlicht/C3DSMeshFileLoader.cpp b/source/Irrlicht/C3DSMeshFileLoader.cpp index 74453b04..3749e26c 100644 --- a/source/Irrlicht/C3DSMeshFileLoader.cpp +++ b/source/Irrlicht/C3DSMeshFileLoader.cpp @@ -134,7 +134,6 @@ C3DSMeshFileLoader::C3DSMeshFileLoader(ISceneManager* smgr, io::IFileSystem* fs) setDebugName("C3DSMeshFileLoader"); #endif - TransformationMatrix.makeIdentity(); if (FileSystem) FileSystem->grab(); } @@ -237,7 +236,7 @@ bool C3DSMeshFileLoader::readPercentageChunk(io::IReadFile* file, ChunkData* chunk, f32& percentage) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load percentage chunk."); + os::Printer::log("Load percentage chunk.", ELL_DEBUG); #endif ChunkData data; @@ -289,7 +288,7 @@ bool C3DSMeshFileLoader::readColorChunk(io::IReadFile* file, ChunkData* chunk, video::SColor& out) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load color chunk."); + os::Printer::log("Load color chunk.", ELL_DEBUG); #endif ChunkData data; readChunkData(file, data); @@ -340,7 +339,7 @@ bool C3DSMeshFileLoader::readColorChunk(io::IReadFile* file, ChunkData* chunk, bool C3DSMeshFileLoader::readMaterialChunk(io::IReadFile* file, ChunkData* parent) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load material chunk."); + os::Printer::log("Load material chunk.", ELL_DEBUG); #endif u16 matSection=0; @@ -551,7 +550,7 @@ bool C3DSMeshFileLoader::readTrackChunk(io::IReadFile* file, ChunkData& data, IMeshBuffer* mb, const core::vector3df& pivot) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load track chunk."); + os::Printer::log("Load track chunk.", ELL_DEBUG); #endif u16 flags; u32 flags2; @@ -634,7 +633,7 @@ bool C3DSMeshFileLoader::readTrackChunk(io::IReadFile* file, ChunkData& data, bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load frame chunk."); + os::Printer::log("Load frame chunk.", ELL_DEBUG); #endif ChunkData data; @@ -645,7 +644,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) else { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load keyframe header."); + os::Printer::log("Load keyframe header.", ELL_DEBUG); #endif u16 version; file->read(&version, 2); @@ -676,7 +675,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) case C3DS_OBJECT_TAG: { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load object tag."); + os::Printer::log("Load object tag.", ELL_DEBUG); #endif mb=0; pivot.set(0.0f, 0.0f, 0.0f); @@ -685,7 +684,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) case C3DS_KF_SEG: { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load keyframe segment."); + os::Printer::log("Load keyframe segment.", ELL_DEBUG); #endif u32 flags; file->read(&flags, 4); @@ -702,7 +701,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) case C3DS_KF_NODE_HDR: { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load keyframe node header."); + os::Printer::log("Load keyframe node header.", ELL_DEBUG); #endif s16 flags; c8* c = new c8[data.header.length - data.read-6]; @@ -737,7 +736,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) case C3DS_KF_CURTIME: { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load keyframe current time."); + os::Printer::log("Load keyframe current time.", ELL_DEBUG); #endif u32 flags; file->read(&flags, 4); @@ -750,7 +749,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) case C3DS_NODE_ID: { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load node ID."); + os::Printer::log("Load node ID.", ELL_DEBUG); #endif u16 flags; file->read(&flags, 2); @@ -763,7 +762,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) case C3DS_PIVOTPOINT: { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load pivot point."); + os::Printer::log("Load pivot point.", ELL_DEBUG); #endif file->read(&pivot.X, sizeof(f32)); file->read(&pivot.Y, sizeof(f32)); @@ -779,7 +778,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) case C3DS_BOUNDBOX: { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load bounding box."); + os::Printer::log("Load bounding box.", ELL_DEBUG); #endif core::aabbox3df bbox; // abuse bboxCenter as temporary variable @@ -808,7 +807,7 @@ bool C3DSMeshFileLoader::readFrameChunk(io::IReadFile* file, ChunkData* parent) case C3DS_MORPH_SMOOTH: { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load morph smooth."); + os::Printer::log("Load morph smooth.", ELL_DEBUG); #endif f32 flag; file->read(&flag, 4); @@ -903,7 +902,7 @@ bool C3DSMeshFileLoader::readChunk(io::IReadFile* file, ChunkData* parent) bool C3DSMeshFileLoader::readObjectChunk(io::IReadFile* file, ChunkData* parent) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load object chunk."); + os::Printer::log("Load object chunk.", ELL_DEBUG); #endif while(parent->read < parent->header.length) { @@ -1004,6 +1003,9 @@ bool C3DSMeshFileLoader::readObjectChunk(io::IReadFile* file, ChunkData* parent) void C3DSMeshFileLoader::composeObject(io::IReadFile* file, const core::stringc& name) { +#ifdef _IRR_DEBUG_3DS_LOADER_ + os::Printer::log("Compose object.", ELL_DEBUG); +#endif if (Mesh->getMeshBufferCount() != Materials.size()) loadMaterials(file); @@ -1254,7 +1256,7 @@ void C3DSMeshFileLoader::cleanUp() void C3DSMeshFileLoader::readTextureCoords(io::IReadFile* file, ChunkData& data) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load texture coords."); + os::Printer::log("Load texture coords.", ELL_DEBUG); #endif file->read(&CountTCoords, sizeof(CountTCoords)); #ifdef __BIG_ENDIAN__ @@ -1282,7 +1284,7 @@ void C3DSMeshFileLoader::readTextureCoords(io::IReadFile* file, ChunkData& data) void C3DSMeshFileLoader::readMaterialGroup(io::IReadFile* file, ChunkData& data) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load material group."); + os::Printer::log("Load material group.", ELL_DEBUG); #endif SMaterialGroup group; @@ -1310,7 +1312,7 @@ void C3DSMeshFileLoader::readMaterialGroup(io::IReadFile* file, ChunkData& data) void C3DSMeshFileLoader::readIndices(io::IReadFile* file, ChunkData& data) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load indices."); + os::Printer::log("Load indices.", ELL_DEBUG); #endif file->read(&CountFaces, sizeof(CountFaces)); #ifdef __BIG_ENDIAN__ @@ -1335,7 +1337,7 @@ void C3DSMeshFileLoader::readIndices(io::IReadFile* file, ChunkData& data) void C3DSMeshFileLoader::readVertices(io::IReadFile* file, ChunkData& data) { #ifdef _IRR_DEBUG_3DS_LOADER_ - os::Printer::log("Load vertices."); + os::Printer::log("Load vertices.", ELL_DEBUG); #endif file->read(&CountVertices, sizeof(CountVertices)); #ifdef __BIG_ENDIAN__ diff --git a/source/Irrlicht/CAnimatedMeshHalfLife.cpp b/source/Irrlicht/CAnimatedMeshHalfLife.cpp index 0b81b3ac..91cff72d 100644 --- a/source/Irrlicht/CAnimatedMeshHalfLife.cpp +++ b/source/Irrlicht/CAnimatedMeshHalfLife.cpp @@ -21,19 +21,19 @@ namespace scene using namespace video; - void AngleQuaternion( const vec3_hl angles, vec4_hl quaternion ) + void AngleQuaternion(const core::vector3df& angles, vec4_hl quaternion) { f32 angle; f32 sr, sp, sy, cr, cp, cy; // FIXME: rescale the inputs to 1/2 angle - angle = angles[2] * 0.5f; + angle = angles.Z * 0.5f; sy = sin(angle); cy = cos(angle); - angle = angles[1] * 0.5f; + angle = angles.Y * 0.5f; sp = sin(angle); cp = cos(angle); - angle = angles[0] * 0.5f; + angle = angles.X * 0.5f; sr = sin(angle); cr = cos(angle); @@ -122,40 +122,17 @@ namespace scene out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; } - #define EQUAL_EPSILON 0.001 - s32 VectorCompare (vec3_hl v1, vec3_hl v2) - { - s32 i; - - for (i=0 ; i<3 ; i++) - if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON) - return false; - - return true; - } - - #define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]) - - inline void VectorTransform (const vec3_hl in1, const f32 in2[3][4], vec3_hl out) - { - out[0] = DotProduct(in1, in2[0]) + in2[0][3]; - out[1] = DotProduct(in1, in2[1]) + in2[1][3]; - out[2] = DotProduct(in1, in2[2]) + in2[2][3]; - } - - inline void VectorTransform2 (core::vector3df &out, const vec3_hl in1, const f32 in2[3][4]) + inline void VectorTransform(const vec3_hl in1, const f32 in2[3][4], core::vector3df& out) { out.X = DotProduct(in1, in2[0]) + in2[0][3]; - out.Z = DotProduct(in1, in2[1]) + in2[1][3]; - out.Y = DotProduct(in1, in2[2]) + in2[2][3]; + out.Y = DotProduct(in1, in2[1]) + in2[1][3]; + out.Z = DotProduct(in1, in2[2]) + in2[2][3]; } - static f32 BoneTransform[MAXSTUDIOBONES][3][4]; // bone transformation matrix - void getBoneVector ( core::vector3df &out, u32 index ) { out.X = BoneTransform[index][0][3]; @@ -185,12 +162,12 @@ namespace scene //! Constructor -CHalflifeMDLMeshFileLoader::CHalflifeMDLMeshFileLoader( scene::ISceneManager* smgr ) +CHalflifeMDLMeshFileLoader::CHalflifeMDLMeshFileLoader( + scene::ISceneManager* smgr) : SceneManager(smgr) { #ifdef _DEBUG setDebugName("CHalflifeMDLMeshFileLoader"); #endif - SceneManager = smgr; } @@ -198,7 +175,7 @@ CHalflifeMDLMeshFileLoader::CHalflifeMDLMeshFileLoader( scene::ISceneManager* sm //! based on the file extension (e.g. ".bsp") bool CHalflifeMDLMeshFileLoader::isALoadableFileExtension(const io::path& filename) const { - return core::hasFileExtension ( filename, "mdl" ); + return core::hasFileExtension(filename, "mdl"); } @@ -211,55 +188,57 @@ IAnimatedMesh* CHalflifeMDLMeshFileLoader::createMesh(io::IReadFile* file) CAnimatedMeshHalfLife* msh = new CAnimatedMeshHalfLife(); if (msh) { - if ( msh->loadModelFile ( file, SceneManager ) ) + if (msh->loadModelFile(file, SceneManager)) return msh; - msh->drop(); } return 0; } + //! Constructor CAnimatedMeshHalfLife::CAnimatedMeshHalfLife() { #ifdef _DEBUG setDebugName("CAnimatedMeshHalfLife"); #endif - - initData (); + initData(); } /*! loads a complete model */ -bool CAnimatedMeshHalfLife::loadModelFile( io::IReadFile* file,ISceneManager * smgr ) +bool CAnimatedMeshHalfLife::loadModelFile(io::IReadFile* file, + ISceneManager* smgr) { if (!file) return false; - bool r = false; - SceneManager = smgr; - if ( loadModel ( file, file->getFileName() ) ) + if ( loadModel(file, file->getFileName()) ) { if ( postLoadModel ( file->getFileName() ) ) { initModel (); //dumpModelInfo ( 1 ); - r = true; + return true; } } - return r; + return false; } - //! Destructor CAnimatedMeshHalfLife::~CAnimatedMeshHalfLife() { - freeModel (); + delete [] (u8*) Header; + if (OwnTexModel) + delete [] (u8*) TextureHeader; + + for (u32 i = 0; i < 32; ++i) + delete [] (u8*) AnimationHeader[i]; } @@ -282,15 +261,13 @@ void CAnimatedMeshHalfLife::setDirty(E_BUFFER_TYPE buffer) } - - -static vec3_hl TransformedVerts[MAXSTUDIOVERTS]; // transformed vertices -static vec3_hl TransformedNormals[MAXSTUDIOVERTS]; // light surface normals +static core::vector3df TransformedVerts[MAXSTUDIOVERTS]; // transformed vertices +//static core::vector3df TransformedNormals[MAXSTUDIOVERTS]; // light surface normals /*! */ -void CAnimatedMeshHalfLife::initModel () +void CAnimatedMeshHalfLife::initModel() { // init Sequences to Animation KeyFrameInterpolation ipol; @@ -337,7 +314,7 @@ void CAnimatedMeshHalfLife::initModel () u32 meshBuffer = 0; BodyList.clear(); SHalflifeBody *body = (SHalflifeBody *) ((u8*) Header + Header->bodypartindex); - for ( i = 0; i < Header->numbodyparts; ++i) + for (i=0; i < Header->numbodyparts; ++i) { BodyPart part; part.name = body[i].name; @@ -360,38 +337,32 @@ void CAnimatedMeshHalfLife::initModel () SequenceIndex = 0; CurrentFrame = 0.f; - SetController (0, 0.f); - SetController (1, 0.f); - SetController (2, 0.f); - SetController (3, 0.f); - SetController (MOUTH_CONTROLLER, 0.f); + SetController(0, 0.f); + SetController(1, 0.f); + SetController(2, 0.f); + SetController(3, 0.f); + SetController(MOUTH_CONTROLLER, 0.f); SetSkin (0); // init Meshbuffers - io::path store; - const SHalflifeTexture *tex = (SHalflifeTexture *) ((u8*) TextureHeader + TextureHeader->textureindex); const u16 *skinref = (u16 *)((u8*)TextureHeader + TextureHeader->skinindex); - if (SkinGroupSelection != 0 && SkinGroupSelection < TextureHeader->numskinfamilies) + if ((SkinGroupSelection != 0) && (SkinGroupSelection < TextureHeader->numskinfamilies)) skinref += (SkinGroupSelection * TextureHeader->numskinref); - io::path fname; - io::path ext; - - core::vector2df tex_scale; core::vector2di tex_trans ( 0, 0 ); #ifdef HL_TEXTURE_ATLAS - TextureAtlas.getScale ( tex_scale ); + TextureAtlas.getScale(tex_scale); #endif - for ( u32 bodypart=0 ; bodypart < Header->numbodyparts ; ++bodypart) + for (u32 bodypart=0 ; bodypart < Header->numbodyparts ; ++bodypart) { const SHalflifeBody *body = (SHalflifeBody *)((u8*) Header + Header->bodypartindex) + bodypart; - for ( u32 modelnr = 0; modelnr < body->nummodels; ++modelnr ) + for (u32 modelnr = 0; modelnr < body->nummodels; ++modelnr) { const SHalflifeModel *model = (SHalflifeModel *)((u8*) Header + body->modelindex) + modelnr; #if 0 @@ -522,10 +493,12 @@ void CAnimatedMeshHalfLife::initModel () } #ifdef HL_TEXTURE_ATLAS - store = TextureBaseName + "atlas"; + io::path store = TextureBaseName + "atlas"; #else + io::path fname; + io::path ext; core::splitFilename ( currentex->name, 0, &fname, &ext ); - store = TextureBaseName + fname; + io::path store = TextureBaseName + fname; #endif m.TextureLayer[0].Texture = SceneManager->getVideoDriver()->getTexture ( store ); m.Lighting = false; @@ -535,7 +508,6 @@ void CAnimatedMeshHalfLife::initModel () } // mesh } // model } // body part - } @@ -562,16 +534,16 @@ void CAnimatedMeshHalfLife::buildVertices () const SHalflifeModel *model = (SHalflifeModel *)((u8*) Header + body->modelindex) + modelnr; const u8 *vertbone = ((u8*)Header + model->vertinfoindex); - const u8 *normbone = ((u8*)Header + model->norminfoindex); const vec3_hl *studioverts = (vec3_hl *)((u8*)Header + model->vertindex); - const vec3_hl *studionorms = (vec3_hl *)((u8*)Header + model->normindex); for ( i = 0; i < model->numverts; i++) { VectorTransform ( studioverts[i], BoneTransform[vertbone[i]], TransformedVerts[i] ); } /* + const u8 *normbone = ((u8*)Header + model->norminfoindex); + const vec3_hl *studionorms = (vec3_hl *)((u8*)Header + model->normindex); for ( i = 0; i < model->numnorms; i++) { VectorTransform ( studionorms[i], BoneTransform[normbone[i]], TransformedNormals[i] ); @@ -593,15 +565,11 @@ void CAnimatedMeshHalfLife::buildVertices () for ( g = 0; g < c; ++g, v += 1, tricmd += 4 ) { // fill vertex - const f32 *av = TransformedVerts[tricmd[0]]; - v->Pos.X = av[0]; - v->Pos.Z = av[1]; - v->Pos.Y = av[2]; + const core::vector3df& av = TransformedVerts[tricmd[0]]; + v->Pos = av; /* - av = TransformedNormals[tricmd[1]]; - v->Normal.X = av[0]; - v->Normal.Z = av[1]; - v->Normal.Y = av[2]; + const core::vector3df& an = TransformedNormals[tricmd[1]]; + v->Normal = an; //v->Normal.normalize(); */ } @@ -612,18 +580,17 @@ void CAnimatedMeshHalfLife::buildVertices () } - /*! render Bones */ -void CAnimatedMeshHalfLife::renderModel ( u32 param, IVideoDriver * driver, const core::matrix4 &absoluteTransformation) +void CAnimatedMeshHalfLife::renderModel(u32 param, IVideoDriver * driver, const core::matrix4 &absoluteTransformation) { SHalflifeBone *bone = (SHalflifeBone *) ((u8 *) Header + Header->boneindex); - video::SColor blue ( 0xFF000080 ); - video::SColor red ( 0xFF800000 ); - video::SColor yellow ( 0xFF808000 ); - video::SColor cyan ( 0xFF008080 ); + video::SColor blue(0xFF000080); + video::SColor red(0xFF800000); + video::SColor yellow(0xFF808000); + video::SColor cyan(0xFF008080); core::aabbox3df box; @@ -654,7 +621,7 @@ void CAnimatedMeshHalfLife::renderModel ( u32 param, IVideoDriver * driver, con } // attachements - SHalfelifeAttachment *attach = (SHalfelifeAttachment *) ((u8*) Header + Header->attachmentindex); + SHalflifeAttachment *attach = (SHalflifeAttachment *) ((u8*) Header + Header->attachmentindex); core::vector3df v[8]; for ( i = 0; i < Header->numattachments; i++) { @@ -729,20 +696,19 @@ void CAnimatedMeshHalfLife::renderModel ( u32 param, IVideoDriver * driver, con } } + //! Returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. IMesh* CAnimatedMeshHalfLife::getMesh(s32 frameInt, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) { f32 frame = frameInt + (detailLevel * 0.001f); u32 frameA = core::floor32 ( frame ); - f32 blend = core::fract ( frame ); - - u32 i; +// f32 blend = core::fract ( frame ); SHalflifeSequence *seq = (SHalflifeSequence*) ((u8*) Header + Header->seqindex); // find SequenceIndex from summed list u32 frameCount = 0; - for ( i = 0; i < Header->numseq; i++) + for (u32 i = 0; i < Header->numseq; ++i) { u32 val = core::max_ ( 1, seq[i].numframes - 1 ); if ( frameCount + val > frameA ) @@ -771,6 +737,7 @@ IMesh* CAnimatedMeshHalfLife::getMesh(s32 frameInt, s32 detailLevel, s32 startFr return &MeshIPol; } + /*! */ void CAnimatedMeshHalfLife::initData () @@ -805,19 +772,6 @@ void CAnimatedMeshHalfLife::initData () #endif } -/*! -*/ -void CAnimatedMeshHalfLife::freeModel () -{ - delete [] (u8*) Header; - if (OwnTexModel ) - delete [] (u8*) TextureHeader; - - for ( u32 i = 0; i < 32; ++i ) - delete [] (u8*) AnimationHeader[i]; -} - - /*! */ @@ -834,6 +788,7 @@ void STextureAtlas::release () Master = 0; } + /*! */ void STextureAtlas::addSource ( const c8 * name, video::IImage * image ) @@ -848,6 +803,7 @@ void STextureAtlas::addSource ( const c8 * name, video::IImage * image ) atlas.push_back ( entry ); } + /*! */ void STextureAtlas::getScale(core::vector2df& scale) @@ -865,12 +821,11 @@ void STextureAtlas::getScale(core::vector2df& scale) scale.Y = 1.f; } + /*! */ -void STextureAtlas::getTranslation ( const c8 * name, core::vector2di &pos ) +void STextureAtlas::getTranslation(const c8* name, core::vector2di& pos) { - u32 i = 0; - for ( u32 i = 0; i < atlas.size(); ++i) { if ( atlas[i].name == name ) @@ -881,9 +836,10 @@ void STextureAtlas::getTranslation ( const c8 * name, core::vector2di &pos ) } } + /*! */ -void STextureAtlas::create ( u32 border, E_TEXTURE_CLAMP texmode) +void STextureAtlas::create(u32 border, E_TEXTURE_CLAMP texmode) { u32 i = 0; u32 w = 0; @@ -955,17 +911,16 @@ void STextureAtlas::create ( u32 border, E_TEXTURE_CLAMP texmode) // build image core::dimension2d dim = core::dimension2d( wsum, hsum ).getOptimalSize(); IImage* master = new CImage( format, dim ); - master->fill ( 0 ); - + master->fill(0); video::SColor col[2]; static const u8 wrap[][4] = { - {1, 0 }, // ETC_REPEAT - {0, 1 }, // ETC_CLAMP - {0, 1 }, // ETC_CLAMP_TO_EDGE - {0, 1 } // ETC_MIRROR + {1, 0}, // ETC_REPEAT + {0, 1}, // ETC_CLAMP + {0, 1}, // ETC_CLAMP_TO_EDGE + {0, 1} // ETC_MIRROR }; s32 a,b; @@ -1004,26 +959,28 @@ void STextureAtlas::create ( u32 border, E_TEXTURE_CLAMP texmode) /*! */ -SHalflifeHeader * CAnimatedMeshHalfLife::loadModel( io::IReadFile* file, const io::path &filename ) +SHalflifeHeader* CAnimatedMeshHalfLife::loadModel(io::IReadFile* file, const io::path& filename) { bool closefile = false; + // if secondary files are needed, open here and mark for closing if ( 0 == file ) { - file = SceneManager->getFileSystem()->createAndOpenFile ( filename ); + file = SceneManager->getFileSystem()->createAndOpenFile(filename); closefile = true; } if ( 0 == file ) return 0; - u8 * pin = new u8 [ file->getSize() ]; - file->read ( pin, file->getSize() ); + // read into memory + u8* pin = new u8[file->getSize()]; + file->read(pin, file->getSize()); - SHalflifeHeader * header = (SHalflifeHeader*) pin; + SHalflifeHeader* header = (SHalflifeHeader*) pin; - const bool idst = 0 == strncmp ( header->id, "IDST", 4); - const bool idsq = 0 == strncmp ( header->id, "IDSQ", 4); + const bool idst = (0 == strncmp(header->id, "IDST", 4)); + const bool idsq = (0 == strncmp(header->id, "IDSQ", 4)); if ( (!idst && !idsq) || (idsq && !Header) ) { @@ -1034,25 +991,22 @@ SHalflifeHeader * CAnimatedMeshHalfLife::loadModel( io::IReadFile* file, const i file = 0; } delete [] pin; - return false; + return 0; } // don't know the real header.. idsg might be different if (header->textureindex && idst ) { + io::path path; io::path fname; io::path ext; - io::path path; - io::path store; - core::splitFilename ( file->getFileName(), &path, &fname, &ext ); + core::splitFilename(file->getFileName(), &path, &fname, &ext); TextureBaseName = path + fname + "_"; SHalflifeTexture *tex = (SHalflifeTexture *)(pin + header->textureindex); - u32 i; - u32 *palette = new u32[256]; - for (i = 0; i < header->numtextures; i++) + for (u32 i = 0; i < header->numtextures; ++i) { const u8 *src = pin + tex[i].index; @@ -1066,25 +1020,16 @@ SHalflifeHeader * CAnimatedMeshHalfLife::loadModel( io::IReadFile* file, const i } } - IImage* image = new CImage( ECF_R8G8B8, core::dimension2d ( tex[i].width, tex[i].height ) ); + IImage* image = new CImage( ECF_R8G8B8, core::dimension2d(tex[i].width, tex[i].height) ); CColorConverter::convert8BitTo24Bit(src, (u8*)image->lock(), tex[i].width, tex[i].height, (u8*) palette, 0, false); image->unlock(); - -#if 0 - core::splitFilename ( tex[i].name, 0, &fname, 0 ); - io::path store = io::path ( "c:/h2/convert/" ) + fname + ".bmp"; - SceneManager->getVideoDriver()->writeImageToFile ( image, store ); -#endif - #ifdef HL_TEXTURE_ATLAS TextureAtlas.addSource ( tex[i].name, image ); #else core::splitFilename ( tex[i].name, 0, &fname, &ext ); - store = TextureBaseName + fname; - - SceneManager->getVideoDriver()->addTexture ( store, image ); + SceneManager->getVideoDriver()->addTexture ( TextureBaseName + fname, image ); image->drop(); #endif } @@ -1092,16 +1037,8 @@ SHalflifeHeader * CAnimatedMeshHalfLife::loadModel( io::IReadFile* file, const i #ifdef HL_TEXTURE_ATLAS TextureAtlas.create ( 2 * 2 + 1, ETC_CLAMP ); - - store = TextureBaseName + "atlas"; - SceneManager->getVideoDriver()->addTexture ( store, TextureAtlas.Master ); - - #if 0 - core::splitFilename ( store, 0, &fname, 0 ); - store = io::path ( "c:/h2/convert/" ) + fname + ".bmp"; - SceneManager->getVideoDriver()->writeImageToFile ( TextureAtlas.Master, store ); - #endif - TextureAtlas.release (); + SceneManager->getVideoDriver()->addTexture ( TextureBaseName + "atlas", TextureAtlas.Master ); + TextureAtlas.release(); #endif } @@ -1174,7 +1111,6 @@ f32 CAnimatedMeshHalfLife::SetController( s32 controllerIndex, f32 value ) } - /*! */ u32 CAnimatedMeshHalfLife::SetSkin( u32 value ) @@ -1185,6 +1121,7 @@ u32 CAnimatedMeshHalfLife::SetSkin( u32 value ) return SkinGroupSelection; } + /*! */ bool CAnimatedMeshHalfLife::postLoadModel( const io::path &filename ) @@ -1196,10 +1133,11 @@ bool CAnimatedMeshHalfLife::postLoadModel( const io::path &filename ) core::splitFilename ( filename ,&path, &texname, 0 ); // preload textures + // if no textures are stored in main file, use texfile if (Header->numtextures == 0) { submodel = path + texname + "T.mdl"; - TextureHeader = loadModel( 0, submodel ); + TextureHeader = loadModel(0, submodel); if (!TextureHeader) return false; OwnTexModel = true; @@ -1210,18 +1148,16 @@ bool CAnimatedMeshHalfLife::postLoadModel( const io::path &filename ) OwnTexModel = false; } - u32 i; - // preload animations if (Header->numseqgroups > 1) { c8 seq[8]; - for ( i = 1; i < Header->numseqgroups; i++) + for (u32 i = 1; i < Header->numseqgroups; i++) { snprintf( seq, 8, "%02d.mdl", i ); submodel = path + texname + seq; - AnimationHeader[i] = loadModel( 0, submodel ); + AnimationHeader[i] = loadModel(0, submodel); if (!AnimationHeader[i]) return false; } @@ -1231,7 +1167,6 @@ bool CAnimatedMeshHalfLife::postLoadModel( const io::path &filename ) } - /*! */ void CAnimatedMeshHalfLife::dumpModelInfo ( u32 level ) @@ -1268,7 +1203,6 @@ void CAnimatedMeshHalfLife::dumpModelInfo ( u32 level ) return; } - printf("id: %c%c%c%c\n", phdr[0], phdr[1], phdr[2], phdr[3]); printf("version: %d\n", hdr->version); printf("name: \"%s\"\n", hdr->name); @@ -1351,7 +1285,7 @@ void CAnimatedMeshHalfLife::dumpModelInfo ( u32 level ) printf("\nnumattachments: %d\n", hdr->numattachments); for (i = 0; i < hdr->numattachments; i++) { - SHalfelifeAttachment *attach = (SHalfelifeAttachment *) ((u8*) hdr + hdr->attachmentindex); + SHalflifeAttachment *attach = (SHalflifeAttachment *) ((u8*) hdr + hdr->attachmentindex); printf("attachment %d.name: \"%s\"\n", i + 1, attach[i].name); } @@ -1370,6 +1304,7 @@ void CAnimatedMeshHalfLife::dumpModelInfo ( u32 level ) } } + /*! */ void CAnimatedMeshHalfLife::ExtractBbox( s32 sequence, core::aabbox3df &box ) @@ -1386,23 +1321,19 @@ void CAnimatedMeshHalfLife::ExtractBbox( s32 sequence, core::aabbox3df &box ) } - /*! */ void CAnimatedMeshHalfLife::calcBoneAdj() { - u32 j; - s32 i; - f32 value; - SHalflifeBoneController *bonecontroller; + SHalflifeBoneController *bonecontroller = + (SHalflifeBoneController *)((u8*) Header + Header->bonecontrollerindex); - bonecontroller = (SHalflifeBoneController *)((u8*) Header + Header->bonecontrollerindex); - - for (j = 0; j < Header->numbonecontrollers; j++) + for (u32 j = 0; j < Header->numbonecontrollers; j++) { - i = bonecontroller[j].index; + s32 i = bonecontroller[j].index; f32 range = i <= 3 ? 255.f : 64.f; // check for 360% wrapping + f32 value; if (bonecontroller[j].type & STUDIO_RLOOP) { value = BoneController[i] * (360.f/256.f) + bonecontroller[j].start; @@ -1431,98 +1362,80 @@ void CAnimatedMeshHalfLife::calcBoneAdj() } } + /*! */ -void CAnimatedMeshHalfLife::calcBoneQuaternion( s32 frame, f32 s, SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *q ) const +void CAnimatedMeshHalfLife::calcBoneQuaternion(const s32 frame, const SHalflifeBone * const bone, + SHalflifeAnimOffset *anim, const u32 j, f32& angle1, f32& angle2) const { - s32 j, k; - vec4_hl q1, q2; - vec3_hl angle1, angle2; - SHalfelifeAnimationFrame *animvalue; - - for (j = 0; j < 3; j++) + // three vector components + if (anim->offset[j+3] == 0) { - if (anim->offset[j+3] == 0) - { - angle2[j] = angle1[j] = bone->value[j+3]; // default; - } - else - { - animvalue = (SHalfelifeAnimationFrame *)((u8*)anim + anim->offset[j+3]); - k = frame; - while (animvalue->num.total <= k) - { - k -= animvalue->num.total; - animvalue += animvalue->num.valid + 1; - } - // Bah, missing blend! - if (animvalue->num.valid > k) - { - angle1[j] = animvalue[k+1].value; - - if (animvalue->num.valid > k + 1) - { - angle2[j] = animvalue[k+2].value; - } - else - { - if (animvalue->num.total > k + 1) - angle2[j] = angle1[j]; - else - angle2[j] = animvalue[animvalue->num.valid+2].value; - } - } - else - { - angle1[j] = animvalue[animvalue->num.valid].value; - if (animvalue->num.total > k + 1) - { - angle2[j] = angle1[j]; - } - else - { - angle2[j] = animvalue[animvalue->num.valid + 2].value; - } - } - angle1[j] = bone->value[j+3] + angle1[j] * bone->scale[j+3]; - angle2[j] = bone->value[j+3] + angle2[j] * bone->scale[j+3]; - } - - if (bone->bonecontroller[j+3] != -1) - { - angle1[j] += BoneAdj[bone->bonecontroller[j+3]]; - angle2[j] += BoneAdj[bone->bonecontroller[j+3]]; - } - } - - if (!VectorCompare( angle1, angle2 )) - { - AngleQuaternion( angle1, q1 ); - AngleQuaternion( angle2, q2 ); - QuaternionSlerp( q1, q2, s, q ); + angle2 = angle1 = bone->value[j+3]; // default } else { - AngleQuaternion( angle1, q ); + SHalflifeAnimationFrame *animvalue = (SHalflifeAnimationFrame *)((u8*)anim + anim->offset[j+3]); + s32 k = frame; + while (animvalue->num.total <= k) + { + k -= animvalue->num.total; + animvalue += animvalue->num.valid + 1; + } + // Bah, missing blend! + if (animvalue->num.valid > k) + { + angle1 = animvalue[k+1].value; + + if (animvalue->num.valid > k + 1) + { + angle2 = animvalue[k+2].value; + } + else + { + if (animvalue->num.total > k + 1) + angle2 = angle1; + else + angle2 = animvalue[animvalue->num.valid+2].value; + } + } + else + { + angle1 = animvalue[animvalue->num.valid].value; + if (animvalue->num.total > k + 1) + { + angle2 = angle1; + } + else + { + angle2 = animvalue[animvalue->num.valid + 2].value; + } + } + angle1 = bone->value[j+3] + angle1 * bone->scale[j+3]; + angle2 = bone->value[j+3] + angle2 * bone->scale[j+3]; + } + + if (bone->bonecontroller[j+3] != -1) + { + angle1 += BoneAdj[bone->bonecontroller[j+3]]; + angle2 += BoneAdj[bone->bonecontroller[j+3]]; } } /*! */ -void CAnimatedMeshHalfLife::calcBonePosition( s32 frame, f32 s, SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *pos ) const +void CAnimatedMeshHalfLife::calcBonePosition(const s32 frame, f32 s, + const SHalflifeBone * const bone, SHalflifeAnimOffset *anim, f32 *pos) const { - s32 j, k; - SHalfelifeAnimationFrame *animvalue; - - for (j = 0; j < 3; j++) + for (s32 j = 0; j < 3; ++j) { pos[j] = bone->value[j]; // default; if (anim->offset[j] != 0) { - animvalue = (SHalfelifeAnimationFrame *)((u8*)anim + anim->offset[j]); + SHalflifeAnimationFrame *animvalue = (SHalflifeAnimationFrame *)((u8*)anim + anim->offset[j]); - k = frame; + s32 k = frame; // find span of values that includes the frame we want while (animvalue->num.total <= k) { @@ -1562,25 +1475,39 @@ void CAnimatedMeshHalfLife::calcBonePosition( s32 frame, f32 s, SHalflifeBone *b } } + /*! */ -void CAnimatedMeshHalfLife::calcRotations ( vec3_hl *pos, vec4_hl *q, SHalflifeSequence *seq, SHalflifeAnimOffset *anim, f32 f ) +void CAnimatedMeshHalfLife::calcRotations(vec3_hl *pos, vec4_hl *q, + SHalflifeSequence *seq, SHalflifeAnimOffset *anim, f32 f) { - s32 frame; - SHalflifeBone *bone; - f32 s; - - frame = (s32)f; - s = (f - frame); + s32 frame = (s32)f; + f32 s = (f - frame); // add in programatic controllers - calcBoneAdj( ); + calcBoneAdj(); - bone = (SHalflifeBone *)((u8 *)Header + Header->boneindex); + SHalflifeBone *bone = (SHalflifeBone *)((u8 *)Header + Header->boneindex); for ( u32 i = 0; i < Header->numbones; i++, bone++, anim++) { - calcBoneQuaternion( frame, s, bone, anim, q[i] ); - calcBonePosition( frame, s, bone, anim, pos[i] ); + core::vector3df angle1, angle2; + calcBoneQuaternion(frame, bone, anim, 0, angle1.X, angle2.X); + calcBoneQuaternion(frame, bone, anim, 1, angle1.Y, angle2.Y); + calcBoneQuaternion(frame, bone, anim, 2, angle1.Z, angle2.Z); + + if (!angle1.equals(angle2)) + { + vec4_hl q1, q2; + AngleQuaternion( angle1, q1 ); + AngleQuaternion( angle2, q2 ); + QuaternionSlerp( q1, q2, s, q[i] ); + } + else + { + AngleQuaternion( angle1, q[i] ); + } + + calcBonePosition(frame, s, bone, anim, pos[i]); } if (seq->motiontype & STUDIO_X) @@ -1591,12 +1518,12 @@ void CAnimatedMeshHalfLife::calcRotations ( vec3_hl *pos, vec4_hl *q, SHalflifeS pos[seq->motionbone][2] = 0.f; } + /*! */ SHalflifeAnimOffset * CAnimatedMeshHalfLife::getAnim( SHalflifeSequence *seq ) { - SHalflifeSequenceGroup *seqgroup; - seqgroup = (SHalflifeSequenceGroup *)((u8*)Header + Header->seqgroupindex) + seq->seqgroup; + SHalflifeSequenceGroup *seqgroup = (SHalflifeSequenceGroup *)((u8*)Header + Header->seqgroupindex) + seq->seqgroup; if (seq->seqgroup == 0) { @@ -1609,18 +1536,18 @@ SHalflifeAnimOffset * CAnimatedMeshHalfLife::getAnim( SHalflifeSequence *seq ) /*! */ -void CAnimatedMeshHalfLife::slerpBones( vec4_hl q1[], vec3_hl pos1[], vec4_hl q2[], vec3_hl pos2[], f32 s ) +void CAnimatedMeshHalfLife::slerpBones(vec4_hl q1[], vec3_hl pos1[], vec4_hl q2[], vec3_hl pos2[], f32 s) { - vec4_hl q3; - f32 s1; + if (s < 0) + s = 0; + else if (s > 1.f) + s = 1.f; - if (s < 0) s = 0; - else if (s > 1.f) s = 1.f; - - s1 = 1.f - s; + f32 s1 = 1.f - s; for ( u32 i = 0; i < Header->numbones; i++) { + vec4_hl q3; QuaternionSlerp( q1[i], q2[i], s, q3 ); q1[i][0] = q3[0]; q1[i][1] = q3[1]; @@ -1632,14 +1559,11 @@ void CAnimatedMeshHalfLife::slerpBones( vec4_hl q1[], vec3_hl pos1[], vec4_hl q2 } } + /*! */ -void CAnimatedMeshHalfLife::setUpBones () +void CAnimatedMeshHalfLife::setUpBones() { - SHalflifeBone *bone; - SHalflifeSequence *seq; - SHalflifeAnimOffset *anim; - static vec3_hl pos[MAXSTUDIOBONES]; f32 bonematrix[3][4]; static vec4_hl q[MAXSTUDIOBONES]; @@ -1654,18 +1578,16 @@ void CAnimatedMeshHalfLife::setUpBones () if (SequenceIndex >= Header->numseq) SequenceIndex = 0; - seq = (SHalflifeSequence *)((u8*) Header + Header->seqindex) + SequenceIndex; + SHalflifeSequence *seq = (SHalflifeSequence *)((u8*) Header + Header->seqindex) + SequenceIndex; - anim = getAnim( seq ); - calcRotations( pos, q, seq, anim, CurrentFrame ); + SHalflifeAnimOffset *anim = getAnim(seq); + calcRotations(pos, q, seq, anim, CurrentFrame); if (seq->numblends > 1) { - f32 s; - anim += Header->numbones; calcRotations( pos2, q2, seq, anim, CurrentFrame ); - s = Blending[0] / 255.f; + f32 s = Blending[0] / 255.f; slerpBones( q, pos, q2, pos2, s ); @@ -1685,7 +1607,7 @@ void CAnimatedMeshHalfLife::setUpBones () } } - bone = (SHalflifeBone *)((u8*) Header + Header->boneindex); + SHalflifeBone *bone = (SHalflifeBone *)((u8*) Header + Header->boneindex); for (u32 i = 0; i < Header->numbones; i++) { @@ -1705,7 +1627,6 @@ void CAnimatedMeshHalfLife::setUpBones () } - //! Returns an axis aligned bounding box const core::aabbox3d& CAnimatedMeshHalfLife::getBoundingBox() const { @@ -1726,10 +1647,11 @@ u32 CAnimatedMeshHalfLife::getMeshBufferCount() const return MeshIPol.getMeshBufferCount(); } + //! returns pointer to a mesh buffer IMeshBuffer* CAnimatedMeshHalfLife::getMeshBuffer(u32 nr) const { - return MeshIPol.getMeshBuffer ( nr ); + return MeshIPol.getMeshBuffer(nr); } @@ -1737,16 +1659,18 @@ IMeshBuffer* CAnimatedMeshHalfLife::getMeshBuffer(u32 nr) const /** \param material: material to search for \return Returns the pointer to the mesh buffer or NULL if there is no such mesh buffer. */ -IMeshBuffer* CAnimatedMeshHalfLife::getMeshBuffer( const video::SMaterial &material) const +IMeshBuffer* CAnimatedMeshHalfLife::getMeshBuffer(const video::SMaterial &material) const { - return MeshIPol.getMeshBuffer ( material ); + return MeshIPol.getMeshBuffer(material); } + void CAnimatedMeshHalfLife::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue) { MeshIPol.setMaterialFlag ( flag, newvalue ); } + //! set user axis aligned bounding box void CAnimatedMeshHalfLife::setBoundingBox(const core::aabbox3df& box) { diff --git a/source/Irrlicht/CAnimatedMeshHalfLife.h b/source/Irrlicht/CAnimatedMeshHalfLife.h index e781255e..c80696ca 100644 --- a/source/Irrlicht/CAnimatedMeshHalfLife.h +++ b/source/Irrlicht/CAnimatedMeshHalfLife.h @@ -18,8 +18,8 @@ namespace irr namespace scene { - -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) # pragma pack( push, packing ) # pragma pack( 1 ) # define PACK_STRUCT @@ -29,320 +29,320 @@ namespace scene # error compiler not supported #endif - // STUDIO MODELS, Copyright (c) 1998, Valve LLC. All rights reserved. - #define MAXSTUDIOTRIANGLES 20000 // TODO: tune this - #define MAXSTUDIOVERTS 2048 // TODO: tune this - #define MAXSTUDIOSEQUENCES 256 // total animation sequences - #define MAXSTUDIOSKINS 100 // total textures - #define MAXSTUDIOSRCBONES 512 // bones allowed at source movement - #define MAXSTUDIOBONES 128 // total bones actually used - #define MAXSTUDIOMODELS 32 // sub-models per model - #define MAXSTUDIOBODYPARTS 32 - #define MAXSTUDIOGROUPS 4 - #define MAXSTUDIOANIMATIONS 512 // per sequence - #define MAXSTUDIOMESHES 256 - #define MAXSTUDIOEVENTS 1024 - #define MAXSTUDIOPIVOTS 256 - #define MAXSTUDIOCONTROLLERS 8 - - typedef f32 vec3_hl[3]; // x,y,z - typedef f32 vec4_hl[4]; // x,y,z,w - - struct SHalflifeHeader - { - c8 id[4]; - s32 version; - - c8 name[64]; - s32 length; - - vec3_hl eyeposition; // ideal eye position - vec3_hl min; // ideal movement hull size - vec3_hl max; - - vec3_hl bbmin; // clipping bounding box - vec3_hl bbmax; - - s32 flags; - - u32 numbones; // bones - u32 boneindex; - - u32 numbonecontrollers; // bone controllers - u32 bonecontrollerindex; - - u32 numhitboxes; // complex bounding boxes - u32 hitboxindex; - - u32 numseq; // animation sequences - u32 seqindex; - - u32 numseqgroups; // demand loaded sequences - u32 seqgroupindex; - - u32 numtextures; // raw textures - u32 textureindex; - u32 texturedataindex; - - u32 numskinref; // replaceable textures - u32 numskinfamilies; - u32 skinindex; - - u32 numbodyparts; - u32 bodypartindex; - - u32 numattachments; // queryable attachable points - u32 attachmentindex; - - s32 soundtable; - s32 soundindex; - s32 soundgroups; - s32 soundgroupindex; - - s32 numtransitions; // animation node to animation node transition graph - s32 transitionindex; - }; - - // header for demand loaded sequence group data - typedef struct - { - s32 id; - s32 version; - - c8 name[64]; - s32 length; - } studioseqhdr_t; - - // bones - struct SHalflifeBone - { - c8 name[32]; // bone name for symbolic links - s32 parent; // parent bone - s32 flags; // ?? - s32 bonecontroller[6]; // bone controller index, -1 == none - f32 value[6]; // default DoF values - f32 scale[6]; // scale for delta DoF values - }; - - - // bone controllers - struct SHalflifeBoneController - { - s32 bone; // -1 == 0 - s32 type; // X, Y, Z, XR, YR, ZR, M - f32 start; - f32 end; - s32 rest; // byte index value at rest - s32 index; // 0-3 user set controller, 4 mouth - }; - - // intersection boxes - struct SHalflifeBBox - { - s32 bone; - s32 group; // intersection group - vec3_hl bbmin; // bounding box - vec3_hl bbmax; - }; - -#ifndef ZONE_H - typedef void *cache_user_t; -#endif - - // demand loaded sequence groups - struct SHalflifeSequenceGroup - { - c8 label[32]; // textual name - c8 name[64]; // file name - cache_user_t cache; // cache index pointer - s32 data; // hack for group 0 - }; - - // sequence descriptions - struct SHalflifeSequence - { - c8 label[32]; // sequence label - - f32 fps; // frames per second - s32 flags; // looping/non-looping flags - - s32 activity; - s32 actweight; - - s32 numevents; - s32 eventindex; - - s32 numframes; // number of frames per sequence - - u32 numpivots; // number of foot pivots - u32 pivotindex; - - s32 motiontype; - s32 motionbone; - vec3_hl linearmovement; - s32 automoveposindex; - s32 automoveangleindex; - - vec3_hl bbmin; // per sequence bounding box - vec3_hl bbmax; - - s32 numblends; - s32 animindex; // SHalflifeAnimOffset pointer relative to start of sequence group data - // [blend][bone][X, Y, Z, XR, YR, ZR] - - s32 blendtype[2]; // X, Y, Z, XR, YR, ZR - f32 blendstart[2]; // starting value - f32 blendend[2]; // ending value - s32 blendparent; - - s32 seqgroup; // sequence group for demand loading - - s32 entrynode; // transition node at entry - s32 exitnode; // transition node at exit - s32 nodeflags; // transition rules - - s32 nextseq; // auto advancing sequences - }; - - // events - typedef struct - { - s32 frame; - s32 event; - s32 type; - c8 options[64]; - } mstudioevent_t; - - - // pivots - typedef struct - { - vec3_hl org; // pivot point - s32 start; - s32 end; - } mstudiopivot_t; - - // attachment - struct SHalfelifeAttachment - { - c8 name[32]; - s32 type; - s32 bone; - vec3_hl org; // attachment point - vec3_hl vectors[3]; - }; - - struct SHalflifeAnimOffset - { - u16 offset[6]; - }; - - // animation frames - union SHalfelifeAnimationFrame - { - struct { - u8 valid; - u8 total; - } num; - s16 value; - }; - - - // body part index - struct SHalflifeBody - { - c8 name[64]; - u32 nummodels; - u32 base; - u32 modelindex; // index into models array - }; - - - // skin info - struct SHalflifeTexture - { - c8 name[64]; - s32 flags; - s32 width; - s32 height; - s32 index; - }; - - - // skin families - // short index[skinfamilies][skinref] - - // studio models - struct SHalflifeModel - { - c8 name[64]; - s32 type; - - f32 boundingradius; - - u32 nummesh; - u32 meshindex; - - u32 numverts; // number of unique vertices - u32 vertinfoindex; // vertex bone info - u32 vertindex; // vertex vec3_hl - u32 numnorms; // number of unique surface normals - u32 norminfoindex; // normal bone info - u32 normindex; // normal vec3_hl - - u32 numgroups; // deformation groups - u32 groupindex; - }; - - - // meshes - typedef struct - { - u32 numtris; - u32 triindex; - u32 skinref; - u32 numnorms; // per mesh normals - u32 normindex; // normal vec3_hl - } SHalflifeMesh; - - // lighting options - #define STUDIO_NF_FLATSHADE 0x0001 - #define STUDIO_NF_CHROME 0x0002 - #define STUDIO_NF_FULLBRIGHT 0x0004 - - // motion flags - #define STUDIO_X 0x0001 - #define STUDIO_Y 0x0002 - #define STUDIO_Z 0x0004 - #define STUDIO_XR 0x0008 - #define STUDIO_YR 0x0010 - #define STUDIO_ZR 0x0020 - #define STUDIO_LX 0x0040 - #define STUDIO_LY 0x0080 - #define STUDIO_LZ 0x0100 - #define STUDIO_AX 0x0200 - #define STUDIO_AY 0x0400 - #define STUDIO_AZ 0x0800 - #define STUDIO_AXR 0x1000 - #define STUDIO_AYR 0x2000 - #define STUDIO_AZR 0x4000 - #define STUDIO_TYPES 0x7FFF - #define STUDIO_RLOOP 0x8000 // controller that wraps shortest distance - - // sequence flags - #define STUDIO_LOOPING 0x0001 - - // bone flags - #define STUDIO_HAS_NORMALS 0x0001 - #define STUDIO_HAS_VERTICES 0x0002 - #define STUDIO_HAS_BBOX 0x0004 - #define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them - - #define RAD_TO_STUDIO (32768.0/M_PI) - #define STUDIO_TO_RAD (M_PI/32768.0) - - // Default alignment -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) + // STUDIO MODELS, Copyright (c) 1998, Valve LLC. All rights reserved. + #define MAXSTUDIOTRIANGLES 20000 // TODO: tune this + #define MAXSTUDIOVERTS 2048 // TODO: tune this + #define MAXSTUDIOSEQUENCES 256 // total animation sequences + #define MAXSTUDIOSKINS 100 // total textures + #define MAXSTUDIOSRCBONES 512 // bones allowed at source movement + #define MAXSTUDIOBONES 128 // total bones actually used + #define MAXSTUDIOMODELS 32 // sub-models per model + #define MAXSTUDIOBODYPARTS 32 + #define MAXSTUDIOGROUPS 4 + #define MAXSTUDIOANIMATIONS 512 // per sequence + #define MAXSTUDIOMESHES 256 + #define MAXSTUDIOEVENTS 1024 + #define MAXSTUDIOPIVOTS 256 + #define MAXSTUDIOCONTROLLERS 8 + + typedef f32 vec3_hl[3]; // x,y,z + typedef f32 vec4_hl[4]; // x,y,z,w + + struct SHalflifeHeader + { + c8 id[4]; + s32 version; + + c8 name[64]; + s32 length; + + vec3_hl eyeposition; // ideal eye position + vec3_hl min; // ideal movement hull size + vec3_hl max; + + vec3_hl bbmin; // clipping bounding box + vec3_hl bbmax; + + s32 flags; + + u32 numbones; // bones + u32 boneindex; + + u32 numbonecontrollers; // bone controllers + u32 bonecontrollerindex; + + u32 numhitboxes; // complex bounding boxes + u32 hitboxindex; + + u32 numseq; // animation sequences + u32 seqindex; + + u32 numseqgroups; // demand loaded sequences + u32 seqgroupindex; + + u32 numtextures; // raw textures + u32 textureindex; + u32 texturedataindex; + + u32 numskinref; // replaceable textures + u32 numskinfamilies; + u32 skinindex; + + u32 numbodyparts; + u32 bodypartindex; + + u32 numattachments; // queryable attachable points + u32 attachmentindex; + + s32 soundtable; + s32 soundindex; + s32 soundgroups; + s32 soundgroupindex; + + s32 numtransitions; // animation node to animation node transition graph + s32 transitionindex; + } PACK_STRUCT; + + // header for demand loaded sequence group data + typedef struct + { + s32 id; + s32 version; + + c8 name[64]; + s32 length; + } PACK_STRUCT studioseqhdr_t; + + // bones + struct SHalflifeBone + { + c8 name[32]; // bone name for symbolic links + s32 parent; // parent bone + s32 flags; // ?? + s32 bonecontroller[6]; // bone controller index, -1 == none + f32 value[6]; // default DoF values + f32 scale[6]; // scale for delta DoF values + } PACK_STRUCT; + + + // bone controllers + struct SHalflifeBoneController + { + s32 bone; // -1 == 0 + s32 type; // X, Y, Z, XR, YR, ZR, M + f32 start; + f32 end; + s32 rest; // byte index value at rest + s32 index; // 0-3 user set controller, 4 mouth + } PACK_STRUCT; + + // intersection boxes + struct SHalflifeBBox + { + s32 bone; + s32 group; // intersection group + vec3_hl bbmin; // bounding box + vec3_hl bbmax; + } PACK_STRUCT; + +#ifndef ZONE_H + typedef void *cache_user_t; +#endif + + // demand loaded sequence groups + struct SHalflifeSequenceGroup + { + c8 label[32]; // textual name + c8 name[64]; // file name + cache_user_t cache; // cache index pointer + s32 data; // hack for group 0 + } PACK_STRUCT; + + // sequence descriptions + struct SHalflifeSequence + { + c8 label[32]; // sequence label + + f32 fps; // frames per second + s32 flags; // looping/non-looping flags + + s32 activity; + s32 actweight; + + s32 numevents; + s32 eventindex; + + s32 numframes; // number of frames per sequence + + u32 numpivots; // number of foot pivots + u32 pivotindex; + + s32 motiontype; + s32 motionbone; + vec3_hl linearmovement; + s32 automoveposindex; + s32 automoveangleindex; + + vec3_hl bbmin; // per sequence bounding box + vec3_hl bbmax; + + s32 numblends; + s32 animindex; // SHalflifeAnimOffset pointer relative to start of sequence group data + // [blend][bone][X, Y, Z, XR, YR, ZR] + + s32 blendtype[2]; // X, Y, Z, XR, YR, ZR + f32 blendstart[2]; // starting value + f32 blendend[2]; // ending value + s32 blendparent; + + s32 seqgroup; // sequence group for demand loading + + s32 entrynode; // transition node at entry + s32 exitnode; // transition node at exit + s32 nodeflags; // transition rules + + s32 nextseq; // auto advancing sequences + } PACK_STRUCT; + + // events + typedef struct + { + s32 frame; + s32 event; + s32 type; + c8 options[64]; + } PACK_STRUCT mstudioevent_t; + + + // pivots + typedef struct + { + vec3_hl org; // pivot point + s32 start; + s32 end; + } PACK_STRUCT mstudiopivot_t; + + // attachment + struct SHalflifeAttachment + { + c8 name[32]; + s32 type; + s32 bone; + vec3_hl org; // attachment point + vec3_hl vectors[3]; + } PACK_STRUCT; + + struct SHalflifeAnimOffset + { + u16 offset[6]; + } PACK_STRUCT; + + // animation frames + union SHalflifeAnimationFrame + { + struct { + u8 valid; + u8 total; + } PACK_STRUCT num; + s16 value; + } PACK_STRUCT; + + + // body part index + struct SHalflifeBody + { + c8 name[64]; + u32 nummodels; + u32 base; + u32 modelindex; // index into models array + } PACK_STRUCT; + + + // skin info + struct SHalflifeTexture + { + c8 name[64]; + s32 flags; + s32 width; + s32 height; + s32 index; + } PACK_STRUCT; + + + // skin families + // short index[skinfamilies][skinref] + + // studio models + struct SHalflifeModel + { + c8 name[64]; + s32 type; + + f32 boundingradius; + + u32 nummesh; + u32 meshindex; + + u32 numverts; // number of unique vertices + u32 vertinfoindex; // vertex bone info + u32 vertindex; // vertex vec3_hl + u32 numnorms; // number of unique surface normals + u32 norminfoindex; // normal bone info + u32 normindex; // normal vec3_hl + + u32 numgroups; // deformation groups + u32 groupindex; + } PACK_STRUCT; + + + // meshes + typedef struct + { + u32 numtris; + u32 triindex; + u32 skinref; + u32 numnorms; // per mesh normals + u32 normindex; // normal vec3_hl + } PACK_STRUCT SHalflifeMesh; + + // lighting options + #define STUDIO_NF_FLATSHADE 0x0001 + #define STUDIO_NF_CHROME 0x0002 + #define STUDIO_NF_FULLBRIGHT 0x0004 + + // motion flags + #define STUDIO_X 0x0001 + #define STUDIO_Y 0x0002 + #define STUDIO_Z 0x0004 + #define STUDIO_XR 0x0008 + #define STUDIO_YR 0x0010 + #define STUDIO_ZR 0x0020 + #define STUDIO_LX 0x0040 + #define STUDIO_LY 0x0080 + #define STUDIO_LZ 0x0100 + #define STUDIO_AX 0x0200 + #define STUDIO_AY 0x0400 + #define STUDIO_AZ 0x0800 + #define STUDIO_AXR 0x1000 + #define STUDIO_AYR 0x2000 + #define STUDIO_AZR 0x4000 + #define STUDIO_TYPES 0x7FFF + #define STUDIO_RLOOP 0x8000 // controller that wraps shortest distance + + // sequence flags + #define STUDIO_LOOPING 0x0001 + + // bone flags + #define STUDIO_HAS_NORMALS 0x0001 + #define STUDIO_HAS_VERTICES 0x0002 + #define STUDIO_HAS_BBOX 0x0004 + #define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them + + #define RAD_TO_STUDIO (32768.0/M_PI) + #define STUDIO_TO_RAD (M_PI/32768.0) + +// Default alignment +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) # pragma pack( pop, packing ) #endif #undef PACK_STRUCT @@ -392,12 +392,102 @@ namespace scene }; + //! Possible types of Animation Type + enum E_ANIMATION_TYPE + { + //! No Animation + EAMT_STILL, + //! From Start to End, then Stop ( Limited Line ) + EAMT_WAYPOINT, + //! Linear Cycling Animation ( Sawtooth ) + EAMT_LOOPING, + //! Linear bobbing ( Triangle ) + EAMT_PINGPONG + }; + + //! Names for Animation Type + const c8* const MeshAnimationTypeNames[] = + { + "still", + "waypoint", + "looping", + "pingpong", + 0 + }; + + + //! Data for holding named Animation Info + struct KeyFrameInterpolation + { + core::stringc Name; // Name of the current Animation/Bone + E_ANIMATION_TYPE AnimationType; // Type of Animation ( looping, usw..) + + f32 CurrentFrame; // Current Frame + s32 NextFrame; // Frame which will be used next. For blending + + s32 StartFrame; // Absolute Frame where the current animation start + s32 Frames; // Relative Frames how much Frames this animation have + s32 LoopingFrames; // How much of Frames sould be looped + s32 EndFrame; // Absolute Frame where the current animation ends End = start + frames - 1 + + f32 FramesPerSecond; // Speed in Frames/Seconds the animation is played + f32 RelativeSpeed; // Factor Original fps is modified + + u32 BeginTime; // Animation started at this thime + u32 EndTime; // Animation end at this time + u32 LastTime; // Last Keyframe was done at this time + + KeyFrameInterpolation ( const c8 * name = "", s32 start = 0, s32 frames = 0, s32 loopingframes = 0, + f32 fps = 0.f, f32 relativefps = 1.f ) + : Name ( name ), AnimationType ( loopingframes ? EAMT_LOOPING : EAMT_WAYPOINT), + CurrentFrame ( (f32) start ), NextFrame ( start ), StartFrame ( start ), + Frames ( frames ), LoopingFrames ( loopingframes ), EndFrame ( start + frames - 1 ), + FramesPerSecond ( fps ), RelativeSpeed ( relativefps ), + BeginTime ( 0 ), EndTime ( 0 ), LastTime ( 0 ) + { + } + + // linear search + bool operator == ( const KeyFrameInterpolation & other ) const + { + return Name.equals_ignore_case ( other.Name ); + } + + }; + + + //! a List holding named Animations + typedef core::array < KeyFrameInterpolation > IAnimationList; + + //! a List holding named Skins + typedef core::array < core::stringc > ISkinList; + + + // Current Model per Body + struct SubModel + { + core::stringc name; + u32 startBuffer; + u32 endBuffer; + u32 state; + }; + + struct BodyPart + { + core::stringc name; + u32 defaultModel; + core::array < SubModel > model; + }; + //! a List holding named Models and SubModels + typedef core::array < BodyPart > IBodyList; + + class CAnimatedMeshHalfLife : public IAnimatedMesh { public: //! constructor - CAnimatedMeshHalfLife( ); + CAnimatedMeshHalfLife(); //! destructor virtual ~CAnimatedMeshHalfLife(); @@ -410,7 +500,7 @@ namespace scene virtual IMesh* getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop); virtual const core::aabbox3d& getBoundingBox() const; virtual E_ANIMATED_MESH_TYPE getMeshType() const; - virtual void renderModel ( u32 param, video::IVideoDriver * driver, const core::matrix4 &absoluteTransformation); + virtual void renderModel ( u32 param, video::IVideoDriver * driver, const core::matrix4 &absoluteTransformation); //! returns amount of mesh buffers. virtual u32 getMeshBufferCount() const; @@ -449,47 +539,43 @@ namespace scene //! return a Mesh per frame SMesh MeshIPol; - ISceneManager *SceneManager; - SHalflifeHeader *Header; - SHalflifeHeader *TextureHeader; - bool OwnTexModel; // do we have a modelT.mdl ? - SHalflifeHeader *AnimationHeader[32]; // sequences named model01.mdl, model02.mdl + SHalflifeHeader *Header; + SHalflifeHeader *TextureHeader; + bool OwnTexModel; // do we have a modelT.mdl ? + SHalflifeHeader *AnimationHeader[32]; // sequences named model01.mdl, model02.mdl void initData (); - void freeModel (); SHalflifeHeader * loadModel( io::IReadFile* file, const io::path &filename ); bool postLoadModel( const io::path &filename ); - u32 SequenceIndex; // sequence index - f32 CurrentFrame; // Current Frame - + u32 SequenceIndex; // sequence index + f32 CurrentFrame; // Current Frame + #define MOUTH_CONTROLLER 4 - u8 BoneController[4 + 1 ]; // bone controllers + mouth position - u8 Blending[2]; // animation blending - + u8 BoneController[4 + 1 ]; // bone controllers + mouth position + u8 Blending[2]; // animation blending + f32 SetController( s32 controllerIndex, f32 value ); - - u32 SkinGroupSelection; // skin group selection + u32 SkinGroupSelection; // skin group selection u32 SetSkin( u32 value ); void initModel (); - void dumpModelInfo ( u32 level); - - void ExtractBbox( s32 sequence, core::aabbox3df &box ); - + void dumpModelInfo ( u32 level); - void setUpBones (); - SHalflifeAnimOffset * getAnim( SHalflifeSequence *seq ); - void slerpBones( vec4_hl q1[], vec3_hl pos1[], vec4_hl q2[], vec3_hl pos2[], f32 s ); - void calcRotations ( vec3_hl *pos, vec4_hl *q, SHalflifeSequence *seq, SHalflifeAnimOffset *anim, f32 f ); - - vec4_hl BoneAdj; - void calcBoneAdj(); - void calcBoneQuaternion( s32 frame, f32 s, SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *q ) const; - void calcBonePosition( s32 frame, f32 s, SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *pos ) const; + void ExtractBbox( s32 sequence, core::aabbox3df &box ); + + void setUpBones (); + SHalflifeAnimOffset * getAnim( SHalflifeSequence *seq ); + void slerpBones( vec4_hl q1[], vec3_hl pos1[], vec4_hl q2[], vec3_hl pos2[], f32 s ); + void calcRotations ( vec3_hl *pos, vec4_hl *q, SHalflifeSequence *seq, SHalflifeAnimOffset *anim, f32 f ); + + vec4_hl BoneAdj; + void calcBoneAdj(); + void calcBoneQuaternion(const s32 frame, const SHalflifeBone *bone, SHalflifeAnimOffset *anim, const u32 j, f32& angle1, f32& angle2) const; + void calcBonePosition(const s32 frame, f32 s, const SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *pos ) const; void buildVertices (); @@ -497,12 +583,11 @@ namespace scene #define HL_TEXTURE_ATLAS - #ifdef HL_TEXTURE_ATLAS STextureAtlas TextureAtlas; video::ITexture *TextureMaster; #endif - + }; @@ -515,13 +600,14 @@ namespace scene CHalflifeMDLMeshFileLoader( scene::ISceneManager* smgr ); //! returns true if the file maybe is able to be loaded by this class - //! based on the file extension (e.g. ".bsp") + /** based on the file extension (e.g. ".bsp") */ virtual bool isALoadableFileExtension(const io::path& filename) const; //! creates/loads an animated mesh from the file. - //! \return Pointer to the created mesh. Returns 0 if loading failed. - //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). - //! See IReferenceCounted::drop() for more information. + /** \return Pointer to the created mesh. Returns 0 if loading failed. + If you no longer need the mesh, you should call IAnimatedMesh::drop(). + See IReferenceCounted::drop() for more information. + */ virtual IAnimatedMesh* createMesh(io::IReadFile* file); private: diff --git a/source/Irrlicht/CAnimatedMeshMD3.cpp b/source/Irrlicht/CAnimatedMeshMD3.cpp index 11187620..c6189163 100644 --- a/source/Irrlicht/CAnimatedMeshMD3.cpp +++ b/source/Irrlicht/CAnimatedMeshMD3.cpp @@ -63,7 +63,7 @@ struct SMD3Shader //! Constructor CAnimatedMeshMD3::CAnimatedMeshMD3() -:Mesh(0), IPolShift(0), LoopMode(0), Scaling(1.f) +:Mesh(0), IPolShift(0), LoopMode(0), Scaling(1.f)//, FramesPerSecond(25.f) { #ifdef _DEBUG setDebugName("CAnimatedMeshMD3"); @@ -71,7 +71,7 @@ CAnimatedMeshMD3::CAnimatedMeshMD3() Mesh = new SMD3Mesh(); - setInterpolationShift ( 0, 0 ); + setInterpolationShift(0, 0); } @@ -119,7 +119,7 @@ SMD3QuaternionTagList *CAnimatedMeshMD3::getTagList(s32 frame, s32 detailLevel, if ( 0 == Mesh ) return 0; - getMesh ( frame, detailLevel, startFrameLoop, endFrameLoop ); + getMesh(frame, detailLevel, startFrameLoop, endFrameLoop); return &TagListIPol; } @@ -130,8 +130,6 @@ IMesh* CAnimatedMeshMD3::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, if ( 0 == Mesh ) return 0; - u32 i; - //! check if we have the mesh in our private cache SCacheInfo candidate ( frame, startFrameLoop, endFrameLoop ); if ( candidate == Current ) @@ -173,12 +171,11 @@ IMesh* CAnimatedMeshMD3::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, } // build current vertex - for ( i = 0; i!= Mesh->Buffer.size (); ++i ) + for (u32 i = 0; i!= Mesh->Buffer.size (); ++i) { buildVertexArray(frameA, frameB, iPol, Mesh->Buffer[i], - (SMeshBufferLightMap*) MeshIPol.getMeshBuffer(i) - ); + (SMeshBufferLightMap*) MeshIPol.getMeshBuffer(i)); } MeshIPol.recalculateBoundingBox(); @@ -192,7 +189,7 @@ IMesh* CAnimatedMeshMD3::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, //! create a Irrlicht MeshBuffer for a MD3 MeshBuffer IMeshBuffer * CAnimatedMeshMD3::createMeshBuffer(const SMD3MeshBuffer* source, - io::IFileSystem* fs, video::IVideoDriver * driver) + io::IFileSystem* fs, video::IVideoDriver * driver) { SMeshBufferLightMap * dest = new SMeshBufferLightMap(); dest->Vertices.set_used( source->MeshHeader.numVertices ); @@ -203,9 +200,9 @@ IMeshBuffer * CAnimatedMeshMD3::createMeshBuffer(const SMD3MeshBuffer* source, // fill in static face info for ( i = 0; i < source->Indices.size(); i += 3 ) { - dest->Indices[i + 0 ] = (u16) source->Indices[i + 0]; - dest->Indices[i + 1 ] = (u16) source->Indices[i + 1]; - dest->Indices[i + 2 ] = (u16) source->Indices[i + 2]; + dest->Indices[i + 0] = (u16) source->Indices[i + 0]; + dest->Indices[i + 1] = (u16) source->Indices[i + 1]; + dest->Indices[i + 2] = (u16) source->Indices[i + 2]; } // fill in static vertex info @@ -294,7 +291,7 @@ void CAnimatedMeshMD3::buildTagArray ( u32 frameA, u32 frameB, f32 interpolate ) loads a model */ bool CAnimatedMeshMD3::loadModelFile( u32 modelIndex, io::IReadFile* file, - io::IFileSystem* fs, video::IVideoDriver * driver) + io::IFileSystem* fs, video::IVideoDriver * driver) { if (!file) return false; @@ -448,4 +445,3 @@ E_ANIMATED_MESH_TYPE CAnimatedMeshMD3::getMeshType() const } // end namespace irr #endif // _IRR_COMPILE_WITH_MD3_LOADER_ - diff --git a/source/Irrlicht/CAnimatedMeshSceneNode.cpp b/source/Irrlicht/CAnimatedMeshSceneNode.cpp index 55b12f2d..f4a59ce9 100644 --- a/source/Irrlicht/CAnimatedMeshSceneNode.cpp +++ b/source/Irrlicht/CAnimatedMeshSceneNode.cpp @@ -777,7 +777,15 @@ void CAnimatedMeshSceneNode::serializeAttributes(io::IAttributes* out, io::SAttr { IAnimatedMeshSceneNode::serializeAttributes(out, options); - out->addString("Mesh", SceneManager->getMeshCache()->getMeshName(Mesh).getPath().c_str()); + if (options && (options->Flags&io::EARWF_USE_RELATIVE_PATHS) && options->Filename) + { + const io::path path = SceneManager->getFileSystem()->getRelativeFilename( + SceneManager->getFileSystem()->getAbsolutePath(SceneManager->getMeshCache()->getMeshName(Mesh).getPath()), + options->Filename); + out->addString("Mesh", path.c_str()); + } + else + out->addString("Mesh", SceneManager->getMeshCache()->getMeshName(Mesh).getPath().c_str()); out->addBool("Looping", Looping); out->addBool("ReadOnlyMaterials", ReadOnlyMaterials); out->addFloat("FramesPerSecond", FramesPerSecond); diff --git a/source/Irrlicht/CAnimatedMeshSceneNode.h b/source/Irrlicht/CAnimatedMeshSceneNode.h index 0e6a8a80..b3eb54c4 100644 --- a/source/Irrlicht/CAnimatedMeshSceneNode.h +++ b/source/Irrlicht/CAnimatedMeshSceneNode.h @@ -88,10 +88,10 @@ namespace scene //! Gets joint count. virtual u32 getJointCount() const; - //! Redundant command, please use getJointNode. + //! Deprecated command, please use getJointNode. virtual ISceneNode* getMS3DJointNode(const c8* jointName); - //! Redundant command, please use getJointNode. + //! Deprecated command, please use getJointNode. virtual ISceneNode* getXJointNode(const c8* jointName); //! Removes a child from this scene node. diff --git a/source/Irrlicht/CAttributeImpl.h b/source/Irrlicht/CAttributeImpl.h index 129d35a5..05c45e3f 100644 --- a/source/Irrlicht/CAttributeImpl.h +++ b/source/Irrlicht/CAttributeImpl.h @@ -1797,8 +1797,8 @@ class CTextureAttribute : public IAttribute { public: - CTextureAttribute(const char* name, video::ITexture* value, video::IVideoDriver* driver) - : Value(0), Driver(driver) + CTextureAttribute(const char* name, video::ITexture* value, video::IVideoDriver* driver, const io::path& filename) + : Value(0), Driver(driver), OverrideName(filename) { if (Driver) Driver->grab(); @@ -1828,13 +1828,15 @@ public: virtual core::stringw getStringW() { - return core::stringw(Value ? Value->getName().getPath().c_str() : 0); + return core::stringw(OverrideName.size()?OverrideName: + Value ? Value->getName().getPath().c_str() : 0); } virtual core::stringc getString() { // since texture names can be stringw we are careful with the types - return core::stringc(Value ? Value->getName().getPath().c_str() : 0); + return core::stringc(OverrideName.size()?OverrideName: + Value ? Value->getName().getPath().c_str() : 0); } virtual void setString(const char* text) @@ -1842,7 +1844,10 @@ public: if (Driver) { if (text && *text) + { setTexture(Driver->getTexture(text)); + OverrideName=text; + } else setTexture(0); } @@ -1875,6 +1880,7 @@ public: video::ITexture* Value; video::IVideoDriver* Driver; + io::path OverrideName; }; diff --git a/source/Irrlicht/CAttributes.cpp b/source/Irrlicht/CAttributes.cpp index 9123caae..12b4fc06 100644 --- a/source/Irrlicht/CAttributes.cpp +++ b/source/Irrlicht/CAttributes.cpp @@ -509,13 +509,13 @@ void CAttributes::getAttributeEnumerationLiteralsOfEnumeration(const c8* attribu } //! Sets an attribute as texture reference -void CAttributes::setAttribute(const c8* attributeName, video::ITexture* value ) +void CAttributes::setAttribute(const c8* attributeName, video::ITexture* value, const io::path& filename) { IAttribute* att = getAttributeP(attributeName); if (att) - att->setTexture(value); + att->setTexture(value, filename); else - Attributes.push_back(new CTextureAttribute(attributeName, value, Driver)); + Attributes.push_back(new CTextureAttribute(attributeName, value, Driver, filename)); } @@ -818,9 +818,9 @@ void CAttributes::addBinary(const c8* attributeName, void* data, s32 dataSizeInB } //! Adds an attribute as texture reference -void CAttributes::addTexture(const c8* attributeName, video::ITexture* texture) +void CAttributes::addTexture(const c8* attributeName, video::ITexture* texture, const io::path& filename) { - Attributes.push_back(new CTextureAttribute(attributeName, texture, Driver)); + Attributes.push_back(new CTextureAttribute(attributeName, texture, Driver, filename)); } //! Returns if an attribute with a name exists @@ -918,10 +918,10 @@ void CAttributes::setAttribute(s32 index, const char* enumValue, const char* con //! Sets an attribute as texture reference -void CAttributes::setAttribute(s32 index, video::ITexture* texture) +void CAttributes::setAttribute(s32 index, video::ITexture* texture, const io::path& filename) { if ((u32)index < Attributes.size()) - Attributes[index]->setTexture(texture); + Attributes[index]->setTexture(texture, filename); } diff --git a/source/Irrlicht/CAttributes.h b/source/Irrlicht/CAttributes.h index 818bdf34..2775a7d3 100644 --- a/source/Irrlicht/CAttributes.h +++ b/source/Irrlicht/CAttributes.h @@ -602,10 +602,10 @@ public: */ //! Adds an attribute as texture reference - virtual void addTexture(const c8* attributeName, video::ITexture* texture); + virtual void addTexture(const c8* attributeName, video::ITexture* texture, const io::path& filename = ""); //! Sets an attribute as texture reference - virtual void setAttribute(const c8* attributeName, video::ITexture* texture ); + virtual void setAttribute(const c8* attributeName, video::ITexture* texture, const io::path& filename = ""); //! Gets an attribute as texture reference //! \param attributeName: Name of the attribute to get. @@ -616,7 +616,7 @@ public: virtual video::ITexture* getAttributeAsTexture(s32 index); //! Sets an attribute as texture reference - virtual void setAttribute(s32 index, video::ITexture* texture); + virtual void setAttribute(s32 index, video::ITexture* texture, const io::path& filename = ""); diff --git a/source/Irrlicht/CBlit.h b/source/Irrlicht/CBlit.h index 5bd51d2e..022a9829 100644 --- a/source/Irrlicht/CBlit.h +++ b/source/Irrlicht/CBlit.h @@ -28,8 +28,13 @@ namespace irr u32 srcPixelMul; u32 dstPixelMul; - }; + bool stretch; + float x_stretch; + float y_stretch; + + SBlitJob() : stretch(false) {} + }; // Bitfields Cohen Sutherland enum eClipCode @@ -164,7 +169,7 @@ inline void GetClip(AbsRectangle &clipping, video::IImage * t) return alpha in [0;256] Granularity from 32-Bit ARGB add highbit alpha ( alpha > 127 ? + 1 ) */ -static inline u32 extractAlpha ( const u32 c ) +static inline u32 extractAlpha(const u32 c) { return ( c >> 24 ) + ( c >> 31 ); } @@ -173,7 +178,7 @@ static inline u32 extractAlpha ( const u32 c ) return alpha in [0;255] Granularity and 32-Bit ARGB add highbit alpha ( alpha > 127 ? + 1 ) */ -static inline u32 packAlpha ( const u32 c ) +static inline u32 packAlpha(const u32 c) { return (c > 127 ? c - 1 : c) << 24; } @@ -183,7 +188,7 @@ static inline u32 packAlpha ( const u32 c ) Scale Color by (1/value) value 0 - 256 ( alpha ) */ -inline u32 PixelLerp32 ( const u32 source, const u32 value ) +inline u32 PixelLerp32(const u32 source, const u32 value) { u32 srcRB = source & 0x00FF00FF; u32 srcXG = (source & 0xFF00FF00) >> 8; @@ -470,38 +475,87 @@ static void RenderLine16_Blend(video::IImage *t, */ static void executeBlit_TextureCopy_x_to_x( const SBlitJob * job ) { - const void *src = (void*) job->src; - void *dst = (void*) job->dst; - - const u32 widthPitch = job->width * job->dstPixelMul; - for ( s32 dy = 0; dy != job->height; ++dy ) + const u32 w = job->width; + const u32 h = job->height; + if (job->stretch) { - memcpy( dst, src, widthPitch ); + const u32 *src = static_cast(job->src); + u32 *dst = static_cast(job->dst); + const float wscale = 1.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; - src = (void*) ( (u8*) (src) + job->srcPitch ); - dst = (void*) ( (u8*) (dst) + job->dstPitch ); + for ( u32 dy = 0; dy < h; ++dy ) + { + const u32 src_y = (u32)(dy*hscale); + src = (u32*) ( (u8*) (job->src) + job->srcPitch*src_y ); + + for ( u32 dx = 0; dx < w; ++dx ) + { + const u32 src_x = (u32)(dx*wscale); + dst[dx] = src[src_x]; + } + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + } + } + else + { + const u32 widthPitch = job->width * job->dstPixelMul; + const void *src = (void*) job->src; + void *dst = (void*) job->dst; + + for ( u32 dy = 0; dy != h; ++dy ) + { + memcpy( dst, src, widthPitch ); + + src = (void*) ( (u8*) (src) + job->srcPitch ); + dst = (void*) ( (u8*) (dst) + job->dstPitch ); + } } } - /*! */ static void executeBlit_TextureCopy_32_to_16( const SBlitJob * job ) { + const u32 w = job->width; + const u32 h = job->height; const u32 *src = static_cast(job->src); u16 *dst = static_cast(job->dst); - for ( s32 dy = 0; dy != job->height; ++dy ) + if (job->stretch) { - for ( s32 dx = 0; dx != job->width; ++dx ) - { - //16 bit Blitter depends on pre-multiplied color - const u32 s = PixelLerp32( src[dx] | 0xFF000000, extractAlpha( src[dx] ) ); - dst[dx] = video::A8R8G8B8toA1R5G5B5( s ); - } + const float wscale = 1.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; - src = (u32*) ( (u8*) (src) + job->srcPitch ); - dst = (u16*) ( (u8*) (dst) + job->dstPitch ); + for ( u32 dy = 0; dy < h; ++dy ) + { + const u32 src_y = (u32)(dy*hscale); + src = (u32*) ( (u8*) (job->src) + job->srcPitch*src_y ); + + for ( u32 dx = 0; dx < w; ++dx ) + { + const u32 src_x = (u32)(dx*wscale); + //16 bit Blitter depends on pre-multiplied color + const u32 s = PixelLerp32( src[src_x] | 0xFF000000, extractAlpha( src[src_x] ) ); + dst[dx] = video::A8R8G8B8toA1R5G5B5( s ); + } + dst = (u16*) ( (u8*) (dst) + job->dstPitch ); + } + } + else + { + for ( u32 dy = 0; dy != h; ++dy ) + { + for ( u32 dx = 0; dx != w; ++dx ) + { + //16 bit Blitter depends on pre-multiplied color + const u32 s = PixelLerp32( src[dx] | 0xFF000000, extractAlpha( src[dx] ) ); + dst[dx] = video::A8R8G8B8toA1R5G5B5( s ); + } + + src = (u32*) ( (u8*) (src) + job->srcPitch ); + dst = (u16*) ( (u8*) (dst) + job->dstPitch ); + } } } @@ -509,21 +563,43 @@ static void executeBlit_TextureCopy_32_to_16( const SBlitJob * job ) */ static void executeBlit_TextureCopy_24_to_16( const SBlitJob * job ) { - const void *src = (void*) job->src; - u16 *dst = (u16*) job->dst; + const u32 w = job->width; + const u32 h = job->height; + const u8 *src = static_cast(job->src); + u16 *dst = static_cast(job->dst); - for ( s32 dy = 0; dy != job->height; ++dy ) + if (job->stretch) { - u8 * s = (u8*) src; + const float wscale = 3.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; - for ( s32 dx = 0; dx != job->width; ++dx ) + for ( u32 dy = 0; dy < h; ++dy ) { - dst[dx] = video::RGBA16(s[0], s[1], s[2]); - s += 3; + const u32 src_y = (u32)(dy*hscale); + src = (u8*)(job->src) + job->srcPitch*src_y; + + for ( u32 dx = 0; dx < w; ++dx ) + { + const u8* src_x = src+(u32)(dx*wscale); + dst[dx] = video::RGBA16(src_x[0], src_x[1], src_x[2]); + } + dst = (u16*) ( (u8*) (dst) + job->dstPitch ); } + } + else + { + for ( u32 dy = 0; dy != h; ++dy ) + { + const u8* s = src; + for ( u32 dx = 0; dx != w; ++dx ) + { + dst[dx] = video::RGBA16(s[0], s[1], s[2]); + s += 3; + } - src = (void*) ( (u8*) (src) + job->srcPitch ); - dst = (u16*) ( (u8*) (dst) + job->dstPitch ); + src = src+job->srcPitch; + dst = (u16*) ( (u8*) (dst) + job->dstPitch ); + } } } @@ -532,116 +608,235 @@ static void executeBlit_TextureCopy_24_to_16( const SBlitJob * job ) */ static void executeBlit_TextureCopy_16_to_32( const SBlitJob * job ) { - const u16 *src = (u16*) job->src; - u32 *dst = (u32*) job->dst; + const u32 w = job->width; + const u32 h = job->height; + const u16 *src = static_cast(job->src); + u32 *dst = static_cast(job->dst); - for ( s32 dy = 0; dy != job->height; ++dy ) + if (job->stretch) { - for ( s32 dx = 0; dx != job->width; ++dx ) - { - dst[dx] = video::A1R5G5B5toA8R8G8B8( src[dx] ); - } + const float wscale = 1.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; - src = (u16*) ( (u8*) (src) + job->srcPitch ); - dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + for ( u32 dy = 0; dy < h; ++dy ) + { + const u32 src_y = (u32)(dy*hscale); + src = (u16*) ( (u8*) (job->src) + job->srcPitch*src_y ); + + for ( u32 dx = 0; dx < w; ++dx ) + { + const u32 src_x = (u32)(dx*wscale); + dst[dx] = video::A1R5G5B5toA8R8G8B8(src[src_x]); + } + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + } + } + else + { + for ( u32 dy = 0; dy != h; ++dy ) + { + for ( u32 dx = 0; dx != w; ++dx ) + { + dst[dx] = video::A1R5G5B5toA8R8G8B8( src[dx] ); + } + + src = (u16*) ( (u8*) (src) + job->srcPitch ); + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + } } } static void executeBlit_TextureCopy_16_to_24( const SBlitJob * job ) { - const u16 *src = (u16*) job->src; - u8 *dst = (u8*) job->dst; + const u32 w = job->width; + const u32 h = job->height; + const u16 *src = static_cast(job->src); + u8 *dst = static_cast(job->dst); - for ( s32 dy = 0; dy != job->height; ++dy ) + if (job->stretch) { - for ( s32 dx = 0; dx != job->width; ++dx ) - { - u32 colour = video::A1R5G5B5toA8R8G8B8( src[dx] ); - u8 * writeTo = &dst[dx * 3]; - *writeTo++ = (colour >> 16)& 0xFF; - *writeTo++ = (colour >> 8) & 0xFF; - *writeTo++ = colour & 0xFF; - } + const float wscale = 1.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; - src = (u16*) ( (u8*) (src) + job->srcPitch ); - dst += job->dstPitch; + for ( u32 dy = 0; dy < h; ++dy ) + { + const u32 src_y = (u32)(dy*hscale); + src = (u16*) ( (u8*) (job->src) + job->srcPitch*src_y ); + + for ( u32 dx = 0; dx < w; ++dx ) + { + const u32 src_x = (u32)(dx*wscale); + u32 color = video::A1R5G5B5toA8R8G8B8(src[src_x]); + u8 * writeTo = &dst[dx * 3]; + *writeTo++ = (color >> 16)& 0xFF; + *writeTo++ = (color >> 8) & 0xFF; + *writeTo++ = color & 0xFF; + } + dst += job->dstPitch; + } + } + else + { + for ( u32 dy = 0; dy != h; ++dy ) + { + for ( u32 dx = 0; dx != w; ++dx ) + { + u32 color = video::A1R5G5B5toA8R8G8B8(src[dx]); + u8 * writeTo = &dst[dx * 3]; + *writeTo++ = (color >> 16)& 0xFF; + *writeTo++ = (color >> 8) & 0xFF; + *writeTo++ = color & 0xFF; + } + + src = (u16*) ( (u8*) (src) + job->srcPitch ); + dst += job->dstPitch; + } } } - /*! */ static void executeBlit_TextureCopy_24_to_32( const SBlitJob * job ) { - void *src = (void*) job->src; - u32 *dst = (u32*) job->dst; + const u32 w = job->width; + const u32 h = job->height; + const u8 *src = static_cast(job->src); + u32 *dst = static_cast(job->dst); - for ( s32 dy = 0; dy != job->height; ++dy ) + if (job->stretch) { - u8 * s = (u8*) src; + const float wscale = 3.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; - for ( s32 dx = 0; dx != job->width; ++dx ) + for ( u32 dy = 0; dy < h; ++dy ) { - dst[dx] = 0xFF000000 | s[0] << 16 | s[1] << 8 | s[2]; - s += 3; + const u32 src_y = (u32)(dy*hscale); + src = (const u8*)job->src+(job->srcPitch*src_y); + + for ( u32 dx = 0; dx < w; ++dx ) + { + const u8* s = src+(u32)(dx*wscale); + dst[dx] = 0xFF000000 | s[0] << 16 | s[1] << 8 | s[2]; + } + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); } + } + else + { + for ( s32 dy = 0; dy != job->height; ++dy ) + { + const u8* s = src; - src = (void*) ( (u8*) (src) + job->srcPitch ); - dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + for ( s32 dx = 0; dx != job->width; ++dx ) + { + dst[dx] = 0xFF000000 | s[0] << 16 | s[1] << 8 | s[2]; + s += 3; + } + + src = src + job->srcPitch; + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + } } } static void executeBlit_TextureCopy_32_to_24( const SBlitJob * job ) { - const u32 * src = (u32*) job->src; - u8 * dst = (u8*) job->dst; + const u32 w = job->width; + const u32 h = job->height; + const u32 *src = static_cast(job->src); + u8 *dst = static_cast(job->dst); - for ( s32 dy = 0; dy != job->height; ++dy ) + if (job->stretch) { - for ( s32 dx = 0; dx != job->width; ++dx ) + const float wscale = 1.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; + + for ( u32 dy = 0; dy < h; ++dy ) { - u8 * writeTo = &dst[dx * 3]; - *writeTo++ = (src[dx] >> 16)& 0xFF; - *writeTo++ = (src[dx] >> 8) & 0xFF; - *writeTo++ = src[dx] & 0xFF; + const u32 src_y = (u32)(dy*hscale); + src = (u32*) ( (u8*) (job->src) + job->srcPitch*src_y); + + for ( u32 dx = 0; dx < w; ++dx ) + { + const u32 src_x = src[(u32)(dx*wscale)]; + u8 * writeTo = &dst[dx * 3]; + *writeTo++ = (src_x >> 16)& 0xFF; + *writeTo++ = (src_x >> 8) & 0xFF; + *writeTo++ = src_x & 0xFF; + } + dst += job->dstPitch; } - - src = (u32*) ( (u8*) (src) + job->srcPitch ); - dst += job->dstPitch ; } + else + { + for ( u32 dy = 0; dy != h; ++dy ) + { + for ( u32 dx = 0; dx != w; ++dx ) + { + u8 * writeTo = &dst[dx * 3]; + *writeTo++ = (src[dx] >> 16)& 0xFF; + *writeTo++ = (src[dx] >> 8) & 0xFF; + *writeTo++ = src[dx] & 0xFF; + } + src = (u32*) ( (u8*) (src) + job->srcPitch ); + dst += job->dstPitch; + } + } } - /*! */ static void executeBlit_TextureBlend_16_to_16( const SBlitJob * job ) { - u32 dx; - s32 dy; + const u32 w = job->width; + const u32 h = job->height; + const u32 rdx = w>>1; - u32 *src = (u32*) job->src; + const u32 *src = (u32*) job->src; u32 *dst = (u32*) job->dst; - - const u32 rdx = job->width >> 1; - const u32 off = core::if_c_a_else_b( job->width & 1 ,job->width - 1, 0 ); - - - for ( dy = 0; dy != job->height; ++dy ) + if (job->stretch) { - for ( dx = 0; dx != rdx; ++dx ) + const float wscale = 1.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; + const u32 off = core::if_c_a_else_b(w&1, (u32)((w-1)*wscale), 0); + for ( u32 dy = 0; dy < h; ++dy ) { - dst[dx] = PixelBlend16_simd( dst[dx], src[dx] ); - } + const u32 src_y = (u32)(dy*hscale); + src = (u32*) ( (u8*) (job->src) + job->srcPitch*src_y ); + + for ( u32 dx = 0; dx < rdx; ++dx ) + { + const u32 src_x = (u32)(dx*wscale); + dst[dx] = PixelBlend16_simd( dst[dx], src[src_x] ); + } + if ( off ) + { + ((u16*) dst)[off] = PixelBlend16( ((u16*) dst)[off], ((u16*) src)[off] ); + } - if ( off ) + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + } + } + else + { + const u32 off = core::if_c_a_else_b(w&1, w-1, 0); + for (u32 dy = 0; dy != h; ++dy ) { - ((u16*) dst)[off] = PixelBlend16( ((u16*) dst)[off], ((u16*) src)[off] ); - } + for (u32 dx = 0; dx != rdx; ++dx ) + { + dst[dx] = PixelBlend16_simd( dst[dx], src[dx] ); + } - src = (u32*) ( (u8*) (src) + job->srcPitch ); - dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + if ( off ) + { + ((u16*) dst)[off] = PixelBlend16( ((u16*) dst)[off], ((u16*) src)[off] ); + } + + src = (u32*) ( (u8*) (src) + job->srcPitch ); + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + } } } @@ -649,17 +844,40 @@ static void executeBlit_TextureBlend_16_to_16( const SBlitJob * job ) */ static void executeBlit_TextureBlend_32_to_32( const SBlitJob * job ) { - u32 *src = (u32*) job->src; + const u32 w = job->width; + const u32 h = job->height; + const u32 *src = (u32*) job->src; u32 *dst = (u32*) job->dst; - for ( s32 dy = 0; dy != job->height; ++dy ) + if (job->stretch) { - for ( s32 dx = 0; dx != job->width; ++dx ) + const float wscale = 1.f/job->x_stretch; + const float hscale = 1.f/job->y_stretch; + for ( u32 dy = 0; dy < h; ++dy ) { - dst[dx] = PixelBlend32( dst[dx], src[dx] ); + const u32 src_y = (u32)(dy*hscale); + src = (u32*) ( (u8*) (job->src) + job->srcPitch*src_y ); + + for ( u32 dx = 0; dx < w; ++dx ) + { + const u32 src_x = (u32)(dx*wscale); + dst[dx] = PixelBlend32( dst[dx], src[src_x] ); + } + + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); + } + } + else + { + for ( u32 dy = 0; dy != h; ++dy ) + { + for ( u32 dx = 0; dx != w; ++dx ) + { + dst[dx] = PixelBlend32( dst[dx], src[dx] ); + } + src = (u32*) ( (u8*) (src) + job->srcPitch ); + dst = (u32*) ( (u8*) (dst) + job->dstPitch ); } - src = (u32*) ( (u8*) (src) + job->srcPitch ); - dst = (u32*) ( (u8*) (dst) + job->dstPitch ); } } @@ -927,10 +1145,8 @@ static s32 Blit(eBlitter operation, job.width = job.Dest.x1 - job.Dest.x0; job.height = job.Dest.y1 - job.Dest.y0; - job.Source.x0 = sourceClip.x0 + ( job.Dest.x0 - v.x0 ); job.Source.x1 = job.Source.x0 + job.width; - job.Source.y0 = sourceClip.y0 + ( job.Dest.y0 - v.y0 ); job.Source.y1 = job.Source.y0 + job.height; @@ -963,6 +1179,60 @@ static s32 Blit(eBlitter operation, return 1; } +static s32 StretchBlit(eBlitter operation, + video::IImage* dest, const core::rect *destRect, + const core::rect *srcRect, video::IImage* const source, + u32 argb) +{ + tExecuteBlit blitter = getBlitter2( operation, dest, source ); + if ( 0 == blitter ) + { + return 0; + } + + SBlitJob job; + + // Clipping + setClip ( job.Source, srcRect, source, 1 ); + setClip ( job.Dest, destRect, dest, 0 ); + + job.width = job.Dest.x1-job.Dest.x0; + job.height = job.Dest.y1-job.Dest.y0; + + job.argb = argb; + + // use original dest size, despite any clipping + job.x_stretch = (float)destRect->getWidth() / (float)(job.Source.x1-job.Source.x0); + job.y_stretch = (float)destRect->getHeight() / (float)(job.Source.y1-job.Source.y0); + job.stretch = (job.x_stretch != 1.f) || (job.y_stretch != 1.f); + + if ( source ) + { + job.srcPitch = source->getPitch(); + job.srcPixelMul = source->getBytesPerPixel(); + job.src = (void*) ( (u8*) source->lock() + ( job.Source.y0 * job.srcPitch ) + ( job.Source.x0 * job.srcPixelMul ) ); + } + else + { + // use srcPitch for color operation on dest + job.srcPitch = job.width * dest->getBytesPerPixel(); + } + + job.dstPitch = dest->getPitch(); + job.dstPixelMul = dest->getBytesPerPixel(); + job.dst = (void*) ( (u8*) dest->lock() + ( job.Dest.y0 * job.dstPitch ) + ( job.Dest.x0 * job.dstPixelMul ) ); + + blitter( &job ); + + if ( source ) + source->unlock(); + + if ( dest ) + dest->unlock(); + + return 1; +} + } #endif diff --git a/source/Irrlicht/CD3D8Driver.cpp b/source/Irrlicht/CD3D8Driver.cpp index 94ec1bd2..11495189 100644 --- a/source/Irrlicht/CD3D8Driver.cpp +++ b/source/Irrlicht/CD3D8Driver.cpp @@ -17,6 +17,7 @@ #include "CD3D8ShaderMaterialRenderer.h" #include "CD3D8NormalMapRenderer.h" #include "CD3D8ParallaxMapRenderer.h" +#include "SIrrCreationParameters.h" namespace irr { @@ -605,6 +606,8 @@ bool CD3D8Driver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const return (Caps.TextureCaps & D3DPTEXTURECAPS_POW2) == 0; case EVDF_COLOR_MASK: return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE) != 0; + case EVDF_BLEND_OPERATIONS: + return true; default: return false; }; @@ -806,13 +809,21 @@ bool CD3D8Driver::setRenderTarget(video::ITexture* texture, //! Creates a render target texture. -ITexture* CD3D8Driver::addRenderTargetTexture(const core::dimension2d& size, - const io::path& name, - const ECOLOR_FORMAT format) +ITexture* CD3D8Driver::addRenderTargetTexture( + const core::dimension2d& size, const io::path& name, + const ECOLOR_FORMAT format) { - ITexture* tex = new CD3D8Texture(this, size, name); - addTexture(tex); - tex->drop(); + CD3D8Texture* tex = new CD3D8Texture(this, size, name); + if (tex) + { + if (!tex->Texture) + { + tex->drop(); + return 0; + } + addTexture(tex); + tex->drop(); + } return tex; } @@ -1562,6 +1573,47 @@ void CD3D8Driver::setBasicRenderStates(const SMaterial& material, const SMateria pID3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, flag); } + if (queryFeature(EVDF_BLEND_OPERATIONS) && + (resetAllRenderstates|| lastmaterial.BlendOperation != material.BlendOperation)) + { + if (EBO_NONE) + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + else + { + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + switch (material.BlendOperation) + { + case EBO_MAX: + case EBO_MAX_FACTOR: + case EBO_MAX_ALPHA: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX); + break; + case EBO_MIN: + case EBO_MIN_FACTOR: + case EBO_MIN_ALPHA: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MIN); + break; + case EBO_SUBTRACT: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT); + break; + case EBO_REVSUBTRACT: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT); + break; + default: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + break; + } + } + } + + // Polygon offset + if (queryFeature(EVDF_POLYGON_OFFSET) && (resetAllRenderstates || + lastmaterial.PolygonOffsetDirection != material.PolygonOffsetDirection || + lastmaterial.PolygonOffsetFactor != material.PolygonOffsetFactor)) + { + pID3DDevice->SetRenderState(D3DRS_ZBIAS, material.PolygonOffsetFactor); + } + // thickness if (resetAllRenderstates || lastmaterial.Thickness != material.Thickness) { @@ -2367,16 +2419,14 @@ namespace video #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ //! creates a video driver -IVideoDriver* createDirectX8Driver(const core::dimension2d& screenSize, - HWND window, u32 bits, bool fullscreen, bool stencilbuffer, - io::IFileSystem* io, bool pureSoftware, bool highPrecisionFPU, - bool vsync, u8 antiAlias, u32 displayAdapter) +IVideoDriver* createDirectX8Driver(const SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window) { - CD3D8Driver* dx8 = new CD3D8Driver(screenSize, window, fullscreen, - stencilbuffer, io, pureSoftware); + const bool pureSoftware = false; + CD3D8Driver* dx8 = new CD3D8Driver(params.WindowSize, window, params.Fullscreen, params.Stencilbuffer, io, pureSoftware); - if (!dx8->initDriver(screenSize, window, bits, fullscreen, - pureSoftware, highPrecisionFPU, vsync, antiAlias, displayAdapter)) + if (!dx8->initDriver(params.WindowSize, window, params.Bits, params.Fullscreen, pureSoftware, params.HighPrecisionFPU, + params.Vsync, params.AntiAlias, params.DisplayAdapter)) { dx8->drop(); dx8 = 0; diff --git a/source/Irrlicht/CD3D8Texture.cpp b/source/Irrlicht/CD3D8Texture.cpp index 4b281a2f..4cced250 100644 --- a/source/Irrlicht/CD3D8Texture.cpp +++ b/source/Irrlicht/CD3D8Texture.cpp @@ -251,7 +251,7 @@ void* CD3D8Texture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel) os::Printer::log("Could not lock DIRECT3D8 Texture.", "Data copy failed.", ELL_ERROR); return 0; } - hr = RTTSurface->LockRect(&rect, 0, readOnly?D3DLOCK_READONLY:0); + hr = RTTSurface->LockRect(&rect, 0, (mode==ETLM_READ_ONLY)?D3DLOCK_READONLY:0); if(FAILED(hr)) { os::Printer::log("Could not lock DIRECT3D8 Texture.", "LockRect failed.", ELL_ERROR); diff --git a/source/Irrlicht/CD3D9Driver.cpp b/source/Irrlicht/CD3D9Driver.cpp index 3dc43d39..a0edb304 100644 --- a/source/Irrlicht/CD3D9Driver.cpp +++ b/source/Irrlicht/CD3D9Driver.cpp @@ -16,12 +16,18 @@ #include "CD3D9NormalMapRenderer.h" #include "CD3D9ParallaxMapRenderer.h" #include "CD3D9HLSLMaterialRenderer.h" +#include "SIrrCreationParameters.h" namespace irr { namespace video { +namespace +{ + inline DWORD F2DW( FLOAT f ) { return *((DWORD*)&f); } +} + //! constructor CD3D9Driver::CD3D9Driver(const core::dimension2d& screenSize, HWND window, bool fullscreen, bool stencilbuffer, @@ -659,6 +665,10 @@ bool CD3D9Driver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING) != 0; case EVDF_OCCLUSION_QUERY: return OcclusionQuerySupport; + case EVDF_POLYGON_OFFSET: + return (Caps.RasterCaps & (D3DPRASTERCAPS_DEPTHBIAS|D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS)) != 0; + case EVDF_BLEND_OPERATIONS: + return true; default: return false; }; @@ -1265,12 +1275,12 @@ void CD3D9Driver::drawHardwareBuffer(SHWBufferLink *_HWBuffer) //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ -void CD3D9Driver::createOcclusionQuery(scene::ISceneNode* node, +void CD3D9Driver::addOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh) { if (!queryFeature(EVDF_OCCLUSION_QUERY)) return; - CNullDriver::createOcclusionQuery(node, mesh); + CNullDriver::addOcclusionQuery(node, mesh); const s32 index = OcclusionQueries.linear_search(SOccQuery(node)); if ((index != -1) && (OcclusionQueries[index].PID == 0)) pID3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, reinterpret_cast(&OcclusionQueries[index].PID)); @@ -1446,13 +1456,13 @@ void CD3D9Driver::draw2D3DVertexPrimitiveList(const void* vertices, if (pType==scene::EPT_POINT_SPRITES) pID3DDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE); pID3DDevice->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE); - pID3DDevice->SetRenderState(D3DRS_POINTSIZE, *(DWORD*)(&tmp)); + pID3DDevice->SetRenderState(D3DRS_POINTSIZE, F2DW(tmp)); tmp=1.0f; - pID3DDevice->SetRenderState(D3DRS_POINTSCALE_A, *(DWORD*)(&tmp)); - pID3DDevice->SetRenderState(D3DRS_POINTSCALE_B, *(DWORD*)(&tmp)); - pID3DDevice->SetRenderState(D3DRS_POINTSIZE_MIN, *(DWORD*)(&tmp)); + pID3DDevice->SetRenderState(D3DRS_POINTSCALE_A, F2DW(tmp)); + pID3DDevice->SetRenderState(D3DRS_POINTSCALE_B, F2DW(tmp)); + pID3DDevice->SetRenderState(D3DRS_POINTSIZE_MIN, F2DW(tmp)); tmp=0.0f; - pID3DDevice->SetRenderState(D3DRS_POINTSCALE_C, *(DWORD*)(&tmp)); + pID3DDevice->SetRenderState(D3DRS_POINTSCALE_C, F2DW(tmp)); if (!vertices) { @@ -2246,6 +2256,64 @@ void CD3D9Driver::setBasicRenderStates(const SMaterial& material, const SMateria pID3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, flag); } + if (queryFeature(EVDF_BLEND_OPERATIONS) && + (resetAllRenderstates|| lastmaterial.BlendOperation != material.BlendOperation)) + { + if (EBO_NONE) + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + else + { + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + switch (material.BlendOperation) + { + case EBO_SUBTRACT: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT); + break; + case EBO_REVSUBTRACT: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT); + break; + case EBO_MIN: + case EBO_MIN_FACTOR: + case EBO_MIN_ALPHA: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MIN); + break; + case EBO_MAX: + case EBO_MAX_FACTOR: + case EBO_MAX_ALPHA: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX); + break; + default: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + break; + } + } + } + + // Polygon offset + if (queryFeature(EVDF_POLYGON_OFFSET) && (resetAllRenderstates || + lastmaterial.PolygonOffsetDirection != material.PolygonOffsetDirection || + lastmaterial.PolygonOffsetFactor != material.PolygonOffsetFactor)) + { + if (material.PolygonOffsetFactor) + { + if (material.PolygonOffsetDirection==EPO_BACK) + { + pID3DDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(1.f)); + pID3DDevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW((FLOAT)material.PolygonOffsetFactor)); + } + else + { + pID3DDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(-1.f)); + pID3DDevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW((FLOAT)-material.PolygonOffsetFactor)); + } + } + else + { + pID3DDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, 0); + pID3DDevice->SetRenderState(D3DRS_DEPTHBIAS, 0); + } + } + // Anti Aliasing if (resetAllRenderstates || lastmaterial.AntiAliasing != material.AntiAliasing) { @@ -2282,7 +2350,7 @@ void CD3D9Driver::setBasicRenderStates(const SMaterial& material, const SMateria // thickness if (resetAllRenderstates || lastmaterial.Thickness != material.Thickness) { - pID3DDevice->SetRenderState(D3DRS_POINTSIZE, *((DWORD*)&material.Thickness)); + pID3DDevice->SetRenderState(D3DRS_POINTSIZE, F2DW(material.Thickness)); } // texture address mode @@ -2291,7 +2359,7 @@ void CD3D9Driver::setBasicRenderStates(const SMaterial& material, const SMateria if (resetAllRenderstates || lastmaterial.TextureLayer[st].LODBias != material.TextureLayer[st].LODBias) { const float tmp = material.TextureLayer[st].LODBias * 0.125f; - pID3DDevice->SetSamplerState(st, D3DSAMP_MIPMAPLODBIAS, *(DWORD*)(&tmp)); + pID3DDevice->SetSamplerState(st, D3DSAMP_MIPMAPLODBIAS, F2DW(tmp)); } if (resetAllRenderstates || lastmaterial.TextureLayer[st].TextureWrapU != material.TextureLayer[st].TextureWrapU) @@ -2354,13 +2422,6 @@ void CD3D9Driver::setRenderStatesStencilShadowMode(bool zfail) setActiveTexture(3,0); pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(3, D3DTSS_ALPHAOP, D3DTOP_DISABLE); pID3DDevice->SetFVF(D3DFVF_XYZ); LastVertexType = (video::E_VERTEX_TYPE)(-1); @@ -2433,11 +2494,6 @@ void CD3D9Driver::setRenderStatesStencilFillMode(bool alpha) pID3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(3, D3DTSS_ALPHAOP, D3DTOP_DISABLE); pID3DDevice->SetRenderState(D3DRS_STENCILREF, 0x1); pID3DDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL); @@ -2452,23 +2508,19 @@ void CD3D9Driver::setRenderStatesStencilFillMode(bool alpha) Transformation3DChanged = false; + pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); if (alpha) { - pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE ); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); - pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); } else { - pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE ); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); - pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); } } @@ -2507,11 +2559,6 @@ void CD3D9Driver::setRenderStates2DMode(bool alpha, bool texture, bool alphaChan // fix everything that is wrongly set by InitMaterial2D default pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(3, D3DTSS_ALPHAOP, D3DTOP_DISABLE); pID3DDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE ); } @@ -2582,7 +2629,6 @@ void CD3D9Driver::setRenderStates2DMode(bool alpha, bool texture, bool alphaChan { pID3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); pID3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); } } @@ -2592,17 +2638,18 @@ void CD3D9Driver::setRenderStates2DMode(bool alpha, bool texture, bool alphaChan pID3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); pID3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); pID3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); if (alpha) { - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); } else { - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); } } @@ -2817,11 +2864,11 @@ void CD3D9Driver::setFog(SColor color, E_FOG_TYPE fogType, f32 start, if (fogType==EFT_FOG_LINEAR) { - pID3DDevice->SetRenderState(D3DRS_FOGSTART, *(DWORD*)(&start)); - pID3DDevice->SetRenderState(D3DRS_FOGEND, *(DWORD*)(&end)); + pID3DDevice->SetRenderState(D3DRS_FOGSTART, F2DW(start)); + pID3DDevice->SetRenderState(D3DRS_FOGEND, F2DW(end)); } else - pID3DDevice->SetRenderState(D3DRS_FOGDENSITY, *(DWORD*)(&density)); + pID3DDevice->SetRenderState(D3DRS_FOGDENSITY, F2DW(density)); if(!pixelFog) pID3DDevice->SetRenderState(D3DRS_RANGEFOGENABLE, rangeFog); @@ -3109,9 +3156,14 @@ ITexture* CD3D9Driver::addRenderTargetTexture(const core::dimension2d& size const io::path& name, const ECOLOR_FORMAT format) { - ITexture* tex = new CD3D9Texture(this, size, name, format); + CD3D9Texture* tex = new CD3D9Texture(this, size, name, format); if (tex) { + if (!tex->Texture) + { + tex->drop(); + return 0; + } checkDepthBuffer(tex); addTexture(tex); tex->drop(); @@ -3447,13 +3499,13 @@ namespace video #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ //! creates a video driver -IVideoDriver* createDirectX9Driver(const core::dimension2d& screenSize, - HWND window, u32 bits, bool fullscreen, bool stencilbuffer, - io::IFileSystem* io, bool pureSoftware, bool highPrecisionFPU, - bool vsync, u8 antiAlias, u32 displayAdapter) +IVideoDriver* createDirectX9Driver(const SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window) { - CD3D9Driver* dx9 = new CD3D9Driver(screenSize, window, fullscreen, stencilbuffer, io, pureSoftware); - if (!dx9->initDriver(screenSize, window, bits, fullscreen, pureSoftware, highPrecisionFPU, vsync, antiAlias, displayAdapter)) + const bool pureSoftware = false; + CD3D9Driver* dx9 = new CD3D9Driver(params.WindowSize, window, params.Fullscreen, params.Stencilbuffer, io, pureSoftware); + if (!dx9->initDriver(params.WindowSize, window, params.Bits, params.Fullscreen, pureSoftware, params.HighPrecisionFPU, + params.Vsync, params.AntiAlias, params.DisplayAdapter)) { dx9->drop(); dx9 = 0; diff --git a/source/Irrlicht/CD3D9Driver.h b/source/Irrlicht/CD3D9Driver.h index d29bf138..390db1f4 100644 --- a/source/Irrlicht/CD3D9Driver.h +++ b/source/Irrlicht/CD3D9Driver.h @@ -121,7 +121,7 @@ namespace video //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ - virtual void createOcclusionQuery(scene::ISceneNode* node, + virtual void addOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh=0); //! Remove occlusion query. diff --git a/source/Irrlicht/CD3D9MaterialRenderer.h b/source/Irrlicht/CD3D9MaterialRenderer.h index bc87b471..d0c411ee 100644 --- a/source/Irrlicht/CD3D9MaterialRenderer.h +++ b/source/Irrlicht/CD3D9MaterialRenderer.h @@ -21,8 +21,36 @@ namespace irr namespace video { +namespace +{ D3DMATRIX UnitMatrixD3D9; D3DMATRIX SphereMapMatrixD3D9; +inline void setTextureColorStage(IDirect3DDevice9* dev, DWORD i, + DWORD arg1, DWORD op, DWORD arg2) +{ + dev->SetTextureStageState(i, D3DTSS_COLOROP, op); + dev->SetTextureStageState(i, D3DTSS_COLORARG1, arg1); + dev->SetTextureStageState(i, D3DTSS_COLORARG2, arg2); +} +inline void setTextureColorStage(IDirect3DDevice9* dev, DWORD i, DWORD arg1) +{ + dev->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + dev->SetTextureStageState(i, D3DTSS_COLORARG1, arg1); +} + +inline void setTextureAlphaStage(IDirect3DDevice9* dev, DWORD i, + DWORD arg1, DWORD op, DWORD arg2) +{ + dev->SetTextureStageState(i, D3DTSS_ALPHAOP, op); + dev->SetTextureStageState(i, D3DTSS_ALPHAARG1, arg1); + dev->SetTextureStageState(i, D3DTSS_ALPHAARG2, arg2); +} +inline void setTextureAlphaStage(IDirect3DDevice9* dev, DWORD i, DWORD arg1) +{ + dev->SetTextureStageState(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + dev->SetTextureStageState(i, D3DTSS_ALPHAARG1, arg1); +} +} // anonymous namespace //! Base class for all internal D3D9 material renderers class CD3D9MaterialRenderer : public IMaterialRenderer @@ -71,10 +99,8 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - pID3DDevice->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); } pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); @@ -116,33 +142,25 @@ public: pID3DDevice->SetRenderState(D3DRS_DESTBLEND, getD3DBlend ( dstFact ) ); } - pID3DDevice->SetTextureStageState (0, D3DTSS_COLOROP, getD3DModulate ( modulate ) ); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, getD3DModulate(modulate), D3DTA_DIFFUSE); if ( textureBlendFunc_hasAlpha ( srcFact ) || textureBlendFunc_hasAlpha ( dstFact ) ) { if (alphaSource==EAS_VERTEX_COLOR) { - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_DIFFUSE); } else if (alphaSource==EAS_TEXTURE) { - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_TEXTURE); } else { - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); + setTextureAlphaStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); } } - else - { - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); - } pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); @@ -215,9 +233,7 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + setTextureColorStage(pID3DDevice, 0, D3DTA_TEXTURE); pID3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0); pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDDIFFUSEALPHA); @@ -243,10 +259,8 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - pID3DDevice->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); @@ -279,15 +293,11 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - pID3DDevice->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - pID3DDevice->SetTextureStageState (0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_DIFFUSE); pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - pID3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); @@ -319,15 +329,11 @@ public: if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates || material.MaterialTypeParam != lastMaterial.MaterialTypeParam ) { - pID3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_CURRENT ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); - - pID3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE ); - pID3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_CURRENT); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_TEXTURE); + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); @@ -368,14 +374,11 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_CURRENT ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); - pID3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_CURRENT); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE ); - pID3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); @@ -418,33 +421,26 @@ public: if (material.MaterialType >= EMT_LIGHTMAP_LIGHTING) { // with lighting - pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); } else { - pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + setTextureColorStage(pID3DDevice, 0, D3DTA_TEXTURE); } - pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); pID3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); - if (material.MaterialType == EMT_LIGHTMAP_ADD) - pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD); - else - 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 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M2) - pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE2X); - else - pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); - - pID3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); - pID3DDevice->SetTextureStageState (1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + setTextureColorStage(pID3DDevice, 1, + D3DTA_TEXTURE, + (material.MaterialType == EMT_LIGHTMAP_ADD)? + D3DTOP_ADD: + (material.MaterialType == EMT_LIGHTMAP_M4 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M4)? + D3DTOP_MODULATE4X: + (material.MaterialType == EMT_LIGHTMAP_M2 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M2)? + D3DTOP_MODULATE2X: + D3DTOP_MODULATE, + D3DTA_CURRENT); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); } @@ -468,16 +464,10 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - pID3DDevice->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - - pID3DDevice->SetTextureStageState (1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED); - pID3DDevice->SetTextureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState (1, D3DTSS_COLORARG2, D3DTA_CURRENT); - pID3DDevice->SetTextureStageState (1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + setTextureColorStage(pID3DDevice, 1, + D3DTA_TEXTURE, D3DTOP_ADDSIGNED, D3DTA_CURRENT); pID3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); } @@ -500,10 +490,8 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - pID3DDevice->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); @@ -538,14 +526,11 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - pID3DDevice->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); - pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + setTextureColorStage(pID3DDevice, 1, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_CURRENT); pID3DDevice->SetTransform( D3DTS_TEXTURE1, &SphereMapMatrixD3D9 ); pID3DDevice->SetTextureStageState( 1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); @@ -578,14 +563,12 @@ public: { if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - - pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); - pID3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); - pID3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_DIFFUSE); + setTextureColorStage(pID3DDevice, 1, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_CURRENT); + setTextureAlphaStage(pID3DDevice, 1, D3DTA_CURRENT); pID3DDevice->SetTransform(D3DTS_TEXTURE1, &SphereMapMatrixD3D9 ); pID3DDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); diff --git a/source/Irrlicht/CD3D9ShaderMaterialRenderer.h b/source/Irrlicht/CD3D9ShaderMaterialRenderer.h index f04f7f76..3cc16557 100644 --- a/source/Irrlicht/CD3D9ShaderMaterialRenderer.h +++ b/source/Irrlicht/CD3D9ShaderMaterialRenderer.h @@ -12,7 +12,6 @@ #if defined(__BORLANDC__) || defined (__BCPLUSPLUS__) #include "irrMath.h" // needed by borland for sqrtf define #endif -#include #include #include "IMaterialRenderer.h" diff --git a/source/Irrlicht/CDefaultSceneNodeAnimatorFactory.cpp b/source/Irrlicht/CDefaultSceneNodeAnimatorFactory.cpp index f3a879fe..d6c681ef 100644 --- a/source/Irrlicht/CDefaultSceneNodeAnimatorFactory.cpp +++ b/source/Irrlicht/CDefaultSceneNodeAnimatorFactory.cpp @@ -42,12 +42,14 @@ CDefaultSceneNodeAnimatorFactory::CDefaultSceneNodeAnimatorFactory(ISceneManager CursorControl->grab(); } + CDefaultSceneNodeAnimatorFactory::~CDefaultSceneNodeAnimatorFactory() { if (CursorControl) CursorControl->drop(); } + //! creates a scene node animator based on its type id ISceneNodeAnimator* CDefaultSceneNodeAnimatorFactory::createSceneNodeAnimator(ESCENE_NODE_ANIMATOR_TYPE type, ISceneNode* target) { diff --git a/source/Irrlicht/CFileSystem.cpp b/source/Irrlicht/CFileSystem.cpp index 0cbaa209..35fd1ae9 100644 --- a/source/Irrlicht/CFileSystem.cpp +++ b/source/Irrlicht/CFileSystem.cpp @@ -58,14 +58,6 @@ CFileSystem::CFileSystem() //! reset current working directory getWorkingDirectory(); -#ifdef __IRR_COMPILE_WITH_ZIP_ARCHIVE_LOADER_ - ArchiveLoader.push_back(new CArchiveLoaderZIP(this)); -#endif - -#ifdef __IRR_COMPILE_WITH_MOUNT_ARCHIVE_LOADER_ - ArchiveLoader.push_back(new CArchiveLoaderMount(this)); -#endif - #ifdef __IRR_COMPILE_WITH_PAK_ARCHIVE_LOADER_ ArchiveLoader.push_back(new CArchiveLoaderPAK(this)); #endif @@ -82,6 +74,14 @@ CFileSystem::CFileSystem() ArchiveLoader.push_back(new CArchiveLoaderWAD(this)); #endif +#ifdef __IRR_COMPILE_WITH_MOUNT_ARCHIVE_LOADER_ + ArchiveLoader.push_back(new CArchiveLoaderMount(this)); +#endif + +#ifdef __IRR_COMPILE_WITH_ZIP_ARCHIVE_LOADER_ + ArchiveLoader.push_back(new CArchiveLoaderZIP(this)); +#endif + } @@ -171,6 +171,20 @@ void CFileSystem::addArchiveLoader(IArchiveLoader* loader) ArchiveLoader.push_back(loader); } +//! Returns the total number of archive loaders added. +u32 CFileSystem::getArchiveLoaderCount() const +{ + return ArchiveLoader.size(); +} + +//! Gets the archive loader by index. +IArchiveLoader* CFileSystem::getArchiveLoader(u32 index) const +{ + if (index < ArchiveLoader.size()) + return ArchiveLoader[index]; + else + return 0; +} //! move the hirarchy of the filesystem. moves sourceIndex relative up or down bool CFileSystem::moveFileArchive(u32 sourceIndex, s32 relative) @@ -202,28 +216,18 @@ bool CFileSystem::addFileArchive(const io::path& filename, bool ignoreCase, { IFileArchive* archive = 0; bool ret = false; - u32 i; - // check if the archive was already loaded - for (i = 0; i < FileArchives.size(); ++i) - { - // TODO: This should go into a path normalization method - // We need to check for directory names with trailing slash and without - const core::stringc absPath = getAbsolutePath(filename); - const core::stringc arcPath = FileArchives[i]->getFileList()->getPath(); - if ((absPath == arcPath) || ((absPath+"/") == arcPath)) - { - if (password.size()) - FileArchives[i]->Password=password; - return true; - } - } + // see if archive is already added + if (changeArchivePassword(filename, password)) + return true; + + s32 i; // do we know what type it should be? if (archiveType == EFAT_UNKNOWN || archiveType == EFAT_FOLDER) { // try to load archive based on file name - for (i = 0; i < ArchiveLoader.size(); ++i) + for (i = ArchiveLoader.size()-1; i >=0 ; --i) { if (ArchiveLoader[i]->isALoadableFileFormat(filename)) { @@ -239,7 +243,7 @@ bool CFileSystem::addFileArchive(const io::path& filename, bool ignoreCase, io::IReadFile* file = createAndOpenFile(filename); if (file) { - for (i = 0; i < ArchiveLoader.size(); ++i) + for (i = ArchiveLoader.size()-1; i >= 0; --i) { file->seek(0); if (ArchiveLoader[i]->isALoadableFileFormat(file)) @@ -260,7 +264,7 @@ bool CFileSystem::addFileArchive(const io::path& filename, bool ignoreCase, io::IReadFile* file = 0; - for (i = 0; i < ArchiveLoader.size(); ++i) + for (i = ArchiveLoader.size()-1; i >= 0; --i) { if (ArchiveLoader[i]->isALoadableFileFormat(archiveType)) { @@ -310,6 +314,105 @@ bool CFileSystem::addFileArchive(const io::path& filename, bool ignoreCase, return ret; } +// don't expose! +bool CFileSystem::changeArchivePassword(const path& filename, const core::stringc& password) +{ + for (s32 idx = 0; idx < (s32)FileArchives.size(); ++idx) + { + // TODO: This should go into a path normalization method + // We need to check for directory names with trailing slash and without + const core::stringc absPath = getAbsolutePath(filename); + const core::stringc arcPath = FileArchives[idx]->getFileList()->getPath(); + if ((absPath == arcPath) || ((absPath+"/") == arcPath)) + { + if (password.size()) + FileArchives[idx]->Password=password; + return true; + } + } + + return false; +} + +bool CFileSystem::addFileArchive(IReadFile* file, bool ignoreCase, bool ignorePaths, + E_FILE_ARCHIVE_TYPE archiveType, const core::stringc& password) +{ + if (!file || archiveType == EFAT_FOLDER) + return false; + + if (file) + { + if (changeArchivePassword(file->getFileName(), password)) + return true; + + IFileArchive* archive = 0; + s32 i; + + if (archiveType == EFAT_UNKNOWN) + { + // try to load archive based on file name + for (i = ArchiveLoader.size()-1; i >=0 ; --i) + { + if (ArchiveLoader[i]->isALoadableFileFormat(file->getFileName())) + { + archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths); + if (archive) + break; + } + } + + // try to load archive based on content + if (!archive) + { + for (i = ArchiveLoader.size()-1; i >= 0; --i) + { + file->seek(0); + if (ArchiveLoader[i]->isALoadableFileFormat(file)) + { + file->seek(0); + archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths); + if (archive) + break; + } + } + } + } + else + { + // try to open archive based on archive loader type + for (i = ArchiveLoader.size()-1; i >= 0; --i) + { + if (ArchiveLoader[i]->isALoadableFileFormat(archiveType)) + { + // attempt to open archive + file->seek(0); + if (ArchiveLoader[i]->isALoadableFileFormat(file)) + { + file->seek(0); + archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths); + if (archive) + break; + } + } + } + } + + if (archive) + { + FileArchives.push_back(archive); + if (password.size()) + archive->Password=password; + return true; + } + else + { + os::Printer::log("Could not create archive for", file->getFileName(), ELL_ERROR); + } + } + + return false; +} + //! removes an archive from the file system. bool CFileSystem::removeFileArchive(u32 index) @@ -851,8 +954,12 @@ IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(IReadFile* file) IXMLWriter* CFileSystem::createXMLWriter(const io::path& filename) { IWriteFile* file = createAndWriteFile(filename); - IXMLWriter* writer = createXMLWriter(file); - file->drop(); + IXMLWriter* writer = 0; + if (file) + { + writer = createXMLWriter(file); + file->drop(); + } return writer; } diff --git a/source/Irrlicht/CFileSystem.h b/source/Irrlicht/CFileSystem.h index 0e1658ec..e6478760 100644 --- a/source/Irrlicht/CFileSystem.h +++ b/source/Irrlicht/CFileSystem.h @@ -51,12 +51,24 @@ public: E_FILE_ARCHIVE_TYPE archiveType = EFAT_UNKNOWN, const core::stringc& password=""); + //! Adds an archive to the file system. + virtual bool addFileArchive(IReadFile* file, bool ignoreCase=true, + bool ignorePaths=true, + E_FILE_ARCHIVE_TYPE archiveType=EFAT_UNKNOWN, + const core::stringc& password=""); + //! move the hirarchy of the filesystem. moves sourceIndex relative up or down virtual bool moveFileArchive( u32 sourceIndex, s32 relative ); //! Adds an external archive loader to the engine. virtual void addArchiveLoader(IArchiveLoader* loader); + //! Returns the total number of archive loaders added. + virtual u32 getArchiveLoaderCount() const; + + //! Gets the archive loader by index. + virtual IArchiveLoader* getArchiveLoader(u32 index) const; + //! gets the file archive count virtual u32 getFileArchiveCount() const; @@ -130,6 +142,9 @@ public: private: + // don't expose, needs refactoring + bool changeArchivePassword(const path& filename, const core::stringc& password); + //! Currently used FileSystemType EFileSystemType FileSystemType; //! WorkingDirectory for Native and Virtual filesystems diff --git a/source/Irrlicht/CGUIColorSelectDialog.cpp b/source/Irrlicht/CGUIColorSelectDialog.cpp index 8ad64cb8..cb7e3aed 100644 --- a/source/Irrlicht/CGUIColorSelectDialog.cpp +++ b/source/Irrlicht/CGUIColorSelectDialog.cpp @@ -16,6 +16,7 @@ #include "IFileList.h" #include "os.h" #include "CImage.h" +#include "fast_atof.h" namespace irr { @@ -25,26 +26,30 @@ namespace gui const s32 CSD_WIDTH = 350; const s32 CSD_HEIGHT = 300; -struct sTemplate +namespace +{ + +struct subElementPredefines { const wchar_t *pre; const wchar_t *init; - const wchar_t *pos; + const wchar_t *post; int x, y; int range_down ,range_up; }; -static const sTemplate Template [] = +static const subElementPredefines Template [] = { - { L"A:", L"0", 0,20,165, 0, 255 }, + { L"A:", L"0", 0,50,165, 0, 255 }, { L"R:", L"0", 0,20,205, 0, 255 }, { L"G:", L"0", 0,20,230, 0, 255 }, { L"B:", L"0", 0,20,255, 0, 255 }, - { L"H:", L"0", L"°",180,205, 0, 360 }, - { L"S:", L"0", L"%",180,230, 0, 100 }, - { L"L:", L"0", L"%",180,255, 0, 100 }, + { L"H:", L"0", L"°",80,205, 0, 360 }, + { L"S:", L"0", L"%",80,230, 0, 100 }, + { L"L:", L"0", L"%",80,255, 0, 100 }, }; +} //! constructor CGUIColorSelectDialog::CGUIColorSelectDialog(const wchar_t* title, IGUIEnvironment* environment, IGUIElement* parent, s32 id) @@ -107,7 +112,7 @@ CGUIColorSelectDialog::CGUIColorSelectDialog(const wchar_t* title, IGUIEnvironme ColorRing.Control->setSubElement(true); ColorRing.Control->grab(); - for ( u32 i = 0; i != sizeof (Template) / sizeof ( sTemplate ); ++i ) + for ( u32 i = 0; i != sizeof (Template) / sizeof ( subElementPredefines ); ++i ) { if ( Template[i].pre ) { @@ -119,41 +124,28 @@ CGUIColorSelectDialog::CGUIColorSelectDialog(const wchar_t* title, IGUIEnvironme t->setSubElement(true); } - if ( Template[i].pos ) + if ( Template[i].post ) { - r.UpperLeftCorner.X = Template[i].x + 52; + r.UpperLeftCorner.X = Template[i].x + 56; r.UpperLeftCorner.Y = Template[i].y; r.LowerRightCorner.X = r.UpperLeftCorner.X + 15; r.LowerRightCorner.Y = r.UpperLeftCorner.Y + 20; - IGUIElement *t = Environment->addStaticText( Template[i].pos, r, false, false, this); + IGUIElement *t = Environment->addStaticText( Template[i].post, r, false, false, this); t->setSubElement(true); } - SBatteryItem item; - item.Incoming=0.f; - item.Outgoing=0.f; - r.UpperLeftCorner.X = Template[i].x + 15; - r.UpperLeftCorner.Y = Template[i].y; - r.LowerRightCorner.X = r.UpperLeftCorner.X + 35; + r.UpperLeftCorner.Y = Template[i].y-2; + r.LowerRightCorner.X = r.UpperLeftCorner.X + 40; r.LowerRightCorner.Y = r.UpperLeftCorner.Y + 20; - item.Edit = Environment->addEditBox( Template[i].init, r, true, this); - item.Edit->setSubElement(true); - item.Edit->grab(); + gui::IGUISpinBox* spin = Environment->addSpinBox( Template[i].init, r, true, this); + spin->setSubElement(true); + spin->setDecimalPlaces(0); + spin->setRange((f32)Template[i].range_down, (f32)Template[i].range_up); + spin->grab(); - r.UpperLeftCorner.X = Template[i].x + 70; - r.UpperLeftCorner.Y = Template[i].y + 4; - r.LowerRightCorner.X = r.UpperLeftCorner.X + 60; - r.LowerRightCorner.Y = r.UpperLeftCorner.Y + 12; - - item.Scrollbar = Environment->addScrollBar(true, r, this); - item.Scrollbar->grab (); - item.Scrollbar->setSubElement(true); - item.Scrollbar->setMax ( Template[i].range_up - Template[i].range_down ); - item.Scrollbar->setSmallStep ( 1 ); - - Battery.push_back ( item ); + Battery.push_back(spin); } bringToFront(CancelButton); @@ -173,11 +165,8 @@ CGUIColorSelectDialog::~CGUIColorSelectDialog() if (CancelButton) CancelButton->drop(); - for ( u32 i = 0; i != Battery.size ();++i ) - { - Battery[i].Edit->drop(); - Battery[i].Scrollbar->drop(); - } + for (u32 i = 0; i != Battery.size(); ++i) + Battery[i]->drop(); if (ColorRing.Control) ColorRing.Control->drop(); @@ -195,10 +184,10 @@ void CGUIColorSelectDialog::buildColorRing( const core::dimension2d & dim, const s32 radiusOut = ( d.Width / 2 ) - 4; const s32 fullR2 = radiusOut * radiusOut; - video::SColor rgb(0xFF000000); + video::SColorf rgb(0,0,0); video::SColorHSL hsl; - hsl.Luminance = 0.5f; - hsl.Saturation = 1.f; + hsl.Luminance = 50; + hsl.Saturation = 100; core::position2d p; for ( p.Y = -radiusOut; p.Y <= radiusOut; p.Y += 1 ) @@ -221,68 +210,58 @@ void CGUIColorSelectDialog::buildColorRing( const core::dimension2d & dim, // normalize, dotproduct = xnorm const f32 xn = r == 0.f ? 0.f : -p.X * core::reciprocal(r); - hsl.Hue = acosf(xn); + hsl.Hue = acosf(xn)*core::RADTODEG; if ( p.Y > 0 ) - hsl.Hue = (2.f * core::PI ) - hsl.Hue; - - hsl.Hue -= core::PI / 2.f; + hsl.Hue = 360 - hsl.Hue; + hsl.Hue -= 90; const f32 rTest = r / radiusOut; -/* - if ( rTest < 0.25f ) +#if 0 + if (rTest < 0.33f) { - hsl.Luminance = rTest / 0.25f; + // luminance from 0 to 50 + hsl.Luminance = 50*(rTest/0.33); hsl.Saturation = 0.f; - hsl.toRGB ( rgb ); - *dst = rgb.color; - } - else - if ( rTest < 0.4f ) - { - hsl.Saturation = ( rTest - 0.25f ) / 0.15f; - hsl.Luminance = 1.f - ( hsl.Saturation / 2.4f ); - hsl.Luminance = 0.5f; - hsl.toRGB ( rgb ); - // *dst = rgb.color; - } - else - if ( rTest < 0.75f ) - { - hsl.Luminance = 0.5f; - hsl.Saturation = 1.f; - hsl.toRGB ( rgb ); - *dst = rgb.color; - } - else - if ( rTest < 0.98f ) - { - hsl.Luminance = 0.5f - ( ( rTest - 0.75f ) / 0.75f ); - hsl.Saturation = 1.f; - hsl.toRGB ( rgb ); - *dst = rgb.color; - } -*/ - - if ( rTest >= 0.5f ) - { - hsl.Luminance = 0.5f; - hsl.Saturation = 1.f; hsl.toRGB(rgb); - - if ( rTest <= 0.55f ) - { - const u32 alpha = (u32) ( (rTest - 0.5f ) * ( 255.f / 0.05f ) ); - rgb.setAlpha(alpha); - } - else if ( rTest >= 0.95f ) - { - const u32 alpha = (u32) ( (rTest - 0.95f ) * ( 255.f / 0.05f ) ); - rgb.setAlpha(255-alpha); - } - else - rgb.setAlpha(255); - RawTexture->setPixel(4+p.X+radiusOut, 4+p.Y+radiusOut, rgb); } + else + if ( rTest < 0.66f ) + { + // saturation from 0 to 100 + hsl.Saturation = 100*(( rTest - 0.33f ) / 0.33f); + hsl.Luminance = 50; + hsl.toRGB(rgb); + } + else + { + // luminance from 50 to 100 + hsl.Luminance = 100*(0.5f + ( ( rTest - 0.66f ) / .66f )); + hsl.Saturation = 100; + hsl.toRGB(rgb); + } + // borders should be slightly transparent + if ( rTest >= 0.95f ) + rgb.a = (1.f-rTest)*20; + else + rgb.a=1.f; +#else + if ( rTest > 0.5f ) + { + hsl.Saturation = 100; + hsl.Luminance = 50; + hsl.toRGB(rgb); + } + // borders should be slightly transparent + if ( rTest < 0.5f ) + rgb.a = 0; + else if ( rTest >= 0.95f ) + rgb.a = (1.f-rTest)*20; + else if ( rTest <= 0.55f ) + rgb.a = (rTest-0.5f)*20; + else + rgb.a=1.f; +#endif + RawTexture->setPixel(4+p.X+radiusOut, 4+p.Y+radiusOut, rgb.toSColor()); } } } @@ -319,43 +298,63 @@ bool CGUIColorSelectDialog::OnEvent(const SEvent& event) case EET_GUI_EVENT: switch(event.GUIEvent.EventType) { - case EGET_SCROLL_BAR_CHANGED: + case EGET_SPINBOX_CHANGED: { for ( u32 i = 0; i!= Battery.size (); ++i ) { - if ( event.GUIEvent.Caller == Battery[i].Scrollbar ) + if ( event.GUIEvent.Caller == Battery[i] ) { - const s32 value = Template[i].range_down + Battery[i].Scrollbar->getPos(); - Battery[i].Edit->setText(core::stringw(value).c_str()); + if (i<4) + { + video::SColor rgb((u32)Battery[0]->getValue(), (u32)Battery[1]->getValue(), + (u32)Battery[2]->getValue(), (u32)Battery[3]->getValue()); + video::SColorHSL hsl; + video::SColorf rgb2(rgb); + hsl.fromRGB(rgb2); + Battery[4]->setValue(hsl.Hue); + Battery[5]->setValue(hsl.Saturation); + Battery[6]->setValue(hsl.Luminance); + } + else + { + video::SColorHSL hsl(Battery[4]->getValue(), Battery[5]->getValue(), + Battery[6]->getValue()); + video::SColorf rgb2; + hsl.toRGB(rgb2); + video::SColor rgb = rgb2.toSColor(); + Battery[1]->setValue((f32)rgb.getRed()); + Battery[2]->setValue((f32)rgb.getGreen()); + Battery[3]->setValue((f32)rgb.getBlue()); + } } } return true; } - case EGET_ELEMENT_FOCUS_LOST: - Dragging = false; - break; - case EGET_BUTTON_CLICKED: - if (event.GUIEvent.Caller == CloseButton || - event.GUIEvent.Caller == CancelButton) - { - sendCancelEvent(); - remove(); - return true; - } - else - if (event.GUIEvent.Caller == OKButton) - { - sendSelectedEvent(); - remove(); - return true; - } - break; + case EGET_ELEMENT_FOCUS_LOST: + Dragging = false; + break; + case EGET_BUTTON_CLICKED: + if (event.GUIEvent.Caller == CloseButton || + event.GUIEvent.Caller == CancelButton) + { + sendCancelEvent(); + remove(); + return true; + } + else + if (event.GUIEvent.Caller == OKButton) + { + sendSelectedEvent(); + remove(); + return true; + } + break; - case EGET_LISTBOX_CHANGED: - case EGET_LISTBOX_SELECTED_AGAIN: - default: - break; + case EGET_LISTBOX_CHANGED: + case EGET_LISTBOX_SELECTED_AGAIN: + default: + break; } break; case EET_MOUSE_INPUT_EVENT: @@ -420,15 +419,41 @@ void CGUIColorSelectDialog::draw() font->draw(Text.c_str(), rect, skin->getColor(EGDC_ACTIVE_CAPTION), false, true, &AbsoluteClippingRect); } - IGUIFont* font = Environment->getBuiltInFont(); - if (font) - font->draw(L"+", core::rect(20,20,50,50), video::SColor(), false, false, - &AbsoluteClippingRect); IGUIElement::draw(); + + // draw color selector after the window elements + core::vector2di pos(ColorRing.Control->getAbsolutePosition().UpperLeftCorner); + pos.X += ColorRing.Texture->getOriginalSize().Width/2; + pos.Y += ColorRing.Texture->getOriginalSize().Height/2; +#if 0 + const f32 h = Battery[4]->getValue(); + const f32 s = Battery[5]->getValue(); + const f32 l = Battery[6]->getValue(); + const f32 factor = 58.f*(((s==0)&&(l<50))?(l*0.33f/50):( + (s<100)?((.33f+(s*0.33f/100))):((0.66f+(l-50)*0.33f/50)))); + +#else + const f32 factor = 44; +#endif + pos.X += core::round32(sinf(Battery[4]->getValue()*core::DEGTORAD)*factor); + pos.Y -= core::round32(cosf(Battery[4]->getValue()*core::DEGTORAD)*factor); + Environment->getVideoDriver()->draw2DPolygon(pos, 4, 0xffffffff, 4); } +video::SColor CGUIColorSelectDialog::getColor() +{ + return video::SColor((u32)Battery[0]->getValue(), (u32)Battery[1]->getValue(), + (u32)Battery[2]->getValue(), (u32)Battery[3]->getValue()); +} + +video::SColorHSL CGUIColorSelectDialog::getColorHSL() +{ + return video::SColorHSL(Battery[4]->getValue(), Battery[5]->getValue(), + Battery[6]->getValue()); +} + //! sends the event that the file has been selected. void CGUIColorSelectDialog::sendSelectedEvent() { @@ -457,4 +482,3 @@ void CGUIColorSelectDialog::sendCancelEvent() } // end namespace irr #endif // _IRR_COMPILE_WITH_GUI_ - diff --git a/source/Irrlicht/CGUIColorSelectDialog.h b/source/Irrlicht/CGUIColorSelectDialog.h index 0edde5cf..3b1e0b75 100644 --- a/source/Irrlicht/CGUIColorSelectDialog.h +++ b/source/Irrlicht/CGUIColorSelectDialog.h @@ -10,8 +10,7 @@ #include "IGUIColorSelectDialog.h" #include "IGUIButton.h" -#include "IGUIEditBox.h" -#include "IGUIScrollBar.h" +#include "IGUISpinBox.h" #include "IGUIImage.h" #include "irrArray.h" @@ -37,6 +36,9 @@ namespace gui //! draws the element and its children virtual void draw(); + virtual video::SColor getColor(); + virtual video::SColorHSL getColorHSL(); + private: //! sends the event that the file has been selected. @@ -51,14 +53,7 @@ namespace gui IGUIButton* OKButton; IGUIButton* CancelButton; - struct SBatteryItem - { - f32 Incoming; - f32 Outgoing; - IGUIEditBox * Edit; - IGUIScrollBar *Scrollbar; - }; - core::array< SBatteryItem > Battery; + core::array Battery; struct SColorCircle { @@ -77,4 +72,3 @@ namespace gui #endif // _IRR_COMPILE_WITH_GUI_ #endif // __C_GUI_COLOR_SELECT_DIALOG_H_INCLUDED__ - diff --git a/source/Irrlicht/CGUIEnvironment.cpp b/source/Irrlicht/CGUIEnvironment.cpp index feec4bf6..6ed59628 100644 --- a/source/Irrlicht/CGUIEnvironment.cpp +++ b/source/Irrlicht/CGUIEnvironment.cpp @@ -696,7 +696,7 @@ IGUIElement* CGUIEnvironment::addGUIElement(const c8* elementName, IGUIElement* if (!parent) parent = this; - for (u32 i=0; i=0 && !node; --i) node = GUIElementFactoryList[i]->addGUIElement(elementName, parent); @@ -1520,10 +1520,9 @@ IGUISpriteBank* CGUIEnvironment::getSpriteBank(const io::path& filename) return Banks[index].Bank; // we don't have this sprite bank, we should load it - if (!FileSystem->existFile(b.NamedPath.getPath())) { - os::Printer::log("Could not load sprite bank because the file does not exist", b.NamedPath.getPath(), ELL_ERROR); + os::Printer::log("Could not load sprite bank because the file does not exist", b.NamedPath.getPath(), ELL_DEBUG); return 0; } diff --git a/source/Irrlicht/CGUIFont.cpp b/source/Irrlicht/CGUIFont.cpp index 0e2a5253..edfeb515 100644 --- a/source/Irrlicht/CGUIFont.cpp +++ b/source/Irrlicht/CGUIFont.cpp @@ -32,7 +32,9 @@ CGUIFont::CGUIFont(IGUIEnvironment *env, const io::path& filename) // don't grab environment, to avoid circular references Driver = Environment->getVideoDriver(); - SpriteBank = Environment->addEmptySpriteBank(filename); + SpriteBank = Environment->getSpriteBank(filename); + if (!SpriteBank) // could be default-font which has no file + SpriteBank = Environment->addEmptySpriteBank(filename); if (SpriteBank) SpriteBank->grab(); } @@ -51,7 +53,13 @@ CGUIFont::~CGUIFont() Driver->drop(); if (SpriteBank) + { SpriteBank->drop(); + // TODO: spritebank still exists in gui-environment and should be removed here when it's + // reference-count is 1. Just can't do that from here at the moment. + // But spritebank would not be able to drop textures anyway because those are in texture-cache + // where they can't be removed unless materials start reference-couting 'em. + } } @@ -61,6 +69,8 @@ bool CGUIFont::load(io::IXMLReader* xml) if (!SpriteBank) return false; + SpriteBank->clear(); + while (xml->read()) { if (io::EXN_ELEMENT == xml->getNodeType()) @@ -191,6 +201,9 @@ bool CGUIFont::load(io::IXMLReader* xml) void CGUIFont::setMaxHeight() { + if ( !SpriteBank ) + return; + MaxHeight = 0; s32 t; @@ -231,7 +244,7 @@ bool CGUIFont::load(const io::path& filename) //! load & prepare font from ITexture bool CGUIFont::loadTexture(video::IImage* image, const io::path& name) { - if (!image) + if (!image || !SpriteBank) return false; s32 lowerRightPositions = 0; @@ -293,6 +306,9 @@ bool CGUIFont::loadTexture(video::IImage* image, const io::path& name) void CGUIFont::readPositions(video::IImage* image, s32& lowerRightPositions) { + if (!SpriteBank ) + return; + const core::dimension2d size = image->getDimension(); video::SColor colorTopLeft = image->getPixel(0,0); @@ -467,7 +483,7 @@ void CGUIFont::draw(const core::stringw& text, const core::rect& position, bool hcenter, bool vcenter, const core::rect* clip ) { - if (!Driver) + if (!Driver || !SpriteBank) return; core::dimension2d textDimension; // NOTE: don't make this u32 or the >> later on can fail when the dimension width is < position width diff --git a/source/Irrlicht/CGUIListBox.cpp b/source/Irrlicht/CGUIListBox.cpp index 4d8e07f3..bb7c25ec 100644 --- a/source/Irrlicht/CGUIListBox.cpp +++ b/source/Irrlicht/CGUIListBox.cpp @@ -125,6 +125,23 @@ void CGUIListBox::removeItem(u32 id) } +s32 CGUIListBox::getItemAt(s32 xpos, s32 ypos) const +{ + if ( xpos < AbsoluteRect.UpperLeftCorner.X || xpos >= AbsoluteRect.LowerRightCorner.X + || ypos < AbsoluteRect.UpperLeftCorner.Y || ypos >= AbsoluteRect.LowerRightCorner.Y + ) + return -1; + + if ( ItemHeight == 0 ) + return -1; + + s32 item = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight; + if ( item < 0 || item >= (s32)Items.size()) + return -1; + + return item; +} + //! clears the list void CGUIListBox::clear() { @@ -438,15 +455,9 @@ void CGUIListBox::selectNew(s32 ypos, bool onlyHover) u32 now = os::Timer::getTime(); s32 oldSelected = Selected; - // find new selected item. - if (ItemHeight!=0) - Selected = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight; - - if (Selected<0) + Selected = getItemAt(AbsoluteRect.UpperLeftCorner.X, ypos); + if (Selected<0 && !Items.empty()) Selected = 0; - else - if ((u32)Selected >= Items.size()) - Selected = Items.size() - 1; recalculateScrollPos(); diff --git a/source/Irrlicht/CGUIListBox.h b/source/Irrlicht/CGUIListBox.h index ac1e101b..0577d88b 100644 --- a/source/Irrlicht/CGUIListBox.h +++ b/source/Irrlicht/CGUIListBox.h @@ -70,6 +70,9 @@ namespace gui //! removes an item from the list virtual void removeItem(u32 id); + //! get the the id of the item at the given absolute coordinates + virtual s32 getItemAt(s32 xpos, s32 ypos) const; + //! Sets the sprite bank which should be used to draw list icons. This font is set to the sprite bank of //! the built-in-font by default. A sprite can be displayed in front of every list item. //! An icon is an index within the icon sprite bank. Several default icons are available in the diff --git a/source/Irrlicht/CGUIScrollBar.cpp b/source/Irrlicht/CGUIScrollBar.cpp index b61708a4..628ac7e8 100644 --- a/source/Irrlicht/CGUIScrollBar.cpp +++ b/source/Irrlicht/CGUIScrollBar.cpp @@ -178,7 +178,11 @@ bool CGUIScrollBar::OnEvent(const SEvent& event) Dragging = false; if ( !Dragging ) + { + if ( event.MouseInput.Event == EMIE_MOUSE_MOVED ) + break; return isInside; + } if ( event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP ) Dragging = false; diff --git a/source/Irrlicht/CGUISpinBox.cpp b/source/Irrlicht/CGUISpinBox.cpp index add98fec..83b63c98 100644 --- a/source/Irrlicht/CGUISpinBox.cpp +++ b/source/Irrlicht/CGUISpinBox.cpp @@ -129,6 +129,8 @@ f32 CGUISpinBox::getValue() const void CGUISpinBox::setRange(f32 min, f32 max) { + if (maxgetSkin()) { BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE); @@ -256,6 +256,22 @@ bool CGUIStaticText::isWordWrapEnabled() const } +void CGUIStaticText::setRightToLeft(bool rtl) +{ + if (RightToLeft != rtl) + { + RightToLeft = rtl; + breakText(); + } +} + + +bool CGUIStaticText::isRightToLeft() const +{ + return RightToLeft; +} + + //! Breaks the single text line. void CGUIStaticText::breakText() { @@ -275,87 +291,206 @@ void CGUIStaticText::breakText() LastBreakFont = font; - core::stringw line; - core::stringw word; - core::stringw whitespace; - s32 size = Text.size(); - s32 length = 0; - s32 elWidth = RelativeRect.getWidth() - 6; - wchar_t c; - - for (s32 i=0; igetDimension(whitespace.c_str()).Width; - const s32 wordlgth = font->getDimension(word.c_str()).Width; - - if (length && (length + wordlgth + whitelgth > elWidth)) + lineBreak = true; + if (Text[i+1] == L'\n') // Windows breaks { - // break to next line - BrokenText.push_back(line); - length = wordlgth; - line = word; + Text.erase(i+1); + --size; } - else + c = '\0'; + } + else if (c == L'\n') // Unix breaks + { + lineBreak = true; + c = '\0'; + } + + if (c==L' ' || c==0 || i==(size-1)) + { + if (word.size()) + { + // here comes the next whitespace, look if + // we must break the last word to the next line. + const s32 whitelgth = font->getDimension(whitespace.c_str()).Width; + const s32 wordlgth = font->getDimension(word.c_str()).Width; + + if (wordlgth > elWidth) + { + // This word is too long to fit in the available space, look for + // the Unicode Soft HYphen (SHY / 00AD) character for a place to + // break the word at + int where = word.findFirst( wchar_t(0x00AD) ); + if (where != -1) + { + core::stringw first = word.subString(0, where); + core::stringw second = word.subString(where, word.size() - where); + BrokenText.push_back(line + first + L"-"); + const s32 secondLength = font->getDimension(second.c_str()).Width; + + length = secondLength; + line = second; + } + else + { + // No soft hyphen found, so there's nothing more we can do + // break to next line + if (length) + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + } + else if (length && (length + wordlgth + whitelgth > elWidth)) + { + // break to next line + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + else + { + // add word to line + line += whitespace; + line += word; + length += whitelgth + wordlgth; + } + + word = L""; + whitespace = L""; + } + + whitespace += c; + + // compute line break + if (lineBreak) { - // add word to line line += whitespace; line += word; - length += whitelgth + wordlgth; + BrokenText.push_back(line); + line = L""; + word = L""; + whitespace = L""; + length = 0; + } + } + else + { + // yippee this is a word.. + word += c; + } + } + + line += whitespace; + line += word; + BrokenText.push_back(line); + } + else + { + // right-to-left + core::stringw line; + core::stringw word; + core::stringw whitespace; + s32 size = Text.size(); + s32 length = 0; + s32 elWidth = RelativeRect.getWidth() - 6; + wchar_t c; + + for (s32 i=size; i>=0; --i) + { + c = Text[i]; + bool lineBreak = false; + + if (c == L'\r') // Mac or Windows breaks + { + lineBreak = true; + if ((i>0) && Text[i-1] == L'\n') // Windows breaks + { + Text.erase(i-1); + --size; + } + c = '\0'; + } + else if (c == L'\n') // Unix breaks + { + lineBreak = true; + c = '\0'; + } + + if (c==L' ' || c==0 || i==0) + { + if (word.size()) + { + // here comes the next whitespace, look if + // we must break the last word to the next line. + const s32 whitelgth = font->getDimension(whitespace.c_str()).Width; + const s32 wordlgth = font->getDimension(word.c_str()).Width; + + if (length && (length + wordlgth + whitelgth > elWidth)) + { + // break to next line + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + else + { + // add word to line + line = whitespace + line; + line = word + line; + length += whitelgth + wordlgth; + } + + word = L""; + whitespace = L""; } - word = L""; - whitespace = L""; + if (c != 0) + whitespace = core::stringw(&c, 1) + whitespace; + + // compute line break + if (lineBreak) + { + line = whitespace + line; + line = word + line; + BrokenText.push_back(line); + line = L""; + word = L""; + whitespace = L""; + length = 0; + } } - - whitespace += c; - - // compute line break - if (lineBreak) + else { - line += whitespace; - line += word; - BrokenText.push_back(line); - line = L""; - word = L""; - whitespace = L""; - length = 0; + // yippee this is a word.. + word = core::stringw(&c, 1) + word; } } - else - { - // yippee this is a word.. - word += c; - } - } - line += whitespace; - line += word; - BrokenText.push_back(line); + line = whitespace + line; + line = word + line; + BrokenText.push_back(line); + } } @@ -445,6 +580,7 @@ void CGUIStaticText::serializeAttributes(io::IAttributes* out, io::SAttributeRea out->addBool ("OverrideBGColorEnabled",OverrideBGColorEnabled); out->addBool ("WordWrap", WordWrap); out->addBool ("Background", Background); + out->addBool ("RightToLeft", RightToLeft); out->addBool ("RestrainTextInside", RestrainTextInside); out->addColor ("OverrideColor", OverrideColor); out->addColor ("BGColor", BGColor); @@ -465,6 +601,7 @@ void CGUIStaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeRe OverrideBGColorEnabled = in->getAttributeAsBool("OverrideBGColorEnabled"); setWordWrap(in->getAttributeAsBool("WordWrap")); Background = in->getAttributeAsBool("Background"); + RightToLeft = in->getAttributeAsBool("RightToLeft"); RestrainTextInside = in->getAttributeAsBool("RestrainTextInside"); OverrideColor = in->getAttributeAsColor("OverrideColor"); BGColor = in->getAttributeAsColor("BGColor"); diff --git a/source/Irrlicht/CGUIStaticText.h b/source/Irrlicht/CGUIStaticText.h index c22eecd9..077dec6a 100644 --- a/source/Irrlicht/CGUIStaticText.h +++ b/source/Irrlicht/CGUIStaticText.h @@ -86,6 +86,17 @@ namespace gui //! Updates the absolute position, splits text if word wrap is enabled virtual void updateAbsolutePosition(); + //! Set whether the string should be interpreted as right-to-left (RTL) text + /** \note This component does not implement the Unicode bidi standard, the + text of the component should be already RTL if you call this. The + main difference when RTL is enabled is that the linebreaks for multiline + elements are performed starting from the end. + */ + virtual void setRightToLeft(bool rtl); + + //! Checks if the text should be interpreted as right-to-left text + virtual bool isRightToLeft() const; + //! Writes attributes of the element. virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const; @@ -104,6 +115,7 @@ namespace gui bool WordWrap; bool Background; bool RestrainTextInside; + bool RightToLeft; video::SColor OverrideColor, BGColor; gui::IGUIFont* OverrideFont; diff --git a/source/Irrlicht/CGUITabControl.cpp b/source/Irrlicht/CGUITabControl.cpp index 2544a8f6..ec82664f 100644 --- a/source/Irrlicht/CGUITabControl.cpp +++ b/source/Irrlicht/CGUITabControl.cpp @@ -641,8 +641,10 @@ void CGUITabControl::draw() skin->draw3DTabButton(this, false, frameRect, &AbsoluteClippingRect, VerticalAlignment); // draw text + core::rect textClipRect(frameRect); // TODO: exact size depends on borders in draw3DTabButton which we don't get with current interface + textClipRect.clipAgainst(AbsoluteClippingRect); font->draw(text, frameRect, Tabs[i]->getTextColor(), - true, true, &frameRect); + true, true, &textClipRect); } } @@ -659,8 +661,10 @@ void CGUITabControl::draw() skin->draw3DTabButton(this, true, frameRect, &AbsoluteClippingRect, VerticalAlignment); // draw text + core::rect textClipRect(frameRect); // TODO: exact size depends on borders in draw3DTabButton which we don't get with current interface + textClipRect.clipAgainst(AbsoluteClippingRect); font->draw(activeTab->getText(), frameRect, activeTab->getTextColor(), - true, true, &frameRect); + true, true, &textClipRect); tr.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X; tr.LowerRightCorner.X = left - 1; diff --git a/source/Irrlicht/CGUITable.cpp b/source/Irrlicht/CGUITable.cpp index 84fcc9c6..a1718d17 100644 --- a/source/Irrlicht/CGUITable.cpp +++ b/source/Irrlicht/CGUITable.cpp @@ -229,6 +229,14 @@ void CGUITable::setColumnWidth(u32 columnIndex, u32 width) recalculateWidths(); } +//! Get the width of a column +u32 CGUITable::getColumnWidth(u32 columnIndex) const +{ + if ( columnIndex >= Columns.size() ) + return 0; + + return Columns[columnIndex].Width; +} void CGUITable::setResizableColumns(bool resizable) { @@ -888,13 +896,10 @@ void CGUITable::draw() // area of for the items (without header and without scrollbars) core::rect clientClip(tableRect); clientClip.UpperLeftCorner.Y = headerBottom + 1; - - core::rect* clipRect = 0; - if (Clip) - clipRect = &AbsoluteClippingRect; + clientClip.clipAgainst(AbsoluteClippingRect); // draw background for whole element - skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT), true, DrawBack, AbsoluteRect, clipRect); + skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT), true, DrawBack, AbsoluteRect, &AbsoluteClippingRect); // scrolledTableClient is the area where the table items would be if it could be drawn completely core::rect scrolledTableClient(tableRect); @@ -965,6 +970,9 @@ void CGUITable::draw() core::rect columnSeparator(clientClip); pos = scrolledTableClient.UpperLeftCorner.X; + core::rect tableClip(tableRect); + tableClip.clipAgainst(AbsoluteClippingRect); + for (u32 i = 0 ; i < Columns.size() ; ++i ) { const wchar_t* text = Columns[i].Name.c_str(); @@ -975,19 +983,19 @@ void CGUITable::draw() core::rect columnrect(pos, tableRect.UpperLeftCorner.Y, pos + colWidth, headerBottom); // draw column background - skin->draw3DButtonPaneStandard(this, columnrect, &tableRect); + skin->draw3DButtonPaneStandard(this, columnrect, &tableClip); // draw column seperator if ( DrawFlags & EGTDF_COLUMNS ) { columnSeparator.UpperLeftCorner.X = pos; columnSeparator.LowerRightCorner.X = pos + 1; - driver->draw2DRectangle(skin->getColor(EGDC_3D_SHADOW), columnSeparator, &tableRect); + driver->draw2DRectangle(skin->getColor(EGDC_3D_SHADOW), columnSeparator, &tableClip); } // draw header column text columnrect.UpperLeftCorner.X += CellWidthPadding; - font->draw(text, columnrect, skin->getColor( isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), false, true, &tableRect); + font->draw(text, columnrect, skin->getColor( isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), false, true, &tableClip); // draw icon for active column tab if ( (s32)i == ActiveTab ) @@ -996,13 +1004,13 @@ void CGUITable::draw() { columnrect.UpperLeftCorner.X = columnrect.LowerRightCorner.X - CellWidthPadding - ARROW_PAD / 2 + 2; columnrect.UpperLeftCorner.Y += 7; - skin->drawIcon(this,EGDI_CURSOR_UP,columnrect.UpperLeftCorner,0,0,false,&tableRect); + skin->drawIcon(this,EGDI_CURSOR_UP,columnrect.UpperLeftCorner,0,0,false,&tableClip); } else { columnrect.UpperLeftCorner.X = columnrect.LowerRightCorner.X - CellWidthPadding - ARROW_PAD / 2 + 2; columnrect.UpperLeftCorner.Y += 7; - skin->drawIcon(this,EGDI_CURSOR_DOWN,columnrect.UpperLeftCorner,0,0,false,&tableRect); + skin->drawIcon(this,EGDI_CURSOR_DOWN,columnrect.UpperLeftCorner,0,0,false,&tableClip); } } @@ -1011,7 +1019,7 @@ void CGUITable::draw() // fill up header background up to the right side core::rect columnrect(pos, tableRect.UpperLeftCorner.Y, tableRect.LowerRightCorner.X , headerBottom); - skin->draw3DButtonPaneStandard(this, columnrect, &tableRect); + skin->draw3DButtonPaneStandard(this, columnrect, &tableClip); IGUIElement::draw(); } diff --git a/source/Irrlicht/CGUITable.h b/source/Irrlicht/CGUITable.h index 7e67dcde..448dcba6 100644 --- a/source/Irrlicht/CGUITable.h +++ b/source/Irrlicht/CGUITable.h @@ -57,6 +57,9 @@ namespace gui //! set a column width virtual void setColumnWidth(u32 columnIndex, u32 width); + //! Get the width of a column + virtual u32 getColumnWidth(u32 columnIndex) const; + //! columns can be resized by drag 'n drop virtual void setResizableColumns(bool resizable); diff --git a/source/Irrlicht/CImageLoaderJPG.cpp b/source/Irrlicht/CImageLoaderJPG.cpp index 87faacf2..1714d2da 100644 --- a/source/Irrlicht/CImageLoaderJPG.cpp +++ b/source/Irrlicht/CImageLoaderJPG.cpp @@ -209,6 +209,7 @@ IImage* CImageLoaderJPG::loadImage(io::IReadFile* file) const cinfo.out_color_space=JCS_RGB; cinfo.out_color_components=3; } + cinfo.output_gamma=2.2; cinfo.do_fancy_upsampling=FALSE; // Start decompressor diff --git a/source/Irrlicht/CImageLoaderPCX.cpp b/source/Irrlicht/CImageLoaderPCX.cpp index 1285a048..27c36d30 100644 --- a/source/Irrlicht/CImageLoaderPCX.cpp +++ b/source/Irrlicht/CImageLoaderPCX.cpp @@ -90,14 +90,14 @@ IImage* CImageLoaderPCX::loadImage(io::IReadFile* file) const u8 *tempPalette = new u8[768]; paletteData = new s32[256]; - memset(paletteData, 0xFF, 256*sizeof(s32)); file->read( tempPalette, 768 ); for( s32 i=0; i<256; i++ ) { - paletteData[i] = (tempPalette[i*3+0] << 16) | - (tempPalette[i*3+1] << 8) | - (tempPalette[i*3+2] ); + paletteData[i] = (0xff000000 | + (tempPalette[i*3+0] << 16) | + (tempPalette[i*3+1] << 8) | + (tempPalette[i*3+2])); } delete [] tempPalette; @@ -107,12 +107,12 @@ IImage* CImageLoaderPCX::loadImage(io::IReadFile* file) const else if( header.BitsPerPixel == 4 ) { paletteData = new s32[16]; - memset(paletteData, 0, 16*sizeof(s32)); - for( s32 i=0; i<256; i++ ) + for( s32 i=0; i<16; i++ ) { - paletteData[i] = (header.Palette[i*3+0] << 16) | - (header.Palette[i*3+1] << 8) | - (header.Palette[i*3+2]); + paletteData[i] = (0xff000000 | + (header.Palette[i*3+0] << 16) | + (header.Palette[i*3+1] << 8) | + (header.Palette[i*3+2])); } } diff --git a/source/Irrlicht/CImageLoaderPNG.cpp b/source/Irrlicht/CImageLoaderPNG.cpp index a47483b2..de624466 100644 --- a/source/Irrlicht/CImageLoaderPNG.cpp +++ b/source/Irrlicht/CImageLoaderPNG.cpp @@ -182,7 +182,22 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); - // Update the changes + int intent; + const double screen_gamma = 2.2; + + if (png_get_sRGB(png_ptr, info_ptr, &intent)) + png_set_gamma(png_ptr, screen_gamma, 0.45455); + else + { + double image_gamma; + if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) + png_set_gamma(png_ptr, screen_gamma, image_gamma); + else + png_set_gamma(png_ptr, screen_gamma, 0.45455); + } + + // Update the changes in between, as we need to get the new color type + // for proper processing of the RGBA type png_read_update_info(png_ptr, info_ptr); { // Use temporary variables to avoid passing casted pointers @@ -205,18 +220,6 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const #endif } - // Update the changes - { - // Use temporary variables to avoid passing casted pointers - png_uint_32 w,h; - // Extract info - png_get_IHDR(png_ptr, info_ptr, - &w, &h, - &BitDepth, &ColorType, NULL, NULL, NULL); - Width=w; - Height=h; - } - // Create the image structure to be filled by png data if (ColorType==PNG_COLOR_TYPE_RGB_ALPHA) image = new CImage(ECF_A8R8G8B8, core::dimension2d(Width, Height)); diff --git a/source/Irrlicht/CImageLoaderPPM.cpp b/source/Irrlicht/CImageLoaderPPM.cpp index 4424e290..7a9e617a 100644 --- a/source/Irrlicht/CImageLoaderPPM.cpp +++ b/source/Irrlicht/CImageLoaderPPM.cpp @@ -64,10 +64,10 @@ IImage* CImageLoaderPPM::loadImage(io::IReadFile* file) const core::stringc token; getNextToken(file, token); - const u32 width = core::strtol10(token.c_str()); + const u32 width = core::strtoul10(token.c_str()); getNextToken(file, token); - const u32 height = core::strtol10(token.c_str()); + const u32 height = core::strtoul10(token.c_str()); u8* data = 0; const u32 size = width*height; @@ -106,7 +106,7 @@ IImage* CImageLoaderPPM::loadImage(io::IReadFile* file) const else { getNextToken(file, token); - const u32 maxDepth = core::strtol10(token.c_str()); + const u32 maxDepth = core::strtoul10(token.c_str()); if (maxDepth > 255) // no double bytes yet return 0; @@ -144,7 +144,7 @@ IImage* CImageLoaderPPM::loadImage(io::IReadFile* file) const for (u32 i=0; i= 0x0501) && defined(CONSOLE_FULLSCREEN_MODE) if (SetConsoleDisplayMode(WindowsSTDOut, CONSOLE_FULLSCREEN_MODE, Dimensions)) { CreationParams.WindowSize.Width = Dimensions->X; CreationParams.WindowSize.Width = Dimensions->Y; } +#endif } else { diff --git a/source/Irrlicht/CIrrDeviceSDL.cpp b/source/Irrlicht/CIrrDeviceSDL.cpp index 45af2e06..eb738ae1 100644 --- a/source/Irrlicht/CIrrDeviceSDL.cpp +++ b/source/Irrlicht/CIrrDeviceSDL.cpp @@ -30,15 +30,13 @@ namespace irr { #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ - IVideoDriver* createDirectX8Driver(const core::dimension2d& screenSize, HWND window, - u32 bits, bool fullscreen, bool stencilbuffer, io::IFileSystem* io, - bool pureSoftware, bool highPrecisionFPU, bool vsync, u8 antiAlias, u32 displayAdapter); + IVideoDriver* createDirectX8Driver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); #endif #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ - IVideoDriver* createDirectX9Driver(const core::dimension2d& screenSize, HWND window, - u32 bits, bool fullscreen, bool stencilbuffer, io::IFileSystem* io, - bool pureSoftware, bool highPrecisionFPU, bool vsync, u8 antiAlias, u32 displayAdapter); + IVideoDriver* createDirectX9Driver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); #endif #ifdef _IRR_COMPILE_WITH_OPENGL_ @@ -223,11 +221,7 @@ void CIrrDeviceSDL::createDriver() case video::EDT_DIRECT3D8: #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ - VideoDriver = video::createDirectX8Driver(CreationParams.WindowSize, Info.window, - CreationParams.Bits, CreationParams.Fullscreen, CreationParams.Stencilbuffer, - FileSystem, false, CreationParams.HighPrecisionFPU, CreationParams.Vsync, - CreationParams.AntiAlias, CreationParams.DisplayAdapter); - + VideoDriver = video::createDirectX8Driver(CreationParams, FileSystem, HWnd); if (!VideoDriver) { os::Printer::log("Could not create DIRECT3D8 Driver.", ELL_ERROR); @@ -241,11 +235,7 @@ void CIrrDeviceSDL::createDriver() case video::EDT_DIRECT3D9: #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ - VideoDriver = video::createDirectX9Driver(CreationParams.WindowSize, Info.window, - CreationParams.Bits, CreationParams.Fullscreen, CreationParams.Stencilbuffer, - FileSystem, false, CreationParams.HighPrecisionFPU, CreationParams.Vsync, - CreationParams.AntiAlias, CreationParams.DisplayAdapter); - + VideoDriver = video::createDirectX9Driver(CreationParams, FileSystem, HWnd); if (!VideoDriver) { os::Printer::log("Could not create DIRECT3D9 Driver.", ELL_ERROR); diff --git a/source/Irrlicht/CIrrDeviceWin32.cpp b/source/Irrlicht/CIrrDeviceWin32.cpp index a2dc0231..649bff50 100644 --- a/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/source/Irrlicht/CIrrDeviceWin32.cpp @@ -18,21 +18,33 @@ #include "IGUISpriteBank.h" #include #include "SExposedVideoData.h" +#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) +#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ +#define DIRECTINPUT_VERSION 0x0800 +#include +#ifdef _MSC_VER +#pragma comment(lib, "dinput8.lib") +#pragma comment(lib, "dxguid.lib") +#endif +#endif +#else +#ifdef _MSC_VER +#pragma comment(lib, "winmm.lib") +#endif +#endif namespace irr { namespace video { #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ - IVideoDriver* createDirectX8Driver(const core::dimension2d& screenSize, HWND window, - u32 bits, bool fullscreen, bool stencilbuffer, io::IFileSystem* io, - bool pureSoftware, bool highPrecisionFPU, bool vsync, u8 antiAlias, u32 displayAdapter); + IVideoDriver* createDirectX8Driver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); #endif #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ - IVideoDriver* createDirectX9Driver(const core::dimension2d& screenSize, HWND window, - u32 bits, bool fullscreen, bool stencilbuffer, io::IFileSystem* io, - bool pureSoftware, bool highPrecisionFPU, bool vsync, u8 antiAlias, u32 displayAdapter); + IVideoDriver* createDirectX9Driver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); #endif #ifdef _IRR_COMPILE_WITH_OPENGL_ @@ -50,6 +62,390 @@ namespace irr } } // end namespace irr +namespace irr +{ +struct SJoystickWin32Control +{ + CIrrDeviceWin32* Device; + +#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_) + IDirectInput8* DirectInputDevice; +#endif +#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) + struct JoystickInfo + { + u32 Index; +#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ + core::stringc Name; + GUID guid; + LPDIRECTINPUTDEVICE8 lpdijoy; + DIDEVCAPS devcaps; + u8 axisValid[8]; +#else + JOYCAPS Caps; +#endif + }; + core::array ActiveJoysticks; +#endif + + SJoystickWin32Control(CIrrDeviceWin32* dev) : Device(dev) + { +#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_) + DirectInputDevice=0; + if (DI_OK != (DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInputDevice, NULL))) + { + os::Printer::log("Could not create DirectInput8 Object", ELL_WARNING); + return; + } +#endif + } + ~SJoystickWin32Control() + { +#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_) + for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + { + LPDIRECTINPUTDEVICE8 dev = ActiveJoysticks[joystick].lpdijoy; + if (dev) + { + dev->Unacquire(); + } + dev->Release(); + } + + if (DirectInputDevice) + DirectInputDevice->Release(); +#endif + } + +#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_) + static BOOL CALLBACK EnumJoysticks(LPCDIDEVICEINSTANCE lpddi, LPVOID cp) + { + SJoystickWin32Control* p=(SJoystickWin32Control*)cp; + p->directInputAddJoystick(lpddi); + return DIENUM_CONTINUE; + } + void directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi) + { + //Get the GUID of the joystuck + const GUID guid = lpddi->guidInstance; + + JoystickInfo activeJoystick; + activeJoystick.Index=ActiveJoysticks.size(); + activeJoystick.guid=guid; + activeJoystick.Name=lpddi->tszProductName; + if (FAILED(DirectInputDevice->CreateDevice(guid, &activeJoystick.lpdijoy, NULL))) + { + os::Printer::log("Could not create DirectInput device", ELL_WARNING); + return; + } + + activeJoystick.devcaps.dwSize=sizeof(activeJoystick.devcaps); + if (FAILED(activeJoystick.lpdijoy->GetCapabilities(&activeJoystick.devcaps))) + { + os::Printer::log("Could not create DirectInput device", ELL_WARNING); + return; + } + + if (FAILED(activeJoystick.lpdijoy->SetCooperativeLevel(Device->HWnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE))) + { + os::Printer::log("Could not set DirectInput device cooperative level", ELL_WARNING); + return; + } + + if (FAILED(activeJoystick.lpdijoy->SetDataFormat(&c_dfDIJoystick2))) + { + os::Printer::log("Could not set DirectInput device data format", ELL_WARNING); + return; + } + + if (FAILED(activeJoystick.lpdijoy->Acquire())) + { + os::Printer::log("Could not set DirectInput cooperative level", ELL_WARNING); + return; + } + + DIJOYSTATE2 info; + if (FAILED(activeJoystick.lpdijoy->GetDeviceState(sizeof(info),&info))) + { + os::Printer::log("Could not read DirectInput device state", ELL_WARNING); + return; + } + + ZeroMemory(activeJoystick.axisValid,sizeof(activeJoystick.axisValid)); + activeJoystick.axisValid[0]= (info.lX!=0) ? 1 : 0; + activeJoystick.axisValid[1]= (info.lY!=0) ? 1 : 0; + activeJoystick.axisValid[2]= (info.lZ!=0) ? 1 : 0; + activeJoystick.axisValid[3]= (info.lRx!=0) ? 1 : 0; + activeJoystick.axisValid[4]= (info.lRy!=0) ? 1 : 0; + activeJoystick.axisValid[5]= (info.lRz!=0) ? 1 : 0; + + int caxis=0; + for (u8 i=0; i<6; i++) + { + if (activeJoystick.axisValid[i]) + caxis++; + } + + for (u8 i=0; i<(activeJoystick.devcaps.dwAxes)-caxis; i++) + { + if (i+caxis < 8) + activeJoystick.axisValid[i+caxis]=1; + } + + ActiveJoysticks.push_back(activeJoystick); + } +#endif + +void pollJoysticks() +{ +#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ +#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ + if(0 == ActiveJoysticks.size()) + return; + + u32 joystick; + DIJOYSTATE2 info; + + for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + { + // needs to be reset for each joystick + // request ALL values and POV as continuous if possible + + const DIDEVCAPS & caps = ActiveJoysticks[joystick].devcaps; + // if no POV is available don't ask for POV values + + if (!FAILED(ActiveJoysticks[joystick].lpdijoy->GetDeviceState(sizeof(info),&info))) + { + SEvent event; + + event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; + event.JoystickEvent.Joystick = (u8)joystick; + + event.JoystickEvent.POV = (u16)info.rgdwPOV[0]; + // set to undefined if no POV value was returned or the value + // is out of range + if ((caps.dwPOVs==0) || (event.JoystickEvent.POV > 35900)) + event.JoystickEvent.POV = 65535; + + for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) + event.JoystickEvent.Axis[axis] = 0; + + u16 dxAxis=0; + u16 irrAxis=0; + + while (dxAxis < 6 && irrAxis 0) + axisFound=1; + + if (axisFound) + { + s32 val=axisValue - 32768; + + if (val <-32767) val=-32767; + if (val > 32767) val=32767; + event.JoystickEvent.Axis[irrAxis]=(s16)(val); + irrAxis++; + } + + dxAxis++; + } + + u32 buttons=0; + BYTE* bytebuttons=info.rgbButtons; + for (u16 i=0; i<32; i++) + { + if (bytebuttons[i] >0) + { + buttons |= (1 << i); + } + } + event.JoystickEvent.ButtonStates = buttons; + + (void)Device->postEventFromUser(event); + } + } +#else + if (0 == ActiveJoysticks.size()) + return; + + u32 joystick; + JOYINFOEX info; + + for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + { + // needs to be reset for each joystick + // request ALL values and POV as continuous if possible + info.dwSize = sizeof(info); + info.dwFlags = JOY_RETURNALL|JOY_RETURNPOVCTS; + const JOYCAPS & caps = ActiveJoysticks[joystick].Caps; + // if no POV is available don't ask for POV values + if (!(caps.wCaps & JOYCAPS_HASPOV)) + info.dwFlags &= ~(JOY_RETURNPOV|JOY_RETURNPOVCTS); + if(JOYERR_NOERROR == joyGetPosEx(ActiveJoysticks[joystick].Index, &info)) + { + SEvent event; + + event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; + event.JoystickEvent.Joystick = (u8)joystick; + + event.JoystickEvent.POV = (u16)info.dwPOV; + // set to undefined if no POV value was returned or the value + // is out of range + if (!(info.dwFlags & JOY_RETURNPOV) || (event.JoystickEvent.POV > 35900)) + event.JoystickEvent.POV = 65535; + + for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) + event.JoystickEvent.Axis[axis] = 0; + + event.JoystickEvent.ButtonStates = info.dwButtons; + + switch(caps.wNumAxes) + { + default: + case 6: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = + (s16)((65535 * (info.dwVpos - caps.wVmin)) / (caps.wVmax - caps.wVmin) - 32768); + + case 5: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = + (s16)((65535 * (info.dwUpos - caps.wUmin)) / (caps.wUmax - caps.wUmin) - 32768); + + case 4: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = + (s16)((65535 * (info.dwRpos - caps.wRmin)) / (caps.wRmax - caps.wRmin) - 32768); + + case 3: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = + (s16)((65535 * (info.dwZpos - caps.wZmin)) / (caps.wZmax - caps.wZmin) - 32768); + + case 2: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = + (s16)((65535 * (info.dwYpos - caps.wYmin)) / (caps.wYmax - caps.wYmin) - 32768); + + case 1: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = + (s16)((65535 * (info.dwXpos - caps.wXmin)) / (caps.wXmax - caps.wXmin) - 32768); + } + + (void)Device->postEventFromUser(event); + } + } +#endif +#endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ +} + +bool activateJoysticks(core::array & joystickInfo) +{ +#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ +#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ + if (!DirectInputDevice || (DirectInputDevice->EnumDevices(DI8DEVCLASS_GAMECTRL, SJoystickWin32Control::EnumJoysticks, this, DIEDFL_ATTACHEDONLY ))) + { + os::Printer::log("Could not enum DirectInput8 controllers", ELL_WARNING); + return false; + } + + for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + { + JoystickInfo& activeJoystick = ActiveJoysticks[joystick]; + SJoystickInfo info; + info.Axes=activeJoystick.devcaps.dwAxes; + info.Buttons=activeJoystick.devcaps.dwButtons; + info.Name=activeJoystick.Name; + info.PovHat = (activeJoystick.devcaps.dwPOVs != 0) + ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; + joystickInfo.push_back(info); + } + return true; +#else + joystickInfo.clear(); + ActiveJoysticks.clear(); + + const u32 numberOfJoysticks = ::joyGetNumDevs(); + JOYINFOEX info; + info.dwSize = sizeof(info); + info.dwFlags = JOY_RETURNALL; + + JoystickInfo activeJoystick; + SJoystickInfo returnInfo; + + joystickInfo.reallocate(numberOfJoysticks); + ActiveJoysticks.reallocate(numberOfJoysticks); + + u32 joystick = 0; + for(; joystick < numberOfJoysticks; ++joystick) + { + if(JOYERR_NOERROR == joyGetPosEx(joystick, &info) + && + JOYERR_NOERROR == joyGetDevCaps(joystick, + &activeJoystick.Caps, + sizeof(activeJoystick.Caps))) + { + activeJoystick.Index = joystick; + ActiveJoysticks.push_back(activeJoystick); + + returnInfo.Joystick = (u8)joystick; + returnInfo.Axes = activeJoystick.Caps.wNumAxes; + returnInfo.Buttons = activeJoystick.Caps.wNumButtons; + returnInfo.Name = activeJoystick.Caps.szPname; + returnInfo.PovHat = ((activeJoystick.Caps.wCaps & JOYCAPS_HASPOV) == JOYCAPS_HASPOV) + ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; + + joystickInfo.push_back(returnInfo); + } + } + + for(joystick = 0; joystick < joystickInfo.size(); ++joystick) + { + char logString[256]; + (void)sprintf(logString, "Found joystick %d, %d axes, %d buttons '%s'", + joystick, joystickInfo[joystick].Axes, + joystickInfo[joystick].Buttons, joystickInfo[joystick].Name.c_str()); + os::Printer::log(logString, ELL_INFORMATION); + } + + return true; +#endif +#else + return false; +#endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ +} +}; +} // end namespace irr + // Get the codepage from the locale language id // Based on the table from http://www.science.co.il/Language/Locale-Codes.asp?s=decimal static unsigned int LocaleIdToCodepage(unsigned int lcid) @@ -514,7 +910,7 @@ namespace irr CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) : CIrrDeviceStub(params), HWnd(0), ChangedToFullScreen(false), IsNonNTWindows(false), Resized(false), - ExternalWindow(false), Win32CursorControl(0) + ExternalWindow(false), Win32CursorControl(0), JoyControl(0) { #ifdef _DEBUG setDebugName("CIrrDeviceWin32"); @@ -619,6 +1015,7 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) Win32CursorControl = new CCursorControl(this, CreationParams.WindowSize, HWnd, CreationParams.Fullscreen); CursorControl = Win32CursorControl; + JoyControl = new SJoystickWin32Control(this); // initialize doubleclicks with system values MouseMultiClicks.DoubleClickTime = GetDoubleClickTime(); @@ -653,6 +1050,8 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) //! destructor CIrrDeviceWin32::~CIrrDeviceWin32() { + delete JoyControl; + // unregister environment irr::core::list::Iterator it = EnvMap.begin(); @@ -677,10 +1076,7 @@ void CIrrDeviceWin32::createDriver() case video::EDT_DIRECT3D8: #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ - VideoDriver = video::createDirectX8Driver(CreationParams.WindowSize, HWnd, - CreationParams.Bits, CreationParams.Fullscreen, CreationParams.Stencilbuffer, - FileSystem, false, CreationParams.HighPrecisionFPU, CreationParams.Vsync, - CreationParams.AntiAlias, CreationParams.DisplayAdapter); + VideoDriver = video::createDirectX8Driver(CreationParams, FileSystem, HWnd); if (!VideoDriver) { @@ -695,10 +1091,7 @@ void CIrrDeviceWin32::createDriver() case video::EDT_DIRECT3D9: #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ - VideoDriver = video::createDirectX9Driver(CreationParams.WindowSize, HWnd, - CreationParams.Bits, CreationParams.Fullscreen, CreationParams.Stencilbuffer, - FileSystem, false, CreationParams.HighPrecisionFPU, CreationParams.Vsync, - CreationParams.AntiAlias, CreationParams.DisplayAdapter); + VideoDriver = video::createDirectX9Driver(CreationParams, FileSystem, HWnd); if (!VideoDriver) { @@ -823,8 +1216,8 @@ bool CIrrDeviceWin32::run() if (!Close) resizeIfNecessary(); - if(!Close) - pollJoysticks(); + if(!Close && JoyControl) + JoyControl->pollJoysticks(); _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return !Close; @@ -1102,6 +1495,9 @@ video::IVideoModeList* CIrrDeviceWin32::getVideoModeList() typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); // Needed for old windows apis +// depending on the SDK version and compilers some defines might be available +// or not +#ifndef PRODUCT_ULTIMATE #define PRODUCT_ULTIMATE 0x00000001 #define PRODUCT_HOME_BASIC 0x00000002 #define PRODUCT_HOME_PREMIUM 0x00000003 @@ -1109,19 +1505,26 @@ typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); #define PRODUCT_HOME_BASIC_N 0x00000005 #define PRODUCT_BUSINESS 0x00000006 #define PRODUCT_STARTER 0x0000000B +#endif +#ifndef PRODUCT_ULTIMATE_N #define PRODUCT_BUSINESS_N 0x00000010 #define PRODUCT_HOME_PREMIUM_N 0x0000001A #define PRODUCT_ENTERPRISE_N 0x0000001B #define PRODUCT_ULTIMATE_N 0x0000001C #define PRODUCT_STARTER_N 0x0000002F +#endif +#ifndef PRODUCT_PROFESSIONAL #define PRODUCT_PROFESSIONAL 0x00000030 #define PRODUCT_PROFESSIONAL_N 0x00000031 +#endif +#ifndef PRODUCT_ULTIMATE_E #define PRODUCT_STARTER_E 0x00000042 #define PRODUCT_HOME_BASIC_E 0x00000043 #define PRODUCT_HOME_PREMIUM_E 0x00000044 #define PRODUCT_PROFESSIONAL_E 0x00000045 #define PRODUCT_ENTERPRISE_E 0x00000046 #define PRODUCT_ULTIMATE_E 0x00000047 +#endif void CIrrDeviceWin32::getWindowsVersion(core::stringc& out) { @@ -1389,129 +1792,12 @@ void CIrrDeviceWin32::restoreWindow() bool CIrrDeviceWin32::activateJoysticks(core::array & joystickInfo) { -#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ - joystickInfo.clear(); - ActiveJoysticks.clear(); - - const u32 numberOfJoysticks = ::joyGetNumDevs(); - JOYINFOEX info; - info.dwSize = sizeof(info); - info.dwFlags = JOY_RETURNALL; - - JoystickInfo activeJoystick; - SJoystickInfo returnInfo; - - joystickInfo.reallocate(numberOfJoysticks); - ActiveJoysticks.reallocate(numberOfJoysticks); - - u32 joystick = 0; - for(; joystick < numberOfJoysticks; ++joystick) - { - if(JOYERR_NOERROR == joyGetPosEx(joystick, &info) - && - JOYERR_NOERROR == joyGetDevCaps(joystick, - &activeJoystick.Caps, - sizeof(activeJoystick.Caps))) - { - activeJoystick.Index = joystick; - ActiveJoysticks.push_back(activeJoystick); - - returnInfo.Joystick = (u8)joystick; - returnInfo.Axes = activeJoystick.Caps.wNumAxes; - returnInfo.Buttons = activeJoystick.Caps.wNumButtons; - returnInfo.Name = activeJoystick.Caps.szPname; - returnInfo.PovHat = ((activeJoystick.Caps.wCaps & JOYCAPS_HASPOV) == JOYCAPS_HASPOV) - ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; - - joystickInfo.push_back(returnInfo); - } - } - - for(joystick = 0; joystick < joystickInfo.size(); ++joystick) - { - char logString[256]; - (void)sprintf(logString, "Found joystick %d, %d axes, %d buttons '%s'", - joystick, joystickInfo[joystick].Axes, - joystickInfo[joystick].Buttons, joystickInfo[joystick].Name.c_str()); - os::Printer::log(logString, ELL_INFORMATION); - } - - return true; -#else - return false; -#endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ + if (JoyControl) + return JoyControl->activateJoysticks(joystickInfo); + else + return false; } -void CIrrDeviceWin32::pollJoysticks() -{ -#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ - if(0 == ActiveJoysticks.size()) - return; - - u32 joystick; - JOYINFOEX info; - - for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) - { - // needs to be reset for each joystick - // request ALL values and POV as continuous if possible - info.dwSize = sizeof(info); - info.dwFlags = JOY_RETURNALL|JOY_RETURNPOVCTS; - const JOYCAPS & caps = ActiveJoysticks[joystick].Caps; - // if no POV is available don't ask for POV values - if (!(caps.wCaps & JOYCAPS_HASPOV)) - info.dwFlags &= ~(JOY_RETURNPOV|JOY_RETURNPOVCTS); - if(JOYERR_NOERROR == joyGetPosEx(ActiveJoysticks[joystick].Index, &info)) - { - SEvent event; - - event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; - event.JoystickEvent.Joystick = (u8)joystick; - - event.JoystickEvent.POV = (u16)info.dwPOV; - // set to undefined if no POV value was returned or the value - // is out of range - if (!(info.dwFlags & JOY_RETURNPOV) || (event.JoystickEvent.POV > 35900)) - event.JoystickEvent.POV = 65535; - - for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) - event.JoystickEvent.Axis[axis] = 0; - - switch(caps.wNumAxes) - { - default: - case 6: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = - (s16)((65535 * (info.dwVpos - caps.wVmin)) / (caps.wVmax - caps.wVmin) - 32768); - - case 5: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = - (s16)((65535 * (info.dwUpos - caps.wUmin)) / (caps.wUmax - caps.wUmin) - 32768); - - case 4: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = - (s16)((65535 * (info.dwRpos - caps.wRmin)) / (caps.wRmax - caps.wRmin) - 32768); - - case 3: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = - (s16)((65535 * (info.dwZpos - caps.wZmin)) / (caps.wZmax - caps.wZmin) - 32768); - - case 2: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = - (s16)((65535 * (info.dwYpos - caps.wYmin)) / (caps.wYmax - caps.wYmin) - 32768); - - case 1: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = - (s16)((65535 * (info.dwXpos - caps.wXmin)) / (caps.wXmax - caps.wXmin) - 32768); - } - - event.JoystickEvent.ButtonStates = info.dwButtons; - - (void)postEventFromUser(event); - } - } -#endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ -} //! Set the current Gamma Value for the Display bool CIrrDeviceWin32::setGammaRamp( f32 red, f32 green, f32 blue, f32 brightness, f32 contrast ) diff --git a/source/Irrlicht/CIrrDeviceWin32.h b/source/Irrlicht/CIrrDeviceWin32.h index fc8a9a01..e474c860 100644 --- a/source/Irrlicht/CIrrDeviceWin32.h +++ b/source/Irrlicht/CIrrDeviceWin32.h @@ -16,13 +16,20 @@ #if !defined(_IRR_XBOX_PLATFORM_) #include #include // For JOYCAPS - #include + #include +#endif +#if !defined(GET_X_LPARAM) +#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) +#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) #endif namespace irr { + struct SJoystickWin32Control; + class CIrrDeviceWin32 : public CIrrDeviceStub, video::IImagePresenter { + friend struct SJoystickWin32Control; public: //! constructor @@ -379,8 +386,6 @@ namespace irr void resizeIfNecessary(); - void pollJoysticks(); - HWND HWnd; bool ChangedToFullScreen; @@ -389,14 +394,7 @@ namespace irr bool ExternalWindow; CCursorControl* Win32CursorControl; -#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) - struct JoystickInfo - { - u32 Index; - JOYCAPS Caps; - }; - core::array ActiveJoysticks; -#endif + SJoystickWin32Control* JoyControl; }; } // end namespace irr diff --git a/source/Irrlicht/CIrrDeviceWinCE.cpp b/source/Irrlicht/CIrrDeviceWinCE.cpp index d74c484c..cfa06757 100644 --- a/source/Irrlicht/CIrrDeviceWinCE.cpp +++ b/source/Irrlicht/CIrrDeviceWinCE.cpp @@ -28,15 +28,13 @@ namespace irr namespace video { #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ - IVideoDriver* createDirectX8Driver(const core::dimension2d& screenSize, HWND window, - u32 bits, bool fullscreen, bool stencilbuffer, io::IFileSystem* io, - bool pureSoftware, bool highPrecisionFPU, bool vsync, bool antiAlias, u32 displayAdapter); + IVideoDriver* createDirectX8Driver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); #endif #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ - IVideoDriver* createDirectX9Driver(const core::dimension2d& screenSize, HWND window, - u32 bits, bool fullscreen, bool stencilbuffer, io::IFileSystem* io, - bool pureSoftware, bool highPrecisionFPU, bool vsync, bool antiAlias, u32 displayAdapter); + IVideoDriver* createDirectX9Driver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); #endif #ifdef _IRR_COMPILE_WITH_OPENGL_ @@ -470,9 +468,7 @@ void CIrrDeviceWinCE::createDriver() { case video::EDT_DIRECT3D8: #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ - VideoDriver = video::createDirectX8Driver(CreationParams.WindowSize, HWnd, CreationParams.Bits, CreationParams.Fullscreen, - CreationParams.Stencilbuffer, FileSystem, false, CreationParams.HighPrecisionFPU, CreationParams.Vsync, CreationParams.AntiAlias - , CreationParams.DisplayAdapter); + VideoDriver = video::createDirectX8Driver(CreationParams, FileSystem, HWnd); if (!VideoDriver) { os::Printer::log("Could not create DIRECT3D8 Driver.", ELL_ERROR); @@ -485,9 +481,7 @@ void CIrrDeviceWinCE::createDriver() case video::EDT_DIRECT3D9: #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ - VideoDriver = video::createDirectX9Driver(CreationParams.WindowSize, HWnd, CreationParams.Bits, CreationParams.Fullscreen, - CreationParams.Stencilbuffer, FileSystem, false, CreationParams.HighPrecisionFPU, CreationParams.Vsync, CreationParams.AntiAlias - , CreationParams.DisplayAdapter); + VideoDriver = video::createDirectX9Driver(CreationParams, FileSystem, HWnd); if (!VideoDriver) { os::Printer::log("Could not create DIRECT3D9 Driver.", ELL_ERROR); diff --git a/source/Irrlicht/CMD2MeshFileLoader.cpp b/source/Irrlicht/CMD2MeshFileLoader.cpp index 4c4ac396..11a3d279 100644 --- a/source/Irrlicht/CMD2MeshFileLoader.cpp +++ b/source/Irrlicht/CMD2MeshFileLoader.cpp @@ -373,4 +373,3 @@ bool CMD2MeshFileLoader::loadFile(io::IReadFile* file, CAnimatedMeshMD2* mesh) #endif // _IRR_COMPILE_WITH_MD2_LOADER_ - diff --git a/source/Irrlicht/CMeshManipulator.cpp b/source/Irrlicht/CMeshManipulator.cpp index 0cc81569..7b60c8f4 100644 --- a/source/Irrlicht/CMeshManipulator.cpp +++ b/source/Irrlicht/CMeshManipulator.cpp @@ -299,19 +299,21 @@ SMesh* CMeshManipulator::createMeshCopy(scene::IMesh* mesh) const for ( u32 b=0; bgetMeshBuffer(b)->getVertexType()) + const IMeshBuffer* const mb = mesh->getMeshBuffer(b); + switch(mb->getVertexType()) { case video::EVT_STANDARD: { SMeshBuffer* buffer = new SMeshBuffer(); - const u32 vcount = mesh->getMeshBuffer(b)->getVertexCount(); + buffer->Material = mb->getMaterial(); + const u32 vcount = mb->getVertexCount(); buffer->Vertices.reallocate(vcount); - video::S3DVertex* vertices = (video::S3DVertex*)mesh->getMeshBuffer(b)->getVertices(); + video::S3DVertex* vertices = (video::S3DVertex*)mb->getVertices(); for (u32 i=0; i < vcount; ++i) buffer->Vertices.push_back(vertices[i]); - const u32 icount = mesh->getMeshBuffer(b)->getIndexCount(); + const u32 icount = mb->getIndexCount(); buffer->Indices.reallocate(icount); - u16* indices = mesh->getMeshBuffer(b)->getIndices(); + const u16* indices = mb->getIndices(); for (u32 i=0; i < icount; ++i) buffer->Indices.push_back(indices[i]); clone->addMeshBuffer(buffer); @@ -321,14 +323,15 @@ SMesh* CMeshManipulator::createMeshCopy(scene::IMesh* mesh) const case video::EVT_2TCOORDS: { SMeshBufferLightMap* buffer = new SMeshBufferLightMap(); - const u32 vcount = mesh->getMeshBuffer(b)->getVertexCount(); + buffer->Material = mb->getMaterial(); + const u32 vcount = mb->getVertexCount(); buffer->Vertices.reallocate(vcount); - video::S3DVertex2TCoords* vertices = (video::S3DVertex2TCoords*)mesh->getMeshBuffer(b)->getVertices(); + video::S3DVertex2TCoords* vertices = (video::S3DVertex2TCoords*)mb->getVertices(); for (u32 i=0; i < vcount; ++i) buffer->Vertices.push_back(vertices[i]); - const u32 icount = mesh->getMeshBuffer(b)->getIndexCount(); + const u32 icount = mb->getIndexCount(); buffer->Indices.reallocate(icount); - u16* indices = mesh->getMeshBuffer(b)->getIndices(); + const u16* indices = mb->getIndices(); for (u32 i=0; i < icount; ++i) buffer->Indices.push_back(indices[i]); clone->addMeshBuffer(buffer); @@ -338,14 +341,15 @@ SMesh* CMeshManipulator::createMeshCopy(scene::IMesh* mesh) const case video::EVT_TANGENTS: { SMeshBufferTangents* buffer = new SMeshBufferTangents(); - const u32 vcount = mesh->getMeshBuffer(b)->getVertexCount(); + buffer->Material = mb->getMaterial(); + const u32 vcount = mb->getVertexCount(); buffer->Vertices.reallocate(vcount); - video::S3DVertexTangents* vertices = (video::S3DVertexTangents*)mesh->getMeshBuffer(b)->getVertices(); + video::S3DVertexTangents* vertices = (video::S3DVertexTangents*)mb->getVertices(); for (u32 i=0; i < vcount; ++i) buffer->Vertices.push_back(vertices[i]); - const u32 icount = mesh->getMeshBuffer(b)->getIndexCount(); + const u32 icount = mb->getIndexCount(); buffer->Indices.reallocate(icount); - u16* indices = mesh->getMeshBuffer(b)->getIndices(); + const u16* indices = mb->getIndices(); for (u32 i=0; i < icount; ++i) buffer->Indices.push_back(indices[i]); clone->addMeshBuffer(buffer); @@ -467,18 +471,19 @@ IMesh* CMeshManipulator::createMeshUniquePrimitives(IMesh* mesh) const for ( u32 b=0; bgetMeshBuffer(b)->getIndexCount(); - const u16* idx = mesh->getMeshBuffer(b)->getIndices(); + const IMeshBuffer* const mb = mesh->getMeshBuffer(b); + const s32 idxCnt = mb->getIndexCount(); + const u16* idx = mb->getIndices(); - switch(mesh->getMeshBuffer(b)->getVertexType()) + switch(mb->getVertexType()) { case video::EVT_STANDARD: { SMeshBuffer* buffer = new SMeshBuffer(); - buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); + buffer->Material = mb->getMaterial(); video::S3DVertex* v = - (video::S3DVertex*)mesh->getMeshBuffer(b)->getVertices(); + (video::S3DVertex*)mb->getVertices(); buffer->Vertices.reallocate(idxCnt); buffer->Indices.reallocate(idxCnt); @@ -493,7 +498,7 @@ IMesh* CMeshManipulator::createMeshUniquePrimitives(IMesh* mesh) const buffer->Indices.push_back( i + 2 ); } - buffer->setBoundingBox(mesh->getMeshBuffer(b)->getBoundingBox()); + buffer->setBoundingBox(mb->getBoundingBox()); clone->addMeshBuffer(buffer); buffer->drop(); } @@ -501,10 +506,10 @@ IMesh* CMeshManipulator::createMeshUniquePrimitives(IMesh* mesh) const case video::EVT_2TCOORDS: { SMeshBufferLightMap* buffer = new SMeshBufferLightMap(); - buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); + buffer->Material = mb->getMaterial(); video::S3DVertex2TCoords* v = - (video::S3DVertex2TCoords*)mesh->getMeshBuffer(b)->getVertices(); + (video::S3DVertex2TCoords*)mb->getVertices(); buffer->Vertices.reallocate(idxCnt); buffer->Indices.reallocate(idxCnt); @@ -518,7 +523,7 @@ IMesh* CMeshManipulator::createMeshUniquePrimitives(IMesh* mesh) const buffer->Indices.push_back( i + 1 ); buffer->Indices.push_back( i + 2 ); } - buffer->setBoundingBox(mesh->getMeshBuffer(b)->getBoundingBox()); + buffer->setBoundingBox(mb->getBoundingBox()); clone->addMeshBuffer(buffer); buffer->drop(); } @@ -526,10 +531,10 @@ IMesh* CMeshManipulator::createMeshUniquePrimitives(IMesh* mesh) const case video::EVT_TANGENTS: { SMeshBufferTangents* buffer = new SMeshBufferTangents(); - buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); + buffer->Material = mb->getMaterial(); video::S3DVertexTangents* v = - (video::S3DVertexTangents*)mesh->getMeshBuffer(b)->getVertices(); + (video::S3DVertexTangents*)mb->getVertices(); buffer->Vertices.reallocate(idxCnt); buffer->Indices.reallocate(idxCnt); @@ -544,7 +549,7 @@ IMesh* CMeshManipulator::createMeshUniquePrimitives(IMesh* mesh) const buffer->Indices.push_back( i + 2 ); } - buffer->setBoundingBox(mesh->getMeshBuffer(b)->getBoundingBox()); + buffer->setBoundingBox(mb->getBoundingBox()); clone->addMeshBuffer(buffer); buffer->drop(); } @@ -567,30 +572,31 @@ IMesh* CMeshManipulator::createMeshWelded(IMesh *mesh, f32 tolerance) const for (u32 b=0; bgetMeshBufferCount(); ++b) { + const IMeshBuffer* const mb = mesh->getMeshBuffer(b); // reset redirect list - redirects.set_used(mesh->getMeshBuffer(b)->getVertexCount()); + redirects.set_used(mb->getVertexCount()); - u16* indices = 0; + const u16* indices = 0; u32 indexCount = 0; core::array* outIdx = 0; - switch(mesh->getMeshBuffer(b)->getVertexType()) + switch(mb->getVertexType()) { case video::EVT_STANDARD: { SMeshBuffer* buffer = new SMeshBuffer(); - buffer->BoundingBox = mesh->getMeshBuffer(b)->getBoundingBox(); - buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); + buffer->BoundingBox = mb->getBoundingBox(); + buffer->Material = mb->getMaterial(); clone->addMeshBuffer(buffer); buffer->drop(); video::S3DVertex* v = - (video::S3DVertex*)mesh->getMeshBuffer(b)->getVertices(); + (video::S3DVertex*)mb->getVertices(); - u32 vertexCount = mesh->getMeshBuffer(b)->getVertexCount(); + u32 vertexCount = mb->getVertexCount(); - indices = mesh->getMeshBuffer(b)->getIndices(); - indexCount = mesh->getMeshBuffer(b)->getIndexCount(); + indices = mb->getIndices(); + indexCount = mb->getIndexCount(); outIdx = &buffer->Indices; buffer->Vertices.reallocate(vertexCount); @@ -622,18 +628,18 @@ IMesh* CMeshManipulator::createMeshWelded(IMesh *mesh, f32 tolerance) const case video::EVT_2TCOORDS: { SMeshBufferLightMap* buffer = new SMeshBufferLightMap(); - buffer->BoundingBox = mesh->getMeshBuffer(b)->getBoundingBox(); - buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); + buffer->BoundingBox = mb->getBoundingBox(); + buffer->Material = mb->getMaterial(); clone->addMeshBuffer(buffer); buffer->drop(); video::S3DVertex2TCoords* v = - (video::S3DVertex2TCoords*)mesh->getMeshBuffer(b)->getVertices(); + (video::S3DVertex2TCoords*)mb->getVertices(); - u32 vertexCount = mesh->getMeshBuffer(b)->getVertexCount(); + u32 vertexCount = mb->getVertexCount(); - indices = mesh->getMeshBuffer(b)->getIndices(); - indexCount = mesh->getMeshBuffer(b)->getIndexCount(); + indices = mb->getIndices(); + indexCount = mb->getIndexCount(); outIdx = &buffer->Indices; buffer->Vertices.reallocate(vertexCount); @@ -665,18 +671,18 @@ IMesh* CMeshManipulator::createMeshWelded(IMesh *mesh, f32 tolerance) const case video::EVT_TANGENTS: { SMeshBufferTangents* buffer = new SMeshBufferTangents(); - buffer->BoundingBox = mesh->getMeshBuffer(b)->getBoundingBox(); - buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); + buffer->BoundingBox = mb->getBoundingBox(); + buffer->Material = mb->getMaterial(); clone->addMeshBuffer(buffer); buffer->drop(); video::S3DVertexTangents* v = - (video::S3DVertexTangents*)mesh->getMeshBuffer(b)->getVertices(); + (video::S3DVertexTangents*)mb->getVertices(); - u32 vertexCount = mesh->getMeshBuffer(b)->getVertexCount(); + u32 vertexCount = mb->getVertexCount(); - indices = mesh->getMeshBuffer(b)->getIndices(); - indexCount = mesh->getMeshBuffer(b)->getIndexCount(); + indices = mb->getIndices(); + indexCount = mb->getIndexCount(); outIdx = &buffer->Indices; buffer->Vertices.reallocate(vertexCount); @@ -737,7 +743,7 @@ IMesh* CMeshManipulator::createMeshWithTangents(IMesh* mesh, bool recalculateNor for (u32 b=0; bgetMeshBuffer(b); + const IMeshBuffer* const original = mesh->getMeshBuffer(b); const u32 idxCnt = original->getIndexCount(); const u16* idx = original->getIndices(); @@ -825,7 +831,7 @@ IMesh* CMeshManipulator::createMeshWith2TCoords(IMesh* mesh) const for (u32 b=0; bgetMeshBuffer(b); + const IMeshBuffer* const original = mesh->getMeshBuffer(b); const u32 idxCnt = original->getIndexCount(); const u16* idx = original->getIndices(); @@ -907,7 +913,7 @@ IMesh* CMeshManipulator::createMeshWith1TCoords(IMesh* mesh) const for (u32 b=0; bgetMeshBuffer(b); + const IMeshBuffer* const original = mesh->getMeshBuffer(b); const u32 idxCnt = original->getIndexCount(); const u16* idx = original->getIndices(); diff --git a/source/Irrlicht/CMeshSceneNode.cpp b/source/Irrlicht/CMeshSceneNode.cpp index 403ff47c..d0178c8d 100644 --- a/source/Irrlicht/CMeshSceneNode.cpp +++ b/source/Irrlicht/CMeshSceneNode.cpp @@ -10,6 +10,7 @@ #include "IMeshCache.h" #include "IAnimatedMesh.h" #include "IMaterialRenderer.h" +#include "IFileSystem.h" namespace irr { @@ -313,7 +314,15 @@ void CMeshSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeRea { IMeshSceneNode::serializeAttributes(out, options); - out->addString("Mesh", SceneManager->getMeshCache()->getMeshName(Mesh).getPath().c_str()); + if (options && (options->Flags&io::EARWF_USE_RELATIVE_PATHS) && options->Filename) + { + const io::path path = SceneManager->getFileSystem()->getRelativeFilename( + SceneManager->getFileSystem()->getAbsolutePath(SceneManager->getMeshCache()->getMeshName(Mesh).getPath()), + options->Filename); + out->addString("Mesh", path.c_str()); + } + else + out->addString("Mesh", SceneManager->getMeshCache()->getMeshName(Mesh).getPath().c_str()); out->addBool("ReadOnlyMaterials", ReadOnlyMaterials); } diff --git a/source/Irrlicht/CMetaTriangleSelector.cpp b/source/Irrlicht/CMetaTriangleSelector.cpp index d7fd366e..dac0da16 100644 --- a/source/Irrlicht/CMetaTriangleSelector.cpp +++ b/source/Irrlicht/CMetaTriangleSelector.cpp @@ -152,6 +152,37 @@ ISceneNode* CMetaTriangleSelector::getSceneNodeForTriangle(u32 triangleIndex) co } +/* Return the number of TriangleSelectors that are inside this one, +Only useful for MetaTriangleSelector others return 1 +*/ +u32 CMetaTriangleSelector::getSelectorCount() const +{ + return TriangleSelectors.size(); +} + + +/* Returns the TriangleSelector based on index based on getSelectorCount +Only useful for MetaTriangleSelector others return 'this' +*/ +ITriangleSelector* CMetaTriangleSelector::getSelector(u32 index) +{ + if (index >= TriangleSelectors.size()) + return 0; + return TriangleSelectors[index]; +} + + +/* Returns the TriangleSelector based on index based on getSelectorCount +Only useful for MetaTriangleSelector others return 'this' +*/ +const ITriangleSelector* CMetaTriangleSelector::getSelector(u32 index) const +{ + if (index >= TriangleSelectors.size()) + return 0; + return TriangleSelectors[index]; +} + + } // end namespace scene } // end namespace irr diff --git a/source/Irrlicht/CMetaTriangleSelector.h b/source/Irrlicht/CMetaTriangleSelector.h index 8b6aee32..798758a5 100644 --- a/source/Irrlicht/CMetaTriangleSelector.h +++ b/source/Irrlicht/CMetaTriangleSelector.h @@ -24,7 +24,7 @@ public: //! destructor virtual ~CMetaTriangleSelector(); - //! Returns amount of all available triangles in this selector + //! Get amount of all available triangles in this selector virtual s32 getTriangleCount() const; //! Gets all triangles. @@ -51,9 +51,18 @@ public: //! Removes all triangle selectors from the collection. virtual void removeAllTriangleSelectors(); - //! Return the scene node associated with a given triangle. + //! Get the scene node associated with a given triangle. virtual ISceneNode* getSceneNodeForTriangle(u32 triangleIndex) const; + // Get the number of TriangleSelectors that are part of this one + virtual u32 getSelectorCount() const; + + // Get the TriangleSelector based on index based on getSelectorCount + virtual ITriangleSelector* getSelector(u32 index); + + // Get the TriangleSelector based on index based on getSelectorCount + virtual const ITriangleSelector* getSelector(u32 index) const; + private: core::array TriangleSelectors; diff --git a/source/Irrlicht/CMountPointReader.cpp b/source/Irrlicht/CMountPointReader.cpp index f85cbe75..60e5bb29 100644 --- a/source/Irrlicht/CMountPointReader.cpp +++ b/source/Irrlicht/CMountPointReader.cpp @@ -129,6 +129,8 @@ void CMountPointReader::buildDirectory() else { const io::path rel = list->getFileName(i); + RealFileNames.push_back(list->getFullFileName(i)); + io::path pwd = Parent->getWorkingDirectory(); if (pwd.lastChar() != '/') pwd.append('/'); diff --git a/source/Irrlicht/CNullDriver.cpp b/source/Irrlicht/CNullDriver.cpp index db114043..5473d3bf 100644 --- a/source/Irrlicht/CNullDriver.cpp +++ b/source/Irrlicht/CNullDriver.cpp @@ -14,6 +14,7 @@ #include "IAnimatedMeshSceneNode.h" #include "CMeshManipulator.h" #include "CColorConverter.h" +#include "IAttributeExchangingObject.h" namespace irr @@ -120,14 +121,20 @@ CNullDriver::CNullDriver(io::IFileSystem* io, const core::dimension2d& scre // create surface loader -#ifdef _IRR_COMPILE_WITH_BMP_LOADER_ - SurfaceLoader.push_back(video::createImageLoaderBMP()); +#ifdef _IRR_COMPILE_WITH_HALFLIFE_LOADER_ + SurfaceLoader.push_back(video::createImageLoaderHalfLife()); #endif -#ifdef _IRR_COMPILE_WITH_JPG_LOADER_ - SurfaceLoader.push_back(video::createImageLoaderJPG()); +#ifdef _IRR_COMPILE_WITH_WAL_LOADER_ + SurfaceLoader.push_back(video::createImageLoaderWAL()); #endif -#ifdef _IRR_COMPILE_WITH_TGA_LOADER_ - SurfaceLoader.push_back(video::createImageLoaderTGA()); +#ifdef _IRR_COMPILE_WITH_LMP_LOADER_ + SurfaceLoader.push_back(video::createImageLoaderLMP()); +#endif +#ifdef _IRR_COMPILE_WITH_PPM_LOADER_ + SurfaceLoader.push_back(video::createImageLoaderPPM()); +#endif +#ifdef _IRR_COMPILE_WITH_RGB_LOADER_ + SurfaceLoader.push_back(video::createImageLoaderRGB()); #endif #ifdef _IRR_COMPILE_WITH_PSD_LOADER_ SurfaceLoader.push_back(video::createImageLoaderPSD()); @@ -138,49 +145,43 @@ CNullDriver::CNullDriver(io::IFileSystem* io, const core::dimension2d& scre #ifdef _IRR_COMPILE_WITH_PCX_LOADER_ SurfaceLoader.push_back(video::createImageLoaderPCX()); #endif +#ifdef _IRR_COMPILE_WITH_TGA_LOADER_ + SurfaceLoader.push_back(video::createImageLoaderTGA()); +#endif #ifdef _IRR_COMPILE_WITH_PNG_LOADER_ SurfaceLoader.push_back(video::createImageLoaderPNG()); #endif -#ifdef _IRR_COMPILE_WITH_WAL_LOADER_ - SurfaceLoader.push_back(video::createImageLoaderWAL()); +#ifdef _IRR_COMPILE_WITH_JPG_LOADER_ + SurfaceLoader.push_back(video::createImageLoaderJPG()); #endif -#ifdef _IRR_COMPILE_WITH_LMP_LOADER_ - SurfaceLoader.push_back(video::createImageLoaderLMP()); -#endif -#ifdef _IRR_COMPILE_WITH_HALFLIFE_LOADER_ - SurfaceLoader.push_back(video::createImageLoaderHalfLife()); -#endif - -#ifdef _IRR_COMPILE_WITH_PPM_LOADER_ - SurfaceLoader.push_back(video::createImageLoaderPPM()); -#endif -#ifdef _IRR_COMPILE_WITH_RGB_LOADER_ - SurfaceLoader.push_back(video::createImageLoaderRGB()); +#ifdef _IRR_COMPILE_WITH_BMP_LOADER_ + SurfaceLoader.push_back(video::createImageLoaderBMP()); #endif -#ifdef _IRR_COMPILE_WITH_BMP_WRITER_ - SurfaceWriter.push_back(video::createImageWriterBMP()); -#endif -#ifdef _IRR_COMPILE_WITH_JPG_WRITER_ - SurfaceWriter.push_back(video::createImageWriterJPG()); -#endif -#ifdef _IRR_COMPILE_WITH_TGA_WRITER_ - SurfaceWriter.push_back(video::createImageWriterTGA()); -#endif -#ifdef _IRR_COMPILE_WITH_PSD_WRITER_ - SurfaceWriter.push_back(video::createImageWriterPSD()); +#ifdef _IRR_COMPILE_WITH_PPM_WRITER_ + SurfaceWriter.push_back(video::createImageWriterPPM()); #endif #ifdef _IRR_COMPILE_WITH_PCX_WRITER_ SurfaceWriter.push_back(video::createImageWriterPCX()); #endif +#ifdef _IRR_COMPILE_WITH_PSD_WRITER_ + SurfaceWriter.push_back(video::createImageWriterPSD()); +#endif +#ifdef _IRR_COMPILE_WITH_TGA_WRITER_ + SurfaceWriter.push_back(video::createImageWriterTGA()); +#endif +#ifdef _IRR_COMPILE_WITH_JPG_WRITER_ + SurfaceWriter.push_back(video::createImageWriterJPG()); +#endif #ifdef _IRR_COMPILE_WITH_PNG_WRITER_ SurfaceWriter.push_back(video::createImageWriterPNG()); #endif -#ifdef _IRR_COMPILE_WITH_PPM_WRITER_ - SurfaceWriter.push_back(video::createImageWriterPPM()); +#ifdef _IRR_COMPILE_WITH_BMP_WRITER_ + SurfaceWriter.push_back(video::createImageWriterBMP()); #endif + // set ExposedData to 0 memset(&ExposedData, 0, sizeof(ExposedData)); for (u32 i=0; i=0; --i) { if (SurfaceLoader[i]->isALoadableFileExtension(file->getFileName())) { @@ -1333,7 +1335,7 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file) } // try to load file based on what is in it - for (i=0; i=0; --i) { // dito file->seek(0); @@ -1369,7 +1371,7 @@ bool CNullDriver::writeImageToFile(IImage* image, io::IWriteFile * file, u32 par if(!file) return false; - for (u32 i=0; i=0; --i) { if (SurfaceWriter[i]->isAWriteableFileExtension(file->getFileName())) { @@ -1593,7 +1595,7 @@ bool CNullDriver::isHardwareBufferRecommend(const scene::IMeshBuffer* mb) //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ -void CNullDriver::createOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh) +void CNullDriver::addOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh) { if (!node) return; @@ -1789,7 +1791,8 @@ void CNullDriver::setMaterialRendererName(s32 idx, const char* name) //! Creates material attributes list from a material, usable for serialization and more. -io::IAttributes* CNullDriver::createAttributesFromMaterial(const video::SMaterial& material) +io::IAttributes* CNullDriver::createAttributesFromMaterial(const video::SMaterial& material, + io::SAttributeReadWriteOptions* options) { io::CAttributes* attr = new io::CAttributes(this); @@ -1807,7 +1810,16 @@ io::IAttributes* CNullDriver::createAttributesFromMaterial(const video::SMateria core::stringc prefix="Texture"; u32 i; for (i=0; iaddTexture((prefix+core::stringc(i+1)).c_str(), material.getTexture(i)); + { + if (options && (options->Flags&io::EARWF_USE_RELATIVE_PATHS) && options->Filename && material.getTexture(i)) + { + io::path path = FileSystem->getRelativeFilename( + FileSystem->getAbsolutePath(material.getTexture(i)->getName()), options->Filename); + attr->addTexture((prefix+core::stringc(i+1)).c_str(), material.getTexture(i), path); + } + else + attr->addTexture((prefix+core::stringc(i+1)).c_str(), material.getTexture(i)); + } attr->addBool("Wireframe", material.Wireframe); attr->addBool("GouraudShading", material.GouraudShading); diff --git a/source/Irrlicht/CNullDriver.h b/source/Irrlicht/CNullDriver.h index add1e820..102523fe 100644 --- a/source/Irrlicht/CNullDriver.h +++ b/source/Irrlicht/CNullDriver.h @@ -22,6 +22,10 @@ #include "SLight.h" #include "SExposedVideoData.h" +#ifdef _MSC_VER +#pragma warning( disable: 4996) +#endif + namespace irr { namespace io @@ -424,7 +428,7 @@ namespace video //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ - virtual void createOcclusionQuery(scene::ISceneNode* node, + virtual void addOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh=0); //! Remove occlusion query. @@ -588,7 +592,8 @@ namespace video virtual void setMaterialRendererName(s32 idx, const char* name); //! Creates material attributes list from a material, usable for serialization and more. - virtual io::IAttributes* createAttributesFromMaterial(const video::SMaterial& material); + virtual io::IAttributes* createAttributesFromMaterial(const video::SMaterial& material, + io::SAttributeReadWriteOptions* options=0); //! Fills an SMaterial structure from attributes. virtual void fillMaterialStructureFromAttributes(video::SMaterial& outMaterial, io::IAttributes* attributes); diff --git a/source/Irrlicht/COBJMeshFileLoader.cpp b/source/Irrlicht/COBJMeshFileLoader.cpp index e1b3353d..1fc85b79 100644 --- a/source/Irrlicht/COBJMeshFileLoader.cpp +++ b/source/Irrlicht/COBJMeshFileLoader.cpp @@ -166,7 +166,7 @@ IAnimatedMesh* COBJMeshFileLoader::createMesh(io::IReadFile* file) if (core::stringc("off")==smooth) smoothingGroup=0; else - smoothingGroup=core::strtol10(smooth, 0); + smoothingGroup=core::strtoul10(smooth); } break; @@ -219,7 +219,7 @@ IAnimatedMesh* COBJMeshFileLoader::createMesh(io::IReadFile* file) Idx[1] = Idx[2] = -1; // read in next vertex's data - u32 wlength = copyWord(vertexWord, linePtr, WORD_BUFFER_LENGTH, bufEnd); + u32 wlength = copyWord(vertexWord, linePtr, WORD_BUFFER_LENGTH, endPtr); // this function will also convert obj's 1-based index to c++'s 0-based index retrieveVertexIndices(vertexWord, Idx, vertexWord+wlength+1, vertexBuffer.size(), textureCoordBuffer.size(), normalsBuffer.size()); v.Pos = vertexBuffer[Idx[0]]; @@ -830,7 +830,8 @@ core::stringc COBJMeshFileLoader::copyLine(const c8* inBuf, const c8* bufEnd) break; ++ptr; } - return core::stringc(inBuf, (u32)(ptr-inBuf+1)); + // we must avoid the +1 in case the array is used up + return core::stringc(inBuf, (u32)(ptr-inBuf+((ptr < bufEnd) ? 1 : 0))); } @@ -861,10 +862,9 @@ bool COBJMeshFileLoader::retrieveVertexIndices(c8* vertexData, s32* idx, const c // number is completed. Convert and store it word[i] = '\0'; // if no number was found index will become 0 and later on -1 by decrement - if (word[0]=='-') + idx[idxType] = core::strtol10(word); + if (idx[idxType]<0) { - idx[idxType] = core::strtol10(word+1,0); - idx[idxType] *= -1; switch (idxType) { case 0: @@ -879,7 +879,7 @@ bool COBJMeshFileLoader::retrieveVertexIndices(c8* vertexData, s32* idx, const c } } else - idx[idxType] = core::strtol10(word,0)-1; + idx[idxType]-=1; // reset the word word[0] = '\0'; diff --git a/source/Irrlicht/COgreMeshFileLoader.cpp b/source/Irrlicht/COgreMeshFileLoader.cpp index 3a96a30a..063d67a7 100644 --- a/source/Irrlicht/COgreMeshFileLoader.cpp +++ b/source/Irrlicht/COgreMeshFileLoader.cpp @@ -467,12 +467,12 @@ void COgreMeshFileLoader::composeMeshBufferMaterial(scene::IMeshBuffer* mb, cons if ((materialName==Materials[k].Name)&&(Materials[k].Techniques.size())&&(Materials[k].Techniques[0].Passes.size())) { material=Materials[k].Techniques[0].Passes[0].Material; - if (Materials[k].Techniques[0].Passes[0].Texture.Filename.size()) + for (u32 i=0; iexistFile(Materials[k].Techniques[0].Passes[0].Texture.Filename)) - material.setTexture(0, Driver->getTexture(Materials[k].Techniques[0].Passes[0].Texture.Filename)); + if (FileSystem->existFile(Materials[k].Techniques[0].Passes[0].Texture.Filename[i])) + material.setTexture(i, Driver->getTexture(Materials[k].Techniques[0].Passes[0].Texture.Filename[i])); else - material.setTexture(0, Driver->getTexture((CurrentlyLoadingFromPath+"/"+FileSystem->getFileBasename(Materials[k].Techniques[0].Passes[0].Texture.Filename)))); + material.setTexture(i, Driver->getTexture((CurrentlyLoadingFromPath+"/"+FileSystem->getFileBasename(Materials[k].Techniques[0].Passes[0].Texture.Filename[i])))); } break; } @@ -604,11 +604,15 @@ scene::SMeshBufferLightMap* COgreMeshFileLoader::composeMeshBufferLightMap(const { u32 eSize=geom.Buffers[j].VertexSize; u32 ePos=geom.Elements[i].Offset; + // make sure we have data for a second texture coord + const bool secondCoord = (eSize>ePos+3); for (s32 k=0; kVertices[k].TCoords.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]); - mb->Vertices[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]); - + if (secondCoord) + mb->Vertices[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]); + else + mb->Vertices[k].TCoords2.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]); ePos += eSize; } } @@ -688,12 +692,18 @@ scene::IMeshBuffer* COgreMeshFileLoader::composeMeshBufferSkinned(scene::CSkinne { u32 eSize=geom.Buffers[j].VertexSize; u32 ePos=geom.Elements[i].Offset; + // make sure we have data for a second texture coord + const bool secondCoord = (eSize>ePos+3); for (s32 k=0; kgetTCoords(k).set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]); if (NumUV>1) - mb->Vertices_2TCoords[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]); - + { + if (secondCoord) + mb->Vertices_2TCoords[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]); + else + mb->Vertices_2TCoords[k].TCoords2.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]); + } ePos += eSize; } } @@ -1057,7 +1067,7 @@ void COgreMeshFileLoader::readPass(io::IReadFile* file, OgreTechnique& technique else if (token=="max_lights") { getMaterialToken(file, token); - pass.MaxLights=strtol(token.c_str(),NULL,10); + pass.MaxLights=core::strtoul10(token.c_str()); } else if (token=="point_size") { @@ -1072,12 +1082,12 @@ void COgreMeshFileLoader::readPass(io::IReadFile* file, OgreTechnique& technique else if (token=="point_size_min") { getMaterialToken(file, token); - pass.PointSizeMin=strtol(token.c_str(),NULL,10); + pass.PointSizeMin=core::strtoul10(token.c_str()); } else if (token=="point_size_max") { getMaterialToken(file, token); - pass.PointSizeMax=strtol(token.c_str(),NULL,10); + pass.PointSizeMax=core::strtoul10(token.c_str()); } else if (token=="texture_unit") { @@ -1090,13 +1100,17 @@ void COgreMeshFileLoader::readPass(io::IReadFile* file, OgreTechnique& technique { if (token=="texture") { - getMaterialToken(file, pass.Texture.Filename); + getMaterialToken(file, token); + pass.Texture.Filename.push_back(token); #ifdef IRR_OGRE_LOADER_DEBUG - os::Printer::log("Read Texture", pass.Texture.Filename.c_str(), ELL_DEBUG); + os::Printer::log("Read Texture", token, ELL_DEBUG); #endif getMaterialToken(file, pass.Texture.CoordsType, true); getMaterialToken(file, pass.Texture.MipMaps, true); getMaterialToken(file, pass.Texture.Alpha, true); + // Hmm, we might need more hints for other material types using two textures... + if (textureUnit>0) + pass.Material.MaterialType=video::EMT_LIGHTMAP; } else if (token=="filtering") { @@ -1135,7 +1149,7 @@ void COgreMeshFileLoader::readPass(io::IReadFile* file, OgreTechnique& technique else if (token=="max_anisotropy") { getMaterialToken(file, token); - pass.Material.TextureLayer[textureUnit].AnisotropicFilter=(u8)core::strtol10(token.c_str()); + pass.Material.TextureLayer[textureUnit].AnisotropicFilter=(u8)core::strtoul10(token.c_str()); } else if (token=="texture_alias") { diff --git a/source/Irrlicht/COgreMeshFileLoader.h b/source/Irrlicht/COgreMeshFileLoader.h index 127582a0..231ddf9e 100644 --- a/source/Irrlicht/COgreMeshFileLoader.h +++ b/source/Irrlicht/COgreMeshFileLoader.h @@ -81,9 +81,7 @@ private: struct OgreTexture { - OgreTexture() : Filename("") {} - - core::stringc Filename; + core::array Filename; core::stringc Alias; core::stringc CoordsType; core::stringc MipMaps; diff --git a/source/Irrlicht/COpenGLDriver.cpp b/source/Irrlicht/COpenGLDriver.cpp index 3ff3748a..13ada6f2 100644 --- a/source/Irrlicht/COpenGLDriver.cpp +++ b/source/Irrlicht/COpenGLDriver.cpp @@ -39,7 +39,7 @@ COpenGLDriver::COpenGLDriver(const irr::SIrrlichtCreationParameters& params, CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8), CurrentTarget(ERT_FRAME_BUFFER), Doublebuffer(params.Doublebuffer), Stereo(params.Stereobuffer), - HDc(0), Window(static_cast(params.WindowId)), Device(device), + HDc(0), Window(static_cast(params.WindowId)), Win32Device(device), DeviceType(EIDT_WIN32) { #ifdef _DEBUG @@ -78,7 +78,6 @@ bool COpenGLDriver::changeRenderContext(const SExposedVideoData& videoData, CIrr return true; } - //! inits the open gl driver bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDeviceWin32* device) { @@ -235,7 +234,7 @@ bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDevi return false; } - io::path wglExtensions; + core::stringc wglExtensions; #ifdef WGL_ARB_extensions_string PFNWGLGETEXTENSIONSSTRINGARBPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB"); if (irrGetExtensionsString) @@ -248,13 +247,15 @@ bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDevi const bool pixel_format_supported = (wglExtensions.find("WGL_ARB_pixel_format") != -1); const bool multi_sample_supported = ((wglExtensions.find("WGL_ARB_multisample") != -1) || (wglExtensions.find("WGL_EXT_multisample") != -1) || (wglExtensions.find("WGL_3DFX_multisample") != -1) ); + const bool framebuffer_srgb_supported = ((wglExtensions.find("WGL_ARB_framebuffer_sRGB") != -1) || + (wglExtensions.find("WGL_EXT_framebuffer_sRGB") != -1) ); #ifdef _DEBUG os::Printer::log("WGL_extensions", wglExtensions); #endif #ifdef WGL_ARB_pixel_format PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat_ARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); - if (pixel_format_supported && multi_sample_supported && wglChoosePixelFormat_ARB) + if (pixel_format_supported && wglChoosePixelFormat_ARB) { // This value determines the number of samples used for antialiasing // My experience is that 8 does not show a big @@ -276,41 +277,48 @@ bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDevi WGL_STENCIL_BITS_ARB,(params.Stencilbuffer) ? 1 : 0, WGL_DOUBLE_BUFFER_ARB,(params.Doublebuffer) ? GL_TRUE : GL_FALSE, WGL_STEREO_ARB,(params.Stereobuffer) ? GL_TRUE : GL_FALSE, -#ifdef WGL_ARB_multisample - WGL_SAMPLE_BUFFERS_ARB, 1, - WGL_SAMPLES_ARB,AntiAlias, // 20,21 -#elif defined(WGL_EXT_multisample) - WGL_SAMPLE_BUFFERS_EXT, 1, - WGL_SAMPLES_EXT,AntiAlias, // 20,21 -#elif defined(WGL_3DFX_multisample) - WGL_SAMPLE_BUFFERS_3DFX, 1, - WGL_SAMPLES_3DFX,AntiAlias, // 20,21 -#endif WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, - // other possible values: - // WGL_ARB_pixel_format_float: WGL_TYPE_RGBA_FLOAT_ARB - // WGL_EXT_pixel_format_packed_float: WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT +#ifdef WGL_ARB_multisample + WGL_SAMPLES_ARB,AntiAlias, // 20,21 + WGL_SAMPLE_BUFFERS_ARB, 1, +#elif defined(WGL_EXT_multisample) + WGL_SAMPLES_EXT,AntiAlias, // 20,21 + WGL_SAMPLE_BUFFERS_EXT, 1, +#elif defined(WGL_3DFX_multisample) + WGL_SAMPLES_3DFX,AntiAlias, // 20,21 + WGL_SAMPLE_BUFFERS_3DFX, 1, +#endif #if 0 -#ifdef WGL_EXT_framebuffer_sRGB - WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT, GL_FALSE, +#ifdef WGL_ARB_framebuffer_sRGB + WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE, +#elif defined(WGL_EXT_framebuffer_sRGB) + WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT, GL_TRUE, #endif #endif 0,0 }; + if (!multi_sample_supported) + { + iAttributes[20]=0; + iAttributes[21]=0; + iAttributes[22]=0; + iAttributes[23]=0; + } s32 rv=0; // Try to get an acceptable pixel format - while(rv==0 && iAttributes[21]>1) + do { - s32 pixelFormat=0; - u32 numFormats=0; - const s32 valid = wglChoosePixelFormat_ARB(HDc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + int pixelFormat=0; + UINT numFormats=0; + const BOOL valid = wglChoosePixelFormat_ARB(HDc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); if (valid && numFormats>0) rv = pixelFormat; else iAttributes[21] -= 1; } + while(rv==0 && iAttributes[21]>1); if (rv) { PixelFormat=rv; @@ -336,7 +344,7 @@ bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDevi } // search for pixel format the simple way - if (AntiAlias < 2) + if (PixelFormat==0 || (!SetPixelFormat(HDc, PixelFormat, &pfd))) { for (u32 i=0; i<5; ++i) { @@ -440,14 +448,7 @@ bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDevi genericDriverInit(params.WindowSize, params.Stencilbuffer); -#ifdef WGL_EXT_swap_control - PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; - // vsync extension - wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); - // set vsync - if (wglSwapIntervalEXT) - wglSwapIntervalEXT(params.Vsync ? 1 : 0); -#endif + extGlSwapInterval(params.Vsync ? 1 : 0); return true; } @@ -466,7 +467,7 @@ COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params, CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8), CurrentTarget(ERT_FRAME_BUFFER), Doublebuffer(params.Doublebuffer), Stereo(params.Stereobuffer), - Device(device), DeviceType(EIDT_OSX) + OSXDevice(device), DeviceType(EIDT_OSX) { #ifdef _DEBUG setDebugName("COpenGLDriver"); @@ -489,7 +490,7 @@ COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params, RenderTargetTexture(0), CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8), CurrentTarget(ERT_FRAME_BUFFER), Doublebuffer(params.Doublebuffer), Stereo(params.Stereobuffer), - Device(device), DeviceType(EIDT_X11) + X11Device(device), DeviceType(EIDT_X11) { #ifdef _DEBUG setDebugName("COpenGLDriver"); @@ -542,16 +543,7 @@ bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params, CIrrDevi genericDriverInit(params.WindowSize, params.Stencilbuffer); // set vsync - //TODO: Check GLX_EXT_swap_control and GLX_MESA_swap_control -#ifdef GLX_SGI_swap_control -#ifdef _IRR_OPENGL_USE_EXTPOINTER_ - if (params.Vsync && glxSwapIntervalSGI) - glxSwapIntervalSGI(1); -#else - if (params.Vsync) - glXSwapIntervalSGI(1); -#endif -#endif + extGlSwapInterval(params.Vsync ? 1 : 0); return true; } @@ -571,7 +563,7 @@ COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params, RenderTargetTexture(0), CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8), CurrentTarget(ERT_FRAME_BUFFER), Doublebuffer(params.Doublebuffer), Stereo(params.Stereobuffer), - Device(device), DeviceType(EIDT_SDL) + SDLDevice(device), DeviceType(EIDT_SDL) { #ifdef _DEBUG setDebugName("COpenGLDriver"); @@ -797,7 +789,7 @@ bool COpenGLDriver::endScene() #ifdef _IRR_COMPILE_WITH_OSX_DEVICE_ if (DeviceType == EIDT_OSX) { - Device->flush(); + OSXDevice->flush(); return true; } #endif @@ -850,7 +842,22 @@ bool COpenGLDriver::beginScene(bool backBuffer, bool zBuffer, SColor color, { CNullDriver::beginScene(backBuffer, zBuffer, color, videoData, sourceRect); - changeRenderContext(videoData, Device); + switch (DeviceType) + { +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ + case EIDT_WIN32: + changeRenderContext(videoData, Win32Device); + break; +#endif +#ifdef _IRR_COMPILE_WITH_X11_DEVICE_ + case EIDT_X11: + changeRenderContext(videoData, X11Device); + break; +#endif + default: + changeRenderContext(videoData, (void*)0); + break; + } #if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) if (DeviceType == EIDT_SDL) @@ -923,9 +930,9 @@ void COpenGLDriver::setTransform(E_TRANSFORMATION_STATE state, const core::matri { GLfloat glmat[16]; if (isRTT) - createGLTextureMatrix(glmat, mat * TextureFlipMatrix); + getGLTextureMatrix(glmat, mat * TextureFlipMatrix); else - createGLTextureMatrix(glmat, mat); + getGLTextureMatrix(glmat, mat); glLoadMatrixf(glmat); } break; @@ -1248,16 +1255,16 @@ void COpenGLDriver::drawHardwareBuffer(SHWBufferLink *_HWBuffer) //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ -void COpenGLDriver::createOcclusionQuery(scene::ISceneNode* node, +void COpenGLDriver::addOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh) { if (!queryFeature(EVDF_OCCLUSION_QUERY)) return; - CNullDriver::createOcclusionQuery(node, mesh); + CNullDriver::addOcclusionQuery(node, mesh); const s32 index = OcclusionQueries.linear_search(SOccQuery(node)); if ((index != -1) && (OcclusionQueries[index].UID == 0)) - extGlGenQueries(1, &OcclusionQueries[index].UID); + extGlGenQueries(1, reinterpret_cast(&OcclusionQueries[index].UID)); } @@ -1268,7 +1275,7 @@ void COpenGLDriver::removeOcclusionQuery(scene::ISceneNode* node) if (index != -1) { if (OcclusionQueries[index].UID != 0) - extGlDeleteQueries(1, &OcclusionQueries[index].UID); + extGlDeleteQueries(1, reinterpret_cast(&OcclusionQueries[index].UID)); CNullDriver::removeOcclusionQuery(node); } } @@ -1383,7 +1390,7 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun CNullDriver::drawVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType); if (vertices && !FeatureAvailable[IRR_ARB_vertex_array_bgra] && !FeatureAvailable[IRR_EXT_vertex_array_bgra]) - createColorBuffer(vertices, vertexCount, vType); + getColorBuffer(vertices, vertexCount, vType); // draw everything setRenderStates3DMode(); @@ -1398,27 +1405,36 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun if ((pType!=scene::EPT_POINTS) && (pType!=scene::EPT_POINT_SPRITES)) glEnableClientState(GL_NORMAL_ARRAY); +//due to missing defines in OSX headers, we have to be more specific with this check +//#if defined(GL_ARB_vertex_array_bgra) || defined(GL_EXT_vertex_array_bgra) +#ifdef GL_BGRA + const GLint colorSize=(FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra])?GL_BGRA:4; +#else + const GLint colorSize=4; +#endif if (vertices) { -#if defined(GL_ARB_vertex_array_bgra) || defined(GL_EXT_vertex_array_bgra) if (FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra]) { switch (vType) { case EVT_STANDARD: - glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, sizeof(S3DVertex), &(static_cast(vertices))[0].Color); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex), &(static_cast(vertices))[0].Color); break; case EVT_2TCOORDS: - glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), &(static_cast(vertices))[0].Color); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), &(static_cast(vertices))[0].Color); break; case EVT_TANGENTS: - glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].Color); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].Color); break; } } else -#endif - glColorPointer(4, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); + { + // avoid passing broken pointer to OpenGL + _IRR_DEBUG_BREAK_IF(ColorBuffer.size()==0); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); + } } switch (vType) @@ -1433,7 +1449,7 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun else { glNormalPointer(GL_FLOAT, sizeof(S3DVertex), buffer_offset(12)); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex), buffer_offset(24)); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex), buffer_offset(24)); glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), buffer_offset(28)); glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex), 0); } @@ -1458,7 +1474,7 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun else { glNormalPointer(GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(12)); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), buffer_offset(24)); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), buffer_offset(24)); glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(28)); glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(0)); } @@ -1484,7 +1500,7 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun else { glNormalPointer(GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(12)); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), buffer_offset(24)); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), buffer_offset(24)); glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(28)); glVertexPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(0)); } @@ -1531,7 +1547,7 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun } -void COpenGLDriver::createColorBuffer(const void* vertices, u32 vertexCount, E_VERTEX_TYPE vType) +void COpenGLDriver::getColorBuffer(const void* vertices, u32 vertexCount, E_VERTEX_TYPE vType) { // convert colors to gl color format. vertexCount *= 4; //reused as color component count @@ -1694,7 +1710,7 @@ void COpenGLDriver::draw2DVertexPrimitiveList(const void* vertices, u32 vertexCo CNullDriver::draw2DVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType); if (vertices && !FeatureAvailable[IRR_ARB_vertex_array_bgra] && !FeatureAvailable[IRR_EXT_vertex_array_bgra]) - createColorBuffer(vertices, vertexCount, vType); + getColorBuffer(vertices, vertexCount, vType); // draw everything this->setActiveTexture(0, Material.getTexture(0)); @@ -1718,27 +1734,36 @@ void COpenGLDriver::draw2DVertexPrimitiveList(const void* vertices, u32 vertexCo if ((pType!=scene::EPT_POINTS) && (pType!=scene::EPT_POINT_SPRITES)) glEnableClientState(GL_TEXTURE_COORD_ARRAY); +//due to missing defines in OSX headers, we have to be more specific with this check +//#if defined(GL_ARB_vertex_array_bgra) || defined(GL_EXT_vertex_array_bgra) +#ifdef GL_BGRA + const GLint colorSize=(FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra])?GL_BGRA:4; +#else + const GLint colorSize=4; +#endif if (vertices) { -#if defined(GL_ARB_vertex_array_bgra) || defined(GL_EXT_vertex_array_bgra) if (FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra]) { switch (vType) { case EVT_STANDARD: - glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, sizeof(S3DVertex), &(static_cast(vertices))[0].Color); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex), &(static_cast(vertices))[0].Color); break; case EVT_2TCOORDS: - glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), &(static_cast(vertices))[0].Color); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), &(static_cast(vertices))[0].Color); break; case EVT_TANGENTS: - glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].Color); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].Color); break; } } else -#endif - glColorPointer(4, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); + { + // avoid passing broken pointer to OpenGL + _IRR_DEBUG_BREAK_IF(ColorBuffer.size()==0); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); + } } switch (vType) @@ -1751,7 +1776,7 @@ void COpenGLDriver::draw2DVertexPrimitiveList(const void* vertices, u32 vertexCo } else { - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex), buffer_offset(24)); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex), buffer_offset(24)); glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), buffer_offset(28)); glVertexPointer(2, GL_FLOAT, sizeof(S3DVertex), 0); } @@ -1774,7 +1799,7 @@ void COpenGLDriver::draw2DVertexPrimitiveList(const void* vertices, u32 vertexCo } else { - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), buffer_offset(24)); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), buffer_offset(24)); glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(28)); glVertexPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(0)); } @@ -1797,7 +1822,7 @@ void COpenGLDriver::draw2DVertexPrimitiveList(const void* vertices, u32 vertexCo } else { - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), buffer_offset(24)); + glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), buffer_offset(24)); glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(28)); glVertexPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(0)); } @@ -2373,14 +2398,14 @@ bool COpenGLDriver::disableTextures(u32 fromStage) //! creates a matrix in supplied GLfloat array to pass to OpenGL -inline void COpenGLDriver::createGLMatrix(GLfloat gl_matrix[16], const core::matrix4& m) +inline void COpenGLDriver::getGLMatrix(GLfloat gl_matrix[16], const core::matrix4& m) { memcpy(gl_matrix, m.pointer(), 16 * sizeof(f32)); } //! creates a opengltexturematrix from a D3D style texture matrix -inline void COpenGLDriver::createGLTextureMatrix(GLfloat *o, const core::matrix4& m) +inline void COpenGLDriver::getGLTextureMatrix(GLfloat *o, const core::matrix4& m) { o[0] = m[0]; o[1] = m[1]; @@ -2756,7 +2781,7 @@ void COpenGLDriver::setBasicRenderStates(const SMaterial& material, const SMater glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (material.TextureLayer[i].BilinearFilter || material.TextureLayer[i].TrilinearFilter) ? GL_LINEAR : GL_NEAREST); - if (material.getTexture(i) && material.getTexture(i)->hasMipMaps() && material.UseMipMaps) + if (material.UseMipMaps && CurrentTexture[i]->hasMipMaps()) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, material.TextureLayer[i].TrilinearFilter ? GL_LINEAR_MIPMAP_LINEAR : material.TextureLayer[i].BilinearFilter ? GL_LINEAR_MIPMAP_NEAREST : @@ -2896,6 +2921,130 @@ void COpenGLDriver::setBasicRenderStates(const SMaterial& material, const SMater (material.ColorMask & ECP_ALPHA)?GL_TRUE:GL_FALSE); } + if (queryFeature(EVDF_BLEND_OPERATIONS) && + (resetAllRenderStates|| lastmaterial.BlendOperation != material.BlendOperation)) + { + if (EBO_NONE) + glDisable(GL_BLEND); + else + { + glEnable(GL_BLEND); +#if defined(GL_EXT_blend_subtract) || defined(GL_EXT_blend_minmax) || defined(GL_EXT_blend_logic_op) || defined(GL_VERSION_1_2) + switch (material.BlendOperation) + { + case EBO_SUBTRACT: +#if defined(GL_EXT_blend_subtract) + if (FeatureAvailable[IRR_EXT_blend_subtract] || (Version>=120)) + extGlBlendEquation(GL_FUNC_SUBTRACT_EXT); +#elif defined(GL_VERSION_1_2) + if (Version>=120) + extGlBlendEquation(GL_FUNC_SUBTRACT); +#endif + break; + case EBO_REVSUBTRACT: +#if defined(GL_EXT_blend_subtract) + if (FeatureAvailable[IRR_EXT_blend_subtract] || (Version>=120)) + extGlBlendEquation(GL_FUNC_REVERSE_SUBTRACT_EXT); +#elif defined(GL_VERSION_1_2) + if (Version>=120) + extGlBlendEquation(GL_FUNC_REVERSE_SUBTRACT); +#endif + break; + case EBO_MIN: +#if defined(GL_EXT_blend_minmax) + if (FeatureAvailable[IRR_EXT_blend_minmax] || (Version>=120)) + extGlBlendEquation(GL_MIN_EXT); +#elif defined(GL_VERSION_1_2) + if (Version>=120) + extGlBlendEquation(GL_MIN); +#endif + break; + case EBO_MAX: +#if defined(GL_EXT_blend_minmax) + if (FeatureAvailable[IRR_EXT_blend_minmax] || (Version>=120)) + extGlBlendEquation(GL_MAX_EXT); +#elif defined(GL_VERSION_1_2) + if (Version>=120) + extGlBlendEquation(GL_MAX); +#endif + break; + case EBO_MIN_FACTOR: +#if defined(GL_AMD_blend_minmax_factor) + if (FeatureAvailable[IRR_AMD_blend_minmax_factor]) + extGlBlendEquation(GL_FACTOR_MIN_AMD); +#endif + // fallback in case of missing extension +#if defined(GL_VERSION_1_2) +#if defined(GL_AMD_blend_minmax_factor) + else +#endif + if (Version>=120) + extGlBlendEquation(GL_MIN); +#endif + break; + case EBO_MAX_FACTOR: +#if defined(GL_AMD_blend_minmax_factor) + if (FeatureAvailable[IRR_AMD_blend_minmax_factor]) + extGlBlendEquation(GL_FACTOR_MAX_AMD); +#endif + // fallback in case of missing extension +#if defined(GL_VERSION_1_2) +#if defined(GL_AMD_blend_minmax_factor) + else +#endif + if (Version>=120) + extGlBlendEquation(GL_MAX); +#endif + break; + case EBO_MIN_ALPHA: +#if defined(GL_SGIX_blend_alpha_minmax) + if (FeatureAvailable[IRR_SGIX_blend_alpha_minmax]) + extGlBlendEquation(GL_ALPHA_MIN_SGIX); + // fallback in case of missing extension + else + if (FeatureAvailable[IRR_EXT_blend_minmax]) + extGlBlendEquation(GL_MIN_EXT); +#endif + break; + case EBO_MAX_ALPHA: +#if defined(GL_SGIX_blend_alpha_minmax) + if (FeatureAvailable[IRR_SGIX_blend_alpha_minmax]) + extGlBlendEquation(GL_ALPHA_MAX_SGIX); + // fallback in case of missing extension + else + if (FeatureAvailable[IRR_EXT_blend_minmax]) + extGlBlendEquation(GL_MAX_EXT); +#endif + break; + default: +#if defined(GL_EXT_blend_subtract) || defined(GL_EXT_blend_minmax) || defined(GL_EXT_blend_logic_op) + extGlBlendEquation(GL_FUNC_ADD_EXT); +#elif defined(GL_VERSION_1_2) + extGlBlendEquation(GL_FUNC_ADD); +#endif + break; + } +#endif + } + } + + // Polygon Offset + if (queryFeature(EVDF_POLYGON_OFFSET) && (resetAllRenderStates || + lastmaterial.PolygonOffsetDirection != material.PolygonOffsetDirection || + lastmaterial.PolygonOffsetFactor != material.PolygonOffsetFactor)) + { + glDisable(lastmaterial.Wireframe?GL_POLYGON_OFFSET_LINE:lastmaterial.PointCloud?GL_POLYGON_OFFSET_POINT:GL_POLYGON_OFFSET_FILL); + if (material.PolygonOffsetFactor) + { + glDisable(material.Wireframe?GL_POLYGON_OFFSET_LINE:material.PointCloud?GL_POLYGON_OFFSET_POINT:GL_POLYGON_OFFSET_FILL); + glEnable(material.Wireframe?GL_POLYGON_OFFSET_LINE:material.PointCloud?GL_POLYGON_OFFSET_POINT:GL_POLYGON_OFFSET_FILL); + } + if (material.PolygonOffsetDirection==EPO_BACK) + glPolygonOffset(1.0f, (GLfloat)material.PolygonOffsetFactor); + else + glPolygonOffset(-1.0f, (GLfloat)-material.PolygonOffsetFactor); + } + // thickness if (resetAllRenderStates || lastmaterial.Thickness != material.Thickness) { @@ -3355,13 +3504,13 @@ void COpenGLDriver::drawStencilShadowVolume(const core::vector3df* triangles, s3 glDepthMask(GL_FALSE); // no depth buffer writing glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no color buffer drawing glEnable(GL_STENCIL_TEST); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(0.0f, 1.0f); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3,GL_FLOAT,sizeof(core::vector3df),&triangles[0]); glStencilMask(~0); glStencilFunc(GL_ALWAYS, 0, ~0); + glPolygonOffset(1.f,1.f); + glEnable(GL_POLYGON_OFFSET_FILL); GLenum incr = GL_INCR; GLenum decr = GL_DECR; @@ -3458,6 +3607,7 @@ void COpenGLDriver::drawStencilShadowVolume(const core::vector3df* triangles, s3 glDisable(GL_DEPTH_CLAMP_NV); #endif + glDisable(GL_POLYGON_OFFSET_FILL); glDisableClientState(GL_VERTEX_ARRAY); //not stored on stack glPopAttrib(); } @@ -3981,15 +4131,55 @@ bool COpenGLDriver::setRenderTarget(const core::array& tar (targets[i].ColorMask & ECP_GREEN)?GL_TRUE:GL_FALSE, (targets[i].ColorMask & ECP_BLUE)?GL_TRUE:GL_FALSE, (targets[i].ColorMask & ECP_ALPHA)?GL_TRUE:GL_FALSE); - if (targets[i].BlendEnable) - extGlEnableIndexed(GL_BLEND, i); - else + if (targets[i].BlendOp==EBO_NONE) extGlDisableIndexed(GL_BLEND, i); + else + extGlEnableIndexed(GL_BLEND, i); } +#if defined(GL_AMD_draw_buffers_blend) || defined(GL_ARB_draw_buffers_blend) if (FeatureAvailable[IRR_AMD_draw_buffers_blend] || FeatureAvailable[IRR_ARB_draw_buffers_blend]) { extGlBlendFuncIndexed(i, getGLBlend(targets[i].BlendFuncSrc), getGLBlend(targets[i].BlendFuncDst)); + switch(targets[i].BlendOp) + { + case EBO_SUBTRACT: + extGlBlendEquationIndexed(i, GL_FUNC_SUBTRACT); + break; + case EBO_REVSUBTRACT: + extGlBlendEquationIndexed(i, GL_FUNC_REVERSE_SUBTRACT); + break; + case EBO_MIN: + extGlBlendEquationIndexed(i, GL_MIN); + break; + case EBO_MAX: + extGlBlendEquationIndexed(i, GL_MAX); + break; + case EBO_MIN_FACTOR: + case EBO_MIN_ALPHA: +#if defined(GL_AMD_blend_minmax_factor) + if (FeatureAvailable[IRR_AMD_blend_minmax_factor]) + extGlBlendEquationIndexed(i, GL_FACTOR_MIN_AMD); + // fallback in case of missing extension + else +#endif + extGlBlendEquation(GL_MIN); + break; + case EBO_MAX_FACTOR: + case EBO_MAX_ALPHA: +#if defined(GL_AMD_blend_minmax_factor) + if (FeatureAvailable[IRR_AMD_blend_minmax_factor]) + extGlBlendEquationIndexed(i, GL_FACTOR_MAX_AMD); + // fallback in case of missing extension + else +#endif + extGlBlendEquation(GL_MAX); + break; + default: + extGlBlendEquationIndexed(i, GL_FUNC_ADD); + break; + } } +#endif if (targets[i].TargetType==ERT_RENDER_TEXTURE) { GLenum attachment = GL_NONE; @@ -4233,7 +4423,7 @@ GLenum COpenGLDriver::primitiveTypeToGL(scene::E_PRIMITIVE_TYPE type) const GLenum COpenGLDriver::getGLBlend (E_BLEND_FACTOR factor) const { - u32 r = 0; + GLenum r = 0; switch (factor) { case EBF_ZERO: r = GL_ZERO; break; diff --git a/source/Irrlicht/COpenGLDriver.h b/source/Irrlicht/COpenGLDriver.h index f45e7f38..4f1518bc 100644 --- a/source/Irrlicht/COpenGLDriver.h +++ b/source/Irrlicht/COpenGLDriver.h @@ -101,7 +101,7 @@ namespace video //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ - virtual void createOcclusionQuery(scene::ISceneNode* node, + virtual void addOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh=0); //! Remove occlusion query. @@ -397,8 +397,8 @@ namespace video virtual video::ITexture* createDeviceDependentTexture(IImage* surface, const io::path& name, void* mipmapData); //! creates a transposed matrix in supplied GLfloat array to pass to OpenGL - inline void createGLMatrix(GLfloat gl_matrix[16], const core::matrix4& m); - inline void createGLTextureMatrix(GLfloat gl_matrix[16], const core::matrix4& m); + inline void getGLMatrix(GLfloat gl_matrix[16], const core::matrix4& m); + inline void getGLTextureMatrix(GLfloat gl_matrix[16], const core::matrix4& m); //! Set GL pipeline to desired texture wrap modes of the material void setWrapMode(const SMaterial& material); @@ -423,7 +423,7 @@ namespace video void assignHardwareLight(u32 lightIndex); //! helper function for render setup. - void createColorBuffer(const void* vertices, u32 vertexCount, E_VERTEX_TYPE vType); + void getColorBuffer(const void* vertices, u32 vertexCount, E_VERTEX_TYPE vType); //! helper function doing the actual rendering. void renderArray(const void* indexList, u32 primitiveCount, @@ -492,19 +492,19 @@ namespace video HDC HDc; // Private GDI Device Context HWND Window; #ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ - CIrrDeviceWin32 *Device; + CIrrDeviceWin32 *Win32Device; #endif #endif #ifdef _IRR_COMPILE_WITH_X11_DEVICE_ GLXDrawable Drawable; Display* X11Display; - CIrrDeviceLinux *Device; + CIrrDeviceLinux *X11Device; #endif #ifdef _IRR_COMPILE_WITH_OSX_DEVICE_ - CIrrDeviceMacOSX *Device; + CIrrDeviceMacOSX *OSXDevice; #endif #ifdef _IRR_COMPILE_WITH_SDL_DEVICE_ - CIrrDeviceSDL *Device; + CIrrDeviceSDL *SDLDevice; #endif E_DEVICE_TYPE DeviceType; diff --git a/source/Irrlicht/COpenGLExtensionHandler.cpp b/source/Irrlicht/COpenGLExtensionHandler.cpp index 61b5d900..463eaaf9 100644 --- a/source/Irrlicht/COpenGLExtensionHandler.cpp +++ b/source/Irrlicht/COpenGLExtensionHandler.cpp @@ -50,9 +50,6 @@ COpenGLExtensionHandler::COpenGLExtensionHandler() : pGlStencilFuncSeparate(0), pGlStencilOpSeparate(0), pGlStencilFuncSeparateATI(0), pGlStencilOpSeparateATI(0), pGlCompressedTexImage2D(0), -#if defined(GLX_SGI_swap_control) - glxSwapIntervalSGI(0), -#endif pGlBindFramebufferEXT(0), pGlDeleteFramebuffersEXT(0), pGlGenFramebuffersEXT(0), pGlCheckFramebufferStatusEXT(0), pGlFramebufferTexture2DEXT(0), pGlBindRenderbufferEXT(0), pGlDeleteRenderbuffersEXT(0), pGlGenRenderbuffersEXT(0), @@ -64,6 +61,7 @@ COpenGLExtensionHandler::COpenGLExtensionHandler() : pGlProvokingVertexARB(0), pGlProvokingVertexEXT(0), pGlColorMaskIndexedEXT(0), pGlEnableIndexedEXT(0), pGlDisableIndexedEXT(0), pGlBlendFuncIndexedAMD(0), pGlBlendFunciARB(0), + pGlBlendEquationIndexedAMD(0), pGlBlendEquationiARB(0), pGlProgramParameteriARB(0), pGlProgramParameteriEXT(0), pGlGenQueriesARB(0), pGlDeleteQueriesARB(0), pGlIsQueryARB(0), pGlBeginQueryARB(0), pGlEndQueryARB(0), pGlGetQueryivARB(0), @@ -71,7 +69,20 @@ COpenGLExtensionHandler::COpenGLExtensionHandler() : pGlGenOcclusionQueriesNV(0), pGlDeleteOcclusionQueriesNV(0), pGlIsOcclusionQueryNV(0), pGlBeginOcclusionQueryNV(0), pGlEndOcclusionQueryNV(0), pGlGetOcclusionQueryivNV(0), - pGlGetOcclusionQueryuivNV(0) + pGlGetOcclusionQueryuivNV(0), + pGlBlendEquationEXT(0), pGlBlendEquation(0) +#if defined(GLX_SGI_swap_control) + ,pGlxSwapIntervalSGI(0) +#endif +#if defined(GLX_EXT_swap_control) + ,pGlxSwapIntervalEXT(0) +#endif +#if defined(WGL_EXT_swap_control) + ,pWglSwapIntervalEXT(0) +#endif +#if defined(GLX_MESA_swap_control) + ,pGlxSwapIntervalMESA(0) +#endif #endif // _IRR_OPENGL_USE_EXTPOINTER_ { for (u32 i=0; i(glGetString(GL_VERSION))); @@ -140,128 +364,10 @@ void COpenGLExtensionHandler::initExtensions(bool stencilBuffer) TextureCompressionExtension = FeatureAvailable[IRR_ARB_texture_compression]; StencilBuffer=stencilBuffer; +#ifdef _IRR_OPENGL_USE_EXTPOINTER_ #ifdef _IRR_WINDOWS_API_ - // get multitexturing function pointers - pGlActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress("glActiveTextureARB"); - pGlClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) wglGetProcAddress("glClientActiveTextureARB"); - - // get fragment and vertex program function pointers - pGlGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) wglGetProcAddress("glGenProgramsARB"); - pGlGenProgramsNV = (PFNGLGENPROGRAMSNVPROC) wglGetProcAddress("glGenProgramsNV"); - pGlBindProgramARB = (PFNGLBINDPROGRAMARBPROC) wglGetProcAddress("glBindProgramARB"); - pGlBindProgramNV = (PFNGLBINDPROGRAMNVPROC) wglGetProcAddress("glBindProgramNV"); - pGlProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) wglGetProcAddress("glProgramStringARB"); - pGlLoadProgramNV = (PFNGLLOADPROGRAMNVPROC) wglGetProcAddress("glLoadProgramNV"); - pGlDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) wglGetProcAddress("glDeleteProgramsARB"); - pGlDeleteProgramsNV = (PFNGLDELETEPROGRAMSNVPROC) wglGetProcAddress("glDeleteProgramsNV"); - pGlProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) wglGetProcAddress("glProgramLocalParameter4fvARB"); - pGlCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) wglGetProcAddress("glCreateShaderObjectARB"); - pGlCreateShader = (PFNGLCREATESHADERPROC) wglGetProcAddress("glCreateShader"); - pGlShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) wglGetProcAddress("glShaderSourceARB"); - pGlShaderSource = (PFNGLSHADERSOURCEPROC) wglGetProcAddress("glShaderSource"); - pGlCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) wglGetProcAddress("glCompileShaderARB"); - pGlCompileShader = (PFNGLCOMPILESHADERPROC) wglGetProcAddress("glCompileShader"); - pGlCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) wglGetProcAddress("glCreateProgramObjectARB"); - pGlCreateProgram = (PFNGLCREATEPROGRAMPROC) wglGetProcAddress("glCreateProgram"); - pGlAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) wglGetProcAddress("glAttachObjectARB"); - pGlAttachShader = (PFNGLATTACHSHADERPROC) wglGetProcAddress("glAttachShader"); - pGlLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) wglGetProcAddress("glLinkProgramARB"); - pGlLinkProgram = (PFNGLLINKPROGRAMPROC) wglGetProcAddress("glLinkProgram"); - pGlUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) wglGetProcAddress("glUseProgramObjectARB"); - pGlUseProgram = (PFNGLUSEPROGRAMPROC) wglGetProcAddress("glUseProgram"); - pGlDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) wglGetProcAddress("glDeleteObjectARB"); - pGlDeleteProgram = (PFNGLDELETEPROGRAMPROC) wglGetProcAddress("glDeleteProgram"); - pGlDeleteShader = (PFNGLDELETESHADERPROC) wglGetProcAddress("glDeleteShader"); - pGlGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) wglGetProcAddress("glGetAttachedShaders"); - pGlGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) wglGetProcAddress("glGetAttachedObjectsARB"); - pGlGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) wglGetProcAddress("glGetInfoLogARB"); - pGlGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) wglGetProcAddress("glGetShaderInfoLog"); - pGlGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) wglGetProcAddress("glGetProgramInfoLog"); - pGlGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) wglGetProcAddress("glGetObjectParameterivARB"); - pGlGetShaderiv = (PFNGLGETSHADERIVPROC) wglGetProcAddress("glGetShaderiv"); - pGlGetProgramiv = (PFNGLGETPROGRAMIVPROC) wglGetProcAddress("glGetProgramiv"); - pGlGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) wglGetProcAddress("glGetUniformLocationARB"); - pGlGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) wglGetProcAddress("glGetUniformLocation"); - pGlUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) wglGetProcAddress("glUniform4fvARB"); - pGlUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) wglGetProcAddress("glUniform1ivARB"); - pGlUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) wglGetProcAddress("glUniform1fvARB"); - pGlUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) wglGetProcAddress("glUniform2fvARB"); - pGlUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) wglGetProcAddress("glUniform3fvARB"); - pGlUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) wglGetProcAddress("glUniformMatrix2fvARB"); - pGlUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) wglGetProcAddress("glUniformMatrix3fvARB"); - pGlUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) wglGetProcAddress("glUniformMatrix4fvARB"); - pGlGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) wglGetProcAddress("glGetActiveUniformARB"); - pGlGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) wglGetProcAddress("glGetActiveUniform"); - - // get point parameter extension - pGlPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC) wglGetProcAddress("glPointParameterfARB"); - pGlPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC) wglGetProcAddress("glPointParameterfvARB"); - - // get stencil extension - pGlStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) wglGetProcAddress("glStencilFuncSeparate"); - pGlStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) wglGetProcAddress("glStencilOpSeparate"); - pGlStencilFuncSeparateATI = (PFNGLSTENCILFUNCSEPARATEATIPROC) wglGetProcAddress("glStencilFuncSeparateATI"); - pGlStencilOpSeparateATI = (PFNGLSTENCILOPSEPARATEATIPROC) wglGetProcAddress("glStencilOpSeparateATI"); - - // compressed textures - pGlCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) wglGetProcAddress("glCompressedTexImage2D"); - - // FrameBufferObjects - pGlBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) wglGetProcAddress("glBindFramebufferEXT"); - pGlDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) wglGetProcAddress("glDeleteFramebuffersEXT"); - pGlGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) wglGetProcAddress("glGenFramebuffersEXT"); - pGlCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) wglGetProcAddress("glCheckFramebufferStatusEXT"); - pGlFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) wglGetProcAddress("glFramebufferTexture2DEXT"); - pGlBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) wglGetProcAddress("glBindRenderbufferEXT"); - pGlDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) wglGetProcAddress("glDeleteRenderbuffersEXT"); - pGlGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) wglGetProcAddress("glGenRenderbuffersEXT"); - pGlRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) wglGetProcAddress("glRenderbufferStorageEXT"); - pGlFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) wglGetProcAddress("glFramebufferRenderbufferEXT"); - pGlDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC) wglGetProcAddress("glDrawBuffersARB"); - pGlDrawBuffersATI = (PFNGLDRAWBUFFERSATIPROC) wglGetProcAddress("glDrawBuffersATI"); - - // get vertex buffer extension - pGlGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB"); - pGlBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB"); - pGlBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB"); - pGlDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB"); - pGlBufferSubDataARB= (PFNGLBUFFERSUBDATAARBPROC) wglGetProcAddress("glBufferSubDataARB"); - pGlGetBufferSubDataARB= (PFNGLGETBUFFERSUBDATAARBPROC)wglGetProcAddress("glGetBufferSubDataARB"); - pGlMapBufferARB= (PFNGLMAPBUFFERARBPROC) wglGetProcAddress("glMapBufferARB"); - pGlUnmapBufferARB= (PFNGLUNMAPBUFFERARBPROC) wglGetProcAddress("glUnmapBufferARB"); - pGlIsBufferARB= (PFNGLISBUFFERARBPROC) wglGetProcAddress("glIsBufferARB"); - pGlGetBufferParameterivARB= (PFNGLGETBUFFERPARAMETERIVARBPROC) wglGetProcAddress("glGetBufferParameterivARB"); - pGlGetBufferPointervARB= (PFNGLGETBUFFERPOINTERVARBPROC) wglGetProcAddress("glGetBufferPointervARB"); - pGlProvokingVertexARB= (PFNGLPROVOKINGVERTEXPROC) wglGetProcAddress("glProvokingVertex"); - pGlProvokingVertexEXT= (PFNGLPROVOKINGVERTEXEXTPROC) wglGetProcAddress("glProvokingVertexEXT"); - pGlColorMaskIndexedEXT= (PFNGLCOLORMASKINDEXEDEXTPROC) wglGetProcAddress("glColorMaskIndexedEXT"); - pGlEnableIndexedEXT= (PFNGLENABLEINDEXEDEXTPROC) wglGetProcAddress("glEnableIndexedEXT"); - pGlDisableIndexedEXT= (PFNGLDISABLEINDEXEDEXTPROC) wglGetProcAddress("glDisableIndexedEXT"); - pGlBlendFuncIndexedAMD= (PFNGLBLENDFUNCINDEXEDAMDPROC) wglGetProcAddress("glBlendFuncIndexedAMD"); - pGlBlendFunciARB= (PFNGLBLENDFUNCIPROC) wglGetProcAddress("glBlendFunciARB"); - pGlProgramParameteriARB= (PFNGLPROGRAMPARAMETERIARBPROC) wglGetProcAddress("glProgramParameteriARB"); - pGlProgramParameteriEXT= (PFNGLPROGRAMPARAMETERIEXTPROC) wglGetProcAddress("glProgramParameteriEXT"); - - // occlusion query - pGlGenQueriesARB = (PFNGLGENQUERIESARBPROC) wglGetProcAddress("glGenQueriesARB"); - pGlDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) wglGetProcAddress("glDeleteQueriesARB"); - pGlIsQueryARB = (PFNGLISQUERYARBPROC) wglGetProcAddress("glIsQueryARB"); - pGlBeginQueryARB = (PFNGLBEGINQUERYARBPROC) wglGetProcAddress("glBeginQueryARB"); - pGlEndQueryARB = (PFNGLENDQUERYARBPROC) wglGetProcAddress("glEndQueryARB"); - pGlGetQueryivARB = (PFNGLGETQUERYIVARBPROC) wglGetProcAddress("glGetQueryivARB"); - pGlGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) wglGetProcAddress("glGetQueryObjectivARB"); - pGlGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) wglGetProcAddress("glGetQueryObjectuivARB"); - pGlGenOcclusionQueriesNV = (PFNGLGENOCCLUSIONQUERIESNVPROC) wglGetProcAddress("glGenOcclusionQueriesNV"); - pGlDeleteOcclusionQueriesNV = (PFNGLDELETEOCCLUSIONQUERIESNVPROC) wglGetProcAddress("glDeleteOcclusionQueriesNV"); - pGlIsOcclusionQueryNV = (PFNGLISOCCLUSIONQUERYNVPROC) wglGetProcAddress("glIsOcclusionQueryNV"); - pGlBeginOcclusionQueryNV = (PFNGLBEGINOCCLUSIONQUERYNVPROC) wglGetProcAddress("glBeginOcclusionQueryNV"); - pGlEndOcclusionQueryNV = (PFNGLENDOCCLUSIONQUERYNVPROC) wglGetProcAddress("glEndOcclusionQueryNV"); - pGlGetOcclusionQueryivNV = (PFNGLGETOCCLUSIONQUERYIVNVPROC) wglGetProcAddress("glGetOcclusionQueryivNV"); - pGlGetOcclusionQueryuivNV = (PFNGLGETOCCLUSIONQUERYUIVNVPROC) wglGetProcAddress("glGetOcclusionQueryuivNV"); - + #define IRR_OGL_LOAD_EXTENSION(x) wglGetProcAddress(reinterpret_cast(x)) #elif defined(_IRR_COMPILE_WITH_X11_DEVICE_) || defined (_IRR_COMPILE_WITH_SDL_DEVICE_) - #ifdef _IRR_OPENGL_USE_EXTPOINTER_ - #if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) && !defined(_IRR_COMPILE_WITH_X11_DEVICE_) #define IRR_OGL_LOAD_EXTENSION(x) SDL_GL_GetProcAddress(reinterpret_cast(x)) #else @@ -275,329 +381,163 @@ void COpenGLExtensionHandler::initExtensions(bool stencilBuffer) // #define _IRR_GETPROCADDRESS_WORKAROUND_ #ifndef _IRR_GETPROCADDRESS_WORKAROUND_ - __GLXextFuncPtr (*IRR_OGL_LOAD_EXTENSION)(const GLubyte*)=0; + __GLXextFuncPtr (*IRR_OGL_LOAD_EXTENSION_FUNCP)(const GLubyte*)=0; #ifdef GLX_VERSION_1_4 int major=0,minor=0; if (glXGetCurrentDisplay()) glXQueryVersion(glXGetCurrentDisplay(), &major, &minor); if ((major>1) || (minor>3)) - IRR_OGL_LOAD_EXTENSION=glXGetProcAddress; + IRR_OGL_LOAD_EXTENSION_FUNCP=glXGetProcAddress; else #endif - IRR_OGL_LOAD_EXTENSION=glXGetProcAddressARB; + IRR_OGL_LOAD_EXTENSION_FUNCP=glXGetProcAddressARB; + #define IRR_OGL_LOAD_EXTENSION(X) IRR_OGL_LOAD_EXTENSION_FUNCP(reinterpret_cast(X)) #else - #define IRR_OGL_LOAD_EXTENSION glXGetProcAddressARB + #define IRR_OGL_LOAD_EXTENSION(X) glXGetProcAddressARB(reinterpret_cast(X)) #endif #endif +#endif - pGlActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glActiveTextureARB")); - - pGlClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glClientActiveTextureARB")); + // get multitexturing function pointers + pGlActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) IRR_OGL_LOAD_EXTENSION("glActiveTextureARB"); + pGlClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) IRR_OGL_LOAD_EXTENSION("glClientActiveTextureARB"); // get fragment and vertex program function pointers - pGlGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGenProgramsARB")); - - pGlGenProgramsNV = (PFNGLGENPROGRAMSNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGenProgramsNV")); - - pGlBindProgramARB = (PFNGLBINDPROGRAMARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBindProgramARB")); - - pGlBindProgramNV = (PFNGLBINDPROGRAMNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBindProgramNV")); - - pGlDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteProgramsARB")); - - pGlDeleteProgramsNV = (PFNGLDELETEPROGRAMSNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteProgramsNV")); - - pGlProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glProgramStringARB")); - - pGlLoadProgramNV = (PFNGLLOADPROGRAMNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glLoadProgramNV")); - - pGlProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glProgramLocalParameter4fvARB")); - - pGlCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glCreateShaderObjectARB")); - - pGlCreateShader = (PFNGLCREATESHADERPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glCreateShader")); - - pGlShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glShaderSourceARB")); - - pGlShaderSource = (PFNGLSHADERSOURCEPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glShaderSource")); - - pGlCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glCompileShaderARB")); - - pGlCompileShader = (PFNGLCOMPILESHADERPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glCompileShader")); - - pGlCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glCreateProgramObjectARB")); - - pGlCreateProgram = (PFNGLCREATEPROGRAMPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glCreateProgram")); - - pGlAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glAttachObjectARB")); - - pGlAttachShader = (PFNGLATTACHSHADERPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glAttachShader")); - - pGlLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glLinkProgram")); - - pGlLinkProgram = (PFNGLLINKPROGRAMPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glLinkProgramARB")); - - pGlUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUseProgramObjectARB")); - - pGlUseProgram = (PFNGLUSEPROGRAMPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUseProgram")); - - pGlDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteObjectARB")); - - pGlDeleteProgram = (PFNGLDELETEPROGRAMPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteProgram")); - - pGlDeleteShader = (PFNGLDELETESHADERPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteShader")); - - pGlGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetAttachedObjectsARB")); - - pGlGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetAttachedShaders")); - - pGlGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetInfoLogARB")); - - pGlGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetShaderInfoLog")); - - pGlGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetProgramInfoLog")); - - pGlGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetObjectParameterivARB")); - - pGlGetShaderiv = (PFNGLGETSHADERIVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetShaderiv")); - - pGlGetProgramiv = (PFNGLGETPROGRAMIVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetProgramiv")); - - pGlGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetUniformLocationARB")); - - pGlGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetUniformLocation")); - - pGlUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniform4fvARB")); - - pGlUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniform1ivARB")); - - pGlUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniform1fvARB")); - - pGlUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniform2fvARB")); - - pGlUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniform3fvARB")); - - pGlUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniform4fvARB")); - - pGlUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniformMatrix2fvARB")); - - pGlUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniformMatrix3fvARB")); - - pGlUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUniformMatrix4fvARB")); - - pGlGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetActiveUniformARB")); - - pGlGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetActiveUniform")); + pGlGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) IRR_OGL_LOAD_EXTENSION("glGenProgramsARB"); + pGlGenProgramsNV = (PFNGLGENPROGRAMSNVPROC) IRR_OGL_LOAD_EXTENSION("glGenProgramsNV"); + pGlBindProgramARB = (PFNGLBINDPROGRAMARBPROC) IRR_OGL_LOAD_EXTENSION("glBindProgramARB"); + pGlBindProgramNV = (PFNGLBINDPROGRAMNVPROC) IRR_OGL_LOAD_EXTENSION("glBindProgramNV"); + pGlProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) IRR_OGL_LOAD_EXTENSION("glProgramStringARB"); + pGlLoadProgramNV = (PFNGLLOADPROGRAMNVPROC) IRR_OGL_LOAD_EXTENSION("glLoadProgramNV"); + pGlDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) IRR_OGL_LOAD_EXTENSION("glDeleteProgramsARB"); + pGlDeleteProgramsNV = (PFNGLDELETEPROGRAMSNVPROC) IRR_OGL_LOAD_EXTENSION("glDeleteProgramsNV"); + pGlProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) IRR_OGL_LOAD_EXTENSION("glProgramLocalParameter4fvARB"); + pGlCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) IRR_OGL_LOAD_EXTENSION("glCreateShaderObjectARB"); + pGlCreateShader = (PFNGLCREATESHADERPROC) IRR_OGL_LOAD_EXTENSION("glCreateShader"); + pGlShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) IRR_OGL_LOAD_EXTENSION("glShaderSourceARB"); + pGlShaderSource = (PFNGLSHADERSOURCEPROC) IRR_OGL_LOAD_EXTENSION("glShaderSource"); + pGlCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) IRR_OGL_LOAD_EXTENSION("glCompileShaderARB"); + pGlCompileShader = (PFNGLCOMPILESHADERPROC) IRR_OGL_LOAD_EXTENSION("glCompileShader"); + pGlCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) IRR_OGL_LOAD_EXTENSION("glCreateProgramObjectARB"); + pGlCreateProgram = (PFNGLCREATEPROGRAMPROC) IRR_OGL_LOAD_EXTENSION("glCreateProgram"); + pGlAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) IRR_OGL_LOAD_EXTENSION("glAttachObjectARB"); + pGlAttachShader = (PFNGLATTACHSHADERPROC) IRR_OGL_LOAD_EXTENSION("glAttachShader"); + pGlLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) IRR_OGL_LOAD_EXTENSION("glLinkProgramARB"); + pGlLinkProgram = (PFNGLLINKPROGRAMPROC) IRR_OGL_LOAD_EXTENSION("glLinkProgram"); + pGlUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) IRR_OGL_LOAD_EXTENSION("glUseProgramObjectARB"); + pGlUseProgram = (PFNGLUSEPROGRAMPROC) IRR_OGL_LOAD_EXTENSION("glUseProgram"); + pGlDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) IRR_OGL_LOAD_EXTENSION("glDeleteObjectARB"); + pGlDeleteProgram = (PFNGLDELETEPROGRAMPROC) IRR_OGL_LOAD_EXTENSION("glDeleteProgram"); + pGlDeleteShader = (PFNGLDELETESHADERPROC) IRR_OGL_LOAD_EXTENSION("glDeleteShader"); + pGlGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) IRR_OGL_LOAD_EXTENSION("glGetAttachedShaders"); + pGlGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) IRR_OGL_LOAD_EXTENSION("glGetAttachedObjectsARB"); + pGlGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) IRR_OGL_LOAD_EXTENSION("glGetInfoLogARB"); + pGlGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) IRR_OGL_LOAD_EXTENSION("glGetShaderInfoLog"); + pGlGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) IRR_OGL_LOAD_EXTENSION("glGetProgramInfoLog"); + pGlGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) IRR_OGL_LOAD_EXTENSION("glGetObjectParameterivARB"); + pGlGetShaderiv = (PFNGLGETSHADERIVPROC) IRR_OGL_LOAD_EXTENSION("glGetShaderiv"); + pGlGetProgramiv = (PFNGLGETPROGRAMIVPROC) IRR_OGL_LOAD_EXTENSION("glGetProgramiv"); + pGlGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) IRR_OGL_LOAD_EXTENSION("glGetUniformLocationARB"); + pGlGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) IRR_OGL_LOAD_EXTENSION("glGetUniformLocation"); + pGlUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) IRR_OGL_LOAD_EXTENSION("glUniform4fvARB"); + pGlUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) IRR_OGL_LOAD_EXTENSION("glUniform1ivARB"); + pGlUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) IRR_OGL_LOAD_EXTENSION("glUniform1fvARB"); + pGlUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) IRR_OGL_LOAD_EXTENSION("glUniform2fvARB"); + pGlUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) IRR_OGL_LOAD_EXTENSION("glUniform3fvARB"); + pGlUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) IRR_OGL_LOAD_EXTENSION("glUniformMatrix2fvARB"); + pGlUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) IRR_OGL_LOAD_EXTENSION("glUniformMatrix3fvARB"); + pGlUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) IRR_OGL_LOAD_EXTENSION("glUniformMatrix4fvARB"); + pGlGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) IRR_OGL_LOAD_EXTENSION("glGetActiveUniformARB"); + pGlGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) IRR_OGL_LOAD_EXTENSION("glGetActiveUniform"); // get point parameter extension - pGlPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glPointParameterfARB")); - pGlPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glPointParameterfvARB")); + pGlPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC) IRR_OGL_LOAD_EXTENSION("glPointParameterfARB"); + pGlPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC) IRR_OGL_LOAD_EXTENSION("glPointParameterfvARB"); // get stencil extension - pGlStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glStencilFuncSeparate")); - pGlStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glStencilOpSeparate")); - pGlStencilFuncSeparateATI = (PFNGLSTENCILFUNCSEPARATEATIPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glStencilFuncSeparateATI")); - pGlStencilOpSeparateATI = (PFNGLSTENCILOPSEPARATEATIPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glStencilOpSeparateATI")); + pGlStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) IRR_OGL_LOAD_EXTENSION("glStencilFuncSeparate"); + pGlStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) IRR_OGL_LOAD_EXTENSION("glStencilOpSeparate"); + pGlStencilFuncSeparateATI = (PFNGLSTENCILFUNCSEPARATEATIPROC) IRR_OGL_LOAD_EXTENSION("glStencilFuncSeparateATI"); + pGlStencilOpSeparateATI = (PFNGLSTENCILOPSEPARATEATIPROC) IRR_OGL_LOAD_EXTENSION("glStencilOpSeparateATI"); // compressed textures - pGlCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glCompressedTexImage2D")); - - #if defined(GLX_SGI_swap_control) && !defined(_IRR_COMPILE_WITH_SDL_DEVICE_) - // get vsync extension - glxSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glXSwapIntervalSGI")); - #endif + pGlCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) IRR_OGL_LOAD_EXTENSION("glCompressedTexImage2D"); // FrameBufferObjects - pGlBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBindFramebufferEXT")); + pGlBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) IRR_OGL_LOAD_EXTENSION("glBindFramebufferEXT"); + pGlDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) IRR_OGL_LOAD_EXTENSION("glDeleteFramebuffersEXT"); + pGlGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) IRR_OGL_LOAD_EXTENSION("glGenFramebuffersEXT"); + pGlCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) IRR_OGL_LOAD_EXTENSION("glCheckFramebufferStatusEXT"); + pGlFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) IRR_OGL_LOAD_EXTENSION("glFramebufferTexture2DEXT"); + pGlBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) IRR_OGL_LOAD_EXTENSION("glBindRenderbufferEXT"); + pGlDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) IRR_OGL_LOAD_EXTENSION("glDeleteRenderbuffersEXT"); + pGlGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) IRR_OGL_LOAD_EXTENSION("glGenRenderbuffersEXT"); + pGlRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) IRR_OGL_LOAD_EXTENSION("glRenderbufferStorageEXT"); + pGlFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) IRR_OGL_LOAD_EXTENSION("glFramebufferRenderbufferEXT"); + pGlDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC) IRR_OGL_LOAD_EXTENSION("glDrawBuffersARB"); + pGlDrawBuffersATI = (PFNGLDRAWBUFFERSATIPROC) IRR_OGL_LOAD_EXTENSION("glDrawBuffersATI"); - pGlDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteFramebuffersEXT")); - - pGlGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGenFramebuffersEXT")); - - pGlCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glCheckFramebufferStatusEXT")); - - pGlFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glFramebufferTexture2DEXT")); - - pGlBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBindRenderbufferEXT")); - - pGlDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteRenderbuffersEXT")); - - pGlGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGenRenderbuffersEXT")); - - pGlRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glRenderbufferStorageEXT")); - - pGlFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glFramebufferRenderbufferEXT")); - - pGlDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDrawBuffersARB")); - - pGlDrawBuffersATI = (PFNGLDRAWBUFFERSATIPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDrawBuffersATI")); - - pGlGenBuffersARB = (PFNGLGENBUFFERSARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGenBuffersARB")); - - pGlBindBufferARB = (PFNGLBINDBUFFERARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBindBufferARB")); - - pGlBufferDataARB = (PFNGLBUFFERDATAARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBufferDataARB")); - - pGlDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteBuffersARB")); - - pGlBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBufferSubDataARB")); - - pGlGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetBufferSubDataARB")); - - pGlMapBufferARB = (PFNGLMAPBUFFERARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glMapBufferARB")); - - pGlUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glUnmapBufferARB")); - - pGlIsBufferARB = (PFNGLISBUFFERARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glIsBufferARB")); - - pGlGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetBufferParameterivARB")); - - pGlGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetBufferPointervARB")); - pGlProvokingVertexARB= (PFNGLPROVOKINGVERTEXPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glProvokingVertex")); - pGlProvokingVertexEXT= (PFNGLPROVOKINGVERTEXEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glProvokingVertexEXT")); - pGlColorMaskIndexedEXT= (PFNGLCOLORMASKINDEXEDEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glColorMaskIndexedEXT")); - pGlEnableIndexedEXT= (PFNGLENABLEINDEXEDEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glEnableIndexedEXT")); - pGlDisableIndexedEXT= (PFNGLDISABLEINDEXEDEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDisableIndexedEXT")); - pGlBlendFuncIndexedAMD= (PFNGLBLENDFUNCINDEXEDAMDPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBlendFuncIndexedAMD")); - pGlBlendFunciARB= (PFNGLBLENDFUNCIPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBlendFunciARB")); - pGlProgramParameteriARB = (PFNGLPROGRAMPARAMETERIARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glProgramParameteriARB")); - pGlProgramParameteriEXT = (PFNGLPROGRAMPARAMETERIEXTPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glProgramParameteriEXT")); + // get vertex buffer extension + pGlGenBuffersARB = (PFNGLGENBUFFERSARBPROC) IRR_OGL_LOAD_EXTENSION("glGenBuffersARB"); + pGlBindBufferARB = (PFNGLBINDBUFFERARBPROC) IRR_OGL_LOAD_EXTENSION("glBindBufferARB"); + pGlBufferDataARB = (PFNGLBUFFERDATAARBPROC) IRR_OGL_LOAD_EXTENSION("glBufferDataARB"); + pGlDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) IRR_OGL_LOAD_EXTENSION("glDeleteBuffersARB"); + pGlBufferSubDataARB= (PFNGLBUFFERSUBDATAARBPROC) IRR_OGL_LOAD_EXTENSION("glBufferSubDataARB"); + pGlGetBufferSubDataARB= (PFNGLGETBUFFERSUBDATAARBPROC)IRR_OGL_LOAD_EXTENSION("glGetBufferSubDataARB"); + pGlMapBufferARB= (PFNGLMAPBUFFERARBPROC) IRR_OGL_LOAD_EXTENSION("glMapBufferARB"); + pGlUnmapBufferARB= (PFNGLUNMAPBUFFERARBPROC) IRR_OGL_LOAD_EXTENSION("glUnmapBufferARB"); + pGlIsBufferARB= (PFNGLISBUFFERARBPROC) IRR_OGL_LOAD_EXTENSION("glIsBufferARB"); + pGlGetBufferParameterivARB= (PFNGLGETBUFFERPARAMETERIVARBPROC) IRR_OGL_LOAD_EXTENSION("glGetBufferParameterivARB"); + pGlGetBufferPointervARB= (PFNGLGETBUFFERPOINTERVARBPROC) IRR_OGL_LOAD_EXTENSION("glGetBufferPointervARB"); + pGlProvokingVertexARB= (PFNGLPROVOKINGVERTEXPROC) IRR_OGL_LOAD_EXTENSION("glProvokingVertex"); + pGlProvokingVertexEXT= (PFNGLPROVOKINGVERTEXEXTPROC) IRR_OGL_LOAD_EXTENSION("glProvokingVertexEXT"); + pGlColorMaskIndexedEXT= (PFNGLCOLORMASKINDEXEDEXTPROC) IRR_OGL_LOAD_EXTENSION("glColorMaskIndexedEXT"); + pGlEnableIndexedEXT= (PFNGLENABLEINDEXEDEXTPROC) IRR_OGL_LOAD_EXTENSION("glEnableIndexedEXT"); + pGlDisableIndexedEXT= (PFNGLDISABLEINDEXEDEXTPROC) IRR_OGL_LOAD_EXTENSION("glDisableIndexedEXT"); + pGlBlendFuncIndexedAMD= (PFNGLBLENDFUNCINDEXEDAMDPROC) IRR_OGL_LOAD_EXTENSION("glBlendFuncIndexedAMD"); + pGlBlendFunciARB= (PFNGLBLENDFUNCIPROC) IRR_OGL_LOAD_EXTENSION("glBlendFunciARB"); + pGlBlendEquationIndexedAMD= (PFNGLBLENDEQUATIONINDEXEDAMDPROC) IRR_OGL_LOAD_EXTENSION("glBlendEquationIndexedAMD"); + pGlBlendEquationiARB= (PFNGLBLENDEQUATIONIPROC) IRR_OGL_LOAD_EXTENSION("glBlendEquationiARB"); + pGlProgramParameteriARB= (PFNGLPROGRAMPARAMETERIARBPROC) IRR_OGL_LOAD_EXTENSION("glProgramParameteriARB"); + pGlProgramParameteriEXT= (PFNGLPROGRAMPARAMETERIEXTPROC) IRR_OGL_LOAD_EXTENSION("glProgramParameteriEXT"); // occlusion query - pGlGenQueriesARB = (PFNGLGENQUERIESARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGenQueriesARB")); - - pGlDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteQueriesARB")); - - pGlIsQueryARB = (PFNGLISQUERYARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glIsQueryARB")); - - pGlBeginQueryARB = (PFNGLBEGINQUERYARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBeginQueryARB")); + pGlGenQueriesARB = (PFNGLGENQUERIESARBPROC) IRR_OGL_LOAD_EXTENSION("glGenQueriesARB"); + pGlDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) IRR_OGL_LOAD_EXTENSION("glDeleteQueriesARB"); + pGlIsQueryARB = (PFNGLISQUERYARBPROC) IRR_OGL_LOAD_EXTENSION("glIsQueryARB"); + pGlBeginQueryARB = (PFNGLBEGINQUERYARBPROC) IRR_OGL_LOAD_EXTENSION("glBeginQueryARB"); + pGlEndQueryARB = (PFNGLENDQUERYARBPROC) IRR_OGL_LOAD_EXTENSION("glEndQueryARB"); + pGlGetQueryivARB = (PFNGLGETQUERYIVARBPROC) IRR_OGL_LOAD_EXTENSION("glGetQueryivARB"); + pGlGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) IRR_OGL_LOAD_EXTENSION("glGetQueryObjectivARB"); + pGlGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) IRR_OGL_LOAD_EXTENSION("glGetQueryObjectuivARB"); + pGlGenOcclusionQueriesNV = (PFNGLGENOCCLUSIONQUERIESNVPROC) IRR_OGL_LOAD_EXTENSION("glGenOcclusionQueriesNV"); + pGlDeleteOcclusionQueriesNV = (PFNGLDELETEOCCLUSIONQUERIESNVPROC) IRR_OGL_LOAD_EXTENSION("glDeleteOcclusionQueriesNV"); + pGlIsOcclusionQueryNV = (PFNGLISOCCLUSIONQUERYNVPROC) IRR_OGL_LOAD_EXTENSION("glIsOcclusionQueryNV"); + pGlBeginOcclusionQueryNV = (PFNGLBEGINOCCLUSIONQUERYNVPROC) IRR_OGL_LOAD_EXTENSION("glBeginOcclusionQueryNV"); + pGlEndOcclusionQueryNV = (PFNGLENDOCCLUSIONQUERYNVPROC) IRR_OGL_LOAD_EXTENSION("glEndOcclusionQueryNV"); + pGlGetOcclusionQueryivNV = (PFNGLGETOCCLUSIONQUERYIVNVPROC) IRR_OGL_LOAD_EXTENSION("glGetOcclusionQueryivNV"); + pGlGetOcclusionQueryuivNV = (PFNGLGETOCCLUSIONQUERYUIVNVPROC) IRR_OGL_LOAD_EXTENSION("glGetOcclusionQueryuivNV"); - pGlEndQueryARB = (PFNGLENDQUERYARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glEndQueryARB")); + // blend equation + pGlBlendEquationEXT = (PFNGLBLENDEQUATIONEXTPROC) IRR_OGL_LOAD_EXTENSION("glBlendEquationEXT"); + pGlBlendEquation = (PFNGLBLENDEQUATIONPROC) IRR_OGL_LOAD_EXTENSION("glBlendEquation"); - pGlGetQueryivARB = (PFNGLGETQUERYIVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetQueryivARB")); + #if defined(WGL_EXT_swap_control) && !defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + // get vsync extension + pWglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) IRR_OGL_LOAD_EXTENSION("wglSwapIntervalEXT"); + #endif - pGlGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetQueryObjectivARB")); - - pGlGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetQueryObjectuivARB")); - - pGlGenOcclusionQueriesNV = (PFNGLGENOCCLUSIONQUERIESNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGenOcclusionQueriesNV")); - - pGlDeleteOcclusionQueriesNV = (PFNGLDELETEOCCLUSIONQUERIESNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glDeleteOcclusionQueriesNV")); - - pGlIsOcclusionQueryNV = (PFNGLISOCCLUSIONQUERYNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glIsOcclusionQueryNV")); - - pGlBeginOcclusionQueryNV = (PFNGLBEGINOCCLUSIONQUERYNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glBeginOcclusionQueryNV")); - - pGlEndOcclusionQueryNV = (PFNGLENDOCCLUSIONQUERYNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glEndOcclusionQueryNV")); - - pGlGetOcclusionQueryivNV = (PFNGLGETOCCLUSIONQUERYIVNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetOcclusionQueryivNV")); - - pGlGetOcclusionQueryuivNV = (PFNGLGETOCCLUSIONQUERYUIVNVPROC) - IRR_OGL_LOAD_EXTENSION(reinterpret_cast("glGetOcclusionQueryuivNV")); - - #endif // _IRR_OPENGL_USE_EXTPOINTER_ +#elif defined(_IRR_COMPILE_WITH_X11_DEVICE_) || defined (_IRR_COMPILE_WITH_SDL_DEVICE_) + // get vsync extension + #if defined(GLX_SGI_swap_control) && !defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + pGlxSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)IRR_OGL_LOAD_EXTENSION("glXSwapIntervalSGI"); + #endif + #if defined(GLX_EXT_swap_control) && !defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + pGlxSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)IRR_OGL_LOAD_EXTENSION("glXSwapIntervalEXT"); + #endif + #if defined(GLX_MESA_swap_control) && !defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + pGlxSwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC)IRR_OGL_LOAD_EXTENSION("glXSwapIntervalMESA"); + #endif #endif // _IRR_WINDOWS_API_ GLint num; @@ -802,6 +742,12 @@ bool COpenGLExtensionHandler::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const return FeatureAvailable[IRR_ARB_draw_buffers_blend] || FeatureAvailable[IRR_AMD_draw_buffers_blend]; case EVDF_OCCLUSION_QUERY: return FeatureAvailable[IRR_ARB_occlusion_query] && OcclusionQuerySupport; + case EVDF_POLYGON_OFFSET: + // both features supported with OpenGL 1.1 + return Version>=110; + case EVDF_BLEND_OPERATIONS: + return (Version>=120) || FeatureAvailable[IRR_EXT_blend_minmax] || + FeatureAvailable[IRR_EXT_blend_subtract] || FeatureAvailable[IRR_EXT_blend_logic_op]; default: return false; }; diff --git a/source/Irrlicht/COpenGLExtensionHandler.h b/source/Irrlicht/COpenGLExtensionHandler.h index b5612b5b..4ac56f86 100644 --- a/source/Irrlicht/COpenGLExtensionHandler.h +++ b/source/Irrlicht/COpenGLExtensionHandler.h @@ -82,6 +82,7 @@ static const char* const OpenGLFeatureStrings[] = { "GL_3DFX_multisample", "GL_3DFX_tbuffer", "GL_3DFX_texture_compression_FXT1", + "GL_AMD_blend_minmax_factor", "GL_AMD_conservative_depth", "GL_AMD_debug_output", "GL_AMD_depth_clamp_separate", @@ -299,6 +300,7 @@ static const char* const OpenGLFeatureStrings[] = { "GL_EXT_texture_shared_exponent", "GL_EXT_texture_snorm", "GL_EXT_texture_sRGB", + "GL_EXT_texture_sRGB_decode", "GL_EXT_texture_swizzle", "GL_EXT_timer_query", "GL_EXT_transform_feedback", @@ -374,6 +376,7 @@ static const char* const OpenGLFeatureStrings[] = { "GL_NV_texture_compression_vtc", "GL_NV_texture_env_combine4", "GL_NV_texture_expand_normal", + "GL_NV_texture_multisample", "GL_NV_texture_rectangle", "GL_NV_texture_shader", "GL_NV_texture_shader2", @@ -481,6 +484,7 @@ class COpenGLExtensionHandler IRR_3DFX_multisample = 0, IRR_3DFX_tbuffer, IRR_3DFX_texture_compression_FXT1, + IRR_AMD_blend_minmax_factor, IRR_AMD_conservative_depth, IRR_AMD_debug_output, IRR_AMD_depth_clamp_separate, @@ -698,6 +702,7 @@ class COpenGLExtensionHandler IRR_EXT_texture_shared_exponent, IRR_EXT_texture_snorm, IRR_EXT_texture_sRGB, + IRR_EXT_texture_sRGB_decode, IRR_EXT_texture_swizzle, IRR_EXT_timer_query, IRR_EXT_transform_feedback, @@ -773,6 +778,7 @@ class COpenGLExtensionHandler IRR_NV_texture_compression_vtc, IRR_NV_texture_env_combine4, IRR_NV_texture_expand_normal, + IRR_NV_texture_multisample, IRR_NV_texture_rectangle, IRR_NV_texture_shader, IRR_NV_texture_shader2, @@ -891,6 +897,8 @@ class COpenGLExtensionHandler //! show all features with availablity void dump() const; + void dumpFramebufferFormats() const; + // Some variables for properties bool StencilBuffer; bool MultiTextureExtension; @@ -1032,6 +1040,7 @@ class COpenGLExtensionHandler void extGlEnableIndexed(GLenum target, GLuint index); void extGlDisableIndexed(GLenum target, GLuint index); void extGlBlendFuncIndexed(GLuint buf, GLenum src, GLenum dst); + void extGlBlendEquationIndexed(GLuint buf, GLenum mode); void extGlProgramParameteri(GLuint program, GLenum pname, GLint value); // occlusion query @@ -1044,10 +1053,16 @@ class COpenGLExtensionHandler void extGlGetQueryObjectiv(GLuint id, GLenum pname, GLint *params); void extGlGetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params); - protected: + // generic vsync setting method for several extensions + void extGlSwapInterval(int interval); + + // blend operations + void extGlBlendEquation(GLenum mode); + // the global feature array bool FeatureAvailable[IRR_OpenGL_Feature_Count]; + protected: #if defined(_IRR_OPENGL_USE_EXTPOINTER_) PFNGLACTIVETEXTUREARBPROC pGlActiveTextureARB; PFNGLCLIENTACTIVETEXTUREARBPROC pGlClientActiveTextureARB; @@ -1104,9 +1119,6 @@ class COpenGLExtensionHandler PFNGLSTENCILFUNCSEPARATEATIPROC pGlStencilFuncSeparateATI; PFNGLSTENCILOPSEPARATEATIPROC pGlStencilOpSeparateATI; PFNGLCOMPRESSEDTEXIMAGE2DPROC pGlCompressedTexImage2D; - #if defined(_IRR_LINUX_PLATFORM_) && defined(GLX_SGI_swap_control) - PFNGLXSWAPINTERVALSGIPROC glxSwapIntervalSGI; - #endif PFNGLBINDFRAMEBUFFEREXTPROC pGlBindFramebufferEXT; PFNGLDELETEFRAMEBUFFERSEXTPROC pGlDeleteFramebuffersEXT; PFNGLGENFRAMEBUFFERSEXTPROC pGlGenFramebuffersEXT; @@ -1138,6 +1150,8 @@ class COpenGLExtensionHandler PFNGLDISABLEINDEXEDEXTPROC pGlDisableIndexedEXT; PFNGLBLENDFUNCINDEXEDAMDPROC pGlBlendFuncIndexedAMD; PFNGLBLENDFUNCIPROC pGlBlendFunciARB; + PFNGLBLENDEQUATIONINDEXEDAMDPROC pGlBlendEquationIndexedAMD; + PFNGLBLENDEQUATIONIPROC pGlBlendEquationiARB; PFNGLPROGRAMPARAMETERIARBPROC pGlProgramParameteriARB; PFNGLPROGRAMPARAMETERIEXTPROC pGlProgramParameteriEXT; PFNGLGENQUERIESARBPROC pGlGenQueriesARB; @@ -1155,6 +1169,20 @@ class COpenGLExtensionHandler PFNGLENDOCCLUSIONQUERYNVPROC pGlEndOcclusionQueryNV; PFNGLGETOCCLUSIONQUERYIVNVPROC pGlGetOcclusionQueryivNV; PFNGLGETOCCLUSIONQUERYUIVNVPROC pGlGetOcclusionQueryuivNV; + PFNGLBLENDEQUATIONEXTPROC pGlBlendEquationEXT; + PFNGLBLENDEQUATIONPROC pGlBlendEquation; + #if defined(WGL_EXT_swap_control) + PFNWGLSWAPINTERVALEXTPROC pWglSwapIntervalEXT; + #endif + #if defined(GLX_SGI_swap_control) + PFNGLXSWAPINTERVALSGIPROC pGlxSwapIntervalSGI; + #endif + #if defined(GLX_EXT_swap_control) + PFNGLXSWAPINTERVALEXTPROC pGlxSwapIntervalEXT; + #endif + #if defined(GLX_MESA_swap_control) + PFNGLXSWAPINTERVALMESAPROC pGlxSwapIntervalMESA; + #endif #endif }; @@ -1478,6 +1506,7 @@ inline void COpenGLExtensionHandler::extGlDeleteShader(GLuint shader) inline void COpenGLExtensionHandler::extGlGetAttachedObjects(GLhandleARB program, GLsizei maxcount, GLsizei* count, GLhandleARB* shaders) { + *count=0; #ifdef _IRR_OPENGL_USE_EXTPOINTER_ if (pGlGetAttachedObjectsARB) pGlGetAttachedObjectsARB(program, maxcount, count, shaders); @@ -1490,6 +1519,7 @@ inline void COpenGLExtensionHandler::extGlGetAttachedObjects(GLhandleARB program inline void COpenGLExtensionHandler::extGlGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) { + *count=0; #ifdef _IRR_OPENGL_USE_EXTPOINTER_ if (pGlGetAttachedShaders) pGlGetAttachedShaders(program, maxcount, count, shaders); @@ -2155,6 +2185,23 @@ inline void COpenGLExtensionHandler::extGlBlendFuncIndexed(GLuint buf, GLenum sr } +inline void COpenGLExtensionHandler::extGlBlendEquationIndexed(GLuint buf, GLenum mode) +{ +#ifdef _IRR_OPENGL_USE_EXTPOINTER_ + if (FeatureAvailable[IRR_ARB_draw_buffers_blend] && pGlBlendEquationiARB) + pGlBlendEquationiARB(buf, mode); + if (FeatureAvailable[IRR_AMD_draw_buffers_blend] && pGlBlendEquationIndexedAMD) + pGlBlendEquationIndexedAMD(buf, mode); +#elif defined(GL_ARB_draw_buffers_blend) + glBlendEquationiARB(buf, src, dst); +#elif defined(GL_AMD_draw_buffers_blend) + glBlendEquationIndexedAMD(buf, src, dst); +#else + os::Printer::log("glBlendEquationIndexed not supported", ELL_ERROR); +#endif +} + + inline void COpenGLExtensionHandler::extGlProgramParameteri(GLuint program, GLenum pname, GLint value) { #if defined(_IRR_OPENGL_USE_EXTPOINTER_) @@ -2300,6 +2347,63 @@ inline void COpenGLExtensionHandler::extGlGetQueryObjectuiv(GLuint id, GLenum pn #endif } +inline void COpenGLExtensionHandler::extGlSwapInterval(int interval) +{ + // we have wglext, so try to use that +#if defined(_IRR_WINDOWS_API_) && defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) +#ifdef WGL_EXT_swap_control + if (pWglSwapIntervalEXT) + pWglSwapIntervalEXT(interval); +#endif +#endif +#ifdef _IRR_COMPILE_WITH_X11_DEVICE_ + //TODO: Check GLX_EXT_swap_control and GLX_MESA_swap_control +#ifdef GLX_SGI_swap_control + // does not work with interval==0 +#ifdef _IRR_OPENGL_USE_EXTPOINTER_ + if (interval && pGlxSwapIntervalSGI) + pGlxSwapIntervalSGI(interval); +#else + if (interval) + glXSwapIntervalSGI(interval); +#endif +#elif defined(GLX_EXT_swap_control) +#ifdef _IRR_OPENGL_USE_EXTPOINTER_ + Display *dpy = glXGetCurrentDisplay(); + GLXDrawable drawable = glXGetCurrentDrawable(); + + if (pGlxSwapIntervalEXT) + pGlxSwapIntervalEXT(dpy, drawable, interval); +#else + pGlXSwapIntervalEXT(dpy, drawable, interval); +#endif +#elif defined(GLX_MESA_swap_control) +#ifdef _IRR_OPENGL_USE_EXTPOINTER_ + if (pGlxSwapIntervalMESA) + pGlxSwapIntervalMESA(interval); +#else + pGlXSwapIntervalMESA(interval); +#endif +#endif +#endif +} + +inline void COpenGLExtensionHandler::extGlBlendEquation(GLenum mode) +{ +#ifdef _IRR_OPENGL_USE_EXTPOINTER_ + if (pGlBlendEquation) + pGlBlendEquation(mode); + else if (pGlBlendEquationEXT) + pGlBlendEquationEXT(mode); +#elif defined(GL_EXT_blend_minmax) || defined(GL_EXT_blend_subtract) || defined(GL_EXT_blend_logic_op) + glBlendEquationEXT(mode); +#elif defined(GL_VERSION_1_2) + glBlendEquation(mode); +#else + os::Printer::log("glBlendEquation not supported", ELL_ERROR); +#endif +} + } } diff --git a/source/Irrlicht/COpenGLMaterialRenderer.h b/source/Irrlicht/COpenGLMaterialRenderer.h index fe73aeb1..cb7ff103 100644 --- a/source/Irrlicht/COpenGLMaterialRenderer.h +++ b/source/Irrlicht/COpenGLMaterialRenderer.h @@ -77,13 +77,13 @@ public: E_BLEND_FACTOR srcFact,dstFact; E_MODULATE_FUNC modulate; u32 alphaSource; - unpack_texureBlendFunc ( srcFact, dstFact, modulate, alphaSource, material.MaterialTypeParam ); + unpack_texureBlendFunc(srcFact, dstFact, modulate, alphaSource, material.MaterialTypeParam); #ifdef GL_ARB_texture_env_combine glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, (f32) modulate ); #else glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); @@ -93,7 +93,7 @@ public: glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, (f32) modulate ); #endif - glBlendFunc( Driver->getGLBlend(srcFact), Driver->getGLBlend(dstFact) ); + glBlendFunc(Driver->getGLBlend(srcFact), Driver->getGLBlend(dstFact)); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.f); glEnable(GL_BLEND); @@ -124,20 +124,14 @@ public: { #ifdef GL_ARB_texture_env_combine glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_TEXTURE); #else glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PRIMARY_COLOR_EXT); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PRIMARY_COLOR_EXT); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_TEXTURE); #endif } - -#ifdef GL_ARB_texture_env_combine - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB); -#else - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT); -#endif } } } @@ -192,8 +186,8 @@ public: glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PRIMARY_COLOR); glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA); #else @@ -201,8 +195,8 @@ public: glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PRIMARY_COLOR_EXT); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_INTERPOLATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE2_RGB_EXT, GL_PRIMARY_COLOR); glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB_EXT, GL_SRC_ALPHA); #endif @@ -283,15 +277,15 @@ public: glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); #else glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PRIMARY_COLOR_EXT ); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); - glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); #endif glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); @@ -462,10 +456,10 @@ public: else glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PREVIOUS_ARB); #else glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); @@ -475,10 +469,10 @@ public: else glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PREVIOUS_EXT); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT); #endif @@ -556,13 +550,13 @@ public: #ifdef GL_ARB_texture_env_combine glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD_SIGNED_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); #else glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD_SIGNED_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); #endif } } @@ -638,13 +632,13 @@ public: #ifdef GL_ARB_texture_env_combine glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); #else glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); #endif } glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); @@ -690,15 +684,15 @@ public: #ifdef GL_ARB_texture_env_combine glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB); #else glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PREVIOUS_ARB); #endif @@ -708,15 +702,15 @@ public: #ifdef GL_ARB_texture_env_combine glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB); #else glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PREVIOUS_ARB); #endif diff --git a/source/Irrlicht/COpenGLTexture.cpp b/source/Irrlicht/COpenGLTexture.cpp index 8c7fb47a..f340d514 100644 --- a/source/Irrlicht/COpenGLTexture.cpp +++ b/source/Irrlicht/COpenGLTexture.cpp @@ -885,7 +885,7 @@ bool checkFBOStatus(COpenGLDriver* Driver) } #endif os::Printer::log("FBO error", ELL_ERROR); - _IRR_DEBUG_BREAK_IF(true); +// _IRR_DEBUG_BREAK_IF(true); return false; } diff --git a/source/Irrlicht/CParticleSphereEmitter.cpp b/source/Irrlicht/CParticleSphereEmitter.cpp index b2f782a0..4161214d 100644 --- a/source/Irrlicht/CParticleSphereEmitter.cpp +++ b/source/Irrlicht/CParticleSphereEmitter.cpp @@ -59,7 +59,7 @@ s32 CParticleSphereEmitter::emitt(u32 now, u32 timeSinceLastCall, SParticle*& ou for(u32 i=0; igetMeshBufferCount(); ++i) + mesh->getMeshBuffer(i)->recalculateBoundingBox(); + + mesh->recalculateBoundingBox(); + SAnimatedMesh *am = new SAnimatedMesh(); + am->addMesh(mesh); + mesh->drop(); + am->recalculateBoundingBox(); + + return am; +} + +void CSMFMeshFileLoader::loadLimb(io::IReadFile* file, SMesh* mesh, const core::matrix4 &parentTransformation) +{ + core::matrix4 transformation; + + // limb transformation + core::vector3df translate, rotate, scale; + io::BinaryFile::read(file, translate); + io::BinaryFile::read(file, rotate); + io::BinaryFile::read(file, scale); + + transformation.setTranslation(translate); + transformation.setRotationDegrees(rotate); + transformation.setScale(scale); + + transformation = parentTransformation * transformation; + + core::stringc textureName, textureGroupName; + + // texture information + io::BinaryFile::read(file, textureGroupName); + io::BinaryFile::read(file, textureName); + + // attempt to load texture using known formats + video::ITexture* texture = 0; + + const c8* extensions[] = {".jpg", ".png", ".tga", ".bmp", 0}; + + for (const c8 **ext = extensions; !texture && *ext; ++ext) + { + texture = Driver->getTexture(textureName + *ext); + if (texture) + textureName = textureName + *ext; + } + // find the correct mesh buffer + u32 i; + for (i=0; iMeshBuffers.size(); ++i) + if (mesh->MeshBuffers[i]->getMaterial().TextureLayer[0].Texture == texture) + break; + + // create mesh buffer if none was found + if (i == mesh->MeshBuffers.size()) + { + CMeshBuffer* mb = new CMeshBuffer(); + mb->Material.TextureLayer[0].Texture = texture; + + // horribly hacky way to do this, maybe it's in the flags? + if (core::hasFileExtension(textureName, "tga", "png")) + mb->Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + else + mb->Material.MaterialType = video::EMT_SOLID; + + mesh->MeshBuffers.push_back(mb); + } + + CMeshBuffer* mb = (CMeshBuffer*)mesh->MeshBuffers[i]; + + u16 vertexCount, firstVertex = mb->getVertexCount(); + + io::BinaryFile::read(file, vertexCount); + mb->Vertices.reallocate(mb->Vertices.size() + vertexCount); + + // add vertices and set positions + for (i=0; iVertices.push_back(vert); + } + + // set vertex normals + for (i=0; i < vertexCount; ++i) + { + core::vector3df normal; + io::BinaryFile::read(file, normal); + transformation.rotateVect(normal); + mb->Vertices[firstVertex + i].Normal = normal; + } + // set texture coordinates + + for (i=0; i < vertexCount; ++i) + { + core::vector2df tcoords; + io::BinaryFile::read(file, tcoords); + mb->Vertices[firstVertex + i].TCoords = tcoords; + } + + // triangles + u32 triangleCount; + // vertexCount used as temporary + io::BinaryFile::read(file, vertexCount); + triangleCount=3*vertexCount; + mb->Indices.reallocate(mb->Indices.size() + triangleCount); + + for (i=0; i < triangleCount; ++i) + { + u16 index; + io::BinaryFile::read(file, index); + mb->Indices.push_back(firstVertex + index); + } + + // read limbs + s32 limbCount; + io::BinaryFile::read(file, limbCount); + + for (s32 l=0; l < limbCount; ++l) + loadLimb(file, mesh, transformation); +} + +} // namespace scene + +// todo: at some point in the future let's move these to a place where everyone can use them. +namespace io +{ + +#if _BIGENDIAN +#define _SYSTEM_BIG_ENDIAN_ (true) +#else +#define _SYSTEM_BIG_ENDIAN_ (false) +#endif + +template +void BinaryFile::read(io::IReadFile* file, T &out, bool bigEndian) +{ + file->read((void*)&out, sizeof(out)); + if (bigEndian != (_SYSTEM_BIG_ENDIAN_)) + out = os::Byteswap::byteswap(out); +} + +//! reads a 3d vector from the file, moving the file pointer along +void BinaryFile::read(io::IReadFile* file, core::vector3df &outVector2d, bool bigEndian) +{ + BinaryFile::read(file, outVector2d.X, bigEndian); + BinaryFile::read(file, outVector2d.Y, bigEndian); + BinaryFile::read(file, outVector2d.Z, bigEndian); +} + +//! reads a 2d vector from the file, moving the file pointer along +void BinaryFile::read(io::IReadFile* file, core::vector2df &outVector2d, bool bigEndian) +{ + BinaryFile::read(file, outVector2d.X, bigEndian); + BinaryFile::read(file, outVector2d.Y, bigEndian); +} + +//! reads a null terminated string from the file, moving the file pointer along +void BinaryFile::read(io::IReadFile* file, core::stringc &outString, bool bigEndian) +{ + c8 c; + file->read((void*)&c, 1); + + while (c) + { + outString += c; + file->read((void*)&c, 1); + } +} + +} // namespace io + +} // namespace irr + +#endif // compile with SMF loader + diff --git a/source/Irrlicht/CSMFMeshFileLoader.h b/source/Irrlicht/CSMFMeshFileLoader.h new file mode 100644 index 00000000..e018b405 --- /dev/null +++ b/source/Irrlicht/CSMFMeshFileLoader.h @@ -0,0 +1,67 @@ +// Copyright (C) 2010-2011 Gaz Davidson +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_SMF_MESH_LOADER_H_INCLUDED__ +#define __C_SMF_MESH_LOADER_H_INCLUDED__ + +#include "IMeshLoader.h" +#include "SMesh.h" + +namespace irr +{ + +namespace video +{ + class IVideoDriver; +} + +namespace scene +{ + +//! Class which can load +class CSMFMeshFileLoader : public virtual IMeshLoader +{ +public: + + CSMFMeshFileLoader(video::IVideoDriver* driver); + + //! Returns true if the file might be loaded by this class. + virtual bool isALoadableFileExtension(const io::path& filename) const; + + //! Creates/loads an animated mesh from the file. + virtual IAnimatedMesh* createMesh(io::IReadFile* file); +private: + + void loadLimb(io::IReadFile* file, scene::SMesh* mesh, const core::matrix4 &parentTransformation); + + video::IVideoDriver* Driver; +}; + +} // end namespace scene + +namespace io +{ + class BinaryFile + { + public: + //! reads most types from the given file, moving the file pointer along + template + static void read(io::IReadFile* file, T &out, bool bigEndian=false); + + //! reads a 3d vector from the file, moving the file pointer along + static void read(io::IReadFile* file, core::vector3df &outVector2d, bool bigEndian=false); + + //! reads a 2d vector from the file, moving the file pointer along + static void read(io::IReadFile* file, core::vector2df &outVector2d, bool bigEndian=false); + + //! reads a null terminated string from the file, moving the file pointer along + static void read(io::IReadFile* file, core::stringc &outString, bool bigEndian=false); + + }; +} + +} // end namespace irr + +#endif // __C_SMF_MESH_LOADER_H_INCLUDED__ + diff --git a/source/Irrlicht/CSceneLoaderIrr.cpp b/source/Irrlicht/CSceneLoaderIrr.cpp new file mode 100644 index 00000000..f2dd123b --- /dev/null +++ b/source/Irrlicht/CSceneLoaderIrr.cpp @@ -0,0 +1,280 @@ +#include "CSceneLoaderIrr.h" +#include "ISceneNodeAnimatorFactory.h" +#include "ISceneUserDataSerializer.h" +#include "ISceneManager.h" +#include "IVideoDriver.h" +#include "IFileSystem.h" +#include "os.h" + +namespace irr +{ +namespace scene +{ + +//! Constructor +CSceneLoaderIrr::CSceneLoaderIrr(ISceneManager *smgr, io::IFileSystem* fs) + : SceneManager(smgr), FileSystem(fs), + IRR_XML_FORMAT_SCENE(L"irr_scene"), IRR_XML_FORMAT_NODE(L"node"), IRR_XML_FORMAT_NODE_ATTR_TYPE(L"type"), + IRR_XML_FORMAT_ATTRIBUTES(L"attributes"), IRR_XML_FORMAT_MATERIALS(L"materials"), + IRR_XML_FORMAT_ANIMATORS(L"animators"), IRR_XML_FORMAT_USERDATA(L"userData") +{ + +} + +//! Destructor +CSceneLoaderIrr::~CSceneLoaderIrr() +{ + +} + +//! Returns true if the class might be able to load this file. +bool CSceneLoaderIrr::isALoadableFileExtension(const io::path& filename) const +{ + return core::hasFileExtension(filename, "irr"); +} + +//! Returns true if the class might be able to load this file. +bool CSceneLoaderIrr::isALoadableFileFormat(io::IReadFile *file) const +{ + // todo: check inside the file + return true; +} + +//! Loads the scene into the scene manager. +bool CSceneLoaderIrr::loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer, + ISceneNode* rootNode) +{ + if (!file) + { + os::Printer::log("Unable to open scene file", ELL_ERROR); + return false; + } + + io::IXMLReader* reader = FileSystem->createXMLReader(file); + if (!reader) + { + os::Printer::log("Scene is not a valid XML file", file->getFileName().c_str(), ELL_ERROR); + return false; + } + + // TODO: COLLADA_CREATE_SCENE_INSTANCES can be removed when the COLLADA loader is a scene loader + bool oldColladaSingleMesh = SceneManager->getParameters()->getAttributeAsBool(COLLADA_CREATE_SCENE_INSTANCES); + SceneManager->getParameters()->setAttribute(COLLADA_CREATE_SCENE_INSTANCES, false); + + // read file + while (reader->read()) + { + readSceneNode(reader, rootNode, userDataSerializer); + } + + // restore old collada parameters + SceneManager->getParameters()->setAttribute(COLLADA_CREATE_SCENE_INSTANCES, oldColladaSingleMesh); + + // clean up + reader->drop(); + return true; +} + + +//! Reads the next node +void CSceneLoaderIrr::readSceneNode(io::IXMLReader* reader, ISceneNode* parent, + ISceneUserDataSerializer* userDataSerializer) +{ + if (!reader) + return; + + scene::ISceneNode* node = 0; + + if (!parent && IRR_XML_FORMAT_SCENE==reader->getNodeName()) + node = SceneManager->getRootSceneNode(); + else if (parent && IRR_XML_FORMAT_NODE==reader->getNodeName()) + { + // find node type and create it + core::stringc attrName = reader->getAttributeValue(IRR_XML_FORMAT_NODE_ATTR_TYPE.c_str()); + + node = SceneManager->addSceneNode(attrName.c_str(), parent); + + if (!node) + os::Printer::log("Could not create scene node of unknown type", attrName.c_str()); + } + else + node=parent; + + // read attributes + while(reader->read()) + { + bool endreached = false; + + const wchar_t* name = reader->getNodeName(); + + switch (reader->getNodeType()) + { + case io::EXN_ELEMENT_END: + if ((IRR_XML_FORMAT_NODE == name) || + (IRR_XML_FORMAT_SCENE == name)) + { + endreached = true; + } + break; + case io::EXN_ELEMENT: + if (IRR_XML_FORMAT_ATTRIBUTES == name) + { + // read attributes + io::IAttributes* attr = FileSystem->createEmptyAttributes(SceneManager->getVideoDriver()); + attr->read(reader, true); + + if (node) + node->deserializeAttributes(attr); + + attr->drop(); + } + else + if (IRR_XML_FORMAT_MATERIALS == name) + readMaterials(reader, node); + else + if (IRR_XML_FORMAT_ANIMATORS == name) + readAnimators(reader, node); + else + if (IRR_XML_FORMAT_USERDATA == name) + readUserData(reader, node, userDataSerializer); + else + if ((IRR_XML_FORMAT_NODE == name) || + (IRR_XML_FORMAT_SCENE == name)) + { + readSceneNode(reader, node, userDataSerializer); + } + else + { + os::Printer::log("Found unknown element in irrlicht scene file", + core::stringc(name).c_str()); + } + break; + default: + break; + } + + if (endreached) + break; + } + if (node && userDataSerializer) + userDataSerializer->OnCreateNode(node); +} + +//! reads materials of a node +void CSceneLoaderIrr::readMaterials(io::IXMLReader* reader, ISceneNode* node) +{ + u32 nr = 0; + + while(reader->read()) + { + const wchar_t* name = reader->getNodeName(); + + switch(reader->getNodeType()) + { + case io::EXN_ELEMENT_END: + if (IRR_XML_FORMAT_MATERIALS == name) + return; + break; + case io::EXN_ELEMENT: + if (IRR_XML_FORMAT_ATTRIBUTES == name) + { + // read materials from attribute list + io::IAttributes* attr = FileSystem->createEmptyAttributes(SceneManager->getVideoDriver()); + attr->read(reader); + + if (node && node->getMaterialCount() > nr) + { + SceneManager->getVideoDriver()->fillMaterialStructureFromAttributes( + node->getMaterial(nr), attr); + } + + attr->drop(); + ++nr; + } + break; + default: + break; + } + } +} + + +//! reads animators of a node +void CSceneLoaderIrr::readAnimators(io::IXMLReader* reader, ISceneNode* node) +{ + while(reader->read()) + { + const wchar_t* name = reader->getNodeName(); + + switch(reader->getNodeType()) + { + case io::EXN_ELEMENT_END: + if (IRR_XML_FORMAT_ANIMATORS == name) + return; + break; + case io::EXN_ELEMENT: + if (IRR_XML_FORMAT_ATTRIBUTES == name) + { + // read animator data from attribute list + io::IAttributes* attr = FileSystem->createEmptyAttributes(SceneManager->getVideoDriver()); + attr->read(reader); + + if (node) + { + core::stringc typeName = attr->getAttributeAsString("Type"); + ISceneNodeAnimator* anim = SceneManager->createSceneNodeAnimator(typeName.c_str(), node); + + if (anim) + { + anim->deserializeAttributes(attr); + anim->drop(); + } + } + + attr->drop(); + } + break; + default: + break; + } + } +} + + +//! reads user data of a node +void CSceneLoaderIrr::readUserData(io::IXMLReader* reader, ISceneNode* node, ISceneUserDataSerializer* userDataSerializer) +{ + while(reader->read()) + { + const wchar_t* name = reader->getNodeName(); + + switch(reader->getNodeType()) + { + case io::EXN_ELEMENT_END: + if (IRR_XML_FORMAT_USERDATA == name) + return; + break; + case io::EXN_ELEMENT: + if (IRR_XML_FORMAT_ATTRIBUTES == name) + { + // read user data from attribute list + io::IAttributes* attr = FileSystem->createEmptyAttributes(SceneManager->getVideoDriver()); + attr->read(reader); + + if (node && userDataSerializer) + { + userDataSerializer->OnReadUserData(node, attr); + } + + attr->drop(); + } + break; + default: + break; + } + } +} + +} // scene +} // irr + diff --git a/source/Irrlicht/CSceneLoaderIrr.h b/source/Irrlicht/CSceneLoaderIrr.h new file mode 100644 index 00000000..a711f236 --- /dev/null +++ b/source/Irrlicht/CSceneLoaderIrr.h @@ -0,0 +1,78 @@ +#ifndef __C_SCENE_LOADER_IRR_H_INCLUDED__ +#define __C_SCENE_LOADER_IRR_H_INCLUDED__ + +#include "ISceneLoader.h" + +#include "IXMLReader.h" + +namespace irr +{ + +namespace io +{ + class IFileSystem; +} + +namespace scene +{ + +class ISceneManager; + +//! Class which can load a scene into the scene manager. +class CSceneLoaderIrr : public virtual ISceneLoader +{ +public: + + //! Constructor + CSceneLoaderIrr(ISceneManager *smgr, io::IFileSystem* fs); + + //! Destructor + virtual ~CSceneLoaderIrr(); + + //! Returns true if the class might be able to load this file. + virtual bool isALoadableFileExtension(const io::path& filename) const; + + //! Returns true if the class might be able to load this file. + virtual bool isALoadableFileFormat(io::IReadFile *file) const; + + //! Loads the scene into the scene manager. + virtual bool loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer=0, + ISceneNode* rootNode=0); + +private: + + //! Recursively reads nodes from the xml file + void readSceneNode(io::IXMLReader* reader, ISceneNode* parent, + ISceneUserDataSerializer* userDataSerializer); + + //! read a node's materials + void readMaterials(io::IXMLReader* reader, ISceneNode* node); + + //! read a node's animators + void readAnimators(io::IXMLReader* reader, ISceneNode* node); + + //! read any other data into the user serializer + void readUserData(io::IXMLReader* reader, ISceneNode* node, + ISceneUserDataSerializer* userDataSerializer); + + ISceneManager *SceneManager; + io::IFileSystem *FileSystem; + + //! constants for reading and writing XML. + //! Not made static due to portability problems. + // TODO: move to own header + const core::stringw IRR_XML_FORMAT_SCENE; + const core::stringw IRR_XML_FORMAT_NODE; + const core::stringw IRR_XML_FORMAT_NODE_ATTR_TYPE; + const core::stringw IRR_XML_FORMAT_ATTRIBUTES; + const core::stringw IRR_XML_FORMAT_MATERIALS; + const core::stringw IRR_XML_FORMAT_ANIMATORS; + const core::stringw IRR_XML_FORMAT_USERDATA; +}; + + +} // end namespace scene +} // end namespace irr + +#endif + diff --git a/source/Irrlicht/CSceneManager.cpp b/source/Irrlicht/CSceneManager.cpp index fe3423d1..a2adb1e0 100644 --- a/source/Irrlicht/CSceneManager.cpp +++ b/source/Irrlicht/CSceneManager.cpp @@ -8,15 +8,22 @@ #include "IFileSystem.h" #include "SAnimatedMesh.h" #include "CMeshCache.h" -#include "IWriteFile.h" #include "IXMLWriter.h" #include "ISceneUserDataSerializer.h" #include "IGUIEnvironment.h" #include "IMaterialRenderer.h" #include "IReadFile.h" +#include "IWriteFile.h" +#include "ISceneLoader.h" #include "os.h" +// We need this include for the case of skinned mesh support without +// any such loader +#ifdef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ +#include "CSkinnedMesh.h" +#endif + #ifdef _IRR_COMPILE_WITH_IRR_MESH_LOADER_ #include "CIrrMeshFileLoader.h" #endif @@ -97,6 +104,14 @@ #include "CPLYMeshFileLoader.h" #endif +#ifdef _IRR_COMPILE_WITH_SMF_LOADER_ +#include "CSMFMeshFileLoader.h" +#endif + +#ifdef _IRR_COMPILE_WITH_IRR_SCENE_LOADER_ +#include "CSceneLoaderIrr.h" +#endif + #ifdef _IRR_COMPILE_WITH_COLLADA_WRITER_ #include "CColladaMeshWriter.h" #endif @@ -122,7 +137,6 @@ #include "CAnimatedMeshSceneNode.h" #include "COctreeSceneNode.h" #include "CCameraSceneNode.h" - #include "CLightSceneNode.h" #include "CBillboardSceneNode.h" #include "CMeshSceneNode.h" @@ -134,6 +148,9 @@ #include "CTerrainSceneNode.h" #include "CEmptySceneNode.h" #include "CTextSceneNode.h" +#include "CQuake3ShaderSceneNode.h" +#include "CVolumeLightSceneNode.h" + #include "CDefaultSceneNodeFactory.h" #include "CSceneCollisionManager.h" @@ -154,8 +171,6 @@ #include "CSceneNodeAnimatorCameraMaya.h" #include "CDefaultSceneNodeAnimatorFactory.h" -#include "CQuake3ShaderSceneNode.h" -#include "CVolumeLightSceneNode.h" #include "CGeometryCreator.h" //! Enable debug features @@ -212,28 +227,20 @@ CSceneManager::CSceneManager(video::IVideoDriver* driver, io::IFileSystem* fs, // create geometry creator GeometryCreator = new CGeometryCreator(); - // add file format loaders + // add file format loaders. add the least commonly used ones first, + // as these are checked last - #ifdef _IRR_COMPILE_WITH_IRR_MESH_LOADER_ - MeshLoaderList.push_back(new CIrrMeshFileLoader(this, FileSystem)); + // TODO: now that we have multiple scene managers, these should be + // shallow copies from the previous manager if there is one. + + #ifdef _IRR_COMPILE_WITH_STL_LOADER_ + MeshLoaderList.push_back(new CSTLMeshFileLoader()); #endif - #ifdef _IRR_COMPILE_WITH_BSP_LOADER_ - MeshLoaderList.push_back(new CBSPMeshFileLoader(this, FileSystem)); + #ifdef _IRR_COMPILE_WITH_PLY_LOADER_ + MeshLoaderList.push_back(new CPLYMeshFileLoader()); #endif - #ifdef _IRR_COMPILE_WITH_MD2_LOADER_ - MeshLoaderList.push_back(new CMD2MeshFileLoader()); - #endif - #ifdef _IRR_COMPILE_WITH_MS3D_LOADER_ - MeshLoaderList.push_back(new CMS3DMeshFileLoader(Driver)); - #endif - #ifdef _IRR_COMPILE_WITH_HALFLIFE_LOADER_ - MeshLoaderList.push_back(new CHalflifeMDLMeshFileLoader( this )); - #endif - #ifdef _IRR_COMPILE_WITH_3DS_LOADER_ - MeshLoaderList.push_back(new C3DSMeshFileLoader(this, FileSystem)); - #endif - #ifdef _IRR_COMPILE_WITH_X_LOADER_ - MeshLoaderList.push_back(new CXMeshFileLoader(this, FileSystem)); + #ifdef _IRR_COMPILE_WITH_SMF_LOADER_ + MeshLoaderList.push_back(new CSMFMeshFileLoader(Driver)); #endif #ifdef _IRR_COMPILE_WITH_OCT_LOADER_ MeshLoaderList.push_back(new COCTLoader(this, FileSystem)); @@ -247,33 +254,54 @@ CSceneManager::CSceneManager(video::IVideoDriver* driver, io::IFileSystem* fs, #ifdef _IRR_COMPILE_WITH_MY3D_LOADER_ MeshLoaderList.push_back(new CMY3DMeshFileLoader(this, FileSystem)); #endif - #ifdef _IRR_COMPILE_WITH_COLLADA_LOADER_ - MeshLoaderList.push_back(new CColladaFileLoader(this, FileSystem)); - #endif #ifdef _IRR_COMPILE_WITH_DMF_LOADER_ MeshLoaderList.push_back(new CDMFLoader(this, FileSystem)); #endif #ifdef _IRR_COMPILE_WITH_OGRE_LOADER_ MeshLoaderList.push_back(new COgreMeshFileLoader(FileSystem, Driver)); #endif - #ifdef _IRR_COMPILE_WITH_OBJ_LOADER_ - MeshLoaderList.push_back(new COBJMeshFileLoader(this, FileSystem)); + #ifdef _IRR_COMPILE_WITH_HALFLIFE_LOADER_ + MeshLoaderList.push_back(new CHalflifeMDLMeshFileLoader( this )); #endif #ifdef _IRR_COMPILE_WITH_MD3_LOADER_ MeshLoaderList.push_back(new CMD3MeshFileLoader( this)); #endif - #ifdef _IRR_COMPILE_WITH_B3D_LOADER_ - MeshLoaderList.push_back(new CB3DMeshFileLoader(this)); - #endif #ifdef _IRR_COMPILE_WITH_LWO_LOADER_ MeshLoaderList.push_back(new CLWOMeshFileLoader(this, FileSystem)); #endif - #ifdef _IRR_COMPILE_WITH_STL_LOADER_ - MeshLoaderList.push_back(new CSTLMeshFileLoader()); + #ifdef _IRR_COMPILE_WITH_MD2_LOADER_ + MeshLoaderList.push_back(new CMD2MeshFileLoader()); #endif - #ifdef _IRR_COMPILE_WITH_PLY_LOADER_ - MeshLoaderList.push_back(new CPLYMeshFileLoader()); + #ifdef _IRR_COMPILE_WITH_IRR_MESH_LOADER_ + MeshLoaderList.push_back(new CIrrMeshFileLoader(this, FileSystem)); #endif + #ifdef _IRR_COMPILE_WITH_BSP_LOADER_ + MeshLoaderList.push_back(new CBSPMeshFileLoader(this, FileSystem)); + #endif + #ifdef _IRR_COMPILE_WITH_COLLADA_LOADER_ + MeshLoaderList.push_back(new CColladaFileLoader(this, FileSystem)); + #endif + #ifdef _IRR_COMPILE_WITH_3DS_LOADER_ + MeshLoaderList.push_back(new C3DSMeshFileLoader(this, FileSystem)); + #endif + #ifdef _IRR_COMPILE_WITH_X_LOADER_ + MeshLoaderList.push_back(new CXMeshFileLoader(this, FileSystem)); + #endif + #ifdef _IRR_COMPILE_WITH_MS3D_LOADER_ + MeshLoaderList.push_back(new CMS3DMeshFileLoader(Driver)); + #endif + #ifdef _IRR_COMPILE_WITH_OBJ_LOADER_ + MeshLoaderList.push_back(new COBJMeshFileLoader(this, FileSystem)); + #endif + #ifdef _IRR_COMPILE_WITH_B3D_LOADER_ + MeshLoaderList.push_back(new CB3DMeshFileLoader(this)); + #endif + + // scene loaders + #ifdef _IRR_COMPILE_WITH_IRR_SCENE_LOADER_ + SceneLoaderList.push_back(new CSceneLoaderIrr(this, FileSystem)); + #endif + // factories ISceneNodeFactory* factory = new CDefaultSceneNodeFactory(this); @@ -295,7 +323,7 @@ CSceneManager::~CSceneManager() //! because Scenes may hold internally data bounded to sceneNodes //! which may be destroyed twice if (Driver) - Driver->removeAllHardwareBuffers (); + Driver->removeAllHardwareBuffers(); if (FileSystem) FileSystem->drop(); @@ -313,10 +341,12 @@ CSceneManager::~CSceneManager() GUIEnvironment->drop(); u32 i; - for (i=0; idrop(); + for (i=0; idrop(); + if (ActiveCamera) ActiveCamera->drop(); ActiveCamera = 0; @@ -330,7 +360,7 @@ CSceneManager::~CSceneManager() for (i=0; idrop(); - if(LightManager) + if (LightManager) LightManager->drop(); // remove all nodes and animators before dropping the driver @@ -358,6 +388,7 @@ IAnimatedMesh* CSceneManager::getMesh(const io::path& filename) return 0; } + // iterate the list in reverse order so user-added loaders can override the built-in ones s32 count = MeshLoaderList.size(); for (s32 i=count-1; i>=0; --i) { @@ -397,6 +428,7 @@ IAnimatedMesh* CSceneManager::getMesh(io::IReadFile* file) if (msh) return msh; + // iterate the list in reverse order so user-added loaders can override the built-in ones s32 count = MeshLoaderList.size(); for (s32 i=count-1; i>=0; --i) { @@ -444,8 +476,6 @@ io::IFileSystem* CSceneManager::getFileSystem() return FileSystem; } - - //! Adds a text scene node, which is able to display //! 2d text at a position in three dimensional space ITextSceneNode* CSceneManager::addTextSceneNode(gui::IGUIFont* font, @@ -476,7 +506,7 @@ IBillboardTextSceneNode* CSceneManager::addBillboardTextSceneNode(gui::IGUIFont* if (!font && GUIEnvironment) font = GUIEnvironment->getBuiltInFont(); - if(!font) + if (!font) return 0; if (!parent) @@ -497,7 +527,7 @@ IMeshSceneNode* CSceneManager::addQuake3SceneNode(const IMeshBuffer* meshBuffer, ISceneNode* parent, s32 id ) { #ifdef _IRR_COMPILE_WITH_BSP_LOADER_ - if ( 0 == shader ) + if (!shader) return 0; if (!parent) @@ -827,7 +857,7 @@ ITerrainSceneNode* CSceneManager::addTerrainSceneNode( { io::IReadFile* file = FileSystem->createAndOpenFile(heightMapFileName); - if(!file && !addAlsoIfHeightmapEmpty) + if (!file && !addAlsoIfHeightmapEmpty) { os::Printer::log("Could not load terrain, because file could not be opened.", heightMapFileName, ELL_ERROR); @@ -1206,15 +1236,15 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE case ESNRP_CAMERA: { taken = 1; - for ( u32 i = 0; i != CameraList.size(); ++i ) + for (u32 i = 0; i != CameraList.size(); ++i) { - if ( CameraList[i] == node ) + if (CameraList[i] == node) { taken = 0; break; } } - if ( taken ) + if (taken) { CameraList.push_back(node); } @@ -1226,7 +1256,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE // Lighting model in irrlicht has to be redone.. //if (!isCulled(node)) { - LightList.push_back(static_cast(node)); + LightList.push_back(node); taken = 1; } break; @@ -1277,7 +1307,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE } // not transparent, register as solid - if ( 0 == taken ) + if (!taken) { SolidNodeList.push_back(node); taken = 1; @@ -1300,7 +1330,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE s32 index = Parameters.findAttribute ( "calls" ); Parameters.setAttribute ( index, Parameters.getAttributeAsInt ( index ) + 1 ); - if ( 0 == taken ) + if (!taken) { index = Parameters.findAttribute ( "culled" ); Parameters.setAttribute ( index, Parameters.getAttributeAsInt ( index ) + 1 ); @@ -1329,7 +1359,7 @@ void CSceneManager::drawAll() // reset all transforms video::IVideoDriver* driver = getVideoDriver(); - if ( driver ) + if (driver) { driver->setMaterial(video::SMaterial()); driver->setTransform ( video::ETS_PROJECTION, core::IdentityMatrix ); @@ -1349,7 +1379,7 @@ void CSceneManager::drawAll() consistent Camera is needed for culling */ camWorldPos.set(0,0,0); - if ( ActiveCamera ) + if (ActiveCamera) { ActiveCamera->render(); camWorldPos = ActiveCamera->getAbsolutePosition(); @@ -1358,7 +1388,7 @@ void CSceneManager::drawAll() // let all nodes register themselves OnRegisterSceneNode(); - if(LightManager) + if (LightManager) LightManager->OnPreRender(LightList); //render camera scenes @@ -1366,7 +1396,7 @@ void CSceneManager::drawAll() CurrentRendertime = ESNRP_CAMERA; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0); - if(LightManager) + if (LightManager) LightManager->OnRenderPassPreRender(CurrentRendertime); for (i=0; iOnRenderPassPostRender(CurrentRendertime); } @@ -1383,7 +1413,7 @@ void CSceneManager::drawAll() CurrentRendertime = ESNRP_LIGHT; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0); - if(LightManager) + if (LightManager) { LightManager->OnRenderPassPreRender(CurrentRendertime); } @@ -1391,19 +1421,19 @@ void CSceneManager::drawAll() { // Sort the lights by distance from the camera core::vector3df camWorldPos(0, 0, 0); - if(ActiveCamera) + if (ActiveCamera) camWorldPos = ActiveCamera->getAbsolutePosition(); core::array SortedLights; SortedLights.set_used(LightList.size()); - for(s32 light = (s32)LightList.size() - 1; light >= 0; --light) + for (s32 light = (s32)LightList.size() - 1; light >= 0; --light) SortedLights[light].setNodeAndDistanceFromPosition(LightList[light], camWorldPos); SortedLights.set_sorted(false); SortedLights.sort(); for(s32 light = (s32)LightList.size() - 1; light >= 0; --light) - LightList[light] = static_cast(SortedLights[light].Node); + LightList[light] = SortedLights[light].Node; } Driver->deleteAllDynamicLights(); @@ -1412,13 +1442,13 @@ void CSceneManager::drawAll() u32 maxLights = LightList.size(); - if(!LightManager) + if (!LightManager) maxLights = core::min_ ( Driver->getMaximalDynamicLightAmount(), maxLights); for (i=0; i< maxLights; ++i) LightList[i]->render(); - if(LightManager) + if (LightManager) LightManager->OnRenderPassPostRender(CurrentRendertime); } @@ -1427,7 +1457,7 @@ void CSceneManager::drawAll() CurrentRendertime = ESNRP_SKY_BOX; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0); - if(LightManager) + if (LightManager) { LightManager->OnRenderPassPreRender(CurrentRendertime); for (i=0; iOnRenderPassPostRender(CurrentRendertime); } @@ -1458,7 +1488,7 @@ void CSceneManager::drawAll() SolidNodeList.sort(); // sort by textures - if(LightManager) + if (LightManager) { LightManager->OnRenderPassPreRender(CurrentRendertime); for (i=0; irender(); } - Parameters.setAttribute ( "drawn_solid", (s32) SolidNodeList.size() ); + Parameters.setAttribute("drawn_solid", (s32) SolidNodeList.size() ); SolidNodeList.set_used(0); - if(LightManager) + if (LightManager) LightManager->OnRenderPassPostRender(CurrentRendertime); } @@ -1487,7 +1517,7 @@ void CSceneManager::drawAll() CurrentRendertime = ESNRP_SHADOW; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0); - if(LightManager) + if (LightManager) { LightManager->OnRenderPassPreRender(CurrentRendertime); for (i=0; iOnRenderPassPostRender(CurrentRendertime); } @@ -1520,7 +1550,7 @@ void CSceneManager::drawAll() Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0); TransparentNodeList.sort(); // sort by distance from camera - if(LightManager) + if (LightManager) { LightManager->OnRenderPassPreRender(CurrentRendertime); @@ -1541,7 +1571,7 @@ void CSceneManager::drawAll() Parameters.setAttribute ( "drawn_transparent", (s32) TransparentNodeList.size() ); TransparentNodeList.set_used(0); - if(LightManager) + if (LightManager) LightManager->OnRenderPassPostRender(CurrentRendertime); } @@ -1552,7 +1582,7 @@ void CSceneManager::drawAll() TransparentEffectNodeList.sort(); // sort by distance from camera - if(LightManager) + if (LightManager) { LightManager->OnRenderPassPreRender(CurrentRendertime); @@ -1574,7 +1604,7 @@ void CSceneManager::drawAll() TransparentEffectNodeList.set_used(0); } - if(LightManager) + if (LightManager) LightManager->OnPostRender(); LightList.set_used(0); @@ -1585,9 +1615,9 @@ void CSceneManager::drawAll() void CSceneManager::setLightManager(ILightManager* lightManager) { - if ( lightManager ) + if (lightManager) lightManager->grab(); - if(LightManager) + if (LightManager) LightManager->drop(); LightManager = lightManager; @@ -1608,7 +1638,6 @@ video::SColor CSceneManager::getShadowColor() const } - //! creates a rotation animator, which rotates the attached scene node around itself. ISceneNodeAnimator* CSceneManager::createRotationAnimator(const core::vector3df& rotationPerSecond) { @@ -1619,7 +1648,6 @@ ISceneNodeAnimator* CSceneManager::createRotationAnimator(const core::vector3df& } - //! creates a fly circle animator, which lets the attached scene node fly around a center. ISceneNodeAnimator* CSceneManager::createFlyCircleAnimator( const core::vector3df& center, f32 radius, f32 speed, @@ -1669,8 +1697,6 @@ ISceneNodeAnimator* CSceneManager::createDeleteAnimator(u32 when) } - - //! Creates a special scene node animator for doing automatic collision detection //! and response. ISceneNodeAnimatorCollisionResponse* CSceneManager::createCollisionResponseAnimator( @@ -1698,7 +1724,6 @@ ISceneNodeAnimator* CSceneManager::createFollowSplineAnimator(s32 startTime, } - //! Adds an external mesh loader. void CSceneManager::addExternalMeshLoader(IMeshLoader* externalLoader) { @@ -1710,6 +1735,51 @@ void CSceneManager::addExternalMeshLoader(IMeshLoader* externalLoader) } +//! Returns the number of mesh loaders supported by Irrlicht at this time +u32 CSceneManager::getMeshLoaderCount() const +{ + return MeshLoaderList.size(); +} + + +//! Retrieve the given mesh loader +IMeshLoader* CSceneManager::getMeshLoader(u32 index) const +{ + if (index < MeshLoaderList.size()) + return MeshLoaderList[index]; + else + return 0; +} + + +//! Adds an external scene loader. +void CSceneManager::addExternalSceneLoader(ISceneLoader* externalLoader) +{ + if (!externalLoader) + return; + + externalLoader->grab(); + SceneLoaderList.push_back(externalLoader); +} + + +//! Returns the number of scene loaders +u32 CSceneManager::getSceneLoaderCount() const +{ + return SceneLoaderList.size(); +} + + +//! Retrieve the given scene loader +ISceneLoader* CSceneManager::getSceneLoader(u32 index) const +{ + if (index < SceneLoaderList.size()) + return SceneLoaderList[index]; + else + return 0; +} + + //! Returns a pointer to the scene collision manager. ISceneCollisionManager* CSceneManager::getSceneCollisionManager() { @@ -1738,12 +1808,13 @@ ITriangleSelector* CSceneManager::createTriangleSelector(IMesh* mesh, ISceneNode //! animated scene node ITriangleSelector* CSceneManager::createTriangleSelector(IAnimatedMeshSceneNode* node) { - if(!node || !node->getMesh()) + if (!node || !node->getMesh()) return 0; return new CTriangleSelector(node); } + //! Creates a simple dynamic ITriangleSelector, based on a axis aligned bounding box. ITriangleSelector* CSceneManager::createTriangleSelectorFromBoundingBox(ISceneNode* node) { @@ -1756,8 +1827,7 @@ ITriangleSelector* CSceneManager::createTriangleSelectorFromBoundingBox(ISceneNo //! Creates a simple ITriangleSelector, based on a mesh. ITriangleSelector* CSceneManager::createOctreeTriangleSelector(IMesh* mesh, - ISceneNode* node, - s32 minimalPolysPerNode) + ISceneNode* node, s32 minimalPolysPerNode) { if (!mesh) return 0; @@ -1766,7 +1836,6 @@ ITriangleSelector* CSceneManager::createOctreeTriangleSelector(IMesh* mesh, } - //! Creates a meta triangle selector. IMetaTriangleSelector* CSceneManager::createMetaTriangleSelector() { @@ -1774,7 +1843,6 @@ IMetaTriangleSelector* CSceneManager::createMetaTriangleSelector() } - //! Creates a triangle selector which can select triangles from a terrain scene node ITriangleSelector* CSceneManager::createTerrainTriangleSelector( ITerrainSceneNode* node, s32 LOD) @@ -1882,6 +1950,7 @@ ISceneNode* CSceneManager::getSceneNodeFromType(scene::ESCENE_NODE_TYPE type, IS return 0; } + //! returns scene nodes by type. void CSceneManager::getSceneNodesFromType(ESCENE_NODE_TYPE type, core::array& outNodes, ISceneNode* start) { @@ -1947,7 +2016,6 @@ E_SCENE_NODE_RENDER_PASS CSceneManager::getSceneNodeRenderPass() const } - //! Returns an interface to the mesh cache which is shared beween all existing scene managers. IMeshCache* CSceneManager::getMeshCache() { @@ -1995,7 +2063,7 @@ u32 CSceneManager::getRegisteredSceneNodeFactoryCount() const //! Returns a scene node factory by index ISceneNodeFactory* CSceneManager::getSceneNodeFactory(u32 index) { - if (indexcreateAndOpenFile(filename); - if (!read) + + io::IReadFile* file = FileSystem->createAndOpenFile(filename); + + if (!file) { - os::Printer::log("Unable to open scene file", filename, ELL_ERROR); - } - else - { - ret = loadScene(read, userDataSerializer, node); - read->drop(); + os::Printer::log("Unable to open scene file", filename.c_str(), ELL_ERROR); + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return false; } + // try scene loaders in reverse order + s32 i = SceneLoaderList.size()-1; + for (; i >= 0 && !ret; --i) + if (SceneLoaderList[i]->isALoadableFileExtension(filename)) + ret = SceneLoaderList[i]->loadScene(file, userDataSerializer, rootNode); + + if (!ret) + os::Printer::log("Could not load scene file, perhaps the format is unsupported: ", filename.c_str(), ELL_ERROR); + + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return ret; } //! Loads a scene. Note that the current scene is not cleared before. -bool CSceneManager::loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer, ISceneNode* node) +bool CSceneManager::loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer, ISceneNode* rootNode) { if (!file) { @@ -2109,256 +2185,24 @@ bool CSceneManager::loadScene(io::IReadFile* file, ISceneUserDataSerializer* use return false; } - io::IXMLReader* reader = FileSystem->createXMLReader(file); - if (!reader) - { - os::Printer::log("Scene is not a valid XML file", file->getFileName(), ELL_ERROR); - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return false; - } + bool ret = false; - // for mesh loading, set collada loading attributes + // try scene loaders in reverse order + s32 i = SceneLoaderList.size()-1; + for (; i >= 0 && !ret; --i) + if (SceneLoaderList[i]->isALoadableFileFormat(file)) + ret = SceneLoaderList[i]->loadScene(file, userDataSerializer, rootNode); - bool oldColladaSingleMesh = getParameters()->getAttributeAsBool(COLLADA_CREATE_SCENE_INSTANCES); - getParameters()->setAttribute(COLLADA_CREATE_SCENE_INSTANCES, false); + if (!ret) + os::Printer::log("Could not load scene file, perhaps the format is unsupported: ", file->getFileName().c_str(), ELL_ERROR); - // read file - - while(reader->read()) - { - readSceneNode(reader, node, userDataSerializer); - } - - // restore old collada parameters - - getParameters()->setAttribute(COLLADA_CREATE_SCENE_INSTANCES, oldColladaSingleMesh); - - // finish up - - reader->drop(); - return true; + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return ret; } - -//! reads a scene node -void CSceneManager::readSceneNode(io::IXMLReader* reader, ISceneNode* parent, ISceneUserDataSerializer* userDataSerializer) -{ - if (!reader) - return; - - scene::ISceneNode* node = 0; - - bool readAttributes=true; - if (IRR_XML_FORMAT_SCENE==reader->getNodeName()) - { - // node==parent on start, which can be scene manager or distinct parent node - if (!parent) - node = this; // root - else - node = parent; - readAttributes = (node==this); - } - else if (parent && IRR_XML_FORMAT_NODE==reader->getNodeName()) - { - // find node type and create it - core::stringc attrName = reader->getAttributeValue(IRR_XML_FORMAT_NODE_ATTR_TYPE.c_str()); - - for (s32 i=(s32)SceneNodeFactoryList.size()-1; i>=0 && !node; --i) - node = SceneNodeFactoryList[i]->addSceneNode(attrName.c_str(), parent); - - if (!node) - { - os::Printer::log("Could not create scene node of unknown type", attrName.c_str()); - node=addEmptySceneNode(parent); - } - } - - // read attributes - while(reader->read()) - { - bool endreached = false; - - switch (reader->getNodeType()) - { - case io::EXN_ELEMENT_END: - if ((IRR_XML_FORMAT_NODE==reader->getNodeName()) || - (IRR_XML_FORMAT_SCENE==reader->getNodeName())) - { - endreached = true; - } - break; - case io::EXN_ELEMENT: - if ((core::stringw(L"attributes")==reader->getNodeName()) && readAttributes) - { - // read attributes - io::IAttributes* attr = FileSystem->createEmptyAttributes(Driver); - attr->read(reader, true); - - if (node) - node->deserializeAttributes(attr); - - attr->drop(); - } - else - if ((core::stringw(L"materials")==reader->getNodeName()) && readAttributes) - readMaterials(reader, node); - else - if ((core::stringw(L"animators")==reader->getNodeName()) && readAttributes) - readAnimators(reader, node); - else - if ((core::stringw(L"userData")==reader->getNodeName()) && readAttributes) - readUserData(reader, node, userDataSerializer); - else - if (IRR_XML_FORMAT_NODE==reader->getNodeName()) - { - readSceneNode(reader, node, userDataSerializer); - } - else - if (IRR_XML_FORMAT_SCENE==reader->getNodeName()) - { - // pass on parent value - readSceneNode(reader, parent, userDataSerializer); - } - else - { - os::Printer::log("Found unknown element in irrlicht scene file", - core::stringc(reader->getNodeName()).c_str()); - } - break; - default: - break; - } - - if (endreached) - break; - } - if ( node && userDataSerializer ) - userDataSerializer->OnCreateNode(node); -} - - -//! reads materials of a node -void CSceneManager::readMaterials(io::IXMLReader* reader, ISceneNode* node) -{ - u32 nr = 0; - - while(reader->read()) - { - const wchar_t* name = reader->getNodeName(); - - switch(reader->getNodeType()) - { - case io::EXN_ELEMENT_END: - if (core::stringw(L"materials")==name) - return; - break; - case io::EXN_ELEMENT: - if (core::stringw(L"attributes")==name) - { - // read materials from attribute list - io::IAttributes* attr = FileSystem->createEmptyAttributes(Driver); - attr->read(reader); - - if (node && node->getMaterialCount() > nr) - { - getVideoDriver()->fillMaterialStructureFromAttributes( - node->getMaterial(nr), attr); - } - - attr->drop(); - ++nr; - } - break; - default: - break; - } - } -} - - -//! reads animators of a node -void CSceneManager::readAnimators(io::IXMLReader* reader, ISceneNode* node) -{ - while(reader->read()) - { - const wchar_t* name = reader->getNodeName(); - - switch(reader->getNodeType()) - { - case io::EXN_ELEMENT_END: - if (core::stringw(L"animators")==name) - return; - break; - case io::EXN_ELEMENT: - if (core::stringw(L"attributes")==name) - { - // read animator data from attribute list - io::IAttributes* attr = FileSystem->createEmptyAttributes(Driver); - attr->read(reader); - - if (node) - { - core::stringc typeName = attr->getAttributeAsString("Type"); - ISceneNodeAnimator* anim = 0; - - for (int i=0; i<(int)SceneNodeAnimatorFactoryList.size() && !anim; ++i) - anim = SceneNodeAnimatorFactoryList[i]->createSceneNodeAnimator(typeName.c_str(), node); - - if (anim) - { - anim->deserializeAttributes(attr); - anim->drop(); - } - } - - attr->drop(); - } - break; - default: - break; - } - } -} - - -//! reads user data of a node -void CSceneManager::readUserData(io::IXMLReader* reader, ISceneNode* node, ISceneUserDataSerializer* userDataSerializer) -{ - while(reader->read()) - { - const wchar_t* name = reader->getNodeName(); - - switch(reader->getNodeType()) - { - case io::EXN_ELEMENT_END: - if (core::stringw(L"userData")==name) - return; - break; - case io::EXN_ELEMENT: - if (core::stringw(L"attributes")==name) - { - // read user data from attribute list - io::IAttributes* attr = FileSystem->createEmptyAttributes(Driver); - attr->read(reader); - - if (node && userDataSerializer) - { - userDataSerializer->OnReadUserData(node, attr); - } - - attr->drop(); - } - break; - default: - break; - } - } -} - - //! writes a scene node void CSceneManager::writeSceneNode(io::IXMLWriter* writer, ISceneNode* node, ISceneUserDataSerializer* userDataSerializer, - const c8* currentPath, bool init) + const fschar_t* currentPath, bool init) { if (!writer || !node || node->isDebugObject()) return; @@ -2410,7 +2254,7 @@ void CSceneManager::writeSceneNode(io::IXMLWriter* writer, ISceneNode* node, ISc for (u32 i=0; i < node->getMaterialCount(); ++i) { io::IAttributes* tmp_attr = - getVideoDriver()->createAttributesFromMaterial(node->getMaterial(i)); + getVideoDriver()->createAttributesFromMaterial(node->getMaterial(i), &options); tmp_attr->write(writer); tmp_attr->drop(); } @@ -2444,7 +2288,7 @@ void CSceneManager::writeSceneNode(io::IXMLWriter* writer, ISceneNode* node, ISc // write possible user data - if ( userDataSerializer ) + if (userDataSerializer) { io::IAttributes* userData = userDataSerializer->createUserData(node); if (userData) @@ -2494,7 +2338,7 @@ const c8* CSceneManager::getSceneNodeTypeName(ESCENE_NODE_TYPE type) { const char* name = 0; - for (int i=(int)SceneNodeFactoryList.size()-1; !name && i>=0; --i) + for (s32 i=(s32)SceneNodeFactoryList.size()-1; !name && i>=0; --i) name = SceneNodeFactoryList[i]->getCreateableSceneNodeTypeName(type); return name; @@ -2505,19 +2349,29 @@ ISceneNode* CSceneManager::addSceneNode(const char* sceneNodeTypeName, ISceneNod { ISceneNode* node = 0; - for (int i=(int)SceneNodeFactoryList.size()-1; i>=0 && !node; --i) + for (s32 i=(s32)SceneNodeFactoryList.size()-1; i>=0 && !node; --i) node = SceneNodeFactoryList[i]->addSceneNode(sceneNodeTypeName, parent); return node; } +ISceneNodeAnimator* CSceneManager::createSceneNodeAnimator(const char* typeName, ISceneNode* target) +{ + ISceneNodeAnimator *animator = 0; + + for (s32 i=(s32)SceneNodeAnimatorFactoryList.size()-1; i>=0 && !animator; --i) + animator = SceneNodeAnimatorFactoryList[i]->createSceneNodeAnimator(typeName, target); + + return animator; +} + //! Returns a typename from a scene node animator type or null if not found const c8* CSceneManager::getAnimatorTypeName(ESCENE_NODE_ANIMATOR_TYPE type) { const char* name = 0; - for (u32 i=0; !name && i= 0; --i) name = SceneNodeAnimatorFactoryList[i]->getCreateableSceneNodeAnimatorTypeName(type); return name; diff --git a/source/Irrlicht/CSceneManager.h b/source/Irrlicht/CSceneManager.h index 3fb8833c..7d34ada8 100644 --- a/source/Irrlicht/CSceneManager.h +++ b/source/Irrlicht/CSceneManager.h @@ -371,6 +371,21 @@ namespace scene //! Adds an external mesh loader. virtual void addExternalMeshLoader(IMeshLoader* externalLoader); + //! Returns the number of mesh loaders supported by Irrlicht at this time + virtual u32 getMeshLoaderCount() const; + + //! Retrieve the given mesh loader + virtual IMeshLoader* getMeshLoader(u32 index) const; + + //! Adds an external scene loader. + virtual void addExternalSceneLoader(ISceneLoader* externalLoader); + + //! Returns the number of scene loaders supported by Irrlicht at this time + virtual u32 getSceneLoaderCount() const; + + //! Retrieve the given scene loader + virtual ISceneLoader* getSceneLoader(u32 index) const; + //! Returns a pointer to the scene collision manager. virtual ISceneCollisionManager* getSceneCollisionManager(); @@ -443,6 +458,9 @@ namespace scene //! Adds a scene node to the scene by name virtual ISceneNode* addSceneNode(const char* sceneNodeTypeName, ISceneNode* parent=0); + //! creates a scene node animator based on its type name + virtual ISceneNodeAnimator* createSceneNodeAnimator(const char* typeName, ISceneNode* target=0); + //! Returns the default scene node animator factory which can create all built-in scene node animators virtual ISceneNodeAnimatorFactory* getDefaultSceneNodeAnimatorFactory(); @@ -456,18 +474,16 @@ namespace scene virtual ISceneNodeAnimatorFactory* getSceneNodeAnimatorFactory(u32 index); //! Saves the current scene into a file. - //! \param filename: Name of the file . virtual bool saveScene(const io::path& filename, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* node=0); //! Saves the current scene into a file. virtual bool saveScene(io::IWriteFile* file, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* node=0); //! Loads a scene. Note that the current scene is not cleared before. - //! \param filename: Name of the file . - virtual bool loadScene(const io::path& filename, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* node=0); + virtual bool loadScene(const io::path& filename, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* rootNode=0); //! Loads a scene. Note that the current scene is not cleared before. - virtual bool loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* node=0); + virtual bool loadScene(io::IReadFile* file, ISceneUserDataSerializer* userDataSerializer=0, ISceneNode* rootNode=0); //! Writes attributes of the scene node. virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const; @@ -502,19 +518,7 @@ namespace scene void clearDeletionList(); //! writes a scene node - void writeSceneNode(io::IXMLWriter* writer, ISceneNode* node, ISceneUserDataSerializer* userDataSerializer, const c8* currentPath=0, bool init=false); - - //! reads a scene node - void readSceneNode(io::IXMLReader* reader, ISceneNode* parent, ISceneUserDataSerializer* userDataSerializer); - - //! read materials - void readMaterials(io::IXMLReader* reader, ISceneNode* node); - - //! reads animators of a node - void readAnimators(io::IXMLReader* reader, ISceneNode* node); - - //! reads user data of a node - void readUserData(io::IXMLReader* reader, ISceneNode* node, ISceneUserDataSerializer* userDataSerializer); + void writeSceneNode(io::IXMLWriter* writer, ISceneNode* node, ISceneUserDataSerializer* userDataSerializer, const fschar_t* currentPath=0, bool init=false); struct DefaultNodeEntry { @@ -597,7 +601,7 @@ namespace scene //! render pass lists core::array CameraList; - core::array LightList; + core::array LightList; core::array ShadowNodeList; core::array SkyBoxList; core::array SolidNodeList; @@ -605,6 +609,7 @@ namespace scene core::array TransparentEffectNodeList; core::array MeshLoaderList; + core::array SceneLoaderList; core::array DeletionList; core::array SceneNodeFactoryList; core::array SceneNodeAnimatorFactoryList; diff --git a/source/Irrlicht/CSoftwareDriver2.cpp b/source/Irrlicht/CSoftwareDriver2.cpp index 7139d2b6..6d31752d 100644 --- a/source/Irrlicht/CSoftwareDriver2.cpp +++ b/source/Irrlicht/CSoftwareDriver2.cpp @@ -12,6 +12,7 @@ #include "CSoftware2MaterialRenderer.h" #include "S3DVertex.h" #include "S4DVertex.h" +#include "CBlit.h" #define MAT_TEXTURE(tex) ( (video::CSoftwareTexture2*) Material.org.getTexture ( tex ) ) @@ -2247,6 +2248,28 @@ void CBurningVideoDriver::draw2DImage(const video::ITexture* texture, const core } +//! Draws a part of the texture into the rectangle. +void CBurningVideoDriver::draw2DImage(const video::ITexture* texture, const core::rect& destRect, + const core::rect& sourceRect, const core::rect* clipRect, + const video::SColor* const colors, bool useAlphaChannelOfTexture) +{ + if (texture) + { + if (texture->getDriverType() != EDT_BURNINGSVIDEO) + { + os::Printer::log("Fatal Error: Tried to copy from a surface not owned by this driver.", ELL_ERROR); + return; + } + + if (useAlphaChannelOfTexture) + StretchBlit(BLITTER_TEXTURE_ALPHA_BLEND, RenderTargetSurface, &destRect, &sourceRect, + ((CSoftwareTexture2*)texture)->getImage(), (colors ? colors[0].color : 0)); + else + StretchBlit(BLITTER_TEXTURE, RenderTargetSurface, &destRect, &sourceRect, + ((CSoftwareTexture2*)texture)->getImage(), (colors ? colors[0].color : 0)); + } +} + //! Draws a 2d line. void CBurningVideoDriver::draw2DLine(const core::position2d& start, const core::position2d& end, diff --git a/source/Irrlicht/CSoftwareDriver2.h b/source/Irrlicht/CSoftwareDriver2.h index d885a41f..5f9d8898 100644 --- a/source/Irrlicht/CSoftwareDriver2.h +++ b/source/Irrlicht/CSoftwareDriver2.h @@ -89,6 +89,11 @@ namespace video const core::rect& sourceRect, const core::rect* clipRect = 0, SColor color=SColor(255,255,255,255), bool useAlphaChannelOfTexture=false); + //! Draws a part of the texture into the rectangle. + virtual void draw2DImage(const video::ITexture* texture, const core::rect& destRect, + const core::rect& sourceRect, const core::rect* clipRect = 0, + const video::SColor* const colors=0, bool useAlphaChannelOfTexture=false); + //! Draws a 3d line. virtual void draw3DLine(const core::vector3df& start, const core::vector3df& end, SColor color = SColor(255,255,255,255)); diff --git a/source/Irrlicht/CTerrainSceneNode.cpp b/source/Irrlicht/CTerrainSceneNode.cpp index 45f7d664..1597efe2 100644 --- a/source/Irrlicht/CTerrainSceneNode.cpp +++ b/source/Irrlicht/CTerrainSceneNode.cpp @@ -37,10 +37,7 @@ namespace scene : ITerrainSceneNode(parent, mgr, id, position, rotation, scale), TerrainData(patchSize, maxLOD, position, rotation, scale), RenderBuffer(0), VerticesToRender(0), IndicesToRender(0), DynamicSelectorUpdate(false), - OverrideDistanceThreshold(false), UseDefaultRotationPivot(true), ForceRecalculation(false), - OldCameraPosition(core::vector3df(-99999.9f, -99999.9f, -99999.9f)), - OldCameraRotation(core::vector3df(-99999.9f, -99999.9f, -99999.9f)), - OldCameraUp(core::vector3df(-99999.9f, -99999.9f, -99999.9f)), + OverrideDistanceThreshold(false), UseDefaultRotationPivot(true), ForceRecalculation(true), CameraMovementDelta(10.0f), CameraRotationDelta(1.0f),CameraFOVDelta(0.1f), TCoordScale1(1.0f), TCoordScale2(1.0f), FileSystem(fs) { @@ -168,7 +165,7 @@ namespace scene vertex.Normal.set(0.0f, 1.0f, 0.0f); vertex.Color = vertexColor; vertex.Pos.X = fx; - vertex.Pos.Y = (f32) heightMap->getPixel(TerrainData.Size-x-1,z).getLuminance(); + vertex.Pos.Y = (f32) heightMap->getPixel(TerrainData.Size-x-1,z).getLightness(); vertex.Pos.Z = fz; vertex.TCoords.X = vertex.TCoords2.X = 1.f-fx2; @@ -539,11 +536,10 @@ namespace scene if (!Mesh->getMeshBufferCount()) return; - TerrainData.Position = TerrainData.Position; - s32 vtxCount = Mesh->getMeshBuffer(0)->getVertexCount(); core::matrix4 rotMatrix; rotMatrix.setRotationDegrees(TerrainData.Rotation); + const s32 vtxCount = Mesh->getMeshBuffer(0)->getVertexCount(); for (s32 i = 0; i < vtxCount; ++i) { RenderBuffer->getVertexBuffer()[i].Pos = Mesh->getMeshBuffer(0)->getPosition(i) * TerrainData.Scale + TerrainData.Position; @@ -1274,54 +1270,57 @@ namespace scene void CTerrainSceneNode::calculatePatchData() { // Reset the Terrains Bounding Box for re-calculation - TerrainData.BoundingBox = core::aabbox3df(999999.9f, 999999.9f, 999999.9f, -999999.9f, -999999.9f, -999999.9f); + TerrainData.BoundingBox.reset(RenderBuffer->getPosition(0)); for (s32 x = 0; x < TerrainData.PatchCount; ++x) { for (s32 z = 0; z < TerrainData.PatchCount; ++z) { const s32 index = x * TerrainData.PatchCount + z; - TerrainData.Patches[index].CurrentLOD = 0; + SPatch& patch = TerrainData.Patches[index]; + patch.CurrentLOD = 0; + const s32 xstart = x*TerrainData.CalcPatchSize; + const s32 xend = xstart+TerrainData.CalcPatchSize; + const s32 zstart = z*TerrainData.CalcPatchSize; + const s32 zend = zstart+TerrainData.CalcPatchSize; // For each patch, calculate the bounding box (mins and maxes) - TerrainData.Patches[index].BoundingBox = core::aabbox3df(999999.9f, 999999.9f, 999999.9f, - -999999.9f, -999999.9f, -999999.9f); - - for (s32 xx = x*(TerrainData.CalcPatchSize); xx <= (x + 1) * TerrainData.CalcPatchSize; ++xx) - for (s32 zz = z*(TerrainData.CalcPatchSize); zz <= (z + 1) * TerrainData.CalcPatchSize; ++zz) - TerrainData.Patches[index].BoundingBox.addInternalPoint(RenderBuffer->getVertexBuffer()[xx * TerrainData.Size + zz].Pos); + patch.BoundingBox.reset(RenderBuffer->getPosition(xstart*TerrainData.Size + zstart)); + for (s32 xx = xstart; xx <= xend; ++xx) + for (s32 zz = zstart; zz <= zend; ++zz) + patch.BoundingBox.addInternalPoint(RenderBuffer->getVertexBuffer()[xx * TerrainData.Size + zz].Pos); // Reconfigure the bounding box of the terrain as a whole - TerrainData.BoundingBox.addInternalBox(TerrainData.Patches[index].BoundingBox); + TerrainData.BoundingBox.addInternalBox(patch.BoundingBox); // get center of Patch - TerrainData.Patches[index].Center = TerrainData.Patches[index].BoundingBox.getCenter(); + patch.Center = patch.BoundingBox.getCenter(); // Assign Neighbours // Top if (x > 0) - TerrainData.Patches[index].Top = &TerrainData.Patches[(x-1) * TerrainData.PatchCount + z]; + patch.Top = &TerrainData.Patches[(x-1) * TerrainData.PatchCount + z]; else - TerrainData.Patches[index].Top = 0; + patch.Top = 0; // Bottom if (x < TerrainData.PatchCount - 1) - TerrainData.Patches[index].Bottom = &TerrainData.Patches[(x+1) * TerrainData.PatchCount + z]; + patch.Bottom = &TerrainData.Patches[(x+1) * TerrainData.PatchCount + z]; else - TerrainData.Patches[index].Bottom = 0; + patch.Bottom = 0; // Left if (z > 0) - TerrainData.Patches[index].Left = &TerrainData.Patches[x * TerrainData.PatchCount + z - 1]; + patch.Left = &TerrainData.Patches[x * TerrainData.PatchCount + z - 1]; else - TerrainData.Patches[index].Left = 0; + patch.Left = 0; // Right if (z < TerrainData.PatchCount - 1) - TerrainData.Patches[index].Right = &TerrainData.Patches[x * TerrainData.PatchCount + z + 1]; + patch.Right = &TerrainData.Patches[x * TerrainData.PatchCount + z + 1]; else - TerrainData.Patches[index].Right = 0; + patch.Right = 0; } } @@ -1378,8 +1377,6 @@ namespace scene if (!Mesh->getMeshBufferCount()) return 0; - f32 height = -999999.9f; - core::matrix4 rotMatrix; rotMatrix.setRotationDegrees(TerrainData.Rotation); core::vector3df pos(x, 0.0f, z); @@ -1390,6 +1387,7 @@ namespace scene s32 X(core::floor32(pos.X)); s32 Z(core::floor32(pos.Z)); + f32 height = -FLT_MAX; if (X >= 0 && X < TerrainData.Size-1 && Z >= 0 && Z < TerrainData.Size-1) { diff --git a/source/Irrlicht/CTerrainSceneNode.h b/source/Irrlicht/CTerrainSceneNode.h index 38bf66c4..a8c2c466 100644 --- a/source/Irrlicht/CTerrainSceneNode.h +++ b/source/Irrlicht/CTerrainSceneNode.h @@ -217,44 +217,34 @@ namespace scene ISceneManager* newManager); private: - friend class CTerrainTriangleSelector; struct SPatch { SPatch() - : CurrentLOD(-1), Top(0), Bottom(0), Right(0), Left(0) + : Top(0), Bottom(0), Right(0), Left(0), CurrentLOD(-1) { } - s32 CurrentLOD; - core::aabbox3df BoundingBox; - core::vector3df Center; - SPatch* Top; - SPatch* Bottom; - SPatch* Right; - SPatch* Left; + SPatch* Top; + SPatch* Bottom; + SPatch* Right; + SPatch* Left; + s32 CurrentLOD; + core::aabbox3df BoundingBox; + core::vector3df Center; }; struct STerrainData { - STerrainData() - : Size(0), PatchSize(0), CalcPatchSize(0), - PatchCount(0), MaxLOD(0), - BoundingBox(core::aabbox3df( 99999.9f, 99999.9f, 99999.9f, -99999.9f, -99999.9f, -99999.9f)), - Patches(0) - { - } - STerrainData(s32 patchSize, s32 maxLOD, const core::vector3df& position, const core::vector3df& rotation, const core::vector3df& scale) - : Size(0), Position(position), Rotation(rotation), Scale(scale), - PatchSize(patchSize), CalcPatchSize(patchSize-1), - PatchCount(0), MaxLOD(maxLOD), - BoundingBox(core::aabbox3df( 99999.9f, 99999.9f, 99999.9f, -99999.9f, -99999.9f, -99999.9f)), - Patches(0) + : Patches(0), Size(0), Position(position), Rotation(rotation), + Scale(scale), PatchSize(patchSize), CalcPatchSize(patchSize-1), + PatchCount(0), MaxLOD(maxLOD) { } + SPatch* Patches; s32 Size; core::vector3df Position; core::vector3df Rotation; @@ -267,7 +257,6 @@ namespace scene s32 MaxLOD; core::aabbox3df BoundingBox; core::array LODDistanceThreshold; - SPatch* Patches; }; virtual void preRenderLODCalculations(); diff --git a/source/Irrlicht/CTerrainTriangleSelector.cpp b/source/Irrlicht/CTerrainTriangleSelector.cpp index 72ab9951..d3dae80e 100644 --- a/source/Irrlicht/CTerrainTriangleSelector.cpp +++ b/source/Irrlicht/CTerrainTriangleSelector.cpp @@ -34,11 +34,8 @@ CTerrainTriangleSelector::~CTerrainTriangleSelector() //! Clears and sets triangle data void CTerrainTriangleSelector::setTriangleData(ITerrainSceneNode* node, s32 LOD) { - core::triangle3df tri; - core::array indices; - // Get pointer to the GeoMipMaps vertices - video::S3DVertex2TCoords* vertices = static_cast(node->getRenderBuffer()->getVertices()); + const video::S3DVertex2TCoords* vertices = static_cast(node->getRenderBuffer()->getVertices()); // Clear current data const s32 count = (static_cast(node))->TerrainData.PatchCount; @@ -49,6 +46,8 @@ void CTerrainTriangleSelector::setTriangleData(ITerrainSceneNode* node, s32 LOD) for (s32 o=0; o indices; s32 tIndex = 0; for(s32 x = 0; x < count; ++x ) { @@ -74,9 +73,11 @@ void CTerrainTriangleSelector::setTriangleData(ITerrainSceneNode* node, s32 LOD) } } + //! Gets all triangles. -void CTerrainTriangleSelector::getTriangles ( core::triangle3df* triangles, s32 arraySize, - s32& outTriangleCount, const core::matrix4* transform) const +void CTerrainTriangleSelector::getTriangles(core::triangle3df* triangles, + s32 arraySize, s32& outTriangleCount, + const core::matrix4* transform) const { s32 count = TrianglePatches.TotalTriangles; @@ -110,9 +111,9 @@ void CTerrainTriangleSelector::getTriangles ( core::triangle3df* triangles, s32 //! Gets all triangles which lie within a specific bounding box. -void CTerrainTriangleSelector::getTriangles ( core::triangle3df* triangles, s32 arraySize, - s32& outTriangleCount, const core::aabbox3d& box, - const core::matrix4* transform) const +void CTerrainTriangleSelector::getTriangles(core::triangle3df* triangles, + s32 arraySize, s32& outTriangleCount, + const core::aabbox3d& box, const core::matrix4* transform) const { s32 count = TrianglePatches.TotalTriangles; @@ -145,10 +146,11 @@ void CTerrainTriangleSelector::getTriangles ( core::triangle3df* triangles, s32 outTriangleCount = tIndex; } + //! Gets all triangles which have or may have contact with a 3d line. -void CTerrainTriangleSelector::getTriangles(core::triangle3df* triangles, s32 arraySize, - s32& outTriangleCount, const core::line3d& line, - const core::matrix4* transform) const +void CTerrainTriangleSelector::getTriangles(core::triangle3df* triangles, + s32 arraySize, s32& outTriangleCount, const core::line3d& line, + const core::matrix4* transform) const { const s32 count = core::min_((s32)TrianglePatches.TotalTriangles, arraySize); @@ -180,17 +182,53 @@ void CTerrainTriangleSelector::getTriangles(core::triangle3df* triangles, s32 ar outTriangleCount = tIndex; } + //! Returns amount of all available triangles in this selector s32 CTerrainTriangleSelector::getTriangleCount() const { return TrianglePatches.TotalTriangles; } -ISceneNode* CTerrainTriangleSelector::getSceneNodeForTriangle(u32 triangleIndex) const + +ISceneNode* CTerrainTriangleSelector::getSceneNodeForTriangle( + u32 triangleIndex) const { return SceneNode; } + +/* Get the number of TriangleSelectors that are part of this one. +Only useful for MetaTriangleSelector others return 1 +*/ +u32 CTerrainTriangleSelector::getSelectorCount() const +{ + return 1; +} + + +/* Get the TriangleSelector based on index based on getSelectorCount. +Only useful for MetaTriangleSelector others return 'this' or 0 +*/ +ITriangleSelector* CTerrainTriangleSelector::getSelector(u32 index) +{ + if (index) + return 0; + else + return this; +} + + +/* Get the TriangleSelector based on index based on getSelectorCount. +Only useful for MetaTriangleSelector others return 'this' or 0 +*/ +const ITriangleSelector* CTerrainTriangleSelector::getSelector(u32 index) const +{ + if (index) + return 0; + else + return this; +} + + } // end namespace scene } // end namespace irr - diff --git a/source/Irrlicht/CTerrainTriangleSelector.h b/source/Irrlicht/CTerrainTriangleSelector.h index 03651e06..719a3704 100644 --- a/source/Irrlicht/CTerrainTriangleSelector.h +++ b/source/Irrlicht/CTerrainTriangleSelector.h @@ -20,14 +20,16 @@ namespace scene class ITerrainSceneNode; //! Triangle Selector for the TerrainSceneNode -//! The code for the TerrainTriangleSelector is based on the GeoMipMapSelector -//! developed by Spintz. He made it available for Irrlicht and allowed it to be -//! distributed under this licence. I only modified some parts. A lot of thanks go to him. +/** The code for the TerrainTriangleSelector is based on the GeoMipMapSelector +developed by Spintz. He made it available for Irrlicht and allowed it to be +distributed under this licence. I only modified some parts. A lot of thanks go +to him. +*/ class CTerrainTriangleSelector : public ITriangleSelector { public: - //! Constructs a selector based on an IGeoMipMapSceneNode + //! Constructs a selector based on an ITerrainSceneNode CTerrainTriangleSelector(ITerrainSceneNode* node, s32 LOD); //! Destructor @@ -55,15 +57,24 @@ public: //! Return the scene node associated with a given triangle. virtual ISceneNode* getSceneNodeForTriangle(u32 triangleIndex) const; + // Get the number of TriangleSelectors that are part of this one + virtual u32 getSelectorCount() const; + + // Get the TriangleSelector based on index based on getSelectorCount + virtual ITriangleSelector* getSelector(u32 index); + + // Get the TriangleSelector based on index based on getSelectorCount + virtual const ITriangleSelector* getSelector(u32 index) const; + private: friend class CTerrainSceneNode; struct SGeoMipMapTrianglePatch { - core::array Triangles; - s32 NumTriangles; - core::aabbox3df Box; + core::array Triangles; + s32 NumTriangles; + core::aabbox3df Box; }; struct SGeoMipMapTrianglePatches @@ -73,13 +84,13 @@ private: { } - core::array TrianglePatchArray; - s32 NumPatches; - u32 TotalTriangles; + core::array TrianglePatchArray; + s32 NumPatches; + u32 TotalTriangles; }; - ITerrainSceneNode* SceneNode; - SGeoMipMapTrianglePatches TrianglePatches; + ITerrainSceneNode* SceneNode; + SGeoMipMapTrianglePatches TrianglePatches; }; } // end namespace scene @@ -87,4 +98,3 @@ private: #endif // __C_TERRAIN_TRIANGLE_SELECTOR_H__ - diff --git a/source/Irrlicht/CTextSceneNode.cpp b/source/Irrlicht/CTextSceneNode.cpp index 183f6eef..67ab54e2 100644 --- a/source/Irrlicht/CTextSceneNode.cpp +++ b/source/Irrlicht/CTextSceneNode.cpp @@ -236,7 +236,7 @@ void CBillboardTextSceneNode::setText(const wchar_t* text) //! pre render event -void CBillboardTextSceneNode::OnRegisterSceneNode() +void CBillboardTextSceneNode::OnAnimate(u32 timeMs) { if (!IsVisible || !Font || !Mesh) return; @@ -256,7 +256,7 @@ void CBillboardTextSceneNode::OnRegisterSceneNode() if (textLength<0.0f) textLength=1.0f; -// const core::matrix4 &m = camera->getViewFrustum()->Matrices[ video::ETS_VIEW ]; + //const core::matrix4 &m = camera->getViewFrustum()->Matrices[ video::ETS_VIEW ]; // make billboard look to camera core::vector3df pos = getAbsolutePosition(); @@ -319,7 +319,10 @@ void CBillboardTextSceneNode::OnRegisterSceneNode() BBox = Mesh->getBoundingBox(); core::matrix4 mat( getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE ); mat.transformBoxEx(BBox); +} +void CBillboardTextSceneNode::OnRegisterSceneNode() +{ SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT); ISceneNode::OnRegisterSceneNode(); } diff --git a/source/Irrlicht/CTextSceneNode.h b/source/Irrlicht/CTextSceneNode.h index 666bd693..8674822f 100644 --- a/source/Irrlicht/CTextSceneNode.h +++ b/source/Irrlicht/CTextSceneNode.h @@ -44,7 +44,7 @@ namespace scene //! sets the color of the text virtual void setTextColor(video::SColor color); - + //! Returns type of the scene node virtual ESCENE_NODE_TYPE getType() const { return ESNT_TEXT; } @@ -61,7 +61,7 @@ namespace scene { public: - CBillboardTextSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, + CBillboardTextSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, gui::IGUIFont* font,const wchar_t* text, const core::vector3df& position, const core::dimension2d& size, video::SColor colorTop, video::SColor shade_bottom); @@ -69,6 +69,10 @@ namespace scene //! destructor virtual ~CBillboardTextSceneNode(); + //! sets the vertex positions etc + virtual void OnAnimate(u32 timeMs); + + //! registers the node into the transparent pass virtual void OnRegisterSceneNode(); //! renders the node. @@ -82,7 +86,7 @@ namespace scene //! sets the color of the text virtual void setTextColor(video::SColor color); - + //! sets the size of the billboard virtual void setSize(const core::dimension2d& size); @@ -90,7 +94,7 @@ namespace scene virtual const core::dimension2d& getSize() const; virtual video::SMaterial& getMaterial(u32 i); - + //! returns amount of materials used by this scene node. virtual u32 getMaterialCount() const; diff --git a/source/Irrlicht/CTriangleSelector.cpp b/source/Irrlicht/CTriangleSelector.cpp index b9f83e5b..f79e205c 100644 --- a/source/Irrlicht/CTriangleSelector.cpp +++ b/source/Irrlicht/CTriangleSelector.cpp @@ -231,6 +231,38 @@ s32 CTriangleSelector::getTriangleCount() const } +/* Get the number of TriangleSelectors that are part of this one. +Only useful for MetaTriangleSelector others return 1 +*/ +u32 CTriangleSelector::getSelectorCount() const +{ + return 1; +} + + +/* Get the TriangleSelector based on index based on getSelectorCount. +Only useful for MetaTriangleSelector others return 'this' or 0 +*/ +ITriangleSelector* CTriangleSelector::getSelector(u32 index) +{ + if (index) + return 0; + else + return this; +} + + +/* Get the TriangleSelector based on index based on getSelectorCount. +Only useful for MetaTriangleSelector others return 'this' or 0 +*/ +const ITriangleSelector* CTriangleSelector::getSelector(u32 index) const +{ + if (index) + return 0; + else + return this; +} + } // end namespace scene } // end namespace irr diff --git a/source/Irrlicht/CTriangleSelector.h b/source/Irrlicht/CTriangleSelector.h index 4a65c17a..5940df0d 100644 --- a/source/Irrlicht/CTriangleSelector.h +++ b/source/Irrlicht/CTriangleSelector.h @@ -54,6 +54,15 @@ public: //! Return the scene node associated with a given triangle. virtual ISceneNode* getSceneNodeForTriangle(u32 triangleIndex) const { return SceneNode; } + // Get the number of TriangleSelectors that are part of this one + virtual u32 getSelectorCount() const; + + // Get the TriangleSelector based on index based on getSelectorCount + virtual ITriangleSelector* getSelector(u32 index); + + // Get the TriangleSelector based on index based on getSelectorCount + virtual const ITriangleSelector* getSelector(u32 index) const; + protected: //! Create from a mesh virtual void createFromMesh(const IMesh* mesh); diff --git a/source/Irrlicht/CXMeshFileLoader.cpp b/source/Irrlicht/CXMeshFileLoader.cpp index 95ed89e8..6532d36b 100644 --- a/source/Irrlicht/CXMeshFileLoader.cpp +++ b/source/Irrlicht/CXMeshFileLoader.cpp @@ -423,14 +423,14 @@ bool CXMeshFileLoader::readFileIntoMemory(io::IReadFile* file) //! read minor and major version, e.g. 0302 or 0303 c8 tmp[3]; - tmp[2] = 0x0; tmp[0] = Buffer[4]; tmp[1] = Buffer[5]; - MajorVersion = core::strtol10(tmp); + tmp[2] = 0x0; + MajorVersion = core::strtoul10(tmp); tmp[0] = Buffer[6]; tmp[1] = Buffer[7]; - MinorVersion = core::strtol10(tmp); + MinorVersion = core::strtoul10(tmp); //! read format if (strncmp(&Buffer[8], "txt ", 4) ==0) @@ -2307,7 +2307,7 @@ u32 CXMeshFileLoader::readInt() else { findNextNoneWhiteSpaceNumber(); - return core::strtol10(P, &P); + return core::strtoul10(P, &P); } } diff --git a/source/Irrlicht/IAttribute.h b/source/Irrlicht/IAttribute.h index 84f3d25a..a7713865 100644 --- a/source/Irrlicht/IAttribute.h +++ b/source/Irrlicht/IAttribute.h @@ -92,7 +92,7 @@ public: virtual void setUserPointer(void* v) {}; virtual void setEnum(const char* enumValue, const char* const* enumerationLiterals) {}; - virtual void setTexture(video::ITexture*) {}; + virtual void setTexture(video::ITexture*, const path& filename) {}; core::stringc Name; diff --git a/source/Irrlicht/Irrlicht-gcc.cbp b/source/Irrlicht/Irrlicht-gcc.cbp index efcbfe71..30dad9ae 100644 --- a/source/Irrlicht/Irrlicht-gcc.cbp +++ b/source/Irrlicht/Irrlicht-gcc.cbp @@ -527,6 +527,7 @@ + @@ -889,12 +890,16 @@ + + + + diff --git a/source/Irrlicht/Irrlicht10.0.vcxproj b/source/Irrlicht/Irrlicht10.0.vcxproj index 1434a800..9bbc5007 100644 --- a/source/Irrlicht/Irrlicht10.0.vcxproj +++ b/source/Irrlicht/Irrlicht10.0.vcxproj @@ -290,7 +290,13 @@ $(DXSDK_DIR)include;$(IncludePath) $(DXSDK_DIR)include;$(IncludePath) C:\Imagination Technologies\POWERVR SDK\OGLES2_WINDOWS_PCEMULATION_2.07.27.0484\Builds\OGLES2\WindowsPC\Lib;$(ExecutablePath) - C:\Imagination Technologies\POWERVR SDK\OGLES2_WINDOWS_PCEMULATION_2.07.27.0484\Builds\OGLES2\WindowsPC\Lib;$(LibraryPath) + C:\Imagination Technologies\POWERVR SDK\OGLES2_WINDOWS_PCEMULATION_2.07.27.0484\Builds\OGLES2\WindowsPC\Lib;$(DXSDK_DIR)Lib\x86;$(LibraryPath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) @@ -321,7 +327,7 @@ /MACHINE:I386 %(AdditionalOptions) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;winmm.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;%(AdditionalDependencies) ..\..\bin\Win32-visualstudio\Irrlicht.dll %(AdditionalLibraryDirectories) libci.lib;%(IgnoreSpecificDefaultLibraries) @@ -399,7 +405,7 @@ /MACHINE:I386 %(AdditionalOptions) - kernel32.lib;user32.lib;gdi32.lib;opengl32.lib;winmm.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;opengl32.lib;%(AdditionalDependencies) ..\..\bin\Win32-visualstudio\Irrlicht.dll libci.lib;%(IgnoreSpecificDefaultLibraries) false @@ -480,7 +486,7 @@ /MACHINE:I386 %(AdditionalOptions) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;opengl32.lib;winmm.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;opengl32.lib;%(AdditionalDependencies) ..\..\bin\Win32-visualstudio\Irrlicht.dll libci.lib;%(IgnoreSpecificDefaultLibraries) false @@ -697,6 +703,7 @@ winmm.lib;%(AdditionalDependencies) ..\..\lib\Win32-visualstudio\Irrlicht.lib + MachineX86 @@ -766,7 +773,7 @@ /MACHINE:I386 %(AdditionalOptions) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;opengl32.lib;winmm.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;opengl32.lib;%(AdditionalDependencies) ..\..\bin\Win32-visualstudio\Irrlicht.dll %(AdditionalLibraryDirectories) libci.lib;%(IgnoreSpecificDefaultLibraries) @@ -815,6 +822,7 @@ + @@ -918,6 +926,7 @@ + @@ -988,6 +997,7 @@ + @@ -1052,6 +1062,7 @@ + @@ -1244,6 +1255,7 @@ + @@ -1305,6 +1317,7 @@ + @@ -1547,4 +1560,4 @@ - \ No newline at end of file + diff --git a/source/Irrlicht/Irrlicht10.0.vcxproj.filters b/source/Irrlicht/Irrlicht10.0.vcxproj.filters index 29abd184..0a8f6e05 100644 --- a/source/Irrlicht/Irrlicht10.0.vcxproj.filters +++ b/source/Irrlicht/Irrlicht10.0.vcxproj.filters @@ -1327,6 +1327,18 @@ include\video + + Irrlicht\scene\loaders + + + include\scene + + + Irrlicht\scene\loaders + + + include\video + @@ -2279,8 +2291,14 @@ Irrlicht\video\OpenGL-ES 2.x + + Irrlicht\scene\loaders + + + Irrlicht\scene\loaders + - \ No newline at end of file + diff --git a/source/Irrlicht/Irrlicht7.1.vcproj b/source/Irrlicht/Irrlicht7.1.vcproj index 28fc05e6..7fff6276 100644 --- a/source/Irrlicht/Irrlicht7.1.vcproj +++ b/source/Irrlicht/Irrlicht7.1.vcproj @@ -674,6 +674,9 @@ + + @@ -1470,6 +1473,12 @@ + + + + @@ -1487,6 +1496,12 @@ + + + + diff --git a/source/Irrlicht/Irrlicht8.0.vcproj b/source/Irrlicht/Irrlicht8.0.vcproj index 3215ef37..4dc60f24 100644 --- a/source/Irrlicht/Irrlicht8.0.vcproj +++ b/source/Irrlicht/Irrlicht8.0.vcproj @@ -945,6 +945,11 @@ RelativePath=".\..\..\include\ISceneManager.h" > + + + @@ -2091,11 +2096,19 @@ > + + + + + + + + diff --git a/source/Irrlicht/Irrlicht9.0.vcproj b/source/Irrlicht/Irrlicht9.0.vcproj index 80845205..05d2315f 100644 --- a/source/Irrlicht/Irrlicht9.0.vcproj +++ b/source/Irrlicht/Irrlicht9.0.vcproj @@ -79,7 +79,7 @@ Name="VCLinkerTool" UseLibraryDependencyInputs="true" AdditionalOptions="/MACHINE:I386" - AdditionalDependencies="kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib opengl32.lib winmm.lib" + AdditionalDependencies="kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib opengl32.lib" OutputFile="..\..\bin\Win32-visualstudio\Irrlicht.dll" LinkIncremental="2" SuppressStartupBanner="true" @@ -1112,6 +1112,10 @@ RelativePath="..\..\include\ISceneCollisionManager.h" > + + @@ -1380,6 +1384,14 @@ RelativePath="CMeshManipulator.h" > + + + + @@ -1575,6 +1587,14 @@ RelativePath="CSkinnedMesh.h" > + + + + diff --git a/source/Irrlicht/Irrlicht_mobile6.vcproj b/source/Irrlicht/Irrlicht_mobile6.vcproj index 44bd8aed..5a400c0c 100644 --- a/source/Irrlicht/Irrlicht_mobile6.vcproj +++ b/source/Irrlicht/Irrlicht_mobile6.vcproj @@ -1592,6 +1592,7 @@ RelativePath="CTRTextureBlend.cpp" > + @@ -1740,6 +1741,14 @@ RelativePath="C3DSMeshFileLoader.h" > + + + + diff --git a/source/Irrlicht/Irrlicht_xbox.vcproj b/source/Irrlicht/Irrlicht_xbox.vcproj index 98b76c32..d92d0dfd 100644 --- a/source/Irrlicht/Irrlicht_xbox.vcproj +++ b/source/Irrlicht/Irrlicht_xbox.vcproj @@ -852,6 +852,12 @@ + + + + diff --git a/source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm b/source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm index fe869fcd..58c7c332 100644 --- a/source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm +++ b/source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm @@ -378,22 +378,18 @@ CIrrDeviceMacOSX::CIrrDeviceMacOSX(const SIrrlichtCreationParameters& param) CursorControl = new CCursorControl(CreationParams.WindowSize, this); createDriver(); - if (IsSoftwareRenderer && CreationParams.DriverType != video::EDT_NULL) - { - // create context for rendering raw bitmap - } - createGUIAndScene(); } CIrrDeviceMacOSX::~CIrrDeviceMacOSX() { + [SoftwareDriverTarget release]; SetSystemUIMode(kUIModeNormal, 0); closeDevice(); #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) - for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + for (u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) { - if(ActiveJoysticks[joystick].interface) + if (ActiveJoysticks[joystick].interface) closeJoystickDevice(&ActiveJoysticks[joystick]); } #endif @@ -450,7 +446,8 @@ bool CIrrDeviceMacOSX::createWindow() CFDictionaryRef displaymode, olddisplaymode; GLint numPixelFormats, newSwapInterval; - int alphaSize = CreationParams.WithAlphaChannel?4:0, depthSize = CreationParams.ZBufferBits; + int alphaSize = CreationParams.WithAlphaChannel?4:0; + int depthSize = CreationParams.ZBufferBits; if (CreationParams.WithAlphaChannel && (CreationParams.Bits == 32)) alphaSize = 8; @@ -583,15 +580,12 @@ bool CIrrDeviceMacOSX::createWindow() error = CGDisplaySwitchToMode(display,displaymode); if (error == CGDisplayNoErr) { - pixelFormat = NULL; - numPixelFormats = 0; - - int index = 0; CGLPixelFormatAttribute fullattribs[] = { kCGLPFAFullScreen, kCGLPFADisplayMask, (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(display), kCGLPFADoubleBuffer, + kCGLPFANoRecovery, kCGLPFAAccelerated, kCGLPFADepthSize, (CGLPixelFormatAttribute)depthSize, kCGLPFAColorSize, (CGLPixelFormatAttribute)CreationParams.Bits, @@ -602,6 +596,8 @@ bool CIrrDeviceMacOSX::createWindow() (CGLPixelFormatAttribute)NULL }; + pixelFormat = NULL; + numPixelFormats = 0; CGLChoosePixelFormat(fullattribs,&pixelFormat,&numPixelFormats); if (pixelFormat != NULL) @@ -620,6 +616,8 @@ bool CIrrDeviceMacOSX::createWindow() result = true; } } + if (!result) + CGReleaseAllDisplays(); } } } @@ -632,6 +630,11 @@ bool CIrrDeviceMacOSX::createWindow() CGLSetCurrentContext(CGLContext); newSwapInterval = (CreationParams.Vsync) ? 1 : 0; CGLSetParameter(CGLContext,kCGLCPSwapInterval,&newSwapInterval); + if (IsSoftwareRenderer && CreationParams.DriverType != video::EDT_NULL) + { + long order = -1; // below window + CGLSetParameter(CGLContext, kCGLCPSurfaceOrder, &order); + } } return (result); @@ -658,6 +661,7 @@ void CIrrDeviceMacOSX::setResize(int width, int height) [(NSOpenGLContext *)OGLContext update]; } + void CIrrDeviceMacOSX::createDriver() { switch (CreationParams.DriverType) @@ -720,7 +724,7 @@ bool CIrrDeviceMacOSX::run() os::Timer::tick(); storeMouseLocation(); - event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]; + event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; if (event != nil) { bzero(&ievent,sizeof(ievent)); @@ -839,7 +843,6 @@ bool CIrrDeviceMacOSX::run() break; } } - [event release]; pollJoysticks(); @@ -850,7 +853,6 @@ bool CIrrDeviceMacOSX::run() //! Pause the current process for the minimum time allowed only to allow other processes to execute void CIrrDeviceMacOSX::yield() { - // TODO: Does this work or maybe is there a better way? struct timespec ts = {0,0}; nanosleep(&ts, NULL); } @@ -859,8 +861,6 @@ void CIrrDeviceMacOSX::yield() //! Pause execution and let other processes to run for a specified amount of time. void CIrrDeviceMacOSX::sleep(u32 timeMs, bool pauseTimer=false) { - // TODO: Does this work or maybe is there a better way? - bool wasStopped = Timer ? Timer->isStopped() : true; struct timespec ts; @@ -1055,13 +1055,10 @@ void CIrrDeviceMacOSX::setMouseLocation(int x,int y) void CIrrDeviceMacOSX::setCursorVisible(bool visible) { - CGDirectDisplayID display; - - display = CGMainDisplayID(); if (visible) - CGDisplayShowCursor(display); + CGDisplayShowCursor(CGMainDisplayID()); else - CGDisplayHideCursor(display); + CGDisplayHideCursor(CGMainDisplayID()); } @@ -1118,28 +1115,34 @@ void CIrrDeviceMacOSX::setResizable(bool resize) #endif } + bool CIrrDeviceMacOSX::isResizable() const { return IsResizable; } + void CIrrDeviceMacOSX::minimizeWindow() { - [Window miniaturize:[NSApp self]]; + if (Window != NULL) + [Window miniaturize:[NSApp self]]; } + //! Maximizes the window if possible. void CIrrDeviceMacOSX::maximizeWindow() { // todo: implement } + //! Restore the window to normal size if possible. void CIrrDeviceMacOSX::restoreWindow() { [Window deminiaturize:[NSApp self]]; } + bool CIrrDeviceMacOSX::present(video::IImage* surface, void* windowId, core::rect* src ) { // todo: implement window ID and src rectangle @@ -1149,12 +1152,14 @@ bool CIrrDeviceMacOSX::present(video::IImage* surface, void* windowId, core::rec if (IsSoftwareRenderer) { + const u32 colorSamples=3; // do we need to change the size? - bool updateSize = !SoftwareDriverTarget || - s32([SoftwareDriverTarget size].width) != surface->getDimension().Width || - s32([SoftwareDriverTarget size].height) != surface->getDimension().Height; + const bool updateSize = !SoftwareDriverTarget || + s32([SoftwareDriverTarget size].width) != surface->getDimension().Width || + s32([SoftwareDriverTarget size].height) != surface->getDimension().Height; NSRect areaRect = NSMakeRect(0.0, 0.0, surface->getDimension().Width, surface->getDimension().Height); + const u32 destPitch = (colorSamples * areaRect.size.width); // create / update the target if (updateSize) @@ -1166,29 +1171,35 @@ bool CIrrDeviceMacOSX::present(video::IImage* surface, void* windowId, core::rec pixelsWide: areaRect.size.width pixelsHigh: areaRect.size.height bitsPerSample: 8 - samplesPerPixel: 3 + samplesPerPixel: colorSamples hasAlpha: NO isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace - bytesPerRow: (3 * areaRect.size.width) - bitsPerPixel: 24]; + bytesPerRow: destPitch + bitsPerPixel: 8*colorSamples]; } - const u32 destwidth = areaRect.size.width; - const u32 minWidth = core::min_(surface->getDimension().Width, destwidth); - const u32 destPitch = (3 * areaRect.size.width); + if (SoftwareDriverTarget==nil) + return false; // get pointer to image data unsigned char* imgData = (unsigned char*)surface->lock(); u8* srcdata = reinterpret_cast(imgData); u8* destData = reinterpret_cast([SoftwareDriverTarget bitmapData]); - const u32 destheight = areaRect.size.height; - const u32 srcheight = core::min_(surface->getDimension().Height, destheight); + const u32 srcheight = core::min_(surface->getDimension().Height, (u32)areaRect.size.height); const u32 srcPitch = surface->getPitch(); + const u32 minWidth = core::min_(surface->getDimension().Width, (u32)areaRect.size.width); for (u32 y=0; y!=srcheight; ++y) { +#if 0 + if (surface->getColorFormat() == video::ECF_A8R8G8B8) + video::CColorConverter::convert_A8R8G8B8toB8G8R8(srcdata, minWidth, destData); + else + video::CColorConverter::convert_A1R5G5B5toB8G8R8(srcdata, minWidth, destData); +#else video::CColorConverter::convert_viaFormat(srcdata, surface->getColorFormat(), minWidth, destData, video::ECF_R8G8B8); +#endif srcdata += srcPitch; destData += destPitch; } @@ -1203,6 +1214,7 @@ bool CIrrDeviceMacOSX::present(video::IImage* surface, void* windowId, core::rec return false; } + #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) static void joystickRemovalCallback(void * target, IOReturn result, void * refcon, void * sender) diff --git a/source/Irrlicht/MacOSX/MacOSX.xcodeproj/project.pbxproj b/source/Irrlicht/MacOSX/MacOSX.xcodeproj/project.pbxproj index ac975c49..ec6ca439 100644 --- a/source/Irrlicht/MacOSX/MacOSX.xcodeproj/project.pbxproj +++ b/source/Irrlicht/MacOSX/MacOSX.xcodeproj/project.pbxproj @@ -117,7 +117,6 @@ 0925114D0D744ADE006784D9 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0925113D0D744ADE006784D9 /* Carbon.framework */; }; 09293C3E0ED32029003B8C9C /* png.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C2C0ED32029003B8C9C /* png.c */; }; 09293C3F0ED32029003B8C9C /* pngerror.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C2D0ED32029003B8C9C /* pngerror.c */; }; - 09293C400ED32029003B8C9C /* pnggccrd.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C2E0ED32029003B8C9C /* pnggccrd.c */; }; 09293C410ED32029003B8C9C /* pngget.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C2F0ED32029003B8C9C /* pngget.c */; }; 09293C420ED32029003B8C9C /* pngmem.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C300ED32029003B8C9C /* pngmem.c */; }; 09293C430ED32029003B8C9C /* pngpread.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C310ED32029003B8C9C /* pngpread.c */; }; @@ -312,7 +311,6 @@ 4C53E2870A4850D60014E966 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C53E26D0A4850D60014E966 /* Cocoa.framework */; }; 4C53E2880A4850D60014E966 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C53E26E0A4850D60014E966 /* OpenGL.framework */; }; 4C53E3890A48559C0014E966 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 4C53E1650A484C2C0014E966 /* MainMenu.nib */; }; - 4C53E3CA0A4856B30014E966 /* gzio.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E17D0A484C2C0014E966 /* gzio.c */; }; 4C53E3D80A4856B30014E966 /* inffast.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1800A484C2C0014E966 /* inffast.c */; }; 4C53E3DC0A4856B30014E966 /* inftrees.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1850A484C2C0014E966 /* inftrees.c */; }; 4C53E3E40A4856B30014E966 /* uncompr.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E18D0A484C2C0014E966 /* uncompr.c */; }; @@ -599,6 +597,489 @@ 5DD480CA0C7DA66800728AA9 /* CIrrDeviceSDL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480C40C7DA66800728AA9 /* CIrrDeviceSDL.cpp */; }; 5DD480CB0C7DA66800728AA9 /* COpenGLExtensionHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480C50C7DA66800728AA9 /* COpenGLExtensionHandler.cpp */; }; 5DD480CC0C7DA66800728AA9 /* CMD3MeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480C60C7DA66800728AA9 /* CMD3MeshFileLoader.cpp */; }; + 95154774133CD9DA008D792F /* aabbox3d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C0A0A88742800B03626 /* aabbox3d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154775133CD9DA008D792F /* CMeshBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910B9FD0D1F64B300D46B04 /* CMeshBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154776133CD9DA008D792F /* coreutil.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910B9FE0D1F64B300D46B04 /* coreutil.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154777133CD9DA008D792F /* dimension2d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C0B0A88742800B03626 /* dimension2d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154778133CD9DA008D792F /* ECullingTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910B9FF0D1F64B300D46B04 /* ECullingTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154779133CD9DA008D792F /* EDebugSceneTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA000D1F64B300D46B04 /* EDebugSceneTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515477A133CD9DA008D792F /* EDriverFeatures.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA010D1F64B300D46B04 /* EDriverFeatures.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515477B133CD9DA008D792F /* EDriverTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C0C0A88742800B03626 /* EDriverTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515477C133CD9DA008D792F /* EGUIAlignment.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA020D1F64B300D46B04 /* EGUIAlignment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515477D133CD9DA008D792F /* EGUIElementTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C0D0A88742800B03626 /* EGUIElementTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515477E133CD9DA008D792F /* EHardwareBufferFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA030D1F64B300D46B04 /* EHardwareBufferFlags.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515477F133CD9DA008D792F /* EMaterialFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA040D1F64B300D46B04 /* EMaterialFlags.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154780133CD9DA008D792F /* EMaterialTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA050D1F64B300D46B04 /* EMaterialTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154781133CD9DA008D792F /* EMeshWriterEnums.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA060D1F64B300D46B04 /* EMeshWriterEnums.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154782133CD9DA008D792F /* EMessageBoxFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA070D1F64B300D46B04 /* EMessageBoxFlags.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154783133CD9DA008D792F /* ESceneNodeAnimatorTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C0E0A88742800B03626 /* ESceneNodeAnimatorTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154784133CD9DA008D792F /* ESceneNodeTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C0F0A88742800B03626 /* ESceneNodeTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154785133CD9DA008D792F /* ETerrainElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA080D1F64B300D46B04 /* ETerrainElements.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154786133CD9DA008D792F /* heapsort.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C100A88742800B03626 /* heapsort.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154787133CD9DA008D792F /* IAnimatedMesh.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C110A88742900B03626 /* IAnimatedMesh.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154788133CD9DA008D792F /* IAnimatedMeshMD2.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C130A88742900B03626 /* IAnimatedMeshMD2.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154789133CD9DA008D792F /* IAnimatedMeshMD3.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA090D1F64B300D46B04 /* IAnimatedMeshMD3.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515478A133CD9DA008D792F /* IAnimatedMeshSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C150A88742900B03626 /* IAnimatedMeshSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515478B133CD9DA008D792F /* IAttributeExchangingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C170A88742900B03626 /* IAttributeExchangingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515478C133CD9DA008D792F /* IAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C180A88742900B03626 /* IAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515478D133CD9DA008D792F /* IBillboardSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C190A88742900B03626 /* IBillboardSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515478E133CD9DA008D792F /* IBoneSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA0A0D1F64B300D46B04 /* IBoneSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515478F133CD9DA008D792F /* ICameraSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C1A0A88742900B03626 /* ICameraSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154790133CD9DA008D792F /* ICursorControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C1B0A88742900B03626 /* ICursorControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154791133CD9DA008D792F /* IDummyTransformationSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C1C0A88742900B03626 /* IDummyTransformationSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154792133CD9DA008D792F /* IEventReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C1D0A88742900B03626 /* IEventReceiver.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154793133CD9DA008D792F /* IFileList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C1E0A88742900B03626 /* IFileList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154794133CD9DA008D792F /* IFileSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C1F0A88742900B03626 /* IFileSystem.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154795133CD9DA008D792F /* IGPUProgrammingServices.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C200A88742900B03626 /* IGPUProgrammingServices.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154796133CD9DA008D792F /* IGUIButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C210A88742900B03626 /* IGUIButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154797133CD9DA008D792F /* IGUICheckBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C220A88742900B03626 /* IGUICheckBox.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154798133CD9DA008D792F /* IGUIColorSelectDialog.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA0B0D1F64B300D46B04 /* IGUIColorSelectDialog.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154799133CD9DA008D792F /* IGUIComboBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C230A88742900B03626 /* IGUIComboBox.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515479A133CD9DA008D792F /* IGUIContextMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C240A88742900B03626 /* IGUIContextMenu.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515479B133CD9DA008D792F /* IGUIEditBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C250A88742900B03626 /* IGUIEditBox.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515479C133CD9DA008D792F /* IGUIElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C260A88742900B03626 /* IGUIElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515479D133CD9DA008D792F /* IGUIElementFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA0C0D1F64B300D46B04 /* IGUIElementFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515479E133CD9DA008D792F /* IGUIEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C270A88742900B03626 /* IGUIEnvironment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515479F133CD9DA008D792F /* IGUIFileOpenDialog.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C280A88742900B03626 /* IGUIFileOpenDialog.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A0133CD9DA008D792F /* IGUIFont.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C290A88742900B03626 /* IGUIFont.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A1133CD9DA008D792F /* IGUIFontBitmap.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA0D0D1F64B300D46B04 /* IGUIFontBitmap.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A2133CD9DA008D792F /* IGUIImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C2A0A88742900B03626 /* IGUIImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A3133CD9DA008D792F /* IGUIInOutFader.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C2B0A88742900B03626 /* IGUIInOutFader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A4133CD9DA008D792F /* IGUIListBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C2C0A88742900B03626 /* IGUIListBox.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A5133CD9DA008D792F /* IGUIMeshViewer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C2D0A88742900B03626 /* IGUIMeshViewer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A6133CD9DA008D792F /* IGUIScrollBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C2E0A88742900B03626 /* IGUIScrollBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A7133CD9DA008D792F /* IGUISkin.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C2F0A88742900B03626 /* IGUISkin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A8133CD9DA008D792F /* IGUISpinBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA0E0D1F64B300D46B04 /* IGUISpinBox.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547A9133CD9DA008D792F /* IGUISpriteBank.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA0F0D1F64B300D46B04 /* IGUISpriteBank.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547AA133CD9DA008D792F /* IGUIStaticText.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C300A88742900B03626 /* IGUIStaticText.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547AB133CD9DA008D792F /* IGUITabControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C310A88742900B03626 /* IGUITabControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547AC133CD9DA008D792F /* IGUITable.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA100D1F64B300D46B04 /* IGUITable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547AD133CD9DA008D792F /* IGUIToolbar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C320A88742900B03626 /* IGUIToolbar.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547AE133CD9DA008D792F /* IGUIWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C330A88742900B03626 /* IGUIWindow.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547AF133CD9DA008D792F /* IImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C340A88742900B03626 /* IImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B0133CD9DA008D792F /* IImageLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C350A88742900B03626 /* IImageLoader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B1133CD9DA008D792F /* IImageWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C360A88742900B03626 /* IImageWriter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B2133CD9DA008D792F /* ILightSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C370A88742900B03626 /* ILightSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B3133CD9DA008D792F /* ILogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C380A88742900B03626 /* ILogger.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B4133CD9DA008D792F /* IMaterialRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C390A88742900B03626 /* IMaterialRenderer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B5133CD9DA008D792F /* IMaterialRendererServices.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C3A0A88742900B03626 /* IMaterialRendererServices.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B6133CD9DA008D792F /* IMesh.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C3B0A88742900B03626 /* IMesh.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B7133CD9DA008D792F /* IMeshBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C3C0A88742900B03626 /* IMeshBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B8133CD9DA008D792F /* IMeshCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C3D0A88742900B03626 /* IMeshCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547B9133CD9DA008D792F /* IMeshLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C3E0A88742900B03626 /* IMeshLoader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547BA133CD9DA008D792F /* IMeshManipulator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C3F0A88742900B03626 /* IMeshManipulator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547BB133CD9DA008D792F /* IMeshSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C400A88742900B03626 /* IMeshSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547BC133CD9DA008D792F /* IMeshWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA110D1F64B300D46B04 /* IMeshWriter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547BD133CD9DA008D792F /* IMetaTriangleSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C410A88742900B03626 /* IMetaTriangleSelector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547BE133CD9DA008D792F /* IOSOperator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C420A88742900B03626 /* IOSOperator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547BF133CD9DA008D792F /* IParticleAffector.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C430A88742900B03626 /* IParticleAffector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C0133CD9DA008D792F /* IParticleAnimatedMeshSceneNodeEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA120D1F64B300D46B04 /* IParticleAnimatedMeshSceneNodeEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C1133CD9DA008D792F /* IParticleAttractionAffector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA130D1F64B300D46B04 /* IParticleAttractionAffector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C2133CD9DA008D792F /* IParticleBoxEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA140D1F64B300D46B04 /* IParticleBoxEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C3133CD9DA008D792F /* IParticleCylinderEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA150D1F64B300D46B04 /* IParticleCylinderEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C4133CD9DA008D792F /* IParticleEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C440A88742900B03626 /* IParticleEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C5133CD9DA008D792F /* IParticleFadeOutAffector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA160D1F64B300D46B04 /* IParticleFadeOutAffector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C6133CD9DA008D792F /* IParticleGravityAffector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA170D1F64B300D46B04 /* IParticleGravityAffector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C7133CD9DA008D792F /* IParticleMeshEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA180D1F64B300D46B04 /* IParticleMeshEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C8133CD9DA008D792F /* IParticleRingEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA190D1F64B300D46B04 /* IParticleRingEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547C9133CD9DA008D792F /* IParticleRotationAffector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA1A0D1F64B300D46B04 /* IParticleRotationAffector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547CA133CD9DA008D792F /* IParticleSphereEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA1B0D1F64B300D46B04 /* IParticleSphereEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547CB133CD9DA008D792F /* IParticleSystemSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C450A88742900B03626 /* IParticleSystemSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547CC133CD9DA008D792F /* IQ3LevelMesh.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C460A88742900B03626 /* IQ3LevelMesh.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547CD133CD9DA008D792F /* IQ3Shader.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA1C0D1F64B300D46B04 /* IQ3Shader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547CE133CD9DA008D792F /* IReadFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C470A88742900B03626 /* IReadFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547CF133CD9DA008D792F /* IReferenceCounted.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA1D0D1F64B300D46B04 /* IReferenceCounted.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D0133CD9DA008D792F /* irrAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C480A88742900B03626 /* irrAllocator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D1133CD9DA008D792F /* irrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C490A88742900B03626 /* irrArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D2133CD9DA008D792F /* IrrCompileConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C4A0A88742900B03626 /* IrrCompileConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D3133CD9DA008D792F /* irrlicht.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C4B0A88742900B03626 /* irrlicht.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D4133CD9DA008D792F /* IrrlichtDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C4C0A88742900B03626 /* IrrlichtDevice.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D5133CD9DA008D792F /* irrList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C4D0A88742900B03626 /* irrList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D6133CD9DA008D792F /* irrMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA1E0D1F64B300D46B04 /* irrMap.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D7133CD9DA008D792F /* irrMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C4E0A88742900B03626 /* irrMath.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D8133CD9DA008D792F /* irrString.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C4F0A88742900B03626 /* irrString.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547D9133CD9DA008D792F /* irrTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C500A88742900B03626 /* irrTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547DA133CD9DA008D792F /* irrXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C510A88742900B03626 /* irrXML.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547DB133CD9DA008D792F /* ISceneCollisionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C520A88742900B03626 /* ISceneCollisionManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547DC133CD9DA008D792F /* ISceneManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C530A88742900B03626 /* ISceneManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547DD133CD9DA008D792F /* ISceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C540A88742900B03626 /* ISceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547DE133CD9DA008D792F /* ISceneNodeAnimator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C550A88742900B03626 /* ISceneNodeAnimator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547DF133CD9DA008D792F /* ISceneNodeAnimatorCollisionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C560A88742900B03626 /* ISceneNodeAnimatorCollisionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E0133CD9DA008D792F /* ISceneNodeAnimatorFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C570A88742900B03626 /* ISceneNodeAnimatorFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E1133CD9DA008D792F /* ISceneNodeFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C580A88742900B03626 /* ISceneNodeFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E2133CD9DA008D792F /* ISceneUserDataSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C590A88742900B03626 /* ISceneUserDataSerializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E3133CD9DA008D792F /* IShaderConstantSetCallBack.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C5A0A88742900B03626 /* IShaderConstantSetCallBack.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E4133CD9DA008D792F /* IShadowVolumeSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C5B0A88742900B03626 /* IShadowVolumeSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E5133CD9DA008D792F /* ISkinnedMesh.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA1F0D1F64B300D46B04 /* ISkinnedMesh.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E6133CD9DA008D792F /* ITerrainSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C5C0A88742900B03626 /* ITerrainSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E7133CD9DA008D792F /* ITextSceneNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C5D0A88742900B03626 /* ITextSceneNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E8133CD9DA008D792F /* ITexture.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C5E0A88742900B03626 /* ITexture.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547E9133CD9DA008D792F /* ITimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C5F0A88742900B03626 /* ITimer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547EA133CD9DA008D792F /* ITriangleSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C600A88742900B03626 /* ITriangleSelector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547EB133CD9DA008D792F /* IVideoDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C620A88742900B03626 /* IVideoDriver.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547EC133CD9DA008D792F /* IVideoModeList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C630A88742900B03626 /* IVideoModeList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547ED133CD9DA008D792F /* IWriteFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C640A88742900B03626 /* IWriteFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547EE133CD9DA008D792F /* IXMLReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C650A88742900B03626 /* IXMLReader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547EF133CD9DA008D792F /* IXMLWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C660A88742900B03626 /* IXMLWriter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F0133CD9DA008D792F /* Keycodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C670A88742900B03626 /* Keycodes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F1133CD9DA008D792F /* line2d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C680A88742900B03626 /* line2d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F2133CD9DA008D792F /* line3d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C690A88742900B03626 /* line3d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F3133CD9DA008D792F /* matrix4.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C6A0A88742900B03626 /* matrix4.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F4133CD9DA008D792F /* plane3d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C6B0A88742900B03626 /* plane3d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F5133CD9DA008D792F /* position2d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C6C0A88742900B03626 /* position2d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F6133CD9DA008D792F /* quaternion.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C6D0A88742900B03626 /* quaternion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F7133CD9DA008D792F /* rect.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C6E0A88742900B03626 /* rect.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F8133CD9DA008D792F /* S3DVertex.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C6F0A88742900B03626 /* S3DVertex.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547F9133CD9DA008D792F /* SAnimatedMesh.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C700A88742900B03626 /* SAnimatedMesh.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547FA133CD9DA008D792F /* SceneParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C710A88742900B03626 /* SceneParameters.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547FB133CD9DA008D792F /* SColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C720A88742900B03626 /* SColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547FC133CD9DA008D792F /* SExposedVideoData.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C730A88742900B03626 /* SExposedVideoData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547FD133CD9DA008D792F /* SIrrCreationParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C740A88742900B03626 /* SIrrCreationParameters.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547FE133CD9DA008D792F /* SKeyMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C750A88742900B03626 /* SKeyMap.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 951547FF133CD9DA008D792F /* SLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C760A88742900B03626 /* SLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154800133CD9DA008D792F /* SMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C770A88742900B03626 /* SMaterial.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154801133CD9DA008D792F /* SMaterialLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA200D1F64B300D46B04 /* SMaterialLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154802133CD9DA008D792F /* SMesh.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C780A88742900B03626 /* SMesh.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154803133CD9DA008D792F /* SMeshBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C790A88742900B03626 /* SMeshBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154804133CD9DA008D792F /* SMeshBufferLightMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C7A0A88742900B03626 /* SMeshBufferLightMap.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154805133CD9DA008D792F /* SMeshBufferTangents.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C7B0A88742900B03626 /* SMeshBufferTangents.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154806133CD9DA008D792F /* SParticle.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C7C0A88742900B03626 /* SParticle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154807133CD9DA008D792F /* SSharedMeshBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA210D1F64B300D46B04 /* SSharedMeshBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154808133CD9DA008D792F /* SSkinMeshBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA220D1F64B300D46B04 /* SSkinMeshBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95154809133CD9DA008D792F /* SViewFrustum.h in Headers */ = {isa = PBXBuildFile; fileRef = 0910BA230D1F64B300D46B04 /* SViewFrustum.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515480A133CD9DA008D792F /* triangle3d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C7E0A88742900B03626 /* triangle3d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515480B133CD9DA008D792F /* vector2d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C7F0A88742900B03626 /* vector2d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9515480C133CD9DA008D792F /* vector3d.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CFA7C800A88742900B03626 /* vector3d.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 959729E912C192DA00BF73D3 /* jcapimin.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6F10A485CD80014E966 /* jcapimin.c */; }; + 959729EA12C192DA00BF73D3 /* jcapistd.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6F20A485CD80014E966 /* jcapistd.c */; }; + 959729EB12C192DA00BF73D3 /* jccoefct.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6F30A485CD80014E966 /* jccoefct.c */; }; + 959729EC12C192DA00BF73D3 /* jccolor.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6F40A485CD80014E966 /* jccolor.c */; }; + 959729ED12C192DA00BF73D3 /* jcdctmgr.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6F50A485CD80014E966 /* jcdctmgr.c */; }; + 959729EE12C192DA00BF73D3 /* jchuff.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6F60A485CD80014E966 /* jchuff.c */; }; + 959729EF12C192DA00BF73D3 /* jcinit.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6F80A485CD80014E966 /* jcinit.c */; }; + 959729F012C192DA00BF73D3 /* jcmainct.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6F90A485CD80014E966 /* jcmainct.c */; }; + 959729F112C192DA00BF73D3 /* jcmarker.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6FA0A485CD80014E966 /* jcmarker.c */; }; + 959729F212C192DA00BF73D3 /* jcmaster.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6FB0A485CD80014E966 /* jcmaster.c */; }; + 959729F312C192DA00BF73D3 /* jcomapi.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E6FC0A485CD80014E966 /* jcomapi.c */; }; + 959729F412C192DA00BF73D3 /* jcparam.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E70A0A485CD80014E966 /* jcparam.c */; }; + 959729F512C192DA00BF73D3 /* jcprepct.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E70C0A485CD80014E966 /* jcprepct.c */; }; + 959729F612C192DA00BF73D3 /* jcsample.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E70D0A485CD80014E966 /* jcsample.c */; }; + 959729F712C192DA00BF73D3 /* jctrans.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E70E0A485CD80014E966 /* jctrans.c */; }; + 959729F812C192DA00BF73D3 /* jdapimin.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E70F0A485CD80014E966 /* jdapimin.c */; }; + 959729F912C192DA00BF73D3 /* jdapistd.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7100A485CD80014E966 /* jdapistd.c */; }; + 959729FA12C192DA00BF73D3 /* jdatadst.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7110A485CD80014E966 /* jdatadst.c */; }; + 959729FB12C192DA00BF73D3 /* jdatasrc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7120A485CD80014E966 /* jdatasrc.c */; }; + 959729FC12C192DA00BF73D3 /* jdcoefct.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7130A485CD80014E966 /* jdcoefct.c */; }; + 959729FD12C192DA00BF73D3 /* jdcolor.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7140A485CD80014E966 /* jdcolor.c */; }; + 959729FE12C192DA00BF73D3 /* jddctmgr.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7160A485CD80014E966 /* jddctmgr.c */; }; + 959729FF12C192DA00BF73D3 /* jdhuff.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7170A485CD80014E966 /* jdhuff.c */; }; + 95972A0012C192DA00BF73D3 /* jdinput.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7190A485CD80014E966 /* jdinput.c */; }; + 95972A0112C192DA00BF73D3 /* jdmainct.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E71A0A485CD80014E966 /* jdmainct.c */; }; + 95972A0212C192DA00BF73D3 /* jdmarker.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E71B0A485CD80014E966 /* jdmarker.c */; }; + 95972A0312C192DA00BF73D3 /* jdmaster.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E71C0A485CD80014E966 /* jdmaster.c */; }; + 95972A0412C192DA00BF73D3 /* jdmerge.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E71D0A485CD80014E966 /* jdmerge.c */; }; + 95972A0512C192DA00BF73D3 /* jdpostct.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E71F0A485CD80014E966 /* jdpostct.c */; }; + 95972A0612C192DA00BF73D3 /* jdsample.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7200A485CD80014E966 /* jdsample.c */; }; + 95972A0712C192DA00BF73D3 /* jdtrans.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7210A485CD80014E966 /* jdtrans.c */; }; + 95972A0812C192DA00BF73D3 /* jerror.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7220A485CD80014E966 /* jerror.c */; }; + 95972A0912C192DA00BF73D3 /* jfdctflt.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7240A485CD80014E966 /* jfdctflt.c */; }; + 95972A0A12C192DA00BF73D3 /* jfdctfst.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7250A485CD80014E966 /* jfdctfst.c */; }; + 95972A0B12C192DA00BF73D3 /* jfdctint.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7260A485CD80014E966 /* jfdctint.c */; }; + 95972A0C12C192DA00BF73D3 /* jidctflt.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7270A485CD80014E966 /* jidctflt.c */; }; + 95972A0D12C192DA00BF73D3 /* jidctfst.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7280A485CD80014E966 /* jidctfst.c */; }; + 95972A0E12C192DA00BF73D3 /* jidctint.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7290A485CD80014E966 /* jidctint.c */; }; + 95972A0F12C192DA00BF73D3 /* jmemmgr.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7300A485CD80014E966 /* jmemmgr.c */; }; + 95972A1012C192DA00BF73D3 /* jmemnobs.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7320A485CD80014E966 /* jmemnobs.c */; }; + 95972A1112C192DA00BF73D3 /* jquant1.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7390A485CD80014E966 /* jquant1.c */; }; + 95972A1212C192DA00BF73D3 /* jquant2.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E73A0A485CD80014E966 /* jquant2.c */; }; + 95972A1312C192DA00BF73D3 /* jutils.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E73B0A485CD80014E966 /* jutils.c */; }; + 95972A1412C192DA00BF73D3 /* rdbmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7540A485CD90014E966 /* rdbmp.c */; }; + 95972A1512C192DA00BF73D3 /* rdcolmap.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7550A485CD90014E966 /* rdcolmap.c */; }; + 95972A1612C192DA00BF73D3 /* rdgif.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7560A485CD90014E966 /* rdgif.c */; }; + 95972A1712C192DA00BF73D3 /* rdppm.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7590A485CD90014E966 /* rdppm.c */; }; + 95972A1812C192DA00BF73D3 /* rdrle.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E75A0A485CD90014E966 /* rdrle.c */; }; + 95972A1912C192DA00BF73D3 /* rdswitch.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E75B0A485CD90014E966 /* rdswitch.c */; }; + 95972A1A12C192DA00BF73D3 /* rdtarga.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E75C0A485CD90014E966 /* rdtarga.c */; }; + 95972A1B12C192DA00BF73D3 /* transupp.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7660A485CD90014E966 /* transupp.c */; }; + 95972A1C12C192DA00BF73D3 /* wrbmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E76A0A485CD90014E966 /* wrbmp.c */; }; + 95972A1D12C192DA00BF73D3 /* wrgif.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E76B0A485CD90014E966 /* wrgif.c */; }; + 95972A1E12C192DA00BF73D3 /* wrppm.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E76E0A485CD90014E966 /* wrppm.c */; }; + 95972A1F12C192DA00BF73D3 /* wrrle.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E76F0A485CD90014E966 /* wrrle.c */; }; + 95972A2012C192DA00BF73D3 /* wrtarga.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E7700A485CD90014E966 /* wrtarga.c */; }; + 95972A2212C192DA00BF73D3 /* inffast.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1800A484C2C0014E966 /* inffast.c */; }; + 95972A2312C192DA00BF73D3 /* inftrees.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1850A484C2C0014E966 /* inftrees.c */; }; + 95972A2412C192DA00BF73D3 /* uncompr.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E18D0A484C2C0014E966 /* uncompr.c */; }; + 95972A2512C192DA00BF73D3 /* compress.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1750A484C2C0014E966 /* compress.c */; }; + 95972A2612C192DA00BF73D3 /* crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1770A484C2C0014E966 /* crc32.c */; }; + 95972A2712C192DA00BF73D3 /* zutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1920A484C2C0014E966 /* zutil.c */; }; + 95972A2812C192DA00BF73D3 /* trees.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E18B0A484C2C0014E966 /* trees.c */; }; + 95972A2912C192DA00BF73D3 /* deflate.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1790A484C2C0014E966 /* deflate.c */; }; + 95972A2A12C192DA00BF73D3 /* adler32.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1720A484C2C0014E966 /* adler32.c */; }; + 95972A2B12C192DA00BF73D3 /* CImageLoaderPNG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF600A484C230014E966 /* CImageLoaderPNG.cpp */; }; + 95972A2C12C192DA00BF73D3 /* CColorConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEFC0A484C220014E966 /* CColorConverter.cpp */; }; + 95972A2D12C192DA00BF73D3 /* CSceneManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFAB0A484C240014E966 /* CSceneManager.cpp */; }; + 95972A2E12C192DA00BF73D3 /* CTRTextureGouraudAdd2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE30A484C250014E966 /* CTRTextureGouraudAdd2.cpp */; }; + 95972A2F12C192DA00BF73D3 /* CNullDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF820A484C240014E966 /* CNullDriver.cpp */; }; + 95972A3012C192DA00BF73D3 /* CCSMLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEFE0A484C220014E966 /* CCSMLoader.cpp */; }; + 95972A3112C192DA00BF73D3 /* irrXML.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E00E0A484C250014E966 /* irrXML.cpp */; }; + 95972A3212C192DA00BF73D3 /* CGUIListBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF420A484C230014E966 /* CGUIListBox.cpp */; }; + 95972A3312C192DA00BF73D3 /* CTRGouraudWire.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFD70A484C240014E966 /* CTRGouraudWire.cpp */; }; + 95972A3412C192DA00BF73D3 /* CIrrDeviceStub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF680A484C230014E966 /* CIrrDeviceStub.cpp */; }; + 95972A3512C192DA00BF73D3 /* CGUIMessageBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF480A484C230014E966 /* CGUIMessageBox.cpp */; }; + 95972A3612C192DA00BF73D3 /* CMeshSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF7A0A484C230014E966 /* CMeshSceneNode.cpp */; }; + 95972A3712C192DA00BF73D3 /* CGUIStaticText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF500A484C230014E966 /* CGUIStaticText.cpp */; }; + 95972A3812C192DA00BF73D3 /* os.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E16A0A484C2C0014E966 /* os.cpp */; }; + 95972A3912C192DA00BF73D3 /* COCTLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF840A484C240014E966 /* COCTLoader.cpp */; }; + 95972A3A12C192DA00BF73D3 /* CGUIContextMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF340A484C230014E966 /* CGUIContextMenu.cpp */; }; + 95972A3B12C192DA00BF73D3 /* CSceneNodeAnimatorFlyCircle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFB10A484C240014E966 /* CSceneNodeAnimatorFlyCircle.cpp */; }; + 95972A3C12C192DA00BF73D3 /* CDefaultSceneNodeFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF1E0A484C230014E966 /* CDefaultSceneNodeFactory.cpp */; }; + 95972A3D12C192DA00BF73D3 /* CD3D9Driver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF0D0A484C230014E966 /* CD3D9Driver.cpp */; }; + 95972A3E12C192DA00BF73D3 /* CTRGouraud.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFD30A484C240014E966 /* CTRGouraud.cpp */; }; + 95972A3F12C192DA00BF73D3 /* C3DSMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEE70A484C220014E966 /* C3DSMeshFileLoader.cpp */; }; + 95972A4012C192DA00BF73D3 /* COgreMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF8A0A484C240014E966 /* COgreMeshFileLoader.cpp */; }; + 95972A4112C192DA00BF73D3 /* CMY3DMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF7F0A484C230014E966 /* CMY3DMeshFileLoader.cpp */; }; + 95972A4212C192DA00BF73D3 /* CLMTSMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF700A484C230014E966 /* CLMTSMeshFileLoader.cpp */; }; + 95972A4312C192DA00BF73D3 /* CGUIFileOpenDialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF3A0A484C230014E966 /* CGUIFileOpenDialog.cpp */; }; + 95972A4412C192DA00BF73D3 /* CSceneNodeAnimatorDelete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFAF0A484C240014E966 /* CSceneNodeAnimatorDelete.cpp */; }; + 95972A4512C192DA00BF73D3 /* CTRGouraudAlphaNoZ2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFD60A484C240014E966 /* CTRGouraudAlphaNoZ2.cpp */; }; + 95972A4612C192DA00BF73D3 /* CGeometryCreator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF2C0A484C230014E966 /* CGeometryCreator.cpp */; }; + 95972A4712C192DA00BF73D3 /* CD3D8Texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF0B0A484C230014E966 /* CD3D8Texture.cpp */; }; + 95972A4812C192DA00BF73D3 /* CSkyBoxSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFBD0A484C240014E966 /* CSkyBoxSceneNode.cpp */; }; + 95972A4912C192DA00BF73D3 /* CMeshManipulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF780A484C230014E966 /* CMeshManipulator.cpp */; }; + 95972A4A12C192DA00BF73D3 /* CTextSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFCE0A484C240014E966 /* CTextSceneNode.cpp */; }; + 95972A4B12C192DA00BF73D3 /* CTRTextureDetailMap2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFDC0A484C250014E966 /* CTRTextureDetailMap2.cpp */; }; + 95972A4C12C192DA00BF73D3 /* CTRTextureGouraudAddNoZ2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE40A484C250014E966 /* CTRTextureGouraudAddNoZ2.cpp */; }; + 95972A4D12C192DA00BF73D3 /* CTRTextureGouraudNoZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE50A484C250014E966 /* CTRTextureGouraudNoZ.cpp */; }; + 95972A4E12C192DA00BF73D3 /* CGUIScrollBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF4C0A484C230014E966 /* CGUIScrollBar.cpp */; }; + 95972A4F12C192DA00BF73D3 /* CSceneCollisionManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFA90A484C240014E966 /* CSceneCollisionManager.cpp */; }; + 95972A5012C192DA00BF73D3 /* CGUICheckBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF300A484C230014E966 /* CGUICheckBox.cpp */; }; + 95972A5112C192DA00BF73D3 /* CQ3LevelMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFA50A484C240014E966 /* CQ3LevelMesh.cpp */; }; + 95972A5212C192DA00BF73D3 /* CParticleGravityAffector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF9F0A484C240014E966 /* CParticleGravityAffector.cpp */; }; + 95972A5312C192DA00BF73D3 /* CSoftwareDriver2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFC20A484C240014E966 /* CSoftwareDriver2.cpp */; }; + 95972A5412C192DA00BF73D3 /* CD3D9ParallaxMapRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF140A484C230014E966 /* CD3D9ParallaxMapRenderer.cpp */; }; + 95972A5512C192DA00BF73D3 /* CD3D8ParallaxMapRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF070A484C230014E966 /* CD3D8ParallaxMapRenderer.cpp */; }; + 95972A5612C192DA00BF73D3 /* CAnimatedMeshMD2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEE90A484C220014E966 /* CAnimatedMeshMD2.cpp */; }; + 95972A5712C192DA00BF73D3 /* CSceneNodeAnimatorFlyStraight.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFB30A484C240014E966 /* CSceneNodeAnimatorFlyStraight.cpp */; }; + 95972A5812C192DA00BF73D3 /* CImageLoaderPCX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF5E0A484C230014E966 /* CImageLoaderPCX.cpp */; }; + 95972A5912C192DA00BF73D3 /* CAnimatedMeshSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEED0A484C220014E966 /* CAnimatedMeshSceneNode.cpp */; }; + 95972A5A12C192DA00BF73D3 /* CTriangleSelector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFDA0A484C250014E966 /* CTriangleSelector.cpp */; }; + 95972A5B12C192DA00BF73D3 /* CTRTextureGouraudVertexAlpha2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE70A484C250014E966 /* CTRTextureGouraudVertexAlpha2.cpp */; }; + 95972A5C12C192DA00BF73D3 /* CTRTextureWire2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFED0A484C250014E966 /* CTRTextureWire2.cpp */; }; + 95972A5D12C192DA00BF73D3 /* CTRTextureFlatWire.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFDE0A484C250014E966 /* CTRTextureFlatWire.cpp */; }; + 95972A5E12C192DA00BF73D3 /* CTRGouraud2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFD40A484C240014E966 /* CTRGouraud2.cpp */; }; + 95972A5F12C192DA00BF73D3 /* CEmptySceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF240A484C230014E966 /* CEmptySceneNode.cpp */; }; + 95972A6012C192DA00BF73D3 /* CTRTextureLightMap2_Add.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE90A484C250014E966 /* CTRTextureLightMap2_Add.cpp */; }; + 95972A6112C192DA00BF73D3 /* CReadFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFA70A484C240014E966 /* CReadFile.cpp */; }; + 95972A6212C192DA00BF73D3 /* COpenGLTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF970A484C240014E966 /* COpenGLTexture.cpp */; }; + 95972A6312C192DA00BF73D3 /* COSOperator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF990A484C240014E966 /* COSOperator.cpp */; }; + 95972A6412C192DA00BF73D3 /* CColladaFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEFA0A484C220014E966 /* CColladaFileLoader.cpp */; }; + 95972A6512C192DA00BF73D3 /* CCameraSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEF80A484C220014E966 /* CCameraSceneNode.cpp */; }; + 95972A6612C192DA00BF73D3 /* CMetaTriangleSelector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF7C0A484C230014E966 /* CMetaTriangleSelector.cpp */; }; + 95972A6712C192DA00BF73D3 /* CTRTextureFlat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFDD0A484C250014E966 /* CTRTextureFlat.cpp */; }; + 95972A6812C192DA00BF73D3 /* CVideoModeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFEE0A484C250014E966 /* CVideoModeList.cpp */; }; + 95972A6912C192DA00BF73D3 /* CXMLReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFFA0A484C250014E966 /* CXMLReader.cpp */; }; + 95972A6A12C192DA00BF73D3 /* COpenGLParallaxMapRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF910A484C240014E966 /* COpenGLParallaxMapRenderer.cpp */; }; + 95972A6B12C192DA00BF73D3 /* CTRTextureGouraudNoZ2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE60A484C250014E966 /* CTRTextureGouraudNoZ2.cpp */; }; + 95972A6C12C192DA00BF73D3 /* CTRTextureGouraudWire.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE80A484C250014E966 /* CTRTextureGouraudWire.cpp */; }; + 95972A6D12C192DA00BF73D3 /* CParticlePointEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFA10A484C240014E966 /* CParticlePointEmitter.cpp */; }; + 95972A6E12C192DA00BF73D3 /* CGUIWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF560A484C230014E966 /* CGUIWindow.cpp */; }; + 95972A6F12C192DA00BF73D3 /* CGUIModalScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF4A0A484C230014E966 /* CGUIModalScreen.cpp */; }; + 95972A7012C192DA00BF73D3 /* CImageLoaderPSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF620A484C230014E966 /* CImageLoaderPSD.cpp */; }; + 95972A7112C192DA00BF73D3 /* CWaterSurfaceSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFF00A484C250014E966 /* CWaterSurfaceSceneNode.cpp */; }; + 95972A7212C192DA00BF73D3 /* CXMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFF80A484C250014E966 /* CXMeshFileLoader.cpp */; }; + 95972A7312C192DA00BF73D3 /* CIrrDeviceLinux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF660A484C230014E966 /* CIrrDeviceLinux.cpp */; }; + 95972A7412C192DA00BF73D3 /* CLightSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF6C0A484C230014E966 /* CLightSceneNode.cpp */; }; + 95972A7512C192DA00BF73D3 /* CTRTextureGouraudAdd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE20A484C250014E966 /* CTRTextureGouraudAdd.cpp */; }; + 95972A7612C192DA00BF73D3 /* CTRTextureGouraud2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFE10A484C250014E966 /* CTRTextureGouraud2.cpp */; }; + 95972A7712C192DA00BF73D3 /* CSoftwareDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFC00A484C240014E966 /* CSoftwareDriver.cpp */; }; + 95972A7812C192DA00BF73D3 /* CTRFlatWire.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFD20A484C240014E966 /* CTRFlatWire.cpp */; }; + 95972A7912C192DA00BF73D3 /* CTRGouraudAlpha2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFD50A484C240014E966 /* CTRGouraudAlpha2.cpp */; }; + 95972A7A12C192DA00BF73D3 /* CSoftwareTexture2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFC60A484C240014E966 /* CSoftwareTexture2.cpp */; }; + 95972A7B12C192DA00BF73D3 /* CZipReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E0030A484C250014E966 /* CZipReader.cpp */; }; + 95972A7C12C192DA00BF73D3 /* COpenGLNormalMapRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF8F0A484C240014E966 /* COpenGLNormalMapRenderer.cpp */; }; + 95972A7D12C192DA00BF73D3 /* CTRTextureLightMap2_M1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFEA0A484C250014E966 /* CTRTextureLightMap2_M1.cpp */; }; + 95972A7E12C192DA00BF73D3 /* CTRTextureLightMap2_M4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFEC0A484C250014E966 /* CTRTextureLightMap2_M4.cpp */; }; + 95972A7F12C192DA00BF73D3 /* CGUISkin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF4E0A484C230014E966 /* CGUISkin.cpp */; }; + 95972A8012C192DA00BF73D3 /* CD3D8Driver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF020A484C220014E966 /* CD3D8Driver.cpp */; }; + 95972A8112C192DA00BF73D3 /* CIrrDeviceWin32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF6A0A484C230014E966 /* CIrrDeviceWin32.cpp */; }; + 95972A8212C192DA00BF73D3 /* CFileSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF280A484C230014E966 /* CFileSystem.cpp */; }; + 95972A8312C192DA00BF73D3 /* CGUIMeshViewer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF460A484C230014E966 /* CGUIMeshViewer.cpp */; }; + 95972A8412C192DA00BF73D3 /* CGUIComboBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF320A484C230014E966 /* CGUIComboBox.cpp */; }; + 95972A8512C192DA00BF73D3 /* CSceneNodeAnimatorRotation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFB70A484C240014E966 /* CSceneNodeAnimatorRotation.cpp */; }; + 95972A8612C192DA00BF73D3 /* CSceneNodeAnimatorTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFB90A484C240014E966 /* CSceneNodeAnimatorTexture.cpp */; }; + 95972A8712C192DA00BF73D3 /* CParticleSystemSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFA30A484C240014E966 /* CParticleSystemSceneNode.cpp */; }; + 95972A8812C192DA00BF73D3 /* CTerrainSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFCA0A484C240014E966 /* CTerrainSceneNode.cpp */; }; + 95972A8912C192DA00BF73D3 /* CGUIFont.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF3C0A484C230014E966 /* CGUIFont.cpp */; }; + 95972A8A12C192DA00BF73D3 /* CParticleFadeOutAffector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF9D0A484C240014E966 /* CParticleFadeOutAffector.cpp */; }; + 95972A8B12C192DA00BF73D3 /* CDummyTransformationSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF220A484C230014E966 /* CDummyTransformationSceneNode.cpp */; }; + 95972A8C12C192DA00BF73D3 /* CFileList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF260A484C230014E966 /* CFileList.cpp */; }; + 95972A8D12C192DA00BF73D3 /* CImageLoaderTGA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF640A484C230014E966 /* CImageLoaderTGA.cpp */; }; + 95972A8E12C192DA00BF73D3 /* CXMLWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFFD0A484C250014E966 /* CXMLWriter.cpp */; }; + 95972A8F12C192DA00BF73D3 /* CSceneNodeAnimatorFollowSpline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFB50A484C240014E966 /* CSceneNodeAnimatorFollowSpline.cpp */; }; + 95972A9012C192DA00BF73D3 /* CZBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFFF0A484C250014E966 /* CZBuffer.cpp */; }; + 95972A9112C192DA00BF73D3 /* CDMFLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF200A484C230014E966 /* CDMFLoader.cpp */; }; + 95972A9212C192DA00BF73D3 /* CD3D9Texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF180A484C230014E966 /* CD3D9Texture.cpp */; }; + 95972A9312C192DA00BF73D3 /* COpenGLShaderMaterialRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF930A484C240014E966 /* COpenGLShaderMaterialRenderer.cpp */; }; + 95972A9412C192DA00BF73D3 /* Irrlicht.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E00A0A484C250014E966 /* Irrlicht.cpp */; }; + 95972A9512C192DA00BF73D3 /* CGUIEditBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF360A484C230014E966 /* CGUIEditBox.cpp */; }; + 95972A9612C192DA00BF73D3 /* COpenGLSLMaterialRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF950A484C240014E966 /* COpenGLSLMaterialRenderer.cpp */; }; + 95972A9712C192DA00BF73D3 /* CD3D9HLSLMaterialRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF0F0A484C230014E966 /* CD3D9HLSLMaterialRenderer.cpp */; }; + 95972A9812C192DA00BF73D3 /* CSoftwareTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFC40A484C240014E966 /* CSoftwareTexture.cpp */; }; + 95972A9912C192DA00BF73D3 /* CCubeSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF000A484C220014E966 /* CCubeSceneNode.cpp */; }; + 95972A9A12C192DA00BF73D3 /* CTriangleBBSelector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFD80A484C240014E966 /* CTriangleBBSelector.cpp */; }; + 95972A9B12C192DA00BF73D3 /* CD3D9ShaderMaterialRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF160A484C230014E966 /* CD3D9ShaderMaterialRenderer.cpp */; }; + 95972A9C12C192DA00BF73D3 /* CD3D8ShaderMaterialRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF090A484C230014E966 /* CD3D8ShaderMaterialRenderer.cpp */; }; + 95972A9D12C192DA00BF73D3 /* CGUIButton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF2E0A484C230014E966 /* CGUIButton.cpp */; }; + 95972A9E12C192DA00BF73D3 /* CGUIToolBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF540A484C230014E966 /* CGUIToolBar.cpp */; }; + 95972A9F12C192DA00BF73D3 /* CDefaultSceneNodeAnimatorFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF1C0A484C230014E966 /* CDefaultSceneNodeAnimatorFactory.cpp */; }; + 95972AA012C192DA00BF73D3 /* CBillboardSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEF20A484C220014E966 /* CBillboardSceneNode.cpp */; }; + 95972AA112C192DA00BF73D3 /* CSceneNodeAnimatorCollisionResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFAD0A484C240014E966 /* CSceneNodeAnimatorCollisionResponse.cpp */; }; + 95972AA212C192DA00BF73D3 /* CLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF720A484C230014E966 /* CLogger.cpp */; }; + 95972AA312C192DA00BF73D3 /* CGUIInOutFader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF400A484C230014E966 /* CGUIInOutFader.cpp */; }; + 95972AA412C192DA00BF73D3 /* CWriteFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFF20A484C250014E966 /* CWriteFile.cpp */; }; + 95972AA512C192DA00BF73D3 /* CTRTextureGouraud.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFDF0A484C250014E966 /* CTRTextureGouraud.cpp */; }; + 95972AA612C192DA00BF73D3 /* CTRFlat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFD10A484C240014E966 /* CTRFlat.cpp */; }; + 95972AA712C192DA00BF73D3 /* CTerrainTriangleSelector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFCC0A484C240014E966 /* CTerrainTriangleSelector.cpp */; }; + 95972AA812C192DA00BF73D3 /* CGUITabControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF520A484C230014E966 /* CGUITabControl.cpp */; }; + 95972AA912C192DA00BF73D3 /* CParticleBoxEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF9B0A484C240014E966 /* CParticleBoxEmitter.cpp */; }; + 95972AAA12C192DA00BF73D3 /* CGUIMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF440A484C230014E966 /* CGUIMenu.cpp */; }; + 95972AAB12C192DA00BF73D3 /* CImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF580A484C230014E966 /* CImage.cpp */; }; + 95972AAC12C192DA00BF73D3 /* CShadowVolumeSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFBB0A484C240014E966 /* CShadowVolumeSceneNode.cpp */; }; + 95972AAD12C192DA00BF73D3 /* CGUIEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF380A484C230014E966 /* CGUIEnvironment.cpp */; }; + 95972AAE12C192DA00BF73D3 /* CLimitReadFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF6E0A484C230014E966 /* CLimitReadFile.cpp */; }; + 95972AAF12C192DA00BF73D3 /* CAttributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEF00A484C220014E966 /* CAttributes.cpp */; }; + 95972AB012C192DA00BF73D3 /* COpenGLDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF8C0A484C240014E966 /* COpenGLDriver.cpp */; }; + 95972AB112C192DA00BF73D3 /* CTRTextureLightMap2_M2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFEB0A484C250014E966 /* CTRTextureLightMap2_M2.cpp */; }; + 95972AB212C192DA00BF73D3 /* CGUIImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF3E0A484C230014E966 /* CGUIImage.cpp */; }; + 95972AB312C192DA00BF73D3 /* CD3D9NormalMapRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF120A484C230014E966 /* CD3D9NormalMapRenderer.cpp */; }; + 95972AB412C192DA00BF73D3 /* CD3D8NormalMapRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF050A484C220014E966 /* CD3D8NormalMapRenderer.cpp */; }; + 95972AB512C192DA00BF73D3 /* CMeshCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF760A484C230014E966 /* CMeshCache.cpp */; }; + 95972AB612C192DA00BF73D3 /* CImageLoaderJPG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF5C0A484C230014E966 /* CImageLoaderJPG.cpp */; }; + 95972AB712C192DA00BF73D3 /* CFPSCounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF2A0A484C230014E966 /* CFPSCounter.cpp */; }; + 95972AB812C192DA00BF73D3 /* OSXClipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E1670A484C2C0014E966 /* OSXClipboard.mm */; }; + 95972AB912C192DA00BF73D3 /* CIrrDeviceMacOSX.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E15F0A484C2C0014E966 /* CIrrDeviceMacOSX.mm */; }; + 95972ABA12C192DA00BF73D3 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C53E14D0A484C2C0014E966 /* AppDelegate.mm */; }; + 95972ABB12C192DA00BF73D3 /* inflate.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C6DC9B60A48715A0017A6E5 /* inflate.c */; }; + 95972ABC12C192DA00BF73D3 /* CSphereSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CC36B0D0A6B61DB0076C4B2 /* CSphereSceneNode.cpp */; }; + 95972ABD12C192DA00BF73D3 /* COBJMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C364EA20A6C6DC2004CFBB4 /* COBJMeshFileLoader.cpp */; }; + 95972ABE12C192DA00BF73D3 /* CPakReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C43EEBE0A74A5C800F942FC /* CPakReader.cpp */; }; + 95972ABF12C192DA00BF73D3 /* CImageLoaderBMP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BDC0A88735900B03626 /* CImageLoaderBMP.cpp */; }; + 95972AC012C192DA00BF73D3 /* CImageWriterBMP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BDE0A88735900B03626 /* CImageWriterBMP.cpp */; }; + 95972AC112C192DA00BF73D3 /* CImageWriterJPG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BE00A88735900B03626 /* CImageWriterJPG.cpp */; }; + 95972AC212C192DA00BF73D3 /* CImageWriterPCX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BE20A88735900B03626 /* CImageWriterPCX.cpp */; }; + 95972AC312C192DA00BF73D3 /* CImageWriterPNG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BE40A88735900B03626 /* CImageWriterPNG.cpp */; }; + 95972AC412C192DA00BF73D3 /* CImageWriterPPM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BE60A88735900B03626 /* CImageWriterPPM.cpp */; }; + 95972AC512C192DA00BF73D3 /* CImageWriterPSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BE80A88735900B03626 /* CImageWriterPSD.cpp */; }; + 95972AC612C192DA00BF73D3 /* CImageWriterTGA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BEA0A88735900B03626 /* CImageWriterTGA.cpp */; }; + 95972AC712C192DA00BF73D3 /* CSkyDomeSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CFA7BEC0A88735A00B03626 /* CSkyDomeSceneNode.cpp */; }; + 95972AC812C192DA00BF73D3 /* CDepthBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD4804C0C7D91DF00728AA9 /* CDepthBuffer.cpp */; }; + 95972AC912C192DA00BF73D3 /* IBurningShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480500C7D936700728AA9 /* IBurningShader.cpp */; }; + 95972ACA12C192DA00BF73D3 /* CAnimatedMeshMD3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480560C7D945800728AA9 /* CAnimatedMeshMD3.cpp */; }; + 95972ACB12C192DA00BF73D3 /* CDefaultGUIElementFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480580C7D945800728AA9 /* CDefaultGUIElementFactory.cpp */; }; + 95972ACC12C192DA00BF73D3 /* CGUIColorSelectDialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD4805E0C7D947B00728AA9 /* CGUIColorSelectDialog.cpp */; }; + 95972ACD12C192DA00BF73D3 /* CGUISpinBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480600C7D947B00728AA9 /* CGUISpinBox.cpp */; }; + 95972ACE12C192DA00BF73D3 /* CGUISpriteBank.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480620C7D947B00728AA9 /* CGUISpriteBank.cpp */; }; + 95972ACF12C192DA00BF73D3 /* CQuake3ShaderSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD4806A0C7D94AC00728AA9 /* CQuake3ShaderSceneNode.cpp */; }; + 95972AD012C192DA00BF73D3 /* CTRTextureBlend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD4806C0C7D94AC00728AA9 /* CTRTextureBlend.cpp */; }; + 95972AD112C192DA00BF73D3 /* CTRTextureGouraudAlpha.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD4806D0C7D94AC00728AA9 /* CTRTextureGouraudAlpha.cpp */; }; + 95972AD212C192DA00BF73D3 /* CTRTextureGouraudAlphaNoZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD4806E0C7D94AC00728AA9 /* CTRTextureGouraudAlphaNoZ.cpp */; }; + 95972AD312C192DA00BF73D3 /* CTRTextureLightMapGouraud2_M4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD4806F0C7D94AC00728AA9 /* CTRTextureLightMapGouraud2_M4.cpp */; }; + 95972AD412C192DA00BF73D3 /* CIrrDeviceSDL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480C40C7DA66800728AA9 /* CIrrDeviceSDL.cpp */; }; + 95972AD512C192DA00BF73D3 /* COpenGLExtensionHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480C50C7DA66800728AA9 /* COpenGLExtensionHandler.cpp */; }; + 95972AD612C192DA00BF73D3 /* CMD3MeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DD480C60C7DA66800728AA9 /* CMD3MeshFileLoader.cpp */; }; + 95972AD712C192DA00BF73D3 /* CB3DMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0968401E0D0F1A2300333EFD /* CB3DMeshFileLoader.cpp */; }; + 95972AD812C192DA00BF73D3 /* CBoneSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840200D0F1A2300333EFD /* CBoneSceneNode.cpp */; }; + 95972AD912C192DA00BF73D3 /* CBSPMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840220D0F1A2300333EFD /* CBSPMeshFileLoader.cpp */; }; + 95972ADA12C192DA00BF73D3 /* CColladaMeshWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840250D0F1A2300333EFD /* CColladaMeshWriter.cpp */; }; + 95972ADB12C192DA00BF73D3 /* CImageLoaderPPM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840270D0F1A2300333EFD /* CImageLoaderPPM.cpp */; }; + 95972ADC12C192DA00BF73D3 /* CIrrMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0968402B0D0F1A2300333EFD /* CIrrMeshFileLoader.cpp */; }; + 95972ADD12C192DA00BF73D3 /* CIrrMeshWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0968402D0D0F1A2300333EFD /* CIrrMeshWriter.cpp */; }; + 95972ADE12C192DA00BF73D3 /* CMD2MeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0968402F0D0F1A2300333EFD /* CMD2MeshFileLoader.cpp */; }; + 95972ADF12C192DA00BF73D3 /* CMS3DMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840310D0F1A2300333EFD /* CMS3DMeshFileLoader.cpp */; }; + 95972AE012C192DA00BF73D3 /* CParticleAnimatedMeshSceneNodeEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840330D0F1A2300333EFD /* CParticleAnimatedMeshSceneNodeEmitter.cpp */; }; + 95972AE112C192DA00BF73D3 /* CParticleAttractionAffector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840350D0F1A2300333EFD /* CParticleAttractionAffector.cpp */; }; + 95972AE212C192DA00BF73D3 /* CParticleCylinderEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840370D0F1A2300333EFD /* CParticleCylinderEmitter.cpp */; }; + 95972AE312C192DA00BF73D3 /* CParticleMeshEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840390D0F1A2300333EFD /* CParticleMeshEmitter.cpp */; }; + 95972AE412C192DA00BF73D3 /* CParticleRingEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0968403B0D0F1A2300333EFD /* CParticleRingEmitter.cpp */; }; + 95972AE512C192DA00BF73D3 /* CParticleRotationAffector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0968403D0D0F1A2300333EFD /* CParticleRotationAffector.cpp */; }; + 95972AE612C192DA00BF73D3 /* CParticleSphereEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0968403F0D0F1A2300333EFD /* CParticleSphereEmitter.cpp */; }; + 95972AE712C192DA00BF73D3 /* CSkinnedMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840410D0F1A2300333EFD /* CSkinnedMesh.cpp */; }; + 95972AE812C192DA00BF73D3 /* CSTLMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840430D0F1A2300333EFD /* CSTLMeshFileLoader.cpp */; }; + 95972AE912C192DA00BF73D3 /* CSTLMeshWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096840450D0F1A2300333EFD /* CSTLMeshWriter.cpp */; }; + 95972AEA12C192DA00BF73D3 /* CBurningShader_Raster_Reference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0910B9D90D1F5D4100D46B04 /* CBurningShader_Raster_Reference.cpp */; }; + 95972AEB12C192DA00BF73D3 /* CGUITable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0910B9DA0D1F5D4100D46B04 /* CGUITable.cpp */; }; + 95972AEC12C192DA00BF73D3 /* CImageLoaderWAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0910B9DC0D1F5D4100D46B04 /* CImageLoaderWAL.cpp */; }; + 95972AED12C192DA00BF73D3 /* CVolumeLightSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 090FBC800D31085E0076D847 /* CVolumeLightSceneNode.cpp */; }; + 95972AEE12C192DA00BF73D3 /* CLWOMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09C638700D4F1A69000B6A18 /* CLWOMeshFileLoader.cpp */; }; + 95972AEF12C192DA00BF73D3 /* CSceneNodeAnimatorCameraFPS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 093973BC0E0458B200E0C987 /* CSceneNodeAnimatorCameraFPS.cpp */; }; + 95972AF012C192DA00BF73D3 /* CSceneNodeAnimatorCameraMaya.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 093973BE0E0458B200E0C987 /* CSceneNodeAnimatorCameraMaya.cpp */; }; + 95972AF112C192DA00BF73D3 /* COBJMeshWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096F8E3B0EA2EFBA00907EC5 /* COBJMeshWriter.cpp */; }; + 95972AF212C192DA00BF73D3 /* CParticleScaleAffector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 096CC0DE0ECE65B500C81DC7 /* CParticleScaleAffector.cpp */; }; + 95972AF312C192DA00BF73D3 /* png.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C2C0ED32029003B8C9C /* png.c */; }; + 95972AF412C192DA00BF73D3 /* pngerror.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C2D0ED32029003B8C9C /* pngerror.c */; }; + 95972AF612C192DA00BF73D3 /* pngget.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C2F0ED32029003B8C9C /* pngget.c */; }; + 95972AF712C192DA00BF73D3 /* pngmem.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C300ED32029003B8C9C /* pngmem.c */; }; + 95972AF812C192DA00BF73D3 /* pngpread.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C310ED32029003B8C9C /* pngpread.c */; }; + 95972AF912C192DA00BF73D3 /* pngread.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C320ED32029003B8C9C /* pngread.c */; }; + 95972AFA12C192DA00BF73D3 /* pngrio.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C330ED32029003B8C9C /* pngrio.c */; }; + 95972AFB12C192DA00BF73D3 /* pngrtran.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C340ED32029003B8C9C /* pngrtran.c */; }; + 95972AFC12C192DA00BF73D3 /* pngrutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C350ED32029003B8C9C /* pngrutil.c */; }; + 95972AFD12C192DA00BF73D3 /* pngset.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C360ED32029003B8C9C /* pngset.c */; }; + 95972AFE12C192DA00BF73D3 /* pngtrans.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C380ED32029003B8C9C /* pngtrans.c */; }; + 95972AFF12C192DA00BF73D3 /* pngwio.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C3A0ED32029003B8C9C /* pngwio.c */; }; + 95972B0012C192DA00BF73D3 /* pngwrite.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C3B0ED32029003B8C9C /* pngwrite.c */; }; + 95972B0112C192DA00BF73D3 /* pngwtran.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C3C0ED32029003B8C9C /* pngwtran.c */; }; + 95972B0212C192DA00BF73D3 /* pngwutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 09293C3D0ED32029003B8C9C /* pngwutil.c */; }; + 95972B0312C192DA00BF73D3 /* CGUIImageList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3484C4E00F48D1B000C81F60 /* CGUIImageList.cpp */; }; + 95972B0412C192DA00BF73D3 /* CGUITreeView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3484C4ED0F48D3A100C81F60 /* CGUITreeView.cpp */; }; + 95972B0512C192DA00BF73D3 /* CMemoryFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3484C4FC0F48D4CB00C81F60 /* CMemoryFile.cpp */; }; + 95972B0612C192DA00BF73D3 /* CIrrDeviceConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34EC243B0F59272E0037BC3A /* CIrrDeviceConsole.cpp */; }; + 95972B0712C192DA00BF73D3 /* CImageLoaderRGB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34EF91D10F65FCA6000B5651 /* CImageLoaderRGB.cpp */; }; + 95972B0812C192DA00BF73D3 /* CPLYMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34EF91D60F65FCF6000B5651 /* CPLYMeshFileLoader.cpp */; }; + 95972B0912C192DA00BF73D3 /* CPLYMeshWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34EF91DB0F65FD14000B5651 /* CPLYMeshWriter.cpp */; }; + 95972B0A12C192DA00BF73D3 /* CTarReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3430E4D51022C391006271FD /* CTarReader.cpp */; }; + 95972B0B12C192DA00BF73D3 /* CMountPointReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 344FD4A41039E98C0045FD3F /* CMountPointReader.cpp */; }; + 95972B0C12C192DA00BF73D3 /* jaricom.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C431103B1B5002DE8D7 /* jaricom.c */; }; + 95972B0D12C192DA00BF73D3 /* jcarith.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C441103B1B5002DE8D7 /* jcarith.c */; }; + 95972B0E12C192DA00BF73D3 /* jdarith.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C451103B1B5002DE8D7 /* jdarith.c */; }; + 95972B0F12C192DA00BF73D3 /* COctreeSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C4B1103B247002DE8D7 /* COctreeSceneNode.cpp */; }; + 95972B1012C192DA00BF73D3 /* COctreeTriangleSelector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C4F1103B261002DE8D7 /* COctreeTriangleSelector.cpp */; }; + 95972B1112C192DA00BF73D3 /* CNPKReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C531103B27D002DE8D7 /* CNPKReader.cpp */; }; + 95972B1212C192DA00BF73D3 /* CIrrDeviceFB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C571103B2AE002DE8D7 /* CIrrDeviceFB.cpp */; }; + 95972B1312C192DA00BF73D3 /* CIrrDeviceWinCE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C591103B2AE002DE8D7 /* CIrrDeviceWinCE.cpp */; }; + 95972B1412C192DA00BF73D3 /* LzmaDec.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C631103B384002DE8D7 /* LzmaDec.c */; }; + 95972B1512C192DA00BF73D3 /* blocksort.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C661103B3B9002DE8D7 /* blocksort.c */; }; + 95972B1612C192DA00BF73D3 /* bzcompress.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C671103B3B9002DE8D7 /* bzcompress.c */; }; + 95972B1712C192DA00BF73D3 /* crctable.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C6A1103B3B9002DE8D7 /* crctable.c */; }; + 95972B1812C192DA00BF73D3 /* decompress.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C6B1103B3B9002DE8D7 /* decompress.c */; }; + 95972B1912C192DA00BF73D3 /* huffman.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C6C1103B3B9002DE8D7 /* huffman.c */; }; + 95972B1A12C192DA00BF73D3 /* randtable.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C6E1103B3B9002DE8D7 /* randtable.c */; }; + 95972B1B12C192DA00BF73D3 /* bzlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C7B1103B4E1002DE8D7 /* bzlib.c */; }; + 95972B1C12C192DA00BF73D3 /* aescrypt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C7E1103B53C002DE8D7 /* aescrypt.cpp */; }; + 95972B1D12C192DA00BF73D3 /* aeskey.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C7F1103B53C002DE8D7 /* aeskey.cpp */; }; + 95972B1E12C192DA00BF73D3 /* aestab.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C801103B53C002DE8D7 /* aestab.cpp */; }; + 95972B1F12C192DA00BF73D3 /* fileenc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C811103B53C002DE8D7 /* fileenc.cpp */; }; + 95972B2012C192DA00BF73D3 /* hmac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C821103B53C002DE8D7 /* hmac.cpp */; }; + 95972B2112C192DA00BF73D3 /* prng.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C831103B53C002DE8D7 /* prng.cpp */; }; + 95972B2212C192DA00BF73D3 /* pwd2key.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C841103B53C002DE8D7 /* pwd2key.cpp */; }; + 95972B2312C192DA00BF73D3 /* sha1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C851103B53C002DE8D7 /* sha1.cpp */; }; + 95972B2412C192DA00BF73D3 /* sha2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E3C861103B53C002DE8D7 /* sha2.cpp */; }; + 95972B8412C19A5C00BF73D3 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95972B8312C19A5C00BF73D3 /* OpenGL.framework */; }; + 95972B8A12C19A7600BF73D3 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95972B8912C19A7600BF73D3 /* IOKit.framework */; }; + 95972B8E12C19A7F00BF73D3 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95972B8D12C19A7F00BF73D3 /* Carbon.framework */; }; + 95E5857112FCE277004946C6 /* CWADReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5857012FCE277004946C6 /* CWADReader.cpp */; }; + 95E5857212FCE277004946C6 /* CWADReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5857012FCE277004946C6 /* CWADReader.cpp */; }; + 95E5857712FCE2CB004946C6 /* CAnimatedMeshHalfLife.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5857512FCE2CB004946C6 /* CAnimatedMeshHalfLife.cpp */; }; + 95E5857812FCE2CB004946C6 /* CAnimatedMeshHalfLife.h in Headers */ = {isa = PBXBuildFile; fileRef = 95E5857612FCE2CB004946C6 /* CAnimatedMeshHalfLife.h */; }; + 95E5857912FCE2CB004946C6 /* CAnimatedMeshHalfLife.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5857512FCE2CB004946C6 /* CAnimatedMeshHalfLife.cpp */; }; + 95E5857C12FCE2DE004946C6 /* CSceneLoaderIrr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5857B12FCE2DE004946C6 /* CSceneLoaderIrr.cpp */; }; + 95E5857D12FCE2DE004946C6 /* CSceneLoaderIrr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5857B12FCE2DE004946C6 /* CSceneLoaderIrr.cpp */; }; + 95E5858D12FCE388004946C6 /* CTRNormalMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5858C12FCE388004946C6 /* CTRNormalMap.cpp */; }; + 95E5858E12FCE388004946C6 /* CTRNormalMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5858C12FCE388004946C6 /* CTRNormalMap.cpp */; }; + 95E5859212FCE3A1004946C6 /* CTRStencilShadow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5859112FCE3A1004946C6 /* CTRStencilShadow.cpp */; }; + 95E5859312FCE3A1004946C6 /* CTRStencilShadow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5859112FCE3A1004946C6 /* CTRStencilShadow.cpp */; }; + 95E5859512FCE3F5004946C6 /* CSMFMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5859412FCE3F5004946C6 /* CSMFMeshFileLoader.cpp */; }; + 95E5859612FCE3F5004946C6 /* CSMFMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5859412FCE3F5004946C6 /* CSMFMeshFileLoader.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -957,7 +1438,6 @@ 0925113D0D744ADE006784D9 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; 09293C2C0ED32029003B8C9C /* png.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = png.c; path = libpng/png.c; sourceTree = ""; }; 09293C2D0ED32029003B8C9C /* pngerror.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = pngerror.c; path = libpng/pngerror.c; sourceTree = ""; }; - 09293C2E0ED32029003B8C9C /* pnggccrd.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = pnggccrd.c; path = libpng/pnggccrd.c; sourceTree = ""; }; 09293C2F0ED32029003B8C9C /* pngget.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = pngget.c; path = libpng/pngget.c; sourceTree = ""; }; 09293C300ED32029003B8C9C /* pngmem.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = pngmem.c; path = libpng/pngmem.c; sourceTree = ""; }; 09293C310ED32029003B8C9C /* pngpread.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = pngpread.c; path = libpng/pngpread.c; sourceTree = ""; }; @@ -1062,6 +1542,13 @@ 0E2E3D3C1103E3F4002DE8D7 /* ManagedLights.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ManagedLights.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0E2E3D681103E6C6002DE8D7 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 0E2E3D791103E6E4002DE8D7 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + 0E2E3E1B1103F773002DE8D7 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + 0E2E3E1D1103F773002DE8D7 /* q3factory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = q3factory.cpp; sourceTree = ""; }; + 0E2E3E1E1103F773002DE8D7 /* q3factory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = q3factory.h; sourceTree = ""; }; + 0E2E3E261103F773002DE8D7 /* sound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sound.cpp; sourceTree = ""; }; + 0E2E3E271103F773002DE8D7 /* sound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sound.h; sourceTree = ""; }; + 0E2E3E291103F773002DE8D7 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + 0E2E3E321103F773002DE8D7 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 3430E4D41022C391006271FD /* CTarReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTarReader.h; sourceTree = ""; }; 3430E4D51022C391006271FD /* CTarReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CTarReader.cpp; sourceTree = ""; }; 344FD4A41039E98C0045FD3F /* CMountPointReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CMountPointReader.cpp; sourceTree = ""; }; @@ -1389,7 +1876,6 @@ 4C53E1750A484C2C0014E966 /* compress.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = compress.c; sourceTree = ""; }; 4C53E1770A484C2C0014E966 /* crc32.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = crc32.c; sourceTree = ""; }; 4C53E1790A484C2C0014E966 /* deflate.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = deflate.c; sourceTree = ""; }; - 4C53E17D0A484C2C0014E966 /* gzio.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = gzio.c; sourceTree = ""; }; 4C53E1800A484C2C0014E966 /* inffast.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = inffast.c; sourceTree = ""; }; 4C53E1850A484C2C0014E966 /* inftrees.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = inftrees.c; sourceTree = ""; }; 4C53E18B0A484C2C0014E966 /* trees.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = trees.c; sourceTree = ""; }; @@ -1630,6 +2116,22 @@ 5DD480C40C7DA66800728AA9 /* CIrrDeviceSDL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CIrrDeviceSDL.cpp; sourceTree = ""; }; 5DD480C50C7DA66800728AA9 /* COpenGLExtensionHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = COpenGLExtensionHandler.cpp; sourceTree = ""; }; 5DD480C60C7DA66800728AA9 /* CMD3MeshFileLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CMD3MeshFileLoader.cpp; sourceTree = ""; }; + 959726FD12C18FFC00BF73D3 /* IrrFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = IrrFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 959726FE12C18FFC00BF73D3 /* IrrFramework-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "IrrFramework-Info.plist"; path = "svn/irrlicht1.7/source/Irrlicht/MacOSX/IrrFramework-Info.plist"; sourceTree = SYSTEM_DEVELOPER_DIR; }; + 95972B8312C19A5C00BF73D3 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; + 95972B8912C19A7600BF73D3 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 95972B8D12C19A7F00BF73D3 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; + 95E5857012FCE277004946C6 /* CWADReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CWADReader.cpp; path = ../CWADReader.cpp; sourceTree = SOURCE_ROOT; }; + 95E5857512FCE2CB004946C6 /* CAnimatedMeshHalfLife.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAnimatedMeshHalfLife.cpp; path = ../CAnimatedMeshHalfLife.cpp; sourceTree = SOURCE_ROOT; }; + 95E5857612FCE2CB004946C6 /* CAnimatedMeshHalfLife.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAnimatedMeshHalfLife.h; path = ../CAnimatedMeshHalfLife.h; sourceTree = SOURCE_ROOT; }; + 95E5857B12FCE2DE004946C6 /* CSceneLoaderIrr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSceneLoaderIrr.cpp; path = ../CSceneLoaderIrr.cpp; sourceTree = SOURCE_ROOT; }; + 95E5858C12FCE388004946C6 /* CTRNormalMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTRNormalMap.cpp; path = ../CTRNormalMap.cpp; sourceTree = SOURCE_ROOT; }; + 95E5859112FCE3A1004946C6 /* CTRStencilShadow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTRStencilShadow.cpp; path = ../CTRStencilShadow.cpp; sourceTree = SOURCE_ROOT; }; + 95E5859412FCE3F5004946C6 /* CSMFMeshFileLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSMFMeshFileLoader.cpp; path = ../CSMFMeshFileLoader.cpp; sourceTree = SOURCE_ROOT; }; + 95E9D50210F42F9A008546FE /* jcarith.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = jcarith.c; path = ../jpeglib/jcarith.c; sourceTree = SOURCE_ROOT; }; + 95E9D50610F42FDF008546FE /* jdarith.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = jdarith.c; path = ../jpeglib/jdarith.c; sourceTree = SOURCE_ROOT; }; + 95E9D50A10F43011008546FE /* jaricom.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = jaricom.c; path = ../jpeglib/jaricom.c; sourceTree = SOURCE_ROOT; }; + 95E9D50E10F43194008546FE /* CNPKReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CNPKReader.cpp; path = ../CNPKReader.cpp; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1717,6 +2219,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 959726FB12C18FFB00BF73D3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 95972B8412C19A5C00BF73D3 /* OpenGL.framework in Frameworks */, + 95972B8A12C19A7600BF73D3 /* IOKit.framework in Frameworks */, + 95972B8E12C19A7F00BF73D3 /* Carbon.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; B81CFE08097FD9F50057C06F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1892,6 +2404,11 @@ 4C00546D0A48470500C844C2 /* examples */, 4C53E2540A48505D0014E966 /* Libraries */, 4C53E24C0A484FED0014E966 /* Products */, + 959726FD12C18FFC00BF73D3 /* IrrFramework.framework */, + 959726FE12C18FFC00BF73D3 /* IrrFramework-Info.plist */, + 95972B8312C19A5C00BF73D3 /* OpenGL.framework */, + 95972B8912C19A7600BF73D3 /* IOKit.framework */, + 95972B8D12C19A7F00BF73D3 /* Carbon.framework */, ); name = MacOSX; sourceTree = ""; @@ -2196,6 +2713,7 @@ 4C53DF770A484C230014E966 /* CMeshCache.h */, 4C53DF780A484C230014E966 /* CMeshManipulator.cpp */, 4C53DF790A484C230014E966 /* CMeshManipulator.h */, + 95E9D50E10F43194008546FE /* CNPKReader.cpp */, 4C53DFAB0A484C240014E966 /* CSceneManager.cpp */, 4C53DFAC0A484C240014E966 /* CSceneManager.h */, ); @@ -2583,7 +3101,6 @@ children = ( 09293C2C0ED32029003B8C9C /* png.c */, 09293C2D0ED32029003B8C9C /* pngerror.c */, - 09293C2E0ED32029003B8C9C /* pnggccrd.c */, 09293C2F0ED32029003B8C9C /* pngget.c */, 09293C300ED32029003B8C9C /* pngmem.c */, 09293C310ED32029003B8C9C /* pngpread.c */, @@ -2679,6 +3196,34 @@ path = 20.ManagedLights; sourceTree = ""; }; + 0E2E3E1A1103F773002DE8D7 /* 21.Quake3Explorer */ = { + isa = PBXGroup; + children = ( + 0E2E3E1B1103F773002DE8D7 /* main.cpp */, + 0E2E3E1D1103F773002DE8D7 /* q3factory.cpp */, + 0E2E3E1E1103F773002DE8D7 /* q3factory.h */, + 0E2E3E261103F773002DE8D7 /* sound.cpp */, + 0E2E3E271103F773002DE8D7 /* sound.h */, + ); + path = 21.Quake3Explorer; + sourceTree = ""; + }; + 0E2E3E281103F773002DE8D7 /* 22.MaterialViewer */ = { + isa = PBXGroup; + children = ( + 0E2E3E291103F773002DE8D7 /* main.cpp */, + ); + path = 22.MaterialViewer; + sourceTree = ""; + }; + 0E2E3E311103F773002DE8D7 /* 23.SMeshHandling */ = { + isa = PBXGroup; + children = ( + 0E2E3E321103F773002DE8D7 /* main.cpp */, + ); + path = 23.SMeshHandling; + sourceTree = ""; + }; 34EF91900F65F9AD000B5651 /* loader */ = { isa = PBXGroup; children = ( @@ -2870,6 +3415,9 @@ 0E2E3D671103E6C6002DE8D7 /* 18.SplitScreen */, 0946CC980EC99B8B00D945A5 /* 19.MouseAndJoystick */, 0E2E3D781103E6E4002DE8D7 /* 20.ManagedLights */, + 0E2E3E1A1103F773002DE8D7 /* 21.Quake3Explorer */, + 0E2E3E281103F773002DE8D7 /* 22.MaterialViewer */, + 0E2E3E311103F773002DE8D7 /* 23.SMeshHandling */, 4C0054C40A48470500C844C2 /* Demo */, ); name = examples; @@ -3010,6 +3558,7 @@ 0E2E3C451103B1B5002DE8D7 /* jdarith.c */, 4C53E6F10A485CD80014E966 /* jcapimin.c */, 4C53E6F20A485CD80014E966 /* jcapistd.c */, + 95E9D50210F42F9A008546FE /* jcarith.c */, 4C53E6F30A485CD80014E966 /* jccoefct.c */, 4C53E6F40A485CD80014E966 /* jccolor.c */, 4C53E6F50A485CD80014E966 /* jcdctmgr.c */, @@ -3024,7 +3573,9 @@ 4C53E70D0A485CD80014E966 /* jcsample.c */, 4C53E70E0A485CD80014E966 /* jctrans.c */, 4C53E70F0A485CD80014E966 /* jdapimin.c */, + 95E9D50A10F43011008546FE /* jaricom.c */, 4C53E7100A485CD80014E966 /* jdapistd.c */, + 95E9D50610F42FDF008546FE /* jdarith.c */, 4C53E7110A485CD80014E966 /* jdatadst.c */, 4C53E7120A485CD80014E966 /* jdatasrc.c */, 4C53E7130A485CD80014E966 /* jdcoefct.c */, @@ -3123,7 +3674,6 @@ 4C53E1750A484C2C0014E966 /* compress.c */, 4C53E1770A484C2C0014E966 /* crc32.c */, 4C53E1790A484C2C0014E966 /* deflate.c */, - 4C53E17D0A484C2C0014E966 /* gzio.c */, 4C53E1800A484C2C0014E966 /* inffast.c */, 4C53E1850A484C2C0014E966 /* inftrees.c */, 4C53E18B0A484C2C0014E966 /* trees.c */, @@ -3175,6 +3725,13 @@ 4C6DC9960A486B110017A6E5 /* Engine */ = { isa = PBXGroup; children = ( + 95E5858C12FCE388004946C6 /* CTRNormalMap.cpp */, + 95E5857512FCE2CB004946C6 /* CAnimatedMeshHalfLife.cpp */, + 95E5859112FCE3A1004946C6 /* CTRStencilShadow.cpp */, + 95E5857612FCE2CB004946C6 /* CAnimatedMeshHalfLife.h */, + 95E5857B12FCE2DE004946C6 /* CSceneLoaderIrr.cpp */, + 95E5859412FCE3F5004946C6 /* CSMFMeshFileLoader.cpp */, + 95E5857012FCE277004946C6 /* CWADReader.cpp */, 0910BA810D1F6BB800D46B04 /* gui */, 0910BA820D1F6C3900D46B04 /* io */, 0910BA830D1F6CA600D46B04 /* irr */, @@ -3209,6 +3766,166 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 959726F812C18FFB00BF73D3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 95154774133CD9DA008D792F /* aabbox3d.h in Headers */, + 95154775133CD9DA008D792F /* CMeshBuffer.h in Headers */, + 95154776133CD9DA008D792F /* coreutil.h in Headers */, + 95154777133CD9DA008D792F /* dimension2d.h in Headers */, + 95154778133CD9DA008D792F /* ECullingTypes.h in Headers */, + 95154779133CD9DA008D792F /* EDebugSceneTypes.h in Headers */, + 9515477A133CD9DA008D792F /* EDriverFeatures.h in Headers */, + 9515477B133CD9DA008D792F /* EDriverTypes.h in Headers */, + 9515477C133CD9DA008D792F /* EGUIAlignment.h in Headers */, + 9515477D133CD9DA008D792F /* EGUIElementTypes.h in Headers */, + 9515477E133CD9DA008D792F /* EHardwareBufferFlags.h in Headers */, + 9515477F133CD9DA008D792F /* EMaterialFlags.h in Headers */, + 95154780133CD9DA008D792F /* EMaterialTypes.h in Headers */, + 95154781133CD9DA008D792F /* EMeshWriterEnums.h in Headers */, + 95154782133CD9DA008D792F /* EMessageBoxFlags.h in Headers */, + 95154783133CD9DA008D792F /* ESceneNodeAnimatorTypes.h in Headers */, + 95154784133CD9DA008D792F /* ESceneNodeTypes.h in Headers */, + 95154785133CD9DA008D792F /* ETerrainElements.h in Headers */, + 95154786133CD9DA008D792F /* heapsort.h in Headers */, + 95154787133CD9DA008D792F /* IAnimatedMesh.h in Headers */, + 95154788133CD9DA008D792F /* IAnimatedMeshMD2.h in Headers */, + 95154789133CD9DA008D792F /* IAnimatedMeshMD3.h in Headers */, + 9515478A133CD9DA008D792F /* IAnimatedMeshSceneNode.h in Headers */, + 9515478B133CD9DA008D792F /* IAttributeExchangingObject.h in Headers */, + 9515478C133CD9DA008D792F /* IAttributes.h in Headers */, + 9515478D133CD9DA008D792F /* IBillboardSceneNode.h in Headers */, + 9515478E133CD9DA008D792F /* IBoneSceneNode.h in Headers */, + 9515478F133CD9DA008D792F /* ICameraSceneNode.h in Headers */, + 95154790133CD9DA008D792F /* ICursorControl.h in Headers */, + 95154791133CD9DA008D792F /* IDummyTransformationSceneNode.h in Headers */, + 95154792133CD9DA008D792F /* IEventReceiver.h in Headers */, + 95154793133CD9DA008D792F /* IFileList.h in Headers */, + 95154794133CD9DA008D792F /* IFileSystem.h in Headers */, + 95154795133CD9DA008D792F /* IGPUProgrammingServices.h in Headers */, + 95154796133CD9DA008D792F /* IGUIButton.h in Headers */, + 95154797133CD9DA008D792F /* IGUICheckBox.h in Headers */, + 95154798133CD9DA008D792F /* IGUIColorSelectDialog.h in Headers */, + 95154799133CD9DA008D792F /* IGUIComboBox.h in Headers */, + 9515479A133CD9DA008D792F /* IGUIContextMenu.h in Headers */, + 9515479B133CD9DA008D792F /* IGUIEditBox.h in Headers */, + 9515479C133CD9DA008D792F /* IGUIElement.h in Headers */, + 9515479D133CD9DA008D792F /* IGUIElementFactory.h in Headers */, + 9515479E133CD9DA008D792F /* IGUIEnvironment.h in Headers */, + 9515479F133CD9DA008D792F /* IGUIFileOpenDialog.h in Headers */, + 951547A0133CD9DA008D792F /* IGUIFont.h in Headers */, + 951547A1133CD9DA008D792F /* IGUIFontBitmap.h in Headers */, + 951547A2133CD9DA008D792F /* IGUIImage.h in Headers */, + 951547A3133CD9DA008D792F /* IGUIInOutFader.h in Headers */, + 951547A4133CD9DA008D792F /* IGUIListBox.h in Headers */, + 951547A5133CD9DA008D792F /* IGUIMeshViewer.h in Headers */, + 951547A6133CD9DA008D792F /* IGUIScrollBar.h in Headers */, + 951547A7133CD9DA008D792F /* IGUISkin.h in Headers */, + 951547A8133CD9DA008D792F /* IGUISpinBox.h in Headers */, + 951547A9133CD9DA008D792F /* IGUISpriteBank.h in Headers */, + 951547AA133CD9DA008D792F /* IGUIStaticText.h in Headers */, + 951547AB133CD9DA008D792F /* IGUITabControl.h in Headers */, + 951547AC133CD9DA008D792F /* IGUITable.h in Headers */, + 951547AD133CD9DA008D792F /* IGUIToolbar.h in Headers */, + 951547AE133CD9DA008D792F /* IGUIWindow.h in Headers */, + 951547AF133CD9DA008D792F /* IImage.h in Headers */, + 951547B0133CD9DA008D792F /* IImageLoader.h in Headers */, + 951547B1133CD9DA008D792F /* IImageWriter.h in Headers */, + 951547B2133CD9DA008D792F /* ILightSceneNode.h in Headers */, + 951547B3133CD9DA008D792F /* ILogger.h in Headers */, + 951547B4133CD9DA008D792F /* IMaterialRenderer.h in Headers */, + 951547B5133CD9DA008D792F /* IMaterialRendererServices.h in Headers */, + 951547B6133CD9DA008D792F /* IMesh.h in Headers */, + 951547B7133CD9DA008D792F /* IMeshBuffer.h in Headers */, + 951547B8133CD9DA008D792F /* IMeshCache.h in Headers */, + 951547B9133CD9DA008D792F /* IMeshLoader.h in Headers */, + 951547BA133CD9DA008D792F /* IMeshManipulator.h in Headers */, + 951547BB133CD9DA008D792F /* IMeshSceneNode.h in Headers */, + 951547BC133CD9DA008D792F /* IMeshWriter.h in Headers */, + 951547BD133CD9DA008D792F /* IMetaTriangleSelector.h in Headers */, + 951547BE133CD9DA008D792F /* IOSOperator.h in Headers */, + 951547BF133CD9DA008D792F /* IParticleAffector.h in Headers */, + 951547C0133CD9DA008D792F /* IParticleAnimatedMeshSceneNodeEmitter.h in Headers */, + 951547C1133CD9DA008D792F /* IParticleAttractionAffector.h in Headers */, + 951547C2133CD9DA008D792F /* IParticleBoxEmitter.h in Headers */, + 951547C3133CD9DA008D792F /* IParticleCylinderEmitter.h in Headers */, + 951547C4133CD9DA008D792F /* IParticleEmitter.h in Headers */, + 951547C5133CD9DA008D792F /* IParticleFadeOutAffector.h in Headers */, + 951547C6133CD9DA008D792F /* IParticleGravityAffector.h in Headers */, + 951547C7133CD9DA008D792F /* IParticleMeshEmitter.h in Headers */, + 951547C8133CD9DA008D792F /* IParticleRingEmitter.h in Headers */, + 951547C9133CD9DA008D792F /* IParticleRotationAffector.h in Headers */, + 951547CA133CD9DA008D792F /* IParticleSphereEmitter.h in Headers */, + 951547CB133CD9DA008D792F /* IParticleSystemSceneNode.h in Headers */, + 951547CC133CD9DA008D792F /* IQ3LevelMesh.h in Headers */, + 951547CD133CD9DA008D792F /* IQ3Shader.h in Headers */, + 951547CE133CD9DA008D792F /* IReadFile.h in Headers */, + 951547CF133CD9DA008D792F /* IReferenceCounted.h in Headers */, + 951547D0133CD9DA008D792F /* irrAllocator.h in Headers */, + 951547D1133CD9DA008D792F /* irrArray.h in Headers */, + 951547D2133CD9DA008D792F /* IrrCompileConfig.h in Headers */, + 951547D3133CD9DA008D792F /* irrlicht.h in Headers */, + 951547D4133CD9DA008D792F /* IrrlichtDevice.h in Headers */, + 951547D5133CD9DA008D792F /* irrList.h in Headers */, + 951547D6133CD9DA008D792F /* irrMap.h in Headers */, + 951547D7133CD9DA008D792F /* irrMath.h in Headers */, + 951547D8133CD9DA008D792F /* irrString.h in Headers */, + 951547D9133CD9DA008D792F /* irrTypes.h in Headers */, + 951547DA133CD9DA008D792F /* irrXML.h in Headers */, + 951547DB133CD9DA008D792F /* ISceneCollisionManager.h in Headers */, + 951547DC133CD9DA008D792F /* ISceneManager.h in Headers */, + 951547DD133CD9DA008D792F /* ISceneNode.h in Headers */, + 951547DE133CD9DA008D792F /* ISceneNodeAnimator.h in Headers */, + 951547DF133CD9DA008D792F /* ISceneNodeAnimatorCollisionResponse.h in Headers */, + 951547E0133CD9DA008D792F /* ISceneNodeAnimatorFactory.h in Headers */, + 951547E1133CD9DA008D792F /* ISceneNodeFactory.h in Headers */, + 951547E2133CD9DA008D792F /* ISceneUserDataSerializer.h in Headers */, + 951547E3133CD9DA008D792F /* IShaderConstantSetCallBack.h in Headers */, + 951547E4133CD9DA008D792F /* IShadowVolumeSceneNode.h in Headers */, + 951547E5133CD9DA008D792F /* ISkinnedMesh.h in Headers */, + 951547E6133CD9DA008D792F /* ITerrainSceneNode.h in Headers */, + 951547E7133CD9DA008D792F /* ITextSceneNode.h in Headers */, + 951547E8133CD9DA008D792F /* ITexture.h in Headers */, + 951547E9133CD9DA008D792F /* ITimer.h in Headers */, + 951547EA133CD9DA008D792F /* ITriangleSelector.h in Headers */, + 951547EB133CD9DA008D792F /* IVideoDriver.h in Headers */, + 951547EC133CD9DA008D792F /* IVideoModeList.h in Headers */, + 951547ED133CD9DA008D792F /* IWriteFile.h in Headers */, + 951547EE133CD9DA008D792F /* IXMLReader.h in Headers */, + 951547EF133CD9DA008D792F /* IXMLWriter.h in Headers */, + 951547F0133CD9DA008D792F /* Keycodes.h in Headers */, + 951547F1133CD9DA008D792F /* line2d.h in Headers */, + 951547F2133CD9DA008D792F /* line3d.h in Headers */, + 951547F3133CD9DA008D792F /* matrix4.h in Headers */, + 951547F4133CD9DA008D792F /* plane3d.h in Headers */, + 951547F5133CD9DA008D792F /* position2d.h in Headers */, + 951547F6133CD9DA008D792F /* quaternion.h in Headers */, + 951547F7133CD9DA008D792F /* rect.h in Headers */, + 951547F8133CD9DA008D792F /* S3DVertex.h in Headers */, + 951547F9133CD9DA008D792F /* SAnimatedMesh.h in Headers */, + 951547FA133CD9DA008D792F /* SceneParameters.h in Headers */, + 951547FB133CD9DA008D792F /* SColor.h in Headers */, + 951547FC133CD9DA008D792F /* SExposedVideoData.h in Headers */, + 951547FD133CD9DA008D792F /* SIrrCreationParameters.h in Headers */, + 951547FE133CD9DA008D792F /* SKeyMap.h in Headers */, + 951547FF133CD9DA008D792F /* SLight.h in Headers */, + 95154800133CD9DA008D792F /* SMaterial.h in Headers */, + 95154801133CD9DA008D792F /* SMaterialLayer.h in Headers */, + 95154802133CD9DA008D792F /* SMesh.h in Headers */, + 95154803133CD9DA008D792F /* SMeshBuffer.h in Headers */, + 95154804133CD9DA008D792F /* SMeshBufferLightMap.h in Headers */, + 95154805133CD9DA008D792F /* SMeshBufferTangents.h in Headers */, + 95154806133CD9DA008D792F /* SParticle.h in Headers */, + 95154807133CD9DA008D792F /* SSharedMeshBuffer.h in Headers */, + 95154808133CD9DA008D792F /* SSkinMeshBuffer.h in Headers */, + 95154809133CD9DA008D792F /* SViewFrustum.h in Headers */, + 9515480A133CD9DA008D792F /* triangle3d.h in Headers */, + 9515480B133CD9DA008D792F /* vector2d.h in Headers */, + 9515480C133CD9DA008D792F /* vector3d.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D2AAC07A0554694100DB518D /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -3307,6 +4024,7 @@ 0E2E3C561103B27D002DE8D7 /* CNPKReader.h in Headers */, 0E2E3C5C1103B2AE002DE8D7 /* CIrrDeviceFB.h in Headers */, 0E2E3C5E1103B2AE002DE8D7 /* CIrrDeviceWinCE.h in Headers */, + 95E5857812FCE2CB004946C6 /* CAnimatedMeshHalfLife.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3439,6 +4157,24 @@ productReference = 0E2E3D3C1103E3F4002DE8D7 /* ManagedLights.app */; productType = "com.apple.product-type.application"; }; + 959726FC12C18FFB00BF73D3 /* IrrFramework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9597270112C18FFD00BF73D3 /* Build configuration list for PBXNativeTarget "IrrFramework" */; + buildPhases = ( + 959726F812C18FFB00BF73D3 /* Headers */, + 959726F912C18FFB00BF73D3 /* Resources */, + 959726FA12C18FFB00BF73D3 /* Sources */, + 959726FB12C18FFB00BF73D3 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = IrrFramework; + productName = IrrFramework; + productReference = 959726FD12C18FFC00BF73D3 /* IrrFramework.framework */; + productType = "com.apple.product-type.framework"; + }; B81CFDFE097FD9F50057C06F /* 06.2DGraphics */ = { isa = PBXNativeTarget; buildConfigurationList = B81CFE0C097FD9F50057C06F /* Build configuration list for PBXNativeTarget "06.2DGraphics" */; @@ -3697,7 +4433,14 @@ isa = PBXProject; buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "MacOSX" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); mainGroup = 0867D691FE84028FC02AAC07 /* MacOSX */; productRefGroup = 0867D691FE84028FC02AAC07 /* MacOSX */; projectDirPath = ""; @@ -3725,6 +4468,7 @@ B8DEF35C0950229200FDEA7E /* Demo */, 09022C520EA0E97F00CD54EE /* GUIEditor */, B81CFFC6097FE9980057C06F /* All */, + 959726FC12C18FFB00BF73D3 /* IrrFramework */, ); }; /* End PBXProject section */ @@ -3786,6 +4530,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 959726F912C18FFB00BF73D3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B81CFE01097FD9F50057C06F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -3956,6 +4707,333 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 959726FA12C18FFB00BF73D3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 959729E912C192DA00BF73D3 /* jcapimin.c in Sources */, + 959729EA12C192DA00BF73D3 /* jcapistd.c in Sources */, + 959729EB12C192DA00BF73D3 /* jccoefct.c in Sources */, + 959729EC12C192DA00BF73D3 /* jccolor.c in Sources */, + 959729ED12C192DA00BF73D3 /* jcdctmgr.c in Sources */, + 959729EE12C192DA00BF73D3 /* jchuff.c in Sources */, + 959729EF12C192DA00BF73D3 /* jcinit.c in Sources */, + 959729F012C192DA00BF73D3 /* jcmainct.c in Sources */, + 959729F112C192DA00BF73D3 /* jcmarker.c in Sources */, + 959729F212C192DA00BF73D3 /* jcmaster.c in Sources */, + 959729F312C192DA00BF73D3 /* jcomapi.c in Sources */, + 959729F412C192DA00BF73D3 /* jcparam.c in Sources */, + 959729F512C192DA00BF73D3 /* jcprepct.c in Sources */, + 959729F612C192DA00BF73D3 /* jcsample.c in Sources */, + 959729F712C192DA00BF73D3 /* jctrans.c in Sources */, + 959729F812C192DA00BF73D3 /* jdapimin.c in Sources */, + 959729F912C192DA00BF73D3 /* jdapistd.c in Sources */, + 959729FA12C192DA00BF73D3 /* jdatadst.c in Sources */, + 959729FB12C192DA00BF73D3 /* jdatasrc.c in Sources */, + 959729FC12C192DA00BF73D3 /* jdcoefct.c in Sources */, + 959729FD12C192DA00BF73D3 /* jdcolor.c in Sources */, + 959729FE12C192DA00BF73D3 /* jddctmgr.c in Sources */, + 959729FF12C192DA00BF73D3 /* jdhuff.c in Sources */, + 95972A0012C192DA00BF73D3 /* jdinput.c in Sources */, + 95972A0112C192DA00BF73D3 /* jdmainct.c in Sources */, + 95972A0212C192DA00BF73D3 /* jdmarker.c in Sources */, + 95972A0312C192DA00BF73D3 /* jdmaster.c in Sources */, + 95972A0412C192DA00BF73D3 /* jdmerge.c in Sources */, + 95972A0512C192DA00BF73D3 /* jdpostct.c in Sources */, + 95972A0612C192DA00BF73D3 /* jdsample.c in Sources */, + 95972A0712C192DA00BF73D3 /* jdtrans.c in Sources */, + 95972A0812C192DA00BF73D3 /* jerror.c in Sources */, + 95972A0912C192DA00BF73D3 /* jfdctflt.c in Sources */, + 95972A0A12C192DA00BF73D3 /* jfdctfst.c in Sources */, + 95972A0B12C192DA00BF73D3 /* jfdctint.c in Sources */, + 95972A0C12C192DA00BF73D3 /* jidctflt.c in Sources */, + 95972A0D12C192DA00BF73D3 /* jidctfst.c in Sources */, + 95972A0E12C192DA00BF73D3 /* jidctint.c in Sources */, + 95972A0F12C192DA00BF73D3 /* jmemmgr.c in Sources */, + 95972A1012C192DA00BF73D3 /* jmemnobs.c in Sources */, + 95972A1112C192DA00BF73D3 /* jquant1.c in Sources */, + 95972A1212C192DA00BF73D3 /* jquant2.c in Sources */, + 95972A1312C192DA00BF73D3 /* jutils.c in Sources */, + 95972A1412C192DA00BF73D3 /* rdbmp.c in Sources */, + 95972A1512C192DA00BF73D3 /* rdcolmap.c in Sources */, + 95972A1612C192DA00BF73D3 /* rdgif.c in Sources */, + 95972A1712C192DA00BF73D3 /* rdppm.c in Sources */, + 95972A1812C192DA00BF73D3 /* rdrle.c in Sources */, + 95972A1912C192DA00BF73D3 /* rdswitch.c in Sources */, + 95972A1A12C192DA00BF73D3 /* rdtarga.c in Sources */, + 95972A1B12C192DA00BF73D3 /* transupp.c in Sources */, + 95972A1C12C192DA00BF73D3 /* wrbmp.c in Sources */, + 95972A1D12C192DA00BF73D3 /* wrgif.c in Sources */, + 95972A1E12C192DA00BF73D3 /* wrppm.c in Sources */, + 95972A1F12C192DA00BF73D3 /* wrrle.c in Sources */, + 95972A2012C192DA00BF73D3 /* wrtarga.c in Sources */, + 95972A2212C192DA00BF73D3 /* inffast.c in Sources */, + 95972A2312C192DA00BF73D3 /* inftrees.c in Sources */, + 95972A2412C192DA00BF73D3 /* uncompr.c in Sources */, + 95972A2512C192DA00BF73D3 /* compress.c in Sources */, + 95972A2612C192DA00BF73D3 /* crc32.c in Sources */, + 95972A2712C192DA00BF73D3 /* zutil.c in Sources */, + 95972A2812C192DA00BF73D3 /* trees.c in Sources */, + 95972A2912C192DA00BF73D3 /* deflate.c in Sources */, + 95972A2A12C192DA00BF73D3 /* adler32.c in Sources */, + 95972A2B12C192DA00BF73D3 /* CImageLoaderPNG.cpp in Sources */, + 95972A2C12C192DA00BF73D3 /* CColorConverter.cpp in Sources */, + 95972A2D12C192DA00BF73D3 /* CSceneManager.cpp in Sources */, + 95972A2E12C192DA00BF73D3 /* CTRTextureGouraudAdd2.cpp in Sources */, + 95972A2F12C192DA00BF73D3 /* CNullDriver.cpp in Sources */, + 95972A3012C192DA00BF73D3 /* CCSMLoader.cpp in Sources */, + 95972A3112C192DA00BF73D3 /* irrXML.cpp in Sources */, + 95972A3212C192DA00BF73D3 /* CGUIListBox.cpp in Sources */, + 95972A3312C192DA00BF73D3 /* CTRGouraudWire.cpp in Sources */, + 95972A3412C192DA00BF73D3 /* CIrrDeviceStub.cpp in Sources */, + 95972A3512C192DA00BF73D3 /* CGUIMessageBox.cpp in Sources */, + 95972A3612C192DA00BF73D3 /* CMeshSceneNode.cpp in Sources */, + 95972A3712C192DA00BF73D3 /* CGUIStaticText.cpp in Sources */, + 95972A3812C192DA00BF73D3 /* os.cpp in Sources */, + 95972A3912C192DA00BF73D3 /* COCTLoader.cpp in Sources */, + 95972A3A12C192DA00BF73D3 /* CGUIContextMenu.cpp in Sources */, + 95972A3B12C192DA00BF73D3 /* CSceneNodeAnimatorFlyCircle.cpp in Sources */, + 95972A3C12C192DA00BF73D3 /* CDefaultSceneNodeFactory.cpp in Sources */, + 95972A3D12C192DA00BF73D3 /* CD3D9Driver.cpp in Sources */, + 95972A3E12C192DA00BF73D3 /* CTRGouraud.cpp in Sources */, + 95972A3F12C192DA00BF73D3 /* C3DSMeshFileLoader.cpp in Sources */, + 95972A4012C192DA00BF73D3 /* COgreMeshFileLoader.cpp in Sources */, + 95972A4112C192DA00BF73D3 /* CMY3DMeshFileLoader.cpp in Sources */, + 95972A4212C192DA00BF73D3 /* CLMTSMeshFileLoader.cpp in Sources */, + 95972A4312C192DA00BF73D3 /* CGUIFileOpenDialog.cpp in Sources */, + 95972A4412C192DA00BF73D3 /* CSceneNodeAnimatorDelete.cpp in Sources */, + 95972A4512C192DA00BF73D3 /* CTRGouraudAlphaNoZ2.cpp in Sources */, + 95972A4612C192DA00BF73D3 /* CGeometryCreator.cpp in Sources */, + 95972A4712C192DA00BF73D3 /* CD3D8Texture.cpp in Sources */, + 95972A4812C192DA00BF73D3 /* CSkyBoxSceneNode.cpp in Sources */, + 95972A4912C192DA00BF73D3 /* CMeshManipulator.cpp in Sources */, + 95972A4A12C192DA00BF73D3 /* CTextSceneNode.cpp in Sources */, + 95972A4B12C192DA00BF73D3 /* CTRTextureDetailMap2.cpp in Sources */, + 95972A4C12C192DA00BF73D3 /* CTRTextureGouraudAddNoZ2.cpp in Sources */, + 95972A4D12C192DA00BF73D3 /* CTRTextureGouraudNoZ.cpp in Sources */, + 95972A4E12C192DA00BF73D3 /* CGUIScrollBar.cpp in Sources */, + 95972A4F12C192DA00BF73D3 /* CSceneCollisionManager.cpp in Sources */, + 95972A5012C192DA00BF73D3 /* CGUICheckBox.cpp in Sources */, + 95972A5112C192DA00BF73D3 /* CQ3LevelMesh.cpp in Sources */, + 95972A5212C192DA00BF73D3 /* CParticleGravityAffector.cpp in Sources */, + 95972A5312C192DA00BF73D3 /* CSoftwareDriver2.cpp in Sources */, + 95972A5412C192DA00BF73D3 /* CD3D9ParallaxMapRenderer.cpp in Sources */, + 95972A5512C192DA00BF73D3 /* CD3D8ParallaxMapRenderer.cpp in Sources */, + 95972A5612C192DA00BF73D3 /* CAnimatedMeshMD2.cpp in Sources */, + 95972A5712C192DA00BF73D3 /* CSceneNodeAnimatorFlyStraight.cpp in Sources */, + 95972A5812C192DA00BF73D3 /* CImageLoaderPCX.cpp in Sources */, + 95972A5912C192DA00BF73D3 /* CAnimatedMeshSceneNode.cpp in Sources */, + 95972A5A12C192DA00BF73D3 /* CTriangleSelector.cpp in Sources */, + 95972A5B12C192DA00BF73D3 /* CTRTextureGouraudVertexAlpha2.cpp in Sources */, + 95972A5C12C192DA00BF73D3 /* CTRTextureWire2.cpp in Sources */, + 95972A5D12C192DA00BF73D3 /* CTRTextureFlatWire.cpp in Sources */, + 95972A5E12C192DA00BF73D3 /* CTRGouraud2.cpp in Sources */, + 95972A5F12C192DA00BF73D3 /* CEmptySceneNode.cpp in Sources */, + 95972A6012C192DA00BF73D3 /* CTRTextureLightMap2_Add.cpp in Sources */, + 95972A6112C192DA00BF73D3 /* CReadFile.cpp in Sources */, + 95972A6212C192DA00BF73D3 /* COpenGLTexture.cpp in Sources */, + 95972A6312C192DA00BF73D3 /* COSOperator.cpp in Sources */, + 95972A6412C192DA00BF73D3 /* CColladaFileLoader.cpp in Sources */, + 95972A6512C192DA00BF73D3 /* CCameraSceneNode.cpp in Sources */, + 95972A6612C192DA00BF73D3 /* CMetaTriangleSelector.cpp in Sources */, + 95972A6712C192DA00BF73D3 /* CTRTextureFlat.cpp in Sources */, + 95972A6812C192DA00BF73D3 /* CVideoModeList.cpp in Sources */, + 95972A6912C192DA00BF73D3 /* CXMLReader.cpp in Sources */, + 95972A6A12C192DA00BF73D3 /* COpenGLParallaxMapRenderer.cpp in Sources */, + 95972A6B12C192DA00BF73D3 /* CTRTextureGouraudNoZ2.cpp in Sources */, + 95972A6C12C192DA00BF73D3 /* CTRTextureGouraudWire.cpp in Sources */, + 95972A6D12C192DA00BF73D3 /* CParticlePointEmitter.cpp in Sources */, + 95972A6E12C192DA00BF73D3 /* CGUIWindow.cpp in Sources */, + 95972A6F12C192DA00BF73D3 /* CGUIModalScreen.cpp in Sources */, + 95972A7012C192DA00BF73D3 /* CImageLoaderPSD.cpp in Sources */, + 95972A7112C192DA00BF73D3 /* CWaterSurfaceSceneNode.cpp in Sources */, + 95972A7212C192DA00BF73D3 /* CXMeshFileLoader.cpp in Sources */, + 95972A7312C192DA00BF73D3 /* CIrrDeviceLinux.cpp in Sources */, + 95972A7412C192DA00BF73D3 /* CLightSceneNode.cpp in Sources */, + 95972A7512C192DA00BF73D3 /* CTRTextureGouraudAdd.cpp in Sources */, + 95972A7612C192DA00BF73D3 /* CTRTextureGouraud2.cpp in Sources */, + 95972A7712C192DA00BF73D3 /* CSoftwareDriver.cpp in Sources */, + 95972A7812C192DA00BF73D3 /* CTRFlatWire.cpp in Sources */, + 95972A7912C192DA00BF73D3 /* CTRGouraudAlpha2.cpp in Sources */, + 95972A7A12C192DA00BF73D3 /* CSoftwareTexture2.cpp in Sources */, + 95972A7B12C192DA00BF73D3 /* CZipReader.cpp in Sources */, + 95972A7C12C192DA00BF73D3 /* COpenGLNormalMapRenderer.cpp in Sources */, + 95972A7D12C192DA00BF73D3 /* CTRTextureLightMap2_M1.cpp in Sources */, + 95972A7E12C192DA00BF73D3 /* CTRTextureLightMap2_M4.cpp in Sources */, + 95972A7F12C192DA00BF73D3 /* CGUISkin.cpp in Sources */, + 95972A8012C192DA00BF73D3 /* CD3D8Driver.cpp in Sources */, + 95972A8112C192DA00BF73D3 /* CIrrDeviceWin32.cpp in Sources */, + 95972A8212C192DA00BF73D3 /* CFileSystem.cpp in Sources */, + 95972A8312C192DA00BF73D3 /* CGUIMeshViewer.cpp in Sources */, + 95972A8412C192DA00BF73D3 /* CGUIComboBox.cpp in Sources */, + 95972A8512C192DA00BF73D3 /* CSceneNodeAnimatorRotation.cpp in Sources */, + 95972A8612C192DA00BF73D3 /* CSceneNodeAnimatorTexture.cpp in Sources */, + 95972A8712C192DA00BF73D3 /* CParticleSystemSceneNode.cpp in Sources */, + 95972A8812C192DA00BF73D3 /* CTerrainSceneNode.cpp in Sources */, + 95972A8912C192DA00BF73D3 /* CGUIFont.cpp in Sources */, + 95972A8A12C192DA00BF73D3 /* CParticleFadeOutAffector.cpp in Sources */, + 95972A8B12C192DA00BF73D3 /* CDummyTransformationSceneNode.cpp in Sources */, + 95972A8C12C192DA00BF73D3 /* CFileList.cpp in Sources */, + 95972A8D12C192DA00BF73D3 /* CImageLoaderTGA.cpp in Sources */, + 95972A8E12C192DA00BF73D3 /* CXMLWriter.cpp in Sources */, + 95972A8F12C192DA00BF73D3 /* CSceneNodeAnimatorFollowSpline.cpp in Sources */, + 95972A9012C192DA00BF73D3 /* CZBuffer.cpp in Sources */, + 95972A9112C192DA00BF73D3 /* CDMFLoader.cpp in Sources */, + 95972A9212C192DA00BF73D3 /* CD3D9Texture.cpp in Sources */, + 95972A9312C192DA00BF73D3 /* COpenGLShaderMaterialRenderer.cpp in Sources */, + 95972A9412C192DA00BF73D3 /* Irrlicht.cpp in Sources */, + 95972A9512C192DA00BF73D3 /* CGUIEditBox.cpp in Sources */, + 95972A9612C192DA00BF73D3 /* COpenGLSLMaterialRenderer.cpp in Sources */, + 95972A9712C192DA00BF73D3 /* CD3D9HLSLMaterialRenderer.cpp in Sources */, + 95972A9812C192DA00BF73D3 /* CSoftwareTexture.cpp in Sources */, + 95972A9912C192DA00BF73D3 /* CCubeSceneNode.cpp in Sources */, + 95972A9A12C192DA00BF73D3 /* CTriangleBBSelector.cpp in Sources */, + 95972A9B12C192DA00BF73D3 /* CD3D9ShaderMaterialRenderer.cpp in Sources */, + 95972A9C12C192DA00BF73D3 /* CD3D8ShaderMaterialRenderer.cpp in Sources */, + 95972A9D12C192DA00BF73D3 /* CGUIButton.cpp in Sources */, + 95972A9E12C192DA00BF73D3 /* CGUIToolBar.cpp in Sources */, + 95972A9F12C192DA00BF73D3 /* CDefaultSceneNodeAnimatorFactory.cpp in Sources */, + 95972AA012C192DA00BF73D3 /* CBillboardSceneNode.cpp in Sources */, + 95972AA112C192DA00BF73D3 /* CSceneNodeAnimatorCollisionResponse.cpp in Sources */, + 95972AA212C192DA00BF73D3 /* CLogger.cpp in Sources */, + 95972AA312C192DA00BF73D3 /* CGUIInOutFader.cpp in Sources */, + 95972AA412C192DA00BF73D3 /* CWriteFile.cpp in Sources */, + 95972AA512C192DA00BF73D3 /* CTRTextureGouraud.cpp in Sources */, + 95972AA612C192DA00BF73D3 /* CTRFlat.cpp in Sources */, + 95972AA712C192DA00BF73D3 /* CTerrainTriangleSelector.cpp in Sources */, + 95972AA812C192DA00BF73D3 /* CGUITabControl.cpp in Sources */, + 95972AA912C192DA00BF73D3 /* CParticleBoxEmitter.cpp in Sources */, + 95972AAA12C192DA00BF73D3 /* CGUIMenu.cpp in Sources */, + 95972AAB12C192DA00BF73D3 /* CImage.cpp in Sources */, + 95972AAC12C192DA00BF73D3 /* CShadowVolumeSceneNode.cpp in Sources */, + 95972AAD12C192DA00BF73D3 /* CGUIEnvironment.cpp in Sources */, + 95972AAE12C192DA00BF73D3 /* CLimitReadFile.cpp in Sources */, + 95972AAF12C192DA00BF73D3 /* CAttributes.cpp in Sources */, + 95972AB012C192DA00BF73D3 /* COpenGLDriver.cpp in Sources */, + 95972AB112C192DA00BF73D3 /* CTRTextureLightMap2_M2.cpp in Sources */, + 95972AB212C192DA00BF73D3 /* CGUIImage.cpp in Sources */, + 95972AB312C192DA00BF73D3 /* CD3D9NormalMapRenderer.cpp in Sources */, + 95972AB412C192DA00BF73D3 /* CD3D8NormalMapRenderer.cpp in Sources */, + 95972AB512C192DA00BF73D3 /* CMeshCache.cpp in Sources */, + 95972AB612C192DA00BF73D3 /* CImageLoaderJPG.cpp in Sources */, + 95972AB712C192DA00BF73D3 /* CFPSCounter.cpp in Sources */, + 95972AB812C192DA00BF73D3 /* OSXClipboard.mm in Sources */, + 95972AB912C192DA00BF73D3 /* CIrrDeviceMacOSX.mm in Sources */, + 95972ABA12C192DA00BF73D3 /* AppDelegate.mm in Sources */, + 95972ABB12C192DA00BF73D3 /* inflate.c in Sources */, + 95972ABC12C192DA00BF73D3 /* CSphereSceneNode.cpp in Sources */, + 95972ABD12C192DA00BF73D3 /* COBJMeshFileLoader.cpp in Sources */, + 95972ABE12C192DA00BF73D3 /* CPakReader.cpp in Sources */, + 95972ABF12C192DA00BF73D3 /* CImageLoaderBMP.cpp in Sources */, + 95972AC012C192DA00BF73D3 /* CImageWriterBMP.cpp in Sources */, + 95972AC112C192DA00BF73D3 /* CImageWriterJPG.cpp in Sources */, + 95972AC212C192DA00BF73D3 /* CImageWriterPCX.cpp in Sources */, + 95972AC312C192DA00BF73D3 /* CImageWriterPNG.cpp in Sources */, + 95972AC412C192DA00BF73D3 /* CImageWriterPPM.cpp in Sources */, + 95972AC512C192DA00BF73D3 /* CImageWriterPSD.cpp in Sources */, + 95972AC612C192DA00BF73D3 /* CImageWriterTGA.cpp in Sources */, + 95972AC712C192DA00BF73D3 /* CSkyDomeSceneNode.cpp in Sources */, + 95972AC812C192DA00BF73D3 /* CDepthBuffer.cpp in Sources */, + 95972AC912C192DA00BF73D3 /* IBurningShader.cpp in Sources */, + 95972ACA12C192DA00BF73D3 /* CAnimatedMeshMD3.cpp in Sources */, + 95972ACB12C192DA00BF73D3 /* CDefaultGUIElementFactory.cpp in Sources */, + 95972ACC12C192DA00BF73D3 /* CGUIColorSelectDialog.cpp in Sources */, + 95972ACD12C192DA00BF73D3 /* CGUISpinBox.cpp in Sources */, + 95972ACE12C192DA00BF73D3 /* CGUISpriteBank.cpp in Sources */, + 95972ACF12C192DA00BF73D3 /* CQuake3ShaderSceneNode.cpp in Sources */, + 95972AD012C192DA00BF73D3 /* CTRTextureBlend.cpp in Sources */, + 95972AD112C192DA00BF73D3 /* CTRTextureGouraudAlpha.cpp in Sources */, + 95972AD212C192DA00BF73D3 /* CTRTextureGouraudAlphaNoZ.cpp in Sources */, + 95972AD312C192DA00BF73D3 /* CTRTextureLightMapGouraud2_M4.cpp in Sources */, + 95972AD412C192DA00BF73D3 /* CIrrDeviceSDL.cpp in Sources */, + 95972AD512C192DA00BF73D3 /* COpenGLExtensionHandler.cpp in Sources */, + 95972AD612C192DA00BF73D3 /* CMD3MeshFileLoader.cpp in Sources */, + 95972AD712C192DA00BF73D3 /* CB3DMeshFileLoader.cpp in Sources */, + 95972AD812C192DA00BF73D3 /* CBoneSceneNode.cpp in Sources */, + 95972AD912C192DA00BF73D3 /* CBSPMeshFileLoader.cpp in Sources */, + 95972ADA12C192DA00BF73D3 /* CColladaMeshWriter.cpp in Sources */, + 95972ADB12C192DA00BF73D3 /* CImageLoaderPPM.cpp in Sources */, + 95972ADC12C192DA00BF73D3 /* CIrrMeshFileLoader.cpp in Sources */, + 95972ADD12C192DA00BF73D3 /* CIrrMeshWriter.cpp in Sources */, + 95972ADE12C192DA00BF73D3 /* CMD2MeshFileLoader.cpp in Sources */, + 95972ADF12C192DA00BF73D3 /* CMS3DMeshFileLoader.cpp in Sources */, + 95972AE012C192DA00BF73D3 /* CParticleAnimatedMeshSceneNodeEmitter.cpp in Sources */, + 95972AE112C192DA00BF73D3 /* CParticleAttractionAffector.cpp in Sources */, + 95972AE212C192DA00BF73D3 /* CParticleCylinderEmitter.cpp in Sources */, + 95972AE312C192DA00BF73D3 /* CParticleMeshEmitter.cpp in Sources */, + 95972AE412C192DA00BF73D3 /* CParticleRingEmitter.cpp in Sources */, + 95972AE512C192DA00BF73D3 /* CParticleRotationAffector.cpp in Sources */, + 95972AE612C192DA00BF73D3 /* CParticleSphereEmitter.cpp in Sources */, + 95972AE712C192DA00BF73D3 /* CSkinnedMesh.cpp in Sources */, + 95972AE812C192DA00BF73D3 /* CSTLMeshFileLoader.cpp in Sources */, + 95972AE912C192DA00BF73D3 /* CSTLMeshWriter.cpp in Sources */, + 95972AEA12C192DA00BF73D3 /* CBurningShader_Raster_Reference.cpp in Sources */, + 95972AEB12C192DA00BF73D3 /* CGUITable.cpp in Sources */, + 95972AEC12C192DA00BF73D3 /* CImageLoaderWAL.cpp in Sources */, + 95972AED12C192DA00BF73D3 /* CVolumeLightSceneNode.cpp in Sources */, + 95972AEE12C192DA00BF73D3 /* CLWOMeshFileLoader.cpp in Sources */, + 95972AEF12C192DA00BF73D3 /* CSceneNodeAnimatorCameraFPS.cpp in Sources */, + 95972AF012C192DA00BF73D3 /* CSceneNodeAnimatorCameraMaya.cpp in Sources */, + 95972AF112C192DA00BF73D3 /* COBJMeshWriter.cpp in Sources */, + 95972AF212C192DA00BF73D3 /* CParticleScaleAffector.cpp in Sources */, + 95972AF312C192DA00BF73D3 /* png.c in Sources */, + 95972AF412C192DA00BF73D3 /* pngerror.c in Sources */, + 95972AF612C192DA00BF73D3 /* pngget.c in Sources */, + 95972AF712C192DA00BF73D3 /* pngmem.c in Sources */, + 95972AF812C192DA00BF73D3 /* pngpread.c in Sources */, + 95972AF912C192DA00BF73D3 /* pngread.c in Sources */, + 95972AFA12C192DA00BF73D3 /* pngrio.c in Sources */, + 95972AFB12C192DA00BF73D3 /* pngrtran.c in Sources */, + 95972AFC12C192DA00BF73D3 /* pngrutil.c in Sources */, + 95972AFD12C192DA00BF73D3 /* pngset.c in Sources */, + 95972AFE12C192DA00BF73D3 /* pngtrans.c in Sources */, + 95972AFF12C192DA00BF73D3 /* pngwio.c in Sources */, + 95972B0012C192DA00BF73D3 /* pngwrite.c in Sources */, + 95972B0112C192DA00BF73D3 /* pngwtran.c in Sources */, + 95972B0212C192DA00BF73D3 /* pngwutil.c in Sources */, + 95972B0312C192DA00BF73D3 /* CGUIImageList.cpp in Sources */, + 95972B0412C192DA00BF73D3 /* CGUITreeView.cpp in Sources */, + 95972B0512C192DA00BF73D3 /* CMemoryFile.cpp in Sources */, + 95972B0612C192DA00BF73D3 /* CIrrDeviceConsole.cpp in Sources */, + 95972B0712C192DA00BF73D3 /* CImageLoaderRGB.cpp in Sources */, + 95972B0812C192DA00BF73D3 /* CPLYMeshFileLoader.cpp in Sources */, + 95972B0912C192DA00BF73D3 /* CPLYMeshWriter.cpp in Sources */, + 95972B0A12C192DA00BF73D3 /* CTarReader.cpp in Sources */, + 95972B0B12C192DA00BF73D3 /* CMountPointReader.cpp in Sources */, + 95972B0C12C192DA00BF73D3 /* jaricom.c in Sources */, + 95972B0D12C192DA00BF73D3 /* jcarith.c in Sources */, + 95972B0E12C192DA00BF73D3 /* jdarith.c in Sources */, + 95972B0F12C192DA00BF73D3 /* COctreeSceneNode.cpp in Sources */, + 95972B1012C192DA00BF73D3 /* COctreeTriangleSelector.cpp in Sources */, + 95972B1112C192DA00BF73D3 /* CNPKReader.cpp in Sources */, + 95972B1212C192DA00BF73D3 /* CIrrDeviceFB.cpp in Sources */, + 95972B1312C192DA00BF73D3 /* CIrrDeviceWinCE.cpp in Sources */, + 95972B1412C192DA00BF73D3 /* LzmaDec.c in Sources */, + 95972B1512C192DA00BF73D3 /* blocksort.c in Sources */, + 95972B1612C192DA00BF73D3 /* bzcompress.c in Sources */, + 95972B1712C192DA00BF73D3 /* crctable.c in Sources */, + 95972B1812C192DA00BF73D3 /* decompress.c in Sources */, + 95972B1912C192DA00BF73D3 /* huffman.c in Sources */, + 95972B1A12C192DA00BF73D3 /* randtable.c in Sources */, + 95972B1B12C192DA00BF73D3 /* bzlib.c in Sources */, + 95972B1C12C192DA00BF73D3 /* aescrypt.cpp in Sources */, + 95972B1D12C192DA00BF73D3 /* aeskey.cpp in Sources */, + 95972B1E12C192DA00BF73D3 /* aestab.cpp in Sources */, + 95972B1F12C192DA00BF73D3 /* fileenc.cpp in Sources */, + 95972B2012C192DA00BF73D3 /* hmac.cpp in Sources */, + 95972B2112C192DA00BF73D3 /* prng.cpp in Sources */, + 95972B2212C192DA00BF73D3 /* pwd2key.cpp in Sources */, + 95972B2312C192DA00BF73D3 /* sha1.cpp in Sources */, + 95972B2412C192DA00BF73D3 /* sha2.cpp in Sources */, + 95E5857212FCE277004946C6 /* CWADReader.cpp in Sources */, + 95E5857912FCE2CB004946C6 /* CAnimatedMeshHalfLife.cpp in Sources */, + 95E5857D12FCE2DE004946C6 /* CSceneLoaderIrr.cpp in Sources */, + 95E5858E12FCE388004946C6 /* CTRNormalMap.cpp in Sources */, + 95E5859312FCE3A1004946C6 /* CTRStencilShadow.cpp in Sources */, + 95E5859612FCE3F5004946C6 /* CSMFMeshFileLoader.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; B81CFE03097FD9F50057C06F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4122,7 +5200,6 @@ 4CA25C1A0A485EAD00B4E469 /* wrppm.c in Sources */, 4CA25C1B0A485EAD00B4E469 /* wrrle.c in Sources */, 4CA25C1C0A485EAD00B4E469 /* wrtarga.c in Sources */, - 4C53E3CA0A4856B30014E966 /* gzio.c in Sources */, 4C53E3D80A4856B30014E966 /* inffast.c in Sources */, 4C53E3DC0A4856B30014E966 /* inftrees.c in Sources */, 4C53E3E40A4856B30014E966 /* uncompr.c in Sources */, @@ -4334,7 +5411,6 @@ 096CC0E00ECE65B500C81DC7 /* CParticleScaleAffector.cpp in Sources */, 09293C3E0ED32029003B8C9C /* png.c in Sources */, 09293C3F0ED32029003B8C9C /* pngerror.c in Sources */, - 09293C400ED32029003B8C9C /* pnggccrd.c in Sources */, 09293C410ED32029003B8C9C /* pngget.c in Sources */, 09293C420ED32029003B8C9C /* pngmem.c in Sources */, 09293C430ED32029003B8C9C /* pngpread.c in Sources */, @@ -4382,6 +5458,12 @@ 0E2E3C8D1103B53C002DE8D7 /* pwd2key.cpp in Sources */, 0E2E3C8E1103B53C002DE8D7 /* sha1.cpp in Sources */, 0E2E3C8F1103B53C002DE8D7 /* sha2.cpp in Sources */, + 95E5857112FCE277004946C6 /* CWADReader.cpp in Sources */, + 95E5857712FCE2CB004946C6 /* CAnimatedMeshHalfLife.cpp in Sources */, + 95E5857C12FCE2DE004946C6 /* CSceneLoaderIrr.cpp in Sources */, + 95E5858D12FCE388004946C6 /* CTRNormalMap.cpp in Sources */, + 95E5859212FCE3A1004946C6 /* CTRStencilShadow.cpp in Sources */, + 95E5859512FCE3F5004946C6 /* CSMFMeshFileLoader.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4709,6 +5791,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; ONLY_LINK_ESSENTIAL_SYMBOLS = YES; @@ -5082,8 +6165,10 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = MacOSX_Prefix.pch; + GCC_VERSION = 4.0; INSTALL_PATH = /usr/local/lib; PRODUCT_NAME = Irrlicht; + SHARED_PRECOMPS_DIR = ""; SYMROOT = build; ZERO_LINK = NO; }; @@ -5098,10 +6183,13 @@ ); GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; - GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = MacOSX_Prefix.pch; + GCC_VERSION = 4.0; INSTALL_PATH = /usr/local/lib; + PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = Irrlicht; + SHARED_PRECOMPS_DIR = ""; }; name = Release; }; @@ -5146,6 +6234,83 @@ }; name = Release; }; + 959726FF12C18FFD00BF73D3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = 4.0; + INFOPLIST_FILE = "IrrFramework-Info.plist"; + INSTALL_PATH = "@executable_path/../Frameworks"; + OTHER_CFLAGS = ( + "-read_only_relocs", + suppress, + ); + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + "-read_only_relocs", + suppress, + ); + PREBINDING = NO; + PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; + PRODUCT_NAME = IrrFramework; + SHARED_PRECOMPS_DIR = ""; + }; + name = Debug; + }; + 9597270012C18FFD00BF73D3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = 4.0; + INFOPLIST_FILE = "IrrFramework-Info.plist"; + INSTALL_PATH = "@executable_path/../Frameworks"; + OTHER_CFLAGS = ( + "-read_only_relocs", + suppress, + ); + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + "-read_only_relocs", + suppress, + ); + PREBINDING = NO; + PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; + PRODUCT_NAME = IrrFramework; + SHARED_PRECOMPS_DIR = ""; + ZERO_LINK = NO; + }; + name = Release; + }; B81CFE0D097FD9F50057C06F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -5159,6 +6324,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; OTHER_LDFLAGS = ( @@ -5187,6 +6353,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; ONLY_LINK_ESSENTIAL_SYMBOLS = YES; @@ -5396,6 +6563,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; OTHER_LDFLAGS = ( @@ -5424,6 +6592,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; ONLY_LINK_ESSENTIAL_SYMBOLS = YES; @@ -5483,6 +6652,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; ONLY_LINK_ESSENTIAL_SYMBOLS = YES; @@ -5494,6 +6664,7 @@ ); PREBINDING = NO; PRODUCT_NAME = UserInterface; + SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; SEPARATE_STRIP = YES; STRIP_INSTALLED_PRODUCT = YES; WRAPPER_EXTENSION = app; @@ -5563,6 +6734,8 @@ B81CFF3F097FE25F0057C06F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; COPY_PHASE_STRIP = NO; DEPLOYMENT_LOCATION = YES; DSTROOT = ../../../bin/MacOSX; @@ -5573,6 +6746,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; LIBRARY_SEARCH_PATHS = ( @@ -5588,6 +6762,7 @@ ); PREBINDING = NO; PRODUCT_NAME = Quake3Map_dbg; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; WRAPPER_EXTENSION = app; ZERO_LINK = YES; }; @@ -5596,6 +6771,8 @@ B81CFF40097FE25F0057C06F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; COPY_PHASE_STRIP = YES; DEAD_CODE_STRIPPING = YES; DEPLOYMENT_LOCATION = YES; @@ -5606,6 +6783,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; LIBRARY_SEARCH_PATHS = ( @@ -5622,6 +6800,7 @@ ); PREBINDING = NO; PRODUCT_NAME = Quake3Map; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; SEPARATE_STRIP = YES; STRIP_INSTALLED_PRODUCT = YES; WRAPPER_EXTENSION = app; @@ -5758,6 +6937,7 @@ B81CFF9D097FE45E0057C06F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = i386; COPY_PHASE_STRIP = NO; DEPLOYMENT_LOCATION = YES; DSTROOT = ../../../bin/MacOSX; @@ -5768,6 +6948,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; OTHER_LDFLAGS = ( @@ -5778,6 +6959,7 @@ ); PREBINDING = NO; PRODUCT_NAME = MeshViewer_dbg; + SDKROOT = ""; WRAPPER_EXTENSION = app; ZERO_LINK = YES; }; @@ -5786,6 +6968,7 @@ B81CFF9E097FE45E0057C06F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = i386; COPY_PHASE_STRIP = YES; DEAD_CODE_STRIPPING = YES; DEPLOYMENT_LOCATION = YES; @@ -5796,6 +6979,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_VERSION = 4.0; INFOPLIST_FILE = "DemoApp-Info.plist"; INSTALL_PATH = /; ONLY_LINK_ESSENTIAL_SYMBOLS = YES; @@ -5807,6 +6991,7 @@ ); PREBINDING = NO; PRODUCT_NAME = MeshViewer; + SDKROOT = ""; SEPARATE_STRIP = YES; STRIP_INSTALLED_PRODUCT = YES; WRAPPER_EXTENSION = app; @@ -6038,6 +7223,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 9597270112C18FFD00BF73D3 /* Build configuration list for PBXNativeTarget "IrrFramework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 959726FF12C18FFD00BF73D3 /* Debug */, + 9597270012C18FFD00BF73D3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; B81CFE0C097FD9F50057C06F /* Build configuration list for PBXNativeTarget "06.2DGraphics" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/source/Irrlicht/Makefile b/source/Irrlicht/Makefile index 142eb9d3..e4dab7c2 100644 --- a/source/Irrlicht/Makefile +++ b/source/Irrlicht/Makefile @@ -1,7 +1,8 @@ VERSION_MAJOR = 1 -VERSION_MINOR = 7 +VERSION_MINOR = 8 VERSION_RELEASE = 0-SVN VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE) +COMPATIBILITY_VERSION = $(VERSION_MAJOR).$(VERSION_MINOR) # Irrlicht Engine 1.7.0-SVN # Makefile for Linux # @@ -22,13 +23,13 @@ VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE) # #List of object files, separated based on engine architecture -IRRMESHLOADER = CBSPMeshFileLoader.o CMD2MeshFileLoader.o CMD3MeshFileLoader.o CMS3DMeshFileLoader.o CB3DMeshFileLoader.o C3DSMeshFileLoader.o COgreMeshFileLoader.o COBJMeshFileLoader.o CColladaFileLoader.o CCSMLoader.o CDMFLoader.o CLMTSMeshFileLoader.o CMY3DMeshFileLoader.o COCTLoader.o CXMeshFileLoader.o CIrrMeshFileLoader.o CSTLMeshFileLoader.o CLWOMeshFileLoader.o CPLYMeshFileLoader.o +IRRMESHLOADER = CBSPMeshFileLoader.o CMD2MeshFileLoader.o CMD3MeshFileLoader.o CMS3DMeshFileLoader.o CB3DMeshFileLoader.o C3DSMeshFileLoader.o COgreMeshFileLoader.o COBJMeshFileLoader.o CColladaFileLoader.o CCSMLoader.o CDMFLoader.o CLMTSMeshFileLoader.o CMY3DMeshFileLoader.o COCTLoader.o CXMeshFileLoader.o CIrrMeshFileLoader.o CSTLMeshFileLoader.o CLWOMeshFileLoader.o CPLYMeshFileLoader.o CSMFMeshFileLoader.o IRRMESHWRITER = CColladaMeshWriter.o CIrrMeshWriter.o CSTLMeshWriter.o COBJMeshWriter.o CPLYMeshWriter.o IRRMESHOBJ = $(IRRMESHLOADER) $(IRRMESHWRITER) \ CSkinnedMesh.o CBoneSceneNode.o CMeshSceneNode.o \ CAnimatedMeshSceneNode.o CAnimatedMeshMD2.o CAnimatedMeshMD3.o \ CQ3LevelMesh.o CQuake3ShaderSceneNode.o CAnimatedMeshHalfLife.o -IRROBJ = CBillboardSceneNode.o CCameraSceneNode.o CDummyTransformationSceneNode.o CEmptySceneNode.o CGeometryCreator.o CLightSceneNode.o CMeshManipulator.o CMetaTriangleSelector.o COctreeSceneNode.o COctreeTriangleSelector.o CSceneCollisionManager.o CSceneManager.o CShadowVolumeSceneNode.o CSkyBoxSceneNode.o CSkyDomeSceneNode.o CTerrainSceneNode.o CTerrainTriangleSelector.o CVolumeLightSceneNode.o CCubeSceneNode.o CSphereSceneNode.o CTextSceneNode.o CTriangleBBSelector.o CTriangleSelector.o CWaterSurfaceSceneNode.o CMeshCache.o CDefaultSceneNodeAnimatorFactory.o CDefaultSceneNodeFactory.o +IRROBJ = CBillboardSceneNode.o CCameraSceneNode.o CDummyTransformationSceneNode.o CEmptySceneNode.o CGeometryCreator.o CLightSceneNode.o CMeshManipulator.o CMetaTriangleSelector.o COctreeSceneNode.o COctreeTriangleSelector.o CSceneCollisionManager.o CSceneManager.o CShadowVolumeSceneNode.o CSkyBoxSceneNode.o CSkyDomeSceneNode.o CTerrainSceneNode.o CTerrainTriangleSelector.o CVolumeLightSceneNode.o CCubeSceneNode.o CSphereSceneNode.o CTextSceneNode.o CTriangleBBSelector.o CTriangleSelector.o CWaterSurfaceSceneNode.o CMeshCache.o CDefaultSceneNodeAnimatorFactory.o CDefaultSceneNodeFactory.o CSceneLoaderIrr.o IRRPARTICLEOBJ = CParticleAnimatedMeshSceneNodeEmitter.o CParticleBoxEmitter.o CParticleCylinderEmitter.o CParticleMeshEmitter.o CParticlePointEmitter.o CParticleRingEmitter.o CParticleSphereEmitter.o CParticleAttractionAffector.o CParticleFadeOutAffector.o CParticleGravityAffector.o CParticleRotationAffector.o CParticleSystemSceneNode.o CParticleScaleAffector.o IRRANIMOBJ = CSceneNodeAnimatorCameraFPS.o CSceneNodeAnimatorCameraMaya.o CSceneNodeAnimatorCollisionResponse.o CSceneNodeAnimatorDelete.o CSceneNodeAnimatorFlyCircle.o CSceneNodeAnimatorFlyStraight.o CSceneNodeAnimatorFollowSpline.o CSceneNodeAnimatorRotation.o CSceneNodeAnimatorTexture.o IRRDRVROBJ = CNullDriver.o COpenGLDriver.o COpenGLNormalMapRenderer.o COpenGLParallaxMapRenderer.o COpenGLShaderMaterialRenderer.o COpenGLTexture.o COpenGLSLMaterialRenderer.o COpenGLExtensionHandler.o CD3D8Driver.o CD3D8NormalMapRenderer.o CD3D8ParallaxMapRenderer.o CD3D8ShaderMaterialRenderer.o CD3D8Texture.o CD3D9Driver.o CD3D9HLSLMaterialRenderer.o CD3D9NormalMapRenderer.o CD3D9ParallaxMapRenderer.o CD3D9ShaderMaterialRenderer.o CD3D9Texture.o COGLESDriver.o COGLESTexture.o COGLESExtensionHandler.o @@ -110,7 +111,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) -o $(SHARED_LIB).$(VERSION) $^ $(LDFLAGS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -shared -Wl,-soname,$(SHARED_LIB).$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE) -o $(SHARED_LIB).$(VERSION) $^ $(LDFLAGS) mkdir -p $(LIB_PATH) cp $(SHARED_LIB).$(VERSION) $(LIB_PATH) @@ -134,7 +135,7 @@ 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) -o $(SHARED_LIB).$(VERSION) $^ $(LDFLAGS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -dynamiclib -Wl,-install_name,$(SHARED_LIB).$(COMPATIBILITY_VERSION) -o $(SHARED_LIB).$(VERSION) $^ $(LDFLAGS) cp $(SHARED_LIB).$(VERSION) $(LIB_PATH) # Installs Irrlicht if it was created as shared lib @@ -143,7 +144,8 @@ install install_osx: 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 $(SHARED_LIB).$(VERSION) $(SHARED_LIB) + cd $(INSTALL_DIR) && ln -s -f $(SHARED_LIB).$(VERSION) $(COMPATIBILITY_VERSION) + # ldconfig -n $(INSTALL_DIR) TAGS: diff --git a/source/Irrlicht/dmfsupport.h b/source/Irrlicht/dmfsupport.h index 9f16a062..0244b656 100644 --- a/source/Irrlicht/dmfsupport.h +++ b/source/Irrlicht/dmfsupport.h @@ -330,7 +330,7 @@ bool GetDMFMaterials(const StringList& RawFile, materials[i].pathName.replace('\\','/'); materials[i].pathName += "/"; // temp[3] is reserved, temp[4] is the number of texture layers - materials[i].textureLayers = core::strtol10(temp[4].c_str()); + materials[i].textureLayers = core::strtoul10(temp[4].c_str()); // Three values are separated by commas temp1=SubdivideString(temp[5],","); @@ -449,7 +449,7 @@ bool GetDMFVerticesFaces(const StringList& RawFile/** pos; - const u32 posCount = core::strtol10(RawFile[offs].c_str()); + const u32 posCount = core::strtoul10(RawFile[offs].c_str()); ++offs; pos.reallocate(posCount); for (u32 i=0; i using namespace irr; +namespace +{ +// don't use this code! It lacks many checks and is for testing +// purposes only!!! +// based on code and media from SuperTuxKart +class ScalableFont : public gui::IGUIFontBitmap +{ + float m_scale; + struct TextureInfo + { + irr::core::stringc m_file_name; + bool m_has_alpha; + float m_scale; + + TextureInfo() + { + m_has_alpha = false; + m_scale = 1.0f; + } + }; + + std::map m_texture_files; + + void lazyLoadTexture(int texID) + { + const bool mipmap = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); + Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true); + // load texture + SpriteBank->setTexture(texID, Driver->getTexture( m_texture_files[texID].m_file_name )); + // set previous mip-map+filter state + Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, mipmap); + + // couldn't load texture, abort. + if (!SpriteBank->getTexture(texID)) + { + return; + } + else + { + // colorkey texture rather than alpha channel? + if (! m_texture_files[texID].m_has_alpha) + { + Driver->makeColorKeyTexture(SpriteBank->getTexture(texID), core::position2di(0,0)); + } + } + } + void doReadXmlFile(io::IXMLReader* xml) + { + while (xml->read()) + { + if (io::EXN_ELEMENT == xml->getNodeType()) + { + if (core::stringw(L"include") == xml->getNodeName()) + { + core::stringc filename = xml->getAttributeValue(L"file"); + io::IXMLReader* included = Environment->getFileSystem()->createXMLReader(filename.c_str()); + if (included != NULL) + { + doReadXmlFile(included); + included->drop(); + } + } + else if (core::stringw(L"Texture") == xml->getNodeName()) + { + // add a texture + core::stringc filename = xml->getAttributeValue(L"filename"); + core::stringc fn = filename; + u32 i = (u32)xml->getAttributeValueAsInt(L"index"); + + float scale=1.0f; + if(xml->getAttributeValue(L"scale")) + scale = xml->getAttributeValueAsFloat(L"scale"); + //std::cout << "scale = " << scale << std::endl; + + core::stringw alpha = xml->getAttributeValue(L"hasAlpha"); + + //std::cout << "---- Adding font texture " << fn.c_str() << "; alpha=" << alpha.c_str() << std::endl; + + + // make sure the sprite bank has enough textures in it + while (i+1 > SpriteBank->getTextureCount()) + { + SpriteBank->addTexture(NULL); + } + + TextureInfo info; + info.m_file_name = fn; + info.m_has_alpha = (alpha == core::stringw("true")); + info.m_scale = scale; + + m_texture_files[i] = info; + } + else if (core::stringw(L"c") == xml->getNodeName()) + { + // adding a character to this font + SFontArea a; + gui::SGUISpriteFrame f; + gui::SGUISprite s; + core::rect rectangle; + + a.underhang = xml->getAttributeValueAsInt(L"u"); + a.overhang = xml->getAttributeValueAsInt(L"o"); + a.spriteno = SpriteBank->getSprites().size(); + s32 texno = xml->getAttributeValueAsInt(L"i"); + + // parse rectangle + core::stringc rectstr = xml->getAttributeValue(L"r"); + wchar_t ch = xml->getAttributeValue(L"c")[0]; + + const c8 *c = rectstr.c_str(); + s32 val; + val = 0; + while (*c >= '0' && *c <= '9') + { + val *= 10; + val += *c - '0'; + c++; + } + rectangle.UpperLeftCorner.X = val; + while (*c == L' ' || *c == L',') c++; + + val = 0; + while (*c >= '0' && *c <= '9') + { + val *= 10; + val += *c - '0'; + c++; + } + rectangle.UpperLeftCorner.Y = val; + while (*c == L' ' || *c == L',') c++; + + val = 0; + while (*c >= '0' && *c <= '9') + { + val *= 10; + val += *c - '0'; + c++; + } + rectangle.LowerRightCorner.X = val; + while (*c == L' ' || *c == L',') c++; + + val = 0; + while (*c >= '0' && *c <= '9') + { + val *= 10; + val += *c - '0'; + c++; + } + rectangle.LowerRightCorner.Y = val; + + CharacterMap[ch] = Areas.size(); + + // make frame + f.rectNumber = SpriteBank->getPositions().size(); + f.textureNumber = texno; + + // add frame to sprite + s.Frames.push_back(f); + s.frameTime = 0; + + // add rectangle to sprite bank + SpriteBank->getPositions().push_back(rectangle); + a.width = rectangle.getWidth(); + + // add sprite to sprite bank + SpriteBank->getSprites().push_back(s); + + // add character to font + Areas.push_back(a); + } + } + } + } + +public: + + bool m_black_border; + + ScalableFont* m_fallback_font; + float m_fallback_font_scale; + int m_fallback_kerning_width; + + //! constructor + ScalableFont(gui::IGUIEnvironment *env, const io::path& filename) + : Driver(0), SpriteBank(0), Environment(env), WrongCharacter(0), + MaxHeight(0), GlobalKerningWidth(0), GlobalKerningHeight(0) + { + #ifdef _DEBUG + setDebugName("ScalableFont"); + #endif + + m_fallback_font = NULL; + m_fallback_kerning_width = 0; + m_fallback_font_scale = 1.0f; + m_scale = 0.37f; + m_black_border = false; + + if (Environment) + { + // don't grab environment, to avoid circular references + Driver = Environment->getVideoDriver(); + + SpriteBank = Environment->addEmptySpriteBank(filename); + if (SpriteBank) + SpriteBank->grab(); + } + + if (Driver) + Driver->grab(); + + setInvisibleCharacters ( L" " ); + + io::IXMLReader* reader = env->getFileSystem()->createXMLReader(filename.c_str()); + if (reader) + { + load( reader ); + reader->drop(); + } + assert(Areas.size() > 0); + } + + //! destructor + virtual ~ScalableFont() + { + if (Driver) Driver->drop(); + if (SpriteBank) SpriteBank->drop(); + } + + //! loads a font from an XML file + bool load(io::IXMLReader* xml) + { + if (!SpriteBank) + return false; + + doReadXmlFile(xml); + + // set bad character + WrongCharacter = getAreaIDFromCharacter(L' ', NULL); + + setMaxHeight(); + + for(wchar_t c='0'; c<='9'; c++) + { + SFontArea a = getAreaFromCharacter(c, NULL); + if(a.overhang > m_max_digit_area.overhang ) m_max_digit_area.overhang = a.overhang; + if(a.underhang > m_max_digit_area.underhang) m_max_digit_area.underhang = a.underhang; + if(a.width > m_max_digit_area.width ) m_max_digit_area.width = a.width; + } + m_max_digit_area.overhang = 0;m_max_digit_area.underhang=0; + return true; + } + //! draws an text and clips it to the specified rectangle if wanted + virtual void draw(const core::stringw& text, const core::rect& position, + video::SColor color, bool hcenter=false, + bool vcenter=false, const core::rect* clip=0) + { + if (!Driver) return; + + core::position2d offset = position.UpperLeftCorner; + core::dimension2d text_dimension; + + // When we use the "tab" hack, disable right-alignment, it messes up everything + bool has_tab = (text.findFirst(L'\t') != -1); + // ---- collect character locations + const unsigned int text_size = text.size(); + core::array indices(text_size); + core::array offsets(text_size); + core::array fallback; + fallback.set_used(text_size); + + for (u32 i = 0; i> 1; + continue; + } // if lineBreak + + bool use_fallback_font = false; + const SFontArea &area = getAreaFromCharacter(c, &use_fallback_font); + fallback[i] = use_fallback_font; + offset.X += area.underhang; + offsets.push_back(offset); + // Invisible character. add something to the array anyway so that + // indices from the various arrays remain in sync + indices.push_back( Invisible.findFirst(c) < 0 ? area.spriteno + : -1 ); + offset.X += getCharWidth(area, fallback[i]); + } // for i& sprites = SpriteBank->getSprites(); + core::array< core::rect >& positions = SpriteBank->getPositions(); + core::array< gui::SGUISprite >* fallback_sprites; + core::array< core::rect >* fallback_positions; + if(m_fallback_font!=NULL) + { + fallback_sprites = &m_fallback_font->SpriteBank->getSprites(); + fallback_positions = &m_fallback_font->SpriteBank->getPositions(); + } + else + { + fallback_sprites = NULL; + fallback_positions = NULL; + } + + video::IVideoDriver* driver = Environment->getVideoDriver(); + const int spriteAmount = sprites.size(); + for (int n=0; n= spriteAmount)) continue; + if (indices[n] == -1) continue; + + //assert(sprites[spriteID].Frames.size() > 0); + + const int texID = (fallback[n] ? + (*fallback_sprites)[spriteID].Frames[0].textureNumber : + sprites[spriteID].Frames[0].textureNumber); + + core::rect source = (fallback[n] ? + (*fallback_positions)[(*fallback_sprites)[spriteID].Frames[0].rectNumber] : + positions[sprites[spriteID].Frames[0].rectNumber]); + + const TextureInfo& info = (fallback[n] ? + (*(m_fallback_font->m_texture_files.find(texID))).second : + (*(m_texture_files.find(texID))).second + ); + float char_scale = info.m_scale; + + core::dimension2d size = source.getSize(); + + float scale = (fallback[n] ? m_scale*m_fallback_font_scale : m_scale); + size.Width = (int)(size.Width * scale * char_scale); + size.Height = (int)(size.Height * scale * char_scale); + + // align vertically if character is smaller + int y_shift = (size.Height < MaxHeight*m_scale ? (int)((MaxHeight*m_scale - size.Height)/2.0f) : 0); + + core::rect dest(offsets[n] + core::position2di(0, y_shift), size); + + video::SColor colors[] = {color, color, color, color}; + + video::ITexture* texture = (fallback[n] ? + m_fallback_font->SpriteBank->getTexture(texID) : + SpriteBank->getTexture(texID) ); + + if (texture == NULL) + { + // perform lazy loading + + if (fallback[n]) + { + m_fallback_font->lazyLoadTexture(texID); + texture = m_fallback_font->SpriteBank->getTexture(texID); + } + else + { + lazyLoadTexture(texID); + texture = SpriteBank->getTexture(texID); + } + + if (texture == NULL) + { + continue; // no such character + } + } + + if (m_black_border) + { + // draw black border + video::SColor black(color.getAlpha(),0,0,0); + video::SColor black_colors[] = {black, black, black, black}; + + for (int x_delta=-2; x_delta<=2; x_delta++) + { + for (int y_delta=-2; y_delta<=2; y_delta++) + { + if (x_delta == 0 || y_delta == 0) continue; + driver->draw2DImage(texture, + dest + core::position2d(x_delta, y_delta), + source, + clip, + black_colors, true); + } + } + } + + if (fallback[n]) + { + // draw text over + static video::SColor orange(color.getAlpha(), 255, 100, 0); + static video::SColor yellow(color.getAlpha(), 255, 220, 15); + video::SColor title_colors[] = {yellow, orange, orange, yellow}; + driver->draw2DImage(texture, + dest, + source, + clip, + title_colors, true); + } + else + { + driver->draw2DImage(texture, + dest, + source, + clip, + colors, true); + + } + } + } + + //! returns the dimension of a text + virtual core::dimension2d getDimension(const wchar_t* text) const + { + assert(Areas.size() > 0); + + core::dimension2d dim(0, 0); + core::dimension2d thisLine(0, (int)(MaxHeight*m_scale)); + + for (const wchar_t* p = text; *p; ++p) + { + if (*p == L'\r' || // Windows breaks + *p == L'\n' ) // Unix breaks + { + if (*p==L'\r' && p[1] == L'\n') // Windows breaks + ++p; + dim.Height += thisLine.Height; + if (dim.Width < thisLine.Width) + dim.Width = thisLine.Width; + thisLine.Width = 0; + continue; + } + + bool fallback = false; + const SFontArea &area = getAreaFromCharacter(*p, &fallback); + + thisLine.Width += area.underhang; + + thisLine.Width += getCharWidth(area, fallback); + } + + dim.Height += thisLine.Height; + if (dim.Width < thisLine.Width) dim.Width = thisLine.Width; + + // std::cout << "ScalableFont::getDimension returns : " << dim.Width << ", " << dim.Height << " --> "; + + dim.Width = (int)(dim.Width + 0.9f); // round up + dim.Height = (int)(dim.Height + 0.9f); + + //std::cout << dim.Width << ", " << dim.Height << std::endl; + + return dim; + } + //! Calculates the index of the character in the text which is on a specific position. + virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const + { + s32 x = 0; + s32 idx = 0; + + while (text[idx]) + { + const SFontArea& a = Areas[getAreaIDFromCharacter(text[idx], NULL)]; + + x += a.width + a.overhang + a.underhang + GlobalKerningWidth; + + if (x >= pixel_x) + return idx; + + ++idx; + } + + return -1; + } + //! Returns the type of this font + virtual gui::EGUI_FONT_TYPE getType() const { return gui::EGFT_BITMAP; } + + //! set an Pixel Offset on Drawing ( scale position on width ) + virtual void setKerningWidth (s32 kerning) + { + GlobalKerningWidth = kerning; + } + virtual void setKerningHeight (s32 kerning) + { + GlobalKerningHeight = kerning; + } + //! set an Pixel Offset on Drawing ( scale position on width ) + virtual s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0) const + { + s32 ret = GlobalKerningWidth; + + if (thisLetter) + { + ret += Areas[getAreaIDFromCharacter(*thisLetter, NULL)].overhang; + + if (previousLetter) + { + ret += Areas[getAreaIDFromCharacter(*previousLetter, NULL)].underhang; + } + } + + return ret; + } + virtual s32 getKerningHeight() const + { + return GlobalKerningHeight; + } + + //! gets the sprite bank + virtual gui::IGUISpriteBank* getSpriteBank() const + { + return SpriteBank; + } + + //! returns the sprite number from a given character + virtual u32 getSpriteNoFromChar(const wchar_t *c) const + { + return Areas[getAreaIDFromCharacter(*c, NULL)].spriteno; + } + + virtual void setInvisibleCharacters( const wchar_t *s ) + { + Invisible = s; + } + +private: + + struct SFontArea + { + SFontArea() : underhang(0), overhang(0), width(0), spriteno(0) {} + s32 underhang; + s32 overhang; + s32 width; + u32 spriteno; + }; + + int getCharWidth(const SFontArea& area, const bool fallback) const + { + core::array< gui::SGUISprite >& sprites = SpriteBank->getSprites(); + core::array< gui::SGUISprite >* fallback_sprites = (m_fallback_font != NULL ? + &m_fallback_font->SpriteBank->getSprites() : + NULL); + + const int texID = (fallback ? + (*fallback_sprites)[area.spriteno].Frames[0].textureNumber : + sprites[area.spriteno].Frames[0].textureNumber); + + const TextureInfo& info = (fallback ? + (*(m_fallback_font->m_texture_files.find(texID))).second : + (*(m_texture_files.find(texID))).second + ); + const float char_scale = info.m_scale; + + //std::cout << "area.spriteno=" << area.spriteno << ", char_scale=" << char_scale << std::endl; + + if (fallback) return (int)(((area.width + area.overhang)*m_fallback_font_scale + m_fallback_kerning_width) * m_scale * char_scale); + else return (int)((area.width + area.overhang + GlobalKerningWidth) * m_scale * char_scale); + } + s32 getAreaIDFromCharacter(const wchar_t c, bool* fallback_font) const + { + std::map::const_iterator n = CharacterMap.find(c); + if (n != CharacterMap.end()) + { + if (fallback_font != NULL) *fallback_font = false; + return (*n).second; + } + else if (m_fallback_font != NULL && fallback_font != NULL) + { + *fallback_font = true; + return m_fallback_font->getAreaIDFromCharacter(c, NULL); + } + else + { + // std::cout << "The font does not have this character : <" << (int)c << ">" << std::endl; + if (fallback_font != NULL) *fallback_font = false; + return WrongCharacter; + } + } + const SFontArea &getAreaFromCharacter(const wchar_t c, bool* fallback_font) const + { + const int area_id = getAreaIDFromCharacter(c, fallback_font); + const bool use_fallback_font = (fallback_font && *fallback_font); + + // Note: fallback_font can be NULL + return ( use_fallback_font ? m_fallback_font->Areas[area_id] : Areas[area_id]); + } // getAreaFromCharacter + void setMaxHeight() + { + // FIXME: should consider per-texture scaling + MaxHeight = 0; + s32 t; + + core::array< core::rect >& p = SpriteBank->getPositions(); + + for (u32 i=0; iMaxHeight) + MaxHeight = t; + } + } + core::array Areas; + /** The maximum values of all digits, used in monospace_digits. */ + mutable SFontArea m_max_digit_area; + std::map CharacterMap; + video::IVideoDriver* Driver; + gui::IGUISpriteBank* SpriteBank; + gui::IGUIEnvironment* Environment; + u32 WrongCharacter; + s32 MaxHeight; + s32 GlobalKerningWidth, GlobalKerningHeight; + + core::stringw Invisible; +}; +} + +// The actual bug that was behind this issue was the combination of +// 2d rendering and mipmaps. The issue was reproduced using the special +// draw2dimage version, hence the name. +static bool draw2DImage4c(video::E_DRIVER_TYPE type) +{ + IrrlichtDevice *device = createDevice(type, core::dimension2d(240, 120)); + + if (!device) + return true; // could not create selected driver. + + video::IVideoDriver* driver = device->getVideoDriver(); + + driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS,true); + driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_QUALITY,true); + + video::ITexture* images = driver->getTexture("../media/2ddemo.png"); + driver->makeColorKeyTexture(images, core::position2d(0,0)); + + core::rect imp1(349,15,385,78); + core::rect imp2(387,15,423,78); + + // font cannot handle loading from sub-dirs + io::path cwd = device->getFileSystem()->getWorkingDirectory(); + device->getFileSystem()->changeWorkingDirectoryTo("media"); + + ScalableFont* font = new ScalableFont(device->getGUIEnvironment(), "title_font.xml"); + font->m_fallback_font_scale = 4.0f; + font->m_fallback_kerning_width = 15; + font->setKerningWidth(-18); + font->m_black_border = true; + + /* + Prepare a nicely filtering 2d render mode for special cases. + */ + driver->getMaterial2D().UseMipMaps = true; + driver->getMaterial2D().TextureLayer[0].BilinearFilter = true; + + { + driver->beginScene(true, true, video::SColor(255,120,102,136)); + + driver->enableMaterial2D(); + + // draw fire & dragons background world + driver->draw2DImage(images, core::position2di(), + core::rect(0,0,342,224), 0, + video::SColor(255,255,255,255), true); + + // draw flying imp + driver->draw2DImage(images, core::position2d(114,75), + imp1, 0, video::SColor(255,255,255,255), true); + + // draw second flying imp + driver->draw2DImage(images, core::position2d(220,55), + imp2, 0, video::SColor(255,255,255,255), true); + + driver->draw2DImage(images, core::rect(10,10,108,48), + core::rect(354,87,442,118)); + + video::SColor colors[] = {0xff00ffff, 0xff00ffff, 0xffffff00, 0xffffff00}; + driver->draw2DImage(images, core::recti(10,50,108,88), + core::recti(354,87,442,118), 0, colors, true); + + font->draw( L"WXYZsSdDrRjJbB", core::rect(30,20,300,300), + video::SColor(255,255,255,255) ); + + driver->enableMaterial2D(false); + + driver->draw2DImage(images, core::recti(10,90,108,128), + core::recti(354,87,442,118), 0, colors, true); + + font->draw( L"WXYZsSdDrRjJbB", core::rect(30,60,300,400), + video::SColor(255,255,255,255) ); + + driver->endScene(); + } + font->drop(); + device->getFileSystem()->changeWorkingDirectoryTo(cwd); + + // don't go under 99% as the difference is not very large + bool result = takeScreenshotAndCompareAgainstReference(driver, "-draw2DImage4cFilter.png"); + + device->closeDevice(); + device->run(); + device->drop(); + return result; +} + // This test renders a 3d scene and a gui on top of it. The GUI is // filtered via 2dmaterial (blurred). // TODO: Works only for OpenGL right now @@ -42,8 +760,6 @@ static bool addBlend2d(video::E_DRIVER_TYPE type) gui::IGUIEnvironment* env = device->getGUIEnvironment(); { - gui::IGUIElement* root = env->getRootGUIElement(); - // create the toolbox window gui::IGUIWindow* wnd = env->addWindow(core::rect(0,0,800,480), false, L"Toolset", 0, 100); @@ -65,7 +781,6 @@ static bool addBlend2d(video::E_DRIVER_TYPE type) } video::SMaterial& material2D = driver->getMaterial2D(); - material2D.setFlag(video::EMF_ANTI_ALIASING, true); for (unsigned int n=0; nenableMaterial2D(false); driver->endScene(); - bool result = takeScreenshotAndCompareAgainstReference(driver, "-addBlend2D.png", 98.66f); + bool result = takeScreenshotAndCompareAgainstReference(driver, "-addBlend2D.png", 98.2f); device->closeDevice(); device->run(); @@ -108,17 +823,18 @@ static bool moreFilterTests(video::E_DRIVER_TYPE type) image->setImage(tex); image->setUseAlphaChannel(true); driver->getMaterial2D().TextureLayer[0].BilinearFilter=true; + driver->getMaterial2D().TextureLayer[0].TrilinearFilter=true; { driver->beginScene(true, true, irr::video::SColor(255,255,255,255)); - // all three logos should be with bilinear filtering + // all three logos should be with filtering driver->enableMaterial2D(); - driver->getMaterial2D().setTexture(0, tex); + driver->getMaterial2D().setTexture(0, 0); driver->draw2DImage(tex, irr::core::rect(64, 64, 128, 128), irr::core::rect(0, 0, 88, 31)); - driver->getMaterial2D().setTexture(0, 0); + driver->getMaterial2D().setTexture(0, tex); driver->draw2DImage(tex, irr::core::rect(64, 0, 128, 64), irr::core::rect(0, 0, 88, 31)); gui->drawAll(); @@ -151,5 +867,10 @@ bool twodmaterial() result &= moreFilterTests(video::EDT_DIRECT3D8); result &= moreFilterTests(video::EDT_BURNINGSVIDEO); + result &= draw2DImage4c(video::EDT_OPENGL); + result &= draw2DImage4c(video::EDT_DIRECT3D9); + result &= draw2DImage4c(video::EDT_DIRECT3D8); + result &= draw2DImage4c(video::EDT_BURNINGSVIDEO); + return result; } diff --git a/tests/anti-aliasing.cpp b/tests/anti-aliasing.cpp index 58d157f1..687f17d9 100644 --- a/tests/anti-aliasing.cpp +++ b/tests/anti-aliasing.cpp @@ -52,7 +52,7 @@ static bool testLineRendering(video::E_DRIVER_TYPE type) driver->draw2DLine(core::position2di(10,10), core::position2di(100,100), video::SColor(255,0,0,0)); driver->endScene(); - bool result = takeScreenshotAndCompareAgainstReference(driver, "-lineAntiAliasing.png", 99.42f ); + bool result = takeScreenshotAndCompareAgainstReference(driver, "-lineAntiAliasing.png", 99.17f ); device->closeDevice(); device->run(); diff --git a/tests/draw2DImage.cpp b/tests/draw2DImage.cpp index 0866b08d..70cff392 100644 --- a/tests/draw2DImage.cpp +++ b/tests/draw2DImage.cpp @@ -2,7 +2,10 @@ using namespace irr; -static bool testWithRenderTarget(video::E_DRIVER_TYPE driverType) +namespace +{ + +bool testWithRenderTarget(video::E_DRIVER_TYPE driverType) { // create device @@ -15,13 +18,13 @@ static bool testWithRenderTarget(video::E_DRIVER_TYPE driverType) video::ITexture* RenderTarget=driver->addRenderTargetTexture(core::dimension2d(64,64), "BASEMAP"); - video::ITexture *Image=driver->getTexture("../media/water.jpg"); + video::ITexture *tex=driver->getTexture("../media/water.jpg"); driver->beginScene(true, true, video::SColor(255,255,0,255));//Backbuffer background is pink //draw the 256x256 water image on the rendertarget: driver->setRenderTarget(RenderTarget,true,true,video::SColor(255,0,0,255));//Rendertarget background is blue - driver->draw2DImage(Image, core::position2d(0,0), core::recti(0,0,32,32)); + driver->draw2DImage(tex, core::position2d(0,0), core::recti(0,0,32,32)); driver->setRenderTarget(0, false); //draw the rendertarget on screen: @@ -38,6 +41,35 @@ static bool testWithRenderTarget(video::E_DRIVER_TYPE driverType) return result; } +// draws a complex (interlaced, paletted, alpha) png image +bool testWithPNG(video::E_DRIVER_TYPE driverType) +{ + // create device + + IrrlichtDevice *device = createDevice(driverType, core::dimension2d(160,120)); + + if (device == 0) + return true; // could not create selected driver. + + video::IVideoDriver* driver = device->getVideoDriver(); + + video::ITexture *tex=driver->getTexture("media/RedbrushAlpha-0.25.png"); + + driver->beginScene(true, true, video::SColor(255,40,40,255));//Backbuffer background is blue + driver->draw2DImage(tex, core::recti(0,0,160,120), core::recti(0,0,256,256), 0, 0, true); + driver->endScene(); + + bool result = takeScreenshotAndCompareAgainstReference(driver, "-draw2DImagePNG.png"); + + device->closeDevice(); + device->run(); + device->drop(); + + return result; +} + +} + bool draw2DImage() { bool result = testWithRenderTarget(video::EDT_DIRECT3D9); @@ -45,5 +77,9 @@ bool draw2DImage() result &= testWithRenderTarget(video::EDT_OPENGL); result &= testWithRenderTarget(video::EDT_BURNINGSVIDEO); result &= testWithRenderTarget(video::EDT_SOFTWARE); + + result &= testWithPNG(video::EDT_DIRECT3D9); + result &= testWithPNG(video::EDT_OPENGL); + return result; } diff --git a/tests/drawPixel.cpp b/tests/drawPixel.cpp index a2b959e9..b8456d54 100644 --- a/tests/drawPixel.cpp +++ b/tests/drawPixel.cpp @@ -15,7 +15,7 @@ using namespace gui; One line should run from green at the top left to red at the bottom right. The other should run from cyan 100% transparent at the bottom left to cyan 100% opaque at the top right. */ -static bool runTestWithDriver(E_DRIVER_TYPE driverType) +static bool lineRender(E_DRIVER_TYPE driverType) { IrrlichtDevice *device = createDevice( driverType, dimension2d(160, 120), 32); if (!device) @@ -53,21 +53,84 @@ static bool runTestWithDriver(E_DRIVER_TYPE driverType) return result; } +// this test draws alternating white and black borders with +// increasing thickness. Intended use of this test is to ensure +// the corect pixel alignment within the render window. +static bool pixelAccuracy(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(); + + device->getSceneManager()->addCameraSceneNode(); + + driver->beginScene(true, true, SColor(255,100,101,140)); + u32 start=0; + for (u32 count=1; count<10; ++count) + { + for (u32 j=0; jdrawPixel(start+x, start, (count%2==1)?0xffffffff:0xff000000); + } + ++start; + } + } + start=0; + for (u32 count=1; count<10; ++count) + { + for (u32 j=0; jdrawPixel(start, start+x, (count%2==1)?0xffffffff:0xff000000); + } + ++start; + } + } + for (u32 x=0; x<100; ++x) + { + driver->drawPixel(x, x, 0xffff0000); + } + driver->endScene(); + + bool result = takeScreenshotAndCompareAgainstReference(driver, "-pixelAccuracy.png"); + + device->closeDevice(); + device->run(); + device->drop(); + + return result; +} bool drawPixel(void) { bool passed = true; logTestString("Check OpenGL driver\n"); - passed &= runTestWithDriver(EDT_OPENGL); + passed &= lineRender(EDT_OPENGL); logTestString("Check Software driver\n"); - passed &= runTestWithDriver(EDT_SOFTWARE); + passed &= lineRender(EDT_SOFTWARE); logTestString("Check Burning's Video driver\n"); - passed &= runTestWithDriver(EDT_BURNINGSVIDEO); + passed &= lineRender(EDT_BURNINGSVIDEO); logTestString("Check Direct3D9 driver\n"); - passed &= runTestWithDriver(EDT_DIRECT3D9); + passed &= lineRender(EDT_DIRECT3D9); logTestString("Check Direct3D8 driver\n"); - passed &= runTestWithDriver(EDT_DIRECT3D8); + passed &= lineRender(EDT_DIRECT3D8); + + logTestString("Check OpenGL driver\n"); + passed &= pixelAccuracy(EDT_OPENGL); + logTestString("Check Software driver\n"); + passed &= pixelAccuracy(EDT_SOFTWARE); + logTestString("Check Burning's Video driver\n"); + passed &= pixelAccuracy(EDT_BURNINGSVIDEO); + logTestString("Check Direct3D9 driver\n"); + passed &= pixelAccuracy(EDT_DIRECT3D9); + logTestString("Check Direct3D8 driver\n"); + passed &= pixelAccuracy(EDT_DIRECT3D8); return passed; } diff --git a/tests/fast_atof.cpp b/tests/fast_atof.cpp index 0349b9f5..7fc579ac 100644 --- a/tests/fast_atof.cpp +++ b/tests/fast_atof.cpp @@ -81,7 +81,7 @@ static inline float old_fast_atof(const char* c) } -static bool testCalculation(const char * valueString) +static bool testCalculation_atof(const char * valueString) { const f32 newFastValue = fast_atof(valueString); const f32 oldFastValue = old_fast_atof(valueString); @@ -98,39 +98,56 @@ static bool testCalculation(const char * valueString) return accurate; } +static bool testCalculation_strtol(const char * valueString) +{ + const s32 newFastValue = strtol10(valueString); + const s32 oldFastValue = old_strtol10(valueString); + const s32 strtolValue = (s32)strtol(valueString, 0, 10); + + logTestString("\n String '%s'\n New fast %d\n Old fast %d\n strtol %d\n", + valueString, newFastValue, oldFastValue, strtolValue); + + bool accurate = (newFastValue == strtolValue) || (oldFastValue != strtolValue); + + if (!accurate) + logTestString("*** ERROR - wrong calculation in new method ***\n\n"); + + return accurate; +} + //! Test both the accuracy and speed of Irrlicht's fast_atof() implementation. -bool fast_atof(void) +bool test_fast_atof(void) { bool accurate = true; - accurate &= testCalculation("340282346638528859811704183484516925440.000000"); - accurate &= testCalculation("3.402823466e+38F"); - accurate &= testCalculation("3402823466e+29F"); - accurate &= testCalculation("-340282346638528859811704183484516925440.000000"); - accurate &= testCalculation("-3.402823466e+38F"); - accurate &= testCalculation("-3402823466e+29F"); - accurate &= testCalculation("34028234663852885981170418348451692544.000000"); - accurate &= testCalculation("3.402823466e+37F"); - accurate &= testCalculation("3402823466e+28F"); - accurate &= testCalculation("-34028234663852885981170418348451692544.000000"); - accurate &= testCalculation("-3.402823466e+37F"); - accurate &= testCalculation("-3402823466e+28F"); - accurate &= testCalculation(".00234567"); - accurate &= testCalculation("-.00234567"); - accurate &= testCalculation("0.00234567"); - accurate &= testCalculation("-0.00234567"); - accurate &= testCalculation("1.175494351e-38F"); - accurate &= testCalculation("1175494351e-47F"); - accurate &= testCalculation("1.175494351e-37F"); - accurate &= testCalculation("1.175494351e-36F"); - accurate &= testCalculation("-1.175494351e-36F"); - accurate &= testCalculation("123456.789"); - accurate &= testCalculation("-123456.789"); - accurate &= testCalculation("0000123456.789"); - accurate &= testCalculation("-0000123456.789"); - accurate &= testCalculation("-0.0690462109446526"); + accurate &= testCalculation_atof("340282346638528859811704183484516925440.000000"); + accurate &= testCalculation_atof("3.402823466e+38F"); + accurate &= testCalculation_atof("3402823466e+29F"); + accurate &= testCalculation_atof("-340282346638528859811704183484516925440.000000"); + accurate &= testCalculation_atof("-3.402823466e+38F"); + accurate &= testCalculation_atof("-3402823466e+29F"); + accurate &= testCalculation_atof("34028234663852885981170418348451692544.000000"); + accurate &= testCalculation_atof("3.402823466e+37F"); + accurate &= testCalculation_atof("3402823466e+28F"); + accurate &= testCalculation_atof("-34028234663852885981170418348451692544.000000"); + accurate &= testCalculation_atof("-3.402823466e+37F"); + accurate &= testCalculation_atof("-3402823466e+28F"); + accurate &= testCalculation_atof(".00234567"); + accurate &= testCalculation_atof("-.00234567"); + accurate &= testCalculation_atof("0.00234567"); + accurate &= testCalculation_atof("-0.00234567"); + accurate &= testCalculation_atof("1.175494351e-38F"); + accurate &= testCalculation_atof("1175494351e-47F"); + accurate &= testCalculation_atof("1.175494351e-37F"); + accurate &= testCalculation_atof("1.175494351e-36F"); + accurate &= testCalculation_atof("-1.175494351e-36F"); + accurate &= testCalculation_atof("123456.789"); + accurate &= testCalculation_atof("-123456.789"); + accurate &= testCalculation_atof("0000123456.789"); + accurate &= testCalculation_atof("-0000123456.789"); + accurate &= testCalculation_atof("-0.0690462109446526"); - if(!accurate) + if (!accurate) { logTestString("Calculation is not accurate, so the speed is irrelevant\n"); return false; @@ -141,7 +158,7 @@ bool fast_atof(void) return false; ITimer* timer = device->getTimer(); - enum { ITERATIONS = 100000 }; + const int ITERATIONS = 100000; int i; f32 value; @@ -161,7 +178,7 @@ bool fast_atof(void) value = old_fast_atof("-340282346638528859811704183484516925440.000000"); const u32 oldFastAtofTime = timer->getRealTime() - then; - logTestString(" atof time = %d\n fast_atof Time = %d\nold fast_atof time = %d\n", + logTestString("Speed test\n atof time = %d\n fast_atof Time = %d\nold fast_atof time = %d\n", atofTime, fastAtofTime, oldFastAtofTime); device->closeDevice(); @@ -176,3 +193,87 @@ bool fast_atof(void) return true; } + +//! Test both the accuracy and speed of Irrlicht's strtol10() implementation. +bool test_strtol(void) +{ + bool accurate = true; + + accurate &= testCalculation_strtol("340282346638528859811704183484516925440"); + accurate &= testCalculation_strtol("3402823466"); + accurate &= testCalculation_strtol("3402823466e+29F"); + accurate &= testCalculation_strtol("-340282346638528859811704183484516925440"); + accurate &= testCalculation_strtol("-3402823466"); + accurate &= testCalculation_strtol("-3402823466e+29F"); + accurate &= testCalculation_strtol("402823466385288598117"); + accurate &= testCalculation_strtol("402823466"); + accurate &= testCalculation_strtol("402823466e+28F"); + accurate &= testCalculation_strtol("402823466385288598117"); + accurate &= testCalculation_strtol("-402823466"); + accurate &= testCalculation_strtol("-402823466e+28F"); + accurate &= testCalculation_strtol(".00234567"); + accurate &= testCalculation_strtol("-234567"); + accurate &= testCalculation_strtol("234567"); + accurate &= testCalculation_strtol("-234567"); + accurate &= testCalculation_strtol("1175494351"); + accurate &= testCalculation_strtol("11754943512"); + accurate &= testCalculation_strtol("11754943513"); + accurate &= testCalculation_strtol("11754943514"); + accurate &= testCalculation_strtol("-1175494351"); + accurate &= testCalculation_strtol("123456789"); + accurate &= testCalculation_strtol("-123456789"); + accurate &= testCalculation_strtol("123456.789"); + accurate &= testCalculation_strtol("-123456.789"); + accurate &= testCalculation_strtol("-109446526"); + + if(!accurate) + { + logTestString("Calculation is not accurate, so the speed is irrelevant\n"); + return false; + } + + IrrlichtDevice* device = createDevice(video::EDT_NULL); + if (!device) + return false; + ITimer* timer = device->getTimer(); + + const int ITERATIONS = 1000000; + int i; + + s32 value; + u32 then = timer->getRealTime(); + for(i = 0; i < ITERATIONS; ++i) + value = strtol("-3402823466", 0, 10); + + const u32 strtolTime = timer->getRealTime() - then; + + then += strtolTime; + for(i = 0; i < ITERATIONS; ++i) + value = strtol10("-3402823466"); + const u32 strtol10Time = timer->getRealTime() - then; + + then += strtol10Time; + for(i = 0; i < ITERATIONS; ++i) + value = old_strtol10("-3402823466"); + const u32 oldstrtol10Time = timer->getRealTime() - then; + + logTestString("Speed test\n strtol time = %d\n strtol10 time = %d\nold strtol10 time = %d\n", + strtolTime, strtol10Time, oldstrtol10Time); + + device->closeDevice(); + device->run(); + device->drop(); + + if (strtol10Time > (1.2f*strtolTime)) + { + logTestString("The fast method is slower than strtol()\n"); + return false; + } + + return true; +} + +bool fast_atof(void) +{ + return test_fast_atof() && test_strtol(); +} diff --git a/tests/filesystem.cpp b/tests/filesystem.cpp index a9f36b26..18a94c8f 100644 --- a/tests/filesystem.cpp +++ b/tests/filesystem.cpp @@ -159,7 +159,7 @@ bool filesystem(void) assert(changed); // adding a folder archive which just should not really change anything - device->getFileSystem()->addFolderFileArchive( "./" ); + device->getFileSystem()->addFileArchive( "./" ); if ( fs->existFile(empty) ) { diff --git a/tests/ioScene.cpp b/tests/ioScene.cpp index 1edce833..1f68de93 100644 --- a/tests/ioScene.cpp +++ b/tests/ioScene.cpp @@ -18,7 +18,6 @@ static bool saveScene(void) if (!device) return false; - IVideoDriver* driver = device->getVideoDriver(); ISceneManager * smgr = device->getSceneManager(); ISkinnedMesh* mesh = (ISkinnedMesh*)smgr->getMesh("../media/ninja.b3d"); @@ -59,9 +58,11 @@ static bool saveScene(void) smgr->addCameraSceneNode(); + logTestString("Test scene.irr"); smgr->saveScene("results/scene.irr"); bool result = binaryCompareFiles("results/scene.irr", "media/scene.irr"); + logTestString("Test scene2.irr"); smgr->saveScene("results/scene2.irr", 0, node3); result &= binaryCompareFiles("results/scene2.irr", "media/scene2.irr"); @@ -69,18 +70,17 @@ static bool saveScene(void) device->run(); device->drop(); - // TODO: The relative texture names are not yet fixed, so ignore this test - return true; + return result; } static bool loadScene(void) { - IrrlichtDevice *device = createDevice(video::EDT_BURNINGSVIDEO, + IrrlichtDevice *device = createDevice(video::EDT_BURNINGSVIDEO, core::dimension2du(160,120), 32); - if (!device) - return false; + if (!device) + return false; - IVideoDriver* driver = device->getVideoDriver(); + IVideoDriver* driver = device->getVideoDriver(); ISceneManager* smgr = device->getSceneManager(); // load scene from example, with correct relative path device->getFileSystem()->changeWorkingDirectoryTo("results"); @@ -88,8 +88,8 @@ static bool loadScene(void) smgr->addCameraSceneNode(0, core::vector3df(0,0,-50)); device->getFileSystem()->changeWorkingDirectoryTo(".."); - bool result = false; - device->run(); + bool result = false; + device->run(); if (driver->beginScene(true, true, video::SColor(0, 80, 80, 80))) { smgr->drawAll(); @@ -115,9 +115,9 @@ static bool loadScene(void) device->closeDevice(); device->run(); - device->drop(); + device->drop(); - return result; + return result; } bool ioScene(void) @@ -125,3 +125,4 @@ bool ioScene(void) bool result = saveScene(); result &= loadScene(); return result; +} diff --git a/tests/lightMaps.cpp b/tests/lightMaps.cpp index d7c8dae5..6c86de14 100644 --- a/tests/lightMaps.cpp +++ b/tests/lightMaps.cpp @@ -21,7 +21,7 @@ static bool runTestWithDriver(E_DRIVER_TYPE driverType) ISceneManager * smgr = device->getSceneManager(); bool result = true; - bool added = device->getFileSystem()->addZipFileArchive("../media/map-20kdm2.pk3"); + bool added = device->getFileSystem()->addFileArchive("../media/map-20kdm2.pk3"); assert(added); if(added) diff --git a/tests/loadTextures.cpp b/tests/loadTextures.cpp index e2e70203..44f38b7d 100644 --- a/tests/loadTextures.cpp +++ b/tests/loadTextures.cpp @@ -60,7 +60,7 @@ bool loadFromFileFolder(void) readFile->drop(); // adding a folder archive - device->getFileSystem()->addFolderFileArchive( "../media/" ); + device->getFileSystem()->addFileArchive( "../media/" ); ITexture * tex3 = driver->getTexture("tools.png"); assert(tex3); diff --git a/tests/main.cpp b/tests/main.cpp index ee8d6578..9c3c22b9 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -104,6 +104,7 @@ int main(int argumentCount, char * arguments[]) TEST(drawRectOutline); TEST(guiDisabledMenu); TEST(makeColorKeyTexture); + TEST(material); TEST(renderTargetTexture); TEST(textureFeatures); TEST(textureRenderStates); diff --git a/tests/makeColorKeyTexture.cpp b/tests/makeColorKeyTexture.cpp index 0e86d7ff..0eafcec7 100644 --- a/tests/makeColorKeyTexture.cpp +++ b/tests/makeColorKeyTexture.cpp @@ -65,21 +65,10 @@ bool makeColorKeyTexture(void) { bool result = true; - //result &= doTestWith(EDT_DIRECT3D9, false); - //result &= doTestWith(EDT_BURNINGSVIDEO, false); - result &= doTestWith(EDT_SOFTWARE, false); - //result &= doTestWith(EDT_OPENGL, false); - - //result &= doTestWith(EDT_DIRECT3D9, true); - //result &= doTestWith(EDT_BURNINGSVIDEO, true); - result &= doTestWith(EDT_SOFTWARE, true); - //result &= doTestWith(EDT_OPENGL, true); - -/* - bool result = doTestWith(EDT_SOFTWARE, false); result &= doTestWith(EDT_BURNINGSVIDEO, false); - result &= doTestWith(EDT_SOFTWARE, true); + result &= doTestWith(EDT_SOFTWARE, false); result &= doTestWith(EDT_BURNINGSVIDEO, true); -*/ + result &= doTestWith(EDT_SOFTWARE, true); + return result; } diff --git a/tests/material.cpp b/tests/material.cpp new file mode 100644 index 00000000..ad6f6be9 --- /dev/null +++ b/tests/material.cpp @@ -0,0 +1,67 @@ +#include "testUtils.h" + +using namespace irr; + +static bool polygonOffset(video::E_DRIVER_TYPE type) +{ + IrrlichtDevice* device = createDevice(type, core::dimension2d(160, 120)); + + if (device == 0) + return true; + + video::IVideoDriver* driver = device->getVideoDriver(); + if (!driver->queryFeature(video::EVDF_POLYGON_OFFSET)) + return true; + scene::ISceneManager* smgr = device->getSceneManager(); + + // create first plane + scene::ISceneNode* plane = smgr->addMeshSceneNode(smgr->addHillPlaneMesh( + "plane", core::dimension2df(10,10), core::dimension2du(2,2)), 0, -1, + core::vector3df(0,0,20), core::vector3df(270,0,0)); + + if (plane) + { + plane->setMaterialTexture(0, driver->getTexture("../media/t351sml.jpg")); + plane->setMaterialFlag(video::EMF_LIGHTING, false); + plane->setMaterialFlag(video::EMF_BACK_FACE_CULLING, true); + } + + // create second plane exactly on top of the first one + scene::ISceneNode* plane2 = smgr->addMeshSceneNode(smgr->addHillPlaneMesh( + "plane2", core::dimension2df(5,5), core::dimension2du(2,2)), 0, -1, + core::vector3df(0,0,20), core::vector3df(270,0,0)); + plane2->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false); + + smgr->addCameraSceneNode(); + + // test back plane to back + plane->getMaterial(0).PolygonOffsetDirection=video::EPO_BACK; + plane->getMaterial(0).PolygonOffsetFactor=7; + + driver->beginScene(true, true, video::SColor(255,113,113,133)); + smgr->drawAll(); + driver->endScene(); + bool result = takeScreenshotAndCompareAgainstReference(driver, "-polygonBack.png"); + + //reset back plane + plane->getMaterial(0).PolygonOffsetFactor=0; + // test front plane to front + plane2->getMaterial(0).PolygonOffsetDirection=video::EPO_FRONT; + plane2->getMaterial(0).PolygonOffsetFactor=7; + driver->beginScene(true, true, video::SColor(255,113,113,133)); + smgr->drawAll(); + driver->endScene(); + result &= takeScreenshotAndCompareAgainstReference(driver, "-polygonFront.png"); + + device->closeDevice(); + device->run(); + device->drop(); + return result; +} + +bool material() +{ + bool result = polygonOffset(video::EDT_OPENGL); + result &= polygonOffset(video::EDT_DIRECT3D9); + return result; +} diff --git a/tests/matrixOps.cpp b/tests/matrixOps.cpp index 05fdbff1..d2ca1bae 100644 --- a/tests/matrixOps.cpp +++ b/tests/matrixOps.cpp @@ -13,8 +13,8 @@ using namespace gui; namespace { -// Basic tests -bool matrices(void) +// Basic tests for identity matrix +bool identity(void) { bool result = true; matrix4 m; @@ -189,6 +189,14 @@ bool rotations(void) result &= (vec1.equals(core::vector3df(0.f, 0.f, 180.0f), 0.000002f)); assert(result); + rot1.makeIdentity(); + rot1.setRotationDegrees(core::vector3df(270.f,0,0)); + rot2.makeIdentity(); + rot2.setRotationDegrees(core::vector3df(-90.f,0,0)); + vec1=(rot1*rot2).getRotationDegrees(); + result &= (vec1.equals(core::vector3df(180.f, 0.f, 0.0f))); + assert(result); + return result; } @@ -233,7 +241,7 @@ bool isOrthogonal(void) bool matrixOps(void) { bool result = true; - result &= matrices(); + result &= identity(); result &= rotations(); result &= isOrthogonal(); result &= transformations(); diff --git a/tests/media/Burning's Video-2dmatFilter.png b/tests/media/Burning's Video-2dmatFilter.png index d52fa50de7b7060d35470587f9ae117879a41f86..489386c0810fe973e490bfb7f5b7849e62104745 100644 GIT binary patch literal 3043 zcmZ`*2{e>@8-Fn&J7u3R8VxtHXDc%_VK7p%8?-R?C1f9Kh3tc()&CLwCIm9>s0N_R$Auln` z-x+rZJ2T_C``Skn060rfNWIG;55MJmdVvQ7TYKt)U2l>d;e+K}nzcuCaN_W*ceK;l zKY_i<`GLztVM+U9rKgY3n0}rS;*#bdg8)j$S=j3cjzE zPlk!v5F{rf&7uTVCKW;3m6(*`WaB-Rgwits85QAZ;c2#VyUzdu5XeYiX9Cg_`1k<; z3LKXPw!gL&9b2X}DevWd!eSL=imoSzvgzpPgoo|*5$RiF;mhy~2k`*Qx?TVpv#?u=7DceEi~HR zQX?MVOGJdRh%oobK&3+@Px#8N)sDHT)d%bd0S)&;J0%K^^*zaYNbP!{k#_!hH32>l z8^m_|)5?YF@Dq}+rLC9z&1APw2^xWKe7!UjX2l#vW#}M~f5fG!^ z&i?BAc|tcx!Vhj38j#F*xm&JUsMloY=8ED9#wjQG^ELgi1_r*X>?|qCLrA^WI#{gg zVP$0{kw|N6YwzE`_we+LYnHmUNNJ7(%x!H&xmXoCv;*gQ`};2|iFCHNi;9YBMbR6w z*49wi$jC@FG4$T9Me>q!%ZFG&LryDj9ChB;0n-?^kp!pFH3fB{Q!wXB;RTx?2((VFE&}hw(?0$ z&BiBefr6sq3;Bi35#J`ii8p!bo=HhbwLYU_cU!=%{ur_v`mGY+OeSFPc>KI$RLP+Q z9xJedjeoUsi!yt3n~Wd(!|N(~$W)f{hDxgM^0I0Q!V$NU;Y6DrUa&JTXgVsLD(e)! zk&>^oX=k99ksq{Q(qckzclW8fu)bwt3jzoW%+Y-M##eH6m(TKDWdAfe+UPw}+Zev3 zyU}lPS6Gqo?AiSMeCSLrHY_a6!^1=Wae6Rufw&s-FktCy)}^t?tx?{y>*i2_Pu1=p z1A5MG%@wAdvac;`XgG6}BPcNNN%;OVp)#Gn5i^?KD%H%N4goYDqDW6%K4jnhs;{L- zil}?c<RldhpYu=5;@pPh-PeaUWNojvDd${GRj&CR$XLRb*U$Wq&j zlLa99*4AYD{vIa>M_yi@*GR2=Y2)_`!`>_+E+KK^kAT_kv-It$W@kSB!Vhzc?@x5s zjW2IKO-okpsHEYV#+R|lMgM~iv7)RZ0yflyE`{J?-^mqIPtX)SU5Ob~5Ii|6tC zH7zfwzB&`zD=wq7YOO~u}+?m%trTBQ7>CQAZ8z{C&XV{=C}Zv}7Q zR_Ivo=hmYyiTi{0O?52vu=SO6DSeX8*0J4=)W%$Towy{;(c9bGz?Cww=XBp+3J>rz zDd(D!uKtync(`L-nFMZKbq!~{@x3<~jA5F@2JXmNKg#Qm7vRTu?vTrD@dX8G2`w!x zyl~n|LnyVfstUe4SLmh|wqv^5MCX+Kws1k2829(!FsR4%MwA)#F2gq70Wi|jw*iUX z;n^yXlsuTf9TYqkI#MvcYu@G#Kt-V(#%qn$kz3rQ1Rw8Gn=tggFl-O6ij#YHt9_LX zM+as*DXh}=wO;P>LNMl!)j5hzjusYGK?{T2;-`mdxq-KD-|8nY)P_7TP)9h45<4;b zw8uFohBX>w(1|3~A^qC;DFDmIHQUc>(Q#5BQ$`;ZY=SE$-I|jM51mYn>0De+TO9hJ zuC0yF_D)MdFAw>&DH~pXxw^*E^^of1s=IY3A4*z3o=Dt%`t+9PU_@y{!{^#>hd71gF~sV^!m0je z7ONp=z&zFrR+7m9-BYXE<+%NhmCt`v`ngM`quL}QDD?U5b-}KbPdlNvuiUz!@7WY- zXo(&OI(hHjaFNw(*kPkfL~?;g^Uq#0Bw@tPUfAD96Gc{%mj>y}#ROPzv}?iqfLmG2 z8BCCCxXkFeNR=m0;ofH@zY&*TEi)D%lQh5-Qku(0>FatTQeQna+RqG}I(TGdeAtwa z<0um%9mI7EDJ38fVG7{KTH>7rK1d<;DqK2pgbNPB?Z{EfY0Ic*&Z9rt$XFS?Fa#*}U+jOp@!am5awpLm7Gn#OYZoVfBHD4Seu%eadSy<-nyYX_M-eTJ;}z)_3G8%vL1vlZu^si#KgXi zhlbCtzM4v?@EAZB70DHYCQ6bHvA|eOOcsaw-%Z4gCboxoUNhmjG`;@#i?^1Br>Ib(36*skK|;QQn4pm#e=RPSC9SPx zX4aKjjcN)cvO`WKr)_LXc-an_+uSd3N)KMAIyQ~3@1+Qy%o+82TccZ`uBxi4sYxL& zQ7F#R$_%JBmsDQV)w%fj_1Z%}_w`lc_IJz5Z1G3sWdLapAb;OG%A%(+$MEaQYK{-G|C%xX9TTV zNX3fD=v0{8^M!+nNVv(b!~rvjw)SOajzxjB!Z*F7GZ4QYH{`jOlfyn?P*_-O#+#CI zes4Eb@aS~CJ{K34qb7We94wt-WQInY$PtO!qO%2zHQL+jj%-*U)Z5wFF)V<#wl+hb z&OQE&hc(U3&uc~Q21;irp{we{qogxLz+g4B1-n1vCo$IO@bK`5fa8Tu1_p)Ku3>NY zfERkWSvR9-{%eyj)pbwf2u*6dCr#cs&qyxUzuX($SM(XLp^hG(h2?`@x?>J*8%h}c z`Y~jexhA@sXeN*(s1c7u!@Xb2Bd{ZAK z7$JBBDm;An7 zjhKbCW}=*5eztUZ6RnwZ9851DG~^+mqnLC`^a_7uY}ZzoOcK0d|So?Or|Z4D$}4&YK|# I`p&Wc0;kQ$Jpcdz literal 3073 zcmd5;i96KY7yptpGwMP!7@-Y5#$GSwJ+O1$JHOM{U; z>tIBMtc4~t#+EhXH^29v`2EiF+5WrZXv7Ge< zXM};cIrDByfIa{m?Z%+b+K1&ZbFcb~k4SYjvvM-WTqC2Mrb%#0YiH=}QN0pHmmrn6 zIYo@GmHm7H*8Z>CBL93Ud_sJoD}pW6yUkak^jcTtVvj4IZ_nY08b!s{%6dp3-{>e0 zSkN!BRc~`S;^x54?pu$KNm&^I?Q*%(?YGWsCLaXfo1H`;NNh-sld})~#!UK9yRZru zw*`-yiqau%Wq9Xn#jiNGvSOyn4y|b$b3!Cn|IW3ru;tG`*e4=rLCFs{BO9yN?adrILYqU2XN3V#E~Ok3lgho=0in>)%MM2Z8lnqa$xH(f@%WKW&yc9^ z-|)>TUB&D=W=D$5X=e$-?{^uePW47l)qKEEZ}exmHr&;4Z~AqmdDli)I(lb5<1}sx z3g$&J3NC;|@}t&zzI?gdIr`EpDJwfWKYDX)Xb4NITBa_QAC=TBbE&U-@xp^f(nz=b za0fbqw;9$29|GlWYLbBj7w-6$#4a!Noc>BL%#gjyRxSEQ>-lZ6TP^QO z@ERUmkwl?I=KU&~D!umPXx*^4hydp%kB@kp6phWqDL1vFKnva#5Jp+5sSiKhmQ6Sr z_v_cM2`YXm&nPl4VzK1#Q3-no2Rt5cwBCnsb#--caJX-(P+7l3{pj;7IBy$k6@IY4 zWwO?Nyx@Y*Y-bAHXlueN&4_Znhd4AeWU4Uu*( z0U!|r1A|TGct%FX$l##IIK|uPpMMPn>KvV!Q4q;B3|&rC&EpWB01=EYEb85PBm<5l4S>Nti&jwI>$|r8w_UTP$Oc*=uhtA;$lt6^rLt;a*#qUAi@QgOS4c) zudIaAHncmTU6`C=4GoRS$w^sRS&K9|nnecQ4+vD5tcanwV=I`VPQddznK@w zYFxL&MW38g28bJCJ1XIBUnxsJ=125upJ_CeZ>~RWTve+O|K7I!b2q=7ks~!>dfl3& zo{D~n_Q?7&^xFDOX(`2jtm-b)d7Q%K5g2#@ROvfhl$9ktf>*(`&rNJV`g8_?CoKt? z-TE0IK_oZS!^2rFDVsM?I4-6n&t&02dS+u|0dw5&QGBtE4l_NyhdC}J06w32`2?eL zta_Gy@U6YfAJ#APlW8&<0(a_Iw zGXo@fB5m{Fd_#3o@qB5Xj>(dc1-YYDWokmmeXl_i40i%VOq2`xB6Da*R<%B(jkhQ1 zk>^x+7c+ck$08NF8qw(R{5g}Ef7$->R|=CFP+;_`C}Meg8|{8jX{xX^YiC^YCRZl$ zd`gN9PgQlAtjRLf*4341h-2wb1w%zhTFvk3>i#Y%@tvgNtur(me}6gl(Pv||OJ3+9 zYMnvH`TI}Q2Q!kT5Q|GoMsjvI9PW&qmX=nnI!ssJ@uFTa3r)C_E}IE4jCzaEle=Ax z>qYVB8rRgQERnLM&CUjnmv5$S(o>;wP2IGKUS5|71j03|u}`0BB7QF>Cxg*GK0c$3k)ikQ zv8%jkob(rj>IbfV@8~4`Xd@Dl-qkO4*t>?f$;QR2^PdG^%Z|rVyRKc(dO1k?JzT&B$xzk{u*;se`LxbMN?B# zv`rvB-Y!=TBE`400j1Gs_=i82mYM_a)f6+(>Js^6@-u8mNDX3OYkAU8J5XAL)xc^h zDk|b!fkL75_4S!5?9KJi8>m#F&;KMRY|}?>9xu6QIrdw^CA*^OiIQTGw7Gkn6i5HJ2kor<2C-E{AP(@7z)4 z4X|Rk@BQ2@Y0 z%MKiP2ont@&#bkJ)%|&U9@L7o`*n~PxmN?^0!WQZ;cMa#a-+fvizd8&{ zl_=Byf7!%RO&qt5xPRVnpqgx3^AtxN1e7xDC^Pn~X`BBlU(Vuw1E;X=Jh=3paQgGQ zs%v2B7ONAlPdQiSB#Af|udrZnl;kQ_e z>=`YQvb|V5VZ~vx`3)x$%{h5lW?x?RfBGH=K=Q2OVTFzRd7RcBz?j*h%TXQ={sZRU B=|=zn diff --git a/tests/media/Burning's Video-draw2DImage4cFilter.png b/tests/media/Burning's Video-draw2DImage4cFilter.png new file mode 100644 index 0000000000000000000000000000000000000000..a245525bbccd0ec1e9b7b5e0c26eeb5704fd8bbf GIT binary patch literal 20835 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

|= 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 JPDHLkV1mt+l_dZG literal 29997 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 IM6N<$g03og(EtDd diff --git a/tests/media/Burning's Video-pixelAccuracy.png b/tests/media/Burning's Video-pixelAccuracy.png new file mode 100644 index 0000000000000000000000000000000000000000..24190ef8f7bd81b0e0150fdb0f63288b072aabac GIT binary patch literal 728 zcmeAS@N?(olHy`uVBq!ia0vp^3xK$Sg9%9fI*@0>5W1g>%-|=i$Ecb%+-)wJY zX|L8kkRfxWG{MC0rJBLJx%)zWmnM8~I9U4igDsQWn;ngUH#f5vNfQB!< z1tfZBF)jZ1a4XPN$-ggFF&x$l6fChTQc{L!$KrRNy? z6>$Tp>FcZ$O!T7q4`jrw^J~1gBj7L4@XlR8GnT)WmFS(H&5&SHx5%*JVnzO8pvHA> zz)*S1%hq#DTAE*?ce{y-fzjw^G>{+z2@#2rb!_136-W6)N_@hkmKt|1C znFN!4VL;E-Yx7F<+KY4a9Q!|&6G(Kix+N<}OY}Z&oO#bee#Xw;Jua<(+4$2S{2&BY`Z^|IFKUyXRsuhh%> zFJ#`v1M%F(Vn4h*k1@sTAB%V}J5_?aW<5i4qrAzkjR*RrZ$&%^?ai+F_407eqGGmo g^@0i}qKWU~ye)?^)%Psh15B|Dp00i_>zopr05uUnlK=n! literal 0 HcmV?d00001 diff --git a/tests/media/Burning's Video-rttAndText.png b/tests/media/Burning's Video-rttAndText.png index dea90d9108639e0e885703dec7dfaef3b7069368..cf7eaf1893cc82c359fa4d8e399b3ea9505ce2aa 100644 GIT binary patch literal 6789 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 Lyt-Vqj5+*&RBbe< literal 1908 zcmbuASu`7n0*1pxMMzO=E25UBwakd6wp7wVs}WlVu{TmBmex9IFGXdHAuVaq*wZ75 z7BQh?OVQT86H)upYDcKrLKL^>zTI=5=FBeE~){G<-%Q5QraY-wW=7*>!1GqHRs7`P!Q9tz?SYe zo~Tq~VBV@2c1LK16Ear$Y$$E7XCzskuOplpWY%vgHPoZ8rIiFpv9Atk>Ep5Y+hTT1 zJoG&Dp!8`-_OUC^uZxBf7t@TdNbMplbz9;#t!1YJn2B28Ehju`G`uH328ZS_Vq-r_} zX;FIh1zV*yJ8C?ljpuhxP-QeUq3*D44f-yVMp!EUGmJ8gQY;I}XUtJME=u$}O6Ftw zdt3L2yaV}HlDP^yiO#tFquw|ke6t8~>sYmLe z)+;~p8lmndwnMI4QAV)a$XAGTN$9={_d1Dc3Uiq?`m{Bv+-+c}l=NX;5)TXYGG6F~ zk)=+3GR~N9w~jFU+;{5kH@1HT>a*VhKIOoCUF~qT@B@qF&N@h9B@CeN?sFf;*6-x( z1@r9Cob;*qLN%?kPONXz?(lx7Fj?v-nzD{go3s5T)u+BGq_3yvGiD_6<6u(m!=d%tUrx5&JM6k<33>Rr?ajHx-g!}sIkpSzx zNP=q}%cbEt4Y`aEi)Fh7+QXL7hksoPo*Pz7$IrgibFDF4 z{_4#P?ryMCQ<^+23*{O^x#ziW#1xAPvrK|4r%}og`D(=TxVn}h+M~v2Dudqty~O}N zQSyyHd-Xacqw(IGmlLa#ATDwx!`B^Ikf{R~J1oF!0of>UhbiE_4wYlqE)g;mAy&0h z@PhfrA4NoY*2TF6>qK2$5AwRvf|aLLpV>X1PBbH%2&q5-dHN*_U)JdP1@1QcU71S3 z#88Ib64;cX=Y?P;lf~auzPqWy$*at*b-4yy^{#7If7spFwWY8WPAH7F!w*97oZI`C z;G%_raBQBTgAIcrV~dh#nhDy44(3(hRmh@?60PbyN_AmQ<NBS zZ!y4Yy5`^+-{w8jZft~e-DS9@Cg26G*SYR0+Tx%p;_ifUFY!aeCigd!r<2-^4;8cX zpSsA9mm@ZOu)dxp*6iltTe#F^Q0r|!cwlR@D6dc!l|tG;8AV0b(k~Lhph5yX@LT(@ zuwJ%jlrZZ4++uxb(}v}qs#Yg56%YsxR-NOLm$}EILE&msy4%(-@8KIx_fcDSXO4!*rh|xxD SC4aXm0CUCG;-#5a(tiNmS$Wa` diff --git a/tests/media/Direct3D 9.0-draw2DImage4cFilter.png b/tests/media/Direct3D 9.0-draw2DImage4cFilter.png new file mode 100644 index 0000000000000000000000000000000000000000..2b64f445bef79adf4976be1a619dc7f22c1d2800 GIT binary patch literal 32938 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 literal 0 HcmV?d00001 diff --git a/tests/media/Direct3D 9.0-polygonBack.png b/tests/media/Direct3D 9.0-polygonBack.png new file mode 100644 index 0000000000000000000000000000000000000000..76fb2e2d1dc78f573bf9b3397dd0bf832fb836b8 GIT binary patch literal 8592 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 literal 0 HcmV?d00001 diff --git a/tests/media/Direct3D 9.0-polygonFront.png b/tests/media/Direct3D 9.0-polygonFront.png new file mode 100644 index 0000000000000000000000000000000000000000..76fb2e2d1dc78f573bf9b3397dd0bf832fb836b8 GIT binary patch literal 8592 zcmb_?`8yPD)c#mPvNV)rZ$d_PvWGI1wHjp0$3BIav5tL9W@L*d3?sr&BNR>cCHpc+ zwk%_3WHO^<-(G#cf5Q9z@GRH${PLW0opYc2xzCeu-~0|Iy8t@?0N^yb3xS@l`KKe8 zjq!BeZ}CwF0K@=BkXx3)xb=vsbeZvFGUG>itko#R@I}G9p(o|=$em05tfV`;i=Ckx zao1X1C*IayC@@^QGEq`0?N8@y;qI1xgWV`3UmPN;V1qaF|6pUJwm0H<_~&t8wr4KX7*a$)d{h7c66iFNa#rgyOHnM>@Ub|Dl0>lHfqBde} zjXmBJE3zmjcHa*>+P50GOj}VkQwFFh`=ct#Vp>Rm0ZJ927-Is4b;=;dbuzY!Xc~_J zjY#0o%)#WyU-}76SNBg@Q*tc%5h_$w5^7~}F)dvgkg(gYCUONfkYYd6j0%jSF#;3e}mW%C~4uh+P(wVp}4uDOJVZEjx#$d4RsO4*H z8-^k4GVr{e&Q5^-is$%HD z%JDO_Q0g74GGaMZ8YH$hnTbMuBtX~5rQ_ZXiyr6G$UbVF{Q6z|6!+Q_nikLqthlLi zuP~K(!+Ai1f*T(j3tVrk9t)+{V#q$3#%AD%qvIpwY{aF(TLZRM591iop~PehVtdhSay+V9H^ZHmQc>2U>PiK<_)UVjHaHX0X)kfDwb3HROD=J zJyv;B#mpJ%?52CNH>exF3lJNq!c`Tux0-%R^rtRTE1FYeF9CqZ>!rHQCo5W@?Bd#y zg_RYSXzWjWV{qln%rCk_QVOlwoxNz6){>cZq)ER+ZM-r~llPN-<(82jA* zx4$zC1Y*it*mVF*gaF3dmDpXgc+RB9?=x_D<15al7#arWpt~l`q*5%JPJ(Q`-n$cs z7{+h>uC{z20x`CSL3r^m;zF8wpXmR)Ohi&G2BSPpzCZ&^2dtY}89@36`aobj&cgV& zS%D68>G&O;Ci?BN^^<|Uhze}|AweHvQ#{rJ)pJCBZzRLs)TYqw^IsZe8j zg!lSy_*QbmlWN?i%BYTJZKk-jqgs=Vy?q9LOd8}I0#PxufFIq`{XEp&E$j*N1WN!` z#zHMDY}D^@Op+ZbL<`?f*W;7 z!+(#D!jfO6ps^;h%%0wHn|V-B@el5D`o?bczAF1b?GOGEZc(8h^dN$OAQAgK(+lRB zq~7K?F#oX#6Scsv+&BnmzYV2sI%?o0M>5f)Q8ui0T>HFe_x*M7%Bh0DrYG8MT{ zE$f`PJ~b=ljsOqypu5{l{HV} zi`v`^=Sr14pNeVkpLb(Ho;Q&rW@s8BQMFWsKWMepKaNkRE;9M+M7RLwfv?(RQ!|zv zcypWrxOCG&9&r+>3%Kvrn{Lu8;eO@6>9ca}h`(bdJ@sT65VfvTwBt5mDr{c@7G_$T z9NRzLFRT;r?s7e=>S?6CtH;&}>Y0*cWNr(vS@udi!+bot%KmxumR?q1xU$golsuo% zllrf+XINI-)2U|c+($h{Y(I&Fo=Z_QgggW?bQNT{W063uD}WF|x+@3x6EBhlDjshurOz3H+fw2pn0#k#EW% zs%KB6dX7Jq5;JfHJZwN5fbGuHciCaPZ2;29JZTCh$KX_-Q{Va8xj+9$L&E#&$mFK| zRn35;Av>+S!8KJg3!C7$Ig+K95jcTOh!n0aG}9af_oR)59jujBaG~^TlK)+SXb^Ua z(vNvqT)0&%4E&ISg%#Aga%yDM+(*;-ngdkZ@y0|+UicqacCmsZ@T~6jnGC&%0HlJy zaVrVN9?W0e*8d2#r2{pOd3iof;oN4B^dh@JJ(j7)=Xzh|;a+Kl$N=Rn`$F5iQ-n;H zE4>l(eQcD(zyk!SYrj+?ek!N(FJ69bl-7DR7Q2iyXeVK?2~%}q4YHuyLU_qa;aUy6 zM|M^_X9R~jG#)EyM0!eVEGsq!C~`+&djx{1s56EOE@box9{}%+-sf1aZc>aGldKpkSA}%cIxZGm@o2fwcGeNnVp=d32+a z%M7v-VhrYeU@c4lXo$(oH5Od>KaxSrOmV&ND9wbb#_O)P@OVz#e)TY24xYxbsDZ zGJCu--h4RQyxIu-edlFI6#B_kbuIukEdLqcK7fB7#_N18?<|6Uj``8m@=^s~fHdtP z^>Da(k1Bt>An$@o)r`G+n_E~T6Ixjtd9bdA7z96C4c2_YhGX;%b30A&lJ~TrdZ?eZ zl`sR-DnB?A`hwsc@`#mJ(f_G0NySh%S0_A9F-CQEHOx)=znHn8TdE%ox@bX-QsKYW zy*Zp_<=j!jcQjKjU1-S?Fcr>s_aHUgG|0Cr{c7(8JJ$)Lb&`x~ZoH`E4-O_Y#0UeI+PBwI`c2!?I;@Tuo~dh` z|H!1r%+0o0KyKlchX)2GezCB<8tkf;=%xewguee_!Xm?ikXCqs58#@lqp5subE_fv zA4qbe^m8T8zc&oJQsBH~_lPs)gP}dG1$$SnLrr4b8VPSYT%^M*p8_@y3o%QfLI~pr z8~P-od3!hha1N#WD1#-xj9M3@HaQrUx58E@V(SxUP;X*9tnyqq?oX?fmZpZ{<~PN0 zo|bR?0zu06hADYbLHcKAg}GwtKiw7oYDi5(JhW;VEZID$m=WbdYLuk^@yTXL8oJlD z_)T!>8QkL1dVbB-@k23S0z$0%0q0p1mX&Yb2gcGx9j`c9t2kbh^y1pIkKpP-Ehi(mGx&YQs|46k4gl=tcO08xcA`TW3P$BTA9tAfRv| zk;Ta&b7uc{8)|q@T*F?;%Dtfd{n8^Zv+ybo{%DM!al(ELpTIjp@+T|*?Q++K(4#sG z4}nwaf*!_b*6Hg%1_!7h&XCEnnRaNX*TCNKu?~d<;odo3NA7G!>JDA_CU=(lZT5J( z`eeIAm;9t3uu+wF^lNlV?Vw(3MI6*F^wln!pHN8Ly5|_FVfM-gA8iW3UjU(}=kywE zReMOGbPfEBmiLz0h3ELf9i@A-Km1s;r*s^y7qE^} zC`3p?jhW%z3|H_%A^L!>;b$pn-#Lwge3sm>QF%K4Sw8cRYsAHj!Ry?p=s&(8VgPuf zu8^ot<;qKUTT9s&?1$N&98$E{cb_0Y_7WNV67E$_vf`T|zIA)$%v41o!QyUH&49i- zWqOd7Q39G#>BTj%4@K32`P)}4%D@Rsho7MG_;Ey$Okg(C8@H8^nOXtftW{l?=~Phl zr@8d1_uDg9h0l*(pw=;B+!EzlJ>@&n?DZW7_7FArOAaMq0q65`s(Nu=7g-g_jdfu= zG>3*Hw^i*yFg}EUJa{}jv4PNRd~addxP(b4Sd~;_?S%NhN0DiT#=gkM$O>Z9!4g@G zeiHu5ov9}5NOE~?rCsoiv6%lLxPOzQ`T$NBrHLAYm(*_xrHYCaAexC6=S0J|f1GPd zKC^QiVa9PT$9{JdpV9IXy9<>|V3T7SYN)S2_8u^;~R6zX5~9{1$E^%(~1v)Bn~5-9xsHD#cGq? z=O&h(C3swKf6IyCvM>j`^RWhw&ID#bd9JPUYZ|)P@j3Vmxre>pp*Nd@m4_=odksFj zn~n4bx89>jIILti0sW|-Y_MyWC=GIh!CY7K zj(59ek7&q$HbxzlMD6!D9{(K{J)G+m{wVR9!9|W3obkji$&FYmhkT3}R zSj~0!8hs`ldBUKlJ|)A`eiqd>KLwwib(;VL)syitS>?e(%B5?^h%RPj;Whz!-NULuLs3FoQol^MVQUXNE(P5wyH)^a3Mev}wV8hYBd-fR6MVz}hQ*B?`UC2_ zrcz?lX?C3AX2{l>)j+}=;+YoHZJhXPJBgCHyHoTXRNLDz0fKPhGu-p6JTt1XrLsX{ zfYZ&Py{dAm<#jZgne?^Qnc(zAF*aR_VlR0(+Z3TZDqvv4QDR~cK^0yiks$SCInVwA z_kM@PWn}{4(YRHAMRd#0&uLWCVP>({4b58o;kIKN9n(n$)85Uq;v)1+U;&8rPH9CfWq`#9kc{m+TJV8uhwVWbA`)jLjJWUt-Ltx z)%&ds_Tz;_`GLN@a2%-GvmtHW^CopN`FzZ@M?gN=Qo2 zblNZj?}&WyqE2@52NH(KU)q1qyK6Nb#MD$%`sf~~OG?m15RVa9qQ&xJ%|lAf z`|UB(%?4TZk5RwQ%)V25j|4yhbn;3rKTs!;Hnp2WHd!{tLcL_g!uSS2<{U4Jl+5Z; zsDm6C!4{RlPro6_{L0RHH>C6K7#Tq(SNncr{G~T>;#)J!OZ&K97_W)SirMgIqBmY; zP#K69FXZOEGoeI*r9jS)=|GJu9Gjc)v1&eRvK>EZLr;52pvx=X4uMHd!y38dV7`nNgq1P4JoGA_~ z)?XY7X#V`hM@THF%n_g$r&w37@iggp_aEfu4U{;uWQZH|d&+?dG^c?@y^s<6bFifA z4p5uJsho!a)52VMH^9mYVyav1JT5cf{bE950@`-}35Q6bZlQuvQO#!E_TnA}YM0?p z_cA_9D?|n&3v}w*%*@PquK}TdvMnMakf*$$A(S|13t+1K%6Z@4_c5PKOEPc&Wg#IH zrF9Q}pA+n3@yyPS#K~HgR2rG$~j_;=TzDIja_WwFMfFEzv&(8cq%iRw7sQuyQ z!w<(|nvuTXUoXXJowZ7Rs?WPVRFB!!3;g9&T@}puwFEtVhfO|VuMUyY3@zfqV)a~; zaNj$fAsnkHZqdk{1Koec&N1p}>y+r3i@T~=DHFxCT~}sUAG{o)jM*tLagILfeIh%F z5Q+L)efAZ^?M?AJT%$O{=I{Ai>Sq#~&`TK5l;|lD>91Xa7Kb$TWa}4usG0c>fTcpS z)LIvz#>N^HsB!X#w7UeNPgRbd7km)?J?agN)lk%8;Op5_k1~}E_mC`jq$Vq0+zn7? z&Jh9@xKV-+5wF~@N^7FyJAOgUEf2u1XIh-2wZC75A8qCjhR-zAp4zj0AgqW5D>~feKIOA` zH*?cClOwue7^go{IAXfs-5*%ty7-g8*bAYy*;wTVN%tq{JGD#PJ3E2WL;F`Uv8-wp zJ(F}K_^cxEfp`X*e@+bRzM?o;DKm833t7@0^oJM+>+n4l5Z{A&s$M8w(y=s4#&#!s zMIBu!De$D}gg#cQp|89j`@?;*!43P8HV(W71U8lj-1Q%O~vmOad4eBId`3$wmb|fi&2EO37Ka%d#DO>@c-` zF7^dj3y|p7R;5c zi{UMeS)L?CN_-a3C*Lf<1zpy>)b!~o{&uABq26aAcpS=wM0sR0BCEWY5=%W1Uh!@G zM2-NgR4Ykrck`{1n~QTo40xzo-_$ze3bI7eySlV!)_`UsWwrG;!C ztFE*>&ay++FY|~e)v-xg-+4suSi_*|NA(j@qEQg-$9a+aJ+miAv;CV~w3O9zR%tGp z@m=&iHJk+LyHsgWCv`*hV)M5w2QJ2m^=*xDu}@rw+6}9V zzk{oFPpg3?olbvd^&Z1GciM6w8}}TB_rh+iHs%dd?n*Eqc7BW%doZqs>o~Gb0odgJ zLuO)1LNa?H(M8_eHGY>nv>at$vO&VJnbDjarpcQ1#g4&j5C0&q%<#PtWTG zbK}RU79$Ous{Ia5`OKE2It#8-sA3%qto zj!C4vw6U|duTp_G#*jYsRHwkhMLlD4fjHpbFHwJ$pX2Jh{Ov{yI(cbA-IlVfy}lyN zmbu5QjseE*&p$93_sS9t%AQ@AlsD93337=>Xgo! z?tU%I6eLTVrsMp|h{XR)lD|ZopdS8Zjn*&-;{jgX*12N*9`neL{Wb8x!UxfQYxH_9 zU`2{W0PS2-Zm$q}xpzU&oR`hFMybtp>@Bm|6**U4;GJ9GZjI!jYV50G#e=J%^5}PC zl^u_pLKc0s$2B|Dbh%nY%zQo@P(ZkJ@q2I5N1zM~=jivn5+Fam;y6~EkZ87s=-jLk zmCmz?;?Z{B{FwkGD>mO*s%02m5ykIZOb~1S4D9eoo;j$L`OV?bDyv2=kd<_qfkmR( zs%knSoy>*i>+gbM#eJ)W_MecSL2TpFfiAn*U?UT!~u@ z!C={tRDwCT_u^|stlfu1$M@B zTbkJu_g{N+IB>A4v36u7nurDLf%U{WV&@lBYO!25c!8)L#dzP|$`6l~&+S}>2!6nS zHe=xCa#gd+Ol^??`B?+Znd?ARo=gC8R{;+@OF_pUo!-zQ~hWP(V5+F|$m&P!Y_f8L;ruE1QyjldMd8q^qxMCL_o z+k%(dP~2rin|ormgf>f25fR37uTov8cU}p;{*6Cl z-NWVgeKfhrl50+lF?r)z=3bpwcp&9D(R@IwAF~4oX0I&%UZvqNt74o8$=&>g%HUSH zzm=b7>2b@{(^lf*yH#Publ7eHJ$%+~7XT}oe zYtf`lA%?%&gY(1=^i!V7F82tS(xCMV$_`IJf1Fwe zjr7#w%uDo!{RVEW4v?Z#hn^|eh<}rnmMAq03p>?4tddY&n-385G9q7x2N=)qS6nRc z(WPYrG5iw%U@-aj7vL_WYf%wuN>Gab&!Zh1!Er`v=E@&cXZxXrOZu`Xf}OWl;&lsi z+6pY++tXXdP`oj0M>3zDmDU5z0)M}s_91D+f}eVEJjqP`yI>g+YTUjYQ{HZB<}5Nq znmJJRbkGn^1%0!1fEVV3ECr9byYrpb5b~k=Nv6-;y+0~JU;)5j zN~uurc8FY@(@i||U+0-AXPPAZpWh3xE?}e~FKl4Jbl%yZ3yC>EcmFV4VAP}Ew8K^m zeZ=N?I+Z`Ld2;EYn(*Rf@UDE*?!DP&#NFFR&9X(|Dvwubv%}HNA#V3~Sa?&9XS==G tXC0S@mJGh$d-UIh!}jI><>mws*nG5Sdii_%=}!xQk%2j+{I*l<{{TSe0S5p8 literal 0 HcmV?d00001 diff --git a/tests/media/Irrlicht Software Driver 1.0-pixelAccuracy.png b/tests/media/Irrlicht Software Driver 1.0-pixelAccuracy.png new file mode 100644 index 0000000000000000000000000000000000000000..70a441dfddd926612f2e2cdb70976e9491750945 GIT binary patch literal 728 zcmeAS@N?(olHy`uVBq!ia0vp^3xK$Sg9%9fI*@0*Teo zOs~y5cYt%wp_Oy*$KC4@-yI#js&=mZJw5K^f800co||u%DRcejYw>kE^R6?!xVevU z_p?*4xD2G&OJyZ`gLiTER4>mD4c5G2n@|vIThrfgvEoe_!{Uzz)-t<24vgmQNmp?G zw{JVc?sBOOxd$?AqGmTLbb-3~3j!aXap4h1kojbNiyp0lFBWF-??&^YF2Qp%IzXB3l#el?mHz2XH3`i_q zb|9msVr}EaA2;f@$T57#tEsHawe34#HRB*dOk7<3|DQLjL>j*`^&C5XMbJP>J|G^b zY1ccT6&bcbXYI6Nb$jgemAmJd>MJRU-udgw6HMyXF*aQM(Rqx)?eSh-=AL7_r8p&e zx0_2CNag3)B$(X0G~qzTo?U^!fQV&gUi`7^7@ON;cV7OUW98mJEw@{Nfzw+AG$AVR zK*pc7CJ840Vt_vUAFc)@w8bQP8*>s&`qaWqq8@iF)i$IXT!`(6?zHJ zjOX#qIN6iVlBV|9vvKCJinxSl2J@t5aQ39LtKE%A=r9xeu{R{S(Z0(>>_>3rF{XOm zGZ7E8RVBG=_DUo-=1)u1`LLI%hqdm)i(^doc^S!#;il_8zC7A~)8#o2pZ|joEJPFU a^+V<#N>F|~M+=x@89ZJ6T-G@yGywqqyGX|X literal 0 HcmV?d00001 diff --git a/tests/media/OpenGL-draw2DImage4cFilter.png b/tests/media/OpenGL-draw2DImage4cFilter.png new file mode 100644 index 0000000000000000000000000000000000000000..57ef58a8795d2f18f48ee794599582d937d6fd3f GIT binary patch literal 36266 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

L!e7&Z(Gl<>QM(k@m-?qe{?pu;*g- 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 literal 0 HcmV?d00001 diff --git a/tests/media/OpenGL-polygonBack.png b/tests/media/OpenGL-polygonBack.png new file mode 100644 index 0000000000000000000000000000000000000000..b6d5770796db6ab8f5779f27b31937d3c604001e GIT binary patch literal 8166 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 literal 0 HcmV?d00001 diff --git a/tests/media/RedbrushAlpha-0.25.png b/tests/media/RedbrushAlpha-0.25.png new file mode 100644 index 0000000000000000000000000000000000000000..e17d5be97d4bbac5ee2adb28033a350cec531fd8 GIT binary patch literal 35945 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 UcIa+cbpQYW07*qoM6N<$g4=*kOaK4? literal 0 HcmV?d00001 diff --git a/tests/media/licenses.txt b/tests/media/licenses.txt new file mode 100644 index 00000000..b8c06bcf --- /dev/null +++ b/tests/media/licenses.txt @@ -0,0 +1,7 @@ +All assets of this test suite, except where noted below, are tkane from the main Irrlicht repository and are copyright/licensed as stated there. The source code is copyright by the authors, except where stated differently inside the files. + +title_font.xml and its png files are taken from SuperTuxKart. They are in the public domain. + +The image RedbrushAlpha-0.25.png is Copyright 1999 Pieter S. van der Meulen. This image, including the alpha channel, may be used, edited and reproduced freely. + Taken from http://www.libpng.org/pub/png/png-RedbrushAlpha.html + diff --git a/tests/media/title_font.png b/tests/media/title_font.png new file mode 100644 index 0000000000000000000000000000000000000000..f075aa2fbeb7a7755862773b37fc4d21099d76ce GIT binary patch literal 190372 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%0AL4_+g9~T)?_1Q< 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