123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688 |
- using System;
- using UnityEngine;
- using UnityEngine.UI;
- using UnityEngine.UI.MPUIKIT;
- #if UNITY_EDITOR
- using UnityEditor;
- #endif
- namespace MPUIKIT {
- [AddComponentMenu("UI/MPUI/MPImage")]
- public class MPImage : Image {
- #region Constants
- public const string MpShaderName = "MPUI/Procedural Image";
- #endregion
- #region SerializedFields
- [SerializeField] private DrawShape m_DrawShape = DrawShape.None;
- [SerializeField] private Type m_ImageType = Type.Simple;
- [SerializeField] private MaterialMode m_MaterialMode;
- [SerializeField] private float m_StrokeWidth;
- [SerializeField] private float m_OutlineWidth;
- [SerializeField] private Color m_OutlineColor = Color.black;
- [SerializeField] private float m_FalloffDistance = 0.5f;
- [SerializeField] private bool m_ConstrainRotation = true;
- [SerializeField] private float m_ShapeRotation;
- [SerializeField] private bool m_FlipHorizontal;
- [SerializeField] private bool m_FlipVertical;
- [SerializeField] private Triangle m_Triangle = new Triangle();
- [SerializeField] private Rectangle m_Rectangle = new Rectangle();
- [SerializeField] private Circle m_Circle = new Circle();
- [SerializeField] private Pentagon m_Pentagon = new Pentagon();
- [SerializeField] private Hexagon m_Hexagon = new Hexagon();
- [SerializeField] private NStarPolygon m_NStarPolygon = new NStarPolygon();
- [SerializeField] private GradientEffect m_GradientEffect = new GradientEffect();
- #endregion
- #region Public Properties
- #region Draw Settings
- /// <summary>
- /// Type of the shape to be drawn. ie: Rectangle, Circle
- /// </summary>
- public DrawShape DrawShape {
- get => m_DrawShape;
- set {
- m_DrawShape = value;
- if (material == m_Material) {
- m_Material.SetInt(SpDrawShape, (int) m_DrawShape);
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Width of the stroke for the drawn shape. 0 is no stroke.
- /// </summary>
- public float StrokeWidth {
- get => m_StrokeWidth;
- set {
- m_StrokeWidth = value;
- m_StrokeWidth = m_StrokeWidth < 0 ? 0 : m_StrokeWidth;
- if (material == m_Material) {
- m_Material.SetFloat(SpStrokeWidth, m_StrokeWidth);
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Width of the outline for the drawn shape. 0 is no outline.
- /// </summary>
- public float OutlineWidth {
- get => m_OutlineWidth;
- set {
- m_OutlineWidth = value;
- m_OutlineWidth = m_OutlineWidth < 0 ? 0 : m_OutlineWidth;
- if (m_Material == material) {
- m_Material.SetFloat(SpOutlineWidth, m_OutlineWidth);
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Color of the Outline. Has no effect is teh value of the OutlineWidth is 0
- /// </summary>
- public Color OutlineColor {
- get => m_OutlineColor;
- set {
- m_OutlineColor = value;
- if (m_Material == material) {
- m_Material.SetColor(SpOutlineColor, m_OutlineColor);
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Edge falloff distance of the shape
- /// </summary>
- public float FalloffDistance {
- get { return m_FalloffDistance; }
- set {
- m_FalloffDistance = Mathf.Max(value, 0f);
- if (material == m_Material) {
- m_Material.SetFloat(SpFalloffDistance, m_FalloffDistance);
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Constrains rotation to 0, 90, 270 degrees angle if set to true. But the width and height of the shape
- /// is replaced as necessary to avoid clipping.
- /// If set to false, any shapes can be rotated in any arbitrary angle but will often result in
- /// clipping of the shape.
- /// </summary>
- public bool ConstrainRotation {
- get { return m_ConstrainRotation; }
- set {
- m_ConstrainRotation = value;
- if (m_Material == material) {
- m_Material.SetInt(SpConstrainedRotation, value?1:0);
- }
- if (value) {
- m_ShapeRotation = ConstrainRotationValue(m_ShapeRotation);
- }
- base.SetVerticesDirty();
- base.SetMaterialDirty();
- }
- }
-
- private float ConstrainRotationValue(float val) {
- float finalRotation = val - val % 90;
- if (Mathf.Abs(finalRotation) >= 360) finalRotation = 0;
- return finalRotation;
- }
- /// <summary>
- /// Rotation of the shape.
- /// </summary>
- public float ShapeRotation {
- get { return m_ShapeRotation; }
- set {
- m_ShapeRotation = m_ConstrainRotation ? ConstrainRotationValue(value) : value;
- if (m_Material == material) {
- m_Material.SetFloat(SpShapeRotation, m_ShapeRotation);
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Flips the shape horizontally.
- /// </summary>
- public bool FlipHorizontal {
- get { return m_FlipHorizontal; }
- set {
- m_FlipHorizontal = value;
- if (m_Material == material) {
- m_Material.SetInt(SpFlipHorizontal, m_FlipHorizontal ? 1 : 0);
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Flips the shape vertically
- /// </summary>
- public bool FlipVertical {
- get { return m_FlipVertical; }
- set {
- m_FlipVertical = value;
- if (m_Material == material) {
- m_Material.SetInt(SpFlipVertical, m_FlipVertical ? 1 : 0);
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Defines what material type of use to render the shape. Dynamic or Shared.
- /// Default is Dynamic and will issue one draw call per image object. If set to shared, assigned
- /// material in the material slot will be used to render the image. It will fallback to dynamic
- /// if no material in the material slot is assigned
- /// </summary>
- public MaterialMode MaterialMode {
- get { return m_MaterialMode; }
- set {
- if (m_MaterialMode == value) return;
- m_MaterialMode = value;
- InitializeComponents();
- if (material == m_Material) {
- InitValuesFromSharedMaterial();
- #if UNITY_EDITOR
- _parseAgainOnValidate = true;
- #endif
- }
- base.SetMaterialDirty();
- }
- }
- /// <summary>
- /// Shared material to use to render the shape. the material must use the "MPUI/Procedural Sprite" shader
- /// </summary>
- public override Material material {
- get {
- if (m_Material && m_MaterialMode == MaterialMode.Shared) {
- return m_Material;
- }
- return DynamicMaterial;
- }
- set {
- m_Material = value;
- if (m_Material && m_MaterialMode == MaterialMode.Shared && m_Material.shader.name == MpShaderName) {
- InitValuesFromSharedMaterial();
- #if UNITY_EDITOR
- _parseAgainOnValidate = true;
- #endif
- }
- InitializeComponents();
- base.SetMaterialDirty();
- }
- }
- // ReSharper disable once InconsistentNaming
- /// <summary>
- /// Type of the image. Only two types are supported. Simple and Filled.
- /// Default and fallback value is Simple.
- /// </summary>
- public new Type type {
- get => m_ImageType;
- set {
- if (m_ImageType == value) return;
- switch (value) {
- case Type.Simple:
- case Type.Filled:
- m_ImageType = value;
- break;
- case Type.Tiled:
- case Type.Sliced:
- break;
- default:
- throw new ArgumentOutOfRangeException(value.ToString(), value, null);
- }
- base.type = m_ImageType;
- }
- }
- #endregion
- public Triangle Triangle {
- get => m_Triangle;
- set {
- m_Triangle = value;
- SetMaterialDirty();
- }
- }
- public Rectangle Rectangle {
- get => m_Rectangle;
- set {
- m_Rectangle = value;
- SetMaterialDirty();
- }
- }
- public Circle Circle{
- get => m_Circle;
- set {
- m_Circle = value;
- SetMaterialDirty();
- }
- }
- public Pentagon Pentagon {
- get => m_Pentagon;
- set {
- m_Pentagon = value;
- SetMaterialDirty();
- }
- }
- public Hexagon Hexagon {
- get => m_Hexagon;
- set {
- m_Hexagon = value;
- SetMaterialDirty();
- }
- }
- public NStarPolygon NStarPolygon {
- get => m_NStarPolygon;
- set {
- m_NStarPolygon = value;
- SetMaterialDirty();
- }
- }
- public GradientEffect GradientEffect {
- get => m_GradientEffect;
- set {
- m_GradientEffect = value;
- SetMaterialDirty();
- }
- }
- #endregion
- #region Private Variables
- private Material _dynamicMaterial;
- private Material DynamicMaterial {
- get {
- if (_dynamicMaterial == null) {
- _dynamicMaterial = new Material(Shader.Find(MpShaderName));
- _dynamicMaterial.name += " [Dynamic]";
- }
- return _dynamicMaterial;
- }
- }
- #if UNITY_EDITOR
- private bool _parseAgainOnValidate;
- #endif
- private Sprite ActiveSprite {
- get {
- Sprite overrideSprite1 = overrideSprite;
- return overrideSprite1 != null ? overrideSprite1 : sprite;
- }
- }
- #endregion
- #region Material PropertyIds
- private static readonly int SpPixelWorldScale = Shader.PropertyToID("_PixelWorldScale");
- private static readonly int SpDrawShape = Shader.PropertyToID("_DrawShape");
- private static readonly int SpStrokeWidth = Shader.PropertyToID("_StrokeWidth");
- private static readonly int SpOutlineWidth = Shader.PropertyToID("_OutlineWidth");
- private static readonly int SpOutlineColor = Shader.PropertyToID("_OutlineColor");
- private static readonly int SpFalloffDistance = Shader.PropertyToID("_FalloffDistance");
- private static readonly int SpShapeRotation = Shader.PropertyToID("_ShapeRotation");
- private static readonly int SpConstrainedRotation = Shader.PropertyToID("_ConstrainRotation");
- private static readonly int SpFlipHorizontal = Shader.PropertyToID("_FlipHorizontal");
- private static readonly int SpFlipVertical = Shader.PropertyToID("_FlipVertical");
- #endregion
- #if UNITY_EDITOR
- public void UpdateSerializedValuesFromSharedMaterial() {
- if (m_Material && MaterialMode == MaterialMode.Shared) {
- InitValuesFromSharedMaterial();
- base.SetMaterialDirty();
- }
- }
- protected override void OnValidate() {
- InitializeComponents();
- if (_parseAgainOnValidate) {
- InitValuesFromSharedMaterial();
- _parseAgainOnValidate = false;
- }
- DrawShape = m_DrawShape;
- StrokeWidth = m_StrokeWidth;
- OutlineWidth = m_OutlineWidth;
- OutlineColor = m_OutlineColor;
- FalloffDistance = m_FalloffDistance;
- ConstrainRotation = m_ConstrainRotation;
- ShapeRotation = m_ShapeRotation;
- FlipHorizontal = m_FlipHorizontal;
- FlipVertical = m_FlipVertical;
-
- m_Triangle.OnValidate();
- m_Circle.OnValidate();
- m_Rectangle.OnValidate();
- m_Pentagon.OnValidate();
- m_Hexagon.OnValidate();
- m_NStarPolygon.OnValidate();
- m_GradientEffect.OnValidate();
- base.OnValidate();
- base.SetMaterialDirty();
- }
- #endif
- private void InitializeComponents() {
- m_Circle.Init(m_Material, material, rectTransform);
- m_Triangle.Init(m_Material, material, rectTransform);
- m_Rectangle.Init(m_Material, material, rectTransform);
- m_Pentagon.Init(m_Material, material, rectTransform);
- m_Hexagon.Init(m_Material, material, rectTransform);
- m_NStarPolygon.Init(m_Material, material, rectTransform);
- m_GradientEffect.Init(m_Material, material, rectTransform);
- }
- void FixAdditionalShaderChannelsInCanvas()
- {
- Canvas c = canvas;
- if(canvas == null) return;
- AdditionalCanvasShaderChannels additionalShaderChannels = c.additionalShaderChannels;
- additionalShaderChannels |= AdditionalCanvasShaderChannels.TexCoord1;
- additionalShaderChannels |= AdditionalCanvasShaderChannels.TexCoord2;
- c.additionalShaderChannels = additionalShaderChannels;
- }
- #if UNITY_EDITOR
- protected override void Reset() {
- InitializeComponents();
- base.Reset();
- }
- #else
- void Reset() {
- InitializeComponents();
- }
- #endif
- protected override void Awake()
- {
- base.Awake();
- Init();
- }
- public void Init()
- {
- InitializeComponents();
- FixAdditionalShaderChannelsInCanvas();
- if (m_Material && MaterialMode == MaterialMode.Shared) {
- InitValuesFromSharedMaterial();
- }
- ListenToComponentChanges(true);
- base.SetAllDirty();
- }
- protected override void OnDestroy() {
- ListenToComponentChanges(false);
- base.OnDestroy();
- }
- protected void ListenToComponentChanges(bool toggle) {
- if (toggle) {
- m_Circle.OnComponentSettingsChanged += OnComponentSettingsChanged;
- m_Triangle.OnComponentSettingsChanged += OnComponentSettingsChanged;
- m_Rectangle.OnComponentSettingsChanged += OnComponentSettingsChanged;
- m_Pentagon.OnComponentSettingsChanged += OnComponentSettingsChanged;
- m_Hexagon.OnComponentSettingsChanged += OnComponentSettingsChanged;
- m_NStarPolygon.OnComponentSettingsChanged += OnComponentSettingsChanged;
- m_GradientEffect.OnComponentSettingsChanged += OnComponentSettingsChanged;
- }
- else {
- m_Circle.OnComponentSettingsChanged -= OnComponentSettingsChanged;
- m_Triangle.OnComponentSettingsChanged -= OnComponentSettingsChanged;
- m_Rectangle.OnComponentSettingsChanged -= OnComponentSettingsChanged;
- m_Pentagon.OnComponentSettingsChanged -= OnComponentSettingsChanged;
- m_Hexagon.OnComponentSettingsChanged -= OnComponentSettingsChanged;
- m_NStarPolygon.OnComponentSettingsChanged -= OnComponentSettingsChanged;
- m_GradientEffect.OnComponentSettingsChanged += OnComponentSettingsChanged;
- }
- }
- protected override void OnTransformParentChanged()
- {
- base.OnTransformParentChanged();
- FixAdditionalShaderChannelsInCanvas();
- }
- private void OnComponentSettingsChanged(object sender, EventArgs e) {
- base.SetMaterialDirty();
- }
- protected override void OnRectTransformDimensionsChange() {
- base.OnRectTransformDimensionsChange();
- m_Circle.UpdateCircleRadius(rectTransform);
- base.SetMaterialDirty();
- }
- protected override void OnPopulateMesh(VertexHelper vh) {
- switch (type) {
- case Type.Simple:
- case Type.Sliced:
- case Type.Tiled:
- MPImageHelper.GenerateSimpleSprite(vh, preserveAspect, canvas, rectTransform, ActiveSprite,
- color, m_FalloffDistance);
- break;
- case Type.Filled:
- MPImageHelper.GenerateFilledSprite(vh, preserveAspect, canvas, rectTransform, ActiveSprite,
- color, fillMethod, fillAmount, fillOrigin, fillClockwise, m_FalloffDistance);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- public override Material GetModifiedMaterial(Material baseMaterial) {
-
- Material mat = base.GetModifiedMaterial(baseMaterial);
-
-
- if (m_Material && MaterialMode == MaterialMode.Shared) {
- InitValuesFromSharedMaterial();
- }
-
- DisableAllMaterialKeywords(mat);
- RectTransform rt = rectTransform;
- if (DrawShape != DrawShape.None) {
- mat.SetFloat(SpOutlineWidth, m_OutlineWidth);
- mat.SetFloat(SpStrokeWidth, m_StrokeWidth);
-
- mat.SetColor(SpOutlineColor, OutlineColor);
- mat.SetFloat(SpFalloffDistance, FalloffDistance);
-
- float pixelSize = 1/Mathf.Max(0, FalloffDistance);
- mat.SetFloat(SpPixelWorldScale, Mathf.Clamp(pixelSize, 0f, 999999f));
- if (m_StrokeWidth > 0 && m_OutlineWidth > 0) {
- mat.EnableKeyword("OUTLINED_STROKE");
- }
- else {
- if (m_StrokeWidth > 0) {
- mat.EnableKeyword("STROKE");
- }
- else if (m_OutlineWidth > 0) {
- mat.EnableKeyword("OUTLINED");
- }
- else {
- mat.DisableKeyword("OUTLINED_STROKE");
- mat.DisableKeyword("STROKE");
- mat.DisableKeyword("OUTLINED");
- }
- }
- }
- m_Triangle.ModifyMaterial(ref mat);
- m_Circle.ModifyMaterial(ref mat, m_FalloffDistance);
- m_Rectangle.ModifyMaterial(ref mat);
- m_Pentagon.ModifyMaterial(ref mat);
- m_Hexagon.ModifyMaterial(ref mat);
- m_NStarPolygon.ModifyMaterial(ref mat);
- m_GradientEffect.ModifyMaterial(ref mat);
- switch (DrawShape) {
- case DrawShape.None:
- mat.DisableKeyword("CIRCLE");
- mat.DisableKeyword("TRIANGLE");
- mat.DisableKeyword("RECTANGLE");
- mat.DisableKeyword("PENTAGON");
- mat.DisableKeyword("HEXAGON");
- mat.DisableKeyword("NSTAR_POLYGON");
- break;
- case DrawShape.Circle:
- mat.EnableKeyword("CIRCLE");
- break;
- case DrawShape.Triangle:
- mat.EnableKeyword("TRIANGLE");
- break;
- case DrawShape.Rectangle:
- mat.EnableKeyword("RECTANGLE");
- break;
- case DrawShape.Pentagon:
- mat.EnableKeyword("PENTAGON");
- break;
- case DrawShape.NStarPolygon:
- mat.EnableKeyword("NSTAR_POLYGON");
- break;
- case DrawShape.Hexagon:
- mat.EnableKeyword("HEXAGON");
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- mat.SetInt(SpDrawShape, (int) DrawShape);
- mat.SetInt(SpFlipHorizontal, m_FlipHorizontal ? 1 : 0);
- mat.SetInt(SpFlipVertical, m_FlipVertical ? 1 : 0);
-
- mat.SetFloat(SpShapeRotation, m_ShapeRotation);
- mat.SetInt(SpConstrainedRotation, m_ConstrainRotation?1:0);
- return mat;
- }
- private void DisableAllMaterialKeywords(Material mat) {
- mat.DisableKeyword("PROCEDURAL");
- mat.DisableKeyword("HYBRID");
- mat.DisableKeyword("CIRCLE");
- mat.DisableKeyword("TRIANGLE");
- mat.DisableKeyword("RECTANGLE");
- mat.DisableKeyword("PENTAGON");
- mat.DisableKeyword("HEXAGON");
- mat.DisableKeyword("NSTAR_POLYGON");
- mat.DisableKeyword("STROKE");
- mat.DisableKeyword("OUTLINED");
- mat.DisableKeyword("OUTLINED_STROKE");
- mat.DisableKeyword("ROUNDED_CORNERS");
- mat.DisableKeyword("GRADIENT_LINEAR");
- mat.DisableKeyword("GRADIENT_CORNER");
- mat.DisableKeyword("GRADIENT_RADIAL");
- }
- public void InitValuesFromSharedMaterial() {
- if (m_Material == null) return;
- Material mat = m_Material;
- //Debug.Log("Parsing shared mat");
- //Basic Settings
- m_DrawShape = (DrawShape) mat.GetInt(SpDrawShape);
- m_StrokeWidth = mat.GetFloat(SpStrokeWidth);
- m_FalloffDistance = mat.GetFloat(SpFalloffDistance);
- m_OutlineWidth = mat.GetFloat(SpOutlineWidth);
- m_OutlineColor = mat.GetColor(SpOutlineColor);
- m_FlipHorizontal = mat.GetInt(SpFlipHorizontal) == 1;
- m_FlipVertical = mat.GetInt(SpFlipVertical) == 1;
- m_ConstrainRotation = mat.GetInt(SpConstrainedRotation) == 1;
- m_ShapeRotation = mat.GetFloat(SpShapeRotation);
- //Debug.Log($"Parsed Falloff Distance: {m_FalloffDistance}");
- m_Triangle.InitValuesFromMaterial(ref mat);
- m_Circle.InitValuesFromMaterial(ref mat);
- m_Rectangle.InitValuesFromMaterial(ref mat);
- m_Pentagon.InitValuesFromMaterial(ref mat);
- m_Hexagon.InitValuesFromMaterial(ref mat);
- m_NStarPolygon.InitValuesFromMaterial(ref mat);
- //GradientEffect
- m_GradientEffect.InitValuesFromMaterial(ref mat);
- }
- #if UNITY_EDITOR
- public Material CreateMaterialAssetFromComponentSettings() {
- Material matAsset = new Material(Shader.Find(MpShaderName));
- matAsset = GetModifiedMaterial(matAsset);
- string path = EditorUtility.SaveFilePanelInProject("Create Material for MPImage",
- "MPMaterial", "mat", "Choose location");
- AssetDatabase.CreateAsset(matAsset, path);
- AssetDatabase.SaveAssets();
- AssetDatabase.Refresh();
- return matAsset;
- }
- #endif
- }
- }
|