void ShaderGenerator::generateShaders(Material& mat, const RenderState& rs, const VertexFormat& vf, const FogParameters fogParams) { bool hasNormalMap = false; bool hasParallaxMap = false; ctemplate::TemplateDictionary vertexShaderDict("vertexShader"); ctemplate::TemplateDictionary fragmentShaderDict("fragmentShader"); ctemplate::TemplateDictionary* vertexIoDict = vertexShaderDict.AddIncludeDictionary("VERTEX_INPUTS"); vertexIoDict->SetFilename("shader_templates/ft_vertex_inout.tpl"); ctemplate::TemplateDictionary* fragmentOutDict = vertexShaderDict.AddIncludeDictionary("FRAGMENT_INPUTS"); fragmentOutDict->SetFilename("shader_templates/ft_fragment_inout.tpl"); fragmentOutDict->ShowSection("OUT_DIRECTION"); ctemplate::TemplateDictionary* uniformsDict = vertexShaderDict.AddIncludeDictionary("UNIFORMS"); uniformsDict->SetFilename("shader_templates/uniforms.tpl"); // emit the fog uniforms if necessary if (fogParams.m_fogMode != FOG_NONE) { uniformsDict->ShowSection("FOG"); } // use lighting when material uses shading and we have normals bool useLighting = !mat.m_shadeless && vf.getAttributeBySemantic(Vertex_Normal); if (useLighting) { uniformsDict->ShowSection("USE_LIGHTING"); } if (vf.getAttributeBySemantic(Vertex_Normal)) { vertexShaderDict.ShowSection("HAS_NORMALS"); vertexShaderDict.ShowSection("NORMALIZE_NORMALS"); vertexIoDict->ShowSection("HAS_NORMALS"); vertexIoDict->ShowSection("NORMALIZE_NORMALS"); fragmentOutDict->ShowSection("HAS_NORMALS"); uniformsDict->ShowSection("HAS_NORMALS"); } if (vf.getAttributeBySemantic(Vertex_Tangent)) { fragmentOutDict->ShowSection("HAS_TANGENTS"); vertexIoDict->ShowSection("HAS_TANGENTS"); } if (vf.getAttributeBySemantic(Vertex_BiNormal)) { fragmentOutDict->ShowSection("HAS_BINORMALS"); vertexIoDict->ShowSection("HAS_BINORMALS"); } if (vf.getAttributeBySemantic(Vertex_Color)) { vertexShaderDict.ShowSection("HAS_COLORS"); vertexIoDict->ShowSection("HAS_COLORS"); fragmentOutDict->ShowSection("HAS_COLORS"); } // indicates if the tex coords generation functions declarations template has already been // included in the vertex shader template bool texGenDeclIncluded = false; // number of active texture units uint activeTextures = 0; ctemplate::TemplateDictionary* texGenDict = vertexShaderDict.AddIncludeDictionary("TEX_COORDS_GEN"); texGenDict->SetFilename("shader_templates/ft_tex_coords_gen.tpl"); // Loops over all material textures and performs the following: // 1. Declares a respective uniform sampler object which will be named as u_sampler#, // where # is the active texture index. // 2. Emits code for any texture coordinates generation scheme, if not simple UV. ctemplate::TemplateDictionary* parallaxMapDict = 0; for (int i = 0; i < MAX_TEXTURES_STACK; i++) { TexturePtr tex = mat.m_texStack->textures[i]; if (tex) { // for the fragment shader, here we can fill in the dictionary for the texture application section // that takes into account the texture's mapTo, environment color, uvset, etc. ctemplate::TemplateDictionary* texUnitDict = fragmentShaderDict.AddIncludeDictionary( "SINGLE_TEXTURE_STACK_ENTRY"); texUnitDict->SetFilename("shader_templates/ft_tex_stack_application.tpl"); ctemplate::TemplateDictionary* alphaMapDict = 0; ctemplate::TemplateDictionary* tangentNormalMapDict = 0; ctemplate::TemplateDictionary* offsetMappingDict = 0; // depending on the texture's mapTo, we will emit different sections in the template ctemplate::TemplateDictionary* texMapToDict = 0; switch (mat.m_texStack->texOutputs[i].mapTo) { case TexMapTo_Diffuse: texMapToDict = texUnitDict->AddSectionDictionary("TEX_MAP_TO_DIFFUSE"); break; case TexMapTo_CSpecular: texMapToDict = texUnitDict->AddSectionDictionary("TEX_MAP_TO_SPECULAR"); break; case TexMapTo_Shininess: texMapToDict = texUnitDict->AddSectionDictionary("TEX_MAP_TO_SHININESS"); break; case TexMapTo_Alpha: alphaMapDict = fragmentShaderDict.AddSectionDictionary("ALPHA_MAP_APPLICATION"); break; case TexMapTo_Normal: tangentNormalMapDict = fragmentShaderDict.AddIncludeDictionary("TANGENT_SPACE_NORMAL_MAP"); tangentNormalMapDict->SetFilename(TANGENT_SPACE_NMAP_TPL); parallaxMapDict = tangentNormalMapDict; hasNormalMap = true; break; case TexMapTo_Parallax: /* IMPORTANT: Parallax maps must come after the normal maps */ if (parallaxMapDict) { offsetMappingDict = parallaxMapDict->AddSectionDictionary("PARALLAX_OFFSET_MAPPING"); } hasParallaxMap = true; break; default: SAFE_THROW(GLException(E_NOTIMPL, "Unimplemented texture output mapping mode")) } texUnitDict->SetIntValue(TEX_INDEX, activeTextures); texUnitDict->SetIntValue(UV_SET_INDEX, mat.m_texStack->texInputs[i].uvSet); if (alphaMapDict) { alphaMapDict->SetIntValue(TEX_INDEX, activeTextures); alphaMapDict->SetIntValue(UV_SET_INDEX, mat.m_texStack->texInputs[i].uvSet); } if (tangentNormalMapDict) { tangentNormalMapDict->SetIntValue(TEX_INDEX, activeTextures); tangentNormalMapDict->SetIntValue(UV_SET_INDEX, mat.m_texStack->texInputs[i].uvSet); } if (offsetMappingDict) { offsetMappingDict->SetIntValue(TEX_INDEX, activeTextures); offsetMappingDict->SetIntValue(UV_SET_INDEX, mat.m_texStack->texInputs[i].uvSet); } switch (mat.m_texStack->texOutputs[i].blendOp) { case TexBlendOp_Mix: texUnitDict->ShowSection("TEX_BLENDOP_BLEND"); break; case TexBlendOp_Add: texUnitDict->ShowSection("TEX_BLENDOP_ADD"); break; case TexBlendOp_Multiply: texUnitDict->ShowSection("TEX_BLENDOP_MULTIPLY"); break; case TexBlendOp_Decal: texUnitDict->ShowSection("TEX_BLENDOP_DECAL"); break; default: SAFE_THROW(GLException(E_NOTIMPL, "Unimplemented texture blending mode")) } // this is the dictionary for a single uniform sampler declaration ctemplate::TemplateDictionary* samplerDict = uniformsDict->AddSectionDictionary("SINGLE_SAMPLER_DECL"); samplerDict->SetIntValue(TEX_INDEX, activeTextures); const char* samplerToken; const char* coordsToken; switch (tex->getTextureTarget()) { case GL_TEXTURE_1D: samplerToken = "sampler1D"; coordsToken = S_COORD; break; case GL_TEXTURE_CUBE_MAP: samplerToken = "samplerCube"; coordsToken = STP_COORDS; break; case GL_TEXTURE_2D: default: samplerToken = "sampler2D"; coordsToken = ST_COORDS; break; } samplerDict->SetValue("SAMPLER_SPEC", samplerToken); texUnitDict->SetValue(TEX_COORDS, coordsToken); if (alphaMapDict) { alphaMapDict->SetValue(TEX_COORDS, coordsToken); } if (tangentNormalMapDict) { tangentNormalMapDict->SetValue(TEX_COORDS, coordsToken); } if (offsetMappingDict) { offsetMappingDict->SetValue(TEX_COORDS, coordsToken); } // When a special texture coordinate generation system is used, we have to include // the TEX_COORDS_GEN_DECL template which contains function declarations for the various // tex gen systems. Then for each texture unit that uses custom tex gen, we need to // instantiate a SINGLE_TEX_COORDS_GEN section with an appropriately initialized dictionary ctemplate::TemplateDictionary* singleTexGen = texGenDict->AddSectionDictionary("SINGLE_TEXCOORDS_ASSIGN"); singleTexGen->SetIntValue(UV_SET_INDEX, mat.m_texStack->texInputs[i].uvSet); singleTexGen->SetValue(TEX_COORDS, coordsToken); TexMapInput inputMapping = mat.m_texStack->texInputs[i].mapping; if (inputMapping != TexMapInput_UV) { if (!texGenDeclIncluded) { ctemplate::TemplateDictionary* texGenDecl = vertexShaderDict.AddIncludeDictionary( "TEX_COORDS_GEN_DECL"); texGenDecl->SetFilename("shader_templates/ft_tex_coords_gen_decl.tpl"); texGenDeclIncluded = true; } switch (inputMapping) { case TexMapInput_Normal: singleTexGen->ShowSection("NORMAL_TEXGEN"); break; case TexMapInput_Refl: singleTexGen->ShowSection("REFLECTION_TEXGEN"); break; case TexMapInput_Spherical: singleTexGen->ShowSection("SPHERE_TEXGEN"); break; case TexMapInput_EyeSpace: singleTexGen->ShowSection("EYE_TEXGEN"); break; case TexMapInput_ObjectSpace: singleTexGen->ShowSection("OBJECT_TEXGEN"); break; case TexMapInput_Cube: singleTexGen->ShowSection("CUBE_TEXGEN"); break; default: SAFE_THROW(GLException(E_NOTIMPL, "Unimplemented texture mapping mode")) } } else { singleTexGen->ShowSection("UV_MAPPING"); } ++activeTextures; } }