/src/ogre/Components/RTShaderSystem/src/OgreShaderExNormalMapLighting.cpp
Line | Count | Source |
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-2014 Torus Knot Software Ltd |
8 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | | of this software and associated documentation files (the "Software"), to deal |
10 | | in the Software without restriction, including without limitation the rights |
11 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
12 | | copies of the Software, and to permit persons to whom the Software is |
13 | | furnished to do so, subject to the following conditions: |
14 | | |
15 | | The above copyright notice and this permission notice shall be included in |
16 | | all copies or substantial portions of the Software. |
17 | | |
18 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
23 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
24 | | THE SOFTWARE. |
25 | | ----------------------------------------------------------------------------- |
26 | | */ |
27 | | #include "OgreShaderPrecompiledHeaders.h" |
28 | | #ifdef RTSHADER_SYSTEM_BUILD_EXT_SHADERS |
29 | | |
30 | 0 | #define SGX_LIB_NORMALMAP "SGXLib_NormalMap" |
31 | 0 | #define SGX_FUNC_FETCHNORMAL "SGX_FetchNormal" |
32 | | |
33 | | namespace Ogre |
34 | | { |
35 | | namespace RTShader |
36 | | { |
37 | | |
38 | | /************************************************************************/ |
39 | | /* */ |
40 | | /************************************************************************/ |
41 | | const String SRS_NORMALMAP = "NormalMap"; |
42 | | |
43 | | //----------------------------------------------------------------------- |
44 | | NormalMapLighting::NormalMapLighting() |
45 | 0 | { |
46 | 0 | mNormalMapSamplerIndex = -1; |
47 | 0 | mVSTexCoordSetIndex = 0; |
48 | 0 | mNormalMapSpace = NMS_TANGENT; |
49 | 0 | mParallaxHeightScale = 0.04f; |
50 | 0 | } |
51 | | |
52 | | //----------------------------------------------------------------------- |
53 | 0 | const String& NormalMapLighting::getType() const { return SRS_NORMALMAP; } |
54 | | //----------------------------------------------------------------------- |
55 | | bool NormalMapLighting::createCpuSubPrograms(ProgramSet* programSet) |
56 | 0 | { |
57 | 0 | Program* vsProgram = programSet->getCpuProgram(GPT_VERTEX_PROGRAM); |
58 | 0 | Function* vsMain = vsProgram->getEntryPointFunction(); |
59 | 0 | Program* psProgram = programSet->getCpuProgram(GPT_FRAGMENT_PROGRAM); |
60 | 0 | Function* psMain = psProgram->getEntryPointFunction(); |
61 | |
|
62 | 0 | vsProgram->addDependency(SGX_LIB_NORMALMAP); |
63 | |
|
64 | 0 | psProgram->addDependency(FFP_LIB_TEXTURING); |
65 | 0 | psProgram->addDependency(SGX_LIB_NORMALMAP); |
66 | |
|
67 | 0 | if (mNormalMapSpace == NMS_PARALLAX_OCCLUSION) |
68 | 0 | psProgram->addPreprocessorDefines("POM_LAYER_COUNT=32"); |
69 | | |
70 | | // Resolve texture coordinates. |
71 | 0 | auto vsInTexcoord = |
72 | 0 | vsMain->resolveInputParameter(Parameter::SPC_TEXTURE_COORDINATE0 + mVSTexCoordSetIndex, GCT_FLOAT2); |
73 | 0 | auto vsOutTexcoord = |
74 | 0 | vsMain->resolveOutputParameter(Parameter::SPC_TEXTURE_COORDINATE0 + mVSTexCoordSetIndex, GCT_FLOAT2); |
75 | 0 | auto psInTexcoord = psMain->resolveInputParameter(vsOutTexcoord); |
76 | | |
77 | | // Resolve normal. |
78 | 0 | auto vsInNormal = vsMain->resolveInputParameter(Parameter::SPC_NORMAL_OBJECT_SPACE); |
79 | 0 | auto vsOutNormal = vsMain->resolveOutputParameter(Parameter::SPC_NORMAL_VIEW_SPACE); |
80 | 0 | auto viewNormal = psMain->resolveInputParameter(vsOutNormal); |
81 | 0 | auto newViewNormal = psMain->resolveLocalParameter(Parameter::SPC_NORMAL_VIEW_SPACE); |
82 | | |
83 | | // Resolve vertex tangent |
84 | 0 | auto vsInTangent = vsMain->resolveInputParameter(Parameter::SPC_TANGENT_OBJECT_SPACE); |
85 | 0 | auto vsOutTangent = vsMain->resolveOutputParameter(Parameter::SPC_TANGENT_OBJECT_SPACE); |
86 | 0 | auto psInTangent = psMain->resolveInputParameter(vsOutTangent); |
87 | | |
88 | | // insert before lighting stage |
89 | 0 | auto vstage = vsMain->getStage(FFP_PS_COLOUR_BEGIN + 1); |
90 | 0 | auto fstage = psMain->getStage(FFP_PS_COLOUR_BEGIN + 1); |
91 | | |
92 | | // Output texture coordinates. |
93 | 0 | vstage.assign(vsInTexcoord, vsOutTexcoord); |
94 | |
|
95 | 0 | auto normalMapSampler = psProgram->resolveParameter(GCT_SAMPLER2D, "gNormalMapSampler", mNormalMapSamplerIndex); |
96 | |
|
97 | 0 | auto psOutTBN = psMain->resolveLocalParameter(GpuConstantType::GCT_MATRIX_3X3, "TBN"); |
98 | 0 | fstage.callFunction("SGX_CalculateTBN", {In(viewNormal), In(psInTangent), Out(psOutTBN)}); |
99 | |
|
100 | 0 | if (mNormalMapSpace == NMS_PARALLAX || mNormalMapSpace == NMS_PARALLAX_OCCLUSION) |
101 | 0 | { |
102 | | // assuming: lighting stage computed this |
103 | 0 | auto vsOutViewPos = vsMain->resolveOutputParameter(Parameter::SPC_POSITION_VIEW_SPACE); |
104 | 0 | auto viewPos = psMain->resolveInputParameter(vsOutViewPos); |
105 | |
|
106 | 0 | fstage.callFunction("SGX_Generate_Parallax_Texcoord", |
107 | 0 | {In(normalMapSampler), In(psInTexcoord), In(viewPos), In(mParallaxHeightScale), |
108 | 0 | In(psOutTBN), Out(psInTexcoord)}); |
109 | | |
110 | | // overwrite texcoord0 unconditionally, only one texcoord set is supported with parallax mapping |
111 | | // we are before FFP_PS_TEXTURING, so the new value will be used |
112 | 0 | auto texcoord0 = psMain->resolveInputParameter(Parameter::SPC_TEXTURE_COORDINATE0, GCT_FLOAT2); |
113 | 0 | fstage.assign(psInTexcoord, texcoord0); |
114 | 0 | } |
115 | | |
116 | | // Add the normal fetch function invocation |
117 | 0 | fstage.callFunction(SGX_FUNC_FETCHNORMAL, normalMapSampler, psInTexcoord, newViewNormal); |
118 | |
|
119 | 0 | if (mNormalMapSpace & NMS_TANGENT) |
120 | 0 | { |
121 | | // transform normal & tangent |
122 | 0 | auto normalMatrix = vsProgram->resolveParameter(GpuProgramParameters::ACT_NORMAL_MATRIX); |
123 | 0 | vstage.callBuiltin("mul", normalMatrix, vsInNormal, vsOutNormal); |
124 | 0 | vstage.callBuiltin("normalize", vsOutNormal, vsOutNormal); |
125 | 0 | vstage.callBuiltin("mul", normalMatrix, In(vsInTangent).xyz(), Out(vsOutTangent).xyz()); |
126 | 0 | vstage.callBuiltin("normalize", In(vsOutTangent).xyz(), Out(vsOutTangent).xyz()); |
127 | 0 | vstage.assign(In(vsInTangent).w(), Out(vsOutTangent).w()); |
128 | | |
129 | | // transform normal |
130 | 0 | fstage.callBuiltin("mul", psOutTBN, newViewNormal, newViewNormal); |
131 | 0 | } |
132 | 0 | else if (mNormalMapSpace & NMS_OBJECT) |
133 | 0 | { |
134 | | // transform normal in FS |
135 | 0 | auto normalMatrix = psProgram->resolveParameter(GpuProgramParameters::ACT_NORMAL_MATRIX); |
136 | 0 | fstage.callBuiltin("mul", normalMatrix, newViewNormal, newViewNormal); |
137 | 0 | } |
138 | |
|
139 | 0 | return true; |
140 | 0 | } |
141 | | |
142 | | //----------------------------------------------------------------------- |
143 | | void NormalMapLighting::copyFrom(const SubRenderState& rhs) |
144 | 0 | { |
145 | 0 | const NormalMapLighting& rhsLighting = static_cast<const NormalMapLighting&>(rhs); |
146 | |
|
147 | 0 | mNormalMapSpace = rhsLighting.mNormalMapSpace; |
148 | 0 | mNormalMapSamplerIndex = rhsLighting.mNormalMapSamplerIndex; |
149 | 0 | mParallaxHeightScale = rhsLighting.mParallaxHeightScale; |
150 | 0 | } |
151 | | |
152 | | //----------------------------------------------------------------------- |
153 | | bool NormalMapLighting::preAddToRenderState(const RenderState* renderState, Pass* srcPass, Pass* dstPass) |
154 | 0 | { |
155 | 0 | if (mNormalMapSamplerIndex >= 0) |
156 | 0 | { |
157 | 0 | mVSTexCoordSetIndex = srcPass->getTextureUnitState(mNormalMapSamplerIndex)->getTextureCoordSet(); |
158 | 0 | return true; |
159 | 0 | } |
160 | | |
161 | 0 | return false; |
162 | 0 | } |
163 | | |
164 | | bool NormalMapLighting::setParameter(const String& name, const String& value) |
165 | 0 | { |
166 | 0 | if (name == "normalmap_space") |
167 | 0 | { |
168 | | // Normal map defines normals in tangent space. |
169 | 0 | if (value == "tangent_space") |
170 | 0 | { |
171 | 0 | setNormalMapSpace(NMS_TANGENT); |
172 | 0 | return true; |
173 | 0 | } |
174 | | // Normal map defines normals in object space. |
175 | 0 | if (value == "object_space") |
176 | 0 | { |
177 | 0 | setNormalMapSpace(NMS_OBJECT); |
178 | 0 | return true; |
179 | 0 | } |
180 | 0 | if (value == "parallax") |
181 | 0 | { |
182 | 0 | setNormalMapSpace(NMS_PARALLAX); |
183 | 0 | return true; |
184 | 0 | } |
185 | 0 | if (value == "parallax_occlusion") |
186 | 0 | { |
187 | 0 | setNormalMapSpace(NMS_PARALLAX_OCCLUSION); |
188 | 0 | return true; |
189 | 0 | } |
190 | 0 | return false; |
191 | 0 | } |
192 | | |
193 | 0 | if (name == "texture_index") |
194 | 0 | { |
195 | 0 | return StringConverter::parse(value, mNormalMapSamplerIndex); |
196 | 0 | } |
197 | | |
198 | 0 | if (name == "height_scale") |
199 | 0 | { |
200 | 0 | return StringConverter::parse(value, mParallaxHeightScale); |
201 | 0 | } |
202 | | |
203 | 0 | return false; |
204 | 0 | } |
205 | | |
206 | | //----------------------------------------------------------------------- |
207 | 0 | const String& NormalMapLightingFactory::getType() const { return SRS_NORMALMAP; } |
208 | | |
209 | | //----------------------------------------------------------------------- |
210 | | SubRenderState* NormalMapLightingFactory::createInstance(const ScriptProperty& prop, Pass* pass, |
211 | | SGScriptTranslator* translator) |
212 | 0 | { |
213 | 0 | if (prop.name == "lighting_stage") |
214 | 0 | { |
215 | 0 | if (prop.values.size() >= 2) |
216 | 0 | { |
217 | | // Case light model type is normal map |
218 | 0 | if (prop.values[0] == "normal_map") |
219 | 0 | { |
220 | 0 | SubRenderState* subRenderState = createOrRetrieveInstance(translator); |
221 | |
|
222 | 0 | TextureUnitState* normalMapTexture = pass->createTextureUnitState(); |
223 | 0 | uint16 texureIdx = pass->getNumTextureUnitStates() - 1; |
224 | 0 | normalMapTexture->setTextureName(prop.values[1]); |
225 | 0 | subRenderState->setParameter("texture_index", std::to_string(texureIdx)); |
226 | |
|
227 | 0 | ShaderGenerator::_markNonFFP(normalMapTexture); |
228 | | |
229 | | // Read normal map space type. |
230 | 0 | if (prop.values.size() >= 3) |
231 | 0 | { |
232 | 0 | if (!subRenderState->setParameter("normalmap_space", prop.values[2])) |
233 | 0 | { |
234 | 0 | translator->emitError(); |
235 | 0 | } |
236 | 0 | } |
237 | | |
238 | | // Read texture coordinate index. |
239 | 0 | if (prop.values.size() >= 4) |
240 | 0 | { |
241 | 0 | unsigned int textureCoordinateIndex = 0; |
242 | 0 | if (StringConverter::parse(prop.values[3], textureCoordinateIndex)) |
243 | 0 | { |
244 | 0 | normalMapTexture->setTextureCoordSet(textureCoordinateIndex); |
245 | 0 | } |
246 | 0 | translator->emitError("use the texture_unit format to specify tex_coord_set and sampler_ref", |
247 | 0 | ScriptCompiler::CE_DEPRECATEDSYMBOL); |
248 | 0 | } |
249 | | |
250 | | // Read texture filtering format. |
251 | 0 | if (prop.values.size() >= 5) |
252 | 0 | { |
253 | | // sampler reference |
254 | 0 | if (auto sampler = TextureManager::getSingleton().getSampler(prop.values[4])) |
255 | 0 | { |
256 | 0 | normalMapTexture->setSampler(sampler); |
257 | 0 | } |
258 | 0 | else |
259 | 0 | { |
260 | 0 | translator->emitError(); |
261 | 0 | } |
262 | 0 | } |
263 | |
|
264 | 0 | return subRenderState; |
265 | 0 | } |
266 | 0 | } |
267 | 0 | } |
268 | 0 | return NULL; |
269 | 0 | } |
270 | | |
271 | | SubRenderState* NormalMapLightingFactory::createInstance(const ScriptProperty& prop, TextureUnitState* texState, |
272 | | SGScriptTranslator* translator) |
273 | 0 | { |
274 | 0 | if (prop.name == "normal_map" && !prop.values.empty()) |
275 | 0 | { |
276 | 0 | auto pass = texState->getParent(); |
277 | 0 | auto texureIdx = pass->getTextureUnitStateIndex(texState); |
278 | | |
279 | | // blacklist from FFP |
280 | 0 | ShaderGenerator::_markNonFFP(texState); |
281 | |
|
282 | 0 | SubRenderState* subRenderState = createOrRetrieveInstance(translator); |
283 | 0 | subRenderState->setParameter("texture_index", std::to_string(texureIdx)); |
284 | |
|
285 | 0 | if (!subRenderState->setParameter("normalmap_space", prop.values[0])) |
286 | 0 | { |
287 | 0 | translator->emitError(); |
288 | 0 | return subRenderState; |
289 | 0 | } |
290 | | |
291 | 0 | if (prop.values.size() % 2 != 1) // parameters must come in pairs now |
292 | 0 | { |
293 | 0 | translator->emitError(); |
294 | 0 | return subRenderState; |
295 | 0 | } |
296 | | |
297 | 0 | auto it = prop.values.begin() + 1; |
298 | 0 | while(it != prop.values.end()) |
299 | 0 | { |
300 | 0 | if (!subRenderState->setParameter(*it, *++it)) |
301 | 0 | { |
302 | 0 | translator->emitError(); |
303 | 0 | return subRenderState; |
304 | 0 | } |
305 | 0 | it++; |
306 | 0 | } |
307 | | |
308 | 0 | return subRenderState; |
309 | 0 | } |
310 | | |
311 | 0 | return NULL; |
312 | 0 | } |
313 | | |
314 | | //----------------------------------------------------------------------- |
315 | | void NormalMapLightingFactory::writeInstance(MaterialSerializer* ser, SubRenderState* subRenderState, |
316 | | const TextureUnitState* srcTex, const TextureUnitState* dstTex) |
317 | 0 | { |
318 | 0 | NormalMapLighting* normalMapSubRenderState = static_cast<NormalMapLighting*>(subRenderState); |
319 | |
|
320 | 0 | auto textureIdx = srcTex->getParent()->getTextureUnitStateIndex(srcTex); |
321 | 0 | if(textureIdx != normalMapSubRenderState->getNormalMapSamplerIndex()) |
322 | 0 | return; |
323 | | |
324 | 0 | ser->writeAttribute(5, "normal_map"); |
325 | |
|
326 | 0 | switch (normalMapSubRenderState->getNormalMapSpace()) |
327 | 0 | { |
328 | 0 | case NormalMapLighting::NMS_TANGENT: |
329 | 0 | ser->writeValue("tangent_space"); |
330 | 0 | break; |
331 | 0 | case NormalMapLighting::NMS_OBJECT: |
332 | 0 | ser->writeValue("object_space"); |
333 | 0 | break; |
334 | 0 | case NormalMapLighting::NMS_PARALLAX: |
335 | 0 | ser->writeValue("parallax"); |
336 | 0 | break; |
337 | 0 | case NormalMapLighting::NMS_PARALLAX_OCCLUSION: |
338 | 0 | ser->writeValue("parallax_occlusion"); |
339 | 0 | break; |
340 | 0 | } |
341 | 0 | } |
342 | | |
343 | | //----------------------------------------------------------------------- |
344 | 0 | SubRenderState* NormalMapLightingFactory::createInstanceImpl() { return OGRE_NEW NormalMapLighting; } |
345 | | |
346 | | } // namespace RTShader |
347 | | } // namespace Ogre |
348 | | |
349 | | #endif |