using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif using System.Collections; using System; [Serializable] public struct UInt3 { public uint x; public uint y; public uint z; } [Serializable] public class AxisSuffix3D { public string X; public string Y; public string Z; public string this[int index] { get { if (index == 0) { return X; } if (index == 1) { return Y; } else { return Z; } } } } [Serializable] public class Plot3DSurface { public string Title; public AxisSuffix3D Suffix; public bool Visible = true; public Vector3 axisBegin = Vector3.zero; public Vector3 axisEnd = Vector3.one; public Plot3DFunction function; } public class Plot3D : MonoBehaviour { public UInt3 sectionsCount = new UInt3() { x = 10, y = 10, z = 10 }; public Mesh plotMesh; public int tessArg0 = 10; public int tessArg1 = 10; public Plot3DSurface[] surfaces; // Use this for initialization void Start() { } // Update is called once per frame void Update() { } private void ValidateView() { var mr = GetComponent(); if (mr == null) { mr = gameObject.AddComponent(); } var mf = GetComponent(); if (mf == null) { mf = gameObject.AddComponent(); } } public void UpdateMesh() { ValidateView(); if (tessArg0 < 1) { tessArg0 = 1; } if (tessArg1 < 1) { tessArg1 = 1; } if (plotMesh == null) { plotMesh = new Mesh(); } //build vertices var surfaceVertexCount = (tessArg0 + 1) * (tessArg1 + 1); Vector3[] vertices = new Vector3[surfaceVertexCount * surfaces.Length]; var ptsX = tessArg0 + 1; var ptsZ = tessArg1 + 1; for (int z = 0; z < ptsZ; ++z) { for (int x = 0; x < ptsX; ++x) { float u = (1.0f / (ptsX - 1)) * x; float v = (1.0f / (ptsZ - 1)) * z; //for each surface generate vertex for (int i = 0; i < surfaces.Length; ++i) { float y = 0; var surf = surfaces[i]; if (surf.Visible) { var function = surf.function; if (function != null) { y = function.f(Mathf.Lerp(surf.axisBegin[0], surf.axisEnd[0], u), Mathf.Lerp(surf.axisBegin[2], surf.axisEnd[2], v)); if (surf.axisEnd[1] - surf.axisBegin[1] < 0.000001f) { y = 0; } else { y = (y - surf.axisBegin[1]) / (surf.axisEnd[1] - surf.axisBegin[1]); } } vertices[z * ptsX + x + surfaceVertexCount * i] = new Vector3(u, y, v); } else { vertices[z * ptsX + x + surfaceVertexCount * i] = Vector3.zero; } } } } //build indices int[] indices_source = new int[tessArg0 * tessArg1 * 6]; int index_id = 0; for (int z = 0; z < tessArg1; ++z) { var lineOff0 = z * ptsX; var lineOff1 = (z + 1) * ptsX; for (int x = 0; x < tessArg0; ++x) { //plotMesh.vertices indices_source[index_id++] = lineOff0 + x; indices_source[index_id++] = lineOff1 + x + 1; indices_source[index_id++] = lineOff0 + x + 1; indices_source[index_id++] = lineOff0 + x; indices_source[index_id++] = lineOff1 + x; indices_source[index_id++] = lineOff1 + x + 1; } } var indices = new int[indices_source.Length * surfaces.Length]; //instantiate indices for (int i = 0; i < surfaces.Length; ++i) { for (int j = 0; j < indices_source.Length; ++j) { indices[j + indices_source.Length * i] = indices_source[j] + surfaceVertexCount * i; } } Vector3[] normals = new Vector3[vertices.Length]; //build normals for (int i = 0; i < indices.Length; i += 3) { var v0 = vertices[indices[i + 0]]; var v1 = vertices[indices[i + 1]]; var v2 = vertices[indices[i + 2]]; var d0 = v1 - v0; var d1 = v2 - v1; var normal = Vector3.Cross(d0, d1).normalized; normals[indices[i + 0]] = normal; normals[indices[i + 1]] = normal; normals[indices[i + 2]] = normal; } plotMesh.Clear(); plotMesh.subMeshCount = surfaces.Length; plotMesh.vertices = vertices; plotMesh.normals = normals; for (int i = 0; i < surfaces.Length; ++i) { var slice_indices = new int[indices_source.Length]; Array.Copy(indices, indices_source.Length * i, slice_indices, 0, indices_source.Length); plotMesh.SetTriangles(slice_indices, i); } plotMesh.RecalculateBounds(); plotMesh.RecalculateNormals(); GetComponent().mesh = plotMesh; var mr = GetComponent(); if (mr.sharedMaterials.Length != surfaces.Length) { var mats = mr.sharedMaterials; Array.Resize(ref mats, surfaces.Length); mr.sharedMaterials = mats; } } private static Vector3[] cubeVertices = new Vector3[] { Vector3.zero, Vector3.forward, Vector3.right + Vector3.forward, Vector3.right, Vector3.zero + Vector3.up, Vector3.forward + Vector3.up, Vector3.right + Vector3.forward + Vector3.up, Vector3.right + Vector3.up, }; static Color[] axisColor = new Color[] { Color.red, Color.green, Color.blue }; void DrawAxisLabels(int axis, bool begin, Vector3 worldPoint, Vector2 offset) { GUIStyle style = new GUIStyle(); Vector3 screenPoint = Camera.current.WorldToScreenPoint(worldPoint); if (screenPoint.z < 0) { return; } screenPoint.x += offset.x; screenPoint.y += offset.y; for (int j = 0; j < surfaces.Length; ++j) { style.normal.textColor = axisColor[axis]; var pos = Camera.current.ScreenToWorldPoint(screenPoint); var val = begin ? surfaces[j].axisBegin[axis] : surfaces[j].axisEnd[axis]; Handles.Label(pos, val.ToString() + " " + surfaces[j].Suffix[axis], style); screenPoint.y -= 12; } } void OnDrawGizmos() { Gizmos.matrix = transform.localToWorldMatrix; //Draw colored axis Gizmos.color = axisColor[0]; Gizmos.DrawLine(cubeVertices[0], cubeVertices[3]); Gizmos.color = axisColor[1]; Gizmos.DrawLine(cubeVertices[0], cubeVertices[4]); Gizmos.color = axisColor[2]; Gizmos.DrawLine(cubeVertices[0], cubeVertices[1]); //Draw cube gizmo Gizmos.color = new Color(1, 1, 1, 0.2f); //Bottom cap Gizmos.DrawLine(cubeVertices[1], cubeVertices[2]); Gizmos.DrawLine(cubeVertices[2], cubeVertices[3]); //top cap Gizmos.DrawLine(cubeVertices[4], cubeVertices[5]); Gizmos.DrawLine(cubeVertices[5], cubeVertices[6]); Gizmos.DrawLine(cubeVertices[6], cubeVertices[7]); Gizmos.DrawLine(cubeVertices[7], cubeVertices[4]); //vertical Gizmos.DrawLine(cubeVertices[0], cubeVertices[4]); Gizmos.DrawLine(cubeVertices[1], cubeVertices[5]); Gizmos.DrawLine(cubeVertices[2], cubeVertices[6]); Gizmos.DrawLine(cubeVertices[3], cubeVertices[7]); //draw sections if (sectionsCount.x < 1) { sectionsCount.x = 1; } if (sectionsCount.y < 1) { sectionsCount.y = 1; } if (sectionsCount.z < 1) { sectionsCount.z = 1; } Gizmos.color = Color.red; for (int x = 0; x <= sectionsCount.x; ++x) { Vector3 pt = Vector3.right * ((1.0f / sectionsCount.x) * x); Gizmos.DrawLine(pt, pt - Vector3.forward * 0.025f); } Gizmos.color = Color.green; for (int x = 0; x <= sectionsCount.y; ++x) { Vector3 pt = Vector3.up * ((1.0f / sectionsCount.y) * x); Gizmos.DrawLine(pt, pt - Vector3.right * 0.025f); } Gizmos.color = Color.blue; for (int x = 0; x <= sectionsCount.z; ++x) { Vector3 pt = Vector3.forward * ((1.0f / sectionsCount.z) * x); Gizmos.DrawLine(pt, pt - Vector3.right * 0.025f); } //Draw labels Gizmos.matrix = Matrix4x4.identity; #if UNITY_EDITOR DrawAxisLabels(0, true, transform.TransformPoint(cubeVertices[0]), Vector2.zero); DrawAxisLabels(0, false, transform.TransformPoint(cubeVertices[3]), Vector2.zero); DrawAxisLabels(1, true, transform.TransformPoint(cubeVertices[0]), new Vector2(0, -14 * surfaces.Length)); DrawAxisLabels(1, false, transform.TransformPoint(cubeVertices[4]), new Vector2(0, 14 * surfaces.Length)); DrawAxisLabels(2, true, transform.TransformPoint(cubeVertices[0]), new Vector2(0, -14 * surfaces.Length * 2)); DrawAxisLabels(2, false, transform.TransformPoint(cubeVertices[1]), new Vector2(0, 0)); #endif } }