protected void ClampPosition() { Vector3 pos = transform.position; pos.x = LC_Math.Clamp(pos.x, MinPosition.x, MaxPosition.x); pos.y = LC_Math.Clamp(pos.y, MinPosition.y, MaxPosition.y); pos.z = LC_Math.Clamp(pos.z, MinPosition.z, MaxPosition.z); transform.position = pos; }
protected override void SplitAndMergeMesh(LC_Chunk <WorldCell> chunk) { List <LC_Math.QuadTreeSector> sectors = LC_Math.SplitAndMerge( (x, z) => { return(chunk.Cells[x, z].RealHeight); }, (x, y) => { return(x == y); }, ChunkSize, true); foreach (LC_Math.QuadTreeSector sector in sectors) { CreateElementMesh(sector.Initial, sector.Final, chunk); } }
/// <summary> /// Compute the heights map using LC_Math.PerlinNoiseMap. /// </summary> /// <param name="chunkPos">Position of the chunk</param> /// <returns></returns> protected virtual float[,] CreateChunkHeightsMap(Vector2Int chunkPos) { return(LC_Math.PerlinNoiseMap( new Vector2Int(ChunkSize + 3, ChunkSize + 3), // +1 for top-right edges and +2 for normals computation Seed, Octaves, Persistance, Lacunarity, new Vector2(0, MaxHeight), HeightsDivisor, (chunkPos.x - 1) * ChunkSize, // -1 for normals computation (get neighbour chunk edge heights) (chunkPos.y - 1) * ChunkSize, // -1 for normals computation (get neighbour chunk edge heights) true)); }
/// <summary> /// <para>Generates a new terrain around the reference with square shape.</para> /// <para>If DynamicChunkLoading is enabled the square shape is substituted using the ChunkRenderDistance.</para> /// <para>If ParallelChunkLoading is enabled the chunks around the reference need to be build at Update method.</para> /// </summary> protected virtual void IniTerrain() { // Always load the current reference chunk LoadChunk(ReferenceChunkPos, true); // Load the other chunks foreach (Vector2Int chunkPos in LC_Math.AroundPositions(ReferenceChunkPos, ChunkRenderDistance)) { LoadChunk(chunkPos); } IsGenerated = true; }
public virtual Cell GetCell(Vector2Int terrainPos, bool isForMap = false) { Cell cell = null; Chunk chunk = GetChunk(terrainPos); // Check ChunksForMap if (chunk == null && isForMap) { Vector2Int chunkPos = TerrainPosToChunk(terrainPos); ChunksForMap.TryGetValue(chunkPos, out chunk); } // Get cell from the chunk if it exists if (chunk != null) { Vector2Int posInChunk = new Vector2Int(LC_Math.Mod(terrainPos.x, ChunkSize), LC_Math.Mod(terrainPos.y, ChunkSize)); cell = chunk.Cells[posInChunk.x, posInChunk.y]; } return(cell); }
/// <summary> /// <para>Calculates a pseudo-random direction based on lastDirection and a random value.</para> /// <para>If the random value is greater than sameDirectionProbability it will compute a diferent direction.</para> /// <para>For that, it will test alternate directions trying to minimize the turn from the lastDirection.</para> /// <para>If no direction is possible, returns Vector2Int.zero.</para> /// </summary> /// <param name="origin">Original position.</param> /// <param name="lastDirection">Last direction. Must be a normalized vector.</param> /// <param name="randGenerator">Random generator for the direction.</param> /// <param name="sameDirectionProbability">Probability of conserve the lastDirection.</param> /// <param name="isPositionAccessible">>Method to check if a position is accessible.</param> /// <returns>The new direction or, if no one is possible, Vector2Int.zero.</returns> public static Vector2Int PseudorandomDirection(Vector2Int origin, Vector2Int lastDirection, System.Random randGenerator, float sameDirectionProbability, IsPositionAccessible isPositionAccessible) { Vector2Int nextDirection = Vector2Int.zero; lastDirection = lastDirection.TransformToDirection(); float sameDirectionProb = Mathf.Clamp(sameDirectionProbability, 0, 100); bool useSameDirection = RandomDouble(randGenerator, 0, 100) <= sameDirectionProb; // If is possible continue in the same direction ( not 0,0 ) if (useSameDirection && !lastDirection.Equals(Vector2Int.zero) && IsPossibleDirection(origin, lastDirection, isPositionAccessible)) { nextDirection = lastDirection; } // Search a new random direction different of last else { int lastDirectionIdx = EightDirections2D.IndexOf(lastDirection); // If any previous direction is possible, assign one random if (lastDirectionIdx == -1) { lastDirectionIdx = randGenerator.Next(0, EightDirections2D.Count); } // Check the possible directions incrementing the offset relative to lastDirection int idx; bool turnRight; Vector2Int possibleDirection; for (int offset = 1; offset < EightDirections2D.Count / 2 && nextDirection == Vector2Int.zero; offset++) { turnRight = randGenerator.Next(0, 2) == 0; idx = LC_Math.Mod(lastDirectionIdx + (turnRight ? offset : -offset), EightDirections2D.Count); possibleDirection = EightDirections2D[idx]; if (IsPossibleDirection(origin, possibleDirection, isPositionAccessible)) { nextDirection = possibleDirection; } else { idx = LC_Math.Mod(lastDirectionIdx + (!turnRight ? offset : -offset), EightDirections2D.Count); possibleDirection = EightDirections2D[idx]; if (IsPossibleDirection(origin, possibleDirection, isPositionAccessible)) { nextDirection = possibleDirection; } } } // If any other direction isn't possible, check the opposite of last direction if (nextDirection == Vector2Int.zero) { possibleDirection = lastDirection * -1; if (IsPossibleDirection(origin, possibleDirection, isPositionAccessible)) { nextDirection = possibleDirection; } } } return(nextDirection); }
/// <summary> /// <para>Compute the normals for each vertex of the mesh, checking the edges cells of other chunks.</para> /// <para>This process is needed to avoid illumination differences between contiguous cells of different chunks (seams).</para> /// </summary> /// <param name="chunk"></param> protected virtual void ComputeNormals(Chunk chunk) { chunk.Normals = new Vector3[(ChunkSize + 1) * (ChunkSize + 1)]; int i, triangleIdx, x, z; Vector2Int vertexTerrainPos; Vector3 a, b, c, normal; // Normal for each vertex for (i = 0; i < chunk.Vertices.Count; i++) { LC_Math.IndexToCoords(i, ChunkSize + 1, out x, out z); // If isn't edge if (x < ChunkSize && z < ChunkSize) { triangleIdx = LC_Math.CoordsToIndex(x, z, ChunkSize) * 6; CalculateTrianglesNormals(triangleIdx, chunk); CalculateTrianglesNormals(triangleIdx + 3, chunk); } // If is edge if (x == 0 || z == 0 || x >= ChunkSize || z >= ChunkSize) { vertexTerrainPos = chunk.ChunkPosToTerrain(new Vector2Int(x, z)); a = chunk.Vertices[i]; if (x == 0 || z == 0) { b = TerrainPosToReal(vertexTerrainPos.x - 1, chunk.HeightsMap[x, z + 1], vertexTerrainPos.y); // x - 1, z c = TerrainPosToReal(vertexTerrainPos.x - 1, chunk.HeightsMap[x, z], vertexTerrainPos.y - 1); // x - 1, z - 1 normal = -Vector3.Cross(b - a, c - a); chunk.Normals[i] += normal; if (x != 0) { chunk.Normals[i - (ChunkSize + 1)] += normal; } b = c; // x - 1, z - 1 c = TerrainPosToReal(vertexTerrainPos.x, chunk.HeightsMap[x + 1, z], vertexTerrainPos.y - 1); // x, z - 1 normal = -Vector3.Cross(b - a, c - a); chunk.Normals[i] += normal; if (z != 0) { chunk.Normals[i - 1] += normal; } } if (x == ChunkSize || z == ChunkSize) { b = TerrainPosToReal(vertexTerrainPos.x + 1, chunk.HeightsMap[x + 2, z + 1], vertexTerrainPos.y); // x + 1, z c = TerrainPosToReal(vertexTerrainPos.x + 1, chunk.HeightsMap[x + 2, z + 2], vertexTerrainPos.y + 1); // x + 1, z + 1 normal = -Vector3.Cross(b - a, c - a); chunk.Normals[i] += normal; if (x < ChunkSize) { chunk.Normals[i + (ChunkSize + 1)] += normal; } b = c; // x + 1, z + 1 c = TerrainPosToReal(vertexTerrainPos.x, chunk.HeightsMap[x + 1, z + 2], vertexTerrainPos.y + 1); // x, z + 1 normal = -Vector3.Cross(b - a, c - a); chunk.Normals[i] += normal; if (z < ChunkSize) { chunk.Normals[i + 1] += normal; } } } } for (i = 0; i < chunk.Normals.Length; i++) { chunk.Normals[i].Normalize(); } }