Files
Fishing2/Packages/com.jbooth.microverse/Scripts/TerrainUtil.cs
2025-06-09 23:23:13 +08:00

222 lines
7.8 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
namespace JBooth.MicroVerseCore
{
public class TerrainUtil
{
public static Mesh GenerateMesh(int segments, Vector3 tsize)
{
float size = tsize.x;
var mesh = new Mesh();
mesh.name = "TerrainProxy";
mesh.hideFlags = HideFlags.DontSave;
int hCount2 = segments + 1;
int vCount2 = segments + 1;
int numTriangles = segments * segments * 6;
int numVertices = hCount2 * vCount2;
Vector3[] vertices = new Vector3[numVertices];
Vector2[] uvs = new Vector2[numVertices];
int[] triangles = new int[numTriangles];
int index = 0;
float uvFactorX = 1.0f / segments;
float uvFactorY = 1.0f / segments;
float scaleX = size / segments;
float scaleY = size / segments;
for (float y = 0.0f; y < vCount2; y++)
{
for (float x = 0.0f; x < hCount2; x++)
{
vertices[index] = new Vector3(x * scaleX, 0, y * scaleY);
uvs[index++] = new Vector2(x * uvFactorX, y * uvFactorY);
}
}
index = 0;
for (int y = 0; y < segments; y++)
{
for (int x = 0; x < segments; x++)
{
triangles[index] = (y * hCount2) + x;
triangles[index + 1] = ((y + 1) * hCount2) + x;
triangles[index + 2] = (y * hCount2) + x + 1;
triangles[index + 3] = ((y + 1) * hCount2) + x;
triangles[index + 4] = ((y + 1) * hCount2) + x + 1;
triangles[index + 5] = (y * hCount2) + x + 1;
index += 6;
}
}
mesh.vertices = vertices;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.bounds = new Bounds(new Vector3(tsize.x * 0.5f, tsize.y * 0.5f, tsize.z * 0.5f), tsize);
mesh.RecalculateTangents();
return mesh;
}
public static Bounds ComputeTerrainBounds(Terrain terrain)
{
if (terrain == null || terrain.terrainData == null)
return new Bounds();
var terrainBounds = terrain.terrainData.bounds;
var ts = terrainBounds.size;
terrainBounds.center = terrain.transform.position;
terrainBounds.center += new Vector3(ts.x * 0.5f, 0, ts.z * 0.5f);
return terrainBounds;
}
/// <summary>
/// Compute the total bounds of the provided terrains
/// </summary>
/// <param name="terrains"></param>
/// <returns></returns>
public static Bounds ComputeTerrainBounds(Terrain[] terrains)
{
Bounds terrainBounds = new Bounds(Vector3.zero, Vector3.zero);
for (int i = 0; i < terrains.Length; i++)
{
Terrain terrain = terrains[i];
Bounds terrainWorldBounds = ComputeTerrainBounds(terrain);
if (i == 0)
{
terrainBounds = terrainWorldBounds;
}
else
{
terrainBounds.Encapsulate(terrainWorldBounds);
}
}
return terrainBounds;
}
public static Bounds AdjustForRotation(Bounds b, Quaternion rot)
{
var mtx = Matrix4x4.TRS(b.center, rot, Vector3.one);
b.Encapsulate(mtx.MultiplyPoint(b.size / 2));
b.Encapsulate(mtx.MultiplyPoint(-b.size / 2));
return b;
}
public static Bounds GetBounds(Transform transform)
{
Vector3 scale = transform.lossyScale;
float max = Mathf.Max(scale.x, scale.z);
var b = TerrainUtil.AdjustForRotation(new Bounds(transform.position, new Vector3(max, max, max)), transform.rotation);
b.max = new Vector3(b.max.x, 99999, b.max.z);
b.min = new Vector3(b.min.x, -99999, b.min.z);
return b;
}
public static Vector3 ComputeTerrainSize(Terrain terrain)
{
var ts = terrain.terrainData.heightmapScale;
var hr = terrain.terrainData.heightmapResolution;
return new Vector3(ts.x * hr, ts.y * 2, ts.z * hr);
}
public static Matrix4x4 ComputeStampMatrix(Terrain terrain, Transform transform, bool heightStamp = false, int sizeXOffset = 0, int sizeZOffset = 0)
{
var ts = terrain.terrainData.size;
Vector2 realSize = new Vector2(ts.x, ts.z);
// We need to expand the height stamp slightly for parts of the equation,
// but only when applying a height stamp, not a copy paste stamp!
if (heightStamp)
{
var hms = terrain.terrainData.heightmapScale;
var hmr = terrain.terrainData.heightmapResolution;
realSize = new Vector2(hms.x * hmr, hms.z * hmr);
}
var localPosition = terrain.transform.worldToLocalMatrix.MultiplyPoint3x4(transform.position);
var size = transform.lossyScale;
Vector2 size2D = new Vector2(size.x + sizeXOffset, size.z + sizeZOffset);
var pos = new Vector2(localPosition.x, localPosition.z);
// use potentially expanded range to compute 01 value in height stamp
var pos01 = pos / realSize;
var rotation = transform.rotation.eulerAngles.y;
var m = Matrix4x4.Translate(-pos01);
m = Matrix4x4.Rotate(Quaternion.AngleAxis(rotation, Vector3.forward)) * m;
// Use the actual size to compute the matrix scale
m = Matrix4x4.Scale(new Vector2(ts.x, ts.z) / size2D) * m;
m = Matrix4x4.Translate(new Vector3(0.5f, 0.5f, 0)) * m;
return m;
}
public static int FindTextureChannelIndex(Terrain terrain, TerrainLayer layer)
{
var layers = terrain.terrainData.terrainLayers;
for (var index = 0; index < layers.Length; index++)
{
var l = layers[index];
if (!ReferenceEquals(l, layer))
continue;
return index;
}
return -1;
}
public static int FindTreeIndex(Terrain terrain, GameObject prefab)
{
var protos = terrain.terrainData.treePrototypes;
for (var index = 0; index < protos.Length; index++)
{
var l = protos[index];
if (!ReferenceEquals(l.prefab, prefab))
continue;
return index;
}
return -1;
}
public static void EnsureTexturesAreOnTerrain(Terrain terrain, List<TerrainLayer> prototypes)
{
var terrainLayers = terrain.terrainData.terrainLayers;
List<TerrainLayer> resultLayers = new List<TerrainLayer>(terrainLayers);
bool edited = false;
int index = -1;
foreach (var prototype in prototypes.Distinct())
{
for (int i = 0; i < terrainLayers.Length; ++i)
{
var tp = terrainLayers[i];
if (ReferenceEquals(prototype, tp))
{
index = i;
}
}
if (index < 0)
{
resultLayers.Add(prototype);
edited = true;
}
}
if (edited)
{
terrain.terrainData.terrainLayers = resultLayers.ToArray();
}
}
}
}