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