/**************************************************************************
 *
 * Copyright 2010 Luca Barbieri
 *
 * 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 (including the
 * next paragraph) 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
 *
 **************************************************************************/

#define INPUT_PATCH_SIZE 3
#define OUTPUT_PATCH_SIZE 3

static const float PI = 3.141592653589793238462643f;

cbuffer cb_frame
{
	float4x4 model;
	float4x4 view_proj;
	float disp_scale;
	float disp_freq;
	float tess_factor;
};

struct IA2VS
{
	float3 position : POSITION;
};

struct VS2HS
{
	float3 position : POSITION;
};

VS2HS vs(IA2VS input)
{
	VS2HS result;
	result.position = input.position;
	return result;
}

struct HS2DS_PATCH
{
	float tessouter[3] : SV_TessFactor;
	float tessinner[1] : SV_InsideTessFactor;
};

struct HS2DS
{
	float3 position : POSITION;
};

HS2DS_PATCH hs_patch(InputPatch<VS2HS, INPUT_PATCH_SIZE> ip)
{    
	HS2DS_PATCH result;

	result.tessouter[0] = result.tessouter[1] = result.tessouter[2]
		= result.tessinner[0] = tess_factor;
	return result;
}

[domain("tri")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(OUTPUT_PATCH_SIZE)]
[patchconstantfunc("hs_patch")]
HS2DS hs(InputPatch<VS2HS, INPUT_PATCH_SIZE> p, uint i : SV_OutputControlPointID)
{
	HS2DS result;
	result.position = p[i].position;
	return result;
}

struct DS2PS
{
	float4 position : SV_POSITION;
	float3 objpos : OBJPOS;
	// float3 worldpos : WORLDPOS;
	float3 objnormal : OBJNORMAL;
	float3 worldnormal : WORLDNORMAL;
};

float3 dnormf_dt(float3 f, float3 dfdt)
{
	float ff = dot(f, f);
	return (ff * dfdt - dot(f, dfdt) * f) / (ff * sqrt(ff));
}

float3 map(float3 p, float3 q, float3 r, float3 k)
{
	return normalize(p * k.x + q * k.y + r * k.z);
}

float3 dmap_du(float3 p, float3 q, float3 r, float3 k)
{
	return dnormf_dt(p * k.x + q * k.y + r * k.z, p);
}

float dispf(float v)
{
	return cos(v * disp_freq);
}

float ddispf(float v)
{
	return -sin(v * disp_freq) * disp_freq;
}

float disp(float3 k)
{
	return dispf(k.x) * dispf(k.y) * dispf(k.z);
}

float ddisp_du(float3 k)
{
	return ddispf(k.x) * dispf(k.y) * dispf(k.z);
}

float3 ddisp(float3 k)
{
	float3 f = float3(dispf(k.x), dispf(k.y), dispf(k.z));
	return float3(ddispf(k.x) * f.y * f.z, ddispf(k.y) * f.z * f.x, ddispf(k.z) * f.x * f.y);
}

[domain("tri")]
DS2PS ds(HS2DS_PATCH input, 
                    float3 k : SV_DomainLocation,
                    const OutputPatch<HS2DS, OUTPUT_PATCH_SIZE> patch)
{
	DS2PS result;

	float3 s = map(patch[0].position, patch[1].position, patch[2].position, k);
	float3 d = 1.0 + disp(s) * disp_scale;
	result.objpos = s * d;
	result.objpos /= (1.0 + disp_scale);
	float3 worldpos = mul(model, float4(result.objpos, 1.0f));
	result.position = mul(view_proj, float4(worldpos, 1.0f));
	
	float3 dd = ddisp(s) * disp_scale;

	/*
	float3 ds_du = dmap_du(patch[0].position, patch[1].position, patch[2].position, k);
	float3 ds_dv = dmap_du(patch[1].position, patch[2].position, patch[0].position, k.yzx);
	float3 ds_dw = dmap_du(patch[2].position, patch[0].position, patch[1].position, k.zxy);

	float3 ds_dU = ds_du - ds_dw;
	float3 ds_dV = ds_dv - ds_dw;

	float3 dc_dU = s * dot(dd, ds_dU) + ds_dU * d;
	float3 dc_dV = s * dot(dd, ds_dV) + ds_dV * d;
	*/

	// this should be faster
	float3 _u = normalize((abs(s.x) > abs(s.y)) ? float3(-s.z, 0, s.x) : float3(0, -s.z, s.y));	
	float3 _v = normalize(cross(s, _u));
	float3 dc_dU = s * dot(dd, _u) + _u * d;
	float3 dc_dV = s * dot(dd, _v) + _v * d;
		
	result.objnormal = normalize(cross(dc_dU, dc_dV));
	result.worldnormal = mul(model, result.objnormal);
	return result;
}

float4 ps(DS2PS input) : SV_TARGET
{
	float3 pseudoambient = float3(0.4, 0.4, 0.6);
	float3 diffuse = float3(0.6, 0.6, 0.4);
	float3 light = normalize(float3(0, 1, -1));

	float4 r;
//	r.xyz = normalize(input.objpos + 2 * input.objnormal);
	r.xyz = pseudoambient * saturate(dot(normalize(input.objnormal), normalize(input.objpos)));
	r.xyz += saturate(dot(light, normalize(input.worldnormal))) * diffuse;

	r.w = 1;
	return r;
}
