private Material LoadMaterial(string name)
        {
            // Check the file exists
            string filename = string.Format("materials/{0}.txt", name);
            if (!File.Exists(filename))
            {
                Console.WriteLine("Failed to load material {0} (file not found)!", name);
                return null;
            }

            // Load it
            string source = File.ReadAllText(filename);
            Console.WriteLine("Loading material {0}...", name);
            JObject root = JObject.Parse(source);

            // Define material and loop each parameter
            Material material = new Material(Owner.GetComponent<Renderer>());
            material.Name = name;
            foreach (var obj in root)
            {
                switch (obj.Key)
                {
                    case "shader":
                        material.Shader = GetShader((string)obj.Value);
                        if (material.Shader == null)
                        {
                            Console.WriteLine("Error loading material {0} - failed to load shader {1}!", name, (string)obj.Value);
                            return null;
                        }
                        break;
                    case "shadowshader":
                        material.ShadowShader = GetShader((string)obj.Value);
                        if (material.ShadowShader == null)
                        {
                            Console.WriteLine("Error loading material {0} - failed to load shader {1}!", name, (string)obj.Value);
                            return null;
                        }
                        break;
                    case "culling":
                        string val = (string)obj.Value;
                        switch (val)
                        {
                            case "frontface":
                                material.CullingMode = Owner.GetComponent<Renderer>().Culling_Frontface;
                                break;
                            case "backface":
                                material.CullingMode = Owner.GetComponent<Renderer>().Culling_Backface;
                                break;
                            case "none":
                                material.CullingMode = Owner.GetComponent<Renderer>().Culling_None;
                                break;
                        }
                        break;
                    default:
                        if (obj.Value.Type == JTokenType.Array)
                        {
                            int cnt = (obj.Value as JArray).Count;
                            if (cnt == 3)
                                material.SetParameter(obj.Key, new Vector3((float)obj.Value[0], (float)obj.Value[1], (float)obj.Value[2]));
                            else if (cnt == 2)
                                material.SetParameter(obj.Key, new Vector2((float)obj.Value[0], (float)obj.Value[1]));
                            else
                                Console.WriteLine("Parameter '{0}' in material {1} is not understood!", obj.Key, name);
                        }
                        else if (obj.Value.Type == JTokenType.Float)
                            material.SetParameter(obj.Key, (float)obj.Value);
                        else if (obj.Value.Type == JTokenType.String)
                        {
                            string value = (string)obj.Value;
                            if (value.Length == 0)
                                Console.WriteLine("Parameter '{0}' in material {1} is an emoty string!", obj.Key, name);
                            else
                            {
                                // What type of object is it trying to indicate?
                                if (value[0] == '$')
                                {
                                    // Is it a scripted texture?
                                    string texturedata = value.Substring(1);
                                    string[] script = null;
                                    if (texturedata.Contains("$"))
                                    {
                                        string[] texturearray = texturedata.Split('$');
                                        if (texturearray.Length != 2)
                                        {
                                            Console.WriteLine("Parameter '{0}' in material {1} has bad scripted texture data!");
                                        }
                                        else
                                        {
                                            texturedata = texturearray[1];
                                            if (texturearray[0].Contains(":"))
                                                script = texturearray[0].Split(':');
                                            else
                                                script = new string[] { texturearray[0] };
                                        }
                                    }

                                    // Texture
                                    Texture2D tex = GetTexture(texturedata);
                                    if (tex == null)
                                        Console.WriteLine("Parameter '{0}' in material {1} referenced an invalid texture!", obj.Key, name);
                                    else
                                    {
                                        // Apply script
                                        if (script != null && script.Length >= 1)
                                        {
                                            switch (script[0])
                                            {
                                                case "normalmap_from_heightmap":
                                                    var device = Owner.GetComponent<Renderer>().Device;
                                                    Texture2D output = new Texture2D(device, tex.Description);
                                                    output.DebugName = tex.DebugName + "_normals";
                                                    float height = 1.0f;
                                                    if (script.Length > 1) float.TryParse(script[1], out height);
                                                    Result result = Texture2D.ComputeNormalMap(device.ImmediateContext, tex, output, (NormalMapFlags)0, Channel.Red, height);
                                                    if (result.IsFailure)
                                                        Console.WriteLine("Failed to convert heightmap at '{0}' to normalmap in material {1}!", texturedata, name);
                                                    else
                                                        tex = output;
                                                    break;
                                            }
                                        }

                                        // Apply parameter
                                        material.SetParameter(obj.Key, tex);
                                    }
                                }
                                else if (value[0] == '@')
                                {
                                    // Sampler
                                    switch (value.Substring(1))
                                    {
                                        case "wrap_linear":
                                            material.SetParameter(obj.Key, Owner.GetComponent<Renderer>().Sampler_Wrap);
                                            break;
                                        case "clamp_linear":
                                            material.SetParameter(obj.Key, Owner.GetComponent<Renderer>().Sampler_Clamp_Linear);
                                            break;
                                    }
                                }
                                else
                                    Console.WriteLine("Parameter '{0}' in material {1} is not understood!", obj.Key, name);
                            }
                        }
                        break;
                }
            }

            // Return it
            return material;
        }
        public void OnInitialise(InitialiseMessage msg)
        {
            // Initialise scene manager
            Console.WriteLine("Initialising scene manager...");

            // Initialise messages
            queuemsg = new PopulateRenderQueue();
            queuemsg.SceneManager = this;
            cameramsg = new PopulateCameraList();
            cameramsg.Cameras = new OrderedList<Camera>(new CameraComparer());
            cameramsg.ShadowCasters = new HashSet<ShadowCaster>();
            lightmsg = new PopulateLightList();
            lightmsg.Lights = new OrderedList<Light>(new LightComparer());
            psysmsg = new PopulateParticleSystemList();
            psysmsg.ParticleSystems = new List<ParticleSystem>();

            // Create render queue
            workitempool = new ResourcePool<RenderWorkItem>();
            renderqueue = new OrderedList<RenderWorkItem>(new RenderWorkItemComparer());
            effectqueue = new OrderedList<PostProcessEffect>(new PostProcessEffectComparer());

            // Setup GBuffer
            Renderer renderer = Owner.GetComponent<Renderer>();
            gbuffer = renderer.CreateRenderTarget(1, "GBuffer");
            gbuffer.ClearColour = new Color4(0.0f, 0.0f, 0.0f, 0.0f);
            gbuffer.AddDepthComponent();
            gbuffer_colour = gbuffer.AddTextureComponent();
            gbuffer_normal = gbuffer.AddTextureComponent(SlimDX.DXGI.Format.R32G32B32A32_Float);
            gbuffer_position = gbuffer.AddTextureComponent(SlimDX.DXGI.Format.R32G32B32A32_Float);
            gbuffer_material = gbuffer.AddTextureComponent(SlimDX.DXGI.Format.R32G32B32A32_Float);
            gbuffer.Finish();

            // Setup light accumulation buffer
            lightaccum = renderer.CreateRenderTarget(1, "LightAccum");
            lightaccum.ClearColour = new Color4(1.0f, 0.0f, 0.0f, 0.0f);
            lightaccum_diffuse = lightaccum.AddTextureComponent(SlimDX.DXGI.Format.R32G32B32A32_Float);
            lightaccum_specular = lightaccum.AddTextureComponent(SlimDX.DXGI.Format.R32G32B32A32_Float);
            lightaccum.Finish();

            // Setup particle accumulation buffer
            particleaccum = renderer.CreateRenderTarget(1, "ParticleAccum");
            particleaccum.ClearColour = new Color4(0.0f, 0.0f, 0.0f, 0.0f);
            particleaccum_colour = particleaccum.AddTextureComponent();
            particleaccum.Finish();

            // Setup swap buffers
            swapA = renderer.CreateRenderTarget(1, "SwapA");
            swapA_colour = swapA.AddTextureComponent();
            swapA.Finish();
            swapB = renderer.CreateRenderTarget(1, "SwapB");
            swapB_colour = swapB.AddTextureComponent();
            swapB.Finish();

            // Setup materials
            MaterialSystem matsys = Owner.GetComponent<MaterialSystem>();
            mat_blit = matsys.CreateMaterial("blit", "blit");
            mat_blit.SetParameter("smpTexture", renderer.Sampler_Clamp);
            mat_blitlight = matsys.CreateMaterial("blit_light", "blit_light");
            mat_blitlight.SetParameter("smpTexture", renderer.Sampler_Clamp);
            mat_blitlight.SetParameter("texColour", gbuffer.GetTexture(gbuffer_colour));
            mat_blitlight.SetParameter("texDiffuseLight", lightaccum.GetTexture(lightaccum_diffuse));
            mat_blitlight.SetParameter("texSpecularLight", lightaccum.GetTexture(lightaccum_specular));

            // Setup lights
            mat_lights = new Dictionary<LightType, Material>();
            mat_lights.Add(LightType.Ambient, matsys.CreateMaterial("light_ambient", "light_ambient"));
            mat_lights.Add(LightType.Directional, matsys.CreateMaterial("light_directional", "light_directional"));
            mat_lights.Add(LightType.Point, matsys.CreateMaterial("light_point", "light_point"));
            foreach (Material mat in mat_lights.Values)
            {
                mat.SetParameter("texNormal", gbuffer.GetTexture(gbuffer_normal));
                mat.SetParameter("texPosition", gbuffer.GetTexture(gbuffer_position));
                mat.SetParameter("texMaterial", gbuffer.GetTexture(gbuffer_material));
                mat.SetParameter("smpTexture", renderer.Sampler_Clamp);
            }

            // Setup meshes
            mesh_fs = MeshBuilder.BuildFullscreenQuad();
            mesh_skybox = MeshBuilder.BuildCube();
        }