private void CreateShaderMaterials(Quake3Level q3lvl, SceneManager sm)
        {
            // NB this only works for the 'default' shaders for now
            // i.e. those that don't have a .shader script and thus default
            // to just texture + lightmap
            // TODO: pre-parse all .shader files and create lookup for next stage (use ROGL shader_file_t)

            // Material names are shadername#lightmapnumber
            // This is because I like to define materials up front completely
            // rather than combine lightmap and shader dynamically (it's
            // more generic). It results in more materials, but they're small
            // beer anyway. Texture duplication is prevented by infrastructure.
            // To do this I actually need to parse the faces since they have the
            // shader/lightmap combo (lightmap number is not in the shader since
            // it can be used with multiple lightmaps)
            string shaderName;
            int face = q3lvl.Faces.Length;

            while(face-- > 0)
            {
                // Check to see if existing material
                // Format shader#lightmap
                int shadIdx = q3lvl.Faces[face].shader;

                shaderName = String.Format("{0}#{1}", q3lvl.Shaders[shadIdx].name, q3lvl.Faces[face].lmTexture);
                Material shadMat = sm.GetMaterial(shaderName);

                if (shadMat == null && !bspOptions.useLightmaps)
                {
                    // try the no-lightmap material
                    shaderName = String.Format("{0}#n", q3lvl.Shaders[shadIdx].name);
                    shadMat = sm.GetMaterial(shaderName);
                }

                if(shadMat == null)
                {
                    // Colour layer
                    // NB no extension in Q3A(doh), have to try shader, .jpg, .tga
                    string tryName = q3lvl.Shaders[shadIdx].name;

                    // Try shader first
                    Quake3Shader shader = (Quake3Shader) Quake3ShaderManager.Instance.GetByName(tryName);

                    if(shader != null)
                    {
                        shadMat = shader.CreateAsMaterial(sm, q3lvl.Faces[face].lmTexture);
                    }
                    else
                    {
                        // No shader script, try default type texture
                        shadMat = sm.CreateMaterial(shaderName);
                        Pass shadPass = shadMat.GetTechnique(0).GetPass(0);

                        // Try jpg
                        TextureUnitState tex = shadPass.CreateTextureUnitState(tryName + ".jpg");
                        tex.Load();

                        if(tex.IsBlank)
                        {
                            // Try tga
                            tex.SetTextureName(tryName + ".tga");
                        }

                        // Set replace on all first layer textures for now
                        tex.SetColorOperation(LayerBlendOperation.Replace);
                        tex.TextureAddressing = TextureAddressing.Wrap;

                        // for ambient lighting
                        tex.ColorBlendMode.source2 = LayerBlendSource.Manual;

                        if(bspOptions.useLightmaps && q3lvl.Faces[face].lmTexture != -1)
                        {
                            // Add lightmap, additive blending
                            tex = shadPass.CreateTextureUnitState(String.Format("@lightmap{0}", q3lvl.Faces[face].lmTexture));

                            // Blend
                            tex.SetColorOperation(LayerBlendOperation.Modulate);

                            // Use 2nd texture co-ordinate set
                            tex.TextureCoordSet = 1;

                            // Clamp
                            tex.TextureAddressing = TextureAddressing.Clamp;
                        }

                        shadMat.CullingMode = CullingMode.None;
                        shadMat.Lighting = false;
                    }
                }

                shadMat.Load();

                // Copy face data
                BspStaticFaceGroup dest = CopyShaderFaceData(q3lvl, face, shadMat, shadIdx);

                faceGroups[face] = dest;
            }
        }
        /// <summary>
        ///		Creates this shader as an OGRE material.
        /// </summary>
        /// <remarks>
        ///		Creates a new material based on this shaders settings and registers it with the
        ///		SceneManager passed in. 
        ///		Material name is in the format of: shader#lightmap.
        /// </remarks>
        /// <param name="sm">SceneManager to register the material with.</param>
        /// <param name="lightmapNumber">Lightmap number</param>
        public Material CreateAsMaterial(SceneManager sm, int lightmapNumber)
        {
            string materialName = String.Format("{0}#{1}", name, lightmapNumber);
            Material material = sm.CreateMaterial(materialName);

            LogManager.Instance.Write("Using Q3 shader {0}", name);

            for(int p = 0; p < pass.Count; ++p)
            {
                TextureUnitState t;

                // Create basic texture
                t = LoadMaterialTextures(p, lightmapNumber, material);

                // Blending
                if(p == 0)
                {
                    // scene blend
                    material.SetSceneBlending(pass[p].blendSrc, pass[p].blendDest);

                    if(material.IsTransparent && (pass[p].blendSrc != SceneBlendFactor.SourceAlpha))
                        material.DepthWrite = false;

                    t.SetColorOperation(LayerBlendOperation.Replace);
                }
                else
                {
                    if(pass[p].customBlend)
                    {
                        // Fallback for now
                        t.SetColorOperation(LayerBlendOperation.Modulate);
                    }
                    else
                    {
                        t.SetColorOperation(pass[p].blend);
                    }
                }

                // Tex coords
                if(pass[p].texGen == ShaderTextureGen.Base)
                    t.TextureCoordSet = 0;
                else if(pass[p].texGen == ShaderTextureGen.Lightmap)
                    t.TextureCoordSet = 1;
                else if(pass[p].texGen == ShaderTextureGen.Environment)
                    t.SetEnvironmentMap(true, EnvironmentMap.Planar);

                // Tex mod
                // Scale
                t.SetTextureScaleU(pass[p].tcModScale[0]);
                t.SetTextureScaleV(pass[p].tcModScale[1]);

                CreateProceduralTextureMods(p, t);
                // Address mode
                t.TextureAddressing = pass[p].addressMode;
                // Alpha mode
                t.SetAlphaRejectSettings(pass[p].alphaFunc, pass[p].alphaVal);
            }

            // Do farbox (create new material)

            // Do skydome (use this material)
            if(skyDome)
            {
                float halfAngle = 0.5f * (0.5f * (4.0f * (float) Math.Atan(1.0f)));
                float sin = (float) Math.Sin(halfAngle);

                // Quake3 is always aligned with Z upwards
                Quaternion q = new Quaternion(
                    (float) Math.Cos(halfAngle),
                    sin * Vector3.UnitX.x,
                    sin * Vector3.UnitY.y,
                    sin * Vector3.UnitX.z
                    );

                // Also draw last, and make close to camera (far clip plane is shorter)
                sm.SetSkyDome(true, materialName, 20 - (cloudHeight / 256 * 18), 12, 2000, false, q);
            }

            material.CullingMode = Axiom.Graphics.CullingMode.None;
            material.ManualCullMode = cullMode;
            material.Lighting = false;
            material.Load();

            return material;
        }