1 #include "pch.h"
2 #include "MaterialInstance.hpp"
3 
4 #include <stdexcept>
5 #include <iostream>
6 
7 #include "Factory.hpp"
8 #include "ShaderSet.hpp"
9 
10 namespace sh
11 {
MaterialInstance(const std::string & name,Factory * f)12 	MaterialInstance::MaterialInstance (const std::string& name, Factory* f)
13 		: mName(name)
14 		, mShadersEnabled(true)
15 		, mFactory(f)
16 		, mListener(NULL)
17 		, mFailedToCreate(false)
18 	{
19 	}
20 
~MaterialInstance()21 	MaterialInstance::~MaterialInstance ()
22 	{
23 	}
24 
setParentInstance(const std::string & name)25 	void MaterialInstance::setParentInstance (const std::string& name)
26 	{
27 		mParentInstance = name;
28 	}
29 
getParentInstance()30 	std::string MaterialInstance::getParentInstance ()
31 	{
32 		return mParentInstance;
33 	}
34 
create(Platform * platform)35 	void MaterialInstance::create (Platform* platform)
36 	{
37 		mMaterial = platform->createMaterial(mName);
38 
39 		if (hasProperty ("shadow_caster_material"))
40 			mMaterial->setShadowCasterMaterial (retrieveValue<StringValue>(getProperty("shadow_caster_material"), NULL).get());
41 
42 		if (hasProperty ("lod_values"))
43 			mMaterial->setLodLevels (retrieveValue<StringValue>(getProperty("lod_values"), NULL).get());
44 	}
45 
destroyAll()46 	void MaterialInstance::destroyAll ()
47 	{
48 		if (hasProperty("create_configuration"))
49 			return;
50 		mMaterial->removeAll();
51 		mTexUnits.clear();
52 		mFailedToCreate = false;
53 	}
54 
setProperty(const std::string & name,PropertyValuePtr value)55 	void MaterialInstance::setProperty (const std::string& name, PropertyValuePtr value)
56 	{
57 		PropertySetGet::setProperty (name, value);
58 		destroyAll(); // trigger updates
59 	}
60 
createForConfiguration(const std::string & configuration,unsigned short lodIndex)61 	bool MaterialInstance::createForConfiguration (const std::string& configuration, unsigned short lodIndex)
62 	{
63 		if (mFailedToCreate)
64 			return false;
65 		try{
66 			mMaterial->ensureLoaded();
67 			bool res = mMaterial->createConfiguration(configuration, lodIndex);
68 			if (!res)
69 				return false; // listener was false positive
70 
71 			if (mListener)
72 				mListener->requestedConfiguration (this, configuration);
73 
74 			mFactory->setActiveConfiguration (configuration);
75 			mFactory->setActiveLodLevel (lodIndex);
76 
77 			bool allowFixedFunction = true;
78 			if (!mShadersEnabled && hasProperty("allow_fixed_function"))
79 			{
80 				allowFixedFunction = retrieveValue<BooleanValue>(getProperty("allow_fixed_function"), NULL).get();
81 			}
82 
83 			bool useShaders = mShadersEnabled || !allowFixedFunction;
84 
85 			// get passes of the top-most parent
86 			PassVector* passes = getParentPasses();
87 			if (passes->empty())
88 				throw std::runtime_error ("material \"" + mName + "\" does not have any passes");
89 
90 			for (PassVector::iterator it = passes->begin(); it != passes->end(); ++it)
91 			{
92 				boost::shared_ptr<Pass> pass = mMaterial->createPass (configuration, lodIndex);
93 				it->copyAll (pass.get(), this);
94 
95 				// texture samplers used in the shaders
96 				std::vector<std::string> usedTextureSamplersVertex;
97 				std::vector<std::string> usedTextureSamplersFragment;
98 
99 				PropertySetGet* context = this;
100 
101 				// create or retrieve shaders
102 				bool hasVertex = it->hasProperty("vertex_program")
103 						&& !retrieveValue<StringValue>(it->getProperty("vertex_program"), context).get().empty();
104 				bool hasFragment = it->hasProperty("fragment_program")
105 						&& !retrieveValue<StringValue>(it->getProperty("fragment_program"), context).get().empty();
106 				if (useShaders)
107 				{
108 					it->setContext(context);
109 					it->mShaderProperties.setContext(context);
110 					if (hasVertex)
111 					{
112 						ShaderSet* vertex = mFactory->getShaderSet(retrieveValue<StringValue>(it->getProperty("vertex_program"), context).get());
113 						ShaderInstance* v = vertex->getInstance(&it->mShaderProperties);
114 						if (v)
115 						{
116 							pass->assignProgram (GPT_Vertex, v->getName());
117 							v->setUniformParameters (pass, &it->mShaderProperties);
118 
119 							std::vector<std::string> sharedParams = v->getSharedParameters ();
120 							for (std::vector<std::string>::iterator it2 = sharedParams.begin(); it2 != sharedParams.end(); ++it2)
121 							{
122 								pass->addSharedParameter (GPT_Vertex, *it2);
123 							}
124 
125 							std::vector<std::string> vector = v->getUsedSamplers ();
126 							usedTextureSamplersVertex.insert(usedTextureSamplersVertex.end(), vector.begin(), vector.end());
127 						}
128 					}
129 					if (hasFragment)
130 					{
131 						ShaderSet* fragment = mFactory->getShaderSet(retrieveValue<StringValue>(it->getProperty("fragment_program"), context).get());
132 						ShaderInstance* f = fragment->getInstance(&it->mShaderProperties);
133 						if (f)
134 						{
135 							pass->assignProgram (GPT_Fragment, f->getName());
136 							f->setUniformParameters (pass, &it->mShaderProperties);
137 
138 							std::vector<std::string> sharedParams = f->getSharedParameters ();
139 							for (std::vector<std::string>::iterator it2 = sharedParams.begin(); it2 != sharedParams.end(); ++it2)
140 							{
141 								pass->addSharedParameter (GPT_Fragment, *it2);
142 							}
143 
144 							std::vector<std::string> vector = f->getUsedSamplers ();
145 							usedTextureSamplersFragment.insert(usedTextureSamplersFragment.end(), vector.begin(), vector.end());
146 						}
147 					}
148 				}
149 
150 				// create texture units
151 				std::vector<MaterialInstanceTextureUnit>* texUnits = &it->mTexUnits;
152 				int i=0;
153 				for (std::vector<MaterialInstanceTextureUnit>::iterator texIt = texUnits->begin(); texIt  != texUnits->end(); ++texIt )
154 				{
155 					// only create those that are needed by the shader, OR those marked to be created in fixed function pipeline if shaders are disabled
156 					bool foundVertex = std::find(usedTextureSamplersVertex.begin(), usedTextureSamplersVertex.end(), texIt->getName()) != usedTextureSamplersVertex.end();
157 					bool foundFragment = std::find(usedTextureSamplersFragment.begin(), usedTextureSamplersFragment.end(), texIt->getName()) != usedTextureSamplersFragment.end();
158 					if (  (foundVertex || foundFragment)
159 							|| (((!useShaders || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue<BooleanValue>(texIt->getProperty("create_in_ffp"), this).get()))
160 					{
161 						boost::shared_ptr<TextureUnitState> texUnit = pass->createTextureUnitState (texIt->getName());
162 						texIt->copyAll (texUnit.get(), context);
163 
164 						mTexUnits.push_back(texUnit);
165 
166 						// set texture unit indices (required by GLSL)
167 						if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && (mFactory->getCurrentLanguage () == Language_GLSL
168 											|| mFactory->getCurrentLanguage() == Language_GLSLES))
169 						{
170 							pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i);
171 
172 							++i;
173 						}
174 					}
175 				}
176 			}
177 
178 			if (mListener)
179 				mListener->createdConfiguration (this, configuration);
180 			return true;
181 
182 		} catch (std::runtime_error& e)
183 		{
184 			destroyAll();
185 			mFailedToCreate = true;
186 			std::stringstream msg;
187 			msg << "Error while creating material " << mName << ": " << e.what();
188 			std::cerr << msg.str() << std::endl;
189 			mFactory->logError(msg.str());
190 			return false;
191 		}
192 	}
193 
getMaterial()194 	Material* MaterialInstance::getMaterial ()
195 	{
196 		return mMaterial.get();
197 	}
198 
createPass()199 	MaterialInstancePass* MaterialInstance::createPass ()
200 	{
201 		mPasses.push_back (MaterialInstancePass());
202 		mPasses.back().setContext(this);
203 		return &mPasses.back();
204 	}
205 
deletePass(unsigned int index)206 	void MaterialInstance::deletePass(unsigned int index)
207 	{
208 		assert(mPasses.size() > index);
209 		mPasses.erase(mPasses.begin()+index);
210 	}
211 
getParentPasses()212 	PassVector* MaterialInstance::getParentPasses()
213 	{
214 		if (mParent)
215 			return static_cast<MaterialInstance*>(mParent)->getParentPasses();
216 		else
217 			return &mPasses;
218 	}
219 
getPasses()220 	PassVector* MaterialInstance::getPasses()
221 	{
222 		return &mPasses;
223 	}
224 
setShadersEnabled(bool enabled)225 	void MaterialInstance::setShadersEnabled (bool enabled)
226 	{
227 		if (enabled == mShadersEnabled)
228 			return;
229 		mShadersEnabled = enabled;
230 
231 		// trigger updates
232 		if (mMaterial.get())
233 			destroyAll();
234 	}
235 
save(std::ofstream & stream)236 	void MaterialInstance::save (std::ofstream& stream)
237 	{
238 		stream << "material " << mName << "\n"
239 			   << "{\n";
240 
241 		if (mParent)
242 		{
243 			stream << "\t" << "parent " << static_cast<MaterialInstance*>(mParent)->getName() << "\n";
244 		}
245 
246 		const PropertyMap& properties = listProperties ();
247 		for (PropertyMap::const_iterator it = properties.begin(); it != properties.end(); ++it)
248 		{
249 			stream << "\t" << it->first << " " << retrieveValue<StringValue>(getProperty(it->first), NULL).get() << "\n";
250 		}
251 
252 		for (PassVector::iterator it = mPasses.begin(); it != mPasses.end(); ++it)
253 		{
254 			stream << "\tpass" << '\n';
255 			stream << "\t{" << '\n';
256 			it->save(stream);
257 			stream << "\t}" << '\n';
258 		}
259 
260 		stream << "}\n";
261 	}
262 }
263