| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 | /** * Copyright(c) Live2D Inc. All rights reserved. * * Use of this source code is governed by the Live2D Open Software license * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */using Live2D.Cubism.Core;using Live2D.Cubism.Rendering.Masking;using System;using UnityEngine;using UnityEngine.Rendering;namespace Live2D.Cubism.Rendering{    /// <summary>    /// Wrapper for drawing <see cref="CubismDrawable"/>s.    /// </summary>    [ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]    public sealed class CubismRenderer : MonoBehaviour    {        /// <summary>        /// <see cref="LocalSortingOrder"/> backing field.        /// </summary>        [SerializeField, HideInInspector]        private int _localSortingOrder;        /// <summary>        /// Local sorting order.        /// </summary>        public int LocalSortingOrder        {            get            {                return _localSortingOrder;            }            set            {                // Return early if same value given.                if (value == _localSortingOrder)                {                    return;                }                // Store value.                _localSortingOrder = value;                // Apply it.                ApplySorting();            }        }        /// <summary>        /// <see cref="Color"/> backing field.        /// </summary>        [SerializeField, HideInInspector]        private Color _color = Color.white;        /// <summary>        /// Color.        /// </summary>        public Color Color        {            get { return _color; }            set            {                // Return early if same value given.                if (value == _color)                {                    return;                }                // Store value.                _color = value;                // Apply color.                ApplyVertexColors();            }        }        /// <summary>        /// <see cref="UnityEngine.Material"/>.        /// </summary>        public Material Material        {            get            {                #if UNITY_EDITOR                if (!Application.isPlaying)                {                    return MeshRenderer.sharedMaterial;                }                #endif                return MeshRenderer.material;            }            set            {                #if UNITY_EDITOR                if (!Application.isPlaying)                {                    MeshRenderer.sharedMaterial = value;                    return;                }                #endif                MeshRenderer.material = value;            }        }        /// <summary>        /// <see cref="MainTexture"/> backing field.        /// </summary>        [SerializeField, HideInInspector]        private Texture2D _mainTexture;        /// <summary>        /// <see cref="MeshRenderer"/>'s main texture.        /// </summary>        public Texture2D MainTexture        {            get { return _mainTexture; }            set            {                // Return early if same value given and main texture is valid.                if (value == _mainTexture && _mainTexture != null)                {                    return;                }                // Store value.                _mainTexture = (value != null)                    ? value                    : Texture2D.whiteTexture;                // Apply it.                ApplyMainTexture();            }        }        /// <summary>        /// Meshes.        /// </summary>        /// <remarks>        /// Double buffering dynamic meshes increases performance on mobile, so we double-buffer them here.        /// </remarks>        private Mesh[] Meshes { get; set; }        /// <summary>        /// Index of front buffer mesh.        /// </summary>        private int FrontMesh { get; set; }        /// <summary>        /// Index of back buffer mesh..        /// </summary>        private int BackMesh { get; set; }        /// <summary>        /// <see cref="UnityEngine.Mesh"/>.        /// </summary>        public Mesh Mesh        {            get { return Meshes[FrontMesh]; }        }        /// <summary>        /// <see cref="MeshFilter"/> backing field.        /// </summary>        [NonSerialized]        private MeshFilter _meshFilter;        /// <summary>        /// <see cref="UnityEngine.MeshFilter"/>.        /// </summary>        public MeshFilter MeshFilter        {            get            {                return _meshFilter;            }        }        /// <summary>        /// <see cref="MeshRenderer"/> backing field.        /// </summary>        [NonSerialized]        private MeshRenderer _meshRenderer;        /// <summary>        /// <see cref="UnityEngine.MeshRenderer"/>.        /// </summary>        public MeshRenderer MeshRenderer        {            get            {                return _meshRenderer;            }        }        #region Interface For CubismRenderController        /// <summary>        /// <see cref="SortingMode"/> backing field.        /// </summary>        [SerializeField, HideInInspector]        private CubismSortingMode _sortingMode;        /// <summary>        /// Sorting mode.        /// </summary>        private CubismSortingMode SortingMode        {            get { return _sortingMode; }            set { _sortingMode = value; }        }        /// <summary>        /// <see cref="SortingOrder"/> backing field.        /// </summary>        [SerializeField, HideInInspector]        private int _sortingOrder;        /// <summary>        /// Sorting mode.        /// </summary>        private int SortingOrder        {            get { return _sortingOrder; }            set { _sortingOrder = value; }        }        /// <summary>        /// <see cref="RenderOrder"/> backing field.        /// </summary>        [SerializeField, HideInInspector]        private int _renderOrder;        /// <summary>        /// Sorting mode.        /// </summary>        private int RenderOrder        {            get { return _renderOrder; }            set { _renderOrder = value; }        }        /// <summary>        /// <see cref="DepthOffset"/> backing field.        /// </summary>        [SerializeField, HideInInspector]        private float _depthOffset = 0.00001f;        /// <summary>        /// Offset to apply in case of depth sorting.        /// </summary>        private float DepthOffset        {            get { return _depthOffset; }            set { _depthOffset = value; }        }        /// <summary>        /// <see cref="Opacity"/> backing field.        /// </summary>        [SerializeField, HideInInspector]        private float _opacity;        /// <summary>        /// Opacity.        /// </summary>        private float Opacity        {            get { return _opacity; }            set { _opacity = value; }        }        /// <summary>        /// Buffer for vertex colors.        /// </summary>        private Color[] VertexColors { get; set; }        /// <summary>        /// Allows tracking of what vertex data was updated last swap.        /// </summary>        private SwapInfo LastSwap { get; set; }        /// <summary>        /// Allows tracking of what vertex data will be swapped.        /// </summary>        private SwapInfo ThisSwap { get; set; }        /// <summary>        /// Swaps mesh buffers.        /// </summary>        /// <remarks>        /// Make sure to manually call this method in case you changed the <see cref="Color"/>.        /// </remarks>        public void SwapMeshes()        {            // Perform internal swap.            BackMesh = FrontMesh;            FrontMesh = (FrontMesh == 0) ? 1 : 0;            var mesh = Meshes[FrontMesh];            // Update colors.            Meshes[BackMesh].colors = VertexColors;            // Update swap info.            LastSwap = ThisSwap;            ResetSwapInfoFlags();            // Apply swap.#if UNITY_EDITOR            if (!Application.isPlaying)            {                MeshFilter.mesh = mesh;                return;            }#endif            MeshFilter.mesh = mesh;        }        /// <summary>        /// Updates visibility.        /// </summary>        public void UpdateVisibility()        {            if (LastSwap.DidBecomeVisible)            {                MeshRenderer.enabled = true;            }            else if (LastSwap.DidBecomeInvisible)            {                MeshRenderer.enabled = false;            }            ResetVisibilityFlags();        }        /// <summary>        /// Updates render order.        /// </summary>        public void UpdateRenderOrder()        {            if (LastSwap.NewRenderOrder)            {                ApplySorting();            }            ResetRenderOrderFlag();        }        /// <summary>        /// Updates sorting layer.        /// </summary>        /// <param name="newSortingLayer">New sorting layer.</param>        internal void OnControllerSortingLayerDidChange(int newSortingLayer)        {            MeshRenderer.sortingLayerID = newSortingLayer;        }        /// <summary>        /// Updates sorting mode.        /// </summary>        /// <param name="newSortingMode">New sorting mode.</param>        internal void OnControllerSortingModeDidChange(CubismSortingMode newSortingMode)        {            SortingMode = newSortingMode;            ApplySorting();        }        /// <summary>        /// Updates sorting order.        /// </summary>        /// <param name="newSortingOrder">New sorting order.</param>        internal void OnControllerSortingOrderDidChange(int newSortingOrder)        {            SortingOrder = newSortingOrder;            ApplySorting();        }        /// <summary>        /// Updates depth offset.        /// </summary>        /// <param name="newDepthOffset"></param>        internal void OnControllerDepthOffsetDidChange(float newDepthOffset)        {            DepthOffset = newDepthOffset;            ApplySorting();        }        /// <summary>        /// Sets the opacity.        /// </summary>        /// <param name="newOpacity">New opacity.</param>        internal void OnDrawableOpacityDidChange(float newOpacity)        {            Opacity = newOpacity;            ApplyVertexColors();        }        /// <summary>        /// Updates render order.        /// </summary>        /// <param name="newRenderOrder">New render order.</param>        internal void OnDrawableRenderOrderDidChange(int newRenderOrder)        {            RenderOrder = newRenderOrder;            SetNewRenderOrder();        }        /// <summary>        /// Sets the <see cref="UnityEngine.Mesh.vertices"/>.        /// </summary>        /// <param name="newVertexPositions">Vertex positions to set.</param>        internal void OnDrawableVertexPositionsDidChange(Vector3[] newVertexPositions)        {            var mesh = Mesh;            // Apply positions and update bounds.            mesh.vertices = newVertexPositions;            mesh.RecalculateBounds();            // Set swap flag.            SetNewVertexPositions();        }        /// <summary>        /// Sets visibility.        /// </summary>        /// <param name="newVisibility">New visibility.</param>        internal void OnDrawableVisiblityDidChange(bool newVisibility)        {            // Set swap flag if visible.            if (newVisibility)            {                BecomeVisible();            }            else            {                BecomeInvisible();            }        }        /// <summary>        /// Sets mask properties.        /// </summary>        /// <param name="newMaskProperties">Value to set.</param>        internal void OnMaskPropertiesDidChange(CubismMaskProperties newMaskProperties)        {            MeshRenderer.GetPropertyBlock(SharedPropertyBlock);            // Write properties.            SharedPropertyBlock.SetTexture(CubismShaderVariables.MaskTexture, newMaskProperties.Texture);            SharedPropertyBlock.SetVector(CubismShaderVariables.MaskTile, newMaskProperties.Tile);            SharedPropertyBlock.SetVector(CubismShaderVariables.MaskTransform, newMaskProperties.Transform);            MeshRenderer.SetPropertyBlock(SharedPropertyBlock);        }        /// <summary>        /// Sets model opacity.        /// </summary>        /// <param name="newModelOpacity">Opacity to set.</param>        internal void OnModelOpacityDidChange(float newModelOpacity)        {            _meshRenderer.GetPropertyBlock(SharedPropertyBlock);            // Write property.            SharedPropertyBlock.SetFloat(CubismShaderVariables.ModelOpacity, newModelOpacity);            MeshRenderer.SetPropertyBlock(SharedPropertyBlock);        }        #endregion        /// <summary>        /// <see cref="SharedPropertyBlock"/> backing field.        /// </summary>        private static MaterialPropertyBlock _sharedPropertyBlock;        /// <summary>        /// <see cref="MaterialPropertyBlock"/> that can be shared on the main script thread.        /// </summary>        private static MaterialPropertyBlock SharedPropertyBlock        {            get            {                // Lazily initialize.                if (_sharedPropertyBlock == null)                {                    _sharedPropertyBlock = new MaterialPropertyBlock();                }                return _sharedPropertyBlock;            }        }        /// <summary>        /// Applies main texture for rendering.        /// </summary>        private void ApplyMainTexture()        {            MeshRenderer.GetPropertyBlock(SharedPropertyBlock);            // Write property.            SharedPropertyBlock.SetTexture(CubismShaderVariables.MainTexture, MainTexture);            MeshRenderer.SetPropertyBlock(SharedPropertyBlock);        }        /// <summary>        /// Applies sorting.        /// </summary>        private void ApplySorting()        {            // Sort by order.            if (SortingMode.SortByOrder())            {                MeshRenderer.sortingOrder = SortingOrder + ((SortingMode == CubismSortingMode.BackToFrontOrder)                    ? (RenderOrder + LocalSortingOrder)                    : -(RenderOrder + LocalSortingOrder));                transform.localPosition = Vector3.zero;                return;            }            // Sort by depth.            var offset = (SortingMode == CubismSortingMode.BackToFrontZ)                    ? -DepthOffset                    : DepthOffset;            MeshRenderer.sortingOrder = SortingOrder + LocalSortingOrder;            transform.localPosition = new Vector3(0f, 0f, RenderOrder * offset);        }        /// <summary>        /// Uploads mesh vertex colors.        /// </summary>        public void ApplyVertexColors()        {            var vertexColors = VertexColors;            var color = Color;            color.a *= Opacity;            for (var i = 0; i < vertexColors.Length; ++i)            {                vertexColors[i] = color;            }            // Set swap flag.            SetNewVertexColors();        }        /// <summary>        /// Initializes the mesh renderer.        /// </summary>        private void TryInitializeMeshRenderer()        {            if (_meshRenderer == null)            {                _meshRenderer = GetComponent<MeshRenderer>();                // Lazily add component.                if (_meshRenderer == null)                {                    _meshRenderer = gameObject.AddComponent<MeshRenderer>();                    _meshRenderer.hideFlags = HideFlags.HideInInspector;                    _meshRenderer.receiveShadows = false;                    _meshRenderer.shadowCastingMode = ShadowCastingMode.Off;                    _meshRenderer.lightProbeUsage = LightProbeUsage.BlendProbes;                }            }        }        /// <summary>        /// Initializes the mesh filter.        /// </summary>        private void TryInitializeMeshFilter()        {            if (_meshFilter == null)            {                _meshFilter = GetComponent<MeshFilter>();                // Lazily add component.                if (_meshFilter == null)                {                    _meshFilter = gameObject.AddComponent<MeshFilter>();                    _meshFilter.hideFlags = HideFlags.HideInInspector;                }            }        }        /// <summary>        /// Initializes the mesh if necessary.        /// </summary>        private void TryInitializeMesh()        {            // Only create mesh if necessary.            // HACK 'Mesh.vertex > 0' makes sure mesh is recreated in case of runtime instantiation.            if (Meshes != null && Mesh.vertexCount > 0)            {                return;            }            // Create mesh for attached drawable.            var drawable = GetComponent<CubismDrawable>();            if (Meshes == null)            {                Meshes = new Mesh[2];            }            for (var i = 0; i < 2; ++i)            {                var mesh = new Mesh                {                    name = drawable.name,                    vertices = drawable.VertexPositions,                    uv = drawable.VertexUvs,                    triangles = drawable.Indices                };                mesh.MarkDynamic();                mesh.RecalculateBounds();                // Store mesh.                Meshes[i] = mesh;            }        }        /// <summary>        /// Initializes vertex colors.        /// </summary>        private void TryInitializeVertexColor()        {            var mesh = Mesh;            VertexColors = new Color[mesh.vertexCount];            for (var i = 0; i < VertexColors.Length; ++i)            {                VertexColors[i] = Color;                VertexColors[i].a *= Opacity;            }        }        /// <summary>        /// Initializes the main texture if possible.        /// </summary>        private void TryInitializeMainTexture()        {            if (MainTexture == null)            {                MainTexture = null;            }            ApplyMainTexture();        }        /// <summary>        /// Initializes components if possible.        /// </summary>        public void TryInitialize()        {            TryInitializeMeshRenderer();            TryInitializeMeshFilter();            TryInitializeMesh();            TryInitializeVertexColor();            TryInitializeMainTexture();            ApplySorting();        }        #region Swap Info        /// <summary>        /// Sets <see cref="NewVertexPositions"/>.        /// </summary>        private void SetNewVertexPositions()        {            var swapInfo = ThisSwap;            swapInfo.NewVertexPositions = true;            ThisSwap = swapInfo;        }        /// <summary>        /// Sets <see cref="NewVertexColors"/>.        /// </summary>        private void SetNewVertexColors()        {            var swapInfo = ThisSwap;            swapInfo.NewVertexColors = true;            ThisSwap = swapInfo;        }        /// <summary>        /// Sets <see cref="DidBecomeVisible"/> on visible.        /// </summary>        private void BecomeVisible()        {            var swapInfo = ThisSwap;            swapInfo.DidBecomeVisible = true;            ThisSwap = swapInfo;        }        /// <summary>        /// Sets <see cref="DidBecomeInvisible"/> on invisible.        /// </summary>        private void BecomeInvisible()        {            var swapInfo = ThisSwap;            swapInfo.DidBecomeInvisible = true;            ThisSwap = swapInfo;        }        /// <summary>        /// Sets <see cref="SetNewRenderOrder"/>.        /// </summary>        private void SetNewRenderOrder()        {            var swapInfo = ThisSwap;            swapInfo.NewRenderOrder = true;            ThisSwap = swapInfo;        }        /// <summary>        /// Resets flags.        /// </summary>        private void ResetSwapInfoFlags()        {            var swapInfo = ThisSwap;            swapInfo.NewVertexColors = false;            swapInfo.NewVertexPositions = false;            swapInfo.DidBecomeVisible = false;            swapInfo.DidBecomeInvisible = false;            ThisSwap = swapInfo;        }        /// <summary>        /// Reset visibility flags.        /// </summary>        private void ResetVisibilityFlags()        {            var swapInfo = LastSwap;            swapInfo.DidBecomeVisible = false;            swapInfo.DidBecomeInvisible = false;            LastSwap = swapInfo;        }        /// <summary>        /// Reset render order flag.        /// </summary>        private void ResetRenderOrderFlag()        {            var swapInfo = LastSwap;            swapInfo.NewRenderOrder = false;            LastSwap = swapInfo;        }        /// <summary>        /// Allows tracking of <see cref="Mesh"/> data changed on a swap.        /// </summary>        private struct SwapInfo        {            /// <summary>            /// Vertex positions were changed.            /// </summary>            public bool NewVertexPositions { get; set; }            /// <summary>            /// Vertex colors were changed.            /// </summary>            public bool NewVertexColors { get; set; }            /// <summary>            /// Visibility were changed to visible.            /// </summary>            public bool DidBecomeVisible { get; set; }            /// <summary>            /// Visibility were changed to invisible.            /// </summary>            public bool DidBecomeInvisible { get; set; }            /// <summary>            /// Render order were changed.            /// </summary>            public bool NewRenderOrder { get; set; }        }        #endregion        #region Unity Events Handling        /// <summary>        /// Finalizes instance.        /// </summary>        private void OnDestroy()        {            if (Meshes == null)            {                return;            }            for (var i = 0; i < Meshes.Length; i++)            {                DestroyImmediate(Meshes[i]);            }        }        #endregion    }}
 |