// vertex shader
        // lights/colors
        // materials name is I_MATERIALS in vs and I_PMATERIALS in ps
        // inColorName is color in vs and colors_ in ps
        // dest is o.colors_ in vs and colors_ in ps
        public string GenerateLightingShader(string materialsName, string lightsName, string inColorName, string dest)
        {
            string s = Tabs + "{\n";

            w(ref s, "//Lighting Section\n");
            for (uint j = 0; j < UsableMaterialNode.LightChannels; j++)
            {
                LightChannelControl color = j == 0 ? UsableMaterialNode._chan1._color : UsableMaterialNode._chan2._color;
                LightChannelControl alpha = j == 0 ? UsableMaterialNode._chan1._alpha : UsableMaterialNode._chan2._alpha;

                if (color.MaterialSource == GXColorSrc.Vertex)
                {
                    if (_colorSet[j] != null)
                    {
                        w(ref s, "mat = {0}{1};\n", inColorName, j);
                    }
                    else if (_colorSet[0] != null)
                    {
                        w(ref s, "mat = {0}0;\n", inColorName);
                    }
                    else
                    {
                        w(ref s, "mat = vec4(1.0f, 1.0f, 1.0f, 1.0f);\n");
                    }
                }
                else
                {
                    w(ref s, "mat = {0}[{1}];\n", materialsName, j + 2);
                }

                if (color.Enabled)
                {
                    if (color.AmbientSource == GXColorSrc.Vertex)
                    {
                        if (_colorSet[j] != null)
                        {
                            w(ref s, "lacc = {0}{1};\n", inColorName, j);
                        }
                        else if (_colorSet[0] != null)
                        {
                            w(ref s, "lacc = {0}0;\n", inColorName);
                        }
                        else
                        {
                            w(ref s, "lacc = vec4(0.0f, 0.0f, 0.0f, 0.0f);\n");
                        }
                    }
                    else
                    {
                        w(ref s, "lacc = {0}[{1}];\n", materialsName, j);
                    }
                }
                else
                {
                    w(ref s, "lacc = vec4(1.0f, 1.0f, 1.0f, 1.0f);\n");
                }

                // check if alpha is different
                if (alpha.MaterialSource != color.MaterialSource)
                {
                    if (alpha.MaterialSource == GXColorSrc.Vertex)
                    {
                        if (_colorSet[j] != null)
                        {
                            w(ref s, "mat.w = {0}{1}.w;\n", inColorName, j);
                        }
                        else if (_colorSet[0] != null)
                        {
                            w(ref s, "mat.w = {0}0.w;\n", inColorName);
                        }
                        else
                        {
                            w(ref s, "mat.w = 1.0f;\n");
                        }
                    }
                    else
                    {
                        w(ref s, "mat.w = {0}[{1}].w;\n", materialsName, j + 2);
                    }
                }

                if (alpha.Enabled)
                {
                    if (alpha.AmbientSource == GXColorSrc.Vertex)
                    {
                        if (_colorSet[j] != null)
                        {
                            w(ref s, "lacc.w = {0}{1}.w;\n", inColorName, j);
                        }
                        else if (_colorSet[0] != null)
                        {
                            w(ref s, "lacc.w = {0}0.w;\n", inColorName);
                        }
                        else
                        {
                            w(ref s, "lacc.w = 0.0f;\n");
                        }
                    }
                    else
                    {
                        w(ref s, "lacc.w = {0}[{1}].w;\n", materialsName, j);
                    }
                }
                else
                {
                    w(ref s, "lacc.w = 1.0f;\n");
                }

                if (color.Enabled && alpha.Enabled)
                {
                    //Both have lighting, test if they use the same lights
                    int mask = 0;
                    if (color.Lights == alpha.Lights)
                    {
                        mask = (int)color.Lights & (int)alpha.Lights;
                        if (mask != 0)
                        {
                            for (int i = 0; i < 8; i++)
                            {
                                if ((mask & (1 << i)) != 0)
                                {
                                    w(ref s, GenerateLightShader(i, color, lightsName, 3));
                                }
                            }
                        }
                    }

                    //No shared lights
                    for (int i = 0; i < 8; i++)
                    {
                        if (((mask & (1 << i)) == 0) && ((int)color.Lights & (1 << i)) != 0)
                        {
                            w(ref s, GenerateLightShader(i, color, lightsName, 1));
                        }
                        if (((mask & (1 << i)) == 0) && ((int)alpha.Lights & (1 << i)) != 0)
                        {
                            w(ref s, GenerateLightShader(i, alpha, lightsName, 2));
                        }
                    }
                }
                else if (color.Enabled || alpha.Enabled)
                {
                    //Lights are disabled on one channel so process only the active ones
                    LightChannelControl workingchannel = color.Enabled ? color : alpha;
                    int coloralpha = color.Enabled ? 1 : 2;
                    for (int i = 0; i < 8; i++)
                    {
                        if (((int)workingchannel.Lights & (1 << i)) != 0)
                        {
                            w(ref s, GenerateLightShader(i, workingchannel, lightsName, coloralpha));
                        }
                    }
                }
                w(ref s, "{0}{1} = mat * saturate(lacc);\n", dest, j);
            }
            w(ref s, "}\n");
            return(s);
        }
        // coloralpha - 1 if color, 2 if alpha
        public string GenerateLightShader(int index, LightChannelControl chan, string lightsName, int coloralpha)
        {
            string s = "";

            string swizzle = "xyzw";

            if (coloralpha == 1)
            {
                swizzle = "xyz";
            }
            else if (coloralpha == 2)
            {
                swizzle = "w";
            }

            if (chan.Attenuation == GXAttnFn.None)
            {
                // atten disabled
                switch (chan.DiffuseFunction)
                {
                case GXDiffuseFn.Disabled:
                    w(ref s, "lacc.{0} += {1}[{2}].{3};\n", swizzle, lightsName, index * 5, swizzle);
                    break;

                case GXDiffuseFn.Enabled:
                case GXDiffuseFn.Clamped:
                    w(ref s, "ldir = normalize({0}[{1} + 3].xyz - pos.xyz);\n", lightsName, index * 5);
                    w(ref s, "lacc.{0} += {1}dot(ldir, _norm0)) * {2}[{3}].{4};\n", swizzle, chan.DiffuseFunction != GXDiffuseFn.Enabled ? "max(0.0f," : "(", lightsName, index * 5, swizzle);
                    break;
                }
            }
            else
            {
                // spec and spot
                if (chan.Attenuation == GXAttnFn.Spotlight)
                {
                    // spot
                    w(ref s, "ldir = {0}[{1} + 3].xyz - pos.xyz;\n", lightsName, index * 5);
                    w(ref s, "dist2 = dot(ldir, ldir);\n" +
                      "dist = sqrt(dist2);\n" +
                      "ldir = ldir / dist;\n" +
                      "attn = max(0.0f, dot(ldir, {0}[{1} + 4].xyz));\n", lightsName, index * 5);
                    w(ref s, "attn = max(0.0f, dot({0}[{1} + 1].xyz, float3(1.0f, attn, attn*attn))) / dot({2}[{3} + 2].xyz, float3(1.0f,dist,dist2));\n", lightsName, index * 5, lightsName, index * 5);
                }
                if (chan.Attenuation == GXAttnFn.Specular)
                {
                    // specular
                    w(ref s, "ldir = normalize({0}[{1} + 3].xyz);\n", lightsName, index * 5);
                    w(ref s, "attn = (dot(_norm0, ldir) >= 0.0f) ? max(0.0f, dot(_norm0, {0}[{1} + 4].xyz)) : 0.0f;\n", lightsName, index * 5);
                    w(ref s, "attn = max(0.0f, dot({0}[{1} + 1].xyz, float3(1,attn,attn*attn))) / dot({2}[{3} + 2].xyz, float3(1,attn,attn*attn));\n", lightsName, index * 5, lightsName, index * 5);
                }

                switch (chan.DiffuseFunction)
                {
                case GXDiffuseFn.Disabled:
                    w(ref s, "lacc.{0} += attn * {1}[{2}].{3};\n", swizzle, lightsName, index * 5, swizzle);
                    break;

                case GXDiffuseFn.Enabled:
                case GXDiffuseFn.Clamped:
                    w(ref s, "lacc.{0} += attn * {1}dot(ldir, _norm0)) * {2}[{3}].{4};\n",
                      swizzle,
                      chan.DiffuseFunction != GXDiffuseFn.Enabled ? "max(0.0f," : "(",
                      lightsName,
                      index * 5,
                      swizzle);
                    break;
                }
            }
            w(ref s, "\n");
            return(s);
        }