public void Draw(Sphere planet, Camera camera) { // Apply matrices to the relevant bones, as discussed in the Simple // Animation Sample. leftBackWheelBone.Transform = wheelRollMatrix * leftBackWheelTransform; rightBackWheelBone.Transform = wheelRollMatrix * rightBackWheelTransform; leftFrontWheelBone.Transform = wheelRollMatrix * leftFrontWheelTransform; rightFrontWheelBone.Transform = wheelRollMatrix * rightFrontWheelTransform; // now that we've updated the wheels' transforms, we can create an array // of absolute transforms for all of the bones, and then use it to draw. Matrix[] boneTransforms = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(boneTransforms); // get world space position by adding terrain node position to planet position Position3 worldSpacePosition = movement.Position; // translate to camera space by subtracting the camera position, scale by planet scale Position3 cameraSpacePosition = (worldSpacePosition - camera.Position) * planet.Scale; Matrix cameraSpaceMatrix = planet.ScaleMatrix * movement.WorldMatrix * Matrix.CreateTranslation(cameraSpacePosition.AsVector3); Vector3 lightDirection = (planet.Position - Position3.Zero).AsVector3; lightDirection.Normalize(); foreach (ModelMesh mesh in model.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.World = boneTransforms[mesh.ParentBone.Index] * cameraSpaceMatrix; effect.View = camera.ViewMatrix; effect.Projection = camera.ProjectionMatrix; effect.DirectionalLight0.Direction = -lightDirection; effect.EnableDefaultLighting(); effect.VertexColorEnabled = false; effect.PreferPerPixelLighting = true; } mesh.Draw(); } }
private void DrawSphereMask(Sphere sphere) { //GraphicsDevice.VertexDeclaration = positionNormalTextureHeight; GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.BlendState = BlendState.Opaque; GraphicsDevice.RasterizerState = currentRasterizerState; //CalculateProjection(); Camera camera = cameraManager.ActiveCamera; // get frustum camera position in planet space Position3 cameraPosition = camera.Position - sphere.Position; // calculate horizon angle, using camera position in planet space float horizonAngle = sphere.CalculateHorizonAngle(cameraPosition); DrawTerrainNodeMask(sphere.Front, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNodeMask(sphere.Back, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNodeMask(sphere.Left, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNodeMask(sphere.Right, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNodeMask(sphere.Top, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNodeMask(sphere.Bottom, cameraPosition, horizonAngle, sphere, camera); }
private void DrawTerrainNodeMask(TerrainNode terrainNode, Position3 cameraPosition, float horizonAngle, Sphere sphere, Camera camera) { // this can happen if a node was partially split if (terrainNode == null) return; // no need to draw or recurse any deeper if the node isn't visible if (CullTerrainNode(terrainNode, cameraPosition, horizonAngle, sphere, cameraManager.ActiveFrustumCamera)) { // if the node is being split then we can cancel the split if it's not visible terrainNode.CancelSplitting = true; return; } // we only draw leaf nodes, so recurse down until we find them, then draw them // a node that's splitting is considered a leaf node if (!terrainNode.Splitting && terrainNode.HasChildren) { DrawTerrainNodeMask(terrainNode.Children[0], cameraPosition, horizonAngle, sphere, camera); DrawTerrainNodeMask(terrainNode.Children[1], cameraPosition, horizonAngle, sphere, camera); DrawTerrainNodeMask(terrainNode.Children[2], cameraPosition, horizonAngle, sphere, camera); DrawTerrainNodeMask(terrainNode.Children[3], cameraPosition, horizonAngle, sphere, camera); } else { // get world space position by adding terrain node position to planet position Position3 worldSpacePosition = sphere.Position + terrainNode.Position; // translate to camera space by subtracting the camera position, scale by planet scale Position3 cameraSpacePosition = (worldSpacePosition - camera.Position) * sphere.Scale; Matrix cameraSpaceMatrix = sphere.ScaleMatrix * Matrix.CreateTranslation(cameraSpacePosition.AsVector3); Matrix worldViewProjectionMatrix = cameraSpaceMatrix * camera.ViewProjectionMatrix; // we're drawing in black, so we don't need to much information planetEffectMask.Parameters["WorldViewProj"].SetValue(worldViewProjectionMatrix); planetEffectMask.CurrentTechnique.Passes[0].Apply(); GraphicsDevice.SetVertexBuffer(terrainNode.VertexBuffer.VertexBuffer); GraphicsDevice.Indices = TerrainNodeIndexBuffer.Indices; GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, terrainNode.VertexBuffer.VertexCount, 0, TerrainNodeIndexBuffer.IndexCount / 3); } }
private void DrawSphere(Sphere sphere) { // get frustum camera position in planet space Camera frustumCamera = cameraManager.ActiveFrustumCamera; Position3 frustumCameraPosition = frustumCamera.Position - sphere.Position; // update sphere, allowing it to split sphere.Update(frustumCameraPosition, MathHelper.ToRadians(frustumCamera.FieldOfView)); //GraphicsDevice.VertexDeclaration = positionNormalTextureHeight; GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.BlendState = BlendState.Opaque; GraphicsDevice.RasterizerState = currentRasterizerState; CalculateProjection(sphere); Camera camera = mainCamera; // get frustum camera position in planet space Position3 cameraPosition = camera.Position - sphere.Position; // calculate horizon angle, using camera position in planet space float horizonAngle = sphere.CalculateHorizonAngle(cameraPosition); Globals.DrawLevel = 0; Globals.DrawCount = 0; Globals.HorizonCullCount = 0; Globals.FrustumCullCount = 0; DrawTerrainNode(sphere.Front, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNode(sphere.Back, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNode(sphere.Left, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNode(sphere.Right, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNode(sphere.Top, cameraPosition, horizonAngle, sphere, camera); DrawTerrainNode(sphere.Bottom, cameraPosition, horizonAngle, sphere, camera); }
private void DrawTerrainNode(TerrainNode terrainNode, Position3 cameraPosition, float horizonAngle, Sphere sphere, Camera camera) { // this can happen if a node was partially split if (terrainNode == null) return; // no need to draw or recurse any deeper if the node isn't visible if (CullTerrainNode(terrainNode, cameraPosition, horizonAngle, sphere, cameraManager.ActiveFrustumCamera)) { // if the node is being split then we can cancel the split if it's not visible terrainNode.CancelSplitting = true; return; } // we only draw leaf nodes, so recurse down until we find them, then draw them // a node that's splitting is considered a leaf node if (!terrainNode.Splitting && terrainNode.HasChildren) { DrawTerrainNode(terrainNode.Children[0], cameraPosition, horizonAngle, sphere, camera); DrawTerrainNode(terrainNode.Children[1], cameraPosition, horizonAngle, sphere, camera); DrawTerrainNode(terrainNode.Children[2], cameraPosition, horizonAngle, sphere, camera); DrawTerrainNode(terrainNode.Children[3], cameraPosition, horizonAngle, sphere, camera); } else { Globals.DrawCount++; // track max draw level if (terrainNode.Level > Globals.DrawLevel) Globals.DrawLevel = terrainNode.Level; // get world space position by adding terrain node position to planet position Position3 worldSpacePosition = sphere.Position + terrainNode.Position; // translate to camera space by subtracting the camera position, scale by planet scale Position3 cameraSpacePosition = (worldSpacePosition - camera.Position) * sphere.Scale; Matrix cameraSpaceMatrix = sphere.ScaleMatrix * Matrix.CreateTranslation(cameraSpacePosition.AsVector3); Matrix worldViewProjectionMatrix = cameraSpaceMatrix * camera.ViewProjectionMatrix; // get light position, translated to camera space, TODO : light position is currently in the solar system center Vector3 lightPosition = (Vector3)(camera.Position - new Position3(0, 0, 0)); // get light direction - "space" doesn't matter here since it's just a direction Vector3 lightDirection = (Vector3)(worldSpacePosition - new Position3(0, 0, 0)); lightDirection.Normalize(); // determine which shader to use double altitude = mainCamera.fLocalPosition.Length(); Effect effect; #if atmosphere if (altitude <= Constants.EarthAtmosphereRadius) effect = planetEffectBumpAtmosphere; else effect = planetEffectBumpSpace; #else effect = planetEffect; #endif effect.Parameters["WorldViewProj"].SetValue(worldViewProjectionMatrix); effect.Parameters["LightDirection"].SetValue(-lightDirection); if (effect == planetEffectTexture) { //effect.Parameters["Tint"].SetValue(new Vector4(1, 1, 1, 1)); effect.Parameters["DiffuseTexture"].SetValue(terrainNode.DiffuseTexture); } else if (effect != planetEffectBasic) { effect.Parameters["WorldMatrix"].SetValue(cameraSpaceMatrix); effect.Parameters["DiffuseTexture"].SetValue(terrainNode.DiffuseTexture); effect.Parameters["NormalTexture"].SetValue(terrainNode.NormalTexture); } /* if (terrainNode == tank.NodeUnderTank) effect.Parameters["Tint"].SetValue(new Vector4(1.0f, 0.5f, 0.5f, 1f)); else*/ effect.Parameters["Tint"].SetValue(new Vector4(1f, 1f, 1f, 1f)); // set up atmosphere parameters #if atmosphere groundFromSpace.ResolveParameters(effect); float cameraHeight = (float)(camera.Position - sphere.Position).Length(); Vector3 cameraPositionNode = (camera.Position - sphere.Position).AsVector3; Vector3 vLightDirection = (Vector3)(Position3.Zero - camera.Position); vLightDirection.Normalize(); effect.Parameters["PatchPosition"].SetValue(terrainNode.Position.AsVector3); effect.Parameters["v3CameraPos"].SetValue(cameraPositionNode); effect.Parameters["v3LightPos"].SetValue(vLightDirection); effect.Parameters["fCameraHeight"].SetValue(cameraHeight); effect.Parameters["fCameraHeight2"].SetValue(cameraHeight * cameraHeight); #endif effect.CurrentTechnique.Passes[0].Apply(); GraphicsDevice.SetVertexBuffer(terrainNode.VertexBuffer.VertexBuffer); GraphicsDevice.Indices = TerrainNodeIndexBuffer.Indices; GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, terrainNode.VertexBuffer.VertexCount, 0, TerrainNodeIndexBuffer.IndexCount / 3); //DrawNodeFrustum(terrainNode); } }
/// <summary> /// Determine if this node should be culled, either horizon or view frustum /// </summary> /// <param name="cameraPosition">Camera position in planet space</param> /// <param name="horizonAngle">Horizon angle in radians</param> /// <returns>Boolean indicating whether or not the node should be culled</returns> private bool CullTerrainNode(TerrainNode terrainNode, Position3 cameraPosition, float horizonAngle, Sphere sphere, Camera camera) { // don't cull away top level nodes if they're splitting if (terrainNode.Splitting && terrainNode.Level <= 2) return false; Position3 nodePosition; ///// horizon culling ///// if (!Constants.DisableHorizonCulling && horizonAngle != 0) { if (terrainNode.Level >= 2) { // get the camera position on the unit sphere Position3 unitCameraPosition = cameraPosition; unitCameraPosition.Normalize(); // get the planet node position on the unit sphere nodePosition = terrainNode.ClosestPosition; nodePosition.Normalize(); // get the node's angle relative to the camera float angle = (float)Math.Acos(Position3.Dot(unitCameraPosition, nodePosition)); // if it's over the horizon then cull the node if (angle > horizonAngle) { Globals.HorizonCullCount++; return true; } } } ///// view frustum culling ///// if (!Constants.DisableFrustumCulling) { // get node's camera space position nodePosition = (sphere.Position + terrainNode.Position) - camera.Position; // create a matrix for frustum culling Matrix M = sphere.ScaleMatrix * Matrix.CreateTranslation(nodePosition.AsVector3 * (float)sphere.Scale); // create bounding box transformed to camera space Vector3 MinV; Vector3 MaxV; MinV = Vector3.Transform(terrainNode.MinVertex, M); MaxV = Vector3.Transform(terrainNode.MaxVertex, M); BoundingBox B = new BoundingBox(MinV, MaxV); ContainmentType CT; // if it's not contained within the view frustum then cull the node camera.Frustum.Contains(ref B, out CT); if (CT == ContainmentType.Disjoint) { Globals.FrustumCullCount++; return true; } } // otherwise it's visible return false; }
private void CalculateProjection(Sphere sphere) { Camera C = cameraManager.ActiveCamera; double H = (sphere.Position - C.Position).Length(); double L = H; // H - 9.0; if (L < sphere.Radius + 10.0) // TODO : the distance here is important - might need to scale to planet size { C.NearClip = (float)(0.001 / sphere.Radius); C.FarClip = 20000.0f; C.UpdateProjectionMatrix(); } else if (L < sphere.Radius + 20.0) // TODO : the distance here is important - might need to scale to planet size { C.NearClip = (float)(0.01 / sphere.Radius); C.FarClip = 20000.0f; C.UpdateProjectionMatrix(); } else if (L < sphere.Radius + 50.0) // TODO : the distance here is important - might need to scale to planet size { C.NearClip = (float)(0.1 / sphere.Radius); C.FarClip = 20000.0f; C.UpdateProjectionMatrix(); } else if (L < sphere.Radius + 6000.0) { C.NearClip = 0.001f; // 0.000001f; // (float)(1.0 /* 30.0 */ / sphere.Radius); C.FarClip = 20000.0f; C.UpdateProjectionMatrix(); } else { C.NearClip = 0.001f; // (float)(0.1 /* 150.0 */ / sphere.Radius); C.FarClip = 20000.0f; C.UpdateProjectionMatrix(); } }
protected override void LoadContent() { WireFrame = new RasterizerState { CullMode = CullMode.CullCounterClockwiseFace, FillMode = FillMode.WireFrame, MultiSampleAntiAlias = true }; DepthNoWrite = new DepthStencilState { DepthBufferEnable = true, DepthBufferWriteEnable = false }; CullClockwiseSolid = new RasterizerState { CullMode = CullMode.CullClockwiseFace, FillMode = FillMode.Solid, MultiSampleAntiAlias = true }; CullClockwiseWireFrame = new RasterizerState { CullMode = CullMode.CullClockwiseFace, FillMode = FillMode.WireFrame, MultiSampleAntiAlias = true }; currentRasterizerState = RasterizerState.CullCounterClockwise; TerrainNodeDelegates.InitializeTerrainNodeDelegates(GraphicsDevice); TerrainNodeSplitManager.Initialize(this); spriteBatch = new SpriteBatch(GraphicsDevice); positionNormalTextureHeight = VertexPositionNormalTextureHeight.VertexDeclaration; // new VertexDeclaration(GraphicsDevice, VertexPositionNormalTextureHeight.VertexElements); positionColor = VertexPositionColor.VertexDeclaration; // new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements); dirtTexture = Content.Load<Texture2D>(@"Textures\dirt_01"); grassTexture = Content.Load<Texture2D>(@"Textures\grass_01"); sunGlowTexture = Content.Load<Texture2D>(@"textures\sun_glow"); planetEffectMask = Content.Load<Effect>(@"Effects\PlanetMask"); planetEffectBasic = Content.Load<Effect>(@"Effects\PlanetBasic"); planetEffectTexture = Content.Load<Effect>(@"Effects\PlanetBasicTexture"); planetEffectBump = Content.Load<Effect>(@"Effects\PlanetBump"); planetEffectBumpSpace = Content.Load<Effect>(@"Effects\PlanetBumpFromSpace"); planetEffectBumpAtmosphere = Content.Load<Effect>(@"Effects\PlanetBumpFromAtmosphere"); planetEffectBumpMaps = Content.Load<Effect>(@"Effects\PlanetBumpMaps"); planetEffectAtmosphereSpace = Content.Load<Effect>(@"Effects\AtmosphereBasicSpace"); planetEffectAtmosphereAtmosphere = Content.Load<Effect>(@"Effects\AtmosphereBasicAtmosphere"); // select initial render effect planetEffect = null; SelectNextRenderMode(); #if sun sunBasicEffect = Content.Load<Effect>(@"Effects\SunBasic"); #endif debugTextFont = Content.Load<SpriteFont>(@"Fonts\DebugTextFont"); // initialize terrain node index buffer TerrainNodeIndexBuffer.CreateIndices(GraphicsDevice, Constants.PatchWidth, Constants.PatchHeight); // create test sphere Position3 p = new Position3(1, 1, 1); p.Normalize(); p *= 149597870.691; // earth -> sun distance #if planet sphere = new Sphere(p, Constants.EarthRadius, true, TerrainNodeDelegates.CreatePositionPlanet, TerrainNodeDelegates.CreateTerrainNodeVertexBuffer, false); #endif #if atmosphere // create test atmosphere atmosphere = new Sphere(p, Constants.EarthAtmosphereRadius, true, TerrainNodeDelegates.CreatePositionSphere, TerrainNodeDelegates.CreateTerrainNodeVertexBuffer, true); // create atmosphere shaders groundFromSpace = new AtmosphereShader(); #endif #if sun // create sun sun = new Sphere(Position3.Zero, Constants.SunRadius, false, TerrainNodeDelegates.CreatePositionSphere, TerrainNodeDelegates.CreateTerrainNodeVertexBuffer, true); #endif testEffect = new BasicEffect(GraphicsDevice); testSquare = GenerateSquare(25, 0, Color.LawnGreen); mainCamera.AttachedPosition = p; // camera is attached to the sphere mainCamera.UpdateCamera(0); mainCamera.LookAt(Position3.Zero); if (frustumCamera != null) { frustumCamera.AttachedPosition = p; // camera is attached to the sphere frustumCamera.UpdateCamera(0); frustumCamera.LookAt(Position3.Zero); } // space dome spaceDome = new SpaceDome(Constants.EarthRadius * 3.0, Constants.EarthRadius * 100.0); spaceDome.LoadContent(this); tank = new Tank(); tank.LoadContent(this); tank.Movement.AttachedPosition = p; tank.Movement.UpdateMovement(0); sunGlowEffect = Content.Load<Effect>(@"effects\billboard"); billboardVertexDeclaration = VertexPositionTexture.VertexDeclaration; // new VertexDeclaration(this.GraphicsDevice, VertexPositionTexture.VertexElements); GenerateSunVertices(); #if lensflare // lens flare lensFlare = new LensFlareComponent(); lensFlare.LoadContent(this); lensFlare.LightPosition = Position3.Zero; lensFlare.MaskMode = true; #endif }
private void DrawAtmosphere(Sphere sphere) { // get frustum camera position in planet space Camera frustumCamera = cameraManager.ActiveFrustumCamera; Position3 frustumCameraPosition = frustumCamera.Position - sphere.Position; // update sphere, allowing it to split sphere.Update(frustumCameraPosition, MathHelper.ToRadians(frustumCamera.FieldOfView)); if (fillMode == FillMode.Solid) GraphicsDevice.RasterizerState = CullClockwiseSolid; else GraphicsDevice.RasterizerState = CullClockwiseWireFrame; GraphicsDevice.DepthStencilState = DepthNoWrite; GraphicsDevice.BlendState = BlendState.NonPremultiplied; //CalculateProjection(sphere); Camera camera = mainCamera; // get frustum camera position in planet space Position3 cameraPosition = camera.Position - sphere.Position; // calculate horizon angle, using camera position in planet space float horizonAngle = sphere.CalculateHorizonAngle(cameraPosition); Globals.DrawLevel = 0; Globals.DrawCount = 0; Globals.HorizonCullCount = 0; Globals.FrustumCullCount = 0; DrawAtmosphereNode(sphere.Front, cameraPosition, horizonAngle, sphere, camera); DrawAtmosphereNode(sphere.Back, cameraPosition, horizonAngle, sphere, camera); DrawAtmosphereNode(sphere.Left, cameraPosition, horizonAngle, sphere, camera); DrawAtmosphereNode(sphere.Right, cameraPosition, horizonAngle, sphere, camera); DrawAtmosphereNode(sphere.Top, cameraPosition, horizonAngle, sphere, camera); DrawAtmosphereNode(sphere.Bottom, cameraPosition, horizonAngle, sphere, camera); }
public void HandleInput(GameTime gameTime, Sphere planet, IInputManager inputManager) { double height; Vector3 normal; Triangle = null; Position3 oldPosition = movement.fLocalPosition; movement.Update(gameTime); NodeUnderTank = planet.FindNodeUnderPosition(movement.Position - planet.Position, out Triangle, out height, out normal); if (NodeUnderTank == null) { if (Globals.Game.Graphics.IsFullScreen) Globals.Game.Graphics.ToggleFullScreen(); throw new Exception("Unable to find node under tank."); } else { Position3 p = movement.Position - planet.Position; p.Normalize(); movement.Position = planet.Position + (p * height); } movement.OrientUp(normal); // now we need to roll the tank's wheels "forward." to do this, we'll // calculate how far they have rolled, and from there calculate how much // they must have rotated. float distanceMoved = (float)Position3.Distance(oldPosition, movement.fLocalPosition); float theta = distanceMoved / TankWheelRadius * 1000.0f; int rollDirection = 0; Vector3 n0 = movement.fLinearVelocity; n0.Normalize(); Vector3 n1 = movement.fForward; if (Vector3.Dot(n0, n1) < 0) rollDirection = 1; else rollDirection = -1; wheelRollMatrix *= Matrix.CreateRotationX(theta * rollDirection); }