1 // Copyright (C) 2002-2012 Nikolaus Gebhardt 2 // This file is part of the "Irrlicht Engine". 3 // For conditions of distribution and use, see copyright notice in irrlicht.h 4 5 #include "IrrCompileConfig.h" 6 #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ 7 8 #include "CD3D9NormalMapRenderer.h" 9 #include "IVideoDriver.h" 10 #include "IMaterialRendererServices.h" 11 #include "os.h" 12 #include "SLight.h" 13 14 namespace irr 15 { 16 namespace video 17 { 18 19 // 1.1 Shaders with two lights and vertex based attenuation 20 21 // Irrlicht Engine D3D9 render path normal map vertex shader 22 const char D3D9_NORMAL_MAP_VSH[] = 23 ";Irrlicht Engine 0.8 D3D9 render path normal map vertex shader\n"\ 24 "; c0-3: Transposed world matrix \n"\ 25 "; c8-11: Transposed worldViewProj matrix (Projection * View * World) \n"\ 26 "; c12: Light01 position \n"\ 27 "; c13: x,y,z: Light01 color; .w: 1/LightRadius� \n"\ 28 "; c14: Light02 position \n"\ 29 "; c15: x,y,z: Light02 color; .w: 1/LightRadius� \n"\ 30 "vs.1.1\n"\ 31 "dcl_position v0 ; position \n"\ 32 "dcl_normal v1 ; normal \n"\ 33 "dcl_color v2 ; color \n"\ 34 "dcl_texcoord0 v3 ; texture coord \n"\ 35 "dcl_texcoord1 v4 ; tangent \n"\ 36 "dcl_texcoord2 v5 ; binormal \n"\ 37 "\n"\ 38 "def c95, 0.5, 0.5, 0.5, 0.5 ; used for moving light vector to ps \n"\ 39 "\n"\ 40 "m4x4 oPos, v0, c8 ; transform position to clip space with worldViewProj matrix\n"\ 41 "\n"\ 42 "m3x3 r5, v4, c0 ; transform tangent U\n"\ 43 "m3x3 r7, v1, c0 ; transform normal W\n"\ 44 "m3x3 r6, v5, c0 ; transform binormal V\n"\ 45 "\n"\ 46 "m4x4 r4, v0, c0 ; vertex into world position\n"\ 47 "add r2, c12, -r4 ; vtxpos - lightpos1\n"\ 48 "add r3, c14, -r4 ; vtxpos - lightpos2\n"\ 49 "\n"\ 50 "dp3 r8.x, r5, r2 ; transform the light vector 1 with U, V, W\n"\ 51 "dp3 r8.y, r6, r2 \n"\ 52 "dp3 r8.z, r7, r2 \n"\ 53 "dp3 r9.x, r5, r3 ; transform the light vector 2 with U, V, W\n"\ 54 "dp3 r9.y, r6, r3 \n"\ 55 "dp3 r9.z, r7, r3 \n"\ 56 "\n"\ 57 "dp3 r8.w, r8, r8 ; normalize light vector 1 (r8)\n"\ 58 "rsq r8.w, r8.w \n"\ 59 "mul r8, r8, r8.w \n"\ 60 "dp3 r9.w, r9, r9 ; normalize light vector 2 (r9)\n"\ 61 "rsq r9.w, r9.w \n"\ 62 "mul r9, r9, r9.w \n"\ 63 "\n"\ 64 "mad oT2.xyz, r8.xyz, c95, c95 ; move light vector 1 from -1..1 into 0..1 \n"\ 65 "mad oT3.xyz, r9.xyz, c95, c95 ; move light vector 2 from -1..1 into 0..1 \n"\ 66 "\n"\ 67 " ; calculate attenuation of light 1 \n"\ 68 "dp3 r2.x, r2.xyz, r2.xyz ; r2.x = r2.x� + r2.y� + r2.z� \n"\ 69 "mul r2.x, r2.x, c13.w ; r2.x * attenutation \n"\ 70 "rsq r2, r2.x ; r2.xyzw = 1/sqrt(r2.x * attenutation)\n"\ 71 "mul oD0, r2, c13 ; resulting light color = lightcolor * attenuation \n"\ 72 "\n"\ 73 " ; calculate attenuation of light 2 \n"\ 74 "dp3 r3.x, r3.xyz, r3.xyz ; r3.x = r3.x� + r3.y� + r3.z� \n"\ 75 "mul r3.x, r3.x, c15.w ; r2.x * attenutation \n"\ 76 "rsq r3, r3.x ; r2.xyzw = 1/sqrt(r2.x * attenutation)\n"\ 77 "mul oD1, r3, c15 ; resulting light color = lightcolor * attenuation \n"\ 78 "\n"\ 79 "mov oT0.xy, v3.xy ; move out texture coordinates 1\n"\ 80 "mov oT1.xy, v3.xy ; move out texture coordinates 2\n"\ 81 "mov oD0.a, v2.a ; move out original alpha value \n"\ 82 "\n"; 83 84 // Irrlicht Engine D3D9 render path normal map pixel shader 85 const char D3D9_NORMAL_MAP_PSH_1_1[] = 86 ";Irrlicht Engine 0.8 D3D9 render path normal map pixel shader\n"\ 87 ";Input: \n"\ 88 ";t0: color map texture coord \n"\ 89 ";t1: normal map texture coords \n"\ 90 ";t2: light 1 vector in tangent space \n"\ 91 ";v0: light 1 color \n"\ 92 ";t3: light 2 vector in tangent space \n"\ 93 ";v1: light 2 color \n"\ 94 ";v0.a: vertex alpha value \n"\ 95 "ps.1.1 \n"\ 96 "tex t0 ; sample color map \n"\ 97 "tex t1 ; sample normal map\n"\ 98 "texcoord t2 ; fetch light vector 1\n"\ 99 "texcoord t3 ; fetch light vector 2\n"\ 100 "\n"\ 101 "dp3_sat r0, t1_bx2, t2_bx2 ; normal dot light 1 (_bx2 because moved into 0..1)\n"\ 102 "mul r0, r0, v0 ; luminance1 * light color 1 \n"\ 103 "\n"\ 104 "dp3_sat r1, t1_bx2, t3_bx2 ; normal dot light 2 (_bx2 because moved into 0..1)\n"\ 105 "mad r0, r1, v1, r0 ; (luminance2 * light color 2) + luminance 1 \n"\ 106 "\n"\ 107 "mul r0.xyz, t0, r0 ; total luminance * base color\n"\ 108 "+mov r0.a, v0.a ; write interpolated vertex alpha value \n"\ 109 "\n"\ 110 ""; 111 112 // Higher-quality normal map pixel shader (requires PS 2.0) 113 // uses per-pixel normalization for improved accuracy 114 const char D3D9_NORMAL_MAP_PSH_2_0[] = 115 ";Irrlicht Engine 0.8 D3D9 render path normal map pixel shader\n"\ 116 ";Input: \n"\ 117 ";t0: color map texture coord \n"\ 118 ";t1: normal map texture coords \n"\ 119 ";t2: light 1 vector in tangent space \n"\ 120 ";v0: light 1 color \n"\ 121 ";t3: light 2 vector in tangent space \n"\ 122 ";v1: light 2 color \n"\ 123 ";v0.a: vertex alpha value \n"\ 124 125 "ps_2_0 \n"\ 126 "def c0, 0, 0, 0, 0\n"\ 127 "def c1, 1.0, 1.0, 1.0, 1.0\n"\ 128 "def c2, 2.0, 2.0, 2.0, 2.0\n"\ 129 "def c3, -.5, -.5, -.5, -.5\n"\ 130 "dcl t0\n"\ 131 "dcl t1\n"\ 132 "dcl t2\n"\ 133 "dcl t3\n"\ 134 "dcl v1\n"\ 135 "dcl v0\n"\ 136 "dcl_2d s0\n"\ 137 "dcl_2d s1\n"\ 138 139 "texld r0, t0, s0 ; sample color map into r0 \n"\ 140 "texld r4, t0, s1 ; sample normal map into r4\n"\ 141 "add r4, r4, c3 ; bias the normal vector\n"\ 142 "add r5, t2, c3 ; bias the light 1 vector into r5\n"\ 143 "add r6, t3, c3 ; bias the light 2 vector into r6\n"\ 144 145 "nrm r1, r4 ; normalize the normal vector into r1\n"\ 146 "nrm r2, r5 ; normalize the light1 vector into r2\n"\ 147 "nrm r3, r6 ; normalize the light2 vector into r3\n"\ 148 149 "dp3 r2, r2, r1 ; let r2 = normal DOT light 1 vector\n"\ 150 "max r2, r2, c0 ; clamp result to positive numbers\n"\ 151 "mul r2, r2, v0 ; let r2 = luminance1 * light color 1 \n"\ 152 153 "dp3 r3, r3, r1 ; let r3 = normal DOT light 2 vector\n"\ 154 "max r3, r3, c0 ; clamp result to positive numbers\n"\ 155 156 "mad r2, r3, v1, r2 ; let r2 = (luminance2 * light color 2) + (luminance2 * light color 1) \n"\ 157 158 "mul r2, r2, r0 ; let r2 = total luminance * base color\n"\ 159 "mov r2.w, v0.w ; write interpolated vertex alpha value \n"\ 160 161 "mov oC0, r2 ; copy r2 to the output register \n"\ 162 163 "\n"\ 164 ""; 165 CD3D9NormalMapRenderer(IDirect3DDevice9 * d3ddev,video::IVideoDriver * driver,s32 & outMaterialTypeNr,IMaterialRenderer * baseMaterial)166 CD3D9NormalMapRenderer::CD3D9NormalMapRenderer( 167 IDirect3DDevice9* d3ddev, video::IVideoDriver* driver, 168 s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial) 169 : CD3D9ShaderMaterialRenderer(d3ddev, driver, 0, baseMaterial) 170 { 171 #ifdef _DEBUG 172 setDebugName("CD3D9NormalMapRenderer"); 173 #endif 174 175 // set this as callback. We could have done this in 176 // the initialization list, but some compilers don't like it. 177 178 CallBack = this; 179 180 // basically, this thing simply compiles the hardcoded shaders 181 // if the hardware is able to do them, otherwise it maps to the 182 // base material 183 184 if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) || 185 !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1)) 186 { 187 // this hardware is not able to do shaders. Fall back to 188 // base material. 189 outMaterialTypeNr = driver->addMaterialRenderer(this); 190 return; 191 } 192 193 // check if already compiled normal map shaders are there. 194 195 video::IMaterialRenderer* renderer = driver->getMaterialRenderer(EMT_NORMAL_MAP_SOLID); 196 if (renderer) 197 { 198 // use the already compiled shaders 199 video::CD3D9NormalMapRenderer* nmr = (video::CD3D9NormalMapRenderer*)renderer; 200 VertexShader = nmr->VertexShader; 201 if (VertexShader) 202 VertexShader->AddRef(); 203 204 PixelShader = nmr->PixelShader; 205 if (PixelShader) 206 PixelShader->AddRef(); 207 208 outMaterialTypeNr = driver->addMaterialRenderer(this); 209 } 210 else 211 { 212 // compile shaders on our own 213 if (driver->queryFeature(video::EVDF_PIXEL_SHADER_2_0)) 214 { 215 init(outMaterialTypeNr, D3D9_NORMAL_MAP_VSH, D3D9_NORMAL_MAP_PSH_2_0); 216 } 217 else 218 { 219 init(outMaterialTypeNr, D3D9_NORMAL_MAP_VSH, D3D9_NORMAL_MAP_PSH_1_1); 220 } 221 } 222 // something failed, use base material 223 if (-1==outMaterialTypeNr) 224 driver->addMaterialRenderer(this); 225 } 226 227 ~CD3D9NormalMapRenderer()228 CD3D9NormalMapRenderer::~CD3D9NormalMapRenderer() 229 { 230 if (CallBack == this) 231 CallBack = 0; 232 } 233 234 OnRender(IMaterialRendererServices * service,E_VERTEX_TYPE vtxtype)235 bool CD3D9NormalMapRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype) 236 { 237 if (vtxtype != video::EVT_TANGENTS) 238 { 239 os::Printer::log("Error: Normal map renderer only supports vertices of type EVT_TANGENTS", ELL_ERROR); 240 return false; 241 } 242 243 return CD3D9ShaderMaterialRenderer::OnRender(service, vtxtype); 244 } 245 246 247 //! Returns the render capability of the material. getRenderCapability() const248 s32 CD3D9NormalMapRenderer::getRenderCapability() const 249 { 250 if (Driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) && 251 Driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1)) 252 return 0; 253 254 return 1; 255 } 256 257 258 //! Called by the engine when the vertex and/or pixel shader constants 259 //! for an material renderer should be set. OnSetConstants(IMaterialRendererServices * services,s32 userData)260 void CD3D9NormalMapRenderer::OnSetConstants(IMaterialRendererServices* services, s32 userData) 261 { 262 video::IVideoDriver* driver = services->getVideoDriver(); 263 264 // set transposed world matrix 265 services->setVertexShaderConstant(driver->getTransform(video::ETS_WORLD).getTransposed().pointer(), 0, 4); 266 267 // set transposed worldViewProj matrix 268 core::matrix4 worldViewProj(driver->getTransform(video::ETS_PROJECTION)); 269 worldViewProj *= driver->getTransform(video::ETS_VIEW); 270 worldViewProj *= driver->getTransform(video::ETS_WORLD); 271 services->setVertexShaderConstant(worldViewProj.getTransposed().pointer(), 8, 4); 272 273 // here we've got to fetch the fixed function lights from the 274 // driver and set them as constants 275 276 u32 cnt = driver->getDynamicLightCount(); 277 278 for (u32 i=0; i<2; ++i) 279 { 280 SLight light; 281 282 if (i<cnt) 283 light = driver->getDynamicLight(i); 284 else 285 { 286 light.DiffuseColor.set(0,0,0); // make light dark 287 light.Radius = 1.0f; 288 } 289 290 light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation 291 292 services->setVertexShaderConstant(reinterpret_cast<const f32*>(&light.Position), 12+(i*2), 1); 293 services->setVertexShaderConstant(reinterpret_cast<const f32*>(&light.DiffuseColor), 13+(i*2), 1); 294 } 295 296 // this is not really necessary in d3d9 (used a def instruction), but to be sure: 297 f32 c95[] = {0.5f, 0.5f, 0.5f, 0.5f}; 298 services->setVertexShaderConstant(c95, 95, 1); 299 } 300 301 302 } // end namespace video 303 } // end namespace irr 304 305 #endif // _IRR_COMPILE_WITH_DIRECT3D_9_ 306 307