protected override void Initialise() { camera = new Camera3D(); camera.Projection.FarClip = 300; camera.Projection.NearClip = 10; camera.Projection.FieldOfView *= 0.55f; //create the draw target. drawToScreen = new DrawTargetScreen(camera); //no need to clear the colour buffer, as a special background will be drawn drawToScreen.ClearBuffer.ClearColourEnabled = false; //create the light collection first lights = new MaterialLightCollection(); // In this example, the rendering order has been manually optimized to reduce the number of pixels drawn // // In xen, rendering is usually explicit. This means, when a call to Draw() is made, the draw order is // respected, and internally the object will be drawn using the graphics API. // Objects added to the screen will have Draw() called in the order they were added. // // However, the draw order can also cause performance problems. // In general, it's best to draw front to back, this means draw the objects closest to the screen first. // // This way, the objects at the back will be drawing behind the objects already drawn. // Modern video cards can quickly discard pixels if they are 'behind' what is already drawn. // Without front-to-back, the objects at the front could be drawing *over* objects already drawn. // // This is known as overdraw, a case where an object is drawn, only to be 'overdrawn' later in the frame. // Reducing overdraw can help performance, especially when complex shaders are used. // // In this example, the sample is usually vertex-limited (that is, the bottleneck is vertex processing) // However, it can demonstrate how optimizing for overdraw can significantly reduce the number of pixels // that are shaded. // // In debug builds, the DrawStatisticsDisplay class will show the number of pixels drawn in the frame. // // With overdraw optimized draw order, ~1,000,000 pixels are drawn per frame. Without, upto 2,100,000 // pixels are drawn per frame (usually ~1,800,000). (A 1280x720 display has 921,600 pixels) // // This means that without an overdraw optimized draw order, on average, each pixel is being drawn // twice. With an optimized draw order, this number is closer to 1.1, which is very close to the // optimal value of 1.0 (where each pixel is only drawn once). // // Note that the number of pixels reported by the DrawStatisticsDisplay is for the entire frame, including // every render target. Some PCs may not support this value, and display -1. // // One last point.... // This sample is an extreme test of a GPU's ability to push triangles onto the screen (vertex/triangle rate). // However, observation will show the number of triangles drawn is often over 3,300,000! // Assuming half the triangles are back-face culled (an accurate approximation), this still means // there are around 1,650,000 triangles that are visible at any time. // (But remember, the vertex shader still runs for back facing triangles!) // // Assuming approximatly half of these triangles are depth occluded (very approximate), still // results in a huge number of visible triangles. // This all means that the average triangle is drawing a *very* small number of pixels, in this case, // the average for the actors is probably *less than 1 pixel per triangle!*. // // Triangles averaging less than 1 pixel are known as subpixel triangles. // For a number of reasons, subpixel triangles are very inefficent. // For example, if a single pixel is drawn, due to the way a video card works, the pixel shader will always // run in multiples of 4 pixels, so a single pixel triangle will still run the pixel shader 4 times. // // As an approximate rule: // Typically drawing a 1 pixel triangle will be as no faster than drawing a 16 pixel triangle. // // This makes this sample a perfect candidate for level of detail optimization, where a lower resolution // model is used as an actor gets further away from the screen. (Eg, two modelInstances, sharing a controller) // // The vertex shader is also very expensive, and for each triangle, it will be run upto 3 times. // This means the vertex shader is running more often than the pixel shader! // This hypothesis can be confirmed; setting the lights to per-vertex, instead of per-pixel, results // in a significantly *lower* frame rate! // // // bool optimizeForOverdraw = true; //create a list of actors to added to the screen var actors = new List<Actor>(500); //create 500 actors! for (int i = 0; i < 500; i++) { Actor actor = new Actor(this.Content, this.UpdateManager, lights, diskRadius); actors.Add(actor); } //create the lights, similar to Tutorial 14 lights.AmbientLightColour = new Vector3(0.35f, 0.35f, 0.45f); Vector3[] lightPositions = { new Vector3(0, 30, 12), new Vector3(0, -30, 12) }; //setup the two lights in the scene IDraw lightGeometry = null; IDraw lightPoleGeometry = null; //create geometry to display the lights var lightSourceGeometry = new List<IDraw>(); //setup the lights, and create the light globe geometry for (int i = 0; i < lightPositions.Length; i++) { var colour = new Vector3(2, 2, 2); var light = lights.CreatePointLight(lightPositions[i], 1, colour, colour); light.SourceRadius = 6; if (lightGeometry == null) { lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); lightPoleGeometry = new Xen.Ex.Geometry.Cube(new Vector3(0.4f, 0.4f, lightPositions[i].Z * 0.5f)); } //visually show the light //create the light sphere geometry from tutorial 14. var position = lightPositions[i]; lightSourceGeometry.Add(new Tutorial_14.LightSourceDrawer(position, lightGeometry, Color.LightYellow)); position.Z *= 0.5f; lightSourceGeometry.Add(new Tutorial_14.LightSourceDrawer(position, lightPoleGeometry, new Color(40,40,70))); } //create the ground plane, also from tutorial 14 var ground = new Tutorial_14.GroundDisk(this.Content, lights, diskRadius); //this is a special background element, //it draws a gradient over the entire screen, fading from dark at the bottom to light at the top. Color darkBlue = new Color(40, 40, 50); Color lightBlue = new Color(100, 100, 110); var background = new BackgroundGradient(lightBlue, darkBlue); if (optimizeForOverdraw == false) { //add all the objects in a naive order //first add the background (fills the entire screen, draws to every pixel, but is very fast) drawToScreen.Add(background); //then add the ground plane (all the actors will appear on top of the ground plane, overdrawing it) drawToScreen.Add(ground); //then add the lights (which are on top of the ground, overdrawing it) foreach (IDraw geometry in lightSourceGeometry) drawToScreen.Add(geometry); //then finally add the actors, in the order they were created foreach (Actor actor in actors) drawToScreen.Add(actor); } else { //or, add the objects in a order optimized for overdraw #if !XBOX360 //first, add the actors. Because they are almost always closest to the screen //however, use a depth sorter so the actors are sorted into a front to back draw order, //this sorting is based on the centre point of the cull tests they perform. var sorter = new Xen.Ex.Scene.DepthDrawSorter(Xen.Ex.Scene.DepthSortMode.FrontToBack); //Remember, the objects placed in the sorter *must* perform a valid CullTest, //if the CullTest simply returns true/false, no sorting will occur. //(Note the Actor.CullTest method) //to ease the CPU load, have the sorter only sort the actors every few frames... sorter.SortDelayFrameCount = 5; foreach (Actor actor in actors) sorter.Add(actor); // add the actors to the sorter (not the screen) //the sorter itself must be added to the screen! drawToScreen.Add(sorter); // the sorter will then draw the actors in a sorted order #else //In this case (on the Xbox), because the application is heavily vertex limited //and already heavily CPU stretched by the animation system, the cost of //sorting the actors actually causes a larger performance hit on the CPU than //the time saved on the GPU. This inballance causes a frame rate drop. // //However, the reason for this may be unexpected. //The framerate drop is not caused by the overhead of sorting the actors. // //Any 3D API calls made are doubly expensive on the XBOX, so in order to //maintain 20fps in this sample, the primary (rendering) thread must not //block, or switch to task processing. //If it does so, valuable rendering time is lost. // //When using a sorter, the actors are drawn in an order that is constantly changing. //However, they always have Update() called in a consistent order. // //During Update() the actors animation controllers will spawn thread tasks to //process their animation. // //These tasks are processed on the spare xbox hardware threads, they are //processed in the order they were added. //Processing the animation usually completes before the rendering finishes. //(the rendering is not delayed waiting for the animation to finish). // //However, when sorting the actors get drawn in an unpredictable order, //this means the last actor added could be the first actor to draw, //in such a case, the chances of it's animation processing having completed //is *very* low. When this happens, the rendering thread has to switch to //processing animations, delaying rendering. // //So, for the xbox, in this sample it's best just to draw in the update order. foreach (Actor actor in actors) drawToScreen.Add(actor); #endif //add the light source geometry, as they are usually below the actors, but above the ground foreach (IDraw geometry in lightSourceGeometry) drawToScreen.Add(geometry); //then add the ground plane, which is usually below the actors and lights. drawToScreen.Add(ground); //finally, enable a special feature of ElementRect. //This makes the element draw at the maximum possible Z distance //(behind anything else that has been drawn) background.DrawAtMaxZDepth = true; //add it to the screen drawToScreen.Add(background); } //finally, //create the draw statistics display stats = new Xen.Ex.Graphics2D.Statistics.DrawStatisticsDisplay(this.UpdateManager); drawToScreen.Add(stats); }
protected override void Initialise() { camera = new Camera3D(); //create the draw target. drawToScreen = new DrawTargetScreen(this, camera); //clear to dark blue drawToScreen.ClearBuffer.ClearColour = new Color(20, 20, 40); //create the light collection lights = new MaterialLightCollection(); //set a dark blue ambient colour lights.AmbientLightColour = new Color(40, 40, 80).ToVector3(); //positions for two lights Vector3[] lightPositions = new Vector3[] { new Vector3(0, 30, 2), new Vector3(0, -30, 2) }; //geometry for a light (shared for each light) IDraw lightGeometry = null; for (int i = 0; i < lightPositions.Length; i++) { float lightHalfFalloffDistance = 15; Color lightColor = Color.LightYellow; Color lightSpecularColour = Color.WhiteSmoke; bool perPixel = i < 2; //first two lights are per-pixel //interface to the light about to be created IMaterialPointLight light = null; //create the point light light = lights.AddPointLight(perPixel, lightPositions[i], lightHalfFalloffDistance, lightColor, lightSpecularColour); //adjust the lighting attenuation model, the constant defaults to 1, which prevents the light being brighter than 1.0 in the falloff equation //set to 0.25, the light will get really bright in close (up to 4) //(see light.QuadraticAttenuation remarks for an explanation of the falloff model) light.ConstantAttenuation = 0.25f; //create the light geometry (a sphere) if (lightGeometry == null) { lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); } //visually show the light with a light drawer IDraw lightSourceDrawer = new LightSourceDrawer(lightPositions[i], lightGeometry, lightColor); //add the light geometry to the screen drawToScreen.Add(lightSourceDrawer); } //create the ground disk GroundDisk ground = new GroundDisk(this.Content, lights, diskRadius); //then add it to the screen drawToScreen.Add(ground); }
protected override void Initialise() { Resource.EnableResourceTracking(); camera = new Camera3D(); camera.Projection.FarClip = 300; camera.Projection.NearClip = 10; camera.Projection.FieldOfView *= 0.55f; //create the draw target. drawToScreen = new DrawTargetScreen(this, camera); //no need to clear the colour buffer, as a special background will be drawn drawToScreen.ClearBuffer.ClearColourEnabled = false; //create the light collection first lights = new MaterialLightCollection(); // In this example, the rendering order has been manually optimized to reduce the number of pixels drawn // // In xen, rendering is usually explicit. This means, when a call to Draw() is made, the draw order is // respected, and internally the object will be drawn using the graphics API. // Objects added to the screen will have Draw() called in the order they were added. // // However, the draw order can also cause performance problems. // In general, it's best to draw front to back, this means draw the objects closest to the screen first. // // This way, the objects at the back will be drawing behind the objects already drawn. // Modern video cards can quickly discard pixels if they are 'behind' what is already drawn. // Without front-to-back, the objects at the front could be drawing *over* objects already drawn. // // This is known as overdraw, a case where an object is drawn, only to be 'overdrawn' later in the frame. // Reducing overdraw can help performance, especially when complex shaders are used. // // In this example, the sample is usually vertex-limited (that is, the bottleneck is vertex processing) // However, it can demonstrate how optimizing for overdraw can significantly reduce the number of pixels // that are shaded. // // In debug builds, the DrawStatisticsDisplay class will show the number of pixels drawn in the frame. // // With overdraw optimized draw order, ~1,000,000 pixels are drawn per frame. Without, upto 2,100,000 // pixels are drawn per frame (usually ~1,800,000). (A 1280x720 display has 921,600 pixels) // // This means that without an overdraw optimized draw order, on average, each pixel is being drawn // twice. With an optimized draw order, this number is closer to 1.1, which is very close to the // optimal value of 1.0 (where each pixel is only drawn once). // // Note that the number of pixels reported by the DrawStatisticsDisplay is for the entire frame, including // every render target. Some PCs may not support this value, and display -1. // // One last point.... // This sample is an extreme test of a GPU's ability to push triangles onto the screen (vertex/triangle rate). // However, observation will show the number of triangles drawn is often over 3,300,000! // Assuming half the triangles are back-face culled (an accurate approximation), this still means // there are around 1,650,000 triangles that are visible at any time. // (But remember, the vertex shader still runs for back facing triangles!) // // Assuming approximatly half of these triangles are depth occluded (very approximate), still // results in a huge number of visible triangles. // This all means that the average triangle is drawing a *very* small number of pixels, in this case, // the average for the actors is probably *less than 1 pixel per triangle!*. // // Triangles averaging less than 1 pixel are known as subpixel triangles. // For a number of reasons, subpixel triangles are very inefficent. // For example, if a single pixel is drawn, due to the way a video card works, the pixel shader will always // run in multiples of 4 pixels, so a single pixel triangle will still run the pixel shader 4 times. // // This makes this sample a perfect candidate for level of detail optimization, where a lower resolution // model is used as an actor gets further away from the screen. // // The vertex shader is also very expensive, and for each triangle, it will be run upto 3 times. // This means the vertex shader is quite possibly running more often than the pixel shader. // This hypothesis can be confirmed; setting the lights to per-vertex, instead of per-pixel, results // in a significantly *lower* frame rate! // // // bool optimizeForOverdraw = true; //create a list of actors to added to the screen List <Actor> actors = new List <Actor>(500); //create 500 actors! for (int i = 0; i < 500; i++) { Actor actor = new Actor(this.Content, this.UpdateManager, lights, diskRadius); actors.Add(actor); } //create the lights, similar to Tutorial 14 lights.AmbientLightColour = new Vector3(0.45f, 0.45f, 0.5f); Vector3[] lightPositions = new Vector3[] { new Vector3(0, 30, 12), new Vector3(0, -30, 12) }; //setup the two lights in the scene IDraw lightGeometry = null; IDraw lightPoleGeometry = null; //create geometry to display the lights List <IDraw> lightSourceGeometry = new List <IDraw>(); //setup the lights, and create the light globe geometry for (int i = 0; i < lightPositions.Length; i++) { Vector3 colour = new Vector3(5, 5, 5); IMaterialPointLight light = lights.AddPointLight(i < 2, lightPositions[i], 8, colour, colour); light.ConstantAttenuation = 0.25f; // make the ligh falloff curve a lot sharper (brighter at the centre) light.LinearAttenuation = 0; light.QuadraticAttenuation = 0.075f; //approximate inverse distance in which the brightness of the light will halve if (lightGeometry == null) { lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); lightPoleGeometry = new Xen.Ex.Geometry.Cube(new Vector3(0.4f, 0.4f, lightPositions[i].Z * 0.5f)); } //visually show the light //create the light sphere geometry from tutorial 14. Vector3 position = lightPositions[i]; lightSourceGeometry.Add(new Tutorial_14.LightSourceDrawer(position, lightGeometry, Color.LightYellow)); position.Z *= 0.5f; lightSourceGeometry.Add(new Tutorial_14.LightSourceDrawer(position, lightPoleGeometry, new Color(40, 40, 70))); } //create the ground plane, also from tutorial 14 Tutorial_14.GroundDisk ground = new Tutorial_14.GroundDisk(this.Content, lights, diskRadius); //this is a special background element, //it draws a gradient over the entire screen, fading from dark at the bottom to light at the top. Color darkBlue = new Color(40, 40, 50); Color lightBlue = new Color(100, 100, 110); BackgroundGradient background = new BackgroundGradient(lightBlue, darkBlue); if (optimizeForOverdraw == false) { //add all the objects in a naive order //first add the background (fills the entire screen, draws to every pixel, but is very fast) drawToScreen.Add(background); //then add the ground plane (all the actors will appear on top of the ground plane, overdrawing it) drawToScreen.Add(ground); //then add the lights (which are on top of the ground, overdrawing it) foreach (IDraw geometry in lightSourceGeometry) { drawToScreen.Add(geometry); } //then finally add the actors, in the order they were created foreach (Actor actor in actors) { drawToScreen.Add(actor); } } else { //or, add the objects in a order optimized for overdraw #if !XBOX360 //first, add the actors. Because they are almost always closest to the screen //however, use a depth sorter so the actors are sorted into a front to back draw order, //this sorting is based on the centre point of the cull tests they perform. Xen.Ex.Scene.DepthDrawSorter sorter = new Xen.Ex.Scene.DepthDrawSorter(Xen.Ex.Scene.DepthSortMode.FrontToBack); //Remember, the objects placed in the sorter *must* perform a valid CullTest, //if the CullTest simply returns true/false, no sorting will occur. //(Note the Actor.CullTest method) //to ease the CPU load, have the sorter only sort the actors every few frames... sorter.SortDelayFrameCount = 5; foreach (Actor actor in actors) { sorter.Add(actor); // add the actors to the sorter (not the screen) } //the sorter itself must be added to the screen! drawToScreen.Add(sorter); // the sorter will then draw the actors in a sorted order #else //In this case (on the Xbox), because the application is heavily vertex limited //and already heavily CPU stretched by the animation system, the cost of //sorting the actors actually causes a larger performance hit on the CPU than //the time saved on the GPU. This inballance causes a frame rate drop. // //However, the reason for this may be unexpected. //The framerate drop is not caused by the overhead of sorting the actors. // //Any 3D API calls made are doubly expensive on the XBOX, so in order to //maintain 20fps in this sample, the primary (rendering) thread must not //block, or switch to task processing. //If it does so, valuable rendering time is lost. // //When using a sorter, the actors are drawn in an order that is constantly changing. //However, they always have Update() called in a consistent order. // //During Update() the actors animation controllers will spawn thread tasks to //process their animation. // //These tasks are processed on the three spare xbox hardware threads, they are //processed in the order they were added. //Processing the animation usually completes before the rendering finishes. //(the rendering is not delayed waiting for the animation to finish). // //However, when sorting the actors get drawn in an unpredictable order, //this means the last actor added could be the first actor to draw, //in such a case, the chances of it's animation processing having completed //is *very* low. When this happens, the rendering thread has to switch to //processing animations, delaying rendering. // //So, for the xbox, it's best just to draw in the update order. foreach (Actor actor in actors) { drawToScreen.Add(actor); } #endif //add the light source geometry, as they are usually below the actors, but above the ground foreach (IDraw geometry in lightSourceGeometry) { drawToScreen.Add(geometry); } //then add the ground plane, which is usually below the actors and lights. drawToScreen.Add(ground); //finally, enable a special feature of ElementRect. //This makes the element draw at the maximum possible Z distance //(behind anything else that has been drawn) background.DrawAtMaxZDepth = true; //add it to the screen drawToScreen.Add(background); } //finally, //create the draw statistics display stats = new Xen.Ex.Graphics2D.Statistics.DrawStatisticsDisplay(this.UpdateManager); drawToScreen.Add(stats); }
protected override void Initialise() { camera = new Camera3D(); //create the draw target. drawToScreen = new DrawTargetScreen(camera); //clear to dark blue drawToScreen.ClearBuffer.ClearColour = new Color(20, 20, 40); //create the light collection lights = new MaterialLightCollection(); //set a dark blue ambient colour lights.AmbientLightColour = new Color(40, 40, 80).ToVector3(); //positions for two lights Vector3[] lightPositions = new Vector3[] { new Vector3(0, 30, 4), new Vector3(0, -30, 4) }; //geometry for a light (shared for each light) IDraw lightGeometry = null; for (int i = 0; i < lightPositions.Length; i++) { float intensity = 2; Color lightColor = Color.LightYellow; Color lightSpecularColour = Color.WhiteSmoke; //interface to the light about to be created IMaterialPointLight light = null; //create the point light light = lights.CreatePointLight(lightPositions[i], intensity, lightColor, lightSpecularColour); //Adjusting this value controls how quickly the light falloff occurs. //A larger value will produce a slower falloff, and result in a softer, brighter light. //A smaller value will produce a darker, but sharper light source. //Generally, if you reduce this value, increase the intensity to compensate. light.SourceRadius = 4; //create the light geometry (a sphere) if (lightGeometry == null) lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); //visually show the light with a light drawer var lightSourceDrawer = new LightSourceDrawer(lightPositions[i], lightGeometry,lightColor); //add the light geometry to the screen drawToScreen.Add(lightSourceDrawer); } //create the ground disk var ground = new GroundDisk(this.Content, lights, diskRadius); //then add it to the screen drawToScreen.Add(ground); }
protected override void Initialise() { //create the camera Xen.Camera.FirstPersonControlledCamera3D camera = new Xen.Camera.FirstPersonControlledCamera3D(this.UpdateManager,Vector3.Zero); camera.Projection.FarClip *= 10; this.camera = camera; //create the draw target. drawToScreen = new DrawTargetScreen(camera); //25,000 instances const int instanceCount = 25000; const float areaRadius = 500; //setup the two draw lists this.staticDrawList = new ToggleDrawList(); this.dynamicDrawList = new ToggleDrawList(); //geometry that will be drawn var geometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 2, true, false, false); //create the mesh instance drawer, (but add it to the screen later) var meshDrawer = new DynamicInstancedMeshGeometry(instanceCount, geometry); var staticMeshDrawer = new StaticInstancedMeshGeometry(instanceCount, geometry); //the dynamicly culled instances are added to a StaticBinaryTreePartition, which //sorts the items into a binary tree, for more efficient culling. //This class assumes it's children do not move (ie they are static) var sceneTree = new Xen.Ex.Scene.StaticBinaryTreePartition(); //add it to the dynamic list dynamicDrawList.Children.Add(sceneTree); //create the instances Random random = new Random(); for (int i = 0; i < instanceCount; i++) { //create a random position in a sphere Vector3 position = new Vector3( (float)(random.NextDouble()-.5), (float)(random.NextDouble()-.5), (float)(random.NextDouble()-.5)); position.Normalize(); position *= (float)Math.Sqrt(random.NextDouble()) * areaRadius; //create the instance var instance = new DynamicMeshInstance(meshDrawer, position); //add the instance to the StaticBinaryTreePartition sceneTree.Add(instance); //add the details of this instance to the static drawer staticMeshDrawer.AddStaticInstance(Matrix.CreateTranslation(position), 1); } //now add the drawer (instances will be drawn by the StaticBinaryPartition, before the drawer) dynamicDrawList.Children.Add(meshDrawer); //now add the static mesh drawer staticDrawList.Children.Add(staticMeshDrawer); //finally, add them both to the screen this.drawToScreen.Add(dynamicDrawList); this.drawToScreen.Add(staticDrawList); //Note that if the StaticBinaryTreePartition was not used, then //in each frame, every single instance would perform a CullTest to the screen //CullTests, despite their simplicity can be very costly in large numbers. //The StaticBinaryTreePartition will usually perform a maximum number of CullTests //that is approximately ~30% the number of children. (in this case, ~8000 tests) //At it's best, when it's entirely off or on screen, it will perform only 1 or 2 CullTests. //The number of cull tests performed will be displayed in debug builds of this tutorial: //add some statusText to display on screen to show the stats statusText = new TextElement(); statusText.Position = new Vector2(50, -50); drawToScreen.Add(statusText); //add the cull test visualiser this.cullVis = new Xen.Ex.Scene.CullTestVisualizer(); drawToScreen.AddModifier(cullVis); }
protected override void Initialise() { camera = new Camera3D(); //create the draw target. drawToScreen = new DrawTargetScreen(camera); //clear to dark blue drawToScreen.ClearBuffer.ClearColour = new Color(20, 20, 40); //create the light collection lights = new MaterialLightCollection(); //set a dark blue ambient colour lights.AmbientLightColour = new Color(40, 40, 80).ToVector3(); //get a list of predifined colours in the 'Color' structure using reflection //avoid doing this sort of thing at runtime! PropertyInfo[] colours = typeof(Color).GetProperties(BindingFlags.Static | BindingFlags.Public); // get all the static properties Random random = new Random(); int lightCount = 12; //geometry for a light (shared for each light) IDraw lightGeometry = null; for (int i = 0; i < lightCount; i++) { //start with white. Color colour = Color.White; //try and pick a random colour from the list, using reflection to get the value of the property try { //pick a random field info object (a reflected colour property) var randomColourField = colours[random.Next(colours.Length)]; //try and get it's value object colourObject = randomColourField.GetValue(null, null); if (colourObject is Color) colour = (Color)colourObject; } catch { //this shouldn't fail, but always be careful with reflection... //typically this would be handled correctly, but here, just stick with white. } float angle = (float)i / (float)lightCount * (float)Math.PI * 2; Vector3 position = new Vector3((float)Math.Sin(angle) * (diskRadius + 1), (float)Math.Cos(angle) * (diskRadius + 1), 4); float intensity = 1; //interface to the light about to be created IMaterialPointLight light = null; //create the point light light = lights.CreatePointLight(position, intensity, colour, colour); light.SourceRadius = 5; //create the light geometry (a sphere) if (lightGeometry == null) lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); //visually show the light with a light drawer IDraw lightSourceDrawer = new LightSourceDrawer(position, lightGeometry, colour); //add the light geometry to the screen drawToScreen.Add(lightSourceDrawer); } //add the actor actor = new Tutorials.Tutorial_11.Actor(this.Content, this.lights); drawToScreen.Add(actor); //create the ground disk GroundDisk ground = new GroundDisk(this.Content, lights, diskRadius); //then add it to the screen drawToScreen.Add(ground); }