<commit_before>
#include "platform/d3d.h"

#include "infrastructure/mdfparser.h"
#include "infrastructure/mesparser.h"
#include "infrastructure/vfs.h"
#include "infrastructure/logging.h"
#include "infrastructure/stringutil.h"

#include "graphics/mdfmaterials.h"
#include "graphics/shaders.h"
#include "graphics/device.h"

using namespace DirectX;

namespace gfx {

	enum MdfShaderRegisters {
		// Projection Matrix (in ToEE's case this is viewProj)
		MDF_REG_VIEWPROJ = 0,
		MDF_REG_MATDIFFUSE = 4,
		MDF_REG_UVANIMTIME = 5,
		MDF_REG_UVROTATION = 6,

		// Lighting related registers
		MDF_REG_LIGHT_POS = 100,
		MDF_REG_LIGHT_DIR = 108,
		MDF_REG_LIGHT_AMBIENT = 116,
		MDF_REG_LIGHT_DIFFUSE = 124,
		MDF_REG_LIGHT_SPECULAR = 132,
		MDF_REG_LIGHT_RANGE = 140,
		MDF_REG_LIGHT_ATTENUATION = 148,
		MDF_REG_LIGHT_SPOT = 156,
		MDF_REG_LIGHT_SPECULARENABLE = 164,
		MDF_REG_LIGHT_SPECULARPOWER = 165,
		MDF_REG_LIGHT_MAT_SPECULAR = 166,
		MDF_REG_LIGHT_COUNT = 167
	};

	MdfRenderMaterial::MdfRenderMaterial(gfx::LegacyShaderId id,
		const std::string& name,
		std::unique_ptr<class gfx::MdfMaterial>&& spec,
		const Material& material)
		: mId(id), mName(name), mSpec(std::move(spec)), mDeviceMaterial(material) {
	}

	gfx::TextureRef MdfRenderMaterial::GetPrimaryTexture() {
		if (mDeviceMaterial.GetSamplers().empty()) {
			return nullptr;
		}
		return mDeviceMaterial.GetSamplers()[0].GetTexture();
	}

	void MdfRenderMaterial::Bind(RenderingDevice& device, 
		gsl::span<Light3d> lights,
		const MdfRenderOverrides *overrides) {

		device.SetMaterial(mDeviceMaterial);

		BindShader(device, lights, overrides);

	}

	void MdfRenderMaterial::BindShader(RenderingDevice &device,
		gsl::span<Light3d> lights,
		const MdfRenderOverrides *overrides) const {

		auto d3d = device.GetDevice();

		// Should we use a separate world matrix?
		if (overrides && overrides->useWorldMatrix) {
			// Build a world * view * proj matrix
			auto worldViewProj{ XMLoadFloat4x4(&overrides->worldMatrix) * XMLoadFloat4x4(&device.GetCamera().GetViewProj()) };
			XMFLOAT4X4A worldViewProjMat;
			XMStoreFloat4x4A(&worldViewProjMat, worldViewProj);

			d3d->SetVertexShaderConstantF(MDF_REG_VIEWPROJ, &worldViewProjMat._11, 4);
		} else {
			d3d->SetVertexShaderConstantF(MDF_REG_VIEWPROJ, &device.GetCamera().GetViewProj()._11, 4);
		}
		
		// Set material diffuse color for shader
		DirectX::XMFLOAT4 floats;
		if (overrides && overrides->overrideColor) {
			floats = D3DColorToFloat4(overrides->overrideColor);
		} else {
			floats = D3DColorToFloat4(mSpec->diffuse);
		}
		if (overrides && overrides->alpha != 1.0f) {
			floats.w *= overrides->alpha;
		}
		d3d->SetVertexShaderConstantF(MDF_REG_MATDIFFUSE, &floats.x, 1);

		// Set time for UV animation in minutes as a floating point number
		auto animTime = device.GetLastFrameStart() - device.GetDeviceCreated();
		auto timeInMs = std::chrono::duration_cast<std::chrono::milliseconds>(animTime).count();
		floats.x = timeInMs / 60000.0f;
		// Clamp to [0, 1]
		if (floats.x > 1) {
			floats.x -= floor(floats.x);
		}
		d3d->SetVertexShaderConstantF(MDF_REG_UVANIMTIME, &floats.x, 1);

		// Swirl is more complicated due to cos/sin involvement
		// This means speedU is in "full rotations every 60 seconds" -> RPM
		for (size_t i = 0; i < mSpec->samplers.size(); ++i) {
			auto& sampler = mSpec->samplers[i];
			if (sampler.uvType != MdfUvType::Swirl) {
				continue;
			}
			DirectX::XMFLOAT4 uvRot;
			uvRot.x = cosf(sampler.speedU * floats.x * XM_2PI) * 0.1f;
			uvRot.y = sinf(sampler.speedV * floats.x * XM_2PI) * 0.1f;
			d3d->SetVertexShaderConstantF(MDF_REG_UVROTATION + i, &uvRot.x, 1);
		}

		if (!mSpec->notLit) {
			auto ignoreLighting = overrides && overrides->ignoreLighting;
			BindVertexLighting(device, lights, ignoreLighting);
		}
	}

	void MdfRenderMaterial::BindVertexLighting(RenderingDevice &device,
		gsl::span<Light3d> lights,
		bool ignoreLighting) const {

		auto d3d = device.GetDevice();

		constexpr auto MaxLights = 8;

		if (lights.size() > MaxLights) {
			lights = lights.subspan(0, MaxLights);
		}

		// To make indexing in the HLSL shader more efficient, we sort the
		// lights here in the following order: directional, point lights, spot lights
		std::array<DirectX::XMFLOAT4A, MaxLights> lightPos;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightDir;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightAmbient;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightDiffuse;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightSpecular;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightRange;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightAttenuation;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightSpot;

		auto directionalCount = 0;
		auto pointCount = 0;
		auto spotCount = 0;

		// In order to not have to use a different shader, we instead
		// opt for using a diffuse light with ambient 1,1,1,1
		if (ignoreLighting) {
			directionalCount = 1;
			lightDir[0] = XMFLOAT4A(0, 0, 0, 0);
			lightDiffuse[0] = XMFLOAT4A(0, 0, 0, 0);
			lightSpecular[0] = XMFLOAT4A(0, 0, 0, 0);

			lightAmbient[0].x = 1;
			lightAmbient[0].y = 1;
			lightAmbient[0].z = 1;
			lightAmbient[0].w = 0;
		} else {
			// Count the number of lights of each type
			for (auto& light : lights) {
				if (light.type == Light3dType::Directional)
					++directionalCount;
				else if (light.type == Light3dType::Point)
					++pointCount;
				else if (light.type == Light3dType::Directional)
					++spotCount;
			}

			auto pointFirstIdx = directionalCount;
			auto spotFirstIdx = pointFirstIdx + pointCount;

			directionalCount = 0;
			pointCount = 0;
			spotCount = 0;

			for (auto& light : lights) {
				int lightIdx;
				if (light.type == Light3dType::Directional)
					lightIdx = directionalCount++;
				else if (light.type == Light3dType::Point)
					lightIdx = pointFirstIdx + pointCount++;
				else if (light.type == Light3dType::Directional)
					lightIdx = spotFirstIdx + spotCount++;<commit_msg>Fixes #163: Spotlights were not rendered correctly.<commit_after>
#include "platform/d3d.h"

#include "infrastructure/mdfparser.h"
#include "infrastructure/mesparser.h"
#include "infrastructure/vfs.h"
#include "infrastructure/logging.h"
#include "infrastructure/stringutil.h"

#include "graphics/mdfmaterials.h"
#include "graphics/shaders.h"
#include "graphics/device.h"

using namespace DirectX;

namespace gfx {

	enum MdfShaderRegisters {
		// Projection Matrix (in ToEE's case this is viewProj)
		MDF_REG_VIEWPROJ = 0,
		MDF_REG_MATDIFFUSE = 4,
		MDF_REG_UVANIMTIME = 5,
		MDF_REG_UVROTATION = 6,

		// Lighting related registers
		MDF_REG_LIGHT_POS = 100,
		MDF_REG_LIGHT_DIR = 108,
		MDF_REG_LIGHT_AMBIENT = 116,
		MDF_REG_LIGHT_DIFFUSE = 124,
		MDF_REG_LIGHT_SPECULAR = 132,
		MDF_REG_LIGHT_RANGE = 140,
		MDF_REG_LIGHT_ATTENUATION = 148,
		MDF_REG_LIGHT_SPOT = 156,
		MDF_REG_LIGHT_SPECULARENABLE = 164,
		MDF_REG_LIGHT_SPECULARPOWER = 165,
		MDF_REG_LIGHT_MAT_SPECULAR = 166,
		MDF_REG_LIGHT_COUNT = 167
	};

	MdfRenderMaterial::MdfRenderMaterial(gfx::LegacyShaderId id,
		const std::string& name,
		std::unique_ptr<class gfx::MdfMaterial>&& spec,
		const Material& material)
		: mId(id), mName(name), mSpec(std::move(spec)), mDeviceMaterial(material) {
	}

	gfx::TextureRef MdfRenderMaterial::GetPrimaryTexture() {
		if (mDeviceMaterial.GetSamplers().empty()) {
			return nullptr;
		}
		return mDeviceMaterial.GetSamplers()[0].GetTexture();
	}

	void MdfRenderMaterial::Bind(RenderingDevice& device, 
		gsl::span<Light3d> lights,
		const MdfRenderOverrides *overrides) {

		device.SetMaterial(mDeviceMaterial);

		BindShader(device, lights, overrides);

	}

	void MdfRenderMaterial::BindShader(RenderingDevice &device,
		gsl::span<Light3d> lights,
		const MdfRenderOverrides *overrides) const {

		auto d3d = device.GetDevice();

		// Should we use a separate world matrix?
		if (overrides && overrides->useWorldMatrix) {
			// Build a world * view * proj matrix
			auto worldViewProj{ XMLoadFloat4x4(&overrides->worldMatrix) * XMLoadFloat4x4(&device.GetCamera().GetViewProj()) };
			XMFLOAT4X4A worldViewProjMat;
			XMStoreFloat4x4A(&worldViewProjMat, worldViewProj);

			d3d->SetVertexShaderConstantF(MDF_REG_VIEWPROJ, &worldViewProjMat._11, 4);
		} else {
			d3d->SetVertexShaderConstantF(MDF_REG_VIEWPROJ, &device.GetCamera().GetViewProj()._11, 4);
		}
		
		// Set material diffuse color for shader
		DirectX::XMFLOAT4 floats;
		if (overrides && overrides->overrideColor) {
			floats = D3DColorToFloat4(overrides->overrideColor);
		} else {
			floats = D3DColorToFloat4(mSpec->diffuse);
		}
		if (overrides && overrides->alpha != 1.0f) {
			floats.w *= overrides->alpha;
		}
		d3d->SetVertexShaderConstantF(MDF_REG_MATDIFFUSE, &floats.x, 1);

		// Set time for UV animation in minutes as a floating point number
		auto animTime = device.GetLastFrameStart() - device.GetDeviceCreated();
		auto timeInMs = std::chrono::duration_cast<std::chrono::milliseconds>(animTime).count();
		floats.x = timeInMs / 60000.0f;
		// Clamp to [0, 1]
		if (floats.x > 1) {
			floats.x -= floor(floats.x);
		}
		d3d->SetVertexShaderConstantF(MDF_REG_UVANIMTIME, &floats.x, 1);

		// Swirl is more complicated due to cos/sin involvement
		// This means speedU is in "full rotations every 60 seconds" -> RPM
		for (size_t i = 0; i < mSpec->samplers.size(); ++i) {
			auto& sampler = mSpec->samplers[i];
			if (sampler.uvType != MdfUvType::Swirl) {
				continue;
			}
			DirectX::XMFLOAT4 uvRot;
			uvRot.x = cosf(sampler.speedU * floats.x * XM_2PI) * 0.1f;
			uvRot.y = sinf(sampler.speedV * floats.x * XM_2PI) * 0.1f;
			d3d->SetVertexShaderConstantF(MDF_REG_UVROTATION + i, &uvRot.x, 1);
		}

		if (!mSpec->notLit) {
			auto ignoreLighting = overrides && overrides->ignoreLighting;
			BindVertexLighting(device, lights, ignoreLighting);
		}
	}

	void MdfRenderMaterial::BindVertexLighting(RenderingDevice &device,
		gsl::span<Light3d> lights,
		bool ignoreLighting) const {

		auto d3d = device.GetDevice();

		constexpr auto MaxLights = 8;

		if (lights.size() > MaxLights) {
			lights = lights.subspan(0, MaxLights);
		}

		// To make indexing in the HLSL shader more efficient, we sort the
		// lights here in the following order: directional, point lights, spot lights
		std::array<DirectX::XMFLOAT4A, MaxLights> lightPos;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightDir;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightAmbient;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightDiffuse;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightSpecular;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightRange;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightAttenuation;
		std::array<DirectX::XMFLOAT4A, MaxLights> lightSpot;

		auto directionalCount = 0;
		auto pointCount = 0;
		auto spotCount = 0;

		// In order to not have to use a different shader, we instead
		// opt for using a diffuse light with ambient 1,1,1,1
		if (ignoreLighting) {
			directionalCount = 1;
			lightDir[0] = XMFLOAT4A(0, 0, 0, 0);
			lightDiffuse[0] = XMFLOAT4A(0, 0, 0, 0);
			lightSpecular[0] = XMFLOAT4A(0, 0, 0, 0);

			lightAmbient[0].x = 1;
			lightAmbient[0].y = 1;
			lightAmbient[0].z = 1;
			lightAmbient[0].w = 0;
		} else {
			// Count the number of lights of each type
			for (auto& light : lights) {
				if (light.type == Light3dType::Directional)
					++directionalCount;
				else if (light.type == Light3dType::Point)
					++pointCount;
				else if (light.type == Light3dType::Spot)
					++spotCount;
			}

			auto pointFirstIdx = directionalCount;
			auto spotFirstIdx = pointFirstIdx + pointCount;

			directionalCount = 0;
			pointCount = 0;
			spotCount = 0;

			for (auto& light : lights) {
				int lightIdx;
				if (light.type == Light3dType::Directional)
					lightIdx = directionalCount++;
				else if (light.type == Light3dType::Point)
					lightIdx = pointFirstIdx + pointCount++;
				else if (light.type == Light3dType::Spot)
					lightIdx = spotFirstIdx + spotCount++;
{'fraction_non_alphanumeric': 0.12355309511826874, 'fraction_numerical': 0.025079684616675056, 'mean_word_length': 4.444292237442922, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7310381', 'n_tokens_mistral': 5053, 'n_tokens_neox': 4346, 'n_words': 913}