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_OPENGL_
7 
8 #include "COpenGLParallaxMapRenderer.h"
9 #include "COpenGLDriver.h"
10 #include "IGPUProgrammingServices.h"
11 #include "IShaderConstantSetCallBack.h"
12 #include "IVideoDriver.h"
13 #include "os.h"
14 
15 namespace irr
16 {
17 namespace video
18 {
19 
20 // Irrlicht Engine OpenGL render path parallax map vertex shader
21 // I guess it could be optimized a lot, because I wrote it in D3D ASM and
22 // transferred it 1:1 to OpenGL
23 const char OPENGL_PARALLAX_MAP_VSH[] =
24 	"!!ARBvp1.0\n"\
25 	"#input\n"\
26 	"# 0-3: transposed world matrix;\n"\
27 	"#;12: Light01 position \n"\
28 	"#;13: x,y,z: Light01 color; .w: 1/LightRadius^2 \n"\
29 	"#;14: Light02 position \n"\
30 	"#;15: x,y,z: Light02 color; .w: 1/LightRadius^2 \n"\
31 	"#;16: Eye position \n"\
32 	"\n"\
33 	"ATTRIB InPos = vertex.position;\n"\
34 	"ATTRIB InColor = vertex.color;\n"\
35 	"ATTRIB InNormal = vertex.normal;\n"\
36 	"ATTRIB InTexCoord = vertex.texcoord[0];\n"\
37 	"ATTRIB InTangent = vertex.texcoord[1];\n"\
38 	"ATTRIB InBinormal = vertex.texcoord[2];\n"\
39 	"\n"\
40 	"#output\n"\
41 	"OUTPUT OutPos = result.position;\n"\
42 	"OUTPUT OutLightColor1 = result.color.primary;\n"\
43 	"OUTPUT OutLightColor2 = result.color.secondary;\n"\
44 	"OUTPUT OutTexCoord = result.texcoord[0];\n"\
45 	"OUTPUT OutLightVector1 = result.texcoord[1];\n"\
46 	"OUTPUT OutLightVector2 = result.texcoord[2];\n"\
47 	"OUTPUT OutEyeVector = result.texcoord[3];\n"\
48 	"\n"\
49 	"PARAM MVP[4] = { state.matrix.mvp }; # modelViewProjection matrix.\n"\
50 	"TEMP Temp;\n"\
51 	"TEMP TempColor;\n"\
52 	"TEMP TempLightVector1;\n"\
53 	"TEMP TempLightVector2;\n"\
54 	"TEMP TempEyeVector;\n"\
55 	"TEMP TempTransLightV1;\n"\
56 	"TEMP TempTransLightV2;\n"\
57 	"\n"\
58 	"# transform position to clip space \n"\
59 	"DP4 OutPos.x, MVP[0], InPos;\n"\
60 	"DP4 OutPos.y, MVP[1], InPos;\n"\
61 	"DP4 Temp.z, MVP[2], InPos;\n"\
62 	"DP4 OutPos.w, MVP[3], InPos;\n"\
63 	"MOV OutPos.z, Temp.z;\n"\
64 	"MOV result.fogcoord.x, Temp.z;\n"\
65 	"\n"\
66 	"# vertex - lightpositions \n"\
67 	"SUB TempLightVector1, program.local[12], InPos; \n"\
68 	"SUB TempLightVector2, program.local[14], InPos; \n"\
69 	"\n"\
70 	"# eye vector \n"\
71 	"SUB Temp, program.local[16], InPos; \n"\
72 	"\n"\
73 	"# transform the light vector 1 with U, V, W \n"\
74 	"DP3 TempTransLightV1.x, InTangent, TempLightVector1; \n"\
75 	"DP3 TempTransLightV1.y, InBinormal, TempLightVector1; \n"\
76 	"DP3 TempTransLightV1.z, InNormal, TempLightVector1; \n"\
77 	"\n"\
78 	"# transform the light vector 2 with U, V, W \n"\
79 	"DP3 TempTransLightV2.x, InTangent, TempLightVector2; \n"\
80 	"DP3 TempTransLightV2.y, InBinormal, TempLightVector2; \n"\
81 	"DP3 TempTransLightV2.z, InNormal, TempLightVector2; \n"\
82 	"\n"\
83 	"# transform the eye vector with U, V, W \n"\
84 	"DP3 TempEyeVector.x, InTangent, Temp; \n"\
85 	"DP3 TempEyeVector.y, InBinormal, Temp; \n"\
86 	"DP3 TempEyeVector.z, InNormal, Temp; \n"\
87 	"\n"\
88 	"# normalize light vector 1 \n"\
89 	"DP3 TempTransLightV1.w, TempTransLightV1, TempTransLightV1; \n"\
90 	"RSQ TempTransLightV1.w, TempTransLightV1.w; \n"\
91 	"MUL TempTransLightV1, TempTransLightV1, TempTransLightV1.w;\n"\
92 	"\n"\
93 	"# normalize light vector 2 \n"\
94 	"DP3 TempTransLightV2.w, TempTransLightV2, TempTransLightV2; \n"\
95 	"RSQ TempTransLightV2.w, TempTransLightV2.w; \n"\
96 	"MUL TempTransLightV2, TempTransLightV2, TempTransLightV2.w;\n"\
97 	"\n"\
98 	"# normalize eye vector \n"\
99 	"DP3 TempEyeVector.w, TempEyeVector, TempEyeVector; \n"\
100 	"RSQ TempEyeVector.w, TempEyeVector.w; \n"\
101 	"MUL TempEyeVector, TempEyeVector, TempEyeVector.w;\n"\
102 	"MUL TempEyeVector, TempEyeVector, {1,-1,-1,1}; # flip x \n"\
103 	"\n"\
104 	"\n"\
105 	"# move light and eye vectors out\n"\
106 	"MAD OutLightVector1, TempTransLightV1, {0.5,0.5,0.5,0.5}, {0.5,0.5,0.5,0.5}; \n"\
107 	"MAD OutLightVector2, TempTransLightV2, {0.5,0.5,0.5,0.5}, {0.5,0.5,0.5,0.5}; \n"\
108 	"MAD OutEyeVector, TempEyeVector, {0.5,0.5,0.5,0.5}, {0.5,0.5,0.5,0.5}; \n"\
109 	"\n"\
110 	"# calculate attenuation of light 1\n"\
111 	"MOV TempLightVector1.w, {0,0,0,0}; \n"\
112 	"DP3 TempLightVector1.x, TempLightVector1, TempLightVector1; \n"\
113 	"MUL TempLightVector1.x, TempLightVector1.x, program.local[13].w;  \n"\
114 	"RSQ TempLightVector1, TempLightVector1.x; \n"\
115 	"MUL OutLightColor1, TempLightVector1, program.local[13]; # resulting light color = lightcolor * attenuation \n"\
116 	"\n"\
117 	"# calculate attenuation of light 2\n"\
118 	"MOV TempLightVector2.w, {0,0,0,0}; \n"\
119 	"DP3 TempLightVector2.x, TempLightVector2, TempLightVector2; \n"\
120 	"MUL TempLightVector2.x, TempLightVector2.x, program.local[15].w;  \n"\
121 	"RSQ TempLightVector2, TempLightVector2.x; \n"\
122 	"MUL OutLightColor2, TempLightVector2, program.local[15]; # resulting light color = lightcolor * attenuation \n"\
123 	"\n"\
124 	"# move out texture coordinates and original alpha value\n"\
125 	"MOV OutTexCoord, InTexCoord; \n"\
126 	"MOV OutLightColor1.w, InColor.w; \n"\
127 	"\n"\
128 	"END\n";
129 
130 // Irrlicht Engine OpenGL render path parallax map pixel shader
131 // I guess it could be optimized a bit, because I wrote it in D3D ASM and
132 // transfered it 1:1 to OpenGL
133 const char OPENGL_PARALLAX_MAP_PSH[] =
134 	"!!ARBfp1.0\n"\
135 	"#_IRR_FOG_MODE_\n"\
136 	"\n"\
137 	"#Input\n"\
138 	"ATTRIB inTexCoord = fragment.texcoord[0];   \n"\
139 	"ATTRIB light1Vector = fragment.texcoord[1]; \n"\
140 	"ATTRIB light2Vector = fragment.texcoord[2];    \n"\
141 	"ATTRIB eyeVector = fragment.texcoord[3];    \n"\
142 	"ATTRIB light1Color = fragment.color.primary;   \n"\
143 	"ATTRIB light2Color = fragment.color.secondary; \n"\
144 	"\n"\
145 	"#Output\n"\
146 	"OUTPUT outColor = result.color;\n"\
147 	"TEMP temp;\n"\
148 	"TEMP temp2;\n"\
149 	"TEMP colorMapColor;\n"\
150 	"TEMP normalMapColor;\n"\
151 	"\n"\
152 	"PARAM height_scale = program.local[0]; \n"\
153 	"# fetch color and normal map; \n"\
154 	"TXP normalMapColor, inTexCoord, texture[1], 2D; \n"\
155 	"MAD normalMapColor, normalMapColor, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
156 	"\n"\
157 	"\n"\
158 	"# extract eye vector (so subtract 0.5f and multiply by 2)\n"\
159 	"MAD temp, eyeVector, {2,2,2,2}, {-1,-1,-1,-1};\n"\
160 	"\n"\
161 	"# height = height * scale \n"\
162 	"MUL normalMapColor, normalMapColor, height_scale;\n"\
163 	"\n"\
164 	"# calculate new texture coord: height * eye + oldTexCoord\n"\
165 	"MAD temp, temp, normalMapColor.wwww, inTexCoord;\n"\
166 	"\n"\
167 	"# fetch new textures \n"\
168 	"TXP colorMapColor, temp, texture[0], 2D; \n"\
169 	"TXP normalMapColor, temp, texture[1], 2D; \n"\
170 	"\n"\
171 	"# calculate color of light1; \n"\
172 	"MAD normalMapColor, normalMapColor, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
173 	"MAD temp, light1Vector, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
174 	"DP3_SAT temp, normalMapColor, temp; \n"\
175 	"MUL temp, light1Color, temp; \n"\
176 	"\n"\
177 	"# calculate color of light2; \n"\
178 	"MAD temp2, light2Vector, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
179 	"DP3_SAT temp2, normalMapColor, temp2; \n"\
180 	"MAD temp, light2Color, temp2, temp; \n"\
181 	"\n"\
182 	"# luminance * base color; \n"\
183 	"MUL outColor, temp, colorMapColor; \n"\
184 	"MOV outColor.a, light1Color.a; #write interpolated vertex alpha value\n"\
185 	"\n"\
186 	"END\n";
187 
188 //! Constructor
COpenGLParallaxMapRenderer(video::COpenGLDriver * driver,s32 & outMaterialTypeNr,IMaterialRenderer * baseMaterial)189 COpenGLParallaxMapRenderer::COpenGLParallaxMapRenderer(video::COpenGLDriver* driver,
190 	s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial)
191 	: COpenGLShaderMaterialRenderer(driver, 0, baseMaterial), CompiledShaders(true)
192 {
193 
194 	#ifdef _DEBUG
195 	setDebugName("COpenGLParallaxMapRenderer");
196 	#endif
197 
198 	// set this as callback. We could have done this in
199 	// the initialization list, but some compilers don't like it.
200 
201 	CallBack = this;
202 
203 	// basically, this simply compiles the hard coded shaders if the
204 	// hardware is able to do them, otherwise it maps to the base material
205 
206 	if (!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1) ||
207 		!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
208 	{
209 		// this hardware is not able to do shaders. Fall back to
210 		// base material.
211 		outMaterialTypeNr = driver->addMaterialRenderer(this);
212 		return;
213 	}
214 
215 	// check if already compiled normal map shaders are there.
216 
217 	video::IMaterialRenderer* renderer = driver->getMaterialRenderer(EMT_PARALLAX_MAP_SOLID);
218 
219 	if (renderer)
220 	{
221 		// use the already compiled shaders
222 		video::COpenGLParallaxMapRenderer* nmr = reinterpret_cast<video::COpenGLParallaxMapRenderer*>(renderer);
223 		CompiledShaders = false;
224 
225 		VertexShader = nmr->VertexShader;
226 		PixelShader = nmr->PixelShader;
227 
228 		outMaterialTypeNr = driver->addMaterialRenderer(this);
229 	}
230 	else
231 	{
232 		// compile shaders on our own
233 		init(outMaterialTypeNr, OPENGL_PARALLAX_MAP_VSH, OPENGL_PARALLAX_MAP_PSH, EVT_TANGENTS);
234 	}
235 
236 	// fallback if compilation has failed
237 	if (-1==outMaterialTypeNr)
238 		outMaterialTypeNr = driver->addMaterialRenderer(this);
239 }
240 
241 
242 //! Destructor
~COpenGLParallaxMapRenderer()243 COpenGLParallaxMapRenderer::~COpenGLParallaxMapRenderer()
244 {
245 	if (CallBack == this)
246 		CallBack = 0;
247 
248 	if (!CompiledShaders)
249 	{
250 		// prevent this from deleting shaders we did not create
251 		VertexShader = 0;
252 		PixelShader.clear();
253 	}
254 }
255 
256 
OnSetMaterial(const video::SMaterial & material,const video::SMaterial & lastMaterial,bool resetAllRenderstates,video::IMaterialRendererServices * services)257 void COpenGLParallaxMapRenderer::OnSetMaterial(const video::SMaterial& material,
258 	const video::SMaterial& lastMaterial,
259 	bool resetAllRenderstates, video::IMaterialRendererServices* services)
260 {
261 	COpenGLShaderMaterialRenderer::OnSetMaterial(material, lastMaterial,
262 			resetAllRenderstates, services);
263 
264 	CurrentScale = material.MaterialTypeParam;
265 }
266 
267 
268 
269 //! Returns the render capability of the material.
getRenderCapability() const270 s32 COpenGLParallaxMapRenderer::getRenderCapability() const
271 {
272 	if (Driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1) &&
273 		Driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
274 		return 0;
275 
276 	return 1;
277 }
278 
279 
280 //! Called by the engine when the vertex and/or pixel shader constants for an
281 //! material renderer should be set.
OnSetConstants(IMaterialRendererServices * services,s32 userData)282 void COpenGLParallaxMapRenderer::OnSetConstants(IMaterialRendererServices* services, s32 userData)
283 {
284 	video::IVideoDriver* driver = services->getVideoDriver();
285 
286 	// set transposed world matrix
287 	const core::matrix4& tWorld = driver->getTransform(video::ETS_WORLD).getTransposed();
288 	services->setVertexShaderConstant(tWorld.pointer(), 0, 4);
289 
290 	// set transposed worldViewProj matrix
291 	core::matrix4 worldViewProj(driver->getTransform(video::ETS_PROJECTION));
292 	worldViewProj *= driver->getTransform(video::ETS_VIEW);
293 	worldViewProj *= driver->getTransform(video::ETS_WORLD);
294 	core::matrix4 tr(worldViewProj.getTransposed());
295 	services->setVertexShaderConstant(tr.pointer(), 8, 4);
296 
297 	// here we fetch the fixed function lights from the driver
298 	// and set them as constants
299 
300 	u32 cnt = driver->getDynamicLightCount();
301 
302 	// Load the inverse world matrix.
303 	core::matrix4 invWorldMat;
304 	driver->getTransform(video::ETS_WORLD).getInverse(invWorldMat);
305 
306 	for (u32 i=0; i<2; ++i)
307 	{
308 		video::SLight light;
309 
310 		if (i<cnt)
311 			light = driver->getDynamicLight(i);
312 		else
313 		{
314 			light.DiffuseColor.set(0,0,0); // make light dark
315 			light.Radius = 1.0f;
316 		}
317 
318 		light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation
319 
320 		// Transform the light by the inverse world matrix to get it into object space.
321 		invWorldMat.transformVect(light.Position);
322 
323 		services->setVertexShaderConstant(
324 			reinterpret_cast<const f32*>(&light.Position), 12+(i*2), 1);
325 
326 		services->setVertexShaderConstant(
327 			reinterpret_cast<const f32*>(&light.DiffuseColor), 13+(i*2), 1);
328 	}
329 
330 	// Obtain the view position by transforming 0,0,0 by the inverse view matrix
331 	// and then multiply this by the inverse world matrix.
332 	core::vector3df viewPos(0.0f, 0.0f, 0.0f);
333 	core::matrix4 inverseView;
334 	driver->getTransform(video::ETS_VIEW).getInverse(inverseView);
335 	inverseView.transformVect(viewPos);
336 	invWorldMat.transformVect(viewPos);
337 	services->setVertexShaderConstant(reinterpret_cast<const f32*>(&viewPos.X), 16, 1);
338 
339 	// set scale factor
340 	f32 factor = 0.02f; // default value
341 	if (CurrentScale != 0.0f)
342 		factor = CurrentScale;
343 
344 	f32 c6[] = {factor, factor, factor, factor};
345 	services->setPixelShaderConstant(c6, 0, 1);
346 }
347 
348 
349 } // end namespace video
350 } // end namespace irr
351 
352 
353 #endif
354 
355