code:
/*
 * The MIT License (MIT)
 * Copyright (c) 2018 Danijel Durakovic
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

///////////////////////////////////////////////////////////////////////////////
//
//  /mINI/ v0.9.10
//  An INI file reader and writer for the modern age.
//
///////////////////////////////////////////////////////////////////////////////
//
//  A tiny utility library for manipulating INI files with a straightforward
//  API and a minimal footprint. It conforms to the (somewhat) standard INI
//  format - sections and keys are case insensitive and all leading and
//  trailing whitespace is ignored. Comments are lines that begin with a
//  semicolon. Trailing comments are allowed on section lines.
//
//  Files are read on demand, upon which data is kept in memory and the file
//  is closed. This utility supports lazy writing, which only writes changes
//  and updates to a file and preserves custom formatting and comments. A lazy
//  write invoked by a write() call will read the output file, find what
//  changes have been made and update the file accordingly. If you only need to
//  generate files, use generate() instead. Section and key order is preserved
//  on read, write and insert.
//
///////////////////////////////////////////////////////////////////////////////
//
//  /* BASIC USAGE EXAMPLE: */
//
//  /* read from file */
//  mINI::INIFile file("myfile.ini");
//  mINI::INIStructure ini;
//  file.read(ini);
//
//  /* read value; gets a reference to actual value in the structure.
//     if key or section don't exist, a new empty value will be created */
//  std::string& value = ini["section"]["key"];
//
//  /* read value safely; gets a copy of value in the structure.
//     does not alter the structure */
//  std::string value = ini.get("section").get("key");
//
//  /* set or update values */
//  ini["section"]["key"] = "value";
//
//  /* set multiple values */
//  ini["section2"].set({
//      {"key1", "value1"},
//      {"key2", "value2"}
//  });
//
//  /* write updates back to file, preserving comments and formatting */
//  file.write(ini);
//
//  /* or generate a file (overwrites the original) */
//  file.generate(ini);
//
///////////////////////////////////////////////////////////////////////////////
//
//  Long live the INI file!!!
//
///////////////////////////////////////////////////////////////////////////////

#ifndef MINI_INI_H_
#define MINI_INI_H_

#include <string>
#include <sstream>
#include <algorithm>
#include <utility>
#include <unordered_map>
#include <vector>
#include <memory>
#include <fstream>
#include <sys/stat.h>
#include <cctype>

namespace mINI
{
	namespace INIStringUtil
	{
		const char* const whitespaceDelimiters = " \t\n\r\f\v";
		inline void trim(std::string& str)
		{
			str.erase(str.find_last_not_of(whitespaceDelimiters) + 1);
			str.erase(0, str.find_first_not_of(whitespaceDelimiters));
		}
#ifndef MINI_CASE_SENSITIVE
		inline void toLower(std::string& str)
		{
			std::transform(str.begin(), str.end(), str.begin(), [](const char c) {
				return static_cast<const char>(std::tolower(c));
			});
		}
#endif
		inline void replace(std::string& str, std::string const& a, std::string const& b)
		{
			if (!a.empty())
			{
				std::size_t pos = 0;
				while ((pos = str.find(a, pos)) != std::string::npos)
				{
					str.replace(pos, a.size(), b);
					pos += b.size();
				}
			}
		}
#ifdef _WIN32
		const char* const endl = "\r\n";
#else
		const char* const endl = "\n";
#endif
	};

	template<typename T>
	class INIMap
	{
	private:
		using T_DataIndexMap = std::unordered_map<std::string, std::size_t>;
		using T_DataItem = std::pair<std::string, T>;
		using T_DataContainer = std::vector<T_DataItem>;
		using T_MultiArgs = typename std::vector<std::pair<std::string, T>>;

		T_DataIndexMap dataIndexMap;
		T_DataContainer data;

		inline std::size_t setEmpty(std::string& key)
		{
			std::size_t index = data.size();
			dataIndexMap[key] = index;
			data.emplace_back(key, T());
			return index;
		}

	public:
		using const_iterator = typename T_DataContainer::const_iterator;

		INIMap() { }

		INIMap(INIMap const& other)
		{
			std::size_t data_size = other.data.size();
			for (std::size_t i = 0; i < data_size; ++i)
			{
				auto const& key = other.data[i].first;
				auto const& obj = other.data[i].second;
				data.emplace_back(key, obj);
			}
			dataIndexMap = T_DataIndexMap(other.dataIndexMap);
		}

		T& operator[](std::string key)
		{
			INIStringUtil::trim(key);
#ifndef MINI_CASE_SENSITIVE
			INIStringUtil::toLower(key);
#endif
			auto it = dataIndexMap.find(key);
			bool hasIt = (it != dataIndexMap.end());
			std::size_t index = (hasIt) ? it->second : setEmpty(key);
			return data[index].second;
		}
		T get(std::string key) const
		{
			INIStringUtil::trim(key);
#ifndef MINI_CASE_SENSITIVE
			INIStringUtil::toLower(key);
#endif
			auto it = dataIndexMap.find(key);
			if (it == dataIndexMap.end())
			{
				return T();
			}
			return T(data[it->second].second);
		}
		bool has(std::string key) const
		{
			INIStringUtil::trim(key);
#ifndef MINI_CASE_SENSITIVE
			INIStringUtil::toLower(key);
#endif
			return (dataIndexMap.count(key) == 1);
		}
		void set(std::string key, T obj)
		{
			INIStringUtil::trim(key);
#ifndef MINI_CASE_SENSITIVE
			INIStringUtil::toLower(key);
#endif
			auto it = dataIndexMap.find(key);
			if (it != dataIndexMap.end())
			{
				data[it->second].second = obj;
			}
			else
			{
				dataIndexMap[key] = data.size();
				data.emplace_back(key, obj);
			}
		}
		void set(T_MultiArgs const& multiArgs)
		{
			for (auto const& it : multiArgs)
			{
				auto const& key = it.first;
				auto const& obj = it.second;
				set(key, obj);
			}
		}
		bool remove(std::string key)
		{
			INIStringUtil::trim(key);
#ifndef MINI_CASE_SENSITIVE
			INIStringUtil::toLower(key);
#endif
			auto it = dataIndexMap.find(key);
			if (it != dataIndexMap.end())
			{
				std::size_t index = it->second;
				data.erase(data.begin() + index);
				dataIndexMap.erase(it);
				for (auto& it2 : dataIndexMap)
				{
					auto& vi = it2.second;
					if (vi > index)
					{
						vi--;
					}
				}
				return true;
			}
			return false;
		}
		void clear()
		{
			data.clear();
			dataIndexMap.clear();
		}
		std::size_t size() const
		{
			return data.size();
		}
		const_iterator begin() const { return data.begin(); }
		const_iterator end() const { return data.end(); }
	};

	using INIStructure = INIMap<INIMap<std::string>>;

	namespace INIParser
	{
		using T_ParseValues = std::pair<std::string, std::string>;

		enum class PDataType : char
		{
			PDATA_NONE,
			PDATA_COMMENT,
			PDATA_SECTION,
			PDATA_KEYVALUE,
			PDATA_UNKNOWN
		};

		inline PDataType parseLine(std::string line, T_ParseValues& parseData)
		{
			parseData.first.clear();
			parseData.second.clear();
			INIStringUtil::trim(line);
			if (line.empty())
			{
				return PDataType::PDATA_NONE;
			}
			char firstCharacter = line[0];
			if (firstCharacter == ';')
			{
				return PDataType::PDATA_COMMENT;
			}
			if (firstCharacter == '[')
			{
				auto commentAt = line.find_first_of(';');
				if (commentAt != std::string::npos)
				{
					line = line.substr(0, commentAt);
				}
				auto closingBracketAt = line.find_last_of(']');
				if (closingBracketAt != std::string::npos)
				{
					auto section = line.substr(1, closingBracketAt - 1);
					INIStringUtil::trim(section);
					parseData.first = section;
					return PDataType::PDATA_SECTION;
				}
			}
			auto lineNorm = line;
			INIStringUtil::replace(lineNorm, "\\=", "  ");
			auto equalsAt = lineNorm.find_first_of('=');
			if (equalsAt != std::string::npos)
			{
				auto key = line.substr(0, equalsAt);
				INIStringUtil::trim(key);
				INIStringUtil::replace(key, "\\=", "=");
				auto value = line.substr(equalsAt + 1);
				INIStringUtil::trim(value);
				parseData.first = key;
				parseData.second = value;
				return PDataType::PDATA_KEYVALUE;
			}
			return PDataType::PDATA_UNKNOWN;
		}
	};

	class INIReader
	{
	public:
		using T_LineData = std::vector<std::string>;
		using T_LineDataPtr = std::shared_ptr<T_LineData>;

	private:
		std::ifstream fileReadStream;
		T_LineDataPtr lineData;

		T_LineData readFile()
		{
			std::string fileContents;
			fileReadStream.seekg(0, std::ios::end);
			fileContents.resize(fileReadStream.tellg());
			fileReadStream.seekg(0, std::ios::beg);
			std::size_t fileSize = fileContents.size();
			fileReadStream.read(&fileContents[0], fileSize);
			fileReadStream.close();
			T_LineData output;
			if (fileSize == 0)
			{
				return output;
			}
			std::string buffer;
			buffer.reserve(50);
			for (std::size_t i = 0; i < fileSize; ++i)
			{
				char& c = fileContents[i];
				if (c == '\n')
				{
					output.emplace_back(buffer);
					buffer.clear();
					continue;
				}
				if (c != '\0' && c != '\r')
				{
					buffer += c;
				}
			}
			output.emplace_back(buffer);
			return output;
		}

	public:
		INIReader(std::string const& filename, bool keepLineData = false)
		{
			fileReadStream.open(filename, std::ios::in | std::ios::binary);
			if (keepLineData)
			{
				lineData = std::make_shared<T_LineData>();
			}
		}
		~INIReader() { }

		bool operator>>(INIStructure& data)
		{
			if (!fileReadStream.is_open())
			{
				return false;
			}
			T_LineData fileLines = readFile();
			std::string section;
			bool inSection = false;
			INIParser::T_ParseValues parseData;
			for (auto const& line : fileLines)
			{
				auto parseResult = INIParser::parseLine(line, parseData);
				if (parseResult == INIParser::PDataType::PDATA_SECTION)
				{
					inSection = true;
					data[section = parseData.first];
				}
				else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE)
				{
					auto const& key = parseData.first;
					auto const& value = parseData.second;
					data[section][key] = value;
				}
				if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN)
				{
					if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection)
					{
						continue;
					}
					lineData->emplace_back(line);
				}
			}
			return true;
		}
		T_LineDataPtr getLines()
		{
			return lineData;
		}
	};

	class INIGenerator
	{
	private:
		std::ofstream fileWriteStream;

	public:
		bool prettyPrint = false;

		INIGenerator(std::string const& filename)
		{
			fileWriteStream.open(filename, std::ios::out | std::ios::binary);
		}
		~INIGenerator() { }

		bool operator<<(INIStructure const& data)
		{
			if (!fileWriteStream.is_open())
			{
				return false;
			}
			if (!data.size())
			{
				return true;
			}
			auto it = data.begin();
			for (;;)
			{
				auto const& section = it->first;
				auto const& collection = it->second;
				fileWriteStream
					<< "["
					<< section
					<< "]";
				if (collection.size())
				{
					fileWriteStream << INIStringUtil::endl;
					auto it2 = collection.begin();
					for (;;)
					{
						auto key = it2->first;
						INIStringUtil::replace(key, "=", "\\=");
						auto value = it2->second;
						INIStringUtil::trim(value);
						fileWriteStream
							<< key
							<< ((prettyPrint) ? " = " : "=")
							<< value;
						if (++it2 == collection.end())
						{
							break;
						}
						fileWriteStream << INIStringUtil::endl;
					}
				}
				if (++it == data.end())
				{
					break;
				}
				fileWriteStream << INIStringUtil::endl;
				if (prettyPrint)
				{
					fileWriteStream << INIStringUtil::endl;
				}
			}
			return true;
		}
	};

	class INIWriter
	{
	private:
		using T_LineData = std::vector<std::string>;
		using T_LineDataPtr = std::shared_ptr<T_LineData>;

		std::string filename;

		T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original)
		{
			T_LineData output;
			INIParser::T_ParseValues parseData;
			std::string sectionCurrent;
			bool parsingSection = false;
			bool continueToNextSection = false;
			bool discardNextEmpty = false;
			bool writeNewKeys = false;
			std::size_t lastKeyLine = 0;
			for (auto line = lineData->begin(); line != lineData->end(); ++line)
			{
				if (!writeNewKeys)
				{
					auto parseResult = INIParser::parseLine(*line, parseData);
					if (parseResult == INIParser::PDataType::PDATA_SECTION)
					{
						if (parsingSection)
						{
							writeNewKeys = true;
							parsingSection = false;
							--line;
							continue;
						}
						sectionCurrent = parseData.first;
						if (data.has(sectionCurrent))
						{
							parsingSection = true;
							continueToNextSection = false;
							discardNextEmpty = false;
							output.emplace_back(*line);
							lastKeyLine = output.size();
						}
						else
						{
							continueToNextSection = true;
							discardNextEmpty = true;
							continue;
						}
					}
					else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE)
					{
						if (continueToNextSection)
						{
							continue;
						}
						if (data.has(sectionCurrent))
						{
							auto& collection = data[sectionCurrent];
							auto const& key = parseData.first;
							auto const& value = parseData.second;
							if (collection.has(key))
							{
								auto outputValue = collection[key];
								if (value == outputValue)
								{
									output.emplace_back(*line);
								}
								else
								{
									INIStringUtil::trim(outputValue);
									auto lineNorm = *line;
									INIStringUtil::replace(lineNorm, "\\=", "  ");
									auto equalsAt = lineNorm.find_first_of('=');
									auto valueAt = lineNorm.find_first_not_of(
										INIStringUtil::whitespaceDelimiters,
										equalsAt + 1
									);
									std::string outputLine = line->substr(0, valueAt);
									if (prettyPrint && equalsAt + 1 == valueAt)
									{
										outputLine += " ";
									}
									outputLine += outputValue;
									output.emplace_back(outputLine);
								}
								lastKeyLine = output.size();
							}
						}
					}
					else
					{
						if (discardNextEmpty && line->empty())
						{
							discardNextEmpty = false;
						}
						else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN)
						{
							output.emplace_back(*line);
						}
					}
				}
				if (writeNewKeys || std::next(line) == lineData->end())
				{
					T_LineData linesToAdd;
					if (data.has(sectionCurrent) && original.has(sectionCurrent))
					{
						auto const& collection = data[sectionCurrent];
						auto const& collectionOriginal = original[sectionCurrent];
						for (auto const& it : collection)
						{
							auto key = it.first;
							if (collectionOriginal.has(key))
							{
								continue;
							}
							auto value = it.second;
							INIStringUtil::replace(key, "=", "\\=");
							INIStringUtil::trim(value);
							linesToAdd.emplace_back(
								key + ((prettyPrint) ? " = " : "=") + value
							);
						}
					}
					if (!linesToAdd.empty())
					{
						output.insert(
							output.begin() + lastKeyLine,
							linesToAdd.begin(),
							linesToAdd.end()
						);
					}
					if (writeNewKeys)
					{
						writeNewKeys = false;
						--line;
					}
				}
			}
			for (auto const& it : data)
			{
				auto const& section = it.first;
				if (original.has(section))
				{
					continue;
				}
				if (prettyPrint && output.size() > 0 && !output.back().empty())
				{
					output.emplace_back();
				}
				output.emplace_back("[" + section + "]");
				auto const& collection = it.second;
				for (auto const& it2 : collection)
				{
					auto key = it2.first;
					auto value = it2.second;
					INIStringUtil::replace(key, "=", "\\=");
					INIStringUtil::trim(value);
					output.emplace_back(
						key + ((prettyPrint) ? " = " : "=") + value
					);
				}
			}
			return output;
		}

	public:
		bool prettyPrint = false;

		INIWriter(std::string const& filename)
		: filename(filename)
		{
		}
		~INIWriter() { }

		bool operator<<(INIStructure& data)
		{
			struct stat buf;
			bool fileExists = (stat(filename.c_str(), &buf) == 0);
			if (!fileExists)
			{
				INIGenerator generator(filename);
				generator.prettyPrint = prettyPrint;
				return generator << data;
			}
			INIStructure originalData;
			T_LineDataPtr lineData;
			bool readSuccess = false;
			{
				INIReader reader(filename, true);
				if ((readSuccess = reader >> originalData))
				{
					lineData = reader.getLines();
				}
			}
			if (!readSuccess)
			{
				return false;
			}
			T_LineData output = getLazyOutput(lineData, data, originalData);
			std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary);
			if (fileWriteStream.is_open())
			{
				if (output.size())
				{
					auto line = output.begin();
					for (;;)
					{
						fileWriteStream << *line;
						if (++line == output.end())
						{
							break;
						}
						fileWriteStream << INIStringUtil::endl;
					}
				}
				return true;
			}
			return false;
		}
	};

	class INIFile
	{
	private:
		std::string filename;

	public:
		INIFile(std::string const& filename)
		: filename(filename)
		{ }

		~INIFile() { }

		bool read(INIStructure& data) const
		{
			if (data.size())
			{
				data.clear();
			}
			if (filename.empty())
			{
				return false;
			}
			INIReader reader(filename);
			return reader >> data;
		}
		bool generate(INIStructure const& data, bool pretty = false) const
		{
			if (filename.empty())
			{
				return false;
			}
			INIGenerator generator(filename);
			generator.prettyPrint = pretty;
			return generator << data;
		}
		bool write(INIStructure& data, bool pretty = false) const
		{
			if (filename.empty())
			{
				return false;
			}
			INIWriter writer(filename);
			writer.prettyPrint = pretty;
			return writer << data;
		}
	};
}

#endif // MINI_INI_H_


code:
#define WITHD3D
#include "common.h"

#ifdef EXTENDED_COLOURFILTER

#ifndef LIBRW
#error "Need librw for EXTENDED_COLOURFILTER"
#endif

#include "main.h"
#include "RwHelper.h"
#include "Camera.h"
#include "MBlur.h"
#include "postfx.h"

RwRaster *CPostFX::pFrontBuffer;
RwRaster *CPostFX::pBackBuffer;
bool CPostFX::bJustInitialised;
int CPostFX::EffectSwitch = POSTFX_NORMAL;
bool CPostFX::MotionBlurOn = false;

static RwIm2DVertex Vertex[4];
static RwIm2DVertex Vertex2[4];
static RwImVertexIndex Index[6] = { 0, 1, 2, 0, 2, 3 };

#ifdef RW_D3D9
void *colourfilterIII_PS;
void *contrast_PS;
#endif
#ifdef RW_OPENGL
int32 u_blurcolor;
int32 u_contrastAdd;
int32 u_contrastMult;
rw::gl3::Shader *colourFilterIII;
rw::gl3::Shader *contrast;
#endif

void
CPostFX::InitOnce(void)
{
#ifdef RW_OPENGL
	u_blurcolor = rw::gl3::registerUniform("u_blurcolor");
	u_contrastAdd = rw::gl3::registerUniform("u_contrastAdd");
	u_contrastMult = rw::gl3::registerUniform("u_contrastMult");
#endif
}

void
CPostFX::Open(RwCamera *cam)
{
	if(pFrontBuffer)
		Close();

	uint32 width  = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1);
	uint32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1);
	uint32 depth  = RwRasterGetDepth(RwCameraGetRaster(cam));
	pFrontBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE);
	pBackBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE);
	bJustInitialised = true;

	float zero, xmax, ymax;

	if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){
		zero = HALFPX;
		xmax = width + HALFPX;
		ymax = height + HALFPX;
	}else{
		zero = -HALFPX;
		xmax = width - HALFPX;
		ymax = height - HALFPX;
	}

	RwIm2DVertexSetScreenX(&Vertex[0], zero);
	RwIm2DVertexSetScreenY(&Vertex[0], zero);
	RwIm2DVertexSetScreenZ(&Vertex[0], RwIm2DGetNearScreenZ());
	RwIm2DVertexSetCameraZ(&Vertex[0], RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetRecipCameraZ(&Vertex[0], 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetU(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetV(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255);

	RwIm2DVertexSetScreenX(&Vertex[1], zero);
	RwIm2DVertexSetScreenY(&Vertex[1], ymax);
	RwIm2DVertexSetScreenZ(&Vertex[1], RwIm2DGetNearScreenZ());
	RwIm2DVertexSetCameraZ(&Vertex[1], RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetRecipCameraZ(&Vertex[1], 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetU(&Vertex[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetV(&Vertex[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255);

	RwIm2DVertexSetScreenX(&Vertex[2], xmax);
	RwIm2DVertexSetScreenY(&Vertex[2], ymax);
	RwIm2DVertexSetScreenZ(&Vertex[2], RwIm2DGetNearScreenZ());
	RwIm2DVertexSetCameraZ(&Vertex[2], RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetRecipCameraZ(&Vertex[2], 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetU(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetV(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255);

	RwIm2DVertexSetScreenX(&Vertex[3], xmax);
	RwIm2DVertexSetScreenY(&Vertex[3], zero);
	RwIm2DVertexSetScreenZ(&Vertex[3], RwIm2DGetNearScreenZ());
	RwIm2DVertexSetCameraZ(&Vertex[3], RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetRecipCameraZ(&Vertex[3], 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetU(&Vertex[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetV(&Vertex[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255);

	RwIm2DVertexSetScreenX(&Vertex2[0], zero + 2.0f);
	RwIm2DVertexSetScreenY(&Vertex2[0], zero + 2.0f);
	RwIm2DVertexSetScreenZ(&Vertex2[0], RwIm2DGetNearScreenZ());
	RwIm2DVertexSetCameraZ(&Vertex2[0], RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetRecipCameraZ(&Vertex2[0], 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetU(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetV(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetIntRGBA(&Vertex2[0], 255, 255, 255, 255);

	RwIm2DVertexSetScreenX(&Vertex2[1], 2.0f);
	RwIm2DVertexSetScreenY(&Vertex2[1], ymax + 2.0f);
	RwIm2DVertexSetScreenZ(&Vertex2[1], RwIm2DGetNearScreenZ());
	RwIm2DVertexSetCameraZ(&Vertex2[1], RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetRecipCameraZ(&Vertex2[1], 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetU(&Vertex2[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetV(&Vertex2[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetIntRGBA(&Vertex2[1], 255, 255, 255, 255);

	RwIm2DVertexSetScreenX(&Vertex2[2], xmax + 2.0f);
	RwIm2DVertexSetScreenY(&Vertex2[2], ymax + 2.0f);
	RwIm2DVertexSetScreenZ(&Vertex2[2], RwIm2DGetNearScreenZ());
	RwIm2DVertexSetCameraZ(&Vertex2[2], RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetRecipCameraZ(&Vertex2[2], 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetU(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetV(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetIntRGBA(&Vertex2[2], 255, 255, 255, 255);

	RwIm2DVertexSetScreenX(&Vertex2[3], xmax + 2.0f);
	RwIm2DVertexSetScreenY(&Vertex2[3], zero + 2.0f);
	RwIm2DVertexSetScreenZ(&Vertex2[3], RwIm2DGetNearScreenZ());
	RwIm2DVertexSetCameraZ(&Vertex2[3], RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetRecipCameraZ(&Vertex2[3], 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetU(&Vertex2[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetV(&Vertex2[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
	RwIm2DVertexSetIntRGBA(&Vertex2[3], 255, 255, 255, 255);

#ifdef RW_D3D9
#include "shaders/obj/colourfilterIII_PS.inc"
	colourfilterIII_PS = rw::d3d::createPixelShader(colourfilterIII_PS_cso);
#include "shaders/obj/contrastPS.inc"
	contrast_PS = rw::d3d::createPixelShader(contrastPS_cso);
#endif
#ifdef RW_OPENGL
	using namespace rw::gl3;
	{
#include "shaders/obj/im2d_vert.inc"
#include "shaders/obj/colourfilterIII_frag.inc"
	const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil };
	const char *fs[] = { shaderDecl, header_frag_src, colourfilterIII_frag_src, nil };
	colourFilterIII = Shader::create(vs, fs);
	assert(colourFilterIII);
	}

	{
#include "shaders/obj/im2d_vert.inc"
#include "shaders/obj/contrast_frag.inc"
	const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil };
	const char *fs[] = { shaderDecl, header_frag_src, contrast_frag_src, nil };
	contrast = Shader::create(vs, fs);
	assert(contrast);
	}

#endif
}

void
CPostFX::Close(void)
{
	if(pFrontBuffer){
		RwRasterDestroy(pFrontBuffer);
		pFrontBuffer = nil;
	}
	if(pBackBuffer){
		RwRasterDestroy(pBackBuffer);
		pBackBuffer = nil;
	}
#ifdef RW_D3D9
	if(colourfilterIII_PS){
		rw::d3d::destroyPixelShader(colourfilterIII_PS);
		colourfilterIII_PS = nil;
	}
	if(contrast_PS){
		rw::d3d::destroyPixelShader(contrast_PS);
		contrast_PS = nil;
	}
#endif
#ifdef RW_OPENGL
	if(colourFilterIII){
		colourFilterIII->destroy();
		colourFilterIII = nil;
	}
	if(contrast){
		contrast->destroy();
		contrast = nil;
	}
#endif
}

void
CPostFX::RenderOverlayBlur(RwCamera *cam, int32 r, int32 g, int32 b, int32 a)
{
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);

	RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a);
	RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a);
	RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a);
	RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a);

	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);

	RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
}

void
CPostFX::RenderOverlaySimple(RwCamera *cam, int32 r, int32 g, int32 b, int32 a)
{
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);

	r *= 0.6f;
	g *= 0.6f;
	b *= 0.6f;
	a *= 0.6f;

	RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a);
	RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a);
	RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a);
	RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a);

	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);

	RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
}

void
CPostFX::RenderOverlaySniper(RwCamera *cam, int32 r, int32 g, int32 b, int32 a)
{
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);

	RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, 80);
	RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, 80);
	RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, 80);
	RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, 80);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);

	RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
}

float CPostFX::Intensity = 1.0f;

void
CPostFX::RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a)
{
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pBackBuffer);

	if(EffectSwitch == POSTFX_MOBILE){
		float mult[3], add[3];
		mult[0] = (r-64)/384.0f + 1.14f;
		mult[1] = (g-64)/384.0f + 1.14f;
		mult[2] = (b-64)/384.0f + 1.14f;
		add[0] = r/1536.f;
		add[1] = g/1536.f;
		add[2] = b/1536.f;
#ifdef RW_D3D9
		rw::d3d::d3ddevice->SetPixelShaderConstantF(10, mult, 1);
		rw::d3d::d3ddevice->SetPixelShaderConstantF(11, add, 1);

		rw::d3d::im2dOverridePS = contrast_PS;
#endif
#ifdef RW_OPENGL
		rw::gl3::im2dOverrideShader = contrast;
		contrast->use();
		glUniform3fv(contrast->uniformLocations[u_contrastMult], 1, mult);
		glUniform3fv(contrast->uniformLocations[u_contrastAdd], 1, add);
#endif
	}else{
		float f = Intensity;
		float blurcolors[4];
		blurcolors[0] = r/255.0f;
		blurcolors[1] = g/255.0f;
		blurcolors[2] = b/255.0f;
		blurcolors[3] = a*f/255.0f;
#ifdef RW_D3D9
		rw::d3d::d3ddevice->SetPixelShaderConstantF(10, blurcolors, 1);
		rw::d3d::im2dOverridePS = colourfilterIII_PS;
#endif
#ifdef RW_OPENGL
		rw::gl3::im2dOverrideShader = colourFilterIII;
		colourFilterIII->use();
		glUniform4fv(colourFilterIII->uniformLocations[u_blurcolor], 1, blurcolors);
#endif
	}
	RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
#ifdef RW_D3D9
	rw::d3d::im2dOverridePS = nil;
#endif
#ifdef RW_OPENGL
	rw::gl3::im2dOverrideShader = nil;
#endif
}

void
CPostFX::RenderMotionBlur(RwCamera *cam, uint32 blur)
{
	if(blur == 0)
		return;

	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);

	RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, blur);
	RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, blur);
	RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, blur);
	RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, blur);

	RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
}

bool
CPostFX::NeedBackBuffer(void)
{
	// Current frame -- needed for non-blur effect
	switch(EffectSwitch){
	case POSTFX_OFF:
		// no actual rendering here
		return false;
	case POSTFX_SIMPLE:
		return false;
	case POSTFX_NORMAL:
		if(MotionBlurOn)
			return false;
		else
			return true;
	case POSTFX_MOBILE:
		return true;
	}
	return false;
}

bool
CPostFX::NeedFrontBuffer(int32 type)
{
	// Last frame -- needed for motion blur
	if(MotionBlurOn)
		return true;
	if(type == MOTION_BLUR_SNIPER)
		return true;

	return false;
}

void
CPostFX::GetBackBuffer(RwCamera *cam)
{
	RwRasterPushContext(pBackBuffer);
	RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0);
	RwRasterPopContext();
}

void
CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha)
{
	switch(type)
	{
	case MOTION_BLUR_SECURITY_CAM:
		red = 0;
		green = 255;
		blue = 0;
		blur = 128;
		break;
	case MOTION_BLUR_INTRO:
		red = 100;
		green = 220;
		blue = 230;
		blur = 158;
		break;
	case MOTION_BLUR_INTRO2:
		red = 80;
		green = 255;
		blue = 230;
		blur = 138;
		break;
	case MOTION_BLUR_INTRO3:
		red = 255;
		green = 60;
		blue = 60;
		blur = 200;
		break;
	case MOTION_BLUR_INTRO4:
		red = 255;
		green = 180;
		blue = 180;
		blur = 128;
		break;
	}

	PUSH_RENDERGROUP("CPostFX::Render");
	if(pFrontBuffer == nil)
		Open(cam);
	assert(pFrontBuffer);
	assert(pBackBuffer);

	if(NeedBackBuffer())
		GetBackBuffer(cam);

	DefinedState();

	RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);

	if(type == MOTION_BLUR_SNIPER){
		if(!bJustInitialised)
			RenderOverlaySniper(cam, red, green, blue, blur);
	}else switch(EffectSwitch){
	case POSTFX_OFF:
		// no actual rendering here
		break;
	case POSTFX_SIMPLE:
		RenderOverlaySimple(cam, red, green, blue, blur);
		break;
	case POSTFX_NORMAL:
		if(MotionBlurOn){
			if(!bJustInitialised)
				RenderOverlayBlur(cam, red, green, blue, blur);
		}else{
			RenderOverlayShader(cam, red, green, blue, blur);
		}
		break;
	case POSTFX_MOBILE:
		RenderOverlayShader(cam, red, green, blue, blur);
		break;
	}

	// TODO? maybe we want this even without motion blur on sometimes?
	if(MotionBlurOn)
		if(!bJustInitialised)
			RenderMotionBlur(cam, bluralpha);

	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);

	if(NeedFrontBuffer(type)){
		RwRasterPushContext(pFrontBuffer);
		RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0);
		RwRasterPopContext();
		bJustInitialised = false;
	}else
		bJustInitialised = true;

	POP_RENDERGROUP();
}

#endif

license: mit


code:
#pragma once

#ifdef EXTENDED_COLOURFILTER

class CPostFX
{
public:
	enum {
		POSTFX_OFF,
		POSTFX_SIMPLE,
		POSTFX_NORMAL,
		POSTFX_MOBILE
	};
	static RwRaster *pFrontBuffer;
	static RwRaster *pBackBuffer;
	static bool bJustInitialised;
	static int EffectSwitch;
	static bool MotionBlurOn;	// or use CMblur for that?
	static float Intensity;

	static void InitOnce(void);
	static void Open(RwCamera *cam);
	static void Close(void);
	static void RenderOverlayBlur(RwCamera *cam, int32 r, int32 g, int32 b, int32 a);
	static void RenderOverlaySimple(RwCamera *cam, int32 r, int32 g, int32 b, int32 a);
	static void RenderOverlaySniper(RwCamera *cam, int32 r, int32 g, int32 b, int32 a);
	static void RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a);
	static void RenderMotionBlur(RwCamera *cam, uint32 blur);
	static void Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha);
	static bool NeedBackBuffer(void);
	static bool NeedFrontBuffer(int32 type);
	static void GetBackBuffer(RwCamera *cam);
	static bool UseBlurColours(void) { return EffectSwitch != POSTFX_SIMPLE; }
};

#endif


code:
#define PRId8     "hhd"
#define PRId16    "hd"
#define PRId32    "ld"
#define PRId64    "lld"

#define PRIdFAST8    "hhd"
#define PRIdFAST16    "hd"
#define PRIdFAST32    "ld"
#define PRIdFAST64   "lld"

#define PRIdLEAST8   "hhd"
#define PRIdLEAST16  "hd"
#define PRIdLEAST32  "ld"
#define PRIdLEAST64  "lld"

#define PRIdMAX   "lld"
#define PRIdPTR   "lld"

#define PRIi8     "hhi"
#define PRIi16    "hi"
#define PRIi32    "li"
#define PRIi64    "lli"

#define PRIiFAST8    "hhi"
#define PRIiFAST16    "hi"
#define PRIiFAST32    "li"
#define PRIiFAST64    "lli"

#define PRIiLEAST8   "hhi"
#define PRIiLEAST16  "hi"
#define PRIiLEAST32  "li"
#define PRIiLEAST64  "lli"

#define PRIiMAX   "lli"
#define PRIiPTR   "lli"

#define PRIo8     "hho"
#define PRIo16    "ho"
#define PRIo32    "lo"
#define PRIo64    "llo"

#define PRIoFAST8    "hho"
#define PRIoFAST16    "ho"
#define PRIoFAST32    "lo"
#define PRIoFAST64   "llo"

#define PRIoLEAST8   "hho"
#define PRIoLEAST16  "ho"
#define PRIoLEAST32  "lo"
#define PRIoLEAST64  "llo"

#define PRIoMAX   "llo"
#define PRIoPTR   "llo"

#define PRIu8     "hhu"
#define PRIu16    "hu"
#define PRIu32    "lu"
#define PRIu64    "llu"

#define PRIuFAST8    "hhu"
#define PRIuFAST16    "hu"
#define PRIuFAST32    "lu"
#define PRIuFAST64   "llu"

#define PRIuLEAST8   "hhu"
#define PRIuLEAST16  "hu"
#define PRIuLEAST32  "lu"
#define PRIuLEAST64  "llu"

#define PRIuMAX   "llu"
#define PRIuPTR   "llu"

#define PRIx8     "hhx"
#define PRIx16    "hx"
#define PRIx32    "lx"
#define PRIx64    "llx"

#define PRIxFAST8    "hhx"
#define PRIxFAST16    "hx"
#define PRIxFAST32    "lx"
#define PRIxFAST64   "llx"

#define PRIxLEAST8   "hhx"
#define PRIxLEAST16  "hx"
#define PRIxLEAST32  "lx"
#define PRIxLEAST64  "llx"

#define PRIxMAX   "llx"
#define PRIxPTR   "llx"

#define PRIX8     "hhX"
#define PRIX16    "hX"
#define PRIX32    "lX"
#define PRIX64    "llX"

#define PRIXFAST8    "hhX"
#define PRIXFAST16    "hX"
#define PRIXFAST32    "lX"
#define PRIXFAST64   "llX"

#define PRIXLEAST8   "hhX"
#define PRIXLEAST16  "hX"
#define PRIXLEAST32  "lX"
#define PRIXLEAST64  "llX"

#define PRIXMAX   "llX"
#define PRIXPTR   "llX"

        /* SCAN FORMAT MACROS */
#define SCNd8     "hhd"
#define SCNd16    "hd"
#define SCNd32    "ld"
#define SCNd64    "lld"

#define SCNdFAST8    "hhd"
#define SCNdFAST16    "hd"
#define SCNdFAST32    "ld"
#define SCNdFAST64   "lld"

#define SCNdLEAST8   "hhd"
#define SCNdLEAST16  "hd"
#define SCNdLEAST32  "ld"
#define SCNdLEAST64  "lld"

#define SCNdMAX   "lld"
#define SCNdPTR   "lld"

#define SCNi8     "hhi"
#define SCNi16    "hi"
#define SCNi32    "li"
#define SCNi64    "lli"

#define SCNiFAST8    "hhi"
#define SCNiFAST16    "hi"
#define SCNiFAST32    "li"
#define SCNiFAST64   "lli"

#define SCNiLEAST8   "hhi"
#define SCNiLEAST16  "hi"
#define SCNiLEAST32  "li"
#define SCNiLEAST64  "lli"

#define SCNiMAX   "lli"
#define SCNiPTR   "lli"

#define SCNo8     "hho"
#define SCNo16    "ho"
#define SCNo32    "lo"
#define SCNo64    "llo"

#define SCNoFAST8    "hho"
#define SCNoFAST16    "ho"
#define SCNoFAST32    "lo"
#define SCNoFAST64   "llo"

#define SCNoLEAST8   "hho"
#define SCNoLEAST16  "ho"
#define SCNoLEAST32  "lo"
#define SCNoLEAST64  "llo"

#define SCNoMAX   "llo"
#define SCNoPTR   "llo"

#define SCNu8     "hhu"
#define SCNu16    "hu"
#define SCNu32    "lu"
#define SCNu64    "llu"

#define SCNuFAST8    "hhu"
#define SCNuFAST16    "hu"
#define SCNuFAST32    "lu"
#define SCNuFAST64   "llu"

#define SCNuLEAST8   "hhu"
#define SCNuLEAST16  "hu"
#define SCNuLEAST32  "lu"
#define SCNuLEAST64  "llu"

#define SCNuMAX   "llu"
#define SCNuPTR   "llu"

#define SCNx8     "hhx"
#define SCNx16    "hx"
#define SCNx32    "lx"
#define SCNx64    "llx"

#define SCNxFAST8    "hhx"
#define SCNxFAST16    "hx"
#define SCNxFAST32    "lx"
#define SCNxFAST64   "llx"

#define SCNxLEAST8   "hhx"
#define SCNxLEAST16  "hx"
#define SCNxLEAST32  "lx"
#define SCNxLEAST64  "llx"

#define SCNxMAX   "llx"
#define SCNxPTR   "llx"

#define SCNX8     "hhX"
#define SCNX16    "hX"
#define SCNX32    "lX"
#define SCNX64    "llX"

#define SCNXFAST8    "hhX"
#define SCNXFAST16    "hX"
#define SCNXFAST32    "lX"
#define SCNXFAST64   "llX"

#define SCNXLEAST8   "hhX"
#define SCNXLEAST16  "hX"
#define SCNXLEAST32  "lX"
#define SCNXLEAST64  "llX"

#define SCNXMAX   "llX"
#define SCNXPTR   "llX"


code:
#define WITHD3D
#include "common.h"

#ifdef SCREEN_DROPLETS

#ifndef LIBRW
#error "Need librw for SCREEN_DROPLETS"
#endif

#include "General.h"
#include "main.h"
#include "RwHelper.h"
#include "Timer.h"
#include "Camera.h"
#include "World.h"
#include "ZoneCull.h"
#include "Weather.h"
#include "ParticleObject.h"
	#include "Pad.h"
#include "RenderBuffer.h"
#include "custompipes.h"
#include "postfx.h"
#include "screendroplets.h"

// for 640
#define MAXSIZE 15
#define MINSIZE 4

int ScreenDroplets::ms_initialised;
RwTexture *ScreenDroplets::ms_maskTex;
RwTexture *ScreenDroplets::ms_screenTex;

bool ScreenDroplets::ms_enabled = true;
bool ScreenDroplets::ms_movingEnabled = true;

ScreenDroplets::ScreenDrop ScreenDroplets::ms_drops[ScreenDroplets::MAXDROPS];
int ScreenDroplets::ms_numDrops;
ScreenDroplets::ScreenDropMoving ScreenDroplets::ms_dropsMoving[ScreenDroplets::MAXDROPSMOVING];
int ScreenDroplets::ms_numDropsMoving;

CVector ScreenDroplets::ms_prevCamUp;
CVector ScreenDroplets::ms_prevCamPos;
CVector ScreenDroplets::ms_camMoveDelta;
float ScreenDroplets::ms_camMoveDist;
CVector ScreenDroplets::ms_screenMoveDelta;
float ScreenDroplets::ms_screenMoveDist;
float ScreenDroplets::ms_camUpAngle;

int ScreenDroplets::ms_splashDuration;
CParticleObject *ScreenDroplets::ms_splashObject;

struct Im2DVertexUV2 : rw::RWDEVICE::Im2DVertex
{
	rw::float32 u2, v2;
};

#ifdef RW_D3D9
static void *screenDroplet_PS;
#endif
#ifdef RW_GL3
static rw::gl3::Shader *screenDroplet;
#endif

// platform specific
static void openim2d_uv2(void);
static void closeim2d_uv2(void);
static void RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices);

static Im2DVertexUV2 VertexBuffer[TEMPBUFFERVERTSIZE];

void
ScreenDroplets::Initialise(void)
{
	Clear();
	ms_splashDuration = -1;
	ms_splashObject = nil;
}

// Create white circle mask for rain drops
static RwTexture*
CreateDropMask(int32 size)
{
	RwImage *img = RwImageCreate(size, size, 32);
	RwImageAllocatePixels(img);

	uint8 *pixels = RwImageGetPixels(img);
	int32 stride = RwImageGetStride(img);

	for(int y = 0; y < size; y++){
		float yf = ((y + 0.5f)/size - 0.5f)*2.0f;
		for(int x = 0; x < size; x++){
			float xf = ((x + 0.5f)/size - 0.5f)*2.0f;
			memset(&pixels[y*stride + x*4], xf*xf + yf*yf < 1.0f ? 0xFF : 0x00, 4);
		}
	}

	int32 width, height, depth, format;
	RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &width, &height, &depth, &format);
	RwRaster *ras = RwRasterCreate(width, height, depth, format);
	RwRasterSetFromImage(ras, img);
	RwImageDestroy(img);
	return RwTextureCreate(ras);
}

void
ScreenDroplets::InitDraw(void)
{
	ms_maskTex = CreateDropMask(64);

	ms_screenTex = RwTextureCreate(nil);
	RwTextureSetFilterMode(ms_screenTex, rwFILTERLINEAR);

	openim2d_uv2();
#ifdef RW_D3D9
#include "shaders/obj/screenDroplet_PS.inc"
	screenDroplet_PS = rw::d3d::createPixelShader(screenDroplet_PS_cso);
#endif
#ifdef RW_GL3
	using namespace rw::gl3;
	{
#include "shaders/obj/im2d_UV2_vert.inc"
#include "shaders/obj/screenDroplet_frag.inc"
	const char *vs[] = { shaderDecl, header_vert_src, im2d_UV2_vert_src, nil };
	const char *fs[] = { shaderDecl, header_frag_src, screenDroplet_frag_src, nil };
	screenDroplet = Shader::create(vs, fs);
	assert(screenDroplet);
	}
#endif

	ms_initialised = 1;
}

void
ScreenDroplets::Shutdown(void)
{
	if(ms_maskTex){
		RwTextureDestroy(ms_maskTex);
		ms_maskTex = nil;
	}
	if(ms_screenTex){
		RwTextureSetRaster(ms_screenTex, nil);
		RwTextureDestroy(ms_screenTex);
		ms_screenTex = nil;
	}
#ifdef RW_D3D9
	if(screenDroplet_PS){
		rw::d3d::destroyPixelShader(screenDroplet_PS);
		screenDroplet_PS = nil;
	}
#endif
#ifdef RW_GL3
	if(screenDroplet){
		screenDroplet->destroy();
		screenDroplet = nil;
	}
#endif

	closeim2d_uv2();
}

void
ScreenDroplets::Process(void)
{
	ProcessCameraMovement();
	SprayDrops();
	ProcessMoving();
	Fade();
}

static void
FlushBuffer(void)
{
	if(TempBufferIndicesStored){
		RenderIndexedPrimitive_UV2(rwPRIMTYPETRILIST,
			VertexBuffer, TempBufferVerticesStored,
			TempBufferRenderIndexList, TempBufferIndicesStored);
		TempBufferVerticesStored = 0;
		TempBufferIndicesStored = 0;
	}
}

static int
StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, Im2DVertexUV2 **vertexStart)
{
	if(TempBufferIndicesStored + numIndices >= TEMPBUFFERINDEXSIZE ||
	   TempBufferVerticesStored + numVertices >= TEMPBUFFERVERTSIZE)
		FlushBuffer();
        *indexStart = &TempBufferRenderIndexList[TempBufferIndicesStored];
        *vertexStart = &VertexBuffer[TempBufferVerticesStored];
	int vertOffset = TempBufferVerticesStored;
        TempBufferIndicesStored += numIndices;
        TempBufferVerticesStored += numVertices;
	return vertOffset;
}

void
ScreenDroplets::Render(void)
{
	ScreenDrop *drop;

	DefinedState();
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(ms_maskTex));
	RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, FALSE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);

	RwTextureSetRaster(ms_screenTex, CPostFX::pBackBuffer);
#ifdef RW_D3D9
	rw::d3d::im2dOverridePS = screenDroplet_PS;
	rw::d3d::setTexture(1, ms_screenTex);
#endif
#ifdef RW_GL3
	rw::gl3::im2dOverrideShader = screenDroplet;
	rw::gl3::setTexture(1, ms_screenTex);
#endif

	RenderBuffer::ClearRenderBuffer();
	for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
		if(drop->active)
			AddToRenderList(drop);
	FlushBuffer();

#ifdef RW_D3D9
	rw::d3d::im2dOverridePS = nil;
	rw::d3d::setTexture(1, nil);
#endif
#ifdef RW_GL3
	rw::gl3::im2dOverrideShader = nil;
	rw::gl3::setTexture(1, nil);
#endif

	RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, FALSE);
}

void
ScreenDroplets::AddToRenderList(ScreenDroplets::ScreenDrop *drop)
{
	static float xy[] = {
		-1.0f, -1.0f,
		-1.0f,  1.0f,
		1.0f,  1.0f,
		1.0f, -1.0f
	};
	static float uv[] = {
		0.0f, 0.0f,
		0.0f, 1.0f,
		1.0f, 1.0f,
		1.0f, 0.0f
	};

	int i;
	RwImVertexIndex *indices;
	Im2DVertexUV2 *verts;
	int first = StartStoring(6, 4, &indices, &verts);

	float scale = 0.5f*SCREEN_SCALE_X(drop->size);

	float screenz = RwIm2DGetNearScreenZ();
	float z = RwCameraGetNearClipPlane(Scene.camera);
	float recipz = 1.0f/z;

	float magSize = SCREEN_SCALE_Y(drop->magnification*(300.0f-40.0f) + 40.0f);
	float ul = drop->x - magSize;
	float vt = drop->y - magSize;
	float ur = drop->x + magSize;
	float vb = drop->y + magSize;
	ul = Max(ul, 0.0f)/RwRasterGetWidth(CPostFX::pBackBuffer);
	vt = Max(vt, 0.0f)/RwRasterGetHeight(CPostFX::pBackBuffer);
	ur = Min(ur, SCREEN_WIDTH)/RwRasterGetWidth(CPostFX::pBackBuffer);
	vb = Min(vb, SCREEN_HEIGHT)/RwRasterGetHeight(CPostFX::pBackBuffer);

	for(i = 0; i < 4; i++){
		RwIm2DVertexSetScreenX(&verts[i], drop->x + xy[i*2]*scale);
		RwIm2DVertexSetScreenY(&verts[i], drop->y + xy[i*2+1]*scale);
		RwIm2DVertexSetScreenZ(&verts[i], screenz);
		RwIm2DVertexSetCameraZ(&verts[i], z);
		RwIm2DVertexSetRecipCameraZ(&verts[i], recipz);
		RwIm2DVertexSetIntRGBA(&verts[i], drop->color.r, drop->color.g, drop->color.b, drop->color.a);
		RwIm2DVertexSetU(&verts[i], uv[i*2], recipz);
		RwIm2DVertexSetV(&verts[i], uv[i*2+1], recipz);

		verts[i].u2 = i < 2 ? ul : ur;
		verts[i].v2 = i % 3 ? vt : vb;
	}
	indices[0] = first + 0;
	indices[1] = first + 1;
	indices[2] = first + 2;
	indices[3] = first + 2;
	indices[4] = first + 3;
	indices[5] = first + 0;
}

void
ScreenDroplets::Clear(void)
{
	ScreenDrop *drop;
	for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
		drop->active = false;
	ms_numDrops = 0;
}

ScreenDroplets::ScreenDrop*
ScreenDroplets::NewDrop(float x, float y, float size, float lifetime, bool fades, int r, int g, int b)
{
	ScreenDrop *drop;
	int i;

	for(i = 0, drop = ms_drops; i < MAXDROPS; i++, drop++)
		if(!ms_drops[i].active)
			goto found;
	return nil;
found:
	ms_numDrops++;
	drop->x = x;
	drop->y = y;
	drop->size = size;
	drop->magnification = (MAXSIZE - size + 1.0f) / (MAXSIZE - MINSIZE + 1.0f);
	drop->fades = fades;
	drop->active = true;
	drop->color.r = r;
	drop->color.g = g;
	drop->color.b = b;
	drop->color.a = 255;
	drop->time = 0.0f;
	drop->lifetime = lifetime;
	return drop;
}

void
ScreenDroplets::SetMoving(ScreenDroplets::ScreenDrop *drop)
{
	ScreenDropMoving *moving;
	for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++)
		if(moving->drop == nil)
			goto found;
	return;
found:
	ms_numDropsMoving++;
	moving->drop = drop;
	moving->dist = 0.0f;
}

void
ScreenDroplets::FillScreen(int n)
{
	float x, y, size;
	ScreenDrop *drop;

	if(!ms_initialised)
		return;
	ms_numDrops = 0;
	for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++){
		drop->active = false;
		if(drop < &ms_drops[n]){
			x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
			y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
			size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
			NewDrop(x, y, size, 2000.0f, true);
		}
	}
}

void
ScreenDroplets::FillScreenMoving(float amount, bool isBlood)
{
	int n = (ms_screenMoveDelta.z > 5.0f ? 1.5f : 1.0f)*amount*20.0f;
	float x, y, size;
	ScreenDrop *drop;

	while(n--)
		if(ms_numDrops < MAXDROPS && ms_numDropsMoving < MAXDROPSMOVING){
			x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
			y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
			size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
			drop = nil;
			if(isBlood)
				drop = NewDrop(x, y, size, 2000.0f, true, 255, 0, 0);
			else
				drop = NewDrop(x, y, size, 2000.0f, true);
			if(drop)
				SetMoving(drop);
		}
}

void
ScreenDroplets::RegisterSplash(CParticleObject *pobj)
{
	CVector dist = pobj->GetPosition() - ms_prevCamPos;
	if(dist.MagnitudeSqr() < 50.0f){	// 20 originally
		ms_splashDuration = 14;
		ms_splashObject = pobj;
	}
}

void
ScreenDroplets::ProcessCameraMovement(void)
{
	RwMatrix *camMat = RwFrameGetMatrix(RwCameraGetFrame(Scene.camera));
	CVector camPos = camMat->pos;
	CVector camUp = camMat->at;
	ms_camMoveDelta = camPos - ms_prevCamPos;
	ms_camMoveDist = ms_camMoveDelta.Magnitude();

	ms_prevCamUp = camUp;
	ms_prevCamPos = camPos;

	ms_screenMoveDelta.x = -RwV3dDotProduct(&camMat->right, &ms_camMoveDelta);
	ms_screenMoveDelta.y = RwV3dDotProduct(&camMat->up, &ms_camMoveDelta);
	ms_screenMoveDelta.z = RwV3dDotProduct(&camMat->at, &ms_camMoveDelta);
	ms_screenMoveDelta *= 10.0f;
	ms_screenMoveDist = ms_screenMoveDelta.Magnitude2D();

	uint16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode;
	bool isTopDown = mode == CCam::MODE_TOPDOWN || mode == CCam::MODE_GTACLASSIC || mode == CCam::MODE_TOP_DOWN_PED;
	bool isLookingInDirection = FindPlayerVehicle() && mode == CCam::MODE_1STPERSON &&
		(CPad::GetPad(0)->GetLookBehindForCar() || CPad::GetPad(0)->GetLookLeft() || CPad::GetPad(0)->GetLookRight());
	ms_enabled = !isTopDown && !isLookingInDirection;
	ms_movingEnabled = !isTopDown && !isLookingInDirection;

	// 0 when looking stright up, 180 when looking up or down
	ms_camUpAngle = RADTODEG(Acos(Clamp(camUp.z, -1.0f, 1.0f)));
}

void
ScreenDroplets::SprayDrops(void)
{
	bool noRain = CCullZones::PlayerNoRain() || CCullZones::CamNoRain();
	if(!noRain && CWeather::Rain > 0.0f && ms_enabled){
		// 180 when looking stright up, 0 when looking up or down
		float angle = 180.0f - ms_camUpAngle;
		angle = Max(angle, 40.0f);	// want at least some rain
		FillScreenMoving((angle - 40.0f) / 150.0f * CWeather::Rain * 0.5f);
	}

	int i;
	for(i = 0; i < MAX_AUDIOHYDRANTS; i++){
		CAudioHydrant *hyd = CAudioHydrant::Get(i);
		if (hyd->pParticleObject){
			CVector dist = hyd->pParticleObject->GetPosition() - ms_prevCamPos;
			if(dist.MagnitudeSqr() > 40.0f ||
			   DotProduct(dist, ms_prevCamUp) < 0.0f) continue;

			FillScreenMoving(1.0f);
		}
	}

	static int ndrops[] = {
		125, 250, 500, 1000, 1000,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	};
	if(ms_splashDuration >= 0){
		if(ms_numDrops < MAXDROPS) {
			float numDropMult = 1.0f;
			if(ms_splashObject){
				float dist = (ms_splashObject->GetPosition() - ms_prevCamPos).Magnitude();
				numDropMult = 1.0f - (dist - 5.0f)/15.0f;
				if(numDropMult < 0) numDropMult = 0.0f;	// fix
			}
			int n = ndrops[ms_splashDuration] * numDropMult;
			while(n--)
				if(ms_numDrops < MAXDROPS){
					float x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
					float y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
					float size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
					NewDrop(x, y, size, 10000.0f, false);
				}
		}
		ms_splashDuration--;
	}
}

void
ScreenDroplets::NewTrace(ScreenDroplets::ScreenDropMoving *moving)
{
	if(ms_numDrops < MAXDROPS){
		moving->dist = 0.0f;
		NewDrop(moving->drop->x, moving->drop->y, MINSIZE, 500.0f, true,
			moving->drop->color.r, moving->drop->color.g, moving->drop->color.b);
	}
}

void
ScreenDroplets::MoveDrop(ScreenDroplets::ScreenDropMoving *moving)
{
	ScreenDrop *drop = moving->drop;
	if(!ms_movingEnabled)
		return;
	if(!drop->active){
		moving->drop = nil;
		ms_numDropsMoving--;
		return;
	}
	if(ms_screenMoveDelta.z > 0.0f && ms_camMoveDist > 0.3f){
		if(ms_screenMoveDist > 0.5f && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){
			// movement when camera turns
			moving->dist += ms_screenMoveDist;
			if(moving->dist > 20.0f && drop->color.a > 100)
				NewTrace(moving);

			drop->x -= ms_screenMoveDelta.x;
			drop->y += ms_screenMoveDelta.y;
		}else{
			// movement out of center
			float d = ms_screenMoveDelta.z*0.2f;
			float dx, dy, sum;
			dx = drop->x - SCREEN_WIDTH*0.5f + ms_screenMoveDelta.x;
			if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON)
				dy = drop->y - SCREEN_HEIGHT*1.2f - ms_screenMoveDelta.y;
			else
				dy = drop->y - SCREEN_HEIGHT*0.5f - ms_screenMoveDelta.y;
			sum = fabs(dx) + fabs(dy);
			if(sum > 0.001f){
				dx /= sum;
				dy /= sum;
			}
			moving->dist += d;
			if(moving->dist > 20.0f && drop->color.a > 100)
				NewTrace(moving);
			drop->x += dx * d;
			drop->y += dy * d;
		}

		if(drop->x < 0.0f || drop->y < 0.0f ||
		   drop->x > SCREEN_WIDTH || drop->y > SCREEN_HEIGHT){
			moving->drop = nil;
			ms_numDropsMoving--;
		}
	}
}

void
ScreenDroplets::ProcessMoving(void)
{
	ScreenDropMoving *moving;
	if(!ms_movingEnabled)
		return;
	for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++)
		if(moving->drop)
			MoveDrop(moving);
}

void
ScreenDroplets::Fade(void)
{
	ScreenDrop *drop;
	for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
		if(drop->active)
			drop->Fade();
}

void
ScreenDroplets::ScreenDrop::Fade(void)
{
	int delta = CTimer::GetTimeStepInMilliseconds();
	time += delta;
	if(time < lifetime){
		color.a = 255 - time/lifetime*255;
	}else if(fades){
		ScreenDroplets::ms_numDrops--;
		active = false;
	}
}

/*
 * Im2D with two uv coors
 */

#ifdef RW_D3D9
// stolen from RW, not in a public header
namespace rw {
namespace d3d {
void addDynamicVB(uint32 length, uint32 fvf, IDirect3DVertexBuffer9 **buf);	// NB: don't share this pointer
void removeDynamicVB(IDirect3DVertexBuffer9 **buf);
void addDynamicIB(uint32 length, IDirect3DIndexBuffer9 **buf);	// NB: don't share this pointer
void removeDynamicIB(IDirect3DIndexBuffer9 **buf);
}
}
// different than im2d
#define NUMINDICES 1024
#define NUMVERTICES 1024

static int primTypeMap[] = {
	D3DPT_POINTLIST,	// invalid
	D3DPT_LINELIST,
	D3DPT_LINESTRIP,
	D3DPT_TRIANGLELIST,
	D3DPT_TRIANGLESTRIP,
	D3DPT_TRIANGLEFAN,
	D3DPT_POINTLIST,	// actually not supported!
};
// end of stolen stuff

static IDirect3DVertexDeclaration9 *im2ddecl_uv2;
static IDirect3DVertexBuffer9 *im2dvertbuf_uv2;
static IDirect3DIndexBuffer9 *im2dindbuf_uv2;

void
openim2d_uv2(void)
{
	using namespace rw;
	using namespace d3d;
	D3DVERTEXELEMENT9 elements[5] = {
		{ 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 },
		{ 0, offsetof(Im2DVertexUV2, color), D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
		{ 0, offsetof(Im2DVertexUV2, u), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
		{ 0, offsetof(Im2DVertexUV2, u2), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
		D3DDECL_END()
	};
	assert(im2ddecl_uv2 == nil);
	im2ddecl_uv2 = (IDirect3DVertexDeclaration9*)d3d9::createVertexDeclaration((d3d9::VertexElement*)elements);
	assert(im2ddecl_uv2);

	assert(im2dvertbuf_uv2 == nil);
	im2dvertbuf_uv2 = (IDirect3DVertexBuffer9*)createVertexBuffer(NUMVERTICES*sizeof(Im2DVertexUV2), 0, true);
	assert(im2dvertbuf_uv2);
	addDynamicVB(NUMVERTICES*sizeof(Im2DVertexUV2), 0, &im2dvertbuf_uv2);

	assert(im2dindbuf_uv2 == nil);
	im2dindbuf_uv2 = (IDirect3DIndexBuffer9*)createIndexBuffer(NUMINDICES*sizeof(rw::uint16), true);
	assert(im2dindbuf_uv2);
	addDynamicIB(NUMINDICES*sizeof(rw::uint16), &im2dindbuf_uv2);
}

void
closeim2d_uv2(void)
{
	using namespace rw;
	using namespace d3d;

	d3d9::destroyVertexDeclaration(im2ddecl_uv2);
	im2ddecl_uv2 = nil;

	removeDynamicVB(&im2dvertbuf_uv2);
	destroyVertexBuffer(im2dvertbuf_uv2);
	im2dvertbuf_uv2 = nil;

	removeDynamicIB(&im2dindbuf_uv2);
	destroyIndexBuffer(im2dindbuf_uv2);
	im2dindbuf_uv2 = nil;
}

void
RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices)
{
	using namespace rw;
	using namespace d3d;

	if(numVertices > NUMVERTICES ||
	   numIndices > NUMINDICES){
		// TODO: error
		return;
	}
	rw::uint16 *lockedindices = lockIndices(im2dindbuf_uv2, 0, numIndices*sizeof(rw::uint16), D3DLOCK_DISCARD);
	memcpy(lockedindices, indices, numIndices*sizeof(rw::uint16));
	unlockIndices(im2dindbuf_uv2);

	rw::uint8 *lockedvertices = lockVertices(im2dvertbuf_uv2, 0, numVertices*sizeof(Im2DVertexUV2), D3DLOCK_DISCARD);
	memcpy(lockedvertices, vertices, numVertices*sizeof(Im2DVertexUV2));
	unlockVertices(im2dvertbuf_uv2);

	setStreamSource(0, im2dvertbuf_uv2, 0, sizeof(Im2DVertexUV2));
	setIndices(im2dindbuf_uv2);
	setVertexDeclaration(im2ddecl_uv2);

	if(im2dOverridePS)
		setPixelShader(im2dOverridePS);
	else if(engine->device.getRenderState(TEXTURERASTER))
		setPixelShader(im2d_tex_PS);
	else
		setPixelShader(im2d_PS);

	d3d::flushCache();

	rw::uint32 primCount = 0;
	switch(primType){
	case PRIMTYPELINELIST:
		primCount = numIndices/2;
		break;
	case PRIMTYPEPOLYLINE:
		primCount = numIndices-1;
		break;
	case PRIMTYPETRILIST:
		primCount = numIndices/3;
		break;
	case PRIMTYPETRISTRIP:
		primCount = numIndices-2;
		break;
	case PRIMTYPETRIFAN:
		primCount = numIndices-2;
		break;
	case PRIMTYPEPOINTLIST:
		primCount = numIndices;
		break;
	}
	d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0,
	                                0, numVertices,
	                                0, primCount);
}
#endif

#ifdef RW_GL3
// different than im2d
#define NUMINDICES 1024
#define NUMVERTICES 1024

static rw::gl3::AttribDesc im2d_UV2_attribDesc[4] = {
	{ rw::gl3::ATTRIB_POS,        GL_FLOAT,         GL_FALSE, 4,
		sizeof(Im2DVertexUV2), 0 },
	{ rw::gl3::ATTRIB_COLOR,      GL_UNSIGNED_BYTE, GL_TRUE,  4,
		sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, r) },
	{ rw::gl3::ATTRIB_TEXCOORDS0, GL_FLOAT,         GL_FALSE, 2,
		sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u) },
	{ rw::gl3::ATTRIB_TEXCOORDS1, GL_FLOAT,         GL_FALSE, 2,
		sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u2) }
};

static int primTypeMap[] = {
	GL_POINTS,	// invalid
	GL_LINES,
	GL_LINE_STRIP,
	GL_TRIANGLES,
	GL_TRIANGLE_STRIP,
	GL_TRIANGLE_FAN,
	GL_POINTS
};

static int32 u_xform;

uint32 im2D_UV2_Vbo, im2D_UV2_Ibo;
#ifdef RW_GL_USE_VAOS
uint32 im2D_UV2_Vao;
#endif

void
openim2d_uv2(void)
{
	u_xform = rw::gl3::registerUniform("u_xform", rw::gl3::UNIFORM_VEC4);	// this doesn't add a new one, so it's safe

	glGenBuffers(1, &im2D_UV2_Ibo);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW);

	glGenBuffers(1, &im2D_UV2_Vbo);
	glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo);
	glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW);

#ifdef RW_GL_USE_VAOS
	glGenVertexArrays(1, &im2D_UV2_Vao);
	glBindVertexArray(im2D_UV2_Vao);
	setAttribPointers(im2d_UV2_attribDesc, 4);
#endif
}

void
closeim2d_uv2(void)
{
	glDeleteBuffers(1, &im2D_UV2_Ibo);
	glDeleteBuffers(1, &im2D_UV2_Vbo);
#ifdef RW_GL_USE_VAOS
	glDeleteVertexArrays(1, &im2D_UV2_Vao);
#endif
}

void
RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices)
{
	using namespace rw;
	using namespace gl3;

	GLfloat xform[4];
	Camera *cam;
	cam = (Camera*)engine->currentCamera;

#ifdef RW_GL_USE_VAOS
	glBindVertexArray(im2D_UV2_Vao);
#endif

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW);
	glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numIndices*2, indices);

	glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo);
	glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices*sizeof(Im2DVertexUV2), vertices);

	xform[0] = 2.0f/cam->frameBuffer->width;
	xform[1] = -2.0f/cam->frameBuffer->height;
	xform[2] = -1.0f;
	xform[3] = 1.0f;

	if(im2dOverrideShader)
		im2dOverrideShader->use();
	else
		assert(0);//im2dShader->use();
#ifndef RW_GL_USE_VAOS
	setAttribPointers(im2d_UV2_attribDesc, 4);
#endif

	setUniform(u_xform, xform);

	flushCache();
	glDrawElements(primTypeMap[primType], numIndices,
	               GL_UNSIGNED_SHORT, nil);
#ifndef RW_GL_USE_VAOS
	disableAttribPointers(im2d_UV2_attribDesc, 4);
#endif
}
#endif

#endif


code:
#pragma once

#ifdef SCREEN_DROPLETS

class CParticleObject;

class ScreenDroplets
{
public:
	enum {
		MAXDROPS = 2000,
		MAXDROPSMOVING = 700
	};

	class ScreenDrop
	{
	public:
		float x, y, time;		// shorts on xbox (short float?)
		float size, magnification, lifetime;	// "
		CRGBA color;
		bool active;
		bool fades;

		void Fade(void);
	};

	struct ScreenDropMoving
	{
		ScreenDrop *drop;
		float dist;
	};

	static int ms_initialised;
	static RwTexture *ms_maskTex;
	static RwTexture *ms_screenTex;

	static bool ms_enabled;
	static bool ms_movingEnabled;

	static ScreenDrop ms_drops[MAXDROPS];
	static int ms_numDrops;
	static ScreenDropMoving ms_dropsMoving[MAXDROPSMOVING];
	static int ms_numDropsMoving;

	static CVector ms_prevCamUp;
	static CVector ms_prevCamPos;
	static CVector ms_camMoveDelta;
	static float ms_camMoveDist;
	static CVector ms_screenMoveDelta;
	static float ms_screenMoveDist;
	static float ms_camUpAngle;

	static int ms_splashDuration;
	static CParticleObject *ms_splashObject;

	static void Initialise(void);
	static void InitDraw(void);
	static void Shutdown(void);
	static void Process(void);
	static void Render(void);
	static void AddToRenderList(ScreenDrop *drop);

	static void Clear(void);
	static ScreenDrop *NewDrop(float x, float y, float size, float lifetime, bool fades, int r = 255, int g = 255, int b = 255);
	static void SetMoving(ScreenDroplets::ScreenDrop *drop);
	static void FillScreen(int n);
	static void FillScreenMoving(float amount, bool isBlood = false);
	static void RegisterSplash(CParticleObject *pobj);

	static void ProcessCameraMovement(void);
	static void SprayDrops(void);
	static void NewTrace(ScreenDroplets::ScreenDropMoving *moving);
	static void MoveDrop(ScreenDropMoving *moving);
	static void ProcessMoving(void);
	static void Fade(void);
};

#endif


code:
uniform sampler2D tex0;
uniform vec4 u_blurcolor;

FSIN vec4 v_color;
FSIN vec2 v_tex0;
FSIN float v_fog;

void
main(void)
{
	float a = u_blurcolor.a;
	vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
	vec4 prev = dst;
	for(int i = 0; i < 5; i++){
		vec4 tmp = dst*(1.0-a) + prev*u_blurcolor*a;
		prev = clamp(tmp, 0.0, 1.0);
	}
	vec4 color;
	color.rgb = prev.rgb;
	color.a = 1.0;

	FRAGCOLOR(color);
}


code:
sampler2D tex : register(s0);
float4 blurcol : register(c10);

float4 main(in float2 texcoord : TEXCOORD0) : COLOR0
{
	float a = blurcol.a;
	float4 dst = tex2D(tex, texcoord.xy);
	float4 prev = dst;
	for(int i = 0; i < 5; i++){
		float4 tmp = dst*(1-a) + prev*blurcol*a;
		prev = saturate(tmp);
	}
	prev.a = 1.0;
	return prev;
}


code:
uniform sampler2D tex0;
uniform vec3 u_contrastAdd;
uniform vec3 u_contrastMult;

FSIN vec4 v_color;
FSIN vec2 v_tex0;
FSIN float v_fog;

void
main(void)
{
	vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
	vec4 color;
	color.rgb = dst.rgb*u_contrastMult + u_contrastAdd;
	color.a = 1.0;

	FRAGCOLOR(color);
}


code:
struct PS_INPUT
{
	float4 position		: POSITION;
	float3 texcoord0	: TEXCOORD0;
	float4 color		: COLOR0;
};

uniform float3 contrastMult : register(c10);
uniform float3 contrastAdd : register(c11);

sampler2D tex : register(s0);

float4
main(PS_INPUT In) : COLOR
{
	float4 dst = tex2D(tex, In.texcoord0.xy);

	dst.rgb = dst.rgb*contrastMult + contrastAdd;
	dst.a = 1.0;
	return dst;
}


code:
VSIN(ATTRIB_POS)	vec3 in_pos;

VSOUT vec4 v_color;
VSOUT vec2 v_tex0;
VSOUT vec2 v_tex1;
VSOUT float v_fog;

void
main(void)
{
	vec4 Vertex = u_world * vec4(in_pos, 1.0);
	gl_Position = u_proj * u_view * Vertex;
	vec3 Normal = mat3(u_world) * in_normal;

	v_tex0 = in_tex0;
	v_tex1 = in_tex1;

	v_color = in_color;
	v_color.rgb += u_ambLight.rgb*surfAmbient;
	v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;
	v_color = clamp(v_color, 0.0, 1.0);
	v_color *= u_matColor;

	v_fog = DoFog(gl_Position.w);
}


code:
#include "standardConstants.h"

struct VS_in
{
	float4 Position		: POSITION;
	float3 Normal		: NORMAL;
	float2 TexCoord		: TEXCOORD0;
	float2 TexCoord1	: TEXCOORD1;
	float4 Prelight		: COLOR0;
};

struct VS_out {
	float4 Position		: POSITION;
	float3 TexCoord0	: TEXCOORD0;	// also fog
	float2 TexCoord1	: TEXCOORD1;
	float4 Color		: COLOR0;
};

VS_out main(in VS_in input)
{
	VS_out output;

	output.Position = mul(combinedMat, input.Position);
	float3 Vertex = mul(worldMat, input.Position).xyz;
	float3 Normal = mul(normalMat, input.Normal);

	output.TexCoord0.xy = input.TexCoord;
	output.TexCoord1.xy = input.TexCoord1;

	output.Color = input.Prelight;
	output.Color.rgb += ambientLight.rgb * surfAmbient;

	int i;
#ifdef DIRECTIONALS
	for(i = 0; i < numDirLights; i++)
		output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse;
#endif
#ifdef POINTLIGHTS
	for(i = 0; i < numPointLights; i++)
		output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse;
#endif
#ifdef SPOTLIGHTS
	for(i = 0; i < numSpotLights; i++)
		output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse;
#endif
	// PS2 clamps before material color
	output.Color = clamp(output.Color, 0.0, 1.0);
	output.Color *= matCol;

	output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);

	return output;
}


code:
uniform vec4 u_xform;

VSIN(ATTRIB_POS)	vec4 in_pos;

VSOUT vec4 v_color;
VSOUT vec2 v_tex0;
VSOUT float v_fog;

void
main(void)
{
	gl_Position = in_pos;
	gl_Position.w = 1.0;
	gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;
	v_fog = DoFog(gl_Position.z);
	gl_Position.xyz *= gl_Position.w;
	v_color = in_color;
	v_tex0 = in_tex0;
}


code:
uniform vec4 u_xform;

VSIN(ATTRIB_POS)	vec4 in_pos;

VSOUT vec4 v_color;
VSOUT vec2 v_tex0;
VSOUT vec2 v_tex1;
VSOUT float v_fog;

void
main(void)
{
	gl_Position = in_pos;
	gl_Position.w = 1.0;
	gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;
	v_fog = DoFog(gl_Position.z);
	gl_Position.xyz *= gl_Position.w;
	v_color = in_color;
	v_tex0 = in_tex0;
	v_tex1 = in_tex1;
}


code:
struct Light
{
	float4 color;	// and radius
	float4 position;	// and -cos(angle)
	float4 direction;	// and falloff clamp
};

float3 DoDirLight(Light L, float3 N)
{
	float l = max(0.0, dot(N, -L.direction.xyz));
	return l*L.color.xyz;
}

float3 DoDirLightSpec(Light L, float3 N, float3 V, float power)
{
	return pow(saturate(dot(N, normalize(V + -L.direction.xyz))), power)*L.color.xyz;
}

float3 DoPointLight(Light L, float3 V, float3 N)
{
	// As on PS2
	float3 dir = V - L.position.xyz;
	float dist = length(dir);
	float atten = max(0.0, (1.0 - dist/L.color.w));
	float l = max(0.0, dot(N, -normalize(dir)));
	return l*L.color.xyz*atten;
}

float3 DoSpotLight(Light L, float3 V, float3 N)
{
	// As on PS2
	float3 dir = V - L.position.xyz;
	float dist = length(dir);
	float atten = max(0.0, (1.0 - dist/L.color.w));
	dir /= dist;
	float l = max(0.0, dot(N, -dir));
	float pcos = dot(dir, L.direction.xyz);	// cos to point
	float ccos = -L.position.w;	// cos of cone
	float falloff = (pcos-ccos)/(1.0-ccos);
	if(falloff < 0)	// outside of cone
		l = 0;
	l *= max(falloff, L.direction.w);	// falloff clamp
	return l*L.color.xyz*atten;
}


code:
#!sh
for i in *.vert; do
	echo $i
	./makeinc_glsl.sh $i
done
for i in *.frag; do
	echo $i
	./makeinc_glsl.sh $i
done


code:
@echo off
for %%f in (*PS.hlsl) do "%DXSDK_DIR%\Utilities\bin\x86\fxc.exe" /T ps_2_0 /nologo /E main /Fo obj\%%~nf.cso %%f
for %%f in (*VS.hlsl) do "%DXSDK_DIR%\Utilities\bin\x86\fxc.exe" /T vs_2_0 /nologo /E main /Fo obj\%%~nf.cso %%f


code:
#!sh
cd obj
for i in *cso; do
	(echo -n 'static '
	xxd -i $i | grep -v '_len = ') > ${i%cso}inc
done


code:
uniform sampler2D tex0;

uniform vec4 u_reflProps;

#define glossMult (u_reflProps.x)

FSIN vec3 v_normal;
FSIN vec3 v_light;
FSIN vec2 v_tex0;
FSIN float v_fog;

void
main(void)
{
	vec4 color = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
	vec3 n = 2.0*v_normal-1.0;            // unpack
	vec3 v = 2.0*v_light-1.0;             //

	float s = dot(n, v);
	color = s*s*s*s*s*s*s*s*color*v_fog*glossMult;

	DoAlphaTest(color.a);

	FRAGCOLOR(color);
}


code:
uniform vec3 u_eye;

VSIN(ATTRIB_POS)	vec3 in_pos;

VSOUT vec3 v_normal;
VSOUT vec3 v_light;
VSOUT vec2 v_tex0;
VSOUT float v_fog;

void
main(void)
{
	vec4 Vertex = u_world * vec4(in_pos, 1.0);
	gl_Position = u_proj * u_view * Vertex;
	vec3 Normal = mat3(u_world) * in_normal;

	v_tex0 = in_tex0;

	vec3 viewVec = normalize(u_eye - Vertex.xyz);
	vec3 Light = normalize(viewVec - u_lightDirection[0].xyz);
	v_normal = 0.5*(1.0 + vec3(0.0, 0.0, 1.0));    // compress
	v_light  = 0.5*(1.0 + Light);                  //

	v_fog = DoFog(gl_Position.w);
}


code:
sampler2D tex0 : register(s0);
float glossMult : register(c1);

struct VS_out
{
	float4 Position         : POSITION;
	float3 TexCoord0        : TEXCOORD0;
	float3 Normal           : COLOR0;
	float3 Light            : COLOR1;
};

float4 main(VS_out input) : COLOR
{
	float4 color = tex2D(tex0, input.TexCoord0.xy);
	float3 n = 2.0*input.Normal-1.0;            // unpack
	float3 v = 2.0*input.Light-1.0;             //

	float s = dot(n, v);
	return s*s*s*s*s*s*s*s*color*input.TexCoord0.z*glossMult;
}


code:
#include "standardConstants.h"

struct VS_in
{
	float4 Position         : POSITION;
	float2 TexCoord         : TEXCOORD0;
};

struct VS_out
{
	float4 Position         : POSITION;
	float3 TexCoord0        : TEXCOORD0;
	float3 Normal           : COLOR0;
	float3 Light            : COLOR1;
};

float3 eye : register(c41);

VS_out main(in VS_in input)
{
	VS_out output;

	output.Position = mul(combinedMat, input.Position);
	float3 Vertex = mul(worldMat, input.Position).xyz;
	output.TexCoord0.xy = input.TexCoord;

	float3 viewVec = normalize(eye - Vertex);
	float3 Light = normalize(viewVec - lights[0].direction.xyz);
	output.Normal = 0.5*(1.0 + float3(0.0, 0.0, 1.0));    // compress
	output.Light  = 0.5*(1.0 + Light);                    //

	output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);

	return output;
}


code:
uniform vec3 u_viewVec;
uniform vec4 u_rampStart;
uniform vec4 u_rampEnd;
uniform vec3 u_rimData;

VSIN(ATTRIB_POS)	vec3 in_pos;

VSOUT vec4 v_color;
VSOUT vec2 v_tex0;
VSOUT float v_fog;

void
main(void)
{
	vec4 Vertex = u_world * vec4(in_pos, 1.0);
	gl_Position = u_proj * u_view * Vertex;
	vec3 Normal = mat3(u_world) * in_normal;

	v_tex0 = in_tex0;

	v_color = in_color;
	v_color.rgb += u_ambLight.rgb*surfAmbient;
	v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;

	// rim light
	float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec);
	vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0);
	v_color.rgb += rimlight.rgb;

	v_color = clamp(v_color, 0.0, 1.0);
	v_color *= u_matColor;

	v_fog = DoFog(gl_Position.w);
}


code:
uniform mat4 u_boneMatrices[64];

uniform vec3 u_viewVec;
uniform vec4 u_rampStart;
uniform vec4 u_rampEnd;
uniform vec3 u_rimData;

VSIN(ATTRIB_POS)	vec3 in_pos;

VSOUT vec4 v_color;
VSOUT vec2 v_tex0;
VSOUT float v_fog;

void
main(void)
{
	vec3 SkinVertex = vec3(0.0, 0.0, 0.0);
	vec3 SkinNormal = vec3(0.0, 0.0, 0.0);
	for(int i = 0; i < 4; i++){
		SkinVertex += (u_boneMatrices[int(in_indices[i])] * vec4(in_pos, 1.0)).xyz * in_weights[i];
		SkinNormal += (mat3(u_boneMatrices[int(in_indices[i])]) * in_normal) * in_weights[i];
	}

	vec4 Vertex = u_world * vec4(SkinVertex, 1.0);
	gl_Position = u_proj * u_view * Vertex;
	vec3 Normal = mat3(u_world) * SkinNormal;

	v_tex0 = in_tex0;

	v_color = in_color;
	v_color.rgb += u_ambLight.rgb*surfAmbient;
	v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;

	// rim light
	float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec);
	vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0);
	v_color.rgb += rimlight.rgb;

	v_color = clamp(v_color, 0.0, 1.0);
	v_color *= u_matColor;

	v_fog = DoFog(gl_Position.z);
}


code:
#include "standardConstants.h"

float4x3 boneMatrices[64] : register(c41);

struct VS_in
{
	float4 Position		: POSITION;
	float3 Normal		: NORMAL;
	float2 TexCoord		: TEXCOORD0;
	float4 Prelight		: COLOR0;
	float4 Weights		: BLENDWEIGHT;
	int4 Indices		: BLENDINDICES;
};

struct VS_out {
	float4 Position		: POSITION;
	float3 TexCoord0	: TEXCOORD0;	// also fog
	float4 Color		: COLOR0;
};

float3	    viewVec     : register(c233);
float4      rampStart   : register(c234);
float4      rampEnd     : register(c235);
float3      rimData     : register(c236);

VS_out main(in VS_in input)
{
	VS_out output;

	int j;
	float3 SkinVertex = float3(0.0, 0.0, 0.0);
	float3 SkinNormal = float3(0.0, 0.0, 0.0);
	for(j = 0; j < 4; j++){
		SkinVertex += mul(input.Position, boneMatrices[input.Indices[j]]).xyz * input.Weights[j];
		SkinNormal += mul(input.Normal, (float3x3)boneMatrices[input.Indices[j]]).xyz * input.Weights[j];
	}

	output.Position = mul(combinedMat, float4(SkinVertex, 1.0));
	float3 Vertex = mul(worldMat, float4(SkinVertex, 1.0)).xyz;
	float3 Normal = mul(normalMat, SkinNormal);

	output.TexCoord0.xy = input.TexCoord;

	output.Color = input.Prelight;
	output.Color.rgb += ambientLight.rgb * surfAmbient;

	int i;
//#ifdef DIRECTIONALS
	for(i = 0; i < numDirLights; i++)
		output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse;
//#endif
//#ifdef POINTLIGHTS
//	for(i = 0; i < numPointLights; i++)
//		output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse;
//#endif
//#ifdef SPOTLIGHTS
//	for(i = 0; i < numSpotLights; i++)
//		output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse;
//#endif

	// rim light
	float f = rimData.x - rimData.y*dot(Normal, viewVec);
	float4 rimlight = saturate(lerp(rampEnd, rampStart, f)*rimData.z);
	output.Color.xyz += rimlight.xyz;

	// PS2 clamps before material color
	output.Color = clamp(output.Color, 0.0, 1.0);
	output.Color *= matCol;

	output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);

	return output;
}


code:
#include "standardConstants.h"

struct VS_in
{
	float4 Position		: POSITION;
	float3 Normal		: NORMAL;
	float2 TexCoord		: TEXCOORD0;
	float4 Prelight		: COLOR0;
};

struct VS_out {
	float4 Position		: POSITION;
	float3 TexCoord0	: TEXCOORD0;	// also fog
	float4 Color		: COLOR0;
};

float3	    viewVec     : register(c233);
float4      rampStart   : register(c234);
float4      rampEnd     : register(c235);
float3      rimData     : register(c236);

VS_out main(in VS_in input)
{
	VS_out output;

	output.Position = mul(combinedMat, input.Position);
	float3 Vertex = mul(worldMat, input.Position).xyz;
	float3 Normal = mul(normalMat, input.Normal);

	output.TexCoord0.xy = input.TexCoord;

	output.Color = input.Prelight;
	output.Color.rgb += ambientLight.rgb * surfAmbient;

	int i;
//#ifdef DIRECTIONALS
	for(i = 0; i < numDirLights; i++)
		output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse;
//#endif
//#ifdef POINTLIGHTS
//	for(i = 0; i < numPointLights; i++)
//		output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse;
//#endif
//#ifdef SPOTLIGHTS
//	for(i = 0; i < numSpotLights; i++)
//		output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse;
//#endif

	// rim light
	float f = rimData.x - rimData.y*dot(Normal, viewVec);
	float4 rimlight = saturate(lerp(rampEnd, rampStart, f)*rimData.z);
	output.Color.xyz += rimlight.xyz;

	// PS2 clamps before material color
	output.Color = clamp(output.Color, 0.0, 1.0);
	output.Color *= matCol;

	output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);

	return output;
}


code:
uniform sampler2D tex0;
uniform sampler2D tex1;

FSIN vec4 v_color;
FSIN vec4 v_reflcolor;
FSIN vec2 v_tex0;
FSIN vec2 v_tex1;
FSIN float v_fog;

void
main(void)
{
	vec4 pass1 = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
	vec3 envmap = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)).rgb;
	pass1.rgb = mix(pass1.rgb, envmap, v_reflcolor.a);
	pass1.rgb = mix(u_fogColor.rgb, pass1.rgb, v_fog);
//	pass1.rgb += v_reflcolor.rgb * v_fog;

	vec3 pass2 = v_reflcolor.rgb * v_fog;

	vec4 color;
	color.rgb = pass1.rgb*pass1.a + pass2;
	color.a = pass1.a;

//	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
	DoAlphaTest(color.a);

	FRAGCOLOR(color);
}


code:
uniform vec3 u_eye;
uniform vec4 u_reflProps;
uniform vec4 u_specDir[5];
uniform vec4 u_specColor[5];

#define fresnel (u_reflProps.x)
#define lightStrength (u_reflProps.y)	// speclight alpha
#define shininess (u_reflProps.z)
#define specularity (u_reflProps.w)

VSIN(ATTRIB_POS)	vec3 in_pos;

VSOUT vec4 v_color;
VSOUT vec4 v_reflcolor;
VSOUT vec2 v_tex0;
VSOUT vec2 v_tex1;
VSOUT float v_fog;

vec3 DoDirLightSpec(vec3 Ldir, vec3 Lcol, vec3 N, vec3 V, float power)
{
	return pow(clamp(dot(N, normalize(V + -Ldir)), 0.0, 1.0), power)*Lcol;
}

void
main(void)
{
	vec4 Vertex = u_world * vec4(in_pos, 1.0);
	gl_Position = u_proj * u_view * Vertex;
	vec3 Normal = mat3(u_world) * in_normal;
	vec3 viewVec = normalize(u_eye - Vertex.xyz);

	v_tex0 = in_tex0;

	v_color = in_color;
	v_color.rgb += u_ambLight.rgb*surfAmbient;
	v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse*lightStrength;
	v_color = clamp(v_color, 0.0, 1.0);
	v_color *= u_matColor;

	// reflect V along Normal
	vec3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec;
	v_tex1 = uv2.xy*0.5 + 0.5;
	float b = 1.0 - clamp(dot(viewVec, Normal), 0.0, 1.0);
	v_reflcolor = vec4(0.0, 0.0, 0.0, 1.0);
	v_reflcolor.a = mix(b*b*b*b*b, 1.0, fresnel)*shininess;

	for(int i = 0; i < 5; i++)
		v_reflcolor.rgb += DoDirLightSpec(u_specDir[i].xyz, u_specColor[i].rgb, Normal, viewVec, u_specDir[i].w)*specularity*lightStrength;

	v_fog = DoFog(gl_Position.w);
}


code:
struct VS_out {
	float4 Position		: POSITION;
	float3 TexCoord0	: TEXCOORD0;
	float2 TexCoord1	: TEXCOORD1;
	float4 Color		: COLOR0;
	float4 ReflColor	: COLOR1;
};

sampler2D tex0 : register(s0);
sampler2D tex1 : register(s1);

float4 fogColor : register(c0);

float4 main(VS_out input) : COLOR
{
	float4 pass1 = input.Color;
//#ifdef TEX
	pass1 *= tex2D(tex0, input.TexCoord0.xy);
//#endif
	float3 envmap = tex2D(tex1, input.TexCoord1).rgb;
	pass1.rgb = lerp(pass1.rgb, envmap, input.ReflColor.a);
//	pass1.rgb = envmap;
//	pass1.rgb *= input.ReflColor.a;
	pass1.rgb = lerp(fogColor.rgb, pass1.rgb, input.TexCoord0.z);
//	pass1.rgb += input.ReflColor.rgb * input.TexCoord0.z;

	float3 pass2 = input.ReflColor.rgb*input.TexCoord0.z;

	float4 color;
	color.rgb = pass1.rgb*pass1.a + pass2;
	color.a = pass1.a;

	return color;
}


code:
#include "standardConstants.h"

struct VS_in
{
	float4 Position		: POSITION;
	float3 Normal		: NORMAL;
	float2 TexCoord		: TEXCOORD0;
	float4 Prelight		: COLOR0;
};

struct VS_out {
	float4 Position		: POSITION;
	float3 TexCoord0	: TEXCOORD0;	// also fog
	float2 TexCoord1	: TEXCOORD1;
	float4 Color		: COLOR0;
	float4 ReflColor	: COLOR1;
};

float3 eye : register(c41);
float4 reflProps : register(c42);
Light specLights[5] : register(c43);

#define fresnel (reflProps.x)
#define lightStrength (reflProps.y)	// speclight alpha
#define shininess (reflProps.z)
#define specularity (reflProps.w)

VS_out main(in VS_in input)
{
	VS_out output;

	output.Position = mul(combinedMat, input.Position);
	float3 Vertex = mul(worldMat, input.Position).xyz;
	float3 Normal = mul(normalMat, input.Normal);
	float3 viewVec = normalize(eye - Vertex);

	output.TexCoord0.xy = input.TexCoord;

	output.Color = input.Prelight;
	output.Color.rgb += ambientLight.rgb * surfAmbient*lightStrength;

	int i;
	for(i = 0; i < numDirLights; i++)
		output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse*lightStrength;
	// PS2 clamps before material color
	output.Color = clamp(output.Color, 0.0, 1.0);
	output.Color *= matCol;

	// reflect V along Normal
	float3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec;
	output.TexCoord1 = uv2.xy*0.5 + 0.5;
	float b = 1.0 - saturate(dot(viewVec, Normal));
	output.ReflColor = float4(0.0, 0.0, 0.0, 1.0);
	output.ReflColor.a = lerp(b*b*b*b*b, 1.0, fresnel)*shininess;

	//Light mainLight = lights[0];
	for(i = 0; i < 5; i++)
		output.ReflColor.xyz += DoDirLightSpec(specLights[i], Normal, viewVec, specLights[i].direction.w)*specularity*lightStrength;

	output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);

	return output;
}


code:
uniform sampler2D tex0;
uniform sampler2D tex1;

uniform vec4 u_lightMap;

FSIN vec4 v_color;
FSIN vec2 v_tex0;
FSIN vec2 v_tex1;
FSIN float v_fog;

void
main(void)
{
	vec4 t0 = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
	vec4 t1 = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));

	vec4 color = t0*v_color*(1.0 + u_lightMap*(2.0*t1-1.0));
	color.a = v_color.a*t0.a*u_lightMap.a;

	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
	DoAlphaTest(color.a);

	FRAGCOLOR(color);
}


code:
sampler2D Diffuse : register(s0);
sampler2D Light : register(s1);
float4 fogColor : register(c0);
float4 lm : register(c1);

struct PS_INPUT
{
	float4 Color	: COLOR0;
	float3 Tex0	: TEXCOORD0;
	float2 Tex1	: TEXCOORD1;
};

float4
main(PS_INPUT IN) : COLOR
{
	float4 t0 = tex2D(Diffuse, IN.Tex0.xy);
	float4 t1 = tex2D(Light, IN.Tex1);

	float4 col = t0*IN.Color*(1 + lm*(2*t1-1));
	col.a = IN.Color.a*t0.a*lm.a;

	col.rgb = lerp(fogColor.rgb, col.rgb, IN.Tex0.z);

	return col;
}


code:
const char *colourfilterIII_frag_src =
"uniform sampler2D tex0;\n"
"uniform vec4 u_blurcolor;\n"

"FSIN vec4 v_color;\n"
"FSIN vec2 v_tex0;\n"
"FSIN float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	float a = u_blurcolor.a;\n"
"	vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
"	vec4 prev = dst;\n"
"	for(int i = 0; i < 5; i++){\n"
"		vec4 tmp = dst*(1.0-a) + prev*u_blurcolor*a;\n"
"		prev = clamp(tmp, 0.0, 1.0);\n"
"	}\n"
"	vec4 color;\n"
"	color.rgb = prev.rgb;\n"
"	color.a = 1.0;\n"

"	FRAGCOLOR(color);\n"
"}\n"

;


code:
const char *contrast_frag_src =
"uniform sampler2D tex0;\n"
"uniform vec3 u_contrastAdd;\n"
"uniform vec3 u_contrastMult;\n"

"FSIN vec4 v_color;\n"
"FSIN vec2 v_tex0;\n"
"FSIN float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
"	vec4 color;\n"
"	color.rgb = dst.rgb*u_contrastMult + u_contrastAdd;\n"
"	color.a = 1.0;\n"

"	FRAGCOLOR(color);\n"
"}\n"

;


code:
const char *default_UV2_vert_src =
"VSIN(ATTRIB_POS)	vec3 in_pos;\n"

"VSOUT vec4 v_color;\n"
"VSOUT vec2 v_tex0;\n"
"VSOUT vec2 v_tex1;\n"
"VSOUT float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
"	gl_Position = u_proj * u_view * Vertex;\n"
"	vec3 Normal = mat3(u_world) * in_normal;\n"

"	v_tex0 = in_tex0;\n"
"	v_tex1 = in_tex1;\n"

"	v_color = in_color;\n"
"	v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
"	v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"
"	v_color = clamp(v_color, 0.0, 1.0);\n"
"	v_color *= u_matColor;\n"

"	v_fog = DoFog(gl_Position.w);\n"
"}\n"
;


code:
const char *im2d_UV2_vert_src =
"uniform vec4 u_xform;\n"

"VSIN(ATTRIB_POS)	vec4 in_pos;\n"

"VSOUT vec4 v_color;\n"
"VSOUT vec2 v_tex0;\n"
"VSOUT vec2 v_tex1;\n"
"VSOUT float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	gl_Position = in_pos;\n"
"	gl_Position.w = 1.0;\n"
"	gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;\n"
"	v_fog = DoFog(gl_Position.z);\n"
"	gl_Position.xyz *= gl_Position.w;\n"
"	v_color = in_color;\n"
"	v_tex0 = in_tex0;\n"
"	v_tex1 = in_tex1;\n"
"}\n"
;


code:
const char *im2d_vert_src =
"uniform vec4 u_xform;\n"

"VSIN(ATTRIB_POS)	vec4 in_pos;\n"

"VSOUT vec4 v_color;\n"
"VSOUT vec2 v_tex0;\n"
"VSOUT float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	gl_Position = in_pos;\n"
"	gl_Position.w = 1.0;\n"
"	gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;\n"
"	v_fog = DoFog(gl_Position.z);\n"
"	gl_Position.xyz *= gl_Position.w;\n"
"	v_color = in_color;\n"
"	v_tex0 = in_tex0;\n"
"}\n"
;


code:
const char *neoGloss_frag_src =
"uniform sampler2D tex0;\n"

"uniform vec4 u_reflProps;\n"

"#define glossMult (u_reflProps.x)\n"

"FSIN vec3 v_normal;\n"
"FSIN vec3 v_light;\n"
"FSIN vec2 v_tex0;\n"
"FSIN float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 color = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
"	vec3 n = 2.0*v_normal-1.0;            // unpack\n"
"	vec3 v = 2.0*v_light-1.0;             //\n"

"	float s = dot(n, v);\n"
"	color = s*s*s*s*s*s*s*s*color*v_fog*glossMult;\n"

"	DoAlphaTest(color.a);\n"

"	FRAGCOLOR(color);\n"
"}\n"

;


code:
const char *neoGloss_vert_src =
"uniform vec3 u_eye;\n"

"VSIN(ATTRIB_POS)	vec3 in_pos;\n"

"VSOUT vec3 v_normal;\n"
"VSOUT vec3 v_light;\n"
"VSOUT vec2 v_tex0;\n"
"VSOUT float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
"	gl_Position = u_proj * u_view * Vertex;\n"
"	vec3 Normal = mat3(u_world) * in_normal;\n"

"	v_tex0 = in_tex0;\n"

"	vec3 viewVec = normalize(u_eye - Vertex.xyz);\n"
"	vec3 Light = normalize(viewVec - u_lightDirection[0].xyz);\n"
"	v_normal = 0.5*(1.0 + vec3(0.0, 0.0, 1.0));    // compress\n"
"	v_light  = 0.5*(1.0 + Light);                  //\n"

"	v_fog = DoFog(gl_Position.w);\n"
"}\n"
;


code:
const char *neoRimSkin_vert_src =
"uniform mat4 u_boneMatrices[64];\n"

"uniform vec3 u_viewVec;\n"
"uniform vec4 u_rampStart;\n"
"uniform vec4 u_rampEnd;\n"
"uniform vec3 u_rimData;\n"

"VSIN(ATTRIB_POS)	vec3 in_pos;\n"

"VSOUT vec4 v_color;\n"
"VSOUT vec2 v_tex0;\n"
"VSOUT float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec3 SkinVertex = vec3(0.0, 0.0, 0.0);\n"
"	vec3 SkinNormal = vec3(0.0, 0.0, 0.0);\n"
"	for(int i = 0; i < 4; i++){\n"
"		SkinVertex += (u_boneMatrices[int(in_indices[i])] * vec4(in_pos, 1.0)).xyz * in_weights[i];\n"
"		SkinNormal += (mat3(u_boneMatrices[int(in_indices[i])]) * in_normal) * in_weights[i];\n"
"	}\n"

"	vec4 Vertex = u_world * vec4(SkinVertex, 1.0);\n"
"	gl_Position = u_proj * u_view * Vertex;\n"
"	vec3 Normal = mat3(u_world) * SkinNormal;\n"

"	v_tex0 = in_tex0;\n"

"	v_color = in_color;\n"
"	v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
"	v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"

"	// rim light\n"
"	float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec);\n"
"	vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0);\n"
"	v_color.rgb += rimlight.rgb;\n"

"	v_color = clamp(v_color, 0.0, 1.0);\n"
"	v_color *= u_matColor;\n"

"	v_fog = DoFog(gl_Position.z);\n"
"}\n"
;


code:
const char *neoRim_vert_src =
"uniform vec3 u_viewVec;\n"
"uniform vec4 u_rampStart;\n"
"uniform vec4 u_rampEnd;\n"
"uniform vec3 u_rimData;\n"

"VSIN(ATTRIB_POS)	vec3 in_pos;\n"

"VSOUT vec4 v_color;\n"
"VSOUT vec2 v_tex0;\n"
"VSOUT float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
"	gl_Position = u_proj * u_view * Vertex;\n"
"	vec3 Normal = mat3(u_world) * in_normal;\n"

"	v_tex0 = in_tex0;\n"

"	v_color = in_color;\n"
"	v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
"	v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"

"	// rim light\n"
"	float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec);\n"
"	vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0);\n"
"	v_color.rgb += rimlight.rgb;\n"

"	v_color = clamp(v_color, 0.0, 1.0);\n"
"	v_color *= u_matColor;\n"

"	v_fog = DoFog(gl_Position.w);\n"
"}\n"
;


code:
const char *neoVehicle_frag_src =
"uniform sampler2D tex0;\n"
"uniform sampler2D tex1;\n"

"FSIN vec4 v_color;\n"
"FSIN vec4 v_reflcolor;\n"
"FSIN vec2 v_tex0;\n"
"FSIN vec2 v_tex1;\n"
"FSIN float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 pass1 = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
"	vec3 envmap = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)).rgb;\n"
"	pass1.rgb = mix(pass1.rgb, envmap, v_reflcolor.a);\n"
"	pass1.rgb = mix(u_fogColor.rgb, pass1.rgb, v_fog);\n"
"//	pass1.rgb += v_reflcolor.rgb * v_fog;\n"

"	vec3 pass2 = v_reflcolor.rgb * v_fog;\n"

"	vec4 color;\n"
"	color.rgb = pass1.rgb*pass1.a + pass2;\n"
"	color.a = pass1.a;\n"

"//	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);\n"
"	DoAlphaTest(color.a);\n"

"	FRAGCOLOR(color);\n"
"}\n"
;


code:
const char *neoVehicle_vert_src =
"uniform vec3 u_eye;\n"
"uniform vec4 u_reflProps;\n"
"uniform vec4 u_specDir[5];\n"
"uniform vec4 u_specColor[5];\n"

"#define fresnel (u_reflProps.x)\n"
"#define lightStrength (u_reflProps.y)	// speclight alpha\n"
"#define shininess (u_reflProps.z)\n"
"#define specularity (u_reflProps.w)\n"

"VSIN(ATTRIB_POS)	vec3 in_pos;\n"

"VSOUT vec4 v_color;\n"
"VSOUT vec4 v_reflcolor;\n"
"VSOUT vec2 v_tex0;\n"
"VSOUT vec2 v_tex1;\n"
"VSOUT float v_fog;\n"

"vec3 DoDirLightSpec(vec3 Ldir, vec3 Lcol, vec3 N, vec3 V, float power)\n"
"{\n"
"	return pow(clamp(dot(N, normalize(V + -Ldir)), 0.0, 1.0), power)*Lcol;\n"
"}\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
"	gl_Position = u_proj * u_view * Vertex;\n"
"	vec3 Normal = mat3(u_world) * in_normal;\n"
"	vec3 viewVec = normalize(u_eye - Vertex.xyz);\n"

"	v_tex0 = in_tex0;\n"

"	v_color = in_color;\n"
"	v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
"	v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse*lightStrength;\n"
"	v_color = clamp(v_color, 0.0, 1.0);\n"
"	v_color *= u_matColor;\n"

"	// reflect V along Normal\n"
"	vec3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec;\n"
"	v_tex1 = uv2.xy*0.5 + 0.5;\n"
"	float b = 1.0 - clamp(dot(viewVec, Normal), 0.0, 1.0);\n"
"	v_reflcolor = vec4(0.0, 0.0, 0.0, 1.0);\n"
"	v_reflcolor.a = mix(b*b*b*b*b, 1.0, fresnel)*shininess;\n"

"	for(int i = 0; i < 5; i++)\n"
"		v_reflcolor.rgb += DoDirLightSpec(u_specDir[i].xyz, u_specColor[i].rgb, Normal, viewVec, u_specDir[i].w)*specularity*lightStrength;\n"

"	v_fog = DoFog(gl_Position.w);\n"
"}\n"
;


code:
const char *neoWorldIII_frag_src =
"uniform sampler2D tex0;\n"
"uniform sampler2D tex1;\n"

"uniform vec4 u_lightMap;\n"

"FSIN vec4 v_color;\n"
"FSIN vec2 v_tex0;\n"
"FSIN vec2 v_tex1;\n"
"FSIN float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 t0 = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
"	vec4 t1 = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));\n"

"	vec4 color = t0*v_color*(1.0 + u_lightMap*(2.0*t1-1.0));\n"
"	color.a = v_color.a*t0.a*u_lightMap.a;\n"

"	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);\n"
"	DoAlphaTest(color.a);\n"

"	FRAGCOLOR(color);\n"
"}\n"

;


code:
const char *screenDroplet_frag_src =
"uniform sampler2D tex0;\n"
"uniform sampler2D tex1;\n"

"FSIN vec4 v_color;\n"
"FSIN vec2 v_tex0;\n"
"FSIN vec2 v_tex1;\n"
"FSIN float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 color;\n"
"	color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
"	color *= texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));\n"

"	FRAGCOLOR(color);\n"
"}\n"

;


code:
const char *simple_frag_src =
"uniform sampler2D tex0;\n"

"FSIN vec4 v_color;\n"
"FSIN vec2 v_tex0;\n"
"FSIN float v_fog;\n"

"void\n"
"main(void)\n"
"{\n"
"	vec4 color;\n"
"	color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
"	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);\n"
"	DoAlphaTest(color.a);\n"

"	FRAGCOLOR(color);\n"
"}\n"

;


code:
uniform sampler2D tex0;
uniform sampler2D tex1;

FSIN vec4 v_color;
FSIN vec2 v_tex0;
FSIN vec2 v_tex1;
FSIN float v_fog;

void
main(void)
{
	vec4 color;
	color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
	color *= texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));

	FRAGCOLOR(color);
}


code:
struct VS_out {
	float4 Position		: POSITION;
	float2 TexCoord0	: TEXCOORD0;
	float2 TexCoord1	: TEXCOORD1;
	float4 Color		: COLOR0;
};

sampler2D tex0 : register(s0);
sampler2D tex1 : register(s1);

float4 main(VS_out input) : COLOR
{
	float4 color = input.Color;
	color *= tex2D(tex0, input.TexCoord0.xy);
	color *= tex2D(tex1, input.TexCoord1.xy);
	return color;
}


code:
uniform sampler2D tex0;

FSIN vec4 v_color;
FSIN vec2 v_tex0;
FSIN float v_fog;

void
main(void)
{
	vec4 color;
	color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
	DoAlphaTest(color.a);

	FRAGCOLOR(color);
}


code:
float4x4	combinedMat	: register(c0);
float4x4	worldMat	: register(c4);
float3x3	normalMat	: register(c8);
float4		matCol		: register(c12);
float4		surfProps	: register(c13);
float4		fogData	: register(c14);
float4		ambientLight	: register(c15);

#define surfAmbient (surfProps.x)
#define surfSpecular (surfProps.y)
#define surfDiffuse (surfProps.z)

#define fogStart (fogData.x)
#define fogEnd (fogData.y)
#define fogRange (fogData.z)
#define fogDisable (fogData.w)

#include "lighting.h"

int numDirLights : register(i0);
int numPointLights : register(i1);
int numSpotLights : register(i2);
int4 firstLight : register(c16);
Light lights[8] : register(c17);

#define firstDirLight (firstLight.x)
#define firstPointLight (firstLight.y)
#define firstSpotLight (firstLight.z)