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