/********************************************************************
 * Displacement mapping shader										*
 * by Joel Sjöqvist ("Rudolpho")									*
 *																	*
 * Included with the dynamic displacement mapping example			*
 * project for the Ziggurat (DX11 plugin) project.					*
 * This code is based on Frank D. Luna's examples given in			*
 * his book «Introduction to 3D Games Programming with DirectX 11».	*
 ********************************************************************/

// This is a constant buffer that will be set by the engine for each rendering pass
cbuffer MatrixBuffer : register(b0) {
	float4x4 View;
	float4x4 Projection;
	float4x4 ViewProj;			// Precomputed on a per-frame basis
};

// This constant buffer is used to tell the shader how much to subdivide each edge based on distance from the camera (given in
// the next cbuffer) as well as a displacement strength value.
cbuffer DisplacementSettingsBuffer : register(b1) {
	float	 MaxTessDistance;	// Maximum amount of tesselation if the camera position <= this distance from the surface
	float	 MinTessDistance;	// Minimum amount of tesselation if the camera position >= this distance from the surface
	float	 MinTessFactor;		// Minimum amount of tesselation. Set to 1 for no tesselation.
	float	 MaxTessFactor;		// Maximum amount of tesselation. The upper limit is to subdivide each primitive 32 times.
	float	 ScaleFactor;		// The height sampled from the displacement map is multiplied by this factor before being applied to the vertex position.
};

// Cbuffer used for providing the current camera position to the shader.
// Separated from the DisplacementSettingsBuffer since they have different update frequencies (the camera's position is assumed 
// to change for each frame while the displacement settings will only be changed by optional input from the user of the main program).
cbuffer CameraBuffer : register(b2) {
	float3	 EyePos;			// Position of the rendering camera in world space.
}

// Texture array, indices into said array. Could also use these to index into a sampler array but we only use the one sampler for both textures here.
#define		TM_DIFFUSE		0
#define		TM_DISPLACEMENT	1

Texture2D	TextureMap[2];
sampler		LinearSampler;

// Transform data is supplied by the Ziggurat engine using a special StructuredBuffer bound to register t24
struct sInstanceTransform {
	float4x4 World;
	float4x4 WorldViewProjection;
};

StructuredBuffer<sInstanceTransform> Transform : register(t24);


// Input from the program to the vertex shader (corresponds to the used vertex layout of the rendered mesh)
struct vs_in {
	float3		pos			: POSITION;
	float3		normal		: NORMAL;
	float2		tex			: TEXCOORD;

	uint		iId			: SV_INSTANCEID;
};

// Output from the (tessellation-enabling) vertex shader, input to the hull shader
struct vs_out {
	float3		posW		: WORLDPOS;
	float3		normalW		: NORMAL;
	float2		tex			: TEXCOORD;
	float		tessFactor	: TESS;
};

// This structure is returned by the patch-constant hull shader and is used directly by the fixed-function tessellator 
// stage to determine how much to tessellate edges and the inside of faces. The tessellator stage also uses the hull 
// shader's constant attributes as input to determine how to do tessellation. The layout of this structure is dependant 
// upon the domain attribute of the hull shader; if it is a triangle domain (ie. triangle faces are to be tessellated) 
// the below is appropriate with 3 edge tessellation factors and one inside tessellation factor. For quad domains it will 
// instead have to provide 4 edge tessellation factors and 2 inside factors (X and Y).
// While it isn't disallowed to put more data in this struct, these two system variables sets are all that the fixed function 
// tessellator stage will care about.
struct PatchTess {
	float		edgeTess[3]	: SV_TessFactor;
	float		insideTess	: SV_InsideTessFactor;
};

// Output from the hull shader, input as a stream to the domain shader
struct hs_out {
	float3		posW		: POSITION;
	float3		normalW		: NORMAL;
	float2		tex			: TEXCOORD0;
	float2		dispTex		: TEXCOORD1;
};

// Output from the domain shader, input to the pixel shader. Represents a single vertex after tessellation has been applied.
// As such it is also used for providing source vertex data directly to the pixel shader from the vsNoTess vertex shader, 
// bypassing the hull and domain shaders, when the dynamic displacement mapping is disabled.
struct ds_out {
	float4		posH		: SV_POSITION;
	float3		posW		: POSITION;
	float3		normalW		: NORMAL;
	float2		tex			: TEXCOORD;
};


/**
 * This vertex shader is to be used in a tessellation setting, that is it will read "control points" rather than vertices and 
 * then forward these to the hull shader. This discernment is transparent in the HLSL code but it can be good to point out 
 * to avoid some of the concept confusion that may otherwise ensue when reading papers or books about shaders.
 * 
 * The vertex shader (or "control point shader", since the domain shader is rather what processes all post-tessellation vertices)
 * forwards the control point's position and normal in world space, as well as determines the tessellation factor to use for the 
 * control point based on how far away it is from the camera in world space (according to the settings provided in the 
 * DisplacementSettingsBuffer).
 * As for the normals, an optimization is made converting these into world space. However if the world matrix has a non-uniform
 * scaling, this will not yield the correct results, which can be good to be aware of if some customly loaded object looks wrong 
 * with this shader.
 **/
vs_out VS(vs_in IN) {
	vs_out OUT;

	OUT.posW		= mul(float4(IN.pos, 1.0), Transform[IN.iId].World).xyz;
	// NOTE: This below world-normal computing approach only works with uniformly scaled world matrices; otherwise it should be multiplied by the inverse world transpose matrix
	OUT.normalW		= mul(IN.normal, (float3x3)Transform[IN.iId].WorldViewProjection);
	OUT.tex			= IN.tex;

	// Determine tesselation factor based on distance to the rendering camera
	float d		= distance(OUT.posW, EyePos);
	float tess	= saturate((MinTessDistance - d) / (MinTessDistance - MaxTessDistance));

	OUT.tessFactor = MinTessFactor + tess * (MaxTessFactor - MinTessFactor);

    return OUT;
}


/**
 * This is a patch-constant function.
 * It is a required component that works together with the hull shader. Whereas the hull shader outputs data based on each individual 
 * input control point, the patch constant function computes data that is shared by an entire patch (which is the same as a "face" of 
 * control points instead of vertices). Its output is required to tell the fixed function tessellator how to tessellate each individual 
 * polygon ("patch").
 * Since the last 3 indices per face refers to dominant vertices / control points sharing position with the first 3, only the first 3 
 * are relevant to the actual face and needs to be processed from here.
 **/
PatchTess HSConstant(InputPatch<vs_out, 6> patch, uint pid : SV_PrimitiveID) {
	PatchTess pt;
	const float tf		= 32.0;

	pt.edgeTess[0]		= 0.5 * (patch[1].tessFactor + patch[2].tessFactor);
	pt.edgeTess[1]		= 0.5 * (patch[2].tessFactor + patch[0].tessFactor);
	pt.edgeTess[2]		= 0.5 * (patch[0].tessFactor + patch[1].tessFactor);
	pt.insideTess		= pt.edgeTess[0];

	return pt;
}

/**
 * This is the main part of the hull shader.
 * The parts enclosed in square brackets preceding the actual shader function declaration are so called 
 * «shader attributes»; these are compile-time constants that are used to determine certain static functionality 
 * modes of the shader. As we can see, the last one is used to declare our patch-constant function by name.
 * The domain attribute tells the tessellator stage whether it should tessellate triangles, quads or isolines (it also
 * determines the layout of the tessellation factors returned by the patch-constant function as described earlier).
 * The partitioning describes how inserted (tessellated) vertices should be distributed by the tessellator. These positions 
 * are provided to the domain shader relative to each tessellated face and can be overridden from there (as we do where we
 * displace the generated vertices). There are four partitioning schemes; 
 *	» integer			- Only the integer part of the tessellation factors are considered and each edge / face is tessellated
 *						  exactly as many times as the integer part of the factors describe. The added vertices will be evenly 
 *						  distributed over the surface of the original non-tessellated face.
 *  » pow2				- Like the integer mode, however the tessellation factors are rounded to the nearest (lower) power-of-two 
 *						  integer, such that you will get no tessellation from factors 1 and 2, then 3 times for 3, 4, 5, 6, 7 and 8, 
 *						  9 times from a factor of 9 etc. This can be useful since the increase in geometry complexity will map directly 
 *						  to the mip-mapped textures.
 *  » fractional_odd	- Of course only whole edges can be added, so there is no such thing as fractional tessellation. However the 
 *						  fractional partitioning schemes will add new edges to coincide with the previously added edges (or the original 
 *						  ones) and then interpolate their positions so that they slide towards the position they would have in the integer 
 *						  partitioning mode as the fractional part approaches the next integer wrap-over.
 *						  This makes it so that the transition between different tessellation resolutions is less noticible on relatively complex 
 *						  meshes, however for less detailed meshes visual artifacts as the added vertices slide into place when the camera is moved 
 *						  can be quite annoying.
 *						  As is evident this approach needs to add two edges for each increase in tessellation. The odd version adds new edges upon 
 *						  reaching an odd integer part of the fractional tessellation factors (3, 5, 7, 9, ...).
 *  » fractional_even	- Identical to the franctional_odd partitioning scheme, except that this one adds new edges upon reaching even integer parts 
 *						  of the tessellation factors (4, 6, 8, 10, ...) instead.
 *
 * The output topology describes the topology, as well as winding order, of the outputed control points from the hull shader. In this case we're outputting 
 * a triangle list with clockwise winding.
 * The hull shader can also amplify or reduce the number of control points that passes through it. Unlike with the geometry shader the amount of output 
 * control points must be determined at compilation time, which is what the [outputcontolpoints(x)] attribute describes. If we were to output 6 control points 
 * instead that would make for two triangles in the output triangle list. However since we're only using the last 3 indices for retrieving dominant vertex data, 
 * we should only forward three control points per such 6-point patch. As can be seen from the code below we use the dominant vertices ("control points") to sample 
 * a texcoord for the displacement map only from those. If there are no shared vertices the dominant indices should be set to that of the source vertex such that 
 * p[0] = p[3], p[1] = p[4] and p[2] = p[5], thus making the below code safe in all contexts.
 * 
 * Apart from this, this particular hull shader basically works as a pass-through shader, forwarding the first 3 ("non-dominant") control points per patch to 
 * the tessellator, which will create new vertices following its format and interpolate all elements of them and then provide them as input to the domain shader.
 **/
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("HSConstant")]
hs_out HS(InputPatch<vs_out, 6> p, uint i : SV_OutputControlPointID, uint patchId : SV_PrimitiveID) {
    hs_out OUT;
    
    // Pass through shader.
    OUT.posW		= p[i].posW;
	OUT.normalW		= p[i].normalW;
	OUT.tex			= p[i].tex;

	// The displacement mapping texture coordinates are sampled from the dominant vertices; for i: 0 => 3, 1 => 4, 2 => 5
	OUT.dispTex		= p[(i % 3) + 3].tex;

    
    return OUT;
}


/**
 * The domain shader receives all output patches (faces) from the hull shader stage and is invoked for each vertex to be added 
 * through tessellation (as well as the original corner points from the hull shader). For tessellated triangles, a barycentric 
 * coordinate is provided that tells us the location of the vertex relative to the original patch (triangle face). This can be 
 * used to determine the final vertex position, normal, texture coordinates etc. through interpolation, which are the outputted 
 * by the domain shader. As such it is clear that the domain shader works like a post-tessellation vertex shader and it will indeed 
 * often contain most of the code that would be in the vertex shader for a non-tessellated mesh.
 * If the domain attribute is set to quad instead of tri, a UV coordinate will be provided for telling the vertex-to-be-created's 
 * position relative to the source patch (which would then consist of 4 control points per patch / face output by the hull shader).
 * 
 * Tessellation for the mere sake of tessellation like this is pretty pointless; all it does is add extra faces but the inserted 
 * vertices just have interpolated data mapping to the original (non-tessellated) vertices / control points. Since all data is 
 * interpolated between vertices when for each pixel sent to the pixel shader anyway, no difference would be visible.
 * There are lots of things that can be done to alter the visuals through tessellation; one main application is displacement mapping 
 * which we will use here. What we will do is maintain a separate height map texture, tessellate the mesh to get additional vertices 
 * and then offset these new vertices relative to their normals in accordance with the height value sampled from the height map at
 * the verex' displacement texture coordinates (which are sampled from dominant vertices in order to avoid problems where two overlapping 
 * vertices have different texture coordinates as described above). For the sake of the example program accompanying this shader, a scale
 * factor is provided through a constant buffer and the sampled height map values are multiplied by this value before being used as 
 * world-space offset values.
 **/
[domain("tri")]
ds_out DS(PatchTess patchTess, float3 bary : SV_DomainLocation, const OutputPatch<hs_out, 3> patch) {
    ds_out OUT;

	// Determine the position of this vertex relative to the original patch of control points it created from tessellation of
	OUT.posW	= bary.x * patch[0].posW + bary.y * patch[1].posW + bary.z * patch[2].posW;
	OUT.tex		= bary.x * patch[0].tex + bary.y * patch[1].tex + bary.z * patch[2].tex;

	float2 dTex = bary.x * patch[0].dispTex + bary.y * patch[1].dispTex + bary.z * patch[2].dispTex;

	// Displace the derived vertex by offsetting it along its normal vector in accordance with the displacement map and a scaling factor
	OUT.normalW = bary.x * patch[0].normalW + bary.y * patch[1].normalW + bary.z * patch[2].normalW;
	// Normalize interpolated normal
	OUT.normalW = normalize(OUT.normalW);

	// Determine mip level to sample the displacement map at; change for each 40 world units of distance from the camera 
	// (arbitrarily chosen distance that yields decent visual results)
	const float MipInterval = 40.0;
	float mipLevel = clamp((distance(OUT.posW, EyePos) - MipInterval) / MipInterval, 0.0, 6.0);

	// Sample displacement (height) map... 
	float h = TextureMap[TM_DISPLACEMENT].SampleLevel(LinearSampler, dTex, mipLevel).r;

	// ... And finally offset the vertex
	OUT.posW += (ScaleFactor * (h - 1.0)) * OUT.normalW;

	// Compute screen-space position from our now displaced world position
	OUT.posH = mul(float4(OUT.posW, 1.0), ViewProj);

	return OUT;
}


/**
 * The pixel shader does nothing out of the ordinary; it simply samples the diffuse texture and returns the sampled colour.
 **/
float4 PS(ds_out IN) : SV_TARGET {
	float4 diffuse = TextureMap[TM_DIFFUSE].Sample(LinearSampler, IN.tex);
	return diffuse;
}


/**
 * A secondary vertex shader used when disabling displacement mapping; set the vertex shader to this and the hull and domain 
 * shaders to null for the associated shader technique and change the primitive topology to triangle list in order to 
 * quickly disable displacement mapping for anything rendered using that technique.
 * When the hull and domain shader stages are not set, the output of the vertex shader will go directly to the pixel shader 
 * like normal (unless of course a geometry shader is set). As described above the domain shader essentially resembles the "actual" 
 * per-vertex shader when the tessellation pipeline configuration is used, which is why this vertex shader returns ds_out struct instances
 * (which is of course also the input format of the above pixel shader).
 **/
ds_out VS_NoTess(vs_in IN) {
	ds_out OUT;

	OUT.posH		= mul(float4(IN.pos, 1.0), Transform[IN.iId].WorldViewProjection);
	OUT.posW		= mul(float4(IN.pos, 1.0), Transform[IN.iId].World).xyz;
	OUT.normalW		= mul(IN.normal, (float3x3)Transform[IN.iId].World);
	OUT.tex			= IN.tex;
	
	return OUT;
}


