function positionToArea( position, corner, area )
	{
		var x = position[0];
		var y = position[1];

		switch( corner )
		{
			case LiteGraph.CORNER_TOP_RIGHT: x = gl.canvas.width - x; break;
			case LiteGraph.CORNER_BOTTOM_LEFT: y = gl.canvas.height - y; break;
			case LiteGraph.CORNER_BOTTOM_RIGHT: x = gl.canvas.width - x; y = gl.canvas.height - y; break;
			case LiteGraph.CORNER_TOP_CENTER: x = gl.canvas.width * 0.5; break;
			case LiteGraph.CORNER_BOTTOM_CENTER: x = gl.canvas.width * 0.5; y = gl.canvas.height - y; break;
			case LiteGraph.CORNER_TOP_LEFT:
			default:
		}

		area[0] = x;
		area[1] = y;
	}
******************

function LGraphSSAO()
{
	this.addInput("depth","Texture");
	this.addInput("camera","Camera");
	this.addOutput("out","Texture");
	this.properties = { enabled: true, intensity: 1, radius: 5, precision: LGraphTexture.DEFAULT };

	this._uniforms = { 
		tDepth:0,
		samples: 64,
		u_resolution: vec2.create(),
		radius: 5,
		zNear: 0.1,
		zFar: 1000
	};
}

LGraphSSAO.widgets_info = {
	"precision": { widget:"combo", values: litegraph_texture_found ? LGraphTexture.MODE_VALUES : [] }
};

LGraphSSAO.title = "SSAO";
LGraphSSAO.desc = "Screen Space Ambient Occlusion";

LGraphSSAO.prototype.onExecute = function()
{
	var depth = this.getInputData(0);
	var camera = this.getInputData(1);

	if( !this.isOutputConnected(0) || !depth || !camera)
		return; //saves work

	var enabled = this.getInputData(2);
	if(enabled != null)
		this.properties.enabled = Boolean( enabled );

	if(this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false )
	{
		this.setOutputData(0, GL.Texture.getWhiteTexture() );
		return;
	}

	var width = depth.width;
	var height = depth.height;
	var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT;
	if (this.precision === LGraphTexture.DEFAULT)
		type = gl.UNSIGNED_BYTE;
	if(!this._tex || this._tex.width != width || this._tex.height != height || this._tex.type != type )
		this._tex = new GL.Texture( width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR });

	var shader = null;
	if(!LGraphSSAO._shader)
		LGraphSSAO._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphSSAO.pixel_shader );
	shader = LGraphSSAO._shader;

	var intensity = this.properties.intensity;

	var uniforms = this._uniforms;
	uniforms.u_resolution.set([width,height]);
	uniforms.zNear = camera.near;
	uniforms.zFar = camera.far;
	uniforms.radius = this.properties.radius;
	uniforms.strength = intensity;

	this._tex.drawTo(function() {
		gl.disable( gl.DEPTH_TEST );
		gl.disable( gl.CULL_FACE );
		gl.disable( gl.BLEND );
		depth.bind(0);
		var mesh = GL.Mesh.getScreenQuad();
		shader.uniforms( uniforms ).draw( mesh );
	});

	this.setOutputData( 0, this._tex );
}

//from https://github.com/spite/Wagner/blob/master/fragment-shaders/ssao-simple-fs.glsl
LGraphSSAO.pixel_shader = "\n\
precision mediump float;\n\
varying vec2 v_coord;\n\
uniform sampler2D tDepth;\n\
uniform vec2 u_resolution;\n\
uniform float zNear;\n\
uniform float zFar;\n\
uniform float strength;\n\
#define PI    3.14159265\n\
\n\
float width; //texture width\n\
float height; //texture height\n\
\n\
vec2 texCoord;\n\
\n\
//general stuff\n\
\n\
//user variables\n\
uniform int samples; //ao sample count //64.0\n\
uniform float radius; //ao radius //5.0\n\
\n\
float aoclamp = 0.125; //depth clamp - reduces haloing at screen edges\n\
bool noise = true; //use noise instead of pattern for sample dithering\n\
float noiseamount = 0.0002; //dithering amount\n\
\n\
float diffarea = 0.3; //self-shadowing reduction\n\
float gdisplace = 0.4; //gauss bell center //0.4\n\
\n\
bool mist = false; //use mist?\n\
float miststart = 0.0; //mist start\n\
float mistend = zFar; //mist end\n\
bool onlyAO = false; //use only ambient occlusion pass?\n\
float lumInfluence = 0.7; //how much luminance affects occlusion\n\
\n\
vec2 rand(vec2 coord) //generating noise/pattern texture for dithering\n\
{\n\
  float noiseX = ((fract(1.0-coord.s*(width/2.0))*0.25)+(fract(coord.t*(height/2.0))*0.75))*2.0-1.0;\n\
  float noiseY = ((fract(1.0-coord.s*(width/2.0))*0.75)+(fract(coord.t*(height/2.0))*0.25))*2.0-1.0;\n\
  if (noise)\n\
  {\n\
    noiseX = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233))) * 43758.5453),0.0,1.0)*2.0-1.0;\n\
    noiseY = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233)*2.0)) * 43758.5453),0.0,1.0)*2.0-1.0;\n\
  }\n\
  return vec2(noiseX,noiseY)*noiseamount;\n\
}\n\
\n\
float doMist()\n\
{\n\
  float zdepth = texture2D(tDepth,texCoord.xy).x;\n\
  float depth = -zFar * zNear / (zdepth * (zFar - zNear) - zFar);\n\
  return clamp((depth-miststart)/mistend,0.0,1.0);\n\
}\n\
\n\
float readDepth(vec2 coord)\n\
{\n\
  if (v_coord.x<0.0||v_coord.y<0.0) return 1.0;\n\
  else {\n\
    float z_b = texture2D(tDepth, coord ).x;\n\
    float z_n = 2.0 * z_b - 1.0;\n\
    return (2.0 * zNear) / (zFar + zNear - z_n * (zFar-zNear));\n\
  }\n\
}\n\
\n\
int compareDepthsFar(float depth1, float depth2) {\n\
  float garea = 2.0; //gauss bell width\n\
  float diff = (depth1 - depth2)*100.0; //depth difference (0-100)\n\
  //reduce left bell width to avoid self-shadowing\n\
  return diff<gdisplace ? 0 : 1;\n\
}\n\
\n\
float compareDepths(float depth1, float depth2)\n\
{\n\
  float garea = 2.0; //gauss bell width\n\
  float diff = (depth1 - depth2)*100.0; //depth difference (0-100)\n\
  //reduce left bell width to avoid self-shadowing\n\
  if (diff<gdisplace)\n\
  {\n\
    garea = diffarea;\n\
  }\n\
	\n\
  float gauss = pow(2.7182,-2.0*(diff-gdisplace)*(diff-gdisplace)/(garea*garea));\n\
  return gauss;\n\
}\n\
\n\
float calAO(float depth,float dw, float dh)\n\
{\n\
  float dd = (1.0-depth)*radius;\n\
\n\
  float temp = 0.0;\n\
  float temp2 = 0.0;\n\
  float coordw = v_coord.x + dw*dd;\n\
  float coordh = v_coord.y + dh*dd;\n\
  float coordw2 = v_coord.x - dw*dd;\n\
  float coordh2 = v_coord.y - dh*dd;\n\
\n\
  vec2 coord = vec2(coordw , coordh);\n\
  vec2 coord2 = vec2(coordw2, coordh2);\n\
\n\
  float cd = readDepth(coord);\n\
  int far = compareDepthsFar(depth, cd);\n\
  temp = compareDepths(depth, cd);\n\
  //DEPTH EXTRAPOLATION:\n\
  if (far > 0)\n\
  {\n\
    temp2 = compareDepths(readDepth(coord2),depth);\n\
    temp += (1.0-temp)*temp2;\n\
  }\n\
\n\
  return temp;\n\
}\n\
\n\
void main(void)\n\
{\n\
	width = u_resolution.x; //texture width\n\
	height = u_resolution.y; //texture height\n\
	texCoord = v_coord;\n\
	vec2 noise = rand(texCoord);\n\
	float depth = readDepth(texCoord);\n\
	\n\
	float w = (1.0 / width)/clamp(depth,aoclamp,1.0)+(noise.x*(1.0-noise.x));\n\
	float h = (1.0 / height)/clamp(depth,aoclamp,1.0)+(noise.y*(1.0-noise.y));\n\
	\n\
	float pw = 0.0;\n\
	float ph = 0.0;\n\
	\n\
	float ao = 0.0;\n\
	\n\
	float dl = PI * (3.0 - sqrt(5.0));\n\
	float dz = 1.0 / float(samples);\n\
	float l = 0.0;\n\
	float z = 1.0 - dz/2.0;\n\
	\n\
	for (int i = 0; i < 64; i++)\n\
	{\n\
		if (i > samples) break;\n\
		float r = sqrt(1.0 - z);\n\
		\n\
		pw = cos(l) * r;\n\
		ph = sin(l) * r;\n\
		ao += calAO(depth,pw*w,ph*h);\n\
		z = z - dz;\n\
		l = l + dl;\n\
	}\n\
\n\
\n\
  ao /= float(samples);\n\
  ao *= strength;\n\
  ao = 1.0-ao;\n\
\n\
  if (mist)\n\
  {\n\
    ao = mix(ao, 1.0, doMist());\n\
  }\n\
\n\
	vec3 final = vec3(ao); //ambient occlusion only\n\
  gl_FragColor = vec4(final,1.0);\n\
}\n\
";

LiteGraph.registerNodeType("texture/SSAO", LGraphSSAO );


/*
function LGraphDepthAwareUpscale()
{
	this.addInput("tex","Texture");
	this.addInput("lowdepth","Texture");
	this.addInput("highdepth","Texture");
	this.addInput("camera","Camera");
	this.addOutput("out","Texture");
	this.properties = { enabled: true, precision: LGraphTexture.DEFAULT };

	this._uniforms = { 
		u_color_texture:0,
		u_lowdepth_texture:1,
		u_highdepth_texture:2,
		u_lowsize: vec4.create(),
		u_highsize: vec4.create(),
		u_viewprojection: null
	};
}

LGraphDepthAwareUpscale.widgets_info = {
	"precision": { widget:"combo", values: LGraphTexture.MODE_VALUES }
};

LGraphDepthAwareUpscale.title = "DepthAware Upscale";
LGraphDepthAwareUpscale.desc = "Upscales a texture";

LGraphDepthAwareUpscale.prototype.onExecute = function()
{
	var tex = this.getInputData(0);
	var lowdepth = this.getInputData(1);
	var highdepth = this.getInputData(2);
	var camera = this.getInputData(3);

	if( !this.isOutputConnected(0) || !tex )
		return; //saves work

	var enabled = this.getInputData(4);
	if(enabled != null)
		this.properties.enabled = Boolean( enabled );

	if(this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false || !lowdepth || !highdepth || !camera )
	{
		this.setOutputData(0, tex);
		return;
	}

	var width = tex.width;
	var height = tex.height;
	var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT;
	if (this.precision === LGraphTexture.DEFAULT)
		type = tex ? tex.type : gl.UNSIGNED_BYTE;
	if(!this._tex || this._tex.width != width || this._tex.height != height || this._tex.type != type || this._tex.format != tex.format )
		this._tex = new GL.Texture( width, height, { type: type, format: tex.format, filter: gl.LINEAR });

	if(!LGraphDepthAwareUpscale._shader)
		LGraphDepthAwareUpscale._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphDepthAwareUpscale.pixel_shader );

	var shader = null;

	var uniforms = this._uniforms;
	uniforms.u_lowsize[0] = lowdepth.width; uniforms.u_lowsize[1] = lowdepth.height; 	uniforms.u_lowsize[2] = 1/lowdepth.width; uniforms.u_lowsize[3] = 1/lowdepth.height;
	uniforms.u_highsize[0] = highdepth.width; uniforms.u_highsize[1] = highdepth.height; uniforms.u_highsize[2] = 1/highdepth.width; uniforms.u_highsize[3] = 1/highdepth.height;
	uniforms.u_viewprojection = camera._viewprojection_matrix;

	this._tex.drawTo(function() {
		gl.disable( gl.DEPTH_TEST );
		gl.disable( gl.CULL_FACE );
		gl.disable( gl.BLEND );
		tex.bind(0);
		lowdepth.bind(1);
		highdepth.bind(2);
		var mesh = GL.Mesh.getScreenQuad();
		shader.uniforms( uniforms ).draw( mesh );
	});

	this.setOutputData( 0, this._tex );
}

LGraphDepthAwareUpscale.pixel_shader = "precision highp float;\n\
		\n\
		uniform sampler2D u_color_texture;\n\
		uniform sampler2D u_lowdepth_texture;\n\
		uniform sampler2D u_highdepth_texture;\n\
		varying vec2 v_coord;\n\
		uniform vec4 u_lowsize;\n\
		uniform vec4 u_highsize;\n\
		uniform mat4 u_viewprojection;\n\
		\n\
		void main() {\n\
			vec2 uv = v_coord;\n\
			vec2 low_ltuv = floor(v_coord * u_lowsize.xy) * u_lowsize.zw;\n\
			vec2 low_rbuv = ceil(v_coord * u_lowsize.xy) * u_lowsize.zw;\n\
			vec2 high_ltuv = floor(v_coord * u_highsize.xy) * u_highsize.zw;\n\
			vec2 high_rbuv = ceil(v_coord * u_highsize.xy) * u_highsize.zw;\n\
			vec4 color = texture2D(u_color_texture, uv);\n\
			float lowdepth = texture2D(u_lowdepth_texture, uv).x;\n\
			float highdepth = texture2D(u_highdepth_texture, uv).x;\n\
			color.xyz = mix(color.xyz, u_light_color, clamp( pow(u_intensity * brightness / float(SAMPLES), u_attenuation),0.0,1.0));\n\
			gl_FragColor = color;\n\
		}\n\
		";

LiteGraph.registerNodeType("texture/depthaware_upscale", LGraphDepthAwareUpscale );