precision highp float;
precision highp int;

// --- applyMaterial constants definition
uniform int MAX_STEPS;
uniform float MAX_DIST;
uniform float SURF_DIST;
uniform float NORMALS_BIAS;
uniform float SHADOW_BIAS;
#define ZERO 0
uniform float debugMinSteps;
uniform float debugMaxSteps;
uniform float debugMinDepth;
uniform float debugMaxDepth;

#include <common>
#include <packing>
#include <lightmap_pars_fragment>
#include <bsdfs>
#include <cube_uv_reflection_fragment>
#include <lights_pars_begin>
#include <lights_physical_pars_fragment>
#include <shadowmap_pars_fragment>
#include <fog_pars_fragment>

#if defined( SHADOW_DISTANCE )
	uniform float shadowDistanceMin;
	uniform float shadowDistanceMax;
#endif 
#if defined( SHADOW_DEPTH )
	uniform float shadowDepthMin;
	uniform float shadowDepthMax;
#endif

// varying vec2 vHighPrecisionZW;

varying vec3 vPw;
varying mat4 vModelMatrix;
varying mat4 vInverseModelMatrix;
varying mat4 VViewMatrix;

#if NUM_SPOT_LIGHTS > 0
	struct SpotLightRayMarching {
		float penumbra;
		float shadowBiasAngle;
		float shadowBiasDistance;
	};
	uniform SpotLightRayMarching spotLightsRayMarching[ NUM_SPOT_LIGHTS ];
	#if NUM_SPOT_LIGHT_COORDS > 0

		uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ];

	#endif
#endif
#if NUM_DIR_LIGHTS > 0
	struct DirectionalLightRayMarching {
		float penumbra;
		float shadowBiasAngle;
		float shadowBiasDistance;
	};
	uniform DirectionalLightRayMarching directionalLightsRayMarching[ NUM_DIR_LIGHTS ];
	#if NUM_DIR_LIGHT_SHADOWS > 0

		uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];

	#endif
#endif
#if NUM_POINT_LIGHTS > 0
	struct PointLightRayMarching {
		float penumbra;
		float shadowBiasAngle;
		float shadowBiasDistance;
	};
	uniform PointLightRayMarching pointLightsRayMarching[ NUM_POINT_LIGHTS ];
	#if NUM_POINT_LIGHT_SHADOWS > 0

		uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];

	#endif
#endif


struct SDFContext {
	float d;
	int stepsCount;
	int matId;
	int matId2;
	float matBlend;
};

SDFContext DefaultSDFContext(){
	return SDFContext( 0., 0, 0, 0, 0. );
}
int DefaultSDFMaterial(){
	return 0;
}

// start raymarching builder define code



// /geo1/MAT/rayMarchingBuilder1/fitFrom01_1
//
//
// FIT
//
//
float fit(float val, float srcMin, float srcMax, float destMin, float destMax){
	float src_range = srcMax - srcMin;
	float dest_range = destMax - destMin;

	float r = (val - srcMin) / src_range;
	return (r * dest_range) + destMin;
}
vec2 fit(vec2 val, vec2 srcMin, vec2 srcMax, vec2 destMin, vec2 destMax){
	return vec2(
		fit(val.x, srcMin.x, srcMax.x, destMin.x, destMax.x),
		fit(val.y, srcMin.y, srcMax.y, destMin.y, destMax.y)
	);
}
vec3 fit(vec3 val, vec3 srcMin, vec3 srcMax, vec3 destMin, vec3 destMax){
	return vec3(
		fit(val.x, srcMin.x, srcMax.x, destMin.x, destMax.x),
		fit(val.y, srcMin.y, srcMax.y, destMin.y, destMax.y),
		fit(val.z, srcMin.z, srcMax.z, destMin.z, destMax.z)
	);
}
vec4 fit(vec4 val, vec4 srcMin, vec4 srcMax, vec4 destMin, vec4 destMax){
	return vec4(
		fit(val.x, srcMin.x, srcMax.x, destMin.x, destMax.x),
		fit(val.y, srcMin.y, srcMax.y, destMin.y, destMax.y),
		fit(val.z, srcMin.z, srcMax.z, destMin.z, destMax.z),
		fit(val.w, srcMin.w, srcMax.w, destMin.w, destMax.w)
	);
}

//
//
// FIT TO 01
// fits the range [srcMin, srcMax] to [0, 1]
//
float fitTo01(float val, float srcMin, float srcMax){
	float size = srcMax - srcMin;
	return (val - srcMin) / size;
}
vec2 fitTo01(vec2 val, vec2 srcMin, vec2 srcMax){
	return vec2(
		fitTo01(val.x, srcMin.x, srcMax.x),
		fitTo01(val.y, srcMin.y, srcMax.y)
	);
}
vec3 fitTo01(vec3 val, vec3 srcMin, vec3 srcMax){
	return vec3(
		fitTo01(val.x, srcMin.x, srcMax.x),
		fitTo01(val.y, srcMin.y, srcMax.y),
		fitTo01(val.z, srcMin.z, srcMax.z)
	);
}
vec4 fitTo01(vec4 val, vec4 srcMin, vec4 srcMax){
	return vec4(
		fitTo01(val.x, srcMin.x, srcMax.x),
		fitTo01(val.y, srcMin.y, srcMax.y),
		fitTo01(val.z, srcMin.z, srcMax.z),
		fitTo01(val.w, srcMin.w, srcMax.w)
	);
}

//
//
// FIT FROM 01
// fits the range [0, 1] to [destMin, destMax]
//
float fitFrom01(float val, float destMin, float destMax){
	return fit(val, 0.0, 1.0, destMin, destMax);
}
vec2 fitFrom01(vec2 val, vec2 srcMin, vec2 srcMax){
	return vec2(
		fitFrom01(val.x, srcMin.x, srcMax.x),
		fitFrom01(val.y, srcMin.y, srcMax.y)
	);
}
vec3 fitFrom01(vec3 val, vec3 srcMin, vec3 srcMax){
	return vec3(
		fitFrom01(val.x, srcMin.x, srcMax.x),
		fitFrom01(val.y, srcMin.y, srcMax.y),
		fitFrom01(val.z, srcMin.z, srcMax.z)
	);
}
vec4 fitFrom01(vec4 val, vec4 srcMin, vec4 srcMax){
	return vec4(
		fitFrom01(val.x, srcMin.x, srcMax.x),
		fitFrom01(val.y, srcMin.y, srcMax.y),
		fitFrom01(val.z, srcMin.z, srcMax.z),
		fitFrom01(val.w, srcMin.w, srcMax.w)
	);
}

//
//
// FIT FROM 01 TO VARIANCE
// fits the range [0, 1] to [center - variance, center + variance]
//
float fitFrom01ToVariance(float val, float center, float variance){
	return fitFrom01(val, center - variance, center + variance);
}
vec2 fitFrom01ToVariance(vec2 val, vec2 center, vec2 variance){
	return vec2(
		fitFrom01ToVariance(val.x, center.x, variance.x),
		fitFrom01ToVariance(val.y, center.y, variance.y)
	);
}
vec3 fitFrom01ToVariance(vec3 val, vec3 center, vec3 variance){
	return vec3(
		fitFrom01ToVariance(val.x, center.x, variance.x),
		fitFrom01ToVariance(val.y, center.y, variance.y),
		fitFrom01ToVariance(val.z, center.z, variance.z)
	);
}
vec4 fitFrom01ToVariance(vec4 val, vec4 center, vec4 variance){
	return vec4(
		fitFrom01ToVariance(val.x, center.x, variance.x),
		fitFrom01ToVariance(val.y, center.y, variance.y),
		fitFrom01ToVariance(val.z, center.z, variance.z),
		fitFrom01ToVariance(val.w, center.w, variance.w)
	);
}

// /geo1/MAT/rayMarchingBuilder1/rotate1


// https://stackoverflow.com/questions/23793698/how-to-implement-slerp-in-glsl-hlsl
// vec4 quatSlerp(vec4 p0, vec4 p1, float t)
// {
// 	float dotp = dot(normalize(p0), normalize(p1));
// 	if ((dotp > 0.9999) || (dotp < -0.9999))
// 	{
// 		if (t<=0.5)
// 			return p0;
// 		return p1;
// 	}
// 	float theta = acos(dotp);
// 	vec4 P = ((p0*sin((1.0-t)*theta) + p1*sin(t*theta)) / sin(theta));
// 	P.w = 1.0;
// 	return P;
// }

// https://devcry.heiho.net/html/2017/20170521-slerp.html
// float lerp(float a, float b, float t) {
// 	return (1.0 - t) * a + t * b;
// }
// vec4 quatSlerp(vec4 p0, vec4 p1, float t){
// 	vec4 qb = p1;

// 	// cos(a) = dot product
// 	float cos_a = p0.x * qb.x + p0.y * qb.y + p0.z * qb.z + p0.w * qb.w;
// 	if (cos_a < 0.0f) {
// 		cos_a = -cos_a;
// 		qb = -qb;
// 	}

// 	// close to zero, cos(a) ~= 1
// 	// do linear interpolation
// 	if (cos_a > 0.999) {
// 		return vec4(
// 			lerp(p0.x, qb.x, t),
// 			lerp(p0.y, qb.y, t),
// 			lerp(p0.z, qb.z, t),
// 			lerp(p0.w, qb.w, t)
// 		);
// 	}

// 	float alpha = acos(cos_a);
// 	return (p0 * sin(1.0 - t) + p1 * sin(t * alpha)) / sin(alpha);
// }

// https://stackoverflow.com/questions/62943083/interpolate-between-two-quaternions-the-long-way
vec4 quatSlerp(vec4 q1, vec4 q2, float t){
	float angle = acos(dot(q1, q2));
	float denom = sin(angle);
	//check if denom is zero
	return (q1*sin((1.0-t)*angle)+q2*sin(t*angle))/denom;
}
// TO CHECK:
// this page https://www.reddit.com/r/opengl/comments/704la7/glsl_quaternion_library/
// has a link to a potentially nice pdf:
// http://web.mit.edu/2.998/www/QuaternionReport1.pdf

// https://github.com/mattatz/ShibuyaCrowd/blob/master/source/shaders/common/quaternion.glsl
vec4 quatMult(vec4 q1, vec4 q2)
{
	return vec4(
	q1.w * q2.x + q1.x * q2.w + q1.z * q2.y - q1.y * q2.z,
	q1.w * q2.y + q1.y * q2.w + q1.x * q2.z - q1.z * q2.x,
	q1.w * q2.z + q1.z * q2.w + q1.y * q2.x - q1.x * q2.y,
	q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
	);
}
// http://glmatrix.net/docs/quat.js.html#line97
//   let ax = a[0], ay = a[1], az = a[2], aw = a[3];

//   let bx = b[0], by = b[1], bz = b[2], bw = b[3];

//   out[0] = ax * bw + aw * bx + ay * bz - az * by;

//   out[1] = ay * bw + aw * by + az * bx - ax * bz;

//   out[2] = az * bw + aw * bz + ax * by - ay * bx;

//   out[3] = aw * bw - ax * bx - ay * by - az * bz;

//   return out



// http://www.neilmendoza.com/glsl-rotation-about-an-arbitrary-axis/
mat4 rotationMatrix(vec3 axis, float angle)
{
	axis = normalize(axis);
	float s = sin(angle);
	float c = cos(angle);
	float oc = 1.0 - c;

 	return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s,  oc * axis.z * axis.x + axis.y * s, 0.0, oc * axis.x * axis.y + axis.z * s,  oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,  0.0, oc * axis.z * axis.x - axis.y * s,  oc * axis.y * axis.z + axis.x * s,  oc * axis.z * axis.z + c, 0.0, 0.0, 0.0, 0.0, 1.0);
}

// https://www.geeks3d.com/20141201/how-to-rotate-a-vertex-by-a-quaternion-in-glsl/
vec4 quatFromAxisAngle(vec3 axis, float angle)
{
	vec4 qr;
	float half_angle = (angle * 0.5); // * 3.14159 / 180.0;
	float sin_half_angle = sin(half_angle);
	qr.x = axis.x * sin_half_angle;
	qr.y = axis.y * sin_half_angle;
	qr.z = axis.z * sin_half_angle;
	qr.w = cos(half_angle);
	return qr;
}
vec3 rotateWithAxisAngle(vec3 position, vec3 axis, float angle)
{
	vec4 q = quatFromAxisAngle(axis, angle);
	vec3 v = position.xyz;
	return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
}
// vec3 applyQuaternionToVector( vec4 q, vec3 v ){
// 	return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
// }
vec3 rotateWithQuat( vec3 v, vec4 q )
{
	// vec4 qv = multQuat( quat, vec4(vec, 0.0) );
	// return multQuat( qv, vec4(-quat.x, -quat.y, -quat.z, quat.w) ).xyz;
	return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
}
// https://github.com/glslify/glsl-look-at/blob/gh-pages/index.glsl
// mat3 rotation_matrix(vec3 origin, vec3 target, float roll) {
// 	vec3 rr = vec3(sin(roll), cos(roll), 0.0);
// 	vec3 ww = normalize(target - origin);
// 	vec3 uu = normalize(cross(ww, rr));
// 	vec3 vv = normalize(cross(uu, ww));

// 	return mat3(uu, vv, ww);
// }
// mat3 rotation_matrix(vec3 target, float roll) {
// 	vec3 rr = vec3(sin(roll), cos(roll), 0.0);
// 	vec3 ww = normalize(target);
// 	vec3 uu = normalize(cross(ww, rr));
// 	vec3 vv = normalize(cross(uu, ww));

// 	return mat3(uu, vv, ww);
// }

float vectorAngle(vec3 start, vec3 dest){
	start = normalize(start);
	dest = normalize(dest);

	float cosTheta = dot(start, dest);
	vec3 c1 = cross(start, dest);
	// We use the dot product of the cross with the Y axis.
	// This is a little arbitrary, but can still give a good sense of direction
	vec3 y_axis = vec3(0.0, 1.0, 0.0);
	float d1 = dot(c1, y_axis);
	float angle = acos(cosTheta) * sign(d1);
	return angle;
}

// http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/#i-need-an-equivalent-of-glulookat-how-do-i-orient-an-object-towards-a-point-
vec4 vectorAlign(vec3 start, vec3 dest){
	start = normalize(start);
	dest = normalize(dest);

	float cosTheta = dot(start, dest);
	vec3 axis;

	// if (cosTheta < -1 + 0.001f){
	// 	// special case when vectors in opposite directions:
	// 	// there is no ideal rotation axis
	// 	// So guess one; any will do as long as it's perpendicular to start
	// 	axis = cross(vec3(0.0f, 0.0f, 1.0f), start);
	// 	if (length2(axis) < 0.01 ) // bad luck, they were parallel, try again!
	// 		axis = cross(vec3(1.0f, 0.0f, 0.0f), start);

	// 	axis = normalize(axis);
	// 	return gtx::quaternion::angleAxis(glm::radians(180.0f), axis);
	// }
	if(cosTheta > (1.0 - 0.0001) || cosTheta < (-1.0 + 0.0001) ){
		axis = normalize(cross(start, vec3(0.0, 1.0, 0.0)));
		if (length(axis) < 0.001 ){ // bad luck, they were parallel, try again!
			axis = normalize(cross(start, vec3(1.0, 0.0, 0.0)));
		}
	} else {
		axis = normalize(cross(start, dest));
	}

	float angle = acos(cosTheta);

	return quatFromAxisAngle(axis, angle);
}
vec4 vectorAlignWithUp(vec3 start, vec3 dest, vec3 up){
	vec4 rot1 = vectorAlign(start, dest);
	up = normalize(up);

	// Recompute desiredUp so that it's perpendicular to the direction
	// You can skip that part if you really want to force desiredUp
	// vec3 right = normalize(cross(dest, up));
	// up = normalize(cross(right, dest));

	// Because of the 1rst rotation, the up is probably completely screwed up.
	// Find the rotation between the up of the rotated object, and the desired up
	vec3 newUp = rotateWithQuat(vec3(0.0, 1.0, 0.0), rot1);//rot1 * vec3(0.0, 1.0, 0.0);
	vec4 rot2 = vectorAlign(up, newUp);

	// return rot1;
	return rot2;
	// return multQuat(rot1, rot2);
	// return rot2 * rot1;

}

// https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
float quatToAngle(vec4 q){
	return 2.0 * acos(q.w);
}
vec3 quatToAxis(vec4 q){
	return vec3(
		q.x / sqrt(1.0-q.w*q.w),
		q.y / sqrt(1.0-q.w*q.w),
		q.z / sqrt(1.0-q.w*q.w)
	);
}

vec4 align(vec3 dir, vec3 up){
	vec3 start_dir = vec3(0.0, 0.0, 1.0);
	vec3 start_up = vec3(0.0, 1.0, 0.0);
	vec4 rot1 = vectorAlign(start_dir, dir);
	up = normalize(up);

	// Recompute desiredUp so that it's perpendicular to the direction
	// You can skip that part if you really want to force desiredUp
	vec3 right = normalize(cross(dir, up));
	if(length(right)<0.001){
		right = vec3(1.0, 0.0, 0.0);
	}
	up = normalize(cross(right, dir));

	// Because of the 1rst rotation, the up is probably completely screwed up.
	// Find the rotation between the up of the rotated object, and the desired up
	vec3 newUp = rotateWithQuat(start_up, rot1);//rot1 * vec3(0.0, 1.0, 0.0);
	vec4 rot2 = vectorAlign(normalize(newUp), up);

	// return rot1;
	return quatMult(rot1, rot2);
	// return rot2 * rot1;

}

// /geo1/MAT/rayMarchingBuilder1/SDFBoxFrame1
float dot2( in vec2 v ) { return dot(v,v); }
float dot2( in vec3 v ) { return dot(v,v); }
float ndot( in vec2 a, in vec2 b ) { return a.x*b.x - a.y*b.y; }
// https://iquilezles.org/articles/distfunctions/


/*
*
* SDF PRIMITIVES
*
*/
float sdSphere( vec3 p, float s )
{
	return length(p)-s;
}
float sdCutSphere( vec3 p, float r, float h )
{
	// sampling independent computations (only depend on shape)
	float w = sqrt(r*r-h*h);

	// sampling dependant computations
	vec2 q = vec2( length(p.xz), p.y );
	float s = max( (h-r)*q.x*q.x+w*w*(h+r-2.0*q.y), h*q.x-w*q.y );
	return (s<0.0) ? length(q)-r :
				(q.x<w) ? h - q.y :
					length(q-vec2(w,h));
}
float sdCutHollowSphere( vec3 p, float r, float h, float t )
{
	// sampling independent computations (only depend on shape)
	float w = sqrt(r*r-h*h);
	
	// sampling dependant computations
	vec2 q = vec2( length(p.xz), p.y );
	return ((h*q.x<w*q.y) ? length(q-vec2(w,h)) : 
							abs(length(q)-r) ) - t;
}

float sdBox( vec3 p, vec3 b )
{
	vec3 q = abs(p) - b*0.5;
	return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}
float sdRoundBox( vec3 p, vec3 b, float r )
{
	vec3 q = abs(p) - b*0.5;
	return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0) - r;
}


float sdBoxFrame( vec3 p, vec3 b, float e )
{
		p = abs(p  )-b*0.5;
	vec3 q = abs(p+e)-e;
	return min(min(
		length(max(vec3(p.x,q.y,q.z),0.0))+min(max(p.x,max(q.y,q.z)),0.0),
		length(max(vec3(q.x,p.y,q.z),0.0))+min(max(q.x,max(p.y,q.z)),0.0)),
		length(max(vec3(q.x,q.y,p.z),0.0))+min(max(q.x,max(q.y,p.z)),0.0));
}
float sdCapsule( vec3 p, vec3 a, vec3 b, float r )
{
	vec3 pa = p - a, ba = b - a;
	float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
	return length( pa - ba*h ) - r;
}
float sdVerticalCapsule( vec3 p, float h, float r )
{
	p.y -= clamp( p.y, 0.0, h );
	return length( p ) - r;
}
float sdCone( in vec3 p, in vec2 c, float h )
{
	// c is the sin/cos of the angle, h is height
	// Alternatively pass q instead of (c,h),
	// which is the point at the base in 2D
	vec2 q = h*vec2(c.x/c.y,-1.0);

	vec2 w = vec2( length(p.xz), p.y );
	vec2 a = w - q*clamp( dot(w,q)/dot(q,q), 0.0, 1.0 );
	vec2 b = w - q*vec2( clamp( w.x/q.x, 0.0, 1.0 ), 1.0 );
	float k = sign( q.y );
	float d = min(dot( a, a ),dot(b, b));
	float s = max( k*(w.x*q.y-w.y*q.x),k*(w.y-q.y)  );
	return sqrt(d)*sign(s);
}
float sdConeWrapped(vec3 pos, float angle, float height){
	return sdCone(pos, vec2(sin(angle), cos(angle)), height);
}
float sdRoundCone( vec3 p, float r1, float r2, float h )
{
	float b = (r1-r2)/h;
	float a = sqrt(1.0-b*b);

	vec2 q = vec2( length(p.xz), p.y );
	float k = dot(q,vec2(-b,a));
	if( k<0.0 ) return length(q) - r1;
	if( k>a*h ) return length(q-vec2(0.0,h)) - r2;
	return dot(q, vec2(a,b) ) - r1;
}
float sdOctogonPrism( in vec3 p, in float r, float h )
{
	const vec3 k = vec3(-0.9238795325,  // sqrt(2+sqrt(2))/2 
						0.3826834323,   // sqrt(2-sqrt(2))/2
						0.4142135623 ); // sqrt(2)-1 
	// reflections
	p = abs(p);
	p.xy -= 2.0*min(dot(vec2( k.x,k.y),p.xy),0.0)*vec2( k.x,k.y);
	p.xy -= 2.0*min(dot(vec2(-k.x,k.y),p.xy),0.0)*vec2(-k.x,k.y);
	// polygon side
	p.xy -= vec2(clamp(p.x, -k.z*r, k.z*r), r);
	vec2 d = vec2( length(p.xy)*sign(p.y), p.z-h );
	return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
float sdHexPrism( vec3 p, vec2 h )
{
	const vec3 k = vec3(-0.8660254, 0.5, 0.57735);
	p = abs(p);
	p.xy -= 2.0*min(dot(k.xy, p.xy), 0.0)*k.xy;
	vec2 d = vec2(
		length(p.xy-vec2(clamp(p.x,-k.z*h.x,k.z*h.x), h.x))*sign(p.y-h.x),
		p.z-h.y );
	return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
float sdHorseshoe( in vec3 p, in float angle, in float r, in float le, vec2 w )
{
	vec2 c = vec2(cos(angle),sin(angle));
	p.x = abs(p.x);
	float l = length(p.xy);
	p.xy = mat2(-c.x, c.y, 
			c.y, c.x)*p.xy;
	p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x),
				(p.x>0.0)?p.y:l );
	p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0);
	
	vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z);
	vec2 d = abs(q) - w;
	return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
float sdTriPrism( vec3 p, vec2 h )
{
	vec3 q = abs(p);
	return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5);
}
float sdPyramid( vec3 p, float h)
{
	float m2 = h*h + 0.25;

	p.xz = abs(p.xz);
	p.xz = (p.z>p.x) ? p.zx : p.xz;
	p.xz -= 0.5;

	vec3 q = vec3( p.z, h*p.y - 0.5*p.x, h*p.x + 0.5*p.y);

	float s = max(-q.x,0.0);
	float t = clamp( (q.y-0.5*p.z)/(m2+0.25), 0.0, 1.0 );

	float a = m2*(q.x+s)*(q.x+s) + q.y*q.y;
	float b = m2*(q.x+0.5*t)*(q.x+0.5*t) + (q.y-m2*t)*(q.y-m2*t);

	float d2 = min(q.y,-q.x*m2-q.y*0.5) > 0.0 ? 0.0 : min(a,b);

	return sqrt( (d2+q.z*q.z)/m2 ) * sign(max(q.z,-p.y));
}

float sdPlane( vec3 p, vec3 n, float h )
{
	// n must be normalized
	return dot(p,n) + h;
}

float sdTorus( vec3 p, vec2 t )
{
	vec2 q = vec2(length(p.xz)-t.x,p.y);
	return length(q)-t.y;
}
float sdCappedTorus(in vec3 p, in float an, in float ra, in float rb)
{
	vec2 sc = vec2(sin(an),cos(an));
	p.x = abs(p.x);
	float k = (sc.y*p.x>sc.x*p.z) ? dot(p.xz,sc) : length(p.xz);
	return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb;
}
float sdLink( vec3 p, float le, float r1, float r2 )
{
  vec3 q = vec3( p.x, max(abs(p.y)-le,0.0), p.z );
  return length(vec2(length(q.xy)-r1,q.z)) - r2;
}
// c is the sin/cos of the desired cone angle
float sdSolidAngle(vec3 pos, vec2 c, float radius)
{
	vec2 p = vec2( length(pos.xz), pos.y );
	float l = length(p) - radius;
	float m = length(p - c*clamp(dot(p,c),0.0,radius) );
	return max(l,m*sign(c.y*p.x-c.x*p.y));
}
float sdSolidAngleWrapped(vec3 pos, float angle, float radius){
	return sdSolidAngle(pos, vec2(sin(angle), cos(angle)), radius);
}
float sdTube( vec3 p, float r )
{
	return length(p.xz)-r;
}
float sdTubeCapped( vec3 p, float h, float r )
{
	vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(r,h);
	return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
float sdOctahedron( vec3 p, float s)
{
  p = abs(p);
  float m = p.x+p.y+p.z-s;
  vec3 q;
       if( 3.0*p.x < m ) q = p.xyz;
  else if( 3.0*p.y < m ) q = p.yzx;
  else if( 3.0*p.z < m ) q = p.zxy;
  else return m*0.57735027;
    
  float k = clamp(0.5*(q.z-q.y+s),0.0,s); 
  return length(vec3(q.x,q.y-s+k,q.z-k)); 
}
float udTriangle( vec3 p, vec3 a, vec3 b, vec3 c, float thickness )
{
	vec3 ba = b - a; vec3 pa = p - a;
	vec3 cb = c - b; vec3 pb = p - b;
	vec3 ac = a - c; vec3 pc = p - c;
	vec3 nor = cross( ba, ac );

	return - thickness + sqrt(
		(sign(dot(cross(ba,nor),pa)) +
		sign(dot(cross(cb,nor),pb)) +
		sign(dot(cross(ac,nor),pc))<2.0)
		?
		min( min(
		dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa),
		dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ),
		dot2(ac*clamp(dot(ac,pc)/dot2(ac),0.0,1.0)-pc) )
		:
		dot(nor,pa)*dot(nor,pa)/dot2(nor) );
}
float udQuad( vec3 p, vec3 a, vec3 b, vec3 c, vec3 d, float thickness )
{
	vec3 ba = b - a; vec3 pa = p - a;
	vec3 cb = c - b; vec3 pb = p - b;
	vec3 dc = d - c; vec3 pc = p - c;
	vec3 ad = a - d; vec3 pd = p - d;
	vec3 nor = cross( ba, ad );

	return - thickness + sqrt(
		(sign(dot(cross(ba,nor),pa)) +
		sign(dot(cross(cb,nor),pb)) +
		sign(dot(cross(dc,nor),pc)) +
		sign(dot(cross(ad,nor),pd))<3.0)
		?
		min( min( min(
		dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa),
		dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ),
		dot2(dc*clamp(dot(dc,pc)/dot2(dc),0.0,1.0)-pc) ),
		dot2(ad*clamp(dot(ad,pd)/dot2(ad),0.0,1.0)-pd) )
		:
		dot(nor,pa)*dot(nor,pa)/dot2(nor) );
}

/*
*
* SDF OPERATIONS
*
*/
float SDFUnion( float d1, float d2 ) { return min(d1,d2); }
float SDFSubtract( float d1, float d2 ) { return max(-d1,d2); }
float SDFIntersect( float d1, float d2 ) { return max(d1,d2); }

float SDFSmoothUnion( float d1, float d2, float k ) {
	float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
	return mix( d2, d1, h ) - k*h*(1.0-h);
}

float SDFSmoothSubtract( float d1, float d2, float k ) {
	float h = clamp( 0.5 - 0.5*(d2+d1)/k, 0.0, 1.0 );
	return mix( d2, -d1, h ) + k*h*(1.0-h);
}

float SDFSmoothIntersect( float d1, float d2, float k ) {
	float h = clamp( 0.5 - 0.5*(d2-d1)/k, 0.0, 1.0 );
	return mix( d2, d1, h ) + k*h*(1.0-h);
}

vec4 SDFElongateFast( in vec3 p, in vec3 h )
{
	return vec4( p-clamp(p,-h,h), 0.0 );
}
vec4 SDFElongateSlow( in vec3 p, in vec3 h )
{
	vec3 q = abs(p)-h;
	return vec4( max(q,0.0), min(max(q.x,max(q.y,q.z)),0.0) );
}

float SDFOnion( in float sdf, in float thickness )
{
	return abs(sdf)-thickness;
}

// /geo1/MAT/rayMarchingBuilder1/easing1

float sineInOut(float t) {
  return -0.5 * (cos(PI * t) - 1.0);
}



// /geo1/MAT/rayMarchingBuilder1/hsvToRgb1
// https://github.com/hughsk/glsl-hsv2rgb
// https://stackoverflow.com/questions/15095909/from-rgb-to-hsv-in-opengl-glsl
vec3 hsv2rgb(vec3 c) {
	vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
	vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
	return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

// /geo1/MAT/rayMarchingBuilder1/SDFMaterial1
const int _GEO1_MAT_RAYMARCHINGBUILDER1_SDFMATERIAL1 = 1;

struct EnvMapProps {
	vec3 tint;
	float intensity;
	float roughness;
	float fresnel;
	float fresnelPower;
};
uniform sampler2D envMap;
uniform float envMapIntensity;
uniform float roughness;
#ifdef ROTATE_ENV_MAP_Y
	uniform float envMapRotationY;
#endif
vec3 envMapSample(vec3 rayDir, float envMapRoughness){
	// http://www.pocketgl.com/reflections/
	vec3 env = vec3(0.);
	// vec2 uv = vec2( atan( -rayDir.z, -rayDir.x ) * RECIPROCAL_PI2 + 0.5, rayDir.y * 0.5 + 0.5 );
	// vec3 env = texture2D(map, uv).rgb;
	#ifdef ENVMAP_TYPE_CUBE_UV
		#ifdef ROTATE_ENV_MAP_Y
			rayDir = rotateWithAxisAngle(rayDir, vec3(0.,1.,0.), envMapRotationY);
		#endif
		env = textureCubeUV(envMap, rayDir, envMapRoughness * roughness).rgb;
	#endif
	return env;
}
vec3 envMapSampleWithFresnel(vec3 rayDir, EnvMapProps envMapProps, vec3 n, vec3 cameraPosition){
	// http://www.pocketgl.com/reflections/
	vec3 env = envMapSample(rayDir, envMapProps.roughness);
	float fresnel = pow(1.-dot(normalize(cameraPosition), n), envMapProps.fresnelPower);
	float fresnelFactor = (1.-envMapProps.fresnel) + envMapProps.fresnel*fresnel;
	return env * envMapIntensity * envMapProps.tint * envMapProps.intensity * fresnelFactor;
}
#define RAYMARCHED_REFRACTIONS 1
#define RAYMARCHED_REFRACTIONS_SAMPLE_ENV_MAP_ON_LAST 1
#define RAYMARCHED_REFRACTIONS_START_OUTSIDE_MEDIUM 1







// /geo1/MAT/rayMarchingBuilder1/globals7
uniform float time;






SDFContext GetDist(vec3 p) {
	SDFContext sdfContext = SDFContext(0., 0, 0, 0, 0.);

	// start GetDist builder body code



	// /geo1/MAT/rayMarchingBuilder1/globals7
	float v_POLY_globals7_time = time;
	vec3 v_POLY_globals7_position = p;
	
	// /geo1/MAT/rayMarchingBuilder1/globals2
	vec3 v_POLY_globals2_position = p;
	
	// /geo1/MAT/rayMarchingBuilder1/globals1
	vec3 v_POLY_globals1_position = p;
	float v_POLY_globals1_time = time;
	
	// /geo1/MAT/rayMarchingBuilder1/globals6
	float v_POLY_globals6_time = time;
	
	// /geo1/MAT/rayMarchingBuilder1/globals3
	vec3 v_POLY_globals3_position = p;
	
	// /geo1/MAT/rayMarchingBuilder1/globals4
	float v_POLY_globals4_time = time;
	
	// /geo1/MAT/rayMarchingBuilder1/vec3ToFloat1
	float v_POLY_vec3ToFloat1_x = v_POLY_globals7_position.x;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd1
	float v_POLY_multAdd1_val = (0.57*(v_POLY_globals1_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd2
	float v_POLY_multAdd2_val = (0.69*(v_POLY_globals1_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd3
	float v_POLY_multAdd3_val = (0.22*(v_POLY_globals1_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd4
	float v_POLY_multAdd4_val = (0.52*(v_POLY_globals1_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd6
	float v_POLY_multAdd6_val = (0.72*(v_POLY_globals1_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd7
	float v_POLY_multAdd7_val = (0.89*(v_POLY_globals1_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd8
	float v_POLY_multAdd8_val = (0.3*(v_POLY_globals1_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd14
	float v_POLY_multAdd14_val = (0.18*(v_POLY_globals6_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd9
	float v_POLY_multAdd9_val = (0.35*(v_POLY_globals4_time + 4.2)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd15
	float v_POLY_multAdd15_val = (0.18*(v_POLY_globals7_time + v_POLY_vec3ToFloat1_x)) + 0.1;
	
	// /geo1/MAT/rayMarchingBuilder1/floatToVec3_2
	vec3 v_POLY_floatToVec3_2_vec3 = vec3(v_POLY_multAdd1_val, v_POLY_multAdd2_val, 0.0);
	
	// /geo1/MAT/rayMarchingBuilder1/cos1
	float v_POLY_cos1_val = cos(v_POLY_multAdd4_val);
	
	// /geo1/MAT/rayMarchingBuilder1/floatToVec3_3
	vec3 v_POLY_floatToVec3_3_vec3 = vec3(v_POLY_multAdd6_val, v_POLY_multAdd7_val, 0.0);
	
	// /geo1/MAT/rayMarchingBuilder1/smoothstep1
	float v_POLY_smoothstep1_val = smoothstep(0.0, 1.0, v_POLY_multAdd14_val);
	
	// /geo1/MAT/rayMarchingBuilder1/sin1
	float v_POLY_sin1_val = sin(v_POLY_multAdd9_val);
	
	// /geo1/MAT/rayMarchingBuilder1/smoothstep2
	float v_POLY_smoothstep2_val = smoothstep(0.0, 1.0, v_POLY_multAdd15_val);
	
	// /geo1/MAT/rayMarchingBuilder1/normalize1
	vec3 v_POLY_normalize1_normalized = normalize(v_POLY_floatToVec3_2_vec3);
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd5
	float v_POLY_multAdd5_val = (1.3*(v_POLY_cos1_val + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/normalize2
	vec3 v_POLY_normalize2_normalized = normalize(v_POLY_floatToVec3_3_vec3);
	
	// /geo1/MAT/rayMarchingBuilder1/fitFrom01_1
	float v_POLY_fitFrom01_1_val = fitFrom01(v_POLY_smoothstep1_val, 0.4, 0.0);
	
	// /geo1/MAT/rayMarchingBuilder1/fit1
	float v_POLY_fit1_val = fit(v_POLY_sin1_val, -1.0, 1.0, -1.0, 2.0);
	
	// /geo1/MAT/rayMarchingBuilder1/fitFrom01_2
	float v_POLY_fitFrom01_2_val = fitFrom01(v_POLY_smoothstep2_val, 0.12, 0.0);
	
	// /geo1/MAT/rayMarchingBuilder1/rotate1
	vec3 v_POLY_rotate1_val = rotateWithAxisAngle(v_POLY_globals2_position, v_POLY_normalize1_normalized, v_POLY_multAdd3_val);
	
	// /geo1/MAT/rayMarchingBuilder1/floatToVec3_1
	vec3 v_POLY_floatToVec3_1_vec3 = vec3(v_POLY_multAdd5_val, 0.0, 0.0);
	
	// /geo1/MAT/rayMarchingBuilder1/clamp1
	float v_POLY_clamp1_val = clamp(v_POLY_fit1_val, 0.0, 1.0);
	
	// /geo1/MAT/rayMarchingBuilder1/SDFBoxFrame1
	float v_POLY_SDFBoxFrame1_float = sdBoxFrame(v_POLY_rotate1_val - vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0)*1.0, 0.07);
	
	// /geo1/MAT/rayMarchingBuilder1/add1
	vec3 v_POLY_add1_sum = (v_POLY_globals3_position + v_POLY_floatToVec3_1_vec3 + vec3(0.0, 0.0, 0.0));
	
	// /geo1/MAT/rayMarchingBuilder1/easing1
	float v_POLY_easing1_out = sineInOut(v_POLY_clamp1_val);
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd12
	float v_POLY_multAdd12_val = (1.0*(v_POLY_SDFBoxFrame1_float + -0.03)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/rotate2
	vec3 v_POLY_rotate2_val = rotateWithAxisAngle(v_POLY_add1_sum, v_POLY_normalize2_normalized, v_POLY_multAdd8_val);
	
	// /geo1/MAT/rayMarchingBuilder1/add3
	float v_POLY_add3_sum = (v_POLY_fitFrom01_2_val + v_POLY_multAdd12_val + 0.0);
	
	// /geo1/MAT/rayMarchingBuilder1/SDFOctahedron1
	float v_POLY_SDFOctahedron1_float = sdOctahedron(v_POLY_rotate2_val - vec3(0.0, 0.0, 0.0), 0.5);
	
	// /geo1/MAT/rayMarchingBuilder1/SDFSphere2
	float v_POLY_SDFSphere2_float = sdSphere(v_POLY_rotate2_val - vec3(0.1, 0.0, 0.0), 0.36);
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd13
	float v_POLY_multAdd13_val = (1.0*(v_POLY_SDFOctahedron1_float + -0.04)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/mix1
	float v_POLY_mix1_mix = mix(v_POLY_multAdd13_val, v_POLY_SDFSphere2_float, v_POLY_easing1_out);
	
	// /geo1/MAT/rayMarchingBuilder1/add2
	float v_POLY_add2_sum = (v_POLY_fitFrom01_1_val + v_POLY_mix1_mix + 0.0);
	
	// /geo1/MAT/rayMarchingBuilder1/SDFUnion1
	float v_POLY_SDFUnion1_union = SDFSmoothUnion(v_POLY_add3_sum, v_POLY_add2_sum, 0.3);
	
	// /geo1/MAT/rayMarchingBuilder1/SDFContext1
	SDFContext v_POLY_SDFContext1_SDFContext = SDFContext(v_POLY_SDFUnion1_union, 0, _GEO1_MAT_RAYMARCHINGBUILDER1_SDFMATERIAL1, _GEO1_MAT_RAYMARCHINGBUILDER1_SDFMATERIAL1, 0.);
	
	// /geo1/MAT/rayMarchingBuilder1/output1
	sdfContext = v_POLY_SDFContext1_SDFContext;



	

	return sdfContext;
}

SDFContext RayMarch(vec3 ro, vec3 rd, float side) {
	SDFContext dO = SDFContext(0.,0,0,0,0.);

	#pragma unroll_loop_start
	for(int i=0; i<MAX_STEPS; i++) {
		vec3 p = ro + rd*dO.d;
		SDFContext sdfContext = GetDist(p);
		dO.d += sdfContext.d * side;
		#if defined( DEBUG_STEPS_COUNT )
			dO.stepsCount += 1;
		#endif
		dO.matId = sdfContext.matId;
		dO.matId2 = sdfContext.matId2;
		dO.matBlend = sdfContext.matBlend;
		if(dO.d>MAX_DIST || abs(sdfContext.d)<SURF_DIST) break;
	}
	#pragma unroll_loop_end

	return dO;
}

vec3 GetNormal(vec3 p) {
	SDFContext sdfContext = GetDist(p);
	vec2 e = vec2(NORMALS_BIAS, 0);

	vec3 n = sdfContext.d - vec3(
		GetDist(p-e.xyy).d,
		GetDist(p-e.yxy).d,
		GetDist(p-e.yyx).d);

	return normalize(n);
}
// https://iquilezles.org/articles/rmshadows
float calcSoftshadow( in vec3 ro, in vec3 rd, float mint, float maxt, float k, inout SDFContext sdfContext )
{
	float res = 1.0;
	float ph = 1e20;
	for( float t=mint; t<maxt; )
	{
		float h = GetDist(ro + rd*t).d;
		#if defined( DEBUG_STEPS_COUNT )
			sdfContext.stepsCount += 1;
		#endif
		if( h<SURF_DIST )
			return 0.0;
		float y = h*h/(2.0*ph);
		float d = sqrt(h*h-y*y);
		res = min( res, k*d/max(0.0,t-y) );
		ph = h;
		t += h;
	}
	return res;
}

vec3 GetLight(vec3 _p, vec3 _n, inout SDFContext sdfContext) {
	vec3 dif = vec3(0.,0.,0.);
	GeometricContext geometry;
	// geometry.position = _p;
	// geometry.normal = _n;
	// geometry.viewDir = rayDir;

	// vec4 mvPosition = vec4( p, 1.0 );
	// mvPosition = modelViewMatrix * mvPosition;
	// vec3 vViewPosition = - mvPosition.xyz;
	vec3 pWorld = ( vModelMatrix * vec4( _p, 1.0 )).xyz;
	vec3 nWorld = transformDirection(_n, vModelMatrix);
	// geometry.position = (VViewMatrix * vec4( _p, 1.0 )).xyz;
	geometry.position = (VViewMatrix * vec4(pWorld, 1.0 )).xyz;
	// geometry.normal = transformDirection(_n, VViewMatrix);
	// geometry.normal = inverseTransformDirection(transformDirection(_n, VViewMatrix), vInverseModelMatrix);
	geometry.normal = transformDirection(nWorld, VViewMatrix);
	geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( cameraPosition - geometry.position );

	#if NUM_SPOT_LIGHTS > 0 || NUM_DIR_LIGHTS > 0 || NUM_HEMI_LIGHTS > 0 || NUM_POINT_LIGHTS > 0 || NUM_RECT_AREA_LIGHTS > 0

		IncidentLight directLight;
		ReflectedLight reflectedLight;
		vec3 lightPos, lightDir, worldLightDir, objectSpaceLightDir, lighDif, directDiffuse;
		float dotNL, lightDistance;
		#if NUM_SPOT_LIGHTS > 0
			SpotLightRayMarching spotLightRayMarching;
			SpotLight spotLight;
			float spotLightSdfShadow;
			#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0
				SpotLightShadow spotLightShadow;
			#endif
			#pragma unroll_loop_start
			for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {
				spotLightRayMarching = spotLightsRayMarching[ i ];
				spotLight = spotLights[ i ];
				getSpotLightInfo( spotLight, geometry, directLight );

				// #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
				// 	spotLightShadow = spotLightShadows[ i ];
				// 	vec4 spotLightShadowCoord = spotLightMatrix[ i ] * vec4(pWorld+SHADOW_BIAS*nWorld, 1.0);
				// 	directLight.color *= (directLight.visible && receiveShadow) ? getShadow(
				// 		spotShadowMap[ i ],
				// 		spotLightShadow.shadowMapSize,
				// 		spotLightShadow.shadowBias,
				// 		spotLightShadow.shadowRadius,
				// 		spotLightShadowCoord
				// 	) : 1.0;
				// #endif

				lightPos = spotLight.position;
				lightDir = normalize(lightPos-geometry.position);
				worldLightDir = inverseTransformDirection(lightDir, VViewMatrix);
				objectSpaceLightDir = inverseTransformDirection(worldLightDir, vModelMatrix);
				lightDistance = distance(geometry.position,lightPos);
				spotLightSdfShadow =
					dot( _n, objectSpaceLightDir ) < spotLightRayMarching.shadowBiasAngle
					? 1.
					: calcSoftshadow(
						_p,
						objectSpaceLightDir,
						spotLightRayMarching.shadowBiasDistance,
						distance(geometry.position,lightPos),
						1./max(spotLightRayMarching.penumbra*0.2,0.001),
						sdfContext
					);
				dotNL = saturate( dot( geometry.normal, directLight.direction ) );
				directDiffuse = dotNL * directLight.color * BRDF_Lambert( vec3(1.) );
				dif += directDiffuse * spotLightSdfShadow;
			}
			#pragma unroll_loop_end
		#endif
		#if NUM_DIR_LIGHTS > 0
			DirectionalLightRayMarching directionalLightRayMarching;
			DirectionalLight directionalLight;
			float dirLightSdfShadow;
			#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
				DirectionalLightShadow directionalLightShadow;
			#endif
			#pragma unroll_loop_start
			for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
				directionalLightRayMarching = directionalLightsRayMarching[ i ];
				directionalLight = directionalLights[ i ];
				
				getDirectionalLightInfo( directionalLight, geometry, directLight );

				// #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
				// 	directionalLightShadow = directionalLightShadows[ i ];
				// 	vec4 dirLightShadowCoord = directionalShadowMatrix[ i ] * vec4(pWorld+SHADOW_BIAS*nWorld, 1.0);
				// 	directLight.color *= (directLight.visible && receiveShadow) ? getShadow(
				// 		directionalShadowMap[ i ],
				// 		directionalLightShadow.shadowMapSize,
				// 		directionalLightShadow.shadowBias,
				// 		directionalLightShadow.shadowRadius,
				// 		dirLightShadowCoord
				// 	) : 1.0;
				// #endif

				lightDir = directionalLight.direction;
				worldLightDir = inverseTransformDirection(lightDir, VViewMatrix);
				objectSpaceLightDir = inverseTransformDirection(worldLightDir, vModelMatrix);
				dirLightSdfShadow =
					dot( _n, objectSpaceLightDir ) < directionalLightRayMarching.shadowBiasAngle
					? 1.
					:
					calcSoftshadow(
						_p,
						objectSpaceLightDir,
						directionalLightRayMarching.shadowBiasDistance,
						MAX_DIST,//distance(geometry.position,lightPos),
						1./max(directionalLightRayMarching.penumbra*0.2,0.001),
						sdfContext
					);
				dotNL = saturate( dot( geometry.normal, directLight.direction ) );
				// lighDif = directLight.color * dotNL * dirLightSdfShadow;
				directDiffuse = dotNL * directLight.color * BRDF_Lambert( vec3(1.) );
				dif += directDiffuse * dirLightSdfShadow;
			}
			#pragma unroll_loop_end
		#endif

		#if ( NUM_HEMI_LIGHTS > 0 )

			#pragma unroll_loop_start
			HemisphereLight hemiLight;
			for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
				hemiLight = hemisphereLights[ i ];
				dif += getHemisphereLightIrradiance( hemiLight, geometry.normal ) * BRDF_Lambert( vec3(1.) );

			}
			#pragma unroll_loop_end

		#endif

		#if NUM_POINT_LIGHTS > 0
			PointLightRayMarching pointLightRayMarching;
			PointLight pointLight;
			float pointLightSdfShadow;
			#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0
				PointLightShadow pointLightShadow;
			#endif
			#pragma unroll_loop_start
			for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {
				pointLightRayMarching = pointLightsRayMarching[ i ];
				pointLight = pointLights[ i ];
				getPointLightInfo( pointLight, geometry, directLight );


				#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
					pointLightShadow = pointLightShadows[ i ];
					vec4 pointLightShadowCoord = pointShadowMatrix[ i ] * vec4(pWorld+SHADOW_BIAS*nWorld, 1.0);
					directLight.color *= (directLight.visible && receiveShadow) ? getPointShadow(
						pointShadowMap[ i ],
						pointLightShadow.shadowMapSize,
						pointLightShadow.shadowBias,
						pointLightShadow.shadowRadius,
						pointLightShadowCoord,
						pointLightShadow.shadowCameraNear,
						pointLightShadow.shadowCameraFar
					) : 1.0;
				#endif

				lightPos = pointLight.position;
				lightDir = normalize(lightPos-geometry.position);
				worldLightDir = inverseTransformDirection(lightDir, VViewMatrix);
				objectSpaceLightDir = inverseTransformDirection(worldLightDir, vModelMatrix);
				pointLightSdfShadow =
					dot( _n, objectSpaceLightDir ) < pointLightRayMarching.shadowBiasAngle
					? 1.
					:
					calcSoftshadow(
					_p,
					objectSpaceLightDir,
					pointLightRayMarching.shadowBiasDistance,
					distance(geometry.position,lightPos),
					1./max(pointLightRayMarching.penumbra*0.2,0.001),
					sdfContext
				);
				dotNL = saturate( dot( geometry.normal, directLight.direction ) );
				directDiffuse = dotNL * directLight.color * BRDF_Lambert( vec3(1.) );
				dif += directDiffuse * pointLightSdfShadow;
			}
			#pragma unroll_loop_end
		#endif

		#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )

			RectAreaLight rectAreaLight;
			// AreaLightRayMarching areaLightRayMarching;
			PhysicalMaterial material;
			material.roughness = 1.;
			material.specularColor = vec3(1.);
			material.diffuseColor = vec3(1.);

			#pragma unroll_loop_start
			for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {
				// areaLightRayMarching = areaLightsRayMarching[ i ];
				rectAreaLight = rectAreaLights[ i ];
				// rectAreaLight.position = areaLightRayMarching.worldPos;

				RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );
			}
			#pragma unroll_loop_end
			dif += reflectedLight.directDiffuse;

		#endif
	#endif

	vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );

	irradiance += getLightProbeIrradiance( lightProbe, geometry.normal );
	dif += irradiance;
	return dif;
}




vec3 applyMaterialWithoutRefraction(vec3 p, vec3 n, vec3 rayDir, int mat, inout SDFContext sdfContext){

	vec3 col = vec3(1.);
	// start applyMaterial builder body code



	// /geo1/MAT/rayMarchingBuilder1/constant1
	vec3 v_POLY_constant1_val = vec3(1.0, 1.0, 1.0);
	
	// /geo1/MAT/rayMarchingBuilder1/globals5
	float v_POLY_globals5_time = time;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd10
	float v_POLY_multAdd10_val = (0.12*(v_POLY_globals5_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/floatToVec3_4
	vec3 v_POLY_floatToVec3_4_vec3 = vec3(v_POLY_multAdd10_val, 0.92, 0.92);
	
	// /geo1/MAT/rayMarchingBuilder1/hsvToRgb1
	vec3 v_POLY_hsvToRgb1_rgb = hsv2rgb(v_POLY_floatToVec3_4_vec3);
	
	// /geo1/MAT/rayMarchingBuilder1/SDFMaterial1
	if(mat == _GEO1_MAT_RAYMARCHINGBUILDER1_SDFMATERIAL1){
		col = vec3(0., 0., 0.);
		col += vec3(0.0, 0.0, 0.0);
		vec3 rayDir = normalize(reflect(rayDir, n));
		EnvMapProps envMapProps;
		envMapProps.tint = vec3(1.0, 1.0, 1.0);
		envMapProps.intensity = 0.07;
		envMapProps.roughness = 1.0;
		envMapProps.fresnel = 0.0;
		envMapProps.fresnelPower = 5.0;
		col += envMapSampleWithFresnel(rayDir, envMapProps, n, cameraPosition);
	
	;
	}



	
	return col;
}

vec3 applyMaterialWithoutReflection(vec3 p, vec3 n, vec3 rayDir, int mat, inout SDFContext sdfContext){

	vec3 col = vec3(1.);
	// start applyMaterial builder body code



	// /geo1/MAT/rayMarchingBuilder1/constant1
	vec3 v_POLY_constant1_val = vec3(1.0, 1.0, 1.0);
	
	// /geo1/MAT/rayMarchingBuilder1/globals5
	float v_POLY_globals5_time = time;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd10
	float v_POLY_multAdd10_val = (0.12*(v_POLY_globals5_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/floatToVec3_4
	vec3 v_POLY_floatToVec3_4_vec3 = vec3(v_POLY_multAdd10_val, 0.92, 0.92);
	
	// /geo1/MAT/rayMarchingBuilder1/hsvToRgb1
	vec3 v_POLY_hsvToRgb1_rgb = hsv2rgb(v_POLY_floatToVec3_4_vec3);
	
	// /geo1/MAT/rayMarchingBuilder1/SDFMaterial1
	if(mat == _GEO1_MAT_RAYMARCHINGBUILDER1_SDFMATERIAL1){
		col = vec3(0., 0., 0.);
		col += vec3(0.0, 0.0, 0.0);
		vec3 rayDir = normalize(reflect(rayDir, n));
		EnvMapProps envMapProps;
		envMapProps.tint = vec3(1.0, 1.0, 1.0);
		envMapProps.intensity = 0.07;
		envMapProps.roughness = 1.0;
		envMapProps.fresnel = 0.0;
		envMapProps.fresnelPower = 5.0;
		col += envMapSampleWithFresnel(rayDir, envMapProps, n, cameraPosition);
	
	;
	}



	
	return col;
}
#ifdef RAYMARCHED_REFLECTIONS
vec3 GetReflection(vec3 p, vec3 n, vec3 rayDir, float biasMult, float roughness, int reflectionDepth, inout SDFContext sdfContextMain){
	bool hitReflection = true;
	vec3 reflectedColor = vec3(0.);
	#pragma unroll_loop_start
	for(int i=0; i < reflectionDepth; i++) {
		if(hitReflection){
			rayDir = reflect(rayDir, n);
			p += n*SURF_DIST*biasMult;
			SDFContext sdfContext = RayMarch(p, rayDir, 1.);
			#if defined( DEBUG_STEPS_COUNT )
				sdfContextMain.stepsCount += sdfContext.stepsCount;
			#endif
			if( sdfContext.d >= MAX_DIST){
				hitReflection = false;
				reflectedColor = envMapSample(rayDir, roughness);
			}
			if(hitReflection){
				p += rayDir * sdfContext.d;
				n = GetNormal(p);
				vec3 matCol = applyMaterialWithoutReflection(p, n, rayDir, sdfContext.matId, sdfContextMain);
				reflectedColor += matCol;
			}
		}
	}
	#pragma unroll_loop_end
	return reflectedColor;
}
#endif

#ifdef RAYMARCHED_REFRACTIONS
// xyz for color, w for distanceInsideMedium
vec4 GetRefractedData(vec3 p, vec3 n, vec3 rayDir, float ior, float biasMult, float roughness, float refractionMaxDist, int refractionDepth, inout SDFContext sdfContextMain){
	bool hitRefraction = true;
	bool changeSide = true;
	#ifdef RAYMARCHED_REFRACTIONS_START_OUTSIDE_MEDIUM
	float side = -1.;
	#else
	float side =  1.;
	#endif
	float iorInverted = 1. / ior;
	vec3 refractedColor = vec3(0.);
	float distanceInsideMedium=0.;
	float totalRefractedDistance=0.;

	#pragma unroll_loop_start
	for(int i=0; i < refractionDepth; i++) {
		if(hitRefraction){
			float currentIor = side<0. ? iorInverted : ior;
			vec3 rayDirPreRefract = rayDir;
			rayDir = refract(rayDir, n, currentIor);
			changeSide = dot(rayDir, rayDir)!=0.;
			if(changeSide == true) {
				p -= n*SURF_DIST*(2.+biasMult);
			} else {
				p += n*SURF_DIST*(   biasMult);
				rayDir = reflect(rayDirPreRefract, n);
			}
			SDFContext sdfContext = RayMarch(p, rayDir, side);
			#if defined( DEBUG_STEPS_COUNT )
				sdfContextMain.stepsCount += sdfContext.stepsCount;
			#endif
			totalRefractedDistance += sdfContext.d;
			if( abs(sdfContext.d) >= MAX_DIST || totalRefractedDistance > refractionMaxDist ){
				hitRefraction = false;
				refractedColor = envMapSample(rayDir, roughness);
			}
			if(hitRefraction){
				p += rayDir * sdfContext.d;
				n = GetNormal(p) * side;
				vec3 matCol = applyMaterialWithoutRefraction(p, n, rayDir, sdfContext.matId, sdfContextMain);
				refractedColor = matCol;

				// same as: side < 0. ? abs(sdfContext.d) : 0.;
				distanceInsideMedium += (side-1.)*-0.5*abs(sdfContext.d);
				if( changeSide ){
					side *= -1.;
				}
			}
		}
		#ifdef RAYMARCHED_REFRACTIONS_SAMPLE_ENV_MAP_ON_LAST
		if(i == refractionDepth-1){
			refractedColor = envMapSample(rayDir, roughness);
		}
		#endif
	}
	#pragma unroll_loop_end
	return vec4(refractedColor, distanceInsideMedium);
}
float refractionTint(float baseValue, float tint, float distanceInsideMedium, float absorption){
	float tintNegated = baseValue-tint;
	float t = tintNegated*( distanceInsideMedium*absorption );
	return max(baseValue-t, 0.);
}
float applyRefractionAbsorption(float refractedDataColor, float baseValue, float tint, float distanceInsideMedium, float absorption){
	return refractedDataColor*refractionTint(baseValue, tint, distanceInsideMedium, absorption);
}
vec3 applyRefractionAbsorption(vec3 refractedDataColor, vec3 baseValue, vec3 tint, float distanceInsideMedium, float absorption){
	return vec3(
		refractedDataColor.r * refractionTint(baseValue.r, tint.r, distanceInsideMedium, absorption),
		refractedDataColor.g * refractionTint(baseValue.g, tint.g, distanceInsideMedium, absorption),
		refractedDataColor.b * refractionTint(baseValue.b, tint.b, distanceInsideMedium, absorption)
	);
}

#endif

vec3 applyMaterial(vec3 p, vec3 n, vec3 rayDir, int mat, inout SDFContext sdfContext){

	vec3 col = vec3(0.);
	// start applyMaterial builder body code



	// /geo1/MAT/rayMarchingBuilder1/constant1
	vec3 v_POLY_constant1_val = vec3(1.0, 1.0, 1.0);
	
	// /geo1/MAT/rayMarchingBuilder1/globals5
	float v_POLY_globals5_time = time;
	
	// /geo1/MAT/rayMarchingBuilder1/multAdd10
	float v_POLY_multAdd10_val = (0.12*(v_POLY_globals5_time + 0.0)) + 0.0;
	
	// /geo1/MAT/rayMarchingBuilder1/floatToVec3_4
	vec3 v_POLY_floatToVec3_4_vec3 = vec3(v_POLY_multAdd10_val, 0.92, 0.92);
	
	// /geo1/MAT/rayMarchingBuilder1/hsvToRgb1
	vec3 v_POLY_hsvToRgb1_rgb = hsv2rgb(v_POLY_floatToVec3_4_vec3);
	
	// /geo1/MAT/rayMarchingBuilder1/SDFMaterial1
	if(mat == _GEO1_MAT_RAYMARCHINGBUILDER1_SDFMATERIAL1){
		col = vec3(0., 0., 0.);
		col += vec3(0.0, 0.0, 0.0);
		vec3 rayDir = normalize(reflect(rayDir, n));
		EnvMapProps envMapProps;
		envMapProps.tint = vec3(1.0, 1.0, 1.0);
		envMapProps.intensity = 0.07;
		envMapProps.roughness = 1.0;
		envMapProps.fresnel = 0.0;
		envMapProps.fresnelPower = 5.0;
		col += envMapSampleWithFresnel(rayDir, envMapProps, n, cameraPosition);
	
		vec3 refractedColor = vec3(0.);
		float ior = 1.45;
		float biasMult = 2.0;
		vec3 baseValue = v_POLY_constant1_val;
		vec3 tint = v_POLY_hsvToRgb1_rgb;
		float absorption = 1.7;
			
	
		vec4 refractedData = GetRefractedData(p, n, rayDir, ior, biasMult, 1.0, 2.3639, 3, sdfContext);
		refractedColor = applyRefractionAbsorption(refractedData.rgb, baseValue, tint, refractedData.w, absorption);
				;
	
		col += refractedColor * 1.0;
	;
	}



	
	return col;
}




vec4 applyShading(vec3 rayOrigin, vec3 rayDir, inout SDFContext sdfContext){
	vec3 p = rayOrigin + rayDir * sdfContext.d;
	vec3 n = GetNormal(p);
	
	vec3 col = applyMaterial(p, n, rayDir, sdfContext.matId, sdfContext);
	if(sdfContext.matBlend > 0.) {
		// blend material colors if needed
		vec3 col2 = applyMaterial(p, n, rayDir, sdfContext.matId2, sdfContext);
		col = (1. - sdfContext.matBlend)*col + sdfContext.matBlend*col2;
	}
		
	// gamma
	//col = pow( col, vec3(0.4545) ); // this gamma leads to a different look than standard materials
	return vec4(col, 1.);
}

void main()	{

	vec3 rayDir = normalize(vPw - cameraPosition);
	rayDir = transformDirection(rayDir, vInverseModelMatrix);
	vec3 rayOrigin = (vInverseModelMatrix * vec4( cameraPosition, 1.0 )).xyz;

	SDFContext sdfContext = RayMarch(rayOrigin, rayDir, 1.);

	#if defined( DEBUG_DEPTH )
		float normalizedDepth = 1.-(sdfContext.d - debugMinDepth ) / ( debugMaxDepth - debugMinDepth );
		normalizedDepth = saturate(normalizedDepth); // clamp to [0,1]
		gl_FragColor = vec4(normalizedDepth);
		return;
	#endif
	#if defined( SHADOW_DEPTH )
		float normalizedDepth = 1.-(sdfContext.d - debugMinDepth ) / ( debugMaxDepth - debugMinDepth );
		// float fragCoordZ = sdfContext.d / vHighPrecisionZW[1];
		float compoundedDepth = 0.5 * (normalizedDepth) + 0.5;
		float alpha = sdfContext.d < MAX_DIST ? 0.:1.;
		gl_FragColor = vec4( vec3(compoundedDepth), alpha );
		// normalizedDepth = 0.5*normalizedDepth+0.5;
		// gl_FragColor = packDepthToRGBA( normalizedDepth );
		// gl_FragColor = vec4(0.);
		return;
	#endif
	#if defined( SHADOW_DISTANCE )
		float normalizedDepth = (sdfContext.d - shadowDistanceMin ) / ( shadowDistanceMax - shadowDistanceMin );
		normalizedDepth = saturate(normalizedDepth); // clamp to [0,1]
		gl_FragColor = packDepthToRGBA( normalizedDepth );
		return;
	#endif

	if( sdfContext.d < MAX_DIST ){
		gl_FragColor = applyShading(rayOrigin, rayDir, sdfContext);
		#if defined( TONE_MAPPING )
			gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );
		#endif
		gl_FragColor = linearToOutputTexel( gl_FragColor );

		#ifdef USE_FOG
			float vFogDepth = sdfContext.d;
			#ifdef FOG_EXP2
				float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );
			#else
				float fogFactor = smoothstep( fogNear, fogFar, vFogDepth );
			#endif
			gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );
		#endif
		#include <premultiplied_alpha_fragment>
		#include <dithering_fragment>
	} else {
		gl_FragColor = vec4(0.);
	}

	#if defined( DEBUG_STEPS_COUNT )
		float normalizedStepsCount = (float(sdfContext.stepsCount) - debugMinSteps ) / ( debugMaxSteps - debugMinSteps );
		gl_FragColor = vec4(normalizedStepsCount, 1.-normalizedStepsCount, 0., 1.);
		return;
	#endif
	
}