mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 07:26:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			694 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			694 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "ShaderInstance.hpp"
 | 
						|
 | 
						|
#include <stdexcept>
 | 
						|
#include <iostream>
 | 
						|
#include <fstream>
 | 
						|
 | 
						|
#include <boost/lexical_cast.hpp>
 | 
						|
#include <boost/algorithm/string.hpp>
 | 
						|
#include <boost/bind.hpp>
 | 
						|
 | 
						|
#include <boost/filesystem.hpp>
 | 
						|
 | 
						|
#include "Preprocessor.hpp"
 | 
						|
#include "Factory.hpp"
 | 
						|
#include "ShaderSet.hpp"
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
	std::string convertLang (sh::Language lang)
 | 
						|
	{
 | 
						|
		if (lang == sh::Language_CG)
 | 
						|
			return "SH_CG";
 | 
						|
		else if (lang == sh::Language_HLSL)
 | 
						|
			return "SH_HLSL";
 | 
						|
		else //if (lang == sh::Language_GLSL)
 | 
						|
			return "SH_GLSL";
 | 
						|
	}
 | 
						|
 | 
						|
	char getComponent(int num)
 | 
						|
	{
 | 
						|
		if (num == 0)
 | 
						|
			return 'x';
 | 
						|
		else if (num == 1)
 | 
						|
			return 'y';
 | 
						|
		else if (num == 2)
 | 
						|
			return 'z';
 | 
						|
		else if (num == 3)
 | 
						|
			return 'w';
 | 
						|
		else
 | 
						|
			throw std::runtime_error("invalid component");
 | 
						|
	}
 | 
						|
 | 
						|
	std::string getFloat(sh::Language lang, int num_components)
 | 
						|
	{
 | 
						|
		if (lang == sh::Language_CG || lang == sh::Language_HLSL)
 | 
						|
			return (num_components == 1) ? "float" : "float" + boost::lexical_cast<std::string>(num_components);
 | 
						|
		else
 | 
						|
			return (num_components == 1) ? "float" : "vec" + boost::lexical_cast<std::string>(num_components);
 | 
						|
	}
 | 
						|
 | 
						|
	bool isCmd (const std::string& source, size_t pos, const std::string& cmd)
 | 
						|
	{
 | 
						|
		return (source.size() >= pos + cmd.size() && source.substr(pos, cmd.size()) == cmd);
 | 
						|
	}
 | 
						|
 | 
						|
	void writeDebugFile (const std::string& content, const std::string& filename)
 | 
						|
	{
 | 
						|
		boost::filesystem::path full_path(boost::filesystem::current_path());
 | 
						|
		std::ofstream of ((full_path / filename ).string().c_str() , std::ios_base::out);
 | 
						|
		of.write(content.c_str(), content.size());
 | 
						|
		of.close();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
namespace sh
 | 
						|
{
 | 
						|
	std::string Passthrough::expand_assign(std::string toAssign)
 | 
						|
	{
 | 
						|
		std::string res;
 | 
						|
 | 
						|
		int i = 0;
 | 
						|
		int current_passthrough = passthrough_number;
 | 
						|
		int current_component_left = component_start;
 | 
						|
		int current_component_right = 0;
 | 
						|
		int components_left = num_components;
 | 
						|
		int components_at_once;
 | 
						|
		while (i < num_components)
 | 
						|
		{
 | 
						|
			if (components_left + current_component_left <= 4)
 | 
						|
				components_at_once = components_left;
 | 
						|
			else
 | 
						|
				components_at_once = 4 - current_component_left;
 | 
						|
 | 
						|
			std::string componentStr = ".";
 | 
						|
			for (int j = 0; j < components_at_once; ++j)
 | 
						|
				componentStr += getComponent(j + current_component_left);
 | 
						|
			std::string componentStr2 = ".";
 | 
						|
			for (int j = 0; j < components_at_once; ++j)
 | 
						|
				componentStr2 += getComponent(j + current_component_right);
 | 
						|
			if (num_components == 1)
 | 
						|
			{
 | 
						|
				componentStr2 = "";
 | 
						|
			}
 | 
						|
			res += "passthrough" + boost::lexical_cast<std::string>(current_passthrough) + componentStr + " = " + toAssign + componentStr2;
 | 
						|
 | 
						|
			current_component_left += components_at_once;
 | 
						|
			current_component_right += components_at_once;
 | 
						|
			components_left -= components_at_once;
 | 
						|
 | 
						|
			i += components_at_once;
 | 
						|
 | 
						|
			if (components_left == 0)
 | 
						|
			{
 | 
						|
				// finished
 | 
						|
				return res;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				// add semicolon to every instruction but the last
 | 
						|
				res += "; ";
 | 
						|
			}
 | 
						|
 | 
						|
			if (current_component_left == 4)
 | 
						|
			{
 | 
						|
				current_passthrough++;
 | 
						|
				current_component_left = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		throw std::runtime_error("expand_assign error"); // this should never happen, but gets us rid of the "control reaches end of non-void function" warning
 | 
						|
	}
 | 
						|
 | 
						|
	std::string Passthrough::expand_receive()
 | 
						|
	{
 | 
						|
		std::string res;
 | 
						|
 | 
						|
		res += getFloat(lang, num_components) + "(";
 | 
						|
 | 
						|
		int i = 0;
 | 
						|
		int current_passthrough = passthrough_number;
 | 
						|
		int current_component = component_start;
 | 
						|
		int components_left = num_components;
 | 
						|
		while (i < num_components)
 | 
						|
		{
 | 
						|
			int components_at_once = std::min(components_left, 4 - current_component);
 | 
						|
 | 
						|
			std::string componentStr;
 | 
						|
			for (int j = 0; j < components_at_once; ++j)
 | 
						|
				componentStr += getComponent(j + current_component);
 | 
						|
 | 
						|
			res += "passthrough" + boost::lexical_cast<std::string>(current_passthrough) + "." + componentStr;
 | 
						|
 | 
						|
			current_component += components_at_once;
 | 
						|
 | 
						|
			components_left -= components_at_once;
 | 
						|
 | 
						|
			i += components_at_once;
 | 
						|
 | 
						|
			if (components_left == 0)
 | 
						|
			{
 | 
						|
				// finished
 | 
						|
				return res + ")";
 | 
						|
;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				// add comma to every variable but the last
 | 
						|
				res += ", ";
 | 
						|
			}
 | 
						|
 | 
						|
			if (current_component == 4)
 | 
						|
			{
 | 
						|
				current_passthrough++;
 | 
						|
				current_component = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		throw std::runtime_error("expand_receive error"); // this should never happen, but gets us rid of the "control reaches end of non-void function" warning
 | 
						|
	}
 | 
						|
 | 
						|
	// ------------------------------------------------------------------------------
 | 
						|
 | 
						|
	void ShaderInstance::parse (std::string& source, PropertySetGet* properties)
 | 
						|
	{
 | 
						|
		size_t pos = 0;
 | 
						|
		while (true)
 | 
						|
		{
 | 
						|
			pos =  source.find("@", pos);
 | 
						|
			if (pos == std::string::npos)
 | 
						|
				break;
 | 
						|
 | 
						|
			if (isCmd(source, pos, "@shProperty"))
 | 
						|
			{
 | 
						|
				std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
 | 
						|
				size_t start = source.find("(", pos);
 | 
						|
				size_t end = source.find(")", pos);
 | 
						|
				std::string cmd = source.substr(pos+1, start-(pos+1));
 | 
						|
 | 
						|
				std::string replaceValue;
 | 
						|
				if (cmd == "shPropertyBool")
 | 
						|
				{
 | 
						|
					std::string propertyName = args[0];
 | 
						|
					PropertyValuePtr value = properties->getProperty(propertyName);
 | 
						|
					bool val = retrieveValue<BooleanValue>(value, properties->getContext()).get();
 | 
						|
					replaceValue = val ? "1" : "0";
 | 
						|
				}
 | 
						|
				else if (cmd == "shPropertyNotBool") // same as above, but inverts the result
 | 
						|
				{
 | 
						|
					std::string propertyName = args[0];
 | 
						|
					PropertyValuePtr value = properties->getProperty(propertyName);
 | 
						|
					bool val = retrieveValue<BooleanValue>(value, properties->getContext()).get();
 | 
						|
					replaceValue = val ? "0" : "1";
 | 
						|
				}
 | 
						|
				else if (cmd == "shPropertyString")
 | 
						|
				{
 | 
						|
					std::string propertyName = args[0];
 | 
						|
					PropertyValuePtr value = properties->getProperty(propertyName);
 | 
						|
					replaceValue = retrieveValue<StringValue>(value, properties->getContext()).get();
 | 
						|
				}
 | 
						|
				else if (cmd == "shPropertyEqual")
 | 
						|
				{
 | 
						|
					std::string propertyName = args[0];
 | 
						|
					std::string comparedAgainst = args[1];
 | 
						|
					std::string value = retrieveValue<StringValue>(properties->getProperty(propertyName), properties->getContext()).get();
 | 
						|
					replaceValue = (value == comparedAgainst) ? "1" : "0";
 | 
						|
				}
 | 
						|
				else
 | 
						|
					throw std::runtime_error ("unknown command \"" + cmd + "\"");
 | 
						|
				source.replace(pos, (end+1)-pos, replaceValue);
 | 
						|
			}
 | 
						|
			else if (isCmd(source, pos, "@shGlobalSetting"))
 | 
						|
			{
 | 
						|
				std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
 | 
						|
				std::string cmd = source.substr(pos+1,  source.find("(", pos)-(pos+1));
 | 
						|
				std::string replaceValue;
 | 
						|
				if (cmd == "shGlobalSettingBool")
 | 
						|
				{
 | 
						|
					std::string settingName = args[0];
 | 
						|
					std::string value = retrieveValue<StringValue>(mParent->getCurrentGlobalSettings()->getProperty(settingName), NULL).get();
 | 
						|
					replaceValue = (value == "true" || value == "1") ? "1" : "0";
 | 
						|
				}
 | 
						|
				else if (cmd == "shGlobalSettingEqual")
 | 
						|
				{
 | 
						|
					std::string settingName = args[0];
 | 
						|
					std::string comparedAgainst = args[1];
 | 
						|
					std::string value = retrieveValue<StringValue>(mParent->getCurrentGlobalSettings()->getProperty(settingName), NULL).get();
 | 
						|
					replaceValue = (value == comparedAgainst) ? "1" : "0";
 | 
						|
				}
 | 
						|
				else if (cmd == "shGlobalSettingString")
 | 
						|
				{
 | 
						|
					std::string settingName = args[0];
 | 
						|
					replaceValue = retrieveValue<StringValue>(mParent->getCurrentGlobalSettings()->getProperty(settingName), NULL).get();
 | 
						|
				}
 | 
						|
				else
 | 
						|
					throw std::runtime_error ("unknown command \"" + cmd + "\"");
 | 
						|
 | 
						|
				source.replace(pos, (source.find(")", pos)+1)-pos, replaceValue);
 | 
						|
			}
 | 
						|
			else if (isCmd(source, pos, "@shForeach"))
 | 
						|
			{
 | 
						|
 | 
						|
				assert(source.find("@shEndForeach", pos) != std::string::npos);
 | 
						|
				size_t block_end = source.find("@shEndForeach", pos);
 | 
						|
 | 
						|
				// get the argument for parsing
 | 
						|
				size_t start = source.find("(", pos);
 | 
						|
				size_t end = start;
 | 
						|
				int brace_depth = 1;
 | 
						|
				while (brace_depth > 0)
 | 
						|
				{
 | 
						|
					++end;
 | 
						|
					if (source[end] == '(')
 | 
						|
						++brace_depth;
 | 
						|
					else if (source[end] == ')')
 | 
						|
						--brace_depth;
 | 
						|
				}
 | 
						|
				std::string arg = source.substr(start+1, end-(start+1));
 | 
						|
				parse(arg, properties);
 | 
						|
 | 
						|
				int num = boost::lexical_cast<int>(arg);
 | 
						|
 | 
						|
				// get the content of the inner block
 | 
						|
				std::string content = source.substr(end+1, block_end - (end+1));
 | 
						|
 | 
						|
				// replace both outer and inner block with content of inner block num times
 | 
						|
				std::string replaceStr;
 | 
						|
				for (int i=0; i<num; ++i)
 | 
						|
				{
 | 
						|
					// replace @shIterator with the current iteration
 | 
						|
					std::string addStr = content;
 | 
						|
 | 
						|
					while (true)
 | 
						|
					{
 | 
						|
						size_t pos2 = addStr.find("@shIterator");
 | 
						|
						if (pos2 == std::string::npos)
 | 
						|
							break;
 | 
						|
 | 
						|
						// optional offset parameter.
 | 
						|
						size_t openBracePos = pos2 + std::string("@shIterator").length();
 | 
						|
						if (addStr[openBracePos] == '(')
 | 
						|
						{
 | 
						|
							// get the argument for parsing
 | 
						|
							size_t _start = openBracePos;
 | 
						|
							size_t _end = _start;
 | 
						|
							int _brace_depth = 1;
 | 
						|
							while (_brace_depth > 0)
 | 
						|
							{
 | 
						|
								++_end;
 | 
						|
								if (addStr[_end] == '(')
 | 
						|
									++_brace_depth;
 | 
						|
								else if (addStr[_end] == ')')
 | 
						|
									--_brace_depth;
 | 
						|
							}
 | 
						|
							std::string arg = addStr.substr(_start+1, _end-(_start+1));
 | 
						|
							parse(arg, properties);
 | 
						|
 | 
						|
							int offset = boost::lexical_cast<int> (arg);
 | 
						|
							addStr.replace(pos2, (_end+1)-pos2, boost::lexical_cast<std::string>(i+offset));
 | 
						|
						}
 | 
						|
						else
 | 
						|
						{
 | 
						|
							addStr.replace(pos2, std::string("@shIterator").length(), boost::lexical_cast<std::string>(i));
 | 
						|
						}
 | 
						|
					}
 | 
						|
 | 
						|
					replaceStr += addStr;
 | 
						|
				}
 | 
						|
				source.replace(pos, (block_end+std::string("@shEndForeach").length())-pos, replaceStr);
 | 
						|
			}
 | 
						|
			else if (source.size() > pos+1)
 | 
						|
				++pos; // skip
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	ShaderInstance::ShaderInstance (ShaderSet* parent, const std::string& name, PropertySetGet* properties)
 | 
						|
		: mName(name)
 | 
						|
		, mParent(parent)
 | 
						|
		, mSupported(true)
 | 
						|
		, mCurrentPassthrough(0)
 | 
						|
		, mCurrentComponent(0)
 | 
						|
	{
 | 
						|
		std::string source = mParent->getSource();
 | 
						|
		int type = mParent->getType();
 | 
						|
		std::string basePath = mParent->getBasePath();
 | 
						|
		size_t pos;
 | 
						|
 | 
						|
		bool readCache = Factory::getInstance ().getReadSourceCache () && boost::filesystem::exists(
 | 
						|
					Factory::getInstance ().getCacheFolder () + "/" + mName);
 | 
						|
		bool writeCache = Factory::getInstance ().getWriteSourceCache ();
 | 
						|
 | 
						|
 | 
						|
		if (readCache)
 | 
						|
		{
 | 
						|
			std::ifstream ifs( std::string(Factory::getInstance ().getCacheFolder () + "/" + mName).c_str() );
 | 
						|
			std::stringstream ss;
 | 
						|
			ss << ifs.rdbuf();
 | 
						|
			source = ss.str();
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			std::vector<std::string> definitions;
 | 
						|
 | 
						|
			if (mParent->getType() == GPT_Vertex)
 | 
						|
				definitions.push_back("SH_VERTEX_SHADER");
 | 
						|
			else
 | 
						|
				definitions.push_back("SH_FRAGMENT_SHADER");
 | 
						|
			definitions.push_back(convertLang(Factory::getInstance().getCurrentLanguage()));
 | 
						|
 | 
						|
			parse(source, properties);
 | 
						|
 | 
						|
			if (Factory::getInstance ().getShaderDebugOutputEnabled ())
 | 
						|
				writeDebugFile(source, name + ".pre");
 | 
						|
 | 
						|
			// why do we need our own preprocessor? there are several custom commands available in the shader files
 | 
						|
			// (for example for binding uniforms to properties or auto constants) - more below. it is important that these
 | 
						|
			// commands are _only executed if the specific code path actually "survives" the compilation.
 | 
						|
			// thus, we run the code through a preprocessor first to remove the parts that are unused because of
 | 
						|
			// unmet #if conditions (or other preprocessor directives).
 | 
						|
			source = Preprocessor::preprocess(source, basePath, definitions, name);
 | 
						|
 | 
						|
			// parse counter
 | 
						|
			std::map<int, int> counters;
 | 
						|
			while (true)
 | 
						|
			{
 | 
						|
				pos = source.find("@shCounter");
 | 
						|
				if (pos == std::string::npos)
 | 
						|
					break;
 | 
						|
 | 
						|
				size_t end = source.find(")", pos);
 | 
						|
 | 
						|
				std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
				assert(args.size());
 | 
						|
 | 
						|
				int index = boost::lexical_cast<int>(args[0]);
 | 
						|
 | 
						|
				if (counters.find(index) == counters.end())
 | 
						|
					counters[index] = 0;
 | 
						|
 | 
						|
				source.replace(pos, (end+1)-pos, boost::lexical_cast<std::string>(counters[index]++));
 | 
						|
			}
 | 
						|
 | 
						|
			// parse passthrough declarations
 | 
						|
			while (true)
 | 
						|
			{
 | 
						|
				pos = source.find("@shAllocatePassthrough");
 | 
						|
				if (pos == std::string::npos)
 | 
						|
					break;
 | 
						|
 | 
						|
				if (mCurrentPassthrough > 7)
 | 
						|
					throw std::runtime_error ("too many passthrough's requested (max 8)");
 | 
						|
 | 
						|
				std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
				assert(args.size() == 2);
 | 
						|
 | 
						|
				size_t end = source.find(")", pos);
 | 
						|
 | 
						|
				Passthrough passthrough;
 | 
						|
 | 
						|
				passthrough.num_components = boost::lexical_cast<int>(args[0]);
 | 
						|
				assert (passthrough.num_components != 0);
 | 
						|
 | 
						|
				std::string passthroughName = args[1];
 | 
						|
				passthrough.lang = Factory::getInstance().getCurrentLanguage ();
 | 
						|
				passthrough.component_start = mCurrentComponent;
 | 
						|
				passthrough.passthrough_number = mCurrentPassthrough;
 | 
						|
 | 
						|
				mPassthroughMap[passthroughName] = passthrough;
 | 
						|
 | 
						|
				mCurrentComponent += passthrough.num_components;
 | 
						|
				if (mCurrentComponent > 3)
 | 
						|
				{
 | 
						|
					mCurrentComponent -= 4;
 | 
						|
					++mCurrentPassthrough;
 | 
						|
				}
 | 
						|
 | 
						|
				source.erase(pos, (end+1)-pos);
 | 
						|
			}
 | 
						|
 | 
						|
			// passthrough assign
 | 
						|
			while (true)
 | 
						|
			{
 | 
						|
				pos = source.find("@shPassthroughAssign");
 | 
						|
				if (pos == std::string::npos)
 | 
						|
					break;
 | 
						|
 | 
						|
				std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
				assert(args.size() == 2);
 | 
						|
 | 
						|
				size_t end = source.find(")", pos);
 | 
						|
 | 
						|
				std::string passthroughName = args[0];
 | 
						|
				std::string assignTo = args[1];
 | 
						|
 | 
						|
				assert(mPassthroughMap.find(passthroughName) != mPassthroughMap.end());
 | 
						|
				Passthrough& p = mPassthroughMap[passthroughName];
 | 
						|
 | 
						|
				source.replace(pos, (end+1)-pos, p.expand_assign(assignTo));
 | 
						|
			}
 | 
						|
 | 
						|
			// passthrough receive
 | 
						|
			while (true)
 | 
						|
			{
 | 
						|
				pos = source.find("@shPassthroughReceive");
 | 
						|
				if (pos == std::string::npos)
 | 
						|
					break;
 | 
						|
 | 
						|
				std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
				assert(args.size() == 1);
 | 
						|
 | 
						|
				size_t end = source.find(")", pos);
 | 
						|
				std::string passthroughName = args[0];
 | 
						|
 | 
						|
				assert(mPassthroughMap.find(passthroughName) != mPassthroughMap.end());
 | 
						|
				Passthrough& p = mPassthroughMap[passthroughName];
 | 
						|
 | 
						|
				source.replace(pos, (end+1)-pos, p.expand_receive());
 | 
						|
			}
 | 
						|
 | 
						|
			// passthrough vertex outputs
 | 
						|
			while (true)
 | 
						|
			{
 | 
						|
				pos = source.find("@shPassthroughVertexOutputs");
 | 
						|
				if (pos == std::string::npos)
 | 
						|
					break;
 | 
						|
 | 
						|
				std::string result;
 | 
						|
				for (int i = 0; i < mCurrentPassthrough+1; ++i)
 | 
						|
				{
 | 
						|
					// not using newlines here, otherwise the line numbers reported by compiler would be messed up..
 | 
						|
					if (Factory::getInstance().getCurrentLanguage () == Language_CG || Factory::getInstance().getCurrentLanguage () == Language_HLSL)
 | 
						|
						result += ", out float4 passthrough" + boost::lexical_cast<std::string>(i) + " : TEXCOORD" + boost::lexical_cast<std::string>(i);
 | 
						|
 | 
						|
					/*
 | 
						|
					else
 | 
						|
						result += "out vec4 passthrough" + boost::lexical_cast<std::string>(i) + "; ";
 | 
						|
						*/
 | 
						|
					else
 | 
						|
						result += "varying vec4 passthrough" + boost::lexical_cast<std::string>(i) + "; ";
 | 
						|
				}
 | 
						|
 | 
						|
				source.replace(pos, std::string("@shPassthroughVertexOutputs").length(), result);
 | 
						|
			}
 | 
						|
 | 
						|
			// passthrough fragment inputs
 | 
						|
			while (true)
 | 
						|
			{
 | 
						|
				pos = source.find("@shPassthroughFragmentInputs");
 | 
						|
				if (pos == std::string::npos)
 | 
						|
					break;
 | 
						|
 | 
						|
				std::string result;
 | 
						|
				for (int i = 0; i < mCurrentPassthrough+1; ++i)
 | 
						|
				{
 | 
						|
					// not using newlines here, otherwise the line numbers reported by compiler would be messed up..
 | 
						|
					if (Factory::getInstance().getCurrentLanguage () == Language_CG || Factory::getInstance().getCurrentLanguage () == Language_HLSL)
 | 
						|
						result += ", in float4 passthrough" + boost::lexical_cast<std::string>(i) + " : TEXCOORD" + boost::lexical_cast<std::string>(i);
 | 
						|
					/*
 | 
						|
					else
 | 
						|
						result += "in vec4 passthrough" + boost::lexical_cast<std::string>(i) + "; ";
 | 
						|
						*/
 | 
						|
					else
 | 
						|
						result += "varying vec4 passthrough" + boost::lexical_cast<std::string>(i) + "; ";
 | 
						|
				}
 | 
						|
 | 
						|
				source.replace(pos, std::string("@shPassthroughFragmentInputs").length(), result);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// save to cache _here_ - we want to preserve some macros
 | 
						|
		if (writeCache && !readCache)
 | 
						|
		{
 | 
						|
			std::ofstream of (std::string(Factory::getInstance ().getCacheFolder () + "/" + mName).c_str(), std::ios_base::out);
 | 
						|
			of.write(source.c_str(), source.size());
 | 
						|
			of.close();
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		// parse shared parameters
 | 
						|
		while (true)
 | 
						|
		{
 | 
						|
			pos = source.find("@shSharedParameter");
 | 
						|
			if (pos == std::string::npos)
 | 
						|
				break;
 | 
						|
 | 
						|
			std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
			assert(args.size());
 | 
						|
 | 
						|
			size_t end = source.find(")", pos);
 | 
						|
 | 
						|
			mSharedParameters.push_back(args[0]);
 | 
						|
 | 
						|
			source.erase(pos, (end+1)-pos);
 | 
						|
		}
 | 
						|
 | 
						|
		// parse auto constants
 | 
						|
		typedef std::map< std::string, std::pair<std::string, std::string> > AutoConstantMap;
 | 
						|
		AutoConstantMap autoConstants;
 | 
						|
		while (true)
 | 
						|
		{
 | 
						|
			pos = source.find("@shAutoConstant");
 | 
						|
			if (pos == std::string::npos)
 | 
						|
				break;
 | 
						|
 | 
						|
			std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
			assert(args.size() >= 2);
 | 
						|
 | 
						|
			size_t end = source.find(")", pos);
 | 
						|
 | 
						|
			std::string autoConstantName, uniformName;
 | 
						|
			std::string extraData;
 | 
						|
 | 
						|
			uniformName = args[0];
 | 
						|
			autoConstantName = args[1];
 | 
						|
			if (args.size() > 2)
 | 
						|
				extraData = args[2];
 | 
						|
 | 
						|
			autoConstants[uniformName] = std::make_pair(autoConstantName, extraData);
 | 
						|
 | 
						|
			source.erase(pos, (end+1)-pos);
 | 
						|
		}
 | 
						|
 | 
						|
		// parse uniform properties
 | 
						|
		while (true)
 | 
						|
		{
 | 
						|
			pos = source.find("@shUniformProperty");
 | 
						|
			if (pos == std::string::npos)
 | 
						|
				break;
 | 
						|
 | 
						|
			std::vector<std::string> args = extractMacroArguments (pos, source);
 | 
						|
			assert(args.size() == 2);
 | 
						|
 | 
						|
			size_t start = source.find("(", pos);
 | 
						|
			size_t end = source.find(")", pos);
 | 
						|
			std::string cmd = source.substr(pos, start-pos);
 | 
						|
 | 
						|
			ValueType vt;
 | 
						|
			if (cmd == "@shUniformProperty4f")
 | 
						|
				vt = VT_Vector4;
 | 
						|
			else if (cmd == "@shUniformProperty3f")
 | 
						|
				vt = VT_Vector3;
 | 
						|
			else if (cmd == "@shUniformProperty2f")
 | 
						|
				vt = VT_Vector2;
 | 
						|
			else if (cmd == "@shUniformProperty1f")
 | 
						|
				vt = VT_Float;
 | 
						|
			else if (cmd == "@shUniformPropertyInt")
 | 
						|
				vt = VT_Int;
 | 
						|
			else
 | 
						|
				throw std::runtime_error ("unsupported command \"" + cmd + "\"");
 | 
						|
 | 
						|
 | 
						|
			std::string propertyName, uniformName;
 | 
						|
			uniformName = args[0];
 | 
						|
			propertyName = args[1];
 | 
						|
			mUniformProperties[uniformName] = std::make_pair(propertyName, vt);
 | 
						|
 | 
						|
			source.erase(pos, (end+1)-pos);
 | 
						|
		}
 | 
						|
 | 
						|
		// parse texture samplers used
 | 
						|
		while (true)
 | 
						|
		{
 | 
						|
			pos = source.find("@shUseSampler");
 | 
						|
			if (pos == std::string::npos)
 | 
						|
				break;
 | 
						|
 | 
						|
			size_t end = source.find(")", pos);
 | 
						|
 | 
						|
			mUsedSamplers.push_back(extractMacroArguments (pos, source)[0]);
 | 
						|
			source.erase(pos, (end+1)-pos);
 | 
						|
		}
 | 
						|
 | 
						|
		// convert any left-over @'s to #
 | 
						|
		boost::algorithm::replace_all(source, "@", "#");
 | 
						|
 | 
						|
		Platform* platform = Factory::getInstance().getPlatform();
 | 
						|
 | 
						|
		std::string profile;
 | 
						|
		if (Factory::getInstance ().getCurrentLanguage () == Language_CG)
 | 
						|
			profile = mParent->getCgProfile ();
 | 
						|
		else if (Factory::getInstance ().getCurrentLanguage () == Language_HLSL)
 | 
						|
			profile = mParent->getHlslProfile ();
 | 
						|
 | 
						|
 | 
						|
		if (type == GPT_Vertex)
 | 
						|
			mProgram = boost::shared_ptr<GpuProgram>(platform->createGpuProgram(GPT_Vertex, "", mName, profile, source, Factory::getInstance().getCurrentLanguage()));
 | 
						|
		else if (type == GPT_Fragment)
 | 
						|
			mProgram = boost::shared_ptr<GpuProgram>(platform->createGpuProgram(GPT_Fragment, "", mName, profile, source, Factory::getInstance().getCurrentLanguage()));
 | 
						|
 | 
						|
 | 
						|
		if (Factory::getInstance ().getShaderDebugOutputEnabled ())
 | 
						|
			writeDebugFile(source, name);
 | 
						|
 | 
						|
		if (!mProgram->getSupported())
 | 
						|
		{
 | 
						|
			std::cerr << "        Full source code below: \n" << source << std::endl;
 | 
						|
			mSupported = false;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		// set auto constants
 | 
						|
		for (AutoConstantMap::iterator it = autoConstants.begin(); it != autoConstants.end(); ++it)
 | 
						|
		{
 | 
						|
			mProgram->setAutoConstant(it->first, it->second.first, it->second.second);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	std::string ShaderInstance::getName ()
 | 
						|
	{
 | 
						|
		return mName;
 | 
						|
	}
 | 
						|
 | 
						|
	bool ShaderInstance::getSupported () const
 | 
						|
	{
 | 
						|
		return mSupported;
 | 
						|
	}
 | 
						|
 | 
						|
	std::vector<std::string> ShaderInstance::getUsedSamplers()
 | 
						|
	{
 | 
						|
		return mUsedSamplers;
 | 
						|
	}
 | 
						|
 | 
						|
	void ShaderInstance::setUniformParameters (boost::shared_ptr<Pass> pass, PropertySetGet* properties)
 | 
						|
	{
 | 
						|
		for (UniformMap::iterator it = mUniformProperties.begin(); it != mUniformProperties.end(); ++it)
 | 
						|
		{
 | 
						|
			pass->setGpuConstant(mParent->getType(), it->first, it->second.second, properties->getProperty(it->second.first), properties->getContext());
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	std::vector<std::string> ShaderInstance::extractMacroArguments (size_t pos, const std::string& source)
 | 
						|
	{
 | 
						|
		size_t start = source.find("(", pos);
 | 
						|
		size_t end = source.find(")", pos);
 | 
						|
		std::string args = source.substr(start+1, end-(start+1));
 | 
						|
		std::vector<std::string> results;
 | 
						|
		boost::algorithm::split(results, args, boost::is_any_of(","));
 | 
						|
		std::for_each(results.begin(), results.end(),
 | 
						|
			boost::bind(&boost::trim<std::string>,
 | 
						|
			_1, std::locale() ));
 | 
						|
		return results;
 | 
						|
	}
 | 
						|
}
 |