public TerrainNodeSplitItem(TerrainNode parentNode, int childIndex, TerrainNodeBounds bounds, bool isSphere) { ParentNode = parentNode; ChildIndex = childIndex; Bounds = bounds; IsSphere = isSphere; }
public void Initialize() { front = CreateRootTerrainNode(Face.Front); back = CreateRootTerrainNode(Face.Back); left = CreateRootTerrainNode(Face.Left); right = CreateRootTerrainNode(Face.Right); top = CreateRootTerrainNode(Face.Top); bottom = CreateRootTerrainNode(Face.Bottom); }
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 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 CountTerrainNodes(TerrainNode node) { Globals.NodeCount++; if (node.HasChildren) { if (node.Children[0] != null) CountTerrainNodes(node.Children[0]); if (node.Children[1] != null) CountTerrainNodes(node.Children[1]); if (node.Children[2] != null) CountTerrainNodes(node.Children[2]); if (node.Children[3] != null) CountTerrainNodes(node.Children[3]); } }
private void DrawNodeFrustum(TerrainNode node) { // calculate the width of the patch; first we need a vertex on both sides of the patch, in planet space Vector3 p1 = node.Position.AsVector3 + node.VertexBuffer.Vertices[1 * 33 + 1].Position; Vector3 p2 = node.Position.AsVector3 + node.VertexBuffer.Vertices[1 * 33 + 33].Position; // need to move them to the sphere radius p1.Normalize(); p1 *= (float)sphere.Radius; p2.Normalize(); p2 *= (float)sphere.Radius; // now we can calculate the opposite and hypotenuse lengths float opposite = Vector3.Distance(p1, p2) * 0.5f; // width is distance between p1 and p2, and we need just half of it float hypotenuse = (float)sphere.Radius; // once we have the width we can calculate the field of view float fieldOfView = (float)(2.0 * Math.Asin(opposite / hypotenuse)); // now we can create the view and projection matrixes Matrix view = Matrix.CreateLookAt(Vector3.Zero, node.Position.AsVector3, Vector3.Up); Matrix projection = Matrix.CreatePerspectiveFieldOfView(fieldOfView, 1.0f, 1.0f, 9000.0f); // which then lets us create the bounding frustum BoundingFrustum frustum = new BoundingFrustum(view * projection); Vector3[] corners = frustum.GetCorners(); // draw lines between corners VertexPositionColor[] vertices = new VertexPositionColor[24]; Position3 p = Position3.Zero; Position3 p0 = node.Position; p0.Normalize(); p0 *= 9000.0f; // line 1 vertices[0] = new VertexPositionColor(corners[0], Color.White); vertices[1] = new VertexPositionColor(corners[1], Color.White); // line 2 vertices[2] = new VertexPositionColor(corners[1], Color.White); vertices[3] = new VertexPositionColor(corners[2], Color.White); // line 3 vertices[4] = new VertexPositionColor(corners[2], Color.White); vertices[5] = new VertexPositionColor(corners[3], Color.White); // line 4 vertices[6] = new VertexPositionColor(corners[3], Color.White); vertices[7] = new VertexPositionColor(corners[0], Color.White); // line 5 vertices[8] = new VertexPositionColor(corners[4], Color.White); vertices[9] = new VertexPositionColor(corners[5], Color.White); // line 6 vertices[10] = new VertexPositionColor(corners[5], Color.White); vertices[11] = new VertexPositionColor(corners[6], Color.White); // line 7 vertices[12] = new VertexPositionColor(corners[6], Color.White); vertices[13] = new VertexPositionColor(corners[7], Color.White); // line 8 vertices[14] = new VertexPositionColor(corners[7], Color.White); vertices[15] = new VertexPositionColor(corners[4], Color.White); // line 9 vertices[16] = new VertexPositionColor(corners[0], Color.White); vertices[17] = new VertexPositionColor(corners[4], Color.White); // line 10 vertices[18] = new VertexPositionColor(corners[1], Color.White); vertices[19] = new VertexPositionColor(corners[5], Color.White); // line 11 vertices[20] = new VertexPositionColor(corners[2], Color.White); vertices[21] = new VertexPositionColor(corners[6], Color.White); // line 12 vertices[22] = new VertexPositionColor(corners[3], Color.White); vertices[23] = new VertexPositionColor(corners[7], Color.White); GraphicsDevice device = graphics.GraphicsDevice; // TODO : device.RenderState.CullMode = CullMode.None; // get world space position for drawing the frustum Position3 worldSpacePosition = sphere.Position; // translate to camera space by subtracting the camera position, scale by planet scale Position3 cameraSpacePosition = (worldSpacePosition - mainCamera.Position) * sphere.Scale; // create world matrix Matrix cameraSpaceMatrix = sphere.ScaleMatrix * Matrix.CreateTranslation(cameraSpacePosition.AsVector3); testEffect.Projection = mainCamera.ProjectionMatrix; testEffect.World = cameraSpaceMatrix; testEffect.View = mainCamera.ViewMatrix; //testEffect.EnableDefaultLighting(); testEffect.VertexColorEnabled = true; testEffect.CurrentTechnique.Passes[0].Apply(); GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices, 0, 12); }
private void DrawNodeBoundingBox(TerrainNode node) { // calculate the width of the patch; first we need a vertex on both sides of the patch, in planet space Vector3 p1 = node.Position.AsVector3 + node.MinVertex; Vector3 p2 = node.Position.AsVector3 + node.MaxVertex; // which then lets us create the bounding frustum BoundingBox box = new BoundingBox(p1, p2); Vector3[] corners = box.GetCorners(); // draw lines between corners VertexPositionColor[] vertices = new VertexPositionColor[24]; // line 1 vertices[0] = new VertexPositionColor(corners[0], Color.White); vertices[1] = new VertexPositionColor(corners[1], Color.White); // line 2 vertices[2] = new VertexPositionColor(corners[1], Color.White); vertices[3] = new VertexPositionColor(corners[2], Color.White); // line 3 vertices[4] = new VertexPositionColor(corners[2], Color.White); vertices[5] = new VertexPositionColor(corners[3], Color.White); // line 4 vertices[6] = new VertexPositionColor(corners[3], Color.White); vertices[7] = new VertexPositionColor(corners[0], Color.White); // line 5 vertices[8] = new VertexPositionColor(corners[4], Color.White); vertices[9] = new VertexPositionColor(corners[5], Color.White); // line 6 vertices[10] = new VertexPositionColor(corners[5], Color.White); vertices[11] = new VertexPositionColor(corners[6], Color.White); // line 7 vertices[12] = new VertexPositionColor(corners[6], Color.White); vertices[13] = new VertexPositionColor(corners[7], Color.White); // line 8 vertices[14] = new VertexPositionColor(corners[7], Color.White); vertices[15] = new VertexPositionColor(corners[4], Color.White); // line 9 vertices[16] = new VertexPositionColor(corners[0], Color.White); vertices[17] = new VertexPositionColor(corners[4], Color.White); // line 10 vertices[18] = new VertexPositionColor(corners[1], Color.White); vertices[19] = new VertexPositionColor(corners[5], Color.White); // line 11 vertices[20] = new VertexPositionColor(corners[2], Color.White); vertices[21] = new VertexPositionColor(corners[6], Color.White); // line 12 vertices[22] = new VertexPositionColor(corners[3], Color.White); vertices[23] = new VertexPositionColor(corners[7], Color.White); GraphicsDevice device = graphics.GraphicsDevice; device.RasterizerState = RasterizerState.CullNone; //device.VertexDeclaration = positionColor; // get world space position for drawing the frustum Position3 worldSpacePosition = sphere.Position; // translate to camera space by subtracting the camera position, scale by planet scale Position3 cameraSpacePosition = (worldSpacePosition - mainCamera.Position) * sphere.Scale; // create world matrix Matrix cameraSpaceMatrix = sphere.ScaleMatrix * Matrix.CreateTranslation(cameraSpacePosition.AsVector3); testEffect.Projection = mainCamera.ProjectionMatrix; testEffect.World = cameraSpaceMatrix; testEffect.View = mainCamera.ViewMatrix; //testEffect.EnableDefaultLighting(); testEffect.VertexColorEnabled = true; testEffect.CurrentTechnique.Passes[0].Apply(); GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices, 0, 12); }
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); }
public static void QueueNodeSplit(TerrainNode parentNode, int childIndex, TerrainNodeBounds bounds, bool isSphere) { QueueNodeSplit(new TerrainNodeSplitItem(parentNode, childIndex, bounds, isSphere)); }