// Copyright (C) 2011 by The Alexandra Institute Ltd.
// 
// 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.

#version 330

precision mediump float;

// Attributes
in vec3 kTexcoord;		// Texcoord
in vec3 kMatTexcoord;	// Material texcoord

// Propagation grid RGB channel output
out vec4 FRAGDATA0;
out vec4 FRAGDATA1;
out vec4 FRAGDATA2;

// Accumulation grid RGB channel output
out vec4 FRAGDATA3;
out vec4 FRAGDATA4;
out vec4 FRAGDATA5;

// Uniforms
uniform float fCellWidth;	// World-space grid cell size

// Intensity input
uniform sampler3D kIntensityRed;
uniform sampler3D kIntensityGreen;
uniform sampler3D kIntensityBlue;

uniform sampler3D kPhase;	// Material HG phase function
uniform sampler3D kAbsScat;	// Material absorption and scattering coefficients

uniform float fTexOffset;	// Texture-space grid cell size
uniform float fPropScale;	// For artist directability

// Constants
const mat4 px = mat4(0.166666666660684, 0.0, 0.0, -0.036816795777334,
                     0.0, 0.069828636319029, 0.0, 0.0,
                     0.0, 0.0, 0.069828636319029, 0.0,
                     -0.239943780012009, 0.0, 0.0, 0.061658794702436);

const mat4 mx = mat4(0.166666666660684, 0.0, 0.0, 0.036816795777334,
                     0.0, 0.069828636319029, 0.0, 0.0,
                     0.0, 0.0, 0.069828636319029, 0.0,
                     0.239943780012009, 0.0, 0.0, 0.061658794702436);

const mat4 py = mat4(0.166666666660684, -0.036816795777334, 0.0, 0.0,
                     -0.239943780012009, 0.061658794702436, 0.0, 0.0, 
                     0.0, 0.0, 0.069828636319029, 0.0,
                     0.0, 0.0, 0.0, 0.069828636319029);

const mat4 my = mat4(0.166666666660684, 0.036816795777334, 0.0, 0.0,
                     0.239943780012009, 0.061658794702436, 0.0, 0.0, 
                     0.0, 0.0, 0.069828636319029, 0.0,
                     0.0, 0.0, 0.0, 0.069828636319029);

const mat4 pz = mat4(0.166666666660684, 0.0, -0.036816795777334, 0.0,
                     0.0, 0.069828636319029, 0.0, 0.0, 
                     -0.239943780012009, 0.0, 0.061658794702436, 0.0,
                     0.0, 0.0, 0.0, 0.069828636319029);

const mat4 mz = mat4(0.166666666660684, 0.0, 0.036816795777334, 0.0,
                     0.0, 0.069828636319029, 0.0, 0.0, 
                     0.239943780012009, 0.0, 0.061658794702436, 0.0,
                     0.0, 0.0, 0.0, 0.069828636319029);

// Shader code
void main()
{
	// Find sample offsets
	vec3 kUp	= kTexcoord + vec3(0.0, fTexOffset, 0.0);
	vec3 kDown	= kTexcoord - vec3(0.0, fTexOffset, 0.0);
	vec3 kFront	= kTexcoord + vec3(0.0, 0.0, fTexOffset);
	vec3 kBack	= kTexcoord - vec3(0.0, 0.0, fTexOffset);
	vec3 kRight	= kTexcoord + vec3(fTexOffset, 0.0, 0.0);
	vec3 kLeft	= kTexcoord - vec3(fTexOffset, 0.0, 0.0);

	// Sample intensity
	vec4 kUpSH[3];
	vec4 kDownSH[3];
	vec4 kFrontSH[3];
	vec4 kBackSH[3];
	vec4 kRightSH[3];
	vec4 kLeftSH[3];
	
	kUpSH[0]	= texture(kIntensityRed, kUp);
	kDownSH[0]	= texture(kIntensityRed, kDown);
	kRightSH[0]	= texture(kIntensityRed, kRight);
	kLeftSH[0]	= texture(kIntensityRed, kLeft);
	kFrontSH[0]	= texture(kIntensityRed, kFront);
	kBackSH[0]	= texture(kIntensityRed, kBack);
	
	kUpSH[1]	= texture(kIntensityGreen, kUp);
	kDownSH[1]	= texture(kIntensityGreen, kDown);
	kRightSH[1]	= texture(kIntensityGreen, kRight);
	kLeftSH[1]	= texture(kIntensityGreen, kLeft);
	kFrontSH[1]	= texture(kIntensityGreen, kFront);
	kBackSH[1]	= texture(kIntensityGreen, kBack);
	
	kUpSH[2]	= texture(kIntensityBlue, kUp);
	kDownSH[2]	= texture(kIntensityBlue, kDown);
	kRightSH[2]	= texture(kIntensityBlue, kRight);
	kLeftSH[2]	= texture(kIntensityBlue, kLeft);
	kFrontSH[2]	= texture(kIntensityBlue, kFront);
	kBackSH[2]	= texture(kIntensityBlue, kBack);	
	
	// Sample material
	vec4 kP = texture(kPhase, kMatTexcoord.xyz);
	vec4 kAS = texture(kAbsScat, kMatTexcoord.xyz);

	// Perform propagation
	vec4 kRes[3];
	for (int i = 0; i < 3; i++)
	{
		kRes[i] = vec4(0.0, 0.0, 0.0, 0.0);
		
		// RIGHT (+x)
		kRes[i] += px * kRightSH[i];

		// LEFT (-x)
		kRes[i] += mx * kLeftSH[i];

		// UP (+y)
		kRes[i] += py * kUpSH[i];

		// DOWN (-y)
		kRes[i] += my * kDownSH[i];

		// FRONT (+z)
		kRes[i] += pz * kFrontSH[i];

		// BACK (-z)
		kRes[i] += mz * kBackSH[i];	

		// Redistribute intensity due to scattering
		kRes[i].yzw *= (1.0 - kAS[3]) + kAS[3] * kP[i];

		// Attenuate due to absorption
		kRes[i] *= exp(-kAS[i] * fCellWidth);

		// Inject some energy - For artist directability
		kRes[i] *= fPropScale;
	}

	// Output result
	FRAGDATA0 = kRes[0];
	FRAGDATA1 = kRes[1];
	FRAGDATA2 = kRes[2];
	FRAGDATA3 = kRes[0];
	FRAGDATA4 = kRes[1];
	FRAGDATA5 = kRes[2];
}
