revival/game/Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs

155 lines
6.4 KiB
C#

using System.Collections.Generic;
using PathCreation.Utility;
using UnityEngine;
namespace PathCreation.Examples {
public class RoadMeshCreator : PathSceneTool {
[Header ("Road settings")]
public float roadWidth = .4f;
[Range (0, .5f)]
public float thickness = .15f;
public bool flattenSurface;
[Header ("Material settings")]
public Material roadMaterial;
public Material undersideMaterial;
public float textureTiling = 1;
[SerializeField, HideInInspector]
GameObject meshHolder;
MeshFilter meshFilter;
MeshRenderer meshRenderer;
Mesh mesh;
protected override void PathUpdated () {
if (pathCreator != null) {
AssignMeshComponents ();
AssignMaterials ();
CreateRoadMesh ();
}
}
void CreateRoadMesh () {
Vector3[] verts = new Vector3[path.NumPoints * 8];
Vector2[] uvs = new Vector2[verts.Length];
Vector3[] normals = new Vector3[verts.Length];
int numTris = 2 * (path.NumPoints - 1) + ((path.isClosedLoop) ? 2 : 0);
int[] roadTriangles = new int[numTris * 3];
int[] underRoadTriangles = new int[numTris * 3];
int[] sideOfRoadTriangles = new int[numTris * 2 * 3];
int vertIndex = 0;
int triIndex = 0;
// Vertices for the top of the road are layed out:
// 0 1
// 8 9
// and so on... So the triangle map 0,8,1 for example, defines a triangle from top left to bottom left to bottom right.
int[] triangleMap = { 0, 8, 1, 1, 8, 9 };
int[] sidesTriangleMap = { 4, 6, 14, 12, 4, 14, 5, 15, 7, 13, 15, 5 };
bool usePathNormals = !(path.space == PathSpace.xyz && flattenSurface);
for (int i = 0; i < path.NumPoints; i++) {
Vector3 localUp = (usePathNormals) ? Vector3.Cross (path.GetTangent (i), path.GetNormal (i)) : path.up;
Vector3 localRight = (usePathNormals) ? path.GetNormal (i) : Vector3.Cross (localUp, path.GetTangent (i));
// Find position to left and right of current path vertex
Vector3 vertSideA = path.GetPoint (i) - localRight * Mathf.Abs (roadWidth);
Vector3 vertSideB = path.GetPoint (i) + localRight * Mathf.Abs (roadWidth);
// Add top of road vertices
verts[vertIndex + 0] = vertSideA;
verts[vertIndex + 1] = vertSideB;
// Add bottom of road vertices
verts[vertIndex + 2] = vertSideA - localUp * thickness;
verts[vertIndex + 3] = vertSideB - localUp * thickness;
// Duplicate vertices to get flat shading for sides of road
verts[vertIndex + 4] = verts[vertIndex + 0];
verts[vertIndex + 5] = verts[vertIndex + 1];
verts[vertIndex + 6] = verts[vertIndex + 2];
verts[vertIndex + 7] = verts[vertIndex + 3];
// Set uv on y axis to path time (0 at start of path, up to 1 at end of path)
uvs[vertIndex + 0] = new Vector2 (0, path.times[i]);
uvs[vertIndex + 1] = new Vector2 (1, path.times[i]);
// Top of road normals
normals[vertIndex + 0] = localUp;
normals[vertIndex + 1] = localUp;
// Bottom of road normals
normals[vertIndex + 2] = -localUp;
normals[vertIndex + 3] = -localUp;
// Sides of road normals
normals[vertIndex + 4] = -localRight;
normals[vertIndex + 5] = localRight;
normals[vertIndex + 6] = -localRight;
normals[vertIndex + 7] = localRight;
// Set triangle indices
if (i < path.NumPoints - 1 || path.isClosedLoop) {
for (int j = 0; j < triangleMap.Length; j++) {
roadTriangles[triIndex + j] = (vertIndex + triangleMap[j]) % verts.Length;
// reverse triangle map for under road so that triangles wind the other way and are visible from underneath
underRoadTriangles[triIndex + j] = (vertIndex + triangleMap[triangleMap.Length - 1 - j] + 2) % verts.Length;
}
for (int j = 0; j < sidesTriangleMap.Length; j++) {
sideOfRoadTriangles[triIndex * 2 + j] = (vertIndex + sidesTriangleMap[j]) % verts.Length;
}
}
vertIndex += 8;
triIndex += 6;
}
mesh.Clear ();
mesh.vertices = verts;
mesh.uv = uvs;
mesh.normals = normals;
mesh.subMeshCount = 3;
mesh.SetTriangles (roadTriangles, 0);
mesh.SetTriangles (underRoadTriangles, 1);
mesh.SetTriangles (sideOfRoadTriangles, 2);
mesh.RecalculateBounds ();
}
// Add MeshRenderer and MeshFilter components to this gameobject if not already attached
void AssignMeshComponents () {
if (meshHolder == null) {
meshHolder = new GameObject ("Road Mesh Holder");
}
meshHolder.transform.rotation = Quaternion.identity;
meshHolder.transform.position = Vector3.zero;
meshHolder.transform.localScale = Vector3.one;
// Ensure mesh renderer and filter components are assigned
if (!meshHolder.gameObject.GetComponent<MeshFilter> ()) {
meshHolder.gameObject.AddComponent<MeshFilter> ();
}
if (!meshHolder.GetComponent<MeshRenderer> ()) {
meshHolder.gameObject.AddComponent<MeshRenderer> ();
}
meshRenderer = meshHolder.GetComponent<MeshRenderer> ();
meshFilter = meshHolder.GetComponent<MeshFilter> ();
if (mesh == null) {
mesh = new Mesh ();
}
meshFilter.sharedMesh = mesh;
}
void AssignMaterials () {
if (roadMaterial != null && undersideMaterial != null) {
meshRenderer.sharedMaterials = new Material[] { roadMaterial, undersideMaterial, undersideMaterial };
meshRenderer.sharedMaterials[0].mainTextureScale = new Vector3 (1, textureTiling);
}
}
}
}