public void UpdateSubmesh(IGraphicsService graphicsService, WaterNode node) { if (node.Volume == null) return; // We have to update the submesh if it is null or disposed. // Submesh == null --> Update // Submesh != null && VertexBuffer.IsDisposed --> Update // Submesh != null && VertexBuffer == null --> This is the EmptyShape. No updated needed. if (Submesh == null || (Submesh.VertexBuffer != null && Submesh.VertexBuffer.IsDisposed)) ShapeMeshCache.GetMesh(graphicsService, node.Volume, out Submesh, out SubmeshMatrix); }
private void RenderUnderwaterGeometry(WaterNode node, CameraNode cameraNode) { var graphicsDevice = _graphicsService.GraphicsDevice; graphicsDevice.RasterizerState = RasterizerState.CullNone; //graphicsDevice.RasterizerState = GraphicsHelper.RasterizerStateWireFrame; graphicsDevice.DepthStencilState = DepthStencilState.None; var data = ((WaterRenderData)node.RenderData); var submesh = data.Submesh; if (submesh != null) { // User-defined volume _parameterWorld.SetValue((Matrix)( node.PoseWorld * Matrix44F.CreateScale(node.ScaleWorld) * data.SubmeshMatrix)); } else { // Underwater rendering of infinite ocean. // --> Use a shared box around the camera. if (_boxSubmesh == null) _boxSubmesh = MeshHelper.GetBox(_graphicsService); // The box must not be clipped at near or far plane: // Make the box extent (size, size / 2, size) with size / 2 = (near + far) / 2. var projection = cameraNode.Camera.Projection; float size = projection.Near + projection.Far; // The box is centered on the camera. (Ignore camera orientation in case // the camera has a roll.) Vector3F position = cameraNode.PoseWorld.Position; // Top of box must go through water node origin, except when waves are // rendered. (Waves are bent up or down at the near plane.) if (node.Waves == null || node.Waves.DisplacementMap == null) position.Y = node.PoseWorld.Position.Y - size / 4.0f; var world = Matrix44F.CreateTranslation(position) * Matrix44F.CreateScale(size, size / 2.0f, size); _parameterWorld.SetValue((Matrix)world); submesh = _boxSubmesh; } if (node.Water.CausticsSampleOffset <= 0 || node.Water.CausticsIntensity <= Numeric.EpsilonF) _passUnderwater.Apply(); else _passUnderwaterCaustics.Apply(); submesh.Draw(); }
private void RenderSurface(WaterNode node, CameraNode cameraNode, bool isCameraUnderwater) { var graphicsDevice = _graphicsService.GraphicsDevice; var projection = cameraNode.Camera.Projection; graphicsDevice.RasterizerState = RasterizerState.CullNone; //graphicsDevice.RasterizerState = isCameraUnderwater ? RasterizerState.CullClockwise : RasterizerState.CullCounterClockwise; //graphicsDevice.RasterizerState = GraphicsHelper.RasterizerStateWireFrame; graphicsDevice.DepthStencilState = node.DepthBufferWriteEnable ? DepthStencilState.Default : DepthStencilState.DepthRead; // TODO: Support gamma corrected LDR rendering. //if (context.IsHdrEnabled()) _passGamma... if (node.Volume != null) { // ----- Render with user-defined water volume. var data = ((WaterRenderData)node.RenderData); _parameterWorld.SetValue((Matrix)( node.PoseWorld * Matrix44F.CreateScale(node.ScaleWorld) * data.SubmeshMatrix)); ApplySurfacePass(node, false); data.Submesh.Draw(); } else if (node.Waves == null || node.Waves.DisplacementMap == null) { // ----- Infinite ocean without displacement map. // Draw using simple, gigantic quad. if (_quadSubmesh == null) { // This is the really lazy way to get a quad submesh. :-P var quadShape = new TransformedShape(new GeometricObject( new RectangleShape(1, 1), new Pose(Matrix33F.CreateRotationX(-ConstantsF.PiOver2)))).GetMesh(0.001f, 4); _quadSubmesh = MeshHelper.CreateSubmesh(graphicsDevice, quadShape, -1); } // Position the quad under the camera and choose a size large enough to cover everything. Vector3F position = cameraNode.PoseWorld.Position; position.Y = node.PoseWorld.Position.Y; // Add a bit to make sure that the surface is rendered above the underwater geometry. // Without the epsilon if the camera cuts the surface, there might be a horizontal ~1 pixel // line when the surface quad ends before the underwater shape. position.Y += Numeric.EpsilonF * 10; float farPlaneRadius = new Vector3F(Math.Max(Math.Abs(projection.Right), Math.Abs(projection.Left)), Math.Max(Math.Abs(projection.Top), Math.Abs(projection.Bottom)), projection.Far ).Length; float size = 2 * farPlaneRadius; Matrix44F world = Matrix44F.CreateTranslation(position) * Matrix44F.CreateScale(size, 1, size); _parameterWorld.SetValue((Matrix)world); ApplySurfacePass(node, false); _quadSubmesh.Draw(); } else { // ----- Use Projected Grid. if (SetProjectedGridParameters(node, cameraNode, isCameraUnderwater)) { ApplySurfacePass(node, true); ProjectedGridParameters.Submesh.Draw(); } } }
private void ApplySurfacePass(WaterNode node, bool useProjectedGrid) { if (!useProjectedGrid) { // Displacement mapping active if we have at least a wave normal map. if (node.Waves != null && node.Waves.NormalMap != null) { if (node.Water.CausticsIntensity > 0) _passMeshDisplacedFoamCaustics.Apply(); else if (node.Water.FoamMap != null) _passMeshDisplacedFoam.Apply(); else _passMeshDisplaced.Apply(); } else { if (node.Water.FoamMap != null && node.Flow != null) _passMeshFoamFlow.Apply(); else if (node.Water.FoamMap != null) _passMeshFoam.Apply(); else if (node.Flow != null) _passMeshFlow.Apply(); else _passMesh.Apply(); } } else { if (node.Water.CausticsIntensity > 0) _passProjectedGridFoamCaustics.Apply(); else if (node.Water.FoamMap != null) _passProjectedGridFoam.Apply(); else _passProjectedGrid.Apply(); } }
// Returns false if the water is not visible. private bool SetProjectedGridParameters(WaterNode node, CameraNode cameraNode, bool isCameraUnderwater) { var projection = cameraNode.Camera.Projection; // Push projection camera back behind the original camera to get a borders // around the visible FOV. Pose cameraPose = cameraNode.PoseWorld; Matrix33F cameraOrientation = cameraPose.Orientation; Vector3F cameraUp = cameraOrientation.GetColumn(1); Vector3F cameraBack = cameraOrientation.GetColumn(2); Vector3F pushedBackCameraPosition = cameraPose.Position + cameraBack * ProjectedGridParameters.Offset; if (_projectedGridNearCorners == null) _projectedGridNearCorners = new Vector3[4]; float seaLevel = node.PoseWorld.Position.Y; // Get view space vectors from camera to near plane. Vector3F rightTopDirection = new Vector3F(projection.Right, projection.Top, -projection.Near); Vector3F leftTopDirection = new Vector3F(projection.Left, projection.Top, -projection.Near); Vector3F leftBottomDirection = new Vector3F(projection.Left, projection.Bottom, -projection.Near); Vector3F rightBottomDirection = new Vector3F(projection.Right, projection.Bottom, -projection.Near); // Transform vectors to world space directions. rightTopDirection = cameraPose.ToWorldDirection(rightTopDirection); leftTopDirection = cameraPose.ToWorldDirection(leftTopDirection); leftBottomDirection = cameraPose.ToWorldDirection(leftBottomDirection); rightBottomDirection = cameraPose.ToWorldDirection(rightBottomDirection); // Projected grid is a quad which must cover the frustum near plane parts which show the // water surface. This part is bound by the horizon plane (horizontal plane through camera // position) and the water plane. // In the simple academic case, the projected grid quad is equal to the near plane quad. // This makes sense if the camera is looking down. if (!Numeric.IsZero(cameraUp.Y)) { // Camera is NOT exactly looking down: // In general the camera will show part of the water and part of the sky and a lot projected // grid triangles are wasted. // For this case it is better to compute a projected grid quad which covers the necessary // camera near plane quad more tightly. // If camera is exactly at sea level, push it away a bit to avoid a degenerate case // where the whole grid is projected into infinity... if (Numeric.AreEqual(pushedBackCameraPosition.Y, seaLevel)) { if (isCameraUnderwater) pushedBackCameraPosition.Y -= Numeric.EpsilonF; else pushedBackCameraPosition.Y += Numeric.EpsilonF; } // Convert near plane corners to world space. // Transform vectors to world space directions. Vector3F rightTopPosition = rightTopDirection + pushedBackCameraPosition; Vector3F leftTopPosition = leftTopDirection + pushedBackCameraPosition; Vector3F rightBottomPosition = rightBottomDirection + pushedBackCameraPosition; Vector3F leftBottomPosition = leftBottomDirection + pushedBackCameraPosition; // Get min and max y of the corners. float minY = Math.Min(rightTopPosition.Y, leftTopPosition.Y); float maxY = Math.Max(rightTopPosition.Y, leftTopPosition.Y); minY = Math.Min(minY, rightBottomPosition.Y); maxY = Math.Max(maxY, rightBottomPosition.Y); minY = Math.Min(minY, leftBottomPosition.Y); maxY = Math.Max(maxY, leftBottomPosition.Y); Debug.Assert(minY < maxY); // Vertically the projected grid is bound by the horizon plane and the water plane. Plane topPlane = new Plane(Vector3F.UnitY, pushedBackCameraPosition.Y); Plane bottomPlane = new Plane(Vector3F.UnitY, seaLevel); if (seaLevel > pushedBackCameraPosition.Y) { // Camera is under water. MathHelper.Swap(ref topPlane, ref bottomPlane); } Debug.Assert(bottomPlane.DistanceFromOrigin < topPlane.DistanceFromOrigin); // Move planes vertically so that they touch the near plane quad. bottomPlane.DistanceFromOrigin = Math.Max(minY, bottomPlane.DistanceFromOrigin); topPlane.DistanceFromOrigin = Math.Min(maxY, topPlane.DistanceFromOrigin); // Abort if camera is not looking at water surface. if (bottomPlane.DistanceFromOrigin > topPlane.DistanceFromOrigin) return false; // Horizontally the projected grid quad is bound by two vertical planes which go through // the camera origin and the left-most and right-most near plane quad corners. // These plane are parallel to the camera up axis. // (The left vector in this case is orthogonal to world up. It is not the camera left.) Vector3F left = Vector3F.Cross(cameraBack, Vector3F.UnitY); // Get left-most and right-most corner. Vector3F leftMostCorner = rightTopPosition; float leftMostDistance = Vector3F.Dot(left, rightTopDirection); Vector3F rightMostCorner = rightTopPosition; float rightMostDistance = -leftMostDistance; float d = Vector3F.Dot(left, leftTopDirection); if (d > leftMostDistance) { leftMostCorner = leftTopPosition; leftMostDistance = d; } if (-d > rightMostDistance) { rightMostCorner = leftTopPosition; rightMostDistance = -d; } d = Vector3F.Dot(left, rightBottomDirection); if (d > leftMostDistance) { leftMostCorner = rightBottomPosition; leftMostDistance = d; } if (-d > rightMostDistance) { rightMostCorner = rightBottomPosition; rightMostDistance = -d; } d = Vector3F.Dot(left, leftBottomDirection); if (d > leftMostDistance) { leftMostCorner = leftBottomPosition; //leftMostDistance = d; } if (-d > rightMostDistance) { rightMostCorner = leftBottomPosition; //rightMostDistance = -d; } // The projected grid must cover a quad on the near plane. Let's compute a rotated // camera with the same near plane (= identical camera forward/backward direction) but // an up vector which is parallel to the world up vector. Vector3F rotatedCameraUp = Vector3F.Cross(left, cameraBack).Normalized; // The side planes go through the left/right-most points and through the camera origin. // They are parallel to the up vector of the rotated camera. Plane leftPlane = new Plane(pushedBackCameraPosition, leftMostCorner, leftMostCorner + rotatedCameraUp); Plane rightPlane = new Plane(pushedBackCameraPosition, rightMostCorner, rightMostCorner + rotatedCameraUp); // The 4 planes (water, horizon, left, right) can be cut with the near plane to get // the projected grid quad. Plane nearPlane = new Plane(leftTopPosition, rightTopPosition, leftBottomPosition); rightTopPosition = GeometryHelper.GetIntersection(nearPlane, topPlane, rightPlane); leftTopPosition = GeometryHelper.GetIntersection(nearPlane, topPlane, leftPlane); rightBottomPosition = GeometryHelper.GetIntersection(nearPlane, bottomPlane, rightPlane); leftBottomPosition = GeometryHelper.GetIntersection(nearPlane, bottomPlane, leftPlane); rightTopDirection = rightTopPosition - pushedBackCameraPosition; leftTopDirection = leftTopPosition - pushedBackCameraPosition; rightBottomDirection = rightBottomPosition - pushedBackCameraPosition; leftBottomDirection = leftBottomPosition - pushedBackCameraPosition; // Choose near corners such that the triangles where texCoord.y is lower are // rendered in the far for correct depth-sorting. if (pushedBackCameraPosition.Y > seaLevel) { if (rightTopDirection.Y < rightBottomDirection.Y) { MathHelper.Swap(ref rightTopDirection, ref rightBottomDirection); MathHelper.Swap(ref leftTopDirection, ref leftBottomDirection); } } else { if (rightTopDirection.Y > rightBottomDirection.Y) { MathHelper.Swap(ref rightTopDirection, ref rightBottomDirection); MathHelper.Swap(ref leftTopDirection, ref leftBottomDirection); } } } else { // Camera is looking down. if (pushedBackCameraPosition.Y < seaLevel) { // Underwater looking up. We have to swap top and bottom. MathHelper.Swap(ref rightTopDirection, ref rightBottomDirection); MathHelper.Swap(ref leftTopDirection, ref leftBottomDirection); } } _projectedGridNearCorners[0] = (Vector3)rightTopDirection; _projectedGridNearCorners[1] = (Vector3)leftTopDirection; _projectedGridNearCorners[2] = (Vector3)rightBottomDirection; _projectedGridNearCorners[3] = (Vector3)leftBottomDirection; _parameterPushedBackCameraPosition.SetValue((Vector3)pushedBackCameraPosition); _parameterNearCorners.SetValue(_projectedGridNearCorners); _parameterWorld.SetValue(node.PoseWorld); return true; }
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); }
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, }); }
protected override void OnLoad() { var scene = (Scene)_services.GetInstance<IScene>(); var content = _services.GetInstance<ContentManager>(); // 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), //UnderwaterFogDensity = new Vector3F(1, 0.8f, 0.6f), //WaterColor = new Vector3F(0.2f, 0.4f, 0.5f), UnderwaterFogDensity = new Vector3F(12, 8, 8) * 0.02f, WaterColor = new Vector3F(60, 30, 19) * 0.002f, // 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 = content.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 = 0, 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, -100, 0)), SkyboxReflection = scene.GetDescendants().OfType<SkyboxNode>().First(), // ExtraHeight must be set to a value greater than the max. wave height. ExtraHeight = 2, }; 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.008f, Wind = new Vector3F(10, 0, 10), Directionality = 1, Choppiness = 1, TileSize = 20, EnableCpuQueries = false, }; CreateGuiControls(); }
// OnUnload() is called when the GameObject is removed from the IGameObjectService. protected override void OnUnload() { // Remove model and rigid body. _waterNode.Parent.Children.Remove(_waterNode); _waterNode.Dispose(false); _waterNode = null; }