got rid of a nagging warning

got rid of a nagging warning

<?
--[[
I'm using from Dullemond:
dt = cfl*min(dx / (|lambdaMax| - |lambdaMin| + epsilon)
for cartesian grid: dx = grid_dx
for curvilinear grid: dx = cell_dx() (which includes metric influence, right?)
for mesh: dx = faceArea (should I use cellDist? volume? etc?)

but "I Do Like CFD" uses for mesh:
dt = cfl*min(dx/(.5*wsn))
dx = cell.volume
edu2d_module_ccfv_residual.f90: compute_residual: it mentions "wsn = the sum of (max_wave_speed)*(face length))"
	calls interface_flux:
	in edu2d_module_flux.f90: "wsn = maximum wave speed (eigenvalue)"
		interface_flux code: wsn = abs(qn) + a = |v_n| + cs = |lambdaMax|
		this is then returned as the varaible 'wave_speed'
		and then wsn(cellIndex) = sum of face[i]'s area * face[i] flux max wavespeed
--]]

-- set to false to use cell-centered U for lambda for dt
-- set to true to use face-averaged U for lambda for dt
-- TODO do this option for the gridsolver above as well
local calcDTFromFaceU = true

-- TODO put all the epsilons in one place?
--local faceAreaEpsilon = 1e-7
--local cellVolumeEpsilon = 1e-7
local faceAreaEpsilon = 0
local cellVolumeEpsilon = 0

?>
//// MODULE_NAME: <?=calcDTCell?>
//// MODULE_DEPENDS: <?=solver_t?> <?=cons_t?> <?=cell_t?> <?=normal_t?> <?=eqn_waveCode_depends?> <?=SETBOUNDS?> <?=cell_dx_i?>

<? if not require "hydro.solver.meshsolver":isa(solver) then ?>

#define <?=calcDTCell?>(\
	/*real * const */dt,\
	/*constant <?=solver_t?> const * const */solver,\
	/*global <?=cons_t?> const * const */U,\
	/*global <?=cell_t?> const * const */cell\
) {\
	<?=eqn:consWaveCodeMinMaxAllSidesPrefix{ --\
		U = "U", --\
		pt = "(cell)->pos", --\
	}:gsub("\\*\n", "\\\n\t")?>\
	<? for side=0,solver.dim-1 do ?>{\
<? --\
if solver.coord.vectorComponent == "holonomic" --\
or require "hydro.coord.cartesian":isa(solver.coord) --\
then --\
?>		real const dx = solver->grid_dx.s<?=side?>;\
<? else --\
?>		real const dx = cell_dx<?=side?>((cell)->pos);\
<? end --\
?>		if (dx > <?=clnumber(faceAreaEpsilon)?>) {\
			<?=normal_t?> const n = normal_forSide<?=side?>((cell)->pos);\
			/* use cell-centered eigenvalues */\
			<?=eqn:consWaveCodeMinMaxAllSides{ --\
				n = "n", --\
				U = "U", --\
				pt = "(cell)->pos", --\
				resultMin = "lambdaMin", --\
				resultMax = "lambdaMax", --\
				declare = true, --\
			}:gsub("\\*\n", "\\\n\t\t\t")?>\
			real absLambdaMax = max(fabs(lambdaMin), fabs(lambdaMax));\
			absLambdaMax = max((real)1e-9, absLambdaMax);\
			*(dt) = (real)min(*(dt), dx / absLambdaMax);\
		}\
	}<? end ?>\
}

<? else -- meshsolver ?>
//// MODULE_DEPENDS: <?=face_t?>

<? if calcDTFromFaceU then ?>
//// MODULE_DEPENDS: <?=getEdgeStates?> <?=eigen_forInterface?>
<? end ?>

#define <?=calcDTCell?>(\
	/*real * const */dt,\
	/*constant <?=solver_t?> const * const */solver,\
	/*global <?=cons_t?> const * const */U,\
	/*global <?=cell_t?> const * const */cell,\
	/*global <?=face_t?> const * const */faces,		/* [numFaces] */\
	/*global int const * const */cellFaceIndexes	/* [numCellFaceIndexes] */\
) {\
	if (cell->volume > <?=clnumber(cellVolumeEpsilon)?>) {\
<? if not calcDTFromFaceU then -- cell-centered lambdas ?>\
		<?=eqn:consWaveCodeMinMaxAllSidesPrefix{ --\
			U = "U", --\
			pt = "(cell)->pos", --\
		}:gsub("\\*\n", "\\\n\t")?>\
<? else -- face-centered lambdas ?>\
		/* eqn:eigenWaveCodeMinMax doesn't use a prefix (right?) */\
<? end ?>\
		for (int i = 0; i < (cell)->faceCount; ++i) {\
			global <?=face_t?> const * const face = faces + cellFaceIndexes[i + (cell)->faceOffset];\
			if (face->area > <?=clnumber(faceAreaEpsilon)?>) {\
				if (face->cells.x != -1 && face->cells.y != -1) {\
					<?=normal_t?> const n = normal_forFace(face);\
					/* all sides? or only the most prominent side? */\
					/* which should we pick eigenvalues from? */\
					/* use cell-centered eigenvalues */\
<? if not calcDTFromFaceU then -- cell-centered lambdas ?>\
					<?=eqn:consWaveCodeMinMaxAllSides{ --\
						n = "n", --\
						U = "U", --\
						pt = "(cell)->pos", --\
						resultMin = "lambdaMin", --\
						resultMax = "lambdaMax", --\
						declare = true, --\
					}:gsub("\\*\n", "\\\n\t\t\t\t\t")?>\
<? else -- face-centered lambdas ?>\
					/* copied from meshsolver.cl's calcFlux() ... */\
					<?=cell_t?> cellL, cellR;\
					<?=cons_t?> UL, UR;\
					<?=getEdgeStates?>(solver, &UL, &UR, cellL, cellR, face, UBuf);\
					<?=eigen_t?> eig;\
					<?=eigen_forInterface?>(&eig, solver, &UL, &UR, &cellL, &cellR, face->pos, n);\
					<?=eqn:eigenWaveCodeMinMax{ --\
						eig = "&eig", --\
						n = "n", --\
						pt = "face->pos", --\
						resultMin = "lambdaMin", --\
						resultMax = "lambdaMax", --\
						declare = true, --\
					}:gsub("\\*\n", "\\\n\t\t\t\t\t")?>\
<? end ?>\
					real absLambdaMax = max(fabs(lambdaMin), fabs(lambdaMax));\
					absLambdaMax = max((real)1e-9, absLambdaMax);\
					*(dt) = (real)min(*(dt), (cell)->volume / (face->area * absLambdaMax));\
				}\
			}\
		}\
	}\
}

<? end -- meshsolver ?>

<?
-- set to false to use cell-centered U for lambda for dt
-- set to true to use face-averaged U for lambda for dt
-- TODO do this option for the gridsolver above as well
local calcDTFromFaceU = true

-- TODO put all the epsilons in one place?
--local faceAreaEpsilon = 1e-7
--local cellVolumeEpsilon = 1e-7
local faceAreaEpsilon = 0
local cellVolumeEpsilon = 0
?>

/*
I'm using from Dullemond:
dt = cfl*min(dx / (|lambdaMax| - |lambdaMin| + epsilon)
for cartesian grid: dx = grid_dx
for curvilinear grid: dx = cell_dx() (which includes metric influence, right?)
for mesh: dx = faceArea (should I use cellDist? volume? etc?)

but "I Do Like CFD" uses for mesh:
dt = cfl*min(dx/(.5*wsn))
dx = cell.volume
edu2d_module_ccfv_residual.f90: compute_residual: it mentions "wsn = the sum of (max_wave_speed)*(face length))"
	calls interface_flux:
	in edu2d_module_flux.f90: "wsn = maximum wave speed (eigenvalue)"
		interface_flux code: wsn = abs(qn) + a = |v_n| + cs = |lambdaMax|
		this is then returned as the varaible 'wave_speed'
		and then wsn(cellIndex) = sum of face[i]'s area * face[i] flux max wavespeed
*/

//// MODULE_NAME: <?=calcDTCell?>
//// MODULE_DEPENDS: <?=solver_t?> <?=cons_t?> <?=cell_t?> <?=normal_t?> <?=eqn_waveCode_depends?> <?=SETBOUNDS?> <?=cell_dx_i?>

<? if not require "hydro.solver.meshsolver":isa(solver) then ?>

#define <?=calcDTCell?>(\
	/*real * const */dt,\
	/*constant <?=solver_t?> const * const */solver,\
	/*global <?=cons_t?> const * const */U,\
	/*global <?=cell_t?> const * const */cell\
) {\
	<?=eqn:consWaveCodeMinMaxAllSidesPrefix{ --\
		U = "U", --\
		pt = "(cell)->pos", --\
	}:gsub("\\*\n", "\\\n\t")?>\
	<? for side=0,solver.dim-1 do ?>{\
<? --\
if solver.coord.vectorComponent == "holonomic" --\
or require "hydro.coord.cartesian":isa(solver.coord) --\
then --\
?>		real const dx = solver->grid_dx.s<?=side?>;\
<? else --\
?>		real const dx = cell_dx<?=side?>((cell)->pos);\
<? end --\
?>		if (dx > <?=clnumber(faceAreaEpsilon)?>) {\
			<?=normal_t?> const n = normal_forSide<?=side?>((cell)->pos);\
			/* use cell-centered eigenvalues */\
			<?=eqn:consWaveCodeMinMaxAllSides{ --\
				n = "n", --\
				U = "U", --\
				pt = "(cell)->pos", --\
				resultMin = "lambdaMin", --\
				resultMax = "lambdaMax", --\
				declare = true, --\
			}:gsub("\\*\n", "\\\n\t\t\t")?>\
			real absLambdaMax = max(fabs(lambdaMin), fabs(lambdaMax));\
			absLambdaMax = max((real)1e-9, absLambdaMax);\
			*(dt) = (real)min(*(dt), dx / absLambdaMax);\
		}\
	}<? end ?>\
}

<? else -- meshsolver ?>
//// MODULE_DEPENDS: <?=face_t?>

<? if calcDTFromFaceU then ?>
//// MODULE_DEPENDS: <?=getEdgeStates?> <?=eigen_forInterface?>
<? end ?>

#define <?=calcDTCell?>(\
	/*real * const */dt,\
	/*constant <?=solver_t?> const * const */solver,\
	/*global <?=cons_t?> const * const */U,\
	/*global <?=cell_t?> const * const */cell,\
	/*global <?=face_t?> const * const */faces,		/* [numFaces] */\
	/*global int const * const */cellFaceIndexes	/* [numCellFaceIndexes] */\
) {\
	if (cell->volume > <?=clnumber(cellVolumeEpsilon)?>) {\
<? if not calcDTFromFaceU then -- cell-centered lambdas ?>\
		<?=eqn:consWaveCodeMinMaxAllSidesPrefix{ --\
			U = "U", --\
			pt = "(cell)->pos", --\
		}:gsub("\\*\n", "\\\n\t")?>\
<? else -- face-centered lambdas ?>\
		/* eqn:eigenWaveCodeMinMax doesn't use a prefix (right?) */\
<? end ?>\
		for (int i = 0; i < (cell)->faceCount; ++i) {\
			global <?=face_t?> const * const face = faces + cellFaceIndexes[i + (cell)->faceOffset];\
			if (face->area > <?=clnumber(faceAreaEpsilon)?>) {\
				if (face->cells.x != -1 && face->cells.y != -1) {\
					<?=normal_t?> const n = normal_forFace(face);\
					/* all sides? or only the most prominent side? */\
					/* which should we pick eigenvalues from? */\
					/* use cell-centered eigenvalues */\
<? if not calcDTFromFaceU then -- cell-centered lambdas ?>\
					<?=eqn:consWaveCodeMinMaxAllSides{ --\
						n = "n", --\
						U = "U", --\
						pt = "(cell)->pos", --\
						resultMin = "lambdaMin", --\
						resultMax = "lambdaMax", --\
						declare = true, --\
					}:gsub("\\*\n", "\\\n\t\t\t\t\t")?>\
<? else -- face-centered lambdas ?>\
					/* copied from meshsolver.cl's calcFlux() ... */\
					<?=cell_t?> cellL, cellR;\
					<?=cons_t?> UL, UR;\
					<?=getEdgeStates?>(solver, &UL, &UR, cellL, cellR, face, UBuf);\
					<?=eigen_t?> eig;\
					<?=eigen_forInterface?>(&eig, solver, &UL, &UR, &cellL, &cellR, face->pos, n);\
					<?=eqn:eigenWaveCodeMinMax{ --\
						eig = "&eig", --\
						n = "n", --\
						pt = "face->pos", --\
						resultMin = "lambdaMin", --\
						resultMax = "lambdaMax", --\
						declare = true, --\
					}:gsub("\\*\n", "\\\n\t\t\t\t\t")?>\
<? end ?>\
					real absLambdaMax = max(fabs(lambdaMin), fabs(lambdaMax));\
					absLambdaMax = max((real)1e-9, absLambdaMax);\
					*(dt) = (real)min(*(dt), (cell)->volume / (face->area * absLambdaMax));\
				}\
			}\
		}\
	}\
}

<? end -- meshsolver ?>


cylindre bug, enregistrer angles pour les matrices

#include "kernel_header.h"
#include "kernel_color.h"
#include "kernel_data.h"
#include "kernel_vector.h"
#include "kernel_debug.h"

float3						rotat_zyx(float3 vect, float pitch, float yaw, float roll)
{
	float3					res;
	float					rad_pitch = pitch * DEG2RAD;
	float					rad_yaw = yaw * DEG2RAD;
	float					rad_roll = roll * DEG2RAD;

	res.x = vect.x * cos(rad_roll) * cos(rad_yaw) + vect.y * (cos(rad_pitch) * -sin(rad_roll) + cos(rad_roll) * sin(rad_yaw) * sin(rad_pitch)) + vect.z * (-sin(rad_roll) * -sin(rad_pitch) + cos(rad_roll) * sin(rad_yaw) * cos(rad_pitch));
	res.y = vect.x * sin(rad_roll) * cos(rad_yaw) + vect.y * (cos(rad_roll) * cos(rad_pitch) + sin(rad_roll) * sin(rad_yaw) * sin(rad_pitch)) + vect.z * (cos(rad_roll) * -sin(rad_pitch) + sin(rad_roll) * sin(rad_yaw) * cos(rad_pitch));
	res.z = vect.x * -sin(rad_yaw) + vect.y * cos(rad_yaw) * sin(rad_pitch) + vect.z * cos(rad_yaw) * cos(rad_pitch);
	return (res);
}

float3						rotat_xyz(float3 vect, float pitch, float yaw, float roll)
{
	float3					res;
	float					rad_pitch = pitch * DEG2RAD;
	float					rad_yaw = yaw * DEG2RAD;
	float					rad_roll = roll * DEG2RAD;

	res.x = vect.x * cos(rad_yaw) * cos(rad_roll) + vect.y * cos(rad_yaw) * -sin(rad_roll) + vect.z * sin(rad_yaw);
	res.y = vect.x * (-sin(rad_pitch) * -sin(rad_yaw) * cos(rad_roll) + cos(rad_pitch) * sin(rad_roll)) + vect.y * (-sin(rad_pitch) * -sin(rad_yaw) * -sin(rad_roll) + cos(rad_pitch) * cos(rad_roll)) + vect.z * cos(rad_yaw) * -sin(rad_pitch);
	res.z = vect.x * (cos(rad_pitch) * -sin(rad_yaw) * cos(rad_roll) + sin(rad_pitch) * sin(rad_roll)) + vect.y * (cos(rad_pitch) * -sin(rad_yaw) * -sin(rad_roll) + sin(rad_pitch) * cos(rad_roll)) + vect.z * cos(rad_yaw) * cos(rad_pitch);
	return (res);
}

float3					get_sphere_abc(float radius, float3 ray, float3 origin)
{
	float3		abc;

	abc.x = dot(ray, ray);
	abc.y = 2 * dot(ray, origin);
	abc.z = dot(origin, origin) - (radius * radius);
	return (abc);
}

float					inter_sphere(t_sphere sphere, float3 ray, float3 origin)
{
	float3				abc;
	float				d;
	float				res1;
	float				res2;

	origin -= sphere.pos;
	abc = get_sphere_abc(sphere.radius, ray, origin);
	d = (abc.y * abc.y) - (4 * (abc.x * abc.z));
	if (d < 0)
		return (0);
	if (d == 0)
		return ((-abc[1]) / (2 * abc[0]));
	res1 = (((-abc[1]) + sqrt(d)) / (2 * abc[0]));
	res2 = (((-abc[1]) - sqrt(d)) / (2 * abc[0]));
	if ((res1 < res2 && res1 > 0) || (res1 > res2 && res2 < 0))
		return (res1);
	return (res2);
}

float3					get_cylinder_abc(float radius, float3 dir, float3 ray, float3 origin)
{
	float3		abc;

	// SEMBLE OK
	abc.x = dot(ray, ray) - (dot(ray, dir) * dot(ray, dir));
	abc.y = 2 * (dot(ray, origin) - (dot(ray, dir) * dot(origin, dir)));
	abc.z = dot(origin, origin) - (dot(origin, dir) * dot(origin, dir)) - (radius * radius);
	return (abc);
}

float					inter_cylinder(t_cylinder cylind, float3 ray, float3 origin)
{
	float3				abc;
	float				d;
	float				res1 = 0;
	float				res2 = 0;
	float				m;

	origin -= cylind.pos;
	abc = get_cylinder_abc(cylind.radius, normalize(cylind.dir), ray, origin);
	d = (abc.y * abc.y) - (4 * (abc.x * abc.z));
	if (d < 0)
		return (0);
	if (d == 0)
		res1 = (-abc[1]) / (2 * abc[0]);
	else
	{
		res1 = (((-abc[1]) + sqrt(d)) / (2 * abc[0]));
		res2 = (((-abc[1]) - sqrt(d)) / (2 * abc[0]));
	}
	if (res1 < 0 && res2 < 0)
		return (0);
	if ((res1 < res2 && res1 > 0) || (res1 > res2 && res2 < 0))
	{
		if (cylind.height == 0 || (dot(ray, fast_normalize(cylind.dir) * res1 +
			dot(origin, fast_normalize(cylind.dir))) < cylind.height && dot(ray, fast_normalize(cylind.dir) * res1 +
			dot(origin, fast_normalize(cylind.dir))) > 0))
			return (res1);
	}
	if (cylind.height ==  0 || (dot(ray, fast_normalize(cylind.dir) * res2 +
			dot(origin, fast_normalize(cylind.dir))) < cylind.height && dot(ray, fast_normalize(cylind.dir) * res2 +
			dot(origin, fast_normalize(cylind.dir))) > 0))
		return (res2);
	else
		return (0);
}

float3					get_cone_abc(t_cone cone, float3 ray, float3 origin)
{
	float3		abc = 0;
	float		k = cone.angle * DEG2RAD;
	k = tan(k);
	k = 1 + k * k;

	abc.x = dot(ray, ray) - (k * (dot(ray, cone.dir) * dot(ray, cone.dir)));
	abc.y = 2 * (dot(ray, origin) - (k * \
			(dot(ray, cone.dir) * dot(origin, cone.dir))));
	abc.z = (dot(origin, origin) - \
			(k * (dot(origin, cone.dir) * dot(origin, cone.dir))));
	return (abc);
}

float					inter_cone(t_cone cone, float3 ray, float3 origin)
{
	float3				abc;
	float				d;
	float				res1;
	float				res2;
	float				res;

	origin -= cone.pos;
	abc = get_cone_abc(cone, ray, origin);
	d = (abc.y * abc.y) - (4 * (abc.x * abc.z));
	if (d < 0)
		return (0);
	if (d == 0)
		res = (-abc[1]) / (2 * abc[0]);
	res1 = (((-abc[1]) + sqrt(d)) / (2 * abc[0]));
	res2 = (((-abc[1]) - sqrt(d)) / (2 * abc[0]));
	if ((res1 < res2 && res1 > 0) || (res1 > res2 && res2 < 0))
		res = res1;
	else
		res = res2;
	return (res);
}

float					inter_plan(t_plane plane, float3 ray, float3 origin)
{
	float				t;

	if ((t = dot(fast_normalize(ray), fast_normalize(plane.normale))) == 0)
		return (0);
	t = (dot(plane.pos - origin, fast_normalize(plane.normale))) / t;
		if (t < 0.001)
		return (0);
	return (t);
}

t_hit			ray_hit(float3 origin, float3 ray, t_scene scene)
{
	unsigned int			i = 0;
	int			max = get_max_obj(PARAM);
	t_hit		hit;
	float		dist = 0;

	hit.dist = 0;
	hit.type = 0;
	hit.id = 0;
	hit.pos = (0, 0, 0);
	hit.normale = (0, 0, 0);
	while (i < max)
	{
		if (i < PARAM->n_cones)
			if (((dist = inter_cone(CONES[i], ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 1;
				hit.id = i;
			}
		if (i < PARAM->n_cylinders)
			if (((dist = inter_cylinder(CYLIND[i], ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 2;
				hit.id = i;
			}
		if (i < PARAM->n_planes)
			if (((dist = inter_plan(PLANE[i], ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 4;
				hit.id = i;
			}
		if (i < PARAM->n_spheres)
			if (((dist = inter_sphere(SPHERE[i], ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 5;
				hit.id = i;
			}
		i++;
	}
	return (hit);
}

float3			rotate_matrix(float angle, float3 v)
{
	float3		res;

	res.x = v.x;
	res.y = (cos(angle) * v.y) - (sin(angle) * v.z);
	res.z = (sin(angle) * v.y) + (cos(angle) * v.z);
	return (res);
}

float3			get_hit_normale(t_scene scene, t_hit hit)
{
	float3		res;

	if (hit.type == 1)
	{
		float k = CONES[hit.id].angle * DEG2RAD;
		k = tan(k);
		k = 1 + k * k;
		res = dot(-scene.ray, fast_normalize(CONES[hit.id].dir)) * \
			hit.dist - dot(ACTIVECAM.pos + PARAM->mvt - CONES[hit.id].pos, fast_normalize(CONES[hit.id].dir));
		res = ((hit.pos - fast_normalize(CONES[hit.id].pos)) - (k * fast_normalize(CONES[hit.id].dir) * res)) * -1;
	}
	else if (hit.type == 2)
	{
		/*float3	base = CYLIND[hit.id].pos;
		res = hit.pos;

		res = rotate_matrix(45 * DEG2RAD, res);
		base.z = res.z;
		base = rotate_matrix(-45 * DEG2RAD, base);
		res = rotate_matrix(-45 * DEG2RAD, res);
		res -= base;*/
//		res = dot(-scene.ray, fast_normalize(CYLIND[hit.id].dir) * hit.dist + \
//			dot(ACTIVECAM.pos + PARAM->mvt - CYLIND[hit.id].pos, fast_normalize(CYLIND[hit.id].dir)));
//		res = hit.pos - CYLIND[hit.id].pos - fast_normalize(CYLIND[hit.id].dir) * res;

	/*	res = fast_normalize(hit.pos - CYLIND[hit.id].pos);
	// fonctionne pour cylindre aligné en Z
		if (dot(fast_normalize(CYLIND[hit.id].dir), res) < 0)
		{
			res += fast_normalize(CYLIND[hit.id].dir);
			res.z += 1;
		}
		else
		{
			res -= fast_normalize(CYLIND[hit.id].dir);
			res.z += 1;
		}*/

		float3	x = fast_normalize(CYLIND[hit.id].dir);
		float3	y = x;
		float3	base = 0;

		base.z = 1;
		x.y = 0;
		x.z = 0;
		y.x = 0;
		y.z = 0;
		x = fast_normalize(x);
		y = fast_normalize(y);
		float pitch = acos(dot(x, base));
		float yaw = acos(dot(y, base));
		res = rotat_zyx(hit.pos, pitch * DEG2RAD, yaw * DEG2RAD, 0);
		res -= rotat_zyx(CYLIND[hit.id].pos, pitch * DEG2RAD, yaw * DEG2RAD, 0);
		res.z = 0;
		res = rotat_zyx(hit.pos, -pitch * DEG2RAD, -yaw * DEG2RAD, 0);
	}
	else if (hit.type == 4)
	{
		if (dot(PLANE[hit.id].normale, -scene.ray) < 0)
			res = -PLANE[hit.id].normale;
		else
			res = PLANE[hit.id].normale;
	}
	else if (hit.type == 5)
		res = hit.pos - SPHERE[hit.id].pos;
	return (fast_normalize(res));
}

unsigned int			phong(t_hit hit, t_scene scene)
{
	int					i = -1;
	unsigned int		obj_color = get_obj_hue(scene, hit);
	unsigned int		ambient_color = get_ambient(obj_color, scene);
	unsigned int		res_color = ambient_color;
	float				tmp = 0;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit;

	while (++i < PARAM->n_lights)
	{
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(hit.pos, light_ray.dir, scene);
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		;
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(hit, scene, res_color, tmp);
			reflect = fast_normalize(mult_fvect(2.0 * dot(hit.normale, light_ray.dir), hit.normale) - light_ray.dir);
			tmp = dot(reflect, -scene.ray);
			if (tmp > 0)
				res_color = color_specular(hit, scene, res_color, tmp);
		}
	}
	return (res_color);
}

unsigned int			phong2(t_hit hit, t_scene scene)
{
	int					i = -1;
	unsigned int		obj_color = get_obj_hue(scene, hit);
	unsigned int		ambient_color = get_ambient(obj_color, scene);
	unsigned int		res_color = ambient_color;
	float				tmp = 0;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit;

	while (++i < PARAM->n_lights)
	{
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(hit.pos, light_ray.dir, scene);
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		;
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(hit, scene, res_color, tmp);
			reflect = fast_normalize(mult_fvect(2.0 * dot(hit.normale, light_ray.dir), hit.normale) - light_ray.dir);
			tmp = dot(reflect, -scene.ray);
			if (tmp > 0)
				res_color = color_specular(hit, scene, res_color, tmp);
		}
	}
	return (res_color);
}

unsigned int		bounce(t_scene scene, t_hit old_hit, int depth)
{
	unsigned int	color = 0;
	float3			reflex = 0;
	t_hit			new_hit;
	while (depth > 0)
	{
//		printf("IM REFLECTING!\n");
		reflex = fast_normalize(scene.ray - mult_fvect((2 * dot(old_hit.normale, scene.ray)), old_hit.normale));
		new_hit.dist = MAX_DIST;
		new_hit = ray_hit(old_hit.pos, reflex, scene);
		if (new_hit.dist > 0 && new_hit.dist < MAX_DIST)
		{
			new_hit.pos = mult_fvect(new_hit.dist, reflex) + old_hit.pos;
			new_hit.normale = get_hit_normale(scene, new_hit);
			new_hit.pos = new_hit.pos + ((new_hit.dist / 100) * new_hit.normale);
			color += 0.1 * phong2(new_hit, scene);
		}
		depth--;
	}
	return (color);
}
unsigned int	get_pixel_color(t_scene scene)
{
	t_hit			hit;
	int				depth = PARAM->depth;
	unsigned int	color  = 0;
	unsigned int	bounce_color = 0;

	hit.dist = MAX_DIST;
	hit = ray_hit((ACTIVECAM.pos + PARAM->mvt), scene.ray, scene);
	if (hit.dist > 0 && hit.dist < MAX_DIST) // ajout d'une distance max pour virer acnee mais pas fiable a 100%
	{
		hit.pos = mult_fvect(hit.dist, scene.ray) + (ACTIVECAM.pos + PARAM->mvt);
		hit.normale = get_hit_normale(scene, hit);
		hit.pos = hit.pos + ((hit.dist / 100) * hit.normale);
		color = phong(hit, scene);
		if (depth > 0)
			bounce_color = bounce(scene, hit, depth);
//		return (color + bounce_color);
		return (blend_add(color, 0.8*bounce_color));
	}
	return (get_ambient(BACKCOLOR, scene));
}

float3						get_ray_cam(t_cam cam, t_scene scene, int x, int y)
{
	float3					cam_ray = 0;
	float					ratio = (float)PARAM->win_w / (float)PARAM->win_h;

	cam_ray.x = ((2 * ((x + 0.5) / PARAM->win_w)) - 1) * ratio * (tan((cam.fov / 2) * DEG2RAD));
	cam_ray.y = ((1 - (2 * ((y + 0.5) / PARAM->win_h))) * tan((cam.fov / 2) * DEG2RAD));
	cam_ray.z = 1;
/*  rotation XYZ
	cam_ray.x = xx * cos(yaw) * cos(roll) + yy * cos(yaw) * -sin(roll) + sin(yaw);
	cam_ray.y = xx * (-sin(pitch) * -sin(yaw) * cos(roll) + cos(pitch) * sin(roll)) + yy * (-sin(pitch) * -sin(yaw) * -sin(roll) + cos(pitch) * cos(roll)) + cos(yaw) * -sin(pitch);
	cam_ray.z = xx * (cos(pitch) * -sin(yaw) * cos(roll) + sin(pitch) * sin(roll)) + yy * (cos(pitch) * -sin(yaw) * -sin(roll) + sin(pitch) * cos(roll)) + cos(yaw) * cos(pitch);

// rotation ZYX
	cam_ray.x = xx * cos(roll) * cos(yaw) + yy * (cos(pitch) * -sin(roll) + cos(roll) * sin(yaw) * sin(pitch)) + (-sin(roll) * -sin(pitch) + cos(roll) * sin(yaw) * cos(pitch));
	cam_ray.y = xx * sin(roll) * cos(yaw) + yy * (cos(roll) * cos(pitch) + sin(roll) * sin(yaw) * sin(pitch)) + (cos(roll) * -sin(pitch) + sin(roll) * sin(yaw) * cos(pitch));
	cam_ray.z = xx * -sin(yaw) + yy * cos(yaw) * sin(pitch) + cos(yaw) * cos(pitch);
calcul simplifié
	cam_ray.x = xx * cos(yaw) + yy * (sin(yaw) * sin(pitch)) + (sin(yaw) * cos(pitch));
	cam_ray.y = yy * cos(pitch) + -sin(pitch);
	cam_ray.z = xx * -sin(yaw) + yy * cos(yaw) * sin(pitch) + cos(yaw) * cos(pitch);
*/
	cam_ray = rotat_zyx(cam_ray, cam.pitch, cam.yaw, 0);
	return(normalize(cam_ray));
}

__kernel void	ray_trace(__global		char		*output,
						  				t_param		param,
						  __constant	t_cam		*cameras,
						  __constant	t_cone		*cones,
						  __constant	t_cylinder	*cylinders,
						  __constant	t_light		*lights,
						  __constant	t_plane		*planes,
						  __constant	t_sphere	*spheres,
						  __global		t_hit		*target_obj)
{
	t_scene		scene = grab_data(cameras, cones, cylinders, lights, planes, spheres, param);
	int			x = get_global_id(0);
	int			y = get_global_id(1);
	int			id = x + (PARAM->win_w * y); // NE PAS VIRER ID CAR BESOIN DANS MACRO OUTPUTE
	if (x == PARAM->mou_x && y == PARAM->mou_y)
		*target_obj = ray_hit((ACTIVECAM.pos + PARAM->mvt), get_ray_cam(ACTIVECAM, scene, PARAM->mou_x, PARAM->mou_y), scene);
	scene.ray = get_ray_cam(ACTIVECAM, scene, x ,y);
	OUTPUTE = get_pixel_color(scene);
}

#include "kernel_header.h"
#include "kernel_color.h"
#include "kernel_data.h"
#include "kernel_vector.h"
#include "kernel_debug.h"

float3						rotat_zyx(float3 vect, float pitch, float yaw, float roll)
{
	float3					res;
	float					rad_pitch = pitch * DEG2RAD;
	float					rad_yaw = yaw * DEG2RAD;
	float					rad_roll = roll * DEG2RAD;

	res.x = vect.x * cos(rad_roll) * cos(rad_yaw) + vect.y * (cos(rad_pitch) * -sin(rad_roll) + cos(rad_roll) * sin(rad_yaw) * sin(rad_pitch)) + vect.z * (-sin(rad_roll) * -sin(rad_pitch) + cos(rad_roll) * sin(rad_yaw) * cos(rad_pitch));
	res.y = vect.x * sin(rad_roll) * cos(rad_yaw) + vect.y * (cos(rad_roll) * cos(rad_pitch) + sin(rad_roll) * sin(rad_yaw) * sin(rad_pitch)) + vect.z * (cos(rad_roll) * -sin(rad_pitch) + sin(rad_roll) * sin(rad_yaw) * cos(rad_pitch));
	res.z = vect.x * -sin(rad_yaw) + vect.y * cos(rad_yaw) * sin(rad_pitch) + vect.z * cos(rad_yaw) * cos(rad_pitch);
	return (res);
}

float3						rotat_xyz(float3 vect, float pitch, float yaw, float roll)
{
	float3					res;
	float					rad_pitch = pitch * DEG2RAD;
	float					rad_yaw = yaw * DEG2RAD;
	float					rad_roll = roll * DEG2RAD;

	res.x = vect.x * cos(rad_yaw) * cos(rad_roll) + vect.y * cos(rad_yaw) * -sin(rad_roll) + vect.z * sin(rad_yaw);
	res.y = vect.x * (-sin(rad_pitch) * -sin(rad_yaw) * cos(rad_roll) + cos(rad_pitch) * sin(rad_roll)) + vect.y * (-sin(rad_pitch) * -sin(rad_yaw) * -sin(rad_roll) + cos(rad_pitch) * cos(rad_roll)) + vect.z * cos(rad_yaw) * -sin(rad_pitch);
	res.z = vect.x * (cos(rad_pitch) * -sin(rad_yaw) * cos(rad_roll) + sin(rad_pitch) * sin(rad_roll)) + vect.y * (cos(rad_pitch) * -sin(rad_yaw) * -sin(rad_roll) + sin(rad_pitch) * cos(rad_roll)) + vect.z * cos(rad_yaw) * cos(rad_pitch);
	return (res);
}

float3					get_sphere_abc(float radius, float3 ray, float3 origin)
{
	float3		abc;

	abc.x = dot(ray, ray);
	abc.y = 2 * dot(ray, origin);
	abc.z = dot(origin, origin) - (radius * radius);
	return (abc);
}

float					inter_sphere(t_sphere sphere, float3 ray, float3 origin)
{
	float3				abc;
	float				d;
	float				res1;
	float				res2;

	origin -= sphere.pos;
	abc = get_sphere_abc(sphere.radius, ray, origin);
	d = (abc.y * abc.y) - (4 * (abc.x * abc.z));
	if (d < 0)
		return (0);
	if (d == 0)
		return ((-abc[1]) / (2 * abc[0]));
	res1 = (((-abc[1]) + sqrt(d)) / (2 * abc[0]));
	res2 = (((-abc[1]) - sqrt(d)) / (2 * abc[0]));
	if ((res1 < res2 && res1 > 0) || (res1 > res2 && res2 < 0))
		return (res1);
	return (res2);
}

float3					get_cylinder_abc(float radius, float3 dir, float3 ray, float3 origin)
{
	float3		abc;

	// SEMBLE OK
	abc.x = dot(ray, ray) - (dot(ray, dir) * dot(ray, dir));
	abc.y = 2 * (dot(ray, origin) - (dot(ray, dir) * dot(origin, dir)));
	abc.z = dot(origin, origin) - (dot(origin, dir) * dot(origin, dir)) - (radius * radius);
	return (abc);
}

float					inter_cylinder(t_cylinder cylind, float3 ray, float3 origin)
{
	float3				abc;
	float				d;
	float				res1 = 0;
	float				res2 = 0;
	float				m;

	origin -= cylind.pos;
	abc = get_cylinder_abc(cylind.radius, normalize(cylind.dir), ray, origin);
	d = (abc.y * abc.y) - (4 * (abc.x * abc.z));
	if (d < 0)
		return (0);
	if (d == 0)
		res1 = (-abc[1]) / (2 * abc[0]);
	else
	{
		res1 = (((-abc[1]) + sqrt(d)) / (2 * abc[0]));
		res2 = (((-abc[1]) - sqrt(d)) / (2 * abc[0]));
	}
	if (res1 < 0 && res2 < 0)
		return (0);
	if ((res1 < res2 && res1 > 0) || (res1 > res2 && res2 < 0))
	{
		if (cylind.height == 0 || (dot(ray, fast_normalize(cylind.dir) * res1 +
			dot(origin, fast_normalize(cylind.dir))) < cylind.height && dot(ray, fast_normalize(cylind.dir) * res1 +
			dot(origin, fast_normalize(cylind.dir))) > 0))
			return (res1);
	}
	if (cylind.height ==  0 || (dot(ray, fast_normalize(cylind.dir) * res2 +
			dot(origin, fast_normalize(cylind.dir))) < cylind.height && dot(ray, fast_normalize(cylind.dir) * res2 +
			dot(origin, fast_normalize(cylind.dir))) > 0))
		return (res2);
	else
		return (0);
}

float3					get_cone_abc(t_cone cone, float3 ray, float3 origin)
{
	float3		abc = 0;
	float		k = cone.angle * DEG2RAD;
	k = tan(k);
	k = 1 + k * k;

	abc.x = dot(ray, ray) - (k * (dot(ray, cone.dir) * dot(ray, cone.dir)));
	abc.y = 2 * (dot(ray, origin) - (k * \
			(dot(ray, cone.dir) * dot(origin, cone.dir))));
	abc.z = (dot(origin, origin) - \
			(k * (dot(origin, cone.dir) * dot(origin, cone.dir))));
	return (abc);
}

float					inter_cone(t_cone cone, float3 ray, float3 origin)
{
	float3				abc;
	float				d;
	float				res1;
	float				res2;
	float				res;

	origin -= cone.pos;
	abc = get_cone_abc(cone, ray, origin);
	d = (abc.y * abc.y) - (4 * (abc.x * abc.z));
	if (d < 0)
		return (0);
	if (d == 0)
		res = (-abc[1]) / (2 * abc[0]);
	res1 = (((-abc[1]) + sqrt(d)) / (2 * abc[0]));
	res2 = (((-abc[1]) - sqrt(d)) / (2 * abc[0]));
	if ((res1 < res2 && res1 > 0) || (res1 > res2 && res2 < 0))
		res = res1;
	else
		res = res2;
	return (res);
}

float					inter_plan(t_plane plane, float3 ray, float3 origin)
{
	float				t;

	if ((t = dot(fast_normalize(ray), fast_normalize(plane.normale))) == 0)
		return (0);
	t = (dot(plane.pos - origin, fast_normalize(plane.normale))) / t;
		if (t < 0.001)
		return (0);
	return (t);
}

t_hit			ray_hit(float3 origin, float3 ray, t_scene scene)
{
	unsigned int			i = 0;
	int			max = get_max_obj(PARAM);
	t_hit		hit;
	float		dist = 0;

	hit.dist = 0;
	hit.type = 0;
	hit.id = 0;
	hit.pos = (0, 0, 0);
	hit.normale = (0, 0, 0);
	while (i < max)
	{
		if (i < PARAM->n_cones)
			if (((dist = inter_cone(CONES[i], ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 1;
				hit.id = i;
			}
		if (i < PARAM->n_cylinders)
			if (((dist = inter_cylinder(CYLIND[i], ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 2;
				hit.id = i;
			}
		if (i < PARAM->n_planes)
			if (((dist = inter_plan(PLANE[i], ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 4;
				hit.id = i;
			}
		if (i < PARAM->n_spheres)
			if (((dist = inter_sphere(SPHERE[i], ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 5;
				hit.id = i;
			}
		i++;
	}
	return (hit);
}

float3			get_hit_normale(t_scene scene, t_hit hit)
{
	float3		res;

	if (hit.type == 1)
	{
		float k = CONES[hit.id].angle * DEG2RAD;
		k = tan(k);
		k = 1 + k * k;
		res = dot(-scene.ray, fast_normalize(CONES[hit.id].dir)) * \
			hit.dist - dot(ACTIVECAM.pos + PARAM->mvt - CONES[hit.id].pos, fast_normalize(CONES[hit.id].dir));
		res = ((hit.pos - fast_normalize(CONES[hit.id].pos)) - (k * fast_normalize(CONES[hit.id].dir) * res)) * -1;
	}
	else if (hit.type == 2)
	{
//		res = dot(-scene.ray, fast_normalize(CYLIND[hit.id].dir) * hit.dist + \
//			dot(ACTIVECAM.pos + PARAM->mvt - CYLIND[hit.id].pos, fast_normalize(CYLIND[hit.id].dir)));
//		res = hit.pos - CYLIND[hit.id].pos - fast_normalize(CYLIND[hit.id].dir) * res;
		res = hit.pos - CYLIND[hit.id].pos;
		res.z = 0;	// fonctionne pour cylindre aligné en Z
	}
	else if (hit.type == 4)
	{
		if (dot(PLANE[hit.id].normale, -scene.ray) < 0)
			res = -PLANE[hit.id].normale;
		else
			res = PLANE[hit.id].normale;
	}
	else if (hit.type == 5)
		res = hit.pos - SPHERE[hit.id].pos;
	return (fast_normalize(res));
}

unsigned int			phong(t_hit hit, t_scene scene)
{
	int					i = -1;
	unsigned int		obj_color = get_obj_hue(scene, hit);
	unsigned int		ambient_color = get_ambient(obj_color, scene);
	unsigned int		res_color = ambient_color;
	float				tmp = 0;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit;

	while (++i < PARAM->n_lights)
	{
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(hit.pos, light_ray.dir, scene);
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		;
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(hit, scene, res_color, tmp);
			reflect = fast_normalize(mult_fvect(2.0 * dot(hit.normale, light_ray.dir), hit.normale) - light_ray.dir);
			tmp = dot(reflect, -scene.ray);
			if (tmp > 0)
				res_color = color_specular(hit, scene, res_color, tmp);
		}
	}
	return (res_color);
}

unsigned int			phong2(t_hit hit, t_scene scene)
{
	int					i = -1;
	unsigned int		obj_color = get_obj_hue(scene, hit);
	unsigned int		ambient_color = get_ambient(obj_color, scene);
	unsigned int		res_color = ambient_color;
	float				tmp = 0;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit;

	while (++i < PARAM->n_lights)
	{
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(hit.pos, light_ray.dir, scene);
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		;
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(hit, scene, res_color, tmp);
			reflect = fast_normalize(mult_fvect(2.0 * dot(hit.normale, light_ray.dir), hit.normale) - light_ray.dir);
			tmp = dot(reflect, -scene.ray);
			if (tmp > 0)
				res_color = color_specular(hit, scene, res_color, tmp);
		}
	}
	return (res_color);
}

unsigned int		bounce(t_scene scene, t_hit old_hit, int depth)
{
	unsigned int	color = 0;
	float3			reflex = 0;
	t_hit			new_hit;
	while (depth > 0)
	{
//		printf("IM REFLECTING!\n");
		reflex = fast_normalize(scene.ray - mult_fvect((2 * dot(old_hit.normale, scene.ray)), old_hit.normale));
		new_hit.dist = MAX_DIST;
		new_hit = ray_hit(old_hit.pos, reflex, scene);
		if (new_hit.dist > 0 && new_hit.dist < MAX_DIST)
		{
			new_hit.pos = mult_fvect(new_hit.dist, reflex) + old_hit.pos;
			new_hit.normale = get_hit_normale(scene, new_hit);
			new_hit.pos = new_hit.pos + ((new_hit.dist / 100) * new_hit.normale);
			color += 0.1 * phong2(new_hit, scene);
		}
		depth--;
	}
	return (color);
}
unsigned int	get_pixel_color(t_scene scene)
{
	t_hit			hit;
	int				depth = PARAM->depth;
	unsigned int	color  = 0;
	unsigned int	bounce_color = 0;

	hit.dist = MAX_DIST;
	hit = ray_hit((ACTIVECAM.pos + PARAM->mvt), scene.ray, scene);
	if (hit.dist > 0 && hit.dist < MAX_DIST) // ajout d'une distance max pour virer acnee mais pas fiable a 100%
	{
		hit.pos = mult_fvect(hit.dist, scene.ray) + (ACTIVECAM.pos + PARAM->mvt);
		hit.normale = get_hit_normale(scene, hit);
		hit.pos = hit.pos + ((hit.dist / 100) * hit.normale);
		color = phong(hit, scene);
		if (depth > 0)
			bounce_color = bounce(scene, hit, depth);
//		return (color + bounce_color);
		return (blend_add(color, 0.8*bounce_color));
	}
	return (get_ambient(BACKCOLOR, scene));
}

float3						get_ray_cam(t_cam cam, t_scene scene, int x, int y)
{
	float3					cam_ray = 0;
	float					ratio = (float)PARAM->win_w / (float)PARAM->win_h;

	cam_ray.x = ((2 * ((x + 0.5) / PARAM->win_w)) - 1) * ratio * (tan((cam.fov / 2) * DEG2RAD));
	cam_ray.y = ((1 - (2 * ((y + 0.5) / PARAM->win_h))) * tan((cam.fov / 2) * DEG2RAD));
	cam_ray.z = 1;
/*  rotation XYZ
	cam_ray.x = xx * cos(yaw) * cos(roll) + yy * cos(yaw) * -sin(roll) + sin(yaw);
	cam_ray.y = xx * (-sin(pitch) * -sin(yaw) * cos(roll) + cos(pitch) * sin(roll)) + yy * (-sin(pitch) * -sin(yaw) * -sin(roll) + cos(pitch) * cos(roll)) + cos(yaw) * -sin(pitch);
	cam_ray.z = xx * (cos(pitch) * -sin(yaw) * cos(roll) + sin(pitch) * sin(roll)) + yy * (cos(pitch) * -sin(yaw) * -sin(roll) + sin(pitch) * cos(roll)) + cos(yaw) * cos(pitch);

// rotation ZYX
	cam_ray.x = xx * cos(roll) * cos(yaw) + yy * (cos(pitch) * -sin(roll) + cos(roll) * sin(yaw) * sin(pitch)) + (-sin(roll) * -sin(pitch) + cos(roll) * sin(yaw) * cos(pitch));
	cam_ray.y = xx * sin(roll) * cos(yaw) + yy * (cos(roll) * cos(pitch) + sin(roll) * sin(yaw) * sin(pitch)) + (cos(roll) * -sin(pitch) + sin(roll) * sin(yaw) * cos(pitch));
	cam_ray.z = xx * -sin(yaw) + yy * cos(yaw) * sin(pitch) + cos(yaw) * cos(pitch);
calcul simplifié
	cam_ray.x = xx * cos(yaw) + yy * (sin(yaw) * sin(pitch)) + (sin(yaw) * cos(pitch));
	cam_ray.y = yy * cos(pitch) + -sin(pitch);
	cam_ray.z = xx * -sin(yaw) + yy * cos(yaw) * sin(pitch) + cos(yaw) * cos(pitch);
*/
	cam_ray = rotat_zyx(cam_ray, cam.pitch, cam.yaw, 0);
	return(normalize(cam_ray));
}

__kernel void	ray_trace(__global		char		*output,
						  				t_param		param,
						  __constant	t_cam		*cameras,
						  __constant	t_cone		*cones,
						  __constant	t_cylinder	*cylinders,
						  __constant	t_light		*lights,
						  __constant	t_plane		*planes,
						  __constant	t_sphere	*spheres,
						  __global		t_hit		*target_obj)
{
	t_scene		scene = grab_data(cameras, cones, cylinders, lights, planes, spheres, param);
	int			x = get_global_id(0);
	int			y = get_global_id(1);
	int			id = x + (PARAM->win_w * y); // NE PAS VIRER ID CAR BESOIN DANS MACRO OUTPUTE
	if (x == PARAM->mou_x && y == PARAM->mou_y)
		*target_obj = ray_hit((ACTIVECAM.pos + PARAM->mvt), get_ray_cam(ACTIVECAM, scene, PARAM->mou_x, PARAM->mou_y), scene);
	scene.ray = get_ray_cam(ACTIVECAM, scene, x ,y);
	OUTPUTE = get_pixel_color(scene);
}


Fixes black bottom bar on output.

Fixes black bottom bar on output.

Caused by index one too far.

const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;

__kernel void desaturate( __global image2d_t image, __global float* desaturated_out, int width )
{
    int2 coord = (int2)(get_global_id(0), get_global_id(1));
    uint4 pixel = read_imageui( image, sampler, coord );
    desaturated_out[coord.x+coord.y*width] = (0.21f * pixel.x + 0.72f * pixel.y + 0.07f * pixel.z);
    //desaturated_out[coord.x+coord.y*width] = coord.x/(float)width * 255;
}

__kernel void gaussx( __global float* gauss_kernel, int kernel_radius, __global float* input, __global float* output, int width )
{
    int2 coord = (int2)(get_global_id(0), get_global_id(1));

    float sum = 0;

    for( int i = -kernel_radius; i <= kernel_radius; i++ )
    {
        sum += gauss_kernel[i+kernel_radius] * input[min(width-1,max(coord.x + i,0))+coord.y*width];
    }

    output[coord.x+coord.y*width] = sum;
}

__kernel void gaussy( __constant float* gauss_kernel, int kernel_radius, __global float* input, __global float* output, int width, int height)
{
    int2 coord = (int2)(get_global_id(0), get_global_id(1));

    float sum = 0;

    for( int i = -kernel_radius; i <= kernel_radius; i++ )
    {
        sum += gauss_kernel[i+kernel_radius] * input[min(height-1,max(coord.y + i,0))*width+coord.x];
    }

    output[coord.x+coord.y*width] = sum;
}

const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;

__kernel void desaturate( __global image2d_t image, __global float* desaturated_out, int width )
{
    int2 coord = (int2)(get_global_id(0), get_global_id(1));
    uint4 pixel = read_imageui( image, sampler, coord );
    desaturated_out[coord.x+coord.y*width] = (0.21f * pixel.x + 0.72f * pixel.y + 0.07f * pixel.z);
    //desaturated_out[coord.x+coord.y*width] = coord.x/(float)width * 255;
}

__kernel void gaussx( __global float* gauss_kernel, int kernel_radius, __global float* input, __global float* output, int width )
{
    int2 coord = (int2)(get_global_id(0), get_global_id(1));

    float sum = 0;

    for( int i = -kernel_radius; i <= kernel_radius; i++ )
    {
        sum += gauss_kernel[i+kernel_radius] * input[min(width,max(coord.x + i,0))+coord.y*width];
    }

    output[coord.x+coord.y*width] = sum;
}

__kernel void gaussy( __constant float* gauss_kernel, int kernel_radius, __global float* input, __global float* output, int width, int height)
{
    int2 coord = (int2)(get_global_id(0), get_global_id(1));

    float sum = 0;

    for( int i = -kernel_radius; i <= kernel_radius; i++ )
    {
        sum += gauss_kernel[i+kernel_radius] * input[min(height,max(coord.y + i,0))*width+coord.x];
    }

    output[coord.x+coord.y*width] = sum;
}


931- Emphatize a specific design decision.

#define B_STRIDE (1024 / sizeof(ulong))
#define V_STRIDE ((1024 * 1024 * 2) / sizeof(ulong))
#define S_STRIDE (2 * 256 * 2)
#define YESCRYPT_r 8

static constant const uint salsa20_simd_shuffle[64] = { // also include LE32DEC
    0x00, 0x01, 0x02, 0x03, 0x14, 0x15, 0x16, 0x17,
    0x28, 0x29, 0x2a, 0x2b, 0x3c, 0x3d, 0x3e, 0x3f,
    0x10, 0x11, 0x12, 0x13, 0x24, 0x25, 0x26, 0x27,
    0x38, 0x39, 0x3a, 0x3b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x20, 0x21, 0x22, 0x23, 0x34, 0x35, 0x36, 0x37,
    0x08, 0x09, 0x0a, 0x0b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x30, 0x31, 0x32, 0x33, 0x04, 0x05, 0x06, 0x07,
    0x18, 0x19, 0x1a, 0x1b, 0x2c, 0x2d, 0x2e, 0x2f
};

static constant const uchar salsa20_simd_unshuffle[64] = { // also with LE32ENC
    0x00, 0x01, 0x02, 0x03, 0x34, 0x35, 0x36, 0x37,
    0x28, 0x29, 0x2a, 0x2b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x10, 0x11, 0x12, 0x13, 0x04, 0x05, 0x06, 0x07,
    0x38, 0x39, 0x3a, 0x3b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x20, 0x21, 0x22, 0x23, 0x14, 0x15, 0x16, 0x17,
    0x08, 0x09, 0x0a, 0x0b, 0x3c, 0x3d, 0x3e, 0x3f,
    0x30, 0x31, 0x32, 0x33, 0x24, 0x25, 0x26, 0x27,
    0x18, 0x19, 0x1a, 0x1b, 0x0c, 0x0d, 0x0e, 0x0f
};

// For GBSTY, only the lower 32 bits are necessary
uint Integerify(local ulong *block, const uint r) {
	const local ulong *X = block + (2 * r - 1) * 8; // SALU FTW
	//const uint lo = (uint)(X[0]);
	//const uint hi = (uint)(X[6] >> 32);
	//return upsample(hi, lo);
	return (uint)(X[0]);
}

void blkxor(local ulong *block, global ulong *modifier, const uint s) {
	local uchar *collide = (local uchar*)block;
	global uchar *src = (global uchar*)modifier;
	for(uint loop = 0; loop < (s * sizeof(ulong)) / get_local_size(0); loop++) {
		collide[get_local_id(0)] ^= src[get_local_id(0)];
		collide += get_local_size(0);
		src += get_local_size(0);
	}
	barrier(CLK_LOCAL_MEM_FENCE);
}

void blkcpy(global ulong *dst, local ulong *src, const uint s) {
	local uchar *value = (local uchar*)src;
	global uchar *target = (global uchar*)dst;
	for(uint loop = 0; loop < (s * sizeof(ulong)) / get_local_size(0); loop++) {
		target[get_local_id(0)] = value[get_local_id(0)];
		target += get_local_size(0);
		value += get_local_size(0);
	}
	barrier(CLK_LOCAL_MEM_FENCE);
}

void Block_pwxform(local ulong *slice, global ulong *valS, local ulong *gather) {
	const size_t YESCRYPT_S_SIZE1 = 1 << 8;
	const size_t YESCRYPT_S_SIMD = 2;
	const size_t YESCRYPT_S_P = 4;
	const size_t YESCRYPT_S_ROUNDS = 6;
	const global uchar *so = (global uchar*)(valS);
	const global uchar *si = (global uchar*)(valS + YESCRYPT_S_SIZE1 * YESCRYPT_S_SIMD);
	slice += (get_local_id(0) / 16) * YESCRYPT_S_SIMD;
	gather += (get_local_id(0) / 16) * YESCRYPT_S_SIMD * 2;
	{
		ulong xo = slice[0];
		ulong xi = slice[1];
		for(uint round = 0; round < YESCRYPT_S_ROUNDS; round++) {
			const uint YESCRYPT_S_MASK = (YESCRYPT_S_SIZE1 - 1) * YESCRYPT_S_SIMD * 8;
			const ulong YESCRYPT_S_MASK2 = ((ulong)YESCRYPT_S_MASK << 32) | YESCRYPT_S_MASK;
			const ulong x = xo & YESCRYPT_S_MASK2;
			{ // unrolled formulation always uses good values
				const uint teamwork = get_local_id(0) % 16;
				local uchar *ubyte = (local uchar*)(gather);
				ubyte[teamwork] = so[(uint)(x) + teamwork];
				ubyte[teamwork + 16] = si[(uint)(x >> 32) + teamwork];
			}
			xo = (ulong)(xo >> 32) * (uint)xo;
			xi = (ulong)(xi >> 32) * (uint)xi;
			barrier(CLK_LOCAL_MEM_FENCE); // allowing the above 2= was a specific design decision
			xo += gather[0 + 0];
			xo ^= gather[2 + 0];
			xi += gather[0 + 1];
			xi ^= gather[2 + 1];
		}
		slice[0] = xo;
		slice[1] = xi;
	}
}

void Salsa20_8(local ulong slice[8]) {
	uint inputValue = 0;
	local uint *words = (local uint*)(slice);
	if(get_local_id(0) < 16) inputValue = words[get_local_id(0)];
	barrier(CLK_LOCAL_MEM_FENCE);
	{
		local uchar *buff = (local uchar*)(slice);
		const uint shuffled = salsa20_simd_unshuffle[get_local_id(0)];
		uchar value = buff[shuffled];
		buff[get_local_id(0)] = value;
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	for(uint round = 0; round < 8; round += 2) { // everything is SALU in there
		words[ 4] ^= rotate(words[ 0] + words[12],  7u);  words[ 8] ^= rotate(words[ 4] + words[ 0],  9u);
		words[12] ^= rotate(words[ 8] + words[ 4], 13u);  words[ 0] ^= rotate(words[12] + words[ 8], 18u);

		words[ 9] ^= rotate(words[ 5] + words[ 1],  7u);  words[13] ^= rotate(words[ 9] + words[ 5],  9u);
		words[ 1] ^= rotate(words[13] + words[ 9], 13u);  words[ 5] ^= rotate(words[ 1] + words[13], 18u);

		words[14] ^= rotate(words[10] + words[ 6],  7u);  words[ 2] ^= rotate(words[14] + words[10],  9u);
		words[ 6] ^= rotate(words[ 2] + words[14], 13u);  words[10] ^= rotate(words[ 6] + words[ 2], 18u);

		words[ 3] ^= rotate(words[15] + words[11], 7u);  words[ 7] ^= rotate(words[ 3] + words[15],  9u);
		words[11] ^= rotate(words[ 7] + words[ 3],13u);  words[15] ^= rotate(words[11] + words[ 7], 18u);

		/* Operate on rows */
		words[ 1] ^= rotate(words[ 0] + words[ 3],  7u);  words[ 2] ^= rotate(words[ 1] + words[ 0],  9u);
		words[ 3] ^= rotate(words[ 2] + words[ 1], 13u);  words[ 0] ^= rotate(words[ 3] + words[ 2], 18u);

		words[ 6] ^= rotate(words[ 5] + words[ 4],  7u);  words[ 7] ^= rotate(words[ 6] + words[ 5],  9u);
		words[ 4] ^= rotate(words[ 7] + words[ 6], 13u);  words[ 5] ^= rotate(words[ 4] + words[ 7], 18u);

		words[11] ^= rotate(words[10] + words[ 9],  7u);  words[ 8] ^= rotate(words[11] + words[10],  9u);
		words[ 9] ^= rotate(words[ 8] + words[11], 13u);  words[10] ^= rotate(words[ 9] + words[ 8], 18u);

		words[12] ^= rotate(words[15] + words[14],  7u);  words[13] ^= rotate(words[12] + words[15],  9u);
		words[14] ^= rotate(words[13] + words[12], 13u);  words[15] ^= rotate(words[14] + words[13], 18u);
	}
	{ // salsa20_simd_shuffle
		local uchar *buffer = (local uchar*)(slice);
		const uint shuffled = salsa20_simd_shuffle[get_local_id(0)];
		uchar value = buffer[shuffled];
		buffer[get_local_id(0)] = value;
	}
	if(get_local_id(0) < 16) words[get_local_id(0)] += inputValue;
	barrier(CLK_LOCAL_MEM_FENCE);
}

void Blockmix_pwxform(local ulong *block, global ulong *valS, local ulong *gather, const uint yescrypt_r1) { // Throw away VALU power like there's no tomorrow
	const size_t YESCRYPT_S_P_SIZE = 8;
	//const size_t yescrypt_r1 = YESCRYPT_r * 128 / (YESCRYPT_S_P_SIZE * 8);
	{
		local uint *slice = (local uint*)(block);
		local uint *modifier = (local uint*)(block + (yescrypt_r1 - 1) * YESCRYPT_S_P_SIZE);
		for(uint loop = get_local_id(0); loop < YESCRYPT_S_P_SIZE * 2; loop += get_local_size(0)) { // 2 uints per ulong
			slice[loop] ^= modifier[loop];
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	Block_pwxform(block, valS, gather);
	for(uint loop = 1; loop < yescrypt_r1; loop++) {
		{
			local uint *slice = (local uint*)(block + loop * YESCRYPT_S_P_SIZE);
			local uint *modifier = (local uint*)(block + (loop - 1) * YESCRYPT_S_P_SIZE);
			for(uint loop = get_local_id(0); loop < YESCRYPT_S_P_SIZE * 2; loop += get_local_size(0)) { // 2 uints per ulong
				slice[loop] ^= modifier[loop];
			}
			barrier(CLK_LOCAL_MEM_FENCE);
		}
		Block_pwxform(block + loop * YESCRYPT_S_P_SIZE, valS, gather);
	}
	const uint i = (yescrypt_r1 - 1) * YESCRYPT_S_P_SIZE / 8;
	Salsa20_8(block + i * 8);
}

/* This was originally written 16x4 ways, keeping 4 hashes in flight but it turns out Smix2 approach is just better,
let the hardware hide latency across different wavefronts instead of providing more instructions. */
__attribute__((reqd_work_group_size(64, 1, 1)))
kernel void SecondSmix1(global ulong *valB, global ulong *valV, global ulong *valS, const uint yescrypt_N, const uint yescrypt_r1) {
	const size_t slot = get_global_id(1) - get_global_offset(1);
	valV += slot * V_STRIDE;
	valS += slot * S_STRIDE;
	valB += slot * B_STRIDE;
	local ulong block[16 * 8];
	{ // there's a complication here: the previous stage outputs uints, not ulongs so adjust
		global uchar *src = (global uchar*)(valB);
		global uchar *dstv = (global uchar*)(valV);
		local uchar *dst = (local uchar*)block;
		const uint shuffled = salsa20_simd_shuffle[get_local_id(0)];
		const uint byteOrder = (get_local_id(0) / 4) * 4 + 3 - get_local_id(0) % 4;
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			const uchar value = src[shuffled];
			dst[get_local_id(0)] = value;
			dstv[byteOrder] = value;
			src += 8 * 8;
			dst += 8 * 8;
			dstv += 8 * 8;
		}
		barrier(CLK_LOCAL_MEM_FENCE);
		local uint *swz = (local uint*)block;
		for(uint i = get_local_id(0); i < 16 * 8 * 2; i += get_local_size(0)) {
			const uint value = swz[i];
			swz[i] = as_uint(as_uchar4(value).s3210);
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	local ulong gather[4 * 4];
	Blockmix_pwxform(block, valS, gather, yescrypt_r1);
	const uint yescrypt_s = 16 * YESCRYPT_r;
	{ // blkcpy
		global uchar *dst = (global uchar*)(valV + yescrypt_s);
		local uchar *src = (local uchar*)block;
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			dst[get_local_id(0)] = src[get_local_id(0)];
			src += 8 * 8;
			dst += 8 * 8;
		}
		barrier(CLK_LOCAL_MEM_FENCE);
	}
	// Now I am inside the big 'else' branch
	Blockmix_pwxform(block, valS, gather, yescrypt_r1);
	const bool yescrypt_rw = true;
	uint yescrypt_n = 1; // this is ulong in reference, but for GBSTY 32 bits are enough. Same applies to loop iterator
	for(uint yescrypt_i = 2; yescrypt_i < yescrypt_N; yescrypt_i++) { // note I do a single block per iteration, CPU does 2
		event_t copied = async_work_group_copy(valV + yescrypt_i * yescrypt_s, block, yescrypt_s, 0);
		wait_group_events(1, &copied);

		if(yescrypt_rw) {
			if((yescrypt_i % 2) == 0) { // this condition isn't in yescrypt, I need it as I do one block per iteration
				if((yescrypt_i & (yescrypt_i - 1)) == 0) yescrypt_n <<= 1;
			}
			uint j = Integerify(block, YESCRYPT_r);
			j &= yescrypt_n - 1;
			j += yescrypt_i - yescrypt_n;

			global const uint *indirected = (global uint*)(valV + j * yescrypt_s);
			local uint *dword = (local uint*)block;
			for(size_t el = 0; el < (yescrypt_s * 2) / 64; el++) {
				dword[get_local_id(0)] ^= indirected[get_local_id(0)];
				dword += get_local_size(0);
				indirected += get_local_size(0);
			}
			// ^ no LDS collisions and low instruction count BUT stride 4 bytes access
			// to be compared against packed 64byte read (uchar*)indirected, el<yescrypt_s*8;
			// Better bandwidth usage, which was supposed to be the problem here.
			barrier(CLK_LOCAL_MEM_FENCE);
		}
		Blockmix_pwxform(block, valS, gather, yescrypt_r1);
	}
	// for GBSTY parameters, _n is 0x20 at this point.
	{
		global uchar *dst = (global uchar*)(valB);
		local uchar *src = (local uchar*)(block);
		const uint shuffled = salsa20_simd_unshuffle[get_local_id(0)];
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			dst[get_local_id(0)] = src[shuffled];
			src += 8 * 8;
			dst += 8 * 8;
		}
	}
}

#ifdef YESCRYPT_RW
/* Smix2 is a bit different. It still revolves around doing integerify and xorring (but also updating) Vj blocks.
What I've seen in smix1 is that we need to be smarter, we cannot keep on iterating host-side.
Plus, I really want to maximize cache usage and latency hiding to have nearly free global memory barriers...
... so I try something different. Instead of 16-way, I go even crazier with 64-way. SALU FTW */
__attribute__((reqd_work_group_size(64, 1, 1))) // a full wavefront to mangle an hash
kernel void Smix2(global ulong *valB, global ulong *valV, global ulong *valS, const uint yescrypt_NLoop, const uint yescrypt_r1) {
	const uint YESCRYPT_s = YESCRYPT_r * 16;
	//const uint yescrypt_NLoop = 682; // for first call
	//                          = 2; // for second call
	//const bool YESCRYPT_RW = 1; // for first call
	//                         0; // for second call
	const uint YESCRYPT_N = 2048;
	const size_t slot = get_global_id(1) - get_global_offset(1);
	valB += slot * B_STRIDE;
	valV += slot * V_STRIDE;
	valS += slot * S_STRIDE;
	local ulong block[16 * 8];
	local ulong gather[4 * 4];

	{
		global uchar *src = (global uchar*)(valB);
		local uchar *dst = (local uchar*)block;
		const uint shuffled = salsa20_simd_shuffle[get_local_id(0)];
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			dst[get_local_id(0)] = src[shuffled];
			src += 8 * 8;
			dst += 8 * 8;
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	for(uint loop = 0; loop < yescrypt_NLoop; loop++) {
		uint j = Integerify(block, YESCRYPT_r);
		j &= (YESCRYPT_N - 1); // modulo pow2
		blkxor(block, valV + j * YESCRYPT_s, YESCRYPT_s);
		if(YESCRYPT_RW) blkcpy(valV + j * YESCRYPT_s, block, YESCRYPT_s);
		Blockmix_pwxform(block, valS, gather, yescrypt_r1);
		barrier(CLK_GLOBAL_MEM_FENCE);
	}
	{
		global uchar *dst = (global uchar*)(valB);
		local uchar *src = (local uchar*)(block);
		const uint shuffled = salsa20_simd_unshuffle[get_local_id(0)];
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			dst[get_local_id(0)] = src[shuffled];
			src += 8 * 8;
			dst += 8 * 8;
		}
	}
}
#endif

#define B_STRIDE (1024 / sizeof(ulong))
#define V_STRIDE ((1024 * 1024 * 2) / sizeof(ulong))
#define S_STRIDE (2 * 256 * 2)
#define YESCRYPT_r 8

static constant const uint salsa20_simd_shuffle[64] = { // also include LE32DEC
    0x00, 0x01, 0x02, 0x03, 0x14, 0x15, 0x16, 0x17,
    0x28, 0x29, 0x2a, 0x2b, 0x3c, 0x3d, 0x3e, 0x3f,
    0x10, 0x11, 0x12, 0x13, 0x24, 0x25, 0x26, 0x27,
    0x38, 0x39, 0x3a, 0x3b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x20, 0x21, 0x22, 0x23, 0x34, 0x35, 0x36, 0x37,
    0x08, 0x09, 0x0a, 0x0b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x30, 0x31, 0x32, 0x33, 0x04, 0x05, 0x06, 0x07,
    0x18, 0x19, 0x1a, 0x1b, 0x2c, 0x2d, 0x2e, 0x2f
};

static constant const uchar salsa20_simd_unshuffle[64] = { // also with LE32ENC
    0x00, 0x01, 0x02, 0x03, 0x34, 0x35, 0x36, 0x37,
    0x28, 0x29, 0x2a, 0x2b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x10, 0x11, 0x12, 0x13, 0x04, 0x05, 0x06, 0x07,
    0x38, 0x39, 0x3a, 0x3b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x20, 0x21, 0x22, 0x23, 0x14, 0x15, 0x16, 0x17,
    0x08, 0x09, 0x0a, 0x0b, 0x3c, 0x3d, 0x3e, 0x3f,
    0x30, 0x31, 0x32, 0x33, 0x24, 0x25, 0x26, 0x27,
    0x18, 0x19, 0x1a, 0x1b, 0x0c, 0x0d, 0x0e, 0x0f
};

// For GBSTY, only the lower 32 bits are necessary
uint Integerify(local ulong *block, const uint r) {
	const local ulong *X = block + (2 * r - 1) * 8; // SALU FTW
	//const uint lo = (uint)(X[0]);
	//const uint hi = (uint)(X[6] >> 32);
	//return upsample(hi, lo);
	return (uint)(X[0]);
}

void blkxor(local ulong *block, global ulong *modifier, const uint s) {
	local uchar *collide = (local uchar*)block;
	global uchar *src = (global uchar*)modifier;
	for(uint loop = 0; loop < (s * sizeof(ulong)) / get_local_size(0); loop++) {
		collide[get_local_id(0)] ^= src[get_local_id(0)];
		collide += get_local_size(0);
		src += get_local_size(0);
	}
	barrier(CLK_LOCAL_MEM_FENCE);
}

void blkcpy(global ulong *dst, local ulong *src, const uint s) {
	local uchar *value = (local uchar*)src;
	global uchar *target = (global uchar*)dst;
	for(uint loop = 0; loop < (s * sizeof(ulong)) / get_local_size(0); loop++) {
		target[get_local_id(0)] = value[get_local_id(0)];
		target += get_local_size(0);
		value += get_local_size(0);
	}
	barrier(CLK_LOCAL_MEM_FENCE);
}

void Block_pwxform(local ulong *slice, global ulong *valS, local ulong *gather) {
	const size_t YESCRYPT_S_SIZE1 = 1 << 8;
	const size_t YESCRYPT_S_SIMD = 2;
	const size_t YESCRYPT_S_P = 4;
	const size_t YESCRYPT_S_ROUNDS = 6;
	const global uchar *so = (global uchar*)(valS);
	const global uchar *si = (global uchar*)(valS + YESCRYPT_S_SIZE1 * YESCRYPT_S_SIMD);
	slice += (get_local_id(0) / 16) * YESCRYPT_S_SIMD;
	gather += (get_local_id(0) / 16) * YESCRYPT_S_SIMD * 2;
	{
		ulong xo = slice[0];  // Bank conflict
		ulong xi = slice[1];  // Bank conflict
		for(uint round = 0; round < YESCRYPT_S_ROUNDS; round++) {
			const uint YESCRYPT_S_MASK = (YESCRYPT_S_SIZE1 - 1) * YESCRYPT_S_SIMD * 8;
			const ulong YESCRYPT_S_MASK2 = ((ulong)YESCRYPT_S_MASK << 32) | YESCRYPT_S_MASK;
			const ulong x = xo & YESCRYPT_S_MASK2;
			{ // unrolled formulation always uses good values
				const uint teamwork = get_local_id(0) % 16;
				local uchar *ubyte = (local uchar*)(gather);
				ubyte[teamwork] = so[(uint)(x) + teamwork];
				ubyte[teamwork + 16] = si[(uint)(x >> 32) + teamwork];
				barrier(CLK_LOCAL_MEM_FENCE);
			}
			xo = (ulong)(xo >> 32) * (uint)xo;
			xo += gather[0 + 0];
			xo ^= gather[2 + 0]; // do this uint for slightly improved perf?
			xi = (ulong)(xi >> 32) * (uint)xi;
			xi += gather[0 + 1];
			xi ^= gather[2 + 1];
		}
		slice[0] = xo;  // Bank conflict
		slice[1] = xi;  // Bank conflict
	}
}

void Salsa20_8(local ulong slice[8]) {
	uint inputValue = 0;
	local uint *words = (local uint*)(slice);
	if(get_local_id(0) < 16) inputValue = words[get_local_id(0)];
	barrier(CLK_LOCAL_MEM_FENCE);
	{
		local uchar *buff = (local uchar*)(slice);
		const uint shuffled = salsa20_simd_unshuffle[get_local_id(0)];
		uchar value = buff[shuffled];
		buff[get_local_id(0)] = value;
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	for(uint round = 0; round < 8; round += 2) { // everything is SALU in there
		words[ 4] ^= rotate(words[ 0] + words[12],  7u);  words[ 8] ^= rotate(words[ 4] + words[ 0],  9u);
		words[12] ^= rotate(words[ 8] + words[ 4], 13u);  words[ 0] ^= rotate(words[12] + words[ 8], 18u);

		words[ 9] ^= rotate(words[ 5] + words[ 1],  7u);  words[13] ^= rotate(words[ 9] + words[ 5],  9u);
		words[ 1] ^= rotate(words[13] + words[ 9], 13u);  words[ 5] ^= rotate(words[ 1] + words[13], 18u);

		words[14] ^= rotate(words[10] + words[ 6],  7u);  words[ 2] ^= rotate(words[14] + words[10],  9u);
		words[ 6] ^= rotate(words[ 2] + words[14], 13u);  words[10] ^= rotate(words[ 6] + words[ 2], 18u);

		words[ 3] ^= rotate(words[15] + words[11], 7u);  words[ 7] ^= rotate(words[ 3] + words[15],  9u);
		words[11] ^= rotate(words[ 7] + words[ 3],13u);  words[15] ^= rotate(words[11] + words[ 7], 18u);

		/* Operate on rows */
		words[ 1] ^= rotate(words[ 0] + words[ 3],  7u);  words[ 2] ^= rotate(words[ 1] + words[ 0],  9u);
		words[ 3] ^= rotate(words[ 2] + words[ 1], 13u);  words[ 0] ^= rotate(words[ 3] + words[ 2], 18u);

		words[ 6] ^= rotate(words[ 5] + words[ 4],  7u);  words[ 7] ^= rotate(words[ 6] + words[ 5],  9u);
		words[ 4] ^= rotate(words[ 7] + words[ 6], 13u);  words[ 5] ^= rotate(words[ 4] + words[ 7], 18u);

		words[11] ^= rotate(words[10] + words[ 9],  7u);  words[ 8] ^= rotate(words[11] + words[10],  9u);
		words[ 9] ^= rotate(words[ 8] + words[11], 13u);  words[10] ^= rotate(words[ 9] + words[ 8], 18u);

		words[12] ^= rotate(words[15] + words[14],  7u);  words[13] ^= rotate(words[12] + words[15],  9u);
		words[14] ^= rotate(words[13] + words[12], 13u);  words[15] ^= rotate(words[14] + words[13], 18u);
	}
	{ // salsa20_simd_shuffle
		local uchar *buffer = (local uchar*)(slice);
		const uint shuffled = salsa20_simd_shuffle[get_local_id(0)];
		uchar value = buffer[shuffled];
		buffer[get_local_id(0)] = value;
	}
	if(get_local_id(0) < 16) words[get_local_id(0)] += inputValue;
	barrier(CLK_LOCAL_MEM_FENCE);
}

void Blockmix_pwxform(local ulong *block, global ulong *valS, local ulong *gather, const uint yescrypt_r1) { // Throw away VALU power like there's no tomorrow
	const size_t YESCRYPT_S_P_SIZE = 8;
	//const size_t yescrypt_r1 = YESCRYPT_r * 128 / (YESCRYPT_S_P_SIZE * 8);
	{
		local uint *slice = (local uint*)(block);
		local uint *modifier = (local uint*)(block + (yescrypt_r1 - 1) * YESCRYPT_S_P_SIZE);
		for(uint loop = get_local_id(0); loop < YESCRYPT_S_P_SIZE * 2; loop += get_local_size(0)) { // 2 uints per ulong
			slice[loop] ^= modifier[loop];
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	Block_pwxform(block, valS, gather);
	for(uint loop = 1; loop < yescrypt_r1; loop++) {
		{
			local uint *slice = (local uint*)(block + loop * YESCRYPT_S_P_SIZE);
			local uint *modifier = (local uint*)(block + (loop - 1) * YESCRYPT_S_P_SIZE);
			for(uint loop = get_local_id(0); loop < YESCRYPT_S_P_SIZE * 2; loop += get_local_size(0)) { // 2 uints per ulong
				slice[loop] ^= modifier[loop];
			}
			barrier(CLK_LOCAL_MEM_FENCE);
		}
		Block_pwxform(block + loop * YESCRYPT_S_P_SIZE, valS, gather);
	}
	const uint i = (yescrypt_r1 - 1) * YESCRYPT_S_P_SIZE / 8;
	Salsa20_8(block + i * 8);
}

/* This was originally written 16x4 ways, keeping 4 hashes in flight but it turns out Smix2 approach is just better,
let the hardware hide latency across different wavefronts instead of providing more instructions. */
__attribute__((reqd_work_group_size(64, 1, 1)))
kernel void SecondSmix1(global ulong *valB, global ulong *valV, global ulong *valS, const uint yescrypt_N, const uint yescrypt_r1) {
	const size_t slot = get_global_id(1) - get_global_offset(1);
	valV += slot * V_STRIDE;
	valS += slot * S_STRIDE;
	valB += slot * B_STRIDE;
	local ulong block[16 * 8];
	{ // there's a complication here: the previous stage outputs uints, not ulongs so adjust
		global uchar *src = (global uchar*)(valB);
		global uchar *dstv = (global uchar*)(valV);
		local uchar *dst = (local uchar*)block;
		const uint shuffled = salsa20_simd_shuffle[get_local_id(0)];
		const uint byteOrder = (get_local_id(0) / 4) * 4 + 3 - get_local_id(0) % 4;
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			const uchar value = src[shuffled];
			dst[get_local_id(0)] = value;
			dstv[byteOrder] = value;
			src += 8 * 8;
			dst += 8 * 8;
			dstv += 8 * 8;
		}
		barrier(CLK_LOCAL_MEM_FENCE);
		local uint *swz = (local uint*)block;
		for(uint i = get_local_id(0); i < 16 * 8 * 2; i += get_local_size(0)) {
			const uint value = swz[i];
			swz[i] = as_uint(as_uchar4(value).s3210);
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	local ulong gather[4 * 4];
	Blockmix_pwxform(block, valS, gather, yescrypt_r1);
	const uint yescrypt_s = 16 * YESCRYPT_r;
	{ // blkcpy
		global uchar *dst = (global uchar*)(valV + yescrypt_s);
		local uchar *src = (local uchar*)block;
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			dst[get_local_id(0)] = src[get_local_id(0)];
			src += 8 * 8;
			dst += 8 * 8;
		}
		barrier(CLK_LOCAL_MEM_FENCE);
	}
	// Now I am inside the big 'else' branch
	Blockmix_pwxform(block, valS, gather, yescrypt_r1);
	const bool yescrypt_rw = true;
	uint yescrypt_n = 1; // this is ulong in reference, but for GBSTY 32 bits are enough. Same applies to loop iterator
	for(uint yescrypt_i = 2; yescrypt_i < yescrypt_N; yescrypt_i++) { // note I do a single block per iteration, CPU does 2
		event_t copied = async_work_group_copy(valV + yescrypt_i * yescrypt_s, block, yescrypt_s, 0);
		wait_group_events(1, &copied);

		if(yescrypt_rw) {
			if((yescrypt_i % 2) == 0) { // this condition isn't in yescrypt, I need it as I do one block per iteration
				if((yescrypt_i & (yescrypt_i - 1)) == 0) yescrypt_n <<= 1;
			}
			uint j = Integerify(block, YESCRYPT_r);
			j &= yescrypt_n - 1;
			j += yescrypt_i - yescrypt_n;

			global const uint *indirected = (global uint*)(valV + j * yescrypt_s);
			local uint *dword = (local uint*)block;
			for(size_t el = 0; el < (yescrypt_s * 2) / 64; el++) {
				dword[get_local_id(0)] ^= indirected[get_local_id(0)];
				dword += get_local_size(0);
				indirected += get_local_size(0);
			}
			// ^ no LDS collisions and low instruction count BUT stride 4 bytes access
			// to be compared against packed 64byte read (uchar*)indirected, el<yescrypt_s*8;
			// Better bandwidth usage, which was supposed to be the problem here.
			barrier(CLK_LOCAL_MEM_FENCE);
		}
		Blockmix_pwxform(block, valS, gather, yescrypt_r1);
	}
	// for GBSTY parameters, _n is 0x20 at this point.
	{
		global uchar *dst = (global uchar*)(valB);
		local uchar *src = (local uchar*)(block);
		const uint shuffled = salsa20_simd_unshuffle[get_local_id(0)];
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			dst[get_local_id(0)] = src[shuffled];
			src += 8 * 8;
			dst += 8 * 8;
		}
	}
}

#ifdef YESCRYPT_RW
/* Smix2 is a bit different. It still revolves around doing integerify and xorring (but also updating) Vj blocks.
What I've seen in smix1 is that we need to be smarter, we cannot keep on iterating host-side.
Plus, I really want to maximize cache usage and latency hiding to have nearly free global memory barriers...
... so I try something different. Instead of 16-way, I go even crazier with 64-way. SALU FTW */
__attribute__((reqd_work_group_size(64, 1, 1))) // a full wavefront to mangle an hash
kernel void Smix2(global ulong *valB, global ulong *valV, global ulong *valS, const uint yescrypt_NLoop, const uint yescrypt_r1) {
	const uint YESCRYPT_s = YESCRYPT_r * 16;
	//const uint yescrypt_NLoop = 682; // for first call
	//                          = 2; // for second call
	//const bool YESCRYPT_RW = 1; // for first call
	//                         0; // for second call
	const uint YESCRYPT_N = 2048;
	const size_t slot = get_global_id(1) - get_global_offset(1);
	valB += slot * B_STRIDE;
	valV += slot * V_STRIDE;
	valS += slot * S_STRIDE;
	local ulong block[16 * 8];
	local ulong gather[4 * 4];

	{
		global uchar *src = (global uchar*)(valB);
		local uchar *dst = (local uchar*)block;
		const uint shuffled = salsa20_simd_shuffle[get_local_id(0)];
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			dst[get_local_id(0)] = src[shuffled];
			src += 8 * 8;
			dst += 8 * 8;
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	for(uint loop = 0; loop < yescrypt_NLoop; loop++) {
		uint j = Integerify(block, YESCRYPT_r);
		j &= (YESCRYPT_N - 1); // modulo pow2
		blkxor(block, valV + j * YESCRYPT_s, YESCRYPT_s);
		if(YESCRYPT_RW) blkcpy(valV + j * YESCRYPT_s, block, YESCRYPT_s);
		Blockmix_pwxform(block, valS, gather, yescrypt_r1);
		barrier(CLK_GLOBAL_MEM_FENCE);
	}
	{
		global uchar *dst = (global uchar*)(valB);
		local uchar *src = (local uchar*)(block);
		const uint shuffled = salsa20_simd_unshuffle[get_local_id(0)];
		for(uint i = 0; i < 2 * YESCRYPT_r; i++) {
			dst[get_local_id(0)] = src[shuffled];
			src += 8 * 8;
			dst += 8 * 8;
		}
	}
}
#endif