//1.mesh构建
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider))]
public class Potteryprototype : MonoBehaviour
{
MeshFilter meshFilter;
MeshRenderer meshRenderer;
MeshCollider meshCollider;
Mesh mesh;
public int details = 40;
public int layer = 20;
public float Height = 0.1f;
public float OuterRadius = 1.0f;
public float InnerRadius = 0.9f;
List<Vector3> vertices;
List<Vector2> UV;
List<int> triangles;
float EachAngle ;
int SideCount;
public MouseControl mouse;
void Start()
{
meshFilter = GetComponent<MeshFilter>();
meshCollider = GetComponent<MeshCollider>();
meshRenderer = GetComponent<MeshRenderer>();
}
[ContextMenu("GeneratePottery")]
void GeneratePrototype()
{
vertices = new List<Vector3>();
triangles = new List<int>();
UV = new List<Vector2>();
EachAngle = Mathf.PI * 2 / details;
for (int i = 0; i < layer; i++)
{
GenerateCircle(i);
}
Capping();
mesh = new Mesh();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.uv = UV.ToArray();
mesh.RecalculateBounds();
mesh.RecalculateTangents();
meshFilter.mesh = mesh;
mesh.RecalculateNormals();
meshCollider.sharedMesh = mesh;
}
void GenerateCircle(int _layer)
{
//外顶点与内顶点分开存储,方便变化操作时的计算
List<Vector3> vertices_outside = new List<Vector3>();
List<Vector3> vertices_inside = new List<Vector3>();
List<Vector2> UV_outside = new List<Vector2>();
List<Vector2> UV_inside = new List<Vector2>();
//外侧和内侧顶点计算
//注意这里让每一圈的首尾重合了,也就是开始和结尾的顶点坐标一致
//目的是计算UV坐标时不会出现空缺
for (float i = 0; i <= Mathf.PI * 2+EachAngle; i += EachAngle)
{
Vector3 v1 = new Vector3(OuterRadius * Mathf.Sin(i), _layer * Height, OuterRadius * Mathf.Cos(i));
Vector3 v2 = new Vector3(OuterRadius * Mathf.Sin(i), (_layer +1)* Height, OuterRadius * Mathf.Cos(i));
Vector3 v3 = new Vector3(InnerRadius * Mathf.Sin(i), _layer * Height, InnerRadius * Mathf.Cos(i));
Vector3 v4 = new Vector3(InnerRadius * Mathf.Sin(i), (_layer+1) * Height, InnerRadius * Mathf.Cos(i));
vertices_outside.Add(v1); vertices_outside.Add(v2);
vertices_inside.Add(v3); vertices_inside.Add(v4);
Vector2 uv1 = new Vector2(i / Mathf.PI*2, _layer*1.0f / layer * 1.0f);
Vector2 uv2 = new Vector2(i / Mathf.PI*2, (_layer + 1)*1.0f / layer * 1.0f);
Vector2 uv3 = new Vector2(i / Mathf.PI*2, _layer*1.0f / layer * 1.0f);
Vector2 uv4 = new Vector2(i / Mathf.PI*2, (_layer + 1) *1.0f/ layer * 1.0f);
UV_outside.Add(uv1); UV_outside.Add(uv2);
UV_inside.Add(uv3); UV_inside.Add(uv4);
}
vertices.AddRange(vertices_outside);
vertices.AddRange(vertices_inside);
UV.AddRange(UV_outside);
UV.AddRange(UV_inside);
SideCount = vertices_outside.Count;
int j = vertices_outside.Count * _layer * 2;
int n = vertices_outside.Count;
for (int i = j; i < j + vertices_outside.Count - 2; i += 2)
{
triangles.Add(i); triangles.Add(i + 2); triangles.Add(i + 1);
triangles.Add(i + 2); triangles.Add(i + 3); triangles.Add(i + 1);
triangles.Add(i + n); triangles.Add(i + n + 1); triangles.Add(i + n + 2);
triangles.Add(i + n + 2); triangles.Add(i + n + 1); triangles.Add(i + n + 3);
}
}
//封顶,底面由于看不见就不用管了
void Capping()
{
for (float i = 0; i <= Mathf.PI * 2+EachAngle; i += EachAngle)
{
Vector3 outer = new Vector3(OuterRadius * Mathf.Sin(i),layer * Height, OuterRadius * Mathf.Cos(i));
Vector3 inner= new Vector3(InnerRadius * Mathf.Sin(i), layer * Height, InnerRadius * Mathf.Cos(i));
vertices.Add(outer);vertices.Add(inner);
Vector2 uv1 = new Vector2(i / Mathf.PI * 2,0); Vector2 uv2 = new Vector2(i / Mathf.PI * 2, 1);
UV.Add(uv1); UV.Add(uv2);
}
int j = SideCount * layer * 2;
for (int i=j;i<vertices.Count-2;i+=2)
{
triangles.Add(i);triangles.Add(i + 3);triangles.Add(i + 1);
triangles.Add(i);triangles.Add(i + 2);triangles.Add(i + 3);
}
triangles.Add(vertices.Count - 2);triangles.Add(j + 1);triangles.Add(vertices.Count - 1);
triangles.Add(vertices.Count - 2);triangles.Add(j);triangles.Add(j + 1);
}
}
//2.动态改变形状
//这个函数放在Update()里调用
void GetMouseControlTransform()
{
//从屏幕鼠标位置发射一条射线到模型上,获取这个坐标
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit info;
if (Physics.Raycast(ray.origin, ray.direction, out info))
{
//在Unity中无法直接修改MeshFilter中Mesh的信息,需要新建一个Mesh修改其引用关系
Mesh mesh = meshFilter.mesh;
Vector3[] _vertices = mesh.vertices;
for (int i = 0; i < _vertices.Length; i++)
{
//x,z平面变换
//顶点移动与Y值的关系限制在5倍单层高度
//这里可以自行修改,限制高度越大,曲线越平滑
if (Mathf.Abs(info.point.y - transform.TransformPoint(_vertices[i]).y) < (5 * Height))
{
//计算顶点移动方向的向量
Vector3 v_xz = (transform.TransformPoint(_vertices[i]) - new Vector3(transform.position.x, transform.TransformPoint(_vertices[i]).y, transform.position.z));
//外顶点与内顶点移动时相对距离应该保持不变
//因为我们知道顶点数组内的顺序关系,所以可以通过计算总顶点数除以每层单侧顶点数的商的奇偶关系来判断是外顶点还是内顶点
int n = i / SideCount;
bool side = n % 2 == 0;
//判断顶面顶点内外关系
bool caps = (i - (SideCount * layer * 2)) % 2 == 0;
//限制每个顶点最大和最小的移动距离
float max;
float min;
if (i < SideCount * layer * 2)
{
max = side ? 2f * OuterRadius : 2f * OuterRadius - (OuterRadius - InnerRadius);
min = side ? 0.5f * OuterRadius : 0.5f * OuterRadius - (OuterRadius - InnerRadius);
}
else
{
max = caps ? 2f * OuterRadius : 2f * OuterRadius - (OuterRadius - InnerRadius); ;
min = caps ? 0.5f * OuterRadius : 0.5f * OuterRadius - (OuterRadius - InnerRadius);
}
//计算当前顶点到鼠标Y值之间的距离,再用余弦函数算出实际位移距离
float dif = Mathf.Abs(info.point.y - transform.TransformPoint(_vertices[i]).y);
if (Input.GetKey(KeyCode.RightArrow))
{
float outer = max - v_xz.magnitude;
_vertices[i] += v_xz.normalized * Mathf.Min(0.01f * Mathf.Cos(((dif / 5 * Height) * Mathf.PI) / 2), outer);
}
else if (Input.GetKey(KeyCode.LeftArrow))
{
float inner = v_xz.magnitude - min;
_vertices[i] -= v_xz.normalized * Mathf.Min(0.01f * Mathf.Cos(((dif / 5 * Height) * Mathf.PI) / 2), inner);
}
//Y轴变换
float scale_y = transform.localScale.y;
if (Input.GetKey(KeyCode.UpArrow))
{
scale_y = Mathf.Min(transform.localScale.y + 0.000001f, 2.0f);
}
else if (Input.GetKey(KeyCode.DownArrow))
{
scale_y = Mathf.Max(transform.localScale.y - 0.000001f, 0.3f);
}
transform.localScale = new Vector3(transform.localScale.x, scale_y, transform.localScale.z);
}
mesh.vertices = _vertices;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
meshFilter.mesh = mesh;
meshCollider.sharedMesh = mesh;
}
}
}
}
//计算时就把顶点坐标系转换为自身坐标系,求得向量后再转换为世界坐标系
Vector3 v_xz = transform.TransformDirection(transform.InverseTransformPoint(_vertices[i]) - transform.InverseTransformPoint(new Vector3(0, _vertices[i].y, 0)));
//3.法线平均化
IEnumerator Print_Normals()
{
for (int i = 0; i < meshFilter.mesh.vertices.Length; i++)
{
if (i % 2 == 0)
{
Debug.DrawRay(transform.TransformPoint(meshFilter.mesh.vertices[i]), transform.TransformDirection(meshFilter.mesh.normals[i] * 0.3f), Color.green, 1000f);
}
else
{
Debug.DrawRay(transform.TransformPoint(meshFilter.mesh.vertices[i]), transform.TransformDirection(meshFilter.mesh.normals[i] * 0.3f), Color.blue, 1000f);
}
yield return new WaitForSeconds(Time.deltaTime);
}
}
//回到项目上来。这段法线计算的代码就不放上来了,大致就是根据顶点在数组中的下标去判断位置是否相同,然后把该顶点的法线相加即可。大家自己构建Mesh时的顶点顺序可能会不太一样。
以上代码是否有问题,请帮我完善
最新发布