diff --git a/data/shaders/normalmap.frag b/data/shaders/normalmap.frag new file mode 100644 index 000000000..6628f77f3 --- /dev/null +++ b/data/shaders/normalmap.frag @@ -0,0 +1,23 @@ +// By http://content.gpwiki.org/index.php/OpenGL:Tutorials:GLSL_Bump_Mapping +// Released under GNU FDL license, without invariant (so DFSG-compliant, see +// http://wiki.debian.org/DFSGLicenses#Exception) + +uniform sampler2D BumpTex; //The bump-map + uniform sampler2D DecalTex; //The texture + varying vec4 passcolor; //Receiving the vertex color from the vertex shader + varying vec3 LightDir; //Receiving the transformed light direction + void main() + { + //Get the color of the bump-map + vec3 BumpNorm = vec3(texture2D(BumpTex, gl_TexCoord[0].xy)); + //Get the color of the texture + vec3 DecalCol = vec3(texture2D(DecalTex, gl_TexCoord[0].xy)); + //Expand the bump-map into a normalized signed vector + BumpNorm = (BumpNorm -0.5) * 2.0; + //Find the dot product between the light direction and the normal + float NdotL = max(dot(BumpNorm, LightDir), 0.0) / 3.0 * 2.1 + 0.5; + //Calculate the final color gl_FragColor + vec3 diffuse = NdotL * passcolor.xyz * DecalCol; + //Set the color of the fragment... If you want specular lighting or other types add it here + gl_FragColor = vec4(diffuse, passcolor.w); + } \ No newline at end of file diff --git a/data/shaders/normalmap.vert b/data/shaders/normalmap.vert new file mode 100644 index 000000000..c555f1aa5 --- /dev/null +++ b/data/shaders/normalmap.vert @@ -0,0 +1,24 @@ +// By http://content.gpwiki.org/index.php/OpenGL:Tutorials:GLSL_Bump_Mapping +// Released under GNU FDL license, without invariant (so DFSG-compliant, see +// http://wiki.debian.org/DFSGLicenses#Exception) + +varying vec4 passcolor; //The vertex color passed + varying vec3 LightDir; //The transformed light direction, to pass to the fragment shader + attribute vec3 tangent; //The inverse tangent to the geometry + attribute vec3 binormal; //The inverse binormal to the geometry + uniform vec3 lightdir; //The direction the light is shining + void main() + { + //Put the color in a varying variable + passcolor = gl_Color; + //Put the vertex in the position passed + gl_Position = ftransform(); + //Construct a 3x3 matrix from the geometry’s inverse tangent, binormal, and normal + mat3 rotmat = mat3(tangent,binormal,gl_Normal); + //Rotate the light into tangent space + LightDir = rotmat * normalize(lightdir); + //Normalize the light + normalize(LightDir); + //Use the first set of texture coordinates in the fragment shader + gl_TexCoord[0] = gl_MultiTexCoord0; + } diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index 98d02c5a7..b9ed8e3b1 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -33,10 +33,37 @@ #include "io/xml_node.hpp" #include "utils/string_utils.hpp" +#include +#include +#include +using namespace irr::video; const unsigned int UCLAMP = 1; const unsigned int VCLAMP = 2; + +class NormalMapProvider : public video::IShaderConstantSetCallBack +{ +public: + LEAK_CHECK() + + virtual void OnSetConstants(irr::video::IMaterialRendererServices *services, s32 userData) + { + // Irrlicht knows this is actually a GLint and makes the conversion + int decaltex = 0.0f; + services->setPixelShaderConstant("DecalTex", (float*)&decaltex, 1); + + // Irrlicht knows this is actually a GLint and makes the conversion + int bumptex = 1.0f; + services->setPixelShaderConstant("BumpTex", (float*)&bumptex, 1); + + // TODO: check the position of the sun + const float lightdir[] = {0.5f, 0.5f, 1.0f}; + services->setVertexShaderConstant("lightdir", lightdir, 3); + } +}; + + //----------------------------------------------------------------------------- /** Create a new material using the parameters specified in the xml file. * \param node Node containing the parameters for this material. @@ -44,7 +71,6 @@ const unsigned int VCLAMP = 2; */ Material::Material(const XMLNode *node, int index) { - node->get("name", &m_texname); if (m_texname=="") { @@ -248,6 +274,7 @@ void Material::init(unsigned int index) m_normal_map = false; m_parallax_map = false; m_is_heightmap = false; + m_normal_map_provider = NULL; for (int n=0; nremoveTexture(m_texture); } + if (m_normal_map_provider != NULL) + { + m_normal_map_provider->drop(); + m_normal_map_provider = NULL; + } + // If a special sfx is installed (that isn't part of stk itself), the // entry needs to be removed from the sfx_manager's mapping, since other // tracks might use the same name. @@ -450,7 +483,7 @@ void Material::setSFXSpeed(SFXBase *sfx, float speed) const /** Sets the appropriate flags in an irrlicht SMaterial. * \param material The irrlicht SMaterial which gets the flags set. */ -void Material::setMaterialProperties(video::SMaterial *m) const +void Material::setMaterialProperties(video::SMaterial *m) { // !!======== This method is only called for materials that can be found in // materials.xml, if you want to set flags for all surfaces, see @@ -513,14 +546,38 @@ void Material::setMaterialProperties(video::SMaterial *m) const } if (m_normal_map) { - video::ITexture* tex = irr_driver->getTexture(m_normal_map_tex) ; - if (m_is_heightmap) + IVideoDriver* video_driver = irr_driver->getVideoDriver(); + if (video_driver->queryFeature(video::EVDF_ARB_GLSL) && + video_driver->queryFeature(video::EVDF_PIXEL_SHADER_2_0) && + video_driver->queryFeature(video::EVDF_RENDER_TO_TARGET)) { - irr_driver->getVideoDriver()->makeNormalMapTexture( tex ); + ITexture* tex = irr_driver->getTexture(m_normal_map_tex); + if (m_is_heightmap) + { + video_driver->makeNormalMapTexture( tex ); + } + m->setTexture(1, tex); + + if (m_normal_map_provider == NULL) + { + m_normal_map_provider = new NormalMapProvider(); + } + + // Material and shaders + IGPUProgrammingServices* gpu = video_driver->getGPUProgrammingServices(); + s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( + (file_manager->getDataDir() + "shaders/normalmap.vert").c_str(), "main", + video::EVST_VS_2_0, + (file_manager->getDataDir() + "shaders/normalmap.frag").c_str(), "main", + video::EPST_PS_2_0, + m_normal_map_provider, + video::EMT_SOLID_2_LAYER ); + m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->Lighting = false; + m->ZWriteEnable = true; + + modes++; } - m->setTexture(1, tex); - m->MaterialType = video::EMT_NORMAL_MAP_SOLID; - modes++; } if (m_parallax_map) { @@ -630,3 +687,5 @@ void Material::adjustForFog(scene::ISceneNode* parent, video::SMaterial *m, parent->setMaterialFlag(video::EMF_FOG_ENABLE, m_fog && use_fog); } } // adjustForFog + +//----------------------------------------------------------------------------- diff --git a/src/graphics/material.hpp b/src/graphics/material.hpp index c7dd8d722..7422c4693 100644 --- a/src/graphics/material.hpp +++ b/src/graphics/material.hpp @@ -38,6 +38,8 @@ class XMLNode; class SFXBase; class ParticleKind; +class NormalMapProvider; + /** * \ingroup graphics */ @@ -156,6 +158,9 @@ private: std::string m_mask; + /** Only used if normal maps are used */ + NormalMapProvider* m_normal_map_provider; + void init (unsigned int index); void install (bool is_full_path=false); void initCustomSFX(const XMLNode *sfx); @@ -168,7 +173,7 @@ public: ~Material (); void setSFXSpeed(SFXBase *sfx, float speed) const; - void setMaterialProperties(video::SMaterial *m) const; + void setMaterialProperties(video::SMaterial *m); void adjustForFog(scene::ISceneNode* parent, video::SMaterial *m, bool use_fog) const; /** Returns the ITexture associated with this material. */ @@ -253,7 +258,6 @@ public: } // getZipperParameter bool isNormalMap() const { return m_normal_map; } - } ;