Skip to content

Instantly share code, notes, and snippets.

@Softdrink117
Last active April 7, 2020 19:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Softdrink117/04ecf6891197da36ce8b78b635a802d7 to your computer and use it in GitHub Desktop.
Save Softdrink117/04ecf6891197da36ce8b78b635a802d7 to your computer and use it in GitHub Desktop.
An extension of UnityEngine.UI.MaskableGraphic that allows for the drawing of arbitrary meshes
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Softdrink
{
// Adapted from https://www.hallgrimgames.com/blog/2018/11/25/custom-unity-ui-meshes
// with significant modifications to account for arbitrary meshes and UVs
public class MeshGraphic : MaskableGraphic
{
[TooltipAttribute("For best results, use a 2D mesh built on the XY plane, with UVs, scaled to fit a unit square.")]
public Mesh mesh = null;
[TooltipAttribute("A scalar multiplier applied to mesh Z thickness.")]
public float zThickness = 0f;
// Scaling factor that will be automatically calculated based on mesh vertices
// Will attempt to automatically long-edge fit the XY projection of the Mesh to the RectTransform area
private float scaleFactor = 1f;
[SerializeField]
[TooltipAttribute("A Texture to map to the Mesh. If the Mesh has no UVs, new UVs will be generated in the RectTransform space.")]
Texture _texture;
[TooltipAttribute("If enabled, the Mesh will be recalculated whenever the RectTransform dimensions change. This provides the most intuitive results, but can be performance intensive. \nIf disabled, you must call UpdateMesh() manually to resize the mesh when changing the RectTransform dimensions.")]
public bool autoUpdateMesh = true;
private Vector3 vertMax;
private Vector3 vertMin;
// Redraw element when texture changed in Inspector
public Texture texture
{
get
{
return _texture;
}
set
{
if (_texture == value)
return;
_texture = value;
SetVerticesDirty();
SetMaterialDirty();
}
}
// Use default white texture if nothing is specified
public override Texture mainTexture
{
get
{
return _texture == null ? s_WhiteTexture : _texture;
}
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
CalcMeshScaleFactor();
SetVerticesDirty();
SetMaterialDirty();
}
#endif
// Calculate a scaling factor for the input mesh based
// on bounding box size of all vertices
void CalcMeshScaleFactor()
{
vertMax = new Vector3(Single.MinValue, Single.MinValue, Single.MinValue);
vertMin = new Vector3(Single.MaxValue, Single.MaxValue, Single.MaxValue);
for(int i = 0; i < mesh.vertices.Length; i++)
{
vertMax = Vector3.Max(vertMax, mesh.vertices[i]);
vertMin = Vector3.Min(vertMin, mesh.vertices[i]);
}
float xRange = Mathf.Abs(vertMax.x - vertMin.x);
float yRange = Mathf.Abs(vertMax.y - vertMin.y);
float maxRange = Mathf.Max(xRange, yRange);
if(maxRange != 0f) scaleFactor = 1.0f/maxRange;
else scaleFactor = 1.0f;
}
// Calculate a UV in RectTransform space by mapping the current vertex position relative
// to the min and max vertex positions of the entire mesh
Vector2 CalculateRectspaceUV(Vector3 vPos)
{
return new Vector2(Map(vPos.x, vertMin.x, vertMax.x, 0f, 1f), Map(vPos.y, vertMin.y, vertMax.y, 0f, 1f));
}
float Map(float x, float aMin, float aMax, float bMin, float bMax)
{
return (x - aMin) * (bMax - bMin) / (aMax - aMin) + bMin;
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
if(mesh == null) return;
bool hasUV = (mesh.uv != null && mesh.uv.Length > 0);
Vector3 scale = new Vector3(rectTransform.rect.width, rectTransform.rect.height, zThickness);
scale *= scaleFactor;
for(int i = 0; i < mesh.vertices.Length; i++)
{
if(hasUV) vh.AddVert(Vector3.Scale(mesh.vertices[i], scale), color, mesh.uv[i]);
else vh.AddVert(Vector3.Scale(mesh.vertices[i], scale), color, CalculateRectspaceUV(mesh.vertices[i]));
}
for(int j = 0; j < mesh.triangles.Length; j+= 3)
{
vh.AddTriangle(mesh.triangles[j], mesh.triangles[j+1], mesh.triangles[j+2]);
}
}
protected override void OnRectTransformDimensionsChange()
{
base.OnRectTransformDimensionsChange();
if(!autoUpdateMesh) return;
SetVerticesDirty();
SetMaterialDirty();
}
public void UpdateMesh()
{
SetVerticesDirty();
SetMaterialDirty();
}
}
}
@Softdrink117
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment