// face space method, using ray intersection in planet space public TerrainNode FindNodeUnderPosition(Position3 position, out Vector3[] triangle, out double height, out Vector3 normal) { triangle = null; height = 0; normal = Vector3.Zero; Position3 originalPosition = position; // normalize the position so it points out from the planet center towards the actual position position.Normalize(); // get the direction vector Vector3 direction = position.AsVector3; // get a position 1km under the planet surface position *= radius - 1; // get ray from position to planet center, translate to patch space Ray ray = new Ray((position - this.Position).AsVector3, direction); Vector3 p1 = MinVertex; Vector3 p2 = MaxVertex; // first do a bounding box/ray intersection test BoundingBox box = new BoundingBox(MinVertex, MaxVertex); // if the ray doesn't intersect the bounding box then it can't intersect terrain node if (ray.Intersects(box) == null) return null; // we have a potential match - do mesh collision to be sure Vector3[] t = this.GetHeightAndNormal(originalPosition, out height, out normal); // if no mesh collision then we're done if (t == null) return null; // if the position is over this node and this is a leaf node then we have what we want if (!HasChildren) { triangle = t; return this; } // otherwise we want to recurse into the children to find the leaf node for (int i = 0; i < children.Length; i++) { TerrainNode node = children[i].FindNodeUnderPosition(position, out triangle, out height, out normal); if (node != null) return node; } // if we get here then we have a problem - the position is allegedly above this node, which means // it should also be above one of this node's children, but we didn't find a child // return this; if (Globals.Game.Graphics.IsFullScreen) Globals.Game.Graphics.ToggleFullScreen(); throw new Exception("Unable to find leaf node."); }
public Vector3[] GetHeightAndNormal(Position3 position, out double height, out Vector3 normal) { // we know position is over this node, now we need to find which triangle within the node // normalize the position so it points out from the planet center towards the actual position position.Normalize(); // get the direction vector Position3 direction3 = position; Vector3 direction = position.AsVector3; // get a position 1km under the planet surface, in planet space position *= radius - 1; // translate to patch space position -= this.Position; // get ray from position to planet center, translate to patch space Ray ray = new Ray(position.AsVector3, direction); float? intersection = null; float u = 0; float v = 0; Vector3 v0 = Vector3.Zero; Vector3 v1 = Vector3.Zero; Vector3 v2 = Vector3.Zero; Vector3 v4 = Vector3.Zero; Vector3 n0 = Vector3.Zero; Vector3 n1 = Vector3.Zero; Vector3 n2 = Vector3.Zero; Vector3 n4 = Vector3.Zero; int triangleIndex = 0; int quadIndex = 0; int index; int y; int x; // now we need to loop through each triangle and see if the ray intersects for (int i = 0; i < TerrainNodeIndexBuffer.IndexCount; i += 3) { index = TerrainNodeIndexBuffer.IndexData[i]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v0 = vertexBuffer.Vertices[index].Position; n0 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[i + 1]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v1 = vertexBuffer.Vertices[index].Position; n1 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[i + 2]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v2 = vertexBuffer.Vertices[index].Position; n2 = vertexBuffer.Vertices[index].Normal; Tools.RayIntersectsTriangle(ref ray, ref v0, ref v1, ref v2, out intersection, out u, out v); if (intersection != null) { triangleIndex = i; quadIndex = i; if (quadIndex % 6 != 0) quadIndex -= 3; break; } } Globals.TriangleIndex = triangleIndex; if (intersection == null) { height = radius; normal = direction; return null; } // Now that we've calculated the indices of the corners of our cell, and // where we are in that cell, we'll use bilinear interpolation to calculuate // our height. This process is best explained with a diagram, so please see // the accompanying doc for more information. // First, calculate the heights on the bottom and top edge of our cell by // interpolating from the left and right sides. // get the vertices for the quad containing this triangle index = TerrainNodeIndexBuffer.IndexData[quadIndex + 0]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v0 = vertexBuffer.Vertices[index].Position; n0 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[quadIndex + 1]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v1 = vertexBuffer.Vertices[index].Position; n1 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[quadIndex + 2]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v2 = vertexBuffer.Vertices[index].Position; n2 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[quadIndex + 4]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v4 = vertexBuffer.Vertices[index].Position; n4 = vertexBuffer.Vertices[index].Normal; /* Vector3 top, bottom, p, topNormal, bottomNormal, n; if (triangleIndex % 6 == 0) { // we're in triangle A - the top left triangle top = Vector3.Lerp(v0, v1, u); bottom = Vector3.Lerp(v2, v4, u); p = Vector3.Lerp(top, bottom, v); topNormal = Vector3.Lerp(n0, n1, u); bottomNormal = Vector3.Lerp(n2, n4, u); n = Vector3.Lerp(topNormal, bottomNormal, v); n.Normalize(); } else { // we're in triangle B - the bottom right triangle // this means u and v are positions within *that* triangle, so we need to do our lerping differently // we're in triangle A - the top left triangle // TODO : except u and v are acting very odd! they don't seem to be correct // let's try getting the quad all at once, then doing the Ray check on each triangle in the quad top = Vector3.Lerp(v0, v1, u); bottom = Vector3.Lerp(v2, v4, u); p = Vector3.Lerp(top, bottom, v); topNormal = Vector3.Lerp(n0, n1, u); bottomNormal = Vector3.Lerp(n2, n4, u); n = Vector3.Lerp(topNormal, bottomNormal, v); n.Normalize(); } */ // intersection gives us the length on the ray of the exact intersection position, // which gives us the exact height value to use Position3 vp = this.Position + (position + (direction3 * (double)intersection)); height = vp.Length(); // but now, how do we get the normal from that? let's start with just averaging the normals normal = (n0 + n1 + n2 + n4) * 0.25f; Vector3[] result = new Vector3[5]; result[0] = v0; result[1] = v1; result[2] = v2; result[3] = v4; result[4] = (vp - this.Position).AsVector3; return result; }
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 }