#ifndef USX_GEOM_INCLUDED
#define USX_GEOM_INCLUDED

#if X_FEATURES

#if CLONES_ENABLED
[instance(9)]
[maxvertexcount(3)]
void geom(triangle v2g i[3], inout TriangleStream<g2f> tristream, uint instanceID : SV_GSInstanceID, uint primID : SV_PrimitiveID){

	if (_Hide == 1)
		return;

	#if SHADOW_PASS
		if (_Screenspace == 1)
			return;
	#endif

	float4 mainTexSampler = MOCHIE_SAMPLE_TEX2D_LOD(_MainTex, i[0].rawUV.xy, 0);
	i[0].localPos = lerp(i[0].localPos, mainTexSampler, _NaNLmao);

	g2f o = (g2f)0;

	DEFAULT_UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i[0]);

	float4 offset = GetCloneCoords(instanceID);
	_Visibility = saturate(_Visibility * offset.w);
	uint check = step(0.00001, _Visibility*any(offset.xyz))*instanceID;
	
	if (instanceID != check)
		return;

	float3 edgeA = i[1].worldPos - i[2].worldPos;
	float3 edgeB = i[2].worldPos - i[0].worldPos;
	float3 edgeC = i[0].worldPos - i[1].worldPos;
	float3 edges = float3(length(edgeC), length(edgeA), length(edgeB));
	float3 normalDir = normalize(cross(edgeA, edgeB));

	float3 bCoords[3];
	GetBarycentricCoords(bCoords, edges);

	float wfStr = lerp(0, _WFVisibility, _WireframeToggle);
	float wfOpac = lerp(0, _WFFill, _WireframeToggle);
	float wfStrAL = 0;
	float wfOpacAL = 0;

	float shatterAmt = 0;
	float3 finalTriOffsetValue = 0;
	float3 offsetPos = 0;
	float3 glitchOffset = 0;
	float3 instabilityOffset = 0;

	[unroll(3)]
	for (uint j = 0; j < 3; j++){

		float3 wPos = i[j].worldPos;
		wPos += offset.xyz;
		if (instanceID != 0){
			float3 nDir = lerp(normalDir, abs(normalDir), _SaturateEP);
			wPos += (((1/_Visibility)-1) * nDir) * _EntryPos;
		}

		if (_ShatterToggle == 1){
			float dist = distance(wPos, i[j].cameraPos);
			float objDist = distance(i[j].objPos, i[j].cameraPos);
			shatterAmt = smoothstep(_ShatterMax, _ShatterMin, dist)*_ShatterSpread;
			if (_ShatterClones == 1){
				if (instanceID != 0){
					if (dist < _ShatterCull)
						return;
					#if OUTLINE_PASS
						if (shatterAmt > 0)
							return;
					#endif

					wPos += shatterAmt*normalDir;
				}
			}
			else {
				if (dist < _ShatterCull)
					return;
				#if OUTLINE_PASS
					if (shatterAmt > 0)
						return;
				#endif

				wPos += shatterAmt*normalDir;	
			}
		}

		#if AUDIOLINK_ENABLED
			audioLinkData al = (audioLinkData)0;

			if (_AudioLinkTriOffsetStrength > 0){
				float triPulseZeroedPos = 1;
				float triPulseZeroedNeg = 1;
				float2 offsetMaskUV = TRANSFORM_TEX(i[j].rawUV, _AudioLinkTriOffsetMask);
				offsetMaskUV += _Time.y * _AudioLinkTriOffsetMaskScroll;
				float offsetMask = MOCHIE_SAMPLE_TEX2D_LOD(_AudioLinkTriOffsetMask, offsetMaskUV,0);
				
				float triOffsetCoords = i[j].localPos[_AudioLinkTriOffsetCoords];
				float triPulseTime = smoothstep(_AudioLinkTriOffsetStartPos, _AudioLinkTriOffsetEndPos, triOffsetCoords);
				float triAudioLinkTime = lerp(triPulseTime, 0, _AudioLinkTriOffsetMode);
				InitializeAudioLink(al, triAudioLinkTime);
				float triOffsetAmp = GetAudioLinkBand(al, _AudioLinkTriOffsetBand, _AudioLinkRemapTriOffsetMin, _AudioLinkRemapTriOffsetMax);
				
				if (_AudioLinkTriOffsetMode == 0){
					triPulseZeroedPos = smoothstep(1, 0.9999, triPulseTime);
					triPulseZeroedNeg = smoothstep(0.0001, 0.00011, triPulseTime);
				}

				finalTriOffsetValue = (
					normalDir * offsetMask * al.textureExists * 
					smoothstep(0.05, 0.1, triOffsetAmp) *
					_AudioLinkTriOffsetStrength * 0.1 *_AudioLinkStrength *
					triPulseZeroedPos * triPulseZeroedNeg
				);

				if (_AudioLinkTriOffsetMode == 1){
					float triOffsetLevel = Remap(triOffsetAmp, 0, 1, _AudioLinkTriOffsetStartPos, _AudioLinkTriOffsetEndPos);
					float triOffsetWidth = _AudioLinkTriOffsetSize * 0.5;
					float triOffsetPositive = triOffsetLevel + triOffsetWidth;
					float triOffsetNegative = triOffsetLevel - triOffsetWidth;

					if (triOffsetCoords > triOffsetPositive || triOffsetCoords < triOffsetNegative)
						finalTriOffsetValue = 0;
				}
				wPos += finalTriOffsetValue;
			}

			if (_AudioLinkWireframeStrength > 0){
				float wfPulseZeroedPos = 1;
				float wfPulseZeroedNeg = 1;
				float2 wfMaskUV = TRANSFORM_TEX(i[j].rawUV, _AudioLinkWireframeMask);
				wfMaskUV += _Time.y * _AudioLinkWireframeMaskScroll;
				float wfMask = MOCHIE_SAMPLE_TEX2D_LOD(_AudioLinkWireframeMask, wfMaskUV,0);
				
				float wfCoords = i[j].localPos[_AudioLinkWireframeCoords];
				float wfPulseTime = smoothstep(_AudioLinkWireframeStartPos, _AudioLinkWireframeEndPos, wfCoords);
				float wfAudioLinkTime = lerp(wfPulseTime, 0, _AudioLinkWireframeMode);
				InitializeAudioLink(al, wfAudioLinkTime);
				float wfAmp = GetAudioLinkBand(al, _AudioLinkWireframeBand, _AudioLinkRemapWireframeMin, _AudioLinkRemapWireframeMax);
				
				if (_AudioLinkWireframeMode == 0){
					wfPulseZeroedPos = smoothstep(1, 0.9999, wfPulseTime);
					wfPulseZeroedNeg = smoothstep(0.0001, 0.00011, wfPulseTime);
				}

				float3 finalWireframeValue = (
					wfMask * al.textureExists * 
					smoothstep(0.05, 0.1, wfAmp) *
					_AudioLinkWireframeStrength * 0.1 *_AudioLinkStrength *
					wfPulseZeroedPos * wfPulseZeroedNeg
				);

				if (_AudioLinkWireframeMode == 1){
					float wfLevel = Remap(wfAmp, 0, 1, _AudioLinkWireframeStartPos, _AudioLinkWireframeEndPos);
					float wfWidth = _AudioLinkWireframeSize * 0.5;
					float wfPositive = wfLevel + wfWidth;
					float wfNegative = wfLevel - wfWidth;

					if (wfCoords > wfPositive || wfCoords < wfNegative)
						finalWireframeValue = 0;
				}
				wfStrAL = smoothstep(0, 0.15, finalWireframeValue);
				wfOpacAL = wfStrAL * 0.25;
			}
		#endif

		#if DISSOLVE_GEOMETRY
			float dissolveValueAL = 1;
			#if AUDIOLINK_ENABLED
				InitializeAudioLink(al, 0);
				dissolveValueAL = GetAudioLinkBand(al, _AudioLinkDissolveBand, _AudioLinkRemapDissolveMin, _AudioLinkRemapDissolveMax);
			#endif
			float scanLine = 0;
			float axisPos = 0;
			if (_GeomDissolveAxis > 2){
				float3 direction = normalize(_DissolvePoint1 - _DissolvePoint0);
				scanLine = dot(direction, i[j].localPos) + _GeomDissolveAmount;
			}
			else {
				axisPos = i[j].localPos[_GeomDissolveAxis];
				axisPos = lerp(axisPos, 1-axisPos, _GeomDissolveAxisFlip);
				scanLine = lerp(_GeomDissolveAmount, 1-_GeomDissolveAmount, _GeomDissolveAxisFlip);
			}
			float scanLineOffset = _GeomDissolveWidth * 0.5;
			float boundLower = scanLine - scanLineOffset;
			float boundUpper = scanLine + scanLineOffset;
			float interp = smootherstep(boundLower, boundUpper, axisPos);
			#if AUDIOLINK_ENABLED
				interp *= lerp(1, dissolveValueAL, _AudioLinkDissolveMultiplier*_AudioLinkStrength);
			#endif
			float wfInterp = smootherstep(boundLower - _GeomDissolveWidth*1.25, boundUpper, axisPos);
			float opacInterp = smootherstep(boundLower - scanLine*1.5, boundUpper, axisPos);
			wfStr = saturate(wfStr * lerp(1, wfInterp, _GeomDissolveWireframe));
			wfOpac = saturate(wfOpac * lerp(1, opacInterp, _GeomDissolveWireframe));

			if (_DissolveClones == 1){
				if (instanceID != 0){
					#if OUTLINE_PASS
						if (interp > 0)
							return;
					#endif

					wPos += lerp(0, lerp(normalDir, abs(normalDir), _GeomDissolveClamp) * _GeomDissolveSpread, interp);

					if (axisPos > boundUpper+_GeomDissolveClip)
						return;
				}
			}
			else {
				#if OUTLINE_PASS
					if (interp > 0)
						return;
				#endif

				float3 offsetDir = lerp(normalDir, abs(normalDir), _GeomDissolveClamp);
				offsetPos = lerp(0, offsetDir * _GeomDissolveSpread, interp);
				wPos += offsetPos;
				
				if (any(offsetPos) && (primID % _GeomDissolveFilter != 0))
					return;

				if (axisPos > boundUpper+_GeomDissolveClip)
					return;
			}
		#endif
		
		if (_GlitchToggle == 1){
			float noise = GetNoise(wPos.xy*frac(_Time.y));
			if (_GlitchClones == 1){
				if (instanceID != 0){
					glitchOffset = ((noise*_GlitchIntensity) * normalDir) * (noise > 0.99999-_GlitchFrequency);
					instabilityOffset = (noise*_Instability) * normalDir;
					wPos.xyz += glitchOffset;
					wPos.xyz += instabilityOffset;
				}
			}
			else {
				glitchOffset = ((noise*_GlitchIntensity) * normalDir) * (noise > 0.99999-_GlitchFrequency);
				instabilityOffset = (noise*_Instability) * normalDir;
				wPos.xyz += glitchOffset;
				wPos.xyz += instabilityOffset;
			}
		}

		if (instanceID == 0 && shatterAmt == 0 && !any(instabilityOffset) && !any(glitchOffset) && !any(offsetPos) && !any(finalTriOffsetValue)){
			o.pos = i[j].pos;
		}
		else {
			o.pos = UnityWorldToClipPos(wPos);
		}
		o.rawUV = i[j].rawUV;
		o.uv = i[j].uv;
		o.uv1 = i[j].uv1;
		o.uv2 = i[j].uv2;
		o.uv3 = i[j].uv3;
		o.worldPos = float4(wPos, i[j].worldPos.w);
		o.binormal = i[j].binormal;
		o.tangentViewDir = i[j].tangentViewDir;
		o.cameraPos = i[j].cameraPos;
		o.objPos = i[j].objPos;
		o.bCoords = bCoords[j];
		o.WFStr = wfStr;
		o.wfOpac = wfOpac;
		o.instID = instanceID;
		o.screenPos = i[j].screenPos;
		o.isReflection = i[j].isReflection;
		o.localPos = i[j].localPos;
		o.wfStrAL = wfStrAL;
		o.wfOpacAL = wfOpacAL;

		o.tangent = i[j].tangent;
		o.normal = i[j].normal;
		
        #if defined(SHADOWS_SCREEN) || (defined(SHADOWS_DEPTH) && defined(SPOT)) || defined(SHADOWS_CUBE)
			UNITY_TRANSFER_SHADOW(o, i[j].pos);
        #endif
		UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
		UNITY_TRANSFER_FOG(o, o.pos);
		tristream.Append(o);
	}   
	tristream.RestartStrip();
}

#else // CLONES_ENABLED

[maxvertexcount(3)]
void geom(triangle v2g i[3], inout TriangleStream<g2f> tristream, uint instanceID : SV_GSInstanceID, uint primID : SV_PrimitiveID){
	
	if (_Hide == 1)
		return;

	#if SHADOW_PASS
		if (_Screenspace == 1)
			return;
	#endif

	g2f o = (g2f)0;

	DEFAULT_UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i[0]);

	float4 mainTexSampler = MOCHIE_SAMPLE_TEX2D_LOD(_MainTex, i[0].rawUV.xy,0);
	i[0].localPos = lerp(i[0].localPos, mainTexSampler, _NaNLmao);

	float3 edgeA = i[1].worldPos - i[2].worldPos;
	float3 edgeB = i[2].worldPos - i[0].worldPos;
	float3 edgeC = i[0].worldPos - i[1].worldPos;
	float3 edges = float3(length(edgeC), length(edgeA), length(edgeB));
	float3 normalDir = normalize(cross(edgeA, edgeB));

	float3 bCoords[3];
	GetBarycentricCoords(bCoords, edges);

	float wfStr = lerp(0, _WFVisibility, _WireframeToggle);
	float wfOpac = lerp(0, _WFFill, _WireframeToggle);
	float wfStrAL = 0;
	float wfOpacAL = 0;

	float shatterAmt = 0;
	float3 finalTriOffsetValue = 0;
	float3 offsetPos = 0;
	float3 glitchOffset = 0;
	float3 instabilityOffset = 0;

	[unroll(3)]
	for (uint j = 0; j < 3; j++){

		float3 wPos = i[j].worldPos;

		if (_ShatterToggle == 1){
			float dist = distance(wPos, i[j].cameraPos);
			float objDist = distance(i[j].objPos, i[j].cameraPos);
			if (dist < _ShatterCull)
				return;
			shatterAmt = smoothstep(_ShatterMax, _ShatterMin, dist)*_ShatterSpread;

			#if OUTLINE_PASS
				if (shatterAmt > 0)
					return;
			#endif

			wPos += shatterAmt*normalDir;
		}

		
		#if AUDIOLINK_ENABLED
			audioLinkData al = (audioLinkData)0;

			if (_AudioLinkTriOffsetStrength > 0){
				float triPulseZeroedPos = 1;
				float triPulseZeroedNeg = 1;
				float2 offsetMaskUV = TRANSFORM_TEX(i[j].rawUV, _AudioLinkTriOffsetMask);
				offsetMaskUV += _Time.y * _AudioLinkTriOffsetMaskScroll;
				float offsetMask = MOCHIE_SAMPLE_TEX2D_LOD(_AudioLinkTriOffsetMask, offsetMaskUV,0);
				
				float triOffsetCoords = i[j].localPos[_AudioLinkTriOffsetCoords];

				float triPulseTime = smoothstep(_AudioLinkTriOffsetStartPos, _AudioLinkTriOffsetEndPos, triOffsetCoords);
				float triAudioLinkTime = lerp(triPulseTime, 0, _AudioLinkTriOffsetMode);
				InitializeAudioLink(al, triAudioLinkTime);
				float triOffsetAmp = GetAudioLinkBand(al, _AudioLinkTriOffsetBand, _AudioLinkRemapTriOffsetMin, _AudioLinkRemapTriOffsetMax);

				if (_AudioLinkTriOffsetMode == 0){
					triPulseZeroedPos = smoothstep(1, 0.9999, triPulseTime);
					triPulseZeroedNeg = smoothstep(0.0001, 0.00011, triPulseTime);
				}

				finalTriOffsetValue = (
					normalDir * offsetMask * al.textureExists * 
					smoothstep(0.05, 0.1, triOffsetAmp) *
					_AudioLinkTriOffsetStrength * 0.1 *_AudioLinkStrength *
					triPulseZeroedPos * triPulseZeroedNeg
				);

				if (_AudioLinkTriOffsetMode == 1){
					float triOffsetLevel = Remap(triOffsetAmp, 0, 1, _AudioLinkTriOffsetStartPos, _AudioLinkTriOffsetEndPos);
					float triOffsetWidth = _AudioLinkTriOffsetSize * 0.5;
					float triOffsetPositive = triOffsetLevel + triOffsetWidth;
					float triOffsetNegative = triOffsetLevel - triOffsetWidth;

					if (triOffsetCoords > triOffsetPositive || triOffsetCoords < triOffsetNegative)
						finalTriOffsetValue = 0;
				}
				wPos += finalTriOffsetValue;
			}

			if (_AudioLinkWireframeStrength > 0){
				float wfPulseZeroedPos = 1;
				float wfPulseZeroedNeg = 1;
				float2 wfMaskUV = TRANSFORM_TEX(i[j].rawUV, _AudioLinkWireframeMask);
				wfMaskUV += _Time.y * _AudioLinkWireframeMaskScroll;
				float wfMask = MOCHIE_SAMPLE_TEX2D_LOD(_AudioLinkWireframeMask, wfMaskUV,0);
				
				float wfCoords = i[j].localPos[_AudioLinkWireframeCoords];
				float wfPulseTime = smoothstep(_AudioLinkWireframeStartPos, _AudioLinkWireframeEndPos, wfCoords);
				float wfAudioLinkTime = lerp(wfPulseTime, 0, _AudioLinkWireframeMode);
				InitializeAudioLink(al, wfAudioLinkTime);
				float wfAmp = GetAudioLinkBand(al, _AudioLinkWireframeBand, _AudioLinkRemapWireframeMin, _AudioLinkRemapWireframeMax);

				if (_AudioLinkWireframeMode == 0){
					wfPulseZeroedPos = smoothstep(1, 0.9999, wfPulseTime);
					wfPulseZeroedNeg = smoothstep(0.0001, 0.00011, wfPulseTime);
				}

				float3 finalWireframeValue = (
					wfMask * al.textureExists * 
					smoothstep(0.05, 0.1, wfAmp) *
					_AudioLinkWireframeStrength * 0.1 *_AudioLinkStrength *
					wfPulseZeroedPos * wfPulseZeroedNeg
				);

				if (_AudioLinkWireframeMode == 1){
					float wfLevel = Remap(wfAmp, 0, 1, _AudioLinkWireframeStartPos, _AudioLinkWireframeEndPos);
					float wfWidth = _AudioLinkWireframeSize * 0.5;
					float wfPositive = wfLevel + wfWidth;
					float wfNegative = wfLevel - wfWidth;
					float wfInterp = smoothstep(wfNegative, wfPositive, wfLevel);

					
					if (wfCoords > wfPositive || wfCoords < wfNegative)
						finalWireframeValue = 0;
				}
				wfStrAL = smoothstep(0, 0.1, finalWireframeValue);
				wfOpacAL = wfStrAL * 0.25;
			}
		#endif

		#if DISSOLVE_GEOMETRY
			float dissolveValueAL = 1;
			#if AUDIOLINK_ENABLED
				InitializeAudioLink(al, 0);
				dissolveValueAL = GetAudioLinkBand(al, _AudioLinkDissolveBand, _AudioLinkRemapDissolveMin, _AudioLinkRemapDissolveMax);
			#endif
			float scanLine = 0;
			float axisPos = 0;
			if (_GeomDissolveAxis > 2){
				float3 direction = normalize(_DissolvePoint1 - _DissolvePoint0);
				scanLine = dot(direction, i[j].localPos) + _GeomDissolveAmount;
			}
			else {
				axisPos = i[j].localPos[_GeomDissolveAxis];
				axisPos = lerp(axisPos, 1-axisPos, _GeomDissolveAxisFlip);
				scanLine = lerp(_GeomDissolveAmount, 1-_GeomDissolveAmount, _GeomDissolveAxisFlip);
			}
			float scanLineOffset = _GeomDissolveWidth * 0.5;
			float boundLower = scanLine - scanLineOffset;
			float boundUpper = scanLine + scanLineOffset;
			float interp = smootherstep(boundLower, boundUpper, axisPos);
			#if AUDIOLINK_ENABLED
				interp *= lerp(1, dissolveValueAL, _AudioLinkDissolveMultiplier*_AudioLinkStrength);
			#endif
			#if OUTLINE_PASS
				if (interp > 0)
					return;
			#endif

			float3 offsetDir = lerp(normalDir, abs(normalDir), _GeomDissolveClamp);
			offsetPos = lerp(0, offsetDir * _GeomDissolveSpread, interp);
			wPos += offsetPos;
			
			if (any(offsetPos) && (primID % _GeomDissolveFilter != 0))
				return;

			if (axisPos > boundUpper + _GeomDissolveClip)
				return;

			float wfInterp = smootherstep(boundLower - _GeomDissolveWidth*1.25, boundUpper, axisPos);
			float opacInterp = smootherstep(boundLower - scanLine*1.25, boundUpper, axisPos);
			wfStr = saturate(wfStr * lerp(1, wfInterp, _GeomDissolveWireframe));
			wfOpac = saturate(wfOpac * lerp(1, opacInterp, _GeomDissolveWireframe));
		#endif
		
		if (_GlitchToggle == 1){
			float noise = GetNoise(wPos.xy*frac(_Time.y));
			glitchOffset = ((noise*_GlitchIntensity) * normalDir) * (noise > 0.99999-_GlitchFrequency);
			instabilityOffset = (noise*_Instability) * normalDir;
			wPos.xyz += glitchOffset;
			wPos.xyz += instabilityOffset;
		}

		if (shatterAmt == 0 && !any(instabilityOffset) && !any(glitchOffset) && !any(offsetPos) && !any(finalTriOffsetValue)){
			o.pos = i[j].pos;
		}
		else {
			o.pos = UnityWorldToClipPos(wPos);
		}

		o.rawUV = i[j].rawUV;
		o.uv = i[j].uv;
		o.uv1 = i[j].uv1;
		o.uv2 = i[j].uv2;
		o.uv3 = i[j].uv3;
		o.worldPos = float4(wPos, i[j].worldPos.w);
		o.binormal = i[j].binormal;
		o.tangentViewDir = i[j].tangentViewDir;
		o.cameraPos = i[j].cameraPos;
		o.objPos = i[j].objPos;
		o.bCoords = bCoords[j];
		o.WFStr = wfStr;
		o.wfOpac = wfOpac;
		o.instID = instanceID;
		o.grabPos = i[j].grabPos;
		o.screenPos = i[j].screenPos;
		o.isReflection = i[j].isReflection;
		o.localPos = i[j].localPos;
		o.wfStrAL = wfStrAL;
		o.wfOpacAL = wfOpacAL;
		
		o.tangent = i[j].tangent;
		o.normal = i[j].normal;
		
        #if defined(SHADOWS_SCREEN) || (defined(SHADOWS_DEPTH) && defined(SPOT)) || defined(SHADOWS_CUBE)
			UNITY_TRANSFER_SHADOW(o, o.pos);
        #endif
		UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
		UNITY_TRANSFER_FOG(o, o.pos);
		tristream.Append(o);
	}   
	tristream.RestartStrip();
}
#endif // CLONES_ENABLED
#endif // X_FEATURES
#endif // USX_GEOM_INCLUDED