diff --git a/CMakeLists.txt b/CMakeLists.txt index bcfa9e11a..30755893d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,10 @@ endif() option(USE_SWITCH "Build targetting switch" OFF) +if(WIN32) +option(USE_DIRECTX "Build DirectX 9 driver (requires DirectX SDK)" OFF) +endif() + option(SERVER_ONLY "Create a server only (i.e. no graphics or sound)" OFF) option(CHECK_ASSETS "Check if assets are installed in ../stk-assets" ON) option(USE_SYSTEM_ANGELSCRIPT "Use system angelscript instead of built-in angelscript. If you enable this option, make sure to use a compatible version." OFF) @@ -158,6 +162,10 @@ if(WIN32) include_directories(${PROJECT_SOURCE_DIR}/${DEPENDENCIES}/include) endif() +if(WIN32 AND NOT USE_DIRECTX) + add_definitions(-DNO_IRR_COMPILE_WITH_DIRECT3D_9_) +endif() + if(USE_GLES2) add_definitions(-DUSE_GLES2) if (NOT IOS) diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt index 99158498e..6a70dc33a 100644 --- a/lib/irrlicht/CMakeLists.txt +++ b/lib/irrlicht/CMakeLists.txt @@ -187,6 +187,19 @@ source/Irrlicht/Irrlicht.cpp source/Irrlicht/irrXML.cpp source/Irrlicht/os.cpp source/Irrlicht/COpenGLNormalMapRenderer.cpp +source/Irrlicht/CD3D9Driver.cpp +source/Irrlicht/CD3D9Driver.h +source/Irrlicht/CD3D9HLSLMaterialRenderer.cpp +source/Irrlicht/CD3D9HLSLMaterialRenderer.h +source/Irrlicht/CD3D9MaterialRenderer.h +source/Irrlicht/CD3D9NormalMapRenderer.cpp +source/Irrlicht/CD3D9NormalMapRenderer.h +source/Irrlicht/CD3D9ParallaxMapRenderer.cpp +source/Irrlicht/CD3D9ParallaxMapRenderer.h +source/Irrlicht/CD3D9ShaderMaterialRenderer.cpp +source/Irrlicht/CD3D9ShaderMaterialRenderer.h +source/Irrlicht/CD3D9Texture.cpp +source/Irrlicht/CD3D9Texture.h source/Irrlicht/CAnimatedMeshSceneNode.h source/Irrlicht/CAttributeImpl.h source/Irrlicht/CAttributes.h diff --git a/lib/irrlicht/include/IrrCompileConfig.h b/lib/irrlicht/include/IrrCompileConfig.h index 3538788d9..ea9097045 100644 --- a/lib/irrlicht/include/IrrCompileConfig.h +++ b/lib/irrlicht/include/IrrCompileConfig.h @@ -157,7 +157,11 @@ If not defined, Windows Multimedia library is used, which offers also broad supp //! Only define _IRR_COMPILE_WITH_DIRECT3D_8_ if you have an appropriate DXSDK, e.g. Summer 2004 // #define _IRR_COMPILE_WITH_DIRECT3D_8_ +#ifdef NO_IRR_COMPILE_WITH_DIRECT3D_9_ #undef _IRR_COMPILE_WITH_DIRECT3D_9_ +#else +#define _IRR_COMPILE_WITH_DIRECT3D_9_ +#endif #endif diff --git a/lib/irrlicht/source/Irrlicht/CD3D9Driver.cpp b/lib/irrlicht/source/Irrlicht/CD3D9Driver.cpp new file mode 100644 index 000000000..94e242d3c --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9Driver.cpp @@ -0,0 +1,3633 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#define _IRR_D3D_NO_SHADER_DEBUGGING 1 +#define _IRR_DONT_DO_MEMORY_DEBUGGING_HERE +#include "CD3D9Driver.h" + +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + +#include "os.h" +#include "S3DVertex.h" +#include "CD3D9Texture.h" +#include "CD3D9MaterialRenderer.h" +#include "CD3D9ShaderMaterialRenderer.h" +#include "CD3D9NormalMapRenderer.h" +#include "CD3D9ParallaxMapRenderer.h" +#include "CD3D9HLSLMaterialRenderer.h" +#include "SIrrCreationParameters.h" + +namespace irr +{ +namespace video +{ + +namespace +{ + inline DWORD F2DW( FLOAT f ) { return *((DWORD*)&f); } +} + +//! constructor +CD3D9Driver::CD3D9Driver(const SIrrlichtCreationParameters& params, io::IFileSystem* io) + : CNullDriver(io, params.WindowSize), CurrentRenderMode(ERM_NONE), + ResetRenderStates(true), Transformation3DChanged(false), + D3DLibrary(0), pID3D(0), pID3DDevice(0), PrevRenderTarget(0), + WindowId(0), SceneSourceRect(0), + LastVertexType((video::E_VERTEX_TYPE)-1), VendorID(0), + MaxTextureUnits(0), MaxUserClipPlanes(0), MaxMRTs(1), NumSetMRTs(1), + MaxLightDistance(0.f), LastSetLight(-1), + ColorFormat(ECF_A8R8G8B8), DeviceLost(false), + DriverWasReset(true), OcclusionQuerySupport(false), + AlphaToCoverageSupport(false), Params(params) +{ + #ifdef _DEBUG + setDebugName("CD3D9Driver"); + #endif + + printVersion(); + + for (u32 i=0; idrop(); + } + DepthBuffers.clear(); + + // drop d3d9 + + if (pID3DDevice) + pID3DDevice->Release(); + + if (pID3D) + pID3D->Release(); + + #ifdef _IRR_COMPILE_WITH_CG_ + cgD3D9SetDevice(0); + + if(CgContext) + { + cgDestroyContext(CgContext); + } + #endif +} + + +void CD3D9Driver::createMaterialRenderers() +{ + // create D3D9 material renderers + + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_SOLID(pID3DDevice, this)); + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_SOLID_2_LAYER(pID3DDevice, this)); + + // add the same renderer for all lightmap types + + CD3D9MaterialRenderer_LIGHTMAP* lmr = new CD3D9MaterialRenderer_LIGHTMAP(pID3DDevice, this); + addMaterialRenderer(lmr); // for EMT_LIGHTMAP: + addMaterialRenderer(lmr); // for EMT_LIGHTMAP_ADD: + addMaterialRenderer(lmr); // for EMT_LIGHTMAP_M2: + addMaterialRenderer(lmr); // for EMT_LIGHTMAP_M4: + addMaterialRenderer(lmr); // for EMT_LIGHTMAP_LIGHTING: + addMaterialRenderer(lmr); // for EMT_LIGHTMAP_LIGHTING_M2: + addMaterialRenderer(lmr); // for EMT_LIGHTMAP_LIGHTING_M4: + lmr->drop(); + + // add remaining fixed function pipeline material renderers + + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_DETAIL_MAP(pID3DDevice, this)); + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_SPHERE_MAP(pID3DDevice, this)); + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_REFLECTION_2_LAYER(pID3DDevice, this)); + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_TRANSPARENT_ADD_COLOR(pID3DDevice, this)); + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL(pID3DDevice, this)); + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF(pID3DDevice, this)); + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_TRANSPARENT_VERTEX_ALPHA(pID3DDevice, this)); + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_TRANSPARENT_REFLECTION_2_LAYER(pID3DDevice, this)); + + // add normal map renderers + + s32 tmp = 0; + video::IMaterialRenderer* renderer = 0; + + renderer = new CD3D9NormalMapRenderer(pID3DDevice, this, tmp, + MaterialRenderers[EMT_SOLID].Renderer); + renderer->drop(); + + renderer = new CD3D9NormalMapRenderer(pID3DDevice, this, tmp, + MaterialRenderers[EMT_TRANSPARENT_ADD_COLOR].Renderer); + renderer->drop(); + + renderer = new CD3D9NormalMapRenderer(pID3DDevice, this, tmp, + MaterialRenderers[EMT_TRANSPARENT_VERTEX_ALPHA].Renderer); + renderer->drop(); + + // add parallax map renderers + + renderer = new CD3D9ParallaxMapRenderer(pID3DDevice, this, tmp, + MaterialRenderers[EMT_SOLID].Renderer); + renderer->drop(); + + renderer = new CD3D9ParallaxMapRenderer(pID3DDevice, this, tmp, + MaterialRenderers[EMT_TRANSPARENT_ADD_COLOR].Renderer); + renderer->drop(); + + renderer = new CD3D9ParallaxMapRenderer(pID3DDevice, this, tmp, + MaterialRenderers[EMT_TRANSPARENT_VERTEX_ALPHA].Renderer); + renderer->drop(); + + // add basic 1 texture blending + addAndDropMaterialRenderer(new CD3D9MaterialRenderer_ONETEXTURE_BLEND(pID3DDevice, this)); +} + + +//! initialises the Direct3D API +bool CD3D9Driver::initDriver(HWND hwnd, bool pureSoftware) +{ + if (!pID3D) + { + D3DLibrary = LoadLibraryA("d3d9.dll"); + + if (!D3DLibrary) + { + os::Printer::log("Error, could not load d3d9.dll.", ELL_ERROR); + return false; + } + + typedef IDirect3D9 * (__stdcall *D3DCREATETYPE)(UINT); + D3DCREATETYPE d3dCreate = (D3DCREATETYPE) GetProcAddress(D3DLibrary, "Direct3DCreate9"); + + if (!d3dCreate) + { + os::Printer::log("Error, could not get proc adress of Direct3DCreate9.", ELL_ERROR); + return false; + } + + //just like pID3D = Direct3DCreate9(D3D_SDK_VERSION); + pID3D = (*d3dCreate)(D3D_SDK_VERSION); + + if (!pID3D) + { + os::Printer::log("Error initializing D3D.", ELL_ERROR); + return false; + } + } + + // print device information + D3DADAPTER_IDENTIFIER9 dai; + if (!FAILED(pID3D->GetAdapterIdentifier(Params.DisplayAdapter, 0, &dai))) + { + char tmp[512]; + + s32 Product = HIWORD(dai.DriverVersion.HighPart); + s32 Version = LOWORD(dai.DriverVersion.HighPart); + s32 SubVersion = HIWORD(dai.DriverVersion.LowPart); + s32 Build = LOWORD(dai.DriverVersion.LowPart); + + sprintf(tmp, "%s %s %d.%d.%d.%d", dai.Description, dai.Driver, Product, Version, + SubVersion, Build); + os::Printer::log(tmp, ELL_INFORMATION); + + // Assign vendor name based on vendor id. + VendorID= static_cast(dai.VendorId); + switch(dai.VendorId) + { + case 0x1002 : VendorName = "ATI Technologies Inc."; break; + case 0x10DE : VendorName = "NVIDIA Corporation"; break; + case 0x102B : VendorName = "Matrox Electronic Systems Ltd."; break; + case 0x121A : VendorName = "3dfx Interactive Inc"; break; + case 0x5333 : VendorName = "S3 Graphics Co., Ltd."; break; + case 0x8086 : VendorName = "Intel Corporation"; break; + default: VendorName = "Unknown VendorId: ";VendorName += (u32)dai.VendorId; break; + } + } + + D3DDISPLAYMODE d3ddm; + if (FAILED(pID3D->GetAdapterDisplayMode(Params.DisplayAdapter, &d3ddm))) + { + os::Printer::log("Error: Could not get Adapter Display mode.", ELL_ERROR); + return false; + } + + ZeroMemory(&present, sizeof(present)); + + present.BackBufferCount = 1; + present.EnableAutoDepthStencil = TRUE; + if (Params.SwapInterval == 1) + present.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + else + present.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + + if (Params.Fullscreen) + { + present.BackBufferWidth = Params.WindowSize.Width; + present.BackBufferHeight = Params.WindowSize.Height; + // request 32bit mode if user specified 32 bit, added by Thomas Stuefe + if (Params.Bits == 32) + present.BackBufferFormat = D3DFMT_X8R8G8B8; + else + present.BackBufferFormat = D3DFMT_R5G6B5; + present.SwapEffect = D3DSWAPEFFECT_FLIP; + present.Windowed = FALSE; + present.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; + } + else + { + present.BackBufferFormat = d3ddm.Format; + present.SwapEffect = D3DSWAPEFFECT_DISCARD; + present.Windowed = TRUE; + } + + UINT adapter = Params.DisplayAdapter; + D3DDEVTYPE devtype = D3DDEVTYPE_HAL; + #ifndef _IRR_D3D_NO_SHADER_DEBUGGING + devtype = D3DDEVTYPE_REF; + #elif defined(_IRR_USE_NVIDIA_PERFHUD_) + for (UINT adapter_i = 0; adapter_i < pID3D->GetAdapterCount(); ++adapter_i) + { + D3DADAPTER_IDENTIFIER9 identifier; + pID3D->GetAdapterIdentifier(adapter_i,0,&identifier); + if (strstr(identifier.Description,"PerfHUD") != 0) + { + adapter = adapter_i; + devtype = D3DDEVTYPE_REF; + break; + } + } + #endif + + // enable anti alias if possible and desired + if (Params.AntiAlias > 0) + { + if (Params.AntiAlias > 32) + Params.AntiAlias = 32; + + DWORD qualityLevels = 0; + + while(Params.AntiAlias > 0) + { + if(SUCCEEDED(pID3D->CheckDeviceMultiSampleType(adapter, + devtype, present.BackBufferFormat, !Params.Fullscreen, + (D3DMULTISAMPLE_TYPE)Params.AntiAlias, &qualityLevels))) + { + present.MultiSampleType = (D3DMULTISAMPLE_TYPE)Params.AntiAlias; + present.MultiSampleQuality = qualityLevels-1; + present.SwapEffect = D3DSWAPEFFECT_DISCARD; + break; + } + --Params.AntiAlias; + } + + if (Params.AntiAlias==0) + { + os::Printer::log("Anti aliasing disabled because hardware/driver lacks necessary caps.", ELL_WARNING); + } + } + + // check stencil buffer compatibility + if (Params.Stencilbuffer) + { + present.AutoDepthStencilFormat = D3DFMT_D24S8; + if(FAILED(pID3D->CheckDeviceFormat(adapter, devtype, + present.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, + D3DRTYPE_SURFACE, present.AutoDepthStencilFormat))) + { + present.AutoDepthStencilFormat = D3DFMT_D24X4S4; + if(FAILED(pID3D->CheckDeviceFormat(adapter, devtype, + present.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, + D3DRTYPE_SURFACE, present.AutoDepthStencilFormat))) + { + present.AutoDepthStencilFormat = D3DFMT_D15S1; + if(FAILED(pID3D->CheckDeviceFormat(adapter, devtype, + present.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, + D3DRTYPE_SURFACE, present.AutoDepthStencilFormat))) + { + os::Printer::log("Device does not support stencilbuffer, disabling stencil buffer.", ELL_WARNING); + Params.Stencilbuffer = false; + } + } + } + else + if(FAILED(pID3D->CheckDepthStencilMatch(adapter, devtype, + present.BackBufferFormat, present.BackBufferFormat, present.AutoDepthStencilFormat))) + { + os::Printer::log("Depth-stencil format is not compatible with display format, disabling stencil buffer.", ELL_WARNING); + Params.Stencilbuffer = false; + } + } + // do not use else here to cope with flag change in previous block + if (!Params.Stencilbuffer) + { + present.AutoDepthStencilFormat = D3DFMT_D32; + if(FAILED(pID3D->CheckDeviceFormat(adapter, devtype, + present.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, + D3DRTYPE_SURFACE, present.AutoDepthStencilFormat))) + { + present.AutoDepthStencilFormat = D3DFMT_D24X8; + if(FAILED(pID3D->CheckDeviceFormat(adapter, devtype, + present.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, + D3DRTYPE_SURFACE, present.AutoDepthStencilFormat))) + { + present.AutoDepthStencilFormat = D3DFMT_D16; + if(FAILED(pID3D->CheckDeviceFormat(adapter, devtype, + present.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, + D3DRTYPE_SURFACE, present.AutoDepthStencilFormat))) + { + os::Printer::log("Device does not support required depth buffer.", ELL_WARNING); + return false; + } + } + } + } + + // create device + + DWORD fpuPrecision = Params.HighPrecisionFPU ? D3DCREATE_FPU_PRESERVE : 0; + DWORD multithreaded = Params.DriverMultithreaded ? D3DCREATE_MULTITHREADED : 0; + if (pureSoftware) + { + if (FAILED(pID3D->CreateDevice(Params.DisplayAdapter, D3DDEVTYPE_REF, hwnd, + fpuPrecision | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present, &pID3DDevice))) + os::Printer::log("Was not able to create Direct3D9 software device.", ELL_ERROR); + } + else + { + HRESULT hr = pID3D->CreateDevice(adapter, devtype, hwnd, + fpuPrecision | multithreaded | D3DCREATE_HARDWARE_VERTEXPROCESSING, &present, &pID3DDevice); + + if(FAILED(hr)) + hr = pID3D->CreateDevice(adapter, devtype, hwnd, + fpuPrecision | multithreaded | D3DCREATE_MIXED_VERTEXPROCESSING , &present, &pID3DDevice); + + if(FAILED(hr)) + hr = pID3D->CreateDevice(adapter, devtype, hwnd, + fpuPrecision | multithreaded | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present, &pID3DDevice); + + if (FAILED(hr)) + os::Printer::log("Was not able to create Direct3D9 device.", ELL_ERROR); + } + + if (!pID3DDevice) + { + os::Printer::log("Was not able to create DIRECT3D9 device.", ELL_ERROR); + return false; + } + + // get caps + pID3DDevice->GetDeviceCaps(&Caps); + + os::Printer::log("Currently available Video Memory (kB)", core::stringc(pID3DDevice->GetAvailableTextureMem()/1024).c_str()); + + // disable stencilbuffer if necessary + if (Params.Stencilbuffer && + (!(Caps.StencilCaps & D3DSTENCILCAPS_DECRSAT) || + !(Caps.StencilCaps & D3DSTENCILCAPS_INCRSAT) || + !(Caps.StencilCaps & D3DSTENCILCAPS_KEEP))) + { + os::Printer::log("Device not able to use stencil buffer, disabling stencil buffer.", ELL_WARNING); + Params.Stencilbuffer = false; + } + + // set default vertex shader + setVertexShader(EVT_STANDARD); + + // set fog mode + setFog(FogColor, FogType, FogStart, FogEnd, FogDensity, PixelFog, RangeFog); + + // set exposed data + ExposedData.D3D9.D3D9 = pID3D; + ExposedData.D3D9.D3DDev9 = pID3DDevice; + ExposedData.D3D9.HWnd = hwnd; + + ResetRenderStates = true; + + // create materials + createMaterialRenderers(); + + MaxTextureUnits = core::min_((u32)Caps.MaxSimultaneousTextures, MATERIAL_MAX_TEXTURES); + MaxUserClipPlanes = (u32)Caps.MaxUserClipPlanes; + MaxMRTs = (s32)Caps.NumSimultaneousRTs; + OcclusionQuerySupport=(pID3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, NULL) == S_OK); + + if (VendorID==0x10DE)//NVidia + AlphaToCoverageSupport = (pID3D->CheckDeviceFormat(adapter, D3DDEVTYPE_HAL, + D3DFMT_X8R8G8B8, 0,D3DRTYPE_SURFACE, + (D3DFORMAT)MAKEFOURCC('A', 'T', 'O', 'C')) == S_OK); + else if (VendorID==0x1002)//ATI + AlphaToCoverageSupport = true; // TODO: Check unknown +#if 0 + AlphaToCoverageSupport = (pID3D->CheckDeviceFormat(adapter, D3DDEVTYPE_HAL, + D3DFMT_X8R8G8B8, 0,D3DRTYPE_SURFACE, + (D3DFORMAT)MAKEFOURCC('A','2','M','1')) == S_OK); +#endif + + DriverAttributes->setAttribute("MaxTextures", (s32)MaxTextureUnits); + DriverAttributes->setAttribute("MaxSupportedTextures", (s32)Caps.MaxSimultaneousTextures); + DriverAttributes->setAttribute("MaxLights", (s32)Caps.MaxActiveLights); + DriverAttributes->setAttribute("MaxAnisotropy", (s32)Caps.MaxAnisotropy); + DriverAttributes->setAttribute("MaxUserClipPlanes", (s32)Caps.MaxUserClipPlanes); + DriverAttributes->setAttribute("MaxMultipleRenderTargets", (s32)Caps.NumSimultaneousRTs); + DriverAttributes->setAttribute("MaxIndices", (s32)Caps.MaxVertexIndex); + DriverAttributes->setAttribute("MaxTextureSize", (s32)core::min_(Caps.MaxTextureHeight,Caps.MaxTextureWidth)); + DriverAttributes->setAttribute("MaxTextureLODBias", 16); + DriverAttributes->setAttribute("Version", 901); + DriverAttributes->setAttribute("ShaderLanguageVersion", (s32)(((0x00ff00 & Caps.VertexShaderVersion)>>8)*100 + (Caps.VertexShaderVersion&0xff))); + DriverAttributes->setAttribute("AntiAlias", Params.AntiAlias); + + // set the renderstates + setRenderStates3DMode(); + + // store the screen's depth buffer + DepthBuffers.push_back(new SDepthSurface()); + if (SUCCEEDED(pID3DDevice->GetDepthStencilSurface(&(DepthBuffers[0]->Surface)))) + { + D3DSURFACE_DESC desc; + DepthBuffers[0]->Surface->GetDesc(&desc); + DepthBuffers[0]->Size.set(desc.Width, desc.Height); + } + else + { + os::Printer::log("Was not able to get main depth buffer.", ELL_ERROR); + return false; + } + + D3DColorFormat = D3DFMT_A8R8G8B8; + IDirect3DSurface9* bb=0; + if (SUCCEEDED(pID3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &bb))) + { + D3DSURFACE_DESC desc; + bb->GetDesc(&desc); + D3DColorFormat = desc.Format; + + if (D3DColorFormat == D3DFMT_X8R8G8B8) + D3DColorFormat = D3DFMT_A8R8G8B8; + + bb->Release(); + } + ColorFormat = getColorFormatFromD3DFormat(D3DColorFormat); + + #ifdef _IRR_COMPILE_WITH_CG_ + CgContext = cgCreateContext(); + cgD3D9SetDevice(pID3DDevice); + #endif + + // so far so good. + return true; +} + + +//! applications must call this method before performing any rendering. returns false if failed. +bool CD3D9Driver::beginScene(bool backBuffer, bool zBuffer, SColor color, + const SExposedVideoData& videoData, core::rect* sourceRect) +{ + CNullDriver::beginScene(backBuffer, zBuffer, color, videoData, sourceRect); + WindowId = (HWND)videoData.D3D9.HWnd; + SceneSourceRect = sourceRect; + + if (!pID3DDevice) + return false; + + HRESULT hr; + if (DeviceLost) + { + if (FAILED(hr = pID3DDevice->TestCooperativeLevel())) + { + if (hr == D3DERR_DEVICELOST) + { + Sleep(100); + hr = pID3DDevice->TestCooperativeLevel(); + if (hr == D3DERR_DEVICELOST) + return false; + } + + if ((hr == D3DERR_DEVICENOTRESET) && !reset()) + return false; + } + } + + DWORD flags = 0; + + if (backBuffer) + flags |= D3DCLEAR_TARGET; + + if (zBuffer) + flags |= D3DCLEAR_ZBUFFER; + + if (Params.Stencilbuffer) + flags |= D3DCLEAR_STENCIL; + + if (flags) + { + hr = pID3DDevice->Clear( 0, NULL, flags, color.color, 1.0, 0); + if (FAILED(hr)) + os::Printer::log("DIRECT3D9 clear failed.", ELL_WARNING); + } + + hr = pID3DDevice->BeginScene(); + if (FAILED(hr)) + { + os::Printer::log("DIRECT3D9 begin scene failed.", ELL_WARNING); + return false; + } + + return true; +} + + +//! applications must call this method after performing any rendering. returns false if failed. +bool CD3D9Driver::endScene() +{ + CNullDriver::endScene(); + DriverWasReset=false; + + HRESULT hr = pID3DDevice->EndScene(); + if (FAILED(hr)) + { + os::Printer::log("DIRECT3D9 end scene failed.", ELL_WARNING); + return false; + } + + RECT* srcRct = 0; + RECT sourceRectData; + if ( SceneSourceRect ) + { + srcRct = &sourceRectData; + sourceRectData.left = SceneSourceRect->UpperLeftCorner.X; + sourceRectData.top = SceneSourceRect->UpperLeftCorner.Y; + sourceRectData.right = SceneSourceRect->LowerRightCorner.X; + sourceRectData.bottom = SceneSourceRect->LowerRightCorner.Y; + } + + IDirect3DSwapChain9* swChain; + hr = pID3DDevice->GetSwapChain(0, &swChain); + DWORD flags = (Params.HandleSRGB && (Caps.Caps3&D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION))?D3DPRESENT_LINEAR_CONTENT:0; + hr = swChain->Present(srcRct, NULL, WindowId, NULL, flags); + swChain->Release(); + + if (SUCCEEDED(hr)) + return true; + + if (hr == D3DERR_DEVICELOST) + { + DeviceLost = true; + os::Printer::log("Present failed", "DIRECT3D9 device lost.", ELL_WARNING); + } +#ifdef D3DERR_DEVICEREMOVED + else if (hr == D3DERR_DEVICEREMOVED) + { + os::Printer::log("Present failed", "Device removed.", ELL_WARNING); + } +#endif + else if (hr == D3DERR_INVALIDCALL) + { + os::Printer::log("Present failed", "Invalid Call", ELL_WARNING); + } + else + os::Printer::log("DIRECT3D9 present failed.", ELL_WARNING); + return false; +} + + +//! queries the features of the driver, returns true if feature is available +bool CD3D9Driver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const +{ + if (!FeatureEnabled[feature]) + return false; + + switch (feature) + { + case EVDF_MULTITEXTURE: + case EVDF_BILINEAR_FILTER: + return true; + case EVDF_RENDER_TO_TARGET: + return Caps.NumSimultaneousRTs > 0; + case EVDF_HARDWARE_TL: + return (Caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0; + case EVDF_MIP_MAP: + return (Caps.TextureCaps & D3DPTEXTURECAPS_MIPMAP) != 0; + case EVDF_MIP_MAP_AUTO_UPDATE: + // always return false because a lot of drivers claim they do + // this but actually don't do this at all. + return false; //(Caps.Caps2 & D3DCAPS2_CANAUTOGENMIPMAP) != 0; + case EVDF_STENCIL_BUFFER: + return Params.Stencilbuffer && Caps.StencilCaps; + case EVDF_VERTEX_SHADER_1_1: + return Caps.VertexShaderVersion >= D3DVS_VERSION(1,1); + case EVDF_VERTEX_SHADER_2_0: + return Caps.VertexShaderVersion >= D3DVS_VERSION(2,0); + case EVDF_VERTEX_SHADER_3_0: + return Caps.VertexShaderVersion >= D3DVS_VERSION(3,0); + case EVDF_PIXEL_SHADER_1_1: + return Caps.PixelShaderVersion >= D3DPS_VERSION(1,1); + case EVDF_PIXEL_SHADER_1_2: + return Caps.PixelShaderVersion >= D3DPS_VERSION(1,2); + case EVDF_PIXEL_SHADER_1_3: + return Caps.PixelShaderVersion >= D3DPS_VERSION(1,3); + case EVDF_PIXEL_SHADER_1_4: + return Caps.PixelShaderVersion >= D3DPS_VERSION(1,4); + case EVDF_PIXEL_SHADER_2_0: + return Caps.PixelShaderVersion >= D3DPS_VERSION(2,0); + case EVDF_PIXEL_SHADER_3_0: + return Caps.PixelShaderVersion >= D3DPS_VERSION(3,0); + case EVDF_HLSL: + return Caps.VertexShaderVersion >= D3DVS_VERSION(1,1); + case EVDF_TEXTURE_NSQUARE: + return (Caps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY) == 0; + case EVDF_TEXTURE_NPOT: + return (Caps.TextureCaps & D3DPTEXTURECAPS_POW2) == 0; + case EVDF_COLOR_MASK: + return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE) != 0; + case EVDF_MULTIPLE_RENDER_TARGETS: + return Caps.NumSimultaneousRTs > 1; + case EVDF_MRT_COLOR_MASK: + return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_INDEPENDENTWRITEMASKS) != 0; + case EVDF_MRT_BLEND: + return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING) != 0; + case EVDF_OCCLUSION_QUERY: + return OcclusionQuerySupport; + case EVDF_POLYGON_OFFSET: + return (Caps.RasterCaps & (D3DPRASTERCAPS_DEPTHBIAS|D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS)) != 0; + case EVDF_BLEND_OPERATIONS: + case EVDF_TEXTURE_MATRIX: +#ifdef _IRR_COMPILE_WITH_CG_ + // available iff. define is present + case EVDF_CG: +#endif + return true; + default: + return false; + }; +} + + +//! sets transformation +void CD3D9Driver::setTransform(E_TRANSFORMATION_STATE state, + const core::matrix4& mat) +{ + Transformation3DChanged = true; + + switch(state) + { + case ETS_VIEW: + pID3DDevice->SetTransform(D3DTS_VIEW, (D3DMATRIX*)((void*)mat.pointer())); + break; + case ETS_WORLD: + pID3DDevice->SetTransform(D3DTS_WORLD, (D3DMATRIX*)((void*)mat.pointer())); + break; + case ETS_PROJECTION: + pID3DDevice->SetTransform( D3DTS_PROJECTION, (D3DMATRIX*)((void*)mat.pointer())); + break; + case ETS_COUNT: + return; + default: + if (state-ETS_TEXTURE_0 < MATERIAL_MAX_TEXTURES) + { + if (mat.isIdentity()) + pID3DDevice->SetTextureStageState( state - ETS_TEXTURE_0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); + else + { + pID3DDevice->SetTextureStageState( state - ETS_TEXTURE_0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); + pID3DDevice->SetTransform((D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0+ ( state - ETS_TEXTURE_0 )), + (D3DMATRIX*)((void*)mat.pointer())); + } + } + break; + } + + Matrices[state] = mat; +} + + +//! sets the current Texture +bool CD3D9Driver::setActiveTexture(u32 stage, const video::ITexture* texture) +{ + if (CurrentTexture[stage] == texture) + return true; + + if (texture && texture->getDriverType() != EDT_DIRECT3D9) + { + os::Printer::log("Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR); + return false; + } + + CurrentTexture[stage] = texture; + + if (!texture) + { + pID3DDevice->SetTexture(stage, 0); + pID3DDevice->SetTextureStageState( stage, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); + } + else + { + pID3DDevice->SetTexture(stage, ((const CD3D9Texture*)texture)->getDX9Texture()); + } + return true; +} + + +//! sets a material +void CD3D9Driver::setMaterial(const SMaterial& material) +{ + Material = material; + OverrideMaterial.apply(Material); + + for (u32 i=0; igetDriverType() != EDT_DIRECT3D9) + { + os::Printer::log("Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR); + return false; + } + + // check for valid render target + + if (texture && !texture->isRenderTarget()) + { + os::Printer::log("Fatal Error: Tried to set a non render target texture as render target.", ELL_ERROR); + return false; + } + + CD3D9Texture* tex = static_cast(texture); + + // check if we should set the previous RT back + + bool ret = true; + + for(u32 i = 1; i < NumSetMRTs; i++) + { + // First texture handled elsewhere + pID3DDevice->SetRenderTarget(i, NULL); + } + if (tex == 0) + { + if (PrevRenderTarget) + { + if (FAILED(pID3DDevice->SetRenderTarget(0, PrevRenderTarget))) + { + os::Printer::log("Error: Could not set back to previous render target.", ELL_ERROR); + ret = false; + } + if (FAILED(pID3DDevice->SetDepthStencilSurface(DepthBuffers[0]->Surface))) + { + os::Printer::log("Error: Could not set main depth buffer.", ELL_ERROR); + } + + CurrentRendertargetSize = core::dimension2d(0,0); + PrevRenderTarget->Release(); + PrevRenderTarget = 0; + } + } + else + { + // we want to set a new target. so do this. + + // store previous target + + if (!PrevRenderTarget) + { + if (FAILED(pID3DDevice->GetRenderTarget(0, &PrevRenderTarget))) + { + os::Printer::log("Could not get previous render target.", ELL_ERROR); + return false; + } + } + + // set new render target + + if (FAILED(pID3DDevice->SetRenderTarget(0, tex->getRenderTargetSurface()))) + { + os::Printer::log("Error: Could not set render target.", ELL_ERROR); + return false; + } + CurrentRendertargetSize = tex->getSize(); + + if (FAILED(pID3DDevice->SetDepthStencilSurface(tex->DepthSurface->Surface))) + { + os::Printer::log("Error: Could not set new depth buffer.", ELL_ERROR); + } + } + Transformation3DChanged=true; + + if (clearBackBuffer || clearZBuffer) + { + DWORD flags = 0; + + if (clearBackBuffer) + flags |= D3DCLEAR_TARGET; + + if (clearZBuffer) + flags |= D3DCLEAR_ZBUFFER; + + pID3DDevice->Clear(0, NULL, flags, color.color, 1.0f, 0); + } + + return ret; +} + + +//! Sets multiple render targets +bool CD3D9Driver::setRenderTarget(const core::array& targets, + bool clearBackBuffer, bool clearZBuffer, SColor color) +{ + if (targets.size()==0) + return setRenderTarget(0, clearBackBuffer, clearZBuffer, color); + + u32 maxMultipleRTTs = core::min_(MaxMRTs, targets.size()); + + for (u32 i = 0; i < maxMultipleRTTs; ++i) + { + if (targets[i].TargetType != ERT_RENDER_TEXTURE || !targets[i].RenderTexture) + { + maxMultipleRTTs = i; + os::Printer::log("Missing texture for MRT.", ELL_WARNING); + break; + } + + // check for right driver type + + if (targets[i].RenderTexture->getDriverType() != EDT_DIRECT3D9) + { + maxMultipleRTTs = i; + os::Printer::log("Tried to set a texture not owned by this driver.", ELL_WARNING); + break; + } + + // check for valid render target + + if (!targets[i].RenderTexture->isRenderTarget()) + { + maxMultipleRTTs = i; + os::Printer::log("Tried to set a non render target texture as render target.", ELL_WARNING); + break; + } + + // check for valid size + + if (targets[0].RenderTexture->getSize() != targets[i].RenderTexture->getSize()) + { + maxMultipleRTTs = i; + os::Printer::log("Render target texture has wrong size.", ELL_WARNING); + break; + } + } + if (maxMultipleRTTs==0) + { + os::Printer::log("Fatal Error: No valid MRT found.", ELL_ERROR); + return false; + } + + CD3D9Texture* tex = static_cast(targets[0].RenderTexture); + + // check if we should set the previous RT back + + bool ret = true; + + // we want to set a new target. so do this. + // store previous target + + if (!PrevRenderTarget) + { + if (FAILED(pID3DDevice->GetRenderTarget(0, &PrevRenderTarget))) + { + os::Printer::log("Could not get previous render target.", ELL_ERROR); + return false; + } + } + + // set new render target + + // In d3d9 we have at most 4 MRTs, so the following is enough + D3DRENDERSTATETYPE colorWrite[4]={D3DRS_COLORWRITEENABLE, D3DRS_COLORWRITEENABLE1, D3DRS_COLORWRITEENABLE2, D3DRS_COLORWRITEENABLE3}; + for (u32 i = 0; i < maxMultipleRTTs; ++i) + { + if (FAILED(pID3DDevice->SetRenderTarget(i, static_cast(targets[i].RenderTexture)->getRenderTargetSurface()))) + { + os::Printer::log("Error: Could not set render target.", ELL_ERROR); + return false; + } + if (i<4 && (i==0 || queryFeature(EVDF_MRT_COLOR_MASK))) + { + const DWORD flag = + ((targets[i].ColorMask & ECP_RED)?D3DCOLORWRITEENABLE_RED:0) | + ((targets[i].ColorMask & ECP_GREEN)?D3DCOLORWRITEENABLE_GREEN:0) | + ((targets[i].ColorMask & ECP_BLUE)?D3DCOLORWRITEENABLE_BLUE:0) | + ((targets[i].ColorMask & ECP_ALPHA)?D3DCOLORWRITEENABLE_ALPHA:0); + pID3DDevice->SetRenderState(colorWrite[i], flag); + } + } + for(u32 i = maxMultipleRTTs; i < NumSetMRTs; i++) + { + pID3DDevice->SetRenderTarget(i, NULL); + } + NumSetMRTs=maxMultipleRTTs; + + CurrentRendertargetSize = tex->getSize(); + + if (FAILED(pID3DDevice->SetDepthStencilSurface(tex->DepthSurface->Surface))) + { + os::Printer::log("Error: Could not set new depth buffer.", ELL_ERROR); + } + + if (clearBackBuffer || clearZBuffer) + { + DWORD flags = 0; + + if (clearBackBuffer) + flags |= D3DCLEAR_TARGET; + + if (clearZBuffer) + flags |= D3DCLEAR_ZBUFFER; + + pID3DDevice->Clear(0, NULL, flags, color.color, 1.0f, 0); + } + + return ret; +} + + +//! sets a viewport +void CD3D9Driver::setViewPort(const core::rect& area) +{ + core::rect vp = area; + core::rect rendert(0,0, getCurrentRenderTargetSize().Width, getCurrentRenderTargetSize().Height); + vp.clipAgainst(rendert); + if (vp.getHeight()>0 && vp.getWidth()>0) + { + D3DVIEWPORT9 viewPort; + viewPort.X = vp.UpperLeftCorner.X; + viewPort.Y = vp.UpperLeftCorner.Y; + viewPort.Width = vp.getWidth(); + viewPort.Height = vp.getHeight(); + viewPort.MinZ = 0.0f; + viewPort.MaxZ = 1.0f; + + HRESULT hr = pID3DDevice->SetViewport(&viewPort); + if (FAILED(hr)) + os::Printer::log("Failed setting the viewport.", ELL_WARNING); + else + ViewPort = vp; + } +} + + +//! gets the area of the current viewport +const core::rect& CD3D9Driver::getViewPort() const +{ + return ViewPort; +} + + +bool CD3D9Driver::updateVertexHardwareBuffer(SHWBufferLink_d3d9 *hwBuffer) +{ + if (!hwBuffer) + return false; + + const scene::IMeshBuffer* mb = hwBuffer->MeshBuffer; + const void* vertices=mb->getVertices(); + const u32 vertexCount=mb->getVertexCount(); + const E_VERTEX_TYPE vType=mb->getVertexType(); + const u32 vertexSize = getVertexPitchFromType(vType); + const u32 bufSize = vertexSize * vertexCount; + + if (!hwBuffer->vertexBuffer || (bufSize > hwBuffer->vertexBufferSize)) + { + if (hwBuffer->vertexBuffer) + { + hwBuffer->vertexBuffer->Release(); + hwBuffer->vertexBuffer=0; + } + + DWORD FVF; + // Get the vertex sizes and cvf + switch (vType) + { + case EVT_STANDARD: + FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1; + break; + case EVT_2TCOORDS: + FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2; + break; + case EVT_TANGENTS: + FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX3; + break; + default: + return false; + } + + DWORD flags = D3DUSAGE_WRITEONLY; // SIO2: Default to D3DUSAGE_WRITEONLY + if (hwBuffer->Mapped_Vertex != scene::EHM_STATIC) + flags |= D3DUSAGE_DYNAMIC; + + if (FAILED(pID3DDevice->CreateVertexBuffer(bufSize, flags, FVF, D3DPOOL_DEFAULT, &hwBuffer->vertexBuffer, NULL))) + return false; + hwBuffer->vertexBufferSize = bufSize; + + flags = 0; // SIO2: Reset flags before Lock + if (hwBuffer->Mapped_Vertex != scene::EHM_STATIC) + flags = D3DLOCK_DISCARD; + + void* lockedBuffer = 0; + hwBuffer->vertexBuffer->Lock(0, bufSize, (void**)&lockedBuffer, flags); + memcpy(lockedBuffer, vertices, bufSize); + hwBuffer->vertexBuffer->Unlock(); + } + else + { + void* lockedBuffer = 0; + hwBuffer->vertexBuffer->Lock(0, bufSize, (void**)&lockedBuffer, D3DLOCK_DISCARD); + memcpy(lockedBuffer, vertices, bufSize); + hwBuffer->vertexBuffer->Unlock(); + } + + return true; +} + + +bool CD3D9Driver::updateIndexHardwareBuffer(SHWBufferLink_d3d9 *hwBuffer) +{ + if (!hwBuffer) + return false; + + const scene::IMeshBuffer* mb = hwBuffer->MeshBuffer; + const u16* indices=mb->getIndices(); + const u32 indexCount=mb->getIndexCount(); + u32 indexSize = 2; + D3DFORMAT indexType=D3DFMT_UNKNOWN; + switch (mb->getIndexType()) + { + case EIT_16BIT: + { + indexType=D3DFMT_INDEX16; + indexSize = 2; + break; + } + case EIT_32BIT: + { + indexType=D3DFMT_INDEX32; + indexSize = 4; + break; + } + } + + const u32 bufSize = indexSize * indexCount; + if (!hwBuffer->indexBuffer || (bufSize > hwBuffer->indexBufferSize)) + { + if (hwBuffer->indexBuffer) + { + hwBuffer->indexBuffer->Release(); + hwBuffer->indexBuffer=0; + } + + DWORD flags = D3DUSAGE_WRITEONLY; // SIO2: Default to D3DUSAGE_WRITEONLY + if (hwBuffer->Mapped_Index != scene::EHM_STATIC) + flags |= D3DUSAGE_DYNAMIC; // SIO2: Add DYNAMIC flag for dynamic buffer data + + if (FAILED(pID3DDevice->CreateIndexBuffer(bufSize, flags, indexType, D3DPOOL_DEFAULT, &hwBuffer->indexBuffer, NULL))) + return false; + + flags = 0; // SIO2: Reset flags before Lock + if (hwBuffer->Mapped_Index != scene::EHM_STATIC) + flags = D3DLOCK_DISCARD; + + void* lockedBuffer = 0; + if (FAILED(hwBuffer->indexBuffer->Lock( 0, 0, (void**)&lockedBuffer, flags))) + return false; + + memcpy(lockedBuffer, indices, bufSize); + hwBuffer->indexBuffer->Unlock(); + + hwBuffer->indexBufferSize = bufSize; + } + else + { + void* lockedBuffer = 0; + if( SUCCEEDED(hwBuffer->indexBuffer->Lock( 0, 0, (void**)&lockedBuffer, D3DLOCK_DISCARD))) + { + memcpy(lockedBuffer, indices, bufSize); + hwBuffer->indexBuffer->Unlock(); + } + } + + return true; +} + + +//! updates hardware buffer if needed +bool CD3D9Driver::updateHardwareBuffer(SHWBufferLink *hwBuffer) +{ + if (!hwBuffer) + return false; + + if (hwBuffer->Mapped_Vertex!=scene::EHM_NEVER) + { + if (hwBuffer->ChangedID_Vertex != hwBuffer->MeshBuffer->getChangedID_Vertex() + || !((SHWBufferLink_d3d9*)hwBuffer)->vertexBuffer) + { + hwBuffer->ChangedID_Vertex = hwBuffer->MeshBuffer->getChangedID_Vertex(); + + if (!updateVertexHardwareBuffer((SHWBufferLink_d3d9*)hwBuffer)) + return false; + } + } + + if (hwBuffer->Mapped_Index!=scene::EHM_NEVER) + { + if (hwBuffer->ChangedID_Index != hwBuffer->MeshBuffer->getChangedID_Index() + || !((SHWBufferLink_d3d9*)hwBuffer)->indexBuffer) + { + hwBuffer->ChangedID_Index = hwBuffer->MeshBuffer->getChangedID_Index(); + + if (!updateIndexHardwareBuffer((SHWBufferLink_d3d9*)hwBuffer)) + return false; + } + } + + return true; +} + + +//! Create hardware buffer from meshbuffer +CD3D9Driver::SHWBufferLink *CD3D9Driver::createHardwareBuffer(const scene::IMeshBuffer* mb) +{ + // Looks like d3d does not support only partial buffering, so refuse + // in any case of NEVER + if (!mb || (mb->getHardwareMappingHint_Index()==scene::EHM_NEVER || mb->getHardwareMappingHint_Vertex()==scene::EHM_NEVER)) + return 0; + + SHWBufferLink_d3d9 *hwBuffer=new SHWBufferLink_d3d9(mb); + + //add to map + HWBufferMap.insert(hwBuffer->MeshBuffer, hwBuffer); + + hwBuffer->ChangedID_Vertex=hwBuffer->MeshBuffer->getChangedID_Vertex(); + hwBuffer->ChangedID_Index=hwBuffer->MeshBuffer->getChangedID_Index(); + hwBuffer->Mapped_Vertex=mb->getHardwareMappingHint_Vertex(); + hwBuffer->Mapped_Index=mb->getHardwareMappingHint_Index(); + hwBuffer->LastUsed=0; + hwBuffer->vertexBuffer=0; + hwBuffer->indexBuffer=0; + hwBuffer->vertexBufferSize=0; + hwBuffer->indexBufferSize=0; + + if (!updateHardwareBuffer(hwBuffer)) + { + deleteHardwareBuffer(hwBuffer); + return 0; + } + + return hwBuffer; +} + + +void CD3D9Driver::deleteHardwareBuffer(SHWBufferLink *_HWBuffer) +{ + if (!_HWBuffer) + return; + + SHWBufferLink_d3d9 *HWBuffer=(SHWBufferLink_d3d9*)_HWBuffer; + if (HWBuffer->indexBuffer) + { + HWBuffer->indexBuffer->Release(); + HWBuffer->indexBuffer = 0; + } + + if (HWBuffer->vertexBuffer) + { + HWBuffer->vertexBuffer->Release(); + HWBuffer->vertexBuffer = 0; + } + + CNullDriver::deleteHardwareBuffer(_HWBuffer); +} + + +//! Draw hardware buffer +void CD3D9Driver::drawHardwareBuffer(SHWBufferLink *_HWBuffer) +{ + if (!_HWBuffer) + return; + + SHWBufferLink_d3d9 *HWBuffer=(SHWBufferLink_d3d9*)_HWBuffer; + + updateHardwareBuffer(HWBuffer); //check if update is needed + + HWBuffer->LastUsed=0;//reset count + + const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer; + const E_VERTEX_TYPE vType = mb->getVertexType(); + const u32 stride = getVertexPitchFromType(vType); + const void* vPtr = mb->getVertices(); + const void* iPtr = mb->getIndices(); + if (HWBuffer->vertexBuffer) + { + pID3DDevice->SetStreamSource(0, HWBuffer->vertexBuffer, 0, stride); + vPtr=0; + } + if (HWBuffer->indexBuffer) + { + pID3DDevice->SetIndices(HWBuffer->indexBuffer); + iPtr=0; + } + + drawVertexPrimitiveList(vPtr, mb->getVertexCount(), iPtr, mb->getIndexCount()/3, mb->getVertexType(), scene::EPT_TRIANGLES, mb->getIndexType()); + + if (HWBuffer->vertexBuffer) + pID3DDevice->SetStreamSource(0, 0, 0, 0); + if (HWBuffer->indexBuffer) + pID3DDevice->SetIndices(0); +} + + +//! Create occlusion query. +/** Use node for identification and mesh for occlusion test. */ +void CD3D9Driver::addOcclusionQuery(scene::ISceneNode* node, + const scene::IMesh* mesh) +{ + if (!queryFeature(EVDF_OCCLUSION_QUERY)) + return; + CNullDriver::addOcclusionQuery(node, mesh); + const s32 index = OcclusionQueries.linear_search(SOccQuery(node)); + if ((index != -1) && (OcclusionQueries[index].PID == 0)) + pID3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, reinterpret_cast(&OcclusionQueries[index].PID)); +} + + +//! Remove occlusion query. +void CD3D9Driver::removeOcclusionQuery(scene::ISceneNode* node) +{ + const s32 index = OcclusionQueries.linear_search(SOccQuery(node)); + if (index != -1) + { + if (OcclusionQueries[index].PID != 0) + reinterpret_cast(OcclusionQueries[index].PID)->Release(); + CNullDriver::removeOcclusionQuery(node); + } +} + + +//! Run occlusion query. Draws mesh stored in query. +/** If the mesh shall not be rendered visible, use +overrideMaterial to disable the color and depth buffer. */ +void CD3D9Driver::runOcclusionQuery(scene::ISceneNode* node, bool visible) +{ + if (!node) + return; + + const s32 index = OcclusionQueries.linear_search(SOccQuery(node)); + if (index != -1) + { + if (OcclusionQueries[index].PID) + reinterpret_cast(OcclusionQueries[index].PID)->Issue(D3DISSUE_BEGIN); + CNullDriver::runOcclusionQuery(node,visible); + if (OcclusionQueries[index].PID) + reinterpret_cast(OcclusionQueries[index].PID)->Issue(D3DISSUE_END); + } +} + + +//! Update occlusion query. Retrieves results from GPU. +/** If the query shall not block, set the flag to false. +Update might not occur in this case, though */ +void CD3D9Driver::updateOcclusionQuery(scene::ISceneNode* node, bool block) +{ + const s32 index = OcclusionQueries.linear_search(SOccQuery(node)); + if (index != -1) + { + // not yet started + if (OcclusionQueries[index].Run==u32(~0)) + return; + bool available = block?true:false; + int tmp=0; + if (!block) + available=(reinterpret_cast(OcclusionQueries[index].PID)->GetData(&tmp, sizeof(DWORD), 0)==S_OK); + else + { + do + { + HRESULT hr = reinterpret_cast(OcclusionQueries[index].PID)->GetData(&tmp, sizeof(DWORD), D3DGETDATA_FLUSH); + available = (hr == S_OK); + if (hr!=S_FALSE) + break; + } while (!available); + } + if (available) + OcclusionQueries[index].Result = tmp; + } +} + + +//! Return query result. +/** Return value is the number of visible pixels/fragments. +The value is a safe approximation, i.e. can be larger than the +actual value of pixels. */ +u32 CD3D9Driver::getOcclusionQueryResult(scene::ISceneNode* node) const +{ + const s32 index = OcclusionQueries.linear_search(SOccQuery(node)); + if (index != -1) + return OcclusionQueries[index].Result; + else + return ~0; +} + + +//! draws a vertex primitive list +void CD3D9Driver::drawVertexPrimitiveList(const void* vertices, + u32 vertexCount, const void* indexList, u32 primitiveCount, + E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, + E_INDEX_TYPE iType) +{ + if (!checkPrimitiveCount(primitiveCount)) + return; + + CNullDriver::drawVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType,iType); + + if (!vertexCount || !primitiveCount) + return; + + draw2D3DVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, + vType, pType, iType, true); +} + + +//! draws a vertex primitive list +void CD3D9Driver::draw2DVertexPrimitiveList(const void* vertices, + u32 vertexCount, const void* indexList, u32 primitiveCount, + E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, + E_INDEX_TYPE iType) +{ + if (!checkPrimitiveCount(primitiveCount)) + return; + + CNullDriver::draw2DVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType,iType); + + if (!vertexCount || !primitiveCount) + return; + + draw2D3DVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, + vType, pType, iType, false); +} + + +void CD3D9Driver::draw2D3DVertexPrimitiveList(const void* vertices, + u32 vertexCount, const void* indexList, u32 primitiveCount, + E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, + E_INDEX_TYPE iType, bool is3D) +{ + setVertexShader(vType); + + const u32 stride = getVertexPitchFromType(vType); + + D3DFORMAT indexType=D3DFMT_UNKNOWN; + switch (iType) + { + case (EIT_16BIT): + { + indexType=D3DFMT_INDEX16; + break; + } + case (EIT_32BIT): + { + indexType=D3DFMT_INDEX32; + break; + } + } + + if (is3D) + { + if (!setRenderStates3DMode()) + return; + } + else + { + if (Material.MaterialType==EMT_ONETEXTURE_BLEND) + { + E_BLEND_FACTOR srcFact; + E_BLEND_FACTOR dstFact; + E_MODULATE_FUNC modulo; + u32 alphaSource; + unpack_textureBlendFunc ( srcFact, dstFact, modulo, alphaSource, Material.MaterialTypeParam); + setRenderStates2DMode(alphaSource&video::EAS_VERTEX_COLOR, (Material.getTexture(0) != 0), (alphaSource&video::EAS_TEXTURE) != 0); + } + else + setRenderStates2DMode(Material.MaterialType==EMT_TRANSPARENT_VERTEX_ALPHA, (Material.getTexture(0) != 0), Material.MaterialType==EMT_TRANSPARENT_ALPHA_CHANNEL); + } + + switch (pType) + { + case scene::EPT_POINT_SPRITES: + case scene::EPT_POINTS: + { + f32 tmp=Material.Thickness/getScreenSize().Height; + if (pType==scene::EPT_POINT_SPRITES) + pID3DDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_POINTSIZE, F2DW(tmp)); + tmp=1.0f; + pID3DDevice->SetRenderState(D3DRS_POINTSCALE_A, F2DW(tmp)); + pID3DDevice->SetRenderState(D3DRS_POINTSCALE_B, F2DW(tmp)); + pID3DDevice->SetRenderState(D3DRS_POINTSIZE_MIN, F2DW(tmp)); + tmp=0.0f; + pID3DDevice->SetRenderState(D3DRS_POINTSCALE_C, F2DW(tmp)); + + if (!vertices) + { + pID3DDevice->DrawIndexedPrimitive(D3DPT_POINTLIST, 0, 0, vertexCount, 0, primitiveCount); + } + else + { + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_POINTLIST, 0, vertexCount, + primitiveCount, indexList, indexType, vertices, stride); + } + + pID3DDevice->SetRenderState(D3DRS_POINTSCALEENABLE, FALSE); + if (pType==scene::EPT_POINT_SPRITES) + pID3DDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, FALSE); + } + break; + case scene::EPT_LINE_STRIP: + if(!vertices) + pID3DDevice->DrawIndexedPrimitive(D3DPT_LINESTRIP, 0, 0, vertexCount, 0, primitiveCount); + else + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_LINESTRIP, 0, vertexCount, + primitiveCount, indexList, indexType, vertices, stride); + break; + case scene::EPT_LINE_LOOP: + if(!vertices) + { + // TODO: Implement proper hardware support for this primitive type. + // (No looping occurs currently because this would require a way to + // draw the hardware buffer with a custom set of indices. We may even + // need to create a new mini index buffer specifically for this + // primitive type.) + pID3DDevice->DrawIndexedPrimitive(D3DPT_LINELIST, 0, 0, vertexCount, 0, primitiveCount); + } + else + { + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_LINESTRIP, 0, vertexCount, + primitiveCount - 1, indexList, indexType, vertices, stride); + + u16 tmpIndices[] = {static_cast(primitiveCount - 1), 0}; + + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, vertexCount, + 1, tmpIndices, indexType, vertices, stride); + } + break; + case scene::EPT_LINES: + if(!vertices) + pID3DDevice->DrawIndexedPrimitive(D3DPT_LINELIST, 0, 0, vertexCount, 0, primitiveCount); + else + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, vertexCount, + primitiveCount, indexList, indexType, vertices, stride); + break; + case scene::EPT_TRIANGLE_STRIP: + if(!vertices) + pID3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, vertexCount, 0, primitiveCount); + else + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLESTRIP, 0, vertexCount, primitiveCount, + indexList, indexType, vertices, stride); + break; + case scene::EPT_TRIANGLE_FAN: + if(!vertices) + pID3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLEFAN, 0, 0, vertexCount, 0, primitiveCount); + else + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLEFAN, 0, vertexCount, primitiveCount, + indexList, indexType, vertices, stride); + break; + case scene::EPT_TRIANGLES: + if(!vertices) + { + pID3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, vertexCount, 0, primitiveCount); + } + else + { + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, vertexCount, + primitiveCount, indexList, indexType, vertices, stride); + } + break; + } +} + + +void CD3D9Driver::draw2DImage(const video::ITexture* texture, + const core::rect& destRect, + const core::rect& sourceRect, + const core::rect* clipRect, + const video::SColor* const colors, + bool useAlphaChannelOfTexture) +{ + if(!texture) + return; + + const core::dimension2d& ss = texture->getOriginalSize(); + core::rect tcoords; + tcoords.UpperLeftCorner.X = (f32)sourceRect.UpperLeftCorner.X / (f32)ss.Width; + tcoords.UpperLeftCorner.Y = (f32)sourceRect.UpperLeftCorner.Y / (f32)ss.Height; + tcoords.LowerRightCorner.X = (f32)sourceRect.LowerRightCorner.X / (f32)ss.Width; + tcoords.LowerRightCorner.Y = (f32)sourceRect.LowerRightCorner.Y / (f32)ss.Height; + + const core::dimension2d& renderTargetSize = getCurrentRenderTargetSize(); + + const video::SColor temp[4] = + { + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF + }; + + const video::SColor* const useColor = colors ? colors : temp; + + S3DVertex vtx[4]; // clock wise + vtx[0] = S3DVertex((f32)destRect.UpperLeftCorner.X, (f32)destRect.UpperLeftCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, useColor[0], + tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y); + vtx[1] = S3DVertex((f32)destRect.LowerRightCorner.X, (f32)destRect.UpperLeftCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, useColor[3], + tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y); + vtx[2] = S3DVertex((f32)destRect.LowerRightCorner.X, (f32)destRect.LowerRightCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, useColor[2], + tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y); + vtx[3] = S3DVertex((f32)destRect.UpperLeftCorner.X, (f32)destRect.LowerRightCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, useColor[1], + tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y); + + s16 indices[6] = {0,1,2,0,2,3}; + + setActiveTexture(0, const_cast(texture)); + + setRenderStates2DMode(useColor[0].getAlpha()<255 || useColor[1].getAlpha()<255 || + useColor[2].getAlpha()<255 || useColor[3].getAlpha()<255, + true, useAlphaChannelOfTexture); + + setVertexShader(EVT_STANDARD); + + if (clipRect) + { + pID3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + RECT scissor; + scissor.left = clipRect->UpperLeftCorner.X; + scissor.top = clipRect->UpperLeftCorner.Y; + scissor.right = clipRect->LowerRightCorner.X; + scissor.bottom = clipRect->LowerRightCorner.Y; + pID3DDevice->SetScissorRect(&scissor); + } + + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &indices[0], + D3DFMT_INDEX16,&vtx[0], sizeof(S3DVertex)); + + if (clipRect) + pID3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); +} + + +void CD3D9Driver::draw2DImageBatch(const video::ITexture* texture, + const core::array >& positions, + const core::array >& sourceRects, + const core::rect* clipRect, + SColor color, + bool useAlphaChannelOfTexture) +{ + if (!texture) + return; + + if (!setActiveTexture(0, const_cast(texture))) + return; + + setRenderStates2DMode(color.getAlpha()<255, true, useAlphaChannelOfTexture); + + const irr::u32 drawCount = core::min_(positions.size(), sourceRects.size()); + + core::array vtx(drawCount * 4); + core::array indices(drawCount * 6); + + for(u32 i = 0;i < drawCount;i++) + { + core::position2d targetPos = positions[i]; + core::position2d sourcePos = sourceRects[i].UpperLeftCorner; + // This needs to be signed as it may go negative. + core::dimension2d sourceSize(sourceRects[i].getSize()); + + if (clipRect) + { + if (targetPos.X < clipRect->UpperLeftCorner.X) + { + sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X; + if (sourceSize.Width <= 0) + continue; + + sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X; + targetPos.X = clipRect->UpperLeftCorner.X; + } + + if (targetPos.X + (s32)sourceSize.Width > clipRect->LowerRightCorner.X) + { + sourceSize.Width -= (targetPos.X + sourceSize.Width) - clipRect->LowerRightCorner.X; + if (sourceSize.Width <= 0) + continue; + } + + if (targetPos.Y < clipRect->UpperLeftCorner.Y) + { + sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y; + if (sourceSize.Height <= 0) + continue; + + sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y; + targetPos.Y = clipRect->UpperLeftCorner.Y; + } + + if (targetPos.Y + (s32)sourceSize.Height > clipRect->LowerRightCorner.Y) + { + sourceSize.Height -= (targetPos.Y + sourceSize.Height) - clipRect->LowerRightCorner.Y; + if (sourceSize.Height <= 0) + continue; + } + } + + // clip these coordinates + + if (targetPos.X<0) + { + sourceSize.Width += targetPos.X; + if (sourceSize.Width <= 0) + continue; + + sourcePos.X -= targetPos.X; + targetPos.X = 0; + } + + const core::dimension2d& renderTargetSize = getCurrentRenderTargetSize(); + + if (targetPos.X + sourceSize.Width > (s32)renderTargetSize.Width) + { + sourceSize.Width -= (targetPos.X + sourceSize.Width) - renderTargetSize.Width; + if (sourceSize.Width <= 0) + continue; + } + + if (targetPos.Y<0) + { + sourceSize.Height += targetPos.Y; + if (sourceSize.Height <= 0) + continue; + + sourcePos.Y -= targetPos.Y; + targetPos.Y = 0; + } + + if (targetPos.Y + sourceSize.Height > (s32)renderTargetSize.Height) + { + sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height; + if (sourceSize.Height <= 0) + continue; + } + + // ok, we've clipped everything. + // now draw it. + + core::rect tcoords; + tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getOriginalSize().Width ; + tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getOriginalSize().Height; + tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getOriginalSize().Width); + tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getOriginalSize().Height); + + const core::rect poss(targetPos, sourceSize); + + vtx.push_back(S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, color, + tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y)); + vtx.push_back(S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, color, + tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y)); + vtx.push_back(S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, color, + tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y)); + vtx.push_back(S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, color, + tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y)); + + const u32 curPos = vtx.size()-4; + indices.push_back(0+curPos); + indices.push_back(1+curPos); + indices.push_back(2+curPos); + + indices.push_back(0+curPos); + indices.push_back(2+curPos); + indices.push_back(3+curPos); + } + + if (vtx.size()) + { + setVertexShader(EVT_STANDARD); + + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, vtx.size(), indices.size() / 3, indices.pointer(), + D3DFMT_INDEX16,vtx.pointer(), sizeof(S3DVertex)); + } +} + + +//! draws a 2d image, using a color and the alpha channel of the texture if +//! desired. The image is drawn at pos and clipped against clipRect (if != 0). +void CD3D9Driver::draw2DImage(const video::ITexture* texture, + const core::position2d& pos, + const core::rect& sourceRect, + const core::rect* clipRect, SColor color, + bool useAlphaChannelOfTexture) +{ + if (!texture) + return; + + if (!sourceRect.isValid()) + return; + + if (!setActiveTexture(0, const_cast(texture))) + return; + + core::position2d targetPos = pos; + core::position2d sourcePos = sourceRect.UpperLeftCorner; + // This needs to be signed as it may go negative. + core::dimension2d sourceSize(sourceRect.getSize()); + + if (clipRect) + { + if (targetPos.X < clipRect->UpperLeftCorner.X) + { + sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X; + if (sourceSize.Width <= 0) + return; + + sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X; + targetPos.X = clipRect->UpperLeftCorner.X; + } + + if (targetPos.X + (s32)sourceSize.Width > clipRect->LowerRightCorner.X) + { + sourceSize.Width -= (targetPos.X + sourceSize.Width) - clipRect->LowerRightCorner.X; + if (sourceSize.Width <= 0) + return; + } + + if (targetPos.Y < clipRect->UpperLeftCorner.Y) + { + sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y; + if (sourceSize.Height <= 0) + return; + + sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y; + targetPos.Y = clipRect->UpperLeftCorner.Y; + } + + if (targetPos.Y + (s32)sourceSize.Height > clipRect->LowerRightCorner.Y) + { + sourceSize.Height -= (targetPos.Y + sourceSize.Height) - clipRect->LowerRightCorner.Y; + if (sourceSize.Height <= 0) + return; + } + } + + // clip these coordinates + + if (targetPos.X<0) + { + sourceSize.Width += targetPos.X; + if (sourceSize.Width <= 0) + return; + + sourcePos.X -= targetPos.X; + targetPos.X = 0; + } + + const core::dimension2d& renderTargetSize = getCurrentRenderTargetSize(); + + if (targetPos.X + sourceSize.Width > (s32)renderTargetSize.Width) + { + sourceSize.Width -= (targetPos.X + sourceSize.Width) - renderTargetSize.Width; + if (sourceSize.Width <= 0) + return; + } + + if (targetPos.Y<0) + { + sourceSize.Height += targetPos.Y; + if (sourceSize.Height <= 0) + return; + + sourcePos.Y -= targetPos.Y; + targetPos.Y = 0; + } + + if (targetPos.Y + sourceSize.Height > (s32)renderTargetSize.Height) + { + sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height; + if (sourceSize.Height <= 0) + return; + } + + // ok, we've clipped everything. + // now draw it. + + core::rect tcoords; + tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getOriginalSize().Width ; + tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getOriginalSize().Height; + tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getOriginalSize().Width); + tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getOriginalSize().Height); + + const core::rect poss(targetPos, sourceSize); + + setRenderStates2DMode(color.getAlpha()<255, true, useAlphaChannelOfTexture); + + S3DVertex vtx[4]; + vtx[0] = S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, color, + tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y); + vtx[1] = S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, color, + tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y); + vtx[2] = S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, color, + tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y); + vtx[3] = S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, color, + tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y); + + s16 indices[6] = {0,1,2,0,2,3}; + + setVertexShader(EVT_STANDARD); + + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &indices[0], + D3DFMT_INDEX16,&vtx[0], sizeof(S3DVertex)); +} + + +//!Draws a 2d rectangle with a gradient. +void CD3D9Driver::draw2DRectangle(const core::rect& position, + SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown, + const core::rect* clip) +{ + core::rect pos(position); + + if (clip) + pos.clipAgainst(*clip); + + if (!pos.isValid()) + return; + + S3DVertex vtx[4]; + vtx[0] = S3DVertex((f32)pos.UpperLeftCorner.X, (f32)pos.UpperLeftCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, colorLeftUp, 0.0f, 0.0f); + vtx[1] = S3DVertex((f32)pos.LowerRightCorner.X, (f32)pos.UpperLeftCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, colorRightUp, 0.0f, 1.0f); + vtx[2] = S3DVertex((f32)pos.LowerRightCorner.X, (f32)pos.LowerRightCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, colorRightDown, 1.0f, 0.0f); + vtx[3] = S3DVertex((f32)pos.UpperLeftCorner.X, (f32)pos.LowerRightCorner.Y, 0.0f, + 0.0f, 0.0f, 0.0f, colorLeftDown, 1.0f, 1.0f); + + s16 indices[6] = {0,1,2,0,2,3}; + + setRenderStates2DMode( + colorLeftUp.getAlpha() < 255 || + colorRightUp.getAlpha() < 255 || + colorLeftDown.getAlpha() < 255 || + colorRightDown.getAlpha() < 255, false, false); + + setActiveTexture(0,0); + + setVertexShader(EVT_STANDARD); + + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &indices[0], + D3DFMT_INDEX16, &vtx[0], sizeof(S3DVertex)); +} + + +//! Draws a 2d line. +void CD3D9Driver::draw2DLine(const core::position2d& start, + const core::position2d& end, + SColor color) +{ + if (start==end) + drawPixel(start.X, start.Y, color); + else + { + // thanks to Vash TheStampede who sent in his implementation + S3DVertex vtx[2]; + vtx[0] = S3DVertex((f32)start.X+0.375f, (f32)start.Y+0.375f, 0.0f, + 0.0f, 0.0f, 0.0f, // normal + color, 0.0f, 0.0f); // texture + + vtx[1] = S3DVertex((f32)end.X+0.375f, (f32)end.Y+0.375f, 0.0f, + 0.0f, 0.0f, 0.0f, + color, 0.0f, 0.0f); + + setRenderStates2DMode(color.getAlpha() < 255, false, false); + setActiveTexture(0,0); + + setVertexShader(EVT_STANDARD); + + pID3DDevice->DrawPrimitiveUP(D3DPT_LINELIST, 1, + &vtx[0], sizeof(S3DVertex) ); + } +} + + +//! Draws a pixel +void CD3D9Driver::drawPixel(u32 x, u32 y, const SColor & color) +{ + const core::dimension2d& renderTargetSize = getCurrentRenderTargetSize(); + if(x > (u32)renderTargetSize.Width || y > (u32)renderTargetSize.Height) + return; + + setRenderStates2DMode(color.getAlpha() < 255, false, false); + setActiveTexture(0,0); + + setVertexShader(EVT_STANDARD); + + S3DVertex vertex((f32)x+0.375f, (f32)y+0.375f, 0.f, 0.f, 0.f, 0.f, color, 0.f, 0.f); + + pID3DDevice->DrawPrimitiveUP(D3DPT_POINTLIST, 1, &vertex, sizeof(vertex)); +} + + +//! sets right vertex shader +void CD3D9Driver::setVertexShader(E_VERTEX_TYPE newType) +{ + if (newType != LastVertexType) + { + LastVertexType = newType; + HRESULT hr = 0; + + switch(newType) + { + case EVT_STANDARD: + hr = pID3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1); + break; + case EVT_2TCOORDS: + hr = pID3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2); + break; + case EVT_TANGENTS: + hr = pID3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX3 | + D3DFVF_TEXCOORDSIZE2(0) | // real texture coord + D3DFVF_TEXCOORDSIZE3(1) | // misuse texture coord 2 for tangent + D3DFVF_TEXCOORDSIZE3(2) // misuse texture coord 3 for binormal + ); + break; + } + + if (FAILED(hr)) + { + os::Printer::log("Could not set vertex Shader.", ELL_ERROR); + return; + } + } +} + + +//! sets the needed renderstates +bool CD3D9Driver::setRenderStates3DMode() +{ + if (!pID3DDevice) + return false; + + if (CurrentRenderMode != ERM_3D) + { + // switch back the matrices + pID3DDevice->SetTransform(D3DTS_VIEW, (D3DMATRIX*)((void*)&Matrices[ETS_VIEW])); + pID3DDevice->SetTransform(D3DTS_WORLD, (D3DMATRIX*)((void*)&Matrices[ETS_WORLD])); + pID3DDevice->SetTransform(D3DTS_PROJECTION, (D3DMATRIX*)((void*)&Matrices[ETS_PROJECTION])); + + pID3DDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); + pID3DDevice->SetRenderState(D3DRS_CLIPPING, TRUE); + + ResetRenderStates = true; + } + + if (ResetRenderStates || LastMaterial != Material) + { + // unset old material + + if (CurrentRenderMode == ERM_3D && + LastMaterial.MaterialType != Material.MaterialType && + LastMaterial.MaterialType >= 0 && LastMaterial.MaterialType < (s32)MaterialRenderers.size()) + MaterialRenderers[LastMaterial.MaterialType].Renderer->OnUnsetMaterial(); + + // set new material. + + if (Material.MaterialType >= 0 && Material.MaterialType < (s32)MaterialRenderers.size()) + MaterialRenderers[Material.MaterialType].Renderer->OnSetMaterial( + Material, LastMaterial, ResetRenderStates, this); + } + + bool shaderOK = true; + if (Material.MaterialType >= 0 && Material.MaterialType < (s32)MaterialRenderers.size()) + shaderOK = MaterialRenderers[Material.MaterialType].Renderer->OnRender(this, LastVertexType); + + LastMaterial = Material; + + ResetRenderStates = false; + + CurrentRenderMode = ERM_3D; + + return shaderOK; +} + + +//! Map Irrlicht texture wrap mode to native values +D3DTEXTUREADDRESS CD3D9Driver::getTextureWrapMode(const u8 clamp) +{ + switch (clamp) + { + case ETC_REPEAT: + if (Caps.TextureAddressCaps & D3DPTADDRESSCAPS_WRAP) + return D3DTADDRESS_WRAP; + case ETC_CLAMP: + case ETC_CLAMP_TO_EDGE: + if (Caps.TextureAddressCaps & D3DPTADDRESSCAPS_CLAMP) + return D3DTADDRESS_CLAMP; + case ETC_MIRROR: + if (Caps.TextureAddressCaps & D3DPTADDRESSCAPS_MIRROR) + return D3DTADDRESS_MIRROR; + case ETC_CLAMP_TO_BORDER: + if (Caps.TextureAddressCaps & D3DPTADDRESSCAPS_BORDER) + return D3DTADDRESS_BORDER; + else + return D3DTADDRESS_CLAMP; + case ETC_MIRROR_CLAMP: + case ETC_MIRROR_CLAMP_TO_EDGE: + case ETC_MIRROR_CLAMP_TO_BORDER: + if (Caps.TextureAddressCaps & D3DPTADDRESSCAPS_MIRRORONCE) + return D3DTADDRESS_MIRRORONCE; + else + return D3DTADDRESS_CLAMP; + default: + return D3DTADDRESS_WRAP; + } +} + + +//! Can be called by an IMaterialRenderer to make its work easier. +void CD3D9Driver::setBasicRenderStates(const SMaterial& material, const SMaterial& lastmaterial, + bool resetAllRenderstates) +{ + // This needs only to be updated onresets + if (Params.HandleSRGB && resetAllRenderstates) + pID3DDevice->SetRenderState(D3DRS_SRGBWRITEENABLE, TRUE); + + if (resetAllRenderstates || + lastmaterial.AmbientColor != material.AmbientColor || + lastmaterial.DiffuseColor != material.DiffuseColor || + lastmaterial.SpecularColor != material.SpecularColor || + lastmaterial.EmissiveColor != material.EmissiveColor || + lastmaterial.Shininess != material.Shininess) + { + D3DMATERIAL9 mat; + mat.Diffuse = colorToD3D(material.DiffuseColor); + mat.Ambient = colorToD3D(material.AmbientColor); + mat.Specular = colorToD3D(material.SpecularColor); + mat.Emissive = colorToD3D(material.EmissiveColor); + mat.Power = material.Shininess; + pID3DDevice->SetMaterial(&mat); + } + + if (lastmaterial.ColorMaterial != material.ColorMaterial) + { + pID3DDevice->SetRenderState(D3DRS_COLORVERTEX, (material.ColorMaterial != ECM_NONE)); + pID3DDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, + ((material.ColorMaterial == ECM_DIFFUSE)|| + (material.ColorMaterial == ECM_DIFFUSE_AND_AMBIENT))?D3DMCS_COLOR1:D3DMCS_MATERIAL); + pID3DDevice->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE, + ((material.ColorMaterial == ECM_AMBIENT)|| + (material.ColorMaterial == ECM_DIFFUSE_AND_AMBIENT))?D3DMCS_COLOR1:D3DMCS_MATERIAL); + pID3DDevice->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, + (material.ColorMaterial == ECM_EMISSIVE)?D3DMCS_COLOR1:D3DMCS_MATERIAL); + pID3DDevice->SetRenderState(D3DRS_SPECULARMATERIALSOURCE, + (material.ColorMaterial == ECM_SPECULAR)?D3DMCS_COLOR1:D3DMCS_MATERIAL); + } + + // fillmode + if (resetAllRenderstates || lastmaterial.Wireframe != material.Wireframe || lastmaterial.PointCloud != material.PointCloud) + { + if (material.Wireframe) + pID3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); + else + if (material.PointCloud) + pID3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_POINT); + else + pID3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + } + + // shademode + + if (resetAllRenderstates || lastmaterial.GouraudShading != material.GouraudShading) + { + if (material.GouraudShading) + pID3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + else + pID3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); + } + + // lighting + + if (resetAllRenderstates || lastmaterial.Lighting != material.Lighting) + { + if (material.Lighting) + pID3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE); + else + pID3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE); + } + + // zbuffer + + if (resetAllRenderstates || lastmaterial.ZBuffer != material.ZBuffer) + { + switch (material.ZBuffer) + { + case ECFN_NEVER: + pID3DDevice->SetRenderState(D3DRS_ZENABLE, FALSE); + break; + case ECFN_LESSEQUAL: + pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); + break; + case ECFN_EQUAL: + pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_EQUAL); + break; + case ECFN_LESS: + pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS); + break; + case ECFN_NOTEQUAL: + pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_NOTEQUAL); + break; + case ECFN_GREATEREQUAL: + pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_GREATEREQUAL); + break; + case ECFN_GREATER: + pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_GREATER); + break; + case ECFN_ALWAYS: + pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS); + break; + } + } + + // zwrite +// if (resetAllRenderstates || (lastmaterial.ZWriteEnable != material.ZWriteEnable)) + { + if ( material.ZWriteEnable && (AllowZWriteOnTransparent || !material.isTransparent())) + pID3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE); + else + pID3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE); + } + + // back face culling + + if (resetAllRenderstates || (lastmaterial.FrontfaceCulling != material.FrontfaceCulling) || (lastmaterial.BackfaceCulling != material.BackfaceCulling)) + { +// if (material.FrontfaceCulling && material.BackfaceCulling) +// pID3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW|D3DCULL_CCW); +// else + if (material.FrontfaceCulling) + pID3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); + else + if (material.BackfaceCulling) + pID3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + else + pID3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + } + + // fog + if (resetAllRenderstates || lastmaterial.FogEnable != material.FogEnable) + { + pID3DDevice->SetRenderState(D3DRS_FOGENABLE, material.FogEnable); + } + + // specular highlights + if (resetAllRenderstates || !core::equals(lastmaterial.Shininess,material.Shininess)) + { + const bool enable = (material.Shininess!=0.0f); + pID3DDevice->SetRenderState(D3DRS_SPECULARENABLE, enable); + pID3DDevice->SetRenderState(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL); + } + + // normalization + if (resetAllRenderstates || lastmaterial.NormalizeNormals != material.NormalizeNormals) + { + pID3DDevice->SetRenderState(D3DRS_NORMALIZENORMALS, material.NormalizeNormals); + } + + // Color Mask + if (queryFeature(EVDF_COLOR_MASK) && + (resetAllRenderstates || lastmaterial.ColorMask != material.ColorMask)) + { + const DWORD flag = + ((material.ColorMask & ECP_RED)?D3DCOLORWRITEENABLE_RED:0) | + ((material.ColorMask & ECP_GREEN)?D3DCOLORWRITEENABLE_GREEN:0) | + ((material.ColorMask & ECP_BLUE)?D3DCOLORWRITEENABLE_BLUE:0) | + ((material.ColorMask & ECP_ALPHA)?D3DCOLORWRITEENABLE_ALPHA:0); + pID3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, flag); + } + + if (queryFeature(EVDF_BLEND_OPERATIONS) && + (resetAllRenderstates|| lastmaterial.BlendOperation != material.BlendOperation)) + { + if (material.BlendOperation==EBO_NONE) + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + else + { + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + switch (material.BlendOperation) + { + case EBO_SUBTRACT: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT); + break; + case EBO_REVSUBTRACT: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT); + break; + case EBO_MIN: + case EBO_MIN_FACTOR: + case EBO_MIN_ALPHA: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MIN); + break; + case EBO_MAX: + case EBO_MAX_FACTOR: + case EBO_MAX_ALPHA: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX); + break; + default: + pID3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + break; + } + } + } + + // Polygon offset + if (queryFeature(EVDF_POLYGON_OFFSET) && (resetAllRenderstates || + lastmaterial.PolygonOffsetDirection != material.PolygonOffsetDirection || + lastmaterial.PolygonOffsetFactor != material.PolygonOffsetFactor)) + { + if (material.PolygonOffsetFactor) + { + if (material.PolygonOffsetDirection==EPO_BACK) + { + pID3DDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(1.f)); + pID3DDevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW((FLOAT)material.PolygonOffsetFactor)); + } + else + { + pID3DDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(-1.f)); + pID3DDevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW((FLOAT)-material.PolygonOffsetFactor)); + } + } + else + { + pID3DDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, 0); + pID3DDevice->SetRenderState(D3DRS_DEPTHBIAS, 0); + } + } + + // Anti Aliasing + if (resetAllRenderstates || lastmaterial.AntiAliasing != material.AntiAliasing) + { + if (AlphaToCoverageSupport && (material.AntiAliasing & EAAM_ALPHA_TO_COVERAGE)) + { + if (VendorID==0x10DE)//NVidia + pID3DDevice->SetRenderState(D3DRS_ADAPTIVETESS_Y, MAKEFOURCC('A','T','O','C')); + // SSAA could give better results on NVidia cards + else if (VendorID==0x1002)//ATI + pID3DDevice->SetRenderState(D3DRS_POINTSIZE, MAKEFOURCC('A','2','M','1')); + } + else if (AlphaToCoverageSupport && (lastmaterial.AntiAliasing & EAAM_ALPHA_TO_COVERAGE)) + { + if (VendorID==0x10DE) + pID3DDevice->SetRenderState(D3DRS_ADAPTIVETESS_Y, D3DFMT_UNKNOWN); + else if (VendorID==0x1002) + pID3DDevice->SetRenderState(D3DRS_POINTSIZE, MAKEFOURCC('A','2','M','0')); + } + + // enable antialiasing + if (Params.AntiAlias) + { + if (material.AntiAliasing & (EAAM_SIMPLE|EAAM_QUALITY)) + pID3DDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, TRUE); + else if (lastmaterial.AntiAliasing & (EAAM_SIMPLE|EAAM_QUALITY)) + pID3DDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, FALSE); + if (material.AntiAliasing & (EAAM_LINE_SMOOTH)) + pID3DDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, TRUE); + else if (lastmaterial.AntiAliasing & (EAAM_LINE_SMOOTH)) + pID3DDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, FALSE); + } + } + + // thickness + if (resetAllRenderstates || lastmaterial.Thickness != material.Thickness) + { + pID3DDevice->SetRenderState(D3DRS_POINTSIZE, F2DW(material.Thickness)); + } + + // texture address mode + for (u32 st=0; stSetSamplerState(st, D3DSAMP_SRGBTEXTURE, TRUE); + + if (resetAllRenderstates || lastmaterial.TextureLayer[st].LODBias != material.TextureLayer[st].LODBias) + { + const float tmp = material.TextureLayer[st].LODBias * 0.125f; + pID3DDevice->SetSamplerState(st, D3DSAMP_MIPMAPLODBIAS, F2DW(tmp)); + } + + if (resetAllRenderstates || lastmaterial.TextureLayer[st].TextureWrapU != material.TextureLayer[st].TextureWrapU) + pID3DDevice->SetSamplerState(st, D3DSAMP_ADDRESSU, getTextureWrapMode(material.TextureLayer[st].TextureWrapU)); + // If separate UV not supported reuse U for V + if (!(Caps.TextureAddressCaps & D3DPTADDRESSCAPS_INDEPENDENTUV)) + pID3DDevice->SetSamplerState(st, D3DSAMP_ADDRESSV, getTextureWrapMode(material.TextureLayer[st].TextureWrapU)); + else if (resetAllRenderstates || lastmaterial.TextureLayer[st].TextureWrapV != material.TextureLayer[st].TextureWrapV) + pID3DDevice->SetSamplerState(st, D3DSAMP_ADDRESSV, getTextureWrapMode(material.TextureLayer[st].TextureWrapV)); + + // Bilinear, trilinear, and anisotropic filter + if (resetAllRenderstates || + lastmaterial.TextureLayer[st].BilinearFilter != material.TextureLayer[st].BilinearFilter || + lastmaterial.TextureLayer[st].TrilinearFilter != material.TextureLayer[st].TrilinearFilter || + lastmaterial.TextureLayer[st].AnisotropicFilter != material.TextureLayer[st].AnisotropicFilter || + lastmaterial.UseMipMaps != material.UseMipMaps) + { + if (material.TextureLayer[st].BilinearFilter || material.TextureLayer[st].TrilinearFilter || material.TextureLayer[st].AnisotropicFilter) + { + D3DTEXTUREFILTERTYPE tftMag = ((Caps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC) && + material.TextureLayer[st].AnisotropicFilter) ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR; + D3DTEXTUREFILTERTYPE tftMin = ((Caps.TextureFilterCaps & D3DPTFILTERCAPS_MINFANISOTROPIC) && + material.TextureLayer[st].AnisotropicFilter) ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR; + D3DTEXTUREFILTERTYPE tftMip = material.UseMipMaps? (material.TextureLayer[st].TrilinearFilter ? D3DTEXF_LINEAR : D3DTEXF_POINT) : D3DTEXF_NONE; + + if (tftMag==D3DTEXF_ANISOTROPIC || tftMin == D3DTEXF_ANISOTROPIC) + pID3DDevice->SetSamplerState(st, D3DSAMP_MAXANISOTROPY, core::min_((DWORD)material.TextureLayer[st].AnisotropicFilter, Caps.MaxAnisotropy)); + pID3DDevice->SetSamplerState(st, D3DSAMP_MAGFILTER, tftMag); + pID3DDevice->SetSamplerState(st, D3DSAMP_MINFILTER, tftMin); + pID3DDevice->SetSamplerState(st, D3DSAMP_MIPFILTER, tftMip); + } + else + { + pID3DDevice->SetSamplerState(st, D3DSAMP_MINFILTER, D3DTEXF_POINT); + pID3DDevice->SetSamplerState(st, D3DSAMP_MIPFILTER, D3DTEXF_NONE); + pID3DDevice->SetSamplerState(st, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + } + } + } +} + + +//! sets the needed renderstates +void CD3D9Driver::setRenderStatesStencilShadowMode(bool zfail, u32 debugDataVisible) +{ + if ((CurrentRenderMode != ERM_SHADOW_VOLUME_ZFAIL && + CurrentRenderMode != ERM_SHADOW_VOLUME_ZPASS) || + Transformation3DChanged) + { + // unset last 3d material + if (CurrentRenderMode == ERM_3D && + static_cast(Material.MaterialType) < MaterialRenderers.size()) + { + MaterialRenderers[Material.MaterialType].Renderer->OnUnsetMaterial(); + ResetRenderStates = true; + } + // switch back the matrices + pID3DDevice->SetTransform(D3DTS_VIEW, (D3DMATRIX*)((void*)&Matrices[ETS_VIEW])); + pID3DDevice->SetTransform(D3DTS_WORLD, (D3DMATRIX*)((void*)&Matrices[ETS_WORLD])); + pID3DDevice->SetTransform(D3DTS_PROJECTION, (D3DMATRIX*)((void*)&Matrices[ETS_PROJECTION])); + + Transformation3DChanged = false; + + setActiveTexture(0,0); + setActiveTexture(1,0); + setActiveTexture(2,0); + setActiveTexture(3,0); + + pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE); + + pID3DDevice->SetFVF(D3DFVF_XYZ); + LastVertexType = (video::E_VERTEX_TYPE)(-1); + + pID3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + pID3DDevice->SetRenderState(D3DRS_STENCILENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); + //pID3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + //pID3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + + pID3DDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); + pID3DDevice->SetRenderState(D3DRS_STENCILREF, 0x0); + 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 ); + + pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS); + + //if (!(debugDataVisible & (scene::EDS_SKELETON|scene::EDS_MESH_WIRE_OVERLAY))) + // pID3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, 0); + if ((debugDataVisible & scene::EDS_MESH_WIRE_OVERLAY)) + pID3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); + } + + if (CurrentRenderMode != ERM_SHADOW_VOLUME_ZPASS && !zfail) + { + // USE THE ZPASS METHOD + pID3DDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); + pID3DDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); + //pID3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR); // does not matter, will be set later + } + else + if (CurrentRenderMode != ERM_SHADOW_VOLUME_ZFAIL && zfail) + { + // USE THE ZFAIL METHOD + pID3DDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); + //pID3DDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_INCR); // does not matter, will be set later + pID3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); + } + + CurrentRenderMode = zfail ? ERM_SHADOW_VOLUME_ZFAIL : ERM_SHADOW_VOLUME_ZPASS; +} + + +//! sets the needed renderstates +void CD3D9Driver::setRenderStatesStencilFillMode(bool alpha) +{ + if (CurrentRenderMode != ERM_STENCIL_FILL || Transformation3DChanged) + { + core::matrix4 mat; + pID3DDevice->SetTransform(D3DTS_VIEW, &UnitMatrixD3D9); + pID3DDevice->SetTransform(D3DTS_WORLD, &UnitMatrixD3D9); + pID3DDevice->SetTransform(D3DTS_PROJECTION, &UnitMatrixD3D9); + + pID3DDevice->SetRenderState(D3DRS_ZENABLE, FALSE); + pID3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE); + pID3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + + pID3DDevice->SetRenderState(D3DRS_STENCILREF, 0x1); + pID3DDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL); + //pID3DDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATEREQUAL); + pID3DDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); + pID3DDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); + pID3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); + pID3DDevice->SetRenderState(D3DRS_STENCILMASK, 0xffffffff); + pID3DDevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff); + + pID3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + + Transformation3DChanged = false; + + pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + if (alpha) + { + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + else + { + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } + } + + CurrentRenderMode = ERM_STENCIL_FILL; +} + + +//! Enable the 2d override material +void CD3D9Driver::enableMaterial2D(bool enable) +{ + if (!enable) + CurrentRenderMode = ERM_NONE; + CNullDriver::enableMaterial2D(enable); +} + + +//! sets the needed renderstates +void CD3D9Driver::setRenderStates2DMode(bool alpha, bool texture, bool alphaChannel) +{ + if (!pID3DDevice) + return; + + if (CurrentRenderMode != ERM_2D || Transformation3DChanged) + { + // unset last 3d material + if (CurrentRenderMode == ERM_3D) + { + if (static_cast(LastMaterial.MaterialType) < MaterialRenderers.size()) + MaterialRenderers[LastMaterial.MaterialType].Renderer->OnUnsetMaterial(); + } + if (!OverrideMaterial2DEnabled) + { + setBasicRenderStates(InitMaterial2D, LastMaterial, true); + LastMaterial=InitMaterial2D; + + // fix everything that is wrongly set by InitMaterial2D default + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + + pID3DDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); + } + core::matrix4 m; +// this fixes some problems with pixel exact rendering, but also breaks nice texturing +// moreover, it would have to be tested in each call, as the texture flag can change each time +// if (!texture) +// m.setTranslation(core::vector3df(0.5f,0.5f,0)); + pID3DDevice->SetTransform(D3DTS_WORLD, (D3DMATRIX*)((void*)m.pointer())); + + // adjust the view such that pixel center aligns with texels + // Otherwise, subpixel artifacts will occur + m.setTranslation(core::vector3df(-0.5f,-0.5f,0)); + pID3DDevice->SetTransform(D3DTS_VIEW, (D3DMATRIX*)((void*)m.pointer())); + + const core::dimension2d& renderTargetSize = getCurrentRenderTargetSize(); + m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0, 1.0); + m.setTranslation(core::vector3df(-1,1,0)); + pID3DDevice->SetTransform(D3DTS_PROJECTION, (D3DMATRIX*)((void*)m.pointer())); + + // 2d elements are clipped in software + pID3DDevice->SetRenderState(D3DRS_CLIPPING, FALSE); + + Transformation3DChanged = false; + } + if (OverrideMaterial2DEnabled) + { + OverrideMaterial2D.Lighting=false; + setBasicRenderStates(OverrideMaterial2D, LastMaterial, false); + LastMaterial = OverrideMaterial2D; + } + + // no alphaChannel without texture + alphaChannel &= texture; + + if (alpha || alphaChannel) + { + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + else + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + pID3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + pID3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + if (texture) + { + setTransform(ETS_TEXTURE_0, core::IdentityMatrix); + // Due to the transformation change, the previous line would call a reset each frame + // but we can safely reset the variable as it was false before + Transformation3DChanged=false; + } + if (alphaChannel) + { + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + + if (alpha) + { + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + } + else + { + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + } + } + else + { + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + if (alpha) + { + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + } + else + { + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + pID3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + } + } + + CurrentRenderMode = ERM_2D; +} + + +//! deletes all dynamic lights there are +void CD3D9Driver::deleteAllDynamicLights() +{ + for (s32 i=0; iLightEnable(i, false); + + LastSetLight = -1; + + CNullDriver::deleteAllDynamicLights(); +} + + +//! adds a dynamic light +s32 CD3D9Driver::addDynamicLight(const SLight& dl) +{ + CNullDriver::addDynamicLight(dl); + + D3DLIGHT9 light; + + switch (dl.Type) + { + case ELT_POINT: + light.Type = D3DLIGHT_POINT; + break; + case ELT_SPOT: + light.Type = D3DLIGHT_SPOT; + break; + case ELT_DIRECTIONAL: + light.Type = D3DLIGHT_DIRECTIONAL; + break; + } + + light.Position = *(D3DVECTOR*)((void*)(&dl.Position)); + light.Direction = *(D3DVECTOR*)((void*)(&dl.Direction)); + + light.Range = core::min_(dl.Radius, MaxLightDistance); + light.Falloff = dl.Falloff; + + light.Diffuse = *(D3DCOLORVALUE*)((void*)(&dl.DiffuseColor)); + light.Specular = *(D3DCOLORVALUE*)((void*)(&dl.SpecularColor)); + light.Ambient = *(D3DCOLORVALUE*)((void*)(&dl.AmbientColor)); + + light.Attenuation0 = dl.Attenuation.X; + light.Attenuation1 = dl.Attenuation.Y; + light.Attenuation2 = dl.Attenuation.Z; + + light.Theta = dl.InnerCone * 2.0f * core::DEGTORAD; + light.Phi = dl.OuterCone * 2.0f * core::DEGTORAD; + + ++LastSetLight; + + if(D3D_OK == pID3DDevice->SetLight(LastSetLight, &light)) + { + // I don't care if this succeeds + (void)pID3DDevice->LightEnable(LastSetLight, true); + return LastSetLight; + } + + return -1; +} + +//! Turns a dynamic light on or off +//! \param lightIndex: the index returned by addDynamicLight +//! \param turnOn: true to turn the light on, false to turn it off +void CD3D9Driver::turnLightOn(s32 lightIndex, bool turnOn) +{ + if(lightIndex < 0 || lightIndex > LastSetLight) + return; + + (void)pID3DDevice->LightEnable(lightIndex, turnOn); +} + + +//! returns the maximal amount of dynamic lights the device can handle +u32 CD3D9Driver::getMaximalDynamicLightAmount() const +{ + return Caps.MaxActiveLights; +} + + +//! Sets the dynamic ambient light color. The default color is +//! (0,0,0,0) which means it is dark. +//! \param color: New color of the ambient light. +void CD3D9Driver::setAmbientLight(const SColorf& color) +{ + if (!pID3DDevice) + return; + + AmbientLight = color; + D3DCOLOR col = color.toSColor().color; + pID3DDevice->SetRenderState(D3DRS_AMBIENT, col); +} + + +//! \return Returns the name of the video driver. Example: In case of the DIRECT3D9 +//! driver, it would return "Direct3D9.0". +const wchar_t* CD3D9Driver::getName() const +{ + return L"Direct3D 9.0"; +} + + +//! Draws a shadow volume into the stencil buffer. To draw a stencil shadow, do +//! this: First, draw all geometry. Then use this method, to draw the shadow +//! volume. Then, use IVideoDriver::drawStencilShadow() to visualize the shadow. +void CD3D9Driver::drawStencilShadowVolume(const core::array& triangles, bool zfail, u32 debugDataVisible) +{ + if (!Params.Stencilbuffer) + return; + + setRenderStatesStencilShadowMode(zfail, debugDataVisible); + + const u32 count = triangles.size(); + if (!count) + return; + + if (!zfail) + { + // ZPASS Method + + // Draw front-side of shadow volume in stencil only + pID3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + pID3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR); + pID3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, count / 3, triangles.const_pointer(), sizeof(core::vector3df)); + + // Now reverse cull order so front sides of shadow volume are written. + pID3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); + pID3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR); + pID3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, count / 3, triangles.const_pointer(), sizeof(core::vector3df)); + } + else + { + // ZFAIL Method + + // Draw front-side of shadow volume in stencil only + pID3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); + pID3DDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_INCR); + pID3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, count / 3, triangles.const_pointer(), sizeof(core::vector3df)); + + // Now reverse cull order so front sides of shadow volume are written. + pID3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW); + pID3DDevice->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_DECR); + pID3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, count / 3, triangles.const_pointer(), sizeof(core::vector3df)); + } +} + + +//! Fills the stencil shadow with color. After the shadow volume has been drawn +//! into the stencil buffer using IVideoDriver::drawStencilShadowVolume(), use this +//! to draw the color of the shadow. +void CD3D9Driver::drawStencilShadow(bool clearStencilBuffer, video::SColor leftUpEdge, + video::SColor rightUpEdge, video::SColor leftDownEdge, video::SColor rightDownEdge) +{ + if (!Params.Stencilbuffer) + return; + + S3DVertex vtx[4]; + vtx[0] = S3DVertex(1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, leftUpEdge, 0.0f, 0.0f); + vtx[1] = S3DVertex(1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, rightUpEdge, 0.0f, 1.0f); + vtx[2] = S3DVertex(-1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, leftDownEdge, 1.0f, 0.0f); + vtx[3] = S3DVertex(-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, rightDownEdge, 1.0f, 1.0f); + + s16 indices[6] = {0,1,2,1,3,2}; + + setRenderStatesStencilFillMode( + leftUpEdge.getAlpha() < 255 || + rightUpEdge.getAlpha() < 255 || + leftDownEdge.getAlpha() < 255 || + rightDownEdge.getAlpha() < 255); + + setActiveTexture(0,0); + + setVertexShader(EVT_STANDARD); + + pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &indices[0], + D3DFMT_INDEX16, &vtx[0], sizeof(S3DVertex)); + + if (clearStencilBuffer) + pID3DDevice->Clear( 0, NULL, D3DCLEAR_STENCIL,0, 1.0, 0); +} + + +//! Returns the maximum amount of primitives (mostly vertices) which +//! the device is able to render with one drawIndexedTriangleList +//! call. +u32 CD3D9Driver::getMaximalPrimitiveCount() const +{ + return Caps.MaxPrimitiveCount; +} + + +//! Sets the fog mode. +void CD3D9Driver::setFog(SColor color, E_FOG_TYPE fogType, f32 start, + f32 end, f32 density, bool pixelFog, bool rangeFog) +{ + CNullDriver::setFog(color, fogType, start, end, density, pixelFog, rangeFog); + + if (!pID3DDevice) + return; + + pID3DDevice->SetRenderState(D3DRS_FOGCOLOR, color.color); + + pID3DDevice->SetRenderState( + pixelFog ? D3DRS_FOGTABLEMODE : D3DRS_FOGVERTEXMODE, + (fogType==EFT_FOG_LINEAR)? D3DFOG_LINEAR : (fogType==EFT_FOG_EXP)?D3DFOG_EXP:D3DFOG_EXP2); + + if (fogType==EFT_FOG_LINEAR) + { + pID3DDevice->SetRenderState(D3DRS_FOGSTART, F2DW(start)); + pID3DDevice->SetRenderState(D3DRS_FOGEND, F2DW(end)); + } + else + pID3DDevice->SetRenderState(D3DRS_FOGDENSITY, F2DW(density)); + + if(!pixelFog) + pID3DDevice->SetRenderState(D3DRS_RANGEFOGENABLE, rangeFog); +} + + +//! Draws a 3d line. +void CD3D9Driver::draw3DLine(const core::vector3df& start, + const core::vector3df& end, SColor color) +{ + setVertexShader(EVT_STANDARD); + setRenderStates3DMode(); + video::S3DVertex v[2]; + v[0].Color = color; + v[1].Color = color; + v[0].Pos = start; + v[1].Pos = end; + + pID3DDevice->DrawPrimitiveUP(D3DPT_LINELIST, 1, v, sizeof(S3DVertex)); +} + + +//! resets the device +bool CD3D9Driver::reset() +{ + u32 i; + os::Printer::log("Resetting D3D9 device.", ELL_INFORMATION); + + for (i=0; iisRenderTarget()) + { + IDirect3DBaseTexture9* tex = ((CD3D9Texture*)(Textures[i].Surface))->getDX9Texture(); + if (tex) + tex->Release(); + } + } + for (i=0; iSurface) + DepthBuffers[i]->Surface->Release(); + } + for (i=0; i(OcclusionQueries[i].PID)->Release(); + OcclusionQueries[i].PID=0; + } + } + // this does not require a restore in the reset method, it's updated + // automatically in the next render cycle. + removeAllHardwareBuffers(); + + DriverWasReset=true; + + HRESULT hr = pID3DDevice->Reset(&present); + + // restore RTTs + for (i=0; iisRenderTarget()) + ((CD3D9Texture*)(Textures[i].Surface))->createRenderTarget(); + } + + // restore screen depthbuffer + pID3DDevice->GetDepthStencilSurface(&(DepthBuffers[0]->Surface)); + D3DSURFACE_DESC desc; + // restore other depth buffers + // depth format is taken from main depth buffer + DepthBuffers[0]->Surface->GetDesc(&desc); + // multisampling is taken from rendertarget + D3DSURFACE_DESC desc2; + for (i=1; iDepthSurface==DepthBuffers[i]) + { + ((CD3D9Texture*)(Textures[j].Surface))->Texture->GetLevelDesc(0,&desc2); + break; + } + } + + pID3DDevice->CreateDepthStencilSurface(DepthBuffers[i]->Size.Width, + DepthBuffers[i]->Size.Height, + desc.Format, + desc2.MultiSampleType, + desc2.MultiSampleQuality, + TRUE, + &(DepthBuffers[i]->Surface), + NULL); + } + for (i=0; iCreateQuery(D3DQUERYTYPE_OCCLUSION, reinterpret_cast(&OcclusionQueries[i].PID)); + } + + if (FAILED(hr)) + { + if (hr == D3DERR_DEVICELOST) + { + DeviceLost = true; + os::Printer::log("Resetting failed due to device lost.", ELL_WARNING); + } +#ifdef D3DERR_DEVICEREMOVED + else if (hr == D3DERR_DEVICEREMOVED) + { + os::Printer::log("Resetting failed due to device removed.", ELL_WARNING); + } +#endif + else if (hr == D3DERR_DRIVERINTERNALERROR) + { + os::Printer::log("Resetting failed due to internal error.", ELL_WARNING); + } + else if (hr == D3DERR_OUTOFVIDEOMEMORY) + { + os::Printer::log("Resetting failed due to out of memory.", ELL_WARNING); + } + else if (hr == D3DERR_DEVICENOTRESET) + { + os::Printer::log("Resetting failed due to not reset.", ELL_WARNING); + } + else if (hr == D3DERR_INVALIDCALL) + { + os::Printer::log("Resetting failed due to invalid call", "You need to release some more surfaces.", ELL_WARNING); + } + else + { + os::Printer::log("Resetting failed due to unknown reason.", core::stringc((int)hr).c_str(), ELL_WARNING); + } + return false; + } + + DeviceLost = false; + ResetRenderStates = true; + LastVertexType = (E_VERTEX_TYPE)-1; + + for (u32 i=0; i& size) +{ + if (!pID3DDevice) + return; + + CNullDriver::OnResize(size); + present.BackBufferWidth = size.Width; + present.BackBufferHeight = size.Height; + + reset(); +} + + +//! Returns type of video driver +E_DRIVER_TYPE CD3D9Driver::getDriverType() const +{ + return EDT_DIRECT3D9; +} + + +//! Returns the transformation set by setTransform +const core::matrix4& CD3D9Driver::getTransform(E_TRANSFORMATION_STATE state) const +{ + return Matrices[state]; +} + + +//! Sets a vertex shader constant. +void CD3D9Driver::setVertexShaderConstant(const f32* data, s32 startRegister, s32 constantAmount) +{ + if (data) + pID3DDevice->SetVertexShaderConstantF(startRegister, data, constantAmount); +} + + +//! Sets a pixel shader constant. +void CD3D9Driver::setPixelShaderConstant(const f32* data, s32 startRegister, s32 constantAmount) +{ + if (data) + pID3DDevice->SetPixelShaderConstantF(startRegister, data, constantAmount); +} + + +//! Sets a constant for the vertex shader based on a name. +bool CD3D9Driver::setVertexShaderConstant(const c8* name, const f32* floats, int count) +{ + if (Material.MaterialType >= 0 && Material.MaterialType < (s32)MaterialRenderers.size()) + { + CD3D9MaterialRenderer* r = (CD3D9MaterialRenderer*)MaterialRenderers[Material.MaterialType].Renderer; + return r->setVariable(true, name, floats, count); + } + + return false; +} + + +//! Bool interface for the above. +bool CD3D9Driver::setVertexShaderConstant(const c8* name, const bool* bools, int count) +{ + if (Material.MaterialType >= 0 && Material.MaterialType < (s32)MaterialRenderers.size()) + { + CD3D9MaterialRenderer* r = (CD3D9MaterialRenderer*)MaterialRenderers[Material.MaterialType].Renderer; + return r->setVariable(true, name, bools, count); + } + + return false; +} + + +//! Int interface for the above. +bool CD3D9Driver::setVertexShaderConstant(const c8* name, const s32* ints, int count) +{ + if (Material.MaterialType >= 0 && Material.MaterialType < (s32)MaterialRenderers.size()) + { + CD3D9MaterialRenderer* r = (CD3D9MaterialRenderer*)MaterialRenderers[Material.MaterialType].Renderer; + return r->setVariable(true, name, ints, count); + } + + return false; +} + + +//! Sets a constant for the pixel shader based on a name. +bool CD3D9Driver::setPixelShaderConstant(const c8* name, const f32* floats, int count) +{ + if (Material.MaterialType >= 0 && Material.MaterialType < (s32)MaterialRenderers.size()) + { + CD3D9MaterialRenderer* r = (CD3D9MaterialRenderer*)MaterialRenderers[Material.MaterialType].Renderer; + return r->setVariable(false, name, floats, count); + } + + return false; +} + + +//! Bool interface for the above. +bool CD3D9Driver::setPixelShaderConstant(const c8* name, const bool* bools, int count) +{ + if (Material.MaterialType >= 0 && Material.MaterialType < (s32)MaterialRenderers.size()) + { + CD3D9MaterialRenderer* r = (CD3D9MaterialRenderer*)MaterialRenderers[Material.MaterialType].Renderer; + return r->setVariable(false, name, bools, count); + } + + return false; +} + + +//! Int interface for the above. +bool CD3D9Driver::setPixelShaderConstant(const c8* name, const s32* ints, int count) +{ + if (Material.MaterialType >= 0 && Material.MaterialType < (s32)MaterialRenderers.size()) + { + CD3D9MaterialRenderer* r = (CD3D9MaterialRenderer*)MaterialRenderers[Material.MaterialType].Renderer; + return r->setVariable(false, name, ints, count); + } + + return false; +} + + +//! Adds a new material renderer to the VideoDriver, using pixel and/or +//! vertex shaders to render geometry. +s32 CD3D9Driver::addShaderMaterial(const c8* vertexShaderProgram, + const c8* pixelShaderProgram, + IShaderConstantSetCallBack* callback, + E_MATERIAL_TYPE baseMaterial, s32 userData) +{ + s32 nr = -1; + CD3D9ShaderMaterialRenderer* r = new CD3D9ShaderMaterialRenderer( + pID3DDevice, this, nr, vertexShaderProgram, pixelShaderProgram, + callback, getMaterialRenderer(baseMaterial), userData); + + r->drop(); + return nr; +} + + +//! Adds a new material renderer to the VideoDriver, based on a high level shading +//! language. +s32 CD3D9Driver::addHighLevelShaderMaterial( + const c8* vertexShaderProgram, + const c8* vertexShaderEntryPointName, + E_VERTEX_SHADER_TYPE vsCompileTarget, + const c8* pixelShaderProgram, + const c8* pixelShaderEntryPointName, + E_PIXEL_SHADER_TYPE psCompileTarget, + const c8* geometryShaderProgram, + const c8* geometryShaderEntryPointName, + E_GEOMETRY_SHADER_TYPE gsCompileTarget, + scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType, + u32 verticesOut, + IShaderConstantSetCallBack* callback, + E_MATERIAL_TYPE baseMaterial, s32 userData, E_GPU_SHADING_LANGUAGE shadingLang) +{ + s32 nr = -1; + + #ifdef _IRR_COMPILE_WITH_CG_ + if(shadingLang == EGSL_CG) + { + CD3D9CgMaterialRenderer* r = new CD3D9CgMaterialRenderer( + this, nr, + vertexShaderProgram, vertexShaderEntryPointName, vsCompileTarget, + pixelShaderProgram, pixelShaderEntryPointName, psCompileTarget, + geometryShaderProgram, geometryShaderEntryPointName, gsCompileTarget, + inType, outType, verticesOut, + callback,getMaterialRenderer(baseMaterial), userData); + + r->drop(); + } + else + #endif + { + CD3D9HLSLMaterialRenderer* r = new CD3D9HLSLMaterialRenderer( + pID3DDevice, this, nr, + vertexShaderProgram, + vertexShaderEntryPointName, + vsCompileTarget, + pixelShaderProgram, + pixelShaderEntryPointName, + psCompileTarget, + callback, + getMaterialRenderer(baseMaterial), + userData); + + r->drop(); + } + + return nr; +} + + +//! Returns a pointer to the IVideoDriver interface. (Implementation for +//! IMaterialRendererServices) +IVideoDriver* CD3D9Driver::getVideoDriver() +{ + return this; +} + + +//! Creates a render target texture. +ITexture* CD3D9Driver::addRenderTargetTexture(const core::dimension2d& size, + const io::path& name, + const ECOLOR_FORMAT format, const bool useStencil) +{ + CD3D9Texture* tex = new CD3D9Texture(this, size, name, format); + if (tex) + { + if (!tex->Texture) + { + tex->drop(); + return 0; + } + checkDepthBuffer(tex); + addTexture(tex); + tex->drop(); + } + return tex; +} + + +//! Clears the ZBuffer. +void CD3D9Driver::clearZBuffer() +{ + HRESULT hr = pID3DDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0, 1.0, 0); + + if (FAILED(hr)) + os::Printer::log("CD3D9Driver clearZBuffer() failed.", ELL_WARNING); +} + + +//! Returns an image created from the last rendered frame. +IImage* CD3D9Driver::createScreenShot(video::ECOLOR_FORMAT format, video::E_RENDER_TARGET target) +{ + if (target != video::ERT_FRAME_BUFFER) + return 0; + + // query the screen dimensions of the current adapter + D3DDISPLAYMODE displayMode; + pID3DDevice->GetDisplayMode(0, &displayMode); + + if (format==video::ECF_UNKNOWN) + format=video::ECF_A8R8G8B8; + + // create the image surface to store the front buffer image [always A8R8G8B8] + HRESULT hr; + LPDIRECT3DSURFACE9 lpSurface; + if (FAILED(hr = pID3DDevice->CreateOffscreenPlainSurface(displayMode.Width, displayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &lpSurface, 0))) + return 0; + + // read the front buffer into the image surface + if (FAILED(hr = pID3DDevice->GetFrontBufferData(0, lpSurface))) + { + lpSurface->Release(); + return 0; + } + + RECT clientRect; + { + POINT clientPoint; + clientPoint.x = 0; + clientPoint.y = 0; + + ClientToScreen((HWND)getExposedVideoData().D3D9.HWnd, &clientPoint); + + clientRect.left = clientPoint.x; + clientRect.top = clientPoint.y; + clientRect.right = clientRect.left + ScreenSize.Width; + clientRect.bottom = clientRect.top + ScreenSize.Height; + + // window can be off-screen partly, we can't take screenshots from that + clientRect.left = core::max_(clientRect.left, 0l); + clientRect.top = core::max_(clientRect.top, 0l); + clientRect.right = core::min_(clientRect.right, (long)displayMode.Width); + clientRect.bottom = core::min_(clientRect.bottom, (long)displayMode.Height ); + } + + // lock our area of the surface + D3DLOCKED_RECT lockedRect; + if (FAILED(lpSurface->LockRect(&lockedRect, &clientRect, D3DLOCK_READONLY))) + { + lpSurface->Release(); + return 0; + } + + irr::core::dimension2d shotSize; + shotSize.Width = core::min_( ScreenSize.Width, (u32)(clientRect.right-clientRect.left) ); + shotSize.Height = core::min_( ScreenSize.Height, (u32)(clientRect.bottom-clientRect.top) ); + + // this could throw, but we aren't going to worry about that case very much + IImage* newImage = createImage(format, shotSize); + + if (newImage) + { + // d3d pads the image, so we need to copy the correct number of bytes + u32* dP = (u32*)newImage->lock(); + u8 * sP = (u8 *)lockedRect.pBits; + + // If the display mode format doesn't promise anything about the Alpha value + // and it appears that it's not presenting 255, then we should manually + // set each pixel alpha value to 255. + if (D3DFMT_X8R8G8B8 == displayMode.Format && (0xFF000000 != (*dP & 0xFF000000))) + { + for (u32 y = 0; y < shotSize.Height; ++y) + { + for (u32 x = 0; x < shotSize.Width; ++x) + { + newImage->setPixel(x,y,*((u32*)sP) | 0xFF000000); + sP += 4; + } + + sP += lockedRect.Pitch - (4 * shotSize.Width); + } + } + else + { + for (u32 y = 0; y < shotSize.Height; ++y) + { + convertColor(sP, video::ECF_A8R8G8B8, shotSize.Width, dP, format); + sP += lockedRect.Pitch; + dP += shotSize.Width; + } + } + + newImage->unlock(); + } + + // we can unlock and release the surface + lpSurface->UnlockRect(); + + // release the image surface + lpSurface->Release(); + + // return status of save operation to caller + return newImage; +} + + +//! returns color format +ECOLOR_FORMAT CD3D9Driver::getColorFormat() const +{ + return ColorFormat; +} + + +//! returns color format +D3DFORMAT CD3D9Driver::getD3DColorFormat() const +{ + return D3DColorFormat; +} + + +// returns the current size of the screen or rendertarget +const core::dimension2d& CD3D9Driver::getCurrentRenderTargetSize() const +{ + if ( CurrentRendertargetSize.Width == 0 ) + return ScreenSize; + else + return CurrentRendertargetSize; +} + + +// Set/unset a clipping plane. +bool CD3D9Driver::setClipPlane(u32 index, const core::plane3df& plane, bool enable) +{ + if (index >= MaxUserClipPlanes) + return false; + + HRESULT ok = pID3DDevice->SetClipPlane(index, (const float*)&(plane.Normal.X)); + if (D3D_OK == ok) + enableClipPlane(index, enable); + return true; +} + + +// Enable/disable a clipping plane. +void CD3D9Driver::enableClipPlane(u32 index, bool enable) +{ + if (index >= MaxUserClipPlanes) + return; + DWORD renderstate; + HRESULT ok = pID3DDevice->GetRenderState(D3DRS_CLIPPLANEENABLE, &renderstate); + if (S_OK == ok) + { + if (enable) + renderstate |= (1 << index); + else + renderstate &= ~(1 << index); + ok = pID3DDevice->SetRenderState(D3DRS_CLIPPLANEENABLE, renderstate); + } +} + + +D3DFORMAT CD3D9Driver::getD3DFormatFromColorFormat(ECOLOR_FORMAT format) const +{ + switch(format) + { + case ECF_A1R5G5B5: + return D3DFMT_A1R5G5B5; + case ECF_R5G6B5: + return D3DFMT_R5G6B5; + case ECF_R8G8B8: + return D3DFMT_R8G8B8; + case ECF_A8R8G8B8: + return D3DFMT_A8R8G8B8; + + // Floating Point formats. Thanks to Patryk "Nadro" Nadrowski. + case ECF_R16F: + return D3DFMT_R16F; + case ECF_G16R16F: + return D3DFMT_G16R16F; + case ECF_A16B16G16R16F: + return D3DFMT_A16B16G16R16F; + case ECF_R32F: + return D3DFMT_R32F; + case ECF_G32R32F: + return D3DFMT_G32R32F; + case ECF_A32B32G32R32F: + return D3DFMT_A32B32G32R32F; + } + return D3DFMT_UNKNOWN; +} + + +ECOLOR_FORMAT CD3D9Driver::getColorFormatFromD3DFormat(D3DFORMAT format) const +{ + switch(format) + { + case D3DFMT_X1R5G5B5: + case D3DFMT_A1R5G5B5: + return ECF_A1R5G5B5; + case D3DFMT_A8B8G8R8: + case D3DFMT_A8R8G8B8: + case D3DFMT_X8R8G8B8: + return ECF_A8R8G8B8; + case D3DFMT_R5G6B5: + return ECF_R5G6B5; + case D3DFMT_R8G8B8: + return ECF_R8G8B8; + + // Floating Point formats. Thanks to Patryk "Nadro" Nadrowski. + case D3DFMT_R16F: + return ECF_R16F; + case D3DFMT_G16R16F: + return ECF_G16R16F; + case D3DFMT_A16B16G16R16F: + return ECF_A16B16G16R16F; + case D3DFMT_R32F: + return ECF_R32F; + case D3DFMT_G32R32F: + return ECF_G32R32F; + case D3DFMT_A32B32G32R32F: + return ECF_A32B32G32R32F; + default: + return (ECOLOR_FORMAT)0; + }; +} + + +void CD3D9Driver::checkDepthBuffer(ITexture* tex) +{ + if (!tex) + return; + const core::dimension2du optSize = tex->getSize().getOptimalSize( + !queryFeature(EVDF_TEXTURE_NPOT), + !queryFeature(EVDF_TEXTURE_NSQUARE), true); + SDepthSurface* depth=0; + core::dimension2du destSize(0x7fffffff, 0x7fffffff); + for (u32 i=0; iSize.Width>=optSize.Width) && + (DepthBuffers[i]->Size.Height>=optSize.Height)) + { + if ((DepthBuffers[i]->Size.WidthSize.HeightSize; + } + } + } + if (!depth) + { + D3DSURFACE_DESC desc; + DepthBuffers[0]->Surface->GetDesc(&desc); + // the multisampling needs to match the RTT + D3DSURFACE_DESC desc2; + ((CD3D9Texture*)tex)->Texture->GetLevelDesc(0,&desc2); + DepthBuffers.push_back(new SDepthSurface()); + HRESULT hr=pID3DDevice->CreateDepthStencilSurface(optSize.Width, + optSize.Height, + desc.Format, + desc2.MultiSampleType, + desc2.MultiSampleQuality, + TRUE, + &(DepthBuffers.getLast()->Surface), + NULL); + if (SUCCEEDED(hr)) + { + depth=DepthBuffers.getLast(); + depth->Surface->GetDesc(&desc); + depth->Size.set(desc.Width, desc.Height); + } + else + { + if (hr == D3DERR_OUTOFVIDEOMEMORY) + os::Printer::log("Could not create DepthBuffer","out of video memory",ELL_ERROR); + else if( hr == E_OUTOFMEMORY ) + os::Printer::log("Could not create DepthBuffer","out of memory",ELL_ERROR); + else + { + char buffer[128]; + sprintf(buffer,"Could not create DepthBuffer of %ix%i",optSize.Width,optSize.Height); + os::Printer::log(buffer,ELL_ERROR); + } + DepthBuffers.erase(DepthBuffers.size()-1); + } + } + else + depth->grab(); + + static_cast(tex)->DepthSurface=depth; +} + + +void CD3D9Driver::removeDepthSurface(SDepthSurface* depth) +{ + for (u32 i=0; iinitDriver(window, pureSoftware)) + { + dx9->drop(); + dx9 = 0; + } + + return dx9; +} +#endif // _IRR_COMPILE_WITH_DIRECT3D_9_ + +} // end namespace video +} // end namespace irr + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9Driver.h b/lib/irrlicht/source/Irrlicht/CD3D9Driver.h new file mode 100644 index 000000000..07c70b6f6 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9Driver.h @@ -0,0 +1,496 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_VIDEO_DIRECTX_9_H_INCLUDED__ +#define __C_VIDEO_DIRECTX_9_H_INCLUDED__ + +#include "IrrCompileConfig.h" + +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + +#ifdef _IRR_WINDOWS_ +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include "CNullDriver.h" +#include "SIrrCreationParameters.h" +#include "IMaterialRendererServices.h" +#if defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +#include "irrMath.h" // needed by borland for sqrtf define +#endif +#include + +#ifdef _IRR_COMPILE_WITH_CG_ +#include "Cg/cg.h" +#include "Cg/cgD3D9.h" +#endif + +namespace irr +{ +namespace video +{ + struct SDepthSurface : public IReferenceCounted + { + SDepthSurface() : Surface(0) + { + #ifdef _DEBUG + setDebugName("SDepthSurface"); + #endif + } + virtual ~SDepthSurface() + { + if (Surface) + Surface->Release(); + } + + IDirect3DSurface9* Surface; + core::dimension2du Size; + }; + + class CD3D9Driver : public CNullDriver, IMaterialRendererServices + { + public: + + friend class CD3D9Texture; + + //! constructor + CD3D9Driver(const SIrrlichtCreationParameters& params, io::IFileSystem* io); + + //! destructor + virtual ~CD3D9Driver(); + + //! applications must call this method before performing any rendering. returns false if failed. + virtual bool beginScene(bool backBuffer=true, bool zBuffer=true, + SColor color=SColor(255,0,0,0), + const SExposedVideoData& videoData=SExposedVideoData(), + core::rect* sourceRect=0); + + //! applications must call this method after performing any rendering. returns false if failed. + virtual bool endScene(); + + //! queries the features of the driver, returns true if feature is available + virtual bool queryFeature(E_VIDEO_DRIVER_FEATURE feature) const; + + //! sets transformation + virtual void setTransform(E_TRANSFORMATION_STATE state, const core::matrix4& mat); + + //! sets a material + virtual void setMaterial(const SMaterial& material); + + //! sets a render target + virtual bool setRenderTarget(video::ITexture* texture, + bool clearBackBuffer=true, bool clearZBuffer=true, + SColor color=video::SColor(0,0,0,0)); + + //! Sets multiple render targets + virtual bool setRenderTarget(const core::array& texture, + bool clearBackBuffer=true, bool clearZBuffer=true, + SColor color=video::SColor(0,0,0,0)); + + //! sets a viewport + virtual void setViewPort(const core::rect& area); + + //! gets the area of the current viewport + virtual const core::rect& getViewPort() const; + + struct SHWBufferLink_d3d9 : public SHWBufferLink + { + SHWBufferLink_d3d9(const scene::IMeshBuffer *_MeshBuffer): + SHWBufferLink(_MeshBuffer), + vertexBuffer(0), indexBuffer(0), + vertexBufferSize(0), indexBufferSize(0) {} + + IDirect3DVertexBuffer9* vertexBuffer; + IDirect3DIndexBuffer9* indexBuffer; + + u32 vertexBufferSize; + u32 indexBufferSize; + }; + + bool updateVertexHardwareBuffer(SHWBufferLink_d3d9 *HWBuffer); + bool updateIndexHardwareBuffer(SHWBufferLink_d3d9 *HWBuffer); + + //! updates hardware buffer if needed + virtual bool updateHardwareBuffer(SHWBufferLink *HWBuffer); + + //! Create hardware buffer from mesh + virtual SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer* mb); + + //! Delete hardware buffer (only some drivers can) + virtual void deleteHardwareBuffer(SHWBufferLink *HWBuffer); + + //! Draw hardware buffer + virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer); + + //! Create occlusion query. + /** Use node for identification and mesh for occlusion test. */ + virtual void addOcclusionQuery(scene::ISceneNode* node, + const scene::IMesh* mesh=0); + + //! Remove occlusion query. + virtual void removeOcclusionQuery(scene::ISceneNode* node); + + //! Run occlusion query. Draws mesh stored in query. + /** If the mesh shall not be rendered visible, use + overrideMaterial to disable the color and depth buffer. */ + virtual void runOcclusionQuery(scene::ISceneNode* node, bool visible=false); + + //! Update occlusion query. Retrieves results from GPU. + /** If the query shall not block, set the flag to false. + Update might not occur in this case, though */ + virtual void updateOcclusionQuery(scene::ISceneNode* node, bool block=true); + + //! Return query result. + /** Return value is the number of visible pixels/fragments. + The value is a safe approximation, i.e. can be larger then the + actual value of pixels. */ + virtual u32 getOcclusionQueryResult(scene::ISceneNode* node) const; + + //! draws a vertex primitive list + virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount, + const void* indexList, u32 primitiveCount, + E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, + E_INDEX_TYPE iType); + + //! draws a vertex primitive list in 2d + virtual void draw2DVertexPrimitiveList(const void* vertices, u32 vertexCount, + const void* indexList, u32 primitiveCount, + E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, + E_INDEX_TYPE iType); + + //! draws an 2d image, using a color (if color is other then Color(255,255,255,255)) and the alpha channel of the texture if wanted. + virtual void draw2DImage(const video::ITexture* texture, const core::position2d& destPos, + const core::rect& sourceRect, const core::rect* clipRect = 0, + SColor color=SColor(255,255,255,255), bool useAlphaChannelOfTexture=false); + + //! Draws a part of the texture into the rectangle. + virtual void draw2DImage(const video::ITexture* texture, const core::rect& destRect, + const core::rect& sourceRect, const core::rect* clipRect = 0, + const video::SColor* const colors=0, bool useAlphaChannelOfTexture=false); + + //! Draws a set of 2d images, using a color and the alpha channel of the texture. + virtual void draw2DImageBatch(const video::ITexture* texture, + const core::array >& positions, + const core::array >& sourceRects, + const core::rect* clipRect=0, + SColor color=SColor(255,255,255,255), + bool useAlphaChannelOfTexture=false); + + //!Draws an 2d rectangle with a gradient. + virtual void draw2DRectangle(const core::rect& pos, + SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown, + const core::rect* clip); + + //! Draws a 2d line. + virtual void draw2DLine(const core::position2d& start, + const core::position2d& end, + SColor color=SColor(255,255,255,255)); + + //! Draws a pixel. + virtual void drawPixel(u32 x, u32 y, const SColor & color); + + //! Draws a 3d line. + virtual void draw3DLine(const core::vector3df& start, + const core::vector3df& end, SColor color = SColor(255,255,255,255)); + + //! initialises the Direct3D API + bool initDriver(HWND hwnd, bool pureSoftware); + + //! \return Returns the name of the video driver. Example: In case of the DIRECT3D8 + //! driver, it would return "Direct3D8.1". + virtual const wchar_t* getName() const; + + //! deletes all dynamic lights there are + virtual void deleteAllDynamicLights(); + + //! adds a dynamic light, returning an index to the light + //! \param light: the light data to use to create the light + //! \return An index to the light, or -1 if an error occurs + virtual s32 addDynamicLight(const SLight& light); + + //! Turns a dynamic light on or off + //! \param lightIndex: the index returned by addDynamicLight + //! \param turnOn: true to turn the light on, false to turn it off + virtual void turnLightOn(s32 lightIndex, bool turnOn); + + //! returns the maximal amount of dynamic lights the device can handle + virtual u32 getMaximalDynamicLightAmount() const; + + //! Sets the dynamic ambient light color. The default color is + //! (0,0,0,0) which means it is dark. + //! \param color: New color of the ambient light. + virtual void setAmbientLight(const SColorf& color); + + //! Draws a shadow volume into the stencil buffer. + virtual void drawStencilShadowVolume(const core::array& triangles, bool zfail=true, u32 debugDataVisible=0); + + //! Fills the stencil shadow with color. + virtual void drawStencilShadow(bool clearStencilBuffer=false, + video::SColor leftUpEdge = video::SColor(0,0,0,0), + video::SColor rightUpEdge = video::SColor(0,0,0,0), + video::SColor leftDownEdge = video::SColor(0,0,0,0), + video::SColor rightDownEdge = video::SColor(0,0,0,0)); + + //! Returns the maximum amount of primitives (mostly vertices) which + //! the device is able to render with one drawIndexedTriangleList + //! call. + virtual u32 getMaximalPrimitiveCount() const; + + //! Enables or disables a texture creation flag. + virtual void setTextureCreationFlag(E_TEXTURE_CREATION_FLAG flag, bool enabled); + + //! Sets the fog mode. + virtual void setFog(SColor color, E_FOG_TYPE fogType, f32 start, + f32 end, f32 density, bool pixelFog, bool rangeFog); + + //! Only used by the internal engine. Used to notify the driver that + //! the window was resized. + virtual void OnResize(const core::dimension2d& size); + + //! Can be called by an IMaterialRenderer to make its work easier. + virtual void setBasicRenderStates(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates); + + //! Returns type of video driver + virtual E_DRIVER_TYPE getDriverType() const; + + //! Returns the transformation set by setTransform + virtual const core::matrix4& getTransform(E_TRANSFORMATION_STATE state) const; + + //! Sets a vertex shader constant. + virtual void setVertexShaderConstant(const f32* data, s32 startRegister, s32 constantAmount=1); + + //! Sets a pixel shader constant. + virtual void setPixelShaderConstant(const f32* data, s32 startRegister, s32 constantAmount=1); + + //! Sets a constant for the vertex shader based on a name. + virtual bool setVertexShaderConstant(const c8* name, const f32* floats, int count); + + //! Bool interface for the above. + virtual bool setVertexShaderConstant(const c8* name, const bool* bools, int count); + + //! Int interface for the above. + virtual bool setVertexShaderConstant(const c8* name, const s32* ints, int count); + + //! Sets a constant for the pixel shader based on a name. + virtual bool setPixelShaderConstant(const c8* name, const f32* floats, int count); + + //! Bool interface for the above. + virtual bool setPixelShaderConstant(const c8* name, const bool* bools, int count); + + //! Int interface for the above. + virtual bool setPixelShaderConstant(const c8* name, const s32* ints, int count); + + //! Returns a pointer to the IVideoDriver interface. (Implementation for + //! IMaterialRendererServices) + virtual IVideoDriver* getVideoDriver(); + + //! Creates a render target texture. + virtual ITexture* addRenderTargetTexture(const core::dimension2d& size, + const io::path& name, const ECOLOR_FORMAT format = ECF_UNKNOWN, const bool useStencil = false); + + //! Clears the ZBuffer. + virtual void clearZBuffer(); + + //! Returns an image created from the last rendered frame. + virtual IImage* createScreenShot(video::ECOLOR_FORMAT format=video::ECF_UNKNOWN, video::E_RENDER_TARGET target=video::ERT_FRAME_BUFFER); + + //! Set/unset a clipping plane. + virtual bool setClipPlane(u32 index, const core::plane3df& plane, bool enable=false); + + //! Enable/disable a clipping plane. + virtual void enableClipPlane(u32 index, bool enable); + + //! Returns the graphics card vendor name. + virtual core::stringc getVendorInfo() {return VendorName;} + + //! Enable the 2d override material + virtual void enableMaterial2D(bool enable=true); + + //! Check if the driver was recently reset. + virtual bool checkDriverReset() {return DriverWasReset;} + + // removes the depth struct from the DepthSurface array + void removeDepthSurface(SDepthSurface* depth); + + //! Get the current color format of the color buffer + /** \return Color format of the color buffer. */ + virtual ECOLOR_FORMAT getColorFormat() const; + + //! Returns the maximum texture size supported. + virtual core::dimension2du getMaxTextureSize() const; + + //! Get the current color format of the color buffer + /** \return Color format of the color buffer as D3D color value. */ + D3DFORMAT getD3DColorFormat() const; + + //! Get D3D color format from Irrlicht color format. + D3DFORMAT getD3DFormatFromColorFormat(ECOLOR_FORMAT format) const; + + //! Get Irrlicht color format from D3D color format. + ECOLOR_FORMAT getColorFormatFromD3DFormat(D3DFORMAT format) const; + + //! Get Cg context + #ifdef _IRR_COMPILE_WITH_CG_ + const CGcontext& getCgContext(); + #endif + + private: + + //! enumeration for rendering modes such as 2d and 3d for minizing the switching of renderStates. + enum E_RENDER_MODE + { + ERM_NONE = 0, // no render state has been set yet. + ERM_2D, // 2d drawing rendermode + ERM_3D, // 3d rendering mode + ERM_STENCIL_FILL, // stencil fill mode + ERM_SHADOW_VOLUME_ZFAIL, // stencil volume draw mode + ERM_SHADOW_VOLUME_ZPASS // stencil volume draw mode + }; + + //! sets right vertex shader + void setVertexShader(video::E_VERTEX_TYPE newType); + + //! sets the needed renderstates + bool setRenderStates3DMode(); + + //! sets the needed renderstates + void setRenderStates2DMode(bool alpha, bool texture, bool alphaChannel); + + //! sets the needed renderstates + void setRenderStatesStencilFillMode(bool alpha); + + //! sets the needed renderstates + void setRenderStatesStencilShadowMode(bool zfail, u32 debugDataVisible); + + //! sets the current Texture + bool setActiveTexture(u32 stage, const video::ITexture* texture); + + //! resets the device + bool reset(); + + //! returns a device dependent texture from a software surface (IImage) + //! THIS METHOD HAS TO BE OVERRIDDEN BY DERIVED DRIVERS WITH OWN TEXTURES + virtual video::ITexture* createDeviceDependentTexture(IImage* surface, const io::path& name, void* mipmapData=0); + + //! returns the current size of the screen or rendertarget + virtual const core::dimension2d& getCurrentRenderTargetSize() const; + + //! Check if a proper depth buffer for the RTT is available, otherwise create it. + void checkDepthBuffer(ITexture* tex); + + //! Adds a new material renderer to the VideoDriver, using pixel and/or + //! vertex shaders to render geometry. + s32 addShaderMaterial(const c8* vertexShaderProgram, const c8* pixelShaderProgram, + IShaderConstantSetCallBack* callback, + E_MATERIAL_TYPE baseMaterial, s32 userData); + + //! Adds a new material renderer to the VideoDriver, based on a high level shading + //! language. + virtual s32 addHighLevelShaderMaterial( + const c8* vertexShaderProgram, + const c8* vertexShaderEntryPointName, + E_VERTEX_SHADER_TYPE vsCompileTarget, + const c8* pixelShaderProgram, + const c8* pixelShaderEntryPointName, + E_PIXEL_SHADER_TYPE psCompileTarget, + const c8* geometryShaderProgram, + const c8* geometryShaderEntryPointName = "main", + E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0, + scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, + scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, + u32 verticesOut = 0, + IShaderConstantSetCallBack* callback = 0, + E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, + s32 userData = 0, + E_GPU_SHADING_LANGUAGE shadingLang = EGSL_DEFAULT); + + void createMaterialRenderers(); + + void draw2D3DVertexPrimitiveList(const void* vertices, + u32 vertexCount, const void* indexList, u32 primitiveCount, + E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, + E_INDEX_TYPE iType, bool is3D); + + D3DTEXTUREADDRESS getTextureWrapMode(const u8 clamp); + + inline D3DCOLORVALUE colorToD3D(const SColor& col) + { + const f32 f = 1.0f / 255.0f; + D3DCOLORVALUE v; + v.r = col.getRed() * f; + v.g = col.getGreen() * f; + v.b = col.getBlue() * f; + v.a = col.getAlpha() * f; + return v; + } + + E_RENDER_MODE CurrentRenderMode; + D3DPRESENT_PARAMETERS present; + + SMaterial Material, LastMaterial; + bool ResetRenderStates; // bool to make all renderstates be reseted if set. + 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. + + HINSTANCE D3DLibrary; + IDirect3D9* pID3D; + IDirect3DDevice9* pID3DDevice; + + IDirect3DSurface9* PrevRenderTarget; + core::dimension2d CurrentRendertargetSize; + + HWND WindowId; + core::rect* SceneSourceRect; + + D3DCAPS9 Caps; + + SIrrlichtCreationParameters Params; + + E_VERTEX_TYPE LastVertexType; + + SColorf AmbientLight; + + core::stringc VendorName; + u16 VendorID; + + core::array DepthBuffers; + + u32 MaxTextureUnits; + u32 MaxUserClipPlanes; + u32 MaxMRTs; + u32 NumSetMRTs; + f32 MaxLightDistance; + s32 LastSetLight; + + enum E_CACHE_2D_ATTRIBUTES + { + EC2D_ALPHA = 0x1, + EC2D_TEXTURE = 0x2, + EC2D_ALPHA_CHANNEL = 0x4 + }; + + ECOLOR_FORMAT ColorFormat; + D3DFORMAT D3DColorFormat; + bool DeviceLost; + bool DriverWasReset; + bool OcclusionQuerySupport; + bool AlphaToCoverageSupport; + + #ifdef _IRR_COMPILE_WITH_CG_ + CGcontext CgContext; + #endif + }; + + +} // end namespace video +} // end namespace irr + + +#endif // _IRR_COMPILE_WITH_DIRECT3D_9_ +#endif // __C_VIDEO_DIRECTX_9_H_INCLUDED__ + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9HLSLMaterialRenderer.cpp b/lib/irrlicht/source/Irrlicht/CD3D9HLSLMaterialRenderer.cpp new file mode 100644 index 000000000..8b5019978 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9HLSLMaterialRenderer.cpp @@ -0,0 +1,429 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "IrrCompileConfig.h" +#define _IRR_D3D_NO_SHADER_DEBUGGING 1 +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + +#include "CD3D9HLSLMaterialRenderer.h" +#include "IShaderConstantSetCallBack.h" +#include "IVideoDriver.h" +#include "os.h" +#include "irrString.h" + +#ifndef _IRR_D3D_NO_SHADER_DEBUGGING +#include +#endif + + +namespace irr +{ +namespace video +{ + + +//! Public constructor +CD3D9HLSLMaterialRenderer::CD3D9HLSLMaterialRenderer(IDirect3DDevice9* d3ddev, + video::IVideoDriver* driver, s32& outMaterialTypeNr, + const c8* vertexShaderProgram, + const c8* vertexShaderEntryPointName, + E_VERTEX_SHADER_TYPE vsCompileTarget, + const c8* pixelShaderProgram, + const c8* pixelShaderEntryPointName, + E_PIXEL_SHADER_TYPE psCompileTarget, + IShaderConstantSetCallBack* callback, + IMaterialRenderer* baseMaterial, + s32 userData) + : CD3D9ShaderMaterialRenderer(d3ddev, driver, callback, baseMaterial, userData), + VSConstantsTable(0), PSConstantsTable(0) +{ + + #ifdef _DEBUG + setDebugName("CD3D9HLSLMaterialRenderer"); + #endif + + outMaterialTypeNr = -1; + + // now create shaders + + if (vsCompileTarget < 0 || vsCompileTarget > EVST_COUNT) + { + os::Printer::log("Invalid HLSL vertex shader compilation target", ELL_ERROR); + return; + } + + if (!createHLSLVertexShader(vertexShaderProgram, + vertexShaderEntryPointName, VERTEX_SHADER_TYPE_NAMES[vsCompileTarget])) + return; + + if (!createHLSLPixelShader(pixelShaderProgram, + pixelShaderEntryPointName, PIXEL_SHADER_TYPE_NAMES[psCompileTarget])) + return; + + // register myself as new material + outMaterialTypeNr = Driver->addMaterialRenderer(this); +} + + +//! Destructor +CD3D9HLSLMaterialRenderer::~CD3D9HLSLMaterialRenderer() +{ + if (VSConstantsTable) + VSConstantsTable->Release(); + + if (PSConstantsTable) + PSConstantsTable->Release(); +} + + +bool CD3D9HLSLMaterialRenderer::createHLSLVertexShader(const char* vertexShaderProgram, + const char* shaderEntryPointName, + const char* shaderTargetName) +{ + if (!vertexShaderProgram) + return true; + + LPD3DXBUFFER buffer = 0; + LPD3DXBUFFER errors = 0; + +#ifdef _IRR_D3D_NO_SHADER_DEBUGGING + + // compile without debug info + HRESULT h = stubD3DXCompileShader( + vertexShaderProgram, + strlen(vertexShaderProgram), + 0, // macros + 0, // no includes + shaderEntryPointName, + shaderTargetName, + 0, // no flags + &buffer, + &errors, + &VSConstantsTable); + +#else + + // compile shader and emitt some debug informations to + // make it possible to debug the shader in visual studio + + static int irr_dbg_hlsl_file_nr = 0; + ++irr_dbg_hlsl_file_nr; + char tmp[32]; + sprintf(tmp, "irr_d3d9_dbg_hlsl_%d.vsh", irr_dbg_hlsl_file_nr); + + FILE* f = fopen(tmp, "wb"); + fwrite(vertexShaderProgram, strlen(vertexShaderProgram), 1, f); + fflush(f); + fclose(f); + + HRESULT h = stubD3DXCompileShaderFromFile( + tmp, + 0, // macros + 0, // no includes + shaderEntryPointName, + shaderTargetName, + D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION, + &buffer, + &errors, + &VSConstantsTable); + +#endif + + if (FAILED(h)) + { + os::Printer::log("HLSL vertex shader compilation failed:", ELL_ERROR); + if (errors) + { + os::Printer::log((c8*)errors->GetBufferPointer(), ELL_ERROR); + errors->Release(); + if (buffer) + buffer->Release(); + } + return false; + } + + if (errors) + errors->Release(); + + if (buffer) + { + if (FAILED(pID3DDevice->CreateVertexShader((DWORD*)buffer->GetBufferPointer(), + &VertexShader))) + { + os::Printer::log("Could not create hlsl vertex shader.", ELL_ERROR); + buffer->Release(); + return false; + } + + buffer->Release(); + return true; + } + + return false; +} + + +bool CD3D9HLSLMaterialRenderer::createHLSLPixelShader(const char* pixelShaderProgram, + const char* shaderEntryPointName, + const char* shaderTargetName) +{ + if (!pixelShaderProgram) + return true; + + LPD3DXBUFFER buffer = 0; + LPD3DXBUFFER errors = 0; + + DWORD flags = 0; + +#ifdef D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY + if (Driver->queryFeature(video::EVDF_VERTEX_SHADER_2_0) || Driver->queryFeature(video::EVDF_VERTEX_SHADER_3_0)) + // this one's for newer DX SDKs which don't support ps_1_x anymore + // instead they'll silently compile 1_x as 2_x when using this flag + flags |= D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY; +#endif +#if defined(_IRR_D3D_USE_LEGACY_HLSL_COMPILER) && defined(D3DXSHADER_USE_LEGACY_D3DX9_31_DLL) +#ifdef D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY + else +#endif + flags |= D3DXSHADER_USE_LEGACY_D3DX9_31_DLL; +#endif + +#ifdef _IRR_D3D_NO_SHADER_DEBUGGING + + // compile without debug info + HRESULT h = stubD3DXCompileShader( + pixelShaderProgram, + strlen(pixelShaderProgram), + 0, // macros + 0, // no includes + shaderEntryPointName, + shaderTargetName, + flags, + &buffer, + &errors, + &PSConstantsTable); + +#else + + // compile shader and emitt some debug informations to + // make it possible to debug the shader in visual studio + + static int irr_dbg_hlsl_file_nr = 0; + ++irr_dbg_hlsl_file_nr; + char tmp[32]; + sprintf(tmp, "irr_d3d9_dbg_hlsl_%d.psh", irr_dbg_hlsl_file_nr); + + FILE* f = fopen(tmp, "wb"); + fwrite(pixelShaderProgram, strlen(pixelShaderProgram), 1, f); + fflush(f); + fclose(f); + + HRESULT h = stubD3DXCompileShaderFromFile( + tmp, + 0, // macros + 0, // no includes + shaderEntryPointName, + shaderTargetName, + flags | D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION, + &buffer, + &errors, + &PSConstantsTable); + +#endif + + if (FAILED(h)) + { + os::Printer::log("HLSL pixel shader compilation failed:", ELL_ERROR); + if (errors) + { + os::Printer::log((c8*)errors->GetBufferPointer(), ELL_ERROR); + errors->Release(); + if (buffer) + buffer->Release(); + } + return false; + } + + if (errors) + errors->Release(); + + if (buffer) + { + if (FAILED(pID3DDevice->CreatePixelShader((DWORD*)buffer->GetBufferPointer(), + &PixelShader))) + { + os::Printer::log("Could not create hlsl pixel shader.", ELL_ERROR); + buffer->Release(); + return false; + } + + buffer->Release(); + return true; + } + + return false; +} + + +bool CD3D9HLSLMaterialRenderer::setVariable(bool vertexShader, const c8* name, + const f32* floats, int count) +{ + LPD3DXCONSTANTTABLE tbl = vertexShader ? VSConstantsTable : PSConstantsTable; + if (!tbl) + return false; + + // currently we only support top level parameters. + // Should be enough for the beginning. (TODO) + + D3DXHANDLE hndl = tbl->GetConstantByName(NULL, name); + if (!hndl) + { + core::stringc s = "HLSL Variable to set not found: '"; + s += name; + s += "'. Available variables are:"; + os::Printer::log(s.c_str(), ELL_WARNING); + printHLSLVariables(tbl); + return false; + } + + D3DXCONSTANT_DESC Description; + UINT ucount = 1; + tbl->GetConstantDesc(hndl, &Description, &ucount); + + if(Description.RegisterSet != D3DXRS_SAMPLER) + { + HRESULT hr = tbl->SetFloatArray(pID3DDevice, hndl, floats, count); + if (FAILED(hr)) + { + os::Printer::log("Error setting float array for HLSL variable", ELL_WARNING); + return false; + } + } + + return true; +} + + +bool CD3D9HLSLMaterialRenderer::setVariable(bool vertexShader, const c8* name, + const bool* bools, int count) +{ + LPD3DXCONSTANTTABLE tbl = vertexShader ? VSConstantsTable : PSConstantsTable; + if (!tbl) + return false; + + // currently we only support top level parameters. + // Should be enough for the beginning. (TODO) + + D3DXHANDLE hndl = tbl->GetConstantByName(NULL, name); + if (!hndl) + { + core::stringc s = "HLSL Variable to set not found: '"; + s += name; + s += "'. Available variables are:"; + os::Printer::log(s.c_str(), ELL_WARNING); + printHLSLVariables(tbl); + return false; + } + + D3DXCONSTANT_DESC Description; + UINT ucount = 1; + tbl->GetConstantDesc(hndl, &Description, &ucount); + + if(Description.RegisterSet != D3DXRS_SAMPLER) + { + HRESULT hr = tbl->SetBoolArray(pID3DDevice, hndl, (BOOL*)bools, count); + if (FAILED(hr)) + { + os::Printer::log("Error setting bool array for HLSL variable", ELL_WARNING); + return false; + } + } + + return true; +} + + +bool CD3D9HLSLMaterialRenderer::setVariable(bool vertexShader, const c8* name, + const s32* ints, int count) +{ + LPD3DXCONSTANTTABLE tbl = vertexShader ? VSConstantsTable : PSConstantsTable; + if (!tbl) + return false; + + // currently we only support top level parameters. + // Should be enough for the beginning. (TODO) + + D3DXHANDLE hndl = tbl->GetConstantByName(NULL, name); + if (!hndl) + { + core::stringc s = "HLSL Variable to set not found: '"; + s += name; + s += "'. Available variables are:"; + os::Printer::log(s.c_str(), ELL_WARNING); + printHLSLVariables(tbl); + return false; + } + + D3DXCONSTANT_DESC Description; + UINT ucount = 1; + tbl->GetConstantDesc(hndl, &Description, &ucount); + + if(Description.RegisterSet != D3DXRS_SAMPLER) + { + HRESULT hr = tbl->SetIntArray(pID3DDevice, hndl, ints, count); + if (FAILED(hr)) + { + os::Printer::log("Error setting int array for HLSL variable", ELL_WARNING); + return false; + } + } + + return true; +} + + +bool CD3D9HLSLMaterialRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype) +{ + if (VSConstantsTable) + VSConstantsTable->SetDefaults(pID3DDevice); + + return CD3D9ShaderMaterialRenderer::OnRender(service, vtxtype); +} + + +void CD3D9HLSLMaterialRenderer::printHLSLVariables(LPD3DXCONSTANTTABLE table) +{ + // currently we only support top level parameters. + // Should be enough for the beginning. (TODO) + + // print out constant names + D3DXCONSTANTTABLE_DESC tblDesc; + HRESULT hr = table->GetDesc(&tblDesc); + if (!FAILED(hr)) + { + for (int i=0; i<(int)tblDesc.Constants; ++i) + { + D3DXCONSTANT_DESC d; + UINT n = 1; + D3DXHANDLE cHndl = table->GetConstant(NULL, i); + if (!FAILED(table->GetConstantDesc(cHndl, &d, &n))) + { + core::stringc s = " '"; + s += d.Name; + s += "' Registers:[begin:"; + s += (int)d.RegisterIndex; + s += ", count:"; + s += (int)d.RegisterCount; + s += "]"; + os::Printer::log(s.c_str()); + } + } + } +} + + +} // end namespace video +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_DIRECT3D_9_ diff --git a/lib/irrlicht/source/Irrlicht/CD3D9HLSLMaterialRenderer.h b/lib/irrlicht/source/Irrlicht/CD3D9HLSLMaterialRenderer.h new file mode 100644 index 000000000..2e051b7b3 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9HLSLMaterialRenderer.h @@ -0,0 +1,85 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_D3D9_HLSL_MATERIAL_RENDERER_H_INCLUDED__ +#define __C_D3D9_HLSL_MATERIAL_RENDERER_H_INCLUDED__ + +#include "IrrCompileConfig.h" +#ifdef _IRR_WINDOWS_ + +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + +#include "CD3D9ShaderMaterialRenderer.h" +#include "IGPUProgrammingServices.h" + +namespace irr +{ +namespace video +{ + +class IVideoDriver; +class IShaderConstantSetCallBack; +class IMaterialRenderer; + +//! Class for using vertex and pixel shaders via HLSL with D3D9 +class CD3D9HLSLMaterialRenderer : public CD3D9ShaderMaterialRenderer +{ +public: + + //! Public constructor + CD3D9HLSLMaterialRenderer(IDirect3DDevice9* d3ddev, video::IVideoDriver* driver, + s32& outMaterialTypeNr, + const c8* vertexShaderProgram, + const c8* vertexShaderEntryPointName, + E_VERTEX_SHADER_TYPE vsCompileTarget, + const c8* pixelShaderProgram, + const c8* pixelShaderEntryPointName, + E_PIXEL_SHADER_TYPE psCompileTarget, + IShaderConstantSetCallBack* callback, + IMaterialRenderer* baseMaterial, + s32 userData); + + //! Destructor + ~CD3D9HLSLMaterialRenderer(); + + //! sets a variable in the shader. + //! \param vertexShader: True if this should be set in the vertex shader, false if + //! in the pixel shader. + //! \param name: Name of the variable + //! \param floats: Pointer to array of floats + //! \param count: Amount of floats in array. + virtual bool setVariable(bool vertexShader, const c8* name, const f32* floats, int count); + + //! Bool interface for the above. + virtual bool setVariable(bool vertexShader, const c8* name, const bool* bools, int count); + + //! Int interface for the above. + virtual bool setVariable(bool vertexShader, const c8* name, const s32* ints, int count); + + bool OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype); + +protected: + + bool createHLSLVertexShader(const char* vertexShaderProgram, + const char* shaderEntryPointName, + const char* shaderTargetName); + + bool createHLSLPixelShader(const char* pixelShaderProgram, + const char* shaderEntryPointName, + const char* shaderTargetName); + + void printHLSLVariables(LPD3DXCONSTANTTABLE table); + + LPD3DXCONSTANTTABLE VSConstantsTable; + LPD3DXCONSTANTTABLE PSConstantsTable; +}; + + +} // end namespace video +} // end namespace irr + +#endif +#endif +#endif + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9MaterialRenderer.h b/lib/irrlicht/source/Irrlicht/CD3D9MaterialRenderer.h new file mode 100644 index 000000000..16536617e --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9MaterialRenderer.h @@ -0,0 +1,615 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_D3D9_MATERIAL_RENDERER_H_INCLUDED__ +#define __C_D3D9_MATERIAL_RENDERER_H_INCLUDED__ + +#include "IrrCompileConfig.h" +#ifdef _IRR_WINDOWS_ + +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ +#if defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +#include "irrMath.h" // needed by borland for sqrtf define +#endif +#include + +#include "IMaterialRenderer.h" + +namespace irr +{ +namespace video +{ + +namespace +{ +D3DMATRIX UnitMatrixD3D9; +D3DMATRIX SphereMapMatrixD3D9; +inline void setTextureColorStage(IDirect3DDevice9* dev, DWORD i, + DWORD arg1, DWORD op, DWORD arg2) +{ + dev->SetTextureStageState(i, D3DTSS_COLOROP, op); + dev->SetTextureStageState(i, D3DTSS_COLORARG1, arg1); + dev->SetTextureStageState(i, D3DTSS_COLORARG2, arg2); +} +inline void setTextureColorStage(IDirect3DDevice9* dev, DWORD i, DWORD arg1) +{ + dev->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + dev->SetTextureStageState(i, D3DTSS_COLORARG1, arg1); +} + +inline void setTextureAlphaStage(IDirect3DDevice9* dev, DWORD i, + DWORD arg1, DWORD op, DWORD arg2) +{ + dev->SetTextureStageState(i, D3DTSS_ALPHAOP, op); + dev->SetTextureStageState(i, D3DTSS_ALPHAARG1, arg1); + dev->SetTextureStageState(i, D3DTSS_ALPHAARG2, arg2); +} +inline void setTextureAlphaStage(IDirect3DDevice9* dev, DWORD i, DWORD arg1) +{ + dev->SetTextureStageState(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + dev->SetTextureStageState(i, D3DTSS_ALPHAARG1, arg1); +} +} // anonymous namespace + +//! Base class for all internal D3D9 material renderers +class CD3D9MaterialRenderer : public IMaterialRenderer +{ +public: + + //! Constructor + CD3D9MaterialRenderer(IDirect3DDevice9* d3ddev, video::IVideoDriver* driver) + : pID3DDevice(d3ddev), Driver(driver) + { + } + + //! sets a variable in the shader. + //! \param vertexShader: True if this should be set in the vertex shader, false if + //! in the pixel shader. + //! \param name: Name of the variable + //! \param floats: Pointer to array of floats + //! \param count: Amount of floats in array. + virtual bool setVariable(bool vertexShader, const c8* name, const f32* floats, int count) + { + os::Printer::log("Invalid material to set variable in."); + return false; + } + + //! Bool interface for the above. + virtual bool setVariable(bool vertexShader, const c8* name, const bool* bools, int count) + { + os::Printer::log("Invalid material to set variable in."); + return false; + } + + //! Int interface for the above. + virtual bool setVariable(bool vertexShader, const c8* name, const s32* ints, int count) + { + os::Printer::log("Invalid material to set variable in."); + return false; + } + +protected: + + IDirect3DDevice9* pID3DDevice; + video::IVideoDriver* Driver; +}; + + +//! Solid material renderer +class CD3D9MaterialRenderer_SOLID : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_SOLID(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + } + + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } +}; + +//! Generic Texture Blend +class CD3D9MaterialRenderer_ONETEXTURE_BLEND : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_ONETEXTURE_BLEND(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || + material.MaterialTypeParam != lastMaterial.MaterialTypeParam || + resetAllRenderstates) + { + + E_BLEND_FACTOR srcFact,dstFact; + E_MODULATE_FUNC modulate; + u32 alphaSource; + unpack_textureBlendFunc ( srcFact, dstFact, modulate, alphaSource, material.MaterialTypeParam ); + + if (srcFact == EBF_SRC_COLOR && dstFact == EBF_ZERO) + { + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } + else + { + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_SRCBLEND, getD3DBlend ( srcFact ) ); + pID3DDevice->SetRenderState(D3DRS_DESTBLEND, getD3DBlend ( dstFact ) ); + } + + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, getD3DModulate(modulate), D3DTA_DIFFUSE); + + if ( textureBlendFunc_hasAlpha ( srcFact ) || textureBlendFunc_hasAlpha ( dstFact ) ) + { + if (alphaSource==EAS_VERTEX_COLOR) + { + setTextureAlphaStage(pID3DDevice, 0, D3DTA_DIFFUSE); + } + else if (alphaSource==EAS_TEXTURE) + { + setTextureAlphaStage(pID3DDevice, 0, D3DTA_TEXTURE); + } + else + { + setTextureAlphaStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + } + } + + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + + } + } + + //! Returns if the material is transparent. + /** The scene management needs to know this for being able to sort the + materials by opaque and transparent. + The return value could be optimized, but we'd need to know the + MaterialTypeParam for it. */ + virtual bool isTransparent() const + { + return true; + } + + private: + + u32 getD3DBlend ( E_BLEND_FACTOR factor ) const + { + u32 r = 0; + switch ( factor ) + { + case EBF_ZERO: r = D3DBLEND_ZERO; break; + case EBF_ONE: r = D3DBLEND_ONE; break; + case EBF_DST_COLOR: r = D3DBLEND_DESTCOLOR; break; + case EBF_ONE_MINUS_DST_COLOR: r = D3DBLEND_INVDESTCOLOR; break; + case EBF_SRC_COLOR: r = D3DBLEND_SRCCOLOR; break; + case EBF_ONE_MINUS_SRC_COLOR: r = D3DBLEND_INVSRCCOLOR; break; + case EBF_SRC_ALPHA: r = D3DBLEND_SRCALPHA; break; + case EBF_ONE_MINUS_SRC_ALPHA: r = D3DBLEND_INVSRCALPHA; break; + case EBF_DST_ALPHA: r = D3DBLEND_DESTALPHA; break; + case EBF_ONE_MINUS_DST_ALPHA: r = D3DBLEND_INVDESTALPHA; break; + case EBF_SRC_ALPHA_SATURATE: r = D3DBLEND_SRCALPHASAT; break; + } + return r; + } + + u32 getD3DModulate ( E_MODULATE_FUNC func ) const + { + u32 r = D3DTOP_MODULATE; + switch ( func ) + { + case EMFN_MODULATE_1X: r = D3DTOP_MODULATE; break; + case EMFN_MODULATE_2X: r = D3DTOP_MODULATE2X; break; + case EMFN_MODULATE_4X: r = D3DTOP_MODULATE4X; break; + } + return r; + } + + bool transparent; + +}; + + + +//! Solid 2 layer material renderer +class CD3D9MaterialRenderer_SOLID_2_LAYER : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_SOLID_2_LAYER(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, D3DTA_TEXTURE); + + pID3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0); + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDDIFFUSEALPHA); + + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } + } +}; + + +//! Transparent add color material renderer +class CD3D9MaterialRenderer_TRANSPARENT_ADD_COLOR : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_TRANSPARENT_ADD_COLOR(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR); + } + } + + //! Returns if the material is transparent. The scene management needs to know this + //! for being able to sort the materials by opaque and transparent. + virtual bool isTransparent() const + { + return true; + } +}; + + +//! Transparent vertex alpha material renderer +class CD3D9MaterialRenderer_TRANSPARENT_VERTEX_ALPHA : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_TRANSPARENT_VERTEX_ALPHA(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_DIFFUSE); + + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + } + + //! Returns if the material is transparent. The scene managment needs to know this + //! for being able to sort the materials by opaque and transparent. + virtual bool isTransparent() const + { + return true; + } +}; + + +//! Transparent alpha channel material renderer +class CD3D9MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates + || material.MaterialTypeParam != lastMaterial.MaterialTypeParam ) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_CURRENT); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_TEXTURE); + + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + + pID3DDevice->SetRenderState(D3DRS_ALPHAREF, core::floor32(material.MaterialTypeParam * 255.f)); + pID3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); + pID3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); + } + } + + virtual void OnUnsetMaterial() + { + pID3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + } + + //! Returns if the material is transparent. The scene managment needs to know this + //! for being able to sort the materials by opaque and transparent. + virtual bool isTransparent() const + { + return true; + } +}; + + + +//! Transparent alpha channel material renderer +class CD3D9MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_CURRENT); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_TEXTURE); + + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + // 127 is required by EMT_TRANSPARENT_ALPHA_CHANNEL_REF + pID3DDevice->SetRenderState(D3DRS_ALPHAREF, 127); + pID3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); + pID3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); + } + } + + virtual void OnUnsetMaterial() + { + pID3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + } + + //! Returns if the material is transparent. The scene managment needs to know this + //! for being able to sort the materials by opaque and transparent. + virtual bool isTransparent() const + { + return false; // this material is not really transparent because it does no blending. + } +}; + + +//! material renderer for all kinds of lightmaps +class CD3D9MaterialRenderer_LIGHTMAP : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_LIGHTMAP(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + if (material.MaterialType >= EMT_LIGHTMAP_LIGHTING) + { + // with lighting + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + } + else + { + setTextureColorStage(pID3DDevice, 0, D3DTA_TEXTURE); + } + + pID3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); + + setTextureColorStage(pID3DDevice, 1, + D3DTA_TEXTURE, + (material.MaterialType == EMT_LIGHTMAP_ADD)? + D3DTOP_ADD: + (material.MaterialType == EMT_LIGHTMAP_M4 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M4)? + D3DTOP_MODULATE4X: + (material.MaterialType == EMT_LIGHTMAP_M2 || material.MaterialType == EMT_LIGHTMAP_LIGHTING_M2)? + D3DTOP_MODULATE2X: + D3DTOP_MODULATE, + D3DTA_CURRENT); + + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } + } +}; + + + +//! material renderer for detail maps +class CD3D9MaterialRenderer_DETAIL_MAP : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_DETAIL_MAP(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + setTextureColorStage(pID3DDevice, 1, + D3DTA_TEXTURE, D3DTOP_ADDSIGNED, D3DTA_CURRENT); + pID3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } + } +}; + + +//! sphere map material renderer +class CD3D9MaterialRenderer_SPHERE_MAP : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_SPHERE_MAP(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + + pID3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + pID3DDevice->SetTransform( D3DTS_TEXTURE0, &SphereMapMatrixD3D9 ); + pID3DDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); + pID3DDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL ); + } + } + + virtual void OnUnsetMaterial() + { + pID3DDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); + pID3DDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0); + pID3DDevice->SetTransform( D3DTS_TEXTURE0, &UnitMatrixD3D9 ); + } +}; + + +//! reflection 2 layer material renderer +class CD3D9MaterialRenderer_REFLECTION_2_LAYER : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_REFLECTION_2_LAYER(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + + setTextureColorStage(pID3DDevice, 1, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_CURRENT); + + pID3DDevice->SetTransform( D3DTS_TEXTURE1, &SphereMapMatrixD3D9 ); + pID3DDevice->SetTextureStageState( 1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); + pID3DDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR); + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } + } + + virtual void OnUnsetMaterial() + { + pID3DDevice->SetTextureStageState( 1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); + pID3DDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 1); + pID3DDevice->SetTransform( D3DTS_TEXTURE1, &UnitMatrixD3D9 ); + } +}; + + +//! reflection 2 layer material renderer +class CD3D9MaterialRenderer_TRANSPARENT_REFLECTION_2_LAYER : public CD3D9MaterialRenderer +{ +public: + + CD3D9MaterialRenderer_TRANSPARENT_REFLECTION_2_LAYER(IDirect3DDevice9* p, video::IVideoDriver* d) + : CD3D9MaterialRenderer(p, d) {} + + virtual void OnSetMaterial(const SMaterial& material, const SMaterial& lastMaterial, + bool resetAllRenderstates, IMaterialRendererServices* services) + { + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); + + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + setTextureColorStage(pID3DDevice, 0, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_DIFFUSE); + setTextureAlphaStage(pID3DDevice, 0, D3DTA_DIFFUSE); + setTextureColorStage(pID3DDevice, 1, + D3DTA_TEXTURE, D3DTOP_MODULATE, D3DTA_CURRENT); + setTextureAlphaStage(pID3DDevice, 1, D3DTA_CURRENT); + + pID3DDevice->SetTransform(D3DTS_TEXTURE1, &SphereMapMatrixD3D9 ); + pID3DDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); + pID3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR); + + pID3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + pID3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + pID3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + } + + virtual void OnUnsetMaterial() + { + pID3DDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE); + pID3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); + pID3DDevice->SetTransform(D3DTS_TEXTURE1, &UnitMatrixD3D9); + } + + //! Returns if the material is transparent. The scene managment needs to know this + //! for being able to sort the materials by opaque and transparent. + virtual bool isTransparent() const + { + return true; + } +}; + +} // end namespace video +} // end namespace irr + +#endif +#endif +#endif + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9NormalMapRenderer.cpp b/lib/irrlicht/source/Irrlicht/CD3D9NormalMapRenderer.cpp new file mode 100644 index 000000000..eb6f7324d --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9NormalMapRenderer.cpp @@ -0,0 +1,306 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "IrrCompileConfig.h" +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + +#include "CD3D9NormalMapRenderer.h" +#include "IVideoDriver.h" +#include "IMaterialRendererServices.h" +#include "os.h" +#include "SLight.h" + +namespace irr +{ +namespace video +{ + + // 1.1 Shaders with two lights and vertex based attenuation + + // Irrlicht Engine D3D9 render path normal map vertex shader + const char D3D9_NORMAL_MAP_VSH[] = + ";Irrlicht Engine 0.8 D3D9 render path normal map vertex shader\n"\ + "; c0-3: Transposed world matrix \n"\ + "; c8-11: Transposed worldViewProj matrix (Projection * View * World) \n"\ + "; c12: Light01 position \n"\ + "; c13: x,y,z: Light01 color; .w: 1/LightRadius˛ \n"\ + "; c14: Light02 position \n"\ + "; c15: x,y,z: Light02 color; .w: 1/LightRadius˛ \n"\ + "vs.1.1\n"\ + "dcl_position v0 ; position \n"\ + "dcl_normal v1 ; normal \n"\ + "dcl_color v2 ; color \n"\ + "dcl_texcoord0 v3 ; texture coord \n"\ + "dcl_texcoord1 v4 ; tangent \n"\ + "dcl_texcoord2 v5 ; binormal \n"\ + "\n"\ + "def c95, 0.5, 0.5, 0.5, 0.5 ; used for moving light vector to ps \n"\ + "\n"\ + "m4x4 oPos, v0, c8 ; transform position to clip space with worldViewProj matrix\n"\ + "\n"\ + "m3x3 r5, v4, c0 ; transform tangent U\n"\ + "m3x3 r7, v1, c0 ; transform normal W\n"\ + "m3x3 r6, v5, c0 ; transform binormal V\n"\ + "\n"\ + "m4x4 r4, v0, c0 ; vertex into world position\n"\ + "add r2, c12, -r4 ; vtxpos - lightpos1\n"\ + "add r3, c14, -r4 ; vtxpos - lightpos2\n"\ + "\n"\ + "dp3 r8.x, r5, r2 ; transform the light vector 1 with U, V, W\n"\ + "dp3 r8.y, r6, r2 \n"\ + "dp3 r8.z, r7, r2 \n"\ + "dp3 r9.x, r5, r3 ; transform the light vector 2 with U, V, W\n"\ + "dp3 r9.y, r6, r3 \n"\ + "dp3 r9.z, r7, r3 \n"\ + "\n"\ + "dp3 r8.w, r8, r8 ; normalize light vector 1 (r8)\n"\ + "rsq r8.w, r8.w \n"\ + "mul r8, r8, r8.w \n"\ + "dp3 r9.w, r9, r9 ; normalize light vector 2 (r9)\n"\ + "rsq r9.w, r9.w \n"\ + "mul r9, r9, r9.w \n"\ + "\n"\ + "mad oT2.xyz, r8.xyz, c95, c95 ; move light vector 1 from -1..1 into 0..1 \n"\ + "mad oT3.xyz, r9.xyz, c95, c95 ; move light vector 2 from -1..1 into 0..1 \n"\ + "\n"\ + " ; calculate attenuation of light 1 \n"\ + "dp3 r2.x, r2.xyz, r2.xyz ; r2.x = r2.x˛ + r2.y˛ + r2.z˛ \n"\ + "mul r2.x, r2.x, c13.w ; r2.x * attenutation \n"\ + "rsq r2, r2.x ; r2.xyzw = 1/sqrt(r2.x * attenutation)\n"\ + "mul oD0, r2, c13 ; resulting light color = lightcolor * attenuation \n"\ + "\n"\ + " ; calculate attenuation of light 2 \n"\ + "dp3 r3.x, r3.xyz, r3.xyz ; r3.x = r3.x˛ + r3.y˛ + r3.z˛ \n"\ + "mul r3.x, r3.x, c15.w ; r2.x * attenutation \n"\ + "rsq r3, r3.x ; r2.xyzw = 1/sqrt(r2.x * attenutation)\n"\ + "mul oD1, r3, c15 ; resulting light color = lightcolor * attenuation \n"\ + "\n"\ + "mov oT0.xy, v3.xy ; move out texture coordinates 1\n"\ + "mov oT1.xy, v3.xy ; move out texture coordinates 2\n"\ + "mov oD0.a, v2.a ; move out original alpha value \n"\ + "\n"; + + // Irrlicht Engine D3D9 render path normal map pixel shader + const char D3D9_NORMAL_MAP_PSH_1_1[] = + ";Irrlicht Engine 0.8 D3D9 render path normal map pixel shader\n"\ + ";Input: \n"\ + ";t0: color map texture coord \n"\ + ";t1: normal map texture coords \n"\ + ";t2: light 1 vector in tangent space \n"\ + ";v0: light 1 color \n"\ + ";t3: light 2 vector in tangent space \n"\ + ";v1: light 2 color \n"\ + ";v0.a: vertex alpha value \n"\ + "ps.1.1 \n"\ + "tex t0 ; sample color map \n"\ + "tex t1 ; sample normal map\n"\ + "texcoord t2 ; fetch light vector 1\n"\ + "texcoord t3 ; fetch light vector 2\n"\ + "\n"\ + "dp3_sat r0, t1_bx2, t2_bx2 ; normal dot light 1 (_bx2 because moved into 0..1)\n"\ + "mul r0, r0, v0 ; luminance1 * light color 1 \n"\ + "\n"\ + "dp3_sat r1, t1_bx2, t3_bx2 ; normal dot light 2 (_bx2 because moved into 0..1)\n"\ + "mad r0, r1, v1, r0 ; (luminance2 * light color 2) + luminance 1 \n"\ + "\n"\ + "mul r0.xyz, t0, r0 ; total luminance * base color\n"\ + "+mov r0.a, v0.a ; write interpolated vertex alpha value \n"\ + "\n"\ + ""; + + // Higher-quality normal map pixel shader (requires PS 2.0) + // uses per-pixel normalization for improved accuracy + const char D3D9_NORMAL_MAP_PSH_2_0[] = + ";Irrlicht Engine 0.8 D3D9 render path normal map pixel shader\n"\ + ";Input: \n"\ + ";t0: color map texture coord \n"\ + ";t1: normal map texture coords \n"\ + ";t2: light 1 vector in tangent space \n"\ + ";v0: light 1 color \n"\ + ";t3: light 2 vector in tangent space \n"\ + ";v1: light 2 color \n"\ + ";v0.a: vertex alpha value \n"\ + + "ps_2_0 \n"\ + "def c0, 0, 0, 0, 0\n"\ + "def c1, 1.0, 1.0, 1.0, 1.0\n"\ + "def c2, 2.0, 2.0, 2.0, 2.0\n"\ + "def c3, -.5, -.5, -.5, -.5\n"\ + "dcl t0\n"\ + "dcl t1\n"\ + "dcl t2\n"\ + "dcl t3\n"\ + "dcl v1\n"\ + "dcl v0\n"\ + "dcl_2d s0\n"\ + "dcl_2d s1\n"\ + + "texld r0, t0, s0 ; sample color map into r0 \n"\ + "texld r4, t0, s1 ; sample normal map into r4\n"\ + "add r4, r4, c3 ; bias the normal vector\n"\ + "add r5, t2, c3 ; bias the light 1 vector into r5\n"\ + "add r6, t3, c3 ; bias the light 2 vector into r6\n"\ + + "nrm r1, r4 ; normalize the normal vector into r1\n"\ + "nrm r2, r5 ; normalize the light1 vector into r2\n"\ + "nrm r3, r6 ; normalize the light2 vector into r3\n"\ + + "dp3 r2, r2, r1 ; let r2 = normal DOT light 1 vector\n"\ + "max r2, r2, c0 ; clamp result to positive numbers\n"\ + "mul r2, r2, v0 ; let r2 = luminance1 * light color 1 \n"\ + + "dp3 r3, r3, r1 ; let r3 = normal DOT light 2 vector\n"\ + "max r3, r3, c0 ; clamp result to positive numbers\n"\ + + "mad r2, r3, v1, r2 ; let r2 = (luminance2 * light color 2) + (luminance2 * light color 1) \n"\ + + "mul r2, r2, r0 ; let r2 = total luminance * base color\n"\ + "mov r2.w, v0.w ; write interpolated vertex alpha value \n"\ + + "mov oC0, r2 ; copy r2 to the output register \n"\ + + "\n"\ + ""; + + CD3D9NormalMapRenderer::CD3D9NormalMapRenderer( + IDirect3DDevice9* d3ddev, video::IVideoDriver* driver, + s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial) + : CD3D9ShaderMaterialRenderer(d3ddev, driver, 0, baseMaterial) + { + #ifdef _DEBUG + setDebugName("CD3D9NormalMapRenderer"); + #endif + + // set this as callback. We could have done this in + // the initialization list, but some compilers don't like it. + + CallBack = this; + + // basically, this thing simply compiles the hardcoded shaders + // if the hardware is able to do them, otherwise it maps to the + // base material + + if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) || + !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1)) + { + // this hardware is not able to do shaders. Fall back to + // base material. + outMaterialTypeNr = driver->addMaterialRenderer(this); + return; + } + + // check if already compiled normal map shaders are there. + + video::IMaterialRenderer* renderer = driver->getMaterialRenderer(EMT_NORMAL_MAP_SOLID); + if (renderer) + { + // use the already compiled shaders + video::CD3D9NormalMapRenderer* nmr = (video::CD3D9NormalMapRenderer*)renderer; + VertexShader = nmr->VertexShader; + if (VertexShader) + VertexShader->AddRef(); + + PixelShader = nmr->PixelShader; + if (PixelShader) + PixelShader->AddRef(); + + outMaterialTypeNr = driver->addMaterialRenderer(this); + } + else + { + // compile shaders on our own + if (driver->queryFeature(video::EVDF_PIXEL_SHADER_2_0)) + { + init(outMaterialTypeNr, D3D9_NORMAL_MAP_VSH, D3D9_NORMAL_MAP_PSH_2_0); + } + else + { + init(outMaterialTypeNr, D3D9_NORMAL_MAP_VSH, D3D9_NORMAL_MAP_PSH_1_1); + } + } + // something failed, use base material + if (-1==outMaterialTypeNr) + driver->addMaterialRenderer(this); + } + + + CD3D9NormalMapRenderer::~CD3D9NormalMapRenderer() + { + if (CallBack == this) + CallBack = 0; + } + + + bool CD3D9NormalMapRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype) + { + if (vtxtype != video::EVT_TANGENTS) + { + os::Printer::log("Error: Normal map renderer only supports vertices of type EVT_TANGENTS", ELL_ERROR); + return false; + } + + return CD3D9ShaderMaterialRenderer::OnRender(service, vtxtype); + } + + + //! Returns the render capability of the material. + s32 CD3D9NormalMapRenderer::getRenderCapability() const + { + if (Driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) && + Driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1)) + return 0; + + return 1; + } + + + //! Called by the engine when the vertex and/or pixel shader constants + //! for an material renderer should be set. + void CD3D9NormalMapRenderer::OnSetConstants(IMaterialRendererServices* services, s32 userData) + { + video::IVideoDriver* driver = services->getVideoDriver(); + + // set transposed world matrix + services->setVertexShaderConstant(driver->getTransform(video::ETS_WORLD).getTransposed().pointer(), 0, 4); + + // set transposed worldViewProj matrix + core::matrix4 worldViewProj(driver->getTransform(video::ETS_PROJECTION)); + worldViewProj *= driver->getTransform(video::ETS_VIEW); + worldViewProj *= driver->getTransform(video::ETS_WORLD); + services->setVertexShaderConstant(worldViewProj.getTransposed().pointer(), 8, 4); + + // here we've got to fetch the fixed function lights from the + // driver and set them as constants + + u32 cnt = driver->getDynamicLightCount(); + + for (u32 i=0; i<2; ++i) + { + SLight light; + + if (igetDynamicLight(i); + else + { + light.DiffuseColor.set(0,0,0); // make light dark + light.Radius = 1.0f; + } + + light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation + + services->setVertexShaderConstant(reinterpret_cast(&light.Position), 12+(i*2), 1); + services->setVertexShaderConstant(reinterpret_cast(&light.DiffuseColor), 13+(i*2), 1); + } + + // this is not really necessary in d3d9 (used a def instruction), but to be sure: + f32 c95[] = {0.5f, 0.5f, 0.5f, 0.5f}; + services->setVertexShaderConstant(c95, 95, 1); + } + + +} // end namespace video +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_DIRECT3D_9_ + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9NormalMapRenderer.h b/lib/irrlicht/source/Irrlicht/CD3D9NormalMapRenderer.h new file mode 100644 index 000000000..70b4a5ce3 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9NormalMapRenderer.h @@ -0,0 +1,56 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_D3D9_NORMAL_MAPMATERIAL_RENDERER_H_INCLUDED__ +#define __C_D3D9_NORMAL_MAPMATERIAL_RENDERER_H_INCLUDED__ + +#include "IrrCompileConfig.h" +#ifdef _IRR_WINDOWS_ + +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ +#if defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +#include "irrMath.h" // needed by borland for sqrtf define +#endif +#include + +#include "CD3D9ShaderMaterialRenderer.h" +#include "IShaderConstantSetCallBack.h" + +namespace irr +{ +namespace video +{ + +//! Renderer for normal maps +class CD3D9NormalMapRenderer : + public CD3D9ShaderMaterialRenderer, IShaderConstantSetCallBack +{ +public: + + CD3D9NormalMapRenderer( + IDirect3DDevice9* d3ddev, video::IVideoDriver* driver, + s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial); + + ~CD3D9NormalMapRenderer(); + + //! Called by the engine when the vertex and/or pixel shader constants for an + //! material renderer should be set. + virtual void OnSetConstants(IMaterialRendererServices* services, s32 userData); + + virtual bool OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype); + + //! Returns the render capability of the material. + virtual s32 getRenderCapability() const; + +private: + +}; + +} // end namespace video +} // end namespace irr + +#endif +#endif +#endif + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9ParallaxMapRenderer.cpp b/lib/irrlicht/source/Irrlicht/CD3D9ParallaxMapRenderer.cpp new file mode 100644 index 000000000..954a55204 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9ParallaxMapRenderer.cpp @@ -0,0 +1,410 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "IrrCompileConfig.h" +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + +#include "CD3D9ParallaxMapRenderer.h" +#include "IMaterialRendererServices.h" +#include "IVideoDriver.h" +#include "os.h" +#include "SLight.h" + +//#define SHADER_EXTERNAL_DEBUG + +#ifdef SHADER_EXTERNAL_DEBUG +#include "CReadFile.h" +#endif + +namespace irr +{ +namespace video +{ + // 1.1/1.4 Shaders with two lights and vertex based attenuation + + // Irrlicht Engine D3D9 render path normal map vertex shader + const char D3D9_PARALLAX_MAP_VSH[] = + ";Irrlicht Engine 0.10 D3D9 render path parallax mapping vertex shader\n"\ + "; c0-3: Transposed world matrix \n"\ + "; c4: Eye position \n"\ + "; c8-11: Transposed worldViewProj matrix (Projection * View * World) \n"\ + "; c12: Light01 position \n"\ + "; c13: x,y,z: Light01 color; .w: 1/LightRadius˛ \n"\ + "; c14: Light02 position \n"\ + "; c15: x,y,z: Light02 color; .w: 1/LightRadius˛ \n"\ + "vs.1.1\n"\ + "dcl_position v0 ; position \n"\ + "dcl_normal v1 ; normal \n"\ + "dcl_color v2 ; color \n"\ + "dcl_texcoord0 v3 ; texture coord \n"\ + "dcl_texcoord1 v4 ; tangent \n"\ + "dcl_texcoord2 v5 ; binormal \n"\ + "\n"\ + "def c95, 0.5, 0.5, 0.5, 0.5 ; used for moving light vector to ps \n"\ + "def c96, -1, 1, 1, 1 ; somewhere I've got a bug. flipping the vectors with this fixes it. \n"\ + "\n"\ + "m4x4 oPos, v0, c8 ; transform position to clip space with worldViewProj matrix\n"\ + "\n"\ + "m3x3 r5, v4, c0 ; transform tangent U\n"\ + "m3x3 r7, v1, c0 ; transform normal W\n"\ + "m3x3 r6, v5, c0 ; transform binormal V\n"\ + "\n"\ + "m4x4 r4, v0, c0 ; vertex into world position\n"\ + "add r2, c12, -r4 ; vtxpos - light1 pos\n"\ + "add r3, c14, -r4 ; vtxpos - light2 pos\n"\ + "add r1, -c4, r4 ; eye - vtxpos \n"\ + "\n"\ + "dp3 r8.x, r5, r2 ; transform the light1 vector with U, V, W\n"\ + "dp3 r8.y, r6, r2 \n"\ + "dp3 r8.z, r7, r2 \n"\ + "dp3 r9.x, r5, r3 ; transform the light2 vector with U, V, W\n"\ + "dp3 r9.y, r6, r3 \n"\ + "dp3 r9.z, r7, r3 \n"\ + "dp3 r10.x, r5, r1 ; transform the eye vector with U, V, W\n"\ + "dp3 r10.y, r6, r1 \n"\ + "dp3 r10.z, r7, r1 \n"\ + "\n"\ + "dp3 r8.w, r8, r8 ; normalize light vector 1 (r8)\n"\ + "rsq r8.w, r8.w \n"\ + "mul r8, r8, r8.w \n"\ + ";mul r8, r8, c96 \n"\ + "dp3 r9.w, r9, r9 ; normalize light vector 2 (r9)\n"\ + "rsq r9.w, r9.w \n"\ + "mul r9, r9, r9.w \n"\ + ";mul r9, r9, c96 \n"\ + "dp3 r10.w, r10, r10 ; normalize eye vector (r10)\n"\ + "rsq r10.w, r10.w \n"\ + "mul r10, r10, r10.w \n"\ + "mul r10, r10, c96 \n"\ + "\n"\ + "\n"\ + "mad oT2.xyz, r8.xyz, c95, c95 ; move light vector 1 from -1..1 into 0..1 \n"\ + "mad oT3.xyz, r9.xyz, c95, c95 ; move light vector 2 from -1..1 into 0..1 \n"\ + "mad oT4.xyz, r10.xyz, c95, c95 ; move eye vector from -1..1 into 0..1 \n"\ + "\n"\ + " ; calculate attenuation of light 1 \n"\ + "dp3 r2.x, r2.xyz, r2.xyz ; r2.x = r2.x˛ + r2.y˛ + r2.z˛ \n"\ + "mul r2.x, r2.x, c13.w ; r2.x * attenutation \n"\ + "rsq r2, r2.x ; r2.xyzw = 1/sqrt(r2.x * attenutation)\n"\ + "mul oD0, r2, c13 ; resulting light color = lightcolor * attenuation \n"\ + "\n"\ + " ; calculate attenuation of light 2 \n"\ + "dp3 r3.x, r3.xyz, r3.xyz ; r3.x = r3.x˛ + r3.y˛ + r3.z˛ \n"\ + "mul r3.x, r3.x, c15.w ; r2.x * attenutation \n"\ + "rsq r3, r3.x ; r2.xyzw = 1/sqrt(r2.x * attenutation)\n"\ + "mul oD1, r3, c15 ; resulting light color = lightcolor * attenuation \n"\ + "\n"\ + "mov oT0.xy, v3.xy ; move out texture coordinates 1\n"\ + "mov oT1.xy, v3.xy ; move out texture coordinates 2\n"\ + "mov oD0.a, v2.a ; move out original alpha value \n"\ + "\n"; + + + // Irrlicht Engine D3D9 render path normal map pixel shader version 1.4 + const char D3D9_PARALLAX_MAP_PSH[] = + ";Irrlicht Engine 0.10 D3D9 render path parallax mapping pixel shader \n"\ + ";Input: \n"\ + ";t0: color map texture coord \n"\ + ";t1: normal map texture coords \n"\ + ";t2: light 1 vector in tangent space \n"\ + ";t4: eye vector in tangent space \n"\ + ";v0: light 1 color \n"\ + ";t3: light 2 vector in tangent space \n"\ + ";v1: light 2 color \n"\ + ";v0.a: vertex alpha value \n"\ + " \n"\ + "ps.1.4 \n"\ + " \n"\ + ";def c6, 0.02f, 0.02f, 0.02f, 0.0f ; scale factor, now set in callback \n"\ + "def c5, 0.5f, 0.5f, 0.5f, 0.0f ; for specular division \n"\ + " \n"\ + "texld r1, t1 ; sample (normal.x, normal.y, normal.z, height) \n"\ + "texcrd r4.xyz, t4 ; fetch eye vector \n"\ + "texcrd r0.xyz, t0 ; color map \n"\ + " \n"\ + "; original parallax mapping: \n"\ + ";mul r3, r1_bx2.wwww, c6; ; r3 = (height, height, height) * scale \n"\ + ";mad r2.xyz, r3, r4_bx2, r0 ; newTexCoord = height * eye + oldTexCoord \n"\ + " \n"\ + "; modified parallax mapping to reduce swimming effect: \n"\ + "mul r3, r1_bx2.wwww, r1_bx2.zzzz ; (nh,nh,nh,nh) = (h,h,h,h) * (n.z,n.z,n.z,n.z,) \n"\ + "mul r3, r3, c6; ; r3 = (nh, nh, nh) * scale \n"\ + "mad r2.xyz, r3, r4_bx2, r0 ; newTexCoord = height * eye + oldTexCoord \n"\ + " \n"\ + "phase \n"\ + " \n"\ + "texld r0, r2 ; load diffuse texture with new tex coord \n"\ + "texld r1, r2 ; sample normal map \n"\ + "texcrd r2.xyz, t2 ; fetch light vector 1 \n"\ + "texcrd r3.xyz, t3 ; fetch light vector 2 \n"\ + " \n"\ + "dp3_sat r5, r1_bx2, r2_bx2 ; normal dot light 1 (_bx2 because moved into 0..1) \n"\ + "mul r5, r5, v0 ; luminance1 * light color 1 \n"\ + " \n"\ + "dp3_sat r3, r1_bx2, r3_bx2 ; normal dot light 2 (_bx2 because moved into 0..1) \n"\ + "mad r3, r3, v1, r5 ; (luminance2 * light color 2) + luminance1 \n"\ + " \n"\ + "mul r0.xyz, r0, r3 ; total luminance * base color \n"\ + "+mov r0.a, v0.a ; write original alpha value \n"\ + "\n"; + + // Irrlicht Engine D3D9 render path normal map pixel shader version 2.0 + const char D3D9_PARALLAX_MAP_PSH_20[] = + ";Irrlicht Engine D3D9 render path parallax mapping pixel shader \n"\ + ";Input: \n"\ + " \n"\ + ";t0: color map texture coord \n"\ + ";t1: normal map texture coords \n"\ + ";t2: light 1 vector in tangent space \n"\ + ";t4: eye vector in tangent space \n"\ + ";v0: light 1 color \n"\ + ";t3: light 2 vector in tangent space \n"\ + ";v1: light 2 color \n"\ + ";v0.a: vertex alpha value \n"\ + " \n"\ + "ps.2.0 \n"\ + " \n"\ + "dcl_2d s0 ; Declare the s0 register to be the sampler for stage 0 \n"\ + "dcl t0.xy ; Declare t0 to have 2D texture coordinates from stage 0 \n"\ + "dcl t1.xy ; Declare t0 to have 2D texture coordinates from stage 0 \n"\ + "dcl_2d s1 ; Declare the s1 register to be the sampler for stage 1 \n"\ + " \n"\ + "dcl t2.xyz ; \n"\ + "dcl t3.xyz ; \n"\ + "dcl t4.xyz ; \n"\ + "dcl v0.xyzw; \n"\ + "dcl v1.xyzw; \n"\ + " \n"\ + "def c0, -1.0f, -1.0f, -1.0f, -1.0f ; for _bx2 emulation \n"\ + "def c1, 2.0f, 2.0f, 2.0f, 2.0f ; for _bx2 emulation \n"\ + "mov r11, c1; \n"\ + " \n"\ + "texld r1, t1, s1 ; sample (normal.x, normal.y, normal.z, height) \n"\ + "mov r4.xyz, t4 ; fetch eye vector \n"\ + "mov r0.xy, t0 ; color map \n"\ + " \n"\ + "; original parallax mapping: \n"\ + "; emulate ps1x _bx2, so substract 0.5f and multiply by 2 \n"\ + "mad r1.xyz, r1, r11, c0; \n"\ + " \n"\ + "mul r3, r1.wwww, c6; ; r3 = (height, height, height) * scale \n"\ + " \n"\ + "; emulate ps1x _bx2, so substract 0.5f and multiply by 2 \n"\ + "mad r4.xyz, r4, r11, c0; \n"\ + " \n"\ + "mad r2.xy, r3, r4, r0 ; newTexCoord = height * eye + oldTexCoord \n"\ + " \n"\ + "; modified parallax mapping to avoid swimming: \n"\ + ";mul r3, r1_bx2.wwww, r1_bx2.zzzz ; r3 = (h,h,h,h) * (n.z, n.z, n.z, n.z,) \n"\ + ";mul r3, r3, c6; ; r3 = (nh, nh, nh) * scale \n"\ + ";mad r2.xyz, r3, r4_bx2, r0 ; newTexCoord = height * eye + oldTexCoord \n"\ + " \n"\ + "texld r0, r2, s0 ; load diffuse texture with new tex coord \n"\ + "texld r1, r2, s1 ; sample normal map \n"\ + "mov r2.xyz, t2 ; fetch light vector 1 \n"\ + "mov r3.xyz, t3 ; fetch light vector 2 \n"\ + " \n"\ + "; emulate ps1x _bx2, so substract 0.5f and multiply by 2 \n"\ + "mad r1.xyz, r1, r11, c0; \n"\ + "mad r2.xyz, r2, r11, c0; \n"\ + "mad r3.xyz, r3, r11, c0; \n"\ + " \n"\ + "dp3_sat r2, r1, r2 ; normal dot light 1 (_bx2 because moved into 0..1) \n"\ + "mul r2, r2, v0 ; luminance1 * light color 1 \n"\ + " \n"\ + "dp3_sat r3, r1, r3 ; normal dot light 2 (_bx2 because moved into 0..1) \n"\ + "mad r3, r3, v1, r2 ; (luminance2 * light color 2) + luminance1 \n"\ + " \n"\ + "mul r0.xyz, r0, r3 ; total luminance * base color \n"\ + "mov r0.a, v0.a ; write original alpha value \n"\ + "mov oC0, r0; \n"\ + "\n"; + + CD3D9ParallaxMapRenderer::CD3D9ParallaxMapRenderer( + IDirect3DDevice9* d3ddev, video::IVideoDriver* driver, + s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial) + : CD3D9ShaderMaterialRenderer(d3ddev, driver, 0, baseMaterial), + CurrentScale(0.0f) + { + + #ifdef _DEBUG + setDebugName("CD3D9ParallaxMapRenderer"); + #endif + + // set this as callback. We could have done this in + // the initialization list, but some compilers don't like it. + + CallBack = this; + + // basicly, this thing simply compiles these hardcoded shaders if the + // hardware is able to do them, otherwise it maps to the base material + + if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_4) || + !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1)) + { + // this hardware is not able to do shaders. Fall back to + // base material. + outMaterialTypeNr = driver->addMaterialRenderer(this); + return; + } + + // check if already compiled parallax map shaders are there. + + video::IMaterialRenderer* renderer = driver->getMaterialRenderer(EMT_PARALLAX_MAP_SOLID); + if (renderer) + { + // use the already compiled shaders + video::CD3D9ParallaxMapRenderer* nmr = (video::CD3D9ParallaxMapRenderer*)renderer; + VertexShader = nmr->VertexShader; + if (VertexShader) + VertexShader->AddRef(); + + PixelShader = nmr->PixelShader; + if (PixelShader) + PixelShader->AddRef(); + + outMaterialTypeNr = driver->addMaterialRenderer(this); + } + else + { + #ifdef SHADER_EXTERNAL_DEBUG + + // quickly load shader from external file + io::CReadFile* file = new io::CReadFile("parallax.psh"); + s32 sz = file->getSize(); + char* s = new char[sz+1]; + file->read(s, sz); + s[sz] = 0; + + init(outMaterialTypeNr, D3D9_PARALLAX_MAP_VSH, s); + + delete [] s; + file->drop(); + + #else + + // compile shaders on our own + init(outMaterialTypeNr, D3D9_PARALLAX_MAP_VSH, D3D9_PARALLAX_MAP_PSH); + + #endif // SHADER_EXTERNAL_DEBUG + } + // something failed, use base material + if (-1==outMaterialTypeNr) + driver->addMaterialRenderer(this); + } + + + CD3D9ParallaxMapRenderer::~CD3D9ParallaxMapRenderer() + { + if (CallBack == this) + CallBack = 0; + } + + bool CD3D9ParallaxMapRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype) + { + if (vtxtype != video::EVT_TANGENTS) + { + os::Printer::log("Error: Parallax map renderer only supports vertices of type EVT_TANGENTS", ELL_ERROR); + return false; + } + + return CD3D9ShaderMaterialRenderer::OnRender(service, vtxtype); + } + + + void CD3D9ParallaxMapRenderer::OnSetMaterial(const video::SMaterial& material, + const video::SMaterial& lastMaterial, + bool resetAllRenderstates, video::IMaterialRendererServices* services) + { + CD3D9ShaderMaterialRenderer::OnSetMaterial(material, lastMaterial, + resetAllRenderstates, services); + + CurrentScale = material.MaterialTypeParam; + } + + + //! Returns the render capability of the material. + s32 CD3D9ParallaxMapRenderer::getRenderCapability() const + { + if (Driver->queryFeature(video::EVDF_PIXEL_SHADER_1_4) && + Driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1)) + return 0; + + return 1; + } + + + //! Called by the engine when the vertex and/or pixel shader constants + //! for an material renderer should be set. + void CD3D9ParallaxMapRenderer::OnSetConstants(IMaterialRendererServices* services, s32 userData) + { + video::IVideoDriver* driver = services->getVideoDriver(); + + // set transposed world matrix + services->setVertexShaderConstant(driver->getTransform(video::ETS_WORLD).getTransposed().pointer(), 0, 4); + + // set eye position + + // The viewpoint is at (0., 0., 0.) in eye space. + // Turning this into a vector [0 0 0 1] and multiply it by + // the inverse of the view matrix, the resulting vector is the + // object space location of the camera. + + f32 floats[4] = {0,0,0,1}; + core::matrix4 minv = driver->getTransform(video::ETS_VIEW); + minv.makeInverse(); + minv.multiplyWith1x4Matrix(floats); + services->setVertexShaderConstant(floats, 4, 1); + + // set transposed worldViewProj matrix + core::matrix4 worldViewProj; + worldViewProj = driver->getTransform(video::ETS_PROJECTION); + worldViewProj *= driver->getTransform(video::ETS_VIEW); + worldViewProj *= driver->getTransform(video::ETS_WORLD); + services->setVertexShaderConstant(worldViewProj.getTransposed().pointer(), 8, 4); + + // here we've got to fetch the fixed function lights from the + // driver and set them as constants + + const u32 cnt = driver->getDynamicLightCount(); + + for (u32 i=0; i<2; ++i) + { + SLight light; + + if (igetDynamicLight(i); + else + { + light.DiffuseColor.set(0,0,0); // make light dark + light.Radius = 1.0f; + } + + light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation + + services->setVertexShaderConstant(reinterpret_cast(&light.Position), 12+(i*2), 1); + services->setVertexShaderConstant(reinterpret_cast(&light.DiffuseColor), 13+(i*2), 1); + } + + // this is not really necessary in d3d9 (used a def instruction), but to be sure: + f32 c95[] = {0.5f, 0.5f, 0.5f, 0.5f}; + services->setVertexShaderConstant(c95, 95, 1); + f32 c96[] = {-1, 1, 1, 1}; + services->setVertexShaderConstant(c96, 96, 1); + + // set scale factor + f32 factor = 0.02f; // default value + if (CurrentScale != 0) + factor = CurrentScale; + + f32 c6[] = {factor, factor, factor, 0}; + services->setPixelShaderConstant(c6, 6, 1); + } + + +} // end namespace video +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_DIRECT3D_9_ + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9ParallaxMapRenderer.h b/lib/irrlicht/source/Irrlicht/CD3D9ParallaxMapRenderer.h new file mode 100644 index 000000000..94010cd73 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9ParallaxMapRenderer.h @@ -0,0 +1,63 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_D3D9_PARALLAX_MAPMATERIAL_RENDERER_H_INCLUDED__ +#define __C_D3D9_PARALLAX_MAPMATERIAL_RENDERER_H_INCLUDED__ + +#include "IrrCompileConfig.h" +#ifdef _IRR_WINDOWS_ + +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ +#if defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +#include "irrMath.h" // needed by borland for sqrtf define +#endif +#include + +#include "CD3D9ShaderMaterialRenderer.h" +#include "IShaderConstantSetCallBack.h" + +namespace irr +{ +namespace video +{ + +//! Renderer for normal maps using parallax mapping +class CD3D9ParallaxMapRenderer : + public CD3D9ShaderMaterialRenderer, IShaderConstantSetCallBack +{ +public: + + CD3D9ParallaxMapRenderer( + IDirect3DDevice9* d3ddev, video::IVideoDriver* driver, + s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial); + + ~CD3D9ParallaxMapRenderer(); + + //! Called by the engine when the vertex and/or pixel shader constants for an + //! material renderer should be set. + virtual void OnSetConstants(IMaterialRendererServices* services, s32 userData); + + virtual bool OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype); + + //! Returns the render capability of the material. + virtual s32 getRenderCapability() const; + + virtual void OnSetMaterial(const SMaterial& material) { } + virtual void OnSetMaterial(const video::SMaterial& material, + const video::SMaterial& lastMaterial, + bool resetAllRenderstates, video::IMaterialRendererServices* services); + +private: + + f32 CurrentScale; + +}; + +} // end namespace video +} // end namespace irr + +#endif +#endif +#endif + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9ShaderMaterialRenderer.cpp b/lib/irrlicht/source/Irrlicht/CD3D9ShaderMaterialRenderer.cpp new file mode 100644 index 000000000..e67c8cd26 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9ShaderMaterialRenderer.cpp @@ -0,0 +1,541 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "IrrCompileConfig.h" +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ +#define _IRR_D3D_NO_SHADER_DEBUGGING 1 + +#include "CD3D9ShaderMaterialRenderer.h" +#include "IShaderConstantSetCallBack.h" +#include "IMaterialRendererServices.h" +#include "IVideoDriver.h" +#include "os.h" +#include "irrString.h" + +#ifndef _IRR_D3D_NO_SHADER_DEBUGGING +#include +#endif + + +namespace irr +{ +namespace video +{ + +//! Public constructor +CD3D9ShaderMaterialRenderer::CD3D9ShaderMaterialRenderer(IDirect3DDevice9* d3ddev, video::IVideoDriver* driver, + s32& outMaterialTypeNr, const c8* vertexShaderProgram, const c8* pixelShaderProgram, + IShaderConstantSetCallBack* callback, IMaterialRenderer* baseMaterial, s32 userData) +: pID3DDevice(d3ddev), Driver(driver), CallBack(callback), BaseMaterial(baseMaterial), + VertexShader(0), OldVertexShader(0), PixelShader(0), UserData(userData) +{ + #ifdef _DEBUG + setDebugName("CD3D9ShaderMaterialRenderer"); + #endif + + if (BaseMaterial) + BaseMaterial->grab(); + + if (CallBack) + CallBack->grab(); + + init(outMaterialTypeNr, vertexShaderProgram, pixelShaderProgram); +} + + +//! constructor only for use by derived classes who want to +//! create a fall back material for example. +CD3D9ShaderMaterialRenderer::CD3D9ShaderMaterialRenderer(IDirect3DDevice9* d3ddev, + video::IVideoDriver* driver, + IShaderConstantSetCallBack* callback, + IMaterialRenderer* baseMaterial, s32 userData) +: pID3DDevice(d3ddev), Driver(driver), CallBack(callback), BaseMaterial(baseMaterial), + VertexShader(0), OldVertexShader(0), PixelShader(0), UserData(userData) +{ + #ifdef _DEBUG + setDebugName("CD3D9ShaderMaterialRenderer"); + #endif + + if (BaseMaterial) + BaseMaterial->grab(); + + if (CallBack) + CallBack->grab(); +} + + +void CD3D9ShaderMaterialRenderer::init(s32& outMaterialTypeNr, + const c8* vertexShaderProgram, const c8* pixelShaderProgram) +{ + outMaterialTypeNr = -1; + + // create vertex shader + if (!createVertexShader(vertexShaderProgram)) + return; + + // create pixel shader + if (!createPixelShader(pixelShaderProgram)) + return; + + // register myself as new material + outMaterialTypeNr = Driver->addMaterialRenderer(this); +} + + +//! Destructor +CD3D9ShaderMaterialRenderer::~CD3D9ShaderMaterialRenderer() +{ + if (CallBack) + CallBack->drop(); + + if (VertexShader) + VertexShader->Release(); + + if (PixelShader) + PixelShader->Release(); + + if (BaseMaterial) + BaseMaterial->drop(); +} + + +bool CD3D9ShaderMaterialRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype) +{ + // call callback to set shader constants + if (CallBack && (VertexShader || PixelShader)) + CallBack->OnSetConstants(service, UserData); + + return true; +} + + +void CD3D9ShaderMaterialRenderer::OnSetMaterial(const video::SMaterial& material, const video::SMaterial& lastMaterial, + bool resetAllRenderstates, video::IMaterialRendererServices* services) +{ + if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) + { + if (VertexShader) + { + // save old vertex shader + pID3DDevice->GetVertexShader(&OldVertexShader); + + // set new vertex shader + if (FAILED(pID3DDevice->SetVertexShader(VertexShader))) + os::Printer::log("Could not set vertex shader.", ELL_WARNING); + } + + // set new pixel shader + if (PixelShader) + { + if (FAILED(pID3DDevice->SetPixelShader(PixelShader))) + os::Printer::log("Could not set pixel shader.", ELL_WARNING); + } + + if (BaseMaterial) + BaseMaterial->OnSetMaterial(material, material, true, services); + } + + //let callback know used material + if (CallBack) + CallBack->OnSetMaterial(material); + + services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); +} + + +void CD3D9ShaderMaterialRenderer::OnUnsetMaterial() +{ + if (VertexShader) + pID3DDevice->SetVertexShader(OldVertexShader); + + if (PixelShader) + pID3DDevice->SetPixelShader(0); + + if (BaseMaterial) + BaseMaterial->OnUnsetMaterial(); +} + + +//! Returns if the material is transparent. The scene managment needs to know this +//! for being able to sort the materials by opaque and transparent. +bool CD3D9ShaderMaterialRenderer::isTransparent() const +{ + return BaseMaterial ? BaseMaterial->isTransparent() : false; +} + + +bool CD3D9ShaderMaterialRenderer::createPixelShader(const c8* pxsh) +{ + if (!pxsh) + return true; + + // compile shader + + LPD3DXBUFFER code = 0; + LPD3DXBUFFER errors = 0; + + #ifdef _IRR_D3D_NO_SHADER_DEBUGGING + + // compile shader without debug info + stubD3DXAssembleShader(pxsh, (UINT)strlen(pxsh), 0, 0, 0, &code, &errors); + #else + + // compile shader and emitt some debug informations to + // make it possible to debug the shader in visual studio + + static int irr_dbg_file_nr = 0; + ++irr_dbg_file_nr; + char tmp[32]; + sprintf(tmp, "irr_d3d9_dbg_shader_%d.psh", irr_dbg_file_nr); + + FILE* f = fopen(tmp, "wb"); + fwrite(pxsh, strlen(pxsh), 1, f); + fflush(f); + fclose(f); + + stubD3DXAssembleShaderFromFile(tmp, 0, 0, D3DXSHADER_DEBUG, &code, &errors); + + #endif + + + if (errors) + { + // print out compilation errors. + os::Printer::log("Pixel shader compilation failed:", ELL_ERROR); + os::Printer::log((c8*)errors->GetBufferPointer(), ELL_ERROR); + + if (code) + code->Release(); + + errors->Release(); + return false; + } + + if (FAILED(pID3DDevice->CreatePixelShader((DWORD*)code->GetBufferPointer(), &PixelShader))) + { + os::Printer::log("Could not create pixel shader.", ELL_ERROR); + code->Release(); + return false; + } + + code->Release(); + return true; +} + + +bool CD3D9ShaderMaterialRenderer::createVertexShader(const char* vtxsh) +{ + if (!vtxsh) + return true; + + // compile shader + + LPD3DXBUFFER code = 0; + LPD3DXBUFFER errors = 0; + + #ifdef _IRR_D3D_NO_SHADER_DEBUGGING + + // compile shader without debug info + stubD3DXAssembleShader(vtxsh, (UINT)strlen(vtxsh), 0, 0, 0, &code, &errors); + + #else + + // compile shader and emitt some debug informations to + // make it possible to debug the shader in visual studio + + static int irr_dbg_file_nr = 0; + ++irr_dbg_file_nr; + char tmp[32]; + sprintf(tmp, "irr_d3d9_dbg_shader_%d.vsh", irr_dbg_file_nr); + + FILE* f = fopen(tmp, "wb"); + fwrite(vtxsh, strlen(vtxsh), 1, f); + fflush(f); + fclose(f); + + stubD3DXAssembleShaderFromFile(tmp, 0, 0, D3DXSHADER_DEBUG, &code, &errors); + + #endif + + if (errors) + { + // print out compilation errors. + os::Printer::log("Vertex shader compilation failed:", ELL_ERROR); + os::Printer::log((c8*)errors->GetBufferPointer(), ELL_ERROR); + + if (code) + code->Release(); + + errors->Release(); + return false; + } + + if (!code || FAILED(pID3DDevice->CreateVertexShader((DWORD*)code->GetBufferPointer(), &VertexShader))) + { + os::Printer::log("Could not create vertex shader.", ELL_ERROR); + if (code) + code->Release(); + return false; + } + + code->Release(); + return true; +} + + +HRESULT CD3D9ShaderMaterialRenderer::stubD3DXAssembleShader(LPCSTR pSrcData, + UINT SrcDataLen, CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, DWORD Flags, LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs) +{ + // Because Irrlicht needs to be able to start up even without installed d3d dlls, it + // needs to load external d3d dlls manually. examples for the dlls are: + // SDK dll name D3DX_SDK_VERSION + // Summer 2004: no dll 22 + // February 2005: d3dx9_24.dll 24 + // April 2005: d3dx9_25.dll 25 + // June 2005: d3dx9_26.dll 26 + // August 2005: d3dx9_27.dll 27 + // October 2005, + // December 2005: d3dx9_28.dll 28 + + #if ( D3DX_SDK_VERSION < 24 ) + // directly link functions, old d3d sdks didn't try to load external dlls + // when linking to the d3dx9.lib + #ifdef _MSC_VER + #pragma comment (lib, "d3dx9.lib") + #endif + + // invoke static linked function + return D3DXAssembleShader(pSrcData, SrcDataLen, pDefines, pInclude, + Flags, ppShader, ppErrorMsgs); + #else + { + // try to load shader functions from the dll and print error if failed. + + // D3DXAssembleShader signature + typedef HRESULT (WINAPI *AssembleShaderFunction)(LPCSTR pSrcData, UINT SrcDataLen, + CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, + DWORD Flags, LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs); + + static bool LoadFailed = false; + static AssembleShaderFunction pFn = 0; + + if (!pFn && !LoadFailed) + { + // try to load dll + io::path strDllName = "d3dx9_"; + strDllName += (int)D3DX_SDK_VERSION; + strDllName += ".dll"; + + HMODULE hMod = LoadLibraryA(strDllName.c_str()); + if (hMod) + pFn = (AssembleShaderFunction)GetProcAddress(hMod, "D3DXAssembleShader"); + + if (!pFn) + { + LoadFailed = true; + os::Printer::log("Could not load shader function D3DXAssembleShader from dll, shaders disabled", + strDllName.c_str(), ELL_ERROR); + } + } + + if (pFn) + { + // call already loaded function + return (*pFn)(pSrcData, SrcDataLen, pDefines, pInclude, Flags, ppShader, ppErrorMsgs); + } + } + #endif // D3DX_SDK_VERSION < 24 + + return 0; +} + + +HRESULT CD3D9ShaderMaterialRenderer::stubD3DXAssembleShaderFromFile(LPCSTR pSrcFile, + CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, DWORD Flags, + LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs) +{ + // wondering what I'm doing here? + // see comment in CD3D9ShaderMaterialRenderer::stubD3DXAssembleShader() + + #if ( D3DX_SDK_VERSION < 24 ) + // directly link functions, old d3d sdks didn't try to load external dlls + // when linking to the d3dx9.lib + #ifdef _MSC_VER + #pragma comment (lib, "d3dx9.lib") + #endif + + // invoke static linked function + return D3DXAssembleShaderFromFileA(pSrcFile, pDefines, pInclude, Flags, + ppShader, ppErrorMsgs); + #else + { + // try to load shader functions from the dll and print error if failed. + + // D3DXAssembleShaderFromFileA signature + typedef HRESULT (WINAPI *AssembleShaderFromFileFunction)(LPCSTR pSrcFile, + CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, DWORD Flags, + LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs); + + static bool LoadFailed = false; + static AssembleShaderFromFileFunction pFn = 0; + + if (!pFn && !LoadFailed) + { + // try to load dll + io::path strDllName = "d3dx9_"; + strDllName += (int)D3DX_SDK_VERSION; + strDllName += ".dll"; + + HMODULE hMod = LoadLibraryA(strDllName.c_str()); + if (hMod) + pFn = (AssembleShaderFromFileFunction)GetProcAddress(hMod, "D3DXAssembleShaderFromFileA"); + + if (!pFn) + { + LoadFailed = true; + os::Printer::log("Could not load shader function D3DXAssembleShaderFromFileA from dll, shaders disabled", + strDllName.c_str(), ELL_ERROR); + } + } + + if (pFn) + { + // call already loaded function + return (*pFn)(pSrcFile, pDefines, pInclude, Flags, ppShader, ppErrorMsgs); + } + } + #endif // D3DX_SDK_VERSION < 24 + + return 0; +} + + +HRESULT CD3D9ShaderMaterialRenderer::stubD3DXCompileShader(LPCSTR pSrcData, UINT SrcDataLen, CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, + LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs, LPD3DXCONSTANTTABLE* ppConstantTable) +{ + // wondering what I'm doing here? + // see comment in CD3D9ShaderMaterialRenderer::stubD3DXAssembleShader() + + #if ( D3DX_SDK_VERSION < 24 ) + // directly link functions, old d3d sdks didn't try to load external dlls + // when linking to the d3dx9.lib + #ifdef _MSC_VER + #pragma comment (lib, "d3dx9.lib") + #endif + + // invoke static linked function + return D3DXCompileShader(pSrcData, SrcDataLen, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs, ppConstantTable); + #else + { + // try to load shader functions from the dll and print error if failed. + + // D3DXCompileShader + typedef HRESULT (WINAPI *D3DXCompileShaderFunction)(LPCSTR pSrcData, UINT SrcDataLen, CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, + LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs, LPD3DXCONSTANTTABLE* ppConstantTable); + + static bool LoadFailed = false; + static D3DXCompileShaderFunction pFn = 0; + + if (!pFn && !LoadFailed) + { + // try to load dll + io::path strDllName = "d3dx9_"; + strDllName += (int)D3DX_SDK_VERSION; + strDllName += ".dll"; + + HMODULE hMod = LoadLibraryA(strDllName.c_str()); + if (hMod) + pFn = (D3DXCompileShaderFunction)GetProcAddress(hMod, "D3DXCompileShader"); + + if (!pFn) + { + LoadFailed = true; + os::Printer::log("Could not load shader function D3DXCompileShader from dll, shaders disabled", + strDllName.c_str(), ELL_ERROR); + } + } + + if (pFn) + { + // call already loaded function + return (*pFn)(pSrcData, SrcDataLen, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs, ppConstantTable); + } + } + #endif // D3DX_SDK_VERSION < 24 + + return 0; +} + +HRESULT CD3D9ShaderMaterialRenderer::stubD3DXCompileShaderFromFile(LPCSTR pSrcFile, CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, + LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs, + LPD3DXCONSTANTTABLE* ppConstantTable) +{ + // wondering what I'm doing here? + // see comment in CD3D9ShaderMaterialRenderer::stubD3DXAssembleShader() + + #if ( D3DX_SDK_VERSION < 24 ) + // directly link functions, old d3d sdks didn't try to load external dlls + // when linking to the d3dx9.lib + #ifdef _MSC_VER + #pragma comment (lib, "d3dx9.lib") + #endif + + // invoke static linked function + return D3DXCompileShaderFromFileA(pSrcFile, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs, ppConstantTable); + #else + { + // try to load shader functions from the dll and print error if failed. + + // D3DXCompileShaderFromFileA + typedef HRESULT (WINAPI *D3DXCompileShaderFromFileFunction)(LPCSTR pSrcFile, + CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, + LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs, + LPD3DXCONSTANTTABLE* ppConstantTable); + + static bool LoadFailed = false; + static D3DXCompileShaderFromFileFunction pFn = 0; + + if (!pFn && !LoadFailed) + { + // try to load dll + io::path strDllName = "d3dx9_"; + strDllName += (int)D3DX_SDK_VERSION; + strDllName += ".dll"; + + HMODULE hMod = LoadLibraryA(strDllName.c_str()); + if (hMod) + pFn = (D3DXCompileShaderFromFileFunction)GetProcAddress(hMod, "D3DXCompileShaderFromFileA"); + + if (!pFn) + { + LoadFailed = true; + os::Printer::log("Could not load shader function D3DXCompileShaderFromFileA from dll, shaders disabled", + strDllName.c_str(), ELL_ERROR); + } + } + + if (pFn) + { + // call already loaded function + return (*pFn)(pSrcFile, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs, ppConstantTable); + } + } + #endif // D3DX_SDK_VERSION < 24 + + return 0; +} + + +} // end namespace video +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_DIRECT3D_9_ diff --git a/lib/irrlicht/source/Irrlicht/CD3D9ShaderMaterialRenderer.h b/lib/irrlicht/source/Irrlicht/CD3D9ShaderMaterialRenderer.h new file mode 100644 index 000000000..2c2bb19ab --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9ShaderMaterialRenderer.h @@ -0,0 +1,102 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_D3D9_SHADER_MATERIAL_RENDERER_H_INCLUDED__ +#define __C_D3D9_SHADER_MATERIAL_RENDERER_H_INCLUDED__ + +#include "IrrCompileConfig.h" +#ifdef _IRR_WINDOWS_ + +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ +#if defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +#include "irrMath.h" // needed by borland for sqrtf define +#endif +#include + +#include "IMaterialRenderer.h" + +namespace irr +{ +namespace video +{ + +class IVideoDriver; +class IShaderConstantSetCallBack; +class IMaterialRenderer; + +//! Class for using vertex and pixel shaders with D3D9 +class CD3D9ShaderMaterialRenderer : public IMaterialRenderer +{ +public: + + //! Public constructor + CD3D9ShaderMaterialRenderer(IDirect3DDevice9* d3ddev, video::IVideoDriver* driver, + s32& outMaterialTypeNr, const c8* vertexShaderProgram, const c8* pixelShaderProgram, + IShaderConstantSetCallBack* callback, IMaterialRenderer* baseMaterial, s32 userData); + + //! Destructor + ~CD3D9ShaderMaterialRenderer(); + + virtual void OnSetMaterial(const video::SMaterial& material, const video::SMaterial& lastMaterial, + bool resetAllRenderstates, video::IMaterialRendererServices* services); + + virtual void OnUnsetMaterial(); + + virtual bool OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype); + + //! Returns if the material is transparent. + virtual bool isTransparent() const; + +protected: + + //! constructor only for use by derived classes who want to + //! create a fall back material for example. + CD3D9ShaderMaterialRenderer(IDirect3DDevice9* d3ddev, + video::IVideoDriver* driver, + IShaderConstantSetCallBack* callback, + IMaterialRenderer* baseMaterial, + s32 userData=0); + + void init(s32& outMaterialTypeNr, const c8* vertexShaderProgram, const c8* pixelShaderProgram); + bool createPixelShader(const c8* pxsh); + bool createVertexShader(const char* vtxsh); + + HRESULT stubD3DXAssembleShader(LPCSTR pSrcData, UINT SrcDataLen, + CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, + DWORD Flags, LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs); + + HRESULT stubD3DXAssembleShaderFromFile(LPCSTR pSrcFile, + CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, DWORD Flags, + LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs); + + HRESULT stubD3DXCompileShader(LPCSTR pSrcData, UINT SrcDataLen, CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, + LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs, LPD3DXCONSTANTTABLE* ppConstantTable); + + HRESULT stubD3DXCompileShaderFromFile(LPCSTR pSrcFile, CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, + LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs, + LPD3DXCONSTANTTABLE* ppConstantTable); + + IDirect3DDevice9* pID3DDevice; + video::IVideoDriver* Driver; + IShaderConstantSetCallBack* CallBack; + IMaterialRenderer* BaseMaterial; + + IDirect3DVertexShader9* VertexShader; + IDirect3DVertexShader9* OldVertexShader; + IDirect3DPixelShader9* PixelShader; + s32 UserData; +}; + + +} // end namespace video +} // end namespace irr + +#endif +#endif +#endif + diff --git a/lib/irrlicht/source/Irrlicht/CD3D9Texture.cpp b/lib/irrlicht/source/Irrlicht/CD3D9Texture.cpp new file mode 100644 index 000000000..122ee2a67 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9Texture.cpp @@ -0,0 +1,699 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "IrrCompileConfig.h" +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + +#define _IRR_DONT_DO_MEMORY_DEBUGGING_HERE +#include "CD3D9Texture.h" +#include "CD3D9Driver.h" +#include "os.h" + +#include + +#ifndef _IRR_COMPILE_WITH_DIRECT3D_8_ +// The D3DXFilterTexture function seems to get linked wrong when +// compiling with both D3D8 and 9, causing it not to work in the D3D9 device. +// So mipmapgeneration is replaced with my own bad generation in d3d 8 when +// compiling with both D3D 8 and 9. +// #define _IRR_USE_D3DXFilterTexture_ +#endif // _IRR_COMPILE_WITH_DIRECT3D_8_ + +#ifdef _IRR_USE_D3DXFilterTexture_ +#pragma comment(lib, "d3dx9.lib") +#endif + +namespace irr +{ +namespace video +{ + +//! rendertarget constructor +CD3D9Texture::CD3D9Texture(CD3D9Driver* driver, const core::dimension2d& size, + const io::path& name, const ECOLOR_FORMAT format) +: ITexture(name), Texture(0), RTTSurface(0), Driver(driver), DepthSurface(0), + TextureSize(size), ImageSize(size), Pitch(0), ColorFormat(ECF_UNKNOWN), + HasMipMaps(false), HardwareMipMaps(false), IsRenderTarget(true) +{ + #ifdef _DEBUG + setDebugName("CD3D9Texture"); + #endif + + Device=driver->getExposedVideoData().D3D9.D3DDev9; + if (Device) + Device->AddRef(); + + createRenderTarget(format); +} + + +//! constructor +CD3D9Texture::CD3D9Texture(IImage* image, CD3D9Driver* driver, + u32 flags, const io::path& name, void* mipmapData) +: ITexture(name), Texture(0), RTTSurface(0), Driver(driver), DepthSurface(0), + TextureSize(0,0), ImageSize(0,0), Pitch(0), ColorFormat(ECF_UNKNOWN), + HasMipMaps(false), HardwareMipMaps(false), IsRenderTarget(false) +{ + #ifdef _DEBUG + setDebugName("CD3D9Texture"); + #endif + + HasMipMaps = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); + + Device=driver->getExposedVideoData().D3D9.D3DDev9; + if (Device) + Device->AddRef(); + + if (image) + { + if (createTexture(flags, image)) + { + if (copyTexture(image)) + { + regenerateMipMapLevels(mipmapData); + } + } + else + os::Printer::log("Could not create DIRECT3D9 Texture.", ELL_WARNING); + } +} + + +//! destructor +CD3D9Texture::~CD3D9Texture() +{ + if (Texture) + Texture->Release(); + + if (RTTSurface) + RTTSurface->Release(); + + // if this texture was the last one using the depth buffer + // we can release the surface. We only use the value of the pointer + // hence it is safe to use the dropped pointer... + if (DepthSurface) + { + if (DepthSurface->drop()) + Driver->removeDepthSurface(DepthSurface); + } + + if (Device) + Device->Release(); +} + + +void CD3D9Texture::createRenderTarget(const ECOLOR_FORMAT format) +{ + // are texture size restrictions there ? + if(!Driver->queryFeature(EVDF_TEXTURE_NPOT)) + { + if (TextureSize != ImageSize) + os::Printer::log("RenderTarget size has to be a power of two", ELL_INFORMATION); + } + TextureSize = TextureSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT), !Driver->queryFeature(EVDF_TEXTURE_NSQUARE), true, Driver->Caps.MaxTextureWidth); + + D3DFORMAT d3dformat = Driver->getD3DColorFormat(); + + if(ColorFormat == ECF_UNKNOWN) + { + // get irrlicht format from backbuffer + // (This will get overwritten by the custom format if it is provided, else kept.) + ColorFormat = Driver->getColorFormat(); + setPitch(d3dformat); + + // Use color format if provided. + if(format != ECF_UNKNOWN) + { + ColorFormat = format; + d3dformat = Driver->getD3DFormatFromColorFormat(format); + setPitch(d3dformat); // This will likely set pitch to 0 for now. + } + } + else + { + d3dformat = Driver->getD3DFormatFromColorFormat(ColorFormat); + } + + // create texture + HRESULT hr; + + hr = Device->CreateTexture( + TextureSize.Width, + TextureSize.Height, + 1, // mip map level count, we don't want mipmaps here + D3DUSAGE_RENDERTARGET, + d3dformat, + D3DPOOL_DEFAULT, + &Texture, + NULL); + + if (FAILED(hr)) + { + if (D3DERR_INVALIDCALL == hr) + os::Printer::log("Could not create render target texture", "Invalid Call"); + else + if (D3DERR_OUTOFVIDEOMEMORY == hr) + os::Printer::log("Could not create render target texture", "Out of Video Memory"); + else + if (E_OUTOFMEMORY == hr) + os::Printer::log("Could not create render target texture", "Out of Memory"); + else + os::Printer::log("Could not create render target texture"); + } +} + + +bool CD3D9Texture::createMipMaps(u32 level) +{ + if (level==0) + return true; + + if (HardwareMipMaps && Texture) + { + // generate mipmaps in hardware + Texture->GenerateMipSubLevels(); + return true; + } + + // manual mipmap generation + IDirect3DSurface9* upperSurface = 0; + IDirect3DSurface9* lowerSurface = 0; + + // get upper level + HRESULT hr = Texture->GetSurfaceLevel(level-1, &upperSurface); + if (FAILED(hr) || !upperSurface) + { + os::Printer::log("Could not get upper surface level for mip map generation", ELL_WARNING); + return false; + } + + // get lower level + hr = Texture->GetSurfaceLevel(level, &lowerSurface); + if (FAILED(hr) || !lowerSurface) + { + os::Printer::log("Could not get lower surface level for mip map generation", ELL_WARNING); + upperSurface->Release(); + return false; + } + + D3DSURFACE_DESC upperDesc, lowerDesc; + upperSurface->GetDesc(&upperDesc); + lowerSurface->GetDesc(&lowerDesc); + + D3DLOCKED_RECT upperlr; + D3DLOCKED_RECT lowerlr; + + // lock upper surface + if (FAILED(upperSurface->LockRect(&upperlr, NULL, 0))) + { + upperSurface->Release(); + lowerSurface->Release(); + os::Printer::log("Could not lock upper texture for mip map generation", ELL_WARNING); + return false; + } + + // lock lower surface + if (FAILED(lowerSurface->LockRect(&lowerlr, NULL, 0))) + { + upperSurface->UnlockRect(); + upperSurface->Release(); + lowerSurface->Release(); + os::Printer::log("Could not lock lower texture for mip map generation", ELL_WARNING); + return false; + } + + if (upperDesc.Format != lowerDesc.Format) + { + os::Printer::log("Cannot copy mip maps with different formats.", ELL_WARNING); + } + else + { + if ((upperDesc.Format == D3DFMT_A1R5G5B5) || (upperDesc.Format == D3DFMT_R5G6B5)) + copy16BitMipMap((char*)upperlr.pBits, (char*)lowerlr.pBits, + lowerDesc.Width, lowerDesc.Height, + upperlr.Pitch, lowerlr.Pitch); + else + if (upperDesc.Format == D3DFMT_A8R8G8B8) + copy32BitMipMap((char*)upperlr.pBits, (char*)lowerlr.pBits, + lowerDesc.Width, lowerDesc.Height, + upperlr.Pitch, lowerlr.Pitch); + else + os::Printer::log("Unsupported mipmap format, cannot copy.", ELL_WARNING); + } + + bool result=true; + // unlock + if (FAILED(upperSurface->UnlockRect())) + result=false; + if (FAILED(lowerSurface->UnlockRect())) + result=false; + + // release + upperSurface->Release(); + lowerSurface->Release(); + + if (!result || (upperDesc.Width <= 3 && upperDesc.Height <= 3)) + return result; // stop generating levels + + // generate next level + return createMipMaps(level+1); +} + + +//! creates the hardware texture +bool CD3D9Texture::createTexture(u32 flags, IImage * image) +{ + ImageSize = image->getDimension(); + + core::dimension2d optSize = ImageSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT), !Driver->queryFeature(EVDF_TEXTURE_NSQUARE), true, Driver->Caps.MaxTextureWidth); + + D3DFORMAT format = D3DFMT_A1R5G5B5; + + switch(getTextureFormatFromFlags(flags)) + { + case ETCF_ALWAYS_16_BIT: + format = D3DFMT_A1R5G5B5; break; + case ETCF_ALWAYS_32_BIT: + format = D3DFMT_A8R8G8B8; break; + case ETCF_OPTIMIZED_FOR_QUALITY: + { + switch(image->getColorFormat()) + { + case ECF_R8G8B8: + case ECF_A8R8G8B8: + format = D3DFMT_A8R8G8B8; break; + case ECF_A1R5G5B5: + case ECF_R5G6B5: + format = D3DFMT_A1R5G5B5; break; + } + } + break; + case ETCF_OPTIMIZED_FOR_SPEED: + format = D3DFMT_A1R5G5B5; + break; + default: + break; + } + if (Driver->getTextureCreationFlag(video::ETCF_NO_ALPHA_CHANNEL)) + { + if (format == D3DFMT_A8R8G8B8) + format = D3DFMT_R8G8B8; + else if (format == D3DFMT_A1R5G5B5) + format = D3DFMT_R5G6B5; + } + + const bool mipmaps = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); + + DWORD usage = 0; + + // This enables hardware mip map generation. + if (mipmaps && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) + { + LPDIRECT3D9 intf = Driver->getExposedVideoData().D3D9.D3D9; + D3DDISPLAYMODE d3ddm; + intf->GetAdapterDisplayMode(Driver->Params.DisplayAdapter, &d3ddm); + + if (D3D_OK==intf->CheckDeviceFormat(Driver->Params.DisplayAdapter,D3DDEVTYPE_HAL,d3ddm.Format,D3DUSAGE_AUTOGENMIPMAP,D3DRTYPE_TEXTURE,format)) + { + usage = D3DUSAGE_AUTOGENMIPMAP; + HardwareMipMaps = true; + } + } + + HRESULT hr = Device->CreateTexture(optSize.Width, optSize.Height, + mipmaps ? 0 : 1, // number of mipmaplevels (0 = automatic all) + usage, // usage + format, D3DPOOL_MANAGED , &Texture, NULL); + + if (FAILED(hr)) + { + // try brute force 16 bit + HardwareMipMaps = false; + if (format == D3DFMT_A8R8G8B8) + format = D3DFMT_A1R5G5B5; + else if (format == D3DFMT_R8G8B8) + format = D3DFMT_R5G6B5; + else + return false; + + hr = Device->CreateTexture(optSize.Width, optSize.Height, + mipmaps ? 0 : 1, // number of mipmaplevels (0 = automatic all) + 0, format, D3DPOOL_MANAGED, &Texture, NULL); + } + + ColorFormat = Driver->getColorFormatFromD3DFormat(format); + setPitch(format); + return (SUCCEEDED(hr)); +} + + +//! copies the image to the texture +bool CD3D9Texture::copyTexture(IImage * image) +{ + if (Texture && image) + { + D3DSURFACE_DESC desc; + Texture->GetLevelDesc(0, &desc); + + TextureSize.Width = desc.Width; + TextureSize.Height = desc.Height; + + D3DLOCKED_RECT rect; + HRESULT hr = Texture->LockRect(0, &rect, 0, 0); + if (FAILED(hr)) + { + os::Printer::log("Texture data not copied", "Could not LockRect D3D9 Texture.", ELL_ERROR); + return false; + } + + Pitch = rect.Pitch; + image->copyToScaling(rect.pBits, TextureSize.Width, TextureSize.Height, ColorFormat, Pitch); + + hr = Texture->UnlockRect(0); + if (FAILED(hr)) + { + os::Printer::log("Texture data not copied", "Could not UnlockRect D3D9 Texture.", ELL_ERROR); + return false; + } + } + + return true; +} + + +//! lock function +void* CD3D9Texture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel) +{ + if (!Texture) + return 0; + + MipLevelLocked=mipmapLevel; + HRESULT hr; + D3DLOCKED_RECT rect; + if(!IsRenderTarget) + { + hr = Texture->LockRect(mipmapLevel, &rect, 0, (mode==ETLM_READ_ONLY)?D3DLOCK_READONLY:0); + if (FAILED(hr)) + { + os::Printer::log("Could not lock DIRECT3D9 Texture.", ELL_ERROR); + return 0; + } + } + else + { + if (!RTTSurface) + { + // Make RTT surface large enough for all miplevels (including 0) + D3DSURFACE_DESC desc; + Texture->GetLevelDesc(0, &desc); + hr = Device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &RTTSurface, 0); + if (FAILED(hr)) + { + os::Printer::log("Could not lock DIRECT3D9 Texture", "Offscreen surface creation failed.", ELL_ERROR); + return 0; + } + } + + IDirect3DSurface9 *surface = 0; + hr = Texture->GetSurfaceLevel(mipmapLevel, &surface); + if (FAILED(hr)) + { + os::Printer::log("Could not lock DIRECT3D9 Texture", "Could not get surface.", ELL_ERROR); + return 0; + } + hr = Device->GetRenderTargetData(surface, RTTSurface); + surface->Release(); + if(FAILED(hr)) + { + os::Printer::log("Could not lock DIRECT3D9 Texture", "Data copy failed.", ELL_ERROR); + return 0; + } + hr = RTTSurface->LockRect(&rect, 0, (mode==ETLM_READ_ONLY)?D3DLOCK_READONLY:0); + if(FAILED(hr)) + { + os::Printer::log("Could not lock DIRECT3D9 Texture", "LockRect failed.", ELL_ERROR); + return 0; + } + } + return rect.pBits; +} + + +//! unlock function +void CD3D9Texture::unlock() +{ + if (!Texture) + return; + + if (!IsRenderTarget) + Texture->UnlockRect(MipLevelLocked); + else if (RTTSurface) + RTTSurface->UnlockRect(); +} + + +//! Returns original size of the texture. +const core::dimension2d& CD3D9Texture::getOriginalSize() const +{ + return ImageSize; +} + + +//! Returns (=size) of the texture. +const core::dimension2d& CD3D9Texture::getSize() const +{ + return TextureSize; +} + + +//! returns driver type of texture (=the driver, who created the texture) +E_DRIVER_TYPE CD3D9Texture::getDriverType() const +{ + return EDT_DIRECT3D9; +} + + +//! returns color format of texture +ECOLOR_FORMAT CD3D9Texture::getColorFormat() const +{ + return ColorFormat; +} + + +//! returns pitch of texture (in bytes) +u32 CD3D9Texture::getPitch() const +{ + return Pitch; +} + + +//! returns the DIRECT3D9 Texture +IDirect3DBaseTexture9* CD3D9Texture::getDX9Texture() const +{ + return Texture; +} + + +//! returns if texture has mipmap levels +bool CD3D9Texture::hasMipMaps() const +{ + return HasMipMaps; +} + + +void CD3D9Texture::copy16BitMipMap(char* src, char* tgt, + s32 width, s32 height, + s32 pitchsrc, s32 pitchtgt) const +{ + for (s32 y=0; y1) + size.Width /=2; + if (size.Height>1) + size.Height /=2; + ++level; + IDirect3DSurface9* mipSurface = 0; + HRESULT hr = Texture->GetSurfaceLevel(level, &mipSurface); + if (FAILED(hr) || !mipSurface) + { + os::Printer::log("Could not get mipmap level", ELL_WARNING); + return; + } + D3DSURFACE_DESC mipDesc; + mipSurface->GetDesc(&mipDesc); + D3DLOCKED_RECT miplr; + + // lock mipmap surface + if (FAILED(mipSurface->LockRect(&miplr, NULL, 0))) + { + mipSurface->Release(); + os::Printer::log("Could not lock texture", ELL_WARNING); + return; + } + + memcpy(miplr.pBits, mipmapData, size.getArea()*getPitch()/TextureSize.Width); + mipmapData = (u8*)mipmapData+size.getArea()*getPitch()/TextureSize.Width; + // unlock + mipSurface->UnlockRect(); + // release + mipSurface->Release(); + } while (size.Width != 1 || size.Height != 1); + } + else if (HasMipMaps) + { + // create mip maps. +#ifdef _IRR_USE_D3DXFilterTexture_ + // The D3DXFilterTexture function seems to get linked wrong when + // compiling with both D3D8 and 9, causing it not to work in the D3D9 device. + // So mipmapgeneration is replaced with my own bad generation + HRESULT hr = D3DXFilterTexture(Texture, NULL, D3DX_DEFAULT, D3DX_DEFAULT); + if (FAILED(hr)) +#endif + createMipMaps(); + } +} + + +//! returns if it is a render target +bool CD3D9Texture::isRenderTarget() const +{ + return IsRenderTarget; +} + + +//! Returns pointer to the render target surface +IDirect3DSurface9* CD3D9Texture::getRenderTargetSurface() +{ + if (!IsRenderTarget) + return 0; + + IDirect3DSurface9 *pRTTSurface = 0; + if (Texture) + Texture->GetSurfaceLevel(0, &pRTTSurface); + + if (pRTTSurface) + pRTTSurface->Release(); + + return pRTTSurface; +} + + +void CD3D9Texture::setPitch(D3DFORMAT d3dformat) +{ + switch(d3dformat) + { + case D3DFMT_X1R5G5B5: + case D3DFMT_A1R5G5B5: + Pitch = TextureSize.Width * 2; + break; + case D3DFMT_A8B8G8R8: + case D3DFMT_A8R8G8B8: + case D3DFMT_X8R8G8B8: + Pitch = TextureSize.Width * 4; + break; + case D3DFMT_R5G6B5: + Pitch = TextureSize.Width * 2; + break; + case D3DFMT_R8G8B8: + Pitch = TextureSize.Width * 3; + break; + default: + Pitch = 0; + }; +} + + +} // end namespace video +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_DIRECT3D_9_ diff --git a/lib/irrlicht/source/Irrlicht/CD3D9Texture.h b/lib/irrlicht/source/Irrlicht/CD3D9Texture.h new file mode 100644 index 000000000..233fa6c84 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/CD3D9Texture.h @@ -0,0 +1,132 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_DIRECTX9_TEXTURE_H_INCLUDED__ +#define __C_DIRECTX9_TEXTURE_H_INCLUDED__ + +#include "IrrCompileConfig.h" +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + +#include "ITexture.h" +#include "IImage.h" +#if defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +#include "irrMath.h" // needed by borland for sqrtf define +#endif +#include + +namespace irr +{ +namespace video +{ + +class CD3D9Driver; +// forward declaration for RTT depth buffer handling +struct SDepthSurface; +/*! + interface for a Video Driver dependent Texture. +*/ +class CD3D9Texture : public ITexture +{ +public: + + //! constructor + CD3D9Texture(IImage* image, CD3D9Driver* driver, + u32 flags, const io::path& name, void* mipmapData=0); + + //! rendertarget constructor + CD3D9Texture(CD3D9Driver* driver, const core::dimension2d& size, const io::path& name, + const ECOLOR_FORMAT format = ECF_UNKNOWN); + + //! destructor + virtual ~CD3D9Texture(); + + //! lock function + virtual void* lock(E_TEXTURE_LOCK_MODE mode=ETLM_READ_WRITE, u32 mipmapLevel=0); + + //! unlock function + virtual void unlock(); + + //! Returns original size of the texture. + virtual const core::dimension2d& getOriginalSize() const; + + //! Returns (=size) of the texture. + virtual const core::dimension2d& getSize() const; + + //! returns driver type of texture (=the driver, who created the texture) + virtual E_DRIVER_TYPE getDriverType() const; + + //! returns color format of texture + virtual ECOLOR_FORMAT getColorFormat() const; + + //! returns pitch of texture (in bytes) + virtual u32 getPitch() const; + + //! returns the DIRECT3D9 Texture + IDirect3DBaseTexture9* getDX9Texture() const; + + //! returns if texture has mipmap levels + bool hasMipMaps() const; + + //! Regenerates the mip map levels of the texture. Useful after locking and + //! modifying the texture + virtual void regenerateMipMapLevels(void* mipmapData=0); + + //! returns if it is a render target + virtual bool isRenderTarget() const; + + //! Returns pointer to the render target surface + IDirect3DSurface9* getRenderTargetSurface(); + + u64 getTextureHandler() const { return (u64)Texture; } + +private: + friend class CD3D9Driver; + + void createRenderTarget(const ECOLOR_FORMAT format = ECF_UNKNOWN); + + //! creates the hardware texture + bool createTexture(u32 flags, IImage * image); + + //! copies the image to the texture + bool copyTexture(IImage * image); + + //! Helper function for mipmap generation. + bool createMipMaps(u32 level=1); + + //! Helper function for mipmap generation. + void copy16BitMipMap(char* src, char* tgt, + s32 width, s32 height, s32 pitchsrc, s32 pitchtgt) const; + + //! Helper function for mipmap generation. + void copy32BitMipMap(char* src, char* tgt, + s32 width, s32 height, s32 pitchsrc, s32 pitchtgt) const; + + //! set Pitch based on the d3d format + void setPitch(D3DFORMAT d3dformat); + + IDirect3DDevice9* Device; + IDirect3DTexture9* Texture; + IDirect3DSurface9* RTTSurface; + CD3D9Driver* Driver; + SDepthSurface* DepthSurface; + core::dimension2d TextureSize; + core::dimension2d ImageSize; + s32 Pitch; + u32 MipLevelLocked; + ECOLOR_FORMAT ColorFormat; + + bool HasMipMaps; + bool HardwareMipMaps; + bool IsRenderTarget; +}; + + +} // end namespace video +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_DIRECT3D_9_ + +#endif // __C_DIRECTX9_TEXTURE_H_INCLUDED__ + + diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp index 94325702d..2ea8ab036 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp @@ -33,7 +33,10 @@ namespace irr io::IFileSystem* io, CIrrDeviceSDL* device); IVideoDriver* createOGLES2Driver(const SIrrlichtCreationParameters& params, io::IFileSystem* io, CIrrDeviceSDL* device, u32 default_fb); - +#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + IVideoDriver* createDirectX9Driver(const SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); +#endif } // end namespace video } // end namespace irr @@ -577,6 +580,16 @@ void CIrrDeviceSDL::createDriver() break; } + case video::EDT_DIRECT3D9: + { + #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + VideoDriver = video::createDirectX9Driver(CreationParams, FileSystem, Info.info.win.window); + #else + os::Printer::log("No DirectX 9 support compiled in.", ELL_ERROR); + #endif + break; + } + case video::EDT_NULL: VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize); break;