1 #include "material.hpp"
2 
3 #include <osg/Fog>
4 #include <osg/Depth>
5 #include <osg/TexEnvCombine>
6 #include <osg/Texture2D>
7 #include <osg/TexMat>
8 #include <osg/BlendFunc>
9 
10 #include <components/shader/shadermanager.hpp>
11 
12 #include <mutex>
13 
14 namespace
15 {
16     class BlendmapTexMat
17     {
18     public:
value(const int blendmapScale)19         static const osg::ref_ptr<osg::TexMat>& value(const int blendmapScale)
20         {
21             static BlendmapTexMat instance;
22             return instance.get(blendmapScale);
23         }
24 
get(const int blendmapScale)25         const osg::ref_ptr<osg::TexMat>& get(const int blendmapScale)
26         {
27             const std::lock_guard<std::mutex> lock(mMutex);
28             auto texMat = mTexMatMap.find(blendmapScale);
29             if (texMat == mTexMatMap.end())
30             {
31                 osg::Matrixf matrix;
32                 float scale = (blendmapScale/(static_cast<float>(blendmapScale)+1.f));
33                 matrix.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f));
34                 matrix.preMultScale(osg::Vec3f(scale, scale, 1.f));
35                 matrix.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f));
36                 // We need to nudge the blendmap to look like vanilla.
37                 // This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the blendmap, apparently.
38                 matrix.preMultTranslate(osg::Vec3f(1.0f/blendmapScale/4.0f, 1.0f/blendmapScale/4.0f, 0.f));
39 
40                 texMat = mTexMatMap.insert(std::make_pair(blendmapScale, new osg::TexMat(matrix))).first;
41             }
42             return texMat->second;
43         }
44 
45     private:
46         std::mutex mMutex;
47         std::map<float, osg::ref_ptr<osg::TexMat>> mTexMatMap;
48     };
49 
50     class LayerTexMat
51     {
52     public:
value(const float layerTileSize)53         static const osg::ref_ptr<osg::TexMat>& value(const float layerTileSize)
54         {
55             static LayerTexMat instance;
56             return instance.get(layerTileSize);
57         }
58 
get(const float layerTileSize)59         const osg::ref_ptr<osg::TexMat>& get(const float layerTileSize)
60         {
61             const std::lock_guard<std::mutex> lock(mMutex);
62             auto texMat = mTexMatMap.find(layerTileSize);
63             if (texMat == mTexMatMap.end())
64             {
65                 texMat = mTexMatMap.insert(std::make_pair(layerTileSize,
66                     new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize, layerTileSize, 1.f))))).first;
67             }
68             return texMat->second;
69         }
70 
71     private:
72         std::mutex mMutex;
73         std::map<float, osg::ref_ptr<osg::TexMat>> mTexMatMap;
74     };
75 
76     class EqualDepth
77     {
78     public:
value()79         static const osg::ref_ptr<osg::Depth>& value()
80         {
81             static EqualDepth instance;
82             return instance.mValue;
83         }
84 
85     private:
86         osg::ref_ptr<osg::Depth> mValue;
87 
EqualDepth()88         EqualDepth()
89             : mValue(new osg::Depth)
90         {
91             mValue->setFunction(osg::Depth::EQUAL);
92         }
93     };
94 
95     class LequalDepth
96     {
97     public:
value()98         static const osg::ref_ptr<osg::Depth>& value()
99         {
100             static LequalDepth instance;
101             return instance.mValue;
102         }
103 
104     private:
105         osg::ref_ptr<osg::Depth> mValue;
106 
LequalDepth()107         LequalDepth()
108             : mValue(new osg::Depth)
109         {
110             mValue->setFunction(osg::Depth::LEQUAL);
111         }
112     };
113 
114     class BlendFuncFirst
115     {
116     public:
value()117         static const osg::ref_ptr<osg::BlendFunc>& value()
118         {
119             static BlendFuncFirst instance;
120             return instance.mValue;
121         }
122 
123     private:
124         osg::ref_ptr<osg::BlendFunc> mValue;
125 
BlendFuncFirst()126         BlendFuncFirst()
127             : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ZERO))
128         {
129         }
130     };
131 
132     class BlendFunc
133     {
134     public:
value()135         static const osg::ref_ptr<osg::BlendFunc>& value()
136         {
137             static BlendFunc instance;
138             return instance.mValue;
139         }
140 
141     private:
142         osg::ref_ptr<osg::BlendFunc> mValue;
143 
BlendFunc()144         BlendFunc()
145             : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE))
146         {
147         }
148     };
149 
150     class TexEnvCombine
151     {
152     public:
value()153         static const osg::ref_ptr<osg::TexEnvCombine>& value()
154         {
155             static TexEnvCombine instance;
156             return instance.mValue;
157         }
158 
159     private:
160         osg::ref_ptr<osg::TexEnvCombine> mValue;
161 
TexEnvCombine()162         TexEnvCombine()
163             : mValue(new osg::TexEnvCombine)
164         {
165             mValue->setCombine_RGB(osg::TexEnvCombine::REPLACE);
166             mValue->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);
167         }
168     };
169 }
170 
171 namespace Terrain
172 {
createPasses(bool useShaders,Shader::ShaderManager * shaderManager,const std::vector<TextureLayer> & layers,const std::vector<osg::ref_ptr<osg::Texture2D>> & blendmaps,int blendmapScale,float layerTileSize)173     std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Shader::ShaderManager* shaderManager, const std::vector<TextureLayer> &layers,
174                                                            const std::vector<osg::ref_ptr<osg::Texture2D> > &blendmaps, int blendmapScale, float layerTileSize)
175     {
176         std::vector<osg::ref_ptr<osg::StateSet> > passes;
177 
178         unsigned int blendmapIndex = 0;
179         unsigned int passIndex = 0;
180         for (std::vector<TextureLayer>::const_iterator it = layers.begin(); it != layers.end(); ++it)
181         {
182             bool firstLayer = (it == layers.begin());
183 
184             osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
185 
186             if (!blendmaps.empty())
187             {
188                 stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
189                 stateset->setRenderBinDetails(passIndex++, "RenderBin");
190                 if (!firstLayer)
191                 {
192                     stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON);
193                     stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON);
194                 }
195                 else
196                 {
197                     stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON);
198                     stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON);
199                 }
200             }
201 
202             int texunit = 0;
203 
204             if (useShaders)
205             {
206                 stateset->setTextureAttributeAndModes(texunit, it->mDiffuseMap);
207 
208                 if (layerTileSize != 1.f)
209                     stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);
210 
211                 stateset->addUniform(new osg::Uniform("diffuseMap", texunit));
212 
213                 if (!blendmaps.empty())
214                 {
215                     ++texunit;
216                     osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
217 
218                     stateset->setTextureAttributeAndModes(texunit, blendmap.get());
219                     stateset->setTextureAttributeAndModes(texunit, BlendmapTexMat::value(blendmapScale));
220                     stateset->addUniform(new osg::Uniform("blendMap", texunit));
221                 }
222 
223                 if (it->mNormalMap)
224                 {
225                     ++texunit;
226                     stateset->setTextureAttributeAndModes(texunit, it->mNormalMap);
227                     stateset->addUniform(new osg::Uniform("normalMap", texunit));
228                 }
229 
230                 Shader::ShaderManager::DefineMap defineMap;
231                 defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0";
232                 defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
233                 defineMap["specularMap"] = it->mSpecular ? "1" : "0";
234                 defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
235 
236                 osg::ref_ptr<osg::Shader> vertexShader = shaderManager->getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
237                 osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
238                 if (!vertexShader || !fragmentShader)
239                 {
240                     // Try again without shader. Error already logged by above
241                     return createPasses(false, shaderManager, layers, blendmaps, blendmapScale, layerTileSize);
242                 }
243 
244                 stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader));
245                 stateset->addUniform(new osg::Uniform("colorMode", 2));
246             }
247             else
248             {
249                 // Add the actual layer texture
250                 osg::ref_ptr<osg::Texture2D> tex = it->mDiffuseMap;
251                 stateset->setTextureAttributeAndModes(texunit, tex.get());
252 
253                 if (layerTileSize != 1.f)
254                     stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);
255 
256                 ++texunit;
257 
258                 // Multiply by the alpha map
259                 if (!blendmaps.empty())
260                 {
261                     osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
262 
263                     stateset->setTextureAttributeAndModes(texunit, blendmap.get());
264 
265                     // This is to map corner vertices directly to the center of a blendmap texel.
266                     stateset->setTextureAttributeAndModes(texunit, BlendmapTexMat::value(blendmapScale));
267                     stateset->setTextureAttributeAndModes(texunit, TexEnvCombine::value(), osg::StateAttribute::ON);
268 
269                     ++texunit;
270                 }
271 
272             }
273 
274             passes.push_back(stateset);
275         }
276         return passes;
277     }
278 
279 }
280