export declare const particleMtlShader = "\n#include \nusing namespace metal;\n\nconstant float d2r = 3.141592653589793 / 180.;\n\n#ifndef USE_BEZIER\n#define USE_BEZIER 1\n#endif\n\nfloat _evaBezier(float time, float4 k0, float4 k1) {\n float t = (time - k0.x) / (k1.x - k0.x);\n float u = 1. - t;\n float4 tp = float4(pow(u, 3.), 3. * t * u * u, 3. * t * t * u, pow(t, 3.));\n float4 vp = float4(k0.y, k0.w, k1.z, k1.y);\n\n return dot(tp, vp);\n}\n\nfloat _evaHermit(float time, float4 keyframe0, float4 keyframe1) {\n float dt = keyframe1.x - keyframe0.x;\n\n float m0 = keyframe0.w * dt;\n float m1 = keyframe1.z * dt;\n\n float t = (time - keyframe0.x) / dt;\n float t2 = t * t;\n float t3 = t2 * t;\n\n return dot(float4(dot(float3(2., -3., 1.), float3(t3, t2, 1.)), dot(float3(1, -2., 1), float3(t3, t2, t)), t3 - t2, dot(float2(-2, 3), float2(t3, t2))), float4(keyframe0.y, m0, m1, keyframe1.y));\n}\n\nfloat valueFromCurveFrames(float time, float frameStart, float frameCount, constant float4 *curves) {\n int start = int(frameStart);\n int count = int(frameCount - 1.);\n int end = start + count;\n\n for (int i = start; i < end; i++) {\n float4 k0 = curves[i];\n float4 k1 = curves[i + 1];\n\n if (time >= k0.x && time <= k1.x) {\n#ifdef USE_BEZIER\n return _evaBezier(time, k0, k1);\n\n#else\n return _evaHermit(time, k0, k1);\n\n#endif\n }\n }\n\n return curves[start].y;\n}\n\nfloat getValueFromTime(float time, float4 value, constant float4 *curves, float seed) {\n float type = value.x;\n\n if (type == 0.) {\n return value.y;\n } else if (type == 1.) {\n return mix(value.y, value.z, time / value.w);\n }\n\n if (type == 2.) {\n return valueFromCurveFrames(time, floor(value.y), floor(1. / fract(value.y) + 0.5), curves) * value.w + value.z;\n }\n\n if (type == 4.) {\n return mix(value.y, value.z, seed);\n }\n\n return 0.;\n}\n\nfloat _intBezier(float t1, float4 k0, float4 k1) {\n float t = (t1 - k0.x) / (k1.x - k0.x);\n float4 y0 = float4(-.25, .75, -.75, .25);\n float4 c0 = float4(1., -2., 1., 0.);\n float4 c1 = float4(-1.5, 1.5, 0., 0.);\n float4 y1 = float4(1., 0., 0., 0.);\n float4 params = float4(k0.y, k0.w, k1.z, k1.y);\n float4 vp = float4(dot(y0, params), dot(c0, params), dot(c1, params), dot(y1, params));\n\n return dot(float4(pow(t, 4.), pow(t, 3.), pow(t, 2.), t), vp);\n}\n\nfloat _intHermit(float t1, float4 k0, float4 k1) {\n float k = k1.x - k0.x;\n float m0 = k0.w * k;\n float m1 = k1.z * k;\n float t0 = k0.x;\n float v0 = k0.y;\n float v1 = k1.y;\n\n float dt = t0 - t1;\n float dt2 = dt * dt;\n float dt3 = dt2 * dt;\n\n float4 a = float4(dt3 * dt, dt3, dt2, dt) / float4(4. * k * k * k, 3. * k * k, 2. * k, 1.);\n float4 b = float4(m0 + m1 + 2. * v0 - 2. * v1, 2. * m0 + m1 + 3. * v0 - 3. * v1, m0, -v0);\n\n return dot(a, b);\n}\n\nfloat _int2Hermit(float t1, float4 k0, float4 k1) {\n float k = k1.x - k0.x;\n float m0 = k0.w * k;\n float m1 = k1.z * k;\n float t0 = k0.x;\n float v0 = k0.y;\n float v1 = k1.y;\n\n float dt = t0 - t1;\n float dt2 = dt * dt;\n float dt3 = dt2 * dt;\n float k2 = k * k;\n float k3 = k2 * k;\n\n float4 a = float4(-30. * k3, 10. * k2, 5. * k, 3.) * float4(dt, dt2, dt3, dt3 * dt);\n float4 b = float4(v0, m0, 2. * m0 + m1 + 3. * v0 - 3. * v1, m0 + m1 + 2. * v0 - 2. * v1)\n * float4(t0 + t1, t0 + 2. * t1, t0 + 3. * t1, t0 + 4. * t1);\n\n return dot(a, b) / 60. / k3;\n}\n\nfloat _int2Bezier(float time, float4 keyframe0, float4 keyframe1){\n float dt = keyframe1.x - keyframe0.x;\n\n float m0 = keyframe0.w * dt;\n float m1 = keyframe1.z * dt;\n\n float t = (time - keyframe0.x) / dt;\n float t2 = t * t;\n float t3 = t2 * t;\n\n return dot(float4(dot(float3(2., -3., 1.), float3(t3, t2, 1.)), dot(float3(1., -2., 1.), float3(t3, t2, t)), t3 - t2, dot(float2(-2., 3.), float2(t3, t2))), float4(keyframe0.y, m0, m1, keyframe1.y));\n}\n\nfloat _int2Curves(float t1, float frameStart, float frameCount, constant float4 *curves) {\n if (t1 == 0.) {\n return 0.;\n }\n\n int start = int(frameStart);\n int count = int(frameCount - 1.);\n int end = start + count;\n\n float ret = 0.;\n\n for (int i = start; i < end; i++) {\n float4 k0 = curves[i];\n float4 k1 = curves[i + 1];\n\n if (t1 > k0.x && t1 <= k1.x) {\n#ifdef USE_BEZIER\n return ret + _int2Bezier(t1, k0, k1);\n\n#else\n return ret + _int2Hermit(t1, k0, k1);\n#endif\n }\n\n#ifdef USE_BEZIER\n ret += _int2Bezier(k1.x, k0, k1);\n#else\n ret += _int2Hermit(k1.x, k0, k1);\n#endif\n }\n\n return ret;\n}\n\nfloat _intCurves(float time, float frameStart, float frameCount, constant float4 *curves) {\n if (time == 0.) {\n return 0.;\n }\n\n int start = int(frameStart);\n int count = int(frameCount - 1.);\n int end = start + count;\n\n float ret = 0.;\n\n for (int i = start; i < end; i++) {\n float4 k0 = curves[i];\n float4 k1 = curves[i + 1];\n\n if (time > k0.x && time <= k1.x) {\n#ifdef USE_BEZIER\n return ret + _intBezier(time, k0, k1);\n\n#else\n return ret + _intHermit(time, k0, k1);\n\n#endif\n }\n\n#ifdef USE_BEZIER\n ret += _intBezier(k1.x, k0, k1);\n#else\n ret += _intHermit(k1.x, k0, k1);\n#endif\n }\n\n return ret;\n}\n\nfloat intByTime(float life, float dur, float4 value, constant float4 *curves, float seed) {\n float type = value.x;\n float time = life * dur;\n\n if (type == 0.) {\n return value.y * time;\n } else if (type == 2.) {\n float idx = floor(value.y);\n float ilen = floor(1. / fract(value.y) + 0.5);\n float d = _intCurves(life, idx, ilen, curves);\n return d * value.w * dur + value.z * time;\n }\n\n if (type == 4.) {\n return mix(value.y, value.z, seed) * time;\n }\n\n return 0.;\n}\n\nfloat3 int2FromTime(float lifetime, float duration, float4 value, float4 acc, constant float4 *curves, float seed) {\n float type = value.x;\n float time = lifetime * duration;\n\n if (type == 0.) {\n return float3(acc.x, acc.y + value.y, acc.z) * pow(time, 2.) / 2.;\n } else if (type == 2.) {\n float idx = floor(value.y);\n float ilen = floor(1. / fract(value.y) + 0.5);\n float d = _int2Curves(time, idx, ilen, curves);\n return float3(acc.x, acc.y * (d * value.w + (1. - d) * value.z), acc.z) * pow(time, 2.) * 0.5;\n }\n\n if (type == 4.) {\n return float3(acc.x, acc.y * mix(value.y, value.z, seed), acc.z) * pow(time, 2.) / 2.;\n }\n\n return float3(0.);\n}\n\nstruct MarsParticleVertexIn0 {\n packed_float3 aPos;\n packed_float3 aVel;\n packed_float3 aDirX;\n packed_float3 aDirY;\n};\n\nstruct MarsParticleVertexIn1 {\n packed_float3 aRot;\n float aSeed;\n packed_float4 aColor;\n};\n\nstruct MarsParticleVertexIn2 {\n packed_float4 aOffset;\n};\n\n#if USE_SPRITE\nstruct MarsParticleVertexIn3 {\n packed_float3 aSprite;\n};\n#endif\n\ntypedef struct {\n float vLife;\n float vSeed;\n half4 vColor;\n float2 vTexCoord;\n#if USE_SPRITE\n float4 vTexCoordBlend;\n#endif\n float4 position [[position]];\n} MarsParV_Out;\n\ntypedef struct {\n float4 uAcceleration;\n float4 uGravityModifier;\n float4 uOpacityOverLifetimeValue;\n#if USE_SPRITE\n float4 uSprite;\n#endif\n#if FINAL_TARGET\n packed_float3 uFinalTarget;\n float4 uForceCurve;\n#endif\n#if ROT_X_LIFETIME\n float4 uRXByLifeTimeValue;\n#endif\n#if ROT_Y_LIFETIME\n float4 uRYByLifeTimeValue;\n#endif\n#if ROT_Z_LIFETIME\n float4 uRZByLifeTimeValue;\n#endif\n#if LINEAR_VEL_X\n float4 uLinearXByLifetimeValue;\n#endif\n#if LINEAR_VEL_Y\n float4 uLinearYByLifetimeValue;\n#endif\n#if LINEAR_VEL_Z\n float4 uLinearZByLifetimeValue;\n#endif\n#if SPEED_OVER_LIFETIME\n float4 uSpeedLifetimeValue;\n#endif\n#if ORB_VEL_X + ORB_VEL_Y + ORB_VEL_Z\n packed_float3 uOrbCenter;\n#endif\n#if ORB_VEL_X\n float4 uOrbXByLifetimeValue;\n#endif\n#if ORB_VEL_Y\n float4 uOrbYByLifetimeValue;\n#endif\n#if ORB_VEL_Z\n float4 uOrbZByLifetimeValue;\n#endif\n\n float4 uSizeByLifetimeValue;\n#if SIZE_Y_BY_LIFE\n float4 uSizeYByLifetimeValue;\n#endif\n\n float4x4 uModel;\n float4x4 uView;\n float4x4 uViewProjection;\n float4 uParams;\n\n#if ENV_EDITOR\n float4 uEditorTransform; //sx sy dx dy\n#endif\n\n // for fragment\n float4 uColorParams;\n packed_float2 uTexOffset;\n} MarsParticleUniform;\n\ntypedef struct {\n#if VERT_CURVE_VALUE_COUNT > 0\n float4 uVCurveValues[VERT_CURVE_VALUE_COUNT];\n#else\n float4 uVCurveValues[1];\n#endif\n#if FRAG_CURVE_VALUE_COUNT > 0\n float4 uFCurveValues[FRAG_CURVE_VALUE_COUNT];\n#else\n float4 uFCurveValues[1];\n#endif\n} MarsParticleCurve;\n\n#if AS_LINEAR_MOVEMENT + AS_ORBITAL_MOVEMENT + ROT_LIFETIME_AS_MOVEMENT\n#define FUNC(a) getValueFromTime(_life, a, curves, seed)\n#else\n#define FUNC(a) intByTime(_life, _dur, a, curves, seed)\n#endif\n\nfloat3 calLinearMov(float _life, float _dur, MarsParticleUniform uniforms, constant float4 *curves, float seed) {\n float3 mov = float3(0.0);\n\n#if LINEAR_VEL_X\n mov.x = FUNC(uniforms.uLinearXByLifetimeValue);\n#endif\n\n#if LINEAR_VEL_Y\n mov.y = FUNC(uniforms.uLinearYByLifetimeValue);\n#endif\n\n#if LINEAR_VEL_Z\n mov.z = FUNC(uniforms.uLinearZByLifetimeValue);\n#endif\n\n return mov;\n}\n\nfloat3 calOrbitalMov(float _life, float _dur, MarsParticleUniform uniforms, constant float4 *curves, float seed) {\n float3 orb = float3(0.0);\n\n#if ORB_VEL_X\n orb.x = FUNC(uniforms.uOrbXByLifetimeValue);\n#endif\n\n#if ORB_VEL_Y\n orb.y = FUNC(uniforms.uOrbYByLifetimeValue);\n#endif\n\n#if ORB_VEL_Z\n orb.z = FUNC(uniforms.uOrbZByLifetimeValue);\n#endif\n\n return orb;\n}\n\n#if USE_SPRITE\n\nstruct UVDetail {\n float2 uv0;\n float3 uv1;\n};\n\nUVDetail getSpriteUV(float2 uv, float lifeTime, float3 aSprite, float4 uSprite) {\n float t = fract(clamp((lifeTime - aSprite.x) / aSprite.y, 0.0, 1.) * aSprite.z);\n float frame = uSprite.z * t;\n float frameIndex = max(ceil(frame) - 1., 0.);\n float row = floor((frameIndex + 0.1) / uSprite.x);\n float col = frameIndex - row * uSprite.x;\n\n float2 retUV = (float2(col, row) + uv) / uSprite.xy;\n UVDetail ret;\n\n if (uSprite.w > 0.) {\n float blend = frame - frameIndex;\n float frameIndex1 = min(ceil(frame), uSprite.z - 1.);\n float row1 = floor((frameIndex1 + 0.1) / uSprite.x);\n float col1 = frameIndex1 - row1 * uSprite.x;\n float2 coord = (float2(col1, row1) + uv) / uSprite.xy - retUV;\n ret.uv1 = float3(coord.x, 1. - coord.y, blend);\n }\n\n ret.uv0 = float2(retUV.x, 1. - retUV.y);\n return ret;\n}\n\n#endif /* if USE_SPRITE */\nfloat3x3 mat3FromRotation(float3 rotation) {\n float3 sinR = sin(rotation * -d2r);\n float3 cosR = cos(rotation * -d2r);\n\n return float3x3(cosR.z, -sinR.z, 0., sinR.z, cosR.z, 0., 0., 0., 1.) *\n float3x3(cosR.y, 0., sinR.y, 0., 1., 0., -sinR.y, 0, cosR.y) *\n float3x3(1., 0., 0., 0, cosR.x, -sinR.x, 0., sinR.x, cosR.x);\n}\n\nfloat3 calculateTranslation(float3 vel, float life, float dur, MarsParticleUniform uniforms, constant float4 *curves, float seed) {\n float3 d = int2FromTime(life, dur, uniforms.uGravityModifier, uniforms.uAcceleration, curves, seed);\n\n#if SPEED_OVER_LIFETIME\n return vel * intByTime(life, 1., uniforms.uSpeedLifetimeValue, curves, seed) + d;\n\n#endif\n return vel * life * dur + d;\n}\n\nfloat3x3 transformFromRotation(float3 rot, float _life, float _dur, MarsParticleUniform uniforms, constant float4 *curves, float seed) {\n float3 rotation = rot;\n\n#if ROT_X_LIFETIME\n rotation.x += FUNC(uniforms.uRXByLifeTimeValue);\n#endif\n\n#if ROT_Y_LIFETIME\n rotation.y += FUNC(uniforms.uRYByLifeTimeValue);\n#endif\n\n#if ROT_Z_LIFETIME\n rotation.z += FUNC(uniforms.uRZByLifeTimeValue);\n#endif\n\n if (dot(rotation, rotation) == 0.0) {\n return float3x3(1.0);\n }\n\n return mat3FromRotation(rotation);\n}\n\nvertex MarsParV_Out vertex_main(const uint vertexID [[ vertex_id ]],\n constant MarsParticleVertexIn0 *__vertexArray0 [[buffer(0)]],\n constant MarsParticleVertexIn1 *__vertexArray1 [[buffer(1)]],\n constant MarsParticleVertexIn2 *__vertexArray2 [[buffer(2)]],\n#if USE_SPRITE\n constant MarsParticleVertexIn3 *__vertexArray3 [[buffer(3)]],\n constant MarsParticleUniform& uMain [[buffer(4)]],\n constant MarsParticleCurve& CurveValues [[buffer(5)]]\n#else\n constant MarsParticleUniform& uMain [[buffer(3)]],\n constant MarsParticleCurve& CurveValues [[buffer(4)]]\n#endif\n#if COLOR_OVER_LIFETIME\n , texture2d < half, access::sample > uColorOverLifetime [[texture(0)]]\n#endif\n ) {\n MarsParV_Out output;\n MarsParticleVertexIn0 attrs0 = __vertexArray0[vertexID];\n MarsParticleVertexIn1 attrs1 = __vertexArray1[vertexID];\n MarsParticleVertexIn2 attrs2 = __vertexArray2[vertexID];\n float4 aOffset = attrs2.aOffset;\n float time = uMain.uParams.x - aOffset.z;\n float dur = aOffset.w;\n float life = time / dur;\n float4 removePos = (step(life, -0.000001) + step(1.000001, life)) * float4(999., 999., 999., 1.);\n\n life = clamp(life, 0.0, 1.0);\n output.vLife = life;\n\n#if USE_SPRITE\n MarsParticleVertexIn3 attrs3 = __vertexArray3[vertexID];\n ParticleUVOut uvD = getSpriteUV(aOffset.xy, time, attrs3.aSprite, uMain.uSprite);\n output.vTexCoord = uvD.uv0;\n output.vTexCoordBlend = float4(uvD.uv1, uMain.uSprite.w);\n#else\n output.vTexCoord = aOffset.xy;\n#endif\n\n output.vColor = half4(attrs1.aColor);\n\n#if COLOR_OVER_LIFETIME\n#ifdef ENABLE_VERTEX_TEXTURE\n constexpr sampler s(coord::normalized, address::repeat, filter::linear);\n output.vColor *= uColorOverLifetime.sample(s, float2(life, 0.));\n#endif\n#endif\n\n output.vColor.a *= clamp(getValueFromTime(life, uMain.uOpacityOverLifetimeValue, CurveValues.uVCurveValues, attrs1.aSeed), 0., 1.) * uMain.uParams.w;\n\n\n float3 size = float3(float2(getValueFromTime(life, uMain.uSizeByLifetimeValue, CurveValues.uVCurveValues, attrs1.aSeed)), 1.0);\n\n\n#if SIZE_Y_BY_LIFE\n size.y = getValueFromTime(life, uMain.uSizeYByLifetimeValue, CurveValues.uVCurveValues, attrs1.aSeed);\n#endif\n float3 point = transformFromRotation(attrs1.aRot, life, dur, uMain, CurveValues.uVCurveValues, attrs1.aSeed) * float3(attrs0.aDirX * size.x + attrs0.aDirY * size.y);\n float3 pt = calculateTranslation(attrs0.aVel, life, dur, uMain, CurveValues.uVCurveValues, attrs1.aSeed);\n float3 _pos = attrs0.aPos + pt;\n\n#if ORB_VEL_X + ORB_VEL_Y + ORB_VEL_Z\n _pos = mat3FromRotation(calOrbitalMov(life, dur, uMain, CurveValues.uVCurveValues, attrs1.aSeed)) * (_pos - uMain.uOrbCenter);\n _pos += uMain.uOrbCenter;\n#endif\n\n#if LINEAR_VEL_X + LINEAR_VEL_Y + LINEAR_VEL_Z\n _pos.xyz += calLinearMov(life, dur, uMain, CurveValues.uVCurveValues, attrs1.aSeed);\n#endif\n\n#if FINAL_TARGET\n float force = getValueFromTime(life, uMain.uForceCurve, CurveValues.uVCurveValues, attrs1.aSeed);\n float4 pos = float4(mix(_pos, float3(uMain.uFinalTarget), force), 1.);\n#else\n float4 pos = float4(_pos, 1.0);\n#endif\n\n#if RENDER_MODE == 0\n pos = uMain.uModel * pos;\n pos.xyz += uMain.uView[0].xyz * point.x + uMain.uView[1].xyz * point.y;\n#elif RENDER_MODE == 1\n pos.xyz += point;\n pos = uMain.uModel * pos;\n#elif RENDER_MODE == 2\n pos = uMain.uModel * pos;\n pos.xy += point.xy;\n#elif RENDER_MODE == 3\n pos = uMain.uModel * pos;\n pos.xyz += uMain.uView[0].xyz * point.x + uMain.uView[2].xyz * point.y;\n#endif\n output.position = uMain.uViewProjection * pos + removePos;\n output.vSeed = attrs1.aSeed;\n\n//#if USE_FILTER\n// filterMain(life);\n//#endif\n\n#if ENV_EDITOR\n output.position = float4(output.position.xy * uMain.uEditorTransform.xy + uMain.uEditorTransform.zw * output.position.w, output.position.zw);\n#endif\n\n return output;\n}\n\nhalf4 blendColor(half4 color, half4 vc, float mode) {\n half4 ret = color * vc;\n\n #if defined(PRE_MULTIPLY_ALPHA) && PRE_MULTIPLY_ALPHA\n float alpha = vc.a;\n #else\n float alpha = ret.a;\n #endif\n\n if (mode == 1.) {\n ret.rgb *= alpha;\n } else if (mode == 2.) {\n ret.rgb *= alpha;\n ret.a = dot(ret.rgb, half3(1. / 3.));\n } else if (mode == 3.) {\n alpha = color.r * alpha;\n ret = half4(vc.rgb * alpha, alpha);\n }\n\n return half4(ret);\n}\n\nfragment half4 frag_main(MarsParV_Out particle [[stage_in]],\n texture2d < half, access::sample > uMaskTex [[texture(0)]],\n#if COLOR_OVER_LIFETIME\n texture2d < half, access::sample > uColorOverLifetime [[texture(1)]],\n#endif\n constant MarsParticleUniform& uMain [[buffer(0)]]) {\n half4 tempColor = particle.vColor;\n float2 texOffset = uMain.uTexOffset;\n\n if (particle.vLife < 0.) {\n discard_fragment();\n }\n\n constexpr sampler s(coord::normalized, address::repeat, filter::linear);\n half4 color = half4(1.0);\n\n if (uMain.uColorParams.x > 0.0 && is_null_texture(uMaskTex) == false) {\n color = uMaskTex.sample(s, particle.vTexCoord);\n\n#if USE_SPRITE\n if (particle.vTexCoordBlend.w > 0.) {\n color = mix(color, uMaskTex.sample(s, particle.vTexCoordBlend.xy + particle.vTexCoord), particle.vTexCoordBlend.z);\n }\n\n#endif\n }\n\n#if COLOR_OVER_LIFETIME\n#ifndef ENABLE_VERTEX_TEXTURE\n if (is_null_texture(uColorOverLifetime) == false) {\n tempColor *= uColorOverLifetime.sample(s, float2(vLife, 0.));\n }\n#endif\n#endif\n color = blendColor(color, tempColor, round(uMain.uColorParams.y));\n\n if (color.a <= 0.01 && uMain.uColorParams.w > 0.) {\n float _at = uMaskTex.sample(s, particle.vTexCoord + texOffset).a + uMaskTex.sample(s, particle.vTexCoord + texOffset * -1.).a;\n\n if (_at <= 0.02) {\n discard_fragment();\n }\n }\n\n return color;\n}\n\n";