Example #1
0
    public SceneCaptureCubeSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      SampleFramework.IsMouseVisible = false;

      // Create a graphics screen. This screen has to call the SceneCaptureRenderer 
      // to handle the SceneCaptureNodes!
      _graphicsScreen = new DeferredGraphicsScreen(Services) { DrawReticle = true };
      GraphicsService.Screens.Insert(0, _graphicsScreen);
      GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services));
      
      Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer);
      Services.Register(typeof(IScene), null, _graphicsScreen.Scene);

      // Add gravity and damping to the physics Simulation.
      Simulation.ForceEffects.Add(new Gravity());
      Simulation.ForceEffects.Add(new Damping());

      // Add a custom game object which controls the camera.
      var cameraGameObject = new CameraObject(Services);
      GameObjectService.Objects.Add(cameraGameObject);
      _graphicsScreen.ActiveCameraNode = cameraGameObject.CameraNode;

      // More standard objects.
      GameObjectService.Objects.Add(new GrabObject(Services));
      GameObjectService.Objects.Add(new ObjectCreatorObject(Services));
      //GameObjectService.Objects.Add(new StaticSkyObject(Services));
      GameObjectService.Objects.Add(new DynamicSkyObject(Services, true, false, true));
      GameObjectService.Objects.Add(new GroundObject(Services));
      GameObjectService.Objects.Add(new DudeObject(Services));
      GameObjectService.Objects.Add(new DynamicObject(Services, 1));
      GameObjectService.Objects.Add(new DynamicObject(Services, 2));
      GameObjectService.Objects.Add(new DynamicObject(Services, 5));
      GameObjectService.Objects.Add(new DynamicObject(Services, 6));
      GameObjectService.Objects.Add(new DynamicObject(Services, 7));
      GameObjectService.Objects.Add(new FogObject(Services) { AttachToCamera = true });
      GameObjectService.Objects.Add(new LavaBallsObject(Services));

      // Add a few palm trees.
      Random random = new Random(12345);
      for (int i = 0; i < 10; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-3, -8), 0, random.NextFloat(0, -5));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        float scale = random.NextFloat(0.5f, 1.2f);
        GameObjectService.Objects.Add(new StaticObject(Services, "PalmTree/palm_tree", scale, new Pose(position, orientation)));
      }

      // Load the "Bubble" mesh and place it at a fixed position in the scene.
      var modelNode = ContentManager.Load<ModelNode>("Bubble/Bubble");
      var meshNode = modelNode.GetDescendants().OfType<MeshNode>().First().Clone();
      meshNode.PoseWorld = new Pose(new Vector3F(0, 1, 0));
      _graphicsScreen.Scene.Children.Add(meshNode);

      // Surface of the mesh should reflect the scene in real-time. Reflections are
      // created using environment mapping: The scene is rendered into a cube map,
      // which is then applied to the mesh.
      // To render the scene into a cube map, we need to define a CameraNode and a
      // SceneCaptureNode: The CameraNode defines the point from where the scene is
      // captured. The SceneCaptureNode defines where and in which format the captured
      // image is needed.

      // Attach a camera to the center of the mesh.
      var projection = new PerspectiveProjection();
      projection.SetFieldOfView(ConstantsF.PiOver2, 1, 0.1f, 20);
      var captureCameraNode = new CameraNode(new Camera(projection));
      meshNode.Children = new SceneNodeCollection { captureCameraNode };

      // Attach a SceneCaptureNode with a cube map render target to the mesh.
      var renderToTexture = new RenderToTexture
      {
        Texture = new RenderTargetCube(
          GraphicsService.GraphicsDevice,
          256,
          true,
          SurfaceFormat.Color,
          DepthFormat.None),
      };
      var sceneCaptureNode = new SceneCaptureNode(renderToTexture)
      {
        Shape = meshNode.Shape,
        CameraNode = captureCameraNode,
      };
      meshNode.Children.Add(sceneCaptureNode);

      // The bubble model uses a special effect and is rendered in the "AlphaBlend"
      // render pass. Let's modify the effect parameters to use the created cube map
      // as the reflection map of the bubble.
      var effectBinding = meshNode.Mesh.Materials[0]["AlphaBlend"];
      effectBinding.Set("ReflectionStrength", 0.5f);
      effectBinding.Set("RefractionStrength", 0.0f);
      effectBinding.Set("FresnelBias", 1.0f);
      effectBinding.Set("BlendMode", 1.0f);
      effectBinding.Set("Alpha", 1.0f);
      effectBinding.Set("CustomEnvironmentMap", (TextureCube)renderToTexture.Texture);
    }
Example #2
0
    /// <summary>
    /// Renders the environment maps for the image-based lights.
    /// </summary>
    /// <remarks>
    /// This method uses the current DeferredGraphicsScreen to render new environment maps at
    /// runtime. The DeferredGraphicsScreen has a SceneCaptureRenderer which we can use to
    /// capture environment maps of the current scene.
    /// To capture new environment maps the flag _updateEnvironmentMaps must be set to true.
    /// When this flag is set, SceneCaptureNodes are added to the scene. When the graphics
    /// screen calls the SceneCaptureRenderer the next time, the new environment maps will be
    /// captured.
    /// The flag _updateEnvironmentMaps remains true until the new environment maps are available.
    /// This method checks the SceneCaptureNode.LastFrame property to check if new environment maps
    /// have been computed. Usually, the environment maps will be available in the next frame.
    /// (However, the XNA Game class can skip graphics rendering if the game is running slowly.
    /// Then we would have to wait more than 1 frame.)
    /// When environment maps are being rendered, the image-based lights are disabled to capture
    /// only the scene with ambient and directional lights. Dynamic objects are also disabled
    /// to capture only the static scene.
    /// </remarks>
    private void UpdateEnvironmentMaps()
    {
      if (!_updateEnvironmentMaps)
        return;

      // One-time initializations: 
      if (_sceneCaptureNodes[0] == null)
      {
        // Create cube maps and scene capture nodes.
        // (Note: A cube map size of 256 is enough for surfaces with a specular power
        // in the range [0, 200000].)
        for (int i = 0; i < _sceneCaptureNodes.Length; i++)
        {
          var renderTargetCube = new RenderTargetCube(
            GraphicsService.GraphicsDevice,
            256,
            true,
            SurfaceFormat.Color,
            DepthFormat.None);

          var renderToTexture = new RenderToTexture { Texture = renderTargetCube };
          var projection = new PerspectiveProjection();
          projection.SetFieldOfView(ConstantsF.PiOver2, 1, 1, 100);
          _sceneCaptureNodes[i] = new SceneCaptureNode(renderToTexture)
          {
            CameraNode = new CameraNode(new Camera(projection))
            {
              PoseWorld = _lightNodes[i].PoseWorld,
            },
          };

          _imageBasedLights[i].Texture = renderTargetCube;
        }

        // We use a ColorEncoder to encode a HDR image in a normal Color texture.
        _colorEncoder = new ColorEncoder(GraphicsService)
        {
          SourceEncoding = ColorEncoding.Rgb,
          TargetEncoding = ColorEncoding.Rgbm,
        };

        // The SceneCaptureRenderer has a render callback which defines what is rendered
        // into the scene capture render targets.
        _graphicsScreen.SceneCaptureRenderer.RenderCallback = context =>
        {
          var graphicsDevice = GraphicsService.GraphicsDevice;
          var renderTargetPool = GraphicsService.RenderTargetPool;

          // Get scene nodes which are visible by the current camera.
          CustomSceneQuery sceneQuery = context.Scene.Query<CustomSceneQuery>(context.CameraNode, context);

          // The final image has to be rendered into this render target.
          var ldrTarget = context.RenderTarget;

          // Use an intermediate HDR render target with the same resolution as the final target.
          var format = new RenderTargetFormat(ldrTarget)
          {
            SurfaceFormat = SurfaceFormat.HdrBlendable,
            DepthStencilFormat = DepthFormat.Depth24Stencil8
          };
          var hdrTarget = renderTargetPool.Obtain2D(format);

          graphicsDevice.SetRenderTarget(hdrTarget);
          context.RenderTarget = hdrTarget;

          // Render scene (without post-processing, without lens flares, no debug rendering, no reticle).
          _graphicsScreen.RenderScene(sceneQuery, context, false, false, false, false);

          // Convert the HDR image to RGBM image.
          context.SourceTexture = hdrTarget;
          context.RenderTarget = ldrTarget;
          _colorEncoder.Process(context);
          context.SourceTexture = null;

          // Clean up.
          renderTargetPool.Recycle(hdrTarget);
          context.RenderTarget = ldrTarget;
        };
      }

      if (_sceneCaptureNodes[0].Parent == null)
      {
        // Add the scene capture nodes to the scene.
        for (int i = 0; i < _sceneCaptureNodes.Length; i++)
          _graphicsScreen.Scene.Children.Add(_sceneCaptureNodes[i]);

        // Remember the old time stamp of the nodes.
        _oldEnvironmentMapTimeStamp = _sceneCaptureNodes[0].LastFrame;

        // Disable all lights except ambient and directional lights.
        // We do not capture the image-based lights or any other lights (e.g. point lights)
        // in the cube map.
        foreach (var lightNode in _graphicsScreen.Scene.GetDescendants().OfType<LightNode>())
          lightNode.IsEnabled = (lightNode.Light is AmbientLight) || (lightNode.Light is DirectionalLight);

        // Disable dynamic objects.
        foreach (var node in _graphicsScreen.Scene.GetDescendants())
          if (node is MeshNode || node is LodGroupNode)
            if (!node.IsStatic)
              node.IsEnabled = false;
      }
      else
      {
        // The scene capture nodes are part of the scene. Check if they have been
        // updated.
        if (_sceneCaptureNodes[0].LastFrame != _oldEnvironmentMapTimeStamp)
        {
          // We have new environment maps. Restore the normal scene.
          for (int i = 0; i < _sceneCaptureNodes.Length; i++)
            _graphicsScreen.Scene.Children.Remove(_sceneCaptureNodes[i]);

          _updateEnvironmentMaps = false;

          foreach (var lightNode in _graphicsScreen.Scene.GetDescendants().OfType<LightNode>())
            lightNode.IsEnabled = true;

          foreach (var node in _graphicsScreen.Scene.GetDescendants())
            if (node is MeshNode || node is LodGroupNode)
              if (!node.IsStatic)
                node.IsEnabled = true;
        }
      }
    }
Example #3
0
    public WaterSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      SampleFramework.IsMouseVisible = false;
      _graphicsScreen = new DeferredGraphicsScreen(Services);
      _graphicsScreen.DrawReticle = true;
      GraphicsService.Screens.Insert(0, _graphicsScreen);
      GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services));

      Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer);
      Services.Register(typeof(IScene), null, _graphicsScreen.Scene);

      // Add gravity and damping to the physics Simulation.
      Simulation.ForceEffects.Add(new Gravity());
      Simulation.ForceEffects.Add(new Damping());

      // Add a custom game object which controls the camera.
      var cameraGameObject = new CameraObject(Services);
      GameObjectService.Objects.Add(cameraGameObject);
      _graphicsScreen.ActiveCameraNode = cameraGameObject.CameraNode;

      // More standard objects.
      GameObjectService.Objects.Add(new GrabObject(Services));
      GameObjectService.Objects.Add(new ObjectCreatorObject(Services));
      //GameObjectService.Objects.Add(new StaticSkyObject(Services));
      var dynamicSkyObject = new DynamicSkyObject(Services, true, false, true);
      GameObjectService.Objects.Add(dynamicSkyObject);

      // Add a ground plane with some detail to see the water refractions.
      Simulation.RigidBodies.Add(new RigidBody(new PlaneShape(new Vector3F(0, 1, 0), 0)));
      GameObjectService.Objects.Add(new StaticObject(Services, "Gravel/Gravel", 1, new Pose(new Vector3F(0, 0.001f, 0))));

      GameObjectService.Objects.Add(new DudeObject(Services));
      GameObjectService.Objects.Add(new DynamicObject(Services, 1));
      GameObjectService.Objects.Add(new DynamicObject(Services, 2));
      GameObjectService.Objects.Add(new DynamicObject(Services, 5));
      GameObjectService.Objects.Add(new DynamicObject(Services, 6));
      GameObjectService.Objects.Add(new DynamicObject(Services, 7));
      GameObjectService.Objects.Add(new FogObject(Services) { AttachToCamera = true });

      // The LavaBalls class controls all lava ball instances.
      var lavaBalls = new LavaBallsObject(Services);
      GameObjectService.Objects.Add(lavaBalls);

      // Add a few palm trees.
      var random = new Random(12345);
      for (int i = 0; i < 10; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-3, -8), 0, random.NextFloat(0, -5));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        float scale = random.NextFloat(0.5f, 1.2f);
        GameObjectService.Objects.Add(new StaticObject(Services, "PalmTree/palm_tree", scale, new Pose(position, orientation)));
      }

      // Define the appearance of the water.
      var water = new Water
      {
        SpecularColor = new Vector3F(10f),

        // Small water ripples/waves are created using scrolling normal maps.
        NormalMap0 = ContentManager.Load<Texture2D>("Water/Wave0"),
        NormalMap1 = ContentManager.Load<Texture2D>("Water/Wave1"),
        NormalMap0Scale = 1.8f,
        NormalMap1Scale = 2.2f,
        NormalMap0Velocity = new Vector3F(-0.02f, 0, 0.03f),
        NormalMap1Velocity = new Vector3F(0.02f, 0, -0.03f),
        NormalMap0Strength = 0.5f,
        NormalMap1Strength = 0.5f,

        ReflectionDistortion = 0.2f,
        ReflectionColor = new Vector3F(0.7f),
        RefractionDistortion = 0.05f,
      };

      // Create a box-shaped body of water.
      // We use a TransformedShape containing a BoxShape because the top of the 
      // water body must be at height 0.
      var shape = new TransformedShape(new GeometricObject(
        new BoxShape(10, 1, 20),
        new Pose(new Vector3F(0, -0.5f, 0))));
      _waterNode0 = new WaterNode(water, shape)
      {
        PoseWorld = new Pose(new Vector3F(-1, 0.5f, 0), Matrix33F.CreateRotationY(0.1f)),
        SkyboxReflection = _graphicsScreen.Scene.GetDescendants().OfType<SkyboxNode>().First(),
        DepthBufferWriteEnable = true,
      };
      _graphicsScreen.Scene.Children.Add(_waterNode0);

      // Optional: Create a WaterFlow to move the water using a flow texture.
      _waterFlow0 = new WaterFlow
      {
        FlowMapSpeed = 0.5f,
        FlowMap = GenerateFlowMap(),
        CycleDuration = 3f,
        NoiseMapStrength = 0.1f,
        NoiseMapScale = 0.5f,
      };
      _waterNode0.Flow = _waterFlow0;

      // Optional: Use a planar reflection instead of the skybox reflection.
      // We add a PlanarReflectionNode as a child of the WaterNode.
      var renderToTexture = new RenderToTexture
      {
        Texture = new RenderTarget2D(GraphicsService.GraphicsDevice, 512, 512, false, SurfaceFormat.HdrBlendable, DepthFormat.None),
      };
      var planarReflectionNode = new PlanarReflectionNode(renderToTexture)
      {
        // Same shape as WaterNode.
        Shape = _waterNode0.Shape,

        // Reflection plane is horizontal.
        NormalLocal = new Vector3F(0, 1, 0),
      };
      _waterNode0.PlanarReflection = planarReflectionNode;
      _waterNode0.Children = new SceneNodeCollection(1) { planarReflectionNode };

      // Create a short river with an inclined water surface.
      // Using a WaterFlow with a SurfaceSlopeSpeed, the water automatically flows
      // down the inclined surface.
      _waterNode1 = new WaterNode(water, GetSpiralShape())
      {
        PoseWorld = new Pose(new Vector3F(10, 1.5f, 0), Matrix33F.CreateRotationY(0.1f)),
        EnableUnderwaterEffect = false,
        SkyboxReflection = _graphicsScreen.Scene.GetDescendants().OfType<SkyboxNode>().First(),
        Flow = new WaterFlow
        {
          SurfaceSlopeSpeed = 0.5f,
          CycleDuration = 2f,
          NoiseMapStrength = 0.1f,
          NoiseMapScale = 1,
        }
      };
      _graphicsScreen.Scene.Children.Add(_waterNode1);
    }
Example #4
0
    public OceanSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      SampleFramework.IsMouseVisible = false;
      _graphicsScreen = new DeferredGraphicsScreen(Services);
      _graphicsScreen.DrawReticle = true;
      GraphicsService.Screens.Insert(0, _graphicsScreen);
      GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services));

      Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer);
      Services.Register(typeof(IScene), null, _graphicsScreen.Scene);

      // Add gravity and damping to the physics Simulation.
      Simulation.ForceEffects.Add(new Gravity());
      Simulation.ForceEffects.Add(new Damping());

      // Add a custom game object which controls the camera.
      var cameraGameObject = new CameraObject(Services);
      GameObjectService.Objects.Add(cameraGameObject);
      _graphicsScreen.ActiveCameraNode = cameraGameObject.CameraNode;

      // More standard objects.
      GameObjectService.Objects.Add(new GrabObject(Services));
      GameObjectService.Objects.Add(new ObjectCreatorObject(Services));
      //GameObjectService.Objects.Add(new StaticSkyObject(Services));
      var dynamicSkyObject = new DynamicSkyObject(Services, true, false, true);
      GameObjectService.Objects.Add(dynamicSkyObject);

      // Add an island model.
      GameObjectService.Objects.Add(new StaticObject(Services, "Island/Island", new Vector3F(30), new Pose(new Vector3F(0, 0.75f, 0)), true, true));

      GameObjectService.Objects.Add(new DynamicObject(Services, 1));
      GameObjectService.Objects.Add(new DynamicObject(Services, 2));
      GameObjectService.Objects.Add(new DynamicObject(Services, 5));
      GameObjectService.Objects.Add(new DynamicObject(Services, 6));
      GameObjectService.Objects.Add(new DynamicObject(Services, 7));
      GameObjectService.Objects.Add(new FogObject(Services) { AttachToCamera = true });

      // The LavaBalls class controls all lava ball instances.
      var lavaBalls = new LavaBallsObject(Services);
      GameObjectService.Objects.Add(lavaBalls);

      // Add a few palm trees.
      Random random = new Random(12345);
      for (int i = 0; i < 20; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-7, 4), 0, random.NextFloat(13, 18));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        float scale = random.NextFloat(0.8f, 1.2f);
        GameObjectService.Objects.Add(new StaticObject(Services, "PalmTree/palm_tree", scale, new Pose(position, orientation)));
      }

      // Define the appearance of the water.
      var waterOcean = new Water
      {
        SpecularColor = new Vector3F(20f),
        SpecularPower = 500,

        NormalMap0 = null,
        NormalMap1 = null,

        RefractionDistortion = 0.1f,
        ReflectionColor = new Vector3F(0.2f),
        RefractionColor = new Vector3F(0.6f),

        // Water is scattered in high waves and this makes the wave crests brighter.
        // ScatterColor defines the intensity of this effect.
        ScatterColor = new Vector3F(0.05f, 0.1f, 0.1f),

        // Foam is automatically rendered where the water intersects geometry and
        // where wave are high.
        FoamMap = ContentManager.Load<Texture2D>("Water/Foam"),
        FoamMapScale = 5,
        FoamColor = new Vector3F(1),
        FoamCrestMin = 0.3f,
        FoamCrestMax = 0.8f,

        // Approximate underwater caustics are computed in real-time from the waves.
        CausticsSampleCount = 3,
        CausticsIntensity = 3,
        CausticsPower = 100,
      };

      // If we do not specify a shape in the WaterNode constructor, we get an infinite
      // water plane.
      _waterNode = new WaterNode(waterOcean, null)
      {
        PoseWorld = new Pose(new Vector3F(0, 0.5f, 0)),
        SkyboxReflection = _graphicsScreen.Scene.GetDescendants().OfType<SkyboxNode>().First(),

        // ExtraHeight must be set to a value greater than the max. wave height. 
        ExtraHeight = 2,
      };
      _graphicsScreen.Scene.Children.Add(_waterNode);

      // OceanWaves can be set to displace water surface using a displacement map.
      // The displacement map is computed by the WaterWaveRenderer (see DeferredGraphicsScreen)
      // using FFT and a statistical ocean model.
      _waterNode.Waves = new OceanWaves
      {
        TextureSize = 256,
        HeightScale = 0.004f,
        Wind = new Vector3F(10, 0, 10),
        Directionality = 1,
        Choppiness = 1,
        TileSize = 20,

        // If we enable CPU queries, we can call OceanWaves.GetDisplacement()
        // (see Update() method below).
        EnableCpuQueries = true,
      };

      // Optional: Use a planar reflection instead of the skybox reflection.
      // We add a PlanarReflectionNode as a child of the WaterNode.
      var renderToTexture = new RenderToTexture
      {
        Texture = new RenderTarget2D(GraphicsService.GraphicsDevice, 512, 512, false, SurfaceFormat.HdrBlendable, DepthFormat.None),
      };
      var planarReflectionNode = new PlanarReflectionNode(renderToTexture)
      {
        Shape = _waterNode.Shape,
        NormalLocal = new Vector3F(0, 1, 0),
        IsEnabled = false,
      };
      _waterNode.PlanarReflection = planarReflectionNode;
      _waterNode.Children = new SceneNodeCollection(1) { planarReflectionNode };

      // To let rigid bodies swim, we add a Buoyancy force effect. This force effect
      // computes buoyancy of a flat water surface.
      Simulation.ForceEffects.Add(new Buoyancy
      {
        Surface = new Plane(new Vector3F(0, 1, 0), _waterNode.PoseWorld.Position.Y),
        Density = 1500,
        AngularDrag = 0.3f,
        LinearDrag = 3,
      });
    }
Example #5
0
    public PlanarReflectionSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      SampleFramework.IsMouseVisible = false;

      // Create a graphics screen. This screen has to call the PlanarReflectionRenderer
      // to handle the PlanarReflectionNodes!
      _graphicsScreen = new DeferredGraphicsScreen(Services) { DrawReticle = true };
      GraphicsService.Screens.Insert(0, _graphicsScreen);
      GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services));

      Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer);
      Services.Register(typeof(IScene), null, _graphicsScreen.Scene);

      // Add gravity and damping to the physics Simulation.
      Simulation.ForceEffects.Add(new Gravity());
      Simulation.ForceEffects.Add(new Damping());

      // Add a custom game object which controls the camera.
      var cameraGameObject = new CameraObject(Services);
      GameObjectService.Objects.Add(cameraGameObject);
      _graphicsScreen.ActiveCameraNode = cameraGameObject.CameraNode;

      // More standard objects.
      GameObjectService.Objects.Add(new GrabObject(Services));
      GameObjectService.Objects.Add(new ObjectCreatorObject(Services));
      GameObjectService.Objects.Add(new StaticSkyObject(Services));
      GameObjectService.Objects.Add(new GroundObject(Services));
      GameObjectService.Objects.Add(new DudeObject(Services));
      GameObjectService.Objects.Add(new DynamicObject(Services, 1));
      GameObjectService.Objects.Add(new DynamicObject(Services, 2));
      GameObjectService.Objects.Add(new DynamicObject(Services, 5));
      GameObjectService.Objects.Add(new DynamicObject(Services, 6));
      GameObjectService.Objects.Add(new DynamicObject(Services, 7));
      GameObjectService.Objects.Add(new FogObject(Services));
      GameObjectService.Objects.Add(new LavaBallsObject(Services));

      // Add a few palm trees.
      Random random = new Random(12345);
      for (int i = 0; i < 10; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-3, -8), 0, random.NextFloat(0, -5));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        float scale = random.NextFloat(0.5f, 1.2f);
        GameObjectService.Objects.Add(new StaticObject(Services, "PalmTree/palm_tree", scale, new Pose(position, orientation)));
      }

#if MONOGAME
      // ----- Workaround for missing effect parameter semantics in MonoGame.
      // The effect used by the reflecting ground object defines some new effect
      // parameters and sets the EffectParameterHint to "PerInstance", e.g.:
      //   texture ReflectionTexture < string Hint = "PerInstance"; >;
      // "PerInstance" means that each mesh instance which uses the effect can 
      // have an individual parameter value, i.e. if there are two instances
      // each instance needs a different ReflectionTexture.
      // MonoGame does not yet support effect parameter annotations in shader 
      // code. But we can add the necessary effect parameter descriptions here:
      var effectInterpreter = GraphicsService.EffectInterpreters.OfType<DefaultEffectInterpreter>().First();
      if (!effectInterpreter.ParameterDescriptions.ContainsKey("ReflectionTexture"))
      {
        effectInterpreter.ParameterDescriptions.Add("ReflectionTexture", (parameter, index) => new EffectParameterDescription(parameter, "ReflectionTexture", index, EffectParameterHint.PerInstance));
        effectInterpreter.ParameterDescriptions.Add("ReflectionTextureSize", (parameter, index) => new EffectParameterDescription(parameter, "ReflectionTextureSize", index, EffectParameterHint.PerInstance));
        effectInterpreter.ParameterDescriptions.Add("ReflectionMatrix", (parameter, index) => new EffectParameterDescription(parameter, "ReflectionMatrix", index, EffectParameterHint.PerInstance));
        effectInterpreter.ParameterDescriptions.Add("ReflectionNormal", (parameter, index) => new EffectParameterDescription(parameter, "ReflectionNormal", index, EffectParameterHint.PerInstance));
      }
#endif

      // Get a ground model which can render a planar reflection. See 
      // GroundReflective/MaterialReflective.fx.
      var groundModel = ContentManager.Load<ModelNode>("GroundReflective/Ground");

      // Use the reflective mesh as the ground.
      var groundMesh = groundModel.GetSubtree().OfType<MeshNode>().First().Clone();
      groundMesh.PoseWorld = new Pose(new Vector3F(0, 0.01f, 0));  // Small y offset to draw above the default ground model from GroundObject.
      _graphicsScreen.Scene.Children.Add(groundMesh);

      // Use another instance of the mesh as a wall.
      var wallMesh = groundMesh.Clone();
      wallMesh.ScaleLocal = new Vector3F(0.2f, 1, 0.1f);
      wallMesh.PoseWorld = new Pose(new Vector3F(5, 2, -5), Matrix33F.CreateRotationY(-0.7f) * Matrix33F.CreateRotationX(ConstantsF.PiOver2));
      _graphicsScreen.Scene.Children.Add(wallMesh);

      // Create a PlanarReflectionNode and add it to the children of the first ground mesh.
      // The RenderToTexture class defines the render target for the reflection.
      var renderToTexture0 = new RenderToTexture
      {
        Texture = new RenderTarget2D(
          GraphicsService.GraphicsDevice, 
          1024, 1024, 
          false,  // No mipmaps. Mipmaps can reduce reflection quality.
          SurfaceFormat.HdrBlendable, DepthFormat.Depth24Stencil8),
      };
      _planarReflectionNode0 = new PlanarReflectionNode(renderToTexture0)
      {
        // The reflection is limited to the bounding shape of the ground mesh.
        Shape = groundMesh.Shape,

        // The normal of the reflection plane.
        NormalLocal = new Vector3F(0, 1, 0),
      };
      groundMesh.Children = new SceneNodeCollection(1) { _planarReflectionNode0 };

      // Add another PlanarReflectionNode to the wall.
      var renderToTexture1 = new RenderToTexture
      {
        Texture = new RenderTarget2D(
          GraphicsService.GraphicsDevice,
          1024, 1024, false, 
          SurfaceFormat.HdrBlendable, DepthFormat.Depth24Stencil8),
      };
      _planarReflectionNode1 = new PlanarReflectionNode(renderToTexture1)
      {
        Shape = groundMesh.Shape,
        NormalLocal = new Vector3F(0, 1, 0),
      };
      wallMesh.Children = new SceneNodeCollection(1) { _planarReflectionNode1 };
      
      // Now we have to use the texture that contains the reflection.
      // We use effect parameter bindings to use the reflection texture in the shader of the meshes.
      SetReflectionEffectParameters(groundMesh, _planarReflectionNode0);
      SetReflectionEffectParameters(wallMesh, _planarReflectionNode1);
    }
Example #6
0
    public SceneCapture2DSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      SampleFramework.IsMouseVisible = false;

      // Create a graphics screen. This screen has to call the SceneCaptureRenderer 
      // to handle the SceneCaptureNodes!
      _graphicsScreen = new DeferredGraphicsScreen(Services) { DrawReticle = true };
      GraphicsService.Screens.Insert(0, _graphicsScreen);
      GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services));

      Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer);
      Services.Register(typeof(IScene), null, _graphicsScreen.Scene);

      // Add gravity and damping to the physics Simulation.
      Simulation.ForceEffects.Add(new Gravity());
      Simulation.ForceEffects.Add(new Damping());

      // Add a custom game object which controls the camera.
      var cameraGameObject = new CameraObject(Services);
      GameObjectService.Objects.Add(cameraGameObject);
      _graphicsScreen.ActiveCameraNode = cameraGameObject.CameraNode;

      // More standard objects.
      GameObjectService.Objects.Add(new GrabObject(Services));
      GameObjectService.Objects.Add(new ObjectCreatorObject(Services));
      GameObjectService.Objects.Add(new StaticSkyObject(Services));
      GameObjectService.Objects.Add(new GroundObject(Services));
      GameObjectService.Objects.Add(new DudeObject(Services));
      GameObjectService.Objects.Add(new DynamicObject(Services, 1));
      GameObjectService.Objects.Add(new DynamicObject(Services, 2));
      GameObjectService.Objects.Add(new DynamicObject(Services, 5));
      GameObjectService.Objects.Add(new DynamicObject(Services, 6));
      GameObjectService.Objects.Add(new DynamicObject(Services, 7));
      GameObjectService.Objects.Add(new FogObject(Services));
      GameObjectService.Objects.Add(new LavaBallsObject(Services));

      // Add a few palm trees.
      Random random = new Random(12345);
      for (int i = 0; i < 10; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-3, -8), 0, random.NextFloat(0, -5));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        float scale = random.NextFloat(0.5f, 1.2f);
        GameObjectService.Objects.Add(new StaticObject(Services, "PalmTree/palm_tree", scale, new Pose(position, orientation)));
      }

      // Create another camera which defines the view that should be captured.
      _sceneCaptureCameraNode = cameraGameObject.CameraNode.Clone();

      // Define the target for the render-to-texture operations.
      _renderToTexture = new RenderToTexture
      {
        Texture = new RenderTarget2D(
          GraphicsService.GraphicsDevice,
          1280 / 2, 720 / 2, 
          true,
          SurfaceFormat.Color, 
          DepthFormat.Depth24Stencil8)
      };

      // Add a few TV objects.
      for (int i = 0; i < 10; i++)
        GameObjectService.Objects.Add(new DynamicObject(Services, 3));

      // Attach a SceneCaptureNode to each TV mesh. The SceneCaptureNodes share the
      // same RenderToTexture instance, which means that all TVs show the same image.
      // The SceneCaptureNode has the same bounding shape as the TV mesh. This shape
      // is used for culling: The texture is only updated when at least one TV is 
      // within the player's view.
      foreach (var meshNode in _graphicsScreen.Scene.GetDescendants().OfType<MeshNode>().Where(n => n.Name == "TV"))
      {
        if (meshNode.Children == null)
          meshNode.Children = new SceneNodeCollection();

        meshNode.Children.Add(new SceneCaptureNode(_renderToTexture)
        {
          CameraNode = _sceneCaptureCameraNode,

          // For culling: Assign TV shape to SceneCaptureNode.
          Shape = meshNode.Shape,
        });
      }

      // Get the material of the TV mesh.
      var tvModel = _graphicsScreen.Scene
                                  .GetDescendants()
                                  .OfType<ModelNode>()
                                  .First(n => n.Name == "TVBox");
      var tvMesh = (MeshNode)tvModel.Children[0];
      var tvScreenMaterial = tvMesh.Mesh
                                   .Materials
                                   .First(m => m.Name == "TestCard");

      // Use the texture on the TV screen.
      tvScreenMaterial["Material"].Set("EmissiveTexture", _renderToTexture.Texture);
      tvScreenMaterial["Material"].Set("Exposure", 0.5f);

      // Also assign the texture to the light projected by the TV screen.
      var lightNode = (LightNode)tvModel.Children[1];
      var projectorLight = (ProjectorLight)lightNode.Light;
      projectorLight.Texture = (Texture2D)_renderToTexture.Texture;
    }