/// <summary>
        ///		Gets the 'nth' texture which references the given content type.
        /// </summary>
        /// <remarks>
        ///     If the 'nth' texture unit which references the content type doesn't
        ///     exist, then this method returns an arbitrary high-value outside the
        ///     valid range to index texture units.
        /// </remarks>
        public int GetTextureUnitWithContentTypeIndex(TextureContentType contentType, int index)
        {
            if (!contentTypeLookupBuilt)
            {
                shadowContentTypeLookup = new List<int>();
                for (int i=0; i<textureUnitStates.Count; ++i) {
                    if (((TextureUnitState)textureUnitStates[i]).ContentType == TextureContentType.Shadow)
                        shadowContentTypeLookup.Add(i);
                }
                contentTypeLookupBuilt = true;
            }

            switch(contentType)
            {
            case TextureContentType.Shadow:
                if (index < shadowContentTypeLookup.Count)
                    return shadowContentTypeLookup[index];
                break;
            default:
                // Simple iteration
                for (int i=0; i<textureUnitStates.Count; ++i) {
                    if (((TextureUnitState)textureUnitStates[i]).ContentType == TextureContentType.Shadow) {
                        if (index == 0)
                            return i;
                        else
                            --index;
                    }
                }
                break;
            }

            // not found - return out of range
            return textureUnitStates.Count + 1;
        }
        /// <summary>
        ///		Constructor.
        /// </summary>
        public TextureUnitState(Pass parent, string textureName, int texCoordSet)
        {
            this.parent = parent;
            isBlank = true;

            colorBlendMode.blendType = LayerBlendType.Color;
            SetColorOperation(LayerBlendOperation.Modulate);
            this.TextureAddressing = TextureAddressing.Wrap;

            // set alpha blending options
            alphaBlendMode.operation = LayerBlendOperationEx.Modulate;
            alphaBlendMode.blendType = LayerBlendType.Alpha;
            alphaBlendMode.source1 = LayerBlendSource.Texture;
            alphaBlendMode.source2 = LayerBlendSource.Current;

            // default filtering and anisotropy
            minFilter = FilterOptions.Linear;
            magFilter = FilterOptions.Linear;
            mipFilter = FilterOptions.Point;
            maxAnisotropy = MaterialManager.Instance.DefaultAnisotropy;
            isDefaultFiltering = true;
            isDefaultAniso = true;

            // texture modification params
            scrollU = scrollV = 0;
            transU = transV = 0;
            scaleU = scaleV = 1;
            rotate = 0;
            texMatrix = Matrix4.Identity;
            animDuration = 0;

            textureType = TextureType.TwoD;
            textureSrcMipmaps = -1;

            bindingType = GpuProgramType.Fragment;
            contentType = TextureContentType.Named;

            // texture params
            SetTextureName(textureName, textureType, textureSrcMipmaps);
            this.TextureCoordSet = texCoordSet;

            parent.DirtyHash();
        }
        /// <summary>
        ///    Add a Texture name to the end of the frame container.
        /// </summary>
        /// <remarks>
        ///    Applies to both fixed-function and programmable pipeline.
        /// </remarks>
        /// <param name="name">The name of the texture.</param>
        public void AddFrameTextureName(string name)
        {
            contentType = TextureContentType.Named;

            frames[numFrames] = name;
            // Add blank pointer, load on demand
            frameTextures[numFrames] = null;

            // Load immediately if Material loaded
            if (IsLoaded)
                Load();
            // Tell parent to recalculate hash
            parent.DirtyHash();
        }
        /// <summary>
        ///    Sets this texture layer to use a single texture, given the name of the texture to use on this layer.
        /// </summary>
        /// <remarks>
        ///    Applies to both fixed-function and programmable pipeline.
        /// </remarks>
        /// <param name="name">Name of the texture.</param>
        /// <param name="type">Type of texture this is.</param>
        public void SetTextureName(string name, TextureType type, int mipmaps, bool alpha)
        {
            contentType = TextureContentType.Named;
            if(type == TextureType.CubeMap) {
                // delegate to cube texture implementation
                SetCubicTextureName(name, true);
            }
            else {
                frames[0] = name;
                frameTextures[0] = null;
                numFrames = 1;
                currentFrame = 0;
                isCubic = false;
                textureType = type;
                textureSrcMipmaps = mipmaps;
                isAlpha = alpha;

                if(name.Length == 0) {
                    isBlank = true;
                    return;
                }

                if (this.IsLoaded) {
                    Load(); // reload
                }
                // Tell parent to recalculate hash (for sorting)
                parent.DirtyHash();
            }
        }
        /// <summary>
        ///    Sets this texture layer to use a combination of 6 texture maps, each one relating to a face of a cube.
        /// </summary>
        /// <remarks>
        ///    Cubic textures are made up of 6 separate texture images. Each one of these is an orthoganal view of the
        ///    world with a FOV of 90 degrees and an aspect ratio of 1:1. You can generate these from 3D Studio by
        ///    rendering a scene to a reflection map of a transparent cube and saving the output files.
        ///    <p/>
        ///    Cubic maps can be used either for skyboxes (complete wrap-around skies, like space) or as environment
        ///    maps to simulate reflections. The system deals with these 2 scenarios in different ways:
        ///    <ul>
        ///    <li>
        ///    <p>
        ///    For cubic environment maps, the 6 textures are combined into a single 'cubic' texture map which
        ///    is then addressed using 3D texture coordinates. This is required because you don't know what
        ///    face of the box you're going to need to address when you render an object, and typically you
        ///    need to reflect more than one face on the one object, so all 6 textures are needed to be
        ///    'active' at once. Cubic environment maps are enabled by calling this method with the forUVW
        ///    parameter set to true, and then calling <code>SetEnvironmentMap(true)</code>.
        ///    </p>
        ///    <p>
        ///    Note that not all cards support cubic environment mapping.
        ///    </p>
        ///    </li>
        ///    <li>
        ///    <p>
        ///    For skyboxes, the 6 textures are kept separate and used independently for each face of the skybox.
        ///    This is done because not all cards support 3D cubic maps and skyboxes do not need to use 3D
        ///    texture coordinates so it is simpler to render each face of the box with 2D coordinates, changing
        ///    texture between faces.
        ///    </p>
        ///    <p>
        ///    Skyboxes are created by calling SceneManager.SetSkyBox.
        ///    </p>
        ///    </li>
        ///    </ul>
        ///    <p/>
        ///    Applies to both fixed-function and programmable pipeline.
        /// </remarks>
        /// <param name="textureNames">
        ///    6 versions of this texture with the suffixes _fr, _bk, _up, _dn, _lf, and _rt (before the extension) which
        ///    make up the 6 sides of the box. The textures must all be the same size and be powers of 2 in width & height.
        ///    If you can't make your texture names conform to this, use the alternative method of the same name which takes
        ///    an array of texture names instead.
        /// </param>
        /// <param name="forUVW">
        ///    Set to true if you want a single 3D texture addressable with 3D texture coordinates rather than
        ///    6 separate textures. Useful for cubic environment mapping.
        /// </param>
        public void SetCubicTextureName(string[] textureNames, bool forUVW)
        {
            contentType = TextureContentType.Named;
            numFrames = forUVW ? 1 : 6;
            currentFrame = 0;
            isCubic = true;
            textureType = forUVW ? TextureType.CubeMap : TextureType.TwoD;

            for(int i = 0; i < numFrames; i++) {
                frames[i] = textureNames[i];
                frameTextures[i] = null;
            }

            // tell parent we need recompiling, will cause reload too
            parent.NotifyNeedsRecompile();
        }
        /// <summary>
        ///    Sets this texture layer to use a combination of 6 texture maps, each one relating to a face of a cube.
        /// </summary>
        /// <remarks>
        ///    Cubic textures are made up of 6 separate texture images. Each one of these is an orthoganal view of the
        ///    world with a FOV of 90 degrees and an aspect ratio of 1:1. You can generate these from 3D Studio by
        ///    rendering a scene to a reflection map of a transparent cube and saving the output files.
        ///    <p/>
        ///    Cubic maps can be used either for skyboxes (complete wrap-around skies, like space) or as environment
        ///    maps to simulate reflections. The system deals with these 2 scenarios in different ways:
        ///    <ol>
        ///    <li>
        ///    <p>
        ///    For cubic environment maps, the 6 textures are combined into a single 'cubic' texture map which
        ///    is then addressed using 3D texture coordinates. This is required because you don't know what
        ///    face of the box you're going to need to address when you render an object, and typically you
        ///    need to reflect more than one face on the one object, so all 6 textures are needed to be
        ///    'active' at once. Cubic environment maps are enabled by calling this method with the forUVW
        ///    parameter set to true, and then calling <code>SetEnvironmentMap(true)</code>.
        ///    </p>
        ///    <p>
        ///    Note that not all cards support cubic environment mapping.
        ///    </p>
        ///    </li>
        ///    <li>
        ///    <p>
        ///    For skyboxes, the 6 textures are kept separate and used independently for each face of the skybox.
        ///    This is done because not all cards support 3D cubic maps and skyboxes do not need to use 3D
        ///    texture coordinates so it is simpler to render each face of the box with 2D coordinates, changing
        ///    texture between faces.
        ///    </p>
        ///    <p>
        ///    Skyboxes are created by calling SceneManager.SetSkyBox.
        ///    </p>
        ///    </li>
        ///    </ul>
        ///    <p/>
        ///    Applies to both fixed-function and programmable pipeline.
        /// </remarks>
        /// <param name="textureName">
        ///    The basic name of the texture e.g. brickwall.jpg, stonefloor.png. There must be 6 versions
        ///    of this texture with the suffixes _fr, _bk, _up, _dn, _lf, and _rt (before the extension) which
        ///    make up the 6 sides of the box. The textures must all be the same size and be powers of 2 in width & height.
        ///    If you can't make your texture names conform to this, use the alternative method of the same name which takes
        ///    an array of texture names instead.
        /// </param>
        /// <param name="forUVW">
        ///    Set to true if you want a single 3D texture addressable with 3D texture coordinates rather than
        ///    6 separate textures. Useful for cubic environment mapping.
        /// </param>
        public void SetCubicTextureName(string textureName, bool forUVW)
        {
            if(forUVW) {
                // pass in the single texture name
                SetCubicTextureName(new string[] { textureName }, forUVW);
            }
            else {
                contentType = TextureContentType.Named;
                string[] postfixes = {"_fr", "_bk", "_lf", "_rt", "_up", "_dn"};
                string[] fullNames = new string[6];
                string baseName;
                string ext;

                int pos = textureName.LastIndexOf(".");

                baseName = textureName.Substring(0, pos);
                ext = textureName.Substring(pos);

                for(int i = 0; i < 6; i++) {
                    fullNames[i] = baseName + postfixes[i] + ext;
                }

                SetCubicTextureName(fullNames, forUVW);
            }
        }
        /// <summary>
        ///     Sets the names of the texture images for an animated texture.
        /// </summary>
        /// <remarks>
        ///     Animated textures are just a series of images making up the frames of the animation. All the images
        ///     must be the same size, and their names must have a frame number appended before the extension, e.g.
        ///     if you specify a name of "wall.jpg" with 3 frames, the image names must be "wall_1.jpg" and "wall_2.jpg".
        ///     <p/>
        ///     You can change the active frame on a texture layer by setting the CurrentFrame property.
        /// </remarks>
        /// <param name="names">An array containing the array names to use for the animation.</param>
        /// <param name="numFrames">Number of frames to be used for this animation.</param>
        /// <param name="duration">
        ///     Total length of the animation sequence.  When set to 0, automatic animation does not occur.
        ///     In that scenario, the values can be changed manually by setting the CurrentFrame property.
        /// </param>
        public void SetAnimatedTextureName(string[] names, int numFrames, float duration)
        {
            contentType = TextureContentType.Named;
            if(numFrames > MaxAnimationFrames) {
                throw new AxiomException("Maximum number of texture animation frames exceeded!");
            }

            this.numFrames = numFrames;
            this.animDuration = duration;
            this.currentFrame = 0;
            this.isCubic = false;

            // copy the texture names
            Array.Copy(names, 0, frames, 0, numFrames);

            // if material is already loaded, load this immediately
            if(IsLoaded) {
                Load();

                // tell parent to recalculate the hash
                parent.DirtyHash();
            }
        }
        /// <summary>
        ///     Sets the names of the texture images for an animated texture.
        /// </summary>
        /// <remarks>
        ///     Animated textures are just a series of images making up the frames of the animation. All the images
        ///     must be the same size, and their names must have a frame number appended before the extension, e.g.
        ///     if you specify a name of "wall.jpg" with 3 frames, the image names must be "wall_1.jpg" and "wall_2.jpg".
        ///     <p/>
        ///     You can change the active frame on a texture layer by setting the CurrentFrame property.
        ///     <p/>
        ///     Note: If you can't make your texture images conform to the naming standard layed out here, you
        ///     can call the alternative SetAnimatedTextureName method which takes an array of names instead.
        /// </remarks>
        /// <param name="name">The base name of the series of textures to use.</param>
        /// <param name="numFrames">Number of frames to be used for this animation.</param>
        /// <param name="duration">
        ///     Total length of the animation sequence.  When set to 0, automatic animation does not occur.
        ///     In that scenario, the values can be changed manually by setting the CurrentFrame property.
        /// </param>
        public void SetAnimatedTextureName(string name, int numFrames, float duration)
        {
            string ext, baseName;

            contentType = TextureContentType.Named;
            // split up the base name and file extension
            int pos = name.LastIndexOf(".");
            baseName = name.Substring(0, pos);
            ext = name.Substring(pos);

            string[] names = new string[numFrames];

            // loop through and create the real texture names from the base name
            for(int i = 0; i < numFrames; i++) {
                names[i] = string.Format("{0}_{1}{2}", baseName, i, ext);
            }

            // call the overloaded method, passing in our final texture names
            SetAnimatedTextureName(names, numFrames, duration);
        }