/// <summary> /// Gets the point of the terrain that the mouse cursor is currently casting a ray to. /// Returns null if the ray range is surpassed. /// </summary> /// <param name="projectionMatrix"> /// The projection matrix. /// </param> /// <param name="viewMatrix"> /// The view matrix. /// </param> /// <param name="terrain"> /// The terrain. /// </param> /// <returns> /// The <see cref="Vector3?"/> /// </returns> public static Vector3 GetTerrainPoint( GraphicsDevice device, Matrix projectionMatrix, Matrix viewMatrix, ITerrain terrain) { Ray ray = CastRay(device, projectionMatrix, viewMatrix); Vector3 currentTerrainPoint = BinarySplitSearch(0, Range, ray, terrain, BinarySplits); if (!IsIntersectionInRange(0, Range, ray, terrain)) { currentTerrainPoint.Y = terrain.GetWorldHeight(currentTerrainPoint.X, currentTerrainPoint.Z); } return(currentTerrainPoint); }
/// <summary> /// Affects entities with gravity /// </summary> /// <param name="gameTime"> /// The game time. /// </param> /// <param name="terrain"> /// The terrain. /// </param> public void GravityAffect(GameTime gameTime, ITerrain terrain) { float height = terrain.GetWorldHeight(this.Position.X, this.Position.Z); if (this.Position.Y < height) { this.Position = new Vector3(this.Position.X, height, this.Position.Z); } else if (this.Position.Y > height || this.Weight < 0) { float y = this.Position.Y - (this.Weight * (float)gameTime.ElapsedGameTime.TotalSeconds); y = y < height ? height : y; this.Position = new Vector3(this.Position.X, y, this.Position.Z); } }
/// <summary> /// Custom split binary search implementation. /// Returns the closest point projected from the mouse cursor /// to the terrain surface. /// </summary> /// <param name="start"> /// The start. /// </param> /// <param name="finish"> /// The finish. /// </param> /// <param name="ray"> /// The ray. /// </param> /// <param name="terrain"> /// The terrain. /// </param> /// <param name="binarySplits"> /// The number of splits for the binary search. /// </param> /// <returns> /// The <see cref="Vector3"/>. /// </returns> private static Vector3 BinarySplitSearch( float start, float finish, Ray ray, ITerrain terrain, uint binarySplits = 1) { binarySplits = binarySplits == 0 ? 1 : binarySplits; Vector3[] endpoints = new Vector3[binarySplits]; // The section size of each split float sectionSize = binarySplits > 0 ? (finish - start) / binarySplits : finish - start; bool found = false; int iterations = 0; for (uint i = 0; i < binarySplits; i++) { iterations++; float s = i * sectionSize; float f = (i + 1) * sectionSize; bool intersectionFound = false; Vector3 endpoint = BinarySearch(s, f, ray, terrain, out intersectionFound); if (intersectionFound) { found = true; float height = terrain.GetWorldHeight(endpoint.X, endpoint.Z); Vector3 vertice = new Vector3(endpoint.X, height, endpoint.Z); float distance = Vector3.Distance(endpoint, vertice); // If the point found is in the seamless range // skip the rest of the search and return it if (distance <= SeamlessDistance) { return(endpoint); } } endpoints[i] = endpoint; } // If no point was in intersection range with terrain during search // returns furthest point in range if (!found) { Vector3 furthestEndpoint = BinarySearch(start, finish, ray, terrain, out found); return(furthestEndpoint); } // Calculates the best result from all splits // Only reachable if no seamless result is found during split search Vector3 winner = endpoints[0]; foreach (var endpoint in endpoints) { float winnerHeight = terrain.GetWorldHeight(winner.X, winner.Z); Vector3 winnerVertice = new Vector3(winner.X, winnerHeight, winner.Z); float winnerDistance = Vector3.Distance(winner, winnerVertice); float height = terrain.GetWorldHeight(endpoint.X, endpoint.Z); Vector3 currentVertice = new Vector3(endpoint.X, height, endpoint.Z); float currentDistance = Vector3.Distance(endpoint, currentVertice); if (currentDistance < winnerDistance) { winner = endpoint; } } return(winner); }
/// <summary> /// Checks if the specified point is under the terrain /// </summary> /// <param name="testPoint"> /// The test point. /// </param> /// <param name="terrain"> /// The terrain. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> private static bool IsUnderGround(Vector3 testPoint, ITerrain terrain) { float height = terrain.GetWorldHeight(testPoint.X, testPoint.Z); return(testPoint.Y < height); }
/// <summary> /// Snaps the entity Y position to the terrain Y at the specified point /// </summary> /// <param name="terrain"> /// The terrain. /// </param> public void SnapToTerrainHeight(ITerrain terrain) { float height = terrain.GetWorldHeight(this.Position.X, this.Position.Z); this.Position = new Vector3(this.Position.X, height, this.Position.Z); }