irrlicht/source/Irrlicht/COpenGLNormalMapRenderer.cpp

297 lines
9.9 KiB
C++

// Copyright (C) 2002-2007 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_OPENGL_
#include "COpenGLNormalMapRenderer.h"
#include "IGPUProgrammingServices.h"
#include "IShaderConstantSetCallBack.h"
#include "IVideoDriver.h"
#include "os.h"
#include "COpenGLDriver.h"
namespace irr
{
namespace video
{
// Irrlicht Engine OpenGL render path normal map vertex shader
// I guess it could be optimized a lot, because I wrote it in D3D ASM and
// transferred it 1:1 to OpenGL
const char OPENGL_NORMAL_MAP_VSH[] =
"!!ARBvp1.0\n"\
"#input\n"\
"# 0-3: transposed world matrix;\n"\
"#;12: Light01 position \n"\
"#;13: x,y,z: Light01 color; .w: 1/LightRadius^2 \n"\
"#;14: Light02 position \n"\
"#;15: x,y,z: Light02 color; .w: 1/LightRadius^2 \n"\
"\n"\
"ATTRIB InPos = vertex.position;\n"\
"ATTRIB InColor = vertex.color;\n"\
"ATTRIB InNormal = vertex.normal;\n"\
"ATTRIB InTexCoord = vertex.texcoord[0];\n"\
"ATTRIB InTangent = vertex.texcoord[1];\n"\
"ATTRIB InBinormal = vertex.texcoord[2];\n"\
"\n"\
"#output\n"\
"OUTPUT OutPos = result.position;\n"\
"OUTPUT OutLightColor1 = result.color.primary;\n"\
"OUTPUT OutLightColor2 = result.color.secondary;\n"\
"OUTPUT OutTexCoord = result.texcoord[0];\n"\
"OUTPUT OutLightVector1 = result.texcoord[1];\n"\
"OUTPUT OutLightVector2 = result.texcoord[2];\n"\
"\n"\
"PARAM MVP[4] = { state.matrix.mvp }; # modelViewProjection matrix.\n"\
"TEMP Temp;\n"\
"TEMP TempColor;\n"\
"TEMP TempNormal;\n"\
"TEMP TempTangent;\n"\
"TEMP TempBinormal;\n"\
"TEMP TempLightVector1;\n"\
"TEMP TempLightVector2;\n"\
"TEMP TempTransLightV1;\n"\
"TEMP TempTransLightV2;\n"\
"\n"\
"# transform position to clip space \n"\
"DP4 OutPos.x, MVP[0], InPos;\n"\
"DP4 OutPos.y, MVP[1], InPos;\n"\
"DP4 OutPos.z, MVP[2], InPos;\n"\
"DP4 OutPos.w, MVP[3], InPos;\n"\
"\n"\
"# transform normal \n"\
"DP3 TempNormal.x, InNormal.x, program.local[0];\n"\
"DP3 TempNormal.y, InNormal.y, program.local[1]; \n"\
"DP3 TempNormal.z, InNormal.z, program.local[2];\n"\
"\n"\
"# transform tangent \n"\
"DP3 TempTangent.x, InTangent.x, program.local[0];\n"\
"DP3 TempTangent.y, InTangent.y, program.local[1]; \n"\
"DP3 TempTangent.z, InTangent.z, program.local[2];\n"\
"\n"\
"# transform binormal \n"\
"DP3 TempBinormal.x, InBinormal.x, program.local[0];\n"\
"DP3 TempBinormal.y, InBinormal.y, program.local[1]; \n"\
"DP3 TempBinormal.z, InBinormal.z, program.local[2];\n"\
"\n"\
"# vertex into world position \n"\
"DP4 Temp.x, InPos, program.local[0];\n"\
"DP4 Temp.y, InPos, program.local[1];\n"\
"DP4 Temp.z, InPos, program.local[2];\n"\
"DP4 Temp.w, InPos, program.local[3];\n"\
"\n"\
"# vertex - lightpositions \n"\
"SUB TempLightVector1, program.local[12], Temp; \n"\
"SUB TempLightVector2, program.local[14], Temp; \n"\
"\n"\
"# transform the light vector 1 with U, V, W \n"\
"DP3 TempTransLightV1.x, TempTangent, TempLightVector1; \n"\
"DP3 TempTransLightV1.y, TempBinormal, TempLightVector1; \n"\
"DP3 TempTransLightV1.z, TempNormal, TempLightVector1; \n"\
"\n"\
"# transform the light vector 2 with U, V, W \n"\
"DP3 TempTransLightV2.x, TempTangent, TempLightVector2; \n"\
"DP3 TempTransLightV2.y, TempBinormal, TempLightVector2; \n"\
"DP3 TempTransLightV2.z, TempNormal, TempLightVector2; \n"\
"\n"\
"# normalize light vector 1 \n"\
"DP3 TempTransLightV1.w, TempTransLightV1, TempTransLightV1; \n"\
"RSQ TempTransLightV1.w, TempTransLightV1.w; \n"\
"MUL TempTransLightV1, TempTransLightV1, TempTransLightV1.w;\n"\
"\n"\
"# normalize light vector 2 \n"\
"DP3 TempTransLightV2.w, TempTransLightV2, TempTransLightV2; \n"\
"RSQ TempTransLightV2.w, TempTransLightV2.w; \n"\
"MUL TempTransLightV2, TempTransLightV2, TempTransLightV2.w;\n"\
"\n"\
"\n"\
"# move light vectors out\n"\
"MAD OutLightVector1, TempTransLightV1, {0.5,0.5,0.5,0.5}, {0.5,0.5,0.5,0.5}; \n"\
"MAD OutLightVector2, TempTransLightV2, {0.5,0.5,0.5,0.5}, {0.5,0.5,0.5,0.5}; \n"\
"\n"\
"# calculate attenuation of light 1\n"\
"MOV TempLightVector1.w, {0,0,0,0}; \n"\
"DP3 TempLightVector1.x, TempLightVector1, TempLightVector1; \n"\
"MUL TempLightVector1.x, TempLightVector1.x, program.local[13].w; \n"\
"RSQ TempLightVector1, TempLightVector1.x; \n"\
"MUL OutLightColor1, TempLightVector1, program.local[13]; # resulting light color = lightcolor * attenuation \n"\
"\n"\
"# calculate attenuation of light 2\n"\
"MOV TempLightVector2.w, {0,0,0,0}; \n"\
"DP3 TempLightVector2.x, TempLightVector2, TempLightVector2; \n"\
"MUL TempLightVector2.x, TempLightVector2.x, program.local[15].w; \n"\
"RSQ TempLightVector2, TempLightVector2.x; \n"\
"MUL OutLightColor2, TempLightVector2, program.local[15]; # resulting light color = lightcolor * attenuation \n"\
"\n"\
"# move out texture coordinates and original alpha value\n"\
"MOV OutTexCoord, InTexCoord; \n"\
"MOV OutLightColor1.w, InColor.w; \n"\
"\n"\
"END\n";
// Irrlicht Engine OpenGL render path normal map pixel shader
// I guess it could be optimized a bit, because I wrote it in D3D ASM and
// transfered it 1:1 to OpenGL
const char OPENGL_NORMAL_MAP_PSH[] =
"!!ARBfp1.0\n"\
"\n"\
"#Input\n"\
"ATTRIB inTexCoord = fragment.texcoord[0]; \n"\
"ATTRIB light1Vector = fragment.texcoord[1]; \n"\
"ATTRIB light2Vector = fragment.texcoord[2]; \n"\
"ATTRIB light1Color = fragment.color.primary; \n"\
"ATTRIB light2Color = fragment.color.secondary; \n"\
"\n"\
"#Output\n"\
"OUTPUT outColor = result.color;\n"\
"TEMP temp;\n"\
"TEMP temp2;\n"\
"TEMP colorMapColor;\n"\
"TEMP normalMapColor;\n"\
"\n"\
"# fetch color and normal map; \n"\
"TXP colorMapColor, inTexCoord, texture[0], 2D; \n"\
"TXP normalMapColor, inTexCoord, texture[1], 2D; \n"\
"\n"\
"# calculate color of light1; \n"\
"MAD normalMapColor, normalMapColor, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
"MAD temp, light1Vector, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
"DP3_SAT temp, normalMapColor, temp; \n"\
"MUL temp, light1Color, temp; \n"\
"\n"\
"# calculate color of light2; \n"\
"MAD temp2, light2Vector, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
"DP3_SAT temp2, normalMapColor, temp2; \n"\
"MAD temp, light2Color, temp2, temp; \n"\
"\n"\
"# luminance * base color; \n"\
"MUL outColor, temp, colorMapColor; \n"\
"MOV outColor.a, light1Color.a; #write interpolated vertex alpha value\n"\
"\n"\
"END\n";
//! Constructor
COpenGLNormalMapRenderer::COpenGLNormalMapRenderer(video::COpenGLDriver* driver,
s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial)
: COpenGLShaderMaterialRenderer(driver, 0, baseMaterial), CompiledShaders(true)
{
// 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_ARB_FRAGMENT_PROGRAM_1) ||
!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_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::COpenGLNormalMapRenderer* nmr = reinterpret_cast<video::COpenGLNormalMapRenderer*>(renderer);
CompiledShaders = false;
VertexShader = nmr->VertexShader;
PixelShader = nmr->PixelShader;
outMaterialTypeNr = driver->addMaterialRenderer(this);
}
else
{
// compile shaders on our own
init(outMaterialTypeNr, OPENGL_NORMAL_MAP_VSH, OPENGL_NORMAL_MAP_PSH, EVT_TANGENTS);
}
}
//! Destructor
COpenGLNormalMapRenderer::~COpenGLNormalMapRenderer()
{
if (CallBack == this)
CallBack = 0;
if (!CompiledShaders)
{
// prevent this from deleting shaders we did not create
VertexShader = 0;
PixelShader = 0;
}
}
//! Returns the render capability of the material.
s32 COpenGLNormalMapRenderer::getRenderCapability() const
{
if (Driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1) &&
Driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_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 COpenGLNormalMapRenderer::OnSetConstants(IMaterialRendererServices* services, s32 userData)
{
video::IVideoDriver* driver = services->getVideoDriver();
// set transposed world matrix
const core::matrix4& tWorld = driver->getTransform(video::ETS_WORLD).getTransposed();
services->setVertexShaderConstant(tWorld.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);
core::matrix4 tr(worldViewProj.getTransposed());
services->setVertexShaderConstant(tr.pointer(), 8, 4);
// here we fetch the fixed function lights from the driver
// and set them as constants
u32 cnt = driver->getDynamicLightCount();
for (u32 i=0; i<2; ++i)
{
video::SLight light;
if (i<cnt)
light = driver->getDynamicLight(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<const f32*>(&light.Position), 12+(i*2), 1);
services->setVertexShaderConstant(
reinterpret_cast<const f32*>(&light.DiffuseColor), 13+(i*2), 1);
}
}
} // end namespace video
} // end namespace irr
#endif