From a90caf686761d45a4e416adf0c0e129dbcc9b1b1 Mon Sep 17 00:00:00 2001 From: cutealien Date: Mon, 25 Nov 2019 14:35:58 +0000 Subject: [PATCH] Merging r5918 through r5944 from trunk to ogl-es branch git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@5945 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 3 + examples/08.SpecialFX/main.cpp | 27 +- examples/27.PostProcessing/Makefile | 38 ++ examples/27.PostProcessing/PostProcessing.cbp | 56 +++ .../PostProcessing_vc10.vcxproj | 231 ++++++++++ .../PostProcessing_vc10.vcxproj.user | 3 + .../PostProcessing_vc11.vcxproj | 235 ++++++++++ .../PostProcessing_vc11.vcxproj.user | 3 + .../PostProcessing_vc12.vcxproj | 235 ++++++++++ .../PostProcessing_vc12.vcxproj.user | 3 + .../PostProcessing_vc14.vcxproj | 235 ++++++++++ .../PostProcessing_vc14.vcxproj.user | 3 + examples/27.PostProcessing/main.cpp | 417 ++++++++++++++++++ examples/BuildAllExamples_vc10.sln | 10 + examples/BuildAllExamples_vc11.sln | 10 + examples/BuildAllExamples_vc12.sln | 34 +- examples/BuildAllExamples_vc14.sln | 34 +- examples/Demo/CDemo.cpp | 8 +- include/EDriverFeatures.h | 3 + include/IAnimatedMeshSceneNode.h | 19 +- include/ICameraSceneNode.h | 2 + include/IMeshSceneNode.h | 14 +- include/IShadowVolumeSceneNode.h | 27 ++ media/027shot.jpg | Bin 0 -> 18390 bytes media/pp_d3d9.hlsl | 43 ++ media/pp_opengl.frag | 17 + media/pp_opengl.vert | 24 + source/Irrlicht/CAnimatedMeshSceneNode.cpp | 6 +- source/Irrlicht/CAnimatedMeshSceneNode.h | 6 +- source/Irrlicht/CCubeSceneNode.h | 4 +- source/Irrlicht/CD3D9Driver.cpp | 5 +- source/Irrlicht/CD3D9Driver.h | 6 +- source/Irrlicht/CMeshSceneNode.h | 4 +- source/Irrlicht/COctreeSceneNode.cpp | 6 +- source/Irrlicht/COctreeSceneNode.h | 2 +- source/Irrlicht/COpenGLDriver.cpp | 10 + source/Irrlicht/COpenGLExtensionHandler.cpp | 3 + source/Irrlicht/CShadowVolumeSceneNode.cpp | 300 +++++++++---- source/Irrlicht/CShadowVolumeSceneNode.h | 18 +- source/Irrlicht/CSkyBoxSceneNode.cpp | 6 +- source/Irrlicht/CSkyBoxSceneNode.h | 2 +- source/Irrlicht/CSkyDomeSceneNode.cpp | 6 +- source/Irrlicht/CSphereSceneNode.cpp | 6 +- source/Irrlicht/CSphereSceneNode.h | 2 +- source/Irrlicht/CVolumeLightSceneNode.cpp | 6 - source/Irrlicht/CWGLManager.cpp | 6 +- source/Irrlicht/Irrlicht10.0.vcxproj | 1 + source/Irrlicht/Irrlicht10.0.vcxproj.filters | 3 + source/Irrlicht/Irrlicht11.0.vcxproj | 1 + source/Irrlicht/Irrlicht11.0.vcxproj.filters | 3 + source/Irrlicht/Irrlicht12.0.vcxproj | 1 + source/Irrlicht/Irrlicht12.0.vcxproj.filters | 3 + source/Irrlicht/Irrlicht14.0.vcxproj | 1 + source/Irrlicht/Irrlicht14.0.vcxproj.filters | 3 + tests/main.cpp | 8 +- .../Burning's Video-stencilSelfShadow.png | Bin 0 -> 16396 bytes tests/media/Burning's Video-stencilShadow.png | Bin 0 -> 3275 bytes tests/media/Direct3D 9.0-orthoStencil.png | Bin 2104 -> 2109 bytes .../media/Direct3D 9.0-stencilSelfShadow.png | Bin 14567 -> 12782 bytes tests/media/Direct3D 9.0-stencilShadow.png | Bin 5671 -> 3312 bytes tests/media/OpenGL-orthoStencil.png | Bin 2084 -> 2075 bytes tests/media/OpenGL-stencilSelfShadow.png | Bin 12250 -> 12917 bytes tests/media/OpenGL-stencilShadow.png | Bin 5321 -> 3346 bytes tests/orthoCam.cpp | 3 +- tests/stencilshadow.cpp | 75 +++- tests/tests-last-passed-at.txt | 8 +- 66 files changed, 2041 insertions(+), 207 deletions(-) create mode 100644 examples/27.PostProcessing/Makefile create mode 100644 examples/27.PostProcessing/PostProcessing.cbp create mode 100644 examples/27.PostProcessing/PostProcessing_vc10.vcxproj create mode 100644 examples/27.PostProcessing/PostProcessing_vc10.vcxproj.user create mode 100644 examples/27.PostProcessing/PostProcessing_vc11.vcxproj create mode 100644 examples/27.PostProcessing/PostProcessing_vc11.vcxproj.user create mode 100644 examples/27.PostProcessing/PostProcessing_vc12.vcxproj create mode 100644 examples/27.PostProcessing/PostProcessing_vc12.vcxproj.user create mode 100644 examples/27.PostProcessing/PostProcessing_vc14.vcxproj create mode 100644 examples/27.PostProcessing/PostProcessing_vc14.vcxproj.user create mode 100644 examples/27.PostProcessing/main.cpp create mode 100644 media/027shot.jpg create mode 100644 media/pp_d3d9.hlsl create mode 100644 media/pp_opengl.frag create mode 100644 media/pp_opengl.vert create mode 100644 tests/media/Burning's Video-stencilSelfShadow.png create mode 100644 tests/media/Burning's Video-stencilShadow.png diff --git a/changes.txt b/changes.txt index df6532b9..490dc90c 100644 --- a/changes.txt +++ b/changes.txt @@ -9,6 +9,9 @@ Changes in ogl-es (not yet released - will be merged with trunk at some point) -------------------------- Changes in 1.9 (not yet released) +- Bugfix: OpenGL no longer antialiasing stencil buffer when driver has antialiasing disabled. +- Add IShadowVolumeSceneNode::setOptimization to allow disabling optimizations for meshes were contour shadows fail. +- Stencil shadows work now also with directional light - Drivers can now try to create textures from images in more exotic color formats (like floating point formats). It depends on the driver how much that works (so far mainly OpenGL can handle it somewhat). - Fix OpenGL to no longer switch colors red and blue in 24-bit RGB format. But warnings added to documentation to avoid 24-bit textures as they are generally just trouble. - No longer try to convert ECF_R5G6B5 to ECF_A1R5G5B5 on OpenGL (just made texture-loading seem to fail). diff --git a/examples/08.SpecialFX/main.cpp b/examples/08.SpecialFX/main.cpp index ddac5d88..c72674b7 100644 --- a/examples/08.SpecialFX/main.cpp +++ b/examples/08.SpecialFX/main.cpp @@ -112,17 +112,16 @@ int main() */ // create light - - node = smgr->addLightSceneNode(0, core::vector3df(0,0,0), + scene::ILightSceneNode * lightNode = smgr->addLightSceneNode(0, core::vector3df(0,0,0), video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 800.0f); scene::ISceneNodeAnimator* anim = 0; - anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f); - node->addAnimator(anim); + anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f, 0.0005f); + lightNode ->addAnimator(anim); anim->drop(); // attach billboard to light - node = smgr->addBillboardSceneNode(node, core::dimension2d(50, 50)); + node = smgr->addBillboardSceneNode(lightNode, core::dimension2d(50, 50)); node->setMaterialFlag(video::EMF_LIGHTING, false); node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); node->setMaterialTexture(0, driver->getTexture(mediaPath + "particlewhite.bmp")); @@ -259,15 +258,29 @@ int main() anode->setPosition(core::vector3df(-50,20,-60)); anode->setAnimationSpeed(15); + /* + Shadows still have to be drawn even then the node causing them is not visible itself. + We have to disable culling if the node is animated or it's transformations change + as otherwise the shadow is not updated correctly. + If you have many objects and this becomes a speed problem you will have to figure + out some manual culling (for exampling hiding all objects beyond a certain distance). + */ + anode->setAutomaticCulling(scene::EAC_OFF); + // add shadow anode->addShadowVolumeSceneNode(); smgr->setShadowColor(video::SColor(150,0,0,0)); - // make the model a little bit bigger and normalize its normals - // because of the scaling, for correct lighting + // make the model a bit bigger anode->setScale(core::vector3df(2,2,2)); + // because of the scaling we have to normalize its normals for correct lighting anode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); + // let the dwarf slowly rotate around it's y axis + scene::ISceneNodeAnimator* ra = smgr->createRotationAnimator(irr::core::vector3df(0, 0.1f, 0)); + anode->addAnimator(ra); + ra->drop(); + /* Finally we simply have to draw everything, that's all. */ diff --git a/examples/27.PostProcessing/Makefile b/examples/27.PostProcessing/Makefile new file mode 100644 index 00000000..c5905005 --- /dev/null +++ b/examples/27.PostProcessing/Makefile @@ -0,0 +1,38 @@ +# 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 = 27.PostProcessing +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 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/27.PostProcessing/PostProcessing.cbp b/examples/27.PostProcessing/PostProcessing.cbp new file mode 100644 index 00000000..b9fc0873 --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing.cbp @@ -0,0 +1,56 @@ + + + + + + diff --git a/examples/27.PostProcessing/PostProcessing_vc10.vcxproj b/examples/27.PostProcessing/PostProcessing_vc10.vcxproj new file mode 100644 index 00000000..65e47f37 --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing_vc10.vcxproj @@ -0,0 +1,231 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + 27.PostProcessing + {2B885150-210F-4CA7-957E-2C3D75974308} + PostProcessing + + + + Application + false + MultiByte + + + Application + false + MultiByte + + + Application + false + MultiByte + + + Application + false + MultiByte + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win64-VisualStudio\ + + + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win64-VisualStudio\ + + + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + .\Debug/PostProcessing.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Debug/PostProcessing.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win64-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win64-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Release/PostProcessing.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + .\Release/PostProcessing.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win64-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win64-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + Disabled + Disabled + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + EnableFastChecks + EnableFastChecks + MaxSpeed + MaxSpeed + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + + + + \ No newline at end of file diff --git a/examples/27.PostProcessing/PostProcessing_vc10.vcxproj.user b/examples/27.PostProcessing/PostProcessing_vc10.vcxproj.user new file mode 100644 index 00000000..695b5c78 --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing_vc10.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/27.PostProcessing/PostProcessing_vc11.vcxproj b/examples/27.PostProcessing/PostProcessing_vc11.vcxproj new file mode 100644 index 00000000..8a732900 --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing_vc11.vcxproj @@ -0,0 +1,235 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + 27.PostProcessing + {F864F96D-F6AE-43E2-9A12-218B1A081255} + PostProcessing + + + + Application + false + MultiByte + v110 + + + Application + false + MultiByte + v110 + + + Application + false + MultiByte + v110 + + + Application + false + MultiByte + v110 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win64-VisualStudio\ + + + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win64-VisualStudio\ + + + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + .\Debug/PostProcessing.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Debug/PostProcessing.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win64-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win64-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Release/PostProcessing.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + .\Release/PostProcessing.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win64-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win64-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + Disabled + Disabled + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + EnableFastChecks + EnableFastChecks + MaxSpeed + MaxSpeed + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + + + + \ No newline at end of file diff --git a/examples/27.PostProcessing/PostProcessing_vc11.vcxproj.user b/examples/27.PostProcessing/PostProcessing_vc11.vcxproj.user new file mode 100644 index 00000000..695b5c78 --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing_vc11.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/27.PostProcessing/PostProcessing_vc12.vcxproj b/examples/27.PostProcessing/PostProcessing_vc12.vcxproj new file mode 100644 index 00000000..8aef23bd --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing_vc12.vcxproj @@ -0,0 +1,235 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + 27.PostProcessing + {17E74625-568E-4008-897E-CAD12A332B0C} + PostProcessing + + + + Application + false + MultiByte + v110 + + + Application + false + MultiByte + v110 + + + Application + false + MultiByte + v110 + + + Application + false + MultiByte + v110 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win64-VisualStudio\ + + + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win64-VisualStudio\ + + + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + .\Debug/PostProcessing.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Debug/PostProcessing.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win64-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win64-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Release/PostProcessing.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + .\Release/PostProcessing.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win64-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win64-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + Disabled + Disabled + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + EnableFastChecks + EnableFastChecks + MaxSpeed + MaxSpeed + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + + + + \ No newline at end of file diff --git a/examples/27.PostProcessing/PostProcessing_vc12.vcxproj.user b/examples/27.PostProcessing/PostProcessing_vc12.vcxproj.user new file mode 100644 index 00000000..695b5c78 --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing_vc12.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/27.PostProcessing/PostProcessing_vc14.vcxproj b/examples/27.PostProcessing/PostProcessing_vc14.vcxproj new file mode 100644 index 00000000..a39c5551 --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing_vc14.vcxproj @@ -0,0 +1,235 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + 27.PostProcessing + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2} + PostProcessing + + + + Application + false + MultiByte + v140 + + + Application + false + MultiByte + v140 + + + Application + false + MultiByte + v140 + + + Application + false + MultiByte + v140 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win64-VisualStudio\ + + + ..\..\bin\Win32-VisualStudio\ + ..\..\bin\Win64-VisualStudio\ + + + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + .\Debug/PostProcessing.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Debug/PostProcessing.tlb + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win64-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win64-visualstudio;%(AdditionalLibraryDirectories) + true + Console + + + + + + + .\Release/PostProcessing.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win32-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win32-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + .\Release/PostProcessing.tlb + + + + + MaxSpeed + OnlyExplicitInline + ..\..\include;%(AdditionalIncludeDirectories) + WIN32;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c07 + + + ..\..\bin\Win64-VisualStudio\27.PostProcessing.exe + ..\..\lib\Win64-visualstudio;%(AdditionalLibraryDirectories) + Console + + + + + + + Disabled + Disabled + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + EnableFastChecks + EnableFastChecks + MaxSpeed + MaxSpeed + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + + + + \ No newline at end of file diff --git a/examples/27.PostProcessing/PostProcessing_vc14.vcxproj.user b/examples/27.PostProcessing/PostProcessing_vc14.vcxproj.user new file mode 100644 index 00000000..695b5c78 --- /dev/null +++ b/examples/27.PostProcessing/PostProcessing_vc14.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/27.PostProcessing/main.cpp b/examples/27.PostProcessing/main.cpp new file mode 100644 index 00000000..e60b7b69 --- /dev/null +++ b/examples/27.PostProcessing/main.cpp @@ -0,0 +1,417 @@ +/** Example 027 Post Processing + +@author Boshen Guan + +This tutorial shows how to implement post processing for D3D9 and OpenGL with +the engine. In order to do post processing, scene objects are firstly rendered +to render target. With the help of screen quad, the render target texture +is then drawn on the quad with shader-defined effects applied. + +This tutorial shows how to create a screen quad. It also shows how to create a +render target texture and associate it with the quad. Effects are defined as +shaders which are applied during rendering the quad with the render target +texture attached to it. + +A simple color inverse example is presented in this tutorial. The effect is +written in HLSL and GLSL. + +We include all headers and define necessary variables as we have done before. +*/ +#include "driverChoice.h" +#include "exampleHelper.h" + +#include + +using namespace irr; + +#ifdef _MSC_VER +#pragma comment(lib, "Irrlicht.lib") +#endif + +/* +We write a class derived from IShaderConstantSetCallBack class and implement +OnSetConstants callback interface. In this callback, we will set constants +used by the shader. +In this example, our HLSL shader needs texture size as input in its vertex +shader. Therefore, we set texture size in OnSetConstants callback using +setVertexShaderConstant function. +*/ + +IrrlichtDevice* device = 0; +video::ITexture* rt = 0; + +class QuadShaderCallBack : public video::IShaderConstantSetCallBack +{ +public: + QuadShaderCallBack() : FirstUpdate(true), TextureSizeID(-1), TextureSamplerID(-1) + { } + + virtual void OnSetConstants(video::IMaterialRendererServices* services, + s32 userData) + { + core::dimension2d size = rt->getSize(); + + // get texture size array + f32 textureSize[] = + { + (f32)size.Width, (f32)size.Height + }; + + if ( FirstUpdate ) + { + TextureSizeID = services->getVertexShaderConstantID("TextureSize"); + TextureSamplerID = services->getPixelShaderConstantID("TextureSampler"); + } + + // set texture size to vertex shader + services->setVertexShaderConstant(TextureSizeID, reinterpret_cast(textureSize), 2); + + // set texture for an OpenGL driver + s32 textureLayer = 0; + services->setPixelShaderConstant(TextureSamplerID, &textureLayer, 1); + } + +private: + bool FirstUpdate; + s32 TextureSizeID; + s32 TextureSamplerID; +}; + +class ScreenQuad : public IReferenceCounted +{ +public: + + ScreenQuad(video::IVideoDriver* driver) + : Driver(driver) + { + // --------------------------------> u + // |[1](-1, 1)----------[2](1, 1) + // | | ( 0, 0) / | (1, 0) + // | | / | + // | | / | + // | | / | + // | | / | + // | | / | + // | | / | + // | | / | + // | | / | + // |[0](-1, -1)---------[3](1, -1) + // | ( 0, 1) (1, 1) + // V + // v + + /* + A screen quad is composed of two adjacent triangles with 4 vertices. + Vertex [0], [1] and [2] create the first triangle and Vertex [0], + [2] and [3] create the second one. To map texture on the quad, UV + coordinates are assigned to the vertices. The origin of UV coordinate + locates on the top-left corner. And the value of UVs range from 0 to 1. + */ + + // define vertices array + + Vertices[0] = irr::video::S3DVertex(-1.0f, -1.0f, 0.0f, 1, 1, 0, irr::video::SColor(0,255,255,255), 0.0f, 1.0f); + Vertices[1] = irr::video::S3DVertex(-1.0f, 1.0f, 0.0f, 1, 1, 0, irr::video::SColor(0,255,255,255), 0.0f, 0.0f); + Vertices[2] = irr::video::S3DVertex( 1.0f, 1.0f, 0.0f, 1, 1, 0, irr::video::SColor(0,255,255,255), 1.0f, 0.0f); + Vertices[3] = irr::video::S3DVertex( 1.0f, -1.0f, 0.0f, 1, 1, 0, irr::video::SColor(0,255,255,255), 1.0f, 1.0f); + + // define indices for triangles + + Indices[0] = 0; + Indices[1] = 1; + Indices[2] = 2; + Indices[3] = 0; + Indices[4] = 2; + Indices[5] = 3; + + // turn off lighting as default + Material.setFlag(video::EMF_LIGHTING, false); + + // set texture warp settings to clamp to edge pixel + for (u32 i = 0; i < video::MATERIAL_MAX_TEXTURES; i++) + { + Material.TextureLayer[i].TextureWrapU = video::ETC_CLAMP_TO_EDGE; + Material.TextureLayer[i].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + } + } + + virtual ~ScreenQuad() {} + + + //! render the screen quad + virtual void render() + { + // set the material of screen quad + Driver->setMaterial(Material); + + // set matrices to fit the quad to full viewport + Driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); + Driver->setTransform(video::ETS_VIEW, core::IdentityMatrix); + Driver->setTransform(video::ETS_PROJECTION, core::IdentityMatrix); + + // draw screen quad + Driver->drawVertexPrimitiveList(Vertices, 4, Indices, 2); + } + + //! sets a flag of material to a new value + virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue) + { + Material.setFlag(flag, newvalue); + } + + //! sets the texture of the specified layer in material to the new texture. + void setMaterialTexture(u32 textureLayer, video::ITexture* texture) + { + Material.setTexture(textureLayer, texture); + } + + //! sets the material type to a new material type. + virtual void setMaterialType(video::E_MATERIAL_TYPE newType) + { + Material.MaterialType = newType; + } + +private: + + video::IVideoDriver *Driver; + video::S3DVertex Vertices[4]; + u16 Indices[6]; + video::SMaterial Material; +}; + +/* +We start up the engine just like before. Then shader programs are selected +according to the driver type. +*/ +int main() +{ + // ask user for driver + video::E_DRIVER_TYPE driverType=driverChoiceConsole(); + if (driverType==video::EDT_COUNT) + return 1; + + // create device + device = createDevice(driverType, core::dimension2d(640, 480)); + + if (device == 0) + return 1; // could not create selected driver. + + video::IVideoDriver* driver = device->getVideoDriver(); + scene::ISceneManager* smgr = device->getSceneManager(); + + /* + In this example, high level post processing shaders are loaded for both + Direct3D and OpenGL drivers. + File pp_d3d9.hlsl is for Direct3D 9, and pp_opengl.frag/pp_opengl.vert + are for OpenGL. + */ + + const io::path mediaPath = getExampleMediaPath(); + io::path vsFileName; // filename for the vertex shader + io::path psFileName; // filename for the pixel shader + + switch(driverType) + { + case video::EDT_DIRECT3D9: + psFileName = mediaPath + "pp_d3d9.hlsl"; + vsFileName = psFileName; // both shaders are in the same file + break; + + case video::EDT_OPENGL: + psFileName = mediaPath + "pp_opengl.frag"; + vsFileName = mediaPath + "pp_opengl.vert"; + break; + } + + /* + Check for hardware capability of executing the corresponding shaders + on selected renderer. This is not necessary though. + */ + + if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) && + !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)) + { + device->getLogger()->log("WARNING: Pixel shaders disabled "\ + "because of missing driver/hardware support."); + psFileName = ""; + } + + if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) && + !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)) + { + device->getLogger()->log("WARNING: Vertex shaders disabled "\ + "because of missing driver/hardware support."); + vsFileName = ""; + } + + /* + An animated mesh is loaded to be displayed. As in most examples, + we'll take the fairy md2 model. + */ + + // load and display animated fairy mesh + + scene::IAnimatedMeshSceneNode* fairy = smgr->addAnimatedMeshSceneNode( + smgr->getMesh(mediaPath + "faerie.md2")); + + if (fairy) + { + fairy->setMaterialTexture(0, + driver->getTexture(mediaPath + "faerie2.bmp")); // set diffuse texture + fairy->setMaterialFlag(video::EMF_LIGHTING, false); // disable dynamic lighting + fairy->setPosition(core::vector3df(-10,0,-100)); + fairy->setMD2Animation ( scene::EMAT_STAND ); + } + + // add scene camera + smgr->addCameraSceneNode(0, core::vector3df(10,10,-80), + core::vector3df(-10,10,-100)); + + /* + We create a render target texture (RTT) with the same size as frame buffer. + Instead of rendering the scene directly to the frame buffer, we firstly + render it to this RTT. Post processing is then applied based on this RTT. + RTT size needs not to be the same with frame buffer though. However in this + example, we expect the result of rendering to RTT to be consistent with the + result of rendering directly to the frame buffer. Therefore, the size of + RTT keeps the same with frame buffer. + */ + + // create render target + + if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET)) + { + rt = driver->addRenderTargetTexture(core::dimension2d(640, 480), "RTT1"); + } + else + { + device->getLogger()->log("Your hardware or this renderer is not able to use the "\ + "render to texture feature. RTT Disabled."); + } + + /* + Post processing is achieved by rendering a screen quad with this RTT (with + previously rendered result) as a texture on the quad. A screen quad is + geometry of flat plane composed of two adjacent triangles covering the + entire area of viewport. In this pass of rendering, RTT works just like + a normal texture and is drawn on the quad during rendering. We can then + take control of this rendering process by applying various shader-defined + materials to the quad. In other words, we can achieve different effect by + writing different shaders. + This process is called post processing because it normally does not rely + on scene geometry. The inputs of this process are just textures, or in + other words, just images. With the help of screen quad, we can draw these + images on the screen with different effects. For example, we can adjust + contrast, make grayscale, add noise, do more fancy effect such as blur, + bloom, ghost, or just like in this example, we invert the color to produce + negative image. + Note that post processing is not limited to use only one texture. It can + take multiple textures as shader inputs to provide desired result. In + addition, post processing can also be chained to produce compound result. + */ + + // we create a screen quad + ScreenQuad *screenQuad = new ScreenQuad(driver); + + // turn off mip maps and bilinear filter since we do not want interpolated result + screenQuad->setMaterialFlag(video::EMF_USE_MIP_MAPS, false); + screenQuad->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); + + // set quad texture to RTT we just create + screenQuad->setMaterialTexture(0, rt); + + /* + Let's create material for the quad. Like in other example, we create material + using IGPUProgrammingServices and call addShaderMaterialFromFiles, which + returns a material type identifier. + */ + + // create materials + + video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices(); + s32 ppMaterialType = 0; + + if (gpu) + { + // We write a QuadShaderCallBack class that implements OnSetConstants + // callback of IShaderConstantSetCallBack class at the beginning of + // this tutorial. We set shader constants in this callback. + + // create an instance of callback class + + QuadShaderCallBack* mc = new QuadShaderCallBack(); + + // create material from post processing shaders + + ppMaterialType = gpu->addHighLevelShaderMaterialFromFiles( + vsFileName, "vertexMain", video::EVST_VS_1_1, + psFileName, "pixelMain", video::EPST_PS_1_1, mc); + + mc->drop(); + } + + // set post processing material type to the quad + screenQuad->setMaterialType((video::E_MATERIAL_TYPE)ppMaterialType); + + /* + Now draw everything. That's all. + */ + + int lastFPS = -1; + + while(device->run()) + { + if (device->isWindowActive()) + { + driver->beginScene(true, true, video::SColor(255,0,0,0)); + + if (rt) + { + // draw scene into render target + + // set render target to RTT + driver->setRenderTarget(rt, true, true, video::SColor(255,0,0,0)); + + // draw scene to RTT just like normal rendering + smgr->drawAll(); + + // after rendering to RTT, we change render target back + driver->setRenderTarget(0, true, true, video::SColor(255,0,0,0)); + + // render screen quad to apply post processing + screenQuad->render(); + } + else + { + // draw scene normally + smgr->drawAll(); + } + + driver->endScene(); + + int fps = driver->getFPS(); + + if (lastFPS != fps) + { + core::stringw str = L"Irrlicht Engine - Post processing example ["; + str += driver->getName(); + str += "] FPS:"; + str += fps; + + device->setWindowCaption(str.c_str()); + lastFPS = fps; + } + } + } + + // do not forget to manually drop the screen quad + + screenQuad->drop(); + + device->drop(); + + return 0; +} + +/* +**/ diff --git a/examples/BuildAllExamples_vc10.sln b/examples/BuildAllExamples_vc10.sln index cc6667e3..52f85843 100644 --- a/examples/BuildAllExamples_vc10.sln +++ b/examples/BuildAllExamples_vc10.sln @@ -146,6 +146,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "25.XmlHandling", "25.XmlHan EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "30.Profiling", "30.Profiling\Profiling_vc10.vcxproj", "{65D9DE2E-B73E-4ADF-96D1-BF4A8B7F4F97}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "27.PostProcessing", "27.PostProcessing\PostProcessing_vc10.vcxproj", "{2B885150-210F-4CA7-957E-2C3D75974308}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -410,6 +412,14 @@ Global {65D9DE2E-B73E-4ADF-96D1-BF4A8B7F4F97}.Release|Win32.Build.0 = Release|Win32 {65D9DE2E-B73E-4ADF-96D1-BF4A8B7F4F97}.Release|x64.ActiveCfg = Release|x64 {65D9DE2E-B73E-4ADF-96D1-BF4A8B7F4F97}.Release|x64.Build.0 = Release|x64 + {2B885150-210F-4CA7-957E-2C3D75974308}.Debug|Win32.ActiveCfg = Debug|Win32 + {2B885150-210F-4CA7-957E-2C3D75974308}.Debug|Win32.Build.0 = Debug|Win32 + {2B885150-210F-4CA7-957E-2C3D75974308}.Debug|x64.ActiveCfg = Debug|x64 + {2B885150-210F-4CA7-957E-2C3D75974308}.Debug|x64.Build.0 = Debug|x64 + {2B885150-210F-4CA7-957E-2C3D75974308}.Release|Win32.ActiveCfg = Release|Win32 + {2B885150-210F-4CA7-957E-2C3D75974308}.Release|Win32.Build.0 = Release|Win32 + {2B885150-210F-4CA7-957E-2C3D75974308}.Release|x64.ActiveCfg = Release|x64 + {2B885150-210F-4CA7-957E-2C3D75974308}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/BuildAllExamples_vc11.sln b/examples/BuildAllExamples_vc11.sln index 1722204c..25bfd7f5 100644 --- a/examples/BuildAllExamples_vc11.sln +++ b/examples/BuildAllExamples_vc11.sln @@ -149,6 +149,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "30.Profiling", "30.Profilin {E08E042A-6C45-411B-92BE-3CC31331019F} = {E08E042A-6C45-411B-92BE-3CC31331019F} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "27.PostProcessing", "27.PostProcessing\PostProcessing_vc11.vcxproj", "{F864F96D-F6AE-43E2-9A12-218B1A081255}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -413,6 +415,14 @@ Global {65D9DE2E-B73E-4ADF-96D1-BF4A8B7F4F97}.Release|Win32.Build.0 = Release|Win32 {65D9DE2E-B73E-4ADF-96D1-BF4A8B7F4F97}.Release|x64.ActiveCfg = Release|x64 {65D9DE2E-B73E-4ADF-96D1-BF4A8B7F4F97}.Release|x64.Build.0 = Release|x64 + {F864F96D-F6AE-43E2-9A12-218B1A081255}.Debug|Win32.ActiveCfg = Debug|Win32 + {F864F96D-F6AE-43E2-9A12-218B1A081255}.Debug|Win32.Build.0 = Debug|Win32 + {F864F96D-F6AE-43E2-9A12-218B1A081255}.Debug|x64.ActiveCfg = Debug|x64 + {F864F96D-F6AE-43E2-9A12-218B1A081255}.Debug|x64.Build.0 = Debug|x64 + {F864F96D-F6AE-43E2-9A12-218B1A081255}.Release|Win32.ActiveCfg = Release|Win32 + {F864F96D-F6AE-43E2-9A12-218B1A081255}.Release|Win32.Build.0 = Release|Win32 + {F864F96D-F6AE-43E2-9A12-218B1A081255}.Release|x64.ActiveCfg = Release|x64 + {F864F96D-F6AE-43E2-9A12-218B1A081255}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/BuildAllExamples_vc12.sln b/examples/BuildAllExamples_vc12.sln index 4c87d9fc..b5a77416 100644 --- a/examples/BuildAllExamples_vc12.sln +++ b/examples/BuildAllExamples_vc12.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Express 2013 for Windows Desktop -VisualStudioVersion = 12.0.30501.0 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "01.HelloWorld", "01.HelloWorld\HelloWorld_vc12.vcxproj", "{5AD4C95C-BA38-4692-BA4B-8C25A86208F9}" ProjectSection(ProjectDependencies) = postProject @@ -151,6 +151,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo", "Demo\Demo_vc12.vcxp EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Irrlicht", "..\source\Irrlicht\Irrlicht12.0.vcxproj", "{E08E042A-6C45-411B-92BE-3CC31331019F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "27.PostProcessing", "27.PostProcessing\PostProcessing_vc12.vcxproj", "{17E74625-568E-4008-897E-CAD12A332B0C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -1065,6 +1067,34 @@ Global {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|Win32.Build.0 = Static lib - Release|Win32 {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|x64.ActiveCfg = Static lib - Release|x64 {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|x64.Build.0 = Static lib - Release|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Debug|Win32.ActiveCfg = Debug|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Debug|Win32.Build.0 = Debug|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Debug|x64.ActiveCfg = Debug|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Debug|x64.Build.0 = Debug|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Release - Fast FPU|Win32.ActiveCfg = Release|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Release - Fast FPU|Win32.Build.0 = Release|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Release - Fast FPU|x64.ActiveCfg = Release|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Release - Fast FPU|x64.Build.0 = Release|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Release|Win32.ActiveCfg = Release|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Release|Win32.Build.0 = Release|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Release|x64.ActiveCfg = Release|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Release|x64.Build.0 = Release|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.SDL-Debug|Win32.ActiveCfg = Debug|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.SDL-Debug|Win32.Build.0 = Debug|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.SDL-Debug|x64.ActiveCfg = Debug|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.SDL-Debug|x64.Build.0 = Debug|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Debug|Win32.ActiveCfg = Debug|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Debug|Win32.Build.0 = Debug|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Debug|x64.ActiveCfg = Debug|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Debug|x64.Build.0 = Debug|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Release - Fast FPU|Win32.ActiveCfg = Release|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Release - Fast FPU|Win32.Build.0 = Release|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Release - Fast FPU|x64.ActiveCfg = Release|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Release - Fast FPU|x64.Build.0 = Release|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Release|Win32.ActiveCfg = Release|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Release|Win32.Build.0 = Release|Win32 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Release|x64.ActiveCfg = Release|x64 + {17E74625-568E-4008-897E-CAD12A332B0C}.Static lib - Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/BuildAllExamples_vc14.sln b/examples/BuildAllExamples_vc14.sln index baa04847..9529fd64 100644 --- a/examples/BuildAllExamples_vc14.sln +++ b/examples/BuildAllExamples_vc14.sln @@ -1,7 +1,7 @@  -Microsoft Visual Studio Solution File, Format Version 14.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "01.HelloWorld", "01.HelloWorld\HelloWorld_vc14.vcxproj", "{5AD4C95C-BA38-4692-BA4B-8C25A86208F9}" ProjectSection(ProjectDependencies) = postProject @@ -146,6 +146,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo", "Demo\Demo_vc14.vcxp EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Irrlicht", "..\source\Irrlicht\Irrlicht14.0.vcxproj", "{E08E042A-6C45-411B-92BE-3CC31331019F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "27.PostProcessing", "27.PostProcessing\PostProcessing_vc14.vcxproj", "{F25F2AC4-AEDA-4A95-9769-01A2652B54A2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -1032,6 +1034,34 @@ Global {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|Win32.Build.0 = Static lib - Release|Win32 {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|x64.ActiveCfg = Static lib - Release|x64 {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|x64.Build.0 = Static lib - Release|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Debug|Win32.ActiveCfg = Debug|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Debug|Win32.Build.0 = Debug|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Debug|x64.ActiveCfg = Debug|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Debug|x64.Build.0 = Debug|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Release - Fast FPU|Win32.ActiveCfg = Release|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Release - Fast FPU|Win32.Build.0 = Release|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Release - Fast FPU|x64.ActiveCfg = Release|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Release - Fast FPU|x64.Build.0 = Release|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Release|Win32.ActiveCfg = Release|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Release|Win32.Build.0 = Release|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Release|x64.ActiveCfg = Release|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Release|x64.Build.0 = Release|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.SDL-Debug|Win32.ActiveCfg = Debug|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.SDL-Debug|Win32.Build.0 = Debug|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.SDL-Debug|x64.ActiveCfg = Debug|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.SDL-Debug|x64.Build.0 = Debug|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Debug|Win32.ActiveCfg = Debug|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Debug|Win32.Build.0 = Debug|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Debug|x64.ActiveCfg = Debug|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Debug|x64.Build.0 = Debug|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Release - Fast FPU|Win32.ActiveCfg = Release|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Release - Fast FPU|Win32.Build.0 = Release|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Release - Fast FPU|x64.ActiveCfg = Release|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Release - Fast FPU|x64.Build.0 = Release|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Release|Win32.ActiveCfg = Release|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Release|Win32.Build.0 = Release|Win32 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Release|x64.ActiveCfg = Release|x64 + {F25F2AC4-AEDA-4A95-9769-01A2652B54A2}.Static lib - Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/Demo/CDemo.cpp b/examples/Demo/CDemo.cpp index 553f85e1..49dd7b6e 100644 --- a/examples/Demo/CDemo.cpp +++ b/examples/Demo/CDemo.cpp @@ -427,7 +427,9 @@ void CDemo::loadSceneData() model1->setMaterialFlag(video::EMF_LIGHTING, false); model1->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); model1->setMaterialType(video::EMT_SPHERE_MAP); - model1->addShadowVolumeSceneNode(); + model1->setAutomaticCulling(scene::EAC_OFF); // avoid shadows not updating + scene::IShadowVolumeSceneNode * shadVol = model1->addShadowVolumeSceneNode(); + shadVol->setOptimization(scene::ESV_NONE); // Sydney has broken shadows otherwise } model2 = sm->addAnimatedMeshSceneNode(mesh); @@ -439,7 +441,9 @@ void CDemo::loadSceneData() model2->setMaterialTexture(0, device->getVideoDriver()->getTexture(mediaPath + "sydney.bmp")); model2->setMaterialFlag(video::EMF_LIGHTING, true); model2->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); - model2->addShadowVolumeSceneNode(); + model2->setAutomaticCulling(scene::EAC_OFF); // avoid shadows not updating + scene::IShadowVolumeSceneNode * shadVol = model2->addShadowVolumeSceneNode(); + shadVol->setOptimization(scene::ESV_NONE); // Sydney has broken shadows otherwise } } diff --git a/include/EDriverFeatures.h b/include/EDriverFeatures.h index 8f3438ca..6d7fee40 100644 --- a/include/EDriverFeatures.h +++ b/include/EDriverFeatures.h @@ -142,6 +142,9 @@ namespace video //! Support for filtering across different faces of the cubemap EVDF_TEXTURE_CUBEMAP_SEAMLESS, + //! Support for clamping vertices beyond far-plane to depth instead of capping them. + EVDF_DEPTH_CLAMP, + //! Only used for counting the elements of this enum EVDF_COUNT }; diff --git a/include/IAnimatedMeshSceneNode.h b/include/IAnimatedMeshSceneNode.h index 1652705c..5a399039 100644 --- a/include/IAnimatedMeshSceneNode.h +++ b/include/IAnimatedMeshSceneNode.h @@ -47,9 +47,7 @@ namespace scene virtual void OnAnimationEnd(IAnimatedMeshSceneNode* node) = 0; }; - //! Scene node capable of displaying an animated mesh and its shadow. - /** The shadow is optional: If a shadow should be displayed too, just - invoke the IAnimatedMeshSceneNode::createShadowVolumeSceneNode().*/ + //! Scene node capable of displaying an animated mesh. class IAnimatedMeshSceneNode : public ISceneNode { public: @@ -90,28 +88,31 @@ namespace scene /** \return Frames per second played. */ virtual f32 getAnimationSpeed() const =0; - //! Creates shadow volume scene node as child of this node. /** The shadow can be rendered using the ZPass or the zfail method. ZPass is a little bit faster because the shadow volume creation is easier, but with this method there occur ugly looking artifacts when the camera is inside the shadow volume. - These error do not occur with the ZFail method. + These error do not occur with the ZFail method, but it can + have trouble with clipping to the far-plane (it usually works + well in OpenGL and fails with other drivers). \param shadowMesh: Optional custom mesh for shadow volume. \param id: Id of the shadow scene node. This id can be used to identify the node later. \param zfailmethod: If set to true, the shadow will use the zfail method, if not, zpass is used. \param infinity: Value used by the shadow volume algorithm to - scale the shadow volume (for zfail shadow volume we support only - finite shadows, so camera zfar must be larger than shadow back cap, - which is depend on infinity parameter). + scale the shadow volume. For zfail shadow volumes on some drivers + only suppport finite shadows, so camera zfar must be larger than + shadow back cap,which is depending on the infinity parameter). + Infinity value also scales by the scaling factors of the model. + If shadows don't show up with zfail then try reducing infinity. + If shadows are cut-off then try increasing infinity. \return Pointer to the created shadow scene node. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual IShadowVolumeSceneNode* addShadowVolumeSceneNode(const IMesh* shadowMesh=0, s32 id=-1, bool zfailmethod=true, f32 infinity=1000.0f) = 0; - //! Get a pointer to a joint in the mesh (if the mesh is a bone based mesh). /** With this method it is possible to attach scene nodes to joints for example possible to attach a weapon to the left hand diff --git a/include/ICameraSceneNode.h b/include/ICameraSceneNode.h index e289628a..2743dae8 100644 --- a/include/ICameraSceneNode.h +++ b/include/ICameraSceneNode.h @@ -38,6 +38,8 @@ namespace scene Note that the matrix will only stay as set by this method until one of the following Methods are called: setNearValue, setFarValue, setAspectRatio, setFOV. + NOTE: The frustum is not updated before render() is called + unless you explicitly call updateMatrices() \param projection The new projection matrix of the camera. \param isOrthogonal Set this to true if the matrix is an orthogonal one (e.g. from matrix4::buildProjectionMatrixOrtho). diff --git a/include/IMeshSceneNode.h b/include/IMeshSceneNode.h index 2fc70396..03c5c522 100644 --- a/include/IMeshSceneNode.h +++ b/include/IMeshSceneNode.h @@ -38,21 +38,25 @@ public: /** \return Pointer to mesh which is displayed by this node. */ virtual IMesh* getMesh(void) = 0; - //! Creates shadow volume scene node as child of this node. /** The shadow can be rendered using the ZPass or the zfail method. ZPass is a little bit faster because the shadow volume creation is easier, but with this method there occur ugly looking artifacts when the camera is inside the shadow volume. - These error do not occur with the ZFail method. + These error do not occur with the ZFail method, but it can + have trouble with clipping to the far-plane (it usually works + well in OpenGL and fails with other drivers). \param shadowMesh: Optional custom mesh for shadow volume. \param id: Id of the shadow scene node. This id can be used to identify the node later. \param zfailmethod: If set to true, the shadow will use the zfail method, if not, zpass is used. \param infinity: Value used by the shadow volume algorithm to - scale the shadow volume (for zfail shadow volume we support only - finite shadows, so camera zfar must be larger than shadow back cap, - which is depend on infinity parameter). + scale the shadow volume. For zfail shadow volumes on some drivers + only suppport finite shadows, so camera zfar must be larger than + shadow back cap,which is depending on the infinity parameter). + Infinity value also scales by the scaling factors of the model. + If shadows don't show up with zfail then try reducing infinity. + If shadows are cut-off then try increasing infinity. \return Pointer to the created shadow scene node. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ diff --git a/include/IShadowVolumeSceneNode.h b/include/IShadowVolumeSceneNode.h index 82c2ad32..f19e6aab 100644 --- a/include/IShadowVolumeSceneNode.h +++ b/include/IShadowVolumeSceneNode.h @@ -13,6 +13,22 @@ namespace scene { class IMesh; + enum ESHADOWVOLUME_OPTIMIZATION + { + //! Create volumes around every triangle + ESV_NONE, + + //! Create volumes only around the silhouette of the mesh + /** This can reduce the number of volumes drastically, + but will have an upfront-cost where it calculates adjacency of + triangles. Also it will not work with all models. Basically + if you see strange black shadow lines then you have a model + for which it won't work. + We get that information about adjacency by comparing the positions of + all edges in the mesh (even if they are in different meshbuffers). */ + ESV_SILHOUETTE_BY_POS + }; + //! Scene node for rendering a shadow volume into a stencil buffer. class IShadowVolumeSceneNode : public ISceneNode { @@ -29,6 +45,17 @@ namespace scene //! Updates the shadow volumes for current light positions. virtual void updateShadowVolumes() = 0; + + //! Set optimization used to create shadow volumes + /** Default is ESV_SILHOUETTE_BY_POS. If the shadow + looks bad then give ESV_NONE a try (which will be slower). + Alternatively you can try to fix the model, it's often + because it's not closed (aka if you'd put water in it then + that would leak out). */ + virtual void setOptimization(ESHADOWVOLUME_OPTIMIZATION optimization) = 0; + + //! Get currently active optimization used to create shadow volumes + virtual ESHADOWVOLUME_OPTIMIZATION getOptimization() const = 0; }; } // end namespace scene diff --git a/media/027shot.jpg b/media/027shot.jpg new file mode 100644 index 0000000000000000000000000000000000000000..274bada7652a56c213db38217a6411b9e47985d5 GIT binary patch literal 18390 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

H;Px@`1IyE|DCoIh4K>oOaShi z&E+)@OP&5q7RP?{6Cb_vl{4#XgZUTBN8RNqYW8Ce;1e5zb z-++X(?fyZ`UoM354lGMdhbYdI6Z_X5B`enLIoR0l>ly!g@bvy8jWK=jxheV*r%vPl zknI1;_W!vs95RyaxJe)3Kx!MA4$o~AXaEdhq8KhjZCm>b*Of*(XaLcF4k~)L;WM*E zZ!}SP3ETf1KQV_DRJPR&t;hpHRFnl(HyC0v3Uc~qcIy3qjM4v^GMLnO+`4Ban0Gp^ z-S?VttL7?7d|_0xMzZb#DZJje(4Dp|9^L63&N_KfN-E(?QxEzB*u9?<;Sh4Ac09)@ z4Er>Wc-Tau52{um=`{N5Oc1UqS#o)2m;O@H&rT#&wV(@g$ z!FQ*-|mP1^kbt61~Dh$w+N8=f<;4Is0P$JgwzG@Vqb zAOl$;8sIwHVfudhuEqFFp2f?cY=gHatEetqpt~9j^dKm3tTfQQBtKVh@xXNJSE~R; zBuLT#uQcO~r(h*-3iYF(AA>Iwwo6RrATe)Fa8876>kFiP1IqwQ0<5QHs9mJzieAnW ztln*VhxdFAturX6wE(mp z(-S&B+6_nJjx@6Fkwq)GH!*BYkyw`YpT%oG{F-DZ!iuRcny-d-lGA= zqAdFo&`&Im3Zya(5e=*tfdq!bnEjgs+}H?ZoOEPdJK^KD_VO72<2&lR&XWCt{j{CZ zFVixDo`J&e^i`7BTX-%4fIcR?&ne20*1{*Hv< zP+KZHPd+sLe&2gy6TnUF99$YmNdNrfn_|#7o!S#95y$d~CZbgP{2+_Bw)ezs{$5B@ zWm|F639n+vnGb*0?F@>l9w?*O;;~Zrgw*^)OBx`*wvpHK&{VA}8}v!Ai_WJ8jilty z=>zY+-5SvYIHoRU>1>}npIDaytP?(rm86{3wZ4I)ugDCTi5!rtBj{C?M2c*OGskZq z{5nQO9PUefZVw%i<1=@^sWuNETHBFM4gLsRHw%?}O$jYpwmj~MHO`OCYzwsb7XCad zDeeh15dGzUY(4+v)xK|hEBFuJ_4oTA-~Z^^@yWMCf|?i{JQfsa4vei-C^MB-~1Wv6CW%3$DVj)&oiN(>zC3# z`3w6qcFS@{Z{F)0et>n&bI)!4;#%*y4op2i@lHye=}YH-T#706%@6PY69XD^V(YZn z$&WQ1k1f$%U-6$|=JStil?_L(^?C{Kn^xsn`q_Kum#w0o{r}iFo&G(a54hoPd;CAw z#sB0KQ~#cCum2GZirDM*(0rEOjL4w`bzS^ByAzbHXVhl^(=9mJ>$=ulsDH4?tp4-) zhtDC-+cN*dM{WC`&p&(y=FL)I-i*S?o69uT&jL>RN~ZnYC)2;a4_LKK%<}(Hp*!>a zhuc6+Q$d>i^^WLo24-ma>o3Ydsn;Vg-W-^<4(j}^lLBrzECCiDzt25Y=KuC@t^D9@;(igd ziu~|_oc|2_Kb(iD;{PMUc3!^z;rxH%*&t(To%ZJgL!!2>y&hD&edtFostfJ6x4-3l zxL5m8TjSy%wWa;??hoJd-FjKR{h!3v{(vppclLR1cle|;+vT8_v4VQ~3`XllhP4}u R`?pP& CAnimatedMeshSceneNode::getBoundingBox() const } -//! returns the material based on the zero based index i. To get the amount -//! of materials used by this scene node, use getMaterialCount(). -//! This function is needed for inserting the node into the scene hirachy on a -//! optimal position for minimizing renderstate changes, but can also be used -//! to directly modify the material of a scene node. +//! returns the material based on the zero based index i. video::SMaterial& CAnimatedMeshSceneNode::getMaterial(u32 i) { if (i >= Materials.size()) diff --git a/source/Irrlicht/CAnimatedMeshSceneNode.h b/source/Irrlicht/CAnimatedMeshSceneNode.h index fe9011e9..6a5914e9 100644 --- a/source/Irrlicht/CAnimatedMeshSceneNode.h +++ b/source/Irrlicht/CAnimatedMeshSceneNode.h @@ -70,7 +70,7 @@ namespace scene //! returns the material based on the zero based index i. To get the amount //! of materials used by this scene node, use getMaterialCount(). - //! This function is needed for inserting the node into the scene hirachy on a + //! This function is needed for inserting the node into the scene hierarchy on a //! optimal position for minimizing renderstate changes, but can also be used //! to directly modify the material of a scene node. virtual video::SMaterial& getMaterial(u32 i) _IRR_OVERRIDE_; @@ -84,7 +84,7 @@ namespace scene s32 id, bool zfailmethod=true, f32 infinity=1000.0f) _IRR_OVERRIDE_; //! Returns a pointer to a child node, which has the same transformation as - //! the corrsesponding joint, if the mesh in this scene node is a skinned mesh. + //! the corresponding joint, if the mesh in this scene node is a skinned mesh. virtual IBoneSceneNode* getJointNode(const c8* jointName) _IRR_OVERRIDE_; //! same as getJointNode(const c8* jointName), but based on id @@ -95,7 +95,7 @@ namespace scene //! Removes a child from this scene node. //! Implemented here, to be able to remove the shadow properly, if there is one, - //! or to remove attached childs. + //! or to remove attached child. virtual bool removeChild(ISceneNode* child) _IRR_OVERRIDE_; //! Starts a MD2 animation. diff --git a/source/Irrlicht/CCubeSceneNode.h b/source/Irrlicht/CCubeSceneNode.h index 8f8aba82..f8ad068f 100644 --- a/source/Irrlicht/CCubeSceneNode.h +++ b/source/Irrlicht/CCubeSceneNode.h @@ -34,7 +34,7 @@ namespace scene //! returns the material based on the zero based index i. To get the amount //! of materials used by this scene node, use getMaterialCount(). - //! This function is needed for inserting the node into the scene hirachy on a + //! This function is needed for inserting the node into the scene hierarchy on a //! optimal position for minimizing renderstate changes, but can also be used //! to directly modify the material of a scene node. virtual video::SMaterial& getMaterial(u32 i) _IRR_OVERRIDE_; @@ -75,7 +75,7 @@ namespace scene //! Removes a child from this scene node. //! Implemented here, to be able to remove the shadow properly, if there is one, - //! or to remove attached childs. + //! or to remove attached child. virtual bool removeChild(ISceneNode* child) _IRR_OVERRIDE_; private: diff --git a/source/Irrlicht/CD3D9Driver.cpp b/source/Irrlicht/CD3D9Driver.cpp index 8f9b24f1..98501b39 100644 --- a/source/Irrlicht/CD3D9Driver.cpp +++ b/source/Irrlicht/CD3D9Driver.cpp @@ -2408,9 +2408,8 @@ void CD3D9Driver::setRenderStatesStencilShadowMode(bool zfail, u32 debugDataVisi pID3DDevice->SetRenderState(D3DRS_STENCILMASK, 0xffffffff); pID3DDevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff); - pID3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); - pID3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO ); - pID3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE ); + BridgeCalls->setBlend(true); + BridgeCalls->setBlendFunc(D3DBLEND_ZERO, D3DBLEND_ONE); pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS); diff --git a/source/Irrlicht/CD3D9Driver.h b/source/Irrlicht/CD3D9Driver.h index f607c553..d066f8e5 100644 --- a/source/Irrlicht/CD3D9Driver.h +++ b/source/Irrlicht/CD3D9Driver.h @@ -321,7 +321,7 @@ namespace video private: - //! enumeration for rendering modes such as 2d and 3d for minizing the switching of renderStates. + //! enumeration for rendering modes such as 2d and 3d for minimizing the switching of renderStates. enum E_RENDER_MODE { ERM_NONE = 0, // no render state has been set yet. @@ -415,7 +415,7 @@ namespace video bool Transformation3DChanged; const ITexture* CurrentTexture[MATERIAL_MAX_TEXTURES]; bool LastTextureMipMapsAvailable[MATERIAL_MAX_TEXTURES]; - core::matrix4 Matrices[ETS_COUNT]; // matrizes of the 3d mode we need to restore when we switch back from the 2d mode. + core::matrix4 Matrices[ETS_COUNT]; // matrices of the 3d mode we need to restore when we switch back from the 2d mode. HINSTANCE D3DLibrary; IDirect3D9* pID3D; @@ -459,7 +459,7 @@ namespace video bool AlphaToCoverageSupport; }; - //! This bridge between Irlicht pseudo D3D9 calls + //! This bridge between Irrlicht pseudo D3D9 calls //! and true D3D9 calls. class CD3D9CallBridge diff --git a/source/Irrlicht/CMeshSceneNode.h b/source/Irrlicht/CMeshSceneNode.h index 329bacca..b7b10be3 100644 --- a/source/Irrlicht/CMeshSceneNode.h +++ b/source/Irrlicht/CMeshSceneNode.h @@ -37,7 +37,7 @@ namespace scene //! returns the material based on the zero based index i. To get the amount //! of materials used by this scene node, use getMaterialCount(). - //! This function is needed for inserting the node into the scene hirachy on a + //! This function is needed for inserting the node into the scene hierarchy on a //! optimal position for minimizing renderstate changes, but can also be used //! to directly modify the material of a scene node. virtual video::SMaterial& getMaterial(u32 i) _IRR_OVERRIDE_; @@ -78,7 +78,7 @@ namespace scene //! Removes a child from this scene node. //! Implemented here, to be able to remove the shadow properly, if there is one, - //! or to remove attached childs. + //! or to remove attached child. virtual bool removeChild(ISceneNode* child) _IRR_OVERRIDE_; protected: diff --git a/source/Irrlicht/COctreeSceneNode.cpp b/source/Irrlicht/COctreeSceneNode.cpp index 79a1a1e2..63e9e43d 100644 --- a/source/Irrlicht/COctreeSceneNode.cpp +++ b/source/Irrlicht/COctreeSceneNode.cpp @@ -579,11 +579,7 @@ bool COctreeSceneNode::createTree(IMesh* mesh) } -//! returns the material based on the zero based index i. To get the amount -//! of materials used by this scene node, use getMaterialCount(). -//! This function is needed for inserting the node into the scene hirachy on a -//! optimal position for minimizing renderstate changes, but can also be used -//! to directly modify the material of a scene node. +//! returns the material based on the zero based index i. video::SMaterial& COctreeSceneNode::getMaterial(u32 i) { if ( i >= Materials.size() ) diff --git a/source/Irrlicht/COctreeSceneNode.h b/source/Irrlicht/COctreeSceneNode.h index 87509869..89de7eda 100644 --- a/source/Irrlicht/COctreeSceneNode.h +++ b/source/Irrlicht/COctreeSceneNode.h @@ -75,7 +75,7 @@ namespace scene //! Removes a child from this scene node. //! Implemented here, to be able to remove the shadow properly, if there is one, - //! or to remove attached children. + //! or to remove attached child. virtual bool removeChild(ISceneNode* child) _IRR_OVERRIDE_; //! Set if/how vertex buffer object are used for the meshbuffers diff --git a/source/Irrlicht/COpenGLDriver.cpp b/source/Irrlicht/COpenGLDriver.cpp index 782c8f19..154261e7 100644 --- a/source/Irrlicht/COpenGLDriver.cpp +++ b/source/Irrlicht/COpenGLDriver.cpp @@ -3289,6 +3289,11 @@ void COpenGLDriver::drawStencilShadowVolume(const core::array& #ifdef GL_NV_depth_clamp if (FeatureAvailable[IRR_NV_depth_clamp]) glEnable(GL_DEPTH_CLAMP_NV); +#elif defined(GL_ARB_depth_clamp) + if (FeatureAvailable[IRR_ARB_depth_clamp]) + { + glEnable(GL_DEPTH_CLAMP); + } #endif // The first parts are not correctly working, yet. @@ -3370,6 +3375,11 @@ void COpenGLDriver::drawStencilShadowVolume(const core::array& #ifdef GL_NV_depth_clamp if (FeatureAvailable[IRR_NV_depth_clamp]) glDisable(GL_DEPTH_CLAMP_NV); +#elif defined(GL_ARB_depth_clamp) + if (FeatureAvailable[IRR_ARB_depth_clamp]) + { + glDisable(GL_DEPTH_CLAMP); + } #endif glDisable(GL_POLYGON_OFFSET_FILL); diff --git a/source/Irrlicht/COpenGLExtensionHandler.cpp b/source/Irrlicht/COpenGLExtensionHandler.cpp index 3416da24..16162640 100644 --- a/source/Irrlicht/COpenGLExtensionHandler.cpp +++ b/source/Irrlicht/COpenGLExtensionHandler.cpp @@ -863,6 +863,9 @@ bool COpenGLExtensionHandler::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const return (Version >= 130) || FeatureAvailable[IRR_ARB_texture_cube_map] || FeatureAvailable[IRR_EXT_texture_cube_map]; case EVDF_TEXTURE_CUBEMAP_SEAMLESS: return FeatureAvailable[IRR_ARB_seamless_cube_map]; + case EVDF_DEPTH_CLAMP: + return FeatureAvailable[IRR_NV_depth_clamp] || FeatureAvailable[IRR_ARB_depth_clamp]; + default: return false; }; diff --git a/source/Irrlicht/CShadowVolumeSceneNode.cpp b/source/Irrlicht/CShadowVolumeSceneNode.cpp index 2f2a2971..9c7b3ab1 100644 --- a/source/Irrlicht/CShadowVolumeSceneNode.cpp +++ b/source/Irrlicht/CShadowVolumeSceneNode.cpp @@ -25,8 +25,9 @@ namespace scene CShadowVolumeSceneNode::CShadowVolumeSceneNode(const IMesh* shadowMesh, ISceneNode* parent, ISceneManager* mgr, s32 id, bool zfailmethod, f32 infinity) : IShadowVolumeSceneNode(parent, mgr, id), + AdjacencyDirtyFlag(true), ShadowMesh(0), IndexCount(0), VertexCount(0), ShadowVolumesUsed(0), - Infinity(infinity), UseZFailMethod(zfailmethod) + Infinity(infinity), UseZFailMethod(zfailmethod), Optimization(ESV_SILHOUETTE_BY_POS) { #ifdef _DEBUG setDebugName("CShadowVolumeSceneNode"); @@ -75,15 +76,22 @@ void CShadowVolumeSceneNode::createShadowVolume(const core::vector3df& light, bo Edges.set_used(IndexCount*2); u32 numEdges = 0; - numEdges=createEdgesAndCaps(light, svp, bb); + numEdges=createEdgesAndCaps(light, isDirectional, svp, bb); // for all edges add the near->far quads + core::vector3df lightDir1(light*Infinity); + core::vector3df lightDir2(light*Infinity); for (u32 i=0; i* bb) { u32 numEdges=0; @@ -116,16 +128,37 @@ u32 CShadowVolumeSceneNode::createEdgesAndCaps(const core::vector3df& light, bb->reset(0,0,0); // Check every face if it is front or back facing the light. + core::vector3df lightDir0(light); + core::vector3df lightDir1(light); + core::vector3df lightDir2(light); for (u32 i=0; igetVideoDriver()->setMaterial(m); +#ifdef IRR_USE_REVERSE_EXTRUDED + SceneManager->getVideoDriver()->draw3DTriangle(core::triangle3df(v0+lightDir0,v1+lightDir0,v2+lightDir0), irr::video::SColor(255,255, 0, 0)); +#else + SceneManager->getVideoDriver()->draw3DTriangle(core::triangle3df(v0-lightDir0,v1-lightDir0,v2-lightDir0), irr::video::SColor(255,255, 0, 0)); +#endif + } #endif if (UseZFailMethod && FaceData[i]) @@ -140,9 +173,14 @@ u32 CShadowVolumeSceneNode::createEdgesAndCaps(const core::vector3df& light, svp->push_back(v0); // add back cap - const core::vector3df i0 = v0+(v0-light).normalize()*Infinity; - const core::vector3df i1 = v1+(v1-light).normalize()*Infinity; - const core::vector3df i2 = v2+(v2-light).normalize()*Infinity; + if ( !isDirectional ) + { + lightDir1 = (v1-light).normalize(); + lightDir2 = (v2-light).normalize(); + } + const core::vector3df i0 = v0+lightDir0*Infinity; + const core::vector3df i1 = v1+lightDir1*Infinity; + const core::vector3df i2 = v2+lightDir2*Infinity; svp->push_back(i0); svp->push_back(i1); @@ -164,41 +202,55 @@ u32 CShadowVolumeSceneNode::createEdgesAndCaps(const core::vector3df& light, const u16 wFace1 = Indices[3*i+1]; const u16 wFace2 = Indices[3*i+2]; - const u16 adj0 = Adjacency[3*i+0]; - const u16 adj1 = Adjacency[3*i+1]; - const u16 adj2 = Adjacency[3*i+2]; - - // add edges if face is adjacent to back-facing face - // or if no adjacent face was found -#ifdef IRR_USE_ADJACENCY - if (adj0 == i || FaceData[adj0] == false) -#endif + if ( Optimization == ESV_NONE ) { // add edge v0-v1 Edges[2*numEdges+0] = wFace0; Edges[2*numEdges+1] = wFace1; ++numEdges; - } -#ifdef IRR_USE_ADJACENCY - if (adj1 == i || FaceData[adj1] == false) -#endif - { // add edge v1-v2 Edges[2*numEdges+0] = wFace1; Edges[2*numEdges+1] = wFace2; ++numEdges; - } -#ifdef IRR_USE_ADJACENCY - if (adj2 == i || FaceData[adj2] == false) -#endif - { // add edge v2-v0 Edges[2*numEdges+0] = wFace2; Edges[2*numEdges+1] = wFace0; ++numEdges; } + else + { + const u16 adj0 = Adjacency[3*i+0]; + const u16 adj1 = Adjacency[3*i+1]; + const u16 adj2 = Adjacency[3*i+2]; + + // add edges if face is adjacent to back-facing face + // or if no adjacent face was found + if (adj0 == i || FaceData[adj0] == false) + { + // add edge v0-v1 + Edges[2*numEdges+0] = wFace0; + Edges[2*numEdges+1] = wFace1; + ++numEdges; + } + + if (adj1 == i || FaceData[adj1] == false) + { + // add edge v1-v2 + Edges[2*numEdges+0] = wFace1; + Edges[2*numEdges+1] = wFace2; + ++numEdges; + } + + if (adj2 == i || FaceData[adj2] == false) + { + // add edge v2-v0 + Edges[2*numEdges+0] = wFace2; + Edges[2*numEdges+1] = wFace0; + ++numEdges; + } + } } } return numEdges; @@ -225,6 +277,10 @@ void CShadowVolumeSceneNode::updateShadowVolumes() const u32 oldIndexCount = IndexCount; const u32 oldVertexCount = VertexCount; + VertexCount = 0; + IndexCount = 0; + ShadowVolumesUsed = 0; + const IMesh* const mesh = ShadowMesh; if (!mesh) return; @@ -237,10 +293,6 @@ void CShadowVolumeSceneNode::updateShadowVolumes() // calculate total amount of vertices and indices - VertexCount = 0; - IndexCount = 0; - ShadowVolumesUsed = 0; - u32 i; u32 totalVertices = 0; u32 totalIndices = 0; @@ -249,8 +301,24 @@ void CShadowVolumeSceneNode::updateShadowVolumes() for (i=0; igetMeshBuffer(i); - totalIndices += buf->getIndexCount(); - totalVertices += buf->getVertexCount(); + if ( buf->getIndexType() == video::EIT_16BIT + && buf->getPrimitiveType() == scene::EPT_TRIANGLES ) + { + totalIndices += buf->getIndexCount(); + totalVertices += buf->getVertexCount(); + } + else + { + os::Printer::log("ShadowVolumeSceneNode only supports meshbuffers with 16 bit indices and triangles", ELL_WARNING); + return; + } + } + if ( totalIndices != (u32)(u16)totalIndices) + { + // We could switch to 32-bit indices, not much work and just bit of extra memory (< 192k) per shadow volume. + // If anyone ever complains and really needs that just switch it. But huge shadows are usually a bad idea as they will be slow. + os::Printer::log("ShadowVolumeSceneNode does not yet support shadowvolumes which need more than 16 bit indices", ELL_WARNING); + return; } // allocate memory if necessary @@ -259,7 +327,8 @@ void CShadowVolumeSceneNode::updateShadowVolumes() Indices.set_used(totalIndices); FaceData.set_used(totalIndices / 3); - // copy mesh + // copy mesh + // (could speed this up for static meshes by adding some user flag to prevents copying) for (i=0; igetMeshBuffer(i); @@ -275,27 +344,45 @@ void CShadowVolumeSceneNode::updateShadowVolumes() } // recalculate adjacency if necessary - if (oldVertexCount != VertexCount || oldIndexCount != IndexCount) + if (oldVertexCount != VertexCount || oldIndexCount != IndexCount || AdjacencyDirtyFlag) calculateAdjacency(); - core::matrix4 mat = Parent->getAbsoluteTransformation(); - mat.makeInverse(); + core::matrix4 matInv(Parent->getAbsoluteTransformation()); + matInv.makeInverse(); + core::matrix4 matTransp(Parent->getAbsoluteTransformation(), core::matrix4::EM4CONST_TRANSPOSED); const core::vector3df parentpos = Parent->getAbsolutePosition(); - // TODO: Only correct for point lights. for (i=0; igetVideoDriver()->getDynamicLight(i); - core::vector3df lpos = dl.Position; - if (dl.CastShadows && - fabs((lpos - parentpos).getLengthSQ()) <= (dl.Radius*dl.Radius*4.0f)) + + if ( dl.Type == video::ELT_DIRECTIONAL ) { - mat.transformVect(lpos); - createShadowVolume(lpos); + core::vector3df ldir(dl.Direction); + matTransp.transformVect(ldir); + createShadowVolume(ldir, true); + } + else + { + core::vector3df lpos(dl.Position); + if (dl.CastShadows && + fabs((lpos - parentpos).getLengthSQ()) <= (dl.Radius*dl.Radius*4.0f)) + { + matInv.transformVect(lpos); + createShadowVolume(lpos, false); + } } } } +void CShadowVolumeSceneNode::setOptimization(ESHADOWVOLUME_OPTIMIZATION optimization) +{ + if ( Optimization != optimization ) + { + Optimization = optimization; + AdjacencyDirtyFlag = true; + } +} //! pre render method void CShadowVolumeSceneNode::OnRegisterSceneNode() @@ -317,45 +404,55 @@ void CShadowVolumeSceneNode::render() driver->setTransform(video::ETS_WORLD, Parent->getAbsoluteTransformation()); + bool checkFarPlaneClipping = UseZFailMethod && !driver->queryFeature(video::EVDF_DEPTH_CLAMP); + + // get camera frustum converted to local coordinates when we have to check for far plane clipping + SViewFrustum frust; + if ( checkFarPlaneClipping ) + { + const irr::scene::ICameraSceneNode* camera = SceneManager->getActiveCamera(); + if ( camera ) + { + frust = *camera->getViewFrustum(); + core::matrix4 invTrans(Parent->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE); + frust.transform(invTrans); + } + else + checkFarPlaneClipping = false; + } + for (u32 i=0; igetActiveCamera()) + if (checkFarPlaneClipping) { // Disable shadows drawing, when back cap is behind of ZFar plane. - - SViewFrustum frust = *SceneManager->getActiveCamera()->getViewFrustum(); - - core::matrix4 invTrans(Parent->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE); - frust.transform(invTrans); + // TODO: Using infinite projection matrices instead is said to work better + // as then we wouldn't fail when the shadow clip the far plane. + // I couldn't get it working (and neither anyone before me it seems). + // Anyone who can figure it out is welcome to provide a patch. core::vector3df edges[8]; ShadowBBox[i].getEdges(edges); - core::vector3df largestEdge = edges[0]; - f32 maxDistance = core::vector3df(SceneManager->getActiveCamera()->getPosition() - edges[0]).getLength(); - f32 curDistance = 0.f; - - for(int j = 1; j < 8; ++j) + for(int j = 0; j < 8; ++j) { - curDistance = core::vector3df(SceneManager->getActiveCamera()->getPosition() - edges[j]).getLength(); - - if(curDistance > maxDistance) + if (frust.planes[scene::SViewFrustum::VF_FAR_PLANE].classifyPointRelation(edges[j]) == core::ISREL3D_FRONT) { - maxDistance = curDistance; - largestEdge = edges[j]; + drawShadow = false; + break; } } - - if (!(frust.planes[scene::SViewFrustum::VF_FAR_PLANE].classifyPointRelation(largestEdge) != core::ISREL3D_FRONT)) - drawShadow = false; } if(drawShadow) driver->drawStencilShadowVolume(ShadowVolumes[i], UseZFailMethod, DebugDataVisible); else { + // TODO: For some reason (not yet further investigated), Direct3D needs a call to drawStencilShadowVolume + // even if we have nothing to draw here to set the renderstate into a StencilShadowMode. + // If that's not done it has effect on further render calls. core::array triangles; driver->drawStencilShadowVolume(triangles, UseZFailMethod, DebugDataVisible); } @@ -373,47 +470,56 @@ const core::aabbox3d& CShadowVolumeSceneNode::getBoundingBox() const //! Generates adjacency information based on mesh indices. void CShadowVolumeSceneNode::calculateAdjacency() { - Adjacency.set_used(IndexCount); + AdjacencyDirtyFlag = false; - // go through all faces and fetch their three neighbours - for (u32 f=0; f store face number, else store adjacent face - if (of >= IndexCount) - Adjacency[f + edge] = f/3; - else - Adjacency[f + edge] = of/3; + // no adjacent edges -> store face number, else store adjacent face + if (of >= IndexCount) + Adjacency[f + edge] = f/3; + else + Adjacency[f + edge] = of/3; + } } } } diff --git a/source/Irrlicht/CShadowVolumeSceneNode.h b/source/Irrlicht/CShadowVolumeSceneNode.h index 92b4ac21..6501652e 100644 --- a/source/Irrlicht/CShadowVolumeSceneNode.h +++ b/source/Irrlicht/CShadowVolumeSceneNode.h @@ -33,6 +33,17 @@ namespace scene /** Called each render cycle from Animated Mesh SceneNode render method. */ virtual void updateShadowVolumes() _IRR_OVERRIDE_; + //! Set optimization used to create shadow volumes + /** Default is ESV_SILHOUETTE_BY_POS. If the shadow + looks bad then give ESV_NONE a try (which will be slower). */ + virtual void setOptimization(ESHADOWVOLUME_OPTIMIZATION optimization) _IRR_OVERRIDE_; + + //! Get currently active optimization used to create shadow volumes + virtual ESHADOWVOLUME_OPTIMIZATION getOptimization() const _IRR_OVERRIDE_ + { + return Optimization; + } + //! pre render method virtual void OnRegisterSceneNode() _IRR_OVERRIDE_; @@ -49,8 +60,8 @@ namespace scene typedef core::array SShadowVolume; - void createShadowVolume(const core::vector3df& pos, bool isDirectional=false); - u32 createEdgesAndCaps(const core::vector3df& light, SShadowVolume* svp, core::aabbox3d* bb); + void createShadowVolume(const core::vector3df& pos, bool isDirectional); + u32 createEdgesAndCaps(const core::vector3df& light, bool isDirectional, SShadowVolume* svp, core::aabbox3d* bb); //! Generates adjacency information based on mesh indices. void calculateAdjacency(); @@ -69,6 +80,7 @@ namespace scene core::array Edges; // tells if face is front facing core::array FaceData; + bool AdjacencyDirtyFlag; const scene::IMesh* ShadowMesh; @@ -77,8 +89,8 @@ namespace scene u32 ShadowVolumesUsed; f32 Infinity; - bool UseZFailMethod; + ESHADOWVOLUME_OPTIMIZATION Optimization; }; } // end namespace scene diff --git a/source/Irrlicht/CSkyBoxSceneNode.cpp b/source/Irrlicht/CSkyBoxSceneNode.cpp index ca66fcdf..de41f03d 100644 --- a/source/Irrlicht/CSkyBoxSceneNode.cpp +++ b/source/Irrlicht/CSkyBoxSceneNode.cpp @@ -221,11 +221,7 @@ void CSkyBoxSceneNode::OnRegisterSceneNode() } -//! returns the material based on the zero based index i. To get the amount -//! of materials used by this scene node, use getMaterialCount(). -//! This function is needed for inserting the node into the scene hirachy on a -//! optimal position for minimizing renderstate changes, but can also be used -//! to directly modify the material of a scene node. +//! returns the material based on the zero based index i. video::SMaterial& CSkyBoxSceneNode::getMaterial(u32 i) { return Material[i]; diff --git a/source/Irrlicht/CSkyBoxSceneNode.h b/source/Irrlicht/CSkyBoxSceneNode.h index 3acaa390..a153b50a 100644 --- a/source/Irrlicht/CSkyBoxSceneNode.h +++ b/source/Irrlicht/CSkyBoxSceneNode.h @@ -33,7 +33,7 @@ namespace scene //! returns the material based on the zero based index i. To get the amount //! of materials used by this scene node, use getMaterialCount(). - //! This function is needed for inserting the node into the scene hirachy on a + //! This function is needed for inserting the node into the scene hierarchy on a //! optimal position for minimizing renderstate changes, but can also be used //! to directly modify the material of a scene node. virtual video::SMaterial& getMaterial(u32 i) _IRR_OVERRIDE_; diff --git a/source/Irrlicht/CSkyDomeSceneNode.cpp b/source/Irrlicht/CSkyDomeSceneNode.cpp index 410a0952..7b6affe0 100644 --- a/source/Irrlicht/CSkyDomeSceneNode.cpp +++ b/source/Irrlicht/CSkyDomeSceneNode.cpp @@ -197,11 +197,7 @@ void CSkyDomeSceneNode::OnRegisterSceneNode() } -//! returns the material based on the zero based index i. To get the amount -//! of materials used by this scene node, use getMaterialCount(). -//! This function is needed for inserting the node into the scene hirachy on a -//! optimal position for minimizing renderstate changes, but can also be used -//! to directly modify the material of a scene node. +//! returns the material based on the zero based index i. video::SMaterial& CSkyDomeSceneNode::getMaterial(u32 i) { return Buffer->Material; diff --git a/source/Irrlicht/CSphereSceneNode.cpp b/source/Irrlicht/CSphereSceneNode.cpp index 7dbe814d..1693f5e2 100644 --- a/source/Irrlicht/CSphereSceneNode.cpp +++ b/source/Irrlicht/CSphereSceneNode.cpp @@ -123,11 +123,7 @@ void CSphereSceneNode::OnRegisterSceneNode() } -//! returns the material based on the zero based index i. To get the amount -//! of materials used by this scene node, use getMaterialCount(). -//! This function is needed for inserting the node into the scene hirachy on a -//! optimal position for minimizing renderstate changes, but can also be used -//! to directly modify the material of a scene node. +//! returns the material based on the zero based index i. video::SMaterial& CSphereSceneNode::getMaterial(u32 i) { if (i>0 || !Mesh) diff --git a/source/Irrlicht/CSphereSceneNode.h b/source/Irrlicht/CSphereSceneNode.h index 904cb508..480881cb 100644 --- a/source/Irrlicht/CSphereSceneNode.h +++ b/source/Irrlicht/CSphereSceneNode.h @@ -35,7 +35,7 @@ namespace scene //! returns the material based on the zero based index i. To get the amount //! of materials used by this scene node, use getMaterialCount(). - //! This function is needed for inserting the node into the scene hirachy on a + //! This function is needed for inserting the node into the scene hierarchy on a //! optimal position for minimizing renderstate changes, but can also be used //! to directly modify the material of a scene node. virtual video::SMaterial& getMaterial(u32 i) _IRR_OVERRIDE_; diff --git a/source/Irrlicht/CVolumeLightSceneNode.cpp b/source/Irrlicht/CVolumeLightSceneNode.cpp index 966e8237..720c3607 100644 --- a/source/Irrlicht/CVolumeLightSceneNode.cpp +++ b/source/Irrlicht/CVolumeLightSceneNode.cpp @@ -80,18 +80,12 @@ void CVolumeLightSceneNode::OnRegisterSceneNode() } -//! returns the material based on the zero based index i. To get the amount -//! of materials used by this scene node, use getMaterialCount(). -//! This function is needed for inserting the node into the scene hirachy on a -//! optimal position for minimizing renderstate changes, but can also be used -//! to directly modify the material of a scene node. video::SMaterial& CVolumeLightSceneNode::getMaterial(u32 i) { return Mesh->getMeshBuffer(i)->getMaterial(); } -//! returns amount of materials used by this scene node. u32 CVolumeLightSceneNode::getMaterialCount() const { return 1; diff --git a/source/Irrlicht/CWGLManager.cpp b/source/Irrlicht/CWGLManager.cpp index 28161a7a..da9fb1a3 100644 --- a/source/Irrlicht/CWGLManager.cpp +++ b/source/Irrlicht/CWGLManager.cpp @@ -233,13 +233,13 @@ bool CWGLManager::initialize(const SIrrlichtCreationParameters& params, const SE WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, #ifdef WGL_ARB_multisample WGL_SAMPLES_ARB,Params.AntiAlias, // 20,21 - WGL_SAMPLE_BUFFERS_ARB, 1, + WGL_SAMPLE_BUFFERS_ARB, (Params.AntiAlias>0) ? 1 : 0, #elif defined(WGL_EXT_multisample) WGL_SAMPLES_EXT,AntiAlias, // 20,21 - WGL_SAMPLE_BUFFERS_EXT, 1, + WGL_SAMPLE_BUFFERS_EXT, (Params.AntiAlias>0) ? 1 : 0, #elif defined(WGL_3DFX_multisample) WGL_SAMPLES_3DFX,AntiAlias, // 20,21 - WGL_SAMPLE_BUFFERS_3DFX, 1, + WGL_SAMPLE_BUFFERS_3DFX, (Params.AntiAlias>0) ? 1 : 0, #endif #ifdef WGL_ARB_framebuffer_sRGB WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, Params.HandleSRGB ? 1:0, diff --git a/source/Irrlicht/Irrlicht10.0.vcxproj b/source/Irrlicht/Irrlicht10.0.vcxproj index 4cccf741..78c279ca 100644 --- a/source/Irrlicht/Irrlicht10.0.vcxproj +++ b/source/Irrlicht/Irrlicht10.0.vcxproj @@ -844,6 +844,7 @@ + diff --git a/source/Irrlicht/Irrlicht10.0.vcxproj.filters b/source/Irrlicht/Irrlicht10.0.vcxproj.filters index ca39f218..6269a9c5 100644 --- a/source/Irrlicht/Irrlicht10.0.vcxproj.filters +++ b/source/Irrlicht/Irrlicht10.0.vcxproj.filters @@ -1387,6 +1387,9 @@ include\video + + include\scene + diff --git a/source/Irrlicht/Irrlicht11.0.vcxproj b/source/Irrlicht/Irrlicht11.0.vcxproj index ce54fce0..791a36a0 100644 --- a/source/Irrlicht/Irrlicht11.0.vcxproj +++ b/source/Irrlicht/Irrlicht11.0.vcxproj @@ -842,6 +842,7 @@ + diff --git a/source/Irrlicht/Irrlicht11.0.vcxproj.filters b/source/Irrlicht/Irrlicht11.0.vcxproj.filters index 13b5ed64..f61a3f95 100644 --- a/source/Irrlicht/Irrlicht11.0.vcxproj.filters +++ b/source/Irrlicht/Irrlicht11.0.vcxproj.filters @@ -1387,6 +1387,9 @@ include\video + + include\scene + diff --git a/source/Irrlicht/Irrlicht12.0.vcxproj b/source/Irrlicht/Irrlicht12.0.vcxproj index c72568e1..7b01c746 100644 --- a/source/Irrlicht/Irrlicht12.0.vcxproj +++ b/source/Irrlicht/Irrlicht12.0.vcxproj @@ -842,6 +842,7 @@ + diff --git a/source/Irrlicht/Irrlicht12.0.vcxproj.filters b/source/Irrlicht/Irrlicht12.0.vcxproj.filters index 1e1d5ce0..7e94d188 100644 --- a/source/Irrlicht/Irrlicht12.0.vcxproj.filters +++ b/source/Irrlicht/Irrlicht12.0.vcxproj.filters @@ -1387,6 +1387,9 @@ include\video + + include\scene + diff --git a/source/Irrlicht/Irrlicht14.0.vcxproj b/source/Irrlicht/Irrlicht14.0.vcxproj index 599145be..22385f15 100644 --- a/source/Irrlicht/Irrlicht14.0.vcxproj +++ b/source/Irrlicht/Irrlicht14.0.vcxproj @@ -853,6 +853,7 @@ + diff --git a/source/Irrlicht/Irrlicht14.0.vcxproj.filters b/source/Irrlicht/Irrlicht14.0.vcxproj.filters index 8b5c48cb..ed8c285b 100644 --- a/source/Irrlicht/Irrlicht14.0.vcxproj.filters +++ b/source/Irrlicht/Irrlicht14.0.vcxproj.filters @@ -1387,6 +1387,9 @@ include\video + + include\scene + diff --git a/tests/main.cpp b/tests/main.cpp index a0a73b9d..0f604a72 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -49,8 +49,9 @@ int main(int argumentCount, char * arguments[]) #if 0 // To interactively debug a test, move it (temporarily) in here and enable the define to only run this test // Otherwise debugging is slightly tricky as each test runs in it's own process. - TEST(textureFeatures); + TEST(stencilShadow); #else + TEST(disambiguateTextures); // Normally you should run this first, since it validates the working directory. // Now the simple tests without device TEST(testIrrArray); @@ -122,9 +123,8 @@ int main(int argumentCount, char * arguments[]) TEST(mrt); TEST(projectionMatrix); // large scenes/long rendering - // shadows are slow -// TEST(orthoCam); -// TEST(stencilShadow); + TEST(orthoCam); + TEST(stencilShadow); // q3 maps are slow TEST(planeMatrix); TEST(terrainSceneNode); diff --git a/tests/media/Burning's Video-stencilSelfShadow.png b/tests/media/Burning's Video-stencilSelfShadow.png new file mode 100644 index 0000000000000000000000000000000000000000..dcdb60873dd30a7bf85280658c1016afc0ce9ba4 GIT binary patch literal 16396 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

P5_4x4N(8;g6`VSx8AFQJ? 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 Q%O9YpWvKZ{!#?`|0O@180{{R3 literal 0 HcmV?d00001 diff --git a/tests/media/Burning's Video-stencilShadow.png b/tests/media/Burning's Video-stencilShadow.png new file mode 100644 index 0000000000000000000000000000000000000000..60f31cdc11908f292f097d3b024441f354172704 GIT binary patch literal 3275 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 literal 0 HcmV?d00001 diff --git a/tests/media/Direct3D 9.0-orthoStencil.png b/tests/media/Direct3D 9.0-orthoStencil.png index bc24e20640f39f979e9af1e97d8b974cc6471409..4e7afb8ab82cdc9e62a13aba884e9ad4d2516ef8 100644 GIT binary patch literal 2109 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 Ay8r+H literal 14567 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 ZaDC?Yf}mVX@V_SmSejkHG@$XM{{a!|L8kx! literal 5671 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

@s|hqfyzT^wWvF;p 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 literal 2084 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 literal 12250 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 kmc5p^mtwe;!=2*{KrAVW4~7G%k^)kA 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 EKivdlzW@LL literal 5321 zcmbVQ)mzjJ6aDSdrQp&H(n^O2vc$ql3M`$X)RKybNG}c2E!|zx9r7X}u(ZIE3hqiH z-F*HH-^I*aoS8FobDo)JB6VIoryyk{1pt6TO;s83FXsKTkHp}A{<7Cq8UU!{)sz+W zKW6P_J4IUqSCcC0Cc+yL{Pp@I)FDcRT#X1bj6l&w zV0ORgy*e*}V+|FvpCQ0)Un!?vxd2!R$;Ua-NWsk=!WA0~8O*7}d9dMjPWHq7_OfN& z_oD~bee4JRyKwsT)ZOyT#=qgz(x~pk}*m3n*+iVzKX8f5)4wxC=+^r8EYB0Ru@gHOc0Kd9SPLV4poD zjb#01bGQzBNTlkH+I0Fl3(unXuY~<%LmWkh%o`58c=9*<&+6E_J|#}2j^Gm#M|9p5 z7mG)!>#_O8Q!SsEm|Axr#H1fOSPg+J&%pUd1A(W7Z5 z)Z%a&0Ls`{C4=2bH?pQN#tFs7YHD6bwwwcPP<@#$zO^!w(NQ@`Dgy$EkYrkY^PQfg zgd-%#Dmt{JNtAlHEG1Dx${bb0QT2$zM5f`u;3@i=NSrDZNuC-{ok%{a$B;40TLpEE zQJdo8udEfRls`Q`3)sh05*-H^lnJIew^O3#%?FB(V5zh1=>0u!ehh}z0GI6vhnzIB z7hL3F!~pX2K6=K#&^WnU<*y$n5Uq)=_(BQy?7GHn&mtm=BKcm|D194>x82GrmPFZs ziXwtj59?A)G<6d3O=iN$O-YR$ML$$$Kiw>n!F)3GP?nvC28zh!>R?z~gWwr%QV<%* zg90H}M%Xh0(;LsPAM+Bw7te84s;M$$j52x!Xa=!I+mywOUyf$qijz=wKL!NZv4udO zO;Zvo%7I*$2q+-uuObn=K!i$sVG$U9W87*H9Ir1FkS3Wq<;-5mQAA9U5lKlMOu}6m z_pNAhDK)ckEs>+5&=wqsG9lG1Yr#l!VaxTELH$zcCR!4cMHx(8#nup-YgF3^W^yX`;NjoHNq?qbyx|K zGDnAbB}$wM3yi1u)4AAegMWqXS5C$;BMxw)U4;NTsbQ8~Om_5B#!Wf>quee#rWMa? z-}ZMux96*hlS=E?tru5Ly6=|m;!IcScegzAIs=CZAue`WJ#HO5wtr~~BZLzG>S9tY zdaL#DBOmqa=RjoPBQX?cYb;s>?rF+=n3!;CR7%Qr<(y*nc1C5;eoEdS*MA3X*?b~Y z-E$c*4i$Ajf8HZ~z2VL=W6^|IIP^a0etN5+_Fabv1e-t%*O&A@o(F6ZB#y+^VB8@p&8hTFnn)m)}41 zr=>VqQ0?&Cy=~ZrVHvG??Jd1hOjN_dzEMOOvUi^z22hcDkn;n*?2KD_lueO^l=uI> z<#yvPgNadZX4p~&$}|?32rd#F5q9!gu-mb5sKC0M$6D6PwpT9OooYM($gW<`9iJb) z+A{w9Y^%lmaZM4dP*tk#gL#rNej_`|5N9;_nFc=S6)d`wv~ z$gH*BKGjw187VGZ@>AXPVe8{;6wWDfQ)p+EK3@^ur`eEnriE{DYZkw-c- zR2d{zF%CA*Na&MM9 zsi1*9&|01S#6t4T{Z|L1Mj%0WK0oJIuJVdL12_RNDlISaRJ(-o;bXie$>rD0JufN> z9d`Ry{BkYMsA^#0_dNEO>Z}J5H5s0mcfeU@ZaKWTke2ja(Q362r3;nrtQqv6GT^=fZV33ylt@nrW^hd`U*B?dklp-a(hcn{SnDeD^F#zWc>msii=}?PP7!)h| z{YrnFNGw8Rp!6~1r9e|y&oUa(G8)98)87w7djARrT}WyG)Q|E%hjx$5D*M#Jk+Sha}|r9o$z z=Bb?*Pj*+DN?>dn92gS2g#ZxQ#r(}-f!lcQ?bq(-xybI1pMP||O&SApz@7=wNkHBOtFBX#sUg9Szv8F1g|8U1|GUpE_rQ@Jf?pPvj4J8pO1 z@1axGWlVUNqJsp#6tix*zueGtx=9WD9*+P#L^ntb40_(iBTk{MJgM6mgARkSEE^lF4zJ?8qtl zNmAcza1&BrUA09q?=}$NwFZo{By!n-waQ`(tYm{{672qn9)N&3>2Hq}SX2vjne!

cy1l$f2GSJFhs4hDh0^7gQnWHW`DzDf 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 cam->setPosition(core::vector3df(300,250,-300)); cam->setTarget(core::vector3df(0,20,0)); - cam->setProjectionMatrix(core::matrix4().buildProjectionMatrixOrthoLH(120,90,0.9f,2000.f,driverType != video::EDT_OPENGL), true); + cam->setProjectionMatrix(core::matrix4().buildProjectionMatrixOrthoLH(120,90,0.9f,5000.f,driverType != video::EDT_OPENGL), true); device->getSceneManager()->addAnimatedMeshSceneNode(device->getSceneManager()->addHillPlaneMesh("plane", core::dimension2df(32,32), core::dimension2du(16,16)));//->setMaterialFlag(video::EMF_WIREFRAME, true); @@ -97,6 +97,7 @@ bool orthoCam(void) passed &= testOrthoCam(video::EDT_BURNINGSVIDEO); passed &= testOrthoCam(video::EDT_DIRECT3D9); + // TODO: not sure if burnings could work? Currently it doesn't. passed &= testOrthoStencil(video::EDT_OPENGL); passed &= testOrthoStencil(video::EDT_DIRECT3D9); diff --git a/tests/stencilshadow.cpp b/tests/stencilshadow.cpp index eb520e6d..4e7f3e3f 100644 --- a/tests/stencilshadow.cpp +++ b/tests/stencilshadow.cpp @@ -5,7 +5,8 @@ using namespace irr; -static bool shadows(video::E_DRIVER_TYPE driverType) +// +static bool stencilShadow(video::E_DRIVER_TYPE driverType) { IrrlichtDevice *device = createDevice (driverType, core::dimension2d(160,120), 16, false, true); if (!device) @@ -14,45 +15,71 @@ static bool shadows(video::E_DRIVER_TYPE driverType) stabilizeScreenBackground(device->getVideoDriver()); scene::ICameraSceneNode* cam = device->getSceneManager()->addCameraSceneNodeFPS(); - cam->setPosition(core::vector3df(-15,55,10)); - cam->setTarget(core::vector3df(-5,-5,-15)); + cam->setPosition(core::vector3df(-15,60,40)); + cam->setTarget(core::vector3df(+25,-5,-25)); device->getSceneManager()->setAmbientLight(video::SColorf(.5f,.5f,.5f)); - scene::IMeshSceneNode* cube = device->getSceneManager()->addCubeSceneNode(100, 0, -1, core::vector3df(0,50,0)); - cube->setScale(core::vector3df(-1,-1,-1)); + device->getSceneManager()->setShadowColor( video::SColor(255, 50, 0, 50)); + device->getSceneManager()->addCubeSceneNode(100, 0, -1, core::vector3df(0,50,0), core::vector3df(), core::vector3df(-1,-1,-1)); - scene::IAnimatedMeshSceneNode* node = device->getSceneManager()->addAnimatedMeshSceneNode(device->getSceneManager()->getMesh("../media/ninja.b3d"), 0, -1, core::vector3df(0,2,0), core::vector3df(),core::vector3df(5,5,5)); + scene::IAnimatedMeshSceneNode* node = device->getSceneManager()->addAnimatedMeshSceneNode(device->getSceneManager()->getMesh("../media/ninja.b3d"), 0, -1, core::vector3df(), core::vector3df(0.f, 230.f, 0.f),core::vector3df(5,5,5)); node->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); - node->addShadowVolumeSceneNode(); + node->addShadowVolumeSceneNode(0, -1, true, 200.f); node->setAnimationSpeed(0.f); - scene::ILightSceneNode* light = device->getSceneManager()->addLightSceneNode(0, core::vector3df(10,10,10)); + scene::IMeshSceneNode* cube2 = device->getSceneManager()->addCubeSceneNode(10, 0, -1, core::vector3df(40,0,0), core::vector3df(), core::vector3df(1,1,2.5f)); + cube2->getMaterial(0).DiffuseColor = video::SColor(220, 0, 100, 100); + cube2->addShadowVolumeSceneNode(0, -1, false, 200.f); + + scene::ILightSceneNode* light = device->getSceneManager()->addLightSceneNode(0, core::vector3df(-40,10,20)); light->setLightType(video::ELT_POINT); light->setRadius(500.f); - light->getLightData().DiffuseColor.set(0,1,1); + light->getLightData().DiffuseColor.set(1,1,1); - device->getVideoDriver()->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0,0,0,0)); + device->getVideoDriver()->beginScene(video::ECBF_ALL, video::SColor(0,0,0,0)); device->getSceneManager()->drawAll(); device->getVideoDriver()->endScene(); bool result = takeScreenshotAndCompareAgainstReference(device->getVideoDriver(), "-stencilShadow.png", 99.91f); - node->remove(); - cube->remove(); - // test self-shadowing - node = device->getSceneManager()->addAnimatedMeshSceneNode(device->getSceneManager()->getMesh("../media/dwarf.x")); + device->closeDevice(); + device->run(); + device->drop(); + + return result; +} + +// test self-shadowing +static bool selfShadowing(video::E_DRIVER_TYPE driverType) +{ + IrrlichtDevice *device = createDevice (driverType, core::dimension2d(160,120), 16, false, true); + if (!device) + return true; // No error if device does not exist + + stabilizeScreenBackground(device->getVideoDriver()); + + device->getSceneManager()->setAmbientLight(video::SColorf(.5f,.5f,.5f)); + device->getSceneManager()->setShadowColor( video::SColor(220, 50, 0, 0)); + + scene::IAnimatedMeshSceneNode* node = device->getSceneManager()->addAnimatedMeshSceneNode(device->getSceneManager()->getMesh("../media/dwarf.x")); node->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); node->addShadowVolumeSceneNode(); node->setAnimationSpeed(0.f); + scene::ICameraSceneNode* cam = device->getSceneManager()->addCameraSceneNodeFPS(); cam->setPosition(core::vector3df(0,55,-30)); cam->setTarget(core::vector3df(60,45,150)); - device->getVideoDriver()->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0,0,0,0)); + scene::ILightSceneNode* light = device->getSceneManager()->addLightSceneNode(0, core::vector3df(100,100,-20)); + light->setLightType(video::ELT_POINT); + light->setRadius(500.f); + light->getLightData().DiffuseColor.set(0,1,1); + + device->getVideoDriver()->beginScene(video::ECBF_ALL, video::SColor(0,0,0,0)); device->getSceneManager()->drawAll(); device->getVideoDriver()->endScene(); - result = takeScreenshotAndCompareAgainstReference(device->getVideoDriver(), "-stencilSelfShadow.png", 99.41f); + bool result = takeScreenshotAndCompareAgainstReference(device->getVideoDriver(), "-stencilSelfShadow.png", 99.41f); device->closeDevice(); device->run(); @@ -65,11 +92,17 @@ bool stencilShadow(void) { bool passed = true; - passed &= shadows(video::EDT_OPENGL); - // no shadows in these renderers -// passed &= shadows(video::EDT_SOFTWARE); -// passed &= shadows(video::EDT_BURNINGSVIDEO); - passed &= shadows(video::EDT_DIRECT3D9); + passed &= stencilShadow(video::EDT_OPENGL); + passed &= stencilShadow(video::EDT_DIRECT3D9); + // no shadows in software renderer +// passed &= stencilShadow(video::EDT_SOFTWARE); + passed &= stencilShadow(video::EDT_BURNINGSVIDEO); // Note: cube has wrong color, if that gets ever changed just update the test-image. + + passed &= selfShadowing(video::EDT_OPENGL); + passed &= selfShadowing(video::EDT_DIRECT3D9); + // no shadows in software renderer +// passed &= selfShadowing(video::EDT_SOFTWARE); + passed &= selfShadowing(video::EDT_BURNINGSVIDEO); return passed; } diff --git a/tests/tests-last-passed-at.txt b/tests/tests-last-passed-at.txt index d503b194..eb9e250c 100644 --- a/tests/tests-last-passed-at.txt +++ b/tests/tests-last-passed-at.txt @@ -1,4 +1,4 @@ -Tests finished. 70 tests of 70 passed. -Compiled as DEBUG -Test suite pass at GMT Tue Nov 12 17:06:08 2019 - +Tests finished. 72 tests of 72 passed. +Compiled as DEBUG +Test suite pass at GMT Fri Nov 22 15:52:21 2019 +