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 Also see acknowledgements in Readme.html
9 
10 You may use this sample code for anything you like, it is not covered by the
11 same license as the rest of the engine.
12 -----------------------------------------------------------------------------
13 */
14 
15 #include "GBufferSchemeHandler.h"
16 
17 #include <OgreMaterialManager.h>
18 #include <OgreTechnique.h>
19 
20 using namespace Ogre;
21 
22 const String GBufferSchemeHandler::NORMAL_MAP_PATTERN = "normal";
23 
handleSchemeNotFound(unsigned short schemeIndex,const String & schemeName,Material * originalMaterial,unsigned short lodIndex,const Renderable * rend)24 Technique* GBufferSchemeHandler::handleSchemeNotFound(unsigned short schemeIndex,
25 		const String& schemeName, Material* originalMaterial, unsigned short lodIndex,
26 		const Renderable* rend)
27 {
28 	Ogre::MaterialManager& matMgr = Ogre::MaterialManager::getSingleton();
29 	String curSchemeName = matMgr.getActiveScheme();
30 	matMgr.setActiveScheme(MaterialManager::DEFAULT_SCHEME_NAME);
31 	Technique* originalTechnique = originalMaterial->getBestTechnique(lodIndex, rend);
32 	matMgr.setActiveScheme(curSchemeName);
33 
34 	Technique* gBufferTech = originalMaterial->createTechnique();
35 	gBufferTech->removeAllPasses();
36 	gBufferTech->setSchemeName(schemeName);
37 
38 	Technique* noGBufferTech = originalMaterial->createTechnique();
39 	noGBufferTech->removeAllPasses();
40 	noGBufferTech->setSchemeName("NoGBuffer");
41 
42 	for (unsigned short i=0; i<originalTechnique->getNumPasses(); i++)
43 	{
44 		Pass* originalPass = originalTechnique->getPass(i);
45 		PassProperties props = inspectPass(originalPass, lodIndex, rend);
46 
47 		if (!props.isDeferred)
48 		{
49 			//Just copy the technique so it gets rendered regularly
50 			Pass* clonePass = noGBufferTech->createPass();
51 			*clonePass = *originalPass;
52 			continue;
53 		}
54 
55 		Pass* newPass = gBufferTech->createPass();
56 		MaterialGenerator::Perm perm = getPermutation(props);
57 
58 		const Ogre::MaterialPtr& templateMat = mMaterialGenerator.getMaterial(perm);
59 
60 		//We assume that the GBuffer technique contains only one pass. But its true.
61 		*newPass = *(templateMat->getTechnique(0)->getPass(0));
62 		fillPass(newPass, originalPass, props);
63 	}
64 
65 	return gBufferTech;
66 }
67 
checkNormalMap(TextureUnitState * tus,GBufferSchemeHandler::PassProperties & props)68 bool GBufferSchemeHandler::checkNormalMap(
69 	TextureUnitState* tus, GBufferSchemeHandler::PassProperties& props)
70 {
71 	bool isNormal = false;
72 	Ogre::String lowerCaseAlias = tus->getTextureNameAlias();
73 	Ogre::StringUtil::toLowerCase(lowerCaseAlias);
74 	if (lowerCaseAlias.find(NORMAL_MAP_PATTERN) != Ogre::String::npos)
75 	{
76 		isNormal = true;
77 	}
78 	else
79 	{
80 		Ogre::String lowerCaseName = tus->getTextureName();
81 		Ogre::StringUtil::toLowerCase(lowerCaseName);
82 		if (lowerCaseName.find(NORMAL_MAP_PATTERN) != Ogre::String::npos)
83 		{
84 			isNormal = true;
85 		}
86 	}
87 
88 	if (isNormal)
89 	{
90 		if (props.normalMap == 0)
91 		{
92 			props.normalMap = tus;
93 		}
94 		else
95 		{
96 			OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM,
97 				"Multiple normal map patterns matches",
98 				"GBufferSchemeHandler::inspectPass");
99 		}
100 	}
101 	return isNormal;
102 }
103 
inspectPass(Pass * pass,unsigned short lodIndex,const Renderable * rend)104 GBufferSchemeHandler::PassProperties GBufferSchemeHandler::inspectPass(
105 	Pass* pass, unsigned short lodIndex, const Renderable* rend)
106 {
107 	PassProperties props;
108 
109 	//TODO : Use renderable to indicate whether this has skinning.
110 	//Probably use same const cast that renderSingleObject uses.
111 	if (pass->hasVertexProgram())
112 	{
113 		props.isSkinned = pass->getVertexProgram()->isSkeletalAnimationIncluded();
114 	}
115 	else
116 	{
117 		props.isSkinned = false;
118 	}
119 
120 	for (unsigned short i=0; i<pass->getNumTextureUnitStates(); i++)
121 	{
122 		TextureUnitState* tus = pass->getTextureUnitState(i);
123 		if (!checkNormalMap(tus, props))
124 		{
125 			props.regularTextures.push_back(tus);
126 		}
127 		if (tus->getEffects().size() > 0)
128 		{
129 			props.isDeferred = false;
130 		}
131 
132 	}
133 
134     if (pass->getDiffuse() != ColourValue::White)
135     {
136         props.hasDiffuseColour = true;
137     }
138 
139     //Check transparency
140     if (pass->getDestBlendFactor() != Ogre::SBF_ZERO)
141     {
142         //TODO : Better ways to do this
143         props.isDeferred = false;
144     }
145 	return props;
146 }
147 
getPermutation(const PassProperties & props)148 MaterialGenerator::Perm GBufferSchemeHandler::getPermutation(const PassProperties& props)
149 {
150 	MaterialGenerator::Perm perm = 0;
151 	switch (props.regularTextures.size())
152 	{
153 	case 0:
154 		perm |= GBufferMaterialGenerator::GBP_NO_TEXTURES;
155 
156 		if (props.normalMap != 0)
157 		{
158 			perm |= GBufferMaterialGenerator::GBP_ONE_TEXCOORD;
159 		}
160 		else
161 		{
162 			perm |= GBufferMaterialGenerator::GBP_NO_TEXCOORDS;
163 		}
164 		break;
165 	case 1:
166 		perm |= GBufferMaterialGenerator::GBP_ONE_TEXTURE;
167 		perm |= GBufferMaterialGenerator::GBP_ONE_TEXCOORD;
168 		break;
169 	case 2:
170 		perm |= GBufferMaterialGenerator::GBP_TWO_TEXTURES;
171 		//TODO : When do we use two texcoords?
172 		perm |= GBufferMaterialGenerator::GBP_ONE_TEXCOORD;
173 		break;
174 	case 3:
175 		perm |= GBufferMaterialGenerator::GBP_THREE_TEXTURES;
176 		perm |= GBufferMaterialGenerator::GBP_ONE_TEXCOORD;
177 		break;
178 	default:
179 		OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,
180 			"Can not generate G-Buffer materials for '>3 regular-texture' objects",
181 			"GBufferSchemeHandler::inspectPass");
182 	}
183 
184 	if (props.isSkinned)
185 	{
186 		perm |= GBufferMaterialGenerator::GBP_SKINNED;
187 	}
188 
189 	if (props.normalMap != 0)
190 	{
191 		perm |= GBufferMaterialGenerator::GBP_NORMAL_MAP;
192 	}
193 
194     if (props.hasDiffuseColour)
195     {
196         perm |= GBufferMaterialGenerator::GBP_HAS_DIFFUSE_COLOUR;
197     }
198 	return perm;
199 }
200 
fillPass(Pass * gBufferPass,Pass * originalPass,const PassProperties & props)201 void GBufferSchemeHandler::fillPass(
202 	Pass* gBufferPass, Pass* originalPass, const PassProperties& props)
203 {
204 	//Reference the correct textures. Normal map first!
205 	int texUnitIndex = 0;
206 	if (props.normalMap != 0)
207 	{
208 		*(gBufferPass->getTextureUnitState(texUnitIndex)) = *(props.normalMap);
209 		texUnitIndex++;
210 	}
211 	for (size_t i=0; i<props.regularTextures.size(); i++)
212 	{
213 		*(gBufferPass->getTextureUnitState(texUnitIndex)) = *(props.regularTextures[i]);
214 		texUnitIndex++;
215 	}
216 	gBufferPass->setAmbient(originalPass->getAmbient());
217 	gBufferPass->setDiffuse(originalPass->getDiffuse());
218 	gBufferPass->setSpecular(originalPass->getSpecular());
219 	gBufferPass->setShininess(originalPass->getShininess());
220     gBufferPass->setCullingMode(originalPass->getCullingMode());
221     gBufferPass->setLightingEnabled(false);
222 }
223