1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreTerrainMaterialGeneratorA.h"
29 #include "OgreTerrain.h"
30 #include "OgreHighLevelGpuProgramManager.h"
31 #include "OgreShadowCameraSetupPSSM.h"
32 
33 namespace Ogre
34 {
35 	//---------------------------------------------------------------------
36 	//---------------------------------------------------------------------
37 	HighLevelGpuProgramPtr
createVertexProgram(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt)38 	TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::createVertexProgram(
39 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
40 	{
41 		HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
42 		String progName = getVertexProgramName(prof, terrain, tt);
43 		HighLevelGpuProgramPtr ret = mgr.getByName(progName);
44 		if (ret.isNull())
45 		{
46 			ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
47 				"cg", GPT_VERTEX_PROGRAM);
48 		}
49 		else
50 		{
51 			ret->unload();
52 		}
53 
54 		ret->setParameter("profiles", "vs_4_0 vs_3_0 vs_2_0 arbvp1");
55 		ret->setParameter("entry_point", "main_vp");
56 
57 		return ret;
58 
59 	}
60 	//---------------------------------------------------------------------
61 	HighLevelGpuProgramPtr
createFragmentProgram(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt)62 		TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::createFragmentProgram(
63 			const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
64 	{
65 		HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
66 		String progName = getFragmentProgramName(prof, terrain, tt);
67 
68 		HighLevelGpuProgramPtr ret = mgr.getByName(progName);
69 		if (ret.isNull())
70 		{
71 			ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
72 				"cg", GPT_FRAGMENT_PROGRAM);
73 		}
74 		else
75 		{
76 			ret->unload();
77 		}
78 
79 		if(prof->isLayerNormalMappingEnabled() || prof->isLayerParallaxMappingEnabled())
80 			ret->setParameter("profiles", "ps_4_0 ps_3_0 ps_2_x fp40 arbfp1");
81 		else
82 			ret->setParameter("profiles", "ps_4_0 ps_3_0 ps_2_0 fp30 arbfp1");
83 		ret->setParameter("entry_point", "main_fp");
84 
85 		return ret;
86 
87 	}
88 	//---------------------------------------------------------------------
generateVpHeader(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)89 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateVpHeader(
90 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
91 	{
92 		outStream <<
93 			"void main_vp(\n";
94 		bool compression = terrain->_getUseVertexCompression() && tt != RENDER_COMPOSITE_MAP;
95 		if (compression)
96 		{
97 			outStream <<
98 				"float2 posIndex : POSITION,\n"
99 				"float height  : TEXCOORD0,\n";
100 		}
101 		else
102 		{
103 			outStream <<
104 				"float4 pos : POSITION,\n"
105 				"float2 uv  : TEXCOORD0,\n";
106 
107 		}
108 		if (tt != RENDER_COMPOSITE_MAP)
109 			outStream << "float2 delta  : TEXCOORD1,\n"; // lodDelta, lodThreshold
110 
111 		outStream <<
112 			"uniform float4x4 worldMatrix,\n"
113 			"uniform float4x4 viewProjMatrix,\n"
114 			"uniform float2   lodMorph,\n"; // morph amount, morph LOD target
115 
116 		if (compression)
117 		{
118 			outStream <<
119 				"uniform float4x4   posIndexToObjectSpace,\n"
120 				"uniform float    baseUVScale,\n";
121 		}
122 		// uv multipliers
123 		uint maxLayers = prof->getMaxLayers(terrain);
124 		uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
125 		uint numUVMultipliers = (numLayers / 4);
126 		if (numLayers % 4)
127 			++numUVMultipliers;
128 		for (uint i = 0; i < numUVMultipliers; ++i)
129 			outStream << "uniform float4 uvMul_" << i << ", \n";
130 
131 		outStream <<
132 			"out float4 oPos : POSITION,\n"
133 			"out float4 oPosObj : TEXCOORD0 \n";
134 
135 		uint texCoordSet = 1;
136 		outStream <<
137 			", out float4 oUVMisc : TEXCOORD" << texCoordSet++ <<" // xy = uv, z = camDepth\n";
138 
139 		// layer UV's premultiplied, packed as xy/zw
140 		uint numUVSets = numLayers / 2;
141 		if (numLayers % 2)
142 			++numUVSets;
143 		if (tt != LOW_LOD)
144 		{
145 			for (uint i = 0; i < numUVSets; ++i)
146 			{
147 				outStream <<
148 					", out float4 oUV" << i << " : TEXCOORD" << texCoordSet++ << "\n";
149 			}
150 		}
151 
152 		if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP)
153 		{
154 			outStream << ", out float2 lodInfo : TEXCOORD" << texCoordSet++ << "\n";
155 		}
156 
157 		bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
158 		if (fog)
159 		{
160 			outStream <<
161 				", uniform float4 fogParams\n"
162 				", out float fogVal : COLOR\n";
163 		}
164 
165 		if (prof->isShadowingEnabled(tt, terrain))
166 		{
167 			texCoordSet = generateVpDynamicShadowsParams(texCoordSet, prof, terrain, tt, outStream);
168 		}
169 
170 		// check we haven't exceeded texture coordinates
171 		if (texCoordSet > 8)
172 		{
173 			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
174 				"Requested options require too many texture coordinate sets! Try reducing the number of layers.",
175 				__FUNCTION__);
176 		}
177 
178 		outStream <<
179 			")\n"
180 			"{\n";
181 		if (compression)
182 		{
183 			outStream <<
184 				"	float4 pos;\n"
185 				"	pos = mul(posIndexToObjectSpace, float4(posIndex, height, 1));\n"
186 				"   float2 uv = float2(posIndex.x * baseUVScale, 1.0 - (posIndex.y * baseUVScale));\n";
187 		}
188 		outStream <<
189 			"	float4 worldPos = mul(worldMatrix, pos);\n"
190 			"	oPosObj = pos;\n";
191 
192 		if (tt != RENDER_COMPOSITE_MAP)
193 		{
194 			// determine whether to apply the LOD morph to this vertex
195 			// we store the deltas against all vertices so we only want to apply
196 			// the morph to the ones which would disappear. The target LOD which is
197 			// being morphed to is stored in lodMorph.y, and the LOD at which
198 			// the vertex should be morphed is stored in uv.w. If we subtract
199 			// the former from the latter, and arrange to only morph if the
200 			// result is negative (it will only be -1 in fact, since after that
201 			// the vertex will never be indexed), we will achieve our aim.
202 			// sign(vertexLOD - targetLOD) == -1 is to morph
203 			outStream <<
204 				"	float toMorph = -min(0, sign(delta.y - lodMorph.y));\n";
205 			// this will either be 1 (morph) or 0 (don't morph)
206 			if (prof->getParent()->getDebugLevel())
207 			{
208 				// x == LOD level (-1 since value is target level, we want to display actual)
209 				outStream << "lodInfo.x = (lodMorph.y - 1) / " << terrain->getNumLodLevels() << ";\n";
210 				// y == LOD morph
211 				outStream << "lodInfo.y = toMorph * lodMorph.x;\n";
212 			}
213 
214 			// morph
215 			switch (terrain->getAlignment())
216 			{
217 			case Terrain::ALIGN_X_Y:
218 				outStream << "	worldPos.z += delta.x * toMorph * lodMorph.x;\n";
219 				break;
220 			case Terrain::ALIGN_X_Z:
221 				outStream << "	worldPos.y += delta.x * toMorph * lodMorph.x;\n";
222 				break;
223 			case Terrain::ALIGN_Y_Z:
224 				outStream << "	worldPos.x += delta.x * toMorph * lodMorph.x;\n";
225 				break;
226 			};
227 		}
228 
229 
230 		// generate UVs
231 		if (tt != LOW_LOD)
232 		{
233 			for (uint i = 0; i < numUVSets; ++i)
234 			{
235 				uint layer  =  i * 2;
236 				uint uvMulIdx = layer / 4;
237 
238 				outStream <<
239 					"	oUV" << i << ".xy = " << " uv.xy * uvMul_" << uvMulIdx << "." << getChannel(layer) << ";\n";
240 				outStream <<
241 					"	oUV" << i << ".zw = " << " uv.xy * uvMul_" << uvMulIdx << "." << getChannel(layer+1) << ";\n";
242 
243 			}
244 
245 		}
246 
247 
248 	}
249 	//---------------------------------------------------------------------
generateFpHeader(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)250 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateFpHeader(
251 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
252 	{
253 
254 		// Main header
255 		outStream <<
256 			// helpers
257 			"float4 expand(float4 v)\n"
258 			"{ \n"
259 			"	return v * 2 - 1;\n"
260 			"}\n\n\n";
261 
262 		if (prof->isShadowingEnabled(tt, terrain))
263 			generateFpDynamicShadowsHelpers(prof, terrain, tt, outStream);
264 
265 
266 		outStream <<
267 			"float4 main_fp(\n"
268 			"float4 vertexPos : POSITION,\n"
269 			"float4 position : TEXCOORD0,\n";
270 
271 		uint texCoordSet = 1;
272 		outStream <<
273 			"float4 uvMisc : TEXCOORD" << texCoordSet++ << ",\n";
274 
275 		// UV's premultiplied, packed as xy/zw
276 		uint maxLayers = prof->getMaxLayers(terrain);
277 		uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount());
278 		uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
279 		uint numUVSets = numLayers / 2;
280 		if (numLayers % 2)
281 			++numUVSets;
282 		if (tt != LOW_LOD)
283 		{
284 			for (uint i = 0; i < numUVSets; ++i)
285 			{
286 				outStream <<
287 					"float4 layerUV" << i << " : TEXCOORD" << texCoordSet++ << ", \n";
288 			}
289 
290 		}
291 		if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP)
292 		{
293 			outStream << "float2 lodInfo : TEXCOORD" << texCoordSet++ << ", \n";
294 		}
295 
296 		bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
297 		if (fog)
298 		{
299 			outStream <<
300 				"uniform float3 fogColour, \n"
301 				"float fogVal : COLOR,\n";
302 		}
303 
304 		uint currentSamplerIdx = 0;
305 
306 		outStream <<
307 			// Only 1 light supported in this version
308 			// deferred shading profile / generator later, ok? :)
309 			"uniform float3 ambient,\n"
310 			"uniform float4 lightPosObjSpace,\n"
311 			"uniform float3 lightDiffuseColour,\n"
312 			"uniform float3 lightSpecularColour,\n"
313 			"uniform float3 eyePosObjSpace,\n"
314 			// pack scale, bias and specular
315 			"uniform float4 scaleBiasSpecular,\n";
316 
317 		if (tt == LOW_LOD)
318 		{
319 			// single composite map covers all the others below
320 			outStream <<
321 				"uniform sampler2D compositeMap : register(s" << currentSamplerIdx++ << ")\n";
322 		}
323 		else
324 		{
325 			outStream <<
326 				"uniform sampler2D globalNormal : register(s" << currentSamplerIdx++ << ")\n";
327 
328 
329 			if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled())
330 			{
331 				outStream << ", uniform sampler2D globalColourMap : register(s"
332 					<< currentSamplerIdx++ << ")\n";
333 			}
334 			if (prof->isLightmapEnabled())
335 			{
336 				outStream << ", uniform sampler2D lightMap : register(s"
337 					<< currentSamplerIdx++ << ")\n";
338 			}
339 			// Blend textures - sampler definitions
340 			for (uint i = 0; i < numBlendTextures; ++i)
341 			{
342 				outStream << ", uniform sampler2D blendTex" << i
343 					<< " : register(s" << currentSamplerIdx++ << ")\n";
344 			}
345 
346 			// Layer textures - sampler definitions & UV multipliers
347 			for (uint i = 0; i < numLayers; ++i)
348 			{
349 				outStream << ", uniform sampler2D difftex" << i
350 					<< " : register(s" << currentSamplerIdx++ << ")\n";
351 				outStream << ", uniform sampler2D normtex" << i
352 					<< " : register(s" << currentSamplerIdx++ << ")\n";
353 			}
354 		}
355 
356 		if (prof->isShadowingEnabled(tt, terrain))
357 		{
358 			generateFpDynamicShadowsParams(&texCoordSet, &currentSamplerIdx, prof, terrain, tt, outStream);
359 		}
360 
361 		// check we haven't exceeded samplers
362 		if (currentSamplerIdx > 16)
363 		{
364 			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
365 				"Requested options require too many texture samplers! Try reducing the number of layers.",
366 				__FUNCTION__);
367 		}
368 
369 		outStream <<
370 			") : COLOR\n"
371 			"{\n"
372 			"	float4 outputCol;\n"
373 			"	float shadow = 1.0;\n"
374 			"	float2 uv = uvMisc.xy;\n"
375 			// base colour
376 			"	outputCol = float4(0,0,0,1);\n";
377 
378 		if (tt != LOW_LOD)
379 		{
380 			outStream <<
381 				// global normal
382 				"	float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n";
383 
384 		}
385 
386 		outStream <<
387 			"	float3 lightDir = \n"
388 			"		lightPosObjSpace.xyz -  (position.xyz * lightPosObjSpace.w);\n"
389 			"	float3 eyeDir = eyePosObjSpace - position.xyz;\n"
390 
391 			// set up accumulation areas
392 			"	float3 diffuse = float3(0,0,0);\n"
393 			"	float specular = 0;\n";
394 
395 
396 		if (tt == LOW_LOD)
397 		{
398 			// we just do a single calculation from composite map
399 			outStream <<
400 				"	float4 composite = tex2D(compositeMap, uv);\n"
401 				"	diffuse = composite.rgb;\n";
402 			// TODO - specular; we'll need normals for this!
403 		}
404 		else
405 		{
406 			// set up the blend values
407 			for (uint i = 0; i < numBlendTextures; ++i)
408 			{
409 				outStream << "	float4 blendTexVal" << i << " = tex2D(blendTex" << i << ", uv);\n";
410 			}
411 
412 			if (prof->isLayerNormalMappingEnabled())
413 			{
414 				// derive the tangent space basis
415 				// we do this in the pixel shader because we don't have per-vertex normals
416 				// because of the LOD, we use a normal map
417 				// tangent is always +x or -z in object space depending on alignment
418 				switch(terrain->getAlignment())
419 				{
420 				case Terrain::ALIGN_X_Y:
421 				case Terrain::ALIGN_X_Z:
422 					outStream << "	float3 tangent = float3(1, 0, 0);\n";
423 					break;
424 				case Terrain::ALIGN_Y_Z:
425 					outStream << "	float3 tangent = float3(0, 0, -1);\n";
426 					break;
427 				};
428 
429 				outStream << "	float3 binormal = normalize(cross(tangent, normal));\n";
430 				// note, now we need to re-cross to derive tangent again because it wasn't orthonormal
431 				outStream << "	tangent = normalize(cross(normal, binormal));\n";
432 				// derive final matrix
433 				outStream << "	float3x3 TBN = float3x3(tangent, binormal, normal);\n";
434 
435 				// set up lighting result placeholders for interpolation
436 				outStream <<  "	float4 litRes, litResLayer;\n";
437 				outStream << "	float3 TSlightDir, TSeyeDir, TShalfAngle, TSnormal;\n";
438 				if (prof->isLayerParallaxMappingEnabled())
439 					outStream << "	float displacement;\n";
440 				// move
441 				outStream << "	TSlightDir = normalize(mul(TBN, lightDir));\n";
442 				outStream << "	TSeyeDir = normalize(mul(TBN, eyeDir));\n";
443 
444 			}
445 			else
446 			{
447 				// simple per-pixel lighting with no normal mapping
448 				outStream << "	lightDir = normalize(lightDir);\n";
449 				outStream << "	eyeDir = normalize(eyeDir);\n";
450 				outStream << "	float3 halfAngle = normalize(lightDir + eyeDir);\n";
451 				outStream << "	float4 litRes = lit(dot(lightDir, normal), dot(halfAngle, normal), scaleBiasSpecular.z);\n";
452 
453 			}
454 		}
455 
456 
457 	}
458 	//---------------------------------------------------------------------
generateVpLayer(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,uint layer,StringUtil::StrStreamType & outStream)459 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateVpLayer(
460 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream)
461 	{
462 		// nothing to do
463 	}
464 	//---------------------------------------------------------------------
generateFpLayer(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,uint layer,StringUtil::StrStreamType & outStream)465 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateFpLayer(
466 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream)
467 	{
468 		uint uvIdx = layer / 2;
469 		String uvChannels = (layer % 2) ? ".zw" : ".xy";
470 		uint blendIdx = (layer-1) / 4;
471 		String blendChannel = getChannel(layer-1);
472 		String blendWeightStr = String("blendTexVal") + StringConverter::toString(blendIdx) +
473 			"." + blendChannel;
474 
475 		// generate early-out conditional
476 		/* Disable - causing some issues even when trying to force the use of texldd
477 		if (layer && prof->_isSM3Available())
478 			outStream << "  if (" << blendWeightStr << " > 0.0003)\n  { \n";
479 		*/
480 
481 		// generate UV
482 		outStream << "	float2 uv" << layer << " = layerUV" << uvIdx << uvChannels << ";\n";
483 
484 		// calculate lighting here if normal mapping
485 		if (prof->isLayerNormalMappingEnabled())
486 		{
487 			if (prof->isLayerParallaxMappingEnabled() && tt != RENDER_COMPOSITE_MAP)
488 			{
489 				// modify UV - note we have to sample an extra time
490 				outStream << "	displacement = tex2D(normtex" << layer << ", uv" << layer << ").a\n"
491 					"		* scaleBiasSpecular.x + scaleBiasSpecular.y;\n";
492 				outStream << "	uv" << layer << " += TSeyeDir.xy * displacement;\n";
493 			}
494 
495 			// access TS normal map
496 			outStream << "	TSnormal = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n";
497 			outStream << "	TShalfAngle = normalize(TSlightDir + TSeyeDir);\n";
498 			outStream << "	litResLayer = lit(dot(TSlightDir, TSnormal), dot(TShalfAngle, TSnormal), scaleBiasSpecular.z);\n";
499 			if (!layer)
500 				outStream << "	litRes = litResLayer;\n";
501 			else
502 				outStream << "	litRes = lerp(litRes, litResLayer, " << blendWeightStr << ");\n";
503 
504 		}
505 
506 		// sample diffuse texture
507 		outStream << "	float4 diffuseSpecTex" << layer
508 			<< " = tex2D(difftex" << layer << ", uv" << layer << ");\n";
509 
510 		// apply to common
511 		if (!layer)
512 		{
513 			outStream << "	diffuse = diffuseSpecTex0.rgb;\n";
514 			if (prof->isLayerSpecularMappingEnabled())
515 				outStream << "	specular = diffuseSpecTex0.a;\n";
516 		}
517 		else
518 		{
519 			outStream << "	diffuse = lerp(diffuse, diffuseSpecTex" << layer
520 				<< ".rgb, " << blendWeightStr << ");\n";
521 			if (prof->isLayerSpecularMappingEnabled())
522 				outStream << "	specular = lerp(specular, diffuseSpecTex" << layer
523 					<< ".a, " << blendWeightStr << ");\n";
524 
525 		}
526 
527 		// End early-out
528 		/* Disable - causing some issues even when trying to force the use of texldd
529 		if (layer && prof->_isSM3Available())
530 			outStream << "  } // early-out blend value\n";
531 		*/
532 	}
533 	//---------------------------------------------------------------------
generateVpFooter(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)534 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateVpFooter(
535 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
536 	{
537 
538 		outStream <<
539 			"	oPos = mul(viewProjMatrix, worldPos);\n"
540 			"	oUVMisc.xy = uv.xy;\n";
541 
542 		bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
543 		if (fog)
544 		{
545 			if (terrain->getSceneManager()->getFogMode() == FOG_LINEAR)
546 			{
547 				outStream <<
548 					"	fogVal = saturate((oPos.z - fogParams.y) * fogParams.w);\n";
549 			}
550 			else
551 			{
552 				outStream <<
553 					"	fogVal = 1 - saturate(1 / (exp(oPos.z * fogParams.x)));\n";
554 			}
555 		}
556 
557 		if (prof->isShadowingEnabled(tt, terrain))
558 			generateVpDynamicShadows(prof, terrain, tt, outStream);
559 
560 		outStream <<
561 			"}\n";
562 
563 
564 	}
565 	//---------------------------------------------------------------------
generateFpFooter(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)566 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateFpFooter(
567 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
568 	{
569 
570 		if (tt == LOW_LOD)
571 		{
572 			if (prof->isShadowingEnabled(tt, terrain))
573 			{
574 				generateFpDynamicShadows(prof, terrain, tt, outStream);
575 				outStream <<
576 					"	outputCol.rgb = diffuse * rtshadow;\n";
577 			}
578 			else
579 			{
580 				outStream <<
581 					"	outputCol.rgb = diffuse;\n";
582 			}
583 		}
584 		else
585 		{
586 			if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled())
587 			{
588 				// sample colour map and apply to diffuse
589 				outStream << "	diffuse *= tex2D(globalColourMap, uv).rgb;\n";
590 			}
591 			if (prof->isLightmapEnabled())
592 			{
593 				// sample lightmap
594 				outStream << "	shadow = tex2D(lightMap, uv).r;\n";
595 			}
596 
597 			if (prof->isShadowingEnabled(tt, terrain))
598 			{
599 				generateFpDynamicShadows(prof, terrain, tt, outStream);
600 			}
601 
602 			// diffuse lighting
603 			outStream << "	outputCol.rgb += ambient.rgb * diffuse + litRes.y * lightDiffuseColour * diffuse * shadow;\n";
604 
605 			// specular default
606 			if (!prof->isLayerSpecularMappingEnabled())
607 				outStream << "	specular = 1.0;\n";
608 
609 			if (tt == RENDER_COMPOSITE_MAP)
610 			{
611 				// Lighting embedded in alpha
612 				outStream <<
613 					"	outputCol.a = shadow;\n";
614 
615 			}
616 			else
617 			{
618 				// Apply specular
619 				outStream << "	outputCol.rgb += litRes.z * lightSpecularColour * specular * shadow;\n";
620 
621 				if (prof->getParent()->getDebugLevel())
622 				{
623 					outStream << "	outputCol.rg += lodInfo.xy;\n";
624 				}
625 			}
626 		}
627 
628 		bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
629 		if (fog)
630 		{
631 			outStream << "	outputCol.rgb = lerp(outputCol.rgb, fogColour, fogVal);\n";
632 		}
633 
634 		// Final return
635 		outStream << "	return outputCol;\n"
636 			<< "}\n";
637 
638 	}
639 	//---------------------------------------------------------------------
generateFpDynamicShadowsHelpers(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)640 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsHelpers(
641 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
642 	{
643 		// TODO make filtering configurable
644 		outStream <<
645 			"// Simple PCF \n"
646 			"// Number of samples in one dimension (square for total samples) \n"
647 			"#define NUM_SHADOW_SAMPLES_1D 2.0 \n"
648 			"#define SHADOW_FILTER_SCALE 1 \n"
649 
650 			"#define SHADOW_SAMPLES NUM_SHADOW_SAMPLES_1D*NUM_SHADOW_SAMPLES_1D \n"
651 
652 			"float4 offsetSample(float4 uv, float2 offset, float invMapSize) \n"
653 			"{ \n"
654 			"	return float4(uv.xy + offset * invMapSize * uv.w, uv.z, uv.w); \n"
655 			"} \n";
656 
657 		if (prof->getReceiveDynamicShadowsDepth())
658 		{
659 			outStream <<
660 				"float calcDepthShadow(sampler2D shadowMap, float4 uv, float invShadowMapSize) \n"
661 				"{ \n"
662 				"	// 4-sample PCF \n"
663 
664 				"	float shadow = 0.0; \n"
665 				"	float offset = (NUM_SHADOW_SAMPLES_1D/2 - 0.5) * SHADOW_FILTER_SCALE; \n"
666 				"	for (float y = -offset; y <= offset; y += SHADOW_FILTER_SCALE) \n"
667 				"		for (float x = -offset; x <= offset; x += SHADOW_FILTER_SCALE) \n"
668 				"		{ \n"
669 				"			float4 newUV = offsetSample(uv, float2(x, y), invShadowMapSize);\n"
670 				"			// manually project and assign derivatives \n"
671 				"			// to avoid gradient issues inside loops \n"
672 				"			newUV = newUV / newUV.w; \n"
673 				"			float depth = tex2D(shadowMap, newUV.xy, 1, 1).x; \n"
674 				"			if (depth >= 1 || depth >= uv.z)\n"
675 				"				shadow += 1.0;\n"
676 				"		} \n"
677 
678 				"	shadow /= SHADOW_SAMPLES; \n"
679 
680 				"	return shadow; \n"
681 				"} \n";
682 		}
683 		else
684 		{
685 			outStream <<
686 				"float calcSimpleShadow(sampler2D shadowMap, float4 shadowMapPos) \n"
687 				"{ \n"
688 				"	return tex2Dproj(shadowMap, shadowMapPos).x; \n"
689 				"} \n";
690 
691 		}
692 
693 		if (prof->getReceiveDynamicShadowsPSSM())
694 		{
695 			uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
696 
697 
698 			if (prof->getReceiveDynamicShadowsDepth())
699 			{
700 				outStream <<
701 					"float calcPSSMDepthShadow(";
702 			}
703 			else
704 			{
705 				outStream <<
706 					"float calcPSSMSimpleShadow(";
707 			}
708 
709 			outStream << "\n	";
710 			for (uint i = 0; i < numTextures; ++i)
711 				outStream << "sampler2D shadowMap" << i << ", ";
712 			outStream << "\n	";
713 			for (uint i = 0; i < numTextures; ++i)
714 				outStream << "float4 lsPos" << i << ", ";
715 			if (prof->getReceiveDynamicShadowsDepth())
716 			{
717 				outStream << "\n	";
718 				for (uint i = 0; i < numTextures; ++i)
719 					outStream << "float invShadowmapSize" << i << ", ";
720 			}
721 			outStream << "\n"
722 				"	float4 pssmSplitPoints, float camDepth) \n"
723 				"{ \n"
724 				"	float shadow; \n"
725 				"	// calculate shadow \n";
726 
727 			for (uint i = 0; i < numTextures; ++i)
728 			{
729 				if (!i)
730 					outStream << "	if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n";
731 				else if (i < numTextures - 1)
732 					outStream << "	else if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n";
733 				else
734 					outStream << "	else \n";
735 
736 				outStream <<
737 					"	{ \n";
738 				if (prof->getReceiveDynamicShadowsDepth())
739 				{
740 					outStream <<
741 						"		shadow = calcDepthShadow(shadowMap" << i << ", lsPos" << i << ", invShadowmapSize" << i << "); \n";
742 				}
743 				else
744 				{
745 					outStream <<
746 						"		shadow = calcSimpleShadow(shadowMap" << i << ", lsPos" << i << "); \n";
747 				}
748 				outStream <<
749 					"	} \n";
750 
751 			}
752 
753 			outStream <<
754 				"	return shadow; \n"
755 				"} \n\n\n";
756 		}
757 
758 
759 	}
760 	//---------------------------------------------------------------------
generateVpDynamicShadowsParams(uint texCoord,const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)761 	uint TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateVpDynamicShadowsParams(
762 		uint texCoord, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
763 	{
764 		// out semantics & params
765 		uint numTextures = 1;
766 		if (prof->getReceiveDynamicShadowsPSSM())
767 		{
768 			numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
769 		}
770 		for (uint i = 0; i < numTextures; ++i)
771 		{
772 			outStream <<
773 				", out float4 oLightSpacePos" << i << " : TEXCOORD" << texCoord++ << " \n" <<
774 				", uniform float4x4 texViewProjMatrix" << i << " \n";
775 			if (prof->getReceiveDynamicShadowsDepth())
776 			{
777 				outStream <<
778 					", uniform float4 depthRange" << i << " // x = min, y = max, z = range, w = 1/range \n";
779 			}
780 		}
781 
782 		return texCoord;
783 
784 	}
785 	//---------------------------------------------------------------------
generateVpDynamicShadows(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)786 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateVpDynamicShadows(
787 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
788 	{
789 		uint numTextures = 1;
790 		if (prof->getReceiveDynamicShadowsPSSM())
791 		{
792 			numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
793 		}
794 
795 		// Calculate the position of vertex in light space
796 		for (uint i = 0; i < numTextures; ++i)
797 		{
798 			outStream <<
799 				"	oLightSpacePos" << i << " = mul(texViewProjMatrix" << i << ", worldPos); \n";
800 			if (prof->getReceiveDynamicShadowsDepth())
801 			{
802 				// make linear
803 				outStream <<
804 					"oLightSpacePos" << i << ".z = (oLightSpacePos" << i << ".z - depthRange" << i << ".x) * depthRange" << i << ".w;\n";
805 
806 			}
807 		}
808 
809 
810 		if (prof->getReceiveDynamicShadowsPSSM())
811 		{
812 			outStream <<
813 				"	// pass cam depth\n"
814 				"	oUVMisc.z = oPos.z;\n";
815 		}
816 
817 	}
818 	//---------------------------------------------------------------------
generateFpDynamicShadowsParams(uint * texCoord,uint * sampler,const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)819 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsParams(
820 		uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
821 	{
822 		if (tt == HIGH_LOD)
823 			mShadowSamplerStartHi = *sampler;
824 		else if (tt == LOW_LOD)
825 			mShadowSamplerStartLo = *sampler;
826 
827 		// in semantics & params
828 		uint numTextures = 1;
829 		if (prof->getReceiveDynamicShadowsPSSM())
830 		{
831 			numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
832 			outStream <<
833 				", uniform float4 pssmSplitPoints \n";
834 		}
835 		for (uint i = 0; i < numTextures; ++i)
836 		{
837 			outStream <<
838 				", float4 lightSpacePos" << i << " : TEXCOORD" << *texCoord << " \n" <<
839 				", uniform sampler2D shadowMap" << i << " : register(s" << *sampler << ") \n";
840 			*sampler = *sampler + 1;
841 			*texCoord = *texCoord + 1;
842 			if (prof->getReceiveDynamicShadowsDepth())
843 			{
844 				outStream <<
845 					", uniform float inverseShadowmapSize" << i << " \n";
846 			}
847 		}
848 
849 	}
850 	//---------------------------------------------------------------------
generateFpDynamicShadows(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringUtil::StrStreamType & outStream)851 	void TerrainMaterialGeneratorA::SM2Profile::ShaderHelperCg::generateFpDynamicShadows(
852 		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
853 	{
854 		if (prof->getReceiveDynamicShadowsPSSM())
855 		{
856 			uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
857 			outStream <<
858 				"	float camDepth = uvMisc.z;\n";
859 
860 			if (prof->getReceiveDynamicShadowsDepth())
861 			{
862 				outStream <<
863 					"	float rtshadow = calcPSSMDepthShadow(";
864 			}
865 			else
866 			{
867 				outStream <<
868 					"	float rtshadow = calcPSSMSimpleShadow(";
869 			}
870 			for (uint i = 0; i < numTextures; ++i)
871 				outStream << "shadowMap" << i << ", ";
872 			outStream << "\n		";
873 
874 			for (uint i = 0; i < numTextures; ++i)
875 				outStream << "lightSpacePos" << i << ", ";
876 			if (prof->getReceiveDynamicShadowsDepth())
877 			{
878 				outStream << "\n		";
879 				for (uint i = 0; i < numTextures; ++i)
880 					outStream << "inverseShadowmapSize" << i << ", ";
881 			}
882 			outStream << "\n" <<
883 				"		pssmSplitPoints, camDepth);\n";
884 
885 		}
886 		else
887 		{
888 			if (prof->getReceiveDynamicShadowsDepth())
889 			{
890 				outStream <<
891 					"	float rtshadow = calcDepthShadow(shadowMap0, lightSpacePos0, inverseShadowmapSize0);";
892 			}
893 			else
894 			{
895 				outStream <<
896 					"	float rtshadow = calcSimpleShadow(shadowMap0, lightSpacePos0);";
897 			}
898 		}
899 
900 		outStream <<
901 			"	shadow = min(shadow, rtshadow);\n";
902 
903 	}
904 }
905