예제 #1
0
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;
		}
	}