mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-03 00:40:41 +01:00
8310a3fbad
git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6000 dfc29bdd-3216-0410-991c-e03cc46cb475
540 lines
15 KiB
C++
540 lines
15 KiB
C++
// 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 "CD3D9ShaderMaterialRenderer.h"
|
|
#include "IShaderConstantSetCallBack.h"
|
|
#include "IMaterialRendererServices.h"
|
|
#include "IVideoDriver.h"
|
|
#include "os.h"
|
|
#include "irrString.h"
|
|
|
|
#ifndef _IRR_D3D_NO_SHADER_DEBUGGING
|
|
#include <stdio.h>
|
|
#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);
|
|
}
|
|
}
|
|
|
|
services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
|
|
|
|
if (BaseMaterial)
|
|
BaseMaterial->OnSetMaterial(material, lastMaterial, resetAllRenderstates, services);
|
|
|
|
if (CallBack)
|
|
CallBack->OnSetMaterial(material);
|
|
}
|
|
|
|
|
|
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 management 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 emit some debug information 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 emit some debug information 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 = LoadLibrary(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 = LoadLibrary(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 = LoadLibrary(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 = LoadLibrary(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_
|