295 lines
9.4 KiB
C#
295 lines
9.4 KiB
C#
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<MeshRenderer>();
|
||
|
|
if (mr == null) {
|
||
|
|
mr = gameObject.AddComponent<MeshRenderer>();
|
||
|
|
}
|
||
|
|
|
||
|
|
var mf = GetComponent<MeshFilter>();
|
||
|
|
if (mf == null) {
|
||
|
|
mf = gameObject.AddComponent<MeshFilter>();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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<MeshFilter>().mesh = plotMesh;
|
||
|
|
|
||
|
|
var mr = GetComponent<MeshRenderer>();
|
||
|
|
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
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
}
|